uint40.go raw

   1  package types
   2  
   3  import (
   4  	"errors"
   5  	"io"
   6  
   7  	"next.orly.dev/pkg/lol/chk"
   8  )
   9  
  10  // MaxUint40 is the maximum value of a 40-bit unsigned integer: 2^40 - 1.
  11  const MaxUint40 uint64 = 1<<40 - 1
  12  
  13  // Uint40 is a codec for encoding and decoding 40-bit unsigned integers.
  14  type Uint40 struct{ value uint64 }
  15  
  16  // Set sets the value as a 40-bit unsigned integer.
  17  // If the value exceeds the maximum allowable value for 40 bits, it returns an error.
  18  func (c *Uint40) Set(value uint64) error {
  19  	if value > MaxUint40 {
  20  		return errors.New("value exceeds 40-bit range")
  21  	}
  22  	c.value = value
  23  	return nil
  24  }
  25  
  26  // Get gets the value as a 40-bit unsigned integer.
  27  func (c *Uint40) Get() uint64 { return c.value }
  28  
  29  // SetInt sets the value as an int, converting it to a 40-bit unsigned integer.
  30  // If the value is out of the 40-bit range, it returns an error.
  31  func (c *Uint40) SetInt(value int) error {
  32  	if value < 0 || uint64(value) > MaxUint40 {
  33  		return errors.New("value exceeds 40-bit range")
  34  	}
  35  	c.value = uint64(value)
  36  	return nil
  37  }
  38  
  39  // GetInt gets the value as an int, converted from the 40-bit unsigned integer.
  40  // Note: If the value exceeds the int range, it will be truncated.
  41  func (c *Uint40) GetInt() int { return int(c.value) }
  42  
  43  // MarshalWrite encodes the 40-bit unsigned integer and writes it to the provided writer.
  44  // The encoding uses 5 bytes in BigEndian order.
  45  func (c *Uint40) MarshalWrite(w io.Writer) (err error) {
  46  	if c.value > MaxUint40 {
  47  		return errors.New("value exceeds 40-bit range")
  48  	}
  49  	// Fixed array avoids heap escape
  50  	var buf [5]byte
  51  	// Write the upper 5 bytes (ignoring the most significant 3 bytes of uint64)
  52  	buf[0] = byte((c.value >> 32) & 0xFF) // Most significant byte
  53  	buf[1] = byte((c.value >> 24) & 0xFF)
  54  	buf[2] = byte((c.value >> 16) & 0xFF)
  55  	buf[3] = byte((c.value >> 8) & 0xFF)
  56  	buf[4] = byte(c.value & 0xFF) // Least significant byte
  57  	_, err = w.Write(buf[:])
  58  	return err
  59  }
  60  
  61  // UnmarshalRead reads 5 bytes from the provided reader and decodes it into a 40-bit unsigned integer.
  62  func (c *Uint40) UnmarshalRead(r io.Reader) (err error) {
  63  	// Fixed array avoids heap escape
  64  	var buf [5]byte
  65  	_, err = r.Read(buf[:])
  66  	if chk.E(err) {
  67  		return err
  68  	}
  69  	// Decode the 5 bytes into a 40-bit unsigned integer
  70  	c.value = (uint64(buf[0]) << 32) |
  71  		(uint64(buf[1]) << 24) |
  72  		(uint64(buf[2]) << 16) |
  73  		(uint64(buf[3]) << 8) |
  74  		uint64(buf[4])
  75  
  76  	return nil
  77  }
  78  
  79  type Uint40s []*Uint40
  80  
  81  // Union computes the union of the current Uint40s slice with another Uint40s slice. The result
  82  // contains all unique elements from both slices.
  83  func (s Uint40s) Union(other Uint40s) Uint40s {
  84  	totalCap := len(s) + len(other)
  85  	valueMap := make(map[uint64]bool, totalCap)
  86  	result := make(Uint40s, 0, totalCap) // Pre-allocate for worst case
  87  
  88  	// Add elements from the current Uint40s slice to the result
  89  	for _, item := range s {
  90  		val := item.Get()
  91  		if !valueMap[val] {
  92  			valueMap[val] = true
  93  			result = append(result, item)
  94  		}
  95  	}
  96  
  97  	// Add elements from the other Uint40s slice to the result
  98  	for _, item := range other {
  99  		val := item.Get()
 100  		if !valueMap[val] {
 101  			valueMap[val] = true
 102  			result = append(result, item)
 103  		}
 104  	}
 105  
 106  	return result
 107  }
 108  
 109  // Intersection computes the intersection of the current Uint40s slice with another Uint40s
 110  // slice. The result contains only the elements that exist in both slices.
 111  func (s Uint40s) Intersection(other Uint40s) Uint40s {
 112  	// Result can be at most the size of the smaller slice
 113  	smallerLen := len(s)
 114  	if len(other) < smallerLen {
 115  		smallerLen = len(other)
 116  	}
 117  	valueMap := make(map[uint64]bool, len(other))
 118  	result := make(Uint40s, 0, smallerLen) // Pre-allocate for worst case
 119  
 120  	// Add all elements from the other Uint40s slice to the map
 121  	for _, item := range other {
 122  		valueMap[item.Get()] = true
 123  	}
 124  
 125  	// Check for common elements in the current Uint40s slice
 126  	for _, item := range s {
 127  		val := item.Get()
 128  		if valueMap[val] {
 129  			result = append(result, item)
 130  		}
 131  	}
 132  
 133  	return result
 134  }
 135  
 136  // Difference computes the difference of the current Uint40s slice with another Uint40s slice.
 137  // The result contains only the elements that are in the current slice but not in the other
 138  // slice.
 139  func (s Uint40s) Difference(other Uint40s) Uint40s {
 140  	valueMap := make(map[uint64]bool, len(other))
 141  	result := make(Uint40s, 0, len(s)) // Pre-allocate for worst case (no overlap)
 142  
 143  	// Mark all elements in the other Uint40s slice
 144  	for _, item := range other {
 145  		valueMap[item.Get()] = true
 146  	}
 147  
 148  	// Add elements from the current Uint40s slice that are not in the other Uint40s slice
 149  	for _, item := range s {
 150  		val := item.Get()
 151  		if !valueMap[val] {
 152  			result = append(result, item)
 153  		}
 154  	}
 155  
 156  	return result
 157  }
 158