<commit_before>use std::borrow::Borrow;
use std::cmp::{max, min};
use std::mem;

use rustbox::keyboard::Key;
use rustbox::{RustBox};

use tui::style;
use tui::termbox;
use tui::widget::{WidgetRet};

// TODO: Make these settings
const SCROLLOFF : i32 = 5;
const HIST_SIZE : usize = 30;

pub struct TextField {
    /// The message that's currently being edited (not yet sent)
    buffer : Vec<char>,

    /// Cursor in currently shown line
    cursor : i32,

    /// Horizontal scroll
    scroll : i32,

    /// Width of the widget
    width : i32,

    /// A history of sent messages/commands. Once added messages are never
    /// modified. A modification attempt should result in a new buffer with a
    /// copy of the vector in history. (old contents of the buffer will be lost)
    history : Vec<Vec<char>>,

    /// Only available when moving in `history` vector.
    /// INVARIANT: When available, it's a valid index in history.
    hist_curs : Option<i32>,
}

impl TextField {
    pub fn new(width : i32) -> TextField {
        TextField {
            buffer: Vec::with_capacity(512),
            cursor: 0,
            scroll: 0,
            width: width,
            history: Vec::with_capacity(HIST_SIZE),
            hist_curs: None,
        }
    }

    pub fn resize(&mut self, width : i32, _ : i32) {
        self.width = width;
        let cursor = self.cursor;
        self.move_cursor(cursor);
    }

    pub fn draw(&self, _ : &RustBox, pos_x : i32, pos_y : i32) {
        // draw text
        let line_borrow : &[char] = {
            if let Some(hist_curs) = self.hist_curs {
                self.history[hist_curs as usize].borrow()
            } else {
                self.buffer.borrow()
            }
        };

        let slice : &[char] =
            &line_borrow[ self.scroll as usize ..
                          min(self.line_len() as usize, (self.scroll + self.width) as usize) ];

        termbox::print_chars(pos_x, pos_y, style::USER_MSG.fg, style::USER_MSG.bg, slice);

        // draw cursor
        // TODO: render the char under the cursor
        termbox::print_char(pos_x + self.cursor - self.scroll, pos_y,
                            style::CURSOR.fg, style::CURSOR.bg,
                            *line_borrow.get(self.cursor as usize).unwrap_or(&' '));
    }

    pub fn keypressed(&mut self, key : Key) -> WidgetRet {
        match key {
            Key::Char(ch) => {
                self.modify();
                self.buffer.insert(self.cursor as usize, ch);
                self.inc_cursor();
                WidgetRet::KeyHandled
            },

            Key::Backspace => {
                if self.cursor > 0 {
                    self.modify();
                    self.buffer.remove(self.cursor as usize - 1);
                    self.dec_cursor();
                }
                WidgetRet::KeyHandled
            },

            Key::Ctrl(ch) => {
                if ch == 'a' {
                    self.move_cursor(0);
                    WidgetRet::KeyHandled
                } else if ch == 'e' {
                    let cur = self.line_len();
                    self.move_cursor(cur);
                    WidgetRet::KeyHandled
                } else if ch == 'k' {
                    self.modify();
                    self.buffer.drain(self.cursor as usize ..);
                    WidgetRet::KeyHandled
                } else if ch == 'w' {
                    self.modify();
                    // TODO: First consume whitespace under the cursor
                    let end_range = self.cursor as usize;
                    let mut begin_range = max(0, self.cursor - 1) as usize;
                    while begin_range > 0
                            && !self.buffer[begin_range].is_whitespace() {
                        begin_range -= 1;
                    }
                    self.buffer.drain(begin_range .. end_range);
                    self.move_cursor(begin_range as i32);
                    WidgetRet::KeyHandled
                } else {
                    WidgetRet::KeyIgnored
                }
            },

            Key::Left => {
                self.dec_cursor();
                WidgetRet::KeyHandled
            },

            Key::Right => {
                self.inc_cursor();
                WidgetRet::KeyHandled
            },

            Key::Enter => {
                let ret = mem::replace(&mut self.buffer, Vec::new());
                if self.history.len() == HIST_SIZE {
                    let mut reuse = self.history.remove(0);
                    reuse.clear();
                    reuse.extend_from_slice(&ret);
                    self.history.push(reuse);
                } else {
                    self.history.push(ret.clone());
                }
                self.move_cursor(0);
                if ret.len() == 0 {
                    WidgetRet::KeyHandled
                } else {
                    WidgetRet::Input(ret)
                }
            },

            ////////////////////////////////////////////////////////////////////
            // Scrolling in history

            Key::Up => {
                match self.hist_curs {
                    Some(hist_curs) if hist_curs > 0 => {
                        self.hist_curs = Some(hist_curs - 1);
                        let cur = self.line_len();
                        self.move_cursor(cur);
                    },
                    Some(_) => {},
                    None => {
                        if !self.history.is_empty() {
                            self.hist_curs = Some((self.history.len() as i32) - 1);
                            let cur = self.line_len();
                            self.move_cursor(cur);
                        }
                    }
                }
                WidgetRet::KeyHandled
            },

            Key::Down => {
                match self.hist_curs {
                    Some(hist_curs) => {
                        if (hist_curs as usize) < self.history.len() - 1 {
                            self.hist_curs = Some(hist_curs + 1);
                            let cur = self.line_len();
                            self.move_cursor(cur);
                        } else {
                            self.hist_curs = None;
                            let cur = self.line_len();
                            self.move_cursor(cur);
                        }
                    },
                    None => {}
                }
                WidgetRet::KeyHandled
            },

            ////////////////////////////////////////////////////////////////////

            _ => WidgetRet::KeyIgnored,
        }
    }

