Skip to content

Commit 76c68e3

Browse files
committed
Move remaining usage of unstable features behind a unstable Cargo feature flag.
Without `--features unstable`: * `Atom` is 16 bytes instead of 8. (It has a drop flag.) * `ns!` and `atom!` are giant generated macros instead of plugins, and so may increase compile time.
1 parent 58fb4a9 commit 76c68e3

File tree

7 files changed

+128
-50
lines changed

7 files changed

+128
-50
lines changed

Cargo.toml

+9
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ description = "A string interning library for Rust, developed as part of the Ser
77
license = "MIT / Apache-2.0"
88
repository = "https://github.com/servo/string-cache"
99
documentation = "http://doc.servo.org/string_cache/"
10+
build = "build.rs"
1011

1112
[lib]
1213
name = "string_cache"
@@ -20,6 +21,9 @@ doctest = false
2021
# See examples/event-log.
2122
log-events = ["rustc-serialize"]
2223

24+
# Use unstable features to optimize space and time (memory and CPU usage).
25+
unstable = ["string_cache_plugin"]
26+
2327
[dependencies]
2428
rand = "0"
2529
lazy_static = "0.1.10"
@@ -32,7 +36,12 @@ optional = true
3236
[dependencies.string_cache_plugin]
3337
path = "plugin"
3438
version = "0.1.1"
39+
optional = true
3540

3641
[dependencies.string_cache_shared]
3742
path = "shared"
3843
version = "0.1.0"
44+
45+
[build-dependencies.string_cache_shared]
46+
path = "shared"
47+
version = "0.1.0"

build.rs

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
extern crate string_cache_shared;
2+
3+
use string_cache_shared::{STATIC_ATOM_SET, ALL_NS, pack_static};
4+
5+
use std::ascii::AsciiExt;
6+
use std::fs::File;
7+
use std::io::{BufWriter, Write};
8+
use std::path::Path;
9+
10+
fn main() {
11+
let path = Path::new(env!("OUT_DIR")).join("ns_macro_without_plugin.rs");
12+
let mut file = BufWriter::new(File::create(&path).unwrap());
13+
writeln!(file, r"#[macro_export]").unwrap();
14+
writeln!(file, r"macro_rules! ns {{").unwrap();
15+
writeln!(file, "(\"\") => {{ $crate::Namespace({}) }};", atom("")).unwrap();
16+
for &(prefix, url) in ALL_NS {
17+
if !prefix.is_empty() {
18+
generate_combination("".to_owned(), prefix, url, &mut file);
19+
}
20+
}
21+
writeln!(file, r"}}").unwrap();
22+
23+
writeln!(file, r"#[macro_export]").unwrap();
24+
writeln!(file, r"macro_rules! atom {{").unwrap();
25+
for &s in STATIC_ATOM_SET.iter() {
26+
if is_ident(s) {
27+
writeln!(file, r"( {} ) => {{ {} }};", s, atom(s)).unwrap();
28+
}
29+
writeln!(file, r"({:?}) => {{ {} }};", s, atom(s)).unwrap();
30+
}
31+
writeln!(file, r"}}").unwrap();
32+
}
33+
34+
fn generate_combination(prefix1: String, suffix: &str, url: &str, file: &mut BufWriter<File>) {
35+
if suffix.is_empty() {
36+
writeln!(file, r"({:?}) => {{ $crate::Namespace({}) }};", prefix1, atom(url)).unwrap();
37+
writeln!(file, r"( {} ) => {{ $crate::Namespace({}) }};", prefix1, atom(url)).unwrap();
38+
} else {
39+
let prefix2 = prefix1.clone();
40+
generate_combination(prefix1 + &*suffix[..1].to_ascii_lowercase(), &suffix[1..], url, file);
41+
generate_combination(prefix2 + &*suffix[..1].to_ascii_uppercase(), &suffix[1..], url, file);
42+
}
43+
}
44+
45+
fn atom(s: &str) -> String {
46+
let data = pack_static(STATIC_ATOM_SET.get_index(s).unwrap() as u32);
47+
format!("$crate::Atom {{ data: 0x{:x} }}", data)
48+
}
49+
50+
fn is_ident(s: &str) -> bool {
51+
let mut chars = s.chars();
52+
!s.is_empty() && match chars.next().unwrap() {
53+
'a'...'z' | 'A'...'Z' | '_' => true,
54+
_ => false
55+
} && chars.all(|c| match c {
56+
'a'...'z' | 'A'...'Z' | '_' | '0'...'9' => true,
57+
_ => false
58+
})
59+
}

