summaryrefslogtreecommitdiffstats
path: root/vendor/string_cache/src/dynamic_set.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/string_cache/src/dynamic_set.rs')
-rw-r--r--vendor/string_cache/src/dynamic_set.rs108
1 files changed, 108 insertions, 0 deletions
diff --git a/vendor/string_cache/src/dynamic_set.rs b/vendor/string_cache/src/dynamic_set.rs
new file mode 100644
index 000000000..602b700c8
--- /dev/null
+++ b/vendor/string_cache/src/dynamic_set.rs
@@ -0,0 +1,108 @@
+// Copyright 2014 The Servo Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use lazy_static::lazy_static;
+use parking_lot::Mutex;
+use std::borrow::Cow;
+use std::mem;
+use std::ptr::NonNull;
+use std::sync::atomic::AtomicIsize;
+use std::sync::atomic::Ordering::SeqCst;
+
+const NB_BUCKETS: usize = 1 << 12; // 4096
+const BUCKET_MASK: u32 = (1 << 12) - 1;
+
+pub(crate) struct Set {
+ buckets: Box<[Option<Box<Entry>>; NB_BUCKETS]>,
+}
+
+pub(crate) struct Entry {
+ pub(crate) string: Box<str>,
+ pub(crate) hash: u32,
+ pub(crate) ref_count: AtomicIsize,
+ next_in_bucket: Option<Box<Entry>>,
+}
+
+// Addresses are a multiples of this,
+// and therefore have have TAG_MASK bits unset, available for tagging.
+pub(crate) const ENTRY_ALIGNMENT: usize = 4;
+
+#[test]
+fn entry_alignment_is_sufficient() {
+ assert!(mem::align_of::<Entry>() >= ENTRY_ALIGNMENT);
+}
+
+lazy_static! {
+ pub(crate) static ref DYNAMIC_SET: Mutex<Set> = Mutex::new({
+ type T = Option<Box<Entry>>;
+ let _static_assert_size_eq = std::mem::transmute::<T, usize>;
+ let vec = std::mem::ManuallyDrop::new(vec![0_usize; NB_BUCKETS]);
+ Set {
+ buckets: unsafe { Box::from_raw(vec.as_ptr() as *mut [T; NB_BUCKETS]) },
+ }
+ });
+}
+
+impl Set {
+ pub(crate) fn insert(&mut self, string: Cow<str>, hash: u32) -> NonNull<Entry> {
+ let bucket_index = (hash & BUCKET_MASK) as usize;
+ {
+ let mut ptr: Option<&mut Box<Entry>> = self.buckets[bucket_index].as_mut();
+
+ while let Some(entry) = ptr.take() {
+ if entry.hash == hash && *entry.string == *string {
+ if entry.ref_count.fetch_add(1, SeqCst) > 0 {
+ return NonNull::from(&mut **entry);
+ }
+ // Uh-oh. The pointer's reference count was zero, which means someone may try
+ // to free it. (Naive attempts to defend against this, for example having the
+ // destructor check to see whether the reference count is indeed zero, don't
+ // work due to ABA.) Thus we need to temporarily add a duplicate string to the
+ // list.
+ entry.ref_count.fetch_sub(1, SeqCst);
+ break;
+ }
+ ptr = entry.next_in_bucket.as_mut();
+ }
+ }
+ debug_assert!(mem::align_of::<Entry>() >= ENTRY_ALIGNMENT);
+ let string = string.into_owned();
+ let mut entry = Box::new(Entry {
+ next_in_bucket: self.buckets[bucket_index].take(),
+ hash,
+ ref_count: AtomicIsize::new(1),
+ string: string.into_boxed_str(),
+ });
+ let ptr = NonNull::from(&mut *entry);
+ self.buckets[bucket_index] = Some(entry);
+
+ ptr
+ }
+
+ pub(crate) fn remove(&mut self, ptr: *mut Entry) {
+ let bucket_index = {
+ let value: &Entry = unsafe { &*ptr };
+ debug_assert!(value.ref_count.load(SeqCst) == 0);
+ (value.hash & BUCKET_MASK) as usize
+ };
+
+ let mut current: &mut Option<Box<Entry>> = &mut self.buckets[bucket_index];
+
+ while let Some(entry_ptr) = current.as_mut() {
+ let entry_ptr: *mut Entry = &mut **entry_ptr;
+ if entry_ptr == ptr {
+ mem::drop(mem::replace(current, unsafe {
+ (*entry_ptr).next_in_bucket.take()
+ }));
+ break;
+ }
+ current = unsafe { &mut (*entry_ptr).next_in_bucket };
+ }
+ }
+}