#![cfg_attr(feature="cargo-clippy", allow(doc_markdown))]
use byteorder::{BigEndian, ByteOrder};
use std::borrow::Cow;
use std::cell::Cell;
use std::marker::PhantomData;
use crypto::{hash, CryptoHash, Hash};
use super::{BaseIndex, BaseIndexIter, Snapshot, Fork, StorageValue};
use super::indexes_metadata::IndexType;
#[derive(Debug, Default, Clone, Copy)]
struct SparseListSize {
capacity: u64,
length: u64,
}
impl SparseListSize {
fn to_array(&self) -> [u8; 16] {
let mut buf = [0; 16];
BigEndian::write_u64(&mut buf[0..8], self.capacity);
BigEndian::write_u64(&mut buf[8..16], self.length);
buf
}
}
impl CryptoHash for SparseListSize {
fn hash(&self) -> Hash {
hash(&self.to_array())
}
}
impl StorageValue for SparseListSize {
fn into_bytes(self) -> Vec<u8> {
self.to_array().to_vec()
}
fn from_bytes(value: Cow<[u8]>) -> Self {
let buf = value.as_ref();
let capacity = BigEndian::read_u64(&buf[0..8]);
let length = BigEndian::read_u64(&buf[8..16]);
SparseListSize { capacity, length }
}
}
#[derive(Debug)]
pub struct SparseListIndex<T, V> {
base: BaseIndex<T>,
size: Cell<Option<SparseListSize>>,
_v: PhantomData<V>,
}
#[derive(Debug)]
pub struct SparseListIndexIter<'a, V> {
base_iter: BaseIndexIter<'a, u64, V>,
}
#[derive(Debug)]
pub struct SparseListIndexKeys<'a> {
base_iter: BaseIndexIter<'a, u64, ()>,
}
#[derive(Debug)]
pub struct SparseListIndexValues<'a, V> {
base_iter: BaseIndexIter<'a, (), V>,
}
impl<T, V> SparseListIndex<T, V>
where
T: AsRef<Snapshot>,
V: StorageValue,
{
pub fn new<S: AsRef<str>>(name: S, view: T) -> Self {
SparseListIndex {
base: BaseIndex::new(name, IndexType::SparseList, view),
size: Cell::new(None),
_v: PhantomData,
}
}
pub fn with_prefix<S: AsRef<str>>(name: S, prefix: Vec<u8>, view: T) -> Self {
SparseListIndex {
base: BaseIndex::with_prefix(name, prefix, IndexType::SparseList, view),
size: Cell::new(None),
_v: PhantomData,
}
}
fn size(&self) -> SparseListSize {
if let Some(size) = self.size.get() {
return size;
}
let size = self.base.get(&()).unwrap_or_default();
self.size.set(Some(size));
size
}
pub fn get(&self, index: u64) -> Option<V> {
self.base.get(&index)
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn capacity(&self) -> u64 {
self.size().capacity
}
pub fn len(&self) -> u64 {
self.size().length
}
pub fn iter(&self) -> SparseListIndexIter<V> {
SparseListIndexIter { base_iter: self.base.iter_from(&(), &0u64) }
}
pub fn indices(&self) -> SparseListIndexKeys {
SparseListIndexKeys { base_iter: self.base.iter_from(&(), &0u64) }
}
pub fn values(&self) -> SparseListIndexValues<V> {
SparseListIndexValues { base_iter: self.base.iter_from(&(), &0u64) }
}
pub fn iter_from(&self, from: u64) -> SparseListIndexIter<V> {
SparseListIndexIter { base_iter: self.base.iter_from(&(), &from) }
}
}
impl<'a, V> SparseListIndex<&'a mut Fork, V>
where
V: StorageValue,
{
fn set_size(&mut self, size: SparseListSize) {
self.base.put(&(), size);
self.size.set(Some(size));
}
pub fn push(&mut self, value: V) {
let mut size = self.size();
self.base.put(&size.capacity, value);
size.capacity += 1;
size.length += 1;
self.set_size(size);
}
pub fn remove(&mut self, index: u64) -> Option<V> {
let mut size = self.size();
if index >= size.capacity {
return None;
}
let v = self.base.get(&index);
if v.is_some() {
self.base.remove(&index);
size.length -= 1;
self.set_size(size);
}
v
}
pub fn extend<I>(&mut self, iter: I)
where
I: IntoIterator<Item = V>,
{
let mut size = self.size();
for value in iter {
self.base.put(&size.capacity, value);
size.capacity += 1;
size.length += 1;
}
self.set_size(size);
}
pub fn set(&mut self, index: u64, value: V) -> Option<V> {
let mut size = self.size();
let old_value = self.base.get::<u64, V>(&index);
if old_value.is_none() {
size.length += 1;
if index >= size.capacity {
size.capacity = index + 1;
}
self.set_size(size);
}
self.base.put(&index, value);
old_value
}
pub fn clear(&mut self) {
self.size.set(Some(SparseListSize::default()));
self.base.clear()
}
pub fn pop(&mut self) -> Option<V> {
let first_item = {
self.iter().next()
};
if let Some((first_index, first_elem)) = first_item {
let mut size = self.size();
self.base.remove(&first_index);
size.length -= 1;
self.set_size(size);
return Some(first_elem);
}
None
}
}
impl<'a, T, V> ::std::iter::IntoIterator for &'a SparseListIndex<T, V>
where
T: AsRef<Snapshot>,
V: StorageValue,
{
type Item = (u64, V);
type IntoIter = SparseListIndexIter<'a, V>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a, V> Iterator for SparseListIndexIter<'a, V>
where
V: StorageValue,
{
type Item = (u64, V);
fn next(&mut self) -> Option<Self::Item> {
self.base_iter.next()
}
}
impl<'a> Iterator for SparseListIndexKeys<'a> {
type Item = u64;
fn next(&mut self) -> Option<Self::Item> {
self.base_iter.next().map(|(k, ..)| k)
}
}
impl<'a, V> Iterator for SparseListIndexValues<'a, V>
where
V: StorageValue,
{
type Item = V;
fn next(&mut self) -> Option<Self::Item> {
self.base_iter.next().map(|(.., v)| v)
}
}
#[cfg(test)]
mod tests {
use rand::{thread_rng, Rng};
use super::SparseListIndex;
use storage::db::Database;
const IDX_NAME: &'static str = "idx_name";
fn gen_tempdir_name() -> String {
thread_rng().gen_ascii_chars().take(10).collect()
}
fn list_index_methods(db: Box<Database>) {
let mut fork = db.fork();
let mut list_index = SparseListIndex::new(IDX_NAME, &mut fork);
assert!(list_index.is_empty());
assert_eq!(0, list_index.capacity());
assert!(list_index.get(0).is_none());
let extended_by = vec![45, 3422, 234];
list_index.extend(extended_by);
assert!(!list_index.is_empty());
assert_eq!(Some(45), list_index.get(0));
assert_eq!(Some(3422), list_index.get(1));
assert_eq!(Some(234), list_index.get(2));
assert_eq!(3, list_index.capacity());
assert_eq!(3, list_index.len());
assert_eq!(Some(234), list_index.set(2, 777));
assert_eq!(Some(777), list_index.get(2));
assert_eq!(3, list_index.capacity());
assert_eq!(3, list_index.len());
let extended_by_again = vec![666, 999];
for el in &extended_by_again {
list_index.push(*el);
}
assert_eq!(Some(666), list_index.get(3));
assert_eq!(Some(999), list_index.get(4));
assert_eq!(5, list_index.capacity());
assert_eq!(5, list_index.len());
assert_eq!(Some(3422), list_index.remove(1));
assert_eq!(None, list_index.remove(1));
assert_eq!(5, list_index.capacity());
assert_eq!(4, list_index.len());
assert_eq!(Some(777), list_index.remove(2));
assert_eq!(5, list_index.capacity());
assert_eq!(3, list_index.len());
assert_eq!(Some(45), list_index.pop());
assert_eq!(5, list_index.capacity());
assert_eq!(2, list_index.len());
assert_eq!(Some(666), list_index.pop());
assert_eq!(5, list_index.capacity());
assert_eq!(1, list_index.len());
list_index.push(42);
assert_eq!(6, list_index.capacity());
assert_eq!(2, list_index.len());
assert_eq!(Some(999), list_index.pop());
assert_eq!(6, list_index.capacity());
assert_eq!(1, list_index.len());
assert_eq!(Some(42), list_index.pop());
assert_eq!(6, list_index.capacity());
assert_eq!(0, list_index.len());
assert_eq!(None, list_index.pop());
assert_eq!(None, list_index.set(42, 1024));
assert_eq!(43, list_index.capacity());
}
fn list_index_iter(db: Box<Database>) {
let mut fork = db.fork();
let mut list_index = SparseListIndex::new(IDX_NAME, &mut fork);
list_index.extend(vec![1u8, 15, 25, 2, 3]);
assert_eq!(
list_index.indices().collect::<Vec<u64>>(),
vec![0u64, 1, 2, 3, 4]
);
assert_eq!(
list_index.values().collect::<Vec<u8>>(),
vec![1u8, 15, 25, 2, 3]
);
list_index.remove(1);
list_index.remove(2);
assert_eq!(
list_index.iter().collect::<Vec<(u64, u8)>>(),
vec![(0u64, 1u8), (3u64, 2u8), (4u64, 3u8)]
);
assert_eq!(
list_index.iter_from(0).collect::<Vec<(u64, u8)>>(),
vec![(0u64, 1u8), (3u64, 2u8), (4u64, 3u8)]
);
assert_eq!(
list_index.iter_from(1).collect::<Vec<(u64, u8)>>(),
vec![(3u64, 2u8), (4u64, 3u8)]
);
assert_eq!(
list_index.iter_from(5).collect::<Vec<(u64, u8)>>(),
Vec::<(u64, u8)>::new()
);
assert_eq!(list_index.indices().collect::<Vec<u64>>(), vec![0u64, 3, 4]);
assert_eq!(list_index.values().collect::<Vec<u8>>(), vec![1u8, 2, 3]);
}
mod memorydb_tests {
use std::path::Path;
use tempdir::TempDir;
use storage::{Database, MemoryDB};
fn create_database(_: &Path) -> Box<Database> {
Box::new(MemoryDB::new())
}
#[test]
fn test_list_index_methods() {
let dir = TempDir::new(super::gen_tempdir_name().as_str()).unwrap();
let path = dir.path();
let db = create_database(path);
super::list_index_methods(db);
}
#[test]
fn test_list_index_iter() {
let dir = TempDir::new(super::gen_tempdir_name().as_str()).unwrap();
let path = dir.path();
let db = create_database(path);
super::list_index_iter(db);
}
}
mod rocksdb_tests {
use std::path::Path;
use tempdir::TempDir;
use storage::{Database, RocksDB, RocksDBOptions};
fn create_database(path: &Path) -> Box<Database> {
let mut opts = RocksDBOptions::default();
opts.create_if_missing(true);
Box::new(RocksDB::open(path, &opts).unwrap())
}
#[test]
fn test_list_index_methods() {
let dir = TempDir::new(super::gen_tempdir_name().as_str()).unwrap();
let path = dir.path();
let db = create_database(path);
super::list_index_methods(db);
}
#[test]
fn test_list_index_iter() {
let dir = TempDir::new(super::gen_tempdir_name().as_str()).unwrap();
let path = dir.path();
let db = create_database(path);
super::list_index_iter(db);
}
}
}