plugin/src/atom/mod.rs

+1-9
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,7 @@ pub fn expand_atom(cx: &mut ExtCtxt, sp: Span, tt: &[TokenTree]) -> Box<MacResul
7171
// Translate `ns!(HTML)` into `Namespace { atom: atom!("http://www.w3.org/1999/xhtml") }`.
7272
// The argument is ASCII-case-insensitive.
7373
pub fn expand_ns(cx: &mut ExtCtxt, sp: Span, tt: &[TokenTree]) -> Box<MacResult+'static> {
74-
static ALL_NS: &'static [(&'static str, &'static str)] = &[
75-
("", ""),
76-
("html", "http://www.w3.org/1999/xhtml"),
77-
("xml", "http://www.w3.org/XML/1998/namespace"),
78-
("xmlns", "http://www.w3.org/2000/xmlns/"),
79-
("xlink", "http://www.w3.org/1999/xlink"),
80-
("svg", "http://www.w3.org/2000/svg"),
81-
("mathml", "http://www.w3.org/1998/Math/MathML"),
82-
];
74+
use string_cache_shared::ALL_NS;
8375

8476
fn usage() -> String {
8577
let ns_names: Vec<&'static str> = ALL_NS[1..].iter()

shared/lib.rs

+10
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,16 @@ pub enum UnpackedAtom {
4848

4949
const STATIC_SHIFT_BITS: usize = 32;
5050

51+
pub static ALL_NS: &'static [(&'static str, &'static str)] = &[
52+
("", ""),
53+
("html", "http://www.w3.org/1999/xhtml"),
54+
("xml", "http://www.w3.org/XML/1998/namespace"),
55+
("xmlns", "http://www.w3.org/2000/xmlns/"),
56+
("xlink", "http://www.w3.org/1999/xlink"),
57+
("svg", "http://www.w3.org/2000/svg"),
58+
("mathml", "http://www.w3.org/1998/Math/MathML"),
59+
];
60+
5161
struct RawSlice {
5262
data: *const u8,
5363
len: usize,

src/atom/mod.rs

+29-24
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,19 @@
1111

1212
use serde::{Deserialize, Deserializer, Serialize, Serializer};
1313

14-
use std::cmp::max;
1514
use std::fmt;
1615
use std::mem;
1716
use std::ops;
1817
use std::ptr;
19-
use std::slice::bytes;
2018
use std::str;
21-
use std::rt::heap;
2219
use std::cmp::Ordering::{self, Equal};
23-
use std::hash::{self, Hash, SipHasher};
20+
use std::hash::{Hash, SipHasher, Hasher};
2421
use std::sync::Mutex;
2522
use std::sync::atomic::AtomicIsize;
2623
use std::sync::atomic::Ordering::SeqCst;
2724

2825
use string_cache_shared::{self, UnpackedAtom, Static, Inline, Dynamic, STATIC_ATOM_SET,
29-
ENTRY_ALIGNMENT};
26+
ENTRY_ALIGNMENT, copy_memory};
3027

3128
#[cfg(feature = "log-events")]
3229
use event::Event;
@@ -71,7 +68,11 @@ impl StringCache {
7168
}
7269

7370
fn add(&mut self, string_to_add: &str) -> *mut StringCacheEntry {
74-
let hash = hash::hash::<_, SipHasher>(&string_to_add);
71+
let hash = {
72+
let mut hasher = SipHasher::default();
73+
string_to_add.hash(&mut hasher);
74+
hasher.finish()
75+
};
7576
let bucket_index = (hash & (self.buckets.len()-1) as u64) as usize;
7677
let mut ptr = self.buckets[bucket_index];
7778

@@ -101,14 +102,11 @@ impl StringCache {
101102
}
102103

103104
if should_add {
104-
unsafe {
105-
ptr = heap::allocate(
106-
mem::size_of::<StringCacheEntry>(),
107-
max(mem::align_of::<StringCacheEntry>(), ENTRY_ALIGNMENT)
108-
) as *mut StringCacheEntry;
109-
ptr::write(ptr,
110-
StringCacheEntry::new(self.buckets[bucket_index], hash, string_to_add));
111-
}
105+
debug_assert!(mem::align_of::<StringCacheEntry>() >= ENTRY_ALIGNMENT);
106+
let mut entry = Box::new(StringCacheEntry::new(
107+
self.buckets[bucket_index], hash, string_to_add));
108+
ptr = &mut *entry;
109+
mem::forget(entry);
112110
self.buckets[bucket_index] = ptr;
113111
log!(Event::Insert(ptr as u64, String::from(string_to_add)));
114112
}
@@ -143,19 +141,21 @@ impl StringCache {
143141
debug_assert!(current != ptr::null_mut());
144142

145143
unsafe {
146-
ptr::read(ptr);
147-
heap::deallocate(ptr as *mut u8,
148-
mem::size_of::<StringCacheEntry>(),
149-
max(mem::align_of::<StringCacheEntry>(), ENTRY_ALIGNMENT));
144+
box_from_raw(ptr);
150145
}
151146

152147
log!(Event::Remove(key));
153148
}
154149
}
155150

151+
// Box::from_raw is not stable yet
152+
unsafe fn box_from_raw<T>(raw: *mut T) -> Box<T> {
153+
mem::transmute(raw)
154+
}
155+
156156
// NOTE: Deriving Eq here implies that a given string must always
157157
// be interned the same way.
158-
#[unsafe_no_drop_flag] // See tests::atom_drop_is_idempotent
158+
#[cfg_attr(unstable, unsafe_no_drop_flag)] // See tests::atom_drop_is_idempotent
159159
#[derive(Eq, Hash, PartialEq)]
160160
pub struct Atom {
161161
/// This field is public so that the `atom!()` macro can use it.
@@ -177,7 +177,7 @@ impl Atom {
177177
let len = string_to_add.len();
178178
if len <= string_cache_shared::MAX_INLINE_LEN {
179179
let mut buf: [u8; 7] = [0; 7];
180-
bytes::copy_memory(string_to_add.as_bytes(), &mut buf);
180+
copy_memory(string_to_add.as_bytes(), &mut buf);
181181
Inline(len as u8, buf)
182182
} else {
183183
Dynamic(STRING_CACHE.lock().unwrap().add(string_to_add) as *mut ())
@@ -325,9 +325,10 @@ mod bench;
325325

326326
#[cfg(test)]
327327
mod tests {
328+
use std::mem;
328329
use std::thread;
329-
use super::Atom;
330-
use string_cache_shared::{Static, Inline, Dynamic};
330+
use super::{Atom, StringCacheEntry};
331+
use string_cache_shared::{Static, Inline, Dynamic, ENTRY_ALIGNMENT, from_packed_dynamic};
331332

332333
#[test]
333334
fn test_as_slice() {
@@ -491,7 +492,7 @@ mod tests {
491492
fn assert_sizes() {
492493
// Guard against accidental changes to the sizes of things.
493494
use std::mem;
494-
assert_eq!(8, mem::size_of::<super::Atom>());
495+
assert_eq!(if cfg!(unstable) { 8 } else { 16 }, mem::size_of::<super::Atom>());
495496
assert_eq!(48, mem::size_of::<super::StringCacheEntry>());
496497
}
497498

@@ -553,8 +554,12 @@ mod tests {
553554
#[test]
554555
fn atom_drop_is_idempotent() {
555556
unsafe {
556-
assert_eq!(::string_cache_shared::from_packed_dynamic(::std::mem::POST_DROP_U64), None);
557+
assert_eq!(from_packed_dynamic(mem::POST_DROP_U64), None);
557558
}
558559
}
559560

561+
#[test]
562+
fn string_cache_entry_alignment_is_sufficient() {
563+
assert!(mem::align_of::<StringCacheEntry>() >= ENTRY_ALIGNMENT);
564+
}
560565
}

src/lib.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@
1010
#![crate_name = "string_cache"]
1111
#![crate_type = "rlib"]
1212

13-
#![feature(plugin, unsafe_no_drop_flag)]
14-
#![feature(slice_bytes, heap_api, hash_default)]
1513
#![deny(warnings)]
1614
#![cfg_attr(test, feature(test, filling_drop))]
1715
#![cfg_attr(bench, feature(rand))]
18-
#![plugin(string_cache_plugin)]
16+
#![cfg_attr(feature = "unstable", feature(unsafe_no_drop_flag, plugin))]
17+
#![cfg_attr(feature = "unstable", plugin(string_cache_plugin))]
1918

2019
#[cfg(test)]
2120
extern crate test;
@@ -43,6 +42,9 @@ macro_rules! qualname (($ns:tt, $local:tt) => (
4342
}
4443
));
4544

45+
#[cfg(not(feature = "unstable"))]
46+
include!(concat!(env!("OUT_DIR"), "/ns_macro_without_plugin.rs"));
47+
4648
#[cfg(feature = "log-events")]
4749
#[macro_use]
4850
pub mod event;

src/namespace.rs

+15-14
Original file line numberDiff line numberDiff line change
@@ -37,30 +37,31 @@ impl QualName {
3737
#[cfg(test)]
3838
mod tests {
3939
use super::{Namespace, QualName};
40+
use Atom;
4041

4142
#[test]
4243
fn ns_macro() {
43-
assert_eq!(ns!(""), Namespace(atom!("")));
44+
assert_eq!(ns!(""), Namespace(Atom::from_slice("")));
4445

45-
assert_eq!(ns!(html), Namespace(atom!("http://www.w3.org/1999/xhtml")));
46-
assert_eq!(ns!(xml), Namespace(atom!("http://www.w3.org/XML/1998/namespace")));
47-
assert_eq!(ns!(xmlns), Namespace(atom!("http://www.w3.org/2000/xmlns/")));
48-
assert_eq!(ns!(xlink), Namespace(atom!("http://www.w3.org/1999/xlink")));
49-
assert_eq!(ns!(svg), Namespace(atom!("http://www.w3.org/2000/svg")));
50-
assert_eq!(ns!(mathml), Namespace(atom!("http://www.w3.org/1998/Math/MathML")));
46+
assert_eq!(ns!(html), Namespace(Atom::from_slice("http://www.w3.org/1999/xhtml")));
47+
assert_eq!(ns!(xml), Namespace(Atom::from_slice("http://www.w3.org/XML/1998/namespace")));
48+
assert_eq!(ns!(xmlns), Namespace(Atom::from_slice("http://www.w3.org/2000/xmlns/")));
49+
assert_eq!(ns!(xlink), Namespace(Atom::from_slice("http://www.w3.org/1999/xlink")));
50+
assert_eq!(ns!(svg), Namespace(Atom::from_slice("http://www.w3.org/2000/svg")));
51+
assert_eq!(ns!(mathml), Namespace(Atom::from_slice("http://www.w3.org/1998/Math/MathML")));
5152

52-
assert_eq!(ns!(HtMl), Namespace(atom!("http://www.w3.org/1999/xhtml")));
53-
assert_eq!(ns!(xMl), Namespace(atom!("http://www.w3.org/XML/1998/namespace")));
54-
assert_eq!(ns!(XmLnS), Namespace(atom!("http://www.w3.org/2000/xmlns/")));
55-
assert_eq!(ns!(xLiNk), Namespace(atom!("http://www.w3.org/1999/xlink")));
56-
assert_eq!(ns!(SvG), Namespace(atom!("http://www.w3.org/2000/svg")));
57-
assert_eq!(ns!(mAtHmL), Namespace(atom!("http://www.w3.org/1998/Math/MathML")));
53+
assert_eq!(ns!(HtMl), Namespace(Atom::from_slice("http://www.w3.org/1999/xhtml")));
54+
assert_eq!(ns!(xMl), Namespace(Atom::from_slice("http://www.w3.org/XML/1998/namespace")));
55+
assert_eq!(ns!(XmLnS), Namespace(Atom::from_slice("http://www.w3.org/2000/xmlns/")));
56+
assert_eq!(ns!(xLiNk), Namespace(Atom::from_slice("http://www.w3.org/1999/xlink")));
57+
assert_eq!(ns!(SvG), Namespace(Atom::from_slice("http://www.w3.org/2000/svg")));
58+
assert_eq!(ns!(mAtHmL), Namespace(Atom::from_slice("http://www.w3.org/1998/Math/MathML")));
5859
}
5960

6061
#[test]
6162
fn qualname() {
6263
assert_eq!(QualName::new(ns!(""), atom!("")),
63-
QualName { ns: ns!(""), local: atom!("") });
64+
QualName { ns: ns!(""), local: Atom::from_slice("") });
6465
assert_eq!(QualName::new(ns!(XML), atom!(base)),
6566
QualName { ns: ns!(XML), local: atom!(base) });
6667
}

0 commit comments

Comments
 (0)