├── LICENSE ├── README.md ├── binary.go ├── binary_test.go └── renovate.json5 /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2015 Alec Thomas 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Compact binary encoding for Go 2 | 3 | The Go standard library package 4 | [encoding/binary](http://golang.org/pkg/encoding/binary/) provides 5 | encoding/decoding of *fixed-size* Go values or slices of same. This package 6 | extends support to arbitrary, variable-sized values by prefixing these values 7 | with their varint-encoded size, recursively. It expects the encoded type and 8 | decoded type to match exactly and makes no attempt to reconcile or check for 9 | any differences. 10 | -------------------------------------------------------------------------------- /binary.go: -------------------------------------------------------------------------------- 1 | package binary 2 | 3 | import ( 4 | "bytes" 5 | "encoding" 6 | "encoding/binary" 7 | "errors" 8 | "fmt" 9 | "io" 10 | "reflect" 11 | ) 12 | 13 | var ( 14 | LittleEndian = binary.LittleEndian 15 | BigEndian = binary.BigEndian 16 | DefaultEndian = LittleEndian 17 | ) 18 | 19 | func Marshal(v interface{}) ([]byte, error) { 20 | b := &bytes.Buffer{} 21 | if err := NewEncoder(b).Encode(v); err != nil { 22 | return nil, err 23 | } 24 | return b.Bytes(), nil 25 | } 26 | 27 | func Unmarshal(b []byte, v interface{}) error { 28 | return NewDecoder(bytes.NewReader(b)).Decode(v) 29 | } 30 | 31 | type Encoder struct { 32 | Order binary.ByteOrder 33 | w io.Writer 34 | buf []byte 35 | strict bool 36 | } 37 | 38 | func NewEncoder(w io.Writer) *Encoder { 39 | return &Encoder{ 40 | Order: DefaultEndian, 41 | w: w, 42 | buf: make([]byte, 8), 43 | } 44 | } 45 | 46 | // NewStrictEncoder creates an encoder similar to NewEncoder, however 47 | // if this encoder attempts to encode a struct and the struct has no encodable 48 | // fields an error is returned whereas the encoder returned from NewEncoder 49 | // will simply not write anything to `w`. 50 | func NewStrictEncoder(w io.Writer) *Encoder { 51 | e := NewEncoder(w) 52 | e.strict = true 53 | return e 54 | } 55 | 56 | func (e *Encoder) writeVarint(v int) error { 57 | l := binary.PutUvarint(e.buf, uint64(v)) 58 | _, err := e.w.Write(e.buf[:l]) 59 | return err 60 | } 61 | 62 | func (b *Encoder) Encode(v interface{}) (err error) { 63 | switch cv := v.(type) { 64 | case encoding.BinaryMarshaler: 65 | buf, err := cv.MarshalBinary() 66 | if err != nil { 67 | return err 68 | } 69 | if err = b.writeVarint(len(buf)); err != nil { 70 | return err 71 | } 72 | _, err = b.w.Write(buf) 73 | 74 | case []byte: // fast-path byte arrays 75 | if err = b.writeVarint(len(cv)); err != nil { 76 | return 77 | } 78 | _, err = b.w.Write(cv) 79 | 80 | default: 81 | rv := reflect.Indirect(reflect.ValueOf(v)) 82 | t := rv.Type() 83 | switch t.Kind() { 84 | case reflect.Array: 85 | l := t.Len() 86 | for i := 0; i < l; i++ { 87 | if err = b.Encode(rv.Index(i).Addr().Interface()); err != nil { 88 | return 89 | } 90 | } 91 | 92 | case reflect.Slice: 93 | l := rv.Len() 94 | if err = b.writeVarint(l); err != nil { 95 | return 96 | } 97 | for i := 0; i < l; i++ { 98 | if err = b.Encode(rv.Index(i).Addr().Interface()); err != nil { 99 | return 100 | } 101 | } 102 | 103 | case reflect.Struct: 104 | l := rv.NumField() 105 | n := 0 106 | for i := 0; i < l; i++ { 107 | if v := rv.Field(i); t.Field(i).Name != "_" && t.Field(i).IsExported() { 108 | if err = b.Encode(v.Interface()); err != nil { 109 | return 110 | } 111 | n++ 112 | } 113 | } 114 | if b.strict && n == 0 { 115 | return fmt.Errorf("binary: struct had no encodable fields") 116 | } 117 | 118 | case reflect.Map: 119 | l := rv.Len() 120 | if err = b.writeVarint(l); err != nil { 121 | return 122 | } 123 | for _, key := range rv.MapKeys() { 124 | value := rv.MapIndex(key) 125 | if err = b.Encode(key.Interface()); err != nil { 126 | return err 127 | } 128 | if err = b.Encode(value.Interface()); err != nil { 129 | return err 130 | } 131 | } 132 | 133 | case reflect.String: 134 | if err = b.writeVarint(rv.Len()); err != nil { 135 | return 136 | } 137 | _, err = b.w.Write([]byte(rv.String())) 138 | 139 | case reflect.Bool: 140 | var out byte 141 | if rv.Bool() { 142 | out = 1 143 | } 144 | err = binary.Write(b.w, b.Order, out) 145 | 146 | case reflect.Int: 147 | err = binary.Write(b.w, b.Order, int64(rv.Int())) 148 | 149 | case reflect.Uint: 150 | err = binary.Write(b.w, b.Order, int64(rv.Uint())) 151 | 152 | case reflect.Int8, reflect.Uint8, reflect.Int16, reflect.Uint16, 153 | reflect.Int32, reflect.Uint32, reflect.Int64, reflect.Uint64, 154 | reflect.Float32, reflect.Float64, 155 | reflect.Complex64, reflect.Complex128: 156 | err = binary.Write(b.w, b.Order, v) 157 | 158 | default: 159 | return errors.New("binary: unsupported type " + t.String()) 160 | } 161 | } 162 | return 163 | } 164 | 165 | type byteReader struct { 166 | io.Reader 167 | } 168 | 169 | func (b *byteReader) ReadByte() (byte, error) { 170 | var buf [1]byte 171 | if _, err := io.ReadFull(b, buf[:]); err != nil { 172 | return 0, err 173 | } 174 | return buf[0], nil 175 | } 176 | 177 | type Decoder struct { 178 | Order binary.ByteOrder 179 | r *byteReader 180 | } 181 | 182 | func NewDecoder(r io.Reader) *Decoder { 183 | return &Decoder{ 184 | Order: DefaultEndian, 185 | r: &byteReader{r}, 186 | } 187 | } 188 | 189 | func (d *Decoder) Decode(v interface{}) (err error) { 190 | // Check if the type implements the encoding.BinaryUnmarshaler interface, and use it if so. 191 | if i, ok := v.(encoding.BinaryUnmarshaler); ok { 192 | var l uint64 193 | if l, err = binary.ReadUvarint(d.r); err != nil { 194 | return 195 | } 196 | buf := make([]byte, l) 197 | _, err = d.r.Read(buf) 198 | return i.UnmarshalBinary(buf) 199 | } 200 | 201 | // Otherwise, use reflection. 202 | rv := reflect.Indirect(reflect.ValueOf(v)) 203 | if !rv.CanAddr() { 204 | return errors.New("binary: can only Decode to pointer type") 205 | } 206 | t := rv.Type() 207 | 208 | switch t.Kind() { 209 | case reflect.Array: 210 | len := t.Len() 211 | for i := 0; i < int(len); i++ { 212 | if err = d.Decode(rv.Index(i).Addr().Interface()); err != nil { 213 | return 214 | } 215 | } 216 | 217 | case reflect.Slice: 218 | var l uint64 219 | if l, err = binary.ReadUvarint(d.r); err != nil { 220 | return 221 | } 222 | if t.Kind() == reflect.Slice { 223 | rv.Set(reflect.MakeSlice(t, int(l), int(l))) 224 | } else if int(l) != t.Len() { 225 | return fmt.Errorf("binary: encoded size %d != real size %d", l, t.Len()) 226 | } 227 | for i := 0; i < int(l); i++ { 228 | if err = d.Decode(rv.Index(i).Addr().Interface()); err != nil { 229 | return 230 | } 231 | } 232 | 233 | case reflect.Struct: 234 | l := rv.NumField() 235 | for i := 0; i < l; i++ { 236 | if v := rv.Field(i); v.CanSet() && t.Field(i).Name != "_" { 237 | if err = d.Decode(v.Addr().Interface()); err != nil { 238 | return 239 | } 240 | } 241 | } 242 | 243 | case reflect.Map: 244 | var l uint64 245 | if l, err = binary.ReadUvarint(d.r); err != nil { 246 | return 247 | } 248 | kt := t.Key() 249 | vt := t.Elem() 250 | rv.Set(reflect.MakeMap(t)) 251 | for i := 0; i < int(l); i++ { 252 | kv := reflect.Indirect(reflect.New(kt)) 253 | if err = d.Decode(kv.Addr().Interface()); err != nil { 254 | return 255 | } 256 | vv := reflect.Indirect(reflect.New(vt)) 257 | if err = d.Decode(vv.Addr().Interface()); err != nil { 258 | return 259 | } 260 | rv.SetMapIndex(kv, vv) 261 | } 262 | 263 | case reflect.String: 264 | var l uint64 265 | if l, err = binary.ReadUvarint(d.r); err != nil { 266 | return 267 | } 268 | buf := make([]byte, l) 269 | _, err = d.r.Read(buf) 270 | rv.SetString(string(buf)) 271 | 272 | case reflect.Bool: 273 | var out byte 274 | err = binary.Read(d.r, d.Order, &out) 275 | rv.SetBool(out != 0) 276 | 277 | case reflect.Int: 278 | var out int64 279 | err = binary.Read(d.r, d.Order, &out) 280 | rv.SetInt(out) 281 | 282 | case reflect.Uint: 283 | var out uint64 284 | err = binary.Read(d.r, d.Order, &out) 285 | rv.SetUint(out) 286 | 287 | case reflect.Int8, reflect.Uint8, reflect.Int16, reflect.Uint16, 288 | reflect.Int32, reflect.Uint32, reflect.Int64, reflect.Uint64, 289 | reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: 290 | err = binary.Read(d.r, d.Order, v) 291 | 292 | default: 293 | return errors.New("binary: unsupported type " + t.String()) 294 | } 295 | return 296 | } 297 | -------------------------------------------------------------------------------- /binary_test.go: -------------------------------------------------------------------------------- 1 | package binary 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "reflect" 9 | "testing" 10 | "time" 11 | 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | type s0 struct { 16 | A string 17 | B string 18 | C int16 19 | } 20 | 21 | var ( 22 | s0v = &s0{"A", "B", 1} 23 | s0b = []byte{0x1, 0x41, 0x1, 0x42, 0x1, 0x0} 24 | ) 25 | 26 | func TestBinaryEncodeStruct(t *testing.T) { 27 | b, err := Marshal(s0v) 28 | assert.NoError(t, err) 29 | assert.Equal(t, s0b, b) 30 | } 31 | 32 | func TestBinaryDecodeStruct(t *testing.T) { 33 | s := &s0{} 34 | err := Unmarshal(s0b, s) 35 | assert.NoError(t, err) 36 | assert.Equal(t, s0v, s) 37 | } 38 | 39 | func TestBinaryDecodeToValueErrors(t *testing.T) { 40 | b := []byte{1, 0, 0, 0} 41 | var v uint32 42 | err := Unmarshal(b, v) 43 | assert.Error(t, err) 44 | err = Unmarshal(b, &v) 45 | assert.NoError(t, err) 46 | assert.Equal(t, uint32(1), v) 47 | } 48 | 49 | type s1 struct { 50 | Name string 51 | BirthDay time.Time 52 | Phone string 53 | Siblings int 54 | Spouse bool 55 | Money float64 56 | Tags map[string]string 57 | Aliases []string 58 | } 59 | 60 | var ( 61 | s1v = &s1{ 62 | Name: "Bob Smith", 63 | BirthDay: time.Date(2013, 1, 2, 3, 4, 5, 6, time.UTC), 64 | Phone: "5551234567", 65 | Siblings: 2, 66 | Spouse: false, 67 | Money: 100.0, 68 | Tags: map[string]string{"key": "value"}, 69 | Aliases: []string{"Bobby", "Robert"}, 70 | } 71 | 72 | svb = []byte{0x9, 0x42, 0x6f, 0x62, 0x20, 0x53, 0x6d, 0x69, 0x74, 0x68, 0xf, 73 | 0x1, 0x0, 0x0, 0x0, 0xe, 0xc8, 0x75, 0x9a, 0xa5, 0x0, 0x0, 0x0, 0x6, 0xff, 74 | 0xff, 0xa, 0x35, 0x35, 0x35, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x2, 75 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x59, 76 | 0x40, 0x1, 0x3, 0x6b, 0x65, 0x79, 0x5, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2, 0x5, 77 | 0x42, 0x6f, 0x62, 0x62, 0x79, 0x6, 0x52, 0x6f, 0x62, 0x65, 0x72, 0x74} 78 | ) 79 | 80 | func TestBinaryEncodeComplex(t *testing.T) { 81 | b, err := Marshal(s1v) 82 | assert.NoError(t, err) 83 | assert.Equal(t, svb, b) 84 | s := &s1{} 85 | err = Unmarshal(svb, s) 86 | assert.NoError(t, err) 87 | assert.Equal(t, s1v, s) 88 | } 89 | 90 | type s2 struct { 91 | b []byte 92 | } 93 | 94 | func (s *s2) UnmarshalBinary(data []byte) error { 95 | if len(data) != 1 { 96 | return errors.New("expected data to be length 1") 97 | } 98 | s.b = data 99 | return nil 100 | } 101 | 102 | func (s *s2) MarshalBinary() (data []byte, err error) { 103 | return s.b, nil 104 | } 105 | 106 | func TestBinaryMarshalUnMarshaler(t *testing.T) { 107 | s2v := &s2{[]byte{0x13}} 108 | b, err := Marshal(s2v) 109 | assert.NoError(t, err) 110 | assert.Equal(t, []byte{0x1, 0x13}, b) 111 | } 112 | 113 | func TestMarshalUnMarshalTypeAliases(t *testing.T) { 114 | type Foo int64 115 | f := Foo(32) 116 | b, err := Marshal(f) 117 | assert.NoError(t, err) 118 | assert.Equal(t, []byte{0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, b) 119 | } 120 | 121 | func TestStructWithStruct(t *testing.T) { 122 | type T1 struct { 123 | ID uint64 124 | Name string 125 | Slice []int 126 | } 127 | type T2 uint64 128 | type Struct struct { 129 | V1 T1 130 | V2 T2 131 | V3 T1 132 | } 133 | 134 | s := Struct{V1: T1{1, "1", []int{1}}, V2: 2, V3: T1{3, "3", []int{3}}} 135 | buf := new(bytes.Buffer) 136 | enc := NewEncoder(buf) 137 | err := enc.Encode(&s) 138 | if err != nil { 139 | t.Fatalf("error: %v\n", err) 140 | } 141 | 142 | v := Struct{} 143 | dec := NewDecoder(buf) 144 | err = dec.Decode(&v) 145 | if err != nil { 146 | t.Fatalf("error: %v\n", err) 147 | } 148 | 149 | if !reflect.DeepEqual(s, v) { 150 | t.Fatalf("got= %#v\nwant=%#v\n", v, s) 151 | } 152 | 153 | } 154 | 155 | func TestStructWithEmbeddedStruct(t *testing.T) { 156 | type T1 struct { 157 | ID uint64 158 | Name string 159 | Slice []int 160 | } 161 | type T2 uint64 162 | type Struct struct { 163 | T1 164 | V2 T2 165 | V3 T1 166 | } 167 | 168 | s := Struct{T1: T1{1, "1", []int{1}}, V2: 2, V3: T1{3, "3", []int{3}}} 169 | buf := new(bytes.Buffer) 170 | enc := NewEncoder(buf) 171 | err := enc.Encode(&s) 172 | if err != nil { 173 | t.Fatalf("error: %v\n", err) 174 | } 175 | 176 | v := Struct{} 177 | dec := NewDecoder(buf) 178 | err = dec.Decode(&v) 179 | if err != nil { 180 | t.Fatalf("error: %v\n", err) 181 | } 182 | 183 | if !reflect.DeepEqual(s, v) { 184 | t.Fatalf("got= %#v\nwant=%#v\n", v, s) 185 | } 186 | 187 | } 188 | 189 | func TestArrayOfStructWithStruct(t *testing.T) { 190 | type T1 struct { 191 | ID uint64 192 | Name string 193 | Slice []int 194 | } 195 | type T2 uint64 196 | type Struct struct { 197 | V1 T1 198 | V2 T2 199 | V3 T1 200 | } 201 | 202 | s := [1]Struct{ 203 | {V1: T1{1, "1", []int{1}}, V2: 2, V3: T1{3, "3", []int{3}}}, 204 | } 205 | buf := new(bytes.Buffer) 206 | enc := NewEncoder(buf) 207 | err := enc.Encode(&s) 208 | if err != nil { 209 | t.Fatalf("error: %v\n", err) 210 | } 211 | 212 | v := [1]Struct{} 213 | dec := NewDecoder(buf) 214 | err = dec.Decode(&v) 215 | if err != nil { 216 | t.Fatalf("error: %v\n", err) 217 | } 218 | 219 | if !reflect.DeepEqual(s, v) { 220 | t.Fatalf("got= %#v\nwant=%#v\n", v, s) 221 | } 222 | 223 | } 224 | 225 | func TestSliceOfStructWithStruct(t *testing.T) { 226 | type T1 struct { 227 | ID uint64 228 | Name string 229 | Slice []int 230 | } 231 | type T2 uint64 232 | type Struct struct { 233 | V1 T1 234 | V2 T2 235 | V3 T1 236 | } 237 | 238 | s := []Struct{ 239 | {V1: T1{1, "1", []int{1}}, V2: 2, V3: T1{3, "3", []int{3}}}, 240 | } 241 | buf := new(bytes.Buffer) 242 | enc := NewEncoder(buf) 243 | err := enc.Encode(&s) 244 | if err != nil { 245 | t.Fatalf("error: %v\n", err) 246 | } 247 | 248 | v := []Struct{} 249 | dec := NewDecoder(buf) 250 | err = dec.Decode(&v) 251 | if err != nil { 252 | t.Fatalf("error: %v\n", err) 253 | } 254 | 255 | if !reflect.DeepEqual(s, v) { 256 | t.Fatalf("got= %#v\nwant=%#v\n", v, s) 257 | } 258 | 259 | } 260 | 261 | func TestMarshalNonPointer(t *testing.T) { 262 | type S struct { 263 | A int 264 | } 265 | s := S{A: 1} 266 | data, err := Marshal(s) 267 | if err != nil { 268 | t.Fatal(err) 269 | } 270 | var res S 271 | if err := Unmarshal(data, &res); err != nil { 272 | t.Fatal(err) 273 | } 274 | if !reflect.DeepEqual(res, s) { 275 | t.Fatalf("expect %v got %v", s, res) 276 | } 277 | } 278 | 279 | func TestStructWithPrivateFields(t *testing.T) { 280 | 281 | type S struct { 282 | Public int 283 | AnotherPublic int 284 | private int 285 | } 286 | 287 | s := S{ 288 | Public: 123, 289 | AnotherPublic: 456, 290 | private: -555, 291 | } 292 | 293 | assert.NotPanics(t, func() { 294 | 295 | data, err := Marshal(s) 296 | assert.NoError(t, err) 297 | 298 | var res S 299 | if err := Unmarshal(data, &res); err != nil { 300 | t.Fatal(err) 301 | } 302 | assert.Equal(t, s.Public, res.Public) 303 | assert.Equal(t, s.AnotherPublic, res.AnotherPublic) 304 | 305 | }) 306 | 307 | } 308 | 309 | func BenchmarkEncodeStructI1(b *testing.B) { 310 | type Struct struct { 311 | S struct { 312 | I int64 313 | } 314 | } 315 | var s Struct 316 | s.S.I = 1024 317 | buf := new(bytes.Buffer) 318 | 319 | b.ResetTimer() 320 | for i := 0; i < b.N; i++ { 321 | enc := NewEncoder(buf) 322 | _ = enc.Encode(&s) 323 | } 324 | 325 | } 326 | 327 | func BenchmarkEncodeStructI2(b *testing.B) { 328 | type Struct struct { 329 | S struct { 330 | I int64 331 | } 332 | } 333 | var s Struct 334 | s.S.I = 1024 335 | buf := new(bytes.Buffer) 336 | enc := NewEncoder(buf) 337 | 338 | b.ResetTimer() 339 | for i := 0; i < b.N; i++ { 340 | _ = enc.Encode(&s) 341 | } 342 | 343 | } 344 | 345 | func BenchmarkEncodeStructI3(b *testing.B) { 346 | type Struct struct { 347 | S struct { 348 | I int64 349 | } 350 | } 351 | var s Struct 352 | s.S.I = 1024 353 | buf := new(bytes.Buffer) 354 | enc := NewEncoder(buf) 355 | 356 | b.ResetTimer() 357 | for i := 0; i < b.N; i++ { 358 | err := enc.Encode(&s) 359 | if err != nil { 360 | b.Fatalf("error: %v\n", err) 361 | } 362 | } 363 | 364 | } 365 | 366 | type bufferT struct { 367 | buf []byte 368 | } 369 | 370 | func (buf *bufferT) Read(data []byte) (int, error) { 371 | n := copy(data, buf.buf) 372 | if n != len(data) { 373 | panic(fmt.Errorf("read too few bytes. got=%d want=%d", n, len(data))) 374 | } 375 | return n, nil 376 | } 377 | 378 | func getTestBuffer(b *testing.B) io.Reader { 379 | return &bufferT{ 380 | buf: []byte{0, 4, 0, 0, 0, 0, 0, 0}, 381 | } 382 | } 383 | 384 | func BenchmarkDecodeStructI1(b *testing.B) { 385 | 386 | type Struct struct { 387 | I int64 388 | } 389 | 390 | var s Struct 391 | 392 | buf := getTestBuffer(b) 393 | b.ResetTimer() 394 | for i := 0; i < b.N; i++ { 395 | dec := NewDecoder(buf) 396 | _ = dec.Decode(&s) 397 | } 398 | 399 | } 400 | 401 | func BenchmarkDecodeStructI2(b *testing.B) { 402 | 403 | type Struct struct { 404 | S struct { 405 | I int64 406 | } 407 | } 408 | var s Struct 409 | 410 | buf := getTestBuffer(b) 411 | dec := NewDecoder(buf) 412 | 413 | b.ResetTimer() 414 | for i := 0; i < b.N; i++ { 415 | _ = dec.Decode(&s) 416 | } 417 | 418 | } 419 | 420 | func BenchmarkDecodeStructI3(b *testing.B) { 421 | 422 | type Struct struct { 423 | S struct { 424 | I int64 425 | } 426 | } 427 | var s Struct 428 | 429 | buf := getTestBuffer(b) 430 | dec := NewDecoder(buf) 431 | 432 | b.ResetTimer() 433 | for i := 0; i < b.N; i++ { 434 | err := dec.Decode(&s) 435 | if err != nil { 436 | b.Fatalf("error: %v\n", err) 437 | } 438 | } 439 | 440 | } 441 | -------------------------------------------------------------------------------- /renovate.json5: -------------------------------------------------------------------------------- 1 | { 2 | $schema: "https://docs.renovatebot.com/renovate-schema.json", 3 | extends: [ 4 | "config:recommended", 5 | ":semanticCommits", 6 | ":semanticCommitTypeAll(chore)", 7 | ":semanticCommitScope(deps)", 8 | "group:allNonMajor", 9 | "schedule:earlyMondays", // Run once a week. 10 | ], 11 | } 12 | --------------------------------------------------------------------------------