<commit_before>//! Generic serialization framework.

#[cfg(feature = "std")]
use std::error;
#[cfg(not(feature = "std"))]
use error;

#[cfg(all(feature = "collections", not(feature = "std")))]
use collections::String;

pub mod impls;

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

/// `Error` is a trait that allows a `Serialize` to generically create a
/// `Serializer` error.
pub trait Error: Sized + error::Error {
    /// Raised when there is general error when deserializing a type.
    #[cfg(any(feature = "std", feature = "collections"))]
    fn custom<T: Into<String>>(msg: T) -> Self;

    /// Raised when there is general error when deserializing a type.
    #[cfg(all(not(feature = "std"), not(feature = "collections")))]
    fn custom<T: Into<&'static str>>(msg: T) -> Self;

    /// Raised when a `Serialize` was passed an incorrect value.
    fn invalid_value(msg: &str) -> Self {
        Error::custom(format!("invalid value: {}", msg))
    }
}

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

/// A trait that describes a type that can be serialized by a `Serializer`.
pub trait Serialize {
    /// Serializes this value into this serializer.
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: Serializer;
}

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

/// A trait that describes a type that can serialize a stream of values into the underlying format.
pub trait Serializer {
    /// The error type that can be returned if some error occurs during serialization.
    type Error: Error;

    /// A state object that is initialized by `serialize_seq`, passed to
    /// `serialize_seq_elt`, and consumed by `serialize_seq_end`. Use `()` if no
    /// state is required.
    type SeqState;
    /// A state object that is initialized by `serialize_tuple`, passed to
    /// `serialize_tuple_elt`, and consumed by `serialize_tuple_end`. Use `()`
    /// if no state is required.
    type TupleState;
    /// A state object that is initialized by `serialize_tuple_struct`, passed
    /// to `serialize_tuple_struct_elt`, and consumed by
    /// `serialize_tuple_struct_end`. Use `()` if no state is required.
    type TupleStructState;
    /// A state object that is initialized by `serialize_tuple_variant`, passed
    /// to `serialize_tuple_variant_elt`, and consumed by
    /// `serialize_tuple_variant_end`. Use `()` if no state is required.
    type TupleVariantState;
    /// A state object that is initialized by `serialize_map`, passed to
    /// `serialize_map_elt`, and consumed by `serialize_map_end`. Use `()` if no
    /// state is required.
    type MapState;
    /// A state object that is initialized by `serialize_struct`, passed to
    /// `serialize_struct_elt`, and consumed by `serialize_struct_end`. Use `()`
    /// if no state is required.
    type StructState;
    /// A state object that is initialized by `serialize_struct_variant`, passed
    /// to `serialize_struct_variant_elt`, and consumed by
    /// `serialize_struct_variant_end`. Use `()` if no state is required.
    type StructVariantState;

    /// Serializes a `bool` value.
    fn serialize_bool(&mut self, v: bool) -> Result<(), Self::Error>;

    /// Serializes an `isize` value. If the format does not differentiate
    /// between `isize` and `i64`, a reasonable implementation would be to cast
    /// the value to `i64` and forward to `serialize_i64`.
    fn serialize_isize(&mut self, v: isize) -> Result<(), Self::Error>;

    /// Serializes an `i8` value. If the format does not differentiate between
    /// `i8` and `i64`, a reasonable implementation would be to cast the value
    /// to `i64` and forward to `serialize_i64`.
    fn serialize_i8(&mut self, v: i8) -> Result<(), Self::Error>;

    /// Serializes an `i16` value. If the format does not differentiate between
    /// `i16` and `i64`, a reasonable implementation would be to cast the value
    /// to `i64` and forward to `serialize_i64`.
    fn serialize_i16(&mut self, v: i16) -> Result<(), Self::Error>;

    /// Serializes an `i32` value. If the format does not differentiate between
    /// `i32` and `i64`, a reasonable implementation would be to cast the value
    /// to `i64` and forward to `serialize_i64`.
    fn serialize_i32(&mut self, v: i32) -> Result<(), Self::Error>;

    /// Serializes an `i64` value.
    fn serialize_i64(&mut self, v: i64) -> Result<(), Self::Error>;

    /// Serializes a `usize` value. If the format does not differentiate between
    /// `usize` and `u64`, a reasonable implementation would be to cast the
    /// value to `u64` and forward to `serialize_u64`.
    fn serialize_usize(&mut self, v: usize) -> Result<(), Self::Error>;

    /// Serializes a `u8` value. If the format does not differentiate between
    /// `u8` and `u64`, a reasonable implementation would be to cast the value
    /// to `u64` and forward to `serialize_u64`.
    fn serialize_u8(&mut self, v: u8) -> Result<(), Self::Error>;

    /// Serializes a `u16` value. If the format does not differentiate between
    /// `u16` and `u64`, a reasonable implementation would be to cast the value
    /// to `u64` and forward to `serialize_u64`.
    fn serialize_u16(&mut self, v: u16) -> Result<(), Self::Error>;

    /// Serializes a `u32` value. If the format does not differentiate between
    /// `u32` and `u64`, a reasonable implementation would be to cast the value
    /// to `u64` and forward to `serialize_u64`.
    fn serialize_u32(&mut self, v: u32) -> Result<(), Self::Error>;

    /// `Serializes a `u64` value.
    fn serialize_u64(&mut self, v: u64) -> Result<(), Self::Error>;

    /// Serializes an `f32` value. If the format does not differentiate between
    /// `f32` and `f64`, a reasonable implementation would be to cast the value
    /// to `f64` and forward to `serialize_f64`.
    fn serialize_f32(&mut self, v: f32) -> Result<(), Self::Error>;

    /// Serializes an `f64` value.
    fn serialize_f64(&mut self, v: f64) -> Result<(), Self::Error>;

    /// Serializes a character.
    fn serialize_char(&mut self, v: char) -> Result<(), Self::Error>;

    /// Serializes a `&str`.
    fn serialize_str(&mut self, value: &str) -> Result<(), Self::Error>;

    /// Enables serializers to serialize byte slices more compactly or more
    /// efficiently than other types of slices. If no efficient implementation
    /// is available, a reasonable implementation would be to forward to
    /// `serialize_seq`.
    fn serialize_bytes(&mut self, value: &[u8]) -> Result<(), Self::Error>;

    /// Serializes a `()` value.
    fn serialize_unit(&mut self) -> Result<(), Self::Error>;

    /// Serializes a unit struct value. A reasonable implementation would be to
    /// forward to `serialize_unit`.
    fn serialize_unit_struct(
        &mut self,
        name: &str,
    ) -> Result<(), Self::Error>;

    /// Serializes a unit variant, otherwise known as a variant with no
    /// arguments. A reasonable implementation would be to forward to
    /// `serialize_unit`.
    fn serialize_unit_variant(
        &mut self,
        name: &str,
        variant_index: usize,
        variant: &str,
    ) -> Result<(), Self::Error>;

    /// Allows a tuple struct with a single element, also known as a newtype
    /// struct, to be more efficiently serialized than a tuple struct with
    /// multiple items. A reasonable implementation would be to forward to
    /// `serialize_tuple_struct`.
    fn serialize_newtype_struct<T: Serialize>(
        &mut self,
        name: &str,
        value: T,
    ) -> Result<(), Self::Error>;

    /// Allows a variant with a single item to be more efficiently serialized
    /// than a variant with multiple items. A reasonable implementation would be
    /// to forward to `serialize_tuple_variant`.
    fn serialize_newtype_variant<T: Serialize>(
        &mut self,
        name: &str,
        variant_index: usize,
        variant: &str,
        value: T,
    ) -> Result<(), Self::Error>;

    /// Serializes a `None` value.
    fn serialize_none(&mut self) -> Result<(), Self::Error>;

    /// Serializes a `Some(...)` value.
    fn serialize_some<T: Serialize>(
        &mut self,
        value: T,
    ) -> Result<(), Self::Error>;

    /// Begins to serialize a sequence. This call must be followed by zero or
    /// more calls to `serialize_seq_elt`, then a call to `serialize_seq_end`.
    fn serialize_seq(
        &mut self,
        len: Option<usize>,
    ) -> Result<Self::SeqState, Self::Error>;

    /// Serializes a sequence element. Must have previously called
    /// `serialize_seq`.
    fn serialize_seq_elt<T: Serialize>(
        &mut self,
        state: &mut Self::SeqState,
        value: T,
    ) -> Result<(), Self::Error>;

    /// Finishes serializing a sequence.
    fn serialize_seq_end(
        &mut self,
        state: Self::SeqState,
    ) -> Result<(), Self::Error>;

    /// Begins to serialize a tuple. This call must be followed by zero or more
    /// calls to `serialize_tuple_elt`, then a call to `serialize_tuple_end`. A
    /// reasonable implementation would be to forward to `serialize_seq`.
    fn serialize_tuple(
        &mut self,
        len: usize,
    ) -> Result<Self::TupleState, Self::Error>;

    /// Serializes a tuple element. Must have previously called
    /// `serialize_tuple`.
    fn serialize_tuple_elt<T: Serialize>(
        &mut self,
        state: &mut Self::TupleState,
        value: T,
    ) -> Result<(), Self::Error>;

    /// Finishes serializing a tuple.
    fn serialize_tuple_end(
        &mut self,
        state: Self::TupleState,
    ) -> Result<(), Self::Error>;

    /// Begins to serialize a tuple struct. This call must be followed by zero
    /// or more calls to `serialize_tuple_struct_elt`, then a call to
    /// `serialize_tuple_struct_end`. A reasonable implementation would be to
    /// forward to `serialize_tuple`.
    fn serialize_tuple_struct(
        &mut self,
        name: &str,
        len: usize,
    ) -> Result<Self::TupleStructState, Self::Error>;

    /// Serializes a tuple struct element. Must have previously called
    /// `serialize_tuple_struct`.
    fn serialize_tuple_struct_elt<T: Serialize>(
        &mut self,
        state: &mut Self::TupleStructState,
        value: T,
    ) -> Result<(), Self::Error>;

    /// Finishes serializing a tuple struct.
    fn serialize_tuple_struct_end(
        &mut self,
        state: Self::TupleStructState,
    ) -> Result<(), Self::Error>;

    /// Begins to serialize a tuple variant. This call must be followed by zero
    /// or more calls to `serialize_tuple_variant_elt`, then a call to
    /// `serialize_tuple_variant_end`. A reasonable implementation would be to
    /// forward to `serialize_tuple_struct`.
    fn serialize_tuple_variant(
        &mut self,
        name: &str,
        variant_index: usize,
        variant: &str,
        len: usize,
    ) -> Result<Self::TupleVariantState, Self::Error>;

    /// Serializes a tuple variant element. Must have previously called
    /// `serialize_tuple_variant`.
    fn serialize_tuple_variant_elt<T: Serialize>(
        &mut self,
        state: &mut Self::TupleVariantState,
        value: T,
    ) -> Result<(), Self::Error>;

    /// Finishes serializing a tuple variant.
    fn serialize_tuple_variant_end(
        &mut self,
        state: Self::TupleVariantState,
    ) -> Result<(), Self::Error>;

    /// Begins to serialize a map. This call must be followed by zero or more
    /// calls to `serialize_map_elt`, then a call to `serialize_map_end`.
    fn serialize_map(
        &mut self,
        len: Option<usize>,
    ) -> Result<Self::MapState, Self::Error>;

