<commit_before>pub const NRBITS: u32 = 8;
pub const TYPEBITS: u32 = 8;

#[cfg(any(target_arch = "mips", target_arch = "powerpc", target_arch = "powerpc64"))]
mod consts {
    pub const NONE: u8 = 1;
    pub const READ: u8 = 2;
    pub const WRITE: u8 = 4;
    pub const SIZEBITS: u8 = 13;
    pub const DIRBITS: u8 = 3;
}

#[cfg(not(any(target_arch = "powerpc",
              target_arch = "mips",
              target_arch = "x86",
              target_arch = "arm",
              target_arch = "x86_64",
              target_arch = "powerpc64",
              target_arch = "aarch64")))]
use this_arch_not_supported;

// "Generic" ioctl protocol
#[cfg(any(target_arch = "x86",
          target_arch = "arm",
          target_arch = "x86_64",
          target_arch = "aarch64"))]
mod consts {
    pub const NONE: u8 = 0;
    pub const READ: u8 = 2;
    pub const WRITE: u8 = 1;
    pub const SIZEBITS: u8 = 14;
    pub const DIRBITS: u8 = 2;
}

pub use self::consts::*;

pub const NRSHIFT: u32 = 0;
pub const TYPESHIFT: u32 = NRSHIFT + NRBITS as u32;
pub const SIZESHIFT: u32 = TYPESHIFT + TYPEBITS as u32;
pub const DIRSHIFT: u32 = SIZESHIFT + SIZEBITS as u32;

pub const NRMASK: u32 = (1 << NRBITS) - 1;
pub const TYPEMASK: u32 = (1 << TYPEBITS) - 1;
pub const SIZEMASK: u32 = (1 << SIZEBITS) - 1;
pub const DIRMASK: u32 = (1 << DIRBITS) - 1;

/// Encode an ioctl command.
#[macro_export]
macro_rules! ioc {
    ($dir:expr, $ty:expr, $nr:expr, $sz:expr) => (
        (($dir as u32) << $crate::sys::ioctl::DIRSHIFT) |
        (($ty as u32) << $crate::sys::ioctl::TYPESHIFT) |
        (($nr as u32) << $crate::sys::ioctl::NRSHIFT) |
        (($sz as u32) << $crate::sys::ioctl::SIZESHIFT))
}

/// Encode an ioctl command that has no associated data.
#[macro_export]
macro_rules! io {
    ($ty:expr, $nr:expr) => (ioc!($crate::sys::ioctl::NONE, $ty, $nr, 0))
}

/// Encode an ioctl command that reads.
#[macro_export]
macro_rules! ior {
    ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::READ, $ty, $nr, $sz))
}

/// Encode an ioctl command that writes.
#[macro_export]
macro_rules! iow {
    ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::WRITE, $ty, $nr, $sz))
}

/// Encode an ioctl command that both reads and writes.
#[macro_export]
macro_rules! iorw {
    ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::READ | $crate::sys::ioctl::WRITE, $ty, $nr, $sz))
}

/// Extracts the "direction" (read/write/none) from an encoded ioctl command.
#[inline(always)]
pub fn ioc_dir(nr: u32) -> u8 {
    ((nr >> DIRSHIFT) & DIRMASK) as u8
}

/// Extracts the type from an encoded ioctl command.
#[inline(always)]
pub fn ioc_type(nr: u32) -> u32 {
    (nr >> TYPESHIFT) & TYPEMASK
}

/// Extracts the ioctl number from an encoded ioctl command.
#[inline(always)]
pub fn ioc_nr(nr: u32) -> u32 {
    (nr >> NRSHIFT) & NRMASK
}

/// Extracts the size from an encoded ioctl command.
#[inline(always)]
pub fn ioc_size(nr: u32) -> u32 {
    ((nr >> SIZESHIFT) as u32) & SIZEMASK
}

pub const IN: u32 = (WRITE as u32) << DIRSHIFT;
pub const OUT: u32 = (READ as u32) << DIRSHIFT;
pub const INOUT: u32 = ((READ|WRITE) as u32) << DIRSHIFT;
pub const SIZE_MASK: u32 = SIZEMASK << SIZESHIFT;
<commit_msg>Enable ioctl on mips64 and s390x<commit_after>pub const NRBITS: u32 = 8;
pub const TYPEBITS: u32 = 8;

#[cfg(any(target_arch = "mips", target_arch = "mips64", target_arch = "powerpc", target_arch = "powerpc64"))]
mod consts {
    pub const NONE: u8 = 1;
    pub const READ: u8 = 2;
    pub const WRITE: u8 = 4;
    pub const SIZEBITS: u8 = 13;
    pub const DIRBITS: u8 = 3;
}

#[cfg(not(any(target_arch = "powerpc",
              target_arch = "mips",
              target_arch = "mips64",
              target_arch = "x86",
              target_arch = "arm",
              target_arch = "x86_64",
              target_arch = "powerpc64",
              target_arch = "s390x",
              target_arch = "aarch64")))]
use this_arch_not_supported;

// "Generic" ioctl protocol
#[cfg(any(target_arch = "x86",
          target_arch = "arm",
          target_arch = "s390x",
          target_arch = "x86_64",
          target_arch = "aarch64"))]
mod consts {
    pub const NONE: u8 = 0;
    pub const READ: u8 = 2;
    pub const WRITE: u8 = 1;
    pub const SIZEBITS: u8 = 14;
    pub const DIRBITS: u8 = 2;
}

pub use self::consts::*;

pub const NRSHIFT: u32 = 0;
pub const TYPESHIFT: u32 = NRSHIFT + NRBITS as u32;
pub const SIZESHIFT: u32 = TYPESHIFT + TYPEBITS as u32;
pub const DIRSHIFT: u32 = SIZESHIFT + SIZEBITS as u32;

pub const NRMASK: u32 = (1 << NRBITS) - 1;
pub const TYPEMASK: u32 = (1 << TYPEBITS) - 1;
pub const SIZEMASK: u32 = (1 << SIZEBITS) - 1;
pub const DIRMASK: u32 = (1 << DIRBITS) - 1;

/// Encode an ioctl command.
#[macro_export]
macro_rules! ioc {
    ($dir:expr, $ty:expr, $nr:expr, $sz:expr) => (
        (($dir as u32) << $crate::sys::ioctl::DIRSHIFT) |
        (($ty as u32) << $crate::sys::ioctl::TYPESHIFT) |
        (($nr as u32) << $crate::sys::ioctl::NRSHIFT) |
        (($sz as u32) << $crate::sys::ioctl::SIZESHIFT))
}

/// Encode an ioctl command that has no associated data.
#[macro_export]
macro_rules! io {
    ($ty:expr, $nr:expr) => (ioc!($crate::sys::ioctl::NONE, $ty, $nr, 0))
}

/// Encode an ioctl command that reads.
#[macro_export]
macro_rules! ior {
    ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::READ, $ty, $nr, $sz))
}

/// Encode an ioctl command that writes.
#[macro_export]
macro_rules! iow {
    ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::WRITE, $ty, $nr, $sz))
}

/// Encode an ioctl command that both reads and writes.
#[macro_export]
macro_rules! iorw {
    ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::READ | $crate::sys::ioctl::WRITE, $ty, $nr, $sz))
}

/// Extracts the "direction" (read/write/none) from an encoded ioctl command.
#[inline(always)]
pub fn ioc_dir(nr: u32) -> u8 {
    ((nr >> DIRSHIFT) & DIRMASK) as u8
}

/// Extracts the type from an encoded ioctl command.
#[inline(always)]
pub fn ioc_type(nr: u32) -> u32 {
    (nr >> TYPESHIFT) & TYPEMASK
}

/// Extracts the ioctl number from an encoded ioctl command.
#[inline(always)]
pub fn ioc_nr(nr: u32) -> u32 {
    (nr >> NRSHIFT) & NRMASK
}

/// Extracts the size from an encoded ioctl command.
#[inline(always)]
pub fn ioc_size(nr: u32) -> u32 {
    ((nr >> SIZESHIFT) as u32) & SIZEMASK
}

pub const IN: u32 = (WRITE as u32) << DIRSHIFT;
pub const OUT: u32 = (READ as u32) << DIRSHIFT;
pub const INOUT: u32 = ((READ|WRITE) as u32) << DIRSHIFT;
pub const SIZE_MASK: u32 = SIZEMASK << SIZESHIFT;
<|endoftext|>


<commit_before>use std::str::{Chars, from_char};
use collections::hashmap::HashMap;
use util::{GossipResult, GossipError};

pub enum Protocol {
    Version(uint),
    Binary(Vec<u8>),
    Text(StrBuf)
}

pub enum TextProtocol {
    Key(StrBuf),
    Value(Vec<u8>)
}

#[deriving(Eq,Show)]
pub enum TextAst {
    Empty,
    KeyVal(StrBuf, StrBuf)
}

// The TextProtocol's parser. All we need to spit out is a key->value combination.
// Because we allow streaming within the protocol, we'll only return each
// key->value pair once we have fully parsed both.
//
// There could be an option to receive the raw data as we get it for the value,
// but not the key.
//
// ```rust
// Parser::new("hello world")
// ```
pub struct Parser<'a> {
    input: &'a str,
    iter: Chars<'a>
}

