<commit_before>//! Concurrent, atomic options.

use std::{mem, ptr};
use std::sync::atomic::{self, AtomicPtr};

use local;
use garbage::Garbage;
use guard::Guard;

/// A concurrently accessible and updatable optional pointer.
///
/// This acts as a kind of concurrent `Option<T>`.  It can be compared to `std::cell::RefCell` in
/// some ways: It allows accessing, referencing, updating, etc., however contrary to `RefCell`,
/// this is concurrent and has no aliasing restrictions. It is further distinguished from
/// `std::sync::AtomicPtr` in that it allows references to the inner data without the ABA problem
/// or any variant thereof.
///
/// It conveniently wraps this crates API in a seemless manner.
#[derive(Default)]
pub struct AtomicOption<T> {
    /// The inner atomic pointer.
    inner: AtomicPtr<T>,
}

impl<T> AtomicOption<T> {
    //// Create a new concurrent option.
    pub fn new(init: Option<Box<T>>) -> AtomicOption<T> {
        AtomicOption {
            // Convert the box to a raw pointer.
            inner: AtomicPtr::new(init.map_or(ptr::null_mut(), Box::into_raw)),
        }
    }

    /// Get a reference to the current content of the option.
    ///
    /// This returns a `Guard<T>`, which "protects" the inner value such that it is not dropped
    /// before the guard is no longer active. This is all handled automatically through RAII.
    ///
    /// The `ordering` defines what constraints the atomic operation has. Refer to the LLVM
    /// documentation for more information.
    pub fn load(&self, ordering: atomic::Ordering) -> Option<Guard<T>> {
        // Load the inner and wrap it in a guard.
        Guard::maybe_new(|| unsafe {
            self.inner.load(ordering).as_ref()
        })
    }

    /// Store a new value in the option.
    ///
    /// The old value of `self` will eventually be dropped, at some point after all the guarding
    /// references are gone.
    ///
    /// The `ordering` defines what constraints the atomic operation has. Refer to the LLVM
    /// documentation for more information.
    pub fn store(&self, new: Option<Box<T>>, ordering: atomic::Ordering) {
        // Transform the optional box to a (possibly null) pointer.
        // TODO: Use coercions.
        let new = new.map_or(ptr::null_mut(), |new| Box::into_raw(new));
        // Swap the contents with the new value.
        let ptr = self.inner.swap(new, ordering);
        if !ptr.is_null() {
            // Queue the deletion of the content.
            local::add_garbage(unsafe { Garbage::new_box(ptr) });
        }
    }

    /// Swap the old value with a new.
    ///
    /// This returns a `Guard<T>` as readers of the old values might exist. The old value will be
    /// queued for destruction.
    ///
    /// The `ordering` defines what constraints the atomic operation has. Refer to the LLVM
    /// documentation for more information.
    ///
    /// # Performance
    ///
    /// This is slower than `store` as it requires initializing a new guard, which requires at
    /// least two atomic operations. Thus, when possible, you should use `store`.
    pub fn swap(&self, new: Option<Box<T>>, ordering: atomic::Ordering) -> Option<Guard<T>> {
        // Convert `new` into a raw pointer.
        // TODO: Use coercions.
        let new_ptr = new.map_or(ptr::null_mut(), Box::into_raw);

        // Create the guard. It is very important that this is done before the garbage is added,
        // otherwise we might introduce premature frees.
        Guard::maybe_new(|| unsafe {
            // Swap the atomic pointer with the new one.
            self.inner.swap(new_ptr, ordering).as_ref()
        }).map(|guard| {
            // Since the pointer is now unreachable from the option, it can safely be queued for
            // deletion.
            local::add_garbage(unsafe { Garbage::new_box(&*guard) });

            guard
        })
    }

    /// Store a value if the current matches a particular value.
    ///
    /// This compares `self` to `old`. If they match, the value is set to `new` and `Ok(())` is
    /// returned. Otherwise, `Err(new)` is returned.
    ///
    /// The `ordering` defines what constraints the atomic operation has. Refer to the LLVM
    /// documentation for more information.
    pub fn compare_and_store(&self, old: Option<*const T>, mut new: Option<Box<T>>, ordering: atomic::Ordering)
    -> Result<(), Option<Box<T>>> {
        // Convert the parameters to raw pointers.
        // TODO: Use coercions.
        let new_ptr = new.as_mut().map_or(ptr::null_mut(), |x| &mut **x);
        let old_ptr = old.map_or(ptr::null_mut(), |x| x as *mut T);

        // Compare-and-swap the value.
        let ptr = self.inner.compare_and_swap(old_ptr, new_ptr, ordering);

        // Check if the CAS was successful.
        if ptr == old_ptr {
            // It was. `self` is now `new`.

            // Ensure that the destructor of `new` is not run.
            mem::forget(new);

            // Queue the deletion of now-unreachable `old` (unless it's `None`).
            if !old_ptr.is_null() {
                local::add_garbage(unsafe { Garbage::new_box(old_ptr) });
            }

            Ok(())
        } else {
            // It failed.
            Err(new)
        }
    }

