<commit_before>use hash::KEY_SIZE;
use hash::Hash160;
use std::net;
use std::collections::VecDeque;
use std::mem;
use itertools::Itertools;

const K : usize = 20;
const BUCKET_DEPTH : usize = K;

/// Kademlia routing table, with 160 buckets of `BUCKET_DEPTH` (k) node
/// identifiers each.
///
/// The structure employs least-recently seen eviction. Conflicts generated
/// by evicting a node by inserting a newer one remain tracked, so they can
/// can be resolved later.
pub struct RoutingTable {
   parent_key : Hash160,
   buckets    : Vec<Bucket>,
   conflicts  : Vec<EvictionConflict>,
}

/// Bucket size is estimated to be small enough not to warrant
/// the downsides of using a linked list.
#[derive(Debug, Clone)]
struct Bucket {
   entries  : VecDeque<NodeInfo>,
}

/// Represents a conflict derived from attempting to insert a node in a full
/// bucket.
#[derive(Debug,Clone)]
struct EvictionConflict {
   evicted  : NodeInfo,
   inserted : NodeInfo
}

#[derive(Debug, Clone, PartialEq)]
pub struct NodeInfo {
   pub key  : Hash160,
   pub ip   : net::IpAddr,
   pub port : u16,
}

#[derive(Debug, PartialEq)]
pub enum LookupResult {
   Found(NodeInfo),
   ClosestNodes(Vec<NodeInfo>),
   Myself,
}

impl RoutingTable {
   pub fn new(parent_key : Hash160) -> RoutingTable {
      let mut buckets = Vec::<Bucket>::with_capacity(KEY_SIZE);
      for _ in 0..KEY_SIZE {
         buckets.push(Bucket::new());
      }

      RoutingTable {
         parent_key : parent_key,
         buckets : buckets,
         conflicts  : Vec::new()
      }
   }

   /// Inserts a node in the routing table. Employs least-recently-seen eviction
   /// by kicking out the oldest node in case the bucket is full, and registering
   /// an eviction conflict that can be revised later.
   pub fn insert_node(&mut self, info : NodeInfo) {
      if let Some(index) = self.bucket_for_node(&info.key) {
         let bucket = &mut self.buckets[index];
         bucket.entries.retain(|ref stored_info| info.key != stored_info.key);
         if bucket.entries.len() == BUCKET_DEPTH {
            let evicted = bucket.entries.pop_front().unwrap();
            let conflict = EvictionConflict { evicted  : evicted,
                                              inserted : info.clone() };
            self.conflicts.push(conflict);
         }

         bucket.entries.push_back(info);
      }
   }

   /// Performs a node lookup on the routing table. The lookup result may
   /// contain the specific node, a list of up to the N closest nodes, or
   /// report that the parent node itself was requested.
   pub fn lookup(&self, key : &Hash160, n : usize) -> LookupResult {
      if *key == self.parent_key {
         return LookupResult::Myself;
      }

      match self.specific_node(key) {
         Some(info) => LookupResult::Found(info),
         None => LookupResult::ClosestNodes(self.closest_n_nodes_to(key, n)),
      }
   }

   /// Returns a table entry for the specific node with a given hash.
   fn specific_node(&self, key : &Hash160) -> Option<NodeInfo> {
      if let Some(index) = self.bucket_for_node(key){
         let bucket = &self.buckets[index];
         return bucket.entries.iter().find(|ref info| *key == info.key).cloned();
      }
      None
   }

   fn closest_n_nodes_to(&self, key : &Hash160, n : usize) -> Vec<NodeInfo>{
      let mut closest = Vec::with_capacity(n);
      // Unwrap because we shouldn't be calling this private method
      // with our own key as argument.
      let mut ideal_bucket = self.bucket_for_node(key).unwrap();

      // descending from the ideal bucket
      for bucket_index in (1..(ideal_bucket+1)).rev() {
         if closest.len() >= n {
            break;
         }
         self.pour_in_order(&mut closest, self.buckets[bucket_index].clone());
      }
      // ascending from above the ideal bucket
      for bucket_index in (ideal_bucket +1)..KEY_SIZE {
         if closest.len() >= n {
            break;
         }
         self.pour_in_order(&mut closest, self.buckets[bucket_index].clone());
      }
      closest
   }

   fn pour_in_order (&self, destination : &mut Vec<NodeInfo>, source : Bucket) {
         let mut nodes_from_bucket = source.entries.into_iter().collect::<Vec<NodeInfo>>();

         let space_left = destination.capacity() - destination.len();
         if nodes_from_bucket.len() >= space_left {
            nodes_from_bucket.sort_by_key(|ref info| &info.key ^ &self.parent_key);
            nodes_from_bucket.truncate(space_left);
         }
         destination.append(&mut nodes_from_bucket);
   }

   /// Returns the appropriate position for a node, by computing
   /// the last bit of their distance set to 1. None if we are
   /// attempting to add the parent key.
   fn bucket_for_node(&self, key : &Hash160) -> Option<usize> {
       (&self.parent_key ^ key).height()
   }

   fn revert_conflict(&mut self, conflict : EvictionConflict) {
      if let Some(index) = self.bucket_for_node(&conflict.inserted.key) {
         let bucket  = &mut self.buckets[index];
         let evictor = &mut bucket.entries.iter_mut().find(|ref info| conflict.inserted.key == info.key).unwrap();
         mem::replace::<NodeInfo>(evictor, conflict.evicted);
      }
   }
}

impl Bucket {
   pub fn new() -> Bucket {
      Bucket { entries : VecDeque::<NodeInfo>::with_capacity(BUCKET_DEPTH) }
   }

}

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

    #[test]
    fn new_routing_table_is_empty() {
       let parent_key = Hash160::random();
       let table = RoutingTable::new(parent_key);
       let test_hash = Hash160::random();

       assert!(table.specific_node(&test_hash).is_none());
    }

    #[test]
    fn inserting_and_retrieving_specific_node() {
       let node_info = NodeInfo {
          key  : Hash160::random(),
          ip   : net::IpAddr::from_str("0.0.0.0").unwrap(),
          port : 50000
       };

       let mut table = RoutingTable::new(Hash160::random());
       table.insert_node(node_info.clone());
       assert_eq!(table.specific_node(&node_info.key), Some(node_info));
    }

    impl RoutingTable {
       pub fn fill_bucket(&mut self, bucket_index : usize, fill_quantity : u8) {
          // Otherwise this helper function becomes quite complex.
          assert!(bucket_index > 7);
          let any_ip = net::IpAddr::from_str("0.0.0.0").unwrap();
          for i in 0..fill_quantity {
             let mut key = self.parent_key.clone();
             key.flip_bit(bucket_index);

             key.raw[0] = i as u8;
             let info = NodeInfo {
                key  : key,
                ip   : any_ip.clone(),
                port : 0
             };
             self.insert_node(info);
          }
       }
    }

    #[test]
    fn inserting_in_a_full_bucket_causes_eviction_conflict() {
       let mut parent_key = Hash160::blank();
       parent_key.raw[1] = 1; // This will guarantee all nodes will fall on the same bucket.
       let any_ip = net::IpAddr::from_str("0.0.0.0").unwrap();

       let mut table = RoutingTable::new(parent_key);

       table.fill_bucket(8, super::BUCKET_DEPTH as u8);
       assert!(table.conflicts.is_empty());

       // When we add another node to the same bucket, we cause a conflict.
       let mut key = Hash160::blank();
       key.raw[0] = 0xFF;
       let info = NodeInfo {
          key  : key,
          ip   : any_ip.clone(),
          port : 0
       };
       table.insert_node(info);
       assert_eq!(table.conflicts.len(), 1);

       let conflict = table.conflicts.first().unwrap();
       // We evicted the oldest node, which has a blank key.
       assert_eq!(conflict.evicted.key, Hash160::blank());
       // We inserted the newest node, but it remains referenced in the conflict.
       assert!(table.specific_node(&conflict.inserted.key).is_some());
    }

    #[test]
    fn reverting_an_eviction_conflict_reinserts_the_evicted_node_in_place_of_evictor() {
       let mut parent_key = Hash160::blank();
       parent_key.raw[1] = 1; // This will guarantee all nodes will fall on the same bucket.
       let any_ip = net::IpAddr::from_str("0.0.0.0").unwrap();

       let mut table = RoutingTable::new(parent_key);

       table.fill_bucket(8, super::BUCKET_DEPTH as u8);
       assert!(table.conflicts.is_empty());

       // When we add another node to the same bucket, we cause a conflict.
       let mut key = Hash160::blank();
       key.raw[0] = 0xFF;
       let info = NodeInfo {
          key  : key,
          ip   : any_ip.clone(),
          port : 0
       };
       table.insert_node(info);
       assert_eq!(table.conflicts.len(), 1);
       let conflict = table.conflicts.pop().unwrap();

       table.revert_conflict(conflict.clone());
       // The evictor has been removed.
       assert!(table.specific_node(&conflict.inserted.key).is_none());
       // And the evicted has been reinserted.
       assert!(table.specific_node(&conflict.evicted.key).is_some());
    }

    #[test]
    fn lookup_for_a_stored_node() {
       let mut table = RoutingTable::new(Hash160::random());
       let node = NodeInfo {
          key  : Hash160::random(),
          ip   : net::IpAddr::from_str("0.0.0.0").unwrap(),
          port : 0,
       };
       table.insert_node(node.clone());

       assert_eq!(table.lookup(&node.key, 20), LookupResult::Found(node));
    }

    #[test]
    fn lookup_for_a_not_stored_node() {
       let mut table = RoutingTable::new(Hash160::random());
       table.fill_bucket(11, 5);
       table.fill_bucket(12, 5);
       table.fill_bucket(13, 5);

       // We construct a node key that would fall in bucket 12, but isn't stored.
       let mut node_key = table.parent_key.clone();
       node_key.flip_bit(12);
       node_key.raw[0] = 0xFF;

       if let LookupResult::ClosestNodes(nodes) = table.lookup(&node_key,20) {
          assert_eq!(nodes.len(), 15);
       }
       else {
          panic!("We shouldn't have found the node!");
       }
    }
}
<commit_msg>Refactoring<commit_after>use hash::KEY_SIZE;
use hash::Hash160;
use std::net;
use std::collections::VecDeque;
use std::mem;

const K : usize = 20;
const BUCKET_DEPTH : usize = K;

/// Kademlia routing table, with 160 buckets of `BUCKET_DEPTH` (k) node
/// identifiers each.
///
/// The structure employs least-recently seen eviction. Conflicts generated
/// by evicting a node by inserting a newer one remain tracked, so they can
/// can be resolved later.
pub struct RoutingTable {
   parent_key : Hash160,
   buckets    : Vec<Bucket>,
   conflicts  : Vec<EvictionConflict>,
}

/// Bucket size is estimated to be small enough not to warrant
/// the downsides of using a linked list.
#[derive(Debug, Clone)]
struct Bucket {
   entries  : VecDeque<NodeInfo>,
}

/// Represents a conflict derived from attempting to insert a node in a full
/// bucket.
#[derive(Debug,Clone)]
struct EvictionConflict {
   evicted  : NodeInfo,
   inserted : NodeInfo
}

#[derive(Debug, Clone, PartialEq)]
pub struct NodeInfo {
   pub key  : Hash160,
   pub ip   : net::IpAddr,
   pub port : u16,
}

