Skip to content
Snippets Groups Projects
Commit 5dfd3029 authored by Éloïs's avatar Éloïs
Browse files

[feat] kv_typed: add zerocopy methods get_ref() and get ref_slice()

parent ba31840c
Branches
Tags
No related merge requests found
Showing with 682 additions and 14 deletions
......@@ -1141,6 +1141,7 @@ dependencies = [
"smallvec",
"thiserror",
"unwrap",
"zerocopy",
]
[[package]]
......@@ -2444,6 +2445,27 @@ dependencies = [
"version_check",
]
[[package]]
name = "zerocopy"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6580539ad917b7c026220c4b3f2c08d52ce54d6ce0dc491e66002e35388fab46"
dependencies = [
"byteorder",
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d498dbd1fd7beb83c86709ae1c33ca50942889473473d287d56ce4770a18edfb"
dependencies = [
"proc-macro2",
"syn",
"synstructure",
]
[[package]]
name = "zeroize"
version = "1.1.1"
......
......@@ -24,6 +24,7 @@ serde_json = { version = "1.0.53", optional = true }
sled = { version = "0.34.4", optional = true }
smallvec = { version = "1.4.0", features = ["serde"] }
thiserror = "1.0.20"
zerocopy = "0.3.0"
[[bench]]
name = "compare_backends"
......@@ -40,7 +41,7 @@ unwrap = "1.2.1"
criterion = { version = "0.3.1" }
[features]
#default = ["memory_backend"]
default = ["memory_backend"]
async = ["async-channel"]
explorer = ["rayon", "regex", "serde_json"]
......@@ -52,6 +53,6 @@ sync = ["crossbeam-channel"]
mock = ["mockall"]
default = ["memory_backend", "subscription", "sync"]
#default = ["memory_backend", "subscription", "sync"]
#default = ["memory_backend", "subscription", "sync", "explorer"]
#default = ["memory_backend", "subscription", "sync", "mock"]
......@@ -18,6 +18,51 @@ impl ValueAsBytes for String {
}
}
impl<T> ValueAsBytes for Vec<T>
where
T: zerocopy::AsBytes,
{
fn as_bytes<D, F: FnMut(&[u8]) -> Result<D, KvError>>(&self, mut f: F) -> Result<D, KvError> {
use zerocopy::AsBytes as _;
f((&self[..]).as_bytes())
}
}
macro_rules! impl_as_bytes_for_smallvec {
($($N:literal),*) => {$(
impl<T> ValueAsBytes for SmallVec<[T; $N]>
where
T: zerocopy::AsBytes,
{
fn as_bytes<D, F: FnMut(&[u8]) -> Result<D, KvError>>(&self, mut f: F) -> Result<D, KvError> {
use zerocopy::AsBytes as _;
f((&self[..]).as_bytes())
}
}
)*};
}
impl_as_bytes_for_smallvec!(1, 2, 4, 8, 16, 32, 64);
impl<T> ValueAsBytes for BTreeSet<T>
where
T: zerocopy::AsBytes + Copy,
{
fn as_bytes<D, F: FnMut(&[u8]) -> Result<D, KvError>>(&self, mut f: F) -> Result<D, KvError> {
use zerocopy::AsBytes as _;
f((&SmallVec::<[T; 32]>::from_iter(self.iter().copied())[..]).as_bytes())
}
}
impl<T> ValueAsBytes for HashSet<T>
where
T: zerocopy::AsBytes + Copy,
{
fn as_bytes<D, F: FnMut(&[u8]) -> Result<D, KvError>>(&self, mut f: F) -> Result<D, KvError> {
use zerocopy::AsBytes as _;
f((&SmallVec::<[T; 32]>::from_iter(self.iter().copied())[..]).as_bytes())
}
}
macro_rules! impl_as_bytes_for_numbers {
($($T:ty),*) => {$(
impl KeyAsBytes for $T {
......@@ -33,4 +78,6 @@ macro_rules! impl_as_bytes_for_numbers {
)*};
}
impl_as_bytes_for_numbers!(usize, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, f32, f64);
impl_as_bytes_for_numbers!(
usize, u8, u16, u32, u64, u128, isize, i8, i16, i32, i64, i128, f32, f64
);
......@@ -59,6 +59,16 @@ pub trait BackendCol: 'static + Clone + Send + Sync {
+ ReversableIterator;
fn get<K: Key, V: Value>(&self, k: &K) -> KvResult<Option<V>>;
fn get_ref<K: Key, V: ValueZc, D, F: Fn(&V::Ref) -> KvResult<D>>(
&self,
k: &K,
f: F,
) -> KvResult<Option<D>>;
fn get_ref_slice<K: Key, V: ValueSliceZc, D, F: Fn(&[V::Elem]) -> KvResult<D>>(
&self,
k: &K,
f: F,
) -> KvResult<Option<D>>;
fn clear(&self) -> KvResult<()>;
fn count(&self) -> KvResult<usize>;
fn iter<K: Key, V: Value>(&self, range: RangeBytes) -> Self::Iter;
......
......@@ -111,6 +111,54 @@ impl BackendCol for LevelDbCol {
})
}
#[inline(always)]
fn get_ref<K: Key, V: ValueZc, D, F: Fn(&V::Ref) -> KvResult<D>>(
&self,
k: &K,
f: F,
) -> KvResult<Option<D>> {
k.as_bytes(|k_bytes| {
self.0
.get(ReadOptions::new(), k_bytes)?
.map(|bytes| {
if let Some(layout_verified) =
zerocopy::LayoutVerified::<_, V::Ref>::new(bytes.as_ref())
{
f(&layout_verified)
} else {
Err(KvError::DeserError(
"Bytes are invalid length or alignment.".to_owned(),
))
}
})
.transpose()
})
}
#[inline(always)]
fn get_ref_slice<K: Key, V: ValueSliceZc, D, F: Fn(&[V::Elem]) -> KvResult<D>>(
&self,
k: &K,
f: F,
) -> KvResult<Option<D>> {
k.as_bytes(|k_bytes| {
self.0
.get(ReadOptions::new(), k_bytes)?
.map(|bytes| {
if let Some(layout_verified) =
zerocopy::LayoutVerified::<_, [V::Elem]>::new_slice(
&bytes[V::prefix_len()..],
)
{
f(&layout_verified)
} else {
Err(KvError::DeserError(
"Bytes are invalid length or alignment.".to_owned(),
))
}
})
.transpose()
})
}
#[inline(always)]
fn delete<K: Key>(&self, k: &K) -> KvResult<()> {
k.as_bytes(|k_bytes| self.0.delete(WriteOptions::new(), k_bytes))?;
Ok(())
......
......@@ -100,6 +100,56 @@ impl BackendCol for MemCol {
})
}
#[inline(always)]
fn get_ref<K: Key, V: ValueZc, D, F: Fn(&V::Ref) -> KvResult<D>>(
&self,
k: &K,
f: F,
) -> KvResult<Option<D>> {
k.as_bytes(|k_bytes| {
let reader = self.0.read();
reader
.get(k_bytes)
.map(|bytes| {
if let Some(layout_verified) =
zerocopy::LayoutVerified::<_, V::Ref>::new(bytes.as_ref())
{
f(&layout_verified)
} else {
Err(KvError::DeserError(
"Bytes are invalid length or alignment.".to_owned(),
))
}
})
.transpose()
})
}
#[inline(always)]
fn get_ref_slice<K: Key, V: ValueSliceZc, D, F: Fn(&[V::Elem]) -> KvResult<D>>(
&self,
k: &K,
f: F,
) -> KvResult<Option<D>> {
k.as_bytes(|k_bytes| {
let reader = self.0.read();
reader
.get(k_bytes)
.map(|bytes| {
if let Some(layout_verified) =
zerocopy::LayoutVerified::<_, [V::Elem]>::new_slice(
&bytes[V::prefix_len()..],
)
{
f(&layout_verified)
} else {
Err(KvError::DeserError(
"Bytes are invalid length or alignment.".to_owned(),
))
}
})
.transpose()
})
}
#[inline(always)]
fn delete<K: Key>(&self, k: &K) -> KvResult<()> {
k.as_bytes(|k_bytes| {
let mut writer = self.0.write();
......
......@@ -89,6 +89,54 @@ impl BackendCol for SledCol {
})
}
#[inline(always)]
fn get_ref<K: Key, V: ValueZc, D, F: Fn(&V::Ref) -> KvResult<D>>(
&self,
k: &K,
f: F,
) -> KvResult<Option<D>> {
k.as_bytes(|k_bytes| {
self.0
.get(k_bytes)?
.map(|bytes| {
if let Some(layout_verified) =
zerocopy::LayoutVerified::<_, V::Ref>::new(bytes.as_ref())
{
f(&layout_verified)
} else {
Err(KvError::DeserError(
"Bytes are invalid length or alignment.".to_owned(),
))
}
})
.transpose()
})
}
#[inline(always)]
fn get_ref_slice<K: Key, V: ValueSliceZc, D, F: Fn(&[V::Elem]) -> KvResult<D>>(
&self,
k: &K,
f: F,
) -> KvResult<Option<D>> {
k.as_bytes(|k_bytes| {
self.0
.get(k_bytes)?
.map(|bytes| {
if let Some(layout_verified) =
zerocopy::LayoutVerified::<_, [V::Elem]>::new_slice(
&bytes[V::prefix_len()..],
)
{
f(&layout_verified)
} else {
Err(KvError::DeserError(
"Bytes are invalid length or alignment.".to_owned(),
))
}
})
.transpose()
})
}
#[inline(always)]
fn delete<K: Key>(&self, k: &K) -> KvResult<()> {
k.as_bytes(|k_bytes| self.0.remove(k_bytes))?;
Ok(())
......
......@@ -83,3 +83,37 @@ impl<BC: BackendCol, E: EventTrait> DbCollectionRo for ColRo<BC, E> {
.map_err(|_| KvError::FailToSubscribe)
}
}
pub trait DbCollectionRoGetRef<V: ValueZc>: DbCollectionRo<V = V> {
fn get_ref<D, F: Fn(&V::Ref) -> KvResult<D>>(
&self,
k: &<Self as DbCollectionRo>::K,
f: F,
) -> KvResult<Option<D>>;
}
impl<V: ValueZc, BC: BackendCol, E: EventTrait<V = V>> DbCollectionRoGetRef<V> for ColRo<BC, E> {
fn get_ref<D, F: Fn(&V::Ref) -> KvResult<D>>(&self, k: &E::K, f: F) -> KvResult<Option<D>> {
self.inner.get_ref::<E::K, V, D, F>(k, f)
}
}
pub trait DbCollectionRoGetRefSlice<V: ValueSliceZc>: DbCollectionRo<V = V> {
fn get_ref_slice<D, F: Fn(&[V::Elem]) -> KvResult<D>>(
&self,
k: &<Self as DbCollectionRo>::K,
f: F,
) -> KvResult<Option<D>>;
}
impl<V: ValueSliceZc, BC: BackendCol, E: EventTrait<V = V>> DbCollectionRoGetRefSlice<V>
for ColRo<BC, E>
{
fn get_ref_slice<D, F: Fn(&[V::Elem]) -> KvResult<D>>(
&self,
k: &E::K,
f: F,
) -> KvResult<Option<D>> {
self.inner.get_ref_slice::<E::K, V, D, F>(k, f)
}
}
......@@ -75,7 +75,7 @@ macro_rules! impl_explorable_value_for_numbers {
}
impl_explorable_value_for_numbers!(
usize, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, f32, f64
usize, u8, u16, u32, u64, u128, isize, i8, i16, i32, i64, i128, f32, f64
);
#[derive(Debug)]
......
......@@ -19,4 +19,76 @@ macro_rules! impl_from_bytes_for_numbers {
)*};
}
impl_from_bytes_for_numbers!(usize, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, f32, f64);
impl_from_bytes_for_numbers!(
usize, u8, u16, u32, u64, u128, isize, i8, i16, i32, i64, i128, f32, f64
);
impl FromBytes for String {
type Err = std::str::Utf8Error;
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Err> {
Ok(std::str::from_utf8(bytes)?.to_owned())
}
}
macro_rules! impl_from_bytes_for_smallvec {
($($N:literal),*) => {$(
impl<T> FromBytes for SmallVec<[T; $N]>
where
T: Copy + zerocopy::FromBytes,
{
type Err = StringErr;
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Err> {
let layout_verified = zerocopy::LayoutVerified::<_, [T]>::new_slice(bytes)
.ok_or_else(|| StringErr("".to_owned()))?;
Ok(SmallVec::from_slice(layout_verified.into_slice()))
}
}
)*};
}
impl_from_bytes_for_smallvec!(1, 2, 4, 8, 16, 32, 64);
impl<T> FromBytes for Vec<T>
where
T: Copy + zerocopy::FromBytes,
{
type Err = StringErr;
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Err> {
let layout_verified = zerocopy::LayoutVerified::<_, [T]>::new_slice(bytes)
.ok_or_else(|| StringErr("".to_owned()))?;
let slice = layout_verified.into_slice();
let mut vec = Vec::with_capacity(slice.len());
vec.copy_from_slice(slice);
Ok(vec)
}
}
impl<T> FromBytes for BTreeSet<T>
where
T: Copy + zerocopy::FromBytes + Ord,
{
type Err = StringErr;
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Err> {
let layout_verified = zerocopy::LayoutVerified::<_, [T]>::new_slice(bytes)
.ok_or_else(|| StringErr("".to_owned()))?;
let slice = layout_verified.into_slice();
Ok(BTreeSet::from_iter(slice.iter().copied()))
}
}
impl<T> FromBytes for HashSet<T>
where
T: Copy + Eq + zerocopy::FromBytes + std::hash::Hash,
{
type Err = StringErr;
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Err> {
let layout_verified = zerocopy::LayoutVerified::<_, [T]>::new_slice(bytes)
.ok_or_else(|| StringErr("".to_owned()))?;
let slice = layout_verified.into_slice();
Ok(HashSet::from_iter(slice.iter().copied()))
}
}
......@@ -50,6 +50,7 @@ pub use async_channel as channel;
pub use crossbeam_channel as channel;
#[cfg(feature = "explorer")]
pub use regex;
pub use zerocopy;
/// Kv Typed prelude
pub mod prelude {
......@@ -66,7 +67,9 @@ pub mod prelude {
pub use crate::batch::Batch;
#[cfg(feature = "mock")]
pub use crate::collection_ro::MockColRo;
pub use crate::collection_ro::{ColRo, DbCollectionRo};
pub use crate::collection_ro::{
ColRo, DbCollectionRo, DbCollectionRoGetRef, DbCollectionRoGetRefSlice,
};
pub use crate::collection_rw::{ColRw, DbCollectionRw};
pub use crate::error::{
DynErr, KvError, KvResult, StringErr, TransactionError, TransactionResult,
......@@ -81,7 +84,7 @@ pub mod prelude {
pub use crate::key::Key;
#[cfg(feature = "subscription")]
pub use crate::subscription::{NewSubscribers, Subscriber, Subscribers};
pub use crate::value::Value;
pub use crate::value::{Value, ValueSliceZc, ValueZc};
pub use kv_typed_code_gen::db_schema;
}
......@@ -102,10 +105,276 @@ use async_channel::{unbounded, Receiver, Sender, TrySendError};
use crossbeam_channel::{unbounded, Receiver, Sender, TrySendError};
pub(crate) use smallvec::SmallVec;
pub(crate) use std::{
collections::{BTreeSet, HashSet},
convert::TryInto,
error::Error,
fmt::Debug,
iter::FromIterator,
marker::PhantomData,
ops::{Bound, RangeBounds},
};
pub(crate) use thiserror::Error;
#[macro_export]
/// $Elem must implement Display + FromStr + zerocopy::AsBytes + zerocopy::FromBytes
macro_rules! impl_value_for_vec_zc {
($T:ty, $Elem:ty) => {
impl ValueAsBytes for $T {
fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, f: F) -> KvResult<T> {
self.0.as_bytes(f)
}
}
impl FromBytes for $T {
type Err = StringErr;
fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
Ok(Self(Vec::<$Elem>::from_bytes(bytes)?))
}
}
impl ValueSliceZc for $T {
type Elem = $Elem;
fn prefix_len() -> usize {
0
}
}
#[cfg(feature = "explorer")]
use std::str::FromStr as _;
#[cfg(feature = "explorer")]
impl ExplorableValue for $T {
fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
if let serde_json::Value::Array(json_array) =
serde_json::Value::from_str(source).map_err(|e| StringErr(format!("{}", e)))?
{
let mut vec = Vec::with_capacity(json_array.len());
for value in json_array {
if let serde_json::Value::String(string) = value {
vec.push(
<$Elem>::from_str(&string)
.map_err(|e| StringErr(format!("{}", e)))?,
);
} else {
return Err(StringErr(format!(
"Expected array of {}.",
stringify!($Elem)
)));
}
}
Ok(Self(vec))
} else {
Err(StringErr(format!(
"Expected array of {}.",
stringify!($Elem)
)))
}
}
fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
Ok(serde_json::Value::Array(
self.0
.iter()
.map(|elem| serde_json::Value::String(format!("{}", elem)))
.collect(),
))
}
}
};
}
#[macro_export]
/// $Elem must implement Display + FromStr + zerocopy::AsBytes + zerocopy::FromBytes
macro_rules! impl_value_for_smallvec_zc {
($T:ty, $Elem:ty, $N:literal) => {
impl ValueAsBytes for $T {
fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, f: F) -> KvResult<T> {
self.0.as_bytes(f)
}
}
impl FromBytes for $T {
type Err = StringErr;
fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
Ok(Self(SmallVec::<[$Elem; $N]>::from_bytes(bytes)?))
}
}
impl ValueSliceZc for $T {
type Elem = $Elem;
fn prefix_len() -> usize {
0
}
}
#[cfg(feature = "explorer")]
use std::str::FromStr as _;
#[cfg(feature = "explorer")]
impl ExplorableValue for $T {
fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
if let serde_json::Value::Array(json_array) =
serde_json::Value::from_str(source).map_err(|e| StringErr(format!("{}", e)))?
{
let mut svec = SmallVec::with_capacity(json_array.len());
for value in json_array {
if let serde_json::Value::String(string) = value {
svec.push(
<$Elem>::from_str(&string)
.map_err(|e| StringErr(format!("{}", e)))?,
);
} else {
return Err(StringErr(format!(
"Expected array of {}.",
stringify!($Elem)
)));
}
}
Ok(Self(svec))
} else {
Err(StringErr(format!(
"Expected array of {}.",
stringify!($Elem)
)))
}
}
fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
Ok(serde_json::Value::Array(
self.0
.iter()
.map(|elem| serde_json::Value::String(format!("{}", elem)))
.collect(),
))
}
}
};
}
#[macro_export]
/// $Elem must implement Display + FromStr + Ord + zerocopy::AsBytes + zerocopy::FromBytes
macro_rules! impl_value_for_btreeset_zc {
($T:ty, $Elem:ty) => {
impl ValueAsBytes for $T {
fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, f: F) -> KvResult<T> {
self.0.as_bytes(f)
}
}
impl FromBytes for $T {
type Err = StringErr;
fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
Ok(Self(BTreeSet::<$Elem>::from_bytes(bytes)?))
}
}
impl ValueSliceZc for $T {
type Elem = $Elem;
fn prefix_len() -> usize {
0
}
}
#[cfg(feature = "explorer")]
use std::str::FromStr as _;
#[cfg(feature = "explorer")]
impl ExplorableValue for $T {
fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
if let serde_json::Value::Array(json_array) =
serde_json::Value::from_str(source).map_err(|e| StringErr(format!("{}", e)))?
{
let mut col = BTreeSet::new();
for value in json_array {
if let serde_json::Value::String(string) = value {
col.insert(
<$Elem>::from_str(&string)
.map_err(|e| StringErr(format!("{}", e)))?,
);
} else {
return Err(StringErr(format!(
"Expected array of {}.",
stringify!($Elem)
)));
}
}
Ok(Self(col))
} else {
Err(StringErr(format!(
"Expected array of {}.",
stringify!($Elem)
)))
}
}
fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
Ok(serde_json::Value::Array(
self.0
.iter()
.map(|elem| serde_json::Value::String(format!("{}", elem)))
.collect(),
))
}
}
};
}
#[macro_export]
/// $Elem must implement Display + Eq + FromStr + std::hash::Hash + zerocopy::AsBytes + zerocopy::FromBytes
macro_rules! impl_value_for_hashset_zc {
($T:ty, $Elem:ty) => {
impl ValueAsBytes for $T {
fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, f: F) -> KvResult<T> {
self.0.as_bytes(f)
}
}
impl FromBytes for $T {
type Err = StringErr;
fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
Ok(Self(HashSet::<$Elem>::from_bytes(bytes)?))
}
}
impl ValueSliceZc for $T {
type Elem = $Elem;
fn prefix_len() -> usize {
0
}
}
#[cfg(feature = "explorer")]
use std::str::FromStr as _;
#[cfg(feature = "explorer")]
impl ExplorableValue for $T {
fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
if let serde_json::Value::Array(json_array) =
serde_json::Value::from_str(source).map_err(|e| StringErr(format!("{}", e)))?
{
let mut col = HashSet::new();
for value in json_array {
if let serde_json::Value::String(string) = value {
col.insert(
<$Elem>::from_str(&string)
.map_err(|e| StringErr(format!("{}", e)))?,
);
} else {
return Err(StringErr(format!(
"Expected array of {}.",
stringify!($Elem)
)));
}
}
Ok(Self(col))
} else {
Err(StringErr(format!(
"Expected array of {}.",
stringify!($Elem)
)))
}
}
fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
Ok(serde_json::Value::Array(
self.0
.iter()
.map(|elem| serde_json::Value::String(format!("{}", elem)))
.collect(),
))
}
}
};
}
......@@ -50,10 +50,34 @@ impl<T> Value for T where
{
}
impl FromBytes for String {
type Err = std::str::Utf8Error;
pub trait ValueZc: Value {
type Ref: Sized + zerocopy::AsBytes + zerocopy::FromBytes;
}
macro_rules! impl_value_zc_for_numbers {
($($T:ty),*) => {$(
impl ValueZc for $T {
type Ref = Self;
}
)*};
}
impl_value_zc_for_numbers!(
usize, u8, u16, u32, u64, u128, isize, i8, i16, i32, i64, i128, f32, f64
);
pub trait ValueSliceZc: Value {
type Elem: Sized + zerocopy::AsBytes + zerocopy::FromBytes;
fn prefix_len() -> usize {
8
}
}
impl ValueSliceZc for String {
type Elem = u8;
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Err> {
Ok(std::str::from_utf8(bytes)?.to_owned())
fn prefix_len() -> usize {
0
}
}
#[cfg(feature = "memory_backend")]
mod tests {
use kv_typed::prelude::*;
use smallvec::SmallVec;
use std::fmt::Debug;
#[derive(Debug, PartialEq)]
pub struct VecU128(Vec<u128>);
kv_typed::impl_value_for_vec_zc!(VecU128, u128);
#[derive(Debug, PartialEq)]
pub struct SVecU128(SmallVec<[u128; 4]>);
kv_typed::impl_value_for_smallvec_zc!(SVecU128, u128, 4);
use std::collections::BTreeSet;
#[derive(Debug, PartialEq)]
pub struct BTSetU128(BTreeSet<u128>);
kv_typed::impl_value_for_btreeset_zc!(BTSetU128, u128);
use std::collections::HashSet;
#[derive(Debug, PartialEq)]
pub struct HashSetU128(HashSet<u128>);
kv_typed::impl_value_for_hashset_zc!(HashSetU128, u128);
db_schema!(
TestV1,
[
["c1", col_1, i32, String],
["c2", col_2, usize, i128],
["c2", col_3, u64, u128],
["c1", col_1, i32, String,],
["c2", col_2, usize, i128,],
["c3", col_3, u64, VecU128],
["c4", col_4, u64, BTSetU128],
]
);
......@@ -41,6 +61,13 @@ mod tests {
}
assert_eq!(db.col_1().get(&3)?, Some("toto".to_owned()),);
let d = db.col_1().get_ref_slice(&3, |bytes| {
let str_ = unsafe { core::str::from_utf8_unchecked(bytes) };
assert_eq!("toto", str_);
assert_eq!(db.col_2().get(&3)?, None,);
Ok(str_.to_owned())
})?;
assert_eq!(d, Some("toto".to_owned()));
assert_eq!(db.col_2().get(&3)?, None,);
......@@ -68,6 +95,22 @@ mod tests {
assert_eq!(iter.next_res()?, Some("toto".to_owned()));
assert_eq!(iter.next_res()?, None);
db.col_3_write().upsert(4, VecU128(vec![1, 2, 3]))?;
db.col_3().get_ref_slice(&4, |numbers| {
assert_eq!(numbers, &[1, 2, 3]);
Ok(())
})?;
use std::iter::FromIterator as _;
db.col_4_write().upsert(
4,
BTSetU128(BTreeSet::from_iter((&[3, 2, 4, 1]).iter().copied())),
)?;
db.col_4().get_ref_slice(&4, |numbers| {
assert_eq!(numbers, &[1, 2, 3, 4]);
Ok(())
})?;
Ok(())
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment