diff --git a/heed-traits/src/lib.rs b/heed-traits/src/lib.rs index 88fb0efe..1a227136 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] forwards 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 5f2fba49..b383680e 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 = &'a [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 = &'a [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 b4b42916..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/Cargo.toml b/heed/Cargo.toml index 4bd00ab4..b14d36c2 100644 --- a/heed/Cargo.toml +++ b/heed/Cargo.toml @@ -30,6 +30,10 @@ tempfile = "3.18.0" [target.'cfg(windows)'.dependencies] url = "2.5.4" +[lints.clippy] +# this lint triggers on code unrelated to this pull request (since Rust 1.88.0) +uninlined_format_args = "allow" + [features] # The `serde` feature makes some types serializable, # like the `EnvOpenOptions` struct. diff --git a/heed/src/cookbook.rs b/heed/src/cookbook.rs index 153d63ed..bb5eb50f 100644 --- a/heed/src/cookbook.rs +++ b/heed/src/cookbook.rs @@ -144,7 +144,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; @@ -152,7 +152,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, @@ -170,18 +170,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) //! } //! } //! @@ -216,9 +218,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/databases/database.rs b/heed/src/databases/database.rs index 46ccc902..004b6246 100644 --- a/heed/src/databases/database.rs +++ b/heed/src/databases/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; @@ -361,9 +360,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 { @@ -450,8 +449,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) @@ -514,8 +513,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)) { @@ -583,9 +582,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), }; @@ -656,9 +656,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, }; @@ -728,8 +729,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)), @@ -1295,26 +1296,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, }; @@ -1386,26 +1392,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, }; @@ -1468,26 +1479,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, }; @@ -1559,26 +1575,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, }; @@ -1643,8 +1664,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)) } @@ -1714,8 +1737,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)) } @@ -1776,8 +1801,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)) } @@ -1847,8 +1874,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)) } @@ -1894,11 +1923,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 { @@ -1963,8 +1992,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; @@ -2059,11 +2088,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 { @@ -2172,11 +2201,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 { @@ -2329,9 +2358,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() | ffi::MDB_RESERVE; @@ -2415,8 +2444,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( @@ -2505,10 +2534,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( diff --git a/heed/src/iterator/iter.rs b/heed/src/iterator/iter.rs index 2f85fca5..057002dd 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}; @@ -269,9 +269,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. @@ -298,8 +298,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. @@ -329,9 +334,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. @@ -615,9 +620,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. @@ -644,8 +649,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. @@ -675,9 +685,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 0feaba08..a6a09873 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; @@ -262,9 +261,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. @@ -291,8 +290,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. @@ -322,9 +326,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. @@ -649,9 +653,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. @@ -678,8 +682,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. @@ -709,9 +718,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 fc40a0b9..0143c521 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; @@ -272,9 +271,9 @@ impl<'txn, KC, DC, C, IM> RwRange<'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. @@ -301,8 +300,13 @@ impl<'txn, KC, DC, C, IM> RwRange<'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. @@ -332,9 +336,9 @@ impl<'txn, KC, DC, C, IM> RwRange<'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. @@ -708,9 +712,9 @@ impl<'txn, KC, DC, C, IM> RwRevRange<'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. @@ -737,8 +741,13 @@ impl<'txn, KC, DC, C, IM> RwRevRange<'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. @@ -768,9 +777,9 @@ impl<'txn, KC, DC, C, IM> RwRevRange<'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.