#[derive(Debug, PartialEq)]
pub enum LookupResult {
   Found(NodeInfo),
   ClosestNodes(Vec<NodeInfo>),
   Myself,
}

impl RoutingTable {
   pub fn new(parent_key : Hash160) -> RoutingTable {
      let mut buckets = Vec::<Bucket>::with_capacity(KEY_SIZE);
      for _ in 0..KEY_SIZE {
         buckets.push(Bucket::new());
      }

      RoutingTable {
         parent_key : parent_key,
         buckets : buckets,
         conflicts  : Vec::new()
      }
   }

   /// Inserts a node in the routing table. Employs least-recently-seen eviction
   /// by kicking out the oldest node in case the bucket is full, and registering
   /// an eviction conflict that can be revised later.
   pub fn insert_node(&mut self, info : NodeInfo) {
      if let Some(index) = self.bucket_for_node(&info.key) {
         let bucket = &mut self.buckets[index];
         bucket.entries.retain(|ref stored_info| info.key != stored_info.key);
         if bucket.entries.len() == BUCKET_DEPTH {
            let evicted = bucket.entries.pop_front().unwrap();
            let conflict = EvictionConflict { evicted  : evicted,
                                              inserted : info.clone() };
            self.conflicts.push(conflict);
         }

         bucket.entries.push_back(info);
      }
   }

   /// Performs a node lookup on the routing table. The lookup result may
   /// contain the specific node, a list of up to the N closest nodes, or
   /// report that the parent node itself was requested.
   pub fn lookup(&self, key : &Hash160, n : usize) -> LookupResult {
      if *key == self.parent_key {
         return LookupResult::Myself;
      }

      match self.specific_node(key) {
         Some(info) => LookupResult::Found(info),
         None => LookupResult::ClosestNodes(self.closest_n_nodes_to(key, n)),
      }
   }

   /// Returns a table entry for the specific node with a given hash.
   fn specific_node(&self, key : &Hash160) -> Option<NodeInfo> {
      if let Some(index) = self.bucket_for_node(key){
         let bucket = &self.buckets[index];
         return bucket.entries.iter().find(|ref info| *key == info.key).cloned();
      }
      None
   }

   fn closest_n_nodes_to(&self, key : &Hash160, n : usize) -> Vec<NodeInfo>{
      let mut closest = Vec::with_capacity(n);
      // Unwrap because we shouldn't be calling this private method
      // with our own key as argument.
      let ideal_bucket_index = self.bucket_for_node(key).unwrap();

      let descent_from_ideal = (1..(ideal_bucket_index+1)).rev();
      let ascent_from_above_ideal = (ideal_bucket_index+1)..KEY_SIZE;
      let lookup_order = descent_from_ideal.chain(ascent_from_above_ideal);

      for bucket_index in lookup_order {
         self.pour_in_order(&mut closest, self.buckets[bucket_index].clone());
         if closest.capacity() == closest.len() {
            break;
         }
      }
      closest
   }

   fn pour_in_order (&self, destination : &mut Vec<NodeInfo>, source : Bucket){
      let mut nodes_from_bucket = source.entries.into_iter().collect::<Vec<NodeInfo>>();
      nodes_from_bucket.sort_by_key(|ref info| &info.key ^ &self.parent_key);

      let space_left = destination.capacity() - destination.len();
      nodes_from_bucket.truncate(space_left);
      destination.append(&mut nodes_from_bucket);
   }

   /// Returns the appropriate position for a node, by computing
   /// the last bit of their distance set to 1. None if we are
   /// attempting to add the parent key.
   fn bucket_for_node(&self, key : &Hash160) -> Option<usize> {
       (&self.parent_key ^ key).height()
   }

   fn revert_conflict(&mut self, conflict : EvictionConflict) {
      if let Some(index) = self.bucket_for_node(&conflict.inserted.key) {
         let bucket  = &mut self.buckets[index];
         let evictor = &mut bucket.entries.iter_mut().find(|ref info| conflict.inserted.key == info.key).unwrap();
         mem::replace::<NodeInfo>(evictor, conflict.evicted);
      }
   }
}