impl<'a> Parser<'a> {
    pub fn new(input: &'a str) -> Parser<'a> {
        Parser {
            input: input,
            iter: input.chars()
        }
    }

    pub fn parse_kv(&mut self, ch: char) -> TextAst {

        // Instantiate default strings that we can append to.
        let mut key   = "".to_strbuf();
        let mut value = "".to_strbuf();
        let mut c     = ch;

        key = key.append(from_char(c));
        while c != '[' {
            c = self.iter.next().unwrap();

            if c != '[' {
                key = key.append(from_char(c));
            }
        }

        // Start the value
        if c == '[' {
            while c != ']' {
                c = self.iter.next().unwrap();

                if c != ']' {
                    value = value.append(from_char(c));
                }
            }
        }

        KeyVal(key, value)
    }

    pub fn parse(&mut self) -> GossipResult<HashMap<StrBuf, StrBuf>> {

        if self.input.len() == 0 {
            return Err(GossipError::new("Failed to parse an empty string".to_strbuf(), None));
        }

        let mut kv = HashMap::<StrBuf, StrBuf>::new();
        let mut c = self.iter.next().unwrap();

        if c.is_alphanumeric() {
            let mut key = "".to_strbuf();
            let mut value = "".to_strbuf();

            match self.parse_kv(c) {
                KeyVal(k, v) => {
                    key = k;
                    value = v;
                },
                _ => {}
            }

            kv.insert(key, value);
        }

        Ok(kv)
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use util::GossipError;

    #[test]
    fn new_parser() {
        let mut parser = Parser::new("hello world");
        assert_eq!(parser.iter.next().unwrap(), 'h');
    }

    #[test]
    fn parser_parse_empty_string() {
        let mut parser = Parser::new("");
        match parser.parse() {
            Err(s) => {},
            _ => fail!("Expected an error")
        }
    }

    #[test]
    fn parse_simple_kv() {
        let mut parser = Parser::new("Version[1]");
        //assert_eq!(parser.parse(), KeyVal("Version".to_owned(), "1".to_owned()));
    }
}<commit_msg>Removed the text parser<commit_after>use std::str::{Chars, from_char};
use collections::hashmap::HashMap;
use util::{GossipResult, GossipError};

pub enum Protocol {
    Version(uint),
    Binary(Vec<u8>)
}

#[cfg(test)]
mod test {
    use super::*;
    use util::GossipError;
}<|endoftext|>


<commit_before>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! # RangeDfa
//!
//! A DFA that matches transitions against symbol ranges.
//!

use std::marker::PhantomData;

use super::dfa_builder::*;
use super::pattern_matcher::*;
use super::symbol_range::*;
use super::state_machine::*;

///
/// DFA that decides on transitions based on non-overlapping, sorted lists of input symbols
///
pub struct SymbolRangeDfa<InputSymbol: PartialOrd, OutputSymbol> {
    //
    // Indexes of where each state starts in the transition table (it ends at the start of the next state)
    //
    states: Vec<usize>,

    //
    // The transitions making up this DFA
    //
    transitions: Vec<(SymbolRange<InputSymbol>, StateId)>,

    //
    // The accepting symbol for each state
    //
    accept: Vec<Option<OutputSymbol>>
}

///
/// DFA builder that creates RangeDfas
///
pub struct SymbolRangeDfaBuilder<InputSymbol: PartialOrd, OutputSymbol> {
    states: Vec<usize>,
    transitions: Vec<(SymbolRange<InputSymbol>, StateId)>,
    accept: Vec<Option<OutputSymbol>>
}

impl<InputSymbol: PartialOrd, OutputSymbol> SymbolRangeDfaBuilder<InputSymbol, OutputSymbol> {
    pub fn new() -> SymbolRangeDfaBuilder<InputSymbol, OutputSymbol> {
        SymbolRangeDfaBuilder { states: vec![], transitions: vec![], accept: vec![] }
    }
}

impl<InputSymbol: PartialOrd, OutputSymbol> DfaBuilder<SymbolRange<InputSymbol>, OutputSymbol, SymbolRangeDfa<InputSymbol, OutputSymbol>> for SymbolRangeDfaBuilder<InputSymbol, OutputSymbol> {
    fn start_state(&mut self) {
        // Begin the next state
        self.states.push(self.transitions.len());
        self.accept.push(None);
    }

    fn transition(&mut self, symbol: SymbolRange<InputSymbol>, target_state: StateId) {
        self.transitions.push((symbol, target_state));
    }

    fn accept(&mut self, symbol: OutputSymbol) {
        self.accept.pop();
        self.accept.push(Some(symbol));
    }

    fn build(self) -> SymbolRangeDfa<InputSymbol, OutputSymbol> {
        // Turn into a RangeDfa
        let mut result = SymbolRangeDfa { states: self.states, transitions: self.transitions, accept: self.accept };

        // 'Cap' the last state so we don't need to special-case it later
        // ie, we can always find the index of the last symbol by looking at the next state and don't need to handle the final state differently
        result.states.push(result.transitions.len());

        result
    }
}

///
/// A state of a symbol range state machine
///
pub struct SymbolRangeState<'a, InputSymbol: PartialOrd+'a, OutputSymbol: Sized+'a> {
    // The current state of the state machine
    state: StateId,

    // The number of symbols that have been processed so far
    count: usize,

    // If something other than none, the most recent accepting state
    accept: Option<(usize, &'a OutputSymbol)>,

    // The state machine this is running
    state_machine: &'a SymbolRangeDfa<InputSymbol, OutputSymbol>
}

impl<'a, InputSymbol: PartialOrd+'a, OutputSymbol: Sized+'a> PatternMatcher<'a, InputSymbol, OutputSymbol> for &'a SymbolRangeDfa<InputSymbol, OutputSymbol> {
    type State = SymbolRangeState<'a, InputSymbol, OutputSymbol>;

    fn start(&self) -> Self::State {
        // TODO: if state 0 is accepting, then this will erroneously not move straight to the accepting state
        SymbolRangeState { state: 0, count: 0, accept: None, state_machine: self }
    }
}

impl<'a, InputSymbol: PartialOrd+'a, OutputSymbol: Sized+'a> MatchingState<'a, InputSymbol, OutputSymbol> for SymbolRangeState<'a, InputSymbol, OutputSymbol> {
    fn next(self, symbol: InputSymbol) -> MatchAction<'a, OutputSymbol, Self> {
        // The transition range is defined by the current state
        let start_transition    = self.state_machine.states[self.state as usize];
        let end_transition      = self.state_machine.states[self.state as usize+1];

        // See if there is an input symbol matching this transition
        // TODO: consider binary searching for states with large numbers of transitions? (Do these occur regularly in patterns that people use?)
        for transit in start_transition..end_transition {
            // Test this transition
            let (ref range, new_state) = self.state_machine.transitions[transit];

            if range.includes(&symbol) {
                // Found a transition to a new state: result will be `More(new state)`
                let new_count = self.count+1;

                // If the new state is an accepting state, then remember it in case we reach a rejecting state later
                let new_accept = if let Some(ref output) = self.state_machine.accept[new_state as usize] {
                    // Some((new_count, output)) // output symbol can't be copied, lifetime?
                    None
                } else {
                    self.accept
                };

                // Action is 'More'
                // TODO: might be an option to return Accept or Reject here if the new state has no transitions
                // (Possible performance advantage, but depends on the regex and input conditions)
                return More(SymbolRangeState { state: new_state, count: new_count, accept: new_accept, state_machine: self.state_machine });
            }
        }

        // No matches: finish the state machine
        self.finish()
    }

    fn finish(self) -> MatchAction<'a, OutputSymbol, Self> {
        if let Some(accept_state) = self.accept {
            // We found an accepting state earlier on, so return that
            let (length, symbol) = accept_state;
            Accept(length, symbol)
        } else {
            // No accepting state was found while this state machine was running
            Reject
        }
    }
}
<commit_msg>Properly generate accepting states for the DFA<commit_after>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! # RangeDfa
//!
//! A DFA that matches transitions against symbol ranges.
//!

use super::dfa_builder::*;
use super::pattern_matcher::*;
use super::symbol_range::*;
use super::state_machine::*;

///
/// DFA that decides on transitions based on non-overlapping, sorted lists of input symbols
///
pub struct SymbolRangeDfa<InputSymbol: PartialOrd, OutputSymbol> {
    //
    // Indexes of where each state starts in the transition table (it ends at the start of the next state)
    //
    states: Vec<usize>,

    //
    // The transitions making up this DFA
    //
    transitions: Vec<(SymbolRange<InputSymbol>, StateId)>,

    //
    // The accepting symbol for each state
    //
    accept: Vec<Option<OutputSymbol>>
}

///
/// DFA builder that creates RangeDfas
///
pub struct SymbolRangeDfaBuilder<InputSymbol: PartialOrd, OutputSymbol> {
    states: Vec<usize>,
    transitions: Vec<(SymbolRange<InputSymbol>, StateId)>,
    accept: Vec<Option<OutputSymbol>>
}

impl<InputSymbol: PartialOrd, OutputSymbol> SymbolRangeDfaBuilder<InputSymbol, OutputSymbol> {
    pub fn new() -> SymbolRangeDfaBuilder<InputSymbol, OutputSymbol> {
        SymbolRangeDfaBuilder { states: vec![], transitions: vec![], accept: vec![] }
    }
}

impl<InputSymbol: PartialOrd, OutputSymbol> DfaBuilder<SymbolRange<InputSymbol>, OutputSymbol, SymbolRangeDfa<InputSymbol, OutputSymbol>> for SymbolRangeDfaBuilder<InputSymbol, OutputSymbol> {
    fn start_state(&mut self) {
        // Begin the next state
        self.states.push(self.transitions.len());
        self.accept.push(None);
    }

    fn transition(&mut self, symbol: SymbolRange<InputSymbol>, target_state: StateId) {
        self.transitions.push((symbol, target_state));
    }

    fn accept(&mut self, symbol: OutputSymbol) {
        self.accept.pop();
        self.accept.push(Some(symbol));
    }

    fn build(self) -> SymbolRangeDfa<InputSymbol, OutputSymbol> {
        // Turn into a RangeDfa
        let mut result = SymbolRangeDfa { states: self.states, transitions: self.transitions, accept: self.accept };

        // 'Cap' the last state so we don't need to special-case it later
        // ie, we can always find the index of the last symbol by looking at the next state and don't need to handle the final state differently
        result.states.push(result.transitions.len());

        result
    }
}

