<commit_before>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// 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::os::unix::io::RawFd;
use std::io::{Error, ErrorKind};

use libc;
use frame;
use rand;
use rand::Rng;
use errno::errno;
use socket::Socket;
use frame::FrameState;

#[derive(Clone)]
pub struct Nbstream {
    id: String,
    inner: Socket,
    state: FrameState,
    buffer: Vec<u8>,
    scratch: Vec<u8>,
    tx_queue: Vec<Vec<u8>>,
    rx_queue: Vec<Vec<u8>>
}

impl Nbstream {
    pub fn new(stream: Socket) -> Result<Nbstream, Error> {
        let mut result;
        result = unsafe { libc::fcntl(stream.as_raw_fd(), libc::F_GETFL, 0) };
        if result < 0 {
            return Err(Error::from_raw_os_error(errno().0 as i32))
        }

        let flags = result | libc::O_NONBLOCK;
        result = unsafe { libc::fcntl(stream.as_raw_fd(), libc::F_SETFL, flags) };
        if result < 0 {
            return Err(Error::from_raw_os_error(errno().0 as i32))
        }

        Ok(Nbstream {
            id: rand::thread_rng().gen_ascii_chars().take(15).collect::<String>(),
            inner: stream,
            state: FrameState::Start,
            buffer: Vec::with_capacity(3),
            scratch: Vec::new(),
            tx_queue: Vec::new(),
            rx_queue: Vec::new()
        })
    }

    pub fn id(&self) -> String {
        self.id.clone()
    }

    pub fn as_raw_fd(&self) -> RawFd {
        self.inner.as_raw_fd()
    }

    pub fn state(&self) -> FrameState {
        self.state.clone()
    }

    pub fn recv(&mut self) -> Result<(), Error> {
        loop {
            let mut buf = Vec::<u8>::with_capacity(512);
            unsafe { buf.set_len(512); }
            let result = self.inner.read(&mut buf[..]);
            if result.is_err() {
                let err = result.unwrap_err();
                if err.kind() == ErrorKind::WouldBlock {
                    trace!("read received WouldBlock");
                    return Ok(())
                }
                return Err(err)
            }
            let num_read = result.unwrap();

            trace!("read: {}bytes", num_read);

            buf = self.buf_with_scratch(&buf[..], num_read);
            let mut seek_pos = 0usize;

            if self.state == FrameState::Start {
                trace!("reading for framestate::start");
                self.read_for_frame_start(&buf[..], &mut seek_pos, num_read);
            }

            if self.state == FrameState::PayloadLen {
                trace!("reading for framestate::payloadlen");
                self.read_payload_len(&buf[..], &mut seek_pos, num_read);
            }

            if self.state == FrameState::Payload {
                trace!("reading for framestate::payload");
                self.read_payload(&buf[..], &mut seek_pos, num_read);
            }

            if self.state == FrameState::End {
                trace!("reading for framestate::end");
                let result = self.read_for_frame_end(&buf[..], seek_pos, num_read);
                if result.is_ok() {
                    self.rx_queue.push(result.unwrap());
                }
            }
        }
    }

    pub fn send(&mut self, buf: &[u8]) -> Result<usize, Error> {
        let mut total_written = 0usize;
        self.tx_queue.push(frame::from_slice(buf));
        trace!("frame pushed to tx queue");
        trace!("tx_queue.len: {}", self.tx_queue.len());
        for x in 0..self.tx_queue.len() {
            let b = self.tx_queue.remove(x);
            let result = self.inner.write(&b[..]);
            if result.is_err() {
                let err = result.unwrap_err();
                if err.kind() == ErrorKind::WouldBlock {
                    trace!("write received WouldBlock");
                    self.tx_queue.insert(x, b);
                }
                return Err(err)
            }

            let num_written = result.unwrap();
            trace!("wrote: {}bytes", num_written);
            total_written += num_written;
            if num_written < b.len() {
                trace!("wrote less than buf.len, adding remainder to tx_queue");
                let remainder = self.vec_from_slice(&b[(b.len() - num_written) ..b.len()]);
                self.tx_queue.insert(x, remainder);
                return Ok(total_written)
            }
        }
        Ok(total_written)
    }

    pub fn drain_rx_queue(&mut self) -> Vec<Vec<u8>> {
        let buf = self.rx_queue.clone();
        self.rx_queue = Vec::new();
        buf
    }

    fn buf_with_scratch(&mut self, buf: &[u8], len: usize) -> Vec<u8> {
        let mut new_buf = Vec::<u8>::with_capacity(self.scratch.len() + len);
        for byte in self.scratch.iter() {
            new_buf.push(*byte);
        }
        self.scratch = Vec::<u8>::new();
        for x in 0..len {
            new_buf.push(buf[x]);
        }
        new_buf
    }

    fn read_for_frame_start(&mut self, buf: &[u8], offset: &mut usize, len: usize) {
        for _ in *offset..len {
            if buf[*offset] == frame::START {
                self.buffer.push(buf[*offset]);
                self.state = FrameState::PayloadLen;
                *offset += 1;
                break;
            }
            *offset += 1;
        }
    }

    fn read_payload_len(&mut self, buf: &[u8], offset: &mut usize, len: usize) {
        for _ in *offset..len {
            self.buffer.push(buf[*offset]);
            if self.buffer.len() == 3 {
                let len = self.payload_len() + 1;
                self.buffer.reserve_exact(len);
                self.state = FrameState::Payload;
                *offset += 1;
                break;
            }
            *offset += 1;
        }
    }

    fn read_payload(&mut self, buf: &[u8], offset: &mut usize, len: usize) {
        for _ in *offset..len {
            self.buffer.push(buf[*offset]);
            if self.buffer.len() == self.payload_len() + 3 {
                self.state = FrameState::End;
                *offset += 1;
                break;
            }
            *offset += 1;
        }
    }

    fn read_for_frame_end(&mut self,
                          buf: &[u8],
                          offset: usize,
                          len: usize)
                          -> Result<Vec<u8>, ()> {
        if offset < len {
            let expected_end_byte = buf[offset];
            if expected_end_byte == frame::END {
                let mut payload = Vec::<u8>::with_capacity(self.payload_len());
                for x in 3..self.buffer.len() {
                    payload.push(self.buffer[x]);
                }

                self.state = FrameState::Start;
                self.buffer = Vec::<u8>::with_capacity(3);

                // If there is anything left in buf, we need to put it in our
                // scratch space because we're exiting here
                let mut offset = offset;
                offset += 1;
                self.scratch = Vec::<u8>::with_capacity(len - offset);
                for x in offset..len {
                    self.scratch.push(buf[x]);
                }
                return Ok(payload)
            }

            // If we're here, the frame was wrong. Maybe our fault, who knows?
            // Either way, we're going to reset and try to start again from the start byte.
            // We need to dump whatever is left in the buffer into our scratch because it
            // might be in there?
            self.state = FrameState::Start;
            self.buffer = Vec::<u8>::with_capacity(3);
            self.scratch = Vec::<u8>::with_capacity(len - offset);
            for x in offset..len {
                self.scratch.push(buf[x]);
            }
        }
        Err(())
    }

    fn payload_len(&self) -> usize {
        let mask = 0xFFFFu16;
        let len = ((self.buffer[1] as u16) << 8) & mask;
        (len | self.buffer[2] as u16) as usize
    }

    fn vec_from_slice(&self, slice: &[u8]) -> Vec<u8> {
        let mut buf = Vec::<u8>::with_capacity(slice.len());
        for byte in slice.iter() {
            buf.push(*byte);
        }
        buf
    }
}
<commit_msg>Stupid conflicts<commit_after>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// 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::os::unix::io::RawFd;
use std::io::{Error, ErrorKind};

use libc;
use frame;
use rand;
use rand::Rng;
use errno::errno;
use socket::Socket;
use frame::FrameState;

#[derive(Clone)]
pub struct Nbstream {
    id: String,
    inner: Socket,
    state: FrameState,
    buffer: Vec<u8>,
    scratch: Vec<u8>,
    tx_queue: Vec<Vec<u8>>,
    rx_queue: Vec<Vec<u8>>
}

impl Nbstream {
    pub fn new(stream: Socket) -> Result<Nbstream, Error> {
        let mut result;
        result = unsafe { libc::fcntl(stream.as_raw_fd(), libc::F_GETFL, 0) };
        if result < 0 {
            return Err(Error::from_raw_os_error(errno().0 as i32))
        }

        let flags = result | libc::O_NONBLOCK;
        result = unsafe { libc::fcntl(stream.as_raw_fd(), libc::F_SETFL, flags) };
        if result < 0 {
            return Err(Error::from_raw_os_error(errno().0 as i32))
        }

        Ok(Nbstream {
            id: rand::thread_rng().gen_ascii_chars().take(15).collect::<String>(),
            inner: stream,
            state: FrameState::Start,
            buffer: Vec::with_capacity(3),
            scratch: Vec::new(),
            tx_queue: Vec::new(),
            rx_queue: Vec::new()
        })
    }

    pub fn id(&self) -> String {
        self.id.clone()
    }

    pub fn as_raw_fd(&self) -> RawFd {
        self.inner.as_raw_fd()
    }

    pub fn state(&self) -> FrameState {
        self.state.clone()
    }