impl Bucket {
   pub fn new() -> Bucket {
      Bucket { entries : VecDeque::<NodeInfo>::with_capacity(BUCKET_DEPTH) }
   }

}

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

    #[test]
    fn new_routing_table_is_empty() {
       let parent_key = Hash160::random();
       let table = RoutingTable::new(parent_key);
       let test_hash = Hash160::random();

       assert!(table.specific_node(&test_hash).is_none());
    }

    #[test]
    fn inserting_and_retrieving_specific_node() {
       let node_info = NodeInfo {
          key  : Hash160::random(),
          ip   : net::IpAddr::from_str("0.0.0.0").unwrap(),
          port : 50000
       };

       let mut table = RoutingTable::new(Hash160::random());
       table.insert_node(node_info.clone());
       assert_eq!(table.specific_node(&node_info.key), Some(node_info));
    }

    impl RoutingTable {
       pub fn fill_bucket(&mut self, bucket_index : usize, fill_quantity : u8) {
          // Otherwise this helper function becomes quite complex.
          assert!(bucket_index > 7);
          let any_ip = net::IpAddr::from_str("0.0.0.0").unwrap();
          for i in 0..fill_quantity {
             let mut key = self.parent_key.clone();
             key.flip_bit(bucket_index);

             key.raw[0] = i as u8;
             let info = NodeInfo {
                key  : key,
                ip   : any_ip.clone(),
                port : 0
             };
             self.insert_node(info);
          }
       }
    }

    #[test]
    fn inserting_in_a_full_bucket_causes_eviction_conflict() {
       let mut parent_key = Hash160::blank();
       parent_key.raw[1] = 1; // This will guarantee all nodes will fall on the same bucket.
       let any_ip = net::IpAddr::from_str("0.0.0.0").unwrap();

       let mut table = RoutingTable::new(parent_key);

       table.fill_bucket(8, super::BUCKET_DEPTH as u8);
       assert!(table.conflicts.is_empty());

       // When we add another node to the same bucket, we cause a conflict.
       let mut key = Hash160::blank();
       key.raw[0] = 0xFF;
       let info = NodeInfo {
          key  : key,
          ip   : any_ip.clone(),
          port : 0
       };
       table.insert_node(info);
       assert_eq!(table.conflicts.len(), 1);

       let conflict = table.conflicts.first().unwrap();
       // We evicted the oldest node, which has a blank key.
       assert_eq!(conflict.evicted.key, Hash160::blank());
       // We inserted the newest node, but it remains referenced in the conflict.
       assert!(table.specific_node(&conflict.inserted.key).is_some());
    }

    #[test]
    fn reverting_an_eviction_conflict_reinserts_the_evicted_node_in_place_of_evictor() {
       let mut parent_key = Hash160::blank();
       parent_key.raw[1] = 1; // This will guarantee all nodes will fall on the same bucket.
       let any_ip = net::IpAddr::from_str("0.0.0.0").unwrap();

       let mut table = RoutingTable::new(parent_key);

       table.fill_bucket(8, super::BUCKET_DEPTH as u8);
       assert!(table.conflicts.is_empty());

       // When we add another node to the same bucket, we cause a conflict.
       let mut key = Hash160::blank();
       key.raw[0] = 0xFF;
       let info = NodeInfo {
          key  : key,
          ip   : any_ip.clone(),
          port : 0
       };
       table.insert_node(info);
       assert_eq!(table.conflicts.len(), 1);
       let conflict = table.conflicts.pop().unwrap();

       table.revert_conflict(conflict.clone());
       // The evictor has been removed.
       assert!(table.specific_node(&conflict.inserted.key).is_none());
       // And the evicted has been reinserted.
       assert!(table.specific_node(&conflict.evicted.key).is_some());
    }

    #[test]
    fn lookup_for_a_stored_node() {
       let mut table = RoutingTable::new(Hash160::random());
       let node = NodeInfo {
          key  : Hash160::random(),
          ip   : net::IpAddr::from_str("0.0.0.0").unwrap(),
          port : 0,
       };
       table.insert_node(node.clone());

       assert_eq!(table.lookup(&node.key, 20), LookupResult::Found(node));
    }

    #[test]
    fn lookup_for_a_not_stored_node() {
       let mut table = RoutingTable::new(Hash160::random());
       table.fill_bucket(11, 5);
       table.fill_bucket(12, 5);
       table.fill_bucket(13, 5);

       // We construct a node key that would fall in bucket 12, but isn't stored.
       let mut node_key = table.parent_key.clone();
       node_key.flip_bit(12);
       node_key.raw[0] = 0xFF;

       if let LookupResult::ClosestNodes(nodes) = table.lookup(&node_key,20) {
          assert_eq!(nodes.len(), 15);
       }
       else {
          panic!("We shouldn't have found the node!");
       }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::str::from_utf8_unchecked;
use std::cmp::min;
use std::process;

use term_painter::Color::*;
use term_painter::ToStyle;

use logger;
use config::{self, Config};
use request::{Request, FormItems};
use data::Data;
use response::Responder;
use router::{Router, Route};
use catcher::{self, Catcher};
use outcome::Outcome;
use error::Error;

use http::{Method, StatusCode};
use http::hyper::{HyperRequest, FreshHyperResponse};
use http::hyper::{HyperServer, HyperHandler, HyperSetCookie, header};

/// The main `Rocket` type: used to mount routes and catchers and launch the
/// application.
pub struct Rocket {
    address: String,
    port: usize,
    router: Router,
    default_catchers: HashMap<u16, Catcher>,
    catchers: HashMap<u16, Catcher>,
}

#[doc(hidden)]
impl HyperHandler for Rocket {
    // This function tries to hide all of the Hyper-ness from Rocket. It
    // essentially converts Hyper types into Rocket types, then calls the
    // `dispatch` function, which knows nothing about Hyper. Because responding
    // depends on the `HyperResponse` type, this function does the actual
    // response processing.
    fn handle<'h, 'k>(&self,
                      hyp_req: HyperRequest<'h, 'k>,
                      mut res: FreshHyperResponse<'h>) {
        // Get all of the information from Hyper.
        let (_, h_method, h_headers, h_uri, _, h_body) = hyp_req.deconstruct();

        // Get a copy of the URI for later use.
        let uri = h_uri.to_string();

        // Try to create a Rocket request from the hyper request info.
        let mut request = match Request::new(h_method, h_headers, h_uri) {
            Ok(req) => req,
            Err(ref reason) => {
                let mock = Request::mock(Method::Get, uri.as_str());
                error!("{}: bad request ({}).", mock, reason);
                self.handle_error(StatusCode::InternalServerError, &mock, res);
                return;
            }
        };

        // Retrieve the data from the hyper body.
        let data = match Data::from_hyp(h_body) {
            Ok(data) => data,
            Err(reason) => {
                error_!("Bad data in request: {}", reason);
                self.handle_error(StatusCode::InternalServerError, &request, res);
                return;
            }
        };

        // Set the common response headers and preprocess the request.
        res.headers_mut().set(header::Server("rocket".to_string()));
        self.preprocess_request(&mut request, &data);

        // Now that we've Rocket-ized everything, actually dispath the request.
        let mut responder = match self.dispatch(&request, data) {
            Ok(responder) => responder,
            Err(code) => {
                self.handle_error(code, &request, res);
                return;
            }
        };

        // We have a responder. Update the cookies in the header.
        let cookie_delta = request.cookies().delta();
        if cookie_delta.len() > 0 {
            res.headers_mut().set(HyperSetCookie(cookie_delta));
        }

        // Actually call the responder.
        let outcome = responder.respond(res);
        info_!("{} {}", White.paint("Outcome:"), outcome);

        // Check if the responder wants to forward to a catcher. If it doesn't,
        // it's a success or failure, so we can't do any more processing.
        if let Some((code, f_res)) = outcome.forwarded() {
            self.handle_error(code, &request, f_res);
        }
    }
}

impl Rocket {
    /// Preprocess the request for Rocket-specific things. At this time, we're
    /// only checking for _method in forms.
    fn preprocess_request(&self, req: &mut Request, data: &Data) {
        // Check if this is a form and if the form contains the special _method
        // field which we use to reinterpret the request's method.
        let data_len = data.peek().len();
        let (min_len, max_len) = ("_method=get".len(), "_method=delete".len());
        if req.content_type().is_form() && data_len >= min_len {
            let form = unsafe {
                from_utf8_unchecked(&data.peek()[..min(data_len, max_len)])
            };

            let mut form_items = FormItems(form);
            if let Some(("_method", value)) = form_items.next() {
                if let Ok(method) = value.parse() {
                    req.method = method;
                }
            }
        }
    }

    /// Tries to find a `Responder` for a given `request`. It does this by
    /// routing the request and calling the handler for each matching route
    /// until one of the handlers returns success or failure. If a handler
    /// returns a failure, or there are no matching handlers willing to accept
    /// the request, this function returns an `Err` with the status code.
    #[doc(hidden)]
    pub fn dispatch<'r>(&self, request: &'r Request, mut data: Data)
            -> Result<Box<Responder + 'r>, StatusCode> {
        // Go through the list of matching routes until we fail or succeed.
        info!("{}:", request);
        let matches = self.router.route(&request);
        for route in matches {
            // Retrieve and set the requests parameters.
            info_!("Matched: {}", route);
            request.set_params(route);

            // Dispatch the request to the handler.
            let response = (route.handler)(&request, data);

            // Check if the request processing completed or if the request needs
            // to be forwarded. If it does, continue the loop to try again.
            info_!("{} {}", White.paint("Response:"), response);
            match response {
                Outcome::Success(responder) => return Ok(responder),
                Outcome::Failure(status_code) => return Err(status_code),
                Outcome::Forward(unused_data) => data = unused_data,
            };
        }

        error_!("No matching routes.");
        Err(StatusCode::NotFound)
    }

    // Attempts to send a response to the client by using the catcher for the
    // given status code. If no catcher is found (including the defaults), the
    // 500 internal server error catcher is used. If the catcher fails to
    // respond, this function returns `false`. It returns `true` if a response
    // was sucessfully sent to the client.
    #[doc(hidden)]
    pub fn handle_error<'r>(&self,
                        code: StatusCode,
                        req: &'r Request,
                        response: FreshHyperResponse) -> bool {
        // Find the catcher or use the one for internal server errors.
        let catcher = self.catchers.get(&code.to_u16()).unwrap_or_else(|| {
            error_!("No catcher found for {}.", code);
            warn_!("Using internal server error catcher.");
            self.catchers.get(&500).expect("500 catcher should exist!")
        });

        if let Some(mut responder) = catcher.handle(Error::NoRoute, req).responder() {
            if !responder.respond(response).is_success() {
                error_!("Catcher outcome was unsuccessul; aborting response.");
                return false;
            } else {
                info_!("Responded with {} catcher.", White.paint(code));
            }
        } else {
            error_!("Catcher returned an incomplete response.");
            warn_!("Using default error response.");
            let catcher = self.default_catchers.get(&code.to_u16())
                .unwrap_or(self.default_catchers.get(&500).expect("500 default"));
            let responder = catcher.handle(Error::Internal, req).responder();
            responder.unwrap().respond(response).expect("default catcher failed")
        }

        true
    }

    /// Create a new `Rocket` application using the configuration information in
    /// `Rocket.toml`. If the file does not exist or if there is an I/O error
    /// reading the file, the defaults are used. See the
    /// [config](/rocket/config/index.html) documentation for more information
    /// on defaults.
    ///
    /// This method is typically called through the `rocket::ignite` alias.
    ///
    /// # Panics
    ///
    /// If there is an error parsing the `Rocket.toml` file, this functions
    /// prints a nice error message and then exits the process.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # {
    /// rocket::ignite()
    /// # };
    /// ```
    pub fn ignite() -> Rocket {
        // Note: init() will exit the process under config errors.
        let (config, initted) = config::init();
        if initted {
            logger::init(config.log_level);
        }

        Rocket::custom(config)
    }

    /// Creates a new `Rocket` application using the supplied custom
    /// configuration information. Ignores the `Rocket.toml` file.
    ///
    /// This method is typically called through the `rocket::custom` alias.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use rocket::config::{Config, Environment};
    /// # use rocket::config::ConfigError;
    ///
    /// # fn try_config() -> Result<(), ConfigError> {
    /// let config = Config::default_for(Environment::active()?, "/custom")?
    ///     .address("1.2.3.4".into())
    ///     .port(9234);
    ///
    /// let app = rocket::custom(&config);
    /// # Ok(())
    /// # }
    /// ```
    pub fn custom(config: &Config) -> Rocket {
        logger::init(config.log_level);

        info!("🔧  Configured for {}.", config.env);
        info_!("listening: {}:{}",
               White.paint(&config.address),
               White.paint(&config.port));
        info_!("logging: {:?}", White.paint(config.log_level));
        info_!("session key: {}", White.paint(config.take_session_key().is_some()));
        for (name, value) in config.extras() {
            info_!("{} {}: {}", Yellow.paint("[extra]"), name, White.paint(value));
        }

        Rocket {
            address: config.address.clone(),
            port: config.port,
            router: Router::new(),
            default_catchers: catcher::defaults::get(),
            catchers: catcher::defaults::get(),
        }
    }

    /// Mounts all of the routes in the supplied vector at the given `base`
    /// path. Mounting a route with path `path` at path `base` makes the route
    /// available at `base/path`.
    ///
    /// # Examples
    ///
    /// Use the `routes!` macro to mount routes created using the code
    /// generation facilities. Requests to the `/hello/world` URI will be
    /// dispatched to the `hi` route.
    ///
    /// ```rust
    /// # #![feature(plugin)]
    /// # #![plugin(rocket_codegen)]
    /// # extern crate rocket;
    /// #
    /// #[get("/world")]
    /// fn hi() -> &'static str {
    ///     "Hello!"
    /// }
    ///
    /// fn main() {
    /// # if false { // We don't actually want to launch the server in an example.
    ///     rocket::ignite().mount("/hello", routes![hi])
    /// #       .launch()
    /// # }
    /// }
    /// ```
    ///
    /// Manually create a route named `hi` at path `"/world"` mounted at base
    /// `"/hello"`. Requests to the `/hello/world` URI will be dispatched to the
    /// `hi` route.
    ///
    /// ```rust
    /// use rocket::{Request, Response, Route, Data};
    /// use rocket::http::Method::*;
    ///
    /// fn hi(_: &Request, _: Data) -> Response<'static> {
    ///     Response::success("Hello!")
    /// }
    ///
    /// # if false { // We don't actually want to launch the server in an example.
    /// rocket::ignite().mount("/hello", vec![Route::new(Get, "/world", hi)])
    /// #     .launch()
    /// # }
    /// ```
    pub fn mount(mut self, base: &str, routes: Vec<Route>) -> Self {
        info!("🛰  {} '{}':", Magenta.paint("Mounting"), base);
        for mut route in routes {
            let path = format!("{}/{}", base, route.path.as_uri());
            route.set_path(path);

            info_!("{}", route);
            self.router.add(route);
        }

        self
    }

    /// Registers all of the catchers in the supplied vector.
    pub fn catch(mut self, catchers: Vec<Catcher>) -> Self {
        info!("👾  {}:", Magenta.paint("Catchers"));
        for c in catchers {
            if self.catchers.get(&c.code).map_or(false, |e| !e.is_default()) {
                let msg = "(warning: duplicate catcher!)";
                info_!("{} {}", c, Yellow.paint(msg));
            } else {
                info_!("{}", c);
            }

            self.catchers.insert(c.code, c);
        }

        self
    }

    /// Starts the application server and begins listening for and dispatching
    /// requests to mounted routes and catchers.
    ///
    /// # Panics
    ///
    /// If the server could not be started, this method prints the reason and
    /// then exits the process.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # if false {
    /// rocket::ignite().launch()
    /// # }
    /// ```
    pub fn launch(self) {
        if self.router.has_collisions() {
            warn!("Route collisions detected!");
        }

        let full_addr = format!("{}:{}", self.address, self.port);
        let server = match HyperServer::http(full_addr.as_str()) {
            Ok(hyper_server) => hyper_server,
            Err(e) => {
                error!("failed to start server.");
                error_!("{}", e);
                process::exit(1);
            }
        };

        info!("🚀  {} {}...",
              White.paint("Rocket has launched from"),
              White.bold().paint(&full_addr));

        server.handle(self).unwrap();
    }
}
<commit_msg>Add an example to Rocket::catch docs.<commit_after>use std::collections::HashMap;
use std::str::from_utf8_unchecked;
use std::cmp::min;
use std::process;

use term_painter::Color::*;
use term_painter::ToStyle;

use logger;
use config::{self, Config};
use request::{Request, FormItems};
use data::Data;
use response::Responder;
use router::{Router, Route};
use catcher::{self, Catcher};
use outcome::Outcome;
use error::Error;

use http::{Method, StatusCode};
use http::hyper::{HyperRequest, FreshHyperResponse};
use http::hyper::{HyperServer, HyperHandler, HyperSetCookie, header};

/// The main `Rocket` type: used to mount routes and catchers and launch the
/// application.
pub struct Rocket {
    address: String,
    port: usize,
    router: Router,
    default_catchers: HashMap<u16, Catcher>,
    catchers: HashMap<u16, Catcher>,
}

#[doc(hidden)]
impl HyperHandler for Rocket {
    // This function tries to hide all of the Hyper-ness from Rocket. It
    // essentially converts Hyper types into Rocket types, then calls the
    // `dispatch` function, which knows nothing about Hyper. Because responding
    // depends on the `HyperResponse` type, this function does the actual
    // response processing.
    fn handle<'h, 'k>(&self,
                      hyp_req: HyperRequest<'h, 'k>,
                      mut res: FreshHyperResponse<'h>) {
        // Get all of the information from Hyper.
        let (_, h_method, h_headers, h_uri, _, h_body) = hyp_req.deconstruct();

        // Get a copy of the URI for later use.
        let uri = h_uri.to_string();

        // Try to create a Rocket request from the hyper request info.
        let mut request = match Request::new(h_method, h_headers, h_uri) {
            Ok(req) => req,
            Err(ref reason) => {
                let mock = Request::mock(Method::Get, uri.as_str());
                error!("{}: bad request ({}).", mock, reason);
                self.handle_error(StatusCode::InternalServerError, &mock, res);
                return;
            }
        };

        // Retrieve the data from the hyper body.
        let data = match Data::from_hyp(h_body) {
            Ok(data) => data,
            Err(reason) => {
                error_!("Bad data in request: {}", reason);
                self.handle_error(StatusCode::InternalServerError, &request, res);
                return;
            }
        };

        // Set the common response headers and preprocess the request.
        res.headers_mut().set(header::Server("rocket".to_string()));
        self.preprocess_request(&mut request, &data);

        // Now that we've Rocket-ized everything, actually dispath the request.
        let mut responder = match self.dispatch(&request, data) {
            Ok(responder) => responder,
            Err(code) => {
                self.handle_error(code, &request, res);
                return;
            }
        };

        // We have a responder. Update the cookies in the header.
        let cookie_delta = request.cookies().delta();
        if cookie_delta.len() > 0 {
            res.headers_mut().set(HyperSetCookie(cookie_delta));
        }

        // Actually call the responder.
        let outcome = responder.respond(res);
        info_!("{} {}", White.paint("Outcome:"), outcome);

        // Check if the responder wants to forward to a catcher. If it doesn't,
        // it's a success or failure, so we can't do any more processing.
        if let Some((code, f_res)) = outcome.forwarded() {
            self.handle_error(code, &request, f_res);
        }
    }
}

