aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/lunixbochs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/lunixbochs')
-rw-r--r--vendor/github.com/lunixbochs/struc/.travis.yml10
-rw-r--r--vendor/github.com/lunixbochs/struc/LICENSE19
-rw-r--r--vendor/github.com/lunixbochs/struc/README.md103
-rw-r--r--vendor/github.com/lunixbochs/struc/bench_test.go165
-rw-r--r--vendor/github.com/lunixbochs/struc/binary.go52
-rw-r--r--vendor/github.com/lunixbochs/struc/custom.go33
-rw-r--r--vendor/github.com/lunixbochs/struc/custom_float16.go78
-rw-r--r--vendor/github.com/lunixbochs/struc/custom_float16_test.go56
-rw-r--r--vendor/github.com/lunixbochs/struc/custom_test.go97
-rw-r--r--vendor/github.com/lunixbochs/struc/field.go281
-rw-r--r--vendor/github.com/lunixbochs/struc/field_test.go77
-rw-r--r--vendor/github.com/lunixbochs/struc/fields.go169
-rw-r--r--vendor/github.com/lunixbochs/struc/fields_test.go59
-rw-r--r--vendor/github.com/lunixbochs/struc/legacy.go16
-rw-r--r--vendor/github.com/lunixbochs/struc/packable_test.go123
-rw-r--r--vendor/github.com/lunixbochs/struc/packer.go13
-rw-r--r--vendor/github.com/lunixbochs/struc/parse.go217
-rw-r--r--vendor/github.com/lunixbochs/struc/parse_test.go62
-rw-r--r--vendor/github.com/lunixbochs/struc/struc.go117
-rw-r--r--vendor/github.com/lunixbochs/struc/struc_test.go197
-rw-r--r--vendor/github.com/lunixbochs/struc/types.go136
-rw-r--r--vendor/github.com/lunixbochs/struc/types_test.go53
22 files changed, 2133 insertions, 0 deletions
diff --git a/vendor/github.com/lunixbochs/struc/.travis.yml b/vendor/github.com/lunixbochs/struc/.travis.yml
new file mode 100644
index 0000000..8316e89
--- /dev/null
+++ b/vendor/github.com/lunixbochs/struc/.travis.yml
@@ -0,0 +1,10 @@
+language: go
+sudo: false
+
+script: go test -v
+
+go:
+ - 1.2
+ - 1.3
+ - 1.4
+ - tip
diff --git a/vendor/github.com/lunixbochs/struc/LICENSE b/vendor/github.com/lunixbochs/struc/LICENSE
new file mode 100644
index 0000000..42e8263
--- /dev/null
+++ b/vendor/github.com/lunixbochs/struc/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015 Ryan Hileman
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/github.com/lunixbochs/struc/README.md b/vendor/github.com/lunixbochs/struc/README.md
new file mode 100644
index 0000000..c813497
--- /dev/null
+++ b/vendor/github.com/lunixbochs/struc/README.md
@@ -0,0 +1,103 @@
+[![Build Status](https://travis-ci.org/lunixbochs/struc.svg?branch=master)](https://travis-ci.org/lunixbochs/struc)
+
+struc
+====
+
+Struc exists to pack and unpack C-style structures from bytes, which is useful for binary files and network protocols. It could be considered an alternative to `encoding/binary`, which requires massive boilerplate for some similar operations.
+
+Take a look at an [example comparing `struc` and `encoding/binary`](https://bochs.info/p/cxvm9)
+
+Struc considers usability first. That said, it does cache reflection data and aims to be competitive with `encoding/binary` struct packing in every way, including performance.
+
+Example struct
+----
+
+```Go
+type Example struct {
+ Var int `struc:"int32,sizeof=Str"`
+ Str string
+ Weird []byte `struc:"[8]int64"`
+ Var []int `struc:"[]int32,little"`
+}
+```
+
+Struct tag format
+----
+
+ - ```Var []int `struc:"[]int32,little,sizeof=StringField"` ``` will pack Var as a slice of little-endian int32, and link it as the size of `StringField`.
+ - `sizeof=`: Indicates this field is a number used to track the length of a another field. `sizeof` fields are automatically updated on `Pack()` based on the current length of the tracked field, and are used to size the target field during `Unpack()`.
+ - Bare values will be parsed as type and endianness.
+
+Endian formats
+----
+
+ - `big` (default)
+ - `little`
+
+Recognized types
+----
+
+ - `pad` - this type ignores field contents and is backed by a `[length]byte` containing nulls
+ - `bool`
+ - `byte`
+ - `int8`, `uint8`
+ - `int16`, `uint16`
+ - `int32`, `uint32`
+ - `int64`, `uint64`
+ - `float32`
+ - `float64`
+
+Types can be indicated as arrays/slices using `[]` syntax. Example: `[]int64`, `[8]int32`.
+
+Bare slice types (those with no `[size]`) must have a linked `Sizeof` field.
+
+Private fields are ignored when packing and unpacking.
+
+Example code
+----
+
+```Go
+package main
+
+import (
+ "bytes"
+ "github.com/lunixbochs/struc"
+)
+
+type Example struct {
+ A int `struc:"big"`
+
+ // B will be encoded/decoded as a 16-bit int (a "short")
+ // but is stored as a native int in the struct
+ B int `struc:"int16"`
+
+ // the sizeof key links a buffer's size to any int field
+ Size int `struc:"int8,little,sizeof=Str"`
+ Str string
+
+ // you can get freaky if you want
+ Str2 string `struc:"[5]int64"`
+}
+
+func main() {
+ var buf bytes.Buffer
+ t := &Example{1, 2, 0, "test", "test2"}
+ err := struc.Pack(&buf, t)
+ o := &Example{}
+ err = struc.Unpack(&buf, o)
+}
+```
+
+Benchmark
+----
+
+`BenchmarkEncode` uses struc. `Stdlib` benchmarks use equivalent `encoding/binary` code. `Manual` encodes without any reflection, and should be considered an upper bound on performance (which generated code based on struc definitions should be able to achieve).
+
+```
+BenchmarkEncode 1000000 1265 ns/op
+BenchmarkStdlibEncode 1000000 1855 ns/op
+BenchmarkManualEncode 5000000 284 ns/op
+BenchmarkDecode 1000000 1259 ns/op
+BenchmarkStdlibDecode 1000000 1656 ns/op
+BenchmarkManualDecode 20000000 89.0 ns/op
+```
diff --git a/vendor/github.com/lunixbochs/struc/bench_test.go b/vendor/github.com/lunixbochs/struc/bench_test.go
new file mode 100644
index 0000000..d73c110
--- /dev/null
+++ b/vendor/github.com/lunixbochs/struc/bench_test.go
@@ -0,0 +1,165 @@
+package struc
+
+import (
+ "bytes"
+ "encoding/binary"
+ "testing"
+)
+
+type BenchExample struct {
+ Test [5]byte
+ A int32
+ B, C, D int16
+ Test2 [4]byte
+ Length int32
+}
+
+type BenchStrucExample struct {
+ Test [5]byte `struc:"[5]byte"`
+ A int `struc:"int32"`
+ B, C, D int `struc:"int16"`
+ Test2 [4]byte `struc:"[4]byte"`
+ Length int `struc:"int32,sizeof=Data"`
+ Data []byte
+}
+
+var benchRef = &BenchExample{
+ [5]byte{1, 2, 3, 4, 5},
+ 1, 2, 3, 4,
+ [4]byte{1, 2, 3, 4},
+ 8,
+}
+
+var eightBytes = []byte("8bytestr")
+
+var benchStrucRef = &BenchStrucExample{
+ [5]byte{1, 2, 3, 4, 5},
+ 1, 2, 3, 4,
+ [4]byte{1, 2, 3, 4},
+ 8, eightBytes,
+}
+
+func BenchmarkEncode(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ var buf bytes.Buffer
+ err := Pack(&buf, benchStrucRef)
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+func BenchmarkStdlibEncode(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ var buf bytes.Buffer
+ err := binary.Write(&buf, binary.BigEndian, benchRef)
+ if err != nil {
+ b.Fatal(err)
+ }
+ _, err = buf.Write(eightBytes)
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+func BenchmarkManualEncode(b *testing.B) {
+ order := binary.BigEndian
+ s := benchStrucRef
+ for i := 0; i < b.N; i++ {
+ var buf bytes.Buffer
+ tmp := make([]byte, 29)
+ copy(tmp[0:5], s.Test[:])
+ order.PutUint32(tmp[5:9], uint32(s.A))
+ order.PutUint16(tmp[9:11], uint16(s.B))
+ order.PutUint16(tmp[11:13], uint16(s.C))
+ order.PutUint16(tmp[13:15], uint16(s.D))
+ copy(tmp[15:19], s.Test2[:])
+ order.PutUint32(tmp[19:23], uint32(s.Length))
+ copy(tmp[23:], s.Data)
+ _, err := buf.Write(tmp)
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+func BenchmarkDecode(b *testing.B) {
+ var out BenchStrucExample
+ var buf bytes.Buffer
+ if err := Pack(&buf, benchStrucRef); err != nil {
+ b.Fatal(err)
+ }
+ bufBytes := buf.Bytes()
+ for i := 0; i < b.N; i++ {
+ buf := bytes.NewReader(bufBytes)
+ err := Unpack(buf, &out)
+ if err != nil {
+ b.Fatal(err)
+ }
+ out.Data = nil
+ }
+}
+
+func BenchmarkStdlibDecode(b *testing.B) {
+ var out BenchExample
+ var buf bytes.Buffer
+ binary.Write(&buf, binary.BigEndian, *benchRef)
+ _, err := buf.Write(eightBytes)
+ if err != nil {
+ b.Fatal(err)
+ }
+ bufBytes := buf.Bytes()
+ for i := 0; i < b.N; i++ {
+ buf := bytes.NewReader(bufBytes)
+ err := binary.Read(buf, binary.BigEndian, &out)
+ if err != nil {
+ b.Fatal(err)
+ }
+ tmp := make([]byte, out.Length)
+ _, err = buf.Read(tmp)
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+func BenchmarkManualDecode(b *testing.B) {
+ var o BenchStrucExample
+ var buf bytes.Buffer
+ if err := Pack(&buf, benchStrucRef); err != nil {
+ b.Fatal(err)
+ }
+ tmp := buf.Bytes()
+ order := binary.BigEndian
+ for i := 0; i < b.N; i++ {
+ copy(o.Test[:], tmp[0:5])
+ o.A = int(order.Uint32(tmp[5:9]))
+ o.B = int(order.Uint16(tmp[9:11]))
+ o.C = int(order.Uint16(tmp[11:13]))
+ o.D = int(order.Uint16(tmp[13:15]))
+ copy(o.Test2[:], tmp[15:19])
+ o.Length = int(order.Uint32(tmp[19:23]))
+ o.Data = make([]byte, o.Length)
+ copy(o.Data, tmp[23:])
+ }
+}
+
+func BenchmarkFullEncode(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ var buf bytes.Buffer
+ if err := Pack(&buf, reference); err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+func BenchmarkFullDecode(b *testing.B) {
+ var out Example
+ for i := 0; i < b.N; i++ {
+ buf := bytes.NewBuffer(referenceBytes)
+ if err := Unpack(buf, &out); err != nil {
+ b.Fatal(err)
+ }
+ }
+}
diff --git a/vendor/github.com/lunixbochs/struc/binary.go b/vendor/github.com/lunixbochs/struc/binary.go
new file mode 100644
index 0000000..4899d08
--- /dev/null
+++ b/vendor/github.com/lunixbochs/struc/binary.go
@@ -0,0 +1,52 @@
+package struc
+
+import (
+ "encoding/binary"
+ "io"
+ "reflect"
+)
+
+type byteWriter struct {
+ buf []byte
+ pos int
+}
+
+func (b byteWriter) Write(p []byte) (int, error) {
+ capacity := len(b.buf) - b.pos
+ if capacity < len(p) {
+ p = p[:capacity]
+ }
+ if len(p) > 0 {
+ copy(b.buf[b.pos:], p)
+ b.pos += len(p)
+ }
+ return len(p), nil
+}
+
+type binaryFallback reflect.Value
+
+func (b binaryFallback) String() string {
+ return b.String()
+}
+
+func (b binaryFallback) Sizeof(val reflect.Value, options *Options) int {
+ return binary.Size(val.Interface())
+}
+
+func (b binaryFallback) Pack(buf []byte, val reflect.Value, options *Options) (int, error) {
+ tmp := byteWriter{buf: buf}
+ var order binary.ByteOrder = binary.BigEndian
+ if options.Order != nil {
+ order = options.Order
+ }
+ err := binary.Write(tmp, order, val.Interface())
+ return tmp.pos, err
+}
+
+func (b binaryFallback) Unpack(r io.Reader, val reflect.Value, options *Options) error {
+ var order binary.ByteOrder = binary.BigEndian
+ if options.Order != nil {
+ order = options.Order
+ }
+ return binary.Read(r, order, val.Interface())
+}
diff --git a/vendor/github.com/lunixbochs/struc/custom.go b/vendor/github.com/lunixbochs/struc/custom.go
new file mode 100644
index 0000000..c468dce
--- /dev/null
+++ b/vendor/github.com/lunixbochs/struc/custom.go
@@ -0,0 +1,33 @@
+package struc
+
+import (
+ "io"
+ "reflect"
+)
+
+type Custom interface {
+ Pack(p []byte, opt *Options) (int, error)
+ Unpack(r io.Reader, length int, opt *Options) error
+ Size(opt *Options) int
+ String() string
+}
+
+type customFallback struct {
+ custom Custom
+}
+
+func (c customFallback) Pack(p []byte, val reflect.Value, opt *Options) (int, error) {
+ return c.custom.Pack(p, opt)
+}
+
+func (c customFallback) Unpack(r io.Reader, val reflect.Value, opt *Options) error {
+ return c.custom.Unpack(r, 1, opt)
+}
+
+func (c customFallback) Sizeof(val reflect.Value, opt *Options) int {
+ return c.custom.Size(opt)
+}
+
+func (c customFallback) String() string {
+ return c.custom.String()
+}
diff --git a/vendor/github.com/lunixbochs/struc/custom_float16.go b/vendor/github.com/lunixbochs/struc/custom_float16.go
new file mode 100644
index 0000000..722be76
--- /dev/null
+++ b/vendor/github.com/lunixbochs/struc/custom_float16.go
@@ -0,0 +1,78 @@
+package struc
+
+import (
+ "encoding/binary"
+ "io"
+ "math"
+ "strconv"
+)
+
+type Float16 float64
+
+func (f *Float16) Pack(p []byte, opt *Options) (int, error) {
+ order := opt.Order
+ if order == nil {
+ order = binary.BigEndian
+ }
+ sign := uint16(0)
+ if *f < 0 {
+ sign = 1
+ }
+ var frac, exp uint16
+ if math.IsInf(float64(*f), 0) {
+ exp = 0x1f
+ frac = 0
+ } else if math.IsNaN(float64(*f)) {
+ exp = 0x1f
+ frac = 1
+ } else {
+ bits := math.Float64bits(float64(*f))
+ exp64 := (bits >> 52) & 0x7ff
+ if exp64 != 0 {
+ exp = uint16((exp64 - 1023 + 15) & 0x1f)
+ }
+ frac = uint16((bits >> 42) & 0x3ff)
+ }
+ var out uint16
+ out |= sign << 15
+ out |= exp << 10
+ out |= frac & 0x3ff
+ order.PutUint16(p, out)
+ return 2, nil
+}
+func (f *Float16) Unpack(r io.Reader, length int, opt *Options) error {
+ order := opt.Order
+ if order == nil {
+ order = binary.BigEndian
+ }
+ var tmp [2]byte
+ if _, err := r.Read(tmp[:]); err != nil {
+ return err
+ }
+ val := order.Uint16(tmp[:2])
+ sign := (val >> 15) & 1
+ exp := int16((val >> 10) & 0x1f)
+ frac := val & 0x3ff
+ if exp == 0x1f {
+ if frac != 0 {
+ *f = Float16(math.NaN())
+ } else {
+ *f = Float16(math.Inf(int(sign)*-2 + 1))
+ }
+ } else {
+ var bits uint64
+ bits |= uint64(sign) << 63
+ bits |= uint64(frac) << 42
+ if exp > 0 {
+ bits |= uint64(exp-15+1023) << 52
+ }
+ *f = Float16(math.Float64frombits(bits))
+ }
+ return nil
+}
+func (f *Float16) Size(opt *Options) int {
+ return 2
+}
+func (f *Float16) String() string {
+ return strconv.FormatFloat(float64(*f), 'g', -1, 32)
+}
diff --git a/vendor/github.com/lunixbochs/struc/custom_float16_test.go b/vendor/github.com/lunixbochs/struc/custom_float16_test.go
new file mode 100644
index 0000000..11f73cb
--- /dev/null
+++ b/vendor/github.com/lunixbochs/struc/custom_float16_test.go
@@ -0,0 +1,56 @@
+package struc
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "math"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+func TestFloat16(t *testing.T) {
+ // test cases from https://en.wikipedia.org/wiki/Half-precision_floating-point_format#Half_precision_examples
+ tests := []struct {
+ B string
+ F float64
+ }{
+ //s expnt significand
+ {"0 01111 0000000000", 1},
+ {"0 01111 0000000001", 1.0009765625},
+ {"1 10000 0000000000", -2},
+ {"0 11110 1111111111", 65504},
+ // {"0 00001 0000000000", 0.0000610352},
+ // {"0 00000 1111111111", 0.0000609756},
+ // {"0 00000 0000000001", 0.0000000596046},
+ {"0 00000 0000000000", 0},
+ // {"1 00000 0000000000", -0},
+ {"0 11111 0000000000", math.Inf(1)},
+ {"1 11111 0000000000", math.Inf(-1)},
+ {"0 01101 0101010101", 0.333251953125},
+ }
+ for _, test := range tests {
+ var buf bytes.Buffer
+ f := Float16(test.F)
+ if err := Pack(&buf, &f); err != nil {
+ t.Error("pack failed:", err)
+ continue
+ }
+ bitval, _ := strconv.ParseUint(strings.Replace(test.B, " ", "", -1), 2, 16)
+ tmp := binary.BigEndian.Uint16(buf.Bytes())
+ if tmp != uint16(bitval) {
+ t.Errorf("incorrect pack: %s != %016b (%f)", test.B, tmp, test.F)
+ continue
+ }
+ var f2 Float16
+ if err := Unpack(&buf, &f2); err != nil {
+ t.Error("unpack failed:", err)
+ continue
+ }
+ // let sprintf deal with (im)precision for me here
+ if fmt.Sprintf("%f", f) != fmt.Sprintf("%f", f2) {
+ t.Errorf("incorrect unpack: %016b %f != %f", bitval, f, f2)
+ }
+ }
+}
diff --git a/vendor/github.com/lunixbochs/struc/custom_test.go b/vendor/github.com/lunixbochs/struc/custom_test.go
new file mode 100644
index 0000000..e601166
--- /dev/null
+++ b/vendor/github.com/lunixbochs/struc/custom_test.go
@@ -0,0 +1,97 @@
+package struc
+
+import (
+ "bytes"
+ "encoding/binary"
+ "io"
+ "strconv"
+ "testing"
+)
+
+type Int3 uint32
+
+func (i *Int3) Pack(p []byte, opt *Options) (int, error) {
+ var tmp [4]byte
+ binary.BigEndian.PutUint32(tmp[:], uint32(*i))
+ copy(p, tmp[1:])
+ return 3, nil
+}
+func (i *Int3) Unpack(r io.Reader, length int, opt *Options) error {
+ var tmp [4]byte
+ if _, err := r.Read(tmp[1:]); err != nil {
+ return err
+ }
+ *i = Int3(binary.BigEndian.Uint32(tmp[:]))
+ return nil
+}
+func (i *Int3) Size(opt *Options) int {
+ return 3
+}
+func (i *Int3) String() string {
+ return strconv.FormatUint(uint64(*i), 10)
+}
+
+func TestCustom(t *testing.T) {
+ var buf bytes.Buffer
+ var i Int3 = 3
+ if err := Pack(&buf, &i); err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(buf.Bytes(), []byte{0, 0, 3}) {
+ t.Fatal("error packing custom int")
+ }
+ var i2 Int3
+ if err := Unpack(&buf, &i2); err != nil {
+ t.Fatal(err)
+ }
+ if i2 != 3 {
+ t.Fatal("error unpacking custom int")
+ }
+}
+
+type Int3Struct struct {
+ I Int3
+}
+
+func TestCustomStruct(t *testing.T) {
+ var buf bytes.Buffer
+ i := Int3Struct{3}
+ if err := Pack(&buf, &i); err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(buf.Bytes(), []byte{0, 0, 3}) {
+ t.Fatal("error packing custom int struct")
+ }
+ var i2 Int3Struct
+ if err := Unpack(&buf, &i2); err != nil {
+ t.Fatal(err)
+ }
+ if i2.I != 3 {
+ t.Fatal("error unpacking custom int struct")
+ }
+}
+
+// TODO: slices of custom types don't work yet
+/*
+type Int3SliceStruct struct {
+ I [2]Int3
+}
+
+func TestCustomSliceStruct(t *testing.T) {
+ var buf bytes.Buffer
+ i := Int3SliceStruct{[2]Int3{3, 4}}
+ if err := Pack(&buf, &i); err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(buf.Bytes(), []byte{0, 0, 3}) {
+ t.Fatal("error packing custom int struct")
+ }
+ var i2 Int3SliceStruct
+ if err := Unpack(&buf, &i2); err != nil {
+ t.Fatal(err)
+ }
+ if i2.I[0] != 3 && i2.I[1] != 4 {
+ t.Fatal("error unpacking custom int struct")
+ }
+}
+*/
diff --git a/vendor/github.com/lunixbochs/struc/field.go b/vendor/github.com/lunixbochs/struc/field.go
new file mode 100644
index 0000000..4af10a7
--- /dev/null
+++ b/vendor/github.com/lunixbochs/struc/field.go
@@ -0,0 +1,281 @@
+package struc
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "math"
+ "reflect"
+)
+
+type Field struct {
+ Name string
+ CanSet bool
+ Ptr bool
+ Index int
+ Type Type
+ defType Type
+ Array bool
+ Slice bool
+ Len int
+ Order binary.ByteOrder
+ Sizeof []int
+ Sizefrom []int
+ Fields Fields
+ kind reflect.Kind
+}
+
+func (f *Field) String() string {
+ var out string
+ if f.Type == Pad {
+ return fmt.Sprintf("{type: Pad, len: %d}", f.Len)
+ } else {
+ out = fmt.Sprintf("type: %s, order: %v", f.Type.String(), f.Order)
+ }
+ if f.Sizefrom != nil {
+ out += fmt.Sprintf(", sizefrom: %v", f.Sizefrom)
+ } else if f.Len > 0 {
+ out += fmt.Sprintf(", len: %d", f.Len)
+ }
+ if f.Sizeof != nil {
+ out += fmt.Sprintf(", sizeof: %v", f.Sizeof)
+ }
+ return "{" + out + "}"
+}
+
+func (f *Field) Size(val reflect.Value, options *Options) int {
+ typ := f.Type.Resolve(options)
+ size := 0
+ if typ == Struct {
+ vals := []reflect.Value{val}
+ if f.Slice {
+ vals = make([]reflect.Value, val.Len())
+ for i := 0; i < val.Len(); i++ {
+ vals[i] = val.Index(i)
+ }
+ }
+ for _, val := range vals {
+ size += f.Fields.Sizeof(val, options)
+ }
+ } else if typ == Pad {
+ size = f.Len
+ } else if f.Slice || f.kind == reflect.String {
+ length := val.Len()
+ if f.Len > 1 {
+ length = f.Len
+ }
+ size = length * typ.Size()
+ } else if typ == CustomType {
+ return val.Addr().Interface().(Custom).Size(options)
+ } else {
+ size = typ.Size()
+ }
+ align := options.ByteAlign
+ if align > 0 && size < align {
+ size = align
+ }
+ return size
+}
+
+func (f *Field) packVal(buf []byte, val reflect.Value, length int, options *Options) (size int, err error) {
+ order := f.Order
+ if options.Order != nil {
+ order = options.Order
+ }
+ if f.Ptr {
+ val = val.Elem()
+ }
+ typ := f.Type.Resolve(options)
+ switch typ {
+ case Struct:
+ return f.Fields.Pack(buf, val, options)
+ case Bool, Int8, Int16, Int32, Int64, Uint8, Uint16, Uint32, Uint64:
+ size = typ.Size()
+ var n uint64
+ switch f.kind {
+ case reflect.Bool:
+ if val.Bool() {
+ n = 1
+ } else {
+ n = 0
+ }
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ n = uint64(val.Int())
+ default:
+ n = val.Uint()
+ }
+ switch typ {
+ case Bool:
+ if n != 0 {
+ buf[0] = 1
+ } else {
+ buf[0] = 0
+ }
+ case Int8, Uint8:
+ buf[0] = byte(n)
+ case Int16, Uint16:
+ order.PutUint16(buf, uint16(n))
+ case Int32, Uint32:
+ order.PutUint32(buf, uint32(n))
+ case Int64, Uint64:
+ order.PutUint64(buf, uint64(n))
+ }
+ case Float32, Float64:
+ size = typ.Size()
+ n := val.Float()
+ switch typ {
+ case Float32:
+ order.PutUint32(buf, math.Float32bits(float32(n)))
+ case Float64:
+ order.PutUint64(buf, math.Float64bits(n))
+ }
+ case String:
+ switch f.kind {
+ case reflect.String:
+ size = val.Len()
+ copy(buf, []byte(val.String()))
+ default:
+ // TODO: handle kind != bytes here
+ size = val.Len()
+ copy(buf, val.Bytes())
+ }
+ case CustomType:
+ return val.Addr().Interface().(Custom).Pack(buf, options)
+ default:
+ panic(fmt.Sprintf("no pack handler for type: %s", typ))
+ }
+ return
+}
+
+func (f *Field) Pack(buf []byte, val reflect.Value, length int, options *Options) (int, error) {
+ typ := f.Type.Resolve(options)
+ if typ == Pad {
+ for i := 0; i < length; i++ {
+ buf[i] = 0
+ }
+ return length, nil
+ }
+ if f.Slice {
+ // special case strings and byte slices for performance
+ end := val.Len()
+ if !f.Array && typ == Uint8 && (f.defType == Uint8 || f.kind == reflect.String) {
+ var tmp []byte
+ if f.kind == reflect.String {
+ tmp = []byte(val.String())
+ } else {
+ tmp = val.Bytes()
+ }
+ copy(buf, tmp)
+ if end < length {
+ // TODO: allow configuring pad byte?
+ rep := bytes.Repeat([]byte{0}, length-end)
+ copy(buf[end:], rep)
+ return length, nil
+ }
+ return val.Len(), nil
+ }
+ pos := 0
+ var zero reflect.Value
+ if end < length {
+ zero = reflect.Zero(val.Type().Elem())
+ }
+ for i := 0; i < length; i++ {
+ cur := zero
+ if i < end {
+ cur = val.Index(i)
+ }
+ if n, err := f.packVal(buf[pos:], cur, 1, options); err != nil {
+ return pos, err
+ } else {
+ pos += n
+ }
+ }
+ return pos, nil
+ } else {
+ return f.packVal(buf, val, length, options)
+ }
+}
+
+func (f *Field) unpackVal(buf []byte, val reflect.Value, length int, options *Options) error {
+ order := f.Order
+ if options.Order != nil {
+ order = options.Order
+ }
+ if f.Ptr {
+ val = val.Elem()
+ }
+ typ := f.Type.Resolve(options)
+ switch typ {
+ case Float32, Float64:
+ var n float64
+ switch typ {
+ case Float32:
+ n = float64(math.Float32frombits(order.Uint32(buf)))
+ case Float64:
+ n = math.Float64frombits(order.Uint64(buf))
+ }
+ switch f.kind {
+ case reflect.Float32, reflect.Float64:
+ val.SetFloat(n)
+ default:
+ return fmt.Errorf("struc: refusing to unpack float into field %s of type %s", f.Name, f.kind.String())
+ }
+ case Bool, Int8, Int16, Int32, Int64, Uint8, Uint16, Uint32, Uint64:
+ var n uint64
+ switch typ {
+ case Bool, Int8, Uint8:
+ n = uint64(buf[0])
+ case Int16, Uint16:
+ n = uint64(order.Uint16(buf))
+ case Int32, Uint32:
+ n = uint64(order.Uint32(buf))
+ case Int64, Uint64:
+ n = uint64(order.Uint64(buf))
+ }
+ switch f.kind {
+ case reflect.Bool:
+ val.SetBool(n != 0)
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ val.SetInt(int64(n))
+ default:
+ val.SetUint(n)
+ }
+ default:
+ panic(fmt.Sprintf("no unpack handler for type: %s", typ))
+ }
+ return nil
+}
+
+func (f *Field) Unpack(buf []byte, val reflect.Value, length int, options *Options) error {
+ typ := f.Type.Resolve(options)
+ if typ == Pad || f.kind == reflect.String {
+ if typ == Pad {
+ return nil
+ } else {
+ val.SetString(string(buf))
+ return nil
+ }
+ } else if f.Slice {
+ if val.Cap() < length {
+ val.Set(reflect.MakeSlice(val.Type(), length, length))
+ } else if val.Len() < length {
+ val.Set(val.Slice(0, length))
+ }
+ // special case byte slices for performance
+ if !f.Array && typ == Uint8 && f.defType == Uint8 {
+ copy(val.Bytes(), buf[:length])
+ return nil
+ }
+ pos := 0
+ size := typ.Size()
+ for i := 0; i < length; i++ {
+ if err := f.unpackVal(buf[pos:pos+size], val.Index(i), 1, options); err != nil {
+ return err
+ }
+ pos += size
+ }
+ return nil
+ } else {
+ return f.unpackVal(buf, val, length, options)
+ }
+}
diff --git a/vendor/github.com/lunixbochs/struc/field_test.go b/vendor/github.com/lunixbochs/struc/field_test.go
new file mode 100644
index 0000000..45a07b2
--- /dev/null
+++ b/vendor/github.com/lunixbochs/struc/field_test.go
@@ -0,0 +1,77 @@
+package struc
+
+import (
+ "bytes"
+ "testing"
+)
+
+type badFloat struct {
+ BadFloat int `struc:"float64"`
+}
+
+func TestBadFloatField(t *testing.T) {
+ buf := bytes.NewReader([]byte("00000000"))
+ err := Unpack(buf, &badFloat{})
+ if err == nil {
+ t.Fatal("failed to error on bad float unpack")
+ }
+}
+
+type emptyLengthField struct {
+ Strlen int `struc:"sizeof=Str"`
+ Str []byte
+}
+
+func TestEmptyLengthField(t *testing.T) {
+ var buf bytes.Buffer
+ s := &emptyLengthField{0, []byte("test")}
+ o := &emptyLengthField{}
+ if err := Pack(&buf, s); err != nil {
+ t.Fatal(err)
+ }
+ if err := Unpack(&buf, o); err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(s.Str, o.Str) {
+ t.Fatal("empty length field encode failed")
+ }
+}
+
+type fixedSlicePad struct {
+ Field []byte `struc:"[4]byte"`
+}
+
+func TestFixedSlicePad(t *testing.T) {
+ var buf bytes.Buffer
+ ref := []byte{0, 0, 0, 0}
+ s := &fixedSlicePad{}
+ if err := Pack(&buf, s); err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(buf.Bytes(), ref) {
+ t.Fatal("implicit fixed slice pack failed")
+ }
+ if err := Unpack(&buf, s); err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(s.Field, ref) {
+ t.Fatal("implicit fixed slice unpack failed")
+ }
+}
+
+type sliceCap struct {
+ Len int `struc:"sizeof=Field"`
+ Field []byte
+}
+
+func TestSliceCap(t *testing.T) {
+ var buf bytes.Buffer
+ tmp := &sliceCap{0, []byte("1234")}
+ if err := Pack(&buf, tmp); err != nil {
+ t.Fatal(err)
+ }
+ tmp.Field = make([]byte, 0, 4)
+ if err := Unpack(&buf, tmp); err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/vendor/github.com/lunixbochs/struc/fields.go b/vendor/github.com/lunixbochs/struc/fields.go
new file mode 100644
index 0000000..80e2045
--- /dev/null
+++ b/vendor/github.com/lunixbochs/struc/fields.go
@@ -0,0 +1,169 @@
+package struc
+
+import (
+ "encoding/binary"
+ "fmt"
+ "io"
+ "reflect"
+ "strings"
+)
+
+type Fields []*Field
+
+func (f Fields) SetByteOrder(order binary.ByteOrder) {
+ for _, field := range f {
+ field.Order = order
+ }
+}
+
+func (f Fields) String() string {
+ fields := make([]string, len(f))
+ for i, field := range f {
+ fields[i] = field.String()
+ }
+ return "{" + strings.Join(fields, ", ") + "}"
+}
+
+func (f Fields) Sizeof(val reflect.Value, options *Options) int {
+ for val.Kind() == reflect.Ptr {
+ val = val.Elem()
+ }
+ size := 0
+ for i, field := range f {
+ v := val.Field(i)
+ if v.CanSet() {
+ size += field.Size(v, options)
+ }
+ }
+ return size
+}
+
+func (f Fields) sizefrom(val reflect.Value, index []int) int {
+ field := val.FieldByIndex(index)
+ switch field.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return int(field.Int())
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ n := int(field.Uint())
+ // all the builtin array length types are native int
+ // so this guards against weird truncation
+ if n < 0 {
+ return 0
+ }
+ return n
+ default:
+ name := val.Type().FieldByIndex(index).Name
+ panic(fmt.Sprintf("sizeof field %T.%s not an integer type", val.Interface(), name))
+ }
+}
+
+func (f Fields) Pack(buf []byte, val reflect.Value, options *Options) (int, error) {
+ for val.Kind() == reflect.Ptr {
+ val = val.Elem()
+ }
+ pos := 0
+ for i, field := range f {
+ if !field.CanSet {
+ continue
+ }
+ v := val.Field(i)
+ length := field.Len
+ if field.Sizefrom != nil {
+ length = f.sizefrom(val, field.Sizefrom)
+ }
+ if length <= 0 && field.Slice {
+ length = v.Len()
+ }
+ if field.Sizeof != nil {
+ length := val.FieldByIndex(field.Sizeof).Len()
+ switch field.kind {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ // allocating a new int here has fewer side effects (doesn't update the original struct)
+ // but it's a wasteful allocation
+ // the old method might work if we just cast the temporary int/uint to the target type
+ v = reflect.New(v.Type()).Elem()
+ v.SetInt(int64(length))
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ v = reflect.New(v.Type()).Elem()
+ v.SetUint(uint64(length))
+ default:
+ panic(fmt.Sprintf("sizeof field is not int or uint type: %s, %s", field.Name, v.Type()))
+ }
+ }
+ if n, err := field.Pack(buf[pos:], v, length, options); err != nil {
+ return n, err
+ } else {
+ pos += n
+ }
+ }
+ return pos, nil
+}
+
+func (f Fields) Unpack(r io.Reader, val reflect.Value, options *Options) error {
+ for val.Kind() == reflect.Ptr {
+ val = val.Elem()
+ }
+ var tmp [8]byte
+ var buf []byte
+ for i, field := range f {
+ if !field.CanSet {
+ continue
+ }
+ v := val.Field(i)
+ length := field.Len
+ if field.Sizefrom != nil {
+ length = f.sizefrom(val, field.Sizefrom)
+ }
+ if v.Kind() == reflect.Ptr && !v.Elem().IsValid() {
+ v.Set(reflect.New(v.Type().Elem()))
+ }
+ if field.Type == Struct {
+ if field.Slice {
+ vals := reflect.MakeSlice(v.Type(), length, length)
+ for i := 0; i < length; i++ {
+ v := vals.Index(i)
+ fields, err := parseFields(v)
+ if err != nil {
+ return err
+ }
+ if err := fields.Unpack(r, v, options); err != nil {
+ return err
+ }
+ }
+ v.Set(vals)
+ } else {
+ // TODO: DRY (we repeat the inner loop above)
+ fields, err := parseFields(v)
+ if err != nil {
+ return err
+ }
+ if err := fields.Unpack(r, v, options); err != nil {
+ return err
+ }
+ }
+ continue
+ } else {
+ typ := field.Type.Resolve(options)
+ if typ == CustomType {
+ if err := v.Addr().Interface().(Custom).Unpack(r, length, options); err != nil {
+ return err
+ }
+ } else {
+ size := length * field.Type.Resolve(options).Size()
+ if size < 8 {
+ buf = tmp[:size]
+ } else {
+ buf = make([]byte, size)
+ }
+ if _, err := io.ReadFull(r, buf); err != nil {
+ return err
+ }
+ err := field.Unpack(buf[:size], v, length, options)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/lunixbochs/struc/fields_test.go b/vendor/github.com/lunixbochs/struc/fields_test.go
new file mode 100644
index 0000000..850e377
--- /dev/null
+++ b/vendor/github.com/lunixbochs/struc/fields_test.go
@@ -0,0 +1,59 @@
+package struc
+
+import (
+ "bytes"
+ "reflect"
+ "testing"
+)
+
+var refVal = reflect.ValueOf(reference)
+
+func TestFieldsParse(t *testing.T) {
+ if _, err := parseFields(refVal); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestFieldsString(t *testing.T) {
+ fields, _ := parseFields(refVal)
+ fields.String()
+}
+
+type sizefromStruct struct {
+ Size1 uint `struc:"sizeof=Var1"`
+ Var1 []byte
+ Size2 int `struc:"sizeof=Var2"`
+ Var2 []byte
+}
+
+func TestFieldsSizefrom(t *testing.T) {
+ var test = sizefromStruct{
+ Var1: []byte{1, 2, 3},
+ Var2: []byte{4, 5, 6},
+ }
+ var buf bytes.Buffer
+ err := Pack(&buf, &test)
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = Unpack(&buf, &test)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+type sizefromStructBad struct {
+ Size1 string `struc:"sizeof=Var1"`
+ Var1 []byte
+}
+
+func TestFieldsSizefromBad(t *testing.T) {
+ var test = &sizefromStructBad{Var1: []byte{1, 2, 3}}
+ var buf bytes.Buffer
+ defer func() {
+ if err := recover(); err == nil {
+ t.Fatal("failed to panic on bad sizeof type")
+ }
+ }()
+ Pack(&buf, &test)
+}
diff --git a/vendor/github.com/lunixbochs/struc/legacy.go b/vendor/github.com/lunixbochs/struc/legacy.go
new file mode 100644
index 0000000..5baf70d
--- /dev/null
+++ b/vendor/github.com/lunixbochs/struc/legacy.go
@@ -0,0 +1,16 @@
+package struc
+
+import (
+ "encoding/binary"
+ "io"
+)
+
+// Deprecated. Use PackWithOptions.
+func PackWithOrder(w io.Writer, data interface{}, order binary.ByteOrder) error {
+ return PackWithOptions(w, data, &Options{Order: order})
+}
+
+// Deprecated. Use UnpackWithOptions.
+func UnpackWithOrder(r io.Reader, data interface{}, order binary.ByteOrder) error {
+ return UnpackWithOptions(r, data, &Options{Order: order})
+}
diff --git a/vendor/github.com/lunixbochs/struc/packable_test.go b/vendor/github.com/lunixbochs/struc/packable_test.go
new file mode 100644
index 0000000..ec2bed9
--- /dev/null
+++ b/vendor/github.com/lunixbochs/struc/packable_test.go
@@ -0,0 +1,123 @@
+package struc
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+)
+
+var packableReference = []byte{
+ 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, 5, 0, 6, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16,
+ 0, 17, 0, 18, 0, 19, 0, 20, 0, 21, 0, 22, 0, 23, 0, 24,
+}
+
+func TestPackable(t *testing.T) {
+ var (
+ buf bytes.Buffer
+
+ i8 int8 = 1
+ i16 int16 = 2
+ i32 int32 = 3
+ i64 int64 = 4
+ u8 uint8 = 5
+ u16 uint16 = 6
+ u32 uint32 = 7
+ u64 uint64 = 8
+
+ u8a = [8]uint8{9, 10, 11, 12, 13, 14, 15, 16}
+ u16a = [8]uint16{17, 18, 19, 20, 21, 22, 23, 24}
+ )
+ // pack tests
+ if err := Pack(&buf, i8); err != nil {
+ t.Fatal(err)
+ }
+ if err := Pack(&buf, i16); err != nil {
+ t.Fatal(err)
+ }
+ if err := Pack(&buf, i32); err != nil {
+ t.Fatal(err)
+ }
+ if err := Pack(&buf, i64); err != nil {
+ t.Fatal(err)
+ }
+ if err := Pack(&buf, u8); err != nil {
+ t.Fatal(err)
+ }
+ if err := Pack(&buf, u16); err != nil {
+ t.Fatal(err)
+ }
+ if err := Pack(&buf, u32); err != nil {
+ t.Fatal(err)
+ }
+ if err := Pack(&buf, u64); err != nil {
+ t.Fatal(err)
+ }
+ if err := Pack(&buf, u8a[:]); err != nil {
+ t.Fatal(err)
+ }
+ if err := Pack(&buf, u16a[:]); err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(buf.Bytes(), packableReference) {
+ fmt.Println(buf.Bytes())
+ fmt.Println(packableReference)
+ t.Fatal("Packable Pack() did not match reference.")
+ }
+ // unpack tests
+ i8 = 0
+ i16 = 0
+ i32 = 0
+ i64 = 0
+ u8 = 0
+ u16 = 0
+ u32 = 0
+ u64 = 0
+ if err := Unpack(&buf, &i8); err != nil {
+ t.Fatal(err)
+ }
+ if err := Unpack(&buf, &i16); err != nil {
+ t.Fatal(err)
+ }
+ if err := Unpack(&buf, &i32); err != nil {
+ t.Fatal(err)
+ }
+ if err := Unpack(&buf, &i64); err != nil {
+ t.Fatal(err)
+ }
+ if err := Unpack(&buf, &u8); err != nil {
+ t.Fatal(err)
+ }
+ if err := Unpack(&buf, &u16); err != nil {
+ t.Fatal(err)
+ }
+ if err := Unpack(&buf, &u32); err != nil {
+ t.Fatal(err)
+ }
+ if err := Unpack(&buf, &u64); err != nil {
+ t.Fatal(err)
+ }
+ if err := Unpack(&buf, u8a[:]); err != nil {
+ t.Fatal(err)
+ }
+ if err := Unpack(&buf, u16a[:]); err != nil {
+ t.Fatal(err)
+ }
+ // unpack checks
+ if i8 != 1 || i16 != 2 || i32 != 3 || i64 != 4 {
+ t.Fatal("Signed integer unpack failed.")
+ }
+ if u8 != 5 || u16 != 6 || u32 != 7 || u64 != 8 {
+ t.Fatal("Unsigned integer unpack failed.")
+ }
+ for i := 0; i < 8; i++ {
+ if u8a[i] != uint8(i+9) {
+ t.Fatal("uint8 array unpack failed.")
+ }
+ }
+ for i := 0; i < 8; i++ {
+ if u16a[i] != uint16(i+17) {
+ t.Fatal("uint16 array unpack failed.")
+ }
+ }
+}
diff --git a/vendor/github.com/lunixbochs/struc/packer.go b/vendor/github.com/lunixbochs/struc/packer.go
new file mode 100644
index 0000000..a3a91a2
--- /dev/null
+++ b/vendor/github.com/lunixbochs/struc/packer.go
@@ -0,0 +1,13 @@
+package struc
+
+import (
+ "io"
+ "reflect"
+)
+
+type Packer interface {
+ Pack(buf []byte, val reflect.Value, options *Options) (int, error)
+ Unpack(r io.Reader, val reflect.Value, options *Options) error
+ Sizeof(val reflect.Value, options *Options) int
+ String() string
+}
diff --git a/vendor/github.com/lunixbochs/struc/parse.go b/vendor/github.com/lunixbochs/struc/parse.go
new file mode 100644
index 0000000..5a30d53
--- /dev/null
+++ b/vendor/github.com/lunixbochs/struc/parse.go
@@ -0,0 +1,217 @@
+package struc
+
+import (
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "reflect"
+ "regexp"
+ "strconv"
+ "strings"
+ "sync"
+)
+
+// struc:"int32,big,sizeof=Data"
+
+var tagWordsRe = regexp.MustCompile(`(\[|\b)[^"]+\b+$`)
+
+type strucTag struct {
+ Type string
+ Order binary.ByteOrder
+ Sizeof string
+}
+
+func parseStrucTag(tag reflect.StructTag) *strucTag {
+ t := &strucTag{
+ Order: binary.BigEndian,
+ }
+ tagStr := tag.Get("struc")
+ if tagStr == "" {
+ // someone's going to typo this (I already did once)
+ // sorry if you made a module actually using this tag
+ // and you're mad at me now
+ tagStr = tag.Get("struct")
+ }
+ for _, s := range strings.Split(tagStr, ",") {
+ if strings.HasPrefix(s, "sizeof=") {
+ tmp := strings.SplitN(s, "=", 2)
+ t.Sizeof = tmp[1]
+ } else if s == "big" {
+ t.Order = binary.BigEndian
+ } else if s == "little" {
+ t.Order = binary.LittleEndian
+ } else {
+ t.Type = s
+ }
+ }
+ return t
+}
+
+var typeLenRe = regexp.MustCompile(`^\[(\d*)\]`)
+
+func parseField(f reflect.StructField) (fd *Field, err error) {
+ tag := parseStrucTag(f.Tag)
+ var ok bool
+ fd = &Field{
+ Name: f.Name,
+ Len: 1,
+ Order: tag.Order,
+ Slice: false,
+ kind: f.Type.Kind(),
+ }
+ switch fd.kind {
+ case reflect.Array:
+ fd.Slice = true
+ fd.Array = true
+ fd.Len = f.Type.Len()
+ fd.kind = f.Type.Elem().Kind()
+ case reflect.Slice:
+ fd.Slice = true
+ fd.Len = -1
+ fd.kind = f.Type.Elem().Kind()
+ case reflect.Ptr:
+ fd.Ptr = true
+ fd.kind = f.Type.Elem().Kind()
+ }
+ // check for custom types
+ tmp := reflect.New(f.Type)
+ if _, ok := tmp.Interface().(Custom); ok {
+ fd.Type = CustomType
+ return
+ }
+ var defTypeOk bool
+ fd.defType, defTypeOk = reflectTypeMap[fd.kind]
+ // find a type in the struct tag
+ pureType := typeLenRe.ReplaceAllLiteralString(tag.Type, "")
+ if fd.Type, ok = typeLookup[pureType]; ok {
+ fd.Len = 1
+ match := typeLenRe.FindAllStringSubmatch(tag.Type, -1)
+ if len(match) > 0 && len(match[0]) > 1 {
+ fd.Slice = true
+ first := match[0][1]
+ // Field.Len = -1 indicates a []slice
+ if first == "" {
+ fd.Len = -1
+ } else {
+ fd.Len, err = strconv.Atoi(first)
+ }
+ }
+ return
+ }
+ // the user didn't specify a type
+ switch f.Type {
+ case reflect.TypeOf(Size_t(0)):
+ fd.Type = SizeType
+ case reflect.TypeOf(Off_t(0)):
+ fd.Type = OffType
+ default:
+ if defTypeOk {
+ fd.Type = fd.defType
+ } else {
+ err = errors.New("struc: Could not find field type.")
+ }
+ }
+ return
+}
+
+func parseFieldsLocked(v reflect.Value) (Fields, error) {
+ // we need to repeat this logic because parseFields() below can't be recursively called due to locking
+ for v.Kind() == reflect.Ptr {
+ v = v.Elem()
+ }
+ t := v.Type()
+ if v.NumField() < 1 {
+ return nil, errors.New("struc: Struct has no fields.")
+ }
+ sizeofMap := make(map[string][]int)
+ fields := make(Fields, 0, v.NumField())
+ for i := 0; i < t.NumField(); i++ {
+ field := t.Field(i)
+ f, err := parseField(field)
+ if err != nil {
+ return nil, err
+ }
+ f.CanSet = v.Field(i).CanSet()
+ if !f.CanSet {
+ continue
+ }
+ f.Index = i
+ tag := parseStrucTag(field.Tag)
+ if tag.Sizeof != "" {
+ target, ok := t.FieldByName(tag.Sizeof)
+ if !ok {
+ return nil, fmt.Errorf("struc: `sizeof=%s` field does not exist", tag.Sizeof)
+ }
+ f.Sizeof = target.Index
+ sizeofMap[tag.Sizeof] = field.Index
+ }
+ if sizefrom, ok := sizeofMap[field.Name]; ok {
+ f.Sizefrom = sizefrom
+ }
+ if f.Len == -1 && f.Sizefrom == nil {
+ return nil, fmt.Errorf("struc: field `%s` is a slice with no length or sizeof field", field.Name)
+ }
+ // recurse into nested structs
+ // TODO: handle loops (probably by indirecting the []Field and putting pointer in cache)
+ if f.Type == Struct {
+ typ := field.Type
+ if f.Ptr {
+ typ = typ.Elem()
+ }
+ if f.Slice {
+ typ = typ.Elem()
+ }
+ f.Fields, err = parseFieldsLocked(reflect.New(typ))
+ if err != nil {
+ return nil, err
+ }
+ }
+ fields = append(fields, f)
+ }
+ return fields, nil
+}
+
+var fieldCache = make(map[reflect.Type]Fields)
+var fieldCacheLock sync.RWMutex
+var parseLock sync.Mutex
+
+func fieldCacheLookup(t reflect.Type) Fields {
+ fieldCacheLock.RLock()
+ defer fieldCacheLock.RUnlock()
+ if cached, ok := fieldCache[t]; ok {
+ return cached
+ }
+ return nil
+}
+
+func parseFields(v reflect.Value) (Fields, error) {
+ for v.Kind() == reflect.Ptr {
+ v = v.Elem()
+ }
+ t := v.Type()
+
+ // fast path: hopefully the field parsing is already cached
+ if cached := fieldCacheLookup(t); cached != nil {
+ return cached, nil
+ }
+
+ // hold a global lock so multiple goroutines can't parse (the same) fields at once
+ parseLock.Lock()
+ defer parseLock.Unlock()
+
+ // check cache a second time, in case parseLock was just released by
+ // another thread who filled the cache for us
+ if cached := fieldCacheLookup(t); cached != nil {
+ return cached, nil
+ }
+
+ // no luck, time to parse and fill the cache ourselves
+ fields, err := parseFieldsLocked(v)
+ if err != nil {
+ return nil, err
+ }
+ fieldCacheLock.Lock()
+ fieldCache[t] = fields
+ fieldCacheLock.Unlock()
+ return fields, nil
+}
diff --git a/vendor/github.com/lunixbochs/struc/parse_test.go b/vendor/github.com/lunixbochs/struc/parse_test.go
new file mode 100644
index 0000000..861fdd1
--- /dev/null
+++ b/vendor/github.com/lunixbochs/struc/parse_test.go
@@ -0,0 +1,62 @@
+package struc
+
+import (
+ "bytes"
+ "reflect"
+ "testing"
+)
+
+func parseTest(data interface{}) error {
+ _, err := parseFields(reflect.ValueOf(data))
+ return err
+}
+
+type empty struct{}
+
+func TestEmptyStruc(t *testing.T) {
+ if err := parseTest(&empty{}); err == nil {
+ t.Fatal("failed to error on empty struct")
+ }
+}
+
+type chanStruct struct {
+ Test chan int
+}
+
+func TestChanError(t *testing.T) {
+ if err := parseTest(&chanStruct{}); err == nil {
+ // TODO: should probably ignore channel fields
+ t.Fatal("failed to error on struct containing channel")
+ }
+}
+
+type badSizeof struct {
+ Size int `struc:"sizeof=Bad"`
+}
+
+func TestBadSizeof(t *testing.T) {
+ if err := parseTest(&badSizeof{}); err == nil {
+ t.Fatal("failed to error on missing Sizeof target")
+ }
+}
+
+type missingSize struct {
+ Test []byte
+}
+
+func TestMissingSize(t *testing.T) {
+ if err := parseTest(&missingSize{}); err == nil {
+ t.Fatal("failed to error on missing field size")
+ }
+}
+
+type badNested struct {
+ Empty empty
+}
+
+func TestNestedParseError(t *testing.T) {
+ var buf bytes.Buffer
+ if err := Pack(&buf, &badNested{}); err == nil {
+ t.Fatal("failed to error on bad nested struct")
+ }
+}
diff --git a/vendor/github.com/lunixbochs/struc/struc.go b/vendor/github.com/lunixbochs/struc/struc.go
new file mode 100644
index 0000000..3d85fe3
--- /dev/null
+++ b/vendor/github.com/lunixbochs/struc/struc.go
@@ -0,0 +1,117 @@
+package struc
+
+import (
+ "encoding/binary"
+ "fmt"
+ "io"
+ "reflect"
+)
+
+type Options struct {
+ ByteAlign int
+ PtrSize int
+ Order binary.ByteOrder
+}
+
+func (o *Options) Validate() error {
+ if o.PtrSize == 0 {
+ o.PtrSize = 32
+ } else {
+ switch o.PtrSize {
+ case 8, 16, 32, 64:
+ default:
+ return fmt.Errorf("Invalid Options.PtrSize: %d. Must be in (8, 16, 32, 64)", o.PtrSize)
+ }
+ }
+ return nil
+}
+
+var emptyOptions = &Options{}
+
+func prep(data interface{}) (reflect.Value, Packer, error) {
+ value := reflect.ValueOf(data)
+ for value.Kind() == reflect.Ptr {
+ next := value.Elem().Kind()
+ if next == reflect.Struct || next == reflect.Ptr {
+ value = value.Elem()
+ } else {
+ break
+ }
+ }
+ switch value.Kind() {
+ case reflect.Struct:
+ fields, err := parseFields(value)
+ return value, fields, err
+ default:
+ if !value.IsValid() {
+ return reflect.Value{}, nil, fmt.Errorf("Invalid reflect.Value for %+v", data)
+ }
+ if c, ok := data.(Custom); ok {
+ return value, customFallback{c}, nil
+ }
+ return value, binaryFallback(value), nil
+ }
+}
+
+func Pack(w io.Writer, data interface{}) error {
+ return PackWithOptions(w, data, nil)
+}
+
+func PackWithOptions(w io.Writer, data interface{}, options *Options) error {
+ if options == nil {
+ options = emptyOptions
+ }
+ if err := options.Validate(); err != nil {
+ return err
+ }
+ val, packer, err := prep(data)
+ if err != nil {
+ return err
+ }
+ if val.Type().Kind() == reflect.String {
+ val = val.Convert(reflect.TypeOf([]byte{}))
+ }
+ size := packer.Sizeof(val, options)
+ buf := make([]byte, size)
+ if _, err := packer.Pack(buf, val, options); err != nil {
+ return err
+ }
+ _, err = w.Write(buf)
+ return err
+}
+
+func Unpack(r io.Reader, data interface{}) error {
+ return UnpackWithOptions(r, data, nil)
+}
+
+func UnpackWithOptions(r io.Reader, data interface{}, options *Options) error {
+ if options == nil {
+ options = emptyOptions
+ }
+ if err := options.Validate(); err != nil {
+ return err
+ }
+ val, packer, err := prep(data)
+ if err != nil {
+ return err
+ }
+ return packer.Unpack(r, val, options)
+}
+
+func Sizeof(data interface{}) (int, error) {
+ return SizeofWithOptions(data, nil)
+}
+
+func SizeofWithOptions(data interface{}, options *Options) (int, error) {
+ if options == nil {
+ options = emptyOptions
+ }
+ if err := options.Validate(); err != nil {
+ return 0, err
+ }
+ val, packer, err := prep(data)
+ if err != nil {
+ return 0, err
+ }
+ return packer.Sizeof(val, options), nil
+}
diff --git a/vendor/github.com/lunixbochs/struc/struc_test.go b/vendor/github.com/lunixbochs/struc/struc_test.go
new file mode 100644
index 0000000..939c3e3
--- /dev/null
+++ b/vendor/github.com/lunixbochs/struc/struc_test.go
@@ -0,0 +1,197 @@
+package struc
+
+import (
+ "bytes"
+ "encoding/binary"
+ "reflect"
+ "testing"
+)
+
+type Nested struct {
+ Test2 int `struc:"int8"`
+}
+
+type Example struct {
+ Pad []byte `struc:"[5]pad"` // 00 00 00 00 00
+ I8f int `struc:"int8"` // 01
+ I16f int `struc:"int16"` // 00 02
+ I32f int `struc:"int32"` // 00 00 00 03
+ I64f int `struc:"int64"` // 00 00 00 00 00 00 00 04
+ U8f int `struc:"uint8,little"` // 05
+ U16f int `struc:"uint16,little"` // 06 00
+ U32f int `struc:"uint32,little"` // 07 00 00 00
+ U64f int `struc:"uint64,little"` // 08 00 00 00 00 00 00 00
+ Boolf int `struc:"bool"` // 01
+ Byte4f []byte `struc:"[4]byte"` // "abcd"
+
+ I8 int8 // 09
+ I16 int16 // 00 0a
+ I32 int32 // 00 00 00 0b
+ I64 int64 // 00 00 00 00 00 00 00 0c
+ U8 uint8 `struc:"little"` // 0d
+ U16 uint16 `struc:"little"` // 0e 00
+ U32 uint32 `struc:"little"` // 0f 00 00 00
+ U64 uint64 `struc:"little"` // 10 00 00 00 00 00 00 00
+ BoolT bool // 01
+ BoolF bool // 00
+ Byte4 [4]byte // "efgh"
+ Float1 float32 // 41 a0 00 00
+ Float2 float64 // 41 35 00 00 00 00 00 00
+
+ Size int `struc:"sizeof=Str,little"` // 0a 00 00 00
+ Str string `struc:"[]byte"` // "ijklmnopqr"
+ Strb string `struc:"[4]byte"` // "stuv"
+
+ Size2 int `struc:"uint8,sizeof=Str2"` // 04
+ Str2 string // "1234"
+
+ Size3 int `struc:"uint8,sizeof=Bstr"` // 04
+ Bstr []byte // "5678"
+
+ Nested Nested // 00 00 00 01
+ NestedP *Nested // 00 00 00 02
+ TestP64 *int `struc:"int64"` // 00 00 00 05
+
+ NestedSize int `struc:"sizeof=NestedA"` // 00 00 00 02
+ NestedA []Nested // [00 00 00 03, 00 00 00 04]
+
+ CustomTypeSize Int3 `struc:"sizeof=CustomTypeSizeArr"` // 00 00 00 04
+ CustomTypeSizeArr []byte // "ABCD"
+}
+
+var five = 5
+
+var reference = &Example{
+ nil,
+ 1, 2, 3, 4, 5, 6, 7, 8, 0, []byte{'a', 'b', 'c', 'd'},
+ 9, 10, 11, 12, 13, 14, 15, 16, true, false, [4]byte{'e', 'f', 'g', 'h'},
+ 20, 21,
+ 10, "ijklmnopqr", "stuv",
+ 4, "1234",
+ 4, []byte("5678"),
+ Nested{1}, &Nested{2}, &five,
+ 6, []Nested{{3}, {4}, {5}, {6}, {7}, {8}},
+ Int3(4), []byte("ABCD"),
+}
+
+var referenceBytes = []byte{
+ 0, 0, 0, 0, 0, // pad(5)
+ 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, // fake int8-int64(1-4)
+ 5, 6, 0, 7, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, // fake little-endian uint8-uint64(5-8)
+ 0, // fake bool(0)
+ 'a', 'b', 'c', 'd', // fake [4]byte
+
+ 9, 0, 10, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 12, // real int8-int64(9-12)
+ 13, 14, 0, 15, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, // real little-endian uint8-uint64(13-16)
+ 1, 0, // real bool(1), bool(0)
+ 'e', 'f', 'g', 'h', // real [4]byte
+ 65, 160, 0, 0, // real float32(20)
+ 64, 53, 0, 0, 0, 0, 0, 0, // real float64(21)
+
+ 10, 0, 0, 0, // little-endian int32(10) sizeof=Str
+ 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', // Str
+ 's', 't', 'u', 'v', // fake string([4]byte)
+ 04, '1', '2', '3', '4', // real string
+ 04, '5', '6', '7', '8', // fake []byte(string)
+
+ 1, 2, // Nested{1}, Nested{2}
+ 0, 0, 0, 0, 0, 0, 0, 5, // &five
+
+ 0, 0, 0, 6, // int32(6)
+ 3, 4, 5, 6, 7, 8, // [Nested{3}, ...Nested{8}]
+
+ 0, 0, 4, 'A', 'B', 'C', 'D', // Int3(4), []byte("ABCD")
+}
+
+func TestCodec(t *testing.T) {
+ var buf bytes.Buffer
+ if err := Pack(&buf, reference); err != nil {
+ t.Fatal(err)
+ }
+ out := &Example{}
+ if err := Unpack(&buf, out); err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(reference, out) {
+ t.Fatal("encode/decode failed")
+ }
+}
+
+func TestEncode(t *testing.T) {
+ var buf bytes.Buffer
+ if err := Pack(&buf, reference); err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(buf.Bytes(), referenceBytes) {
+ t.Fatal("encode failed")
+ }
+}
+
+func TestDecode(t *testing.T) {
+ buf := bytes.NewReader(referenceBytes)
+ out := &Example{}
+ if err := Unpack(buf, out); err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(reference, out) {
+ t.Fatal("decode failed")
+ }
+}
+
+func TestSizeof(t *testing.T) {
+ size, err := Sizeof(reference)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if size != len(referenceBytes) {
+ t.Fatal("sizeof failed")
+ }
+}
+
+type ExampleEndian struct {
+ T int `struc:"int16,big"`
+}
+
+func TestEndianSwap(t *testing.T) {
+ var buf bytes.Buffer
+ big := &ExampleEndian{1}
+ if err := PackWithOrder(&buf, big, binary.BigEndian); err != nil {
+ t.Fatal(err)
+ }
+ little := &ExampleEndian{}
+ if err := UnpackWithOrder(&buf, little, binary.LittleEndian); err != nil {
+ t.Fatal(err)
+ }
+ if little.T != 256 {
+ t.Fatal("big -> little conversion failed")
+ }
+}
+
+func TestNilValue(t *testing.T) {
+ var buf bytes.Buffer
+ if err := Pack(&buf, nil); err == nil {
+ t.Fatal("failed throw error for bad struct value")
+ }
+ if err := Unpack(&buf, nil); err == nil {
+ t.Fatal("failed throw error for bad struct value")
+ }
+ if _, err := Sizeof(nil); err == nil {
+ t.Fatal("failed to throw error for bad struct value")
+ }
+}
+
+type sliceUnderrun struct {
+ Str string `struc:"[10]byte"`
+ Arr []uint16 `struc:"[10]uint16"`
+}
+
+func TestSliceUnderrun(t *testing.T) {
+ var buf bytes.Buffer
+ v := sliceUnderrun{
+ Str: "foo",
+ Arr: []uint16{1, 2, 3},
+ }
+ if err := Pack(&buf, &v); err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/vendor/github.com/lunixbochs/struc/types.go b/vendor/github.com/lunixbochs/struc/types.go
new file mode 100644
index 0000000..6ca97f4
--- /dev/null
+++ b/vendor/github.com/lunixbochs/struc/types.go
@@ -0,0 +1,136 @@
+package struc
+
+import (
+ "fmt"
+ "reflect"
+)
+
+type Type int
+
+const (
+ Invalid Type = iota
+ Pad
+ Bool
+ Int
+ Int8
+ Uint8
+ Int16
+ Uint16
+ Int32
+ Uint32
+ Int64
+ Uint64
+ Float32
+ Float64
+ String
+ Struct
+ Ptr
+
+ SizeType
+ OffType
+ CustomType
+)
+
+func (t Type) Resolve(options *Options) Type {
+ switch t {
+ case OffType:
+ switch options.PtrSize {
+ case 8:
+ return Int8
+ case 16:
+ return Int16
+ case 32:
+ return Int32
+ case 64:
+ return Int64
+ default:
+ panic(fmt.Sprintf("unsupported ptr bits: %d", options.PtrSize))
+ }
+ case SizeType:
+ switch options.PtrSize {
+ case 8:
+ return Uint8
+ case 16:
+ return Uint16
+ case 32:
+ return Uint32
+ case 64:
+ return Uint64
+ default:
+ panic(fmt.Sprintf("unsupported ptr bits: %d", options.PtrSize))
+ }
+ }
+ return t
+}
+
+func (t Type) String() string {
+ return typeNames[t]
+}
+
+func (t Type) Size() int {
+ switch t {
+ case SizeType, OffType:
+ panic("Size_t/Off_t types must be converted to another type using options.PtrSize")
+ case Pad, String, Int8, Uint8, Bool:
+ return 1
+ case Int16, Uint16:
+ return 2
+ case Int32, Uint32, Float32:
+ return 4
+ case Int64, Uint64, Float64:
+ return 8
+ default:
+ panic("Cannot resolve size of type:" + t.String())
+ }
+}
+
+var typeLookup = map[string]Type{
+ "pad": Pad,
+ "bool": Bool,
+ "byte": Uint8,
+ "int8": Int8,
+ "uint8": Uint8,
+ "int16": Int16,
+ "uint16": Uint16,
+ "int32": Int32,
+ "uint32": Uint32,
+ "int64": Int64,
+ "uint64": Uint64,
+ "float32": Float32,
+ "float64": Float64,
+
+ "size_t": SizeType,
+ "off_t": OffType,
+}
+
+var typeNames = map[Type]string{
+ CustomType: "Custom",
+}
+
+func init() {
+ for name, enum := range typeLookup {
+ typeNames[enum] = name
+ }
+}
+
+type Size_t uint64
+type Off_t int64
+
+var reflectTypeMap = map[reflect.Kind]Type{
+ reflect.Bool: Bool,
+ reflect.Int8: Int8,
+ reflect.Int16: Int16,
+ reflect.Int: Int32,
+ reflect.Int32: Int32,
+ reflect.Int64: Int64,
+ reflect.Uint8: Uint8,
+ reflect.Uint16: Uint16,
+ reflect.Uint: Uint32,
+ reflect.Uint32: Uint32,
+ reflect.Uint64: Uint64,
+ reflect.Float32: Float32,
+ reflect.Float64: Float64,
+ reflect.String: String,
+ reflect.Struct: Struct,
+ reflect.Ptr: Ptr,
+}
diff --git a/vendor/github.com/lunixbochs/struc/types_test.go b/vendor/github.com/lunixbochs/struc/types_test.go
new file mode 100644
index 0000000..3b33e8f
--- /dev/null
+++ b/vendor/github.com/lunixbochs/struc/types_test.go
@@ -0,0 +1,53 @@
+package struc
+
+import (
+ "bytes"
+ "testing"
+)
+
+func TestBadType(t *testing.T) {
+ defer func() { recover() }()
+ Type(-1).Size()
+ t.Fatal("failed to panic for invalid Type.Size()")
+}
+
+func TestTypeString(t *testing.T) {
+ if Pad.String() != "pad" {
+ t.Fatal("type string representation failed")
+ }
+}
+
+type sizeOffTest struct {
+ Size Size_t
+ Off Off_t
+}
+
+func TestSizeOffTypes(t *testing.T) {
+ bits := []int{8, 16, 32, 64}
+ var buf bytes.Buffer
+ test := &sizeOffTest{1, 2}
+ for _, b := range bits {
+ if err := PackWithOptions(&buf, test, &Options{PtrSize: b}); err != nil {
+ t.Fatal(err)
+ }
+ }
+ reference := []byte{
+ 1, 2,
+ 0, 1, 0, 2,
+ 0, 0, 0, 1, 0, 0, 0, 2,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2,
+ }
+ if !bytes.Equal(reference, buf.Bytes()) {
+ t.Errorf("reference != bytes: %v", reference, buf.Bytes())
+ }
+ reader := bytes.NewReader(buf.Bytes())
+ for _, b := range bits {
+ out := &sizeOffTest{}
+ if err := UnpackWithOptions(reader, out, &Options{PtrSize: b}); err != nil {
+ t.Fatal(err)
+ }
+ if out.Size != 1 || out.Off != 2 {
+ t.Errorf("Size_t/Off_t mismatch: {%d, %d}\n%v", out.Size, out.Off, buf.Bytes())
+ }
+ }
+}