<commit_before>use ahash::AHashMap;
use std::hash::Hash;
use std::marker::PhantomData;

use crate::entity::storage::{
    AsStorage, AsStorageMut, DependentStorage, Dispatch, Dynamic, Enumerate, Get, InnerKey, Insert,
    InsertWithKey, Key, Keyer, Mode, Remove, Static, StorageTarget,
};
use crate::entity::{Entity, Payload};

// TODO: Complete the `AsStorage` and `Dispatch` implementations to cover
//       `Keyer`s and dispatch to `IndependentStorage`.

pub struct HashStorage<E, R = (), P = Static>
where
    E: Entity,
    R: Default,
    P: Mode,
{
    inner: AHashMap<InnerKey<<E as Entity>::Key>, E>,
    keyer: R,
    phantom: PhantomData<P>,
}

impl<E> AsStorage<E> for HashStorage<E, (), Dynamic>
where
    E: Entity<Storage = Self>,
    InnerKey<E::Key>: Eq + Hash,
{
    fn as_storage(&self) -> &StorageTarget<E> {
        self
    }
}

impl<E, R> AsStorage<E> for HashStorage<E, R, Static>
where
    E: Entity<Storage = Self>,
    InnerKey<E::Key>: Eq + Hash,
    R: 'static + Default,
{
    fn as_storage(&self) -> &StorageTarget<E> {
        self
    }
}

impl<E> AsStorageMut<E> for HashStorage<E, (), Dynamic>
where
    E: Entity<Storage = Self>,
    InnerKey<E::Key>: Eq + Hash,
{
    fn as_storage_mut(&mut self) -> &mut StorageTarget<E> {
        self
    }
}

impl<E, R> AsStorageMut<E> for HashStorage<E, R, Static>
where
    E: Entity<Storage = Self>,
    InnerKey<E::Key>: Eq + Hash,
    R: 'static + Default,
{
    fn as_storage_mut(&mut self) -> &mut StorageTarget<E> {
        self
    }
}

impl<E, R, P> Default for HashStorage<E, R, P>
where
    E: Entity,
    R: Default,
    P: Mode,
{
    fn default() -> Self {
        HashStorage {
            inner: Default::default(),
            keyer: Default::default(),
            phantom: PhantomData,
        }
    }
}

#[cfg(not(all(nightly, feature = "unstable")))]
impl<E> Dispatch<E> for HashStorage<E, (), Dynamic>
where
    E: Entity<Storage = Self>,
    InnerKey<E::Key>: Eq + Hash,
{
    type Target = dyn 'static + DependentStorage<E>;
}

#[cfg(all(nightly, feature = "unstable"))]
#[rustfmt::skip]
impl<E> Dispatch<E> for HashStorage<E, (), Dynamic>
where
    E: Entity<Storage = Self>,
    InnerKey<E::Key>: Eq + Hash,
{
    type Target<'a> where E: 'a = dyn 'a + DependentStorage<E>;
}

#[cfg(not(all(nightly, feature = "unstable")))]
impl<E, R> Dispatch<E> for HashStorage<E, R, Static>
where
    E: Entity<Storage = Self>,
    InnerKey<E::Key>: Eq + Hash,
    R: 'static + Default,
{
    type Target = Self;
}

#[cfg(all(nightly, feature = "unstable"))]
#[rustfmt::skip]
impl<E, R> Dispatch<E> for HashStorage<E, R, Static>
where
    E: Entity<Storage = Self>,
    InnerKey<E::Key>: Eq + Hash,
    R: 'static + Default,
{
    type Target<'a> where E: 'a = Self;
}

impl<E, R, P> Enumerate<E> for HashStorage<E, R, P>
where
    E: Entity,
    InnerKey<E::Key>: Eq + Hash,
    R: Default,
    P: Mode,
{
    fn len(&self) -> usize {
        self.inner.len()
    }

    fn iter<'a>(&'a self) -> Box<dyn 'a + Iterator<Item = (E::Key, &E)>> {
        Box::new(
            self.inner
                .iter()
                .map(|(key, entity)| (E::Key::from_inner(*key), entity)),
        )
    }

    fn iter_mut<'a>(&'a mut self) -> Box<dyn 'a + Iterator<Item = (E::Key, &mut E::Data)>>
    where
        E: Payload,
    {
        Box::new(
            self.inner
                .iter_mut()
                .map(|(key, entity)| (E::Key::from_inner(*key), entity.get_mut())),
        )
    }
}

impl<E, R, P> Get<E> for HashStorage<E, R, P>
where
    E: Entity,
    InnerKey<E::Key>: Eq + Hash,
    R: Default,
    P: Mode,
{
    fn get(&self, key: &E::Key) -> Option<&E> {
        self.inner.get(&key.into_inner())
    }

    fn get_mut(&mut self, key: &E::Key) -> Option<&mut E> {
        self.inner.get_mut(&key.into_inner())
    }
}

impl<E, R, P> Insert<E> for HashStorage<E, R, P>
where
    E: Entity,
    InnerKey<E::Key>: Eq + Hash,
    R: Keyer<E::Key>,
    P: Mode,
{
    fn insert(&mut self, entity: E) -> E::Key {
        let key = self.keyer.next();
        self.inner.insert(key, entity);
        Key::from_inner(key)
    }
}

impl<E, P> InsertWithKey<E> for HashStorage<E, (), P>
where
    E: Entity,
    InnerKey<E::Key>: Eq + Hash,
    P: Mode,
{
    fn insert_with_key(&mut self, key: &E::Key, entity: E) -> Option<E> {
        self.inner.insert(key.into_inner(), entity)
    }
}

impl<E, R, P> Remove<E> for HashStorage<E, R, P>
where
    E: Entity,
    InnerKey<E::Key>: Eq + Hash,
    R: Default,
    P: Mode,
{
    fn remove(&mut self, key: &E::Key) -> Option<E> {
        self.inner.remove(&key.into_inner())
    }
}
<commit_msg>Implement `AsStorage` and `Dispatch` traits based on keying for `HashStorage`.<commit_after>use ahash::AHashMap;
use std::hash::Hash;
use std::marker::PhantomData;

use crate::entity::storage::{
    AsStorage, AsStorageMut, DependentStorage, Dispatch, Dynamic, Enumerate, Get, IncrementalKeyer,
    IndependentStorage, InnerKey, Insert, InsertWithKey, Key, Keyer, Mode, Remove, Static,
    StorageTarget,
};
use crate::entity::{Entity, Payload};

// TODO: The `Keyer` parameter `R` of `HashStorage` cannot be parameterized when
//       implementing the `AsStorage` and `Dispatch` traits even if the
//       conflicting implementations use a private local type or the `Keyer`
//       trait is private. Instead, this is implemented more specifically for
//       `IncrementalKeyer`. Perhaps this will be possible in the future.
//
//       See https://github.com/rust-lang/rust/issues/48869

pub struct HashStorage<E, R = (), P = Static>
where
    E: Entity,
    R: Default,
    P: Mode,
{
    inner: AHashMap<InnerKey<<E as Entity>::Key>, E>,
    keyer: R,
    phantom: PhantomData<P>,
}

impl<E> AsStorage<E> for HashStorage<E, (), Dynamic>
where
    E: Entity<Storage = Self>,
    InnerKey<E::Key>: Eq + Hash,
{
    fn as_storage(&self) -> &StorageTarget<E> {
        self
    }
}

impl<E> AsStorage<E> for HashStorage<E, IncrementalKeyer, Dynamic>
where
    E: Entity<Storage = Self>,
    E::Key: Key<Inner = u64>,
{
    fn as_storage(&self) -> &StorageTarget<E> {
        self
    }
}

impl<E, R> AsStorage<E> for HashStorage<E, R, Static>
where
    E: Entity<Storage = Self>,
    InnerKey<E::Key>: Eq + Hash,
    R: 'static + Default,
{
    fn as_storage(&self) -> &StorageTarget<E> {
        self
    }
}

impl<E> AsStorageMut<E> for HashStorage<E, (), Dynamic>
where
    E: Entity<Storage = Self>,
    InnerKey<E::Key>: Eq + Hash,
{
    fn as_storage_mut(&mut self) -> &mut StorageTarget<E> {
        self
    }
}

impl<E> AsStorageMut<E> for HashStorage<E, IncrementalKeyer, Dynamic>
where
    E: Entity<Storage = Self>,
    E::Key: Key<Inner = u64>,
{
    fn as_storage_mut(&mut self) -> &mut StorageTarget<E> {
        self
    }
}

impl<E, R> AsStorageMut<E> for HashStorage<E, R, Static>
where
    E: Entity<Storage = Self>,
    InnerKey<E::Key>: Eq + Hash,
    R: 'static + Default,
{
    fn as_storage_mut(&mut self) -> &mut StorageTarget<E> {
        self
    }
}

impl<E, R, P> Default for HashStorage<E, R, P>
where
    E: Entity,
    R: Default,
    P: Mode,
{
    fn default() -> Self {
        HashStorage {
            inner: Default::default(),
            keyer: Default::default(),
            phantom: PhantomData,
        }
    }
}

#[cfg(not(all(nightly, feature = "unstable")))]
impl<E> Dispatch<E> for HashStorage<E, (), Dynamic>
where
    E: Entity<Storage = Self>,
    InnerKey<E::Key>: Eq + Hash,
{
    type Target = dyn 'static + DependentStorage<E>;
}

#[cfg(all(nightly, feature = "unstable"))]
#[rustfmt::skip]
impl<E> Dispatch<E> for HashStorage<E, (), Dynamic>
where
    E: Entity<Storage = Self>,
    InnerKey<E::Key>: Eq + Hash,
{
    type Target<'a> where E: 'a = dyn 'a + DependentStorage<E>;
}

#[cfg(not(all(nightly, feature = "unstable")))]
impl<E> Dispatch<E> for HashStorage<E, IncrementalKeyer, Dynamic>
where
    E: Entity<Storage = Self>,
    E::Key: Key<Inner = u64>,
{
    type Target = dyn 'static + IndependentStorage<E>;
}

#[cfg(all(nightly, feature = "unstable"))]
#[rustfmt::skip]
impl<E> Dispatch<E> for HashStorage<E, IncrementalKeyer, Dynamic>
where
    E: Entity<Storage = Self>,
    E::Key: Key<Inner = u64>,
{
    type Target<'a> where E: 'a = dyn 'a + IndependentStorage<E>;
}

#[cfg(not(all(nightly, feature = "unstable")))]
impl<E, R> Dispatch<E> for HashStorage<E, R, Static>
where
    E: Entity<Storage = Self>,
    InnerKey<E::Key>: Eq + Hash,
    R: 'static + Default,
{
    type Target = Self;
}

#[cfg(all(nightly, feature = "unstable"))]
#[rustfmt::skip]
impl<E, R> Dispatch<E> for HashStorage<E, R, Static>
where
    E: Entity<Storage = Self>,
    InnerKey<E::Key>: Eq + Hash,
    R: 'static + Default,
{
    type Target<'a> where E: 'a = Self;
}

impl<E, R, P> Enumerate<E> for HashStorage<E, R, P>
where
    E: Entity,
    InnerKey<E::Key>: Eq + Hash,
    R: Default,
    P: Mode,
{
    fn len(&self) -> usize {
        self.inner.len()
    }

    fn iter<'a>(&'a self) -> Box<dyn 'a + Iterator<Item = (E::Key, &E)>> {
        Box::new(
            self.inner
                .iter()
                .map(|(key, entity)| (E::Key::from_inner(*key), entity)),
        )
    }

    fn iter_mut<'a>(&'a mut self) -> Box<dyn 'a + Iterator<Item = (E::Key, &mut E::Data)>>
    where
        E: Payload,
    {
        Box::new(
            self.inner
                .iter_mut()
                .map(|(key, entity)| (E::Key::from_inner(*key), entity.get_mut())),
        )
    }
}

impl<E, R, P> Get<E> for HashStorage<E, R, P>
where
    E: Entity,
    InnerKey<E::Key>: Eq + Hash,
    R: Default,
    P: Mode,
{
    fn get(&self, key: &E::Key) -> Option<&E> {
        self.inner.get(&key.into_inner())
    }

    fn get_mut(&mut self, key: &E::Key) -> Option<&mut E> {
        self.inner.get_mut(&key.into_inner())
    }
}

impl<E, R, P> Insert<E> for HashStorage<E, R, P>
where
    E: Entity,
    InnerKey<E::Key>: Eq + Hash,
    R: Keyer<E::Key>,
    P: Mode,
{
    fn insert(&mut self, entity: E) -> E::Key {
        let key = self.keyer.next();
        self.inner.insert(key, entity);
        Key::from_inner(key)
    }
}

impl<E, P> InsertWithKey<E> for HashStorage<E, (), P>
where
    E: Entity,
    InnerKey<E::Key>: Eq + Hash,
    P: Mode,
{
    fn insert_with_key(&mut self, key: &E::Key, entity: E) -> Option<E> {
        self.inner.insert(key.into_inner(), entity)
    }
}

