icydb-schema 0.219.0

Bounded public schema proposal contract for IcyDB
Documentation
use crate::decimal::{DECIMAL_DIGIT_BUFFER_LEN, Decimal};
use std::{
    cmp::Ordering,
    hash::{Hash, Hasher},
};

impl Decimal {
    pub(in crate::decimal) fn cmp_decimal(&self, other: &Self) -> Ordering {
        let (lhs_m, lhs_s) = self.normalized_parts();
        let (rhs_m, rhs_s) = other.normalized_parts();

        if lhs_m == rhs_m && lhs_s == rhs_s {
            return Ordering::Equal;
        }

        if lhs_m == 0 {
            return if rhs_m.is_negative() {
                Ordering::Greater
            } else {
                Ordering::Less
            };
        }

        if rhs_m == 0 {
            return if lhs_m.is_negative() {
                Ordering::Less
            } else {
                Ordering::Greater
            };
        }

        if lhs_m.is_negative() != rhs_m.is_negative() {
            return if lhs_m.is_negative() {
                Ordering::Less
            } else {
                Ordering::Greater
            };
        }

        let positive = !lhs_m.is_negative();
        let mut lhs_digits = [0u8; DECIMAL_DIGIT_BUFFER_LEN];
        let mut rhs_digits = [0u8; DECIMAL_DIGIT_BUFFER_LEN];
        let lhs_len = write_u128_decimal_digits(lhs_m.unsigned_abs(), &mut lhs_digits);
        let rhs_len = write_u128_decimal_digits(rhs_m.unsigned_abs(), &mut rhs_digits);

        let lhs_exponent = compare_exponent(lhs_s, lhs_len).unwrap_or(i64::MIN);
        let rhs_exponent = compare_exponent(rhs_s, rhs_len).unwrap_or(i64::MIN);

        let exponent_cmp = lhs_exponent.cmp(&rhs_exponent);
        if exponent_cmp != Ordering::Equal {
            return if positive {
                exponent_cmp
            } else {
                exponent_cmp.reverse()
            };
        }

        let significand_cmp =
            cmp_significand_digits(&lhs_digits[..lhs_len], &rhs_digits[..rhs_len]);
        if positive {
            significand_cmp
        } else {
            significand_cmp.reverse()
        }
    }
}

impl PartialEq for Decimal {
    fn eq(&self, other: &Self) -> bool {
        let (lhs_m, lhs_s) = self.normalized_parts();
        let (rhs_m, rhs_s) = other.normalized_parts();
        lhs_m == rhs_m && lhs_s == rhs_s
    }
}

impl Eq for Decimal {}

impl PartialOrd for Decimal {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(Ord::cmp(self, other))
    }
}

impl Ord for Decimal {
    fn cmp(&self, other: &Self) -> Ordering {
        self.cmp_decimal(other)
    }
}

impl Hash for Decimal {
    fn hash<H: Hasher>(&self, state: &mut H) {
        let (mantissa, scale) = self.normalized_parts();
        mantissa.hash(state);
        scale.hash(state);
    }
}

fn write_u128_decimal_digits(mut value: u128, out: &mut [u8; DECIMAL_DIGIT_BUFFER_LEN]) -> usize {
    let mut write_idx = DECIMAL_DIGIT_BUFFER_LEN;

    loop {
        write_idx = write_idx.saturating_sub(1);
        out[write_idx] = match value % 10 {
            0 => b'0',
            1 => b'1',
            2 => b'2',
            3 => b'3',
            4 => b'4',
            5 => b'5',
            6 => b'6',
            7 => b'7',
            8 => b'8',
            9 => b'9',
            _ => unreachable!("decimal digit remainder must be in 0..=9"),
        };
        value /= 10;

        if value == 0 {
            break;
        }
    }

    let len = DECIMAL_DIGIT_BUFFER_LEN.saturating_sub(write_idx);
    out.copy_within(write_idx..DECIMAL_DIGIT_BUFFER_LEN, 0);
    len
}

fn compare_exponent(scale: u32, digit_len: usize) -> Option<i64> {
    let digit_count = i64::try_from(digit_len).ok()?;
    let scale = i64::from(scale);
    digit_count.checked_sub(1)?.checked_sub(scale)
}

fn cmp_significand_digits(lhs: &[u8], rhs: &[u8]) -> Ordering {
    let width = lhs.len().max(rhs.len());
    for idx in 0..width {
        let l = lhs.get(idx).copied().unwrap_or(b'0');
        let r = rhs.get(idx).copied().unwrap_or(b'0');
        let cmp = l.cmp(&r);
        if cmp != Ordering::Equal {
            return cmp;
        }
    }

    Ordering::Equal
}