github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/ledger/common/bitutils/utils_test.go (about)

     1  package bitutils
     2  
     3  import (
     4  	crand "crypto/rand"
     5  	"math/big"
     6  	"math/bits"
     7  	"math/rand"
     8  	"time"
     9  
    10  	"testing"
    11  
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  )
    15  
    16  func TestBitVectorAllocation(t *testing.T) {
    17  	for bits := 0; bits <= 127; bits++ {
    18  		numBytes := bits / 8 // integer division with floor
    19  		if bits%8 > 0 {
    20  			numBytes += 1
    21  		}
    22  
    23  		vect := MakeBitVector(bits)
    24  		assert.Equal(t, numBytes, len(vect))
    25  	}
    26  }
    27  
    28  // Test_PaddedByteSliceLength tests that MinimalByteSliceLength returns the
    29  func Test_PaddedByteSliceLength(t *testing.T) {
    30  	for bits := 0; bits <= 127; bits++ {
    31  		numBytes := bits / 8 // integer division with floor
    32  		if bits%8 > 0 {
    33  			numBytes += 1
    34  		}
    35  
    36  		assert.Equal(t, numBytes, MinimalByteSliceLength(bits))
    37  	}
    38  }
    39  
    40  func TestBitTools(t *testing.T) {
    41  	seed := time.Now().UnixNano()
    42  	t.Logf("rand seed is %d", seed)
    43  	r := rand.NewSource(seed)
    44  
    45  	const maxBits = 131 * 8 // upper bound of indices to test
    46  	var b big.Int
    47  
    48  	t.Run("testing ReadBit", func(t *testing.T) {
    49  		var max big.Int
    50  		// set max to 2^maxBits
    51  		max.SetBit(&max, maxBits, 1)
    52  		// random big int less that 2^maxBits
    53  		b.Rand(rand.New(r), &max)
    54  		maxBitsLen := (maxBits + 7) / 8 // length in bytes needed for maxbits
    55  		bytes := make([]byte, maxBitsLen)
    56  		copy(bytes[maxBitsLen-len(b.Bytes()):], b.Bytes())
    57  
    58  		// reverse the endianness (both bits and bytes)
    59  		for j := 0; j < len(bytes)/2; j++ {
    60  			bytes[j], bytes[len(bytes)-j-1] = bytes[len(bytes)-j-1], bytes[j]
    61  		}
    62  		for j := 0; j < len(bytes); j++ {
    63  			bytes[j] = bits.Reverse8(bytes[j])
    64  		}
    65  		// test bit reads are equal for all indices
    66  		for i := 0; i < maxBits; i++ {
    67  			bit := int(b.Bit(i))
    68  			assert.Equal(t, bit, ReadBit(bytes, i))
    69  		}
    70  	})
    71  
    72  	t.Run("testing WriteBit", func(t *testing.T) {
    73  		b.SetInt64(0)
    74  		bytes := MakeBitVector(maxBits)
    75  		_, err := crand.Read(bytes) // fill bytes with random values to verify that writing to each individual bit works
    76  		require.NoError(t, err)
    77  
    78  		// build a random big bit by bit
    79  		for idx := 0; idx < maxBits; idx++ {
    80  			bit := rand.Intn(2)
    81  			// b = 2*b + bit
    82  			b.Lsh(&b, 1)
    83  			b.Add(&b, big.NewInt(int64(bit)))
    84  			WriteBit(bytes, idx, bit) // sets the bit at index `idx`
    85  		}
    86  
    87  		// get the big int from the random slice
    88  		var randomBig big.Int
    89  		randomBig.SetBytes(bytes)
    90  		assert.Equal(t, 0, randomBig.Cmp(&b))
    91  	})
    92  
    93  	t.Run("testing ClearBit and SetBit", func(t *testing.T) {
    94  		b.SetInt64(0)
    95  		bytes := MakeBitVector(maxBits)
    96  		_, err := crand.Read(bytes) // fill bytes with random values to verify that writing to each individual bit works
    97  		require.NoError(t, err)
    98  
    99  		// build a random big bit by bit
   100  		for idx := 0; idx < maxBits; idx++ {
   101  			bit := rand.Intn(2)
   102  			// b = 2*b + bit
   103  			b.Lsh(&b, 1)
   104  			b.Add(&b, big.NewInt(int64(bit)))
   105  			// sets the bit at index i
   106  			if bit == 1 {
   107  				SetBit(bytes, idx)
   108  			} else {
   109  				ClearBit(bytes, idx)
   110  			}
   111  		}
   112  
   113  		// get the big int from the random slice
   114  		var randomBig big.Int
   115  		randomBig.SetBytes(bytes)
   116  		assert.Equal(t, 0, randomBig.Cmp(&b))
   117  	})
   118  }