github.com/ledgerwatch/erigon-lib@v1.0.0/recsplit/eliasfano32/elias_fano_fuzz_test.go (about)

     1  //go:build !nofuzz
     2  
     3  /*
     4  Copyright 2021 Erigon contributors
     5  
     6  Licensed under the Apache License, Version 2.0 (the "License");
     7  you may not use this file except in compliance with the License.
     8  You may obtain a copy of the License at
     9  
    10  	http://www.apache.org/licenses/LICENSE-2.0
    11  
    12  Unless required by applicable law or agreed to in writing, software
    13  distributed under the License is distributed on an "AS IS" BASIS,
    14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15  See the License for the specific language governing permissions and
    16  limitations under the License.
    17  */
    18  package eliasfano32
    19  
    20  import (
    21  	"bytes"
    22  	"testing"
    23  )
    24  
    25  // go test -trimpath -v -fuzz=FuzzSingleEliasFano ./recsplit/eliasfano32
    26  // go test -trimpath -v -fuzz=FuzzDoubleEliasFano ./recsplit/eliasfano32
    27  
    28  func FuzzSingleEliasFano(f *testing.F) {
    29  	f.Fuzz(func(t *testing.T, in []byte) {
    30  		if len(in)%2 == 1 {
    31  			t.Skip()
    32  		}
    33  		if len(in) == 0 {
    34  			t.Skip()
    35  		}
    36  		for len(in) < int(2*superQ) { // make input large enough to trigger supreQ jump logic
    37  			in = append(in, in...)
    38  		}
    39  
    40  		// Treat each byte of the sequence as difference between previous value and the next
    41  		count := len(in)
    42  		keys := make([]uint64, count+1)
    43  		for i, b := range in {
    44  			keys[i+1] = keys[i] + uint64(b)
    45  		}
    46  		ef := NewEliasFano(uint64(count+1), keys[count])
    47  		for _, c := range keys {
    48  			ef.AddOffset(c)
    49  		}
    50  		ef.Build()
    51  
    52  		// Try to read from ef
    53  		for i := 0; i < count; i++ {
    54  			if ef.Get(uint64(i)) != keys[i] {
    55  				t.Fatalf("i %d: got %d, expected %d", i, ef.Get(uint64(i)), keys[i])
    56  			}
    57  		}
    58  
    59  		it := ef.Iterator()
    60  		for it.HasNext() {
    61  			it.Next()
    62  		}
    63  		buf := bytes.NewBuffer(nil)
    64  		ef.Write(buf)
    65  		if ef.Max() != Max(buf.Bytes()) {
    66  			t.Fatalf("max: got %d, expected %d", ef.Max(), Max(buf.Bytes()))
    67  		}
    68  		if ef.Min() != Min(buf.Bytes()) {
    69  			t.Fatalf("min: got %d, expected %d", ef.Min(), Min(buf.Bytes()))
    70  		}
    71  		if ef.Count() != Count(buf.Bytes()) {
    72  			t.Fatalf("max: got %d, expected %d", ef.Count(), Count(buf.Bytes()))
    73  		}
    74  	})
    75  }
    76  
    77  func FuzzDoubleEliasFano(f *testing.F) {
    78  	f.Fuzz(func(t *testing.T, in []byte) {
    79  		if len(in)%2 == 1 {
    80  			t.Skip()
    81  		}
    82  		if len(in) == 0 {
    83  			t.Skip()
    84  		}
    85  		for len(in) < int(2*superQ) { // make input large enough to trigger supreQ jump logic
    86  			in = append(in, in...)
    87  		}
    88  
    89  		var ef DoubleEliasFano
    90  		// Treat each byte of the sequence as difference between previous value and the next
    91  		numBuckets := len(in) / 2
    92  		cumKeys := make([]uint64, numBuckets+1)
    93  		position := make([]uint64, numBuckets+1)
    94  		for i, b := range in[:numBuckets] {
    95  			cumKeys[i+1] = cumKeys[i] + uint64(b)
    96  		}
    97  		for i, b := range in[numBuckets:] {
    98  			position[i+1] = position[i] + uint64(b)
    99  		}
   100  		ef1 := NewEliasFano(uint64(numBuckets+1), cumKeys[numBuckets])
   101  		for _, c := range cumKeys {
   102  			ef1.AddOffset(c)
   103  		}
   104  		ef1.Build()
   105  		ef2 := NewEliasFano(uint64(numBuckets+1), position[numBuckets])
   106  		for _, p := range position {
   107  			ef2.AddOffset(p)
   108  		}
   109  		ef2.Build()
   110  		ef.Build(cumKeys, position)
   111  		// Try to read from ef
   112  		for bucket := 0; bucket < numBuckets; bucket++ {
   113  			cumKey, bitPos := ef.Get2(uint64(bucket))
   114  			if cumKey != cumKeys[bucket] {
   115  				t.Fatalf("bucket %d: cumKey from EF = %d, expected %d", bucket, cumKey, cumKeys[bucket])
   116  			}
   117  			if bitPos != position[bucket] {
   118  				t.Fatalf("bucket %d: position from EF = %d, expected %d", bucket, bitPos, position[bucket])
   119  			}
   120  			cumKey = ef1.Get(uint64(bucket))
   121  			if cumKey != cumKeys[bucket] {
   122  				t.Fatalf("bucket %d: cumKey from EF1 = %d, expected %d", bucket, cumKey, cumKeys[bucket])
   123  			}
   124  			bitPos = ef2.Get(uint64(bucket))
   125  			if bitPos != position[bucket] {
   126  				t.Fatalf("bucket %d: position from EF2 = %d, expected %d", bucket, bitPos, position[bucket])
   127  			}
   128  		}
   129  		for bucket := 0; bucket < numBuckets; bucket++ {
   130  			cumKey, cumKeysNext, bitPos := ef.Get3(uint64(bucket))
   131  			if cumKey != cumKeys[bucket] {
   132  				t.Fatalf("bucket %d: cumKey from EF = %d, expected %d", bucket, cumKey, cumKeys[bucket])
   133  			}
   134  			if bitPos != position[bucket] {
   135  				t.Fatalf("bucket %d: position from EF = %d, expected %d", bucket, bitPos, position[bucket])
   136  			}
   137  			if cumKeysNext != cumKeys[bucket+1] {
   138  				t.Fatalf("bucket %d: cumKeysNext from EF = %d, expected %d", bucket, cumKeysNext, cumKeys[bucket+1])
   139  			}
   140  		}
   141  	})
   142  }