<commit_before>//! Wrapper for `std::io::Read`.

use std::fmt;
use std::mem;
use std::io;

macro_rules! impl_read_little_endian_integer {
    ($ty:ident, $name:ident, $size:expr) => {
/// Reads a little-endian value and returns it.
        fn $name(&mut self) -> io::Result<$ty> {
            assert_eq!($size, mem::size_of::<$ty>());

            let mut data: $ty = 0;
            let mut slice = unsafe {
                ::std::slice::from_raw_parts_mut(&mut data as *mut $ty as *mut u8, $size)
            };
            self.read_exact(slice)?;
            Ok($ty::from_le(data))
        }
    }
}

macro_rules! impl_read_little_endian_integer_array {
    ($ty:ident, $name:ident, $size:expr) => {
/// Reads a little-endian values into the given buffer.
        fn $name(&mut self, buf: &mut [$ty]) -> io::Result<()> {
            assert_eq!($size, mem::size_of::<$ty>());

            {
                let mut slice = unsafe {
                    ::std::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u8, $size * buf.len())
                };
                self.read_exact(slice)?;
            }
            for elem in buf {
                *elem = $ty::from_le(*elem);
            }
            Ok(())
        }
    }
}

/// The `ReadLittleEndian` trait allows for reading little-endian primitive type values from a
/// source.
pub trait ReadLittleEndian: io::Read {
    impl_read_little_endian_integer!(u8, read_u8, 1);
    impl_read_little_endian_integer!(u32, read_u32, 4);
    impl_read_little_endian_integer!(u64, read_u64, 8);
    impl_read_little_endian_integer!(i16, read_i16, 2);
    impl_read_little_endian_integer!(i32, read_i32, 4);
    impl_read_little_endian_integer!(i64, read_i64, 8);

    /// Reads a little-endian value and returns it.
    fn read_f32(&mut self) -> io::Result<f32> {
        let integer = self.read_u32()?;
        let val = unsafe { mem::transmute(integer) };
        Ok(val)
    }

    /// Reads a little-endian value and returns it.
    fn read_f64(&mut self) -> io::Result<f64> {
        let integer = self.read_u64()?;
        let val = unsafe { mem::transmute(integer) };
        Ok(val)
    }

    impl_read_little_endian_integer_array!(u8, read_u8_arr, 1);
    impl_read_little_endian_integer_array!(u32, read_u32_arr, 4);
    impl_read_little_endian_integer_array!(u64, read_u64_arr, 8);
    impl_read_little_endian_integer_array!(i16, read_i16_arr, 2);
    impl_read_little_endian_integer_array!(i32, read_i32_arr, 4);
    impl_read_little_endian_integer_array!(i64, read_i64_arr, 8);

    /// Reads a little-endian values into the given buffer.
    fn read_f32_arr(&mut self, buf: &mut [f32]) -> io::Result<()> {
        self.read_u32_arr(unsafe { mem::transmute(buf) })
    }

    /// Reads a little-endian values into the given buffer.
    fn read_f64_arr(&mut self, buf: &mut [f64]) -> io::Result<()> {
        self.read_u64_arr(unsafe { mem::transmute(buf) })
    }
}

impl<R: io::Read> ReadLittleEndian for R {}

/// Source stream for `BinaryParser`.
pub trait ParserSource: fmt::Debug + io::Read {
    /// Returns the current position from the start of the stream.
    fn position(&self) -> u64;

    /// Skips to the given position.
    ///
    /// # Panics
    /// Panics if a byte at the given position has been already read.
    fn skip_to(&mut self, dest_pos: u64) -> io::Result<()>;
}

/// Reader with position info.
pub struct BasicSource<R> {
    /// Source reader.
    source: R,
    /// Current position from the start of the stream..
    position: u64,
}

impl<R: io::Read> BasicSource<R> {
    /// Creates a new `BasicSource`.
    pub fn new(source: R) -> Self {
        BasicSource {
            source: source,
            position: 0,
        }
    }
}

impl<R: io::Read> io::Read for BasicSource<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let read_len = self.source.read(buf)?;
        self.position += read_len as u64;
        Ok(read_len)
    }

    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
        self.source.read_exact(buf)?;
        self.position += buf.len() as u64;
        Ok(())
    }
}

impl<R> fmt::Debug for BasicSource<R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("BasicSource")
            .field("position", &self.position)
            .finish()
    }
}

impl<R: io::Read> ParserSource for BasicSource<R> {
    fn position(&self) -> u64 {
        self.position
    }

    fn skip_to(&mut self, dest_pos: u64) -> io::Result<()> {
        use std::io::Read;

        assert!(dest_pos >= self.position(),
                "Destination position should be after current position: dest_pos={}, position={}",
                dest_pos,
                self.position());
        const TEMP_BUF_LEN: usize = 256;
        let mut temp_buf = [0u8; TEMP_BUF_LEN];
        let mut rest_len = dest_pos - self.position();
        while rest_len > TEMP_BUF_LEN as u64 {
            self.read_exact(&mut temp_buf)?;
            rest_len -= TEMP_BUF_LEN as u64;
        }
        self.read_exact(&mut temp_buf[0..rest_len as usize])?;

        assert_eq!(self.position(), dest_pos);
        Ok(())
    }
}

/// Reader with position info and seek feature.
///
/// This wrapper doesn't manage offset, i.e. the start of the source stream should be the start of
/// the FBX data.
pub struct SeekableSource<R> {
    /// Source reader.
    source: R,
    /// Current position from the start of the stream..
    position: u64,
}

impl<R: io::Read + io::Seek> SeekableSource<R> {
    /// Creates a new `SeekableSource`.
    pub fn new(source: R) -> Self {
        SeekableSource {
            source: source,
            position: 0,
        }
    }
}

impl<R: io::Read> io::Read for SeekableSource<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let read_len = self.source.read(buf)?;
        self.position += read_len as u64;
        Ok(read_len)
    }

    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
        self.source.read_exact(buf)?;
        self.position += buf.len() as u64;
        Ok(())
    }
}

impl<R: io::Read + io::Seek> io::Seek for SeekableSource<R> {
    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
        self.position = self.source.seek(pos)?;
        Ok(self.position)
    }
}

impl<R> fmt::Debug for SeekableSource<R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("SeekableSource")
            .field("position", &self.position)
            .finish()
    }
}

impl<R: io::Read + io::Seek> ParserSource for SeekableSource<R> {
    fn position(&self) -> u64 {
        self.position
    }

    fn skip_to(&mut self, dest_pos: u64) -> io::Result<()> {
        use std::io::{Seek, SeekFrom};

        assert!(dest_pos >= self.position(),
                "Destination position should be after current position: dest_pos={}, position={}",
                dest_pos,
                self.position());
        self.seek(SeekFrom::Start(dest_pos))?;

        assert_eq!(self.position(), dest_pos);
        Ok(())
    }
}

/// Reader which can read and seek limited area of a stream.
pub struct LimitedSeekReader<R> {
    /// Source stream.
    source: R,
    /// Current position
    current: u64,
    /// Start position.
    begin: u64,
    /// End position.
    end: u64,
}

impl<R: io::Read> LimitedSeekReader<R> {
    /// Creates a new `LimitedSeekReader`.
    pub fn new(source: R, current: u64, begin: u64, end: u64) -> Self {
        assert!(begin <= end);
        assert!(begin <= current);
        assert!(current <= end);

        LimitedSeekReader {
            source: source,
            current: current,
            begin: begin,
            end: end,
        }
    }
}

impl<R> LimitedSeekReader<R> {
    /// Returns length of region allowed to be read in bytes.
    pub fn len(&self) -> u64 {
        assert!(self.begin <= self.end);
        self.end - self.begin
    }

    /// Returns `true` if the reader can read no data.
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Returns rest length of region allowed to be read in bytes.
    fn rest_len(&self) -> u64 {
        assert!(self.current <= self.end);
        self.end - self.current
    }

    /// Returns the current position relative to the start of the limited region.
    fn rel_pos(&self) -> u64 {
        assert!(self.begin <= self.current);
        self.current - self.begin
    }
}

impl<R: io::Read> io::Read for LimitedSeekReader<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let limit = self.rest_len();
        let size = self.source.by_ref().take(limit).read(buf)?;
        self.current += size as u64;
        Ok(size)
    }
}

impl<R: io::Seek> io::Seek for LimitedSeekReader<R> {
    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
        match pos {
            io::SeekFrom::Start(val) => {
                let offset = ::std::cmp::min(self.len(), val);
                let target = self.begin + offset;
                assert!(target >= self.begin);
                assert!(target <= self.end);
                self.current = self.source.seek(io::SeekFrom::Start(target))?;
            },
            io::SeekFrom::Current(val) => {
                let offset = if val < 0 {
                    ::std::cmp::max(-(self.rel_pos() as i64), val)
                } else {
                    ::std::cmp::min(self.rest_len(), val as u64) as i64
                };
                let target = self.current as i64 + offset;
                assert!(target as u64 >= self.begin);
                assert!(target as u64 <= self.end);
                self.current = self.source.seek(io::SeekFrom::Current(offset))?;
            },
            io::SeekFrom::End(val) => {
                let offset = ::std::cmp::max(-(self.len() as i64), ::std::cmp::min(0, val));
                let target = (self.end as i64 + offset) as u64;
                assert!(target >= self.begin);
                assert!(target <= self.end);
                self.current = self.source.seek(io::SeekFrom::Start(target))?;
            },
        }
        Ok(self.rel_pos())
    }
}

impl<R> fmt::Debug for LimitedSeekReader<R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("LimitedSeekReader")
            .field("current", &self.current)
            .field("begin", &self.begin)
            .field("end", &self.end)
            .finish()
    }
}

#[cfg(test)]
mod tests {
    use std::io::{Cursor, Seek, SeekFrom};
    use super::{ParserSource, BasicSource};

    fn do_test_skip_to(buf_size: usize, skip_dest: u64) {
        let mut short_buf = Cursor::new(vec![0; buf_size]);
        let short_count = {
            let mut reader = BasicSource::new(&mut short_buf);
            reader.skip_to(skip_dest).expect("Failed to skip");
            reader.position()
        };
        assert_eq!(short_count, skip_dest);
        assert_eq!(short_count,
                   short_buf.seek(SeekFrom::Current(0)).expect("Failed to seek"));
    }

    #[test]
    fn test_skip_to() {
        do_test_skip_to(0, 0);
        do_test_skip_to(15, 0);
        do_test_skip_to(30, 23);
        do_test_skip_to(512, 401);
        do_test_skip_to(64, 64);
    }
}
<commit_msg>Add test for `SeekableSource`<commit_after>//! Wrapper for `std::io::Read`.

use std::fmt;
use std::mem;
use std::io;

macro_rules! impl_read_little_endian_integer {
    ($ty:ident, $name:ident, $size:expr) => {
/// Reads a little-endian value and returns it.
        fn $name(&mut self) -> io::Result<$ty> {
            assert_eq!($size, mem::size_of::<$ty>());

            let mut data: $ty = 0;
            let mut slice = unsafe {
                ::std::slice::from_raw_parts_mut(&mut data as *mut $ty as *mut u8, $size)
            };
            self.read_exact(slice)?;
            Ok($ty::from_le(data))
        }
    }
}

macro_rules! impl_read_little_endian_integer_array {
    ($ty:ident, $name:ident, $size:expr) => {
/// Reads a little-endian values into the given buffer.
        fn $name(&mut self, buf: &mut [$ty]) -> io::Result<()> {
            assert_eq!($size, mem::size_of::<$ty>());

            {
                let mut slice = unsafe {
                    ::std::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u8, $size * buf.len())
                };
                self.read_exact(slice)?;
            }
            for elem in buf {
                *elem = $ty::from_le(*elem);
            }
            Ok(())
        }
    }
}

/// The `ReadLittleEndian` trait allows for reading little-endian primitive type values from a
/// source.
pub trait ReadLittleEndian: io::Read {
    impl_read_little_endian_integer!(u8, read_u8, 1);
    impl_read_little_endian_integer!(u32, read_u32, 4);
    impl_read_little_endian_integer!(u64, read_u64, 8);
    impl_read_little_endian_integer!(i16, read_i16, 2);
    impl_read_little_endian_integer!(i32, read_i32, 4);
    impl_read_little_endian_integer!(i64, read_i64, 8);