impl Rocket {
    /// Preprocess the request for Rocket-specific things. At this time, we're
    /// only checking for _method in forms.
    fn preprocess_request(&self, req: &mut Request, data: &Data) {
        // Check if this is a form and if the form contains the special _method
        // field which we use to reinterpret the request's method.
        let data_len = data.peek().len();
        let (min_len, max_len) = ("_method=get".len(), "_method=delete".len());
        if req.content_type().is_form() && data_len >= min_len {
            let form = unsafe {
                from_utf8_unchecked(&data.peek()[..min(data_len, max_len)])
            };

            let mut form_items = FormItems(form);
            if let Some(("_method", value)) = form_items.next() {
                if let Ok(method) = value.parse() {
                    req.method = method;
                }
            }
        }
    }

    /// Tries to find a `Responder` for a given `request`. It does this by
    /// routing the request and calling the handler for each matching route
    /// until one of the handlers returns success or failure. If a handler
    /// returns a failure, or there are no matching handlers willing to accept
    /// the request, this function returns an `Err` with the status code.
    #[doc(hidden)]
    pub fn dispatch<'r>(&self, request: &'r Request, mut data: Data)
            -> Result<Box<Responder + 'r>, StatusCode> {
        // Go through the list of matching routes until we fail or succeed.
        info!("{}:", request);
        let matches = self.router.route(&request);
        for route in matches {
            // Retrieve and set the requests parameters.
            info_!("Matched: {}", route);
            request.set_params(route);

            // Dispatch the request to the handler.
            let response = (route.handler)(&request, data);

            // Check if the request processing completed or if the request needs
            // to be forwarded. If it does, continue the loop to try again.
            info_!("{} {}", White.paint("Response:"), response);
            match response {
                Outcome::Success(responder) => return Ok(responder),
                Outcome::Failure(status_code) => return Err(status_code),
                Outcome::Forward(unused_data) => data = unused_data,
            };
        }

        error_!("No matching routes.");
        Err(StatusCode::NotFound)
    }

    // Attempts to send a response to the client by using the catcher for the
    // given status code. If no catcher is found (including the defaults), the
    // 500 internal server error catcher is used. If the catcher fails to
    // respond, this function returns `false`. It returns `true` if a response
    // was sucessfully sent to the client.
    #[doc(hidden)]
    pub fn handle_error<'r>(&self,
                        code: StatusCode,
                        req: &'r Request,
                        response: FreshHyperResponse) -> bool {
        // Find the catcher or use the one for internal server errors.
        let catcher = self.catchers.get(&code.to_u16()).unwrap_or_else(|| {
            error_!("No catcher found for {}.", code);
            warn_!("Using internal server error catcher.");
            self.catchers.get(&500).expect("500 catcher should exist!")
        });

        if let Some(mut responder) = catcher.handle(Error::NoRoute, req).responder() {
            if !responder.respond(response).is_success() {
                error_!("Catcher outcome was unsuccessul; aborting response.");
                return false;
            } else {
                info_!("Responded with {} catcher.", White.paint(code));
            }
        } else {
            error_!("Catcher returned an incomplete response.");
            warn_!("Using default error response.");
            let catcher = self.default_catchers.get(&code.to_u16())
                .unwrap_or(self.default_catchers.get(&500).expect("500 default"));
            let responder = catcher.handle(Error::Internal, req).responder();
            responder.unwrap().respond(response).expect("default catcher failed")
        }

        true
    }

    /// Create a new `Rocket` application using the configuration information in
    /// `Rocket.toml`. If the file does not exist or if there is an I/O error
    /// reading the file, the defaults are used. See the
    /// [config](/rocket/config/index.html) documentation for more information
    /// on defaults.
    ///
    /// This method is typically called through the `rocket::ignite` alias.
    ///
    /// # Panics
    ///
    /// If there is an error parsing the `Rocket.toml` file, this functions
    /// prints a nice error message and then exits the process.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # {
    /// rocket::ignite()
    /// # };
    /// ```
    pub fn ignite() -> Rocket {
        // Note: init() will exit the process under config errors.
        let (config, initted) = config::init();
        if initted {
            logger::init(config.log_level);
        }

        Rocket::custom(config)
    }

    /// Creates a new `Rocket` application using the supplied custom
    /// configuration information. Ignores the `Rocket.toml` file.
    ///
    /// This method is typically called through the `rocket::custom` alias.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use rocket::config::{Config, Environment};
    /// # use rocket::config::ConfigError;
    ///
    /// # fn try_config() -> Result<(), ConfigError> {
    /// let config = Config::default_for(Environment::active()?, "/custom")?
    ///     .address("1.2.3.4".into())
    ///     .port(9234);
    ///
    /// let app = rocket::custom(&config);
    /// # Ok(())
    /// # }
    /// ```
    pub fn custom(config: &Config) -> Rocket {
        logger::init(config.log_level);

        info!("🔧  Configured for {}.", config.env);
        info_!("listening: {}:{}",
               White.paint(&config.address),
               White.paint(&config.port));
        info_!("logging: {:?}", White.paint(config.log_level));
        info_!("session key: {}", White.paint(config.take_session_key().is_some()));
        for (name, value) in config.extras() {
            info_!("{} {}: {}", Yellow.paint("[extra]"), name, White.paint(value));
        }

        Rocket {
            address: config.address.clone(),
            port: config.port,
            router: Router::new(),
            default_catchers: catcher::defaults::get(),
            catchers: catcher::defaults::get(),
        }
    }

    /// Mounts all of the routes in the supplied vector at the given `base`
    /// path. Mounting a route with path `path` at path `base` makes the route
    /// available at `base/path`.
    ///
    /// # Examples
    ///
    /// Use the `routes!` macro to mount routes created using the code
    /// generation facilities. Requests to the `/hello/world` URI will be
    /// dispatched to the `hi` route.
    ///
    /// ```rust
    /// # #![feature(plugin)]
    /// # #![plugin(rocket_codegen)]
    /// # extern crate rocket;
    /// #
    /// #[get("/world")]
    /// fn hi() -> &'static str {
    ///     "Hello!"
    /// }
    ///
    /// fn main() {
    /// # if false { // We don't actually want to launch the server in an example.
    ///     rocket::ignite().mount("/hello", routes![hi])
    /// #       .launch()
    /// # }
    /// }
    /// ```
    ///
    /// Manually create a route named `hi` at path `"/world"` mounted at base
    /// `"/hello"`. Requests to the `/hello/world` URI will be dispatched to the
    /// `hi` route.
    ///
    /// ```rust
    /// use rocket::{Request, Response, Route, Data};
    /// use rocket::http::Method::*;
    ///
    /// fn hi(_: &Request, _: Data) -> Response<'static> {
    ///     Response::success("Hello!")
    /// }
    ///
    /// # if false { // We don't actually want to launch the server in an example.
    /// rocket::ignite().mount("/hello", vec![Route::new(Get, "/world", hi)])
    /// #     .launch()
    /// # }
    /// ```
    pub fn mount(mut self, base: &str, routes: Vec<Route>) -> Self {
        info!("🛰  {} '{}':", Magenta.paint("Mounting"), base);
        for mut route in routes {
            let path = format!("{}/{}", base, route.path.as_uri());
            route.set_path(path);

            info_!("{}", route);
            self.router.add(route);
        }

        self
    }

    /// Registers all of the catchers in the supplied vector.
    ///
    /// # Examples
    ///
    /// ```rust
    /// #![feature(plugin)]
    /// #![plugin(rocket_codegen)]
    ///
    /// extern crate rocket;
    ///
    /// use rocket::Request;
    ///
    /// #[error(500)]
    /// fn internal_error() -> &'static str {
    ///     "Whoops! Looks like we messed up."
    /// }
    ///
    /// #[error(400)]
    /// fn not_found(req: &Request) -> String {
    ///     format!("I couldn't find '{}'. Try something else?", req.uri())
    /// }
    ///
    /// fn main() {
    /// # if false { // We don't actually want to launch the server in an example.
    ///     rocket::ignite().catch(errors![internal_error, not_found])
    /// #       .launch()
    /// # }
    /// }
    /// ```
    pub fn catch(mut self, catchers: Vec<Catcher>) -> Self {
        info!("👾  {}:", Magenta.paint("Catchers"));
        for c in catchers {
            if self.catchers.get(&c.code).map_or(false, |e| !e.is_default()) {
                let msg = "(warning: duplicate catcher!)";
                info_!("{} {}", c, Yellow.paint(msg));
            } else {
                info_!("{}", c);
            }

            self.catchers.insert(c.code, c);
        }

        self
    }

    /// Starts the application server and begins listening for and dispatching
    /// requests to mounted routes and catchers.
    ///
    /// # Panics
    ///
    /// If the server could not be started, this method prints the reason and
    /// then exits the process.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # if false {
    /// rocket::ignite().launch()
    /// # }
    /// ```
    pub fn launch(self) {
        if self.router.has_collisions() {
            warn!("Route collisions detected!");
        }

        let full_addr = format!("{}:{}", self.address, self.port);
        let server = match HyperServer::http(full_addr.as_str()) {
            Ok(hyper_server) => hyper_server,
            Err(e) => {
                error!("failed to start server.");
                error_!("{}", e);
                process::exit(1);
            }
        };

        info!("🚀  {} {}...",
              White.paint("Rocket has launched from"),
              White.bold().paint(&full_addr));

        server.handle(self).unwrap();
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#[doc(hidden)];

use libc::c_char;

// Some basic logging
macro_rules! rtdebug_ (
    ($( $arg:expr),+) => ( {
        dumb_println(fmt!( $($arg),+ ));

        fn dumb_println(s: &str) {
            use io::WriterUtil;
            let dbg = ::libc::STDERR_FILENO as ::io::fd_t;
            dbg.write_str(s);
            dbg.write_str("\n");
        }

    } )
)

// An alternate version with no output, for turning off logging
macro_rules! rtdebug (
    ($( $arg:expr),+) => ( $(let _ = $arg)*; )
)

#[path = "sched/mod.rs"]
mod sched;
mod rtio;
pub mod uvll;
mod uvio;
#[path = "uv/mod.rs"]
mod uv;
#[path = "io/mod.rs"]
mod io;
// FIXME #5248: The import in `sched` doesn't resolve unless this is pub!
pub mod thread_local_storage;
mod work_queue;
mod stack;
mod context;
mod thread;
pub mod env;

/// Tools for testing the runtime
#[cfg(test)]
pub mod test;

#[cfg(stage0)]
pub fn start(main: *u8, _argc: int, _argv: *c_char, _crate_map: *u8) -> int {
    use self::sched::{Scheduler, Task};
    use self::uvio::UvEventLoop;

    let loop_ = ~UvEventLoop::new();
    let mut sched = ~Scheduler::new(loop_);
    let main_task = ~do Task::new(&mut sched.stack_pool) {
        // XXX: Can't call a C function pointer from Rust yet
        unsafe { rust_call_nullary_fn(main) };
    };
    sched.task_queue.push_back(main_task);
    sched.run();
    return 0;

    extern {
        fn rust_call_nullary_fn(f: *u8);
    }
}

#[cfg(not(stage0))]
pub fn start(main: *u8, _argc: int, _argv: **c_char, _crate_map: *u8) -> int {
    use self::sched::{Scheduler, Task};
    use self::uvio::UvEventLoop;

    let loop_ = ~UvEventLoop::new();
    let mut sched = ~Scheduler::new(loop_);
    let main_task = ~do Task::new(&mut sched.stack_pool) {
        // XXX: Can't call a C function pointer from Rust yet
        unsafe { rust_call_nullary_fn(main) };
    };
    sched.task_queue.push_back(main_task);
    sched.run();
    return 0;

    extern {
        fn rust_call_nullary_fn(f: *u8);
    }
}

/// Possible contexts in which Rust code may be executing.
/// Different runtime services are available depending on context.
#[deriving(Eq)]
pub enum RuntimeContext {
    // Only default services, e.g. exchange heap
    GlobalContext,
    // The scheduler may be accessed
    SchedulerContext,
    // Full task services, e.g. local heap, unwinding
    TaskContext,
    // Running in an old-style task
    OldTaskContext
}

pub fn context() -> RuntimeContext {

    use task::rt::rust_task;
    use self::sched::local_sched;

    // XXX: Hitting TLS twice to check if the scheduler exists
    // then to check for the task is not good for perf
    if unsafe { rust_try_get_task().is_not_null() } {
        return OldTaskContext;
    } else {
        if local_sched::exists() {
            let context = ::cell::empty_cell();
            do local_sched::borrow |sched| {
                if sched.in_task_context() {
                    context.put_back(TaskContext);
                } else {
                    context.put_back(SchedulerContext);
                }
            }
            return context.take();
        } else {
            return GlobalContext;
        }
    }

    pub extern {
        #[rust_stack]
        fn rust_try_get_task() -> *rust_task;
    }
}

#[test]
fn test_context() {
    use unstable::run_in_bare_thread;
    use self::sched::{local_sched, Task};
    use self::uvio::UvEventLoop;
    use cell::Cell;

    assert!(context() == OldTaskContext);
    do run_in_bare_thread {
        assert!(context() == GlobalContext);
        let mut sched = ~UvEventLoop::new_scheduler();
        let task = ~do Task::new(&mut sched.stack_pool) {
            assert!(context() == TaskContext);
            let sched = local_sched::take();
            do sched.deschedule_running_task_and_then() |task| {
                assert!(context() == SchedulerContext);
                let task = Cell(task);
                do local_sched::borrow |sched| {
                    sched.task_queue.push_back(task.take());
                }
            }
        };
        sched.task_queue.push_back(task);
        sched.run();
    }
}

// For setting up tests of the new scheduler
#[cfg(test)]
pub fn run_in_newsched_task(f: ~fn()) {
    use cell::Cell;
    use unstable::run_in_bare_thread;
    use self::sched::Task;
    use self::uvio::UvEventLoop;

    let f = Cell(Cell(f));

    do run_in_bare_thread {
        let mut sched = ~UvEventLoop::new_scheduler();
        let f = f.take();
        let task = ~do Task::new(&mut sched.stack_pool) {
            (f.take())();
        };
        sched.task_queue.push_back(task);
        sched.run();
    }
}
<commit_msg>core::rt: Remove redundant copy of run_in_newsched_task<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#[doc(hidden)];

use libc::c_char;

// Some basic logging
macro_rules! rtdebug_ (
    ($( $arg:expr),+) => ( {
        dumb_println(fmt!( $($arg),+ ));

        fn dumb_println(s: &str) {
            use io::WriterUtil;
            let dbg = ::libc::STDERR_FILENO as ::io::fd_t;
            dbg.write_str(s);
            dbg.write_str("\n");
        }

    } )
)

// An alternate version with no output, for turning off logging
macro_rules! rtdebug (
    ($( $arg:expr),+) => ( $(let _ = $arg)*; )
)

#[path = "sched/mod.rs"]
mod sched;
mod rtio;
pub mod uvll;
mod uvio;
#[path = "uv/mod.rs"]
mod uv;
#[path = "io/mod.rs"]
mod io;
// FIXME #5248: The import in `sched` doesn't resolve unless this is pub!
pub mod thread_local_storage;
mod work_queue;
mod stack;
mod context;
mod thread;
pub mod env;

/// Tools for testing the runtime
#[cfg(test)]
pub mod test;

#[cfg(stage0)]
pub fn start(main: *u8, _argc: int, _argv: *c_char, _crate_map: *u8) -> int {
    use self::sched::{Scheduler, Task};
    use self::uvio::UvEventLoop;

    let loop_ = ~UvEventLoop::new();
    let mut sched = ~Scheduler::new(loop_);
    let main_task = ~do Task::new(&mut sched.stack_pool) {
        // XXX: Can't call a C function pointer from Rust yet
        unsafe { rust_call_nullary_fn(main) };
    };
    sched.task_queue.push_back(main_task);
    sched.run();
    return 0;

    extern {
        fn rust_call_nullary_fn(f: *u8);
    }
}

#[cfg(not(stage0))]
pub fn start(main: *u8, _argc: int, _argv: **c_char, _crate_map: *u8) -> int {
    use self::sched::{Scheduler, Task};
    use self::uvio::UvEventLoop;

    let loop_ = ~UvEventLoop::new();
    let mut sched = ~Scheduler::new(loop_);
    let main_task = ~do Task::new(&mut sched.stack_pool) {
        // XXX: Can't call a C function pointer from Rust yet
        unsafe { rust_call_nullary_fn(main) };
    };
    sched.task_queue.push_back(main_task);
    sched.run();
    return 0;

    extern {
        fn rust_call_nullary_fn(f: *u8);
    }
}

/// Possible contexts in which Rust code may be executing.
/// Different runtime services are available depending on context.
#[deriving(Eq)]
pub enum RuntimeContext {
    // Only default services, e.g. exchange heap
    GlobalContext,
    // The scheduler may be accessed
    SchedulerContext,
    // Full task services, e.g. local heap, unwinding
    TaskContext,
    // Running in an old-style task
    OldTaskContext
}

pub fn context() -> RuntimeContext {

    use task::rt::rust_task;
    use self::sched::local_sched;

    // XXX: Hitting TLS twice to check if the scheduler exists
    // then to check for the task is not good for perf
    if unsafe { rust_try_get_task().is_not_null() } {
        return OldTaskContext;
    } else {
        if local_sched::exists() {
            let context = ::cell::empty_cell();
            do local_sched::borrow |sched| {
                if sched.in_task_context() {
                    context.put_back(TaskContext);
                } else {
                    context.put_back(SchedulerContext);
                }
            }
            return context.take();
        } else {
            return GlobalContext;
        }
    }

    pub extern {
        #[rust_stack]
        fn rust_try_get_task() -> *rust_task;
    }
}

#[test]
fn test_context() {
    use unstable::run_in_bare_thread;
    use self::sched::{local_sched, Task};
    use self::uvio::UvEventLoop;
    use cell::Cell;

    assert!(context() == OldTaskContext);
    do run_in_bare_thread {
        assert!(context() == GlobalContext);
        let mut sched = ~UvEventLoop::new_scheduler();
        let task = ~do Task::new(&mut sched.stack_pool) {
            assert!(context() == TaskContext);
            let sched = local_sched::take();
            do sched.deschedule_running_task_and_then() |task| {
                assert!(context() == SchedulerContext);
                let task = Cell(task);
                do local_sched::borrow |sched| {
                    sched.task_queue.push_back(task.take());
                }
            }
        };
        sched.task_queue.push_back(task);
        sched.run();
    }
}
<|endoftext|>


<commit_before>use platform::{Chip, Platform, MPU, SysTick};
use process;
use process::Process;
use common::Queue;
use syscall;

pub unsafe fn do_process<P: Platform, C: Chip>(platform: &mut P, chip: &mut C, process: &mut Process,
                  appid: ::AppId) {
    let systick = chip.systick();
    systick.reset();
    systick.set_timer(10000);
    systick.enable(true);

    loop {
        if chip.has_pending_interrupts() ||
                systick.overflowed() || systick.value() <= 500 {
            break;
        }

        match process.state {
            process::State::Running => {
                let (data_start, data_len, text_start, text_len) =
                        process.memory_regions();
                // Data segment read/write/execute
                chip.mpu().set_mpu(
                    0, data_start as u32, data_len as u32, true, 0b011);
                // Text segment read/execute (no write)
                chip.mpu().set_mpu(
                    1, text_start as u32, text_len as u32, true, 0b111);
                systick.enable(true);
                process.switch_to();
                systick.enable(false);
            }
            process::State::Waiting => {
                match process.callbacks.dequeue() {
                    None => { break },
                    Some(cb) => {
                        process.state = process::State::Running;
                        process.push_callback(cb);
                        continue;
                    }
                }
            }
        }

        if !process.syscall_fired() {
            break;
        }

        match process.svc_number() {
            Some(syscall::MEMOP) => {
                let brk_type = process.r0();
                let r1 = process.r1();

                let res = match brk_type {
                    0 /* BRK */ => {
                        process.brk(r1 as *const u8)
                            .map(|_| 0).unwrap_or(-1)
                    },
                    1 /* SBRK */ => {
                        process.sbrk(r1 as isize)
                            .map(|addr| addr as isize).unwrap_or(-1)
                    },
                    _ => -2
                };
                process.set_r0(res);
            },
            Some(syscall::WAIT) => {
                process.state = process::State::Waiting;
                process.pop_syscall_stack();

                // There might be already enqueued callbacks
                continue;
            },
            Some(syscall::SUBSCRIBE) => {
                let driver_num = process.r0();
                let subdriver_num = process.r1();
                let callback_ptr = process.r2() as *mut ();
                let appdata = process.r3();

                let res = platform.with_driver(driver_num, |driver| {
                    let callback =
                        ::Callback::new(appid, appdata, callback_ptr);
                    match driver {
                        Some(d) => d.subscribe(subdriver_num,
                                               callback),
                        None => -1
                    }
                });
                process.set_r0(res);
            },
            Some(syscall::COMMAND) => {
                let res = platform.with_driver(process.r0(), |driver| {
                    match driver {
                        Some(d) => d.command(process.r1(),
                                             process.r2(),
                                             appid),
                        None => -1
                    }
                });
                process.set_r0(res);
            },
            Some(syscall::ALLOW) => {
                let res = platform.with_driver(process.r0(), |driver| {
                    match driver {
                        Some(d) => {
                            let start_addr = process.r2() as *mut u8;
                            let size = process.r3();
                            if process.in_exposed_bounds(start_addr, size) {
                                let slice = ::AppSlice::new(start_addr as *mut u8, size, appid);
                                d.allow(appid, process.r1(), slice)
                            } else {
                                -1
                            }
                        },
                        None => -1
                    }
                });
                process.set_r0(res);
            }
            _ => {}
        }
    }
    systick.reset();
}
<commit_msg>Call `service_pending_interrupts` much more often<commit_after>use platform::{Chip, Platform, MPU, SysTick};
use process;
use process::Process;
use common::Queue;
use syscall;

pub unsafe fn do_process<P: Platform, C: Chip>(platform: &mut P, chip: &mut C, process: &mut Process,
                  appid: ::AppId) {
    chip.systick().reset();
    chip.systick().set_timer(10000);
    chip.systick().enable(true);

    loop {
        chip.service_pending_interrupts();
        if chip.systick().overflowed() || chip.systick().value() <= 500 {
            break;
        }

        match process.state {
            process::State::Running => {
                let (data_start, data_len, text_start, text_len) =
                        process.memory_regions();
                // Data segment read/write/execute
                chip.mpu().set_mpu(
                    0, data_start as u32, data_len as u32, true, 0b011);
                // Text segment read/execute (no write)
                chip.mpu().set_mpu(
                    1, text_start as u32, text_len as u32, true, 0b111);

                chip.service_pending_interrupts();

                chip.systick().enable(true);
                process.switch_to();
                chip.systick().enable(false);

                chip.service_pending_interrupts();
            }
            process::State::Waiting => {
                match process.callbacks.dequeue() {
                    None => { break },
                    Some(cb) => {
                        process.state = process::State::Running;
                        process.push_callback(cb);
                        continue;
                    }
                }
            }
        }

        if !process.syscall_fired() {
            break;
        }

        match process.svc_number() {
            Some(syscall::MEMOP) => {
                let brk_type = process.r0();
                let r1 = process.r1();

                let res = match brk_type {
                    0 /* BRK */ => {
                        process.brk(r1 as *const u8)
                            .map(|_| 0).unwrap_or(-1)
                    },
                    1 /* SBRK */ => {
                        process.sbrk(r1 as isize)
                            .map(|addr| addr as isize).unwrap_or(-1)
                    },
                    _ => -2
                };
                process.set_r0(res);
            },
            Some(syscall::WAIT) => {
                process.state = process::State::Waiting;
                process.pop_syscall_stack();

                // There might be already enqueued callbacks
                continue;
            },
            Some(syscall::SUBSCRIBE) => {
                let driver_num = process.r0();
                let subdriver_num = process.r1();
                let callback_ptr = process.r2() as *mut ();
                let appdata = process.r3();

                let res = platform.with_driver(driver_num, |driver| {
                    let callback =
                        ::Callback::new(appid, appdata, callback_ptr);
                    match driver {
                        Some(d) => d.subscribe(subdriver_num,
                                               callback),
                        None => -1
                    }
                });
                process.set_r0(res);
            },
            Some(syscall::COMMAND) => {
                let res = platform.with_driver(process.r0(), |driver| {
                    match driver {
                        Some(d) => d.command(process.r1(),
                                             process.r2(),
                                             appid),
                        None => -1
                    }
                });
                process.set_r0(res);
            },
            Some(syscall::ALLOW) => {
                let res = platform.with_driver(process.r0(), |driver| {
                    match driver {
                        Some(d) => {
                            let start_addr = process.r2() as *mut u8;
                            let size = process.r3();
                            if process.in_exposed_bounds(start_addr, size) {
                                let slice = ::AppSlice::new(start_addr as *mut u8, size, appid);
                                d.allow(appid, process.r1(), slice)
                            } else {
                                -1
                            }
                        },
                        None => -1
                    }
                });
                process.set_r0(res);
            }
            _ => {}
        }
    }
    chip.systick().reset();
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use std::{mem, ptr};
use ffi;
use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, PythonObjectWithTypeObject, PythonObjectDowncastError};
use objects::PyType;
use err::PyResult;

/// Represents a reference to a Python object.
///
/// Python objects are reference counted.
/// Calling `clone_ref()` on a `PyObject` will return a new reference to the same object
/// (thus incrementing the reference count).
/// The `Drop` implementation will automatically decrement the reference count.
/// You can also call `release_ref()` to explicitly decrement the reference count.
/// This is slightly faster than relying on automatic drop, because `release_ref`
/// does not need to check whether the GIL needs to be acquired.
///
/// `PyObject` can be used with all Python objects, since all python types
/// derive from `object`. This crate also contains other, more specific types
/// that serve as references to Python objects (e.g. `PyTuple` for Python tuples, etc.).
///
/// You can convert from any Python object to `PyObject` by calling `as_object()` or `into_object()`
/// from the [PythonObject trait](trait.PythonObject.html).
/// In the other direction, you can call `cast_as()` or `cast_into()`
/// on `PyObject` to convert to more specific object types.
///
/// Most of the interesting methods are provided by the [ObjectProtocol trait](trait.ObjectProtocol.html).
#[repr(C)]
pub struct PyObject {
    // PyObject owns one reference to the *PyObject
    // ptr is not null
    #[cfg(feature="nightly")]
    ptr: ptr::Shared<ffi::PyObject>,
    #[cfg(not(feature="nightly"))]
    ptr: *mut ffi::PyObject,
}

// PyObject is thread-safe, because all operations on it require a Python<'p> token.
unsafe impl Send for PyObject {}
unsafe impl Sync for PyObject {}

/// Dropping a `PyObject` decrements the reference count on the object by 1.
impl Drop for PyObject {
    fn drop(&mut self) {
        let _gil_guard = Python::acquire_gil();
        unsafe { ffi::Py_DECREF(unpack_shared(self.ptr)); }
    }
}

#[inline]
#[cfg(feature="nightly")]
unsafe fn make_shared(ptr: *mut ffi::PyObject) -> ptr::Shared<ffi::PyObject> {
    ptr::Shared::new(ptr)
}

#[inline]
#[cfg(not(feature="nightly"))]
unsafe fn make_shared(ptr: *mut ffi::PyObject) -> *mut ffi::PyObject {
    ptr
}

#[inline]
#[cfg(feature="nightly")]
fn unpack_shared(ptr: ptr::Shared<ffi::PyObject>) -> *mut ffi::PyObject {
    ptr.as_ptr()
}

#[inline]
#[cfg(not(feature="nightly"))]
fn unpack_shared(ptr: *mut ffi::PyObject) -> *mut ffi::PyObject {
    ptr
}

impl PythonObject for PyObject {
    #[inline]
    fn as_object(&self) -> &PyObject {
        self
    }

    #[inline]
    fn into_object(self) -> PyObject {
        self
    }

    #[inline]
    unsafe fn unchecked_downcast_from(o: PyObject) -> PyObject {
        o
    }

    #[inline]
    unsafe fn unchecked_downcast_borrow_from(o: &PyObject) -> &PyObject {
        o
    }
}

impl PythonObjectWithCheckedDowncast for PyObject {
    #[inline]
    fn downcast_from<'p>(_py: Python<'p>, obj: PyObject) -> Result<PyObject, PythonObjectDowncastError<'p>> {
        Ok(obj)
    }

    #[inline]
    fn downcast_borrow_from<'a, 'p>(_py: Python<'p>, obj: &'a PyObject) -> Result<&'a PyObject, PythonObjectDowncastError<'p>> {
        Ok(obj)
    }
}

