diff --git a/heed-traits/src/lib.rs b/heed-traits/src/lib.rs index 88fb0efe..adf0c14a 100644 --- a/heed-traits/src/lib.rs +++ b/heed-traits/src/lib.rs @@ -9,9 +9,9 @@ #![warn(missing_docs)] -use std::borrow::Cow; use std::cmp::{Ord, Ordering}; use std::error::Error as StdError; +use std::io; /// A boxed `Send + Sync + 'static` error. pub type BoxedError = Box; @@ -21,8 +21,48 @@ pub trait BytesEncode<'a> { /// The type to encode. type EItem: ?Sized + 'a; + /// The type containing the encoded bytes. + type ReturnBytes: Into> + AsRef<[u8]> + 'a; + + /// The error type to return when decoding goes wrong. + type Error: StdError + Send + Sync + 'static; + + /// This function can be used to hint callers of the + /// [`bytes_encode`][BytesEncode::bytes_encode] function to use + /// [`bytes_encode_into_writer`][BytesEncode::bytes_encode_into_writer] instead, if the latter + /// runs faster (for example if it needs less heap allocations). + /// + /// The default implementation returns `true` because the default implementation of + /// [`bytes_encode_into_writer`][BytesEncode::bytes_encode_into_writer] is to forward to + /// [`bytes_encode`][BytesEncode::bytes_encode]. + fn zero_copy(item: &Self::EItem) -> bool { + // This is preferred to renaming the function parameter (to _item) because IDEs can + // autofill trait implementations, which will default the paramter name to _item then and + // this could probably also mess with clippy's renamed_function_params lint. + let _ = item; + + true + } + /// Encode the given item as bytes. - fn bytes_encode(item: &'a Self::EItem) -> Result, BoxedError>; + fn bytes_encode(item: &'a Self::EItem) -> Result; + + /// Encode the given item as bytes and write it into the writer. + /// + /// When implementing this, also take a look at [`zero_copy`][BytesEncode::zero_copy]'s + /// documentation. + /// + /// The default implementation forwards to [`bytes_encode`][BytesEncode::bytes_encode]. + fn bytes_encode_into_writer( + item: &'a Self::EItem, + mut writer: W, + ) -> Result<(), BoxedError> { + let bytes = Self::bytes_encode(item)?; + + writer.write_all(bytes.as_ref())?; + + Ok(()) + } } /// A trait that represents a decoding structure. diff --git a/heed-types/src/bytes.rs b/heed-types/src/bytes.rs index ae602ba0..934357bd 100644 --- a/heed-types/src/bytes.rs +++ b/heed-types/src/bytes.rs @@ -1,4 +1,4 @@ -use std::borrow::Cow; +use std::convert::Infallible; use heed_traits::{BoxedError, BytesDecode, BytesEncode}; @@ -11,8 +11,12 @@ pub enum Bytes {} impl<'a> BytesEncode<'a> for Bytes { type EItem = [u8]; - fn bytes_encode(item: &'a Self::EItem) -> Result, BoxedError> { - Ok(Cow::Borrowed(item)) + type ReturnBytes = &'a [u8]; + + type Error = Infallible; + + fn bytes_encode(item: &'a Self::EItem) -> Result { + Ok(item) } } @@ -23,3 +27,26 @@ impl<'a> BytesDecode<'a> for Bytes { Ok(bytes) } } + +/// Like [`Bytes`], but always contains exactly `N` (the generic parameter) bytes. +pub enum FixedSizeBytes {} + +impl<'a, const N: usize> BytesEncode<'a> for FixedSizeBytes { + type EItem = [u8; N]; + + type ReturnBytes = [u8; N]; // TODO &'a [u8; N] or [u8; N] + + type Error = Infallible; + + fn bytes_encode(item: &'a Self::EItem) -> Result { + Ok(*item) + } +} + +impl<'a, const N: usize> BytesDecode<'a> for FixedSizeBytes { + type DItem = [u8; N]; // TODO &'a [u8; N] or [u8; N] + + fn bytes_decode(bytes: &'a [u8]) -> Result { + bytes.try_into().map_err(Into::into) + } +} diff --git a/heed-types/src/integer.rs b/heed-types/src/integer.rs index 71435103..fd9a67de 100644 --- a/heed-types/src/integer.rs +++ b/heed-types/src/integer.rs @@ -1,4 +1,4 @@ -use std::borrow::Cow; +use std::convert::Infallible; use std::marker::PhantomData; use std::mem::size_of; @@ -11,8 +11,12 @@ pub struct U8; impl BytesEncode<'_> for U8 { type EItem = u8; - fn bytes_encode(item: &Self::EItem) -> Result, BoxedError> { - Ok(Cow::from([*item].to_vec())) + type ReturnBytes = [u8; 1]; + + type Error = Infallible; + + fn bytes_encode(item: &Self::EItem) -> Result { + Ok([*item]) } } @@ -30,8 +34,12 @@ pub struct I8; impl BytesEncode<'_> for I8 { type EItem = i8; - fn bytes_encode(item: &Self::EItem) -> Result, BoxedError> { - Ok(Cow::from([*item as u8].to_vec())) + type ReturnBytes = [u8; 1]; + + type Error = Infallible; + + fn bytes_encode(item: &Self::EItem) -> Result { + Ok([*item as u8]) } } @@ -54,10 +62,14 @@ macro_rules! define_type { impl BytesEncode<'_> for $name { type EItem = $native; - fn bytes_encode(item: &Self::EItem) -> Result, BoxedError> { - let mut buf = vec![0; size_of::()]; + type ReturnBytes = [u8; size_of::<$native>()]; + + type Error = Infallible; + + fn bytes_encode(item: &Self::EItem) -> Result { + let mut buf = [0; size_of::<$native>()]; O::$write_method(&mut buf, *item); - Ok(Cow::from(buf)) + Ok(buf) } } diff --git a/heed-types/src/serde_bincode.rs b/heed-types/src/serde_bincode.rs index 37fefe01..d65ce05f 100644 --- a/heed-types/src/serde_bincode.rs +++ b/heed-types/src/serde_bincode.rs @@ -1,5 +1,3 @@ -use std::borrow::Cow; - use heed_traits::{BoxedError, BytesDecode, BytesEncode}; use serde::{Deserialize, Serialize}; @@ -14,8 +12,24 @@ where { type EItem = T; - fn bytes_encode(item: &'a Self::EItem) -> Result, BoxedError> { - bincode::serialize(item).map(Cow::Owned).map_err(Into::into) + type ReturnBytes = Vec; + + type Error = bincode::Error; + + fn zero_copy(_item: &Self::EItem) -> bool { + false + } + + fn bytes_encode(item: &Self::EItem) -> Result { + bincode::serialize(item) + } + + fn bytes_encode_into_writer( + item: &'a Self::EItem, + writer: W, + ) -> Result<(), BoxedError> { + bincode::serialize_into(writer, item)?; + Ok(()) } } diff --git a/heed-types/src/serde_json.rs b/heed-types/src/serde_json.rs index f1f1c3be..80624ff2 100644 --- a/heed-types/src/serde_json.rs +++ b/heed-types/src/serde_json.rs @@ -1,5 +1,3 @@ -use std::borrow::Cow; - use heed_traits::{BoxedError, BytesDecode, BytesEncode}; use serde::{Deserialize, Serialize}; @@ -14,8 +12,24 @@ where { type EItem = T; - fn bytes_encode(item: &Self::EItem) -> Result, BoxedError> { - serde_json::to_vec(item).map(Cow::Owned).map_err(Into::into) + type ReturnBytes = Vec; + + type Error = serde_json::Error; + + fn zero_copy(_item: &Self::EItem) -> bool { + false + } + + fn bytes_encode(item: &Self::EItem) -> Result { + serde_json::to_vec(item) + } + + fn bytes_encode_into_writer( + item: &'a Self::EItem, + writer: W, + ) -> Result<(), BoxedError> { + serde_json::to_writer(writer, item)?; + Ok(()) } } diff --git a/heed-types/src/serde_rmp.rs b/heed-types/src/serde_rmp.rs index f33c7467..00127131 100644 --- a/heed-types/src/serde_rmp.rs +++ b/heed-types/src/serde_rmp.rs @@ -1,5 +1,3 @@ -use std::borrow::Cow; - use heed_traits::{BoxedError, BytesDecode, BytesEncode}; use serde::{Deserialize, Serialize}; @@ -14,8 +12,24 @@ where { type EItem = T; - fn bytes_encode(item: &Self::EItem) -> Result, BoxedError> { - rmp_serde::to_vec(item).map(Cow::Owned).map_err(Into::into) + type ReturnBytes = Vec; + + type Error = rmp_serde::encode::Error; + + fn zero_copy(_item: &Self::EItem) -> bool { + false + } + + fn bytes_encode(item: &Self::EItem) -> Result { + rmp_serde::to_vec(item) + } + + fn bytes_encode_into_writer( + item: &'a Self::EItem, + mut writer: W, + ) -> Result<(), BoxedError> { + rmp_serde::encode::write(&mut writer, item)?; + Ok(()) } } diff --git a/heed-types/src/str.rs b/heed-types/src/str.rs index 220306d9..454ec6fd 100644 --- a/heed-types/src/str.rs +++ b/heed-types/src/str.rs @@ -1,16 +1,19 @@ -use std::borrow::Cow; -use std::str; +use std::convert::Infallible; use heed_traits::{BoxedError, BytesDecode, BytesEncode}; -/// Describes a [`prim@str`]. +/// Describes a [`str`]. pub enum Str {} -impl BytesEncode<'_> for Str { +impl<'a> BytesEncode<'a> for Str { type EItem = str; - fn bytes_encode(item: &Self::EItem) -> Result, BoxedError> { - Ok(Cow::Borrowed(item.as_bytes())) + type ReturnBytes = &'a [u8]; + + type Error = Infallible; + + fn bytes_encode(item: &'a Self::EItem) -> Result { + Ok(item.as_bytes()) } } @@ -18,6 +21,6 @@ impl<'a> BytesDecode<'a> for Str { type DItem = &'a str; fn bytes_decode(bytes: &'a [u8]) -> Result { - str::from_utf8(bytes).map_err(Into::into) + std::str::from_utf8(bytes).map_err(Into::into) } } diff --git a/heed-types/src/unit.rs b/heed-types/src/unit.rs index 4fbb9e2c..a644ca4c 100644 --- a/heed-types/src/unit.rs +++ b/heed-types/src/unit.rs @@ -1,4 +1,4 @@ -use std::borrow::Cow; +use std::convert::Infallible; use std::{error, fmt}; use heed_traits::{BoxedError, BytesDecode, BytesEncode}; @@ -9,8 +9,12 @@ pub enum Unit {} impl BytesEncode<'_> for Unit { type EItem = (); - fn bytes_encode(_item: &Self::EItem) -> Result, BoxedError> { - Ok(Cow::Borrowed(&[])) + type ReturnBytes = [u8; 0]; + + type Error = Infallible; + + fn bytes_encode(&(): &Self::EItem) -> Result { + Ok([]) } } diff --git a/heed/src/cookbook.rs b/heed/src/cookbook.rs index f2cfd905..cb3b8e63 100644 --- a/heed/src/cookbook.rs +++ b/heed/src/cookbook.rs @@ -146,7 +146,7 @@ //! to create codecs to encode prefixes when possible instead of using a slice of bytes. //! //! ``` -//! use std::borrow::Cow; +//! use std::convert::Infallible; //! use std::error::Error; //! use std::fs; //! use std::path::Path; @@ -154,7 +154,7 @@ //! use heed::types::*; //! use heed::{BoxedError, BytesDecode, BytesEncode, Database, EnvOpenOptions}; //! -//! #[derive(Debug, PartialEq, Eq)] +//! #[derive(Debug, Clone, Copy, PartialEq, Eq)] //! pub enum Level { //! Debug, //! Warn, @@ -172,18 +172,20 @@ //! impl<'a> BytesEncode<'a> for LogKeyCodec { //! type EItem = LogKey; //! +//! type ReturnBytes = [u8; 5]; +//! +//! type Error = Infallible; +//! //! /// Encodes the u32 timestamp in big endian followed by the log level with a single byte. -//! fn bytes_encode(log: &Self::EItem) -> Result, BoxedError> { -//! let (timestamp_bytes, level_byte) = match log { -//! LogKey { timestamp, level: Level::Debug } => (timestamp.to_be_bytes(), 0), -//! LogKey { timestamp, level: Level::Warn } => (timestamp.to_be_bytes(), 1), -//! LogKey { timestamp, level: Level::Error } => (timestamp.to_be_bytes(), 2), -//! }; +//! fn bytes_encode(log: &Self::EItem) -> Result { +//! let mut output = [0; 5]; //! -//! let mut output = Vec::new(); -//! output.extend_from_slice(×tamp_bytes); -//! output.push(level_byte); -//! Ok(Cow::Owned(output)) +//! let [timestamp @ .., level] = &mut output; +//! +//! *timestamp = log.timestamp.to_be_bytes(); +//! *level = log.level as u8; +//! +//! Ok(output) //! } //! } //! @@ -218,9 +220,14 @@ //! impl<'a> BytesEncode<'a> for LogAtHalfTimestampCodec { //! type EItem = u32; //! +//! type ReturnBytes = [u8; 2]; +//! +//! type Error = Infallible; +//! //! /// This method encodes only the prefix of the keys in this particular case, the timestamp. -//! fn bytes_encode(half_timestamp: &Self::EItem) -> Result, BoxedError> { -//! Ok(Cow::Owned(half_timestamp.to_be_bytes()[..2].to_vec())) +//! fn bytes_encode(half_timestamp: &Self::EItem) -> Result { +//! let [bytes @ .., _, _] = half_timestamp.to_be_bytes(); +//! Ok(bytes) //! } //! } //! diff --git a/heed/src/database.rs b/heed/src/database.rs index 3817103f..a2e303b2 100644 --- a/heed/src/database.rs +++ b/heed/src/database.rs @@ -1,8 +1,7 @@ -use std::borrow::Cow; use std::ops::{Bound, RangeBounds}; use std::{any, fmt, marker, mem, ptr}; -use heed_traits::{Comparator, LexicographicComparator}; +use heed_traits::{BytesEncode, Comparator, LexicographicComparator}; use types::{DecodeIgnore, LazyDecode}; use crate::cursor::MoveOperation; @@ -346,9 +345,9 @@ impl Database { { assert_eq_env_db_txn!(self, txn); - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; - let mut key_val = unsafe { crate::into_val(&key_bytes) }; + let mut key_val = unsafe { crate::into_val(key_bytes.as_ref()) }; let mut data_val = mem::MaybeUninit::uninit(); let result = unsafe { @@ -429,8 +428,8 @@ impl Database { assert_eq_env_db_txn!(self, txn); let mut cursor = RoCursor::new(txn, self.dbi)?; - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - if cursor.move_on_key(&key_bytes)? { + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + if cursor.move_on_key(key_bytes.as_ref())? { Ok(Some(RoIter::new(cursor))) } else { Ok(None) @@ -493,8 +492,8 @@ impl Database { assert_eq_env_db_txn!(self, txn); let mut cursor = RoCursor::new(txn, self.dbi)?; - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - cursor.move_on_key_greater_than_or_equal_to(&key_bytes)?; + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + cursor.move_on_key_greater_than_or_equal_to(key_bytes.as_ref())?; match cursor.move_on_prev(MoveOperation::NoDup) { Ok(Some((key, data))) => match (KC::bytes_decode(key), DC::bytes_decode(data)) { @@ -562,9 +561,10 @@ impl Database { assert_eq_env_db_txn!(self, txn); let mut cursor = RoCursor::new(txn, self.dbi)?; - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - let result = match cursor.move_on_key_greater_than_or_equal_to(&key_bytes) { - Ok(Some((key, data))) if key == &key_bytes[..] => Ok(Some((key, data))), + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + let key_bytes = key_bytes.as_ref(); + let result = match cursor.move_on_key_greater_than_or_equal_to(key_bytes) { + Ok(Some((key, data))) if key == key_bytes => Ok(Some((key, data))), Ok(_) => cursor.move_on_prev(MoveOperation::NoDup), Err(e) => Err(e), }; @@ -635,9 +635,10 @@ impl Database { assert_eq_env_db_txn!(self, txn); let mut cursor = RoCursor::new(txn, self.dbi)?; - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - let entry = match cursor.move_on_key_greater_than_or_equal_to(&key_bytes)? { - Some((key, data)) if key > &key_bytes[..] => Some((key, data)), + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + let key_bytes = key_bytes.as_ref(); + let entry = match cursor.move_on_key_greater_than_or_equal_to(key_bytes)? { + Some((key, data)) if key > key_bytes => Some((key, data)), Some((_key, _data)) => cursor.move_on_next(MoveOperation::NoDup)?, None => None, }; @@ -707,8 +708,8 @@ impl Database { assert_eq_env_db_txn!(self, txn); let mut cursor = RoCursor::new(txn, self.dbi)?; - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - match cursor.move_on_key_greater_than_or_equal_to(&key_bytes) { + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + match cursor.move_on_key_greater_than_or_equal_to(key_bytes.as_ref()) { Ok(Some((key, data))) => match (KC::bytes_decode(key), DC::bytes_decode(data)) { (Ok(key), Ok(data)) => Ok(Some((key, data))), (Err(e), _) | (_, Err(e)) => Err(Error::Decoding(e)), @@ -1230,26 +1231,31 @@ impl Database { { assert_eq_env_db_txn!(self, txn); + // TODO optimize, this might do unnecessary allocations on types that are already 'static let start_bound = match range.start_bound() { Bound::Included(bound) => { - let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?; - Bound::Included(bytes.into_owned()) + let bytes = + KC::bytes_encode(bound).map_err(|err| Error::Encoding(Box::new(err)))?; + Bound::Included(bytes.into()) } Bound::Excluded(bound) => { - let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?; - Bound::Excluded(bytes.into_owned()) + let bytes = + KC::bytes_encode(bound).map_err(|err| Error::Encoding(Box::new(err)))?; + Bound::Excluded(bytes.into()) } Bound::Unbounded => Bound::Unbounded, }; let end_bound = match range.end_bound() { Bound::Included(bound) => { - let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?; - Bound::Included(bytes.into_owned()) + let bytes = + KC::bytes_encode(bound).map_err(|err| Error::Encoding(Box::new(err)))?; + Bound::Included(bytes.into()) } Bound::Excluded(bound) => { - let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?; - Bound::Excluded(bytes.into_owned()) + let bytes = + KC::bytes_encode(bound).map_err(|err| Error::Encoding(Box::new(err)))?; + Bound::Excluded(bytes.into()) } Bound::Unbounded => Bound::Unbounded, }; @@ -1322,26 +1328,31 @@ impl Database { { assert_eq_env_db_txn!(self, txn); + // TODO optimize, this might do unnecessary allocations on types that are already 'static let start_bound = match range.start_bound() { Bound::Included(bound) => { - let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?; - Bound::Included(bytes.into_owned()) + let bytes = + KC::bytes_encode(bound).map_err(|err| Error::Encoding(Box::new(err)))?; + Bound::Included(bytes.into()) } Bound::Excluded(bound) => { - let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?; - Bound::Excluded(bytes.into_owned()) + let bytes = + KC::bytes_encode(bound).map_err(|err| Error::Encoding(Box::new(err)))?; + Bound::Excluded(bytes.into()) } Bound::Unbounded => Bound::Unbounded, }; let end_bound = match range.end_bound() { Bound::Included(bound) => { - let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?; - Bound::Included(bytes.into_owned()) + let bytes = + KC::bytes_encode(bound).map_err(|err| Error::Encoding(Box::new(err)))?; + Bound::Included(bytes.into()) } Bound::Excluded(bound) => { - let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?; - Bound::Excluded(bytes.into_owned()) + let bytes = + KC::bytes_encode(bound).map_err(|err| Error::Encoding(Box::new(err)))?; + Bound::Excluded(bytes.into()) } Bound::Unbounded => Bound::Unbounded, }; @@ -1404,26 +1415,31 @@ impl Database { { assert_eq_env_db_txn!(self, txn); + // TODO optimize, this might do unnecessary allocations on types that are already 'static let start_bound = match range.start_bound() { Bound::Included(bound) => { - let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?; - Bound::Included(bytes.into_owned()) + let bytes = + KC::bytes_encode(bound).map_err(|err| Error::Encoding(Box::new(err)))?; + Bound::Included(bytes.into()) } Bound::Excluded(bound) => { - let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?; - Bound::Excluded(bytes.into_owned()) + let bytes = + KC::bytes_encode(bound).map_err(|err| Error::Encoding(Box::new(err)))?; + Bound::Excluded(bytes.into()) } Bound::Unbounded => Bound::Unbounded, }; let end_bound = match range.end_bound() { Bound::Included(bound) => { - let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?; - Bound::Included(bytes.into_owned()) + let bytes = + KC::bytes_encode(bound).map_err(|err| Error::Encoding(Box::new(err)))?; + Bound::Included(bytes.into()) } Bound::Excluded(bound) => { - let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?; - Bound::Excluded(bytes.into_owned()) + let bytes = + KC::bytes_encode(bound).map_err(|err| Error::Encoding(Box::new(err)))?; + Bound::Excluded(bytes.into()) } Bound::Unbounded => Bound::Unbounded, }; @@ -1496,26 +1512,31 @@ impl Database { { assert_eq_env_db_txn!(self, txn); + // TODO optimize, this might do unnecessary allocations on types that are already 'static let start_bound = match range.start_bound() { Bound::Included(bound) => { - let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?; - Bound::Included(bytes.into_owned()) + let bytes = + KC::bytes_encode(bound).map_err(|err| Error::Encoding(Box::new(err)))?; + Bound::Included(bytes.into()) } Bound::Excluded(bound) => { - let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?; - Bound::Excluded(bytes.into_owned()) + let bytes = + KC::bytes_encode(bound).map_err(|err| Error::Encoding(Box::new(err)))?; + Bound::Excluded(bytes.into()) } Bound::Unbounded => Bound::Unbounded, }; let end_bound = match range.end_bound() { Bound::Included(bound) => { - let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?; - Bound::Included(bytes.into_owned()) + let bytes = + KC::bytes_encode(bound).map_err(|err| Error::Encoding(Box::new(err)))?; + Bound::Included(bytes.into()) } Bound::Excluded(bound) => { - let bytes = KC::bytes_encode(bound).map_err(Error::Encoding)?; - Bound::Excluded(bytes.into_owned()) + let bytes = + KC::bytes_encode(bound).map_err(|err| Error::Encoding(Box::new(err)))?; + Bound::Excluded(bytes.into()) } Bound::Unbounded => Bound::Unbounded, }; @@ -1579,8 +1600,10 @@ impl Database { { assert_eq_env_db_txn!(self, txn); - let prefix_bytes = KC::bytes_encode(prefix).map_err(Error::Encoding)?; - let prefix_bytes = prefix_bytes.into_owned(); + let prefix_bytes = + KC::bytes_encode(prefix).map_err(|err| Error::Encoding(Box::new(err)))?; + // TODO optimize, this might do unnecessary allocations on types that are already 'static + let prefix_bytes = prefix_bytes.into(); RoCursor::new(txn, self.dbi).map(|cursor| RoPrefix::new(cursor, prefix_bytes)) } @@ -1650,8 +1673,10 @@ impl Database { { assert_eq_env_db_txn!(self, txn); - let prefix_bytes = KC::bytes_encode(prefix).map_err(Error::Encoding)?; - let prefix_bytes = prefix_bytes.into_owned(); + let prefix_bytes = + KC::bytes_encode(prefix).map_err(|err| Error::Encoding(Box::new(err)))?; + // TODO optimize, this might do unnecessary allocations on types that are already 'static + let prefix_bytes = prefix_bytes.into(); RwCursor::new(txn, self.dbi).map(|cursor| RwPrefix::new(cursor, prefix_bytes)) } @@ -1711,8 +1736,10 @@ impl Database { { assert_eq_env_db_txn!(self, txn); - let prefix_bytes = KC::bytes_encode(prefix).map_err(Error::Encoding)?; - let prefix_bytes = prefix_bytes.into_owned(); + let prefix_bytes = + KC::bytes_encode(prefix).map_err(|err| Error::Encoding(Box::new(err)))?; + // TODO optimize, this might do unnecessary allocations on types that are already 'static + let prefix_bytes = prefix_bytes.into(); RoCursor::new(txn, self.dbi).map(|cursor| RoRevPrefix::new(cursor, prefix_bytes)) } @@ -1782,8 +1809,10 @@ impl Database { { assert_eq_env_db_txn!(self, txn); - let prefix_bytes = KC::bytes_encode(prefix).map_err(Error::Encoding)?; - let prefix_bytes = prefix_bytes.into_owned(); + let prefix_bytes = + KC::bytes_encode(prefix).map_err(|err| Error::Encoding(Box::new(err)))?; + // TODO optimize, this might do unnecessary allocations on types that are already 'static + let prefix_bytes = prefix_bytes.into(); RwCursor::new(txn, self.dbi).map(|cursor| RwRevPrefix::new(cursor, prefix_bytes)) } @@ -1829,11 +1858,11 @@ impl Database { { assert_eq_env_db_txn!(self, txn); - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - let data_bytes: Cow<[u8]> = DC::bytes_encode(data).map_err(Error::Encoding)?; + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + let data_bytes = DC::bytes_encode(data).map_err(|err| Error::Encoding(Box::new(err)))?; - let mut key_val = unsafe { crate::into_val(&key_bytes) }; - let mut data_val = unsafe { crate::into_val(&data_bytes) }; + let mut key_val = unsafe { crate::into_val(key_bytes.as_ref()) }; + let mut data_val = unsafe { crate::into_val(data_bytes.as_ref()) }; let flags = 0; unsafe { @@ -1892,8 +1921,8 @@ impl Database { { assert_eq_env_db_txn!(self, txn); - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - let mut key_val = unsafe { crate::into_val(&key_bytes) }; + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + let mut key_val = unsafe { crate::into_val(key_bytes.as_ref()) }; let mut reserved = ffi::reserve_size_val(data_size); let flags = ffi::MDB_RESERVE; @@ -1982,11 +2011,11 @@ impl Database { { assert_eq_env_db_txn!(self, txn); - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - let data_bytes: Cow<[u8]> = DC::bytes_encode(data).map_err(Error::Encoding)?; + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + let data_bytes = DC::bytes_encode(data).map_err(|err| Error::Encoding(Box::new(err)))?; - let mut key_val = unsafe { crate::into_val(&key_bytes) }; - let mut data_val = unsafe { crate::into_val(&data_bytes) }; + let mut key_val = unsafe { crate::into_val(key_bytes.as_ref()) }; + let mut data_val = unsafe { crate::into_val(data_bytes.as_ref()) }; let flags = flags.bits(); unsafe { @@ -2089,11 +2118,11 @@ impl Database { { assert_eq_env_db_txn!(self, txn); - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - let data_bytes: Cow<[u8]> = DC::bytes_encode(data).map_err(Error::Encoding)?; + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + let data_bytes = DC::bytes_encode(data).map_err(|err| Error::Encoding(Box::new(err)))?; - let mut key_val = unsafe { crate::into_val(&key_bytes) }; - let mut data_val = unsafe { crate::into_val(&data_bytes) }; + let mut key_val = unsafe { crate::into_val(key_bytes.as_ref()) }; + let mut data_val = unsafe { crate::into_val(data_bytes.as_ref()) }; let flags = (flags | PutFlags::NO_OVERWRITE).bits(); let result = unsafe { @@ -2240,9 +2269,9 @@ impl Database { { assert_eq_env_db_txn!(self, txn); - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; - let mut key_val = unsafe { crate::into_val(&key_bytes) }; + let mut key_val = unsafe { crate::into_val(key_bytes.as_ref()) }; let mut reserved = ffi::reserve_size_val(data_size); let flags = (flags | PutFlags::NO_OVERWRITE).bits() | lmdb_master_sys::MDB_RESERVE; @@ -2320,8 +2349,8 @@ impl Database { { assert_eq_env_db_txn!(self, txn); - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - let mut key_val = unsafe { crate::into_val(&key_bytes) }; + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + let mut key_val = unsafe { crate::into_val(key_bytes.as_ref()) }; let result = unsafe { mdb_result(ffi::mdb_del(txn.txn.txn, self.dbi, &mut key_val, ptr::null_mut())) @@ -2405,10 +2434,10 @@ impl Database { { assert_eq_env_db_txn!(self, txn); - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - let data_bytes: Cow<[u8]> = DC::bytes_encode(data).map_err(Error::Encoding)?; - let mut key_val = unsafe { crate::into_val(&key_bytes) }; - let mut data_val = unsafe { crate::into_val(&data_bytes) }; + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + let data_bytes = DC::bytes_encode(data).map_err(|err| Error::Encoding(Box::new(err)))?; + let mut key_val = unsafe { crate::into_val(key_bytes.as_ref()) }; + let mut data_val = unsafe { crate::into_val(data_bytes.as_ref()) }; let result = unsafe { mdb_result(ffi::mdb_del(txn.txn.txn, self.dbi, &mut key_val, &mut data_val)) }; diff --git a/heed/src/iterator/iter.rs b/heed/src/iterator/iter.rs index 87f67662..a8da8198 100644 --- a/heed/src/iterator/iter.rs +++ b/heed/src/iterator/iter.rs @@ -1,6 +1,6 @@ -use std::borrow::Cow; use std::marker; +use heed_traits::BytesEncode; use types::LazyDecode; use crate::iteration_method::{IterationMethod, MoveBetweenKeys, MoveThroughDuplicateValues}; @@ -272,9 +272,9 @@ impl<'txn, KC, DC, IM> RwIter<'txn, KC, DC, IM> { KC: BytesEncode<'a>, DC: BytesEncode<'a>, { - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - let data_bytes: Cow<[u8]> = DC::bytes_encode(data).map_err(Error::Encoding)?; - self.cursor.put_current(&key_bytes, &data_bytes) + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + let data_bytes = DC::bytes_encode(data).map_err(|err| Error::Encoding(Box::new(err)))?; + self.cursor.put_current(key_bytes.as_ref(), data_bytes.as_ref()) } /// Write a new value to the current entry. The entry is written with the specified flags. @@ -301,8 +301,13 @@ impl<'txn, KC, DC, IM> RwIter<'txn, KC, DC, IM> { KC: BytesEncode<'a>, F: FnOnce(&mut ReservedSpace) -> io::Result<()>, { - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - self.cursor.put_current_reserved_with_flags(flags, &key_bytes, data_size, write_func) + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + self.cursor.put_current_reserved_with_flags( + flags, + key_bytes.as_ref(), + data_size, + write_func, + ) } /// Insert a key-value pair in this database. The entry is written with the specified flags and data codec. @@ -332,9 +337,9 @@ impl<'txn, KC, DC, IM> RwIter<'txn, KC, DC, IM> { KC: BytesEncode<'a>, NDC: BytesEncode<'a>, { - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - let data_bytes: Cow<[u8]> = NDC::bytes_encode(data).map_err(Error::Encoding)?; - self.cursor.put_current_with_flags(flags, &key_bytes, &data_bytes) + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + let data_bytes = NDC::bytes_encode(data).map_err(|err| Error::Encoding(Box::new(err)))?; + self.cursor.put_current_with_flags(flags, key_bytes.as_ref(), data_bytes.as_ref()) } /// Move on the first value of keys, ignoring duplicate values. @@ -621,9 +626,9 @@ impl<'txn, KC, DC, IM> RwRevIter<'txn, KC, DC, IM> { KC: BytesEncode<'a>, DC: BytesEncode<'a>, { - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - let data_bytes: Cow<[u8]> = DC::bytes_encode(data).map_err(Error::Encoding)?; - self.cursor.put_current(&key_bytes, &data_bytes) + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + let data_bytes = DC::bytes_encode(data).map_err(|err| Error::Encoding(Box::new(err)))?; + self.cursor.put_current(key_bytes.as_ref(), data_bytes.as_ref()) } /// Write a new value to the current entry. The entry is written with the specified flags. @@ -650,8 +655,13 @@ impl<'txn, KC, DC, IM> RwRevIter<'txn, KC, DC, IM> { KC: BytesEncode<'a>, F: FnOnce(&mut ReservedSpace) -> io::Result<()>, { - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - self.cursor.put_current_reserved_with_flags(flags, &key_bytes, data_size, write_func) + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + self.cursor.put_current_reserved_with_flags( + flags, + key_bytes.as_ref(), + data_size, + write_func, + ) } /// Insert a key-value pair in this database. The entry is written with the specified flags and data codec. @@ -681,9 +691,9 @@ impl<'txn, KC, DC, IM> RwRevIter<'txn, KC, DC, IM> { KC: BytesEncode<'a>, NDC: BytesEncode<'a>, { - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - let data_bytes: Cow<[u8]> = NDC::bytes_encode(data).map_err(Error::Encoding)?; - self.cursor.put_current_with_flags(flags, &key_bytes, &data_bytes) + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + let data_bytes = NDC::bytes_encode(data).map_err(|err| Error::Encoding(Box::new(err)))?; + self.cursor.put_current_with_flags(flags, key_bytes.as_ref(), data_bytes.as_ref()) } /// Move on the first value of keys, ignoring duplicate values. diff --git a/heed/src/iterator/prefix.rs b/heed/src/iterator/prefix.rs index fabd2f49..5d85a828 100644 --- a/heed/src/iterator/prefix.rs +++ b/heed/src/iterator/prefix.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use std::marker; use heed_traits::LexicographicComparator; @@ -265,9 +264,9 @@ impl<'txn, KC, DC, C, IM> RwPrefix<'txn, KC, DC, C, IM> { KC: BytesEncode<'a>, DC: BytesEncode<'a>, { - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - let data_bytes: Cow<[u8]> = DC::bytes_encode(data).map_err(Error::Encoding)?; - self.cursor.put_current(&key_bytes, &data_bytes) + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + let data_bytes = DC::bytes_encode(data).map_err(|err| Error::Encoding(Box::new(err)))?; + self.cursor.put_current(key_bytes.as_ref(), data_bytes.as_ref()) } /// Write a new value to the current entry. The entry is written with the specified flags. @@ -294,8 +293,13 @@ impl<'txn, KC, DC, C, IM> RwPrefix<'txn, KC, DC, C, IM> { KC: BytesEncode<'a>, F: FnOnce(&mut ReservedSpace) -> io::Result<()>, { - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - self.cursor.put_current_reserved_with_flags(flags, &key_bytes, data_size, write_func) + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + self.cursor.put_current_reserved_with_flags( + flags, + key_bytes.as_ref(), + data_size, + write_func, + ) } /// Insert a key-value pair in this database. The entry is written with the specified flags and data codec. @@ -325,9 +329,9 @@ impl<'txn, KC, DC, C, IM> RwPrefix<'txn, KC, DC, C, IM> { KC: BytesEncode<'a>, NDC: BytesEncode<'a>, { - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - let data_bytes: Cow<[u8]> = NDC::bytes_encode(data).map_err(Error::Encoding)?; - self.cursor.put_current_with_flags(flags, &key_bytes, &data_bytes) + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + let data_bytes = NDC::bytes_encode(data).map_err(|err| Error::Encoding(Box::new(err)))?; + self.cursor.put_current_with_flags(flags, key_bytes.as_ref(), data_bytes.as_ref()) } /// Move on the first value of keys, ignoring duplicate values. @@ -655,9 +659,9 @@ impl<'txn, KC, DC, C, IM> RwRevPrefix<'txn, KC, DC, C, IM> { KC: BytesEncode<'a>, DC: BytesEncode<'a>, { - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - let data_bytes: Cow<[u8]> = DC::bytes_encode(data).map_err(Error::Encoding)?; - self.cursor.put_current(&key_bytes, &data_bytes) + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + let data_bytes = DC::bytes_encode(data).map_err(|err| Error::Encoding(Box::new(err)))?; + self.cursor.put_current(key_bytes.as_ref(), data_bytes.as_ref()) } /// Write a new value to the current entry. The entry is written with the specified flags. @@ -684,8 +688,13 @@ impl<'txn, KC, DC, C, IM> RwRevPrefix<'txn, KC, DC, C, IM> { KC: BytesEncode<'a>, F: FnOnce(&mut ReservedSpace) -> io::Result<()>, { - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - self.cursor.put_current_reserved_with_flags(flags, &key_bytes, data_size, write_func) + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + self.cursor.put_current_reserved_with_flags( + flags, + key_bytes.as_ref(), + data_size, + write_func, + ) } /// Insert a key-value pair in this database. The entry is written with the specified flags and data codec. @@ -715,9 +724,9 @@ impl<'txn, KC, DC, C, IM> RwRevPrefix<'txn, KC, DC, C, IM> { KC: BytesEncode<'a>, NDC: BytesEncode<'a>, { - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - let data_bytes: Cow<[u8]> = NDC::bytes_encode(data).map_err(Error::Encoding)?; - self.cursor.put_current_with_flags(flags, &key_bytes, &data_bytes) + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + let data_bytes = NDC::bytes_encode(data).map_err(|err| Error::Encoding(Box::new(err)))?; + self.cursor.put_current_with_flags(flags, key_bytes.as_ref(), data_bytes.as_ref()) } /// Move on the first value of keys, ignoring duplicate values. diff --git a/heed/src/iterator/range.rs b/heed/src/iterator/range.rs index c6dc99c4..cdd5751a 100644 --- a/heed/src/iterator/range.rs +++ b/heed/src/iterator/range.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use std::marker; use std::ops::Bound; @@ -274,9 +273,9 @@ impl<'txn, KC, DC, IM> RwRange<'txn, KC, DC, IM> { KC: BytesEncode<'a>, DC: BytesEncode<'a>, { - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - let data_bytes: Cow<[u8]> = DC::bytes_encode(data).map_err(Error::Encoding)?; - self.cursor.put_current(&key_bytes, &data_bytes) + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + let data_bytes = DC::bytes_encode(data).map_err(|err| Error::Encoding(Box::new(err)))?; + self.cursor.put_current(key_bytes.as_ref(), data_bytes.as_ref()) } /// Write a new value to the current entry. The entry is written with the specified flags. @@ -303,8 +302,13 @@ impl<'txn, KC, DC, IM> RwRange<'txn, KC, DC, IM> { KC: BytesEncode<'a>, F: FnOnce(&mut ReservedSpace) -> io::Result<()>, { - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - self.cursor.put_current_reserved_with_flags(flags, &key_bytes, data_size, write_func) + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + self.cursor.put_current_reserved_with_flags( + flags, + key_bytes.as_ref(), + data_size, + write_func, + ) } /// Insert a key-value pair in this database. The entry is written with the specified flags and data codec. @@ -334,9 +338,9 @@ impl<'txn, KC, DC, IM> RwRange<'txn, KC, DC, IM> { KC: BytesEncode<'a>, NDC: BytesEncode<'a>, { - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - let data_bytes: Cow<[u8]> = NDC::bytes_encode(data).map_err(Error::Encoding)?; - self.cursor.put_current_with_flags(flags, &key_bytes, &data_bytes) + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + let data_bytes = NDC::bytes_encode(data).map_err(|err| Error::Encoding(Box::new(err)))?; + self.cursor.put_current_with_flags(flags, key_bytes.as_ref(), data_bytes.as_ref()) } /// Move on the first value of keys, ignoring duplicate values. @@ -711,9 +715,9 @@ impl<'txn, KC, DC, IM> RwRevRange<'txn, KC, DC, IM> { KC: BytesEncode<'a>, DC: BytesEncode<'a>, { - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - let data_bytes: Cow<[u8]> = DC::bytes_encode(data).map_err(Error::Encoding)?; - self.cursor.put_current(&key_bytes, &data_bytes) + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + let data_bytes = DC::bytes_encode(data).map_err(|err| Error::Encoding(Box::new(err)))?; + self.cursor.put_current(key_bytes.as_ref(), data_bytes.as_ref()) } /// Write a new value to the current entry. The entry is written with the specified flags. @@ -740,8 +744,13 @@ impl<'txn, KC, DC, IM> RwRevRange<'txn, KC, DC, IM> { KC: BytesEncode<'a>, F: FnOnce(&mut ReservedSpace) -> io::Result<()>, { - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - self.cursor.put_current_reserved_with_flags(flags, &key_bytes, data_size, write_func) + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + self.cursor.put_current_reserved_with_flags( + flags, + key_bytes.as_ref(), + data_size, + write_func, + ) } /// Insert a key-value pair in this database. The entry is written with the specified flags and data codec. @@ -771,9 +780,9 @@ impl<'txn, KC, DC, IM> RwRevRange<'txn, KC, DC, IM> { KC: BytesEncode<'a>, NDC: BytesEncode<'a>, { - let key_bytes: Cow<[u8]> = KC::bytes_encode(key).map_err(Error::Encoding)?; - let data_bytes: Cow<[u8]> = NDC::bytes_encode(data).map_err(Error::Encoding)?; - self.cursor.put_current_with_flags(flags, &key_bytes, &data_bytes) + let key_bytes = KC::bytes_encode(key).map_err(|err| Error::Encoding(Box::new(err)))?; + let data_bytes = NDC::bytes_encode(data).map_err(|err| Error::Encoding(Box::new(err)))?; + self.cursor.put_current_with_flags(flags, key_bytes.as_ref(), data_bytes.as_ref()) } /// Move on the first value of keys, ignoring duplicate values.