packages feed

xcodec 1.1.0.0 → 2.0.0.0

raw patch · 7 files changed

+350/−380 lines, 7 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Data.XCodec.BinaryTranscoder: class (Monoid bxc) => BinaryTranscoder bxc
- Data.XCodec.BinaryTranscoder: dropOctetsEnd :: BinaryTranscoder bxc => Int -> bxc -> bxc
- Data.XCodec.BinaryTranscoder: dropOctetsTop :: BinaryTranscoder bxc => Int -> bxc -> bxc
- Data.XCodec.BinaryTranscoder: dropWhileEndOctets :: BinaryTranscoder bxc => (Octet -> Bool) -> bxc -> bxc
- Data.XCodec.BinaryTranscoder: dropWhileTopOctets :: BinaryTranscoder bxc => (Octet -> Bool) -> bxc -> bxc
- Data.XCodec.BinaryTranscoder: fromOctet :: BinaryTranscoder bxc => Octet -> bxc
- Data.XCodec.BinaryTranscoder: instance Data.XCodec.BinaryTranscoder.BinaryTranscoder Data.ByteString.Internal.Type.ByteString
- Data.XCodec.BinaryTranscoder: instance Data.XCodec.BinaryTranscoder.BinaryTranscoder Data.ByteString.Lazy.Internal.LazyByteString
- Data.XCodec.BinaryTranscoder: instance Data.XCodec.BinaryTranscoder.BinaryTranscoder Data.ByteString.Short.Internal.ShortByteString
- Data.XCodec.BinaryTranscoder: null :: BinaryTranscoder bxc => bxc -> Bool
- Data.XCodec.BinaryTranscoder: packOctets :: BinaryTranscoder bxc => [Octet] -> bxc
- Data.XCodec.BinaryTranscoder: packValueBE :: (Integral val, Bits val, BinaryTranscoder bxc) => val -> bxc
- Data.XCodec.BinaryTranscoder: packValueLE :: (Integral val, Bits val, BinaryTranscoder bxc) => val -> bxc
- Data.XCodec.BinaryTranscoder: pushOctetEnd :: BinaryTranscoder bxc => bxc -> Octet -> bxc
- Data.XCodec.BinaryTranscoder: pushOctetTop :: BinaryTranscoder bxc => bxc -> Octet -> bxc
- Data.XCodec.BinaryTranscoder: replicateOctet :: BinaryTranscoder bxc => Int -> Octet -> bxc
- Data.XCodec.BinaryTranscoder: spanOctetsEnd :: BinaryTranscoder bxc => (Octet -> Bool) -> bxc -> (bxc, bxc)
- Data.XCodec.BinaryTranscoder: spanOctetsTop :: BinaryTranscoder bxc => (Octet -> Bool) -> bxc -> (bxc, bxc)
- Data.XCodec.BinaryTranscoder: splitOffset :: BinaryTranscoder bxc => Int -> bxc -> (bxc, bxc)
- Data.XCodec.BinaryTranscoder: swapOrder :: BinaryTranscoder bxc => bxc -> bxc
- Data.XCodec.BinaryTranscoder: takeOctetsEnd :: BinaryTranscoder bxc => Int -> bxc -> bxc
- Data.XCodec.BinaryTranscoder: takeOctetsTop :: BinaryTranscoder bxc => Int -> bxc -> bxc
- Data.XCodec.BinaryTranscoder: takeWhileEndOctets :: BinaryTranscoder bxc => (Octet -> Bool) -> bxc -> bxc
- Data.XCodec.BinaryTranscoder: takeWhileTopOctets :: BinaryTranscoder bxc => (Octet -> Bool) -> bxc -> bxc
- Data.XCodec.BinaryTranscoder: totalOctets :: BinaryTranscoder bxc => bxc -> Int
- Data.XCodec.BinaryTranscoder: type BitSet = Integer
- Data.XCodec.BinaryTranscoder: type Octet = Word8
- Data.XCodec.BinaryTranscoder: type UnpackOctets bxc = bxc -> [Octet]
- Data.XCodec.BinaryTranscoder: type UnpackSerial bxc = bxc -> Builder
- Data.XCodec.BinaryTranscoder: type UnpackValue bxc = bxc -> BitSet
- Data.XCodec.BinaryTranscoder: uncons :: BinaryTranscoder bxc => bxc -> Maybe (Octet, bxc)
- Data.XCodec.BinaryTranscoder: unpackOctets :: BinaryTranscoder bxc => UnpackOctets bxc
- Data.XCodec.BinaryTranscoder: unpackSerial :: BinaryTranscoder bxc => UnpackSerial bxc
- Data.XCodec.BinaryTranscoder: unpackValueBE :: BinaryTranscoder bxc => UnpackValue bxc
- Data.XCodec.BinaryTranscoder: unpackValueCPU :: BinaryTranscoder bxc => UnpackValue bxc
- Data.XCodec.BinaryTranscoder: unpackValueLE :: BinaryTranscoder bxc => UnpackValue bxc
- Data.XCodec.BinaryTranscoder: unsnoc :: BinaryTranscoder bxc => bxc -> Maybe (bxc, Octet)
- Data.XCodec.StreamTranscoder: class (BinaryTranscoder xtype) => StreamTranscoder codec xtype ytype
- Data.XCodec.StreamTranscoder: streamDecoder :: StreamTranscoder codec xtype ytype => codec -> Decoder xtype ytype
- Data.XCodec.StreamTranscoder: streamEncoder :: StreamTranscoder codec xtype ytype => codec -> Encoder xtype ytype
- Data.XCodec.StreamTranscoder: type Decoder xtype ytype = ytype -> Maybe xtype
- Data.XCodec.StreamTranscoder: type Encoder xtype ytype = xtype -> ytype
+ XCodec.Transcoder: class (Monoid tc) => Transcoder tc
+ XCodec.Transcoder: dropOctetsEnd :: Transcoder tc => Int -> tc -> tc
+ XCodec.Transcoder: dropOctetsTop :: Transcoder tc => Int -> tc -> tc
+ XCodec.Transcoder: dropWhileEndOctets :: Transcoder tc => (Octet -> Bool) -> tc -> tc
+ XCodec.Transcoder: dropWhileTopOctets :: Transcoder tc => (Octet -> Bool) -> tc -> tc
+ XCodec.Transcoder: execBuilder :: Transcoder tc => Builder -> tc
+ XCodec.Transcoder: fromOctet :: Transcoder tc => Octet -> tc
+ XCodec.Transcoder: instance XCodec.Transcoder.Transcoder Data.ByteString.Internal.Type.ByteString
+ XCodec.Transcoder: instance XCodec.Transcoder.Transcoder Data.ByteString.Lazy.Internal.LazyByteString
+ XCodec.Transcoder: instance XCodec.Transcoder.Transcoder Data.ByteString.Short.Internal.ShortByteString
+ XCodec.Transcoder: null :: Transcoder tc => tc -> Bool
+ XCodec.Transcoder: packOctets :: Transcoder tc => [Octet] -> tc
+ XCodec.Transcoder: packValueBE :: (Integral val, Bits val, Transcoder tc) => val -> tc
+ XCodec.Transcoder: packValueLE :: (Integral val, Bits val, Transcoder tc) => val -> tc
+ XCodec.Transcoder: pushOctetEnd :: Transcoder tc => tc -> Octet -> tc
+ XCodec.Transcoder: pushOctetTop :: Transcoder tc => tc -> Octet -> tc
+ XCodec.Transcoder: replicateOctet :: Transcoder tc => Int -> Octet -> tc
+ XCodec.Transcoder: spanOctetsEnd :: Transcoder tc => (Octet -> Bool) -> tc -> (tc, tc)
+ XCodec.Transcoder: spanOctetsTop :: Transcoder tc => (Octet -> Bool) -> tc -> (tc, tc)
+ XCodec.Transcoder: splitOffset :: Transcoder tc => Int -> tc -> (tc, tc)
+ XCodec.Transcoder: swapOrder :: Transcoder tc => tc -> tc
+ XCodec.Transcoder: takeOctetsEnd :: Transcoder tc => Int -> tc -> tc
+ XCodec.Transcoder: takeOctetsTop :: Transcoder tc => Int -> tc -> tc
+ XCodec.Transcoder: takeWhileEndOctets :: Transcoder tc => (Octet -> Bool) -> tc -> tc
+ XCodec.Transcoder: takeWhileTopOctets :: Transcoder tc => (Octet -> Bool) -> tc -> tc
+ XCodec.Transcoder: totalOctets :: Transcoder tc => tc -> Int
+ XCodec.Transcoder: type BitSet = Integer
+ XCodec.Transcoder: type Octet = Word8
+ XCodec.Transcoder: uncons :: Transcoder tc => tc -> Maybe (Octet, tc)
+ XCodec.Transcoder: unpackBuilder :: Transcoder tc => tc -> Builder
+ XCodec.Transcoder: unpackOctets :: Transcoder tc => tc -> [Octet]
+ XCodec.Transcoder: unpackValueBE :: Transcoder tc => tc -> BitSet
+ XCodec.Transcoder: unpackValueCPU :: Transcoder tc => tc -> BitSet
+ XCodec.Transcoder: unpackValueLE :: Transcoder tc => tc -> BitSet
+ XCodec.Transcoder: unsnoc :: Transcoder tc => tc -> Maybe (tc, Octet)

