README
¶
Go Blob
A package that provides utilities for writing and reading primitive types to and from byte sequences.
User's Guide
For a more complete documentation, check the GoDocs.
Block API
The Block API allows one to place values of concrete primitive types at specific offsets inside a byte slice. It is up to the caller to ensure that the slice has the necessary size.
Example:
offset := 4
block := make(gblob.LittleEndianBlock, 32)
block.SetFloat32(offset, 3.5)
fmt.Println(block.Float32(offset))
This API is similar to Go's built-in binary.ByteOrder
. The difference here is that the gblob API is slightly more compact, it has helper functions for more primitive types and allows one to pass an offset into the byte slice for better readability.
There are two implementations available - LittleEndianBlock and BigEndianBlock, depending on the desired byte order.
TypedWriter / TypedReader API
The TypedWriter API allows one to write concrete primitive types to an io.Writer
. No padding or alignment is added to the output.
Example:
var buffer bytes.Buffer
writer := gblob.NewLittleEndianWriter(&buffer)
writer.WriteUint64(0x13743521FA954321)
There are two implementations available - NewLittleEndianWriter and NewBigEndianWriter, depending on the desired byte order.
The TypedReader API allows one to read concrete primitive types from an io.Reader
.
Example:
buffer := bytes.NewBuffer([]uint8{
0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20,
})
reader := gblob.NewLittleEndianReader(buffer)
value, err := reader.ReadUint64()
There are two implementations available - NewLittleEndianReader and NewBigEndianReader, depending on the desired byte order.
PackedEncoder / PackedDecoder API
The PackedEncoder API allows one to marshal a data structure to a binary sequence.
Example:
var buffer bytes.Buffer
gblob.NewLittleEndianPackedEncoder(&buffer).Encode(struct{
A float32
B uint64
}{
A: 3.14,
B: 0xFF003344FF003344,
})
There are two implementations available - NewLittleEndianPackedEncoder and NewBigEndianPackedEncoder, depending on the desired byte order.
This is similar to Go's bytes.Write
, except that it supports slices, maps and strings.
The PackedDecoder API allows one to unmarshal a data structure from a binary sequence that was previously marshaled through the PackedEncoder API.
Example:
buffer := bytes.NewBuffer([]uint8{
0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20,
})
var target uint64
gblob.NewLittleEndianPackedDecoder(buffer).Decode(&target)
There are two implementations available - NewLittleEndianPackedDecoder and NewBigEndianPackedDecoder, depending on the desired byte order.
This is similar to Go's bytes.Read
, except that it supports slices, maps and strings.
Performance
Following are some benchmark results. They compare this library against Go's binary
and gob
packages, since those are closest in terms of features. Results are based on the following hardware:
goos: linux
goarch: amd64
pkg: github.com/mokiat/gblob
cpu: AMD Ryzen 7 3700X 8-Core Processor
You can find the benchmark tests in the bench_test.go
file. You can run them with the following command:
go test -benchmem -run=^$ -bench ^Benchmark github.com/mokiat/gblob
Adjust the ^Benchmark
regex to focus on specific benchmark sets and use the -cpuprofile bench.pprof
flag if profiling is desired.
Take the results that follow with a grain of salt. Do your own measurements for your particular use case.
Block API
Following is a benchmark comparison between Block
and Go's binary.ByteOrder
API.
Approach | Time per Operation | Allocated Memory per Operation | Allocation Count per Operation |
---|---|---|---|
Block |
1493 ns/op | 0 B/op | 0 allocs/op |
binary.ByteOrder |
1496 ns/op | 0 B/op | 0 allocs/op |
Both APIs provide similar performance so it boils down to ease of use of and personal preference.
TypedWriter
Following is a benchmark comparison between TypedWriter
and Go's binary.Write
functions.
Approach | Time per Operation | Allocated Memory per Operation | Allocation Count per Operation |
---|---|---|---|
TypedWriter |
7753 ns/op | 0 B/op | 0 allocs/op |
binary.Write |
39768 ns/op | 4096 B/op | 1024 allocs/op |
The
TypedWriter
does not allocate any memory per write operation and it also runs about3-4 times
faster. In reality, it allocates an initial buffer of size8 bytes
that it reuses to achieve these results.
TypedReader
Following is a benchmark comparison between TypedReader
and Go's binary.Read
functions.
Approach | Time per Operation | Allocated Memory per Operation | Allocation Count per Operation |
---|---|---|---|
TypedReader |
11595 ns/op | 0 B/op | 0 allocs/op |
binary.Read |
53088 ns/op | 4096 B/op | 1024 allocs/op |
The
TypedReader
does not allocate any memory per read and it also runs4-5 times
faster. This is again achieved by having theTypedReader
allocate an initial buffer of8 bytes
that it reuses.
PackedEncoder
When serializing larger types with PackedEncoder
vs binary.Write
, the performance difference is negligible, though the memory aspect remains the same as before. More interesting in this case is the comparison with gob.Encoder
, due to a similar feature set.
WARNING: The scenario that is compared is the one where a new instance of an Encoder
is created for each Encode
performed (i.e. gob.NewEncoder(out).Encode(&target)
). This is arguably the more common scenario (saving an asset / writing a response). When a gob.Encoder
is reused to encode multiple sequences of items to a stream, it is significantly faster than PackedEncoder
, likely due to caching.
Approach | Time per Operation | Allocated Memory per Operation | Allocation Count per Operation |
---|---|---|---|
PackedEncoder |
7644113 ns/op | 179158 B/op | 3072 allocs/op |
gob.Encoder |
11349161 ns/op | 2408841 B/op | 38912 allocs/op |
Here the
gob.Encoder
performs worse, especially when memory is concerned.
PackedDecoder
Following is the comparison between PackedDecoder
and gob.Decoder
.
WARNING: The scenario that is compared is the one where a new instance of a Decoder
is created for each Decode
performed (i.e. gob.NewDecoder(in).Decode(&target)
). This is arguably the more common scenario (loading an asset / reading a request). When a gob.Decoder
is reused to read multiple sequences of items from a stream, it is significantly faster than PackedDecoder
, likely due to caching.
Approach | Time per Operation | Allocated Memory per Operation | Allocation Count per Operation |
---|---|---|---|
PackedDecoder |
17483112 ns/op | 2261422 B/op | 5120 allocs/op |
gob.Decoder |
45206563 ns/op | 11466272 B/op | 236565 allocs/op |
The
gob.Decoder
performs werse, especially when memory is concerned.
Documentation
¶
Overview ¶
Package gblob (Go Blob) provides utilities for writing and reading binary primitives. While Go does have encoding/binary and encoding/gob packages, they have certain limitations that this package attempts to overcome at the cost of other limitations.
Index ¶
- type BigEndianBlock
- func (b BigEndianBlock) Float32(offset int) float32
- func (b BigEndianBlock) Float64(offset int) float64
- func (b BigEndianBlock) Int16(offset int) int16
- func (b BigEndianBlock) Int32(offset int) int32
- func (b BigEndianBlock) Int64(offset int) int64
- func (b BigEndianBlock) Int8(offset int) int8
- func (b BigEndianBlock) SetFloat32(offset int, value float32)
- func (b BigEndianBlock) SetFloat64(offset int, value float64)
- func (b BigEndianBlock) SetInt16(offset int, value int16)
- func (b BigEndianBlock) SetInt32(offset int, value int32)
- func (b BigEndianBlock) SetInt64(offset int, value int64)
- func (b BigEndianBlock) SetInt8(offset int, value int8)
- func (b BigEndianBlock) SetUint16(offset int, value uint16)
- func (b BigEndianBlock) SetUint32(offset int, value uint32)
- func (b BigEndianBlock) SetUint64(offset int, value uint64)
- func (b BigEndianBlock) SetUint8(offset int, value uint8)
- func (b BigEndianBlock) Uint16(offset int) uint16
- func (b BigEndianBlock) Uint32(offset int) uint32
- func (b BigEndianBlock) Uint64(offset int) uint64
- func (b BigEndianBlock) Uint8(offset int) uint8
- type Block
- type LittleEndianBlock
- func (b LittleEndianBlock) Float32(offset int) float32
- func (b LittleEndianBlock) Float64(offset int) float64
- func (b LittleEndianBlock) Int16(offset int) int16
- func (b LittleEndianBlock) Int32(offset int) int32
- func (b LittleEndianBlock) Int64(offset int) int64
- func (b LittleEndianBlock) Int8(offset int) int8
- func (b LittleEndianBlock) SetFloat32(offset int, value float32)
- func (b LittleEndianBlock) SetFloat64(offset int, value float64)
- func (b LittleEndianBlock) SetInt16(offset int, value int16)
- func (b LittleEndianBlock) SetInt32(offset int, value int32)
- func (b LittleEndianBlock) SetInt64(offset int, value int64)
- func (b LittleEndianBlock) SetInt8(offset int, value int8)
- func (b LittleEndianBlock) SetUint16(offset int, value uint16)
- func (b LittleEndianBlock) SetUint32(offset int, value uint32)
- func (b LittleEndianBlock) SetUint64(offset int, value uint64)
- func (b LittleEndianBlock) SetUint8(offset int, value uint8)
- func (b LittleEndianBlock) Uint16(offset int) uint16
- func (b LittleEndianBlock) Uint32(offset int) uint32
- func (b LittleEndianBlock) Uint64(offset int) uint64
- func (b LittleEndianBlock) Uint8(offset int) uint8
- type PackedDecodable
- type PackedDecoder
- type PackedEncodable
- type PackedEncoder
- type TypedReader
- type TypedWriter
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type BigEndianBlock ¶
type BigEndianBlock []byte
BigEndianBlock represents a fixed-size block of bytes that holds values encoded in Big Endian order.
func (BigEndianBlock) Float32 ¶
func (b BigEndianBlock) Float32(offset int) float32
Float32 returns the float32 value at the specified offset.
func (BigEndianBlock) Float64 ¶
func (b BigEndianBlock) Float64(offset int) float64
Float64 returns the float64 value at the specified offset.
func (BigEndianBlock) Int16 ¶
func (b BigEndianBlock) Int16(offset int) int16
Int16 returns the int16 value at the specified offset.
func (BigEndianBlock) Int32 ¶
func (b BigEndianBlock) Int32(offset int) int32
Int32 returns the int32 value at the specified offset.
func (BigEndianBlock) Int64 ¶
func (b BigEndianBlock) Int64(offset int) int64
Int64 returns the int64 value at the specified offset.
func (BigEndianBlock) Int8 ¶
func (b BigEndianBlock) Int8(offset int) int8
Int8 returns the int8 value at the specified offset.
func (BigEndianBlock) SetFloat32 ¶
func (b BigEndianBlock) SetFloat32(offset int, value float32)
SetFloat32 places the float32 value at the specified offset.
func (BigEndianBlock) SetFloat64 ¶
func (b BigEndianBlock) SetFloat64(offset int, value float64)
SetFloat64 places the float64 value at the specified offset.
func (BigEndianBlock) SetInt16 ¶
func (b BigEndianBlock) SetInt16(offset int, value int16)
SetInt16 places the int16 value at the specified offset.
func (BigEndianBlock) SetInt32 ¶
func (b BigEndianBlock) SetInt32(offset int, value int32)
SetInt32 places the int32 value at the specified offset.
func (BigEndianBlock) SetInt64 ¶
func (b BigEndianBlock) SetInt64(offset int, value int64)
SetInt64 places the int64 value at the specified offset.
func (BigEndianBlock) SetInt8 ¶
func (b BigEndianBlock) SetInt8(offset int, value int8)
SetInt8 places the int8 value at the specified offset.
func (BigEndianBlock) SetUint16 ¶
func (b BigEndianBlock) SetUint16(offset int, value uint16)
SetUint16 places the uint16 value at the specified offset.
func (BigEndianBlock) SetUint32 ¶
func (b BigEndianBlock) SetUint32(offset int, value uint32)
SetUint32 places the uint32 value at the specified offset.
func (BigEndianBlock) SetUint64 ¶
func (b BigEndianBlock) SetUint64(offset int, value uint64)
SetUint64 places the uint64 value at the specified offset.
func (BigEndianBlock) SetUint8 ¶
func (b BigEndianBlock) SetUint8(offset int, value uint8)
SetUint8 places the uint8 value at the specified offset.
func (BigEndianBlock) Uint16 ¶
func (b BigEndianBlock) Uint16(offset int) uint16
Uint16 returns the uint16 value at the specified offset.
func (BigEndianBlock) Uint32 ¶
func (b BigEndianBlock) Uint32(offset int) uint32
Uint32 returns the uint32 value at the specified offset.
func (BigEndianBlock) Uint64 ¶
func (b BigEndianBlock) Uint64(offset int) uint64
Uint64 returns the uint64 value at the specified offset.
func (BigEndianBlock) Uint8 ¶
func (b BigEndianBlock) Uint8(offset int) uint8
Uint8 returns the uint8 value at the specified offset.
type Block ¶
type Block interface { // Uint8 returns the uint8 value at the specified offset. Uint8(offset int) uint8 // SetUint8 places the uint8 value at the specified offset. SetUint8(offset int, value uint8) // Int8 returns the int8 value at the specified offset. Int8(offset int) int8 // SetInt8 places the int8 value at the specified offset. SetInt8(offset int, value int8) // Uint16 returns the uint16 value at the specified offset. Uint16(offset int) uint16 // SetUint16 places the uint16 value at the specified offset. SetUint16(offset int, value uint16) // Int16 returns the int16 value at the specified offset. Int16(offset int) int16 // SetInt16 places the int16 value at the specified offset. SetInt16(offset int, value int16) // Uint32 returns the uint32 value at the specified offset. Uint32(offset int) uint32 // SetUint32 places the uint32 value at the specified offset. SetUint32(offset int, value uint32) // Int32 returns the int32 value at the specified offset. Int32(offset int) int32 // SetInt32 places the int32 value at the specified offset. SetInt32(offset int, value int32) // Uint64 returns the uint64 value at the specified offset. Uint64(offset int) uint64 // SetUint64 places the uint64 value at the specified offset. SetUint64(offset int, value uint64) // Int64 returns the int64 value at the specified offset. Int64(offset int) int64 // SetInt64 places the int64 value at the specified offset. SetInt64(offset int, value int64) // Float32 returns the float32 value at the specified offset. Float32(offset int) float32 // SetFloat32 places the float32 value at the specified offset. SetFloat32(offset int, value float32) // Float64 returns the float64 value at the specified offset. Float64(offset int) float64 // SetFloat64 places the float64 value at the specified offset. SetFloat64(offset int, value float64) }
Block represents a fixed-size block of bytes that holds values encoded in a particular order.
type LittleEndianBlock ¶
type LittleEndianBlock []byte
LittleEndianBlock represents a fixed-size block of bytes that holds values encoded in Little Endian order.
func (LittleEndianBlock) Float32 ¶
func (b LittleEndianBlock) Float32(offset int) float32
Float32 returns the float32 value at the specified offset.
func (LittleEndianBlock) Float64 ¶
func (b LittleEndianBlock) Float64(offset int) float64
Float64 returns the float64 value at the specified offset.
func (LittleEndianBlock) Int16 ¶
func (b LittleEndianBlock) Int16(offset int) int16
Int16 returns the int16 value at the specified offset.
func (LittleEndianBlock) Int32 ¶
func (b LittleEndianBlock) Int32(offset int) int32
Int32 returns the int32 value at the specified offset.
func (LittleEndianBlock) Int64 ¶
func (b LittleEndianBlock) Int64(offset int) int64
Int64 returns the int64 value at the specified offset.
func (LittleEndianBlock) Int8 ¶
func (b LittleEndianBlock) Int8(offset int) int8
Int8 returns the int8 value at the specified offset.
func (LittleEndianBlock) SetFloat32 ¶
func (b LittleEndianBlock) SetFloat32(offset int, value float32)
SetFloat32 places the float32 value at the specified offset.
func (LittleEndianBlock) SetFloat64 ¶
func (b LittleEndianBlock) SetFloat64(offset int, value float64)
SetFloat64 places the float64 value at the specified offset.
func (LittleEndianBlock) SetInt16 ¶
func (b LittleEndianBlock) SetInt16(offset int, value int16)
SetInt16 places the int16 value at the specified offset.
func (LittleEndianBlock) SetInt32 ¶
func (b LittleEndianBlock) SetInt32(offset int, value int32)
SetInt32 places the int32 value at the specified offset.
func (LittleEndianBlock) SetInt64 ¶
func (b LittleEndianBlock) SetInt64(offset int, value int64)
SetInt64 places the int64 value at the specified offset.
func (LittleEndianBlock) SetInt8 ¶
func (b LittleEndianBlock) SetInt8(offset int, value int8)
SetInt8 places the int8 value at the specified offset.
func (LittleEndianBlock) SetUint16 ¶
func (b LittleEndianBlock) SetUint16(offset int, value uint16)
SetUint16 places the uint16 value at the specified offset.
func (LittleEndianBlock) SetUint32 ¶
func (b LittleEndianBlock) SetUint32(offset int, value uint32)
SetUint32 places the uint32 value at the specified offset.
func (LittleEndianBlock) SetUint64 ¶
func (b LittleEndianBlock) SetUint64(offset int, value uint64)
SetUint64 places the uint64 value at the specified offset.
func (LittleEndianBlock) SetUint8 ¶
func (b LittleEndianBlock) SetUint8(offset int, value uint8)
SetUint8 places the uint8 value at the specified offset.
func (LittleEndianBlock) Uint16 ¶
func (b LittleEndianBlock) Uint16(offset int) uint16
Uint16 returns the uint16 value at the specified offset.
func (LittleEndianBlock) Uint32 ¶
func (b LittleEndianBlock) Uint32(offset int) uint32
Uint32 returns the uint32 value at the specified offset.
func (LittleEndianBlock) Uint64 ¶
func (b LittleEndianBlock) Uint64(offset int) uint64
Uint64 returns the uint64 value at the specified offset.
func (LittleEndianBlock) Uint8 ¶
func (b LittleEndianBlock) Uint8(offset int) uint8
Uint8 returns the uint8 value at the specified offset.
type PackedDecodable ¶ added in v0.3.0
type PackedDecodable interface { // DecodePacked decodes the receiver from the specified reader. DecodePacked(reader TypedReader) error }
PackedDecodable is an interface that can be implemented by types that want to provide a custom packed decoding.
type PackedDecoder ¶ added in v0.2.0
type PackedDecoder struct {
// contains filtered or unexported fields
}
PackedDecoder decodes arbitrary Go objects from binary form by going through each field in sequence and deserializing it without any padding.
func NewBigEndianPackedDecoder ¶ added in v0.2.0
func NewBigEndianPackedDecoder(in io.Reader) *PackedDecoder
NewBigEndianPackedDecoder creates a new PackedDecoder that is configured to read its input in Big Endian order.
func NewLittleEndianPackedDecoder ¶ added in v0.2.0
func NewLittleEndianPackedDecoder(in io.Reader) *PackedDecoder
NewLittleEndianPackedDecoder creates a new PackedDecoder that is configured to read its input in Little Endian order.
func (*PackedDecoder) Decode ¶ added in v0.2.0
func (d *PackedDecoder) Decode(target interface{}) error
Decode decodes the specified target value from the Reader.
type PackedEncodable ¶ added in v0.3.0
type PackedEncodable interface { // EncodePacked encodes the receiver into the specified writer. EncodePacked(writer TypedWriter) error }
PackedEncodable is an interface that can be implemented by types that want to provide a custom packed encoding.
type PackedEncoder ¶ added in v0.2.0
type PackedEncoder struct {
// contains filtered or unexported fields
}
PackedEncoder encodes arbitrary Go objects in binary form by going through each field in sequence and serializing it without any padding.
func NewBigEndianPackedEncoder ¶ added in v0.2.0
func NewBigEndianPackedEncoder(out io.Writer) *PackedEncoder
NewBigEndianPackedEncoder creates a new PackedEncoder that is configured to write its output in Big Endian order.
func NewLittleEndianPackedEncoder ¶ added in v0.2.0
func NewLittleEndianPackedEncoder(out io.Writer) *PackedEncoder
NewLittleEndianPackedEncoder creates a new PackedEncoder that is configured to write its output in Little Endian order.
func (*PackedEncoder) Encode ¶ added in v0.2.0
func (e *PackedEncoder) Encode(source any) error
Encode encodes the specified source value into the Writer.
type TypedReader ¶
type TypedReader interface { // ReadUint8 reads a single uint8 from the source. ReadUint8() (uint8, error) // ReadInt8 reads a single int8 from the source. ReadInt8() (int8, error) // ReadUint16 reads a single uint16 from the source. ReadUint16() (uint16, error) // ReadInt16 reads a single int16 from the source. ReadInt16() (int16, error) // ReadUint32 reads a single uint32 from the source. ReadUint32() (uint32, error) // ReadInt32 reads a single int32 from the source. ReadInt32() (int32, error) // ReadUint64 reads a single uint64 from the source. ReadUint64() (uint64, error) // ReadInt64 reads a single int64 from the source. ReadInt64() (int64, error) // ReadFloat32 reads a single float32 from the source. ReadFloat32() (float32, error) // ReadFloat64 reads a single float64 from the source. ReadFloat64() (float64, error) // ReadBytes reads exactly len(target) bytes from the source and places // them inside target. ReadBytes(target []byte) error }
TypedReader represents a reader that can parse specific Go types from a byte sequence.
The endianness depends on the actual implementation.
func NewBigEndianReader ¶
func NewBigEndianReader(in io.Reader) TypedReader
NewBigEndianReader returns an implementation of TypedReader that reads from the specified in Reader in Big Endian order.
func NewLittleEndianReader ¶
func NewLittleEndianReader(in io.Reader) TypedReader
NewLittleEndianReader returns an implementation of TypedReader that reads from the specified in Reader in Little Endian order.
type TypedWriter ¶
type TypedWriter interface { // WriteUint8 writes a single uint8 to the target. WriteUint8(uint8) error // WriteInt8 writes a single int8 to the target. WriteInt8(int8) error // WriteUint16 writes a single uint16 to the target. WriteUint16(uint16) error // WriteInt16 writes a single int16 to the target. WriteInt16(int16) error // WriteUint32 writes a single uint32 to the target. WriteUint32(uint32) error // WriteInt32 writes a single int32 to the target. WriteInt32(int32) error // WriteUint64 writes a single uint64 to the target. WriteUint64(uint64) error // WriteInt64 writes a single int64 to the target. WriteInt64(int64) error // WriteFloat32 writes a single float32 to the target. WriteFloat32(float32) error // WriteFloat64 writes a single float64 to the target. WriteFloat64(float64) error // WriteBytes writes len(bytes) from source to the target. WriteBytes(source []byte) error }
TypedWriter represents a writer that can serialize specific Go types to a byte sequence.
The endianness depends on the actual implementation.
func NewBigEndianWriter ¶
func NewBigEndianWriter(out io.Writer) TypedWriter
NewBigEndianWriter returns an implementation of TypedWriter that writes to the specified out Writer in Big Endian order.
func NewLittleEndianWriter ¶
func NewLittleEndianWriter(out io.Writer) TypedWriter
NewLittleEndianWriter returns an implementation of TypedWriter that writes to the specified out Writer in Little Endian order.