Skip to content

Commit

Permalink
Improve decoding speed affected by commit 1a29187
Browse files Browse the repository at this point in the history
Adding Unmarshaler decreased decoding speed in commit 1a29187. This
affects all data types.

By caching the result of reflect pkg's Type.Implements() , decoding
speed is improved: slice 28%, struct 19%, map 15%.

I didn't do this for encoding because the gain was too minimal to
justify the added complexity.
  • Loading branch information
fxamacker committed Nov 7, 2019
1 parent b05d751 commit 726c423
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 71 deletions.
58 changes: 30 additions & 28 deletions BENCHMARKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,37 +13,39 @@ Benchmarks use data representing the following values:
* Array: `[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26]`
* Map: `{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E", "f": "F", "g": "G", "h": "H", "i": "I", "j": "J", "l": "L", "m": "M", "n": "N"}}`

Benchmarks show that decoding into struct is >66% faster than decoding into map, and encoding struct is >64% faster than encoding map.
Benchmarks data show:
* decoding into struct is >66% faster than decoding into map.
* encoding struct is >63% faster than encoding map.

Decoding Benchmark | Time | Memory | Allocs
--- | ---: | ---: | ---:
BenchmarkUnmarshal/CBOR_bool_to_Go_interface_{}-2 | 119 ns/op | 16 B/op | 1 allocs/op
BenchmarkUnmarshal/CBOR_bool_to_Go_bool-2 | 66.8 ns/op | 1 B/op | 1 allocs/op
BenchmarkUnmarshal/CBOR_positive_int_to_Go_interface_{}-2 | 142 ns/op | 24 B/op | 2 allocs/op
BenchmarkUnmarshal/CBOR_positive_int_to_Go_uint64-2 | 71.4 ns/op | 8 B/op | 1 allocs/op
BenchmarkUnmarshal/CBOR_negative_int_to_Go_interface_{}-2 | 140 ns/op | 24 B/op | 2 allocs/op
BenchmarkUnmarshal/CBOR_negative_int_to_Go_int64-2 | 73.9 ns/op | 8 B/op | 1 allocs/op
BenchmarkUnmarshal/CBOR_float_to_Go_interface_{}-2 | 145 ns/op | 24 B/op | 2 allocs/op
BenchmarkUnmarshal/CBOR_float_to_Go_float64-2 | 71.9 ns/op | 8 B/op | 1 allocs/op
BenchmarkUnmarshal/CBOR_bytes_to_Go_interface_{}-2 | 187 ns/op | 80 B/op | 3 allocs/op
BenchmarkUnmarshal/CBOR_bytes_to_Go_[]uint8-2 | 158 ns/op | 64 B/op | 2 allocs/op
BenchmarkUnmarshal/CBOR_text_to_Go_interface_{}-2 | 215 ns/op | 80 B/op | 3 allocs/op
BenchmarkUnmarshal/CBOR_text_to_Go_string-2 | 159 ns/op | 64 B/op | 2 allocs/op
BenchmarkUnmarshal/CBOR_array_to_Go_interface_{}-2 |1122 ns/op | 672 B/op | 29 allocs/op
BenchmarkUnmarshal/CBOR_array_to_Go_[]int-2 | 1025 ns/op | 272 B/op | 3 allocs/op
BenchmarkUnmarshal/CBOR_map_to_Go_interface_{}-2 | 3043 ns/op | 1420 B/op | 30 allocs/op
BenchmarkUnmarshal/CBOR_map_to_Go_map[string]interface_{}-2 | 3857 ns/op | 964 B/op | 19 allocs/op
BenchmarkUnmarshal/CBOR_map_to_Go_map[string]string-2 | 2647 ns/op | 741 B/op | 5 allocs/op
BenchmarkUnmarshal/CBOR_map_to_Go_struct-2 | 1274 ns/op| 224 B/op | 2 allocs/op
BenchmarkUnmarshal/CBOR_bool_to_Go_interface_{}-2 | 143 ns/op | 16 B/op | 1 allocs/op
BenchmarkUnmarshal/CBOR_bool_to_Go_bool-2 | 89.6 ns/op | 1 B/op | 1 allocs/op
BenchmarkUnmarshal/CBOR_positive_int_to_Go_interface_{}-2 | 166 ns/op | 24 B/op | 2 allocs/op
BenchmarkUnmarshal/CBOR_positive_int_to_Go_uint64-2 | 101 ns/op | 8 B/op | 1 allocs/op
BenchmarkUnmarshal/CBOR_negative_int_to_Go_interface_{}-2 | 167 ns/op | 24 B/op | 2 allocs/op
BenchmarkUnmarshal/CBOR_negative_int_to_Go_int64-2 | 102 ns/op | 8 B/op | 1 allocs/op
BenchmarkUnmarshal/CBOR_float_to_Go_interface_{}-2 | 167 ns/op | 24 B/op | 2 allocs/op
BenchmarkUnmarshal/CBOR_float_to_Go_float64-2 | 101 ns/op | 8 B/op | 1 allocs/op
BenchmarkUnmarshal/CBOR_bytes_to_Go_interface_{}-2 | 214 ns/op | 80 B/op | 3 allocs/op
BenchmarkUnmarshal/CBOR_bytes_to_Go_[]uint8-2 | 187 ns/op | 64 B/op | 2 allocs/op
BenchmarkUnmarshal/CBOR_text_to_Go_interface_{}-2 | 245 ns/op | 80 B/op | 3 allocs/op
BenchmarkUnmarshal/CBOR_text_to_Go_string-2 | 181 ns/op | 64 B/op | 2 allocs/op
BenchmarkUnmarshal/CBOR_array_to_Go_interface_{}-2 |1121 ns/op | 672 B/op | 29 allocs/op
BenchmarkUnmarshal/CBOR_array_to_Go_[]int-2 | 1087 ns/op | 272 B/op | 3 allocs/op
BenchmarkUnmarshal/CBOR_map_to_Go_interface_{}-2 | 3093 ns/op | 1421 B/op | 30 allocs/op
BenchmarkUnmarshal/CBOR_map_to_Go_map[string]interface_{}-2 | 3936 ns/op | 964 B/op | 19 allocs/op
BenchmarkUnmarshal/CBOR_map_to_Go_map[string]string-2 | 2708 ns/op | 740 B/op | 5 allocs/op
BenchmarkUnmarshal/CBOR_map_to_Go_struct-2 | 1324 ns/op| 224 B/op | 2 allocs/op

