Skip to content

Commit 958abbf

Browse files
committed
Support preserve_order in no_std builds
1 parent de85007 commit 958abbf

4 files changed

Lines changed: 25 additions & 12 deletions

File tree

.github/workflows/ci.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,18 @@ jobs:
6767
- run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc
6868
- run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc,arbitrary_precision
6969
- run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc,raw_value
70-
- run: cargo check --manifest-path tests/crate/Cargo.toml --features serde_json/preserve_order
70+
- run: cargo check --manifest-path tests/crate/Cargo.toml --features preserve_order
7171
if: matrix.rust != '1.71.0'
72-
- run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc,serde_json/preserve_order
72+
- run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features preserve_order
7373
if: matrix.rust != '1.71.0'
7474
- name: Build without std
7575
run: cargo check --manifest-path tests/crate/Cargo.toml --target
7676
${{matrix.target}} --no-default-features --features alloc
7777
if: matrix.target
78+
- name: Build without std with preserve_order
79+
run: cargo check --manifest-path tests/crate/Cargo.toml --target
80+
${{matrix.target}} --no-default-features --features alloc,preserve_order
81+
if: matrix.target
7882

7983
minimal:
8084
name: Minimal versions

Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ repository = "https://github.com/serde-rs/json"
1212
rust-version = "1.71"
1313

1414
[dependencies]
15-
indexmap = { version = "2.2.3", optional = true }
15+
indexmap = { version = "2.2.3", optional = true, default-features = false }
1616
itoa = "1.0"
1717
memchr = { version = "2", default-features = false }
18+
rustc-hash = { version = "2", optional = true, default-features = false }
1819
serde_core = { version = "1.0.220", default-features = false }
1920
zmij = "1.0"
2021

@@ -52,7 +53,7 @@ features = ["float_roundtrip", "raw_value", "unbounded_depth"]
5253
[features]
5354
default = ["std"]
5455

55-
std = ["memchr/std", "serde_core/std"]
56+
std = ["memchr/std", "serde_core/std", "indexmap?/std"]
5657

5758
# Provide integration for heap-allocated collections without depending on the
5859
# rest of the Rust standard library.
@@ -62,7 +63,7 @@ alloc = ["serde_core/alloc"]
6263
# Make serde_json::Map use a representation which maintains insertion order.
6364
# This allows data to be read into a Value and written back to a JSON string
6465
# while preserving the order of map keys in the input.
65-
preserve_order = ["indexmap", "std"]
66+
preserve_order = ["indexmap", "alloc", "dep:rustc-hash"]
6667

6768
# Use sufficient precision when parsing fixed precision floats from JSON to
6869
# ensure that they maintain accuracy when round-tripped through JSON. This comes

src/map.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,14 @@
33
//! By default the map is backed by a [`BTreeMap`]. Enable the `preserve_order`
44
//! feature of serde_json to use [`IndexMap`] instead.
55
//!
6+
//! In builds without the `std` feature, `preserve_order` hashes keys with a
7+
//! deterministic fixed-seed hasher, as no entropy source is available in
8+
//! core/alloc. Such builds are not resistant to HashDoS attacks. Builds with
9+
//! `std` use [`RandomState`] and are unaffected.
10+
//!
611
//! [`BTreeMap`]: std::collections::BTreeMap
712
//! [`IndexMap`]: indexmap::IndexMap
13+
//! [`RandomState`]: std::collections::hash_map::RandomState
814
915
use crate::error::Error;
1016
use crate::value::Value;
@@ -32,15 +38,17 @@ pub struct Map<K, V> {
3238

3339
#[cfg(not(feature = "preserve_order"))]
3440
type MapImpl<K, V> = BTreeMap<K, V>;
35-
#[cfg(feature = "preserve_order")]
41+
#[cfg(all(feature = "preserve_order", feature = "std"))]
3642
type MapImpl<K, V> = IndexMap<K, V>;
43+
#[cfg(all(feature = "preserve_order", not(feature = "std")))]
44+
type MapImpl<K, V> = IndexMap<K, V, rustc_hash::FxBuildHasher>;
3745

3846
impl Map<String, Value> {
3947
/// Makes a new empty Map.
4048
#[inline]
4149
pub fn new() -> Self {
4250
Map {
43-
map: MapImpl::new(),
51+
map: MapImpl::default(),
4452
}
4553
}
4654

@@ -54,8 +62,10 @@ impl Map<String, Value> {
5462
let _ = capacity;
5563
BTreeMap::new()
5664
},
57-
#[cfg(feature = "preserve_order")]
65+
#[cfg(all(feature = "preserve_order", feature = "std"))]
5866
map: IndexMap::with_capacity(capacity),
67+
#[cfg(all(feature = "preserve_order", not(feature = "std")))]
68+
map: IndexMap::with_capacity_and_hasher(capacity, rustc_hash::FxBuildHasher),
5969
}
6070
}
6171

@@ -386,9 +396,7 @@ impl Map<String, Value> {
386396
impl Default for Map<String, Value> {
387397
#[inline]
388398
fn default() -> Self {
389-
Map {
390-
map: MapImpl::new(),
391-
}
399+
Map::new()
392400
}
393401
}
394402

tests/crate/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ serde_json = { path = "../..", default-features = false }
1515
default = ["std"]
1616
std = ["serde_json/std"]
1717
alloc = ["serde_json/alloc"]
18-
#preserve_order = ["serde_json/preserve_order"]
18+
preserve_order = ["serde_json/preserve_order"]
1919
float_roundtrip = ["serde_json/float_roundtrip"]
2020
arbitrary_precision = ["serde_json/arbitrary_precision"]
2121
raw_value = ["serde_json/raw_value"]

0 commit comments

Comments
 (0)