Files

CHANGELOG.md view
@@ -1,6 +1,23 @@ [`xcodec`](http://hackage.haskell.org/package/xcodec) change log: ================================================================= +Major release 2.0+-----------------++## Version 2.0.0.0 (07-21-2026)++XCodec is now on version 2.0! This version is a clean up of the naming, and+removes the `StreamTranscoder` module, since it provided no functionality to+the project.++The module naming scheme has been changed. Now, instead of+`Data.XCodec.BinaryTranscoder` to access the transcoder class, you use+`XCodec.Transcoder`. Additionally, the type-class name for `BinaryTranscoder`+has been changed to `Transcoder`. The function `unpackSerial` was renamed to+`unpackBuilder` and `execBuilder` was added to put builders into XCodec.+Finally, exports of function type-aliases have been removed, as they have+been inlined into the class function signatures.+ Major release 1.0 ----------------- 
README.md view
@@ -5,18 +5,16 @@ https://builds.sr.ht/~z0/xcodec/commits/main/ipfshs-ci.yml?) ------------------------------------------------------------ -The `xcodec` Haskell library provides a type-class for generic programming on-bit data for writing encoders and decoders for codecs. The `BinaryTranscoder`+The xcodec Haskell library provides a type-class for generic programming on+bit data for writing encoders and decoders for codecs. The `Transcoder` class provides a common interface of methods for processing binary data, and default instances for the `bytestring` types: `ByteString`, `ShortByteString`,-and `LazyByteString`. The `StreamTranscoder` class then provides the abstract-methods for a generic function encoding from and a generic function decoding to-the types implementing `BinaryTranscoder`.+and `LazyByteString`.  Why? ---- -`xcodec` exists to abstract common patterns that arise when writing code with+xcodec exists to abstract common patterns that arise when writing code with the `bytestring` library in Haskell: - Reusing code for `ShortByteString`, `ByteString` and `LazyByteString`. - Having common interface for transferring into the bytestring `Builder` type.@@ -24,14 +22,14 @@ from constant values.  Making bytestring code generic, can also make code more memory efficient; for-example, we can write functions for `BinaryTranscoder` and apply them to+example, we can write functions for `Transcoder` and apply them to `LazyByteString` when reading large files and to `ShortByteString` when working on smaller internal structures.  Examples -------- -Using `xcodec` we can easily read numeric bit data to binary formats,+Using xcodec we can easily read numeric bit data to binary formats, programmatically:  ```haskell@@ -39,7 +37,7 @@ import Data.ByteString.Short (ShortByteString) import Data.ByteString.Lazy (LazyByteString) import Data.Word (Word16)-import Data.XCodec.BinaryTranscoder (packValue)+import XCodec.Transcoder (packValue)  -- Infers: packValue :: Int -> ByteString exStrict :: ByteString@@ -60,30 +58,30 @@ ```haskell import Data.ByteString.Builder qualified as Builder import Data.ByteString.Lazy (LazyByteString)-import Data.XCodec.BinaryTranscoder (serializeValue)+import XCodec.Transcoder (unpackBuilder)  -- We can join several BXCs and Builders into a single unit of data. exBuilder :: LazyByteString exBuilder =   Builder.toLazyByteString . mconcat $-    [ serializeValue exLazy, -- serializeValue :: LazyByteString -> Builder-      serializeValue exStrict, -- serializeValue :: ByteString -> Builder-      serializeValue exShort, -- serializeValue :: ShortByteString -> Builder+    [ unpackBuilder exLazy, -- unpackBuilder :: LazyByteString -> Builder+      unpackBuilder exStrict, -- unpackBuilder :: ByteString -> Builder+      unpackBuilder exShort, -- unpackBuilder :: ShortByteString -> Builder       Builder.string8 "Hello, World!" -- string8 :: String -> Builder     ] ```  It also enables extracting binary data into `Integer` format so that bitwise transformations can be performed on large sets of binary data in *O(n)* time-with a specified [byte-order](https://en.wikipedia.org/wiki/Endianness):+with a specified [byte order](https://en.wikipedia.org/wiki/Endianness):  ```haskell import Data.Bits ((.>>.), (.&.)) import Data.Word (Word32)-import Data.XCodec.BinaryTranscoder (BinaryTranscoder, unpackValue)+import XCodec.Transcoder (unpackValueBE, unpackValueLE)  -- We can easily read an entire bit-set representation of transcoder data,--- directly from LazyByteString because it derives BinaryTranscoder. This+-- directly from LazyByteString because it derives Transcoder. This -- function produces its big-endian representation largeBitSet :: Integer largeBitSet = unpackValueBE exBuilder@@ -97,15 +95,27 @@ Development ----------- -Unit tests are provided on the main [ipfshs repo](https://git.sr.ht/~z0/ipfshs),-and bugs can be reported on [ipfshs ticket tracker](-https://todo.sr.ht/~z0/ipfshs).+This project is part of [ipfshs](https://sr.ht/~z0/ipfshs); unit tests are+provided on the main page and bugs can be reported on its [ticket tracker](+https://todo.sr.ht/~z0/ipfshs). Patches and pull requests can be submitted with+[`git send-email`](https://git-send-email.io/). To build and test this project+against all of ipfshs read [this](https://sr.ht/~z0/ipfshs/#development)+section on setting up an ipfshs development environment. +This project can also be built as a standalone library with [`cabal`](+https://github.com/haskell/cabal#ways-to-get-the-cabal-install-binary).++```shell+$ git clone https://git.sr.ht/~z0/xcodec+$ cd ipldm+$ cabal build+```+ Licensing --------- -The `xcodec` project and its modules are free software and licensed under the-BSD 3-clause license. See [LICENSE.txt](LICENSE.txt).+The xcodec project and its modules are free software and licensed under the BSD+3-clause license. See [`LICENSE.txt`](LICENSE.txt).  Copyright © 2026 Zoey McBride | [zoeymcbride@mailbox.org]( mailto:zoeymcbride@mailbox.org)
xcodec.cabal view
@@ -1,5 +1,5 @@ cabal-version:   3.0-version:         1.1.0.0+version:         2.0.0.0 name:            xcodec build-type:      Simple author:          Zoey McBride@@ -61,10 +61,5 @@     else { import: BuildUser }     exposed-modules:         -- * Type-class for transcoding binary data.-        Data.XCodec.BinaryTranscoder-        -- * Type-class for transcoding streams derivided from BinaryTranscoder-        Data.XCodec.StreamTranscoder-    other-modules:-        -- * Helper functions-        Data.XCodec.Internal+        XCodec.Transcoder }
− xcodec/Data/XCodec/BinaryTranscoder.hs
@@ -1,295 +0,0 @@--- | Module      : Data.XCodec.BinaryTranscoder---   Description : Type-class for building generic codecs on binary data.---   Copyright   : Zoey McBride (c) 2026---   License     : BSD-3-Clause---   Maintainer  : zoeymcbride@mailbox.org---   Stability   : experimental-module Data.XCodec.BinaryTranscoder-  ( -- * Interface for generic transcoding of binary data.-    BinaryTranscoder (..),--    -- * BinaryTranscoder data as numeric values-    BitSet,-    Octet,--    -- * Converts BinaryTranscoders into editable streams.-    UnpackValue,-    UnpackOctets,-    UnpackSerial,--    -- * Conversion of Integral values to transcoder data.-    packValueBE,-    packValueLE,--    -- * BinaryTranscoder utilities-    fromOctet,-    null,-    uncons,-    unsnoc,-    takeOctetsTop,-    dropOctetsTop,-    takeOctetsEnd,-    dropOctetsEnd,-    takeWhileEndOctets,-    takeWhileTopOctets,-    dropWhileEndOctets,-    dropWhileTopOctets,-  )-where--import Data.Bits (Bits, (.&.), (.<<.), (.>>.), (.|.))-import Data.ByteString (ByteString)-import Data.ByteString qualified as Bytes-import Data.ByteString.Builder (Builder)-import Data.ByteString.Builder qualified as Builder-import Data.ByteString.Lazy (LazyByteString)-import Data.ByteString.Lazy qualified as LBS-import Data.ByteString.Short (ShortByteString)-import Data.ByteString.Short qualified as SBS-import Data.Word (Word8)-import Data.XCodec.Internal (iterateInit, replaceNull)-import Prelude hiding (null)---- | Stores 8-bit values.-type Octet = Word8---- | Stores BinaryTranscoder as numeric values.-type BitSet = Integer---- | Functions used for unpacking transcoder data into BitSets.-type UnpackValue bxc = bxc -> BitSet---- | Functions used for unpacking transcoder data into octets/8-bit values.-type UnpackOctets bxc = bxc -> [Octet]---- | Functions used for unpacking transcoder data into bytestring Builders.-type UnpackSerial bxc = bxc -> Builder---- | Class of functions for working on binary transcoder streams, that is--- streams of bytes that we can easily turn into a bitset, and can partition--- into smaller streams for extracting and parsing data.-class (Monoid bxc) => BinaryTranscoder bxc where-  {-# MINIMAL-    totalOctets,-    swapOrder,-    unpackSerial,-    unpackValueCPU,-    unpackValueBE,-    unpackValueLE,-    packOctets,-    unpackOctets,-    pushOctetTop,-    pushOctetEnd,-    spanOctetsTop,-    spanOctetsEnd,-    splitOffset,-    replicateOctet-    #-}--  -- | Gives back the length in octets of the Transcoder data.-  totalOctets :: bxc -> Int--  -- | Packs Octet values into transcoder data `bxc`.-  packOctets :: [Octet] -> bxc--  -- | Places the contents of the transcoder into a ByteString Builder.-  unpackSerial :: UnpackSerial bxc--  -- | Unpacks transcoder data into a list of bytes.-  unpackOctets :: UnpackOctets bxc--  -- | Unpacks transcoder data as BitSet in the host CPU's endianness.-  unpackValueCPU :: UnpackValue bxc--  -- | Unpacks transcoder data as BitSet in Big-Endian (BE) order.-  unpackValueBE :: UnpackValue bxc--  -- | Unpacks transcoder data as BitSet in Little-Endian (LE) order.-  unpackValueLE :: UnpackValue bxc--  -- | Reverses the byte order of the transcoder data.-  swapOrder :: bxc -> bxc--  -- | Prepends an octet value to the top of the transcoder data.-  pushOctetTop :: bxc -> Octet -> bxc--  -- | Appends the octet value to the end of the transcoder data.-  pushOctetEnd :: bxc -> Octet -> bxc--  -- | Parititions the binary data at-  splitOffset :: Int -> bxc -> (bxc, bxc)--  -- | Repeats an octet value in binary data.-  replicateOctet :: Int -> Octet -> bxc--  -- | Splits octet values based on the first false result from the predicate.-  spanOctetsTop :: (Octet -> Bool) -> bxc -> (bxc, bxc)--  -- | Splits the list while the element predicate holds staring from the left-  -- side.-  spanOctetsEnd :: (Octet -> Bool) -> bxc -> (bxc, bxc)---- | Implements binary decoder for Bytestrings, best for constant values that--- exist for the lifetime of the program.--- https://hackage-content.haskell.org/package/bytestring-0.12.2.0/docs/Data-ByteString.html#g:2-instance BinaryTranscoder ByteString where-  totalOctets = Bytes.length-  swapOrder = Bytes.reverse-  packOctets = Bytes.pack-  unpackSerial = Builder.byteString-  unpackValueCPU = unpackValueCPU . LBS.fromStrict-  unpackValueLE = unpackValueLE . LBS.fromStrict-  unpackValueBE = unpackValueBE . LBS.fromStrict-  unpackOctets = Bytes.unpack-  pushOctetTop = flip Bytes.cons-  pushOctetEnd = Bytes.snoc-  spanOctetsTop = Bytes.span-  spanOctetsEnd = Bytes.spanEnd-  splitOffset = Bytes.splitAt-  replicateOctet k = Bytes.replicate $ fromIntegral k---- TODO: we can improve the proformance of unpackBits by examining the current--- bytestring, and loading a range of 1-8 bytes to OR simulaneously---- | Implements binary decoder for LazyByteString, best for very large data sets--- that can be loaded in and out of memory on demand.--- https://hackage-content.haskell.org/package/bytestring-0.12.2.0/docs/Data-ByteString-Lazy.html-instance BinaryTranscoder LazyByteString where-  totalOctets = fromIntegral . LBS.length-  swapOrder = LBS.reverse-  packOctets = LBS.pack-  unpackOctets = LBS.unpack-  unpackValueLE =-    -- From MSB to LSB, OR the current byte and shift the total bits.-    LBS.foldr (\byte bits -> (bits .<<. 8) .|. fromIntegral byte) 0-      . LBS.dropWhileEnd (== 0)-  unpackValueBE =-    -- From LSB to MSB, OR the current byte and shift the total bits.-    LBS.foldl' (\bits byte -> (bits .<<. 8) .|. fromIntegral byte) 0-      . LBS.dropWhile (== 0)-  unpackSerial = Builder.lazyByteString-  pushOctetTop = flip LBS.cons-  pushOctetEnd = LBS.snoc-  spanOctetsTop = LBS.span-  spanOctetsEnd = LBS.spanEnd-  splitOffset k = LBS.splitAt $ fromIntegral k-  replicateOctet k = LBS.replicate $ fromIntegral k-  unpackValueCPU = unpackValueLE---- | Implements binary decoder for ShortByteString, best for compact data that--- doesn't exist long in memory. Notably, it packs better in memory than normal--- ByteString (prone to Heap Fragmentation).--- https://hackage-content.haskell.org/package/bytestring-0.12.2.0/docs/Data-ByteString-Short.html#g:1-instance BinaryTranscoder ShortByteString where-  totalOctets = SBS.length-  swapOrder = SBS.reverse-  packOctets = SBS.pack-  unpackOctets = SBS.unpack-  unpackValueLE =-    -- From MSB to LSB , OR the current byte and shift the total bits.-    SBS.foldr (\byte bits -> (bits .<<. 8) .|. fromIntegral byte) 0-      . SBS.dropWhileEnd (== 0)-  unpackValueBE =-    -- From LSB to MSB, OR the current byte and shift the total bits.-    SBS.foldl' (\bits byte -> (bits .<<. 8) .|. fromIntegral byte) 0-      . SBS.dropWhile (== 0)-  unpackSerial = Builder.shortByteString-  pushOctetTop = flip SBS.cons-  pushOctetEnd = SBS.snoc-  spanOctetsTop = SBS.span-  spanOctetsEnd = SBS.spanEnd-  splitOffset = SBS.splitAt-  replicateOctet k = SBS.replicate $ fromIntegral k-  unpackValueCPU = unpackValueLE---- | Takes a subsection of octets from the beginning of the transcoder data.-takeOctetsTop :: (BinaryTranscoder bxc) => Int -> bxc -> bxc-takeOctetsTop idx = fst . splitOffset idx---- | Removes octets from the beginning of transcoder data.-dropOctetsTop :: (BinaryTranscoder bxc) => Int -> bxc -> bxc-dropOctetsTop idx = snd . splitOffset idx---- | Removes octets from the end of transcoder data.-takeOctetsEnd :: (BinaryTranscoder bxc) => Int -> bxc -> bxc-takeOctetsEnd idx bxc = snd $ splitOffset (totalOctets bxc - idx) bxc---- | Removes octets from the end of transcoder data.-dropOctetsEnd :: (BinaryTranscoder bxc) => Int -> bxc -> bxc-dropOctetsEnd idx bxc = fst $ splitOffset (totalOctets bxc - idx) bxc---- | Implements `takeWhile` for byte data in binary transcoder.-takeWhileTopOctets :: (BinaryTranscoder bxc) => (Octet -> Bool) -> bxc -> bxc-takeWhileTopOctets f = fst . spanOctetsEnd f---- | Implements `dropWhile` for byte data in binary transcoder.-dropWhileTopOctets :: (BinaryTranscoder bxc) => (Octet -> Bool) -> bxc -> bxc-dropWhileTopOctets f = snd . spanOctetsEnd f---- | Implements `takeWhileEnd` for byte data in binary transcoder.-takeWhileEndOctets :: (BinaryTranscoder bxc) => (Octet -> Bool) -> bxc -> bxc-takeWhileEndOctets f = snd . spanOctetsEnd f---- | Implements `dropWhileEnd` for byte data in binary transcoder.-dropWhileEndOctets :: (BinaryTranscoder bxc) => (Octet -> Bool) -> bxc -> bxc-dropWhileEndOctets f = fst . spanOctetsEnd f---- | Creates a binary transcoder from a single byte.-{-# INLINE fromOctet #-}-fromOctet :: (BinaryTranscoder bxc) => Octet -> bxc-fromOctet w8 = packOctets [w8]---- | Returns True if the binary transcoder data is empty.-{-# INLINE null #-}-null :: (BinaryTranscoder bxc) => bxc -> Bool-null bxc = totalOctets bxc == 0---- | Returns the first byte removed from the transcoder data, if available.-{-# INLINE uncons #-}-uncons :: (BinaryTranscoder bxc) => bxc -> Maybe (Octet, bxc)-uncons bxc =-  let (start, rest) = splitOffset 1 bxc-   in case unpackOctets start of-        [top] -> Just (top, rest)-        _ -> Nothing---- | Returns the last byte removed from the transcoder data, if available.-{-# INLINE unsnoc #-}-unsnoc :: (BinaryTranscoder bxc) => bxc -> Maybe (bxc, Octet)-unsnoc bxc =-  let offset = totalOctets bxc - 1-      (rest, taken) = splitOffset offset bxc-   in case unpackOctets taken of-        [final] -> Just (rest, final)-        _ -> Nothing---- | Packs a bit string value into some bytes data `bxc`.-{-# INLINE packValueOrder #-}-packValueOrder ::-  (Integral val, Bits val, BinaryTranscoder bxc) =>-  ([Octet] -> [Octet]) ->-  val ->-  bxc-packValueOrder setorder intdata =-  -- This code can be optimized; if we precalculate the length of the BitSet-  -- in bits we can mask from the other direction using left shifts, and-  -- remove the call to setorder, maybe we can even use a list comprehension to-  -- build in place for packOctets-  packOctets-    . replaceNull [0]-    . setorder-    . map (\(_, byte) -> fromIntegral byte)-    . takeWhile (\(rest, byte) -> rest > 0 || byte > 0)-    $ iterateInit (\(input, _) -> extractByte input) (,0) intdata-  where-    -- Gets the byte from LSB in input and shifts the value 8 bits.-    extractByte input = (input .>>. 8, input .&. 0xFF)---- | Packs a bit string value into some bytes data `bxc` in Big-Endian format.-packValueBE :: (Integral val, Bits val, BinaryTranscoder bxc) => val -> bxc-packValueBE = packValueOrder reverse---- | Packs a bit string value into some bytes data `bxc` in Little-Endian--- format.-packValueLE :: (Integral val, Bits val, BinaryTranscoder bxc) => val -> bxc-packValueLE = packValueOrder id
− xcodec/Data/XCodec/Internal.hs
@@ -1,23 +0,0 @@--- | Module      : Data.XCodec.Internal---   Description : Utility functions for lists.---   Copyright   : Zoey McBride (c) 2026---   License     : BSD-3-Clause---   Maintainer  : zoeymcbride@mailbox.org---   Stability   : experimental-module Data.XCodec.Internal-  ( -- * List utilities-    replaceNull,-    iterateInit,-  )-where---- | Return the the first param if the second param is empty.-{-# INLINE replaceNull #-}-replaceNull :: [a] -> [a] -> [a]-replaceNull replace xs-  | null xs = replace-  | otherwise = xs---- | Wraps iterate to compose with an intermediary type constructor.-iterateInit :: (b -> b) -> (a -> b) -> a -> [b]-iterateInit f iterinit = drop 1 . iterate f . iterinit
− xcodec/Data/XCodec/StreamTranscoder.hs
@@ -1,34 +0,0 @@--- | Module      : Data.XCodec.StreamTranscoder---   Description : Type-class for encoding and decoding binary streams.---   Copyright   : Zoey McBride (c) 2026---   License     : BSD-3-Clause---   Maintainer  : zoeymcbride@mailbox.org---   Stability   : experimental-module Data.XCodec.StreamTranscoder-  ( -- * Interface for implementing generic digit symbol encoder/decoders.-    StreamTranscoder (..),--    -- * Type-functions for generating generic StreamTranscoder instances.-    Encoder,-    Decoder,-  )-where--import Data.XCodec.BinaryTranscoder (BinaryTranscoder)---- | Type of function for transforming some `xtype` to some `ytype`.-type Encoder xtype ytype = xtype -> ytype---- | Type of function for transforming some `ytype` into an `xtype` if the--- `ytype` is well formed.-type Decoder xtype ytype = ytype -> Maybe xtype---- | Type-class of `codec`s for which `streamEncoder` produces a representation--- of an `xtype` through some `ytype` and which `streamDecoder` attempts to form--- a `xtype` only from well-formed values in `ytype`.-class (BinaryTranscoder xtype) => StreamTranscoder codec xtype ytype where-  -- | Implements a `Encoder` function for the `codec`.-  streamEncoder :: codec -> Encoder xtype ytype--  -- | Implements a `Decoder` function for the `codec`.-  streamDecoder :: codec -> Decoder xtype ytype
+ xcodec/XCodec/Transcoder.hs view
@@ -0,0 +1,300 @@+-- | Module      : XCodec.Transcoder+--   Description : Type-class for building generic codecs on binary data.+--   Copyright   : Zoey McBride (c) 2026+--   License     : BSD-3-Clause+--   Maintainer  : zoeymcbride@mailbox.org+--   Stability   : experimental+module XCodec.Transcoder+  ( -- * Interface for generic transcoding of binary data.+    Transcoder (..),++    -- * Transcoder data as numeric values+    BitSet,+    Octet,++    -- * Conversion of Integral values to transcoder data.+    packValueBE,+    packValueLE,++    -- * Transcoder utilities+    fromOctet,+    null,+    uncons,+    unsnoc,+    takeOctetsTop,+    dropOctetsTop,+    takeOctetsEnd,+    dropOctetsEnd,+    takeWhileEndOctets,+    takeWhileTopOctets,+    dropWhileEndOctets,+    dropWhileTopOctets,+  )+where++import Data.Bits (Bits, (.&.), (.<<.), (.>>.), (.|.))+import Data.ByteString (ByteString)+import Data.ByteString qualified as Bytes+import Data.ByteString.Builder (Builder)+import Data.ByteString.Builder qualified as BSB+import Data.ByteString.Builder qualified as Builder+import Data.ByteString.Lazy (LazyByteString)+import Data.ByteString.Lazy qualified as LBS+import Data.ByteString.Short (ShortByteString)+import Data.ByteString.Short qualified as SBS+import Data.List qualified as List+import Data.Word (Word8)+import Prelude hiding (null)++-- | Stores 8-bit values.+type Octet = Word8++-- | Stores Transcoder as numeric values.+type BitSet = Integer++-- | Class of functions for working on binary transcoder streams, that is+-- streams of bytes that we can easily turn into a bitset, and can partition+-- into smaller streams for extracting and parsing data.+class (Monoid tc) => Transcoder tc where+  {-# MINIMAL+    totalOctets,+    swapOrder,+    packOctets,+    execBuilder,+    unpackValueCPU,+    unpackValueBE,+    unpackValueLE,+    unpackBuilder,+    unpackOctets,+    pushOctetTop,+    pushOctetEnd,+    spanOctetsTop,+    spanOctetsEnd,+    splitOffset,+    replicateOctet+    #-}++  -- | Gives back the length in octets of the Transcoder data.+  totalOctets :: tc -> Int++  -- | Reverses the byte order of the transcoder data.+  swapOrder :: tc -> tc++  -- | Packs Octet values into transcoder data `tc`.+  packOctets :: [Octet] -> tc++  -- | Executes the `Builder` into transcoder data.+  execBuilder :: Builder -> tc++  -- | Unpacks transcoder data into a list of bytes.+  unpackOctets :: tc -> [Octet]++  -- | Unpacks transcoder data as BitSet in the host CPU's endianness.+  unpackValueCPU :: tc -> BitSet++  -- | Unpacks transcoder data as BitSet in Big-Endian (BE) order.+  unpackValueBE :: tc -> BitSet++  -- | Unpacks transcoder data as BitSet in Little-Endian (LE) order.+  unpackValueLE :: tc -> BitSet++  -- | Places the contents of the transcoder into a ByteString `Builder`.+  unpackBuilder :: tc -> Builder++  -- | Prepends an octet value to the top of the transcoder data.+  pushOctetTop :: tc -> Octet -> tc++  -- | Appends the octet value to the end of the transcoder data.+  pushOctetEnd :: tc -> Octet -> tc++  -- | Parititions the binary data at+  splitOffset :: Int -> tc -> (tc, tc)++  -- | Repeats an octet value in binary data.+  replicateOctet :: Int -> Octet -> tc++  -- | Splits octet values based on the first false result from the predicate.+  spanOctetsTop :: (Octet -> Bool) -> tc -> (tc, tc)++  -- | Splits the list while the element predicate holds staring from the left+  -- side.+  spanOctetsEnd :: (Octet -> Bool) -> tc -> (tc, tc)++-- | Implements binary decoder for Bytestrings, best for constant values that+-- exist for the lifetime of the program.+-- https://hackage-content.haskell.org/package/bytestring-0.12.2.0/docs/Data-ByteString.html#g:2+instance Transcoder ByteString where+  totalOctets = Bytes.length+  swapOrder = Bytes.reverse+  execBuilder = LBS.toStrict . execBuilder+  packOctets = Bytes.pack+  unpackBuilder = Builder.byteString+  unpackValueCPU = unpackValueCPU . LBS.fromStrict+  unpackValueLE = unpackValueLE . LBS.fromStrict+  unpackValueBE = unpackValueBE . LBS.fromStrict+  unpackOctets = Bytes.unpack+  pushOctetTop = flip Bytes.cons+  pushOctetEnd = Bytes.snoc+  spanOctetsTop = Bytes.span+  spanOctetsEnd = Bytes.spanEnd+  splitOffset = Bytes.splitAt+  replicateOctet k = Bytes.replicate $ fromIntegral k++-- TODO: we can improve the proformance of unpackBits by examining the current+-- bytestring, and loading a range of 1-8 bytes to OR simulaneously++-- | Implements binary decoder for LazyByteString, best for very large data sets+-- that can be loaded in and out of memory on demand.+-- https://hackage-content.haskell.org/package/bytestring-0.12.2.0/docs/Data-ByteString-Lazy.html+instance Transcoder LazyByteString where+  totalOctets = fromIntegral . LBS.length+  swapOrder = LBS.reverse+  execBuilder = BSB.toLazyByteString+  packOctets = LBS.pack+  unpackOctets = LBS.unpack+  unpackValueLE =+    -- From MSB to LSB, OR the current byte and shift the total bits.+    LBS.foldr (\byte bits -> (bits .<<. 8) .|. fromIntegral byte) 0+      . LBS.dropWhileEnd (== 0)+  unpackValueBE =+    -- From LSB to MSB, OR the current byte and shift the total bits.+    LBS.foldl' (\bits byte -> (bits .<<. 8) .|. fromIntegral byte) 0+      . LBS.dropWhile (== 0)+  unpackBuilder = Builder.lazyByteString+  pushOctetTop = flip LBS.cons+  pushOctetEnd = LBS.snoc+  spanOctetsTop = LBS.span+  spanOctetsEnd = LBS.spanEnd+  splitOffset k = LBS.splitAt $ fromIntegral k+  replicateOctet k = LBS.replicate $ fromIntegral k+  unpackValueCPU = unpackValueLE++-- | Implements binary decoder for ShortByteString, best for compact data that+-- doesn't exist long in memory. Notably, it packs better in memory than normal+-- ByteString (prone to Heap Fragmentation).+-- https://hackage-content.haskell.org/package/bytestring-0.12.2.0/docs/Data-ByteString-Short.html#g:1+instance Transcoder ShortByteString where+  totalOctets = SBS.length+  swapOrder = SBS.reverse+  execBuilder = SBS.toShort . execBuilder+  packOctets = SBS.pack+  unpackOctets = SBS.unpack+  unpackValueLE =+    -- From MSB to LSB , OR the current byte and shift the total bits.+    SBS.foldr (\byte bits -> (bits .<<. 8) .|. fromIntegral byte) 0+      . SBS.dropWhileEnd (== 0)+  unpackValueBE =+    -- From LSB to MSB, OR the current byte and shift the total bits.+    SBS.foldl' (\bits byte -> (bits .<<. 8) .|. fromIntegral byte) 0+      . SBS.dropWhile (== 0)+  unpackBuilder = Builder.shortByteString+  pushOctetTop = flip SBS.cons+  pushOctetEnd = SBS.snoc+  spanOctetsTop = SBS.span+  spanOctetsEnd = SBS.spanEnd+  splitOffset = SBS.splitAt+  replicateOctet k = SBS.replicate $ fromIntegral k+  unpackValueCPU = unpackValueLE++-- | Takes a subsection of octets from the beginning of the transcoder data.+takeOctetsTop :: (Transcoder tc) => Int -> tc -> tc+takeOctetsTop idx = fst . splitOffset idx++-- | Removes octets from the beginning of transcoder data.+dropOctetsTop :: (Transcoder tc) => Int -> tc -> tc+dropOctetsTop idx = snd . splitOffset idx++-- | Removes octets from the end of transcoder data.+takeOctetsEnd :: (Transcoder tc) => Int -> tc -> tc+takeOctetsEnd idx tc = snd $ splitOffset (totalOctets tc - idx) tc++-- | Removes octets from the end of transcoder data.+dropOctetsEnd :: (Transcoder tc) => Int -> tc -> tc+dropOctetsEnd idx tc = fst $ splitOffset (totalOctets tc - idx) tc++-- | Implements `takeWhile` for byte data in binary transcoder.+takeWhileTopOctets :: (Transcoder tc) => (Octet -> Bool) -> tc -> tc+takeWhileTopOctets f = fst . spanOctetsEnd f++-- | Implements `dropWhile` for byte data in binary transcoder.+dropWhileTopOctets :: (Transcoder tc) => (Octet -> Bool) -> tc -> tc+dropWhileTopOctets f = snd . spanOctetsEnd f++-- | Implements `takeWhileEnd` for byte data in binary transcoder.+takeWhileEndOctets :: (Transcoder tc) => (Octet -> Bool) -> tc -> tc+takeWhileEndOctets f = snd . spanOctetsEnd f++-- | Implements `dropWhileEnd` for byte data in binary transcoder.+dropWhileEndOctets :: (Transcoder tc) => (Octet -> Bool) -> tc -> tc+dropWhileEndOctets f = fst . spanOctetsEnd f++-- | Creates a binary transcoder from a single byte.+{-# INLINE fromOctet #-}+fromOctet :: (Transcoder tc) => Octet -> tc+fromOctet w8 = packOctets [w8]++-- | Returns True if the binary transcoder data is empty.+{-# INLINE null #-}+null :: (Transcoder tc) => tc -> Bool+null tc = totalOctets tc == 0++-- | Returns the first byte removed from the transcoder data, if available.+{-# INLINE uncons #-}+uncons :: (Transcoder tc) => tc -> Maybe (Octet, tc)+uncons tc =+  let (start, rest) = splitOffset 1 tc+   in case unpackOctets start of+        [top] -> Just (top, rest)+        _ -> Nothing++-- | Returns the last byte removed from the transcoder data, if available.+{-# INLINE unsnoc #-}+unsnoc :: (Transcoder tc) => tc -> Maybe (tc, Octet)+unsnoc tc =+  let offset = totalOctets tc - 1+      (rest, taken) = splitOffset offset tc+   in case unpackOctets taken of+        [final] -> Just (rest, final)+        _ -> Nothing++-- | Packs a bit string value into some bytes data `tc`.+{-# INLINE packValueOrder #-}+packValueOrder ::+  (Integral val, Bits val, Transcoder tc) =>+  ([Octet] -> [Octet]) ->+  val ->+  tc+packValueOrder setorder intdata =+  -- This code can be optimized; if we precalculate the length of the BitSet+  -- in bits we can mask from the other direction using left shifts, and+  -- remove the call to setorder, maybe we can even use a list comprehension to+  -- build in place for packOctets+  packOctets+    . replaceNull [0]+    . setorder+    . map (\(_, byte) -> fromIntegral byte)+    . takeWhile (\(rest, byte) -> rest > 0 || byte > 0)+    $ iterateInit (\(input, _) -> extractByte input) (,0) intdata+  where+    -- Gets the byte from LSB in input and shifts the value 8 bits.+    extractByte input = (input .>>. 8, input .&. 0xFF)++-- | Packs a bit string value into some bytes data `tc` in Big-Endian format.+packValueBE :: (Integral val, Bits val, Transcoder tc) => val -> tc+packValueBE = packValueOrder reverse++-- | Packs a bit string value into some bytes data `tc` in Little-Endian+-- format.+packValueLE :: (Integral val, Bits val, Transcoder tc) => val -> tc+packValueLE = packValueOrder id++-- | Return the the first param if the second param is empty.+{-# INLINE replaceNull #-}+replaceNull :: [a] -> [a] -> [a]+replaceNull replace xs+  | List.null xs = replace+  | otherwise = xs++-- | Wraps iterate to compose with an intermediary type constructor.+iterateInit :: (b -> b) -> (a -> b) -> a -> [b]+iterateInit f iterinit = drop 1 . iterate f . iterinit