use std::io::Cursor;
use wbase::num::try_parse_i64;
use wdev::Device;
use wobject::hash::hash_object::{HashObject, HashOperation};
use super::{
super::storage_session::StorageSession,
common::{ObjState, RmwOutcome},
};
use crate::{
api::garnet_status::GarnetStatus, objects::types::object_output::ObjectOutput,
resp::parser::session_parse_state::strict_f64,
};
impl<'a, D: Device> StorageSession<'a, D> {
async fn hash_rmw<R>(
&self,
key: &[u8],
create: bool,
f: impl FnOnce(&mut HashObject) -> R,
) -> wkv::Result<RmwOutcome<R>> {
self
.rmw_object_store_operation(key, super::common::OBJ_TAG_HASH, |payload| {
let mut obj = match payload {
Some(bytes) => HashObject::deserialize(&mut Cursor::new(bytes)).unwrap_or_default(),
None if create => HashObject::new(),
None => return None,
};
let r = f(&mut obj);
let mut out = Vec::new();
obj.serialize(&mut out).ok()?;
Some((out, r))
})
.await
}
pub async fn hash_delete(
&self,
key: &[u8],
fields: &[&[u8]],
) -> wkv::Result<(GarnetStatus, i64)> {
match self
.hash_rmw(key, false, |obj| {
let mut n = 0i64;
for field in fields {
if obj.operate(HashOperation::HDEL, field, b"").is_some() {
n += 1;
}
}
(n, obj.hash.pin().is_empty())
})
.await?
{
RmwOutcome::WrongType => Ok((GarnetStatus::WrongType, 0)),
outcome => {
let n = self.finalize_removal(key, outcome, 0).await?;
Ok((GarnetStatus::Ok, n))
}
}
}
pub async fn hash_get(
&self,
key: &[u8],
field: &[u8],
) -> wkv::Result<(GarnetStatus, Option<Vec<u8>>)> {
match self.obj_load(key, super::common::OBJ_TAG_HASH).await? {
ObjState::Absent => Ok((GarnetStatus::NotFound, None)),
ObjState::WrongType => Ok((GarnetStatus::WrongType, None)),
ObjState::Present(payload) => {
let obj = HashObject::deserialize(&mut Cursor::new(payload)).unwrap_or_default();
Ok((
GarnetStatus::Ok,
obj.operate(HashOperation::HGET, field, b""),
))
}
}
}
pub async fn hash_get_multiple(
&self,
key: &[u8],
fields: &[&[u8]],
) -> wkv::Result<(GarnetStatus, Vec<Option<Vec<u8>>>)> {
match self.obj_load(key, super::common::OBJ_TAG_HASH).await? {
ObjState::Absent => Ok((
GarnetStatus::NotFound,
fields.iter().map(|_| None).collect(),
)),
ObjState::WrongType => Ok((GarnetStatus::WrongType, Vec::new())),
ObjState::Present(payload) => {
let obj = HashObject::deserialize(&mut Cursor::new(payload)).unwrap_or_default();
let pin = obj.hash.pin();
let mut out = Vec::with_capacity(fields.len());
for field in fields {
out.push(pin.get(*field).cloned());
}
Ok((GarnetStatus::Ok, out))
}
}
}
pub async fn hash_get_all(
&self,
key: &[u8],
) -> wkv::Result<(GarnetStatus, Vec<(Vec<u8>, Vec<u8>)>)> {
match self.obj_load(key, super::common::OBJ_TAG_HASH).await? {
ObjState::Absent => Ok((GarnetStatus::NotFound, Vec::new())),
ObjState::WrongType => Ok((GarnetStatus::WrongType, Vec::new())),
ObjState::Present(payload) => {
let obj = HashObject::deserialize(&mut Cursor::new(payload)).unwrap_or_default();
Ok((GarnetStatus::Ok, obj.hash_get_all()))
}
}
}
pub async fn hash_length(&self, key: &[u8]) -> wkv::Result<(GarnetStatus, usize)> {
match self.obj_load(key, super::common::OBJ_TAG_HASH).await? {
ObjState::Absent => Ok((GarnetStatus::NotFound, 0)),
ObjState::WrongType => Ok((GarnetStatus::WrongType, 0)),
ObjState::Present(payload) => {
let obj = HashObject::deserialize(&mut Cursor::new(payload)).unwrap_or_default();
Ok((GarnetStatus::Ok, obj.hash.pin().len()))
}
}
}
pub async fn hash_exists(&self, key: &[u8], field: &[u8]) -> wkv::Result<(GarnetStatus, bool)> {
match self.obj_load(key, super::common::OBJ_TAG_HASH).await? {
ObjState::Absent => Ok((GarnetStatus::NotFound, false)),
ObjState::WrongType => Ok((GarnetStatus::WrongType, false)),
ObjState::Present(payload) => {
let obj = HashObject::deserialize(&mut Cursor::new(payload)).unwrap_or_default();
Ok((GarnetStatus::Ok, obj.hash.pin().contains_key(field)))
}
}
}
pub async fn hash_random_field(
&self,
key: &[u8],
count: i64,
with_values: bool,
) -> wkv::Result<(GarnetStatus, Vec<(Vec<u8>, Option<Vec<u8>>)>)> {
match self.obj_load(key, super::common::OBJ_TAG_HASH).await? {
ObjState::Absent => Ok((GarnetStatus::NotFound, Vec::new())),
ObjState::WrongType => Ok((GarnetStatus::WrongType, Vec::new())),
ObjState::Present(payload) => {
let obj = HashObject::deserialize(&mut Cursor::new(payload)).unwrap_or_default();
let keys = obj.get_keys();
if keys.is_empty() {
return Ok((GarnetStatus::Ok, Vec::new()));
}
let pin = obj.hash.pin();
let pick = |k: Vec<u8>| {
let v = pin.get(&k).cloned();
(k, with_values.then_some(v).flatten())
};
if count < 0 {
let n = count.unsigned_abs() as usize;
let out = (0..n)
.map(|_| pick(keys[fastrand::usize(..keys.len())].clone()))
.collect();
return Ok((GarnetStatus::Ok, out));
}
let want = (count as usize).min(keys.len());
let mut pool = keys;
let mut out = Vec::with_capacity(want);
for _ in 0..want {
let last = pool.len() - 1;
let idx = fastrand::usize(..pool.len());
pool.swap(idx, last);
out.push(pick(pool.pop().unwrap_or_default()));
}
Ok((GarnetStatus::Ok, out))
}
}
}
pub async fn hash_str_length(
&self,
key: &[u8],
field: &[u8],
) -> wkv::Result<(GarnetStatus, Option<usize>)> {
Ok(match self.hash_get(key, field).await? {
(GarnetStatus::WrongType, _) => (GarnetStatus::WrongType, None),
(s, v) => (s, v.map(|b| b.len())),
})
}
pub async fn hash_keys(&self, key: &[u8]) -> wkv::Result<(GarnetStatus, Vec<Vec<u8>>)> {
Ok(match self.hash_get_all(key).await? {
(GarnetStatus::WrongType, _) => (GarnetStatus::WrongType, Vec::new()),
(s, all) => (s, all.into_iter().map(|(k, _)| k).collect()),
})
}
pub async fn hash_vals(&self, key: &[u8]) -> wkv::Result<(GarnetStatus, Vec<Vec<u8>>)> {
Ok(match self.hash_get_all(key).await? {
(GarnetStatus::WrongType, _) => (GarnetStatus::WrongType, Vec::new()),
(s, all) => (s, all.into_iter().map(|(_, v)| v).collect()),
})
}
pub async fn hash_set(
&self,
key: &[u8],
fields: &[(&[u8], &[u8])],
nx: bool,
) -> wkv::Result<(GarnetStatus, i64)> {
let added = self
.hash_rmw(key, true, |obj| {
let mut n = 0i64;
for (field, value) in fields {
let existed = obj.operate(HashOperation::HGET, field, b"").is_some();
if nx && existed {
continue;
}
obj.operate(HashOperation::HSET, field, value);
if !existed {
n += 1;
}
}
n
})
.await?;
match added {
RmwOutcome::WrongType => Ok((GarnetStatus::WrongType, 0)),
outcome => Ok((GarnetStatus::Ok, outcome.unwrap_or(0))),
}
}
pub async fn hash_increment(
&self,
key: &[u8],
field: &[u8],
delta: &[u8],
float: bool,
) -> wkv::Result<(GarnetStatus, Option<Vec<u8>>)> {
let outcome = self
.hash_rmw(key, true, |obj| {
let current = obj.operate(HashOperation::HGET, field, b"");
if float {
let d = strict_f64(delta, true)?;
if d.is_infinite() {
return None; }
match current.as_deref() {
None => {
obj.operate(HashOperation::HSET, field, delta);
Some((true, Some(delta.to_vec())))
}
Some(b) => {
let Some(cur) = strict_f64(b, true) else {
return None; };
if cur.is_infinite() {
return None; }
let new = cur + d;
let text = ObjectOutput::format_double(new);
obj.operate(HashOperation::HSET, field, text.as_bytes());
Some((true, Some(text.into_bytes())))
}
}
} else {
let parse_i64 = |b: &[u8]| -> Option<i64> {
let mut value = 0_i64;
try_parse_i64(b, &mut value).then_some(value)
};
let Some(d) = parse_i64(delta) else {
return None; };
match current.as_deref() {
None => {
obj.operate(HashOperation::HSET, field, delta);
Some((true, Some(delta.to_vec())))
}
Some(b) => {
let Some(cur) = parse_i64(b) else {
return None; };
let text = cur.wrapping_add(d).to_string();
obj.operate(HashOperation::HSET, field, text.as_bytes());
Some((true, Some(text.into_bytes())))
}
}
}
})
.await?;
match outcome {
RmwOutcome::Written(Some((true, v))) => Ok((GarnetStatus::Ok, v)),
RmwOutcome::Written(Some((false, _)))
| RmwOutcome::Written(None)
| RmwOutcome::Aborted
| RmwOutcome::WrongType => Ok((GarnetStatus::WrongType, None)),
}
}
pub async fn hash_time_to_live(&self, key: &[u8]) -> wkv::Result<(GarnetStatus, i64)> {
let pttl = self.pttl_ms(key).await?;
if pttl == -2 {
Ok((GarnetStatus::NotFound, pttl))
} else {
Ok((GarnetStatus::Ok, pttl))
}
}
}