Currently I'm implementing SSH (for now client only) on Sagittarius and have noticed it would be convenient to have a library which handles binary data structure read/write. So I've written (binary data) library. (not sure if the name should be '(binary structure)' or '(binary io)', or something else).
Here is the simple example;
;; The definition is from RFC 4250-4254
;; atom datum
(define-simple-datum-define define-ssh-type read-message write-message)
(define-ssh-type <name-list> (<ssh-type>)
names '()
(lambda (in)
(let* ((len (get-unpack in "!L"))
(names (get-bytevector-n in len)))
(string-split (utf8->string names) #/,/)))
(lambda (out names)
(let ((names (string->utf8 (string-join names ","))))
(put-bytevector out (pack "!L" (bytevector-length names)))
(put-bytevector out names)))
:parent-metaclass <ssh-type-meta>)
;; composite data
(define-composite-data-define define-ssh-message read-message write-message)
(define-ssh-message <ssh-msg-keyinit> (<ssh-message>)
((type :byte +ssh-msg-kexinit+)
(cookie (:byte 16)) ;; array of byte
(kex-algorithms <name-list>)
(server-host-key-algorithms <name-list>)
(encryption-algorithms-client-to-server <name-list>)
(encryption-algorithms-server-to-client <name-list>)
(mac-algorithms-client-to-server <name-list>)
(mac-algorithms-server-to-client <name-list>)
(compression-algorithms-client-to-server <name-list> (name-list "none"))
(compression-algorithms-server-to-client <name-list> (name-list "none"))
(language-client-to-server <name-list> (name-list))
(language-server-to-client <name-list> (name-list))
(first-kex-packat-follows :boolean #f)
(reserved :uint32 0)))
So the idea of the library is that structured data are either simple datum or composite of simple datum. Thus if we define how to read/write the simple datum, then composite data's read/write are already decided. This might not be always true but as far as I know most of the case.
BTW, I think the naming of the macro is ugly so if you have a better suggestion it's very welcome :)