github.com/dgraph-io/ristretto@v0.1.2-0.20240116140435-c67e07994f91/z/buffer_test.go (about) 1 /* 2 * Copyright 2020 Dgraph Labs, Inc. and Contributors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package z 18 19 import ( 20 "bytes" 21 "encoding/binary" 22 "encoding/hex" 23 "fmt" 24 "math/rand" 25 "sort" 26 "testing" 27 "time" 28 29 "github.com/stretchr/testify/require" 30 ) 31 32 func TestBuffer(t *testing.T) { 33 rand.Seed(time.Now().Unix()) 34 const capacity = 512 35 buffers := newTestBuffers(t, capacity) 36 37 for _, buf := range buffers { 38 name := fmt.Sprintf("Using buffer type: %s", buf.bufType) 39 t.Run(name, func(t *testing.T) { 40 // This is just for verifying result 41 var bytesBuf bytes.Buffer 42 bytesBuf.Grow(capacity) 43 44 // Writer small []byte 45 var smallData [256]byte 46 rand.Read(smallData[:]) 47 var bigData [1024]byte 48 rand.Read(bigData[:]) 49 50 _, err := buf.Write(smallData[:]) 51 require.NoError(t, err, "unable to write data to page buffer") 52 _, err = buf.Write(bigData[:]) 53 require.NoError(t, err, "unable to write data to page buffer") 54 55 // Write data to bytesBuffer also, just to match result. 56 bytesBuf.Write(smallData[:]) 57 bytesBuf.Write(bigData[:]) 58 require.Equal(t, buf.Bytes(), bytesBuf.Bytes()) 59 }) 60 } 61 } 62 63 func TestBufferWrite(t *testing.T) { 64 rand.Seed(time.Now().Unix()) 65 const capacity = 32 66 buffers := newTestBuffers(t, capacity) 67 68 for _, buf := range buffers { 69 name := fmt.Sprintf("Using buffer type: %s", buf.bufType) 70 t.Run(name, func(t *testing.T) { 71 var data [128]byte 72 rand.Read(data[:]) 73 bytesBuf := new(bytes.Buffer) 74 75 end := 32 76 for i := 0; i < 3; i++ { 77 n, err := buf.Write(data[:end]) 78 require.NoError(t, err, "unable to write bytes to buffer") 79 require.Equal(t, n, end, "length of buffer and length written should be equal") 80 81 // append to bb also for testing. 82 bytesBuf.Write(data[:end]) 83 84 require.Equal(t, buf.Bytes(), bytesBuf.Bytes()) 85 end = end * 2 86 } 87 88 }) 89 } 90 } 91 92 func TestBufferAutoMmap(t *testing.T) { 93 buf := NewBuffer(1<<20, "test").WithAutoMmap(64<<20, "") 94 defer func() { require.NoError(t, buf.Release()) }() 95 96 N := 128 << 10 97 var wb [1024]byte 98 for i := 0; i < N; i++ { 99 rand.Read(wb[:]) 100 b := buf.SliceAllocate(len(wb)) 101 copy(b, wb[:]) 102 } 103 t.Logf("Buffer size: %d\n", buf.LenWithPadding()) 104 105 buf.SortSlice(func(l, r []byte) bool { 106 return bytes.Compare(l, r) < 0 107 }) 108 t.Logf("sort done\n") 109 110 var count int 111 var last []byte 112 require.NoError(t, buf.SliceIterate(func(slice []byte) error { 113 require.True(t, bytes.Compare(slice, last) >= 0) 114 last = append(last[:0], slice...) 115 count++ 116 return nil 117 })) 118 require.Equal(t, N, count) 119 } 120 121 func TestBufferSimpleSort(t *testing.T) { 122 bufs := newTestBuffers(t, 1<<20) 123 for _, buf := range bufs { 124 name := fmt.Sprintf("Using buffer type: %s", buf.bufType) 125 t.Run(name, func(t *testing.T) { 126 for i := 0; i < 25600; i++ { 127 b := buf.SliceAllocate(4) 128 binary.BigEndian.PutUint32(b, uint32(rand.Int31n(256000))) 129 } 130 buf.SortSlice(func(ls, rs []byte) bool { 131 left := binary.BigEndian.Uint32(ls) 132 right := binary.BigEndian.Uint32(rs) 133 return left < right 134 }) 135 var last uint32 136 var i int 137 require.NoError(t, buf.SliceIterate(func(slice []byte) error { 138 num := binary.BigEndian.Uint32(slice) 139 if num < last { 140 fmt.Printf("num: %d idx: %d last: %d\n", num, i, last) 141 } 142 i++ 143 require.GreaterOrEqual(t, num, last) 144 last = num 145 // fmt.Printf("Got number: %d\n", num) 146 return nil 147 })) 148 }) 149 } 150 } 151 152 func TestBufferSlice(t *testing.T) { 153 const capacity = 32 154 buffers := newTestBuffers(t, capacity) 155 156 for _, buf := range buffers { 157 name := fmt.Sprintf("Using buffer type: %s", buf.bufType) 158 t.Run(name, func(t *testing.T) { 159 count := 10000 160 exp := make([][]byte, 0, count) 161 162 // Create "count" number of slices. 163 for i := 0; i < count; i++ { 164 sz := 1 + rand.Intn(8) 165 testBuf := make([]byte, sz) 166 rand.Read(testBuf) 167 168 newSlice := buf.SliceAllocate(sz) 169 require.Equal(t, sz, copy(newSlice, testBuf)) 170 171 // Save testBuf for verification. 172 exp = append(exp, testBuf) 173 } 174 175 compare := func() { 176 i := 0 177 require.NoError(t, buf.SliceIterate(func(slice []byte) error { 178 // All the slices returned by the buffer should be equal to what we 179 // inserted earlier. 180 if !bytes.Equal(exp[i], slice) { 181 fmt.Printf("exp: %s got: %s\n", hex.Dump(exp[i]), hex.Dump(slice)) 182 t.Fail() 183 } 184 require.Equal(t, exp[i], slice) 185 i++ 186 return nil 187 })) 188 require.Equal(t, len(exp), i) 189 } 190 compare() // same order as inserted. 191 192 t.Logf("Sorting using sort.Slice\n") 193 sort.Slice(exp, func(i, j int) bool { 194 return bytes.Compare(exp[i], exp[j]) < 0 195 }) 196 t.Logf("Sorting using buf.SortSlice\n") 197 buf.SortSlice(func(a, b []byte) bool { 198 return bytes.Compare(a, b) < 0 199 }) 200 t.Logf("Done sorting\n") 201 compare() // same order after sort. 202 }) 203 } 204 } 205 206 func TestBufferSort(t *testing.T) { 207 const capacity = 32 208 bufs := newTestBuffers(t, capacity) 209 210 for _, buf := range bufs { 211 name := fmt.Sprintf("Using buffer type: %s", buf.bufType) 212 t.Run(name, func(t *testing.T) { 213 const N = 10000 214 215 for i := 0; i < N; i++ { 216 newSlice := buf.SliceAllocate(8) 217 uid := uint64(rand.Int63()) 218 binary.BigEndian.PutUint64(newSlice, uid) 219 } 220 221 test := func(start, end int) { 222 start = buf.StartOffset() + 16*start 223 end = buf.StartOffset() + 16*end 224 buf.SortSliceBetween(start, end, func(ls, rs []byte) bool { 225 lhs := binary.BigEndian.Uint64(ls) 226 rhs := binary.BigEndian.Uint64(rs) 227 return lhs < rhs 228 }) 229 230 next := start 231 var slice []byte 232 var last uint64 233 var count int 234 for next >= 0 && next < end { 235 slice, next = buf.Slice(next) 236 uid := binary.BigEndian.Uint64(slice) 237 require.GreaterOrEqual(t, uid, last) 238 last = uid 239 count++ 240 } 241 require.Equal(t, (end-start)/16, count) 242 } 243 for i := 10; i <= N; i += 10 { 244 test(i-10, i) 245 } 246 test(0, N) 247 }) 248 } 249 } 250 251 // Test that the APIs returns the expected offsets. 252 func TestBufferPadding(t *testing.T) { 253 bufs := newTestBuffers(t, 1<<10) 254 for _, buf := range bufs { 255 name := fmt.Sprintf("Using buffer type: %s", buf.bufType) 256 t.Run(name, func(t *testing.T) { 257 sz := rand.Int31n(100) 258 259 writeOffset := buf.AllocateOffset(int(sz)) 260 require.Equal(t, buf.StartOffset(), writeOffset) 261 262 b := make([]byte, sz) 263 rand.Read(b) 264 265 copy(buf.Bytes(), b) 266 data := buf.Data(buf.StartOffset()) 267 require.Equal(t, b, data[:sz]) 268 }) 269 } 270 } 271 272 func newTestBuffers(t *testing.T, capacity int) []*Buffer { 273 var bufs []*Buffer 274 275 buf := NewBuffer(capacity, "test") 276 bufs = append(bufs, buf) 277 278 buf, err := NewBufferTmp("", capacity) 279 require.NoError(t, err) 280 bufs = append(bufs, buf) 281 282 t.Cleanup(func() { 283 for _, buf := range bufs { 284 require.NoError(t, buf.Release()) 285 } 286 }) 287 288 return bufs 289 } 290 291 func TestSmallBuffer(t *testing.T) { 292 buf := NewBuffer(5, "test") 293 t.Cleanup(func() { 294 require.NoError(t, buf.Release()) 295 }) 296 // Write something to buffer so sort actually happens. 297 buf.WriteSlice([]byte("abc")) 298 // This test fails if the buffer has offset > currSz. 299 require.NotPanics(t, func() { 300 buf.SortSlice(func(left, right []byte) bool { 301 return true 302 }) 303 }) 304 }