1use core::fmt;
8use core::ptr::NonNull;
9use core::sync::atomic::Ordering;
10
11use ribbit::Unpack as _;
12
13use crate::concurrent;
14use crate::raw::Edge as RawEdge;
15use crate::raw::edge;
16use crate::raw::edge::Meta as _;
17use crate::raw::node;
18use crate::sequential;
19use crate::sync::Atomic;
20
21pub const VERSION: u16 = 1;
23
24#[derive(Clone, Debug, PartialEq, Eq)]
26pub struct Topology<V> {
27 pub version: u16,
29 pub root: Option<Edge<V>>,
31}
32
33#[derive(Clone, Debug, PartialEq, Eq)]
35pub struct Edge<V> {
36 pub metadata: u64,
38 pub child: Child<V>,
40}
41
42#[derive(Clone, Debug, PartialEq, Eq)]
44pub enum Child<V> {
45 Value(V),
47 Node(Node<V>),
49}
50
51#[derive(Clone, Debug, PartialEq, Eq)]
53pub struct Node<V> {
54 pub kind: NodeKind,
56 pub slot_count: u16,
60 pub branches: Vec<Branch<V>>,
62}
63
64#[derive(Clone, Debug, PartialEq, Eq)]
66pub struct Branch<V> {
67 pub key: u8,
69 pub slot: u16,
71 pub edge: Edge<V>,
73}
74
75#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
77pub enum NodeKind {
78 Node3,
80 Node15,
82 Node47,
84 Node256,
86}
87
88impl NodeKind {
89 fn capacity(self) -> usize {
90 match self {
91 Self::Node3 => 3,
92 Self::Node15 => 15,
93 Self::Node47 => 47,
94 Self::Node256 => 256,
95 }
96 }
97
98 fn from_raw(kind: ribbit::Packed<node::Type>) -> Self {
99 match kind.unpack() {
100 node::Type::Node3 => Self::Node3,
101 node::Type::Node15 => Self::Node15,
102 node::Type::Node47 => Self::Node47,
103 node::Type::Node256 => Self::Node256,
104 }
105 }
106
107 fn into_raw(self) -> node::Type {
108 match self {
109 Self::Node3 => node::Type::Node3,
110 Self::Node15 => node::Type::Node15,
111 Self::Node47 => node::Type::Node47,
112 Self::Node256 => node::Type::Node256,
113 }
114 }
115}
116
117#[derive(Clone, Debug, PartialEq, Eq)]
119pub enum Error {
120 UnsupportedVersion {
122 found: u16,
124 },
125 InvalidMetadata {
127 metadata: u64,
129 },
130 InvalidKeyLength {
132 found: usize,
134 expected: usize,
136 },
137 EmptyNode,
139 NodeCapacity {
141 kind: NodeKind,
143 found: usize,
145 },
146 DuplicateKey {
148 key: u8,
150 },
151 InvalidSlot {
153 kind: NodeKind,
155 slot: u16,
157 },
158}
159
160impl fmt::Display for Error {
161 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
162 match self {
163 Self::UnsupportedVersion { found } => {
164 write!(formatter, "unsupported Arctic topology version {found}")
165 }
166 Self::InvalidMetadata { metadata } => {
167 write!(formatter, "invalid Arctic edge metadata {metadata:#018x}")
168 }
169 Self::InvalidKeyLength { found, expected } => write!(
170 formatter,
171 "Arctic path has {found} key bytes but key type requires {expected}",
172 ),
173 Self::EmptyNode => formatter.write_str("Arctic topology contains an empty node"),
174 Self::NodeCapacity { kind, found } => {
175 write!(formatter, "{kind:?} cannot contain {found} branches")
176 }
177 Self::DuplicateKey { key } => {
178 write!(formatter, "Arctic node contains duplicate byte {key}")
179 }
180 Self::InvalidSlot { kind, slot } => {
181 write!(formatter, "invalid or duplicate {kind:?} slot {slot}")
182 }
183 }
184 }
185}
186
187impl std::error::Error for Error {}
188
189mod private {
190 use super::*;
191
192 pub trait Sealed: crate::Key {
193 const BYTES: usize;
194
195 fn metadata_to_raw(metadata: ribbit::Packed<Self::Edge>) -> u64;
196
197 unsafe fn metadata_from_raw(raw: u64) -> ribbit::Packed<Self::Edge>;
198
199 fn validate_metadata(raw: u64) -> Result<usize, Error>;
200 }
201
202 macro_rules! impl_be_key {
203 ($($key:ty),+ $(,)?) => {
204 $(
205 impl Sealed for $key {
206 const BYTES: usize = core::mem::size_of::<Self>();
207
208 fn metadata_to_raw(metadata: ribbit::Packed<Self::Edge>) -> u64 {
209 metadata.with_frozen(false).with_value(false).into_raw()
210 }
211
212 unsafe fn metadata_from_raw(raw: u64) -> ribbit::Packed<Self::Edge> {
213 unsafe { ribbit::Packed::<edge::Be>::from_raw_unchecked(raw) }
214 }
215
216 fn validate_metadata(raw: u64) -> Result<usize, Error> {
217 validate_be_metadata(raw)
218 }
219 }
220 )+
221 };
222 }
223
224 impl_be_key!(u16, u32, u128);
225 #[cfg(not(feature = "opt-no-int"))]
226 impl_be_key!(u64);
227
228 #[cfg(feature = "opt-no-int")]
229 impl Sealed for u64 {
230 const BYTES: usize = core::mem::size_of::<Self>();
231
232 fn metadata_to_raw(metadata: ribbit::Packed<Self::Edge>) -> u64 {
233 metadata.with_frozen(false).with_value(false).into_raw()
234 }
235
236 unsafe fn metadata_from_raw(raw: u64) -> ribbit::Packed<Self::Edge> {
237 unsafe { ribbit::Packed::<edge::Le>::from_raw_unchecked(raw) }
238 }
239
240 fn validate_metadata(raw: u64) -> Result<usize, Error> {
241 validate_le_metadata(raw)
242 }
243 }
244}
245
246pub trait Key: crate::Key + private::Sealed {}
251
252impl Key for u16 {}
253impl Key for u32 {}
254impl Key for u64 {}
255impl Key for u128 {}
256
257impl<V> Topology<V> {
258 pub fn validate<K: Key>(&self) -> Result<(), Error> {
260 if self.version != VERSION {
261 return Err(Error::UnsupportedVersion {
262 found: self.version,
263 });
264 }
265
266 if let Some(root) = &self.root {
267 validate_edge::<K, V>(root, 0)?;
268 }
269 Ok(())
270 }
271}
272
273impl<K, V> sequential::Map<K, V>
274where
275 K: Key,
276 V: sequential::Value,
277{
278 pub fn export_topology<T>(
283 &self,
284 mut encode: impl FnMut(&V) -> T,
285 ) -> Result<Topology<T>, Error> {
286 let root =
287 unsafe { export_edge::<K, V, T, _>(NonNull::from(self.raw.root()), &mut encode) };
288 let topology = Topology {
289 version: VERSION,
290 root,
291 };
292 topology.validate::<K>()?;
293 Ok(topology)
294 }
295
296 pub fn from_topology<T>(
298 topology: Topology<T>,
299 mut decode: impl FnMut(T) -> V,
300 ) -> Result<Self, Error> {
301 topology.validate::<K>()?;
302
303 let mut map = Self::new();
304 if let Some(root) = topology.root {
305 let root = unsafe { import_edge::<K, V, T, _>(root, &mut decode) };
306 map.raw.set_empty_root(root);
307 }
308 Ok(map)
309 }
310}
311
312impl<K, V, S> concurrent::Map<K, V, S>
313where
314 K: Key,
315 V: concurrent::Value,
316 S: concurrent::Smr<K, V>,
317{
318 pub fn export_topology<T>(
323 &mut self,
324 encode: impl FnMut(&V) -> T,
325 ) -> Result<Topology<T>, Error> {
326 self.as_sequential().export_topology(encode)
327 }
328}
329
330impl<K, V, S> concurrent::Map<K, V, S>
331where
332 K: Key,
333 V: concurrent::Value,
334 S: concurrent::Smr<K, V> + Default,
335{
336 pub fn from_topology<T>(
338 topology: Topology<T>,
339 decode: impl FnMut(T) -> V,
340 ) -> Result<Self, Error> {
341 sequential::Map::from_topology(topology, decode).map(Into::into)
342 }
343}
344
345fn validate_edge<K: Key, V>(edge: &Edge<V>, path_bytes: usize) -> Result<(), Error> {
346 let prefix_bytes = K::validate_metadata(edge.metadata)?;
347 let path_bytes = path_bytes
348 .checked_add(prefix_bytes)
349 .ok_or(Error::InvalidKeyLength {
350 found: usize::MAX,
351 expected: K::BYTES,
352 })?;
353
354 match &edge.child {
355 Child::Value(_) if path_bytes == K::BYTES => Ok(()),
356 Child::Value(_) => Err(Error::InvalidKeyLength {
357 found: path_bytes,
358 expected: K::BYTES,
359 }),
360 Child::Node(node) => {
361 if path_bytes >= K::BYTES {
362 return Err(Error::InvalidKeyLength {
363 found: path_bytes + 1,
364 expected: K::BYTES,
365 });
366 }
367 if node.branches.is_empty() {
368 return Err(Error::EmptyNode);
369 }
370 let slot_count = node.slot_count as usize;
371 if node.branches.len() > node.kind.capacity()
372 || slot_count > node.kind.capacity()
373 || slot_count < node.branches.len()
374 || (node.kind == NodeKind::Node256 && slot_count != 256)
375 {
376 return Err(Error::NodeCapacity {
377 kind: node.kind,
378 found: slot_count.max(node.branches.len()),
379 });
380 }
381
382 let mut keys = [false; 256];
383 let mut slots = [false; 256];
384 for branch in &node.branches {
385 if core::mem::replace(&mut keys[branch.key as usize], true) {
386 return Err(Error::DuplicateKey { key: branch.key });
387 }
388
389 let slot = branch.slot as usize;
390 if slot >= slot_count
391 || core::mem::replace(&mut slots[slot], true)
392 || (node.kind == NodeKind::Node256 && slot != branch.key as usize)
393 {
394 return Err(Error::InvalidSlot {
395 kind: node.kind,
396 slot: branch.slot,
397 });
398 }
399
400 validate_edge::<K, V>(&branch.edge, path_bytes + 1)?;
401 }
402 Ok(())
403 }
404 }
405}
406
407fn validate_be_metadata(metadata: u64) -> Result<usize, Error> {
408 const FLAGS: u64 = 0b111;
409 const LENGTH: u64 = 0b11_1000;
410
411 let bits = (metadata & LENGTH) as usize;
412 let prefix_mask = if bits == 0 {
413 0
414 } else {
415 u64::MAX << (64 - bits)
416 };
417 if metadata & FLAGS != 0 || metadata & !(prefix_mask | LENGTH) != 0 {
418 return Err(Error::InvalidMetadata { metadata });
419 }
420 Ok(bits / 8)
421}
422
423#[cfg(feature = "opt-no-int")]
424fn validate_le_metadata(metadata: u64) -> Result<usize, Error> {
425 const FLAGS: u64 = 0b111 << 56;
426 const LENGTH: u64 = 0b11_1000 << 56;
427
428 let bits = ((metadata & LENGTH) >> 56) as usize;
429 let prefix_mask = if bits == 0 {
430 0
431 } else {
432 u64::MAX >> (64 - bits)
433 };
434 if metadata & FLAGS != 0 || metadata & !(prefix_mask | LENGTH) != 0 {
435 return Err(Error::InvalidMetadata { metadata });
436 }
437 Ok(bits / 8)
438}
439
440unsafe fn export_edge<K, V, T, F>(
441 pointer: NonNull<Atomic<RawEdge<K::Edge>>>,
442 encode: &mut F,
443) -> Option<Edge<T>>
444where
445 K: Key,
446 V: sequential::Value,
447 F: FnMut(&V) -> T,
448{
449 let edge = unsafe { pointer.as_ref() }.load_packed(Ordering::Acquire);
450 let child = edge.child()?;
451 let metadata = K::metadata_to_raw(edge.meta());
452
453 let child = match child {
454 edge::Child::Value(_) => {
455 let value = unsafe { RawEdge::as_value_unchecked(pointer).cast::<V>().as_ref() };
456 Child::Value(encode(value))
457 }
458 edge::Child::Node(node) => {
459 let kind = NodeKind::from_raw(node.r#type());
460 let entries = unsafe { node.topology_entries() };
461 let slot_count = entries.len() as u16;
462 let mut branches = Vec::new();
463 for (key, slot, pointer) in entries {
464 if let Some(edge) = unsafe { export_edge::<K, V, T, F>(pointer.cast(), encode) } {
465 branches.push(Branch { key, slot, edge });
466 }
467 }
468 branches.sort_unstable_by_key(|branch| branch.slot);
469 Child::Node(Node {
470 kind,
471 slot_count,
472 branches,
473 })
474 }
475 };
476
477 Some(Edge { metadata, child })
478}
479
480unsafe fn import_edge<K, V, T, F>(edge: Edge<T>, decode: &mut F) -> ribbit::Packed<RawEdge<K::Edge>>
481where
482 K: Key,
483 V: sequential::Value,
484 F: FnMut(T) -> V,
485{
486 let metadata = unsafe { K::metadata_from_raw(edge.metadata) };
487 match edge.child {
488 Child::Value(value) => RawEdge::new_value(metadata, decode(value).into_raw()),
489 Child::Node(mut node) => {
490 node.branches.sort_unstable_by_key(|branch| branch.slot);
491 let kind = node.kind;
492 let slot_count = node.slot_count as usize;
493 let (keys, edges) = if kind == NodeKind::Node256 {
494 let mut keys = Vec::with_capacity(node.branches.len());
495 let mut edges = Vec::with_capacity(node.branches.len());
496 for branch in node.branches {
497 keys.push(branch.key);
498 edges.push(unsafe { import_edge::<K, V, T, F>(branch.edge, decode) }.erase());
499 }
500 (keys, edges)
501 } else {
502 let mut used_keys = [false; 256];
503 for branch in &node.branches {
504 used_keys[branch.key as usize] = true;
505 }
506
507 let mut filler_keys = (u8::MIN..=u8::MAX).filter(|key| !used_keys[*key as usize]);
508 let mut keys = Vec::with_capacity(slot_count);
509 for _ in 0..slot_count {
510 keys.push(
511 filler_keys
512 .next()
513 .expect("validated node has spare key bytes"),
514 );
515 }
516 let mut edges = vec![RawEdge::<K::Edge>::NULL.erase(); slot_count];
517
518 for branch in node.branches {
519 let slot = branch.slot as usize;
520 keys[slot] = branch.key;
521 edges[slot] = unsafe { import_edge::<K, V, T, F>(branch.edge, decode) }.erase();
522 }
523 (keys, edges)
524 };
525 let pointer = unsafe { node::Ptr::new_exact(kind.into_raw(), &keys, &edges) };
526 RawEdge::new_node(metadata, pointer)
527 }
528 }
529}
530
531#[cfg(test)]
532mod tests {
533 use super::*;
534
535 fn contains_kind<V>(edge: &Edge<V>, expected: NodeKind) -> bool {
536 match &edge.child {
537 Child::Value(_) => false,
538 Child::Node(node) => {
539 node.kind == expected
540 || node
541 .branches
542 .iter()
543 .any(|branch| contains_kind(&branch.edge, expected))
544 }
545 }
546 }
547
548 fn assert_round_trip_for_keys(keys: impl IntoIterator<Item = u64>, expected: NodeKind) {
549 let mut map = sequential::Map::<u64, u64>::new();
550 for key in keys {
551 map.insert(key, key.rotate_left(17)).unwrap();
552 }
553
554 let before = map.export_topology(|value| *value).unwrap();
555 assert!(contains_kind(before.root.as_ref().unwrap(), expected));
556 let restored =
557 sequential::Map::<u64, u64>::from_topology(before.clone(), |value| value).unwrap();
558 let after = restored.export_topology(|value| *value).unwrap();
559 assert_eq!(after, before);
560 }
561
562 #[test]
563 fn sequential_round_trip_preserves_topology_and_values() {
564 let mut map = sequential::Map::<u64, Box<u64>>::new();
565 for key in (0..4_096).map(|key| key * 17) {
566 map.insert(key, Box::new(key ^ 0xA5A5)).unwrap();
567 }
568 for key in (0..4_096).step_by(7).map(|key| key * 17) {
569 map.remove(&key).unwrap();
570 }
571
572 let before = map.export_topology(|value| **value).unwrap();
573 let restored =
574 sequential::Map::<u64, Box<u64>>::from_topology(before.clone(), Box::new).unwrap();
575 let after = restored.export_topology(|value| **value).unwrap();
576
577 assert_eq!(after, before);
578 for key in (0..4_096).map(|key| key * 17) {
579 let expected = (key / 17 % 7 != 0).then_some(key ^ 0xA5A5);
580 assert_eq!(restored.get(&key).map(|value| **value), expected);
581 }
582 }
583
584 #[test]
585 fn concurrent_round_trip_requires_exclusive_snapshot() {
586 let mut map = concurrent::Map::<u64, Box<u64>>::default();
587 for key in 0..1_024 {
588 map.insert(key, Box::new(key + 1)).unwrap();
589 }
590
591 let topology = map.export_topology(|value| **value).unwrap();
592 let restored = concurrent::Map::<u64, Box<u64>>::from_topology(topology, Box::new).unwrap();
593
594 for key in 0..1_024 {
595 assert_eq!(restored.get(&key).as_deref(), Some(&(key + 1)));
596 }
597 }
598
599 #[test]
600 fn rejects_transient_metadata_flags() {
601 let topology = Topology::<u64> {
602 version: VERSION,
603 root: Some(Edge {
604 metadata: 1,
605 child: Child::Value(42),
606 }),
607 };
608 assert_eq!(
609 topology.validate::<u64>(),
610 Err(Error::InvalidMetadata { metadata: 1 }),
611 );
612 }
613
614 #[test]
615 fn preserves_every_adaptive_node_kind() {
616 assert_round_trip_for_keys(0..2, NodeKind::Node3);
617 assert_round_trip_for_keys(0..10, NodeKind::Node15);
618 assert_round_trip_for_keys(0..32, NodeKind::Node47);
619 assert_round_trip_for_keys(0..256, NodeKind::Node256);
620 }
621}