Encoding Data with the Go Binary Package

Vladimir Vivien
Jun 25 · 6 min read

A simple binary protocol

0       1       2       3       4       5       6       7
0123456701234567012345670123456701234567012345670123456701234567
+-------+-------+-------+-------+-------+-------+-------+------+
| SensorID | LocationID | Timestamp |
+-------+-------+-------+-------+-------+-------+-------+------+
| Temp |
+---------------+

Data representation concepts

Fixed-size values

The binary package also supports variable-length encoding of numeric values where smaller values require fewer bytes. However, this is not covered in this writeup.

Byte order

type ByteOrder interface {
Uint16([]byte) uint16
Uint32([]byte) uint32
Uint64([]byte) uint64
PutUint16([]byte, uint16)
PutUint32([]byte, uint32)
PutUint64([]byte, uint64)
String() string
}

Encoding and decoding directly

package mainimport (
"encoding/binary"
...
)
func main() {
buf := make([]byte, 10)
ts := uint32(time.Now().Unix())
binary.BigEndian.PutUint16(buf[0:], 0xa20c) // sensorID
binary.BigEndian.PutUint16(buf[2:], 0x04af) // locationID
binary.BigEndian.PutUint32(buf[4:], ts) // timestamp
binary.BigEndian.PutUint16(buf[8:], 479) // temp
fmt.Printf("% x\n", buf)
}
func main() {
buf := <contains encoded bytes>
sensorID := binary.BigEndian.Uint16(buf[0:])
locID := binary.BigEndian.Uint16(buf[2:])
tstamp := binary.BigEndian.Uint32(buf[4:])
temp := binary.BigEndian.Uint16(buf[8:])
fmt.Printf("sid: %0#x, locID %0#x ts: %0#x, temp:%d\n",
sensorID, locID, tstamp, temp)
}

Encoding/decoding with an IO streams

Encoding with binary.Write

type packet struct {
Sensid uint32
Locid uint16
Tstamp uint32
Temp int16
}
func main() {
dataIn := packet{
Sensid: 1, Locid: 1233, Tstamp: 123452123, Temp: 12,
}
buf := new(bytes.Buffer)
err := binary.Write(buf, binary.BigEndian, dataIn)
if err != nil {
fmt.Println(err)
return
}
}

Decoding with binary.Read

type packet struct {
Sensid uint32
Locid uint16
Tstamp uint32
Temp int16
}
func main() {
buf := <reader with encoded binary data>
var dataOut packet
err := binary.Read(buf, binary.BigEndian, &dataOut)
if err != nil {
fmt.Println("failed to Read:", err)
return
}
}

Encoding multiple packets

type packet struct {
Sensid uint32
Locid uint16
Tstamp uint32
Temp int16
}
func main() {
dataOut := []packet{
{Sensid: 1, Locid: 1233, Tstamp: 123452123, Temp: 12},
{Sensid: 2, Locid: 4567, Tstamp: 133452124, Temp: 32},
{Sensid: 7, Locid: 8910, Tstamp: 143452125, Temp: -12},
}
// encode a slice of packet
buf := new(bytes.Buffer)
err := binary.Write(buf, binary.LittleEndian, dataOut)
if err != nil {
fmt.Println(err)
return
}
// decode all items from slice
dataIn := make([]packet, 3)
err := binary.Read(buf, binary.LittleEndian, dataIn)
if err != nil {
fmt.Println("failed to Read:", err)
return
}
fmt.Printf("%v", dataIn)}

Conclusion

Learning the Go Programming Language

Short and insightful posts for newcomers learning the Go programming language

Vladimir Vivien

Written by

Software Eng • Go Programming • Kubernetes • Author http://golang.fyi

Learning the Go Programming Language

Short and insightful posts for newcomers learning the Go programming language