Skip to content

Commit ea17c56

Browse files
authored
RUST-283 Replace linked-hash-map with indexmap (#247)
This also updates the Entry API of Document to match std::collections::HashMap
1 parent 0ebb96c commit ea17c56

File tree

3 files changed

+102
-52
lines changed

3 files changed

+102
-52
lines changed

Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,12 @@ decimal128 = ["decimal"]
3939
name = "bson"
4040

4141
[dependencies]
42+
ahash = "0.7.2"
4243
chrono = "0.4"
4344
rand = "0.7"
4445
serde = { version = "1.0", features = ["derive"] }
4546
serde_json = { version = "1.0", features = ["preserve_order"] }
46-
linked-hash-map = "0.5.3"
47+
indexmap = "1.6.2"
4748
hex = "0.4.2"
4849
decimal = { version = "2.1.0", default_features = false, optional = true }
4950
base64 = "0.13.0"

src/document.rs

+77-44
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,14 @@ use std::{
44
error,
55
fmt::{self, Debug, Display, Formatter},
66
io::{Read, Write},
7-
iter::{Extend, FromIterator, IntoIterator, Map},
7+
iter::{Extend, FromIterator, IntoIterator},
88
marker::PhantomData,
99
mem,
1010
};
1111

12+
use ahash::RandomState;
1213
use chrono::{DateTime, Utc};
13-
14-
use linked_hash_map::{self, LinkedHashMap};
15-
14+
use indexmap::IndexMap;
1615
use serde::de::{self, Error, MapAccess, Visitor};
1716

1817
#[cfg(feature = "decimal128")]
@@ -64,7 +63,7 @@ impl error::Error for ValueAccessError {}
6463
/// A BSON document represented as an associative HashMap with insertion ordering.
6564
#[derive(Clone, PartialEq)]
6665
pub struct Document {
67-
inner: LinkedHashMap<String, Bson>,
66+
inner: IndexMap<String, Bson, RandomState>,
6867
}
6968

7069
impl Default for Document {
@@ -101,24 +100,22 @@ impl Debug for Document {
101100

102101
/// An iterator over Document entries.
103102
pub struct DocumentIntoIterator {
104-
inner: LinkedHashMap<String, Bson>,
103+
inner: indexmap::map::IntoIter<String, Bson>,
105104
}
106105

107106
/// An owning iterator over Document entries.
108107
pub struct DocumentIterator<'a> {
109-
inner: linked_hash_map::Iter<'a, String, Bson>,
108+
inner: indexmap::map::Iter<'a, String, Bson>,
110109
}
111110

112-
type DocumentMap<'a, T> = Map<DocumentIterator<'a>, fn((&'a String, &'a Bson)) -> T>;
113-
114111
/// An iterator over an Document's keys.
115112
pub struct Keys<'a> {
116-
inner: DocumentMap<'a, &'a String>,
113+
inner: indexmap::map::Keys<'a, String, Bson>,
117114
}
118115

119116
/// An iterator over an Document's values.
120117
pub struct Values<'a> {
121-
inner: DocumentMap<'a, &'a Bson>,
118+
inner: indexmap::map::Values<'a, String, Bson>,
122119
}
123120

124121
impl<'a> Iterator for Keys<'a> {
@@ -142,7 +139,9 @@ impl IntoIterator for Document {
142139
type IntoIter = DocumentIntoIterator;
143140

144141
fn into_iter(self) -> Self::IntoIter {
145-
DocumentIntoIterator { inner: self.inner }
142+
DocumentIntoIterator {
143+
inner: self.inner.into_iter(),
144+
}
146145
}
147146
}
148147

@@ -171,7 +170,7 @@ impl<'a> Iterator for DocumentIntoIterator {
171170
type Item = (String, Bson);
172171

173172
fn next(&mut self) -> Option<(String, Bson)> {
174-
self.inner.pop_front()
173+
self.inner.next()
175174
}
176175
}
177176

