use crate::RespCode;
use std::hint::unreachable_unchecked;
#[derive(Debug)]
pub(super) struct Parser<'a> {
cursor: usize,
buffer: &'a [u8],
}
#[derive(Debug, PartialEq)]
#[non_exhaustive]
pub enum Element {
Array(Vec<Element>),
String(String),
UnsignedInt(u64),
FlatArray(Vec<String>),
RespCode(RespCode),
}
#[derive(Debug, PartialEq)]
pub enum ParseError {
NotEnough,
UnexpectedByte,
BadPacket,
DataTypeParseError,
UnknownDatatype,
Empty,
}
#[derive(Debug, PartialEq)]
pub enum RawResponse {
SimpleQuery(Element),
PipelinedQuery(Vec<Element>),
}
pub type ParseResult<T> = Result<T, ParseError>;
impl<'a> Parser<'a> {
pub const fn new(buffer: &'a [u8]) -> Self {
Parser {
cursor: 0usize,
buffer,
}
}
fn read_until(&mut self, until: usize) -> ParseResult<&[u8]> {
if let Some(b) = self.buffer.get(self.cursor..self.cursor + until) {
self.cursor += until;
Ok(b)
} else {
Err(ParseError::NotEnough)
}
}
fn read_line(&mut self) -> (usize, usize) {
let started_at = self.cursor;
let mut stopped_at = self.cursor;
while self.cursor < self.buffer.len() {
if self.buffer[self.cursor] == b'\n' {
self.incr_cursor();
break;
}
stopped_at += 1;
self.incr_cursor();
}
(started_at, stopped_at)
}
fn incr_cursor(&mut self) {
self.cursor += 1;
}
fn will_cursor_give_char(&self, ch: u8, this_if_nothing_ahead: bool) -> ParseResult<bool> {
self.buffer.get(self.cursor).map_or(
if this_if_nothing_ahead {
Ok(true)
} else {
Err(ParseError::NotEnough)
},
|v| Ok(*v == ch),
)
}
fn will_cursor_give_linefeed(&self) -> ParseResult<bool> {
self.will_cursor_give_char(b'\n', false)
}
fn parse_into_usize(bytes: &[u8]) -> ParseResult<usize> {
if bytes.is_empty() {
return Err(ParseError::NotEnough);
}
let byte_iter = bytes.iter();
let mut item_usize = 0usize;
for dig in byte_iter {
if !dig.is_ascii_digit() {
return Err(ParseError::DataTypeParseError);
}
let curdig: usize = dig
.checked_sub(48)
.unwrap_or_else(|| unsafe { unreachable_unchecked() })
.into();
let product = match item_usize.checked_mul(10) {
Some(not_overflowed) => not_overflowed,
None => return Err(ParseError::DataTypeParseError),
};
let sum = match product.checked_add(curdig) {
Some(not_overflowed) => not_overflowed,
None => return Err(ParseError::DataTypeParseError),
};
item_usize = sum;
}
Ok(item_usize)
}
fn parse_into_u64(bytes: &[u8]) -> ParseResult<u64> {
if bytes.is_empty() {
return Err(ParseError::NotEnough);
}
let byte_iter = bytes.iter();
let mut item_u64 = 0u64;
for dig in byte_iter {
if !dig.is_ascii_digit() {
return Err(ParseError::DataTypeParseError);
}
let curdig: u64 = dig
.checked_sub(48)
.unwrap_or_else(|| unsafe { unreachable_unchecked() })
.into();
let product = match item_u64.checked_mul(10) {
Some(not_overflowed) => not_overflowed,
None => return Err(ParseError::DataTypeParseError),
};
let sum = match product.checked_add(curdig) {
Some(not_overflowed) => not_overflowed,
None => return Err(ParseError::DataTypeParseError),
};
item_u64 = sum;
}
Ok(item_u64)
}
fn parse_metaframe_get_datagroup_count(&mut self) -> ParseResult<usize> {
if self.buffer.len() < 3 {
return Err(ParseError::NotEnough);
}
let (start, stop) = self.read_line();
if let Some(our_chunk) = self.buffer.get(start..stop) {
if our_chunk[0] == b'*' {
let ret = Self::parse_into_usize(&our_chunk[1..])?;
Ok(ret)
} else {
Err(ParseError::UnexpectedByte)
}
} else {
Err(ParseError::NotEnough)
}
}
fn __get_next_element(&mut self) -> ParseResult<&[u8]> {
let string_sizeline = self.read_line();
if let Some(line) = self.buffer.get(string_sizeline.0..string_sizeline.1) {
let string_size = Self::parse_into_usize(line)?;
let our_chunk = self.read_until(string_size)?;
Ok(our_chunk)
} else {
Err(ParseError::NotEnough)
}
}
fn parse_next_string(&mut self) -> ParseResult<String> {
let our_string_chunk = self.__get_next_element()?;
let our_string = String::from_utf8_lossy(&our_string_chunk).to_string();
if self.will_cursor_give_linefeed()? {
self.incr_cursor();
Ok(our_string)
} else {
Err(ParseError::UnexpectedByte)
}
}
fn parse_next_u64(&mut self) -> ParseResult<u64> {
let our_u64_chunk = self.__get_next_element()?;
let our_u64 = Self::parse_into_u64(our_u64_chunk)?;
if self.will_cursor_give_linefeed()? {
self.incr_cursor();
Ok(our_u64)
} else {
Err(ParseError::UnexpectedByte)
}
}
fn parse_next_respcode(&mut self) -> ParseResult<RespCode> {
let our_respcode_chunk = self.__get_next_element()?;
let our_respcode = RespCode::from_str(&String::from_utf8_lossy(our_respcode_chunk));
if self.will_cursor_give_linefeed()? {
self.incr_cursor();
Ok(our_respcode)
} else {
Err(ParseError::UnexpectedByte)
}
}
fn parse_next_element(&mut self) -> ParseResult<Element> {
if let Some(tsymbol) = self.buffer.get(self.cursor) {
self.incr_cursor();
let ret = match *tsymbol {
b'+' => Element::String(self.parse_next_string()?),
b':' => Element::UnsignedInt(self.parse_next_u64()?),
b'&' => Element::Array(self.parse_next_array()?),
b'!' => Element::RespCode(self.parse_next_respcode()?),
b'_' => Element::FlatArray(self.parse_next_flat_array()?),
_ => return Err(ParseError::UnknownDatatype),
};
Ok(ret)
} else {
Err(ParseError::NotEnough)
}
}
fn parse_next_flat_array(&mut self) -> ParseResult<Vec<String>> {
let (start, stop) = self.read_line();
if let Some(our_size_chunk) = self.buffer.get(start..stop) {
let array_size = Self::parse_into_usize(&our_size_chunk)?;
let mut array = Vec::with_capacity(array_size);
for _ in 0..array_size {
if let Some(tsymbol) = self.buffer.get(self.cursor) {
self.incr_cursor();
let ret = match *tsymbol {
b'+' => self.parse_next_string()?,
_ => return Err(ParseError::UnknownDatatype),
};
array.push(ret);
} else {
return Err(ParseError::NotEnough);
}
}
Ok(array)
} else {
Err(ParseError::NotEnough)
}
}
fn parse_next_array(&mut self) -> ParseResult<Vec<Element>> {
let (start, stop) = self.read_line();
if let Some(our_size_chunk) = self.buffer.get(start..stop) {
let array_size = Self::parse_into_usize(our_size_chunk)?;
let mut array = Vec::with_capacity(array_size);
for _ in 0..array_size {
array.push(self.parse_next_element()?);
}
Ok(array)
} else {
Err(ParseError::NotEnough)
}
}
pub fn parse(mut self) -> Result<(RawResponse, usize), ParseError> {
let number_of_queries = self.parse_metaframe_get_datagroup_count()?;
if number_of_queries == 0 {
return Err(ParseError::BadPacket);
}
if number_of_queries == 1 {
let single_group = self.parse_next_element()?;
#[allow(clippy::blocks_in_if_conditions)]
if self
.will_cursor_give_char(b'*', true)
.unwrap_or_else(|_| unsafe {
unreachable_unchecked()
})
{
Ok((RawResponse::SimpleQuery(single_group), self.cursor))
} else {
Err(ParseError::UnexpectedByte)
}
} else {
let mut queries = Vec::with_capacity(number_of_queries);
for _ in 0..number_of_queries {
queries.push(self.parse_next_element()?);
}
if self.will_cursor_give_char(b'*', true)? {
Ok((RawResponse::PipelinedQuery(queries), self.cursor))
} else {
Err(ParseError::UnexpectedByte)
}
}
}
}