    pub fn recv(&mut self) -> Result<(), Error> {
        loop {
            let mut buf = Vec::<u8>::with_capacity(512);
            unsafe { buf.set_len(512); }
            let result = self.inner.read(&mut buf[..]);
            if result.is_err() {
                let err = result.unwrap_err();
                if err.kind() == ErrorKind::WouldBlock {
                    trace!("read received WouldBlock");
                    return Ok(())
                }
                return Err(err)
            }
            let num_read = result.unwrap();

            trace!("read: {}bytes", num_read);

            buf = self.buf_with_scratch(&buf[..], num_read);
            let len = buf.len();
            let mut seek_pos = 0usize;

            if self.state == FrameState::Start {
                trace!("reading for framestate::start");
                self.read_for_frame_start(&buf[..], &mut seek_pos, len);
            }

            if self.state == FrameState::PayloadLen {
                trace!("reading for framestate::payloadlen");
                self.read_payload_len(&buf[..], &mut seek_pos, len);
            }

            if self.state == FrameState::Payload {
                trace!("reading for framestate::payload");
                self.read_payload(&buf[..], &mut seek_pos, len);
            }

            if self.state == FrameState::End {
                trace!("reading for framestate::end");
                let result = self.read_for_frame_end(&buf[..], seek_pos, len);
                if result.is_ok() {
                    self.rx_queue.push(result.unwrap());
                }
            }
        }
    }

    pub fn send(&mut self, buf: &[u8]) -> Result<usize, Error> {
        let mut total_written = 0usize;
        self.tx_queue.push(frame::from_slice(buf));
        trace!("frame pushed to tx queue");
        trace!("tx_queue.len: {}", self.tx_queue.len());
        for x in 0..self.tx_queue.len() {
            let b = self.tx_queue.remove(x);
            let result = self.inner.write(&b[..]);
            if result.is_err() {
                let err = result.unwrap_err();
                if err.kind() == ErrorKind::WouldBlock {
                    trace!("write received WouldBlock");
                    self.tx_queue.insert(x, b);
                }
                return Err(err)
            }

            let num_written = result.unwrap();
            trace!("wrote: {}bytes", num_written);
            total_written += num_written;
            if num_written < b.len() {
                trace!("wrote less than buf.len, adding remainder to tx_queue");
                let remainder = self.vec_from_slice(&b[(b.len() - num_written) ..b.len()]);
                self.tx_queue.insert(x, remainder);
                return Ok(total_written)
            }
        }
        Ok(total_written)
    }

    pub fn drain_rx_queue(&mut self) -> Vec<Vec<u8>> {
        let buf = self.rx_queue.clone();
        self.rx_queue = Vec::new();
        buf
    }

    fn buf_with_scratch(&mut self, buf: &[u8], len: usize) -> Vec<u8> {
        let mut new_buf = Vec::<u8>::with_capacity(self.scratch.len() + len);
        for byte in self.scratch.iter() {
            new_buf.push(*byte);
        }
        self.scratch = Vec::<u8>::new();
        for x in 0..len {
            new_buf.push(buf[x]);
        }
        new_buf
    }

    fn read_for_frame_start(&mut self, buf: &[u8], offset: &mut usize, len: usize) {
        for _ in *offset..len {
            if buf[*offset] == frame::START {
                self.buffer.push(buf[*offset]);
                self.state = FrameState::PayloadLen;
                *offset += 1;
                break;
            }
            *offset += 1;
        }
    }

    fn read_payload_len(&mut self, buf: &[u8], offset: &mut usize, len: usize) {
        for _ in *offset..len {
            self.buffer.push(buf[*offset]);
            if self.buffer.len() == 3 {
                let len = self.payload_len() + 1;
                self.buffer.reserve_exact(len);
                self.state = FrameState::Payload;
                *offset += 1;
                break;
            }
            *offset += 1;
        }
    }

    fn read_payload(&mut self, buf: &[u8], offset: &mut usize, len: usize) {
        for _ in *offset..len {
            self.buffer.push(buf[*offset]);
            if self.buffer.len() == self.payload_len() + 3 {
                self.state = FrameState::End;
                *offset += 1;
                break;
            }
            *offset += 1;
        }
    }

    fn read_for_frame_end(&mut self,
                          buf: &[u8],
                          offset: usize,
                          len: usize)
                          -> Result<Vec<u8>, ()> {
        if offset < len {
            let expected_end_byte = buf[offset];
            if expected_end_byte == frame::END {
                let mut payload = Vec::<u8>::with_capacity(self.payload_len());
                for x in 3..self.buffer.len() {
                    payload.push(self.buffer[x]);
                }

                self.state = FrameState::Start;
                self.buffer = Vec::<u8>::with_capacity(3);

                // If there is anything left in buf, we need to put it in our
                // scratch space because we're exiting here
                let mut offset = offset;
                offset += 1;
                self.scratch = Vec::<u8>::with_capacity(len - offset);
                for x in offset..len {
                    self.scratch.push(buf[x]);
                }
                return Ok(payload)
            }

            // If we're here, the frame was wrong. Maybe our fault, who knows?
            // Either way, we're going to reset and try to start again from the start byte.
            // We need to dump whatever is left in the buffer into our scratch because it
            // might be in there?
            self.state = FrameState::Start;
            self.buffer = Vec::<u8>::with_capacity(3);
            self.scratch = Vec::<u8>::with_capacity(len - offset);
            for x in offset..len {
                self.scratch.push(buf[x]);
            }
        }
        Err(())
    }

    fn payload_len(&self) -> usize {
        let mask = 0xFFFFu16;
        let len = ((self.buffer[1] as u16) << 8) & mask;
        (len | self.buffer[2] as u16) as usize
    }

    fn vec_from_slice(&self, slice: &[u8]) -> Vec<u8> {
        let mut buf = Vec::<u8>::with_capacity(slice.len());
        for byte in slice.iter() {
            buf.push(*byte);
        }
        buf
    }
}
<|endoftext|>


<commit_before>// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use libc::EINVAL;
use winapi::um::processthreadsapi::GetCurrentThread;
use winapi::um::winbase::SetThreadAffinityMask;

use super::errno_result;
use super::Error;
use super::Result;

/// Set the CPU affinity of the current thread to a given set of CPUs.
/// The cpus must be a subset of those in the process affinity mask.
/// If there are more than 64 processors, the affinity mask must specify
/// processors in the thread's current processor group.
///
/// Returns the previous bits set for cpu_affinity.
/// # Examples
///
/// Set the calling thread's CPU affinity so it will run on only CPUs
/// 0, 1, 5, and 6.
///
/// ```
/// # use base::platform::set_cpu_affinity;
///   set_cpu_affinity(vec![0, 1, 5, 6]).unwrap();
/// ```
pub fn set_cpu_affinity<I: IntoIterator<Item = usize>>(cpus: I) -> Result<usize> {
    let mut affinity_mask: usize = 0;
    for cpu in cpus {
        affinity_mask |= 1 << cpu;
    }
    set_cpu_affinity_mask(affinity_mask)
}

pub fn set_cpu_affinity_mask(affinity_mask: usize) -> Result<usize> {
    let res: usize = unsafe {
        let thread_handle = GetCurrentThread();
        SetThreadAffinityMask(thread_handle, affinity_mask)
    };
    if res == 0 {
        return errno_result::<usize>();
    }
    Ok(res)
}

pub fn get_cpu_affinity() -> Result<Vec<usize>> {
    // Unimplemented for now, since this is unused on Windows.  Thread affinity can be read by
    // calling GetThreadAffinityMask twice, to get and restore the previous affinities.
    Err(Error::new(EINVAL))
}

#[cfg(test)]
mod tests {
    use winapi::um::processthreadsapi::GetCurrentProcess;
    use winapi::um::winbase::GetProcessAffinityMask;

    use super::super::sched::*;
    #[test]
    fn cpu_affinity() {
        let mut process_affinity_mask: usize = 0;
        let mut system_affinity_mask: usize = 0;
        let res = unsafe {
            GetProcessAffinityMask(
                GetCurrentProcess(),
                &mut process_affinity_mask as *mut usize,
                &mut system_affinity_mask as *mut usize,
            )
        };
        assert_ne!(res, 0);
        let mut prev_thread_affinity = set_cpu_affinity(vec![0, 1]).unwrap();
        assert_eq!(process_affinity_mask, prev_thread_affinity);
        // reset to original process affinity and grab previous affinity.
        prev_thread_affinity = set_cpu_affinity_mask(process_affinity_mask).unwrap();
        // cpu 0 + cpu 1, means bit 0 and bit 1 are set (1 + 2 = 3). Verify that is the case.
        assert_eq!(prev_thread_affinity, 3);
    }
}
<commit_msg>base: windows: error if CPU >= 64 in set_cpu_affinity<commit_after>// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use libc::EINVAL;
use winapi::um::processthreadsapi::GetCurrentThread;
use winapi::um::winbase::SetThreadAffinityMask;

use super::errno_result;
use super::Error;
use super::Result;

/// Set the CPU affinity of the current thread to a given set of CPUs.
/// The cpus must be a subset of those in the process affinity mask.
/// If there are more than 64 processors, the affinity mask must specify
/// processors in the thread's current processor group.
///
/// Returns the previous bits set for cpu_affinity.
/// # Examples
///
/// Set the calling thread's CPU affinity so it will run on only CPUs
/// 0, 1, 5, and 6.
///
/// ```
/// # use base::platform::set_cpu_affinity;
///   set_cpu_affinity(vec![0, 1, 5, 6]).unwrap();
/// ```
pub fn set_cpu_affinity<I: IntoIterator<Item = usize>>(cpus: I) -> Result<usize> {
    let mut affinity_mask: usize = 0;
    for cpu in cpus {
        if cpu >= 64 {
            return Err(Error::new(EINVAL));
        }
        affinity_mask |= 1 << cpu;
    }
    set_cpu_affinity_mask(affinity_mask)
}

