use crate::*;
use std::hash::{Hash, Hasher};
pub enum SData {
Small(PData, usize, usize),
Large(LVec<u8>),
}
impl SData {
pub fn data(&mut self) -> &[u8] {
match self {
SData::Small(pdata, off, len) => &pdata.data[*off..*off + *len],
SData::Large(v) => v,
}
}
pub fn drop(&mut self, ps: &mut PageSet) {
match self {
SData::Small(pdata, _, _) => ps.note(std::mem::take(pdata)),
SData::Large(_v) => {}
}
}
}
#[derive(Debug)]
pub struct Store {
main: VBuckMapInfo,
extra: VBuckMapInfo,
extra_next_id: u64,
record_count: u64,
delete_balance: u64, }
impl Store {
pub fn new(ps: &mut PageSet) -> Self {
let main = VBuckMap::new(1, ps).save();
let extra = VBuckMap::new(1, ps).save();
Self {
main,
extra,
extra_next_id: 0,
record_count: 0,
delete_balance: 0,
}
}
pub fn insert<K: VKey>(&mut self, key: &K, user_data: &[u8], ps: &mut PageSet) {
let len = user_data.len();
let mut x = LVec::<u8>::new();
if len < 255 {
x.push(1); x.extend_from_slice(user_data);
} else
{
let mut done = 0;
let mut id = self.extra_next_id;
let code: u8 = if len as u64 <= u16::MAX as u64 {
2
} else if len as u64 <= u32::MAX as u64 {
4
} else {
8
};
x.push(code);
x.extend_from_slice(&id.to_le_bytes());
match code {
2 => x.extend_from_slice(&(len as u16).to_le_bytes()),
4 => x.extend_from_slice(&(len as u32).to_le_bytes()),
8 => x.extend_from_slice(&(len as u64).to_le_bytes()),
_ => panic!(),
}
if let Some(klen) = key.len() {
assert!(klen <= len);
if 1 + 8 + code as usize + klen < 256 {
x.extend_from_slice(&user_data[0..klen]);
assert!(x.len() < 256);
done += klen;
}
}
let mut em = VBuckMap::restore(self.extra, ps);
let mut todo = len - done;
while todo > 0
{
let mut amount = 247; if amount > todo {
amount = todo;
}
let chunk = &user_data[done..done + amount];
let mut t = LVec::new();
t.extend_from_slice(&id.to_le_bytes());
t.extend_from_slice(chunk);
let key = IdVKey { id };
em.insert(&key, &t);
id += 1;
todo -= amount;
done += amount;
}
self.extra = em.save();
self.extra_next_id = id;
}
let mut m = VBuckMap::restore(self.main, ps);
let key = StoreKey { key, store: self };
m.insert(&key, &x);
self.main = m.save();
self.record_count += 1;
if self.delete_balance > 0 { self.delete_balance -= 1; }
}
pub fn get<K: VKey>(&mut self, key: &K, ps: &mut PageSet) -> Option<SData> {
let mut m = VBuckMap::restore(self.main, ps);
let key = StoreKey { key, store: self };
if let Some((pdata, off, len)) = m.get(&key) {
if pdata.data[off] == 1 {
return Some(SData::Small(pdata, off + 1, len - 1));
} else {
let v = self.fetch_chunks(&pdata.data[off..off + len], ps);
ps.note(pdata);
return Some(SData::Large(v));
}
}
None
}
pub fn remove<K: VKey>(&mut self, key: &K, ps: &mut PageSet) -> bool {
let mut m = VBuckMap::restore(self.main, ps);
let got = {
let key = StoreKey { key, store: self };
m.get(&key)
};
if let Some((pdata, off, len)) = got {
if pdata.data[off] != 1 {
self.remove_chunks(&pdata.data[off..off + len], m.ps);
}
m.ps.note(pdata);
let key = StoreKey { key, store: self };
m.remove(&key);
self.main = m.save(); self.record_count -= 1;
self.delete_balance += 1;
true
} else {
false
}
}
pub fn iter<'a>(&'a mut self, ps: &mut PageSet) -> StoreIter<'a> {
let mut m = VBuckMap::restore(self.main, ps);
let inner = m.iter();
StoreIter {
inner,
store: self,
v: LVec::new(),
}
}
fn remove_chunks(&mut self, x: &[u8], ps: &mut PageSet) {
let (mut id, len, local) = self.parse_x(x);
let mut em = VBuckMap::restore(self.extra, ps);
let mut done = 0;
while done < len - local {
let key = IdVKey { id };
let amt = em.remove(&key);
assert!(amt > 0);
done += amt;
id += 1;
}
self.extra = em.save(); }
fn parse_x(&self, x: &[u8]) -> (u64, usize, usize) {
let code = x[0];
let id = u64::from_le_bytes(x[1..9].try_into().unwrap());
let len = match code {
2 => u16::from_le_bytes(x[9..11].try_into().unwrap()) as usize,
4 => u32::from_le_bytes(x[9..13].try_into().unwrap()) as usize,
8 => u64::from_le_bytes(x[9..17].try_into().unwrap()) as usize,
_ => panic!(),
};
let over = 1 + 8 + code as usize; let local = x.len() - over; (id, len, local)
}
fn fetch_chunks(&self, x: &[u8], ps: &mut PageSet) -> LVec<u8> {
let (_, len, _) = self.parse_x(x);
self.chunks(x, len, ps)
}
fn some_chunks(&self, x: &[u8], len: Option<usize>, ps: &mut PageSet) -> LVec<u8> {
if let Some(len) = len {
self.chunks(x, len, ps)
} else {
self.fetch_chunks(x, ps)
}
}
fn chunks(&self, x: &[u8], len: usize, ps: &mut PageSet) -> LVec<u8> {
let mut done = 0;
let mut result = LVec::new();
let code = x[0];
let over = 1 + 8 + code as usize; let local = x.len() - over; if local > 0 {
result.extend_from_slice(&x[over..over + local]);
done += local;
}
if done < len {
let mut id = u64::from_le_bytes(x[1..9].try_into().unwrap());
let mut em = VBuckMap::restore(self.extra, ps);
while done < len {
let key = IdVKey { id };
let (rdata, off, amt) = em.get(&key).unwrap();
let (off, amt) = (off + 8, amt - 8); result.extend_from_slice(&rdata.data[off..off + amt]);
em.ps.note(rdata);
done += amt;
id += 1;
}
}
result
}
}
pub struct StoreIter<'a> {
inner: VBuckMapIter,
store: &'a Store,
v: LVec<u8>,
}
impl<'a> StoreIter<'a> {
pub fn next(&mut self, ps: &mut PageSet) -> Option<&[u8]> {
if let Some(data) = self.inner.next(ps) {
let result = if data[0] == 1 {
&data[1..]
} else {
self.v = self.store.fetch_chunks(data, ps);
&self.v
};
Some(result)
} else {
None
}
}
pub fn drop(&mut self, ps: &mut PageSet) {
self.inner.drop(ps);
}
}
struct StoreKey<'a, K: VKey> {
key: &'a K,
store: &'a Store,
}
impl<'a, K: VKey> VKey for StoreKey<'a, K> {
fn ok(&self, bytes: &[u8], ps: &mut PageSet) -> bool {
if bytes[0] == 1 {
self.key.ok(&bytes[1..], ps)
} else {
let v = self.store.some_chunks(bytes, self.key.len(), ps);
self.key.ok(&v, ps)
}
}
fn rehash<H: Hasher>(&self, bytes: &[u8], h: &mut H, ps: &mut PageSet) {
if bytes[0] == 1 {
self.key.rehash(&bytes[1..], h, ps);
} else {
let v = self.store.some_chunks(bytes, self.key.len(), ps);
self.key.rehash(&v, h, ps);
}
}
}
impl<'a, K: VKey> Hash for StoreKey<'a, K> {
fn hash<H>(&self, h: &mut H)
where
H: Hasher,
{
self.key.hash(h);
}
}
#[cfg(test)]
fn test_insert(m: &mut Store, dt: &DataType, v: &Value, id: u64, ps: &mut PageSet) {
let td = dt.value_to_bytes(v);
let mut x = Vec::new();
x.extend_from_slice(&id.to_le_bytes());
x.extend_from_slice(&td);
let key = IdVKey { id };
m.insert(&key, &x, ps);
if let Some(mut sd) = m.get(&key, ps) {
assert_eq!(sd.data(), &x);
sd.drop(ps);
} else {
panic!()
}
}
#[cfg(test)]
fn test_get(m: &mut Store, dt: &DataType, v: &Value, id: u64, ps: &mut PageSet) {
let td = dt.value_to_bytes(v);
let mut x = Vec::new();
x.extend_from_slice(&id.to_le_bytes());
x.extend_from_slice(&td);
let key = IdVKey { id };
if let Some(mut sd) = m.get(&key, ps) {
assert!(sd.data() == &x);
sd.drop(ps);
} else {
panic!()
}
}
#[cfg(test)]
fn cust_dt() -> DataType {
use pstd::veca;
DataType::Struct(veca![
(LString::from("Name"), DataType::String),
(LString::from("Email"), DataType::String),
(LString::from("Postal"), DataType::String),
])
}
#[cfg(test)]
pub fn test_store(ps: &mut PageSet) {
use pstd::veca;
let mut m = Store::new(ps);
let n = 256 * 256;
println!("testing store n={}", n);
let _big = vec![b'a'; 250];
let v = Value::List(veca![
Value::String(LString::from("maz")),
Value::String(LString::from("maz@gmail.com")),
Value::String(LString::from(tos(&_big))),
]);
println!("test value={:?}", v);
let dt = cust_dt();
println!("testing insert store={:?}", m);
for i in 0..n {
test_insert(&mut m, &dt, &v, i, ps);
}
println!("testing get store={:?}", m);
for i in 0..n {
test_get(&mut m, &dt, &v, i, ps);
}
println!("testing store went ok");
let start = std::time::Instant::now();
let mut iter = m.iter(ps);
let mut count = 0;
while let Some(_r) = iter.next(ps) {
count += 1;
}
iter.drop(ps);
assert_eq!(count, n);
println!(
"time to iterate over {} records = {} micro-sec",
n,
start.elapsed().as_micros()
);
println!("testing remove");
for id in 0..n {
let key = IdVKey { id };
m.remove(&key, ps);
}
println!("testing store - everything is ok, m={:?}", m);
}