    /// Serialize a map element. Must have previously called `serialize_map`.
    fn serialize_map_elt<K: Serialize, V: Serialize>(
        &mut self,
        state: &mut Self::MapState,
        key: K,
        value: V,
    ) -> Result<(), Self::Error>;

    /// Finishes serializing a map.
    fn serialize_map_end(
        &mut self,
        state: Self::MapState,
    ) -> Result<(), Self::Error>;

    /// Begins to serialize a struct. This call must be followed by zero or more
    /// calls to `serialize_struct_elt`, then a call to `serialize_struct_end`.
    fn serialize_struct(
        &mut self,
        name: &str,
        len: usize,
    ) -> Result<Self::StructState, Self::Error>;

    /// Serializes a struct field. Must have previously called
    /// `serialize_struct`.
    fn serialize_struct_elt<V: Serialize>(
        &mut self,
        state: &mut Self::StructState,
        key: &str,
        value: V,
    ) -> Result<(), Self::Error>;

    /// Finishes serializing a struct.
    fn serialize_struct_end(
        &mut self,
        state: Self::StructState,
    ) -> Result<(), Self::Error>;

    /// Begins to serialize a struct variant. This call must be followed by zero
    /// or more calls to `serialize_struct_variant_elt`, then a call to
    /// `serialize_struct_variant_end`.
    fn serialize_struct_variant(
        &mut self,
        name: &str,
        variant_index: usize,
        variant: &str,
        len: usize,
    ) -> Result<Self::StructVariantState, Self::Error>;

    /// Serialize a struct variant element. Must have previously called
    /// `serialize_struct_variant`.
    fn serialize_struct_variant_elt<V: Serialize>(
        &mut self,
        state: &mut Self::StructVariantState,
        key: &str,
        value: V,
    ) -> Result<(), Self::Error>;

    /// Finishes serializing a struct variant.
    fn serialize_struct_variant_end(
        &mut self,
        state: Self::StructVariantState,
    ) -> Result<(), Self::Error>;
}
<commit_msg>Restore serialize_seq_fixed_size<commit_after>//! Generic serialization framework.

#[cfg(feature = "std")]
use std::error;
#[cfg(not(feature = "std"))]
use error;

#[cfg(all(feature = "collections", not(feature = "std")))]
use collections::String;

pub mod impls;

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

/// `Error` is a trait that allows a `Serialize` to generically create a
/// `Serializer` error.
pub trait Error: Sized + error::Error {
    /// Raised when there is general error when deserializing a type.
    #[cfg(any(feature = "std", feature = "collections"))]
    fn custom<T: Into<String>>(msg: T) -> Self;

    /// Raised when there is general error when deserializing a type.
    #[cfg(all(not(feature = "std"), not(feature = "collections")))]
    fn custom<T: Into<&'static str>>(msg: T) -> Self;

    /// Raised when a `Serialize` was passed an incorrect value.
    fn invalid_value(msg: &str) -> Self {
        Error::custom(format!("invalid value: {}", msg))
    }
}

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

/// A trait that describes a type that can be serialized by a `Serializer`.
pub trait Serialize {
    /// Serializes this value into this serializer.
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: Serializer;
}

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

/// A trait that describes a type that can serialize a stream of values into the underlying format.
pub trait Serializer {
    /// The error type that can be returned if some error occurs during serialization.
    type Error: Error;

    /// A state object that is initialized by `serialize_seq`, passed to
    /// `serialize_seq_elt`, and consumed by `serialize_seq_end`. Use `()` if no
    /// state is required.
    type SeqState;
    /// A state object that is initialized by `serialize_tuple`, passed to
    /// `serialize_tuple_elt`, and consumed by `serialize_tuple_end`. Use `()`
    /// if no state is required.
    type TupleState;
    /// A state object that is initialized by `serialize_tuple_struct`, passed
    /// to `serialize_tuple_struct_elt`, and consumed by
    /// `serialize_tuple_struct_end`. Use `()` if no state is required.
    type TupleStructState;
    /// A state object that is initialized by `serialize_tuple_variant`, passed
    /// to `serialize_tuple_variant_elt`, and consumed by
    /// `serialize_tuple_variant_end`. Use `()` if no state is required.
    type TupleVariantState;
    /// A state object that is initialized by `serialize_map`, passed to
    /// `serialize_map_elt`, and consumed by `serialize_map_end`. Use `()` if no
    /// state is required.
    type MapState;
    /// A state object that is initialized by `serialize_struct`, passed to
    /// `serialize_struct_elt`, and consumed by `serialize_struct_end`. Use `()`
    /// if no state is required.
    type StructState;
    /// A state object that is initialized by `serialize_struct_variant`, passed
    /// to `serialize_struct_variant_elt`, and consumed by
    /// `serialize_struct_variant_end`. Use `()` if no state is required.
    type StructVariantState;

    /// Serializes a `bool` value.
    fn serialize_bool(&mut self, v: bool) -> Result<(), Self::Error>;

    /// Serializes an `isize` value. If the format does not differentiate
    /// between `isize` and `i64`, a reasonable implementation would be to cast
    /// the value to `i64` and forward to `serialize_i64`.
    fn serialize_isize(&mut self, v: isize) -> Result<(), Self::Error>;

    /// Serializes an `i8` value. If the format does not differentiate between
    /// `i8` and `i64`, a reasonable implementation would be to cast the value
    /// to `i64` and forward to `serialize_i64`.
    fn serialize_i8(&mut self, v: i8) -> Result<(), Self::Error>;

    /// Serializes an `i16` value. If the format does not differentiate between
    /// `i16` and `i64`, a reasonable implementation would be to cast the value
    /// to `i64` and forward to `serialize_i64`.
    fn serialize_i16(&mut self, v: i16) -> Result<(), Self::Error>;

    /// Serializes an `i32` value. If the format does not differentiate between
    /// `i32` and `i64`, a reasonable implementation would be to cast the value
    /// to `i64` and forward to `serialize_i64`.
    fn serialize_i32(&mut self, v: i32) -> Result<(), Self::Error>;

    /// Serializes an `i64` value.
    fn serialize_i64(&mut self, v: i64) -> Result<(), Self::Error>;

    /// Serializes a `usize` value. If the format does not differentiate between
    /// `usize` and `u64`, a reasonable implementation would be to cast the
    /// value to `u64` and forward to `serialize_u64`.
    fn serialize_usize(&mut self, v: usize) -> Result<(), Self::Error>;

    /// Serializes a `u8` value. If the format does not differentiate between
    /// `u8` and `u64`, a reasonable implementation would be to cast the value
    /// to `u64` and forward to `serialize_u64`.
    fn serialize_u8(&mut self, v: u8) -> Result<(), Self::Error>;

    /// Serializes a `u16` value. If the format does not differentiate between
    /// `u16` and `u64`, a reasonable implementation would be to cast the value
    /// to `u64` and forward to `serialize_u64`.
    fn serialize_u16(&mut self, v: u16) -> Result<(), Self::Error>;

    /// Serializes a `u32` value. If the format does not differentiate between
    /// `u32` and `u64`, a reasonable implementation would be to cast the value
    /// to `u64` and forward to `serialize_u64`.
    fn serialize_u32(&mut self, v: u32) -> Result<(), Self::Error>;

    /// `Serializes a `u64` value.
    fn serialize_u64(&mut self, v: u64) -> Result<(), Self::Error>;

    /// Serializes an `f32` value. If the format does not differentiate between
    /// `f32` and `f64`, a reasonable implementation would be to cast the value
    /// to `f64` and forward to `serialize_f64`.
    fn serialize_f32(&mut self, v: f32) -> Result<(), Self::Error>;

    /// Serializes an `f64` value.
    fn serialize_f64(&mut self, v: f64) -> Result<(), Self::Error>;

    /// Serializes a character.
    fn serialize_char(&mut self, v: char) -> Result<(), Self::Error>;

    /// Serializes a `&str`.
    fn serialize_str(&mut self, value: &str) -> Result<(), Self::Error>;

    /// Enables serializers to serialize byte slices more compactly or more
    /// efficiently than other types of slices. If no efficient implementation
    /// is available, a reasonable implementation would be to forward to
    /// `serialize_seq`.
    fn serialize_bytes(&mut self, value: &[u8]) -> Result<(), Self::Error>;

    /// Serializes a `()` value.
    fn serialize_unit(&mut self) -> Result<(), Self::Error>;

    /// Serializes a unit struct value. A reasonable implementation would be to
    /// forward to `serialize_unit`.
    fn serialize_unit_struct(
        &mut self,
        name: &str,
    ) -> Result<(), Self::Error>;

    /// Serializes a unit variant, otherwise known as a variant with no
    /// arguments. A reasonable implementation would be to forward to
    /// `serialize_unit`.
    fn serialize_unit_variant(
        &mut self,
        name: &str,
        variant_index: usize,
        variant: &str,
    ) -> Result<(), Self::Error>;

    /// Allows a tuple struct with a single element, also known as a newtype
    /// struct, to be more efficiently serialized than a tuple struct with
    /// multiple items. A reasonable implementation would be to forward to
    /// `serialize_tuple_struct`.
    fn serialize_newtype_struct<T: Serialize>(
        &mut self,
        name: &str,
        value: T,
    ) -> Result<(), Self::Error>;

    /// Allows a variant with a single item to be more efficiently serialized
    /// than a variant with multiple items. A reasonable implementation would be
    /// to forward to `serialize_tuple_variant`.
    fn serialize_newtype_variant<T: Serialize>(
        &mut self,
        name: &str,
        variant_index: usize,
        variant: &str,
        value: T,
    ) -> Result<(), Self::Error>;

    /// Serializes a `None` value.
    fn serialize_none(&mut self) -> Result<(), Self::Error>;

    /// Serializes a `Some(...)` value.
    fn serialize_some<T: Serialize>(
        &mut self,
        value: T,
    ) -> Result<(), Self::Error>;

    /// Begins to serialize a sequence. This call must be followed by zero or
    /// more calls to `serialize_seq_elt`, then a call to `serialize_seq_end`.
    fn serialize_seq(
        &mut self,
        len: Option<usize>,
    ) -> Result<Self::SeqState, Self::Error>;

    /// Serializes a sequence element. Must have previously called
    /// `serialize_seq`.
    fn serialize_seq_elt<T: Serialize>(
        &mut self,
        state: &mut Self::SeqState,
        value: T,
    ) -> Result<(), Self::Error>;

    /// Finishes serializing a sequence.
    fn serialize_seq_end(
        &mut self,
        state: Self::SeqState,
    ) -> Result<(), Self::Error>;

    /// Begins to serialize a sequence whose length will be known at
    /// deserialization time. This call must be followed by zero or more calls
    /// to `serialize_seq_elt`, then a call to `serialize_seq_end`. A reasonable
    /// implementation would be to forward to `serialize_seq`.
    fn serialize_seq_fixed_size(
        &mut self,
        size: usize,
    ) -> Result<Self::SeqState, Self::Error>;

    /// Begins to serialize a tuple. This call must be followed by zero or more
    /// calls to `serialize_tuple_elt`, then a call to `serialize_tuple_end`. A
    /// reasonable implementation would be to forward to `serialize_seq`.
    fn serialize_tuple(
        &mut self,
        len: usize,
    ) -> Result<Self::TupleState, Self::Error>;

    /// Serializes a tuple element. Must have previously called
    /// `serialize_tuple`.
    fn serialize_tuple_elt<T: Serialize>(
        &mut self,
        state: &mut Self::TupleState,
        value: T,
    ) -> Result<(), Self::Error>;