    /// Reads a little-endian value and returns it.
    fn read_f32(&mut self) -> io::Result<f32> {
        let integer = self.read_u32()?;
        let val = unsafe { mem::transmute(integer) };
        Ok(val)
    }

    /// Reads a little-endian value and returns it.
    fn read_f64(&mut self) -> io::Result<f64> {
        let integer = self.read_u64()?;
        let val = unsafe { mem::transmute(integer) };
        Ok(val)
    }

    impl_read_little_endian_integer_array!(u8, read_u8_arr, 1);
    impl_read_little_endian_integer_array!(u32, read_u32_arr, 4);
    impl_read_little_endian_integer_array!(u64, read_u64_arr, 8);
    impl_read_little_endian_integer_array!(i16, read_i16_arr, 2);
    impl_read_little_endian_integer_array!(i32, read_i32_arr, 4);
    impl_read_little_endian_integer_array!(i64, read_i64_arr, 8);

    /// Reads a little-endian values into the given buffer.
    fn read_f32_arr(&mut self, buf: &mut [f32]) -> io::Result<()> {
        self.read_u32_arr(unsafe { mem::transmute(buf) })
    }

    /// Reads a little-endian values into the given buffer.
    fn read_f64_arr(&mut self, buf: &mut [f64]) -> io::Result<()> {
        self.read_u64_arr(unsafe { mem::transmute(buf) })
    }
}

impl<R: io::Read> ReadLittleEndian for R {}

/// Source stream for `BinaryParser`.
pub trait ParserSource: fmt::Debug + io::Read {
    /// Returns the current position from the start of the stream.
    fn position(&self) -> u64;

    /// Skips to the given position.
    ///
    /// # Panics
    /// Panics if a byte at the given position has been already read.
    fn skip_to(&mut self, dest_pos: u64) -> io::Result<()>;
}

/// Reader with position info.
pub struct BasicSource<R> {
    /// Source reader.
    source: R,
    /// Current position from the start of the stream..
    position: u64,
}

impl<R: io::Read> BasicSource<R> {
    /// Creates a new `BasicSource`.
    pub fn new(source: R) -> Self {
        BasicSource {
            source: source,
            position: 0,
        }
    }
}

impl<R: io::Read> io::Read for BasicSource<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let read_len = self.source.read(buf)?;
        self.position += read_len as u64;
        Ok(read_len)
    }

    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
        self.source.read_exact(buf)?;
        self.position += buf.len() as u64;
        Ok(())
    }
}

impl<R> fmt::Debug for BasicSource<R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("BasicSource")
            .field("position", &self.position)
            .finish()
    }
}

impl<R: io::Read> ParserSource for BasicSource<R> {
    fn position(&self) -> u64 {
        self.position
    }

    fn skip_to(&mut self, dest_pos: u64) -> io::Result<()> {
        use std::io::Read;

        assert!(dest_pos >= self.position(),
                "Destination position should be after current position: dest_pos={}, position={}",
                dest_pos,
                self.position());
        const TEMP_BUF_LEN: usize = 256;
        let mut temp_buf = [0u8; TEMP_BUF_LEN];
        let mut rest_len = dest_pos - self.position();
        while rest_len > TEMP_BUF_LEN as u64 {
            self.read_exact(&mut temp_buf)?;
            rest_len -= TEMP_BUF_LEN as u64;
        }
        self.read_exact(&mut temp_buf[0..rest_len as usize])?;

        assert_eq!(self.position(), dest_pos);
        Ok(())
    }
}

/// Reader with position info and seek feature.
///
/// This wrapper doesn't manage offset, i.e. the start of the source stream should be the start of
/// the FBX data.
pub struct SeekableSource<R> {
    /// Source reader.
    source: R,
    /// Current position from the start of the stream..
    position: u64,
}

impl<R: io::Read + io::Seek> SeekableSource<R> {
    /// Creates a new `SeekableSource`.
    pub fn new(source: R) -> Self {
        SeekableSource {
            source: source,
            position: 0,
        }
    }
}

impl<R: io::Read> io::Read for SeekableSource<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let read_len = self.source.read(buf)?;
        self.position += read_len as u64;
        Ok(read_len)
    }

    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
        self.source.read_exact(buf)?;
        self.position += buf.len() as u64;
        Ok(())
    }
}

impl<R: io::Read + io::Seek> io::Seek for SeekableSource<R> {
    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
        self.position = self.source.seek(pos)?;
        Ok(self.position)
    }
}

impl<R> fmt::Debug for SeekableSource<R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("SeekableSource")
            .field("position", &self.position)
            .finish()
    }
}

impl<R: io::Read + io::Seek> ParserSource for SeekableSource<R> {
    fn position(&self) -> u64 {
        self.position
    }

    fn skip_to(&mut self, dest_pos: u64) -> io::Result<()> {
        use std::io::{Seek, SeekFrom};

        assert!(dest_pos >= self.position(),
                "Destination position should be after current position: dest_pos={}, position={}",
                dest_pos,
                self.position());
        self.seek(SeekFrom::Start(dest_pos))?;

        assert_eq!(self.position(), dest_pos);
        Ok(())
    }
}

/// Reader which can read and seek limited area of a stream.
pub struct LimitedSeekReader<R> {
    /// Source stream.
    source: R,
    /// Current position
    current: u64,
    /// Start position.
    begin: u64,
    /// End position.
    end: u64,
}

impl<R: io::Read> LimitedSeekReader<R> {
    /// Creates a new `LimitedSeekReader`.
    pub fn new(source: R, current: u64, begin: u64, end: u64) -> Self {
        assert!(begin <= end);
        assert!(begin <= current);
        assert!(current <= end);

        LimitedSeekReader {
            source: source,
            current: current,
            begin: begin,
            end: end,
        }
    }
}

impl<R> LimitedSeekReader<R> {
    /// Returns length of region allowed to be read in bytes.
    pub fn len(&self) -> u64 {
        assert!(self.begin <= self.end);
        self.end - self.begin
    }

    /// Returns `true` if the reader can read no data.
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Returns rest length of region allowed to be read in bytes.
    fn rest_len(&self) -> u64 {
        assert!(self.current <= self.end);
        self.end - self.current
    }

    /// Returns the current position relative to the start of the limited region.
    fn rel_pos(&self) -> u64 {
        assert!(self.begin <= self.current);
        self.current - self.begin
    }
}

impl<R: io::Read> io::Read for LimitedSeekReader<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let limit = self.rest_len();
        let size = self.source.by_ref().take(limit).read(buf)?;
        self.current += size as u64;
        Ok(size)
    }
}

impl<R: io::Seek> io::Seek for LimitedSeekReader<R> {
    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
        match pos {
            io::SeekFrom::Start(val) => {
                let offset = ::std::cmp::min(self.len(), val);
                let target = self.begin + offset;
                assert!(target >= self.begin);
                assert!(target <= self.end);
                self.current = self.source.seek(io::SeekFrom::Start(target))?;
            },
            io::SeekFrom::Current(val) => {
                let offset = if val < 0 {
                    ::std::cmp::max(-(self.rel_pos() as i64), val)
                } else {
                    ::std::cmp::min(self.rest_len(), val as u64) as i64
                };
                let target = self.current as i64 + offset;
                assert!(target as u64 >= self.begin);
                assert!(target as u64 <= self.end);
                self.current = self.source.seek(io::SeekFrom::Current(offset))?;
            },
            io::SeekFrom::End(val) => {
                let offset = ::std::cmp::max(-(self.len() as i64), ::std::cmp::min(0, val));
                let target = (self.end as i64 + offset) as u64;
                assert!(target >= self.begin);
                assert!(target <= self.end);
                self.current = self.source.seek(io::SeekFrom::Start(target))?;
            },
        }
        Ok(self.rel_pos())
    }
}

impl<R> fmt::Debug for LimitedSeekReader<R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("LimitedSeekReader")
            .field("current", &self.current)
            .field("begin", &self.begin)
            .field("end", &self.end)
            .finish()
    }
}

#[cfg(test)]
mod tests {
    use std::io::{Cursor, Seek, SeekFrom};
    use super::{ParserSource, BasicSource, SeekableSource};

    fn do_test_skip_to(buf_size: usize, skip_dest: u64) {
        do_test_basic_skip_to(buf_size, skip_dest);
        do_test_seekable_skip_to(buf_size, skip_dest);
    }

    fn do_test_basic_skip_to(buf_size: usize, skip_dest: u64) {
        let mut short_buf = Cursor::new(vec![0; buf_size]);
        let short_count = {
            let mut reader = BasicSource::new(&mut short_buf);
            reader.skip_to(skip_dest).expect("Failed to skip");
            reader.position()
        };
        assert_eq!(short_count, skip_dest);
        assert_eq!(short_count,
                   short_buf.seek(SeekFrom::Current(0)).expect("Failed to seek"));
    }

    fn do_test_seekable_skip_to(buf_size: usize, skip_dest: u64) {
        let mut short_buf = Cursor::new(vec![0; buf_size]);
        let mut reader = SeekableSource::new(&mut short_buf);
        reader.skip_to(skip_dest).expect("Failed to skip");
        assert_eq!(reader.position(), skip_dest);
        reader.seek(SeekFrom::End(0)).expect("Failed to seek");;
        assert_eq!(reader.position(), buf_size as u64);
        reader.seek(SeekFrom::Start(0)).expect("Failed to seek");
        assert_eq!(reader.position(), 0);
    }

    #[test]
    fn test_skip_to() {
        do_test_skip_to(0, 0);
        do_test_skip_to(15, 0);
        do_test_skip_to(30, 23);
        do_test_skip_to(512, 401);
        do_test_skip_to(64, 64);
    }
}
<|endoftext|>


<commit_before><commit_msg>added vec4, mat4 implementations<commit_after><|endoftext|>


<commit_before>use amq_protocol::frame::{GenError, Offset, gen_frame, parse_frame};
use log::{error, trace};
use mio::{Evented, Events, Poll, PollOpt, Ready, Registration, SetReadiness, Token};
use parking_lot::Mutex;

use std::{
  io::{self, Read, Write},
  sync::{
    Arc,
    atomic::{AtomicBool, Ordering},
  },
  thread::{self, Builder as ThreadBuilder, JoinHandle},
  time::{Duration, Instant},
};

use crate::{
  buffer::Buffer,
  connection::Connection,
  connection_status::ConnectionState,
  error::{Error, ErrorKind},
};

const SOCKET:   Token = Token(0);
const DATA:     Token = Token(1);
const CONTINUE: Token = Token(2);

const FRAMES_STORAGE: usize = 32;

pub struct IoLoop<T> {
  inner: Arc<Mutex<Inner<T>>>,
}

impl<T: Evented + Read + Write + Send + 'static> IoLoop<T> {
  pub fn new(connection: Connection, socket: T) -> Result<Self, Error> {
    let inner = Arc::new(Mutex::new(Inner::new(connection, socket)?));
    Ok(Self { inner })
  }

  pub fn run(&self) -> Result<(), Error> {
    let inner  = self.inner.clone();
    let handle = ThreadBuilder::new().name("io_loop".to_owned()).spawn(move || {
      let mut events = Events::with_capacity(1024);
      loop {
        inner.lock().run(&mut events)?;
      }
    }).map_err(ErrorKind::IOError)?;
    self.inner.lock().handle = Some(handle);
    Ok(())
  }
}

#[derive(PartialEq)]
enum Status {
  Initial,
  Setup,
}

struct Inner<T> {
  connection:     Connection,
  socket:         T,
  status:         Status,
  poll:           Poll,
  registration:   Registration,
  set_readiness:  SetReadiness,
  handle:         Option<JoinHandle<Result<(), Error>>>,
  hb_handle:      Option<JoinHandle<()>>,
  frame_size:     usize,
  receive_buffer: Buffer,
  send_buffer:    Buffer,
  can_write:      bool,
  can_read:       bool,
  has_data:       bool,
  send_heartbeat: Arc<AtomicBool>,
}

impl<T> Drop for Inner<T> {
  fn drop(&mut self) {
    if let Some(handle) = self.handle.take() {
      handle.join().expect("io loop failed").expect("io loop failed");
    }
    if let Some(hb_handle) = self.hb_handle.take() {
      hb_handle.join().expect("heartbeat loop failed");
    }
  }
}

