Home | History | Annotate | Download | only in binary
      1 // Copyright 2009 The Go Authors.  All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package binary
      6 
      7 import (
      8 	"bytes"
      9 	"io"
     10 	"math"
     11 	"reflect"
     12 	"strings"
     13 	"testing"
     14 )
     15 
     16 type Struct struct {
     17 	Int8       int8
     18 	Int16      int16
     19 	Int32      int32
     20 	Int64      int64
     21 	Uint8      uint8
     22 	Uint16     uint16
     23 	Uint32     uint32
     24 	Uint64     uint64
     25 	Float32    float32
     26 	Float64    float64
     27 	Complex64  complex64
     28 	Complex128 complex128
     29 	Array      [4]uint8
     30 }
     31 
     32 type T struct {
     33 	Int     int
     34 	Uint    uint
     35 	Uintptr uintptr
     36 	Array   [4]int
     37 }
     38 
     39 var s = Struct{
     40 	0x01,
     41 	0x0203,
     42 	0x04050607,
     43 	0x08090a0b0c0d0e0f,
     44 	0x10,
     45 	0x1112,
     46 	0x13141516,
     47 	0x1718191a1b1c1d1e,
     48 
     49 	math.Float32frombits(0x1f202122),
     50 	math.Float64frombits(0x232425262728292a),
     51 	complex(
     52 		math.Float32frombits(0x2b2c2d2e),
     53 		math.Float32frombits(0x2f303132),
     54 	),
     55 	complex(
     56 		math.Float64frombits(0x333435363738393a),
     57 		math.Float64frombits(0x3b3c3d3e3f404142),
     58 	),
     59 
     60 	[4]uint8{0x43, 0x44, 0x45, 0x46},
     61 }
     62 
     63 var big = []byte{
     64 	1,
     65 	2, 3,
     66 	4, 5, 6, 7,
     67 	8, 9, 10, 11, 12, 13, 14, 15,
     68 	16,
     69 	17, 18,
     70 	19, 20, 21, 22,
     71 	23, 24, 25, 26, 27, 28, 29, 30,
     72 
     73 	31, 32, 33, 34,
     74 	35, 36, 37, 38, 39, 40, 41, 42,
     75 	43, 44, 45, 46, 47, 48, 49, 50,
     76 	51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66,
     77 
     78 	67, 68, 69, 70,
     79 }
     80 
     81 var little = []byte{
     82 	1,
     83 	3, 2,
     84 	7, 6, 5, 4,
     85 	15, 14, 13, 12, 11, 10, 9, 8,
     86 	16,
     87 	18, 17,
     88 	22, 21, 20, 19,
     89 	30, 29, 28, 27, 26, 25, 24, 23,
     90 
     91 	34, 33, 32, 31,
     92 	42, 41, 40, 39, 38, 37, 36, 35,
     93 	46, 45, 44, 43, 50, 49, 48, 47,
     94 	58, 57, 56, 55, 54, 53, 52, 51, 66, 65, 64, 63, 62, 61, 60, 59,
     95 
     96 	67, 68, 69, 70,
     97 }
     98 
     99 var src = []byte{1, 2, 3, 4, 5, 6, 7, 8}
    100 var res = []int32{0x01020304, 0x05060708}
    101 
    102 func checkResult(t *testing.T, dir string, order ByteOrder, err error, have, want interface{}) {
    103 	if err != nil {
    104 		t.Errorf("%v %v: %v", dir, order, err)
    105 		return
    106 	}
    107 	if !reflect.DeepEqual(have, want) {
    108 		t.Errorf("%v %v:\n\thave %+v\n\twant %+v", dir, order, have, want)
    109 	}
    110 }
    111 
    112 func testRead(t *testing.T, order ByteOrder, b []byte, s1 interface{}) {
    113 	var s2 Struct
    114 	err := Read(bytes.NewReader(b), order, &s2)
    115 	checkResult(t, "Read", order, err, s2, s1)
    116 }
    117 
    118 func testWrite(t *testing.T, order ByteOrder, b []byte, s1 interface{}) {
    119 	buf := new(bytes.Buffer)
    120 	err := Write(buf, order, s1)
    121 	checkResult(t, "Write", order, err, buf.Bytes(), b)
    122 }
    123 
    124 func TestLittleEndianRead(t *testing.T)     { testRead(t, LittleEndian, little, s) }
    125 func TestLittleEndianWrite(t *testing.T)    { testWrite(t, LittleEndian, little, s) }
    126 func TestLittleEndianPtrWrite(t *testing.T) { testWrite(t, LittleEndian, little, &s) }
    127 
    128 func TestBigEndianRead(t *testing.T)     { testRead(t, BigEndian, big, s) }
    129 func TestBigEndianWrite(t *testing.T)    { testWrite(t, BigEndian, big, s) }
    130 func TestBigEndianPtrWrite(t *testing.T) { testWrite(t, BigEndian, big, &s) }
    131 
    132 func TestReadSlice(t *testing.T) {
    133 	slice := make([]int32, 2)
    134 	err := Read(bytes.NewReader(src), BigEndian, slice)
    135 	checkResult(t, "ReadSlice", BigEndian, err, slice, res)
    136 }
    137 
    138 func TestWriteSlice(t *testing.T) {
    139 	buf := new(bytes.Buffer)
    140 	err := Write(buf, BigEndian, res)
    141 	checkResult(t, "WriteSlice", BigEndian, err, buf.Bytes(), src)
    142 }
    143 
    144 // Addresses of arrays are easier to manipulate with reflection than are slices.
    145 var intArrays = []interface{}{
    146 	&[100]int8{},
    147 	&[100]int16{},
    148 	&[100]int32{},
    149 	&[100]int64{},
    150 	&[100]uint8{},
    151 	&[100]uint16{},
    152 	&[100]uint32{},
    153 	&[100]uint64{},
    154 }
    155 
    156 func TestSliceRoundTrip(t *testing.T) {
    157 	buf := new(bytes.Buffer)
    158 	for _, array := range intArrays {
    159 		src := reflect.ValueOf(array).Elem()
    160 		unsigned := false
    161 		switch src.Index(0).Kind() {
    162 		case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
    163 			unsigned = true
    164 		}
    165 		for i := 0; i < src.Len(); i++ {
    166 			if unsigned {
    167 				src.Index(i).SetUint(uint64(i * 0x07654321))
    168 			} else {
    169 				src.Index(i).SetInt(int64(i * 0x07654321))
    170 			}
    171 		}
    172 		buf.Reset()
    173 		srcSlice := src.Slice(0, src.Len())
    174 		err := Write(buf, BigEndian, srcSlice.Interface())
    175 		if err != nil {
    176 			t.Fatal(err)
    177 		}
    178 		dst := reflect.New(src.Type()).Elem()
    179 		dstSlice := dst.Slice(0, dst.Len())
    180 		err = Read(buf, BigEndian, dstSlice.Interface())
    181 		if err != nil {
    182 			t.Fatal(err)
    183 		}
    184 		if !reflect.DeepEqual(src.Interface(), dst.Interface()) {
    185 			t.Fatal(src)
    186 		}
    187 	}
    188 }
    189 
    190 func TestWriteT(t *testing.T) {
    191 	buf := new(bytes.Buffer)
    192 	ts := T{}
    193 	if err := Write(buf, BigEndian, ts); err == nil {
    194 		t.Errorf("WriteT: have err == nil, want non-nil")
    195 	}
    196 
    197 	tv := reflect.Indirect(reflect.ValueOf(ts))
    198 	for i, n := 0, tv.NumField(); i < n; i++ {
    199 		typ := tv.Field(i).Type().String()
    200 		if typ == "[4]int" {
    201 			typ = "int" // the problem is int, not the [4]
    202 		}
    203 		if err := Write(buf, BigEndian, tv.Field(i).Interface()); err == nil {
    204 			t.Errorf("WriteT.%v: have err == nil, want non-nil", tv.Field(i).Type())
    205 		} else if !strings.Contains(err.Error(), typ) {
    206 			t.Errorf("WriteT: have err == %q, want it to mention %s", err, typ)
    207 		}
    208 	}
    209 }
    210 
    211 type BlankFields struct {
    212 	A uint32
    213 	_ int32
    214 	B float64
    215 	_ [4]int16
    216 	C byte
    217 	_ [7]byte
    218 	_ struct {
    219 		f [8]float32
    220 	}
    221 }
    222 
    223 type BlankFieldsProbe struct {
    224 	A  uint32
    225 	P0 int32
    226 	B  float64
    227 	P1 [4]int16
    228 	C  byte
    229 	P2 [7]byte
    230 	P3 struct {
    231 		F [8]float32
    232 	}
    233 }
    234 
    235 func TestBlankFields(t *testing.T) {
    236 	buf := new(bytes.Buffer)
    237 	b1 := BlankFields{A: 1234567890, B: 2.718281828, C: 42}
    238 	if err := Write(buf, LittleEndian, &b1); err != nil {
    239 		t.Error(err)
    240 	}
    241 
    242 	// zero values must have been written for blank fields
    243 	var p BlankFieldsProbe
    244 	if err := Read(buf, LittleEndian, &p); err != nil {
    245 		t.Error(err)
    246 	}
    247 
    248 	// quick test: only check first value of slices
    249 	if p.P0 != 0 || p.P1[0] != 0 || p.P2[0] != 0 || p.P3.F[0] != 0 {
    250 		t.Errorf("non-zero values for originally blank fields: %#v", p)
    251 	}
    252 
    253 	// write p and see if we can probe only some fields
    254 	if err := Write(buf, LittleEndian, &p); err != nil {
    255 		t.Error(err)
    256 	}
    257 
    258 	// read should ignore blank fields in b2
    259 	var b2 BlankFields
    260 	if err := Read(buf, LittleEndian, &b2); err != nil {
    261 		t.Error(err)
    262 	}
    263 	if b1.A != b2.A || b1.B != b2.B || b1.C != b2.C {
    264 		t.Errorf("%#v != %#v", b1, b2)
    265 	}
    266 }
    267 
    268 // An attempt to read into a struct with an unexported field will
    269 // panic.  This is probably not the best choice, but at this point
    270 // anything else would be an API change.
    271 
    272 type Unexported struct {
    273 	a int32
    274 }
    275 
    276 func TestUnexportedRead(t *testing.T) {
    277 	var buf bytes.Buffer
    278 	u1 := Unexported{a: 1}
    279 	if err := Write(&buf, LittleEndian, &u1); err != nil {
    280 		t.Fatal(err)
    281 	}
    282 
    283 	defer func() {
    284 		if recover() == nil {
    285 			t.Fatal("did not panic")
    286 		}
    287 	}()
    288 	var u2 Unexported
    289 	Read(&buf, LittleEndian, &u2)
    290 }
    291 
    292 func TestReadErrorMsg(t *testing.T) {
    293 	var buf bytes.Buffer
    294 	read := func(data interface{}) {
    295 		err := Read(&buf, LittleEndian, data)
    296 		want := "binary.Read: invalid type " + reflect.TypeOf(data).String()
    297 		if err == nil {
    298 			t.Errorf("%T: got no error; want %q", data, want)
    299 			return
    300 		}
    301 		if got := err.Error(); got != want {
    302 			t.Errorf("%T: got %q; want %q", data, got, want)
    303 		}
    304 	}
    305 	read(0)
    306 	s := new(struct{})
    307 	read(&s)
    308 	p := &s
    309 	read(&p)
    310 }
    311 
    312 type byteSliceReader struct {
    313 	remain []byte
    314 }
    315 
    316 func (br *byteSliceReader) Read(p []byte) (int, error) {
    317 	n := copy(p, br.remain)
    318 	br.remain = br.remain[n:]
    319 	return n, nil
    320 }
    321 
    322 func BenchmarkReadSlice1000Int32s(b *testing.B) {
    323 	bsr := &byteSliceReader{}
    324 	slice := make([]int32, 1000)
    325 	buf := make([]byte, len(slice)*4)
    326 	b.SetBytes(int64(len(buf)))
    327 	b.ResetTimer()
    328 	for i := 0; i < b.N; i++ {
    329 		bsr.remain = buf
    330 		Read(bsr, BigEndian, slice)
    331 	}
    332 }
    333 
    334 func BenchmarkReadStruct(b *testing.B) {
    335 	bsr := &byteSliceReader{}
    336 	var buf bytes.Buffer
    337 	Write(&buf, BigEndian, &s)
    338 	b.SetBytes(int64(dataSize(reflect.ValueOf(s))))
    339 	t := s
    340 	b.ResetTimer()
    341 	for i := 0; i < b.N; i++ {
    342 		bsr.remain = buf.Bytes()
    343 		Read(bsr, BigEndian, &t)
    344 	}
    345 	b.StopTimer()
    346 	if !reflect.DeepEqual(s, t) {
    347 		b.Fatal("no match")
    348 	}
    349 }
    350 
    351 func BenchmarkReadInts(b *testing.B) {
    352 	var ls Struct
    353 	bsr := &byteSliceReader{}
    354 	var r io.Reader = bsr
    355 	b.SetBytes(2 * (1 + 2 + 4 + 8))
    356 	b.ResetTimer()
    357 	for i := 0; i < b.N; i++ {
    358 		bsr.remain = big
    359 		Read(r, BigEndian, &ls.Int8)
    360 		Read(r, BigEndian, &ls.Int16)
    361 		Read(r, BigEndian, &ls.Int32)
    362 		Read(r, BigEndian, &ls.Int64)
    363 		Read(r, BigEndian, &ls.Uint8)
    364 		Read(r, BigEndian, &ls.Uint16)
    365 		Read(r, BigEndian, &ls.Uint32)
    366 		Read(r, BigEndian, &ls.Uint64)
    367 	}
    368 
    369 	want := s
    370 	want.Float32 = 0
    371 	want.Float64 = 0
    372 	want.Complex64 = 0
    373 	want.Complex128 = 0
    374 	for i := range want.Array {
    375 		want.Array[i] = 0
    376 	}
    377 	b.StopTimer()
    378 	if !reflect.DeepEqual(ls, want) {
    379 		panic("no match")
    380 	}
    381 }
    382 
    383 func BenchmarkWriteInts(b *testing.B) {
    384 	buf := new(bytes.Buffer)
    385 	var w io.Writer = buf
    386 	b.SetBytes(2 * (1 + 2 + 4 + 8))
    387 	b.ResetTimer()
    388 	for i := 0; i < b.N; i++ {
    389 		buf.Reset()
    390 		Write(w, BigEndian, s.Int8)
    391 		Write(w, BigEndian, s.Int16)
    392 		Write(w, BigEndian, s.Int32)
    393 		Write(w, BigEndian, s.Int64)
    394 		Write(w, BigEndian, s.Uint8)
    395 		Write(w, BigEndian, s.Uint16)
    396 		Write(w, BigEndian, s.Uint32)
    397 		Write(w, BigEndian, s.Uint64)
    398 	}
    399 	b.StopTimer()
    400 	if !bytes.Equal(buf.Bytes(), big[:30]) {
    401 		b.Fatalf("first half doesn't match: %x %x", buf.Bytes(), big[:30])
    402 	}
    403 }
    404 
    405 func BenchmarkWriteSlice1000Int32s(b *testing.B) {
    406 	slice := make([]int32, 1000)
    407 	buf := new(bytes.Buffer)
    408 	var w io.Writer = buf
    409 	b.SetBytes(4 * 1000)
    410 	b.ResetTimer()
    411 	for i := 0; i < b.N; i++ {
    412 		buf.Reset()
    413 		Write(w, BigEndian, slice)
    414 	}
    415 	b.StopTimer()
    416 }
    417