impl<E, R, P> Remove<E> for HashStorage<E, R, P>
where
    E: Entity,
    InnerKey<E::Key>: Eq + Hash,
    R: Default,
    P: Mode,
{
    fn remove(&mut self, key: &E::Key) -> Option<E> {
        self.inner.remove(&key.into_inner())
    }
}
<|endoftext|>


<commit_before>mod thread_guard;

use std::fs;
use std::fmt;
use std::io;
use std::io::prelude::*;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{channel, Sender, Receiver, TryRecvError};
use std::thread;
use std::time::{Duration, Instant};
use std::ops::Deref;

use nonblock::NonBlockingReader;
use self::thread_guard::*;
use super::Packet;

pub enum ConnectionEvent {
    Packet(Packet),
    InvalidPacket(Packet),
    ReadError(io::Error),
    BadData(Vec<u8>),
    Heartbeat
}

impl fmt::Display for ConnectionEvent {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &ConnectionEvent::Packet(ref p) => write!(f, "{}", p),
            &ConnectionEvent::InvalidPacket(ref p) => write!(f, "Invalid! {}", p),
            &ConnectionEvent::ReadError(ref err) => write!(f, "ERROR! [{}]", err),
            &ConnectionEvent::BadData(ref data) => write!(f, "BAD DATA! [{:?}]", data),
            &ConnectionEvent::Heartbeat => write!(f, "Tick Tock")
        }
    }
}

pub struct Connection {
    pub tty_path: String,
    tty_writer: fs::File,
    receiver: Receiver<ConnectionEvent>,
    kill_thread_signal: Arc<Mutex<bool>>,
    reader_thread_handle: Option<ThreadHandle<()>>,
}

impl Connection {
    pub fn new(tty_path: &str, heartbeat_milliseconds: Option<u64>) -> io::Result<Connection> {
        let (tx, rx) = channel::<ConnectionEvent>();

        let kill_signal = Arc::new(Mutex::new(false));

        let tty_reader = try!(fs::OpenOptions::new()
                        .create(false)
                        .read(true)
                        .write(false)
                        .open(tty_path));

        let tty_writer = try!(fs::OpenOptions::new()
                        .create(false)
                        .read(false)
                        .write(true)
                        .open(tty_path));

        let nonblocking_reader = NonBlockingReader::from_fd(tty_reader).expect("error creating non-blocking reader");

        let reader_thread = build_connection_read_thread(nonblocking_reader, tx, heartbeat_milliseconds, kill_signal.clone());

        Ok(Connection {
            tty_path: tty_path.to_string(),
            tty_writer: tty_writer,
            receiver: rx,
            kill_thread_signal: kill_signal,
            reader_thread_handle: Some(reader_thread)
        })
    }

    pub fn send(&mut self, p: &Packet) -> io::Result<usize> {
        match self.is_ok() {
            true => self.tty_writer.write(&p.data[..]),
            false => Err(io::Error::new(io::ErrorKind::ConnectionAborted, "worker threads have stopped"))
        }
    }

    pub fn wait(&self) -> io::Result<ConnectionEvent> {
        match self.is_ok() {
            false => Err(io::Error::new(io::ErrorKind::ConnectionAborted, "worker threads have stopped")),
            true => {
                Ok(self.receiver.recv().unwrap())
            }
        }
    }

    pub fn get_events(&self) -> io::Result<Vec<ConnectionEvent>> {
        match self.is_ok() {
            false => Err(io::Error::new(io::ErrorKind::ConnectionAborted, "worker threads have stopped")),
            true => {
                let mut data: Vec<ConnectionEvent> = vec!();

                loop {
                    match self.receiver.try_recv() {
                        Ok(evt) => { data.push(evt) },
                        Err(TryRecvError::Empty) => { break; },
                        Err(TryRecvError::Disconnected) => { panic!("channels should not disconnect") }
                    }
                }

                Ok(data)
            }
        }
    }

    pub fn is_ok(&self) -> bool {
        let mut ret = true;

        match &self.reader_thread_handle {
            &Some(ref h) => {
                ret = ret && match h.status.lock().unwrap().deref() {
                    &ThreadStatus::Ok => true,
                    _ => false
                }
            },
            _ => {}
        }
        ret
    }
}

impl Drop for Connection {
    fn drop(&mut self) {

        {
            *self.kill_thread_signal.lock().unwrap() = true;
        }

        match self.reader_thread_handle.take() {
            Some(th) => {
                th.handle.join().unwrap();
            },
            None => {}
        }
    }
}

fn build_connection_read_thread(mut reader: NonBlockingReader<fs::File>, sender: Sender<ConnectionEvent>, heartbeat: Option<u64>, kill_signal: Arc<Mutex<bool>>) -> ThreadHandle<()> {
    guard_thread("reader_thread", move || {
        let mut packet_buffer: Vec<u8> = Vec::new();
        let mut read_buffer: Vec<u8> = Vec::new();
        let mut last_read = Instant::now();
        let mut last_heartbeat = Instant::now();

        while !*kill_signal.lock().unwrap() {

            if packet_buffer.len() > 0 && last_read.elapsed().as_secs() > 2 {
                let evt = ConnectionEvent::BadData(packet_buffer.clone());
                sender.send(evt).unwrap();
                packet_buffer.clear();
            }

            read_buffer.clear();

            match reader.read_available(&mut read_buffer) {
                Err(e) => {
                    let evt = ConnectionEvent::ReadError(e);
                    sender.send(evt).unwrap();
                },
                Ok(bytes) if bytes > 0 => {
                    last_read = Instant::now();
                    for x in 0 .. bytes {
                        packet_buffer.push(read_buffer[x]);
                    }
                },
                Ok(_) => {} // do nothing for 0 bytes read
            }

            while packet_buffer.len() >= 128 {
                let data: Vec<u8> = packet_buffer.drain(0..128).collect();
                let p = Packet::from_bytes(&data);

                if p.is_checksum_valid() {
                    let evt = ConnectionEvent::Packet(p);
                    sender.send(evt).unwrap();
                } else {
                    let evt = ConnectionEvent::InvalidPacket(p);
                    sender.send(evt).unwrap();
                }
            }

            match heartbeat {
                Some(ms) => {
                    if last_heartbeat.elapsed() >= Duration::from_millis(ms) {
                        let evt = ConnectionEvent::Heartbeat;
                        sender.send(evt).unwrap();
                        last_heartbeat = Instant::now();
                    }
                },
                None => {}
            }

            thread::sleep(Duration::from_millis(100));
        }
        ()
    })
}
<commit_msg>debug<commit_after>mod thread_guard;

use std::fs;
use std::fmt;
use std::io;
use std::io::prelude::*;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{channel, Sender, Receiver, TryRecvError};
use std::thread;
use std::time::{Duration, Instant};
use std::ops::Deref;

use nonblock::NonBlockingReader;
use self::thread_guard::*;
use super::Packet;

pub enum ConnectionEvent {
    Packet(Packet),
    InvalidPacket(Packet),
    ReadError(io::Error),
    BadData(Vec<u8>),
    Heartbeat
}

impl fmt::Display for ConnectionEvent {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &ConnectionEvent::Packet(ref p) => write!(f, "{}", p),
            &ConnectionEvent::InvalidPacket(ref p) => write!(f, "Invalid! {}", p),
            &ConnectionEvent::ReadError(ref err) => write!(f, "ERROR! [{}]", err),
            &ConnectionEvent::BadData(ref data) => write!(f, "BAD DATA! [{:?}]", data),
            &ConnectionEvent::Heartbeat => write!(f, "Tick Tock")
        }
    }
}

pub struct Connection {
    pub tty_path: String,
    tty_writer: fs::File,
    receiver: Receiver<ConnectionEvent>,
    kill_thread_signal: Arc<Mutex<bool>>,
    reader_thread_handle: Option<ThreadHandle<()>>,
}

impl Connection {
    pub fn new(tty_path: &str, heartbeat_milliseconds: Option<u64>) -> io::Result<Connection> {
        let (tx, rx) = channel::<ConnectionEvent>();

        let kill_signal = Arc::new(Mutex::new(false));

        let tty_reader = try!(fs::OpenOptions::new()
                        .create(false)
                        .read(true)
                        .write(false)
                        .open(tty_path));

        let tty_writer = try!(fs::OpenOptions::new()
                        .create(false)
                        .read(false)
                        .write(true)
                        .open(tty_path));

        let nonblocking_reader = NonBlockingReader::from_fd(tty_reader).expect("error creating non-blocking reader");

        let reader_thread = build_connection_read_thread(nonblocking_reader, tx, heartbeat_milliseconds, kill_signal.clone());

        Ok(Connection {
            tty_path: tty_path.to_string(),
            tty_writer: tty_writer,
            receiver: rx,
            kill_thread_signal: kill_signal,
            reader_thread_handle: Some(reader_thread)
        })
    }

    pub fn send(&mut self, p: &Packet) -> io::Result<usize> {
        match self.is_ok() {
            true => self.tty_writer.write(&p.data[..]),
            false => Err(io::Error::new(io::ErrorKind::ConnectionAborted, "worker threads have stopped"))
        }
    }

    pub fn wait(&self) -> io::Result<ConnectionEvent> {
        match self.is_ok() {
            false => Err(io::Error::new(io::ErrorKind::ConnectionAborted, "worker threads have stopped")),
            true => {
                Ok(self.receiver.recv().unwrap())
            }
        }
    }

    pub fn get_events(&self) -> io::Result<Vec<ConnectionEvent>> {
        match self.is_ok() {
            false => Err(io::Error::new(io::ErrorKind::ConnectionAborted, "worker threads have stopped")),
            true => {
                let mut data: Vec<ConnectionEvent> = vec!();

                loop {
                    match self.receiver.try_recv() {
                        Ok(evt) => { data.push(evt) },
                        Err(TryRecvError::Empty) => { break; },
                        Err(TryRecvError::Disconnected) => { panic!("channels should not disconnect") }
                    }
                }

                Ok(data)
            }
        }
    }

    pub fn is_ok(&self) -> bool {
        let mut ret = true;

        match &self.reader_thread_handle {
            &Some(ref h) => {
                ret = ret && match h.status.lock().unwrap().deref() {
                    &ThreadStatus::Ok => true,
                    _ => false
                }
            },
            _ => {}
        }
        ret
    }
}

impl Drop for Connection {
    fn drop(&mut self) {

        {
            *self.kill_thread_signal.lock().unwrap() = true;
        }

        match self.reader_thread_handle.take() {
            Some(th) => {
                th.handle.join().unwrap();
            },
            None => {}
        }
    }
}

fn build_connection_read_thread(mut reader: NonBlockingReader<fs::File>, sender: Sender<ConnectionEvent>, heartbeat: Option<u64>, kill_signal: Arc<Mutex<bool>>) -> ThreadHandle<()> {
    guard_thread("reader_thread", move || {
        let mut packet_buffer: Vec<u8> = Vec::new();
        let mut read_buffer: Vec<u8> = Vec::new();
        let mut last_read = Instant::now();
        let mut last_heartbeat = Instant::now();

        while !*kill_signal.lock().unwrap() {

            println!("a");

            if packet_buffer.len() > 0 && last_read.elapsed().as_secs() > 2 {
                let evt = ConnectionEvent::BadData(packet_buffer.clone());
                sender.send(evt).unwrap();
                packet_buffer.clear();
            }

            println!("b");

            read_buffer.clear();

            match reader.read_available(&mut read_buffer) {
                Err(e) => {
                    let evt = ConnectionEvent::ReadError(e);
                    sender.send(evt).unwrap();
                },
                Ok(bytes) if bytes > 0 => {
                    last_read = Instant::now();
                    for x in 0 .. bytes {
                        packet_buffer.push(read_buffer[x]);
                    }
                },
                Ok(_) => {} // do nothing for 0 bytes read
            }

            println!("c");

            while packet_buffer.len() >= 128 {
                let data: Vec<u8> = packet_buffer.drain(0..128).collect();
                let p = Packet::from_bytes(&data);

                if p.is_checksum_valid() {
                    let evt = ConnectionEvent::Packet(p);
                    sender.send(evt).unwrap();
                } else {
                    let evt = ConnectionEvent::InvalidPacket(p);
                    sender.send(evt).unwrap();
                }
            }

            println!("d");

            match heartbeat {
                Some(ms) => {
                    if last_heartbeat.elapsed() >= Duration::from_millis(ms) {
                        let evt = ConnectionEvent::Heartbeat;
                        sender.send(evt).unwrap();
                        last_heartbeat = Instant::now();
                    }
                },
                None => {}
            }

            println!("e");

            thread::sleep(Duration::from_millis(100));

            println!("f");
        }
        ()
    })
}
<|endoftext|>


