1#![doc = include_str!("../README.md")]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![deny(
4 nonstandard_style,
5 rust_2018_idioms,
6 rustdoc::broken_intra_doc_links,
7 rustdoc::private_intra_doc_links
8)]
9#![forbid(non_ascii_idents, unsafe_code)]
10#![warn(
11 deprecated_in_future,
12 missing_copy_implementations,
13 missing_debug_implementations,
14 missing_docs,
15 unreachable_pub,
16 unused_import_braces,
17 unused_labels,
18 unused_lifetimes,
19 unused_qualifications,
20 unused_results
21)]
22#![allow(clippy::uninlined_format_args)]
23use std::{ops::Deref, str::Utf8Error};
24
25use thiserror::Error;
26
27#[derive(Debug)]
32pub struct NetstringParser {
33 buf: Vec<u8>,
34 len: usize,
35}
36
37impl NetstringParser {
38 pub fn new(buf_size: usize) -> Self {
40 Self {
41 buf: vec![0; buf_size],
42 len: 0,
43 }
44 }
45
46 pub fn available_buffer(&mut self) -> &mut [u8] {
62 &mut self.buf[self.len..]
63 }
64
65 pub fn advance(&mut self, count: usize) {
72 self.len += count;
73 }
74
75 pub fn write(&mut self, data: &[u8]) -> Result<(), WriteError> {
85 let remaining = self.buf.len() - self.len;
86 if data.len() <= remaining {
87 self.buf[self.len..self.len + data.len()].copy_from_slice(data);
88 self.len += data.len();
89 Ok(())
90 } else {
91 Err(WriteError::BufferTooSmall)
92 }
93 }
94
95 pub fn is_buffer_full(&self) -> bool {
97 self.len >= self.buf.len()
98 }
99
100 pub fn is_buffer_empty(&self) -> bool {
102 self.len == 0
103 }
104
105 pub fn parse_next<'a>(&'a mut self) -> Result<Option<Netstring<'a>>, NetstringError> {
110 match parse_length(&self.buf[..self.len])? {
111 None => Ok(None),
112 Some((len, rest)) => {
113 if rest.len() < len + 1 {
114 return Ok(None); }
116 if rest[len] != b',' {
117 return Err(NetstringError::MissingComma);
118 }
119 let offset = self.len - rest.len();
120 Ok(Some(Netstring {
121 parser: self,
122 offset,
123 length: len,
124 }))
125 }
126 }
127 }
128
129 pub fn clear(&mut self) {
131 self.len = 0;
132 }
133
134 fn discard(&mut self, count: usize) {
138 self.buf.copy_within(count..self.len, 0);
139 self.len = self.len.saturating_sub(count);
140 }
141}
142
143#[derive(Debug, Error, Copy, Clone)]
145pub enum NetstringError {
146 #[error("String too long")]
148 StringTooLong,
149 #[error("Invalid data")]
151 InvalidData,
152 #[error("No colon found")]
154 NoColonFound,
155 #[error("Missing comma")]
157 MissingComma,
158 #[error("Invalid length")]
160 InvalidLength,
161}
162
163#[derive(Debug, Error, Copy, Clone)]
165pub enum WriteError {
166 #[error("Buffer too small")]
168 BufferTooSmall,
169}
170
171pub struct Netstring<'a> {
175 parser: &'a mut NetstringParser,
176 offset: usize,
177 length: usize,
178}
179
180impl Netstring<'_> {
181 pub fn to_str(&self) -> Result<&str, Utf8Error> {
184 std::str::from_utf8(self)
185 }
186 pub fn as_bytes(&self) -> &[u8] {
188 self
189 }
190}
191
192impl<'a> std::fmt::Debug for Netstring<'a> {
193 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
194 f.debug_tuple("Netstring").field(&self.as_bytes()).finish()
195 }
196}
197
198impl<'a> std::fmt::Display for Netstring<'a> {
199 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
200 match self.to_str() {
201 Ok(s) => f.write_str(s),
202 Err(_) => write!(f, "<invalid utf-8: {:?}>", self.as_bytes()),
203 }
204 }
205}
206
207impl<'a> Deref for Netstring<'a> {
208 type Target = [u8];
209 fn deref(&self) -> &Self::Target {
210 &self.parser.buf[self.offset..self.offset + self.length]
211 }
212}
213
214impl<'a> Drop for Netstring<'a> {
215 fn drop(&mut self) {
216 self.parser.discard(self.offset + self.length + 1);
218 }
219}
220
221fn parse_length(input: &[u8]) -> Result<Option<(usize, &[u8])>, NetstringError> {
222 let Some(colon_pos) = input.iter().position(|&b| b == b':') else {
223 if input.len() > 20 {
224 return Err(NetstringError::NoColonFound);
229 }
230 return Ok(None);
231 };
232 let len = &input[..colon_pos];
233 let rest = &input[colon_pos + 1..];
234 let Ok(len) = std::str::from_utf8(len) else {
235 return Err(NetstringError::InvalidLength);
236 };
237 let Ok(len) = len.parse::<usize>() else {
238 return Err(NetstringError::InvalidLength);
239 };
240 Ok(Some((len, rest)))
241}