Introduction
Restruct is a library for the Go programming language that is designed to provide raw binary serialization and deserialization.
Restruct provides functionality similar to what you might expect from using raw structures in C, only richer. While using C structures is significantly faster, restruct provides greater functionality, such as allowing custom serialization/deserialization routines for user-defined types, explicit control of padding, and fine-grained control over byte order.
Features
- Open Source, ISC license
- No external dependencies
- Supports Go 1.3+
- Unit tested and documented
- Supports a wide array of binary serialization tasks:
- Conversions between integer types
- Automatic size-prefixed arrays
- Custom serialization for user-defined types
- Granular endianness
Getting Started
Restruct's API is versioned using gopkg.in. To install the stable version of restruct, issue the following command:
go get gopkg.in/restruct.v1
Basic Usage
When not using any of the advanced features, restruct behaves similar to encoding/binary. Here's an example of reading and writing data with restruct, using only basic features:
package main
import (
"encoding/binary"
"io/ioutil"
"gopkg.in/restruct.v1"
)
func main() {
test := struct {
Counter int32
}{}
if data, err := ioutil.ReadFile("counter"); err == nil {
restruct.Unpack(data, binary.LittleEndian, &test)
}
test.Counter++
data, _ := restruct.Pack(binary.LittleEndian, &test)
ioutil.WriteFile("counter", data, 0777)
}
In this code sample, the file 'counter' will contain a mere 4 bytes, which will increment up from 1 each time the program is ran. Though contrived, this small program shows most of the public API of restruct.
The additional features provided by restruct are mostly provided through struct tagging.
Advanced Features
Struct tag formatting in restruct is very simple. Restruct uses the 'struct' name for its tags. The value of this tag will always be a comma separated list of flags, which are processed left-to-right. The following flags are currently supported:
Flag | Description |
---|---|
|
A bare Go type expression, e.g. int32 or
[]string . This allows you to override the binary
interpretation of a field in many cases, although not every combination
will work properly. Notably, however, it is possible to use a
fixed-length byte array as a representation of a string, among many
other transformations.
This field is parsed using the Go parser, so any valid Go expression will parse properly. However, channels and maps will cause an error. |
|
Specifies that this field should be treated as a count of the number of
elements in Field . Inspired by lunixbochs/struc,
this flag makes it easy to unpack a variable number of elements. Unlike
struc, however, the target field can be any slice type - it can be a
slice of structures, a slice of arrays, and the binary size of elements
can vary per element safely.
|
|
Specifies that the size of this field should be taken from the count in
Field . Using this flag, you can get the same functionality
as sizeof=[Field] , but with the relationship reversed,
allowing, for example, multiple fields that use the same size counter.
|
|
Specifies an offset to skip before this field. This can be used to,
for example, emulate C-style structure alignment. It is also possible
to use this with unnamed (_ ) fields to skip an arbitrary
number of bytes from the source without taking up any bytes of memory.
|
|
Specifies that this field should be treated as big endian. This will
override the byte order passed into Pack or Unpack .
This tag will also apply recursively if you put it on a composite field.
|
|
Like the above, but specifies little endian instead. |
|
When encoding boolean values, this flag will cause the true state to
be encoded as ^0 (all bits set) instead of 1 (first bit set.) This
emulates the VARIANT_BOOL type.
|
|
When encoding boolean values, this flag will cause the true and
false encodings to be swapped.
|
Example
It would be difficult to show off every feature of restruct in a single example, but this slightly more advanced example shows off how some of the flags can be used in a real program.
package main
import (
"encoding/binary"
"io/ioutil"
"gopkg.in/restruct.v1"
)
type Record struct {
Message string `struct:"[128]byte"`
}
type Container struct {
Version int `struct:"int32"`
NumRecord int `struct:"int32,sizeof=Records"`
Records []Record
}
func main() {
var c Container
data, _ := ioutil.ReadFile("records")
restruct.Unpack(data, binary.LittleEndian, &c)
}