    /// Finishes serializing a tuple.
    fn serialize_tuple_end(
        &mut self,
        state: Self::TupleState,
    ) -> Result<(), Self::Error>;

    /// Begins to serialize a tuple struct. This call must be followed by zero
    /// or more calls to `serialize_tuple_struct_elt`, then a call to
    /// `serialize_tuple_struct_end`. A reasonable implementation would be to
    /// forward to `serialize_tuple`.
    fn serialize_tuple_struct(
        &mut self,
        name: &str,
        len: usize,
    ) -> Result<Self::TupleStructState, Self::Error>;

    /// Serializes a tuple struct element. Must have previously called
    /// `serialize_tuple_struct`.
    fn serialize_tuple_struct_elt<T: Serialize>(
        &mut self,
        state: &mut Self::TupleStructState,
        value: T,
    ) -> Result<(), Self::Error>;

    /// Finishes serializing a tuple struct.
    fn serialize_tuple_struct_end(
        &mut self,
        state: Self::TupleStructState,
    ) -> Result<(), Self::Error>;

    /// Begins to serialize a tuple variant. This call must be followed by zero
    /// or more calls to `serialize_tuple_variant_elt`, then a call to
    /// `serialize_tuple_variant_end`. A reasonable implementation would be to
    /// forward to `serialize_tuple_struct`.
    fn serialize_tuple_variant(
        &mut self,
        name: &str,
        variant_index: usize,
        variant: &str,
        len: usize,
    ) -> Result<Self::TupleVariantState, Self::Error>;

    /// Serializes a tuple variant element. Must have previously called
    /// `serialize_tuple_variant`.
    fn serialize_tuple_variant_elt<T: Serialize>(
        &mut self,
        state: &mut Self::TupleVariantState,
        value: T,
    ) -> Result<(), Self::Error>;

    /// Finishes serializing a tuple variant.
    fn serialize_tuple_variant_end(
        &mut self,
        state: Self::TupleVariantState,
    ) -> Result<(), Self::Error>;

    /// Begins to serialize a map. This call must be followed by zero or more
    /// calls to `serialize_map_elt`, then a call to `serialize_map_end`.
    fn serialize_map(
        &mut self,
        len: Option<usize>,
    ) -> Result<Self::MapState, Self::Error>;

    /// Serialize a map element. Must have previously called `serialize_map`.
    fn serialize_map_elt<K: Serialize, V: Serialize>(
        &mut self,
        state: &mut Self::MapState,
        key: K,
        value: V,
    ) -> Result<(), Self::Error>;

    /// Finishes serializing a map.
    fn serialize_map_end(
        &mut self,
        state: Self::MapState,
    ) -> Result<(), Self::Error>;

    /// Begins to serialize a struct. This call must be followed by zero or more
    /// calls to `serialize_struct_elt`, then a call to `serialize_struct_end`.
    fn serialize_struct(
        &mut self,
        name: &str,
        len: usize,
    ) -> Result<Self::StructState, Self::Error>;

    /// Serializes a struct field. Must have previously called
    /// `serialize_struct`.
    fn serialize_struct_elt<V: Serialize>(
        &mut self,
        state: &mut Self::StructState,
        key: &str,
        value: V,
    ) -> Result<(), Self::Error>;

    /// Finishes serializing a struct.
    fn serialize_struct_end(
        &mut self,
        state: Self::StructState,
    ) -> Result<(), Self::Error>;

    /// Begins to serialize a struct variant. This call must be followed by zero
    /// or more calls to `serialize_struct_variant_elt`, then a call to
    /// `serialize_struct_variant_end`.
    fn serialize_struct_variant(
        &mut self,
        name: &str,
        variant_index: usize,
        variant: &str,
        len: usize,
    ) -> Result<Self::StructVariantState, Self::Error>;

    /// Serialize a struct variant element. Must have previously called
    /// `serialize_struct_variant`.
    fn serialize_struct_variant_elt<V: Serialize>(
        &mut self,
        state: &mut Self::StructVariantState,
        key: &str,
        value: V,
    ) -> Result<(), Self::Error>;

    /// Finishes serializing a struct variant.
    fn serialize_struct_variant_end(
        &mut self,
        state: Self::StructVariantState,
    ) -> Result<(), Self::Error>;
}
<|endoftext|>


<commit_before>//! Simple mailbox implementation using `HashMap`s
//!

use std::collections::{HashMap, VecDeque};
use std::collections::hash_map::Entry;
use std::fmt::Debug;
use std::cmp::Ordering;

use rustc_serialize::Encodable;
use rustc_serialize::json;

use mpirs::comm_request::{CommRequest, RequestProc};

macro_rules! get_value {
    ($m: ident, $k: expr) => {
        match $k {
            KT::H1(ref k) => $m.h1.get(k).clone(),
            KT::H2(ref k) => $m.h2.get(k).clone(),
        }
    }
}