<commit_before>use std::fmt::{Show,Formatter};
use std::fmt;
use std::hash::Hasher;
use std::hash::sip::SipHasher;
use std::str;

use core::source::{Source, SourceId, GitKind, Location, Remote, Local};
use core::{Package,PackageId,Summary};
use util::{CargoResult, Config, to_hex};
use sources::PathSource;
use sources::git::utils::{GitReference,GitRemote,Master,Other};

/* TODO: Refactor GitSource to delegate to a PathSource
 */
pub struct GitSource<'a, 'b> {
    remote: GitRemote,
    reference: GitReference,
    db_path: Path,
    checkout_path: Path,
    path_source: PathSource,
    config: &'a mut Config<'b>
}

impl<'a, 'b> GitSource<'a, 'b> {
    pub fn new<'a, 'b>(source_id: &SourceId, config: &'a mut Config<'b>) -> GitSource<'a, 'b> {
        assert!(source_id.is_git(), "id is not git, id={}", source_id);

        let reference = match source_id.kind {
            GitKind(ref reference) => reference,
            _ => fail!("Not a git source; id={}", source_id)
        };

        let remote = GitRemote::new(source_id.get_location());
        let ident = ident(source_id.get_location());

        let db_path = config.git_db_path()
            .join(ident.as_slice());

        let checkout_path = config.git_checkout_path()
            .join(ident.as_slice()).join(reference.as_slice());

        let path_source = PathSource::new(&checkout_path, source_id);

        GitSource {
            remote: remote,
            reference: GitReference::for_str(reference.as_slice()),
            db_path: db_path,
            checkout_path: checkout_path,
            path_source: path_source,
            config: config
        }
    }

    pub fn get_namespace<'a>(&'a self) -> &'a Location {
        self.remote.get_location()
    }
}

fn ident(location: &Location) -> String {
    let hasher = SipHasher::new_with_keys(0,0);

    // FIXME: this really should be able to not use to_str() everywhere, but the
    //        compiler seems to currently ask for static lifetimes spuriously.
    //        Perhaps related to rust-lang/rust#15144
    let ident = match *location {
        Local(ref path) => {
            let last = path.components().last().unwrap();
            str::from_utf8(last).unwrap().to_str()
        }
        Remote(ref url) => url.path.as_slice().split('/').last().unwrap().to_str()
    };

    let ident = if ident.as_slice() == "" {
        "_empty".to_string()
    } else {
        ident
    };

    format!("{}-{}", ident, to_hex(hasher.hash(&location.to_str())))
}

impl<'a, 'b> Show for GitSource<'a, 'b> {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        try!(write!(f, "git repo at {}", self.remote.get_location()));

        match self.reference {
            Master => Ok(()),
            Other(ref reference) => write!(f, " ({})", reference)
        }
    }
}

impl<'a, 'b> Source for GitSource<'a, 'b> {
    fn update(&mut self) -> CargoResult<()> {
        let should_update = self.config.update_remotes() || {
            !self.remote.has_ref(&self.db_path, self.reference.as_slice()).is_ok()
        };

        let repo = if should_update {
            try!(self.config.shell().status("Updating",
                format!("git repository `{}`", self.remote.get_location())));

            log!(5, "updating git source `{}`", self.remote);
            try!(self.remote.checkout(&self.db_path))
        } else {
            self.remote.db_at(&self.db_path)
        };

        try!(repo.copy_to(self.reference.as_slice(), &self.checkout_path));

        self.path_source.update()
    }

    fn list(&self) -> CargoResult<Vec<Summary>> {
        self.path_source.list()
    }

    fn download(&self, _: &[PackageId]) -> CargoResult<()> {
        // TODO: assert! that the PackageId is contained by the source
        Ok(())
    }

    fn get(&self, ids: &[PackageId]) -> CargoResult<Vec<Package>> {
        log!(5, "getting packages for package ids `{}` from `{}`", ids, self.remote);
        self.path_source.get(ids)
    }

    fn fingerprint(&self, _pkg: &Package) -> CargoResult<String> {
        let db = self.remote.db_at(&self.db_path);
        db.rev_for(self.reference.as_slice())
    }
}

#[cfg(test)]
mod test {
    use url;
    use url::Url;
    use core::source::Remote;
    use super::ident;

    #[test]
    pub fn test_url_to_path_ident_with_path() {
        let ident = ident(&Remote(url("https://github.com/carlhuda/cargo")));
        assert_eq!(ident.as_slice(), "cargo-0eed735c8ffd7c88");
    }

    #[test]
    pub fn test_url_to_path_ident_without_path() {
        let ident = ident(&Remote(url("https://github.com")));
        assert_eq!(ident.as_slice(), "_empty-fc065c9b6b16fc00");
    }

    fn url(s: &str) -> Url {
        url::from_str(s).unwrap()
    }
}
<commit_msg>auto merge of #90 : brson/cargo/fix, r=alexcrichton<commit_after>use std::fmt::{Show,Formatter};
use std::fmt;
use std::hash::Hasher;
use std::hash::sip::SipHasher;
use std::str;

use core::source::{Source, SourceId, GitKind, Location, Remote, Local};
use core::{Package,PackageId,Summary};
use util::{CargoResult, Config, to_hex};
use sources::PathSource;
use sources::git::utils::{GitReference,GitRemote,Master,Other};

/* TODO: Refactor GitSource to delegate to a PathSource
 */
pub struct GitSource<'a, 'b> {
    remote: GitRemote,
    reference: GitReference,
    db_path: Path,
    checkout_path: Path,
    path_source: PathSource,
    config: &'a mut Config<'b>
}

impl<'a, 'b> GitSource<'a, 'b> {
    pub fn new<'a, 'b>(source_id: &SourceId, config: &'a mut Config<'b>) -> GitSource<'a, 'b> {
        assert!(source_id.is_git(), "id is not git, id={}", source_id);

        let reference = match source_id.kind {
            GitKind(ref reference) => reference,
            _ => fail!("Not a git source; id={}", source_id)
        };

        let remote = GitRemote::new(source_id.get_location());
        let ident = ident(source_id.get_location());

        let db_path = config.git_db_path()
            .join(ident.as_slice());

        let checkout_path = config.git_checkout_path()
            .join(ident.as_slice()).join(reference.as_slice());

        let path_source = PathSource::new(&checkout_path, source_id);

        GitSource {
            remote: remote,
            reference: GitReference::for_str(reference.as_slice()),
            db_path: db_path,
            checkout_path: checkout_path,
            path_source: path_source,
            config: config
        }
    }

    pub fn get_namespace<'a>(&'a self) -> &'a Location {
        self.remote.get_location()
    }
}

fn ident(location: &Location) -> String {
    let hasher = SipHasher::new_with_keys(0,0);

    // FIXME: this really should be able to not use to_str() everywhere, but the
    //        compiler seems to currently ask for static lifetimes spuriously.
    //        Perhaps related to rust-lang/rust#15144
    let ident = match *location {
        Local(ref path) => {
            let last = path.components().last().unwrap();
            str::from_utf8(last).unwrap().to_str()
        }
        Remote(ref url) => {
            let path = canonicalize_url(url.path.as_slice());
            path.as_slice().split('/').last().unwrap().to_str()
        }
    };

    let ident = if ident.as_slice() == "" {
        "_empty".to_string()
    } else {
        ident
    };

    let location = canonicalize_url(location.to_str().as_slice());

    format!("{}-{}", ident, to_hex(hasher.hash(&location.as_slice())))
}

fn strip_trailing_slash<'a>(path: &'a str) -> &'a str {
    // Remove the trailing '/' so that 'split' doesn't give us
    // an empty string, making '../foo/' and '../foo' both
    // result in the name 'foo' (#84)
    if path.as_bytes().last() != Some(&('/' as u8)) {
        path.clone()
    } else {
        path.slice(0, path.len() - 1)
    }
}

// Some hacks and heuristics for making equivalent URLs hash the same
fn canonicalize_url(url: &str) -> String {
    let url = strip_trailing_slash(url);

    // HACKHACK: For github URL's specifically just lowercase
    // everything.  GitHub traits both the same, but they hash
    // differently, and we're gonna be hashing them. This wants a more
    // general solution, and also we're almost certainly not using the
    // same case conversion rules that GitHub does. (#84)

    let lower_url = url.chars().map(|c|c.to_lowercase()).collect::<String>();
    let url = if lower_url.as_slice().contains("github.com") {
        lower_url
    } else {
        url.to_string()
    };

    // Repos generally can be accessed with or w/o '.git'
    let url = if !url.as_slice().ends_with(".git") {
        url
    } else {
        url.as_slice().slice(0, url.len() - 4).to_string()
    };

    return url;
}

impl<'a, 'b> Show for GitSource<'a, 'b> {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        try!(write!(f, "git repo at {}", self.remote.get_location()));

        match self.reference {
            Master => Ok(()),
            Other(ref reference) => write!(f, " ({})", reference)
        }
    }
}

impl<'a, 'b> Source for GitSource<'a, 'b> {
    fn update(&mut self) -> CargoResult<()> {
        let should_update = self.config.update_remotes() || {
            !self.remote.has_ref(&self.db_path, self.reference.as_slice()).is_ok()
        };

        let repo = if should_update {
            try!(self.config.shell().status("Updating",
                format!("git repository `{}`", self.remote.get_location())));

            log!(5, "updating git source `{}`", self.remote);
            try!(self.remote.checkout(&self.db_path))
        } else {
            self.remote.db_at(&self.db_path)
        };

        try!(repo.copy_to(self.reference.as_slice(), &self.checkout_path));

        self.path_source.update()
    }

    fn list(&self) -> CargoResult<Vec<Summary>> {
        self.path_source.list()
    }

    fn download(&self, _: &[PackageId]) -> CargoResult<()> {
        // TODO: assert! that the PackageId is contained by the source
        Ok(())
    }

    fn get(&self, ids: &[PackageId]) -> CargoResult<Vec<Package>> {
        log!(5, "getting packages for package ids `{}` from `{}`", ids, self.remote);
        self.path_source.get(ids)
    }

    fn fingerprint(&self, _pkg: &Package) -> CargoResult<String> {
        let db = self.remote.db_at(&self.db_path);
        db.rev_for(self.reference.as_slice())
    }
}

#[cfg(test)]
mod test {
    use url;
    use url::Url;
    use core::source::Remote;
    use super::ident;

    #[test]
    pub fn test_url_to_path_ident_with_path() {
        let ident = ident(&Remote(url("https://github.com/carlhuda/cargo")));
        assert_eq!(ident.as_slice(), "cargo-0eed735c8ffd7c88");
    }

    #[test]
    pub fn test_url_to_path_ident_without_path() {
        let ident = ident(&Remote(url("https://github.com")));
        assert_eq!(ident.as_slice(), "_empty-fc065c9b6b16fc00");
    }

    #[test]
    fn test_canonicalize_idents_by_stripping_trailing_url_slash() {
        let ident1 = ident(&Remote(url("https://github.com/PistonDevelopers/piston/")));
        let ident2 = ident(&Remote(url("https://github.com/PistonDevelopers/piston")));
        assert_eq!(ident1, ident2);
    }

    #[test]
    fn test_canonicalize_idents_by_lowercasing_github_urls() {
        let ident1 = ident(&Remote(url("https://github.com/PistonDevelopers/piston")));
        let ident2 = ident(&Remote(url("https://github.com/pistondevelopers/piston")));
        assert_eq!(ident1, ident2);
    }

    #[test]
    fn test_canonicalize_idents_by_stripping_dot_git() {
        let ident1 = ident(&Remote(url("https://github.com/PistonDevelopers/piston")));
        let ident2 = ident(&Remote(url("https://github.com/PistonDevelopers/piston.git")));
        assert_eq!(ident1, ident2);
    }

    fn url(s: &str) -> Url {
        url::from_str(s).unwrap()
    }
}
<|endoftext|>


<commit_before>extern crate piston_window;
extern crate gfx_device_gl;
extern crate gfx_graphics;

use piston_window::Transformed;

use piston_window as pw;

mod object;
use object::Object;

struct Game {
    rotation: f64,
    player: Object,
    up: bool,
    down: bool,
    left: bool,
    right: bool,
}

impl Game {
    fn new() -> Game {
        Game { rotation: 0.0, player: Object::new(), up: false, down: false, left: false, right: false }
    }

    fn on_update(&mut self, args: &pw::UpdateArgs) {
        self.rotation += 3.0 * args.dt;

        if self.up   { self.player.mov(0.0, -150.0 * args.dt); }
        if self.down { self.player.mov(0.0, 150.0 * args.dt); }
        if self.left { self.player.mov(-150.0 * args.dt, 0.0); }
        if self.right { self.player.mov(150.0 * args.dt, 0.0); }
    }

    fn on_draw(&mut self, args: &pw::RenderArgs, window: &pw::PistonWindow) {
        window.draw_2d(|context, graphics| {
            pw::clear([0.0, 0.0, 0.0, 1.0], graphics);

            let center = context.transform.trans((args.width / 2) as f64, (args.height / 2) as f64);
            self.player.render(graphics, &center);
        });
    }