pub fn set_cpu_affinity_mask(affinity_mask: usize) -> Result<usize> {
    let res: usize = unsafe {
        let thread_handle = GetCurrentThread();
        SetThreadAffinityMask(thread_handle, affinity_mask)
    };
    if res == 0 {
        return errno_result::<usize>();
    }
    Ok(res)
}

pub fn get_cpu_affinity() -> Result<Vec<usize>> {
    // Unimplemented for now, since this is unused on Windows.  Thread affinity can be read by
    // calling GetThreadAffinityMask twice, to get and restore the previous affinities.
    Err(Error::new(EINVAL))
}

#[cfg(test)]
mod tests {
    use winapi::um::processthreadsapi::GetCurrentProcess;
    use winapi::um::winbase::GetProcessAffinityMask;

    use super::super::sched::*;
    #[test]
    fn cpu_affinity() {
        let mut process_affinity_mask: usize = 0;
        let mut system_affinity_mask: usize = 0;
        let res = unsafe {
            GetProcessAffinityMask(
                GetCurrentProcess(),
                &mut process_affinity_mask as *mut usize,
                &mut system_affinity_mask as *mut usize,
            )
        };
        assert_ne!(res, 0);
        let mut prev_thread_affinity = set_cpu_affinity(vec![0, 1]).unwrap();
        assert_eq!(process_affinity_mask, prev_thread_affinity);
        // reset to original process affinity and grab previous affinity.
        prev_thread_affinity = set_cpu_affinity_mask(process_affinity_mask).unwrap();
        // cpu 0 + cpu 1, means bit 0 and bit 1 are set (1 + 2 = 3). Verify that is the case.
        assert_eq!(prev_thread_affinity, 3);
    }
}
<|endoftext|>


<commit_before>use std::sync::atomic::{AtomicUsize, Ordering};

/// A monotonically-increasing value used to compare when entities entered
/// their current zone.
pub type Timestamp = usize;

lazy_static! {
    static ref LAST_TIMESTAMP: AtomicUsize = AtomicUsize::new(0);
}

/// Generate a new timestamp, which should always be larger than the last one.
pub fn get_timestamp() -> Timestamp {
    LAST_TIMESTAMP.fetch_add(1, Ordering::SeqCst)
}

#[test]
fn it_gives_unique_numbers() {
    let a = get_timestamp();
    let b = get_timestamp();

    assert!(a != b);
    assert!(b > a);
}
<commit_msg>Clean up timestamp test<commit_after>use std::sync::atomic::{AtomicUsize, Ordering};

/// A monotonically-increasing value used to compare when entities entered
/// their current zone.
pub type Timestamp = usize;

lazy_static! {
    static ref LAST_TIMESTAMP: AtomicUsize = AtomicUsize::new(0);
}

/// Generate a new timestamp, which should always be larger than the last one.
pub fn get_timestamp() -> Timestamp {
    LAST_TIMESTAMP.fetch_add(1, Ordering::SeqCst)
}

#[test]
fn it_gives_increasing_numbers() {
    let a = get_timestamp();
    let b = get_timestamp();

    assert!(b > a);
}
<|endoftext|>


<commit_before>use id_allocator::IdAllocator;
use in_progress_terrain::InProgressTerrain;
use physics::Physics;
use state::EntityId;
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::collections::hash_map::Entry;
use std::rc::Rc;
use stopwatch::TimerSet;
use terrain::Terrain;
use terrain_block::BlockPosition;
use terrain_vram_buffers::TerrainVRAMBuffers;
use yaglw::gl_context::{GLContext, GLContextExistence};
use yaglw::shader::Shader;
use yaglw::texture::TextureUnit;

/// These are used to identify the owners of terrain load operations.
#[deriving(Copy, Clone, Show, PartialEq, Eq, Hash, Default)]
pub struct OwnerId(u32);

impl Add<u32, OwnerId> for OwnerId {
  fn add(self, rhs: u32) -> OwnerId {
    let OwnerId(id) = self;
    OwnerId(id + rhs)
  }
}

/// Load and unload TerrainBlocks from the game.
pub trait TerrainGameLoader {
  /// Ensure a `TerrainBlock` is loaded.
  fn load(
    &mut self,
    timers: &TimerSet,
    gl: &mut GLContext,
    id_allocator: &mut IdAllocator<EntityId>,
    physics: &mut Physics,
    block_position: &BlockPosition,
    lod: uint,
    owner: OwnerId,
  ) -> bool;

  /// Release a request for a `TerrainBlock`.
  fn unload(
    &mut self,
    timers: &TimerSet,
    gl: &mut GLContext,
    physics: &mut Physics,
    block_position: &BlockPosition,
    owner: OwnerId,
  ) -> bool;

  /// If the block is not already loaded, insert a solid block as a placeholder.
  /// Returns true if the block is not already loaded.
  fn insert_placeholder(
    &mut self,
    id_allocator: &mut IdAllocator<EntityId>,
    physics: &mut Physics,
    block_position: &BlockPosition,
    owner: OwnerId,
  ) -> bool;

  /// Remove a placeholder that was inserted by insert_placeholder.
  /// Returns true if a placeholder was removed.
  fn remove_placeholder(
    &mut self,
    physics: &mut Physics,
    block_position: &BlockPosition,
    owner: OwnerId,
  ) -> bool;
}

struct BlockLoadState {
  pub owners: HashSet<OwnerId>,
  /// If this is None, only a placeholder is loaded.
  pub loaded_lod: Option<uint>,
}

pub struct Default<'a> {
  pub terrain: Terrain,
  pub terrain_vram_buffers: TerrainVRAMBuffers<'a>,
  pub in_progress_terrain: InProgressTerrain,
  // The blocks that are currently loaded, and their owners and
  pub loaded: HashMap<BlockPosition, BlockLoadState>,
}

impl<'a> Default<'a> {
  pub fn new(
    gl: &'a GLContextExistence,
    gl_context: &mut GLContext,
    shader: Rc<RefCell<Shader>>,
    texture_unit_alloc: &mut IdAllocator<TextureUnit>,
  ) -> Default<'a> {
    let terrain_vram_buffers = TerrainVRAMBuffers::new(gl, gl_context);
    terrain_vram_buffers.bind_glsl_uniforms(gl_context, texture_unit_alloc, shader.clone());

    Default {
      terrain: Terrain::new(),
      terrain_vram_buffers: terrain_vram_buffers,
      in_progress_terrain: InProgressTerrain::new(),
      loaded: HashMap::new(),
    }
  }
}

impl<'a> TerrainGameLoader for Default<'a> {
  fn load(
    &mut self,
    timers: &TimerSet,
    gl: &mut GLContext,
    id_allocator: &mut IdAllocator<EntityId>,
    physics: &mut Physics,
    block_position: &BlockPosition,
    lod: uint,
    owner: OwnerId,
  ) -> bool {
    match self.loaded.entry(*block_position) {
      Entry::Occupied(mut entry) => {
        let block_load_state = entry.get_mut();
        let already_loaded = block_load_state.loaded_lod.is_some();
        block_load_state.owners.insert(owner);
        if already_loaded {
          return false;
        }
        block_load_state.loaded_lod = Some(lod);
      },
      Entry::Vacant(entry) => {
        let mut owners = HashSet::new();
        owners.insert(owner);
        entry.set(BlockLoadState {
          owners: owners,
          loaded_lod: Some(lod),
        });
      },
    }

    timers.time("terrain_game_loader.load", || {
      let block = unsafe {
        self.terrain.load(timers, id_allocator, block_position, lod)
      };

      timers.time("terrain_game_loader.load.physics", || {
        for (&id, bounds) in block.bounds.iter() {
          physics.insert_terrain(id, bounds.clone());
        }
      });

      let terrain_vram_buffers = &mut self.terrain_vram_buffers;
      timers.time("terrain_game_loader.load.gpu", || {
        terrain_vram_buffers.push(
          gl,
          block.vertex_coordinates.as_slice(),
          block.normals.as_slice(),
          block.typs.as_slice(),
          block.ids.as_slice(),
        );
      });

      self.in_progress_terrain.remove(physics, block_position);
    });

    true
  }

  fn unload(
    &mut self,
    timers: &TimerSet,
    gl: &mut GLContext,
    physics: &mut Physics,
    block_position: &BlockPosition,
    owner: OwnerId,
  ) -> bool {
    let loaded_lod;
    match self.loaded.entry(*block_position) {
      Entry::Occupied(mut entry) => {
        {
          let block_load_state = entry.get_mut();
          match block_load_state.loaded_lod {
            None => return false,
            Some(lod) => loaded_lod = lod,
          };
          let should_unload =
            block_load_state.owners.remove(&owner)
            && block_load_state.owners.is_empty();
          if !should_unload {
            return false;
          }
        }
        entry.take();
      },
      Entry::Vacant(_) => {
        return false;
      },
    };

    timers.time("terrain_game_loader.unload", || {
      let lods =
        self.terrain.all_blocks.get(block_position)
        .unwrap()
        .lods
        .as_slice();
      let block = lods[loaded_lod].as_ref().unwrap();
      for id in block.ids.iter() {
        physics.remove_terrain(*id);
        self.terrain_vram_buffers.swap_remove(gl, *id);
      }
    });

    true
  }