@@ -187,7 +186,7 @@ impl Document {
187186
/// Creates a new empty Document.
188187
pub fn new() -> Document {
189188
Document {
190-
inner: LinkedHashMap::new(),
189+
inner: IndexMap::default(),
191190
}
192191
}
193192

@@ -457,26 +456,16 @@ impl Document {
457456
}
458457

459458
/// Gets a collection of all keys in the document.
460-
pub fn keys<'a>(&'a self) -> Keys<'a> {
461-
fn first<A, B>((a, _): (A, B)) -> A {
462-
a
463-
}
464-
let first: fn((&'a String, &'a Bson)) -> &'a String = first;
465-
459+
pub fn keys(&self) -> Keys {
466460
Keys {
467-
inner: self.iter().map(first),
461+
inner: self.inner.keys(),
468462
}
469463
}
470464

471465
/// Gets a collection of all values in the document.
472-
pub fn values<'a>(&'a self) -> Values<'a> {
473-
fn second<A, B>((_, b): (A, B)) -> B {
474-
b
475-
}
476-
let second: fn((&'a String, &'a Bson)) -> &'a Bson = second;
477-
466+
pub fn values(&self) -> Values {
478467
Values {
479-
inner: self.iter().map(second),
468+
inner: self.inner.values(),
480469
}
481470
}
482471

@@ -498,13 +487,15 @@ impl Document {
498487
}
499488

500489
/// Takes the value of the entry out of the document, and returns it.
490+
/// Computes in **O(n)** time (average).
501491
pub fn remove(&mut self, key: &str) -> Option<Bson> {
502-
self.inner.remove(key)
492+
self.inner.shift_remove(key)
503493
}
504494

505495
pub fn entry(&mut self, k: String) -> Entry {
506-
Entry {
507-
inner: self.inner.entry(k),
496+
match self.inner.entry(k) {
497+
indexmap::map::Entry::Occupied(o) => Entry::Occupied(OccupiedEntry { inner: o }),
498+
indexmap::map::Entry::Vacant(v) => Entry::Vacant(VacantEntry { inner: v }),
508499
}
509500
}
510501

@@ -575,37 +566,79 @@ impl Document {
575566
}
576567
}
577568

578-
pub struct Entry<'a> {
579-
inner: linked_hash_map::Entry<'a, String, Bson>,
569+
/// A view into a single entry in a map, which may either be vacant or occupied.
570+
///
571+
/// This enum is constructed from the entry method on HashMap.
572+
pub enum Entry<'a> {
573+
/// An occupied entry.
574+
Occupied(OccupiedEntry<'a>),
575+
576+
/// A vacant entry.
577+
Vacant(VacantEntry<'a>),
580578
}
581579

582580
impl<'a> Entry<'a> {
581+
/// Returns a reference to this entry's key.
583582
pub fn key(&self) -> &str {
584-
self.inner.key()
583+
match self {
584+
Self::Vacant(v) => v.key(),
585+
Self::Occupied(o) => o.key(),
586+
}
585587
}
586588

589+
fn into_indexmap_entry(self) -> indexmap::map::Entry<'a, String, Bson> {
590+
match self {
591+
Self::Occupied(o) => indexmap::map::Entry::Occupied(o.inner),
592+
Self::Vacant(v) => indexmap::map::Entry::Vacant(v.inner),
593+
}
594+
}
595+
596+
/// Inserts the given default value in the entry if it is vacant and returns a mutable reference
597+
/// to it. Otherwise a mutable reference to an already existent value is returned.
587598
pub fn or_insert(self, default: Bson) -> &'a mut Bson {
588-
self.inner.or_insert(default)
599+
self.into_indexmap_entry().or_insert(default)
589600
}
590601

602+
/// Inserts the result of the `default` function in the entry if it is vacant and returns a
603+
/// mutable reference to it. Otherwise a mutable reference to an already existent value is
604+
/// returned.
591605
pub fn or_insert_with<F: FnOnce() -> Bson>(self, default: F) -> &'a mut Bson {
592-
self.inner.or_insert_with(default)
606+
self.into_indexmap_entry().or_insert_with(default)
607+
}
608+
}
609+
610+
/// A view into a vacant entry in a [Document]. It is part of the [Entry] enum.
611+
pub struct VacantEntry<'a> {
612+
inner: indexmap::map::VacantEntry<'a, String, Bson>,
613+
}
614+
615+
impl<'a> VacantEntry<'a> {
616+
/// Gets a reference to the key that would be used when inserting a value through the
617+
/// [VacantEntry].
618+
fn key(&self) -> &str {
619+
self.inner.key()
593620
}
594621
}
595622