impl PythonObjectWithTypeObject for PyObject {
    #[inline]
    fn type_object(py: Python) -> PyType {
        unsafe { PyType::from_type_ptr(py, &mut ffi::PyBaseObject_Type) }
    }
}

impl PyObject {
    /// Creates a PyObject instance for the given FFI pointer.
    /// This moves ownership over the pointer into the PyObject.
    /// Undefined behavior if the pointer is NULL or invalid.
    #[inline]
    pub unsafe fn from_owned_ptr(_py: Python, ptr: *mut ffi::PyObject) -> PyObject {
        debug_assert!(!ptr.is_null() && ffi::Py_REFCNT(ptr) > 0);
        PyObject { ptr: make_shared(ptr) }
    }

    /// Creates a PyObject instance for the given FFI pointer.
    /// Calls Py_INCREF() on the ptr.
    /// Undefined behavior if the pointer is NULL or invalid.
    #[inline]
    pub unsafe fn from_borrowed_ptr(_py : Python, ptr : *mut ffi::PyObject) -> PyObject {
        debug_assert!(!ptr.is_null() && ffi::Py_REFCNT(ptr) > 0);
        ffi::Py_INCREF(ptr);
        PyObject { ptr: make_shared(ptr) }
    }

    /// Creates a PyObject instance for the given FFI pointer.
    /// This moves ownership over the pointer into the PyObject.
    /// Returns None for null pointers; undefined behavior if the pointer is invalid.
    #[inline]
    pub unsafe fn from_owned_ptr_opt(py: Python, ptr: *mut ffi::PyObject) -> Option<PyObject> {
        if ptr.is_null() {
            None
        } else {
            Some(PyObject::from_owned_ptr(py, ptr))
        }
    }

