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-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
5 Fixup Ports
open-fixup-port
fixup-port?
push-fixup
pop-fixup
fixup-port-flush
7.7

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-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.

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.