  /// Note that we want a specific `TerrainBlock`.
  fn insert_placeholder(
    &mut self,
    id_allocator: &mut IdAllocator<EntityId>,
    physics: &mut Physics,
    block_position: &BlockPosition,
    owner: OwnerId,
  ) -> bool {
    match self.loaded.entry(*block_position) {
      Entry::Occupied(mut entry) => {
        let block_load_state = entry.get_mut();
        block_load_state.owners.insert(owner);
        false
      },
      Entry::Vacant(entry) => {
        let mut owners = HashSet::new();
        owners.insert(owner);
        entry.set(BlockLoadState {
          owners: owners,
          loaded_lod: None,
        });
        assert!(self.in_progress_terrain.insert(id_allocator, physics, block_position));
        true
      },
    }
  }

  fn remove_placeholder(
    &mut self,
    physics: &mut Physics,
    block_position: &BlockPosition,
    owner: OwnerId,
  ) -> bool {
    match self.loaded.entry(*block_position) {
      Entry::Occupied(mut entry) => {
        {
          let block_load_state = entry.get_mut();
          let should_unload =
            block_load_state.loaded_lod.is_none()
            && block_load_state.owners.remove(&owner)
            && block_load_state.owners.is_empty();
          if !should_unload {
            return false;
          }
        }
        entry.take();
      },
      Entry::Vacant(_) => {
        return false;
      },
    };

    assert!(self.in_progress_terrain.remove(physics, block_position));

    true
  }
}
<commit_msg>Remove unused return types from TerrainGameLoader functions.<commit_after>use id_allocator::IdAllocator;
use in_progress_terrain::InProgressTerrain;
use physics::Physics;
use state::EntityId;
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::collections::hash_map::Entry;
use std::rc::Rc;
use stopwatch::TimerSet;
use terrain::Terrain;
use terrain_block::BlockPosition;
use terrain_vram_buffers::TerrainVRAMBuffers;
use yaglw::gl_context::{GLContext, GLContextExistence};
use yaglw::shader::Shader;
use yaglw::texture::TextureUnit;

/// These are used to identify the owners of terrain load operations.
#[deriving(Copy, Clone, Show, PartialEq, Eq, Hash, Default)]
pub struct OwnerId(u32);

impl Add<u32, OwnerId> for OwnerId {
  fn add(self, rhs: u32) -> OwnerId {
    let OwnerId(id) = self;
    OwnerId(id + rhs)
  }
}

/// Load and unload TerrainBlocks from the game.
pub trait TerrainGameLoader {
  /// Ensure a `TerrainBlock` is loaded.
  fn load(
    &mut self,
    timers: &TimerSet,
    gl: &mut GLContext,
    id_allocator: &mut IdAllocator<EntityId>,
    physics: &mut Physics,
    block_position: &BlockPosition,
    lod: uint,
    owner: OwnerId,
  );

  /// Release a request for a `TerrainBlock`.
  fn unload(
    &mut self,
    timers: &TimerSet,
    gl: &mut GLContext,
    physics: &mut Physics,
    block_position: &BlockPosition,
    owner: OwnerId,
  );

  /// If the block is not already loaded, insert a solid block as a placeholder.
  /// Returns true if the block is not already loaded.
  fn insert_placeholder(
    &mut self,
    id_allocator: &mut IdAllocator<EntityId>,
    physics: &mut Physics,
    block_position: &BlockPosition,
    owner: OwnerId,
  );

  /// Remove a placeholder that was inserted by insert_placeholder.
  /// Returns true if a placeholder was removed.
  fn remove_placeholder(
    &mut self,
    physics: &mut Physics,
    block_position: &BlockPosition,
    owner: OwnerId,
  );
}

struct BlockLoadState {
  pub owners: HashSet<OwnerId>,
  /// If this is None, only a placeholder is loaded.
  pub loaded_lod: Option<uint>,
}

pub struct Default<'a> {
  pub terrain: Terrain,
  pub terrain_vram_buffers: TerrainVRAMBuffers<'a>,
  pub in_progress_terrain: InProgressTerrain,
  // The blocks that are currently loaded, and their owners and
  pub loaded: HashMap<BlockPosition, BlockLoadState>,
}

impl<'a> Default<'a> {
  pub fn new(
    gl: &'a GLContextExistence,
    gl_context: &mut GLContext,
    shader: Rc<RefCell<Shader>>,
    texture_unit_alloc: &mut IdAllocator<TextureUnit>,
  ) -> Default<'a> {
    let terrain_vram_buffers = TerrainVRAMBuffers::new(gl, gl_context);
    terrain_vram_buffers.bind_glsl_uniforms(gl_context, texture_unit_alloc, shader.clone());

    Default {
      terrain: Terrain::new(),
      terrain_vram_buffers: terrain_vram_buffers,
      in_progress_terrain: InProgressTerrain::new(),
      loaded: HashMap::new(),
    }
  }
}

impl<'a> TerrainGameLoader for Default<'a> {
  fn load(
    &mut self,
    timers: &TimerSet,
    gl: &mut GLContext,
    id_allocator: &mut IdAllocator<EntityId>,
    physics: &mut Physics,
    block_position: &BlockPosition,
    lod: uint,
    owner: OwnerId,
  ) {
    match self.loaded.entry(*block_position) {
      Entry::Occupied(mut entry) => {
        let block_load_state = entry.get_mut();
        let already_loaded = block_load_state.loaded_lod.is_some();
        block_load_state.owners.insert(owner);
        if already_loaded {
          return;
        }
        block_load_state.loaded_lod = Some(lod);
      },
      Entry::Vacant(entry) => {
        let mut owners = HashSet::new();
        owners.insert(owner);
        entry.set(BlockLoadState {
          owners: owners,
          loaded_lod: Some(lod),
        });
      },
    }

    timers.time("terrain_game_loader.load", || {
      let block = unsafe {
        self.terrain.load(timers, id_allocator, block_position, lod)
      };

      timers.time("terrain_game_loader.load.physics", || {
        for (&id, bounds) in block.bounds.iter() {
          physics.insert_terrain(id, bounds.clone());
        }
      });

      let terrain_vram_buffers = &mut self.terrain_vram_buffers;
      timers.time("terrain_game_loader.load.gpu", || {
        terrain_vram_buffers.push(
          gl,
          block.vertex_coordinates.as_slice(),
          block.normals.as_slice(),
          block.typs.as_slice(),
          block.ids.as_slice(),
        );
      });

      self.in_progress_terrain.remove(physics, block_position);
    });
  }

  fn unload(
    &mut self,
    timers: &TimerSet,
    gl: &mut GLContext,
    physics: &mut Physics,
    block_position: &BlockPosition,
    owner: OwnerId,
  ) {
    let loaded_lod;
    match self.loaded.entry(*block_position) {
      Entry::Occupied(mut entry) => {
        {
          let block_load_state = entry.get_mut();
          match block_load_state.loaded_lod {
            None => return,
            Some(lod) => loaded_lod = lod,
          };
          let should_unload =
            block_load_state.owners.remove(&owner)
            && block_load_state.owners.is_empty();
          if !should_unload {
            return;
          }
        }
        entry.take();
      },
      Entry::Vacant(_) => {
        return;
      },
    };

    timers.time("terrain_game_loader.unload", || {
      let lods =
        self.terrain.all_blocks.get(block_position)
        .unwrap()
        .lods
        .as_slice();
      let block = lods[loaded_lod].as_ref().unwrap();
      for id in block.ids.iter() {
        physics.remove_terrain(*id);
        self.terrain_vram_buffers.swap_remove(gl, *id);
      }
    });
  }

  /// Note that we want a specific `TerrainBlock`.
  fn insert_placeholder(
    &mut self,
    id_allocator: &mut IdAllocator<EntityId>,
    physics: &mut Physics,
    block_position: &BlockPosition,
    owner: OwnerId,
  ) {
    match self.loaded.entry(*block_position) {
      Entry::Occupied(mut entry) => {
        let block_load_state = entry.get_mut();
        block_load_state.owners.insert(owner);
      },
      Entry::Vacant(entry) => {
        let mut owners = HashSet::new();
        owners.insert(owner);
        entry.set(BlockLoadState {
          owners: owners,
          loaded_lod: None,
        });
        assert!(self.in_progress_terrain.insert(id_allocator, physics, block_position));
      },
    }
  }

  fn remove_placeholder(
    &mut self,
    physics: &mut Physics,
    block_position: &BlockPosition,
    owner: OwnerId,
  ) {
    match self.loaded.entry(*block_position) {
      Entry::Occupied(mut entry) => {
        {
          let block_load_state = entry.get_mut();
          let should_unload =
            block_load_state.loaded_lod.is_none()
            && block_load_state.owners.remove(&owner)
            && block_load_state.owners.is_empty();
          if !should_unload {
            return;
          }
        }
        entry.take();
      },
      Entry::Vacant(_) => {
        return;
      },
    };

    assert!(self.in_progress_terrain.remove(physics, block_position));
  }
}
<|endoftext|>


<commit_before>//  notty is a new kind of terminal emulator.
//  Copyright (C) 2015 without boats
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Affero General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Affero General Public License for more details.
//
//  You should have received a copy of the GNU Affero General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
extern crate gdk;
extern crate gtk;

extern crate tty;
extern crate notty;
extern crate notty_cairo;

