Skip to content

Commit

Permalink
Improve encoding speed
Browse files Browse the repository at this point in the history
Speed improvements:
- streamline []byte/slice/array encoding
- preallocate 32 bytes for new encoding buffer

Comparison with commit 593ee34:
benchmark                                               old ns/op     new ns/op     delta
BenchmarkMarshal/Go_bool_to_CBOR_bool-2                 94.3          93.4          -0.95%
BenchmarkMarshal/Go_int64_to_CBOR_negative_int-2        97.7          97.1          -0.61%
BenchmarkMarshal/Go_float64_to_CBOR_float-2             103           102           -0.97%
BenchmarkMarshal/Go_[]uint8_to_CBOR_bytes-2             140           131           -6.43%
BenchmarkMarshal/Go_string_to_CBOR_text-2               124           122           -1.61%
BenchmarkMarshal/Go_[]int_to_CBOR_array-2               598           529           -11.54%
BenchmarkMarshal/Go_map[string]string_to_CBOR_map-2     2278          2254          -1.05%
BenchmarkMarshal/Go_struct_to_CBOR_map-2                728           714           -1.92%
  • Loading branch information
fxamacker committed Nov 12, 2019
1 parent 593ee34 commit d85552b
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 35 deletions.
27 changes: 17 additions & 10 deletions decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1213,16 +1213,6 @@ func TestValid(t *testing.T) {
}
}

func TestSkipData(t *testing.T) {
cborData := hexDecode("850102031bffffffffffffffffc11a514b67b0") // [1, 2, 3, 18446744073709551615, 1363896240]
i := 0
if err := cbor.Unmarshal(cborData, &i); err == nil {
t.Errorf("Unmarshal(0x%0x) doesn't return error, want UnmarshalTypeError", cborData)
} else if _, ok := err.(*cbor.UnmarshalTypeError); !ok {
t.Errorf("Unmarshal(0x%0x) returns wrong type of error %T, want (*cbor.UnmarshalTypeError)", cborData, err)
}
}

func TestStructFieldNil(t *testing.T) {
type TestStruct struct {
I int
Expand Down Expand Up @@ -1323,6 +1313,23 @@ func TestFuzzCrash5(t *testing.T) {
}
}

func TestFuzzCrash6(t *testing.T) {
// Crash5: parsing nil into map key.
testData := [][]byte{
[]byte("\x82\xa1\xf60000"),
[]byte("\xa1\xf600"),
}
for _, data := range testData {
var intf interface{}
if err := cbor.Unmarshal(data, &intf); err != nil {
t.Fatalf("Unmarshal(0x%02x) returns error %s\n", data, err)
}
if _, err := cbor.Marshal(intf, cbor.EncOptions{Canonical: true}); err != nil {
t.Errorf("Marshal(%v) returns error %s", intf, err)
}
}
}

func TestDecodeTime(t *testing.T) {
testCases := []struct {
name string
Expand Down
55 changes: 30 additions & 25 deletions encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ type encodeState struct {
// encodeStatePool caches unused encodeState objects for later reuse.
var encodeStatePool = sync.Pool{
New: func() interface{} {
return new(encodeState)
es := new(encodeState)
es.Grow(32) // TODO: make this configurable
return es
},
}

Expand Down Expand Up @@ -175,12 +177,19 @@ func encodeFloat(e *encodeState, v reflect.Value, opts EncOptions) (int, error)
}

func encodeByteString(e *encodeState, v reflect.Value, opts EncOptions) (int, error) {
n1 := encodeTypeAndAdditionalValue(e, byte(cborTypeByteString), uint64(v.Len()))
if v.Kind() == reflect.Slice && v.IsNil() {
return e.Write(cborNil)
}
len := v.Len()
if len == 0 {
return 1, e.WriteByte(byte(cborTypeByteString))
}
n1 := encodeTypeAndAdditionalValue(e, byte(cborTypeByteString), uint64(len))
if v.Kind() == reflect.Array {
for i := 0; i < v.Len(); i++ {
for i := 0; i < len; i++ {
e.WriteByte(byte(v.Index(i).Uint()))
}
return n1 + v.Len(), nil
return n1 + len, nil
}
n2, _ := e.Write(v.Bytes())
return n1 + n2, nil
Expand All @@ -196,26 +205,20 @@ func encodeStringInternal(e *encodeState, s string, opts EncOptions) (int, error
return n1 + n2, nil
}

func encodeSlice(e *encodeState, v reflect.Value, opts EncOptions) (int, error) {
if v.IsNil() {
return e.Write(cborNil)
}
return encodeArray(e, v, opts)
}

func encodeArray(e *encodeState, v reflect.Value, opts EncOptions) (int, error) {
if v.Type().Elem().Kind() == reflect.Uint8 {
return encodeByteString(e, v, opts)
}
f := getEncodeFunc(v.Type().Elem())
if f == nil {
return 0, &UnsupportedTypeError{v.Type()}
}
if v.Len() == 0 {
return encodeTypeAndAdditionalValue(e, byte(cborTypeArray), uint64(0)), nil
if v.Kind() == reflect.Slice && v.IsNil() {
return e.Write(cborNil)
}
n := encodeTypeAndAdditionalValue(e, byte(cborTypeArray), uint64(v.Len()))
for i := 0; i < v.Len(); i++ {
len := v.Len()
if len == 0 {
return 1, e.WriteByte(byte(cborTypeArray))
}
n := encodeTypeAndAdditionalValue(e, byte(cborTypeArray), uint64(len))
for i := 0; i < len; i++ {
n1, err := f(e, v.Index(i), opts)
if err != nil {
return 0, err
Expand All @@ -237,10 +240,11 @@ func encodeMap(e *encodeState, v reflect.Value, opts EncOptions) (int, error) {
if v.IsNil() {
return e.Write(cborNil)
}
if v.Len() == 0 {
return encodeTypeAndAdditionalValue(e, byte(cborTypeMap), uint64(0)), nil
len := v.Len()
if len == 0 {
return 1, e.WriteByte(byte(cborTypeMap))
}
n := encodeTypeAndAdditionalValue(e, byte(cborTypeMap), uint64(v.Len()))
n := encodeTypeAndAdditionalValue(e, byte(cborTypeMap), uint64(len))
iter := v.MapRange()
for iter.Next() {
n1, err := kf(e, iter.Key(), opts)
Expand Down Expand Up @@ -310,7 +314,7 @@ func encodeMapCanonical(e *encodeState, v reflect.Value, opts EncOptions) (int,
return e.Write(cborNil)
}
if v.Len() == 0 {
return encodeTypeAndAdditionalValue(e, byte(cborTypeMap), uint64(0)), nil
return 1, e.WriteByte(byte(cborTypeMap))
}
pairEncodeState := getEncodeState() // accumulated cbor encoded map key-value pairs
pairs := getByCanonical(v.Len()) // for sorting keys
Expand Down Expand Up @@ -492,10 +496,11 @@ func getEncodeFunc(t reflect.Type) encodeFunc {
return encodeFloat
case reflect.String:
return encodeString
case reflect.Array:
case reflect.Slice, reflect.Array:
if t.Elem().Kind() == reflect.Uint8 {
return encodeByteString
}
return encodeArray
case reflect.Slice:
return encodeSlice
case reflect.Map:
return encodeMap
case reflect.Struct:
Expand Down

0 comments on commit d85552b

Please sign in to comment.