1use bytes::{Buf, BufMut, Bytes, BytesMut};
2
3use crate::codec::DecodeOutcome;
4use crate::error::{Error, ErrorKind};
5use crate::response::Response;
6use crate::types::{
7 MetaCode, MetaFlags, MetaResponse, Op, Protocol, ReplyMode, Request, RequestMeta, StatLine,
8 ValueEntry,
9};
10
11#[derive(Debug, Clone, Copy)]
13pub struct AsciiLimits {
14 pub max_line_len: usize,
15 pub max_blob_len: usize,
16}
17
18impl Default for AsciiLimits {
19 fn default() -> Self {
20 Self {
21 max_line_len: 4096,
22 max_blob_len: 1 << 20,
23 }
24 }
25}
26
27#[derive(Debug, Clone)]
28struct BlobState {
29 len: usize,
30 req: Request,
31 meta: RequestMeta,
32}
33
34#[derive(Debug, Clone)]
35enum AsciiState {
36 Line,
37 Blob(Box<BlobState>),
38}
39
40#[derive(Debug, Clone)]
42pub struct AsciiDecoder {
43 state: AsciiState,
44 discarding_line: bool,
45}
46
47impl Default for AsciiDecoder {
48 fn default() -> Self {
49 Self::new()
50 }
51}
52
53impl AsciiDecoder {
54 pub fn new() -> Self {
55 Self {
56 state: AsciiState::Line,
57 discarding_line: false,
58 }
59 }
60
61 pub fn decode(&mut self, buf: &mut BytesMut, limits: AsciiLimits) -> Option<DecodeOutcome> {
62 let state = std::mem::replace(&mut self.state, AsciiState::Line);
63 match state {
64 AsciiState::Line => {
65 self.state = AsciiState::Line;
66 self.decode_line(buf, limits)
67 }
68 AsciiState::Blob(blob) => {
69 let BlobState { len, mut req, meta } = *blob;
70 if buf.len() < len + 2 {
71 self.state = AsciiState::Blob(Box::new(BlobState { len, req, meta }));
72 return None;
73 }
74 let payload = buf.split_to(len).freeze();
75 let cr = buf.get_u8();
76 let lf = buf.get_u8();
77 if cr != b'\r' || lf != b'\n' {
78 let response = Response::Error(Error::client("bad data chunk"));
79 self.state = AsciiState::Line;
80 return Some(DecodeOutcome::Response(meta, response));
81 }
82 req.value = Some(payload);
83 self.state = AsciiState::Line;
84 Some(DecodeOutcome::Request(req, meta))
85 }
86 }
87 }
88
89 fn decode_line(&mut self, buf: &mut BytesMut, limits: AsciiLimits) -> Option<DecodeOutcome> {
90 if self.discarding_line {
91 if let Some(pos) = find_crlf(buf) {
92 buf.advance(pos + 2);
93 self.discarding_line = false;
94 let meta = RequestMeta::ascii();
95 let response = Response::Error(Error::client("line too long"));
96 return Some(DecodeOutcome::Response(meta, response));
97 }
98 if buf.len() > limits.max_line_len {
99 buf.clear();
100 }
101 return None;
102 }
103
104 let pos = match find_crlf(buf) {
105 Some(pos) => pos,
106 None => {
107 if buf.len() > limits.max_line_len {
108 self.discarding_line = true;
109 }
110 return None;
111 }
112 };
113
114 if pos > limits.max_line_len {
115 buf.advance(pos + 2);
116 let meta = RequestMeta::ascii();
117 let response = Response::Error(Error::client("line too long"));
118 return Some(DecodeOutcome::Response(meta, response));
119 }
120
121 let line = buf.split_to(pos).freeze();
122 buf.advance(2);
123
124 if line.is_empty() {
125 let meta = RequestMeta::ascii();
126 let response = Response::Error(Error::client("empty command"));
127 return Some(DecodeOutcome::Response(meta, response));
128 }
129
130 match parse_line(line, limits.max_blob_len) {
131 Ok(LineParse::Line(req, meta)) => Some(DecodeOutcome::Request(req, meta)),
132 Ok(LineParse::Blob { len, mut req, meta }) => {
133 if buf.len() >= len + 2 {
134 let payload = buf.split_to(len).freeze();
135 let cr = buf.get_u8();
136 let lf = buf.get_u8();
137 if cr != b'\r' || lf != b'\n' {
138 let response = Response::Error(Error::client("bad data chunk"));
139 return Some(DecodeOutcome::Response(meta, response));
140 }
141 req.value = Some(payload);
142 return Some(DecodeOutcome::Request(req, meta));
143 }
144 self.state = AsciiState::Blob(Box::new(BlobState { len, req, meta }));
145 None
146 }
147 Err(err) => Some(DecodeOutcome::Response(
148 RequestMeta::ascii(),
149 Response::Error(err),
150 )),
151 }
152 }
153}
154
155enum LineParse {
156 Line(Request, RequestMeta),
157 Blob {
158 len: usize,
159 req: Request,
160 meta: RequestMeta,
161 },
162}
163
164fn parse_line(line: Bytes, max_blob_len: usize) -> Result<LineParse, Error> {
165 let tokens = split_tokens(&line);
166 if tokens.is_empty() {
167 return Err(Error::client("empty command"));
168 }
169
170 let cmd = tokens[0].as_ref();
171 let op = parse_op(cmd);
172 let mut meta = RequestMeta::ascii();
173 let mut req = Request::new(op);
174
175 match op {
176 Op::Get | Op::Gets => {
177 if tokens.len() < 2 {
178 return Err(Error::client("missing key"));
179 }
180 let keys = parse_keys(&tokens[1..])?;
181 if keys.len() == 1 {
182 req.key = Some(keys[0].clone());
183 } else {
184 req.keys = keys;
185 }
186 }
187 Op::Gat | Op::Gats => {
188 if tokens.len() < 3 {
189 return Err(Error::client("missing exptime or key"));
190 }
191 let exptime =
192 parse_i64(tokens[1].as_ref()).ok_or_else(|| Error::client("invalid exptime"))?;
193 req.exptime = Some(exptime);
194 let keys = parse_keys(&tokens[2..])?;
195 if keys.len() == 1 {
196 req.key = Some(keys[0].clone());
197 } else {
198 req.keys = keys;
199 }
200 }
201 Op::Set | Op::Add | Op::Replace | Op::Append | Op::Prepend | Op::Cas => {
202 let min = if matches!(op, Op::Cas) { 6 } else { 5 };
203 if tokens.len() < min {
204 return Err(Error::client("missing arguments"));
205 }
206 let mut end = tokens.len();
207 let mut reply = ReplyMode::Always;
208 if tokens.len() > min && tokens.last().map(|t| t.as_ref()) == Some(b"noreply") {
209 reply = ReplyMode::SuppressSuccess;
210 end -= 1;
211 }
212 if end != min {
213 return Err(Error::client("invalid arguments"));
214 }
215 let key = tokens[1].clone();
216 validate_key(&key)?;
217 req.key = Some(key);
218 req.flags =
219 Some(parse_u32(tokens[2].as_ref()).ok_or_else(|| Error::client("invalid flags"))?);
220 req.exptime = Some(
221 parse_i64(tokens[3].as_ref()).ok_or_else(|| Error::client("invalid exptime"))?,
222 );
223 let bytes =
224 parse_usize(tokens[4].as_ref()).ok_or_else(|| Error::client("invalid bytes"))?;
225 if bytes > max_blob_len {
226 return Err(Error::server("value too large"));
227 }
228 if matches!(op, Op::Cas) {
229 req.cas = Some(
230 parse_u64(tokens[5].as_ref()).ok_or_else(|| Error::client("invalid cas"))?,
231 );
232 }
233 meta.reply = reply;
234 return Ok(LineParse::Blob {
235 len: bytes,
236 req,
237 meta,
238 });
239 }
240 Op::Delete => {
241 if tokens.len() < 2 {
242 return Err(Error::client("missing key"));
243 }
244 let mut reply = ReplyMode::Always;
245 if tokens.len() > 2 && tokens.last().map(|t| t.as_ref()) == Some(b"noreply") {
246 reply = ReplyMode::SuppressSuccess;
247 if tokens.len() != 3 {
248 return Err(Error::client("invalid arguments"));
249 }
250 } else if tokens.len() != 2 {
251 return Err(Error::client("invalid arguments"));
252 }
253 let key = tokens[1].clone();
254 validate_key(&key)?;
255 req.key = Some(key);
256 meta.reply = reply;
257 }
258 Op::Incr | Op::Decr => {
259 if tokens.len() < 3 {
260 return Err(Error::client("missing arguments"));
261 }
262 let mut reply = ReplyMode::Always;
263 if tokens.len() > 3 && tokens.last().map(|t| t.as_ref()) == Some(b"noreply") {
264 reply = ReplyMode::SuppressSuccess;
265 if tokens.len() != 4 {
266 return Err(Error::client("invalid arguments"));
267 }
268 } else if tokens.len() != 3 {
269 return Err(Error::client("invalid arguments"));
270 }
271 let key = tokens[1].clone();
272 validate_key(&key)?;
273 req.key = Some(key);
274 req.delta =
275 Some(parse_u64(tokens[2].as_ref()).ok_or_else(|| Error::client("invalid delta"))?);
276 meta.reply = reply;
277 }
278 Op::Touch => {
279 if tokens.len() < 3 {
280 return Err(Error::client("missing arguments"));
281 }
282 let mut reply = ReplyMode::Always;
283 if tokens.len() > 3 && tokens.last().map(|t| t.as_ref()) == Some(b"noreply") {
284 reply = ReplyMode::SuppressSuccess;
285 if tokens.len() != 4 {
286 return Err(Error::client("invalid arguments"));
287 }
288 } else if tokens.len() != 3 {
289 return Err(Error::client("invalid arguments"));
290 }
291 let key = tokens[1].clone();
292 validate_key(&key)?;
293 req.key = Some(key);
294 req.exptime = Some(
295 parse_i64(tokens[2].as_ref()).ok_or_else(|| Error::client("invalid exptime"))?,
296 );
297 meta.reply = reply;
298 }
299 Op::Stats => {
300 if tokens.len() > 1 {
301 let keys = parse_keys(&tokens[1..])?;
302 req.keys = keys;
303 }
304 }
305 Op::Version | Op::Quit => {
306 if tokens.len() != 1 {
307 return Err(Error::client("invalid arguments"));
308 }
309 }
310 Op::MetaGet
311 | Op::MetaSet
312 | Op::MetaDelete
313 | Op::MetaArithmetic
314 | Op::MetaDebug
315 | Op::MetaNoop => {
316 meta.protocol = Protocol::Meta;
317 if op == Op::MetaNoop {
318 if tokens.len() != 1 {
319 return Err(Error::client("invalid arguments"));
320 }
321 return Ok(LineParse::Line(req, meta));
322 }
323 if tokens.len() < 2 {
324 return Err(Error::client("missing key"));
325 }
326 let mut idx = 2;
327 let mut datalen = None;
328 if op == Op::MetaSet {
329 if tokens.len() < 3 {
330 return Err(Error::client("missing data length"));
331 }
332 datalen = Some(
333 parse_usize(tokens[2].as_ref())
334 .ok_or_else(|| Error::client("invalid data length"))?,
335 );
336 idx = 3;
337 }
338 let meta_flags = parse_meta_flags(&tokens[idx..]);
339 if meta_flags.has(b'q') {
340 meta.reply = if op == Op::MetaGet {
341 ReplyMode::SuppressMiss
342 } else {
343 ReplyMode::SuppressSuccess
344 };
345 }
346 let mut key = tokens[1].clone();
347 if meta_flags.has(b'b') {
348 let decoded = decode_base64(key.as_ref())?;
349 if decoded.len() > 250 {
350 return Err(Error::client("key too long"));
351 }
352 key = Bytes::from(decoded);
353 } else {
354 validate_key(&key)?;
355 }
356 req.key = Some(key);
357 if let Some(delta) = meta_flags
358 .token(b'D')
359 .and_then(|token| parse_u64(token.as_ref()))
360 {
361 req.delta = Some(delta);
362 }
363 if let Some(initial) = meta_flags
364 .token(b'J')
365 .and_then(|token| parse_u64(token.as_ref()))
366 {
367 req.initial = Some(initial);
368 }
369 if let Some(cas) = meta_flags
370 .token(b'C')
371 .and_then(|token| parse_u64(token.as_ref()))
372 {
373 req.cas = Some(cas);
374 }
375 req.meta = Some(meta_flags.clone());
376 if op == Op::MetaSet {
377 let mut length = datalen.unwrap_or(0);
378 if let Some(slen) = meta_flags
379 .token(b'S')
380 .and_then(|token| parse_usize(token.as_ref()))
381 {
382 length = slen;
383 }
384 if length > max_blob_len {
385 return Err(Error::server("value too large"));
386 }
387 if length == 0 {
388 return Ok(LineParse::Line(req, meta));
389 }
390 return Ok(LineParse::Blob {
391 len: length,
392 req,
393 meta,
394 });
395 }
396 }
397 Op::Noop | Op::Unknown => {
398 if op == Op::Unknown {
399 req.op = Op::Unknown;
400 }
401 }
402 }
403
404 Ok(LineParse::Line(req, meta))
405}
406
407fn parse_op(cmd: &[u8]) -> Op {
408 match cmd {
409 b"get" => Op::Get,
410 b"gets" => Op::Gets,
411 b"gat" => Op::Gat,
412 b"gats" => Op::Gats,
413 b"set" => Op::Set,
414 b"add" => Op::Add,
415 b"replace" => Op::Replace,
416 b"append" => Op::Append,
417 b"prepend" => Op::Prepend,
418 b"cas" => Op::Cas,
419 b"delete" => Op::Delete,
420 b"incr" => Op::Incr,
421 b"decr" => Op::Decr,
422 b"touch" => Op::Touch,
423 b"stats" => Op::Stats,
424 b"version" => Op::Version,
425 b"quit" => Op::Quit,
426 b"mg" => Op::MetaGet,
427 b"ms" => Op::MetaSet,
428 b"md" => Op::MetaDelete,
429 b"ma" => Op::MetaArithmetic,
430 b"me" => Op::MetaDebug,
431 b"mn" => Op::MetaNoop,
432 _ => Op::Unknown,
433 }
434}
435
436fn parse_keys(tokens: &[Bytes]) -> Result<Vec<Bytes>, Error> {
437 let mut keys = Vec::with_capacity(tokens.len());
438 for token in tokens {
439 validate_key(token)?;
440 keys.push(token.clone());
441 }
442 Ok(keys)
443}
444
445fn validate_key(key: &Bytes) -> Result<(), Error> {
446 if key.is_empty() {
447 return Err(Error::client("empty key"));
448 }
449 if key.len() > 250 {
450 return Err(Error::client("key too long"));
451 }
452 for &b in key.as_ref() {
453 if b <= b' ' || b == 0x7f {
454 return Err(Error::client("invalid key"));
455 }
456 }
457 Ok(())
458}
459
460fn split_tokens(line: &Bytes) -> Vec<Bytes> {
461 let mut tokens = Vec::new();
462 let mut start = 0;
463 let bytes = line.as_ref();
464 while start < bytes.len() {
465 while start < bytes.len() && bytes[start] == b' ' {
466 start += 1;
467 }
468 if start >= bytes.len() {
469 break;
470 }
471 let mut end = start;
472 while end < bytes.len() && bytes[end] != b' ' {
473 end += 1;
474 }
475 tokens.push(line.slice(start..end));
476 start = end;
477 }
478 tokens
479}
480
481fn find_crlf(buf: &BytesMut) -> Option<usize> {
482 let bytes = buf.as_ref();
483 if bytes.len() < 2 {
484 return None;
485 }
486 let mut i = 0;
487 while i + 1 < bytes.len() {
488 if bytes[i] == b'\r' && bytes[i + 1] == b'\n' {
489 return Some(i);
490 }
491 i += 1;
492 }
493 None
494}
495
496fn parse_u32(token: &[u8]) -> Option<u32> {
497 parse_u64(token).and_then(|value| u32::try_from(value).ok())
498}
499
500fn parse_usize(token: &[u8]) -> Option<usize> {
501 parse_u64(token).and_then(|value| usize::try_from(value).ok())
502}
503
504fn parse_u64(token: &[u8]) -> Option<u64> {
505 if token.is_empty() {
506 return None;
507 }
508 let mut value: u64 = 0;
509 for &b in token {
510 if !b.is_ascii_digit() {
511 return None;
512 }
513 value = value.checked_mul(10)?;
514 value = value.checked_add((b - b'0') as u64)?;
515 }
516 Some(value)
517}
518
519fn parse_i64(token: &[u8]) -> Option<i64> {
520 if token.is_empty() {
521 return None;
522 }
523 let (neg, rest) = if token[0] == b'-' {
524 (true, &token[1..])
525 } else {
526 (false, token)
527 };
528 let value = parse_u64(rest)? as i64;
529 if neg { Some(-value) } else { Some(value) }
530}
531
532fn parse_meta_flags(tokens: &[Bytes]) -> MetaFlags {
533 let mut ordered = Vec::with_capacity(tokens.len());
534 for token in tokens {
535 if token.is_empty() {
536 continue;
537 }
538 let code = token.as_ref()[0];
539 let rest = if token.len() > 1 {
540 Some(token.slice(1..))
541 } else {
542 None
543 };
544 ordered.push(crate::types::MetaFlag { code, token: rest });
545 }
546 MetaFlags::new(ordered)
547}
548
549fn decode_base64(input: &[u8]) -> Result<Vec<u8>, Error> {
550 if !input.len().is_multiple_of(4) {
551 return Err(Error::client("invalid base64"));
552 }
553 let mut out = Vec::with_capacity(input.len() / 4 * 3);
554 let mut i = 0;
555 while i < input.len() {
556 let a = decode_base64_val(input[i])?;
557 let b = decode_base64_val(input[i + 1])?;
558 let c = input[i + 2];
559 let d = input[i + 3];
560 let c_val = if c == b'=' {
561 None
562 } else {
563 Some(decode_base64_val(c)?)
564 };
565 let d_val = if d == b'=' {
566 None
567 } else {
568 Some(decode_base64_val(d)?)
569 };
570
571 out.push((a << 2) | (b >> 4));
572 if let Some(c_val) = c_val {
573 out.push((b << 4) | (c_val >> 2));
574 if let Some(d_val) = d_val {
575 out.push((c_val << 6) | d_val);
576 }
577 }
578 i += 4;
579 }
580 Ok(out)
581}
582
583fn decode_base64_val(byte: u8) -> Result<u8, Error> {
584 match byte {
585 b'A'..=b'Z' => Ok(byte - b'A'),
586 b'a'..=b'z' => Ok(byte - b'a' + 26),
587 b'0'..=b'9' => Ok(byte - b'0' + 52),
588 b'+' => Ok(62),
589 b'/' => Ok(63),
590 b'=' => Ok(0),
591 _ => Err(Error::client("invalid base64")),
592 }
593}
594
595pub fn should_suppress_ascii(meta: RequestMeta, response: &Response) -> bool {
596 match meta.reply {
597 ReplyMode::Always => false,
598 ReplyMode::SuppressMiss => {
599 if let Response::Meta(meta_resp) = response {
600 meta_resp.code.is_miss()
601 } else {
602 false
603 }
604 }
605 ReplyMode::SuppressSuccess => !matches!(response, Response::Error(_)),
606 ReplyMode::QuietBuffered => false,
607 }
608}
609
610pub fn should_suppress_meta(meta: RequestMeta, response: &Response) -> bool {
611 match meta.reply {
612 ReplyMode::Always => false,
613 ReplyMode::SuppressMiss => {
614 if let Response::Meta(meta_resp) = response {
615 meta_resp.code.is_miss()
616 } else {
617 false
618 }
619 }
620 ReplyMode::SuppressSuccess => {
621 if let Response::Meta(meta_resp) = response {
622 meta_resp.code.is_success()
623 } else {
624 false
625 }
626 }
627 ReplyMode::QuietBuffered => false,
628 }
629}
630
631pub fn encode_ascii_response(
632 req: &Request,
633 meta: RequestMeta,
634 response: &Response,
635 out: &mut BytesMut,
636) -> bool {
637 if should_suppress_ascii(meta, response) {
638 return false;
639 }
640 match response {
641 Response::Stored => out.extend_from_slice(b"STORED\r\n"),
642 Response::NotStored => out.extend_from_slice(b"NOT_STORED\r\n"),
643 Response::Exists => out.extend_from_slice(b"EXISTS\r\n"),
644 Response::NotFound => out.extend_from_slice(b"NOT_FOUND\r\n"),
645 Response::Deleted => out.extend_from_slice(b"DELETED\r\n"),
646 Response::Touched => out.extend_from_slice(b"TOUCHED\r\n"),
647 Response::Numeric(value) => {
648 write_u64(out, *value);
649 out.extend_from_slice(b"\r\n");
650 }
651 Response::Value(entry) => {
652 let include_cas = matches!(req.op, Op::Gets | Op::Gats);
653 encode_value_entry(entry, include_cas, out);
654 out.extend_from_slice(b"END\r\n");
655 }
656 Response::Values(entries) => {
657 let include_cas = matches!(req.op, Op::Gets | Op::Gats);
658 for entry in entries {
659 encode_value_entry(entry, include_cas, out);
660 }
661 out.extend_from_slice(b"END\r\n");
662 }
663 Response::Stats(lines) => {
664 for line in lines {
665 encode_stat_line(line, out);
666 }
667 out.extend_from_slice(b"END\r\n");
668 }
669 Response::Version(version) => {
670 out.extend_from_slice(b"VERSION ");
671 out.extend_from_slice(version);
672 out.extend_from_slice(b"\r\n");
673 }
674 Response::Noop => {
675 if req.op == Op::MetaNoop {
676 out.extend_from_slice(b"MN\r\n");
677 }
678 }
679 Response::Error(err) => match err.kind {
680 ErrorKind::UnknownCommand => out.extend_from_slice(b"ERROR\r\n"),
681 ErrorKind::Client => {
682 out.extend_from_slice(b"CLIENT_ERROR ");
683 out.extend_from_slice(err.message.as_ref());
684 out.extend_from_slice(b"\r\n");
685 }
686 ErrorKind::Server => {
687 out.extend_from_slice(b"SERVER_ERROR ");
688 out.extend_from_slice(err.message.as_ref());
689 out.extend_from_slice(b"\r\n");
690 }
691 },
692 Response::Meta(_) | Response::ValuesStream(_) | Response::StatsStream(_) => {}
693 }
694 true
695}
696
697pub fn encode_meta_response(
698 req: &Request,
699 meta: RequestMeta,
700 response: &MetaResponse,
701 out: &mut BytesMut,
702) {
703 if should_suppress_meta(meta, &Response::Meta(response.clone())) {
704 return;
705 }
706 out.extend_from_slice(response.code.as_bytes());
707 if response.code == MetaCode::Va {
708 out.extend_from_slice(b" ");
709 let size = response
710 .size
711 .or_else(|| response.value.as_ref().map(|value| value.len()))
712 .unwrap_or(0);
713 write_usize(out, size);
714 }
715
716 if let Some(flags) = req.meta.as_ref() {
717 for flag in &flags.ordered {
718 append_meta_token(req, response, flag.code, out);
719 }
720 }
721
722 if let Some(win) = response.extra.won {
723 out.extend_from_slice(match win {
724 crate::types::WinState::Won => b" W",
725 crate::types::WinState::AlreadyWon => b" Z",
726 });
727 }
728 if response.extra.stale {
729 out.extend_from_slice(b" X");
730 }
731
732 out.extend_from_slice(b"\r\n");
733
734 let want_value = req
735 .meta
736 .as_ref()
737 .map(|flags| flags.has(b'v'))
738 .unwrap_or(false);
739 if want_value && let Some(value) = response.value.as_ref() {
740 out.extend_from_slice(value);
741 out.extend_from_slice(b"\r\n");
742 }
743}
744
745pub fn encode_meta_debug(
746 req: &Request,
747 lines: impl IntoIterator<Item = StatLine>,
748 out: &mut BytesMut,
749) {
750 let key = match req.key.as_ref() {
751 Some(key) => key.as_ref(),
752 None => b"",
753 };
754 out.extend_from_slice(b"ME ");
755 out.extend_from_slice(key);
756 for line in lines {
757 out.extend_from_slice(b" ");
758 out.extend_from_slice(line.key.as_ref());
759 out.extend_from_slice(b"=");
760 out.extend_from_slice(line.value.as_ref());
761 }
762 out.extend_from_slice(b"\r\n");
763}
764
765pub fn encode_value_entry(entry: &ValueEntry, include_cas: bool, out: &mut BytesMut) {
766 out.extend_from_slice(b"VALUE ");
767 out.extend_from_slice(entry.key.as_ref());
768 out.extend_from_slice(b" ");
769 write_u32(out, entry.flags);
770 out.extend_from_slice(b" ");
771 write_usize(out, entry.value.len());
772 if include_cas {
773 out.extend_from_slice(b" ");
774 write_u64(out, entry.cas.unwrap_or(0));
775 }
776 out.extend_from_slice(b"\r\n");
777 out.extend_from_slice(entry.value.as_ref());
778 out.extend_from_slice(b"\r\n");
779}
780
781pub fn encode_stat_line(line: &StatLine, out: &mut BytesMut) {
782 out.extend_from_slice(b"STAT ");
783 out.extend_from_slice(line.key.as_ref());
784 out.extend_from_slice(b" ");
785 out.extend_from_slice(line.value.as_ref());
786 out.extend_from_slice(b"\r\n");
787}
788
789fn append_meta_token(req: &Request, response: &MetaResponse, code: u8, out: &mut BytesMut) {
790 match code {
791 b'O' => {
792 if let Some(token) = req.meta.as_ref().and_then(|flags| flags.token(b'O')) {
793 out.extend_from_slice(b" O");
794 out.extend_from_slice(token.as_ref());
795 }
796 }
797 b'k' => {
798 if let Some(key) = req.key.as_ref() {
799 out.extend_from_slice(b" k");
800 out.extend_from_slice(key.as_ref());
801 }
802 }
803 b'c' => {
804 if let Some(cas) = response.cas {
805 out.extend_from_slice(b" c");
806 write_u64(out, cas);
807 }
808 }
809 b't' => {
810 if let Some(ttl) = response.ttl {
811 out.extend_from_slice(b" t");
812 write_i64(out, ttl);
813 }
814 }
815 b'f' => {
816 if let Some(flags) = response.flags {
817 out.extend_from_slice(b" f");
818 write_u32(out, flags);
819 }
820 }
821 b's' => {
822 if let Some(size) = response.size {
823 out.extend_from_slice(b" s");
824 write_usize(out, size);
825 }
826 }
827 b'h' => {
828 if let Some(hit) = response.hit {
829 out.extend_from_slice(b" h");
830 out.extend_from_slice(if hit { b"1" } else { b"0" });
831 }
832 }
833 b'l' => {
834 if let Some(last) = response.last_access {
835 out.extend_from_slice(b" l");
836 write_u64(out, last);
837 }
838 }
839 _ => {}
840 }
841}
842
843fn write_u32(out: &mut BytesMut, value: u32) {
844 write_u64(out, value as u64)
845}
846
847fn write_usize(out: &mut BytesMut, value: usize) {
848 write_u64(out, value as u64)
849}
850
851fn write_u64(out: &mut BytesMut, mut value: u64) {
852 let mut buf = [0u8; 20];
853 let mut i = buf.len();
854 if value == 0 {
855 out.put_u8(b'0');
856 return;
857 }
858 while value > 0 {
859 i -= 1;
860 buf[i] = b'0' + (value % 10) as u8;
861 value /= 10;
862 }
863 out.extend_from_slice(&buf[i..]);
864}
865
866fn write_i64(out: &mut BytesMut, value: i64) {
867 if value < 0 {
868 out.put_u8(b'-');
869 write_u64(out, (-value) as u64);
870 } else {
871 write_u64(out, value as u64);
872 }
873}
874
875#[cfg(test)]
876mod tests {
877 use super::*;
878 use bytes::BytesMut;
879
880 #[test]
881 fn decode_set_with_value() {
882 let mut decoder = AsciiDecoder::new();
883 let mut buf = BytesMut::from("set key 1 10 5\r\nhello\r\n");
884 let limits = AsciiLimits::default();
885
886 let outcome = decoder.decode(&mut buf, limits);
887 let (req, meta) = match outcome {
888 Some(DecodeOutcome::Request(req, meta)) => (req, meta),
889 _ => panic!("unexpected decode outcome"),
890 };
891 assert_eq!(req.op, Op::Set);
892 assert_eq!(req.value.unwrap(), Bytes::from_static(b"hello"));
893 assert_eq!(meta.reply, ReplyMode::Always);
894 }
895
896 #[test]
897 fn decode_multi_get() {
898 let mut decoder = AsciiDecoder::new();
899 let mut buf = BytesMut::from("get k1 k2\r\n");
900 let outcome = decoder.decode(&mut buf, AsciiLimits::default());
901 let (req, _) = match outcome {
902 Some(DecodeOutcome::Request(req, meta)) => (req, meta),
903 _ => panic!("unexpected decode outcome"),
904 };
905 assert_eq!(req.op, Op::Get);
906 assert_eq!(req.keys.len(), 2);
907 }
908
909 #[test]
910 fn decode_meta_set_s_token() {
911 let mut decoder = AsciiDecoder::new();
912 let mut buf = BytesMut::from("ms key 5 S3\r\nabc\r\n");
913 let limits = AsciiLimits::default();
914
915 let outcome = decoder.decode(&mut buf, limits);
916 let (req, meta) = match outcome {
917 Some(DecodeOutcome::Request(req, meta)) => (req, meta),
918 _ => panic!("unexpected decode outcome"),
919 };
920 assert_eq!(meta.protocol, Protocol::Meta);
921 assert_eq!(req.value.unwrap(), Bytes::from_static(b"abc"));
922 }
923}