Encoding Data with the Go Binary Package - Learning the Go Programming Language

By Vladimir Vivien

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