    /// Swap a value if it matches.
    ///
    /// This compares `self` to `old`. If they match, it is swapped with `new` and a guard to the
    /// old value is returned wrapped in `Ok`. If not, a tuple containing the guard to the actual
    /// (non-matching) value and the box of `new` — wrapped in `Err` — is returned.
    ///
    /// The `ordering` defines what constraints the atomic operation has. Refer to the LLVM
    /// documentation for more information.
    ///
    /// # Performance
    ///
    /// This is slower than `compare_and_set` as it requires initializing a new guard, which
    /// requires at least two atomic operations. Thus, when possible, you should use
    /// `compare_and_set`.
    pub fn compare_and_swap(&self, old: Option<*const T>, mut new: Option<Box<T>>, ordering: atomic::Ordering)
    -> Result<Option<Guard<T>>, (Option<Guard<T>>, Option<Box<T>>)> {
        // Convert the parameters to raw pointers.
        // TODO: Use coercions.
        let new_ptr = new.as_mut().map_or(ptr::null_mut(), |x| &mut **x);
        let old_ptr = old.map_or(ptr::null_mut(), |x| x as *mut T);

        // Create the guard beforehand to avoid premature frees.
        let guard = Guard::maybe_new(|| {
            // The guard is active, so we can do the CAS now.
            unsafe { self.inner.compare_and_swap(old_ptr, new_ptr, ordering).as_ref() }
        });

        // Convert the guard to a raw pointer.
        // TODO: Use coercions.
        let guard_ptr = guard.as_ref().map_or(ptr::null_mut(), |x| &**x as *const T as *mut T);

        // Check if the CAS was successful.
        if guard_ptr == old_ptr {
            // It was. `self` is now `new`.

            // Ensure that the destructor of `new` is not run.
            mem::forget(new);

            // Queue the deletion of now-unreachable `old` (unless it's `None`).
            if !old_ptr.is_null() {
                local::add_garbage(unsafe { Garbage::new_box(old_ptr) });
            }

            Ok(guard)
        } else {
            // It failed; cast the raw pointer back to a box and return.
            Err((guard, new))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use std::sync::{atomic, Arc};
    use std::sync::atomic::AtomicUsize;
    use std::thread;

    struct Basic;

    impl Drop for Basic {
        fn drop(&mut self) {
            basic();
        }
    }

    thread_local! {
        static BASIC: Basic = Basic;
    }

    fn basic() {
        let opt = AtomicOption::default();
        assert!(opt.load(atomic::Ordering::Relaxed).is_none());
        assert!(opt.swap(None, atomic::Ordering::Relaxed).is_none());
        assert!(opt.load(atomic::Ordering::Relaxed).is_none());
        assert!(opt.swap(Some(Box::new(42)), atomic::Ordering::Relaxed).is_none());
        assert_eq!(*opt.load(atomic::Ordering::Relaxed).unwrap(), 42);
        assert_eq!(*opt.swap(Some(Box::new(43)), atomic::Ordering::Relaxed).unwrap(), 42);
        assert_eq!(*opt.load(atomic::Ordering::Relaxed).unwrap(), 43);
    }

    #[test]
    fn basic_properties() {
        basic()
    }

    #[test]
    fn cas() {
        let bx1 = Box::new(1);
        let ptr1 = &*bx1 as *const usize;
        let bx2 = Box::new(1);
        let ptr2 = &*bx2 as *const usize;

        let opt = AtomicOption::new(Some(bx1));
        assert_eq!(ptr1, &*opt.compare_and_swap(Some(ptr2), None, atomic::Ordering::Relaxed).unwrap_err().0.unwrap());
        assert_eq!(ptr1, &*opt.load(atomic::Ordering::Relaxed).unwrap());

        assert_eq!(ptr1, &*opt.compare_and_swap(None, Some(Box::new(2)), atomic::Ordering::Relaxed).unwrap_err().0.unwrap());
        assert_eq!(ptr1, &*opt.load(atomic::Ordering::Relaxed).unwrap());

        opt.compare_and_swap(Some(ptr1), None, atomic::Ordering::Relaxed).unwrap();
        assert!(opt.load(atomic::Ordering::Relaxed).is_none());

        opt.compare_and_swap(None, Some(bx2), atomic::Ordering::Relaxed).unwrap();
        assert_eq!(ptr2, &*opt.load(atomic::Ordering::Relaxed).unwrap());

        opt.compare_and_store(Some(ptr2), None, atomic::Ordering::Relaxed).unwrap();
        opt.compare_and_store(Some(Box::into_raw(Box::new(2))), None, atomic::Ordering::Relaxed).unwrap_err();

        assert!(opt.load(atomic::Ordering::Relaxed).is_none());

        // To check that GC doesn't segfault or something.
        ::gc();
        ::gc();
        ::gc();
        ::gc();
    }

    #[test]
    fn spam() {
        let opt = Arc::new(AtomicOption::default());

        let mut j = Vec::new();
        for _ in 0..16 {
            let opt = opt.clone();
            j.push(thread::spawn(move || {
                for i in 0..1_000_001 {
                    let _ = opt.load(atomic::Ordering::Relaxed);
                    opt.store(Some(Box::new(i)), atomic::Ordering::Relaxed);
                }
                opt
            }))
        }

        ::gc();
        ::gc();

        for i in j {
            i.join().unwrap();
        }

        assert_eq!(*opt.load(atomic::Ordering::Relaxed).unwrap(), 1_000_000);
    }

    #[test]
    fn drop() {
        #[derive(Clone)]
        struct Dropper {
            d: Arc<AtomicUsize>,
        }

        impl Drop for Dropper {
            fn drop(&mut self) {
                self.d.fetch_add(1, atomic::Ordering::Relaxed);
            }
        }

        let drops = Arc::new(AtomicUsize::default());
        let opt = Arc::new(AtomicOption::new(None));

        let d = Dropper {
            d: drops.clone(),
        };

        let mut j = Vec::new();
        for _ in 0..16 {
            let d = d.clone();
            let opt = opt.clone();

            j.push(thread::spawn(move || {
                for _ in 0..1_000_000 {
                    opt.store(Some(Box::new(d.clone())), atomic::Ordering::Relaxed);
                }
            }))
        }

        for i in j {
            i.join().unwrap();
        }

        opt.store(None, atomic::Ordering::Relaxed);

        ::gc();

        // The 16 are for the `d` variable in the loop above.
        assert_eq!(drops.load(atomic::Ordering::Relaxed), 16_000_000 + 16);
    }

    #[test]
    fn tls() {
        thread::spawn(|| BASIC.with(|_| {})).join().unwrap();
        thread::spawn(|| BASIC.with(|_| {})).join().unwrap();
        thread::spawn(|| BASIC.with(|_| {})).join().unwrap();
        thread::spawn(|| BASIC.with(|_| {})).join().unwrap();
    }
}
<commit_msg>Remove traces of `compare_and_set` in the docs.<commit_after>//! Concurrent, atomic options.

use std::{mem, ptr};
use std::sync::atomic::{self, AtomicPtr};

use local;
use garbage::Garbage;
use guard::Guard;

/// A concurrently accessible and updatable optional pointer.
///
/// This acts as a kind of concurrent `Option<T>`.  It can be compared to `std::cell::RefCell` in
/// some ways: It allows accessing, referencing, updating, etc., however contrary to `RefCell`,
/// this is concurrent and has no aliasing restrictions. It is further distinguished from
/// `std::sync::AtomicPtr` in that it allows references to the inner data without the ABA problem
/// or any variant thereof.
///
/// It conveniently wraps this crates API in a seemless manner.
#[derive(Default)]
pub struct AtomicOption<T> {
    /// The inner atomic pointer.
    inner: AtomicPtr<T>,
}

impl<T> AtomicOption<T> {
    //// Create a new concurrent option.
    pub fn new(init: Option<Box<T>>) -> AtomicOption<T> {
        AtomicOption {
            // Convert the box to a raw pointer.
            inner: AtomicPtr::new(init.map_or(ptr::null_mut(), Box::into_raw)),
        }
    }

    /// Get a reference to the current content of the option.
    ///
    /// This returns a `Guard<T>`, which "protects" the inner value such that it is not dropped
    /// before the guard is no longer active. This is all handled automatically through RAII.
    ///
    /// The `ordering` defines what constraints the atomic operation has. Refer to the LLVM
    /// documentation for more information.
    pub fn load(&self, ordering: atomic::Ordering) -> Option<Guard<T>> {
        // Load the inner and wrap it in a guard.
        Guard::maybe_new(|| unsafe {
            self.inner.load(ordering).as_ref()
        })
    }

    /// Store a new value in the option.
    ///
    /// The old value of `self` will eventually be dropped, at some point after all the guarding
    /// references are gone.
    ///
    /// The `ordering` defines what constraints the atomic operation has. Refer to the LLVM
    /// documentation for more information.
    pub fn store(&self, new: Option<Box<T>>, ordering: atomic::Ordering) {
        // Transform the optional box to a (possibly null) pointer.
        // TODO: Use coercions.
        let new = new.map_or(ptr::null_mut(), |new| Box::into_raw(new));
        // Swap the contents with the new value.
        let ptr = self.inner.swap(new, ordering);
        if !ptr.is_null() {
            // Queue the deletion of the content.
            local::add_garbage(unsafe { Garbage::new_box(ptr) });
        }
    }

    /// Swap the old value with a new.
    ///
    /// This returns a `Guard<T>` as readers of the old values might exist. The old value will be
    /// queued for destruction.
    ///
    /// The `ordering` defines what constraints the atomic operation has. Refer to the LLVM
    /// documentation for more information.
    ///
    /// # Performance
    ///
    /// This is slower than `store` as it requires initializing a new guard, which requires at
    /// least two atomic operations. Thus, when possible, you should use `store`.
    pub fn swap(&self, new: Option<Box<T>>, ordering: atomic::Ordering) -> Option<Guard<T>> {
        // Convert `new` into a raw pointer.
        // TODO: Use coercions.
        let new_ptr = new.map_or(ptr::null_mut(), Box::into_raw);

        // Create the guard. It is very important that this is done before the garbage is added,
        // otherwise we might introduce premature frees.
        Guard::maybe_new(|| unsafe {
            // Swap the atomic pointer with the new one.
            self.inner.swap(new_ptr, ordering).as_ref()
        }).map(|guard| {
            // Since the pointer is now unreachable from the option, it can safely be queued for
            // deletion.
            local::add_garbage(unsafe { Garbage::new_box(&*guard) });

            guard
        })
    }

    /// Store a value if the current matches a particular value.
    ///
    /// This compares `self` to `old`. If they match, the value is set to `new` and `Ok(())` is
    /// returned. Otherwise, `Err(new)` is returned.
    ///
    /// The `ordering` defines what constraints the atomic operation has. Refer to the LLVM
    /// documentation for more information.
    pub fn compare_and_store(&self, old: Option<*const T>, mut new: Option<Box<T>>, ordering: atomic::Ordering)
    -> Result<(), Option<Box<T>>> {
        // Convert the parameters to raw pointers.
        // TODO: Use coercions.
        let new_ptr = new.as_mut().map_or(ptr::null_mut(), |x| &mut **x);
        let old_ptr = old.map_or(ptr::null_mut(), |x| x as *mut T);

        // Compare-and-swap the value.
        let ptr = self.inner.compare_and_swap(old_ptr, new_ptr, ordering);

        // Check if the CAS was successful.
        if ptr == old_ptr {
            // It was. `self` is now `new`.

            // Ensure that the destructor of `new` is not run.
            mem::forget(new);

            // Queue the deletion of now-unreachable `old` (unless it's `None`).
            if !old_ptr.is_null() {
                local::add_garbage(unsafe { Garbage::new_box(old_ptr) });
            }

            Ok(())
        } else {
            // It failed.
            Err(new)
        }
    }

    /// Swap a value if it matches.
    ///
    /// This compares `self` to `old`. If they match, it is swapped with `new` and a guard to the
    /// old value is returned wrapped in `Ok`. If not, a tuple containing the guard to the actual
    /// (non-matching) value and the box of `new` — wrapped in `Err` — is returned.
    ///
    /// The `ordering` defines what constraints the atomic operation has. Refer to the LLVM
    /// documentation for more information.
    ///
    /// # Performance
    ///
    /// This is slower than `compare_and_store` as it requires initializing a new guard, which
    /// requires at least two atomic operations. Thus, when possible, you should use
    /// `compare_and_store`.
    pub fn compare_and_swap(&self, old: Option<*const T>, mut new: Option<Box<T>>, ordering: atomic::Ordering)
    -> Result<Option<Guard<T>>, (Option<Guard<T>>, Option<Box<T>>)> {
        // Convert the parameters to raw pointers.
        // TODO: Use coercions.
        let new_ptr = new.as_mut().map_or(ptr::null_mut(), |x| &mut **x);
        let old_ptr = old.map_or(ptr::null_mut(), |x| x as *mut T);

        // Create the guard beforehand to avoid premature frees.
        let guard = Guard::maybe_new(|| {
            // The guard is active, so we can do the CAS now.
            unsafe { self.inner.compare_and_swap(old_ptr, new_ptr, ordering).as_ref() }
        });

        // Convert the guard to a raw pointer.
        // TODO: Use coercions.
        let guard_ptr = guard.as_ref().map_or(ptr::null_mut(), |x| &**x as *const T as *mut T);

        // Check if the CAS was successful.
        if guard_ptr == old_ptr {
            // It was. `self` is now `new`.

            // Ensure that the destructor of `new` is not run.
            mem::forget(new);

            // Queue the deletion of now-unreachable `old` (unless it's `None`).
            if !old_ptr.is_null() {
                local::add_garbage(unsafe { Garbage::new_box(old_ptr) });
            }

            Ok(guard)
        } else {
            // It failed; cast the raw pointer back to a box and return.
            Err((guard, new))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use std::sync::{atomic, Arc};
    use std::sync::atomic::AtomicUsize;
    use std::thread;

    struct Basic;

    impl Drop for Basic {
        fn drop(&mut self) {
            basic();
        }
    }

    thread_local! {
        static BASIC: Basic = Basic;
    }

    fn basic() {
        let opt = AtomicOption::default();
        assert!(opt.load(atomic::Ordering::Relaxed).is_none());
        assert!(opt.swap(None, atomic::Ordering::Relaxed).is_none());
        assert!(opt.load(atomic::Ordering::Relaxed).is_none());
        assert!(opt.swap(Some(Box::new(42)), atomic::Ordering::Relaxed).is_none());
        assert_eq!(*opt.load(atomic::Ordering::Relaxed).unwrap(), 42);
        assert_eq!(*opt.swap(Some(Box::new(43)), atomic::Ordering::Relaxed).unwrap(), 42);
        assert_eq!(*opt.load(atomic::Ordering::Relaxed).unwrap(), 43);
    }

    #[test]
    fn basic_properties() {
        basic()
    }

    #[test]
    fn cas() {
        let bx1 = Box::new(1);
        let ptr1 = &*bx1 as *const usize;
        let bx2 = Box::new(1);
        let ptr2 = &*bx2 as *const usize;

        let opt = AtomicOption::new(Some(bx1));
        assert_eq!(ptr1, &*opt.compare_and_swap(Some(ptr2), None, atomic::Ordering::Relaxed).unwrap_err().0.unwrap());
        assert_eq!(ptr1, &*opt.load(atomic::Ordering::Relaxed).unwrap());

        assert_eq!(ptr1, &*opt.compare_and_swap(None, Some(Box::new(2)), atomic::Ordering::Relaxed).unwrap_err().0.unwrap());
        assert_eq!(ptr1, &*opt.load(atomic::Ordering::Relaxed).unwrap());

        opt.compare_and_swap(Some(ptr1), None, atomic::Ordering::Relaxed).unwrap();
        assert!(opt.load(atomic::Ordering::Relaxed).is_none());

        opt.compare_and_swap(None, Some(bx2), atomic::Ordering::Relaxed).unwrap();
        assert_eq!(ptr2, &*opt.load(atomic::Ordering::Relaxed).unwrap());

        opt.compare_and_store(Some(ptr2), None, atomic::Ordering::Relaxed).unwrap();
        opt.compare_and_store(Some(Box::into_raw(Box::new(2))), None, atomic::Ordering::Relaxed).unwrap_err();

        assert!(opt.load(atomic::Ordering::Relaxed).is_none());

        // To check that GC doesn't segfault or something.
        ::gc();
        ::gc();
        ::gc();
        ::gc();
    }

    #[test]
    fn spam() {
        let opt = Arc::new(AtomicOption::default());

        let mut j = Vec::new();
        for _ in 0..16 {
            let opt = opt.clone();
            j.push(thread::spawn(move || {
                for i in 0..1_000_001 {
                    let _ = opt.load(atomic::Ordering::Relaxed);
                    opt.store(Some(Box::new(i)), atomic::Ordering::Relaxed);
                }
                opt
            }))
        }

        ::gc();
        ::gc();

        for i in j {
            i.join().unwrap();
        }

        assert_eq!(*opt.load(atomic::Ordering::Relaxed).unwrap(), 1_000_000);
    }

    #[test]
    fn drop() {
        #[derive(Clone)]
        struct Dropper {
            d: Arc<AtomicUsize>,
        }

        impl Drop for Dropper {
            fn drop(&mut self) {
                self.d.fetch_add(1, atomic::Ordering::Relaxed);
            }
        }

        let drops = Arc::new(AtomicUsize::default());
        let opt = Arc::new(AtomicOption::new(None));

        let d = Dropper {
            d: drops.clone(),
        };

        let mut j = Vec::new();
        for _ in 0..16 {
            let d = d.clone();
            let opt = opt.clone();

            j.push(thread::spawn(move || {
                for _ in 0..1_000_000 {
                    opt.store(Some(Box::new(d.clone())), atomic::Ordering::Relaxed);
                }
            }))
        }

        for i in j {
            i.join().unwrap();
        }

        opt.store(None, atomic::Ordering::Relaxed);

        ::gc();

        // The 16 are for the `d` variable in the loop above.
        assert_eq!(drops.load(atomic::Ordering::Relaxed), 16_000_000 + 16);
    }

    #[test]
    fn tls() {
        thread::spawn(|| BASIC.with(|_| {})).join().unwrap();
        thread::spawn(|| BASIC.with(|_| {})).join().unwrap();
        thread::spawn(|| BASIC.with(|_| {})).join().unwrap();
        thread::spawn(|| BASIC.with(|_| {})).join().unwrap();
    }
}
<|endoftext|>


<commit_before>// Kevin Cantu
// replace djb with murmur

#[link(name = "murmur", author = "kcantu", vers = "0.2")];

use std;

// rotation left
fn rotl64(x: u64, r: u64) -> u64
{
   ret (x << r) | (x >> (64u-r));
}

// fmix
fn fmix(k_: u64) -> u64
{
   let mut kk = k_;

   kk ^= kk >> 33u;
   kk *= 0x_ff51afd7ed558ccd_u64;
   kk ^= kk >> 33u;
   kk *= 0x_c4ceb9fe1a85ec53_u64;
   kk ^= kk >> 33u;

   ret kk;
}

fn apply_constants(rot: u64,
                   kk: u64,
                   h: u64,
                   c: u64,
                   c_: u64) -> u64
{

   let mut k = kk;
   k *= c;
   k  = rotl64(k, rot);
   k *= c_;
   ret h ^ k;
}

// crunch body data
fn do_body(blocks: [u64],
           nblocks: uint,
           h1_: u64,
           h2_: u64,
           c1: u64,
           c2: u64) -> (u64,u64)
{
   let mut h1 = h1_;
   let mut h2 = h2_;

   let mut ii = 0u;

   //std::io::println(#fmt("nblocks: %u", nblocks));

   while ii < nblocks
   {
      let k1 = blocks[ii*2u + 0u];
      let k2 = blocks[ii*2u + 1u];

      h1 = apply_constants(31u64, k1, h1, c1, c2);

      h1 = rotl64(h1, 27u64);
      h1 += h2;
      h1 = h1*5u64 + 0x_52dce729_u64;

      h2 = apply_constants(33u64, k2, h2, c2, c1);

      h2 = rotl64(h2, 31u64);
      h2 += h1;
      h2 = h2*5u64 + 0x_38495ab5_u64;

      ii += 1u;
   }

   ret (h1, h2);
}

// tail
fn do_tail(tail: [u8], h1: u64, h2: u64, c1: u64, c2: u64) -> (u64,u64) {
   let len = vec::len(tail);

   let mut k1 = 0u64;
   let mut k2 = 0u64;

   if 15u <= len { k2 ^= (tail[14u] as u64) << 48u}
   if 14u <= len { k2 ^= (tail[13u] as u64) << 40u}
   if 13u <= len { k2 ^= (tail[12u] as u64) << 32u}
   if 12u <= len { k2 ^= (tail[11u] as u64) << 24u}
   if 11u <= len { k2 ^= (tail[10u] as u64) << 16u}
   if 10u <= len { k2 ^= (tail[ 9u] as u64) <<  8u}
   if  9u <= len { k2 ^= (tail[ 8u] as u64) <<  0u}
   if  8u <= len { k1 ^= (tail[ 7u] as u64) << 56u}
   if  7u <= len { k1 ^= (tail[ 6u] as u64) << 48u}
   if  6u <= len { k1 ^= (tail[ 5u] as u64) << 40u}
   if  5u <= len { k1 ^= (tail[ 4u] as u64) << 32u}
   if  4u <= len { k1 ^= (tail[ 3u] as u64) << 24u}
   if  3u <= len { k1 ^= (tail[ 2u] as u64) << 16u}
   if  2u <= len { k1 ^= (tail[ 1u] as u64) <<  8u}
   if  1u <= len { k1 ^= (tail[ 0u] as u64) <<  0u}

   let h2_ = apply_constants(33u64, k2, h2, c2, c1);
   let h1_ = apply_constants(31u64, k1, h1, c1, c2);
   ret (h1_,h2_);
}

// finalization
fn finalize(h1_: u64, h2_: u64, len: uint) -> (u64,u64)
{
   let mut h1 = h1_;
   let mut h2 = h2_;

   h1 ^= len as u64;
   h2 ^= len as u64;

   h1 += h2;
   h2 += h1;

   h1 = fmix(h1);
   h2 = fmix(h2);

   h1 += h2;
   h2 += h1;

   ret (h1, h2);
}

// I suspect this flips the endianness from the C/C++ equivalent
fn to_u64(bb: [u8]) -> ([u64], [u8]) unsafe {
   let len = vec::len(bb);
   let head_len = len / 8u;

   let mut head = vec::slice(bb, 0u, head_len);
   let mut tail = vec::slice(bb, head_len, len);

   let mut vv: [u64] = ::unsafe::reinterpret_cast(head);
   ::unsafe::forget(head);
   vec::unsafe::set_len(vv, vec::len(bb) / 8u);

   ret (vv, tail);
}

// murmur3 x64 128-bit
fn murmur(&&key_: str) -> [u64]
{

   let key = str::bytes(key_);

   // TODO: random seeds
   let seed = 0u64;
   let mut h1 = seed;
   let mut h2 = seed;

   let c1 = 0x_87c37b91114253d5_u64;
   let c2 = 0x_4cf5ad432745937f_u64;

   // split body data and tail
   let nbytes  = vec::len(key);
   let nblocks = nbytes / 16u;
   let body_ = vec::slice(key, 0u, nblocks*16u);
   let tail = vec::slice(key, nblocks*16u, nbytes);
   let (body, _) = to_u64(body_);

   let (h1, h2) = do_body(body, nblocks, h1, h2, c1, c2);
   let (h1, h2) = do_tail(tail, h1, h2, c1, c2);
   let (h1, h2) = finalize(h1, h2, nbytes);

   ret [h1, h2];
}

// translate to hex
fn murmur_str(&&ss: str) -> str
{
   let mm = murmur(ss);
   ret #fmt("%016X%016X", mm[0u], mm[1u]);
}

fn murmur_chopped(&&ss: str) -> u64
{
   ret murmur(ss)[0];
}

<commit_msg>aha, fixed<commit_after>// Kevin Cantu
// replace djb with murmur

#[link(name = "murmur", author = "kcantu", vers = "0.2")];

use std;

// rotation left
fn rotl64(x: u64, r: u64) -> u64
{
   ret (x << r) | (x >> (64u-r));
}

// fmix
fn fmix(k_: u64) -> u64
{
   let mut kk = k_;

   kk ^= kk >> 33u;
   kk *= 0x_ff51afd7ed558ccd_u64;
   kk ^= kk >> 33u;
   kk *= 0x_c4ceb9fe1a85ec53_u64;
   kk ^= kk >> 33u;

   ret kk;
}

fn apply_constants(rot: u64,
                   kk: u64,
                   h: u64,
                   c: u64,
                   c_: u64) -> u64
{

   let mut k = kk;
   k *= c;
   k  = rotl64(k, rot);
   k *= c_;
   ret h ^ k;
}

// crunch body data
#[inline(always)]
fn do_body(blocks: [u64],
           nblocks: uint,
           h1_: u64,
           h2_: u64,
           c1: u64,
           c2: u64) -> (u64,u64)
{
   let mut h1 = h1_;
   let mut h2 = h2_;

   let mut ii = 0u;

   //std::io::println(#fmt("nblocks: %u", nblocks));

   while ii < nblocks
   {
      let k1 = blocks[ii*2u + 0u];
      let k2 = blocks[ii*2u + 1u];

      h1 = apply_constants(31u64, k1, h1, c1, c2);

      h1 = rotl64(h1, 27u64);
      h1 += h2;
      h1 = h1*5u64 + 0x_52dce729_u64;

      h2 = apply_constants(33u64, k2, h2, c2, c1);

      h2 = rotl64(h2, 31u64);
      h2 += h1;
      h2 = h2*5u64 + 0x_38495ab5_u64;

      ii += 1u;
   }

   ret (h1, h2);
}

// tail
fn do_tail(tail: [u8], h1: u64, h2: u64, c1: u64, c2: u64) -> (u64,u64) {
   let len = vec::len(tail);

   let mut k1 = 0u64;
   let mut k2 = 0u64;

   if 15u <= len { k2 ^= (tail[14u] as u64) << 48u}
   if 14u <= len { k2 ^= (tail[13u] as u64) << 40u}
   if 13u <= len { k2 ^= (tail[12u] as u64) << 32u}
   if 12u <= len { k2 ^= (tail[11u] as u64) << 24u}
   if 11u <= len { k2 ^= (tail[10u] as u64) << 16u}
   if 10u <= len { k2 ^= (tail[ 9u] as u64) <<  8u}
   if  9u <= len { k2 ^= (tail[ 8u] as u64) <<  0u}
   if  8u <= len { k1 ^= (tail[ 7u] as u64) << 56u}
   if  7u <= len { k1 ^= (tail[ 6u] as u64) << 48u}
   if  6u <= len { k1 ^= (tail[ 5u] as u64) << 40u}
   if  5u <= len { k1 ^= (tail[ 4u] as u64) << 32u}
   if  4u <= len { k1 ^= (tail[ 3u] as u64) << 24u}
   if  3u <= len { k1 ^= (tail[ 2u] as u64) << 16u}
   if  2u <= len { k1 ^= (tail[ 1u] as u64) <<  8u}
   if  1u <= len { k1 ^= (tail[ 0u] as u64) <<  0u}

   let h2_ = apply_constants(33u64, k2, h2, c2, c1);
   let h1_ = apply_constants(31u64, k1, h1, c1, c2);
   ret (h1_,h2_);
}

// finalization
fn finalize(h1_: u64, h2_: u64, len: uint) -> (u64,u64)
{
   let mut h1 = h1_;
   let mut h2 = h2_;

   h1 ^= len as u64;
   h2 ^= len as u64;

   h1 += h2;
   h2 += h1;

   h1 = fmix(h1);
   h2 = fmix(h2);

   h1 += h2;
   h2 += h1;

   ret (h1, h2);
}

// I suspect this flips the endianness from the C/C++ equivalent
fn to_u64(bb: [u8]) -> ([u64], [u8]) unsafe {
   let len = vec::len(bb);
   let head_len = (len / 8u) * 8u;

   let mut head = vec::slice(bb, 0u, head_len);
   let mut tail = vec::slice(bb, head_len, len);

   let mut vv: [u64] = ::unsafe::reinterpret_cast(head);
   ::unsafe::forget(head);
   vec::unsafe::set_len(vv, vec::len(bb) / 8u);

   ret (vv, tail);
}

#[test]
fn TEST_0_to_u64() {
   let (xx, yy) = to_u64([1u8,2u8,3u8,4u8,5u8,6u8,7u8,8u8,9u8,10u8]);
   assert [0x08070605_04030201_u64] == xx;
   assert [9u8, 10u8] == yy;
}

// murmur3 x64 128-bit
fn murmur(&&key_: str) -> [u64]
{

   let key = str::bytes(key_);

   // TODO: random seeds
   let seed = 0u64;
   let mut h1 = seed;
   let mut h2 = seed;

   let c1 = 0x_87c37b91114253d5_u64;
   let c2 = 0x_4cf5ad432745937f_u64;

   // split body data and tail
   let nbytes  = vec::len(key);
   let nblocks = nbytes / 16u;
   let body_ = vec::slice(key, 0u, nblocks*16u);
   let tail = vec::slice(key, nblocks*16u, nbytes);
   let (body, _) = to_u64(body_);

   let (h1, h2) = do_body(body, nblocks, h1, h2, c1, c2);
   let (h1, h2) = do_tail(tail, h1, h2, c1, c2);
   let (h1, h2) = finalize(h1, h2, nbytes);

   ret [h1, h2];
}

// translate to hex
fn murmur_str(&&ss: str) -> str
{
   let mm = murmur(ss);
   ret #fmt("%016X%016X", mm[0u], mm[1u]);
}

fn murmur_chopped(&&ss: str) -> u64
{
   ret murmur(ss)[0];
}

<|endoftext|>


<commit_before>use gltf_derive::Validate;
use serde_derive::{Serialize, Deserialize};

/// Texture sampler properties for filtering and wrapping modes.
#[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
pub struct Sampler {}

/// A texture and its sampler.
#[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
pub struct Texture {}

#[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
/// Reference to a `Texture`.
pub struct Info {
    #[cfg(feature = "KHR_texture_transform")]
    #[serde(default, rename = "KHR_texture_transform", skip_serializing_if = "Option::is_none")]
    pub texture_transform: Option<TextureTransform>,
}

/// Foo
#[cfg(feature = "KHR_texture_transform")]
#[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
#[serde(default, rename_all = "camelCase")]
pub struct TextureTransform {
    /// Foo
    pub offset: TextureTransformOffset,

    /// Foo
    pub rotation: TextureTransformRotation,

    /// Foo
    pub scale: TextureTransformScale,

    /// Foo
    pub tex_coord: Option<u32>,
}

/// Foo
#[cfg(feature = "KHR_texture_transform")]
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct TextureTransformOffset(pub [f32; 2]);

#[cfg(feature = "KHR_texture_transform")]
impl Default for TextureTransformOffset {
    fn default() -> Self {
        Self([0.0, 0.0])
    }
}

#[cfg(feature = "KHR_texture_transform")]
impl Validate for TextureTransformOffset {}

/// Foo
#[cfg(feature = "KHR_texture_transform")]
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct TextureTransformRotation(pub f32);

#[cfg(feature = "KHR_texture_transform")]
impl Default for TextureTransformRotation {
    fn default() -> Self {
        Self(0.0)
    }
}

#[cfg(feature = "KHR_texture_transform")]
impl Validate for TextureTransformRotation {}

/// Foo
#[cfg(feature = "KHR_texture_transform")]
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct TextureTransformScale(pub [f32; 2]);

#[cfg(feature = "KHR_texture_transform")]
impl Default for TextureTransformScale {
    fn default() -> Self {
        Self([1.0, 1.0])
    }
}

#[cfg(feature = "KHR_texture_transform")]
impl Validate for TextureTransformScale {}
<commit_msg>gltf-json KHR_texture_transform docs<commit_after>use gltf_derive::Validate;
use serde_derive::{Serialize, Deserialize};

/// Texture sampler properties for filtering and wrapping modes.
#[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
pub struct Sampler {}

/// A texture and its sampler.
#[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
pub struct Texture {}

#[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
/// Reference to a `Texture`.
pub struct Info {
    #[cfg(feature = "KHR_texture_transform")]
    #[serde(default, rename = "KHR_texture_transform", skip_serializing_if = "Option::is_none")]
    pub texture_transform: Option<TextureTransform>,
}

/// Many techniques can be used to optimize resource usage for a 3d scene.
/// Chief among them is the ability to minimize the number of textures the GPU must load.
/// To achieve this, many engines encourage packing many objects' low-resolution textures into a single large texture atlas.
/// The region of the resulting atlas that corresponds with each object is then defined by vertical and horizontal offsets,
/// and the width and height of the region.
///
/// To support this use case, this extension adds `offset`, `rotation`, and `scale` properties to textureInfo structures.
/// These properties would typically be implemented as an affine transform on the UV coordinates.
#[cfg(feature = "KHR_texture_transform")]
#[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
#[serde(default, rename_all = "camelCase")]
pub struct TextureTransform {
    // The offset of the UV coordinate origin as a factor of the texture dimensions.
    pub offset: TextureTransformOffset,

    /// Rotate the UVs by this many radians counter-clockwise around the origin.
    /// This is equivalent to a similar rotation of the image clockwise.
    pub rotation: TextureTransformRotation,

    /// The scale factor applied to the components of the UV coordinates.
    pub scale: TextureTransformScale,

    /// Overrides the textureInfo texCoord value if supplied, and if this extension is supported.
    pub tex_coord: Option<u32>,
}

/// The offset of the UV coordinate origin as a factor of the texture dimensions.
#[cfg(feature = "KHR_texture_transform")]
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct TextureTransformOffset(pub [f32; 2]);

#[cfg(feature = "KHR_texture_transform")]
impl Default for TextureTransformOffset {
    fn default() -> Self {
        Self([0.0, 0.0])
    }
}

#[cfg(feature = "KHR_texture_transform")]
impl Validate for TextureTransformOffset {}

/// Rotate the UVs by this many radians counter-clockwise around the origin.
/// This is equivalent to a similar rotation of the image clockwise.
#[cfg(feature = "KHR_texture_transform")]
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct TextureTransformRotation(pub f32);

#[cfg(feature = "KHR_texture_transform")]
impl Default for TextureTransformRotation {
    fn default() -> Self {
        Self(0.0)
    }
}

#[cfg(feature = "KHR_texture_transform")]
impl Validate for TextureTransformRotation {}

/// The scale factor applied to the components of the UV coordinates.
#[cfg(feature = "KHR_texture_transform")]
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct TextureTransformScale(pub [f32; 2]);

#[cfg(feature = "KHR_texture_transform")]
impl Default for TextureTransformScale {
    fn default() -> Self {
        Self([1.0, 1.0])
    }
}

#[cfg(feature = "KHR_texture_transform")]
impl Validate for TextureTransformScale {}
<|endoftext|>


<commit_before>//! Utilities for managing and calling external binaries

use binary::{
    eyre::{bail, Result},
    Binary, BinaryInstallation, BinaryTrait,
};
use once_cell::sync::Lazy;
use std::collections::{BTreeMap, HashMap};
use tokio::sync::RwLock;

///! A module for locating, running and installing third party binaries.
///!
///! Binaries may be used as runtimes for plugins (e.g. Node.js, Python) or
///! are used in created (e.g Pandoc by the `codec-pandoc` crate).

/// A global store of binaries
///
/// This is an immutable, lazily initialized list of "registered" binaries
/// that Stencila knows how to install, get the version for etc. However,
/// the functions below can be used for any other binary as well.
#[allow(unused_mut)]
static BINARIES: Lazy<BTreeMap<String, Box<dyn BinaryTrait>>> = Lazy::new(|| {
    let mut map: BTreeMap<String, Box<dyn BinaryTrait>> = BTreeMap::new();

    macro_rules! binary_new {
        ($feat:literal, $bin:expr) => {
            #[cfg(feature = $feat)]
            {
                map.insert($bin.spec().name.to_lowercase(), Box::new($bin));
            }
        };
    }

    binary_new!("binary-chrome", binary_chrome::ChromeBinary {});
    binary_new!("binary-chromium", binary_chromium::ChromiumBinary {});
    binary_new!("binary-node", binary_node::NodeBinary {});
    binary_new!("binary-pandoc", binary_pandoc::PandocBinary {});
    binary_new!("binary-python", binary_python::PythonBinary {});
    binary_new!("binary-r", binary_r::RBinary {});

    map
});

/// A cache of installations used to memoize calls to `installation`.
static INSTALLATIONS: Lazy<RwLock<HashMap<String, BinaryInstallation>>> =
    Lazy::new(|| RwLock::new(HashMap::new()));

/// Get an installation
///
/// This is a relatively expensive function, even if the binary is already installed,
/// because it searches the file system and executes commands to get their version.
/// Therefore, this function memoizes installations in `INSTALLATIONS` for each `name` and `semver`.
/// Each cached result is removed if the binary is installed or uninstalled.
pub async fn installation(name: &str, semver: &str) -> Result<BinaryInstallation> {
    let name_semver = [name, "@", semver].concat();

    if let Some(installation) = INSTALLATIONS.read().await.get(&name_semver) {
        return Ok(installation.clone());
    }

    let unregistered: Box<dyn BinaryTrait> = Box::new(Binary::unregistered(name));
    let binary = BINARIES.get(name).unwrap_or(&unregistered);

    let semver = if semver == "*" {
        None
    } else {
        Some(semver.into())
    };

    if let Some(installation) = binary.installed(semver)? {
        INSTALLATIONS
            .write()
            .await
            .insert(name_semver, installation.clone());
        Ok(installation)
    } else {
        bail!("No matching installation found")
    }
}

/// Is a binary installation meeting semantic versioning requirements installed?
pub async fn installed(name: &str, semver: &str) -> bool {
    installation(name, semver).await.is_ok()
}

/// Get a binary installation meeting semantic versioning requirements.
///
/// If the binary is already available, or automatic installs are configured, returns
/// a `BinaryInstallation` that can be used to run commands. Otherwise, errors
/// with a message that the required binary is not yet installed, or failed to install.
pub async fn require(name: &str, semver: &str) -> Result<BinaryInstallation> {
    if let Ok(installation) = installation(name, semver).await {
        return Ok(installation);
    }

    // TODO: Use an env var to set this?
    let auto = true;
    if auto {
        let name_semver = [name, "@", semver].concat();
        let semver = if semver == "*" {
            None
        } else {
            Some(semver.into())
        };

        let binary = match BINARIES.get(name) {
            Some(binary) => binary,
            None => bail!("Unable to automatically install binary `{}`", name),
        };
        binary.install(semver.clone(), None, None).await?;

        if let Some(installation) = binary.installed(semver)? {
            let mut installations = INSTALLATIONS.write().await;
            installations.insert(name_semver, installation.clone());
            Ok(installation)
        } else {
            bail!("Failed to automatically install binary `{}`", name)
        }
    } else {
        bail!("Required binary `{}` is not installed", name)
    }
}

#[cfg(feature = "cli")]
pub mod commands {
    use super::*;
    use cli_utils::structopt::StructOpt;
    use cli_utils::{async_trait::async_trait, result, Result, Run};

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Manage helper binaries",
        setting = structopt::clap::AppSettings::ColoredHelp,
        setting = structopt::clap::AppSettings::VersionlessSubcommands
    )]
    pub struct Command {
        #[structopt(subcommand)]
        pub action: Action,
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder
    )]
    pub enum Action {
        Show(Show),
        Installable(Installable),
        Install(Install),
        Uninstall(Uninstall),
        Run(Run_),
    }

    #[async_trait]
    impl Run for Command {
        async fn run(&self) -> Result {
            let Self { action } = self;
            match action {
                Action::Show(action) => action.run().await,
                Action::Installable(action) => action.run().await,
                Action::Install(action) => action.run().await,
                Action::Uninstall(action) => action.run().await,
                Action::Run(action) => action.run().await,
            }
        }
    }

    /// Show information on a binary
    ///
    /// Searches for the binary on your path and in Stencila's "binaries"
    /// folder for versions that are installed. Use the `semver` argument
    /// to show the latest version that meets the semantic version requirement.
    ///
    /// This command should find any binary that is on your PATH
    /// (i.e. including those not in the `stencila binaries installable` list).
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Show {
        /// The name of the binary e.g. pandoc
        pub name: String,

        /// The semantic version requirement for the binary e.g. >2
        ///
        /// If this is supplied and the binary does not meet the semver
        /// requirement nothing will be shown
        pub semver: Option<String>,
    }

    #[async_trait]
    impl Run for Show {
        async fn run(&self) -> Result {
            // Try to get registered binary (because has potential aliases and extracting versions) but fall
            // back to unregistered for others
            let unregistered: Box<dyn BinaryTrait> = Box::new(Binary::unregistered(&self.name));
            let binary = BINARIES
                .get(&self.name.to_lowercase())
                .unwrap_or(&unregistered);
            if self.semver.is_some() {
                if let Ok(installation) = binary.installed(self.semver.clone()) {
                    result::value(installation)
                } else {
                    tracing::info!(
                        "No matching binary found. Perhaps use `stencila binaries install`?"
                    );
                    result::nothing()
                }
            } else {
                let installations = binary.installations();
                result::value(installations)
            }
        }
    }

    /// List binaries (and their versions) that can be installed using Stencila
    ///
    /// The returned list is a list of the binaries/versions that Stencila knows how to install.
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Installable {}

    #[async_trait]
    impl Run for Installable {
        async fn run(&self) -> Result {
            let list: Vec<serde_json::Value> = BINARIES
                .values()
                .map(|binary| {
                    let spec = binary.spec();
                    serde_json::json!({
                        "name": spec.name,
                        "versions": spec.installable
                    })
                })
                .collect();
            result::value(list)
        }
    }

    /// Install a binary
    ///
    /// Installs the latest version of the binary, that also meets any
    /// semantic version requirement supplied, into the Stencila "binaries"
    /// folder.
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Install {
        /// The name of the binary (must be a registered binary name)
        pub name: String,

        /// The semantic version requirement (the most recent matching version will be installed)
        pub semver: Option<String>,

        /// The operating system to install for (defaults to the current)
        #[structopt(short, long, possible_values = &OS_VALUES )]
        pub os: Option<String>,

        /// The architecture to install for (defaults to the current)
        #[structopt(short, long, possible_values = &ARCH_VALUES)]
        pub arch: Option<String>,
    }

    const OS_VALUES: [&str; 3] = ["macos", "windows", "linux"];
    const ARCH_VALUES: [&str; 3] = ["x86", "x86_64", "arm"];

    #[async_trait]
    impl Run for Install {
        async fn run(&self) -> Result {
            match BINARIES.get(&self.name.to_lowercase()) {
                Some(binary) => {
                    binary
                        .install(self.semver.clone(), self.os.clone(), self.arch.clone())
                        .await?;
                    tracing::info!("📦 Installed {}", self.name);
                }
                None => {
                    tracing::error!("Stencila is unable to install `{}`", self.name);
                }
            }
            result::nothing()
        }
    }

    /// Uninstall a binary
    ///
    /// Removes the binary (optionally, just a specific version) from the Stencila
    /// "binaries" folder. No other installations of the binary on the system will
    /// will be removed.
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Uninstall {
        /// The name of the binary (must be a registered binary name)
        pub name: String,

        /// The specific version of the binary to uninstall
        ///
        /// If this is not provided, all versions will be removed.
        pub version: Option<String>,
    }
    #[async_trait]
    impl Run for Uninstall {
        async fn run(&self) -> Result {
            // Fallback to unregistered since that is sufficient for uninstall
            let unregistered: Box<dyn BinaryTrait> = Box::new(Binary::unregistered(&self.name));
            let binary = BINARIES
                .get(&self.name.to_lowercase())
                .unwrap_or(&unregistered);
            binary.uninstall(self.version.clone()).await?;

            tracing::info!("🗑 Uninstalled {}", self.name);
            result::nothing()
        }
    }

    /// Run a command using a binary
    ///
    /// Pass arguments and options to the binary after the `--` flag.
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Run_ {
        /// The name of the binary e.g. node
        pub name: String,

        /// The semantic version requirement e.g. 16
        pub semver: Option<String>,

        /// The arguments and options to pass to the binary
        #[structopt(raw(true))]
        pub args: Vec<String>,
    }

    #[async_trait]
    impl Run for Run_ {
        async fn run(&self) -> Result {
            let installation = require(
                &self.name.to_lowercase(),
                &self.semver.clone().unwrap_or_else(|| "*".to_string()),
            )
            .await?;
            let output = installation.run(&self.args).await?;

            use std::io::Write;
            std::io::stdout().write_all(output.stdout.as_ref())?;
            std::io::stderr().write_all(output.stderr.as_ref())?;

            result::nothing()
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    // End to end CLI test that install, show and uninstall
    // the latest version of each binary. Intended as a coarse
    // tests of installation of each binary. These tests are
    // tagged with #[ignore] because they are slow, so in development
    // you don't want to run them, and because if they are run in
    // parallel with other tests that use `require()` they can cause deadlocks
    // and other on-disk conflicts.

    // Run this test at the start of CI tests using
    //   cargo test binaries::tests::install -- --ignored --nocapture
    #[cfg(feature = "cli")]
    #[tokio::test]
    #[ignore]
    async fn install() -> Result<()> {
        use super::commands::{Install, Installable, Show};
        use cli_utils::Run;
        use test_utils::assert_json_eq;

        Installable {}.run().await?;

        for name in BINARIES.keys() {
            eprintln!("Testing {}", name);

            Install {
                name: name.clone(),
                semver: None,
                os: None,
                arch: None,
            }
            .run()
            .await?;

            let display = Show {
                name: name.clone(),
                semver: None,
            }
            .run()
            .await?;

            let value = if let Some(value) = display.value {
                value
            } else {
                bail!("Expected value")
            };
            assert_json_eq!(value.get("name"), Some(name.clone()));
            assert!(!value
                .get("installs")
                .expect("To have installs")
                .as_array()
                .expect("To be array")
                .is_empty());
        }

        Ok(())
    }

    // Run this test at the end of CI tests using
    //   cargo test binaries::tests::uninstall -- --ignored --nocapture
    #[cfg(feature = "cli")]
    #[tokio::test]
    #[ignore]
    async fn uninstall() -> Result<()> {
        use super::commands::Uninstall;
        use cli_utils::Run;

        for name in BINARIES.keys() {
            Uninstall {
                name: name.clone(),
                version: None,
            }
            .run()
            .await?;
        }

        Ok(())
    }
}
<commit_msg>fix(Binaries): Search for name or aliases of registered binaries<commit_after>//! Utilities for managing and calling external binaries

use binary::{
    eyre::{bail, Result},
    Binary, BinaryInstallation, BinaryTrait,
};
use once_cell::sync::Lazy;
use std::collections::{BTreeMap, HashMap};
use tokio::sync::RwLock;

///! A module for locating, running and installing third party binaries.
///!
///! Binaries may be used as runtimes for plugins (e.g. Node.js, Python) or
///! are used in created (e.g Pandoc by the `codec-pandoc` crate).

/// A global store of binaries
///
/// This is an immutable, lazily initialized list of "registered" binaries
/// that Stencila knows how to install, get the version for etc. However,
/// the functions below can be used for any other binary as well.
#[allow(unused_mut)]
static BINARIES: Lazy<BTreeMap<String, Box<dyn BinaryTrait>>> = Lazy::new(|| {
    let mut map: BTreeMap<String, Box<dyn BinaryTrait>> = BTreeMap::new();

    macro_rules! binary_new {
        ($feat:literal, $bin:expr) => {
            #[cfg(feature = $feat)]
            {
                map.insert($bin.spec().name.to_lowercase(), Box::new($bin));
            }
        };
    }

    binary_new!("binary-chrome", binary_chrome::ChromeBinary {});
    binary_new!("binary-chromium", binary_chromium::ChromiumBinary {});
    binary_new!("binary-node", binary_node::NodeBinary {});
    binary_new!("binary-pandoc", binary_pandoc::PandocBinary {});
    binary_new!("binary-python", binary_python::PythonBinary {});
    binary_new!("binary-r", binary_r::RBinary {});

    map
});

/// Get a registered binary by matching by name and or aliases (case insensitively)
#[allow(clippy::borrowed_box)]
fn registered(name: &str) -> Option<&Box<dyn BinaryTrait>> {
    let name = name.to_lowercase();

    let binary = BINARIES.get(&name);
    if binary.is_some() {
        return binary;
    }

    for binary in BINARIES.values() {
        for alias in binary.spec().aliases {
            if alias.to_lowercase() == name {
                return Some(binary);
            }
        }
    }

    None
}

/// A cache of installations used to memoize calls to `installation`.
static INSTALLATIONS: Lazy<RwLock<HashMap<String, BinaryInstallation>>> =
    Lazy::new(|| RwLock::new(HashMap::new()));

/// Get an installation
///
/// This is a relatively expensive function, even if the binary is already installed,
/// because it searches the file system and executes commands to get their version.
/// Therefore, this function memoizes installations in `INSTALLATIONS` for each `name` and `semver`.
/// Each cached result is removed if the binary is installed or uninstalled.
pub async fn installation(name: &str, semver: &str) -> Result<BinaryInstallation> {
    let name_semver = [name, "@", semver].concat();

    if let Some(installation) = INSTALLATIONS.read().await.get(&name_semver) {
        return Ok(installation.clone());
    }

    let unregistered: Box<dyn BinaryTrait> = Box::new(Binary::unregistered(name));
    let binary = registered(name).unwrap_or(&unregistered);

    let semver = if semver == "*" {
        None
    } else {
        Some(semver.into())
    };

    if let Some(installation) = binary.installed(semver)? {
        INSTALLATIONS
            .write()
            .await
            .insert(name_semver, installation.clone());
        Ok(installation)
    } else {
        bail!("No matching installation found")
    }
}

/// Is a binary installation meeting semantic versioning requirements installed?
pub async fn installed(name: &str, semver: &str) -> bool {
    installation(name, semver).await.is_ok()
}

/// Get a binary installation meeting semantic versioning requirements.
///
/// If the binary is already available, or automatic installs are configured, returns
/// a `BinaryInstallation` that can be used to run commands. Otherwise, errors
/// with a message that the required binary is not yet installed, or failed to install.
pub async fn require(name: &str, semver: &str) -> Result<BinaryInstallation> {
    if let Ok(installation) = installation(name, semver).await {
        return Ok(installation);
    }

    // TODO: Use an env var to set this?
    let auto = true;
    if auto {
        let name_semver = [name, "@", semver].concat();
        let semver = if semver == "*" {
            None
        } else {
            Some(semver.into())
        };

        let binary = match registered(name) {
            Some(binary) => binary,
            None => bail!("Unable to automatically install binary `{}`", name),
        };
        binary.install(semver.clone(), None, None).await?;

        if let Some(installation) = binary.installed(semver)? {
            let mut installations = INSTALLATIONS.write().await;
            installations.insert(name_semver, installation.clone());
            Ok(installation)
        } else {
            bail!("Failed to automatically install binary `{}`", name)
        }
    } else {
        bail!("Required binary `{}` is not installed", name)
    }
}

#[cfg(feature = "cli")]
pub mod commands {
    use super::*;
    use cli_utils::structopt::StructOpt;
    use cli_utils::{async_trait::async_trait, result, Result, Run};

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Manage helper binaries",
        setting = structopt::clap::AppSettings::ColoredHelp,
        setting = structopt::clap::AppSettings::VersionlessSubcommands
    )]
    pub struct Command {
        #[structopt(subcommand)]
        pub action: Action,
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder
    )]
    pub enum Action {
        Show(Show),
        Installable(Installable),
        Install(Install),
        Uninstall(Uninstall),
        Run(Run_),
    }