    #[inline]
    fn line_len(&self) -> i32 {
        if let Some(hist_curs) = self.hist_curs {
            self.history[hist_curs as usize].len() as i32
        } else {
            self.buffer.len() as i32
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    // We never modify history, so if the user attempts at a modification, we
    // just make the history entry current message by updating the current
    // buffer. (old contents are lost)

    fn modify(&mut self) {
        if let Some(hist_idx) = self.hist_curs {
            self.buffer.clear();
            self.buffer.extend_from_slice(self.history[hist_idx as usize].borrow());
            self.hist_curs = None;
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    // Manipulating cursor

    fn inc_cursor(&mut self) {
        let cur = min(self.line_len(), self.cursor + 1);
        self.move_cursor(cur);
    }

    fn dec_cursor(&mut self) {
        let cur = max(0, self.cursor - 1);
        self.move_cursor(cur);
    }

    // NOTE: This doesn't do bounds checking! Use dec_cursor() and inc_cursor().
    // move_cursor(0) should always be safe.
    fn move_cursor(&mut self, cursor : i32) {
        self.cursor = cursor;

        if self.line_len() + 1 < self.width {
            self.scroll = 0;
        } else {
            let scrolloff = { if self.width < 2 * SCROLLOFF + 1 { 0 } else { SCROLLOFF } };

            let left_end  = self.scroll;
            let right_end = self.scroll + self.width;

            if cursor - scrolloff < left_end {
                self.scroll = max(0, cursor - scrolloff);
            } else if cursor + scrolloff >= right_end {
                self.scroll = min(// +1 because cursor should be visible, i.e.
                                  // right_end > cursor should hold after this
                                  max(0, cursor + 1 + scrolloff - self.width),
                                  // +1 because cursor goes one more character
                                  // after the buffer, to be able to add chars
                                  max(0, self.line_len() + 1 - self.width));
            }
        }
    }
}
<commit_msg>text_field: minor refactoring<commit_after>use std::borrow::Borrow;
use std::cmp::{max, min};
use std::mem;

use rustbox::keyboard::Key;
use rustbox::{RustBox};

use tui::style;
use tui::termbox;
use tui::widget::{WidgetRet};

// TODO: Make these settings
const SCROLLOFF : i32 = 5;
const HIST_SIZE : usize = 30;

pub struct TextField {
    /// The message that's currently being edited (not yet sent)
    buffer : Vec<char>,

    /// Cursor in currently shown line
    cursor : i32,

    /// Horizontal scroll
    scroll : i32,

    /// Width of the widget
    width : i32,

    /// A history of sent messages/commands. Once added messages are never
    /// modified. A modification attempt should result in a new buffer with a
    /// copy of the vector in history. (old contents of the buffer will be lost)
    history : Vec<Vec<char>>,

    /// Only available when moving in `history` vector.
    /// INVARIANT: When available, it's a valid index in history.
    hist_curs : Option<i32>,
}

impl TextField {
    pub fn new(width : i32) -> TextField {
        TextField {
            buffer: Vec::with_capacity(512),
            cursor: 0,
            scroll: 0,
            width: width,
            history: Vec::with_capacity(HIST_SIZE),
            hist_curs: None,
        }
    }

    pub fn resize(&mut self, width : i32, _ : i32) {
        self.width = width;
        let cursor = self.cursor;
        self.move_cursor(cursor);
    }

    pub fn draw(&self, _ : &RustBox, pos_x : i32, pos_y : i32) {
        // draw text
        let line_borrow : &[char] = {
            if let Some(hist_curs) = self.hist_curs {
                self.history[hist_curs as usize].borrow()
            } else {
                self.buffer.borrow()
            }
        };

        let slice : &[char] =
            &line_borrow[ self.scroll as usize ..
                          min(line_borrow.len(), (self.scroll + self.width) as usize) ];

        termbox::print_chars(pos_x, pos_y, style::USER_MSG.fg, style::USER_MSG.bg, slice);

        // draw cursor
        // TODO: render the char under the cursor
        termbox::print_char(pos_x + self.cursor - self.scroll, pos_y,
                            style::CURSOR.fg, style::CURSOR.bg,
                            *line_borrow.get(self.cursor as usize).unwrap_or(&' '));
    }

    pub fn keypressed(&mut self, key : Key) -> WidgetRet {
        match key {
            Key::Char(ch) => {
                self.modify();
                self.buffer.insert(self.cursor as usize, ch);
                self.inc_cursor();
                WidgetRet::KeyHandled
            },

            Key::Backspace => {
                if self.cursor > 0 {
                    self.modify();
                    self.buffer.remove(self.cursor as usize - 1);
                    self.dec_cursor();
                }
                WidgetRet::KeyHandled
            },

            Key::Ctrl(ch) => {
                if ch == 'a' {
                    self.move_cursor(0);
                    WidgetRet::KeyHandled
                } else if ch == 'e' {
                    let cur = self.line_len();
                    self.move_cursor(cur);
                    WidgetRet::KeyHandled
                } else if ch == 'k' {
                    self.modify();
                    self.buffer.drain(self.cursor as usize ..);
                    WidgetRet::KeyHandled
                } else if ch == 'w' {
                    self.modify();
                    // TODO: First consume whitespace under the cursor
                    let end_range = self.cursor as usize;
                    let mut begin_range = max(0, self.cursor - 1) as usize;
                    while begin_range > 0
                            && !self.buffer[begin_range].is_whitespace() {
                        begin_range -= 1;
                    }
                    self.buffer.drain(begin_range .. end_range);
                    self.move_cursor(begin_range as i32);
                    WidgetRet::KeyHandled
                } else {
                    WidgetRet::KeyIgnored
                }
            },

            Key::Left => {
                self.dec_cursor();
                WidgetRet::KeyHandled
            },

            Key::Right => {
                self.inc_cursor();
                WidgetRet::KeyHandled
            },

            Key::Enter => {
                let ret = mem::replace(&mut self.buffer, Vec::new());
                if self.history.len() == HIST_SIZE {
                    let mut reuse = self.history.remove(0);
                    reuse.clear();
                    reuse.extend_from_slice(&ret);
                    self.history.push(reuse);
                } else {
                    self.history.push(ret.clone());
                }
                self.move_cursor(0);
                if ret.len() == 0 {
                    WidgetRet::KeyHandled
                } else {
                    WidgetRet::Input(ret)
                }
            },

            ////////////////////////////////////////////////////////////////////
            // Scrolling in history

            Key::Up => {
                match self.hist_curs {
                    Some(hist_curs) if hist_curs > 0 => {
                        self.hist_curs = Some(hist_curs - 1);
                        let cur = self.line_len();
                        self.move_cursor(cur);
                    },
                    Some(_) => {},
                    None => {
                        if !self.history.is_empty() {
                            self.hist_curs = Some((self.history.len() as i32) - 1);
                            let cur = self.line_len();
                            self.move_cursor(cur);
                        }
                    }
                }
                WidgetRet::KeyHandled
            },

            Key::Down => {
                match self.hist_curs {
                    Some(hist_curs) => {
                        if (hist_curs as usize) < self.history.len() - 1 {
                            self.hist_curs = Some(hist_curs + 1);
                            let cur = self.line_len();
                            self.move_cursor(cur);
                        } else {
                            self.hist_curs = None;
                            let cur = self.line_len();
                            self.move_cursor(cur);
                        }
                    },
                    None => {}
                }
                WidgetRet::KeyHandled
            },

            ////////////////////////////////////////////////////////////////////

            _ => WidgetRet::KeyIgnored,
        }
    }

    #[inline]
    fn line_len(&self) -> i32 {
        if let Some(hist_curs) = self.hist_curs {
            self.history[hist_curs as usize].len() as i32
        } else {
            self.buffer.len() as i32
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    // We never modify history, so if the user attempts at a modification, we
    // just make the history entry current message by updating the current
    // buffer. (old contents are lost)

    fn modify(&mut self) {
        if let Some(hist_idx) = self.hist_curs {
            self.buffer.clear();
            self.buffer.extend_from_slice(self.history[hist_idx as usize].borrow());
            self.hist_curs = None;
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    // Manipulating cursor

    fn inc_cursor(&mut self) {
        let cur = min(self.line_len(), self.cursor + 1);
        self.move_cursor(cur);
    }

    fn dec_cursor(&mut self) {
        let cur = max(0, self.cursor - 1);
        self.move_cursor(cur);
    }

    // NOTE: This doesn't do bounds checking! Use dec_cursor() and inc_cursor().
    // move_cursor(0) should always be safe.
    fn move_cursor(&mut self, cursor : i32) {
        self.cursor = cursor;

        if self.line_len() + 1 < self.width {
            self.scroll = 0;
        } else {
            let scrolloff = { if self.width < 2 * SCROLLOFF + 1 { 0 } else { SCROLLOFF } };

            let left_end  = self.scroll;
            let right_end = self.scroll + self.width;

            if cursor - scrolloff < left_end {
                self.scroll = max(0, cursor - scrolloff);
            } else if cursor + scrolloff >= right_end {
                self.scroll = min(// +1 because cursor should be visible, i.e.
                                  // right_end > cursor should hold after this
                                  max(0, cursor + 1 + scrolloff - self.width),
                                  // +1 because cursor goes one more character
                                  // after the buffer, to be able to add chars
                                  max(0, self.line_len() + 1 - self.width));
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! Communication channel with a physical device.
//!
//! The `Device` is one of the most important objects of Vulkan. Creating a `Device` is required
//! before you can create buffers, textures, shaders, etc.
//!
use std::ffi::CString;
use std::fmt;
use std::error;
use std::mem;
use std::ptr;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::MutexGuard;
use smallvec::SmallVec;

use instance::Features;
use instance::Instance;
use instance::PhysicalDevice;
use instance::QueueFamily;
use memory::pool::StdMemoryPool;
use sync::Semaphore;

use Error;
use OomError;
use SynchronizedVulkanObject;
use VulkanObject;
use VulkanPointers;
use check_errors;
use vk;

pub use instance::DeviceExtensions;

/// Represents a Vulkan context.
pub struct Device {
    instance: Arc<Instance>,
    physical_device: usize,
    device: vk::Device,
    vk: vk::DevicePointers,
    standard_pool: Mutex<Option<Arc<StdMemoryPool>>>,
    features: Features,
    extensions: DeviceExtensions,
}

impl Device {
    /// Builds a new Vulkan device for the given physical device.
    ///
    /// You must pass two things when creating a logical device:
    ///
    /// - A list of optional Vulkan features that must be enabled on the device. Note that if a
    ///   feature is not enabled at device creation, you can't use it later even it it's supported
    ///   by the physical device.
    ///
    /// - An iterator to a list of queues to create. Each element of the iterator must indicate
    ///   the family whose queue belongs to and a priority between 0.0 and 1.0 to assign to it.
    ///   A queue with a higher value indicates that the commands will execute faster than on a
    ///   queue with a lower value. Note however that no guarantee can be made on the way the
    ///   priority value is handled by the implementation.
    ///
    /// # Panic
    ///
    /// - Panicks if one of the requested features is not supported by the physical device.
    /// - Panicks if one of the queue families doesn't belong to the given device.
    /// - Panicks if you request more queues from a family than available.
    /// - Panicks if one of the priorities is outside of the `[0.0 ; 1.0]` range.
    ///
    // TODO: return Arc<Queue> and handle synchronization in the Queue
    pub fn new<'a, I, L>(phys: &'a PhysicalDevice, requested_features: &Features,
                         extensions: &DeviceExtensions, layers: L, queue_families: I)
                         -> Result<(Arc<Device>, Vec<Arc<Queue>>), DeviceCreationError>
        where I: IntoIterator<Item = (QueueFamily<'a>, f32)>,
              L: IntoIterator<Item = &'a &'a str>
    {
        let queue_families = queue_families.into_iter();

        assert!(phys.supported_features().superset_of(&requested_features));

        let vk_i = phys.instance().pointers();

        // this variable will contain the queue family ID and queue ID of each requested queue
        let mut output_queues: Vec<(u32, u32)> = Vec::with_capacity(queue_families.size_hint().0);

        let layers = layers.into_iter().map(|&layer| {
            // FIXME: check whether each layer is supported
            CString::new(layer).unwrap()
        }).collect::<SmallVec<[_; 8]>>();
        let layers = layers.iter().map(|layer| {
            layer.as_ptr()
        }).collect::<SmallVec<[_; 8]>>();

        let extensions_list = extensions.build_extensions_list();
        let extensions_list = extensions_list.iter().map(|extension| {
            extension.as_ptr()
        }).collect::<SmallVec<[_; 16]>>();

        // device creation
        let device = unsafe {
            // each element of `queues` is a `(queue_family, priorities)`
            // each queue family must only have one entry in `queues`
            let mut queues: Vec<(u32, Vec<f32>)> = Vec::with_capacity(phys.queue_families().len());

            for (queue_family, priority) in queue_families {
                // checking the parameters
                assert_eq!(queue_family.physical_device().internal_object(),
                           phys.internal_object());
                assert!(priority >= 0.0 && priority <= 1.0);

                // adding to `queues` and `output_queues`
                if let Some(q) = queues.iter_mut().find(|q| q.0 == queue_family.id()) {
                    output_queues.push((queue_family.id(), q.1.len() as u32));
                    q.1.push(priority);
                    assert!(q.1.len() < queue_family.queues_count());
                    continue;
                }
                queues.push((queue_family.id(), vec![priority]));
                output_queues.push((queue_family.id(), 0));
            }

            // turning `queues` into an array of `vkDeviceQueueCreateInfo` suitable for Vulkan
            let queues = queues.iter().map(|&(queue_id, ref priorities)| {
                vk::DeviceQueueCreateInfo {
                    sType: vk::STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
                    pNext: ptr::null(),
                    flags: 0,   // reserved
                    queueFamilyIndex: queue_id,
                    queueCount: priorities.len() as u32,
                    pQueuePriorities: priorities.as_ptr()
                }
            }).collect::<SmallVec<[_; 16]>>();

            // TODO: The plan regarding `robustBufferAccess` is to check the shaders' code to see
            //       if they can possibly perform out-of-bounds reads and writes. If the user tries
            //       to use a shader that can perform out-of-bounds operations without having
            //       `robustBufferAccess` enabled, an error is returned.
            //
            //       However for the moment this verification isn't performed. In order to be safe,
            //       we always enable the `robustBufferAccess` feature as it is guaranteed to be
            //       supported everywhere.
            //
            //       The only alternative (while waiting for shaders introspection to work) is to
            //       make all shaders depend on `robustBufferAccess`. But since usually the
            //       majority of shaders don't need this feature, it would be very annoying to have
            //       to enable it manually when you don't need it.
            let features = {
                let mut features: vk::PhysicalDeviceFeatures = requested_features.clone().into();
                features.robustBufferAccess = vk::TRUE;
                features
            };

            let infos = vk::DeviceCreateInfo {
                sType: vk::STRUCTURE_TYPE_DEVICE_CREATE_INFO,
                pNext: ptr::null(),
                flags: 0,   // reserved
                queueCreateInfoCount: queues.len() as u32,
                pQueueCreateInfos: queues.as_ptr(),
                enabledLayerCount: layers.len() as u32,
                ppEnabledLayerNames: layers.as_ptr(),
                enabledExtensionCount: extensions_list.len() as u32,
                ppEnabledExtensionNames: extensions_list.as_ptr(),
                pEnabledFeatures: &features,
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk_i.CreateDevice(phys.internal_object(), &infos,
                                                ptr::null(), &mut output)));
            output
        };

        // loading the function pointers of the newly-created device
        let vk = vk::DevicePointers::load(|name| {
            unsafe { vk_i.GetDeviceProcAddr(device, name.as_ptr()) as *const _ }
        });

        let device = Arc::new(Device {
            instance: phys.instance().clone(),
            physical_device: phys.index(),
            device: device,
            vk: vk,
            standard_pool: Mutex::new(None),
            features: requested_features.clone(),
            extensions: extensions.clone(),
        });

        // Creating the memory pool.
        {
            let mut pool_dest = device.standard_pool.lock().unwrap();
            *pool_dest = Some(StdMemoryPool::new(&device));
        }

        // querying the queues
        let output_queues = output_queues.into_iter().map(|(family, id)| {
            unsafe {
                let mut output = mem::uninitialized();
                device.vk.GetDeviceQueue(device.device, family, id, &mut output);
                Arc::new(Queue {
                    queue: Mutex::new(output),
                    device: device.clone(),
                    family: family,
                    id: id,
                    dedicated_semaphore: Mutex::new(None),
                })
            }
        }).collect();

        Ok((device, output_queues))
    }

    /// Waits until all work on this device has finished. You should never need to call
    /// this function, but it can be useful for debugging or benchmarking purposes.
    ///
    /// This is the Vulkan equivalent of `glFinish`.
    // FIXME: must synchronize all queuees
    #[inline]
    pub fn wait(&self) -> Result<(), OomError> {
        unsafe {
            try!(check_errors(self.vk.DeviceWaitIdle(self.device)));
            Ok(())
        }
    }

    /// Returns the instance used to create this device.
    #[inline]
    pub fn instance(&self) -> &Arc<Instance> {
        &self.instance
    }

    /// Returns the physical device that was used to create this device.
    #[inline]
    pub fn physical_device(&self) -> PhysicalDevice {
        PhysicalDevice::from_index(&self.instance, self.physical_device).unwrap()
    }

    /// Returns the features that are enabled in the device.
    #[inline]
    pub fn enabled_features(&self) -> &Features {
        &self.features
    }

    /// Returns the list of extensions that have been loaded.
    #[inline]
    pub fn loaded_extensions(&self) -> &DeviceExtensions {
        &self.extensions
    }

    /// Returns the standard memory pool used by default if you don't provide any other pool.
    #[inline]
    pub fn standard_pool(&self) -> Arc<StdMemoryPool> {
        self.standard_pool.lock().unwrap().clone().unwrap()
    }
}

impl fmt::Debug for Device {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "<Vulkan device>")
    }
}

unsafe impl VulkanObject for Device {
    type Object = vk::Device;

    #[inline]
    fn internal_object(&self) -> vk::Device {
        self.device
    }
}

impl VulkanPointers for Device {
    type Pointers = vk::DevicePointers;

    #[inline]
    fn pointers(&self) -> &vk::DevicePointers {
        &self.vk
    }
}

impl Drop for Device {
    #[inline]
    fn drop(&mut self) {
        let _ = self.standard_pool.lock().unwrap().take();

        unsafe {
            self.vk.DeviceWaitIdle(self.device);
            self.vk.DestroyDevice(self.device, ptr::null());
        }
    }
}

/// Error that can be returned when creating a device.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DeviceCreationError {
    /// There is no memory available on the host (ie. the CPU, RAM, etc.).
    OutOfHostMemory,
    /// There is no memory available on the device (ie. video memory).
    OutOfDeviceMemory,
    // FIXME: other values
}

impl error::Error for DeviceCreationError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            DeviceCreationError::OutOfHostMemory => "no memory available on the host",
            DeviceCreationError::OutOfDeviceMemory => "no memory available on the graphical device",
        }
    }
}

impl fmt::Display for DeviceCreationError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

impl From<Error> for DeviceCreationError {
    #[inline]
    fn from(err: Error) -> DeviceCreationError {
        match err {
            Error::OutOfHostMemory => DeviceCreationError::OutOfHostMemory,
            Error::OutOfDeviceMemory => DeviceCreationError::OutOfDeviceMemory,
            _ => panic!("Unexpected error value: {}", err as i32)
        }
    }
}

/// Represents a queue where commands can be submitted.
// TODO: should use internal synchronization
#[derive(Debug)]
pub struct Queue {
    queue: Mutex<vk::Queue>,
    device: Arc<Device>,
    family: u32,
    id: u32,    // id within family

    // For safety purposes, each command buffer submitted to a queue has to both wait on and
    // signal the semaphore specified here.
    //
    // If this is `None`, then that means we haven't used the semaphore yet.
    //
    // For more infos, see TODO: see what?
    dedicated_semaphore: Mutex<Option<Arc<Semaphore>>>,
}

impl Queue {
    /// Returns the device this queue belongs to.
    #[inline]
    pub fn device(&self) -> &Arc<Device> {
        &self.device
    }

    /// Returns the family this queue belongs to.
    #[inline]
    pub fn family(&self) -> QueueFamily {
        self.device.physical_device().queue_family_by_id(self.family).unwrap()
    }

    /// Returns the index of this queue within its family.
    #[inline]
    pub fn id_within_family(&self) -> u32 {
        self.id
    }

    /// Waits until all work on this queue has finished.
    ///
    /// Just like `Device::wait()`, you shouldn't have to call this function.
    #[inline]
    pub fn wait(&self) -> Result<(), OomError> {
        unsafe {
            let vk = self.device.pointers();
            let queue = self.queue.lock().unwrap();
            try!(check_errors(vk.QueueWaitIdle(*queue)));
            Ok(())
        }
    }

    // TODO: the design of this functions depends on https://github.com/KhronosGroup/Vulkan-Docs/issues/155
    /*// TODO: document
    #[doc(hidden)]
    #[inline]
    pub unsafe fn dedicated_semaphore(&self) -> Result<(Arc<Semaphore>, bool), OomError> {
        let mut sem = self.dedicated_semaphore.lock().unwrap();

        if let Some(ref semaphore) = *sem {
            return Ok((semaphore.clone(), true));
        }

        let semaphore = try!(Semaphore::new(&self.device));
        *sem = Some(semaphore.clone());
        Ok((semaphore, false))
    }*/
    #[doc(hidden)]
    #[inline]
    pub unsafe fn dedicated_semaphore(&self, signalled: Arc<Semaphore>) -> Option<Arc<Semaphore>> {
        let mut sem = self.dedicated_semaphore.lock().unwrap();
        mem::replace(&mut *sem, Some(signalled))
    }
}

unsafe impl SynchronizedVulkanObject for Queue {
    type Object = vk::Queue;

    #[inline]
    fn internal_object_guard(&self) -> MutexGuard<vk::Queue> {
        self.queue.lock().unwrap()
    }
}
<commit_msg>Add failing test for having one Arc ref to a Device<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! Communication channel with a physical device.
//!
//! The `Device` is one of the most important objects of Vulkan. Creating a `Device` is required
//! before you can create buffers, textures, shaders, etc.
//!
use std::ffi::CString;
use std::fmt;
use std::error;
use std::mem;
use std::ptr;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::MutexGuard;
use smallvec::SmallVec;

use instance::Features;
use instance::Instance;
use instance::PhysicalDevice;
use instance::QueueFamily;
use memory::pool::StdMemoryPool;
use sync::Semaphore;

use Error;
use OomError;
use SynchronizedVulkanObject;
use VulkanObject;
use VulkanPointers;
use check_errors;
use vk;

pub use instance::DeviceExtensions;

/// Represents a Vulkan context.
pub struct Device {
    instance: Arc<Instance>,
    physical_device: usize,
    device: vk::Device,
    vk: vk::DevicePointers,
    standard_pool: Mutex<Option<Arc<StdMemoryPool>>>,
    features: Features,
    extensions: DeviceExtensions,
}

impl Device {
    /// Builds a new Vulkan device for the given physical device.
    ///
    /// You must pass two things when creating a logical device:
    ///
    /// - A list of optional Vulkan features that must be enabled on the device. Note that if a
    ///   feature is not enabled at device creation, you can't use it later even it it's supported
    ///   by the physical device.
    ///
    /// - An iterator to a list of queues to create. Each element of the iterator must indicate
    ///   the family whose queue belongs to and a priority between 0.0 and 1.0 to assign to it.
    ///   A queue with a higher value indicates that the commands will execute faster than on a
    ///   queue with a lower value. Note however that no guarantee can be made on the way the
    ///   priority value is handled by the implementation.
    ///
    /// # Panic
    ///
    /// - Panicks if one of the requested features is not supported by the physical device.
    /// - Panicks if one of the queue families doesn't belong to the given device.
    /// - Panicks if you request more queues from a family than available.
    /// - Panicks if one of the priorities is outside of the `[0.0 ; 1.0]` range.
    ///
    // TODO: return Arc<Queue> and handle synchronization in the Queue
    pub fn new<'a, I, L>(phys: &'a PhysicalDevice, requested_features: &Features,
                         extensions: &DeviceExtensions, layers: L, queue_families: I)
                         -> Result<(Arc<Device>, Vec<Arc<Queue>>), DeviceCreationError>
        where I: IntoIterator<Item = (QueueFamily<'a>, f32)>,
              L: IntoIterator<Item = &'a &'a str>
    {
        let queue_families = queue_families.into_iter();

        assert!(phys.supported_features().superset_of(&requested_features));

        let vk_i = phys.instance().pointers();

        // this variable will contain the queue family ID and queue ID of each requested queue
        let mut output_queues: Vec<(u32, u32)> = Vec::with_capacity(queue_families.size_hint().0);

        let layers = layers.into_iter().map(|&layer| {
            // FIXME: check whether each layer is supported
            CString::new(layer).unwrap()
        }).collect::<SmallVec<[_; 8]>>();
        let layers = layers.iter().map(|layer| {
            layer.as_ptr()
        }).collect::<SmallVec<[_; 8]>>();

        let extensions_list = extensions.build_extensions_list();
        let extensions_list = extensions_list.iter().map(|extension| {
            extension.as_ptr()
        }).collect::<SmallVec<[_; 16]>>();

        // device creation
        let device = unsafe {
            // each element of `queues` is a `(queue_family, priorities)`
            // each queue family must only have one entry in `queues`
            let mut queues: Vec<(u32, Vec<f32>)> = Vec::with_capacity(phys.queue_families().len());

            for (queue_family, priority) in queue_families {
                // checking the parameters
                assert_eq!(queue_family.physical_device().internal_object(),
                           phys.internal_object());
                assert!(priority >= 0.0 && priority <= 1.0);

                // adding to `queues` and `output_queues`
                if let Some(q) = queues.iter_mut().find(|q| q.0 == queue_family.id()) {
                    output_queues.push((queue_family.id(), q.1.len() as u32));
                    q.1.push(priority);
                    assert!(q.1.len() < queue_family.queues_count());
                    continue;
                }
                queues.push((queue_family.id(), vec![priority]));
                output_queues.push((queue_family.id(), 0));
            }

            // turning `queues` into an array of `vkDeviceQueueCreateInfo` suitable for Vulkan
            let queues = queues.iter().map(|&(queue_id, ref priorities)| {
                vk::DeviceQueueCreateInfo {
                    sType: vk::STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
                    pNext: ptr::null(),
                    flags: 0,   // reserved
                    queueFamilyIndex: queue_id,
                    queueCount: priorities.len() as u32,
                    pQueuePriorities: priorities.as_ptr()
                }
            }).collect::<SmallVec<[_; 16]>>();

            // TODO: The plan regarding `robustBufferAccess` is to check the shaders' code to see
            //       if they can possibly perform out-of-bounds reads and writes. If the user tries
            //       to use a shader that can perform out-of-bounds operations without having
            //       `robustBufferAccess` enabled, an error is returned.
            //
            //       However for the moment this verification isn't performed. In order to be safe,
            //       we always enable the `robustBufferAccess` feature as it is guaranteed to be
            //       supported everywhere.
            //
            //       The only alternative (while waiting for shaders introspection to work) is to
            //       make all shaders depend on `robustBufferAccess`. But since usually the
            //       majority of shaders don't need this feature, it would be very annoying to have
            //       to enable it manually when you don't need it.
            let features = {
                let mut features: vk::PhysicalDeviceFeatures = requested_features.clone().into();
                features.robustBufferAccess = vk::TRUE;
                features
            };

            let infos = vk::DeviceCreateInfo {
                sType: vk::STRUCTURE_TYPE_DEVICE_CREATE_INFO,
                pNext: ptr::null(),
                flags: 0,   // reserved
                queueCreateInfoCount: queues.len() as u32,
                pQueueCreateInfos: queues.as_ptr(),
                enabledLayerCount: layers.len() as u32,
                ppEnabledLayerNames: layers.as_ptr(),
                enabledExtensionCount: extensions_list.len() as u32,
                ppEnabledExtensionNames: extensions_list.as_ptr(),
                pEnabledFeatures: &features,
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk_i.CreateDevice(phys.internal_object(), &infos,
                                                ptr::null(), &mut output)));
            output
        };

        // loading the function pointers of the newly-created device
        let vk = vk::DevicePointers::load(|name| {
            unsafe { vk_i.GetDeviceProcAddr(device, name.as_ptr()) as *const _ }
        });

        let device = Arc::new(Device {
            instance: phys.instance().clone(),
            physical_device: phys.index(),
            device: device,
            vk: vk,
            standard_pool: Mutex::new(None),
            features: requested_features.clone(),
            extensions: extensions.clone(),
        });

        // Creating the memory pool.
        {
            let mut pool_dest = device.standard_pool.lock().unwrap();
            *pool_dest = Some(StdMemoryPool::new(&device));
        }

        // querying the queues
        let output_queues = output_queues.into_iter().map(|(family, id)| {
            unsafe {
                let mut output = mem::uninitialized();
                device.vk.GetDeviceQueue(device.device, family, id, &mut output);
                Arc::new(Queue {
                    queue: Mutex::new(output),
                    device: device.clone(),
                    family: family,
                    id: id,
                    dedicated_semaphore: Mutex::new(None),
                })
            }
        }).collect();

        Ok((device, output_queues))
    }

    /// Waits until all work on this device has finished. You should never need to call
    /// this function, but it can be useful for debugging or benchmarking purposes.
    ///
    /// This is the Vulkan equivalent of `glFinish`.
    // FIXME: must synchronize all queuees
    #[inline]
    pub fn wait(&self) -> Result<(), OomError> {
        unsafe {
            try!(check_errors(self.vk.DeviceWaitIdle(self.device)));
            Ok(())
        }
    }

    /// Returns the instance used to create this device.
    #[inline]
    pub fn instance(&self) -> &Arc<Instance> {
        &self.instance
    }

    /// Returns the physical device that was used to create this device.
    #[inline]
    pub fn physical_device(&self) -> PhysicalDevice {
        PhysicalDevice::from_index(&self.instance, self.physical_device).unwrap()
    }

    /// Returns the features that are enabled in the device.
    #[inline]
    pub fn enabled_features(&self) -> &Features {
        &self.features
    }

    /// Returns the list of extensions that have been loaded.
    #[inline]
    pub fn loaded_extensions(&self) -> &DeviceExtensions {
        &self.extensions
    }

    /// Returns the standard memory pool used by default if you don't provide any other pool.
    #[inline]
    pub fn standard_pool(&self) -> Arc<StdMemoryPool> {
        self.standard_pool.lock().unwrap().clone().unwrap()
    }
}

impl fmt::Debug for Device {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "<Vulkan device>")
    }
}

unsafe impl VulkanObject for Device {
    type Object = vk::Device;

    #[inline]
    fn internal_object(&self) -> vk::Device {
        self.device
    }
}

impl VulkanPointers for Device {
    type Pointers = vk::DevicePointers;

    #[inline]
    fn pointers(&self) -> &vk::DevicePointers {
        &self.vk
    }
}

impl Drop for Device {
    #[inline]
    fn drop(&mut self) {
        let _ = self.standard_pool.lock().unwrap().take();

        unsafe {
            self.vk.DeviceWaitIdle(self.device);
            self.vk.DestroyDevice(self.device, ptr::null());
        }
    }
}

/// Error that can be returned when creating a device.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DeviceCreationError {
    /// There is no memory available on the host (ie. the CPU, RAM, etc.).
    OutOfHostMemory,
    /// There is no memory available on the device (ie. video memory).
    OutOfDeviceMemory,
    // FIXME: other values
}

impl error::Error for DeviceCreationError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            DeviceCreationError::OutOfHostMemory => "no memory available on the host",
            DeviceCreationError::OutOfDeviceMemory => "no memory available on the graphical device",
        }
    }
}