///
/// A state of a symbol range state machine
///
pub struct SymbolRangeState<'a, InputSymbol: PartialOrd+'a, OutputSymbol: Sized+'a> {
    // The current state of the state machine
    state: StateId,

    // The number of symbols that have been processed so far
    count: usize,

    // If something other than none, the most recent accepting state
    accept: Option<(usize, &'a OutputSymbol)>,

    // The state machine this is running
    state_machine: &'a SymbolRangeDfa<InputSymbol, OutputSymbol>
}

impl<'a, InputSymbol: PartialOrd+'a, OutputSymbol: Sized+'a> PatternMatcher<'a, InputSymbol, OutputSymbol> for &'a SymbolRangeDfa<InputSymbol, OutputSymbol> {
    type State = SymbolRangeState<'a, InputSymbol, OutputSymbol>;

    fn start(&self) -> Self::State {
        // TODO: if state 0 is accepting, then this will erroneously not move straight to the accepting state
        SymbolRangeState { state: 0, count: 0, accept: None, state_machine: self }
    }
}

impl<'a, InputSymbol: PartialOrd+'a, OutputSymbol: Sized+'a> MatchingState<'a, InputSymbol, OutputSymbol> for SymbolRangeState<'a, InputSymbol, OutputSymbol> {
    fn next(self, symbol: InputSymbol) -> MatchAction<'a, OutputSymbol, Self> {
        // The transition range is defined by the current state
        let start_transition    = self.state_machine.states[self.state as usize];
        let end_transition      = self.state_machine.states[self.state as usize+1];

        // See if there is an input symbol matching this transition
        // TODO: consider binary searching for states with large numbers of transitions? (Do these occur regularly in patterns that people use?)
        for transit in start_transition..end_transition {
            // Test this transition
            let (ref range, new_state) = self.state_machine.transitions[transit];

            if range.includes(&symbol) {
                // Found a transition to a new state: result will be `More(new state)`
                let new_count = self.count+1;

                // If the new state is an accepting state, then remember it in case we reach a rejecting state later
                let new_accept = if let Some(ref output) = self.state_machine.accept[new_state as usize] {
                    Some((new_count, output))
                } else {
                    self.accept
                };

                // Action is 'More'
                // TODO: might be an option to return Accept or Reject here if the new state has no transitions
                // (Possible performance advantage, but depends on the regex and input conditions)
                return More(SymbolRangeState { state: new_state, count: new_count, accept: new_accept, state_machine: self.state_machine });
            }
        }

        // No matches: finish the state machine
        self.finish()
    }

    fn finish(self) -> MatchAction<'a, OutputSymbol, Self> {
        if let Some(accept_state) = self.accept {
            // We found an accepting state earlier on, so return that
            let (length, symbol) = accept_state;
            Accept(length, symbol)
        } else {
            // No accepting state was found while this state machine was running
            Reject
        }
    }
}
<|endoftext|>


<commit_before>//! Verifies that we can read data messages even if we have initiated a close handshake,
//! but before we got confirmation.

use std::{
    net::TcpListener,
    process::exit,
    thread::{sleep, spawn},
    time::Duration,
};

use tungstenite::{accept, connect, error::ProtocolErrorType, Error, Message};
use url::Url;

#[test]
fn test_no_send_after_close() {
    env_logger::init();

    spawn(|| {
        sleep(Duration::from_secs(5));
        println!("Unit test executed too long, perhaps stuck on WOULDBLOCK...");
        exit(1);
    });

    let server = TcpListener::bind("127.0.0.1:3013").unwrap();

    let client_thread = spawn(move || {
        let (mut client, _) = connect(Url::parse("ws://localhost:3013/socket").unwrap()).unwrap();

        let message = client.read_message().unwrap(); // receive close from server
        assert!(message.is_close());

        let err = client.read_message().unwrap_err(); // now we should get ConnectionClosed
        match err {
            Error::ConnectionClosed => {}
            _ => panic!("unexpected error: {:?}", err),
        }
    });

    let client_handler = server.incoming().next().unwrap();
    let mut client_handler = accept(client_handler.unwrap()).unwrap();

    client_handler.close(None).unwrap(); // send close to client

    let err = client_handler.write_message(Message::Text("Hello WebSocket".into()));

    assert!(err.is_err());

    match err.unwrap_err() {
        Error::Protocol(s) => assert_eq!(s, ProtocolErrorType::SendAfterClosing),
        e => panic!("unexpected error: {:?}", e),
    }

    drop(client_handler);

    client_thread.join().unwrap();
}
<commit_msg>Change ProtocolErrorType to ProtocolError in test<commit_after>//! Verifies that we can read data messages even if we have initiated a close handshake,
//! but before we got confirmation.

use std::{
    net::TcpListener,
    process::exit,
    thread::{sleep, spawn},
    time::Duration,
};

use tungstenite::{accept, connect, error::ProtocolError, Error, Message};
use url::Url;

#[test]
fn test_no_send_after_close() {
    env_logger::init();

    spawn(|| {
        sleep(Duration::from_secs(5));
        println!("Unit test executed too long, perhaps stuck on WOULDBLOCK...");
        exit(1);
    });

    let server = TcpListener::bind("127.0.0.1:3013").unwrap();

    let client_thread = spawn(move || {
        let (mut client, _) = connect(Url::parse("ws://localhost:3013/socket").unwrap()).unwrap();

        let message = client.read_message().unwrap(); // receive close from server
        assert!(message.is_close());

        let err = client.read_message().unwrap_err(); // now we should get ConnectionClosed
        match err {
            Error::ConnectionClosed => {}
            _ => panic!("unexpected error: {:?}", err),
        }
    });

    let client_handler = server.incoming().next().unwrap();
    let mut client_handler = accept(client_handler.unwrap()).unwrap();

    client_handler.close(None).unwrap(); // send close to client

    let err = client_handler.write_message(Message::Text("Hello WebSocket".into()));

    assert!(err.is_err());

    match err.unwrap_err() {
        Error::Protocol(s) => assert_eq!(s, ProtocolError::SendAfterClosing),
        e => panic!("unexpected error: {:?}", e),
    }

    drop(client_handler);

    client_thread.join().unwrap();
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use serde::{Deserialize, Serialize};
use serde_json::json;
use thiserror::Error;

/// Request timeout for github operations
const ACCEPT_HEADER: &str = "Accept";
const ACCEPT_VALUE: &str = "Accept: application/vnd.github.v3+json";
const TIMEOUT: u64 = 10_000;
const URL: &str = "https://api.github.com";

#[derive(Debug, Error, PartialEq)]
pub enum Error {
    #[error("Http error: {1}")]
    HttpError(u16, String),
    #[error("Internal error: {0}")]
    InternalError(String),
    #[error("Missing field {0}")]
    MissingField(String),
    #[error("404: Not Found: {0}")]
    NotFound(String),
    #[error("Serialization error: {0}")]
    SerializationError(String),
}

impl From<std::io::Error> for Error {
    fn from(error: std::io::Error) -> Self {
        Self::SerializationError(format!("{}", error))
    }
}

impl From<ureq::Response> for Error {
    fn from(resp: ureq::Response) -> Self {
        Error::HttpError(resp.status(), resp.status_line().into())
    }
}

impl From<serde_json::Error> for Error {
    fn from(error: serde_json::Error) -> Self {
        Self::SerializationError(format!("{}", error))
    }
}

/// Client provides a client around the restful interface to GitHub API version 3. Learn more
/// here: https://developer.github.com/v3
///
/// This is not intended for securely storing private data, though perhaps it could with a private
/// repository. The tooling is intended to be used to exchange data in an authenticated fashion
/// across multiple peers.
pub struct Client {
    repository: String,
    owner: String,
    token: String,
}

impl Client {
    pub fn new(owner: String, repository: String, token: String) -> Self {
        Self {
            repository,
            owner,
            token,
        }
    }

    /// Delete a file from a GitHub repository
    pub fn delete_file(&self, path: &str) -> Result<(), Error> {
        // Occasionally GitHub sends us back delayed results and the file is already deleted.
        let hash = match self.get_sha(path) {
            Ok(hash) => hash,
            Err(Error::NotFound(_)) => return Ok(()),
            Err(e) => return Err(e),
        };

        let resp = self
            .upgrade_request(ureq::delete(&self.url(path)))
            .send_json(json!({ "message": "libra-secure", "sha": hash }));

        match resp.status() {
            200 => Ok(()),
            _ => Err(resp.into()),
        }
    }

    /// Recursively delete all files, which as a by product will delete all folders
    pub fn delete_directory(&self, path: &str) -> Result<(), Error> {
        let files = self.get_directory(path)?;
        for file in files {
            if file.ends_with('/') {
                self.delete_directory(&file[..file.len() - 1])?;
                continue;
            }
            self.delete_file(&file)?;
        }
        Ok(())
    }

    /// Retrieve a list of branches, this is effectively a status check on the repository
    pub fn get_branches(&self) -> Result<Vec<String>, Error> {
        let url = format!("{}/repos/{}/{}/branches", URL, self.owner, self.repository);
        let resp = self.upgrade_request(ureq::get(&url)).call();

        match resp.status() {
            200 => {
                let resp = resp.into_string()?;
                let branches: Vec<Branch> = serde_json::from_str(&resp)?;
                Ok(branches.into_iter().map(|b| b.name).collect())
            }
            _ => Err(resp.into()),
        }
    }

    /// Retrieve the name of contents within a given directory, note, there are no such thing as
    /// empty directories in git.
    pub fn get_directory(&self, path: &str) -> Result<Vec<String>, Error> {
        Ok(self
            .get_internal(path)?
            .into_iter()
            .map(|entry| {
                if entry.content_type == "dir" {
                    entry.path + "/"
                } else {
                    entry.path
                }
            })
            .collect())
    }

    /// Retrieve the contents of a file.
    pub fn get_file(&self, path: &str) -> Result<String, Error> {
        let value = self.get_internal(path)?;
        if value.len() == 1 && value[0].path == path {
            let content = value[0]
                .content
                .as_ref()
                .ok_or_else(|| Error::InternalError("No content found".into()))?;
            // Apparently GitHub introduces newlines every 60 characters and at the end of content,
            // this strips those characters out.
            Ok(content.lines().collect::<Vec<_>>().join(""))
        } else {
            Err(Error::InternalError(format!(
                "get mismatch, found {} entries",
                value.len()
            )))
        }
    }

    /// Create or update a file.
    pub fn put(&self, path: &str, content: &str) -> Result<(), Error> {
        let json = match self.get_sha(path) {
            Ok(hash) => json!({ "content": content, "message": "libra-secure", "sha": hash }),
            Err(Error::NotFound(_)) => json!({ "content": content, "message": "libra-secure" }),
            Err(e) => return Err(e),
        };

        let resp = self
            .upgrade_request(ureq::put(&self.url(path)))
            .send_json(json);

        match resp.status() {
            200 => Ok(()),
            201 => Ok(()),
            _ => Err(resp.into()),
        }
    }

    /// Simple wrapper around requests to add default parameters to the request
    fn upgrade_request(&self, mut request: ureq::Request) -> ureq::Request {
        request
            .set("Authorization", &format!("token {}", self.token))
            .set(ACCEPT_HEADER, ACCEPT_VALUE)
            .timeout_connect(TIMEOUT);
        request
    }

    /// Get can read files or directories, this makes it easier to use
    fn get_internal(&self, path: &str) -> Result<Vec<GetResponse>, Error> {
        let resp = self.upgrade_request(ureq::get(&self.url(path))).call();
        match resp.status() {
            200 => {
                let resp = resp.into_string()?;
                let get_resp: Result<GetResponse, Error> =
                    serde_json::from_str(&resp).map_err(|e| e.into());

                if let Ok(get_resp) = get_resp {
                    return Ok(vec![get_resp]);
                }

                let get_resp: Result<Vec<GetResponse>, Error> =
                    serde_json::from_str(&resp).map_err(|e| e.into());
                if let Ok(get_resp) = get_resp {
                    return Ok(get_resp);
                }

                Err(Error::SerializationError(resp))
            }
            404 => Err(Error::NotFound(path.into())),
            _ => Err(resp.into()),
        }
    }

    /// Returns the sha hash of a file according to GitHub
    fn get_sha(&self, path: &str) -> Result<String, Error> {
        let value = self.get_internal(path)?;
        if let Some(value) = value.iter().find(|entry| entry.path == path) {
            Ok(value.sha.clone())
        } else {
            // This is a directory
            Err(Error::InternalError(format!(
                "get mismatch, found {} entries",
                value.len()
            )))
        }
    }

    fn url(&self, path: &str) -> String {
        format!(
            "{}/repos/{}/{}/contents/{}",
            URL, self.owner, self.repository, path
        )
    }
}

#[derive(Debug, Deserialize, PartialEq, Serialize)]
struct Branch {
    name: String,
}

#[derive(Debug, Deserialize, PartialEq, Serialize)]
struct GetResponse {
    #[serde(rename = "type")]
    content_type: String,
    path: String,
    sha: String,
    content: Option<String>,
}

// This test suite depends on the developer providing an active repository, the account where that
// repository is hosted, and a token that can modify that repository. Due to the nature of these
// tests, they cannot be provided in this framework and the tests are labeled as ignored
//
// GitHub updates can misbehave due to changes in file structure thus dependent operations can
// fail, for example, an update of a file reads its hash and then updates. If another operation
// happens after the hash retrieval but before the update, the hash will change resulting in a
// failed update. Therefore these tests must be run in series via:
// `cargo xtest -- --ignored --test-threads=1`
#[cfg(test)]
mod tests {
    use super::*;

    const OWNER: &str = "OWNER";
    const REPOSITORY: &str = "REPOSITORY";
    const TOKEN: &str = "TOKEN";

    #[ignore]
    #[test]
    fn test_files() {
        let path = "data.txt";
        let value1 = "hello";
        let value1_encoded = base64::encode(&value1);
        let value2 = "world";
        let value2_encoded = base64::encode(&value2);

        let github = Client::new(OWNER.into(), REPOSITORY.into(), TOKEN.into());

        // Try a create
        github.get_file(path).unwrap_err();
        github.put(path, &value1_encoded).unwrap();
        let b64_value = base64::decode(github.get_file(path).unwrap()).unwrap();
        let value = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, value1);

        // Try an update
        github.put(path, &value2_encoded).unwrap();
        let b64_value = base64::decode(github.get_file(path).unwrap()).unwrap();
        let value = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, value2);

        // Verify delete_file
        github.delete_file(path).unwrap();
        github.get_file(path).unwrap_err();
    }

    #[ignore]
    #[test]
    fn test_directories() {
        let path1_root = "dir";
        let path1 = "dir/data1.txt";
        let value1 = "hello";
        let value1_encoded = base64::encode(&value1);

        let path2_root = "dir1";
        let path2 = "dir1/data1.txt";
        let value2 = "world";
        let value2_encoded = base64::encode(&value2);

        let github = Client::new(OWNER.into(), REPOSITORY.into(), TOKEN.into());

        // Initialize two directories with a file each
        github.put(path1, &value1_encoded).unwrap();
        github.put(path2, &value2_encoded).unwrap();

        // Verify the contents
        let b64_value = base64::decode(github.get_file(path1).unwrap()).unwrap();
        let value = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, value1);

        let b64_value = base64::decode(github.get_file(path2).unwrap()).unwrap();
        let value = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, value2);

        // Delete one of the directories
        github.delete_file(path1).unwrap();

        // Verify one is good and the other is gone
        github.get_directory(path1_root).unwrap_err();

        let b64_value = base64::decode(github.get_file(path2).unwrap()).unwrap();
        let value = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, value2);

        // Cleanup the rest
        github.delete_file(path2).unwrap();
        github.get_directory(path2_root).unwrap_err();
    }

    #[ignore]
    #[test]
    fn test_recursive_delete_file() {
        let root = "root_dir";
        let file0 = "root_dir/another_dir/another_dir/ok.txt";
        let file1 = "root_dir/another_dir/ok.txt";
        let value = "hello";
        let value_encoded = base64::encode(&value);

        let github = Client::new(OWNER.into(), REPOSITORY.into(), TOKEN.into());

        github.put(file0, &value_encoded).unwrap();
        github.put(file1, &value_encoded).unwrap();

        let b64_value = base64::decode(github.get_file(file0).unwrap()).unwrap();
        let rv = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, rv);

        let b64_value = base64::decode(github.get_file(file1).unwrap()).unwrap();
        let rv = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, rv);

        github.delete_directory(root).unwrap();
        github.get_file(file0).unwrap_err();
        github.get_file(file1).unwrap_err();
    }

    #[ignore]
    #[test]
    fn test_branches() {
        let github = Client::new(OWNER.into(), REPOSITORY.into(), TOKEN.into());
        let branches = github.get_branches().unwrap();
        assert!(!branches.is_empty());
        assert!(branches.iter().any(|b| b == "master"));
    }
}
<commit_msg>[secure-storage] Support HTTPS proxy for Github backend<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use serde::{Deserialize, Serialize};
use serde_json::json;
use std::env;
use thiserror::Error;

/// Request timeout for github operations
const ACCEPT_HEADER: &str = "Accept";
const ACCEPT_VALUE: &str = "Accept: application/vnd.github.v3+json";
const TIMEOUT: u64 = 10_000;
const URL: &str = "https://api.github.com";

#[derive(Debug, Error, PartialEq)]
pub enum Error {
    #[error("Http error: {1}")]
    HttpError(u16, String),
    #[error("Internal error: {0}")]
    InternalError(String),
    #[error("Missing field {0}")]
    MissingField(String),
    #[error("404: Not Found: {0}")]
    NotFound(String),
    #[error("Serialization error: {0}")]
    SerializationError(String),
}

impl From<std::io::Error> for Error {
    fn from(error: std::io::Error) -> Self {
        Self::SerializationError(format!("{}", error))
    }
}

impl From<ureq::Response> for Error {
    fn from(resp: ureq::Response) -> Self {
        Error::HttpError(resp.status(), resp.status_line().into())
    }
}

impl From<serde_json::Error> for Error {
    fn from(error: serde_json::Error) -> Self {
        Self::SerializationError(format!("{}", error))
    }
}

/// Client provides a client around the restful interface to GitHub API version 3. Learn more
/// here: https://developer.github.com/v3
///
/// This is not intended for securely storing private data, though perhaps it could with a private
/// repository. The tooling is intended to be used to exchange data in an authenticated fashion
/// across multiple peers.
pub struct Client {
    repository: String,
    owner: String,
    token: String,
}

impl Client {
    pub fn new(owner: String, repository: String, token: String) -> Self {
        Self {
            repository,
            owner,
            token,
        }
    }

    /// Delete a file from a GitHub repository
    pub fn delete_file(&self, path: &str) -> Result<(), Error> {
        // Occasionally GitHub sends us back delayed results and the file is already deleted.
        let hash = match self.get_sha(path) {
            Ok(hash) => hash,
            Err(Error::NotFound(_)) => return Ok(()),
            Err(e) => return Err(e),
        };

        let resp = self
            .upgrade_request(ureq::delete(&self.url(path)))
            .send_json(json!({ "message": "libra-secure", "sha": hash }));

        match resp.status() {
            200 => Ok(()),
            _ => Err(resp.into()),
        }
    }

    /// Recursively delete all files, which as a by product will delete all folders
    pub fn delete_directory(&self, path: &str) -> Result<(), Error> {
        let files = self.get_directory(path)?;
        for file in files {
            if file.ends_with('/') {
                self.delete_directory(&file[..file.len() - 1])?;
                continue;
            }
            self.delete_file(&file)?;
        }
        Ok(())
    }