    #[async_trait]
    impl Run for Command {
        async fn run(&self) -> Result {
            let Self { action } = self;
            match action {
                Action::Show(action) => action.run().await,
                Action::Installable(action) => action.run().await,
                Action::Install(action) => action.run().await,
                Action::Uninstall(action) => action.run().await,
                Action::Run(action) => action.run().await,
            }
        }
    }

    /// Show information on a binary
    ///
    /// Searches for the binary on your path and in Stencila's "binaries"
    /// folder for versions that are installed. Use the `semver` argument
    /// to show the latest version that meets the semantic version requirement.
    ///
    /// This command should find any binary that is on your PATH
    /// (i.e. including those not in the `stencila binaries installable` list).
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Show {
        /// The name of the binary e.g. pandoc
        pub name: String,

        /// The semantic version requirement for the binary e.g. >2
        ///
        /// If this is supplied and the binary does not meet the semver
        /// requirement nothing will be shown
        pub semver: Option<String>,
    }

    #[async_trait]
    impl Run for Show {
        async fn run(&self) -> Result {
            // Try to get registered binary (because has potential aliases and extracting versions) but fall
            // back to unregistered for others
            let unregistered: Box<dyn BinaryTrait> = Box::new(Binary::unregistered(&self.name));
            let binary = registered(&self.name).unwrap_or(&unregistered);
            if self.semver.is_some() {
                if let Ok(installation) = binary.installed(self.semver.clone()) {
                    result::value(installation)
                } else {
                    tracing::info!(
                        "No matching binary found. Perhaps use `stencila binaries install`?"
                    );
                    result::nothing()
                }
            } else {
                let installations = binary.installations();
                result::value(installations)
            }
        }
    }

    /// List binaries (and their versions) that can be installed using Stencila
    ///
    /// The returned list is a list of the binaries/versions that Stencila knows how to install.
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Installable {}

    #[async_trait]
    impl Run for Installable {
        async fn run(&self) -> Result {
            let list: Vec<serde_json::Value> = BINARIES
                .values()
                .map(|binary| {
                    let spec = binary.spec();
                    serde_json::json!({
                        "name": spec.name,
                        "versions": spec.installable
                    })
                })
                .collect();
            result::value(list)
        }
    }

    /// Install a binary
    ///
    /// Installs the latest version of the binary, that also meets any
    /// semantic version requirement supplied, into the Stencila "binaries"
    /// folder.
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Install {
        /// The name of the binary (must be a registered binary name)
        pub name: String,

        /// The semantic version requirement (the most recent matching version will be installed)
        pub semver: Option<String>,

        /// The operating system to install for (defaults to the current)
        #[structopt(short, long, possible_values = &OS_VALUES )]
        pub os: Option<String>,

        /// The architecture to install for (defaults to the current)
        #[structopt(short, long, possible_values = &ARCH_VALUES)]
        pub arch: Option<String>,
    }

    const OS_VALUES: [&str; 3] = ["macos", "windows", "linux"];
    const ARCH_VALUES: [&str; 3] = ["x86", "x86_64", "arm"];

