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