<commit_before>//! The implementation is based on Dmitry Vyukov's bounded MPMC queue.
//!
//! Source:
//!   - http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue
//!
//! Copyright & License:
//!   - Copyright (c) 2010-2011 Dmitry Vyukov
//!   - Simplified BSD License and Apache License, Version 2.0
//!   - http://www.1024cores.net/home/code-license

use alloc::vec::Vec;
use core::cell::UnsafeCell;
use core::fmt;
use core::marker::PhantomData;
use core::mem;
use core::ptr;
use core::sync::atomic::{self, AtomicUsize, Ordering};

use crossbeam_utils::{Backoff, CachePadded};

use maybe_uninit::MaybeUninit;

use err::{PopError, PushError};

/// A slot in a queue.
struct Slot<T> {
    /// The current stamp.
    ///
    /// If the stamp equals the tail, this node will be next written to. If it equals head + 1,
    /// this node will be next read from.
    stamp: AtomicUsize,

    /// The value in this slot.
    value: UnsafeCell<MaybeUninit<T>>,
}

/// A bounded multi-producer multi-consumer queue.
///
/// This queue allocates a fixed-capacity buffer on construction, which is used to store pushed
/// elements. The queue cannot hold more elements than the buffer allows. Attempting to push an
/// element into a full queue will fail. Having a buffer allocated upfront makes this queue a bit
/// faster than [`SegQueue`].
///
/// [`SegQueue`]: struct.SegQueue.html
///
/// # Examples
///
/// ```
/// use crossbeam_queue::{ArrayQueue, PushError};
///
/// let q = ArrayQueue::new(2);
///
/// assert_eq!(q.push('a'), Ok(()));
/// assert_eq!(q.push('b'), Ok(()));
/// assert_eq!(q.push('c'), Err(PushError('c')));
/// assert_eq!(q.pop(), Ok('a'));
/// ```
pub struct ArrayQueue<T> {
    /// The head of the queue.
    ///
    /// This value is a "stamp" consisting of an index into the buffer and a lap, but packed into a
    /// single `usize`. The lower bits represent the index, while the upper bits represent the lap.
    ///
    /// Elements are popped from the head of the queue.
    head: CachePadded<AtomicUsize>,

    /// The tail of the queue.
    ///
    /// This value is a "stamp" consisting of an index into the buffer and a lap, but packed into a
    /// single `usize`. The lower bits represent the index, while the upper bits represent the lap.
    ///
    /// Elements are pushed into the tail of the queue.
    tail: CachePadded<AtomicUsize>,

    /// The buffer holding slots.
    buffer: *mut Slot<T>,

    /// The queue capacity.
    cap: usize,

    /// A stamp with the value of `{ lap: 1, index: 0 }`.
    one_lap: usize,

    /// Indicates that dropping an `ArrayQueue<T>` may drop elements of type `T`.
    _marker: PhantomData<T>,
}

unsafe impl<T: Send> Sync for ArrayQueue<T> {}
unsafe impl<T: Send> Send for ArrayQueue<T> {}

impl<T> ArrayQueue<T> {
    /// Creates a new bounded queue with the given capacity.
    ///
    /// # Panics
    ///
    /// Panics if the capacity is zero.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_queue::ArrayQueue;
    ///
    /// let q = ArrayQueue::<i32>::new(100);
    /// ```
    pub fn new(cap: usize) -> ArrayQueue<T> {
        assert!(cap > 0, "capacity must be non-zero");

        // Head is initialized to `{ lap: 0, index: 0 }`.
        // Tail is initialized to `{ lap: 0, index: 0 }`.
        let head = 0;
        let tail = 0;

        // Allocate a buffer of `cap` slots.
        let buffer = {
            let mut v = Vec::<Slot<T>>::with_capacity(cap);
            let ptr = v.as_mut_ptr();
            mem::forget(v);
            ptr
        };

        // Initialize stamps in the slots.
        for i in 0..cap {
            unsafe {
                // Set the stamp to `{ lap: 0, index: i }`.
                let slot = buffer.add(i);
                ptr::write(&mut (*slot).stamp, AtomicUsize::new(i));
            }
        }

        // One lap is the smallest power of two greater than `cap`.
        let one_lap = (cap + 1).next_power_of_two();

        ArrayQueue {
            buffer,
            cap,
            one_lap,
            head: CachePadded::new(AtomicUsize::new(head)),
            tail: CachePadded::new(AtomicUsize::new(tail)),
            _marker: PhantomData,
        }
    }

    /// Attempts to push an element into the queue.
    ///
    /// If the queue is full, the element is returned back as an error.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_queue::{ArrayQueue, PushError};
    ///
    /// let q = ArrayQueue::new(1);
    ///
    /// assert_eq!(q.push(10), Ok(()));
    /// assert_eq!(q.push(20), Err(PushError(20)));
    /// ```
    pub fn push(&self, value: T) -> Result<(), PushError<T>> {
        let backoff = Backoff::new();
        let mut tail = self.tail.load(Ordering::Relaxed);

        loop {
            // Deconstruct the tail.
            let index = tail & (self.one_lap - 1);
            let lap = tail & !(self.one_lap - 1);

            // Inspect the corresponding slot.
            let slot = unsafe { &*self.buffer.add(index) };
            let stamp = slot.stamp.load(Ordering::Acquire);

            // If the tail and the stamp match, we may attempt to push.
            if tail == stamp {
                let new_tail = if index + 1 < self.cap {
                    // Same lap, incremented index.
                    // Set to `{ lap: lap, index: index + 1 }`.
                    tail + 1
                } else {
                    // One lap forward, index wraps around to zero.
                    // Set to `{ lap: lap.wrapping_add(1), index: 0 }`.
                    lap.wrapping_add(self.one_lap)
                };

                // Try moving the tail.
                match self.tail.compare_exchange_weak(
                    tail,
                    new_tail,
                    Ordering::SeqCst,
                    Ordering::Relaxed,
                ) {
                    Ok(_) => {
                        // Write the value into the slot and update the stamp.
                        unsafe { slot.value.get().write(MaybeUninit::new(value)); }
                        slot.stamp.store(tail + 1, Ordering::Release);
                        return Ok(());
                    }
                    Err(t) => {
                        tail = t;
                        backoff.spin();
                    }
                }
            } else if stamp.wrapping_add(self.one_lap) == tail + 1 {
                atomic::fence(Ordering::SeqCst);
                let head = self.head.load(Ordering::Relaxed);

                // If the head lags one lap behind the tail as well...
                if head.wrapping_add(self.one_lap) == tail {
                    // ...then the queue is full.
                    return Err(PushError(value));
                }

                backoff.spin();
                tail = self.tail.load(Ordering::Relaxed);
            } else {
                // Snooze because we need to wait for the stamp to get updated.
                backoff.snooze();
                tail = self.tail.load(Ordering::Relaxed);
            }
        }
    }

    /// Attempts to pop an element from the queue.
    ///
    /// If the queue is empty, an error is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_queue::{ArrayQueue, PopError};
    ///
    /// let q = ArrayQueue::new(1);
    /// assert_eq!(q.push(10), Ok(()));
    ///
    /// assert_eq!(q.pop(), Ok(10));
    /// assert_eq!(q.pop(), Err(PopError));
    /// ```
    pub fn pop(&self) -> Result<T, PopError> {
        let backoff = Backoff::new();
        let mut head = self.head.load(Ordering::Relaxed);

        loop {
            // Deconstruct the head.
            let index = head & (self.one_lap - 1);
            let lap = head & !(self.one_lap - 1);

            // Inspect the corresponding slot.
            let slot = unsafe { &*self.buffer.add(index) };
            let stamp = slot.stamp.load(Ordering::Acquire);

            // If the the stamp is ahead of the head by 1, we may attempt to pop.
            if head + 1 == stamp {
                let new = if index + 1 < self.cap {
                    // Same lap, incremented index.
                    // Set to `{ lap: lap, index: index + 1 }`.
                    head + 1
                } else {
                    // One lap forward, index wraps around to zero.
                    // Set to `{ lap: lap.wrapping_add(1), index: 0 }`.
                    lap.wrapping_add(self.one_lap)
                };

                // Try moving the head.
                match self.head.compare_exchange_weak(
                    head,
                    new,
                    Ordering::SeqCst,
                    Ordering::Relaxed,
                ) {
                    Ok(_) => {
                        // Read the value from the slot and update the stamp.
                        let msg = unsafe { slot.value.get().read().assume_init() };
                        slot.stamp
                            .store(head.wrapping_add(self.one_lap), Ordering::Release);
                        return Ok(msg);
                    }
                    Err(h) => {
                        head = h;
                        backoff.spin();
                    }
                }
            } else if stamp == head {
                atomic::fence(Ordering::SeqCst);
                let tail = self.tail.load(Ordering::Relaxed);

                // If the tail equals the head, that means the channel is empty.
                if tail == head {
                    return Err(PopError);
                }

                backoff.spin();
                head = self.head.load(Ordering::Relaxed);
            } else {
                // Snooze because we need to wait for the stamp to get updated.
                backoff.snooze();
                head = self.head.load(Ordering::Relaxed);
            }
        }
    }

    /// Returns the capacity of the queue.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_queue::{ArrayQueue, PopError};
    ///
    /// let q = ArrayQueue::<i32>::new(100);
    ///
    /// assert_eq!(q.capacity(), 100);
    /// ```
    pub fn capacity(&self) -> usize {
        self.cap
    }

    /// Returns `true` if the queue is empty.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_queue::{ArrayQueue, PopError};
    ///
    /// let q = ArrayQueue::new(100);
    ///
    /// assert!(q.is_empty());
    /// q.push(1).unwrap();
    /// assert!(!q.is_empty());
    /// ```
    pub fn is_empty(&self) -> bool {
        let head = self.head.load(Ordering::SeqCst);
        let tail = self.tail.load(Ordering::SeqCst);

        // Is the tail lagging one lap behind head?
        // Is the tail equal to the head?
        //
        // Note: If the head changes just before we load the tail, that means there was a moment
        // when the channel was not empty, so it is safe to just return `false`.
        tail == head
    }

    /// Returns `true` if the queue is full.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_queue::{ArrayQueue, PopError};
    ///
    /// let q = ArrayQueue::new(1);
    ///
    /// assert!(!q.is_full());
    /// q.push(1).unwrap();
    /// assert!(q.is_full());
    /// ```
    pub fn is_full(&self) -> bool {
        let tail = self.tail.load(Ordering::SeqCst);
        let head = self.head.load(Ordering::SeqCst);

        // Is the head lagging one lap behind tail?
        //
        // Note: If the tail changes just before we load the head, that means there was a moment
        // when the queue was not full, so it is safe to just return `false`.
        head.wrapping_add(self.one_lap) == tail
    }

