github.com/protolambda/zssz@v0.1.5/types/ssz_bitlist.go (about)

     1  package types
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/protolambda/zssz/bitfields"
     6  	. "github.com/protolambda/zssz/dec"
     7  	. "github.com/protolambda/zssz/enc"
     8  	. "github.com/protolambda/zssz/htr"
     9  	"github.com/protolambda/zssz/merkle"
    10  	. "github.com/protolambda/zssz/pretty"
    11  	"github.com/protolambda/zssz/util/ptrutil"
    12  	"reflect"
    13  	"unsafe"
    14  )
    15  
    16  type SSZBitlist struct {
    17  	bitLimit  uint64
    18  	byteLimit uint64 // exclusive delimiting bit
    19  	leafLimit uint64 // exclusive delimiting bit
    20  }
    21  
    22  var bitlistMeta = reflect.TypeOf((*bitfields.BitlistMeta)(nil)).Elem()
    23  
    24  func NewSSZBitlist(typ reflect.Type) (*SSZBitlist, error) {
    25  	if typ.Kind() != reflect.Slice {
    26  		return nil, fmt.Errorf("typ is not a dynamic-length bytes slice (bitlist requirement)")
    27  	}
    28  	if typ.Elem().Kind() != reflect.Uint8 {
    29  		return nil, fmt.Errorf("typ is not a bytes slice (bitlist requirement)")
    30  	}
    31  	bitLimit, err := ReadListLimit(typ)
    32  	if err != nil {
    33  		return nil, err
    34  	}
    35  	byteLimit := (bitLimit + 7) >> 3
    36  	res := &SSZBitlist{
    37  		bitLimit:  bitLimit,
    38  		byteLimit: byteLimit,
    39  		leafLimit: (byteLimit + 31) >> 5,
    40  	}
    41  	return res, nil
    42  }
    43  
    44  // in bytes (rounded up), not bits
    45  func (v *SSZBitlist) FuzzMinLen() uint64 {
    46  	// 8 for a random byte count, 1 for a random leading byte
    47  	return 8 + 1
    48  }
    49  
    50  // in bytes (rounded up), not bits
    51  func (v *SSZBitlist) FuzzMaxLen() uint64 {
    52  	// 8 for a random byte count, limit for maximum fill
    53  	return 8 + v.byteLimit
    54  }
    55  
    56  // in bytes (rounded up), not bits. Includes the delimiting 1 bit.
    57  func (v *SSZBitlist) MinLen() uint64 {
    58  	// leading bit to mark it the 0 length makes it 1 byte.
    59  	return 1
    60  }
    61  
    62  // in bytes (rounded up), not bits
    63  func (v *SSZBitlist) MaxLen() uint64 {
    64  	return (v.bitLimit >> 3) + 1
    65  }
    66  
    67  // in bytes (rounded up), not bits
    68  func (v *SSZBitlist) FixedLen() uint64 {
    69  	return 0
    70  }
    71  
    72  func (v *SSZBitlist) IsFixed() bool {
    73  	return false
    74  }
    75  
    76  func (v *SSZBitlist) SizeOf(p unsafe.Pointer) uint64 {
    77  	sh := ptrutil.ReadSliceHeader(p)
    78  	return uint64(sh.Len)
    79  }
    80  
    81  func (v *SSZBitlist) Encode(eb *EncodingWriter, p unsafe.Pointer) error {
    82  	sh := ptrutil.ReadSliceHeader(p)
    83  	data := *(*[]byte)(unsafe.Pointer(sh))
    84  	return eb.Write(data)
    85  }
    86  
    87  func (v *SSZBitlist) Decode(dr *DecodingReader, p unsafe.Pointer) error {
    88  	var byteLen uint64
    89  	if dr.IsFuzzMode() {
    90  		x, err := dr.ReadUint64()
    91  		if err != nil {
    92  			return err
    93  		}
    94  		// get span to fill with available space
    95  		span := dr.GetBytesSpan() - 1
    96  		// respect type limit
    97  		if span > v.byteLimit {
    98  			span = v.byteLimit
    99  		}
   100  		if span != 0 {
   101  			byteLen = x % span
   102  		}
   103  		// completely empty bitlists are invalid. Need a leading 1 bit.
   104  		byteLen += 1
   105  	} else {
   106  		byteLen = dr.GetBytesSpan()
   107  	}
   108  	ptrutil.BytesAllocFn(p, byteLen)
   109  	data := *(*[]byte)(p)
   110  	if _, err := dr.Read(data); err != nil {
   111  		return err
   112  	}
   113  	if dr.IsFuzzMode() {
   114  		// mask last byte to stay within bit-limit
   115  		data[len(data)-1] &= (1 << (v.bitLimit + 1)) - 1
   116  		if data[len(data)-1] == 0 {
   117  			// last byte must not be 0 for bitlist to be valid
   118  			data[len(data)-1] = 1
   119  		}
   120  	}
   121  	// check if the data is a valid bitvector value (0 bits for unused bits)
   122  	return bitfields.BitlistCheck(data, v.bitLimit)
   123  }
   124  
   125  func (v *SSZBitlist) DryCheck(dr *DecodingReader) error {
   126  	span := dr.GetBytesSpan()
   127  	if err := bitfields.BitlistCheckByteLen(span, v.bitLimit); err != nil {
   128  		return err
   129  	}
   130  	// 0 span is already checked by BitlistCheckByteLen
   131  	if _, err := dr.Skip(span - 1); err != nil {
   132  		return err
   133  	}
   134  	last, err := dr.ReadByte()
   135  	if err != nil {
   136  		return err
   137  	}
   138  	return bitfields.BitlistCheckLastByte(last, v.bitLimit-((span-1)<<3))
   139  }
   140  
   141  func (v *SSZBitlist) HashTreeRoot(h MerkleFn, p unsafe.Pointer) [32]byte {
   142  	sh := ptrutil.ReadSliceHeader(p)
   143  	data := *(*[]byte)(unsafe.Pointer(sh))
   144  	bitLen := bitfields.BitlistLen(data)
   145  	byteLen := (bitLen + 7) >> 3
   146  	leafCount := (byteLen + 31) >> 5
   147  	leaf := func(i uint64) []byte {
   148  		s := i << 5
   149  		e := (i + 1) << 5
   150  		// pad the data
   151  		if e > byteLen {
   152  			x := [32]byte{}
   153  			copy(x[:], data[s:byteLen])
   154  			if bitLen&7 != 0 && byteLen != 0 { // if we not already cut off the delimiting bit with a bytes boundary
   155  				// find the index of the length-determining 1 bit (bitlist length == index of this bit)
   156  				delimitByteIndex := (byteLen - 1) & 31
   157  				mask := ^(byte(1) << bitfields.BitIndex(x[delimitByteIndex]))
   158  				// zero out the length bit.
   159  				x[delimitByteIndex] &= mask
   160  			}
   161  			return x[:]
   162  		}
   163  		// if the last leaf does not have to be padded,
   164  		// then the length-determining bitlist bit is already cut off,
   165  		// i.e. as sole bit in next (ignored) chunk of data.
   166  		return data[s:e]
   167  	}
   168  	return h.MixIn(merkle.Merkleize(h, leafCount, v.leafLimit, leaf), bitLen)
   169  }
   170  
   171  func (v *SSZBitlist) Pretty(indent uint32, w *PrettyWriter, p unsafe.Pointer) {
   172  	w.WriteIndent(indent)
   173  	sh := ptrutil.ReadSliceHeader(p)
   174  	data := *(*[]byte)(unsafe.Pointer(sh))
   175  	w.Write(fmt.Sprintf("%08b", data))
   176  }