    /// Retrieve a list of branches, this is effectively a status check on the repository
    pub fn get_branches(&self) -> Result<Vec<String>, Error> {
        let url = format!("{}/repos/{}/{}/branches", URL, self.owner, self.repository);
        let resp = self.upgrade_request(ureq::get(&url)).call();

        match resp.status() {
            200 => {
                let resp = resp.into_string()?;
                let branches: Vec<Branch> = serde_json::from_str(&resp)?;
                Ok(branches.into_iter().map(|b| b.name).collect())
            }
            _ => Err(resp.into()),
        }
    }

    /// Retrieve the name of contents within a given directory, note, there are no such thing as
    /// empty directories in git.
    pub fn get_directory(&self, path: &str) -> Result<Vec<String>, Error> {
        Ok(self
            .get_internal(path)?
            .into_iter()
            .map(|entry| {
                if entry.content_type == "dir" {
                    entry.path + "/"
                } else {
                    entry.path
                }
            })
            .collect())
    }

    /// Retrieve the contents of a file.
    pub fn get_file(&self, path: &str) -> Result<String, Error> {
        let value = self.get_internal(path)?;
        if value.len() == 1 && value[0].path == path {
            let content = value[0]
                .content
                .as_ref()
                .ok_or_else(|| Error::InternalError("No content found".into()))?;
            // Apparently GitHub introduces newlines every 60 characters and at the end of content,
            // this strips those characters out.
            Ok(content.lines().collect::<Vec<_>>().join(""))
        } else {
            Err(Error::InternalError(format!(
                "get mismatch, found {} entries",
                value.len()
            )))
        }
    }

    /// Create or update a file.
    pub fn put(&self, path: &str, content: &str) -> Result<(), Error> {
        let json = match self.get_sha(path) {
            Ok(hash) => json!({ "content": content, "message": "libra-secure", "sha": hash }),
            Err(Error::NotFound(_)) => json!({ "content": content, "message": "libra-secure" }),
            Err(e) => return Err(e),
        };

        let resp = self
            .upgrade_request(ureq::put(&self.url(path)))
            .send_json(json);

        match resp.status() {
            200 => Ok(()),
            201 => Ok(()),
            _ => Err(resp.into()),
        }
    }

    /// Simple wrapper around requests to add default parameters to the request
    fn upgrade_request(&self, mut request: ureq::Request) -> ureq::Request {
        request
            .set("Authorization", &format!("token {}", self.token))
            .set(ACCEPT_HEADER, ACCEPT_VALUE)
            .timeout_connect(TIMEOUT);
        match env::var("https_proxy") {
            Ok(proxy) => request
                .set_proxy(
                    ureq::Proxy::new(proxy)
                        .expect("Unable to parse https_proxy environment variable"),
                )
                .build(),
            Err(_e) => request,
        }
    }

    /// Get can read files or directories, this makes it easier to use
    fn get_internal(&self, path: &str) -> Result<Vec<GetResponse>, Error> {
        let resp = self.upgrade_request(ureq::get(&self.url(path))).call();
        match resp.status() {
            200 => {
                let resp = resp.into_string()?;
                let get_resp: Result<GetResponse, Error> =
                    serde_json::from_str(&resp).map_err(|e| e.into());

                if let Ok(get_resp) = get_resp {
                    return Ok(vec![get_resp]);
                }

                let get_resp: Result<Vec<GetResponse>, Error> =
                    serde_json::from_str(&resp).map_err(|e| e.into());
                if let Ok(get_resp) = get_resp {
                    return Ok(get_resp);
                }

                Err(Error::SerializationError(resp))
            }
            404 => Err(Error::NotFound(path.into())),
            _ => Err(resp.into()),
        }
    }

    /// Returns the sha hash of a file according to GitHub
    fn get_sha(&self, path: &str) -> Result<String, Error> {
        let value = self.get_internal(path)?;
        if let Some(value) = value.iter().find(|entry| entry.path == path) {
            Ok(value.sha.clone())
        } else {
            // This is a directory
            Err(Error::InternalError(format!(
                "get mismatch, found {} entries",
                value.len()
            )))
        }
    }

    fn url(&self, path: &str) -> String {
        format!(
            "{}/repos/{}/{}/contents/{}",
            URL, self.owner, self.repository, path
        )
    }
}

#[derive(Debug, Deserialize, PartialEq, Serialize)]
struct Branch {
    name: String,
}

#[derive(Debug, Deserialize, PartialEq, Serialize)]
struct GetResponse {
    #[serde(rename = "type")]
    content_type: String,
    path: String,
    sha: String,
    content: Option<String>,
}

// This test suite depends on the developer providing an active repository, the account where that
// repository is hosted, and a token that can modify that repository. Due to the nature of these
// tests, they cannot be provided in this framework and the tests are labeled as ignored
//
// GitHub updates can misbehave due to changes in file structure thus dependent operations can
// fail, for example, an update of a file reads its hash and then updates. If another operation
// happens after the hash retrieval but before the update, the hash will change resulting in a
// failed update. Therefore these tests must be run in series via:
// `cargo xtest -- --ignored --test-threads=1`
#[cfg(test)]
mod tests {
    use super::*;

    const OWNER: &str = "OWNER";
    const REPOSITORY: &str = "REPOSITORY";
    const TOKEN: &str = "TOKEN";

    #[ignore]
    #[test]
    fn test_files() {
        let path = "data.txt";
        let value1 = "hello";
        let value1_encoded = base64::encode(&value1);
        let value2 = "world";
        let value2_encoded = base64::encode(&value2);

        let github = Client::new(OWNER.into(), REPOSITORY.into(), TOKEN.into());

        // Try a create
        github.get_file(path).unwrap_err();
        github.put(path, &value1_encoded).unwrap();
        let b64_value = base64::decode(github.get_file(path).unwrap()).unwrap();
        let value = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, value1);

        // Try an update
        github.put(path, &value2_encoded).unwrap();
        let b64_value = base64::decode(github.get_file(path).unwrap()).unwrap();
        let value = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, value2);

        // Verify delete_file
        github.delete_file(path).unwrap();
        github.get_file(path).unwrap_err();
    }

    #[ignore]
    #[test]
    fn test_directories() {
        let path1_root = "dir";
        let path1 = "dir/data1.txt";
        let value1 = "hello";
        let value1_encoded = base64::encode(&value1);

        let path2_root = "dir1";
        let path2 = "dir1/data1.txt";
        let value2 = "world";
        let value2_encoded = base64::encode(&value2);

        let github = Client::new(OWNER.into(), REPOSITORY.into(), TOKEN.into());

        // Initialize two directories with a file each
        github.put(path1, &value1_encoded).unwrap();
        github.put(path2, &value2_encoded).unwrap();

        // Verify the contents
        let b64_value = base64::decode(github.get_file(path1).unwrap()).unwrap();
        let value = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, value1);

        let b64_value = base64::decode(github.get_file(path2).unwrap()).unwrap();
        let value = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, value2);

        // Delete one of the directories
        github.delete_file(path1).unwrap();

        // Verify one is good and the other is gone
        github.get_directory(path1_root).unwrap_err();

        let b64_value = base64::decode(github.get_file(path2).unwrap()).unwrap();
        let value = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, value2);

        // Cleanup the rest
        github.delete_file(path2).unwrap();
        github.get_directory(path2_root).unwrap_err();
    }

    #[ignore]
    #[test]
    fn test_recursive_delete_file() {
        let root = "root_dir";
        let file0 = "root_dir/another_dir/another_dir/ok.txt";
        let file1 = "root_dir/another_dir/ok.txt";
        let value = "hello";
        let value_encoded = base64::encode(&value);

        let github = Client::new(OWNER.into(), REPOSITORY.into(), TOKEN.into());

        github.put(file0, &value_encoded).unwrap();
        github.put(file1, &value_encoded).unwrap();

        let b64_value = base64::decode(github.get_file(file0).unwrap()).unwrap();
        let rv = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, rv);

        let b64_value = base64::decode(github.get_file(file1).unwrap()).unwrap();
        let rv = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, rv);

        github.delete_directory(root).unwrap();
        github.get_file(file0).unwrap_err();
        github.get_file(file1).unwrap_err();
    }

    #[ignore]
    #[test]
    fn test_branches() {
        let github = Client::new(OWNER.into(), REPOSITORY.into(), TOKEN.into());
        let branches = github.get_branches().unwrap();
        assert!(!branches.is_empty());
        assert!(branches.iter().any(|b| b == "master"));
    }
}
<|endoftext|>


<commit_before>use crate::{
    analysis::{
        functions::{Info as FuncInfo, Visibility},
        imports::Imports,
    },
    config::GObject,
    library::{Type as LibType, TypeId},
    version::Version,
};
use std::{collections::BTreeMap, str::FromStr};

#[derive(Clone, Copy, Eq, Debug, Ord, PartialEq, PartialOrd)]
pub enum Type {
    Compare,
    Copy,
    Equal,
    Free,
    Ref,
    Display,
    Unref,
    Hash,
}

impl FromStr for Type {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        use self::Type::*;
        match s {
            "compare" => Ok(Compare),
            "copy" => Ok(Copy),
            "equal" => Ok(Equal),
            "free" | "destroy" => Ok(Free),
            "is_equal" => Ok(Equal),
            "ref" | "ref_" => Ok(Ref),
            "unref" => Ok(Unref),
            "hash" => Ok(Hash),
            _ => Err(format!("Unknown type '{}'", s)),
        }
    }
}

#[derive(Debug, Clone)]
pub struct TraitInfo {
    pub glib_name: String,
    pub version: Option<Version>,
    pub first_parameter_mut: bool,
}

type TraitInfos = BTreeMap<Type, TraitInfo>;

#[derive(Clone, Copy, Eq, Debug, Ord, PartialEq, PartialOrd)]
pub enum FunctionType {
    StaticStringify,
}

#[derive(Debug, Clone)]
pub struct FunctionInfo {
    pub type_: FunctionType,
    pub version: Option<Version>,
}

type FunctionInfos = BTreeMap<String, FunctionInfo>;

#[derive(Debug, Default)]
pub struct Infos {
    traits: TraitInfos,
    functions: FunctionInfos,
}

