Skip to content

Repository files navigation

Go Report CardCodeCovGoDocLicense: MIT

binstruct

Golang binary decoder to structure

Install

go get -u github.com/ghostiam/binstruct

Examples

ZIP decoder
PNG decoder

Use

For struct

From file or other io.ReadSeeker:

package main
import (
"encoding/binary""fmt""log""os""github.com/ghostiam/binstruct"
)
funcmain() {
file, err:=os.Open("testdata/file.bin")
iferr!=nil {
log.Fatal(err)
}
typedataStructstruct {
Arr []int16`bin:"len:4"`
}
varactualdataStructdecoder:=binstruct.NewDecoder(file, binary.BigEndian)
// decoder.SetDebug(true) // you can enable the output of bytes read for debuggingerr=decoder.Decode(&actual)
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("%+v", actual)
// Output:// {Arr:[1 2 3 4]}
}

From bytes

package main
import (
"fmt""log""github.com/ghostiam/binstruct"
)
funcmain() {
data:= []byte{
0x00, 0x01,
0x00, 0x02,
0x00, 0x03,
0x00, 0x04,
}
typedataStructstruct {
Arr []int16`bin:"len:4"`
}
varactualdataStructerr:=binstruct.UnmarshalBE(data, &actual) // UnmarshalLE() or Unmarshal()iferr!=nil {
log.Fatal(err)
}
fmt.Printf("%+v", actual)
// Output: {Arr:[1 2 3 4]}
}

or just use reader without mapping data into the structure

You can not use the functionality for mapping data into the structure, you can use the interface to get data from the stream (io.ReadSeeker)

reader.go

typeReaderinterface {
io.ReadSeeker// Peek returns the next n bytes without advancing the reader.Peek(nint) ([]byte, error)
// ReadBytes reads up to n bytes. It returns the number of bytes// read, bytes and any error encountered.ReadBytes(nint) (anint, b []byte, errerror)
// ReadAll reads until an error or EOF and returns the data it read.ReadAll() ([]byte, error)
// ReadByte read and return one byteReadByte() (byte, error)
// ReadBool read one byte and return boolean valueReadBool() (bool, error)
// ReadUint8 read one byte and return uint8 valueReadUint8() (uint8, error)
// ReadUint16 read two bytes and return uint16 valueReadUint16() (uint16, error)
// ReadUint32 read four bytes and return uint32 valueReadUint32() (uint32, error)
// ReadUint64 read eight bytes and return uint64 valueReadUint64() (uint64, error)
// ReadUintX read X bytes and return uint64 valueReadUintX(xint) (uint64, error)
// ReadInt8 read one byte and return int8 valueReadInt8() (int8, error)
// ReadInt16 read two bytes and return int16 valueReadInt16() (int16, error)
// ReadInt32 read four bytes and return int32 valueReadInt32() (int32, error)
// ReadInt64 read eight bytes and return int64 valueReadInt64() (int64, error)
// ReadIntX read X bytes and return int64 valueReadIntX(xint) (int64, error)
// ReadFloat32 read four bytes and return float32 valueReadFloat32() (float32, error)
// ReadFloat64 read eight bytes and return float64 valueReadFloat64() (float64, error)
// Unmarshal parses the binary data and stores the result// in the value pointed to by v.Unmarshal(vinterface{}) error// WithOrder changes the byte order for the new ReaderWithOrder(order binary.ByteOrder) Reader
}

Example:

package main
import (
"encoding/binary""fmt""log""github.com/ghostiam/binstruct"
)
funcmain() {
data:= []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}
reader:=binstruct.NewReaderFromBytes(data, binary.BigEndian, false)
i16, err:=reader.ReadInt16()
iferr!=nil {
log.Fatal(err)
}
fmt.Println(i16)
i32, err:=reader.ReadInt32()
iferr!=nil {
log.Fatal(err)
}
fmt.Println(i32)
b, err:=reader.Peek(4)
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Peek bytes: %#v\n", b)
an, b, err:=reader.ReadBytes(4)
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Read %d bytes: %#v\n", an, b)
other, err:=reader.ReadAll()
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Read all: %#v\n", other)
// Output:// 258// 50595078// Peek bytes: []byte{0x7, 0x8, 0x9, 0xa}// Read 4 bytes: []byte{0x7, 0x8, 0x9, 0xa}// Read all: []byte{0xb, 0xc, 0xd, 0xe, 0xf}
}

