Skip to content

Commit

Permalink
Merge cbd739a into a7708ec
Browse files Browse the repository at this point in the history
  • Loading branch information
Robbepop committed Jul 18, 2020
2 parents a7708ec + cbd739a commit 1cfed1f
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 28 deletions.
68 changes: 68 additions & 0 deletions src/backend/bucket/fixed_str.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use super::InternedStr;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FixedString {
contents: String,
}

impl Default for FixedString {
#[inline]
fn default() -> Self {
Self {
contents: String::new(),
}
}
}

impl FixedString {
/// Creates a new fixed string with the given fixed capacity.
#[inline]
pub fn with_capacity(cap: usize) -> Self {
Self {
contents: String::with_capacity(cap),
}
}

/// Returns the underlying [`Box<str>`].
///
/// Guarantees not to perform any reallocations in this process.
#[inline]
pub fn finish(self) -> String {
self.contents
}

/// Returns the capacity in bytes of the fixed string.
#[inline]
pub fn capacity(&self) -> usize {
self.contents.capacity()
}

/// Returns the length in bytes of the fixed string.
#[inline]
pub fn len(&self) -> usize {
self.contents.len()
}

/// Pushes the given string into the fixed string if there is enough capacity.
///
/// Returns a reference to the pushed string if there was enough capacity to
/// perform the operation. Otherwise returns `None`.
#[inline]
pub fn push_str(&mut self, string: &str) -> Option<InternedStr> {
let len = self.len();
if self.capacity() < len + string.len() {
return None
}
self.contents.push_str(string);
debug_assert_eq!(self.contents.len(), len + string.len());
Some(InternedStr::new(
// SAFETY: We convert from bytes to utf8 from which we know through the
// input string that they must represent valid utf8.
unsafe {
core::str::from_utf8_unchecked(
&self.contents.as_bytes()[len..len + string.len()],
)
},
))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use core::ptr::NonNull;
///
/// It is inherently `unsafe` to use instances of this type and should not be
/// done outside of the `string-interner` crate itself.
#[derive(Debug, Copy, Clone)]
#[derive(Debug)]
#[repr(transparent)]
pub struct InternedStr {
ptr: NonNull<str>,
Expand Down
62 changes: 38 additions & 24 deletions src/backend/bucket.rs → src/backend/bucket/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
#![cfg(feature = "backends")]

mod fixed_str;
mod interned_str;

use self::{
fixed_str::FixedString,
interned_str::InternedStr,
};
use super::Backend;
use crate::{
backend::InternedStr,
compat::{
String,
Vec,
},
compat::Vec,
symbol::expect_valid_symbol,
Symbol,
};
Expand Down Expand Up @@ -39,21 +42,35 @@ use core::{
/// | Allocations | **good** |
/// | Footprint | **ok** |
/// | Supports `get_or_intern_static` | **yes** |
/// | `Send` + `Sync` | **no** |
/// | `Send` + `Sync` | **yes** |
#[derive(Debug)]
pub struct BucketBackend<S> {
spans: Vec<InternedStr>,
head: String,
head: FixedString,
full: Vec<String>,
marker: PhantomData<fn() -> S>,
}

/// # Safety
///
/// The bucket backend requires a manual [`Send`] impl because it is self
/// referential. When cloning a bucket backend a deep clone is performed and
/// all references to itself are updated for the clone.
unsafe impl<S> Send for BucketBackend<S> where S: Symbol {}

/// # Safety
///
/// The bucket backend requires a manual [`Send`] impl because it is self
/// referential. Those references won't escape its own scope and also
/// the bucket backend has no interior mutability.
unsafe impl<S> Sync for BucketBackend<S> where S: Symbol {}

impl<S> Default for BucketBackend<S> {
#[cfg_attr(feature = "inline-more", inline)]
fn default() -> Self {
Self {
spans: Vec::new(),
head: String::new(),
head: FixedString::default(),
full: Vec::new(),
marker: Default::default(),
}
Expand All @@ -68,7 +85,7 @@ where
fn with_capacity(cap: usize) -> Self {
Self {
spans: Vec::with_capacity(cap),
head: String::with_capacity(cap),
head: FixedString::with_capacity(cap),
full: Vec::new(),
marker: Default::default(),
}
Expand Down Expand Up @@ -123,16 +140,13 @@ where
let cap = self.head.capacity();
if cap < self.head.len() + string.len() {
let new_cap = (usize::max(cap, string.len()) + 1).next_power_of_two();
let new_head = String::with_capacity(new_cap);
let new_head = FixedString::with_capacity(new_cap);
let old_head = core::mem::replace(&mut self.head, new_head);
self.full.push(old_head);
self.full.push(old_head.finish());
}
let interned = {
let start = self.head.len();
self.head.push_str(string);
&self.head[start..start + string.len()]
};
InternedStr::new(interned)
self.head
.push_str(string)
.expect("encountered invalid head capacity (2)")
}
}

Expand All @@ -143,15 +157,15 @@ where
fn clone(&self) -> Self {
// For performance reasons we copy all cloned strings into a single cloned
// head string leaving the cloned `full` empty.
let new_head_cap = self.head.capacity()
+ self.full.iter().fold(0, |lhs, rhs| lhs + rhs.capacity());
let mut head = String::with_capacity(new_head_cap);
let new_head_cap =
self.head.capacity() + self.full.iter().fold(0, |lhs, rhs| lhs + rhs.len());
let mut head = FixedString::with_capacity(new_head_cap);
let mut spans = Vec::with_capacity(self.spans.len());
for &span in &self.spans {
for span in &self.spans {
let string = span.as_str();
let start = head.len();
head.push_str(string);
let interned = InternedStr::new(&head[start..start + string.len()]);
let interned = head
.push_str(string)
.expect("encountered invalid head capacity");
spans.push(interned);
}
Self {
Expand Down
3 changes: 0 additions & 3 deletions src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,9 @@
//! find the backend that suits their use case best.

mod bucket;
mod interned_str;
mod simple;
mod string;

#[cfg(feature = "backends")]
use self::interned_str::InternedStr;
#[cfg(feature = "backends")]
pub use self::{
bucket::BucketBackend,
Expand Down

0 comments on commit 1cfed1f

Please sign in to comment.