github.com/ledgerwatch/erigon-lib@v1.0.0/recsplit/eliasfano32/elias_fano_test.go (about) 1 /* 2 Copyright 2022 Erigon 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 eliasfano32 18 19 import ( 20 "bytes" 21 "math" 22 "math/bits" 23 "testing" 24 25 "github.com/ledgerwatch/erigon-lib/kv/iter" 26 "github.com/stretchr/testify/assert" 27 "github.com/stretchr/testify/require" 28 ) 29 30 func TestEliasFanoSeek(t *testing.T) { 31 count := uint64(1_000_000) 32 maxOffset := (count - 1) * 123 33 ef := NewEliasFano(count, maxOffset) 34 vals := make([]uint64, 0, count) 35 for offset := uint64(0); offset < count; offset++ { 36 val := offset * 123 37 vals = append(vals, val) 38 ef.AddOffset(val) 39 } 40 ef.Build() 41 42 t.Run("iter match vals", func(t *testing.T) { 43 it := ef.Iterator() 44 for i := 0; it.HasNext(); i++ { 45 n, err := it.Next() 46 require.NoError(t, err) 47 require.Equal(t, int(vals[i]), int(n)) 48 } 49 }) 50 t.Run("iter grow", func(t *testing.T) { 51 it := ef.Iterator() 52 prev, _ := it.Next() 53 for it.HasNext() { 54 n, _ := it.Next() 55 require.GreaterOrEqual(t, int(n), int(prev)) 56 } 57 }) 58 59 { 60 v2, ok2 := ef.Search(ef.Max()) 61 require.True(t, ok2, v2) 62 require.Equal(t, ef.Max(), v2) 63 it := ef.Iterator() 64 //it.SeekDeprecated(ef.Max()) 65 for i := 0; i < int(ef.Count()-1); i++ { 66 it.Next() 67 } 68 //save all fields values 69 //v1, v2, v3, v4, v5 := it.upperIdx, it.upperMask, it.lowerIdx, it.upper, it.idx 70 // seek to same item and check new fields 71 it.Seek(ef.Max()) 72 //require.Equal(t, int(v1), int(it.upperIdx)) 73 //require.Equal(t, int(v3), int(it.lowerIdx)) 74 //require.Equal(t, int(v5), int(it.idx)) 75 //require.Equal(t, bits.TrailingZeros64(v2), bits.TrailingZeros64(it.upperMask)) 76 //require.Equal(t, int(v4), int(it.upper)) 77 78 require.True(t, it.HasNext(), v2) 79 itV, err := it.Next() 80 require.NoError(t, err) 81 require.Equal(t, int(ef.Max()), int(itV)) 82 } 83 84 { 85 v2, ok2 := ef.Search(ef.Min()) 86 require.True(t, ok2, v2) 87 require.Equal(t, int(ef.Min()), int(v2)) 88 it := ef.Iterator() 89 it.Seek(ef.Min()) 90 require.True(t, it.HasNext(), v2) 91 itV, err := it.Next() 92 require.NoError(t, err) 93 require.Equal(t, int(ef.Min()), int(itV)) 94 } 95 96 { 97 v2, ok2 := ef.Search(0) 98 require.True(t, ok2, v2) 99 require.Equal(t, int(ef.Min()), int(v2)) 100 it := ef.Iterator() 101 it.Seek(0) 102 require.True(t, it.HasNext(), v2) 103 itV, err := it.Next() 104 require.NoError(t, err) 105 require.Equal(t, int(ef.Min()), int(itV)) 106 } 107 108 { 109 v2, ok2 := ef.Search(math.MaxUint32) 110 require.False(t, ok2, v2) 111 it := ef.Iterator() 112 it.Seek(math.MaxUint32) 113 require.False(t, it.HasNext(), v2) 114 } 115 116 { 117 v2, ok2 := ef.Search((count+1)*123 + 1) 118 require.False(t, ok2, v2) 119 it := ef.Iterator() 120 it.Seek((count+1)*123 + 1) 121 require.False(t, it.HasNext(), v2) 122 } 123 124 t.Run("search and seek can't return smaller", func(t *testing.T) { 125 for i := uint64(0); i < count; i++ { 126 search := i * 123 127 v, ok2 := ef.Search(search) 128 require.True(t, ok2, search) 129 require.GreaterOrEqual(t, int(v), int(search)) 130 it := ef.Iterator() 131 it.Seek(search) 132 itV, err := it.Next() 133 require.NoError(t, err) 134 require.GreaterOrEqual(t, int(itV), int(search), int(v)) 135 } 136 }) 137 138 } 139 140 func TestEliasFano(t *testing.T) { 141 offsets := []uint64{1, 4, 6, 8, 10, 14, 16, 19, 22, 34, 37, 39, 41, 43, 48, 51, 54, 58, 62} 142 count := uint64(len(offsets)) 143 maxOffset := offsets[0] 144 for _, offset := range offsets { 145 if offset > maxOffset { 146 maxOffset = offset 147 } 148 } 149 ef := NewEliasFano(count, maxOffset) 150 for _, offset := range offsets { 151 ef.AddOffset(offset) 152 } 153 ef.Build() 154 for i, offset := range offsets { 155 offset1 := ef.Get(uint64(i)) 156 assert.Equal(t, offset, offset1, "offset") 157 } 158 v, ok := ef.Search(37) 159 assert.True(t, ok, "search1") 160 assert.Equal(t, uint64(37), v, "search1") 161 v, ok = ef.Search(0) 162 assert.True(t, ok, "search2") 163 assert.Equal(t, uint64(1), v, "search2") 164 _, ok = ef.Search(100) 165 assert.False(t, ok, "search3") 166 v, ok = ef.Search(11) 167 assert.True(t, ok, "search4") 168 assert.Equal(t, uint64(14), v, "search4") 169 170 buf := bytes.NewBuffer(nil) 171 ef.Write(buf) 172 assert.Equal(t, ef.AppendBytes(nil), buf.Bytes()) 173 174 ef2, _ := ReadEliasFano(buf.Bytes()) 175 assert.Equal(t, ef.Min(), ef2.Min()) 176 assert.Equal(t, ef.Max(), ef2.Max()) 177 assert.Equal(t, ef2.Max(), Max(buf.Bytes())) 178 assert.Equal(t, ef2.Min(), Min(buf.Bytes())) 179 assert.Equal(t, ef2.Count(), Count(buf.Bytes())) 180 } 181 182 func TestIterator(t *testing.T) { 183 offsets := []uint64{1, 4, 6, 8, 10, 14, 16, 19, 22, 34, 37, 39, 41, 43, 48, 51, 54, 58, 62} 184 count := uint64(len(offsets)) 185 maxOffset := offsets[0] 186 for _, offset := range offsets { 187 if offset > maxOffset { 188 maxOffset = offset 189 } 190 } 191 ef := NewEliasFano(count, maxOffset) 192 for _, offset := range offsets { 193 ef.AddOffset(offset) 194 } 195 ef.Build() 196 t.Run("scan", func(t *testing.T) { 197 efi := ef.Iterator() 198 i := 0 199 var values []uint64 200 for efi.HasNext() { 201 v, _ := efi.Next() 202 values = append(values, v) 203 assert.Equal(t, offsets[i], v, "iter") 204 i++ 205 } 206 iter.ExpectEqualU64(t, iter.ReverseArray(values), ef.ReverseIterator()) 207 }) 208 209 t.Run("seek", func(t *testing.T) { 210 iter2 := ef.Iterator() 211 iter2.Seek(2) 212 n, err := iter2.Next() 213 require.NoError(t, err) 214 require.Equal(t, 4, int(n)) 215 216 iter2.Seek(5) 217 n, err = iter2.Next() 218 require.NoError(t, err) 219 require.Equal(t, 6, int(n)) 220 221 iter2.Seek(62) 222 n, err = iter2.Next() 223 require.NoError(t, err) 224 require.Equal(t, 62, int(n)) 225 226 iter2.Seek(1024) 227 require.False(t, iter2.HasNext()) 228 }) 229 } 230 231 func TestIteratorAndSeekAreBasedOnSameFields(t *testing.T) { 232 vals := []uint64{1, 123, 789} 233 ef := NewEliasFano(uint64(len(vals)), vals[len(vals)-1]) 234 for _, v := range vals { 235 ef.AddOffset(v) 236 } 237 ef.Build() 238 239 for i := range vals { 240 checkSeek(t, i, ef, vals) 241 } 242 } 243 244 func checkSeek(t *testing.T, j int, ef *EliasFano, vals []uint64) { 245 t.Helper() 246 efi := ef.Iterator() 247 // drain iterator to given item 248 for i := 0; i < j; i++ { 249 efi.Next() 250 } 251 //save all fields values 252 v1, v2, v3, v4, v5 := efi.upperIdx, efi.upperMask, efi.lowerIdx, efi.upper, efi.idx 253 // seek to same item and check new fields 254 efi.Seek(vals[j]) 255 require.Equal(t, int(v1), int(efi.upperIdx)) 256 require.Equal(t, int(v3), int(efi.lowerIdx)) 257 require.Equal(t, int(v4), int(efi.upper)) 258 require.Equal(t, int(v5), int(efi.idx)) 259 require.Equal(t, bits.TrailingZeros64(v2), bits.TrailingZeros64(efi.upperMask)) 260 } 261 262 func BenchmarkName(b *testing.B) { 263 count := uint64(1_000_000) 264 maxOffset := (count - 1) * 123 265 ef := NewEliasFano(count, maxOffset) 266 for offset := uint64(0); offset < count; offset++ { 267 ef.AddOffset(offset * 123) 268 } 269 ef.Build() 270 b.Run("next", func(b *testing.B) { 271 for i := 0; i < b.N; i++ { 272 it := ef.Iterator() 273 for it.HasNext() { 274 n, _ := it.Next() 275 if n > 1_000_000 { 276 break 277 } 278 } 279 } 280 }) 281 b.Run("seek", func(b *testing.B) { 282 for i := 0; i < b.N; i++ { 283 it := ef.Iterator() 284 it.SeekDeprecated(1_000_000) 285 } 286 }) 287 b.Run("seek2", func(b *testing.B) { 288 for i := 0; i < b.N; i++ { 289 it := ef.Iterator() 290 it.Seek(1_000_000) 291 } 292 }) 293 }