    /// Returns the number of elements in the queue.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_queue::{ArrayQueue, PopError};
    ///
    /// let q = ArrayQueue::new(100);
    /// assert_eq!(q.len(), 0);
    ///
    /// q.push(10).unwrap();
    /// assert_eq!(q.len(), 1);
    ///
    /// q.push(20).unwrap();
    /// assert_eq!(q.len(), 2);
    /// ```
    pub fn len(&self) -> usize {
        loop {
            // Load the tail, then load the head.
            let tail = self.tail.load(Ordering::SeqCst);
            let head = self.head.load(Ordering::SeqCst);

            // If the tail didn't change, we've got consistent values to work with.
            if self.tail.load(Ordering::SeqCst) == tail {
                let hix = head & (self.one_lap - 1);
                let tix = tail & (self.one_lap - 1);

                return if hix < tix {
                    tix - hix
                } else if hix > tix {
                    self.cap - hix + tix
                } else if tail == head {
                    0
                } else {
                    self.cap
                };
            }
        }
    }
}

impl<T> Drop for ArrayQueue<T> {
    fn drop(&mut self) {
        // Get the index of the head.
        let hix = self.head.load(Ordering::Relaxed) & (self.one_lap - 1);

        // Loop over all slots that hold a message and drop them.
        for i in 0..self.len() {
            // Compute the index of the next slot holding a message.
            let index = if hix + i < self.cap {
                hix + i
            } else {
                hix + i - self.cap
            };

            unsafe {
                let p = {
                    let slot = &mut *self.buffer.add(index);
                    let value = &mut *slot.value.get();
                    value.as_mut_ptr()
                };
                p.drop_in_place();
            }
        }

        // Finally, deallocate the buffer, but don't run any destructors.
        unsafe {
            Vec::from_raw_parts(self.buffer, 0, self.cap);
        }
    }
}

impl<T> fmt::Debug for ArrayQueue<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.pad("ArrayQueue { .. }")
    }
}
<commit_msg>Fix formatting issues in crossbeam-queue/src/array_queue.rs<commit_after>//! The implementation is based on Dmitry Vyukov's bounded MPMC queue.
//!
//! Source:
//!   - http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue
//!
//! Copyright & License:
//!   - Copyright (c) 2010-2011 Dmitry Vyukov
//!   - Simplified BSD License and Apache License, Version 2.0
//!   - http://www.1024cores.net/home/code-license

use alloc::vec::Vec;
use core::cell::UnsafeCell;
use core::fmt;
use core::marker::PhantomData;
use core::mem;
use core::ptr;
use core::sync::atomic::{self, AtomicUsize, Ordering};

use crossbeam_utils::{Backoff, CachePadded};

use maybe_uninit::MaybeUninit;

use err::{PopError, PushError};

/// A slot in a queue.
struct Slot<T> {
    /// The current stamp.
    ///
    /// If the stamp equals the tail, this node will be next written to. If it equals head + 1,
    /// this node will be next read from.
    stamp: AtomicUsize,

    /// The value in this slot.
    value: UnsafeCell<MaybeUninit<T>>,
}

/// A bounded multi-producer multi-consumer queue.
///
/// This queue allocates a fixed-capacity buffer on construction, which is used to store pushed
/// elements. The queue cannot hold more elements than the buffer allows. Attempting to push an
/// element into a full queue will fail. Having a buffer allocated upfront makes this queue a bit
/// faster than [`SegQueue`].
///
/// [`SegQueue`]: struct.SegQueue.html
///
/// # Examples
///
/// ```
/// use crossbeam_queue::{ArrayQueue, PushError};
///
/// let q = ArrayQueue::new(2);
///
/// assert_eq!(q.push('a'), Ok(()));
/// assert_eq!(q.push('b'), Ok(()));
/// assert_eq!(q.push('c'), Err(PushError('c')));
/// assert_eq!(q.pop(), Ok('a'));
/// ```
pub struct ArrayQueue<T> {
    /// The head of the queue.
    ///
    /// This value is a "stamp" consisting of an index into the buffer and a lap, but packed into a
    /// single `usize`. The lower bits represent the index, while the upper bits represent the lap.
    ///
    /// Elements are popped from the head of the queue.
    head: CachePadded<AtomicUsize>,

    /// The tail of the queue.
    ///
    /// This value is a "stamp" consisting of an index into the buffer and a lap, but packed into a
    /// single `usize`. The lower bits represent the index, while the upper bits represent the lap.
    ///
    /// Elements are pushed into the tail of the queue.
    tail: CachePadded<AtomicUsize>,

    /// The buffer holding slots.
    buffer: *mut Slot<T>,

    /// The queue capacity.
    cap: usize,

    /// A stamp with the value of `{ lap: 1, index: 0 }`.
    one_lap: usize,

    /// Indicates that dropping an `ArrayQueue<T>` may drop elements of type `T`.
    _marker: PhantomData<T>,
}

unsafe impl<T: Send> Sync for ArrayQueue<T> {}
unsafe impl<T: Send> Send for ArrayQueue<T> {}

impl<T> ArrayQueue<T> {
    /// Creates a new bounded queue with the given capacity.
    ///
    /// # Panics
    ///
    /// Panics if the capacity is zero.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_queue::ArrayQueue;
    ///
    /// let q = ArrayQueue::<i32>::new(100);
    /// ```
    pub fn new(cap: usize) -> ArrayQueue<T> {
        assert!(cap > 0, "capacity must be non-zero");

        // Head is initialized to `{ lap: 0, index: 0 }`.
        // Tail is initialized to `{ lap: 0, index: 0 }`.
        let head = 0;
        let tail = 0;

        // Allocate a buffer of `cap` slots.
        let buffer = {
            let mut v = Vec::<Slot<T>>::with_capacity(cap);
            let ptr = v.as_mut_ptr();
            mem::forget(v);
            ptr
        };

        // Initialize stamps in the slots.
        for i in 0..cap {
            unsafe {
                // Set the stamp to `{ lap: 0, index: i }`.
                let slot = buffer.add(i);
                ptr::write(&mut (*slot).stamp, AtomicUsize::new(i));
            }
        }

        // One lap is the smallest power of two greater than `cap`.
        let one_lap = (cap + 1).next_power_of_two();

        ArrayQueue {
            buffer,
            cap,
            one_lap,
            head: CachePadded::new(AtomicUsize::new(head)),
            tail: CachePadded::new(AtomicUsize::new(tail)),
            _marker: PhantomData,
        }
    }

    /// Attempts to push an element into the queue.
    ///
    /// If the queue is full, the element is returned back as an error.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_queue::{ArrayQueue, PushError};
    ///
    /// let q = ArrayQueue::new(1);
    ///
    /// assert_eq!(q.push(10), Ok(()));
    /// assert_eq!(q.push(20), Err(PushError(20)));
    /// ```
    pub fn push(&self, value: T) -> Result<(), PushError<T>> {
        let backoff = Backoff::new();
        let mut tail = self.tail.load(Ordering::Relaxed);

        loop {
            // Deconstruct the tail.
            let index = tail & (self.one_lap - 1);
            let lap = tail & !(self.one_lap - 1);

            // Inspect the corresponding slot.
            let slot = unsafe { &*self.buffer.add(index) };
            let stamp = slot.stamp.load(Ordering::Acquire);

            // If the tail and the stamp match, we may attempt to push.
            if tail == stamp {
                let new_tail = if index + 1 < self.cap {
                    // Same lap, incremented index.
                    // Set to `{ lap: lap, index: index + 1 }`.
                    tail + 1
                } else {
                    // One lap forward, index wraps around to zero.
                    // Set to `{ lap: lap.wrapping_add(1), index: 0 }`.
                    lap.wrapping_add(self.one_lap)
                };

                // Try moving the tail.
                match self.tail.compare_exchange_weak(
                    tail,
                    new_tail,
                    Ordering::SeqCst,
                    Ordering::Relaxed,
                ) {
                    Ok(_) => {
                        // Write the value into the slot and update the stamp.
                        unsafe {
                            slot.value.get().write(MaybeUninit::new(value));
                        }
                        slot.stamp.store(tail + 1, Ordering::Release);
                        return Ok(());
                    }
                    Err(t) => {
                        tail = t;
                        backoff.spin();
                    }
                }
            } else if stamp.wrapping_add(self.one_lap) == tail + 1 {
                atomic::fence(Ordering::SeqCst);
                let head = self.head.load(Ordering::Relaxed);

                // If the head lags one lap behind the tail as well...
                if head.wrapping_add(self.one_lap) == tail {
                    // ...then the queue is full.
                    return Err(PushError(value));
                }

                backoff.spin();
                tail = self.tail.load(Ordering::Relaxed);
            } else {
                // Snooze because we need to wait for the stamp to get updated.
                backoff.snooze();
                tail = self.tail.load(Ordering::Relaxed);
            }
        }
    }

    /// Attempts to pop an element from the queue.
    ///
    /// If the queue is empty, an error is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_queue::{ArrayQueue, PopError};
    ///
    /// let q = ArrayQueue::new(1);
    /// assert_eq!(q.push(10), Ok(()));
    ///
    /// assert_eq!(q.pop(), Ok(10));
    /// assert_eq!(q.pop(), Err(PopError));
    /// ```
    pub fn pop(&self) -> Result<T, PopError> {
        let backoff = Backoff::new();
        let mut head = self.head.load(Ordering::Relaxed);

        loop {
            // Deconstruct the head.
            let index = head & (self.one_lap - 1);
            let lap = head & !(self.one_lap - 1);

            // Inspect the corresponding slot.
            let slot = unsafe { &*self.buffer.add(index) };
            let stamp = slot.stamp.load(Ordering::Acquire);

            // If the the stamp is ahead of the head by 1, we may attempt to pop.
            if head + 1 == stamp {
                let new = if index + 1 < self.cap {
                    // Same lap, incremented index.
                    // Set to `{ lap: lap, index: index + 1 }`.
                    head + 1
                } else {
                    // One lap forward, index wraps around to zero.
                    // Set to `{ lap: lap.wrapping_add(1), index: 0 }`.
                    lap.wrapping_add(self.one_lap)
                };

                // Try moving the head.
                match self.head.compare_exchange_weak(
                    head,
                    new,
                    Ordering::SeqCst,
                    Ordering::Relaxed,
                ) {
                    Ok(_) => {
                        // Read the value from the slot and update the stamp.
                        let msg = unsafe { slot.value.get().read().assume_init() };
                        slot.stamp
                            .store(head.wrapping_add(self.one_lap), Ordering::Release);
                        return Ok(msg);
                    }
                    Err(h) => {
                        head = h;
                        backoff.spin();
                    }
                }
            } else if stamp == head {
                atomic::fence(Ordering::SeqCst);
                let tail = self.tail.load(Ordering::Relaxed);

                // If the tail equals the head, that means the channel is empty.
                if tail == head {
                    return Err(PopError);
                }

                backoff.spin();
                head = self.head.load(Ordering::Relaxed);
            } else {
                // Snooze because we need to wait for the stamp to get updated.
                backoff.snooze();
                head = self.head.load(Ordering::Relaxed);
            }
        }
    }

