Skip to content

Commit af2296e

Browse files
committed
Faster float parsing
1 parent c7bb527 commit af2296e

File tree

2 files changed

+113
-83
lines changed

2 files changed

+113
-83
lines changed

src/parser.rs

Lines changed: 111 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -157,19 +157,20 @@ macro_rules! expect_string {
157157
})
158158
}
159159

160-
fn exponent_to_power(e: i32) -> f64 {
161-
static POWERS: [f64; 22] = [
162-
1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8,
163-
1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16,
164-
1e17, 1e18, 1e19, 1e20, 1e21, 1e22
165-
];
166-
167-
static NEG_POWERS: [f64; 22] = [
168-
1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, 1e-8,
169-
1e-9, 1e-10, 1e-11, 1e-12, 1e-13, 1e-14, 1e-15, 1e-16,
170-
1e-17, 1e-18, 1e-19, 1e-20, 1e-21, 1e-22
171-
];
160+
static POWERS: [f64; 22] = [
161+
1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8,
162+
1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16,
163+
1e17, 1e18, 1e19, 1e20, 1e21, 1e22
164+
];
172165

166+
static NEG_POWERS: [f64; 22] = [
167+
1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, 1e-8,
168+
1e-9, 1e-10, 1e-11, 1e-12, 1e-13, 1e-14, 1e-15, 1e-16,
169+
1e-17, 1e-18, 1e-19, 1e-20, 1e-21, 1e-22
170+
];
171+
172+
#[inline(always)]
173+
fn exponent_to_power(e: i32) -> f64 {
173174
let index = (e.abs() - 1) as usize;
174175

175176
// index=0 is e=1
@@ -185,6 +186,7 @@ fn exponent_to_power(e: i32) -> f64 {
185186
}
186187
}
187188

189+
#[inline(always)]
188190
fn make_float(num: u64, e: i32) -> f64 {
189191
(num as f64) * exponent_to_power(e)
190192
}
@@ -216,12 +218,67 @@ macro_rules! expect_number {
216218
// Avoid multiplication with bitshifts and addition
217219
num = (num << 1) + (num << 3) + (ch - b'0') as u64;
218220
},
219-
b'.' | b'e' | b'E' => {
220-
result = try!($parser.read_number_with_fraction(num, 0));
221+
_ => {
222+
let mut e = 0;
223+
result = allow_number_extensions!($parser, num, e, ch);
221224
break;
225+
}
226+
}
227+
}
228+
229+
result
230+
})
231+
}
232+
233+
macro_rules! allow_number_extensions {
234+
($parser:ident, $num:ident, $e:ident, $ch:ident) => ({
235+
match $ch {
236+
b'.' => {
237+
$parser.bump();
238+
expect_fracton!($parser, $num, $e)
239+
},
240+
b'e' | b'E' => {
241+
$parser.bump();
242+
try!($parser.expect_exponent($num, $e))
243+
},
244+
_ => $num as f64
245+
}
246+
});
247+
248+
($parser:ident) => ({
249+
let mut num = 0;
250+
let mut e = 0;
251+
let ch = $parser.read_byte();
252+
allow_number_extensions!($parser, num, e, ch)
253+
})
254+
}
255+
256+
macro_rules! expect_fracton {
257+
($parser:ident, $num:ident, $e:ident) => ({
258+
let result: f64;
259+
260+
loop {
261+
if $parser.is_eof() {
262+
result = make_float($num, $e);
263+
break;
264+
}
265+
let ch = $parser.read_byte();
266+
267+
match ch {
268+
b'0' ... b'9' => {
269+
$parser.bump();
270+
if $num < MAX_PRECISION {
271+
$num = ($num << 3) + ($num << 1) + (ch - b'0') as u64;
272+
$e -= 1;
273+
}
222274
},
223-
_ => {
224-
result = num as f64;
275+
b'e' | b'E' => {
276+
$parser.bump();
277+
result = try!($parser.expect_exponent($num, $e));
278+
break;
279+
}
280+
_ => {
281+
result = make_float($num, $e);
225282
break;
226283
}
227284
}
@@ -244,22 +301,17 @@ macro_rules! expect_value {
244301
b'[' => JsonValue::Array(try!($parser.read_array())),
245302
b'{' => JsonValue::Object(try!($parser.read_object())),
246303
b'"' => expect_string!($parser).into(),
247-
b'0' => {
248-
let num = try!($parser.read_number_with_fraction(0, 0));
249-
JsonValue::Number(num)
250-
},
304+
b'0' => JsonValue::Number(allow_number_extensions!($parser)),
251305
b'1' ... b'9' => {
252-
let num = expect_number!($parser, ch);
253-
JsonValue::Number(num)
306+
JsonValue::Number(expect_number!($parser, ch))
254307
},
255308
b'-' => {
256309
let ch = expect_byte!($parser);
257-
let num = match ch {
258-
b'0' => try!($parser.read_number_with_fraction(0, 0)),
310+
JsonValue::Number(- match ch {
311+
b'0' => allow_number_extensions!($parser),
259312
b'1' ... b'9' => expect_number!($parser, ch),
260313
_ => return $parser.unexpected_character(ch)
261-
};
262-
JsonValue::Number(-num)
314+
})
263315
}
264316
b't' => {
265317
sequence!($parser, b'r', b'u', b'e');
@@ -301,7 +353,7 @@ impl<'a> Parser<'a> {
301353

302354
#[inline(always)]
303355
fn bump(&mut self) {
304-
self.index += 1;
356+
self.index = self.index.wrapping_add(1);
305357
}
306358

307359
fn source_position_from_index(&self, index: usize) -> Position {
@@ -473,7 +525,7 @@ impl<'a> Parser<'a> {
473525
})
474526
}
475527

476-
fn read_big_number(&mut self, num: u64) -> Result<f64> {
528+
fn read_big_number(&mut self, mut num: u64) -> Result<f64> {
477529
// Attempt to continue reading digits that would overflow
478530
// u64 into freshly converted f64
479531

@@ -487,70 +539,46 @@ impl<'a> Parser<'a> {
487539
self.bump();
488540
e += 1;
489541
},
542+
b'.' => {
543+
self.bump();
544+
return Ok(expect_fracton!(self, num, e));
545+
},
546+
b'e' | b'E' => {
547+
self.bump();
548+
return self.expect_exponent(num, e);
549+
}
490550
_ => break
491551
}
492552
}
493553

494-
self.read_number_with_fraction(num, e)
554+
Ok(make_float(num, e))
555+
// self.expect_exponent(num, e)
495556
}
496557

497-
fn read_number_with_fraction(&mut self, mut num: u64, mut e: i32) -> Result<f64> {
498-
if self.is_eof() {
499-
return Ok(make_float(num, e));
500-
}
501-
502-
let mut ch = self.read_byte();
503-
504-
if ch == b'.' {
505-
self.bump();
506-
507-
loop {
508-
if self.is_eof() {
509-
return Ok(make_float(num, e));
510-
}
511-
ch = self.read_byte();
512-
513-
match ch {
514-
b'0' ... b'9' => {
515-
self.bump();
516-
if num < MAX_PRECISION {
517-
num = (num << 3) + (num << 1) + (ch - b'0') as u64;
518-
e -= 1;
519-
}
520-
},
521-
_ => break
522-
}
523-
}
524-
}
525-
526-
if ch == b'e' || ch == b'E' {
527-
self.bump();
528-
ch = expect_byte!(self);
529-
let sign = match ch {
530-
b'-' => {
531-
ch = expect_byte!(self);
532-
-1
533-
},
534-
b'+' => {
535-
ch = expect_byte!(self);
536-
1
537-
},
538-
_ => 1
539-
};
540-
541-
let num = make_float(num, e);
558+
fn expect_exponent(&mut self, num: u64, e: i32) -> Result<f64> {
559+
let mut ch = expect_byte!(self);
560+
let sign = match ch {
561+
b'-' => {
562+
ch = expect_byte!(self);
563+
-1
564+
},
565+
b'+' => {
566+
ch = expect_byte!(self);
567+
1
568+
},
569+
_ => 1
570+
};
542571

543-
let mut e = match ch {
544-
b'0' ... b'9' => (ch - b'0') as i32,
545-
_ => return self.unexpected_character(ch),
546-
};
572+
let num = make_float(num, e);
547573

548-
read_num!(self, digit, e = (e << 3) + (e << 1) + digit as i32);
574+
let mut e = match ch {
575+
b'0' ... b'9' => (ch - b'0') as i32,
576+
_ => return self.unexpected_character(ch),
577+
};
549578

550-
return Ok(num * exponent_to_power(e * sign));
551-
}
579+
read_num!(self, digit, e = (e << 3) + (e << 1) + digit as i32);
552580

553-
Ok(make_float(num, e))
581+
Ok(num * exponent_to_power(e * sign))
554582
}
555583

556584
fn read_object(&mut self) -> Result<Object> {
@@ -585,7 +613,7 @@ impl<'a> Parser<'a> {
585613
fn read_array(&mut self) -> Result<Vec<JsonValue>> {
586614
let first = expect_value!{ self, b']' => return Ok(Vec::new()) };
587615

588-
let mut array = Vec::with_capacity(2);
616+
let mut array = Vec::with_capacity(3);
589617
array.push(first);
590618

591619
loop {

src/value.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use iterators::{ Members, MembersMut, Entries, EntriesMut };
77
use short::Short;
88
use object::Object;
99

10+
// These are convenience macros for converting `f64` to the `$unsigned` type.
11+
// The macros check that the numbers are representable the target type.
1012
macro_rules! f64_to_unsinged {
1113
($unsigned:ident, $value:expr) => {
1214
if $value < 0.0 || $value > $unsigned::MAX as f64 {

0 commit comments

Comments
 (0)