xcodec 

The xcodec Haskell library provides a type-class for generic programming on
bit data for writing encoders and decoders for codecs. The BinaryTranscoder
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.
Why?
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.
- Reading to and from numeric values for bitwise manipulation or initializing
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
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,
programmatically:
import Data.ByteString (ByteString)
import Data.ByteString.Short (ShortByteString)
import Data.ByteString.Lazy (LazyByteString)
import Data.Word (Word16)
import Data.XCodec.BinaryTranscoder (packValue)
-- Infers: packValue :: Int -> ByteString
exStrict :: ByteString
exStrict = packValue (0xdeadbeef :: Int)
-- Infers: packValue :: Integer -> LazyByteString
exLazy :: LazyByteString
exLazy = packValue (0xf0000000000000000000000d :: Integer)
-- Infers: packValue :: Word16 -> ShortByteString
exShort :: ShortByteString
exShort = packValue (0xcafe :: Word16)
And then serialize them through a common interface which makes it easy to
intersperse data from different formats:
import Data.ByteString.Builder qualified as Builder
import Data.ByteString.Lazy (LazyByteString)
import Data.XCodec.BinaryTranscoder (serializeValue)
-- 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
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:
import Data.Bits ((.>>.), (.&.))
import Data.Word (Word32)
import Data.XCodec.BinaryTranscoder (BinaryTranscoder, unpackValue)
-- We can easily read an entire bit-set representation of transcoder data,
-- directly from LazyByteString because it derives BinaryTranscoder. This
-- function produces its big-endian representation
largeBitSet :: Integer
largeBitSet = unpackValueBE exBuilder
-- Now, we can perform bitwise operations on the value, for instance, extracting
-- the lower bits 32nd-63rd bits from a little-endian value
extract32bits :: Word32
extract32bits = fromIntegral ((unpackValueLE exBuilder .>>. 32) .&. 0xFFFFFFFF)
Development
Unit tests are provided on the main ipfshs repo,
and bugs can be reported on ipfshs ticket tracker.
Licensing
The xcodec project and its modules are free software and licensed under the
BSD 3-clause license. See LICENSE.txt.
Copyright © 2026 Zoey McBride | zoeymcbride@mailbox.org