Commit 3050a0a7 authored by Rob Pike's avatar Rob Pike

encoding/gob: remove unsafe, use reflection.

This removes a major unsafe thorn in our side, a perennial obstacle
to clean garbage collection.
Not coincidentally: In cleaning this up, several bugs were found,
including code that reached inside by-value interfaces to create
pointers for pointer-receiver methods. Unsafe code is just as
advertised.

Performance of course suffers, but not too badly. The Pipe number
is more indicative, since it's doing I/O that simulates a network
connection. Plus these are end-to-end, so each end suffers
only half of this pain.

The edit is pretty much a line-by-line conversion, with a few
simplifications and a couple of new tests. There may be more
performance to gain.

BenchmarkEndToEndByteBuffer     2493          3033          +21.66%
BenchmarkEndToEndPipe           4953          5597          +13.00%

Fixes #5159.

LGTM=rsc
R=rsc
CC=golang-codereviews, khr
https://golang.org/cl/102680045
parent 3e692bec
This diff is collapsed.
......@@ -306,7 +306,7 @@ func (deb *debugger) common() CommonType {
// Id typeId
id = deb.typeId()
default:
errorf("corrupted CommonType")
errorf("corrupted CommonType, delta is %d fieldNum is %d", delta, fieldNum)
}
}
return CommonType{name, id}
......@@ -598,11 +598,11 @@ func (deb *debugger) printBuiltin(indent tab, id typeId) {
fmt.Fprintf(os.Stderr, "%s%d\n", indent, x)
case tFloat:
x := deb.uint64()
fmt.Fprintf(os.Stderr, "%s%g\n", indent, floatFromBits(x))
fmt.Fprintf(os.Stderr, "%s%g\n", indent, float64FromBits(x))
case tComplex:
r := deb.uint64()
i := deb.uint64()
fmt.Fprintf(os.Stderr, "%s%g+%gi\n", indent, floatFromBits(r), floatFromBits(i))
fmt.Fprintf(os.Stderr, "%s%g+%gi\n", indent, float64FromBits(r), float64FromBits(i))
case tBytes:
x := int(deb.uint64())
b := make([]byte, x)
......
This diff is collapsed.
This diff is collapsed.
......@@ -13,6 +13,52 @@ import (
"testing"
)
// Test basic operations in a safe manner.
func TestBasicEncoderDecoder(t *testing.T) {
var values = []interface{}{
true,
int(123),
int8(123),
int16(-12345),
int32(123456),
int64(-1234567),
uint(123),
uint8(123),
uint16(12345),
uint32(123456),
uint64(1234567),
uintptr(12345678),
float32(1.2345),
float64(1.2345678),
complex64(1.2345 + 2.3456i),
complex128(1.2345678 + 2.3456789i),
[]byte("hello"),
string("hello"),
}
for _, value := range values {
b := new(bytes.Buffer)
enc := NewEncoder(b)
err := enc.Encode(value)
if err != nil {
t.Error("encoder fail:", err)
}
dec := NewDecoder(b)
result := reflect.New(reflect.TypeOf(value))
err = dec.Decode(result.Interface())
if err != nil {
t.Fatalf("error decoding %T: %v:", reflect.TypeOf(value), err)
}
if !reflect.DeepEqual(value, result.Elem().Interface()) {
t.Fatalf("%T: expected %v got %v", value, value, result.Elem().Interface())
}
}
}
type ET0 struct {
A int
B string
}
type ET2 struct {
X string
}
......@@ -40,14 +86,40 @@ type ET4 struct {
func TestEncoderDecoder(t *testing.T) {
b := new(bytes.Buffer)
enc := NewEncoder(b)
et0 := new(ET0)
et0.A = 7
et0.B = "gobs of fun"
err := enc.Encode(et0)
if err != nil {
t.Error("encoder fail:", err)
}
//fmt.Printf("% x %q\n", b, b)
//Debug(b)
dec := NewDecoder(b)
newEt0 := new(ET0)
err = dec.Decode(newEt0)
if err != nil {
t.Fatal("error decoding ET0:", err)
}
if !reflect.DeepEqual(et0, newEt0) {
t.Fatalf("invalid data for et0: expected %+v; got %+v", *et0, *newEt0)
}
if b.Len() != 0 {
t.Error("not at eof;", b.Len(), "bytes left")
}
// t.FailNow()
b = new(bytes.Buffer)
enc = NewEncoder(b)
et1 := new(ET1)
et1.A = 7
et1.Et2 = new(ET2)
err := enc.Encode(et1)
err = enc.Encode(et1)
if err != nil {
t.Error("encoder fail:", err)
}
dec := NewDecoder(b)
dec = NewDecoder(b)
newEt1 := new(ET1)
err = dec.Decode(newEt1)
if err != nil {
......
......@@ -279,7 +279,7 @@ func TestGobEncoderValueField(t *testing.T) {
b := new(bytes.Buffer)
// First a field that's a structure.
enc := NewEncoder(b)
err := enc.Encode(GobTestValueEncDec{17, StringStruct{"HIJKL"}})
err := enc.Encode(&GobTestValueEncDec{17, StringStruct{"HIJKL"}})
if err != nil {
t.Fatal("encode error:", err)
}
......@@ -326,7 +326,7 @@ func TestGobEncoderArrayField(t *testing.T) {
for i := range a.A.a {
a.A.a[i] = byte(i)
}
err := enc.Encode(a)
err := enc.Encode(&a)
if err != nil {
t.Fatal("encode error:", err)
}
......@@ -589,7 +589,8 @@ func TestGobEncoderStructSingleton(t *testing.T) {
func TestGobEncoderNonStructSingleton(t *testing.T) {
b := new(bytes.Buffer)
enc := NewEncoder(b)
err := enc.Encode(Gobber(1234))
var g Gobber = 1234
err := enc.Encode(&g)
if err != nil {
t.Fatal("encode error:", err)
}
......
......@@ -23,7 +23,7 @@ func benchmarkEndToEnd(r io.Reader, w io.Writer, b *testing.B) {
b.StopTimer()
enc := NewEncoder(w)
dec := NewDecoder(r)
bench := &Bench{7, 3.2, "now is the time", []byte("for all good men")}
bench := &Bench{7, 3.2, "now is the time", bytes.Repeat([]byte("for all good men"), 100)}
b.StartTimer()
for i := 0; i < b.N; i++ {
if enc.Encode(bench) != nil {
......@@ -103,7 +103,7 @@ func TestCountDecodeMallocs(t *testing.T) {
t.Fatal("decode:", err)
}
})
if allocs != 3 {
t.Fatalf("mallocs per decode of type Bench: %v; wanted 3\n", allocs)
if allocs != 4 {
t.Fatalf("mallocs per decode of type Bench: %v; wanted 4\n", allocs)
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment