Skip to content

Commit

Permalink
Auto merge of #33 - servo:…, r=nox
Browse files Browse the repository at this point in the history
Port NonZero optimization to stable Rust

… and various drive-by fixes.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/tendril/33)
<!-- Reviewable:end -->
  • Loading branch information
bors-servo authored Jun 29, 2017
2 parents 69ef3ec + 8d80121 commit f9e5c61
Show file tree
Hide file tree
Showing 9 changed files with 34 additions and 56 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ script:
- cargo build
- cargo doc
- cargo test
- "if [ $TRAVIS_RUST_VERSION = nightly ]; then cargo test --features unstable; fi"
- "if [ $TRAVIS_RUST_VERSION = nightly ]; then cargo test --features bench; fi"
- "if [ $TRAVIS_RUST_VERSION = nightly ]; then (cd capi/ctest; ./build-and-test.sh); fi"
notifications:
webhooks: http://build.servo.org:54856/travis
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "tendril"
version = "0.3.0"
version = "0.4.0"
authors = ["Keegan McAllister <[email protected]>",
"Simon Sapin <[email protected]>",
"Chris Morgan <[email protected]>"]
Expand All @@ -19,4 +19,4 @@ utf-8 = "0.7"
rand = "0"

[features]
unstable = []
bench = []
1 change: 0 additions & 1 deletion capi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ libc = "0.1"

[dependencies.tendril]
path = "../"
features = ["unstable"] # Drop flags and C API don’t play friends

[build-dependencies]
gcc = "0"
2 changes: 1 addition & 1 deletion capi/include/tendril.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ void tendril_debug_dump(const tendril *t, FILE *stream);
//// implementation details follow
////

