Skip to content

Commit

Permalink
Merge pull request #27 from Alexhuszagh/read_u64
Browse files Browse the repository at this point in the history
Added read_u64 optimizations to big endian.
  • Loading branch information
aldanor authored May 24, 2021
2 parents 0c714bb + bc9ed2e commit ec1b7d4
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 42 deletions.
41 changes: 27 additions & 14 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,8 @@ impl<'a> AsciiStr<'a> {
#[inline]
pub fn read_u64(&self) -> u64 {
debug_assert!(self.check_len(8));
let mut value = 0_u64;
let src = self.ptr;
let dst = &mut value as *mut _ as *mut u8;
unsafe { ptr::copy_nonoverlapping(src, dst, 8) };
value
let src = self.ptr as *const u64;
u64::from_le(unsafe { ptr::read_unaligned(src) })
}

#[inline]
Expand Down Expand Up @@ -159,26 +156,22 @@ pub trait ByteSlice: AsRef<[u8]> + AsMut<[u8]> {
#[inline]
fn read_u64(&self) -> u64 {
debug_assert!(self.as_ref().len() >= 8);
let mut value = 0_u64;
let src = self.as_ref().as_ptr();
let dst = &mut value as *mut _ as *mut u8;
unsafe { ptr::copy_nonoverlapping(src, dst, 8) };
value
let src = self.as_ref().as_ptr() as *const u64;
u64::from_le(unsafe { ptr::read_unaligned(src) })
}

#[inline]
fn write_u64(&mut self, value: u64) {
debug_assert!(self.as_ref().len() >= 8);
let src = &value as *const _ as *const u8;
let dst = self.as_mut().as_mut_ptr();
unsafe { ptr::copy_nonoverlapping(src, dst, 8) };
let dst = self.as_mut().as_mut_ptr() as *mut u64;
unsafe { ptr::write_unaligned(dst, u64::to_le(value)) };
}
}

impl ByteSlice for [u8] {}

#[inline]
pub fn is_8digits_le(v: u64) -> bool {
pub fn is_8digits(v: u64) -> bool {
let a = v.wrapping_add(0x4646_4646_4646_4646);
let b = v.wrapping_sub(0x3030_3030_3030_3030);
(a | b) & 0x8080_8080_8080_8080 == 0
Expand Down Expand Up @@ -212,3 +205,23 @@ impl AdjustedMantissa {
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_read_write_u64() {
let bytes = b"01234567";
let string = AsciiStr::new(bytes);
let int = string.read_u64();
assert_eq!(int, 0x3736353433323130);

let int = bytes.read_u64();
assert_eq!(int, 0x3736353433323130);

let mut slc = [0u8; 8];
slc.write_u64(0x3736353433323130);
assert_eq!(&slc, bytes);
}
}
18 changes: 8 additions & 10 deletions src/decimal.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use core::fmt::{self, Debug};

use crate::common::{is_8digits_le, parse_digits, ByteSlice};
use crate::common::{is_8digits, parse_digits, ByteSlice};

#[derive(Clone)]
pub struct Decimal {
Expand Down Expand Up @@ -204,16 +204,14 @@ pub fn parse_decimal(mut s: &[u8]) -> Decimal {
if d.num_digits == 0 {
s = s.skip_chars(b'0');
}
if cfg!(target_endian = "little") {
while s.len() >= 8 && d.num_digits + 8 < Decimal::MAX_DIGITS {
let v = s.read_u64();
if !is_8digits_le(v) {
break;
}
d.digits[d.num_digits..].write_u64(v - 0x3030_3030_3030_3030);
d.num_digits += 8;
s = s.advance(8);
while s.len() >= 8 && d.num_digits + 8 < Decimal::MAX_DIGITS {
let v = s.read_u64();
if !is_8digits(v) {
break;
}
d.digits[d.num_digits..].write_u64(v - 0x3030_3030_3030_3030);
d.num_digits += 8;
s = s.advance(8);
}
parse_digits(&mut s, |digit| d.try_add_digit(digit));
d.decimal_point = s.len() as i32 - first.len() as i32;
Expand Down
34 changes: 16 additions & 18 deletions src/number.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::common::{is_8digits_le, AsciiStr, ByteSlice};
use crate::common::{is_8digits, AsciiStr, ByteSlice};
use crate::float::Float;

const MIN_19DIGIT_INT: u64 = 100_0000_0000_0000_0000;
Expand Down Expand Up @@ -70,7 +70,7 @@ impl Number {
}

#[inline]
fn parse_8digits_le(mut v: u64) -> u64 {
fn parse_8digits(mut v: u64) -> u64 {
const MASK: u64 = 0x0000_00FF_0000_00FF;
const MUL1: u64 = 0x000F_4240_0000_0064;
const MUL2: u64 = 0x0000_2710_0000_0001;
Expand Down Expand Up @@ -98,22 +98,20 @@ fn try_parse_19digits(s: &mut AsciiStr<'_>, x: &mut u64) {
}

#[inline]
fn try_parse_8digits_le(s: &mut AsciiStr<'_>, x: &mut u64) {
fn try_parse_8digits(s: &mut AsciiStr<'_>, x: &mut u64) {
// may cause overflows, to be handled later
if cfg!(target_endian = "little") {
if let Some(v) = s.try_read_u64() {
if is_8digits_le(v) {
*x = x
.wrapping_mul(1_0000_0000)
.wrapping_add(parse_8digits_le(v));
s.step_by(8);
if let Some(v) = s.try_read_u64() {
if is_8digits_le(v) {
*x = x
.wrapping_mul(1_0000_0000)
.wrapping_add(parse_8digits_le(v));
s.step_by(8);
}
if let Some(v) = s.try_read_u64() {
if is_8digits(v) {
*x = x
.wrapping_mul(1_0000_0000)
.wrapping_add(parse_8digits(v));
s.step_by(8);
if let Some(v) = s.try_read_u64() {
if is_8digits(v) {
*x = x
.wrapping_mul(1_0000_0000)
.wrapping_add(parse_8digits(v));
s.step_by(8);
}
}
}
Expand Down Expand Up @@ -180,7 +178,7 @@ pub fn parse_number(s: &[u8]) -> Option<(Number, usize)> {
if s.check_first(b'.') {
s.step();
let before = s;
try_parse_8digits_le(&mut s, &mut mantissa);
try_parse_8digits(&mut s, &mut mantissa);
try_parse_digits(&mut s, &mut mantissa);
n_after_dot = s.offset_from(&before);
exponent = -n_after_dot as i64;
Expand Down

0 comments on commit ec1b7d4

Please sign in to comment.