Skip to content

0.7.1 performance boost #23

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Jun 22, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
[package]
name = "json"
version = "0.7.0"
version = "0.7.1"
authors = ["Maciej Hirsz <[email protected]>"]
description = "JSON implementation in Rust"
repository = "https://github.com/maciejhirsz/json-rust"
documentation = "http://terhix.com/doc/json/"
license = "MIT"

[dependencies]
206 changes: 143 additions & 63 deletions src/codegen.rs
Original file line number Diff line number Diff line change
@@ -1,122 +1,202 @@
use JsonValue;

pub struct Generator {
pub minify: bool,
code: String,
dent: u16,
spaces_per_indent: u16,
}
pub trait Generator {
fn new_line(&mut self) {}

impl Generator {
pub fn new(minify: bool, spaces: u16) -> Self {
Generator {
minify: minify,
code: String::new(),
dent: 0,
spaces_per_indent: spaces
fn write(&mut self, slice: &[u8]);

fn write_min(&mut self, slice: &[u8], minslice: &[u8]);

fn write_char(&mut self, ch: u8);

fn indent(&mut self) {}

fn dedent(&mut self) {}

fn write_string(&mut self, string: &str) {
self.write_char(b'"');

for ch in string.bytes() {
match ch {
b'\\' | b'"' => {
self.write_char(b'\\');
self.write_char(ch);
},
b'\n' => self.write(b"\\n"),
b'\r' => self.write(b"\\r"),
b'\t' => self.write(b"\\t"),
0xC => self.write(b"\\f"),
0x8 => self.write(b"\\b"),
_ => self.write_char(ch)
}
}

self.write_char(b'"');
}

pub fn new_line(&mut self) {
if !self.minify {
self.code.push('\n');
for _ in 0..(self.dent * self.spaces_per_indent) {
self.code.push(' ');
}
fn write_digits_from_u64(&mut self, mut num: u64, length: &mut u8) {
let digit = (num % 10) as u8;
num /= 10;
if num > 0 {
self.write_digits_from_u64(num, length);
}
*length += 1;
self.write_char(digit + b'0');
}

pub fn write_json(&mut self, json: &JsonValue) {
match *json {
JsonValue::String(ref string) => {
self.write_char('"');

for ch in string.chars() {
match ch {
'\\' | '"' => {
self.write_char('\\');
self.write_char(ch);
},
'\n' => self.write("\\n"),
'\r' => self.write("\\r"),
'\t' => self.write("\\t"),
'\u{000C}' => self.write("\\f"),
'\u{0008}' => self.write("\\b"),
_ => self.write_char(ch)
}
}
fn write_number(&mut self, mut num: f64) {
let mut length = 0;
if num < 0.0 {
num = -num;
self.write_char(b'-');
}

self.write_char('"');
},
JsonValue::Number(ref number) => self.write(&number.to_string()),
JsonValue::Boolean(ref value) => self.write(if *value { "true" } else { "false" }),
JsonValue::Null => self.write("null"),
self.write_digits_from_u64(num as u64, &mut length);

let mut fract = num.fract();
if fract < 1e-10 {
return;
}

fract *= 10.0;
self.write_char(b'.');
self.write_char((fract as u8) + b'0');
fract = fract.fract();
length += 2;

while length < 17 && fract > 0.01 {
fract *= 10.0;
self.write_char((fract as u8) + b'0');
fract = fract.fract();
length += 1;
}
}

fn write_json(&mut self, json: &JsonValue) {
match *json {
JsonValue::String(ref string) => self.write_string(string),
JsonValue::Number(ref number) => self.write_number(*number),
JsonValue::Boolean(ref value) => self.write(if *value { b"true" } else { b"false" }),
JsonValue::Null => self.write(b"null"),
JsonValue::Array(ref array) => {
self.write_char('[');
self.write_char(b'[');
self.indent();
let mut first = true;
for item in array {
if first {
first = false;
self.new_line();
} else {
self.write(",");
self.write(b",");
self.new_line();
}
self.write_json(item);
}
self.dedent();
self.new_line();
self.write_char(']');
self.write_char(b']');
},
JsonValue::Object(ref object) => {
self.write_char('{');
self.write_char(b'{');
self.indent();
let mut first = true;
for (key, value) in object.iter() {
if first {
first = false;
self.new_line();
} else {
self.write(",");
self.write(b",");
self.new_line();
}
self.write(&format!("{:?}", key));
self.write_min(": ", ":");
self.write_string(key);
self.write_min(b": ", b":");
self.write_json(value);
}
self.dedent();
self.new_line();
self.write_char('}');
self.write_char(b'}');
}
}
}

pub fn write(&mut self, slice: &str) {
self.code.push_str(slice);
fn consume(self) -> String;
}

pub struct DumpGenerator {
code: Vec<u8>,
}

impl DumpGenerator {
pub fn new() -> Self {
DumpGenerator {
code: Vec::with_capacity(1024),
}
}
}

impl Generator for DumpGenerator {
fn write(&mut self, slice: &[u8]) {
self.code.extend_from_slice(slice);
}

fn write_min(&mut self, _: &[u8], minslice: &[u8]) {
self.code.extend_from_slice(minslice);
}

fn write_char(&mut self, ch: u8) {
self.code.push(ch);
}

fn consume(self) -> String {
String::from_utf8(self.code).unwrap()
}
}

pub struct PrettyGenerator {
code: Vec<u8>,
dent: u16,
spaces_per_indent: u16,
}

impl PrettyGenerator {
pub fn new(spaces: u16) -> Self {
PrettyGenerator {
code: Vec::with_capacity(1024),
dent: 0,
spaces_per_indent: spaces
}
}
}

pub fn write_min(&mut self, slice: &str, minslice: &str) {
if self.minify {
self.write(minslice);
} else {
self.write(slice);
impl Generator for PrettyGenerator {
fn new_line(&mut self) {
self.code.push(b'\n');
for _ in 0..(self.dent * self.spaces_per_indent) {
self.code.push(b' ');
}
}

pub fn write_char(&mut self, ch: char) {
fn write(&mut self, slice: &[u8]) {
self.code.extend_from_slice(slice);
}

fn write_min(&mut self, slice: &[u8], _: &[u8]) {
self.code.extend_from_slice(slice);
}

fn write_char(&mut self, ch: u8) {
self.code.push(ch);
}

pub fn indent(&mut self) {
fn indent(&mut self) {
self.dent += 1;
}

pub fn dedent(&mut self) {
fn dedent(&mut self) {
self.dent -= 1;
}

pub fn consume(self) -> String {
self.code
fn consume(self) -> String {
String::from_utf8(self.code).unwrap()
}
}
35 changes: 21 additions & 14 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use parser::Token;
use std::error::Error;
use std::fmt;
use std::char;

#[derive(Debug)]
pub enum JsonError {
UnexpectedToken(String),
UnexpectedCharacter(char),
UnexpectedEndOfJson,
CantCastCodepointToCharacter(u32),
FailedUtf8Parsing,
ArrayIndexOutOfBounds,
WrongType(String),
UndefinedField(String),
Expand All @@ -18,6 +19,12 @@ impl JsonError {
JsonError::UnexpectedToken(format!("{:?}", token))
}

pub fn unexpected_character(byte: u8) -> Self {
JsonError::UnexpectedCharacter(
char::from_u32(byte as u32).unwrap_or('?')
)
}

pub fn wrong_type(expected: &str) -> Self {
JsonError::WrongType(expected.into())
}
Expand All @@ -32,13 +39,13 @@ impl fmt::Display for JsonError {
use JsonError::*;

match *self {
UnexpectedToken(ref s) => write!(f, "Unexpected token: {}", s),
UnexpectedCharacter(c) => write!(f, "Unexpected character: {}", c),
UnexpectedEndOfJson => write!(f, "Unexpected end of JSON"),
CantCastCodepointToCharacter(i) => write!(f, "Cannot cast this codepoint to a character: {}", i),
ArrayIndexOutOfBounds => write!(f, "Array index out of bounds!"),
WrongType(ref s) => write!(f, "Wrong type, expected: {}", s),
UndefinedField(ref s) => write!(f, "Undefined field: {}", s)
UnexpectedToken(ref s) => write!(f, "Unexpected token: {}", s),
UnexpectedCharacter(ch) => write!(f, "Unexpected character: {}", ch),
UnexpectedEndOfJson => write!(f, "Unexpected end of JSON"),
FailedUtf8Parsing => write!(f, "Failed to parse UTF-8 bytes"),
ArrayIndexOutOfBounds => write!(f, "Array index out of bounds!"),
WrongType(ref s) => write!(f, "Wrong type, expected: {}", s),
UndefinedField(ref s) => write!(f, "Undefined field: {}", s)
}
}
}
Expand All @@ -47,13 +54,13 @@ impl Error for JsonError {
fn description(&self) -> &str {
use JsonError::*;
match *self {
UnexpectedToken(_) => "Unexpected token",
UnexpectedToken(_) => "Unexpected token",
UnexpectedCharacter(_) => "Unexpected character",
UnexpectedEndOfJson => "Unexpected end of JSON",
CantCastCodepointToCharacter(_) => "Cannot cast this codepoint to a character",
ArrayIndexOutOfBounds => "Array index out of bounds!",
WrongType(_) => "Wrong type",
UndefinedField(_) => "Undefined field",
UnexpectedEndOfJson => "Unexpected end of JSON",
FailedUtf8Parsing => "Failed to read bytes as UTF-8 from JSON",
ArrayIndexOutOfBounds => "Array index out of bounds!",
WrongType(_) => "Wrong type",
UndefinedField(_) => "Undefined field",
}
}
}
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ pub use value::JsonValue::Null;
pub type JsonResult<T> = Result<T, JsonError>;

pub use parser::parse;
use codegen::Generator;
use codegen::{ Generator, PrettyGenerator, DumpGenerator };

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

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