formualizer_eval/engine/
addr.rs1use formualizer_common::Coord as AbsCoord;
32use std::fmt;
33
34const RESERVED_HIGH_MASK: u64 = 0xFFFFF000_00000000;
36
37const SYMBOL_TAG: u64 = 1 << 63;
39
40const SYMBOL_PAYLOAD_MASK: u64 = !RESERVED_HIGH_MASK;
42
43#[repr(transparent)]
48#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
49pub struct GridAddr(AbsCoord);
50
51impl Default for GridAddr {
52 #[inline]
53 fn default() -> Self {
54 Self::new(0, 0)
55 }
56}
57
58impl Ord for GridAddr {
59 #[inline]
60 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
61 (self.row(), self.col()).cmp(&(other.row(), other.col()))
62 }
63}
64
65impl PartialOrd for GridAddr {
66 #[inline]
67 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
68 Some(self.cmp(other))
69 }
70}
71
72impl GridAddr {
73 #[inline]
75 pub fn new(row: u32, col: u32) -> Self {
76 Self(AbsCoord::new(row, col))
77 }
78
79 #[inline]
81 pub const fn from_coord(coord: AbsCoord) -> Self {
82 Self(coord)
83 }
84
85 #[inline]
87 pub const fn coord(self) -> AbsCoord {
88 self.0
89 }
90
91 #[inline]
92 pub fn row(self) -> u32 {
93 self.0.row()
94 }
95
96 #[inline]
97 pub fn col(self) -> u32 {
98 self.0.col()
99 }
100}
101
102impl From<AbsCoord> for GridAddr {
103 #[inline]
104 fn from(coord: AbsCoord) -> Self {
105 Self(coord)
106 }
107}
108
109impl From<GridAddr> for AbsCoord {
110 #[inline]
111 fn from(addr: GridAddr) -> Self {
112 addr.0
113 }
114}
115
116#[repr(transparent)]
122#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
123pub struct SymbolAddr(u32);
124
125impl SymbolAddr {
126 #[inline]
127 pub const fn new(index: u32) -> Self {
128 Self(index)
129 }
130
131 #[inline]
132 pub const fn index(self) -> u32 {
133 self.0
134 }
135}
136
137#[repr(transparent)]
139#[derive(Copy, Clone, PartialEq, Eq, Hash)]
140pub struct VertexAddr(u64);
141
142impl VertexAddr {
143 pub const INVALID: Self = Self(u64::MAX);
145
146 #[inline]
148 pub fn grid(addr: GridAddr) -> Self {
149 Self(addr.0.as_u64())
150 }
151
152 #[inline]
154 pub const fn symbol(addr: SymbolAddr) -> Self {
155 Self(SYMBOL_TAG | (addr.0 as u64))
156 }
157
158 #[inline]
163 pub fn as_grid(self) -> Option<GridAddr> {
164 (self.0 & RESERVED_HIGH_MASK == 0).then(|| GridAddr(unsafe_coord_from_raw(self.0)))
165 }
166
167 #[inline]
169 pub fn as_symbol(self) -> Option<SymbolAddr> {
170 (self.0 & RESERVED_HIGH_MASK == SYMBOL_TAG)
171 .then_some(SymbolAddr((self.0 & SYMBOL_PAYLOAD_MASK) as u32))
172 }
173
174 #[inline]
175 pub fn is_symbol(self) -> bool {
176 self.0 & RESERVED_HIGH_MASK == SYMBOL_TAG
177 }
178
179 #[inline]
180 pub fn is_grid(self) -> bool {
181 self.0 & RESERVED_HIGH_MASK == 0
182 }
183
184 #[inline]
185 pub const fn as_u64(self) -> u64 {
186 self.0
187 }
188
189 #[inline]
195 pub fn order_key(self) -> (u32, u32) {
196 match (self.as_grid(), self.as_symbol()) {
197 (Some(grid), _) => (grid.row(), grid.col()),
198 (_, Some(symbol)) => (u32::MAX, symbol.index()),
199 _ => (u32::MAX, u32::MAX),
200 }
201 }
202}
203
204#[inline]
206fn unsafe_coord_from_raw(raw: u64) -> AbsCoord {
207 debug_assert!(raw & RESERVED_HIGH_MASK == 0);
208 AbsCoord::from_raw(raw).unwrap_or_else(|_| AbsCoord::new(0, 0))
211}
212
213impl From<GridAddr> for VertexAddr {
214 #[inline]
215 fn from(addr: GridAddr) -> Self {
216 Self::grid(addr)
217 }
218}
219
220impl From<SymbolAddr> for VertexAddr {
221 #[inline]
222 fn from(addr: SymbolAddr) -> Self {
223 Self::symbol(addr)
224 }
225}
226
227impl From<AbsCoord> for VertexAddr {
228 #[inline]
229 fn from(coord: AbsCoord) -> Self {
230 Self::grid(GridAddr(coord))
231 }
232}
233
234impl Default for VertexAddr {
235 #[inline]
236 fn default() -> Self {
237 Self::grid(GridAddr::default())
238 }
239}
240
241impl fmt::Debug for VertexAddr {
242 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243 if let Some(grid) = self.as_grid() {
244 write!(f, "Grid(r{}, c{})", grid.row(), grid.col())
245 } else if let Some(symbol) = self.as_symbol() {
246 write!(f, "Symbol({})", symbol.index())
247 } else {
248 write!(f, "VertexAddr::INVALID")
249 }
250 }
251}
252
253#[cfg(test)]
254mod tests {
255 use super::*;
256
257 #[test]
258 fn vertex_addr_is_eight_bytes() {
259 assert_eq!(std::mem::size_of::<VertexAddr>(), 8);
260 assert_eq!(std::mem::size_of::<VertexAddr>(), size_of::<AbsCoord>());
261 assert_eq!(std::mem::align_of::<VertexAddr>(), align_of::<AbsCoord>());
262 assert_eq!(size_of::<GridAddr>(), 8);
263 }
264
265 #[test]
266 fn grid_addresses_round_trip_and_are_never_symbols() {
267 for (row, col) in [(0, 0), (1, 1), (1_048_575, 16_383), (7, 0), (0, 16_383)] {
268 let addr = VertexAddr::grid(GridAddr::new(row, col));
269 assert!(addr.is_grid());
270 assert!(!addr.is_symbol());
271 assert_eq!(addr.as_symbol(), None);
272 let grid = addr.as_grid().expect("grid address must decode");
273 assert_eq!((grid.row(), grid.col()), (row, col));
274 assert_eq!(addr.order_key(), (row, col));
275 }
276 }
277
278 #[test]
279 fn symbol_addresses_round_trip_and_are_never_grid() {
280 for index in [0u32, 1, 16_384, u32::MAX] {
281 let addr = VertexAddr::symbol(SymbolAddr::new(index));
282 assert!(addr.is_symbol());
283 assert!(!addr.is_grid());
284 assert_eq!(addr.as_grid(), None);
285 assert_eq!(addr.as_symbol(), Some(SymbolAddr::new(index)));
286 assert_eq!(addr.order_key(), (u32::MAX, index));
287 }
288 }
289
290 #[test]
291 fn invalid_sentinel_is_neither_grid_nor_symbol() {
292 assert!(!VertexAddr::INVALID.is_grid());
293 assert!(!VertexAddr::INVALID.is_symbol());
294 assert_eq!(VertexAddr::INVALID.as_grid(), None);
295 assert_eq!(VertexAddr::INVALID.as_symbol(), None);
296 }
297
298 #[test]
299 fn symbols_order_after_every_grid_position() {
300 let last_cell = VertexAddr::grid(GridAddr::new(1_048_575, 16_383));
301 let first_symbol = VertexAddr::symbol(SymbolAddr::new(0));
302 assert!(last_cell.order_key() < first_symbol.order_key());
303 }
304}