github.com/MetalBlockchain/metalgo@v1.11.9/utils/wrappers/packing.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package wrappers
     5  
     6  import (
     7  	"encoding/binary"
     8  	"errors"
     9  	"math"
    10  )
    11  
    12  const (
    13  	MaxStringLen = math.MaxUint16
    14  
    15  	// ByteLen is the number of bytes per byte...
    16  	ByteLen = 1
    17  	// ShortLen is the number of bytes per short
    18  	ShortLen = 2
    19  	// IntLen is the number of bytes per int
    20  	IntLen = 4
    21  	// LongLen is the number of bytes per long
    22  	LongLen = 8
    23  	// BoolLen is the number of bytes per bool
    24  	BoolLen = 1
    25  )
    26  
    27  func StringLen(str string) int {
    28  	// note: there is a max length for string ([MaxStringLen])
    29  	// we defer to PackString checking whether str is within limits
    30  	return ShortLen + len(str)
    31  }
    32  
    33  var (
    34  	ErrInsufficientLength = errors.New("packer has insufficient length for input")
    35  	errNegativeOffset     = errors.New("negative offset")
    36  	errInvalidInput       = errors.New("input does not match expected format")
    37  	errBadBool            = errors.New("unexpected value when unpacking bool")
    38  	errOversized          = errors.New("size is larger than limit")
    39  )
    40  
    41  // Packer packs and unpacks a byte array from/to standard values
    42  type Packer struct {
    43  	Errs
    44  
    45  	// The largest allowed size of expanding the byte array
    46  	MaxSize int
    47  	// The current byte array
    48  	Bytes []byte
    49  	// The offset that is being written to in the byte array
    50  	Offset int
    51  }
    52  
    53  // PackByte append a byte to the byte array
    54  func (p *Packer) PackByte(val byte) {
    55  	p.expand(ByteLen)
    56  	if p.Errored() {
    57  		return
    58  	}
    59  
    60  	p.Bytes[p.Offset] = val
    61  	p.Offset++
    62  }
    63  
    64  // UnpackByte unpack a byte from the byte array
    65  func (p *Packer) UnpackByte() byte {
    66  	p.checkSpace(ByteLen)
    67  	if p.Errored() {
    68  		return 0
    69  	}
    70  
    71  	val := p.Bytes[p.Offset]
    72  	p.Offset += ByteLen
    73  	return val
    74  }
    75  
    76  // PackShort append a short to the byte array
    77  func (p *Packer) PackShort(val uint16) {
    78  	p.expand(ShortLen)
    79  	if p.Errored() {
    80  		return
    81  	}
    82  
    83  	binary.BigEndian.PutUint16(p.Bytes[p.Offset:], val)
    84  	p.Offset += ShortLen
    85  }
    86  
    87  // UnpackShort unpack a short from the byte array
    88  func (p *Packer) UnpackShort() uint16 {
    89  	p.checkSpace(ShortLen)
    90  	if p.Errored() {
    91  		return 0
    92  	}
    93  
    94  	val := binary.BigEndian.Uint16(p.Bytes[p.Offset:])
    95  	p.Offset += ShortLen
    96  	return val
    97  }
    98  
    99  // PackInt append an int to the byte array
   100  func (p *Packer) PackInt(val uint32) {
   101  	p.expand(IntLen)
   102  	if p.Errored() {
   103  		return
   104  	}
   105  
   106  	binary.BigEndian.PutUint32(p.Bytes[p.Offset:], val)
   107  	p.Offset += IntLen
   108  }
   109  
   110  // UnpackInt unpack an int from the byte array
   111  func (p *Packer) UnpackInt() uint32 {
   112  	p.checkSpace(IntLen)
   113  	if p.Errored() {
   114  		return 0
   115  	}
   116  
   117  	val := binary.BigEndian.Uint32(p.Bytes[p.Offset:])
   118  	p.Offset += IntLen
   119  	return val
   120  }
   121  
   122  // PackLong append a long to the byte array
   123  func (p *Packer) PackLong(val uint64) {
   124  	p.expand(LongLen)
   125  	if p.Errored() {
   126  		return
   127  	}
   128  
   129  	binary.BigEndian.PutUint64(p.Bytes[p.Offset:], val)
   130  	p.Offset += LongLen
   131  }
   132  
   133  // UnpackLong unpack a long from the byte array
   134  func (p *Packer) UnpackLong() uint64 {
   135  	p.checkSpace(LongLen)
   136  	if p.Errored() {
   137  		return 0
   138  	}
   139  
   140  	val := binary.BigEndian.Uint64(p.Bytes[p.Offset:])
   141  	p.Offset += LongLen
   142  	return val
   143  }
   144  
   145  // PackBool packs a bool into the byte array
   146  func (p *Packer) PackBool(b bool) {
   147  	if b {
   148  		p.PackByte(1)
   149  	} else {
   150  		p.PackByte(0)
   151  	}
   152  }
   153  
   154  // UnpackBool unpacks a bool from the byte array
   155  func (p *Packer) UnpackBool() bool {
   156  	b := p.UnpackByte()
   157  	switch b {
   158  	case 0:
   159  		return false
   160  	case 1:
   161  		return true
   162  	default:
   163  		p.Add(errBadBool)
   164  		return false
   165  	}
   166  }
   167  
   168  // PackFixedBytes append a byte slice, with no length descriptor to the byte
   169  // array
   170  func (p *Packer) PackFixedBytes(bytes []byte) {
   171  	p.expand(len(bytes))
   172  	if p.Errored() {
   173  		return
   174  	}
   175  
   176  	copy(p.Bytes[p.Offset:], bytes)
   177  	p.Offset += len(bytes)
   178  }
   179  
   180  // UnpackFixedBytes unpack a byte slice, with no length descriptor from the byte
   181  // array
   182  func (p *Packer) UnpackFixedBytes(size int) []byte {
   183  	p.checkSpace(size)
   184  	if p.Errored() {
   185  		return nil
   186  	}
   187  
   188  	bytes := p.Bytes[p.Offset : p.Offset+size]
   189  	p.Offset += size
   190  	return bytes
   191  }
   192  
   193  // PackBytes append a byte slice to the byte array
   194  func (p *Packer) PackBytes(bytes []byte) {
   195  	p.PackInt(uint32(len(bytes)))
   196  	p.PackFixedBytes(bytes)
   197  }
   198  
   199  // UnpackBytes unpack a byte slice from the byte array
   200  func (p *Packer) UnpackBytes() []byte {
   201  	size := p.UnpackInt()
   202  	return p.UnpackFixedBytes(int(size))
   203  }
   204  
   205  // UnpackLimitedBytes unpacks a byte slice. If the size of the slice is greater
   206  // than [limit], adds [errOversized] to the packer and returns nil.
   207  func (p *Packer) UnpackLimitedBytes(limit uint32) []byte {
   208  	size := p.UnpackInt()
   209  	if size > limit {
   210  		p.Add(errOversized)
   211  		return nil
   212  	}
   213  	return p.UnpackFixedBytes(int(size))
   214  }
   215  
   216  // PackStr append a string to the byte array
   217  func (p *Packer) PackStr(str string) {
   218  	strSize := len(str)
   219  	if strSize > MaxStringLen {
   220  		p.Add(errInvalidInput)
   221  		return
   222  	}
   223  	p.PackShort(uint16(strSize))
   224  	p.PackFixedBytes([]byte(str))
   225  }
   226  
   227  // UnpackStr unpacks a string from the byte array
   228  func (p *Packer) UnpackStr() string {
   229  	strSize := p.UnpackShort()
   230  	return string(p.UnpackFixedBytes(int(strSize)))
   231  }
   232  
   233  // UnpackLimitedStr unpacks a string. If the size of the string is greater than
   234  // [limit], adds [errOversized] to the packer and returns the empty string.
   235  func (p *Packer) UnpackLimitedStr(limit uint16) string {
   236  	strSize := p.UnpackShort()
   237  	if strSize > limit {
   238  		p.Add(errOversized)
   239  		return ""
   240  	}
   241  	return string(p.UnpackFixedBytes(int(strSize)))
   242  }
   243  
   244  // checkSpace requires that there is at least [bytes] of write space left in the
   245  // byte array. If this is not true, an error is added to the packer
   246  func (p *Packer) checkSpace(bytes int) {
   247  	switch {
   248  	case p.Offset < 0:
   249  		p.Add(errNegativeOffset)
   250  	case bytes < 0:
   251  		p.Add(errInvalidInput)
   252  	case len(p.Bytes)-p.Offset < bytes:
   253  		p.Add(ErrInsufficientLength)
   254  	}
   255  }
   256  
   257  // expand ensures that there is [bytes] bytes left of space in the byte slice.
   258  // If this is not allowed due to the maximum size, an error is added to the packer
   259  // In order to understand this code, its important to understand the difference
   260  // between a slice's length and its capacity.
   261  func (p *Packer) expand(bytes int) {
   262  	neededSize := bytes + p.Offset // Need byte slice's length to be at least [neededSize]
   263  	switch {
   264  	case neededSize <= len(p.Bytes): // Byte slice has sufficient length already
   265  		return
   266  	case neededSize > p.MaxSize: // Lengthening the byte slice would cause it to grow too large
   267  		p.Err = ErrInsufficientLength
   268  		return
   269  	case neededSize <= cap(p.Bytes): // Byte slice has sufficient capacity to lengthen it without mem alloc
   270  		p.Bytes = p.Bytes[:neededSize]
   271  		return
   272  	default: // Add capacity/length to byte slice
   273  		p.Bytes = append(p.Bytes[:cap(p.Bytes)], make([]byte, neededSize-cap(p.Bytes))...)
   274  	}
   275  }