impl<T: Evented + Read + Write + Send + 'static> Inner<T> {
  fn new(connection: Connection, socket: T) -> Result<Self, Error> {
    let frame_size = std::cmp::max(8192, connection.configuration.frame_max() as usize);
    let (registration, set_readiness) = Registration::new2();
    let inner = Self {
      connection,
      socket,
      status:         Status::Initial,
      poll:           Poll::new().map_err(ErrorKind::IOError)?,
      registration,
      set_readiness,
      handle:         None,
      hb_handle:      None,
      frame_size,
      receive_buffer: Buffer::with_capacity(FRAMES_STORAGE * frame_size),
      send_buffer:    Buffer::with_capacity(FRAMES_STORAGE * frame_size),
      can_write:      false,
      can_read:       false,
      has_data:       false,
      send_heartbeat: Arc::new(AtomicBool::new(false)),
    };
    inner.poll.register(&inner.socket, SOCKET, Ready::readable() | Ready::writable(), PollOpt::edge()).map_err(ErrorKind::IOError)?;
    inner.poll.register(&inner.connection, DATA, Ready::readable(), PollOpt::edge()).map_err(ErrorKind::IOError)?;
    inner.poll.register(&inner.registration, CONTINUE, Ready::readable(), PollOpt::edge()).map_err(ErrorKind::IOError)?;
    Ok(inner)
  }

  pub fn start_heartbeat(&mut self, interval: Duration) -> Result<(), Error> {
    let send_hartbeat = self.send_heartbeat.clone();
    let hb_handle = ThreadBuilder::new().name("heartbeat".to_owned()).spawn(move || {
      loop {
        let start         = Instant::now();
        let mut remaining = interval;

        loop {
          thread::park_timeout(remaining);
          let elapsed = start.elapsed();
          if elapsed >= remaining {
            break
          }
          remaining -= interval - elapsed;
        }

        send_hartbeat.store(true, Ordering::Relaxed);
      }
    }).map_err(ErrorKind::IOError)?;
    self.hb_handle = Some(hb_handle);
    Ok(())
  }

  fn heartbeat(&mut self) -> Result<(), Error> {
    self.connection.send_heartbeat()?;
    self.send_heartbeat.store(false, Ordering::Relaxed);
    Ok(())
  }

  fn ensure_setup(&mut self) -> Result<(), Error> {
    if self.status != Status::Setup && self.connection.status.connected() {
      let frame_max = self.connection.configuration.frame_max() as usize;
      self.frame_size = std::cmp::max(self.frame_size, frame_max);
      self.receive_buffer.grow(FRAMES_STORAGE * self.frame_size);
      self.send_buffer.grow(FRAMES_STORAGE * self.frame_size);
      let heartbeat = self.connection.configuration.heartbeat();
      if heartbeat != 0 {
        trace!("io_loop: start heartbeat");
        self.start_heartbeat(Duration::from_secs(heartbeat as u64))?;
        trace!("io_loop: heartbeat started");
      }
      self.status = Status::Setup;
    }
    Ok(())
  }

  fn wants_to_write(&self) -> bool {
    self.can_write && self.has_data
  }

  fn wants_to_read(&self) -> bool {
    self.can_read
  }

  fn do_run(&mut self) -> Result<(), Error> {
    trace!("io_loop do_run; can_read={}, can_write={}, has_data={}", self.can_read, self.can_write, self.has_data);
    loop {
      if self.send_heartbeat.load(Ordering::Relaxed) {
        self.heartbeat()?;
      }
      if self.wants_to_write() && !self.connection.status.blocked() {
        if let Err(e) = self.write_to_stream() {
          match e.kind() {
            ErrorKind::IOError(e) if e.kind() == io::ErrorKind::WouldBlock => self.can_write = false,
            _ => {
              error!("error writing: {:?}", e);
              self.connection.set_error()?;
              return Err(e);
            }
          }
        }
        self.send_buffer.shift_unless_available(self.frame_size);
      }
      if self.wants_to_read() {
        if let Err(e) = self.read_from_stream() {
          match e.kind() {
            ErrorKind::IOError(e) if e.kind() == io::ErrorKind::WouldBlock => self.can_read = false,
            _ => {
              error!("error reading: {:?}", e);
              self.connection.set_error()?;
              return Err(e);
            }
          }
        }
        self.receive_buffer.shift_unless_available(self.frame_size);
      }
      if self.can_parse() {
        self.parse()?;
      }
      if !self.can_read || !self.can_write {
        if self.wants_to_read() || self.can_parse() || self.has_data {
          trace!("io_loop send continue; can_read={}, can_write={}, has_data={}", self.can_read, self.can_write, self.has_data);
          self.set_readiness.set_readiness(Ready::readable()).map_err(ErrorKind::IOError)?;
        }
        break;
      }
    }
    trace!("io_loop do_run done; can_read={}, can_write={}, has_data={}", self.can_read, self.can_write, self.has_data);
    Ok(())
  }

  fn run(&mut self, events: &mut Events) -> Result<(), Error> {
    trace!("io_loop run");
    self.ensure_setup()?;
    trace!("io_loop poll");
    self.poll.poll(events, None).map_err(ErrorKind::IOError)?;
    trace!("io_loop poll done");
    for event in events.iter() {
      match event.token() {
        SOCKET    => {
          if event.readiness().is_readable() {
            self.can_read = true;
          }
          if event.readiness().is_writable() {
            self.can_write = true;
          }
        },
        DATA      => self.has_data = true,
        _         => {},
      }
    }
    self.do_run()
  }

  fn can_parse(&self) -> bool {
    self.receive_buffer.available_data() > 0
  }

  fn write_to_stream(&mut self) -> Result<(), Error> {
    self.serialize()?;

    self.socket.write(&self.send_buffer.data()).map(|sz| {
      trace!("wrote {} bytes", sz);
      self.send_buffer.consume(sz);
    }).map_err(|e| ErrorKind::IOError(e).into())
  }

  fn read_from_stream(&mut self) -> Result<(), Error> {
    let state = self.connection.status.state();
    if state == ConnectionState::Initial || state == ConnectionState::Closed || state == ConnectionState::Error {
      self.connection.set_error()?;
      Err(ErrorKind::InvalidConnectionState(state).into())
    } else {
      self.socket.read(&mut self.receive_buffer.space()).map(|sz| {
        trace!("read {} bytes", sz);
        self.receive_buffer.fill(sz);
      }).map_err(|e| ErrorKind::IOError(e).into())
    }
  }

  fn serialize(&mut self) -> Result<(), Error> {
    if let Some((send_id, next_msg)) = self.connection.next_frame() {
      trace!("will write to buffer: {:?}", next_msg);
      match gen_frame(self.send_buffer.space(), &next_msg).map(|tup| tup.0) {
        Ok(sz) => {
          self.send_buffer.fill(sz);
          self.connection.mark_sent(send_id);
          Ok(())
        },
        Err(e) => {
          match e {
            GenError::BufferTooSmall(_) => {
              // Requeue msg
              self.connection.requeue_frame(send_id, next_msg)?;
              self.send_buffer.shift();
              Ok(())
            },
            GenError::InvalidOffset | GenError::CustomError(_) | GenError::NotYetImplemented => {
              error!("error generating frame: {:?}", e);
              self.connection.set_error()?;
              Err(ErrorKind::SerialisationError(e).into())
            }
          }
        }
      }
    } else {
      self.has_data = false;
      Ok(())
    }
  }

  fn parse(&mut self) -> Result<(), Error> {
    match parse_frame(self.receive_buffer.data()) {
      Ok((i, f)) => {

        let consumed = self.receive_buffer.data().offset(i);
        self.receive_buffer.consume(consumed);

        if let Err(e) = self.connection.handle_frame(f) {
          self.connection.set_error()?;
          Err(e)
        } else {
          Ok(())
        }
      },
      Err(e) => {
        if e.is_incomplete() {
          self.receive_buffer.shift();
          Ok(())
        } else {
          error!("parse error: {:?}", e);
          self.connection.set_error()?;
          Err(ErrorKind::ParsingError(format!("{:?}", e)).into())
        }
      }
    }
  }
}
<commit_msg>io_loop: update condition<commit_after>use amq_protocol::frame::{GenError, Offset, gen_frame, parse_frame};
use log::{error, trace};
use mio::{Evented, Events, Poll, PollOpt, Ready, Registration, SetReadiness, Token};
use parking_lot::Mutex;

use std::{
  io::{self, Read, Write},
  sync::{
    Arc,
    atomic::{AtomicBool, Ordering},
  },
  thread::{self, Builder as ThreadBuilder, JoinHandle},
  time::{Duration, Instant},
};

use crate::{
  buffer::Buffer,
  connection::Connection,
  connection_status::ConnectionState,
  error::{Error, ErrorKind},
};

const SOCKET:   Token = Token(0);
const DATA:     Token = Token(1);
const CONTINUE: Token = Token(2);

const FRAMES_STORAGE: usize = 32;

pub struct IoLoop<T> {
  inner: Arc<Mutex<Inner<T>>>,
}

impl<T: Evented + Read + Write + Send + 'static> IoLoop<T> {
  pub fn new(connection: Connection, socket: T) -> Result<Self, Error> {
    let inner = Arc::new(Mutex::new(Inner::new(connection, socket)?));
    Ok(Self { inner })
  }

  pub fn run(&self) -> Result<(), Error> {
    let inner  = self.inner.clone();
    let handle = ThreadBuilder::new().name("io_loop".to_owned()).spawn(move || {
      let mut events = Events::with_capacity(1024);
      loop {
        inner.lock().run(&mut events)?;
      }
    }).map_err(ErrorKind::IOError)?;
    self.inner.lock().handle = Some(handle);
    Ok(())
  }
}

#[derive(PartialEq)]
enum Status {
  Initial,
  Setup,
}

struct Inner<T> {
  connection:     Connection,
  socket:         T,
  status:         Status,
  poll:           Poll,
  registration:   Registration,
  set_readiness:  SetReadiness,
  handle:         Option<JoinHandle<Result<(), Error>>>,
  hb_handle:      Option<JoinHandle<()>>,
  frame_size:     usize,
  receive_buffer: Buffer,
  send_buffer:    Buffer,
  can_write:      bool,
  can_read:       bool,
  has_data:       bool,
  send_heartbeat: Arc<AtomicBool>,
}

impl<T> Drop for Inner<T> {
  fn drop(&mut self) {
    if let Some(handle) = self.handle.take() {
      handle.join().expect("io loop failed").expect("io loop failed");
    }
    if let Some(hb_handle) = self.hb_handle.take() {
      hb_handle.join().expect("heartbeat loop failed");
    }
  }
}

