use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use zeroize::{Zeroize, ZeroizeOnDrop};
use uuid::Uuid;
use crate::crypto::{aead, primitives::Epoch};
use crate::crypto::kdf::MasterKeySet;
use crate::error::VaultError;
#[derive(Clone, Debug, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
pub struct KeywordState {
pub live_indices: Vec<u64>,
#[zeroize(skip)]
pub index_to_doc: HashMap<u64, [u8; 16]>,
pub total_writes: u64,
pub epoch: Epoch,
}
impl KeywordState {
pub fn new() -> Self {
Self {
live_indices: Vec::new(),
index_to_doc: HashMap::new(),
total_writes: 0,
epoch: 0,
}
}
pub fn next_index(&mut self) -> u64 {
let idx = self.total_writes;
self.total_writes += 1;
idx
}
pub fn record_add(&mut self, index: u64, doc_id: Uuid) {
let bytes: [u8; 16] = *doc_id.as_bytes();
self.live_indices.push(index);
self.index_to_doc.insert(index, bytes);
}
pub fn evict_doc(&mut self, doc_id: Uuid) -> bool {
let target: [u8; 16] = *doc_id.as_bytes();
let before = self.live_indices.len();
self.live_indices.retain(|idx| {
self.index_to_doc.get(idx) != Some(&target)
});
self.index_to_doc.retain(|_, v| v != &target);
if self.live_indices.len() < before {
self.epoch += 1;
true
} else {
false
}
}
}
impl Default for KeywordState {
fn default() -> Self { Self::new() }
}
pub struct ClientStateTable {
inner: HashMap<[u8; 32], KeywordState>,
}
impl ClientStateTable {
pub fn new() -> Self {
Self { inner: HashMap::new() }
}
pub fn get_or_create(&mut self, keyword_hash: [u8; 32]) -> &mut KeywordState {
self.inner.entry(keyword_hash).or_default()
}
pub fn get(&self, keyword_hash: &[u8; 32]) -> Option<&KeywordState> {
self.inner.get(keyword_hash)
}
pub fn export_encrypted(&self, keys: &MasterKeySet) -> Result<Vec<u8>, VaultError> {
let plaintext = bincode::serialize(&self.inner)?;
let enc = aead::encrypt(keys.k_state.as_bytes(), &plaintext)?;
Ok(enc.0)
}
pub fn import_encrypted(blob: &[u8], keys: &MasterKeySet) -> Result<Self, VaultError> {
use crate::crypto::primitives::EncValue;
let plaintext = aead::decrypt(keys.k_state.as_bytes(), &EncValue(blob.to_vec()))?;
let inner: HashMap<[u8; 32], KeywordState> = bincode::deserialize(&plaintext)?;
Ok(Self { inner })
}
}
impl Default for ClientStateTable {
fn default() -> Self { Self::new() }
}