Skip to content

Commit

Permalink
Refactor building from pairs (#129)
Browse files Browse the repository at this point in the history
  • Loading branch information
Wang, Chi authored Feb 10, 2023
1 parent 898d0a8 commit 0eaabe8
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 85 deletions.
63 changes: 63 additions & 0 deletions src/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use crate::{Entry, Slab};

// Building `Slab` from pairs (usize, T).
pub(crate) struct Builder<T> {
slab: Slab<T>,
vacant_list_broken: bool,
first_vacant_index: Option<usize>,
}

impl<T> Builder<T> {
pub(crate) fn with_capacity(capacity: usize) -> Self {
Self {
slab: Slab::with_capacity(capacity),
vacant_list_broken: false,
first_vacant_index: None,
}
}
pub(crate) fn pair(&mut self, key: usize, value: T) {
let slab = &mut self.slab;
if key < slab.entries.len() {
// iterator is not sorted, might need to recreate vacant list
if let Entry::Vacant(_) = slab.entries[key] {
self.vacant_list_broken = true;
slab.len += 1;
}
// if an element with this key already exists, replace it.
// This is consistent with HashMap and BtreeMap
slab.entries[key] = Entry::Occupied(value);
} else {
if self.first_vacant_index.is_none() && slab.entries.len() < key {
self.first_vacant_index = Some(slab.entries.len());
}
// insert holes as necessary
while slab.entries.len() < key {
// add the entry to the start of the vacant list
let next = slab.next;
slab.next = slab.entries.len();
slab.entries.push(Entry::Vacant(next));
}
slab.entries.push(Entry::Occupied(value));
slab.len += 1;
}
}

pub(crate) fn build(self) -> Slab<T> {
let mut slab = self.slab;
if slab.len == slab.entries.len() {
// no vacant entries, so next might not have been updated
slab.next = slab.entries.len();
} else if self.vacant_list_broken {
slab.recreate_vacant_list();
} else if let Some(first_vacant_index) = self.first_vacant_index {
let next = slab.entries.len();
match &mut slab.entries[first_vacant_index] {
Entry::Vacant(n) => *n = next,
_ => unreachable!(),
}
} else {
unreachable!()
}
slab
}
}
47 changes: 5 additions & 42 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ extern crate std as alloc;
#[cfg(feature = "serde")]
mod serde;

mod builder;

use alloc::vec::{self, Vec};
use core::iter::{self, FromIterator, FusedIterator};
use core::{fmt, mem, ops, slice};
Expand Down Expand Up @@ -1260,51 +1262,12 @@ impl<T> FromIterator<(usize, T)> for Slab<T> {
I: IntoIterator<Item = (usize, T)>,
{
let iterator = iterable.into_iter();
let mut slab = Self::with_capacity(iterator.size_hint().0);
let mut builder = builder::Builder::with_capacity(iterator.size_hint().0);

let mut vacant_list_broken = false;
let mut first_vacant_index = None;
for (key, value) in iterator {
if key < slab.entries.len() {
// iterator is not sorted, might need to recreate vacant list
if let Entry::Vacant(_) = slab.entries[key] {
vacant_list_broken = true;
slab.len += 1;
}
// if an element with this key already exists, replace it.
// This is consistent with HashMap and BtreeMap
slab.entries[key] = Entry::Occupied(value);
} else {
if first_vacant_index.is_none() && slab.entries.len() < key {
first_vacant_index = Some(slab.entries.len());
}
// insert holes as necessary
while slab.entries.len() < key {
// add the entry to the start of the vacant list
let next = slab.next;
slab.next = slab.entries.len();
slab.entries.push(Entry::Vacant(next));
}
slab.entries.push(Entry::Occupied(value));
slab.len += 1;
}
builder.pair(key, value)
}
if slab.len == slab.entries.len() {
// no vacant entries, so next might not have been updated
slab.next = slab.entries.len();
} else if vacant_list_broken {
slab.recreate_vacant_list();
} else if let Some(first_vacant_index) = first_vacant_index {
let next = slab.entries.len();
match &mut slab.entries[first_vacant_index] {
Entry::Vacant(n) => *n = next,
_ => unreachable!(),
}
} else {
unreachable!()
}

slab
builder.build()
}
}

Expand Down
47 changes: 4 additions & 43 deletions src/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use core::marker::PhantomData;
use serde::de::{Deserialize, Deserializer, MapAccess, Visitor};
use serde::ser::{Serialize, SerializeMap, Serializer};

use super::{Entry, Slab};
use super::{builder::Builder, Slab};

impl<T> Serialize for Slab<T>
where
Expand Down Expand Up @@ -39,52 +39,13 @@ where
where
A: MapAccess<'de>,
{
let mut slab = Slab::with_capacity(map.size_hint().unwrap_or(0));
let mut builder = Builder::with_capacity(map.size_hint().unwrap_or(0));

// same as FromIterator impl
let mut vacant_list_broken = false;
let mut first_vacant_index = None;
while let Some((key, value)) = map.next_entry()? {
if key < slab.entries.len() {
// iterator is not sorted, might need to recreate vacant list
if let Entry::Vacant(_) = slab.entries[key] {
vacant_list_broken = true;
slab.len += 1;
}
// if an element with this key already exists, replace it.
// This is consistent with HashMap and BtreeMap
slab.entries[key] = Entry::Occupied(value);
} else {
if first_vacant_index.is_none() && slab.entries.len() < key {
first_vacant_index = Some(slab.entries.len());
}
// insert holes as necessary
while slab.entries.len() < key {
// add the entry to the start of the vacant list
let next = slab.next;
slab.next = slab.entries.len();
slab.entries.push(Entry::Vacant(next));
}
slab.entries.push(Entry::Occupied(value));
slab.len += 1;
}
}
if slab.len == slab.entries.len() {
// no vacant entries, so next might not have been updated
slab.next = slab.entries.len();
} else if vacant_list_broken {
slab.recreate_vacant_list();
} else if let Some(first_vacant_index) = first_vacant_index {
let next = slab.entries.len();
match &mut slab.entries[first_vacant_index] {
Entry::Vacant(n) => *n = next,
_ => unreachable!(),
}
} else {
unreachable!()
builder.pair(key, value)
}

Ok(slab)
Ok(builder.build())
}
}

Expand Down

0 comments on commit 0eaabe8

Please sign in to comment.