impl<T: Evented + Read + Write + Send + 'static> Inner<T> {
  fn new(connection: Connection, socket: T) -> Result<Self, Error> {
    let frame_size = std::cmp::max(8192, connection.configuration.frame_max() as usize);
    let (registration, set_readiness) = Registration::new2();
    let inner = Self {
      connection,
      socket,
      status:         Status::Initial,
      poll:           Poll::new().map_err(ErrorKind::IOError)?,
      registration,
      set_readiness,
      handle:         None,
      hb_handle:      None,
      frame_size,
      receive_buffer: Buffer::with_capacity(FRAMES_STORAGE * frame_size),
      send_buffer:    Buffer::with_capacity(FRAMES_STORAGE * frame_size),
      can_write:      false,
      can_read:       false,
      has_data:       false,
      send_heartbeat: Arc::new(AtomicBool::new(false)),
    };
    inner.poll.register(&inner.socket, SOCKET, Ready::readable() | Ready::writable(), PollOpt::edge()).map_err(ErrorKind::IOError)?;
    inner.poll.register(&inner.connection, DATA, Ready::readable(), PollOpt::edge()).map_err(ErrorKind::IOError)?;
    inner.poll.register(&inner.registration, CONTINUE, Ready::readable(), PollOpt::edge()).map_err(ErrorKind::IOError)?;
    Ok(inner)
  }

  pub fn start_heartbeat(&mut self, interval: Duration) -> Result<(), Error> {
    let send_hartbeat = self.send_heartbeat.clone();
    let hb_handle = ThreadBuilder::new().name("heartbeat".to_owned()).spawn(move || {
      loop {
        let start         = Instant::now();
        let mut remaining = interval;

        loop {
          thread::park_timeout(remaining);
          let elapsed = start.elapsed();
          if elapsed >= remaining {
            break
          }
          remaining -= interval - elapsed;
        }

        send_hartbeat.store(true, Ordering::Relaxed);
      }
    }).map_err(ErrorKind::IOError)?;
    self.hb_handle = Some(hb_handle);
    Ok(())
  }

  fn heartbeat(&mut self) -> Result<(), Error> {
    self.connection.send_heartbeat()?;
    self.send_heartbeat.store(false, Ordering::Relaxed);
    Ok(())
  }

  fn ensure_setup(&mut self) -> Result<(), Error> {
    if self.status != Status::Setup && self.connection.status.connected() {
      let frame_max = self.connection.configuration.frame_max() as usize;
      self.frame_size = std::cmp::max(self.frame_size, frame_max);
      self.receive_buffer.grow(FRAMES_STORAGE * self.frame_size);
      self.send_buffer.grow(FRAMES_STORAGE * self.frame_size);
      let heartbeat = self.connection.configuration.heartbeat();
      if heartbeat != 0 {
        trace!("io_loop: start heartbeat");
        self.start_heartbeat(Duration::from_secs(heartbeat as u64))?;
        trace!("io_loop: heartbeat started");
      }
      self.status = Status::Setup;
    }
    Ok(())
  }

  fn wants_to_write(&self) -> bool {
    self.can_write && self.has_data
  }

  fn wants_to_read(&self) -> bool {
    self.can_read
  }

  fn do_run(&mut self) -> Result<(), Error> {
    trace!("io_loop do_run; can_read={}, can_write={}, has_data={}", self.can_read, self.can_write, self.has_data);
    loop {
      if self.send_heartbeat.load(Ordering::Relaxed) {
        self.heartbeat()?;
      }
      if self.wants_to_write() && !self.connection.status.blocked() {
        if let Err(e) = self.write_to_stream() {
          match e.kind() {
            ErrorKind::IOError(e) if e.kind() == io::ErrorKind::WouldBlock => self.can_write = false,
            _ => {
              error!("error writing: {:?}", e);
              self.connection.set_error()?;
              return Err(e);
            }
          }
        }
        self.send_buffer.shift_unless_available(self.frame_size);
      }
      if self.wants_to_read() {
        if let Err(e) = self.read_from_stream() {
          match e.kind() {
            ErrorKind::IOError(e) if e.kind() == io::ErrorKind::WouldBlock => self.can_read = false,
            _ => {
              error!("error reading: {:?}", e);
              self.connection.set_error()?;
              return Err(e);
            }
          }
        }
        self.receive_buffer.shift_unless_available(self.frame_size);
      }
      if self.can_parse() {
        self.parse()?;
      }
      if !self.wants_to_read() || !self.wants_to_write() {
        if self.wants_to_read() || self.can_parse() || self.has_data {
          trace!("io_loop send continue; can_read={}, can_write={}, has_data={}", self.can_read, self.can_write, self.has_data);
          self.set_readiness.set_readiness(Ready::readable()).map_err(ErrorKind::IOError)?;
        }
        break;
      }
    }
    trace!("io_loop do_run done; can_read={}, can_write={}, has_data={}", self.can_read, self.can_write, self.has_data);
    Ok(())
  }

  fn run(&mut self, events: &mut Events) -> Result<(), Error> {
    trace!("io_loop run");
    self.ensure_setup()?;
    trace!("io_loop poll");
    self.poll.poll(events, None).map_err(ErrorKind::IOError)?;
    trace!("io_loop poll done");
    for event in events.iter() {
      match event.token() {
        SOCKET    => {
          if event.readiness().is_readable() {
            self.can_read = true;
          }
          if event.readiness().is_writable() {
            self.can_write = true;
          }
        },
        DATA      => self.has_data = true,
        _         => {},
      }
    }
    self.do_run()
  }

  fn can_parse(&self) -> bool {
    self.receive_buffer.available_data() > 0
  }

  fn write_to_stream(&mut self) -> Result<(), Error> {
    self.serialize()?;

    self.socket.write(&self.send_buffer.data()).map(|sz| {
      trace!("wrote {} bytes", sz);
      self.send_buffer.consume(sz);
    }).map_err(|e| ErrorKind::IOError(e).into())
  }

  fn read_from_stream(&mut self) -> Result<(), Error> {
    let state = self.connection.status.state();
    if state == ConnectionState::Initial || state == ConnectionState::Closed || state == ConnectionState::Error {
      self.connection.set_error()?;
      Err(ErrorKind::InvalidConnectionState(state).into())
    } else {
      self.socket.read(&mut self.receive_buffer.space()).map(|sz| {
        trace!("read {} bytes", sz);
        self.receive_buffer.fill(sz);
      }).map_err(|e| ErrorKind::IOError(e).into())
    }
  }

  fn serialize(&mut self) -> Result<(), Error> {
    if let Some((send_id, next_msg)) = self.connection.next_frame() {
      trace!("will write to buffer: {:?}", next_msg);
      match gen_frame(self.send_buffer.space(), &next_msg).map(|tup| tup.0) {
        Ok(sz) => {
          self.send_buffer.fill(sz);
          self.connection.mark_sent(send_id);
          Ok(())
        },
        Err(e) => {
          match e {
            GenError::BufferTooSmall(_) => {
              // Requeue msg
              self.connection.requeue_frame(send_id, next_msg)?;
              self.send_buffer.shift();
              Ok(())
            },
            GenError::InvalidOffset | GenError::CustomError(_) | GenError::NotYetImplemented => {
              error!("error generating frame: {:?}", e);
              self.connection.set_error()?;
              Err(ErrorKind::SerialisationError(e).into())
            }
          }
        }
      }
    } else {
      self.has_data = false;
      Ok(())
    }
  }

  fn parse(&mut self) -> Result<(), Error> {
    match parse_frame(self.receive_buffer.data()) {
      Ok((i, f)) => {

        let consumed = self.receive_buffer.data().offset(i);
        self.receive_buffer.consume(consumed);

        if let Err(e) = self.connection.handle_frame(f) {
          self.connection.set_error()?;
          Err(e)
        } else {
          Ok(())
        }
      },
      Err(e) => {
        if e.is_incomplete() {
          self.receive_buffer.shift();
          Ok(())
        } else {
          error!("parse error: {:?}", e);
          self.connection.set_error()?;
          Err(ErrorKind::ParsingError(format!("{:?}", e)).into())
        }
      }
    }
  }
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate glium;

use clap::ArgMatches;

pub struct Shader {
    vert_shader: String,
    frag_shader: String,
}

impl Shader {
    pub fn construct(args: &ArgMatches) -> Self {
        Shader { vert_shader: Shader::construct_vert_shader(args),
                 frag_shader: Shader::construct_frag_shader(args) }
    }

    #[allow(unused_variables)]
    fn construct_vert_shader(args: &ArgMatches) -> String {
        DEFAULT_VERT_SHADER.to_string()
    }

    fn construct_frag_shader(args: &ArgMatches) -> String {
        frag_shader::gen_shader(args)
    }

    pub fn compile<F>(self, display: &F) -> glium::Program
                  where F: glium::backend::Facade {
        let res = glium::Program::from_source(display,
                &self.vert_shader,
                &self.frag_shader,
                None);
        match res {
            Ok(t) => t,
            Err(e) => {
                panic!("{}", e);
            },
        }
    }
}

const DEFAULT_VERT_SHADER: &'static str = r#"

#version 330

in vec2 pos;
out vec3 dir_v;
out vec2 pos_v;

uniform float height_ratio; // height / width
uniform float fov_ratio; // tan(fov / 2)

uniform mat3 facing;

void main() {
    float x = pos.x * fov_ratio;
    float y = pos.y * fov_ratio * height_ratio;
    dir_v = facing * vec3(x, y, 1.0);
    pos_v = pos;

    gl_Position = vec4(pos, 0.0, 1.0);
}

"#;

#[allow(unused_variables)]
mod frag_shader {
    use clap::ArgMatches;
    pub fn gen_shader(args: &ArgMatches) -> String {
        format!(r#"
{preamble}

{bg_func}

{trace_params}

{ad_params}

void main() {{
    float alpha_rem = 1.0;
    vec4 ccolor = vec4(0.0, 0.0, 0.0, 0.0);
    vec3 dir = normalize(dir_v);
    vec3 pos = src;

    /* closest approach to BH */
    float min_dist = length(cross(dir, src));

    {loop_vars}

    {trace_vars}

    {loop_cond} {{
        vec3 npos, ndir;

        {update_func}

        {bh_check}
        {ad_check}

        pos = npos;
        dir = ndir;
    }}

    ccolor += alpha_rem * bg_col(dir);

    color = ccolor;
}}

    "#,

        preamble = PREAMBLE,
        bg_func = bg::func(args),
        trace_params = trace::params(args),
        ad_params = ad::params(args),
        loop_vars = iter::vars(args),
        trace_vars = trace::vars(args),
        loop_cond = iter::cond(args),
        update_func = trace::update(args),
        bh_check = bh::check(args),
        ad_check = ad::check(args))
    }

    const PREAMBLE: &'static str = r#"
#version 330

#define M_PI (3.1415926535897932384626433832795)
/* we set constants to convenient values for now */
const float C = 1.0;
const float R_s = 1.0;
const float M = 0.5; /* must be R_s / 2 */
const float G = 1.0;

uniform vec3 src;

in vec3 dir_v;
in vec3 pos_v;
out vec4 color;

float atan2(float y, float x) {
    return x == 0.0 ? sign(y) * M_PI / 2 : atan(y, x);
}

float yaw(vec3 v) {
    return atan2(v.x, v.z);
}

float yaw_coord(vec3 v) {
    return (yaw(v) + M_PI) / (2. * M_PI);
}

float pitch(vec3 v) {
    return asin(v.y);
}

float pitch_coord(vec3 v) {
    return (pitch(v) + M_PI / 2.) / M_PI;
}

float ts_func(float ts, vec3 pos) {
    //float r2 = dot(pos,pos);
    return ts;
    //return ts * clamp(r2 / 2.25, 1.0, 10.0);
}
"#;

    mod bg {
        use clap::ArgMatches;
        enum Type {
            Black,
            Texture,
        }

        pub fn func(args: &ArgMatches) -> String {
            let s = args.value_of("bg").unwrap_or("img");
            BGS[(match s {
                "img" => Type::Texture,
                "black" => Type::Black,
                _ => panic!("Invalid bg type"),
            }) as usize].to_string()
        }

        const BGS: [&'static str; 2] = [
        r#"
vec4 bg_col(vec3 dir) {
    return vec4(0.0, 0.0, 0.0, 1.0);
}"#,
            r#"

uniform sampler2D bg_tex;
vec4 bg_col(vec3 dir) {
    float x = yaw_coord(dir);
    float y = pitch_coord(dir);

    vec2 tex_coords = vec2(x, y);

    float invert_x = x - 0.5;
    invert_x = invert_x - sign(invert_x) * 0.5;
    vec2 invert_coords = vec2(invert_x, y);

    vec2 dx1 = dFdx(tex_coords);
    vec2 dx2 = dFdx(invert_coords);

    vec2 dy1 = dFdy(tex_coords);
    vec2 dy2 = dFdy(invert_coords);

    vec2 dx = dot(dx1, dx1) < dot(dx2, dx2) ? dx1 : dx2;
    vec2 dy = dot(dy1, dy1) < dot(dy2, dy2) ? dy1 : dy2;

    /* force the LOD so that GLSL doesn't flip out on the discontinuity
       at the texture border */
    return textureGrad(bg_tex, tex_coords, dx, dy);
}"#,
        ];
    }

    mod iter {
        use clap::ArgMatches;
        pub fn vars(args: &ArgMatches) -> String {
            "".to_string()
        }

