(binary data) - Binary data read/write

Library (binary data)

This library provides yet another binary data structure read/write. The difference between (binary pack) and this is the level of abstraction. This library provides higher abstraction layer of the way how to handle binary data.

Macro define-simple-datum-define name reader writer
Macro define-composite-data-define name reader writer

Defines a macro named name to read binary data by generic method reader and write binary data by writer.

To use defining macros effectively, both forms' reader and _writer_should be the same name.

The first form defines a macro which takes 5 required arguments and optional keyword arguments. Let's say the name is define-simple, the reader is simple-read and the write is simple-write, Then the definition of defined macro would be like this;

Macro define-simple name parents slots read-proc write-proc :key (parent-metaclass )

Defines name class whose parent classes are parents and slots are slots.

read-proc must be a procedure which takes one argument, a input port and return number of slots values.

write-proc must be a procedure which takes number of slots plus 1 arguments. The first one is an output port and the rest are the value of the slots of the defined class' instance.

The keyword argument parent-metaclass is a parent class of the metaclass of this class.

The slots form must be a list of slot definitions which must be followings;

The first and second form define a slot which name is name and its initial value is #f. The third form defines a slot which name is _name_and its initial is default.

Note that current implemenation does not handle parent classes slots. This is only for seamless operations with other CLOS class.

The second form defines a macro which takes 3 required arguments and optional keyword arguments. Let's say the name is define-composite, the reader is simple-read and the write is simple-write, Then the definition of defined macro would be like this;

Macro define-simple name parents slots :key (parent-metaclass )

Defines a composite data class named name whose parent classes are parents and slots are slots.

It is similar form with define-class however slots must be a list of one of the followings.

name must be a symbol which represents the slot name.

type can be class name or eqv? comparable datum. e.g. keyword.

default can be any object.

count must be a non negative exact integer.

The first form is equivalent with the following form; (name type #f). And the third form is equivalent with the following form; (name (type count) #f).

The first 2 forms defines a datum slot which the datum is read by _reader_passing type and written by writer.

The rest forms defines an array data represented by a vector.

If the type is not defined by neither of the definition forms, then it is users responsibility to define a method which handles the type.

Following is the simple example to show how to use the macros above.

(import (clos user) (binary data))

;; use the same name of reader and writer
(define-simple-datum-define   define-simple    sample-read sample-write)
(define-composite-data-define define-composite sample-read sample-write)

(define-simple <simple> ()
  (a b (c 0))
  (lambda (in) (values (get-u8 in) (get-u8 in) (get-u8 in)))
  (lambda (out a b c) (put-u8 out a) (put-u8 out b) (put-u8 out c)))

(define-composite <composite> ()
  ((d :byte 1)
   (e (:byte 4) #vu8(1 2 3 4))
   (f <simple>)))

;; :byte reader and writer
(define-method sample-read ((o (eql :byte)) in array-size?)
  (if array-size?
      (get-bytevector-n in array-size?)
      (get-u8 in)))

(define-method sample-write ((type (eql :byte)) o out array-size?)
  (if array-size?
     (put-bytevector out o)
     (put-u8 out o)))

How to use the defined data structure.

;; read as a <composite> object
;; "deeeeabc" in ascii
(define bv #vu8(#x64 #x65 #x65 #x65 #x65 #x61 #x62 #x63))
(call-with-port (open-bytevector-input-port bv)
  (lambda (in)
    (let ((s (sample-read <composite> in)))
      (slot-ref s 'd) ;; => #\d
      (slot-ref s 'f) ;; => <simple>
      )))

;; write <composite> object
(call-with-bytevector-output-port
  (lambda (out)
    (let* ((s (make <simple> :a 10 :b 20))
           (c (make <composite> :f s)))
      ;; this can be written like this as well (sample-write o out)
      (sample-write <composite> c out))))
;; => #vu8(1 1 2 3 4 10 20 0)