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,
}
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 }
}
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 start_id = self.extra_next_id;
let mut done = 0;
let mut todo = len;
let mut em = VBuckMap::restore(self.extra, ps);
let mut id = self.extra_next_id;
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 code : u8 = if len as u64 <= u16::MAX as u64 { 2 }
else if len as u64 <= u32::MAX as u64 { 3 }
else {4};
x.push( code );
x.extend_from_slice( &start_id.to_le_bytes() );
match code {
2 => x.extend_from_slice( &(len as u16).to_le_bytes() ),
3 => x.extend_from_slice( &(len as u32).to_le_bytes() ),
4 => x.extend_from_slice( &(len as u64).to_le_bytes() ),
_ => panic!()
}
}
let mut m = VBuckMap::restore(self.main, ps);
let key = StoreKey{ key, store: self };
m.insert( &key, &x );
self.main = m.save();
}
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
}
fn fetch_chunks(&self, x: &[u8], ps: &mut PageSet) -> LVec<u8>
{
let code = x[0];
let mut 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,
3 => u32::from_le_bytes( x[9..13].try_into().unwrap() ) as usize,
4 => u64::from_le_bytes( x[9..17].try_into().unwrap() ) as usize,
_ => panic!(),
};
let mut done = 0;
let mut em = VBuckMap::restore(self.extra, ps);
let mut result = LVec::new();
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
}
}
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.fetch_chunks( bytes, ps );
let result = self.key.ok( &v, ps );
result
}
}
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.fetch_chunks( bytes, 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!( 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'; 256 ];
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 - everything is ok");
}