    fn on_input(&mut self, input: &pw::Input) {
        use piston_window::{Button, Key};

        match input {
            &pw::Input::Press(button) => {
                match button {
                    Button::Keyboard(Key::Up) => { self.up = true; }
                    Button::Keyboard(Key::Down) => { self.down = true; }
                    Button::Keyboard(Key::Left) => { self.left = true; }
                    Button::Keyboard(Key::Right) => { self.right = true; }
                    _ => {}
                }
            }
            &pw::Input::Release(button) => {
                match button {
                    Button::Keyboard(Key::Up) => { self.up = false; }
                    Button::Keyboard(Key::Down) => { self.down = false; }
                    Button::Keyboard(Key::Left) => { self.left = false; }
                    Button::Keyboard(Key::Right) => { self.right = false; }
                    _ => {}
                }
            }
            _ => {}
        }
    }
}

fn main() {
    use piston_window::Event;

    let window: pw::PistonWindow = pw::WindowSettings::new("rrthozopsi", [600, 600])
        .exit_on_esc(true)
        .build()
        .unwrap();

    let mut game = Game::new();

    for w in window {
        match w.event {
            Some(Event::Update(args)) => { game.on_update(&args); }
            Some(Event::Render(args)) => { game.on_draw(&args, &w); }
            Some(Event::Input(input)) => { game.on_input(&input); }
            _ => {}
        }
    }
}
<commit_msg>Rotation has been removed<commit_after>extern crate piston_window;
extern crate gfx_device_gl;
extern crate gfx_graphics;

use piston_window::Transformed;

use piston_window as pw;

mod object;
use object::Object;

struct Game {
    player: Object,
    up: bool,
    down: bool,
    left: bool,
    right: bool,
}

impl Game {
    fn new() -> Game {
        Game { player: Object::new(), up: false, down: false, left: false, right: false }
    }

    fn on_update(&mut self, args: &pw::UpdateArgs) {
        if self.up   { self.player.mov(0.0, -150.0 * args.dt); }
        if self.down { self.player.mov(0.0, 150.0 * args.dt); }
        if self.left { self.player.mov(-150.0 * args.dt, 0.0); }
        if self.right { self.player.mov(150.0 * args.dt, 0.0); }
    }

    fn on_draw(&mut self, args: &pw::RenderArgs, window: &pw::PistonWindow) {
        window.draw_2d(|context, graphics| {
            pw::clear([0.0, 0.0, 0.0, 1.0], graphics);

            let center = context.transform.trans((args.width / 2) as f64, (args.height / 2) as f64);
            self.player.render(graphics, &center);
        });
    }

    fn on_input(&mut self, input: &pw::Input) {
        use piston_window::{Button, Key};

        match input {
            &pw::Input::Press(button) => {
                match button {
                    Button::Keyboard(Key::Up) => { self.up = true; }
                    Button::Keyboard(Key::Down) => { self.down = true; }
                    Button::Keyboard(Key::Left) => { self.left = true; }
                    Button::Keyboard(Key::Right) => { self.right = true; }
                    _ => {}
                }
            }
            &pw::Input::Release(button) => {
                match button {
                    Button::Keyboard(Key::Up) => { self.up = false; }
                    Button::Keyboard(Key::Down) => { self.down = false; }
                    Button::Keyboard(Key::Left) => { self.left = false; }
                    Button::Keyboard(Key::Right) => { self.right = false; }
                    _ => {}
                }
            }
            _ => {}
        }
    }
}

fn main() {
    use piston_window::Event;

    let window: pw::PistonWindow = pw::WindowSettings::new("rrthozopsi", [600, 600])
        .exit_on_esc(true)
        .build()
        .unwrap();

    let mut game = Game::new();

    for w in window {
        match w.event {
            Some(Event::Update(args)) => { game.on_update(&args); }
            Some(Event::Render(args)) => { game.on_draw(&args, &w); }
            Some(Event::Input(input)) => { game.on_input(&input); }
            _ => {}
        }
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code, non_camel_case_types)]

extern crate libc;

pub use self::os::{mdb_mode_t, mdb_filehandle_t};
use libc::{c_int, c_uint, c_void, c_char, size_t};

#[cfg(any(target_os = "macos", target_os = "ios", target_os = "linux",
          target_os = "freebsd", target_os = "dragonfly",
          target_os = "android"))]
mod os {
    use libc;

    pub type mdb_mode_t = libc::mode_t;

    pub type mdb_filehandle_t = libc::c_int;
}

#[cfg(target_os = "windows")]
mod os {
    use libc::{c_int, c_void};

    pub type mdb_mode_t = c_int;

    pub type mdb_filehandle_t = *const c_void;
}

pub type MDB_dbi = c_uint;

pub type MDB_rel_func = fn(*const MDB_val, *const c_void, *const c_void, *const c_void);
pub type MDB_msg_func = fn(*const c_char, *const c_void) -> c_int;
pub type MDB_cmp_func = fn(*const MDB_val, *const MDB_val) -> c_int;

#[derive(Copy, Clone)]
#[allow(raw_pointer_derive)]
#[repr(C)]

pub struct MDB_val {
    pub mv_size: size_t,
    pub mv_data: *const c_void,
}

#[repr(C)]
#[allow(missing_copy_implementations)]
pub struct MDB_env;

#[repr(C)]
#[allow(missing_copy_implementations)]
pub struct MDB_txn;

#[repr(C)]
#[allow(missing_copy_implementations)]
pub struct MDB_cursor;

#[repr(C)]

#[derive(Copy, Clone)]
pub struct MDB_stat {
    pub ms_psize: c_uint,
    pub ms_depth: c_uint,
    pub ms_branch_pages: size_t,
    pub ms_leaf_pages: size_t,
    pub ms_overflow_pages: size_t,
    pub ms_entries: size_t
}

#[repr(C)]

#[allow(missing_copy_implementations)]
pub struct MDB_envinfo {
    pub me_mapaddr: *const c_void,
    pub me_mapsize: size_t,
    pub me_last_pgno: size_t,
    pub me_last_txnid: size_t,
    pub me_maxreaders: c_uint,
    pub me_numreaders: c_uint
}

#[repr(C)]
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum MDB_cursor_op {
    MDB_FIRST,
    MDB_FIRST_DUP,
    MDB_GET_BOTH,
    MDB_GET_BOTH_RANGE,
    MDB_GET_CURRENT,
    MDB_GET_MULTIPLE,
    MDB_LAST,
    MDB_LAST_DUP,
    MDB_NEXT,
    MDB_NEXT_DUP,
    MDB_NEXT_MULTIPLE,
    MDB_NEXT_NODUP,
    MDB_PREV,
    MDB_PREV_DUP,
    MDB_PREV_NODUP,
    MDB_SET,
    MDB_SET_KEY,
    MDB_SET_RANGE
}

// Return codes
pub const MDB_SUCCESS: c_int = 0;
pub const MDB_KEYEXIST: c_int = -30799;
pub const MDB_NOTFOUND: c_int = -30798;
pub const MDB_PAGE_NOTFOUND: c_int = -30797;
pub const MDB_CORRUPTED: c_int = -30796;
pub const MDB_PANIC: c_int = -30795;
pub const MDB_VERSION_MISMATCH: c_int = -30794;
pub const MDB_INVALID: c_int = -30793;
pub const MDB_MAP_FULL: c_int = -30792;
pub const MDB_DBS_FULL: c_int = -30791;
pub const MDB_READERS_FULL: c_int = -30790;
pub const MDB_TLS_FULL: c_int = -30789;
pub const MDB_TXN_FULL: c_int = -30788;
pub const MDB_CURSOR_FULL: c_int = -30787;
pub const MDB_PAGE_FULL: c_int = -30786;
pub const MDB_MAP_RESIZED: c_int = -30785;
pub const MDB_INCOMPATIBLE: c_int = -30784;
pub const MDB_BAD_RSLOT: c_int = -30783;
pub const MDB_BAD_TXN: c_int = -30782;
pub const MDB_BAD_VALSIZE: c_int = -30781;

// Write flags
pub const MDB_NOOVERWRITE: c_uint = 0x10;
pub const MDB_NODUPDATA: c_uint = 0x20;
pub const MDB_CURRENT: c_uint = 0x40;
pub const MDB_RESERVE: c_uint = 0x10000;
pub const MDB_APPEND: c_uint = 0x20000;
pub const MDB_APPENDDUP: c_uint = 0x40000;
pub const MDB_MULTIPLE: c_uint = 0x80000;

// Database flags
pub const MDB_REVERSEKEY: c_uint = 0x02;
pub const MDB_DUPSORT: c_uint = 0x04;
pub const MDB_INTEGERKEY: c_uint = 0x08;
pub const MDB_DUPFIXED: c_uint = 0x10;
pub const MDB_INTEGERDUP: c_uint = 0x20;
pub const MDB_REVERSEDUP: c_uint =  0x40;
pub const MDB_CREATE: c_uint = 0x40000;

// Environment flags
pub const MDB_FIXEDMAP: c_uint =  0x01;
pub const MDB_NOSUBDIR: c_uint = 0x4000;
pub const MDB_NOSYNC: c_uint = 0x10000;
pub const MDB_RDONLY: c_uint = 0x20000;
pub const MDB_NOMETASYNC: c_uint = 0x40000;
pub const MDB_WRITEMAP: c_uint = 0x80000;
pub const MDB_MAPASYNC: c_uint = 0x100000;
pub const MDB_NOTLS: c_uint = 0x200000;
pub const MDB_NOLOCK: c_uint =  0x400000;
pub const MDB_NORDAHEAD: c_uint = 0x800000;
pub const MDB_NOMEMINIT: c_uint =  0x1000000;

// Embedding should work better for now
extern "C" {
    pub fn mdb_version(major: *mut c_int, minor: *mut c_int, patch: *mut c_int) -> *const c_char;
    pub fn mdb_strerror(err: c_int) -> *const c_char;
    pub fn mdb_env_create(env: *mut *mut MDB_env) -> c_int;
    pub fn mdb_env_open(env: *mut MDB_env, path: *const c_char, flags: c_uint, mode: mdb_mode_t) -> c_int;
    pub fn mdb_env_copy(env: *mut MDB_env, path: *const c_char) -> c_int;
    pub fn mdb_env_copyfd(env: *mut MDB_env, fd: mdb_filehandle_t) -> c_int;
    pub fn mdb_env_stat(env: *mut MDB_env, stat: *mut MDB_stat) -> c_int;
    pub fn mdb_env_info(env: *mut MDB_env, info: *mut MDB_envinfo) -> c_int;
    pub fn mdb_env_sync(env: *mut MDB_env, force: c_int) -> c_int;
    pub fn mdb_env_close(env: *mut MDB_env);
    pub fn mdb_env_set_flags(env: *mut MDB_env, flags: c_uint, onoff: c_int) -> c_int;
    pub fn mdb_env_get_flags(env: *mut MDB_env, flags: *mut c_uint) -> c_int;
    pub fn mdb_env_get_path(env: *mut MDB_env, path: *mut *mut c_char) -> c_int;
    pub fn mdb_env_get_fd(env: *mut MDB_env, fd: *mut mdb_filehandle_t) -> c_int;
    pub fn mdb_env_set_mapsize(env: *mut MDB_env, size: size_t) -> c_int;
    pub fn mdb_env_set_maxreaders(env: *mut MDB_env, readers: c_uint) -> c_int;
    pub fn mdb_env_get_maxreaders(env: *mut MDB_env, readers: *mut c_uint) -> c_int;
    pub fn mdb_env_set_maxdbs(env: *mut MDB_env, dbs: MDB_dbi) -> c_int;
    pub fn mdb_env_get_maxkeysize(env: *mut MDB_env) -> c_int;
    pub fn mdb_txn_begin(env: *mut MDB_env, parent: *mut MDB_txn, flags: c_uint, txn: *mut *mut MDB_txn) -> c_int;
    pub fn mdb_txn_env(txn: *mut MDB_txn) -> *mut MDB_env;
    pub fn mdb_txn_commit(txn: *mut MDB_txn) -> c_int;
    pub fn mdb_txn_abort(txn: *mut MDB_txn);
    pub fn mdb_txn_reset(txn: *mut MDB_txn);
    pub fn mdb_txn_renew(txn: *mut MDB_txn) -> c_int;
    pub fn mdb_dbi_open(txn: *mut MDB_txn, name: *const c_char, flags: c_uint, dbi: *mut MDB_dbi) -> c_int;
    pub fn mdb_stat(txn: *mut MDB_txn, dbi: MDB_dbi, stat: *mut MDB_stat) -> c_int;
    pub fn mdb_dbi_flags(txn: *mut MDB_txn, dbi: MDB_dbi, flags: *mut c_uint) -> c_int;
    pub fn mdb_dbi_close(txn: *mut MDB_env, dbi: MDB_dbi);
    pub fn mdb_drop(txn: *mut MDB_txn, dbi: MDB_dbi, del: c_int) -> c_int;
    pub fn mdb_set_compare(txn: *mut MDB_txn, dbi: MDB_dbi, cmp: MDB_cmp_func) -> c_int;
    pub fn mdb_set_dupsort(txn: *mut MDB_txn, dbi: MDB_dbi, cmp: MDB_cmp_func) -> c_int;
    pub fn mdb_set_relfunc(txn: *mut MDB_txn, dbi: MDB_dbi, rel: MDB_rel_func) -> c_int;
    pub fn mdb_set_relctx(txn: *mut MDB_txn, dbi: MDB_dbi, ctx: *const c_void) -> c_int;
    pub fn mdb_get(txn: *mut MDB_txn, dbi: MDB_dbi, key: *mut MDB_val, data: *mut MDB_val) -> c_int;
    pub fn mdb_put(txn: *mut MDB_txn, dbi: MDB_dbi, key: *mut MDB_val, data: *mut MDB_val, flags: c_uint) -> c_int;
    pub fn mdb_del(txn: *mut MDB_txn, dbi: MDB_dbi, key: *mut MDB_val, data: *mut MDB_val) -> c_int;
    pub fn mdb_cursor_open(txn: *mut MDB_txn, dbi: MDB_dbi, cursor: *mut *mut MDB_cursor) -> c_int;
    pub fn mdb_cursor_close(cursor: *mut MDB_cursor) -> c_int;
    pub fn mdb_cursor_renew(txn: *mut MDB_txn, cursor: *mut MDB_cursor) -> c_int;
    pub fn mdb_cursor_txn(cursor: *mut MDB_cursor) -> *mut MDB_txn;
    pub fn mdb_cursor_dbi(cursor: *mut MDB_cursor) -> *mut MDB_dbi;
    pub fn mdb_cursor_get(cursor: *mut MDB_cursor, key: *mut MDB_val, data: *mut MDB_val, op: MDB_cursor_op) -> c_int;
    pub fn mdb_cursor_put(cursor: *mut MDB_cursor, key: *mut MDB_val, data: *mut MDB_val, flags: c_uint) -> c_int;
    pub fn mdb_cursor_del(cursor: *mut MDB_cursor, flags: c_uint) -> c_int;
    pub fn mdb_cursor_count(cursor: *mut MDB_cursor, countp: *mut size_t) -> c_int;
    pub fn mdb_cmp(txn: *mut MDB_txn, dbi: MDB_dbi, a: *mut MDB_val, b: *mut MDB_val) -> c_int;
    pub fn mdb_dcmp(txn: *mut MDB_txn, dbi: MDB_dbi, a: *mut MDB_val, b: *mut MDB_val) -> c_int;
    pub fn mdb_reader_list(env: *mut MDB_env, func: MDB_msg_func, ctx: *const c_void) -> c_int;
    pub fn mdb_reader_check(env: *mut MDB_env, dead: *mut c_int) -> c_int;
}

