chickadee » cbor

cbor

Concise Binary Object Representation (CBOR) for CHICKEN 6.

CBOR (RFC 8949) is a compact binary data format with a JSON-like data model. The cbor library provides routines to read and write CBOR data on binary ports. It maps Scheme values to CBOR data items and back, and it writes SRFI-4 numeric vectors as typed arrays (RFC 8746): one block of raw bytes, with no per-element conversion.

The design follows the Haskell cborg library: a low-level token layer, a generic term tree, and a mapping for native values on top.

Modules

cbor
Scheme values to and from CBOR. Most programs need only this module.
cbor-core
Low-level encoding and decoding of individual tokens.
cbor-term
A generic tree that mirrors a CBOR data item exactly, and diagnostic notation.
cbor-token
CBOR as a flat list of tokens, for testing and inspection.
cbor-deflate
Compressed data items, using the bundled miniz.

Module cbor

Reading and writing

write-cbor OBJ #!optional PORTprocedure

Writes OBJ to PORT as one CBOR data item. PORT must accept binary output.

read-cbor #!optional PORTprocedure

Reads one data item from PORT. Returns an eof object if PORT is already at its end.

cbor->bytevector OBJprocedure
bytevector->cbor BYTEVECTORprocedure

Encode to, and decode from, a bytevector. bytevector->cbor signals an error if the bytevector holds anything after the first data item.

write-cbor-file PATH OBJprocedure
read-cbor-file PATHprocedure

Write and read a file holding one data item. The file starts with the self-describe tag 55799 (the bytes d9 d9 f7), which marks it as CBOR.

call-with-cbor-output-file PATH PROCprocedure
call-with-cbor-input-file PATH PROCprocedure

Open PATH as a binary port and call PROC with the port. The output version writes the self-describe tag first; PROC should then write exactly one data item.

write-cbor-sequence LIST #!optional PORTprocedure
read-cbor-sequence #!optional PORTprocedure

Write each element of LIST as a separate data item, and read all items up to the end of input, as a CBOR sequence (RFC 8742).

encode-value OBJ PORTprocedure
decode-value DECODERprocedure

The same mapping as write-cbor and read-cbor, for use together with the token-level procedures of cbor-core.

cbor-preferred-floatsparameter

When true, flonums are written in the shortest float width that holds them exactly (half, single or double). The default, #f, always writes doubles.

The mapping

|| Scheme || CBOR || | exact integer | major type 0 or 1; beyond 64 bits, bignum tags 2 and 3 | | flonum | double (or shortest exact width, see above) | | exact rational | tag 30, [numerator, denominator] | | #t, #f | true, false | | the void value | undefined | | cbor-null | null | | string | text string | | symbol | tag 39 (identifier) with a text string | | proper list | array | | improper list or pair | tag tag-scheme-pair, [items..., tail] | | vector | tag tag-scheme-vector with an array | | bytevector (also blob, u8vector) | byte string | | s8, u16, s16, u32, s32, u64, s64, f32 and f64 vectors | RFC 8746 typed array, in host byte order | | character | tag tag-scheme-char with its code point | | keyword | tag tag-scheme-keyword with a text string | | SRFI-69 hash table | map (read back with equal? hashing) | | cbor-tagged record | the given tag and value | | cbor-simple record | the given simple value |

When reading, maps become SRFI-69 hash tables, typed arrays in either byte order become SRFI-4 vectors (half-precision arrays become f32vectors), the self-describe tag is skipped, and tags without a codec become cbor-tagged records. Circular lists and cyclic vectors cannot be written.

The private tags tag-scheme-pair, tag-scheme-vector, tag-scheme-char and tag-scheme-keyword lie in the first-come-first-served range of the IANA tag registry.

cbor-nullconstant
cbor-null? OBJprocedure

The CBOR null value.

make-cbor-tagged TAG VALUEprocedure
cbor-tagged? OBJprocedure
cbor-tagged-tag TAGGEDprocedure
cbor-tagged-value TAGGEDprocedure

A tagged item whose tag has no codec.

make-cbor-simple Nprocedure
cbor-simple? OBJprocedure
cbor-simple-value SIMPLEprocedure

A simple value other than false, true, null and undefined.

Codecs

A codec adds a Scheme type, tied to a CBOR tag.

make-cbor-codec TAG PREDICATE ENCODER DECODERprocedure

PREDICATE recognizes values of the type. ENCODER is called with three arguments: the value, a procedure that writes a nested Scheme value, and the output port (for writing tokens directly with cbor-core). It must write exactly one data item, the content of the tag; the tag itself is written before it. DECODER is called with the decoded content and returns the value.

register-cbor-codec! CODECprocedure

Adds CODEC to the global registry, ahead of earlier codecs.

cbor-codecsparameter

A list of codecs that take priority over the global registry, for use with parameterize. Built-in types are always handled first.

