(module
bokbok-packet
(make-core-object
core-object?
core-object-tag
core-object-value
max-receive-depth
max-receive-length
read-binary
write-binary
split-packet
join-packet)
(import chicken scheme)
(use extras)
(use ports)
(use numbers)
(define-record-type core-object
(make-core-object tag value)
core-object?
(tag core-object-tag)
(value core-object-value))
;; Literals in code can't be this big, so we have to cheat.
(define two63 (expt 2 63))
(define two64 (expt 2 64))
(define (read-s8 port)
(let ((v (read-byte port)))
(if (eof-object? v)
#!eof
(if (< v 128)
v
(- v 256)))))
(define (read-u16 port)
(let* ((b1 (read-byte port))
(b2 (read-byte port)))
(if (or (eof-object? b1) (eof-object? b2))
#!eof
(+ (* b1 256) b2))))
(define (write-u16 i port)
(write-byte (quotient i 256) port)
(write-byte (modulo i 256) port))
(define (read-s16 port)
(let ((v (read-u16 port)))
(if (eof-object? v)
#!eof
(if (< v 32768)
v
(- v 65536)))))
(define (write-s16 i port)
(if (negative? i)
(write-u16 (+ 65536 i) port)
(write-u16 i port)))
(define (read-u32 port)
(let ((w1 (read-u16 port))
(w2 (read-u16 port)))
(if (or (eof-object? w1) (eof-object? w2))
#!eof
(+ (* w1 65536) w2))))
(define (write-u32 i port)
(write-byte (quotient i 16777216) port)
(write-byte (quotient (modulo i 16777216) 65536) port)
(write-byte (quotient (modulo i 65536) 256) port)
(write-byte (modulo i 256) port))
(define (read-s32 port)
(let ((v (read-u32 port)))
(if (eof-object? v)
#!eof
(if (< v 2147483648)
v
(- v 4294967296)))))
(define (write-s32 i port)
(if (negative? i)
(write-u32 (+ 4294967296 i) port)
(write-u32 i port)))
(define (read-u64 port)
(let ((w1 (read-u32 port))
(w2 (read-u32 port)))
(if (or (eof-object? w1) (eof-object? w2))
#!eof
(+ (* w1 4294967296) w2))))
(define (write-u64 i port)
(write-u32 (quotient i 4294967296) port)
(write-u32 (modulo i 4294967296) port))
(define (read-s64 port)
(let ((v (read-u64 port)))
(if (eof-object? v)
#!eof
(if (< v two63)
v
(- v two64)))))
(define (write-s64 i port)
(if (negative? i)
(write-u64 (+ two64 i) port)
(write-u64 i port)))
(define (read-length port)
(let ((b (read-byte port)))
(cond
((eof-object? b)
#!eof)
((= b #x80)
'variable)
((< b #x80)
b)
((eq? b #x82)
(read-u16 port))
((eq? b #x84)
(read-u32 port))
((eq? b #x88)
(read-u64 port))
(else
(error "Unknown length prefix" b)))))
(define (write-length l port)
(cond
((< l #x80)
(write-byte l port))
((< l #x8000)
(write-byte #x82)
(write-u16 l port))
((< l #x80000000)
(write-byte #x84)
(write-u32 l port))
((< l two63)
(write-byte #x88)
(write-u64 l port))
(else
(error "Size too big" l))))
(define (write-binary obj port)
(cond
((list? obj)
(write-byte #xe0 port)
(write-byte #x80 port)
(for-each
(lambda (o)
(write-binary o port))
obj)
(write-byte #x00 port)
(write-byte #x00 port))
((symbol? obj)
(let ((s (symbol->string obj)))
(write-byte #xdd port)
(write-length (string-length s) port)
(write-string s #f port)))
((string? obj)
(write-byte #x0c port)
(write-length (string-length obj) port)
(write-string obj #f port))
((boolean? obj)
(write-byte #x01 port)
(write-byte #x01 port)
(write-byte (if obj #xff #x00) port))
((vector? obj)
(write-byte #x30 port)
(write-byte #x80 port)
(let ((final (vector-length obj)))
(let loop
((idx 0))
(if (>= idx final)
(begin
(write-byte #x00 port)
(write-byte #x00 port))
(begin
(write-binary (vector-ref obj idx) port)
(loop (+ idx 1)))))))
((and (integer? obj) (exact? obj))
(write-byte #x02 port)
(cond
((zero? obj)
(write-byte #x00 port))
((<= -128 obj 127)
(write-byte #x01 port)
(write-byte obj port))
((<= -32768 obj 32767)
(write-byte #x02 port)
(write-s16 obj port))
((<= -2147483648 obj 2147483647)
(write-byte #x04 port)
(write-s32 obj port))
((<= (- two63) obj (- two63 1))
(write-byte #x08 port)
(write-s64 obj port))
(else
;; Chop into 64-bit chunks
(let loop
((parts '())
(n obj))
(if (<= (- two63) n (- two63 1))
(begin
(write-length (* (+ 1 (length parts)) 8) port)
(if (negative? obj)
(if (zero? (car parts))
(write-s64 -1 port)
(write-s64 (- n 1) port))
(write-s64 n port))
(for-each
(lambda (p)
(write-u64 p port))
parts))
(loop
(cons (modulo n two64) parts)
(quotient n two64)))))))
((number? obj)
(write-byte #xfe port)
(let ((s (number->string obj)))
(write-length (string-length s) port)
(write-string s #f port)))
((core-object? obj)
(if (number? (core-object-tag obj))
(begin
(write-byte (core-object-tag obj) port)
(write-byte #x80 port))
(begin
(let ((t (symbol->string (core-object-tag obj))))
(write-byte #xFF port)
(write-byte #x80 port)
(write-byte #xdd port)
(write-length (string-length t) port)
(write-string t #f port))))
(write-binary (core-object-value obj) port)
(write-byte #x00 port)
(write-byte #x00 port))
(else
(error "Unsupported kind of thing to write" obj))))
(define (join-packet ps)
(with-output-to-string
(lambda ()
(write-binary ps (current-output-port)))))
(define max-receive-depth (make-parameter +inf.0))
(define max-receive-length (make-parameter +inf.0))
(define (read-binary* port level)
(when (>= level (max-receive-depth))
(error "Maximum receive depth exceeded"))
(let* ((t (read-byte port))
(l (read-length port)))
(assert (or (eq? l 'variable) (<= l (max-receive-length))))
(if (or (eof-object? t) (eof-object? l))
#!eof
(case t
((#xe0) ;; List
(assert (eq? l 'variable))
(let loop
((result '())
(length 0))
(when (>= length (max-receive-length))
(error "Maximum receive length exceeded in list"))
(let ((v (read-binary* port (+ level 1))))
(if (eof-object? v)
(reverse result)
(loop (cons v result)
(+ length 1))))))
((#xdd) ;; Symbol
(string->symbol (read-string l port)))
((#x0c) ;; String
(read-string l port))
((#x01) ;; Boolean
(let ((v (read-byte port)))
(case v
((#x00) #f)
((#xff) #t)
(else (error "Invalid boolean value" v)))))
((#x30) ;; Vector
(assert (eq? l 'variable))
(let loop
((result '())
(length 0))
(when (>= length (max-receive-length))
(error "Maximum receive length exceeded in vector"))
(let ((v (read-binary* port (+ level 1))))
(if (eof-object? v)
(list->vector (reverse result))
(loop (cons v result)
(+ length 1))))))
((#x02) ;; Integer
(case l
((#x00) 0)
((#x01) (read-s8 port))
((#x02) (read-s16 port))
((#x04) (read-s32 port))
((#x08) (read-s64 port))
(else
(assert (zero? (modulo l 8)))
(let loop
((result (read-s64 port))
(parts-left (- (/ l 8) 1)))
(if (zero? parts-left)
result
(let ((v (read-u64 port)))
(loop
(+ (* two64 result) v)
(- parts-left 1))))))))
((#xfe) ;; Arbitrary Scheme number
(let ((s (read-string l port)))
(string->number s)))
((#xff) ;; Symbol-tagged core object
(assert (eq? l 'variable))
(assert (= (read-byte port) #xdd))
(let ((tl (read-length port)))
(assert (<= tl (max-receive-length)))
(let* ((tag (string->symbol (read-string tl port)))
(inner-value (read-binary* port (+ level 1))))
(assert (= (read-byte port) #x00))
(assert (= (read-byte port) #x00))
(make-core-object tag inner-value))))
((#x00) ;; EOC
(assert (= l 0))
#!eof)
(else ;; Number-tagged core object
(assert (eq? l 'variable))
(let ((inner-value (read-binary* port (+ level 1))))
(assert (= (read-byte port) #x00))
(assert (= (read-byte port) #x00))
(make-core-object t inner-value)))))))
(define (read-binary port)
(read-binary* port 0))
(define (split-packet ps)
(with-input-from-string ps
(lambda ()
(read-binary (current-input-port)))))
)