    #[async_trait]
    impl Run for Install {
        async fn run(&self) -> Result {
            match registered(&self.name) {
                Some(binary) => {
                    binary
                        .install(self.semver.clone(), self.os.clone(), self.arch.clone())
                        .await?;
                    tracing::info!("📦 Installed {}", self.name);
                }
                None => {
                    tracing::error!("Stencila is unable to install `{}`", self.name);
                }
            }
            result::nothing()
        }
    }

    /// Uninstall a binary
    ///
    /// Removes the binary (optionally, just a specific version) from the Stencila
    /// "binaries" folder. No other installations of the binary on the system will
    /// will be removed.
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Uninstall {
        /// The name of the binary (must be a registered binary name)
        pub name: String,

        /// The specific version of the binary to uninstall
        ///
        /// If this is not provided, all versions will be removed.
        pub version: Option<String>,
    }
    #[async_trait]
    impl Run for Uninstall {
        async fn run(&self) -> Result {
            // Fallback to unregistered since that is sufficient for uninstall
            let unregistered: Box<dyn BinaryTrait> = Box::new(Binary::unregistered(&self.name));
            let binary = registered(&self.name).unwrap_or(&unregistered);
            binary.uninstall(self.version.clone()).await?;

            tracing::info!("🗑 Uninstalled {}", self.name);
            result::nothing()
        }
    }

