use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, PoisonError};
const SNAPSHOT_PREALLOC: usize = 256;
pub struct SnapshotSlot {
bytes: Mutex<Vec<u8>>,
supported: AtomicBool,
}
impl SnapshotSlot {
#[must_use]
pub fn new() -> Arc<Self> {
Arc::new(Self {
bytes: Mutex::new(Vec::with_capacity(SNAPSHOT_PREALLOC)),
supported: AtomicBool::new(false),
})
}
pub fn publish(&self, write: impl FnOnce(&mut Vec<u8>) -> bool) {
if let Ok(mut guard) = self.bytes.try_lock()
&& write(&mut guard)
{
self.supported.store(true, Ordering::Release);
}
}
#[must_use]
pub fn is_supported(&self) -> bool {
self.supported.load(Ordering::Acquire)
}
#[must_use]
pub fn read(&self) -> Option<Vec<u8>> {
if !self.supported.load(Ordering::Acquire) {
return None;
}
Some(
self.bytes
.lock()
.unwrap_or_else(PoisonError::into_inner)
.clone(),
)
}
}
#[cfg(test)]
mod tests {
use super::{SNAPSHOT_PREALLOC, SnapshotSlot};
#[test]
fn buffer_is_prewarmed_so_first_publish_does_not_allocate() {
let slot = SnapshotSlot::new();
slot.publish(|buf| {
buf.clear();
buf.extend_from_slice(&[0xAB; SNAPSHOT_PREALLOC]);
true
});
let published = slot.read().expect("published");
assert_eq!(published.len(), SNAPSHOT_PREALLOC);
}
#[test]
fn read_is_none_until_first_publish() {
let slot = SnapshotSlot::new();
assert!(slot.read().is_none());
slot.publish(|buf| {
buf.clear();
buf.extend_from_slice(&[1, 2, 3]);
true
});
assert_eq!(slot.read(), Some(vec![1, 2, 3]));
}
#[test]
fn unsupported_publish_never_marks_supported() {
let slot = SnapshotSlot::new();
slot.publish(|_| false);
assert!(slot.read().is_none());
}
#[test]
fn is_supported_latches_on_first_true_publish() {
let slot = SnapshotSlot::new();
assert!(!slot.is_supported());
slot.publish(|_| false);
assert!(!slot.is_supported());
slot.publish(|buf| {
buf.clear();
buf.push(1);
true
});
assert!(slot.is_supported());
slot.publish(|buf| {
buf.clear();
true
});
assert!(slot.is_supported());
assert_eq!(slot.read(), Some(vec![]));
}
#[test]
fn publish_overwrites_and_reuses_capacity() {
let slot = SnapshotSlot::new();
slot.publish(|buf| {
buf.clear();
buf.extend_from_slice(&[9; 64]);
true
});
slot.publish(|buf| {
buf.clear();
buf.extend_from_slice(&[7, 7]);
true
});
assert_eq!(slot.read(), Some(vec![7, 7]));
}
}