use crate::{keys::Key, reader::RodeoReader, resolver::RodeoResolver, single_threaded::Rodeo};
use core::{fmt, iter, marker::PhantomData, num::NonZeroUsize, slice};
pub type LassoResult<T> = core::result::Result<T, LassoError>;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LassoError {
kind: LassoErrorKind,
}
impl LassoError {
#[cfg_attr(feature = "inline-more", inline)]
pub const fn kind(&self) -> LassoErrorKind {
self.kind
}
}
impl LassoError {
pub(crate) const fn new(kind: LassoErrorKind) -> Self {
Self { kind }
}
}
impl fmt::Display for LassoError {
#[cfg_attr(feature = "inline-more", inline)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Lasso encountered an error: {}", self.kind)
}
}
#[cfg(not(feature = "no-std"))]
impl std::error::Error for LassoError {}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum LassoErrorKind {
MemoryLimitReached,
KeySpaceExhaustion,
FailedAllocation,
}
impl LassoErrorKind {
#[cfg_attr(feature = "inline-more", inline)]
pub fn is_memory_limit(self) -> bool {
self == Self::MemoryLimitReached
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn is_keyspace_exhaustion(self) -> bool {
self == Self::KeySpaceExhaustion
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn is_failed_alloc(self) -> bool {
self == Self::FailedAllocation
}
}
impl fmt::Display for LassoErrorKind {
#[cfg_attr(feature = "inline-more", inline)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::MemoryLimitReached => f.write_str("The configured memory limit was reached"),
Self::KeySpaceExhaustion => f.write_str("The key space was exhausted"),
Self::FailedAllocation => f.write_str("Failed to allocate memory"),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Capacity {
pub(crate) strings: usize,
pub(crate) bytes: NonZeroUsize,
}
impl Capacity {
#[cfg_attr(feature = "inline-more", inline)]
pub fn new(strings: usize, bytes: NonZeroUsize) -> Self {
Self { strings, bytes }
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn for_strings(strings: usize) -> Self {
Self {
strings,
..Self::default()
}
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn for_bytes(bytes: NonZeroUsize) -> Self {
Self {
bytes,
..Self::default()
}
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn minimal() -> Self {
Self {
strings: 0,
bytes: unsafe { NonZeroUsize::new_unchecked(1) },
}
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn strings(&self) -> usize {
self.strings
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn bytes(&self) -> NonZeroUsize {
self.bytes
}
}
impl Default for Capacity {
#[cfg_attr(feature = "inline-more", inline)]
fn default() -> Self {
Self {
strings: 50,
bytes: unsafe { NonZeroUsize::new_unchecked(4096) },
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MemoryLimits {
pub(crate) max_memory_usage: usize,
}
impl MemoryLimits {
#[cfg_attr(feature = "inline-more", inline)]
pub fn new(max_memory_usage: usize) -> Self {
Self { max_memory_usage }
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn for_memory_usage(max_memory_usage: usize) -> Self {
Self { max_memory_usage }
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn max_memory_usage(&self) -> usize {
self.max_memory_usage
}
}
impl Default for MemoryLimits {
#[cfg_attr(feature = "inline-more", inline)]
fn default() -> Self {
Self {
max_memory_usage: usize::max_value(),
}
}
}
#[derive(Clone, Debug)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct Iter<'a, K> {
iter: iter::Enumerate<slice::Iter<'a, &'a str>>,
__key: PhantomData<K>,
}
impl<'a, K> Iter<'a, K> {
#[cfg_attr(feature = "inline-more", inline)]
pub(crate) fn from_rodeo<S>(rodeo: &'a Rodeo<K, S>) -> Self {
Self {
iter: rodeo.strings.iter().enumerate(),
__key: PhantomData,
}
}
#[cfg_attr(feature = "inline-more", inline)]
pub(crate) fn from_reader<S>(rodeo: &'a RodeoReader<K, S>) -> Self {
Self {
iter: rodeo.strings.iter().enumerate(),
__key: PhantomData,
}
}
#[cfg_attr(feature = "inline-more", inline)]
pub(crate) fn from_resolver(rodeo: &'a RodeoResolver<K>) -> Self {
Self {
iter: rodeo.strings.iter().enumerate(),
__key: PhantomData,
}
}
}
fn iter_element<'a, K>((key, string): (usize, &&'a str)) -> (K, &'a str)
where
K: Key,
{
(
K::try_from_usize(key).unwrap_or_else(|| unreachable!()),
*string,
)
}
impl<'a, K> Iterator for Iter<'a, K>
where
K: Key,
{
type Item = (K, &'a str);
#[cfg_attr(feature = "inline-more", inline)]
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(iter_element)
}
#[cfg_attr(feature = "inline-more", inline)]
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
impl<'a, K> DoubleEndedIterator for Iter<'a, K>
where
K: Key,
{
#[cfg_attr(feature = "inline-more", inline)]
fn next_back(&mut self) -> Option<(K, &'a str)> {
self.iter.next_back().map(iter_element)
}
#[cfg_attr(feature = "inline-more", inline)]
fn nth_back(&mut self, n: usize) -> Option<(K, &'a str)> {
self.iter.nth_back(n).map(iter_element)
}
}
impl<'a, K: Key> ExactSizeIterator for Iter<'a, K> {}
impl<'a, K: Key> iter::FusedIterator for Iter<'a, K> {}
#[derive(Clone, Debug)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct Strings<'a, K> {
iter: slice::Iter<'a, &'a str>,
__key: PhantomData<K>,
}
impl<'a, K> Strings<'a, K> {
#[cfg_attr(feature = "inline-more", inline)]
pub(crate) fn from_rodeo<H>(rodeo: &'a Rodeo<K, H>) -> Self {
Self {
iter: rodeo.strings.iter(),
__key: PhantomData,
}
}
#[cfg_attr(feature = "inline-more", inline)]
pub(crate) fn from_reader<H>(rodeo: &'a RodeoReader<K, H>) -> Self {
Self {
iter: rodeo.strings.iter(),
__key: PhantomData,
}
}
#[cfg_attr(feature = "inline-more", inline)]
pub(crate) fn from_resolver(rodeo: &'a RodeoResolver<K>) -> Self {
Self {
iter: rodeo.strings.iter(),
__key: PhantomData,
}
}
}
impl<'a, K> Iterator for Strings<'a, K> {
type Item = &'a str;
#[cfg_attr(feature = "inline-more", inline)]
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().copied()
}
#[cfg_attr(feature = "inline-more", inline)]
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
impl<'a, K> DoubleEndedIterator for Strings<'a, K>
where
K: Key,
{
#[cfg_attr(feature = "inline-more", inline)]
fn next_back(&mut self) -> Option<&'a str> {
self.iter.next_back().copied()
}
#[cfg_attr(feature = "inline-more", inline)]
fn nth_back(&mut self, n: usize) -> Option<&'a str> {
self.iter.nth_back(n).copied()
}
}
impl<'a, K> ExactSizeIterator for Strings<'a, K> {}
impl<'a, K: Key> iter::FusedIterator for Strings<'a, K> {}
macro_rules! compile {
($(
if #[$meta:meta] {
$($item:item)*
} $(else if #[$else_if_meta:meta] {
$($else_if_item:item)*
})* $(else {
$($else_item:item)*
})?
)+) => {
$(
$(
#[cfg($meta)]
$item
)*
compile!{
@inner
( $meta, )
$(else if #[$else_if_meta] {
$( $else_if_item )*
})* $(else {
$( $else_item )*
})?
}
)+
};
(@recurse
($($prev_metas:tt)*)
($new_meta:meta)
$($rem:tt)*
) => {
compile!{
@inner
($( $prev_metas )* $new_meta,)
$( $rem )*
}
};
(@inner
$prev_metas:tt
else if #[$meta:meta] {
$($else_if_item:item)*
}
$($rem:tt)*
) => {
$(
#[cfg(all(not(any $prev_metas), $meta))]
$else_if_item
)*
compile! {
@recurse $prev_metas ($meta) $( $rem )*
}
};
(@inner
$prev_metas:tt
else {
$($else_item:item)*
}
)=>{
$(
#[cfg(not(any $prev_metas))]
$else_item
)*
};
(@inner ($($prev_metas:tt)*))=>{};
}
#[cfg(debug_assertions)]
macro_rules! index_unchecked {
($slice:expr, $idx:expr) => {{
unsafe fn x() {}
x();
let elem: &_ = $slice[$idx];
elem
}};
}
#[cfg(not(debug_assertions))]
macro_rules! index_unchecked {
($slice:expr, $idx:expr) => {{
let elem: &_ = $slice.get_unchecked($idx);
elem
}};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_capacity() {
let capacity = Capacity::new(100, NonZeroUsize::new(100).unwrap());
assert_eq!(100, capacity.strings());
assert_eq!(100, capacity.bytes.get());
let capacity = Capacity::default();
assert_eq!(capacity.strings, capacity.strings());
assert_eq!(capacity.bytes, capacity.bytes());
let capacity = Capacity::for_strings(10);
assert_eq!(capacity.strings(), 10);
let capacity = Capacity::for_bytes(NonZeroUsize::new(10).unwrap());
assert_eq!(capacity.bytes().get(), 10);
let capacity = Capacity::minimal();
assert_eq!(capacity.strings(), 0);
assert_eq!(capacity.bytes().get(), 1);
}
#[test]
fn iter_rodeo() {
let mut rodeo = Rodeo::default();
let a = rodeo.get_or_intern("A");
let b = rodeo.get_or_intern("B");
let c = rodeo.get_or_intern("C");
let d = rodeo.get_or_intern("D");
let mut iter = Iter::from_rodeo(&rodeo);
assert_eq!((4, Some(4)), iter.size_hint());
assert_eq!(Some((a, "A")), iter.next());
assert_eq!(Some((b, "B")), iter.next());
assert_eq!(Some((c, "C")), iter.next());
assert_eq!(Some((d, "D")), iter.next());
assert_eq!(None, iter.next());
assert_eq!((0, Some(0)), iter.size_hint());
}
#[test]
fn iter_reader() {
let mut rodeo = Rodeo::default();
let a = rodeo.get_or_intern("A");
let b = rodeo.get_or_intern("B");
let c = rodeo.get_or_intern("C");
let d = rodeo.get_or_intern("D");
let reader = rodeo.into_reader();
let mut iter = Iter::from_reader(&reader);
assert_eq!((4, Some(4)), iter.size_hint());
assert_eq!(Some((a, "A")), iter.next());
assert_eq!(Some((b, "B")), iter.next());
assert_eq!(Some((c, "C")), iter.next());
assert_eq!(Some((d, "D")), iter.next());
assert_eq!(None, iter.next());
assert_eq!((0, Some(0)), iter.size_hint());
}
#[test]
fn iter_resolver() {
let mut rodeo = Rodeo::default();
let a = rodeo.get_or_intern("A");
let b = rodeo.get_or_intern("B");
let c = rodeo.get_or_intern("C");
let d = rodeo.get_or_intern("D");
let resolver = rodeo.into_resolver();
let mut iter = Iter::from_resolver(&resolver);
assert_eq!((4, Some(4)), iter.size_hint());
assert_eq!(Some((a, "A")), iter.next());
assert_eq!(Some((b, "B")), iter.next());
assert_eq!(Some((c, "C")), iter.next());
assert_eq!(Some((d, "D")), iter.next());
assert_eq!(None, iter.next());
assert_eq!((0, Some(0)), iter.size_hint());
}
#[test]
fn strings_rodeo() {
let mut rodeo = Rodeo::default();
rodeo.get_or_intern("A");
rodeo.get_or_intern("B");
rodeo.get_or_intern("C");
rodeo.get_or_intern("D");
let mut iter = Strings::from_rodeo(&rodeo);
assert_eq!((4, Some(4)), iter.size_hint());
assert_eq!(Some("A"), iter.next());
assert_eq!(Some("B"), iter.next());
assert_eq!(Some("C"), iter.next());
assert_eq!(Some("D"), iter.next());
assert_eq!(None, iter.next());
assert_eq!((0, Some(0)), iter.size_hint());
}
#[test]
fn strings_reader() {
let mut rodeo = Rodeo::default();
rodeo.get_or_intern("A");
rodeo.get_or_intern("B");
rodeo.get_or_intern("C");
rodeo.get_or_intern("D");
let reader = rodeo.into_reader();
let mut iter = Strings::from_reader(&reader);
assert_eq!((4, Some(4)), iter.size_hint());
assert_eq!(Some("A"), iter.next());
assert_eq!(Some("B"), iter.next());
assert_eq!(Some("C"), iter.next());
assert_eq!(Some("D"), iter.next());
assert_eq!(None, iter.next());
assert_eq!((0, Some(0)), iter.size_hint());
}
#[test]
fn strings_resolver() {
let mut rodeo = Rodeo::default();
rodeo.get_or_intern("A");
rodeo.get_or_intern("B");
rodeo.get_or_intern("C");
rodeo.get_or_intern("D");
let resolver = rodeo.into_resolver();
let mut iter = Strings::from_resolver(&resolver);
assert_eq!((4, Some(4)), iter.size_hint());
assert_eq!(Some("A"), iter.next());
assert_eq!(Some("B"), iter.next());
assert_eq!(Some("C"), iter.next());
assert_eq!(Some("D"), iter.next());
assert_eq!(None, iter.next());
assert_eq!((0, Some(0)), iter.size_hint());
}
}