    /// Returns None for null pointers; undefined behavior if the pointer is invalid.
    #[inline]
    pub unsafe fn from_borrowed_ptr_opt(py: Python, ptr: *mut ffi::PyObject) -> Option<PyObject> {
        if ptr.is_null() {
            None
        } else {
            Some(PyObject::from_borrowed_ptr(py, ptr))
        }
    }

    /// Gets the underlying FFI pointer.
    /// Returns a borrowed pointer.
    #[inline]
    pub fn as_ptr(&self) -> *mut ffi::PyObject {
        unpack_shared(self.ptr)
    }

    /// Gets the underlying FFI pointer.
    /// Consumes `self` without calling `Py_DECREF()`, thus returning an owned pointer.
    #[inline]
    #[must_use]
    pub fn steal_ptr(self) -> *mut ffi::PyObject {
        let ptr = self.as_ptr();
        mem::forget(self);
        ptr
    }

    /// Transmutes an FFI pointer to `&PyObject`.
    /// Undefined behavior if the pointer is NULL or invalid.
    #[inline]
    pub unsafe fn borrow_from_ptr<'a>(ptr : &'a *mut ffi::PyObject) -> &'a PyObject {
        debug_assert!(!ptr.is_null());
        mem::transmute(ptr)
    }

    /// Transmutes a slice of owned FFI pointers to `&[PyObject]`.
    /// Undefined behavior if any pointer in the slice is NULL or invalid.
    #[inline]
    pub unsafe fn borrow_from_owned_ptr_slice<'a>(ptr : &'a [*mut ffi::PyObject]) -> &'a [PyObject] {
        mem::transmute(ptr)
    }

    /// Gets the reference count of this Python object.
    #[inline]
    pub fn get_refcnt(&self, _py: Python) -> usize {
        unsafe { ffi::Py_REFCNT(self.as_ptr()) as usize }
    }

    /// Gets the Python type object for this object's type.
    pub fn get_type(&self, py: Python) -> PyType {
        unsafe {
            PyType::from_type_ptr(py, (*self.as_ptr()).ob_type)
        }
    }

    /// Casts the PyObject to a concrete Python object type.
    /// Causes undefined behavior if the object is not of the expected type.
    /// This is a wrapper function around `PythonObject::unchecked_downcast_from()`.
    #[inline]
    pub unsafe fn unchecked_cast_into<T>(self) -> T
        where T: PythonObject
    {
        PythonObject::unchecked_downcast_from(self)
    }

    /// Casts the PyObject to a concrete Python object type.
    /// Fails with `PythonObjectDowncastError` if the object is not of the expected type.
    /// This is a wrapper function around `PythonObjectWithCheckedDowncast::downcast_from()`.
    #[inline]
    pub fn cast_into<'p, T>(self, py: Python<'p>) -> Result<T, PythonObjectDowncastError<'p>>
        where T: PythonObjectWithCheckedDowncast
    {
        PythonObjectWithCheckedDowncast::downcast_from(py, self)
    }

    /// Casts the PyObject to a concrete Python object type.
    /// Causes undefined behavior if the object is not of the expected type.
    /// This is a wrapper function around `PythonObject::unchecked_downcast_borrow_from()`.
    #[inline]
    pub unsafe fn unchecked_cast_as<'s, T>(&'s self) -> &'s T
        where T: PythonObject
    {
        PythonObject::unchecked_downcast_borrow_from(self)
    }

    /// Casts the PyObject to a concrete Python object type.
    /// Fails with `PythonObjectDowncastError` if the object is not of the expected type.
    /// This is a wrapper function around `PythonObjectWithCheckedDowncast::downcast_borrow_from()`.
    #[inline]
    pub fn cast_as<'s, 'p, T>(&'s self, py: Python<'p>) -> Result<&'s T, PythonObjectDowncastError<'p>>
        where T: PythonObjectWithCheckedDowncast
    {
        PythonObjectWithCheckedDowncast::downcast_borrow_from(py, self)
    }

    /// Extracts some type from the Python object.
    /// This is a wrapper function around `FromPyObject::from_py_object()`.
    #[inline]
    pub fn extract<'a, T>(&'a self, py: Python) -> PyResult<T>
        where T: ::conversion::FromPyObject<'a>
    {
        ::conversion::FromPyObject::extract(py, self)
    }
}

