Skip to content
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
20 changes: 20 additions & 0 deletions benches/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ fn citm_catalog_json_large_serde_value(b: &mut Bencher) {
bench_serde_value(b, &get_citm_catalog_json_large());
}

#[bench]
fn key_heavy_json_value(b: &mut Bencher) {
bench_value(b, &get_key_heavy_json());
}

#[bench]
fn tsconfig_json_ast(b: &mut Bencher) {
bench_ast(b, &get_tsconfig_json());
Expand Down Expand Up @@ -92,6 +97,21 @@ fn get_citm_catalog_json() -> String {
read_to_string("benches/data/citm_catalog.json").unwrap()
}

fn get_key_heavy_json() -> String {
// array of many small objects with short distinct string keys, to stress
// per-property object-key allocation on the value-building path
let mut result = String::new();
result.push('[');
for i in 0..20_000 {
if i > 0 {
result.push(',');
}
result.push_str(r#"{"id":1,"name":"abc","kind":"widget","enabled":true,"count":42,"tag":"x"}"#);
}
result.push(']');
result
}

fn get_tsconfig_json() -> String {
read_to_string("benches/data/tsconfig.json").unwrap()
}
Expand Down
6 changes: 3 additions & 3 deletions dprint.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
"./benches/json"
],
"plugins": [
"https://plugins.dprint.dev/markdown-0.19.0.wasm",
"https://plugins.dprint.dev/exec-0.6.0.json@a054130d458f124f9b5c91484833828950723a5af3f8ff2bd1523bd47b83b364",
"https://plugins.dprint.dev/json-0.20.0.wasm"
"https://plugins.dprint.dev/markdown-0.22.1.wasm",
"https://plugins.dprint.dev/exec-0.7.2.json@6c30bd00dfb176774ece412d0fbf149478269ecc92c55ab2d6f4116938ed993e",
"https://plugins.dprint.dev/json-0.22.0.wasm"
]
}
10 changes: 5 additions & 5 deletions src/parse_to_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ fn parse_object<'a>(parser: &mut JsoncParser<'a>) -> Result<JsonValue<'a>, Parse
None => break,
Some(key) => {
first = false;
let key_string = key.into_string();
let key_string = key.into_cow();
parser.scan_object_colon()?;
match parser.scan()? {
Some(value_token) => {
Expand Down Expand Up @@ -132,13 +132,13 @@ mod tests {
.unwrap();

let mut object_map = Map::new();
object_map.insert(String::from("a"), JsonValue::Null);
object_map.insert(Cow::Borrowed("a"), JsonValue::Null);
object_map.insert(
String::from("b"),
Cow::Borrowed("b"),
JsonValue::Array(vec![JsonValue::Null, JsonValue::String(Cow::Borrowed("text"))].into()),
);
object_map.insert(String::from("c"), JsonValue::Boolean(true));
object_map.insert(String::from("d"), JsonValue::Number("25.55"));
object_map.insert(Cow::Borrowed("c"), JsonValue::Boolean(true));
object_map.insert(Cow::Borrowed("d"), JsonValue::Number("25.55"));
assert_eq!(value, JsonValue::Object(object_map.into()));
}

Expand Down
9 changes: 9 additions & 0 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ impl<'a> ObjectKey<'a> {
ObjectKey::Word(s) => s.to_string(),
}
}

/// Converts the key into a `Cow`, borrowing from the source when possible
/// to avoid an allocation for clean (unescaped) keys.
pub fn into_cow(self) -> Cow<'a, str> {
match self {
ObjectKey::String(s) => s,
ObjectKey::Word(s) => Cow::Borrowed(s),
}
}
}

/// Shared JSONC parser infrastructure used by both `parse_to_value` and
Expand Down
26 changes: 13 additions & 13 deletions src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,35 +19,35 @@ pub type Map<K, V> = indexmap::IndexMap<K, V>;

