use crate::error::{RedisParseError, RedisProtocolError, RedisProtocolErrorKind};
use core::str;
use crc16::{State, XMODEM};
use crate::types::REDIS_CLUSTER_SLOTS;
#[cfg(feature = "bytes")]
use bytes::BytesMut;
#[cfg(feature = "std")]
pub fn digits_in_number(d: usize) -> usize {
if d == 0 {
return 1;
}
((d as f64).log10()).floor() as usize + 1
}
#[cfg(feature = "libm")]
pub fn digits_in_number(d: usize) -> usize {
if d == 0 {
return 1;
}
libm::floor(libm::log10(d as f64)) as usize + 1
}
pub fn isize_to_usize<T>(val: isize) -> Result<usize, RedisParseError<T>> {
if val >= 0 {
Ok(val as usize)
} else {
Err(RedisParseError::new_custom("isize_to_usize", "Invalid length."))
}
}
#[cfg(feature = "bytes")]
#[cfg_attr(docsrs, doc(cfg(feature = "bytes")))]
pub fn zero_extend(buf: &mut BytesMut, amt: usize) {
buf.resize(buf.len() + amt, 0);
}
pub(crate) fn is_redirection(payload: &str) -> bool {
if payload.starts_with("MOVED") || payload.starts_with("ASK") {
payload.split(' ').count() == 3
} else {
false
}
}
fn crc16_xmodem(key: &[u8]) -> u16 {
State::<XMODEM>::calculate(key) % REDIS_CLUSTER_SLOTS
}
pub fn redis_keyslot(key: &[u8]) -> u16 {
let (mut i, mut j): (Option<usize>, Option<usize>) = (None, None);
for (idx, c) in key.iter().enumerate() {
if *c == b'{' {
i = Some(idx);
break;
}
}
if i.is_none() || (i.is_some() && i.unwrap() == key.len() - 1) {
return crc16_xmodem(key);
}
let i = i.unwrap();
for (idx, c) in key[i + 1 ..].iter().enumerate() {
if *c == b'}' {
j = Some(idx);
break;
}
}
if j.is_none() {
return crc16_xmodem(key);
}
let j = j.unwrap();
if i + j == key.len() || j == 0 {
crc16_xmodem(key)
} else {
crc16_xmodem(&key[i + 1 .. i + j + 1])
}
}
pub fn str_to_f64(s: &str) -> Result<f64, RedisProtocolError> {
match s {
"+inf" | "inf" => Ok(f64::INFINITY),
"-inf" => Ok(f64::NEG_INFINITY),
"nan" => Ok(f64::NAN),
_ => s.parse::<f64>().map_err(|_| {
RedisProtocolError::new(
RedisProtocolErrorKind::Unknown,
"Could not convert to floating point value.",
)
}),
}
}
pub(crate) fn bytes_to_bool(b: &[u8]) -> Option<bool> {
match b {
b"true" | b"TRUE" | b"t" | b"T" | b"1" => Some(true),
b"false" | b"FALSE" | b"f" | b"F" | b"0" => Some(false),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec::Vec;
fn read_kitten_file() -> Vec<u8> {
include_bytes!("../tests/kitten.jpeg").to_vec()
}
#[test]
fn should_crc16_123456789() {
let key = "123456789";
let expected: u16 = 12739;
let actual = redis_keyslot(key.as_bytes());
assert_eq!(actual, expected);
}
#[test]
fn should_crc16_with_brackets() {
let key = "foo{123456789}bar";
let expected: u16 = 12739;
let actual = redis_keyslot(key.as_bytes());
assert_eq!(actual, expected);
}
#[test]
fn should_crc16_with_brackets_no_padding() {
let key = "{123456789}";
let expected: u16 = 12739;
let actual = redis_keyslot(key.as_bytes());
assert_eq!(actual, expected);
}
#[test]
fn should_crc16_with_invalid_brackets_lhs() {
let key = "foo{123456789";
let expected: u16 = 10378;
let actual = redis_keyslot(key.as_bytes());
assert_eq!(actual, expected);
}
#[test]
fn should_crc16_with_invalid_brackets_rhs() {
let key = "foo}123456789";
let expected: u16 = 6965;
let actual = redis_keyslot(key.as_bytes());
assert_eq!(actual, expected);
}
#[test]
fn should_crc16_with_random_string() {
let key = "8xjx7vWrfPq54mKfFD3Y1CcjjofpnAcQ";
let expected: u16 = 5458;
let actual = redis_keyslot(key.as_bytes());
assert_eq!(actual, expected);
}
#[test]
fn should_hash_non_ascii_string_bytes() {
let key = "💩 👻 💀 ☠️ 👽 👾";
let expected: u16 = 13954;
let actual = redis_keyslot(key.as_bytes());
assert_eq!(actual, expected);
}
#[test]
fn should_hash_non_ascii_string_bytes_with_tag() {
let key = "💩 👻 💀{123456789}☠️ 👽 👾";
let expected: u16 = 12739;
let actual = redis_keyslot(key.as_bytes());
assert_eq!(actual, expected);
}
#[test]
fn should_hash_non_utf8_string_bytes() {
let key = read_kitten_file();
let expected: u16 = 1589;
let actual = redis_keyslot(&key);
assert_eq!(actual, expected)
}
#[test]
fn should_hash_non_utf8_string_bytes_with_tag() {
let mut key = read_kitten_file();
for (idx, c) in "{123456789}".as_bytes().iter().enumerate() {
key[242 + idx] = *c;
}
let expected: u16 = 12739;
let actual = redis_keyslot(&key);
assert_eq!(actual, expected)
}
}