use crate::db::cursor::token::TokenWireError;
use std::str;
pub(in crate::db::cursor::token) struct ByteCursor<'a> {
bytes: &'a [u8],
offset: usize,
}
impl<'a> ByteCursor<'a> {
pub(in crate::db::cursor::token) const fn new(bytes: &'a [u8]) -> Self {
Self { bytes, offset: 0 }
}
const fn remaining(&self) -> usize {
self.bytes.len().saturating_sub(self.offset)
}
pub(in crate::db::cursor::token) fn read_exact(
&mut self,
len: usize,
) -> Result<&'a [u8], TokenWireError> {
let end = self
.offset
.checked_add(len)
.ok_or_else(|| TokenWireError::decode("cursor token length overflow"))?;
let Some(slice) = self.bytes.get(self.offset..end) else {
return Err(TokenWireError::decode(format!(
"cursor token truncated: needed {len} bytes with {} remaining",
self.remaining()
)));
};
self.offset = end;
Ok(slice)
}
pub(in crate::db::cursor::token) fn read_array<const N: usize>(
&mut self,
) -> Result<[u8; N], TokenWireError> {
let bytes = self.read_exact(N)?;
let mut out = [0u8; N];
out.copy_from_slice(bytes);
Ok(out)
}
pub(in crate::db::cursor::token) fn read_u8(&mut self) -> Result<u8, TokenWireError> {
Ok(self.read_exact(1)?[0])
}
pub(in crate::db::cursor::token) fn read_u32(&mut self) -> Result<u32, TokenWireError> {
Ok(u32::from_be_bytes(self.read_array()?))
}
pub(in crate::db::cursor::token) fn read_u64(&mut self) -> Result<u64, TokenWireError> {
Ok(u64::from_be_bytes(self.read_array()?))
}
pub(in crate::db::cursor::token) fn read_i64(&mut self) -> Result<i64, TokenWireError> {
Ok(i64::from_be_bytes(self.read_array()?))
}
pub(in crate::db::cursor::token) fn read_i128(&mut self) -> Result<i128, TokenWireError> {
Ok(i128::from_be_bytes(self.read_array()?))
}
pub(in crate::db::cursor::token) fn read_u128(&mut self) -> Result<u128, TokenWireError> {
Ok(u128::from_be_bytes(self.read_array()?))
}
pub(in crate::db::cursor::token) fn read_len_prefixed_bytes(
&mut self,
) -> Result<&'a [u8], TokenWireError> {
let len = usize::try_from(self.read_u32()?)
.map_err(|_| TokenWireError::decode("cursor token length does not fit usize"))?;
self.read_exact(len)
}
pub(in crate::db::cursor::token) fn read_string(&mut self) -> Result<String, TokenWireError> {
let bytes = self.read_len_prefixed_bytes()?;
let text = str::from_utf8(bytes)
.map_err(|err| TokenWireError::decode(format!("cursor token invalid utf-8: {err}")))?;
Ok(text.to_string())
}
pub(in crate::db::cursor::token) fn finish(self) -> Result<(), TokenWireError> {
if self.remaining() == 0 {
return Ok(());
}
Err(TokenWireError::decode(format!(
"cursor token has {} trailing bytes",
self.remaining()
)))
}
}
pub(in crate::db::cursor::token) fn checked_len_u32(len: usize) -> Result<u32, TokenWireError> {
u32::try_from(len)
.map_err(|_| TokenWireError::encode("cursor token payload exceeds u32 length"))
}
pub(in crate::db::cursor::token) fn write_u32(out: &mut Vec<u8>, value: u32) {
out.extend_from_slice(&value.to_be_bytes());
}
pub(in crate::db::cursor::token) fn write_u64(out: &mut Vec<u8>, value: u64) {
out.extend_from_slice(&value.to_be_bytes());
}
pub(in crate::db::cursor::token) fn write_i64(out: &mut Vec<u8>, value: i64) {
out.extend_from_slice(&value.to_be_bytes());
}
pub(in crate::db::cursor::token) fn write_i128(out: &mut Vec<u8>, value: i128) {
out.extend_from_slice(&value.to_be_bytes());
}
pub(in crate::db::cursor::token) fn write_u128(out: &mut Vec<u8>, value: u128) {
out.extend_from_slice(&value.to_be_bytes());
}
pub(in crate::db::cursor::token) fn write_len_prefixed_bytes(
out: &mut Vec<u8>,
bytes: &[u8],
) -> Result<(), TokenWireError> {
write_u32(out, checked_len_u32(bytes.len())?);
out.extend_from_slice(bytes);
Ok(())
}
pub(in crate::db::cursor::token) fn write_string(
out: &mut Vec<u8>,
value: &str,
) -> Result<(), TokenWireError> {
write_len_prefixed_bytes(out, value.as_bytes())
}