596-
impl From<LinkedHashMap<String, Bson>> for Document {
597-
fn from(tree: LinkedHashMap<String, Bson>) -> Document {
598-
Document { inner: tree }
623+
/// A view into an occupied entry in a [Document]. It is part of the [Entry] enum.
624+
pub struct OccupiedEntry<'a> {
625+
inner: indexmap::map::OccupiedEntry<'a, String, Bson>,
626+
}
627+
628+
impl<'a> OccupiedEntry<'a> {
629+
/// Gets a reference to the key in the entry.
630+
pub fn key(&self) -> &str {
631+
self.inner.key()
599632
}
600633
}
601634

602-
pub struct DocumentVisitor {
635+
pub(crate) struct DocumentVisitor {
603636
marker: PhantomData<Document>,
604637
}
605638

606639
impl DocumentVisitor {
607640
#[allow(clippy::new_without_default)]
608-
pub fn new() -> DocumentVisitor {
641+
pub(crate) fn new() -> DocumentVisitor {
609642
DocumentVisitor {
610643
marker: PhantomData,
611644
}
@@ -633,15 +666,15 @@ impl<'de> Visitor<'de> for DocumentVisitor {
633666
V: MapAccess<'de>,
634667
{
635668
let mut inner = match visitor.size_hint() {
636-
Some(size) => LinkedHashMap::with_capacity(size),
637-
None => LinkedHashMap::new(),
669+
Some(size) => IndexMap::with_capacity_and_hasher(size, RandomState::default()),
670+
None => IndexMap::default(),
638671
};
639672

640673
while let Some((key, value)) = visitor.next_entry()? {
641674
inner.insert(key, value);
642675
}
643676

644-
Ok(inner.into())
677+
Ok(Document { inner })
645678
}
646679
}
647680

src/tests/modules/ordered.rs

+23-7
Original file line numberDiff line numberDiff line change
@@ -163,18 +163,34 @@ fn test_getters() {
163163
#[test]
164164
fn remove() {
165165
let _guard = LOCK.run_concurrently();
166-
let mut doc = Document::new();
167-
doc.insert("first", Bson::Int32(1));
168-
doc.insert("second", Bson::String("foo".to_owned()));
169-
doc.insert("alphanumeric", Bson::String("bar".to_owned()));
170166

171-
assert!(doc.remove("second").is_some());
172-
assert!(doc.remove("none").is_none());
167+
let mut doc = Document::new();
168+
doc.insert("first", 1i32);
169+
doc.insert("second", "foo");
170+
doc.insert("third", "bar".to_owned());
171+
doc.insert("fourth", "bar".to_owned());
173172

174-
let expected_keys = vec!["first", "alphanumeric"];
173+
let mut expected_keys = vec![
174+
"first".to_owned(),
175+
"second".to_owned(),
176+
"third".to_owned(),
177+
"fourth".to_owned(),
178+
];
175179

176180
let keys: Vec<_> = doc.iter().map(|(key, _)| key.to_owned()).collect();
177181
assert_eq!(expected_keys, keys);
182+
183+
assert_eq!(doc.remove("none"), None);
184+
185+
assert!(doc.remove("second").is_some());
186+
expected_keys.remove(1);
187+
let keys: Vec<_> = doc.iter().map(|(key, _)| key.to_owned()).collect();
188+
assert_eq!(keys, expected_keys);
189+
190+
assert!(doc.remove("first").is_some());
191+
expected_keys.remove(0);
192+
let keys: Vec<_> = doc.iter().map(|(key, _)| key.to_owned()).collect();
193+
assert_eq!(keys, expected_keys);
178194
}
179195

180196
#[test]

0 commit comments

Comments
 (0)