github.com/fiatjaf/generic-ristretto@v0.0.1/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  	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  			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  				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() + 12*start
   223  				end = buf.StartOffset() + 12*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  				slice, next := []byte{}, start
   231  				var last uint64
   232  				var count int
   233  				for next >= 0 && next < end {
   234  					slice, next = buf.Slice(next)
   235  					uid := binary.BigEndian.Uint64(slice)
   236  					require.GreaterOrEqual(t, uid, last)
   237  					last = uid
   238  					count++
   239  				}
   240  				require.Equal(t, (end-start)/12, count)
   241  			}
   242  			for i := 10; i <= N; i += 10 {
   243  				test(i-10, i)
   244  			}
   245  			test(0, N)
   246  		})
   247  	}
   248  }
   249  
   250  // Test that the APIs returns the expected offsets.
   251  func TestBufferPadding(t *testing.T) {
   252  	bufs := newTestBuffers(t, 1<<10)
   253  	for _, buf := range bufs {
   254  		name := fmt.Sprintf("Using buffer type: %s", buf.bufType)
   255  		t.Run(name, func(t *testing.T) {
   256  			sz := rand.Int31n(100)
   257  
   258  			writeOffset := buf.AllocateOffset(int(sz))
   259  			require.Equal(t, buf.StartOffset(), writeOffset)
   260  
   261  			b := make([]byte, sz)
   262  			rand.Read(b)
   263  
   264  			copy(buf.Bytes(), b)
   265  			data := buf.Data(buf.StartOffset())
   266  			require.Equal(t, b, data[:sz])
   267  		})
   268  	}
   269  }
   270  
   271  func newTestBuffers(t *testing.T, capacity int) []*Buffer {
   272  	var bufs []*Buffer
   273  
   274  	buf := NewBuffer(capacity, "test")
   275  	bufs = append(bufs, buf)
   276  
   277  	buf, err := NewBufferTmp("", capacity)
   278  	require.NoError(t, err)
   279  	bufs = append(bufs, buf)
   280  
   281  	t.Cleanup(func() {
   282  		for _, buf := range bufs {
   283  			require.NoError(t, buf.Release())
   284  		}
   285  	})
   286  
   287  	return bufs
   288  }
   289  
   290  func TestSmallBuffer(t *testing.T) {
   291  	buf := NewBuffer(5, "test")
   292  	t.Cleanup(func() {
   293  		require.NoError(t, buf.Release())
   294  	})
   295  	// Write something to buffer so sort actually happens.
   296  	buf.WriteSlice([]byte("abc"))
   297  	// This test fails if the buffer has offset > currSz.
   298  	require.NotPanics(t, func() {
   299  		buf.SortSlice(func(left, right []byte) bool {
   300  			return true
   301  		})
   302  	})
   303  }