From bd429cc227fc482e56630999bdf02c78402cc04f Mon Sep 17 00:00:00 2001 From: Maciej Hirsz Date: Fri, 6 Sep 2019 17:55:48 +0200 Subject: [PATCH] v0.12.0 --- Cargo.toml | 3 +- src/codegen.rs | 66 +++++++++++------------ src/lib.rs | 8 +-- src/number.rs | 4 +- src/object.rs | 18 ++++++- src/parser.rs | 114 +++++++++++++++++----------------------- src/util/grisu2.rs | 2 +- src/util/print_dec.rs | 18 +++---- src/value/implements.rs | 52 ++++++------------ src/value/mod.rs | 23 ++++---- tests/stringify.rs | 8 +-- 11 files changed, 147 insertions(+), 169 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 212522f..c4759ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,9 @@ [package] name = "json" -version = "0.11.15" +version = "0.12.0" authors = ["Maciej Hirsz "] description = "JSON implementation in Rust" repository = "https://github.com/maciejhirsz/json-rust" documentation = "https://docs.rs/json/" license = "MIT/Apache-2.0" +edition = "2018" diff --git a/src/codegen.rs b/src/codegen.rs index cd07869..dbda7a4 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -1,11 +1,11 @@ use std::ptr; use std::io::Write; -use JsonValue; -use number::Number; -use object::Object; use std::io; -use util::print_dec; +use crate::JsonValue; +use crate::number::Number; +use crate::object::Object; +use crate::util::print_dec; const QU: u8 = b'"'; const BS: u8 = b'\\'; @@ -66,27 +66,27 @@ pub trait Generator { #[inline(never)] fn write_string_complex(&mut self, string: &str, mut start: usize) -> io::Result<()> { - try!(self.write(string[ .. start].as_bytes())); + self.write(string[ .. start].as_bytes())?; for (index, ch) in string.bytes().enumerate().skip(start) { let escape = ESCAPED[ch as usize]; if escape > 0 { - try!(self.write(string[start .. index].as_bytes())); - try!(self.write(&[b'\\', escape])); + self.write(string[start .. index].as_bytes())?; + self.write(&[b'\\', escape])?; start = index + 1; } if escape == b'u' { - try!(write!(self.get_writer(), "{:04x}", ch)); + write!(self.get_writer(), "{:04x}", ch)?; } } - try!(self.write(string[start ..].as_bytes())); + self.write(string[start ..].as_bytes())?; self.write_char(b'"') } #[inline(always)] fn write_string(&mut self, string: &str) -> io::Result<()> { - try!(self.write_char(b'"')); + self.write_char(b'"')?; for (index, ch) in string.bytes().enumerate() { if ESCAPED[ch as usize] > 0 { @@ -94,7 +94,7 @@ pub trait Generator { } } - try!(self.write(string.as_bytes())); + self.write(string.as_bytes())?; self.write_char(b'"') } @@ -116,30 +116,30 @@ pub trait Generator { #[inline(always)] fn write_object(&mut self, object: &Object) -> io::Result<()> { - try!(self.write_char(b'{')); + self.write_char(b'{')?; let mut iter = object.iter(); if let Some((key, value)) = iter.next() { self.indent(); - try!(self.new_line()); - try!(self.write_string(key)); - try!(self.write_min(b": ", b':')); - try!(self.write_json(value)); + self.new_line()?; + self.write_string(key)?; + self.write_min(b": ", b':')?; + self.write_json(value)?; } else { - try!(self.write_char(b'}')); + self.write_char(b'}')?; return Ok(()); } for (key, value) in iter { - try!(self.write_char(b',')); - try!(self.new_line()); - try!(self.write_string(key)); - try!(self.write_min(b": ", b':')); - try!(self.write_json(value)); + self.write_char(b',')?; + self.new_line()?; + self.write_string(key)?; + self.write_min(b": ", b':')?; + self.write_json(value)?; } self.dedent(); - try!(self.new_line()); + self.new_line()?; self.write_char(b'}') } @@ -152,26 +152,26 @@ pub trait Generator { JsonValue::Boolean(true) => self.write(b"true"), JsonValue::Boolean(false) => self.write(b"false"), JsonValue::Array(ref array) => { - try!(self.write_char(b'[')); + self.write_char(b'[')?; let mut iter = array.iter(); if let Some(item) = iter.next() { self.indent(); - try!(self.new_line()); - try!(self.write_json(item)); + self.new_line()?; + self.write_json(item)?; } else { - try!(self.write_char(b']')); + self.write_char(b']')?; return Ok(()); } for item in iter { - try!(self.write_char(b',')); - try!(self.new_line()); - try!(self.write_json(item)); + self.write_char(b',')?; + self.new_line()?; + self.write_json(item)?; } self.dedent(); - try!(self.new_line()); + self.new_line()?; self.write_char(b']') }, JsonValue::Object(ref object) => { @@ -345,9 +345,9 @@ impl<'a, W> Generator for PrettyWriterGenerator<'a, W> where W: Write { } fn new_line(&mut self) -> io::Result<()> { - try!(self.write_char(b'\n')); + self.write_char(b'\n')?; for _ in 0..(self.dent * self.spaces_per_indent) { - try!(self.write_char(b' ')); + self.write_char(b' ')?; } Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index db835b4..e08389f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -237,7 +237,7 @@ pub mod iterators { pub use Error as JsonError; #[deprecated(since="0.9.0", note="use `json::Result` instead")] -pub use Result as JsonResult; +pub use crate::Result as JsonResult; pub use parser::parse; @@ -280,7 +280,8 @@ macro_rules! array { [] => ($crate::JsonValue::new_array()); [ $( $item:expr ),* ] => ({ - let mut array = Vec::new(); + let size = 0 $( + {let _ = $item; 1} )*; + let mut array = Vec::with_capacity(size); $( array.push($item.into()); @@ -326,7 +327,8 @@ macro_rules! object { { $( $key:expr => $value:expr, )* } => ({ use $crate::object::Object; - let mut object = Object::new(); + let size = 0 $( + {let _ = $key; 1} )*; + let mut object = Object::with_capacity(size); $( object.insert($key, $value.into()); diff --git a/src/number.rs b/src/number.rs index 40c98b4..deee06a 100644 --- a/src/number.rs +++ b/src/number.rs @@ -1,7 +1,7 @@ use std::{ ops, fmt, f32, f64 }; use std::num::FpCategory; -use util::grisu2; -use util::print_dec; +use crate::util::grisu2; +use crate::util::print_dec; /// NaN value represented in `Number` type. NaN is equal to itself. pub const NAN: Number = Number { diff --git a/src/object.rs b/src/object.rs index 3bd6831..bac50f1 100644 --- a/src/object.rs +++ b/src/object.rs @@ -1,8 +1,9 @@ use std::{ ptr, mem, str, slice, fmt }; use std::ops::{ Index, IndexMut, Deref }; +use std::iter::FromIterator; -use codegen::{ DumpGenerator, Generator, PrettyGenerator }; -use value::JsonValue; +use crate::codegen::{ DumpGenerator, Generator, PrettyGenerator }; +use crate::value::JsonValue; const KEY_BUF_LEN: usize = 32; static NULL: JsonValue = JsonValue::Null; @@ -539,6 +540,19 @@ impl Clone for Object { } } +impl, V: Into> FromIterator<(K, V)> for Object { + fn from_iter>(iter: I) -> Self { + let iter = iter.into_iter(); + let mut object = Object::with_capacity(iter.size_hint().0); + + for (key, value) in iter { + object.insert(key.as_ref(), value.into()); + } + + object + } +} + // Because keys can inserted in different order, the safe way to // compare `Object`s is to iterate over one and check if the other // has all the same keys. diff --git a/src/parser.rs b/src/parser.rs index 56849b8..1c9ea7a 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -17,10 +17,12 @@ // This makes for some ugly code, but it is faster. Hopefully in the future // with MIR support the compiler will get smarter about this. -use std::{ str, slice }; -use object::Object; -use number::Number; -use { JsonValue, Error, Result }; +use std::{str, slice}; +use std::char::decode_utf16; +use std::convert::TryFrom; +use crate::object::Object; +use crate::number::Number; +use crate::{JsonValue, Error, Result}; // This is not actual max precision, but a threshold at which number parsing // kicks into checked math. @@ -97,10 +99,10 @@ macro_rules! expect_byte_ignore_whitespace { // Don't go straight for the loop, assume we are in the clear first. match ch { // whitespace - 9 ... 13 | 32 => { + 9 ..= 13 | 32 => { loop { match expect_byte!($parser) { - 9 ... 13 | 32 => {}, + 9 ..= 13 | 32 => {}, next => { ch = next; break; @@ -120,7 +122,7 @@ macro_rules! expect_eof { ($parser:ident) => ({ while !$parser.is_eof() { match $parser.read_byte() { - 9 ... 13 | 32 => $parser.bump(), + 9 ..= 13 | 32 => $parser.bump(), _ => { $parser.bump(); return $parser.unexpected_character(); @@ -159,7 +161,7 @@ macro_rules! expect { // form in a string. const QU: bool = false; // double quote 0x22 const BS: bool = false; // backslash 0x5C -const CT: bool = false; // control character 0x00 ... 0x1F +const CT: bool = false; // control character 0x00 ..= 0x1F const __: bool = true; static ALLOWED: [bool; 256] = [ @@ -208,7 +210,7 @@ macro_rules! expect_string { break; } if ch == b'\\' { - result = try!($parser.read_complex_string(start)); + result = $parser.read_complex_string(start)?; break; } @@ -231,7 +233,7 @@ macro_rules! expect_number { // in order to avoid an overflow. loop { if num >= MAX_PRECISION { - result = try!($parser.read_big_number(num)); + result = $parser.read_big_number(num)?; break; } @@ -243,7 +245,7 @@ macro_rules! expect_number { let ch = $parser.read_byte(); match ch { - b'0' ... b'9' => { + b'0' ..= b'9' => { $parser.bump(); num = num * 10 + (ch - b'0') as u64; }, @@ -271,7 +273,7 @@ macro_rules! allow_number_extensions { }, b'e' | b'E' => { $parser.bump(); - try!($parser.expect_exponent($num, $e)) + $parser.expect_exponent($num, $e)? }, _ => $num.into() } @@ -302,7 +304,7 @@ macro_rules! expect_fraction { let ch = expect_byte!($parser); match ch { - b'0' ... b'9' => { + b'0' ..= b'9' => { if $num < MAX_PRECISION { $num = $num * 10 + (ch - b'0') as u64; $e -= 1; @@ -329,7 +331,7 @@ macro_rules! expect_fraction { let ch = $parser.read_byte(); match ch { - b'0' ... b'9' => { + b'0' ..= b'9' => { $parser.bump(); if $num < MAX_PRECISION { $num = $num * 10 + (ch - b'0') as u64; @@ -348,7 +350,7 @@ macro_rules! expect_fraction { }, b'e' | b'E' => { $parser.bump(); - result = try!($parser.expect_exponent($num, $e)); + result = $parser.expect_exponent($num, $e)?; break; } _ => { @@ -425,23 +427,23 @@ impl<'a> Parser<'a> { } // Boring - fn read_hexdec_digit(&mut self) -> Result { + fn read_hexdec_digit(&mut self) -> Result { let ch = expect_byte!(self); Ok(match ch { - b'0' ... b'9' => (ch - b'0'), - b'a' ... b'f' => (ch + 10 - b'a'), - b'A' ... b'F' => (ch + 10 - b'A'), + b'0' ..= b'9' => (ch - b'0'), + b'a' ..= b'f' => (ch + 10 - b'a'), + b'A' ..= b'F' => (ch + 10 - b'A'), _ => return self.unexpected_character(), - } as u32) + } as u16) } // Boring - fn read_hexdec_codepoint(&mut self) -> Result { + fn read_hexdec_codepoint(&mut self) -> Result { Ok( - try!(self.read_hexdec_digit()) << 12 | - try!(self.read_hexdec_digit()) << 8 | - try!(self.read_hexdec_digit()) << 4 | - try!(self.read_hexdec_digit()) + self.read_hexdec_digit()? << 12 | + self.read_hexdec_digit()? << 8 | + self.read_hexdec_digit()? << 4 | + self.read_hexdec_digit()? ) } @@ -449,47 +451,25 @@ impl<'a> Parser<'a> { // sequence such as `\uDEAD` from the string. Except `DEAD` is // not a valid codepoint, so it also needs to handle errors... fn read_codepoint(&mut self) -> Result<()> { - let mut codepoint = try!(self.read_hexdec_codepoint()); - - match codepoint { - 0x0000 ... 0xD7FF => {}, - 0xD800 ... 0xDBFF => { - codepoint -= 0xD800; - codepoint <<= 10; + let mut buf = [0; 4]; + let codepoint = self.read_hexdec_codepoint()?; + let unicode = match char::try_from(codepoint as u32) { + Ok(code) => code, + // Handle surrogate pairs + Err(_) => { expect_sequence!(self, b'\\', b'u'); - let lower = try!(self.read_hexdec_codepoint()); - - if let 0xDC00 ... 0xDFFF = lower { - codepoint = (codepoint | lower - 0xDC00) + 0x010000; - } else { - return Err(Error::FailedUtf8Parsing) + match decode_utf16( + [codepoint, self.read_hexdec_codepoint()?].iter().copied() + ).next() { + Some(Ok(code)) => code, + _ => return Err(Error::FailedUtf8Parsing), } - }, - 0xE000 ... 0xFFFF => {}, - _ => return Err(Error::FailedUtf8Parsing) - } + } + }; - match codepoint { - 0x0000 ... 0x007F => self.buffer.push(codepoint as u8), - 0x0080 ... 0x07FF => self.buffer.extend_from_slice(&[ - (((codepoint >> 6) as u8) & 0x1F) | 0xC0, - ((codepoint as u8) & 0x3F) | 0x80 - ]), - 0x0800 ... 0xFFFF => self.buffer.extend_from_slice(&[ - (((codepoint >> 12) as u8) & 0x0F) | 0xE0, - (((codepoint >> 6) as u8) & 0x3F) | 0x80, - ((codepoint as u8) & 0x3F) | 0x80 - ]), - 0x10000 ... 0x10FFFF => self.buffer.extend_from_slice(&[ - (((codepoint >> 18) as u8) & 0x07) | 0xF0, - (((codepoint >> 12) as u8) & 0x3F) | 0x80, - (((codepoint >> 6) as u8) & 0x3F) | 0x80, - ((codepoint as u8) & 0x3F) | 0x80 - ]), - _ => return Err(Error::FailedUtf8Parsing) - } + self.buffer.extend_from_slice(unicode.encode_utf8(&mut buf).as_bytes()); Ok(()) } @@ -521,7 +501,7 @@ impl<'a> Parser<'a> { let escaped = expect_byte!(self); let escaped = match escaped { b'u' => { - try!(self.read_codepoint()); + self.read_codepoint()?; ch = expect_byte!(self); continue; }, @@ -572,7 +552,7 @@ impl<'a> Parser<'a> { } let ch = self.read_byte(); match ch { - b'0' ... b'9' => { + b'0' ..= b'9' => { self.bump(); match num.checked_mul(10).and_then(|num| { num.checked_add((ch - b'0') as u64) @@ -613,7 +593,7 @@ impl<'a> Parser<'a> { }; let mut e = match ch { - b'0' ... b'9' => (ch - b'0') as i16, + b'0' ..= b'9' => (ch - b'0') as i16, _ => return self.unexpected_character(), }; @@ -623,7 +603,7 @@ impl<'a> Parser<'a> { } let ch = self.read_byte(); match ch { - b'0' ... b'9' => { + b'0' ..= b'9' => { self.bump(); e = e.saturating_mul(10).saturating_add((ch - b'0') as i16); }, @@ -683,14 +663,14 @@ impl<'a> Parser<'a> { }, b'"' => expect_string!(self).into(), b'0' => JsonValue::Number(allow_number_extensions!(self)), - b'1' ... b'9' => { + b'1' ..= b'9' => { JsonValue::Number(expect_number!(self, ch)) }, b'-' => { let ch = expect_byte!(self); JsonValue::Number(- match ch { b'0' => allow_number_extensions!(self), - b'1' ... b'9' => expect_number!(self, ch), + b'1' ..= b'9' => expect_number!(self, ch), _ => return self.unexpected_character() }) } diff --git a/src/util/grisu2.rs b/src/util/grisu2.rs index ce3b865..2054c71 100644 --- a/src/util/grisu2.rs +++ b/src/util/grisu2.rs @@ -15,7 +15,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use util::diyfp::{ self, DiyFp }; +use crate::util::diyfp::{ self, DiyFp }; #[inline] unsafe fn grisu_round(buffer: &mut u64, delta: u64, mut rest: u64, ten_kappa: u64, wp_w: u64) { diff --git a/src/util/print_dec.rs b/src/util/print_dec.rs index ea55aee..c8e275d 100644 --- a/src/util/print_dec.rs +++ b/src/util/print_dec.rs @@ -52,7 +52,7 @@ unsafe fn write_num(n: &mut u64, curr: &mut isize, buf_ptr: *mut u8, lut_ptr: *c pub unsafe fn write(wr: &mut W, positive: bool, mut n: u64, exponent: i16) -> io::Result<()> { if !positive { - try!(wr.write_all(b"-")); + wr.write_all(b"-")?; } if n == 0 { @@ -180,12 +180,12 @@ pub unsafe fn write(wr: &mut W, positive: bool, mut n: u64, expone } // Write out the number with a fraction - try!(wr.write_all( + wr.write_all( slice::from_raw_parts( buf_ptr.offset(curr), buf.len() - curr as usize ) - )); + )?; // Omit the 'e' notation for e == 0 if e == 0 { @@ -207,12 +207,12 @@ pub unsafe fn write(wr: &mut W, positive: bool, mut n: u64, expone // No need for `e` notation, just print out zeroes if (printed + exponent as usize) <= 20 { - try!(wr.write_all( + wr.write_all( slice::from_raw_parts( buf_ptr.offset(curr), buf.len() - curr as usize ) - )); + )?; return wr.write_all(&ZEROFILL[ .. exponent as usize]); } @@ -227,13 +227,13 @@ pub unsafe fn write(wr: &mut W, positive: bool, mut n: u64, expone e += (printed as u64) - 1; } - try!(wr.write_all( + wr.write_all( slice::from_raw_parts( buf_ptr.offset(curr), buf.len() - curr as usize ) - )); - try!(wr.write_all(b"e")); + )?; + wr.write_all(b"e")?; write(wr, true, e, 0) } @@ -243,4 +243,4 @@ fn safe_abs(x : i16) -> u16 { } else { i16::max_value() as u16 + 1u16 } -} \ No newline at end of file +} diff --git a/src/value/implements.rs b/src/value/implements.rs index 7d140c0..7833f7e 100644 --- a/src/value/implements.rs +++ b/src/value/implements.rs @@ -1,14 +1,12 @@ // This is a private module that contains `PartialEq` and `From` trait // implementations for `JsonValue`. -use std::collections::{ BTreeMap, HashMap }; -use std::mem; +use std::collections::{BTreeMap, HashMap}; -use short::{ self, Short }; -use number::Number; -use object::Object; - -use { JsonValue, Null }; +use crate::short::{self, Short}; +use crate::number::Number; +use crate::object::Object; +use crate::value::JsonValue; macro_rules! implement_eq { ($to:ident, $from:ty) => { @@ -83,41 +81,25 @@ impl> From> for JsonValue { impl> From> for JsonValue { fn from(val: Vec) -> JsonValue { - let mut array = Vec::with_capacity(val.len()); - - for val in val { - array.push(val.into()); - } - - JsonValue::Array(array) + JsonValue::Array(val.into_iter().map(Into::into).collect()) } } -impl From> for JsonValue { - fn from(mut val: HashMap) -> JsonValue { - let mut object = Object::with_capacity(val.len()); - - for (key, value) in val.drain() { - object.insert(&key, value); - } - - JsonValue::Object(object) +impl<'a, T: Into + Clone> From<&'a [T]> for JsonValue { + fn from(val: &'a [T]) -> JsonValue { + JsonValue::Array(val.iter().cloned().map(Into::into).collect()) } } -impl From> for JsonValue { - fn from(mut val: BTreeMap) -> JsonValue { - let mut object = Object::with_capacity(val.len()); - - for (key, value) in val.iter_mut() { - // Since BTreeMap has no `drain` available, we can use - // the mutable iterator and replace all values by nulls, - // taking ownership and transferring it to the new `Object`. - let value = mem::replace(value, Null); - object.insert(key, value); - } +impl, V: Into> From> for JsonValue { + fn from(val: HashMap) -> JsonValue { + JsonValue::Object(val.into_iter().collect()) + } +} - JsonValue::Object(object) +impl, V: Into> From> for JsonValue { + fn from(val: BTreeMap) -> JsonValue { + JsonValue::Object(val.into_iter().collect()) } } diff --git a/src/value/mod.rs b/src/value/mod.rs index 866ff84..6fe59a8 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -1,14 +1,13 @@ -use { Result, Error }; +use std::ops::{Index, IndexMut, Deref}; +use std::{fmt, mem, usize, u8, u16, u32, u64, isize, i8, i16, i32, i64, f32}; +use std::io::{self, Write}; -use std::ops::{ Index, IndexMut, Deref }; -use std::{ fmt, mem, usize, u8, u16, u32, u64, isize, i8, i16, i32, i64, f32 }; -use std::io::{ self, Write }; - -use short::Short; -use number::Number; -use object::Object; -use iterators::{ Members, MembersMut, Entries, EntriesMut }; -use codegen::{ Generator, PrettyGenerator, DumpGenerator, WriterGenerator, PrettyWriterGenerator }; +use crate::{Result, Error}; +use crate::short::Short; +use crate::number::Number; +use crate::object::Object; +use crate::iterators::{ Members, MembersMut, Entries, EntriesMut }; +use crate::codegen::{ Generator, PrettyGenerator, DumpGenerator, WriterGenerator, PrettyWriterGenerator }; mod implements; @@ -483,8 +482,8 @@ impl JsonValue { } /// Works on `JsonValue::Object` - inserts a new entry, or override an existing - /// one into the object. Note that `key` has to be a `&str` slice and not an owned - /// `String`. The internals of `Object` will handle the heap allocation of the key + /// one into the object. Note that `key` has to be a `&str` slice and not an owned + /// `String`. The internals of `Object` will handle the heap allocation of the key /// if needed for better performance. pub fn insert(&mut self, key: &str, value: T) -> Result<()> where T: Into { diff --git a/tests/stringify.rs b/tests/stringify.rs index d70c39e..d2a8232 100644 --- a/tests/stringify.rs +++ b/tests/stringify.rs @@ -163,8 +163,8 @@ fn stringify_raw_object() { fn stringify_btree_map() { let mut map = BTreeMap::new(); - map.insert("name".into(), "Maciej".into()); - map.insert("age".into(), 30.into()); + map.insert("name", JsonValue::from("Maciej")); + map.insert("age", JsonValue::from(30)); // BTreeMap will sort keys assert_eq!(stringify(map), r#"{"age":30,"name":"Maciej"}"#); @@ -174,8 +174,8 @@ fn stringify_btree_map() { fn stringify_hash_map() { let mut map = HashMap::new(); - map.insert("name".into(), "Maciej".into()); - map.insert("age".into(), 30.into()); + map.insert("name", JsonValue::from("Maciej")); + map.insert("age", JsonValue::from(30)); // HashMap does not sort keys, but depending on hashing used the // order can be different. Safe bet is to parse the result and