Skip to content

Commit 18a1c88

Browse files
committed
feat: use serialize to provide Writable trait for ordered types
1 parent 66bdf3d commit 18a1c88

File tree

13 files changed

+2619
-185
lines changed

13 files changed

+2619
-185
lines changed

src/serde/value/borrowed/ordered/se.rs

Lines changed: 798 additions & 0 deletions
Large diffs are not rendered by default.

src/serde/value/owned/ordered/se.rs

Lines changed: 749 additions & 0 deletions
Large diffs are not rendered by default.

src/value.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ pub use self::borrowed::{
6363
Value as BorrowedValue, to_value as to_borrowed_value,
6464
to_value_with_buffers as to_borrowed_value_with_buffers,
6565
};
66-
#[cfg(feature = "preserve_ordered")]
66+
#[cfg(feature = "preserve_order")]
6767
pub use self::borrowed::ordered::{
6868
Value as OrderedBorrowedValue, to_value as to_ordered_borrowed_value,
6969
to_value_with_buffers as to_ordered_borrowed_value_with_buffers,
@@ -72,7 +72,7 @@ pub use self::owned::{
7272
Value as OwnedValue, to_value as to_owned_value,
7373
to_value_with_buffers as to_owned_value_with_buffers,
7474
};
75-
#[cfg(feature = "preserve_ordered")]
75+
#[cfg(feature = "preserve_order")]
7676
pub use self::owned::ordered::{
7777
Value as OrderedOwnedValue, to_value as to_ordered_owned_value,
7878
to_value_with_buffers as to_ordered_owned_value_with_buffers,

src/value/borrowed.rs

Lines changed: 4 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -428,11 +428,11 @@ impl<'de> BorrowDeserializer<'de> {
428428
// element so we eat this
429429
for _ in 0..len {
430430
if let Node::String(key) = unsafe { self.0.next_() } {
431-
#[cfg(all(not(feature = "value-no-dup-keys"), not(feature = "preserve_order")))]
431+
#[cfg(not(feature = "value-no-dup-keys"))]
432432
unsafe {
433433
res.insert_nocheck(key.into(), self.parse());
434434
};
435-
#[cfg(any(feature = "value-no-dup-keys", feature = "preserve_order"))]
435+
#[cfg(feature = "value-no-dup-keys")]
436436
res.insert(key.into(), self.parse());
437437
} else {
438438
unreachable!("parse_map: key not a string");
@@ -490,11 +490,11 @@ impl<'tape, 'de> BorrowSliceDeserializer<'tape, 'de> {
490490
// element so we eat this
491491
for _ in 0..len {
492492
if let Node::String(key) = unsafe { self.next_() } {
493-
#[cfg(all(not(feature = "value-no-dup-keys"), not(feature = "preserve_order")))]
493+
#[cfg(not(feature = "value-no-dup-keys"))]
494494
unsafe {
495495
res.insert_nocheck(key.into(), self.parse());
496496
};
497-
#[cfg(any(feature = "value-no-dup-keys", feature = "preserve_order"))]
497+
#[cfg(feature = "value-no-dup-keys")]
498498
res.insert(key.into(), self.parse());
499499
} else {
500500
unreachable!("parse_map: key needs to be a string");
@@ -1136,59 +1136,6 @@ mod test {
11361136
assert_eq!(result_keys, expected_keys);
11371137
}
11381138

1139-
#[cfg(feature = "preserve_order")]
1140-
#[test]
1141-
fn preserve_order_33_keys() {
1142-
// halfbrown uses Vec for <=32 keys, switches to HashMap at 33+
1143-
// This is where order gets lost without IndexMap
1144-
let keys: Vec<String> = (0..33).map(|i| format!("key_{}", i)).collect();
1145-
let json_pairs: Vec<String> = keys.iter().map(|k| format!(r#""{}": {}"#, k, 1)).collect();
1146-
let json = format!("{{{}}}", json_pairs.join(", "));
1147-
let mut input = json.into_bytes();
1148-
1149-
let v = to_value(input.as_mut_slice()).expect("valid JSON");
1150-
let obj = v.as_object().expect("is object");
1151-
let result_keys: Vec<&str> = obj.keys().map(|k| k.as_ref()).collect();
1152-
let expected_keys: Vec<&str> = keys.iter().map(|s| s.as_str()).collect();
1153-
1154-
assert_eq!(result_keys, expected_keys);
1155-
}
1156-
1157-
#[cfg(feature = "preserve_order")]
1158-
#[test]
1159-
fn preserve_order_50_keys() {
1160-
// Test well past the threshold
1161-
let keys: Vec<String> = (0..50).map(|i| format!("key_{}", i)).collect();
1162-
let json_pairs: Vec<String> = keys.iter().map(|k| format!(r#""{}": {}"#, k, 1)).collect();
1163-
let json = format!("{{{}}}", json_pairs.join(", "));
1164-
let mut input = json.into_bytes();
1165-
1166-
let v = to_value(input.as_mut_slice()).expect("valid JSON");
1167-
let obj = v.as_object().expect("is object");
1168-
let result_keys: Vec<&str> = obj.keys().map(|k| k.as_ref()).collect();
1169-
let expected_keys: Vec<&str> = keys.iter().map(|s| s.as_str()).collect();
1170-
1171-
assert_eq!(result_keys, expected_keys);
1172-
}
1173-
1174-
#[cfg(feature = "preserve_order")]
1175-
#[test]
1176-
fn preserve_order_roundtrip_33_keys() {
1177-
let keys: Vec<String> = (0..33).map(|i| format!("key_{}", i)).collect();
1178-
let json_pairs: Vec<String> = keys.iter().map(|k| format!(r#""{}": {}"#, k, 1)).collect();
1179-
let json = format!("{{{}}}", json_pairs.join(", "));
1180-
let mut input = json.into_bytes();
1181-
1182-
let v = to_value(input.as_mut_slice()).expect("valid JSON");
1183-
let mut serialized = v.encode();
1184-
let v2 = to_value(unsafe { serialized.as_bytes_mut() }).expect("valid JSON");
1185-
1186-
let keys1: Vec<&str> = v.as_object().unwrap().keys().map(|k| k.as_ref()).collect();
1187-
let keys2: Vec<&str> = v2.as_object().unwrap().keys().map(|k| k.as_ref()).collect();
1188-
1189-
assert_eq!(keys1, keys2);
1190-
}
1191-
11921139
// #[test]
11931140
// fn size() {
11941141
// assert_eq!(std::mem::size_of::<Value>(), 24);

src/value/borrowed/ordered.rs

Lines changed: 10 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,22 @@
2020
/// a["key"] = "value".into();
2121
/// assert_eq!(a["key"], "value");
2222
/// ```
23+
/// Partial equality comparison impls
24+
pub mod cmp;
25+
/// From converter impls
2326
pub mod from;
27+
/// Provide Writable trait
28+
pub mod serialize;
2429

2530
use crate::{Buffers, prelude::*};
26-
use crate::{Deserializer, Node, Result};
31+
use crate::{Deserializer, Node, ObjectHasher, Result};
2732
use crate::cow::Cow;
2833
use indexmap::IndexMap;
2934
use std::fmt;
3035
use std::ops::{Index, IndexMut};
3136

3237
/// Representation of a JSON object
33-
pub type Object<'value> = IndexMap<Cow<'value, str>, Value<'value>, std::hash::RandomState>;
38+
pub type Object<'value> = IndexMap<Cow<'value, str>, Value<'value>, ObjectHasher>;
3439

3540
/// Representation of a JSON array
3641
pub type Array<'value> = Vec<Value<'value>>;
@@ -74,7 +79,7 @@ pub fn to_value_with_buffers<'value>(
7479

7580
/// Borrowed JSON-DOM Value, consider using the `ValueTrait`
7681
/// to access its content
77-
#[derive(Debug, Clone, PartialEq)]
82+
#[derive(Debug, Clone)]
7883
#[cfg_attr(feature = "ordered-float", derive(Eq))]
7984
pub enum Value<'value> {
8085
/// Static values
@@ -175,7 +180,7 @@ impl<'value> ValueBuilder<'value> for Value<'value> {
175180
fn object_with_capacity(capacity: usize) -> Self {
176181
Self::Object(Box::new(Object::with_capacity_and_hasher(
177182
capacity,
178-
std::hash::RandomState::default(),
183+
ObjectHasher::default(),
179184
)))
180185
}
181186
}
@@ -421,7 +426,7 @@ impl<'de> BorrowDeserializer<'de> {
421426

422427
#[cfg_attr(not(feature = "no-inline"), inline)]
423428
fn parse_map(&mut self, len: usize) -> Value<'de> {
424-
let mut res = Object::with_capacity_and_hasher(len, std::hash::RandomState::default());
429+
let mut res = Object::with_capacity_and_hasher(len, ObjectHasher::default());
425430

426431
// Since we checked if it's empty we know that we at least have one
427432
// element so we eat this
@@ -931,43 +936,6 @@ mod test {
931936
assert_eq!(Value::default(), Value::null());
932937
}
933938

934-
#[cfg(not(target_arch = "wasm32"))]
935-
use proptest::prelude::*;
936-
937-
#[cfg(not(target_arch = "wasm32"))]
938-
fn arb_value() -> BoxedStrategy<Value<'static>> {
939-
let leaf = prop_oneof![
940-
Just(Value::Static(StaticNode::Null)),
941-
any::<bool>()
942-
.prop_map(StaticNode::Bool)
943-
.prop_map(Value::Static),
944-
any::<i64>()
945-
.prop_map(StaticNode::I64)
946-
.prop_map(Value::Static),
947-
any::<u64>()
948-
.prop_map(StaticNode::U64)
949-
.prop_map(Value::Static),
950-
any::<f64>()
951-
.prop_map(StaticNode::from)
952-
.prop_map(Value::Static),
953-
".*".prop_map(Value::from),
954-
];
955-
leaf.prop_recursive(
956-
8, // 8 levels deep
957-
256, // Shoot for maximum size of 256 nodes
958-
10, // We put up to 10 items per collection
959-
|inner| {
960-
prop_oneof![
961-
// Take the inner strategy and make the two recursive cases.
962-
prop::collection::vec(inner.clone(), 0..10).prop_map(Value::from),
963-
prop::collection::hash_map(".*".prop_map(Cow::from), inner, 0..10)
964-
.prop_map(|m| m.into_iter().collect()),
965-
]
966-
},
967-
)
968-
.boxed()
969-
}
970-
971939
#[test]
972940
fn preserve_order_33_keys() {
973941
// halfbrown uses Vec for <=32 keys, switches to HashMap at 33+

src/value/borrowed/ordered/cmp.rs

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
use super::Value;
2+
use crate::OrderedOwnedValue as OwnedValue;
3+
use crate::prelude::*;
4+
5+
#[allow(clippy::cast_sign_loss, clippy::default_trait_access)]
6+
impl PartialEq for Value<'_> {
7+
#[cfg_attr(not(feature = "no-inline"), inline)]
8+
fn eq(&self, other: &Self) -> bool {
9+
match (self, other) {
10+
(Self::Static(s1), Self::Static(s2)) => s1 == s2,
11+
(Self::String(v1), Self::String(v2)) => v1.eq(v2),
12+
(Self::Array(v1), Self::Array(v2)) => v1.eq(v2),
13+
(Self::Object(v1), Self::Object(v2)) => v1.eq(v2),
14+
_ => false,
15+
}
16+
}
17+
}
18+
19+
impl<'value, T> PartialEq<&T> for Value<'value>
20+
where
21+
Value<'value>: PartialEq<T>,
22+
{
23+
#[cfg_attr(not(feature = "no-inline"), inline)]
24+
fn eq(&self, other: &&T) -> bool {
25+
self == *other
26+
}
27+
}
28+
29+
impl PartialEq<OwnedValue> for Value<'_> {
30+
#[cfg_attr(not(feature = "no-inline"), inline)]
31+
fn eq(&self, other: &OwnedValue) -> bool {
32+
// We only need to implement this once
33+
other.eq(self)
34+
}
35+
}
36+
37+
impl PartialEq<()> for Value<'_> {
38+
#[cfg_attr(not(feature = "no-inline"), inline)]
39+
fn eq(&self, _other: &()) -> bool {
40+
self.is_null()
41+
}
42+
}
43+
44+
impl PartialEq<bool> for Value<'_> {
45+
#[cfg_attr(not(feature = "no-inline"), inline)]
46+
fn eq(&self, other: &bool) -> bool {
47+
self.as_bool().is_some_and(|t| t.eq(other))
48+
}
49+
}
50+
51+
impl PartialEq<str> for Value<'_> {
52+
#[cfg_attr(not(feature = "no-inline"), inline)]
53+
fn eq(&self, other: &str) -> bool {
54+
self.as_str().is_some_and(|t| t.eq(other))
55+
}
56+
}
57+
58+
impl PartialEq<&str> for Value<'_> {
59+
#[cfg_attr(not(feature = "no-inline"), inline)]
60+
fn eq(&self, other: &&str) -> bool {
61+
self == *other
62+
}
63+
}
64+
65+
impl PartialEq<String> for Value<'_> {
66+
#[cfg_attr(not(feature = "no-inline"), inline)]
67+
fn eq(&self, other: &String) -> bool {
68+
self.as_str().is_some_and(|t| t.eq(other))
69+
}
70+
}
71+
72+
impl PartialEq<i8> for Value<'_> {
73+
#[cfg_attr(not(feature = "no-inline"), inline)]
74+
fn eq(&self, other: &i8) -> bool {
75+
self.as_i8().is_some_and(|t| t.eq(other))
76+
}
77+
}
78+
79+
impl PartialEq<i16> for Value<'_> {
80+
#[cfg_attr(not(feature = "no-inline"), inline)]
81+
fn eq(&self, other: &i16) -> bool {
82+
self.as_i16().is_some_and(|t| t.eq(other))
83+
}
84+
}
85+
86+
impl PartialEq<i32> for Value<'_> {
87+
#[cfg_attr(not(feature = "no-inline"), inline)]
88+
fn eq(&self, other: &i32) -> bool {
89+
self.as_i32().is_some_and(|t| t.eq(other))
90+
}
91+
}
92+
93+
impl PartialEq<i64> for Value<'_> {
94+
#[cfg_attr(not(feature = "no-inline"), inline)]
95+
fn eq(&self, other: &i64) -> bool {
96+
self.as_i64().is_some_and(|t| t.eq(other))
97+
}
98+
}
99+
100+
impl PartialEq<i128> for Value<'_> {
101+
#[cfg_attr(not(feature = "no-inline"), inline)]
102+
fn eq(&self, other: &i128) -> bool {
103+
self.as_i128().is_some_and(|t| t.eq(other))
104+
}
105+
}
106+
107+
impl PartialEq<u8> for Value<'_> {
108+
#[cfg_attr(not(feature = "no-inline"), inline)]
109+
fn eq(&self, other: &u8) -> bool {
110+
self.as_u8().is_some_and(|t| t.eq(other))
111+
}
112+
}
113+
114+
impl PartialEq<u16> for Value<'_> {
115+
#[cfg_attr(not(feature = "no-inline"), inline)]
116+
fn eq(&self, other: &u16) -> bool {
117+
self.as_u16().is_some_and(|t| t.eq(other))
118+
}
119+
}
120+
121+
impl PartialEq<u32> for Value<'_> {
122+
#[cfg_attr(not(feature = "no-inline"), inline)]
123+
fn eq(&self, other: &u32) -> bool {
124+
self.as_u32().is_some_and(|t| t.eq(other))
125+
}
126+
}
127+
128+
impl PartialEq<u64> for Value<'_> {
129+
#[cfg_attr(not(feature = "no-inline"), inline)]
130+
fn eq(&self, other: &u64) -> bool {
131+
self.as_u64().is_some_and(|t| t.eq(other))
132+
}
133+
}
134+
135+
impl PartialEq<usize> for Value<'_> {
136+
#[cfg_attr(not(feature = "no-inline"), inline)]
137+
fn eq(&self, other: &usize) -> bool {
138+
self.as_usize().is_some_and(|t| t.eq(other))
139+
}
140+
}
141+
142+
impl PartialEq<u128> for Value<'_> {
143+
#[cfg_attr(not(feature = "no-inline"), inline)]
144+
fn eq(&self, other: &u128) -> bool {
145+
self.as_u128().is_some_and(|t| t.eq(other))
146+
}
147+
}
148+
149+
impl PartialEq<f32> for Value<'_> {
150+
#[cfg_attr(not(feature = "no-inline"), inline)]
151+
fn eq(&self, other: &f32) -> bool {
152+
self.as_f32().is_some_and(|t| t.eq(other))
153+
}
154+
}
155+
156+
impl PartialEq<f64> for Value<'_> {
157+
#[cfg_attr(not(feature = "no-inline"), inline)]
158+
fn eq(&self, other: &f64) -> bool {
159+
self.as_f64().is_some_and(|t| t.eq(other))
160+
}
161+
}
162+
163+
impl<'v, T> PartialEq<&[T]> for Value<'v>
164+
where
165+
Value<'v>: PartialEq<T>,
166+
{
167+
#[cfg_attr(not(feature = "no-inline"), inline)]
168+
fn eq(&self, other: &&[T]) -> bool {
169+
self.as_array().is_some_and(|t| t.eq(other))
170+
}
171+
}
172+
173+
impl<'v, K, T, S> PartialEq<std::collections::HashMap<K, T, S>> for Value<'v>
174+
where
175+
K: AsRef<str> + std::hash::Hash + Eq,
176+
Value<'v>: PartialEq<T>,
177+
S: std::hash::BuildHasher,
178+
{
179+
#[cfg_attr(not(feature = "no-inline"), inline)]
180+
fn eq(&self, other: &std::collections::HashMap<K, T, S>) -> bool {
181+
self.as_object().is_some_and(|object| {
182+
object.len() == other.len()
183+
&& other
184+
.iter()
185+
.all(|(key, value)| object.get(key.as_ref()).is_some_and(|v| *v == *value))
186+
})
187+
}
188+
}

0 commit comments

Comments
 (0)