Sync from internal version:
- packed repeated field support
- more useful ErrRequiredNotSet
- decoding simplification using append
R=r
CC=golang-dev
http://codereview.appspot.com/3337042
diff --git a/proto/all_test.go b/proto/all_test.go
index 03f5369..b56d9be 100644
--- a/proto/all_test.go
+++ b/proto/all_test.go
@@ -40,6 +40,8 @@
"bytes"
"fmt"
"os"
+ "reflect"
+ "strings"
"testing"
. "goprotobuf.googlecode.com/hg/proto"
@@ -427,8 +429,11 @@
func TestRequiredBit(t *testing.T) {
o := old()
pb := new(GoTest)
- if o.Marshal(pb) != ErrRequiredNotSet {
- t.Errorf("did not catch missing required fields")
+ err := o.Marshal(pb)
+ if err == nil {
+ t.Error("did not catch missing required fields")
+ } else if strings.Index(err.String(), "GoTest") < 0 {
+ t.Error("wrong error type:", err)
}
}
@@ -747,10 +752,10 @@
"ea0106"+"7361696c6f72"+ // field 29, encoding 2, string "sailor"
"ca0c03"+"626967"+ // field 201, encoding 2, string "big"
"ca0c04"+"6e6f7365"+ // field 201, encoding 2, string "nose"
- "d00c40"+ // field 202, encoding 0, value 64
- "d00c3f"+ // field 202, encoding 0, value 63
- "d80c8001"+ // field 203, encoding 0, value 128
- "d80c7f"+ // field 203, encoding 0, value 127
+ "d00c40"+ // field 202, encoding 0, value 32
+ "d00c3f"+ // field 202, encoding 0, value -32
+ "d80c8001"+ // field 203, encoding 0, value 64
+ "d80c7f"+ // field 203, encoding 0, value -64
"c00201"+ // field 40, encoding 0, value 1
"c80220"+ // field 41, encoding 0, value 32
"d00240"+ // field 42, encoding 0, value 64
@@ -776,13 +781,68 @@
}
+// All required fields set, all packed repeated fields given two values.
+func TestEncodeDecode6(t *testing.T) {
+ pb := initGoTest(false)
+ pb.F_BoolRepeatedPacked = []bool{false, true}
+ pb.F_Int32RepeatedPacked = []int32{32, 33}
+ pb.F_Int64RepeatedPacked = []int64{64, 65}
+ pb.F_Fixed32RepeatedPacked = []uint32{3232, 3333}
+ pb.F_Fixed64RepeatedPacked = []uint64{6464, 6565}
+ pb.F_Uint32RepeatedPacked = []uint32{323232, 333333}
+ pb.F_Uint64RepeatedPacked = []uint64{646464, 656565}
+ pb.F_FloatRepeatedPacked = []float32{32., 33.}
+ pb.F_DoubleRepeatedPacked = []float64{64., 65.}
+ pb.F_Sint32RepeatedPacked = []int32{32, -32}
+ pb.F_Sint64RepeatedPacked = []int64{64, -64}
+
+ overify(t, pb,
+ "0807"+ // field 1, encoding 0, value 7
+ "220d"+"0a056c6162656c120474797065"+ // field 4, encoding 2 (GoTestField)
+ "5001"+ // field 10, encoding 0, value 1
+ "5803"+ // field 11, encoding 0, value 3
+ "6006"+ // field 12, encoding 0, value 6
+ "6d20000000"+ // field 13, encoding 5, value 32
+ "714000000000000000"+ // field 14, encoding 1, value 64
+ "78a019"+ // field 15, encoding 0, value 3232
+ "8001c032"+ // field 16, encoding 0, value 6464
+ "8d0100004a45"+ // field 17, encoding 5, value 3232.0
+ "9101000000000040b940"+ // field 18, encoding 1, value 6464.0
+ "9a0106"+"737472696e67"+ // field 19, encoding 2 string "string"
+ "aa0605"+"6279746573"+ // field 101, encoding 2 string "bytes"
+ "b0063f"+ // field 102, encoding 0, 0x3f zigzag32
+ "b8067f"+ // field 103, encoding 0, 0x7f zigzag64
+ "9203020001"+ // field 50, encoding 2, 2 bytes, value 0, value 1
+ "9a03022021"+ // field 51, encoding 2, 2 bytes, value 32, value 33
+ "a203024041"+ // field 52, encoding 2, 2 bytes, value 64, value 65
+ "aa0308"+ // field 53, encoding 2, 8 bytes
+ "a00c0000050d0000"+ // value 3232, value 3333
+ "b20310"+ // field 54, encoding 2, 16 bytes
+ "4019000000000000a519000000000000"+ // value 6464, value 6565
+ "ba0306"+ // field 55, encoding 2, 6 bytes
+ "a0dd1395ac14"+ // value 323232, value 333333
+ "c20306"+ // field 56, encoding 2, 6 bytes
+ "c0ba27b58928"+ // value 646464, value 656565
+ "ca0308"+ // field 57, encoding 2, 8 bytes
+ "0000004200000442"+ // value 32.0, value 33.0
+ "d20310"+ // field 58, encoding 2, 16 bytes
+ "00000000000050400000000000405040"+ // value 64.0, value 65.0
+ "b21f02"+ // field 502, encoding 2, 2 bytes
+ "403f"+ // value 32, value -32
+ "ba1f03"+ // field 503, encoding 2, 3 bytes
+ "80017f"+ // value 64, value -64
+ "b304"+ // start group field 70 level 1
+ "ba0408"+"7265717569726564"+ // field 71, encoding 2, string "required"
+ "b404") // end group field 70 level 1
+}
+
// Test that we can encode empty bytes fields.
func TestEncodeDecodeBytes1(t *testing.T) {
pb := initGoTest(false)
// Create our bytes
pb.F_BytesRequired = []byte{}
- pb.F_BytesRepeated = [][]byte{ []byte{} }
+ pb.F_BytesRepeated = [][]byte{[]byte{}}
pb.F_BytesOptional = []byte{}
d, err := Marshal(pb)
@@ -812,10 +872,10 @@
pb := initGoTest(false)
// Create our bytes
- pb.F_BytesRepeated = [][]byte{ nil }
+ pb.F_BytesRepeated = [][]byte{nil}
d, err := Marshal(pb)
- if err != nil {
+ if err != nil {
t.Errorf(err.String())
}
@@ -1018,6 +1078,35 @@
}
}
+func encodeDecode(t *testing.T, in, out interface{}, msg string) {
+ buf, err := Marshal(in)
+ if err != nil {
+ t.Fatalf("failed marshaling %v: %v", msg, err)
+ }
+ if err := Unmarshal(buf, out); err != nil {
+ t.Fatalf("failed unmarshaling %v: %v", msg, err)
+ }
+}
+
+func TestPackedNonPackedDecoderSwitching(t *testing.T) {
+ np, p := new(NonPackedTest), new(PackedTest)
+
+ // non-packed -> packed
+ np.A = []int32{0, 1, 1, 2, 3, 5}
+ encodeDecode(t, np, p, "non-packed -> packed")
+ if !reflect.DeepEqual(np.A, p.B) {
+ t.Errorf("failed non-packed -> packed; np.A=%+v, p.B=%+v", np.A, p.B)
+ }
+
+ // packed -> non-packed
+ np.Reset()
+ p.B = []int32{3, 1, 4, 1, 5, 9}
+ encodeDecode(t, p, np, "packed -> non-packed")
+ if !reflect.DeepEqual(p.B, np.A) {
+ t.Errorf("failed packed -> non-packed; p.B=%+v, np.A=%+v", p.B, np.A)
+ }
+}
+
func TestProto1RepeatedGroup(t *testing.T) {
pb := &MessageList{
Message: []*MessageList_Message{
@@ -1060,8 +1149,10 @@
func TestRequiredFieldEnforcement(t *testing.T) {
pb := new(GoTestField)
_, err := Marshal(pb)
- if err == nil || err != ErrRequiredNotSet {
- t.Errorf("marshal: expected %q, got %q", ErrRequiredNotSet, err)
+ if err == nil {
+ t.Error("marshal: expected error, got nil")
+ } else if strings.Index(err.String(), "GoTestField") < 0 {
+ t.Errorf("marshal: bad error type: %v", err)
}
// A slightly sneaky, yet valid, proto. It encodes the same required field twice,
@@ -1069,8 +1160,10 @@
// field 1, encoding 2, value "hi"
buf := []byte("\x0A\x02hi\x0A\x02hi")
err = Unmarshal(buf, pb)
- if err == nil || err != ErrRequiredNotSet {
- t.Errorf("unmarshal: expected %q, got %q", ErrRequiredNotSet, err)
+ if err == nil {
+ t.Error("unmarshal: expected error, got nil")
+ } else if strings.Index(err.String(), "GoTestField") < 0 {
+ t.Errorf("unmarshal: bad error type: %v", err)
}
}
diff --git a/proto/decode.go b/proto/decode.go
index 1019d2a..5423895 100644
--- a/proto/decode.go
+++ b/proto/decode.go
@@ -372,11 +372,17 @@
fmt.Fprintf(os.Stderr, "no protobuf decoder for %s.%s\n", t, st.Field(fieldnum).Name)
continue
}
+ dec := p.dec
if wire != WireStartGroup && wire != p.WireType {
- err = ErrWrongType
- continue
+ if wire == WireBytes && p.packedDec != nil {
+ // a packable field
+ dec = p.packedDec
+ } else {
+ err = ErrWrongType
+ continue
+ }
}
- err = p.dec(o, p, base, sbase)
+ err = dec(o, p, base, sbase)
if err == nil && p.Required {
// Successfully decoded a required field.
if tag <= 64 {
@@ -401,7 +407,7 @@
return io.ErrUnexpectedEOF
}
if required > 0 {
- return ErrRequiredNotSet
+ return &ErrRequiredNotSet{st}
}
}
return err
@@ -415,6 +421,17 @@
sp.Cap = startSize
}
+// Make *pslice have base address base, length 0, and capacity max(startSize, n).
+func initSlicen(pslice unsafe.Pointer, base uintptr, n int) {
+ if n < startSize {
+ n = startSize
+ }
+ sp := (*reflect.SliceHeader)(pslice)
+ sp.Data = base
+ sp.Len = 0
+ sp.Cap = n
+}
+
// Individual type decoders
// For each,
// u is the decoded value,
@@ -483,30 +500,12 @@
x := (*[]uint8)(unsafe.Pointer(base + p.offset))
y := *x
- c := cap(y)
- if c == 0 {
+ if cap(y) == 0 {
initSlice(unsafe.Pointer(x), sbase+p.scratch)
y = *x
- c = cap(y)
}
- l := len(y)
- lb := len(b)
- if l+lb > c {
- // incremental growth is max(len(slice)*1.5, len(slice)+len(bytes))
- g := l * 3 / 2
- if l+lb > g {
- g = l + lb
- }
- z := make([]uint8, l, g)
- copy(z, y)
- y = z
- }
-
- y = y[0 : l+lb]
- copy(y[l:l+lb], b)
-
- *x = y
+ *x = append(y, b...)
return nil
}
@@ -519,21 +518,42 @@
x := (*[]bool)(unsafe.Pointer(base + p.offset))
y := *x
- c := cap(y)
- if c == 0 {
+ if cap(y) == 0 {
initSlice(unsafe.Pointer(x), sbase+p.scratch)
y = *x
- c = cap(y)
}
- l := len(y)
- if l >= c {
- g := l * 3 / 2
- z := make([]bool, l, g)
- copy(z, y)
- y = z
+
+ *x = append(y, 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))
+
+ nn, err := o.DecodeVarint()
+ if err != nil {
+ return err
}
- y = y[0 : l+1]
- y[l] = u != 0
+ nb := int(nn) // number of bytes of encoded bools
+
+ y := *x
+ if cap(y) == 0 {
+ // Packed fields are usually only encoded once,
+ // so this branch is almost always executed.
+ // The append in the loop below takes care of other cases.
+ initSlicen(unsafe.Pointer(x), sbase+p.scratch, nb)
+ y = *x
+ }
+
+ for i := 0; i < nb; i++ {
+ u, err := p.valDec(o)
+ if err != nil {
+ return err
+ }
+ y = append(y, u != 0)
+ }
+
*x = y
return nil
}
@@ -547,21 +567,43 @@
x := (*[]int32)(unsafe.Pointer(base + p.offset))
y := *x
- c := cap(y)
- if c == 0 {
+ if cap(y) == 0 {
initSlice(unsafe.Pointer(x), sbase+p.scratch)
y = *x
- c = cap(y)
}
- l := len(y)
- if l >= c {
- g := l * 3 / 2
- z := make([]int32, l, g)
- copy(z, y)
- y = z
+
+ *x = append(y, 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))
+
+ nn, err := o.DecodeVarint()
+ if err != nil {
+ return err
}
- y = y[0 : l+1]
- y[l] = int32(u)
+ nb := int(nn) // number of bytes of encoded int32s
+
+ y := *x
+ if cap(y) == 0 {
+ // Packed fields are usually only encoded once,
+ // so this branch is almost always executed.
+ // The append in the loop below takes care of other cases.
+ initSlicen(unsafe.Pointer(x), sbase+p.scratch, nb)
+ y = *x
+ }
+
+ fin := o.index + nb
+ for o.index < fin {
+ u, err := p.valDec(o)
+ if err != nil {
+ return err
+ }
+ y = append(y, int32(u))
+ }
+
*x = y
return nil
}
@@ -575,21 +617,43 @@
x := (*[]int64)(unsafe.Pointer(base + p.offset))
y := *x
- c := cap(y)
- if c == 0 {
+ if cap(y) == 0 {
initSlice(unsafe.Pointer(x), sbase+p.scratch)
y = *x
- c = cap(y)
}
- l := len(y)
- if l >= c {
- g := l * 3 / 2
- z := make([]int64, l, g)
- copy(z, y)
- y = z
+
+ *x = 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))
+
+ nn, err := o.DecodeVarint()
+ if err != nil {
+ return err
}
- y = y[0 : l+1]
- y[l] = int64(u)
+ nb := int(nn) // number of bytes of encoded int64s
+
+ y := *x
+ if cap(y) == 0 {
+ // Packed fields are usually only encoded once,
+ // so this branch is almost always executed.
+ // The append in the loop below takes care of other cases.
+ initSlicen(unsafe.Pointer(x), sbase+p.scratch, nb)
+ y = *x
+ }
+
+ fin := o.index + nb
+ for o.index < fin {
+ u, err := p.valDec(o)
+ if err != nil {
+ return err
+ }
+ y = append(y, int64(u))
+ }
+
*x = y
return nil
}
@@ -603,22 +667,12 @@
x := (*[]string)(unsafe.Pointer(base + p.offset))
y := *x
- c := cap(y)
- if c == 0 {
+ if cap(y) == 0 {
initSlice(unsafe.Pointer(x), sbase+p.scratch)
y = *x
- c = cap(y)
}
- l := len(y)
- if l >= c {
- g := l * 3 / 2
- z := make([]string, l, g)
- copy(z, y)
- y = z
- }
- y = y[0 : l+1]
- y[l] = s
- *x = y
+
+ *x = append(y, s)
return nil
}
@@ -631,22 +685,12 @@
x := (*[][]byte)(unsafe.Pointer(base + p.offset))
y := *x
- c := cap(y)
- if c == 0 {
+ if cap(y) == 0 {
initSlice(unsafe.Pointer(x), sbase+p.scratch)
y = *x
- c = cap(y)
}
- l := len(y)
- if l >= c {
- g := l * 3 / 2
- z := make([][]byte, l, g)
- copy(z, y)
- y = z
- }
- y = y[0 : l+1]
- y[l] = b
- *x = y
+
+ *x = append(y, b)
return nil
}
@@ -709,28 +753,16 @@
x := (*[]*struct{})(unsafe.Pointer(base + p.offset))
y := *x
- c := cap(y)
- if c == 0 {
+ if cap(y) == 0 {
initSlice(unsafe.Pointer(x), sbase+p.scratch)
y = *x
- c = cap(y)
}
- l := len(y)
- if l >= c {
- // Create a new slice with 1.5X the capacity.
- g := l * 3 / 2
- z := make([]*struct{}, l, g)
- copy(z, y)
- y = z
- }
- y = y[0 : l+1]
- *x = y
-
typ := p.stype.Elem().(*reflect.StructType)
structv := unsafe.New(typ)
bas := uintptr(structv)
- y[l] = (*struct{})(structv)
+ y = append(y, (*struct{})(structv))
+ *x = y
if is_group {
err := o.unmarshalType(p.stype, is_group, bas)
@@ -743,7 +775,7 @@
}
// If the object can unmarshal itself, let it.
- iv := unsafe.Unreflect(p.stype, unsafe.Pointer(&y[l]))
+ iv := unsafe.Unreflect(p.stype, unsafe.Pointer(&y[len(y)-1]))
if u, ok := iv.(Unmarshaler); ok {
return u.Unmarshal(raw)
}
diff --git a/proto/encode.go b/proto/encode.go
index 37c572c..13fd835 100644
--- a/proto/encode.go
+++ b/proto/encode.go
@@ -48,7 +48,13 @@
// all been initialized. It is also the error returned if Unmarshal is
// called with an encoded protocol buffer that does not include all the
// required fields.
-var ErrRequiredNotSet = os.NewError("required fields not set")
+type ErrRequiredNotSet struct {
+ t reflect.Type
+}
+
+func (e *ErrRequiredNotSet) String() string {
+ return "required fields not set in " + e.t.String()
+}
// ErrRepeatedHasNil is the error returned if Marshal is called with
// a protocol buffer struct with a repeated field containing a nil element.
@@ -361,6 +367,24 @@
return nil
}
+// Encode a slice of bools ([]bool) in packed format.
+func (o *Buffer) enc_slice_packed_bool(p *Properties, base uintptr) os.Error {
+ s := *(*[]uint8)(unsafe.Pointer(base + p.offset))
+ l := len(s)
+ if l == 0 {
+ return ErrNil
+ }
+ o.buf = append(o.buf, p.tagcode...)
+ o.EncodeVarint(uint64(l)) // each bool takes exactly one byte
+ for _, x := range s {
+ if x != 0 {
+ x = 1
+ }
+ p.valEnc(o, uint64(x))
+ }
+ return nil
+}
+
// Encode a slice of bytes ([]byte).
func (o *Buffer) enc_slice_byte(p *Properties, base uintptr) os.Error {
s := *(*[]uint8)(unsafe.Pointer(base + p.offset))
@@ -387,6 +411,25 @@
return nil
}
+// Encode a slice of int32s ([]int32) in packed format.
+func (o *Buffer) enc_slice_packed_int32(p *Properties, base uintptr) os.Error {
+ s := *(*[]uint32)(unsafe.Pointer(base + p.offset))
+ l := len(s)
+ if l == 0 {
+ return ErrNil
+ }
+ // TODO: Reuse a Buffer.
+ buf := NewBuffer(nil)
+ for i := 0; i < l; i++ {
+ p.valEnc(buf, uint64(s[i]))
+ }
+
+ o.buf = append(o.buf, p.tagcode...)
+ o.EncodeVarint(uint64(len(buf.buf)))
+ o.buf = append(o.buf, buf.buf...)
+ return nil
+}
+
// Encode a slice of int64s ([]int64).
func (o *Buffer) enc_slice_int64(p *Properties, base uintptr) os.Error {
s := *(*[]uint64)(unsafe.Pointer(base + p.offset))
@@ -402,6 +445,25 @@
return nil
}
+// Encode a slice of int64s ([]int64) in packed format.
+func (o *Buffer) enc_slice_packed_int64(p *Properties, base uintptr) os.Error {
+ s := *(*[]uint64)(unsafe.Pointer(base + p.offset))
+ l := len(s)
+ if l == 0 {
+ return ErrNil
+ }
+ // TODO: Reuse a Buffer.
+ buf := NewBuffer(nil)
+ for i := 0; i < l; i++ {
+ p.valEnc(buf, s[i])
+ }
+
+ o.buf = append(o.buf, p.tagcode...)
+ o.EncodeVarint(uint64(len(buf.buf)))
+ o.buf = append(o.buf, buf.buf...)
+ return nil
+}
+
// Encode a slice of slice of bytes ([][]byte).
func (o *Buffer) enc_slice_slice_byte(p *Properties, base uintptr) os.Error {
ss := *(*[][]uint8)(unsafe.Pointer(base + p.offset))
@@ -535,7 +597,7 @@
}
// See if we encoded all required fields.
if required > 0 {
- return ErrRequiredNotSet
+ return &ErrRequiredNotSet{t}
}
return nil
diff --git a/proto/properties.go b/proto/properties.go
index b6e5526..4d15b28 100644
--- a/proto/properties.go
+++ b/proto/properties.go
@@ -95,6 +95,7 @@
Required bool
Optional bool
Repeated bool
+ Packed bool // relevant for repeated primitives only
Enum string // set for enum types only
Default string // default value
def_uint64 uint64
@@ -111,6 +112,9 @@
scratch uintptr
sizeof int // calculations of scratch space
alignof int
+
+ // If this is a packable field, this will be the decoder for the packed version of the field.
+ packedDec decoder
}
// String formats the properties in the "PB(...)" struct tag style.
@@ -127,6 +131,9 @@
if p.Repeated {
s += ",rep"
}
+ if p.Packed {
+ s += ",packed"
+ }
if p.OrigName != p.Name {
s += ",name=" + p.OrigName
}
@@ -193,6 +200,8 @@
p.Optional = true
case f == "rep":
p.Repeated = true
+ case f == "packed":
+ p.Packed = true
case len(f) >= 5 && f[0:5] == "name=":
p.OrigName = f[5:len(f)]
case len(f) >= 5 && f[0:5] == "enum=":
@@ -291,20 +300,35 @@
fmt.Fprintf(os.Stderr, "proto: no slice oenc for %T = []%T\n", t1, t2)
break
case *reflect.BoolType:
- p.enc = (*Buffer).enc_slice_bool
+ if p.Packed {
+ p.enc = (*Buffer).enc_slice_packed_bool
+ } else {
+ p.enc = (*Buffer).enc_slice_bool
+ }
p.dec = (*Buffer).dec_slice_bool
+ p.packedDec = (*Buffer).dec_slice_packed_bool
p.alignof = unsafe.Alignof(vbool)
p.sizeof = startSize * unsafe.Sizeof(vbool)
case *reflect.IntType, *reflect.UintType:
switch t2.Bits() {
case 32:
- p.enc = (*Buffer).enc_slice_int32
+ if p.Packed {
+ p.enc = (*Buffer).enc_slice_packed_int32
+ } else {
+ p.enc = (*Buffer).enc_slice_int32
+ }
p.dec = (*Buffer).dec_slice_int32
+ p.packedDec = (*Buffer).dec_slice_packed_int32
p.alignof = unsafe.Alignof(vint32)
p.sizeof = startSize * unsafe.Sizeof(vint32)
case 64:
- p.enc = (*Buffer).enc_slice_int64
+ if p.Packed {
+ p.enc = (*Buffer).enc_slice_packed_int64
+ } else {
+ p.enc = (*Buffer).enc_slice_int64
+ }
p.dec = (*Buffer).dec_slice_int64
+ p.packedDec = (*Buffer).dec_slice_packed_int64
p.alignof = unsafe.Alignof(vint64)
p.sizeof = startSize * unsafe.Sizeof(vint64)
case 8:
@@ -320,13 +344,25 @@
case *reflect.FloatType:
switch t2.Bits() {
case 32:
- p.enc = (*Buffer).enc_slice_int32 // can just treat them as bits
+ // can just treat them as bits
+ if p.Packed {
+ p.enc = (*Buffer).enc_slice_packed_int32
+ } else {
+ p.enc = (*Buffer).enc_slice_int32
+ }
p.dec = (*Buffer).dec_slice_int32
+ p.packedDec = (*Buffer).dec_slice_packed_int32
p.alignof = unsafe.Alignof(vfloat32)
p.sizeof = startSize * unsafe.Sizeof(vfloat32)
case 64:
- p.enc = (*Buffer).enc_slice_int64 // can just treat them as bits
+ // can just treat them as bits
+ if p.Packed {
+ p.enc = (*Buffer).enc_slice_packed_int64
+ } else {
+ p.enc = (*Buffer).enc_slice_int64
+ }
p.dec = (*Buffer).dec_slice_int64
+ p.packedDec = (*Buffer).dec_slice_packed_int64
p.alignof = unsafe.Alignof(vfloat64)
p.sizeof = startSize * unsafe.Sizeof(vfloat64)
default:
@@ -368,7 +404,11 @@
}
// precalculate tag code
- x := p.Tag<<3 | p.WireType
+ wire := p.WireType
+ if p.Packed {
+ wire = WireBytes
+ }
+ x := p.Tag<<3 | wire
i := 0
for i = 0; x > 127; i++ {
p.tagbuf[i] = 0x80 | uint8(x&0x7F)
diff --git a/proto/testdata/test.pb.go b/proto/testdata/test.pb.go
index 8fe742f..a1fe1de 100644
--- a/proto/testdata/test.pb.go
+++ b/proto/testdata/test.pb.go
@@ -173,6 +173,17 @@
F_BytesDefaulted []byte "PB(bytes,401,opt,name=F_Bytes_defaulted,def=Bignose)"
F_Sint32Defaulted *int32 "PB(zigzag32,402,opt,name=F_Sint32_defaulted,def=-32)"
F_Sint64Defaulted *int64 "PB(zigzag64,403,opt,name=F_Sint64_defaulted,def=-64)"
+ F_BoolRepeatedPacked []bool "PB(varint,50,rep,packed,name=F_Bool_repeated_packed)"
+ F_Int32RepeatedPacked []int32 "PB(varint,51,rep,packed,name=F_Int32_repeated_packed)"
+ F_Int64RepeatedPacked []int64 "PB(varint,52,rep,packed,name=F_Int64_repeated_packed)"
+ F_Fixed32RepeatedPacked []uint32 "PB(fixed32,53,rep,packed,name=F_Fixed32_repeated_packed)"
+ F_Fixed64RepeatedPacked []uint64 "PB(fixed64,54,rep,packed,name=F_Fixed64_repeated_packed)"
+ F_Uint32RepeatedPacked []uint32 "PB(varint,55,rep,packed,name=F_Uint32_repeated_packed)"
+ F_Uint64RepeatedPacked []uint64 "PB(varint,56,rep,packed,name=F_Uint64_repeated_packed)"
+ F_FloatRepeatedPacked []float32 "PB(fixed32,57,rep,packed,name=F_Float_repeated_packed)"
+ F_DoubleRepeatedPacked []float64 "PB(fixed64,58,rep,packed,name=F_Double_repeated_packed)"
+ F_Sint32RepeatedPacked []int32 "PB(zigzag32,502,rep,packed,name=F_Sint32_repeated_packed)"
+ F_Sint64RepeatedPacked []int64 "PB(zigzag64,503,rep,packed,name=F_Sint64_repeated_packed)"
Requiredgroup *GoTest_RequiredGroup "PB(group,70,req,name=requiredgroup)"
Repeatedgroup []*GoTest_RepeatedGroup "PB(group,80,rep,name=repeatedgroup)"
Optionalgroup *GoTest_OptionalGroup "PB(group,90,opt,name=optionalgroup)"
@@ -240,6 +251,22 @@
*this = GoSkipTest_SkipGroup{}
}
+type NonPackedTest struct {
+ A []int32 "PB(varint,1,rep,name=a)"
+ XXX_unrecognized []byte
+}
+func (this *NonPackedTest) Reset() {
+ *this = NonPackedTest{}
+}
+
+type PackedTest struct {
+ B []int32 "PB(varint,1,rep,packed,name=b)"
+ XXX_unrecognized []byte
+}
+func (this *PackedTest) Reset() {
+ *this = PackedTest{}
+}
+
type InnerMessage struct {
Host *string "PB(bytes,1,req,name=host)"
Port *int32 "PB(varint,2,opt,name=port,def=4000)"
diff --git a/proto/testdata/test.proto b/proto/testdata/test.proto
index 474ec39..58582d6 100644
--- a/proto/testdata/test.proto
+++ b/proto/testdata/test.proto
@@ -142,6 +142,19 @@
optional sint32 F_Sint32_defaulted = 402 [default = -32];
optional sint64 F_Sint64_defaulted = 403 [default = -64];
+ // Packed repeated fields (no string or bytes).
+ repeated bool F_Bool_repeated_packed = 50 [packed=true];
+ repeated int32 F_Int32_repeated_packed = 51 [packed=true];
+ repeated int64 F_Int64_repeated_packed = 52 [packed=true];
+ repeated fixed32 F_Fixed32_repeated_packed = 53 [packed=true];
+ repeated fixed64 F_Fixed64_repeated_packed = 54 [packed=true];
+ repeated uint32 F_Uint32_repeated_packed = 55 [packed=true];
+ repeated uint64 F_Uint64_repeated_packed = 56 [packed=true];
+ repeated float F_Float_repeated_packed = 57 [packed=true];
+ repeated double F_Double_repeated_packed = 58 [packed=true];
+ repeated sint32 F_Sint32_repeated_packed = 502 [packed=true];
+ repeated sint64 F_Sint64_repeated_packed = 503 [packed=true];
+
// Required, repeated, and optional groups.
required group RequiredGroup = 70 {
required string RequiredField = 71;
@@ -170,6 +183,16 @@
}
}
+// For testing packed/non-packed decoder switching.
+// A serialized instance of one should be deserializable as the other.
+message NonPackedTest {
+ repeated int32 a = 1;
+}
+
+message PackedTest {
+ repeated int32 b = 1 [packed=true];
+}
+
// Smaller tests for ASCII formatting.
message InnerMessage {