impl std::fmt::Debug for MDB_val {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        unsafe {
            let buf: &[u8] = std::slice::from_raw_parts(std::mem::transmute(self.mv_data),
                                                        self.mv_size as usize);
            write!(fmt, "{:?}@{:?}", buf, self.mv_data)
        }
    }
}
<commit_msg>Fix compiler warnings on C opaque structs (see also rust-lang/rust/issues/27303)<commit_after>#![allow(dead_code, non_camel_case_types)]

extern crate libc;

pub use self::os::{mdb_mode_t, mdb_filehandle_t};
use libc::{c_int, c_uint, c_void, c_char, size_t};

#[cfg(any(target_os = "macos", target_os = "ios", target_os = "linux",
          target_os = "freebsd", target_os = "dragonfly",
          target_os = "android"))]
mod os {
    use libc;

    pub type mdb_mode_t = libc::mode_t;

    pub type mdb_filehandle_t = libc::c_int;
}

#[cfg(target_os = "windows")]
mod os {
    use libc::{c_int, c_void};

    pub type mdb_mode_t = c_int;

    pub type mdb_filehandle_t = *const c_void;
}

pub type MDB_dbi = c_uint;

pub type MDB_rel_func = fn(*const MDB_val, *const c_void, *const c_void, *const c_void);
pub type MDB_msg_func = fn(*const c_char, *const c_void) -> c_int;
pub type MDB_cmp_func = fn(*const MDB_val, *const MDB_val) -> c_int;

#[derive(Copy, Clone)]
#[allow(raw_pointer_derive)]
#[repr(C)]
pub struct MDB_val {
    pub mv_size: size_t,
    pub mv_data: *const c_void,
}

#[allow(missing_copy_implementations)]
pub enum MDB_env {}

#[allow(missing_copy_implementations)]
pub enum MDB_txn {}

#[allow(missing_copy_implementations)]
pub enum MDB_cursor {}

#[repr(C)]
#[derive(Copy, Clone)]
pub struct MDB_stat {
    pub ms_psize: c_uint,
    pub ms_depth: c_uint,
    pub ms_branch_pages: size_t,
    pub ms_leaf_pages: size_t,
    pub ms_overflow_pages: size_t,
    pub ms_entries: size_t
}

#[repr(C)]
#[allow(missing_copy_implementations)]
pub struct MDB_envinfo {
    pub me_mapaddr: *const c_void,
    pub me_mapsize: size_t,
    pub me_last_pgno: size_t,
    pub me_last_txnid: size_t,
    pub me_maxreaders: c_uint,
    pub me_numreaders: c_uint
}

#[repr(C)]
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum MDB_cursor_op {
    MDB_FIRST,
    MDB_FIRST_DUP,
    MDB_GET_BOTH,
    MDB_GET_BOTH_RANGE,
    MDB_GET_CURRENT,
    MDB_GET_MULTIPLE,
    MDB_LAST,
    MDB_LAST_DUP,
    MDB_NEXT,
    MDB_NEXT_DUP,
    MDB_NEXT_MULTIPLE,
    MDB_NEXT_NODUP,
    MDB_PREV,
    MDB_PREV_DUP,
    MDB_PREV_NODUP,
    MDB_SET,
    MDB_SET_KEY,
    MDB_SET_RANGE
}

// Return codes
pub const MDB_SUCCESS: c_int = 0;
pub const MDB_KEYEXIST: c_int = -30799;
pub const MDB_NOTFOUND: c_int = -30798;
pub const MDB_PAGE_NOTFOUND: c_int = -30797;
pub const MDB_CORRUPTED: c_int = -30796;
pub const MDB_PANIC: c_int = -30795;
pub const MDB_VERSION_MISMATCH: c_int = -30794;
pub const MDB_INVALID: c_int = -30793;
pub const MDB_MAP_FULL: c_int = -30792;
pub const MDB_DBS_FULL: c_int = -30791;
pub const MDB_READERS_FULL: c_int = -30790;
pub const MDB_TLS_FULL: c_int = -30789;
pub const MDB_TXN_FULL: c_int = -30788;
pub const MDB_CURSOR_FULL: c_int = -30787;
pub const MDB_PAGE_FULL: c_int = -30786;
pub const MDB_MAP_RESIZED: c_int = -30785;
pub const MDB_INCOMPATIBLE: c_int = -30784;
pub const MDB_BAD_RSLOT: c_int = -30783;
pub const MDB_BAD_TXN: c_int = -30782;
pub const MDB_BAD_VALSIZE: c_int = -30781;

// Write flags
pub const MDB_NOOVERWRITE: c_uint = 0x10;
pub const MDB_NODUPDATA: c_uint = 0x20;
pub const MDB_CURRENT: c_uint = 0x40;
pub const MDB_RESERVE: c_uint = 0x10000;
pub const MDB_APPEND: c_uint = 0x20000;
pub const MDB_APPENDDUP: c_uint = 0x40000;
pub const MDB_MULTIPLE: c_uint = 0x80000;

// Database flags
pub const MDB_REVERSEKEY: c_uint = 0x02;
pub const MDB_DUPSORT: c_uint = 0x04;
pub const MDB_INTEGERKEY: c_uint = 0x08;
pub const MDB_DUPFIXED: c_uint = 0x10;
pub const MDB_INTEGERDUP: c_uint = 0x20;
pub const MDB_REVERSEDUP: c_uint =  0x40;
pub const MDB_CREATE: c_uint = 0x40000;

// Environment flags
pub const MDB_FIXEDMAP: c_uint =  0x01;
pub const MDB_NOSUBDIR: c_uint = 0x4000;
pub const MDB_NOSYNC: c_uint = 0x10000;
pub const MDB_RDONLY: c_uint = 0x20000;
pub const MDB_NOMETASYNC: c_uint = 0x40000;
pub const MDB_WRITEMAP: c_uint = 0x80000;
pub const MDB_MAPASYNC: c_uint = 0x100000;
pub const MDB_NOTLS: c_uint = 0x200000;
pub const MDB_NOLOCK: c_uint =  0x400000;
pub const MDB_NORDAHEAD: c_uint = 0x800000;
pub const MDB_NOMEMINIT: c_uint =  0x1000000;

// Embedding should work better for now
extern "C" {
    pub fn mdb_version(major: *mut c_int, minor: *mut c_int, patch: *mut c_int) -> *const c_char;
    pub fn mdb_strerror(err: c_int) -> *const c_char;
    pub fn mdb_env_create(env: *mut *mut MDB_env) -> c_int;
    pub fn mdb_env_open(env: *mut MDB_env, path: *const c_char, flags: c_uint, mode: mdb_mode_t) -> c_int;
    pub fn mdb_env_copy(env: *mut MDB_env, path: *const c_char) -> c_int;
    pub fn mdb_env_copyfd(env: *mut MDB_env, fd: mdb_filehandle_t) -> c_int;
    pub fn mdb_env_stat(env: *mut MDB_env, stat: *mut MDB_stat) -> c_int;
    pub fn mdb_env_info(env: *mut MDB_env, info: *mut MDB_envinfo) -> c_int;
    pub fn mdb_env_sync(env: *mut MDB_env, force: c_int) -> c_int;
    pub fn mdb_env_close(env: *mut MDB_env);
    pub fn mdb_env_set_flags(env: *mut MDB_env, flags: c_uint, onoff: c_int) -> c_int;
    pub fn mdb_env_get_flags(env: *mut MDB_env, flags: *mut c_uint) -> c_int;
    pub fn mdb_env_get_path(env: *mut MDB_env, path: *mut *mut c_char) -> c_int;
    pub fn mdb_env_get_fd(env: *mut MDB_env, fd: *mut mdb_filehandle_t) -> c_int;
    pub fn mdb_env_set_mapsize(env: *mut MDB_env, size: size_t) -> c_int;
    pub fn mdb_env_set_maxreaders(env: *mut MDB_env, readers: c_uint) -> c_int;
    pub fn mdb_env_get_maxreaders(env: *mut MDB_env, readers: *mut c_uint) -> c_int;
    pub fn mdb_env_set_maxdbs(env: *mut MDB_env, dbs: MDB_dbi) -> c_int;
    pub fn mdb_env_get_maxkeysize(env: *mut MDB_env) -> c_int;
    pub fn mdb_txn_begin(env: *mut MDB_env, parent: *mut MDB_txn, flags: c_uint, txn: *mut *mut MDB_txn) -> c_int;
    pub fn mdb_txn_env(txn: *mut MDB_txn) -> *mut MDB_env;
    pub fn mdb_txn_commit(txn: *mut MDB_txn) -> c_int;
    pub fn mdb_txn_abort(txn: *mut MDB_txn);
    pub fn mdb_txn_reset(txn: *mut MDB_txn);
    pub fn mdb_txn_renew(txn: *mut MDB_txn) -> c_int;
    pub fn mdb_dbi_open(txn: *mut MDB_txn, name: *const c_char, flags: c_uint, dbi: *mut MDB_dbi) -> c_int;
    pub fn mdb_stat(txn: *mut MDB_txn, dbi: MDB_dbi, stat: *mut MDB_stat) -> c_int;
    pub fn mdb_dbi_flags(txn: *mut MDB_txn, dbi: MDB_dbi, flags: *mut c_uint) -> c_int;
    pub fn mdb_dbi_close(txn: *mut MDB_env, dbi: MDB_dbi);
    pub fn mdb_drop(txn: *mut MDB_txn, dbi: MDB_dbi, del: c_int) -> c_int;
    pub fn mdb_set_compare(txn: *mut MDB_txn, dbi: MDB_dbi, cmp: MDB_cmp_func) -> c_int;
    pub fn mdb_set_dupsort(txn: *mut MDB_txn, dbi: MDB_dbi, cmp: MDB_cmp_func) -> c_int;
    pub fn mdb_set_relfunc(txn: *mut MDB_txn, dbi: MDB_dbi, rel: MDB_rel_func) -> c_int;
    pub fn mdb_set_relctx(txn: *mut MDB_txn, dbi: MDB_dbi, ctx: *const c_void) -> c_int;
    pub fn mdb_get(txn: *mut MDB_txn, dbi: MDB_dbi, key: *mut MDB_val, data: *mut MDB_val) -> c_int;
    pub fn mdb_put(txn: *mut MDB_txn, dbi: MDB_dbi, key: *mut MDB_val, data: *mut MDB_val, flags: c_uint) -> c_int;
    pub fn mdb_del(txn: *mut MDB_txn, dbi: MDB_dbi, key: *mut MDB_val, data: *mut MDB_val) -> c_int;
    pub fn mdb_cursor_open(txn: *mut MDB_txn, dbi: MDB_dbi, cursor: *mut *mut MDB_cursor) -> c_int;
    pub fn mdb_cursor_close(cursor: *mut MDB_cursor) -> c_int;
    pub fn mdb_cursor_renew(txn: *mut MDB_txn, cursor: *mut MDB_cursor) -> c_int;
    pub fn mdb_cursor_txn(cursor: *mut MDB_cursor) -> *mut MDB_txn;
    pub fn mdb_cursor_dbi(cursor: *mut MDB_cursor) -> *mut MDB_dbi;
    pub fn mdb_cursor_get(cursor: *mut MDB_cursor, key: *mut MDB_val, data: *mut MDB_val, op: MDB_cursor_op) -> c_int;
    pub fn mdb_cursor_put(cursor: *mut MDB_cursor, key: *mut MDB_val, data: *mut MDB_val, flags: c_uint) -> c_int;
    pub fn mdb_cursor_del(cursor: *mut MDB_cursor, flags: c_uint) -> c_int;
    pub fn mdb_cursor_count(cursor: *mut MDB_cursor, countp: *mut size_t) -> c_int;
    pub fn mdb_cmp(txn: *mut MDB_txn, dbi: MDB_dbi, a: *mut MDB_val, b: *mut MDB_val) -> c_int;
    pub fn mdb_dcmp(txn: *mut MDB_txn, dbi: MDB_dbi, a: *mut MDB_val, b: *mut MDB_val) -> c_int;
    pub fn mdb_reader_list(env: *mut MDB_env, func: MDB_msg_func, ctx: *const c_void) -> c_int;
    pub fn mdb_reader_check(env: *mut MDB_env, dead: *mut c_int) -> c_int;
}

