-
Notifications
You must be signed in to change notification settings - Fork 57
Home
Serializing Clojure sequences is a breeze – (prn x)
and you’re done. Unfortunately, most of the world doesn’t speak s-expressions; the lowest common denominator for communication between software is bytes, and will probably remain so for a while.
Java’s ByteBuffers aren’t too bad, but aren’t particularly idiomatic either. Unfortunately, TCP is a streaming protocol, which means that we have to handle reading halfway through a sequence and then waiting for the rest. This is made even more complex if we’re not willing to dedicate a thread to waiting for the next packet.
Gloss tries to make things easier. It provides a DSL for specifying a wide variety of byte formats, making it easy to both create compact representations of Clojure data structures and to talk to external systems via complex protocols.
In Gloss, byte formats are called frames. A frame is a standard Clojure data structure, with types substituted for actual values. For instance, this is a valid frame:
{:a :int16, :b :float32}
To transform a frame to a byte representation and back again, we use encode
and decode
:
> (def-frame fr {:a :int16, :b :float32})
#'fr
> (encode fr {:a 1, :b 2})
[ #<HeapByteBuffer java.nio.HeapByteBuffer[pos=0 lim=6 cap=6]> ]
> (decode fr *1)
{:a 1, :b 2.0}
Notice that encode
returns a sequence of ByteBuffers. Gloss consumes and emits sequences of ByteBuffers, because it’s designed to deal with streaming data. Turning these sequences into a contiguous ByteBuffer can be accomplished by calling (contiguous buffer-sequence)
, but this is only necessary when interfacing with external libraries.
To encode and decode sequences of frames, use encode-all
and decode-all
.
> (def-frame fr :float32)
#'fr
> (encode-all fr [1 2])
( #<HeapByteBuffer java.nio.HeapByteBuffer[pos=0 lim=4 cap=4]> #<HeapByteBuffer java.nio.HeapByteBuffer[pos=0 lim=4 cap=4]> )
> (decode-all fr *1)
[1.0 2.0]
user>