pub mod kind;
pub mod pool;
use std::sync::Arc;
pub use self::kind::{BufferKind, BufferKindRegistry};
pub use self::pool::{BufferPool, BufferPoolSnapshot, PoolClass};
#[derive(Debug, Clone)]
pub struct ZeroCopyBuffer {
storage: Arc<[u8]>,
start: usize,
end: usize,
kind: BufferKind,
tenant_id: Option<Arc<str>>,
}
impl ZeroCopyBuffer {
pub fn from_bytes(bytes: impl Into<Vec<u8>>, kind: BufferKind) -> Self {
let v = bytes.into();
let len = v.len();
let storage: Arc<[u8]> = v.into();
ZeroCopyBuffer {
storage,
start: 0,
end: len,
kind,
tenant_id: None,
}
}
pub fn from_arc(storage: Arc<[u8]>, kind: BufferKind) -> Self {
let len = storage.len();
ZeroCopyBuffer {
storage,
start: 0,
end: len,
kind,
tenant_id: None,
}
}
pub fn with_tenant(mut self, tenant_id: impl Into<Arc<str>>) -> Self {
self.tenant_id = Some(tenant_id.into());
self
}
pub fn len(&self) -> usize {
self.end - self.start
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn kind(&self) -> BufferKind {
self.kind.clone()
}
pub fn tenant_id(&self) -> Option<&str> {
self.tenant_id.as_deref()
}
pub fn retag(&self, kind: BufferKind) -> Self {
let mut cloned = self.clone();
cloned.kind = kind;
cloned
}
pub fn as_slice(&self) -> &[u8] {
&self.storage[self.start..self.end]
}
pub fn slice(&self, range: std::ops::Range<usize>) -> Self {
let len = self.len();
assert!(
range.start <= range.end,
"slice start {} > end {}",
range.start,
range.end
);
assert!(
range.end <= len,
"slice end {} exceeds buffer len {}",
range.end,
len
);
ZeroCopyBuffer {
storage: Arc::clone(&self.storage),
start: self.start + range.start,
end: self.start + range.end,
kind: self.kind.clone(),
tenant_id: self.tenant_id.clone(),
}
}
pub fn sharers(&self) -> usize {
Arc::strong_count(&self.storage)
}
pub fn sha256(&self) -> [u8; 32] {
use sha2::{Digest, Sha256};
let mut h = Sha256::new();
h.update(self.as_slice());
let out = h.finalize();
let mut array = [0u8; 32];
array.copy_from_slice(&out);
array
}
}
#[derive(Debug)]
pub struct BufferMut {
storage: Vec<u8>,
kind: BufferKind,
tenant_id: Option<Arc<str>>,
}
impl BufferMut {
pub fn with_capacity(capacity: usize, kind: BufferKind) -> Self {
BufferMut {
storage: Vec::with_capacity(capacity),
kind,
tenant_id: None,
}
}
pub fn with_tenant(mut self, tenant_id: impl Into<Arc<str>>) -> Self {
self.tenant_id = Some(tenant_id.into());
self
}
pub fn len(&self) -> usize {
self.storage.len()
}
pub fn is_empty(&self) -> bool {
self.storage.is_empty()
}
pub fn capacity(&self) -> usize {
self.storage.capacity()
}
pub fn extend_from_slice(&mut self, bytes: &[u8]) {
self.storage.extend_from_slice(bytes);
}
pub fn freeze(self) -> ZeroCopyBuffer {
let len = self.storage.len();
let storage: Arc<[u8]> = self.storage.into();
ZeroCopyBuffer {
storage,
start: 0,
end: len,
kind: self.kind,
tenant_id: self.tenant_id,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_bytes_roundtrip() {
let b = ZeroCopyBuffer::from_bytes(vec![1, 2, 3], BufferKind::raw());
assert_eq!(b.len(), 3);
assert_eq!(b.as_slice(), &[1, 2, 3]);
assert_eq!(b.kind(), BufferKind::raw());
}
#[test]
fn clone_shares_storage() {
let b = ZeroCopyBuffer::from_bytes(vec![0u8; 1024], BufferKind::raw());
let c = b.clone();
assert_eq!(b.sharers(), 2);
assert_eq!(c.sharers(), 2);
drop(c);
assert_eq!(b.sharers(), 1);
}
#[test]
fn slice_shares_storage_and_preserves_kind() {
let b = ZeroCopyBuffer::from_bytes(
vec![10, 20, 30, 40],
BufferKind::pcm16(),
);
let s = b.slice(1..3);
assert_eq!(s.as_slice(), &[20, 30]);
assert_eq!(s.kind(), BufferKind::pcm16());
assert_eq!(b.sharers(), 2);
}
#[test]
fn slice_out_of_range_panics() {
let b = ZeroCopyBuffer::from_bytes(vec![1, 2, 3], BufferKind::raw());
let result = std::panic::catch_unwind(|| {
let _ = b.slice(0..10);
});
assert!(result.is_err());
}
#[test]
fn retag_leaves_original_kind() {
let b = ZeroCopyBuffer::from_bytes(vec![1, 2, 3], BufferKind::raw());
let j = b.retag(BufferKind::jpeg());
assert_eq!(b.kind(), BufferKind::raw());
assert_eq!(j.kind(), BufferKind::jpeg());
assert!(b.sharers() >= 2);
}
#[test]
fn buffer_mut_freeze_reuses_allocation() {
let mut bm = BufferMut::with_capacity(1024, BufferKind::raw());
bm.extend_from_slice(b"hello ");
bm.extend_from_slice(b"world");
let frozen = bm.freeze();
assert_eq!(frozen.as_slice(), b"hello world");
assert_eq!(frozen.len(), 11);
}
#[test]
fn sha256_computes_on_visible_slice_only() {
let b = ZeroCopyBuffer::from_bytes(
vec![b'a', b'b', b'c', b'd'],
BufferKind::raw(),
);
let s = b.slice(1..3); let reference = ZeroCopyBuffer::from_bytes(
b"bc".to_vec(),
BufferKind::raw(),
);
assert_eq!(s.sha256(), reference.sha256());
}
#[test]
fn tenant_tag_propagates_through_clone_and_slice() {
let b = ZeroCopyBuffer::from_bytes(
vec![0u8; 16],
BufferKind::raw(),
)
.with_tenant("alpha");
let c = b.clone();
assert_eq!(c.tenant_id(), Some("alpha"));
let s = b.slice(0..4);
assert_eq!(s.tenant_id(), Some("alpha"));
}
}