use std::{
collections::VecDeque,
io::{self, Read, Write},
};
use wresp::cmd_strings::RESP_ERR_GENERIC_UNSUPPORTED_OPERATION as RESP_ERR_UNSUPPORTED_OPERATION;
use wval::GarnetObjectType;
use crate::{
object_store_utils::GarnetObjectPayload,
types::{
ObjectInput,
object_output::{ObjectOutput, ObjectOutputFlags},
},
};
#[derive(
Debug, Clone, Copy, PartialEq, Eq, num_enum::TryFromPrimitive, num_enum::IntoPrimitive,
)]
#[repr(u8)]
pub enum ListOperation {
Lpop = 0,
Lpush = 1,
Lpushx = 2,
Rpop = 3,
Rpush = 4,
Rpushx = 5,
Llen = 6,
Ltrim = 7,
Lrange = 8,
Lindex = 9,
Linsert = 10,
Lrem = 11,
Rpoplpush = 12,
Lmove = 13,
Lset = 14,
Brpop = 15,
Blpop = 16,
Lpos = 17,
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, num_enum::TryFromPrimitive, num_enum::IntoPrimitive,
)]
#[repr(u8)]
pub enum OperationDirection {
Left = 0,
Right = 1,
Unknown = 2,
}
#[derive(Debug, Clone, Default)]
pub struct ListObject {
pub list: VecDeque<Vec<u8>>,
pub heap_memory_size: i64,
}
impl ListObject {
pub fn new() -> Self {
Self::default()
}
pub fn deserialize_from_slice(slice: &[u8]) -> io::Result<Self> {
let list: VecDeque<Vec<u8>> =
bitcode::decode(slice).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
let mut obj = Self::new();
for item in &list {
obj.update_size(item, true);
}
obj.list = list;
Ok(obj)
}
pub fn deserialize<R: Read>(reader: &mut R) -> io::Result<Self> {
let mut buf = Vec::new();
reader.read_to_end(&mut buf)?;
Self::deserialize_from_slice(&buf)
}
#[inline]
pub fn serialize_to_vec(&self) -> Vec<u8> {
bitcode::encode(&self.list)
}
pub fn serialize<W: Write>(&self, writer: &mut W) -> io::Result<()> {
writer.write_all(&self.serialize_to_vec())
}
pub fn len(&self) -> usize {
self.list.len()
}
pub fn count(&self) -> usize {
self.list.len()
}
pub fn is_empty(&self) -> bool {
self.list.is_empty()
}
pub fn operate_basic(&mut self, op: ListOperation, item: &[u8]) -> Option<Vec<u8>> {
match op {
ListOperation::Lpush => {
self.update_size(item, true);
self.list.push_front(item.to_vec());
None
}
ListOperation::Rpush => {
self.update_size(item, true);
self.list.push_back(item.to_vec());
None
}
ListOperation::Lpop => {
let val = self.list.pop_front()?;
self.update_size(&val, false);
Some(val)
}
ListOperation::Rpop => {
let val = self.list.pop_back()?;
self.update_size(&val, false);
Some(val)
}
_ => None,
}
}
pub fn index(&self, index: isize) -> Option<Vec<u8>> {
let len = self.list.len() as isize;
let actual_idx = if index < 0 { len + index } else { index };
if actual_idx < 0 || actual_idx >= len {
None
} else {
self.list.get(actual_idx as usize).cloned()
}
}
pub fn range(&self, start: isize, stop: isize) -> Vec<Vec<u8>> {
let len = self.list.len() as isize;
let mut s = if start < 0 { len + start } else { start };
let mut e = if stop < 0 { len + stop } else { stop };
if s < 0 {
s = 0;
}
if e >= len {
e = len - 1;
}
if s > e || s >= len {
return vec![];
}
self
.list
.range((s as usize)..=(e as usize))
.cloned()
.collect()
}
pub fn trim(&mut self, start: isize, stop: isize) {
let len = self.list.len() as isize;
let mut s = if start < 0 { len + start } else { start };
let mut e = if stop < 0 { len + stop } else { stop };
if s < 0 {
s = 0;
}
if e >= len {
e = len - 1;
}
if s > e || s >= len {
self.list.clear();
self.heap_memory_size = 0;
return;
}
self.list.truncate((e + 1) as usize);
for _ in 0..s {
if let Some(item) = self.list.pop_front() {
self.update_size(&item, false);
}
}
}
pub fn operate(
&mut self,
input: &ObjectInput,
output: &mut ObjectOutput,
resp_protocol_version: u8,
) -> bool {
if input.header.data[0] != GarnetObjectType::List as u8 {
output.output_flags |= ObjectOutputFlags::WRONG_TYPE;
output.payload.clear();
return true;
}
let Some(op) = list_op_from_header(input) else {
output.write_error(RESP_ERR_UNSUPPORTED_OPERATION.as_bytes());
return true;
};
match op {
ListOperation::Lpush | ListOperation::Lpushx => self.list_push(input, output, true),
ListOperation::Rpush | ListOperation::Rpushx => self.list_push(input, output, false),
ListOperation::Lpop => self.list_pop(input, output, resp_protocol_version, true),
ListOperation::Rpop => self.list_pop(input, output, resp_protocol_version, false),
ListOperation::Llen => self.list_length(output),
ListOperation::Ltrim => self.list_trim(input, output),
ListOperation::Lrange => self.list_range(input, output),
ListOperation::Lindex => self.list_index(input, output),
ListOperation::Linsert => self.list_insert(input, output),
ListOperation::Lrem => self.list_remove(input, output),
ListOperation::Lset => self.list_set(input, output),
ListOperation::Lpos => self.list_position(input, output),
ListOperation::Rpoplpush
| ListOperation::Lmove
| ListOperation::Brpop
| ListOperation::Blpop => {
output.write_error(RESP_ERR_UNSUPPORTED_OPERATION.as_bytes());
}
}
if self.list.is_empty() {
output.output_flags |= ObjectOutputFlags::REMOVE_KEY;
}
true
}
pub fn update_size(&mut self, item: &[u8], add: bool) {
let memory_size = (item.len().div_ceil(8) * 8 + 16 + 16) as i64;
if add {
self.heap_memory_size += memory_size;
} else {
self.heap_memory_size -= memory_size;
}
}
#[inline]
pub fn nodes(&self) -> impl Iterator<Item = usize> {
0..self.list.len()
}
}
#[inline]
pub fn list_op_from_header(input: &ObjectInput) -> Option<ListOperation> {
ListOperation::try_from(input.header.sub_id()).ok()
}
impl GarnetObjectPayload for ListObject {
const OBJECT_TAG: GarnetObjectType = GarnetObjectType::List;
#[inline]
fn from_blob(raw: &[u8]) -> Self {
Self::deserialize_from_slice(raw).unwrap_or_default()
}
#[inline]
fn to_blob(&self) -> Vec<u8> {
self.serialize_to_vec()
}
#[inline]
fn is_empty(&self) -> bool {
self.list.is_empty()
}
}