1use super::{Config, Error};
2use crate::{Context, rmap::RMap};
3use commonware_codec::{CodecFixed, FixedSize, Read, ReadExt, Write as CodecWrite};
4use commonware_cryptography::{Crc32, crc32};
5use commonware_formatting::hex;
6use commonware_runtime::{
7 Blob, Buf, BufMut, Error as RError, WriteOptions,
8 buffer::{Read as ReadBuffer, Write},
9 telemetry::metrics::{Counter, MetricsExt as _},
10};
11use commonware_utils::bitmap::BitMap;
12use futures::future::try_join_all;
13use std::{
14 collections::{BTreeMap, BTreeSet, btree_map::Entry},
15 marker::PhantomData,
16};
17use tracing::{debug, warn};
18
19#[derive(Debug, Clone)]
21struct Record<V: CodecFixed<Cfg = ()>> {
22 value: V,
23 crc: u32,
24}
25
26impl<V: CodecFixed<Cfg = ()>> Record<V> {
27 fn encode(value: &V) -> Vec<u8> {
29 let mut buf = Vec::with_capacity(Self::SIZE);
30 value.write(&mut buf);
31 assert_eq!(buf.len(), V::SIZE, "write() did not write expected bytes");
32 let crc = Crc32::checksum(&buf);
33 crc.write(&mut buf);
34 buf
35 }
36
37 fn decode_valid(mut buf: &[u8]) -> Option<V> {
40 let crc = Crc32::checksum(buf.get(..V::SIZE)?);
41 let record = Self::read(&mut buf).ok()?;
42 (record.crc == crc).then_some(record.value)
43 }
44}
45
46impl<V: CodecFixed<Cfg = ()>> FixedSize for Record<V> {
47 const SIZE: usize = V::SIZE + crc32::Digest::SIZE;
48}
49
50impl<V: CodecFixed<Cfg = ()>> CodecWrite for Record<V> {
51 fn write(&self, buf: &mut impl BufMut) {
52 self.value.write(buf);
53 self.crc.write(buf);
54 }
55}
56
57impl<V: CodecFixed<Cfg = ()>> Read for Record<V> {
58 type Cfg = ();
59
60 fn read_cfg(buf: &mut impl Buf, _: &Self::Cfg) -> Result<Self, commonware_codec::Error> {
61 let value = V::read(buf)?;
62 let crc = u32::read(buf)?;
63
64 Ok(Self { value, crc })
65 }
66}
67
68#[cfg(feature = "arbitrary")]
69impl<V: CodecFixed<Cfg = ()>> arbitrary::Arbitrary<'_> for Record<V>
70where
71 V: for<'a> arbitrary::Arbitrary<'a>,
72{
73 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
74 let value = V::arbitrary(u)?;
75 let mut buf = Vec::with_capacity(V::SIZE);
76 value.write(&mut buf);
77 let crc = Crc32::checksum(&buf);
78 Ok(Self { value, crc })
79 }
80}
81
82struct Inner<E: Context, V: CodecFixed<Cfg = ()>> {
84 context: E,
86 config: Config,
87
88 blobs: BTreeMap<u64, Write<E::Blob>>,
90
91 intervals: RMap,
93
94 pending: BTreeSet<u64>,
96
97 puts: Counter,
99 gets: Counter,
100 has: Counter,
101 syncs: Counter,
102 pruned: Counter,
103
104 _phantom: PhantomData<V>,
105}
106
107impl<E: Context, V: CodecFixed<Cfg = ()>> Inner<E, V> {
108 async fn init(
110 context: E,
111 config: Config,
112 bits: Option<BTreeMap<u64, &Option<BitMap>>>,
113 ) -> Result<Self, Error> {
114 let record_size = Record::<V>::SIZE as u64;
116 let items_per_blob = config.items_per_blob.get();
117 let mut blobs = BTreeMap::new();
118 let stored_blobs = if bits.is_none() {
119 match context.remove(&config.partition, None).await {
120 Ok(()) | Err(RError::PartitionMissing(_)) => Vec::new(),
121 Err(err) => return Err(Error::Runtime(err)),
122 }
123 } else {
124 match context.scan(&config.partition).await {
125 Ok(blobs) => blobs,
126 Err(RError::PartitionMissing(_)) => Vec::new(),
127 Err(err) => return Err(Error::Runtime(err)),
128 }
129 };
130
131 for name in stored_blobs {
133 let (blob, mut len) = context.open(&config.partition, &name).await?;
134 let index = match name.try_into() {
135 Ok(index) => u64::from_be_bytes(index),
136 Err(nm) => Err(Error::InvalidBlobName(hex(&nm)))?,
137 };
138
139 if bits.is_some() && len % record_size != 0 {
141 warn!(
142 blob = index,
143 invalid_size = len,
144 record_size,
145 "blob size is not a multiple of record size, truncating"
146 );
147 len -= len % record_size;
148 blob.resize(len).await?;
149 blob.sync().await?;
150 }
151
152 debug!(blob = index, len, "found index blob");
153 blobs.insert(index, (blob, len));
154 }
155
156 debug!(
158 blobs = blobs.len(),
159 "rebuilding intervals from existing index"
160 );
161 let start = context.current();
162 let mut items = 0;
163 let mut intervals = RMap::new();
164 if let Some(bits) = &bits {
165 let sections = blobs.keys().copied().collect::<Vec<_>>();
167 for section in sections {
168 let keep = match bits.get(§ion) {
169 Some(Some(bits)) => bits.count_ones() != 0,
170 Some(None) => true,
171 None => false,
172 };
173 if !keep {
174 context
175 .remove(&config.partition, Some(§ion.to_be_bytes()))
176 .await?;
177 blobs.remove(§ion);
178 }
179 }
180
181 let empty = vec![0u8; Record::<V>::SIZE];
184 for (section, (blob, size)) in &blobs {
185 let Some(Some(bits)) = bits.get(section) else {
187 continue;
188 };
189 let mut modified = false;
190 for bit_index in 0..(*size / record_size) {
191 if bit_index >= bits.len() || !bits.get(bit_index) {
192 blob.write_at(
193 bit_index * record_size,
194 empty.clone(),
195 WriteOptions::default(),
196 )
197 .await?;
198 modified = true;
199 }
200 }
201 if modified {
202 blob.sync().await?;
203 }
204 }
205
206 for (section, bits) in bits {
208 if let Some(bits) = bits
209 && bits.count_ones() == 0
210 {
211 continue;
212 }
213
214 let Some((blob, size)) = blobs.get(section) else {
215 return Err(Error::MissingRecord(section * items_per_blob));
216 };
217
218 let mut set_indices = bits.as_ref().map(|bits| bits.ones_iter());
221 let mut all_indices = 0..items_per_blob;
222
223 let mut replay_blob = bits.is_none().then(|| {
227 ReadBuffer::from_pooler(&context, blob.clone(), *size, config.replay_buffer)
228 });
229 while let Some(bit_index) = set_indices
230 .as_mut()
231 .map_or_else(|| all_indices.next(), |indices| indices.next())
232 {
233 let index = section * items_per_blob + bit_index;
234 if bit_index >= items_per_blob {
235 return Err(Error::MissingRecord(index));
236 }
237 let offset = bit_index * record_size;
238 if offset + record_size > *size {
239 return Err(Error::MissingRecord(index));
240 }
241
242 if let Some(replay_blob) = replay_blob.as_mut() {
244 replay_blob.seek_to(offset)?;
245 let record_buf = replay_blob.read(Record::<V>::SIZE).await?.coalesce();
246 if Record::<V>::decode_valid(record_buf.as_ref()).is_none() {
247 return Err(Error::MissingRecord(index));
248 }
249 }
250 items += 1;
251 intervals.insert(index);
252 }
253 }
254 }
255 debug!(
256 items,
257 elapsed = ?context.current().duration_since(start).unwrap_or_default(),
258 "rebuilt intervals"
259 );
260
261 let blobs = blobs
263 .into_iter()
264 .map(|(index, (blob, len))| {
265 (
266 index,
267 Write::from_pooler(&context, blob, len, config.write_buffer),
268 )
269 })
270 .collect();
271
272 let puts = context.counter("puts", "Number of put calls");
274 let gets = context.counter("gets", "Number of get calls");
275 let has = context.counter("has", "Number of has calls");
276 let syncs = context.counter("syncs", "Number of sync calls");
277 let pruned = context.counter("pruned", "Number of pruned blobs");
278
279 Ok(Self {
280 context,
281 config,
282 blobs,
283 intervals,
284 pending: BTreeSet::new(),
285 puts,
286 gets,
287 has,
288 syncs,
289 pruned,
290 _phantom: PhantomData,
291 })
292 }
293
294 async fn put(&mut self, index: u64, value: V) -> Result<(), Error> {
296 self.puts.inc();
297
298 let items_per_blob = self.config.items_per_blob.get();
300 let section = index / items_per_blob;
301 if let Entry::Vacant(entry) = self.blobs.entry(section) {
302 let (blob, len) = self
303 .context
304 .open(&self.config.partition, §ion.to_be_bytes())
305 .await?;
306 entry.insert(Write::from_pooler(
307 &self.context,
308 blob,
309 len,
310 self.config.write_buffer,
311 ));
312 debug!(section, "created blob");
313 }
314
315 let blob = self.blobs.get_mut(§ion).unwrap();
317 let offset = (index % items_per_blob) * Record::<V>::SIZE as u64;
318 blob.write_at(offset, Record::encode(&value)).await?;
319 self.pending.insert(section);
320
321 self.intervals.insert(index);
323
324 Ok(())
325 }
326
327 async fn get(&self, index: u64) -> Result<Option<V>, Error> {
329 self.gets.inc();
330
331 if self.intervals.get(&index).is_none() {
333 return Ok(None);
334 }
335
336 let items_per_blob = self.config.items_per_blob.get();
338 let section = index / items_per_blob;
339 let blob = self.blobs.get(§ion).unwrap();
340 let offset = (index % items_per_blob) * Record::<V>::SIZE as u64;
341 let read_buf = blob.read_at(offset, Record::<V>::SIZE).await?.coalesce();
342
343 let value =
345 Record::<V>::decode_valid(read_buf.as_ref()).ok_or(Error::InvalidRecord(index))?;
346 Ok(Some(value))
347 }
348
349 fn has(&self, index: u64) -> bool {
351 self.has.inc();
352
353 self.intervals.get(&index).is_some()
354 }
355
356 fn next_gap(&self, index: u64) -> (Option<u64>, Option<u64>) {
358 self.intervals.next_gap(index)
359 }
360
361 fn ranges(&self) -> impl Iterator<Item = (u64, u64)> + '_ {
363 self.intervals.iter().map(|(&s, &e)| (s, e))
364 }
365
366 fn ranges_from(&self, from: u64) -> impl Iterator<Item = (u64, u64)> + '_ {
368 self.intervals.iter_from(from).map(|(&s, &e)| (s, e))
369 }
370
371 fn first_index(&self) -> Option<u64> {
373 self.intervals.first_index()
374 }
375
376 fn last_index(&self) -> Option<u64> {
378 self.intervals.last_index()
379 }
380
381 fn missing_items(&self, start: u64, max: usize) -> Vec<u64> {
383 self.intervals.missing_items(start, max)
384 }
385
386 async fn prune(&mut self, min: u64) -> Result<(), Error> {
388 let items_per_blob = self.config.items_per_blob.get();
390 let min_section = min / items_per_blob;
391 let sections_to_remove: Vec<u64> = self
392 .blobs
393 .keys()
394 .filter(|&§ion| section < min_section)
395 .copied()
396 .collect();
397
398 for section in sections_to_remove {
400 if let Some(blob) = self.blobs.remove(§ion) {
401 drop(blob);
402 self.context
403 .remove(&self.config.partition, Some(§ion.to_be_bytes()))
404 .await?;
405
406 let start_index = section * items_per_blob;
408 let end_index = (section + 1) * items_per_blob - 1;
409 self.intervals.remove(start_index, end_index);
410 debug!(section, start_index, end_index, "pruned blob");
411 }
412
413 self.pruned.inc();
415 }
416
417 self.pending.retain(|§ion| section >= min_section);
419
420 Ok(())
421 }
422
423 async fn sync(&mut self) -> Result<(), Error> {
425 self.syncs.inc();
426
427 if self.pending.is_empty() {
428 return Ok(());
429 }
430
431 let futures: Vec<_> = self
432 .blobs
433 .iter_mut()
434 .filter(|(section, _)| self.pending.contains(section))
435 .map(|(_, blob)| blob.sync())
436 .collect();
437 try_join_all(futures).await?;
438
439 self.pending.clear();
441
442 Ok(())
443 }
444
445 async fn destroy(self) -> Result<(), Error> {
447 for (i, blob) in self.blobs.into_iter() {
448 drop(blob);
449 self.context
450 .remove(&self.config.partition, Some(&i.to_be_bytes()))
451 .await?;
452 debug!(section = i, "destroyed blob");
453 }
454 match self.context.remove(&self.config.partition, None).await {
455 Ok(()) => {}
456 Err(RError::PartitionMissing(_)) => {
457 }
459 Err(err) => return Err(Error::Runtime(err)),
460 }
461 Ok(())
462 }
463}
464
465pub struct Ordinal<E: Context, V: CodecFixed<Cfg = ()>>(Box<Inner<E, V>>);
470
471impl<E: Context, V: CodecFixed<Cfg = ()>> std::fmt::Debug for Ordinal<E, V> {
472 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
473 f.debug_struct("Ordinal")
474 .field("first_index", &self.0.intervals.first_index())
475 .field("last_index", &self.0.intervals.last_index())
476 .finish_non_exhaustive()
477 }
478}
479
480impl<E: Context, V: CodecFixed<Cfg = ()>> Ordinal<E, V> {
481 pub async fn init(
491 context: E,
492 config: Config,
493 bits: Option<BTreeMap<u64, &Option<BitMap>>>,
494 ) -> Result<Self, Error> {
495 Ok(Self(Box::new(Inner::init(context, config, bits).await?)))
496 }
497
498 pub async fn put(mut self, index: u64, value: V) -> Result<Self, Error> {
500 self.0.put(index, value).await?;
501 Ok(self)
502 }
503
504 pub async fn get(&self, index: u64) -> Result<Option<V>, Error> {
506 self.0.get(index).await
507 }
508
509 pub fn has(&self, index: u64) -> bool {
511 self.0.has(index)
512 }
513
514 pub fn next_gap(&self, index: u64) -> (Option<u64>, Option<u64>) {
516 self.0.next_gap(index)
517 }
518
519 pub fn ranges(&self) -> impl Iterator<Item = (u64, u64)> + '_ {
521 self.0.ranges()
522 }
523
524 pub fn ranges_from(&self, from: u64) -> impl Iterator<Item = (u64, u64)> + '_ {
526 self.0.ranges_from(from)
527 }
528
529 pub fn first_index(&self) -> Option<u64> {
531 self.0.first_index()
532 }
533
534 pub fn last_index(&self) -> Option<u64> {
536 self.0.last_index()
537 }
538
539 pub fn missing_items(&self, start: u64, max: usize) -> Vec<u64> {
544 self.0.missing_items(start, max)
545 }
546
547 pub async fn prune(mut self, min: u64) -> Result<Self, Error> {
552 self.0.prune(min).await?;
553 Ok(self)
554 }
555
556 pub async fn sync(mut self) -> Result<Self, Error> {
558 self.0.sync().await?;
559 Ok(self)
560 }
561
562 pub async fn destroy(self) -> Result<(), Error> {
564 self.0.destroy().await
565 }
566}
567
568#[cfg(all(test, feature = "arbitrary"))]
569mod conformance {
570 use super::*;
571 use commonware_codec::conformance::CodecConformance;
572
573 commonware_conformance::conformance_tests! {
574 CodecConformance<Record<u32>>
575 }
576}
577
578#[cfg(test)]
579mod tests {
580 use super::*;
581 use commonware_runtime::deterministic::Context;
582
583 type TestOrdinal = Ordinal<Context, u64>;
584
585 fn is_send<T: Send>(_: T) {}
586
587 #[allow(dead_code)]
588 fn assert_ordinal_futures_are_send(ordinal: TestOrdinal, key: u64) {
589 is_send(ordinal.get(key));
590 is_send(ordinal.put(key, 0u64));
591 }
592
593 #[allow(dead_code)]
594 fn assert_ordinal_destroy_is_send(ordinal: TestOrdinal) {
595 is_send(ordinal.destroy());
596 }
597}