binrep-0.8.0: Encode precise binary representations directly in types
Safe HaskellSafe-Inferred
LanguageGHC2021

Binrep

Description

Top-level binrep module, exporting all classes, generics & runners.

binrep helps you precisely model binary schemas by combining simple "building blocks" (e.g. NullTerminated a) in regular Haskell types. You can then receive high-performance serializers and parsers for free via generics.

binrep is not a general-purpose parsing/serializing library. For that, see

  • mason, for fast and flexible serializing
  • flatparse, for extremely performant parsing
  • bytezap, for overly-fast serializing and parsing (but very limited)
Synopsis

Class and instance design

At the core of binrep are a set of classes defining parsers, serializers, and serialized length checkers on supported types. binrep is its own ecosystem where explicitness and correctness win over all:

  • there are no binrep instances for Void or V1 because we can't use them; rather than providing an absurd, possibly convenient instance, we emit a type error for their attempted use.
  • you can't put/get Word32s etc by themselves; you must provide endianness information via the ByteOrdered newtype
  • Get ByteString just consumes the whole input. seem weird? it works with the combinators (it's actually rather important)

Here are some important design decisions:

  • Fields in product types are concatenated left-to-right. e.g. Put (l, r) first puts l, then r. Nothing is placed between them.
  • Sum types must define how to handle the constructor sum. Generics are split into sum handlers and non-sum handlers. binrep instances are not provided for types such as Either a b, where we can't state how to choose between the Left and Right constructors.
  • Refined (pl And pr) a is re-associated to Refined pr (Refined pl a). The single layer of refinements is ergonomic, but the way binrep instances work means we need the latter. So And instances essentially rewrite themselves to work as if it were a stack of refinements. (See NullTermPadded for an example.)

Struct parsing & serializing

There are experimental "struct" handlers, which only work on data types that look like C structs. That is,

  • every field must be constant length, and
  • no sums allowed.

The underlying runners for these are even faster-- they shouldn't do much more work than the code a C compiler would generate for a similar struct. But they are very inflexible (few binrep instances, hard to write by hand) and poorly tested. Please be warned when using them. (And do consider sending bug reports to the author!)

module Binrep.Put

module Binrep.Get