macro_rules! insert_value {
    ($m: ident, $k: expr, $v: expr) => {
        match $k {
            KT::H1(ref k) => $m.h1.insert(k.clone(), $v.clone()),
            KT::H2(ref k) => $m.h2.insert(k.clone(), $v.clone()),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct MailboxKey {
    key_type: MessageTy,
    actor: RequestProc,
    tag: u64,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum MessageTy {
    MSend,
    MRecv,
}

impl MailboxKey {
    fn new(kt: MessageTy, p: RequestProc, t: u64) -> MailboxKey {
        MailboxKey {
            key_type: kt,
            actor: p,
            tag: t,
        }
    }
}

#[derive(Clone, Debug)]
enum KT {
    H1(MailboxKey),
    H2(MailboxKey),
}

#[derive(Clone, Debug)]
pub struct Mail {
    id: usize,
    port: usize,
    req: String,
}

impl Mail {
    pub fn new<T>(id: usize, port: usize, req: &CommRequest<T>) -> Mail
        where T: Debug + Clone + Encodable
    {
        Mail {
            id: id,
            port: port,
            req: json::encode(req).unwrap(),
        }
    }
}

impl PartialEq for Mail {
    fn eq(&self, other: &Mail) -> bool {
        self.id == other.id
    }
}

impl Eq for Mail { }

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

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

#[derive(Debug, Clone)]
pub struct Mailbox {
    h1: HashMap<MailboxKey, VecDeque<Mail>>,
    h2: HashMap<MailboxKey, VecDeque<Mail>>,
    id: usize,
}

impl Mailbox {
    pub fn new() -> Mailbox {
        Mailbox {
            id: 0,
            h1: HashMap::new(),
            h2: HashMap::new(),
        }
    }

    pub fn pop_matching_mail<T>(&mut self, req: &CommRequest<T>) -> Option<Mail>
        where T: Debug + Clone + Encodable
    {
        let keys = Mailbox::mirror_keys(req);
        match keys.len() {
            2 => self.fast_first_union(keys),
            3 => self.fast_first_union_intersect(keys),
            _ => unreachable!(),
        }
    }

    pub fn insert_mail<T>(&mut self, mail: Mail, req: &CommRequest<T>, port: usize)
        where T: Debug + Clone + Encodable
    {
        let mtype = if req.is_send() {
            MessageTy::MSend
        } else {
            MessageTy::MRecv
        };

        let mail = Mail::new(self.id, port, req);
        self.id += 1;

        let h1_key = MailboxKey::new(mtype, req.src().unwrap(), req.tag());
        let h2_key = MailboxKey::new(mtype, req.dst().unwrap(), req.tag());

        if !self.h1.contains_key(&h1_key) {
            self.h1.insert(h1_key, VecDeque::new());
        }

        if let Some(ref mut v) = self.h1.get_mut(&h1_key) {
            v.push_back(mail.clone());
        }

        if !self.h2.contains_key(&h2_key) {
            self.h2.insert(h2_key, VecDeque::new());
        }

        if let Some(ref mut v) = self.h2.get_mut(&h2_key) {
            v.push_back(mail.clone());
        }
    }

    fn mirror_keys<T>(req: &CommRequest<T>) -> Vec<KT>
        where T: Debug + Clone + Encodable
    {
        if req.is_send() {
            let mut keys = vec![KT::H1(MailboxKey::new(MessageTy::MRecv,
                                                       req.src().unwrap(),
                                                       req.tag())),
                                KT::H1(MailboxKey::new(MessageTy::MRecv,
                                                       RequestProc::Any,
                                                       req.tag()))];
            if !req.is_dst_any() {
                keys.push(KT::H2(MailboxKey::new(MessageTy::MRecv, req.dst().unwrap(), req.tag())));
            }
            keys
        } else if req.is_recv() {
            let mut keys = vec![KT::H2(MailboxKey::new(MessageTy::MSend,
                                                       req.dst().unwrap(),
                                                       req.tag())),
                                KT::H2(MailboxKey::new(MessageTy::MSend,
                                                       RequestProc::Any,
                                                       req.tag()))];
            if !req.is_src_any() {
                keys.push(KT::H1(MailboxKey::new(MessageTy::MSend, req.src().unwrap(), req.tag())));
            }
            keys
        } else {
            panic!("Request is neither a send nor recv");
        }
    }

    fn fast_first_union_intersect(&mut self, keys: Vec<KT>) -> Option<Mail> {
        unimplemented!()
    }

    fn fast_first_union(&mut self, keys: Vec<KT>) -> Option<Mail> {
        let (e, v1_, v2_) = {
            let v1 = get_value!(self, keys[0]);
            let v2 = get_value!(self, keys[1]);

            match (v1, v2) {
                (None, None) => (None, None, None),
                (None, Some(e)) => {
                    let mut e_ = e.clone();
                    let ele = e_.pop_front();
                    (ele, None, Some(e_))
                }
                (Some(e), None) => {
                    let mut e_ = e.clone();
                    let ele = e_.pop_front();
                    (ele, Some(e_), None)
                }
                (Some(e), Some(e_)) => {
                    let mut vec1 = e.clone();
                    let mut vec2 = e_.clone();
                    let ele = match vec1[0].cmp(&vec2[0]) {
                        Ordering::Less => vec1.pop_front(),
                        Ordering::Greater => vec2.pop_front(),
                        Ordering::Equal => unreachable!(),
                    };
                    (ele, Some(vec1), Some(vec2))
                },
            }
        };
        if v1_.is_some() {
            insert_value!(self, keys[0], v1_.unwrap());
        }
        if v2_.is_some() {
            insert_value!(self, keys[1], v2_.unwrap());
        }
        e
    }
}
<commit_msg>Implements fast first union intesect<commit_after>//! Simple mailbox implementation using `HashMap`s
//!

use std::collections::{HashMap, VecDeque};
use std::collections::hash_map::Entry;
use std::fmt::Debug;
use std::cmp::Ordering;

use rustc_serialize::Encodable;
use rustc_serialize::json;

use mpirs::comm_request::{CommRequest, RequestProc};

macro_rules! get_value {
    ($m: ident, $k: expr) => {
        match $k {
            KT::H1(ref k) => $m.h1.get(k).cloned(),
            KT::H2(ref k) => $m.h2.get(k).cloned(),
        }
    }
}

macro_rules! insert_value {
    ($m: ident, $k: expr, $v: expr) => {
        match $k {
            KT::H1(ref k) => $m.h1.insert(k.clone(), $v.clone()),
            KT::H2(ref k) => $m.h2.insert(k.clone(), $v.clone()),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct MailboxKey {
    key_type: MessageTy,
    actor: RequestProc,
    tag: u64,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum MessageTy {
    MSend,
    MRecv,
}

impl MailboxKey {
    fn new(kt: MessageTy, p: RequestProc, t: u64) -> MailboxKey {
        MailboxKey {
            key_type: kt,
            actor: p,
            tag: t,
        }
    }
}

#[derive(Clone, Debug)]
enum KT {
    H1(MailboxKey),
    H2(MailboxKey),
}

#[derive(Clone, Debug)]
pub struct Mail {
    id: usize,
    port: usize,
    req: String,
}

impl Mail {
    pub fn new<T>(id: usize, port: usize, req: &CommRequest<T>) -> Mail
        where T: Debug + Clone + Encodable
    {
        Mail {
            id: id,
            port: port,
            req: json::encode(req).unwrap(),
        }
    }
}

impl PartialEq for Mail {
    fn eq(&self, other: &Mail) -> bool {
        self.id == other.id
    }
}

impl Eq for Mail { }

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

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

#[derive(Debug, Clone)]
pub struct Mailbox {
    h1: HashMap<MailboxKey, VecDeque<Mail>>,
    h2: HashMap<MailboxKey, VecDeque<Mail>>,
    id: usize,
}

impl Mailbox {
    pub fn new() -> Mailbox {
        Mailbox {
            id: 0,
            h1: HashMap::new(),
            h2: HashMap::new(),
        }
    }

    pub fn pop_matching_mail<T>(&mut self, req: &CommRequest<T>) -> Option<Mail>
        where T: Debug + Clone + Encodable
    {
        let keys = Mailbox::mirror_keys(req);
        match keys.len() {
            2 => self.fast_first_union(keys),
            3 => self.fast_first_union_intersect(keys),
            _ => unreachable!(),
        }
    }

    pub fn insert_mail<T>(&mut self, mail: Mail, req: &CommRequest<T>, port: usize)
        where T: Debug + Clone + Encodable
    {
        let mtype = if req.is_send() {
            MessageTy::MSend
        } else {
            MessageTy::MRecv
        };

        let mail = Mail::new(self.id, port, req);
        self.id += 1;

        let h1_key = MailboxKey::new(mtype, req.src().unwrap(), req.tag());
        let h2_key = MailboxKey::new(mtype, req.dst().unwrap(), req.tag());

        if !self.h1.contains_key(&h1_key) {
            self.h1.insert(h1_key, VecDeque::new());
        }

        if let Some(ref mut v) = self.h1.get_mut(&h1_key) {
            v.push_back(mail.clone());
        }

        if !self.h2.contains_key(&h2_key) {
            self.h2.insert(h2_key, VecDeque::new());
        }

        if let Some(ref mut v) = self.h2.get_mut(&h2_key) {
            v.push_back(mail.clone());
        }
    }

    fn mirror_keys<T>(req: &CommRequest<T>) -> Vec<KT>
        where T: Debug + Clone + Encodable
    {
        if req.is_send() {
            let mut keys = vec![KT::H1(MailboxKey::new(MessageTy::MRecv,
                                                       req.src().unwrap(),
                                                       req.tag())),
                                KT::H1(MailboxKey::new(MessageTy::MRecv,
                                                       RequestProc::Any,
                                                       req.tag()))];
            if !req.is_dst_any() {
                keys.push(KT::H2(MailboxKey::new(MessageTy::MRecv, req.dst().unwrap(), req.tag())));
            }
            keys
        } else if req.is_recv() {
            let mut keys = vec![KT::H2(MailboxKey::new(MessageTy::MSend,
                                                       req.dst().unwrap(),
                                                       req.tag())),
                                KT::H2(MailboxKey::new(MessageTy::MSend,
                                                       RequestProc::Any,
                                                       req.tag()))];
            if !req.is_src_any() {
                keys.push(KT::H1(MailboxKey::new(MessageTy::MSend, req.src().unwrap(), req.tag())));
            }
            keys
        } else {
            panic!("Request is neither a send nor recv");
        }
    }

    fn fast_first_union_intersect(&mut self, keys: Vec<KT>) -> Option<Mail> {
        let mut v1 = get_value!(self, keys[0]).unwrap_or(VecDeque::new());
        let mut v2 = get_value!(self, keys[1]).unwrap_or(VecDeque::new());
        let mut v3 = get_value!(self, keys[2]).unwrap_or(VecDeque::new());

        let mut i: usize = 0;
        let mut j: usize = 0;
        let mut k: usize = 0;

        let mut found: Option<Mail> = None;

        loop {
            if k >= v3.len() { break; }
            if i >= v1.len() && j >= v2.len() { break; }

            if i < v1.len() {
                if v1[i] < v3[k] {
                    i += 1;
                } else if v1[i] == v3[k] {
                    found = v1.remove(i);
                    v3.remove(k);
                    break;
                }
            }

            if j < v2.len() {
                if v2[j] < v3[k] {
                    j += 1;
                } else if v2[j] == v3[k] {
                    found = v2.remove(j);
                    v3.remove(k);
                    break;
                }
            }

            if i < v1.len() && j < v2.len() {
                if v1[i] > v3[k] && v2[j] > v3[k] {
                    k += 1;
                }
            }
        }

        insert_value!(self, keys[0], v1);
        insert_value!(self, keys[1], v2);
        insert_value!(self, keys[2], v3);
        found
    }

    fn fast_first_union(&mut self, keys: Vec<KT>) -> Option<Mail> {
        let (e, v1_, v2_) = {
            let v1 = get_value!(self, keys[0]);
            let v2 = get_value!(self, keys[1]);

            match (v1, v2) {
                (None, None) => (None, None, None),
                (None, Some(e)) => {
                    let mut e_ = e.clone();
                    let ele = e_.pop_front();
                    (ele, None, Some(e_))
                }
                (Some(e), None) => {
                    let mut e_ = e.clone();
                    let ele = e_.pop_front();
                    (ele, Some(e_), None)
                }
                (Some(e), Some(e_)) => {
                    let mut vec1 = e.clone();
                    let mut vec2 = e_.clone();
                    let ele = match vec1[0].cmp(&vec2[0]) {
                        Ordering::Less => vec1.pop_front(),
                        Ordering::Greater => vec2.pop_front(),
                        Ordering::Equal => unreachable!(),
                    };
                    (ele, Some(vec1), Some(vec2))
                },
            }
        };
        if v1_.is_some() {
            insert_value!(self, keys[0], v1_.unwrap());
        }
        if v2_.is_some() {
            insert_value!(self, keys[1], v2_.unwrap());
        }
        e
    }
}
<|endoftext|>


<commit_before>use std::os::getenv;

struct HttpContext{
    request: Request,
    response: Response
}

impl HttpContext{
    fn create() -> HttpContext{
        HttpContext{
            request: Request::populate(),
            response: Response::new()
        }
    }
}

struct Request {
    path: ~str,
    querystring: ~str
}

impl Request {
    fn populate() -> Request {
        Request{
            path: getenv("PATH_INFO").unwrap_or(~""),
            querystring: getenv("QUERY_STRING").unwrap_or(~"")
        }
    }
}

struct Response{
    contenttype: ~str,
    body: ~str
}

impl Response{
    fn new() -> Response{
        Response{contenttype: ~"text/plain", body: ~""}
    }
}

struct Route {
    path: ~str,
    handler: ~fn(@mut HttpContext)
}

struct Router{
    routes: ~[Route],
}

impl Router{
    fn new() -> Router{
        Router{routes: ~[]}
    }
    fn add(&mut self, r: ~Route){
        self.routes.push(*r);
    }
    fn execute(&self, c: @mut HttpContext) {
        for route in self.routes.iter(){
            if(route.path == c.request.path){
                (route.handler)(c);
            }
        }
    }
    fn controller<T>(@mut self, creator: ~fn(@mut HttpContext) -> T) -> ControllerBox<T>
    {
        ControllerBox{
            router: self,
            route: ~Route{path: ~"", handler: default_handler},
            creator: Some(creator)
        }
    }
}

trait HttpController{

}

struct ControllerBox<T>{
    router: @mut Router,
    route: ~Route,
    creator: Option<~fn(@mut HttpContext) -> T>
}

impl<T> ControllerBox<T>{
    fn handles(@mut self, path: ~str) -> @mut ControllerBox<T>{
        self.route.path=path;
        self
    }
    fn with(@mut self, invoker: ~fn(&mut T)) -> @mut ControllerBox<T>{
        let tmp=self.creator.take();

        let h = |c| {
            match tmp {
                None => (),
                Some(ref t) => {
                    let mut ctrl=(*t)(c);
                    invoker(&mut ctrl);
                }
            };
        };
        self.router.add(~Route{path: self.route.path.clone(), handler:h});
        self
    }
}
/*
struct Meh{
    biz: ~fn(int) -> int
}

impl Meh{
    fn foo(@mut self
}
*/

struct TestController{
    context: @mut HttpContext
}

impl TestController{
    fn new(c: @mut HttpContext) -> TestController{
        TestController{ context: c}
    }
    fn index(&mut self) {
        self.context.response.body.push_str("test index");
    }
}

fn default_handler(context: @mut HttpContext){
    context.response.body.push_str("404 not found");
}

fn index(context: @mut HttpContext){
    context.response.body.push_str("yay index");
}

fn foo(context: @mut HttpContext){
    context.response.body.push_str("yay foo");
}

fn main() {
    let mut context=@mut HttpContext::create();
    let mut router=@mut Router::new();
    router.add(~Route{path: ~"", handler: index});
    router.add(~Route{path: ~"/foo", handler: foo});

    let mut test = @mut router.controller(|c| TestController::new(c));
    test.handles(~"/test").with(|c| c.index());

    router.execute(context);

    println(context.response.contenttype);
    println("");
    println(context.response.body);
}<commit_msg>It actually works!<commit_after>use std::os::getenv;

struct HttpContext{
    request: Request,
    response: Response
}

impl HttpContext{
    fn create() -> HttpContext{
        HttpContext{
            request: Request::populate(),
            response: Response::new()
        }
    }
}

struct Request {
    path: ~str,
    querystring: ~str
}

impl Request {
    fn populate() -> Request {
        Request{
            path: getenv("PATH_INFO").unwrap_or(~""),
            querystring: getenv("QUERY_STRING").unwrap_or(~"")
        }
    }
}

struct Response{
    contenttype: ~str,
    body: ~str
}

impl Response{
    fn new() -> Response{
        Response{contenttype: ~"text/plain", body: ~""}
    }
}

struct Route {
    path: ~str,
    handler: ~fn(@mut HttpContext)
}

struct Router{
    routes: ~[Route],
}

impl Router{
    fn new() -> Router{
        Router{routes: ~[]}
    }
    fn add(&mut self, r: ~Route){
        self.routes.push(*r);
    }
    fn execute(&self, c: @mut HttpContext) {
        for route in self.routes.iter(){
            if(route.path == c.request.path){
                (route.handler)(c);
            }
        }
    }
    fn controller<T>(@mut self, creator: ~fn(@mut HttpContext) -> T) -> ControllerBox<T>
    {
        ControllerBox{
            router: self,
            route: ~Route{path: ~"", handler: default_handler},
            creator: Some(creator)
        }
    }
}

trait HttpController{

}

struct ControllerBox<T>{
    router: @mut Router,
    route: ~Route,
    creator: Option<~fn(@mut HttpContext) -> T>
}

impl<T> ControllerBox<T>{
    fn handles(@mut self, path: ~str) -> @mut ControllerBox<T>{
        self.route.path=path;
        self
    }
    fn with(@mut self, invoker: ~fn(&mut T)) -> @mut ControllerBox<T>{
        let tmp=self.creator.take();

        self.router.add(~Route{path: self.route.path.clone(), handler:
         |c| {
            match tmp {
                None => (),
                Some(ref t) => {
                    let mut ctrl=(*t)(c);
                    invoker(&mut ctrl);
                }
            };
        }});
        self
    }
}
/*
struct Meh{
    biz: ~fn(int) -> int
}

impl Meh{
    fn foo(@mut self
}
*/

struct TestController{
    context: @mut HttpContext
}

impl TestController{
    fn new(c: @mut HttpContext) -> TestController{
        TestController{ context: c}
    }
    fn index(&mut self) {
        self.context.response.body.push_str("test index");
    }
}

fn default_handler(context: @mut HttpContext){
    context.response.body.push_str("404 not found");
}

fn index(context: @mut HttpContext){
    context.response.body.push_str("yay index");
}

fn foo(context: @mut HttpContext){
    context.response.body.push_str("yay foo");
}

fn main() {
    let mut context=@mut HttpContext::create();
    let mut router=@mut Router::new();
    router.add(~Route{path: ~"", handler: index});
    router.add(~Route{path: ~"/foo", handler: foo});

    let mut test = @mut router.controller(|c| TestController::new(c));
    test.handles(~"/test").with(|c| c.index());

    router.execute(context);

    println(context.response.contenttype);
    println("");
    println(context.response.body);
}<|endoftext|>


<commit_before>extern crate tempdir;

use nix::unistd::*;
use nix::unistd::ForkResult::*;
use nix::sys::wait::*;
use nix::sys::stat;
use std::iter;
use std::ffi::CString;
use std::fs::File;
use std::io::Write;
use std::os::unix::prelude::*;
use tempfile::tempfile;
use tempdir::TempDir;
use libc::off_t;
use std::process::exit;

#[test]
fn test_fork_and_waitpid() {
    #[allow(unused_variables)]
    let m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");
    let pid = fork();
    match pid {
        Ok(Child) => exit(0),
        Ok(Parent { child }) => {
            // assert that child was created and pid > 0
            let child_raw: ::libc::pid_t = child.into();
            assert!(child_raw > 0);
            let wait_status = waitpid(child, None);
            match wait_status {
                // assert that waitpid returned correct status and the pid is the one of the child
                Ok(WaitStatus::Exited(pid_t, _)) =>  assert!(pid_t == child),

                // panic, must never happen
                s @ Ok(_) => panic!("Child exited {:?}, should never happen", s),

                // panic, waitpid should never fail
                Err(s) => panic!("Error: waitpid returned Err({:?}", s)
            }

        },
        // panic, fork should never fail unless there is a serious problem with the OS
        Err(_) => panic!("Error: Fork Failed")
    }
}

#[test]
fn test_wait() {
    // Grab FORK_MTX so wait doesn't reap a different test's child process
    #[allow(unused_variables)]
    let m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");
    let pid = fork();
    match pid {
        Ok(Child) => exit(0),
        Ok(Parent { child }) => {
            let wait_status = wait();

            // just assert that (any) one child returns with WaitStatus::Exited
            assert_eq!(wait_status, Ok(WaitStatus::Exited(child, 0)));
        },
        // panic, fork should never fail unless there is a serious problem with the OS
        Err(_) => panic!("Error: Fork Failed")
    }
}

#[test]
fn test_mkstemp() {
    #[cfg(target_os = "android")]
    let tmp = "/data/local/tmp/";
    #[cfg(not(target_os = "android"))]
    let tmp = "/tmp/";

    let result = mkstemp((tmp.to_owned() + "nix_tempfile.XXXXXX").as_str());
    match result {
        Ok((fd, path)) => {
            close(fd).unwrap();
            unlink(path.as_path()).unwrap();
        },
        Err(e) => panic!("mkstemp failed: {}", e)
    }

    let result = mkstemp(tmp);
    match result {
        Ok(_) => {
            panic!("mkstemp succeeded even though it should fail (provided a directory)");
        },
        Err(_) => {}
    }
}

#[test]
fn test_getpid() {
    let pid: ::libc::pid_t = getpid().into();
    let ppid: ::libc::pid_t = getppid().into();
    assert!(pid > 0);
    assert!(ppid > 0);
}

#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux_android {
    use nix::unistd::gettid;

    #[test]
    fn test_gettid() {
        let tid: ::libc::pid_t = gettid().into();
        assert!(tid > 0);
    }
}

macro_rules! execve_test_factory(
    ($test_name:ident, $syscall:ident, $unix_sh:expr, $android_sh:expr) => (
    #[test]
    fn $test_name() {
        #[allow(unused_variables)]
        let m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");
        // The `exec`d process will write to `writer`, and we'll read that
        // data from `reader`.
        let (reader, writer) = pipe().unwrap();

        match fork().unwrap() {
            Child => {
                #[cfg(not(target_os = "android"))]
                const SH_PATH: &'static [u8] = $unix_sh;

                #[cfg(target_os = "android")]
                const SH_PATH: &'static [u8] = $android_sh;

                // Close stdout.
                close(1).unwrap();
                // Make `writer` be the stdout of the new process.
                dup(writer).unwrap();
                // exec!
                $syscall(
                    &CString::new(SH_PATH).unwrap(),
                    &[CString::new(b"".as_ref()).unwrap(),
                      CString::new(b"-c".as_ref()).unwrap(),
                      CString::new(b"echo nix!!! && echo foo=$foo && echo baz=$baz"
                                   .as_ref()).unwrap()],
                    &[CString::new(b"foo=bar".as_ref()).unwrap(),
                      CString::new(b"baz=quux".as_ref()).unwrap()]).unwrap();
            },
            Parent { child } => {
                // Wait for the child to exit.
                waitpid(child, None).unwrap();
                // Read 1024 bytes.
                let mut buf = [0u8; 1024];
                read(reader, &mut buf).unwrap();
                // It should contain the things we printed using `/bin/sh`.
                let string = String::from_utf8_lossy(&buf);
                assert!(string.contains("nix!!!"));
                assert!(string.contains("foo=bar"));
                assert!(string.contains("baz=quux"));
            }
        }
    }
    )
);

#[test]
fn test_fchdir() {
    // fchdir changes the process's cwd
    #[allow(unused_variables)]
    let m = ::CWD_MTX.lock().expect("Mutex got poisoned by another test");

    let tmpdir = TempDir::new("test_fchdir").unwrap();
    let tmpdir_path = tmpdir.path().canonicalize().unwrap();
    let tmpdir_fd = File::open(&tmpdir_path).unwrap().into_raw_fd();

    assert!(fchdir(tmpdir_fd).is_ok());
    assert_eq!(getcwd().unwrap(), tmpdir_path);

    assert!(close(tmpdir_fd).is_ok());
}

#[test]
fn test_getcwd() {
    // chdir changes the process's cwd
    #[allow(unused_variables)]
    let m = ::CWD_MTX.lock().expect("Mutex got poisoned by another test");

    let tmpdir = TempDir::new("test_getcwd").unwrap();
    let tmpdir_path = tmpdir.path().canonicalize().unwrap();
    assert!(chdir(&tmpdir_path).is_ok());
    assert_eq!(getcwd().unwrap(), tmpdir_path);

    // make path 500 chars longer so that buffer doubling in getcwd
    // kicks in.  Note: One path cannot be longer than 255 bytes
    // (NAME_MAX) whole path cannot be longer than PATH_MAX (usually
    // 4096 on linux, 1024 on macos)
    let mut inner_tmp_dir = tmpdir_path.to_path_buf();
    for _ in 0..5 {
        let newdir = iter::repeat("a").take(100).collect::<String>();
        inner_tmp_dir.push(newdir);
        assert!(mkdir(inner_tmp_dir.as_path(), stat::S_IRWXU).is_ok());
    }
    assert!(chdir(inner_tmp_dir.as_path()).is_ok());
    assert_eq!(getcwd().unwrap(), inner_tmp_dir.as_path());
}

#[test]
fn test_lseek() {
    const CONTENTS: &'static [u8] = b"abcdef123456";
    let mut tmp = tempfile().unwrap();
    tmp.write_all(CONTENTS).unwrap();
    let tmpfd = tmp.into_raw_fd();

    let offset: off_t = 5;
    lseek(tmpfd, offset, Whence::SeekSet).unwrap();

    let mut buf = [0u8; 7];
    ::read_exact(tmpfd, &mut buf);
    assert_eq!(b"f123456", &buf);

    close(tmpfd).unwrap();
}

#[cfg(any(target_os = "linux", target_os = "android"))]
#[test]
fn test_lseek64() {
    const CONTENTS: &'static [u8] = b"abcdef123456";
    let mut tmp = tempfile().unwrap();
    tmp.write(CONTENTS).unwrap();
    let tmpfd = tmp.into_raw_fd();

    lseek64(tmpfd, 5, Whence::SeekSet).unwrap();

    let mut buf = [0u8; 7];
    ::read_exact(tmpfd, &mut buf);
    assert_eq!(b"f123456", &buf);

    close(tmpfd).unwrap();
}

execve_test_factory!(test_execve, execve, b"/bin/sh", b"/system/bin/sh");

#[test]
fn test_fpathconf_limited() {
    let f = tempfile().unwrap();
    // AFAIK, PATH_MAX is limited on all platforms, so it makes a good test
    let path_max = fpathconf(f.as_raw_fd(), PathconfVar::PATH_MAX);
    assert!(path_max.expect("fpathconf failed").expect("PATH_MAX is unlimited") > 0);
}

#[test]
fn test_pathconf_limited() {
    // AFAIK, PATH_MAX is limited on all platforms, so it makes a good test
    let path_max = pathconf(".", PathconfVar::PATH_MAX);
    assert!(path_max.expect("pathconf failed").expect("PATH_MAX is unlimited") > 0);
}

#[test]
fn test_sysconf_limited() {
    // AFAIK, OPEN_MAX is limited on all platforms, so it makes a good test
    let open_max = sysconf(SysconfVar::OPEN_MAX);
    assert!(open_max.expect("sysconf failed").expect("OPEN_MAX is unlimited") > 0);
}

#[cfg(target_os = "freebsd")]
#[test]
fn test_sysconf_unsupported() {
    // I know of no sysconf variables that are unsupported everywhere, but
    // _XOPEN_CRYPT is unsupported on FreeBSD 11.0, which is one of the platforms
    // we test.
    let open_max = sysconf(SysconfVar::_XOPEN_CRYPT);
    assert!(open_max.expect("sysconf failed").is_none())
}
<commit_msg>use std::env::temp_dir() to retrieve the temp directory in test_mkstemp<commit_after>extern crate tempdir;

use nix::unistd::*;
use nix::unistd::ForkResult::*;
use nix::sys::wait::*;
use nix::sys::stat;
use std::{env, iter};
use std::ffi::CString;
use std::fs::File;
use std::io::Write;
use std::os::unix::prelude::*;
use tempfile::tempfile;
use tempdir::TempDir;
use libc::off_t;
use std::process::exit;

#[test]
fn test_fork_and_waitpid() {
    #[allow(unused_variables)]
    let m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");
    let pid = fork();
    match pid {
        Ok(Child) => exit(0),
        Ok(Parent { child }) => {
            // assert that child was created and pid > 0
            let child_raw: ::libc::pid_t = child.into();
            assert!(child_raw > 0);
            let wait_status = waitpid(child, None);
            match wait_status {
                // assert that waitpid returned correct status and the pid is the one of the child
                Ok(WaitStatus::Exited(pid_t, _)) =>  assert!(pid_t == child),

                // panic, must never happen
                s @ Ok(_) => panic!("Child exited {:?}, should never happen", s),

                // panic, waitpid should never fail
                Err(s) => panic!("Error: waitpid returned Err({:?}", s)
            }

        },
        // panic, fork should never fail unless there is a serious problem with the OS
        Err(_) => panic!("Error: Fork Failed")
    }
}

#[test]
fn test_wait() {
    // Grab FORK_MTX so wait doesn't reap a different test's child process
    #[allow(unused_variables)]
    let m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");
    let pid = fork();
    match pid {
        Ok(Child) => exit(0),
        Ok(Parent { child }) => {
            let wait_status = wait();

            // just assert that (any) one child returns with WaitStatus::Exited
            assert_eq!(wait_status, Ok(WaitStatus::Exited(child, 0)));
        },
        // panic, fork should never fail unless there is a serious problem with the OS
        Err(_) => panic!("Error: Fork Failed")
    }
}

#[test]
fn test_mkstemp() {
    let mut path = env::temp_dir();
    path.push("nix_tempfile.XXXXXX");

    let result = mkstemp(&path);
    match result {
        Ok((fd, path)) => {
            close(fd).unwrap();
            unlink(path.as_path()).unwrap();
        },
        Err(e) => panic!("mkstemp failed: {}", e)
    }
}

#[test]
fn test_mkstemp_directory() {
    // mkstemp should fail if a directory is given
    assert!(mkstemp(&env::temp_dir()).is_err());
}

#[test]
fn test_getpid() {
    let pid: ::libc::pid_t = getpid().into();
    let ppid: ::libc::pid_t = getppid().into();
    assert!(pid > 0);
    assert!(ppid > 0);
}

#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux_android {
    use nix::unistd::gettid;

    #[test]
    fn test_gettid() {
        let tid: ::libc::pid_t = gettid().into();
        assert!(tid > 0);
    }
}

macro_rules! execve_test_factory(
    ($test_name:ident, $syscall:ident, $unix_sh:expr, $android_sh:expr) => (
    #[test]
    fn $test_name() {
        #[allow(unused_variables)]
        let m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");
        // The `exec`d process will write to `writer`, and we'll read that
        // data from `reader`.
        let (reader, writer) = pipe().unwrap();

        match fork().unwrap() {
            Child => {
                #[cfg(not(target_os = "android"))]
                const SH_PATH: &'static [u8] = $unix_sh;

                #[cfg(target_os = "android")]
                const SH_PATH: &'static [u8] = $android_sh;

                // Close stdout.
                close(1).unwrap();
                // Make `writer` be the stdout of the new process.
                dup(writer).unwrap();
                // exec!
                $syscall(
                    &CString::new(SH_PATH).unwrap(),
                    &[CString::new(b"".as_ref()).unwrap(),
                      CString::new(b"-c".as_ref()).unwrap(),
                      CString::new(b"echo nix!!! && echo foo=$foo && echo baz=$baz"
                                   .as_ref()).unwrap()],
                    &[CString::new(b"foo=bar".as_ref()).unwrap(),
                      CString::new(b"baz=quux".as_ref()).unwrap()]).unwrap();
            },
            Parent { child } => {
                // Wait for the child to exit.
                waitpid(child, None).unwrap();
                // Read 1024 bytes.
                let mut buf = [0u8; 1024];
                read(reader, &mut buf).unwrap();
                // It should contain the things we printed using `/bin/sh`.
                let string = String::from_utf8_lossy(&buf);
                assert!(string.contains("nix!!!"));
                assert!(string.contains("foo=bar"));
                assert!(string.contains("baz=quux"));
            }
        }
    }
    )
);

#[test]
fn test_fchdir() {
    // fchdir changes the process's cwd
    #[allow(unused_variables)]
    let m = ::CWD_MTX.lock().expect("Mutex got poisoned by another test");

    let tmpdir = TempDir::new("test_fchdir").unwrap();
    let tmpdir_path = tmpdir.path().canonicalize().unwrap();
    let tmpdir_fd = File::open(&tmpdir_path).unwrap().into_raw_fd();

    assert!(fchdir(tmpdir_fd).is_ok());
    assert_eq!(getcwd().unwrap(), tmpdir_path);

    assert!(close(tmpdir_fd).is_ok());
}

#[test]
fn test_getcwd() {
    // chdir changes the process's cwd
    #[allow(unused_variables)]
    let m = ::CWD_MTX.lock().expect("Mutex got poisoned by another test");

    let tmpdir = TempDir::new("test_getcwd").unwrap();
    let tmpdir_path = tmpdir.path().canonicalize().unwrap();
    assert!(chdir(&tmpdir_path).is_ok());
    assert_eq!(getcwd().unwrap(), tmpdir_path);

    // make path 500 chars longer so that buffer doubling in getcwd
    // kicks in.  Note: One path cannot be longer than 255 bytes
    // (NAME_MAX) whole path cannot be longer than PATH_MAX (usually
    // 4096 on linux, 1024 on macos)
    let mut inner_tmp_dir = tmpdir_path.to_path_buf();
    for _ in 0..5 {
        let newdir = iter::repeat("a").take(100).collect::<String>();
        inner_tmp_dir.push(newdir);
        assert!(mkdir(inner_tmp_dir.as_path(), stat::S_IRWXU).is_ok());
    }
    assert!(chdir(inner_tmp_dir.as_path()).is_ok());
    assert_eq!(getcwd().unwrap(), inner_tmp_dir.as_path());
}

#[test]
fn test_lseek() {
    const CONTENTS: &'static [u8] = b"abcdef123456";
    let mut tmp = tempfile().unwrap();
    tmp.write_all(CONTENTS).unwrap();
    let tmpfd = tmp.into_raw_fd();

    let offset: off_t = 5;
    lseek(tmpfd, offset, Whence::SeekSet).unwrap();

    let mut buf = [0u8; 7];
    ::read_exact(tmpfd, &mut buf);
    assert_eq!(b"f123456", &buf);

    close(tmpfd).unwrap();
}

#[cfg(any(target_os = "linux", target_os = "android"))]
#[test]
fn test_lseek64() {
    const CONTENTS: &'static [u8] = b"abcdef123456";
    let mut tmp = tempfile().unwrap();
    tmp.write(CONTENTS).unwrap();
    let tmpfd = tmp.into_raw_fd();

    lseek64(tmpfd, 5, Whence::SeekSet).unwrap();

    let mut buf = [0u8; 7];
    ::read_exact(tmpfd, &mut buf);
    assert_eq!(b"f123456", &buf);

    close(tmpfd).unwrap();
}

execve_test_factory!(test_execve, execve, b"/bin/sh", b"/system/bin/sh");

#[test]
fn test_fpathconf_limited() {
    let f = tempfile().unwrap();
    // AFAIK, PATH_MAX is limited on all platforms, so it makes a good test
    let path_max = fpathconf(f.as_raw_fd(), PathconfVar::PATH_MAX);
    assert!(path_max.expect("fpathconf failed").expect("PATH_MAX is unlimited") > 0);
}

#[test]
fn test_pathconf_limited() {
    // AFAIK, PATH_MAX is limited on all platforms, so it makes a good test
    let path_max = pathconf(".", PathconfVar::PATH_MAX);
    assert!(path_max.expect("pathconf failed").expect("PATH_MAX is unlimited") > 0);
}

#[test]
fn test_sysconf_limited() {
    // AFAIK, OPEN_MAX is limited on all platforms, so it makes a good test
    let open_max = sysconf(SysconfVar::OPEN_MAX);
    assert!(open_max.expect("sysconf failed").expect("OPEN_MAX is unlimited") > 0);
}

#[cfg(target_os = "freebsd")]
#[test]
fn test_sysconf_unsupported() {
    // I know of no sysconf variables that are unsupported everywhere, but
    // _XOPEN_CRYPT is unsupported on FreeBSD 11.0, which is one of the platforms
    // we test.
    let open_max = sysconf(SysconfVar::_XOPEN_CRYPT);
    assert!(open_max.expect("sysconf failed").is_none())
}
<|endoftext|>


<commit_before><commit_msg>Add a FauxStdout struct to use for output testing (unfinished)<commit_after>use std::io::{Write,Result};
use std::str;

pub struct FauxStdout {
    buffer: String
}

impl Write for FauxStdout {
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        self.buffer += str::from_utf8(buf).expect("u8 slice provided to FauxStdout.write() was not valid UTF-8");
        Ok(0)
    }

    fn flush(&mut self) -> Result<()> {
        Ok(())
    }
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Eliza Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2016-2017 Eliza Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Initial 32-bit bootloader for x86_64
#![crate_name = "boot"]
#![feature(asm)]
#![feature(lang_items)]
#![feature(naked_functions)]
#![feature(static_recursion)]
#![feature(linkage)]
#![feature(struct_field_attributes)]
#![feature(stmt_expr_attributes)]
#![no_std]

extern crate rlibc;

const TABLE_LENGTH: usize = 512;
const HUGE_PAGE_SIZE: u64 = 2 * 1024 * 1024; // 2 MiB

const ENTRY_FLAGS_PW: u64 = 0b11;

type Table = [TableEntry; TABLE_LENGTH];

trait PageTable: Sized {
    /// Install this page table as the top-level page table.
    #[inline(always)]
    unsafe fn set_top_level(&'static self) {
        asm!("mov cr3, $0" :: "r"(self) :: "intel");
    }
}

impl PageTable for Table { }

#[repr(C)]
struct TableEntry(u64);

impl TableEntry {
    /// Set this table entry to map to a lower-level page table.
    // TODO: use a marker type to ensure this always maps to a _lower-level_
    //       table?
    #[inline(always)]
    unsafe fn map_to_table(&mut self, to: &'static Table) {
        *self = TableEntry(to as *const _ as u64 | ENTRY_FLAGS_PW);
    }

    /// Set this table entry to map to a huge page with the given number.
    #[inline(always)]
    unsafe fn map_to_page(&mut self, number: usize) {
        const ENTRY_FLAGS_HUGE: u64 = 0b10000011;
        // the start address is the page number times the page's size
        let addr = number as u64 * HUGE_PAGE_SIZE;
        *self = TableEntry(addr | ENTRY_FLAGS_HUGE);
    }

}

use core::convert;

macro_rules! set_flags {
    (%$register:ident $( |= $body:expr);+ ) => {
        let mut $register: usize;
        asm!( concat!("mov $0, ", stringify!($register))
            : "=r"($register)
            ::: "intel");
        $($register |= $body;)+
        asm!( concat!("mov ", stringify!($register), ", $0")
            :: "r"($register)
            :: "intel");
    }
}

#[repr(C, packed)]
pub struct Gdt { _null: u64
               , code: u64
               }

#[repr(C, packed)]
pub struct GdtPointer { /// the length of the GDT
                        pub limit: u16
                      , /// pointer to the GDT
                        pub base: &'static Gdt
                      }

impl GdtPointer {
    #[cold] #[inline(always)]
    unsafe fn load (&self) {
        asm!("lgdt ($0)" :: "r"(self) : "memory");
    }
}

impl convert::From<&'static Gdt> for GdtPointer {
    #[cold] #[inline(always)]
    fn from(gdt: &'static Gdt) -> Self {
        use core::mem::size_of_val;
        GdtPointer { limit: size_of_val(gdt) as u16 - 1
                   , base: gdt }
    }
}

#[link_name = ".gdt64"]
#[link_section = ".gdt"]
#[no_mangle]
pub static GDT: Gdt
    = Gdt { _null: 0
          , code: (1<<44) | (1<<47) | (1<<41) | (1<<43) | (1<<53)
          };

#[inline(always)]
fn boot_write(s: &[u8]) {
    unsafe {
        use core::ptr::write_volatile;
        let vga_buf = 0xb8000 as *mut u16;
    	for (n, c) in s.iter().enumerate() {
    		write_volatile(vga_buf.offset(n as isize), 0x0200 + *c as u16);
    	}

    }

}

extern "C" {
    static mut pml4_table: Table;
    static mut pdp_table: Table;
    static mut pd_table: Table;
}

#[cold]
#[inline(always)]
#[naked]
unsafe fn create_page_tables() {
    //-- map the PML4 and PDP tables -----------------------------------------
    // recursive map last PML4 entry
    pml4_table[511].map_to_table(&pml4_table);
    // map first PML4 entry to PDP table
    pml4_table[0].map_to_table(&pdp_table);
    // map first PDPT entry to PD table
    pdp_table[0].map_to_table(&pd_table);

    boot_write(b"3.1");

    //-- map the PD table ----------------------------------------------------
    for (page_number, entry) in pd_table.iter_mut().enumerate() {
        entry.map_to_page(page_number);
    }

    boot_write(b"3.2");
}

#[cold]
#[inline(always)]
unsafe fn set_long_mode() {
    // load PML4 addr to cr3
    &pml4_table.set_top_level();
    boot_write(b"3.3");

    // // enable PAE flag in cr4
    set_flags!(%cr4 |= 1 << 5 );
    boot_write(b"3.4");

    // set the long mode bit in EFER MSR (model specific register)
    asm!( "mov   ecx, 0xC0000080
           rdmsr
           or    eax, 1 << 8
           wrmsr"
        :::: "intel");
    boot_write(b"3.5");

    // enable paging in cr0
    set_flags!(%cr0 |= 1 << 31;
                    |= 1 << 16 );
    boot_write(b"3.6")
}

/// Test whether or not this system supports Multiboot 2
#[cold]
#[inline(always)]
unsafe fn is_multiboot_supported() -> bool {
    const MULTIBOOT_MAGIC: usize = 0x36d76289;
    let eax: usize;
    asm!("mov eax, $0" : "=r"(eax) ::: "intel");
    eax == MULTIBOOT_MAGIC
}

#[cold]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn _start() {
    boot_write(b"0");
    asm!("cli");

    // 1. Move Multiboot info pointer to edi
    asm!("mov edi, ebx" :::: "intel");
    boot_write(b"1");

    // 2. make sure the system supports SOS
    // TODO: port this from boot.asm
    if !is_multiboot_supported() {
        loop { boot_write(b"ERROR: multiboot not supported!"); }
    }
    boot_write(b"2");

    // 3. if everything is okay, create the page tables and start long mode
    create_page_tables();
    set_long_mode();

    // 4. load the 64-bit GDT
    GdtPointer::from(&GDT).load();
    boot_write(b"4");

    // 6. jump to the 64-bit boot subroutine.
    asm!("ljmpl $$8, $$long_mode_init");

}
<commit_msg>[boot][ci skip] added more documentation to boot crate<commit_after>//
//  SOS: the Stupid Operating System
//  by Eliza Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2016-2017 Eliza Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! # SOS x86_64 bootstrap
//!
//! This crate contains code for the 32-bit protected mode boot routine for
//! x86_64 CPUs. When we boot up an x86_64 system using GRUB & multiboot, we
//! end up with the CPU running in 32-bit 'protected mode', rather than 64-bit
//! 'long mode'. Before we can jump into long mode, we have to perform a
//! handful of setup tasks in protected mode.
//!
//! This boot routine lives in a separate crate, with a 32-bit `target.json`.
//! When building SOS for x86_64, we have to compile this crate separately and
//! then link the resulting 32-bit object with the rest of the 64-bit kernel
//! object.
//!
//! This boot crate is only necessary for x86_64. For other architectures, all
//! boot code can live in the main kernel crate.
//!
//! For more information, refer to:
//! + the [intermezzOS book]
//! + the [OSDev Wiki] article on long mode
//! + Philipp Oppermann's [blog post]
//!
//! [intermezzOS book]: http://intermezzos.github.io/book/transitioning-to-long-mode.html
//! [OSDev Wiki]: http://wiki.osdev.org/Long_Mode#Long_Mode
//! [blog post]: http://os.phil-opp.com/entering-longmode.html

#![crate_name = "boot"]
#![feature(asm)]
#![feature(lang_items)]
#![feature(naked_functions)]
#![feature(static_recursion)]
#![feature(linkage)]
#![feature(struct_field_attributes)]
#![feature(stmt_expr_attributes)]
#![no_std]

extern crate rlibc;

const TABLE_LENGTH: usize = 512;
/// The size of a "huge" page
const HUGE_PAGE_SIZE: u64 = 2 * 1024 * 1024; // 2 MiB
/// Page table entry flags for a page that is present and writable.
const ENTRY_FLAGS_PW: u64 = 0b11;
/// Page table entry flags for a page that is huge, present, and writable.
const ENTRY_FLAGS_HUGE: u64 = 0b10000000 & ENTRY_FLAGS_PW;

type Table = [TableEntry; TABLE_LENGTH];

trait PageTable: Sized {
    /// Install this page table as the top-level page table.
    #[inline(always)]
    unsafe fn set_top_level(&'static self) {
        asm!("mov cr3, $0" :: "r"(self) :: "intel");
    }
}

impl PageTable for Table { }

#[repr(C)]
struct TableEntry(u64);

impl TableEntry {
    /// Set this table entry to map to a lower-level page table.
    // TODO: use a marker type to ensure this always maps to a _lower-level_
    //       table?
    #[inline(always)]
    unsafe fn map_to_table(&mut self, to: &'static Table) {
        *self = TableEntry(to as *const _ as u64 | ENTRY_FLAGS_PW);
    }

    /// Set this table entry to map to a huge page with the given number.
    #[inline(always)]
    unsafe fn map_to_page(&mut self, number: usize) {

        // the start address is the page number times the page's size
        let addr = number as u64 * HUGE_PAGE_SIZE;
        *self = TableEntry(addr | ENTRY_FLAGS_HUGE);
    }

}

use core::convert;

macro_rules! set_flags {
    (%$register:ident $( |= $body:expr);+ ) => {
        let mut $register: usize;
        asm!( concat!("mov $0, ", stringify!($register))
            : "=r"($register)
            ::: "intel");
        $($register |= $body;)+
        asm!( concat!("mov ", stringify!($register), ", $0")
            :: "r"($register)
            :: "intel");
    }
}

#[repr(C, packed)]
pub struct Gdt { _null: u64
               , code: u64
               }

#[repr(C, packed)]
pub struct GdtPointer { /// the length of the GDT
                        pub limit: u16
                      , /// pointer to the GDT
                        pub base: &'static Gdt
                      }

impl GdtPointer {
    #[cold] #[inline(always)]
    unsafe fn load (&self) {
        asm!("lgdt ($0)" :: "r"(self) : "memory");
    }
}

impl convert::From<&'static Gdt> for GdtPointer {
    #[cold] #[inline(always)]
    fn from(gdt: &'static Gdt) -> Self {
        use core::mem::size_of_val;
        GdtPointer { limit: size_of_val(gdt) as u16 - 1
                   , base: gdt }
    }
}

#[link_name = ".gdt64"]
#[link_section = ".gdt"]
#[no_mangle]
pub static GDT: Gdt
    = Gdt { _null: 0
          , code: (1<<44) | (1<<47) | (1<<41) | (1<<43) | (1<<53)
          };

#[inline(always)]
fn boot_write(s: &[u8]) {
    unsafe {
        use core::ptr::write_volatile;
        let vga_buf = 0xb8000 as *mut u16;
    	for (n, c) in s.iter().enumerate() {
    		write_volatile(vga_buf.offset(n as isize), 0x0200 + *c as u16);
    	}

    }

}

extern "C" {
    static mut pml4_table: Table;
    static mut pdp_table: Table;
    static mut pd_table: Table;
}

#[cold]
#[inline(always)]
#[naked]
unsafe fn create_page_tables() {
    //-- map the PML4 and PDP tables -----------------------------------------
    // recursive map last PML4 entry
    pml4_table[511].map_to_table(&pml4_table);
    // map first PML4 entry to PDP table
    pml4_table[0].map_to_table(&pdp_table);
    // map first PDPT entry to PD table
    pdp_table[0].map_to_table(&pd_table);

    boot_write(b"3.1");

    //-- map the PD table ----------------------------------------------------
    for (page_number, entry) in pd_table.iter_mut().enumerate() {
        entry.map_to_page(page_number);
    }

    boot_write(b"3.2");
}

#[cold]
#[inline(always)]
unsafe fn set_long_mode() {
    // load PML4 addr to cr3
    &pml4_table.set_top_level();
    boot_write(b"3.3");

    // // enable PAE flag in cr4
    set_flags!(%cr4 |= 1 << 5 );
    boot_write(b"3.4");

    // set the long mode bit in EFER MSR (model specific register)
    asm!( "mov   ecx, 0xC0000080
           rdmsr
           or    eax, 1 << 8
           wrmsr"
        :::: "intel");
    boot_write(b"3.5");

    // enable paging in cr0
    set_flags!(%cr0 |= 1 << 31;
                    |= 1 << 16 );
    boot_write(b"3.6")
}

/// Test whether or not this system supports Multiboot 2
#[cold]
#[inline(always)]
unsafe fn is_multiboot_supported() -> bool {
    const MULTIBOOT_MAGIC: usize = 0x36d76289;
    let eax: usize;
    asm!("mov eax, $0" : "=r"(eax) ::: "intel");
    eax == MULTIBOOT_MAGIC
}

#[cold]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn _start() {
    boot_write(b"0");
    asm!("cli");

    // 1. Move Multiboot info pointer to edi
    asm!("mov edi, ebx" :::: "intel");
    boot_write(b"1");

    // 2. make sure the system supports SOS
    // TODO: port this from boot.asm
    if !is_multiboot_supported() {
        loop { boot_write(b"ERROR: multiboot not supported!"); }
    }
    boot_write(b"2");

    // 3. if everything is okay, create the page tables and start long mode
    create_page_tables();
    set_long_mode();

    // 4. load the 64-bit GDT
    GdtPointer::from(&GDT).load();
    boot_write(b"4");

    // 6. jump to the 64-bit boot subroutine.
    asm!("ljmpl $$8, $$long_mode_init");

}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Eliza Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2016-2017 Eliza Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Initial 32-bit bootloader for x86_64
#![crate_name = "boot"]
// #![crate_type = "staticlib"]
#![feature(asm)]
#![feature(lang_items)]
#![feature(naked_functions)]
#![feature(static_recursion)]
#![feature(linkage)]
#![feature(struct_field_attributes)]
#![feature(stmt_expr_attributes)]
#![no_std]
// #![no_main]

const TABLE_LENGTH: usize = 512;

type Table = [u64; TABLE_LENGTH];

use core::ptr;
use core::convert;

#[repr(C, packed)]
pub struct Gdt { _null: u64
               , code: u64
               }

#[repr(C, packed)]
pub struct GdtPointer { /// the length of the GDT
                        pub limit: u16
                      , /// pointer to the GDT
                        pub base: &'static Gdt
                      }

impl GdtPointer {
    #[cold] #[inline(always)]
    unsafe fn load (&self) {
        asm!("lgdt ($0)" :: "r"(self) : "memory");
    }
}

impl convert::From<&'static Gdt> for GdtPointer {
    #[cold] #[inline(always)]
    fn from(gdt: &'static Gdt) -> Self {
        use core::mem::size_of_val;
        GdtPointer { limit: size_of_val(gdt) as u16 - 1
                   , base: gdt }
    }
}

#[link_name = ".gdt64"]
#[link_section = ".gdt"]
#[no_mangle]
pub static GDT: Gdt
    = Gdt { _null: 0
          , code: (1<<44) | (1<<47) | (1<<41) | (1<<43) | (1<<53)
        //   , data: (1 << 44) | (1 << 47) | (1 << 41)
          };

#[cfg(feature = "log")]
#[inline(always)]
#[naked]
fn boot_write(s: &[u8]) {
	// use core::ptr;
    let mut offset = 0;
    unsafe {
        let vga_buf: *mut u16 = 0xb8000 as *mut u16;
    	for c in s {
    		ptr::write_volatile( vga_buf.offset(offset)
                               , 0x0200 + *c as u16);
    		offset += 1;
    	}

    }

}

#[cfg(not(feature = "log"))]
fn boot_write(_s: &[u8]) { }

extern "C" {
    static mut pml4_table: Table;
    static mut pdp_table: Table;
    static mut pd_table: Table;    // static mut page_table: Table;
    // #[link_name = "__vga_buffer"]
    // static mut vga_buf: *mut u16;
}

#[cold]
#[inline(always)]
#[naked]
unsafe fn create_page_tables() {
    // 3. if everything is okay, create the page tables and start long mode
    const HUGE_PAGE_SIZE: u64 = 2 * 1024 * 1024; // 2 MiB

    //-- map the PML4 and PDP tables -----------------------------------------
    // recursive map last PML4 entry
    pml4_table[511] = (&pml4_table as *const _ as u64) | 0b11;
    // map first PML4 entry to PDP table
    pml4_table[0] = (&pdp_table as *const _ as u64) | 0b11;
    // map first PDPT entry to PD table
    pdp_table[0] = (&pd_table as *const _ as u64) | 0b11;

    boot_write(b"3.1");

    //-- map the PD table ----------------------------------------------------
    for (number, entry) in pd_table.iter_mut().enumerate() {
        // set each PD entry equal to the start address of the page (the page
        // number times the page's size)
        let addr = number as u64 * HUGE_PAGE_SIZE;
        // with the appropriate flags (present + writable + huge)
        // TODO: do we want to do this using bitflags, or is that too
        //       heavyweight for the boot module?
        //          - eliza, 1/23/2017
        *entry = addr | 0b10000011;
    }

    boot_write(b"3.2");
}
#[cold]
#[inline(always)]
#[naked]
unsafe fn set_long_mode() {

    // load PML4 addr to cr3
    asm!( "mov   cr3, $0" :: "r"(&pml4_table) :: "intel");
    boot_write(b"3.3");

    // enable PAE flag in cr4
    let mut cr4: usize;
    asm!("mov $0, cr4" : "=r"(cr4) ::: "intel");
    cr4 |= 1 << 5;
    asm!("mov cr4, $0" :: "r"(cr4) :: "intel");
    boot_write(b"3.4");

    // set the long mode bit in EFER MSR (model specific register)
    asm!( "mov   ecx, 0xC0000080
           rdmsr
           or    eax, 1 << 8
           wrmsr"
        :::: "intel");
    boot_write(b"3.5");

    // enable paging in cr0
    // let mut cr0: usize;
    // asm!( "mov $0, cr0" : "=r"(cr0) ::: "intel");
    // cr0 |= 1 << 31;
    // cr0 |= 1 << 16;
    // asm!( "mov cr0, $0" :: "r"(cr0) :: "intel");
    // boot_write(b"3.6");
    asm!( "mov eax, cr0
           or  eax, 1 << 31
           or  eax, 1 << 16
           mov cr0, eax"
        :::: "intel");
}

#[cold]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn _start() {
    boot_write(b"0");
    asm!("cli");

    // 1. Move Multiboot info pointer to edi
    // let multiboot: usize;
    asm!("mov edi, ebx"
         : //"=r"(multiboot)
         ::: "intel"
    );
    boot_write(b"1");

    // 2. make sure the system supports SOS
    // TODO: port this from boot.asm
    boot_write(b"2");

    create_page_tables();
    set_long_mode();

    // 4. load the 64-bit GDT
    GdtPointer::from(&GDT).load();

    boot_write(b"4");

    // // 5. update selectors
    // asm!("mov ax, 0x10" :::: "intel");
    // boot_write(b"5.1");
    // // stack selector
    // asm!("mov ss, ax" :::: "intel");
    // boot_write(b"5.2");
    // // data selector
    // asm!("mov ds, ax" :::: "intel");
    // boot_write(b"5.3");
    // // extra selector
    // asm!("mov es, ax" :::: "intel");
    // boot_write(b"5.4");
    // loop { }
    // 6. jump to the 64-bit boot subroutine.
    // 0. Move the stack pointer to the top of the stack.
    asm!("ljmpl $$8, $$long_mode_init");

}
<commit_msg>[boot] add a cute macro for setting control register flags<commit_after>//
//  SOS: the Stupid Operating System
//  by Eliza Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2016-2017 Eliza Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Initial 32-bit bootloader for x86_64
#![crate_name = "boot"]
// #![crate_type = "staticlib"]
#![feature(asm)]
#![feature(lang_items)]
#![feature(naked_functions)]
#![feature(static_recursion)]
#![feature(linkage)]
#![feature(struct_field_attributes)]
#![feature(stmt_expr_attributes)]
#![no_std]
// #![no_main]

