#[derive(Debug, PartialEq)]
pub struct Base(i32);
impl Base {
pub fn new(base: i32) -> Base {
if !(2..=36).contains(&base) {
panic!("base not in 2..36 range ({})", base);
}
Base(base)
}
pub fn unsafe_new(base: i32) -> Base {
Base(base)
}
}
#[derive(Debug)]
pub enum Case {
Lower,
Upper,
}
impl Case {
pub fn alphabet(&self) -> Vec<char> {
match self {
Case::Lower => "0123456789abcdefghijklmnopqrstuvwxyz",
Case::Upper => "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",
}
.chars()
.collect::<Vec<char>>()
}
}
pub fn itoa_signed(number: isize, base: Base, case: Case) -> String {
let mut number = number;
let base = base.0;
let mut result = String::new();
let sign = if number < 0isize {
number = -number;
true
} else {
false
};
let alphabet = case.alphabet();
loop {
result.push(alphabet[(number % base as isize) as usize]);
number /= base as isize;
if number == 0 {
break;
}
}
if sign {
result.push('-');
}
result
.chars()
.rev()
.collect()
}
pub fn itoa_unsigned(number: usize, base: Base, case: Case) -> String {
let mut number = number;
let base = base.0;
let mut result = String::new();
let alphabet = case.alphabet();
loop {
result.push(alphabet[number % base as usize]);
number /= base as usize;
if number == 0 {
break;
}
}
result
.chars()
.rev()
.collect()
}
#[derive(Debug, Clone, PartialEq)]
pub enum ParseIntKind {
Empty,
InvalidCharacter,
Overflow,
}
pub fn atoi_signed(
o: &String,
base: Base,
ignore_overflow: bool,
) -> Result<isize, ParseIntKind> {
let base = base.0;
let mut iterator = o.chars();
let mut success = false;
let mut sign = false;
let mut alphabet = Case::Upper.alphabet();
alphabet.truncate(base as usize);
let mut looped = false;
let mut result = 0isize;
loop {
let char = iterator.next();
if char.is_none() {
break;
}
let char = char
.unwrap()
.to_ascii_uppercase();
if char == '-' {
sign = true;
continue;
}
if char == '\t' ||
char == '\n' ||
char == '\x0b' ||
char == '\x0c' ||
char == '\n' ||
char == ' ' ||
char == '_'
{
continue;
}
looped = true;
let position = alphabet
.iter()
.position(|&e| e == char);
if position.is_none() {
break;
}
success = true;
let mut tmp = result.overflowing_mul(base as isize);
if !ignore_overflow && tmp.1 {
return Err(ParseIntKind::Overflow);
}
result = tmp.0;
tmp = result.overflowing_add(position.unwrap() as isize);
if !ignore_overflow && tmp.1 {
return Err(ParseIntKind::Overflow);
}
result = tmp.0;
}
if success {
Ok(if sign {
-result
} else {
result
})
} else {
Err(if looped {
ParseIntKind::InvalidCharacter
} else {
ParseIntKind::Empty
})
}
}
pub fn atoi_unsigned(
o: &String,
base: Base,
ignore_overflow: bool,
) -> Result<usize, ParseIntKind> {
let base = base.0;
let mut iterator = o.chars();
let mut success = false;
let mut alphabet = Case::Upper.alphabet();
alphabet.truncate(base as usize);
let mut looped = false;
let mut result = 0usize;
loop {
let char = iterator.next();
if char.is_none() {
break;
}
let char = char
.unwrap()
.to_ascii_uppercase();
if char == '\t' ||
char == '\n' ||
char == '\x0b' ||
char == '\x0c' ||
char == '\n' ||
char == ' ' ||
char == '_'
{
continue;
}
looped = true;
let position = alphabet
.iter()
.position(|&e| e == char);
if position.is_none() {
break;
}
success = true;
let mut tmp = result.overflowing_mul(base as usize);
if !ignore_overflow && tmp.1 {
return Err(ParseIntKind::Overflow);
}
result = tmp.0;
tmp = result.overflowing_add(position.unwrap() as usize);
if !ignore_overflow && tmp.1 {
return Err(ParseIntKind::Overflow);
}
result = tmp.0;
}
if success {
Ok(result)
} else {
Err(if looped {
ParseIntKind::InvalidCharacter
} else {
ParseIntKind::Empty
})
}
}
#[macro_export]
macro_rules! atoi {
($e:expr) => {
{
let tmp = atoi_signed($e, Base::new(10), true).unwrap_or(0);
if (tmp & 0xffffffffisize) != tmp {
0
} else {
tmp
}
}
};
}
pub use atoi;