aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/encoding/gob
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/encoding/gob')
-rw-r--r--libgo/go/encoding/gob/codec_test.go4
-rw-r--r--libgo/go/encoding/gob/dec_helpers.go9
-rw-r--r--libgo/go/encoding/gob/decgen.go9
-rw-r--r--libgo/go/encoding/gob/decode.go64
-rw-r--r--libgo/go/encoding/gob/doc.go29
-rw-r--r--libgo/go/encoding/gob/encode.go19
-rw-r--r--libgo/go/encoding/gob/encoder_test.go2
-rw-r--r--libgo/go/encoding/gob/timing_test.go45
-rw-r--r--libgo/go/encoding/gob/type.go2
9 files changed, 127 insertions, 56 deletions
diff --git a/libgo/go/encoding/gob/codec_test.go b/libgo/go/encoding/gob/codec_test.go
index c2583bf..8efcdc7 100644
--- a/libgo/go/encoding/gob/codec_test.go
+++ b/libgo/go/encoding/gob/codec_test.go
@@ -88,7 +88,6 @@ func verifyInt(i int64, t *testing.T) {
encState := newEncoderState(b)
encState.encodeInt(i)
decState := newDecodeState(newDecBuffer(b.Bytes()))
- decState.buf = make([]byte, 8)
j := decState.decodeInt()
if i != j {
t.Errorf("Encode/Decode: sent %#x received %#x", uint64(i), uint64(j))
@@ -127,7 +126,6 @@ var bytesResult = []byte{0x07, 0x05, 'h', 'e', 'l', 'l', 'o'}
func newDecodeState(buf *decBuffer) *decoderState {
d := new(decoderState)
d.b = buf
- d.buf = make([]byte, uint64Size)
return d
}
@@ -1488,7 +1486,7 @@ func TestErrorInvalidTypeId(t *testing.T) {
var foo struct{}
err := d.Decode(&foo)
if err != errBadType {
- t.Fatal("decode: expected %s, got %s", errBadType, err)
+ t.Fatalf("decode: expected %s, got %s", errBadType, err)
}
}
}
diff --git a/libgo/go/encoding/gob/dec_helpers.go b/libgo/go/encoding/gob/dec_helpers.go
index a1b6766..3aa038d 100644
--- a/libgo/go/encoding/gob/dec_helpers.go
+++ b/libgo/go/encoding/gob/dec_helpers.go
@@ -327,11 +327,12 @@ func decStringSlice(state *decoderState, v reflect.Value, length int, ovfl error
errorf("string data too long for buffer: %d", n)
}
// Read the data.
- data := make([]byte, n)
- if _, err := state.b.Read(data); err != nil {
- errorf("error decoding string: %s", err)
+ data := state.b.Bytes()
+ if len(data) < n {
+ errorf("invalid string length %d: exceeds input size %d", n, len(data))
}
- slice[i] = string(data)
+ slice[i] = string(data[:n])
+ state.b.Drop(n)
}
return true
}
diff --git a/libgo/go/encoding/gob/decgen.go b/libgo/go/encoding/gob/decgen.go
index da41a89..ef73f2d 100644
--- a/libgo/go/encoding/gob/decgen.go
+++ b/libgo/go/encoding/gob/decgen.go
@@ -112,11 +112,12 @@ var types = []Type{
errorf("string data too long for buffer: %d", n)
}
// Read the data.
- data := make([]byte, n)
- if _, err := state.b.Read(data); err != nil {
- errorf("error decoding string: %s", err)
+ data := state.b.Bytes()
+ if len(data) < n {
+ errorf("invalid string length %d: exceeds input size %d", n, len(data))
}
- slice[i] = string(data)`,
+ slice[i] = string(data[:n])
+ state.b.Drop(n)`,
},
{
"uint",
diff --git a/libgo/go/encoding/gob/decode.go b/libgo/go/encoding/gob/decode.go
index e913f15..3b0dca8 100644
--- a/libgo/go/encoding/gob/decode.go
+++ b/libgo/go/encoding/gob/decode.go
@@ -29,8 +29,7 @@ type decoderState struct {
// The buffer is stored with an extra indirection because it may be replaced
// if we load a type during decode (when reading an interface value).
b *decBuffer
- fieldnum int // the last field number read.
- buf []byte
+ fieldnum int // the last field number read.
next *decoderState // for free list
}
@@ -97,7 +96,6 @@ func (dec *Decoder) newDecoderState(buf *decBuffer) *decoderState {
if d == nil {
d = new(decoderState)
d.dec = dec
- d.buf = make([]byte, uint64Size)
} else {
dec.freeList = d.next
}
@@ -160,15 +158,16 @@ func (state *decoderState) decodeUint() (x uint64) {
if n > uint64Size {
error_(errBadUint)
}
- width, err := state.b.Read(state.buf[0:n])
- if err != nil {
- error_(err)
+ buf := state.b.Bytes()
+ if len(buf) < n {
+ errorf("invalid uint data length %d: exceeds input size %d", n, len(buf))
}
// Don't need to check error; it's safe to loop regardless.
// Could check that the high byte is zero but it's not worth it.
- for _, b := range state.buf[0:width] {
+ for _, b := range buf[0:n] {
x = x<<8 | uint64(b)
}
+ state.b.Drop(n)
return x
}
@@ -397,11 +396,13 @@ func decString(i *decInstr, state *decoderState, value reflect.Value) {
errorf("bad %s slice length: %d", value.Type(), n)
}
// Read the data.
- data := make([]byte, n)
- if _, err := state.b.Read(data); err != nil {
- errorf("error decoding string: %s", err)
+ data := state.b.Bytes()
+ if len(data) < n {
+ errorf("invalid string length %d: exceeds input size %d", n, len(data))
}
- value.SetString(string(data))
+ s := string(data[:n])
+ state.b.Drop(n)
+ value.SetString(s)
}
// ignoreUint8Array skips over the data for a byte slice value with no destination.
@@ -410,8 +411,11 @@ func ignoreUint8Array(i *decInstr, state *decoderState, value reflect.Value) {
if !ok {
errorf("slice length too large")
}
- b := make([]byte, n)
- state.b.Read(b)
+ bn := state.b.Len()
+ if bn < n {
+ errorf("invalid slice length %d: exceeds input size %d", n, bn)
+ }
+ state.b.Drop(n)
}
// Execution engine
@@ -634,15 +638,15 @@ func (dec *Decoder) ignoreSlice(state *decoderState, elemOp decOp) {
func (dec *Decoder) decodeInterface(ityp reflect.Type, state *decoderState, value reflect.Value) {
// Read the name of the concrete type.
nr := state.decodeUint()
- if nr < 0 || nr > 1<<31 { // zero is permissible for anonymous types
+ if nr > 1<<31 { // zero is permissible for anonymous types
errorf("invalid type name length %d", nr)
}
if nr > uint64(state.b.Len()) {
errorf("invalid type name length %d: exceeds input size", nr)
}
- b := make([]byte, nr)
- state.b.Read(b)
- name := string(b)
+ n := int(nr)
+ name := string(state.b.Bytes()[:n])
+ state.b.Drop(n)
// Allocate the destination interface value.
if name == "" {
// Copy the nil interface value to the target.
@@ -689,11 +693,11 @@ func (dec *Decoder) ignoreInterface(state *decoderState) {
if !ok {
errorf("bad interface encoding: name too large for buffer")
}
- b := make([]byte, n)
- _, err := state.b.Read(b)
- if err != nil {
- error_(err)
+ bn := state.b.Len()
+ if bn < n {
+ errorf("invalid interface value length %d: exceeds input size %d", n, bn)
}
+ state.b.Drop(n)
id := dec.decodeTypeSequence(true)
if id < 0 {
error_(dec.err)
@@ -714,11 +718,13 @@ func (dec *Decoder) decodeGobDecoder(ut *userTypeInfo, state *decoderState, valu
if !ok {
errorf("GobDecoder: length too large for buffer")
}
- b := make([]byte, n)
- _, err := state.b.Read(b)
- if err != nil {
- error_(err)
+ b := state.b.Bytes()
+ if len(b) < n {
+ errorf("GobDecoder: invalid data length %d: exceeds input size %d", n, len(b))
}
+ b = b[:n]
+ state.b.Drop(n)
+ var err error
// We know it's one of these.
switch ut.externalDec {
case xGob:
@@ -740,11 +746,11 @@ func (dec *Decoder) ignoreGobDecoder(state *decoderState) {
if !ok {
errorf("GobDecoder: length too large for buffer")
}
- b := make([]byte, n)
- _, err := state.b.Read(b)
- if err != nil {
- error_(err)
+ bn := state.b.Len()
+ if bn < n {
+ errorf("GobDecoder: invalid data length %d: exceeds input size %d", n, bn)
}
+ state.b.Drop(n)
}
// Index by Go types.
diff --git a/libgo/go/encoding/gob/doc.go b/libgo/go/encoding/gob/doc.go
index 4d3d007..cf878f4 100644
--- a/libgo/go/encoding/gob/doc.go
+++ b/libgo/go/encoding/gob/doc.go
@@ -82,6 +82,12 @@ slice has capacity the slice will be extended in place; if not, a new array is
allocated. Regardless, the length of the resulting slice reports the number of
elements decoded.
+In general, if allocation is required, the decoder will allocate memory. If not,
+it will update the destination variables with values read from the stream. It does
+not initialize them first, so if the destination is a compound value such as a
+map, struct, or slice, the decoded values will be merged elementwise into the
+existing variables.
+
Functions and channels will not be sent in a gob. Attempting to encode such a value
at the top level will fail. A struct field of chan or func type is treated exactly
like an unexported field and is ignored.
@@ -141,18 +147,21 @@ pairs. Empty but non-nil maps are sent, so if the receiver has not allocated
one already, one will always be allocated on receipt unless the transmitted map
is nil and not at the top level.
+In slices and arrays, as well as maps, all elements, even zero-valued elements,
+are transmitted, even if all the elements are zero.
+
Structs are sent as a sequence of (field number, field value) pairs. The field
value is sent using the standard gob encoding for its type, recursively. If a
-field has the zero value for its type, it is omitted from the transmission. The
-field number is defined by the type of the encoded struct: the first field of the
-encoded type is field 0, the second is field 1, etc. When encoding a value, the
-field numbers are delta encoded for efficiency and the fields are always sent in
-order of increasing field number; the deltas are therefore unsigned. The
-initialization for the delta encoding sets the field number to -1, so an unsigned
-integer field 0 with value 7 is transmitted as unsigned delta = 1, unsigned value
-= 7 or (01 07). Finally, after all the fields have been sent a terminating mark
-denotes the end of the struct. That mark is a delta=0 value, which has
-representation (00).
+field has the zero value for its type (except for arrays; see above), it is omitted
+from the transmission. The field number is defined by the type of the encoded
+struct: the first field of the encoded type is field 0, the second is field 1,
+etc. When encoding a value, the field numbers are delta encoded for efficiency
+and the fields are always sent in order of increasing field number; the deltas are
+therefore unsigned. The initialization for the delta encoding sets the field
+number to -1, so an unsigned integer field 0 with value 7 is transmitted as unsigned
+delta = 1, unsigned value = 7 or (01 07). Finally, after all the fields have been
+sent a terminating mark denotes the end of the struct. That mark is a delta=0
+value, which has representation (00).
Interface types are not checked for compatibility; all interface types are
treated, for transmission, as members of a single "interface" type, analogous to
diff --git a/libgo/go/encoding/gob/encode.go b/libgo/go/encoding/gob/encode.go
index f66279f..96052ef 100644
--- a/libgo/go/encoding/gob/encode.go
+++ b/libgo/go/encoding/gob/encode.go
@@ -10,6 +10,7 @@ import (
"encoding"
"math"
"reflect"
+ "sync"
)
const uint64Size = 8
@@ -36,6 +37,14 @@ type encBuffer struct {
scratch [64]byte
}
+var encBufferPool = sync.Pool{
+ New: func() interface{} {
+ e := new(encBuffer)
+ e.data = e.scratch[0:0]
+ return e
+ },
+}
+
func (e *encBuffer) WriteByte(c byte) {
e.data = append(e.data, c)
}
@@ -58,7 +67,11 @@ func (e *encBuffer) Bytes() []byte {
}
func (e *encBuffer) Reset() {
- e.data = e.data[0:0]
+ if len(e.data) >= tooBig {
+ e.data = e.scratch[0:0]
+ } else {
+ e.data = e.data[0:0]
+ }
}
func (enc *Encoder) newEncoderState(b *encBuffer) *encoderState {
@@ -407,7 +420,7 @@ func (enc *Encoder) encodeInterface(b *encBuffer, iv reflect.Value) {
// Encode the value into a new buffer. Any nested type definitions
// should be written to b, before the encoded value.
enc.pushWriter(b)
- data := new(encBuffer)
+ data := encBufferPool.Get().(*encBuffer)
data.Write(spaceForLength)
enc.encode(data, elem, ut)
if enc.err != nil {
@@ -415,6 +428,8 @@ func (enc *Encoder) encodeInterface(b *encBuffer, iv reflect.Value) {
}
enc.popWriter()
enc.writeMessage(b, data)
+ data.Reset()
+ encBufferPool.Put(data)
if enc.err != nil {
error_(enc.err)
}
diff --git a/libgo/go/encoding/gob/encoder_test.go b/libgo/go/encoding/gob/encoder_test.go
index dc65734..570d796 100644
--- a/libgo/go/encoding/gob/encoder_test.go
+++ b/libgo/go/encoding/gob/encoder_test.go
@@ -978,7 +978,7 @@ var badDataTests = []badDataTest{
{"0f1000fb285d003316020735ff023a65c5", "interface encoding", nil},
{"03fffb0616fffc00f902ff02ff03bf005d02885802a311a8120228022c028ee7", "GobDecoder", nil},
// Issue 10491.
- {"10fe010f020102fe01100001fe010e000016fe010d030102fe010e00010101015801fe01100000000bfe011000f85555555555555555", "length exceeds input size", nil},
+ {"10fe010f020102fe01100001fe010e000016fe010d030102fe010e00010101015801fe01100000000bfe011000f85555555555555555", "exceeds input size", nil},
}
// TestBadData tests that various problems caused by malformed input
diff --git a/libgo/go/encoding/gob/timing_test.go b/libgo/go/encoding/gob/timing_test.go
index 940e5ad..424b7e6 100644
--- a/libgo/go/encoding/gob/timing_test.go
+++ b/libgo/go/encoding/gob/timing_test.go
@@ -127,8 +127,8 @@ func TestCountDecodeMallocs(t *testing.T) {
t.Fatal("decode:", err)
}
})
- if allocs != 4 {
- t.Fatalf("mallocs per decode of type Bench: %v; wanted 4\n", allocs)
+ if allocs != 3 {
+ t.Fatalf("mallocs per decode of type Bench: %v; wanted 3\n", allocs)
}
}
@@ -200,6 +200,23 @@ func BenchmarkEncodeStringSlice(b *testing.B) {
}
}
+func BenchmarkEncodeInterfaceSlice(b *testing.B) {
+ var buf bytes.Buffer
+ enc := NewEncoder(&buf)
+ a := make([]interface{}, 1000)
+ for i := range a {
+ a[i] = "now is the time"
+ }
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ buf.Reset()
+ err := enc.Encode(a)
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
// benchmarkBuf is a read buffer we can reset
type benchmarkBuf struct {
offset int
@@ -323,3 +340,27 @@ func BenchmarkDecodeStringSlice(b *testing.B) {
}
}
}
+
+func BenchmarkDecodeInterfaceSlice(b *testing.B) {
+ var buf bytes.Buffer
+ enc := NewEncoder(&buf)
+ a := make([]interface{}, 1000)
+ for i := range a {
+ a[i] = "now is the time"
+ }
+ err := enc.Encode(a)
+ if err != nil {
+ b.Fatal(err)
+ }
+ x := make([]interface{}, 1000)
+ bbuf := benchmarkBuf{data: buf.Bytes()}
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ bbuf.reset()
+ dec := NewDecoder(&bbuf)
+ err := dec.Decode(&x)
+ if err != nil {
+ b.Fatal(i, err)
+ }
+ }
+}
diff --git a/libgo/go/encoding/gob/type.go b/libgo/go/encoding/gob/type.go
index a49b71a..cf5cec0 100644
--- a/libgo/go/encoding/gob/type.go
+++ b/libgo/go/encoding/gob/type.go
@@ -787,7 +787,7 @@ func mustGetTypeInfo(rt reflect.Type) *typeInfo {
// contain things such as private fields, channels, and functions,
// which are not usually transmissible in gob streams.
//
-// Note: Since gobs can be stored permanently, It is good design
+// Note: Since gobs can be stored permanently, it is good design
// to guarantee the encoding used by a GobEncoder is stable as the
// software evolves. For instance, it might make sense for GobEncode
// to include a version number in the encoding.