Decode to fields

typeteststruct {
// Read 1 byteFieldboolFieldbyteField [1]byteFieldint8Fielduint8// Read 2 bytesFieldint16Fielduint16Field [2]byte// Read 4 bytesFieldint32Fielduint32Field [4]byte// Read 8 bytesFieldint64Fielduint64Field [8]byte// You can override lengthFieldint64`bin:"len:2"`// Or even use very weird byte lengths for intFieldint64`bin:"len:3"`Fieldint64`bin:"len:5"`Fieldint64`bin:"len:7"`// Fields of type int, uint and string are not read automatically // because the size is not known, you need to set it manuallyFieldint`bin:"len:2"`Fielduint`bin:"len:4"`Fieldstring`bin:"len:42"`// Can read arrays and slicesArray [2]int32// read 8 bytes (4+4byte for 2 int32)Slice []int32`bin:"len:2"`// read 8 bytes (4+4byte for 2 int32)// Also two-dimensional slices work (binstruct_test.go:307 Test_SliceOfSlice)Slice2D [][]int32`bin:"len:2,[len:2]"`// and even three-dimensional slices (binstruct_test.go:329 Test_SliceOfSliceOfSlice)Slice3D [][][]int32`bin:"len:2,[len:2,[len:2]]"`// Structures and embedding are also supported.Structstruct {
...
}
OtherStructOtherOther// embedding
}
typeOtherstruct {
...
}

Tags

typeteststruct {
IgnoredField []byte`bin:"-"`// ignore fieldCallMethod []byte`bin:"MethodName"`// Call method "MethodName"ReadLength []byte`bin:"len:42"`// read 42 bytes// Offsets test binstruct_test.go:9Offsetbyte`bin:"offset:42"`// move to 42 bytes from current position and read byteOffsetStartbyte`bin:"offsetStart:42"`// move to 42 bytes from start position and read byteOffsetEndbyte`bin:"offsetEnd:-42"`// move to -42 bytes from end position and read byteOffsetStartbyte`bin:"offsetStart:42, offset:10"`// also worked and equally `offsetStart:52`OffsetWithRestorebyte`bin:"offset:42, offsetRestore"`// move to 42 bytes from current position and read byte, then restore position to the previous one (before offset)// Calculations supported +,-,/,* and are performed from left to right that is 2+2*2=8 not 6!!!CalcTagValue []byte`bin:"len:10+5+2+3"`// equally len:20// You can refer to another field to get the value.DataLengthint// actual lengthValueFromOtherFieldstring`bin:"len:DataLength"`CalcValueFromOtherFieldstring`bin:"len:DataLength+10"`// also work calculations// Also supported nested structures.Innerstruct {
DataLengthint// actual length for ValueFromInnerField and CalcValueFromInnerField
}
ValueFromInnerFieldstring`bin:"len:Inner.DataLength"`CalcValueFromInnerFieldstring`bin:"len:Inner.DataLength+10"`// You can change the byte order directly from the tagUInt16LEuint16`bin:"le"`UInt16BEuint16`bin:"be"`// Or when you call the method, it will contain the Reader with the byte order you needCallMethodWithLEReaderuint16`bin:"MethodNameWithLEReader,le"`CallMethodWithBEReaderuint16`bin:"be,MethodNameWithBEReader"`
} // Method can be:func (*test) MethodName(r binstruct.Reader) (error) {}
// orfunc (*test) MethodName(r binstruct.Reader) (FieldType, error) {}

See the tests and examples for more information.

License

MIT License

About

Golang binary decoder for mapping data into the structure

Topics

Resources

Stars

114 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages