use super::cursor::Reader;
use super::packed::{self, Element, ListpackWriter};
use super::{LoadStats, Result};
use crate::db::{Stream, StreamId, Value};
use bytes::Bytes;
const STREAM_LISTPACKS_2: u8 = 19;
const STREAM_LISTPACKS_3: u8 = 21;
const FLAG_DELETED: i64 = 1;
const FLAG_SAMEFIELDS: i64 = 2;
pub const WRITE_TYPE: u8 = STREAM_LISTPACKS_2;
pub fn read(r: &mut Reader, type_code: u8, stats: &mut LoadStats) -> Result<Value> {
let mut stream = Stream::default();
let nodes = r.count()?;
for _ in 0..nodes {
let key = r.string()?;
let master_id = stream_id_from_key(&key)
.ok_or_else(|| r.corrupt("stream node key is not a 16-byte id"))?;
let blob = r.string()?;
let elements =
packed::listpack(&blob).ok_or_else(|| r.corrupt("malformed stream listpack"))?;
read_node(r, &elements, master_id, &mut stream)?;
}
let _length = r.count_u64()?;
stream.last_id = StreamId {
ms: r.count_u64()?,
seq: r.count_u64()?,
};
if type_code >= STREAM_LISTPACKS_2 {
let _first_ms = r.count_u64()?;
let _first_seq = r.count_u64()?;
stream.max_deleted_id = StreamId {
ms: r.count_u64()?,
seq: r.count_u64()?,
};
let _entries_added = r.count_u64()?;
}
let groups = r.count()?;
for _ in 0..groups {
skip_consumer_group(r, type_code)?;
}
stats.dropped_groups += groups;
Ok(Value::Stream(stream))
}
fn read_node(
r: &Reader,
elements: &[Element],
master_id: StreamId,
stream: &mut Stream,
) -> Result<()> {
let int_at = |i: usize| -> Result<i64> {
elements
.get(i)
.and_then(Element::as_int)
.ok_or_else(|| r.corrupt("stream listpack: expected an integer"))
};
let count = int_at(0)?;
let deleted = int_at(1)?;
let num_master_fields = int_at(2)?;
if count < 0 || deleted < 0 || num_master_fields < 0 {
return Err(r.corrupt("stream listpack: negative master entry counter"));
}
let num_master_fields = num_master_fields as usize;
let master_fields: Vec<Bytes> = elements
.get(3..3 + num_master_fields)
.ok_or_else(|| r.corrupt("stream listpack: truncated master fields"))?
.iter()
.cloned()
.map(Element::into_bytes)
.collect();
if int_at(3 + num_master_fields)? != 0 {
return Err(r.corrupt("stream listpack: master entry is not zero-terminated"));
}
let mut i = 4 + num_master_fields;
for _ in 0..count.saturating_add(deleted) {
let flags = int_at(i)?;
let ms_diff = int_at(i + 1)?;
let seq_diff = int_at(i + 2)?;
i += 3;
let id = StreamId {
ms: master_id.ms.wrapping_add(ms_diff as u64),
seq: master_id.seq.wrapping_add(seq_diff as u64),
};
let fields: Vec<(Bytes, Bytes)> = if flags & FLAG_SAMEFIELDS != 0 {
let values = elements
.get(i..i + num_master_fields)
.ok_or_else(|| r.corrupt("stream listpack: truncated entry values"))?;
i += num_master_fields;
master_fields
.iter()
.cloned()
.zip(values.iter().cloned().map(Element::into_bytes))
.collect()
} else {
let n = int_at(i)?;
if n < 0 {
return Err(r.corrupt("stream listpack: negative field count"));
}
let n = n as usize;
i += 1;
let flat = elements
.get(i..i + n * 2)
.ok_or_else(|| r.corrupt("stream listpack: truncated entry fields"))?;
i += n * 2;
flat.chunks(2)
.map(|pair| (pair[0].clone().into_bytes(), pair[1].clone().into_bytes()))
.collect()
};
i += 1;
if flags & FLAG_DELETED == 0 {
stream.entries.insert(id, fields);
}
}
Ok(())
}
fn skip_consumer_group(r: &mut Reader, type_code: u8) -> Result<()> {
r.string()?; r.count_u64()?; r.count_u64()?; if type_code >= STREAM_LISTPACKS_2 {
r.count_u64()?; }
let pending = r.count()?;
for _ in 0..pending {
r.take(16)?; r.u64le()?; r.count_u64()?; }
let consumers = r.count()?;
for _ in 0..consumers {
r.string()?; r.u64le()?; if type_code >= STREAM_LISTPACKS_3 {
r.u64le()?; }
let owned = r.count()?;
for _ in 0..owned {
r.take(16)?; }
}
Ok(())
}
fn stream_id_from_key(raw: &[u8]) -> Option<StreamId> {
if raw.len() != 16 {
return None;
}
Some(StreamId {
ms: u64::from_be_bytes(raw[..8].try_into().ok()?),
seq: u64::from_be_bytes(raw[8..].try_into().ok()?),
})
}
pub fn write(out: &mut Vec<u8>, s: &Stream) {
use super::write::{put_length, put_raw_string};
put_length(out, s.entries.len() as u64);
for (id, fields) in &s.entries {
let mut key = Vec::with_capacity(16);
key.extend_from_slice(&id.ms.to_be_bytes());
key.extend_from_slice(&id.seq.to_be_bytes());
put_raw_string(out, &key);
let mut lp = ListpackWriter::new();
lp.int(1);
lp.int(0);
lp.int(fields.len() as i64);
for (field, _) in fields {
lp.str(field);
}
lp.int(0);
lp.int(FLAG_SAMEFIELDS);
lp.int(0); lp.int(0); for (_, value) in fields {
lp.str(value);
}
lp.int(fields.len() as i64 + 3);
put_raw_string(out, &lp.finish());
}
put_length(out, s.entries.len() as u64); put_length(out, s.last_id.ms);
put_length(out, s.last_id.seq);
let first = s.entries.keys().next().copied().unwrap_or(StreamId::MIN);
put_length(out, first.ms);
put_length(out, first.seq);
put_length(out, s.max_deleted_id.ms);
put_length(out, s.max_deleted_id.seq);
put_length(out, s.entries.len() as u64);
put_length(out, 0); }