    /// Returns the capacity of the queue.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_queue::{ArrayQueue, PopError};
    ///
    /// let q = ArrayQueue::<i32>::new(100);
    ///
    /// assert_eq!(q.capacity(), 100);
    /// ```
    pub fn capacity(&self) -> usize {
        self.cap
    }

    /// Returns `true` if the queue is empty.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_queue::{ArrayQueue, PopError};
    ///
    /// let q = ArrayQueue::new(100);
    ///
    /// assert!(q.is_empty());
    /// q.push(1).unwrap();
    /// assert!(!q.is_empty());
    /// ```
    pub fn is_empty(&self) -> bool {
        let head = self.head.load(Ordering::SeqCst);
        let tail = self.tail.load(Ordering::SeqCst);

        // Is the tail lagging one lap behind head?
        // Is the tail equal to the head?
        //
        // Note: If the head changes just before we load the tail, that means there was a moment
        // when the channel was not empty, so it is safe to just return `false`.
        tail == head
    }

    /// Returns `true` if the queue is full.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_queue::{ArrayQueue, PopError};
    ///
    /// let q = ArrayQueue::new(1);
    ///
    /// assert!(!q.is_full());
    /// q.push(1).unwrap();
    /// assert!(q.is_full());
    /// ```
    pub fn is_full(&self) -> bool {
        let tail = self.tail.load(Ordering::SeqCst);
        let head = self.head.load(Ordering::SeqCst);

        // Is the head lagging one lap behind tail?
        //
        // Note: If the tail changes just before we load the head, that means there was a moment
        // when the queue was not full, so it is safe to just return `false`.
        head.wrapping_add(self.one_lap) == tail
    }

    /// Returns the number of elements in the queue.
    ///
    /// # Examples
    ///
    /// ```
    /// use crossbeam_queue::{ArrayQueue, PopError};
    ///
    /// let q = ArrayQueue::new(100);
    /// assert_eq!(q.len(), 0);
    ///
    /// q.push(10).unwrap();
    /// assert_eq!(q.len(), 1);
    ///
    /// q.push(20).unwrap();
    /// assert_eq!(q.len(), 2);
    /// ```
    pub fn len(&self) -> usize {
        loop {
            // Load the tail, then load the head.
            let tail = self.tail.load(Ordering::SeqCst);
            let head = self.head.load(Ordering::SeqCst);

            // If the tail didn't change, we've got consistent values to work with.
            if self.tail.load(Ordering::SeqCst) == tail {
                let hix = head & (self.one_lap - 1);
                let tix = tail & (self.one_lap - 1);

                return if hix < tix {
                    tix - hix
                } else if hix > tix {
                    self.cap - hix + tix
                } else if tail == head {
                    0
                } else {
                    self.cap
                };
            }
        }
    }
}

impl<T> Drop for ArrayQueue<T> {
    fn drop(&mut self) {
        // Get the index of the head.
        let hix = self.head.load(Ordering::Relaxed) & (self.one_lap - 1);

        // Loop over all slots that hold a message and drop them.
        for i in 0..self.len() {
            // Compute the index of the next slot holding a message.
            let index = if hix + i < self.cap {
                hix + i
            } else {
                hix + i - self.cap
            };

            unsafe {
                let p = {
                    let slot = &mut *self.buffer.add(index);
                    let value = &mut *slot.value.get();
                    value.as_mut_ptr()
                };
                p.drop_in_place();
            }
        }

        // Finally, deallocate the buffer, but don't run any destructors.
        unsafe {
            Vec::from_raw_parts(self.buffer, 0, self.cap);
        }
    }
}

impl<T> fmt::Debug for ArrayQueue<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.pad("ArrayQueue { .. }")
    }
}
<|endoftext|>


<commit_before>#![feature(unsize, coerce_unsized, raw_ref_op)]

use std::collections::hash_map::DefaultHasher;
use std::hash::Hash;

fn test_basic() {
    #[repr(packed)]
    struct S {
        fill: u8,
        a: i32,
        b: i64,
    }

    #[repr(packed)]
    #[allow(dead_code)]
    struct Test1<'a> {
        x: u8,
        other: &'a u32,
    }

    #[repr(packed)]
    #[allow(dead_code)]
    struct Test2<'a> {
        x: u8,
        other: &'a Test1<'a>,
    }

    fn test(t: Test2) {
        let x = *t.other.other;
        assert_eq!(x, 42);
    }

    let mut x = S {
        fill: 0,
        a: 42,
        b: 99,
    };
    let a = x.a;
    let b = x.b;
    assert_eq!(a, 42);
    assert_eq!(b, 99);
    assert_eq!(&x.fill, &0); // `fill` just requirs 1-byte-align, so this is fine
    // can't do `assert_eq!(x.a, 42)`, because `assert_eq!` takes a reference
    assert_eq!({x.a}, 42);
    assert_eq!({x.b}, 99);
    // but we *can* take a raw pointer!
    assert_eq!(unsafe { (&raw const x.a).read_unaligned() }, 42);
    assert_eq!(unsafe { (&raw const x.b).read_unaligned() }, 99);

    x.b = 77;
    assert_eq!({x.b}, 77);

    test(Test2 { x: 0, other: &Test1 { x: 0, other: &42 }});
}

fn test_unsizing() {
    #[repr(packed)]
    #[allow(dead_code)]
    struct UnalignedPtr<'a, T: ?Sized>
    where T: 'a,
    {
        data: &'a T,
    }

    impl<'a, T, U> std::ops::CoerceUnsized<UnalignedPtr<'a, U>> for UnalignedPtr<'a, T>
    where
        T: std::marker::Unsize<U> + ?Sized,
        U: ?Sized,
    { }

    let arr = [1, 2, 3];
    let arr_unaligned: UnalignedPtr<[i32; 3]> = UnalignedPtr { data: &arr };
    let arr_unaligned: UnalignedPtr<[i32]> = arr_unaligned;
    let _unused = &arr_unaligned; // forcing an allocation, which could also yield "unaligned write"-errors
}

fn test_drop() {
    struct Wrap(u32);
    impl Drop for Wrap {
        fn drop(&mut self) {
            // Do an (aligned) load
            let _test = self.0;
            // For the fun of it, test alignment
            assert_eq!(&self.0 as *const _ as usize % std::mem::align_of::<u32>(), 0);
        }
    }

    #[repr(packed,C)]
    struct Packed<T> {
        f1: u8, // this should move the second field to something not very aligned
        f2: T,
    }

    let p = Packed { f1: 42, f2: Wrap(23) };
    drop(p);
}

fn test_inner_packed() {
    // Even if just the inner struct is packed, accesses to the outer field can get unaligned.
    // Make sure that works.
    #[repr(packed)]
    #[derive(Clone,Copy)]
    struct Inner(u32);

    #[derive(Clone,Copy)]
    struct Outer(u8, Inner);

    let o = Outer(0, Inner(42));
    let _x = o.1;
    let _y = (o.1).0;
    let _o2 = o.clone();
}

fn test_static() {
    #[repr(packed)]
    struct Foo {
        i: i32
    }

    static FOO: Foo = Foo { i: 42 };

    assert_eq!({FOO.i}, 42);
}

fn test_derive() {
    #[repr(packed)]
    #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
    struct P {
        a: usize,
        b: u8,
        c: usize,
    }

    let x = P {a: 1usize, b: 2u8, c: 3usize};
    let y = P {a: 1usize, b: 2u8, c: 4usize};

    let _clone = x.clone();
    assert!(x != y);
    assert_eq!(x.partial_cmp(&y).unwrap(), x.cmp(&y));
    x.hash(&mut DefaultHasher::new());
    P::default();
    format!("{:?}", x);
}

fn main() {
    test_basic();
    test_unsizing();
    test_drop();
    test_inner_packed();
    test_static();
    test_derive();
}
<commit_msg>make sure the raw_ptr macros also avoid UB<commit_after>#![feature(unsize, coerce_unsized, raw_ref_op, raw_ref_macros)]

use std::collections::hash_map::DefaultHasher;
use std::hash::Hash;
use std::ptr;

fn test_basic() {
    #[repr(packed)]
    struct S {
        fill: u8,
        a: i32,
        b: i64,
    }

    #[repr(packed)]
    #[allow(dead_code)]
    struct Test1<'a> {
        x: u8,
        other: &'a u32,
    }

    #[repr(packed)]
    #[allow(dead_code)]
    struct Test2<'a> {
        x: u8,
        other: &'a Test1<'a>,
    }

    fn test(t: Test2) {
        let x = *t.other.other;
        assert_eq!(x, 42);
    }

    let mut x = S {
        fill: 0,
        a: 42,
        b: 99,
    };
    let a = x.a;
    let b = x.b;
    assert_eq!(a, 42);
    assert_eq!(b, 99);
    assert_eq!(&x.fill, &0); // `fill` just requirs 1-byte-align, so this is fine
    // can't do `assert_eq!(x.a, 42)`, because `assert_eq!` takes a reference
    assert_eq!({x.a}, 42);
    assert_eq!({x.b}, 99);
    // but we *can* take a raw pointer!
    assert_eq!(unsafe { (&raw const x.a).read_unaligned() }, 42);
    assert_eq!(unsafe { ptr::raw_const!(x.a).read_unaligned() }, 42);
    assert_eq!(unsafe { (&raw const x.b).read_unaligned() }, 99);
    assert_eq!(unsafe { ptr::raw_const!(x.b).read_unaligned() }, 99);

    x.b = 77;
    assert_eq!({x.b}, 77);

    test(Test2 { x: 0, other: &Test1 { x: 0, other: &42 }});
}

fn test_unsizing() {
    #[repr(packed)]
    #[allow(dead_code)]
    struct UnalignedPtr<'a, T: ?Sized>
    where T: 'a,
    {
        data: &'a T,
    }

    impl<'a, T, U> std::ops::CoerceUnsized<UnalignedPtr<'a, U>> for UnalignedPtr<'a, T>
    where
        T: std::marker::Unsize<U> + ?Sized,
        U: ?Sized,
    { }

    let arr = [1, 2, 3];
    let arr_unaligned: UnalignedPtr<[i32; 3]> = UnalignedPtr { data: &arr };
    let arr_unaligned: UnalignedPtr<[i32]> = arr_unaligned;
    let _unused = &arr_unaligned; // forcing an allocation, which could also yield "unaligned write"-errors
}

fn test_drop() {
    struct Wrap(u32);
    impl Drop for Wrap {
        fn drop(&mut self) {
            // Do an (aligned) load
            let _test = self.0;
            // For the fun of it, test alignment
            assert_eq!(&self.0 as *const _ as usize % std::mem::align_of::<u32>(), 0);
        }
    }

    #[repr(packed,C)]
    struct Packed<T> {
        f1: u8, // this should move the second field to something not very aligned
        f2: T,
    }

    let p = Packed { f1: 42, f2: Wrap(23) };
    drop(p);
}

fn test_inner_packed() {
    // Even if just the inner struct is packed, accesses to the outer field can get unaligned.
    // Make sure that works.
    #[repr(packed)]
    #[derive(Clone,Copy)]
    struct Inner(u32);

    #[derive(Clone,Copy)]
    struct Outer(u8, Inner);

    let o = Outer(0, Inner(42));
    let _x = o.1;
    let _y = (o.1).0;
    let _o2 = o.clone();
}