const TABLE_LENGTH: usize = 512;

type Table = [u64; TABLE_LENGTH];

use core::ptr;
use core::convert;

macro_rules! set_flags {
    (%$register:ident $( |= $body:expr);+ ) => {
        let mut $register: usize;
        asm!( concat!("mov $0, ", stringify!($register))
            : "=r"($register)
            ::: "intel");
        $($register |= $body;)+
        asm!( concat!("mov ", stringify!($register), ", $0")
            :: "r"($register)
            :: "intel");
    }
}

#[repr(C, packed)]
pub struct Gdt { _null: u64
               , code: u64
               }

#[repr(C, packed)]
pub struct GdtPointer { /// the length of the GDT
                        pub limit: u16
                      , /// pointer to the GDT
                        pub base: &'static Gdt
                      }

impl GdtPointer {
    #[cold] #[inline(always)]
    unsafe fn load (&self) {
        asm!("lgdt ($0)" :: "r"(self) : "memory");
    }
}

impl convert::From<&'static Gdt> for GdtPointer {
    #[cold] #[inline(always)]
    fn from(gdt: &'static Gdt) -> Self {
        use core::mem::size_of_val;
        GdtPointer { limit: size_of_val(gdt) as u16 - 1
                   , base: gdt }
    }
}

#[link_name = ".gdt64"]
#[link_section = ".gdt"]
#[no_mangle]
pub static GDT: Gdt
    = Gdt { _null: 0
          , code: (1<<44) | (1<<47) | (1<<41) | (1<<43) | (1<<53)
        //   , data: (1 << 44) | (1 << 47) | (1 << 41)
          };

