github.com/mattn/go@v0.0.0-20171011075504-07f7db3ea99f/src/encoding/binary/binary_test.go (about)

     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  
   504  func BenchmarkPutUint16(b *testing.B) {
   505  	buf := [2]byte{}
   506  	b.SetBytes(2)
   507  	for i := 0; i < b.N; i++ {
   508  		BigEndian.PutUint16(buf[:], uint16(i))
   509  	}
   510  }
   511  
   512  func BenchmarkPutUint32(b *testing.B) {
   513  	buf := [4]byte{}
   514  	b.SetBytes(4)
   515  	for i := 0; i < b.N; i++ {
   516  		BigEndian.PutUint32(buf[:], uint32(i))
   517  	}
   518  }
   519  
   520  func BenchmarkPutUint64(b *testing.B) {
   521  	buf := [8]byte{}
   522  	b.SetBytes(8)
   523  	for i := 0; i < b.N; i++ {
   524  		BigEndian.PutUint64(buf[:], uint64(i))
   525  	}
   526  }