use lib::{mem, ptr};
use table::*;
use util::*;
#[inline(always)]
pub(crate) unsafe fn parse_digit<T: Integer>(digit: &mut T, p: *const u8)
-> T
{
let x = char_to_digit(*p);
*digit = as_cast(x);
*digit
}
#[inline]
unsafe fn unchecked_unsafe<T>(value: &mut T, base: T, first: *const u8, last: *const u8)
-> (*const u8, usize)
where T: Integer
{
let mut p = first;
let mut truncated_p = last;
let mut digit: T = mem::uninitialized();
while p < last && parse_digit(&mut digit, p) < base {
let (v, o1) = value.overflowing_mul(base);
let (v, o2) = v.overflowing_add(digit);
*value = v;
if (truncated_p == last) && (o1 | o2) {
truncated_p = p;
}
p = p.add(1);
}
(p, distance(truncated_p.min(p), p))
}
#[inline]
pub(crate) fn unchecked<T>(value: &mut T, base: u32, first: *const u8, last: *const u8)
-> (*const u8, usize)
where T: Integer
{
unsafe {
unchecked_unsafe::<T>(value, as_cast(base), first, last)
}
}
#[inline]
unsafe fn checked_unsafe<T>(value: &mut T, base: T, first: *const u8, last: *const u8)
-> (*const u8, usize)
where T: Integer
{
let mut p = first;
let mut truncated_p = last;
let mut digit: T = mem::uninitialized();
while p < last && parse_digit(&mut digit, p) < base {
if truncated_p == last {
match value.checked_mul(base).and_then(|v| v.checked_add(digit)) {
Some(v) => *value = v,
None => truncated_p = p,
}
}
p = p.add(1);
}
(p, distance(truncated_p.min(p), p))
}
#[inline]
#[allow(dead_code)]
pub(crate) fn checked<T>(value: &mut T, base: u32, first: *const u8, last: *const u8)
-> (*const u8, usize)
where T: Integer
{
unsafe {
checked_unsafe::<T>(value, as_cast(base), first, last)
}
}
#[inline]
pub(crate) unsafe fn value<T, Cb>(base: u32, first: *const u8, last: *const u8, cb: Cb)
-> (T, *const u8, bool)
where T: Integer,
Cb: FnOnce(&mut T, u32, *const u8, *const u8) -> (*const u8, usize)
{
debug_assert!(base >= 2 && base <= 36, "Numerical base must be from 2-36");
let p = ltrim_char_range(first, last, b'0');
let mut v: T = T::ZERO;
let (p, overflow) = cb(&mut v, base, p, last);
(v, p, overflow != 0)
}
#[inline]
pub(crate) unsafe fn filter_sign<T, Cb>(base: u32, first: *const u8, last: *const u8, cb: Cb)
-> (T, *const u8, bool, i32)
where T: Integer,
Cb: FnOnce(&mut T, u32, *const u8, *const u8) -> (*const u8, usize)
{
match *first {
b'+' => {
let (v, p, o) = value::<T, Cb>(base, first.add(1), last, cb);
(v, p, o, 1)
},
b'-' => {
let (v, p, o) = value::<T, Cb>(base, first.add(1), last, cb);
(v, p, o, -1)
},
_ => {
let (v, p, o) = value::<T, Cb>(base, first, last, cb);
(v, p, o, 1)
},
}
}
#[inline]
pub(crate) unsafe fn unsigned<T, Cb>(base: u32, first: *const u8, last: *const u8, cb: Cb)
-> (T, *const u8, bool)
where T: UnsignedInteger,
Cb: FnOnce(&mut T, u32, *const u8, *const u8) -> (*const u8, usize)
{
if first == last {
(T::ZERO, ptr::null(), false)
} else {
let (v, p, o, s) = filter_sign::<T, Cb>(base, first, last, cb);
match s {
-1 => (v.wrapping_neg(), p, true),
1 => (v, p, o),
_ => unreachable!(),
}
}
}
#[inline]
pub(crate) unsafe fn signed<T, Cb>(base: u32, first: *const u8, last: *const u8, cb: Cb)
-> (T, *const u8, bool)
where T: SignedInteger,
Cb: FnOnce(&mut T, u32, *const u8, *const u8) -> (*const u8, usize)
{
if first == last {
(T::ZERO, ptr::null(), false)
} else {
let (v, p, o, s) = filter_sign::<T, Cb>(base, first, last, cb);
match s {
-1 => (-v, p, true),
1 => (v, p, o),
_ => unreachable!(),
}
}
}
macro_rules! generate_unsafe_unsigned {
($func:ident, $t:tt) => (
#[inline]
pub unsafe extern "C" fn $func(base: u8, first: *const u8, last: *const u8) -> ($t, *const u8, bool)
{
unsigned::<$t, _>(base.into(), first, last, unchecked::<$t>)
}
)
}
generate_unsafe_unsigned!(atou8_unsafe, u8);
generate_unsafe_unsigned!(atou16_unsafe, u16);
generate_unsafe_unsigned!(atou32_unsafe, u32);
generate_unsafe_unsigned!(atou64_unsafe, u64);
generate_unsafe_unsigned!(atousize_unsafe, usize);
macro_rules! generate_unsafe_signed {
($func:ident, $t:tt) => (
#[inline]
pub unsafe extern "C" fn $func(base: u8, first: *const u8, last: *const u8) -> ($t, *const u8, bool)
{
signed::<$t, _>(base.into(), first, last, unchecked::<$t>)
}
)
}
generate_unsafe_signed!(atoi8_unsafe, i8);
generate_unsafe_signed!(atoi16_unsafe, i16);
generate_unsafe_signed!(atoi32_unsafe, i32);
generate_unsafe_signed!(atoi64_unsafe, i64);
generate_unsafe_signed!(atoisize_unsafe, isize);
generate_from_bytes_local!(atou8_local, u8, atou8_unsafe);
generate_from_bytes_local!(atou16_local, u16, atou16_unsafe);
generate_from_bytes_local!(atou32_local, u32, atou32_unsafe);
generate_from_bytes_local!(atou64_local, u64, atou64_unsafe);
generate_from_bytes_local!(atousize_local, usize, atousize_unsafe);
generate_from_bytes_local!(atoi8_local, i8, atoi8_unsafe);
generate_from_bytes_local!(atoi16_local, i16, atoi16_unsafe);
generate_from_bytes_local!(atoi32_local, i32, atoi32_unsafe);
generate_from_bytes_local!(atoi64_local, i64, atoi64_unsafe);
generate_from_bytes_local!(atoisize_local, isize, atoisize_unsafe);
generate_from_bytes_api!(atou8_bytes, u8, atou8_local);
generate_from_bytes_api!(atou16_bytes, u16, atou16_local);
generate_from_bytes_api!(atou32_bytes, u32, atou32_local);
generate_from_bytes_api!(atou64_bytes, u64, atou64_local);
generate_from_bytes_api!(atousize_bytes, usize, atousize_local);
generate_from_bytes_api!(atoi8_bytes, i8, atoi8_local);
generate_from_bytes_api!(atoi16_bytes, i16, atoi16_local);
generate_from_bytes_api!(atoi32_bytes, i32, atoi32_local);
generate_from_bytes_api!(atoi64_bytes, i64, atoi64_local);
generate_from_bytes_api!(atoisize_bytes, isize, atoisize_local);
generate_try_from_bytes_api!(try_atou8_bytes, u8, atou8_local);
generate_try_from_bytes_api!(try_atou16_bytes, u16, atou16_local);
generate_try_from_bytes_api!(try_atou32_bytes, u32, atou32_local);
generate_try_from_bytes_api!(try_atou64_bytes, u64, atou64_local);
generate_try_from_bytes_api!(try_atousize_bytes, usize, atousize_local);
generate_try_from_bytes_api!(try_atoi8_bytes, i8, atoi8_local);
generate_try_from_bytes_api!(try_atoi16_bytes, i16, atoi16_local);
generate_try_from_bytes_api!(try_atoi32_bytes, i32, atoi32_local);
generate_try_from_bytes_api!(try_atoi64_bytes, i64, atoi64_local);
generate_try_from_bytes_api!(try_atoisize_bytes, isize, atoisize_local);
#[cfg(test)]
mod tests {
use error::{invalid_digit, overflow};
use super::*;
const DATA: [(u8, &'static str); 35] = [
(2, "100101"),
(3, "1101"),
(4, "211"),
(5, "122"),
(6, "101"),
(7, "52"),
(8, "45"),
(9, "41"),
(10, "37"),
(11, "34"),
(12, "31"),
(13, "2B"),
(14, "29"),
(15, "27"),
(16, "25"),
(17, "23"),
(18, "21"),
(19, "1I"),
(20, "1H"),
(21, "1G"),
(22, "1F"),
(23, "1E"),
(24, "1D"),
(25, "1C"),
(26, "1B"),
(27, "1A"),
(28, "19"),
(29, "18"),
(30, "17"),
(31, "16"),
(32, "15"),
(33, "14"),
(34, "13"),
(35, "12"),
(36, "11"),
];
#[test]
fn checked_test() {
let s = "1234567891234567890123";
unsafe {
let first = s.as_bytes().as_ptr();
let last = first.add(s.len());
let mut value: u64 = 0;
let (f, truncated) = checked(&mut value, 10, first, last);
assert_eq!(value, 12345678912345678901);
assert_eq!(f, last);
assert_eq!(truncated, 2);
}
}
#[test]
fn unchecked_test() {
let s = "1234567891234567890123";
unsafe {
let first = s.as_bytes().as_ptr();
let last = first.add(s.len());
let mut value: u64 = 0;
let (f, truncated) = unchecked(&mut value, 10, first, last);
assert_eq!(value, 17082782369737483467);
assert_eq!(f, last);
assert_eq!(truncated, 2);
}
}
#[test]
fn atou8_base10_test() {
assert_eq!(0, atou8_bytes(10, b"0"));
assert_eq!(127, atou8_bytes(10, b"127"));
assert_eq!(128, atou8_bytes(10, b"128"));
assert_eq!(255, atou8_bytes(10, b"255"));
assert_eq!(255, atou8_bytes(10, b"-1"));
assert_eq!(1, atou8_bytes(10, b"1a"));
}
#[test]
fn atou8_basen_test() {
for (b, s) in DATA.iter() {
assert_eq!(atou8_bytes(*b, s.as_bytes()), 37);
}
}
#[test]
fn atoi8_base10_test() {
assert_eq!(0, atoi8_bytes(10, b"0"));
assert_eq!(127, atoi8_bytes(10, b"127"));
assert_eq!(-128, atoi8_bytes(10, b"128"));
assert_eq!(-1, atoi8_bytes(10, b"255"));
assert_eq!(-1, atoi8_bytes(10, b"-1"));
assert_eq!(1, atoi8_bytes(10, b"1a"));
}
#[test]
fn atou16_base10_test() {
assert_eq!(0, atou16_bytes(10, b"0"));
assert_eq!(32767, atou16_bytes(10, b"32767"));
assert_eq!(32768, atou16_bytes(10, b"32768"));
assert_eq!(65535, atou16_bytes(10, b"65535"));
assert_eq!(65535, atou16_bytes(10, b"-1"));
assert_eq!(1, atou16_bytes(10, b"1a"));
}
#[test]
fn atoi16_base10_test() {
assert_eq!(0, atoi16_bytes(10, b"0"));
assert_eq!(32767, atoi16_bytes(10, b"32767"));
assert_eq!(-32768, atoi16_bytes(10, b"32768"));
assert_eq!(-1, atoi16_bytes(10, b"65535"));
assert_eq!(-1, atoi16_bytes(10, b"-1"));
assert_eq!(1, atoi16_bytes(10, b"1a"));
}
#[test]
fn atoi16_basen_test() {
assert_eq!(atoi16_bytes(36, b"YA"), 1234);
}
#[test]
fn atou32_base10_test() {
assert_eq!(0, atou32_bytes(10, b"0"));
assert_eq!(2147483647, atou32_bytes(10, b"2147483647"));
assert_eq!(2147483648, atou32_bytes(10, b"2147483648"));
assert_eq!(4294967295, atou32_bytes(10, b"4294967295"));
assert_eq!(4294967295, atou32_bytes(10, b"-1"));
assert_eq!(1, atou32_bytes(10, b"1a"));
}
#[test]
fn atoi32_base10_test() {
assert_eq!(0, atoi32_bytes(10, b"0"));
assert_eq!(2147483647, atoi32_bytes(10, b"2147483647"));
assert_eq!(-2147483648, atoi32_bytes(10, b"2147483648"));
assert_eq!(-1, atoi32_bytes(10, b"4294967295"));
assert_eq!(-1, atoi32_bytes(10, b"-1"));
assert_eq!(1, atoi32_bytes(10, b"1a"));
}
#[test]
fn atou64_base10_test() {
assert_eq!(0, atou64_bytes(10, b"0"));
assert_eq!(9223372036854775807, atou64_bytes(10, b"9223372036854775807"));
assert_eq!(9223372036854775808, atou64_bytes(10, b"9223372036854775808"));
assert_eq!(18446744073709551615, atou64_bytes(10, b"18446744073709551615"));
assert_eq!(18446744073709551615, atou64_bytes(10, b"-1"));
assert_eq!(1, atou64_bytes(10, b"1a"));
}
#[test]
fn atoi64_base10_test() {
assert_eq!(0, atoi64_bytes(10, b"0"));
assert_eq!(9223372036854775807, atoi64_bytes(10, b"9223372036854775807"));
assert_eq!(-9223372036854775808, atoi64_bytes(10, b"9223372036854775808"));
assert_eq!(-1, atoi64_bytes(10, b"18446744073709551615"));
assert_eq!(-1, atoi64_bytes(10, b"-1"));
assert_eq!(1, atoi64_bytes(10, b"1a"));
}
#[test]
fn try_atou8_base10_test() {
assert_eq!(Err(invalid_digit(0)), try_atou8_bytes(10, b""));
assert_eq!(Ok(0), try_atou8_bytes(10, b"0"));
assert_eq!(Err(invalid_digit(1)), try_atou8_bytes(10, b"1a"));
assert_eq!(Err(overflow()), try_atou8_bytes(10, b"256"));
}
#[test]
fn try_atoi8_base10_test() {
assert_eq!(Err(invalid_digit(0)), try_atoi8_bytes(10, b""));
assert_eq!(Ok(0), try_atoi8_bytes(10, b"0"));
assert_eq!(Err(invalid_digit(1)), try_atoi8_bytes(10, b"1a"));
assert_eq!(Err(overflow()), try_atoi8_bytes(10, b"128"));
}
#[test]
fn try_atou16_base10_test() {
assert_eq!(Err(invalid_digit(0)), try_atou16_bytes(10, b""));
assert_eq!(Ok(0), try_atou16_bytes(10, b"0"));
assert_eq!(Err(invalid_digit(1)), try_atou16_bytes(10, b"1a"));
assert_eq!(Err(overflow()), try_atou16_bytes(10, b"65536"));
}
#[test]
fn try_atoi16_base10_test() {
assert_eq!(Err(invalid_digit(0)), try_atoi16_bytes(10, b""));
assert_eq!(Ok(0), try_atoi16_bytes(10, b"0"));
assert_eq!(Err(invalid_digit(1)), try_atoi16_bytes(10, b"1a"));
assert_eq!(Err(overflow()), try_atoi16_bytes(10, b"32768"));
}
#[test]
fn try_atou32_base10_test() {
assert_eq!(Err(invalid_digit(0)), try_atou32_bytes(10, b""));
assert_eq!(Ok(0), try_atou32_bytes(10, b"0"));
assert_eq!(Err(invalid_digit(1)), try_atou32_bytes(10, b"1a"));
assert_eq!(Err(overflow()), try_atou32_bytes(10, b"4294967296"));
}
#[test]
fn try_atoi32_base10_test() {
assert_eq!(Err(invalid_digit(0)), try_atoi32_bytes(10, b""));
assert_eq!(Ok(0), try_atoi32_bytes(10, b"0"));
assert_eq!(Err(invalid_digit(1)), try_atoi32_bytes(10, b"1a"));
assert_eq!(Err(overflow()), try_atoi32_bytes(10, b"2147483648"));
}
#[test]
fn try_atou64_base10_test() {
assert_eq!(Err(invalid_digit(0)), try_atou64_bytes(10, b""));
assert_eq!(Ok(0), try_atou64_bytes(10, b"0"));
assert_eq!(Err(invalid_digit(1)), try_atou64_bytes(10, b"1a"));
assert_eq!(Err(overflow()), try_atou64_bytes(10, b"18446744073709551616"));
}
#[test]
fn try_atoi64_base10_test() {
assert_eq!(Err(invalid_digit(0)), try_atoi64_bytes(10, b""));
assert_eq!(Ok(0), try_atoi64_bytes(10, b"0"));
assert_eq!(Err(invalid_digit(1)), try_atoi64_bytes(10, b"1a"));
assert_eq!(Err(overflow()), try_atoi64_bytes(10, b"9223372036854775808"));
}
}