impl Infos {
    pub fn traits(&self) -> &TraitInfos {
        &self.traits
    }

    pub fn traits_mut(&mut self) -> &mut TraitInfos {
        &mut self.traits
    }

    pub fn has_trait(&self, type_: Type) -> bool {
        self.traits.contains_key(&type_)
    }

    pub fn functions(&self) -> &FunctionInfos {
        &self.functions
    }
}

/// Returns true on functions that take an instance as single argument and
/// return a string as result.
fn is_stringify(func: &mut FuncInfo, parent_type: &LibType, obj: &GObject) -> bool {
    if func.parameters.c_parameters.len() != 1 {
        return false;
    }
    if !func.parameters.c_parameters[0].instance_parameter {
        return false;
    }

    if let Some(ret) = func.ret.parameter.as_mut() {
        if ret.lib_par.typ != TypeId::tid_utf8() {
            return false;
        }

        if func.name == "to_string" {
            // Rename to to_str to make sure it doesn't clash with ToString::to_string
            func.name = "to_str".to_owned();

            // As to not change old code behaviour, assume non-nullability outside
            // enums and flags only, and exclusively for to_string. Function inside
            // enums and flags have been appropriately marked in Gir.
            if !obj.trust_return_value_nullability
                && !matches!(parent_type, LibType::Enumeration(_) | LibType::Bitfield(_))
            {
                *ret.lib_par.nullable = false;
            }
        }

        // Cannot generate Display implementation for Option<>
        !*ret.lib_par.nullable
    } else {
        false
    }
}

fn update_func(func: &mut FuncInfo, type_: Type) -> bool {
    if func.visibility != Visibility::Comment {
        func.visibility = visibility(type_);
    }
    true
}

pub fn extract(functions: &mut Vec<FuncInfo>, parent_type: &LibType, obj: &GObject) -> Infos {
    let mut specials = Infos::default();
    let mut has_copy = false;
    let mut has_free = false;
    let mut destroy = None;

    for (pos, func) in functions.iter_mut().enumerate() {
        if is_stringify(func, parent_type, obj) {
            let return_transfer_none = func.ret.parameter.as_ref().map_or(false, |ret| {
                ret.lib_par.transfer == crate::library::Transfer::None
            });

            // Assume only enumerations and bitfields can return static strings
            let returns_static_ref = return_transfer_none
                && matches!(parent_type, LibType::Enumeration(_) | LibType::Bitfield(_))
                // We cannot mandate returned lifetime if this is not generated.
                // (And this prevents an unused std::ffi::CStr from being emitted below)
                && func.status.need_generate();

            if returns_static_ref {
                // Override the function with a &'static (non allocating) -returning string
                // if the transfer type is none and it matches the above heuristics.
                specials.functions.insert(
                    func.glib_name.clone(),
                    FunctionInfo {
                        type_: FunctionType::StaticStringify,
                        version: func.version,
                    },
                );
            }

            // Some stringifying functions can serve as Display implementation
            if matches!(
                func.name.as_str(),
                "to_string" | "to_str" | "name" | "get_name"
            ) {
                // FUTURE: Decide which function gets precedence if multiple Display prospects exist.
                specials.traits.insert(
                    Type::Display,
                    TraitInfo {
                        glib_name: func.glib_name.clone(),
                        version: func.version,
                        first_parameter_mut: false,
                    },
                );
            }
        } else if let Ok(type_) = func.name.parse() {
            if func.name == "destroy" {
                destroy = Some((func.glib_name.clone(), pos));
                continue;
            }
            if !update_func(func, type_) {
                continue;
            }
            if func.name == "copy" {
                has_copy = true;
            } else if func.name == "free" {
                has_free = true;
            }

            let first_parameter_mut = func
                .parameters
                .c_parameters
                .first()
                .map_or(false, |p| p.ref_mode == super::ref_mode::RefMode::ByRefMut);

            specials.traits.insert(
                type_,
                TraitInfo {
                    glib_name: func.glib_name.clone(),
                    version: func.version,
                    first_parameter_mut,
                },
            );
        }
    }

    if has_copy && !has_free {
        if let Some((glib_name, pos)) = destroy {
            let ty_ = Type::from_str("destroy").unwrap();
            let func = &mut functions[pos];
            update_func(func, ty_);
            specials.traits.insert(
                ty_,
                TraitInfo {
                    glib_name,
                    version: func.version,
                    first_parameter_mut: true,
                },
            );
        }
    }

    specials
}

fn visibility(t: Type) -> Visibility {
    use self::Type::*;
    match t {
        Copy | Free | Ref | Unref => Visibility::Hidden,
        Hash | Compare | Equal => Visibility::Private,
        Display => Visibility::Public,
    }
}

// Some special functions (e.g. `copy` on refcounted types) should be exposed
pub fn unhide(functions: &mut Vec<FuncInfo>, specials: &Infos, type_: Type) {
    if let Some(func) = specials.traits().get(&type_) {
        let func = functions
            .iter_mut()
            .find(|f| f.glib_name == func.glib_name && f.visibility != Visibility::Comment);
        if let Some(func) = func {
            func.visibility = Visibility::Public;
        }
    }
}

pub fn analyze_imports(specials: &Infos, imports: &mut Imports) {
    for (type_, info) in specials.traits() {
        use self::Type::*;
        match type_ {
            Copy if info.first_parameter_mut => {
                imports.add_with_version("glib::translate::*", info.version)
            }
            Compare => imports.add_with_version("std::cmp", info.version),
            Display => imports.add_with_version("std::fmt", info.version),
            Hash => imports.add_with_version("std::hash", info.version),
            _ => {}
        }
    }
    for info in specials.functions().values() {
        match info.type_ {
            FunctionType::StaticStringify => {
                imports.add_with_version("std::ffi::CStr", info.version)
            }
        }
    }
}
<commit_msg>analysis: mark to_str functions as renamed<commit_after>use crate::{
    analysis::{
        functions::{Info as FuncInfo, Visibility},
        imports::Imports,
    },
    config::GObject,
    library::{Type as LibType, TypeId},
    version::Version,
};
use std::{collections::BTreeMap, str::FromStr};

#[derive(Clone, Copy, Eq, Debug, Ord, PartialEq, PartialOrd)]
pub enum Type {
    Compare,
    Copy,
    Equal,
    Free,
    Ref,
    Display,
    Unref,
    Hash,
}

impl FromStr for Type {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        use self::Type::*;
        match s {
            "compare" => Ok(Compare),
            "copy" => Ok(Copy),
            "equal" => Ok(Equal),
            "free" | "destroy" => Ok(Free),
            "is_equal" => Ok(Equal),
            "ref" | "ref_" => Ok(Ref),
            "unref" => Ok(Unref),
            "hash" => Ok(Hash),
            _ => Err(format!("Unknown type '{}'", s)),
        }
    }
}

#[derive(Debug, Clone)]
pub struct TraitInfo {
    pub glib_name: String,
    pub version: Option<Version>,
    pub first_parameter_mut: bool,
}

type TraitInfos = BTreeMap<Type, TraitInfo>;

#[derive(Clone, Copy, Eq, Debug, Ord, PartialEq, PartialOrd)]
pub enum FunctionType {
    StaticStringify,
}

#[derive(Debug, Clone)]
pub struct FunctionInfo {
    pub type_: FunctionType,
    pub version: Option<Version>,
}

type FunctionInfos = BTreeMap<String, FunctionInfo>;

#[derive(Debug, Default)]
pub struct Infos {
    traits: TraitInfos,
    functions: FunctionInfos,
}

impl Infos {
    pub fn traits(&self) -> &TraitInfos {
        &self.traits
    }

    pub fn traits_mut(&mut self) -> &mut TraitInfos {
        &mut self.traits
    }

    pub fn has_trait(&self, type_: Type) -> bool {
        self.traits.contains_key(&type_)
    }

    pub fn functions(&self) -> &FunctionInfos {
        &self.functions
    }
}

/// Returns true on functions that take an instance as single argument and
/// return a string as result.
fn is_stringify(func: &mut FuncInfo, parent_type: &LibType, obj: &GObject) -> bool {
    if func.parameters.c_parameters.len() != 1 {
        return false;
    }
    if !func.parameters.c_parameters[0].instance_parameter {
        return false;
    }

    if let Some(ret) = func.ret.parameter.as_mut() {
        if ret.lib_par.typ != TypeId::tid_utf8() {
            return false;
        }

        if func.name == "to_string" {
            // Rename to to_str to make sure it doesn't clash with ToString::to_string
            assert!(func.new_name.is_none(), "A `to_string` function can't be renamed manually. It's automatically renamed to `to_str`");
            func.new_name = Some("to_str".to_owned());

            // As to not change old code behaviour, assume non-nullability outside
            // enums and flags only, and exclusively for to_string. Function inside
            // enums and flags have been appropriately marked in Gir.
            if !obj.trust_return_value_nullability
                && !matches!(parent_type, LibType::Enumeration(_) | LibType::Bitfield(_))
            {
                *ret.lib_par.nullable = false;
            }
        }

        // Cannot generate Display implementation for Option<>
        !*ret.lib_par.nullable
    } else {
        false
    }
}

fn update_func(func: &mut FuncInfo, type_: Type) -> bool {
    if func.visibility != Visibility::Comment {
        func.visibility = visibility(type_);
    }
    true
}