__attribute__((packed)) struct tendril_impl {
struct tendril_impl {
uintptr_t __ptr;
uint32_t __a;
uint32_t __b;
Expand Down
15 changes: 3 additions & 12 deletions examples/fuzz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ fn fuzz() {
fn random_boundary<R: Rng>(rng: &mut R, text: &str) -> usize {
loop {
let i = Range::new(0, text.len()+1).ind_sample(rng);
if is_char_boundary(text, i) {
if text.is_char_boundary(i) {
return i;
}
}
Expand All @@ -115,25 +115,16 @@ fn random_slice<R: Rng>(rng: &mut R, text: &str) -> (usize, usize) {
loop {
let start = Range::new(0, text.len()+1).ind_sample(rng);
let end = Range::new(start, text.len()+1).ind_sample(rng);
if !is_char_boundary(text, start) {
if !text.is_char_boundary(start) {
continue;
}
if end < text.len() && !is_char_boundary(text, end) {
if end < text.len() && !text.is_char_boundary(end) {
continue;
}
return (start, end);
}
}

// Copy of the str::is_char_boundary method, which is unstable.
fn is_char_boundary(s: &str, index: usize) -> bool {
if index == s.len() { return true; }
match s.as_bytes().get(index) {
None => false,
Some(&b) => b < 128 || b >= 192,
}
}

static TEXT: &'static str =
"It was from the artists and poets that the pertinent answers came, and I \
know that panic would have broken loose had they been able to compare notes. \
Expand Down
1 change: 0 additions & 1 deletion src/buf32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ pub const MAX_LEN: usize = u32::MAX as usize;

/// A buffer points to a header of type `H`, which is followed by `MIN_CAP` or more
/// bytes of storage.
#[repr(packed)]
pub struct Buf32<H> {
pub ptr: *mut H,
pub len: u32,
Expand Down
8 changes: 2 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,15 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![cfg_attr(feature = "unstable", feature(nonzero))]
#![cfg_attr(all(test, feature = "unstable"), feature(test))]
#![cfg_attr(all(test, feature = "bench"), feature(test))]
#![cfg_attr(test, deny(warnings))]

#[cfg(feature = "unstable")] extern crate core;
#[cfg(all(test, feature = "bench"))] extern crate test;
#[cfg(feature = "encoding")] pub extern crate encoding;
#[macro_use] extern crate mac;
extern crate futf;
extern crate utf8;

#[cfg(all(test, feature = "unstable"))]
extern crate test;

pub use tendril::{Tendril, ByteTendril, StrTendril, SliceExt, ReadExt, SubtendrilError};
pub use tendril::{SendTendril, Atomicity, Atomic, NonAtomic};
pub use fmt::Format;
Expand Down
36 changes: 15 additions & 21 deletions src/tendril.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,18 @@ use buf32::{self, Buf32};
use fmt::{self, Slice};
use fmt::imp::Fixup;
use util::{unsafe_slice, unsafe_slice_mut, copy_and_advance, copy_lifetime_mut, copy_lifetime,
NonZero};
NonZeroUsize};
use OFLOW;

const MAX_INLINE_LEN: usize = 8;
const MAX_INLINE_TAG: usize = 0xF;
const EMPTY_TAG: usize = 0xF;

#[inline(always)]
fn inline_tag(len: u32) -> NonZero<usize> {
fn inline_tag(len: u32) -> NonZeroUsize {
debug_assert!(len <= MAX_INLINE_LEN as u32);
unsafe {
NonZero::new(if len == 0 {
NonZeroUsize::new(if len == 0 {
EMPTY_TAG
} else {
len as usize
Expand All @@ -47,7 +47,7 @@ fn inline_tag(len: u32) -> NonZero<usize> {
///
/// Exactly two types implement this trait:
///
/// - `Atomic`: use this in your tendril and you will have a `Send + Sync` tendril which works
/// - `Atomic`: use this in your tendril and you will have a `Send` tendril which works
/// across threads; this is akin to `Arc`.
///
/// - `NonAtomic`: use this in your tendril and you will have a tendril which is neither
Expand Down Expand Up @@ -104,8 +104,8 @@ unsafe impl Atomicity for NonAtomic {

/// A marker of an atomic (and hence concurrent) tendril.
///
/// This is used as the second, optional type parameter of a `Tendril`; `Tendril<F, Atomic>` thus
/// implements both `Send` and `Sync`.
/// This is used as the second, optional type parameter of a `Tendril`;
/// `Tendril<F, Atomic>` thus implements`Send`.
///
/// This is akin to using `Arc` for reference counting.
pub struct Atomic(AtomicUsize);
Expand Down Expand Up @@ -182,24 +182,23 @@ pub enum SubtendrilError {
///
/// The type parameter `A` indicates the atomicity of the tendril; it is by
/// default `NonAtomic`, but can be specified as `Atomic` to get a tendril
/// which implements `Send` and `Sync` (viz. a thread-safe tendril).
/// which implements `Send` (viz. a thread-safe tendril).
///
/// The maximum length of a `Tendril` is 4 GB. The library will panic if
/// you attempt to go over the limit.
#[repr(packed)]
#[repr(C)]
pub struct Tendril<F, A = NonAtomic>
where F: fmt::Format,
A: Atomicity,
{
ptr: Cell<NonZero<usize>>,
ptr: Cell<NonZeroUsize>,
len: u32,
aux: Cell<u32>,
marker: PhantomData<*mut F>,
refcount_marker: PhantomData<A>,
}

unsafe impl<F, A> Send for Tendril<F, A> where F: fmt::Format, A: Atomicity + Sync { }
unsafe impl<F, A> Sync for Tendril<F, A> where F: fmt::Format, A: Atomicity + Sync { }

/// `Tendril` for storing native Rust strings.
pub type StrTendril = Tendril<fmt::UTF8>;
Expand Down Expand Up @@ -586,7 +585,7 @@ impl<F, A> Tendril<F, A>
#[inline]
pub fn clear(&mut self) {
if self.ptr.get().get() <= MAX_INLINE_TAG {
self.ptr.set(unsafe { NonZero::new(EMPTY_TAG) });
self.ptr.set(unsafe { NonZeroUsize::new(EMPTY_TAG) });
} else {
let (_, shared, _) = unsafe { self.assume_buf() };
if shared {
Expand Down Expand Up @@ -966,7 +965,7 @@ impl<F, A> Tendril<F, A>
let header = p as *mut Header<A>;
(*header).cap = self.aux.get();

self.ptr.set(NonZero::new(p | 1));
self.ptr.set(NonZeroUsize::new(p | 1));
self.aux.set(0);
}
}
Expand All @@ -989,7 +988,7 @@ impl<F, A> Tendril<F, A>
self.make_owned();
let mut buf = self.assume_buf().0;
buf.grow(cap);
self.ptr.set(NonZero::new(buf.ptr as usize));
self.ptr.set(NonZeroUsize::new(buf.ptr as usize));
self.aux.set(buf.cap);
}

Expand Down Expand Up @@ -1032,7 +1031,7 @@ impl<F, A> Tendril<F, A>
#[inline]
unsafe fn owned(x: Buf32<Header<A>>) -> Tendril<F, A> {
Tendril {
ptr: Cell::new(NonZero::new(x.ptr as usize)),
ptr: Cell::new(NonZeroUsize::new(x.ptr as usize)),
len: x.len,
aux: Cell::new(x.cap),
marker: PhantomData,
Expand All @@ -1052,7 +1051,7 @@ impl<F, A> Tendril<F, A>
#[inline]
unsafe fn shared(buf: Buf32<Header<A>>, off: u32, len: u32) -> Tendril<F, A> {
Tendril {
ptr: Cell::new(NonZero::new((buf.ptr as usize) | 1)),
ptr: Cell::new(NonZeroUsize::new((buf.ptr as usize) | 1)),
len: len,
aux: Cell::new(off),
marker: PhantomData,
Expand Down Expand Up @@ -1548,7 +1547,7 @@ impl<'a, A> From<&'a Tendril<fmt::UTF8, A>> for String
}


#[cfg(all(test, feature = "unstable"))]
#[cfg(all(test, feature = "bench"))]
#[path="bench.rs"]
mod bench;

Expand All @@ -1561,7 +1560,6 @@ mod test {
use std::thread;

fn assert_send<T: Send>() { }
fn assert_sync<T: Sync>() { }

#[test]
fn smoke_test() {
Expand Down Expand Up @@ -1589,9 +1587,6 @@ mod test {
assert_eq!(correct, mem::size_of::<ByteTendril>());
assert_eq!(correct, mem::size_of::<StrTendril>());

// Check that the NonZero<T> optimization is working, if on unstable Rust.
let option_tag = if cfg!(feature = "unstable") { 0 } else { 1 };
let correct = correct + option_tag;
assert_eq!(correct, mem::size_of::<Option<ByteTendril>>());
assert_eq!(correct, mem::size_of::<Option<StrTendril>>());

Expand Down Expand Up @@ -2211,7 +2206,6 @@ mod test {
#[test]
fn atomic() {
assert_send::<Tendril<fmt::UTF8, Atomic>>();
assert_sync::<Tendril<fmt::UTF8, Atomic>>();
let s: Tendril<fmt::UTF8, Atomic> = Tendril::from_slice("this is a string");
assert!(!s.is_shared());
let mut t = s.clone();
Expand Down
21 changes: 10 additions & 11 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,18 @@ pub unsafe fn copy_lifetime<'a, S: ?Sized, T: ?Sized + 'a>
mem::transmute(ptr)
}

#[cfg(feature = "unstable")] pub use core::nonzero::NonZero;

#[cfg(not(feature = "unstable"))]
#[derive(Copy, Clone)]
pub struct NonZero<T>(T);
pub struct NonZeroUsize(&'static u8);

#[cfg(not(feature = "unstable"))]
impl<T> NonZero<T> {
pub unsafe fn new(x: T) -> NonZero<T> { NonZero(x) }
}
impl NonZeroUsize {
#[inline]
pub unsafe fn new(value: usize) -> Self {
debug_assert!(value != 0);
NonZeroUsize(&*(value as *const u8))
}

#[cfg(not(feature = "unstable"))]
impl<T> NonZero<T> {
#[inline]
pub fn get(self) -> T { self.0 }
pub fn get(self) -> usize {
self.0 as *const u8 as usize
}
}

0 comments on commit f9e5c61

Please sign in to comment.