use std::cell::{Cell, RefCell};
use std::env;
use std::io::BufReader;
use std::sync::mpsc;
use std::rc::Rc;
use std::thread;

use gtk::{WindowTrait, WidgetTrait, WidgetSignals, ContainerTrait};

use notty::{Output, Command, KeyPress, KeyRelease};
use notty::terminal::Terminal;
use notty_cairo::Renderer;

mod commands;
mod key;

use commands::CommandApplicator;
use key::FromEvent;

static mut X_PIXELS: Option<u32> = None;
static mut Y_PIXELS: Option<u32> = None;

static COLS: u32 = 80;
static ROWS: u32 = 25;

fn main() {

    // Set up window and drawing canvas.
    gtk::init().unwrap();
    let window = gtk::Window::new(gtk::WindowType::Toplevel).unwrap();
    let canvas = Rc::new(gtk::DrawingArea::new().unwrap());
    window.add(&*canvas);

    // Set the TERM variable and establish a TTY connection
    env::set_var("TERM", "notty");
    let (tty_r, tty_w) = tty::pty("sh", COLS as u16, ROWS as u16);

    // Handler program output (tty -> screen) on separate thread.
    let (tx_out, rx) = mpsc::channel();
    let (tx_key_press, tx_key_release) = (tx_out.clone(), tx_out.clone());
    thread::spawn(move || {
        let output = Output::new(BufReader::new(tty_r));
        for cmd in output {
            tx_out.send(cmd.unwrap()).unwrap();
        }
    });

    // Create the scroll position tracker.
    let scroll   = Rc::new(Cell::new(0));
    let (scroll2, scroll3) = (scroll.clone(), scroll.clone());

    // Set up logical terminal and renderer.
    let terminal        = Rc::new(RefCell::new(Terminal::new(COLS, ROWS, tty_w)));

    // Process screen logic every 125 milliseconds.
    let cmd = CommandApplicator::new(rx, terminal.clone(), canvas.clone());
    gdk::glib::timeout_add(125, move || cmd.apply());

    // Connect signal to draw on canvas.
    canvas.connect_draw(move |_, canvas| {
        let mut terminal = terminal.borrow_mut();
        let skip = terminal.grid_height.saturating_sub(terminal.height + scroll.get() as u32);
        let renderer = Renderer::new(&canvas, skip);
        if let (Some(x_pix), Some(y_pix)) = unsafe {(X_PIXELS.take(), Y_PIXELS.take())} {
            renderer.reset_dimensions(&mut terminal, x_pix, y_pix);
        }
        renderer.draw(&terminal, &canvas);
        gtk::signal::Inhibit(false)
    });

    // Connect signal for changing window size.
    canvas.connect_configure_event(move |canvas, config| {
        unsafe {
            X_PIXELS = Some(config.width as u32);
            Y_PIXELS = Some(config.height as u32);
        }
        canvas.queue_draw();
        gtk::signal::Inhibit(false)
    });

    // Connect signal to receive key presses.
    window.connect_key_press_event(move |window, event| {
        if let Some(cmd) = KeyPress::from_event(event, &*scroll2) {
            tx_key_press.send(cmd).unwrap();
        } else { window.queue_draw(); }
        gtk::signal::Inhibit(false)
    });

    // Connect signal to receive key releases.
    window.connect_key_release_event(move |window, event| {
        if let Some(cmd) = KeyRelease::from_event(event, &*scroll3) {
            tx_key_release.send(cmd).unwrap();
        } else { window.queue_draw(); }
        gtk::signal::Inhibit(false)
    });

    // Show the window and run the GTK event loop.
    window.set_default_size(800, 800);
    window.show_all();
    gtk::main();

}
<commit_msg>Add window 'delete_event' handler.<commit_after>//  notty is a new kind of terminal emulator.
//  Copyright (C) 2015 without boats
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Affero General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Affero General Public License for more details.
//
//  You should have received a copy of the GNU Affero General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
extern crate gdk;
extern crate gtk;

extern crate tty;
extern crate notty;
extern crate notty_cairo;

use std::cell::{Cell, RefCell};
use std::env;
use std::io::BufReader;
use std::sync::mpsc;
use std::rc::Rc;
use std::thread;

use gtk::{WindowTrait, WidgetTrait, WidgetSignals, ContainerTrait};

use notty::{Output, Command, KeyPress, KeyRelease};
use notty::terminal::Terminal;
use notty_cairo::Renderer;

mod commands;
mod key;

use commands::CommandApplicator;
use key::FromEvent;

static mut X_PIXELS: Option<u32> = None;
static mut Y_PIXELS: Option<u32> = None;

static COLS: u32 = 80;
static ROWS: u32 = 25;

fn main() {

    // Set up window and drawing canvas.
    gtk::init().unwrap();
    let window = gtk::Window::new(gtk::WindowType::Toplevel).unwrap();
    let canvas = Rc::new(gtk::DrawingArea::new().unwrap());
    window.add(&*canvas);

    // Set the TERM variable and establish a TTY connection
    env::set_var("TERM", "notty");
    let (tty_r, tty_w) = tty::pty("sh", COLS as u16, ROWS as u16);

    // Handler program output (tty -> screen) on separate thread.
    let (tx_out, rx) = mpsc::channel();
    let (tx_key_press, tx_key_release) = (tx_out.clone(), tx_out.clone());
    thread::spawn(move || {
        let output = Output::new(BufReader::new(tty_r));
        for cmd in output {
            tx_out.send(cmd.unwrap()).unwrap();
        }
    });

    // Create the scroll position tracker.
    let scroll   = Rc::new(Cell::new(0));
    let (scroll2, scroll3) = (scroll.clone(), scroll.clone());

    // Set up logical terminal and renderer.
    let terminal        = Rc::new(RefCell::new(Terminal::new(COLS, ROWS, tty_w)));

    // Process screen logic every 125 milliseconds.
    let cmd = CommandApplicator::new(rx, terminal.clone(), canvas.clone());
    gdk::glib::timeout_add(125, move || cmd.apply());

    // Connect signal to draw on canvas.
    canvas.connect_draw(move |_, canvas| {
        let mut terminal = terminal.borrow_mut();
        let skip = terminal.grid_height.saturating_sub(terminal.height + scroll.get() as u32);
        let renderer = Renderer::new(&canvas, skip);
        if let (Some(x_pix), Some(y_pix)) = unsafe {(X_PIXELS.take(), Y_PIXELS.take())} {
            renderer.reset_dimensions(&mut terminal, x_pix, y_pix);
        }
        renderer.draw(&terminal, &canvas);
        gtk::signal::Inhibit(false)
    });

    // Connect signal for changing window size.
    canvas.connect_configure_event(move |canvas, config| {
        unsafe {
            X_PIXELS = Some(config.width as u32);
            Y_PIXELS = Some(config.height as u32);
        }
        canvas.queue_draw();
        gtk::signal::Inhibit(false)
    });

    // Connect signal to receive key presses.
    window.connect_key_press_event(move |window, event| {
        if let Some(cmd) = KeyPress::from_event(event, &*scroll2) {
            tx_key_press.send(cmd).unwrap();
        } else { window.queue_draw(); }
        gtk::signal::Inhibit(false)
    });

    // Connect signal to receive key releases.
    window.connect_key_release_event(move |window, event| {
        if let Some(cmd) = KeyRelease::from_event(event, &*scroll3) {
            tx_key_release.send(cmd).unwrap();
        } else { window.queue_draw(); }
        gtk::signal::Inhibit(false)
    });

    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        gtk::signal::Inhibit(false)
    });

    // Show the window and run the GTK event loop.
    window.set_default_size(800, 800);
    window.show_all();
    gtk::main();

}
<|endoftext|>


<commit_before>use std::cmp::{Ord, Ordering};
use std::sync::atomic;
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};

/// Each node is given a unique ID.
/// This is necessary because these nodes need to comparable,
/// and pointer comparison might not be valid if the type has zero size and is
/// stored in an optimized container.
type NodeId = u32;

/// Takes two inputs:
///   A1 (a sum of automations)
///   A2 (a sum of automations)
/// Provides one output: A1*A2, which is a sum of automations
pub struct ANode {
    id: NodeId,
}

/// Takes two inputs:
///   Y (a sum of partials)
///   A (a sum of automations)
/// Provides one output: Y*A, which is a sum of partials
pub struct YNode {
    id: NodeId,
}

pub enum Node {
    ANode(ANode),
    YNode(YNode),
}

/// Used primarily in Sends; Automations can either be sent to the *left* input
/// of an ANode, or to the right input.
#[derive(Clone, Copy, Debug)]
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub enum NodeInputSlot {
    Left,
    Right,
}

static mut prev_node_id : AtomicUsize = ATOMIC_USIZE_INIT;

impl Node {
    fn consume_next_id() -> NodeId {
        let id = unsafe {
            prev_node_id.fetch_add(1, atomic::Ordering::SeqCst)
        };
        id as u32
    }
    fn id(&self) -> NodeId {
        match self {
            &Node::ANode(ref anode) => anode.id(),
            &Node::YNode(ref ynode) => ynode.id(),
        }
    }
}

impl ANode {
    pub fn new() -> ANode {
        ANode{ id: Node::consume_next_id() }
    }
    fn id(&self) -> NodeId {
        self.id
    }
}

impl YNode {
    pub fn new() -> YNode {
        YNode{ id: Node::consume_next_id() }
    }
    fn id(&self) -> NodeId {
        self.id
    }
}

