Skip to content
ztellman edited this page Nov 19, 2010 · 17 revisions

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.

the basics

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>
Clone this wiki locally