impl std::fmt::Debug for MDB_val {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        unsafe {
            let buf: &[u8] = std::slice::from_raw_parts(std::mem::transmute(self.mv_data),
                                                        self.mv_size as usize);
            write!(fmt, "{:?}@{:?}", buf, self.mv_data)
        }
    }
}
<|endoftext|>


<commit_before>// Code style
#![forbid(private_in_public)]
#![forbid(bare_trait_objects)]
// #![deny(unused_assignments)]
// Code style (development time)
#![allow(unused_macros)]
// Use std only in tests
#![cfg_attr(not(test), no_std)]
// Safety
#![deny(overflowing_literals)]
#![deny(safe_packed_borrows)]
#![deny(unused_must_use)]
// Unstable features
#![feature(lang_items)]
#![feature(naked_functions)]

use core::arch::asm;
use core::intrinsics::copy_nonoverlapping;
use core::{panic::PanicInfo, ptr};

mod ata_pio;

macro_rules! sizeof {
    ($t:ty) => {{ ::core::mem::size_of::<$t>() }};
}

macro_rules! panic_indicator {
    ($x:expr) => ({
        asm!(
            concat!("mov eax, ", stringify!($x), "; mov [0xb809c], eax"),
            out("eax") _,
            options(nostack)
        );
    });
    () => ({
        panic_indicator!(0x4f214f70);   // !p
    });
}

// Keep in sync with plan.md and constants/1_boot.toml
const ELF_LOADPOINT: usize = 0x10_0000;
const INITRD_SPLIT_SECTOR_PTR: usize = 0x3000;
const INITRD_END_SECTOR_PTR: usize = 0x3004;
const KERNEL_ENTRY_POINT: u64 = 0x100_0000;
const PAGE_SIZE_BYTES: u64 = 0x20_0000;
const BOOTLOADER_SECTOR_COUNT: usize = 6;

/// Align upwards to page size
pub fn page_align_up(addr: u64) -> u64 {
    (addr + PAGE_SIZE_BYTES - 1) & !(PAGE_SIZE_BYTES - 1)
}

/// Write 'ER:_' to top top right of screen in red
/// _ denotes the argument
#[cfg(not(test))]
fn error(c: char) -> ! {
    unsafe {
        // 'ER: _'
        asm!(
            "mov [0xb8000 + 76*2], rax",
            in("rax") 0x4f5f4f3a4f524f45u64,
            options(nostack)
        );
        // set arg
        asm!(
            "mov [0xb8006 + 76*2], al",
            in("al") c as u8,
            options(nostack)
        );
        loop {
            asm!("hlt");
        }
    }
}

/// Not used when testing
#[cfg(test)]
fn create_progress_indicator() {}

/// Not used when testing
#[cfg(test)]
fn progress_indicator(_a: u8) {}

#[inline(always)]
unsafe fn check_elf() {
    // https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header
    // Check that this is an ELF file (Magic number)
    if ptr::read(ELF_LOADPOINT as *const u32) != 0x464c457f {
        error('M');
    }

    // Check that the kernel entry point is correct (0x_100_0000, 1MiB)
    if ptr::read((ELF_LOADPOINT + 0x18) as *const u64) != KERNEL_ENTRY_POINT {
        error('P');
    }
}

#[naked]
#[no_mangle]
pub unsafe extern "C" fn d7boot() {
    // Location info
    let initrd_start_sector = (*(INITRD_SPLIT_SECTOR_PTR as *const u32)) as usize;
    let initrd_end_sector = (*(INITRD_END_SECTOR_PTR as *const u32)) as usize;

    // Load disk sectors
    let supported_sector_count = ata_pio::init();
    if (supported_sector_count as usize) < initrd_end_sector {
        // Not enough sectors
        error('S');
    }
    let mut dst: *mut u16 = ELF_LOADPOINT as *mut u16;
    for lba in BOOTLOADER_SECTOR_COUNT..initrd_end_sector {
        ata_pio::read_lba(lba as u64, 1, dst);
        dst = dst.add(0x100);
    }

    // Load kernel ELF image
    check_elf();

    // Go through the program header table
    // Just assume that the header has standard lengths and positions
    let program_header_count: u16 = ptr::read((ELF_LOADPOINT + 0x38) as *const u16);

    // Load and copy sectors
    let mut max_vaddr = 0;
    for i in 0..program_header_count {
        let base = ELF_LOADPOINT + 0x40 + i as usize * 0x38;

        let p_type = ptr::read(base as *const u32);
        // LOAD
        if p_type == 1 {
            // Read values
            let p_offset = ptr::read((base + 0x08) as *const u64);
            let p_vaddr = ptr::read((base + 0x10) as *const u64);
            let p_filesz = ptr::read((base + 0x20) as *const u64);
            let p_memsz = ptr::read((base + 0x28) as *const u64);

            if max_vaddr < p_vaddr {
                max_vaddr = p_vaddr + p_memsz;
            }

            // Clear p_memsz bytes at p_vaddr to 0
            ptr::write_bytes(p_vaddr as *mut u8, 0, p_memsz as usize);

            // Copy p_filesz bytes from p_offset to p_vaddr
            copy_nonoverlapping(
                (ELF_LOADPOINT as u64 + p_offset) as *const u8,
                p_vaddr as *mut u8,
                p_filesz as usize,
            );
        }
    }

    // Copy InitRD to be just after the kernel
    let src_ptr =
        (ELF_LOADPOINT + (initrd_start_sector - BOOTLOADER_SECTOR_COUNT) * 0x200) as *const u8;
    let dst_ptr = page_align_up(max_vaddr) as *mut u8;
    let count = (initrd_end_sector - initrd_start_sector) * 0x200;
    copy_nonoverlapping(src_ptr, dst_ptr, count);

    // Show message ('-> K') and jump to kernel
    asm!("mov [0xb8000], rax", in("rax") 0x0f4b0f200f3e0f2du64, options(nostack));
    // Set rax = 0 to signal that this is the BSP i.e. cpu0
    asm!(
        concat!("push ", 0x100_0000, "; ret"),
        in("rcx") 0
    ); // KERNEL_ENTRY_POINT
    core::hint::unreachable_unchecked();
}

#[cfg(not(test))]
#[panic_handler]
#[allow(unused_variables)]
#[no_mangle]
extern "C" fn panic(info: &PanicInfo) -> ! {
    unsafe {
        panic_indicator!(0x4f214f45); // E!
        loop {
            asm!("hlt");
        }
    }
}
<commit_msg>d7boot: Fix warnings<commit_after>// Code style
#![forbid(private_in_public)]
#![forbid(bare_trait_objects)]
// #![deny(unused_assignments)]
// Code style (development time)
#![allow(unused_macros)]
// Use std only in tests
#![cfg_attr(not(test), no_std)]
// Safety
#![deny(overflowing_literals)]
#![deny(unaligned_references)]
#![deny(unused_must_use)]
// Unstable features
#![feature(lang_items)]
#![feature(naked_functions)]

use core::arch::asm;
use core::intrinsics::copy_nonoverlapping;
use core::{panic::PanicInfo, ptr};

mod ata_pio;

macro_rules! sizeof {
    ($t:ty) => {{ ::core::mem::size_of::<$t>() }};
}

macro_rules! panic_indicator {
    ($x:expr) => ({
        asm!(
            concat!("mov eax, ", stringify!($x), "; mov [0xb809c], eax"),
            out("eax") _,
            options(nostack)
        );
    });
    () => ({
        panic_indicator!(0x4f214f70);   // !p
    });
}

// Keep in sync with plan.md and constants/1_boot.toml
const ELF_LOADPOINT: usize = 0x10_0000;
const INITRD_SPLIT_SECTOR_PTR: usize = 0x3000;
const INITRD_END_SECTOR_PTR: usize = 0x3004;
const KERNEL_ENTRY_POINT: u64 = 0x100_0000;
const PAGE_SIZE_BYTES: u64 = 0x20_0000;
const BOOTLOADER_SECTOR_COUNT: usize = 6;

/// Align upwards to page size
pub fn page_align_up(addr: u64) -> u64 {
    (addr + PAGE_SIZE_BYTES - 1) & !(PAGE_SIZE_BYTES - 1)
}

/// Write 'ER:_' to top top right of screen in red
/// _ denotes the argument
#[cfg(not(test))]
fn error(c: char) -> ! {
    unsafe {
        // 'ER: _'
        asm!(
            "mov [0xb8000 + 76*2], rax",
            in("rax") 0x4f5f4f3a4f524f45u64,
            options(nostack)
        );
        // set arg
        asm!(
            "mov [0xb8006 + 76*2], al",
            in("al") c as u8,
            options(nostack)
        );
        loop {
            asm!("hlt");
        }
    }
}

/// Not used when testing
#[cfg(test)]
fn create_progress_indicator() {}

/// Not used when testing
#[cfg(test)]
fn progress_indicator(_a: u8) {}

#[inline(always)]
unsafe fn check_elf() {
    // https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header
    // Check that this is an ELF file (Magic number)
    if ptr::read(ELF_LOADPOINT as *const u32) != 0x464c457f {
        error('M');
    }

    // Check that the kernel entry point is correct (0x_100_0000, 1MiB)
    if ptr::read((ELF_LOADPOINT + 0x18) as *const u64) != KERNEL_ENTRY_POINT {
        error('P');
    }
}