    /// Run a command using a binary
    ///
    /// Pass arguments and options to the binary after the `--` flag.
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Run_ {
        /// The name of the binary e.g. node
        pub name: String,

        /// The semantic version requirement e.g. 16
        pub semver: Option<String>,

        /// The arguments and options to pass to the binary
        #[structopt(raw(true))]
        pub args: Vec<String>,
    }

    #[async_trait]
    impl Run for Run_ {
        async fn run(&self) -> Result {
            let installation = require(
                &self.name,
                &self.semver.clone().unwrap_or_else(|| "*".to_string()),
            )
            .await?;
            let output = installation.run(&self.args).await?;

            use std::io::Write;
            std::io::stdout().write_all(output.stdout.as_ref())?;
            std::io::stderr().write_all(output.stderr.as_ref())?;

            result::nothing()
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    // End to end CLI test that install, show and uninstall
    // the latest version of each binary. Intended as a coarse
    // tests of installation of each binary. These tests are
    // tagged with #[ignore] because they are slow, so in development
    // you don't want to run them, and because if they are run in
    // parallel with other tests that use `require()` they can cause deadlocks
    // and other on-disk conflicts.

    // Run this test at the start of CI tests using
    //   cargo test binaries::tests::install -- --ignored --nocapture
    #[cfg(feature = "cli")]
    #[tokio::test]
    #[ignore]
    async fn install() -> Result<()> {
        use super::commands::{Install, Installable, Show};
        use cli_utils::Run;
        use test_utils::assert_json_eq;

        Installable {}.run().await?;

        for name in BINARIES.keys() {
            eprintln!("Testing {}", name);

            Install {
                name: name.clone(),
                semver: None,
                os: None,
                arch: None,
            }
            .run()
            .await?;

            let display = Show {
                name: name.clone(),
                semver: None,
            }
            .run()
            .await?;

            let value = if let Some(value) = display.value {
                value
            } else {
                bail!("Expected value")
            };
            assert_json_eq!(value.get("name"), Some(name.clone()));
            assert!(!value
                .get("installs")
                .expect("To have installs")
                .as_array()
                .expect("To be array")
                .is_empty());
        }

        Ok(())
    }

    // Run this test at the end of CI tests using
    //   cargo test binaries::tests::uninstall -- --ignored --nocapture
    #[cfg(feature = "cli")]
    #[tokio::test]
    #[ignore]
    async fn uninstall() -> Result<()> {
        use super::commands::Uninstall;
        use cli_utils::Run;

        for name in BINARIES.keys() {
            Uninstall {
                name: name.clone(),
                version: None,
            }
            .run()
            .await?;
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>//! Concrete error types for all operations which may be invalid in a certain const context.

use rustc::hir::def_id::DefId;
use rustc::mir::BorrowKind;
use rustc::session::config::nightly_options;
use rustc::ty::TyCtxt;
use syntax::feature_gate::feature_err;
use syntax::symbol::sym;
use syntax_pos::{Span, Symbol};

use super::{ConstKind, Item};

use rustc_error_codes::*;

/// An operation that is not *always* allowed in a const context.
pub trait NonConstOp: std::fmt::Debug {
    /// Whether this operation can be evaluated by miri.
    const IS_SUPPORTED_IN_MIRI: bool = true;

    /// Returns a boolean indicating whether the feature gate that would allow this operation is
    /// enabled, or `None` if such a feature gate does not exist.
    fn feature_gate(_tcx: TyCtxt<'tcx>) -> Option<bool> {
        None
    }

    /// Returns `true` if this operation is allowed in the given item.
    ///
    /// This check should assume that we are not in a non-const `fn`, where all operations are
    /// legal.
    fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
        Self::feature_gate(item.tcx).unwrap_or(false)
    }

    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        let mut err = struct_span_err!(
            item.tcx.sess,
            span,
            E0019,
            "{} contains unimplemented expression type",
            item.const_kind()
        );
        if item.tcx.sess.teach(&err.get_code().unwrap()) {
            err.note("A function call isn't allowed in the const's initialization expression \
                      because the expression's value must be known at compile-time.");
            err.note("Remember: you can't use a function call inside a const's initialization \
                      expression! However, you can use it anywhere else.");
        }
        err.emit();
    }
}

/// A `Downcast` projection.
#[derive(Debug)]
pub struct Downcast;
impl NonConstOp for Downcast {
    fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
        Some(tcx.features().const_if_match)
    }
}

/// A function call where the callee is a pointer.
#[derive(Debug)]
pub struct FnCallIndirect;
impl NonConstOp for FnCallIndirect {
    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        let mut err = item.tcx.sess.struct_span_err(
            span,
            &format!("function pointers are not allowed in const fn"));
        err.emit();
    }
}

/// A function call where the callee is not marked as `const`.
#[derive(Debug)]
pub struct FnCallNonConst(pub DefId);
impl NonConstOp for FnCallNonConst {
    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        let mut err = struct_span_err!(
            item.tcx.sess,
            span,
            E0015,
            "calls in {}s are limited to constant functions, \
             tuple structs and tuple variants",
            item.const_kind(),
        );
        err.emit();
    }
}

