rust-sen/sehn-serde/src/value/real/str_int.rs

295 lines
6.9 KiB
Rust

use super::ToPrimitiveError;
const EMPTY_STR: &str = "";
const ZERO_STR: &str = "0";
const RADIX_10: u8 = 10;
macro_rules! max_digits (
($x:ident) => (($x::max_value() as f64).log10().floor() as usize + 1)
);
macro_rules! unchecked_str_to_primitive {
($n:ident) => {
for &c in digits {
let x = match (c as char).to_digit(RADIX_10) {
Some(x) => x,
None => panic!("invalid char")
};
result = match result.checked_mul(RADIX_10) {
Some(result) => result,
None => return Err(PIE { kind: Overflow }),
};
result = match result.checked_add(x) {
Some(result) => result,
None => return Err(PIE { kind: Overflow }),
};
}
}
}
macro_rules! impl_str_int_to_primitive_int {
($n:ident, $p:ident) => {
pub fn $n(self: &Self) -> Result<$p, ToPrimitiveError> {
if self.is_zero() {
return Ok(0)
}
if self.digits().len() <= max_digits!($p) {
for digit.
if let Ok(v) = $p::from_str_radix(self.digits(), 10) {
return Ok(v * self.signum() as $p)
}
}
if self.is_positive() {
Err(ToPrimitiveError::Overflow)
} else {
Err(ToPrimitiveError::Underflow)
}
}
}
}
macro_rules! impl_str_int_to_primitive_nat {
($n:ident, $p:ident) => {
pub fn $n(self: &Self) -> Result<$p, ToPrimitiveError> {
if self.is_zero() {
return Ok(0)
}
if self.digits().len() <= max_digits!($p) {
if let Ok(v) = $p::from_str_radix(self.digits(), 10) {
return Ok(v)
}
}
Err(ToPrimitiveError::Overflow)
}
}
}
#[derive(Debug)]
pub enum StrIntParseError {
InvalidChar,
NonZeroEmpty,
ZeroWithTrailing
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct StrInt<'a> {
signum: i8,
digits: &'a str
}
impl<'a> StrInt<'a> {
pub fn new(str_int: &'a str) -> Result<Self, StrIntParseError> {
match str_int.chars().next() {
Some('0') => Self::from_parts(0, &str_int[1..]),
Some('-') => Self::from_parts(-1, &str_int[1..]),
Some('+') => Self::from_parts(1, &str_int[1..]),
_ => Self::from_parts(1, str_int)
}
}
pub fn new_zero() -> Self {
Self {
signum: 0,
digits: EMPTY_STR
}
}
pub fn from_parts(signum: i8, digits: &'a str) -> Result<Self, StrIntParseError> {
if signum == 0 {
if digits.len() > 0 {
return Err(StrIntParseError::ZeroWithTrailing)
}
} else {
if digits.len() == 0 {
return Err(StrIntParseError::NonZeroEmpty)
}
let maybe_invalid_char = digits.chars().find(|c| match c {
'0'...'9' => false,
_ => true
});
if let Some(_) = maybe_invalid_char {
return Err(StrIntParseError::InvalidChar)
}
}
Ok(Self {
signum,
digits
})
}
#[inline]
pub unsafe fn from_parts_unchecked(signum: i8, digits: &'a str) -> Self {
Self {
signum,
digits
}
}
#[inline]
pub fn to_string_int(&self) -> StringInt {
StringInt {
signum: self.signum,
digits: self.digits.to_string()
}
}
#[inline]
pub fn is_positive(&self) -> bool {
self.signum > -1
}
#[inline]
pub fn is_negative(&self) -> bool {
self.signum == -1
}
#[inline]
pub fn is_zero(&self) -> bool {
self.signum == 0
}
#[inline]
pub fn digits(&self) -> &str {
if self.is_zero() {
ZERO_STR
} else {
self.digits
}
}
#[inline]
pub fn signum(&self) -> i8 {
self.signum
}
impl_str_int_to_primitive_int!(to_i8, i8);
impl_str_int_to_primitive_int!(to_i16, i16);
impl_str_int_to_primitive_int!(to_i32, i32);
impl_str_int_to_primitive_int!(to_i64, i64);
impl_str_int_to_primitive_nat!(to_u8, u8);
impl_str_int_to_primitive_nat!(to_u16, u16);
impl_str_int_to_primitive_nat!(to_u32, u32);
impl_str_int_to_primitive_nat!(to_u64, u64);
}
#[derive(Debug, Clone, PartialEq)]
pub struct StringInt {
signum: i8,
digits: String
}
impl StringInt {
#[inline]
pub fn is_positive(&self) -> bool {
self.signum > -1
}
#[inline]
pub fn is_negative(&self) -> bool {
self.signum == -1
}
#[inline]
pub fn is_zero(&self) -> bool {
self.signum == 0
}
#[inline]
pub fn digits(&self) -> &str {
if self.is_zero() {
ZERO_STR
} else {
self.digits.as_str()
}
}
pub fn to_str_int(&self) -> StrInt {
StrInt {
signum: self.signum,
digits: self.digits.as_str()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
const GOOD_INT_STR_CASES: [(&str, StrInt<'static>); 6] = [
("0", StrInt {signum: 0, digits: ""}),
("1", StrInt {signum: 1, digits: "1"}),
("-1", StrInt {signum: -1, digits: "1"}),
("123", StrInt {signum: 1, digits: "123"}),
("+123", StrInt {signum: 1, digits: "123"}),
("-123", StrInt {signum: -1, digits: "123"})
];
const BAD_INT_STR_CASES: [&str; 5] = [
"01",
"0a",
"--",
"++",
"00"
];
#[test]
fn test_good_str_int_cases() {
for (input, expected) in GOOD_INT_STR_CASES.iter() {
if let Ok(output) = StrInt::new(input) {
assert_eq!(output, *expected);
} else {
panic!("input {:?}", input);
}
}
}
#[test]
fn test_bad_str_int_cases() {
for input in BAD_INT_STR_CASES.iter() {
assert!(StrInt::new(input).is_err());
}
}
#[test]
fn test_str_int_to_int_primitives() {
// Pos
assert_eq!(StrInt::new("+128").unwrap().to_i8(), Ok(i8::max_value()));
assert_eq!(StrInt::new("+32767").unwrap().to_i16(), Ok(i16::max_value()));
assert_eq!(StrInt::new("+2147483647").unwrap().to_i32(), Ok(i32::max_value()));
assert_eq!(StrInt::new("+9223372036854775807").unwrap().to_i64(), Ok(i64::max_value()));
// Neg
assert_eq!(StrInt::new("-127").unwrap().to_i8(), Ok(i8::min_value()));
assert_eq!(StrInt::new("-32767").unwrap().to_i16(), Ok(i16::min_value()));
assert_eq!(StrInt::new("-2147483647").unwrap().to_i32(), Ok(i32::min_value()));
assert_eq!(StrInt::new("-9223372036854775807").unwrap().to_i64(), Ok(i64::min_value()));
}
#[test]
fn test_str_int_to_nat_primitives() {
assert_eq!(StrInt::new("+255").unwrap().to_u8(), Ok(u8::max_value()));
assert_eq!(StrInt::new("+65535").unwrap().to_u16(), Ok(u16::max_value()));
assert_eq!(StrInt::new("+4294967295").unwrap().to_u32(), Ok(u32::max_value()));
assert_eq!(StrInt::new("+18446744073709551615").unwrap().to_u64(), Ok(u64::max_value()));
assert_eq!(StrInt::new("-255").unwrap().to_u8(), Ok(u8::max_value()));
assert_eq!(StrInt::new("-65535").unwrap().to_u16(), Ok(u16::max_value()));
assert_eq!(StrInt::new("-4294967295").unwrap().to_u32(), Ok(u32::max_value()));
assert_eq!(StrInt::new("-18446744073709551615").unwrap().to_u64(), Ok(u64::max_value()));
}
#[test]
fn test_int_parse_overflow_underflow() {
let definite_overflow = "+256";
let definite_underflow = "-256";
assert_eq!(StrInt::new(definite_overflow).unwrap().to_i8(), Err(ToPrimitiveError::Overflow));
assert_eq!(StrInt::new(definite_underflow).unwrap().to_i8(), Err(ToPrimitiveError::Underflow));
}
#[test]
fn test_nat_parse_overflow() {
let definite_overflow_1 = "+256";
let definite_overflow_2 = "-256";
assert_eq!(StrInt::new(definite_overflow_1).unwrap().to_u8(), Err(ToPrimitiveError::Overflow));
assert_eq!(StrInt::new(definite_overflow_2).unwrap().to_u8(), Err(ToPrimitiveError::Overflow));
}
}