uint24.go raw

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