Skip to content

Commit 4cf6490

Browse files
committed
Take advantage of string([]byte) optimization
Previously, we used unsafe to avoid copying the bytes from the buffer when doing the map lookup for the struct field. The Go compiler has a special optimization for string([]byte) lookups that we can take advantage of instead: golang/go#3512 This reduces code complexity a bit and eliminates unsafe usage for non-Windows builds.
1 parent eac6e3b commit 4cf6490

File tree

3 files changed

+19
-47
lines changed

3 files changed

+19
-47
lines changed

decoder.go

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -416,9 +416,9 @@ func (d *decoder) decodeMap(size uint, offset uint, result reflect.Value) (uint,
416416
}
417417

418418
for i := uint(0); i < size; i++ {
419-
var key string
419+
var key []byte
420420
var err error
421-
key, offset, err = d.decodeKeyString(offset)
421+
key, offset, err = d.decodeKey(offset)
422422

423423
if err != nil {
424424
return 0, err
@@ -429,7 +429,7 @@ func (d *decoder) decodeMap(size uint, offset uint, result reflect.Value) (uint,
429429
if err != nil {
430430
return 0, err
431431
}
432-
result.SetMapIndex(reflect.ValueOf(key), value.Elem())
432+
result.SetMapIndex(reflect.ValueOf(string(key)), value.Elem())
433433
}
434434
return offset, nil
435435
}
@@ -534,13 +534,15 @@ func (d *decoder) decodeStruct(size uint, offset uint, result reflect.Value) (ui
534534
for i := uint(0); i < size; i++ {
535535
var (
536536
err error
537-
key string
537+
key []byte
538538
)
539-
key, offset, err = d.decodeStructKey(offset)
539+
key, offset, err = d.decodeKey(offset)
540540
if err != nil {
541541
return 0, err
542542
}
543-
j, ok := fields.namedFields[key]
543+
// The string() does not create a copy due to this compiler
544+
// optimization: https://github.com/golang/go/issues/3512
545+
j, ok := fields.namedFields[string(key)]
544546
if !ok {
545547
offset = d.nextValueOffset(offset, 1)
546548
continue
@@ -577,17 +579,22 @@ func uintFromBytes(prefix uint64, uintBytes []byte) uint64 {
577579
return val
578580
}
579581

580-
func (d *decoder) decodeKeyString(offset uint) (string, uint, error) {
581-
typeNum, size, newOffset := d.decodeCtrlData(offset)
582+
// decodeKey decodes a map key into []byte slice. We use a []byte so that we
583+
// can take advantage of https://github.com/golang/go/issues/3512 to avoid
584+
// copying the bytes when decoding a struct. Previously, we achieved this by
585+
// using unsafe.
586+
func (d *decoder) decodeKey(offset uint) ([]byte, uint, error) {
587+
typeNum, size, dataOffset := d.decodeCtrlData(offset)
582588
if typeNum == _Pointer {
583-
pointer, ptrOffset := d.decodePointer(size, newOffset)
584-
key, _, err := d.decodeKeyString(pointer)
589+
pointer, ptrOffset := d.decodePointer(size, dataOffset)
590+
key, _, err := d.decodeKey(pointer)
585591
return key, ptrOffset, err
586592
}
587593
if typeNum != _String {
588-
return "", 0, newInvalidDatabaseError("unexpected type when decoding string: %v", typeNum)
594+
return nil, 0, newInvalidDatabaseError("unexpected type when decoding string: %v", typeNum)
589595
}
590-
return d.decodeString(size, newOffset)
596+
newOffset := dataOffset + size
597+
return d.buffer[dataOffset:newOffset], newOffset, nil
591598
}
592599

593600
// This function is used to skip ahead to the next value without decoding

key_appengine.go

Lines changed: 0 additions & 7 deletions
This file was deleted.

key_other.go

Lines changed: 0 additions & 28 deletions
This file was deleted.

0 commit comments

Comments
 (0)