use once_cell::sync::Lazy;
use smallvec::SmallVec;
use crate::collection::NitriteId;
use crate::common::NavigableMap;
use crate::common::{Key, Value};
use crate::errors::{ErrorKind, NitriteError, NitriteResult};
use crate::store::{EntryIterator, NitriteMap, NitriteMapProvider};
use std::collections::BTreeMap;
use std::fmt::{Debug, Display, Formatter};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
static INDEX_CORRUPT_ERROR: Lazy<NitriteError> = Lazy::new(|| {
NitriteError::new(
"Index is in corrupt state",
ErrorKind::IndexingError,
)
});
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) enum IndexLayout {
Array,
Composite,
}
pub(crate) fn normalize_index_value(value: &Value) -> Value {
match value {
Value::U8(v) => Value::I8(*v as i8),
Value::U16(v) => Value::I16(*v as i16),
Value::U32(v) => Value::I32(*v as i32),
Value::U64(v) => Value::I64(*v as i64),
Value::U128(v) => Value::I128(*v as i128),
Value::USize(v) => Value::ISize(*v as isize),
other => other.clone(),
}
}
pub(crate) fn composite_key(value: &Value, id: &NitriteId) -> Key {
Value::Array(vec![normalize_index_value(value), Value::NitriteId(*id)])
}
fn composite_upper(value: &Value) -> Key {
Value::Array(vec![
normalize_index_value(value),
Value::NitriteId(NitriteId::max_sentinel()),
])
}
fn composite_prefix(value: &Value) -> Key {
Value::Array(vec![normalize_index_value(value)])
}
fn composite_lead(key: &Key) -> Option<&Value> {
match key {
Value::Array(parts) if !parts.is_empty() => Some(&parts[0]),
_ => None,
}
}
fn reconstruct_group(rows: &[&[Value]], value_fields: usize) -> Value {
if value_fields == 0 {
let ids = rows
.iter()
.filter_map(|r| r.first().cloned())
.collect::<Vec<_>>();
return Value::Array(ids);
}
let mut map: BTreeMap<Value, Value> = BTreeMap::new();
let mut i = 0;
while i < rows.len() {
let Some(field_value) = rows[i].first() else {
i += 1;
continue;
};
let field_value = field_value.clone();
let mut tails: Vec<&[Value]> = Vec::new();
while i < rows.len() && rows[i].first() == Some(&field_value) {
tails.push(&rows[i][1..]);
i += 1;
}
map.insert(field_value, reconstruct_group(&tails, value_fields - 1));
}
Value::Map(map)
}
#[derive(Clone)]
pub struct IndexMap {
inner: Arc<IndexMapInner>,
}
impl IndexMap {
pub fn new(
nitrite_map: Option<NitriteMap>,
sub_map: Option<BTreeMap<Value, Value>>,
) -> Self {
let inner_map = IndexMapInner::new(nitrite_map, sub_map, IndexLayout::Array, 1);
IndexMap {
inner: Arc::new(inner_map),
}
}
pub(crate) fn composite(nitrite_map: NitriteMap, arity: usize) -> Self {
let inner_map =
IndexMapInner::new(Some(nitrite_map), None, IndexLayout::Composite, arity.max(1));
IndexMap {
inner: Arc::new(inner_map),
}
}
pub fn get(&self, key: &Key) -> NitriteResult<Option<Value>> {
self.inner.get(key)
}
pub fn first_key(&self) -> NitriteResult<Option<Key>> {
self.inner.first_key()
}
pub fn last_key(&self) -> NitriteResult<Option<Key>> {
self.inner.last_key()
}
pub fn higher_key(&self, key: &Key) -> NitriteResult<Option<Key>> {
self.inner.higher_key(key)
}
pub fn ceiling_key(&self, key: &Key) -> NitriteResult<Option<Key>> {
self.inner.ceiling_key(key)
}
pub fn lower_key(&self, key: &Key) -> NitriteResult<Option<Key>> {
self.inner.lower_key(key)
}
pub fn floor_key(&self, key: &Key) -> NitriteResult<Option<Key>> {
self.inner.floor_key(key)
}
pub fn entries(&self) -> NitriteResult<IndexMapIterator> {
self.inner.entries()
}
pub(crate) fn set_reverse_scan(&self, reverse_scan: bool) {
self.inner.set_reverse_scan(reverse_scan)
}
pub(crate) fn terminal_nitrite_ids(&self) -> NitriteResult<SmallVec<[NitriteId; 16]>> {
self.inner.terminal_nitrite_ids()
}
}
impl Debug for IndexMap {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
if let Some(ref nitrite_map) = &self.inner.nitrite_map {
let entries = match nitrite_map.entries() {
Ok(entries) => entries,
Err(e) => return write!(f, "Error retrieving entries: {:?}", e),
};
for entry in entries {
match entry {
Ok((key, value)) => writeln!(f, "Key: {:?}, Value: {:?}", key, value)?,
Err(e) => write!(f, "Error retrieving entry: {:?}", e)?,
}
}
} else if let Some(sub_map) = &self.inner.sub_map {
let entries = match sub_map.iter() {
Ok(entries) => entries,
Err(e) => return write!(f, "Error retrieving sub_map entries: {:?}", e),
};
for entry in entries {
match entry {
Ok((key, value)) => writeln!(f, "Key: {:?}, Value: {:?}", key, value)?,
Err(e) => write!(f, "Error retrieving entry: {:?}", e)?,
}
}
} else {
write!(f, "Empty IndexMap")?
}
Ok(())
}
}
struct IndexMapInner {
nitrite_map: Option<NitriteMap>,
sub_map: Option<InMemoryIndexMap>,
layout: IndexLayout,
arity: usize,
reverse_scan: AtomicBool,
}
impl IndexMapInner {
fn new(
nitrite_map: Option<NitriteMap>,
sub_map: Option<BTreeMap<Value, Value>>,
layout: IndexLayout,
arity: usize,
) -> Self {
let in_memory_map = sub_map.map(InMemoryIndexMap::new);
IndexMapInner {
nitrite_map,
sub_map: in_memory_map,
layout,
arity,
reverse_scan: AtomicBool::from(false),
}
}
fn composite_map(&self) -> NitriteResult<&NitriteMap> {
self.nitrite_map.as_ref().ok_or_else(|| {
log::error!("Composite index is in corrupt state: missing backing map");
INDEX_CORRUPT_ERROR.clone()
})
}
fn composite_get(&self, value: &Key) -> NitriteResult<Option<Value>> {
let map = self.composite_map()?;
let target = normalize_index_value(value);
let mut rows: Vec<Vec<Value>> = Vec::new();
let mut key = map.ceiling_key(&composite_prefix(value))?;
while let Some(k) = key {
match &k {
Value::Array(parts)
if parts.first().map(|p| *p == target).unwrap_or(false) =>
{
rows.push(parts[1..].to_vec());
key = map.higher_key(&k)?;
}
_ => break,
}
}
if rows.is_empty() {
Ok(None)
} else {
let refs: Vec<&[Value]> = rows.iter().map(|r| r.as_slice()).collect();
Ok(Some(reconstruct_group(&refs, self.arity - 1)))
}
}
fn composite_value_of(key: Option<Key>) -> Option<Key> {
key.and_then(|k| composite_lead(&k).cloned())
}
fn composite_higher_scan(&self, value: &Key) -> NitriteResult<Option<Key>> {
let map = self.composite_map()?;
let target = normalize_index_value(value);
let mut key = map.ceiling_key(&composite_prefix(value))?;
while let Some(k) = key {
match composite_lead(&k) {
Some(lead) if *lead == target => {
key = map.higher_key(&k)?;
}
Some(lead) => return Ok(Some(lead.clone())),
None => return Ok(None),
}
}
Ok(None)
}
pub fn get(&self, key: &Key) -> NitriteResult<Option<Value>> {
if self.layout == IndexLayout::Composite {
return self.composite_get(key);
}
if let Some(ref nitrite_map) = self.nitrite_map {
let value = nitrite_map.get(key)?;
Ok(value)
} else {
let sub_map = self.sub_map.as_ref().ok_or_else(|| {
log::error!("Index is in corrupt state. Could not get value for key: {:?}", key);
INDEX_CORRUPT_ERROR.clone()
})?;
sub_map.get(key)
}
}
pub fn first_key(&self) -> NitriteResult<Option<Key>> {
if self.layout == IndexLayout::Composite {
return Ok(Self::composite_value_of(self.composite_map()?.first_key()?));
}
if let Some(ref nitrite_map) = &self.nitrite_map {
let key = nitrite_map.first_key()?;
Ok(key)
} else {
let sub_map = self.sub_map.as_ref().ok_or_else(|| {
log::error!("Index is in corrupt state. Could not get first key");
INDEX_CORRUPT_ERROR.clone()
})?;
sub_map.first_key()
}
}
pub fn last_key(&self) -> NitriteResult<Option<Key>> {
if self.layout == IndexLayout::Composite {
return Ok(Self::composite_value_of(self.composite_map()?.last_key()?));
}
if let Some(ref nitrite_map) = &self.nitrite_map {
let key = nitrite_map.last_key()?;
Ok(key)
} else {
let sub_map = self.sub_map.as_ref().ok_or_else(|| {
log::error!("Index is in corrupt state. Could not get last key");
INDEX_CORRUPT_ERROR.clone()
})?;
sub_map.last_key()
}
}
pub fn higher_key(&self, key: &Key) -> NitriteResult<Option<Key>> {
if self.layout == IndexLayout::Composite {
if self.arity == 1 {
return Ok(Self::composite_value_of(
self.composite_map()?.higher_key(&composite_upper(key))?,
));
}
return self.composite_higher_scan(key);
}
if let Some(ref nitrite_map) = &self.nitrite_map {
let key = nitrite_map.higher_key(key)?;
Ok(key)
} else {
let sub_map = self.sub_map.as_ref().ok_or_else(|| {
log::error!("Index is in corrupt state. Could not get higher key for key: {:?}", key);
INDEX_CORRUPT_ERROR.clone()
})?;
sub_map.higher_key(key)
}
}
pub fn ceiling_key(&self, key: &Key) -> NitriteResult<Option<Key>> {
if self.layout == IndexLayout::Composite {
return Ok(Self::composite_value_of(
self.composite_map()?.ceiling_key(&composite_prefix(key))?,
));
}
if let Some(ref nitrite_map) = &self.nitrite_map {
let key = nitrite_map.ceiling_key(key)?;
Ok(key)
} else {
let sub_map = self.sub_map.as_ref().ok_or_else(|| {
log::error!("Index is in corrupt state. Could not get ceiling key for key: {:?}", key);
INDEX_CORRUPT_ERROR.clone()
})?;
sub_map.ceiling_key(key)
}
}
pub fn lower_key(&self, key: &Key) -> NitriteResult<Option<Key>> {
if self.layout == IndexLayout::Composite {
return Ok(Self::composite_value_of(
self.composite_map()?.lower_key(&composite_prefix(key))?,
));
}
if let Some(ref nitrite_map) = &self.nitrite_map {
let key = nitrite_map.lower_key(key)?;
Ok(key)
} else {
let sub_map = self.sub_map.as_ref().ok_or_else(|| {
log::error!("Index is in corrupt state. Could not get lower key for key: {:?}", key);
INDEX_CORRUPT_ERROR.clone()
})?;
sub_map.lower_key(key)
}
}
pub fn floor_key(&self, key: &Key) -> NitriteResult<Option<Key>> {
if self.layout == IndexLayout::Composite {
if self.arity == 1 {
return Ok(Self::composite_value_of(
self.composite_map()?.floor_key(&composite_upper(key))?,
));
}
let target = normalize_index_value(key);
let ceiling = self.composite_map()?.ceiling_key(&composite_prefix(key))?;
if ceiling
.as_ref()
.and_then(composite_lead)
.map(|lead| *lead == target)
.unwrap_or(false)
{
return Ok(Some(target));
}
return Ok(Self::composite_value_of(
self.composite_map()?.lower_key(&composite_prefix(key))?,
));
}
if let Some(ref nitrite_map) = &self.nitrite_map {
let key = nitrite_map.floor_key(key)?;
Ok(key)
} else {
let sub_map = self.sub_map.as_ref().ok_or_else(|| {
log::error!("Index is in corrupt state. Could not get floor key for key: {:?}", key);
INDEX_CORRUPT_ERROR.clone()
})?;
sub_map.floor_key(key)
}
}
pub fn entries(&self) -> NitriteResult<IndexMapIterator> {
if self.layout == IndexLayout::Composite {
let iterator = self.composite_map()?.entries()?;
return Ok(IndexMapIterator::new_composite(iterator, self.arity));
}
if let Some(ref nitrite_map) = &self.nitrite_map {
let iterator = nitrite_map.entries()?;
Ok(IndexMapIterator::new(
Some(iterator),
None,
self.reverse_scan.load(Ordering::Relaxed),
))
} else {
let sub_map = self.sub_map.as_ref().ok_or_else(|| {
log::error!("Index is in corrupt state. Could not get entries");
INDEX_CORRUPT_ERROR.clone()
})?;
sub_map.iter()
}
}
pub(crate) fn terminal_nitrite_ids(&self) -> NitriteResult<SmallVec<[NitriteId; 16]>> {
let mut nitrite_ids = SmallVec::new();
for entry in self.entries()? {
let (_, value) = entry?;
match value {
Value::Array(array) => {
for value in array {
if let Value::NitriteId(nitrite_id) = value {
nitrite_ids.push(nitrite_id);
}
}
}
Value::Map(btree_map) => {
let index_map = IndexMap::new(None, Some(btree_map));
let terminal_nitrite_ids = index_map.terminal_nitrite_ids()?;
nitrite_ids.extend(terminal_nitrite_ids);
}
_ => continue,
}
}
Ok(nitrite_ids)
}
pub(crate) fn set_reverse_scan(&self, reverse_scan: bool) {
self.reverse_scan.store(reverse_scan, Ordering::Relaxed);
}
}
#[derive(Clone)]
struct InMemoryIndexMap {
inner: Arc<InMemoryIndexMapInner>,
}
impl InMemoryIndexMap {
fn new(inner_map: BTreeMap<Value, Value>) -> Self {
let inner = InMemoryIndexMapInner {
inner_map,
reverse_scan: AtomicBool::from(false),
};
InMemoryIndexMap {
inner: Arc::new(inner),
}
}
fn iter(&self) -> NitriteResult<IndexMapIterator> {
Ok(IndexMapIterator::new(
None,
Some(self.clone()),
self.inner.reverse_scan.load(Ordering::Relaxed),
))
}
fn get(&self, key: &Key) -> NitriteResult<Option<Value>> {
self.inner.as_ref().get(key)
}
fn first_key(&self) -> NitriteResult<Option<Key>> {
self.inner.as_ref().first_key()
}
fn last_key(&self) -> NitriteResult<Option<Key>> {
self.inner.as_ref().last_key()
}
fn higher_key(&self, key: &Key) -> NitriteResult<Option<Key>> {
self.inner.as_ref().higher_key(key)
}
fn ceiling_key(&self, key: &Key) -> NitriteResult<Option<Key>> {
self.inner.as_ref().ceiling_key(key)
}
fn lower_key(&self, key: &Key) -> NitriteResult<Option<Key>> {
self.inner.as_ref().lower_key(key)
}
fn floor_key(&self, key: &Key) -> NitriteResult<Option<Key>> {
self.inner.as_ref().floor_key(key)
}
fn set_reverse_scan(&self, _reverse_scan: bool) {
}
}
pub struct InMemoryIndexMapInner {
inner_map: BTreeMap<Value, Value>,
reverse_scan: AtomicBool,
}
impl InMemoryIndexMapInner {
fn get(&self, key: &Key) -> NitriteResult<Option<Value>> {
Ok(self.inner_map.get(key).cloned())
}
fn first_key(&self) -> NitriteResult<Option<Key>> {
self.inner_map.first_key()
}
fn last_key(&self) -> NitriteResult<Option<Key>> {
self.inner_map.last_key()
}
fn higher_key(&self, key: &Key) -> NitriteResult<Option<Key>> {
self.inner_map.higher_key(key)
}
fn ceiling_key(&self, key: &Key) -> NitriteResult<Option<Key>> {
self.inner_map.ceiling_key(key)
}
fn lower_key(&self, key: &Key) -> NitriteResult<Option<Key>> {
self.inner_map.lower_key(key)
}
fn floor_key(&self, key: &Key) -> NitriteResult<Option<Key>> {
self.inner_map.floor_key(key)
}
}
pub struct IndexMapIterator {
nitrite_map_iterator: Option<EntryIterator>,
cached_index_map: Option<InMemoryIndexMap>,
current: Option<Key>,
reverse_scan: bool,
composite_iterator: Option<EntryIterator>,
composite_arity: usize,
composite_pending: Option<(Key, Value)>,
}
impl IndexMapIterator {
fn new(
nitrite_map_iterator: Option<EntryIterator>,
cached_index_map: Option<InMemoryIndexMap>,
reverse_scan: bool,
) -> Self {
IndexMapIterator {
nitrite_map_iterator,
cached_index_map,
current: None,
reverse_scan,
composite_iterator: None,
composite_arity: 1,
composite_pending: None,
}
}
fn new_composite(iterator: EntryIterator, arity: usize) -> Self {
IndexMapIterator {
nitrite_map_iterator: None,
cached_index_map: None,
current: None,
reverse_scan: false,
composite_iterator: Some(iterator),
composite_arity: arity,
composite_pending: None,
}
}
fn next_composite(&mut self) -> Option<NitriteResult<(Key, Value)>> {
let arity = self.composite_arity;
let iterator = self.composite_iterator.as_mut()?;
let first = match self.composite_pending.take() {
Some(kv) => kv,
None => match iterator.next() {
Some(Ok(kv)) => kv,
Some(Err(e)) => return Some(Err(e)),
None => return None,
},
};
let (group_value, first_tail) = match &first.0 {
Value::Array(parts) if !parts.is_empty() => {
(parts[0].clone(), parts[1..].to_vec())
}
_ => {
log::error!("Composite index is in corrupt state: malformed key {:?}", first.0);
return Some(Err(INDEX_CORRUPT_ERROR.clone()));
}
};
let mut rows: Vec<Vec<Value>> = vec![first_tail];
loop {
match iterator.next() {
Some(Ok(kv)) => match &kv.0 {
Value::Array(parts)
if parts.first().map(|p| *p == group_value).unwrap_or(false) =>
{
rows.push(parts[1..].to_vec());
}
Value::Array(_) => {
self.composite_pending = Some(kv);
break;
}
_ => {
log::error!("Composite index is in corrupt state: malformed key {:?}", kv.0);
return Some(Err(INDEX_CORRUPT_ERROR.clone()));
}
},
Some(Err(e)) => return Some(Err(e)),
None => break,
}
}
let refs: Vec<&[Value]> = rows.iter().map(|r| r.as_slice()).collect();
Some(Ok((group_value, reconstruct_group(&refs, arity - 1))))
}
fn higher_key(&self, btree_map: &InMemoryIndexMap) -> NitriteResult<Option<Key>> {
match &self.current {
Some(current_key) => btree_map.higher_key(current_key),
None => btree_map.first_key(),
}
}
fn lower_key(&self, btree_map: &InMemoryIndexMap) -> NitriteResult<Option<Key>> {
match &self.current {
Some(current_key) => btree_map.lower_key(current_key),
None => btree_map.last_key(),
}
}
}
impl Iterator for IndexMapIterator {
type Item = NitriteResult<(Key, Value)>;
fn next(&mut self) -> Option<Self::Item> {
if self.composite_iterator.is_some() {
return self.next_composite();
}
if !self.reverse_scan {
if let Some(nitrite_map_iterator) = &mut self.nitrite_map_iterator {
let next = nitrite_map_iterator.next();
if let Some(Ok((key, _))) = &next {
self.current = Some(key.clone());
}
next
} else if let Some(map) = &self.cached_index_map {
let next_key = self.higher_key(map);
match next_key {
Ok(Some(key)) => {
self.current = Some(key.clone());
match map.inner.get(&key) {
Ok(Some(value)) => Some(Ok((key.clone(), value))),
Ok(None) => Some(Ok((key.clone(), Value::Null))),
Err(e) => Some(Err(e)),
}
}
Ok(None) => {
self.current = None;
None
}
Err(e) => Some(Err(e)),
}
} else {
None
}
} else if let Some(nitrite_map_iterator) = &mut self.nitrite_map_iterator {
let next = nitrite_map_iterator.next_back();
if let Some(Ok((key, _))) = &next {
self.current = Some(key.clone());
}
next
} else if let Some(map) = &self.cached_index_map {
let next_key = self.lower_key(map);
match next_key {
Ok(Some(key)) => {
self.current = Some(key.clone());
match map.inner.get(&key) {
Ok(Some(value)) => Some(Ok((key, value))),
Ok(None) => Some(Ok((key, Value::Null))),
Err(e) => Some(Err(e)),
}
}
Ok(None) => {
self.current = None;
None
}
Err(e) => Some(Err(e)),
}
} else {
None
}
}
}
impl DoubleEndedIterator for IndexMapIterator {
fn next_back(&mut self) -> Option<Self::Item> {
if self.composite_iterator.is_some() {
return self.next_composite();
}
if !self.reverse_scan {
if let Some(nitrite_map_iterator) = &mut self.nitrite_map_iterator {
let next = nitrite_map_iterator.next_back();
if let Some(Ok((key, _))) = &next {
self.current = Some(key.clone());
}
next
} else if let Some(map) = &self.cached_index_map {
let next_key = self.lower_key(map);
match next_key {
Ok(Some(key)) => {
self.current = Some(key.clone());
match map.inner.get(&key) {
Ok(Some(value)) => Some(Ok((key, value))),
Ok(None) => Some(Ok((key, Value::Null))),
Err(e) => Some(Err(e)),
}
}
Ok(None) => {
self.current = None;
None
}
Err(e) => Some(Err(e)),
}
} else {
None
}
} else if let Some(nitrite_map_iterator) = &mut self.nitrite_map_iterator {
let next = nitrite_map_iterator.next();
if let Some(Ok((key, _))) = &next {
self.current = Some(key.clone());
}
next
} else if let Some(map) = &self.cached_index_map {
let next_key = self.higher_key(map);
match next_key {
Ok(Some(key)) => {
self.current = Some(key.clone());
match map.inner.get(&key) {
Ok(Some(value)) => Some(Ok((key, value))),
Ok(None) => Some(Ok((key, Value::Null))),
Err(e) => Some(Err(e)),
}
}
Ok(None) => {
self.current = None;
None
}
Err(e) => Some(Err(e)),
}
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::collection::NitriteId;
use crate::common::{Key, Value};
use crate::store::{NitriteStore, NitriteStoreProvider};
use std::collections::BTreeMap;
#[test]
fn test_index_map_new() {
let store = NitriteStore::default();
let map = store.open_map("test").expect("Failed to open map");
let nitrite_map = Some(map);
let sub_map = Some(BTreeMap::new());
let index_map = IndexMap::new(nitrite_map.clone(), sub_map.clone());
assert!(index_map.inner.nitrite_map.is_some());
assert!(index_map.inner.sub_map.is_some());
}
#[test]
fn test_index_map_get() {
let mut sub_map = BTreeMap::new();
sub_map.insert(Value::String("key1".to_string()), Value::String("value1".to_string()));
let index_map = IndexMap::new(None, Some(sub_map));
let key = Key::String("key1".to_string());
let result = index_map.get(&key).unwrap();
assert_eq!(result, Some(Value::String("value1".to_string())));
}
#[test]
fn test_index_map_get_not_found() {
let sub_map = BTreeMap::new();
let index_map = IndexMap::new(None, Some(sub_map));
let key = Key::String("key1".to_string());
let result = index_map.get(&key).unwrap();
assert_eq!(result, None);
}
#[test]
fn test_index_map_first_key() {
let mut sub_map = BTreeMap::new();
sub_map.insert(Value::String("key1".to_string()), Value::String("value1".to_string()));
let index_map = IndexMap::new(None, Some(sub_map));
let result = index_map.first_key().unwrap();
assert_eq!(result, Some(Key::String("key1".to_string())));
}
#[test]
fn test_index_map_last_key() {
let mut sub_map = BTreeMap::new();
sub_map.insert(Value::String("key1".to_string()), Value::String("value1".to_string()));
let index_map = IndexMap::new(None, Some(sub_map));
let result = index_map.last_key().unwrap();
assert_eq!(result, Some(Key::String("key1".to_string())));
}
#[test]
fn test_index_map_higher_key() {
let mut sub_map = BTreeMap::new();
sub_map.insert(Value::String("key1".to_string()), Value::String("value1".to_string()));
sub_map.insert(Value::String("key2".to_string()), Value::String("value2".to_string()));
let index_map = IndexMap::new(None, Some(sub_map));
let key = Key::String("key1".to_string());
let result = index_map.higher_key(&key).unwrap();
assert_eq!(result, Some(Key::String("key2".to_string())));
}
#[test]
fn test_index_map_ceiling_key() {
let mut sub_map = BTreeMap::new();
sub_map.insert(Value::String("key1".to_string()), Value::String("value1".to_string()));
sub_map.insert(Value::String("key2".to_string()), Value::String("value2".to_string()));
let index_map = IndexMap::new(None, Some(sub_map));
let key = Key::String("key1".to_string());
let result = index_map.ceiling_key(&key).unwrap();
assert_eq!(result, Some(Key::String("key1".to_string())));
}
#[test]
fn test_index_map_lower_key() {
let mut sub_map = BTreeMap::new();
sub_map.insert(Value::String("key1".to_string()), Value::String("value1".to_string()));
sub_map.insert(Value::String("key2".to_string()), Value::String("value2".to_string()));
let index_map = IndexMap::new(None, Some(sub_map));
let key = Key::String("key2".to_string());
let result = index_map.lower_key(&key).unwrap();
assert_eq!(result, Some(Key::String("key1".to_string())));
}
#[test]
fn test_index_map_floor_key() {
let mut sub_map = BTreeMap::new();
sub_map.insert(Value::String("key1".to_string()), Value::String("value1".to_string()));
sub_map.insert(Value::String("key2".to_string()), Value::String("value2".to_string()));
let index_map = IndexMap::new(None, Some(sub_map));
let key = Key::String("key2".to_string());
let result = index_map.floor_key(&key).unwrap();
assert_eq!(result, Some(Key::String("key2".to_string())));
}
#[test]
fn test_index_map_entries() {
let mut sub_map = BTreeMap::new();
sub_map.insert(Value::String("key1".to_string()), Value::String("value1".to_string()));
let index_map = IndexMap::new(None, Some(sub_map));
let mut entries = index_map.entries().unwrap();
let entry = entries.next().unwrap().unwrap();
assert_eq!(entry.0, Key::String("key1".to_string()));
assert_eq!(entry.1, Value::String("value1".to_string()));
}
#[test]
fn test_index_map_terminal_nitrite_ids() {
let mut sub_map = BTreeMap::new();
sub_map.insert(Value::String("key1".to_string()), Value::Array(vec![Value::NitriteId(NitriteId::new())]));
let index_map = IndexMap::new(None, Some(sub_map));
let result = index_map.terminal_nitrite_ids().unwrap();
assert_eq!(result.len(), 1);
}
#[test]
fn test_index_map_terminal_nitrite_ids_empty() {
let sub_map = BTreeMap::new();
let index_map = IndexMap::new(None, Some(sub_map));
let result = index_map.terminal_nitrite_ids().unwrap();
assert_eq!(result.len(), 0);
}
#[test]
fn test_index_map_iterator_forward_scan_with_cached_map() {
let mut sub_map = BTreeMap::new();
sub_map.insert(Value::String("key1".to_string()), Value::String("value1".to_string()));
sub_map.insert(Value::String("key2".to_string()), Value::String("value2".to_string()));
sub_map.insert(Value::String("key3".to_string()), Value::String("value3".to_string()));
let index_map = IndexMap::new(None, Some(sub_map));
let mut entries = index_map.entries().unwrap();
let entry1 = entries.next().unwrap().unwrap();
assert_eq!(entry1.0, Key::String("key1".to_string()));
assert_eq!(entry1.1, Value::String("value1".to_string()));
let entry2 = entries.next().unwrap().unwrap();
assert_eq!(entry2.0, Key::String("key2".to_string()));
assert_eq!(entry2.1, Value::String("value2".to_string()));
let entry3 = entries.next().unwrap().unwrap();
assert_eq!(entry3.0, Key::String("key3".to_string()));
assert_eq!(entry3.1, Value::String("value3".to_string()));
}
#[test]
fn test_index_map_iterator_reverse_scan_with_cached_map() {
let mut sub_map = BTreeMap::new();
sub_map.insert(Value::String("key1".to_string()), Value::String("value1".to_string()));
sub_map.insert(Value::String("key2".to_string()), Value::String("value2".to_string()));
let index_map = IndexMap::new(None, Some(sub_map));
let mut entries = index_map.entries().unwrap();
let entry_back = entries.next_back().unwrap().unwrap();
assert_eq!(entry_back.0, Key::String("key2".to_string()));
assert_eq!(entry_back.1, Value::String("value2".to_string()));
}
#[test]
fn test_index_map_in_memory_get_none_handling() {
let sub_map = BTreeMap::new();
let index_map = IndexMap::new(None, Some(sub_map));
let key = Key::String("nonexistent".to_string());
let result = index_map.get(&key);
assert!(result.is_ok());
assert_eq!(result.unwrap(), None);
}
#[test]
fn test_index_map_null_value_handling() {
let mut sub_map = BTreeMap::new();
sub_map.insert(Value::String("key1".to_string()), Value::Null);
let index_map = IndexMap::new(None, Some(sub_map));
let key = Key::String("key1".to_string());
let result = index_map.get(&key).unwrap();
assert_eq!(result, Some(Value::Null));
}
#[test]
fn test_index_map_iterator_with_null_values() {
let mut sub_map = BTreeMap::new();
sub_map.insert(Value::String("key1".to_string()), Value::Null);
sub_map.insert(Value::String("key2".to_string()), Value::String("value2".to_string()));
let index_map = IndexMap::new(None, Some(sub_map));
let mut entries = index_map.entries().unwrap();
let entry1 = entries.next().unwrap().unwrap();
assert_eq!(entry1.0, Key::String("key1".to_string()));
assert_eq!(entry1.1, Value::Null);
let entry2 = entries.next().unwrap().unwrap();
assert_eq!(entry2.0, Key::String("key2".to_string()));
assert_eq!(entry2.1, Value::String("value2".to_string()));
}
#[test]
fn test_index_map_iterator_forward_scan_efficiency() {
let mut sub_map = BTreeMap::new();
for i in 0..10 {
sub_map.insert(
Value::String(format!("key{}", i)),
Value::String(format!("value{}", i))
);
}
let index_map = IndexMap::new(None, Some(sub_map));
let entries = index_map.entries().unwrap();
let mut count = 0;
for result in entries {
assert!(result.is_ok());
count += 1;
}
assert_eq!(count, 10);
}
#[test]
fn test_index_map_iterator_reverse_scan_efficiency() {
let mut sub_map = BTreeMap::new();
for i in 0..10 {
sub_map.insert(
Value::String(format!("key{}", i)),
Value::String(format!("value{}", i))
);
}
let index_map = IndexMap::new(None, Some(sub_map));
index_map.set_reverse_scan(true);
let entries = index_map.entries().unwrap();
let mut count = 0;
for result in entries {
assert!(result.is_ok());
count += 1;
}
assert_eq!(count, 10);
}
#[test]
fn test_index_map_iterator_double_ended_forward() {
let mut sub_map = BTreeMap::new();
for i in 0..5 {
sub_map.insert(
Value::String(format!("key{}", i)),
Value::String(format!("value{}", i))
);
}
let index_map = IndexMap::new(None, Some(sub_map));
let mut entries = index_map.entries().unwrap();
if let Some(result) = entries.next() {
let first = result.unwrap();
assert_eq!(first.0, Key::String("key0".to_string()));
}
if let Some(result) = entries.next_back() {
let last = result.unwrap();
assert_eq!(last.0, Key::String("key4".to_string()));
}
}
#[test]
fn test_index_map_terminal_nitrite_ids_performance() {
let mut sub_map = BTreeMap::new();
let ids: Vec<Value> = (0..100)
.map(|_| Value::NitriteId(NitriteId::new()))
.collect();
sub_map.insert(Value::String("key1".to_string()), Value::Array(ids.clone()));
sub_map.insert(Value::String("key2".to_string()), Value::Array(ids.clone()));
let index_map = IndexMap::new(None, Some(sub_map));
let result = index_map.terminal_nitrite_ids().unwrap();
assert!(!result.is_empty());
}
#[test]
fn test_index_map_entries_no_unwrap_errors() {
let mut sub_map = BTreeMap::new();
sub_map.insert(Value::String("key1".to_string()), Value::Null);
sub_map.insert(Value::String("key2".to_string()), Value::Array(vec![]));
sub_map.insert(Value::String("key3".to_string()), Value::String("value".to_string()));
let index_map = IndexMap::new(None, Some(sub_map));
let mut entries = index_map.entries().unwrap();
let count = entries.by_ref().filter(|e| e.is_ok()).count();
assert_eq!(count, 3);
}
}