From a54684f8daaf56b466429518b721d24d75b6632c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=B4she=20van=20der=20Sterre?= Date: Sat, 14 Nov 2009 14:42:22 -0800 Subject: [PATCH] encoding/binary: implemented the Write function The ByteOrder.Put* methods are already available, this change uses them to implement the Write function. R=golang-dev, agl1, rsc, r https://golang.org/cl/152141 --- src/pkg/encoding/binary/binary.go | 94 +++++++++++++++++++++++++- src/pkg/encoding/binary/binary_test.go | 47 ++++++++----- 2 files changed, 122 insertions(+), 19 deletions(-) diff --git a/src/pkg/encoding/binary/binary.go b/src/pkg/encoding/binary/binary.go index abf2465cc7..92c89cea97 100644 --- a/src/pkg/encoding/binary/binary.go +++ b/src/pkg/encoding/binary/binary.go @@ -117,8 +117,8 @@ func (bigEndian) GoString() string { return "binary.BigEndian" } // A fixed-size value is either a fixed-size integer // (int8, uint8, int16, uint16, ...) or an array or struct // containing only fixed-size values. Bytes read from -// r are decoded using order and written to successive -// fields of the data. +// r are decoded using the specified byte order and written +// to successive fields of the data. func Read(r io.Reader, order ByteOrder, data interface{}) os.Error { v := reflect.NewValue(data).(*reflect.PtrValue).Elem(); size := sizeof(v.Type()); @@ -133,6 +133,27 @@ func Read(r io.Reader, order ByteOrder, data interface{}) os.Error { return nil; } +// Write writes the binary representation of data into w. +// Data must be a fixed-size value or a pointer to +// a fixed-size value. +// A fixed-size value is either a fixed-size integer +// (int8, uint8, int16, uint16, ...) or an array or struct +// containing only fixed-size values. Bytes written to +// w are encoded using the specified byte order and read +// from successive fields of the data. +func Write(w io.Writer, order ByteOrder, data interface{}) os.Error { + v := reflect.Indirect(reflect.NewValue(data)); + size := sizeof(v.Type()); + if size < 0 { + return os.NewError("binary.Write: invalid type " + v.Type().String()) + } + buf := make([]byte, size); + e := &encoder{order: order, buf: buf}; + e.value(v); + _, err := w.Write(buf); + return err; +} + func sizeof(t reflect.Type) int { switch t := t.(type) { case *reflect.ArrayType: @@ -182,38 +203,71 @@ type decoder struct { buf []byte; } +type encoder struct { + order ByteOrder; + buf []byte; +} + func (d *decoder) uint8() uint8 { x := d.buf[0]; d.buf = d.buf[1:len(d.buf)]; return x; } +func (e *encoder) uint8(x uint8) { + e.buf[0] = x; + e.buf = e.buf[1:len(e.buf)]; +} + func (d *decoder) uint16() uint16 { x := d.order.Uint16(d.buf[0:2]); d.buf = d.buf[2:len(d.buf)]; return x; } +func (e *encoder) uint16(x uint16) { + e.order.PutUint16(e.buf[0:2], x); + e.buf = e.buf[2:len(e.buf)]; +} + func (d *decoder) uint32() uint32 { x := d.order.Uint32(d.buf[0:4]); d.buf = d.buf[4:len(d.buf)]; return x; } +func (e *encoder) uint32(x uint32) { + e.order.PutUint32(e.buf[0:4], x); + e.buf = e.buf[4:len(e.buf)]; +} + func (d *decoder) uint64() uint64 { x := d.order.Uint64(d.buf[0:8]); d.buf = d.buf[8:len(d.buf)]; return x; } +func (e *encoder) uint64(x uint64) { + e.order.PutUint64(e.buf[0:8], x); + e.buf = e.buf[8:len(e.buf)]; +} + func (d *decoder) int8() int8 { return int8(d.uint8()) } +func (e *encoder) int8(x int8) { e.uint8(uint8(x)) } + func (d *decoder) int16() int16 { return int16(d.uint16()) } +func (e *encoder) int16(x int16) { e.uint16(uint16(x)) } + func (d *decoder) int32() int32 { return int32(d.uint32()) } +func (e *encoder) int32(x int32) { e.uint32(uint32(x)) } + func (d *decoder) int64() int64 { return int64(d.uint64()) } +func (e *encoder) int64(x int64) { e.uint64(uint64(x)) } + func (d *decoder) value(v reflect.Value) { switch v := v.(type) { case *reflect.ArrayValue: @@ -249,3 +303,39 @@ func (d *decoder) value(v reflect.Value) { v.Set(math.Float64frombits(d.uint64())) } } + +func (e *encoder) value(v reflect.Value) { + switch v := v.(type) { + case *reflect.ArrayValue: + l := v.Len(); + for i := 0; i < l; i++ { + e.value(v.Elem(i)) + } + case *reflect.StructValue: + l := v.NumField(); + for i := 0; i < l; i++ { + e.value(v.Field(i)) + } + + case *reflect.Uint8Value: + e.uint8(v.Get()) + case *reflect.Uint16Value: + e.uint16(v.Get()) + case *reflect.Uint32Value: + e.uint32(v.Get()) + case *reflect.Uint64Value: + e.uint64(v.Get()) + case *reflect.Int8Value: + e.int8(v.Get()) + case *reflect.Int16Value: + e.int16(v.Get()) + case *reflect.Int32Value: + e.int32(v.Get()) + case *reflect.Int64Value: + e.int64(v.Get()) + case *reflect.Float32Value: + e.uint32(math.Float32bits(v.Get())) + case *reflect.Float64Value: + e.uint64(math.Float64bits(v.Get())) + } +} diff --git a/src/pkg/encoding/binary/binary_test.go b/src/pkg/encoding/binary/binary_test.go index 71c6875cc9..d98f050563 100644 --- a/src/pkg/encoding/binary/binary_test.go +++ b/src/pkg/encoding/binary/binary_test.go @@ -5,6 +5,7 @@ package binary import ( + "os"; "bytes"; "math"; "reflect"; @@ -63,24 +64,36 @@ var little = []byte{ 39, 40, 41, 42, } -func TestRead(t *testing.T) { - var sl, sb Struct; - - err := Read(bytes.NewBuffer(big), BigEndian, &sb); +func checkResult(t *testing.T, dir string, order, err os.Error, have, want interface{}) { if err != nil { - t.Errorf("Read big-endian: %v", err); - goto little; + t.Errorf("%v %v: %v", dir, order, err); + return; } - if !reflect.DeepEqual(sb, s) { - t.Errorf("Read big-endian:\n\thave %+v\n\twant %+v", sb, s) - } - -little: - err = Read(bytes.NewBuffer(little), LittleEndian, &sl); - if err != nil { - t.Errorf("Read little-endian: %v", err) - } - if !reflect.DeepEqual(sl, s) { - t.Errorf("Read little-endian:\n\thave %+v\n\twant %+v", sl, s) + if !reflect.DeepEqual(have, want) { + t.Errorf("%v %v:\n\thave %+v\n\twant %+v", dir, order, have, want) } } + +func testRead(t *testing.T, order ByteOrder, b []byte, s1 interface{}) { + var s2 Struct; + err := Read(bytes.NewBuffer(b), order, &s2); + checkResult(t, "Read", order, err, s2, s1); +} + +func testWrite(t *testing.T, order ByteOrder, b []byte, s1 interface{}) { + buf := new(bytes.Buffer); + err := Write(buf, order, s1); + checkResult(t, "Write", order, err, buf.Bytes(), b); +} + +func TestBigEndianRead(t *testing.T) { testRead(t, BigEndian, big, s) } + +func TestLittleEndianRead(t *testing.T) { testRead(t, LittleEndian, little, s) } + +func TestBigEndianWrite(t *testing.T) { testWrite(t, BigEndian, big, s) } + +func TestLittleEndianWrite(t *testing.T) { testWrite(t, LittleEndian, little, s) } + +func TestBigEndianPtrWrite(t *testing.T) { testWrite(t, BigEndian, big, &s) } + +func TestLittleEndianPtrWrite(t *testing.T) { testWrite(t, LittleEndian, little, &s) }