1use crate::{FastCacheError, Result};
2
3pub const FAST_REQUEST_MAGIC: u8 = 0xFA;
12pub const FAST_RESPONSE_MAGIC: u8 = 0xFB;
13pub const FAST_PROTOCOL_VERSION: u8 = 2;
14
15pub const FAST_FLAG_KEY_HASH: u8 = 0x01;
16pub const FAST_FLAG_ROUTE_SHARD: u8 = 0x02;
17pub const FAST_FLAG_KEY_TAG: u8 = 0x04;
18
19const REQUEST_HEADER_LEN: usize = 8;
20const RESPONSE_HEADER_LEN: usize = 8;
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23#[repr(u8)]
24pub enum FastCommandKind {
25 Get = 1,
26 Set = 2,
27 SetEx = 3,
28 GetEx = 4,
29 Delete = 5,
30 Exists = 6,
31 Ttl = 7,
32 Expire = 8,
33 Ping = 9,
34 MGet = 10,
35 MSet = 11,
36 Auth = 60,
37 Hello = 61,
38 Select = 62,
39 Quit = 63,
40 Echo = 64,
41 Info = 65,
42 Command = 66,
43 CommandDocs = 67,
44 ConfigGet = 68,
45 DbSize = 69,
46 Time = 70,
47 ClientGetName = 71,
48 ClientSetName = 72,
49 ClientId = 73,
50 ClientList = 74,
51 ClientKill = 75,
52 HSet = 20,
53 HGet = 21,
54 HDel = 22,
55 HLen = 23,
56 HMGet = 24,
57 LPush = 30,
58 RPush = 31,
59 LPop = 32,
60 RPop = 33,
61 LLen = 34,
62 LIndex = 35,
63 LRange = 36,
64 SAdd = 40,
65 SRem = 41,
66 SIsMember = 42,
67 SCard = 43,
68 SMembers = 44,
69 ZAdd = 50,
70 ZRem = 51,
71 ZScore = 52,
72 ZCard = 53,
73 ZRange = 54,
74 RespCommand = 200,
75}
76
77impl FastCommandKind {
78 fn from_u8(value: u8) -> Result<Self> {
79 match value {
80 1 => Ok(Self::Get),
81 2 => Ok(Self::Set),
82 3 => Ok(Self::SetEx),
83 4 => Ok(Self::GetEx),
84 5 => Ok(Self::Delete),
85 6 => Ok(Self::Exists),
86 7 => Ok(Self::Ttl),
87 8 => Ok(Self::Expire),
88 9 => Ok(Self::Ping),
89 10 => Ok(Self::MGet),
90 11 => Ok(Self::MSet),
91 60 => Ok(Self::Auth),
92 61 => Ok(Self::Hello),
93 62 => Ok(Self::Select),
94 63 => Ok(Self::Quit),
95 64 => Ok(Self::Echo),
96 65 => Ok(Self::Info),
97 66 => Ok(Self::Command),
98 67 => Ok(Self::CommandDocs),
99 68 => Ok(Self::ConfigGet),
100 69 => Ok(Self::DbSize),
101 70 => Ok(Self::Time),
102 71 => Ok(Self::ClientGetName),
103 72 => Ok(Self::ClientSetName),
104 73 => Ok(Self::ClientId),
105 74 => Ok(Self::ClientList),
106 75 => Ok(Self::ClientKill),
107 20 => Ok(Self::HSet),
108 21 => Ok(Self::HGet),
109 22 => Ok(Self::HDel),
110 23 => Ok(Self::HLen),
111 24 => Ok(Self::HMGet),
112 30 => Ok(Self::LPush),
113 31 => Ok(Self::RPush),
114 32 => Ok(Self::LPop),
115 33 => Ok(Self::RPop),
116 34 => Ok(Self::LLen),
117 35 => Ok(Self::LIndex),
118 36 => Ok(Self::LRange),
119 40 => Ok(Self::SAdd),
120 41 => Ok(Self::SRem),
121 42 => Ok(Self::SIsMember),
122 43 => Ok(Self::SCard),
123 44 => Ok(Self::SMembers),
124 50 => Ok(Self::ZAdd),
125 51 => Ok(Self::ZRem),
126 52 => Ok(Self::ZScore),
127 53 => Ok(Self::ZCard),
128 54 => Ok(Self::ZRange),
129 200 => Ok(Self::RespCommand),
130 other => Err(FastCacheError::Protocol(format!(
131 "unsupported fast command id: {other}"
132 ))),
133 }
134 }
135}
136
137#[derive(Debug, Clone, PartialEq)]
138pub enum FastCommand<'a> {
139 Ping(Option<&'a [u8]>),
140 Auth,
141 Hello {
142 proto: Option<u64>,
143 },
144 Select {
145 db: u64,
146 },
147 Quit,
148 Echo {
149 payload: &'a [u8],
150 },
151 Info,
152 Command,
153 CommandDocs,
154 ConfigGet {
155 patterns: Vec<&'a [u8]>,
156 },
157 DbSize,
158 Time,
159 ClientGetName,
160 ClientSetName {
161 name: &'a [u8],
162 },
163 ClientId,
164 ClientList,
165 ClientKill,
166 Get {
167 key: &'a [u8],
168 },
169 Set {
170 key: &'a [u8],
171 value: &'a [u8],
172 },
173 SetEx {
174 key: &'a [u8],
175 value: &'a [u8],
176 ttl_ms: u64,
177 },
178 GetEx {
179 key: &'a [u8],
180 ttl_ms: u64,
181 },
182 Delete {
183 key: &'a [u8],
184 },
185 Exists {
186 key: &'a [u8],
187 },
188 Ttl {
189 key: &'a [u8],
190 },
191 Expire {
192 key: &'a [u8],
193 ttl_ms: u64,
194 },
195 MGet {
196 keys: Vec<&'a [u8]>,
197 },
198 MSet {
199 items: Vec<(&'a [u8], &'a [u8])>,
200 },
201 HSet {
202 key: &'a [u8],
203 field: &'a [u8],
204 value: &'a [u8],
205 },
206 HGet {
207 key: &'a [u8],
208 field: &'a [u8],
209 },
210 HDel {
211 key: &'a [u8],
212 field: &'a [u8],
213 },
214 HLen {
215 key: &'a [u8],
216 },
217 HMGet {
218 key: &'a [u8],
219 fields: Vec<&'a [u8]>,
220 },
221 LPush {
222 key: &'a [u8],
223 values: Vec<&'a [u8]>,
224 },
225 RPush {
226 key: &'a [u8],
227 values: Vec<&'a [u8]>,
228 },
229 LPop {
230 key: &'a [u8],
231 },
232 RPop {
233 key: &'a [u8],
234 },
235 LLen {
236 key: &'a [u8],
237 },
238 LIndex {
239 key: &'a [u8],
240 index: i64,
241 },
242 LRange {
243 key: &'a [u8],
244 start: i64,
245 stop: i64,
246 },
247 SAdd {
248 key: &'a [u8],
249 members: Vec<&'a [u8]>,
250 },
251 SRem {
252 key: &'a [u8],
253 members: Vec<&'a [u8]>,
254 },
255 SIsMember {
256 key: &'a [u8],
257 member: &'a [u8],
258 },
259 SCard {
260 key: &'a [u8],
261 },
262 SMembers {
263 key: &'a [u8],
264 },
265 ZAdd {
266 key: &'a [u8],
267 score: f64,
268 member: &'a [u8],
269 },
270 ZRem {
271 key: &'a [u8],
272 member: &'a [u8],
273 },
274 ZScore {
275 key: &'a [u8],
276 member: &'a [u8],
277 },
278 ZCard {
279 key: &'a [u8],
280 },
281 ZRange {
282 key: &'a [u8],
283 start: i64,
284 stop: i64,
285 },
286 RespCommand {
293 parts: Vec<&'a [u8]>,
294 },
295}
296
297impl<'a> FastCommand<'a> {
298 pub fn kind(&self) -> FastCommandKind {
299 match self {
300 Self::Get { .. } => FastCommandKind::Get,
301 Self::Set { .. } => FastCommandKind::Set,
302 Self::SetEx { .. } => FastCommandKind::SetEx,
303 Self::GetEx { .. } => FastCommandKind::GetEx,
304 Self::Delete { .. } => FastCommandKind::Delete,
305 Self::Exists { .. } => FastCommandKind::Exists,
306 Self::Ttl { .. } => FastCommandKind::Ttl,
307 Self::Expire { .. } => FastCommandKind::Expire,
308 Self::Ping(_) => FastCommandKind::Ping,
309 Self::MGet { .. } => FastCommandKind::MGet,
310 Self::MSet { .. } => FastCommandKind::MSet,
311 Self::Auth => FastCommandKind::Auth,
312 Self::Hello { .. } => FastCommandKind::Hello,
313 Self::Select { .. } => FastCommandKind::Select,
314 Self::Quit => FastCommandKind::Quit,
315 Self::Echo { .. } => FastCommandKind::Echo,
316 Self::Info => FastCommandKind::Info,
317 Self::Command => FastCommandKind::Command,
318 Self::CommandDocs => FastCommandKind::CommandDocs,
319 Self::ConfigGet { .. } => FastCommandKind::ConfigGet,
320 Self::DbSize => FastCommandKind::DbSize,
321 Self::Time => FastCommandKind::Time,
322 Self::ClientGetName => FastCommandKind::ClientGetName,
323 Self::ClientSetName { .. } => FastCommandKind::ClientSetName,
324 Self::ClientId => FastCommandKind::ClientId,
325 Self::ClientList => FastCommandKind::ClientList,
326 Self::ClientKill => FastCommandKind::ClientKill,
327 Self::HSet { .. } => FastCommandKind::HSet,
328 Self::HGet { .. } => FastCommandKind::HGet,
329 Self::HDel { .. } => FastCommandKind::HDel,
330 Self::HLen { .. } => FastCommandKind::HLen,
331 Self::HMGet { .. } => FastCommandKind::HMGet,
332 Self::LPush { .. } => FastCommandKind::LPush,
333 Self::RPush { .. } => FastCommandKind::RPush,
334 Self::LPop { .. } => FastCommandKind::LPop,
335 Self::RPop { .. } => FastCommandKind::RPop,
336 Self::LLen { .. } => FastCommandKind::LLen,
337 Self::LIndex { .. } => FastCommandKind::LIndex,
338 Self::LRange { .. } => FastCommandKind::LRange,
339 Self::SAdd { .. } => FastCommandKind::SAdd,
340 Self::SRem { .. } => FastCommandKind::SRem,
341 Self::SIsMember { .. } => FastCommandKind::SIsMember,
342 Self::SCard { .. } => FastCommandKind::SCard,
343 Self::SMembers { .. } => FastCommandKind::SMembers,
344 Self::ZAdd { .. } => FastCommandKind::ZAdd,
345 Self::ZRem { .. } => FastCommandKind::ZRem,
346 Self::ZScore { .. } => FastCommandKind::ZScore,
347 Self::ZCard { .. } => FastCommandKind::ZCard,
348 Self::ZRange { .. } => FastCommandKind::ZRange,
349 Self::RespCommand { .. } => FastCommandKind::RespCommand,
350 }
351 }
352
353 pub fn route_key(&self) -> Option<&'a [u8]> {
354 match self {
355 Self::Ping(_)
356 | Self::Auth
357 | Self::Hello { .. }
358 | Self::Select { .. }
359 | Self::Quit
360 | Self::Echo { .. }
361 | Self::Info
362 | Self::Command
363 | Self::CommandDocs
364 | Self::ConfigGet { .. }
365 | Self::DbSize
366 | Self::Time
367 | Self::ClientGetName
368 | Self::ClientSetName { .. }
369 | Self::ClientId
370 | Self::ClientList
371 | Self::ClientKill => None,
372 Self::RespCommand { parts } => parts.get(1).copied(),
373 Self::Get { key }
374 | Self::Set { key, .. }
375 | Self::SetEx { key, .. }
376 | Self::GetEx { key, .. }
377 | Self::Delete { key }
378 | Self::Exists { key }
379 | Self::Ttl { key }
380 | Self::Expire { key, .. }
381 | Self::HSet { key, .. }
382 | Self::HGet { key, .. }
383 | Self::HDel { key, .. }
384 | Self::HLen { key }
385 | Self::HMGet { key, .. }
386 | Self::LPush { key, .. }
387 | Self::RPush { key, .. }
388 | Self::LPop { key }
389 | Self::RPop { key }
390 | Self::LLen { key }
391 | Self::LIndex { key, .. }
392 | Self::LRange { key, .. }
393 | Self::SAdd { key, .. }
394 | Self::SRem { key, .. }
395 | Self::SIsMember { key, .. }
396 | Self::SCard { key }
397 | Self::SMembers { key }
398 | Self::ZAdd { key, .. }
399 | Self::ZRem { key, .. }
400 | Self::ZScore { key, .. }
401 | Self::ZCard { key }
402 | Self::ZRange { key, .. } => Some(key),
403 Self::MGet { keys } => keys.first().copied(),
404 Self::MSet { items } => items.first().map(|(key, _)| *key),
405 }
406 }
407}
408
409#[derive(Debug, Clone, PartialEq)]
410pub struct FastRequest<'a> {
411 pub key_hash: Option<u64>,
412 pub route_shard: Option<u32>,
413 pub key_tag: Option<u64>,
414 pub command: FastCommand<'a>,
415}
416
417#[derive(Debug, Clone, PartialEq)]
418pub enum FastResponse {
419 Ok,
420 Null,
421 Error(Vec<u8>),
422 Integer(i64),
423 Value(Vec<u8>),
424 Boolean(bool),
425 Array(Vec<Option<Vec<u8>>>),
426 Float(f64),
427}
428
429pub type FastRequestDecodeResult<'a> = Option<(FastRequest<'a>, usize)>;
430pub type FastResponseDecodeResult = Option<(FastResponse, usize)>;
431
432#[derive(Debug, Default, Clone, Copy)]
433pub struct FastCodec;
434
435impl FastCodec {
436 pub fn is_fast_request_prefix(byte: u8) -> bool {
437 byte == FAST_REQUEST_MAGIC
438 }
439
440 pub fn decode_request(buffer: &[u8]) -> Result<FastRequestDecodeResult<'_>> {
441 if buffer.is_empty() {
442 return Ok(None);
443 }
444 if buffer[0] != FAST_REQUEST_MAGIC {
445 return Err(FastCacheError::Protocol(
446 "invalid fast request magic byte".into(),
447 ));
448 }
449 if buffer.len() < REQUEST_HEADER_LEN {
450 return Ok(None);
451 }
452
453 let version = buffer[1];
454 if version != FAST_PROTOCOL_VERSION {
455 return Err(FastCacheError::Protocol(format!(
456 "unsupported fast protocol version: {version}"
457 )));
458 }
459
460 let kind = FastCommandKind::from_u8(buffer[2])?;
461 let flags = buffer[3];
462 if flags & !(FAST_FLAG_KEY_HASH | FAST_FLAG_ROUTE_SHARD | FAST_FLAG_KEY_TAG) != 0 {
463 return Err(FastCacheError::Protocol(format!(
464 "unsupported fast flags: {flags:#04x}"
465 )));
466 }
467
468 let body_len = u32::from_le_bytes(buffer[4..8].try_into().unwrap()) as usize;
469 if buffer.len() < REQUEST_HEADER_LEN + body_len {
470 return Ok(None);
471 }
472
473 let body = &buffer[REQUEST_HEADER_LEN..REQUEST_HEADER_LEN + body_len];
474 let mut cursor = 0usize;
475 let key_hash = if flags & FAST_FLAG_KEY_HASH != 0 {
476 Some(take_u64(body, &mut cursor, "fast key hash")?)
477 } else {
478 None
479 };
480 let route_shard = if flags & FAST_FLAG_ROUTE_SHARD != 0 {
481 Some(take_u32(body, &mut cursor, "fast route shard")?)
482 } else {
483 None
484 };
485 let key_tag = if flags & FAST_FLAG_KEY_TAG != 0 {
486 Some(take_u64(body, &mut cursor, "fast key tag")?)
487 } else {
488 None
489 };
490
491 let command = match kind {
492 FastCommandKind::Ping => {
493 let payload = (!body[cursor..].is_empty()).then_some(&body[cursor..]);
494 cursor = body.len();
495 FastCommand::Ping(payload)
496 }
497 FastCommandKind::Auth => FastCommand::Auth,
498 FastCommandKind::Hello => {
499 let proto = if body.len().saturating_sub(cursor) >= 8 {
500 Some(take_u64(body, &mut cursor, "fast HELLO proto")?)
501 } else {
502 None
503 };
504 FastCommand::Hello { proto }
505 }
506 FastCommandKind::Select => FastCommand::Select {
507 db: take_u64(body, &mut cursor, "fast SELECT db")?,
508 },
509 FastCommandKind::Quit => FastCommand::Quit,
510 FastCommandKind::Echo => FastCommand::Echo {
511 payload: take_len_prefixed_slice(body, &mut cursor, "fast ECHO payload")?,
512 },
513 FastCommandKind::Info => FastCommand::Info,
514 FastCommandKind::Command => FastCommand::Command,
515 FastCommandKind::CommandDocs => FastCommand::CommandDocs,
516 FastCommandKind::ConfigGet => FastCommand::ConfigGet {
517 patterns: take_len_prefixed_list(body, &mut cursor, "fast CONFIG GET patterns")?,
518 },
519 FastCommandKind::DbSize => FastCommand::DbSize,
520 FastCommandKind::Time => FastCommand::Time,
521 FastCommandKind::ClientGetName => FastCommand::ClientGetName,
522 FastCommandKind::ClientSetName => FastCommand::ClientSetName {
523 name: take_len_prefixed_slice(body, &mut cursor, "fast CLIENT SETNAME name")?,
524 },
525 FastCommandKind::ClientId => FastCommand::ClientId,
526 FastCommandKind::ClientList => FastCommand::ClientList,
527 FastCommandKind::ClientKill => FastCommand::ClientKill,
528 FastCommandKind::Get => FastCommand::Get {
529 key: take_len_prefixed_slice(body, &mut cursor, "fast GET key")?,
530 },
531 FastCommandKind::Set => {
532 let (key, value) = take_key_value(body, &mut cursor, "fast SET")?;
533 FastCommand::Set { key, value }
534 }
535 FastCommandKind::SetEx => {
536 let ttl_ms = take_u64(body, &mut cursor, "fast SETEX ttl")?;
537 let (key, value) = take_key_value(body, &mut cursor, "fast SETEX")?;
538 FastCommand::SetEx { key, value, ttl_ms }
539 }
540 FastCommandKind::GetEx => {
541 let ttl_ms = take_u64(body, &mut cursor, "fast GETEX ttl")?;
542 FastCommand::GetEx {
543 key: take_len_prefixed_slice(body, &mut cursor, "fast GETEX key")?,
544 ttl_ms,
545 }
546 }
547 FastCommandKind::Delete => FastCommand::Delete {
548 key: take_len_prefixed_slice(body, &mut cursor, "fast DELETE key")?,
549 },
550 FastCommandKind::Exists => FastCommand::Exists {
551 key: take_len_prefixed_slice(body, &mut cursor, "fast EXISTS key")?,
552 },
553 FastCommandKind::Ttl => FastCommand::Ttl {
554 key: take_len_prefixed_slice(body, &mut cursor, "fast TTL key")?,
555 },
556 FastCommandKind::Expire => {
557 let ttl_ms = take_u64(body, &mut cursor, "fast EXPIRE ttl")?;
558 FastCommand::Expire {
559 key: take_len_prefixed_slice(body, &mut cursor, "fast EXPIRE key")?,
560 ttl_ms,
561 }
562 }
563 FastCommandKind::MGet => FastCommand::MGet {
564 keys: take_len_prefixed_list(body, &mut cursor, "fast MGET keys")?,
565 },
566 FastCommandKind::MSet => FastCommand::MSet {
567 items: take_key_value_list(body, &mut cursor, "fast MSET items")?,
568 },
569 FastCommandKind::HSet => {
570 let (key, field, value) = take_key_field_value(body, &mut cursor, "fast HSET")?;
571 FastCommand::HSet { key, field, value }
572 }
573 FastCommandKind::HGet => {
574 let (key, field) = take_key_field(body, &mut cursor, "fast HGET")?;
575 FastCommand::HGet { key, field }
576 }
577 FastCommandKind::HDel => {
578 let (key, field) = take_key_field(body, &mut cursor, "fast HDEL")?;
579 FastCommand::HDel { key, field }
580 }
581 FastCommandKind::HLen => FastCommand::HLen {
582 key: take_len_prefixed_slice(body, &mut cursor, "fast HLEN key")?,
583 },
584 FastCommandKind::HMGet => {
585 let (key, fields) = take_key_list(body, &mut cursor, "fast HMGET")?;
586 FastCommand::HMGet { key, fields }
587 }
588 FastCommandKind::LPush => {
589 let (key, values) = take_key_list(body, &mut cursor, "fast LPUSH")?;
590 FastCommand::LPush { key, values }
591 }
592 FastCommandKind::RPush => {
593 let (key, values) = take_key_list(body, &mut cursor, "fast RPUSH")?;
594 FastCommand::RPush { key, values }
595 }
596 FastCommandKind::LPop => FastCommand::LPop {
597 key: take_len_prefixed_slice(body, &mut cursor, "fast LPOP key")?,
598 },
599 FastCommandKind::RPop => FastCommand::RPop {
600 key: take_len_prefixed_slice(body, &mut cursor, "fast RPOP key")?,
601 },
602 FastCommandKind::LLen => FastCommand::LLen {
603 key: take_len_prefixed_slice(body, &mut cursor, "fast LLEN key")?,
604 },
605 FastCommandKind::LIndex => {
606 let index = take_i64(body, &mut cursor, "fast LINDEX index")?;
607 FastCommand::LIndex {
608 key: take_len_prefixed_slice(body, &mut cursor, "fast LINDEX key")?,
609 index,
610 }
611 }
612 FastCommandKind::LRange => {
613 let start = take_i64(body, &mut cursor, "fast LRANGE start")?;
614 let stop = take_i64(body, &mut cursor, "fast LRANGE stop")?;
615 FastCommand::LRange {
616 key: take_len_prefixed_slice(body, &mut cursor, "fast LRANGE key")?,
617 start,
618 stop,
619 }
620 }
621 FastCommandKind::SAdd => {
622 let (key, members) = take_key_list(body, &mut cursor, "fast SADD")?;
623 FastCommand::SAdd { key, members }
624 }
625 FastCommandKind::SRem => {
626 let (key, members) = take_key_list(body, &mut cursor, "fast SREM")?;
627 FastCommand::SRem { key, members }
628 }
629 FastCommandKind::SIsMember => {
630 let (key, member) = take_key_field(body, &mut cursor, "fast SISMEMBER")?;
631 FastCommand::SIsMember { key, member }
632 }
633 FastCommandKind::SCard => FastCommand::SCard {
634 key: take_len_prefixed_slice(body, &mut cursor, "fast SCARD key")?,
635 },
636 FastCommandKind::SMembers => FastCommand::SMembers {
637 key: take_len_prefixed_slice(body, &mut cursor, "fast SMEMBERS key")?,
638 },
639 FastCommandKind::ZAdd => {
640 let score = take_f64(body, &mut cursor, "fast ZADD score")?;
641 let (key, member) = take_key_field(body, &mut cursor, "fast ZADD")?;
642 FastCommand::ZAdd { key, score, member }
643 }
644 FastCommandKind::ZRem => {
645 let (key, member) = take_key_field(body, &mut cursor, "fast ZREM")?;
646 FastCommand::ZRem { key, member }
647 }
648 FastCommandKind::ZScore => {
649 let (key, member) = take_key_field(body, &mut cursor, "fast ZSCORE")?;
650 FastCommand::ZScore { key, member }
651 }
652 FastCommandKind::ZCard => FastCommand::ZCard {
653 key: take_len_prefixed_slice(body, &mut cursor, "fast ZCARD key")?,
654 },
655 FastCommandKind::ZRange => {
656 let start = take_i64(body, &mut cursor, "fast ZRANGE start")?;
657 let stop = take_i64(body, &mut cursor, "fast ZRANGE stop")?;
658 FastCommand::ZRange {
659 key: take_len_prefixed_slice(body, &mut cursor, "fast ZRANGE key")?,
660 start,
661 stop,
662 }
663 }
664 FastCommandKind::RespCommand => FastCommand::RespCommand {
665 parts: take_len_prefixed_list(body, &mut cursor, "fast RESP command parts")?,
666 },
667 };
668 ensure_finished(body, cursor, "fast request")?;
669
670 Ok(Some((
671 FastRequest {
672 key_hash,
673 route_shard,
674 key_tag,
675 command,
676 },
677 REQUEST_HEADER_LEN + body_len,
678 )))
679 }
680
681 pub fn encode_request(request: &FastRequest<'_>, out: &mut Vec<u8>) {
682 let mut flags = 0u8;
683 if request.key_hash.is_some() {
684 flags |= FAST_FLAG_KEY_HASH;
685 }
686 if request.route_shard.is_some() {
687 flags |= FAST_FLAG_ROUTE_SHARD;
688 }
689 if request.key_tag.is_some() {
690 flags |= FAST_FLAG_KEY_TAG;
691 }
692
693 out.push(FAST_REQUEST_MAGIC);
694 out.push(FAST_PROTOCOL_VERSION);
695 out.push(request.command.kind() as u8);
696 out.push(flags);
697
698 let body_len_index = out.len();
699 out.extend_from_slice(&0_u32.to_le_bytes());
700 let body_start = out.len();
701
702 if let Some(key_hash) = request.key_hash {
703 out.extend_from_slice(&key_hash.to_le_bytes());
704 }
705 if let Some(route_shard) = request.route_shard {
706 out.extend_from_slice(&route_shard.to_le_bytes());
707 }
708 if let Some(key_tag) = request.key_tag {
709 out.extend_from_slice(&key_tag.to_le_bytes());
710 }
711
712 match &request.command {
713 FastCommand::Ping(payload) => {
714 if let Some(payload) = payload {
715 out.extend_from_slice(payload);
716 }
717 }
718 FastCommand::Auth
719 | FastCommand::Quit
720 | FastCommand::Info
721 | FastCommand::Command
722 | FastCommand::CommandDocs
723 | FastCommand::DbSize
724 | FastCommand::Time
725 | FastCommand::ClientGetName
726 | FastCommand::ClientId
727 | FastCommand::ClientList
728 | FastCommand::ClientKill => {}
729 FastCommand::Hello { proto } => {
730 if let Some(proto) = proto {
731 out.extend_from_slice(&proto.to_le_bytes());
732 }
733 }
734 FastCommand::Select { db } => {
735 out.extend_from_slice(&db.to_le_bytes());
736 }
737 FastCommand::Echo { payload } => encode_len_prefixed(payload, out),
738 FastCommand::ConfigGet { patterns } => encode_len_prefixed_list(patterns, out),
739 FastCommand::ClientSetName { name } => encode_len_prefixed(name, out),
740 FastCommand::Get { key }
741 | FastCommand::Delete { key }
742 | FastCommand::Exists { key }
743 | FastCommand::Ttl { key } => encode_len_prefixed(key, out),
744 FastCommand::Set { key, value } => encode_key_value(key, value, out),
745 FastCommand::SetEx { key, value, ttl_ms } => {
746 out.extend_from_slice(&ttl_ms.to_le_bytes());
747 encode_key_value(key, value, out);
748 }
749 FastCommand::GetEx { key, ttl_ms } | FastCommand::Expire { key, ttl_ms } => {
750 out.extend_from_slice(&ttl_ms.to_le_bytes());
751 encode_len_prefixed(key, out);
752 }
753 FastCommand::MGet { keys } => encode_len_prefixed_list(keys, out),
754 FastCommand::MSet { items } => encode_key_value_list(items, out),
755 FastCommand::HSet { key, field, value } => {
756 encode_key_field_value(key, field, value, out);
757 }
758 FastCommand::HGet { key, field }
759 | FastCommand::HDel { key, field }
760 | FastCommand::SIsMember { key, member: field }
761 | FastCommand::ZRem { key, member: field }
762 | FastCommand::ZScore { key, member: field } => {
763 encode_key_field(key, field, out);
764 }
765 FastCommand::HLen { key }
766 | FastCommand::LPop { key }
767 | FastCommand::RPop { key }
768 | FastCommand::LLen { key }
769 | FastCommand::SCard { key }
770 | FastCommand::SMembers { key }
771 | FastCommand::ZCard { key } => encode_len_prefixed(key, out),
772 FastCommand::HMGet { key, fields } => encode_key_list(key, fields, out),
773 FastCommand::LPush { key, values } | FastCommand::RPush { key, values } => {
774 encode_key_list(key, values, out);
775 }
776 FastCommand::LIndex { key, index } => {
777 out.extend_from_slice(&index.to_le_bytes());
778 encode_len_prefixed(key, out);
779 }
780 FastCommand::LRange { key, start, stop } | FastCommand::ZRange { key, start, stop } => {
781 out.extend_from_slice(&start.to_le_bytes());
782 out.extend_from_slice(&stop.to_le_bytes());
783 encode_len_prefixed(key, out);
784 }
785 FastCommand::SAdd { key, members } | FastCommand::SRem { key, members } => {
786 encode_key_list(key, members, out);
787 }
788 FastCommand::ZAdd { key, score, member } => {
789 out.extend_from_slice(&score.to_le_bytes());
790 encode_key_field(key, member, out);
791 }
792 FastCommand::RespCommand { parts } => encode_len_prefixed_list(parts, out),
793 }
794
795 let body_len = (out.len() - body_start) as u32;
796 out[body_len_index..body_len_index + 4].copy_from_slice(&body_len.to_le_bytes());
797 }
798
799 pub fn encode_response(response: &FastResponse, out: &mut Vec<u8>) {
800 out.push(FAST_RESPONSE_MAGIC);
801 out.push(FAST_PROTOCOL_VERSION);
802 match response {
803 FastResponse::Ok => {
804 out.push(0);
805 out.push(0);
806 out.extend_from_slice(&0_u32.to_le_bytes());
807 }
808 FastResponse::Null => {
809 out.push(1);
810 out.push(0);
811 out.extend_from_slice(&0_u32.to_le_bytes());
812 }
813 FastResponse::Error(message) => {
814 out.push(2);
815 out.push(0);
816 out.extend_from_slice(&(message.len() as u32).to_le_bytes());
817 out.extend_from_slice(message);
818 }
819 FastResponse::Integer(value) => {
820 out.push(3);
821 out.push(0);
822 out.extend_from_slice(&8_u32.to_le_bytes());
823 out.extend_from_slice(&value.to_le_bytes());
824 }
825 FastResponse::Value(bytes) => {
826 out.push(4);
827 out.push(0);
828 out.extend_from_slice(&(bytes.len() as u32).to_le_bytes());
829 out.extend_from_slice(bytes);
830 }
831 FastResponse::Boolean(value) => {
832 out.push(5);
833 out.push(0);
834 out.extend_from_slice(&1_u32.to_le_bytes());
835 out.push(*value as u8);
836 }
837 FastResponse::Array(values) => {
838 out.push(6);
839 out.push(0);
840 let body_len_index = out.len();
841 out.extend_from_slice(&0_u32.to_le_bytes());
842 let body_start = out.len();
843 encode_array_body(values.iter().map(|value| value.as_deref()), out);
844 let body_len = (out.len() - body_start) as u32;
845 out[body_len_index..body_len_index + 4].copy_from_slice(&body_len.to_le_bytes());
846 }
847 FastResponse::Float(value) => {
848 out.push(7);
849 out.push(0);
850 out.extend_from_slice(&8_u32.to_le_bytes());
851 out.extend_from_slice(&value.to_le_bytes());
852 }
853 }
854 }
855
856 pub fn decode_response(buffer: &[u8]) -> Result<FastResponseDecodeResult> {
857 if buffer.is_empty() {
858 return Ok(None);
859 }
860 if buffer[0] != FAST_RESPONSE_MAGIC {
861 return Err(FastCacheError::Protocol(
862 "invalid fast response magic byte".into(),
863 ));
864 }
865 if buffer.len() < RESPONSE_HEADER_LEN {
866 return Ok(None);
867 }
868
869 let version = buffer[1];
870 if version != FAST_PROTOCOL_VERSION {
871 return Err(FastCacheError::Protocol(format!(
872 "unsupported fast response protocol version: {version}"
873 )));
874 }
875
876 let status = buffer[2];
877 let _flags = buffer[3];
878 let body_len = u32::from_le_bytes(buffer[4..8].try_into().unwrap()) as usize;
879 if buffer.len() < RESPONSE_HEADER_LEN + body_len {
880 return Ok(None);
881 }
882
883 let body = &buffer[RESPONSE_HEADER_LEN..RESPONSE_HEADER_LEN + body_len];
884 let response = match status {
885 0 => FastResponse::Ok,
886 1 => FastResponse::Null,
887 2 => FastResponse::Error(body.to_vec()),
888 3 => {
889 if body.len() != 8 {
890 return Err(FastCacheError::Protocol(
891 "fast integer response is truncated".into(),
892 ));
893 }
894 FastResponse::Integer(i64::from_le_bytes(body.try_into().unwrap()))
895 }
896 4 => FastResponse::Value(body.to_vec()),
897 5 => {
898 if body.len() != 1 {
899 return Err(FastCacheError::Protocol(
900 "fast boolean response is truncated".into(),
901 ));
902 }
903 FastResponse::Boolean(body[0] != 0)
904 }
905 6 => FastResponse::Array(decode_array_body(body)?),
906 7 => {
907 if body.len() != 8 {
908 return Err(FastCacheError::Protocol(
909 "fast float response is truncated".into(),
910 ));
911 }
912 FastResponse::Float(f64::from_le_bytes(body.try_into().unwrap()))
913 }
914 other => {
915 return Err(FastCacheError::Protocol(format!(
916 "unsupported fast response status: {other}"
917 )));
918 }
919 };
920
921 Ok(Some((response, RESPONSE_HEADER_LEN + body_len)))
922 }
923}
924
925fn take_u32(body: &[u8], cursor: &mut usize, field: &str) -> Result<u32> {
926 if body.len().saturating_sub(*cursor) < 4 {
927 return Err(FastCacheError::Protocol(format!("{field} is truncated")));
928 }
929 let value = u32::from_le_bytes(body[*cursor..*cursor + 4].try_into().unwrap());
930 *cursor += 4;
931 Ok(value)
932}
933
934fn take_u64(body: &[u8], cursor: &mut usize, field: &str) -> Result<u64> {
935 if body.len().saturating_sub(*cursor) < 8 {
936 return Err(FastCacheError::Protocol(format!("{field} is truncated")));
937 }
938 let value = u64::from_le_bytes(body[*cursor..*cursor + 8].try_into().unwrap());
939 *cursor += 8;
940 Ok(value)
941}
942
943fn take_i64(body: &[u8], cursor: &mut usize, field: &str) -> Result<i64> {
944 if body.len().saturating_sub(*cursor) < 8 {
945 return Err(FastCacheError::Protocol(format!("{field} is truncated")));
946 }
947 let value = i64::from_le_bytes(body[*cursor..*cursor + 8].try_into().unwrap());
948 *cursor += 8;
949 Ok(value)
950}
951
952fn take_f64(body: &[u8], cursor: &mut usize, field: &str) -> Result<f64> {
953 if body.len().saturating_sub(*cursor) < 8 {
954 return Err(FastCacheError::Protocol(format!("{field} is truncated")));
955 }
956 let value = f64::from_le_bytes(body[*cursor..*cursor + 8].try_into().unwrap());
957 *cursor += 8;
958 Ok(value)
959}
960
961fn take_exact_slice<'a>(
962 body: &'a [u8],
963 cursor: &mut usize,
964 len: usize,
965 field: &str,
966) -> Result<&'a [u8]> {
967 if body.len().saturating_sub(*cursor) < len {
968 return Err(FastCacheError::Protocol(format!(
969 "{field} length does not match body"
970 )));
971 }
972 let value = &body[*cursor..*cursor + len];
973 *cursor += len;
974 Ok(value)
975}
976
977fn take_len_prefixed_slice<'a>(
978 body: &'a [u8],
979 cursor: &mut usize,
980 field: &str,
981) -> Result<&'a [u8]> {
982 let len = take_u32(body, cursor, field)? as usize;
983 take_exact_slice(body, cursor, len, field)
984}
985
986fn take_key_value<'a>(
987 body: &'a [u8],
988 cursor: &mut usize,
989 field: &str,
990) -> Result<(&'a [u8], &'a [u8])> {
991 let key_len = take_u32(body, cursor, field)? as usize;
992 let value_len = take_u32(body, cursor, field)? as usize;
993 let key = take_exact_slice(body, cursor, key_len, field)?;
994 let value = take_exact_slice(body, cursor, value_len, field)?;
995 Ok((key, value))
996}
997
998fn take_key_field<'a>(
999 body: &'a [u8],
1000 cursor: &mut usize,
1001 field: &str,
1002) -> Result<(&'a [u8], &'a [u8])> {
1003 let key_len = take_u32(body, cursor, field)? as usize;
1004 let field_len = take_u32(body, cursor, field)? as usize;
1005 let key = take_exact_slice(body, cursor, key_len, field)?;
1006 let member = take_exact_slice(body, cursor, field_len, field)?;
1007 Ok((key, member))
1008}
1009
1010fn take_key_field_value<'a>(
1011 body: &'a [u8],
1012 cursor: &mut usize,
1013 field: &str,
1014) -> Result<(&'a [u8], &'a [u8], &'a [u8])> {
1015 let key_len = take_u32(body, cursor, field)? as usize;
1016 let field_len = take_u32(body, cursor, field)? as usize;
1017 let value_len = take_u32(body, cursor, field)? as usize;
1018 let key = take_exact_slice(body, cursor, key_len, field)?;
1019 let member = take_exact_slice(body, cursor, field_len, field)?;
1020 let value = take_exact_slice(body, cursor, value_len, field)?;
1021 Ok((key, member, value))
1022}
1023
1024fn take_len_prefixed_list<'a>(
1025 body: &'a [u8],
1026 cursor: &mut usize,
1027 field: &str,
1028) -> Result<Vec<&'a [u8]>> {
1029 let count = take_u32(body, cursor, field)? as usize;
1030 let mut values = Vec::with_capacity(count);
1031 for _ in 0..count {
1032 values.push(take_len_prefixed_slice(body, cursor, field)?);
1033 }
1034 Ok(values)
1035}
1036
1037fn take_key_list<'a>(
1038 body: &'a [u8],
1039 cursor: &mut usize,
1040 field: &str,
1041) -> Result<(&'a [u8], Vec<&'a [u8]>)> {
1042 let key = take_len_prefixed_slice(body, cursor, field)?;
1043 let values = take_len_prefixed_list(body, cursor, field)?;
1044 Ok((key, values))
1045}
1046
1047fn take_key_value_list<'a>(
1048 body: &'a [u8],
1049 cursor: &mut usize,
1050 field: &str,
1051) -> Result<Vec<(&'a [u8], &'a [u8])>> {
1052 let count = take_u32(body, cursor, field)? as usize;
1053 let mut items = Vec::with_capacity(count);
1054 for _ in 0..count {
1055 items.push(take_key_value(body, cursor, field)?);
1056 }
1057 Ok(items)
1058}
1059
1060fn ensure_finished(body: &[u8], cursor: usize, field: &str) -> Result<()> {
1061 if cursor != body.len() {
1062 return Err(FastCacheError::Protocol(format!(
1063 "{field} has trailing bytes"
1064 )));
1065 }
1066 Ok(())
1067}
1068
1069fn encode_len_prefixed(value: &[u8], out: &mut Vec<u8>) {
1070 out.extend_from_slice(&(value.len() as u32).to_le_bytes());
1071 out.extend_from_slice(value);
1072}
1073
1074fn encode_key_value(key: &[u8], value: &[u8], out: &mut Vec<u8>) {
1075 out.extend_from_slice(&(key.len() as u32).to_le_bytes());
1076 out.extend_from_slice(&(value.len() as u32).to_le_bytes());
1077 out.extend_from_slice(key);
1078 out.extend_from_slice(value);
1079}
1080
1081fn encode_key_field(key: &[u8], field: &[u8], out: &mut Vec<u8>) {
1082 out.extend_from_slice(&(key.len() as u32).to_le_bytes());
1083 out.extend_from_slice(&(field.len() as u32).to_le_bytes());
1084 out.extend_from_slice(key);
1085 out.extend_from_slice(field);
1086}
1087
1088fn encode_key_field_value(key: &[u8], field: &[u8], value: &[u8], out: &mut Vec<u8>) {
1089 out.extend_from_slice(&(key.len() as u32).to_le_bytes());
1090 out.extend_from_slice(&(field.len() as u32).to_le_bytes());
1091 out.extend_from_slice(&(value.len() as u32).to_le_bytes());
1092 out.extend_from_slice(key);
1093 out.extend_from_slice(field);
1094 out.extend_from_slice(value);
1095}
1096
1097fn encode_len_prefixed_list(values: &[&[u8]], out: &mut Vec<u8>) {
1098 out.extend_from_slice(&(values.len() as u32).to_le_bytes());
1099 for value in values {
1100 encode_len_prefixed(value, out);
1101 }
1102}
1103
1104fn encode_key_list(key: &[u8], values: &[&[u8]], out: &mut Vec<u8>) {
1105 encode_len_prefixed(key, out);
1106 encode_len_prefixed_list(values, out);
1107}
1108
1109fn encode_key_value_list(items: &[(&[u8], &[u8])], out: &mut Vec<u8>) {
1110 out.extend_from_slice(&(items.len() as u32).to_le_bytes());
1111 for (key, value) in items {
1112 encode_key_value(key, value, out);
1113 }
1114}
1115
1116fn encode_array_body<'a>(values: impl IntoIterator<Item = Option<&'a [u8]>>, out: &mut Vec<u8>) {
1117 let values = values.into_iter().collect::<Vec<_>>();
1118 out.extend_from_slice(&(values.len() as u32).to_le_bytes());
1119 for value in values {
1120 match value {
1121 Some(value) => {
1122 out.extend_from_slice(&(value.len() as u32).to_le_bytes());
1123 out.extend_from_slice(value);
1124 }
1125 None => out.extend_from_slice(&u32::MAX.to_le_bytes()),
1126 }
1127 }
1128}
1129
1130fn decode_array_body(body: &[u8]) -> Result<Vec<Option<Vec<u8>>>> {
1131 let mut cursor = 0usize;
1132 let count = take_u32(body, &mut cursor, "fast array count")? as usize;
1133 let mut values = Vec::with_capacity(count);
1134 for _ in 0..count {
1135 let len = take_u32(body, &mut cursor, "fast array item")?;
1136 if len == u32::MAX {
1137 values.push(None);
1138 } else {
1139 values.push(Some(
1140 take_exact_slice(body, &mut cursor, len as usize, "fast array item")?.to_vec(),
1141 ));
1142 }
1143 }
1144 ensure_finished(body, cursor, "fast array response")?;
1145 Ok(values)
1146}
1147
1148#[cfg(test)]
1149mod tests {
1150 use super::{FAST_PROTOCOL_VERSION, FastCodec, FastCommand, FastRequest, FastResponse};
1151
1152 #[test]
1153 fn round_trips_get_request_with_hash_and_shard() {
1154 let request = FastRequest {
1155 key_hash: Some(42),
1156 route_shard: Some(3),
1157 key_tag: Some(99),
1158 command: FastCommand::Get { key: b"alpha" },
1159 };
1160 let mut encoded = Vec::new();
1161 FastCodec::encode_request(&request, &mut encoded);
1162 assert_eq!(encoded[1], FAST_PROTOCOL_VERSION);
1163 let decoded = FastCodec::decode_request(&encoded).unwrap().unwrap().0;
1164 assert_eq!(decoded, request);
1165 }
1166
1167 #[test]
1168 fn round_trips_set_request() {
1169 let request = FastRequest {
1170 key_hash: Some(7),
1171 route_shard: None,
1172 key_tag: Some(13),
1173 command: FastCommand::Set {
1174 key: b"alpha",
1175 value: b"beta",
1176 },
1177 };
1178 let mut encoded = Vec::new();
1179 FastCodec::encode_request(&request, &mut encoded);
1180 let decoded = FastCodec::decode_request(&encoded).unwrap().unwrap().0;
1181 assert_eq!(decoded, request);
1182 }
1183
1184 #[test]
1185 fn round_trips_setex_request() {
1186 let request = FastRequest {
1187 key_hash: Some(7),
1188 route_shard: None,
1189 key_tag: Some(13),
1190 command: FastCommand::SetEx {
1191 key: b"alpha",
1192 value: b"beta",
1193 ttl_ms: 60_000,
1194 },
1195 };
1196 let mut encoded = Vec::new();
1197 FastCodec::encode_request(&request, &mut encoded);
1198 let decoded = FastCodec::decode_request(&encoded).unwrap().unwrap().0;
1199 assert_eq!(decoded, request);
1200 }
1201
1202 #[test]
1203 fn round_trips_getex_request() {
1204 let request = FastRequest {
1205 key_hash: Some(11),
1206 route_shard: None,
1207 key_tag: None,
1208 command: FastCommand::GetEx {
1209 key: b"alpha",
1210 ttl_ms: 5_000,
1211 },
1212 };
1213 let mut encoded = Vec::new();
1214 FastCodec::encode_request(&request, &mut encoded);
1215 let decoded = FastCodec::decode_request(&encoded).unwrap().unwrap().0;
1216 assert_eq!(decoded, request);
1217 }
1218
1219 #[test]
1220 fn round_trips_probe_requests() {
1221 let requests = [
1222 FastRequest {
1223 key_hash: None,
1224 route_shard: None,
1225 key_tag: None,
1226 command: FastCommand::Hello { proto: Some(2) },
1227 },
1228 FastRequest {
1229 key_hash: None,
1230 route_shard: None,
1231 key_tag: None,
1232 command: FastCommand::Select { db: 0 },
1233 },
1234 FastRequest {
1235 key_hash: None,
1236 route_shard: None,
1237 key_tag: None,
1238 command: FastCommand::Echo { payload: b"hello" },
1239 },
1240 FastRequest {
1241 key_hash: None,
1242 route_shard: None,
1243 key_tag: None,
1244 command: FastCommand::ConfigGet {
1245 patterns: vec![b"*".as_slice()],
1246 },
1247 },
1248 FastRequest {
1249 key_hash: None,
1250 route_shard: None,
1251 key_tag: None,
1252 command: FastCommand::ClientSetName { name: b"bench" },
1253 },
1254 FastRequest {
1255 key_hash: None,
1256 route_shard: None,
1257 key_tag: None,
1258 command: FastCommand::DbSize,
1259 },
1260 ];
1261
1262 for request in requests {
1263 let mut encoded = Vec::new();
1264 FastCodec::encode_request(&request, &mut encoded);
1265 let decoded = FastCodec::decode_request(&encoded).unwrap().unwrap().0;
1266 assert_eq!(decoded, request);
1267 }
1268 }
1269
1270 #[test]
1271 fn round_trips_hash_object_request() {
1272 let request = FastRequest {
1273 key_hash: Some(12),
1274 route_shard: Some(1),
1275 key_tag: None,
1276 command: FastCommand::HSet {
1277 key: b"user:1",
1278 field: b"name",
1279 value: b"ada",
1280 },
1281 };
1282 let mut encoded = Vec::new();
1283 FastCodec::encode_request(&request, &mut encoded);
1284 let decoded = FastCodec::decode_request(&encoded).unwrap().unwrap().0;
1285 assert_eq!(decoded, request);
1286 }
1287
1288 #[test]
1289 fn round_trips_multivalue_object_request() {
1290 let request = FastRequest {
1291 key_hash: Some(13),
1292 route_shard: None,
1293 key_tag: None,
1294 command: FastCommand::HMGet {
1295 key: b"user:1",
1296 fields: vec![b"name".as_slice(), b"email".as_slice()],
1297 },
1298 };
1299 let mut encoded = Vec::new();
1300 FastCodec::encode_request(&request, &mut encoded);
1301 let decoded = FastCodec::decode_request(&encoded).unwrap().unwrap().0;
1302 assert_eq!(decoded, request);
1303 }
1304
1305 #[test]
1306 fn round_trips_range_and_zadd_requests() {
1307 let range = FastRequest {
1308 key_hash: Some(14),
1309 route_shard: None,
1310 key_tag: None,
1311 command: FastCommand::LRange {
1312 key: b"items",
1313 start: -2,
1314 stop: -1,
1315 },
1316 };
1317 let zadd = FastRequest {
1318 key_hash: Some(15),
1319 route_shard: None,
1320 key_tag: None,
1321 command: FastCommand::ZAdd {
1322 key: b"scores",
1323 score: 42.5,
1324 member: b"ada",
1325 },
1326 };
1327 for request in [range, zadd] {
1328 let mut encoded = Vec::new();
1329 FastCodec::encode_request(&request, &mut encoded);
1330 let decoded = FastCodec::decode_request(&encoded).unwrap().unwrap().0;
1331 assert_eq!(decoded, request);
1332 }
1333 }
1334
1335 #[test]
1336 fn round_trips_generic_resp_command_request() {
1337 let request = FastRequest {
1338 key_hash: None,
1339 route_shard: None,
1340 key_tag: None,
1341 command: FastCommand::RespCommand {
1342 parts: vec![
1343 b"HSET".as_slice(),
1344 b"h".as_slice(),
1345 b"f".as_slice(),
1346 b"v".as_slice(),
1347 ],
1348 },
1349 };
1350 let mut encoded = Vec::new();
1351 FastCodec::encode_request(&request, &mut encoded);
1352 let decoded = FastCodec::decode_request(&encoded).unwrap().unwrap().0;
1353 assert_eq!(decoded, request);
1354 assert_eq!(decoded.command.route_key(), Some(b"h".as_slice()));
1355 }
1356
1357 #[test]
1358 fn encodes_value_response() {
1359 let mut encoded = Vec::new();
1360 FastCodec::encode_response(&FastResponse::Value(b"payload".to_vec()), &mut encoded);
1361 assert_eq!(encoded[0], super::FAST_RESPONSE_MAGIC);
1362 assert_eq!(encoded[1], FAST_PROTOCOL_VERSION);
1363 }
1364
1365 #[test]
1366 fn round_trips_integer_response() {
1367 let mut encoded = Vec::new();
1368 FastCodec::encode_response(&FastResponse::Integer(42), &mut encoded);
1369 let decoded = FastCodec::decode_response(&encoded).unwrap().unwrap().0;
1370 assert_eq!(decoded, FastResponse::Integer(42));
1371 }
1372
1373 #[test]
1374 fn round_trips_array_and_float_responses() {
1375 let array = FastResponse::Array(vec![
1376 Some(b"ada".to_vec()),
1377 None,
1378 Some(b"lovelace".to_vec()),
1379 ]);
1380 let float = FastResponse::Float(12.25);
1381 for response in [array, float] {
1382 let mut encoded = Vec::new();
1383 FastCodec::encode_response(&response, &mut encoded);
1384 let decoded = FastCodec::decode_response(&encoded).unwrap().unwrap().0;
1385 assert_eq!(decoded, response);
1386 }
1387 }
1388}