Parsing
Now, let’s look at the parse_frame()
function. Parsing is done in two steps.
- Ensure a full frame is buffered and find the end index of the frame.
- Parse the frame.
The mini-redis
crate provides us with a function for both of these steps:
We will also reuse the Buf
abstraction to help. A Buf
is passed into Frame::check
. As the check
function iterates the passed in buffer, the internal cursor will be advanced. When check
returns, the buffer’s internal cursor points to the end of the frame.
For the Buf
type, we will use std::io::Cursor<&[u8]>
.
use mini_redis::{Frame, Result};
use mini_redis::frame::Error::Incomplete;
use bytes::Buf;
use std::io::Cursor;
fn parse_frame(&mut self)
-> Result<Option<Frame>>
{
// Create the `T: Buf` type.
let mut buf = Cursor::new(&self.buffer[..]);
// Check whether a full frame is available
match Frame::check(&mut buf) {
Ok(_) => {
// Get the byte length of the frame
let len = buf.position() as usize;
// Reset the internal cursor for the
// call to `parse`.
buf.set_position(0);
// Parse the frame
let frame = Frame::parse(&mut buf)?;
// Discard the frame from the buffer
self.buffer.advance(len);
// Return the frame to the caller.
Ok(Some(frame))
}
// Not enough data has been buffered
Err(Incomplete) => Ok(None),
// An error was encountered
Err(e) => Err(e.into()),
}
}
The full Frame::check
function can be found here. We will not cover it in its entirety.
The relevant thing to note is that Buf
‘s “byte iterator” style APIs are used. These fetch data and advance the internal cursor. For example, to parse a frame, the first byte is checked to determine the type of the frame. The function used is Buf::get_u8
. This fetches the byte at the current cursor’s position and advances the cursor by one.
There are more useful methods on the Buf
trait. Check the API docs for more details.