1use crate::error::ProtocolError;
31use crate::proto::Limits;
32use yo_common::num::parse_i64;
33
34#[derive(Debug, Clone, Copy)]
36struct Span {
37 start: usize,
38 len: u32,
39 scratch: bool,
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub enum Step {
47 Incomplete,
49 Command {
57 consumed: usize,
59 },
60}
61
62#[derive(Debug, Default)]
67pub struct Argv {
68 spans: Vec<Span>,
69 scratch: Vec<u8>,
72 next: usize,
74 want: Option<u32>,
76}
77
78impl Argv {
79 pub fn new() -> Argv {
81 Argv::default()
82 }
83
84 pub fn with_capacity(n: usize) -> Argv {
89 Argv {
90 spans: Vec::with_capacity(n),
91 ..Argv::default()
92 }
93 }
94
95 #[cfg(test)]
101 pub(crate) fn room(&self) -> usize {
102 self.spans.capacity()
103 }
104
105 pub fn reset(&mut self) {
111 self.spans.clear();
112 self.scratch.clear();
113 self.next = 0;
114 self.want = None;
115 }
116
117 pub fn len(&self) -> usize {
119 self.spans.len()
120 }
121
122 pub fn is_empty(&self) -> bool {
124 self.spans.is_empty()
125 }
126
127 pub fn arg<'a>(&'a self, buf: &'a [u8], i: usize) -> Option<&'a [u8]> {
132 let s = self.spans.get(i)?;
133 let src = if s.scratch { &self.scratch[..] } else { buf };
134 src.get(s.start..s.start + s.len as usize)
135 }
136
137 pub fn args<'a>(&'a self, buf: &'a [u8]) -> impl Iterator<Item = &'a [u8]> {
139 (0..self.spans.len()).filter_map(move |i| self.arg(buf, i))
140 }
141
142 pub fn decode(&mut self, buf: &[u8], limits: &Limits) -> Result<Step, ProtocolError> {
151 if self.want.is_none() {
152 self.spans.clear();
156 self.scratch.clear();
157 self.next = 0;
158
159 if buf.is_empty() {
160 return Ok(Step::Incomplete);
161 }
162 if buf[0] != b'*' {
163 return self.inline(buf, limits);
164 }
165 let Some((line, after)) = line_at(buf, 1, ProtocolError::InvalidMultibulkLength)?
166 else {
167 return if buf.len() > limits.max_inline {
171 Err(ProtocolError::TooBigMbulkCount)
172 } else {
173 Ok(Step::Incomplete)
174 };
175 };
176 let count = parse_i64(line).ok_or(ProtocolError::InvalidMultibulkLength)?;
177 if count > limits.max_multibulk as i64 {
178 return Err(ProtocolError::InvalidMultibulkLength);
179 }
180 if count <= 0 {
181 return Ok(Step::Command { consumed: after });
184 }
185 self.spans.reserve(count as usize);
188 self.next = after;
189 self.want = Some(count as u32);
190 }
191
192 while self.want.is_some_and(|w| w > 0) {
193 let Some(&kind) = buf.get(self.next) else {
194 return Ok(Step::Incomplete);
195 };
196 if kind != b'$' {
197 return Err(ProtocolError::ExpectedDollar(kind));
198 }
199 let Some((line, after)) =
200 line_at(buf, self.next + 1, ProtocolError::InvalidBulkLength)?
201 else {
202 return if buf.len() - self.next > limits.max_inline {
203 Err(ProtocolError::TooBigBulkCount)
204 } else {
205 Ok(Step::Incomplete)
206 };
207 };
208 let len = parse_i64(line).ok_or(ProtocolError::InvalidBulkLength)?;
209 if len < 0 || len > limits.max_bulk as i64 {
210 return Err(ProtocolError::InvalidBulkLength);
211 }
212 let len = len as usize;
213 if buf.len() < after + len + 2 {
217 return Ok(Step::Incomplete);
218 }
219 self.spans.push(Span {
220 start: after,
221 len: len as u32,
222 scratch: false,
223 });
224 self.next = after + len + 2;
225 self.want = self.want.map(|w| w - 1);
226 }
227
228 let consumed = self.next;
229 self.want = None;
230 self.next = 0;
231 Ok(Step::Command { consumed })
232 }
233
234 fn inline(&mut self, buf: &[u8], limits: &Limits) -> Result<Step, ProtocolError> {
240 let Some(nl) = buf.iter().position(|&b| b == b'\n') else {
241 return if buf.len() > limits.max_inline {
242 Err(ProtocolError::TooBigInline)
243 } else {
244 Ok(Step::Incomplete)
245 };
246 };
247 let mut line = &buf[..nl];
248 if line.last() == Some(&b'\r') {
249 line = &line[..line.len() - 1];
250 }
251 self.split_inline(line)?;
252 Ok(Step::Command { consumed: nl + 1 })
253 }
254
255 fn split_inline(&mut self, line: &[u8]) -> Result<(), ProtocolError> {
263 let mut i = 0;
264 loop {
265 while i < line.len() && is_space(line[i]) {
266 i += 1;
267 }
268 if i >= line.len() {
269 return Ok(());
270 }
271 let start = self.scratch.len();
272 let mut in_double = false;
273 let mut in_single = false;
274 let mut done = false;
275 while !done {
276 let c = line.get(i).copied();
277 if in_double {
278 match c {
279 Some(b'\\')
280 if i + 3 < line.len()
281 && line[i + 1] == b'x'
282 && hex(line[i + 2]).is_some()
283 && hex(line[i + 3]).is_some() =>
284 {
285 let hi = hex(line[i + 2]).unwrap_or(0);
286 let lo = hex(line[i + 3]).unwrap_or(0);
287 self.scratch.push(hi * 16 + lo);
288 i += 3;
289 }
290 Some(b'\\') if i + 1 < line.len() => {
291 i += 1;
292 self.scratch.push(match line[i] {
293 b'n' => b'\n',
294 b'r' => b'\r',
295 b't' => b'\t',
296 b'b' => 0x08,
297 b'a' => 0x07,
298 other => other,
299 });
300 }
301 Some(b'"') => {
302 if line.get(i + 1).is_some_and(|&n| !is_space(n)) {
303 return Err(ProtocolError::UnbalancedQuotes);
304 }
305 done = true;
306 }
307 None => return Err(ProtocolError::UnbalancedQuotes),
308 Some(ch) => self.scratch.push(ch),
309 }
310 } else if in_single {
311 match c {
312 Some(b'\\') if line.get(i + 1) == Some(&b'\'') => {
313 i += 1;
314 self.scratch.push(b'\'');
315 }
316 Some(b'\'') => {
317 if line.get(i + 1).is_some_and(|&n| !is_space(n)) {
318 return Err(ProtocolError::UnbalancedQuotes);
319 }
320 done = true;
321 }
322 None => return Err(ProtocolError::UnbalancedQuotes),
323 Some(ch) => self.scratch.push(ch),
324 }
325 } else {
326 match c {
327 None | Some(b' ') | Some(b'\n') | Some(b'\r') | Some(b'\t') => done = true,
328 Some(b'"') => in_double = true,
329 Some(b'\'') => in_single = true,
330 Some(ch) => self.scratch.push(ch),
331 }
332 }
333 if i < line.len() {
334 i += 1;
335 }
336 }
337 let len = self.scratch.len() - start;
338 self.spans.push(Span {
339 start,
340 len: len as u32,
341 scratch: true,
342 });
343 }
344 }
345}
346
347#[inline]
349const fn is_space(b: u8) -> bool {
350 matches!(b, b' ' | b'\t' | b'\n' | 0x0b | 0x0c | b'\r')
351}
352
353#[inline]
355const fn hex(b: u8) -> Option<u8> {
356 match b {
357 b'0'..=b'9' => Some(b - b'0'),
358 b'a'..=b'f' => Some(b - b'a' + 10),
359 b'A'..=b'F' => Some(b - b'A' + 10),
360 _ => None,
361 }
362}
363
364fn line_at(
372 buf: &[u8],
373 from: usize,
374 bad: ProtocolError,
375) -> Result<Option<(&[u8], usize)>, ProtocolError> {
376 let Some(off) = buf[from..].iter().position(|&b| b == b'\r') else {
377 return Ok(None);
378 };
379 let cr = from + off;
380 match buf.get(cr + 1) {
381 None => Ok(None),
382 Some(&b'\n') => Ok(Some((&buf[from..cr], cr + 2))),
383 Some(_) => Err(bad),
384 }
385}
386
387#[cfg(test)]
388mod tests {
389 use super::*;
390
391 fn one(buf: &[u8]) -> Result<(Vec<Vec<u8>>, usize), ProtocolError> {
394 let mut argv = Argv::new();
395 match argv.decode(buf, &Limits::default())? {
396 Step::Incomplete => panic!("expected a whole command in {buf:?}"),
397 Step::Command { consumed } => Ok((
398 argv.args(buf).map(<[u8]>::to_vec).collect::<Vec<_>>(),
399 consumed,
400 )),
401 }
402 }
403
404 fn words(buf: &[u8]) -> Vec<Vec<u8>> {
405 one(buf).expect("should decode").0
406 }
407
408 #[test]
409 fn a_multibulk_command_comes_out_as_its_arguments() {
410 let (args, consumed) = one(b"*3\r\n$3\r\nSET\r\n$1\r\nk\r\n$1\r\nv\r\n").unwrap();
411 assert_eq!(args, vec![b"SET".to_vec(), b"k".to_vec(), b"v".to_vec()]);
412 assert_eq!(consumed, 27);
413 }
414
415 #[test]
416 fn an_empty_argument_is_an_argument() {
417 assert_eq!(
418 words(b"*2\r\n$3\r\nGET\r\n$0\r\n\r\n"),
419 vec![b"GET".to_vec(), Vec::new()]
420 );
421 }
422
423 #[test]
424 fn a_value_can_hold_anything_including_crlf() {
425 let args = words(b"*3\r\n$3\r\nSET\r\n$1\r\nk\r\n$4\r\na\r\nb\r\n");
426 assert_eq!(args[2], b"a\r\nb".to_vec());
427 }
428
429 #[test]
432 fn commands_come_out_one_at_a_time_from_one_buffer() {
433 let buf = b"*1\r\n$4\r\nPING\r\n*2\r\n$3\r\nGET\r\n$1\r\nk\r\n";
434 let mut argv = Argv::new();
435 let mut at = 0;
436 let mut seen: Vec<Vec<Vec<u8>>> = Vec::new();
437 loop {
438 match argv.decode(&buf[at..], &Limits::default()).unwrap() {
439 Step::Incomplete => break,
440 Step::Command { consumed } => {
441 seen.push(argv.args(&buf[at..]).map(<[u8]>::to_vec).collect());
442 at += consumed;
443 }
444 }
445 }
446 assert_eq!(at, buf.len());
447 assert_eq!(seen.len(), 2);
448 assert_eq!(seen[0], vec![b"PING".to_vec()]);
449 assert_eq!(seen[1], vec![b"GET".to_vec(), b"k".to_vec()]);
450 }
451
452 #[test]
457 fn a_command_arriving_one_byte_at_a_time_decodes_once_at_the_end() {
458 let whole = b"*3\r\n$3\r\nSET\r\n$5\r\nhello\r\n$5\r\nworld\r\n";
459 let mut argv = Argv::new();
460 for n in 0..whole.len() {
461 assert_eq!(
462 argv.decode(&whole[..n], &Limits::default()).unwrap(),
463 Step::Incomplete,
464 "the first {n} bytes should not be a command"
465 );
466 }
467 let step = argv.decode(whole, &Limits::default()).unwrap();
468 assert_eq!(
469 step,
470 Step::Command {
471 consumed: whole.len()
472 }
473 );
474 assert_eq!(
475 argv.args(whole).map(<[u8]>::to_vec).collect::<Vec<_>>(),
476 vec![b"SET".to_vec(), b"hello".to_vec(), b"world".to_vec()]
477 );
478 }
479
480 #[test]
485 fn a_partly_arrived_command_remembers_where_it_got_to() {
486 let mut argv = Argv::new();
487 let head = b"*3\r\n$3\r\nSET\r\n$1\r\nk\r\n$10\r\nabc";
488 assert_eq!(
489 argv.decode(head, &Limits::default()).unwrap(),
490 Step::Incomplete
491 );
492 assert_eq!(argv.want, Some(1), "two of three arguments are in");
493 assert_eq!(argv.next, 20, "the third argument's body starts here");
494 }
495
496 #[test]
497 fn an_empty_command_is_consumed_and_has_no_arguments() {
498 for buf in [&b"*0\r\n"[..], b"*-1\r\n"] {
499 let (args, consumed) = one(buf).unwrap();
500 assert!(args.is_empty(), "{buf:?}");
501 assert_eq!(consumed, buf.len());
502 }
503 }
504
505 #[test]
506 fn an_inline_command_is_split_on_whitespace() {
507 assert_eq!(words(b"PING\r\n"), vec![b"PING".to_vec()]);
508 assert_eq!(
509 words(b"SET key value\n"),
510 vec![b"SET".to_vec(), b"key".to_vec(), b"value".to_vec()]
511 );
512 assert!(words(b"\r\n").is_empty());
513 assert!(words(b" \n").is_empty());
514 }
515
516 #[test]
517 fn inline_quotes_and_escapes_follow_redis() {
518 assert_eq!(
519 words(b"SET k \"a b\"\r\n"),
520 vec![b"SET".to_vec(), b"k".to_vec(), b"a b".to_vec()]
521 );
522 assert_eq!(words(b"ECHO \"\\x41\\x42\"\r\n")[1], b"AB".to_vec());
523 assert_eq!(words(b"ECHO \"a\\nb\"\r\n")[1], b"a\nb".to_vec());
524 assert_eq!(words(b"ECHO 'it\\'s'\r\n")[1], b"it's".to_vec());
525 assert_eq!(words(b"ECHO \"\"\r\n")[1], Vec::<u8>::new());
526 }
527
528 #[test]
529 fn an_unclosed_or_misplaced_quote_is_an_error() {
530 for bad in [
531 &b"ECHO \"abc\r\n"[..],
532 b"ECHO 'abc\r\n",
533 b"ECHO \"abc\"d\r\n",
534 b"ECHO 'abc'd\r\n",
535 ] {
536 let mut argv = Argv::new();
537 assert_eq!(
538 argv.decode(bad, &Limits::default()),
539 Err(ProtocolError::UnbalancedQuotes),
540 "{bad:?}"
541 );
542 }
543 }
544
545 #[test]
546 fn the_wrong_type_byte_where_an_argument_belongs_names_the_byte() {
547 let mut argv = Argv::new();
548 assert_eq!(
549 argv.decode(b"*1\r\n+OK\r\n", &Limits::default()),
550 Err(ProtocolError::ExpectedDollar(b'+'))
551 );
552 }
553
554 #[test]
555 fn lengths_that_are_not_lengths_are_refused() {
556 let cases: &[(&[u8], ProtocolError)] = &[
557 (b"*x\r\n", ProtocolError::InvalidMultibulkLength),
558 (b"*\r\n", ProtocolError::InvalidMultibulkLength),
559 (b"*01\r\n", ProtocolError::InvalidMultibulkLength),
560 (
561 b"*99999999999999999999\r\n",
562 ProtocolError::InvalidMultibulkLength,
563 ),
564 (b"*2\r\n$x\r\n", ProtocolError::InvalidBulkLength),
565 (b"*2\r\n$-1\r\n", ProtocolError::InvalidBulkLength),
566 ];
567 for &(buf, want) in cases {
568 let mut argv = Argv::new();
569 assert_eq!(argv.decode(buf, &Limits::default()), Err(want), "{buf:?}");
570 }
571 }
572
573 #[test]
577 fn an_enormous_count_is_refused_rather_than_reserved() {
578 let mut argv = Argv::new();
579 assert_eq!(
580 argv.decode(b"*2000000000\r\n", &Limits::default()),
581 Err(ProtocolError::InvalidMultibulkLength)
582 );
583 assert_eq!(
584 argv.spans.capacity(),
585 0,
586 "nothing should have been reserved"
587 );
588 }
589
590 #[test]
591 fn a_bulk_past_the_limit_is_refused() {
592 let limits = Limits {
593 max_bulk: 16,
594 ..Limits::default()
595 };
596 let mut argv = Argv::new();
597 assert_eq!(
598 argv.decode(b"*1\r\n$17\r\n", &limits),
599 Err(ProtocolError::InvalidBulkLength)
600 );
601 let mut argv = Argv::new();
603 assert_eq!(argv.decode(b"*1\r\n$16\r\n", &limits), Ok(Step::Incomplete));
604 }
605
606 #[test]
607 fn a_line_that_never_ends_is_refused_rather_than_buffered_forever() {
608 let limits = Limits {
609 max_inline: 8,
610 ..Limits::default()
611 };
612 let mut argv = Argv::new();
613 assert_eq!(
614 argv.decode(b"*123456789", &limits),
615 Err(ProtocolError::TooBigMbulkCount)
616 );
617 let mut argv = Argv::new();
618 assert_eq!(
619 argv.decode(b"*1\r\n$123456789", &limits),
620 Err(ProtocolError::TooBigBulkCount)
621 );
622 let mut argv = Argv::new();
623 assert_eq!(
624 argv.decode(b"PING PING PING", &limits),
625 Err(ProtocolError::TooBigInline)
626 );
627 }
628
629 #[test]
630 fn a_carriage_return_with_no_newline_after_it_is_a_protocol_error() {
631 let mut argv = Argv::new();
632 assert_eq!(
633 argv.decode(b"*1\rx", &Limits::default()),
634 Err(ProtocolError::InvalidMultibulkLength)
635 );
636 }
637
638 #[test]
639 fn a_reset_forgets_a_half_read_command() {
640 let mut argv = Argv::new();
641 assert_eq!(
642 argv.decode(b"*2\r\n$3\r\nGET\r\n", &Limits::default())
643 .unwrap(),
644 Step::Incomplete
645 );
646 assert_eq!(argv.want, Some(1));
647 argv.reset();
648 assert_eq!(argv.want, None);
649 assert_eq!(argv.next, 0);
650 assert_eq!(
652 argv.decode(b"*1\r\n$4\r\nPING\r\n", &Limits::default())
653 .unwrap(),
654 Step::Command { consumed: 14 }
655 );
656 }
657
658 #[test]
662 fn arguments_do_not_survive_into_the_next_command() {
663 let mut argv = Argv::new();
664 argv.decode(b"*2\r\n$3\r\nGET\r\n$1\r\nk\r\n", &Limits::default())
665 .unwrap();
666 assert_eq!(argv.len(), 2);
667 argv.decode(b"*1\r\n$4\r\nPING\r\n", &Limits::default())
668 .unwrap();
669 assert_eq!(argv.len(), 1);
670 assert_eq!(argv.arg(b"*1\r\n$4\r\nPING\r\n", 1), None);
671 }
672}