use crate::{
api::sortedint::r#const::{ERR_MAX_NOT_INT, ERR_MIN_GT_MAX, ERR_MIN_NOT_INT},
error::{Error, Result},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, bitcode::Encode, bitcode::Decode)]
pub struct SortedintRange {
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 SortedintRange {
#[inline]
fn default() -> Self {
Self {
min: u64::MIN,
max: u64::MAX,
minex: false,
maxex: false,
offset: 0,
count: None,
reversed: false,
}
}
}
impl SortedintRange {
#[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 with_min(mut self, min: u64, minex: bool) -> Self {
self.min = min;
self.minex = minex;
self
}
#[inline]
pub const fn with_max(mut self, max: u64, maxex: bool) -> Self {
self.max = max;
self.maxex = maxex;
self
}
#[inline]
pub const fn is_empty_range(&self) -> bool {
if self.min > self.max {
return true;
}
let eff_min = if self.minex {
match self.min.checked_add(1) {
Some(v) => v,
None => return true,
}
} else {
self.min
};
let eff_max = if self.maxex {
match self.max.checked_sub(1) {
Some(v) => v,
None => return true,
}
} else {
self.max
};
eff_min > eff_max
}
#[inline]
pub const fn contains(&self, val: u64) -> bool {
if self.minex {
if val <= self.min {
return false;
}
} else if val < self.min {
return false;
}
if self.maxex {
if val >= self.max {
return false;
}
} else if val > self.max {
return false;
}
true
}
}
#[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 num_str = num_str.strip_prefix('+').unwrap_or(num_str);
let val = num_str.parse::<u64>().map_err(|_| {
if is_min {
Error::redis(ERR_MIN_NOT_INT)
} else {
Error::redis(ERR_MAX_NOT_INT)
}
})?;
Ok((val, ex))
}
pub fn parse_range_spec(min_str: &str, max_str: &str) -> Result<SortedintRange> {
let min_str = min_str.trim();
let max_str = max_str.trim();
if min_str == "+inf" || max_str == "-inf" {
return Err(Error::redis(ERR_MIN_GT_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(SortedintRange {
min,
max,
minex,
maxex,
offset: 0,
count: None,
reversed: false,
})
}
#[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 let Some((chunk, _)) = bytes.split_first_chunk::<8>() {
Some(u64::from_be_bytes(*chunk))
} else {
None
}
}
use std::ops::{Bound, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
pub trait IntoSortedintRange {
fn into_sortedint_range(self) -> SortedintRange;
}
impl IntoSortedintRange for SortedintRange {
#[inline]
fn into_sortedint_range(self) -> SortedintRange {
self
}
}
impl IntoSortedintRange for &SortedintRange {
#[inline]
fn into_sortedint_range(self) -> SortedintRange {
*self
}
}
impl IntoSortedintRange for (u64, u64) {
#[inline]
fn into_sortedint_range(self) -> SortedintRange {
SortedintRange {
min: self.0,
max: self.1,
minex: false,
maxex: false,
offset: 0,
count: None,
reversed: false,
}
}
}
impl IntoSortedintRange for Range<u64> {
#[inline]
fn into_sortedint_range(self) -> SortedintRange {
SortedintRange {
min: self.start,
max: self.end,
minex: false,
maxex: true,
offset: 0,
count: None,
reversed: false,
}
}
}
impl IntoSortedintRange for RangeInclusive<u64> {
#[inline]
fn into_sortedint_range(self) -> SortedintRange {
SortedintRange {
min: *self.start(),
max: *self.end(),
minex: false,
maxex: false,
offset: 0,
count: None,
reversed: false,
}
}
}
impl IntoSortedintRange for RangeFrom<u64> {
#[inline]
fn into_sortedint_range(self) -> SortedintRange {
SortedintRange {
min: self.start,
max: u64::MAX,
minex: false,
maxex: false,
offset: 0,
count: None,
reversed: false,
}
}
}
impl IntoSortedintRange for RangeTo<u64> {
#[inline]
fn into_sortedint_range(self) -> SortedintRange {
SortedintRange {
min: u64::MIN,
max: self.end,
minex: false,
maxex: true,
offset: 0,
count: None,
reversed: false,
}
}
}
impl IntoSortedintRange for RangeToInclusive<u64> {
#[inline]
fn into_sortedint_range(self) -> SortedintRange {
SortedintRange {
min: u64::MIN,
max: self.end,
minex: false,
maxex: false,
offset: 0,
count: None,
reversed: false,
}
}
}
impl IntoSortedintRange for RangeFull {
#[inline]
fn into_sortedint_range(self) -> SortedintRange {
SortedintRange {
min: u64::MIN,
max: u64::MAX,
minex: false,
maxex: false,
offset: 0,
count: None,
reversed: false,
}
}
}
impl IntoSortedintRange for (Bound<u64>, Bound<u64>) {
#[inline]
fn into_sortedint_range(self) -> SortedintRange {
let (min, minex) = match self.0 {
Bound::Included(v) => (v, false),
Bound::Excluded(v) => (v, true),
Bound::Unbounded => (u64::MIN, false),
};
let (max, maxex) = match self.1 {
Bound::Included(v) => (v, false),
Bound::Excluded(v) => (v, true),
Bound::Unbounded => (u64::MAX, false),
};
SortedintRange {
min,
max,
minex,
maxex,
offset: 0,
count: None,
reversed: false,
}
}
}