/// A function call where the callee is not a function definition or function pointer, e.g. a
/// closure.
///
/// This can be subdivided in the future to produce a better error message.
#[derive(Debug)]
pub struct FnCallOther;
impl NonConstOp for FnCallOther {
    const IS_SUPPORTED_IN_MIRI: bool = false;
}

/// A call to a `#[unstable]` const fn or `#[rustc_const_unstable]` function.
///
/// Contains the name of the feature that would allow the use of this function.
#[derive(Debug)]
pub struct FnCallUnstable(pub DefId, pub Symbol);
impl NonConstOp for FnCallUnstable {
    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        let FnCallUnstable(def_id, feature) = *self;

        let mut err = item.tcx.sess.struct_span_err(span,
            &format!("`{}` is not yet stable as a const fn",
                    item.tcx.def_path_str(def_id)));
        if nightly_options::is_nightly_build() {
            help!(&mut err,
                  "add `#![feature({})]` to the \
                   crate attributes to enable",
                  feature);
        }
        err.emit();
    }
}

#[derive(Debug)]
pub struct HeapAllocation;
impl NonConstOp for HeapAllocation {
    const IS_SUPPORTED_IN_MIRI: bool = false;

    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        let mut err = struct_span_err!(item.tcx.sess, span, E0010,
                                       "allocations are not allowed in {}s", item.const_kind());
        err.span_label(span, format!("allocation not allowed in {}s", item.const_kind()));
        if item.tcx.sess.teach(&err.get_code().unwrap()) {
            err.note(
                "The value of statics and constants must be known at compile time, \
                 and they live for the entire lifetime of a program. Creating a boxed \
                 value allocates memory on the heap at runtime, and therefore cannot \
                 be done at compile time."
            );
        }
        err.emit();
    }
}

#[derive(Debug)]
pub struct IfOrMatch;
impl NonConstOp for IfOrMatch {
    fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
        Some(tcx.features().const_if_match)
    }

    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        // This should be caught by the HIR const-checker.
        item.tcx.sess.delay_span_bug(
            span,
            "complex control flow is forbidden in a const context",
        );
    }
}

#[derive(Debug)]
pub struct LiveDrop;
impl NonConstOp for LiveDrop {
    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        struct_span_err!(item.tcx.sess, span, E0493,
                         "destructors cannot be evaluated at compile-time")
            .span_label(span, format!("{}s cannot evaluate destructors",
                                      item.const_kind()))
            .emit();
    }
}

#[derive(Debug)]
pub struct Loop;
impl NonConstOp for Loop {
    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        // This should be caught by the HIR const-checker.
        item.tcx.sess.delay_span_bug(
            span,
            "complex control flow is forbidden in a const context",
        );
    }
}

#[derive(Debug)]
pub struct MutBorrow(pub BorrowKind);
impl NonConstOp for MutBorrow {
    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        let kind = self.0;
        if let BorrowKind::Mut { .. } = kind {
            let mut err = struct_span_err!(item.tcx.sess, span, E0017,
                                           "references in {}s may only refer \
                                            to immutable values", item.const_kind());
            err.span_label(span, format!("{}s require immutable values",
                                                item.const_kind()));
            if item.tcx.sess.teach(&err.get_code().unwrap()) {
                err.note("References in statics and constants may only refer \
                          to immutable values.\n\n\
                          Statics are shared everywhere, and if they refer to \
                          mutable data one might violate memory safety since \
                          holding multiple mutable references to shared data \
                          is not allowed.\n\n\
                          If you really want global mutable state, try using \
                          static mut or a global UnsafeCell.");
            }
            err.emit();
        } else {
            span_err!(item.tcx.sess, span, E0492,
                      "cannot borrow a constant which may contain \
                       interior mutability, create a static instead");
        }
    }
}

#[derive(Debug)]
pub struct MutDeref;
impl NonConstOp for MutDeref {}

#[derive(Debug)]
pub struct Panic;
impl NonConstOp for Panic {
    fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
        Some(tcx.features().const_panic)
    }

    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        feature_err(
            &item.tcx.sess.parse_sess,
            sym::const_panic,
            span,
            &format!("panicking in {}s is unstable", item.const_kind()),
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct RawPtrComparison;
impl NonConstOp for RawPtrComparison {
    fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
        Some(tcx.features().const_compare_raw_pointers)
    }

    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        feature_err(
            &item.tcx.sess.parse_sess,
            sym::const_compare_raw_pointers,
            span,
            &format!("comparing raw pointers inside {}", item.const_kind()),
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct RawPtrDeref;
impl NonConstOp for RawPtrDeref {
    fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
        Some(tcx.features().const_raw_ptr_deref)
    }

    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        feature_err(
            &item.tcx.sess.parse_sess, sym::const_raw_ptr_deref, span,
            &format!(
                "dereferencing raw pointers in {}s is unstable",
                item.const_kind(),
            ),
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct RawPtrToIntCast;
impl NonConstOp for RawPtrToIntCast {
    fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
        Some(tcx.features().const_raw_ptr_to_usize_cast)
    }

    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        feature_err(
            &item.tcx.sess.parse_sess, sym::const_raw_ptr_to_usize_cast, span,
            &format!(
                "casting pointers to integers in {}s is unstable",
                item.const_kind(),
            ),
        )
        .emit();
    }
}

/// An access to a (non-thread-local) `static`.
#[derive(Debug)]
pub struct StaticAccess;
impl NonConstOp for StaticAccess {
    fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
        item.const_kind().is_static()
    }

    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        let mut err = struct_span_err!(item.tcx.sess, span, E0013,
                                        "{}s cannot refer to statics, use \
                                        a constant instead", item.const_kind());
        if item.tcx.sess.teach(&err.get_code().unwrap()) {
            err.note(
                "Static and const variables can refer to other const variables. \
                    But a const variable cannot refer to a static variable."
            );
            err.help(
                "To fix this, the value can be extracted as a const and then used."
            );
        }
        err.emit();
    }
}

/// An access to a thread-local `static`.
#[derive(Debug)]
pub struct ThreadLocalAccess;
impl NonConstOp for ThreadLocalAccess {
    const IS_SUPPORTED_IN_MIRI: bool = false;

    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        span_err!(item.tcx.sess, span, E0625,
            "thread-local statics cannot be \
            accessed at compile-time");
    }
}

