github.com/m3db/m3@v1.5.0/src/dbnode/persist/fs/msgpack/stream_test.go (about)

     1  // Copyright (c) 2017 Uber Technologies, Inc
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE
    20  
    21  package msgpack
    22  
    23  import (
    24  	"bytes"
    25  	"fmt"
    26  	"io"
    27  	"runtime"
    28  	"strings"
    29  	"testing"
    30  
    31  	"github.com/stretchr/testify/assert"
    32  	"github.com/stretchr/testify/require"
    33  	msgpacklib "gopkg.in/vmihailenco/msgpack.v2"
    34  )
    35  
    36  // Call Read to accumulate the text of a file.
    37  func reads(buf ByteDecoderStream, m int) string {
    38  	var b [1000]byte
    39  	if int(buf.Remaining()) > len(b) {
    40  		panic(fmt.Errorf("cannot read all"))
    41  	}
    42  
    43  	nb := 0
    44  	for {
    45  		n, err := buf.Read(b[nb : nb+m])
    46  		nb += n
    47  		if err == io.EOF {
    48  			break
    49  		}
    50  	}
    51  	return string(b[0:nb])
    52  }
    53  
    54  func TestDecoderStream(t *testing.T) {
    55  	var texts [31]string
    56  	str := ""
    57  	all := ""
    58  	for i := 0; i < len(texts)-1; i++ {
    59  		texts[i] = str + "\n"
    60  		all += texts[i]
    61  		str += string(rune(i%26 + 'a'))
    62  	}
    63  	texts[len(texts)-1] = all
    64  
    65  	buf := NewByteDecoderStream(nil)
    66  	for i := 0; i < len(texts); i++ {
    67  		text := texts[i]
    68  		for j := 1; j <= 8; j++ {
    69  			buf.Reset([]byte(text))
    70  			s := reads(buf, j)
    71  			if s != text {
    72  				t.Errorf("m=%d want=%q got=%q", j, text, s)
    73  			}
    74  		}
    75  	}
    76  }
    77  
    78  func TestDecoderStreamSkip(t *testing.T) {
    79  	d := []byte{1, 2, 3, 4, 5}
    80  	buf := NewByteDecoderStream(d)
    81  	assert.Equal(t, int64(5), buf.Remaining())
    82  	assert.NoError(t, buf.Skip(3))
    83  	assert.Equal(t, int64(2), buf.Remaining())
    84  
    85  	p := make([]byte, 2)
    86  	n, err := buf.Read(p)
    87  	assert.Equal(t, 2, n)
    88  	assert.NoError(t, err)
    89  	assert.Equal(t, []byte{4, 5}, p)
    90  }
    91  
    92  func TestDecoderStreamUnreadByte(t *testing.T) {
    93  	segments := []string{"Hello, ", "world"}
    94  	got := ""
    95  	want := strings.Join(segments, "")
    96  	r := NewByteDecoderStream([]byte(want))
    97  	// Normal execution.
    98  	for {
    99  		b1, err := r.ReadByte()
   100  		if err != nil {
   101  			if err != io.EOF {
   102  				t.Error("unexpected error on ReadByte:", err)
   103  			}
   104  			break
   105  		}
   106  		got += string(b1)
   107  		// Put it back and read it again.
   108  		if err = r.UnreadByte(); err != nil {
   109  			t.Fatal("unexpected error on UnreadByte:", err)
   110  		}
   111  		b2, err := r.ReadByte()
   112  		if err != nil {
   113  			t.Fatal("unexpected error reading after unreading:", err)
   114  		}
   115  		if b1 != b2 {
   116  			t.Fatalf("incorrect byte after unread: got %q, want %q", b1, b2)
   117  		}
   118  	}
   119  	if got != want {
   120  		t.Errorf("got %q, want %q", got, want)
   121  	}
   122  }
   123  
   124  func TestDecoderStreamUnreadByteMultiple(t *testing.T) {
   125  	segments := []string{"Hello, ", "world"}
   126  	data := []byte(strings.Join(segments, ""))
   127  	for n := 0; n <= len(data); n++ {
   128  		r := NewByteDecoderStream(data)
   129  		// Read n bytes.
   130  		for i := 0; i < n; i++ {
   131  			b, err := r.ReadByte()
   132  			if err != nil {
   133  				t.Fatalf("n = %d: unexpected error on ReadByte: %v", n, err)
   134  			}
   135  			if b != data[i] {
   136  				t.Fatalf("n = %d: incorrect byte returned from ReadByte: got %q, want %q", n, b, data[i])
   137  			}
   138  		}
   139  		// Unread one byte if there is one.
   140  		if n > 0 {
   141  			remaining := r.Remaining()
   142  			if expect := int64(len(data) - n); remaining != expect {
   143  				t.Errorf("n = %d: unexpected remaining before UnreadByte: got %d, want %d", n, remaining, expect)
   144  			}
   145  			if err := r.UnreadByte(); err != nil {
   146  				t.Errorf("n = %d: unexpected error on UnreadByte: %v", n, err)
   147  			}
   148  			remaining = r.Remaining()
   149  			if expect := int64(len(data) - n + 1); remaining != expect {
   150  				t.Errorf("n = %d: unexpected remaining after UnreadByte: got %d, want %d", n, remaining, expect)
   151  			}
   152  		}
   153  		// Test that we cannot unread any further.
   154  		if err := r.UnreadByte(); err == nil {
   155  			t.Errorf("n = %d: expected error on UnreadByte", n)
   156  		}
   157  		// Test that it can be read back with Read.
   158  		if n > 0 {
   159  			var c [1]byte
   160  			_, err := r.Read(c[:])
   161  			if err != nil {
   162  				t.Errorf("n = %d: unexpected error on Read after UnreadByte: %v", n, err)
   163  			}
   164  			if c[0] != data[n-1] {
   165  				t.Errorf("n = %d: unexpected error on Read after UnreadByte: %v != %v", n, c[0], data[n-1])
   166  			}
   167  		}
   168  	}
   169  }
   170  
   171  type bufioWrapCheckReaderNotImpl struct {
   172  	wasWrappedByBufio bool
   173  }
   174  
   175  func (f *bufioWrapCheckReaderNotImpl) Read(p []byte) (int, error) {
   176  	buf := make([]byte, 100000)
   177  	n := runtime.Stack(buf, false)
   178  	if n == 0 {
   179  		panic("runtime.Stack did not write anything")
   180  	}
   181  	if bytes.Contains(buf, []byte("bufio")) {
   182  		f.wasWrappedByBufio = true
   183  	}
   184  
   185  	return 0, nil
   186  }
   187  
   188  type bufioWrapCheckReader struct {
   189  	byteDecoderStream
   190  	wasWrappedByBufio bool
   191  }
   192  
   193  func (f *bufioWrapCheckReader) Read(p []byte) (int, error) {
   194  	buf := make([]byte, 100000)
   195  	n := runtime.Stack(buf, false)
   196  	if n == 0 {
   197  		panic("runtime.Stack did not write anything")
   198  	}
   199  	if bytes.Contains(buf, []byte("bufio")) {
   200  		f.wasWrappedByBufio = true
   201  	}
   202  
   203  	return 0, nil
   204  }
   205  
   206  // The underlying msgpack library that we use will attempt to wrap decoder streams
   207  // that are passed to it that do not implement a specific interface in a bufio.Reader.
   208  // This is wasteful for us because we're using mmap'd byte slices under the hood which
   209  // which do not require buffered IO and each of the bufio.Readers() that the library
   210  // allocates uses 4KiB of memory, and there can be hundreds of thousands of them.
   211  //
   212  // This test makes sure that our DecoderStream can be passed to the library without
   213  // being wrapped by a bufio.Reader().
   214  func TestStreamCanBeUsedWithMsgpackLibraryNoBufio(t *testing.T) {
   215  	// First, make sure that we can actually detect if our reader gets
   216  	// wrapped in a bufio.Reader().
   217  	wrapCheckNotImpl := &bufioWrapCheckReaderNotImpl{}
   218  	decoder := msgpacklib.NewDecoder(wrapCheckNotImpl)
   219  	decoder.DecodeArrayLen()
   220  	require.True(t, wrapCheckNotImpl.wasWrappedByBufio)
   221  
   222  	// Next, make sure that anything implementing our DecoderStream
   223  	// interface won't get wrapped up in a bufio.Reader().
   224  	wrapCheckImpl := &bufioWrapCheckReader{}
   225  	var _ DecoderStream = wrapCheckImpl // Make sure our fake one implements the iFace.
   226  	decoder = msgpacklib.NewDecoder(wrapCheckImpl)
   227  	decoder.DecodeArrayLen()
   228  	require.False(t, wrapCheckImpl.wasWrappedByBufio)
   229  }