/// PyObject implements the `==` operator using reference equality:
/// `obj1 == obj2` in rust is equivalent to `obj1 is obj2` in Python.
impl PartialEq for PyObject {
    #[inline]
    fn eq(&self, o : &PyObject) -> bool {
        self.as_ptr() == o.as_ptr()
    }
}

/// PyObject implements the `==` operator using reference equality:
/// `obj1 == obj2` in rust is equivalent to `obj1 is obj2` in Python.
impl Eq for PyObject { }

#[test]
fn test_sizeof() {
    // should be a static_assert, but size_of is not a compile-time const
    // these are necessary for the transmutes in this module
    assert_eq!(mem::size_of::<PyObject>(), mem::size_of::<*mut ffi::PyObject>());
    assert_eq!(mem::size_of::<PyType>(), mem::size_of::<*mut ffi::PyTypeObject>());
}

<commit_msg>Update make_shared for latest nightly<commit_after>// Copyright (c) 2015 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use std::{mem, ptr};
use ffi;
use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, PythonObjectWithTypeObject, PythonObjectDowncastError};
use objects::PyType;
use err::PyResult;

/// Represents a reference to a Python object.
///
/// Python objects are reference counted.
/// Calling `clone_ref()` on a `PyObject` will return a new reference to the same object
/// (thus incrementing the reference count).
/// The `Drop` implementation will automatically decrement the reference count.
/// You can also call `release_ref()` to explicitly decrement the reference count.
/// This is slightly faster than relying on automatic drop, because `release_ref`
/// does not need to check whether the GIL needs to be acquired.
///
/// `PyObject` can be used with all Python objects, since all python types
/// derive from `object`. This crate also contains other, more specific types
/// that serve as references to Python objects (e.g. `PyTuple` for Python tuples, etc.).
///
/// You can convert from any Python object to `PyObject` by calling `as_object()` or `into_object()`
/// from the [PythonObject trait](trait.PythonObject.html).
/// In the other direction, you can call `cast_as()` or `cast_into()`
/// on `PyObject` to convert to more specific object types.
///
/// Most of the interesting methods are provided by the [ObjectProtocol trait](trait.ObjectProtocol.html).
#[repr(C)]
pub struct PyObject {
    // PyObject owns one reference to the *PyObject
    // ptr is not null
    #[cfg(feature="nightly")]
    ptr: ptr::Shared<ffi::PyObject>,
    #[cfg(not(feature="nightly"))]
    ptr: *mut ffi::PyObject,
}

// PyObject is thread-safe, because all operations on it require a Python<'p> token.
unsafe impl Send for PyObject {}
unsafe impl Sync for PyObject {}

/// Dropping a `PyObject` decrements the reference count on the object by 1.
impl Drop for PyObject {
    fn drop(&mut self) {
        let _gil_guard = Python::acquire_gil();
        unsafe { ffi::Py_DECREF(unpack_shared(self.ptr)); }
    }
}

#[inline]
#[cfg(feature="nightly")]
unsafe fn make_shared(ptr: *mut ffi::PyObject) -> ptr::Shared<ffi::PyObject> {
    ptr::Shared::new(ptr).expect("ptr should not be null")
}

#[inline]
#[cfg(not(feature="nightly"))]
unsafe fn make_shared(ptr: *mut ffi::PyObject) -> *mut ffi::PyObject {
    ptr
}

#[inline]
#[cfg(feature="nightly")]
fn unpack_shared(ptr: ptr::Shared<ffi::PyObject>) -> *mut ffi::PyObject {
    ptr.as_ptr()
}

#[inline]
#[cfg(not(feature="nightly"))]
fn unpack_shared(ptr: *mut ffi::PyObject) -> *mut ffi::PyObject {
    ptr
}

impl PythonObject for PyObject {
    #[inline]
    fn as_object(&self) -> &PyObject {
        self
    }

    #[inline]
    fn into_object(self) -> PyObject {
        self
    }

    #[inline]
    unsafe fn unchecked_downcast_from(o: PyObject) -> PyObject {
        o
    }

    #[inline]
    unsafe fn unchecked_downcast_borrow_from(o: &PyObject) -> &PyObject {
        o
    }
}

impl PythonObjectWithCheckedDowncast for PyObject {
    #[inline]
    fn downcast_from<'p>(_py: Python<'p>, obj: PyObject) -> Result<PyObject, PythonObjectDowncastError<'p>> {
        Ok(obj)
    }

    #[inline]
    fn downcast_borrow_from<'a, 'p>(_py: Python<'p>, obj: &'a PyObject) -> Result<&'a PyObject, PythonObjectDowncastError<'p>> {
        Ok(obj)
    }
}

impl PythonObjectWithTypeObject for PyObject {
    #[inline]
    fn type_object(py: Python) -> PyType {
        unsafe { PyType::from_type_ptr(py, &mut ffi::PyBaseObject_Type) }
    }
}

impl PyObject {
    /// Creates a PyObject instance for the given FFI pointer.
    /// This moves ownership over the pointer into the PyObject.
    /// Undefined behavior if the pointer is NULL or invalid.
    #[inline]
    pub unsafe fn from_owned_ptr(_py: Python, ptr: *mut ffi::PyObject) -> PyObject {
        debug_assert!(!ptr.is_null() && ffi::Py_REFCNT(ptr) > 0);
        PyObject { ptr: make_shared(ptr) }
    }

    /// Creates a PyObject instance for the given FFI pointer.
    /// Calls Py_INCREF() on the ptr.
    /// Undefined behavior if the pointer is NULL or invalid.
    #[inline]
    pub unsafe fn from_borrowed_ptr(_py : Python, ptr : *mut ffi::PyObject) -> PyObject {
        debug_assert!(!ptr.is_null() && ffi::Py_REFCNT(ptr) > 0);
        ffi::Py_INCREF(ptr);
        PyObject { ptr: make_shared(ptr) }
    }

    /// Creates a PyObject instance for the given FFI pointer.
    /// This moves ownership over the pointer into the PyObject.
    /// Returns None for null pointers; undefined behavior if the pointer is invalid.
    #[inline]
    pub unsafe fn from_owned_ptr_opt(py: Python, ptr: *mut ffi::PyObject) -> Option<PyObject> {
        if ptr.is_null() {
            None
        } else {
            Some(PyObject::from_owned_ptr(py, ptr))
        }
    }

    /// Returns None for null pointers; undefined behavior if the pointer is invalid.
    #[inline]
    pub unsafe fn from_borrowed_ptr_opt(py: Python, ptr: *mut ffi::PyObject) -> Option<PyObject> {
        if ptr.is_null() {
            None
        } else {
            Some(PyObject::from_borrowed_ptr(py, ptr))
        }
    }

    /// Gets the underlying FFI pointer.
    /// Returns a borrowed pointer.
    #[inline]
    pub fn as_ptr(&self) -> *mut ffi::PyObject {
        unpack_shared(self.ptr)
    }

    /// Gets the underlying FFI pointer.
    /// Consumes `self` without calling `Py_DECREF()`, thus returning an owned pointer.
    #[inline]
    #[must_use]
    pub fn steal_ptr(self) -> *mut ffi::PyObject {
        let ptr = self.as_ptr();
        mem::forget(self);
        ptr
    }

    /// Transmutes an FFI pointer to `&PyObject`.
    /// Undefined behavior if the pointer is NULL or invalid.
    #[inline]
    pub unsafe fn borrow_from_ptr<'a>(ptr : &'a *mut ffi::PyObject) -> &'a PyObject {
        debug_assert!(!ptr.is_null());
        mem::transmute(ptr)
    }

    /// Transmutes a slice of owned FFI pointers to `&[PyObject]`.
    /// Undefined behavior if any pointer in the slice is NULL or invalid.
    #[inline]
    pub unsafe fn borrow_from_owned_ptr_slice<'a>(ptr : &'a [*mut ffi::PyObject]) -> &'a [PyObject] {
        mem::transmute(ptr)
    }

    /// Gets the reference count of this Python object.
    #[inline]
    pub fn get_refcnt(&self, _py: Python) -> usize {
        unsafe { ffi::Py_REFCNT(self.as_ptr()) as usize }
    }

    /// Gets the Python type object for this object's type.
    pub fn get_type(&self, py: Python) -> PyType {
        unsafe {
            PyType::from_type_ptr(py, (*self.as_ptr()).ob_type)
        }
    }

    /// Casts the PyObject to a concrete Python object type.
    /// Causes undefined behavior if the object is not of the expected type.
    /// This is a wrapper function around `PythonObject::unchecked_downcast_from()`.
    #[inline]
    pub unsafe fn unchecked_cast_into<T>(self) -> T
        where T: PythonObject
    {
        PythonObject::unchecked_downcast_from(self)
    }

    /// Casts the PyObject to a concrete Python object type.
    /// Fails with `PythonObjectDowncastError` if the object is not of the expected type.
    /// This is a wrapper function around `PythonObjectWithCheckedDowncast::downcast_from()`.
    #[inline]
    pub fn cast_into<'p, T>(self, py: Python<'p>) -> Result<T, PythonObjectDowncastError<'p>>
        where T: PythonObjectWithCheckedDowncast
    {
        PythonObjectWithCheckedDowncast::downcast_from(py, self)
    }

    /// Casts the PyObject to a concrete Python object type.
    /// Causes undefined behavior if the object is not of the expected type.
    /// This is a wrapper function around `PythonObject::unchecked_downcast_borrow_from()`.
    #[inline]
    pub unsafe fn unchecked_cast_as<'s, T>(&'s self) -> &'s T
        where T: PythonObject
    {
        PythonObject::unchecked_downcast_borrow_from(self)
    }

    /// Casts the PyObject to a concrete Python object type.
    /// Fails with `PythonObjectDowncastError` if the object is not of the expected type.
    /// This is a wrapper function around `PythonObjectWithCheckedDowncast::downcast_borrow_from()`.
    #[inline]
    pub fn cast_as<'s, 'p, T>(&'s self, py: Python<'p>) -> Result<&'s T, PythonObjectDowncastError<'p>>
        where T: PythonObjectWithCheckedDowncast
    {
        PythonObjectWithCheckedDowncast::downcast_borrow_from(py, self)
    }

    /// Extracts some type from the Python object.
    /// This is a wrapper function around `FromPyObject::from_py_object()`.
    #[inline]
    pub fn extract<'a, T>(&'a self, py: Python) -> PyResult<T>
        where T: ::conversion::FromPyObject<'a>
    {
        ::conversion::FromPyObject::extract(py, self)
    }
}

