binaryio:   Functions for Reading and Writing Binary Data
1 Bytes
read-bytes*
write-null-terminated-bytes
read-null-terminated-bytes
2 Integers
integer->bytes
bytes->integer
integer-bytes-length
integer-bytes-length<=?
write-integer
read-integer
3 Floating-point
write-float
read-float
4 Binary Reader
make-binary-reader
binary-reader?
make-binary-reader-error-handler
binary-reader-error-handler?
b-get-limit
b-at-limit?
b-at-limit/  eof?
b-push-limit
b-pop-limit
b-call/  save-limit
b-check-exhausted
b-read-bytes
b-read-bytes!
b-read-byte
b-read-integer
b-read-float
b-read-be-int
b-read-be-uint
b-read-le-int
b-read-le-uint
b-read-nul-terminated-bytes
b-read-bytes-line+  eol
b-read-bytes-line
5 Fixup Ports
open-fixup-port
fixup-port?
push-fixup
pop-fixup
fixup-port-flush
6 Short Bitvectors
SBV-LENGTH-BITS
SBV-LENGTH-BOUND
sbv?
canonical-sbv?
make-sbv
make-be-sbv
empty-sbv
sbv-empty?
sbv-length
sbv-bits
sbv-bit-field
sbv-slice
sbv-shift
sbv-bit-set?
sbv-ref
sbv-car
sbv-cdr
sbv-append
sbv-cons
sbv-reverse
sbv-prefix?
sbv->string
string->sbv
7 Bitports
output-bitport?
open-output-bitport
output-bitport-partial
output-bitport-write-bit
output-bitport-write-sbv
output-bitport-get-output
output-bitport-pad
bytes-bit-set?
8 Prefix Codes:   Encoding and Decoding
prefixcode-encode
prefixcode-encode!
prefixcode-build-decode-tree
prefixcode-decode
prefixcode-decode!
prefixcode-decode1
8.6

binaryio: Functions for Reading and Writing Binary Data

Ryan Culpepper <ryanc@racket-lang.org>

This library provides functions for reading, writing, and converting binary data. It is designed for the use case of implementing network protocols, although this library focuses on low-level support, not high-level protocol specification.

 (require binaryio) package: binaryio-lib

This module combines the exports of binaryio/bytes, binaryio/integer, and binaryio/float.

1 Bytes

 (require binaryio/bytes) package: binaryio-lib

procedure

(read-bytes* len [in])  bytes?

  len : exact-nonnegative-integer?
  in : input-port? = (current-input-port)
Like read-bytes, but returns a byte string of exactly len bytes. If fewer than len bytes are available before the end of input, an exception is raised.

