use rapidhash::{HashMapExt, RapidHashMap as HashMap};
use crate::{
api::hash::{
CachedFieldState,
hfe::{
commit_hash_batch, extract_subkey_payload, get_live_hfe_meta, load_field_state,
purge_expired_physical_field, remove_field_in_batch,
},
meta::{HashFieldStateKind, HashItemKeyComposer, compose_hash_meta_key},
},
engine::Engine,
error::{Error, Result},
meta::current_now_ms,
wedb::Db,
};
impl<E: Engine> Db<E>
where
Error: From<E::Error>,
{
#[inline]
pub fn hgetdel_one<K: AsRef<[u8]>, F: AsRef<[u8]>>(
&self,
key: K,
field: F,
) -> Result<Option<Vec<u8>>> {
let key_bytes = key.as_ref();
let kc = self.kc();
let meta_k = compose_hash_meta_key(&kc, key_bytes);
let now_ms = current_now_ms();
let mut meta = match get_live_hfe_meta(self, key_bytes, &meta_k, now_ms)? {
Some(m) => m,
None => return Ok(None),
};
let f_bytes = field.as_ref();
let mut composer = HashItemKeyComposer::new(&kc, key_bytes);
let item_k = composer.key_for_field(f_bytes);
let entry = load_field_state(self.data(), &meta, item_k, now_ms)?;
match entry.kind {
HashFieldStateKind::Missing => Ok(None),
HashFieldStateKind::ExpiredTTLPhysical => {
purge_expired_physical_field(&meta_k, &mut meta, item_k, self.batch_with_capacity(2))?;
Ok(None)
}
HashFieldStateKind::Persistent | HashFieldStateKind::LiveTTL => {
let payload = extract_subkey_payload(&meta, entry.raw.as_deref()).to_vec();
let mut batch = self.batch_with_capacity(2);
remove_field_in_batch(&mut meta, item_k, entry.kind, &mut batch);
commit_hash_batch(&meta_k, &mut meta, batch)?;
Ok(Some(payload))
}
}
}
#[inline]
pub fn hgetdel<K: AsRef<[u8]>, F: AsRef<[u8]>>(
&self,
key: K,
fields: &[F],
) -> Result<Vec<Option<Vec<u8>>>> {
if fields.is_empty() {
return Ok(Vec::new());
}
if fields.len() == 1 {
return Ok(vec![self.hgetdel_one(key, &fields[0])?]);
}
let key_bytes = key.as_ref();
let kc = self.kc();
let meta_k = compose_hash_meta_key(&kc, key_bytes);
let now_ms = current_now_ms();
let mut meta = match get_live_hfe_meta(self, key_bytes, &meta_k, now_ms)? {
Some(m) => m,
None => return Ok(vec![None; fields.len()]),
};
let mut results = Vec::with_capacity(fields.len());
let mut batch = self.batch();
let mut meta_changed = false;
let mut state_cache: HashMap<&[u8], CachedFieldState> = HashMap::with_capacity(fields.len());
let data_ks = self.data();
let mut composer = HashItemKeyComposer::new(&kc, key_bytes);
for f in fields {
let f_bytes = f.as_ref();
let item_k = composer.key_for_field(f_bytes);
let entry = if let Some(cached) = state_cache.get(f_bytes) {
cached.clone()
} else {
let state_entry = load_field_state(data_ks, &meta, item_k, now_ms)?;
state_cache.insert(f_bytes, state_entry.clone());
state_entry
};
match entry.kind {
HashFieldStateKind::Missing => {
results.push(None);
}
HashFieldStateKind::Persistent | HashFieldStateKind::LiveTTL => {
let payload = extract_subkey_payload(&meta, entry.raw.as_deref()).to_vec();
results.push(Some(payload));
remove_field_in_batch(&mut meta, item_k, entry.kind, &mut batch);
meta_changed = true;
state_cache.insert(
f_bytes,
CachedFieldState {
kind: HashFieldStateKind::Missing,
expire: 0,
raw: None,
},
);
}
HashFieldStateKind::ExpiredTTLPhysical => {
batch.rm_data(item_k);
meta.apply_ttl_to_deleted();
meta_changed = true;
state_cache.insert(
f_bytes,
CachedFieldState {
kind: HashFieldStateKind::Missing,
expire: 0,
raw: None,
},
);
results.push(None);
}
}
}
if meta_changed {
commit_hash_batch(&meta_k, &mut meta, batch)?;
}
Ok(results)
}
}