fn test_static() {
    #[repr(packed)]
    struct Foo {
        i: i32
    }

    static FOO: Foo = Foo { i: 42 };

    assert_eq!({FOO.i}, 42);
}

fn test_derive() {
    #[repr(packed)]
    #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
    struct P {
        a: usize,
        b: u8,
        c: usize,
    }

    let x = P {a: 1usize, b: 2u8, c: 3usize};
    let y = P {a: 1usize, b: 2u8, c: 4usize};

    let _clone = x.clone();
    assert!(x != y);
    assert_eq!(x.partial_cmp(&y).unwrap(), x.cmp(&y));
    x.hash(&mut DefaultHasher::new());
    P::default();
    format!("{:?}", x);
}

fn main() {
    test_basic();
    test_unsizing();
    test_drop();
    test_inner_packed();
    test_static();
    test_derive();
}
<|endoftext|>


<commit_before>use encoding::types::EncodingRef;

use std::u32;
use error::Error;
use history::{Cursor, History};
use buffer::Buffer;
use parser::{parse, ParseError, ParseSuccess};
use instr;

#[derive(Copy,Clone)]
pub enum EditMode {
    Emacs,
    Vi,
}

#[derive(Copy, Clone, PartialEq)]
pub enum ViMode {
    Insert,
    Normal,
    Replace,
    MoveChar(instr::CharMoveType),
    DeleteMoveChar(instr::CharMoveType),
    ChangeMoveChar(instr::CharMoveType),
    Delete,
    Change,
}

/// Set a new vi mode based on the current vi mode.
///
/// There are several common mode transitions that we handle here.
macro_rules! set_next_vi_mode {
    ( $ctx:ident ) => {
        $ctx.vi_mode = match $ctx.vi_mode {
            ViMode::Delete => ViMode::Normal,
            ViMode::Change => ViMode::Insert,
            ViMode::DeleteMoveChar(_) => ViMode::Normal,
            ViMode::ChangeMoveChar(_) => ViMode::Insert,
            _ => ViMode::Normal,
        }
    }
}

pub struct EditCtx<'a> {
    buf: Buffer,
    history_cursor: Cursor<'a>,
    prompt: &'a str,
    seq: Vec<u8>,
    enc: EncodingRef,
    mode: EditMode,
    vi_mode: ViMode,
    vi_count: u32,
}

impl<'a> EditCtx<'a> {

    pub fn new(prompt: &'a str, history: &'a History, enc: EncodingRef, mode: EditMode) -> Self {
        EditCtx {
            buf: Buffer::new(),
            history_cursor: Cursor::new(history),
            prompt: prompt,
            seq: Vec::new(),
            enc: enc,
            mode: mode,
            // always start in insert mode
            vi_mode: ViMode::Insert,
            vi_count: 0,
        }
    }

    pub fn fill(&mut self, byte: u8) {
        self.seq.push(byte)
    }

    /// Ignore one past the end of the line in vi normal mode.
    fn exclude_eol(&mut self) {
        if self.vi_mode == ViMode::Normal {
            self.buf.exclude_eol();
        }
    }

    fn set_next_vi_mode(&mut self) {
        set_next_vi_mode!(self);
    }
}

pub enum EditResult<C> {
    Cont(C),
    Halt(Result<String, Error>)
}

macro_rules! vi_repeat {
    ( $ctx:ident, $x:expr ) => {
        match $ctx.vi_count {
            0 => { $x; }
            _ => for _ in 0..$ctx.vi_count {
                if !$x {
                    break;
                }
            },
        }
        $ctx.vi_count = 0;
    };
    ( $ctx:ident, $operation:expr, $step:expr ) => {
        match $ctx.vi_count {
            0 => { $operation; }
            _ => {
                if $operation {
                    for _ in 1..$ctx.vi_count {
                        if !$step {
                            break;
                        }
                        if !$operation {
                            break;
                        }
                    }
                }
            }
        }
        $ctx.vi_count = 0;
    };
}

macro_rules! vi_delete {
    ( $ctx:ident with $dc:ident $x:expr ) => {
        vi_repeat!($ctx, $x);
        match $ctx.vi_mode {
            ViMode::Delete | ViMode::Change => {
                $dc.delete();
            }
            _ => {}
        }
        set_next_vi_mode!($ctx);
    };
}

