Binary-class: parsing and saving binary data
(require binary-class) | package: binary-class |
The binary-class combines binary-class/base, binary-class/common and binary-class/string.
1 Binary class. Base system.
(require binary-class/base) | package: binary-class |
This package is based upon idea in Practical Common Lisp.
Binary formats usually are represented as a sequence of fields. So the module allows to define classes with their binary types.
ID3/file identifier "ID3" |
ID3 version $02 00 |
ID3 flags %xx000000 |
ID3 size 4 * %0xxxxxxx |
(define-binary-class id3-tag ((file-identifier (iso-8859-1-bytes 3)) (major-version u1) (revision u1) (flags u1) (size id3-tag-size) (frames (id3-frames size))))
Here iso-8859-1-bytes should be a function of one argument, that returns structure binary. u1, id3-tag-size are simply such structures.
struct
(struct binary (read write) #:extra-constructor-name make-binary) read : (-> input-port? any)
write :
(or/c (->* (output-port? any/c) #:rest list? void?) (-> output-port? any/c void?))
Note, that you may use values of previous fields to calculate the type of the next. In the example the value of field size is used to set the type of field frames.
Another common case is "tagged structures". In that case there is a tag in the beginning of data. The structure of the rest of the data depends upon the value of the tag.
(define-binary-class id3-frame ((id (iso-8859-1-bytes 3)) (size u3)) #:dispatch (find-frame-class id))
Function find-frame-class should return the binary class for given id.
(define-binary-class id3-tag ((identifier (iso-8859-1-bytes 3)) (major-version u1) (revision u1) (flags u1) (size id3-tag-size)) #:dispatch (case major-version ((2) id3v2.2-tag) ((3) id3v2.3-tag)))
(define-binary-class id3v2.2-tag id3-tag ((frames (id3-frames size id3v2.2-frame))))
If you use #:dispatch, result class should be either inherited from current class, or at least to have all fields that the current class has. Super class of a binary class may be also not binary class, but then it should have no methods read and write.
syntax
(define-binary-class id [superclass-expr] ((field field-expr arg ...) ...) [#:dispatch dispatch-expr] class-body ...)
field = _ | id | (id ...)
superclass-expr : class?
field-expr : (or/c binary? (implementation?/c binary<%>))
dispatch-expr : (is-a?/c binary<%>)
field may be _. This means, that the field is omitted. In this case no field is created in class, but the data is read and is written from/to the binary port. Value for writing is #f.
field may be list of id’s. In this case binary, returned from field-expr should return the same number of values.
superclass-expr may be either id of a binary class, or any expression, returning non-binary class. If you return binary class from expression, then it is not error, but fields of given class will not be visible inside the current class field-exprs.
If field-expr returns class, implementing binary<%>, then provided arg’s will be used as init arguments to make-object, otherwise they ignored.
If field-expr returns not binary<%> neither binary, then the returned value assigned to field as is.
Binary class implements interface binary<%>:
|
method
in : input-port? Reads the object from in and returns it.
method
out : output-port? Writes the object to out
1.1 Utilities
To make the usage of the module easier there are some shortcuts for reading and writing binary values.
procedure
(read-value type in init-v ...) → any
type : any/c in : input-port? init-v : any/c
procedure
(write-value type out value) → void?
type : any/c out : output-port? value : any/c
2 Common datatypes
(require binary-class/common) | package: binary-class |
Most common data in binary file is integer numbers in little-endian or big-endian order, or bytestrings. So you may use them from this module.
procedure
(integer-be bytes [bits-per-byte]) → binary?
bytes : exact-positive-integer? bits-per-byte : exact-positive-integer? = 8
procedure
(integer-le bytes [bits-per-byte]) → binary?
bytes : exact-positive-integer? bits-per-byte : exact-positive-integer? = 8
procedure
base-type : (-> exact-positive-integer? exact-positive-integer? binary?) bytes : exact-positive-integer? bits-per-byte : exact-positive-integer? = 8
value
value
value
value
value
value
value
value
procedure
(bytestring bytes) → binary?
bytes : exact-positive-integer?
2.1 Control binary types
procedure
bytes : exact-positive-integer?
value
procedure
(move-position position) → binary?
position : exact-nonnegative-integer?
procedure
position : exact-nonnegative-integer? type : binary? init-v : any/c
3 Strings
(require binary-class/string) | package: binary-class |
In this module there are several binary types for reading and writing string?.
procedure
(generic-string length character-type) → binary?
length : exact-positive-integer? character-type : binary?
procedure
(generic-terminated-string terminator character-type) → binary? terminator : char? character-type : binary?
procedure
(iso-8859-1-string length) → binary?
length : exact-positive-integer?
procedure
(iso-8859-1-terminated-string [terminator]) → binary?
terminator : char? = #\nul
procedure
(ucs-2-string length) → binary?
length : exact-positive-integer?
procedure
(ucs-2-terminated-string [terminator]) → binary?
terminator : char? = #\nul
value
procedure
(ucs-2-char swap?) → binary?
swap? : boolean?
procedure
(ucs-2-char-type byte-order-marker) → binary?
byte-order-marker : (or/c 65279 65534)
4 Performance and safety
By default contracts in binary-class/* check only function arguments. If you need more security or more performance, you may use instead their submodules: Submodule safe gives maximum safety and contract checks, submodule unsafe gives maximum performance, but no check at all.
(require (submod binary-class safe)) | |
(require (submod binary-class unsafe)) | |
(require (submod binary-class/base safe)) | |
(require (submod binary-class/base unsafe)) | |
(require (submod binary-class/common safe)) | |
(require (submod binary-class/common unsafe)) | |
(require (submod binary-class/string safe)) | |
(require (submod binary-class/string unsafe)) |
5 Contracts
(require binary-class/contract) | package: binary-class |
This module introduces useful contracts for use with binary classes.
syntax
(binary-class/c binary-class-id maybe-opaque member-spec ...)
maybe-opaque =
| #:opaque member-spec = method-spec | (field field-spec ...) | (init field-spec ...) | (init-field field-spec ...) | (inherit method-spec ...) | (inherit-field field-spec ...) | (super method-spec ...) | (inner method-spec ...) | (override method-spec ...) | (augment method-spec ...) | (augride method-spec ...) | (absent absent-spec ...) method-spec = method-id | (method-id method-contract-expr) field-spec = field-id | (field-id contract-expr) absent-spec = method-id | (field field-id ...)
procedure
(unsigned-integer/c bytes [bits-per-byte]) → flat-contract?
bytes : exact-integer? bits-per-byte : exact-integer? = 8
procedure
(signed-integer/c bytes [bits-per-byte]) → flat-contract?
bytes : exact-integer? bits-per-byte : exact-integer? = 8
value
u1? : flat-contract?
value
u2? : flat-contract?
value
u3? : flat-contract?
value
u4? : flat-contract?
value
iso-8859-1-char? : flat-contract?
value
ucs-2-char? : flat-contract?
value
iso-8859-1-string? : flat-contract?
value
ucs-2-string? : flat-contract?
procedure
(iso-8859-1-len/c len) → flat-contract?
len : real?
procedure
(ucs-2-len/c len) → flat-contract?
len : real?
procedure
(iso-8859-1-terminated/c terminator) → flat-contract?
terminator : char?
procedure
(ucs-2-terminated/c terminator) → flat-contract?
terminator : char?
procedure
(string-terminated/c terminator [ char-contract]) → flat-contract? terminator : char? char-contract : flat-contract? = any/c