#[no_mangle]
pub unsafe extern "C" fn d7boot() {
    // Location info
    let initrd_start_sector = (*(INITRD_SPLIT_SECTOR_PTR as *const u32)) as usize;
    let initrd_end_sector = (*(INITRD_END_SECTOR_PTR as *const u32)) as usize;

    // Load disk sectors
    let supported_sector_count = ata_pio::init();
    if (supported_sector_count as usize) < initrd_end_sector {
        // Not enough sectors
        error('S');
    }
    let mut dst: *mut u16 = ELF_LOADPOINT as *mut u16;
    for lba in BOOTLOADER_SECTOR_COUNT..initrd_end_sector {
        ata_pio::read_lba(lba as u64, 1, dst);
        dst = dst.add(0x100);
    }

    // Load kernel ELF image
    check_elf();

    // Go through the program header table
    // Just assume that the header has standard lengths and positions
    let program_header_count: u16 = ptr::read((ELF_LOADPOINT + 0x38) as *const u16);

    // Load and copy sectors
    let mut max_vaddr = 0;
    for i in 0..program_header_count {
        let base = ELF_LOADPOINT + 0x40 + i as usize * 0x38;

        let p_type = ptr::read(base as *const u32);
        // LOAD
        if p_type == 1 {
            // Read values
            let p_offset = ptr::read((base + 0x08) as *const u64);
            let p_vaddr = ptr::read((base + 0x10) as *const u64);
            let p_filesz = ptr::read((base + 0x20) as *const u64);
            let p_memsz = ptr::read((base + 0x28) as *const u64);

            if max_vaddr < p_vaddr {
                max_vaddr = p_vaddr + p_memsz;
            }

            // Clear p_memsz bytes at p_vaddr to 0
            ptr::write_bytes(p_vaddr as *mut u8, 0, p_memsz as usize);

            // Copy p_filesz bytes from p_offset to p_vaddr
            copy_nonoverlapping(
                (ELF_LOADPOINT as u64 + p_offset) as *const u8,
                p_vaddr as *mut u8,
                p_filesz as usize,
            );
        }
    }

    // Copy InitRD to be just after the kernel
    let src_ptr =
        (ELF_LOADPOINT + (initrd_start_sector - BOOTLOADER_SECTOR_COUNT) * 0x200) as *const u8;
    let dst_ptr = page_align_up(max_vaddr) as *mut u8;
    let count = (initrd_end_sector - initrd_start_sector) * 0x200;
    copy_nonoverlapping(src_ptr, dst_ptr, count);

    // Show message ('-> K') and jump to kernel
    asm!("mov [0xb8000], rax", in("rax") 0x0f4b0f200f3e0f2du64, options(nostack));
    // Set rax = 0 to signal that this is the BSP i.e. cpu0
    asm!(
        concat!("push ", 0x100_0000, "; ret"),
        in("rcx") 0
    ); // KERNEL_ENTRY_POINT
    core::hint::unreachable_unchecked();
}

#[cfg(not(test))]
#[panic_handler]
#[allow(unused_variables)]
#[no_mangle]
extern "C" fn panic(info: &PanicInfo) -> ! {
    unsafe {
        panic_indicator!(0x4f214f45); // E!
        loop {
            asm!("hlt");
        }
    }
}
<|endoftext|>


<commit_before>//! Request identity service for Actix applications.
//!
//! [**IdentityService**](struct.IdentityService.html) middleware can be
//! used with different policies types to store identity information.
//!
//! Bu default, only cookie identity policy is implemented. Other backend
//! implementations can be added separately.
//!
//! [**CookieIdentityPolicy**](struct.CookieIdentityPolicy.html)
//! uses cookies as identity storage.
//!
//! To access current request identity
//! [**RequestIdentity**](trait.RequestIdentity.html) should be used.
//! *HttpRequest* implements *RequestIdentity* trait.
//!
//! ```rust
//! use actix_web::middleware::identity::RequestIdentity;
//! use actix_web::middleware::identity::{CookieIdentityPolicy, IdentityService};
//! use actix_web::*;
//!
//! fn index(req: HttpRequest) -> Result<String> {
//!     // access request identity
//!     if let Some(id) = req.identity() {
//!         Ok(format!("Welcome! {}", id))
//!     } else {
//!         Ok("Welcome Anonymous!".to_owned())
//!     }
//! }
//!
//! fn login(mut req: HttpRequest) -> HttpResponse {
//!     req.remember("User1".to_owned()); // <- remember identity
//!     HttpResponse::Ok().finish()
//! }
//!
//! fn logout(mut req: HttpRequest) -> HttpResponse {
//!     req.forget(); // <- remove identity
//!     HttpResponse::Ok().finish()
//! }
//!
//! fn main() {
//!     let app = App::new().middleware(IdentityService::new(
//!         // <- create identity middleware
//!         CookieIdentityPolicy::new(&[0; 32])    // <- create cookie session backend
//!               .name("auth-cookie")
//!               .secure(false),
//!     ));
//! }
//! ```
use std::rc::Rc;

use cookie::{Cookie, CookieJar, Key};
use futures::future::{err as FutErr, ok as FutOk, FutureResult};
use futures::Future;
use time::Duration;

use error::{Error, Result};
use http::header::{self, HeaderValue};
use httprequest::HttpRequest;
use httpresponse::HttpResponse;
use middleware::{Middleware, Response, Started};

/// The helper trait to obtain your identity from a request.
///
/// ```rust
/// use actix_web::middleware::identity::RequestIdentity;
/// use actix_web::*;
///
/// fn index(req: HttpRequest) -> Result<String> {
///     // access request identity
///     if let Some(id) = req.identity() {
///         Ok(format!("Welcome! {}", id))
///     } else {
///         Ok("Welcome Anonymous!".to_owned())
///     }
/// }
///
/// fn login(mut req: HttpRequest) -> HttpResponse {
///     req.remember("User1".to_owned()); // <- remember identity
///     HttpResponse::Ok().finish()
/// }
///
/// fn logout(mut req: HttpRequest) -> HttpResponse {
///     req.forget(); // <- remove identity
///     HttpResponse::Ok().finish()
/// }
/// # fn main() {}
/// ```
pub trait RequestIdentity {
    /// Return the claimed identity of the user associated request or
    /// ``None`` if no identity can be found associated with the request.
    fn identity(&self) -> Option<&str>;

    /// Remember identity.
    fn remember(&mut self, identity: String);

    /// This method is used to 'forget' the current identity on subsequent
    /// requests.
    fn forget(&mut self);
}

impl<S> RequestIdentity for HttpRequest<S> {
    fn identity(&self) -> Option<&str> {
        if let Some(id) = self.extensions().get::<IdentityBox>() {
            return id.0.identity();
        }
        None
    }

    fn remember(&mut self, identity: String) {
        if let Some(id) = self.extensions_mut().get_mut::<IdentityBox>() {
            return id.0.remember(identity);
        }
    }

    fn forget(&mut self) {
        if let Some(id) = self.extensions_mut().get_mut::<IdentityBox>() {
            return id.0.forget();
        }
    }
}

/// An identity
pub trait Identity: 'static {
    /// Return the claimed identity of the user associated request or
    /// ``None`` if no identity can be found associated with the request.
    fn identity(&self) -> Option<&str>;

    /// Remember identity.
    fn remember(&mut self, key: String);

    /// This method is used to 'forget' the current identity on subsequent
    /// requests.
    fn forget(&mut self);

    /// Write session to storage backend.
    fn write(&mut self, resp: HttpResponse) -> Result<Response>;
}

/// Identity policy definition.
pub trait IdentityPolicy<S>: Sized + 'static {
    /// The associated identity
    type Identity: Identity;

    /// The return type of the middleware
    type Future: Future<Item = Self::Identity, Error = Error>;

    /// Parse the session from request and load data from a service identity.
    fn from_request(&self, request: &mut HttpRequest<S>) -> Self::Future;
}

/// Request identity middleware
///
/// ```rust
/// # extern crate actix_web;
/// use actix_web::middleware::identity::{CookieIdentityPolicy, IdentityService};
/// use actix_web::App;
///
/// fn main() {
///     let app = App::new().middleware(IdentityService::new(
///         // <- create identity middleware
///         CookieIdentityPolicy::new(&[0; 32])    // <- create cookie session backend
///               .name("auth-cookie")
///               .secure(false),
///     ));
/// }
/// ```
pub struct IdentityService<T> {
    backend: T,
}

impl<T> IdentityService<T> {
    /// Create new identity service with specified backend.
    pub fn new(backend: T) -> Self {
        IdentityService { backend }
    }
}

struct IdentityBox(Box<Identity>);

#[doc(hidden)]
unsafe impl Send for IdentityBox {}
#[doc(hidden)]
unsafe impl Sync for IdentityBox {}

impl<S: 'static, T: IdentityPolicy<S>> Middleware<S> for IdentityService<T> {
    fn start(&mut self, req: &mut HttpRequest<S>) -> Result<Started> {
        let mut req = req.clone();

        let fut = self
            .backend
            .from_request(&mut req)
            .then(move |res| match res {
                Ok(id) => {
                    req.extensions_mut().insert(IdentityBox(Box::new(id)));
                    FutOk(None)
                }
                Err(err) => FutErr(err),
            });
        Ok(Started::Future(Box::new(fut)))
    }

    fn response(
        &mut self, req: &mut HttpRequest<S>, resp: HttpResponse,
    ) -> Result<Response> {
        if let Some(mut id) = req.extensions_mut().remove::<IdentityBox>() {
            id.0.write(resp)
        } else {
            Ok(Response::Done(resp))
        }
    }
}

#[doc(hidden)]
/// Identity that uses private cookies as identity storage.
pub struct CookieIdentity {
    changed: bool,
    identity: Option<String>,
    inner: Rc<CookieIdentityInner>,
}

impl Identity for CookieIdentity {
    fn identity(&self) -> Option<&str> {
        self.identity.as_ref().map(|s| s.as_ref())
    }

    fn remember(&mut self, value: String) {
        self.changed = true;
        self.identity = Some(value);
    }

    fn forget(&mut self) {
        self.changed = true;
        self.identity = None;
    }

    fn write(&mut self, mut resp: HttpResponse) -> Result<Response> {
        if self.changed {
            let _ = self.inner.set_cookie(&mut resp, self.identity.take());
        }
        Ok(Response::Done(resp))
    }
}

struct CookieIdentityInner {
    key: Key,
    name: String,
    path: String,
    domain: Option<String>,
    secure: bool,
    max_age: Option<Duration>,
}

impl CookieIdentityInner {
    fn new(key: &[u8]) -> CookieIdentityInner {
        CookieIdentityInner {
            key: Key::from_master(key),
            name: "actix-identity".to_owned(),
            path: "/".to_owned(),
            domain: None,
            secure: true,
            max_age: None,
        }
    }

    fn set_cookie(&self, resp: &mut HttpResponse, id: Option<String>) -> Result<()> {
        let some = id.is_some();
        {
            let id = id.unwrap_or_else(String::new);
            let mut cookie = Cookie::new(self.name.clone(), id);
            cookie.set_path(self.path.clone());
            cookie.set_secure(self.secure);
            cookie.set_http_only(true);

            if let Some(ref domain) = self.domain {
                cookie.set_domain(domain.clone());
            }

            if let Some(max_age) = self.max_age {
                cookie.set_max_age(max_age);
            }

            let mut jar = CookieJar::new();
            if some {
                jar.private(&self.key).add(cookie);
            } else {
                jar.add_original(cookie.clone());
                jar.private(&self.key).remove(cookie);
            }

            for cookie in jar.delta() {
                let val = HeaderValue::from_str(&cookie.to_string())?;
                resp.headers_mut().append(header::SET_COOKIE, val);
            }
        }

        Ok(())
    }

    fn load<S>(&self, req: &mut HttpRequest<S>) -> Option<String> {
        if let Ok(cookies) = req.cookies() {
            for cookie in cookies {
                if cookie.name() == self.name {
                    let mut jar = CookieJar::new();
                    jar.add_original(cookie.clone());

                    let cookie_opt = jar.private(&self.key).get(&self.name);
                    if let Some(cookie) = cookie_opt {
                        return Some(cookie.value().into());
                    }
                }
            }
        }
        None
    }
}

/// Use cookies for request identity storage.
///
/// The constructors take a key as an argument.
/// This is the private key for cookie - when this value is changed,
/// all identities are lost. The constructors will panic if the key is less
/// than 32 bytes in length.
///
/// # Example
///
/// ```rust
/// # extern crate actix_web;
/// use actix_web::middleware::identity::{CookieIdentityPolicy, IdentityService};
/// use actix_web::App;
///
/// fn main() {
///     let app = App::new().middleware(IdentityService::new(
///         // <- create identity middleware
///         CookieIdentityPolicy::new(&[0; 32])  // <- construct cookie policy
///                .domain("www.rust-lang.org")
///                .name("actix_auth")
///                .path("/")
///                .secure(true),
///     ));
/// }
/// ```
pub struct CookieIdentityPolicy(Rc<CookieIdentityInner>);

impl CookieIdentityPolicy {
    /// Construct new `CookieIdentityPolicy` instance.
    ///
    /// Panics if key length is less than 32 bytes.
    pub fn new(key: &[u8]) -> CookieIdentityPolicy {
        CookieIdentityPolicy(Rc::new(CookieIdentityInner::new(key)))
    }

    /// Sets the `path` field in the session cookie being built.
    pub fn path<S: Into<String>>(mut self, value: S) -> CookieIdentityPolicy {
        Rc::get_mut(&mut self.0).unwrap().path = value.into();
        self
    }

    /// Sets the `name` field in the session cookie being built.
    pub fn name<S: Into<String>>(mut self, value: S) -> CookieIdentityPolicy {
        Rc::get_mut(&mut self.0).unwrap().name = value.into();
        self
    }

    /// Sets the `domain` field in the session cookie being built.
    pub fn domain<S: Into<String>>(mut self, value: S) -> CookieIdentityPolicy {
        Rc::get_mut(&mut self.0).unwrap().domain = Some(value.into());
        self
    }