impl fmt::Display for DeviceCreationError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

impl From<Error> for DeviceCreationError {
    #[inline]
    fn from(err: Error) -> DeviceCreationError {
        match err {
            Error::OutOfHostMemory => DeviceCreationError::OutOfHostMemory,
            Error::OutOfDeviceMemory => DeviceCreationError::OutOfDeviceMemory,
            _ => panic!("Unexpected error value: {}", err as i32)
        }
    }
}

/// Represents a queue where commands can be submitted.
// TODO: should use internal synchronization
#[derive(Debug)]
pub struct Queue {
    queue: Mutex<vk::Queue>,
    device: Arc<Device>,
    family: u32,
    id: u32,    // id within family

    // For safety purposes, each command buffer submitted to a queue has to both wait on and
    // signal the semaphore specified here.
    //
    // If this is `None`, then that means we haven't used the semaphore yet.
    //
    // For more infos, see TODO: see what?
    dedicated_semaphore: Mutex<Option<Arc<Semaphore>>>,
}

impl Queue {
    /// Returns the device this queue belongs to.
    #[inline]
    pub fn device(&self) -> &Arc<Device> {
        &self.device
    }

    /// Returns the family this queue belongs to.
    #[inline]
    pub fn family(&self) -> QueueFamily {
        self.device.physical_device().queue_family_by_id(self.family).unwrap()
    }

