Skip to content

Commit 094fdc1

Browse files
authored
Merge pull request #37 from PSeitz/add_docs
improve docs, bump version
2 parents a27d99a + 9c11ce1 commit 094fdc1

File tree

8 files changed

+115
-52
lines changed

8 files changed

+115
-52
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
0.8.0 (2025-06-15)
2+
==================
3+
perf: add CowStr type to be able to use Cow::Borrowed on keys https://github.com/PSeitz/serde_json_borrow/pull/32 (Thanks @jszwec)
4+
15
0.7.1 (2024-11-02)
26
==================
37
strip extra iteration when initialising ObjectAsVec https://github.com/PSeitz/serde_json_borrow/pull/29 (Thanks @meskill)

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "serde_json_borrow"
33
categories = ["parsing", "parser-implementations", "encoding"]
44
authors = ["Pascal Seitz <pascal.seitz@gmail.com>"]
55
description = "Provides JSON deserialization into a borrowed DOM"
6-
version = "0.7.1"
6+
version = "0.8.0"
77
edition = "2021"
88
license = "MIT"
99
keywords = ["JSON", "serde", "deserialization", "ref", "borrowed"]

README.md

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,27 +45,54 @@ List of _unsupported_ characters (https://www.json.org/json-en.html) in keys wit
4545
* hdfs -> log
4646
* wiki -> few keys with large text body
4747
* gh-archive -> highly nested object
48+
49+
Access benchmarks are done to show the relative overhead of accessing data on the resulting `Value` DOM, which
50+
is insignificant compared to the parsing time here.
51+
Since `serde_json_borrow` deserializes into a `Vec` instead of a `BTreeMap`.
52+
53+
In the benchmarks below it consistently outperforms `serde_json` and `simd-json`.
54+
Benchmarks are done on a AMD Ryzen 7 9800X3D on 6.14.6-2-MANJARO.
55+
56+
4857
```
58+
parse
4959
simple_json
50-
serde_json Avg: 139.29 MiB/s Median: 139.53 MiB/s [134.51 MiB/s .. 140.45 MiB/s]
51-
serde_json_borrow Avg: 210.33 MiB/s Median: 209.66 MiB/s [204.08 MiB/s .. 214.28 MiB/s]
52-
SIMD_json_borrow Avg: 140.36 MiB/s Median: 140.44 MiB/s [138.96 MiB/s .. 141.75 MiB/s]
60+
serde_json Avg: 297.30 MB/s Median: 296.71 MB/s [293.91 MB/s .. 312.10 MB/s]
61+
serde_json + access by key Avg: 297.43 MB/s Median: 296.54 MB/s [285.99 MB/s .. 306.42 MB/s]
62+
serde_json_borrow::OwnedValue Avg: 552.02 MB/s Median: 552.80 MB/s [538.45 MB/s .. 555.48 MB/s]
63+
serde_json_borrow::OwnedValue + access by key Avg: 535.23 MB/s Median: 535.60 MB/s [524.21 MB/s .. 537.36 MB/s]
64+
SIMD_json_borrow Avg: 296.12 MB/s Median: 296.68 MB/s [289.54 MB/s .. 297.27 MB/s]
5365
hdfs
54-
serde_json Avg: 284.64 MiB/s Median: 284.60 MiB/s [280.98 MiB/s .. 286.46 MiB/s]
55-
serde_json_borrow Avg: 372.99 MiB/s Median: 371.75 MiB/s [365.97 MiB/s .. 379.96 MiB/s]
56-
SIMD_json_borrow Avg: 294.41 MiB/s Median: 294.96 MiB/s [287.76 MiB/s .. 296.96 MiB/s]
66+
serde_json Avg: 688.77 MB/s Median: 690.78 MB/s [660.10 MB/s .. 698.89 MB/s]
67+
serde_json + access by key Avg: 675.24 MB/s Median: 675.85 MB/s [661.28 MB/s .. 683.70 MB/s]
68+
serde_json_borrow::OwnedValue Avg: 1.1158 GB/s Median: 1.1175 GB/s [1.0847 GB/s .. 1.1301 GB/s]
69+
serde_json_borrow::OwnedValue + access by key Avg: 1.1044 GB/s Median: 1.1085 GB/s [1.0040 GB/s .. 1.1262 GB/s]
70+
SIMD_json_borrow Avg: 687.72 MB/s Median: 688.76 MB/s [663.14 MB/s .. 700.18 MB/s]
5771
hdfs_with_array
58-
serde_json Avg: 194.50 MiB/s Median: 200.41 MiB/s [155.44 MiB/s .. 211.49 MiB/s]
59-
serde_json_borrow Avg: 275.01 MiB/s Median: 282.74 MiB/s [208.35 MiB/s .. 289.78 MiB/s]
60-
SIMD_json_borrow Avg: 206.34 MiB/s Median: 210.52 MiB/s [180.99 MiB/s .. 220.30 MiB/s]
72+
serde_json Avg: 468.18 MB/s Median: 466.53 MB/s [455.06 MB/s .. 486.57 MB/s]
73+
serde_json + access by key Avg: 478.47 MB/s Median: 478.35 MB/s [467.25 MB/s .. 494.33 MB/s]
74+
serde_json_borrow::OwnedValue Avg: 813.81 MB/s Median: 816.95 MB/s [770.63 MB/s .. 825.62 MB/s]
75+
serde_json_borrow::OwnedValue + access by key Avg: 821.20 MB/s Median: 824.86 MB/s [788.96 MB/s .. 833.78 MB/s]
76+
SIMD_json_borrow Avg: 536.68 MB/s Median: 538.81 MB/s [517.16 MB/s .. 545.30 MB/s]
6177
wiki
62-
serde_json Avg: 439.95 MiB/s Median: 441.28 MiB/s [429.97 MiB/s .. 444.82 MiB/s]
63-
serde_json_borrow Avg: 484.74 MiB/s Median: 485.29 MiB/s [471.38 MiB/s .. 489.16 MiB/s]
64-
SIMD_json_borrow Avg: 576.57 MiB/s Median: 578.11 MiB/s [554.03 MiB/s .. 586.18 MiB/s]
78+
serde_json Avg: 1.3004 GB/s Median: 1.3014 GB/s [1.2670 GB/s .. 1.3182 GB/s]
79+
serde_json + access by key Avg: 1.3521 GB/s Median: 1.3531 GB/s [1.3180 GB/s .. 1.3678 GB/s]
80+
serde_json_borrow::OwnedValue Avg: 1.5089 GB/s Median: 1.5072 GB/s [1.4898 GB/s .. 1.5289 GB/s]
81+
serde_json_borrow::OwnedValue + access by key Avg: 1.5656 GB/s Median: 1.5638 GB/s [1.5393 GB/s .. 1.5945 GB/s]
82+
SIMD_json_borrow Avg: 1.4824 GB/s Median: 1.4838 GB/s [1.4146 GB/s .. 1.5250 GB/s]
83+
gh-archive
84+
serde_json Avg: 451.29 MB/s Median: 451.74 MB/s [439.02 MB/s .. 455.40 MB/s]
85+
serde_json + access by key Avg: 453.74 MB/s Median: 454.52 MB/s [444.96 MB/s .. 457.46 MB/s]
86+
serde_json_borrow::OwnedValue Avg: 1.1181 GB/s Median: 1.1236 GB/s [1.0584 GB/s .. 1.1467 GB/s]
87+
serde_json_borrow::OwnedValue + access by key Avg: 1.1361 GB/s Median: 1.1416 GB/s [1.0744 GB/s .. 1.1611 GB/s]
88+
SIMD_json_borrow Avg: 992.99 MB/s Median: 995.00 MB/s [956.99 MB/s .. 1.0086 GB/s]
89+
access
90+
simple_json
91+
serde_json access Avg: 8.9616 GB/s Median: 8.9910 GB/s [8.5910 GB/s .. 9.0113 GB/s] Output: 7_002
92+
serde_json_borrow access Avg: 30.654 GB/s Median: 30.859 GB/s [29.639 GB/s .. 31.056 GB/s] Output: 7_002
6593
gh-archive
66-
serde_json Avg: 176.21 MiB/s Median: 176.37 MiB/s [172.52 MiB/s .. 177.78 MiB/s]
67-
serde_json_borrow Avg: 363.58 MiB/s Median: 364.02 MiB/s [355.28 MiB/s .. 374.10 MiB/s]
68-
SIMD_json_borrow Avg: 383.66 MiB/s Median: 386.94 MiB/s [363.80 MiB/s .. 400.25 MiB/s]
94+
serde_json access Avg: 15.686 GB/s Median: 15.729 GB/s [15.137 GB/s .. 15.843 GB/s] Output: 231_243
95+
serde_json_borrow access Avg: 35.535 GB/s Median: 35.575 GB/s [34.149 GB/s .. 37.598 GB/s] Output: 231_243
6996
7097
```
7198

benches/bench.rs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ fn lines_for_file(file: &str) -> impl Iterator<Item = String> {
1616
}
1717

1818
fn main() {
19-
access_bench();
2019
parse_bench();
20+
access_bench();
2121
}
2222

2323
fn parse_bench() {
@@ -68,7 +68,7 @@ fn parse_bench() {
6868
}
6969
black_box(total_size);
7070
});
71-
runner.register("serde_json_borrow", move |_data| {
71+
runner.register("serde_json_borrow::OwnedValue", move |_data| {
7272
let mut val = None;
7373
for line in input_gen() {
7474
let json: OwnedValue = OwnedValue::parse_from(line).unwrap();
@@ -77,14 +77,17 @@ fn parse_bench() {
7777
black_box(val);
7878
});
7979

80-
runner.register("serde_json_borrow + access by key", move |_data| {
81-
let mut total_size = 0;
82-
for line in input_gen() {
83-
let json: OwnedValue = OwnedValue::parse_from(line).unwrap();
84-
total_size += access_json_borrowed(&json, access);
85-
}
86-
black_box(total_size);
87-
});
80+
runner.register(
81+
"serde_json_borrow::OwnedValue + access by key",
82+
move |_data| {
83+
let mut total_size = 0;
84+
for line in input_gen() {
85+
let json: OwnedValue = OwnedValue::parse_from(line).unwrap();
86+
total_size += access_json_borrowed(&json, access);
87+
}
88+
black_box(total_size);
89+
},
90+
);
8891

8992
runner.register("SIMD_json_borrow", move |_data| {
9093
for line in input_gen() {

src/lib.rs

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,23 @@
2525
//! due to less allocations. By borrowing a DOM, the library ensures that no additional memory is
2626
//! allocated for `Strings`, that contain no JSON escape codes.
2727
//!
28+
//! # Usage
29+
//! ```rust
30+
//! use std::io;
31+
//! fn main() -> io::Result<()> {
32+
//! let data: &str = r#"{"bool": true, "key": "123"}"#;
33+
//! // Note that serde_json_borrow::Value<'ctx> is tied to the lifetime of data.
34+
//! let value: serde_json_borrow::Value = serde_json::from_str(&data)?;
35+
//! assert_eq!(value.get("bool"), &serde_json_borrow::Value::Bool(true));
36+
//! assert_eq!(value.get("key"), &serde_json_borrow::Value::Str("123".into()));
37+
//! // Using OwnedValue will take ownership of the String.
38+
//! let value: serde_json_borrow::OwnedValue = serde_json_borrow::OwnedValue::from_str(&data)?;
39+
//! assert_eq!(value.get("bool"), &serde_json_borrow::Value::Bool(true));
40+
//! assert_eq!(value.get("key"), &serde_json_borrow::Value::Str("123".into()));
41+
//! Ok(())
42+
//! }
43+
//! ```
44+
//!
2845
//! ## OwnedValue
2946
//! You can take advantage of [`OwnedValue`] to parse a `String` containing
3047
//! unparsed `JSON` into a `Value` without having to worry about lifetimes,
@@ -36,7 +53,7 @@
3653
//! support for escaped data in keys. Without the `cowkeys` feature flag `&str` is used, which does
3754
//! not allow any JSON escaping characters in keys.
3855
//!
39-
//! List of _unsupported_ characters (https://www.json.org/json-en.html) in object keys without `cowkeys` feature flag.
56+
//! List of _unsupported_ characters (<https://www.json.org/json-en.html>) in object keys without `cowkeys` feature flag.
4057
//!
4158
//! ```text
4259
//! \" represents the quotation mark character (U+0022).
@@ -48,18 +65,6 @@
4865
//! \r represents the carriage return character (U+000D).
4966
//! \t represents the character tabulation character (U+0009).
5067
//! ```
51-
//! # Usage
52-
//! ```rust
53-
//! use std::io;
54-
//! use serde_json_borrow::Value;
55-
//! fn main() -> io::Result<()> {
56-
//! let data = r#"{"bool": true, "key": "123"}"#;
57-
//! let value: Value = serde_json::from_str(&data)?;
58-
//! assert_eq!(value.get("bool"), &Value::Bool(true));
59-
//! assert_eq!(value.get("key"), &Value::Str("123".into()));
60-
//! Ok(())
61-
//! }
62-
//! ```
6368
//! # Performance
6469
//! Performance gain depends on how many allocations can be avoided, and how many objects there are,
6570
//! as deserializing into a vec is significantly faster.
@@ -76,13 +81,13 @@ mod deserializer;
7681
mod index;
7782
mod num;
7883
mod object_vec;
79-
mod owned;
84+
mod ownedvalue;
8085
mod ser;
8186
mod value;
8287

8388
#[cfg(feature = "cowkeys")]
8489
mod cowstr;
8590

8691
pub use object_vec::{KeyStrType, ObjectAsVec, ObjectAsVec as Map};
87-
pub use owned::OwnedValue;
92+
pub use ownedvalue::OwnedValue;
8893
pub use value::Value;

src/object_vec.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ use std::borrow::Cow;
66
use crate::Value;
77

88
#[cfg(feature = "cowkeys")]
9-
/// The string type used. Can be toggled between &str and Cow<str> via `cowstr` feature flag
9+
/// The string type used. Can be toggled between `&str` and `Cow<str>` via `cowstr` feature flag
1010
pub type KeyStrType<'a> = crate::cowstr::CowStr<'a>;
1111

1212
#[cfg(not(feature = "cowkeys"))]
13-
/// The string type used. Can be toggled between &str and Cow<str> via `cowstr` feature flag
13+
/// The string type used. Can be toggled between `&str` and `Cow<str>` via `cowstr` feature flag
1414
/// Cow strings
1515
pub type KeyStrType<'a> = &'a str;
1616

@@ -51,7 +51,7 @@ impl<'ctx> ObjectAsVec<'ctx> {
5151
/// # Note
5252
/// Since KeyStrType can be changed via a feature flag avoid using `as_vec` and use other
5353
/// methods instead. This could be a problem with feature unification, when one crate uses it
54-
/// as &str and another uses it as Cow<str>, both will get Cow<str?
54+
/// as `&str` and another uses it as `Cow<str>`, both will get `Cow<str>`
5555
#[inline]
5656
pub fn as_vec(&self) -> &Vec<(KeyStrType, Value<'ctx>)> {
5757
&self.0

src/owned.rs renamed to src/ownedvalue.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,26 @@ use std::ops::Deref;
33

44
use crate::Value;
55

6-
/// Parses a `String` into `Value`, by taking ownership of `String` and reference slices from it in
7-
/// contrast to copying the contents.
6+
/// Parses a `String` into `Value`, by taking ownership of `String` and reference slices from it.
7+
///
8+
/// With [`crate::Value`], your lifetime is tied to the lifetime of the
9+
/// passed `str`. This means that the `Value` can only be used as long as the original `str` is
10+
/// valid. With [`OwnedValue`], you get a owned Value instead.
11+
///
12+
/// Note: `OwnedValue` does not implement `Deserialize`, as it is not intended to be used for
13+
/// deserialization. It is designed to be used when you already have a `String` containing JSON
14+
/// data, and you want to parse it into a `Value` without worrying about lifetimes.
15+
///
16+
/// ## Example
17+
/// ```
18+
/// use serde_json_borrow::OwnedValue;
19+
/// use serde_json_borrow::Value;
20+
/// let raw_json = r#"{"name": "John", "age": 30}"#;
21+
/// let owned_value = OwnedValue::from_string(raw_json.to_string()).unwrap();
22+
/// assert_eq!(owned_value.get("name"), &Value::Str("John".into()));
23+
/// assert_eq!(owned_value.get("age"), &Value::Number(30_u64.into()));
24+
/// ```
825
///
9-
/// This is done to mitigate lifetime issues.
1026
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
1127
pub struct OwnedValue {
1228
/// Keep owned data, to be able to safely reference it from Value<'static>

src/ser.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
use serde::ser::{Serialize, Serializer};
22

33
use crate::num::{Number, N};
4-
use crate::owned::OwnedValue;
4+
use crate::ownedvalue::OwnedValue;
55
use crate::value::Value;
66
use crate::Map;
77

88
impl Serialize for Value<'_> {
99
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
10-
where S: Serializer {
10+
where
11+
S: Serializer,
12+
{
1113
match self {
1214
Value::Null => serializer.serialize_unit(),
1315
Value::Bool(b) => serializer.serialize_bool(*b),
@@ -20,21 +22,27 @@ impl Serialize for Value<'_> {
2022
}
2123
impl Serialize for Map<'_> {
2224
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
23-
where S: Serializer {
25+
where
26+
S: Serializer,
27+
{
2428
serializer.collect_map(self.iter())
2529
}
2630
}
2731

2832
impl Serialize for OwnedValue {
2933
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
30-
where S: Serializer {
34+
where
35+
S: Serializer,
36+
{
3137
Value::serialize(self.get_value(), serializer)
3238
}
3339
}
3440

3541
impl Serialize for Number {
3642
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
37-
where S: Serializer {
43+
where
44+
S: Serializer,
45+
{
3846
match self.n {
3947
N::PosInt(n) => serializer.serialize_u64(n),
4048
N::NegInt(n) => serializer.serialize_i64(n),

0 commit comments

Comments
 (0)