// Implement comparisons between Nodes so that they can be orderable.
impl Ord for Node {
    fn cmp(&self, other: &Node) -> Ordering {
        self.id().cmp(&other.id())
    }
}

impl PartialOrd for Node {
    fn partial_cmp(&self, other: &Node) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl PartialEq for Node {
    fn eq(&self, other: &Node) -> bool {
        self.cmp(other) == Ordering::Equal
    }
}
impl Eq for Node {}

// Comparisons for ANode
impl Ord for ANode {
    fn cmp(&self, other: &ANode) -> Ordering {
        self.id().cmp(&other.id())
    }
}

impl PartialOrd for ANode {
    fn partial_cmp(&self, other: &ANode) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl PartialEq for ANode {
    fn eq(&self, other: &ANode) -> bool {
        self.cmp(other) == Ordering::Equal
    }
}
impl Eq for ANode {}

// Comparisons for YNode
impl Ord for YNode {
    fn cmp(&self, other: &YNode) -> Ordering {
        self.id().cmp(&other.id())
    }
}

impl PartialOrd for YNode {
    fn partial_cmp(&self, other: &YNode) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl PartialEq for YNode {
    fn eq(&self, other: &YNode) -> bool {
        self.cmp(other) == Ordering::Equal
    }
}
impl Eq for YNode {}<commit_msg>Simplify ordering for Nodes<commit_after>use std::cmp::{Ord, Ordering};
use std::sync::atomic;
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};

/// Each node is given a unique ID.
/// This is necessary because these nodes need to comparable,
/// and pointer comparison might not be valid if the type has zero size and is
/// stored in an optimized container.
type NodeId = u32;

/// Takes two inputs:
///   A1 (a sum of automations)
///   A2 (a sum of automations)
/// Provides one output: A1*A2, which is a sum of automations
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub struct ANode {
    id: NodeId,
}

/// Takes two inputs:
///   Y (a sum of partials)
///   A (a sum of automations)
/// Provides one output: Y*A, which is a sum of partials
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub struct YNode {
    id: NodeId,
}

#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub enum Node {
    ANode(ANode),
    YNode(YNode),
}

/// Used primarily in Sends; Automations can either be sent to the *left* input
/// of an ANode, or to the right input.
#[derive(Clone, Copy, Debug)]
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub enum NodeInputSlot {
    Left,
    Right,
}

static mut prev_node_id : AtomicUsize = ATOMIC_USIZE_INIT;

impl Node {
    fn consume_next_id() -> NodeId {
        let id = unsafe {
            prev_node_id.fetch_add(1, atomic::Ordering::SeqCst)
        };
        id as u32
    }
}

impl ANode {
    pub fn new() -> ANode {
        ANode{ id: Node::consume_next_id() }
    }
}

impl YNode {
    pub fn new() -> YNode {
        YNode{ id: Node::consume_next_id() }
    }
}
<|endoftext|>


<commit_before><commit_msg>More CI fixes<commit_after><|endoftext|>


<commit_before>//! The types modules describes all the structures to express FITS files.

use std::str::FromStr;
use std::fmt::{Display, Formatter, Error};

/// Representation of a FITS file.
#[derive(Debug, PartialEq)]
pub struct Fits<'a> {
    /// The primary header
    pub primary_header: PrimaryHeader<'a>
}

impl<'a> Fits<'a> {
    /// Create a Fits structure with a given primary header
    pub fn new(primary_header: PrimaryHeader<'a>) -> Fits<'a> {
        Fits { primary_header: primary_header }
    }
}

/// The primary header of a FITS file.
#[derive(Debug, PartialEq)]
pub struct PrimaryHeader<'a> {
    /// The keyword records of the primary header.
    pub keyword_records: Vec<KeywordRecord<'a>>,
}

impl<'a> PrimaryHeader<'a> {
    /// Create a PrimaryHeader with a given set of keyword_records
    pub fn new(keyword_records: Vec<KeywordRecord<'a>>) -> PrimaryHeader<'a> {
        PrimaryHeader { keyword_records: keyword_records }
    }
}

/// A keyword record contains information about a FITS header. It consists of a
/// keyword, the corresponding value and an optional comment.
#[derive(Debug, PartialEq)]
pub struct KeywordRecord<'a> {
    /// The keyword of this record.
    keyword: Keyword,
    /// The value of this record.
    value: Value<'a>,
    /// The comment of this record.
    comment: Option<&'a str>
}

impl<'a> KeywordRecord<'a> {
    /// Create a `KeywordRecord` from a specific `Keyword`.
    pub fn new(keyword: Keyword, value: Value<'a>, comment: Option<&'a str>) -> KeywordRecord<'a> {
        KeywordRecord { keyword: keyword, value: value, comment: comment }
    }
}

impl<'a> Display for KeywordRecord<'a> {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        write!(f, "{:?}= {:?}/{}", self.keyword, self.value, self.comment.unwrap_or(""))
    }
}

/// The possible values of a KeywordRecord.
#[derive(Debug, PartialEq)]
pub enum Value<'a> {
    /// A string enclosed in single quotes `'`.
    CharacterString(&'a str),
    /// A logical constant signified by either an uppercase `F` or an uppercase `T`.
    Logical(bool),
    /// An optionally signed decimal integer.
    Integer(i64),
    /// Fixed format real floating point number.
    Real(f64),
    /// Complex number represented by a real and imaginary component.
    Complex((f64, f64)),
    /// When a value is not present
    Undefined,
}

/// A unit struct that will act as a placeholder for blank records.
#[derive(Debug, PartialEq)]
pub struct BlankRecord;

/// The various keywords that can be found in headers.
#[derive(Debug, PartialEq)]
#[allow(non_camel_case_types, missing_docs)]
pub enum Keyword {
    SIMPLE,
    BITPIX,
    NAXIS,
    NAXISn(u8),
    EXTEND,
    NEXTEND,
    EXTNAME,
    EXTVER,
    ORIGIN,
    DATE,
    CREATOR,
    PROCVER,
    FILEVER,
    TIMVERSN,
    TELESCOP,
    INSTRUME,
    OBJECT,
    KEPLERID,
    CHANNEL,
    MODULE,
    OUTPUT,
    CAMPAIGN,
    DATA_REL,
    OBSMODE,
    MISSION,
    TTABLEID,
    RADESYS,
    RA_OBJ,
    DEC_OBJ,
    EQUINOX,
    PMRA,
    PMDEC,
    PMTOTAL,
    PARALLAX,
    GLON,
    GLAT,
    GMAG,
    RMAG,
    IMAG,
    ZMAG,
    JMAG,
    HMAG,
    KMAG,
    KEPMAG,
    GRCOLOR,
    JKCOLOR,
    GKCOLOR,
    TEFF,
    LOGG,
    FEH,
    EBMINUSV,
    AV,
    RADIUS,
    TMINDEX,
    CHECKSUM,
    DATASUM,
    END,
}

/// Problems that could occur when parsing a `str` for a Keyword are enumerated here.
#[derive(Debug)]
pub enum ParseKeywordError {
    /// When a str can not be recognized as a keyword, this error will be returned
    UnknownKeyword
}

