├── .github └── workflows │ └── haskell-ci.yml ├── .gitignore ├── .stylish-haskell.yaml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── binary-orphans.cabal ├── cabal.haskell-ci ├── cabal.project ├── src └── Data │ └── Binary │ └── Orphans.hs └── test └── Tests.hs /.github/workflows/haskell-ci.yml: -------------------------------------------------------------------------------- 1 | # This GitHub workflow config has been generated by a script via 2 | # 3 | # haskell-ci 'github' 'cabal.project' 4 | # 5 | # To regenerate the script (for example after adjusting tested-with) run 6 | # 7 | # haskell-ci regenerate 8 | # 9 | # For more information, see https://github.com/haskell-CI/haskell-ci 10 | # 11 | # version: 0.19.20250104 12 | # 13 | # REGENDATA ("0.19.20250104",["github","cabal.project"]) 14 | # 15 | name: Haskell-CI 16 | on: 17 | push: 18 | branches: 19 | - master 20 | pull_request: 21 | branches: 22 | - master 23 | jobs: 24 | linux: 25 | name: Haskell-CI - Linux - ${{ matrix.compiler }} 26 | runs-on: ubuntu-20.04 27 | timeout-minutes: 28 | 60 29 | container: 30 | image: buildpack-deps:jammy 31 | continue-on-error: ${{ matrix.allow-failure }} 32 | strategy: 33 | matrix: 34 | include: 35 | - compiler: ghc-9.12.1 36 | compilerKind: ghc 37 | compilerVersion: 9.12.1 38 | setup-method: ghcup 39 | allow-failure: false 40 | - compiler: ghc-9.10.1 41 | compilerKind: ghc 42 | compilerVersion: 9.10.1 43 | setup-method: ghcup 44 | allow-failure: false 45 | - compiler: ghc-9.8.4 46 | compilerKind: ghc 47 | compilerVersion: 9.8.4 48 | setup-method: ghcup 49 | allow-failure: false 50 | - compiler: ghc-9.6.6 51 | compilerKind: ghc 52 | compilerVersion: 9.6.6 53 | setup-method: ghcup 54 | allow-failure: false 55 | - compiler: ghc-9.4.8 56 | compilerKind: ghc 57 | compilerVersion: 9.4.8 58 | setup-method: ghcup 59 | allow-failure: false 60 | - compiler: ghc-9.2.8 61 | compilerKind: ghc 62 | compilerVersion: 9.2.8 63 | setup-method: ghcup 64 | allow-failure: false 65 | - compiler: ghc-9.0.2 66 | compilerKind: ghc 67 | compilerVersion: 9.0.2 68 | setup-method: ghcup 69 | allow-failure: false 70 | - compiler: ghc-8.10.7 71 | compilerKind: ghc 72 | compilerVersion: 8.10.7 73 | setup-method: ghcup 74 | allow-failure: false 75 | - compiler: ghc-8.8.4 76 | compilerKind: ghc 77 | compilerVersion: 8.8.4 78 | setup-method: ghcup 79 | allow-failure: false 80 | - compiler: ghc-8.6.5 81 | compilerKind: ghc 82 | compilerVersion: 8.6.5 83 | setup-method: ghcup 84 | allow-failure: false 85 | fail-fast: false 86 | steps: 87 | - name: apt-get install 88 | run: | 89 | apt-get update 90 | apt-get install -y --no-install-recommends gnupg ca-certificates dirmngr curl git software-properties-common libtinfo5 91 | - name: Install GHCup 92 | run: | 93 | mkdir -p "$HOME/.ghcup/bin" 94 | curl -sL https://downloads.haskell.org/ghcup/0.1.30.0/x86_64-linux-ghcup-0.1.30.0 > "$HOME/.ghcup/bin/ghcup" 95 | chmod a+x "$HOME/.ghcup/bin/ghcup" 96 | - name: Install cabal-install 97 | run: | 98 | "$HOME/.ghcup/bin/ghcup" install cabal 3.14.1.1 || (cat "$HOME"/.ghcup/logs/*.* && false) 99 | echo "CABAL=$HOME/.ghcup/bin/cabal-3.14.1.1 -vnormal+nowrap" >> "$GITHUB_ENV" 100 | - name: Install GHC (GHCup) 101 | if: matrix.setup-method == 'ghcup' 102 | run: | 103 | "$HOME/.ghcup/bin/ghcup" install ghc "$HCVER" || (cat "$HOME"/.ghcup/logs/*.* && false) 104 | HC=$("$HOME/.ghcup/bin/ghcup" whereis ghc "$HCVER") 105 | HCPKG=$(echo "$HC" | sed 's#ghc$#ghc-pkg#') 106 | HADDOCK=$(echo "$HC" | sed 's#ghc$#haddock#') 107 | echo "HC=$HC" >> "$GITHUB_ENV" 108 | echo "HCPKG=$HCPKG" >> "$GITHUB_ENV" 109 | echo "HADDOCK=$HADDOCK" >> "$GITHUB_ENV" 110 | env: 111 | HCKIND: ${{ matrix.compilerKind }} 112 | HCNAME: ${{ matrix.compiler }} 113 | HCVER: ${{ matrix.compilerVersion }} 114 | - name: Set PATH and environment variables 115 | run: | 116 | echo "$HOME/.cabal/bin" >> $GITHUB_PATH 117 | echo "LANG=C.UTF-8" >> "$GITHUB_ENV" 118 | echo "CABAL_DIR=$HOME/.cabal" >> "$GITHUB_ENV" 119 | echo "CABAL_CONFIG=$HOME/.cabal/config" >> "$GITHUB_ENV" 120 | HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\d+)\.(\d+)\.(\d+)(\.(\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))') 121 | echo "HCNUMVER=$HCNUMVER" >> "$GITHUB_ENV" 122 | echo "ARG_TESTS=--enable-tests" >> "$GITHUB_ENV" 123 | echo "ARG_BENCH=--enable-benchmarks" >> "$GITHUB_ENV" 124 | echo "HEADHACKAGE=false" >> "$GITHUB_ENV" 125 | echo "ARG_COMPILER=--$HCKIND --with-compiler=$HC" >> "$GITHUB_ENV" 126 | env: 127 | HCKIND: ${{ matrix.compilerKind }} 128 | HCNAME: ${{ matrix.compiler }} 129 | HCVER: ${{ matrix.compilerVersion }} 130 | - name: env 131 | run: | 132 | env 133 | - name: write cabal config 134 | run: | 135 | mkdir -p $CABAL_DIR 136 | cat >> $CABAL_CONFIG <> $CABAL_CONFIG < cabal-plan.xz 169 | echo 'f62ccb2971567a5f638f2005ad3173dba14693a45154c1508645c52289714cb2 cabal-plan.xz' | sha256sum -c - 170 | xz -d < cabal-plan.xz > $HOME/.cabal/bin/cabal-plan 171 | rm -f cabal-plan.xz 172 | chmod a+x $HOME/.cabal/bin/cabal-plan 173 | cabal-plan --version 174 | - name: checkout 175 | uses: actions/checkout@v4 176 | with: 177 | path: source 178 | - name: initial cabal.project for sdist 179 | run: | 180 | touch cabal.project 181 | echo "packages: $GITHUB_WORKSPACE/source/." >> cabal.project 182 | cat cabal.project 183 | - name: sdist 184 | run: | 185 | mkdir -p sdist 186 | $CABAL sdist all --output-dir $GITHUB_WORKSPACE/sdist 187 | - name: unpack 188 | run: | 189 | mkdir -p unpacked 190 | find sdist -maxdepth 1 -type f -name '*.tar.gz' -exec tar -C $GITHUB_WORKSPACE/unpacked -xzvf {} \; 191 | - name: generate cabal.project 192 | run: | 193 | PKGDIR_binary_orphans="$(find "$GITHUB_WORKSPACE/unpacked" -maxdepth 1 -type d -regex '.*/binary-orphans-[0-9.]*')" 194 | echo "PKGDIR_binary_orphans=${PKGDIR_binary_orphans}" >> "$GITHUB_ENV" 195 | rm -f cabal.project cabal.project.local 196 | touch cabal.project 197 | touch cabal.project.local 198 | echo "packages: ${PKGDIR_binary_orphans}" >> cabal.project 199 | echo "package binary-orphans" >> cabal.project 200 | echo " ghc-options: -Werror=missing-methods" >> cabal.project 201 | cat >> cabal.project <> cabal.project.local 204 | cat cabal.project 205 | cat cabal.project.local 206 | - name: dump install plan 207 | run: | 208 | $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dry-run all 209 | cabal-plan 210 | - name: restore cache 211 | uses: actions/cache/restore@v4 212 | with: 213 | key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }} 214 | path: ~/.cabal/store 215 | restore-keys: ${{ runner.os }}-${{ matrix.compiler }}- 216 | - name: install dependencies 217 | run: | 218 | $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --dependencies-only -j2 all 219 | $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dependencies-only -j2 all 220 | - name: build w/o tests 221 | run: | 222 | $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all 223 | - name: build 224 | run: | 225 | $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --write-ghc-environment-files=always 226 | - name: tests 227 | run: | 228 | $CABAL v2-test $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --test-show-details=direct 229 | - name: cabal check 230 | run: | 231 | cd ${PKGDIR_binary_orphans} || false 232 | ${CABAL} -vnormal check 233 | - name: haddock 234 | run: | 235 | $CABAL v2-haddock --disable-documentation --haddock-all $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH all 236 | - name: unconstrained build 237 | run: | 238 | rm -f cabal.project.local 239 | $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all 240 | - name: save cache 241 | if: always() 242 | uses: actions/cache/save@v4 243 | with: 244 | key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }} 245 | path: ~/.cabal/store 246 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | dist-newstyle/ 3 | .ghc.environment.* 4 | -------------------------------------------------------------------------------- /.stylish-haskell.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - imports: 3 | align: group 4 | list_align: after_alias 5 | long_list_align: new_line 6 | empty_list_align: right_after 7 | list_padding: module_name 8 | - language_pragmas: 9 | style: vertical 10 | remove_redundant: true 11 | - trailing_whitespace: {} 12 | columns: 80 13 | language_extensions: 14 | - DataKinds 15 | - EmptyCase 16 | - ExplicitForAll 17 | - FlexibleContexts 18 | - MultiParamTypeClasses 19 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 1.0.5 2 | 3 | - Support GHC-8.6.5..GHC-9.10.1 4 | 5 | # 1.0.4.1 6 | 7 | - Support OneTuple-0.4 8 | 9 | # 1.0.4 10 | 11 | - Depend on `data-byte-array` to provide `Data.Array.Byte` instance 12 | 13 | # 1.0.3 14 | 15 | - Add `ByteArray` (from `Data.Array.Byte` instance) 16 | 17 | # 1.0.2 18 | 19 | - Add `Solo` instance 20 | 21 | # 1.0.1 22 | 23 | - Fix `MonadFail` instances shim 24 | - `NonEmpty` doesn't fail on empty list 25 | 26 | # 1 27 | 28 | Stripped down the package to only shim `binary` orphans. 29 | 30 | For more instances check [binary-instances](https://hackage.haskell.org/package/binary-instances) package. 31 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Patches welcome! 2 | 3 | - If you are only going to bump bounds: 4 | - If it's really **only bounds**, please simply open an issue (so you'll have a URL to refer to). I have a semi-automated process to make revisions, pull requests only disturb it. 5 | - If patch includes **source code change** (i.e. I'll need to make a release), and it's a patch to support **newer base/GHC version**: 6 | - Amend `tested-with` to include that GHC 7 | - Regenerate `.github/workflows/haskell-ci.yml` with `haskell-ci regenerate` (get the latest from [GitHub haskell-ci/haskell-ci](https://github.com/haskell-ci/haskell-ci)) 8 | 9 | - Don't edit `CHANGELOG.md`, rather include a copyable entry in your pull request description. I often process pull requests in bulk, and everyone editing the `CHANGELOG.md` causes unnecessary conflicts. 10 | - For the same reason, do not edit `version` or `x-revision` 11 | 12 | - I use [`stylish-haskell`](https://github.com/jaspervdj/stylish-haskell) to format imports. I encourage you to use it too, when contributing. 13 | - General code style is 4 spaces, just look around how it looks, it's not so strict. 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2019 @binary@-contributors, Oleg Grenrus 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | * Neither the name Mark Karpov nor the names of contributors may be used to 16 | endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS “AS IS” AND ANY EXPRESS 20 | OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 22 | NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 25 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 26 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 28 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /binary-orphans.cabal: -------------------------------------------------------------------------------- 1 | cabal-version: 1.12 2 | name: binary-orphans 3 | version: 1.0.5 4 | x-revision: 1 5 | synopsis: Compatibility package for binary; provides instances 6 | category: Data, Binary, Parsing, Compatibility 7 | description: 8 | This package provides instances defined in later versions of @binary@ package 9 | . 10 | Prior version 1 this packages provided instances for other packages. 11 | That functionality is moved to [binary-instances](https://hackage.haskell.org/package/binary-instances) package. 12 | 13 | build-type: Simple 14 | maintainer: Oleg Grenrus 15 | author: Oleg Grenrus 16 | license: BSD3 17 | license-file: LICENSE 18 | tested-with: 19 | GHC ==8.6.5 20 | || ==8.8.4 21 | || ==8.10.7 22 | || ==9.0.2 23 | || ==9.2.8 24 | || ==9.4.8 25 | || ==9.6.6 26 | || ==9.8.4 27 | || ==9.10.1 28 | || ==9.12.1 29 | 30 | extra-source-files: CHANGELOG.md 31 | 32 | source-repository head 33 | type: git 34 | location: https://github.com/phadej/binary-orphans.git 35 | 36 | library 37 | default-language: Haskell2010 38 | hs-source-dirs: src 39 | ghc-options: -Wall 40 | exposed-modules: Data.Binary.Orphans 41 | other-extensions: CPP 42 | build-depends: 43 | base >=4.12.0.0 && <4.22 44 | , binary >=0.8.6.0 && <0.8.10 45 | 46 | if !impl(ghc >=9.2) 47 | build-depends: OneTuple >=0.4.2 && <0.5 48 | 49 | if impl(ghc >=8.0 && <9.4) 50 | build-depends: data-array-byte >=0.1.0.1 && <0.2 51 | 52 | test-suite binary-orphans-test 53 | default-language: Haskell2010 54 | type: exitcode-stdio-1.0 55 | main-is: Tests.hs 56 | hs-source-dirs: test 57 | ghc-options: -Wall 58 | build-depends: 59 | base 60 | , binary 61 | , binary-orphans 62 | , OneTuple >=0.3 && <0.5 63 | , QuickCheck >=2.13.1 && <2.16 64 | , quickcheck-instances >=0.3.28 && <0.4 65 | , tagged >=0.8.6 && <0.9 66 | , tasty >=0.10.1.2 && <1.6 67 | , tasty-quickcheck >=0.8.3.2 && <0.12 68 | 69 | if impl(ghc >=8.0 && <9.4) 70 | build-depends: data-array-byte 71 | -------------------------------------------------------------------------------- /cabal.haskell-ci: -------------------------------------------------------------------------------- 1 | branches: master 2 | -------------------------------------------------------------------------------- /cabal.project: -------------------------------------------------------------------------------- 1 | packages: . 2 | tests: True 3 | -------------------------------------------------------------------------------- /src/Data/Binary/Orphans.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CPP #-} 2 | {-# LANGUAGE FlexibleContexts #-} 3 | {-# LANGUAGE FlexibleInstances #-} 4 | {-# LANGUAGE MagicHash #-} 5 | {-# LANGUAGE ScopedTypeVariables #-} 6 | {-# LANGUAGE UnboxedTuples #-} 7 | {-# LANGUAGE PolyKinds #-} 8 | {-# OPTIONS_GHC -Wno-orphans #-} 9 | module Data.Binary.Orphans () where 10 | 11 | import Data.Binary 12 | 13 | import Control.Monad (replicateM) 14 | 15 | #if MIN_VERSION_base(4,18,0) 16 | import Data.Tuple (Solo (MkSolo)) 17 | #elif MIN_VERSION_base(4,16,0) 18 | import Data.Tuple (Solo (Solo)) 19 | #define MkSolo Solo 20 | #else 21 | import Data.Tuple.Solo (Solo (MkSolo)) 22 | #endif 23 | 24 | import Data.Array.Byte (ByteArray (..), MutableByteArray (..)) 25 | import GHC.Exts 26 | (Int (..), indexWord8Array#, newByteArray#, sizeofByteArray#, 27 | unsafeFreezeByteArray#, writeWord8Array#) 28 | import GHC.ST (ST (..), runST) 29 | import GHC.Word (Word8 (..)) 30 | 31 | ------------------------------------------------------------------------------- 32 | -- future-binary 33 | ------------------------------------------------------------------------------- 34 | 35 | -- | @since 1.0.2 36 | instance Binary a => Binary (Solo a) where 37 | put (MkSolo x) = put x 38 | get = fmap MkSolo get 39 | 40 | -- | @since 1.0.3 41 | instance Binary ByteArray where 42 | put ba = put maxI >> go 0 43 | where 44 | maxI :: Int 45 | maxI = sizeofByteArray ba 46 | 47 | go :: Int -> Put 48 | go i | i < maxI = put (indexByteArray ba i) >> go (i + 1) 49 | | otherwise = return () 50 | 51 | get = do 52 | len <- get 53 | xs <- replicateM len get 54 | return (byteArrayFromListN len xs) 55 | 56 | {-# INLINE sizeofByteArray #-} 57 | sizeofByteArray :: ByteArray -> Int 58 | sizeofByteArray (ByteArray ba) = I# (sizeofByteArray# ba) 59 | 60 | {-# INLINE indexByteArray #-} 61 | indexByteArray :: ByteArray -> Int -> Word8 62 | indexByteArray (ByteArray ba) (I# i) = W8# (indexWord8Array# ba i) 63 | 64 | {-# INLINE byteArrayFromListN #-} 65 | byteArrayFromListN :: Int -> [Word8] -> ByteArray 66 | byteArrayFromListN len xs = runST $ do 67 | mba <- newByteArray len 68 | go mba 0 xs 69 | unsafeFreezeByteArray mba 70 | where 71 | go :: MutableByteArray s -> Int -> [Word8] -> ST s () 72 | go mba i ys 73 | | i < len = case ys of 74 | [] -> writeWord8Array mba i 0 >> go mba (i + 1) ys 75 | z:zs -> writeWord8Array mba i z >> go mba (i + 1) zs 76 | 77 | | otherwise = return () 78 | 79 | {-# INLINE newByteArray #-} 80 | newByteArray :: Int -> ST s (MutableByteArray s) 81 | newByteArray (I# len) = ST $ \s -> case newByteArray# len s of 82 | (# s', mba #) -> (# s', MutableByteArray mba #) 83 | 84 | {-# INLINE unsafeFreezeByteArray #-} 85 | unsafeFreezeByteArray :: MutableByteArray s -> ST s ByteArray 86 | unsafeFreezeByteArray (MutableByteArray mba) = ST $ \s -> case unsafeFreezeByteArray# mba s of 87 | (# s', ba #) -> (# s', ByteArray ba #) 88 | 89 | {-# INLINE writeWord8Array #-} 90 | writeWord8Array :: MutableByteArray s -> Int -> Word8 -> ST s () 91 | writeWord8Array (MutableByteArray mba) (I# i) (W8# w) = ST $ \s -> case writeWord8Array# mba i w s of 92 | s' -> (# s', () #) 93 | -------------------------------------------------------------------------------- /test/Tests.hs: -------------------------------------------------------------------------------- 1 | module Main (main) where 2 | 3 | import Data.Array.Byte (ByteArray) 4 | import Data.Binary (Binary, decode, encode) 5 | import Data.Binary.Orphans () 6 | import Data.Monoid (Sum) 7 | import Data.Proxy (Proxy (..)) 8 | import Data.Semigroup (Min (..)) 9 | import Data.Tuple.Solo (Solo (..)) 10 | import Numeric.Natural (Natural) 11 | import Test.QuickCheck (Property, (===)) 12 | import Test.QuickCheck.Instances () 13 | import Test.Tasty (TestTree, defaultMain, testGroup) 14 | import Test.Tasty.QuickCheck (testProperty) 15 | 16 | main :: IO () 17 | main = defaultMain tests 18 | 19 | tests :: TestTree 20 | tests = testGroup "Roundtrip" 21 | [ testProperty "Natural" $ roundtrip (Proxy :: Proxy Natural) 22 | , testProperty "Sum Int" $ roundtrip (Proxy :: Proxy (Sum Int)) 23 | , testProperty "Min Int" $ roundtrip (Proxy :: Proxy (Min Int)) 24 | , testProperty "Solo Int" $ roundtrip (Proxy :: Proxy (Solo Int)) 25 | , testProperty "ByteArray" $ roundtrip (Proxy :: Proxy ByteArray) 26 | ] 27 | 28 | roundtrip :: (Eq a, Show a, Binary a) => Proxy a -> a -> Property 29 | roundtrip _ x = x === decode (encode x) 30 | --------------------------------------------------------------------------------