mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-07 09:54:42 +00:00
No bug - Update parking_lot_core and revendor to remove duplicate version of smallvec. r=me
MozReview-Commit-ID: 6B2hp9CLs2z
This commit is contained in:
parent
c280467718
commit
ca0f202c0c
@ -1 +1 @@
|
||||
{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","Cargo.toml":"133226f5f42038bf3bbf3266d955a6b6933306d0107593eac3a0b25cf8a08862","src/lib.rs":"f5a0333cbb54faba082d1d8621beb50e711169590cc35e73268bd2841bd1163f","src/parking_lot.rs":"17c34c9cd3bc3e649567a920466ef0799be66c556d34d4dbc9c246a154a4b647","src/spinwait.rs":"682e5a3fc71d1d7a7d8da377d1d93415fa64b4f53809bd80bbf2942628e3a23d","src/stable.rs":"ddfa0601589bb86d7fb5a6d697601c40a47d395641e26ba126ff666fd7ee7715","src/thread_parker/generic.rs":"6e3c7ef287691673c5001362da8757ac30f47481c804397f1c313153971f1d61","src/thread_parker/linux.rs":"894e289090ba21235b1f8bd0ea57defa6e199998e635a61b1292e38d50c5c46a","src/thread_parker/unix.rs":"bb1102ff79ab9ef98ce0df55e74ba561b92e39db55bcf5260c3dc017129994f4","src/thread_parker/windows.rs":"c10871429ce0bf757517c69dc9b1ba9e14bf8ff4363b04a13604f98d9c525e9b","src/util.rs":"2d07c0c010a857790ae2ed6a1215eeed8af76859e076797ea1ba8dec82169e84","src/word_lock.rs":"a3a33b4e13c79ff84cf049f47694f165c050baa179dac6262318ef05b8c9f8db"},"package":"fb1b97670a2ffadce7c397fb80a3d687c4f3060140b885621ef1653d0e5d5068"}
|
||||
{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","Cargo.toml":"8ba636346d2228cb56ba5468f224fde5be21f90b903422ac462ca7b3cfbd1f97","src/lib.rs":"6649792fe4bef4cbac34090bc599a86dc58d4528a8bfba4fce6a56bee3537c46","src/parking_lot.rs":"f18ed052d295746d1495a2cfab5a7fd25947542764d294dd1df1f43b36e5361e","src/spinwait.rs":"682e5a3fc71d1d7a7d8da377d1d93415fa64b4f53809bd80bbf2942628e3a23d","src/stable.rs":"cc18c58404dc6017924d88fb9f4ed94e5320b8cb0a36985162b23130b8cd7480","src/thread_parker/generic.rs":"6e3c7ef287691673c5001362da8757ac30f47481c804397f1c313153971f1d61","src/thread_parker/linux.rs":"0a3c63a8cba192827edb55dd785ccab22229370ad2bc4b52e81cc25c1fb63696","src/thread_parker/unix.rs":"bb1102ff79ab9ef98ce0df55e74ba561b92e39db55bcf5260c3dc017129994f4","src/thread_parker/windows/keyed_event.rs":"ceea3746ae04f59f86a6ccddde651cbbf8a203eb53a9e83349dfb5159eca4d88","src/thread_parker/windows/mod.rs":"d0caa762effeba24a5d866be812d8b1e8e076394142cdde5ef873a1247e867d9","src/thread_parker/windows/waitaddress.rs":"7f280d3c60bc4290b5bf30bc9740322cc0723742cfce58dcfcbfe4058e32d3a0","src/util.rs":"2d07c0c010a857790ae2ed6a1215eeed8af76859e076797ea1ba8dec82169e84","src/word_lock.rs":"a3a33b4e13c79ff84cf049f47694f165c050baa179dac6262318ef05b8c9f8db"},"package":"56a19dcbb5d1e32b6cccb8a9aa1fc2a38418c8699652e735e2bf391a3dc0aa16"}
|
4
third_party/rust/parking_lot_core/Cargo.toml
vendored
4
third_party/rust/parking_lot_core/Cargo.toml
vendored
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "parking_lot_core"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
authors = ["Amanieu d'Antras <amanieu@gmail.com>"]
|
||||
description = "An advanced API for creating custom synchronization primitives."
|
||||
documentation = "https://amanieu.github.io/parking_lot/parking_lot_core/index.html"
|
||||
@ -9,7 +9,7 @@ repository = "https://github.com/Amanieu/parking_lot"
|
||||
keywords = ["mutex", "condvar", "rwlock", "once", "thread"]
|
||||
|
||||
[dependencies]
|
||||
smallvec = "0.1"
|
||||
smallvec = "0.3"
|
||||
rand = "0.3"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
|
4
third_party/rust/parking_lot_core/src/lib.rs
vendored
4
third_party/rust/parking_lot_core/src/lib.rs
vendored
@ -38,7 +38,7 @@
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![cfg_attr(feature = "nightly", feature(const_fn))]
|
||||
#![cfg_attr(feature = "nightly", feature(integer_atomics))]
|
||||
#![cfg_attr(all(feature = "nightly", target_os = "linux"), feature(integer_atomics))]
|
||||
#![cfg_attr(feature = "nightly", feature(asm))]
|
||||
|
||||
extern crate smallvec;
|
||||
@ -59,7 +59,7 @@ mod thread_parker;
|
||||
#[path = "thread_parker/unix.rs"]
|
||||
mod thread_parker;
|
||||
#[cfg(windows)]
|
||||
#[path = "thread_parker/windows.rs"]
|
||||
#[path = "thread_parker/windows/mod.rs"]
|
||||
mod thread_parker;
|
||||
#[cfg(not(any(windows, unix)))]
|
||||
#[path = "thread_parker/generic.rs"]
|
||||
|
@ -13,7 +13,7 @@ use std::time::{Instant, Duration};
|
||||
use std::cell::{Cell, UnsafeCell};
|
||||
use std::ptr;
|
||||
use std::mem;
|
||||
use smallvec::SmallVec8;
|
||||
use smallvec::SmallVec;
|
||||
use rand::{self, XorShiftRng, Rng};
|
||||
use thread_parker::ThreadParker;
|
||||
use word_lock::WordLock;
|
||||
@ -723,7 +723,7 @@ pub unsafe fn unpark_all(key: usize, unpark_token: UnparkToken) -> usize {
|
||||
let mut link = &bucket.queue_head;
|
||||
let mut current = bucket.queue_head.get();
|
||||
let mut previous = ptr::null();
|
||||
let mut threads = SmallVec8::new();
|
||||
let mut threads = SmallVec::<[_; 8]>::new();
|
||||
while !current.is_null() {
|
||||
if (*current).key.load(Ordering::Relaxed) == key {
|
||||
// Remove the thread from the queue
|
||||
@ -942,7 +942,7 @@ unsafe fn unpark_filter_internal(key: usize,
|
||||
let mut link = &bucket.queue_head;
|
||||
let mut current = bucket.queue_head.get();
|
||||
let mut previous = ptr::null();
|
||||
let mut threads = SmallVec8::new();
|
||||
let mut threads = SmallVec::<[_; 8]>::new();
|
||||
let mut result = UnparkResult {
|
||||
unparked_threads: 0,
|
||||
have_more_threads: false,
|
||||
|
12
third_party/rust/parking_lot_core/src/stable.rs
vendored
12
third_party/rust/parking_lot_core/src/stable.rs
vendored
@ -62,11 +62,7 @@ impl AtomicUsize {
|
||||
_: Ordering)
|
||||
-> Result<usize, usize> {
|
||||
let res = self.0.compare_and_swap(old, new, order);
|
||||
if res == old {
|
||||
Ok(res)
|
||||
} else {
|
||||
Err(res)
|
||||
}
|
||||
if res == old { Ok(res) } else { Err(res) }
|
||||
}
|
||||
#[inline]
|
||||
pub fn compare_exchange_weak(&self,
|
||||
@ -76,10 +72,6 @@ impl AtomicUsize {
|
||||
_: Ordering)
|
||||
-> Result<usize, usize> {
|
||||
let res = self.0.compare_and_swap(old, new, order);
|
||||
if res == old {
|
||||
Ok(res)
|
||||
} else {
|
||||
Err(res)
|
||||
}
|
||||
if res == old { Ok(res) } else { Err(res) }
|
||||
}
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ impl ThreadParker {
|
||||
// We don't need to lock anything, just clear the state
|
||||
self.futex.store(0, Ordering::Release);
|
||||
|
||||
UnparkHandle { thread_parker: self }
|
||||
UnparkHandle { futex: &self.futex }
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,7 +101,7 @@ impl ThreadParker {
|
||||
// as unparked while holding the queue lock, but we delay the actual unparking
|
||||
// until after the queue lock is released.
|
||||
pub struct UnparkHandle {
|
||||
thread_parker: *const ThreadParker,
|
||||
futex: *const AtomicI32,
|
||||
}
|
||||
|
||||
impl UnparkHandle {
|
||||
@ -110,10 +110,7 @@ impl UnparkHandle {
|
||||
pub unsafe fn unpark(self) {
|
||||
// The thread data may have been freed at this point, but it doesn't
|
||||
// matter since the syscall will just return EFAULT in that case.
|
||||
let r = libc::syscall(SYS_FUTEX,
|
||||
&(*self.thread_parker).futex,
|
||||
FUTEX_WAKE | FUTEX_PRIVATE,
|
||||
1);
|
||||
let r = libc::syscall(SYS_FUTEX, self.futex, FUTEX_WAKE | FUTEX_PRIVATE, 1);
|
||||
debug_assert!(r == 0 || r == 1 || r == -1);
|
||||
if r == -1 {
|
||||
debug_assert_eq!(*libc::__errno_location(), libc::EFAULT);
|
||||
|
@ -6,17 +6,21 @@
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
use stable::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
|
||||
use stable::{AtomicUsize, Ordering};
|
||||
use std::time::Instant;
|
||||
use std::ptr;
|
||||
use std::mem;
|
||||
use winapi;
|
||||
use kernel32;
|
||||
|
||||
const STATE_UNPARKED: usize = 0;
|
||||
const STATE_PARKED: usize = 1;
|
||||
const STATE_TIMED_OUT: usize = 2;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
struct KeyedEvent {
|
||||
pub struct KeyedEvent {
|
||||
handle: winapi::HANDLE,
|
||||
NtReleaseKeyedEvent: extern "system" fn(EventHandle: winapi::HANDLE,
|
||||
Key: winapi::PVOID,
|
||||
@ -42,51 +46,27 @@ impl KeyedEvent {
|
||||
(self.NtReleaseKeyedEvent)(self.handle, key, 0, ptr::null_mut())
|
||||
}
|
||||
|
||||
unsafe fn get() -> &'static KeyedEvent {
|
||||
static KEYED_EVENT: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||
|
||||
// Fast path: use the existing object
|
||||
let keyed_event = KEYED_EVENT.load(Ordering::Acquire);
|
||||
if keyed_event != 0 {
|
||||
return &*(keyed_event as *const KeyedEvent);
|
||||
};
|
||||
|
||||
// Try to create a new object
|
||||
let keyed_event = Box::into_raw(KeyedEvent::create());
|
||||
match KEYED_EVENT.compare_exchange(0,
|
||||
keyed_event as usize,
|
||||
Ordering::Release,
|
||||
Ordering::Relaxed) {
|
||||
Ok(_) => &*(keyed_event as *const KeyedEvent),
|
||||
Err(x) => {
|
||||
// We lost the race, free our object and return the global one
|
||||
Box::from_raw(keyed_event);
|
||||
&*(x as *const KeyedEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn create() -> Box<KeyedEvent> {
|
||||
pub unsafe fn create() -> Option<KeyedEvent> {
|
||||
let ntdll = kernel32::GetModuleHandleA(b"ntdll.dll\0".as_ptr() as winapi::LPCSTR);
|
||||
if ntdll.is_null() {
|
||||
panic!("Could not get module handle for ntdll.dll");
|
||||
return None;
|
||||
}
|
||||
|
||||
let NtCreateKeyedEvent =
|
||||
kernel32::GetProcAddress(ntdll, b"NtCreateKeyedEvent\0".as_ptr() as winapi::LPCSTR);
|
||||
if NtCreateKeyedEvent.is_null() {
|
||||
panic!("Entry point NtCreateKeyedEvent not found in ntdll.dll");
|
||||
return None;
|
||||
}
|
||||
let NtReleaseKeyedEvent =
|
||||
kernel32::GetProcAddress(ntdll, b"NtReleaseKeyedEvent\0".as_ptr() as winapi::LPCSTR);
|
||||
if NtReleaseKeyedEvent.is_null() {
|
||||
panic!("Entry point NtReleaseKeyedEvent not found in ntdll.dll");
|
||||
return None;
|
||||
}
|
||||
let NtWaitForKeyedEvent =
|
||||
kernel32::GetProcAddress(ntdll, b"NtWaitForKeyedEvent\0".as_ptr() as winapi::LPCSTR);
|
||||
if NtWaitForKeyedEvent.is_null() {
|
||||
panic!("Entry point NtWaitForKeyedEvent not found in ntdll.dll");
|
||||
return None;
|
||||
}
|
||||
|
||||
let NtCreateKeyedEvent: extern "system" fn(KeyedEventHandle: winapi::PHANDLE,
|
||||
@ -101,76 +81,37 @@ impl KeyedEvent {
|
||||
ptr::null_mut(),
|
||||
0);
|
||||
if status != winapi::STATUS_SUCCESS {
|
||||
panic!("NtCreateKeyedEvent failed: {:x}", status);
|
||||
return None;
|
||||
}
|
||||
|
||||
Box::new(KeyedEvent {
|
||||
Some(KeyedEvent {
|
||||
handle: handle,
|
||||
NtReleaseKeyedEvent: mem::transmute(NtReleaseKeyedEvent),
|
||||
NtWaitForKeyedEvent: mem::transmute(NtWaitForKeyedEvent),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for KeyedEvent {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let ok = kernel32::CloseHandle(self.handle);
|
||||
debug_assert_eq!(ok, winapi::TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const STATE_UNPARKED: usize = 0;
|
||||
const STATE_PARKED: usize = 1;
|
||||
const STATE_TIMED_OUT: usize = 2;
|
||||
|
||||
// Helper type for putting a thread to sleep until some other thread wakes it up
|
||||
pub struct ThreadParker {
|
||||
key: AtomicUsize,
|
||||
keyed_event: &'static KeyedEvent,
|
||||
}
|
||||
|
||||
impl ThreadParker {
|
||||
pub fn new() -> ThreadParker {
|
||||
// Initialize the keyed event here to ensure we don't get any panics
|
||||
// later on, which could leave synchronization primitives in a broken
|
||||
// state.
|
||||
ThreadParker {
|
||||
key: AtomicUsize::new(STATE_UNPARKED),
|
||||
keyed_event: unsafe { KeyedEvent::get() },
|
||||
}
|
||||
pub unsafe fn prepare_park(&'static self, key: &AtomicUsize) {
|
||||
key.store(STATE_PARKED, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
// Prepares the parker. This should be called before adding it to the queue.
|
||||
pub unsafe fn prepare_park(&self) {
|
||||
self.key.store(STATE_PARKED, Ordering::Relaxed);
|
||||
pub unsafe fn timed_out(&'static self, key: &AtomicUsize) -> bool {
|
||||
key.load(Ordering::Relaxed) == STATE_TIMED_OUT
|
||||
}
|
||||
|
||||
// Checks if the park timed out. This should be called while holding the
|
||||
// queue lock after park_until has returned false.
|
||||
pub unsafe fn timed_out(&self) -> bool {
|
||||
self.key.load(Ordering::Relaxed) == STATE_TIMED_OUT
|
||||
}
|
||||
|
||||
// Parks the thread until it is unparked. This should be called after it has
|
||||
// been added to the queue, after unlocking the queue.
|
||||
pub unsafe fn park(&self) {
|
||||
let status = self.keyed_event.wait_for(self as *const _ as winapi::PVOID, ptr::null_mut());
|
||||
pub unsafe fn park(&'static self, key: &AtomicUsize) {
|
||||
let status = self.wait_for(key as *const _ as winapi::PVOID, ptr::null_mut());
|
||||
debug_assert_eq!(status, winapi::STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
// Parks the thread until it is unparked or the timeout is reached. This
|
||||
// should be called after it has been added to the queue, after unlocking
|
||||
// the queue. Returns true if we were unparked and false if we timed out.
|
||||
pub unsafe fn park_until(&self, timeout: Instant) -> bool {
|
||||
pub unsafe fn park_until(&'static self, key: &AtomicUsize, timeout: Instant) -> bool {
|
||||
let now = Instant::now();
|
||||
if timeout <= now {
|
||||
// If another thread unparked us, we need to call
|
||||
// NtWaitForKeyedEvent otherwise that thread will stay stuck at
|
||||
// NtReleaseKeyedEvent.
|
||||
if self.key.swap(STATE_TIMED_OUT, Ordering::Relaxed) == STATE_UNPARKED {
|
||||
self.park();
|
||||
if key.swap(STATE_TIMED_OUT, Ordering::Relaxed) == STATE_UNPARKED {
|
||||
self.park(key);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -186,12 +127,12 @@ impl ThreadParker {
|
||||
Some(x) => x,
|
||||
None => {
|
||||
// Timeout overflowed, just sleep indefinitely
|
||||
self.park();
|
||||
self.park(key);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
let status = self.keyed_event.wait_for(self as *const _ as winapi::PVOID, &mut nt_timeout);
|
||||
let status = self.wait_for(key as *const _ as winapi::PVOID, &mut nt_timeout);
|
||||
if status == winapi::STATUS_SUCCESS {
|
||||
return true;
|
||||
}
|
||||
@ -199,22 +140,34 @@ impl ThreadParker {
|
||||
|
||||
// If another thread unparked us, we need to call NtWaitForKeyedEvent
|
||||
// otherwise that thread will stay stuck at NtReleaseKeyedEvent.
|
||||
if self.key.swap(STATE_TIMED_OUT, Ordering::Relaxed) == STATE_UNPARKED {
|
||||
self.park();
|
||||
if key.swap(STATE_TIMED_OUT, Ordering::Relaxed) == STATE_UNPARKED {
|
||||
self.park(key);
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
// Locks the parker to prevent the target thread from exiting. This is
|
||||
// necessary to ensure that thread-local ThreadData objects remain valid.
|
||||
// This should be called while holding the queue lock.
|
||||
pub unsafe fn unpark_lock(&self) -> UnparkHandle {
|
||||
pub unsafe fn unpark_lock(&'static self, key: &AtomicUsize) -> UnparkHandle {
|
||||
// If the state was STATE_PARKED then we need to wake up the thread
|
||||
if self.key.swap(STATE_UNPARKED, Ordering::Relaxed) == STATE_PARKED {
|
||||
UnparkHandle { thread_parker: self }
|
||||
if key.swap(STATE_UNPARKED, Ordering::Relaxed) == STATE_PARKED {
|
||||
UnparkHandle {
|
||||
key: key,
|
||||
keyed_event: self,
|
||||
}
|
||||
} else {
|
||||
UnparkHandle { thread_parker: ptr::null() }
|
||||
UnparkHandle {
|
||||
key: ptr::null(),
|
||||
keyed_event: self,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for KeyedEvent {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let ok = kernel32::CloseHandle(self.handle);
|
||||
debug_assert_eq!(ok, winapi::TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -223,17 +176,16 @@ impl ThreadParker {
|
||||
// as unparked while holding the queue lock, but we delay the actual unparking
|
||||
// until after the queue lock is released.
|
||||
pub struct UnparkHandle {
|
||||
thread_parker: *const ThreadParker,
|
||||
key: *const AtomicUsize,
|
||||
keyed_event: &'static KeyedEvent,
|
||||
}
|
||||
|
||||
impl UnparkHandle {
|
||||
// Wakes up the parked thread. This should be called after the queue lock is
|
||||
// released to avoid blocking the queue for too long.
|
||||
pub unsafe fn unpark(self) {
|
||||
if !self.thread_parker.is_null() {
|
||||
let status = (*self.thread_parker)
|
||||
.keyed_event
|
||||
.release(self.thread_parker as winapi::PVOID);
|
||||
if !self.key.is_null() {
|
||||
let status = self.keyed_event.release(self.key as winapi::PVOID);
|
||||
debug_assert_eq!(status, winapi::STATUS_SUCCESS);
|
||||
}
|
||||
}
|
137
third_party/rust/parking_lot_core/src/thread_parker/windows/mod.rs
vendored
Normal file
137
third_party/rust/parking_lot_core/src/thread_parker/windows/mod.rs
vendored
Normal file
@ -0,0 +1,137 @@
|
||||
// Copyright 2016 Amanieu d'Antras
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
// http://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.
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
use stable::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
|
||||
use std::time::Instant;
|
||||
|
||||
mod keyed_event;
|
||||
mod waitaddress;
|
||||
|
||||
enum Backend {
|
||||
KeyedEvent(keyed_event::KeyedEvent),
|
||||
WaitAddress(waitaddress::WaitAddress),
|
||||
}
|
||||
|
||||
impl Backend {
|
||||
unsafe fn get() -> &'static Backend {
|
||||
static BACKEND: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||
|
||||
// Fast path: use the existing object
|
||||
let backend = BACKEND.load(Ordering::Acquire);
|
||||
if backend != 0 {
|
||||
return &*(backend as *const Backend);
|
||||
};
|
||||
|
||||
// Try to create a new Backend
|
||||
let backend;
|
||||
if let Some(waitaddress) = waitaddress::WaitAddress::create() {
|
||||
backend = Backend::WaitAddress(waitaddress);
|
||||
} else if let Some(keyed_event) = keyed_event::KeyedEvent::create() {
|
||||
backend = Backend::KeyedEvent(keyed_event);
|
||||
} else {
|
||||
panic!("parking_lot requires either NT Keyed Events (WinXP+) or \
|
||||
WaitOnAddress/WakeByAddress (Win8+)");
|
||||
}
|
||||
|
||||
// Try to create a new object
|
||||
let backend = Box::into_raw(Box::new(backend));
|
||||
match BACKEND.compare_exchange(0, backend as usize, Ordering::Release, Ordering::Relaxed) {
|
||||
Ok(_) => &*(backend as *const Backend),
|
||||
Err(x) => {
|
||||
// We lost the race, free our object and return the global one
|
||||
Box::from_raw(backend);
|
||||
&*(x as *const Backend)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper type for putting a thread to sleep until some other thread wakes it up
|
||||
pub struct ThreadParker {
|
||||
key: AtomicUsize,
|
||||
backend: &'static Backend,
|
||||
}
|
||||
|
||||
impl ThreadParker {
|
||||
pub fn new() -> ThreadParker {
|
||||
// Initialize the backend here to ensure we don't get any panics
|
||||
// later on, which could leave synchronization primitives in a broken
|
||||
// state.
|
||||
ThreadParker {
|
||||
key: AtomicUsize::new(0),
|
||||
backend: unsafe { Backend::get() },
|
||||
}
|
||||
}
|
||||
|
||||
// Prepares the parker. This should be called before adding it to the queue.
|
||||
pub unsafe fn prepare_park(&self) {
|
||||
match *self.backend {
|
||||
Backend::KeyedEvent(ref x) => x.prepare_park(&self.key),
|
||||
Backend::WaitAddress(ref x) => x.prepare_park(&self.key),
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if the park timed out. This should be called while holding the
|
||||
// queue lock after park_until has returned false.
|
||||
pub unsafe fn timed_out(&self) -> bool {
|
||||
match *self.backend {
|
||||
Backend::KeyedEvent(ref x) => x.timed_out(&self.key),
|
||||
Backend::WaitAddress(ref x) => x.timed_out(&self.key),
|
||||
}
|
||||
}
|
||||
|
||||
// Parks the thread until it is unparked. This should be called after it has
|
||||
// been added to the queue, after unlocking the queue.
|
||||
pub unsafe fn park(&self) {
|
||||
match *self.backend {
|
||||
Backend::KeyedEvent(ref x) => x.park(&self.key),
|
||||
Backend::WaitAddress(ref x) => x.park(&self.key),
|
||||
}
|
||||
}
|
||||
|
||||
// Parks the thread until it is unparked or the timeout is reached. This
|
||||
// should be called after it has been added to the queue, after unlocking
|
||||
// the queue. Returns true if we were unparked and false if we timed out.
|
||||
pub unsafe fn park_until(&self, timeout: Instant) -> bool {
|
||||
match *self.backend {
|
||||
Backend::KeyedEvent(ref x) => x.park_until(&self.key, timeout),
|
||||
Backend::WaitAddress(ref x) => x.park_until(&self.key, timeout),
|
||||
}
|
||||
}
|
||||
|
||||
// Locks the parker to prevent the target thread from exiting. This is
|
||||
// necessary to ensure that thread-local ThreadData objects remain valid.
|
||||
// This should be called while holding the queue lock.
|
||||
pub unsafe fn unpark_lock(&self) -> UnparkHandle {
|
||||
match *self.backend {
|
||||
Backend::KeyedEvent(ref x) => UnparkHandle::KeyedEvent(x.unpark_lock(&self.key)),
|
||||
Backend::WaitAddress(ref x) => UnparkHandle::WaitAddress(x.unpark_lock(&self.key)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle for a thread that is about to be unparked. We need to mark the thread
|
||||
// as unparked while holding the queue lock, but we delay the actual unparking
|
||||
// until after the queue lock is released.
|
||||
pub enum UnparkHandle {
|
||||
KeyedEvent(keyed_event::UnparkHandle),
|
||||
WaitAddress(waitaddress::UnparkHandle),
|
||||
}
|
||||
|
||||
impl UnparkHandle {
|
||||
// Wakes up the parked thread. This should be called after the queue lock is
|
||||
// released to avoid blocking the queue for too long.
|
||||
pub unsafe fn unpark(self) {
|
||||
match self {
|
||||
UnparkHandle::KeyedEvent(x) => x.unpark(),
|
||||
UnparkHandle::WaitAddress(x) => x.unpark(),
|
||||
}
|
||||
}
|
||||
}
|
128
third_party/rust/parking_lot_core/src/thread_parker/windows/waitaddress.rs
vendored
Normal file
128
third_party/rust/parking_lot_core/src/thread_parker/windows/waitaddress.rs
vendored
Normal file
@ -0,0 +1,128 @@
|
||||
// Copyright 2016 Amanieu d'Antras
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
// http://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.
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
use stable::{AtomicUsize, Ordering};
|
||||
use std::time::Instant;
|
||||
use std::mem;
|
||||
use winapi;
|
||||
use kernel32;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub struct WaitAddress {
|
||||
WaitOnAddress: extern "system" fn(Address: winapi::PVOID,
|
||||
CompareAddress: winapi::PVOID,
|
||||
AddressSize: winapi::SIZE_T,
|
||||
dwMilliseconds: winapi::DWORD)
|
||||
-> winapi::BOOL,
|
||||
WakeByAddressSingle: extern "system" fn(Address: winapi::PVOID),
|
||||
}
|
||||
|
||||
impl WaitAddress {
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe fn create() -> Option<WaitAddress> {
|
||||
// MSDN claims that that WaitOnAddress and WakeByAddressSingle are
|
||||
// located in kernel32.dll, but they are lying...
|
||||
let synch_dll = kernel32::GetModuleHandleA(b"api-ms-win-core-synch-l1-2-0.dll\0"
|
||||
.as_ptr() as winapi::LPCSTR);
|
||||
if synch_dll.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let WaitOnAddress = kernel32::GetProcAddress(synch_dll,
|
||||
b"WaitOnAddress\0".as_ptr() as winapi::LPCSTR);
|
||||
if WaitOnAddress.is_null() {
|
||||
return None;
|
||||
}
|
||||
let WakeByAddressSingle = kernel32::GetProcAddress(synch_dll,
|
||||
b"WakeByAddressSingle\0".as_ptr() as
|
||||
winapi::LPCSTR);
|
||||
if WakeByAddressSingle.is_null() {
|
||||
return None;
|
||||
}
|
||||
Some(WaitAddress {
|
||||
WaitOnAddress: mem::transmute(WaitOnAddress),
|
||||
WakeByAddressSingle: mem::transmute(WakeByAddressSingle),
|
||||
})
|
||||
}
|
||||
|
||||
pub unsafe fn prepare_park(&'static self, key: &AtomicUsize) {
|
||||
key.store(1, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub unsafe fn timed_out(&'static self, key: &AtomicUsize) -> bool {
|
||||
key.load(Ordering::Relaxed) != 0
|
||||
}
|
||||
|
||||
pub unsafe fn park(&'static self, key: &AtomicUsize) {
|
||||
while key.load(Ordering::Acquire) != 0 {
|
||||
let cmp = 1usize;
|
||||
let r = (self.WaitOnAddress)(key as *const _ as winapi::PVOID,
|
||||
&cmp as *const _ as winapi::PVOID,
|
||||
mem::size_of::<usize>() as winapi::SIZE_T,
|
||||
winapi::INFINITE);
|
||||
debug_assert!(r == winapi::TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn park_until(&'static self, key: &AtomicUsize, timeout: Instant) -> bool {
|
||||
while key.load(Ordering::Acquire) != 0 {
|
||||
let now = Instant::now();
|
||||
if timeout <= now {
|
||||
return false;
|
||||
}
|
||||
let diff = timeout - now;
|
||||
let timeout = diff.as_secs()
|
||||
.checked_mul(1000)
|
||||
.and_then(|x| x.checked_add((diff.subsec_nanos() as u64 + 999999) / 1000000))
|
||||
.map(|ms| if ms > <winapi::DWORD>::max_value() as u64 {
|
||||
winapi::INFINITE
|
||||
} else {
|
||||
ms as winapi::DWORD
|
||||
})
|
||||
.unwrap_or(winapi::INFINITE);
|
||||
let cmp = 1usize;
|
||||
let r = (self.WaitOnAddress)(key as *const _ as winapi::PVOID,
|
||||
&cmp as *const _ as winapi::PVOID,
|
||||
mem::size_of::<usize>() as winapi::SIZE_T,
|
||||
timeout);
|
||||
if r == winapi::FALSE {
|
||||
debug_assert_eq!(kernel32::GetLastError(), winapi::ERROR_TIMEOUT);
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub unsafe fn unpark_lock(&'static self, key: &AtomicUsize) -> UnparkHandle {
|
||||
// We don't need to lock anything, just clear the state
|
||||
key.store(0, Ordering::Release);
|
||||
|
||||
UnparkHandle {
|
||||
key: key,
|
||||
waitaddress: self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Handle for a thread that is about to be unparked. We need to mark the thread
|
||||
// as unparked while holding the queue lock, but we delay the actual unparking
|
||||
// until after the queue lock is released.
|
||||
pub struct UnparkHandle {
|
||||
key: *const AtomicUsize,
|
||||
waitaddress: &'static WaitAddress,
|
||||
}
|
||||
|
||||
impl UnparkHandle {
|
||||
// Wakes up the parked thread. This should be called after the queue lock is
|
||||
// released to avoid blocking the queue for too long.
|
||||
pub unsafe fn unpark(self) {
|
||||
(self.waitaddress.WakeByAddressSingle)(self.key as winapi::PVOID);
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"f9b1ca6ae27d1c18215265024629a8960c31379f206d9ed20f64e0b2dcf79805",".travis.yml":"1e747fe88fd7f3851492acddd8a051ddb65fdf28bc66bce87635d091c5791c78","Cargo.toml":"129458de74d26efa7902323cd543b4e478aa4bcf3e5c76ebf95c93949686c564","README.md":"85c6105e404b1febba9e06773350cc81fe5966369530210669b3465c66237a09","lib.rs":"ae2fda3b1e527c9ff7afd01ecb2b747225376bd0c009e5012117a16fd17b0817"},"package":"fcc8d19212aacecf95e4a7a2179b26f7aeb9732a915cf01f05b0d3e044865410"}
|
2
third_party/rust/smallvec-0.1.8/.gitignore
vendored
2
third_party/rust/smallvec-0.1.8/.gitignore
vendored
@ -1,2 +0,0 @@
|
||||
target
|
||||
Cargo.lock
|
7
third_party/rust/smallvec-0.1.8/.travis.yml
vendored
7
third_party/rust/smallvec-0.1.8/.travis.yml
vendored
@ -1,7 +0,0 @@
|
||||
language: rust
|
||||
rust:
|
||||
- nightly
|
||||
- beta
|
||||
- stable
|
||||
notifications:
|
||||
webhooks: http://build.servo.org:54856/travis
|
15
third_party/rust/smallvec-0.1.8/Cargo.toml
vendored
15
third_party/rust/smallvec-0.1.8/Cargo.toml
vendored
@ -1,15 +0,0 @@
|
||||
[package]
|
||||
name = "smallvec"
|
||||
version = "0.1.8"
|
||||
authors = ["Simon Sapin <simon.sapin@exyr.org>"]
|
||||
license = "MPL-2.0"
|
||||
repository = "https://github.com/servo/rust-smallvec"
|
||||
description = "'Small vector' optimization: store up to a small number of items on the stack"
|
||||
keywords = ["small", "vec", "vector", "stack"]
|
||||
readme = "README.md"
|
||||
documentation = "http://doc.servo.org/smallvec/"
|
||||
|
||||
[lib]
|
||||
name = "smallvec"
|
||||
path = "lib.rs"
|
||||
doctest = false
|
6
third_party/rust/smallvec-0.1.8/README.md
vendored
6
third_party/rust/smallvec-0.1.8/README.md
vendored
@ -1,6 +0,0 @@
|
||||
rust-smallvec
|
||||
=============
|
||||
|
||||
[Documentation](http://doc.servo.org/smallvec/)
|
||||
|
||||
"Small vector" optimization for Rust: store up to a small number of items on the stack
|
672
third_party/rust/smallvec-0.1.8/lib.rs
vendored
672
third_party/rust/smallvec-0.1.8/lib.rs
vendored
@ -1,672 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! Small vectors in various sizes. These store a certain number of elements inline and fall back
|
||||
//! to the heap for larger allocations.
|
||||
|
||||
use std::cmp;
|
||||
use std::fmt;
|
||||
use std::iter::{IntoIterator, FromIterator};
|
||||
use std::mem;
|
||||
use std::ops;
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
|
||||
use SmallVecData::{Inline, Heap};
|
||||
|
||||
|
||||
pub trait VecLike<T>:
|
||||
ops::Index<usize, Output=T> +
|
||||
ops::IndexMut<usize> +
|
||||
ops::Index<ops::Range<usize>, Output=[T]> +
|
||||
ops::IndexMut<ops::Range<usize>> +
|
||||
ops::Index<ops::RangeFrom<usize>, Output=[T]> +
|
||||
ops::IndexMut<ops::RangeFrom<usize>> +
|
||||
ops::Index<ops::RangeTo<usize>, Output=[T]> +
|
||||
ops::IndexMut<ops::RangeTo<usize>> +
|
||||
ops::Index<ops::RangeFull, Output=[T]> +
|
||||
ops::IndexMut<ops::RangeFull> +
|
||||
ops::Deref +
|
||||
ops::DerefMut {
|
||||
|
||||
fn len(&self) -> usize;
|
||||
fn push(&mut self, value: T);
|
||||
}
|
||||
|
||||
impl<T> VecLike<T> for Vec<T> {
|
||||
#[inline]
|
||||
fn len(&self) -> usize {
|
||||
Vec::len(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn push(&mut self, value: T) {
|
||||
Vec::push(self, value);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn deallocate<T>(ptr: *mut T, capacity: usize) {
|
||||
let _vec: Vec<T> = Vec::from_raw_parts(ptr, 0, capacity);
|
||||
// Let it drop.
|
||||
}
|
||||
|
||||
pub struct SmallVecMoveIterator<'a, T: 'a> {
|
||||
iter: slice::IterMut<'a,T>,
|
||||
}
|
||||
|
||||
impl<'a, T: 'a> Iterator for SmallVecMoveIterator<'a,T> {
|
||||
type Item = T;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<T> {
|
||||
match self.iter.next() {
|
||||
None => None,
|
||||
Some(reference) => {
|
||||
unsafe {
|
||||
Some(ptr::read(reference))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'a> Drop for SmallVecMoveIterator<'a,T> {
|
||||
fn drop(&mut self) {
|
||||
// Destroy the remaining elements.
|
||||
for _ in self.by_ref() {}
|
||||
}
|
||||
}
|
||||
|
||||
enum SmallVecData<A: Array> {
|
||||
Inline { array: A },
|
||||
Heap { ptr: *mut A::Item, capacity: usize },
|
||||
}
|
||||
|
||||
unsafe impl<A: Array + Send> Send for SmallVecData<A> {}
|
||||
unsafe impl<A: Array + Sync> Sync for SmallVecData<A> {}
|
||||
|
||||
impl<A: Array> Drop for SmallVecData<A> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
match *self {
|
||||
ref mut inline @ Inline { .. } => {
|
||||
// Inhibit the array destructor.
|
||||
ptr::write(inline, Heap {
|
||||
ptr: ptr::null_mut(),
|
||||
capacity: 0,
|
||||
});
|
||||
}
|
||||
Heap { ptr, capacity } => deallocate(ptr, capacity),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct SmallVec<A: Array> {
|
||||
len: usize,
|
||||
data: SmallVecData<A>,
|
||||
}
|
||||
|
||||
impl<A: Array> SmallVec<A> {
|
||||
pub unsafe fn set_len(&mut self, new_len: usize) {
|
||||
self.len = new_len
|
||||
}
|
||||
|
||||
pub fn inline_size(&self) -> usize {
|
||||
A::size()
|
||||
}
|
||||
pub fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len == 0
|
||||
}
|
||||
pub fn capacity(&self) -> usize {
|
||||
match self.data {
|
||||
Inline { .. } => A::size(),
|
||||
Heap { capacity, .. } => capacity,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spilled(&self) -> bool {
|
||||
match self.data {
|
||||
Inline { .. } => false,
|
||||
Heap { .. } => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// NB: For efficiency reasons (avoiding making a second copy of the inline elements), this
|
||||
/// actually clears out the original array instead of moving it.
|
||||
/// FIXME: Rename this to `drain`? It’s more like `Vec::drain` than `Vec::into_iter`.
|
||||
pub fn into_iter<'a>(&'a mut self) -> SmallVecMoveIterator<'a, A::Item> {
|
||||
unsafe {
|
||||
let current_len = self.len();
|
||||
self.set_len(0);
|
||||
|
||||
let ptr = match self.data {
|
||||
Inline { ref mut array } => array.ptr_mut(),
|
||||
Heap { ptr, .. } => ptr,
|
||||
};
|
||||
|
||||
let slice = slice::from_raw_parts_mut(ptr, current_len);
|
||||
|
||||
SmallVecMoveIterator {
|
||||
iter: slice.iter_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, value: A::Item) {
|
||||
let cap = self.capacity();
|
||||
if self.len == cap {
|
||||
self.grow(cmp::max(cap * 2, 1))
|
||||
}
|
||||
unsafe {
|
||||
let end = self.as_mut_ptr().offset(self.len as isize);
|
||||
ptr::write(end, value);
|
||||
let len = self.len;
|
||||
self.set_len(len + 1)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_all_move<V: IntoIterator<Item=A::Item>>(&mut self, other: V) {
|
||||
for value in other {
|
||||
self.push(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pop(&mut self) -> Option<A::Item> {
|
||||
if self.len == 0 {
|
||||
return None
|
||||
}
|
||||
let last_index = self.len - 1;
|
||||
if (last_index as isize) < 0 {
|
||||
panic!("overflow")
|
||||
}
|
||||
unsafe {
|
||||
let end_ptr = self.as_mut_ptr().offset(last_index as isize);
|
||||
let value = ptr::replace(end_ptr, mem::uninitialized());
|
||||
self.set_len(last_index);
|
||||
Some(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn grow(&mut self, new_cap: usize) {
|
||||
let mut vec: Vec<A::Item> = Vec::with_capacity(new_cap);
|
||||
let new_alloc = vec.as_mut_ptr();
|
||||
unsafe {
|
||||
mem::forget(vec);
|
||||
ptr::copy_nonoverlapping(self.as_ptr(), new_alloc, self.len);
|
||||
|
||||
match self.data {
|
||||
Inline { .. } => {}
|
||||
Heap { ptr, capacity } => deallocate(ptr, capacity),
|
||||
}
|
||||
ptr::write(&mut self.data, Heap {
|
||||
ptr: new_alloc,
|
||||
capacity: new_cap,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reserve(&mut self, additional: usize) {
|
||||
let len = self.len();
|
||||
if self.capacity() - len < additional {
|
||||
match len.checked_add(additional).and_then(usize::checked_next_power_of_two) {
|
||||
Some(cap) => self.grow(cap),
|
||||
None => self.grow(usize::max_value()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reserve_exact(&mut self, additional: usize) {
|
||||
let len = self.len();
|
||||
if self.capacity() - len < additional {
|
||||
match len.checked_add(additional) {
|
||||
Some(cap) => self.grow(cap),
|
||||
None => panic!("reserve_exact overflow"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shrink_to_fit(&mut self) {
|
||||
let len = self.len;
|
||||
if self.spilled() && self.capacity() > len {
|
||||
self.grow(len);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn truncate(&mut self, len: usize) {
|
||||
let end_ptr = self.as_ptr();
|
||||
while len < self.len {
|
||||
unsafe {
|
||||
let last_index = self.len - 1;
|
||||
self.set_len(last_index);
|
||||
ptr::read(end_ptr.offset(last_index as isize));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn swap_remove(&mut self, index: usize) -> A::Item {
|
||||
let len = self.len;
|
||||
self.swap(len - 1, index);
|
||||
self.pop().unwrap()
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.truncate(0);
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, index: usize) -> A::Item {
|
||||
let len = self.len();
|
||||
|
||||
assert!(index < len);
|
||||
|
||||
unsafe {
|
||||
let ptr = self.as_mut_ptr().offset(index as isize);
|
||||
let item = ptr::read(ptr);
|
||||
ptr::copy(ptr.offset(1), ptr, len - index - 1);
|
||||
self.set_len(len - 1);
|
||||
item
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, index: usize, element: A::Item) {
|
||||
self.reserve(1);
|
||||
|
||||
let len = self.len;
|
||||
assert!(index <= len);
|
||||
|
||||
unsafe {
|
||||
let ptr = self.as_mut_ptr().offset(index as isize);
|
||||
ptr::copy(ptr, ptr.offset(1), len - index);
|
||||
ptr::write(ptr, element);
|
||||
self.set_len(len + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Array> ops::Deref for SmallVec<A> {
|
||||
type Target = [A::Item];
|
||||
#[inline]
|
||||
fn deref(&self) -> &[A::Item] {
|
||||
let ptr: *const _ = match self.data {
|
||||
Inline { ref array } => array.ptr(),
|
||||
Heap { ptr, .. } => ptr,
|
||||
};
|
||||
unsafe {
|
||||
slice::from_raw_parts(ptr, self.len)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Array> ops::DerefMut for SmallVec<A> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut [A::Item] {
|
||||
let ptr = match self.data {
|
||||
Inline { ref mut array } => array.ptr_mut(),
|
||||
Heap { ptr, .. } => ptr,
|
||||
};
|
||||
unsafe {
|
||||
slice::from_raw_parts_mut(ptr, self.len)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_index {
|
||||
($index_type: ty, $output_type: ty) => {
|
||||
impl<A: Array> ops::Index<$index_type> for SmallVec<A> {
|
||||
type Output = $output_type;
|
||||
#[inline]
|
||||
fn index(&self, index: $index_type) -> &$output_type {
|
||||
&(&**self)[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Array> ops::IndexMut<$index_type> for SmallVec<A> {
|
||||
#[inline]
|
||||
fn index_mut(&mut self, index: $index_type) -> &mut $output_type {
|
||||
&mut (&mut **self)[index]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_index!(usize, A::Item);
|
||||
impl_index!(ops::Range<usize>, [A::Item]);
|
||||
impl_index!(ops::RangeFrom<usize>, [A::Item]);
|
||||
impl_index!(ops::RangeTo<usize>, [A::Item]);
|
||||
impl_index!(ops::RangeFull, [A::Item]);
|
||||
|
||||
|
||||
impl<A: Array> VecLike<A::Item> for SmallVec<A> {
|
||||
#[inline]
|
||||
fn len(&self) -> usize {
|
||||
SmallVec::len(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn push(&mut self, value: A::Item) {
|
||||
SmallVec::push(self, value);
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Array> FromIterator<A::Item> for SmallVec<A> {
|
||||
fn from_iter<I: IntoIterator<Item=A::Item>>(iterable: I) -> SmallVec<A> {
|
||||
let mut v = SmallVec::new();
|
||||
v.extend(iterable);
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Array> SmallVec<A> {
|
||||
pub fn extend<I: IntoIterator<Item=A::Item>>(&mut self, iterable: I) {
|
||||
let iter = iterable.into_iter();
|
||||
let (lower_size_bound, _) = iter.size_hint();
|
||||
|
||||
let target_len = self.len + lower_size_bound;
|
||||
|
||||
if target_len > self.capacity() {
|
||||
self.grow(target_len);
|
||||
}
|
||||
|
||||
for elem in iter {
|
||||
self.push(elem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Array> fmt::Debug for SmallVec<A> where A::Item: fmt::Debug {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", &**self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Array> SmallVec<A> {
|
||||
#[inline]
|
||||
pub fn new() -> SmallVec<A> {
|
||||
unsafe {
|
||||
SmallVec {
|
||||
len: 0,
|
||||
data: Inline { array: mem::zeroed() },
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Array> Drop for SmallVec<A> {
|
||||
fn drop(&mut self) {
|
||||
// Note on panic safety: dropping an element may panic,
|
||||
// but the inner SmallVecData destructor will still run
|
||||
unsafe {
|
||||
let ptr = self.as_ptr();
|
||||
for i in 0 .. self.len {
|
||||
ptr::read(ptr.offset(i as isize));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Array> Clone for SmallVec<A> where A::Item: Clone {
|
||||
fn clone(&self) -> SmallVec<A> {
|
||||
let mut new_vector = SmallVec::new();
|
||||
for element in self.iter() {
|
||||
new_vector.push((*element).clone())
|
||||
}
|
||||
new_vector
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Array, B: Array> PartialEq<SmallVec<B>> for SmallVec<A>
|
||||
where A::Item: PartialEq<B::Item> {
|
||||
#[inline]
|
||||
fn eq(&self, other: &SmallVec<B>) -> bool { self[..] == other[..] }
|
||||
#[inline]
|
||||
fn ne(&self, other: &SmallVec<B>) -> bool { self[..] != other[..] }
|
||||
}
|
||||
|
||||
impl<A: Array> Eq for SmallVec<A> where A::Item: Eq {}
|
||||
|
||||
impl<A: Array> PartialOrd for SmallVec<A> where A::Item: PartialOrd {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &SmallVec<A>) -> Option<cmp::Ordering> {
|
||||
PartialOrd::partial_cmp(&**self, &**other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Array> Ord for SmallVec<A> where A::Item: Ord {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &SmallVec<A>) -> cmp::Ordering {
|
||||
Ord::cmp(&**self, &**other)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<A: Array> Send for SmallVec<A> where A::Item: Send {}
|
||||
|
||||
// TODO: Remove these and its users.
|
||||
|
||||
/// Deprecated alias to ease transition from an earlier version.
|
||||
pub type SmallVec1<T> = SmallVec<[T; 1]>;
|
||||
|
||||
/// Deprecated alias to ease transition from an earlier version.
|
||||
pub type SmallVec2<T> = SmallVec<[T; 2]>;
|
||||
|
||||
/// Deprecated alias to ease transition from an earlier version.
|
||||
pub type SmallVec4<T> = SmallVec<[T; 4]>;
|
||||
|
||||
/// Deprecated alias to ease transition from an earlier version.
|
||||
pub type SmallVec8<T> = SmallVec<[T; 8]>;
|
||||
|
||||
/// Deprecated alias to ease transition from an earlier version.
|
||||
pub type SmallVec16<T> = SmallVec<[T; 16]>;
|
||||
|
||||
/// Deprecated alias to ease transition from an earlier version.
|
||||
pub type SmallVec24<T> = SmallVec<[T; 24]>;
|
||||
|
||||
/// Deprecated alias to ease transition from an earlier version.
|
||||
pub type SmallVec32<T> = SmallVec<[T; 32]>;
|
||||
|
||||
|
||||
pub unsafe trait Array {
|
||||
type Item;
|
||||
fn size() -> usize;
|
||||
fn ptr(&self) -> *const Self::Item;
|
||||
fn ptr_mut(&mut self) -> *mut Self::Item;
|
||||
}
|
||||
|
||||
macro_rules! impl_array(
|
||||
($($size:expr),+) => {
|
||||
$(
|
||||
unsafe impl<T> Array for [T; $size] {
|
||||
type Item = T;
|
||||
fn size() -> usize { $size }
|
||||
fn ptr(&self) -> *const T { &self[0] }
|
||||
fn ptr_mut(&mut self) -> *mut T { &mut self[0] }
|
||||
}
|
||||
)+
|
||||
}
|
||||
);
|
||||
|
||||
impl_array!(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 32,
|
||||
0x40, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000,
|
||||
0x10000, 0x20000, 0x40000, 0x80000, 0x100000);
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use SmallVec;
|
||||
use std::borrow::ToOwned;
|
||||
|
||||
// We heap allocate all these strings so that double frees will show up under valgrind.
|
||||
|
||||
#[test]
|
||||
pub fn test_inline() {
|
||||
let mut v = SmallVec::<[_; 16]>::new();
|
||||
v.push("hello".to_owned());
|
||||
v.push("there".to_owned());
|
||||
assert_eq!(&*v, &[
|
||||
"hello".to_owned(),
|
||||
"there".to_owned(),
|
||||
][..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_spill() {
|
||||
let mut v = SmallVec::<[_; 2]>::new();
|
||||
v.push("hello".to_owned());
|
||||
assert_eq!(v[0], "hello");
|
||||
v.push("there".to_owned());
|
||||
v.push("burma".to_owned());
|
||||
assert_eq!(v[0], "hello");
|
||||
v.push("shave".to_owned());
|
||||
assert_eq!(&*v, &[
|
||||
"hello".to_owned(),
|
||||
"there".to_owned(),
|
||||
"burma".to_owned(),
|
||||
"shave".to_owned(),
|
||||
][..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_double_spill() {
|
||||
let mut v = SmallVec::<[_; 2]>::new();
|
||||
v.push("hello".to_owned());
|
||||
v.push("there".to_owned());
|
||||
v.push("burma".to_owned());
|
||||
v.push("shave".to_owned());
|
||||
v.push("hello".to_owned());
|
||||
v.push("there".to_owned());
|
||||
v.push("burma".to_owned());
|
||||
v.push("shave".to_owned());
|
||||
assert_eq!(&*v, &[
|
||||
"hello".to_owned(),
|
||||
"there".to_owned(),
|
||||
"burma".to_owned(),
|
||||
"shave".to_owned(),
|
||||
"hello".to_owned(),
|
||||
"there".to_owned(),
|
||||
"burma".to_owned(),
|
||||
"shave".to_owned(),
|
||||
][..]);
|
||||
}
|
||||
|
||||
/// https://github.com/servo/rust-smallvec/issues/4
|
||||
#[test]
|
||||
fn issue_4() {
|
||||
SmallVec::<[Box<u32>; 2]>::new();
|
||||
}
|
||||
|
||||
/// https://github.com/servo/rust-smallvec/issues/5
|
||||
#[test]
|
||||
fn issue_5() {
|
||||
assert!(Some(SmallVec::<[&u32; 2]>::new()).is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn into_iter() {
|
||||
let mut v: SmallVec<[u8; 2]> = SmallVec::new();
|
||||
v.push(3);
|
||||
assert_eq!(v.into_iter().collect::<Vec<_>>(), &[3]);
|
||||
|
||||
// spilling the vec
|
||||
v.push(3);
|
||||
v.push(4);
|
||||
v.push(5);
|
||||
assert_eq!(v.into_iter().collect::<Vec<_>>(), &[3, 4, 5]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_capacity() {
|
||||
let mut v: SmallVec<[u8; 2]> = SmallVec::new();
|
||||
v.reserve(1);
|
||||
assert_eq!(v.capacity(), 2);
|
||||
assert!(!v.spilled());
|
||||
|
||||
v.reserve_exact(0x100);
|
||||
assert!(v.capacity() >= 0x100);
|
||||
|
||||
v.push(0);
|
||||
v.push(1);
|
||||
v.push(2);
|
||||
v.push(3);
|
||||
|
||||
v.shrink_to_fit();
|
||||
assert!(v.capacity() < 0x100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_truncate() {
|
||||
let mut v: SmallVec<[Box<u8>; 8]> = SmallVec::new();
|
||||
|
||||
for x in 0..8 {
|
||||
v.push(Box::new(x));
|
||||
}
|
||||
v.truncate(4);
|
||||
|
||||
assert_eq!(v.len(), 4);
|
||||
assert!(!v.spilled());
|
||||
|
||||
assert_eq!(*v.swap_remove(1), 1);
|
||||
assert_eq!(*v.remove(1), 3);
|
||||
v.insert(1, Box::new(3));
|
||||
|
||||
assert_eq!(&v.iter().map(|v| **v).collect::<Vec<_>>(), &[0, 3, 2]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_drop_panic_smallvec() {
|
||||
// This test should only panic once, and not double panic,
|
||||
// which would mean a double drop
|
||||
struct DropPanic;
|
||||
|
||||
impl Drop for DropPanic {
|
||||
fn drop(&mut self) {
|
||||
panic!("drop");
|
||||
}
|
||||
}
|
||||
|
||||
let mut v = SmallVec::<[_; 1]>::new();
|
||||
v.push(DropPanic);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eq() {
|
||||
let mut a: SmallVec<[u32; 2]> = SmallVec::new();
|
||||
let mut b: SmallVec<[u32; 2]> = SmallVec::new();
|
||||
let mut c: SmallVec<[u32; 2]> = SmallVec::new();
|
||||
// a = [1, 2]
|
||||
a.push(1);
|
||||
a.push(2);
|
||||
// b = [1, 2]
|
||||
b.push(1);
|
||||
b.push(2);
|
||||
// c = [3, 4]
|
||||
c.push(3);
|
||||
c.push(4);
|
||||
|
||||
assert!(a == b);
|
||||
assert!(a != c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ord() {
|
||||
let mut a: SmallVec<[u32; 2]> = SmallVec::new();
|
||||
let mut b: SmallVec<[u32; 2]> = SmallVec::new();
|
||||
let mut c: SmallVec<[u32; 2]> = SmallVec::new();
|
||||
// a = [1]
|
||||
a.push(1);
|
||||
// b = [1, 1]
|
||||
b.push(1);
|
||||
b.push(1);
|
||||
// c = [1, 2]
|
||||
c.push(1);
|
||||
c.push(2);
|
||||
|
||||
assert!(a < b);
|
||||
assert!(b > a);
|
||||
assert!(b < c);
|
||||
assert!(c > b);
|
||||
}
|
||||
}
|
14
toolkit/library/gtest/rust/Cargo.lock
generated
14
toolkit/library/gtest/rust/Cargo.lock
generated
@ -547,19 +547,19 @@ version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"owning_ref 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -802,11 +802,6 @@ name = "siphasher"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "0.3.2"
|
||||
@ -1230,7 +1225,7 @@ dependencies = [
|
||||
"checksum osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b"
|
||||
"checksum owning_ref 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9d52571ddcb42e9c900c901a18d8d67e393df723fcd51dd59c5b1a85d0acb6cc"
|
||||
"checksum parking_lot 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fa12d706797d42551663426a45e2db2e0364bd1dbf6aeada87e89c5f981f43e9"
|
||||
"checksum parking_lot_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb1b97670a2ffadce7c397fb80a3d687c4f3060140b885621ef1653d0e5d5068"
|
||||
"checksum parking_lot_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "56a19dcbb5d1e32b6cccb8a9aa1fc2a38418c8699652e735e2bf391a3dc0aa16"
|
||||
"checksum pdqsort 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ceca1642c89148ca05611cc775a0c383abef355fc4907c4e95f49f7b09d6287c"
|
||||
"checksum phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cb325642290f28ee14d8c6201159949a872f220c62af6e110a56ea914fbe42fc"
|
||||
"checksum phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d62594c0bb54c464f633175d502038177e90309daf2e0158be42ed5f023ce88f"
|
||||
@ -1254,7 +1249,6 @@ dependencies = [
|
||||
"checksum serde_json 0.9.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6501ac6f8b74f9b1033f7ddf79a08edfa0f58d6f8e3190cb8dc97736afa257a8"
|
||||
"checksum shared_library 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fb04126b6fcfd2710fb5b6d18f4207b6c535f2850a7e1a43bcd526d44f30a79a"
|
||||
"checksum siphasher 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2ffc669b726f2bc9a3bcff66e5e23b56ba6bf70e22a34c3d7b6d0b3450b65b84"
|
||||
"checksum smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fcc8d19212aacecf95e4a7a2179b26f7aeb9732a915cf01f05b0d3e044865410"
|
||||
"checksum smallvec 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dca03f2f42500a9ef8ac0d16183dff8bed40e3dcf98f9d4147928548d5c4236e"
|
||||
"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
|
||||
"checksum syn 0.11.8 (registry+https://github.com/rust-lang/crates.io-index)" = "37c279fb816210c9bb28b2c292664581e7b87b4561e86b94df462664d8620bb8"
|
||||
|
14
toolkit/library/rust/Cargo.lock
generated
14
toolkit/library/rust/Cargo.lock
generated
@ -534,19 +534,19 @@ version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"owning_ref 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -789,11 +789,6 @@ name = "siphasher"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "0.3.2"
|
||||
@ -1217,7 +1212,7 @@ dependencies = [
|
||||
"checksum osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b"
|
||||
"checksum owning_ref 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9d52571ddcb42e9c900c901a18d8d67e393df723fcd51dd59c5b1a85d0acb6cc"
|
||||
"checksum parking_lot 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fa12d706797d42551663426a45e2db2e0364bd1dbf6aeada87e89c5f981f43e9"
|
||||
"checksum parking_lot_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb1b97670a2ffadce7c397fb80a3d687c4f3060140b885621ef1653d0e5d5068"
|
||||
"checksum parking_lot_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "56a19dcbb5d1e32b6cccb8a9aa1fc2a38418c8699652e735e2bf391a3dc0aa16"
|
||||
"checksum pdqsort 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ceca1642c89148ca05611cc775a0c383abef355fc4907c4e95f49f7b09d6287c"
|
||||
"checksum phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cb325642290f28ee14d8c6201159949a872f220c62af6e110a56ea914fbe42fc"
|
||||
"checksum phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d62594c0bb54c464f633175d502038177e90309daf2e0158be42ed5f023ce88f"
|
||||
@ -1241,7 +1236,6 @@ dependencies = [
|
||||
"checksum serde_json 0.9.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6501ac6f8b74f9b1033f7ddf79a08edfa0f58d6f8e3190cb8dc97736afa257a8"
|
||||
"checksum shared_library 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fb04126b6fcfd2710fb5b6d18f4207b6c535f2850a7e1a43bcd526d44f30a79a"
|
||||
"checksum siphasher 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2ffc669b726f2bc9a3bcff66e5e23b56ba6bf70e22a34c3d7b6d0b3450b65b84"
|
||||
"checksum smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fcc8d19212aacecf95e4a7a2179b26f7aeb9732a915cf01f05b0d3e044865410"
|
||||
"checksum smallvec 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dca03f2f42500a9ef8ac0d16183dff8bed40e3dcf98f9d4147928548d5c4236e"
|
||||
"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
|
||||
"checksum syn 0.11.8 (registry+https://github.com/rust-lang/crates.io-index)" = "37c279fb816210c9bb28b2c292664581e7b87b4561e86b94df462664d8620bb8"
|
||||
|
Loading…
Reference in New Issue
Block a user