From 7cd011f327b2d10a4cc623063e9b603a9c7e0e5b Mon Sep 17 00:00:00 2001 From: knickish Date: Wed, 29 May 2024 21:13:16 -0500 Subject: [PATCH] remove no_std hashbrown dep (#104) --- Cargo.toml | 3 +- derive/Cargo.toml | 5 +--- src/serde_bin.rs | 63 ++++++++++++++++++++++++++++----------- src/serde_json.rs | 75 ++++++++++++++++++++++++++++++++++++----------- src/serde_ron.rs | 70 ++++++++++++++++++++++++++++++++----------- src/toml.rs | 26 +++++++--------- tests/bin.rs | 26 +++++++--------- tests/json.rs | 16 +++++----- tests/ron.rs | 16 +++++----- tests/ser_de.rs | 13 ++++---- tests/toml.rs | 8 ++--- 11 files changed, 203 insertions(+), 118 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 31233b0..ff100ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,8 +27,7 @@ json = ["dep:nanoserde-derive", "nanoserde-derive/json"] binary = ["dep:nanoserde-derive", "nanoserde-derive/binary"] ron = ["dep:nanoserde-derive", "nanoserde-derive/ron"] toml = [] -no_std = ["dep:hashbrown"] +no_std = [] [dependencies] -hashbrown = { version = "0.12.3", optional = true } nanoserde-derive = { path = "derive", version = "=0.2.0", optional = true } diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 30ea539..ceff924 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -14,7 +14,4 @@ default = [] json = [] binary = [] ron = [] -no_std = ["dep:hashbrown"] - -[dependencies] -hashbrown = { version = "0.12.3", optional = true } +no_std = [] diff --git a/src/serde_bin.rs b/src/serde_bin.rs index 93e0a1c..66c7fc1 100644 --- a/src/serde_bin.rs +++ b/src/serde_bin.rs @@ -1,18 +1,11 @@ use core::convert::TryInto; -use core::hash::Hash; use alloc::borrow::ToOwned; use alloc::boxed::Box; -use alloc::collections::{BTreeSet, LinkedList}; +use alloc::collections::{BTreeMap, BTreeSet, LinkedList}; use alloc::string::String; use alloc::vec::Vec; -#[cfg(feature = "no_std")] -use hashbrown::{HashMap, HashSet}; - -#[cfg(not(feature = "no_std"))] -use std::collections::{HashMap, HashSet}; - /// A trait for objects that can be serialized to binary. pub trait SerBin { /// Serialize Self to bytes. @@ -294,7 +287,8 @@ where } } -impl SerBin for HashSet +#[cfg(not(feature = "no_std"))] +impl SerBin for std::collections::HashSet where T: SerBin, { @@ -307,13 +301,14 @@ where } } -impl DeBin for HashSet +#[cfg(not(feature = "no_std"))] +impl DeBin for std::collections::HashSet where - T: DeBin + Hash + Eq, + T: DeBin + core::hash::Hash + Eq, { - fn de_bin(o: &mut usize, d: &[u8]) -> Result, DeBinErr> { + fn de_bin(o: &mut usize, d: &[u8]) -> Result { let len: usize = DeBin::de_bin(o, d)?; - let mut out = HashSet::with_capacity(len); + let mut out = std::collections::HashSet::with_capacity(len); for _ in 0..len { out.insert(DeBin::de_bin(o, d)?); } @@ -534,7 +529,41 @@ where } } -impl SerBin for HashMap +#[cfg(not(feature = "no_std"))] +impl SerBin for std::collections::HashMap +where + K: SerBin, + V: SerBin, +{ + fn ser_bin(&self, s: &mut Vec) { + let len = self.len(); + len.ser_bin(s); + for (k, v) in self { + k.ser_bin(s); + v.ser_bin(s); + } + } +} + +#[cfg(not(feature = "no_std"))] +impl DeBin for std::collections::HashMap +where + K: DeBin + core::cmp::Eq + core::hash::Hash, + V: DeBin, +{ + fn de_bin(o: &mut usize, d: &[u8]) -> Result { + let len: usize = DeBin::de_bin(o, d)?; + let mut h = std::collections::HashMap::with_capacity(len); + for _ in 0..len { + let k = DeBin::de_bin(o, d)?; + let v = DeBin::de_bin(o, d)?; + h.insert(k, v); + } + Ok(h) + } +} + +impl SerBin for BTreeMap where K: SerBin, V: SerBin, @@ -549,14 +578,14 @@ where } } -impl DeBin for HashMap +impl DeBin for BTreeMap where - K: DeBin + core::cmp::Eq + Hash, + K: DeBin + core::cmp::Eq + Ord, V: DeBin, { fn de_bin(o: &mut usize, d: &[u8]) -> Result { let len: usize = DeBin::de_bin(o, d)?; - let mut h = HashMap::with_capacity(len); + let mut h = BTreeMap::new(); for _ in 0..len { let k = DeBin::de_bin(o, d)?; let v = DeBin::de_bin(o, d)?; diff --git a/src/serde_json.rs b/src/serde_json.rs index 6f3139e..47ceef0 100644 --- a/src/serde_json.rs +++ b/src/serde_json.rs @@ -1,18 +1,11 @@ -use core::hash::Hash; use core::str::Chars; use alloc::boxed::Box; -use alloc::collections::{BTreeSet, LinkedList}; +use alloc::collections::{BTreeMap, BTreeSet, LinkedList}; use alloc::format; use alloc::string::{String, ToString}; use alloc::vec::Vec; -#[cfg(feature = "no_std")] -use hashbrown::{HashMap, HashSet}; - -#[cfg(not(feature = "no_std"))] -use std::collections::{HashMap, HashSet}; - /// The internal state of a JSON serialization. pub struct SerJsonState { pub out: String, @@ -885,7 +878,8 @@ where } } -impl SerJson for HashSet +#[cfg(not(feature = "no_std"))] +impl SerJson for std::collections::HashSet where T: SerJson, { @@ -905,12 +899,13 @@ where } } -impl DeJson for HashSet +#[cfg(not(feature = "no_std"))] +impl DeJson for std::collections::HashSet where - T: DeJson + Hash + Eq, + T: DeJson + core::hash::Hash + Eq, { - fn de_json(s: &mut DeJsonState, i: &mut Chars) -> Result, DeJsonErr> { - let mut out = HashSet::new(); + fn de_json(s: &mut DeJsonState, i: &mut Chars) -> Result { + let mut out = std::collections::HashSet::new(); s.block_open(i)?; while s.tok != DeJsonTok::BlockClose { @@ -1177,7 +1172,53 @@ where } } -impl SerJson for HashMap +#[cfg(not(feature = "no_std"))] +impl SerJson for std::collections::HashMap +where + K: SerJson, + V: SerJson, +{ + fn ser_json(&self, d: usize, s: &mut SerJsonState) { + s.out.push('{'); + let len = self.len(); + let mut index = 0; + for (k, v) in self { + s.indent(d + 1); + k.ser_json(d + 1, s); + s.out.push(':'); + v.ser_json(d + 1, s); + if (index + 1) < len { + s.conl(); + } + index += 1; + } + s.indent(d); + s.out.push('}'); + } +} + +#[cfg(not(feature = "no_std"))] +impl DeJson for std::collections::HashMap +where + K: DeJson + Eq + core::hash::Hash, + V: DeJson, +{ + fn de_json(s: &mut DeJsonState, i: &mut Chars) -> Result { + let mut h = std::collections::HashMap::new(); + s.curly_open(i)?; + while s.tok != DeJsonTok::CurlyClose { + let k = DeJson::de_json(s, i)?; + s.colon(i)?; + let v = DeJson::de_json(s, i)?; + s.eat_comma_curly(i)?; + h.insert(k, v); + } + s.curly_close(i)?; + Ok(h) + } +} + +impl SerJson for BTreeMap where K: SerJson, V: SerJson, @@ -1201,13 +1242,13 @@ where } } -impl DeJson for HashMap +impl DeJson for BTreeMap where - K: DeJson + Eq + Hash, + K: DeJson + Eq + Ord, V: DeJson, { fn de_json(s: &mut DeJsonState, i: &mut Chars) -> Result { - let mut h = HashMap::new(); + let mut h = BTreeMap::new(); s.curly_open(i)?; while s.tok != DeJsonTok::CurlyClose { let k = DeJson::de_json(s, i)?; diff --git a/src/serde_ron.rs b/src/serde_ron.rs index ba7b049..ce8b954 100644 --- a/src/serde_ron.rs +++ b/src/serde_ron.rs @@ -1,18 +1,11 @@ -use core::hash::Hash; use core::str::Chars; use alloc::boxed::Box; -use alloc::collections::{BTreeSet, LinkedList}; +use alloc::collections::{BTreeMap, BTreeSet, LinkedList}; use alloc::format; use alloc::string::{String, ToString}; use alloc::vec::Vec; -#[cfg(feature = "no_std")] -use hashbrown::{HashMap, HashSet}; - -#[cfg(not(feature = "no_std"))] -use std::collections::{HashMap, HashSet}; - /// The internal state of a RON serialization. pub struct SerRonState { pub out: String, @@ -883,7 +876,8 @@ where } } -impl SerRon for HashSet +#[cfg(not(feature = "no_std"))] +impl SerRon for std::collections::HashSet where T: SerRon, { @@ -903,12 +897,13 @@ where } } -impl DeRon for HashSet +#[cfg(not(feature = "no_std"))] +impl DeRon for std::collections::HashSet where - T: DeRon + Hash + Eq, + T: DeRon + core::hash::Hash + Eq, { - fn de_ron(s: &mut DeRonState, i: &mut Chars) -> Result, DeRonErr> { - let mut out = HashSet::new(); + fn de_ron(s: &mut DeRonState, i: &mut Chars) -> Result { + let mut out = std::collections::HashSet::new(); s.block_open(i)?; while s.tok != DeRonTok::BlockClose { @@ -1189,7 +1184,48 @@ where } } -impl SerRon for HashMap +#[cfg(not(feature = "no_std"))] +impl SerRon for std::collections::HashMap +where + K: SerRon, + V: SerRon, +{ + fn ser_ron(&self, d: usize, s: &mut SerRonState) { + s.out.push_str("{\n"); + for (k, v) in self { + s.indent(d + 1); + k.ser_ron(d + 1, s); + s.out.push_str(":"); + v.ser_ron(d + 1, s); + s.conl(); + } + s.indent(d); + s.out.push('}'); + } +} + +#[cfg(not(feature = "no_std"))] +impl DeRon for std::collections::HashMap +where + K: DeRon + Eq + core::hash::Hash, + V: DeRon, +{ + fn de_ron(s: &mut DeRonState, i: &mut Chars) -> Result { + let mut h = std::collections::HashMap::new(); + s.curly_open(i)?; + while s.tok != DeRonTok::CurlyClose { + let k = DeRon::de_ron(s, i)?; + s.colon(i)?; + let v = DeRon::de_ron(s, i)?; + s.eat_comma_curly(i)?; + h.insert(k, v); + } + s.curly_close(i)?; + Ok(h) + } +} + +impl SerRon for BTreeMap where K: SerRon, V: SerRon, @@ -1208,13 +1244,13 @@ where } } -impl DeRon for HashMap +impl DeRon for BTreeMap where - K: DeRon + Eq + Hash, + K: DeRon + Eq + Ord, V: DeRon, { fn de_ron(s: &mut DeRonState, i: &mut Chars) -> Result { - let mut h = HashMap::new(); + let mut h = BTreeMap::new(); s.curly_open(i)?; while s.tok != DeRonTok::CurlyClose { let k = DeRon::de_ron(s, i)?; diff --git a/src/toml.rs b/src/toml.rs index bfe4843..3073e02 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -2,13 +2,7 @@ use core::str::Chars; use alloc::format; use alloc::string::{String, ToString}; -use alloc::{vec, vec::Vec}; - -#[cfg(feature = "no_std")] -use hashbrown::HashMap; - -#[cfg(not(feature = "no_std"))] -use std::collections::HashMap; +use alloc::{collections::BTreeMap, vec, vec::Vec}; /// Pattern matching any valid unquoted key character as u32. /// ABNF line: https://github.com/toml-lang/toml/blob/2431aa308a7bc97eeb50673748606e23a6e0f201/toml.abnf#L55 @@ -121,12 +115,12 @@ pub enum Toml { Bool(bool), Num(f64), Date(String), - Array(Vec>), + Array(Vec>), SimpleArray(Vec), } impl core::ops::Index for Toml { - type Output = HashMap; + type Output = BTreeMap; fn index(&self, index: usize) -> &Self::Output { match self { @@ -178,7 +172,7 @@ impl Toml { /// Get the TOML value as a table /// /// Panics if the TOML value isn't actually a table - pub fn arr(&self) -> &Vec> { + pub fn arr(&self) -> &Vec> { match self { Toml::Array(array) => array, _ => panic!(), @@ -222,7 +216,7 @@ impl core::fmt::Display for TomlErr { } struct Out { - out: HashMap, + out: BTreeMap, active_array_element: Option<(String, usize)>, } impl Out { @@ -234,7 +228,7 @@ impl Out { let n = match self.out.get_mut(key).unwrap() { Toml::Array(array) => { let n = array.len(); - array.push(HashMap::new()); + array.push(BTreeMap::new()); n } _ => unreachable!(), @@ -243,7 +237,7 @@ impl Out { self.active_array_element = Some((key.to_string(), n)); } - fn out(&mut self) -> &mut HashMap { + fn out(&mut self) -> &mut BTreeMap { if let Some((table, n)) = self.active_array_element.clone() { match self.out.get_mut(&table).unwrap() { Toml::Array(array) => &mut array[n], @@ -263,12 +257,12 @@ impl std::error::Error for TomlErr {} impl TomlParser { /// Parse a TOML string. - pub fn parse(data: &str) -> Result, TomlErr> { + pub fn parse(data: &str) -> Result, TomlErr> { let i = &mut data.chars(); let mut t = TomlParser::default(); t.next(i); let mut out = Out { - out: HashMap::new(), + out: BTreeMap::new(), active_array_element: None, }; let mut local_scope = String::new(); @@ -377,7 +371,7 @@ impl TomlParser { local_scope: &String, key: String, i: &mut Chars, - out: &mut HashMap, + out: &mut BTreeMap, ) -> Result<(), TomlErr> { let tok = self.next_tok(i)?; if tok != TomlTok::Equals { diff --git a/tests/bin.rs b/tests/bin.rs index 27be065..3a3371d 100644 --- a/tests/bin.rs +++ b/tests/bin.rs @@ -1,14 +1,10 @@ #![cfg(feature = "binary")] -use std::{ - array, - collections::{BTreeSet, LinkedList}, - sync::atomic::AtomicBool, -}; -#[cfg(feature = "no_std")] -use hashbrown::{HashMap, HashSet}; -#[cfg(not(feature = "no_std"))] -use std::collections::{HashMap, HashSet}; +extern crate alloc; + +use std::{array, sync::atomic::AtomicBool}; + +use alloc::collections::{BTreeMap, BTreeSet, LinkedList}; use nanoserde::{DeBin, SerBin}; @@ -39,16 +35,16 @@ fn binary() { #[test] fn binary_generics() { #[derive(DeBin, SerBin, PartialEq)] - struct TestGenericsWSkip + struct TestGenericsWSkip where - EE: Eq + std::hash::Hash, + EE: Eq + Ord, { test1: A, test2: B, test3: C, #[nserde(skip)] - test4: HashMap, - test5: HashMap, + test4: BTreeMap, + test5: BTreeMap, } let test: TestGenericsWSkip, u128> = TestGenericsWSkip { @@ -255,14 +251,14 @@ fn collections() { pub struct Test { pub a: Vec, pub b: LinkedList, - pub c: HashSet, + pub c: BTreeMap, pub d: BTreeSet, } let test: Test = Test { a: vec![1, 2, 3], b: vec![1.0, 2.0, 3.0, 4.0].into_iter().collect(), - c: vec![1, 2, 3, 4, 5].into_iter().collect(), + c: vec![(1, 2), (3, 4)].into_iter().collect(), d: vec![1, 2, 3, 4, 5, 6].into_iter().collect(), }; diff --git a/tests/json.rs b/tests/json.rs index 5275b6f..5aa14d1 100644 --- a/tests/json.rs +++ b/tests/json.rs @@ -2,15 +2,12 @@ use nanoserde::{DeJson, SerJson}; use std::{ - collections::{BTreeSet, LinkedList}, + collections::{BTreeMap, BTreeSet, LinkedList}, sync::atomic::AtomicBool, }; -#[cfg(feature = "no_std")] -use hashbrown::{HashMap, HashSet}; - #[cfg(not(feature = "no_std"))] -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; #[test] fn de() { @@ -403,11 +400,11 @@ fn one_field() { fn one_field_map() { #[derive(DeJson, SerJson, PartialEq)] pub struct OneField { - field: HashMap, + field: BTreeMap, } let test = OneField { - field: HashMap::new(), + field: BTreeMap::new(), }; let bytes = SerJson::serialize_json(&test); let test_deserialized = DeJson::deserialize_json(&bytes).unwrap(); @@ -470,6 +467,7 @@ fn path_type() { assert_eq!(bar.d, Some(vec![vec![1, 2], vec![3, 4]])); } +#[cfg(not(feature = "no_std"))] #[test] fn hashmaps() { #[derive(DeJson)] @@ -533,14 +531,14 @@ fn collections() { pub struct Test { pub a: Vec, pub b: LinkedList, - pub c: HashSet, + pub c: BTreeMap, pub d: BTreeSet, } let test: Test = Test { a: vec![1, 2, 3], b: vec![1.0, 2.0, 3.0, 4.0].into_iter().collect(), - c: vec![1, 2, 3, 4, 5].into_iter().collect(), + c: vec![(1, 2), (3, 4)].into_iter().collect(), d: vec![1, 2, 3, 4, 5, 6].into_iter().collect(), }; diff --git a/tests/ron.rs b/tests/ron.rs index 856a978..df8395e 100644 --- a/tests/ron.rs +++ b/tests/ron.rs @@ -2,15 +2,12 @@ use nanoserde::{DeRon, SerRon}; use std::{ - collections::{BTreeSet, LinkedList}, + collections::{BTreeMap, BTreeSet, LinkedList}, sync::atomic::AtomicBool, }; -#[cfg(feature = "no_std")] -use hashbrown::{HashMap, HashSet}; - #[cfg(not(feature = "no_std"))] -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; #[test] fn ron_de() { @@ -175,11 +172,11 @@ fn one_field() { fn one_field_map() { #[derive(DeRon, SerRon, PartialEq)] pub struct OneField { - field: HashMap, + field: BTreeMap, } let test = OneField { - field: HashMap::new(), + field: BTreeMap::new(), }; let bytes = SerRon::serialize_ron(&test); let test_deserialized = DeRon::deserialize_ron(&bytes).unwrap(); @@ -223,14 +220,14 @@ fn collections() { pub struct Test { pub a: Vec, pub b: LinkedList, - pub c: HashSet, + pub c: BTreeMap, pub d: BTreeSet, } let test: Test = Test { a: vec![1, 2, 3], b: vec![1.0, 2.0, 3.0, 4.0].into_iter().collect(), - c: vec![1, 2, 3, 4, 5].into_iter().collect(), + c: vec![(1, 2), (3, 4)].into_iter().collect(), d: vec![1, 2, 3, 4, 5, 6].into_iter().collect(), }; @@ -266,6 +263,7 @@ fn path_type() { assert_eq!(bar.d, Some(vec![vec![1, 2], vec![3, 4]])); } +#[cfg(not(feature = "no_std"))] #[test] fn hashmaps() { #[derive(DeRon)] diff --git a/tests/ser_de.rs b/tests/ser_de.rs index 4c37964..0509b0c 100644 --- a/tests/ser_de.rs +++ b/tests/ser_de.rs @@ -1,4 +1,7 @@ #![cfg(any(feature = "binary", feature = "json", feature = "ron"))] + +extern crate alloc; + #[cfg(feature = "binary")] use nanoserde::{DeBin, SerBin}; #[cfg(feature = "json")] @@ -6,11 +9,7 @@ use nanoserde::{DeJson, SerJson}; #[cfg(feature = "ron")] use nanoserde::{DeRon, SerRon}; -#[cfg(feature = "no_std")] -use hashbrown::HashMap; - -#[cfg(not(feature = "no_std"))] -use std::collections::HashMap; +use alloc::collections::BTreeMap; #[test] fn ser_de() { @@ -23,12 +22,12 @@ fn ser_de() { pub b: f32, c: Option, d: Option, - e: Option>, + e: Option>, f: Option<([u32; 4], String)>, g: (), } - let mut map = HashMap::new(); + let mut map = BTreeMap::new(); map.insert("a".to_string(), "b".to_string()); let test: Test = Test { diff --git a/tests/toml.rs b/tests/toml.rs index c7c3c39..05389a3 100644 --- a/tests/toml.rs +++ b/tests/toml.rs @@ -1,10 +1,8 @@ #![cfg(feature = "toml")] -#[cfg(feature = "no_std")] -use hashbrown::HashMap; -#[cfg(not(feature = "no_std"))] -use std::collections::HashMap; +extern crate alloc; +use alloc::collections::BTreeMap; use nanoserde::Toml; use nanoserde::TomlParser; @@ -104,7 +102,7 @@ fn toml_key_chars() { assert_eq!( TomlParser::parse(toml_str).unwrap(), - HashMap::from([ + BTreeMap::from([ ( "foo.bar.baz.123abc456def".to_string(), Toml::Str("myval".to_string())