impl FromStr for Keyword {
    type Err = ParseKeywordError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // TODO parse NAXIS1 correctly
        match s.trim_right() {
            "SIMPLE" => Ok(Keyword::SIMPLE),
            "BITPIX" => Ok(Keyword::BITPIX),
            "NAXIS" => Ok(Keyword::NAXIS),
            "EXTEND" => Ok(Keyword::EXTEND),
            "NEXTEND" => Ok(Keyword::NEXTEND),
            "EXTNAME" => Ok(Keyword::EXTNAME),
            "EXTVER" => Ok(Keyword::EXTVER),
            "ORIGIN" => Ok(Keyword::ORIGIN),
            "DATE" => Ok(Keyword::DATE),
            "CREATOR" => Ok(Keyword::CREATOR),
            "PROCVER" => Ok(Keyword::PROCVER),
            "FILEVER" => Ok(Keyword::FILEVER),
            "TIMVERSN" => Ok(Keyword::TIMVERSN),
            "TELESCOP" => Ok(Keyword::TELESCOP),
            "INSTRUME" => Ok(Keyword::INSTRUME),
            "OBJECT" => Ok(Keyword::OBJECT),
            "KEPLERID" => Ok(Keyword::KEPLERID),
            "CHANNEL" => Ok(Keyword::CHANNEL),
            "MODULE" => Ok(Keyword::MODULE),
            "OUTPUT" => Ok(Keyword::OUTPUT),
            "CAMPAIGN" => Ok(Keyword::CAMPAIGN),
            "DATA_REL" => Ok(Keyword::DATA_REL),
            "OBSMODE" => Ok(Keyword::OBSMODE),
            "MISSION" => Ok(Keyword::MISSION),
            "TTABLEID" => Ok(Keyword::TTABLEID),
            "RADESYS" => Ok(Keyword::RADESYS),
            "RA_OBJ" => Ok(Keyword::RA_OBJ),
            "DEC_OBJ" => Ok(Keyword::DEC_OBJ),
            "EQUINOX" => Ok(Keyword::EQUINOX),
            "PMRA" => Ok(Keyword::PMRA),
            "PMDEC" => Ok(Keyword::PMDEC),
            "PMTOTAL" => Ok(Keyword::PMTOTAL),
            "PARALLAX" => Ok(Keyword::PARALLAX),
            "GLON" => Ok(Keyword::GLON),
            "GLAT" => Ok(Keyword::GLAT),
            "GMAG" => Ok(Keyword::GMAG),
            "RMAG" => Ok(Keyword::RMAG),
            "IMAG" => Ok(Keyword::IMAG),
            "ZMAG" => Ok(Keyword::ZMAG),
            "JMAG" => Ok(Keyword::JMAG),
            "HMAG" => Ok(Keyword::HMAG),
            "KMAG" => Ok(Keyword::KMAG),
            "KEPMAG" => Ok(Keyword::KEPMAG),
            "GRCOLOR" => Ok(Keyword::GRCOLOR),
            "JKCOLOR" => Ok(Keyword::JKCOLOR),
            "GKCOLOR" => Ok(Keyword::GKCOLOR),
            "TEFF" => Ok(Keyword::TEFF),
            "LOGG" => Ok(Keyword::LOGG),
            "FEH" => Ok(Keyword::FEH),
            "EBMINUSV" => Ok(Keyword::EBMINUSV),
            "AV" => Ok(Keyword::AV),
            "RADIUS" => Ok(Keyword::RADIUS),
            "TMINDEX" => Ok(Keyword::TMINDEX),
            "CHECKSUM" => Ok(Keyword::CHECKSUM),
            "DATASUM" => Ok(Keyword::DATASUM),
            "END" => Ok(Keyword::END),
            _ => Err(ParseKeywordError::UnknownKeyword)
        }
    }
}

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

    #[test]
    fn fits_constructed_from_the_new_function_should_eq_hand_construction() {
        assert_eq!(
            Fits { primary_header: PrimaryHeader::new(vec!()) },
            Fits::new(PrimaryHeader::new(vec!()))
        );
    }

    #[test]
    fn primary_header_constructed_from_the_new_function_shoul_eq_hand_construction() {
        assert_eq!(
            PrimaryHeader { keyword_records: vec!(
                KeywordRecord::new(Keyword::SIMPLE, Value::Logical(true), Option::None),
                KeywordRecord::new(Keyword::NEXTEND, Value::Integer(0i64), Option::Some("no extensions")),
            )},
            PrimaryHeader::new(vec!(
                KeywordRecord::new(Keyword::SIMPLE, Value::Logical(true), Option::None),
                KeywordRecord::new(Keyword::NEXTEND, Value::Integer(0i64), Option::Some("no extensions")),
            ))
        );
    }

    #[test]
    fn keyword_record_constructed_from_the_new_function_should_eq_hand_construction() {
        assert_eq!(
            KeywordRecord { keyword: Keyword::ORIGIN, value: Value::Undefined, comment: Option::None },
            KeywordRecord::new(Keyword::ORIGIN, Value::Undefined, Option::None));
    }

    #[test]
    fn keywords_could_be_constructed_from_str() {
        let data = vec!(
            ("SIMPLE", Keyword::SIMPLE),
            ("BITPIX", Keyword::BITPIX),
            ("NAXIS", Keyword::NAXIS),
            ("EXTEND", Keyword::EXTEND),
            ("NEXTEND", Keyword::NEXTEND),
            ("EXTVER", Keyword::EXTVER),
            ("ORIGIN", Keyword::ORIGIN),
            ("DATE", Keyword::DATE),
            ("CREATOR", Keyword::CREATOR),
            ("PROCVER", Keyword::PROCVER),
            ("FILEVER", Keyword::FILEVER),
            ("TIMVERSN", Keyword::TIMVERSN),
            ("TELESCOP", Keyword::TELESCOP),
            ("INSTRUME", Keyword::INSTRUME),
            ("OBJECT", Keyword::OBJECT),
            ("KEPLERID", Keyword::KEPLERID),
            ("CHANNEL", Keyword::CHANNEL),
            ("MODULE", Keyword::MODULE),
            ("OUTPUT", Keyword::OUTPUT),
            ("CAMPAIGN", Keyword::CAMPAIGN),
            ("DATA_REL", Keyword::DATA_REL),
            ("OBSMODE", Keyword::OBSMODE),
            ("MISSION", Keyword::MISSION),
            ("TTABLEID", Keyword::TTABLEID),
            ("RADESYS", Keyword::RADESYS),
            ("RA_OBJ", Keyword::RA_OBJ),
            ("DEC_OBJ", Keyword::DEC_OBJ),
            ("EQUINOX", Keyword::EQUINOX),
            ("PMRA", Keyword::PMRA),
            ("PMDEC", Keyword::PMDEC),
            ("PMTOTAL", Keyword::PMTOTAL),
            ("PARALLAX", Keyword::PARALLAX),
            ("GLON", Keyword::GLON),
            ("GLAT", Keyword::GLAT),
            ("GMAG", Keyword::GMAG),
            ("RMAG", Keyword::RMAG),
            ("IMAG", Keyword::IMAG),
            ("ZMAG", Keyword::ZMAG),
            ("JMAG", Keyword::JMAG),
            ("HMAG", Keyword::HMAG),
            ("KMAG", Keyword::KMAG),
            ("KEPMAG", Keyword::KEPMAG),
            ("GRCOLOR", Keyword::GRCOLOR),
            ("JKCOLOR", Keyword::JKCOLOR),
            ("GKCOLOR", Keyword::GKCOLOR),
            ("TEFF", Keyword::TEFF),
            ("LOGG", Keyword::LOGG),
            ("FEH", Keyword::FEH),
            ("EBMINUSV", Keyword::EBMINUSV),
            ("AV", Keyword::AV),
            ("RADIUS", Keyword::RADIUS),
            ("TMINDEX", Keyword::TMINDEX),
            ("CHECKSUM", Keyword::CHECKSUM),
            ("DATASUM", Keyword::DATASUM),
            ("END", Keyword::END),
        );

        for (input, expected) in data {
            assert_eq!(Keyword::from_str(input).unwrap(), expected);
        }
    }

    #[test]
    fn should_also_parse_whitespace_keywords() {
        assert_eq!(Keyword::from_str("SIMPLE  ").unwrap(), Keyword::SIMPLE);
    }
}
<commit_msg>Introduce the optional primary data array<commit_after>//! The types modules describes all the structures to express FITS files.

use std::str::FromStr;
use std::fmt::{Display, Formatter, Error};

/// Representation of a FITS file.
#[derive(Debug, PartialEq)]
pub struct Fits<'a> {
    /// The primary header
    pub primary_header: PrimaryHeader<'a>,
    /// The optional primary data array
    pub primary_data_array: Option<DataArray>
}

impl<'a> Fits<'a> {
    /// Create a Fits structure with a given primary header
    pub fn new(primary_header: PrimaryHeader<'a>) -> Fits<'a> {
        Fits {
            primary_header: primary_header,
            primary_data_array: Option::None,
        }
    }
}

/// The primary header of a FITS file.
#[derive(Debug, PartialEq)]
pub struct PrimaryHeader<'a> {
    /// The keyword records of the primary header.
    pub keyword_records: Vec<KeywordRecord<'a>>,
}

impl<'a> PrimaryHeader<'a> {
    /// Create a PrimaryHeader with a given set of keyword_records
    pub fn new(keyword_records: Vec<KeywordRecord<'a>>) -> PrimaryHeader<'a> {
        PrimaryHeader { keyword_records: keyword_records }
    }
}

/// Placeholder for DataArray
#[derive(Debug, PartialEq)]
pub struct DataArray;

/// A keyword record contains information about a FITS header. It consists of a
/// keyword, the corresponding value and an optional comment.
#[derive(Debug, PartialEq)]
pub struct KeywordRecord<'a> {
    /// The keyword of this record.
    keyword: Keyword,
    /// The value of this record.
    value: Value<'a>,
    /// The comment of this record.
    comment: Option<&'a str>
}

impl<'a> KeywordRecord<'a> {
    /// Create a `KeywordRecord` from a specific `Keyword`.
    pub fn new(keyword: Keyword, value: Value<'a>, comment: Option<&'a str>) -> KeywordRecord<'a> {
        KeywordRecord { keyword: keyword, value: value, comment: comment }
    }
}

impl<'a> Display for KeywordRecord<'a> {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        write!(f, "{:?}= {:?}/{}", self.keyword, self.value, self.comment.unwrap_or(""))
    }
}

/// The possible values of a KeywordRecord.
#[derive(Debug, PartialEq)]
pub enum Value<'a> {
    /// A string enclosed in single quotes `'`.
    CharacterString(&'a str),
    /// A logical constant signified by either an uppercase `F` or an uppercase `T`.
    Logical(bool),
    /// An optionally signed decimal integer.
    Integer(i64),
    /// Fixed format real floating point number.
    Real(f64),
    /// Complex number represented by a real and imaginary component.
    Complex((f64, f64)),
    /// When a value is not present
    Undefined,
}

/// A unit struct that will act as a placeholder for blank records.
#[derive(Debug, PartialEq)]
pub struct BlankRecord;