/// A JSON object.
#[derive(Clone, PartialEq, Debug)]
pub struct JsonObject<'a>(Map<String, JsonValue<'a>>);
pub struct JsonObject<'a>(Map<Cow<'a, str>, JsonValue<'a>>);

impl<'a> IntoIterator for JsonObject<'a> {
type Item = (String, JsonValue<'a>);
type Item = (Cow<'a, str>, JsonValue<'a>);
#[cfg(not(feature = "preserve_order"))]
type IntoIter = std::collections::hash_map::IntoIter<String, JsonValue<'a>>;
type IntoIter = std::collections::hash_map::IntoIter<Cow<'a, str>, JsonValue<'a>>;
#[cfg(feature = "preserve_order")]
type IntoIter = indexmap::map::IntoIter<String, JsonValue<'a>>;
type IntoIter = indexmap::map::IntoIter<Cow<'a, str>, JsonValue<'a>>;

fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}

impl<'a> From<Map<String, JsonValue<'a>>> for JsonObject<'a> {
fn from(properties: Map<String, JsonValue>) -> JsonObject {
impl<'a> From<Map<Cow<'a, str>, JsonValue<'a>>> for JsonObject<'a> {
fn from(properties: Map<Cow<'a, str>, JsonValue<'a>>) -> JsonObject<'a> {
JsonObject::new(properties)
}
}

#[cfg(not(feature = "preserve_order"))]
#[inline(always)]
fn remove_entry<'a>(map: &mut Map<String, JsonValue<'a>>, key: &str) -> Option<(String, JsonValue<'a>)> {
fn remove_entry<'a>(map: &mut Map<Cow<'a, str>, JsonValue<'a>>, key: &str) -> Option<(Cow<'a, str>, JsonValue<'a>)> {
map.remove_entry(key)
}

#[cfg(feature = "preserve_order")]
#[inline(always)]
fn remove_entry<'a>(map: &mut Map<String, JsonValue<'a>>, key: &str) -> Option<(String, JsonValue<'a>)> {
fn remove_entry<'a>(map: &mut Map<Cow<'a, str>, JsonValue<'a>>, key: &str) -> Option<(Cow<'a, str>, JsonValue<'a>)> {
map.shift_remove_entry(key)
}

Expand Down Expand Up @@ -76,7 +76,7 @@ macro_rules! generate_get {

impl<'a> JsonObject<'a> {
/// Creates a new JsonObject.
pub fn new(inner: Map<String, JsonValue<'a>>) -> JsonObject<'a> {
pub fn new(inner: Map<Cow<'a, str>, JsonValue<'a>>) -> JsonObject<'a> {
JsonObject(inner)
}

Expand All @@ -86,7 +86,7 @@ impl<'a> JsonObject<'a> {
}

/// Drops the object returning the inner map.
pub fn take_inner(self) -> Map<String, JsonValue<'a>> {
pub fn take_inner(self) -> Map<Cow<'a, str>, JsonValue<'a>> {
self.0
}

Expand Down Expand Up @@ -231,8 +231,8 @@ mod test {
#[test]
fn it_should_take() {
let mut inner = Map::new();
inner.insert(String::from("prop"), JsonValue::String(Cow::Borrowed("asdf")));
inner.insert(String::from("other"), JsonValue::String(Cow::Borrowed("text")));
inner.insert(Cow::Borrowed("prop"), JsonValue::String(Cow::Borrowed("asdf")));
inner.insert(Cow::Borrowed("other"), JsonValue::String(Cow::Borrowed("text")));
let mut obj = JsonObject::new(inner);

assert_eq!(obj.len(), 2);
Expand All @@ -251,7 +251,7 @@ mod test {
#[test]
fn it_should_get() {
let mut inner = Map::new();
inner.insert(String::from("prop"), JsonValue::String(Cow::Borrowed("asdf")));
inner.insert(Cow::Borrowed("prop"), JsonValue::String(Cow::Borrowed("asdf")));
let obj = JsonObject::new(inner);

assert_eq!(obj.len(), 1);
Expand Down
Loading