    /// Returns the index of this queue within its family.
    #[inline]
    pub fn id_within_family(&self) -> u32 {
        self.id
    }

    /// Waits until all work on this queue has finished.
    ///
    /// Just like `Device::wait()`, you shouldn't have to call this function.
    #[inline]
    pub fn wait(&self) -> Result<(), OomError> {
        unsafe {
            let vk = self.device.pointers();
            let queue = self.queue.lock().unwrap();
            try!(check_errors(vk.QueueWaitIdle(*queue)));
            Ok(())
        }
    }

    // TODO: the design of this functions depends on https://github.com/KhronosGroup/Vulkan-Docs/issues/155
    /*// TODO: document
    #[doc(hidden)]
    #[inline]
    pub unsafe fn dedicated_semaphore(&self) -> Result<(Arc<Semaphore>, bool), OomError> {
        let mut sem = self.dedicated_semaphore.lock().unwrap();

        if let Some(ref semaphore) = *sem {
            return Ok((semaphore.clone(), true));
        }

        let semaphore = try!(Semaphore::new(&self.device));
        *sem = Some(semaphore.clone());
        Ok((semaphore, false))
    }*/
    #[doc(hidden)]
    #[inline]
    pub unsafe fn dedicated_semaphore(&self, signalled: Arc<Semaphore>) -> Option<Arc<Semaphore>> {
        let mut sem = self.dedicated_semaphore.lock().unwrap();
        mem::replace(&mut *sem, Some(signalled))
    }
}

unsafe impl SynchronizedVulkanObject for Queue {
    type Object = vk::Queue;

    #[inline]
    fn internal_object_guard(&self) -> MutexGuard<vk::Queue> {
        self.queue.lock().unwrap()
    }
}

#[cfg(test)]
mod tests {
    use std::sync::Arc;

    #[test]
    #[ignore]   // FIXME: failing, fix me
    fn one_ref() {
        let (mut device, _) = gfx_dev_and_queue!();
        assert!(Arc::get_mut(&mut device).is_some());
    }
}
<|endoftext|>


<commit_before>/*
 * Cymbalum, Molecular Simulation in Rust
 * Copyright (C) 2015 Guillaume Fraux
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/
*/
use std::f64::consts::PI;
use std::ops::Mul;
use std::convert::Into;
use ::types::*;

/// The type of a cell determine how we will be able to compute the periodic
/// boundaries condition.
#[derive(Clone, Copy)]
pub enum CellType {
    /// Infinite unit cell, with no boundaries
    INFINITE,
    /// Orthorombic unit cell, with cuboide shape
    ORTHOROMBIC,
    /// Triclinic unit cell, with arbitrary parallelepipedic shape
    TRICLINIC,
}

/// The Universe type hold all the data about a system.
#[derive(Clone, Copy)]
pub struct UnitCell {
    data: Matrix3,
    celltype: CellType,
}

impl UnitCell {
    /// Create an infinite unit cell
    pub fn new() -> UnitCell {
        UnitCell{data: Matrix3::zero(), celltype: CellType::INFINITE}
    }
    /// Create an orthorombic unit cell
    pub fn ortho(a: f64, b: f64, c: f64) -> UnitCell {
        UnitCell{data: Matrix3::new(a, 0.0, 0.0,
                                    0.0, b, 0.0,
                                    0.0, 0.0, c),
                 celltype: CellType::ORTHOROMBIC}
    }
    /// Create a cubic unit cell
    pub fn cubic(L: f64) -> UnitCell {
        UnitCell{data: Matrix3::new(L, 0.0, 0.0,
                                    0.0, L, 0.0,
                                    0.0, 0.0, L),
                 celltype: CellType::ORTHOROMBIC}
    }
    /// Create a triclinic unit cell
    pub fn triclinic(a: f64, b: f64, c: f64, alpha: f64, beta: f64, gamma: f64) -> UnitCell {
        let cos_alpha = deg2rad(alpha).cos();
        let cos_beta = deg2rad(beta).cos();
        let (sin_gamma, cos_gamma) = deg2rad(gamma).sin_cos();

        let b_x = b * cos_gamma;
        let b_y = b * sin_gamma;

        let c_x = c * cos_beta;
        let c_y = c * (cos_alpha - cos_beta*cos_gamma)/sin_gamma;
        let c_z = f64::sqrt(c*c - c_y*c_y - c_x*c_x);

        UnitCell{data: Matrix3::new(a,   b_x, c_x,
                                    0.0, b_y, c_y,
                                    0.0, 0.0, c_z),
                 celltype: CellType::TRICLINIC}
    }

    /// Get the cell type
    pub fn celltype(&self) -> CellType {
        self.celltype
    }
    /// Set the cell type
    pub fn set_celltype(&mut self, ctype: CellType) {
        self.celltype = ctype;
    }

    /// Get the first vector of the cell
    pub fn vect_a(&self) -> Vector3D {
        let x = self.data[(0, 0)];
        let y = self.data[(1, 0)];
        let z = self.data[(2, 0)];
        Vector3D::new(x, y, z)
    }
    /// Get the first length of the cell
    pub fn a(&self) -> f64 {
        match self.celltype {
            CellType::TRICLINIC => self.vect_a().norm(),
            _ => self.data[(0, 0)],
        }
    }

    /// Get the second length of the cell
    pub fn vect_b(&self) -> Vector3D {
        let x = self.data[(0, 1)];
        let y = self.data[(1, 1)];
        let z = self.data[(2, 1)];
        Vector3D::new(x, y, z)
    }
    /// Get the second length of the cell
    pub fn b(&self) -> f64 {
        match self.celltype {
            CellType::TRICLINIC => self.vect_b().norm(),
            _ => self.data[(1, 1)],
        }
    }

