1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
use crate::codec::byte_to_message_decoder::MessageDecoder;

use bytes::BytesMut;
use std::io::ErrorKind;

/// Delimiter with different terminator type \n` or `\r\n`
#[derive(Default, PartialEq, Eq)]
pub enum TerminatorType {
    /// Delimiter with \n` or `\r\n`
    #[default]
    BOTH,

    /// Delimiter with `\n` only
    NEWLINE,

    /// Delimiter with `\r\n` only
    CarriageNewline,
}

/// A line based frame decoder with [TerminatorType] as delimiter
#[derive(Default)]
pub struct LineBasedFrameDecoder {
    max_length: usize,
    strip_delimiter: bool,
    terminator_type: TerminatorType,

    discarding: bool,
    discarded_bytes: usize,
}

impl LineBasedFrameDecoder {
    /// Creates a new LineBasedFrameDecoder
    pub fn new(max_length: usize, strip_delimiter: bool, terminator_type: TerminatorType) -> Self {
        Self {
            max_length,
            strip_delimiter,
            terminator_type,
            ..Default::default()
        }
    }

    fn find_end_of_line(&mut self, buf: &BytesMut) -> Option<usize> {
        let mut i = 0usize;
        while i < self.max_length && i < buf.len() {
            let b = buf[i];
            if (b == b'\n' && self.terminator_type != TerminatorType::CarriageNewline)
                || (self.terminator_type != TerminatorType::NEWLINE
                    && b == b'\r'
                    && i + 1 < buf.len()
                    && buf[i + 1] == b'\n')
            {
                return Some(i);
            }
            i += 1;
        }

        None
    }
}

impl MessageDecoder for LineBasedFrameDecoder {
    fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<BytesMut>, std::io::Error> {
        let eol = self.find_end_of_line(buf);
        let mut offset = 0;
        if !self.discarding {
            if let Some(eol) = eol {
                offset += eol;
                let delim_length = if buf[offset] == b'\r' { 2 } else { 1 };
                if eol > self.max_length {
                    return Err(std::io::Error::new(
                        ErrorKind::Other,
                        format!("frame length {} exceeds max {}", eol, self.max_length),
                    ));
                }

                let frame = if self.strip_delimiter {
                    let frame = buf.split_to(eol);
                    let _ = buf.split_to(delim_length);
                    frame
                } else {
                    buf.split_to(eol + delim_length)
                };

                Ok(Some(frame))
            } else {
                let len = buf.len();
                if len > self.max_length {
                    self.discarded_bytes = len;
                    let _ = buf.split_to(len);
                    self.discarding = true;
                    Err(std::io::Error::new(
                        ErrorKind::Other,
                        format!("over {}", len),
                    ))
                } else {
                    Ok(None)
                }
            }
        } else {
            if let Some(eol) = eol {
                offset += eol;
                let delim_length = if buf[offset] == b'\r' { 2 } else { 1 };
                let _ = buf.split_to(eol + delim_length);
                self.discarded_bytes = 0;
                self.discarding = false;
            } else {
                self.discarded_bytes = buf.len();
                let _ = buf.split_to(buf.len());
            }

            Ok(None)
        }
    }
}