Skip to content

Commit

Permalink
Fix encoding struct with null ptr to embedded struct
Browse files Browse the repository at this point in the history
When encoding struct with null pointer to embedded struct, encoder
skips the embedded struct because it is unreachable and doesn't
return any error.

Fixed: #24
  • Loading branch information
fxamacker committed Nov 12, 2019
1 parent e9f5a7f commit 593ee34
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 23 deletions.
23 changes: 16 additions & 7 deletions encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,19 +360,28 @@ func encodeStruct(e *encodeState, v reflect.Value, opts EncOptions) (int, error)
if flds[i].ef == nil {
return 0, &UnsupportedTypeError{v.Type()}
}
fv, err := fieldByIndex(v, flds[i].idx)
if err != nil {
putEncodeState(kve)
return 0, err
fv := v
ignore := false
for k, n := range flds[i].idx {
if k > 0 {
if fv.Kind() == reflect.Ptr && fv.Type().Elem().Kind() == reflect.Struct {
if fv.IsNil() {
// Null pointer to embedded struct
ignore = true
break
}
fv = fv.Elem()
}
}
fv = fv.Field(n)
}
if !fv.IsValid() || (flds[i].omitempty && isEmptyValue(fv)) {
if ignore || (flds[i].omitempty && isEmptyValue(fv)) {
continue
}

kve.Write(flds[i].cborName)

_, err = flds[i].ef(kve, fv, opts)
if err != nil {
if _, err := flds[i].ef(kve, fv, opts); err != nil {
putEncodeState(kve)
return 0, err
}
Expand Down
72 changes: 56 additions & 16 deletions encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,27 @@ func encodeCborHeader(t byte, n uint64) []byte {
}
}

func testMarshal(t *testing.T, testCases []marshalTest) {
for _, tc := range testCases {
for _, value := range tc.values {
if _, err := cbor.Marshal(value, cbor.EncOptions{}); err != nil {
t.Errorf("Marshal(%v, cbor.EncOptions{}) returns error %v", value, err)
}
if b, err := cbor.Marshal(value, cbor.EncOptions{Canonical: true}); err != nil {
t.Errorf("Marshal(%v, cbor.EncOptions{Canonical: true}) returns error %v", value, err)
} else if !bytes.Equal(b, tc.cborData) {
t.Errorf("Marshal(%v, cbor.EncOptions{Canonical: true}) = 0x%0x, want 0x%0x", value, b, tc.cborData)
}
}
r := cbor.RawMessage(tc.cborData)
if b, err := cbor.Marshal(r, cbor.EncOptions{}); err != nil {
t.Errorf("Marshal(%v, cbor.EncOptions{}) returns error %v", r, err)
} else if !bytes.Equal(b, r) {
t.Errorf("Marshal(%v, cbor.EncOptions{}) returns %v, want %v", r, b, r)
}
}
}

func TestMarshalStruct(t *testing.T) {
v1 := outer{
IntField: 123,
Expand Down Expand Up @@ -534,24 +555,43 @@ func TestMarshalStructCanonical(t *testing.T) {
}
}

func testMarshal(t *testing.T, testCases []marshalTest) {
for _, tc := range testCases {
for _, value := range tc.values {
if _, err := cbor.Marshal(value, cbor.EncOptions{}); err != nil {
t.Errorf("Marshal(%v, cbor.EncOptions{}) returns error %v", value, err)
}
if b, err := cbor.Marshal(value, cbor.EncOptions{Canonical: true}); err != nil {
t.Errorf("Marshal(%v, cbor.EncOptions{Canonical: true}) returns error %v", value, err)
} else if !bytes.Equal(b, tc.cborData) {
t.Errorf("Marshal(%v, cbor.EncOptions{Canonical: true}) = 0x%0x, want 0x%0x", value, b, tc.cborData)
}
func TestMarshalNullPointerToEmbeddedStruct(t *testing.T) {
type (
T1 struct {
N int
}
r := cbor.RawMessage(tc.cborData)
if b, err := cbor.Marshal(r, cbor.EncOptions{}); err != nil {
t.Errorf("Marshal(%v, cbor.EncOptions{}) returns error %v", r, err)
} else if !bytes.Equal(b, r) {
t.Errorf("Marshal(%v, cbor.EncOptions{}) returns %v, want %v", r, b, r)
T2 struct {
*T1
}
)
v := T2{}
wantCborData := []byte{0xa0} // {}
cborData, err := cbor.Marshal(v, cbor.EncOptions{})
if err != nil {
t.Fatalf("Marshal(%v) returns error %v", v, err)
}
if !bytes.Equal(wantCborData, cborData) {
t.Errorf("Marshal(%v) = 0x%0x, want 0x%0x", v, cborData, wantCborData)
}
}

func TestMarshalNullPointerToStruct(t *testing.T) {
type (
T1 struct {
N int
}
T2 struct {
T *T1
}
)
v := T2{}
wantCborData := []byte{0xa1, 0x61, 0x54, 0xf6} // {T: nil}
cborData, err := cbor.Marshal(v, cbor.EncOptions{})
if err != nil {
t.Fatalf("Marshal(%v) returns error %v", v, err)
}
if !bytes.Equal(wantCborData, cborData) {
t.Errorf("Marshal(%v) = 0x%0x, want 0x%0x", v, cborData, wantCborData)
}
}

Expand Down

0 comments on commit 593ee34

Please sign in to comment.