diff --git a/columnar_derive/src/lib.rs b/columnar_derive/src/lib.rs index 6547a9a..6d777fb 100644 --- a/columnar_derive/src/lib.rs +++ b/columnar_derive/src/lib.rs @@ -317,10 +317,20 @@ fn derive_struct(name: &syn::Ident, generics: &syn::Generics, data_struct: syn:: quote! { impl #impl_gen ::columnar::FromBytes<'columnar> for #c_ident #ty_gen #where_clause { + const SLICE_COUNT: usize = 0 #(+ <#container_types>::SLICE_COUNT)*; #[inline(always)] fn from_bytes(bytes: &mut impl Iterator) -> Self { Self { #(#names: ::columnar::FromBytes::from_bytes(bytes),)* } } + #[inline(always)] + fn from_byte_slices(bytes: &[&'columnar [u8]]) -> Self { + let mut _offset = 0; + #( + let #names = <#container_types>::from_byte_slices(&bytes[_offset .. _offset + <#container_types>::SLICE_COUNT]); + _offset += <#container_types>::SLICE_COUNT; + )* + Self { #(#names,)* } + } } } }; @@ -500,10 +510,15 @@ fn derive_unit_struct(name: &syn::Ident, _generics: &syn::Generics, vis: syn::Vi } impl<'columnar> ::columnar::FromBytes<'columnar> for #c_ident <&'columnar u64> { + const SLICE_COUNT: usize = 1; #[inline(always)] fn from_bytes(bytes: &mut impl Iterator) -> Self { Self { count: &::columnar::bytemuck::try_cast_slice(bytes.next().unwrap()).unwrap()[0] } } + #[inline(always)] + fn from_byte_slices(bytes: &[&'columnar [u8]]) -> Self { + Self { count: &::columnar::bytemuck::try_cast_slice(bytes[0]).unwrap()[0] } + } } impl ::columnar::Columnar for #name { @@ -884,6 +899,7 @@ fn derive_enum(name: &syn::Ident, generics: &syn:: Generics, data_enum: syn::Dat quote! { #[allow(non_snake_case)] impl #impl_gen ::columnar::FromBytes<'columnar> for #c_ident #ty_gen #where_clause { + const SLICE_COUNT: usize = 0 #(+ <#container_types>::SLICE_COUNT)* + CVar::SLICE_COUNT + COff::SLICE_COUNT; #[inline(always)] fn from_bytes(bytes: &mut impl Iterator) -> Self { Self { @@ -892,6 +908,18 @@ fn derive_enum(name: &syn::Ident, generics: &syn:: Generics, data_enum: syn::Dat offset: ::columnar::FromBytes::from_bytes(bytes), } } + #[inline(always)] + fn from_byte_slices(bytes: &[&'columnar [u8]]) -> Self { + let mut _offset = 0; + #( + let #names = <#container_types>::from_byte_slices(&bytes[_offset .. _offset + <#container_types>::SLICE_COUNT]); + _offset += <#container_types>::SLICE_COUNT; + )* + let variant = CVar::from_byte_slices(&bytes[_offset .. _offset + CVar::SLICE_COUNT]); + _offset += CVar::SLICE_COUNT; + let offset = COff::from_byte_slices(&bytes[_offset ..]); + Self { #(#names,)* variant, offset } + } } } }; @@ -1153,10 +1181,15 @@ fn derive_tags(name: &syn::Ident, _generics: &syn:: Generics, data_enum: syn::Da } impl<'columnar, CVar: ::columnar::FromBytes<'columnar>> ::columnar::FromBytes<'columnar> for #c_ident { + const SLICE_COUNT: usize = CVar::SLICE_COUNT; #[inline(always)] fn from_bytes(bytes: &mut impl Iterator) -> Self { Self { variant: ::columnar::FromBytes::from_bytes(bytes) } } + #[inline(always)] + fn from_byte_slices(bytes: &[&'columnar [u8]]) -> Self { + Self { variant: CVar::from_byte_slices(bytes) } + } } impl ::columnar::Columnar for #name { diff --git a/src/arc.rs b/src/arc.rs index 2ed3840..a526a67 100644 --- a/src/arc.rs +++ b/src/arc.rs @@ -23,7 +23,9 @@ impl<'a, T: AsBytes<'a>> AsBytes<'a> for Arc { #[inline(always)] fn as_bytes(&self) -> impl Iterator { self.as_ref().as_bytes() } } impl<'a, T: FromBytes<'a>> FromBytes<'a> for Arc { + const SLICE_COUNT: usize = T::SLICE_COUNT; #[inline(always)] fn from_bytes(bytes: &mut impl Iterator) -> Self { Arc::new(T::from_bytes(bytes)) } + #[inline(always)] fn from_byte_slices(bytes: &[&'a [u8]]) -> Self { Arc::new(T::from_byte_slices(bytes)) } } #[cfg(test)] diff --git a/src/boxed.rs b/src/boxed.rs index 13e6d35..52f019c 100644 --- a/src/boxed.rs +++ b/src/boxed.rs @@ -59,7 +59,9 @@ impl<'a, C: AsBytes<'a>> AsBytes<'a> for Boxed { #[inline(always)] fn as_bytes(&self) -> impl Iterator { self.0.as_bytes() } } impl<'a, C: FromBytes<'a>> FromBytes<'a> for Boxed { + const SLICE_COUNT: usize = C::SLICE_COUNT; #[inline(always)] fn from_bytes(bytes: &mut impl Iterator) -> Self { Self(C::from_bytes(bytes)) } + #[inline(always)] fn from_byte_slices(bytes: &[&'a [u8]]) -> Self { Self(C::from_byte_slices(bytes)) } } impl Index for Boxed { type Ref = Boxed; diff --git a/src/bytes.rs b/src/bytes.rs index c2df6e9..84219cd 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -243,20 +243,26 @@ pub mod serialization_neu { } /// Decodes an encoded sequence of byte slices. Each result will be `u64` aligned. + #[inline(always)] pub fn decode(store: &[u64]) -> impl Iterator { - assert!(store[0] % 8 == 0); - let slices = (store[0] / 8) - 1; - (0 .. slices).map(|i| decode_index(store, i)) + let slices = store[0] as usize / 8 - 1; + let index = &store[..slices + 1]; + let last = index[slices] as usize; + let bytes: &[u8] = &bytemuck::cast_slice(store)[..last]; + (0 .. slices).map(move |i| { + let upper = (index[i + 1] as usize).min(last); + let lower = (((index[i] as usize) + 7) & !7).min(upper); + &bytes[lower .. upper] + }) } /// Decodes a specific byte slice by index. It will be `u64` aligned. #[inline(always)] pub fn decode_index(store: &[u64], index: u64) -> &[u8] { - debug_assert!(index + 1 < store[0]/8); - let index: usize = index.try_into().unwrap(); - let lower: usize = ((store[index] + 7) & !7).try_into().unwrap(); - let upper: usize = (store[index + 1]).try_into().unwrap(); - let bytes: &[u8] = bytemuck::try_cast_slice(store).expect("&[u64] should convert to &[u8]"); + let index = index as usize; + let bytes: &[u8] = bytemuck::cast_slice(store); + let upper = (store[index + 1] as usize).min(bytes.len()); + let lower = (((store[index] as usize) + 7) & !7).min(upper); &bytes[lower .. upper] } @@ -433,5 +439,35 @@ mod test { assert_eq!(column3.get(2*i+0), column2.get(2*i+0)); assert_eq!(column3.get(2*i+1), column2.get(2*i+1)); } + + // Test from_byte_slices round-trip. + let byte_vec: Vec<&[u8]> = column.borrow().as_bytes().map(|(_, bytes)| bytes).collect(); + let column4 = crate::Results::<&[u64], &[u64], &[u64], &[u64], &u64>::from_byte_slices(&byte_vec); + for i in 0..100 { + assert_eq!(column.get(2*i+0), column4.get(2*i+0).copied().map_err(|e| *e)); + assert_eq!(column.get(2*i+1), column4.get(2*i+1).copied().map_err(|e| *e)); + } + } + + /// Test from_byte_slices for tuples. + #[test] + fn from_byte_slices_tuple() { + use crate::common::{Push, Index}; + use crate::{Borrow, AsBytes, FromBytes, ContainerOf}; + + let mut column: ContainerOf<(u64, String, Vec)> = Default::default(); + for i in 0..50u64 { + column.push(&(i, format!("hello {i}"), vec![i as u32; i as usize])); + } + + let byte_vec: Vec<&[u8]> = column.borrow().as_bytes().map(|(_, bytes)| bytes).collect(); + type Borrowed<'a> = )> as crate::Borrow>::Borrowed<'a>; + let reconstructed = Borrowed::from_byte_slices(&byte_vec); + for i in 0..50 { + let (a, b, _c) = reconstructed.get(i); + assert_eq!(*a, i as u64); + assert_eq!(b, &*format!("hello {i}")); + } } + } diff --git a/src/lib.rs b/src/lib.rs index 8115157..8691749 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -619,6 +619,11 @@ pub mod common { /// Implementors of this trait almost certainly reference the lifetime `'a` themselves, /// unless they actively deserialize the bytes (vs sit on the slices, as if zero-copy). pub trait FromBytes<'a> { + /// The number of byte slices this type consumes when reconstructed. + /// + /// This enables `from_byte_slices`, which can index directly into a slice + /// of byte slices rather than consuming from an iterator sequentially. + const SLICE_COUNT: usize; /// Reconstructs `self` from a sequence of correctly aligned and sized bytes slices. /// /// The implementation is expected to consume the right number of items from the iterator, @@ -631,6 +636,14 @@ pub mod common { /// they are inlined. A single non-inlined function on a tree of `from_bytes` calls /// can cause the performance to drop significantly. fn from_bytes(bytes: &mut impl Iterator) -> Self; + /// Reconstructs `self` from a slice of byte slices, using direct indexing. + /// + /// The slice should contain exactly `Self::SLICE_COUNT` elements. + /// This avoids the iterator chain overhead of `from_bytes`. + #[inline(always)] + fn from_byte_slices(bytes: &[&'a [u8]]) -> Self where Self: Sized { + Self::from_bytes(&mut bytes.iter().copied()) + } } } diff --git a/src/primitive.rs b/src/primitive.rs index 6c8829e..b608b44 100644 --- a/src/primitive.rs +++ b/src/primitive.rs @@ -21,11 +21,16 @@ macro_rules! implement_columnable { } } impl<'a> crate::FromBytes<'a> for &'a [$index_type] { + const SLICE_COUNT: usize = 1; #[inline(always)] fn from_bytes(bytes: &mut impl Iterator) -> Self { // We use `unwrap()` here in order to panic with the `bytemuck` error, which may be informative. bytemuck::try_cast_slice(bytes.next().expect("Iterator exhausted prematurely")).unwrap() } + #[inline(always)] + fn from_byte_slices(bytes: &[&'a [u8]]) -> Self { + bytemuck::try_cast_slice(bytes[0]).unwrap() + } } impl<'a, const N: usize> crate::AsBytes<'a> for &'a [[$index_type; N]] { #[inline(always)] @@ -34,11 +39,16 @@ macro_rules! implement_columnable { } } impl<'a, const N: usize> crate::FromBytes<'a> for &'a [[$index_type; N]] { + const SLICE_COUNT: usize = 1; #[inline(always)] fn from_bytes(bytes: &mut impl Iterator) -> Self { // We use `unwrap()` here in order to panic with the `bytemuck` error, which may be informative. bytemuck::try_cast_slice(bytes.next().expect("Iterator exhausted prematurely")).unwrap() } + #[inline(always)] + fn from_byte_slices(bytes: &[&'a [u8]]) -> Self { + bytemuck::try_cast_slice(bytes[0]).unwrap() + } } )* } } @@ -126,10 +136,15 @@ mod sizes { } impl<'a, CV: crate::FromBytes<'a>> crate::FromBytes<'a> for crate::primitive::Usizes { + const SLICE_COUNT: usize = CV::SLICE_COUNT; #[inline(always)] fn from_bytes(bytes: &mut impl Iterator) -> Self { Self { values: CV::from_bytes(bytes) } } + #[inline(always)] + fn from_byte_slices(bytes: &[&'a [u8]]) -> Self { + Self { values: CV::from_byte_slices(bytes) } + } } @@ -203,10 +218,15 @@ mod sizes { } impl<'a, CV: crate::FromBytes<'a>> crate::FromBytes<'a> for crate::primitive::Isizes { + const SLICE_COUNT: usize = CV::SLICE_COUNT; #[inline(always)] fn from_bytes(bytes: &mut impl Iterator) -> Self { Self { values: CV::from_bytes(bytes) } } + #[inline(always)] + fn from_byte_slices(bytes: &[&'a [u8]]) -> Self { + Self { values: CV::from_byte_slices(bytes) } + } } } @@ -285,10 +305,15 @@ mod chars { } impl<'a, CV: crate::FromBytes<'a>> crate::FromBytes<'a> for Chars { + const SLICE_COUNT: usize = CV::SLICE_COUNT; #[inline(always)] fn from_bytes(bytes: &mut impl Iterator) -> Self { Self { values: CV::from_bytes(bytes) } } + #[inline(always)] + fn from_byte_slices(bytes: &[&'a [u8]]) -> Self { + Self { values: CV::from_byte_slices(bytes) } + } } } @@ -367,10 +392,15 @@ mod larges { } impl<'a, CV: crate::FromBytes<'a>> crate::FromBytes<'a> for U128s { + const SLICE_COUNT: usize = CV::SLICE_COUNT; #[inline(always)] fn from_bytes(bytes: &mut impl Iterator) -> Self { Self { values: CV::from_bytes(bytes) } } + #[inline(always)] + fn from_byte_slices(bytes: &[&'a [u8]]) -> Self { + Self { values: CV::from_byte_slices(bytes) } + } } #[derive(Copy, Clone, Default)] @@ -439,10 +469,15 @@ mod larges { } impl<'a, CV: crate::FromBytes<'a>> crate::FromBytes<'a> for I128s { + const SLICE_COUNT: usize = CV::SLICE_COUNT; #[inline(always)] fn from_bytes(bytes: &mut impl Iterator) -> Self { Self { values: CV::from_bytes(bytes) } } + #[inline(always)] + fn from_byte_slices(bytes: &[&'a [u8]]) -> Self { + Self { values: CV::from_byte_slices(bytes) } + } } } @@ -534,10 +569,15 @@ pub mod offsets { } } impl<'a, const K: u64> crate::FromBytes<'a> for Fixeds { + const SLICE_COUNT: usize = 1; #[inline(always)] fn from_bytes(bytes: &mut impl Iterator) -> Self { Self { count: &bytemuck::try_cast_slice(bytes.next().expect("Iterator exhausted prematurely")).unwrap()[0] } } + #[inline(always)] + fn from_byte_slices(bytes: &[&'a [u8]]) -> Self { + Self { count: &bytemuck::try_cast_slice(bytes[0]).unwrap()[0] } + } } use super::Strides; @@ -622,6 +662,7 @@ pub mod offsets { } } impl<'a, BC: FromBytes<'a>> FromBytes<'a> for Strides { + const SLICE_COUNT: usize = 2 + BC::SLICE_COUNT; #[inline(always)] fn from_bytes(bytes: &mut impl Iterator) -> Self { let stride = &bytemuck::try_cast_slice(bytes.next().expect("Iterator exhausted prematurely")).unwrap()[0]; @@ -629,6 +670,13 @@ pub mod offsets { let bounds = BC::from_bytes(bytes); Self { stride, length, bounds } } + #[inline(always)] + fn from_byte_slices(bytes: &[&'a [u8]]) -> Self { + let stride = &bytemuck::try_cast_slice(bytes[0]).unwrap()[0]; + let length = &bytemuck::try_cast_slice(bytes[1]).unwrap()[0]; + let bounds = BC::from_byte_slices(&bytes[2..]); + Self { stride, length, bounds } + } } impl Strides { @@ -809,10 +857,15 @@ mod empty { } } impl<'a> crate::FromBytes<'a> for crate::primitive::Empties<&'a u64> { + const SLICE_COUNT: usize = 1; #[inline(always)] fn from_bytes(bytes: &mut impl Iterator) -> Self { Self { count: &bytemuck::try_cast_slice(bytes.next().expect("Iterator exhausted prematurely")).unwrap()[0], empty: () } } + #[inline(always)] + fn from_byte_slices(bytes: &[&'a [u8]]) -> Self { + Self { count: &bytemuck::try_cast_slice(bytes[0]).unwrap()[0], empty: () } + } } } @@ -882,6 +935,7 @@ mod boolean { } impl<'a, VC: crate::FromBytes<'a>> crate::FromBytes<'a> for crate::primitive::Bools { + const SLICE_COUNT: usize = VC::SLICE_COUNT + 2; #[inline(always)] fn from_bytes(bytes: &mut impl Iterator) -> Self { let values = crate::FromBytes::from_bytes(bytes); @@ -889,6 +943,13 @@ mod boolean { let last_bits = &bytemuck::try_cast_slice(bytes.next().expect("Iterator exhausted prematurely")).unwrap()[0]; Self { values, last_word, last_bits } } + #[inline(always)] + fn from_byte_slices(bytes: &[&'a [u8]]) -> Self { + let values = VC::from_byte_slices(&bytes[..VC::SLICE_COUNT]); + let last_word = &bytemuck::try_cast_slice(bytes[VC::SLICE_COUNT]).unwrap()[0]; + let last_bits = &bytemuck::try_cast_slice(bytes[VC::SLICE_COUNT + 1]).unwrap()[0]; + Self { values, last_word, last_bits } + } } impl> Len for Bools { @@ -1016,6 +1077,7 @@ mod duration { } } impl<'a, SC: crate::FromBytes<'a>, NC: crate::FromBytes<'a>> crate::FromBytes<'a> for crate::primitive::Durations { + const SLICE_COUNT: usize = SC::SLICE_COUNT + NC::SLICE_COUNT; #[inline(always)] fn from_bytes(bytes: &mut impl Iterator) -> Self { Self { @@ -1023,6 +1085,13 @@ mod duration { nanoseconds: crate::FromBytes::from_bytes(bytes), } } + #[inline(always)] + fn from_byte_slices(bytes: &[&'a [u8]]) -> Self { + Self { + seconds: SC::from_byte_slices(&bytes[..SC::SLICE_COUNT]), + nanoseconds: NC::from_byte_slices(&bytes[SC::SLICE_COUNT..]), + } + } } impl Len for Durations { diff --git a/src/rc.rs b/src/rc.rs index bb1752c..62d80ff 100644 --- a/src/rc.rs +++ b/src/rc.rs @@ -23,7 +23,9 @@ impl<'a, T: AsBytes<'a>> AsBytes<'a> for Rc { #[inline(always)] fn as_bytes(&self) -> impl Iterator { self.as_ref().as_bytes() } } impl<'a, T: FromBytes<'a>> FromBytes<'a> for Rc { + const SLICE_COUNT: usize = T::SLICE_COUNT; #[inline(always)] fn from_bytes(bytes: &mut impl Iterator) -> Self { Rc::new(T::from_bytes(bytes)) } + #[inline(always)] fn from_byte_slices(bytes: &[&'a [u8]]) -> Self { Rc::new(T::from_byte_slices(bytes)) } } #[cfg(test)] diff --git a/src/string.rs b/src/string.rs index 15b2f8c..8bc280c 100644 --- a/src/string.rs +++ b/src/string.rs @@ -94,6 +94,7 @@ impl<'a, BC: crate::AsBytes<'a>, VC: crate::AsBytes<'a>> crate::AsBytes<'a> for } } impl<'a, BC: crate::FromBytes<'a>, VC: crate::FromBytes<'a>> crate::FromBytes<'a> for Strings { + const SLICE_COUNT: usize = BC::SLICE_COUNT + VC::SLICE_COUNT; #[inline(always)] fn from_bytes(bytes: &mut impl Iterator) -> Self { Self { @@ -101,6 +102,13 @@ impl<'a, BC: crate::FromBytes<'a>, VC: crate::FromBytes<'a>> crate::FromBytes<'a values: crate::FromBytes::from_bytes(bytes), } } + #[inline(always)] + fn from_byte_slices(bytes: &[&'a [u8]]) -> Self { + Self { + bounds: BC::from_byte_slices(&bytes[..BC::SLICE_COUNT]), + values: VC::from_byte_slices(&bytes[BC::SLICE_COUNT..]), + } + } } impl Len for Strings { diff --git a/src/sums.rs b/src/sums.rs index 991b469..7134f39 100644 --- a/src/sums.rs +++ b/src/sums.rs @@ -54,6 +54,7 @@ pub mod rank_select { } } impl<'a, CC: crate::FromBytes<'a>, VC: crate::FromBytes<'a>> crate::FromBytes<'a> for RankSelect { + const SLICE_COUNT: usize = CC::SLICE_COUNT + >::SLICE_COUNT; #[inline(always)] fn from_bytes(bytes: &mut impl Iterator) -> Self { Self { @@ -61,6 +62,13 @@ pub mod rank_select { values: crate::FromBytes::from_bytes(bytes), } } + #[inline(always)] + fn from_byte_slices(bytes: &[&'a [u8]]) -> Self { + Self { + counts: CC::from_byte_slices(&bytes[..CC::SLICE_COUNT]), + values: >::from_byte_slices(&bytes[CC::SLICE_COUNT..]), + } + } } @@ -255,6 +263,7 @@ pub mod result { } } impl<'a, SC: crate::FromBytes<'a>, TC: crate::FromBytes<'a>, CC: crate::FromBytes<'a>, VC: crate::FromBytes<'a>> crate::FromBytes<'a> for Results { + const SLICE_COUNT: usize = >::SLICE_COUNT + SC::SLICE_COUNT + TC::SLICE_COUNT; #[inline(always)] fn from_bytes(bytes: &mut impl Iterator) -> Self { Self { @@ -263,6 +272,15 @@ pub mod result { errs: crate::FromBytes::from_bytes(bytes), } } + #[inline(always)] + fn from_byte_slices(bytes: &[&'a [u8]]) -> Self { + let ix_count = >::SLICE_COUNT; + Self { + indexes: crate::FromBytes::from_byte_slices(&bytes[..ix_count]), + oks: SC::from_byte_slices(&bytes[ix_count .. ix_count + SC::SLICE_COUNT]), + errs: TC::from_byte_slices(&bytes[ix_count + SC::SLICE_COUNT ..]), + } + } } impl> Len for Results { @@ -501,6 +519,7 @@ pub mod option { } impl <'a, TC: crate::FromBytes<'a>, CC: crate::FromBytes<'a>, VC: crate::FromBytes<'a>> crate::FromBytes<'a> for Options { + const SLICE_COUNT: usize = >::SLICE_COUNT + TC::SLICE_COUNT; #[inline(always)] fn from_bytes(bytes: &mut impl Iterator) -> Self { Self { @@ -508,6 +527,14 @@ pub mod option { somes: crate::FromBytes::from_bytes(bytes), } } + #[inline(always)] + fn from_byte_slices(bytes: &[&'a [u8]]) -> Self { + let ix_count = >::SLICE_COUNT; + Self { + indexes: crate::FromBytes::from_byte_slices(&bytes[..ix_count]), + somes: TC::from_byte_slices(&bytes[ix_count..]), + } + } } impl> Len for Options { diff --git a/src/tuple.rs b/src/tuple.rs index 45b1442..add36e5 100644 --- a/src/tuple.rs +++ b/src/tuple.rs @@ -66,12 +66,23 @@ macro_rules! tuple_impl { } } impl<'a, $($name: crate::FromBytes<'a>),*> crate::FromBytes<'a> for ($($name,)*) { + const SLICE_COUNT: usize = 0 $(+ $name::SLICE_COUNT)*; #[inline(always)] #[allow(non_snake_case)] fn from_bytes(bytes: &mut impl Iterator) -> Self { $(let $name = crate::FromBytes::from_bytes(bytes);)* ($($name,)*) } + #[inline(always)] + #[allow(non_snake_case)] + fn from_byte_slices(bytes: &[&'a [u8]]) -> Self { + let mut _offset = 0; + $( + let $name = $name::from_byte_slices(&bytes[_offset .. _offset + $name::SLICE_COUNT]); + _offset += $name::SLICE_COUNT; + )* + ($($name,)*) + } } impl<$($name: Len),*> Len for ($($name,)*) { diff --git a/src/vector.rs b/src/vector.rs index c663fd8..f38fefb 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -125,6 +125,7 @@ impl<'a, TC: crate::AsBytes<'a>, BC: crate::AsBytes<'a>> crate::AsBytes<'a> for } } impl<'a, TC: crate::FromBytes<'a>, BC: crate::FromBytes<'a>> crate::FromBytes<'a> for Vecs { + const SLICE_COUNT: usize = BC::SLICE_COUNT + TC::SLICE_COUNT; #[inline(always)] fn from_bytes(bytes: &mut impl Iterator) -> Self { Self { @@ -132,6 +133,13 @@ impl<'a, TC: crate::FromBytes<'a>, BC: crate::FromBytes<'a>> crate::FromBytes<'a values: crate::FromBytes::from_bytes(bytes), } } + #[inline(always)] + fn from_byte_slices(bytes: &[&'a [u8]]) -> Self { + Self { + bounds: BC::from_byte_slices(&bytes[..BC::SLICE_COUNT]), + values: TC::from_byte_slices(&bytes[BC::SLICE_COUNT..]), + } + } } impl Vecs {