use crate::error::{Error, Result};
use serde::{Deserialize, Serialize};
pub const HEX_LEN: usize = 16;
pub const HEX_CHARS: &[u8; 16] = b"0123456789abcdef";
const HEX_DECODE_LUT: [i8; 256] = {
let mut table = [-1i8; 256];
let mut i = 0;
while i < 10 {
table[(b'0' + i) as usize] = i as i8;
i += 1;
}
let mut i = 0;
while i < 6 {
table[(b'a' + i) as usize] = (10 + i) as i8;
table[(b'A' + i) as usize] = (10 + i) as i8;
i += 1;
}
table
};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SortedintRangeSpec {
pub min: u64,
pub max: u64,
pub minex: bool,
pub maxex: bool,
pub offset: usize,
pub count: Option<usize>,
pub reversed: bool,
}
impl Default for SortedintRangeSpec {
#[inline]
fn default() -> Self {
Self {
min: u64::MIN,
max: u64::MAX,
minex: false,
maxex: false,
offset: 0,
count: None,
reversed: false,
}
}
}
impl SortedintRangeSpec {
#[inline]
pub const fn all() -> Self {
Self {
min: u64::MIN,
max: u64::MAX,
minex: false,
maxex: false,
offset: 0,
count: None,
reversed: false,
}
}
#[inline]
pub const fn with_offset(mut self, offset: usize) -> Self {
self.offset = offset;
self
}
#[inline]
pub const fn with_count(mut self, count: usize) -> Self {
self.count = Some(count);
self
}
#[inline]
pub const fn with_reversed(mut self, reversed: bool) -> Self {
self.reversed = reversed;
self
}
#[inline]
pub const fn is_empty_range(&self) -> bool {
if self.min > self.max {
return true;
}
if self.min == self.max && (self.minex || self.maxex) {
return true;
}
false
}
}
#[inline]
fn parse_bound(s: &str, is_min: bool) -> Result<(u64, bool)> {
let (num_str, ex) = if let Some(stripped) = s.strip_prefix('(') {
(stripped, true)
} else if let Some(stripped) = s.strip_prefix('[') {
(stripped, false)
} else {
(s, false)
};
let val = num_str.parse::<u64>().map_err(|_| {
let label = if is_min { "min" } else { "max" };
Error::invalid_data(format!("ERR the {label} isn't integer"))
})?;
Ok((val, ex))
}
pub fn parse_range_spec(min_str: &str, max_str: &str) -> Result<SortedintRangeSpec> {
let min_str = min_str.trim();
let max_str = max_str.trim();
if min_str == "+inf" || max_str == "-inf" {
return Err(Error::invalid_data("ERR min > max"));
}
let (min, minex) = if min_str == "-inf" {
(u64::MIN, false)
} else {
parse_bound(min_str, true)?
};
let (max, maxex) = if max_str == "+inf" {
(u64::MAX, false)
} else {
parse_bound(max_str, false)?
};
Ok(SortedintRangeSpec {
min,
max,
minex,
maxex,
offset: 0,
count: None,
reversed: false,
})
}
#[inline(always)]
pub const fn decode_hex_u64(hex: &[u8]) -> Option<u64> {
if hex.len() != HEX_LEN {
return None;
}
let mut val = 0u64;
let mut i = 0;
while i < HEX_LEN {
let d = HEX_DECODE_LUT[hex[i] as usize];
if d < 0 {
return None;
}
val = (val << 4) | (d as u64);
i += 1;
}
Some(val)
}
#[inline(always)]
pub const fn encode_hex_u64(val: u64) -> [u8; HEX_LEN] {
let mut buf = [0u8; HEX_LEN];
let mut i = 0;
while i < HEX_LEN {
let shift = (15 - i) * 4;
buf[i] = HEX_CHARS[((val >> shift) & 0xF) as usize];
i += 1;
}
buf
}
#[inline(always)]
pub const fn encode_be_u64(val: u64) -> [u8; 8] {
val.to_be_bytes()
}
#[inline(always)]
pub const fn decode_be_u64(bytes: &[u8]) -> Option<u64> {
if bytes.len() != 8 {
return None;
}
let mut buf = [0u8; 8];
let mut i = 0;
while i < 8 {
buf[i] = bytes[i];
i += 1;
}
Some(u64::from_be_bytes(buf))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_range_spec_cases() {
let spec1 = parse_range_spec("-inf", "+inf").unwrap();
assert_eq!(spec1.min, u64::MIN);
assert_eq!(spec1.max, u64::MAX);
assert!(!spec1.minex);
assert!(!spec1.maxex);
assert!(!spec1.is_empty_range());
let spec2 = parse_range_spec("(10", "[200").unwrap();
assert_eq!(spec2.min, 10);
assert_eq!(spec2.max, 200);
assert!(spec2.minex);
assert!(!spec2.maxex);
assert!(!spec2.is_empty_range());
let spec3 = parse_range_spec("20", "(300").unwrap();
assert_eq!(spec3.min, 20);
assert_eq!(spec3.max, 300);
assert!(!spec3.minex);
assert!(spec3.maxex);
assert!(!spec3.is_empty_range());
let empty_spec1 = parse_range_spec("300", "200").unwrap();
assert!(empty_spec1.is_empty_range());
let empty_spec2 = parse_range_spec("(200", "200").unwrap();
assert!(empty_spec2.is_empty_range());
let empty_spec3 = parse_range_spec("200", "(200").unwrap();
assert!(empty_spec3.is_empty_range());
assert!(parse_range_spec("+inf", "100").is_err());
assert!(parse_range_spec("100", "-inf").is_err());
assert!(parse_range_spec("abc", "100").is_err());
assert!(parse_range_spec("100", "xyz").is_err());
}
#[test]
fn test_builder_methods() {
let spec = SortedintRangeSpec::all()
.with_offset(5)
.with_count(10)
.with_reversed(true);
assert_eq!(spec.offset, 5);
assert_eq!(spec.count, Some(10));
assert!(spec.reversed);
assert_eq!(spec.min, u64::MIN);
assert_eq!(spec.max, u64::MAX);
}
#[test]
fn test_hex_u64_codec() {
let test_cases = [0u64, 1, 15, 16, 255, 1000, 1234567890123456789, u64::MAX];
for &val in &test_cases {
let encoded = encode_hex_u64(val);
let decoded = decode_hex_u64(&encoded).expect("Decode should succeed");
assert_eq!(val, decoded);
let expected_hex = format!("{val:016x}");
assert_eq!(encoded, expected_hex.as_bytes());
}
assert_eq!(decode_hex_u64(b"invalid_length"), None);
assert_eq!(decode_hex_u64(b"000000000000000g"), None);
assert_eq!(decode_hex_u64(b"000000000000000A"), Some(10));
assert_eq!(decode_hex_u64(b"000000000000000F"), Some(15));
}
#[test]
fn test_be_u64_codec() {
let test_cases = [0u64, 1, 42, 1024, 0x123456789ABCDEF0, u64::MAX];
for &val in &test_cases {
let encoded = encode_be_u64(val);
assert_eq!(encoded, val.to_be_bytes());
let decoded = decode_be_u64(&encoded).expect("Decode BE u64 should succeed");
assert_eq!(val, decoded);
}
assert_eq!(decode_be_u64(b"short"), None);
}
}