use core::cmp::Ordering;
use crate::ErrorCode;
pub const MAX_SAFE_INTEGER: u64 = 9_007_199_254_740_991;
pub const MAX_SAFE_INTEGER_I64: i64 = 9_007_199_254_740_991;
pub const MIN_SAFE_INTEGER: i64 = -MAX_SAFE_INTEGER_I64;
const MAX_SAFE_INTEGER_BE: [u8; 7] = [0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
pub const CANONICAL_NAN_BITS: u64 = 0x7ff8_0000_0000_0000;
pub const NEGATIVE_ZERO_BITS: u64 = 0x8000_0000_0000_0000;
const EXP_MASK: u64 = 0x7ff0_0000_0000_0000;
const MANT_MASK: u64 = 0x000f_ffff_ffff_ffff;
#[inline]
pub const fn validate_f64_bits(bits: u64) -> Result<(), ErrorCode> {
if bits == NEGATIVE_ZERO_BITS {
return Err(ErrorCode::NegativeZeroForbidden);
}
let is_nan = (bits & EXP_MASK) == EXP_MASK && (bits & MANT_MASK) != 0;
if is_nan && bits != CANONICAL_NAN_BITS {
return Err(ErrorCode::NonCanonicalNaN);
}
Ok(())
}
#[inline]
#[cfg(feature = "alloc")]
pub const fn validate_int_safe_i64(v: i64) -> Result<(), ErrorCode> {
if v < MIN_SAFE_INTEGER || v > MAX_SAFE_INTEGER_I64 {
return Err(ErrorCode::IntegerOutsideSafeRange);
}
Ok(())
}
pub fn validate_bignum_bytes(negative: bool, magnitude: &[u8]) -> Result<(), ErrorCode> {
if magnitude.is_empty() || magnitude[0] == 0 {
return Err(ErrorCode::BignumNotCanonical);
}
let cmp = cmp_big_endian(magnitude, &MAX_SAFE_INTEGER_BE);
let outside = if negative {
cmp != Ordering::Less
} else {
cmp == Ordering::Greater
};
if !outside {
return Err(ErrorCode::BignumMustBeOutsideSafeRange);
}
Ok(())
}
fn cmp_big_endian(a: &[u8], b: &[u8]) -> Ordering {
match a.len().cmp(&b.len()) {
Ordering::Equal => a.cmp(b),
other => other,
}
}
#[inline]
#[must_use]
pub fn cmp_encoded_key_bytes(a: &[u8], b: &[u8]) -> Ordering {
match a.len().cmp(&b.len()) {
Ordering::Equal => a.cmp(b),
other => other,
}
}
#[inline]
#[must_use]
pub fn is_strictly_increasing_encoded(prev: &[u8], curr: &[u8]) -> bool {
cmp_encoded_key_bytes(prev, curr) == Ordering::Less
}
#[inline]
#[must_use]
pub fn cmp_text_keys_by_canonical_encoding(a: &str, b: &str) -> Ordering {
match a.len().cmp(&b.len()) {
Ordering::Equal => a.as_bytes().cmp(b.as_bytes()),
other => other,
}
}
#[inline]
pub fn checked_text_len(n: usize) -> Result<u64, ErrorCode> {
let n_u64 = u64::try_from(n).map_err(|_| ErrorCode::LengthOverflow)?;
let header = if n < 24 {
1
} else if n <= 0xff {
2
} else if n <= 0xffff {
3
} else if n <= 0xffff_ffff {
5
} else {
9
};
n_u64.checked_add(header).ok_or(ErrorCode::LengthOverflow)
}