1use crate::error::ProtocolError;
23use crate::proto::Limits;
24use yo_common::num::parse_i64;
25
26#[derive(Debug, Clone, PartialEq)]
28#[non_exhaustive]
29pub enum Frame<'a> {
30 Simple(&'a [u8]),
32 Error(&'a [u8]),
34 BlobError(&'a [u8]),
36 Int(i64),
38 Bulk(&'a [u8]),
40 Null,
46 Double(f64),
48 Bool(bool),
50 BigNumber(&'a [u8]),
52 Verbatim {
54 format: &'a [u8],
56 text: &'a [u8],
58 },
59 Array(Vec<Frame<'a>>),
61 Map(Vec<(Frame<'a>, Frame<'a>)>),
63 Set(Vec<Frame<'a>>),
65 Push(Vec<Frame<'a>>),
67 Attribute(Vec<(Frame<'a>, Frame<'a>)>),
74}
75
76impl Frame<'_> {
77 pub fn is_error(&self) -> bool {
79 matches!(self, Frame::Error(_) | Frame::BlobError(_))
80 }
81}
82
83pub fn decode<'a>(
94 buf: &'a [u8],
95 limits: &Limits,
96) -> Result<Option<(Frame<'a>, usize)>, ProtocolError> {
97 decode_at(buf, 0, limits, 0)
98}
99
100fn decode_at<'a>(
101 buf: &'a [u8],
102 at: usize,
103 limits: &Limits,
104 depth: usize,
105) -> Result<Option<(Frame<'a>, usize)>, ProtocolError> {
106 if depth > limits.max_depth {
107 return Err(ProtocolError::TooDeep);
108 }
109 let Some(&kind) = buf.get(at) else {
110 return Ok(None);
111 };
112 let Some((line, after)) = line_at(buf, at + 1) else {
113 return Ok(None);
114 };
115 match kind {
116 b'+' => Ok(Some((Frame::Simple(line), after))),
117 b'-' => Ok(Some((Frame::Error(line), after))),
118 b':' => {
119 let n = parse_i64(line).ok_or(ProtocolError::InvalidBulkLength)?;
120 Ok(Some((Frame::Int(n), after)))
121 }
122 b'_' => Ok(Some((Frame::Null, after))),
123 b'#' => match line {
124 b"t" => Ok(Some((Frame::Bool(true), after))),
125 b"f" => Ok(Some((Frame::Bool(false), after))),
126 _ => Err(ProtocolError::UnknownType(b'#')),
127 },
128 b',' => Ok(Some((Frame::Double(parse_double(line)?), after))),
129 b'(' => Ok(Some((Frame::BigNumber(line), after))),
130 b'$' | b'!' | b'=' => {
131 if line == b"?" {
132 return Err(ProtocolError::Unsupported(kind));
133 }
134 let len = parse_i64(line).ok_or(ProtocolError::InvalidBulkLength)?;
135 if len < 0 {
136 return if kind == b'$' && len == -1 {
138 Ok(Some((Frame::Null, after)))
139 } else {
140 Err(ProtocolError::InvalidBulkLength)
141 };
142 }
143 let len = len as usize;
144 if len > limits.max_bulk {
145 return Err(ProtocolError::InvalidBulkLength);
146 }
147 if buf.len() < after + len + 2 {
148 return Ok(None);
149 }
150 let body = &buf[after..after + len];
151 let end = after + len + 2;
152 match kind {
153 b'$' => Ok(Some((Frame::Bulk(body), end))),
154 b'!' => Ok(Some((Frame::BlobError(body), end))),
155 _ => {
156 if body.len() < 4 || body[3] != b':' {
158 return Err(ProtocolError::InvalidBulkLength);
159 }
160 Ok(Some((
161 Frame::Verbatim {
162 format: &body[..3],
163 text: &body[4..],
164 },
165 end,
166 )))
167 }
168 }
169 }
170 b'*' | b'~' | b'>' => {
171 if line == b"?" {
172 return Err(ProtocolError::Unsupported(kind));
173 }
174 let n = parse_i64(line).ok_or(ProtocolError::InvalidMultibulkLength)?;
175 if n < 0 {
176 return if kind == b'*' && n == -1 {
177 Ok(Some((Frame::Null, after)))
178 } else {
179 Err(ProtocolError::InvalidMultibulkLength)
180 };
181 }
182 let Some((items, end)) = children(buf, after, n as usize, limits, depth)? else {
183 return Ok(None);
184 };
185 Ok(Some((
186 match kind {
187 b'*' => Frame::Array(items),
188 b'~' => Frame::Set(items),
189 _ => Frame::Push(items),
190 },
191 end,
192 )))
193 }
194 b'%' | b'|' => {
195 if line == b"?" {
196 return Err(ProtocolError::Unsupported(kind));
197 }
198 let n = parse_i64(line).ok_or(ProtocolError::InvalidMultibulkLength)?;
199 if n < 0 {
200 return Err(ProtocolError::InvalidMultibulkLength);
201 }
202 let Some((items, end)) = children(buf, after, (n as usize) * 2, limits, depth)? else {
204 return Ok(None);
205 };
206 let mut pairs = Vec::with_capacity(n as usize);
207 let mut it = items.into_iter();
208 while let (Some(k), Some(v)) = (it.next(), it.next()) {
209 pairs.push((k, v));
210 }
211 Ok(Some((
212 if kind == b'%' {
213 Frame::Map(pairs)
214 } else {
215 Frame::Attribute(pairs)
216 },
217 end,
218 )))
219 }
220 other => Err(ProtocolError::UnknownType(other)),
221 }
222}
223
224fn children<'a>(
231 buf: &'a [u8],
232 from: usize,
233 n: usize,
234 limits: &Limits,
235 depth: usize,
236) -> Result<Option<(Vec<Frame<'a>>, usize)>, ProtocolError> {
237 let mut items = Vec::new();
238 let mut at = from;
239 for _ in 0..n {
240 let Some((frame, next)) = decode_at(buf, at, limits, depth + 1)? else {
241 return Ok(None);
242 };
243 items.push(frame);
244 at = next;
245 }
246 Ok(Some((items, at)))
247}
248
249fn parse_double(line: &[u8]) -> Result<f64, ProtocolError> {
251 match line {
252 b"inf" | b"+inf" => return Ok(f64::INFINITY),
253 b"-inf" => return Ok(f64::NEG_INFINITY),
254 b"nan" => return Ok(f64::NAN),
255 _ => {}
256 }
257 core::str::from_utf8(line)
258 .ok()
259 .and_then(|s| s.parse::<f64>().ok())
260 .ok_or(ProtocolError::UnknownType(b','))
261}
262
263fn line_at(buf: &[u8], from: usize) -> Option<(&[u8], usize)> {
265 let off = buf.get(from..)?.iter().position(|&b| b == b'\r')?;
266 let cr = from + off;
267 if buf.get(cr + 1) == Some(&b'\n') {
268 Some((&buf[from..cr], cr + 2))
269 } else {
270 None
271 }
272}
273
274#[cfg(test)]
275mod tests {
276 use super::*;
277 use crate::proto::Proto;
278 use crate::reply::Out;
279
280 fn whole(buf: &[u8]) -> Frame<'_> {
281 let (frame, used) = decode(buf, &Limits::default())
282 .expect("should not be a protocol error")
283 .expect("should be a whole frame");
284 assert_eq!(used, buf.len(), "the frame should use the whole buffer");
285 frame
286 }
287
288 #[test]
289 fn the_resp2_types_decode() {
290 assert_eq!(whole(b"+OK\r\n"), Frame::Simple(b"OK"));
291 assert_eq!(whole(b"-ERR nope\r\n"), Frame::Error(b"ERR nope"));
292 assert_eq!(whole(b":-7\r\n"), Frame::Int(-7));
293 assert_eq!(whole(b"$3\r\nabc\r\n"), Frame::Bulk(b"abc"));
294 assert_eq!(whole(b"$0\r\n\r\n"), Frame::Bulk(b""));
295 assert_eq!(whole(b"$-1\r\n"), Frame::Null);
296 assert_eq!(whole(b"*-1\r\n"), Frame::Null);
297 assert_eq!(
298 whole(b"*2\r\n$1\r\na\r\n:1\r\n"),
299 Frame::Array(vec![Frame::Bulk(b"a"), Frame::Int(1)])
300 );
301 assert_eq!(whole(b"*0\r\n"), Frame::Array(Vec::new()));
302 }
303
304 #[test]
305 fn the_resp3_types_decode() {
306 assert_eq!(whole(b"_\r\n"), Frame::Null);
307 assert_eq!(whole(b"#t\r\n"), Frame::Bool(true));
308 assert_eq!(whole(b"#f\r\n"), Frame::Bool(false));
309 assert_eq!(whole(b",1.5\r\n"), Frame::Double(1.5));
310 assert_eq!(whole(b",inf\r\n"), Frame::Double(f64::INFINITY));
311 assert_eq!(whole(b",-inf\r\n"), Frame::Double(f64::NEG_INFINITY));
312 assert_eq!(
313 whole(b"(12345678901234567890\r\n"),
314 Frame::BigNumber(b"12345678901234567890")
315 );
316 assert_eq!(whole(b"!5\r\nboom!\r\n"), Frame::BlobError(b"boom!"));
317 assert_eq!(
318 whole(b"=15\r\ntxt:Some string\r\n"),
319 Frame::Verbatim {
320 format: b"txt",
321 text: b"Some string"
322 }
323 );
324 assert_eq!(
325 whole(b"%1\r\n$1\r\na\r\n:1\r\n"),
326 Frame::Map(vec![(Frame::Bulk(b"a"), Frame::Int(1))])
327 );
328 assert_eq!(whole(b"~1\r\n:9\r\n"), Frame::Set(vec![Frame::Int(9)]));
329 assert_eq!(whole(b">1\r\n:9\r\n"), Frame::Push(vec![Frame::Int(9)]));
330 assert_eq!(
331 whole(b"|1\r\n$3\r\nttl\r\n:60\r\n"),
332 Frame::Attribute(vec![(Frame::Bulk(b"ttl"), Frame::Int(60))])
333 );
334 }
335
336 #[test]
337 fn a_nan_decodes_even_though_it_never_equals_itself() {
338 let Frame::Double(d) = whole(b",nan\r\n") else {
339 panic!("not a double")
340 };
341 assert!(d.is_nan());
342 }
343
344 #[test]
348 fn every_prefix_of_a_reply_is_incomplete() {
349 let replies: &[&[u8]] = &[
350 b"+OK\r\n",
351 b"$5\r\nhello\r\n",
352 b"*2\r\n$1\r\na\r\n$1\r\nb\r\n",
353 b"%1\r\n$1\r\na\r\n*2\r\n:1\r\n:2\r\n",
354 b"=15\r\ntxt:Some string\r\n",
355 ];
356 for reply in replies {
357 for n in 0..reply.len() {
358 assert_eq!(
359 decode(&reply[..n], &Limits::default()),
360 Ok(None),
361 "{:?} truncated to {n} bytes",
362 core::str::from_utf8(reply).unwrap_or("?")
363 );
364 }
365 assert!(decode(reply, &Limits::default()).unwrap().is_some());
366 }
367 }
368
369 #[test]
370 fn pipelined_replies_come_out_one_at_a_time() {
371 let buf = b"+OK\r\n:1\r\n$3\r\nabc\r\n";
372 let mut at = 0;
373 let mut seen = Vec::new();
374 while let Some((frame, used)) = decode(&buf[at..], &Limits::default()).unwrap() {
375 seen.push(frame);
376 at += used;
377 }
378 assert_eq!(at, buf.len());
379 assert_eq!(
380 seen,
381 vec![Frame::Simple(b"OK"), Frame::Int(1), Frame::Bulk(b"abc")]
382 );
383 }
384
385 #[test]
388 fn a_deeply_nested_reply_is_refused_rather_than_overflowing_the_stack() {
389 let mut buf = Vec::new();
390 for _ in 0..10_000 {
391 buf.extend_from_slice(b"*1\r\n");
392 }
393 buf.extend_from_slice(b":1\r\n");
394 assert_eq!(
395 decode(&buf, &Limits::default()),
396 Err(ProtocolError::TooDeep)
397 );
398 }
399
400 #[test]
403 fn an_enormous_element_count_does_not_reserve_anything() {
404 assert_eq!(
405 decode(b"*4000000000\r\n", &Limits::default()),
406 Ok(None),
407 "it should be waiting for elements, not allocating for them"
408 );
409 }
410
411 #[test]
412 fn the_streamed_forms_say_so_rather_than_being_mis_parsed() {
413 for buf in [&b"$?\r\n"[..], b"*?\r\n", b"%?\r\n", b"~?\r\n"] {
414 assert!(
415 matches!(
416 decode(buf, &Limits::default()),
417 Err(ProtocolError::Unsupported(_))
418 ),
419 "{buf:?}"
420 );
421 }
422 }
423
424 #[test]
425 fn an_unknown_type_byte_is_named() {
426 assert_eq!(
427 decode(b"@1\r\n", &Limits::default()),
428 Err(ProtocolError::UnknownType(b'@'))
429 );
430 }
431
432 #[test]
437 fn everything_the_encoder_writes_reads_back() {
438 for proto in [Proto::Resp2, Proto::Resp3] {
439 let mut out = Out::new(proto);
440 out.simple(b"OK");
441 out.error(b"ERR nope");
442 out.int(-7);
443 out.bulk(b"hello");
444 out.nil();
445 out.nil_array();
446 out.bool(true);
447 out.double(1.5);
448 out.verbatim(b"txt", b"note");
449 out.big_number(b"123456789012345678901234567890");
450 out.array(2);
451 out.bulk(b"a");
452 out.int(1);
453 out.map(1);
454 out.bulk(b"k");
455 out.bulk(b"v");
456 out.set(1);
457 out.bulk(b"m");
458 out.push(2);
459 out.bulk(b"message");
460 out.bulk(b"ch");
461
462 let buf = out.into_inner();
463 let mut at = 0;
464 let mut count = 0;
465 while at < buf.len() {
466 let (_, used) = decode(&buf[at..], &Limits::default())
467 .unwrap_or_else(|e| panic!("{proto:?} produced bytes that do not parse: {e}"))
468 .unwrap_or_else(|| panic!("{proto:?} produced a truncated frame at {at}"));
469 at += used;
470 count += 1;
471 }
472 assert_eq!(at, buf.len());
473 assert_eq!(count, 14, "{proto:?} top level frames");
477 }
478 }
479}