Encoding Benchmark | Time | Memory | Allocs
--- | ---: | ---: | ---:
BenchmarkMarshal/Go_bool_to_CBOR_bool-2 | 75.0 ns/op | 1 B/op | 1 allocs/op
BenchmarkMarshal/Go_uint64_to_CBOR_positive_int-2 | 86.1 ns/op | 16 B/op | 1 allocs/op
BenchmarkMarshal/Go_int64_to_CBOR_negative_int-2 | 81.4 ns/op | 3 B/op | 1 allocs/op
BenchmarkMarshal/Go_float64_to_CBOR_float-2 | 85.4 ns/op | 16 B/op | 1 allocs/op
BenchmarkMarshal/Go_[]uint8_to_CBOR_bytes-2 | 119 ns/op | 32 B/op | 1 allocs/op
BenchmarkMarshal/Go_string_to_CBOR_text-2 | 105 ns/op | 48 B/op | 1 allocs/op
BenchmarkMarshal/Go_[]int_to_CBOR_array-2 | 561 ns/op | 32 B/op | 1 allocs/op
BenchmarkMarshal/Go_map[string]string_to_CBOR_map-2 | 2181 ns/op | 576 B/op | 28 allocs/op
BenchmarkMarshal/Go_struct_to_CBOR_map-2 | 780 ns/op | 64 B/op | 1 allocs/op
BenchmarkMarshal/Go_bool_to_CBOR_bool-2 | 93.8 ns/op | 1 B/op | 1 allocs/op
BenchmarkMarshal/Go_uint64_to_CBOR_positive_int-2 | 103 ns/op | 16 B/op | 1 allocs/op
BenchmarkMarshal/Go_int64_to_CBOR_negative_int-2 | 96.9 ns/op | 3 B/op | 1 allocs/op
BenchmarkMarshal/Go_float64_to_CBOR_float-2 | 101 ns/op | 16 B/op | 1 allocs/op
BenchmarkMarshal/Go_[]uint8_to_CBOR_bytes-2 | 140 ns/op | 32 B/op | 1 allocs/op
BenchmarkMarshal/Go_string_to_CBOR_text-2 | 121 ns/op | 48 B/op | 1 allocs/op
BenchmarkMarshal/Go_[]int_to_CBOR_array-2 | 599 ns/op | 32 B/op | 1 allocs/op
BenchmarkMarshal/Go_map[string]string_to_CBOR_map-2 | 2211 ns/op | 576 B/op | 28 allocs/op
BenchmarkMarshal/Go_struct_to_CBOR_map-2 | 809 ns/op | 64 B/op | 1 allocs/op
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,10 @@ err = enc.EndIndefinite()

