use bytes::BytesMut;
use cookie_factory::GenError;
use crc16::{State, XMODEM};
use types::*;
pub const CRLF: &'static str = "\r\n";
pub const NULL: &'static str = "$-1\r\n";
pub const KB: usize = 1024;
pub const ZEROED_KB: &'static [u8; 1024] = &[0; 1024];
const REDIS_CLUSTER_SLOTS: u16 = 16384;
const PUBSUB_PREFIX: &'static str = "message";
const PATTERN_PUBSUB_PREFIX: &'static str = "pmessage";
#[inline]
pub fn check_offset(x: &(&mut [u8], usize)) -> Result<(), GenError> {
if x.1 > x.0.len() {
Err(GenError::InvalidOffset)
} else {
Ok(())
}
}
#[inline]
pub fn digits_in_number(d: usize) -> usize {
if d == 0 {
return 1;
}
((d as f64).log10()).floor() as usize + 1
}
#[inline]
pub fn bulkstring_encode_len(b: &[u8]) -> usize {
1 + digits_in_number(b.len()) + 2 + b.len() + 2
}
#[inline]
pub fn array_encode_len(frames: &Vec<Frame>) -> Result<usize, GenError> {
let padding = 1 + digits_in_number(frames.len()) + 2;
frames.iter().fold(Ok(padding), |m, f| {
m.and_then(|s| encode_len(f).map(|l| s + l))
})
}
#[inline]
pub fn simplestring_encode_len(s: &str) -> usize {
1 + s.len() + 2
}
#[inline]
pub fn error_encode_len(s: &str) -> usize {
1 + s.len() + 2
}
#[inline]
pub fn integer_encode_len(i: &i64) -> usize {
let prefix = if *i < 0 { 1 } else { 0 };
let as_usize = if *i < 0 {
(*i * -1) as usize
} else {
*i as usize
};
1 + digits_in_number(as_usize) + 2 + prefix
}
pub fn encode_len(data: &Frame) -> Result<usize, GenError> {
match *data {
Frame::BulkString(ref b) => Ok(bulkstring_encode_len(&b)),
Frame::Array(ref frames) => array_encode_len(frames),
Frame::Null => Ok(NULL.as_bytes().len()),
Frame::SimpleString(ref s) => Ok(simplestring_encode_len(s)),
Frame::Error(ref s) => Ok(error_encode_len(s)),
Frame::Integer(ref i) => Ok(integer_encode_len(i)),
Frame::Moved(ref s) => Ok(error_encode_len(s)),
Frame::Ask(ref s) => Ok(error_encode_len(s)),
}
}
pub fn zero_extend(buf: &mut BytesMut, mut amt: usize) {
trace!("allocating more, len: {}, amt: {}", buf.len(), amt);
buf.reserve(amt);
while amt >= KB {
buf.extend_from_slice(ZEROED_KB);
amt -= KB;
}
if amt > 0 {
buf.extend_from_slice(&ZEROED_KB[0..amt]);
}
}
#[inline]
pub fn redirection_to_frame(prefix: &'static str, slot: u16, host: &str, port: u16) -> String {
format!("{} {} {}:{}", prefix, slot, host, port)
}
pub fn string_to_redirection(s: &str) -> Result<Redirection, RedisProtocolError> {
let parts: Vec<&str> = s.split(" ").collect();
if parts.len() != 3 {
return Err(RedisProtocolError::new(
RedisProtocolErrorKind::Unknown,
"Invalid redirection.",
));
}
let is_moved = match parts[0].as_ref() {
"MOVED" => true,
"ASK" => false,
_ => {
return Err(RedisProtocolError::new(
RedisProtocolErrorKind::Unknown,
"Invalid redirection kind.",
))
}
};
let slot = match parts[1].parse::<u16>() {
Ok(s) => s,
Err(_) => {
return Err(RedisProtocolError::new(
RedisProtocolErrorKind::Unknown,
"Invalid hash slot redirection.",
))
}
};
let address_parts: Vec<&str> = parts[2].split(":").collect();
if address_parts.len() != 2 {
return Err(RedisProtocolError::new(
RedisProtocolErrorKind::Unknown,
"Invalid redirection address.",
));
}
let host = address_parts[0].to_owned();
let port = match address_parts[1].parse::<u16>() {
Ok(p) => p,
Err(_) => {
return Err(RedisProtocolError::new(
RedisProtocolErrorKind::Unknown,
"Invalid redirection address port.",
))
}
};
if is_moved {
Ok(Redirection::Moved { slot, host, port })
} else {
Ok(Redirection::Ask { slot, host, port })
}
}
#[inline]
fn crc16_xmodem(key: &str) -> u16 {
State::<XMODEM>::calculate(key.as_bytes()) % REDIS_CLUSTER_SLOTS
}
pub fn redis_keyslot(key: &str) -> u16 {
let (mut i, mut j): (Option<usize>, Option<usize>) = (None, None);
for (idx, c) in key.chars().enumerate() {
if c == '{' {
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..].chars().enumerate() {
if c == '}' {
j = Some(idx);
break;
}
}
if j.is_none() {
return crc16_xmodem(key);
}
let j = j.unwrap();
let out = if i + j == key.len() || j == 0 {
crc16_xmodem(key)
} else {
crc16_xmodem(&key[i + 1..i + j + 1])
};
trace!("mapped {} to redis slot {}", key, out);
out
}
pub fn read_cluster_error(payload: &str) -> Option<Frame> {
if payload.starts_with("MOVED") {
let parts: Vec<&str> = payload.split(" ").collect();
Some(Frame::Moved(parts[1..].join(" ").to_owned()))
} else if payload.starts_with("ASK") {
let parts: Vec<&str> = payload.split(" ").collect();
Some(Frame::Ask(parts[1..].join(" ").to_owned()))
} else {
None
}
}
pub fn opt_frame_to_string_panic(f: Option<Frame>, msg: &str) -> String {
f.expect(msg).to_string().expect(msg)
}
pub fn is_normal_pubsub(frames: &Vec<Frame>) -> bool {
frames.len() == 3
&& frames[0].kind() == FrameKind::BulkString
&& frames[0]
.as_str()
.map(|s| s == PUBSUB_PREFIX)
.unwrap_or(false)
}
pub fn is_pattern_pubsub(frames: &Vec<Frame>) -> bool {
frames.len() == 4
&& frames[0].kind() == FrameKind::BulkString
&& frames[0]
.as_str()
.map(|s| s == PATTERN_PUBSUB_PREFIX)
.unwrap_or(false)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_get_encode_len_simplestring() {
let ss1 = "Ok";
let ss2 = "FooBarBaz";
let ss3 = "-&#$@9232";
assert_eq!(simplestring_encode_len(ss1), 5);
assert_eq!(simplestring_encode_len(ss2), 12);
assert_eq!(simplestring_encode_len(ss3), 12);
}
#[test]
fn should_get_encode_len_error() {
let e1 = "MOVED 3999 127.0.0.1:6381";
let e2 = "ERR unknown command 'foobar'";
let e3 = "WRONGTYPE Operation against a key holding the wrong kind of value";
assert_eq!(error_encode_len(e1), 28);
assert_eq!(error_encode_len(e2), 31);
assert_eq!(error_encode_len(e3), 68);
}
#[test]
fn should_get_encode_len_integer() {
let i1: i64 = 38473;
let i2: i64 = -74834;
assert_eq!(integer_encode_len(&i1), 8);
assert_eq!(integer_encode_len(&i2), 9);
}
#[test]
fn should_crc16_123456789() {
let key = "123456789";
let expected: u16 = 12739;
let actual = redis_keyslot(key);
assert_eq!(actual, expected);
}
#[test]
fn should_crc16_with_brackets() {
let key = "foo{123456789}bar";
let expected: u16 = 12739;
let actual = redis_keyslot(key);
assert_eq!(actual, expected);
}
#[test]
fn should_crc16_with_brackets_no_padding() {
let key = "{123456789}";
let expected: u16 = 12739;
let actual = redis_keyslot(key);
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);
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);
assert_eq!(actual, expected);
}
#[test]
fn should_crc16_with_random_string() {
let key = "8xjx7vWrfPq54mKfFD3Y1CcjjofpnAcQ";
let expected: u16 = 5458;
let actual = redis_keyslot(key);
assert_eq!(actual, expected);
}
}