    /// Get the third length of the cell
    pub fn vect_c(&self) -> Vector3D {
        let x = self.data[(0, 2)];
        let y = self.data[(1, 2)];
        let z = self.data[(2, 2)];
        Vector3D::new(x, y, z)
    }
    /// Get the second length of the cell
    pub fn c(&self) -> f64 {
        match self.celltype {
            CellType::TRICLINIC => self.vect_c().norm(),
            _ => self.data[(2, 2)],
        }
    }

    /// Get the first angle of the cell
    pub fn alpha(&self) -> f64 {
        match self.celltype {
            CellType::TRICLINIC => {
                let b = self.vect_b();
                let c = self.vect_c();
                rad2deg(angle(b, c))
            },
            _ => 90.0,
        }
    }

    /// Get the second angle of the cell
    pub fn beta(&self) -> f64 {
        match self.celltype {
            CellType::TRICLINIC => {
                let a = self.vect_a();
                let c = self.vect_c();
                rad2deg(angle(a, c))
            },
            _ => 90.0,
        }
    }

    /// Get the third angle of the cell
    pub fn gamma(&self) -> f64 {
        match self.celltype {
            CellType::TRICLINIC => {
                let a = self.vect_a();
                let b = self.vect_b();
                rad2deg(angle(a, b))
            },
            _ => 90.0,
        }
    }

    /// Get the volume angle of the cell
    pub fn volume(&self) -> f64 {
        match self.celltype {
            CellType::INFINITE => 0.0,
            CellType::ORTHOROMBIC => self.a()*self.b()*self.c(),
            CellType::TRICLINIC => {
                // The volume is the mixed product of the three cell vectors
                let a = self.vect_a();
                let b = self.vect_b();
                let c = self.vect_c();
                a * (b ^ c)
            },
        }
    }

    pub fn scale_mut<S>(&mut self, scale: S) where S: Mul<Matrix3>, <S as Mul<Matrix3>>::Output: Into<Matrix3> {
        self.data = (scale * self.data).into();
    }

    pub fn scale<S>(&self, scale: S) -> UnitCell where S: Mul<Matrix3>, <S as Mul<Matrix3>>::Output: Into<Matrix3> {
        UnitCell{data: (scale * self.data).into(), celltype: self.celltype}
    }

}

/// Convert `x` from degrees to radians
fn deg2rad(x: f64) -> f64 {
    x * PI / 180.0
}
/// Convert `x` from radians to degrees
fn rad2deg(x: f64) -> f64 {
    x * 180.0 / PI
}

/// Get the angles between the vectors `u` and `v`.
fn angle(u: Vector3D, v: Vector3D) -> f64 {
    let un = u.normalize();
    let vn = v.normalize();
    f64::acos(un*vn)
}

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

    #[test]
    fn infinite() {
        let cell = UnitCell::new();
        assert_eq!(cell.a(), 0.0);
        assert_eq!(cell.b(), 0.0);
        assert_eq!(cell.c(), 0.0);

        assert_eq!(cell.alpha(), 90.0);
        assert_eq!(cell.beta(), 90.0);
        assert_eq!(cell.gamma(), 90.0);

        assert_eq!(cell.volume(), 0.0);
    }

    #[test]
    fn cubic() {
        let cell = UnitCell::cubic(3.0);
        assert_eq!(cell.a(), 3.0);
        assert_eq!(cell.b(), 3.0);
        assert_eq!(cell.c(), 3.0);

        assert_eq!(cell.alpha(), 90.0);
        assert_eq!(cell.beta(), 90.0);
        assert_eq!(cell.gamma(), 90.0);

        assert_eq!(cell.volume(), 3.0*3.0*3.0);
    }

    #[test]
    fn orthorombic() {
        let cell = UnitCell::ortho(3.0, 4.0, 5.0);
        assert_eq!(cell.a(), 3.0);
        assert_eq!(cell.b(), 4.0);
        assert_eq!(cell.c(), 5.0);

        assert_eq!(cell.alpha(), 90.0);
        assert_eq!(cell.beta(), 90.0);
        assert_eq!(cell.gamma(), 90.0);

        assert_eq!(cell.volume(), 3.0*4.0*5.0);
    }

    #[test]
    fn triclinic() {
        let cell = UnitCell::triclinic(3.0, 4.0, 5.0, 80.0, 90.0, 110.0);
        assert_eq!(cell.a(), 3.0);
        assert_eq!(cell.b(), 4.0);
        assert_eq!(cell.c(), 5.0);

        assert_eq!(cell.alpha(), 80.0);
        assert_eq!(cell.beta(), 90.0);
        assert_eq!(cell.gamma(), 110.0);

        assert_approx_eq!(cell.volume(), 55.410529, 1e-6);
    }

    #[test]
    fn scale() {
        let cell = UnitCell::ortho(3.0, 4.0, 5.0);
        let cell = cell.scale(2.0);

        assert_eq!(cell.a(), 6.0);
        assert_eq!(cell.b(), 8.0);
        assert_eq!(cell.c(), 10.0);

        let unit = Matrix3::one();
        let A = 0.5 * unit;
        let cell = cell.scale(A);

        assert_eq!(cell.a(), 3.0);
        assert_eq!(cell.b(), 4.0);
        assert_eq!(cell.c(), 5.0);
    }

    #[test]
    fn scale_mut() {
        let mut cell = UnitCell::ortho(3.0, 4.0, 5.0);
        cell.scale_mut(2.0);

        assert_eq!(cell.a(), 6.0);
        assert_eq!(cell.b(), 8.0);
        assert_eq!(cell.c(), 10.0);

        let unit = Matrix3::one();
        let A = 0.5 * unit;
        cell.scale_mut(A);

        assert_eq!(cell.a(), 3.0);
        assert_eq!(cell.b(), 4.0);
        assert_eq!(cell.c(), 5.0);
    }

}
<commit_msg>Only scale cells with a Matrix<commit_after>/*
 * Cymbalum, Molecular Simulation in Rust
 * Copyright (C) 2015 Guillaume Fraux
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/
*/
use std::f64::consts::PI;
use std::ops::Mul;
use std::convert::Into;
use ::types::*;

/// The type of a cell determine how we will be able to compute the periodic
/// boundaries condition.
#[derive(Clone, Copy)]
pub enum CellType {
    /// Infinite unit cell, with no boundaries
    INFINITE,
    /// Orthorombic unit cell, with cuboide shape
    ORTHOROMBIC,
    /// Triclinic unit cell, with arbitrary parallelepipedic shape
    TRICLINIC,
}

/// The Universe type hold all the data about a system.
#[derive(Clone, Copy)]
pub struct UnitCell {
    data: Matrix3,
    celltype: CellType,
}

impl UnitCell {
    /// Create an infinite unit cell
    pub fn new() -> UnitCell {
        UnitCell{data: Matrix3::zero(), celltype: CellType::INFINITE}
    }
    /// Create an orthorombic unit cell
    pub fn ortho(a: f64, b: f64, c: f64) -> UnitCell {
        UnitCell{data: Matrix3::new(a, 0.0, 0.0,
                                    0.0, b, 0.0,
                                    0.0, 0.0, c),
                 celltype: CellType::ORTHOROMBIC}
    }
    /// Create a cubic unit cell
    pub fn cubic(L: f64) -> UnitCell {
        UnitCell{data: Matrix3::new(L, 0.0, 0.0,
                                    0.0, L, 0.0,
                                    0.0, 0.0, L),
                 celltype: CellType::ORTHOROMBIC}
    }
    /// Create a triclinic unit cell
    pub fn triclinic(a: f64, b: f64, c: f64, alpha: f64, beta: f64, gamma: f64) -> UnitCell {
        let cos_alpha = deg2rad(alpha).cos();
        let cos_beta = deg2rad(beta).cos();
        let (sin_gamma, cos_gamma) = deg2rad(gamma).sin_cos();

        let b_x = b * cos_gamma;
        let b_y = b * sin_gamma;

        let c_x = c * cos_beta;
        let c_y = c * (cos_alpha - cos_beta*cos_gamma)/sin_gamma;
        let c_z = f64::sqrt(c*c - c_y*c_y - c_x*c_x);

        UnitCell{data: Matrix3::new(a,   b_x, c_x,
                                    0.0, b_y, c_y,
                                    0.0, 0.0, c_z),
                 celltype: CellType::TRICLINIC}
    }

    /// Get the cell type
    pub fn celltype(&self) -> CellType {
        self.celltype
    }
    /// Set the cell type
    pub fn set_celltype(&mut self, ctype: CellType) {
        self.celltype = ctype;
    }

    /// Get the first vector of the cell
    pub fn vect_a(&self) -> Vector3D {
        let x = self.data[(0, 0)];
        let y = self.data[(1, 0)];
        let z = self.data[(2, 0)];
        Vector3D::new(x, y, z)
    }
    /// Get the first length of the cell
    pub fn a(&self) -> f64 {
        match self.celltype {
            CellType::TRICLINIC => self.vect_a().norm(),
            _ => self.data[(0, 0)],
        }
    }

    /// Get the second length of the cell
    pub fn vect_b(&self) -> Vector3D {
        let x = self.data[(0, 1)];
        let y = self.data[(1, 1)];
        let z = self.data[(2, 1)];
        Vector3D::new(x, y, z)
    }
    /// Get the second length of the cell
    pub fn b(&self) -> f64 {
        match self.celltype {
            CellType::TRICLINIC => self.vect_b().norm(),
            _ => self.data[(1, 1)],
        }
    }

    /// Get the third length of the cell
    pub fn vect_c(&self) -> Vector3D {
        let x = self.data[(0, 2)];
        let y = self.data[(1, 2)];
        let z = self.data[(2, 2)];
        Vector3D::new(x, y, z)
    }
    /// Get the second length of the cell
    pub fn c(&self) -> f64 {
        match self.celltype {
            CellType::TRICLINIC => self.vect_c().norm(),
            _ => self.data[(2, 2)],
        }
    }

    /// Get the first angle of the cell
    pub fn alpha(&self) -> f64 {
        match self.celltype {
            CellType::TRICLINIC => {
                let b = self.vect_b();
                let c = self.vect_c();
                rad2deg(angle(b, c))
            },
            _ => 90.0,
        }
    }

    /// Get the second angle of the cell
    pub fn beta(&self) -> f64 {
        match self.celltype {
            CellType::TRICLINIC => {
                let a = self.vect_a();
                let c = self.vect_c();
                rad2deg(angle(a, c))
            },
            _ => 90.0,
        }
    }

    /// Get the third angle of the cell
    pub fn gamma(&self) -> f64 {
        match self.celltype {
            CellType::TRICLINIC => {
                let a = self.vect_a();
                let b = self.vect_b();
                rad2deg(angle(a, b))
            },
            _ => 90.0,
        }
    }

    /// Get the volume angle of the cell
    pub fn volume(&self) -> f64 {
        match self.celltype {
            CellType::INFINITE => 0.0,
            CellType::ORTHOROMBIC => self.a()*self.b()*self.c(),
            CellType::TRICLINIC => {
                // The volume is the mixed product of the three cell vectors
                let a = self.vect_a();
                let b = self.vect_b();
                let c = self.vect_c();
                a * (b ^ c)
            },
        }
    }

    /// Scale this unit cell in-place by multiplying the H matrix by `s`.
    pub fn scale_mut(&mut self, s: Matrix3) {
        self.data = s * self.data;
    }