        pub fn cond(args: &ArgMatches) -> String {
            r#"while(dot(pos, pos) <= 15.0 * 15.0 &&
                alpha_rem >= 0.01)"#.to_string()
        }
    }

    mod trace {
        use clap::ArgMatches;

        enum Type {
            Flat = 0,
            Verlet = 1,
            Rk4 = 2,
        }

        fn get_type(args: &ArgMatches) -> Type {
            if args.is_present("flat") {
                Type::Flat
            } else {
                match args.value_of("method").unwrap_or("rk4") {
                    "verlet" => Type::Verlet,
                    "rk4" => Type::Rk4,
                    s => panic!("invalid integration scheme: {}", s),
                }
            }
        }

        pub fn params(args: &ArgMatches) -> String {
            PARAMS[get_type(args) as usize].to_string()
        }

        pub fn update(args: &ArgMatches) -> String {
            UPDATES[get_type(args) as usize].to_string()
        }

        pub fn vars(args: &ArgMatches) -> String {
            VARS[get_type(args) as usize].to_string()
        }

        const VARS: [&'static str; 3] = [
            r#"
            float time_step;
            "#,
            r#"
            float time_step;
            vec3 h = cross(pos, dir);
            float h2 = dot(h, h);
            "#,
            r#"
            float time_step;
            vec3 h = cross(pos, dir);
            float h2 = dot(h, h);
            "#,
        ];

        const PARAMS: [&'static str; 3] = [
            r#"
            uniform float TIME_STEP;
        "#,
            r#"
            uniform float TIME_STEP;
        "#,
            r#"
            uniform float TIME_STEP;

            vec3 accel(vec3 pos, float h2) {
                return -pos * 1.5 * h2 * pow(dot(pos, pos), -2.5);
            }
        "#,
        ];

        const UPDATES: [&'static str; 3] = [
            r#"
            time_step = ts_func(TIME_STEP, pos);
            npos = pos + dir * time_step;
            ndir = dir;
        "#,
            r#"
            time_step = ts_func(TIME_STEP, pos);
            npos = pos + dir * time_step;
            vec3 accel = -pos * 1.5 * h2 * pow(dot(pos, pos), -2.5);
            ndir = dir + accel * time_step;
            if(dot(ndir, ndir) > 100.0) {
                /* experimental renormalization */
                ndir = normalize(ndir);
                h = cross(ndir, npos);
                h2 = dot(h, h);
            }
        "#,
            r#"
            time_step = ts_func(TIME_STEP, pos);
            {
                vec3 x1 = pos;
                vec3 v1 = dir;
                vec3 a1 = accel(x1, h2);

                vec3 x2 = pos + 0.5 * v1 * time_step;
                vec3 v2 = dir + 0.5 * a1 * time_step;
                vec3 a2 = accel(x2, h2);

                vec3 x3 = pos + 0.5 * v2 * time_step;
                vec3 v3 = dir + 0.5 * a2 * time_step;
                vec3 a3 = accel(x3, h2);

                vec3 x4 = pos + v3 * time_step;
                vec3 v4 = dir + a3 * time_step;
                vec3 a4 = accel(x4, h2);

                npos = pos + (time_step/6.0) * (v1 + 2*v2 + 2*v3 + v4);
                ndir = dir + (time_step/6.0) * (a1 + 2*a2 + 2*a3 + a4);
            }
            if(dot(ndir, ndir) > 100.0) {
                /* experimental renormalization */
                ndir = normalize(ndir);
                h = cross(ndir, npos);
                h2 = dot(h, h);
            }
        "#,
        ];
    }

    mod bh {
        use clap::ArgMatches;
        pub fn check(args: &ArgMatches) -> String {
            BH_CHECK.to_string()
        }

        const BH_CHECK: &'static str = r#"
            {
            float mindist2;
            float t;
            vec3 closest;
            {
                vec3 c = cross(npos, pos);
                vec3 d = pos - npos;
                mindist2 = dot(c, c) / dot(d, d);

                t = dot(pos, d) / dot(d, d);
                t = clamp(t, 0.0, 1.0);
                closest = pos + t * (npos - pos);

            }
            if(dot(closest, closest) <= R_s * R_s) {
                ccolor += vec4(0.0, 0.0, 0.0, 1.0) * alpha_rem * 1.0;
                alpha_rem -= alpha_rem * 1.0;
            }
            }
        "#;
    }

    mod ad {
        use clap::ArgMatches;
        enum Type {
            NoDisk = 0,
            White = 1,
            Tex = 2,
        }

        fn get_type(args: &ArgMatches) -> Type {
            match args.value_of("accdisk").unwrap_or("white") {
                "none" => Type::NoDisk,
                "white" => Type::White,
                "img" => Type::Tex,
                s => panic!("invalid accretion disk type: {}", s),
            }
        }

        pub fn check(args: &ArgMatches) -> String {
            CHECK.to_string()
        }

        pub fn params(args: &ArgMatches) -> String {
            let or: f32 = args.value_of("oradius").unwrap().parse().unwrap();
            let ir: f32 = args.value_of("iradius").unwrap().parse().unwrap();
            let extra = PARAMS[get_type(args) as usize].to_string();
            format!(r#"
                const float DISK_O_RAD = {};
                const float DISK_I_RAD = {};
                {}
                "#, or, ir, extra)
        }

        const CHECK: &'static str = r#"
            {
            float t = -pos.y / (npos.y - pos.y);
            if(t >= 0 && t <= 1) {
                vec3 p = pos + t * (npos - pos);
                float mag = length(p);
                if(mag >= DISK_I_RAD && mag <= DISK_O_RAD) {
                    vec4 col = ad_col(p, mag);
                    float rat = col.a;
                    ccolor += col * alpha_rem;
                    alpha_rem -= alpha_rem * rat;
                }
            }
            }
        "#;

        const PARAMS: [&'static str; 3] = [
            r#"
            vec4 ad_col(vec3 intersect) {
                return vec4(0.0, 0.0, 0.0, 0.0);
            }
        "#,
            r#"
            vec4 ad_col(vec3 intersect, float mag) {
                return vec4(1.0, 1.0, 1.0, 1.0);
            }
        "#,
            r#"
            uniform sampler2D ad_tex;
            vec4 ad_col(vec3 intersect, float mag) {
                float x = atan2(intersect.x, intersect.z);
                float y = (DISK_O_RAD - mag) / (DISK_O_RAD - DISK_I_RAD);

                float invert_x = x - 0.5;
                invert_x = invert_x - sign(invert_x) * 0.5;

                vec2 c1 = vec2(x, y);
                vec2 c2 = vec2(invert_x, y);

                vec2 dx1 = dFdx(c1);
                vec2 dx2 = dFdx(c2);

                vec2 dy1 = dFdy(c1);
                vec2 dy2 = dFdy(c2);

                vec2 dx = dot(dx1, dx1) < dot(dx2, dx2) ? dx1 : dx2;
                vec2 dy = dot(dy1, dy1) < dot(dy2, dy2) ? dy1 : dy2;

                return textureGrad(ad_tex, c1, dx, dy);
            }
        "#,
        ];
    }
}

<commit_msg>fixed moving outside 15<commit_after>extern crate clap;
extern crate glium;

use clap::ArgMatches;

pub struct Shader {
    vert_shader: String,
    frag_shader: String,
}

impl Shader {
    pub fn construct(args: &ArgMatches) -> Self {
        Shader { vert_shader: Shader::construct_vert_shader(args),
                 frag_shader: Shader::construct_frag_shader(args) }
    }

    #[allow(unused_variables)]
    fn construct_vert_shader(args: &ArgMatches) -> String {
        DEFAULT_VERT_SHADER.to_string()
    }

    fn construct_frag_shader(args: &ArgMatches) -> String {
        frag_shader::gen_shader(args)
    }

    pub fn compile<F>(self, display: &F) -> glium::Program
                  where F: glium::backend::Facade {
        let res = glium::Program::from_source(display,
                &self.vert_shader,
                &self.frag_shader,
                None);
        match res {
            Ok(t) => t,
            Err(e) => {
                panic!("{}", e);
            },
        }
    }
}

const DEFAULT_VERT_SHADER: &'static str = r#"

#version 330

in vec2 pos;
out vec3 dir_v;
out vec2 pos_v;

uniform float height_ratio; // height / width
uniform float fov_ratio; // tan(fov / 2)

uniform mat3 facing;

void main() {
    float x = pos.x * fov_ratio;
    float y = pos.y * fov_ratio * height_ratio;
    dir_v = facing * vec3(x, y, 1.0);
    pos_v = pos;

    gl_Position = vec4(pos, 0.0, 1.0);
}

"#;

#[allow(unused_variables)]
mod frag_shader {
    use clap::ArgMatches;
    pub fn gen_shader(args: &ArgMatches) -> String {
        format!(r#"
{preamble}

{bg_func}

{trace_params}

{ad_params}

void main() {{
    float alpha_rem = 1.0;
    vec4 ccolor = vec4(0.0, 0.0, 0.0, 0.0);
    vec3 dir = normalize(dir_v);
    vec3 pos = src;

    /* closest approach to BH */
    float min_dist = length(cross(dir, src));

    {loop_vars}

    {trace_vars}

    {loop_cond} {{
        vec3 npos, ndir;

        {update_func}

        {bh_check}
        {ad_check}

        pos = npos;
        dir = ndir;
    }}

    ccolor += alpha_rem * bg_col(dir);

    color = ccolor;
}}

    "#,

        preamble = PREAMBLE,
        bg_func = bg::func(args),
        trace_params = trace::params(args),
        ad_params = ad::params(args),
        loop_vars = iter::vars(args),
        trace_vars = trace::vars(args),
        loop_cond = iter::cond(args),
        update_func = trace::update(args),
        bh_check = bh::check(args),
        ad_check = ad::check(args))
    }

    const PREAMBLE: &'static str = r#"
#version 330

#define M_PI (3.1415926535897932384626433832795)
/* we set constants to convenient values for now */
const float C = 1.0;
const float R_s = 1.0;
const float M = 0.5; /* must be R_s / 2 */
const float G = 1.0;

uniform vec3 src;

in vec3 dir_v;
in vec3 pos_v;
out vec4 color;

float atan2(float y, float x) {
    return x == 0.0 ? sign(y) * M_PI / 2 : atan(y, x);
}

float yaw(vec3 v) {
    return atan2(v.x, v.z);
}

float yaw_coord(vec3 v) {
    return (yaw(v) + M_PI) / (2. * M_PI);
}

float pitch(vec3 v) {
    return asin(v.y);
}

float pitch_coord(vec3 v) {
    return (pitch(v) + M_PI / 2.) / M_PI;
}

float ts_func(float ts, vec3 pos) {
    //float r2 = dot(pos,pos);
    return ts;
    //return ts * clamp(r2 / 2.25, 1.0, 10.0);
}
"#;

    mod bg {
        use clap::ArgMatches;
        enum Type {
            Black,
            Texture,
        }

        pub fn func(args: &ArgMatches) -> String {
            let s = args.value_of("bg").unwrap_or("img");
            BGS[(match s {
                "img" => Type::Texture,
                "black" => Type::Black,
                _ => panic!("Invalid bg type"),
            }) as usize].to_string()
        }

        const BGS: [&'static str; 2] = [
        r#"
vec4 bg_col(vec3 dir) {
    return vec4(0.0, 0.0, 0.0, 1.0);
}"#,
            r#"

uniform sampler2D bg_tex;
vec4 bg_col(vec3 dir) {
    float x = yaw_coord(dir);
    float y = pitch_coord(dir);

    vec2 tex_coords = vec2(x, y);

    float invert_x = x - 0.5;
    invert_x = invert_x - sign(invert_x) * 0.5;
    vec2 invert_coords = vec2(invert_x, y);

    vec2 dx1 = dFdx(tex_coords);
    vec2 dx2 = dFdx(invert_coords);

    vec2 dy1 = dFdy(tex_coords);
    vec2 dy2 = dFdy(invert_coords);

    vec2 dx = dot(dx1, dx1) < dot(dx2, dx2) ? dx1 : dx2;
    vec2 dy = dot(dy1, dy1) < dot(dy2, dy2) ? dy1 : dy2;

    /* force the LOD so that GLSL doesn't flip out on the discontinuity
       at the texture border */
    return textureGrad(bg_tex, tex_coords, dx, dy);
}"#,
        ];
    }

    mod iter {
        use clap::ArgMatches;
        pub fn vars(args: &ArgMatches) -> String {
            "".to_string()
        }

