use bytes::Bytes;
use http::StatusCode;
use super::envelope::unix_ms_to_system_time;
use super::version_from_u8;
use crate::backend::{CacheEntry, CacheRead};
use crate::error::CacheError;
fn err(msg: impl Into<String>) -> CacheError {
CacheError::Backend(format!("legacy bincode1 decode: {}", msg.into()))
}
struct Cursor<'a> {
bytes: &'a [u8],
pos: usize,
}
impl<'a> Cursor<'a> {
fn new(bytes: &'a [u8]) -> Self {
Self { bytes, pos: 0 }
}
fn remaining(&self) -> usize {
self.bytes.len() - self.pos
}
fn take(&mut self, n: usize) -> Result<&'a [u8], CacheError> {
let end = self
.pos
.checked_add(n)
.ok_or_else(|| err("length overflow"))?;
if end > self.bytes.len() {
return Err(err(format!(
"unexpected end of input: need {} bytes, {} remain",
n,
self.remaining()
)));
}
let slice = &self.bytes[self.pos..end];
self.pos = end;
Ok(slice)
}
fn u8(&mut self) -> Result<u8, CacheError> {
Ok(self.take(1)?[0])
}
fn u16(&mut self) -> Result<u16, CacheError> {
Ok(u16::from_le_bytes(self.take(2)?.try_into().unwrap()))
}
fn u64(&mut self) -> Result<u64, CacheError> {
Ok(u64::from_le_bytes(self.take(8)?.try_into().unwrap()))
}
fn len(&mut self) -> Result<usize, CacheError> {
let n = self.u64()?;
if n > self.remaining() as u64 {
return Err(err(format!(
"declared length {} exceeds the {} bytes remaining",
n,
self.remaining()
)));
}
Ok(n as usize)
}
fn byte_string(&mut self) -> Result<Vec<u8>, CacheError> {
let n = self.len()?;
Ok(self.take(n)?.to_vec())
}
fn string(&mut self) -> Result<String, CacheError> {
let n = self.len()?;
String::from_utf8(self.take(n)?.to_vec()).map_err(|e| err(e.to_string()))
}
fn headers(&mut self) -> Result<Vec<(String, Vec<u8>)>, CacheError> {
let n = self.len()?;
let mut out = Vec::with_capacity(n.min(self.remaining() / 16 + 1));
for _ in 0..n {
let name = self.string()?;
let value = self.byte_string()?;
out.push((name, value));
}
Ok(out)
}
fn finish(self) -> Result<(), CacheError> {
if self.pos != self.bytes.len() {
return Err(err(format!(
"{} trailing bytes after the record",
self.remaining()
)));
}
Ok(())
}
}
fn status_from_u16(value: u16) -> Result<StatusCode, CacheError> {
StatusCode::from_u16(value).map_err(|e| err(e.to_string()))
}
pub fn decode_legacy_redis(bytes: &[u8]) -> Result<CacheRead, CacheError> {
let mut cursor = Cursor::new(bytes);
let payload = cursor.byte_string()?;
let expires_at_ms = cursor.u64()?;
let stale_until_ms = cursor.u64()?;
cursor.finish()?;
let mut inner = Cursor::new(&payload);
let status = inner.u16()?;
let version = inner.u8()?;
let headers = inner.headers()?;
let body = inner.byte_string()?;
inner.finish()?;
Ok(CacheRead {
entry: CacheEntry {
status: status_from_u16(status)?,
version: version_from_u8(version)?,
headers,
body: Bytes::from(body),
tags: None,
},
expires_at: Some(unix_ms_to_system_time(expires_at_ms)),
stale_until: Some(unix_ms_to_system_time(stale_until_ms)),
})
}