pub fn edit<'a>(ctx: &mut EditCtx<'a>) -> EditResult<Vec<u8>> {
    use self::EditResult::*;

    let res = match parse(&ctx.seq, ctx.enc) {
        Err(ParseError::Error(len)) => {
            for _ in (0..len) {
                ctx.seq.remove(0);
            };
            Cont(false)
        },
        Err(ParseError::Incomplete) => Cont(false),
        Ok(ParseSuccess(token, len)) => {
            let res = match instr::interpret_token(token, ctx.mode, ctx.vi_mode) {
                instr::Instr::Done => {
                    Halt(Ok(ctx.buf.drain()))
                },
                instr::Instr::DoneOrEof => {
                    if ctx.buf.is_empty() {
                        Halt(Err(Error::EndOfFile))
                    }
                    else {
                        Halt(Ok(ctx.buf.drain()))
                    }
                }
                instr::Instr::DeleteCharLeftOfCursor => {
                    vi_repeat!(ctx, ctx.buf.delete_char_left_of_cursor());
                    Cont(false)
                },
                instr::Instr::DeleteCharRightOfCursor => {
                    vi_repeat!(ctx, ctx.buf.delete_char_right_of_cursor());
                    ctx.exclude_eol();
                    Cont(false)
                },
                instr::Instr::DeleteCharRightOfCursorOrEOF => {
                    if !ctx.buf.delete_char_right_of_cursor() {
                        Halt(Err(Error::EndOfFile))
                    } else {
                        Cont(false)
                    }
                },
                instr::Instr::DeleteLine => {
                    ctx.buf.drain();
                    ctx.vi_mode = ViMode::Normal;
                    ctx.vi_count = 0;
                    Cont(false)
                }
                instr::Instr::DeleteToEnd => {
                    ctx.vi_count = 0;
                    {
                        ctx.vi_mode = ViMode::Delete;
                        let mut dc = ctx.buf.start_delete();
                        vi_delete!(ctx with dc { dc.move_end(); false });
                    }
                    ctx.exclude_eol();
                    Cont(false)
                }
                instr::Instr::ChangeLine => {
                    ctx.buf.drain();
                    ctx.vi_mode = ViMode::Insert;
                    ctx.vi_count = 0;
                    Cont(false)
                }
                instr::Instr::ChangeToEnd => {
                    ctx.vi_count = 0;
                    {
                        ctx.vi_mode = ViMode::Change;
                        let mut dc = ctx.buf.start_delete();
                        vi_delete!(ctx with dc { dc.move_end(); false });
                    }
                    ctx.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveCursorLeft => {
                    let mut dc = ctx.buf.start_delete();
                    vi_delete!(ctx with dc { dc.move_left() });
                    Cont(false)
                },
                instr::Instr::MoveCursorRight => {
                    {
                        let mut dc = ctx.buf.start_delete();
                        vi_delete!(ctx with dc { dc.move_right() });
                    }
                    ctx.exclude_eol();
                    Cont(false)
                },
                instr::Instr::MoveCursorStart => {
                    ctx.vi_count = 0;
                    let mut dc = ctx.buf.start_delete();
                    dc.move_start();
                    if ctx.vi_mode == ViMode::Delete || ctx.vi_mode == ViMode::Change {
                        dc.delete();
                        set_next_vi_mode!(ctx);
                    }
                    Cont(false)
                },
                instr::Instr::MoveCursorEnd => {
                    ctx.vi_count = 0;
                    {
                        let mut dc = ctx.buf.start_delete();
                        vi_delete!(ctx with dc { dc.move_end(); false });
                    }
                    ctx.exclude_eol();
                    Cont(false)
                },
                instr::Instr::HistoryPrev => {
                    vi_repeat!(ctx, {
                        let end = ctx.history_cursor.incr();
                        if end {
                            ctx.buf.swap()
                        }
                        ctx.history_cursor.get().map(|s| ctx.buf.replace(s));
                        end
                    });
                    Cont(false)
                },
                instr::Instr::HistoryNext => {
                    vi_repeat!(ctx, {
                        let end = ctx.history_cursor.decr();
                        if end {
                            ctx.buf.swap()
                        }
                        ctx.history_cursor.get().map(|s| ctx.buf.replace(s));
                        end
                    });
                    Cont(false)
                },
                instr::Instr::NormalMode => {
                    if ctx.vi_mode == ViMode::Insert {
                        // cursor moves left when leaving insert mode
                        ctx.buf.move_left();
                    }
                    ctx.vi_mode = ViMode::Normal;
                    ctx.vi_count = 0;
                    Cont(false)
                }
                instr::Instr::ReplaceMode => {
                    ctx.vi_mode = ViMode::Replace;
                    Cont(false)
                }
                instr::Instr::MoveCharMode(mode) => {
                    ctx.vi_mode = match ctx.vi_mode {
                        ViMode::Delete => ViMode::DeleteMoveChar(mode),
                        ViMode::Change => ViMode::ChangeMoveChar(mode),
                        _              => ViMode::MoveChar(mode),
                    };
                    Cont(false)
                }
                instr::Instr::DeleteMode => {
                    ctx.vi_mode = ViMode::Delete;
                    Cont(false)
                }
                instr::Instr::ChangeMode => {
                    ctx.vi_mode = ViMode::Change;
                    Cont(false)
                }
                instr::Instr::Insert => {
                    ctx.vi_mode = ViMode::Insert;
                    Cont(false)
                }
                instr::Instr::InsertStart => {
                    ctx.vi_mode = ViMode::Insert;
                    ctx.buf.move_start();
                    Cont(false)
                }
                instr::Instr::Append => {
                    ctx.vi_mode = ViMode::Insert;
                    ctx.buf.move_right();
                    Cont(false)
                }
                instr::Instr::AppendEnd => {
                    ctx.vi_mode = ViMode::Insert;
                    ctx.buf.move_end();
                    Cont(false)
                }
                instr::Instr::Digit(i) => {
                    match (ctx.vi_count, i) {
                        // if count is 0, then 0 moves to the start of a line
                        (0, 0) => {
                            let mut dc = ctx.buf.start_delete();
                            vi_delete!(ctx with dc { dc.move_start(); false });
                        }
                        // otherwise add a digit to the count
                        (_, i) => {
                            if ctx.vi_count <= (u32::MAX - i) / 10 {
                                ctx.vi_count = ctx.vi_count * 10 + i
                            }
                        }
                    }
                    Cont(false)
                }
                instr::Instr::MoveEndOfWordRight => {
                    {
                        let mut dc = ctx.buf.start_delete();
                        vi_repeat!(ctx, dc.move_to_end_of_word());
                        if ctx.vi_mode == ViMode::Delete || ctx.vi_mode == ViMode::Change {
                            dc.move_right(); // vi deletes an extra character
                            dc.delete();
                            set_next_vi_mode!(ctx);
                        }
                    }
                    ctx.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveEndOfWordWsRight => {
                    {
                        let mut dc = ctx.buf.start_delete();
                        vi_repeat!(ctx, dc.move_to_end_of_word_ws());
                        if ctx.vi_mode == ViMode::Delete || ctx.vi_mode == ViMode::Change {
                            dc.move_right(); // vi deletes an extra character
                            dc.delete();
                            set_next_vi_mode!(ctx);
                        }
                    }
                    ctx.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveWordRight => {
                    {
                        let mut dc = ctx.buf.start_delete();
                        vi_repeat!(ctx, dc.move_word());
                        match ctx.vi_mode {
                            ViMode::Delete => dc.delete(),
                            ViMode::Change => {
                                // move word right has special behavior in change mode
                                if !dc.started_on_whitespace() && dc.move_right() {
                                    dc.move_to_end_of_word_back();
                                    dc.move_right();
                                }
                                dc.delete();
                            }
                            _ => {}
                        }
                        set_next_vi_mode!(ctx);
                    }
                    ctx.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveWordWsRight => {
                    {
                        let mut dc = ctx.buf.start_delete();
                        vi_repeat!(ctx, dc.move_word_ws());
                        match ctx.vi_mode {
                            ViMode::Delete => dc.delete(),
                            ViMode::Change => {
                                // move word right has special behavior in change mode
                                if !dc.started_on_whitespace() && dc.move_right() {
                                    dc.move_to_end_of_word_ws_back();
                                    dc.move_right();
                                }
                                dc.delete();
                            }
                            _ => {}
                        }
                        set_next_vi_mode!(ctx);
                    }
                    ctx.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveWordLeft => {
                    let mut dc = ctx.buf.start_delete();
                    vi_delete!(ctx with dc { dc.move_word_back() });
                    Cont(false)
                }
                instr::Instr::MoveWordWsLeft => {
                    let mut dc = ctx.buf.start_delete();
                    vi_delete!(ctx with dc { dc.move_word_ws_back() });
                    Cont(false)
                }
                instr::Instr::MoveCharRight(c) => {
                    {
                        let mut dc = ctx.buf.start_delete();
                        dc.move_to_char_right(c, match ctx.vi_count {
                            0 => 1,
                            n => n,
                        });
                        match ctx.vi_mode {
                            ViMode::DeleteMoveChar(_) | ViMode::ChangeMoveChar(_) => {
                                dc.move_right(); // make deletion inclusive
                                dc.delete();
                            }
                            _ => {},
                        }
                    }
                    ctx.vi_count = 0;
                    ctx.set_next_vi_mode();
                    ctx.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveCharLeft(c) => {
                    let mut dc = ctx.buf.start_delete();
                    dc.move_to_char_left(c, match ctx.vi_count {
                        0 => 1,
                        n => n,
                    });
                    match ctx.vi_mode {
                        ViMode::DeleteMoveChar(_) | ViMode::ChangeMoveChar(_) => dc.delete(),
                        _ => {},
                    }
                    ctx.vi_count = 0;
                    set_next_vi_mode!(ctx);
                    Cont(false)
                }
                instr::Instr::MoveBeforeCharRight(c) => {
                    let count = match ctx.vi_count {
                        0 => 1,
                        n => n,
                    };

                    let mut dc = ctx.buf.start_delete();
                    if dc.move_to_char_right(c, count) {
                        dc.move_left();
                        match ctx.vi_mode {
                            ViMode::DeleteMoveChar(_) | ViMode::ChangeMoveChar(_) => {
                                dc.move_right(); // make deletion inclusive
                                dc.delete();
                            }
                            _ => {},
                        }
                    }
                    ctx.vi_count = 0;
                    set_next_vi_mode!(ctx);
                    Cont(false)
                }
                instr::Instr::MoveBeforeCharLeft(c) => {
                    let count = match ctx.vi_count {
                        0 => 1,
                        n => n,
                    };

                    let mut dc = ctx.buf.start_delete();
                    if dc.move_to_char_left(c, count) {
                        dc.move_right();
                        match ctx.vi_mode {
                            ViMode::DeleteMoveChar(_) | ViMode::ChangeMoveChar(_) => dc.delete(),
                            _ => {},
                        }
                    }
                    ctx.vi_count = 0;
                    set_next_vi_mode!(ctx);
                    Cont(false)
                }
                instr::Instr::Substitute => {
                    vi_repeat!(ctx, ctx.buf.delete_char_right_of_cursor());
                    ctx.vi_mode = ViMode::Insert;
                    Cont(false)
                }
                instr::Instr::Noop => {
                    Cont(false)
                },
                instr::Instr::Cancel => Halt(Err(Error::Cancel)),
                instr::Instr::Clear => {
                    Cont(true)
                },
                instr::Instr::InsertAtCursor(text) => {
                    ctx.buf.insert_chars_at_cursor(text);
                    Cont(false)
                }
                instr::Instr::ReplaceAtCursor(text) => {
                    vi_repeat!(
                        ctx,
                        {
                            ctx.buf.replace_chars_at_cursor(text.clone());
                            true
                        },
                        {
                            ctx.buf.move_right();
                            ctx.buf.exclude_eol()
                        }
                    );
                    ctx.vi_mode = ViMode::Normal;
                    Cont(false)
                }
            };
            for _ in (0..len) {
                ctx.seq.remove(0);
            };
            res
        }
    };
    match res {
        Cont(clear) => EditResult::Cont(ctx.buf.get_line(ctx.prompt, clear)),
        Halt(res) => EditResult::Halt(res)
    }
}
<commit_msg>convert set_next_vi_mode() to a function<commit_after>use encoding::types::EncodingRef;

use std::u32;
use error::Error;
use history::{Cursor, History};
use buffer::Buffer;
use parser::{parse, ParseError, ParseSuccess};
use instr;

#[derive(Copy,Clone)]
pub enum EditMode {
    Emacs,
    Vi,
}

#[derive(Copy, Clone, PartialEq)]
pub enum ViMode {
    Insert,
    Normal,
    Replace,
    MoveChar(instr::CharMoveType),
    DeleteMoveChar(instr::CharMoveType),
    ChangeMoveChar(instr::CharMoveType),
    Delete,
    Change,
}

/// Set a new vi mode based on the current vi mode.
///
/// There are several common mode transitions that we handle here.
fn next_vi_mode(mode: ViMode) -> ViMode {
    match mode {
        ViMode::Delete => ViMode::Normal,
        ViMode::Change => ViMode::Insert,
        ViMode::DeleteMoveChar(_) => ViMode::Normal,
        ViMode::ChangeMoveChar(_) => ViMode::Insert,
        _ => ViMode::Normal,
    }
}

pub struct EditCtx<'a> {
    buf: Buffer,
    history_cursor: Cursor<'a>,
    prompt: &'a str,
    seq: Vec<u8>,
    enc: EncodingRef,
    mode: EditMode,
    vi_mode: ViMode,
    vi_count: u32,
}

impl<'a> EditCtx<'a> {

    pub fn new(prompt: &'a str, history: &'a History, enc: EncodingRef, mode: EditMode) -> Self {
        EditCtx {
            buf: Buffer::new(),
            history_cursor: Cursor::new(history),
            prompt: prompt,
            seq: Vec::new(),
            enc: enc,
            mode: mode,
            // always start in insert mode
            vi_mode: ViMode::Insert,
            vi_count: 0,
        }
    }

    pub fn fill(&mut self, byte: u8) {
        self.seq.push(byte)
    }

    /// Ignore one past the end of the line in vi normal mode.
    fn exclude_eol(&mut self) {
        if self.vi_mode == ViMode::Normal {
            self.buf.exclude_eol();
        }
    }
}

pub enum EditResult<C> {
    Cont(C),
    Halt(Result<String, Error>)
}

macro_rules! vi_repeat {
    ( $ctx:ident, $x:expr ) => {
        match $ctx.vi_count {
            0 => { $x; }
            _ => for _ in 0..$ctx.vi_count {
                if !$x {
                    break;
                }
            },
        }
        $ctx.vi_count = 0;
    };
    ( $ctx:ident, $operation:expr, $step:expr ) => {
        match $ctx.vi_count {
            0 => { $operation; }
            _ => {
                if $operation {
                    for _ in 1..$ctx.vi_count {
                        if !$step {
                            break;
                        }
                        if !$operation {
                            break;
                        }
                    }
                }
            }
        }
        $ctx.vi_count = 0;
    };
}

macro_rules! vi_delete {
    ( $ctx:ident with $dc:ident $x:expr ) => {
        vi_repeat!($ctx, $x);
        match $ctx.vi_mode {
            ViMode::Delete | ViMode::Change => {
                $dc.delete();
            }
            _ => {}
        }
        $ctx.vi_mode = next_vi_mode($ctx.vi_mode);
    };
}

pub fn edit<'a>(ctx: &mut EditCtx<'a>) -> EditResult<Vec<u8>> {
    use self::EditResult::*;

    let res = match parse(&ctx.seq, ctx.enc) {
        Err(ParseError::Error(len)) => {
            for _ in (0..len) {
                ctx.seq.remove(0);
            };
            Cont(false)
        },
        Err(ParseError::Incomplete) => Cont(false),
        Ok(ParseSuccess(token, len)) => {
            let res = match instr::interpret_token(token, ctx.mode, ctx.vi_mode) {
                instr::Instr::Done => {
                    Halt(Ok(ctx.buf.drain()))
                },
                instr::Instr::DoneOrEof => {
                    if ctx.buf.is_empty() {
                        Halt(Err(Error::EndOfFile))
                    }
                    else {
                        Halt(Ok(ctx.buf.drain()))
                    }
                }
                instr::Instr::DeleteCharLeftOfCursor => {
                    vi_repeat!(ctx, ctx.buf.delete_char_left_of_cursor());
                    Cont(false)
                },
                instr::Instr::DeleteCharRightOfCursor => {
                    vi_repeat!(ctx, ctx.buf.delete_char_right_of_cursor());
                    ctx.exclude_eol();
                    Cont(false)
                },
                instr::Instr::DeleteCharRightOfCursorOrEOF => {
                    if !ctx.buf.delete_char_right_of_cursor() {
                        Halt(Err(Error::EndOfFile))
                    } else {
                        Cont(false)
                    }
                },
                instr::Instr::DeleteLine => {
                    ctx.buf.drain();
                    ctx.vi_mode = ViMode::Normal;
                    ctx.vi_count = 0;
                    Cont(false)
                }
                instr::Instr::DeleteToEnd => {
                    ctx.vi_count = 0;
                    {
                        ctx.vi_mode = ViMode::Delete;
                        let mut dc = ctx.buf.start_delete();
                        vi_delete!(ctx with dc { dc.move_end(); false });
                    }
                    ctx.exclude_eol();
                    Cont(false)
                }
                instr::Instr::ChangeLine => {
                    ctx.buf.drain();
                    ctx.vi_mode = ViMode::Insert;
                    ctx.vi_count = 0;
                    Cont(false)
                }
                instr::Instr::ChangeToEnd => {
                    ctx.vi_count = 0;
                    {
                        ctx.vi_mode = ViMode::Change;
                        let mut dc = ctx.buf.start_delete();
                        vi_delete!(ctx with dc { dc.move_end(); false });
                    }
                    ctx.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveCursorLeft => {
                    let mut dc = ctx.buf.start_delete();
                    vi_delete!(ctx with dc { dc.move_left() });
                    Cont(false)
                },
                instr::Instr::MoveCursorRight => {
                    {
                        let mut dc = ctx.buf.start_delete();
                        vi_delete!(ctx with dc { dc.move_right() });
                    }
                    ctx.exclude_eol();
                    Cont(false)
                },
                instr::Instr::MoveCursorStart => {
                    ctx.vi_count = 0;
                    let mut dc = ctx.buf.start_delete();
                    dc.move_start();
                    if ctx.vi_mode == ViMode::Delete || ctx.vi_mode == ViMode::Change {
                        dc.delete();
                        ctx.vi_mode = next_vi_mode(ctx.vi_mode);
                    }
                    Cont(false)
                },
                instr::Instr::MoveCursorEnd => {
                    ctx.vi_count = 0;
                    {
                        let mut dc = ctx.buf.start_delete();
                        vi_delete!(ctx with dc { dc.move_end(); false });
                    }
                    ctx.exclude_eol();
                    Cont(false)
                },
                instr::Instr::HistoryPrev => {
                    vi_repeat!(ctx, {
                        let end = ctx.history_cursor.incr();
                        if end {
                            ctx.buf.swap()
                        }
                        ctx.history_cursor.get().map(|s| ctx.buf.replace(s));
                        end
                    });
                    Cont(false)
                },
                instr::Instr::HistoryNext => {
                    vi_repeat!(ctx, {
                        let end = ctx.history_cursor.decr();
                        if end {
                            ctx.buf.swap()
                        }
                        ctx.history_cursor.get().map(|s| ctx.buf.replace(s));
                        end
                    });
                    Cont(false)
                },
                instr::Instr::NormalMode => {
                    if ctx.vi_mode == ViMode::Insert {
                        // cursor moves left when leaving insert mode
                        ctx.buf.move_left();
                    }
                    ctx.vi_mode = ViMode::Normal;
                    ctx.vi_count = 0;
                    Cont(false)
                }
                instr::Instr::ReplaceMode => {
                    ctx.vi_mode = ViMode::Replace;
                    Cont(false)
                }
                instr::Instr::MoveCharMode(mode) => {
                    ctx.vi_mode = match ctx.vi_mode {
                        ViMode::Delete => ViMode::DeleteMoveChar(mode),
                        ViMode::Change => ViMode::ChangeMoveChar(mode),
                        _              => ViMode::MoveChar(mode),
                    };
                    Cont(false)
                }
                instr::Instr::DeleteMode => {
                    ctx.vi_mode = ViMode::Delete;
                    Cont(false)
                }
                instr::Instr::ChangeMode => {
                    ctx.vi_mode = ViMode::Change;
                    Cont(false)
                }
                instr::Instr::Insert => {
                    ctx.vi_mode = ViMode::Insert;
                    Cont(false)
                }
                instr::Instr::InsertStart => {
                    ctx.vi_mode = ViMode::Insert;
                    ctx.buf.move_start();
                    Cont(false)
                }
                instr::Instr::Append => {
                    ctx.vi_mode = ViMode::Insert;
                    ctx.buf.move_right();
                    Cont(false)
                }
                instr::Instr::AppendEnd => {
                    ctx.vi_mode = ViMode::Insert;
                    ctx.buf.move_end();
                    Cont(false)
                }
                instr::Instr::Digit(i) => {
                    match (ctx.vi_count, i) {
                        // if count is 0, then 0 moves to the start of a line
                        (0, 0) => {
                            let mut dc = ctx.buf.start_delete();
                            vi_delete!(ctx with dc { dc.move_start(); false });
                        }
                        // otherwise add a digit to the count
                        (_, i) => {
                            if ctx.vi_count <= (u32::MAX - i) / 10 {
                                ctx.vi_count = ctx.vi_count * 10 + i
                            }
                        }
                    }
                    Cont(false)
                }
                instr::Instr::MoveEndOfWordRight => {
                    {
                        let mut dc = ctx.buf.start_delete();
                        vi_repeat!(ctx, dc.move_to_end_of_word());
                        if ctx.vi_mode == ViMode::Delete || ctx.vi_mode == ViMode::Change {
                            dc.move_right(); // vi deletes an extra character
                            dc.delete();
                            ctx.vi_mode = next_vi_mode(ctx.vi_mode);
                        }
                    }
                    ctx.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveEndOfWordWsRight => {
                    {
                        let mut dc = ctx.buf.start_delete();
                        vi_repeat!(ctx, dc.move_to_end_of_word_ws());
                        if ctx.vi_mode == ViMode::Delete || ctx.vi_mode == ViMode::Change {
                            dc.move_right(); // vi deletes an extra character
                            dc.delete();
                            ctx.vi_mode = next_vi_mode(ctx.vi_mode);
                        }
                    }
                    ctx.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveWordRight => {
                    {
                        let mut dc = ctx.buf.start_delete();
                        vi_repeat!(ctx, dc.move_word());
                        match ctx.vi_mode {
                            ViMode::Delete => dc.delete(),
                            ViMode::Change => {
                                // move word right has special behavior in change mode
                                if !dc.started_on_whitespace() && dc.move_right() {
                                    dc.move_to_end_of_word_back();
                                    dc.move_right();
                                }
                                dc.delete();
                            }
                            _ => {}
                        }
                        ctx.vi_mode = next_vi_mode(ctx.vi_mode);
                    }
                    ctx.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveWordWsRight => {
                    {
                        let mut dc = ctx.buf.start_delete();
                        vi_repeat!(ctx, dc.move_word_ws());
                        match ctx.vi_mode {
                            ViMode::Delete => dc.delete(),
                            ViMode::Change => {
                                // move word right has special behavior in change mode
                                if !dc.started_on_whitespace() && dc.move_right() {
                                    dc.move_to_end_of_word_ws_back();
                                    dc.move_right();
                                }
                                dc.delete();
                            }
                            _ => {}
                        }
                        ctx.vi_mode = next_vi_mode(ctx.vi_mode);
                    }
                    ctx.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveWordLeft => {
                    let mut dc = ctx.buf.start_delete();
                    vi_delete!(ctx with dc { dc.move_word_back() });
                    Cont(false)
                }
                instr::Instr::MoveWordWsLeft => {
                    let mut dc = ctx.buf.start_delete();
                    vi_delete!(ctx with dc { dc.move_word_ws_back() });
                    Cont(false)
                }
                instr::Instr::MoveCharRight(c) => {
                    {
                        let mut dc = ctx.buf.start_delete();
                        dc.move_to_char_right(c, match ctx.vi_count {
                            0 => 1,
                            n => n,
                        });
                        match ctx.vi_mode {
                            ViMode::DeleteMoveChar(_) | ViMode::ChangeMoveChar(_) => {
                                dc.move_right(); // make deletion inclusive
                                dc.delete();
                            }
                            _ => {},
                        }
                    }
                    ctx.vi_count = 0;
                    ctx.vi_mode = next_vi_mode(ctx.vi_mode);
                    ctx.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveCharLeft(c) => {
                    let mut dc = ctx.buf.start_delete();
                    dc.move_to_char_left(c, match ctx.vi_count {
                        0 => 1,
                        n => n,
                    });
                    match ctx.vi_mode {
                        ViMode::DeleteMoveChar(_) | ViMode::ChangeMoveChar(_) => dc.delete(),
                        _ => {},
                    }
                    ctx.vi_count = 0;
                    ctx.vi_mode = next_vi_mode(ctx.vi_mode);
                    Cont(false)
                }
                instr::Instr::MoveBeforeCharRight(c) => {
                    let count = match ctx.vi_count {
                        0 => 1,
                        n => n,
                    };

                    let mut dc = ctx.buf.start_delete();
                    if dc.move_to_char_right(c, count) {
                        dc.move_left();
                        match ctx.vi_mode {
                            ViMode::DeleteMoveChar(_) | ViMode::ChangeMoveChar(_) => {
                                dc.move_right(); // make deletion inclusive
                                dc.delete();
                            }
                            _ => {},
                        }
                    }
                    ctx.vi_count = 0;
                    ctx.vi_mode = next_vi_mode(ctx.vi_mode);
                    Cont(false)
                }
                instr::Instr::MoveBeforeCharLeft(c) => {
                    let count = match ctx.vi_count {
                        0 => 1,
                        n => n,
                    };

                    let mut dc = ctx.buf.start_delete();
                    if dc.move_to_char_left(c, count) {
                        dc.move_right();
                        match ctx.vi_mode {
                            ViMode::DeleteMoveChar(_) | ViMode::ChangeMoveChar(_) => dc.delete(),
                            _ => {},
                        }
                    }
                    ctx.vi_count = 0;
                    ctx.vi_mode = next_vi_mode(ctx.vi_mode);
                    Cont(false)
                }
                instr::Instr::Substitute => {
                    vi_repeat!(ctx, ctx.buf.delete_char_right_of_cursor());
                    ctx.vi_mode = ViMode::Insert;
                    Cont(false)
                }
                instr::Instr::Noop => {
                    Cont(false)
                },
                instr::Instr::Cancel => Halt(Err(Error::Cancel)),
                instr::Instr::Clear => {
                    Cont(true)
                },
                instr::Instr::InsertAtCursor(text) => {
                    ctx.buf.insert_chars_at_cursor(text);
                    Cont(false)
                }
                instr::Instr::ReplaceAtCursor(text) => {
                    vi_repeat!(
                        ctx,
                        {
                            ctx.buf.replace_chars_at_cursor(text.clone());
                            true
                        },
                        {
                            ctx.buf.move_right();
                            ctx.buf.exclude_eol()
                        }
                    );
                    ctx.vi_mode = ViMode::Normal;
                    Cont(false)
                }
            };
            for _ in (0..len) {
                ctx.seq.remove(0);
            };
            res
        }
    };
    match res {
        Cont(clear) => EditResult::Cont(ctx.buf.get_line(ctx.prompt, clear)),
        Halt(res) => EditResult::Halt(res)
    }
}
<|endoftext|>


<commit_before>use crate::colour::Colour;
use crate::graph::{Graph, Position};
use crate::grid::Grid;

pub enum GameState {
    Solving,
    Solved,
}

pub struct Game {
    pub state: GameState,
    pub graph: Graph,
    number_of_clicks: u32,
    allowed_clicks: u32,
}

const TOP_LEFT_CELL: Position = Position{column: 0_usize, row: 0_usize};

impl Game {
    pub fn create(size: u32, number_of_colours: u32) -> Self {
        let grid = Grid::generate(size as usize, number_of_colours);
        let graph = Graph::create(&grid);

        let allowed_clicks = 25 * 2 * size * number_of_colours / (2 * 14 * 6);

        Self {
            state: GameState::Solving,
            graph,
            number_of_clicks: 0,
            allowed_clicks,
        }
    }

    pub fn fill_component_of_top_left_cell_with(&mut self, colour: Colour) {
        self.number_of_clicks += 1;
        self.graph.change_colour_of_component_at(&TOP_LEFT_CELL, colour);
    }
}
<commit_msg>Make number of clicks and allowed clicks public<commit_after>use crate::colour::Colour;
use crate::graph::{Graph, Position};
use crate::grid::Grid;

pub enum GameState {
    Solving,
    Solved,
}

pub struct Game {
    pub state: GameState,
    pub graph: Graph,
    pub number_of_clicks: u32,
    pub allowed_clicks: u32,
}

const TOP_LEFT_CELL: Position = Position{column: 0_usize, row: 0_usize};

impl Game {
    pub fn create(size: u32, number_of_colours: u32) -> Self {
        let grid = Grid::generate(size as usize, number_of_colours);
        let graph = Graph::create(&grid);

        let allowed_clicks = 25 * 2 * size * number_of_colours / (2 * 14 * 6);

        Self {
            state: GameState::Solving,
            graph,
            number_of_clicks: 0,
            allowed_clicks,
        }
    }

    pub fn fill_component_of_top_left_cell_with(&mut self, colour: Colour) {
        self.number_of_clicks += 1;
        self.graph.change_colour_of_component_at(&TOP_LEFT_CELL, colour);
    }
}
<|endoftext|>


<commit_before>use ::korome::*;
use ::Vector2;
use ::obj::*;

use std::ops::Deref;

pub struct SpaceShooter<'a>{
    objs: ObjSystem<'a>,
    player: &'a Texture,
    sun   : &'a Texture,
    planet: &'a Texture,
    arrow : &'a Texture,
    laser : &'a Texture,
    oobb  : OutOfBoundsBehaviour,
    dbg_a : bool,
    deltas: Option<Vec<f32>>,
}

use std::ops::Drop;

impl<'a> Drop for SpaceShooter<'a>{
    fn drop(&mut self){
        if let Some(ref mut deltas) = self.deltas{
            use std::fs::File;
            use std::io::{Write, BufWriter};

            let file = File::create("deltas.log").unwrap();
            let mut writer = BufWriter::new(file);

            let (len, mut min, mut max, mut sum) = (deltas.len(), ::std::f32::MAX, 0f32, 0.);

            // Skip the first two 'cause they're normally too high.
            for n in deltas.drain(..).skip(2){
                min = min.min(n);
                max = max.max(n);
                sum += n;

                writer.write_fmt(format_args!("{}s ~ {}fps\n", n, 1./n)).unwrap()
            }

            let avg = sum/len as f32;

            println!("\nTotal  : {}s", sum);
            println!("Best   : {}s ~ {}fps", min, 1./min);
            println!("Worst  : {}s ~ {}fps", max, 1./max);
            println!("Average: {}s ~ {}fps", avg, 1./avg);
        }
    }
}

impl<'a> SpaceShooter<'a>{
    pub fn new(deltas: bool, planet: &'a Texture, player: &'a Texture, sun: &'a Texture, arrow: &'a Texture, laser: &'a Texture) -> Self{
        let mut objs = ObjSystem::new();
        objs.players.push(new_player(player));

        SpaceShooter{
            player: player,
            sun   : sun,
            objs  : objs,
            planet: planet,
            arrow : arrow,
            laser : laser,
            oobb  : Bounce,
            dbg_a : true,
            deltas: if deltas{Some(Vec::with_capacity(10_000))}else{None},
        }
    }

    fn add_player(&mut self){
        self.objs.players.push(new_player(self.player))
    }

    fn add_planet(&mut self, pos: Vector2<f32>){
        self.objs.bodies.push(Object::new(self.planet, pos, 50, 64., 1e13));
    }

    fn add_sun(&mut self, pos: Vector2<f32>){
        self.objs.bodies.push(Object::new(self.sun, pos, 100, 64., 2e14));
    }

    fn add_laser(&mut self, pos: Vector2<f32>, direction: f32){
        self.objs.projectiles.push(new_laser(self.laser, pos, direction));
    }
}

macro_rules! when_released{
    ($info:expr; $($key:ident => $b:block),+) => {
        for ke in $info.get_key_events(){
            match *ke{
                $((false, VirtualKeyCode::$key) => $b,)+
                _ => ()
            }
        }
    };
    ($info:expr;MOUSE; $($key:ident => $b:block),+) => {
        for ke in $info.get_mouse_events(){
            match *ke{
                $((false, MouseButton::$key) => $b,)+
                _ => ()
            }
        }
    };
}

impl<'a> Game for SpaceShooter<'a>{
    fn frame(&mut self, info: &FrameInfo, drawer: &mut Drawer) -> GameUpdate{
        self.deltas.as_mut().map(|d| d.push(info.delta));

        when_released!{info;
            Escape => {
                return GameUpdate::Close
            },
            M => {
                self.oobb = match self.oobb{
                    Bounce => Stop,
                    Stop => Wrap,
                    Wrap => Bounce
                };
                println!("OOBB changed to: {:?}", self.oobb);
            },
            N => {
                self.dbg_a = !self.dbg_a;
                println!("Turned debug arrows {}", if self.dbg_a{"on"}else{"off"});
            },
            C => {
                self.objs.bodies.clear();
                println!("Bodies cleared, new count: {}", self.objs.len());
            },
            K => {
                self.objs.projectiles.clear();
                println!("Projectiles cleared, new count: {}", self.objs.len());
            },
            O => {
                self.objs.players.clear();
                println!("Players cleared, new count: {}", self.objs.len());
            },
            P => {
                self.add_player();
                println!("Player added, new count: {}", self.objs.len());
            },
            Q => {
                let ObjSystem{players: ref a, bodies: ref b, projectiles: ref c} = self.objs;

                println!("{}::{}:{}:{}", self.deltas.as_ref().map(Vec::capacity).unwrap_or_default(), a.capacity(), b.capacity(), c.capacity());
            },
            Z => {
                let ObjSystem{players: ref mut a, bodies: ref mut b, projectiles: ref mut c} = self.objs;
                print!("{};{};{}->", a.capacity(), b.capacity(), c.capacity());
                a.shrink_to_fit();
                b.shrink_to_fit();
                c.shrink_to_fit();
                println!("{};{};{}", a.capacity(), b.capacity(), c.capacity());
            },
            X => {
                for x in self.objs.players.iter().chain(self.objs.bodies.iter()).chain(self.objs.projectiles.iter()){
                    println!("{:?}", &**x);
                }
            },
            Space => {
                let iter: Vec<_> = self.objs.players.iter().map(|x| {
                    let &InnerObject{pos, rot,..} = x.deref();
                    (pos, rot)
                }).collect();
                for &(pos, rot) in &iter{
                    self.add_laser(pos, rot);
                }
                println!("Laser{} added, new count: {}", if self.objs.players.len() == 1{""}else{"s"}, self.objs.len());
            }
        }

        when_released!{info;MOUSE;
            Left => {
                self.add_planet(info.mousepos.into());
                println!("Object added, new count: {}", self.objs.len());
            },
            Right => {
                self.add_sun(info.mousepos.into());
                println!("Object added, new count: {}", self.objs.len());
            }
        }

        drawer.clear(0., 0., 0.);
        self.objs.update(info, drawer, if self.dbg_a{Some(self.arrow)}else{None}, self.oobb);

        GameUpdate::Nothing
    }
}

#[derive(Default)]
pub struct ObjSystem<'a>{
    bodies     : Vec<Object<'a>>,
    players    : Vec<Object<'a>>,
    projectiles: Vec<Object<'a>>,
}

const G: f32 = 6.671281903963040991511534289e-11;
#[allow(dead_code)]
const G_OLD: f32 = 6.67384E-11;

use OutOfBoundsBehaviour::*;
use OutOfBoundsBehaviour;

fn gravity(x: &InnerObject, y: &InnerObject) -> Vector2<f32>{
    let r = x.pos.distance_to(y.pos);
    let g_force = (G * x.mass * y.mass) / (r * r);
    let dir_towards_body = x.pos.direction_to(y.pos);

    let force = Vector2::unit_vector(dir_towards_body) * g_force;
    if !force.is_any_nan(){
        force
    }else{
        Vector2(0., 0.)
    }
}

fn inners(x: &Vec<Object>) -> Vec<InnerObject>{
    x.iter().map(|x| **x).collect()
}

impl<'a> ObjSystem<'a>{
    #[inline]
    pub fn new() -> Self{
        Default::default()
    }

    pub fn update(&mut self, info: &FrameInfo, drawer: &mut Drawer, arrow: Option<&Texture>, oobb: OutOfBoundsBehaviour){
        let bs = inners(&self.bodies);
        let wh = drawer.graphics.get_h_size();
        let delta = info.delta as f32;

        let (mut acceleration, mut rot) = (0., 0.);
        is_down!{info;
            D, Right => {
                rot -= 2. * delta
            },
            A, Left => {
                rot += 2. * delta
            },
            S, Down => {
                acceleration -= 52. * delta
            },
            W, Up => {
                acceleration += 52. * delta;
            },
            LShift => {
                acceleration *= 7.;
            }
        }

        let mut indices_to_die = [Vec::new(), Vec::new(), Vec::new()];

        for (i, player) in self.players.iter_mut().enumerate(){
            let net_grav = bs.iter().fold(Vector2(0., 0.), |n_g, y| n_g + gravity(&player, y));

            let propulsion_force = Vector2::<f32>::unit_vector(player.rot) * acceleration;

            player.rot += rot;
            player.vel += propulsion_force;

            player.update(info, net_grav, wh, oobb);
            player.draw(drawer, arrow, net_grav, net_grav+propulsion_force);

            if player.health == 0{
                indices_to_die[0].push(i);
            }
        }
        for (i, body) in self.bodies.iter_mut().enumerate(){
            let net_grav = bs.iter().enumerate().filter_map(|(j, y)| if i==j{None}else{Some(y)}).fold(Vector2(0., 0.), |n_g, y| n_g + gravity(&body, y));

            body.update(info, net_grav, wh, oobb);
            body.draw(drawer, arrow, net_grav, net_grav);

            if body.health == 0{
                indices_to_die[1].push(i);
            }
        }
        for (i, projectile) in self.projectiles.iter_mut().enumerate(){
            let net_grav = bs.iter().fold(Vector2(0., 0.), |n_g, y| n_g + gravity(&projectile, y));

            projectile.rot = projectile.vel.direction();

            projectile.update(info, net_grav, wh, oobb);
            projectile.draw(drawer, arrow, net_grav, net_grav);

            for player in self.players.iter_mut(){
                if projectile.pos.distance_to(player.pos) - player.diameter/2. <= 16.{
                    player.health = player.health.saturating_sub(1);
                    indices_to_die[2].push(i);
                }
            }
            for body in self.bodies.iter_mut(){
                if projectile.pos.distance_to(body.pos) - body.diameter/2. <= 16.{
                    body.health = body.health.saturating_sub(1);
                    indices_to_die[2].push(i);
                }
            }
        }
        for v in &mut indices_to_die{
            v.sort();
            v.dedup();
        }
        for &i in indices_to_die[0].iter().rev(){
            self.players.remove(i);
        }
        for &i in indices_to_die[1].iter().rev(){
            self.bodies.remove(i);
        }
        for &i in indices_to_die[2].iter().rev(){
            self.projectiles.remove(i);
        }
    }

    pub fn len(&self) -> usize{
        self.players.len() + self.bodies.len() + self.projectiles.len()
    }
}
<commit_msg>Small clean-up<commit_after>use ::korome::*;
use ::Vector2;
use ::obj::*;

use std::ops::Deref;

pub struct SpaceShooter<'a>{
    objs: ObjSystem<'a>,
    player: &'a Texture,
    sun   : &'a Texture,
    planet: &'a Texture,
    arrow : &'a Texture,
    laser : &'a Texture,
    oobb  : OutOfBoundsBehaviour,
    dbg_a : bool,
    deltas: Option<Vec<f32>>,
}

use std::ops::Drop;

impl<'a> Drop for SpaceShooter<'a>{
    fn drop(&mut self){
        if let Some(ref mut deltas) = self.deltas{
            use std::fs::File;
            use std::io::{Write, BufWriter};

            let file = File::create("deltas.log").unwrap();
            let mut writer = BufWriter::new(file);

            let (len, mut min, mut max, mut sum) = (deltas.len() as f32 - 2., ::std::f32::MAX, 0f32, 0.);

            // Skip the first two 'cause they're normally too high.
            for n in deltas.drain(..).skip(2){
                min = min.min(n);
                max = max.max(n);
                sum += n;

                write!(writer, "{}s ~ {}fps\n", n, n.recip()).unwrap();
            }

            let avg = sum/len as f32;

            println!("\nTotal  : {}s", sum);
            println!("Best   : {}s ~ {}fps", min, min.recip());
            println!("Worst  : {}s ~ {}fps", max, max.recip());
            println!("Average: {}s ~ {}fps", avg, avg.recip());
        }
    }
}

impl<'a> SpaceShooter<'a>{
    pub fn new(deltas: bool, planet: &'a Texture, player: &'a Texture, sun: &'a Texture, arrow: &'a Texture, laser: &'a Texture) -> Self{
        let mut objs = ObjSystem::new();
        objs.players.push(new_player(player));

        SpaceShooter{
            player: player,
            sun   : sun,
            objs  : objs,
            planet: planet,
            arrow : arrow,
            laser : laser,
            oobb  : Bounce,
            dbg_a : true,
            deltas: if deltas{Some(Vec::with_capacity(10_000))}else{None},
        }
    }

    fn add_player(&mut self){
        self.objs.players.push(new_player(self.player))
    }

    fn add_planet(&mut self, pos: Vector2<f32>){
        self.objs.bodies.push(Object::new(self.planet, pos, 50, 64., 1e13));
    }

    fn add_sun(&mut self, pos: Vector2<f32>){
        self.objs.bodies.push(Object::new(self.sun, pos, 100, 64., 2e14));
    }

    fn add_laser(&mut self, pos: Vector2<f32>, direction: f32){
        self.objs.projectiles.push(new_laser(self.laser, pos, direction));
    }
}

macro_rules! when_released{
    ($info:expr; $($key:ident => $b:block),+) => {
        for ke in $info.get_key_events(){
            match *ke{
                $((false, VirtualKeyCode::$key) => $b,)+
                _ => ()
            }
        }
    };
    ($info:expr;MOUSE; $($key:ident => $b:block),+) => {
        for ke in $info.get_mouse_events(){
            match *ke{
                $((false, MouseButton::$key) => $b,)+
                _ => ()
            }
        }
    };
}

impl<'a> Game for SpaceShooter<'a>{
    fn frame(&mut self, info: &FrameInfo, drawer: &mut Drawer) -> GameUpdate{
        self.deltas.as_mut().map(|d| d.push(info.delta));

        when_released!{info;
            Escape => {
                return GameUpdate::Close
            },
            M => {
                self.oobb = match self.oobb{
                    Bounce => Stop,
                    Stop => Wrap,
                    Wrap => Bounce
                };
                println!("OOBB changed to: {:?}", self.oobb);
            },
            N => {
                self.dbg_a = !self.dbg_a;
                println!("Turned debug arrows {}", if self.dbg_a{"on"}else{"off"});
            },
            C => {
                self.objs.bodies.clear();
                println!("Bodies cleared, new count: {}", self.objs.len());
            },
            K => {
                self.objs.projectiles.clear();
                println!("Projectiles cleared, new count: {}", self.objs.len());
            },
            O => {
                self.objs.players.clear();
                println!("Players cleared, new count: {}", self.objs.len());
            },
            P => {
                self.add_player();
                println!("Player added, new count: {}", self.objs.len());
            },
            Q => {
                let ObjSystem{players: ref a, bodies: ref b, projectiles: ref c} = self.objs;

                println!("{}::{}:{}:{}", if let Some(ref d) = self.deltas {d.capacity()}else{0}, a.capacity(), b.capacity(), c.capacity());
            },
            Z => {
                let ObjSystem{players: ref mut a, bodies: ref mut b, projectiles: ref mut c} = self.objs;
                print!("{};{};{}->", a.capacity(), b.capacity(), c.capacity());
                a.shrink_to_fit();
                b.shrink_to_fit();
                c.shrink_to_fit();
                println!("{};{};{}", a.capacity(), b.capacity(), c.capacity());
            },
            X => {
                for x in self.objs.players.iter().chain(self.objs.bodies.iter()).chain(self.objs.projectiles.iter()){
                    println!("{:?}", &**x);
                }
            },
            Space => {
                let iter: Vec<_> = self.objs.players.iter().map(|x| {
                    let &InnerObject{pos, rot,..} = x.deref();
                    (pos, rot)
                }).collect();
                for &(pos, rot) in &iter{
                    self.add_laser(pos, rot);
                }
                println!("Laser{} added, new count: {}", if self.objs.players.len() == 1{""}else{"s"}, self.objs.len());
            }
        }

        when_released!{info;MOUSE;
            Left => {
                self.add_planet(info.mousepos.into());
                println!("Object added, new count: {}", self.objs.len());
            },
            Right => {
                self.add_sun(info.mousepos.into());
                println!("Object added, new count: {}", self.objs.len());
            }
        }

        drawer.clear(0., 0., 0.);
        self.objs.update(info, drawer, if self.dbg_a{Some(self.arrow)}else{None}, self.oobb);

        GameUpdate::Nothing
    }
}

#[derive(Default)]
pub struct ObjSystem<'a>{
    bodies     : Vec<Object<'a>>,
    players    : Vec<Object<'a>>,
    projectiles: Vec<Object<'a>>,
}

const G: f32 = 6.671281903963040991511534289e-11;

use OutOfBoundsBehaviour::*;
use OutOfBoundsBehaviour;

fn gravity(x: &InnerObject, y: &InnerObject) -> Vector2<f32>{
    let r = x.pos.distance_to(y.pos);
    let g_force = (G * x.mass * y.mass) / (r * r);
    let dir_towards_body = x.pos.direction_to(y.pos);

    let force = Vector2::unit_vector(dir_towards_body) * g_force;
    if !force.is_any_nan(){
        force
    }else{
        Vector2(0., 0.)
    }
}

fn inners(x: &Vec<Object>) -> Vec<InnerObject>{
    x.iter().map(|x| **x).collect()
}

impl<'a> ObjSystem<'a>{
    #[inline]
    pub fn new() -> Self{
        Default::default()
    }

    pub fn update(&mut self, info: &FrameInfo, drawer: &mut Drawer, arrow: Option<&Texture>, oobb: OutOfBoundsBehaviour){
        let bs = inners(&self.bodies);
        let wh = drawer.graphics.get_h_size();
        let delta = info.delta;

        let (mut acceleration, mut rot) = (0., 0.);
        is_down!{info;
            D, Right => {
                rot -= 2. * delta
            },
            A, Left => {
                rot += 2. * delta
            },
            S, Down => {
                acceleration -= 52. * delta
            },
            W, Up => {
                acceleration += 52. * delta;
            },
            LShift => {
                acceleration *= 7.;
            }
        }

        let mut indices_to_die = [Vec::new(), Vec::new(), Vec::new()];

        for (i, player) in self.players.iter_mut().enumerate(){
            let net_grav = bs.iter().fold(Vector2(0., 0.), |n_g, y| n_g + gravity(&player, y));

            let propulsion_force = Vector2::<f32>::unit_vector(player.rot) * acceleration;

            player.rot += rot;
            player.vel += propulsion_force;

            player.update(info, net_grav, wh, oobb);
            player.draw(drawer, arrow, net_grav, net_grav+propulsion_force);

            if player.health == 0{
                indices_to_die[0].push(i);
            }
        }
        for (i, body) in self.bodies.iter_mut().enumerate(){
            let net_grav = bs.iter().enumerate().filter_map(|(j, y)| if i==j{None}else{Some(y)}).fold(Vector2(0., 0.), |n_g, y| n_g + gravity(&body, y));

            body.update(info, net_grav, wh, oobb);
            body.draw(drawer, arrow, net_grav, net_grav);

            if body.health == 0{
                indices_to_die[1].push(i);
            }
        }
        for (i, projectile) in self.projectiles.iter_mut().enumerate(){
            let net_grav = bs.iter().fold(Vector2(0., 0.), |n_g, y| n_g + gravity(&projectile, y));

            projectile.rot = projectile.vel.direction();

            projectile.update(info, net_grav, wh, oobb);
            projectile.draw(drawer, arrow, net_grav, net_grav);

            for player in self.players.iter_mut(){
                if projectile.pos.distance_to(player.pos) - player.diameter/2. <= 16.{
                    player.health = player.health.saturating_sub(1);
                    indices_to_die[2].push(i);
                }
            }
            for body in self.bodies.iter_mut(){
                if projectile.pos.distance_to(body.pos) - body.diameter/2. <= 16.{
                    body.health = body.health.saturating_sub(1);
                    indices_to_die[2].push(i);
                }
            }
        }
        for v in &mut indices_to_die{
            v.sort();
            v.dedup();
        }
        for &i in indices_to_die[0].iter().rev(){
            self.players.remove(i);
        }
        for &i in indices_to_die[1].iter().rev(){
            self.bodies.remove(i);
        }
        for &i in indices_to_die[2].iter().rev(){
            self.projectiles.remove(i);
        }
    }

    pub fn len(&self) -> usize{
        self.players.len() + self.bodies.len() + self.projectiles.len()
    }
}
<|endoftext|>