(define-record-type point (make-point x y) point? (x point-x) (y point-y))

(register-cbor-codec!
 (make-cbor-codec 1000001 point?
                  (lambda (p emit port) (emit (list (point-x p) (point-y p))))
                  (lambda (content) (apply make-point content))))

Limits and errors

cbor-max-depthparameter

Deepest nesting of arrays, maps and tags accepted when reading, and written when encoding (default 1024).

cbor-max-lengthparameter

Largest string (in bytes) or array or map (in items) accepted when reading, or #f for no limit (the default). Very long byte strings are read in two steps, so a forged length in a small input cannot force a large allocation.

Decoding errors signal a condition of kinds exn and cbor. The cbor part has the properties reason (one of eof, malformed, type, limit, utf8) and offset (the byte position in the input).

Module cbor-core

Encoders take the value first and an optional output port: encode-head, encode-uint, encode-nint, encode-int, encode-bytes (with optional start and end), encode-bytes-begin, encode-string, encode-string-begin, encode-list-len, encode-list-begin, encode-map-len, encode-map-begin, encode-tag, encode-bool, encode-null, encode-undefined, encode-simple, encode-float16, encode-float32, encode-float64, encode-float (shortest exact width), encode-break and encode-encoded (pre-encoded bytes).

Decoders take a decoder, made with (make-decoder [PORT]): peek-token-type, decode-uint, decode-nint, decode-int, decode-integer (also bignums), decode-bytes, decode-string, decode-bytes-indef, decode-string-indef, decode-list-len, decode-list-len-indef, decode-list-len-or-indef, decode-map-len, decode-map-len-indef, decode-map-len-or-indef, decode-tag, decode-bool, decode-null, decode-undefined, decode-simple, decode-float, decode-float-token, decode-break, decode-break-or and decode-skip.

peek-token-type returns one of the symbols uint, nint, bytes, bytes-indef, string, string-indef, list-len, list-len-indef, map-len, map-len-indef, tag, bool, null, undefined, simple, float16, float32, float64, break, invalid or eof.

Byte and text strings are decoded whole, whether definite or indefinite. Decoders that build nested items call decoder-enter! and decoder-leave! around each level so that cbor-max-depth applies.

Module cbor-term

decode-term DECODERprocedure
encode-term TERM #!optional PORTprocedure

Terms are a datatype with the variants term-int, term-bytes, term-bytes-indef, term-string, term-string-indef, term-list, term-list-indef, term-map, term-map-indef (pairs as (key . value)), term-tagged, term-bool, term-null, term-undefined, term-simple, term-half, term-float and term-double. Terms keep float widths and indefinite lengths, so re-encoding a decoded term gives back the original bytes whenever they were in preferred form.

term->diagnostic TERMprocedure

Returns the diagnostic notation of RFC 8949 section 8, for example "[1, [2, 3]]".

term=? TERM1 TERM2procedure

Structural equality that treats NaN as equal to itself.

Module cbor-token

A datatype of tokens (tk-uint, tk-nint, tk-bytes, tk-bytes-begin, tk-string, tk-string-begin, tk-list-len, tk-list-begin, tk-map-len, tk-map-begin, tk-tag, tk-bool, tk-null, tk-undefined, tk-simple, tk-float16, tk-float32, tk-float64, tk-break), with write-token, write-tokens, read-token, read-tokens and token->list.

Module cbor-deflate

Loading this module registers a compressed-item tag, tag-compressed. Its content is the array [1, length, data]: 1 names zlib (RFC 1950), length is the size of the uncompressed encoding, and data is a byte string holding the compressed encoding of one data item. Writers emit the data as an indefinite-length byte string of 1 MB chunks, so compression runs in bounded memory. read-cbor expands compressed items transparently.

cbor-deflated OBJprocedure

Wraps OBJ so that it is written compressed. The wrapper may appear anywhere inside a larger value.

write-cbor/compressed OBJ #!optional PORTprocedure

Writes OBJ as one compressed data item.

cbor-deflate-levelparameter

Compression level from 0 to 10 (default 6; 1 is fastest).

deflate-bytevector BYTEVECTORprocedure
inflate-bytevector BYTEVECTOR LENGTHprocedure

Compress to, and decompress from, a zlib stream.

Examples

(import cbor (srfi 4))

(write-cbor-file "weights.cbor"
                 `((name . "layer1")
                   (shape 256 784)
                   (data . ,(make-f32vector (* 256 784) 0.5))))

(define checkpoint (read-cbor-file "weights.cbor"))
(cdr (assq 'shape checkpoint))            ; => (256 784)

Python can read the same file with cbor2; the typed array comes back as a tagged byte string that numpy.frombuffer turns into an array.

Author

Ivan Raikov

License

MIT

Version history

1.0
Initial release

Contents »