use alloc::vec::Vec;
use crate::tchar::Tchar;
const PARSE_BUFFER: usize = 128;
pub(crate) fn parse_window(units: &[Tchar]) -> &[Tchar] {
&units[..units.len().min(PARSE_BUFFER - 1)]
}
#[inline]
fn at(s: &[Tchar], i: usize) -> u32 {
s.get(i).map_or(0, |&c| u32::from(c))
}
fn scan(s: &[Tchar]) -> (isize, usize) {
const ZERO: u32 = b'0' as u32;
let mut v: isize = 0;
if at(s, 0) == ZERO && (at(s, 1) == b'x' as u32 || at(s, 1) == b'X' as u32) {
let mut i = 1;
loop {
i += 1;
let c = at(s, i);
let digit = match c {
0x30..=0x39 => c - 0x30,
0x61..=0x66 => c - 0x61 + 10,
0x41..=0x46 => c - 0x41 + 10,
_ => break,
};
v = (v << 4).wrapping_add(digit as isize);
}
return (v, i);
}
if at(s, 0) == ZERO && (ZERO..=b'7' as u32).contains(&at(s, 1)) {
let mut i = 0;
loop {
i += 1;
let c = at(s, i);
if !(ZERO..=b'7' as u32).contains(&c) {
break;
}
v = (v << 3).wrapping_add((c - ZERO) as isize);
}
return (v, i);
}
let negative = at(s, 0) == b'-' as u32;
let mut i = usize::from(negative);
loop {
let c = at(s, i);
if !(ZERO..=b'9' as u32).contains(&c) {
break;
}
v = v.wrapping_mul(10).wrapping_add((c - ZERO) as isize);
i += 1;
}
if negative {
v = v.wrapping_neg();
}
(v, i)
}
pub fn str_to_ptr(s: &[Tchar]) -> isize {
scan(s).0
}
pub fn atoi_or(s: &[Tchar]) -> isize {
let mut acc: isize = 0;
let mut rest = s;
loop {
let (v, end) = scan(rest);
acc |= v;
if at(rest, end) != b'|' as u32 {
return acc;
}
rest = &rest[end + 1..];
}
}
pub fn format(v: isize) -> Vec<Tchar> {
let negative = v < 0;
let mut magnitude = if negative {
(v as usize).wrapping_neg()
} else {
v as usize
};
let mut digits = [0u8; 24];
let mut n = 0;
loop {
digits[n] = b'0' + (magnitude % 10) as u8;
magnitude /= 10;
n += 1;
if magnitude == 0 {
break;
}
}
let mut out = Vec::with_capacity(n + 1);
if negative {
out.push(b'-' as Tchar);
}
for i in (0..n).rev() {
out.push(digits[i] as Tchar);
}
out
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tchar::encode;
fn parse(s: &str) -> isize {
str_to_ptr(&encode(s))
}
fn parse_or(s: &str) -> isize {
atoi_or(&encode(s))
}
#[test]
fn decimal() {
assert_eq!(parse("0"), 0);
assert_eq!(parse("42"), 42);
assert_eq!(parse("-42"), -42);
assert_eq!(parse("9abc"), 9);
}
#[test]
fn hexadecimal() {
assert_eq!(parse("0x1f"), 31);
assert_eq!(parse("0X1F"), 31);
assert_eq!(parse("0xdeadg"), 0xdead);
assert_eq!(parse("0x"), 0);
}
#[test]
fn octal() {
assert_eq!(parse("0755"), 0o755);
assert_eq!(parse("017"), 15);
assert_eq!(parse("08"), 8);
}
#[test]
fn garbage_is_zero_not_an_error() {
assert_eq!(parse(""), 0);
assert_eq!(parse("hello"), 0);
assert_eq!(parse("-"), 0);
assert_eq!(parse(" 12"), 0);
}
#[test]
fn ored_forms() {
assert_eq!(parse_or("2|4|8"), 14);
assert_eq!(parse_or("0x1|0x2"), 3);
assert_eq!(parse_or("5"), 5);
assert_eq!(parse_or("1|"), 1);
}
#[test]
fn or_is_not_applied_by_the_plain_parser() {
assert_eq!(parse("2|4|8"), 2);
}
#[test]
fn round_trip() {
for v in [0isize, 1, -1, 12345, -12345, isize::MAX, isize::MIN] {
assert_eq!(str_to_ptr(&format(v)), v, "round-tripping {v}");
}
}
}