    /// Scale this unit cell by multiplying the H matrix by `s`, and return a new unit cell
    pub fn scale(&self, s: Matrix3) -> UnitCell {
        UnitCell{data: s * self.data, celltype: self.celltype}
    }

}

/// Convert `x` from degrees to radians
fn deg2rad(x: f64) -> f64 {
    x * PI / 180.0
}
/// Convert `x` from radians to degrees
fn rad2deg(x: f64) -> f64 {
    x * 180.0 / PI
}

/// Get the angles between the vectors `u` and `v`.
fn angle(u: Vector3D, v: Vector3D) -> f64 {
    let un = u.normalize();
    let vn = v.normalize();
    f64::acos(un*vn)
}

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

    #[test]
    fn infinite() {
        let cell = UnitCell::new();
        assert_eq!(cell.a(), 0.0);
        assert_eq!(cell.b(), 0.0);
        assert_eq!(cell.c(), 0.0);

        assert_eq!(cell.alpha(), 90.0);
        assert_eq!(cell.beta(), 90.0);
        assert_eq!(cell.gamma(), 90.0);

        assert_eq!(cell.volume(), 0.0);
    }

    #[test]
    fn cubic() {
        let cell = UnitCell::cubic(3.0);
        assert_eq!(cell.a(), 3.0);
        assert_eq!(cell.b(), 3.0);
        assert_eq!(cell.c(), 3.0);

        assert_eq!(cell.alpha(), 90.0);
        assert_eq!(cell.beta(), 90.0);
        assert_eq!(cell.gamma(), 90.0);

        assert_eq!(cell.volume(), 3.0*3.0*3.0);
    }

    #[test]
    fn orthorombic() {
        let cell = UnitCell::ortho(3.0, 4.0, 5.0);
        assert_eq!(cell.a(), 3.0);
        assert_eq!(cell.b(), 4.0);
        assert_eq!(cell.c(), 5.0);

        assert_eq!(cell.alpha(), 90.0);
        assert_eq!(cell.beta(), 90.0);
        assert_eq!(cell.gamma(), 90.0);

        assert_eq!(cell.volume(), 3.0*4.0*5.0);
    }

    #[test]
    fn triclinic() {
        let cell = UnitCell::triclinic(3.0, 4.0, 5.0, 80.0, 90.0, 110.0);
        assert_eq!(cell.a(), 3.0);
        assert_eq!(cell.b(), 4.0);
        assert_eq!(cell.c(), 5.0);

        assert_eq!(cell.alpha(), 80.0);
        assert_eq!(cell.beta(), 90.0);
        assert_eq!(cell.gamma(), 110.0);

        assert_approx_eq!(cell.volume(), 55.410529, 1e-6);
    }

    #[test]
    fn scale() {
        let cell = UnitCell::ortho(3.0, 4.0, 5.0);
        let A = 2.0 * Matrix3::one();
        let cell = cell.scale(A);

        assert_eq!(cell.a(), 6.0);
        assert_eq!(cell.b(), 8.0);
        assert_eq!(cell.c(), 10.0);
    }

    #[test]
    fn scale_mut() {
        let mut cell = UnitCell::ortho(3.0, 4.0, 5.0);
        let A = 2.0 * Matrix3::one();
        cell.scale_mut(A);

        assert_eq!(cell.a(), 6.0);
        assert_eq!(cell.b(), 8.0);
        assert_eq!(cell.c(), 10.0);

    }

}
<|endoftext|>


<commit_before>/*******************************************************************************
 *
 * kit/kernel/kernel.rs
 *
 * vim:ft=rust:ts=4:sw=4:et:tw=80
 *
 * Copyright (C) 2015, Devyn Cairns
 * Redistribution of this file is permitted under the terms of the simplified
 * BSD license. See LICENSE for more information.
 *
 * Based on OSDev Bare Bones tutorial
 * http://wiki.osdev.org/Bare_Bones
 *
 ******************************************************************************/

//! The Kit kernel.

#![crate_type="lib"]
#![feature(core)]
#![feature(unique)]
#![feature(libc)]
#![feature(asm)]
#![feature(no_std)]
#![feature(lang_items)]
#![feature(unsafe_destructor)]
#![no_std]

#[macro_use]
extern crate core;

// No linkage. Mostly for types.
extern crate libc;

use core::prelude::*;

pub mod terminal;
pub mod constants;
pub mod multiboot;
pub mod memory;
pub mod interrupt;
pub mod paging;
pub mod keyboard;
pub mod archive;
pub mod process;
pub mod elf;
pub mod scheduler;
pub mod shell;
pub mod c_ffi;

use terminal::*;
use elf::Elf;
use process::Process;
use shell::shell;
use memory::Box;

use c_ffi::CStr;

/// Main kernel entry point.
#[no_mangle]
pub extern fn kernel_main() -> ! {

    console().reset().unwrap();
    console().set_color(Color::Red, Color::White).unwrap();

    console().write_str("+ Hello, I'm Kit.\n").unwrap();

    console().set_color(Color::White, Color::Red).unwrap();
    console().write_char('\n').unwrap();

    let mb_info = unsafe { multiboot::get_info() };

    match mb_info.mem_sizes() {
        Some((lower, upper)) => {
            write!(console(),
                   "{:<20} {:<10} KiB\n\
                    {:<20} {:<10} KiB\n",
                   "Lower memory:", lower,
                   "Upper memory:", upper).unwrap();
        },
        None => {
            write!(console(),
                   "W: Bootloader did not provide valid memory information!")
                .unwrap();
        }
    }

    match unsafe { mb_info.cmdline() } {
        Some(cmdline) => {
            write!(console(), "{:<20} ", "Kernel command line:").unwrap();

            console().write_raw_bytes(cmdline.as_bytes()).unwrap();
            console().write_char('\n').unwrap();
        },
        None => {
            write!(console(), "Kernel command line:\n").unwrap();
        }
    }

    console().write_char('\n').unwrap();
    console().set_color(Color::LightGrey, Color::Black).unwrap();
    console().write_char('\n').unwrap();

    if mb_info.flags & multiboot::info_flags::MEM_MAP != 0 {
        unsafe {
            let mmap = constants::translate_low_addr(mb_info.mmap_addr)
                .expect("mmap pointer outside low region");

            memory::initialize(mmap, mb_info.mmap_length);
        }
    } else {
        panic!("Bootloader did not provide memory map!");
    }

    unsafe {
        interrupt::initialize();
        paging::initialize();
        keyboard::initialize().unwrap();
    }

    if mb_info.flags & multiboot::info_flags::MODS != 0 {
        unsafe {
            let mods = constants::translate_low_addr(mb_info.mods_addr)
                .expect("mods pointer outside low region");

            if !archive::initialize(mods, mb_info.mods_count) {
                panic!("Archive initialization failed. Are you sure \
                        system.kit was provided?");
            }
        }
    } else {
        panic!("Bootloader did not provide modules!");
    }

    unsafe {
        process::initialize();
    }

    {
        // TEST
        let _b = Box::new(MyTest);
    }

    {
        let cmdline = unsafe { mb_info.cmdline().unwrap() };

        if !cmdline.is_empty() {
            spawn_init(cmdline).unwrap();
        } else {
            write!(console(), "W: No initial program specified on kernel \
                               command line; dropping into kernel\n   \
                               shell.\n").unwrap();

            unsafe {
                interrupt::enable();
                shell();
            }
        }
    }

    unreachable!();
}

struct MyTest;

impl Drop for MyTest {
    fn drop(&mut self) {
        let _ = write!(console(), "Dropping\n");
    }
}

#[derive(Debug)]
enum SpawnInitError {
    NoProgramSpecified,
    FileNotFound,
    ElfVerifyError,
    ProcessCreateError,
    ElfLoadError,
    SetArgsError
}
use SpawnInitError::*;

fn spawn_init<'a>(filename: CStr<'static>) -> Result<(), SpawnInitError> {

    console().set_color(Color::White, Color::Magenta).unwrap();

    if filename.is_empty() {
        return Err(NoProgramSpecified);
    }

    let system = archive::system();

    let data = try!(system.get(filename).ok_or(FileNotFound));

    let elf = try!(Elf::new(data).ok_or(ElfVerifyError));

    let mut process = try!(Process::new(filename).ok_or(ProcessCreateError));

    if !process.load(&elf) {
        return Err(ElfLoadError)
    }

    if !process.set_args(&[filename.as_ptr()]) {
        return Err(SetArgsError)
    }

    console().set_color(Color::LightGrey, Color::Black).unwrap();

    process.run();

    unsafe { scheduler::enter(); }

    Ok(())
}

#[lang = "stack_exhausted"]
extern fn stack_exhausted() {
}

#[lang = "eh_personality"]
extern fn eh_personality() {
}

#[lang = "panic_fmt"]
extern fn panic_fmt(fmt: core::fmt::Arguments,
                    file: &'static str,
                    line: usize) -> ! {

    let _ = console().set_color(Color::White, Color::Red);

    let _ = write!(console(), "\nKernel panic in {}:{}:\n  {}\n\n",
                   file, line, fmt);

    unsafe {
        asm!("cli" :::: "volatile");

        loop {
            asm!("hlt" :::: "volatile");
        }
    }
}
<commit_msg>[kernel] remove the Box test<commit_after>/*******************************************************************************
 *
 * kit/kernel/kernel.rs
 *
 * vim:ft=rust:ts=4:sw=4:et:tw=80
 *
 * Copyright (C) 2015, Devyn Cairns
 * Redistribution of this file is permitted under the terms of the simplified
 * BSD license. See LICENSE for more information.
 *
 * Based on OSDev Bare Bones tutorial
 * http://wiki.osdev.org/Bare_Bones
 *
 ******************************************************************************/

//! The Kit kernel.

#![crate_type="lib"]
#![feature(core)]
#![feature(unique)]
#![feature(libc)]
#![feature(asm)]
#![feature(no_std)]
#![feature(lang_items)]
#![feature(unsafe_destructor)]
#![no_std]

#[macro_use]
extern crate core;

// No linkage. Mostly for types.
extern crate libc;

use core::prelude::*;

pub mod terminal;
pub mod constants;
pub mod multiboot;
pub mod memory;
pub mod interrupt;
pub mod paging;
pub mod keyboard;
pub mod archive;
pub mod process;
pub mod elf;
pub mod scheduler;
pub mod shell;
pub mod c_ffi;

use terminal::*;
use elf::Elf;
use process::Process;
use shell::shell;

use c_ffi::CStr;

