use std::io::Cursor;
use wdev::Device;
use wobject::set::set_object::{SetObject, SetOperation};
use super::{
super::storage_session::StorageSession,
common::{ObjState, RmwOutcome},
};
use crate::api::garnet_status::GarnetStatus;
impl<'a, D: Device> StorageSession<'a, D> {
async fn set_rmw<R>(
&self,
key: &[u8],
create: bool,
f: impl FnOnce(&mut SetObject) -> Option<R>,
) -> wkv::Result<RmwOutcome<R>> {
self
.rmw_object_store_operation(key, super::common::OBJ_TAG_SET, |payload| {
let mut obj = match payload {
Some(bytes) => SetObject::deserialize(&mut Cursor::new(bytes)).unwrap_or_default(),
None if create => SetObject::new(),
None => return None,
};
let r = f(&mut obj)?;
let mut out = Vec::new();
obj.serialize(&mut out).ok()?;
Some((out, r))
})
.await
}
async fn set_load(&self, key: &[u8]) -> wkv::Result<Result<Option<SetObject>, GarnetStatus>> {
Ok(
match self.obj_load(key, super::common::OBJ_TAG_SET).await? {
ObjState::Absent => Ok(None),
ObjState::WrongType => Err(GarnetStatus::WrongType),
ObjState::Present(p) => Ok(Some(
SetObject::deserialize(&mut Cursor::new(p)).unwrap_or_default(),
)),
},
)
}
pub async fn set_add(&self, key: &[u8], members: &[&[u8]]) -> wkv::Result<(GarnetStatus, i64)> {
let added = self
.set_rmw(key, true, |obj| {
let mut n = 0i64;
for m in members {
if obj.operate(SetOperation::Sadd, m) {
n += 1;
}
}
Some(n)
})
.await?;
match added {
RmwOutcome::WrongType => Ok((GarnetStatus::WrongType, 0)),
outcome => Ok((GarnetStatus::Ok, outcome.unwrap_or(0))),
}
}
pub async fn set_remove(
&self,
key: &[u8],
members: &[&[u8]],
) -> wkv::Result<(GarnetStatus, i64)> {
let removed = self
.set_rmw(key, false, |obj| {
let mut n = 0i64;
for m in members {
if obj.operate(SetOperation::Srem, m) {
n += 1;
}
}
Some((n, obj.set.pin().is_empty()))
})
.await?;
match removed {
RmwOutcome::WrongType => Ok((GarnetStatus::WrongType, 0)),
RmwOutcome::Aborted => Ok((GarnetStatus::NotFound, 0)),
outcome => {
let n = self.finalize_removal(key, outcome, 0).await?;
Ok((GarnetStatus::Ok, n))
}
}
}
pub async fn set_length(&self, key: &[u8]) -> wkv::Result<(GarnetStatus, usize)> {
match self.set_load(key).await? {
Err(s) => Ok((s, 0)),
Ok(None) => Ok((GarnetStatus::NotFound, 0)),
Ok(Some(obj)) => Ok((GarnetStatus::Ok, obj.count())),
}
}
pub async fn set_members(&self, key: &[u8]) -> wkv::Result<(GarnetStatus, Vec<Vec<u8>>)> {
match self.set_load(key).await? {
Err(s) => Ok((s, Vec::new())),
Ok(None) => Ok((GarnetStatus::NotFound, Vec::new())),
Ok(Some(obj)) => Ok((GarnetStatus::Ok, obj.get_keys())),
}
}
pub async fn set_pop(
&self,
key: &[u8],
count: usize,
) -> wkv::Result<(GarnetStatus, Vec<Vec<u8>>)> {
let popped = self
.set_rmw(key, false, |obj| {
let mut out = Vec::new();
for _ in 0..count {
match obj.pop() {
Some(m) => out.push(m),
None => break,
}
}
Some((out, obj.set.pin().is_empty()))
})
.await?;
match popped {
RmwOutcome::WrongType => Ok((GarnetStatus::WrongType, Vec::new())),
RmwOutcome::Aborted => Ok((GarnetStatus::NotFound, Vec::new())),
outcome => {
let out = self.finalize_removal(key, outcome, Vec::new()).await?;
Ok((GarnetStatus::Ok, out))
}
}
}
pub async fn set_move(
&self,
src: &[u8],
dest: &[u8],
member: &[u8],
) -> wkv::Result<(GarnetStatus, bool)> {
let s = match self.set_load(src).await? {
Err(st) => return Ok((st, false)),
Ok(None) => return Ok((GarnetStatus::NotFound, false)),
Ok(Some(s)) => s,
};
if src == dest {
return Ok((GarnetStatus::Ok, false));
}
if let Err(st) = self.set_load(dest).await? {
return Ok((st, false));
}
if s.set.pin().get(member).is_none() {
return Ok((GarnetStatus::Ok, false));
}
let emptied = self
.set_rmw(src, false, |obj| {
Some(obj.operate(SetOperation::Srem, member) && obj.set.pin().is_empty())
})
.await?
.unwrap_or(false);
if emptied {
let _ = self.delete_string(src).await?;
}
self.set_add(dest, &[member]).await?;
Ok((GarnetStatus::Ok, true))
}
pub async fn set_intersect(&self, keys: &[&[u8]]) -> wkv::Result<(GarnetStatus, Vec<Vec<u8>>)> {
let Some(first) = keys.first() else {
return Ok((GarnetStatus::Ok, Vec::new()));
};
let base = match self.set_load(first).await? {
Err(s) => return Ok((s, Vec::new())),
Ok(None) => return Ok((GarnetStatus::Ok, Vec::new())),
Ok(Some(o)) => o,
};
let mut result: Vec<Vec<u8>> = base.get_keys();
for key in &keys[1..] {
match self.set_load(key).await? {
Err(s) => return Ok((s, Vec::new())),
Ok(None) => return Ok((GarnetStatus::Ok, Vec::new())),
Ok(Some(other)) => {
let pin = other.set.pin();
result.retain(|m| pin.contains(m));
}
}
if result.is_empty() {
break;
}
}
Ok((GarnetStatus::Ok, result))
}
pub async fn set_intersect_store(
&self,
dest: &[u8],
keys: &[&[u8]],
) -> wkv::Result<(GarnetStatus, usize)> {
if keys.is_empty() {
return Ok((GarnetStatus::Ok, 0));
}
let (status, members) = self.set_intersect(keys).await?;
if status != GarnetStatus::Ok {
return Ok((status, 0));
}
let _ = self.delete_string(dest).await?;
if members.is_empty() {
return Ok((GarnetStatus::Ok, 0));
}
let refs: Vec<&[u8]> = members.iter().map(Vec::as_slice).collect();
let (_, n) = self.set_add(dest, &refs).await?;
Ok((GarnetStatus::Ok, n as usize))
}
pub async fn set_union(&self, keys: &[&[u8]]) -> wkv::Result<(GarnetStatus, Vec<Vec<u8>>)> {
let merged = SetObject::new();
let pin = merged.set.pin();
for key in keys {
match self.set_load(key).await? {
Err(s) => return Ok((s, Vec::new())),
Ok(Some(obj)) => {
for m in obj.get_keys() {
pin.insert(m);
}
}
Ok(None) => {}
}
}
drop(pin);
Ok((GarnetStatus::Ok, merged.get_keys()))
}
pub async fn set_union_store(
&self,
dest: &[u8],
keys: &[&[u8]],
) -> wkv::Result<(GarnetStatus, usize)> {
if keys.is_empty() {
return Ok((GarnetStatus::Ok, 0));
}
let (status, members) = self.set_union(keys).await?;
if status != GarnetStatus::Ok {
return Ok((status, 0));
}
let _ = self.delete_string(dest).await?;
if members.is_empty() {
return Ok((GarnetStatus::Ok, 0));
}
let refs: Vec<&[u8]> = members.iter().map(Vec::as_slice).collect();
let (_, n) = self.set_add(dest, &refs).await?;
Ok((GarnetStatus::Ok, n as usize))
}
pub async fn set_is_member(
&self,
key: &[u8],
member: &[u8],
) -> wkv::Result<(GarnetStatus, bool)> {
match self.set_load(key).await? {
Err(s) => Ok((s, false)),
Ok(None) => Ok((GarnetStatus::NotFound, false)),
Ok(Some(obj)) => Ok((GarnetStatus::Ok, obj.set.pin().contains(member))),
}
}
pub async fn set_random_member(
&self,
key: &[u8],
count: i64,
) -> wkv::Result<(GarnetStatus, Vec<Vec<u8>>)> {
let (status, members) = self.set_members(key).await?;
if status != GarnetStatus::Ok || members.is_empty() {
return Ok((status, Vec::new()));
}
let out = if count < 0 {
let n = count.unsigned_abs() as usize;
(0..n)
.map(|_| members[fastrand::usize(..members.len())].clone())
.collect()
} else {
let n = (count as usize).min(members.len());
let mut pool = members;
let mut out = Vec::with_capacity(n);
for _ in 0..n {
let last = pool.len() - 1;
let idx = fastrand::usize(..pool.len());
pool.swap(idx, last);
out.push(pool.pop().unwrap_or_default());
}
out
};
Ok((GarnetStatus::Ok, out))
}
pub async fn set_diff(&self, keys: &[&[u8]]) -> wkv::Result<(GarnetStatus, Vec<Vec<u8>>)> {
let Some(first) = keys.first() else {
return Ok((GarnetStatus::Ok, Vec::new()));
};
let base = match self.set_load(first).await? {
Err(s) => return Ok((s, Vec::new())),
Ok(None) => return Ok((GarnetStatus::Ok, Vec::new())),
Ok(Some(o)) => o,
};
let mut result = base.get_keys();
for key in &keys[1..] {
match self.set_load(key).await? {
Err(s) => return Ok((s, Vec::new())),
Ok(Some(other)) => {
let pin = other.set.pin();
result.retain(|m| !pin.contains(m));
}
Ok(None) => {}
}
}
Ok((GarnetStatus::Ok, result))
}
pub async fn set_diff_store(
&self,
dest: &[u8],
keys: &[&[u8]],
) -> wkv::Result<(GarnetStatus, usize)> {
if keys.is_empty() {
return Ok((GarnetStatus::Ok, 0));
}
let (status, members) = self.set_diff(keys).await?;
if status != GarnetStatus::Ok {
return Ok((status, 0));
}
let _ = self.delete_string(dest).await?;
if members.is_empty() {
return Ok((GarnetStatus::Ok, 0));
}
let refs: Vec<&[u8]> = members.iter().map(Vec::as_slice).collect();
let (_, n) = self.set_add(dest, &refs).await?;
Ok((GarnetStatus::Ok, n as usize))
}
pub async fn set_intersect_length(&self, keys: &[&[u8]]) -> wkv::Result<(GarnetStatus, usize)> {
let (_, members) = self.set_intersect(keys).await?;
Ok((GarnetStatus::Ok, members.len()))
}
}