        pub fn cond(args: &ArgMatches) -> String {
            r#"float border = max(15.0 * 15.0, dot(src, src));
            while(dot(pos, pos) <= border &&
                alpha_rem >= 0.01)"#.to_string()
        }
    }

    mod trace {
        use clap::ArgMatches;

        enum Type {
            Flat = 0,
            Verlet = 1,
            Rk4 = 2,
        }

        fn get_type(args: &ArgMatches) -> Type {
            if args.is_present("flat") {
                Type::Flat
            } else {
                match args.value_of("method").unwrap_or("rk4") {
                    "verlet" => Type::Verlet,
                    "rk4" => Type::Rk4,
                    s => panic!("invalid integration scheme: {}", s),
                }
            }
        }

        pub fn params(args: &ArgMatches) -> String {
            PARAMS[get_type(args) as usize].to_string()
        }

        pub fn update(args: &ArgMatches) -> String {
            UPDATES[get_type(args) as usize].to_string()
        }

        pub fn vars(args: &ArgMatches) -> String {
            VARS[get_type(args) as usize].to_string()
        }

        const VARS: [&'static str; 3] = [
            r#"
            float time_step;
            "#,
            r#"
            float time_step;
            vec3 h = cross(pos, dir);
            float h2 = dot(h, h);
            "#,
            r#"
            float time_step;
            vec3 h = cross(pos, dir);
            float h2 = dot(h, h);
            "#,
        ];

        const PARAMS: [&'static str; 3] = [
            r#"
            uniform float TIME_STEP;
        "#,
            r#"
            uniform float TIME_STEP;
        "#,
            r#"
            uniform float TIME_STEP;

            vec3 accel(vec3 pos, float h2) {
                return -pos * 1.5 * h2 * pow(dot(pos, pos), -2.5);
            }
        "#,
        ];

        const UPDATES: [&'static str; 3] = [
            r#"
            time_step = ts_func(TIME_STEP, pos);
            npos = pos + dir * time_step;
            ndir = dir;
        "#,
            r#"
            time_step = ts_func(TIME_STEP, pos);
            npos = pos + dir * time_step;
            vec3 accel = -pos * 1.5 * h2 * pow(dot(pos, pos), -2.5);
            ndir = dir + accel * time_step;
            if(dot(ndir, ndir) > 100.0) {
                /* experimental renormalization */
                ndir = normalize(ndir);
                h = cross(ndir, npos);
                h2 = dot(h, h);
            }
        "#,
            r#"
            time_step = ts_func(TIME_STEP, pos);
            {
                vec3 x1 = pos;
                vec3 v1 = dir;
                vec3 a1 = accel(x1, h2);

                vec3 x2 = pos + 0.5 * v1 * time_step;
                vec3 v2 = dir + 0.5 * a1 * time_step;
                vec3 a2 = accel(x2, h2);

                vec3 x3 = pos + 0.5 * v2 * time_step;
                vec3 v3 = dir + 0.5 * a2 * time_step;
                vec3 a3 = accel(x3, h2);

                vec3 x4 = pos + v3 * time_step;
                vec3 v4 = dir + a3 * time_step;
                vec3 a4 = accel(x4, h2);

                npos = pos + (time_step/6.0) * (v1 + 2*v2 + 2*v3 + v4);
                ndir = dir + (time_step/6.0) * (a1 + 2*a2 + 2*a3 + a4);
            }
            if(dot(ndir, ndir) > 100.0) {
                /* experimental renormalization */
                ndir = normalize(ndir);
                h = cross(ndir, npos);
                h2 = dot(h, h);
            }
        "#,
        ];
    }

    mod bh {
        use clap::ArgMatches;
        pub fn check(args: &ArgMatches) -> String {
            BH_CHECK.to_string()
        }

        const BH_CHECK: &'static str = r#"
            {
            float mindist2;
            float t;
            vec3 closest;
            {
                vec3 c = cross(npos, pos);
                vec3 d = pos - npos;
                mindist2 = dot(c, c) / dot(d, d);

                t = dot(pos, d) / dot(d, d);
                t = clamp(t, 0.0, 1.0);
                closest = pos + t * (npos - pos);

            }
            if(dot(closest, closest) <= R_s * R_s) {
                ccolor += vec4(0.0, 0.0, 0.0, 1.0) * alpha_rem * 1.0;
                alpha_rem -= alpha_rem * 1.0;
            }
            }
        "#;
    }

    mod ad {
        use clap::ArgMatches;
        enum Type {
            NoDisk = 0,
            White = 1,
            Tex = 2,
        }

        fn get_type(args: &ArgMatches) -> Type {
            match args.value_of("accdisk").unwrap_or("white") {
                "none" => Type::NoDisk,
                "white" => Type::White,
                "img" => Type::Tex,
                s => panic!("invalid accretion disk type: {}", s),
            }
        }

        pub fn check(args: &ArgMatches) -> String {
            CHECK.to_string()
        }

        pub fn params(args: &ArgMatches) -> String {
            let or: f32 = args.value_of("oradius").unwrap().parse().unwrap();
            let ir: f32 = args.value_of("iradius").unwrap().parse().unwrap();
            let extra = PARAMS[get_type(args) as usize].to_string();
            format!(r#"
                const float DISK_O_RAD = {};
                const float DISK_I_RAD = {};
                {}
                "#, or, ir, extra)
        }

        const CHECK: &'static str = r#"
            {
            float t = -pos.y / (npos.y - pos.y);
            if(t >= 0 && t <= 1) {
                vec3 p = pos + t * (npos - pos);
                float mag = length(p);
                if(mag >= DISK_I_RAD && mag <= DISK_O_RAD) {
                    vec4 col = ad_col(p, mag);
                    float rat = col.a;
                    ccolor += col * alpha_rem;
                    alpha_rem -= alpha_rem * rat;
                }
            }
            }
        "#;

        const PARAMS: [&'static str; 3] = [
            r#"
            vec4 ad_col(vec3 intersect) {
                return vec4(0.0, 0.0, 0.0, 0.0);
            }
        "#,
            r#"
            vec4 ad_col(vec3 intersect, float mag) {
                return vec4(1.0, 1.0, 1.0, 1.0);
            }
        "#,
            r#"
            uniform sampler2D ad_tex;
            vec4 ad_col(vec3 intersect, float mag) {
                float x = atan2(intersect.x, intersect.z);
                float y = (DISK_O_RAD - mag) / (DISK_O_RAD - DISK_I_RAD);

                float invert_x = x - 0.5;
                invert_x = invert_x - sign(invert_x) * 0.5;

                vec2 c1 = vec2(x, y);
                vec2 c2 = vec2(invert_x, y);

                vec2 dx1 = dFdx(c1);
                vec2 dx2 = dFdx(c2);

                vec2 dy1 = dFdy(c1);
                vec2 dy2 = dFdy(c2);

                vec2 dx = dot(dx1, dx1) < dot(dx2, dx2) ? dx1 : dx2;
                vec2 dy = dot(dy1, dy1) < dot(dy2, dy2) ? dy1 : dy2;

                return textureGrad(ad_tex, c1, dx, dy);
            }
        "#,
        ];
    }
}

<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::path::PathBuf;
use std::process::Command;

use rustc_version::VersionMeta;
use tempdir::TempDir;
use toml::{Table, Value};

use CompilationMode;
use cargo::{Root, Rustflags};
use errors::*;
use extensions::CommandExt;
use rustc::{Src, Sysroot};
use util;
use xargo::Home;
use {cargo, xargo};

#[cfg(feature = "dev")]
fn profile() -> &'static str {
    "debug"
}

#[cfg(not(feature = "dev"))]
fn profile() -> &'static str {
    "release"
}

fn build(cmode: &CompilationMode,
         blueprint: Blueprint,
         ctoml: &cargo::Toml,
         home: &Home,
         rustflags: &Rustflags,
         hash: u64,
         verbose: bool)
         -> Result<()> {
    const TOML: &'static str = r#"
[package]
authors = ["The Rust Project Developers"]
name = "sysroot"
version = "0.0.0"
"#;

    let rustlib = home.lock_rw(cmode.triple())?;
    rustlib.remove_siblings()
        .chain_err(|| format!("couldn't clear {}", rustlib.path().display()))?;
    let dst = rustlib.parent().join("lib");
    util::mkdir(&dst)?;
    for (_, stage) in blueprint.stages {
        let td = TempDir::new("xargo")
            .chain_err(|| "couldn't create a temporary directory")?;
        let td = td.path();

        let mut stoml = TOML.to_owned();

        let mut map = Table::new();
        map.insert("dependencies".to_owned(), Value::Table(stage.toml));
        stoml.push_str(&Value::Table(map).to_string());

        if let Some(profile) = ctoml.profile() {
            stoml.push_str(&profile.to_string())
        }

        util::write(&td.join("Cargo.toml"), &stoml)?;
        util::mkdir(&td.join("src"))?;
        util::write(&td.join("src/lib.rs"), "")?;

        let cargo = || {
            let mut cmd = Command::new("cargo");
            cmd.env("RUSTFLAGS", rustflags.for_xargo(home));
            cmd.env_remove("CARGO_TARGET_DIR");
            cmd.arg("build");

            match () {
                #[cfg(feature = "dev")]
                () => {}
                #[cfg(not(feature = "dev"))]
                () => {
                    cmd.arg("--release");
                }
            }
            cmd.arg("--manifest-path");
            cmd.arg(td.join("Cargo.toml"));
            cmd.args(&["--target", cmode.triple()]);

            if verbose {
                cmd.arg("-v");
            }

            cmd
        };

        for krate in stage.crates {
            cargo().arg("-p").arg(krate).run(verbose)?;
        }

        // Copy artifacts to Xargo sysroot
        util::cp_r(&td.join("target")
                       .join(cmode.triple())
                       .join(profile())
                       .join("deps"),
                   &dst)?;

    }

    // Create hash file
    util::write(&rustlib.parent().join(".hash"), &hash.to_string())?;

    Ok(())
}

fn old_hash(cmode: &CompilationMode, home: &Home) -> Result<Option<u64>> {
    // FIXME this should be `lock_ro`
    let lock = home.lock_rw(cmode.triple())?;
    let hfile = lock.parent().join(".hash");

    if hfile.exists() {
        Ok(util::read(&hfile)?.parse().ok())
    } else {
        Ok(None)
    }
}

/// Computes the hash of the would-be target sysroot
///
/// This information is used to compute the hash
///
/// - Dependencies in `Xargo.toml` for a specific target
/// - RUSTFLAGS / build.rustflags / target.*.rustflags
/// - The target specification file, is any
/// - `[profile.release]` in `Cargo.toml`
/// - `rustc` commit hash
fn hash(cmode: &CompilationMode,
        blueprint: &Blueprint,
        rustflags: &Rustflags,
        ctoml: &cargo::Toml,
        meta: &VersionMeta)
        -> Result<u64> {
    let mut hasher = DefaultHasher::new();

    blueprint.hash(&mut hasher);

    rustflags.hash(&mut hasher);

    cmode.hash(&mut hasher)?;

    if let Some(profile) = ctoml.profile() {
        profile.hash(&mut hasher);
    }

    if let Some(ref hash) = meta.commit_hash {
        hash.hash(&mut hasher);
    }

    Ok(hasher.finish())
}

pub fn update(cmode: &CompilationMode,
              home: &Home,
              root: &Root,
              rustflags: &Rustflags,
              meta: &VersionMeta,
              src: &Src,
              sysroot: &Sysroot,
              verbose: bool)
              -> Result<()> {
    let ctoml = cargo::toml(root)?;
    let xtoml = xargo::toml(root)?;

    let blueprint =
        Blueprint::from(xtoml.as_ref(), cmode.triple(), root, &src)?;

    let hash = hash(cmode, &blueprint, rustflags, &ctoml, meta)?;

    if old_hash(cmode, home)? != Some(hash) {
        build(cmode, blueprint, &ctoml, home, rustflags, hash, verbose)?;
    }

    // copy host artifacts into the sysroot, if necessary
    if cmode.is_native() {
        return Ok(());
    }

    let lock = home.lock_rw(&meta.host)?;
    let hfile = lock.parent().join(".hash");

    let hash = meta.commit_hash.as_ref().map(|s| &**s).unwrap_or("");
    if hfile.exists() {
        if util::read(&hfile)? == hash {
            return Ok(());
        }
    }

    lock.remove_siblings()
        .chain_err(|| format!("couldn't clear {}", lock.path().display()))?;
    let dst = lock.parent().join("lib");
    util::mkdir(&dst)?;
    util::cp_r(&sysroot.path()
                   .join("lib/rustlib")
                   .join(&meta.host)
                   .join("lib"),
               &dst)?;

    util::write(&hfile, hash)?;

    Ok(())
}