/// The various keywords that can be found in headers.
#[derive(Debug, PartialEq)]
#[allow(non_camel_case_types, missing_docs)]
pub enum Keyword {
    SIMPLE,
    BITPIX,
    NAXIS,
    NAXISn(u8),
    EXTEND,
    NEXTEND,
    EXTNAME,
    EXTVER,
    ORIGIN,
    DATE,
    CREATOR,
    PROCVER,
    FILEVER,
    TIMVERSN,
    TELESCOP,
    INSTRUME,
    OBJECT,
    KEPLERID,
    CHANNEL,
    MODULE,
    OUTPUT,
    CAMPAIGN,
    DATA_REL,
    OBSMODE,
    MISSION,
    TTABLEID,
    RADESYS,
    RA_OBJ,
    DEC_OBJ,
    EQUINOX,
    PMRA,
    PMDEC,
    PMTOTAL,
    PARALLAX,
    GLON,
    GLAT,
    GMAG,
    RMAG,
    IMAG,
    ZMAG,
    JMAG,
    HMAG,
    KMAG,
    KEPMAG,
    GRCOLOR,
    JKCOLOR,
    GKCOLOR,
    TEFF,
    LOGG,
    FEH,
    EBMINUSV,
    AV,
    RADIUS,
    TMINDEX,
    CHECKSUM,
    DATASUM,
    END,
}

/// Problems that could occur when parsing a `str` for a Keyword are enumerated here.
#[derive(Debug)]
pub enum ParseKeywordError {
    /// When a str can not be recognized as a keyword, this error will be returned
    UnknownKeyword
}

impl FromStr for Keyword {
    type Err = ParseKeywordError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // TODO parse NAXIS1 correctly
        match s.trim_right() {
            "SIMPLE" => Ok(Keyword::SIMPLE),
            "BITPIX" => Ok(Keyword::BITPIX),
            "NAXIS" => Ok(Keyword::NAXIS),
            "EXTEND" => Ok(Keyword::EXTEND),
            "NEXTEND" => Ok(Keyword::NEXTEND),
            "EXTNAME" => Ok(Keyword::EXTNAME),
            "EXTVER" => Ok(Keyword::EXTVER),
            "ORIGIN" => Ok(Keyword::ORIGIN),
            "DATE" => Ok(Keyword::DATE),
            "CREATOR" => Ok(Keyword::CREATOR),
            "PROCVER" => Ok(Keyword::PROCVER),
            "FILEVER" => Ok(Keyword::FILEVER),
            "TIMVERSN" => Ok(Keyword::TIMVERSN),
            "TELESCOP" => Ok(Keyword::TELESCOP),
            "INSTRUME" => Ok(Keyword::INSTRUME),
            "OBJECT" => Ok(Keyword::OBJECT),
            "KEPLERID" => Ok(Keyword::KEPLERID),
            "CHANNEL" => Ok(Keyword::CHANNEL),
            "MODULE" => Ok(Keyword::MODULE),
            "OUTPUT" => Ok(Keyword::OUTPUT),
            "CAMPAIGN" => Ok(Keyword::CAMPAIGN),
            "DATA_REL" => Ok(Keyword::DATA_REL),
            "OBSMODE" => Ok(Keyword::OBSMODE),
            "MISSION" => Ok(Keyword::MISSION),
            "TTABLEID" => Ok(Keyword::TTABLEID),
            "RADESYS" => Ok(Keyword::RADESYS),
            "RA_OBJ" => Ok(Keyword::RA_OBJ),
            "DEC_OBJ" => Ok(Keyword::DEC_OBJ),
            "EQUINOX" => Ok(Keyword::EQUINOX),
            "PMRA" => Ok(Keyword::PMRA),
            "PMDEC" => Ok(Keyword::PMDEC),
            "PMTOTAL" => Ok(Keyword::PMTOTAL),
            "PARALLAX" => Ok(Keyword::PARALLAX),
            "GLON" => Ok(Keyword::GLON),
            "GLAT" => Ok(Keyword::GLAT),
            "GMAG" => Ok(Keyword::GMAG),
            "RMAG" => Ok(Keyword::RMAG),
            "IMAG" => Ok(Keyword::IMAG),
            "ZMAG" => Ok(Keyword::ZMAG),
            "JMAG" => Ok(Keyword::JMAG),
            "HMAG" => Ok(Keyword::HMAG),
            "KMAG" => Ok(Keyword::KMAG),
            "KEPMAG" => Ok(Keyword::KEPMAG),
            "GRCOLOR" => Ok(Keyword::GRCOLOR),
            "JKCOLOR" => Ok(Keyword::JKCOLOR),
            "GKCOLOR" => Ok(Keyword::GKCOLOR),
            "TEFF" => Ok(Keyword::TEFF),
            "LOGG" => Ok(Keyword::LOGG),
            "FEH" => Ok(Keyword::FEH),
            "EBMINUSV" => Ok(Keyword::EBMINUSV),
            "AV" => Ok(Keyword::AV),
            "RADIUS" => Ok(Keyword::RADIUS),
            "TMINDEX" => Ok(Keyword::TMINDEX),
            "CHECKSUM" => Ok(Keyword::CHECKSUM),
            "DATASUM" => Ok(Keyword::DATASUM),
            "END" => Ok(Keyword::END),
            _ => Err(ParseKeywordError::UnknownKeyword)
        }
    }
}

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

    #[test]
    fn fits_constructed_from_the_new_function_should_eq_hand_construction() {
        assert_eq!(
            Fits {
                primary_header: PrimaryHeader::new(vec!()),
                primary_data_array: Option::None,
            },
            Fits::new(PrimaryHeader::new(vec!()))
        );
    }

    #[test]
    fn primary_header_constructed_from_the_new_function_shoul_eq_hand_construction() {
        assert_eq!(
            PrimaryHeader { keyword_records: vec!(
                KeywordRecord::new(Keyword::SIMPLE, Value::Logical(true), Option::None),
                KeywordRecord::new(Keyword::NEXTEND, Value::Integer(0i64), Option::Some("no extensions")),
            )},
            PrimaryHeader::new(vec!(
                KeywordRecord::new(Keyword::SIMPLE, Value::Logical(true), Option::None),
                KeywordRecord::new(Keyword::NEXTEND, Value::Integer(0i64), Option::Some("no extensions")),
            ))
        );
    }

    #[test]
    fn keyword_record_constructed_from_the_new_function_should_eq_hand_construction() {
        assert_eq!(
            KeywordRecord { keyword: Keyword::ORIGIN, value: Value::Undefined, comment: Option::None },
            KeywordRecord::new(Keyword::ORIGIN, Value::Undefined, Option::None));
    }

    #[test]
    fn keywords_could_be_constructed_from_str() {
        let data = vec!(
            ("SIMPLE", Keyword::SIMPLE),
            ("BITPIX", Keyword::BITPIX),
            ("NAXIS", Keyword::NAXIS),
            ("EXTEND", Keyword::EXTEND),
            ("NEXTEND", Keyword::NEXTEND),
            ("EXTVER", Keyword::EXTVER),
            ("ORIGIN", Keyword::ORIGIN),
            ("DATE", Keyword::DATE),
            ("CREATOR", Keyword::CREATOR),
            ("PROCVER", Keyword::PROCVER),
            ("FILEVER", Keyword::FILEVER),
            ("TIMVERSN", Keyword::TIMVERSN),
            ("TELESCOP", Keyword::TELESCOP),
            ("INSTRUME", Keyword::INSTRUME),
            ("OBJECT", Keyword::OBJECT),
            ("KEPLERID", Keyword::KEPLERID),
            ("CHANNEL", Keyword::CHANNEL),
            ("MODULE", Keyword::MODULE),
            ("OUTPUT", Keyword::OUTPUT),
            ("CAMPAIGN", Keyword::CAMPAIGN),
            ("DATA_REL", Keyword::DATA_REL),
            ("OBSMODE", Keyword::OBSMODE),
            ("MISSION", Keyword::MISSION),
            ("TTABLEID", Keyword::TTABLEID),
            ("RADESYS", Keyword::RADESYS),
            ("RA_OBJ", Keyword::RA_OBJ),
            ("DEC_OBJ", Keyword::DEC_OBJ),
            ("EQUINOX", Keyword::EQUINOX),
            ("PMRA", Keyword::PMRA),
            ("PMDEC", Keyword::PMDEC),
            ("PMTOTAL", Keyword::PMTOTAL),
            ("PARALLAX", Keyword::PARALLAX),
            ("GLON", Keyword::GLON),
            ("GLAT", Keyword::GLAT),
            ("GMAG", Keyword::GMAG),
            ("RMAG", Keyword::RMAG),
            ("IMAG", Keyword::IMAG),
            ("ZMAG", Keyword::ZMAG),
            ("JMAG", Keyword::JMAG),
            ("HMAG", Keyword::HMAG),
            ("KMAG", Keyword::KMAG),
            ("KEPMAG", Keyword::KEPMAG),
            ("GRCOLOR", Keyword::GRCOLOR),
            ("JKCOLOR", Keyword::JKCOLOR),
            ("GKCOLOR", Keyword::GKCOLOR),
            ("TEFF", Keyword::TEFF),
            ("LOGG", Keyword::LOGG),
            ("FEH", Keyword::FEH),
            ("EBMINUSV", Keyword::EBMINUSV),
            ("AV", Keyword::AV),
            ("RADIUS", Keyword::RADIUS),
            ("TMINDEX", Keyword::TMINDEX),
            ("CHECKSUM", Keyword::CHECKSUM),
            ("DATASUM", Keyword::DATASUM),
            ("END", Keyword::END),
        );

        for (input, expected) in data {
            assert_eq!(Keyword::from_str(input).unwrap(), expected);
        }
    }

    #[test]
    fn should_also_parse_whitespace_keywords() {
        assert_eq!(Keyword::from_str("SIMPLE  ").unwrap(), Keyword::SIMPLE);
    }
}
<|endoftext|>