/// PyObject implements the `==` operator using reference equality:
/// `obj1 == obj2` in rust is equivalent to `obj1 is obj2` in Python.
impl PartialEq for PyObject {
    #[inline]
    fn eq(&self, o : &PyObject) -> bool {
        self.as_ptr() == o.as_ptr()
    }
}

/// PyObject implements the `==` operator using reference equality:
/// `obj1 == obj2` in rust is equivalent to `obj1 is obj2` in Python.
impl Eq for PyObject { }

#[test]
fn test_sizeof() {
    // should be a static_assert, but size_of is not a compile-time const
    // these are necessary for the transmutes in this module
    assert_eq!(mem::size_of::<PyObject>(), mem::size_of::<*mut ffi::PyObject>());
    assert_eq!(mem::size_of::<PyType>(), mem::size_of::<*mut ffi::PyTypeObject>());
}

<|endoftext|>


<commit_before>use glob;

use fs::DotFilter;
use fs::filter::{FileFilter, SortField, SortCase, IgnorePatterns};

use options::{flags, Misfire};
use options::parser::Matches;

impl FileFilter {

    /// Determines the set of file filter options to use, based on the user’s
    /// command-line arguments.
    pub fn deduce(matches: &Matches) -> Result<FileFilter, Misfire> {
        Ok(FileFilter {
            list_dirs_first: matches.has(&flags::DIRS_FIRST),
            reverse:         matches.has(&flags::REVERSE),
            sort_field:      SortField::deduce(matches)?,
            dot_filter:      DotFilter::deduce(matches)?,
            ignore_patterns: IgnorePatterns::deduce(matches)?,
        })
    }
}

impl Default for SortField {
    fn default() -> SortField {
        SortField::Name(SortCase::Sensitive)
    }
}

impl SortField {

    /// Determine the sort field to use, based on the presence of a “sort”
    /// argument. This will return `Err` if the option is there, but does not
    /// correspond to a valid field.
    fn deduce(matches: &Matches) -> Result<SortField, Misfire> {

        const SORTS: &[&str] = &[ "name", "Name", "size", "extension",
                                  "Extension", "modified", "accessed",
                                  "created", "inode", "type", "none" ];

        let word = match matches.get(&flags::SORT) {
            Some(w)  => w,
            None     => return Ok(SortField::default()),
        };

        if word == "name" || word == "filename" {
            Ok(SortField::Name(SortCase::Sensitive))
        }
        else if word == "Name" || word == "Filename" {
            Ok(SortField::Name(SortCase::Insensitive))
        }
        else if word == "size" || word == "filesize" {
            Ok(SortField::Size)
        }
        else if word == "ext" || word == "extension" {
            Ok(SortField::Extension(SortCase::Sensitive))
        }
        else if word == "Ext" || word == "Extension" {
            Ok(SortField::Extension(SortCase::Insensitive))
        }
        else if word == "mod" || word == "modified" {
            Ok(SortField::ModifiedDate)
        }
        else if word == "acc" || word == "accessed" {
            Ok(SortField::AccessedDate)
        }
        else if word == "cr" || word == "created" {
            Ok(SortField::CreatedDate)
        }
        else if word == "inode" {
            Ok(SortField::FileInode)
        }
        else if word == "type" {
            Ok(SortField::FileType)
        }
        else if word == "none" {
            Ok(SortField::Unsorted)
        }
        else {
            Err(Misfire::bad_argument(&flags::SORT, word, SORTS))
        }
    }
}

impl DotFilter {
    pub fn deduce(matches: &Matches) -> Result<DotFilter, Misfire> {
        let dots = match matches.count(&flags::ALL) {
            0 => return Ok(DotFilter::JustFiles),
            1 => DotFilter::Dotfiles,
            _ => DotFilter::DotfilesAndDots,
        };

        if matches.has(&flags::TREE) {
            Err(Misfire::TreeAllAll)
        }
        else {
            Ok(dots)
        }
    }
}

impl IgnorePatterns {

    /// Determines the set of file filter options to use, based on the user’s
    /// command-line arguments.
    pub fn deduce(matches: &Matches) -> Result<IgnorePatterns, Misfire> {
        let patterns = match matches.get(&flags::IGNORE_GLOB) {
            None => Ok(Vec::new()),
            Some(is) => is.to_string_lossy().split('|').map(|a| glob::Pattern::new(a)).collect(),
        }?;

        // TODO: is to_string_lossy really the best way to handle
        // invalid UTF-8 there?

        Ok(IgnorePatterns { patterns })
    }
}
<commit_msg>Add tests for sort field<commit_after>use glob;

use fs::DotFilter;
use fs::filter::{FileFilter, SortField, SortCase, IgnorePatterns};

use options::{flags, Misfire};
use options::parser::Matches;

impl FileFilter {

    /// Determines the set of file filter options to use, based on the user’s
    /// command-line arguments.
    pub fn deduce(matches: &Matches) -> Result<FileFilter, Misfire> {
        Ok(FileFilter {
            list_dirs_first: matches.has(&flags::DIRS_FIRST),
            reverse:         matches.has(&flags::REVERSE),
            sort_field:      SortField::deduce(matches)?,
            dot_filter:      DotFilter::deduce(matches)?,
            ignore_patterns: IgnorePatterns::deduce(matches)?,
        })
    }
}

impl Default for SortField {
    fn default() -> SortField {
        SortField::Name(SortCase::Sensitive)
    }
}

const SORTS: &[&str] = &[ "name", "Name", "size", "extension",
                          "Extension", "modified", "accessed",
                          "created", "inode", "type", "none" ];

impl SortField {

    /// Determine the sort field to use, based on the presence of a “sort”
    /// argument. This will return `Err` if the option is there, but does not
    /// correspond to a valid field.
    fn deduce(matches: &Matches) -> Result<SortField, Misfire> {
        let word = match matches.get(&flags::SORT) {
            Some(w)  => w,
            None     => return Ok(SortField::default()),
        };

        if word == "name" || word == "filename" {
            Ok(SortField::Name(SortCase::Sensitive))
        }
        else if word == "Name" || word == "Filename" {
            Ok(SortField::Name(SortCase::Insensitive))
        }
        else if word == "size" || word == "filesize" {
            Ok(SortField::Size)
        }
        else if word == "ext" || word == "extension" {
            Ok(SortField::Extension(SortCase::Sensitive))
        }
        else if word == "Ext" || word == "Extension" {
            Ok(SortField::Extension(SortCase::Insensitive))
        }
        else if word == "mod" || word == "modified" {
            Ok(SortField::ModifiedDate)
        }
        else if word == "acc" || word == "accessed" {
            Ok(SortField::AccessedDate)
        }
        else if word == "cr" || word == "created" {
            Ok(SortField::CreatedDate)
        }
        else if word == "inode" {
            Ok(SortField::FileInode)
        }
        else if word == "type" {
            Ok(SortField::FileType)
        }
        else if word == "none" {
            Ok(SortField::Unsorted)
        }
        else {
            Err(Misfire::bad_argument(&flags::SORT, word, SORTS))
        }
    }
}

impl DotFilter {
    pub fn deduce(matches: &Matches) -> Result<DotFilter, Misfire> {
        let dots = match matches.count(&flags::ALL) {
            0 => return Ok(DotFilter::JustFiles),
            1 => DotFilter::Dotfiles,
            _ => DotFilter::DotfilesAndDots,
        };

        if matches.has(&flags::TREE) {
            Err(Misfire::TreeAllAll)
        }
        else {
            Ok(dots)
        }
    }
}

impl IgnorePatterns {

    /// Determines the set of file filter options to use, based on the user’s
    /// command-line arguments.
    pub fn deduce(matches: &Matches) -> Result<IgnorePatterns, Misfire> {
        let patterns = match matches.get(&flags::IGNORE_GLOB) {
            None => Ok(Vec::new()),
            Some(is) => is.to_string_lossy().split('|').map(|a| glob::Pattern::new(a)).collect(),
        }?;

        // TODO: is to_string_lossy really the best way to handle
        // invalid UTF-8 there?

        Ok(IgnorePatterns { patterns })
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::ffi::OsString;
    use options::flags;

    pub fn os(input: &'static str) -> OsString {
        let mut os = OsString::new();
        os.push(input);
        os
    }

    macro_rules! test {
        ($name:ident: $type:ident <- $inputs:expr => $result:expr) => {
            #[test]
            fn $name() {
                use options::parser::{parse, Args, Arg};
                use std::ffi::OsString;

                static TEST_ARGS: &[&Arg] = &[ &flags::SORT ];

                let bits = $inputs.as_ref().into_iter().map(|&o| os(o)).collect::<Vec<OsString>>();
                let results = parse(&Args(TEST_ARGS), bits.iter());
                assert_eq!($type::deduce(results.as_ref().unwrap()), $result);
            }
        };
    }

    mod sort_fields {
        use super::*;

        // Default behaviour
        test!(empty:         SortField <- []                  => Ok(SortField::default()));

        // Sort field arguments
        test!(one_arg:       SortField <- ["--sort=cr"]       => Ok(SortField::CreatedDate));
        test!(one_long:      SortField <- ["--sort=size"]     => Ok(SortField::Size));
        test!(one_short:     SortField <- ["-saccessed"]      => Ok(SortField::AccessedDate));
        test!(lowercase:     SortField <- ["--sort", "name"]  => Ok(SortField::Name(SortCase::Sensitive)));
        test!(uppercase:     SortField <- ["--sort", "Name"]  => Ok(SortField::Name(SortCase::Insensitive)));

        // Errors
        test!(error:         SortField <- ["--sort=colour"]   => Err(Misfire::bad_argument(&flags::SORT, &os("colour"), super::SORTS)));

        // Overriding
        test!(overridden:    SortField <- ["--sort=cr",       "--sort", "mod"]     => Ok(SortField::ModifiedDate));
        test!(overridden_2:  SortField <- ["--sort", "none",  "--sort=Extension"]  => Ok(SortField::Extension(SortCase::Insensitive)));
    }
}
<|endoftext|>