Skip to content

Commit 604effa

Browse files
authored
Merge pull request #23 from maciejhirsz/0.7.1
0.7.1 performance boost, related to #6
2 parents d32e964 + a9a7132 commit 604effa

File tree

6 files changed

+354
-238
lines changed

6 files changed

+354
-238
lines changed

Cargo.toml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
[package]
22
name = "json"
3-
version = "0.7.0"
3+
version = "0.7.1"
44
authors = ["Maciej Hirsz <[email protected]>"]
55
description = "JSON implementation in Rust"
66
repository = "https://github.com/maciejhirsz/json-rust"
77
documentation = "http://terhix.com/doc/json/"
88
license = "MIT"
9-
10-
[dependencies]

src/codegen.rs

Lines changed: 143 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,122 +1,202 @@
11
use JsonValue;
22

3-
pub struct Generator {
4-
pub minify: bool,
5-
code: String,
6-
dent: u16,
7-
spaces_per_indent: u16,
8-
}
3+
pub trait Generator {
4+
fn new_line(&mut self) {}
95

10-
impl Generator {
11-
pub fn new(minify: bool, spaces: u16) -> Self {
12-
Generator {
13-
minify: minify,
14-
code: String::new(),
15-
dent: 0,
16-
spaces_per_indent: spaces
6+
fn write(&mut self, slice: &[u8]);
7+
8+
fn write_min(&mut self, slice: &[u8], minslice: &[u8]);
9+
10+
fn write_char(&mut self, ch: u8);
11+
12+
fn indent(&mut self) {}
13+
14+
fn dedent(&mut self) {}
15+
16+
fn write_string(&mut self, string: &str) {
17+
self.write_char(b'"');
18+
19+
for ch in string.bytes() {
20+
match ch {
21+
b'\\' | b'"' => {
22+
self.write_char(b'\\');
23+
self.write_char(ch);
24+
},
25+
b'\n' => self.write(b"\\n"),
26+
b'\r' => self.write(b"\\r"),
27+
b'\t' => self.write(b"\\t"),
28+
0xC => self.write(b"\\f"),
29+
0x8 => self.write(b"\\b"),
30+
_ => self.write_char(ch)
31+
}
1732
}
33+
34+
self.write_char(b'"');
1835
}
1936

20-
pub fn new_line(&mut self) {
21-
if !self.minify {
22-
self.code.push('\n');
23-
for _ in 0..(self.dent * self.spaces_per_indent) {
24-
self.code.push(' ');
25-
}
37+
fn write_digits_from_u64(&mut self, mut num: u64, length: &mut u8) {
38+
let digit = (num % 10) as u8;
39+
num /= 10;
40+
if num > 0 {
41+
self.write_digits_from_u64(num, length);
2642
}
43+
*length += 1;
44+
self.write_char(digit + b'0');
2745
}
2846

29-
pub fn write_json(&mut self, json: &JsonValue) {
30-
match *json {
31-
JsonValue::String(ref string) => {
32-
self.write_char('"');
33-
34-
for ch in string.chars() {
35-
match ch {
36-
'\\' | '"' => {
37-
self.write_char('\\');
38-
self.write_char(ch);
39-
},
40-
'\n' => self.write("\\n"),
41-
'\r' => self.write("\\r"),
42-
'\t' => self.write("\\t"),
43-
'\u{000C}' => self.write("\\f"),
44-
'\u{0008}' => self.write("\\b"),
45-
_ => self.write_char(ch)
46-
}
47-
}
47+
fn write_number(&mut self, mut num: f64) {
48+
let mut length = 0;
49+
if num < 0.0 {
50+
num = -num;
51+
self.write_char(b'-');
52+
}
4853

49-
self.write_char('"');
50-
},
51-
JsonValue::Number(ref number) => self.write(&number.to_string()),
52-
JsonValue::Boolean(ref value) => self.write(if *value { "true" } else { "false" }),
53-
JsonValue::Null => self.write("null"),
54+
self.write_digits_from_u64(num as u64, &mut length);
55+
56+
let mut fract = num.fract();
57+
if fract < 1e-10 {
58+
return;
59+
}
60+
61+
fract *= 10.0;
62+
self.write_char(b'.');
63+
self.write_char((fract as u8) + b'0');
64+
fract = fract.fract();
65+
length += 2;
66+
67+
while length < 17 && fract > 0.01 {
68+
fract *= 10.0;
69+
self.write_char((fract as u8) + b'0');
70+
fract = fract.fract();
71+
length += 1;
72+
}
73+
}
74+
75+
fn write_json(&mut self, json: &JsonValue) {
76+
match *json {
77+
JsonValue::String(ref string) => self.write_string(string),
78+
JsonValue::Number(ref number) => self.write_number(*number),
79+
JsonValue::Boolean(ref value) => self.write(if *value { b"true" } else { b"false" }),
80+
JsonValue::Null => self.write(b"null"),
5481
JsonValue::Array(ref array) => {
55-
self.write_char('[');
82+
self.write_char(b'[');
5683
self.indent();
5784
let mut first = true;
5885
for item in array {
5986
if first {
6087
first = false;
6188
self.new_line();
6289
} else {
63-
self.write(",");
90+
self.write(b",");
6491
self.new_line();
6592
}
6693
self.write_json(item);
6794
}
6895
self.dedent();
6996
self.new_line();
70-
self.write_char(']');
97+
self.write_char(b']');
7198
},
7299
JsonValue::Object(ref object) => {
73-
self.write_char('{');
100+
self.write_char(b'{');
74101
self.indent();
75102
let mut first = true;
76103
for (key, value) in object.iter() {
77104
if first {
78105
first = false;
79106
self.new_line();
80107
} else {
81-
self.write(",");
108+
self.write(b",");
82109
self.new_line();
83110
}
84-
self.write(&format!("{:?}", key));
85-
self.write_min(": ", ":");
111+
self.write_string(key);
112+
self.write_min(b": ", b":");
86113
self.write_json(value);
87114
}
88115
self.dedent();
89116
self.new_line();
90-
self.write_char('}');
117+
self.write_char(b'}');
91118
}
92119
}
93120
}
94121

95-
pub fn write(&mut self, slice: &str) {
96-
self.code.push_str(slice);
122+
fn consume(self) -> String;
123+
}
124+
125+
pub struct DumpGenerator {
126+
code: Vec<u8>,
127+
}
128+
129+
impl DumpGenerator {
130+
pub fn new() -> Self {
131+
DumpGenerator {
132+
code: Vec::with_capacity(1024),
133+
}
134+
}
135+
}
136+
137+
impl Generator for DumpGenerator {
138+
fn write(&mut self, slice: &[u8]) {
139+
self.code.extend_from_slice(slice);
140+
}
141+
142+
fn write_min(&mut self, _: &[u8], minslice: &[u8]) {
143+
self.code.extend_from_slice(minslice);
144+
}
145+
146+
fn write_char(&mut self, ch: u8) {
147+
self.code.push(ch);
148+
}
149+
150+
fn consume(self) -> String {
151+
String::from_utf8(self.code).unwrap()
152+
}
153+
}
154+
155+
pub struct PrettyGenerator {
156+
code: Vec<u8>,
157+
dent: u16,
158+
spaces_per_indent: u16,
159+
}
160+
161+
impl PrettyGenerator {
162+
pub fn new(spaces: u16) -> Self {
163+
PrettyGenerator {
164+
code: Vec::with_capacity(1024),
165+
dent: 0,
166+
spaces_per_indent: spaces
167+
}
97168
}
169+
}
98170

99-
pub fn write_min(&mut self, slice: &str, minslice: &str) {
100-
if self.minify {
101-
self.write(minslice);
102-
} else {
103-
self.write(slice);
171+
impl Generator for PrettyGenerator {
172+
fn new_line(&mut self) {
173+
self.code.push(b'\n');
174+
for _ in 0..(self.dent * self.spaces_per_indent) {
175+
self.code.push(b' ');
104176
}
105177
}
106178

107-
pub fn write_char(&mut self, ch: char) {
179+
fn write(&mut self, slice: &[u8]) {
180+
self.code.extend_from_slice(slice);
181+
}
182+
183+
fn write_min(&mut self, slice: &[u8], _: &[u8]) {
184+
self.code.extend_from_slice(slice);
185+
}
186+
187+
fn write_char(&mut self, ch: u8) {
108188
self.code.push(ch);
109189
}
110190

111-
pub fn indent(&mut self) {
191+
fn indent(&mut self) {
112192
self.dent += 1;
113193
}
114194

115-
pub fn dedent(&mut self) {
195+
fn dedent(&mut self) {
116196
self.dent -= 1;
117197
}
118198

119-
pub fn consume(self) -> String {
120-
self.code
199+
fn consume(self) -> String {
200+
String::from_utf8(self.code).unwrap()
121201
}
122202
}

src/error.rs

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
use parser::Token;
22
use std::error::Error;
33
use std::fmt;
4+
use std::char;
45

56
#[derive(Debug)]
67
pub enum JsonError {
78
UnexpectedToken(String),
89
UnexpectedCharacter(char),
910
UnexpectedEndOfJson,
10-
CantCastCodepointToCharacter(u32),
11+
FailedUtf8Parsing,
1112
ArrayIndexOutOfBounds,
1213
WrongType(String),
1314
UndefinedField(String),
@@ -18,6 +19,12 @@ impl JsonError {
1819
JsonError::UnexpectedToken(format!("{:?}", token))
1920
}
2021

22+
pub fn unexpected_character(byte: u8) -> Self {
23+
JsonError::UnexpectedCharacter(
24+
char::from_u32(byte as u32).unwrap_or('?')
25+
)
26+
}
27+
2128
pub fn wrong_type(expected: &str) -> Self {
2229
JsonError::WrongType(expected.into())
2330
}
@@ -32,13 +39,13 @@ impl fmt::Display for JsonError {
3239
use JsonError::*;
3340

3441
match *self {
35-
UnexpectedToken(ref s) => write!(f, "Unexpected token: {}", s),
36-
UnexpectedCharacter(c) => write!(f, "Unexpected character: {}", c),
37-
UnexpectedEndOfJson => write!(f, "Unexpected end of JSON"),
38-
CantCastCodepointToCharacter(i) => write!(f, "Cannot cast this codepoint to a character: {}", i),
39-
ArrayIndexOutOfBounds => write!(f, "Array index out of bounds!"),
40-
WrongType(ref s) => write!(f, "Wrong type, expected: {}", s),
41-
UndefinedField(ref s) => write!(f, "Undefined field: {}", s)
42+
UnexpectedToken(ref s) => write!(f, "Unexpected token: {}", s),
43+
UnexpectedCharacter(ch) => write!(f, "Unexpected character: {}", ch),
44+
UnexpectedEndOfJson => write!(f, "Unexpected end of JSON"),
45+
FailedUtf8Parsing => write!(f, "Failed to parse UTF-8 bytes"),
46+
ArrayIndexOutOfBounds => write!(f, "Array index out of bounds!"),
47+
WrongType(ref s) => write!(f, "Wrong type, expected: {}", s),
48+
UndefinedField(ref s) => write!(f, "Undefined field: {}", s)
4249
}
4350
}
4451
}
@@ -47,13 +54,13 @@ impl Error for JsonError {
4754
fn description(&self) -> &str {
4855
use JsonError::*;
4956
match *self {
50-
UnexpectedToken(_) => "Unexpected token",
57+
UnexpectedToken(_) => "Unexpected token",
5158
UnexpectedCharacter(_) => "Unexpected character",
52-
UnexpectedEndOfJson => "Unexpected end of JSON",
53-
CantCastCodepointToCharacter(_) => "Cannot cast this codepoint to a character",
54-
ArrayIndexOutOfBounds => "Array index out of bounds!",
55-
WrongType(_) => "Wrong type",
56-
UndefinedField(_) => "Undefined field",
59+
UnexpectedEndOfJson => "Unexpected end of JSON",
60+
FailedUtf8Parsing => "Failed to read bytes as UTF-8 from JSON",
61+
ArrayIndexOutOfBounds => "Array index out of bounds!",
62+
WrongType(_) => "Wrong type",
63+
UndefinedField(_) => "Undefined field",
5764
}
5865
}
5966
}

src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ pub use value::JsonValue::Null;
194194
pub type JsonResult<T> = Result<T, JsonError>;
195195

196196
pub use parser::parse;
197-
use codegen::Generator;
197+
use codegen::{ Generator, PrettyGenerator, DumpGenerator };
198198

199199
use std::collections::HashMap;
200200
use std::collections::BTreeMap;
@@ -206,15 +206,15 @@ pub type Object = BTreeMap<String, JsonValue>;
206206
impl JsonValue {
207207
/// Prints out the value as JSON string.
208208
pub fn dump(&self) -> String {
209-
let mut gen = Generator::new(true, 0);
209+
let mut gen = DumpGenerator::new();
210210
gen.write_json(self);
211211
gen.consume()
212212
}
213213

214214
/// Pretty prints out the value as JSON string. Takes an argument that's
215215
/// number of spaces to indent new blocks with.
216216
pub fn pretty(&self, spaces: u16) -> String {
217-
let mut gen = Generator::new(false, spaces);
217+
let mut gen = PrettyGenerator::new(spaces);
218218
gen.write_json(self);
219219
gen.consume()
220220
}

0 commit comments

Comments
 (0)