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