use alloc::sync::Arc;
use alloc::vec::Vec;
pub(crate) use crate::DataType as Type;
pub(crate) const fn type_mask(ty: Type) -> u8 {
match ty {
Type::Serial => 1,
Type::Struct => 2,
Type::Numeric => 4,
Type::String => 8,
}
}
pub(crate) const fn type_code(ty: Type) -> u8 {
match ty {
Type::Serial => 0,
Type::Struct => 1,
Type::Numeric => 2,
Type::String => 3,
}
}
#[derive(Clone, Debug)]
pub(crate) enum Bytes<'a> {
Borrowed(&'a [u8]),
Shared {
buf: Arc<Vec<u8>>,
start: usize,
len: usize,
},
}
impl<'a> Bytes<'a> {
pub(crate) fn as_slice(&self) -> &[u8] {
match self {
Self::Borrowed(bytes) => bytes,
Self::Shared { buf, start, len } => &buf[*start..*start + *len],
}
}
pub(crate) fn window(&self, offset: usize, len: usize) -> Option<Self> {
let end = offset.checked_add(len)?;
if end > self.as_slice().len() {
return None;
}
Some(match self {
Self::Borrowed(bytes) => Self::Borrowed(&bytes[offset..end]),
Self::Shared { buf, start, .. } => Self::Shared {
buf: Arc::clone(buf),
start: start + offset,
len,
},
})
}
}
impl From<Vec<u8>> for Bytes<'_> {
fn from(buf: Vec<u8>) -> Self {
let len = buf.len();
Self::Shared {
buf: Arc::new(buf),
start: 0,
len,
}
}
}
#[derive(Clone, Debug)]
pub(crate) enum Lens<'a> {
None,
Borrowed(&'a [u32]),
Shared(Arc<Vec<u32>>),
}
impl Lens<'_> {
pub(crate) fn as_slice(&self) -> &[u32] {
match self {
Self::None => &[],
Self::Borrowed(lens) => lens,
Self::Shared(lens) => lens,
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct Stream<'a> {
ty: Type,
width: usize,
bytes: Bytes<'a>,
lens: Lens<'a>,
metadata: Vec<(i32, i32)>,
}
impl<'a> Stream<'a> {
pub(crate) const fn new(ty: Type, width: usize, bytes: Bytes<'a>) -> Self {
Self {
ty,
width,
bytes,
lens: Lens::None,
metadata: Vec::new(),
}
}
pub(crate) const fn serial(bytes: &'a [u8]) -> Self {
Self::new(Type::Serial, 1, Bytes::Borrowed(bytes))
}
pub(crate) const fn strings(bytes: &'a [u8], lens: &'a [u32]) -> Self {
Self {
ty: Type::String,
width: 1,
bytes: Bytes::Borrowed(bytes),
lens: Lens::Borrowed(lens),
metadata: Vec::new(),
}
}
pub(crate) fn strings_from(bytes: Bytes<'a>, lens: Vec<u32>) -> Self {
Self {
ty: Type::String,
width: 1,
bytes,
lens: Lens::Shared(Arc::new(lens)),
metadata: Vec::new(),
}
}
pub(crate) const fn ty(&self) -> Type {
self.ty
}
pub(crate) const fn width(&self) -> usize {
self.width
}
pub(crate) fn bytes(&self) -> &[u8] {
self.bytes.as_slice()
}
pub(crate) const fn content(&self) -> &Bytes<'a> {
&self.bytes
}
pub(crate) fn lens(&self) -> &[u32] {
self.lens.as_slice()
}
pub(crate) fn content_size(&self) -> usize {
self.bytes.as_slice().len()
}
pub(crate) fn n_elts(&self) -> usize {
match self.ty {
Type::String => self.lens.as_slice().len(),
_ => self.content_size() / self.width.max(1),
}
}
pub(crate) fn weight(&self) -> usize {
self.content_size()
+ if self.ty == Type::String {
4 * self.n_elts()
} else {
0
}
}
pub(crate) fn set_metadata(&mut self, id: i32, value: i32) {
match self.metadata.iter_mut().find(|(key, _)| *key == id) {
Some(slot) => slot.1 = value,
None => self.metadata.push((id, value)),
}
}
pub(crate) fn metadata(&self) -> &[(i32, i32)] {
&self.metadata
}
pub(crate) fn slice(&self, first: usize, count: usize) -> Option<Stream<'a>> {
let end = first.checked_add(count)?;
if end > self.n_elts() {
return None;
}
if self.ty == Type::String {
let lens = self.lens.as_slice();
let skip: usize = lens[..first].iter().map(|&n| n as usize).sum();
let take: usize = lens[first..end].iter().map(|&n| n as usize).sum();
let bytes = self.bytes.window(skip, take)?;
let lens = match &self.lens {
Lens::Borrowed(all) => Lens::Borrowed(&all[first..end]),
_ => Lens::Shared(Arc::new(lens[first..end].to_vec())),
};
return Some(Self {
ty: self.ty,
width: 1,
bytes,
lens,
metadata: Vec::new(),
});
}
Some(Self::new(
self.ty,
self.width,
self.bytes.window(first * self.width, count * self.width)?,
))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn masks_and_codes_follow_the_reference() {
let types = [Type::Serial, Type::Struct, Type::Numeric, Type::String];
assert_eq!(types.map(type_mask), [1, 2, 4, 8]);
assert_eq!(types.map(type_code), [0, 1, 2, 3]);
}
#[test]
fn windows_share_or_borrow_and_stay_in_bounds() {
let owned = Bytes::from(alloc::vec![1u8, 2, 3, 4]);
let window = owned.window(1, 2).unwrap();
assert_eq!(window.as_slice(), [2, 3]);
assert_eq!(window.window(1, 1).unwrap().as_slice(), [3]);
assert!(owned.window(3, 2).is_none());
assert!(owned.window(usize::MAX, 2).is_none());
let data = [9u8, 8, 7];
let borrowed = Bytes::Borrowed(&data);
assert_eq!(borrowed.window(2, 1).unwrap().as_slice(), [7]);
assert!(borrowed.window(2, 2).is_none());
}
#[test]
fn streams_report_elements_weight_and_metadata() {
let mut numbers = Stream::new(Type::Numeric, 4, Bytes::from(alloc::vec![0u8; 12]));
assert_eq!(
(numbers.n_elts(), numbers.content_size(), numbers.weight()),
(3, 12, 12)
);
assert!(numbers.metadata().is_empty());
numbers.set_metadata(7, 1);
numbers.set_metadata(7, 2);
numbers.set_metadata(8, 3);
assert_eq!(numbers.metadata(), [(7, 2), (8, 3)]);
assert_eq!(numbers.content().as_slice().len(), 12);
assert_eq!((numbers.ty(), numbers.width()), (Type::Numeric, 4));
let strings = Stream::strings(b"abcdef", &[1, 2, 3]);
assert_eq!((strings.n_elts(), strings.weight()), (3, 6 + 12));
assert_eq!(strings.lens(), [1, 2, 3]);
assert!(numbers.lens().is_empty());
}
#[test]
fn slicing_cuts_elements_and_strings() {
let numbers = Stream::new(
Type::Numeric,
2,
Bytes::from(alloc::vec![1u8, 0, 2, 0, 3, 0]),
);
let tail = numbers.slice(1, 2).unwrap();
assert_eq!((tail.bytes(), tail.n_elts()), (&[2u8, 0, 3, 0][..], 2));
assert!(numbers.slice(2, 2).is_none());
let strings = Stream::strings(b"abcdef", &[1, 2, 3]);
let part = strings.slice(1, 2).unwrap();
assert_eq!((part.bytes(), part.lens()), (&b"bcdef"[..], &[2u32, 3][..]));
let owned = Stream::strings_from(Bytes::from(b"abcdef".to_vec()), alloc::vec![1, 2, 3]);
let part = owned.slice(0, 2).unwrap();
assert_eq!((part.bytes(), part.lens()), (&b"abc"[..], &[1u32, 2][..]));
assert!(owned.slice(1, 3).is_none());
}
}