1mod conformance;
10pub use conformance::{verify_identifier_allocations, verify_identifier_batches};
11
12use std::{num::NonZeroU64, ops::RangeInclusive};
13
14use uqa_core::memory::{MemoryError, MemoryReservation};
15
16use crate::{read_control::StorageReadControl, StorageBackendResult};
17
18use super::{VersionError, VersionResult};
19
20pub trait IdentifierAllocator: Send + Sync {
22 fn identifier_watermark(&self, namespace: &[u8]) -> StorageBackendResult<Option<u64>>;
24
25 fn allocate_identifiers(
26 &self,
27 namespace: &[u8],
28 request: IdentifierRequest,
29 ) -> StorageBackendResult<IdentifierAllocation>;
30}
31
32#[derive(Debug, Clone, Copy)]
34pub enum IdentifierRequest {
35 Observe(u64),
36 Reserve {
37 minimum: u64,
38 maximum: u64,
39 count: NonZeroU64,
40 },
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub struct IdentifierAllocation {
46 watermark: u64,
47 first: Option<u64>,
48}
49
50impl IdentifierAllocation {
51 pub const fn watermark(self) -> u64 {
52 self.watermark
53 }
54
55 pub fn range(self) -> Option<RangeInclusive<u64>> {
56 self.first.map(|first| first..=self.watermark)
57 }
58}
59
60impl IdentifierRequest {
61 pub fn prepare(self, current: Option<u64>) -> VersionResult<IdentifierAllocation> {
63 let (watermark, first) = match self {
64 Self::Observe(value) => (current.map_or(value, |current| current.max(value)), None),
65 Self::Reserve {
66 minimum,
67 maximum,
68 count,
69 } => {
70 if minimum > maximum {
71 return Err(VersionError::InvalidIdentifierBounds);
72 }
73 let first = match current {
74 Some(current) => current
75 .checked_add(1)
76 .ok_or(VersionError::IdentifiersExhausted)?
77 .max(minimum),
78 None => minimum,
79 };
80 let last = first
81 .checked_add(count.get() - 1)
82 .filter(|last| *last <= maximum)
83 .ok_or(VersionError::IdentifiersExhausted)?;
84 (last, Some(first))
85 }
86 };
87 Ok(IdentifierAllocation { watermark, first })
88 }
89
90 pub fn reserve_workspace(
92 self,
93 namespace: &[u8],
94 control: &StorageReadControl,
95 ) -> VersionResult<MemoryReservation> {
96 self.prepare(None)?;
97 reserve_identifier_workspace(namespace, control)
98 }
99}
100
101pub fn reserve_identifier_workspace(
103 namespace: &[u8],
104 control: &StorageReadControl,
105) -> VersionResult<MemoryReservation> {
106 control.cancellation().check()?;
107 if namespace.is_empty() {
108 return Err(VersionError::InvalidEncoding("empty identifier namespace"));
109 }
110 let bytes = namespace
111 .len()
112 .checked_mul(2)
113 .and_then(|bytes| bytes.checked_add(64))
114 .ok_or(MemoryError::SizeOverflow)?;
115 Ok(control.memory().reserve(bytes)?)
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 fn reserve(minimum: u64, maximum: u64, count: u64) -> IdentifierRequest {
123 IdentifierRequest::Reserve {
124 minimum,
125 maximum,
126 count: NonZeroU64::new(count).unwrap(),
127 }
128 }
129
130 #[test]
131 fn reservations_cover_zero_full_width_bounds_and_atomic_exhaustion() {
132 assert_eq!(reserve(0, 9, 2).prepare(None).unwrap().range(), Some(0..=1));
133 assert_eq!(
134 reserve(5, 9, 2).prepare(Some(1)).unwrap().range(),
135 Some(5..=6)
136 );
137 assert_eq!(
138 reserve(0, u64::MAX, 2)
139 .prepare(Some(u64::MAX - 2))
140 .unwrap()
141 .range(),
142 Some(u64::MAX - 1..=u64::MAX)
143 );
144 for (request, current) in [
145 (reserve(0, 9, 2), Some(8)),
146 (reserve(0, u64::MAX, 2), Some(u64::MAX - 1)),
147 (reserve(0, u64::MAX, 1), Some(u64::MAX)),
148 ] {
149 assert!(matches!(
150 request.prepare(current),
151 Err(VersionError::IdentifiersExhausted)
152 ));
153 }
154 assert!(matches!(
155 reserve(2, 1, 1).prepare(None),
156 Err(VersionError::InvalidIdentifierBounds)
157 ));
158 }
159
160 #[test]
161 fn observations_never_rewind_or_wrap_a_namespace() {
162 for (current, observed, expected) in [
163 (None, 0, 0),
164 (Some(9), 2, 9),
165 (Some(2), 9, 9),
166 (Some(u64::MAX), 0, u64::MAX),
167 ] {
168 let allocation = IdentifierRequest::Observe(observed)
169 .prepare(current)
170 .unwrap();
171 assert_eq!(allocation.watermark(), expected);
172 assert_eq!(allocation.range(), None);
173 }
174 }
175}