    /// Sets the `secure` field in the session cookie being built.
    ///
    /// If the `secure` field is set, a cookie will only be transmitted when the
    /// connection is secure - i.e. `https`
    pub fn secure(mut self, value: bool) -> CookieIdentityPolicy {
        Rc::get_mut(&mut self.0).unwrap().secure = value;
        self
    }

    /// Sets the `max-age` field in the session cookie being built.
    pub fn max_age(mut self, value: Duration) -> CookieIdentityPolicy {
        Rc::get_mut(&mut self.0).unwrap().max_age = Some(value);
        self
    }
}

impl<S> IdentityPolicy<S> for CookieIdentityPolicy {
    type Identity = CookieIdentity;
    type Future = FutureResult<CookieIdentity, Error>;

    fn from_request(&self, req: &mut HttpRequest<S>) -> Self::Future {
        let identity = self.0.load(req);
        FutOk(CookieIdentity {
            identity,
            changed: false,
            inner: Rc::clone(&self.0),
        })
    }
}
<commit_msg>Fix typo<commit_after>//! Request identity service for Actix applications.
//!
//! [**IdentityService**](struct.IdentityService.html) middleware can be
//! used with different policies types to store identity information.
//!
//! By default, only cookie identity policy is implemented. Other backend
//! implementations can be added separately.
//!
//! [**CookieIdentityPolicy**](struct.CookieIdentityPolicy.html)
//! uses cookies as identity storage.
//!
//! To access current request identity
//! [**RequestIdentity**](trait.RequestIdentity.html) should be used.
//! *HttpRequest* implements *RequestIdentity* trait.
//!
//! ```rust
//! use actix_web::middleware::identity::RequestIdentity;
//! use actix_web::middleware::identity::{CookieIdentityPolicy, IdentityService};
//! use actix_web::*;
//!
//! fn index(req: HttpRequest) -> Result<String> {
//!     // access request identity
//!     if let Some(id) = req.identity() {
//!         Ok(format!("Welcome! {}", id))
//!     } else {
//!         Ok("Welcome Anonymous!".to_owned())
//!     }
//! }
//!
//! fn login(mut req: HttpRequest) -> HttpResponse {
//!     req.remember("User1".to_owned()); // <- remember identity
//!     HttpResponse::Ok().finish()
//! }
//!
//! fn logout(mut req: HttpRequest) -> HttpResponse {
//!     req.forget(); // <- remove identity
//!     HttpResponse::Ok().finish()
//! }
//!
//! fn main() {
//!     let app = App::new().middleware(IdentityService::new(
//!         // <- create identity middleware
//!         CookieIdentityPolicy::new(&[0; 32])    // <- create cookie session backend
//!               .name("auth-cookie")
//!               .secure(false),
//!     ));
//! }
//! ```
use std::rc::Rc;

use cookie::{Cookie, CookieJar, Key};
use futures::future::{err as FutErr, ok as FutOk, FutureResult};
use futures::Future;
use time::Duration;

use error::{Error, Result};
use http::header::{self, HeaderValue};
use httprequest::HttpRequest;
use httpresponse::HttpResponse;
use middleware::{Middleware, Response, Started};

/// The helper trait to obtain your identity from a request.
///
/// ```rust
/// use actix_web::middleware::identity::RequestIdentity;
/// use actix_web::*;
///
/// fn index(req: HttpRequest) -> Result<String> {
///     // access request identity
///     if let Some(id) = req.identity() {
///         Ok(format!("Welcome! {}", id))
///     } else {
///         Ok("Welcome Anonymous!".to_owned())
///     }
/// }
///
/// fn login(mut req: HttpRequest) -> HttpResponse {
///     req.remember("User1".to_owned()); // <- remember identity
///     HttpResponse::Ok().finish()
/// }
///
/// fn logout(mut req: HttpRequest) -> HttpResponse {
///     req.forget(); // <- remove identity
///     HttpResponse::Ok().finish()
/// }
/// # fn main() {}
/// ```
pub trait RequestIdentity {
    /// Return the claimed identity of the user associated request or
    /// ``None`` if no identity can be found associated with the request.
    fn identity(&self) -> Option<&str>;

    /// Remember identity.
    fn remember(&mut self, identity: String);

    /// This method is used to 'forget' the current identity on subsequent
    /// requests.
    fn forget(&mut self);
}

impl<S> RequestIdentity for HttpRequest<S> {
    fn identity(&self) -> Option<&str> {
        if let Some(id) = self.extensions().get::<IdentityBox>() {
            return id.0.identity();
        }
        None
    }

    fn remember(&mut self, identity: String) {
        if let Some(id) = self.extensions_mut().get_mut::<IdentityBox>() {
            return id.0.remember(identity);
        }
    }

    fn forget(&mut self) {
        if let Some(id) = self.extensions_mut().get_mut::<IdentityBox>() {
            return id.0.forget();
        }
    }
}

/// An identity
pub trait Identity: 'static {
    /// Return the claimed identity of the user associated request or
    /// ``None`` if no identity can be found associated with the request.
    fn identity(&self) -> Option<&str>;

    /// Remember identity.
    fn remember(&mut self, key: String);

    /// This method is used to 'forget' the current identity on subsequent
    /// requests.
    fn forget(&mut self);

    /// Write session to storage backend.
    fn write(&mut self, resp: HttpResponse) -> Result<Response>;
}

/// Identity policy definition.
pub trait IdentityPolicy<S>: Sized + 'static {
    /// The associated identity
    type Identity: Identity;

    /// The return type of the middleware
    type Future: Future<Item = Self::Identity, Error = Error>;

    /// Parse the session from request and load data from a service identity.
    fn from_request(&self, request: &mut HttpRequest<S>) -> Self::Future;
}

/// Request identity middleware
///
/// ```rust
/// # extern crate actix_web;
/// use actix_web::middleware::identity::{CookieIdentityPolicy, IdentityService};
/// use actix_web::App;
///
/// fn main() {
///     let app = App::new().middleware(IdentityService::new(
///         // <- create identity middleware
///         CookieIdentityPolicy::new(&[0; 32])    // <- create cookie session backend
///               .name("auth-cookie")
///               .secure(false),
///     ));
/// }
/// ```
pub struct IdentityService<T> {
    backend: T,
}

impl<T> IdentityService<T> {
    /// Create new identity service with specified backend.
    pub fn new(backend: T) -> Self {
        IdentityService { backend }
    }
}

struct IdentityBox(Box<Identity>);

#[doc(hidden)]
unsafe impl Send for IdentityBox {}
#[doc(hidden)]
unsafe impl Sync for IdentityBox {}

impl<S: 'static, T: IdentityPolicy<S>> Middleware<S> for IdentityService<T> {
    fn start(&mut self, req: &mut HttpRequest<S>) -> Result<Started> {
        let mut req = req.clone();

        let fut = self
            .backend
            .from_request(&mut req)
            .then(move |res| match res {
                Ok(id) => {
                    req.extensions_mut().insert(IdentityBox(Box::new(id)));
                    FutOk(None)
                }
                Err(err) => FutErr(err),
            });
        Ok(Started::Future(Box::new(fut)))
    }

    fn response(
        &mut self, req: &mut HttpRequest<S>, resp: HttpResponse,
    ) -> Result<Response> {
        if let Some(mut id) = req.extensions_mut().remove::<IdentityBox>() {
            id.0.write(resp)
        } else {
            Ok(Response::Done(resp))
        }
    }
}

#[doc(hidden)]
/// Identity that uses private cookies as identity storage.
pub struct CookieIdentity {
    changed: bool,
    identity: Option<String>,
    inner: Rc<CookieIdentityInner>,
}

impl Identity for CookieIdentity {
    fn identity(&self) -> Option<&str> {
        self.identity.as_ref().map(|s| s.as_ref())
    }

    fn remember(&mut self, value: String) {
        self.changed = true;
        self.identity = Some(value);
    }

    fn forget(&mut self) {
        self.changed = true;
        self.identity = None;
    }

    fn write(&mut self, mut resp: HttpResponse) -> Result<Response> {
        if self.changed {
            let _ = self.inner.set_cookie(&mut resp, self.identity.take());
        }
        Ok(Response::Done(resp))
    }
}

struct CookieIdentityInner {
    key: Key,
    name: String,
    path: String,
    domain: Option<String>,
    secure: bool,
    max_age: Option<Duration>,
}

impl CookieIdentityInner {
    fn new(key: &[u8]) -> CookieIdentityInner {
        CookieIdentityInner {
            key: Key::from_master(key),
            name: "actix-identity".to_owned(),
            path: "/".to_owned(),
            domain: None,
            secure: true,
            max_age: None,
        }
    }

    fn set_cookie(&self, resp: &mut HttpResponse, id: Option<String>) -> Result<()> {
        let some = id.is_some();
        {
            let id = id.unwrap_or_else(String::new);
            let mut cookie = Cookie::new(self.name.clone(), id);
            cookie.set_path(self.path.clone());
            cookie.set_secure(self.secure);
            cookie.set_http_only(true);

            if let Some(ref domain) = self.domain {
                cookie.set_domain(domain.clone());
            }

            if let Some(max_age) = self.max_age {
                cookie.set_max_age(max_age);
            }

            let mut jar = CookieJar::new();
            if some {
                jar.private(&self.key).add(cookie);
            } else {
                jar.add_original(cookie.clone());
                jar.private(&self.key).remove(cookie);
            }

            for cookie in jar.delta() {
                let val = HeaderValue::from_str(&cookie.to_string())?;
                resp.headers_mut().append(header::SET_COOKIE, val);
            }
        }

        Ok(())
    }

    fn load<S>(&self, req: &mut HttpRequest<S>) -> Option<String> {
        if let Ok(cookies) = req.cookies() {
            for cookie in cookies {
                if cookie.name() == self.name {
                    let mut jar = CookieJar::new();
                    jar.add_original(cookie.clone());

                    let cookie_opt = jar.private(&self.key).get(&self.name);
                    if let Some(cookie) = cookie_opt {
                        return Some(cookie.value().into());
                    }
                }
            }
        }
        None
    }
}

/// Use cookies for request identity storage.
///
/// The constructors take a key as an argument.
/// This is the private key for cookie - when this value is changed,
/// all identities are lost. The constructors will panic if the key is less
/// than 32 bytes in length.
///
/// # Example
///
/// ```rust
/// # extern crate actix_web;
/// use actix_web::middleware::identity::{CookieIdentityPolicy, IdentityService};
/// use actix_web::App;
///
/// fn main() {
///     let app = App::new().middleware(IdentityService::new(
///         // <- create identity middleware
///         CookieIdentityPolicy::new(&[0; 32])  // <- construct cookie policy
///                .domain("www.rust-lang.org")
///                .name("actix_auth")
///                .path("/")
///                .secure(true),
///     ));
/// }
/// ```
pub struct CookieIdentityPolicy(Rc<CookieIdentityInner>);

impl CookieIdentityPolicy {
    /// Construct new `CookieIdentityPolicy` instance.
    ///
    /// Panics if key length is less than 32 bytes.
    pub fn new(key: &[u8]) -> CookieIdentityPolicy {
        CookieIdentityPolicy(Rc::new(CookieIdentityInner::new(key)))
    }

    /// Sets the `path` field in the session cookie being built.
    pub fn path<S: Into<String>>(mut self, value: S) -> CookieIdentityPolicy {
        Rc::get_mut(&mut self.0).unwrap().path = value.into();
        self
    }

    /// Sets the `name` field in the session cookie being built.
    pub fn name<S: Into<String>>(mut self, value: S) -> CookieIdentityPolicy {
        Rc::get_mut(&mut self.0).unwrap().name = value.into();
        self
    }

    /// Sets the `domain` field in the session cookie being built.
    pub fn domain<S: Into<String>>(mut self, value: S) -> CookieIdentityPolicy {
        Rc::get_mut(&mut self.0).unwrap().domain = Some(value.into());
        self
    }

    /// Sets the `secure` field in the session cookie being built.
    ///
    /// If the `secure` field is set, a cookie will only be transmitted when the
    /// connection is secure - i.e. `https`
    pub fn secure(mut self, value: bool) -> CookieIdentityPolicy {
        Rc::get_mut(&mut self.0).unwrap().secure = value;
        self
    }

    /// Sets the `max-age` field in the session cookie being built.
    pub fn max_age(mut self, value: Duration) -> CookieIdentityPolicy {
        Rc::get_mut(&mut self.0).unwrap().max_age = Some(value);
        self
    }
}

impl<S> IdentityPolicy<S> for CookieIdentityPolicy {
    type Identity = CookieIdentity;
    type Future = FutureResult<CookieIdentity, Error>;

    fn from_request(&self, req: &mut HttpRequest<S>) -> Self::Future {
        let identity = self.0.load(req);
        FutOk(CookieIdentity {
            identity,
            changed: false,
            inner: Rc::clone(&self.0),
        })
    }
}
<|endoftext|>