/// Per stage dependencies
#[derive(Debug)]
pub struct Stage {
    crates: Vec<String>,
    toml: Table,
}

/// A sysroot that will be built in "stages"
#[derive(Debug)]
pub struct Blueprint {
    stages: BTreeMap<i64, Stage>,
}

impl Blueprint {
    fn new() -> Self {
        Blueprint { stages: BTreeMap::new() }
    }

    fn from(toml: Option<&xargo::Toml>,
            target: &str,
            root: &Root,
            src: &Src)
            -> Result<Self> {
        let deps = match (toml.and_then(|t| t.dependencies()),
                          toml.and_then(|t| t.target_dependencies(target))) {
            (Some(value), Some(tvalue)) => {
                let mut deps = value.as_table()
                    .cloned()
                    .ok_or_else(|| {
                        format!("Xargo.toml: `dependencies` must be a table")
                    })?;

                let more_deps = tvalue.as_table()
                    .ok_or_else(|| {
                        format!("Xargo.toml: `target.{}.dependencies` must be \
                                 a table",
                                target)
                    })?;
                for (k, v) in more_deps {
                    if deps.insert(k.to_owned(), v.clone()).is_some() {
                        Err(format!("found duplicate dependency name {}, \
                                     but all dependencies must have a \
                                     unique name",
                                    k))?
                    }
                }

                deps
            }
            (Some(value), None) |
            (None, Some(value)) => {
                if let Some(table) = value.as_table() {
                    table.clone()
                } else {
                    Err(format!("Xargo.toml: target.{}.dependencies must be \
                                 a table",
                                target))?
                }
            }
            (None, None) => {
                // If no dependencies were listed, we assume `core` as the
                // only dependency
                let mut t = BTreeMap::new();
                t.insert("core".to_owned(), Value::Table(BTreeMap::new()));
                t
            }
        };

        let mut blueprint = Blueprint::new();
        for (k, v) in deps {
            if let Value::Table(mut map) = v {
                let stage = if let Some(value) = map.remove("stage") {
                    value.as_integer()
                        .ok_or_else(|| {
                            format!("dependencies.{}.stage must be an integer",
                                    k)
                        })?
                } else {
                    0
                };

                if let Some(path) = map.get_mut("path") {
                    let p = PathBuf::from(path.as_str()
                        .ok_or_else(|| {
                            format!("dependencies.{}.path must be a string", k)
                        })?);

                    if !p.is_absolute() {
                        *path = Value::String(root.path()
                            .join(&p)
                            .canonicalize()
                            .chain_err(|| {
                                format!("couldn't canonicalize {}", p.display())
                            })?
                            .display()
                            .to_string());
                    }
                }

                if !map.contains_key("path") && !map.contains_key("git") {
                    let path = src.path()
                        .join(format!("lib{}", k))
                        .display()
                        .to_string();

                    map.insert("path".to_owned(), Value::String(path));
                }

                blueprint.push(stage, k, map);
            } else {
                Err(format!("Xargo.toml: target.{}.dependencies.{} must be \
                             a table",
                            target,
                            k))?
            }
        }

        Ok(blueprint)
    }

    fn push(&mut self, stage: i64, krate: String, toml: Table) {
        let stage = self.stages.entry(stage).or_insert_with(|| {
            Stage {
                crates: vec![],
                toml: Table::new(),
            }
        });

        stage.toml.insert(krate.clone(), Value::Table(toml));
        stage.crates.push(krate);
    }

    fn hash<H>(&self, hasher: &mut H)
        where H: Hasher
    {
        for stage in self.stages.values() {
            for (k, v) in stage.toml.iter() {
                k.hash(hasher);
                v.to_string().hash(hasher);
            }
        }
    }
}
<commit_msg>Pass "-Z force-unstable-if-unmarked" to Rustc<commit_after>use std::collections::BTreeMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::path::PathBuf;
use std::process::Command;

use rustc_version::VersionMeta;
use tempdir::TempDir;
use toml::{Table, Value};

use CompilationMode;
use cargo::{Root, Rustflags};
use errors::*;
use extensions::CommandExt;
use rustc::{Src, Sysroot};
use util;
use xargo::Home;
use {cargo, xargo};

#[cfg(feature = "dev")]
fn profile() -> &'static str {
    "debug"
}

#[cfg(not(feature = "dev"))]
fn profile() -> &'static str {
    "release"
}

fn build(cmode: &CompilationMode,
         blueprint: Blueprint,
         ctoml: &cargo::Toml,
         home: &Home,
         rustflags: &Rustflags,
         hash: u64,
         verbose: bool)
         -> Result<()> {
    const TOML: &'static str = r#"
[package]
authors = ["The Rust Project Developers"]
name = "sysroot"
version = "0.0.0"
"#;

    let rustlib = home.lock_rw(cmode.triple())?;
    rustlib.remove_siblings()
        .chain_err(|| format!("couldn't clear {}", rustlib.path().display()))?;
    let dst = rustlib.parent().join("lib");
    util::mkdir(&dst)?;
    for (_, stage) in blueprint.stages {
        let td = TempDir::new("xargo")
            .chain_err(|| "couldn't create a temporary directory")?;
        let td = td.path();

        let mut stoml = TOML.to_owned();

        let mut map = Table::new();
        map.insert("dependencies".to_owned(), Value::Table(stage.toml));
        stoml.push_str(&Value::Table(map).to_string());

        if let Some(profile) = ctoml.profile() {
            stoml.push_str(&profile.to_string())
        }

        util::write(&td.join("Cargo.toml"), &stoml)?;
        util::mkdir(&td.join("src"))?;
        util::write(&td.join("src/lib.rs"), "")?;

        let cargo = || {
            let mut cmd = Command::new("cargo");
            let mut flags = rustflags.for_xargo(home);
            flags.push_str(" -Z force-unstable-if-unmarked");
            cmd.env("RUSTFLAGS", flags);
            cmd.env_remove("CARGO_TARGET_DIR");
            cmd.arg("build");

            match () {
                #[cfg(feature = "dev")]
                () => {}
                #[cfg(not(feature = "dev"))]
                () => {
                    cmd.arg("--release");
                }
            }
            cmd.arg("--manifest-path");
            cmd.arg(td.join("Cargo.toml"));
            cmd.args(&["--target", cmode.triple()]);

            if verbose {
                cmd.arg("-v");
            }

            cmd
        };

        for krate in stage.crates {
            cargo().arg("-p").arg(krate).run(verbose)?;
        }

        // Copy artifacts to Xargo sysroot
        util::cp_r(&td.join("target")
                       .join(cmode.triple())
                       .join(profile())
                       .join("deps"),
                   &dst)?;

    }

    // Create hash file
    util::write(&rustlib.parent().join(".hash"), &hash.to_string())?;

    Ok(())
}

fn old_hash(cmode: &CompilationMode, home: &Home) -> Result<Option<u64>> {
    // FIXME this should be `lock_ro`
    let lock = home.lock_rw(cmode.triple())?;
    let hfile = lock.parent().join(".hash");

    if hfile.exists() {
        Ok(util::read(&hfile)?.parse().ok())
    } else {
        Ok(None)
    }
}

/// Computes the hash of the would-be target sysroot
///
/// This information is used to compute the hash
///
/// - Dependencies in `Xargo.toml` for a specific target
/// - RUSTFLAGS / build.rustflags / target.*.rustflags
/// - The target specification file, is any
/// - `[profile.release]` in `Cargo.toml`
/// - `rustc` commit hash
fn hash(cmode: &CompilationMode,
        blueprint: &Blueprint,
        rustflags: &Rustflags,
        ctoml: &cargo::Toml,
        meta: &VersionMeta)
        -> Result<u64> {
    let mut hasher = DefaultHasher::new();

    blueprint.hash(&mut hasher);

    rustflags.hash(&mut hasher);

    cmode.hash(&mut hasher)?;

    if let Some(profile) = ctoml.profile() {
        profile.hash(&mut hasher);
    }

    if let Some(ref hash) = meta.commit_hash {
        hash.hash(&mut hasher);
    }

    Ok(hasher.finish())
}

pub fn update(cmode: &CompilationMode,
              home: &Home,
              root: &Root,
              rustflags: &Rustflags,
              meta: &VersionMeta,
              src: &Src,
              sysroot: &Sysroot,
              verbose: bool)
              -> Result<()> {
    let ctoml = cargo::toml(root)?;
    let xtoml = xargo::toml(root)?;

    let blueprint =
        Blueprint::from(xtoml.as_ref(), cmode.triple(), root, &src)?;

    let hash = hash(cmode, &blueprint, rustflags, &ctoml, meta)?;

    if old_hash(cmode, home)? != Some(hash) {
        build(cmode, blueprint, &ctoml, home, rustflags, hash, verbose)?;
    }

    // copy host artifacts into the sysroot, if necessary
    if cmode.is_native() {
        return Ok(());
    }

    let lock = home.lock_rw(&meta.host)?;
    let hfile = lock.parent().join(".hash");

    let hash = meta.commit_hash.as_ref().map(|s| &**s).unwrap_or("");
    if hfile.exists() {
        if util::read(&hfile)? == hash {
            return Ok(());
        }
    }

    lock.remove_siblings()
        .chain_err(|| format!("couldn't clear {}", lock.path().display()))?;
    let dst = lock.parent().join("lib");
    util::mkdir(&dst)?;
    util::cp_r(&sysroot.path()
                   .join("lib/rustlib")
                   .join(&meta.host)
                   .join("lib"),
               &dst)?;

    util::write(&hfile, hash)?;

    Ok(())
}

/// Per stage dependencies
#[derive(Debug)]
pub struct Stage {
    crates: Vec<String>,
    toml: Table,
}

/// A sysroot that will be built in "stages"
#[derive(Debug)]
pub struct Blueprint {
    stages: BTreeMap<i64, Stage>,
}

impl Blueprint {
    fn new() -> Self {
        Blueprint { stages: BTreeMap::new() }
    }

    fn from(toml: Option<&xargo::Toml>,
            target: &str,
            root: &Root,
            src: &Src)
            -> Result<Self> {
        let deps = match (toml.and_then(|t| t.dependencies()),
                          toml.and_then(|t| t.target_dependencies(target))) {
            (Some(value), Some(tvalue)) => {
                let mut deps = value.as_table()
                    .cloned()
                    .ok_or_else(|| {
                        format!("Xargo.toml: `dependencies` must be a table")
                    })?;

                let more_deps = tvalue.as_table()
                    .ok_or_else(|| {
                        format!("Xargo.toml: `target.{}.dependencies` must be \
                                 a table",
                                target)
                    })?;
                for (k, v) in more_deps {
                    if deps.insert(k.to_owned(), v.clone()).is_some() {
                        Err(format!("found duplicate dependency name {}, \
                                     but all dependencies must have a \
                                     unique name",
                                    k))?
                    }
                }

                deps
            }
            (Some(value), None) |
            (None, Some(value)) => {
                if let Some(table) = value.as_table() {
                    table.clone()
                } else {
                    Err(format!("Xargo.toml: target.{}.dependencies must be \
                                 a table",
                                target))?
                }
            }
            (None, None) => {
                // If no dependencies were listed, we assume `core` as the
                // only dependency
                let mut t = BTreeMap::new();
                t.insert("core".to_owned(), Value::Table(BTreeMap::new()));
                t
            }
        };

        let mut blueprint = Blueprint::new();
        for (k, v) in deps {
            if let Value::Table(mut map) = v {
                let stage = if let Some(value) = map.remove("stage") {
                    value.as_integer()
                        .ok_or_else(|| {
                            format!("dependencies.{}.stage must be an integer",
                                    k)
                        })?
                } else {
                    0
                };

                if let Some(path) = map.get_mut("path") {
                    let p = PathBuf::from(path.as_str()
                        .ok_or_else(|| {
                            format!("dependencies.{}.path must be a string", k)
                        })?);

                    if !p.is_absolute() {
                        *path = Value::String(root.path()
                            .join(&p)
                            .canonicalize()
                            .chain_err(|| {
                                format!("couldn't canonicalize {}", p.display())
                            })?
                            .display()
                            .to_string());
                    }
                }

                if !map.contains_key("path") && !map.contains_key("git") {
                    let path = src.path()
                        .join(format!("lib{}", k))
                        .display()
                        .to_string();

                    map.insert("path".to_owned(), Value::String(path));
                }

                blueprint.push(stage, k, map);
            } else {
                Err(format!("Xargo.toml: target.{}.dependencies.{} must be \
                             a table",
                            target,
                            k))?
            }
        }

        Ok(blueprint)
    }