/// Main kernel entry point.
#[no_mangle]
pub extern fn kernel_main() -> ! {

    console().reset().unwrap();
    console().set_color(Color::Red, Color::White).unwrap();

    console().write_str("+ Hello, I'm Kit.\n").unwrap();

    console().set_color(Color::White, Color::Red).unwrap();
    console().write_char('\n').unwrap();

    let mb_info = unsafe { multiboot::get_info() };

    match mb_info.mem_sizes() {
        Some((lower, upper)) => {
            write!(console(),
                   "{:<20} {:<10} KiB\n\
                    {:<20} {:<10} KiB\n",
                   "Lower memory:", lower,
                   "Upper memory:", upper).unwrap();
        },
        None => {
            write!(console(),
                   "W: Bootloader did not provide valid memory information!")
                .unwrap();
        }
    }

    match unsafe { mb_info.cmdline() } {
        Some(cmdline) => {
            write!(console(), "{:<20} ", "Kernel command line:").unwrap();

            console().write_raw_bytes(cmdline.as_bytes()).unwrap();
            console().write_char('\n').unwrap();
        },
        None => {
            write!(console(), "Kernel command line:\n").unwrap();
        }
    }

    console().write_char('\n').unwrap();
    console().set_color(Color::LightGrey, Color::Black).unwrap();
    console().write_char('\n').unwrap();

    if mb_info.flags & multiboot::info_flags::MEM_MAP != 0 {
        unsafe {
            let mmap = constants::translate_low_addr(mb_info.mmap_addr)
                .expect("mmap pointer outside low region");

            memory::initialize(mmap, mb_info.mmap_length);
        }
    } else {
        panic!("Bootloader did not provide memory map!");
    }

    unsafe {
        interrupt::initialize();
        paging::initialize();
        keyboard::initialize().unwrap();
    }

    if mb_info.flags & multiboot::info_flags::MODS != 0 {
        unsafe {
            let mods = constants::translate_low_addr(mb_info.mods_addr)
                .expect("mods pointer outside low region");

            if !archive::initialize(mods, mb_info.mods_count) {
                panic!("Archive initialization failed. Are you sure \
                        system.kit was provided?");
            }
        }
    } else {
        panic!("Bootloader did not provide modules!");
    }

    unsafe {
        process::initialize();
    }

    {
        let cmdline = unsafe { mb_info.cmdline().unwrap() };

        if !cmdline.is_empty() {
            spawn_init(cmdline).unwrap();
        } else {
            write!(console(), "W: No initial program specified on kernel \
                               command line; dropping into kernel\n   \
                               shell.\n").unwrap();

            unsafe {
                interrupt::enable();
                shell();
            }
        }
    }

    unreachable!();
}

#[derive(Debug)]
enum SpawnInitError {
    NoProgramSpecified,
    FileNotFound,
    ElfVerifyError,
    ProcessCreateError,
    ElfLoadError,
    SetArgsError
}
use SpawnInitError::*;

fn spawn_init<'a>(filename: CStr<'static>) -> Result<(), SpawnInitError> {

    console().set_color(Color::White, Color::Magenta).unwrap();

    if filename.is_empty() {
        return Err(NoProgramSpecified);
    }

    let system = archive::system();

    let data = try!(system.get(filename).ok_or(FileNotFound));

    let elf = try!(Elf::new(data).ok_or(ElfVerifyError));

    let mut process = try!(Process::new(filename).ok_or(ProcessCreateError));

    if !process.load(&elf) {
        return Err(ElfLoadError)
    }

    if !process.set_args(&[filename.as_ptr()]) {
        return Err(SetArgsError)
    }

    console().set_color(Color::LightGrey, Color::Black).unwrap();

    process.run();

    unsafe { scheduler::enter(); }

    Ok(())
}

#[lang = "stack_exhausted"]
extern fn stack_exhausted() {
}

#[lang = "eh_personality"]
extern fn eh_personality() {
}

#[lang = "panic_fmt"]
extern fn panic_fmt(fmt: core::fmt::Arguments,
                    file: &'static str,
                    line: usize) -> ! {

    let _ = console().set_color(Color::White, Color::Red);

    let _ = write!(console(), "\nKernel panic in {}:{}:\n  {}\n\n",
                   file, line, fmt);

    unsafe {
        asm!("cli" :::: "volatile");

        loop {
            asm!("hlt" :::: "volatile");
        }
    }
}
<|endoftext|>


<commit_before>//! This pass is only used for UNIT TESTS related to incremental
//! compilation. It tests whether a particular `.o` file will be re-used
//! from a previous compilation or whether it must be regenerated.
//!
//! The user adds annotations to the crate of the following form:
//!
//! ```
//! #![rustc_partition_reused(module="spike", cfg="rpass2")]
//! #![rustc_partition_codegened(module="spike-x", cfg="rpass2")]
//! ```
//!
//! The first indicates (in the cfg `rpass2`) that `spike.o` will be
//! reused, the second that `spike-x.o` will be recreated. If these
//! annotations are inaccurate, errors are reported.
//!
//! The reason that we use `cfg=...` and not `#[cfg_attr]` is so that
//! the HIR doesn't change as a result of the annotations, which might
//! perturb the reuse results.
//!
//! `#![rustc_expected_cgu_reuse(module="spike", cfg="rpass2", kind="post-lto")]
//! allows for doing a more fine-grained check to see if pre- or post-lto data
//! was re-used.

use rustc_ast::ast;
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::mir::mono::CodegenUnitNameBuilder;
use rustc_middle::ty::TyCtxt;
use rustc_session::cgu_reuse_tracker::*;
use rustc_span::symbol::{sym, Symbol};
use std::collections::BTreeSet;

pub fn assert_module_sources(tcx: TyCtxt<'_>) {
    tcx.dep_graph.with_ignore(|| {
        if tcx.sess.opts.incremental.is_none() {
            return;
        }

        let available_cgus = tcx
            .collect_and_partition_mono_items(LOCAL_CRATE)
            .1
            .iter()
            .map(|cgu| cgu.name())
            .collect::<BTreeSet<Symbol>>();

        let ams = AssertModuleSource { tcx, available_cgus };

        for attr in tcx.hir().krate().item.attrs {
            ams.check_attr(attr);
        }
    })
}

struct AssertModuleSource<'tcx> {
    tcx: TyCtxt<'tcx>,
    available_cgus: BTreeSet<Symbol>,
}

impl AssertModuleSource<'tcx> {
    fn check_attr(&self, attr: &ast::Attribute) {
        let (expected_reuse, comp_kind) = if attr.check_name(sym::rustc_partition_reused) {
            (CguReuse::PreLto, ComparisonKind::AtLeast)
        } else if attr.check_name(sym::rustc_partition_codegened) {
            (CguReuse::No, ComparisonKind::Exact)
        } else if attr.check_name(sym::rustc_expected_cgu_reuse) {
            match self.field(attr, sym::kind) {
                sym::no => (CguReuse::No, ComparisonKind::Exact),
                sym::pre_dash_lto => (CguReuse::PreLto, ComparisonKind::Exact),
                sym::post_dash_lto => (CguReuse::PostLto, ComparisonKind::Exact),
                sym::any => (CguReuse::PreLto, ComparisonKind::AtLeast),
                other => {
                    self.tcx.sess.span_fatal(
                        attr.span,
                        &format!("unknown cgu-reuse-kind `{}` specified", other),
                    );
                }
            }
        } else {
            return;
        };

        if !self.tcx.sess.opts.debugging_opts.query_dep_graph {
            self.tcx.sess.span_fatal(
                attr.span,
                "found CGU-reuse attribute but `-Zquery-dep-graph` was not specified",
            );
        }

        if !self.check_config(attr) {
            debug!("check_attr: config does not match, ignoring attr");
            return;
        }

        let user_path = self.field(attr, sym::module).to_string();
        let crate_name = self.tcx.crate_name(LOCAL_CRATE).to_string();

        if !user_path.starts_with(&crate_name) {
            let msg = format!(
                "Found malformed codegen unit name `{}`. \
                Codegen units names must always start with the name of the \
                crate (`{}` in this case).",
                user_path, crate_name
            );
            self.tcx.sess.span_fatal(attr.span, &msg);
        }

        // Split of the "special suffix" if there is one.
        let (user_path, cgu_special_suffix) = if let Some(index) = user_path.rfind('.') {
            (&user_path[..index], Some(&user_path[index + 1..]))
        } else {
            (&user_path[..], None)
        };

        let mut cgu_path_components = user_path.split('-').collect::<Vec<_>>();

        // Remove the crate name
        assert_eq!(cgu_path_components.remove(0), crate_name);

        let cgu_name_builder = &mut CodegenUnitNameBuilder::new(self.tcx);
        let cgu_name =
            cgu_name_builder.build_cgu_name(LOCAL_CRATE, cgu_path_components, cgu_special_suffix);

        debug!("mapping '{}' to cgu name '{}'", self.field(attr, sym::module), cgu_name);

        if !self.available_cgus.contains(&cgu_name) {
            self.tcx.sess.span_err(
                attr.span,
                &format!(
                    "no module named `{}` (mangled: {}). \
                          Available modules: {}",
                    user_path,
                    cgu_name,
                    self.available_cgus
                        .iter()
                        .map(|cgu| cgu.to_string())
                        .collect::<Vec<_>>()
                        .join(", ")
                ),
            );
        }

        self.tcx.sess.cgu_reuse_tracker.set_expectation(
            cgu_name,
            &user_path,
            attr.span,
            expected_reuse,
            comp_kind,
        );
    }

    fn field(&self, attr: &ast::Attribute, name: Symbol) -> Symbol {
        for item in attr.meta_item_list().unwrap_or_else(Vec::new) {
            if item.has_name(name) {
                if let Some(value) = item.value_str() {
                    return value;
                } else {
                    self.tcx.sess.span_fatal(
                        item.span(),
                        &format!("associated value expected for `{}`", name),
                    );
                }
            }
        }

        self.tcx.sess.span_fatal(attr.span, &format!("no field `{}`", name));
    }

    /// Scan for a `cfg="foo"` attribute and check whether we have a
    /// cfg flag called `foo`.
    fn check_config(&self, attr: &ast::Attribute) -> bool {
        let config = &self.tcx.sess.parse_sess.config;
        let value = self.field(attr, sym::cfg);
        debug!("check_config(config={:?}, value={:?})", config, value);
        if config.iter().any(|&(name, _)| name == value) {
            debug!("check_config: matched");
            return true;
        }
        debug!("check_config: no match found");
        false
    }
}
<commit_msg>Rollup merge of #75210 - nnethercote:change-type-of-available_cgus, r=ecstatic-morse<commit_after>//! This pass is only used for UNIT TESTS related to incremental
//! compilation. It tests whether a particular `.o` file will be re-used
//! from a previous compilation or whether it must be regenerated.
//!
//! The user adds annotations to the crate of the following form:
//!
//! ```
//! #![rustc_partition_reused(module="spike", cfg="rpass2")]
//! #![rustc_partition_codegened(module="spike-x", cfg="rpass2")]
//! ```
//!
//! The first indicates (in the cfg `rpass2`) that `spike.o` will be
//! reused, the second that `spike-x.o` will be recreated. If these
//! annotations are inaccurate, errors are reported.
//!
//! The reason that we use `cfg=...` and not `#[cfg_attr]` is so that
//! the HIR doesn't change as a result of the annotations, which might
//! perturb the reuse results.
//!
//! `#![rustc_expected_cgu_reuse(module="spike", cfg="rpass2", kind="post-lto")]
//! allows for doing a more fine-grained check to see if pre- or post-lto data
//! was re-used.

use rustc_ast::ast;
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::mir::mono::CodegenUnitNameBuilder;
use rustc_middle::ty::TyCtxt;
use rustc_session::cgu_reuse_tracker::*;
use rustc_span::symbol::{sym, Symbol};
use std::collections::BTreeSet;

