1use crate::error::{RedisParseError, RedisProtocolError, RedisProtocolErrorKind};
2use core::str;
3use crc16::{State, XMODEM};
4
5use crate::types::REDIS_CLUSTER_SLOTS;
6#[cfg(feature = "bytes")]
7use bytes::BytesMut;
8
9#[cfg(feature = "std")]
11pub fn digits_in_usize(d: usize) -> usize {
12 if d == 0 {
13 return 1;
14 }
15
16 ((d as f64).log10()).floor() as usize + 1
17}
18
19#[cfg(feature = "libm")]
21pub fn digits_in_usize(d: usize) -> usize {
22 if d == 0 {
23 return 1;
24 }
25
26 libm::floor(libm::log10(d as f64)) as usize + 1
27}
28
29#[cfg(feature = "std")]
31pub fn digits_in_i64(d: i64) -> usize {
32 if d == 0 {
33 return 1;
34 }
35 let prefix = if d.is_negative() { 1 } else { 0 };
36
37 prefix + ((d.unsigned_abs() as f64).log10()).floor() as usize + 1
38}
39
40#[cfg(feature = "libm")]
42pub fn digits_in_i64(d: i64) -> usize {
43 if d == 0 {
44 return 1;
45 }
46 let prefix = if d.is_negative() { 1 } else { 0 };
47
48 prefix + libm::floor(libm::log10(d.unsigned_abs() as f64)) as usize + 1
49}
50
51pub fn isize_to_usize<T>(val: isize) -> Result<usize, RedisParseError<T>> {
52 if val >= 0 {
53 Ok(val as usize)
54 } else {
55 Err(RedisParseError::new_custom("isize_to_usize", "Invalid length."))
56 }
57}
58
59#[cfg(feature = "bytes")]
61#[cfg_attr(docsrs, doc(cfg(feature = "bytes")))]
62pub fn zero_extend(buf: &mut BytesMut, amt: usize) {
63 buf.resize(buf.len() + amt, 0);
64}
65
66pub(crate) fn is_redirection(payload: &str) -> bool {
68 if payload.starts_with("MOVED") || payload.starts_with("ASK") {
69 payload.split(' ').count() == 3
70 } else {
71 false
72 }
73}
74
75fn crc16_xmodem(key: &[u8]) -> u16 {
77 State::<XMODEM>::calculate(key) % REDIS_CLUSTER_SLOTS
78}
79
80pub fn redis_keyslot(key: &[u8]) -> u16 {
92 let (mut i, mut j): (Option<usize>, Option<usize>) = (None, None);
93
94 for (idx, c) in key.iter().enumerate() {
95 if *c == b'{' {
96 i = Some(idx);
97 break;
98 }
99 }
100
101 if i.is_none() || (i.is_some() && i.unwrap() == key.len() - 1) {
102 return crc16_xmodem(key);
103 }
104
105 let i = i.unwrap();
106 for (idx, c) in key[i + 1 ..].iter().enumerate() {
107 if *c == b'}' {
108 j = Some(idx);
109 break;
110 }
111 }
112
113 if j.is_none() {
114 return crc16_xmodem(key);
115 }
116
117 let j = j.unwrap();
118 if i + j == key.len() || j == 0 {
119 crc16_xmodem(key)
120 } else {
121 crc16_xmodem(&key[i + 1 .. i + j + 1])
122 }
123}
124
125pub fn str_to_f64(s: &str) -> Result<f64, RedisProtocolError> {
127 match s {
129 "+inf" | "inf" => Ok(f64::INFINITY),
130 "-inf" => Ok(f64::NEG_INFINITY),
131 "nan" => Ok(f64::NAN),
132 _ => s.parse::<f64>().map_err(|_| {
133 RedisProtocolError::new(
134 RedisProtocolErrorKind::Unknown,
135 "Could not convert to floating point value.",
136 )
137 }),
138 }
139}
140
141pub(crate) fn bytes_to_bool(b: &[u8]) -> Option<bool> {
143 match b {
144 b"true" | b"TRUE" | b"t" | b"T" | b"1" => Some(true),
145 b"false" | b"FALSE" | b"f" | b"F" | b"0" => Some(false),
146 _ => None,
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153 use alloc::vec::Vec;
154
155 fn read_kitten_file() -> Vec<u8> {
156 include_bytes!("../tests/kitten.jpeg").to_vec()
157 }
158
159 #[test]
160 fn should_crc16_123456789() {
161 let key = "123456789";
162 let expected: u16 = 12739;
164 let actual = redis_keyslot(key.as_bytes());
165
166 assert_eq!(actual, expected);
167 }
168
169 #[test]
170 fn should_crc16_with_brackets() {
171 let key = "foo{123456789}bar";
172 let expected: u16 = 12739;
174 let actual = redis_keyslot(key.as_bytes());
175
176 assert_eq!(actual, expected);
177 }
178
179 #[test]
180 fn should_crc16_with_brackets_no_padding() {
181 let key = "{123456789}";
182 let expected: u16 = 12739;
184 let actual = redis_keyslot(key.as_bytes());
185
186 assert_eq!(actual, expected);
187 }
188
189 #[test]
190 fn should_crc16_with_invalid_brackets_lhs() {
191 let key = "foo{123456789";
192 let expected: u16 = 10378;
194 let actual = redis_keyslot(key.as_bytes());
195
196 assert_eq!(actual, expected);
197 }
198
199 #[test]
200 fn should_crc16_with_invalid_brackets_rhs() {
201 let key = "foo}123456789";
202 let expected: u16 = 6965;
204 let actual = redis_keyslot(key.as_bytes());
205
206 assert_eq!(actual, expected);
207 }
208
209 #[test]
210 fn should_crc16_with_random_string() {
211 let key = "8xjx7vWrfPq54mKfFD3Y1CcjjofpnAcQ";
212 let expected: u16 = 5458;
215 let actual = redis_keyslot(key.as_bytes());
216
217 assert_eq!(actual, expected);
218 }
219
220 #[test]
221 fn should_hash_non_ascii_string_bytes() {
222 let key = "💩 👻 💀 ☠️ 👽 👾";
223 let expected: u16 = 13954;
226 let actual = redis_keyslot(key.as_bytes());
227
228 assert_eq!(actual, expected);
229 }
230
231 #[test]
232 fn should_hash_non_ascii_string_bytes_with_tag() {
233 let key = "💩 👻 💀{123456789}☠️ 👽 👾";
234 let expected: u16 = 12739;
237 let actual = redis_keyslot(key.as_bytes());
238
239 assert_eq!(actual, expected);
240 }
241
242 #[test]
243 fn should_hash_non_utf8_string_bytes() {
244 let key = read_kitten_file();
245 let expected: u16 = 1589;
246 let actual = redis_keyslot(&key);
247
248 assert_eq!(actual, expected)
249 }
250
251 #[test]
252 fn should_hash_non_utf8_string_bytes_with_tag() {
253 let mut key = read_kitten_file();
254 for (idx, c) in "{123456789}".as_bytes().iter().enumerate() {
255 key[242 + idx] = *c;
256 }
257
258 let expected: u16 = 12739;
259 let actual = redis_keyslot(&key);
260 assert_eq!(actual, expected)
261 }
262}