goprotobuf: remove unsafe buffer scratch allocator
This is part of the work towards making protobufs run on App Engine.
By the way, it's faster now.
Before:
proto_test.BenchmarkUnmarshal 20000 87053 ns/op
After:
proto_test.BenchmarkUnmarshal 20000 84825 ns/op
R=dsymonds, rsc, ken, r
CC=golang-dev
http://codereview.appspot.com/5310048
diff --git a/proto/decode.go b/proto/decode.go
index fe28821..fd3960d 100644
--- a/proto/decode.go
+++ b/proto/decode.go
@@ -333,7 +333,6 @@
st := t.Elem()
prop := GetProperties(st)
required, reqFields := prop.reqCount, uint64(0)
- sbase := getsbase(prop) // scratch area for data items
var err os.Error
for err == nil && o.index < len(o.buf) {
@@ -354,7 +353,7 @@
fieldnum, ok := prop.tags[tag]
if !ok {
// Maybe it's an extension?
- o.ptr = base
+ o.ptr = base // copy the address here to avoid a heap allocation.
iv := unsafe.Unreflect(t, unsafe.Pointer(&o.ptr))
if e, ok := iv.(extendableProto); ok && isExtensionField(e, int32(tag)) {
if err = o.skip(st, tag, wire); err == nil {
@@ -381,7 +380,7 @@
continue
}
}
- err = dec(o, p, base, sbase)
+ err = dec(o, p, base)
if err == nil && p.Required {
// Successfully decoded a required field.
if tag <= 64 {
@@ -424,100 +423,102 @@
// For each,
// u is the decoded value,
// v is a pointer to the field (pointer) in the struct
-// x is a pointer to the preallocated scratch space to hold the decoded value.
+
+// Sizes of the pools to allocate inside the Buffer.
+// The goal is modest amortization and allocation on at lest 16-byte boundaries.
+const (
+ boolPoolSize = 16
+ int32PoolSize = 8
+ int64PoolSize = 4
+)
// Decode a bool.
-func (o *Buffer) dec_bool(p *Properties, base uintptr, sbase uintptr) os.Error {
+func (o *Buffer) dec_bool(p *Properties, base uintptr) os.Error {
u, err := p.valDec(o)
if err != nil {
return err
}
- v := (**uint8)(unsafe.Pointer(base + p.offset))
- x := (*uint8)(unsafe.Pointer(sbase + p.scratch))
- *x = uint8(u)
- *v = x
+ if len(o.bools) == 0 {
+ o.bools = make([]bool, boolPoolSize)
+ }
+ o.bools[0] = u != 0
+ v := (**bool)(unsafe.Pointer(base + p.offset))
+ *v = &o.bools[0]
+ o.bools = o.bools[1:]
return nil
}
// Decode an int32.
-func (o *Buffer) dec_int32(p *Properties, base uintptr, sbase uintptr) os.Error {
+func (o *Buffer) dec_int32(p *Properties, base uintptr) os.Error {
u, err := p.valDec(o)
if err != nil {
return err
}
+ if len(o.int32s) == 0 {
+ o.int32s = make([]int32, int32PoolSize)
+ }
+ o.int32s[0] = int32(u)
v := (**int32)(unsafe.Pointer(base + p.offset))
- x := (*int32)(unsafe.Pointer(sbase + p.scratch))
- *x = int32(u)
- *v = x
+ *v = &o.int32s[0]
+ o.int32s = o.int32s[1:]
return nil
}
// Decode an int64.
-func (o *Buffer) dec_int64(p *Properties, base uintptr, sbase uintptr) os.Error {
+func (o *Buffer) dec_int64(p *Properties, base uintptr) os.Error {
u, err := p.valDec(o)
if err != nil {
return err
}
+ if len(o.int64s) == 0 {
+ o.int64s = make([]int64, int64PoolSize)
+ }
+ o.int64s[0] = int64(u)
v := (**int64)(unsafe.Pointer(base + p.offset))
- x := (*int64)(unsafe.Pointer(sbase + p.scratch))
- *x = int64(u)
- *v = x
+ *v = &o.int64s[0]
+ o.int64s = o.int64s[1:]
return nil
}
// Decode a string.
-func (o *Buffer) dec_string(p *Properties, base uintptr, sbase uintptr) os.Error {
+func (o *Buffer) dec_string(p *Properties, base uintptr) os.Error {
s, err := o.DecodeStringBytes()
if err != nil {
return err
}
+ sp := new(string)
+ *sp = s
v := (**string)(unsafe.Pointer(base + p.offset))
- x := (*string)(unsafe.Pointer(sbase + p.scratch))
- *x = s
- *v = x
+ *v = sp
return nil
}
// Decode a slice of bytes ([]byte).
-func (o *Buffer) dec_slice_byte(p *Properties, base uintptr, sbase uintptr) os.Error {
- b, err := o.DecodeRawBytes(false)
+func (o *Buffer) dec_slice_byte(p *Properties, base uintptr) os.Error {
+ b, err := o.DecodeRawBytes(true)
if err != nil {
return err
}
- x := (*[]uint8)(unsafe.Pointer(base + p.offset))
-
- y := *x
- if cap(y) == 0 {
- initSlice(unsafe.Pointer(x), sbase+p.scratch)
- y = *x
- }
-
- *x = append(y, b...)
+ v := (*[]byte)(unsafe.Pointer(base + p.offset))
+ *v = b
return nil
}
// Decode a slice of bools ([]bool).
-func (o *Buffer) dec_slice_bool(p *Properties, base uintptr, sbase uintptr) os.Error {
+func (o *Buffer) dec_slice_bool(p *Properties, base uintptr) os.Error {
u, err := p.valDec(o)
if err != nil {
return err
}
- x := (*[]bool)(unsafe.Pointer(base + p.offset))
-
- y := *x
- if cap(y) == 0 {
- initSlice(unsafe.Pointer(x), sbase+p.scratch)
- y = *x
- }
-
- *x = append(y, u != 0)
+ v := (*[]bool)(unsafe.Pointer(base + p.offset))
+ *v = append(*v, u != 0)
return nil
}
// Decode a slice of bools ([]bool) in packed format.
-func (o *Buffer) dec_slice_packed_bool(p *Properties, base uintptr, sbase uintptr) os.Error {
- x := (*[]bool)(unsafe.Pointer(base + p.offset))
+func (o *Buffer) dec_slice_packed_bool(p *Properties, base uintptr) os.Error {
+ v := (*[]bool)(unsafe.Pointer(base + p.offset))
nn, err := o.DecodeVarint()
if err != nil {
@@ -525,12 +526,7 @@
}
nb := int(nn) // number of bytes of encoded bools
- y := *x
- if cap(y) == 0 {
- initSlice(unsafe.Pointer(x), sbase+p.scratch)
- y = *x
- }
-
+ y := *v
for i := 0; i < nb; i++ {
u, err := p.valDec(o)
if err != nil {
@@ -539,31 +535,24 @@
y = append(y, u != 0)
}
- *x = y
+ *v = y
return nil
}
// Decode a slice of int32s ([]int32).
-func (o *Buffer) dec_slice_int32(p *Properties, base uintptr, sbase uintptr) os.Error {
+func (o *Buffer) dec_slice_int32(p *Properties, base uintptr) os.Error {
u, err := p.valDec(o)
if err != nil {
return err
}
- x := (*[]int32)(unsafe.Pointer(base + p.offset))
-
- y := *x
- if cap(y) == 0 {
- initSlice(unsafe.Pointer(x), sbase+p.scratch)
- y = *x
- }
-
- *x = append(y, int32(u))
+ v := (*[]int32)(unsafe.Pointer(base + p.offset))
+ *v = append(*v, int32(u))
return nil
}
// Decode a slice of int32s ([]int32) in packed format.
-func (o *Buffer) dec_slice_packed_int32(p *Properties, base uintptr, sbase uintptr) os.Error {
- x := (*[]int32)(unsafe.Pointer(base + p.offset))
+func (o *Buffer) dec_slice_packed_int32(p *Properties, base uintptr) os.Error {
+ v := (*[]int32)(unsafe.Pointer(base + p.offset))
nn, err := o.DecodeVarint()
if err != nil {
@@ -571,11 +560,7 @@
}
nb := int(nn) // number of bytes of encoded int32s
- y := *x
- if cap(y) == 0 {
- initSlice(unsafe.Pointer(x), sbase+p.scratch)
- y = *x
- }
+ y := *v
fin := o.index + nb
for o.index < fin {
@@ -586,31 +571,26 @@
y = append(y, int32(u))
}
- *x = y
+ *v = y
return nil
}
// Decode a slice of int64s ([]int64).
-func (o *Buffer) dec_slice_int64(p *Properties, base uintptr, sbase uintptr) os.Error {
+func (o *Buffer) dec_slice_int64(p *Properties, base uintptr) os.Error {
u, err := p.valDec(o)
if err != nil {
return err
}
- x := (*[]int64)(unsafe.Pointer(base + p.offset))
+ v := (*[]int64)(unsafe.Pointer(base + p.offset))
- y := *x
- if cap(y) == 0 {
- initSlice(unsafe.Pointer(x), sbase+p.scratch)
- y = *x
- }
-
- *x = append(y, int64(u))
+ y := *v
+ *v = append(y, int64(u))
return nil
}
// Decode a slice of int64s ([]int64) in packed format.
-func (o *Buffer) dec_slice_packed_int64(p *Properties, base uintptr, sbase uintptr) os.Error {
- x := (*[]int64)(unsafe.Pointer(base + p.offset))
+func (o *Buffer) dec_slice_packed_int64(p *Properties, base uintptr) os.Error {
+ v := (*[]int64)(unsafe.Pointer(base + p.offset))
nn, err := o.DecodeVarint()
if err != nil {
@@ -618,12 +598,7 @@
}
nb := int(nn) // number of bytes of encoded int64s
- y := *x
- if cap(y) == 0 {
- initSlice(unsafe.Pointer(x), sbase+p.scratch)
- y = *x
- }
-
+ y := *v
fin := o.index + nb
for o.index < fin {
u, err := p.valDec(o)
@@ -633,48 +608,38 @@
y = append(y, int64(u))
}
- *x = y
+ *v = y
return nil
}
// Decode a slice of strings ([]string).
-func (o *Buffer) dec_slice_string(p *Properties, base uintptr, sbase uintptr) os.Error {
+func (o *Buffer) dec_slice_string(p *Properties, base uintptr) os.Error {
s, err := o.DecodeStringBytes()
if err != nil {
return err
}
- x := (*[]string)(unsafe.Pointer(base + p.offset))
+ v := (*[]string)(unsafe.Pointer(base + p.offset))
- y := *x
- if cap(y) == 0 {
- initSlice(unsafe.Pointer(x), sbase+p.scratch)
- y = *x
- }
-
- *x = append(y, s)
+ y := *v
+ *v = append(y, s)
return nil
}
// Decode a slice of slice of bytes ([][]byte).
-func (o *Buffer) dec_slice_slice_byte(p *Properties, base uintptr, sbase uintptr) os.Error {
+func (o *Buffer) dec_slice_slice_byte(p *Properties, base uintptr) os.Error {
b, err := o.DecodeRawBytes(true)
if err != nil {
return err
}
- x := (*[][]byte)(unsafe.Pointer(base + p.offset))
+ v := (*[][]byte)(unsafe.Pointer(base + p.offset))
- y := *x
- if cap(y) == 0 {
- initSlice(unsafe.Pointer(x), sbase+p.scratch)
- y = *x
- }
-
- *x = append(y, b)
+ y := *v
+ *v = append(y, b)
return nil
}
// Decode a group.
-func (o *Buffer) dec_struct_group(p *Properties, base uintptr, sbase uintptr) os.Error {
+func (o *Buffer) dec_struct_group(p *Properties, base uintptr) os.Error {
ptr := (**struct{})(unsafe.Pointer(base + p.offset))
typ := p.stype.Elem()
structv := unsafe.New(typ)
@@ -687,7 +652,7 @@
}
// Decode an embedded message.
-func (o *Buffer) dec_struct_message(p *Properties, base uintptr, sbase uintptr) (err os.Error) {
+func (o *Buffer) dec_struct_message(p *Properties, base uintptr) (err os.Error) {
raw, e := o.DecodeRawBytes(false)
if e != nil {
return e
@@ -718,30 +683,26 @@
}
// Decode a slice of embedded messages.
-func (o *Buffer) dec_slice_struct_message(p *Properties, base uintptr, sbase uintptr) os.Error {
- return o.dec_slice_struct(p, false, base, sbase)
+func (o *Buffer) dec_slice_struct_message(p *Properties, base uintptr) os.Error {
+ return o.dec_slice_struct(p, false, base)
}
// Decode a slice of embedded groups.
-func (o *Buffer) dec_slice_struct_group(p *Properties, base uintptr, sbase uintptr) os.Error {
- return o.dec_slice_struct(p, true, base, sbase)
+func (o *Buffer) dec_slice_struct_group(p *Properties, base uintptr) os.Error {
+ return o.dec_slice_struct(p, true, base)
}
// Decode a slice of structs ([]*struct).
-func (o *Buffer) dec_slice_struct(p *Properties, is_group bool, base uintptr, sbase uintptr) os.Error {
+func (o *Buffer) dec_slice_struct(p *Properties, is_group bool, base uintptr) os.Error {
- x := (*[]*struct{})(unsafe.Pointer(base + p.offset))
- y := *x
- if cap(y) == 0 {
- initSlice(unsafe.Pointer(x), sbase+p.scratch)
- y = *x
- }
+ v := (*[]*struct{})(unsafe.Pointer(base + p.offset))
+ y := *v
typ := p.stype.Elem()
structv := unsafe.New(typ)
bas := uintptr(structv)
y = append(y, (*struct{})(structv))
- *x = y
+ *v = y
if is_group {
err := o.unmarshalType(p.stype, is_group, bas)