#[cfg(feature = "log")]
#[inline(always)]
#[naked]
fn boot_write(s: &[u8]) {
	// use core::ptr;
    let mut offset = 0;
    unsafe {
        let vga_buf: *mut u16 = 0xb8000 as *mut u16;
    	for c in s {
    		ptr::write_volatile( vga_buf.offset(offset)
                               , 0x0200 + *c as u16);
    		offset += 1;
    	}

    }

}

#[cfg(not(feature = "log"))]
fn boot_write(_s: &[u8]) { }

extern "C" {
    static mut pml4_table: Table;
    static mut pdp_table: Table;
    static mut pd_table: Table;
}

#[cold]
#[inline(always)]
#[naked]
unsafe fn create_page_tables() {
    // 3. if everything is okay, create the page tables and start long mode
    const HUGE_PAGE_SIZE: u64 = 2 * 1024 * 1024; // 2 MiB

    //-- map the PML4 and PDP tables -----------------------------------------
    // recursive map last PML4 entry
    pml4_table[511] = (&pml4_table as *const _ as u64) | 0b11;
    // map first PML4 entry to PDP table
    pml4_table[0] = (&pdp_table as *const _ as u64) | 0b11;
    // map first PDPT entry to PD table
    pdp_table[0] = (&pd_table as *const _ as u64) | 0b11;

    boot_write(b"3.1");

    //-- map the PD table ----------------------------------------------------
    for (number, entry) in pd_table.iter_mut().enumerate() {
        // set each PD entry equal to the start address of the page (the page
        // number times the page's size)
        let addr = number as u64 * HUGE_PAGE_SIZE;
        // with the appropriate flags (present + writable + huge)
        // TODO: do we want to do this using bitflags, or is that too
        //       heavyweight for the boot module?
        //          - eliza, 1/23/2017
        *entry = addr | 0b10000011;
    }

    boot_write(b"3.2");
}
#[cold]
#[inline(always)]
#[naked]
unsafe fn set_long_mode() {

    // load PML4 addr to cr3
    asm!("mov cr3, $0" :: "r"(&pml4_table) :: "intel");
    boot_write(b"3.3");

    // // enable PAE flag in cr4
    set_flags!(%cr4 |= 1 << 5 );
    boot_write(b"3.4");

    // set the long mode bit in EFER MSR (model specific register)
    asm!( "mov   ecx, 0xC0000080
           rdmsr
           or    eax, 1 << 8
           wrmsr"
        :::: "intel");
    boot_write(b"3.5");

    // enable paging in cr0
    set_flags!(%cr0 |= 1 << 31;
                    |= 1 << 16 );
}

#[cold]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn _start() {
    boot_write(b"0");
    asm!("cli");

    // 1. Move Multiboot info pointer to edi
    asm!("mov edi, ebx" :::: "intel");
    boot_write(b"1");

    // 2. make sure the system supports SOS
    // TODO: port this from boot.asm
    boot_write(b"2");

    create_page_tables();
    set_long_mode();

    // 4. load the 64-bit GDT
    GdtPointer::from(&GDT).load();

    boot_write(b"4");

    // // 5. update selectors
    // asm!("mov ax, 0x10" :::: "intel");
    // boot_write(b"5.1");
    // // stack selector
    // asm!("mov ss, ax" :::: "intel");
    // boot_write(b"5.2");
    // // data selector
    // asm!("mov ds, ax" :::: "intel");
    // boot_write(b"5.3");
    // // extra selector
    // asm!("mov es, ax" :::: "intel");
    // boot_write(b"5.4");
    // loop { }
    // 6. jump to the 64-bit boot subroutine.
    // 0. Move the stack pointer to the top of the stack.
    asm!("ljmpl $$8, $$long_mode_init");

}
<|endoftext|>