Examples:
> (define in (open-input-bytes #"abcde"))
> (read-bytes* 4 in)

#"abcd"

> (read-bytes* 2 in)

read-bytes*: unexpected end of input

  tried to read: 2 bytes

  available: 1 bytes

  received: #"e"

procedure

(write-null-terminated-bytes bstr    
  [out    
  start    
  end])  void?
  bstr : bytes?
  out : output-port? = (current-output-port)
  start : exact-nonnegative-integer? = 0
  end : exact-nonnegative-integer? = (bytes-length bstr)
Writes bytes to out from bstr from index start (inclusive) to end (exclusive), and then writes a null (0) byte terminator.

If bstr contains any null bytes between start and end, an error is raised.

procedure

(read-null-terminated-bytes [in])  bytes?

  in : input-port? = (current-input-port)
Reads from in until a null (0) byte is found, then returns the bytes read, excluding the null terminator. If no null terminator is found before the end of input, an error is raised.

Examples:
> (define-values (in out) (make-pipe))
> (write-null-terminated-bytes #"abcde" out)
> (read-null-terminated-bytes in)

#"abcde"

2 Integers

 (require binaryio/integer) package: binaryio-lib

procedure

(integer->bytes val    
  size    
  signed?    
  [big-endian?    
  dest    
  start])  bytes?
  val : exact-integer?
  size : exact-positive-integer?
  signed? : boolean?
  big-endian? : boolean? = #t
  dest : (and/c bytes? (not/c mutable?)) = (make-bytes size)
  start : exact-nonnegative-integer? = 0
Like integer->integer-bytes, except that arbitrary size arguments are supported, and big-endian? defaults to #t (network byte order) rather than the host byte order.

Examples:
> (integer->bytes -1 3 #t)

#"\377\377\377"

> (integer->bytes (expt 23 31) 18 #f)

#"\22\305U2\375\302\332\26_\5\6\235q\30~\253\6\247"

procedure

(bytes->integer bstr    
  signed?    
  [big-endian?    
  start    
  end])  exact-integer?
  bstr : bytes?
  signed? : boolean?
  big-endian? : boolean? = #t
  start : exact-nonnegative-integer? = 0
  end : exact-nonnegative-integer? = (bytes-length bstr)
Like integer-bytes->integer, except that arbitrary sizes—that is, (- end start)are supported, and big-endian? defaults to #t (network byte order) rather than the host byte order.

procedure

(integer-bytes-length val signed?)  exact-nonnegative-integer?

  val : exact-integer?
  signed? : boolean?
Returns the number of bytes needed to encode val, including the sign bit if signed? is true.

Examples:
> (integer-bytes-length 127 #t)

1

> (integer-bytes-length 128 #t)

2

procedure

(integer-bytes-length<=? val nbytes signed?)  boolean?

  val : exact-integer?
  nbytes : exact-nonnegative-integer?
  signed? : boolean?
Equivalent to (<= (integer-bytes-length val signed?) nbytes), but can be faster for small values of nbytes.

procedure

(write-integer val    
  size    
  signed?    
  [out    
  big-endian?])  void?
  val : exact-integer?
  size : exact-positive-integer?
  signed? : boolean?
  out : output-port? = (current-output-port)
  big-endian? : boolean? = #t
Writes the encoding of val to out.

Equivalent to (write-bytes (integer->bytes val size signed? big-endian?) out).

procedure

(read-integer size signed? [in big-endian?])  exact-integer?

  size : exact-positive-integer?
  signed? : boolean?
  in : input-port? = (current-input-port)
  big-endian? : boolean? = #t
Reads size bytes from in and decodes it as an integer. If fewer than size bytes are available before the end of input, an error is raised.

Equivalent to (bytes->integer (read-bytes* size in) signed? big-endian?).

3 Floating-point

 (require binaryio/float) package: binaryio-lib

procedure

(write-float val size [out big-endian?])  void?

  val : real?
  size : (or/c 4 8)
  out : output-port? = (current-output-port)
  big-endian? : boolean? = #t
Equivalent to (write-bytes (real->floating-point-bytes val size big-endian?) out).

procedure

(read-float size [in big-endian?])  real?

  size : (or/c 4 8)
  in : input-port? = (current-input-port)
  big-endian? : boolean? = #t
Equivalent to (floating-point-bytes->real (read-bytes* size in) big-endian?).

4 Binary Reader

 (require binaryio/reader) package: binaryio-lib

Added in version 1.1 of package binaryio-lib.

procedure

(make-binary-reader in 
  [#:limit limit 
  #:error-handler error-handler]) 
  binary-reader?
  in : input-port?
  limit : (or/c exact-nonnegative-integer? #f) = #f
  error-handler : (binary-reader-error-handler? #f) = #f
Creates a new binary reader that reads from in with an initial limit of limit bytes.

The binary reader wraps the input port with the following additional features:
  • Convenience functions for reading binary encodings of integers and floating-point numbers of different lengths, endianness, etc.

  • The error-handler hook for customizing error message. See make-binary-reader-error-handler for details.

  • Automatic handling of short reads. If in returns eof or fewer bytes than requested in a read operation on the binary reader, the error-handler is used to raise an error. Thus, for example, the caller of (b-read-bytes br len) can rely on receiving a bytestring of exactly len bytes.

  • A stack of limits, maintained with b-push-limit and b-pop-limit. On every read operation, the limits are decremented by the number of bytes read. If a read operation requests more bytes than the current limit, the error-handler is used to raise an error.

Binary readers are not thread-safe. Be careful when interleaving uses of a binary reader with direct uses of its input port. For example, direct reads from in do not count against limits imposed on the binary reader.

procedure

(binary-reader? v)  boolean?

  v : any/c
Returns #t if v is a binary reader created by make-binary-reader, #f otherwise.

procedure

(make-binary-reader-error-handler 
  [#:error error-callback 
  #:show-data? show-data?-callback]) 
  binary-reader-error-handler?
  error-callback : (or/c #f (->* [binary-reader? symbol? string?] [] #:rest list? none/c))
   = #f
  show-data?-callback : (or/c #f (-> binary-reader? symbol? boolean?))
   = #f
Creates an error handler object for customizing the reporting of binary reader errors.

procedure

(binary-reader-error-handler? v)  boolean?

  v : any/c
Returns #t if v is an error handler object created by make-binary-reader-error-handler, #f otherwise.

procedure

(b-get-limit br)  (or/c exact-nonnegative-integer? #f)

  br : binary-reader?
Returns br’s current limit. The value #f means no limit; the idiom (or (b-get-limit br) +inf.0) may be useful for comparisons.

procedure

(b-at-limit? br)  boolean?

  br : binary-reader?
Returns #t if (b-get-limit br) is zero.

procedure

(b-at-limit/eof? br)  boolean?

  br : binary-reader?
Returns #t if (b-get-limit br) is zero or if peeking on the underlying input port returns eof.

procedure

(b-push-limit br limit)  void?

  br : binary-reader?
  limit : exact-nonnegative-integer?
Pushes a new limit on br. If the new limit is larger than the current limit, an exception is raised.

procedure

(b-pop-limit br)  void?

  br : binary-reader?
Pops the current limit from br and restores the previous limit, decremented by the number of bytes read since the current limit was pushed.

procedure

(b-call/save-limit br proc)  any

  br : binary-reader?
  proc : (-> any)
Saves the current limit stack of br and calls proc, restoring br’s limit stack when the call to proc returns normally or escapes.

Added in version 1.2 of package binaryio-lib.

procedure

(b-check-exhausted br what)  void?

  br : binary-reader?
  what : (or/c string? #f)
Raises an exception unless (b-at-limit/eof? br) is true. If what is a string, the text “after reading what” is included.

procedure

(b-read-bytes br len)  bytes?

  br : binary-reader?
  len : exact-nonnegative-integer?

procedure

(b-read-bytes! br bs [start end])  exact-nonnegative-integer?

  br : binary-reader?
  bs : bytes?
  start : exact-nonnegative-integer? = 0
  end : exact-nonnegative-integer? = (bytes-length bs)

procedure

(b-read-byte br)  byte?

  br : binary-reader?

procedure

(b-read-integer br size signed? [big-endian?])  exact-integer?

  br : binary-reader?
  size : exact-positive-integer?
  signed? : boolean?
  big-endian? : boolean? = #t

procedure

(b-read-float br size [big-endian?])  real?

  br : binary-reader?
  size : (or/c 4 8)
  big-endian? : boolean? = #t
Read operations on binary readers. Note that b-read-bytes etc never return eof; if fewer bytes than requested are available before the end of input, the binary reader’s short-read handler is called to raise an exception. If br’s current limit is smaller than the number of bytes requested, br’s long-read handler is called to raise an exception.

procedure

(b-read-be-int br size)  exact-integer?

  br : binary-reader?
  size : exact-positive-integer?

procedure

(b-read-be-uint br size)  exact-nonnegative-integer?

  br : binary-reader?
  size : exact-positive-integer?

procedure

(b-read-le-int br size)  exact-integer?

  br : binary-reader?
  size : exact-positive-integer?

procedure

(b-read-le-uint br size)  exact-integer?

  br : binary-reader?
  size : exact-positive-integer?
Specialized versions of b-read-integer for reading big-endian and little-endian encodings of signed and unsigned integers, respectively.

procedure

(b-read-nul-terminated-bytes br)  bytes?

  br : binary-reader?
Reads bytes until a NUL byte is found, and returns the accumulated bytes without the NUL terminator. If no NUL terminator is found before the current limit or the end of input is reached, the binary reader’s error handler is used to raise an error.

procedure

(b-read-bytes-line+eol br eol-mode)  
bytes? bytes?
  br : binary-reader?
  eol-mode : (or/c 'linefeed 'return 'return-linefeed 'any 'any-one)
Reades bytes until a line ending is found, as determined by eol-mode. Returns the line contents and the line ending as separate byte strings. If no line ending is found before the current limit or the end of input is reached, the binary reader’s error handler is used to raise an error.

Added in version 1.2 of package binaryio-lib.

procedure

(b-read-bytes-line br eol-mode)  bytes?

  br : binary-reader?
  eol-mode : (or/c 'linefeed 'return 'return-linefeed 'any 'any-one)
Like b-read-bytes-line+eol but does not return the line ending.

Added in version 1.2 of package binaryio-lib.

5 Fixup Ports

Added in version 1.1 of package binaryio-lib.

 (require binaryio/fixup-port) package: binaryio-lib

procedure

(open-fixup-port)  fixup-port?

Creates a new fixup port. A fixup port acts as an output port that writes to an internal buffer. It also supports a stack of fixupslocations in the output buffer where additional data must be inserted later. The primary use case for fixups is length-prefixed data, where the length is not known in advance.

Operations on fixup ports are not thread-safe.

procedure

(fixup-port? v)  boolean?

  v : any/c
Returns #t if v is a fixup port created by open-fixup-port, #f otherwise.

procedure

(push-fixup fp [size])  void?

  fp : fixup-port?
  size : (or/c exact-positive-integer? #f) = #f
Pushes a new fixup corresponding to the current location in the output buffer. If size is an integer, then size bytes are reserved for the fixup and its value must be exactly size bytes; otherwise the fixup’s value may be any size. (Sized fixups may have better performance than unsized fixups.)

procedure

(pop-fixup fp fixup)  void?

  fp : fixup-port?
  fixup : (-> exact-nonnegative-integer? bytes?)
Pops the current fixup and sets its value to the result of (fixup len), where len is the number of bytes (including subsequent fixup values) written to fp since the fixup was pushed.

If the fixup was created with size size, then (fixup len) must return exactly size bytes, otherwise an error is raised.

procedure

(fixup-port-flush fp out)  void?

  fp : fixup-port?
  out : output-port?
Writes the buffered output and fixups to out. There must be no pending fixups on fp; otherwise an exception is raised.

6 Short Bitvectors

Added in version 1.2 of package binaryio-lib.

A short bitvector is an immutable bitvector represented as a Racket exact nonnegative integer. The bitvector [b_0 b_1 b_2 ... b_L-1] is represented as the integer
(+ L
   (* b_0 (arithmetic-shift 1 (+ 0 SBV-LENGTH-BITS)))
   (* b_1 (arithmetic-shift 1 (+ 1 SBV-LENGTH-BITS)))
   (* b_2 (arithmetic-shift 1 (+ 2 SBV-LENGTH-BITS)))
   ...
   (* b_L-1 (arithmetic-shift 1 (+ L-1 SBV-LENGTH-BITS))))
where SBV-LENGTH-BITS is currently 16. That is, a bitvector is represented as a length field plus a shifted little-endian encoding of its bits (the first bit of the bitvector is represented by the least significant bit of the encoded number, before shifting).

Consequently, bitvectors up to about 46 bits are represented using fixnums (on 64-bit versions of Racket), and only bitvectors up to (sub1 (expt 2 SBV-LENGTH-BITS)) bits are representable.

Number of bits used to represent the length of a bitvector.

Bound for representable bitvector lengths. Specifically, a length must be strictly less than SBV-LENGTH-BOUND to be representable.

procedure

(sbv? v)  boolean?

  v : any/c
Returns #t if v represents a short bitvector, #f otherwise.

Equivalent to exact-nonnegative-integer?. See also canonical-sbv?.

procedure

(canonical-sbv? v)  boolean?

  v : any/c
Returns #t if v is a canonical representation of a short bitvector, #f otherwise.

For example, (make-sbv #b1011 2) is not canonical because it has a bit set after the first two bits.

Warning: In general, the functions in this library may produce bad results if given non-canonical bitvector values.

procedure

(make-sbv le-bits bitlength)  sbv?

  le-bits : exact-nonnegative-integer?
  bitlength : exact-nonnegative-integer?
Returns #t if v is a short bitvector, #f otherwise. The number le-bits is interpreted in a little-endian fashion: the first bit of the bitvector is the least significant bit of le-bits.

If le-bits has a bit set after the first bitlength bits, then the result is non-canonical (see canonical-sbv?). If bitlength is not less then SBV-LENGTH-BOUND, an error is raised.

Example:
> (sbv->string (make-sbv 6 3))

"011"

procedure

(make-be-sbv be-bits bitlength)  sbv?

  be-bits : exact-nonnegative-integer?
  bitlength : exact-nonnegative-integer?
Like make-sbv, but interprets be-bits in a big-endian fashion: the first bit of the bitvector is the most significant bit of be-bits (interpreted as a bitlength-bit number).

Equivalent to (sbv-reverse (make-sbv be-bits bitlength)).

Example:
> (sbv->string (make-be-sbv 6 3))

"110"

value

empty-sbv : sbv? = (make-sbv 0 0)

The empty bitvector.

procedure

(sbv-empty? sbv)  boolean?

  sbv : sbv?
Returns #t if v is the empty bitvector, #f otherwise.

procedure

(sbv-length sbv)  exact-nonnegative-integer?

  sbv : sbv?
Returns the length of the bitvector.

procedure

(sbv-bits sbv)  exact-nonnegative-integer?

  sbv : sbv?
Returns the little-endian encoding of the bitvector’s bits.

Example:
> (sbv-bits (string->sbv "1011"))

13

procedure

(sbv-bit-field sbv start end)  exact-nonnegative-integer?

  sbv : sbv?
  start : exact-nonnegative-integer?
  end : exact-nonnegative-integer?
Returns the little-endian encoding of the bitvector’s bits from start (inclusive) to end (exclusive).

If end is greater than (sbv-length sbv), then the “out of range” bits are set to zero.

Examples:
> (sbv-bit-field (string->sbv "11100") 1 4)

3

> (sbv-bit-field (string->sbv "11100") 1 10)

3

procedure

(sbv-slice sbv start end)  sbv?

  sbv : sbv?
  start : exact-nonnegative-integer?
  end : exact-nonnegative-integer?
Returns the bitvector containing the subsequence of bits from sbv from indexes start (inclusive) to end (exclusive).

If end is greater than (sbv-length sbv), then the “out of range” bits are set to zero.

Examples:
> (sbv->string (sbv-slice (string->sbv "11100") 1 4))

"110"

> (sbv->string (sbv-slice (string->sbv "11100") 1 10))

"110000000"

procedure

(sbv-shift sbv lshift)  sbv?

  sbv : sbv?
  lshift : exact-integer?
If lshift is positive, adds lshift zero bits to the beginning of the bitvector. If lshift is negative, removes (- lshift) bits from the beginning of the bitvector.

Examples:
> (sbv->string (sbv-shift (string->sbv "11100") 3))

"00011100"

> (sbv->string (sbv-shift (string->sbv "11100") -2))

"100"

procedure

(sbv-bit-set? sbv index)  boolean?

  sbv : sbv?
  index : exact-nonnegative-integer?
Returns #t if the bit at index index of sbv is set (1), #f if it is unset (0). If index is not less than (sbv-length sbv), and sbv is canonical, then #f is returned.

Examples:
> (sbv-bit-set? (string->sbv "1101") 0)

#t

> (sbv-bit-set? (string->sbv "1101") 1)

#t

> (sbv-bit-set? (string->sbv "1101") 2)

#f

procedure

(sbv-ref sbv index)  (or/c 0 1)

  sbv : sbv?
  index : exact-nonnegative-integer?
Like sbv-bit-set?, but returns the bit at the given index.

Examples:
> (sbv-ref (string->sbv "1101") 0)

1

> (sbv-ref (string->sbv "1101") 1)

1

> (sbv-ref (string->sbv "1101") 2)

0

procedure

(sbv-car sbv)  (or/c 0 1)

  sbv : sbv?

procedure

(sbv-cdr sbv)  sbv?

  sbv : sbv?
Returns the first bit of the bitvector and the rest of the bitvector, respectively.

Equivalent to (sbv-ref sbv 0) and (sbv-shift sbv -1).

Examples:
> (sbv-car (string->sbv "110"))

1

> (sbv->string (sbv-cdr (string->sbv "110")))

"10"

procedure

(sbv-append sbv ...)  sbv?

  sbv : sbv?
Returns the concatenation of the given bitvectors.

Example:
> (sbv->string (sbv-append (string->sbv "1011") (string->sbv "00")))

"101100"

procedure

(sbv-cons bit sbv)  sbv?

  bit : (or/c 0 1)
  sbv : sbv?
Adds a bit to the beginning of the bitvector.

Equivalent to (sbv-append (make-sbv bit 1) sbv).

procedure

(sbv-reverse sbv)  sbv?

  sbv : sbv?
Reverses the bits of the bitvector.

Example:
> (sbv->string (sbv-reverse (string->sbv "1101")))

"1011"

procedure

(sbv-prefix? sbv1 sbv2)  boolean?

  sbv1 : sbv?
  sbv2 : sbv?
Returns #t if sbv1 is a prefix of sbv2.

Examples:
> (sbv-prefix? (string->sbv "110") (string->sbv "110"))

#t

> (sbv-prefix? (string->sbv "110") (string->sbv "1100"))

#t

> (sbv-prefix? (string->sbv "110") (string->sbv "100110"))

#f

> (sbv-prefix? (string->sbv "110") (string->sbv "11"))

#f

procedure

(sbv->string sbv)  string?

  sbv : sbv?
Returns a string of 0 and 1 characters representing the bits of sbv.

Example:
> (sbv->string (sbv-append (make-sbv 1 1) (make-sbv 1 1) (make-sbv 0 1)))

"110"

If sbv is canonical, then sbv is equal to

procedure

(string->sbv s)  sbv?

  s : (and/c string? #rx"^[01]*$")
Parses s, which should consist of 0 and 1 characters, and returns the corresponding bitvector.

7 Bitports

Added in version 1.2 of package binaryio-lib.

An output bitport is like an output string port (open-output-bytes), except that instead of accumulating bytes, it accumulates bits and packs them into a byte string.

procedure

(output-bitport? v)  boolean?

  v : any/c
Returns #t if v is an output bitport, #f otherwise.

Creates a new empty output bitport.

procedure

(output-bitport-partial bp)  sbv?

  bp : output-bitport?
Returns bp’s current partial bitvector, consisting of the last bits written (between 0 and 7, inclusive) that have not yet been packed into a byte.

procedure

(output-bitport-write-bit bp bit)  void?

  bp : output-bitport?
  bit : (or/c 0 1)
Writes a single bit to bp.

Equivalent to (output-bitport-write-sbv (make-sbv bit 1)).

procedure

(output-bitport-write-sbv bp sbv)  void?

  bp : output-bitport?
  sbv : sbv?
Writes the bitvector sbv to bp.

procedure

(output-bitport-get-output bp 
  [#:reset? reset? 
  #:pad pad-sbv]) 
  
bytes? exact-nonnegative-integer?
  bp : output-bitport?
  reset? : boolean? = #f
  pad-sbv : sbv? = empty-sbv
Returns (values output bits-written), where output is the accumulated output of bp as a big-endian byte string and bits-written is the number of bits written to bp. The byte string is big-endian in the following sense: the first bit written is the most significant bit of the first byte in the byte string.

If bp contains an incomplete byte (because bits-written is not divisible by 8), then the final byte of the output is padded with the lower bits of pad-sbv (extended with zeros if more padding bits are needed). If bits-written is divisible by 8, no padding is included.

If reset? is true, then all written bits are removed from bp.

procedure

(output-bitport-pad bp [#:pad pad-sbv])

  exact-nonnegative-integer?
  bp : output-bitport?
  pad-sbv : sbv? = 0
Pads the bits written to bp to a whole byte, using the lower bits of pad-sbv. If the number of bits written to bp is divisible by 8, then no padding is done. The result is the number of padding bits added (between 0 and 7, inclusive).

procedure

(bytes-bit-set? bs bit-index)  boolean?

  bs : bytes?
  bit-index : exact-nonnegative-integer?
Returns #t if the bit at bit-index is set in bs, #f otherwise.

The byte string is interpreted as big-endian in the following sense: within a single byte in bs, bits are indexed started with the most significant bit first. So for example, (bytes-bit-set? (bytes b) 0) is (bitwise-bit-set? b 7).

Examples:
> (bytes-bit-set? (bytes 1 128) 0)

#f

> (bytes-bit-set? (bytes 1 128) 7)

#t

> (bytes-bit-set? (bytes 1 128) 8)

#t

> (bytes-bit-set? (bytes 1 128) 15)

#f

8 Prefix Codes: Encoding and Decoding

Added in version 1.2 of package binaryio-lib.

This module provides encoding and decoding support using prefix codes (aka prefix-free codes), including Huffman codes. It does not implement algorithms for building such codes.

procedure

(prefixcode-encode encode-table 
  input 
  [#:pad pad-sbv]) 
  
bytes? exact-nonnegative-integer?
  encode-table : 
(or/c (hash/c any/c sbv?)
      (listof (cons/c any/c sbv?))
      (vectorof sbv?))
  input : sequence?
  pad-sbv : sbv? = empty-sbv
Encodes the values of input using encode-table, which represents the prefix code as a mapping of values to short bitvectors. The input can be any sequence, including strings, byte strings, lists, and so on. The result is a byte string containing the encoded bits, along with the number of bits in the encoding. See output-bitport-get-output for the format of the encoded byte string and the interpretation of the pad-sbv argument.

The encode-table must be one of the following:
Each code in the table must be unique, and the set of codes must form a valid prefix code. Otherwise, the results of encoding and decoding are unpredictable.

Examples:
> (require binaryio/examples/hpack)
> (define-values (enc enc-bits)
    (prefixcode-encode hpack-encode-table #"hello world!" #:pad hpack-end-code))
> (values enc enc-bits)

#"\234\264Pu<\36\312$\376?"

74

procedure

(prefixcode-encode! bp    
  encode-table    
  input    
  [#:pad pad-sbv])  void?
  bp : output-bitport?
  encode-table : 
(or/c (hash/c any/c sbv?)
      (listof (cons/c any/c sbv?))
      (vectorof sbv?))
  input : sequence?
  pad-sbv : sbv? = empty-sbv
Like prefixcode-encode, but writes the encoded bits to bp.

procedure

(prefixcode-build-decode-tree encode-table)  any/c

  encode-table : 
(or/c (hash/c any/c sbv?)
(listof (cons/c any/c sbv?))
(vectorof sbv?))
Converts encode-table (see prefixcode-encode) into a data structure suitable for decoding the same prefix code.

The representation is not specified, but if all values in the table are readable (or quotable), then the representation of the decoder tree is readable (or quotable).

Example:
> (define hpack-decode-tree (prefixcode-build-decode-tree hpack-encode-table))

procedure

(prefixcode-decode decode-tree    
  bs    
  [start-bit-index    
  end-bit-index    
  #:end end-code    
  #:handle-error handle-error])  bytes?
  decode-tree : any/c
  bs : bytes?
  start-bit-index : exact-nonnegative-integer? = 0
  end-bit-index : exact-nonnegative-integer?
   = (* 8 (bytes-length bs))
  end-code : (or/c sbv? #f) = #f
  handle-error : 
(-> (or/c 'bad 'incomplete)
    exact-nonnegative-integer?
    exact-nonnegative-integer?
    sbv?
    any)
   = (lambda (mode start end code) (error ....))
Decodes bs (from start-bit-index, inclusive, to end-bit-index, exclusive) using the prefix code represented by decode-tree, which must have been produced by prefixcode-build-decode-tree.

Each value represented by decode-tree must be a byte, character, byte string, or character string. Each decoded value is accumulated into a byte string, which is the result of successful decoding.

If the decoder encounters a sequence of bits that is not a valid code prefix, it calls

(handle-error 'bad bad-start-index bad-end-index bad-code)

to handle the error. If the decoder reaches end-bit-index without completing the current code, and if end-code is #f, then it handles the error by calling

(handle-error 'incomplete incomplete-start-index end-bit-index incomplete-code)

But if end-code is a bitvector, then no error is signaled if the bits between the end of the last complete code and end-bit-index are a prefix of end-code.

Note that if handle-error returns normally, its result is discarded, so it is recommended that handle-error escape (for example, by raising an exception).

Examples:
> (prefixcode-decode hpack-decode-tree enc 0 enc-bits)

#"hello world!"

> (prefixcode-decode hpack-decode-tree enc #:end hpack-end-code)

#"hello world!"

> (prefixcode-decode hpack-decode-tree enc #:handle-error list)

#"hello world!"

procedure

(prefixcode-decode! output 
  decode-tree 
  bs 
  [start-bit-index 
  end-bit-index 
  #:end end-code 
  #:handle-error handle-error]) 
  (or/c void? any)
  output : (or/c output-port? (-> any/c void?))
  decode-tree : any/c
  bs : bytes?
  start-bit-index : exact-nonnegative-integer? = 0
  end-bit-index : exact-nonnegative-integer?
   = (* 8 (bytes-length bs))
  end-code : (or/c sbv? #f) = #f
  handle-error : 
(-> (or/c 'bad 'incomplete)
    exact-nonnegative-integer?
    exact-nonnegative-integer?
    sbv?
    any)
   = (lambda (mode start end code) (error ....))
Like prefixcode-decode, but each decoded value is sent to output, and the result of a successful decoding is (void).

If output is an output port, then a decoded value must be a byte, character, byte string, or character string, and the value is emitted by writing it to the port. If output is a procedure, then any value is allowed, and the value is emitted by calling output on it.

If decoding completes successfully, the result is (void); otherwise, it is the result of the call to handle-error.

Examples:
> (call-with-output-bytes
    (lambda (out)
      (prefixcode-decode! out hpack-decode-tree enc 0 enc-bits)))

#"hello world!"

> (call-with-output-bytes
    (lambda (out)
      (prefixcode-decode! out hpack-decode-tree enc 0 #:end hpack-end-code)))

#"hello world!"

> (prefixcode-decode! void hpack-decode-tree enc #:handle-error list)

'(incomplete 74 80 4128774)

procedure

(prefixcode-decode1 decode-tree 
  bs 
  [start-bit-index 
  end-bit-index 
  #:end end-code]) 
  
(or/c 'ok 'bad 'end 'incomplete)
exact-nonnegative-integer?
any/c
  decode-tree : any/c
  bs : bytes?
  start-bit-index : exact-nonnegative-integer? = 0
  end-bit-index : exact-nonnegative-integer?
   = (* 8 (bytes-length bs))
  end-code : (or/c sbv? #f) = #f
Like prefixcode-decode, but decodes a single value from the input. The result is one of the following:
  • (values 'ok next-bit-index value) the bits from start-bit-index to next-bit-index represent the value value

  • (values 'bad next-bit-index bad-code) the bits from start-bit-index to next-bit-index do not represent a valid code (or its prefix); bad-code contains those bits as a bitvector

  • (values 'end next-bit-index incomplete-code) the bits from start-bit-index to next-bit-index represent an incomplete prefix of end-code; incomplete-code contains those bits as a bitvector

  • (values 'incomplete next-bit-index incomplete-code) the bits from start-bit-index to next-bit-index represent an incomplete code, but it is not a prefix of end-code; incomplete-code contains those bits as a bitvector

Examples:
> (prefixcode-decode1 hpack-decode-tree enc 0)

'ok

6

104

> (prefixcode-decode1 hpack-decode-tree enc 6)

'ok

11

101

> (bytes 104 101)

#"he"