pub fn extract(functions: &mut Vec<FuncInfo>, parent_type: &LibType, obj: &GObject) -> Infos {
    let mut specials = Infos::default();
    let mut has_copy = false;
    let mut has_free = false;
    let mut destroy = None;

    for (pos, func) in functions.iter_mut().enumerate() {
        if is_stringify(func, parent_type, obj) {
            let return_transfer_none = func.ret.parameter.as_ref().map_or(false, |ret| {
                ret.lib_par.transfer == crate::library::Transfer::None
            });

            // Assume only enumerations and bitfields can return static strings
            let returns_static_ref = return_transfer_none
                && matches!(parent_type, LibType::Enumeration(_) | LibType::Bitfield(_))
                // We cannot mandate returned lifetime if this is not generated.
                // (And this prevents an unused std::ffi::CStr from being emitted below)
                && func.status.need_generate();

            if returns_static_ref {
                // Override the function with a &'static (non allocating) -returning string
                // if the transfer type is none and it matches the above heuristics.
                specials.functions.insert(
                    func.glib_name.clone(),
                    FunctionInfo {
                        type_: FunctionType::StaticStringify,
                        version: func.version,
                    },
                );
            }

            // Some stringifying functions can serve as Display implementation
            if matches!(
                func.name.as_str(),
                "to_string" | "to_str" | "name" | "get_name"
            ) {
                // FUTURE: Decide which function gets precedence if multiple Display prospects exist.
                specials.traits.insert(
                    Type::Display,
                    TraitInfo {
                        glib_name: func.glib_name.clone(),
                        version: func.version,
                        first_parameter_mut: false,
                    },
                );
            }
        } else if let Ok(type_) = func.name.parse() {
            if func.name == "destroy" {
                destroy = Some((func.glib_name.clone(), pos));
                continue;
            }
            if !update_func(func, type_) {
                continue;
            }
            if func.name == "copy" {
                has_copy = true;
            } else if func.name == "free" {
                has_free = true;
            }

            let first_parameter_mut = func
                .parameters
                .c_parameters
                .first()
                .map_or(false, |p| p.ref_mode == super::ref_mode::RefMode::ByRefMut);

            specials.traits.insert(
                type_,
                TraitInfo {
                    glib_name: func.glib_name.clone(),
                    version: func.version,
                    first_parameter_mut,
                },
            );
        }
    }

    if has_copy && !has_free {
        if let Some((glib_name, pos)) = destroy {
            let ty_ = Type::from_str("destroy").unwrap();
            let func = &mut functions[pos];
            update_func(func, ty_);
            specials.traits.insert(
                ty_,
                TraitInfo {
                    glib_name,
                    version: func.version,
                    first_parameter_mut: true,
                },
            );
        }
    }

    specials
}

fn visibility(t: Type) -> Visibility {
    use self::Type::*;
    match t {
        Copy | Free | Ref | Unref => Visibility::Hidden,
        Hash | Compare | Equal => Visibility::Private,
        Display => Visibility::Public,
    }
}

// Some special functions (e.g. `copy` on refcounted types) should be exposed
pub fn unhide(functions: &mut Vec<FuncInfo>, specials: &Infos, type_: Type) {
    if let Some(func) = specials.traits().get(&type_) {
        let func = functions
            .iter_mut()
            .find(|f| f.glib_name == func.glib_name && f.visibility != Visibility::Comment);
        if let Some(func) = func {
            func.visibility = Visibility::Public;
        }
    }
}

pub fn analyze_imports(specials: &Infos, imports: &mut Imports) {
    for (type_, info) in specials.traits() {
        use self::Type::*;
        match type_ {
            Copy if info.first_parameter_mut => {
                imports.add_with_version("glib::translate::*", info.version)
            }
            Compare => imports.add_with_version("std::cmp", info.version),
            Display => imports.add_with_version("std::fmt", info.version),
            Hash => imports.add_with_version("std::hash", info.version),
            _ => {}
        }
    }
    for info in specials.functions().values() {
        match info.type_ {
            FunctionType::StaticStringify => {
                imports.add_with_version("std::ffi::CStr", info.version)
            }
        }
    }
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! 64-bit Interrupt Descriptor Table implementation.
//!
//! Refer to section 6.10 of the _Intel® 64 and IA-32 Architectures
//! Software Developer’s Manual_ for more information.
use core::mem;
use core::ptr;
use spin::Mutex;
use super::{Registers, DTable, DTablePtr, segment};

#[path = "../../x86_all/interrupts.rs"] mod interrupts_all;
#[path = "../../x86_all/pics.rs"] pub mod pics;
pub use self::interrupts_all::*;

//==------------------------------------------------------------------------==
// Interface into ASM interrupt handling
extern {
    /// Offset of the 64-bit GDT main code segment.
    /// Exported by `boot.asm`
    static gdt64_offset: u16;

    /// Array of interrupt handlers from ASM
    static interrupt_handlers: [*const u8; IDT_ENTRIES];
}

/// State stored when handling an interrupt.
#[allow(dead_code)]
#[repr(C, packed)]
pub struct InterruptCtx64 { /// callee-saved registers
                            pub registers: Registers
                          , /// interrupt ID number
                            pub int_id:  u32
                          , __pad_1: u32
                          , /// error number
                            pub err_no:  u32
                          , __pad_2: u32
                          }

impl InterruptContext for InterruptCtx64 {
    type Registers = Registers;
    // All these inline functions are basically just faking
    // object orientation in a way the Rust compiler understands
    #[inline] fn registers(&self) -> Self::Registers { self.registers }
    #[inline] fn err_no(&self) -> u32 { self.err_no }
    #[inline] fn int_id(&self) -> u32 { self.int_id }
}

//==------------------------------------------------------------------------==
// 64-bit implementation of the IDT gate trait

/// An IDT entry is called a gate.
///
/// Based on code from the OS Dev Wiki
/// http://wiki.osdev.org/Interrupt_Descriptor_Table#Structure
///
/// Refer also to "6.14.1 64-Bit Mode IDT"  and "Table 3-2. System-Segment and
/// Gate-Descriptor Types" in the _Intel® 64 and IA-32 Architectures
/// Software Developer’s Manual_
#[repr(C, packed)]
#[derive(Copy,Clone)]
pub struct Gate64 { /// bits 0 - 15 of the offset
                    pub offset_lower: u16
                  , /// code segment selector (GDT or LDT)
                    pub selector: segment::Selector
                  , /// always zero
                    zero: u8
                  , /// indicates the gate's type and attributes.
                    /// the second half indicates the type:
                    ///   + `0b1100`: Call gate
                    ///   + `0b1110`: Interrupt gate
                    ///   + `0b1111`: Trap Gate
                    pub type_attr: GateType
                  , /// bits 16 - 31 of the offset
                    pub offset_mid: u16
                  , /// bits 32 - 63 of the offset
                    pub offset_upper: u32
                  , /// always zero (according to the spec, this is "reserved")
                    reserved: u32
                  }

impl Gate64 {
    /// Creates a new IDT gate marked as `absent`.
    ///
    /// This is basically just for filling the new IDT table
    /// with valid (but useless) gates upon init.
    ///
    /// This would be in the `Gate` trait, but this has to be a `const fn` so
    /// that it can be usedm in static initializers, and trait functions cannot
    /// be `const`.
    ///
    /// Actually triggering an absent interrupt will send a General Protection
    /// fault (13).
    const fn absent() -> Self {
        Gate64 { offset_lower: 0
               , selector: segment::Selector::from_raw(0)
               , zero: 0
               , type_attr: GateType::Absent
               , offset_mid: 0
               , offset_upper: 0
               , reserved: 0
               }
    }
}

impl Gate for Gate64 {

    /// Creates a new IDT gate pointing at the given handler function.
    ///
    /// The `handler` function must have been created with valid interrupt
    /// calling conventions.
    fn from_handler(handler: Handler) -> Self {
        unsafe { // trust me on this.
                 // `mem::transmute()` is glorious black magic
            let (low, mid, high): (u16, u16, u32)
                = mem::transmute(handler);

            Gate64 { offset_lower: low
                   , selector: segment::Selector::from_raw(gdt64_offset)
                   , zero: 0
                   // Bit 7 is the present bit
                   // Bits 4-0 indicate this is an interrupt gate
                   , type_attr: GateType::Interrupt
                   , offset_mid: mid
                   , offset_upper: high
                   , reserved: 0
                   }
        }
    }

    unsafe fn from_raw(handler: *const u8) -> Self {
        let (low, mid, high): (u16, u16, u32)
            = mem::transmute(handler as u64);

        Gate64 { offset_lower: low
               , selector: segment::Selector::from_raw(gdt64_offset)
               , zero: 0
               , type_attr: GateType::Interrupt
               , offset_mid: mid
               , offset_upper: high
               , reserved: 0
               }
    }
}

//==------------------------------------------------------------------------==
// 64-bit implementation of the IDT trait
struct Idt64([Gate64; IDT_ENTRIES]);

impl Idt for Idt64 {
    // type Ptr = IdtPtr<Self>;
    type Ctx = InterruptCtx64;
    type GateSize = Gate64;
    type PtrSize = u64;

    fn get_ptr(&self) -> DTablePtr<u64>{
        DTablePtr { limit: (mem::size_of::<Self::GateSize>() * IDT_ENTRIES)
                            as u16
                  , base: &self.0[0] as *const Gate64 as u64
                  }
    }

    /// Get the IDT pointer struct to pass to `lidt`
    // fn get_ptr(&self) -> DTablePtr<Self> {
    //     IdtPtr { limit: (mem::size_of::<Gate64>() * IDT_ENTRIES) as u16
    //            , base:  self as *const Idt64
    //            }
    // }

    /// Add an entry for the given handler at the given index
    fn add_gate(&mut self, index: usize, handler: Handler) {
        self.0[index] = Gate64::from_handler(handler)
    }

    /// Assembly interrupt handlers call into this
    #[no_mangle]
    unsafe extern "C" fn handle_interrupt(state: &Self::Ctx) {
        let id = state.int_id();
        match id {
            // interrupts 0 - 16 are CPU exceptions
            0x00...0x0f => Self::handle_cpu_exception(state)
            // System timer
          , 0x20 => { /* TODO: make this work */ }
            // Keyboard
          , 0x21 => { /* TODO: make this work */ }
            // Loonix syscall vector
          , 0x80 => { // TODO: currently, we do nothing here, do we want
                      // our syscalls on this vector as well?
            }
          , _ => panic!("Unknown interrupt: #{} Sorry!", id)
        }
        // send the PICs the end interrupt signal
        pics::end_pic_interrupt(id as u8);
    }
}

impl DTable for Idt64 {
    #[inline] unsafe fn load(&self) {
        print!("Loading IDT...");
        asm!(  "lidt ($0)"
            :: "r"(&self.get_ptr())
            :  "memory" );
        println!("    [DONE]");
    }
}

