Skip to content

Commit 784cf99

Browse files
Merge pull request #72 from frankmcsherry/stash
Introduce `Stash` container
2 parents 18f08cd + 6a5357a commit 784cf99

File tree

2 files changed

+109
-1
lines changed

2 files changed

+109
-1
lines changed

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ columnar_derive = { path = "columnar_derive", version = "0.11" }
2424
[dev-dependencies]
2525
bencher = "0.1.5"
2626
bincode = "1.3.3"
27-
rmp-serde = "1.3.0"
2827
serde_json = "1.0"
2928

3029
[features]

src/lib.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -915,6 +915,115 @@ pub mod bytes {
915915
}
916916
}
917917

918+
/// A container of either typed columns, or serialized bytes that can be borrowed as the former.
919+
pub mod stash {
920+
921+
use crate::{Len, FromBytes};
922+
use crate::bytes::{EncodeDecode, Indexed};
923+
924+
/// A container of either typed columns, or serialized bytes that can be borrowed as the former.
925+
///
926+
/// When `B` dereferences to a byte slice, the container can be borrowed as if the container type `C`.
927+
/// This container inherents the readable properties of `C` through borrowing, but does not implement
928+
/// the traits itself.
929+
///
930+
/// The container can be cleared and pushed into. When cleared it reverts to a typed variant, and when
931+
/// pushed into if the typed variant it will accept the item, and if not it will panic.
932+
#[derive(Clone)]
933+
pub enum Stash<C, B> {
934+
/// The typed variant of the container.
935+
Typed(C),
936+
/// The bytes variant of the container.
937+
Bytes(B),
938+
/// Relocated, aligned binary data, if `Bytes` doesn't work for some reason.
939+
///
940+
/// Most commonly this works around misaligned binary data, but it can also be useful if the `B`
941+
/// type is a scarce resource that should be released.
942+
Align(Box<[u64]>),
943+
}
944+
945+
impl<C: Default, B> Default for Stash<C, B> { fn default() -> Self { Self::Typed(Default::default()) } }
946+
947+
impl<C: crate::ContainerBytes, B: std::ops::Deref<Target=[u8]> + Clone + 'static> crate::Borrow for Stash<C, B> {
948+
949+
type Ref<'a> = <C as crate::Borrow>::Ref<'a>;
950+
type Borrowed<'a> = <C as crate::Borrow>::Borrowed<'a>;
951+
952+
#[inline(always)] fn borrow<'a>(&'a self) -> Self::Borrowed<'a> { self.borrow() }
953+
#[inline(always)] fn reborrow<'b, 'a: 'b>(item: Self::Borrowed<'a>) -> Self::Borrowed<'b> where Self: 'a { <C as crate::Borrow>::reborrow(item) }
954+
#[inline(always)] fn reborrow_ref<'b, 'a: 'b>(item: Self::Ref<'a>) -> Self::Ref<'b> where Self: 'a { <C as crate::Borrow>::reborrow_ref(item) }
955+
}
956+
957+
impl<C: crate::ContainerBytes, B: std::ops::Deref<Target=[u8]>> Len for Stash<C, B> {
958+
#[inline(always)] fn len(&self) -> usize { self.borrow().len() }
959+
}
960+
961+
impl<C: crate::ContainerBytes, B: std::ops::Deref<Target=[u8]>> Stash<C, B> {
962+
/// Borrows the contents, either from a typed container or by decoding serialized bytes.
963+
///
964+
/// This method is relatively cheap but is not free.
965+
#[inline(always)] pub fn borrow<'a>(&'a self) -> <C as crate::Borrow>::Borrowed<'a> {
966+
match self {
967+
Stash::Typed(t) => t.borrow(),
968+
Stash::Bytes(b) => <C::Borrowed<'_> as FromBytes>::from_bytes(&mut Indexed::decode(bytemuck::cast_slice(b))),
969+
Stash::Align(a) => <C::Borrowed<'_> as FromBytes>::from_bytes(&mut Indexed::decode(a)),
970+
}
971+
}
972+
/// The number of bytes needed to write the contents using the `Indexed` encoder.
973+
pub fn length_in_bytes(&self) -> usize {
974+
match self {
975+
// We'll need one u64 for the length, then the length rounded up to a multiple of 8.
976+
Stash::Typed(t) => 8 * Indexed::length_in_words(&t.borrow()),
977+
Stash::Bytes(b) => b.len(),
978+
Stash::Align(a) => 8 * a.len(),
979+
}
980+
}
981+
/// Write the contents into a `std::io::Write` using the `Indexed` encoder.
982+
pub fn into_bytes<W: ::std::io::Write>(&self, writer: &mut W) {
983+
match self {
984+
Stash::Typed(t) => { Indexed::write(writer, &t.borrow()).unwrap() },
985+
Stash::Bytes(b) => writer.write_all(&b[..]).unwrap(),
986+
Stash::Align(a) => writer.write_all(bytemuck::cast_slice(&a[..])).unwrap(),
987+
}
988+
}
989+
}
990+
991+
impl<T, C: crate::Container + crate::Push<T>, B> crate::Push<T> for Stash<C, B> {
992+
fn push(&mut self, item: T) {
993+
match self {
994+
Stash::Typed(t) => t.push(item),
995+
Stash::Bytes(_) | Stash::Align(_) => unimplemented!(),
996+
}
997+
}
998+
}
999+
1000+
impl<C: crate::Clear + Default, B> crate::Clear for Stash<C, B> {
1001+
fn clear(&mut self) {
1002+
match self {
1003+
Stash::Typed(t) => t.clear(),
1004+
Stash::Bytes(_) | Stash::Align(_) => {
1005+
*self = Stash::Typed(Default::default());
1006+
}
1007+
}
1008+
}
1009+
}
1010+
1011+
impl<C: crate::Container, B: std::ops::Deref<Target = [u8]>> From<B> for Stash<C, B> {
1012+
fn from(bytes: B) -> Self {
1013+
assert!(bytes.len() % 8 == 0);
1014+
if bytemuck::try_cast_slice::<_, u64>(&bytes).is_ok() {
1015+
Self::Bytes(bytes)
1016+
}
1017+
else {
1018+
// Re-locating bytes for alignment reasons.
1019+
let mut alloc: Vec<u64> = vec![0; bytes.len() / 8];
1020+
bytemuck::cast_slice_mut(&mut alloc[..]).copy_from_slice(&bytes[..]);
1021+
Self::Align(alloc.into())
1022+
}
1023+
}
1024+
}
1025+
}
1026+
9181027
#[cfg(test)]
9191028
mod test {
9201029
use crate::ContainerOf;

0 commit comments

Comments
 (0)