Skip to content

Commit faa6e73

Browse files
authored
Release/v0.12.0 (#216)
* DecodeInt{8, 16} negative limit checks + tests (#125) Updates #120 Also adds a test for Byteslice encoding and decoding roundtripping and part slicing then rejoining. * fix circleci2.0 * some metalinter issues * skip over default values when encoding time (#178) * do not encode empty structs, unless `amino:"write_empty"` is set (#179) * skip over empty structs by default * slightly more info on panics * Fix zero time decoding (#190) - fix decoding of skipped fields in time, or completely skipped time - necessary because sec=0 and ns=0 do not result in time.Time{} and vice versa * Prepare release 0.11.0 (#193) * Fix time decoding & encoding of arrays and structs - top-level entry functions called with BinFieldNum:1 to properly encode e.g. arrays of structs (see non-time related test) - add defaultValue method, different from #196 it deals with multiply nested pointers * Removed dependency on tmlibs/common * Always write empty if struct field is pointer * add tests for #206 - test for pointers to empty struct and nil pointer * fix proto3 compatibility for empty structs * Add EmptyStruct to fuzz tests * Revert "fix proto3 compatibility for empty structs" * Do not allow encoding of nil struct pointers in a slice/array * By default, 0-length list elements are decoded as nil
1 parent 2106ca6 commit faa6e73

24 files changed

+924
-237
lines changed
Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
version: 2
2-
32
jobs:
43
build:
5-
working_directory: /go/src/github.com/tendermint/go-amino
64
docker:
7-
- image: circleci/golang:1.10.3
8-
environment:
9-
GOBIN: /tmp/workspace/bin
5+
- image: circleci/golang:1.10.3
6+
environment:
7+
GOBIN: /tmp/workspace/bin
8+
9+
working_directory: /go/src/github.com/tendermint/go-amino
10+
1011
steps:
1112
- run: mkdir -p /tmp/workspace/bin
1213
- run: mkdir -p /tmp/workspace/profiles
@@ -17,9 +18,11 @@ jobs:
1718
- run:
1819
name: test
1920
command: |
21+
export PATH="$GOBIN:$PATH"
22+
go env
2023
go version
21-
cd $PROJECT_PATH && make get_tools && make get_vendor_deps && make test
22-
- save_cache:
23-
key: v1-dep-{{ .Branch }}
24-
paths:
25-
- /go/pkg
24+
make get_tools && make get_vendor_deps && make test
25+
- save_cache:
26+
key: v1-dep-{{ .Branch }}
27+
paths:
28+
- /go/pkg

CHANGELOG.md

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,35 @@
11
# Changelog
22

3+
## 0.12.0 (August 4, 2018)
4+
5+
BREAKING CHANGE:
6+
- Write empty (non-nil) struct pointers, unless (is list element and empty_elements isn't set) #206
7+
8+
## 0.11.1 (July 17, 2018)
9+
10+
IMPROVEMENTS:
11+
- Remove dependency on tmlibs/common
12+
13+
## 0.11.0 (June 19, 2018)
14+
15+
BREAKING CHANGE:
16+
17+
- Do not encode zero values in `EncodeTime`
18+
(to match proto3's behaviour) (#178, #190)
19+
- Do not encode empty structs, unless explicitly enforced
20+
via `amino:"write_empty"` (to match proto3's behaviour) (#179)
21+
22+
IMPROVEMENTS:
23+
- DecodeInt{8, 16} negative limit checks (#125)
24+
325
## 0.10.1 (June 15, 2018)
426

527
FEATURE:
628

729
- [aminoscan] aminoscan --color will print ASCII bytes in different colors
8-
30+
931
BUG FIXES:
10-
- do not err if prefix bytes are exactly 4 (for registered types)
32+
- do not err if prefix bytes are exactly 4 (for registered types)
1133

1234
## 0.10.0 (June 12, 2018)
1335

Gopkg.lock

Lines changed: 1 addition & 44 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Gopkg.toml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,6 @@
3737
name = "github.com/stretchr/testify"
3838
version = "1.2.1"
3939

40-
[[constraint]]
41-
name = "github.com/tendermint/tmlibs"
42-
version = "0.9.0-rc1"
43-
4440
[prune]
4541
go-tests = true
4642
unused-packages = true

amino.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,28 @@ import (
88
"fmt"
99
"io"
1010
"reflect"
11+
"time"
1112
)
1213

1314
//----------------------------------------
1415
// Global methods for global sealed codec.
1516
var gcdc *Codec
1617

18+
// we use this time to init. a zero value (opposed to reflect.Zero which gives time.Time{} / 01-01-01 00:00:00)
19+
var zeroTime time.Time
20+
21+
const (
22+
unixEpochStr = "1970-01-01 00:00:00 +0000 UTC"
23+
epochFmt = "2006-01-02 15:04:05 +0000 UTC"
24+
)
25+
1726
func init() {
1827
gcdc = NewCodec().Seal()
28+
var err error
29+
zeroTime, err = time.Parse(epochFmt, unixEpochStr)
30+
if err != nil {
31+
panic("couldn't parse Zero value for time")
32+
}
1933
}
2034

2135
func MarshalBinary(o interface{}) ([]byte, error) {
@@ -189,7 +203,7 @@ func (cdc *Codec) MarshalBinaryBare(o interface{}) ([]byte, error) {
189203
if err != nil {
190204
return nil, err
191205
}
192-
err = cdc.encodeReflectBinary(buf, info, rv, FieldOptions{}, true)
206+
err = cdc.encodeReflectBinary(buf, info, rv, FieldOptions{BinFieldNum: 1}, true)
193207
if err != nil {
194208
return nil, err
195209
}
@@ -327,7 +341,7 @@ func (cdc *Codec) UnmarshalBinaryBare(bz []byte, ptr interface{}) error {
327341
bz = bz[4:]
328342
}
329343
// Decode contents into rv.
330-
n, err := cdc.decodeReflectBinary(bz, info, rv, FieldOptions{}, true)
344+
n, err := cdc.decodeReflectBinary(bz, info, rv, FieldOptions{BinFieldNum: 1}, true)
331345
if err != nil {
332346
return fmt.Errorf("unmarshal to %v failed after %d bytes (%v): %X", info.Type, n, err, bz)
333347
}

binary-decode.go

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ func (cdc *Codec) decodeReflectBinaryByteArray(bz []byte, info *TypeInfo, rv ref
401401
}
402402

403403
// CONTRACT: rv.CanAddr() is true.
404+
// NOTE: Keep the code structure similar to decodeReflectBinarySlice.
404405
func (cdc *Codec) decodeReflectBinaryArray(bz []byte, info *TypeInfo, rv reflect.Value, fopts FieldOptions, bare bool) (n int, err error) {
405406
if !rv.CanAddr() {
406407
panic("rv not addressable")
@@ -461,6 +462,9 @@ func (cdc *Codec) decodeReflectBinaryArray(bz []byte, info *TypeInfo, rv reflect
461462
return
462463
}
463464
} else {
465+
// NOTE: ert is for the element value, while einfo.Type is dereferenced.
466+
isErtStructPointer := ert.Kind() == reflect.Ptr && einfo.Type.Kind() == reflect.Struct
467+
464468
// Read elements in unpacked form.
465469
for i := 0; i < length; i++ {
466470
// Read field key (number and type).
@@ -480,10 +484,16 @@ func (cdc *Codec) decodeReflectBinaryArray(bz []byte, info *TypeInfo, rv reflect
480484
}
481485
// Decode the next ByteLength bytes into erv.
482486
var erv = rv.Index(i)
483-
// Special case if next ByteLength bytes are 0x00, set nil.
484-
if len(bz) > 0 && bz[0] == 0x00 {
487+
// Special case if:
488+
// * next ByteLength bytes are 0x00, and
489+
// * - erv is not a struct pointer, or
490+
// - field option doesn't have EmptyElements set
491+
// (the condition below uses demorgan's law)
492+
if (len(bz) > 0 && bz[0] == 0x00) &&
493+
!(isErtStructPointer && fopts.EmptyElements) {
494+
485495
slide(&bz, &n, 1)
486-
erv.Set(reflect.Zero(erv.Type()))
496+
erv.Set(defaultValue(erv.Type()))
487497
continue
488498
}
489499
// Normal case, read next non-nil element from bz.
@@ -547,6 +557,7 @@ func (cdc *Codec) decodeReflectBinaryByteSlice(bz []byte, info *TypeInfo, rv ref
547557
}
548558

549559
// CONTRACT: rv.CanAddr() is true.
560+
// NOTE: Keep the code structure similar to decodeReflectBinaryArray.
550561
func (cdc *Codec) decodeReflectBinarySlice(bz []byte, info *TypeInfo, rv reflect.Value, fopts FieldOptions, bare bool) (n int, err error) {
551562
if !rv.CanAddr() {
552563
panic("rv not addressable")
@@ -611,6 +622,9 @@ func (cdc *Codec) decodeReflectBinarySlice(bz []byte, info *TypeInfo, rv reflect
611622
srv = reflect.Append(srv, erv)
612623
}
613624
} else {
625+
// NOTE: ert is for the element value, while einfo.Type is dereferenced.
626+
isErtStructPointer := ert.Kind() == reflect.Ptr && einfo.Type.Kind() == reflect.Struct
627+
614628
// Read elements in unpacked form.
615629
for {
616630
if len(bz) == 0 {
@@ -636,10 +650,16 @@ func (cdc *Codec) decodeReflectBinarySlice(bz []byte, info *TypeInfo, rv reflect
636650
}
637651
// Decode the next ByteLength bytes into erv.
638652
erv, _n := reflect.New(ert).Elem(), int(0)
639-
// Special case if next ByteLength bytes are 0x00, set nil.
640-
if len(bz) > 0 && bz[0] == 0x00 {
653+
// Special case if:
654+
// * next ByteLength bytes are 0x00, and
655+
// * - erv is not a struct pointer, or
656+
// - field option doesn't have EmptyElements set
657+
// (the condition below uses demorgan's law)
658+
if (len(bz) > 0 && bz[0] == 0x00) &&
659+
!(isErtStructPointer && fopts.EmptyElements) {
660+
641661
slide(&bz, &n, 1)
642-
erv.Set(reflect.Zero(erv.Type()))
662+
erv.Set(defaultValue(erv.Type()))
643663
srv = reflect.Append(srv, erv)
644664
continue
645665
}
@@ -703,7 +723,6 @@ func (cdc *Codec) decodeReflectBinaryStruct(bz []byte, info *TypeInfo, rv reflec
703723
var lastFieldNum uint32
704724
// Read each field.
705725
for _, field := range info.Fields {
706-
707726
// Get field rv and info.
708727
var frv = rv.Field(field.Index)
709728
var finfo *TypeInfo
@@ -714,7 +733,7 @@ func (cdc *Codec) decodeReflectBinaryStruct(bz []byte, info *TypeInfo, rv reflec
714733

715734
// We're done if we've consumed all the bytes.
716735
if len(bz) == 0 {
717-
frv.Set(reflect.Zero(frv.Type()))
736+
frv.Set(defaultValue(frv.Type()))
718737
continue
719738
}
720739

@@ -731,7 +750,7 @@ func (cdc *Codec) decodeReflectBinaryStruct(bz []byte, info *TypeInfo, rv reflec
731750
fnum, typ, _n, err = decodeFieldNumberAndTyp3(bz)
732751
if field.BinFieldNum < fnum {
733752
// Set zero field value.
734-
frv.Set(reflect.Zero(frv.Type()))
753+
frv.Set(defaultValue(frv.Type()))
735754
continue
736755
// Do not slide, we will read it again.
737756
}
@@ -760,7 +779,6 @@ func (cdc *Codec) decodeReflectBinaryStruct(bz []byte, info *TypeInfo, rv reflec
760779
typWanted, fnum, info.Type, typ))
761780
return
762781
}
763-
764782
// Decode field into frv.
765783
_n, err = cdc.decodeReflectBinary(bz, finfo, frv, field.FieldOptions, false)
766784
if slide(&bz, &n, _n) && err != nil {

binary-encode.go

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ import (
2424
// CONTRACT: rv is valid.
2525
func (cdc *Codec) encodeReflectBinary(w io.Writer, info *TypeInfo, rv reflect.Value, fopts FieldOptions, bare bool) (err error) {
2626
if rv.Kind() == reflect.Ptr {
27-
panic("should not happen")
27+
panic("not allowed to be called with a reflect.Ptr")
2828
}
2929
if !rv.IsValid() {
30-
panic("should not happen")
30+
panic("not allowed to be called with invalid / zero Value")
3131
}
3232
if printLog {
3333
spew.Printf("(E) encodeReflectBinary(info: %v, rv: %#v (%v), fopts: %v)\n",
@@ -297,6 +297,9 @@ func (cdc *Codec) encodeReflectBinaryList(w io.Writer, info *TypeInfo, rv reflec
297297
}
298298
}
299299
} else {
300+
// NOTE: ert is for the element value, while einfo.Type is dereferenced.
301+
isErtStructPointer := ert.Kind() == reflect.Ptr && einfo.Type.Kind() == reflect.Struct
302+
300303
// Write elems in unpacked form.
301304
for i := 0; i < rv.Len(); i++ {
302305
// Write elements as repeated fields of the parent struct.
@@ -307,6 +310,17 @@ func (cdc *Codec) encodeReflectBinaryList(w io.Writer, info *TypeInfo, rv reflec
307310
// Get dereferenced element value and info.
308311
var erv, isDefault = isDefaultValue(rv.Index(i))
309312
if isDefault {
313+
// Special case if:
314+
// - erv is a struct pointer and
315+
// - field option has EmptyElements set
316+
if isErtStructPointer && fopts.EmptyElements {
317+
// NOTE: Not sure what to do here, but for future-proofing,
318+
// we explicitly fail on nil pointers, just like
319+
// Proto3's Golang client does.
320+
// This also makes it easier to upgrade to Amino2
321+
// which would enable the encoding of nil structs.
322+
return errors.New("nil struct pointers not supported when empty_elements field tag is set")
323+
}
310324
// Nothing to encode, so the length is 0.
311325
err = EncodeByte(buf, byte(0x00))
312326
if err != nil {
@@ -384,28 +398,42 @@ func (cdc *Codec) encodeReflectBinaryStruct(w io.Writer, info *TypeInfo, rv refl
384398
return
385399
}
386400
// Get dereferenced field value and info.
387-
var frv, isDefault = isDefaultValue(rv.Field(field.Index))
388-
if isDefault {
389-
// Do not encode default value fields.
401+
var frv = rv.Field(field.Index)
402+
var frvIsPtr = frv.Kind() == reflect.Ptr
403+
var dfrv, isDefault = isDefaultValue(frv)
404+
if isDefault && !fopts.WriteEmpty {
405+
// Do not encode default value fields
406+
// (except when `amino:"write_empty"` is set).
390407
continue
391408
}
392409
if field.UnpackedList {
393410
// Write repeated field entries for each list item.
394-
err = cdc.encodeReflectBinaryList(buf, finfo, frv, field.FieldOptions, true)
411+
err = cdc.encodeReflectBinaryList(buf, finfo, dfrv, field.FieldOptions, true)
395412
if err != nil {
396413
return
397414
}
398415
} else {
416+
lBeforeKey := buf.Len()
399417
// Write field key (number and type).
400418
err = encodeFieldNumberAndTyp3(buf, field.BinFieldNum, typeToTyp3(finfo.Type, field.FieldOptions))
401419
if err != nil {
402420
return
403421
}
404-
// Write field from rv.
405-
err = cdc.encodeReflectBinary(buf, finfo, frv, field.FieldOptions, false)
422+
lBeforeValue := buf.Len()
423+
424+
// Write field value from rv.
425+
err = cdc.encodeReflectBinary(buf, finfo, dfrv, field.FieldOptions, false)
406426
if err != nil {
407427
return
408428
}
429+
lAfterValue := buf.Len()
430+
431+
if !frvIsPtr && !fopts.WriteEmpty && lBeforeValue == lAfterValue-1 && buf.Bytes()[buf.Len()-1] == 0x00 {
432+
// rollback typ3/fieldnum and last byte if
433+
// not a pointer and empty:
434+
buf.Truncate(lBeforeKey)
435+
}
436+
409437
}
410438
}
411439
}

0 commit comments

Comments
 (0)