1use super::addr::{GridAddr, VertexAddr};
2use super::vertex::{VertexId, VertexKind};
3use crate::SheetId;
4use std::sync::atomic::{AtomicU8, Ordering};
5
6#[cfg(test)]
7mod tests {
8 use super::*;
9 use crate::engine::addr::SymbolAddr;
10
11 fn grid(row: u32, col: u32) -> VertexAddr {
12 VertexAddr::grid(GridAddr::new(row, col))
13 }
14
15 #[test]
16 fn test_vertex_store_allocation() {
17 let mut store = VertexStore::new();
18 let id = store.allocate(grid(10, 20), 1, 0x01);
19 assert_eq!(store.addr(id), grid(10, 20));
20 assert_eq!(store.sheet_id(id), 1);
21 assert_eq!(store.flags(id), 0x01);
22 }
23
24 #[test]
25 fn prepared_batch_vertex_overflow_is_checked_before_mutation() {
26 let mut store = VertexStore::new();
27 store.len = u32::MAX as usize - FIRST_NORMAL_VERTEX as usize + 1;
28 let before = (
29 store.coords.len(),
30 store.sheet_kind.len(),
31 store.flags.len(),
32 );
33 assert_eq!(
34 store.try_allocate_batch(&[(grid(0, 0), 0, 0)], &[VertexId(FIRST_NORMAL_VERTEX)],),
35 Err(VertexBatchAllocationError::IdExhausted)
36 );
37 assert_eq!(
38 before,
39 (
40 store.coords.len(),
41 store.sheet_kind.len(),
42 store.flags.len()
43 )
44 );
45 }
46
47 #[test]
48 fn prepared_batch_reserved_id_mismatch_is_checked_before_mutation() {
49 let mut store = VertexStore::new();
50 let before = (store.len(), store.coords.len(), store.flags.len());
51 assert_eq!(
52 store.try_allocate_batch(&[(grid(0, 0), 0, 0)], &[VertexId(FIRST_NORMAL_VERTEX + 1)],),
53 Err(VertexBatchAllocationError::ReservedIdsMismatch)
54 );
55 assert_eq!(before, (store.len(), store.coords.len(), store.flags.len()));
56 }
57
58 #[test]
59 fn test_vertex_store_grow() {
60 let mut store = VertexStore::with_capacity(1000);
61 for i in 0..10_000 {
62 store.allocate(grid(i, i), 0, 0);
63 }
64 assert_eq!(store.len(), 10_000);
65 }
69
70 #[test]
71 fn test_vertex_store_capacity() {
72 let store = VertexStore::with_capacity(100);
73 assert!(store.coords.capacity() >= 100);
74 assert!(store.sheet_kind.capacity() >= 100);
75 assert!(store.flags.capacity() >= 100);
76 assert!(store.value_ref.capacity() >= 100);
77 assert!(store.edge_offset.capacity() >= 100);
78 }
79
80 #[test]
81 fn test_vertex_store_accessors() {
82 let mut store = VertexStore::new();
83 let id = store.allocate(grid(5, 10), 3, 0x03);
84
85 let position = store
87 .grid_addr(id)
88 .expect("cell vertices carry a grid position");
89 assert_eq!(position.row(), 5);
90 assert_eq!(position.col(), 10);
91
92 assert_eq!(store.sheet_id(id), 3);
94
95 assert_eq!(store.flags(id), 0x03);
97 assert!(store.is_dirty(id));
98 assert!(store.is_volatile(id));
99
100 store.set_kind(id, VertexKind::Cell);
102 assert_eq!(store.kind(id), VertexKind::Cell);
103 }
104
105 #[test]
106 fn symbol_vertices_have_no_grid_position() {
107 let mut store = VertexStore::new();
108 let cell = store.allocate(grid(3, 4), 0, 0);
109 let symbol = store.allocate(VertexAddr::symbol(SymbolAddr::new(0)), 0, 0);
110
111 assert_eq!(store.grid_addr(cell), Some(GridAddr::new(3, 4)));
112 assert_eq!(store.grid_addr(symbol), None);
113 assert!(store.addr(symbol).is_symbol());
114 assert_eq!(store.addr(symbol).as_symbol(), Some(SymbolAddr::new(0)));
115 }
116
117 #[test]
120 fn stored_address_element_size_is_unchanged() {
121 assert_eq!(std::mem::size_of::<VertexAddr>(), 8);
122 assert_eq!(
123 std::mem::size_of::<VertexAddr>(),
124 std::mem::size_of::<formualizer_common::Coord>(),
125 );
126 }
127
128 #[test]
129 fn test_reserved_vertex_range() {
130 let mut store = VertexStore::new();
131 let id = store.allocate(grid(0, 0), 0, 0);
133 assert!(id.0 >= FIRST_NORMAL_VERTEX);
134 }
135
136 #[test]
137 fn test_atomic_flag_operations() {
138 let mut store = VertexStore::new();
139 let id = store.allocate(grid(0, 0), 0, 0);
140
141 store.set_dirty(id, true);
143 assert!(store.is_dirty(id));
144
145 store.set_dirty(id, false);
146 assert!(!store.is_dirty(id));
147
148 store.set_volatile(id, true);
149 assert!(store.is_volatile(id));
150 }
151
152 #[test]
153 fn test_vertex_store_set_addr() {
154 let mut store = VertexStore::new();
155 let id = store.allocate(grid(1, 1), 0, 0);
156
157 store.set_addr(id, grid(5, 10));
159 assert_eq!(store.addr(id), grid(5, 10));
160 }
161
162 #[test]
163 fn test_vertex_store_atomic_flags() {
164 let mut store = VertexStore::new();
165 let id = store.allocate(grid(0, 0), 0, 0);
166
167 store.set_dirty(id, true);
169 assert!(store.is_dirty(id));
170
171 store.set_volatile(id, true);
172 assert!(store.is_volatile(id));
173
174 store.mark_deleted(id, true);
176 assert!(store.is_deleted(id));
177 }
178
179 #[test]
180 fn test_reserved_id_range_preserved() {
181 let mut store = VertexStore::new();
182
183 let id = store.allocate(grid(0, 0), 0, 0);
185 assert!(id.0 >= FIRST_NORMAL_VERTEX);
186
187 store.mark_deleted(id, true);
189 assert!(store.vertex_exists(id));
190 assert!(store.is_deleted(id));
191 }
192}
193
194pub const FIRST_NORMAL_VERTEX: u32 = 1024;
196pub const RANGE_VERTEX_START: u32 = 0;
197pub const EXTERNAL_VERTEX_START: u32 = 256;
198
199#[derive(Clone, Copy, Debug, PartialEq, Eq)]
200pub(crate) enum VertexBatchAllocationError {
201 IdExhausted,
202 ReservedIdsMismatch,
203}
204
205#[repr(C, align(64))]
212#[derive(Debug)]
213pub struct VertexStore {
214 coords: Vec<VertexAddr>, sheet_kind: Vec<u32>, flags: Vec<AtomicU8>, value_ref: Vec<u32>, edge_offset: Vec<u32>, len: usize,
223}
224
225impl Default for VertexStore {
226 fn default() -> Self {
227 Self::new()
228 }
229}
230
231impl VertexStore {
232 pub fn new() -> Self {
233 Self {
234 coords: Vec::new(),
235 sheet_kind: Vec::new(),
236 flags: Vec::new(),
237 value_ref: Vec::new(),
238 edge_offset: Vec::new(),
239 len: 0,
240 }
241 }
242
243 pub fn with_capacity(capacity: usize) -> Self {
244 Self {
245 coords: Vec::with_capacity(capacity),
246 sheet_kind: Vec::with_capacity(capacity),
247 flags: Vec::with_capacity(capacity),
248 value_ref: Vec::with_capacity(capacity),
249 edge_offset: Vec::with_capacity(capacity),
250 len: 0,
251 }
252 }
253
254 pub fn reserve(&mut self, additional: usize) {
256 if additional == 0 {
257 return;
258 }
259 let target = self.len + additional;
261 if self.coords.capacity() < target {
262 self.coords.reserve(additional);
263 }
264 if self.sheet_kind.capacity() < target {
265 self.sheet_kind.reserve(additional);
266 }
267 if self.flags.capacity() < target {
268 self.flags.reserve(additional);
269 }
270 if self.value_ref.capacity() < target {
271 self.value_ref.reserve(additional);
272 }
273 if self.edge_offset.capacity() < target {
274 self.edge_offset.reserve(additional);
275 }
276 }
277
278 pub fn allocate(&mut self, addr: VertexAddr, sheet: SheetId, flags: u8) -> VertexId {
281 let id = VertexId(self.len as u32 + FIRST_NORMAL_VERTEX);
282 debug_assert!(id.0 >= FIRST_NORMAL_VERTEX);
283
284 self.coords.push(addr);
285 self.sheet_kind.push((sheet as u32) << 16);
286 self.flags.push(AtomicU8::new(flags));
287 self.value_ref.push(0);
288 self.edge_offset.push(0);
289 self.len += 1;
290
291 id
292 }
293
294 pub(crate) fn try_allocate_batch(
295 &mut self,
296 vertices: &[(VertexAddr, SheetId, u8)],
297 expected_ids: &[VertexId],
298 ) -> Result<Vec<VertexId>, VertexBatchAllocationError> {
299 if vertices.len() != expected_ids.len() {
300 return Err(VertexBatchAllocationError::ReservedIdsMismatch);
301 }
302 let start = u32::try_from(self.len)
303 .map_err(|_| VertexBatchAllocationError::IdExhausted)?
304 .checked_add(FIRST_NORMAL_VERTEX)
305 .ok_or(VertexBatchAllocationError::IdExhausted)?;
306 let count =
307 u32::try_from(vertices.len()).map_err(|_| VertexBatchAllocationError::IdExhausted)?;
308 if count != 0 {
309 start
310 .checked_add(count - 1)
311 .ok_or(VertexBatchAllocationError::IdExhausted)?;
312 }
313 let ids: Vec<_> = (0..count).map(|offset| VertexId(start + offset)).collect();
314 if ids != expected_ids {
315 return Err(VertexBatchAllocationError::ReservedIdsMismatch);
316 }
317 self.reserve(vertices.len());
318 for &(addr, sheet, flags) in vertices {
319 self.coords.push(addr);
320 self.sheet_kind.push((u32::from(sheet)) << 16);
321 self.flags.push(AtomicU8::new(flags));
322 self.value_ref.push(0);
323 self.edge_offset.push(0);
324 self.len += 1;
325 }
326 Ok(ids)
327 }
328
329 pub(crate) fn allocate_prevalidated_batch(&mut self, vertices: &[(VertexAddr, SheetId, u8)]) {
332 self.reserve(vertices.len());
333 for &(addr, sheet, flags) in vertices {
334 self.allocate(addr, sheet, flags);
335 }
336 }
337
338 pub fn allocate_contiguous(
341 &mut self,
342 sheet: SheetId,
343 addrs: &[VertexAddr],
344 flags: u8,
345 ) -> Vec<VertexId> {
346 if addrs.is_empty() {
347 return Vec::new();
348 }
349 self.reserve(addrs.len());
350 let mut ids = Vec::with_capacity(addrs.len());
351 for &addr in addrs {
352 ids.push(self.allocate(addr, sheet, flags));
353 }
354 ids
355 }
356
357 #[inline]
358 pub fn len(&self) -> usize {
359 self.len
360 }
361
362 #[inline]
364 fn vertex_id_to_index(&self, id: VertexId) -> Option<usize> {
365 if id.0 < FIRST_NORMAL_VERTEX {
366 return None;
367 }
368 let idx = (id.0 - FIRST_NORMAL_VERTEX) as usize;
369 if idx >= self.len {
370 return None;
371 }
372 Some(idx)
373 }
374
375 #[inline]
376 pub fn is_empty(&self) -> bool {
377 self.len == 0
378 }
379
380 #[inline]
384 pub fn addr(&self, id: VertexId) -> VertexAddr {
385 if let Some(idx) = self.vertex_id_to_index(id) {
386 self.coords[idx]
387 } else {
388 VertexAddr::INVALID }
390 }
391
392 #[inline]
396 pub fn grid_addr(&self, id: VertexId) -> Option<GridAddr> {
397 self.addr(id).as_grid()
398 }
399
400 #[inline]
401 pub fn sheet_id(&self, id: VertexId) -> SheetId {
402 if let Some(idx) = self.vertex_id_to_index(id) {
403 (self.sheet_kind[idx] >> 16) as SheetId
404 } else {
405 0 }
407 }
408
409 #[inline]
410 pub fn kind(&self, id: VertexId) -> VertexKind {
411 if let Some(idx) = self.vertex_id_to_index(id) {
412 let tag = ((self.sheet_kind[idx] >> 8) & 0xFF) as u8;
413 VertexKind::from_tag(tag)
414 } else {
415 VertexKind::Empty }
417 }
418
419 #[inline]
420 pub fn set_kind(&mut self, id: VertexId, kind: VertexKind) {
421 if let Some(idx) = self.vertex_id_to_index(id) {
422 let sheet_bits = self.sheet_kind[idx] & 0xFFFF0000;
423 self.sheet_kind[idx] = sheet_bits | ((kind.to_tag() as u32) << 8);
424 }
425 }
426
427 #[inline]
428 pub fn flags(&self, id: VertexId) -> u8 {
429 if let Some(idx) = self.vertex_id_to_index(id) {
430 self.flags[idx].load(Ordering::Acquire)
431 } else {
432 0 }
434 }
435
436 #[inline]
437 pub fn is_dirty(&self, id: VertexId) -> bool {
438 self.flags(id) & 0x01 != 0
439 }
440
441 #[inline]
442 pub fn is_volatile(&self, id: VertexId) -> bool {
443 self.flags(id) & 0x02 != 0
444 }
445
446 #[inline]
447 pub fn is_deleted(&self, id: VertexId) -> bool {
448 self.flags(id) & 0x04 != 0
449 }
450
451 #[inline]
452 pub fn is_dynamic(&self, id: VertexId) -> bool {
453 self.flags(id) & 0x08 != 0
454 }
455
456 #[inline]
457 pub fn set_dirty(&self, id: VertexId, dirty: bool) {
458 if id.0 < FIRST_NORMAL_VERTEX {
459 return; }
461 let idx = (id.0 - FIRST_NORMAL_VERTEX) as usize;
462 if idx >= self.flags.len() {
463 return; }
465 if dirty {
466 self.flags[idx].fetch_or(0x01, Ordering::Release);
467 } else {
468 self.flags[idx].fetch_and(!0x01, Ordering::Release);
469 }
470 }
471
472 #[inline]
473 pub fn set_volatile(&self, id: VertexId, volatile: bool) {
474 if id.0 < FIRST_NORMAL_VERTEX {
475 return;
476 }
477 if let Some(idx) = self.vertex_id_to_index(id) {
478 if volatile {
479 self.flags[idx].fetch_or(0x02, std::sync::atomic::Ordering::Release);
480 } else {
481 self.flags[idx].fetch_and(!0x02, std::sync::atomic::Ordering::Release);
482 }
483 }
484 }
485
486 #[inline]
487 pub fn set_dynamic(&self, id: VertexId, dynamic: bool) {
488 if id.0 < FIRST_NORMAL_VERTEX {
489 return;
490 }
491 if let Some(idx) = self.vertex_id_to_index(id) {
492 if dynamic {
493 self.flags[idx].fetch_or(0x08, std::sync::atomic::Ordering::Release);
494 } else {
495 self.flags[idx].fetch_and(!0x08, std::sync::atomic::Ordering::Release);
496 }
497 }
498 }
499
500 #[inline]
501 pub fn value_ref(&self, id: VertexId) -> u32 {
502 if let Some(idx) = self.vertex_id_to_index(id) {
503 self.value_ref[idx]
504 } else {
505 0 }
507 }
508
509 #[inline]
510 pub fn set_value_ref(&mut self, id: VertexId, value_ref: u32) {
511 if let Some(idx) = self.vertex_id_to_index(id) {
512 self.value_ref[idx] = value_ref;
513 }
514 }
515
516 #[inline]
517 pub fn edge_offset(&self, id: VertexId) -> u32 {
518 if let Some(idx) = self.vertex_id_to_index(id) {
519 self.edge_offset[idx]
520 } else {
521 0 }
523 }
524
525 #[inline]
526 pub fn set_edge_offset(&mut self, id: VertexId, offset: u32) {
527 if let Some(idx) = self.vertex_id_to_index(id) {
528 self.edge_offset[idx] = offset;
529 }
530 }
531
532 #[doc(hidden)]
536 pub fn set_addr(&mut self, id: VertexId, addr: VertexAddr) {
537 if let Some(idx) = self.vertex_id_to_index(id) {
538 self.coords[idx] = addr;
539 }
540 }
541
542 pub fn mark_deleted(&self, id: VertexId, deleted: bool) {
544 if let Some(idx) = self.vertex_id_to_index(id) {
545 if deleted {
546 self.flags[idx].fetch_or(0x04, Ordering::Release);
547 } else {
548 self.flags[idx].fetch_and(!0x04, Ordering::Release);
549 }
550 }
551 }
552
553 pub fn vertex_exists(&self, id: VertexId) -> bool {
555 self.vertex_id_to_index(id).is_some()
556 }
557
558 pub fn vertex_exists_active(&self, id: VertexId) -> bool {
560 self.vertex_id_to_index(id)
561 .map(|_| !self.is_deleted(id))
562 .unwrap_or(false)
563 }
564
565 pub fn all_vertices(&self) -> impl Iterator<Item = VertexId> + '_ {
567 (0..self.len).map(|i| VertexId((i as u32) + FIRST_NORMAL_VERTEX))
568 }
569}