mirror of
https://github.com/italicsjenga/valence.git
synced 2024-12-23 14:31:30 +11:00
Replace HashMap with BTreeMap in valence_nbt (#98)
* Replace HashMap with BTreeMap in valence_nbt Turns out that `BTreeMap`s are a bit faster when the element count is low. This change also makes debugging compounds a bit easier since the elements are displayed in sorted order. * Simplify read_list function slightly
This commit is contained in:
parent
9c62bc1b90
commit
2cd8bd2195
|
@ -6,7 +6,7 @@ repository = "https://github.com/valence-rs/valence_nbt"
|
|||
readme = "README.md"
|
||||
license = "MIT"
|
||||
keywords = ["nbt", "minecraft", "serialization"]
|
||||
version = "0.1.2"
|
||||
version = "0.2.0"
|
||||
authors = ["Ryan Johnson <ryanj00a@gmail.com>"]
|
||||
edition = "2021"
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ pub struct Compound {
|
|||
}
|
||||
|
||||
#[cfg(not(feature = "preserve_order"))]
|
||||
type Map = std::collections::HashMap<String, Value>;
|
||||
type Map = std::collections::BTreeMap<String, Value>;
|
||||
|
||||
#[cfg(feature = "preserve_order")]
|
||||
type Map = indexmap::IndexMap<String, Value>;
|
||||
|
@ -24,6 +24,13 @@ impl Compound {
|
|||
|
||||
pub fn with_capacity(cap: usize) -> Self {
|
||||
Self {
|
||||
#[cfg(not(feature = "preserve_order"))]
|
||||
map: {
|
||||
// BTreeMap does not have with_capacity.
|
||||
let _ = cap;
|
||||
Map::new()
|
||||
},
|
||||
#[cfg(feature = "preserve_order")]
|
||||
map: Map::with_capacity(cap),
|
||||
}
|
||||
}
|
||||
|
@ -88,14 +95,22 @@ impl Compound {
|
|||
self.map.remove_entry(k)
|
||||
}
|
||||
|
||||
// TODO: append function for potential BTreeMap usage?
|
||||
pub fn append(&mut self, other: &mut Self) {
|
||||
#[cfg(not(feature = "preserve_order"))]
|
||||
self.map.append(&mut other.map);
|
||||
|
||||
#[cfg(feature = "preserve_order")]
|
||||
for (k, v) in std::mem::replace(&mut other.map, Map::default()) {
|
||||
self.map.insert(k, v);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn entry<K>(&mut self, k: K) -> Entry
|
||||
where
|
||||
K: Into<String>,
|
||||
{
|
||||
#[cfg(not(feature = "preserve_order"))]
|
||||
use std::collections::hash_map::Entry as EntryImpl;
|
||||
use std::collections::btree_map::Entry as EntryImpl;
|
||||
|
||||
#[cfg(feature = "preserve_order")]
|
||||
use indexmap::map::Entry as EntryImpl;
|
||||
|
@ -219,7 +234,7 @@ impl<'a> Entry<'a> {
|
|||
|
||||
pub struct VacantEntry<'a> {
|
||||
#[cfg(not(feature = "preserve_order"))]
|
||||
ve: std::collections::hash_map::VacantEntry<'a, String, Value>,
|
||||
ve: std::collections::btree_map::VacantEntry<'a, String, Value>,
|
||||
#[cfg(feature = "preserve_order")]
|
||||
ve: indexmap::map::VacantEntry<'a, String, Value>,
|
||||
}
|
||||
|
@ -236,7 +251,7 @@ impl<'a> VacantEntry<'a> {
|
|||
|
||||
pub struct OccupiedEntry<'a> {
|
||||
#[cfg(not(feature = "preserve_order"))]
|
||||
oe: std::collections::hash_map::OccupiedEntry<'a, String, Value>,
|
||||
oe: std::collections::btree_map::OccupiedEntry<'a, String, Value>,
|
||||
#[cfg(feature = "preserve_order")]
|
||||
oe: indexmap::map::OccupiedEntry<'a, String, Value>,
|
||||
}
|
||||
|
@ -336,7 +351,7 @@ impl<'a> IntoIterator for &'a Compound {
|
|||
#[derive(Clone)]
|
||||
pub struct Iter<'a> {
|
||||
#[cfg(not(feature = "preserve_order"))]
|
||||
iter: std::collections::hash_map::Iter<'a, String, Value>,
|
||||
iter: std::collections::btree_map::Iter<'a, String, Value>,
|
||||
#[cfg(feature = "preserve_order")]
|
||||
iter: indexmap::map::Iter<'a, String, Value>,
|
||||
}
|
||||
|
@ -356,7 +371,7 @@ impl<'a> IntoIterator for &'a mut Compound {
|
|||
|
||||
pub struct IterMut<'a> {
|
||||
#[cfg(not(feature = "preserve_order"))]
|
||||
iter: std::collections::hash_map::IterMut<'a, String, Value>,
|
||||
iter: std::collections::btree_map::IterMut<'a, String, Value>,
|
||||
#[cfg(feature = "preserve_order")]
|
||||
iter: indexmap::map::IterMut<'a, String, Value>,
|
||||
}
|
||||
|
@ -376,7 +391,7 @@ impl IntoIterator for Compound {
|
|||
|
||||
pub struct IntoIter {
|
||||
#[cfg(not(feature = "preserve_order"))]
|
||||
iter: std::collections::hash_map::IntoIter<String, Value>,
|
||||
iter: std::collections::btree_map::IntoIter<String, Value>,
|
||||
#[cfg(feature = "preserve_order")]
|
||||
iter: indexmap::map::IntoIter<String, Value>,
|
||||
}
|
||||
|
@ -386,7 +401,7 @@ impl_iterator_traits!((IntoIter) => (String, Value));
|
|||
#[derive(Clone)]
|
||||
pub struct Keys<'a> {
|
||||
#[cfg(not(feature = "preserve_order"))]
|
||||
iter: std::collections::hash_map::Keys<'a, String, Value>,
|
||||
iter: std::collections::btree_map::Keys<'a, String, Value>,
|
||||
#[cfg(feature = "preserve_order")]
|
||||
iter: indexmap::map::Keys<'a, String, Value>,
|
||||
}
|
||||
|
@ -396,7 +411,7 @@ impl_iterator_traits!((Keys<'a>) => &'a String);
|
|||
#[derive(Clone)]
|
||||
pub struct Values<'a> {
|
||||
#[cfg(not(feature = "preserve_order"))]
|
||||
iter: std::collections::hash_map::Values<'a, String, Value>,
|
||||
iter: std::collections::btree_map::Values<'a, String, Value>,
|
||||
#[cfg(feature = "preserve_order")]
|
||||
iter: indexmap::map::Values<'a, String, Value>,
|
||||
}
|
||||
|
@ -405,7 +420,7 @@ impl_iterator_traits!((Values<'a>) => &'a Value);
|
|||
|
||||
pub struct ValuesMut<'a> {
|
||||
#[cfg(not(feature = "preserve_order"))]
|
||||
iter: std::collections::hash_map::ValuesMut<'a, String, Value>,
|
||||
iter: std::collections::btree_map::ValuesMut<'a, String, Value>,
|
||||
#[cfg(feature = "preserve_order")]
|
||||
iter: indexmap::map::ValuesMut<'a, String, Value>,
|
||||
}
|
||||
|
|
|
@ -160,55 +160,44 @@ impl DecodeState<'_, '_> {
|
|||
"TAG_End list with nonzero length of {len}"
|
||||
))),
|
||||
},
|
||||
Tag::Byte => Ok(self
|
||||
.read_list::<_, _, 1>(Tag::Byte, |st| st.read_byte())?
|
||||
.into()),
|
||||
Tag::Short => Ok(self
|
||||
.read_list::<_, _, 2>(Tag::Short, |st| st.read_short())?
|
||||
.into()),
|
||||
Tag::Int => Ok(self
|
||||
.read_list::<_, _, 4>(Tag::Int, |st| st.read_int())?
|
||||
.into()),
|
||||
Tag::Long => Ok(self
|
||||
.read_list::<_, _, 8>(Tag::Long, |st| st.read_long())?
|
||||
.into()),
|
||||
Tag::Float => Ok(self
|
||||
.read_list::<_, _, 4>(Tag::Float, |st| st.read_float())?
|
||||
.into()),
|
||||
Tag::Byte => Ok(self.read_list(Tag::Byte, 1, |st| st.read_byte())?.into()),
|
||||
Tag::Short => Ok(self.read_list(Tag::Short, 2, |st| st.read_short())?.into()),
|
||||
Tag::Int => Ok(self.read_list(Tag::Int, 4, |st| st.read_int())?.into()),
|
||||
Tag::Long => Ok(self.read_list(Tag::Long, 8, |st| st.read_long())?.into()),
|
||||
Tag::Float => Ok(self.read_list(Tag::Float, 4, |st| st.read_float())?.into()),
|
||||
Tag::Double => Ok(self
|
||||
.read_list::<_, _, 8>(Tag::Double, |st| st.read_double())?
|
||||
.read_list(Tag::Double, 8, |st| st.read_double())?
|
||||
.into()),
|
||||
Tag::ByteArray => Ok(self
|
||||
.read_list::<_, _, 4>(Tag::ByteArray, |st| st.read_byte_array())?
|
||||
.read_list(Tag::ByteArray, 4, |st| st.read_byte_array())?
|
||||
.into()),
|
||||
Tag::String => Ok(self
|
||||
.read_list::<_, _, 2>(Tag::String, |st| st.read_string())?
|
||||
.read_list(Tag::String, 2, |st| st.read_string())?
|
||||
.into()),
|
||||
Tag::List => self.check_depth(|st| {
|
||||
Ok(st
|
||||
.read_list::<_, _, 5>(Tag::List, |st| st.read_any_list())?
|
||||
.into())
|
||||
}),
|
||||
Tag::List => self
|
||||
.check_depth(|st| Ok(st.read_list(Tag::List, 5, |st| st.read_any_list())?.into())),
|
||||
Tag::Compound => self.check_depth(|st| {
|
||||
Ok(st
|
||||
.read_list::<_, _, 1>(Tag::Compound, |st| st.read_compound())?
|
||||
.read_list(Tag::Compound, 1, |st| st.read_compound())?
|
||||
.into())
|
||||
}),
|
||||
Tag::IntArray => Ok(self
|
||||
.read_list::<_, _, 4>(Tag::IntArray, |st| st.read_int_array())?
|
||||
.read_list(Tag::IntArray, 4, |st| st.read_int_array())?
|
||||
.into()),
|
||||
Tag::LongArray => Ok(self
|
||||
.read_list::<_, _, 4>(Tag::LongArray, |st| st.read_long_array())?
|
||||
.read_list(Tag::LongArray, 4, |st| st.read_long_array())?
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Assumes the element tag has already been read.
|
||||
///
|
||||
/// `MIN_ELEM_SIZE` is the minimum size of the list element when encoded.
|
||||
fn read_list<T, F, const MIN_ELEM_SIZE: usize>(
|
||||
/// `min_elem_size` is the minimum size of the list element when encoded.
|
||||
#[inline]
|
||||
fn read_list<T, F>(
|
||||
&mut self,
|
||||
elem_type: Tag,
|
||||
min_elem_size: usize,
|
||||
mut read_elem: F,
|
||||
) -> Result<Vec<T>>
|
||||
where
|
||||
|
@ -224,7 +213,7 @@ impl DecodeState<'_, '_> {
|
|||
|
||||
// Ensure we don't reserve more than the maximum amount of memory required given
|
||||
// the size of the remaining input.
|
||||
if len as u64 * MIN_ELEM_SIZE as u64 > self.slice.len() as u64 {
|
||||
if len as u64 * min_elem_size as u64 > self.slice.len() as u64 {
|
||||
return Err(Error::new_owned(format!(
|
||||
"{elem_type} list of length {len} exceeds remainder of input"
|
||||
)));
|
||||
|
|
Loading…
Reference in a new issue