pub fn assert_module_sources(tcx: TyCtxt<'_>) {
    tcx.dep_graph.with_ignore(|| {
        if tcx.sess.opts.incremental.is_none() {
            return;
        }

        let available_cgus = tcx
            .collect_and_partition_mono_items(LOCAL_CRATE)
            .1
            .iter()
            .map(|cgu| cgu.name().to_string())
            .collect::<BTreeSet<String>>();

        let ams = AssertModuleSource { tcx, available_cgus };

        for attr in tcx.hir().krate().item.attrs {
            ams.check_attr(attr);
        }
    })
}

struct AssertModuleSource<'tcx> {
    tcx: TyCtxt<'tcx>,
    available_cgus: BTreeSet<String>,
}

impl AssertModuleSource<'tcx> {
    fn check_attr(&self, attr: &ast::Attribute) {
        let (expected_reuse, comp_kind) = if attr.check_name(sym::rustc_partition_reused) {
            (CguReuse::PreLto, ComparisonKind::AtLeast)
        } else if attr.check_name(sym::rustc_partition_codegened) {
            (CguReuse::No, ComparisonKind::Exact)
        } else if attr.check_name(sym::rustc_expected_cgu_reuse) {
            match self.field(attr, sym::kind) {
                sym::no => (CguReuse::No, ComparisonKind::Exact),
                sym::pre_dash_lto => (CguReuse::PreLto, ComparisonKind::Exact),
                sym::post_dash_lto => (CguReuse::PostLto, ComparisonKind::Exact),
                sym::any => (CguReuse::PreLto, ComparisonKind::AtLeast),
                other => {
                    self.tcx.sess.span_fatal(
                        attr.span,
                        &format!("unknown cgu-reuse-kind `{}` specified", other),
                    );
                }
            }
        } else {
            return;
        };

        if !self.tcx.sess.opts.debugging_opts.query_dep_graph {
            self.tcx.sess.span_fatal(
                attr.span,
                "found CGU-reuse attribute but `-Zquery-dep-graph` was not specified",
            );
        }

        if !self.check_config(attr) {
            debug!("check_attr: config does not match, ignoring attr");
            return;
        }

        let user_path = self.field(attr, sym::module).to_string();
        let crate_name = self.tcx.crate_name(LOCAL_CRATE).to_string();

        if !user_path.starts_with(&crate_name) {
            let msg = format!(
                "Found malformed codegen unit name `{}`. \
                Codegen units names must always start with the name of the \
                crate (`{}` in this case).",
                user_path, crate_name
            );
            self.tcx.sess.span_fatal(attr.span, &msg);
        }

        // Split of the "special suffix" if there is one.
        let (user_path, cgu_special_suffix) = if let Some(index) = user_path.rfind('.') {
            (&user_path[..index], Some(&user_path[index + 1..]))
        } else {
            (&user_path[..], None)
        };

        let mut cgu_path_components = user_path.split('-').collect::<Vec<_>>();

        // Remove the crate name
        assert_eq!(cgu_path_components.remove(0), crate_name);

        let cgu_name_builder = &mut CodegenUnitNameBuilder::new(self.tcx);
        let cgu_name =
            cgu_name_builder.build_cgu_name(LOCAL_CRATE, cgu_path_components, cgu_special_suffix);

        debug!("mapping '{}' to cgu name '{}'", self.field(attr, sym::module), cgu_name);

        if !self.available_cgus.contains(&*cgu_name.as_str()) {
            self.tcx.sess.span_err(
                attr.span,
                &format!(
                    "no module named `{}` (mangled: {}). Available modules: {}",
                    user_path,
                    cgu_name,
                    self.available_cgus
                        .iter()
                        .map(|cgu| cgu.to_string())
                        .collect::<Vec<_>>()
                        .join(", ")
                ),
            );
        }

        self.tcx.sess.cgu_reuse_tracker.set_expectation(
            cgu_name,
            &user_path,
            attr.span,
            expected_reuse,
            comp_kind,
        );
    }

    fn field(&self, attr: &ast::Attribute, name: Symbol) -> Symbol {
        for item in attr.meta_item_list().unwrap_or_else(Vec::new) {
            if item.has_name(name) {
                if let Some(value) = item.value_str() {
                    return value;
                } else {
                    self.tcx.sess.span_fatal(
                        item.span(),
                        &format!("associated value expected for `{}`", name),
                    );
                }
            }
        }

        self.tcx.sess.span_fatal(attr.span, &format!("no field `{}`", name));
    }

    /// Scan for a `cfg="foo"` attribute and check whether we have a
    /// cfg flag called `foo`.
    fn check_config(&self, attr: &ast::Attribute) -> bool {
        let config = &self.tcx.sess.parse_sess.config;
        let value = self.field(attr, sym::cfg);
        debug!("check_config(config={:?}, value={:?})", config, value);
        if config.iter().any(|&(name, _)| name == value) {
            debug!("check_config: matched");
            return true;
        }
        debug!("check_config: no match found");
        false
    }
}
<|endoftext|>


<commit_before>use crate::transform::{MirPass, MirSource};
use crate::util::patch::MirPatch;
use rustc_hir::lang_items;
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::*;
use rustc_middle::ty;
use rustc_middle::ty::TyCtxt;
use rustc_span::def_id::DefId;
use rustc_span::Span;

/// Inserts call to count_code_region() as a placeholder to be replaced during code generation with
/// the intrinsic llvm.instrprof.increment.
pub struct InstrumentCoverage;

impl<'tcx> MirPass<'tcx> for InstrumentCoverage {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
        if tcx.sess.opts.debugging_opts.instrument_coverage {
            debug!("instrumenting {:?}", src.def_id());
            instrument_coverage(tcx, body);
        }
    }
}

// The first counter (start of the function) is index zero.
const INIT_FUNCTION_COUNTER: u32 = 0;

/// Injects calls to placeholder function `count_code_region()`.
// FIXME(richkadel): As a first step, counters are only injected at the top of each function.
// The complete solution will inject counters at each conditional code branch.
pub fn instrument_coverage<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
    let span = body.span.shrink_to_lo();

    let count_code_region_fn = function_handle(
        tcx,
        tcx.require_lang_item(lang_items::CountCodeRegionFnLangItem, None),
        span,
    );
    let counter_index = Operand::const_from_scalar(
        tcx,
        tcx.types.u32,
        Scalar::from_u32(INIT_FUNCTION_COUNTER),
        span,
    );

    let mut patch = MirPatch::new(body);

    let new_block = patch.new_block(placeholder_block(SourceInfo::outermost(body.span)));
    let next_block = START_BLOCK;

    let temp = patch.new_temp(tcx.mk_unit(), body.span);
    patch.patch_terminator(
        new_block,
        TerminatorKind::Call {
            func: count_code_region_fn,
            args: vec![counter_index],
            // new_block will swapped with the next_block, after applying patch
            destination: Some((Place::from(temp), new_block)),
            cleanup: None,
            from_hir_call: false,
        },
    );

    patch.add_statement(new_block.start_location(), StatementKind::StorageLive(temp));
    patch.add_statement(next_block.start_location(), StatementKind::StorageDead(temp));

    patch.apply(body);

    // To insert the `new_block` in front of the first block in the counted branch (for example,
    // the START_BLOCK, at the top of the function), just swap the indexes, leaving the rest of the
    // graph unchanged.
    body.basic_blocks_mut().swap(next_block, new_block);
}

fn function_handle<'tcx>(tcx: TyCtxt<'tcx>, fn_def_id: DefId, span: Span) -> Operand<'tcx> {
    let ret_ty = tcx.fn_sig(fn_def_id).output();
    let ret_ty = ret_ty.no_bound_vars().unwrap();
    let substs = tcx.mk_substs(::std::iter::once(ty::subst::GenericArg::from(ret_ty)));
    Operand::function_handle(tcx, fn_def_id, substs, span)
}

fn placeholder_block<'tcx>(source_info: SourceInfo) -> BasicBlockData<'tcx> {
    BasicBlockData {
        statements: vec![],
        terminator: Some(Terminator {
            source_info,
            // this gets overwritten by the counter Call
            kind: TerminatorKind::Unreachable,
        }),
        is_cleanup: false,
    }
}
<commit_msg>Add new `fn_span` to TerminatorKind::Call instance<commit_after>use crate::transform::{MirPass, MirSource};
use crate::util::patch::MirPatch;
use rustc_hir::lang_items;
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::*;
use rustc_middle::ty;
use rustc_middle::ty::TyCtxt;
use rustc_span::def_id::DefId;
use rustc_span::Span;

/// Inserts call to count_code_region() as a placeholder to be replaced during code generation with
/// the intrinsic llvm.instrprof.increment.
pub struct InstrumentCoverage;

impl<'tcx> MirPass<'tcx> for InstrumentCoverage {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
        if tcx.sess.opts.debugging_opts.instrument_coverage {
            debug!("instrumenting {:?}", src.def_id());
            instrument_coverage(tcx, body);
        }
    }
}

// The first counter (start of the function) is index zero.
const INIT_FUNCTION_COUNTER: u32 = 0;

/// Injects calls to placeholder function `count_code_region()`.
// FIXME(richkadel): As a first step, counters are only injected at the top of each function.
// The complete solution will inject counters at each conditional code branch.
pub fn instrument_coverage<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
    let span = body.span.shrink_to_lo();

    let count_code_region_fn = function_handle(
        tcx,
        tcx.require_lang_item(lang_items::CountCodeRegionFnLangItem, None),
        span,
    );
    let counter_index = Operand::const_from_scalar(
        tcx,
        tcx.types.u32,
        Scalar::from_u32(INIT_FUNCTION_COUNTER),
        span,
    );

    let mut patch = MirPatch::new(body);

    let new_block = patch.new_block(placeholder_block(SourceInfo::outermost(body.span)));
    let next_block = START_BLOCK;

    let temp = patch.new_temp(tcx.mk_unit(), body.span);
    patch.patch_terminator(
        new_block,
        TerminatorKind::Call {
            func: count_code_region_fn,
            args: vec![counter_index],
            // new_block will swapped with the next_block, after applying patch
            destination: Some((Place::from(temp), new_block)),
            cleanup: None,
            from_hir_call: false,
            fn_span: span,
        },
    );

    patch.add_statement(new_block.start_location(), StatementKind::StorageLive(temp));
    patch.add_statement(next_block.start_location(), StatementKind::StorageDead(temp));

    patch.apply(body);

    // To insert the `new_block` in front of the first block in the counted branch (for example,
    // the START_BLOCK, at the top of the function), just swap the indexes, leaving the rest of the
    // graph unchanged.
    body.basic_blocks_mut().swap(next_block, new_block);
}

fn function_handle<'tcx>(tcx: TyCtxt<'tcx>, fn_def_id: DefId, span: Span) -> Operand<'tcx> {
    let ret_ty = tcx.fn_sig(fn_def_id).output();
    let ret_ty = ret_ty.no_bound_vars().unwrap();
    let substs = tcx.mk_substs(::std::iter::once(ty::subst::GenericArg::from(ret_ty)));
    Operand::function_handle(tcx, fn_def_id, substs, span)
}

fn placeholder_block<'tcx>(source_info: SourceInfo) -> BasicBlockData<'tcx> {
    BasicBlockData {
        statements: vec![],
        terminator: Some(Terminator {
            source_info,
            // this gets overwritten by the counter Call
            kind: TerminatorKind::Unreachable,
        }),
        is_cleanup: false,
    }
}
<|endoftext|>