impl Idt64 {
    fn add_handlers(&mut self) -> &mut Self {
        print!("Adding interrupt handlers to IDT...");
        for (i, &h_ptr) in interrupt_handlers.iter()
            .enumerate()
            .filter(|&(_, &h_ptr)| h_ptr != ptr::null() ) {
                unsafe { self.0[i] = Gate64::from_raw(h_ptr) }
        }
        println!("    [DONE]");
        self
    }
}

// impl IdtPtrOps for IdtPtr<Idt64> {
//     /// Load the IDT at the given location.
//     /// This just calls `lidt`.
//     unsafe fn load(&self) {
//         asm!(  "lidt ($0)"
//             :: "{rax}"(self)
//             :: "volatile" );
//     }
// }

/// Global Interrupt Descriptor Table instance
/// Our global IDT.
static IDT: Mutex<Idt64>
    = Mutex::new(Idt64([Gate64::absent(); IDT_ENTRIES]));

pub unsafe fn initialize() {
    pics::initialize();
    // TODO: load interrupts into IDT
    IDT.lock()
       .add_handlers()
       .load();                 // Load the IDT pointer
    print!("Testing interrupt handling...");
    asm!("int $0" :: "N" (0x80));
    println!("   [DONE]");

    Idt64::enable_interrupts(); // enable interrupts
}
<commit_msg>[x86_64] Removed test interrupt since it prints output weirdly<commit_after>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! 64-bit Interrupt Descriptor Table implementation.
//!
//! Refer to section 6.10 of the _Intel® 64 and IA-32 Architectures
//! Software Developer’s Manual_ for more information.
use core::mem;
use core::ptr;
use spin::Mutex;
use super::{Registers, DTable, DTablePtr, segment};

#[path = "../../x86_all/interrupts.rs"] mod interrupts_all;
#[path = "../../x86_all/pics.rs"] pub mod pics;
pub use self::interrupts_all::*;

//==------------------------------------------------------------------------==
// Interface into ASM interrupt handling
extern {
    /// Offset of the 64-bit GDT main code segment.
    /// Exported by `boot.asm`
    static gdt64_offset: u16;

    /// Array of interrupt handlers from ASM
    static interrupt_handlers: [*const u8; IDT_ENTRIES];
}

/// State stored when handling an interrupt.
#[allow(dead_code)]
#[repr(C, packed)]
pub struct InterruptCtx64 { /// callee-saved registers
                            pub registers: Registers
                          , /// interrupt ID number
                            pub int_id:  u32
                          , __pad_1: u32
                          , /// error number
                            pub err_no:  u32
                          , __pad_2: u32
                          }

impl InterruptContext for InterruptCtx64 {
    type Registers = Registers;
    // All these inline functions are basically just faking
    // object orientation in a way the Rust compiler understands
    #[inline] fn registers(&self) -> Self::Registers { self.registers }
    #[inline] fn err_no(&self) -> u32 { self.err_no }
    #[inline] fn int_id(&self) -> u32 { self.int_id }
}

//==------------------------------------------------------------------------==
// 64-bit implementation of the IDT gate trait

/// An IDT entry is called a gate.
///
/// Based on code from the OS Dev Wiki
/// http://wiki.osdev.org/Interrupt_Descriptor_Table#Structure
///
/// Refer also to "6.14.1 64-Bit Mode IDT"  and "Table 3-2. System-Segment and
/// Gate-Descriptor Types" in the _Intel® 64 and IA-32 Architectures
/// Software Developer’s Manual_
#[repr(C, packed)]
#[derive(Copy,Clone)]
pub struct Gate64 { /// bits 0 - 15 of the offset
                    pub offset_lower: u16
                  , /// code segment selector (GDT or LDT)
                    pub selector: segment::Selector
                  , /// always zero
                    zero: u8
                  , /// indicates the gate's type and attributes.
                    /// the second half indicates the type:
                    ///   + `0b1100`: Call gate
                    ///   + `0b1110`: Interrupt gate
                    ///   + `0b1111`: Trap Gate
                    pub type_attr: GateType
                  , /// bits 16 - 31 of the offset
                    pub offset_mid: u16
                  , /// bits 32 - 63 of the offset
                    pub offset_upper: u32
                  , /// always zero (according to the spec, this is "reserved")
                    reserved: u32
                  }

impl Gate64 {
    /// Creates a new IDT gate marked as `absent`.
    ///
    /// This is basically just for filling the new IDT table
    /// with valid (but useless) gates upon init.
    ///
    /// This would be in the `Gate` trait, but this has to be a `const fn` so
    /// that it can be usedm in static initializers, and trait functions cannot
    /// be `const`.
    ///
    /// Actually triggering an absent interrupt will send a General Protection
    /// fault (13).
    const fn absent() -> Self {
        Gate64 { offset_lower: 0
               , selector: segment::Selector::from_raw(0)
               , zero: 0
               , type_attr: GateType::Absent
               , offset_mid: 0
               , offset_upper: 0
               , reserved: 0
               }
    }
}

impl Gate for Gate64 {

    /// Creates a new IDT gate pointing at the given handler function.
    ///
    /// The `handler` function must have been created with valid interrupt
    /// calling conventions.
    fn from_handler(handler: Handler) -> Self {
        unsafe { // trust me on this.
                 // `mem::transmute()` is glorious black magic
            let (low, mid, high): (u16, u16, u32)
                = mem::transmute(handler);

            Gate64 { offset_lower: low
                   , selector: segment::Selector::from_raw(gdt64_offset)
                   , zero: 0
                   // Bit 7 is the present bit
                   // Bits 4-0 indicate this is an interrupt gate
                   , type_attr: GateType::Interrupt
                   , offset_mid: mid
                   , offset_upper: high
                   , reserved: 0
                   }
        }
    }

    unsafe fn from_raw(handler: *const u8) -> Self {
        let (low, mid, high): (u16, u16, u32)
            = mem::transmute(handler as u64);

        Gate64 { offset_lower: low
               , selector: segment::Selector::from_raw(gdt64_offset)
               , zero: 0
               , type_attr: GateType::Interrupt
               , offset_mid: mid
               , offset_upper: high
               , reserved: 0
               }
    }
}

//==------------------------------------------------------------------------==
// 64-bit implementation of the IDT trait
struct Idt64([Gate64; IDT_ENTRIES]);

impl Idt for Idt64 {
    // type Ptr = IdtPtr<Self>;
    type Ctx = InterruptCtx64;
    type GateSize = Gate64;
    type PtrSize = u64;

    fn get_ptr(&self) -> DTablePtr<u64>{
        DTablePtr { limit: (mem::size_of::<Self::GateSize>() * IDT_ENTRIES)
                            as u16
                  , base: &self.0[0] as *const Gate64 as u64
                  }
    }

    /// Get the IDT pointer struct to pass to `lidt`
    // fn get_ptr(&self) -> DTablePtr<Self> {
    //     IdtPtr { limit: (mem::size_of::<Gate64>() * IDT_ENTRIES) as u16
    //            , base:  self as *const Idt64
    //            }
    // }

    /// Add an entry for the given handler at the given index
    fn add_gate(&mut self, index: usize, handler: Handler) {
        self.0[index] = Gate64::from_handler(handler)
    }

    /// Assembly interrupt handlers call into this
    #[no_mangle]
    unsafe extern "C" fn handle_interrupt(state: &Self::Ctx) {
        let id = state.int_id();
        match id {
            // interrupts 0 - 16 are CPU exceptions
            0x00...0x0f => Self::handle_cpu_exception(state)
            // System timer
          , 0x20 => { /* TODO: make this work */ }
            // Keyboard
          , 0x21 => { /* TODO: make this work */ }
            // Loonix syscall vector
          , 0x80 => { // TODO: currently, we do nothing here, do we want
                      // our syscalls on this vector as well?
            }
          , _ => panic!("Unknown interrupt: #{} Sorry!", id)
        }
        // send the PICs the end interrupt signal
        pics::end_pic_interrupt(id as u8);
    }
}

impl DTable for Idt64 {
    #[inline] unsafe fn load(&self) {
        print!("Loading IDT...");
        asm!(  "lidt ($0)"
            :: "r"(&self.get_ptr())
            :  "memory" );
        println!("    [DONE]");
    }
}

impl Idt64 {
    fn add_handlers(&mut self) -> &mut Self {
        print!("Adding interrupt handlers to IDT...");
        for (i, &h_ptr) in interrupt_handlers.iter()
            .enumerate()
            .filter(|&(_, &h_ptr)| h_ptr != ptr::null() ) {
                unsafe { self.0[i] = Gate64::from_raw(h_ptr) }
        }
        println!("    [DONE]");
        self
    }
}

// impl IdtPtrOps for IdtPtr<Idt64> {
//     /// Load the IDT at the given location.
//     /// This just calls `lidt`.
//     unsafe fn load(&self) {
//         asm!(  "lidt ($0)"
//             :: "{rax}"(self)
//             :: "volatile" );
//     }
// }

/// Global Interrupt Descriptor Table instance
/// Our global IDT.
static IDT: Mutex<Idt64>
    = Mutex::new(Idt64([Gate64::absent(); IDT_ENTRIES]));

pub unsafe fn initialize() {
    pics::initialize();
    // TODO: load interrupts into IDT
    IDT.lock()
       .add_handlers()
       .load();                 // Load the IDT pointer
    // print!("Testing interrupt handling...");
    // asm!("int $0" :: "N" (0x80));
    // println!("   [DONE]");

    Idt64::enable_interrupts(); // enable interrupts
}
<|endoftext|>