## Benchmarks

Benchmarks data show:
* decoding into struct is >66% faster than decoding into map.
* encoding struct is >63% faster than encoding map.

See [Benchmarks for fxamacker/cbor](BENCHMARKS.md).

## Code of Conduct
Expand Down
62 changes: 37 additions & 25 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ func (d *decodeState) value(v interface{}) error {
return err
}

return d.parse(rv.Elem())
rv = rv.Elem()
return d.parse(rv, implementsUnmarshaler(rv.Type()))
}

// skip moves data offset to the next item. skip assumes data is well-formed,
Expand Down Expand Up @@ -180,7 +181,7 @@ func (d *decodeState) skip() {
}

// parse assumes data is well-formed, and does not perform bounds checking.
func (d *decodeState) parse(v reflect.Value) (err error) {
func (d *decodeState) parse(v reflect.Value, isUnmarshaler bool) (err error) {
if v.Kind() == reflect.Interface && v.NumMethod() == 0 && v.IsNil() {
// nil interface
res, err := d.parseInterface()
Expand All @@ -206,17 +207,18 @@ func (d *decodeState) parse(v reflect.Value) (err error) {
}
}

if reflect.PtrTo(v.Type()).Implements(typeUnmarshaler) {
pv := reflect.New(v.Type())
pv.Elem().Set(v)
u := pv.Interface().(Unmarshaler)
start := d.offset
d.skip()
if err := u.UnmarshalCBOR(d.data[start:d.offset]); err != nil {
return err
if isUnmarshaler {
v1 := v
if v1.Kind() != reflect.Ptr && v1.CanAddr() {
v1 = v1.Addr()
}
if v1.Kind() == reflect.Ptr && !v1.IsNil() {
if u, ok := v1.Interface().(Unmarshaler); ok {
start := d.offset
d.skip()
return u.UnmarshalCBOR(d.data[start:d.offset])
}
}
v.Set(pv.Elem())
return nil
}

// Process byte/text string.
Expand Down Expand Up @@ -245,7 +247,7 @@ func (d *decodeState) parse(v reflect.Value) (err error) {
nValue := int64(-1) ^ int64(val)
return fillNegativeInt(t, nValue, v)
case cborTypeTag:
return d.parse(v)
return d.parse(v, isUnmarshaler)
case cborTypePrimitives:
if ai < 20 {
return fillPositiveInt(t, uint64(ai), v)
Expand Down Expand Up @@ -459,8 +461,9 @@ func (d *decodeState) parseSlice(t cborType, count int, v reflect.Value) error {
v.Set(reflect.MakeSlice(v.Type(), count, count))
}
v.SetLen(count)
elemIsUnmarshaler := implementsUnmarshaler(v.Type().Elem())
for i := 0; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ {
if err := d.parse(v.Index(i)); err != nil {
if err := d.parse(v.Index(i), elemIsUnmarshaler); err != nil {
if _, ok := err.(*UnmarshalTypeError); ok {
for i++; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ {
d.skip()
Expand All @@ -474,9 +477,10 @@ func (d *decodeState) parseSlice(t cborType, count int, v reflect.Value) error {

func (d *decodeState) parseArray(t cborType, count int, v reflect.Value) error {
hasSize := count >= 0
elemIsUnmarshaler := implementsUnmarshaler(v.Type().Elem())
i := 0
for ; i < v.Len() && ((hasSize && i < count) || (!hasSize && !d.foundBreak())); i++ {
if err := d.parse(v.Index(i)); err != nil {
if err := d.parse(v.Index(i), elemIsUnmarshaler); err != nil {
if _, ok := err.(*UnmarshalTypeError); ok {
for i++; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ {
d.skip()
Expand Down Expand Up @@ -531,6 +535,8 @@ func (d *decodeState) parseMap(t cborType, count int, v reflect.Value) error {
keyType, eleType := v.Type().Key(), v.Type().Elem()
reuseKey, reuseEle := isImmutableKind(keyType.Kind()), isImmutableKind(eleType.Kind())
var keyValue, eleValue, zeroKeyValue, zeroEleValue reflect.Value
keyIsUnmarshaler := implementsUnmarshaler(v.Type().Key())
elemIsUnmarshaler := implementsUnmarshaler(v.Type().Elem())
for i := 0; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ {
if !keyValue.IsValid() {
keyValue = reflect.New(keyType).Elem()
Expand All @@ -540,7 +546,7 @@ func (d *decodeState) parseMap(t cborType, count int, v reflect.Value) error {
}
keyValue.Set(zeroKeyValue)
}
if err := d.parse(keyValue); err != nil {
if err := d.parse(keyValue, keyIsUnmarshaler); err != nil {
if _, ok := err.(*UnmarshalTypeError); ok {
for i = i*2 + 1; (hasSize && i < count*2) || (!hasSize && !d.foundBreak()); i++ {
d.skip()
Expand All @@ -557,7 +563,7 @@ func (d *decodeState) parseMap(t cborType, count int, v reflect.Value) error {
}
eleValue.Set(zeroEleValue)
}
if err := d.parse(eleValue); err != nil {
if err := d.parse(eleValue, elemIsUnmarshaler); err != nil {
if _, ok := err.(*UnmarshalTypeError); ok {
for i = i*2 + 2; (hasSize && i < count*2) || (!hasSize && !d.foundBreak()); i++ {
d.skip()
Expand Down Expand Up @@ -626,7 +632,7 @@ func (d *decodeState) parseStruct(t cborType, count int, v reflect.Value) error
if err != nil {
return err
}
if err := d.parse(fv); err != nil {
if err := d.parse(fv, f.isUnmarshaler); err != nil {
if d.err == nil {
if typeError, ok := err.(*UnmarshalTypeError); ok {
typeError.Struct = v.Type().String()
Expand Down Expand Up @@ -769,14 +775,13 @@ func fillFloat(t cborType, val float64, v reflect.Value) error {

func fillByteString(t cborType, val []byte, v reflect.Value) error {
if v.Type() != typeTime && reflect.PtrTo(v.Type()).Implements(typeBinaryUnmarshaler) {
pv := reflect.New(v.Type())
pv.Elem().Set(v)
u := pv.Interface().(encoding.BinaryUnmarshaler)
if err := u.UnmarshalBinary(val); err != nil {
return err
if v.CanAddr() {
v = v.Addr()
if u, ok := v.Interface().(encoding.BinaryUnmarshaler); ok {
return u.UnmarshalBinary(val)
}
}
v.Set(pv.Elem())
return nil
return errors.New("cbor: cannot set new value for " + v.Type().String())
}
if v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.Uint8 {
v.SetBytes(val)
Expand Down Expand Up @@ -843,6 +848,13 @@ func isHashableKind(k reflect.Kind) bool {
}
}

func implementsUnmarshaler(t reflect.Type) bool {
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
return reflect.PtrTo(t).Implements(typeUnmarshaler)
}

// Unmarshal parses the CBOR-encoded data and stores the result in the value
// pointed to by v. If v is nil or not a pointer, Unmarshal returns an error.
//
Expand Down
30 changes: 29 additions & 1 deletion decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -764,7 +764,6 @@ func TestUnmarshalIntoPointer(t *testing.T) {
} else if p3 != nil {
t.Errorf("Unmarshal(0x%0x) = %v (%T), want nil", cborDataNil, p1, p1)
}

// Unmarshal CBOR integer into a non-nil pointer.
if err := cbor.Unmarshal(cborDataInt, &ppi); err != nil {
t.Errorf("Unmarshal(0x%0x) returns error %v", cborDataInt, err)
Expand Down Expand Up @@ -1752,3 +1751,32 @@ func TestOutOfMem2(t *testing.T) {
t.Errorf("Unmarshal(0x%0x) doesn't return error", cborData)
}
}

// Found at https://github.com/cose-wg/Examples/tree/master/RFC8152
func TestCOSEExamples(t *testing.T) {
cborData := [][]byte{
hexDecode("D8608443A10101A1054C02D1F7E6F26C43D4868D87CE582464F84D913BA60A76070A9A48F26E97E863E2852948658F0811139868826E89218A75715B818440A101225818DBD43C4E9D719C27C6275C67D628D493F090593DB8218F11818344A1013818A220A401022001215820B2ADD44368EA6D641F9CA9AF308B4079AEB519F11E9B8A55A600B21233E86E6822F40458246D65726961646F632E6272616E64796275636B406275636B6C616E642E6578616D706C6540"),
hexDecode("D8628440A054546869732069732074686520636F6E74656E742E818343A10126A1044231315840E2AEAFD40D69D19DFE6E52077C5D7FF4E408282CBEFB5D06CBF414AF2E19D982AC45AC98B8544C908B4507DE1E90B717C3D34816FE926A2B98F53AFD2FA0F30A"),
hexDecode("D8628440A054546869732069732074686520636F6E74656E742E828343A10126A1044231315840E2AEAFD40D69D19DFE6E52077C5D7FF4E408282CBEFB5D06CBF414AF2E19D982AC45AC98B8544C908B4507DE1E90B717C3D34816FE926A2B98F53AFD2FA0F30A8344A1013823A104581E62696C626F2E62616767696E7340686F626269746F6E2E6578616D706C65588400A2D28A7C2BDB1587877420F65ADF7D0B9A06635DD1DE64BB62974C863F0B160DD2163734034E6AC003B01E8705524C5C4CA479A952F0247EE8CB0B4FB7397BA08D009E0C8BF482270CC5771AA143966E5A469A09F613488030C5B07EC6D722E3835ADB5B2D8C44E95FFB13877DD2582866883535DE3BB03D01753F83AB87BB4F7A0297"),
hexDecode("D8628440A1078343A10126A10442313158405AC05E289D5D0E1B0A7F048A5D2B643813DED50BC9E49220F4F7278F85F19D4A77D655C9D3B51E805A74B099E1E085AACD97FC29D72F887E8802BB6650CCEB2C54546869732069732074686520636F6E74656E742E818343A10126A1044231315840E2AEAFD40D69D19DFE6E52077C5D7FF4E408282CBEFB5D06CBF414AF2E19D982AC45AC98B8544C908B4507DE1E90B717C3D34816FE926A2B98F53AFD2FA0F30A"),
hexDecode("D8628456A2687265736572766564F40281687265736572766564A054546869732069732074686520636F6E74656E742E818343A10126A10442313158403FC54702AA56E1B2CB20284294C9106A63F91BAC658D69351210A031D8FC7C5FF3E4BE39445B1A3E83E1510D1ACA2F2E8A7C081C7645042B18ABA9D1FAD1BD9C"),
hexDecode("D28443A10126A10442313154546869732069732074686520636F6E74656E742E58408EB33E4CA31D1C465AB05AAC34CC6B23D58FEF5C083106C4D25A91AEF0B0117E2AF9A291AA32E14AB834DC56ED2A223444547E01F11D3B0916E5A4C345CACB36"),
hexDecode("D8608443A10101A1054CC9CF4DF2FE6C632BF788641358247ADBE2709CA818FB415F1E5DF66F4E1A51053BA6D65A1A0C52A357DA7A644B8070A151B0818344A1013818A220A40102200121582098F50A4FF6C05861C8860D13A638EA56C3F5AD7590BBFBF054E1C7B4D91D628022F50458246D65726961646F632E6272616E64796275636B406275636B6C616E642E6578616D706C6540"),
hexDecode("D8608443A1010AA1054D89F52F65A1C580933B5261A76C581C753548A19B1307084CA7B2056924ED95F2E3B17006DFE931B687B847818343A10129A2335061616262636364646565666667676868044A6F75722D73656372657440"),
hexDecode("D8608443A10101A2054CC9CF4DF2FE6C632BF7886413078344A1013823A104581E62696C626F2E62616767696E7340686F626269746F6E2E6578616D706C65588400929663C8789BB28177AE28467E66377DA12302D7F9594D2999AFA5DFA531294F8896F2B6CDF1740014F4C7F1A358E3A6CF57F4ED6FB02FCF8F7AA989F5DFD07F0700A3A7D8F3C604BA70FA9411BD10C2591B483E1D2C31DE003183E434D8FBA18F17A4C7E3DFA003AC1CF3D30D44D2533C4989D3AC38C38B71481CC3430C9D65E7DDFF58247ADBE2709CA818FB415F1E5DF66F4E1A51053BA6D65A1A0C52A357DA7A644B8070A151B0818344A1013818A220A40102200121582098F50A4FF6C05861C8860D13A638EA56C3F5AD7590BBFBF054E1C7B4D91D628022F50458246D65726961646F632E6272616E64796275636B406275636B6C616E642E6578616D706C6540"),
hexDecode("D8608443A10101A1054C02D1F7E6F26C43D4868D87CE582464F84D913BA60A76070A9A48F26E97E863E28529D8F5335E5F0165EEE976B4A5F6C6F09D818344A101381FA3225821706572656772696E2E746F6F6B407475636B626F726F7567682E6578616D706C650458246D65726961646F632E6272616E64796275636B406275636B6C616E642E6578616D706C6535420101581841E0D76F579DBD0D936A662D54D8582037DE2E366FDE1C62"),
hexDecode("D08343A1010AA1054D89F52F65A1C580933B5261A78C581C5974E1B99A3A4CC09A659AA2E9E7FFF161D38CE71CB45CE460FFB569"),
hexDecode("D08343A1010AA1064261A7581C252A8911D465C125B6764739700F0141ED09192DE139E053BD09ABCA"),
hexDecode("D8618543A1010FA054546869732069732074686520636F6E74656E742E489E1226BA1F81B848818340A20125044A6F75722D73656372657440"),
hexDecode("D8618543A10105A054546869732069732074686520636F6E74656E742E582081A03448ACD3D305376EAA11FB3FE416A955BE2CBE7EC96F012C994BC3F16A41818344A101381AA3225821706572656772696E2E746F6F6B407475636B626F726F7567682E6578616D706C650458246D65726961646F632E6272616E64796275636B406275636B6C616E642E6578616D706C653558404D8553E7E74F3C6A3A9DD3EF286A8195CBF8A23D19558CCFEC7D34B824F42D92BD06BD2C7F0271F0214E141FB779AE2856ABF585A58368B017E7F2A9E5CE4DB540"),
hexDecode("D8618543A1010EA054546869732069732074686520636F6E74656E742E4836F5AFAF0BAB5D43818340A2012404582430313863306165352D346439622D343731622D626664362D6565663331346263373033375818711AB0DC2FC4585DCE27EFFA6781C8093EBA906F227B6EB0"),
hexDecode("D8618543A10105A054546869732069732074686520636F6E74656E742E5820BF48235E809B5C42E995F2B7D5FA13620E7ED834E337F6AA43DF161E49E9323E828344A101381CA220A4010220032158420043B12669ACAC3FD27898FFBA0BCD2E6C366D53BC4DB71F909A759304ACFB5E18CDC7BA0B13FF8C7636271A6924B1AC63C02688075B55EF2D613574E7DC242F79C322F504581E62696C626F2E62616767696E7340686F626269746F6E2E6578616D706C655828339BC4F79984CDC6B3E6CE5F315A4C7D2B0AC466FCEA69E8C07DFBCA5BB1F661BC5F8E0DF9E3EFF58340A2012404582430313863306165352D346439622D343731622D626664362D65656633313462633730333758280B2C7CFCE04E98276342D6476A7723C090DFDD15F9A518E7736549E998370695E6D6A83B4AE507BB"),
hexDecode("D18443A1010FA054546869732069732074686520636F6E74656E742E48726043745027214F"),
}
for _, d := range cborData {
var v interface{}
if err := cbor.Unmarshal(d, &v); err != nil {
t.Errorf("Unmarshal(0x%0x) returns error %s", d, err)
}
}
}
19 changes: 10 additions & 9 deletions encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,16 @@ func getEncodeIndirectValueFunc(f encodeFunc) encodeFunc {
}

func getEncodeFunc(t reflect.Type) encodeFunc {
if t.Kind() == reflect.Ptr {
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
f := getEncodeFunc(t)
if f == nil {
return f
}
return getEncodeIndirectValueFunc(f)
}
if reflect.PtrTo(t).Implements(typeMarshaler) {
return encodeMarshalerType
}
Expand Down Expand Up @@ -483,15 +493,6 @@ func getEncodeFunc(t reflect.Type) encodeFunc {
return encodeMap
case reflect.Struct:
return encodeStruct
case reflect.Ptr:
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
f := getEncodeFunc(t)
if f == nil {
return f
}
return getEncodeIndirectValueFunc(f)
case reflect.Interface:
return encodeIntf
default:
Expand Down
20 changes: 12 additions & 8 deletions structfields.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,15 @@ func fieldByIndex(v reflect.Value, index []int) (reflect.Value, error) {
}

type field struct {
name string
idx []int
typ reflect.Type
ef encodeFunc
cborName [64]byte
cborNameLen int
tagged bool // used to choose dominant field (at the same level tagged fields dominate untagged fields)
omitempty bool // used to skip empty field
name string
idx []int
typ reflect.Type
isUnmarshaler bool
ef encodeFunc
cborName [64]byte
cborNameLen int
tagged bool // used to choose dominant field (at the same level tagged fields dominate untagged fields)
omitempty bool // used to skip empty field
}

type fields []field
Expand Down Expand Up @@ -245,6 +246,9 @@ func getStructFields(t reflect.Type) fields {
return v.(fields)
}
flds := getFields(t)
for i := 0; i < len(flds); i++ {
flds[i].isUnmarshaler = implementsUnmarshaler(flds[i].typ)
}
cachedStructFields.Store(t, flds)
return flds
}
Expand Down

0 comments on commit 726c423

Please sign in to comment.