use crate::{
ClientError, Error, Result,
resp::{
BIG_NUMBER_TAG, BOOL_TAG, BULK_ERROR_TAG, BULK_STRING_TAG, DOUBLE_TAG, INTEGER_TAG,
NULL_TAG, SIMPLE_ERROR_TAG, SIMPLE_STRING_TAG, VERBATIM_STRING_TAG,
},
};
use memchr::memchr;
use std::ops::Range;
const NO_BULK_LIMIT: usize = usize::MAX;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ScalarKind {
SimpleString,
Error,
Integer,
Double,
BulkString,
Boolean,
Null,
}
struct ScalarLayout {
kind: ScalarKind,
value: Range<usize>,
end: usize,
}
#[inline]
fn check_bulk_len(len: i64, max_bulk_length: usize) -> Result<()> {
if len as usize > max_bulk_length {
return Err(Error::Client(ClientError::BulkLengthTooLarge));
}
Ok(())
}
#[inline]
pub(crate) fn bulk_value_end(data: &[u8], pos: usize, max_bulk_length: usize) -> Option<usize> {
let tag = *data.get(pos)?;
if tag != b'$' && tag != b'=' {
return None;
}
let (len, after) = parse_int_at(data, pos + 1).ok()?;
if len < 0 {
return None;
}
check_bulk_len(len, max_bulk_length).ok()?;
Some(after + len as usize + 2)
}
#[inline(always)]
fn slice(data: &[u8], range: Range<usize>) -> Result<&[u8]> {
data.get(range).ok_or_else(|| Error::EOF)
}
#[inline]
fn find_crlf(data: &[u8], from: usize) -> Result<usize> {
let rem = data.get(from..).ok_or_else(|| Error::EOF)?;
let i = memchr(b'\r', rem).ok_or_else(|| Error::EOF)?;
if rem.get(i + 1) != Some(&b'\n') {
return Err(Error::EOF);
}
Ok(from + i)
}
#[inline(always)]
fn crlf_at<const FRAME: bool>(data: &[u8], from: usize) -> Result<usize> {
if !FRAME {
return find_crlf(data, from);
}
let cr = data.len().checked_sub(2).ok_or_else(|| Error::EOF)?;
if cr < from {
return Err(Error::EOF);
}
debug_assert_eq!(
Some(cr),
find_crlf(data, from).ok(),
"a frame's own bytes must end at its scalar's terminator"
);
Ok(cr)
}
#[inline]
pub(crate) fn parse_int_at(data: &[u8], from: usize) -> Result<(i64, usize)> {
let digits = data.get(from..).ok_or_else(|| Error::EOF)?;
let mut i = 0;
let negative = if let Some(&b'-') = digits.first() {
i += 1;
true
} else {
false
};
let mut n = 0i64;
while let Some(&digit) = digits.get(i) {
match digit {
b'0'..=b'9' => {
n = n
.checked_mul(10)
.and_then(|n| n.checked_sub((digit - b'0') as i64))
.ok_or_else(|| Error::Client(ClientError::CannotParseInteger))?;
i += 1;
}
b'\r' => match digits.get(i + 1) {
Some(&b'\n') => {
let value = if negative {
n
} else {
n.checked_neg()
.ok_or_else(|| Error::Client(ClientError::CannotParseInteger))?
};
return Ok((value, from + i + 2));
}
Some(_) => return Err(Error::Client(ClientError::CannotParseInteger)),
None => return Err(Error::EOF),
},
_ => return Err(Error::Client(ClientError::CannotParseInteger)),
}
}
Err(Error::EOF)
}
#[inline(always)]
fn scalar_layout<const FRAME: bool>(
data: &[u8],
at: usize,
max_bulk_length: usize,
) -> Result<ScalarLayout> {
let tag = *data.get(at).ok_or_else(|| Error::EOF)?;
let start = at + 1;
match tag {
SIMPLE_STRING_TAG => {
let cr = crlf_at::<FRAME>(data, start)?;
Ok(ScalarLayout {
kind: ScalarKind::SimpleString,
value: start..cr,
end: cr + 2,
})
}
SIMPLE_ERROR_TAG => {
let cr = crlf_at::<FRAME>(data, start)?;
Ok(ScalarLayout {
kind: ScalarKind::Error,
value: start..cr,
end: cr + 2,
})
}
INTEGER_TAG => {
let cr = crlf_at::<FRAME>(data, start)?;
Ok(ScalarLayout {
kind: ScalarKind::Integer,
value: start..cr,
end: cr + 2,
})
}
DOUBLE_TAG => {
let cr = crlf_at::<FRAME>(data, start)?;
Ok(ScalarLayout {
kind: ScalarKind::Double,
value: start..cr,
end: cr + 2,
})
}
BIG_NUMBER_TAG => {
let cr = crlf_at::<FRAME>(data, start)?;
Ok(ScalarLayout {
kind: ScalarKind::BulkString,
value: start..cr,
end: cr + 2,
})
}
NULL_TAG => {
let cr = crlf_at::<FRAME>(data, start)?;
Ok(ScalarLayout {
kind: ScalarKind::Null,
value: start..start,
end: cr + 2,
})
}
BOOL_TAG => {
match slice(data, start..start + 3)? {
[b't' | b'f', b'\r', b'\n'] => {}
_ => return Err(Error::Client(ClientError::CannotParseBoolean)),
}
Ok(ScalarLayout {
kind: ScalarKind::Boolean,
value: start..start + 1,
end: start + 3,
})
}
BULK_STRING_TAG => {
let (len, after) = parse_int_at(data, start)?;
if len == -1 {
return Ok(ScalarLayout {
kind: ScalarKind::Null,
value: after..after,
end: after,
});
}
if len < 0 {
return Err(Error::Client(ClientError::CannotParseBulkString));
}
check_bulk_len(len, max_bulk_length)?;
let end = after + len as usize + 2;
if slice(data, end - 2..end)? != b"\r\n" {
return Err(Error::Client(ClientError::CannotParseBulkString));
}
Ok(ScalarLayout {
kind: ScalarKind::BulkString,
value: after..after + len as usize,
end,
})
}
VERBATIM_STRING_TAG => {
let (len, after) = parse_int_at(data, start)?;
if len == -1 {
return Ok(ScalarLayout {
kind: ScalarKind::Null,
value: after..after,
end: after,
});
}
if len < 4 {
return Err(Error::Client(ClientError::VerbatimStringTooShort));
}
check_bulk_len(len, max_bulk_length)?;
let end = after + len as usize + 2;
if slice(data, end - 2..end)? != b"\r\n" {
return Err(Error::Client(ClientError::CannotParseVerbatimString));
}
Ok(ScalarLayout {
kind: ScalarKind::BulkString,
value: after + 4..after + len as usize,
end,
})
}
BULK_ERROR_TAG => {
let (len, after) = parse_int_at(data, start)?;
if len < 0 {
return Err(Error::Client(ClientError::CannotParseBulkError));
}
check_bulk_len(len, max_bulk_length)?;
let end = after + len as usize + 2;
if slice(data, end - 2..end)? != b"\r\n" {
return Err(Error::Client(ClientError::CannotParseBulkError));
}
Ok(ScalarLayout {
kind: ScalarKind::Error,
value: after..after + len as usize,
end,
})
}
_ => Err(Error::Client(ClientError::UnknownRespTag(tag as char))),
}
}
#[inline(always)]
pub(crate) fn scalar_end(data: &[u8], at: usize, max_bulk_length: usize) -> Result<usize> {
Ok(scalar_layout::<false>(data, at, max_bulk_length)?.end)
}
#[inline(always)]
pub(crate) fn scalar_value(data: &[u8], at: usize) -> Result<(ScalarKind, Range<usize>)> {
let layout = scalar_layout::<false>(data, at, NO_BULK_LIMIT)?;
Ok((layout.kind, layout.value))
}
#[inline(always)]
pub(crate) fn scalar_span(data: &[u8], at: usize) -> Result<Range<usize>> {
Ok(at..scalar_layout::<false>(data, at, NO_BULK_LIMIT)?.end)
}
#[inline(always)]
pub(crate) fn frame_scalar_value(data: &[u8]) -> Result<(ScalarKind, Range<usize>)> {
let layout = scalar_layout::<true>(data, 0, NO_BULK_LIMIT)?;
debug_assert_eq!(
layout.end,
data.len(),
"a frame's own bytes must hold nothing but its scalar"
);
Ok((layout.kind, layout.value))
}