#[derive(Debug)]
pub struct Transmute;
impl NonConstOp for Transmute {
    fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
        Some(tcx.features().const_transmute)
    }

    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        feature_err(
            &item.tcx.sess.parse_sess, sym::const_transmute, span,
            &format!("The use of std::mem::transmute() is gated in {}s", item.const_kind())
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct UnionAccess;
impl NonConstOp for UnionAccess {
    fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
        // Union accesses are stable in all contexts except `const fn`.
        item.const_kind() != ConstKind::ConstFn || Self::feature_gate(item.tcx).unwrap()
    }

    fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
        Some(tcx.features().const_fn_union)
    }

    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        feature_err(
            &item.tcx.sess.parse_sess, sym::const_fn_union, span,
            "unions in const fn are unstable",
        )
        .emit();
    }
}
<commit_msg>Allow mutable borrows in constant bodies<commit_after>//! Concrete error types for all operations which may be invalid in a certain const context.

use rustc::hir::def_id::DefId;
use rustc::mir::BorrowKind;
use rustc::session::config::nightly_options;
use rustc::ty::TyCtxt;
use syntax::feature_gate::feature_err;
use syntax::symbol::sym;
use syntax_pos::{Span, Symbol};

use super::{ConstKind, Item};

use rustc_error_codes::*;

/// An operation that is not *always* allowed in a const context.
pub trait NonConstOp: std::fmt::Debug {
    /// Whether this operation can be evaluated by miri.
    const IS_SUPPORTED_IN_MIRI: bool = true;

    /// Returns a boolean indicating whether the feature gate that would allow this operation is
    /// enabled, or `None` if such a feature gate does not exist.
    fn feature_gate(_tcx: TyCtxt<'tcx>) -> Option<bool> {
        None
    }

    /// Returns `true` if this operation is allowed in the given item.
    ///
    /// This check should assume that we are not in a non-const `fn`, where all operations are
    /// legal.
    fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
        Self::feature_gate(item.tcx).unwrap_or(false)
    }

    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        let mut err = struct_span_err!(
            item.tcx.sess,
            span,
            E0019,
            "{} contains unimplemented expression type",
            item.const_kind()
        );
        if item.tcx.sess.teach(&err.get_code().unwrap()) {
            err.note("A function call isn't allowed in the const's initialization expression \
                      because the expression's value must be known at compile-time.");
            err.note("Remember: you can't use a function call inside a const's initialization \
                      expression! However, you can use it anywhere else.");
        }
        err.emit();
    }
}

/// A `Downcast` projection.
#[derive(Debug)]
pub struct Downcast;
impl NonConstOp for Downcast {
    fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
        Some(tcx.features().const_if_match)
    }
}

/// A function call where the callee is a pointer.
#[derive(Debug)]
pub struct FnCallIndirect;
impl NonConstOp for FnCallIndirect {
    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        let mut err = item.tcx.sess.struct_span_err(
            span,
            &format!("function pointers are not allowed in const fn"));
        err.emit();
    }
}

/// A function call where the callee is not marked as `const`.
#[derive(Debug)]
pub struct FnCallNonConst(pub DefId);
impl NonConstOp for FnCallNonConst {
    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        let mut err = struct_span_err!(
            item.tcx.sess,
            span,
            E0015,
            "calls in {}s are limited to constant functions, \
             tuple structs and tuple variants",
            item.const_kind(),
        );
        err.emit();
    }
}

/// A function call where the callee is not a function definition or function pointer, e.g. a
/// closure.
///
/// This can be subdivided in the future to produce a better error message.
#[derive(Debug)]
pub struct FnCallOther;
impl NonConstOp for FnCallOther {
    const IS_SUPPORTED_IN_MIRI: bool = false;
}

/// A call to a `#[unstable]` const fn or `#[rustc_const_unstable]` function.
///
/// Contains the name of the feature that would allow the use of this function.
#[derive(Debug)]
pub struct FnCallUnstable(pub DefId, pub Symbol);
impl NonConstOp for FnCallUnstable {
    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        let FnCallUnstable(def_id, feature) = *self;

        let mut err = item.tcx.sess.struct_span_err(span,
            &format!("`{}` is not yet stable as a const fn",
                    item.tcx.def_path_str(def_id)));
        if nightly_options::is_nightly_build() {
            help!(&mut err,
                  "add `#![feature({})]` to the \
                   crate attributes to enable",
                  feature);
        }
        err.emit();
    }
}

#[derive(Debug)]
pub struct HeapAllocation;
impl NonConstOp for HeapAllocation {
    const IS_SUPPORTED_IN_MIRI: bool = false;

    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        let mut err = struct_span_err!(item.tcx.sess, span, E0010,
                                       "allocations are not allowed in {}s", item.const_kind());
        err.span_label(span, format!("allocation not allowed in {}s", item.const_kind()));
        if item.tcx.sess.teach(&err.get_code().unwrap()) {
            err.note(
                "The value of statics and constants must be known at compile time, \
                 and they live for the entire lifetime of a program. Creating a boxed \
                 value allocates memory on the heap at runtime, and therefore cannot \
                 be done at compile time."
            );
        }
        err.emit();
    }
}

#[derive(Debug)]
pub struct IfOrMatch;
impl NonConstOp for IfOrMatch {
    fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
        Some(tcx.features().const_if_match)
    }

    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        // This should be caught by the HIR const-checker.
        item.tcx.sess.delay_span_bug(
            span,
            "complex control flow is forbidden in a const context",
        );
    }
}

#[derive(Debug)]
pub struct LiveDrop;
impl NonConstOp for LiveDrop {
    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        struct_span_err!(item.tcx.sess, span, E0493,
                         "destructors cannot be evaluated at compile-time")
            .span_label(span, format!("{}s cannot evaluate destructors",
                                      item.const_kind()))
            .emit();
    }
}

#[derive(Debug)]
pub struct Loop;
impl NonConstOp for Loop {
    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        // This should be caught by the HIR const-checker.
        item.tcx.sess.delay_span_bug(
            span,
            "complex control flow is forbidden in a const context",
        );
    }
}

#[derive(Debug)]
pub struct MutBorrow(pub BorrowKind);
impl NonConstOp for MutBorrow {
    fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
        Some(tcx.features().const_mut_refs)
    }

    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        let kind = self.0;
        if let BorrowKind::Mut { .. } = kind {
            let mut err = struct_span_err!(item.tcx.sess, span, E0017,
                                           "references in {}s may only refer \
                                            to immutable values", item.const_kind());
            err.span_label(span, format!("{}s require immutable values",
                                                item.const_kind()));
            if item.tcx.sess.teach(&err.get_code().unwrap()) {
                err.note("References in statics and constants may only refer \
                          to immutable values.\n\n\
                          Statics are shared everywhere, and if they refer to \
                          mutable data one might violate memory safety since \
                          holding multiple mutable references to shared data \
                          is not allowed.\n\n\
                          If you really want global mutable state, try using \
                          static mut or a global UnsafeCell.");
            }
            err.emit();
        } else {
            span_err!(item.tcx.sess, span, E0492,
                      "cannot borrow a constant which may contain \
                       interior mutability, create a static instead");
        }
    }
}

#[derive(Debug)]
pub struct MutDeref;
impl NonConstOp for MutDeref {}

#[derive(Debug)]
pub struct Panic;
impl NonConstOp for Panic {
    fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
        Some(tcx.features().const_panic)
    }

    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        feature_err(
            &item.tcx.sess.parse_sess,
            sym::const_panic,
            span,
            &format!("panicking in {}s is unstable", item.const_kind()),
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct RawPtrComparison;
impl NonConstOp for RawPtrComparison {
    fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
        Some(tcx.features().const_compare_raw_pointers)
    }

    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        feature_err(
            &item.tcx.sess.parse_sess,
            sym::const_compare_raw_pointers,
            span,
            &format!("comparing raw pointers inside {}", item.const_kind()),
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct RawPtrDeref;
impl NonConstOp for RawPtrDeref {
    fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
        Some(tcx.features().const_raw_ptr_deref)
    }

    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        feature_err(
            &item.tcx.sess.parse_sess, sym::const_raw_ptr_deref, span,
            &format!(
                "dereferencing raw pointers in {}s is unstable",
                item.const_kind(),
            ),
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct RawPtrToIntCast;
impl NonConstOp for RawPtrToIntCast {
    fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
        Some(tcx.features().const_raw_ptr_to_usize_cast)
    }

    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        feature_err(
            &item.tcx.sess.parse_sess, sym::const_raw_ptr_to_usize_cast, span,
            &format!(
                "casting pointers to integers in {}s is unstable",
                item.const_kind(),
            ),
        )
        .emit();
    }
}

/// An access to a (non-thread-local) `static`.
#[derive(Debug)]
pub struct StaticAccess;
impl NonConstOp for StaticAccess {
    fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
        item.const_kind().is_static()
    }

    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        let mut err = struct_span_err!(item.tcx.sess, span, E0013,
                                        "{}s cannot refer to statics, use \
                                        a constant instead", item.const_kind());
        if item.tcx.sess.teach(&err.get_code().unwrap()) {
            err.note(
                "Static and const variables can refer to other const variables. \
                    But a const variable cannot refer to a static variable."
            );
            err.help(
                "To fix this, the value can be extracted as a const and then used."
            );
        }
        err.emit();
    }
}

/// An access to a thread-local `static`.
#[derive(Debug)]
pub struct ThreadLocalAccess;
impl NonConstOp for ThreadLocalAccess {
    const IS_SUPPORTED_IN_MIRI: bool = false;

    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        span_err!(item.tcx.sess, span, E0625,
            "thread-local statics cannot be \
            accessed at compile-time");
    }
}

#[derive(Debug)]
pub struct Transmute;
impl NonConstOp for Transmute {
    fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
        Some(tcx.features().const_transmute)
    }

    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        feature_err(
            &item.tcx.sess.parse_sess, sym::const_transmute, span,
            &format!("The use of std::mem::transmute() is gated in {}s", item.const_kind())
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct UnionAccess;
impl NonConstOp for UnionAccess {
    fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
        // Union accesses are stable in all contexts except `const fn`.
        item.const_kind() != ConstKind::ConstFn || Self::feature_gate(item.tcx).unwrap()
    }

    fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
        Some(tcx.features().const_fn_union)
    }

    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
        feature_err(
            &item.tcx.sess.parse_sess, sym::const_fn_union, span,
            "unions in const fn are unstable",
        )
        .emit();
    }
}
<|endoftext|>