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 }