mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-08-19 00:00:09 +02:00
90 lines
2.7 KiB
Rust
90 lines
2.7 KiB
Rust
use bytes::{BufMut, BytesMut};
|
|
use tokio_io::_tokio_codec::{Encoder, Decoder};
|
|
use std::{io, str};
|
|
|
|
/// A simple `Codec` implementation that splits up data into lines.
|
|
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
|
pub struct LinesCodec {
|
|
// Stored index of the next index to examine for a `\n` character.
|
|
// This is used to optimize searching.
|
|
// For example, if `decode` was called with `abc`, it would hold `3`,
|
|
// because that is the next index to examine.
|
|
// The next time `decode` is called with `abcde\n`, the method will
|
|
// only look at `de\n` before returning.
|
|
next_index: usize,
|
|
}
|
|
|
|
impl LinesCodec {
|
|
/// Returns a `LinesCodec` for splitting up data into lines.
|
|
pub fn new() -> LinesCodec {
|
|
LinesCodec { next_index: 0 }
|
|
}
|
|
}
|
|
|
|
fn utf8(buf: &[u8]) -> Result<&str, io::Error> {
|
|
str::from_utf8(buf).map_err(|_|
|
|
io::Error::new(
|
|
io::ErrorKind::InvalidData,
|
|
"Unable to decode input as UTF8"))
|
|
}
|
|
|
|
fn without_carriage_return(s: &[u8]) -> &[u8] {
|
|
if let Some(&b'\r') = s.last() {
|
|
&s[..s.len() - 1]
|
|
} else {
|
|
s
|
|
}
|
|
}
|
|
|
|
impl Decoder for LinesCodec {
|
|
type Item = String;
|
|
type Error = io::Error;
|
|
|
|
fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<String>, io::Error> {
|
|
if let Some(newline_offset) =
|
|
buf[self.next_index..].iter().position(|b| *b == b'\n')
|
|
{
|
|
let newline_index = newline_offset + self.next_index;
|
|
let line = buf.split_to(newline_index + 1);
|
|
let line = &line[..line.len()-1];
|
|
let line = without_carriage_return(line);
|
|
let line = utf8(line)?;
|
|
self.next_index = 0;
|
|
Ok(Some(line.to_string()))
|
|
} else {
|
|
self.next_index = buf.len();
|
|
Ok(None)
|
|
}
|
|
}
|
|
|
|
fn decode_eof(&mut self, buf: &mut BytesMut) -> Result<Option<String>, io::Error> {
|
|
Ok(match self.decode(buf)? {
|
|
Some(frame) => Some(frame),
|
|
None => {
|
|
// No terminating newline - return remaining data, if any
|
|
if buf.is_empty() || buf == &b"\r"[..] {
|
|
None
|
|
} else {
|
|
let line = buf.take();
|
|
let line = without_carriage_return(&line);
|
|
let line = utf8(line)?;
|
|
self.next_index = 0;
|
|
Some(line.to_string())
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Encoder for LinesCodec {
|
|
type Item = String;
|
|
type Error = io::Error;
|
|
|
|
fn encode(&mut self, line: String, buf: &mut BytesMut) -> Result<(), io::Error> {
|
|
buf.reserve(line.len() + 1);
|
|
buf.put(line);
|
|
buf.put_u8(b'\n');
|
|
Ok(())
|
|
}
|
|
}
|