    fn push(&mut self, stage: i64, krate: String, toml: Table) {
        let stage = self.stages.entry(stage).or_insert_with(|| {
            Stage {
                crates: vec![],
                toml: Table::new(),
            }
        });

        stage.toml.insert(krate.clone(), Value::Table(toml));
        stage.crates.push(krate);
    }

    fn hash<H>(&self, hasher: &mut H)
        where H: Hasher
    {
        for stage in self.stages.values() {
            for (k, v) in stage.toml.iter() {
                k.hash(hasher);
                v.to_string().hash(hasher);
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::marker::PhantomData;
use gl;
use gl::types::*;
use context::Context;
use types::GLError;

pub struct Texture<T: TextureType> {
    gl_id: GLuint,
    phantom: PhantomData<*mut T>
}
pub type Texture2d = Texture<Tx2d>;
pub type TextureCubeMap = Texture<TxCubeMap>;

impl<T: TextureType> Texture<T> {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl<T: TextureType> Drop for Texture<T> {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteTextures(1, &self.gl_id as *const GLuint);
        }
    }
}

impl Context {
    pub fn gen_texture<T: TextureType>(&self) -> Texture<T> {
        unsafe {
            let mut id : GLuint =  0;

            gl::GenTextures(1, &mut id as *mut GLuint);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`n` is negative",
                _ => "Unknown error"
            }

            Texture {
                gl_id: id,
                phantom: PhantomData
            }
        }
    }
}

pub trait ImageTargetType {
    fn gl_enum(&self) -> GLenum;
}

pub trait TextureType {
    type ImageTargetType: ImageTargetType;

    fn target() -> TextureBindingTarget;
}

pub struct Tx2d;

gl_enum! {
    pub gl_enum Tx2dImageTarget {
        Texture2d as TEXTURE_2D_TARGET = gl::TEXTURE_2D
    }
}

impl ImageTargetType for Tx2dImageTarget {
    fn gl_enum(&self) -> GLenum {
        self.gl_enum()
    }
}

impl TextureType for Tx2d {
    type ImageTargetType = Tx2dImageTarget;

    fn target() -> TextureBindingTarget {
        TextureBindingTarget::Texture2d
    }
}

pub struct TxCubeMap;

gl_enum! {
    pub gl_enum TxCubeMapImageTarget {
        CubeMapPositiveX as TEXTURE_CUBE_MAP_POSITIVE_X =
            gl::TEXTURE_CUBE_MAP_POSITIVE_X,
        CubeMapNegativeX as TEXTURE_CUBE_MAP_NEGATIVE_X =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_X,
        CubeMapPositiveY as TEXTURE_CUBE_MAP_POSITIVE_Y =
            gl::TEXTURE_CUBE_MAP_POSITIVE_Y,
        CubeMapNegativeY as TEXTURE_CUBE_MAP_NEGATIVE_Y =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_Y,
        CubeMapPositiveZ as TEXTURE_CUBE_MAP_POSITIVE_Z =
            gl::TEXTURE_CUBE_MAP_POSITIVE_Z,
        CubeMapNegativeZ as TEXTURE_CUBE_MAP_NEGATIVE_Z =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_Z
    }
}

impl ImageTargetType for TxCubeMapImageTarget {
    fn gl_enum(&self) -> GLenum {
        self.gl_enum()
    }
}

impl TextureType for TxCubeMap {
    type ImageTargetType = TxCubeMapImageTarget;

    fn target() -> TextureBindingTarget {
        TextureBindingTarget::TextureCubeMap
    }
}

gl_enum! {
    pub gl_enum TextureBindingTarget {
        Texture2d as TEXTURE_2D = gl::TEXTURE_2D,
        TextureCubeMap as TEXTURE_CUBE_MAP = gl::TEXTURE_CUBE_MAP
    }
}

pub trait TextureBinding {
    type TextureType: TextureType;

    fn target() -> TextureBindingTarget {
        Self::TextureType::target()
    }
}

pub struct Texture2dBinding<'a> {
    phantom: PhantomData<&'a mut Texture2d>
}

impl<'a> TextureBinding for Texture2dBinding<'a> {
    type TextureType = Tx2d;
}

pub struct TextureCubeMapBinding<'a> {
    phantom: PhantomData<&'a mut TextureCubeMap>
}

impl<'a> TextureBinding for TextureCubeMapBinding<'a> {
    type TextureType = TxCubeMap;
}

unsafe fn _bind_texture<T: TextureType>(texture: &mut Texture<T>) {
    gl::BindTexture(T::target().gl_enum(), texture.gl_id());
    dbg_gl_error! {
        GLError::InvalidEnum => "`target` is not one of the allowed values",
        GLError::InvalidOperation => "`texture` was created with a target that doesn't match `target`",
        _ => "Unknown error"
    }
}

pub struct Texture2dBinder;
impl Texture2dBinder {
    pub fn bind<'a>(&'a mut self, texture: &mut Texture2d)
        -> Texture2dBinding<'a>
    {
        unsafe {
            _bind_texture(texture);
        }
        Texture2dBinding { phantom: PhantomData }
    }
}

pub struct TextureCubeMapBinder;
impl TextureCubeMapBinder {
    pub fn bind<'a>(&'a mut self, texture: &mut TextureCubeMap)
        -> TextureCubeMapBinding<'a>
    {
        unsafe {
            _bind_texture(texture);
        }
        TextureCubeMapBinding { phantom: PhantomData }
    }
}
<commit_msg>Add `TextureBinding.image_2d()`<commit_after>use std::mem;
use std::marker::PhantomData;
use gl;
use gl::types::*;
use context::Context;
use image_data::Image2d;
use types::GLError;

pub struct Texture<T: TextureType> {
    gl_id: GLuint,
    phantom: PhantomData<*mut T>
}
pub type Texture2d = Texture<Tx2d>;
pub type TextureCubeMap = Texture<TxCubeMap>;

impl<T: TextureType> Texture<T> {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl<T: TextureType> Drop for Texture<T> {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteTextures(1, &self.gl_id as *const GLuint);
        }
    }
}

impl Context {
    pub fn gen_texture<T: TextureType>(&self) -> Texture<T> {
        unsafe {
            let mut id : GLuint =  0;

            gl::GenTextures(1, &mut id as *mut GLuint);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`n` is negative",
                _ => "Unknown error"
            }

            Texture {
                gl_id: id,
                phantom: PhantomData
            }
        }
    }
}

pub trait ImageTargetType {
    fn gl_enum(&self) -> GLenum;
}

pub trait TextureType {
    type ImageTargetType: ImageTargetType;

    fn target() -> TextureBindingTarget;
}

pub struct Tx2d;

gl_enum! {
    pub gl_enum Tx2dImageTarget {
        Texture2d as TEXTURE_2D_TARGET = gl::TEXTURE_2D
    }
}

impl ImageTargetType for Tx2dImageTarget {
    fn gl_enum(&self) -> GLenum {
        self.gl_enum()
    }
}

impl TextureType for Tx2d {
    type ImageTargetType = Tx2dImageTarget;

    fn target() -> TextureBindingTarget {
        TextureBindingTarget::Texture2d
    }
}

pub struct TxCubeMap;

gl_enum! {
    pub gl_enum TxCubeMapImageTarget {
        CubeMapPositiveX as TEXTURE_CUBE_MAP_POSITIVE_X =
            gl::TEXTURE_CUBE_MAP_POSITIVE_X,
        CubeMapNegativeX as TEXTURE_CUBE_MAP_NEGATIVE_X =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_X,
        CubeMapPositiveY as TEXTURE_CUBE_MAP_POSITIVE_Y =
            gl::TEXTURE_CUBE_MAP_POSITIVE_Y,
        CubeMapNegativeY as TEXTURE_CUBE_MAP_NEGATIVE_Y =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_Y,
        CubeMapPositiveZ as TEXTURE_CUBE_MAP_POSITIVE_Z =
            gl::TEXTURE_CUBE_MAP_POSITIVE_Z,
        CubeMapNegativeZ as TEXTURE_CUBE_MAP_NEGATIVE_Z =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_Z
    }
}

impl ImageTargetType for TxCubeMapImageTarget {
    fn gl_enum(&self) -> GLenum {
        self.gl_enum()
    }
}

impl TextureType for TxCubeMap {
    type ImageTargetType = TxCubeMapImageTarget;

    fn target() -> TextureBindingTarget {
        TextureBindingTarget::TextureCubeMap
    }
}

gl_enum! {
    pub gl_enum TextureBindingTarget {
        Texture2d as TEXTURE_2D = gl::TEXTURE_2D,
        TextureCubeMap as TEXTURE_CUBE_MAP = gl::TEXTURE_CUBE_MAP
    }
}

pub trait TextureBinding {
    type TextureType: TextureType;

    fn target() -> TextureBindingTarget {
        Self::TextureType::target()
    }

    fn image_2d<I>(&mut self,
                   level: i32,
                   target: <Self::TextureType as TextureType>::ImageTargetType,
                   img: &I)
        where I: Image2d
    {
        unsafe {
            let ptr = mem::transmute(img.textel_bytes().as_ptr());
            gl::TexImage2D(target.gl_enum(),
                           level as GLint,
                           img.format().textel_format.gl_enum() as GLint,
                           img.width() as i32,
                           img.height() as i32,
                           0,
                           img.format().textel_format.gl_enum(),
                           img.format().textel_type.gl_enum(),
                           ptr);
            dbg_gl_error! {
                GLError::InvalidEnum => "`target`, `format`, or `type` is not an accepted value",
                GLError::InvalidValue => "`target`, `level`, `internalformat`, `width`, `height`, or `border` is an invalid value",
                GLError::InvalidOperation => "`format` conflicts with either `internalformat` or `type`",
                _ => "Unknown error"
            }
        }
    }
}

pub struct Texture2dBinding<'a> {
    phantom: PhantomData<&'a mut Texture2d>
}

impl<'a> TextureBinding for Texture2dBinding<'a> {
    type TextureType = Tx2d;
}

pub struct TextureCubeMapBinding<'a> {
    phantom: PhantomData<&'a mut TextureCubeMap>
}

impl<'a> TextureBinding for TextureCubeMapBinding<'a> {
    type TextureType = TxCubeMap;
}

unsafe fn _bind_texture<T: TextureType>(texture: &mut Texture<T>) {
    gl::BindTexture(T::target().gl_enum(), texture.gl_id());
    dbg_gl_error! {
        GLError::InvalidEnum => "`target` is not one of the allowed values",
        GLError::InvalidOperation => "`texture` was created with a target that doesn't match `target`",
        _ => "Unknown error"
    }
}

pub struct Texture2dBinder;
impl Texture2dBinder {
    pub fn bind<'a>(&'a mut self, texture: &mut Texture2d)
        -> Texture2dBinding<'a>
    {
        unsafe {
            _bind_texture(texture);
        }
        Texture2dBinding { phantom: PhantomData }
    }
}

pub struct TextureCubeMapBinder;
impl TextureCubeMapBinder {
    pub fn bind<'a>(&'a mut self, texture: &mut TextureCubeMap)
        -> TextureCubeMapBinding<'a>
    {
        unsafe {
            _bind_texture(texture);
        }
        TextureCubeMapBinding { phantom: PhantomData }
    }
}
<|endoftext|>


<commit_before><commit_msg>Clippify the last changes<commit_after><|endoftext|>