<commit_before>use redox::Vec;

pub struct AvlNode<T> {
    value: T,
    left: Option<AvlNodeId>, // ID for left node
    right: Option<AvlNodeId>, // ID for right node
}

#[derive(Copy, Clone)]
pub struct AvlNodeId {
    index: usize,
    time_stamp: u64,
}

impl AvlNodeId {
    pub fn get<'a, T>(&self, avl: &'a Avl<T>) -> &'a AvlNode<T> {
        let ref slot = avl.nodes[self.index];
        if slot.time_stamp == self.time_stamp {
            slot.node.as_ref().unwrap()
        } else {
            panic!("AvlNodeId had invalid time_stamp");
        }
    }

    pub fn try_get<'a, T>(&self, avl: &'a Avl<T>) -> Option<&'a AvlNode<T>> {
        avl.nodes
           .get(self.index)
           .and_then(|slot| {
               if slot.time_stamp == self.time_stamp {
                   slot.node.as_ref()
               } else {
                   None
               }
           })
    }

    pub fn get_mut<'a, T>(&self, avl: &'a mut Avl<T>) -> &'a mut AvlNode<T> {
        let ref mut slot = avl.nodes[self.index];
        if slot.time_stamp == self.time_stamp {
            slot.node.as_mut().unwrap()
        } else {
            panic!("AvlNodeId had invalid time_stamp");
        }
    }

    pub fn try_get_mut<'a, T>(&self, avl: &'a mut Avl<T>) -> Option<&'a mut AvlNode<T>> {
        avl.nodes
           .get_mut(self.index)
           .and_then(|slot| {
               if slot.time_stamp == self.time_stamp {
                   slot.node.as_mut()
               } else {
                   None
               }
           })
    }
}

pub struct Avl<T> {
    root: usize, // Index of the root node
    nodes: Vec<AvlSlot<T>>,
    free_list: Vec<usize>,
}

impl<T> Avl<T> {
    pub fn new() -> Self {
        Avl {
            root: 0,
            nodes: Vec::new(),
            free_list: Vec::new(),
        }
    }

    pub fn insert(&mut self, value: T) -> AvlNodeId {
        // TODO this is just a placeholder, we need to deal with all the fancy rotation stuff that
        // AVL trees do
        self.allocate_node(value)
    }

    // Performs a left rotation on a tree/subtree.
    // Returns the replace the specified node with
    fn rotate_left(&mut self, node: AvlNodeId) -> AvlNodeId {
        // Keep track of the original node positions
        // For a rotate left, the right child node must exist
        let r = node.get(self).right.unwrap();
        let rl = r.get(self).left;

        let ret = r;
        node.get_mut(self).right = rl;
        ret.get_mut(self).left = Some(node);

        ret
    }

    // Performs a right rotation on a tree/subtree.
    // Returns the replace the specified node with
    fn rotate_right(&mut self, node: AvlNodeId) -> AvlNodeId {
        // Keep track of the original node positions
        // For a rotate right, the left child node must exist
        let l = node.get(self).left.unwrap();
        let lr = l.get(self).right;

        let ret = l;
        node.get_mut(self).left = lr;
        ret.get_mut(self).right = Some(node);

        ret
    }

    // performs a left-right double rotation on a tree/subtree.
    fn rotate_leftright(&mut self, node: AvlNodeId) -> AvlNodeId {
        let l = node.get(self).left.unwrap();
        let new_l = self.rotate_left(l); // Left node needs to exist
        node.get_mut(self).left = Some(new_l);
        self.rotate_right(node)
    }

    // performs a right-left double rotation on a tree/subtree.
    fn rotate_rightleft(&mut self, node: AvlNodeId) -> AvlNodeId {
        let r = node.get(self).right.unwrap();
        let new_r = self.rotate_right(r); // Right node needs to exist
        node.get_mut(self).right = Some(new_r);
        self.rotate_left(node)
    }

    // _ins is the implementation of the binary tree insert function. Lesser values will be stored on
    // the left, while greater values will be stored on the right. No duplicates are allowed.
    /*fn _ins(&mut self, node_index: Option<AvlNodeId>, value: T) -> AvlNodeId {
        let node =
            match node_index {
                Some(node) => {
                    // Node exists, check which way to branch.
                    if n == node->val {
                        return node;
                    else if (n < node->val)
                        _ins(n, node->left);
                    else if (n > node->val)
                        _ins(n, node->right);
                },
                None => {
                    // The node doesn't exist, create it here.
                    self.allocate_node(value)
                },
            };

        rebalance(node);
    }*/

    // _rebalance rebalances the provided node
    /*fn rebalance(node: AvlNodeId) {
        if !node {
            return;
        }

        let balance = self.height(node.get(self).left) - self.height(node.right);
        if balance == 2 { // left
            let lbalance = self.height(node.left.left) - self.height(node.left.right);
            if lbalance == 0 || lbalance == 1 { // left left - need to rotate right
                rotate_right(node);
            } else if lbalance == -1 { // left right
                rotate_leftright(node); // function name is just a coincidence
            }
        }
        else if balance == -2 { // right
            let rbalance = _height(node->right->left) - _height(node->right->right);
            if rbalance == 1 { // right left
                rotate_rightleft(node); // function name is just a coincidence
            } else if (rbalance == 0 || rbalance == -1) { // right right - need to rotate left
                rotate_left(node);
            }
        }
    }*/

    // height gets the height of a tree or subtree
    fn height(&self, node: Option<AvlNodeId>) -> i64 {
        match node {
            Some(node) => {
                let left_height = self.height(node.get(self).left);
                let right_height = self.height(node.get(self).right);

                if left_height > right_height {
                    left_height+1
                } else {
                    right_height+1
                }
            },
            None => { -1 },
        }
    }

    fn allocate_node(&mut self, value: T) -> AvlNodeId {
        match self.free_list.pop() {
            Some(index) => {
                AvlNodeId { time_stamp: self.nodes[index].time_stamp+1, index: index }
            },
            None => {
                // No free slots, create a new one
                let id = AvlNodeId { index: self.nodes.len(), time_stamp: 0 };
                self.nodes.push(AvlSlot { time_stamp: 0,
                                          node: Some(AvlNode { value: value, left: None, right: None }) });
                id
            },
        }
    }

    fn free_node(&mut self, id: AvlNodeId) -> AvlNode<T> {
        self.free_list.push(id.index);

        // NOTE: We unwrap here, because we trust that `id` points to a valid node, because
        // only we can create and free AvlNodes and their AvlNodeIds
        self.nodes[id.index].node.take().unwrap()
    }
}

struct AvlSlot<T> {
    time_stamp: u64,
    node: Option<AvlNode<T>>,
}
<commit_msg>AvlTree::rebalance compiles<commit_after>use redox::Vec;

pub struct AvlNode<T> {
    value: T,
    left: Option<AvlNodeId>, // ID for left node
    right: Option<AvlNodeId>, // ID for right node
}

#[derive(Copy, Clone)]
pub struct AvlNodeId {
    index: usize,
    time_stamp: u64,
}

impl AvlNodeId {
    pub fn get<'a, T>(&self, avl: &'a Avl<T>) -> &'a AvlNode<T> {
        let ref slot = avl.nodes[self.index];
        if slot.time_stamp == self.time_stamp {
            slot.node.as_ref().unwrap()
        } else {
            panic!("AvlNodeId had invalid time_stamp");
        }
    }

    pub fn try_get<'a, T>(&self, avl: &'a Avl<T>) -> Option<&'a AvlNode<T>> {
        avl.nodes
           .get(self.index)
           .and_then(|slot| {
               if slot.time_stamp == self.time_stamp {
                   slot.node.as_ref()
               } else {
                   None
               }
           })
    }

    pub fn get_mut<'a, T>(&self, avl: &'a mut Avl<T>) -> &'a mut AvlNode<T> {
        let ref mut slot = avl.nodes[self.index];
        if slot.time_stamp == self.time_stamp {
            slot.node.as_mut().unwrap()
        } else {
            panic!("AvlNodeId had invalid time_stamp");
        }
    }

    pub fn try_get_mut<'a, T>(&self, avl: &'a mut Avl<T>) -> Option<&'a mut AvlNode<T>> {
        avl.nodes
           .get_mut(self.index)
           .and_then(|slot| {
               if slot.time_stamp == self.time_stamp {
                   slot.node.as_mut()
               } else {
                   None
               }
           })
    }
}

pub struct Avl<T> {
    root: usize, // Index of the root node
    nodes: Vec<AvlSlot<T>>,
    free_list: Vec<usize>,
}

impl<T> Avl<T> {
    pub fn new() -> Self {
        Avl {
            root: 0,
            nodes: Vec::new(),
            free_list: Vec::new(),
        }
    }

    pub fn insert(&mut self, value: T) -> AvlNodeId {
        // TODO this is just a placeholder, we need to deal with all the fancy rotation stuff that
        // AVL trees do
        self.allocate_node(value)
    }

    // Performs a left rotation on a tree/subtree.
    // Returns the replace the specified node with
    fn rotate_left(&mut self, node: AvlNodeId) -> AvlNodeId {
        // Keep track of the original node positions
        // For a rotate left, the right child node must exist
        let r = node.get(self).right.unwrap();
        let rl = r.get(self).left;

        let ret = r;
        node.get_mut(self).right = rl;
        ret.get_mut(self).left = Some(node);

        ret
    }

    // Performs a right rotation on a tree/subtree.
    // Returns the replace the specified node with
    fn rotate_right(&mut self, node: AvlNodeId) -> AvlNodeId {
        // Keep track of the original node positions
        // For a rotate right, the left child node must exist
        let l = node.get(self).left.unwrap();
        let lr = l.get(self).right;

        let ret = l;
        node.get_mut(self).left = lr;
        ret.get_mut(self).right = Some(node);

        ret
    }

    // performs a left-right double rotation on a tree/subtree.
    fn rotate_leftright(&mut self, node: AvlNodeId) -> AvlNodeId {
        let l = node.get(self).left.unwrap();
        let new_l = self.rotate_left(l); // Left node needs to exist
        node.get_mut(self).left = Some(new_l);
        self.rotate_right(node)
    }

    // performs a right-left double rotation on a tree/subtree.
    fn rotate_rightleft(&mut self, node: AvlNodeId) -> AvlNodeId {
        let r = node.get(self).right.unwrap();
        let new_r = self.rotate_right(r); // Right node needs to exist
        node.get_mut(self).right = Some(new_r);
        self.rotate_left(node)
    }

    // _ins is the implementation of the binary tree insert function. Lesser values will be stored on
    // the left, while greater values will be stored on the right. No duplicates are allowed.
    /*fn _ins(&mut self, node_index: Option<AvlNodeId>, value: T) -> AvlNodeId {
        let node =
            match node_index {
                Some(node) => {
                    // Node exists, check which way to branch.
                    if n == node->val {
                        return node;
                    else if (n < node->val)
                        _ins(n, node->left);
                    else if (n > node->val)
                        _ins(n, node->right);
                },
                None => {
                    // The node doesn't exist, create it here.
                    self.allocate_node(value)
                },
            };

        rebalance(node)
    }*/

    // _rebalance rebalances the provided node
    fn rebalance(&mut self, node: AvlNodeId) -> AvlNodeId {
        let balance = self.height(node.get(self).left) - self.height(node.get(self).right);
        if balance == 2 { // left
            let lbalance = self.height(node.get(self).left.unwrap().get(self).left) -
                           self.height(node.get(self).left.unwrap().get(self).right);
            if lbalance == 0 || lbalance == 1 { // left left - need to rotate right
                return self.rotate_right(node);
            } else if lbalance == -1 { // left right
                return self.rotate_leftright(node); // function name is just a coincidence
            }
        } else if balance == -2 { // right
            let rbalance = self.height(node.get(self).right.unwrap().get(self).left) -
                           self.height(node.get(self).right.unwrap().get(self).right);
            if rbalance == 1 { // right left
                return self.rotate_rightleft(node); // function name is just a coincidence
            } else if rbalance == 0 || rbalance == -1 { // right right - need to rotate left
                return self.rotate_left(node);
            }
        }

        node
    }

    // height gets the height of a tree or subtree
    fn height(&self, node: Option<AvlNodeId>) -> i64 {
        match node {
            Some(node) => {
                let left_height = self.height(node.get(self).left);
                let right_height = self.height(node.get(self).right);

                if left_height > right_height {
                    left_height+1
                } else {
                    right_height+1
                }
            },
            None => { -1 },
        }
    }

    fn allocate_node(&mut self, value: T) -> AvlNodeId {
        match self.free_list.pop() {
            Some(index) => {
                AvlNodeId { time_stamp: self.nodes[index].time_stamp+1, index: index }
            },
            None => {
                // No free slots, create a new one
                let id = AvlNodeId { index: self.nodes.len(), time_stamp: 0 };
                self.nodes.push(AvlSlot { time_stamp: 0,
                                          node: Some(AvlNode { value: value, left: None, right: None }) });
                id
            },
        }
    }

    fn free_node(&mut self, id: AvlNodeId) -> AvlNode<T> {
        self.free_list.push(id.index);

        // NOTE: We unwrap here, because we trust that `id` points to a valid node, because
        // only we can create and free AvlNodes and their AvlNodeIds
        self.nodes[id.index].node.take().unwrap()
    }
}

struct AvlSlot<T> {
    time_stamp: u64,
    node: Option<AvlNode<T>>,
}
<|endoftext|>


<commit_before>// This example just prints "Hello Tock World" to the terminal.
// Run `tockloader listen` to view the message.

#![no_std]

use core::fmt::Write;
use libtock::result::TockResult;

#[libtock::main]
async fn main() -> TockResult<()> {
    let drivers = libtock::retrieve_drivers()?;

    let mut console = drivers.console.create_console();

    writeln!(console, "Hello Tock World")?;

    Ok(())
}
<commit_msg>update comments for 'hello_world' example<commit_after>// This example just prints "Hello Tock World" to the terminal.
// Run `tockloader listen`, or use any serial program of your choice
//  (e.g. `screen`, `minicom`) to view the message.

#![no_std]

use core::fmt::Write;
use libtock::result::TockResult;

#[libtock::main]
async fn main() -> TockResult<()> {
    let drivers = libtock::retrieve_drivers()?;

    let mut console = drivers.console.create_console();

    writeln!(console, "Hello Tock World")?;

    Ok(())
}
<|endoftext|>


<commit_before>extern crate cocoa;

use cocoa::base::{NSUInteger, nil, ObjCSelector};
use cocoa::appkit::{NSApp, NSRect, NSPoint, NSSize,
					NSAutoreleasePool, NSProcessInfo,
					NSApplication, NSApplicationActivationPolicyRegular,
					NSWindow, NSTitledWindowMask, NSBackingStoreBuffered,
					NSString,
					NSMenu, NSMenuItem};

fn main() {
	unsafe {
		let pool = NSAutoreleasePool::new(nil);

		let app = NSApp();
		app.setActivationPolicy_(NSApplicationActivationPolicyRegular);

		// create Menu Bar
		let menubar = NSMenu::new(nil);
		let app_menu_item = NSMenuItem::new(nil);
		menubar.addItem_(app_menu_item);
		app.setMainMenu_(menubar);

		// create Application menu
		let app_menu = NSMenu::new(nil);
		let quit_prefix = NSString::alloc(nil).init_str("Quit \0");
		let quit_title = quit_prefix.stringByAppendingString_(
			NSProcessInfo::processInfo(nil).processName()
		);
		let quit_action = "terminate:".as_selector();
		let quit_key = NSString::alloc(nil).init_str("q\0");
		let quit_item = NSMenuItem::alloc(nil).initWithTitle_action_keyEquivalent_(
			quit_title,
			quit_action,
			quit_key
		);
		app_menu.addItem_(quit_item);
		app_menu_item.setSubmenu_(app_menu);

		// create Window
		let window = NSWindow::alloc(nil).initWithContentRect_styleMask_backing_defer_(
			NSRect::new(NSPoint::new(0., 0.), NSSize::new(200., 200.)),
			NSTitledWindowMask as NSUInteger,
			NSBackingStoreBuffered,
			false
		);

		window.cascadeTopLeftFromPoint_(NSPoint::new(20., 20.));
		window.center();
		let title = NSString::alloc(nil).init_str("Hello World!\0");
		window.setTitle_(title);
		window.makeKeyAndOrderFront_(nil);

		app.activateIgnoringOtherApps_(true);
		app.run();
	}
}
<commit_msg>Use autorelease in hello_world<commit_after>extern crate cocoa;

use cocoa::base::{NSUInteger, nil, ObjCSelector};
use cocoa::appkit::{NSApp, NSRect, NSPoint, NSSize,
					NSAutoreleasePool, NSProcessInfo,
					NSApplication, NSApplicationActivationPolicyRegular,
					NSWindow, NSTitledWindowMask, NSBackingStoreBuffered,
					NSString,
					NSMenu, NSMenuItem};

fn main() {
	unsafe {
		let pool = NSAutoreleasePool::new(nil);

		let app = NSApp();
		app.setActivationPolicy_(NSApplicationActivationPolicyRegular);

		// create Menu Bar
		let menubar = NSMenu::new(nil).autorelease();
		let app_menu_item = NSMenuItem::new(nil).autorelease();
		menubar.addItem_(app_menu_item);
		app.setMainMenu_(menubar);

		// create Application menu
		let app_menu = NSMenu::new(nil).autorelease();
		let quit_prefix = NSString::alloc(nil).init_str("Quit \0");
		let quit_title = quit_prefix.stringByAppendingString_(
			NSProcessInfo::processInfo(nil).processName()
		);
		let quit_action = "terminate:".as_selector();
		let quit_key = NSString::alloc(nil).init_str("q\0");
		let quit_item = NSMenuItem::alloc(nil).initWithTitle_action_keyEquivalent_(
			quit_title,
			quit_action,
			quit_key
		).autorelease();
		app_menu.addItem_(quit_item);
		app_menu_item.setSubmenu_(app_menu);

		// create Window
		let window = NSWindow::alloc(nil).initWithContentRect_styleMask_backing_defer_(
			NSRect::new(NSPoint::new(0., 0.), NSSize::new(200., 200.)),
			NSTitledWindowMask as NSUInteger,
			NSBackingStoreBuffered,
			false
		).autorelease();
		window.cascadeTopLeftFromPoint_(NSPoint::new(20., 20.));
		window.center();
		let title = NSString::alloc(nil).init_str("Hello World!\0");
		window.setTitle_(title);
		window.makeKeyAndOrderFront_(nil);

		app.activateIgnoringOtherApps_(true);
		app.run();
	}
}
<|endoftext|>


<commit_before>use super::RGB;

use self::PaletteGrayShade::*;

/// The 4 shades of grey that the Game Boy (Classic)'s monochrome LCD can
/// display.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum PaletteGrayShade {
    White = 0,
    LightGray = 1,
    DarkGray = 2,
    Dark = 3,
}

impl PaletteGrayShade {
    /// Build a 'PaletteGrayShade' value from a byte.
    /// Assumption : value <= 3 (see 'PaletteClassic' usage for justification).
    pub fn from_u8(value: u8) -> PaletteGrayShade {
        match value {
            0 => White,
            1 => LightGray,
            2 => DarkGray,
            3 => Dark,
            _ => unreachable!(),
        }
    }

    /// Get the RGB color corresponding to the palette value.
    pub fn to_rgb(&self) -> RGB {
        PALETTE_CLASSIC_RGB[self.clone() as usize]
    }
}

/// Gives the RGB colors corresponding to the GB's monochrome palette values.
const PALETTE_CLASSIC_RGB: [RGB; 4] = [
    RGB { r: 255, g: 255, b: 255 },
    RGB { r: 192, g: 192, b: 192 },
    RGB { r:  96, g:  96, b:  96 },
    RGB { r:  0 , g:  0 , b:  0  },
];

/// The palette in the Game Boy (Classic) allows by changing a single byte to
/// individually assign 4 colors to arbitrary 'PaletteGrayShade' values :
///
/// bits 7-6 : shade for color 3
/// bits 5-4 : shade for color 2
/// bits 3-2 : shade for color 1
/// bits 1-0 : shade for color 0
///
/// A shade is thus coded as two bits to directly map to the 'PaletteGrayShade'
/// values : 0 for white, 1 for light gray, 2 for dark gray and 3 for dark.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PaletteClassic {
    /// The palette's raw byte value.
    raw: u8,
    /// The palette's colors.
    data: [PaletteGrayShade; 4],
}

impl PaletteClassic {
    pub fn new() -> PaletteClassic {
        // TODO : check default palette value
        PaletteClassic{ raw: 0xFF, data: [White, White, White, White] }
    }

    pub fn set(&mut self, value: u8) {
        self.raw     = value;
        self.data[0] = PaletteGrayShade::from_u8((value >> 0) & 0b11);
        self.data[1] = PaletteGrayShade::from_u8((value >> 2) & 0b11);
        self.data[2] = PaletteGrayShade::from_u8((value >> 4) & 0b11);
        self.data[3] = PaletteGrayShade::from_u8((value >> 6) & 0b11);
    }

    pub fn raw(&self) -> u8 {
        self.raw
    }

    pub fn data(&self) -> &[PaletteGrayShade; 4] {
        &self.data
    }
}

#[cfg(test)]
mod test {
    use super::{PaletteGrayShade, PaletteClassic, PALETTE_CLASSIC_RGB};
    use super::PaletteGrayShade::*;

    #[test]
    fn test_PaletteGrayShade_values() {
        assert_eq!(White as u8, 0b00);
        assert_eq!(LightGray as u8, 0b01);
        assert_eq!(DarkGray as u8, 0b10);
        assert_eq!(Dark as u8, 0b11);
    }

    #[test]
    fn test_PaletteGrayShade_to_RGB() {
        assert_eq!(White.to_rgb(), PALETTE_CLASSIC_RGB[0]);
        assert_eq!(LightGray.to_rgb(), PALETTE_CLASSIC_RGB[1]);
        assert_eq!(DarkGray.to_rgb(), PALETTE_CLASSIC_RGB[2]);
        assert_eq!(Dark.to_rgb(), PALETTE_CLASSIC_RGB[3]);
    }

    #[test]
    fn test_PaletteClassic() {
        let mut palette = PaletteClassic::new();
        palette.set(0b_1011_0001);
        let colors = palette.data();
        assert_eq!(palette.raw(), 0b_1011_0001);
        assert_eq!(colors[0], LightGray);
        assert_eq!(colors[1], White);
        assert_eq!(colors[2], Dark);
        assert_eq!(colors[3], DarkGray);
    }
}
<commit_msg>Implemented Color representation for CGB palette<commit_after>use super::RGB;

use self::PaletteGrayShade::*;

/// The 4 shades of grey that the Game Boy (Classic)'s monochrome LCD can
/// display.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum PaletteGrayShade {
    White = 0,
    LightGray = 1,
    DarkGray = 2,
    Dark = 3,
}

impl PaletteGrayShade {
    /// Build a 'PaletteGrayShade' value from a byte.
    /// Assumption : value <= 3 (see 'PaletteClassic' usage for justification).
    pub fn from_u8(value: u8) -> PaletteGrayShade {
        match value {
            0 => White,
            1 => LightGray,
            2 => DarkGray,
            3 => Dark,
            _ => unreachable!(),
        }
    }

    /// Get the RGB color corresponding to the palette value.
    pub fn to_rgb(&self) -> RGB {
        PALETTE_CLASSIC_RGB[self.clone() as usize]
    }
}

/// Gives the RGB colors corresponding to the GB's monochrome palette values.
const PALETTE_CLASSIC_RGB: [RGB; 4] = [
    RGB { r: 255, g: 255, b: 255 },
    RGB { r: 192, g: 192, b: 192 },
    RGB { r:  96, g:  96, b:  96 },
    RGB { r:  0 , g:  0 , b:  0  },
];

/// The palette in the Game Boy (Classic) allows by changing a single byte to
/// individually assign 4 colors to arbitrary 'PaletteGrayShade' values :
///
/// bits 7-6 : shade for color 3
/// bits 5-4 : shade for color 2
/// bits 3-2 : shade for color 1
/// bits 1-0 : shade for color 0
///
/// A shade is thus coded as two bits to directly map to the 'PaletteGrayShade'
/// values : 0 for white, 1 for light gray, 2 for dark gray and 3 for dark.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct PaletteClassic {
    /// The palette's raw byte value.
    raw: u8,
    /// The palette's colors.
    data: [PaletteGrayShade; 4],
}

impl PaletteClassic {
    pub fn new() -> PaletteClassic {
        // TODO : check default palette value
        PaletteClassic{ raw: 0xFF, data: [White, White, White, White] }
    }

    pub fn set(&mut self, value: u8) {
        self.raw     = value;
        self.data[0] = PaletteGrayShade::from_u8((value >> 0) & 0b11);
        self.data[1] = PaletteGrayShade::from_u8((value >> 2) & 0b11);
        self.data[2] = PaletteGrayShade::from_u8((value >> 4) & 0b11);
        self.data[3] = PaletteGrayShade::from_u8((value >> 6) & 0b11);
    }

    pub fn raw(&self) -> u8 {
        self.raw
    }

    pub fn data(&self) -> &[PaletteGrayShade; 4] {
        &self.data
    }
}

/// A color in the GameBoyColor is defined by 15 bits (low byte : 0-7 bits,
/// high byte : 8-15 bits) as such:
/// bit 0-4: red intensity (so the possible values are : 00-1F)
/// bit 5-9: green intensity
/// bit 10-14: blue intensity
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
struct PaletteColorValue {
    /// The color's raw value.
    raw: u16,
    /// The color's RGB value, stored for more efficient drawing.
    rgb: RGB,
}

impl PaletteColorValue {
    pub fn new(raw_value: u16) -> PaletteColorValue {
        PaletteColorValue {
            raw: raw_value,
            rgb: PaletteColorValue::compute_rgb(raw_value),
        }
    }

    pub fn set(&mut self, raw_value: u16) {
        self.raw = raw_value;
        self.rgb = PaletteColorValue::compute_rgb(raw_value);
    }

    pub fn raw_low(&self) -> u8 {
        (self.raw & 0x00FF) as u8
    }
    pub fn raw_high(&self) -> u8 {
        (self.raw >> 8) as u8
    }

    pub fn rgb(&self) -> RGB { self.rgb }

    fn compute_rgb(raw_value: u16) -> RGB {
        // the color values are on 5 bits, which means 32 values
        // we thus need to multiply by 8 to reach the 256 RGB scale
        RGB {
            r: (((raw_value >> 0)  & 0x001F) as u8) * 8,
            g: (((raw_value >> 5)  & 0x001F) as u8) * 8,
            b: (((raw_value >> 10) & 0x001F) as u8) * 8,
        }
    }
}

/// A GameBoyColor palette is defined by its 4 'PaletteColorValue', meaning
/// 4 * 2 = 8 bytes.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct PaletteColor {
    data: [PaletteColorValue; 4],
}

impl PaletteColor {
    pub fn new() -> PaletteColor {
        // TODO : check default palette value
        PaletteColor{ data: [PaletteColorValue::new(0x0000); 4] }
    }
}

#[cfg(test)]
mod test {
    use super::{PaletteGrayShade, PaletteClassic, PALETTE_CLASSIC_RGB,
        PaletteColorValue, PaletteColor};
    use super::PaletteGrayShade::*;

    #[test]
    fn test_PaletteGrayShade_values() {
        assert_eq!(White as u8, 0b00);
        assert_eq!(LightGray as u8, 0b01);
        assert_eq!(DarkGray as u8, 0b10);
        assert_eq!(Dark as u8, 0b11);
    }

    #[test]
    fn test_PaletteGrayShade_to_RGB() {
        assert_eq!(White.to_rgb(), PALETTE_CLASSIC_RGB[0]);
        assert_eq!(LightGray.to_rgb(), PALETTE_CLASSIC_RGB[1]);
        assert_eq!(DarkGray.to_rgb(), PALETTE_CLASSIC_RGB[2]);
        assert_eq!(Dark.to_rgb(), PALETTE_CLASSIC_RGB[3]);
    }

    #[test]
    fn test_PaletteClassic() {
        let mut palette = PaletteClassic::new();
        palette.set(0b_1011_0001);
        let colors = palette.data();
        assert_eq!(palette.raw(), 0b_1011_0001);
        assert_eq!(colors[0], LightGray);
        assert_eq!(colors[1], White);
        assert_eq!(colors[2], Dark);
        assert_eq!(colors[3], DarkGray);
    }

    #[test]
    fn test_PaletteColorValue() {
        let mut color = PaletteColorValue::new(0x0000);
        assert_eq!(color.raw_low(), 0x00);
        assert_eq!(color.raw_high(), 0x00);
        assert_eq!(color.rgb().r, 0);
        assert_eq!(color.rgb().g, 0);
        assert_eq!(color.rgb().b, 0);

        color.set(0xF7A9);
        assert_eq!(color.raw_low(), 0xA9);
        assert_eq!(color.raw_high(), 0xF7);
        assert_eq!(color.rgb().r,  72);
        assert_eq!(color.rgb().g, 232);
        assert_eq!(color.rgb().b, 232);
    }
}
<|endoftext|>


<commit_before>use std::collections::hash_map::{HashMap, Entry};
use std::path::Path;

use url::Url;

use ffi;
use ffi::opt;
use ffi::easy::Easy;
use http::Response;
use http::body::{Body,ToBody};
use {ProgressCb,ErrCode};

use self::Method::{Get, Head, Post, Put, Patch, Delete};
use self::BodyType::{Fixed, Chunked};

const DEFAULT_TIMEOUT_MS: usize = 30_000;

pub struct Handle {
    easy: Easy,
}

impl Handle {
    pub fn new() -> Handle {
        return configure(Handle { easy: Easy::new() }
            .timeout(DEFAULT_TIMEOUT_MS)
            .connect_timeout(DEFAULT_TIMEOUT_MS));

        #[cfg(unix)]
        fn configure(handle: Handle) -> Handle {
            let probe = ::openssl::probe::probe();
            let handle = match probe.cert_file {
                Some(ref path) => handle.ssl_ca_info(path),
                None => handle,
            };
            match probe.cert_dir {
                Some(ref path) => handle.ssl_ca_path(path),
                None => handle,
            }
        }

        #[cfg(not(unix))]
        fn configure(handle: Handle) -> Handle { handle }
    }

    pub fn timeout(mut self, ms: usize) -> Handle {
        self.easy.setopt(opt::TIMEOUT_MS, ms).unwrap();
        self
    }

    pub fn connect_timeout(mut self, ms: usize) -> Handle {
        self.easy.setopt(opt::CONNECTTIMEOUT_MS, ms).unwrap();
        self
    }

    /// Set the time in seconds that the transfer speed should be below
    /// the `low_speed_limit` rate of bytes per second for the library to
    /// consider it too slow and abort.
    ///
    /// The default for this option is 0 which means that this option is
    /// disabled.
    pub fn low_speed_timeout(mut self, seconds: usize) -> Handle {
        self.easy.setopt(opt::LOW_SPEED_TIME, seconds).unwrap();
        self
    }

    /// Set the average transfer speed in bytes per second that the
    /// transfer should be below during `low_speed_timeout` seconds for
    /// libcurl to consider it to be too slow and abort.
    ///
    /// The default for this option is 0 which means that this option is
    /// disabled.
    pub fn low_speed_limit(mut self, bytes_per_second: usize) -> Handle {
        self.easy.setopt(opt::LOW_SPEED_LIMIT, bytes_per_second).unwrap();
        self
    }

    pub fn verbose(mut self) -> Handle {
        self.easy.setopt(opt::VERBOSE, 1).unwrap();
        self
    }

    pub fn proxy<U: ToUrl>(mut self, proxy: U) -> Handle {
        proxy.with_url_str(|s| {
            self.easy.setopt(opt::PROXY, s).unwrap();
        });

        self
    }

    pub fn ssl_ca_path(mut self, path: &Path) -> Handle {
        self.easy.setopt(opt::CAPATH, path).unwrap();
        self
    }

    pub fn ssl_ca_info(mut self, path: &Path) -> Handle {
        self.easy.setopt(opt::CAINFO, path).unwrap();
        self
    }

    pub fn cookie_jar(mut self, path: &Path) -> Handle {
        self.easy.setopt(opt::COOKIEJAR, path).unwrap();
        self
    }

    pub fn cookie_file(mut self, path: &Path) -> Handle {
        self.easy.setopt(opt::COOKIEFILE, path).unwrap();
        self
    }

    pub fn cookies(self, path: &Path) -> Handle {
        self.cookie_jar(path).cookie_file(path)
    }

    pub fn get<'a, 'b, U: ToUrl>(&'a mut self, uri: U) -> Request<'a, 'b> {
        Request::new(self, Get).uri(uri)
    }

    pub fn head<'a, 'b, U: ToUrl>(&'a mut self, uri: U) -> Request<'a, 'b> {
        Request::new(self, Head).uri(uri)
    }

    pub fn post<'a, 'b, U: ToUrl, B: ToBody<'b>>(&'a mut self, uri: U, body: B) -> Request<'a, 'b> {
        Request::new(self, Post).uri(uri).body(body)
    }

    pub fn put<'a, 'b, U: ToUrl, B: ToBody<'b>>(&'a mut self, uri: U, body: B) -> Request<'a, 'b> {
        Request::new(self, Put).uri(uri).body(body)
    }

    pub fn patch<'a, 'b, U: ToUrl, B: ToBody<'b>>(&'a mut self, uri: U, body: B) -> Request<'a, 'b> {
        Request::new(self, Patch).uri(uri).body(body)
    }

    pub fn delete<'a, 'b, U: ToUrl>(&'a mut self, uri: U) -> Request<'a, 'b> {
        Request::new(self, Delete).uri(uri)
    }
}

#[derive(Copy, Clone)]
pub enum Method {
    Options,
    Get,
    Head,
    Post,
    Put,
    Patch,
    Delete,
    Trace,
    Connect
}

pub struct Request<'a, 'b> {
    err: Option<ErrCode>,
    handle: &'a mut Handle,
    method: Method,
    headers: HashMap<String, Vec<String>>,
    body: Option<Body<'b>>,
    body_type: Option<BodyType>,
    content_type: bool, // whether or not the content type was set
    expect_continue: bool, // whether to expect a 100 continue from the server
    progress: Option<Box<ProgressCb<'b>>>,
    follow: bool,
}

enum BodyType {
    Fixed(usize),
    Chunked,
}

impl<'a, 'b> Request<'a, 'b> {
    pub fn new(handle: &'a mut Handle, method: Method) -> Request<'a, 'b> {
        Request {
            err: None,
            handle: handle,
            method: method,
            headers: HashMap::new(),
            body: None,
            body_type: None,
            content_type: false,
            expect_continue: false,
            progress: None,
            follow: false,
        }
    }

    pub fn uri<U: ToUrl>(mut self, uri: U) -> Request<'a, 'b> {
        uri.with_url_str(|s| {
            match self.handle.easy.setopt(opt::URL, s) {
                Ok(_) => {}
                Err(e) => self.err = Some(e)
            }
        });

        self
    }

    pub fn body<B: ToBody<'b>>(mut self, body: B) -> Request<'a, 'b> {
        self.body = Some(body.to_body());
        self
    }

    pub fn content_type(mut self, ty: &str) -> Request<'a, 'b> {
        if !self.content_type {
            self.content_type = true;
            append_header(&mut self.headers, "Content-Type", ty);
        }

        self
    }

    pub fn content_length(mut self, len: usize) -> Request<'a, 'b> {
        self.body_type = Some(Fixed(len));
        self
    }

    pub fn chunked(mut self) -> Request<'a, 'b> {
        self.body_type = Some(Chunked);
        self
    }

    pub fn expect_continue(mut self) -> Request<'a, 'b> {
        self.expect_continue = true;
        self
    }

    pub fn header(mut self, name: &str, val: &str) -> Request<'a, 'b> {
        append_header(&mut self.headers, name, val);
        self
    }

    pub fn get_header(&self, name: &str) -> Option<&[String]> {
        self.headers.get(name).map(|a| &a[..])
    }

    pub fn headers<'c, 'd, I: Iterator<Item=(&'c str, &'d str)>>(mut self, hdrs: I) -> Request<'a, 'b> {
        for (name, val) in hdrs {
            append_header(&mut self.headers, name, val);
        }

        self
    }

    pub fn progress<F>(mut self, cb: F) -> Request<'a, 'b>
        where F: FnMut(usize, usize, usize, usize) + 'b
    {
        self.progress = Some(Box::new(cb) as Box<ProgressCb<'b>>);
        self
    }

    pub fn follow_redirects(mut self, follow: bool) -> Request<'a, 'b> {
        self.follow = follow;
        self
    }

    pub fn exec(self) -> Result<Response, ErrCode> {
        // Deconstruct the struct
        let Request {
            err,
            handle,
            method,
            mut headers,
            mut body,
            body_type,
            content_type,
            expect_continue,
            progress,
            follow,
            ..
        } = self;

        if follow {
            try!(handle.easy.setopt(opt::FOLLOWLOCATION, 1));
        }

        match err {
            Some(e) => return Err(e),
            None => {}
        }

        // Clear custom headers set from the previous request
        try!(handle.easy.setopt(opt::HTTPHEADER, 0));

        match method {
            Get => try!(handle.easy.setopt(opt::HTTPGET, 1)),
            Head => try!(handle.easy.setopt(opt::NOBODY, 1)),
            Post => try!(handle.easy.setopt(opt::POST, 1)),
            Put => try!(handle.easy.setopt(opt::UPLOAD, 1)),
            Patch => {
                try!(handle.easy.setopt(opt::CUSTOMREQUEST, "PATCH"));
                try!(handle.easy.setopt(opt::UPLOAD, 1));
            },
            Delete => {
                if body.is_some() {
                    try!(handle.easy.setopt(opt::UPLOAD, 1));
                }

                try!(handle.easy.setopt(opt::CUSTOMREQUEST, "DELETE"));
            }
            _ => unimplemented!()
        }

        match body.as_ref() {
            None => {}
            Some(body) => {
                let body_type = body_type.unwrap_or(match body.get_size() {
                    Some(len) => Fixed(len),
                    None => Chunked,
                });

                match body_type {
                    Fixed(len) => {
                        match method {
                            Post => try!(handle.easy.setopt(opt::POSTFIELDSIZE, len)),
                            Put | Patch | Delete  => try!(handle.easy.setopt(opt::INFILESIZE, len)),
                            _ => {}
                        }
                        append_header(&mut headers, "Content-Length",
                                      &len.to_string());
                    }
                    Chunked => {
                        append_header(&mut headers, "Transfer-Encoding",
                                      "chunked");
                    }

                }

                if !content_type {
                    append_header(&mut headers, "Content-Type", "application/octet-stream");
                }

                if !expect_continue {
                    append_header(&mut headers, "Expect", "");
                }
            }
        }

        let mut ffi_headers = ffi::List::new();

        if !headers.is_empty() {
            let mut buf = Vec::new();

            for (k, v) in headers.iter() {
                buf.extend(k.bytes());
                buf.extend(": ".bytes());

                for v in v.iter() {
                    buf.extend(v.bytes());
                    buf.push(0);
                    ffi_headers.push_bytes(&buf);
                    buf.truncate(k.len() + 2);
                }

                buf.truncate(0);
            }

            try!(handle.easy.setopt(opt::HTTPHEADER, &ffi_headers));
        }

        handle.easy.perform(body.as_mut(), progress)
    }
}

fn append_header(map: &mut HashMap<String, Vec<String>>, key: &str, val: &str) {
    match map.entry(key.to_string()) {
        Entry::Vacant(entry) => {
            let mut values = Vec::new();
            values.push(val.to_string());
            entry.insert(values)
        },
        Entry::Occupied(entry) => entry.into_mut()
    };
}

pub trait ToUrl{
    fn with_url_str<F>(self, f: F) where F: FnOnce(&str);
}

impl<'a> ToUrl for &'a str {
    fn with_url_str<F>(self, f: F) where F: FnOnce(&str) {
        f(self);
    }
}

impl<'a> ToUrl for &'a Url {
    fn with_url_str<F>(self, f: F) where F: FnOnce(&str) {
        self.to_string().with_url_str(f);
    }
}

impl ToUrl for String {
    fn with_url_str<F>(self, f: F) where F: FnOnce(&str) {
        self[..].with_url_str(f);
    }
}

#[cfg(test)]
mod tests {
    use super::Handle;

    #[test]
    fn get_header() {
        let mut h = Handle::new();
        let r = h.get("/foo").header("foo", "bar");
        assert_eq!(r.get_header("foo"), Some(&["bar".to_string()][..]));
    }
}
<commit_msg>Older curl does not dedup Content-Length<commit_after>use std::collections::hash_map::{HashMap, Entry};
use std::path::Path;

use url::Url;

use ffi;
use ffi::opt;
use ffi::easy::Easy;
use http::Response;
use http::body::{Body,ToBody};
use {ProgressCb,ErrCode};

use self::Method::{Get, Head, Post, Put, Patch, Delete};
use self::BodyType::{Fixed, Chunked};

const DEFAULT_TIMEOUT_MS: usize = 30_000;

pub struct Handle {
    easy: Easy,
}

impl Handle {
    pub fn new() -> Handle {
        return configure(Handle { easy: Easy::new() }
            .timeout(DEFAULT_TIMEOUT_MS)
            .connect_timeout(DEFAULT_TIMEOUT_MS));

        #[cfg(unix)]
        fn configure(handle: Handle) -> Handle {
            let probe = ::openssl::probe::probe();
            let handle = match probe.cert_file {
                Some(ref path) => handle.ssl_ca_info(path),
                None => handle,
            };
            match probe.cert_dir {
                Some(ref path) => handle.ssl_ca_path(path),
                None => handle,
            }
        }

        #[cfg(not(unix))]
        fn configure(handle: Handle) -> Handle { handle }
    }

    pub fn timeout(mut self, ms: usize) -> Handle {
        self.easy.setopt(opt::TIMEOUT_MS, ms).unwrap();
        self
    }

    pub fn connect_timeout(mut self, ms: usize) -> Handle {
        self.easy.setopt(opt::CONNECTTIMEOUT_MS, ms).unwrap();
        self
    }

    /// Set the time in seconds that the transfer speed should be below
    /// the `low_speed_limit` rate of bytes per second for the library to
    /// consider it too slow and abort.
    ///
    /// The default for this option is 0 which means that this option is
    /// disabled.
    pub fn low_speed_timeout(mut self, seconds: usize) -> Handle {
        self.easy.setopt(opt::LOW_SPEED_TIME, seconds).unwrap();
        self
    }

    /// Set the average transfer speed in bytes per second that the
    /// transfer should be below during `low_speed_timeout` seconds for
    /// libcurl to consider it to be too slow and abort.
    ///
    /// The default for this option is 0 which means that this option is
    /// disabled.
    pub fn low_speed_limit(mut self, bytes_per_second: usize) -> Handle {
        self.easy.setopt(opt::LOW_SPEED_LIMIT, bytes_per_second).unwrap();
        self
    }

    pub fn verbose(mut self) -> Handle {
        self.easy.setopt(opt::VERBOSE, 1).unwrap();
        self
    }

    pub fn proxy<U: ToUrl>(mut self, proxy: U) -> Handle {
        proxy.with_url_str(|s| {
            self.easy.setopt(opt::PROXY, s).unwrap();
        });

        self
    }

    pub fn ssl_ca_path(mut self, path: &Path) -> Handle {
        self.easy.setopt(opt::CAPATH, path).unwrap();
        self
    }

    pub fn ssl_ca_info(mut self, path: &Path) -> Handle {
        self.easy.setopt(opt::CAINFO, path).unwrap();
        self
    }

    pub fn cookie_jar(mut self, path: &Path) -> Handle {
        self.easy.setopt(opt::COOKIEJAR, path).unwrap();
        self
    }

    pub fn cookie_file(mut self, path: &Path) -> Handle {
        self.easy.setopt(opt::COOKIEFILE, path).unwrap();
        self
    }

    pub fn cookies(self, path: &Path) -> Handle {
        self.cookie_jar(path).cookie_file(path)
    }

    pub fn get<'a, 'b, U: ToUrl>(&'a mut self, uri: U) -> Request<'a, 'b> {
        Request::new(self, Get).uri(uri)
    }

    pub fn head<'a, 'b, U: ToUrl>(&'a mut self, uri: U) -> Request<'a, 'b> {
        Request::new(self, Head).uri(uri)
    }

    pub fn post<'a, 'b, U: ToUrl, B: ToBody<'b>>(&'a mut self, uri: U, body: B) -> Request<'a, 'b> {
        Request::new(self, Post).uri(uri).body(body)
    }

    pub fn put<'a, 'b, U: ToUrl, B: ToBody<'b>>(&'a mut self, uri: U, body: B) -> Request<'a, 'b> {
        Request::new(self, Put).uri(uri).body(body)
    }

    pub fn patch<'a, 'b, U: ToUrl, B: ToBody<'b>>(&'a mut self, uri: U, body: B) -> Request<'a, 'b> {
        Request::new(self, Patch).uri(uri).body(body)
    }

    pub fn delete<'a, 'b, U: ToUrl>(&'a mut self, uri: U) -> Request<'a, 'b> {
        Request::new(self, Delete).uri(uri)
    }
}

#[derive(Copy, Clone)]
pub enum Method {
    Options,
    Get,
    Head,
    Post,
    Put,
    Patch,
    Delete,
    Trace,
    Connect
}

pub struct Request<'a, 'b> {
    err: Option<ErrCode>,
    handle: &'a mut Handle,
    method: Method,
    headers: HashMap<String, Vec<String>>,
    body: Option<Body<'b>>,
    body_type: Option<BodyType>,
    content_type: bool, // whether or not the content type was set
    expect_continue: bool, // whether to expect a 100 continue from the server
    progress: Option<Box<ProgressCb<'b>>>,
    follow: bool,
}

enum BodyType {
    Fixed(usize),
    Chunked,
}

impl<'a, 'b> Request<'a, 'b> {
    pub fn new(handle: &'a mut Handle, method: Method) -> Request<'a, 'b> {
        Request {
            err: None,
            handle: handle,
            method: method,
            headers: HashMap::new(),
            body: None,
            body_type: None,
            content_type: false,
            expect_continue: false,
            progress: None,
            follow: false,
        }
    }

    pub fn uri<U: ToUrl>(mut self, uri: U) -> Request<'a, 'b> {
        uri.with_url_str(|s| {
            match self.handle.easy.setopt(opt::URL, s) {
                Ok(_) => {}
                Err(e) => self.err = Some(e)
            }
        });

        self
    }

    pub fn body<B: ToBody<'b>>(mut self, body: B) -> Request<'a, 'b> {
        self.body = Some(body.to_body());
        self
    }

    pub fn content_type(mut self, ty: &str) -> Request<'a, 'b> {
        if !self.content_type {
            self.content_type = true;
            append_header(&mut self.headers, "Content-Type", ty);
        }

        self
    }

    pub fn content_length(mut self, len: usize) -> Request<'a, 'b> {
        self.body_type = Some(Fixed(len));
        self
    }

    pub fn chunked(mut self) -> Request<'a, 'b> {
        self.body_type = Some(Chunked);
        self
    }

    pub fn expect_continue(mut self) -> Request<'a, 'b> {
        self.expect_continue = true;
        self
    }

    pub fn header(mut self, name: &str, val: &str) -> Request<'a, 'b> {
        append_header(&mut self.headers, name, val);
        self
    }

    pub fn get_header(&self, name: &str) -> Option<&[String]> {
        self.headers.get(name).map(|a| &a[..])
    }

    pub fn headers<'c, 'd, I: Iterator<Item=(&'c str, &'d str)>>(mut self, hdrs: I) -> Request<'a, 'b> {
        for (name, val) in hdrs {
            append_header(&mut self.headers, name, val);
        }

        self
    }

    pub fn progress<F>(mut self, cb: F) -> Request<'a, 'b>
        where F: FnMut(usize, usize, usize, usize) + 'b
    {
        self.progress = Some(Box::new(cb) as Box<ProgressCb<'b>>);
        self
    }

    pub fn follow_redirects(mut self, follow: bool) -> Request<'a, 'b> {
        self.follow = follow;
        self
    }

    pub fn exec(self) -> Result<Response, ErrCode> {
        // Deconstruct the struct
        let Request {
            err,
            handle,
            method,
            mut headers,
            mut body,
            body_type,
            content_type,
            expect_continue,
            progress,
            follow,
            ..
        } = self;

        if follow {
            try!(handle.easy.setopt(opt::FOLLOWLOCATION, 1));
        }

        match err {
            Some(e) => return Err(e),
            None => {}
        }

        // Clear custom headers set from the previous request
        try!(handle.easy.setopt(opt::HTTPHEADER, 0));

        match method {
            Get => try!(handle.easy.setopt(opt::HTTPGET, 1)),
            Head => try!(handle.easy.setopt(opt::NOBODY, 1)),
            Post => try!(handle.easy.setopt(opt::POST, 1)),
            Put => try!(handle.easy.setopt(opt::UPLOAD, 1)),
            Patch => {
                try!(handle.easy.setopt(opt::CUSTOMREQUEST, "PATCH"));
                try!(handle.easy.setopt(opt::UPLOAD, 1));
            },
            Delete => {
                if body.is_some() {
                    try!(handle.easy.setopt(opt::UPLOAD, 1));
                }

                try!(handle.easy.setopt(opt::CUSTOMREQUEST, "DELETE"));
            }
            _ => unimplemented!()
        }

        match body.as_ref() {
            None => {}
            Some(body) => {
                let body_type = body_type.unwrap_or(match body.get_size() {
                    Some(len) => Fixed(len),
                    None => Chunked,
                });

                match body_type {
                    Fixed(len) => {
                        match method {
                            Post => try!(handle.easy.setopt(opt::POSTFIELDSIZE, len)),
                            Put | Patch | Delete  => try!(handle.easy.setopt(opt::INFILESIZE, len)),
                            _ => {
                                append_header(&mut headers, "Content-Length",
                                              &len.to_string());
                            }
                        }
                    }
                    Chunked => {
                        append_header(&mut headers, "Transfer-Encoding",
                                      "chunked");
                    }

                }

                if !content_type {
                    append_header(&mut headers, "Content-Type", "application/octet-stream");
                }

                if !expect_continue {
                    append_header(&mut headers, "Expect", "");
                }
            }
        }

        let mut ffi_headers = ffi::List::new();

        if !headers.is_empty() {
            let mut buf = Vec::new();

            for (k, v) in headers.iter() {
                buf.extend(k.bytes());
                buf.extend(": ".bytes());

                for v in v.iter() {
                    buf.extend(v.bytes());
                    buf.push(0);
                    ffi_headers.push_bytes(&buf);
                    buf.truncate(k.len() + 2);
                }

                buf.truncate(0);
            }

            try!(handle.easy.setopt(opt::HTTPHEADER, &ffi_headers));
        }

        handle.easy.perform(body.as_mut(), progress)
    }
}

fn append_header(map: &mut HashMap<String, Vec<String>>, key: &str, val: &str) {
    match map.entry(key.to_string()) {
        Entry::Vacant(entry) => {
            let mut values = Vec::new();
            values.push(val.to_string());
            entry.insert(values)
        },
        Entry::Occupied(entry) => entry.into_mut()
    };
}

pub trait ToUrl{
    fn with_url_str<F>(self, f: F) where F: FnOnce(&str);
}

impl<'a> ToUrl for &'a str {
    fn with_url_str<F>(self, f: F) where F: FnOnce(&str) {
        f(self);
    }
}

impl<'a> ToUrl for &'a Url {
    fn with_url_str<F>(self, f: F) where F: FnOnce(&str) {
        self.to_string().with_url_str(f);
    }
}

impl ToUrl for String {
    fn with_url_str<F>(self, f: F) where F: FnOnce(&str) {
        self[..].with_url_str(f);
    }
}

#[cfg(test)]
mod tests {
    use super::Handle;

    #[test]
    fn get_header() {
        let mut h = Handle::new();
        let r = h.get("/foo").header("foo", "bar");
        assert_eq!(r.get_header("foo"), Some(&["bar".to_string()][..]));
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use azure::{AzScaledFontRef, AzFloat};
use azure::{struct__AzNativeFont};

use azure_hl::{BackendType,SkiaBackend};
use azure::{AzCreateScaledFontForNativeFont, AzReleaseScaledFont};

use std::cast;
use std::libc::c_void;
use std::ptr;

#[cfg(target_os="macos")]
use scaled_font::macos::*;

#[cfg(target_os="linux")]
use scaled_font::linux::*;

#[cfg(target_os="android")]
use scaled_font::android::*;

#[cfg(target_os="macos")]
pub mod macos {
    extern mod core_graphics = "rust-core-graphics";
    extern mod core_text = "rust-core-text";

    pub use scaled_font::macos::core_text::font::CTFontRef;
    pub use scaled_font::macos::core_graphics::font::{CGFont, CGFontRef};
}

#[cfg(target_os="linux")]
pub mod linux {
    extern mod freetype;

    pub use scaled_font::linux::freetype::freetype::{FT_Face, FT_LOAD_DEFAULT};
}

#[cfg(target_os="android")]
pub mod android {
    extern mod freetype;

    pub use scaled_font::android::freetype::freetype::{FT_Face, FT_LOAD_DEFAULT};
}

type SkTypeface = *c_void;

pub struct ScaledFont {
    azure_scaled_font: AzScaledFontRef,
}

impl Drop for ScaledFont {
    #[fixed_stack_segment]
    fn drop(&mut self) {
        unsafe {
            AzReleaseScaledFont(self.azure_scaled_font);
        }
    }
}

impl ScaledFont {
    pub fn get_ref(&self) -> AzScaledFontRef {
        self.azure_scaled_font
    }

    #[cfg(target_os="linux")]
    #[cfg(target_os="android")]
    #[fixed_stack_segment]
    pub fn new(backend: BackendType, native_font: FT_Face, size: AzFloat)
        -> ScaledFont {
        use azure::AZ_NATIVE_FONT_SKIA_FONT_FACE;
        use azure::{AzCreateFontOptions, AzDestroyFontOptions};

        let mut azure_native_font = struct__AzNativeFont {
            mType: 0,
            mFont: ptr::null()
        };

        match backend {
            SkiaBackend => {
                unsafe {
                    // NOTE: azure style flags and freetype style flags are the same in the lowest 2 bits
                    let style = ((*native_font).style_flags & 3) as u32;
                    let options = AzCreateFontOptions((*native_font).family_name, style);
                    azure_native_font.mType = AZ_NATIVE_FONT_SKIA_FONT_FACE;
                    azure_native_font.mFont = cast::transmute(options);
                    let azure_native_font_ptr = ptr::to_unsafe_ptr(&azure_native_font);
                    let azure_scaled_font = AzCreateScaledFontForNativeFont(azure_native_font_ptr, size);
                    AzDestroyFontOptions(options);
                    ScaledFont { azure_scaled_font: azure_scaled_font }
                }
            }
            _ => { fail!(~"don't know how to make a scaled font for this backend"); }
        }
    }

    /// Mac-specific function to create a font for the given backend.
    #[cfg(target_os="macos")]
    #[fixed_stack_segment]
    pub fn new(backend: BackendType, native_font: &CGFont, size: AzFloat) -> ScaledFont {
        use azure::AZ_NATIVE_FONT_MAC_FONT_FACE;
        use azure_hl::{CoreGraphicsBackend,CoreGraphicsAcceleratedBackend};

        let mut azure_native_font = struct__AzNativeFont {
            mType: 0,
            mFont: ptr::null()
        };

        match backend {
            CoreGraphicsBackend | CoreGraphicsAcceleratedBackend | SkiaBackend => {
                azure_native_font.mType = AZ_NATIVE_FONT_MAC_FONT_FACE;
                unsafe {
                    azure_native_font.mFont = cast::transmute(*native_font.contents.borrow_ref());
                }
            }
            _ => {
                fail!(~"don't know how to make a scaled font for this backend");
            }
        }

        unsafe {
            let azure_scaled_font = AzCreateScaledFontForNativeFont(&azure_native_font, size);
            ScaledFont {
                azure_scaled_font: azure_scaled_font
            }
        }
    }
}

// FIXME: Move this stuff to a rust-skia?
// FIXME: Demangle the names!!!
#[cfg(target_os="macos")]
#[link_args="-lskia"]
extern {
    pub fn _Z26SkCreateTypefaceFromCTFontPK8__CTFont(font: CTFontRef) -> *SkTypeface;
}

<commit_msg>Update for the Core Foundation bindings revamp<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use azure::{AzScaledFontRef, AzFloat};
use azure::{struct__AzNativeFont};

use azure_hl::{BackendType,SkiaBackend};
use azure::{AzCreateScaledFontForNativeFont, AzReleaseScaledFont};

use std::cast;
use std::libc::c_void;
use std::ptr;

#[cfg(target_os="macos")]
use scaled_font::macos::*;

#[cfg(target_os="linux")]
use scaled_font::linux::*;

#[cfg(target_os="android")]
use scaled_font::android::*;

#[cfg(target_os="macos")]
pub mod macos {
    extern mod core_foundation = "rust-core-foundation";
    extern mod core_graphics = "rust-core-graphics";
    extern mod core_text = "rust-core-text";

    pub use scaled_font::macos::core_text::font::CTFontRef;
    pub use scaled_font::macos::core_graphics::font::{CGFont, CGFontRef};
}

#[cfg(target_os="linux")]
pub mod linux {
    extern mod freetype;

    pub use scaled_font::linux::freetype::freetype::{FT_Face, FT_LOAD_DEFAULT};
}

#[cfg(target_os="android")]
pub mod android {
    extern mod freetype;

    pub use scaled_font::android::freetype::freetype::{FT_Face, FT_LOAD_DEFAULT};
}

type SkTypeface = *c_void;

pub struct ScaledFont {
    azure_scaled_font: AzScaledFontRef,
}

impl Drop for ScaledFont {
    #[fixed_stack_segment]
    fn drop(&mut self) {
        unsafe {
            AzReleaseScaledFont(self.azure_scaled_font);
        }
    }
}

impl ScaledFont {
    pub fn get_ref(&self) -> AzScaledFontRef {
        self.azure_scaled_font
    }

    #[cfg(target_os="linux")]
    #[cfg(target_os="android")]
    #[fixed_stack_segment]
    pub fn new(backend: BackendType, native_font: FT_Face, size: AzFloat)
        -> ScaledFont {
        use azure::AZ_NATIVE_FONT_SKIA_FONT_FACE;
        use azure::{AzCreateFontOptions, AzDestroyFontOptions};

        let mut azure_native_font = struct__AzNativeFont {
            mType: 0,
            mFont: ptr::null()
        };

        match backend {
            SkiaBackend => {
                unsafe {
                    // NOTE: azure style flags and freetype style flags are the same in the lowest 2 bits
                    let style = ((*native_font).style_flags & 3) as u32;
                    let options = AzCreateFontOptions((*native_font).family_name, style);
                    azure_native_font.mType = AZ_NATIVE_FONT_SKIA_FONT_FACE;
                    azure_native_font.mFont = cast::transmute(options);
                    let azure_native_font_ptr = ptr::to_unsafe_ptr(&azure_native_font);
                    let azure_scaled_font = AzCreateScaledFontForNativeFont(azure_native_font_ptr, size);
                    AzDestroyFontOptions(options);
                    ScaledFont { azure_scaled_font: azure_scaled_font }
                }
            }
            _ => { fail!(~"don't know how to make a scaled font for this backend"); }
        }
    }

    /// Mac-specific function to create a font for the given backend.
    #[cfg(target_os="macos")]
    #[fixed_stack_segment]
    pub fn new(backend: BackendType, native_font: &CGFont, size: AzFloat) -> ScaledFont {
        use azure::AZ_NATIVE_FONT_MAC_FONT_FACE;
        use azure_hl::{CoreGraphicsBackend,CoreGraphicsAcceleratedBackend};
        use scaled_font::macos::core_foundation::base::TCFType;

        let mut azure_native_font = struct__AzNativeFont {
            mType: 0,
            mFont: ptr::null()
        };

        match backend {
            CoreGraphicsBackend | CoreGraphicsAcceleratedBackend | SkiaBackend => {
                azure_native_font.mType = AZ_NATIVE_FONT_MAC_FONT_FACE;
                unsafe {
                    azure_native_font.mFont = cast::transmute(native_font.as_concrete_TypeRef());
                }
            }
            _ => {
                fail!(~"don't know how to make a scaled font for this backend");
            }
        }

        unsafe {
            let azure_scaled_font = AzCreateScaledFontForNativeFont(&azure_native_font, size);
            ScaledFont {
                azure_scaled_font: azure_scaled_font
            }
        }
    }
}

// FIXME: Move this stuff to a rust-skia?
// FIXME: Demangle the names!!!
#[cfg(target_os="macos")]
#[link_args="-lskia"]
extern {
    pub fn _Z26SkCreateTypefaceFromCTFontPK8__CTFont(font: CTFontRef) -> *SkTypeface;
}

<|endoftext|>


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

use crate::{
    counters,
    errors::{is_internal_error, JsonRpcError},
    methods::{build_registry, JsonRpcRequest, JsonRpcService, RpcRegistry},
    response::JsonRpcResponse,
};
use futures::future::join_all;
use libra_config::config::{NodeConfig, RoleType};
use libra_logger::{debug, Level, Schema};
use libra_mempool::MempoolClientSender;
use libra_types::{chain_id::ChainId, ledger_info::LedgerInfoWithSignatures};
use rand::{rngs::OsRng, RngCore};
use serde_json::{map::Map, Value};
use std::{net::SocketAddr, sync::Arc};
use storage_interface::DbReader;
use tokio::runtime::{Builder, Runtime};
use warp::{
    reject::{self, Reject},
    Filter,
};

// Counter labels for runtime metrics
const LABEL_FAIL: &str = "fail";
const LABEL_SUCCESS: &str = "success";
const LABEL_BATCH: &str = "batch";
const LABEL_SINGLE: &str = "single";

#[derive(Schema)]
struct HttpRequestLog<'a> {
    #[schema(display)]
    remote_addr: Option<std::net::SocketAddr>,
    method: String,
    path: String,
    status: u16,
    referer: Option<&'a str>,
    user_agent: Option<&'a str>,
    #[schema(debug)]
    elapsed: std::time::Duration,
}

#[derive(Schema)]
struct RpcRequestLog {
    trace_id: u64,
    request: Value,
}

#[derive(Schema)]
struct RpcResponseLog<'a> {
    trace_id: u64,
    is_batch: bool,
    response_error: bool,
    response: &'a JsonRpcResponse,
}

#[macro_export]
macro_rules! log_response {
    ($trace_id: expr, $resp: expr, $is_batch: expr) => {
        let mut level = Level::Trace;
        if let Some(ref error) = $resp.error {
            if is_internal_error(&error.code) {
                level = Level::Error
            }
        }
        libra_logger::log!(
            level,
            RpcResponseLog {
                trace_id: $trace_id,
                is_batch: $is_batch,
                response_error: $resp.error.is_some(),
                response: $resp,
            }
        );
    };
}

/// Creates HTTP server (warp-based) that serves JSON RPC requests
/// Returns handle to corresponding Tokio runtime
pub fn bootstrap(
    address: SocketAddr,
    batch_size_limit: u16,
    page_size_limit: u16,
    content_len_limit: usize,
    libra_db: Arc<dyn DbReader>,
    mp_sender: MempoolClientSender,
    role: RoleType,
    chain_id: ChainId,
) -> Runtime {
    let runtime = Builder::new()
        .thread_name("json-rpc")
        .threaded_scheduler()
        .enable_all()
        .build()
        .expect("[json-rpc] failed to create runtime");

    let registry = Arc::new(build_registry());
    let service = JsonRpcService::new(
        libra_db,
        mp_sender,
        role,
        chain_id,
        batch_size_limit,
        page_size_limit,
    );

    let base_route = warp::any()
        .and(warp::post())
        .and(warp::header::exact("content-type", "application/json"))
        .and(warp::body::content_length_limit(content_len_limit as u64))
        .and(warp::body::json())
        .and(warp::any().map(move || service.clone()))
        .and(warp::any().map(move || Arc::clone(&registry)))
        .and_then(rpc_endpoint)
        .with(warp::log::custom(|info| {
            debug!(HttpRequestLog {
                remote_addr: info.remote_addr(),
                method: info.method().to_string(),
                path: info.path().to_string(),
                status: info.status().as_u16(),
                referer: info.referer(),
                user_agent: info.user_agent(),
                elapsed: info.elapsed(),
            })
        }))
        .with(warp::cors().allow_any_origin().allow_methods(vec!["POST"]));

    // For now we still allow user to use "/", but user should start to move to "/v1" soon
    let route_root = warp::path::end().and(base_route.clone());

    let route_v1 = warp::path::path("v1")
        .and(warp::path::end())
        .and(base_route);

    let health_route = warp::path!("-" / "healthy")
        .and(warp::path::end())
        .map(|| "libra-node:ok");

    let full_route = health_route.or(route_v1.or(route_root));

    // Ensure that we actually bind to the socket first before spawning the
    // server tasks. This helps in tests to prevent races where a client attempts
    // to make a request before the server task is actually listening on the
    // socket.
    //
    // Note: we need to enter the runtime context first to actually bind, since
    //       tokio TcpListener can only be bound inside a tokio context.
    let server = runtime.enter(move || warp::serve(full_route).bind(address));
    runtime.handle().spawn(server);
    runtime
}

/// Creates JSON RPC endpoint by given node config
pub fn bootstrap_from_config(
    config: &NodeConfig,
    chain_id: ChainId,
    libra_db: Arc<dyn DbReader>,
    mp_sender: MempoolClientSender,
) -> Runtime {
    bootstrap(
        config.rpc.address,
        config.rpc.batch_size_limit,
        config.rpc.page_size_limit,
        config.rpc.content_length_limit,
        libra_db,
        mp_sender,
        config.base.role,
        chain_id,
    )
}

/// JSON RPC entry point
/// Handles all incoming rpc requests
/// Performs routing based on methods defined in `registry`
pub(crate) async fn rpc_endpoint(
    data: Value,
    service: JsonRpcService,
    registry: Arc<RpcRegistry>,
) -> Result<Box<dyn warp::Reply>, warp::Rejection> {
    let label = match data {
        Value::Array(_) => LABEL_BATCH,
        _ => LABEL_SINGLE,
    };
    counters::RPC_REQUESTS.with_label_values(&[label]).inc();
    let timer = counters::RPC_REQUEST_LATENCY
        .with_label_values(&[label])
        .start_timer();
    let ret = rpc_endpoint_without_metrics(data, service, registry).await;
    timer.stop_and_record();
    ret
}

async fn rpc_endpoint_without_metrics(
    data: Value,
    service: JsonRpcService,
    registry: Arc<RpcRegistry>,
) -> Result<Box<dyn warp::Reply>, warp::Rejection> {
    // take snapshot of latest version of DB to be used across all requests, especially for batched requests
    let ledger_info = service
        .get_latest_ledger_info()
        .map_err(|_| reject::custom(DatabaseError))?;

    let mut rng = OsRng;
    let trace_id = rng.next_u64();
    debug!(RpcRequestLog {
        trace_id,
        request: data.clone(),
    });

    let resp = Ok(if let Value::Array(requests) = data {
        match service.validate_batch_size_limit(requests.len()) {
            Ok(_) => {
                // batch API call
                let futures = requests.into_iter().map(|req| {
                    rpc_request_handler(
                        req,
                        service.clone(),
                        Arc::clone(&registry),
                        ledger_info.clone(),
                        LABEL_BATCH,
                        trace_id,
                    )
                });
                let responses = join_all(futures).await;
                for resp in &responses {
                    log_response!(trace_id, &resp, true);
                }
                warp::reply::json(&responses)
            }
            Err(err) => {
                let mut resp = JsonRpcResponse::new(
                    service.chain_id(),
                    ledger_info.ledger_info().version(),
                    ledger_info.ledger_info().timestamp_usecs(),
                );
                set_response_error(&mut resp, err, LABEL_BATCH, "unknown");
                log_response!(trace_id, &resp, true);

                warp::reply::json(&resp)
            }
        }
    } else {
        // single API call
        let resp =
            rpc_request_handler(data, service, registry, ledger_info, LABEL_SINGLE, trace_id).await;
        log_response!(trace_id, &resp, false);

        warp::reply::json(&resp)
    });

    Ok(Box::new(resp) as Box<dyn warp::Reply>)
}

/// Handler of single RPC request
/// Performs validation and executes corresponding rpc handler
async fn rpc_request_handler(
    req: Value,
    service: JsonRpcService,
    registry: Arc<RpcRegistry>,
    ledger_info: LedgerInfoWithSignatures,
    request_type_label: &str,
    trace_id: u64,
) -> JsonRpcResponse {
    let request: Map<String, Value>;
    let mut response = JsonRpcResponse::new(
        service.chain_id(),
        ledger_info.ledger_info().version(),
        ledger_info.ledger_info().timestamp_usecs(),
    );

    match req {
        Value::Object(data) => {
            request = data;
        }
        _ => {
            set_response_error(
                &mut response,
                JsonRpcError::invalid_format(),
                request_type_label,
                "unknown",
            );
            return response;
        }
    }

    // parse request id
    match parse_request_id(&request) {
        Ok(request_id) => {
            response.id = Some(request_id);
        }
        Err(err) => {
            set_response_error(&mut response, err, request_type_label, "unknown");
            return response;
        }
    };

    // verify protocol version
    if let Err(err) = verify_protocol(&request) {
        set_response_error(&mut response, err, request_type_label, "unknown");
        return response;
    }

    // parse parameters
    let params;
    match request.get("params") {
        Some(Value::Array(parameters)) => {
            params = parameters.to_vec();
        }
        _ => {
            set_response_error(
                &mut response,
                JsonRpcError::invalid_params(None),
                request_type_label,
                "unknown",
            );
            return response;
        }
    }

    let request_params = JsonRpcRequest {
        trace_id,
        ledger_info,
        params,
    };
    // get rpc handler
    match request.get("method") {
        Some(Value::String(name)) => match registry.get(name) {
            Some(handler) => {
                let timer = counters::METHOD_LATENCY
                    .with_label_values(&[request_type_label, name])
                    .start_timer();
                match handler(service, request_params).await {
                    Ok(result) => {
                        response.result = Some(result);
                        counters::REQUESTS
                            .with_label_values(&[request_type_label, name, LABEL_SUCCESS])
                            .inc();
                    }
                    Err(err) => {
                        // check for custom error
                        set_response_error(
                            &mut response,
                            err.downcast_ref::<JsonRpcError>()
                                .cloned()
                                .unwrap_or_else(|| JsonRpcError::internal_error(err.to_string())),
                            request_type_label,
                            &name,
                        );
                        counters::REQUESTS
                            .with_label_values(&[request_type_label, name, LABEL_FAIL])
                            .inc();
                    }
                }
                timer.stop_and_record();
            }
            None => set_response_error(
                &mut response,
                JsonRpcError::method_not_found(),
                request_type_label,
                "not_found",
            ),
        },
        _ => set_response_error(
            &mut response,
            JsonRpcError::method_not_found(),
            request_type_label,
            "not_found",
        ),
    }

    response
}

// Sets the JSON RPC error value for a given response.
// If a counter label is supplied, also increments the invalid request counter using the label,
fn set_response_error(
    response: &mut JsonRpcResponse,
    error: JsonRpcError,
    request_type: &str,
    method: &str,
) {
    let err_code = error.code;
    if is_internal_error(&error.code) {
        counters::INTERNAL_ERRORS
            .with_label_values(&[request_type, method, &err_code.to_string()])
            .inc();
    } else {
        let label = match err_code {
            -32600 => "invalid_request",
            -32601 => "method_not_found",
            -32602 => "invalid_params",
            -32604 => "invalid_format",
            _ => "unexpected_code",
        };
        counters::INVALID_REQUESTS
            .with_label_values(&[request_type, method, label])
            .inc();
    }

    response.error = Some(error);
}

fn parse_request_id(request: &Map<String, Value>) -> Result<Value, JsonRpcError> {
    match request.get("id") {
        Some(req_id) => {
            if req_id.is_string() || req_id.is_number() || req_id.is_null() {
                Ok(req_id.clone())
            } else {
                Err(JsonRpcError::invalid_format())
            }
        }
        None => Ok(Value::Null),
    }
}

fn verify_protocol(request: &Map<String, Value>) -> Result<(), JsonRpcError> {
    if let Some(Value::String(protocol)) = request.get("jsonrpc") {
        if protocol == "2.0" {
            return Ok(());
        }
    }
    Err(JsonRpcError::invalid_request())
}

/// Warp rejection types
#[derive(Debug)]
struct DatabaseError;

impl Reject for DatabaseError {}
<commit_msg>[json-rpc] add header forwarded into http request log<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    counters,
    errors::{is_internal_error, JsonRpcError},
    methods::{build_registry, JsonRpcRequest, JsonRpcService, RpcRegistry},
    response::JsonRpcResponse,
};
use futures::future::join_all;
use libra_config::config::{NodeConfig, RoleType};
use libra_logger::{debug, Level, Schema};
use libra_mempool::MempoolClientSender;
use libra_types::{chain_id::ChainId, ledger_info::LedgerInfoWithSignatures};
use rand::{rngs::OsRng, RngCore};
use serde_json::{map::Map, Value};
use std::{net::SocketAddr, sync::Arc};
use storage_interface::DbReader;
use tokio::runtime::{Builder, Runtime};
use warp::{
    http::header,
    reject::{self, Reject},
    Filter,
};

// Counter labels for runtime metrics
const LABEL_FAIL: &str = "fail";
const LABEL_SUCCESS: &str = "success";
const LABEL_BATCH: &str = "batch";
const LABEL_SINGLE: &str = "single";

#[derive(Schema)]
struct HttpRequestLog<'a> {
    #[schema(display)]
    remote_addr: Option<std::net::SocketAddr>,
    method: String,
    path: String,
    status: u16,
    referer: Option<&'a str>,
    user_agent: Option<&'a str>,
    #[schema(debug)]
    elapsed: std::time::Duration,
    forwarded: Option<&'a str>,
}

#[derive(Schema)]
struct RpcRequestLog {
    trace_id: u64,
    request: Value,
}

#[derive(Schema)]
struct RpcResponseLog<'a> {
    trace_id: u64,
    is_batch: bool,
    response_error: bool,
    response: &'a JsonRpcResponse,
}

#[macro_export]
macro_rules! log_response {
    ($trace_id: expr, $resp: expr, $is_batch: expr) => {
        let mut level = Level::Trace;
        if let Some(ref error) = $resp.error {
            if is_internal_error(&error.code) {
                level = Level::Error
            }
        }
        libra_logger::log!(
            level,
            RpcResponseLog {
                trace_id: $trace_id,
                is_batch: $is_batch,
                response_error: $resp.error.is_some(),
                response: $resp,
            }
        );
    };
}

/// Creates HTTP server (warp-based) that serves JSON RPC requests
/// Returns handle to corresponding Tokio runtime
pub fn bootstrap(
    address: SocketAddr,
    batch_size_limit: u16,
    page_size_limit: u16,
    content_len_limit: usize,
    libra_db: Arc<dyn DbReader>,
    mp_sender: MempoolClientSender,
    role: RoleType,
    chain_id: ChainId,
) -> Runtime {
    let runtime = Builder::new()
        .thread_name("json-rpc")
        .threaded_scheduler()
        .enable_all()
        .build()
        .expect("[json-rpc] failed to create runtime");

    let registry = Arc::new(build_registry());
    let service = JsonRpcService::new(
        libra_db,
        mp_sender,
        role,
        chain_id,
        batch_size_limit,
        page_size_limit,
    );

    let base_route = warp::any()
        .and(warp::post())
        .and(warp::header::exact("content-type", "application/json"))
        .and(warp::body::content_length_limit(content_len_limit as u64))
        .and(warp::body::json())
        .and(warp::any().map(move || service.clone()))
        .and(warp::any().map(move || Arc::clone(&registry)))
        .and_then(rpc_endpoint)
        .with(warp::log::custom(|info| {
            debug!(HttpRequestLog {
                remote_addr: info.remote_addr(),
                method: info.method().to_string(),
                path: info.path().to_string(),
                status: info.status().as_u16(),
                referer: info.referer(),
                user_agent: info.user_agent(),
                elapsed: info.elapsed(),
                forwarded: info
                    .request_headers()
                    .get(header::FORWARDED)
                    .and_then(|v| v.to_str().ok())
            })
        }))
        .with(warp::cors().allow_any_origin().allow_methods(vec!["POST"]));

    // For now we still allow user to use "/", but user should start to move to "/v1" soon
    let route_root = warp::path::end().and(base_route.clone());

    let route_v1 = warp::path::path("v1")
        .and(warp::path::end())
        .and(base_route);

    let health_route = warp::path!("-" / "healthy")
        .and(warp::path::end())
        .map(|| "libra-node:ok");

    let full_route = health_route.or(route_v1.or(route_root));

    // Ensure that we actually bind to the socket first before spawning the
    // server tasks. This helps in tests to prevent races where a client attempts
    // to make a request before the server task is actually listening on the
    // socket.
    //
    // Note: we need to enter the runtime context first to actually bind, since
    //       tokio TcpListener can only be bound inside a tokio context.
    let server = runtime.enter(move || warp::serve(full_route).bind(address));
    runtime.handle().spawn(server);
    runtime
}

/// Creates JSON RPC endpoint by given node config
pub fn bootstrap_from_config(
    config: &NodeConfig,
    chain_id: ChainId,
    libra_db: Arc<dyn DbReader>,
    mp_sender: MempoolClientSender,
) -> Runtime {
    bootstrap(
        config.rpc.address,
        config.rpc.batch_size_limit,
        config.rpc.page_size_limit,
        config.rpc.content_length_limit,
        libra_db,
        mp_sender,
        config.base.role,
        chain_id,
    )
}

/// JSON RPC entry point
/// Handles all incoming rpc requests
/// Performs routing based on methods defined in `registry`
pub(crate) async fn rpc_endpoint(
    data: Value,
    service: JsonRpcService,
    registry: Arc<RpcRegistry>,
) -> Result<Box<dyn warp::Reply>, warp::Rejection> {
    let label = match data {
        Value::Array(_) => LABEL_BATCH,
        _ => LABEL_SINGLE,
    };
    counters::RPC_REQUESTS.with_label_values(&[label]).inc();
    let timer = counters::RPC_REQUEST_LATENCY
        .with_label_values(&[label])
        .start_timer();
    let ret = rpc_endpoint_without_metrics(data, service, registry).await;
    timer.stop_and_record();
    ret
}

async fn rpc_endpoint_without_metrics(
    data: Value,
    service: JsonRpcService,
    registry: Arc<RpcRegistry>,
) -> Result<Box<dyn warp::Reply>, warp::Rejection> {
    // take snapshot of latest version of DB to be used across all requests, especially for batched requests
    let ledger_info = service
        .get_latest_ledger_info()
        .map_err(|_| reject::custom(DatabaseError))?;

    let mut rng = OsRng;
    let trace_id = rng.next_u64();
    debug!(RpcRequestLog {
        trace_id,
        request: data.clone(),
    });

    let resp = Ok(if let Value::Array(requests) = data {
        match service.validate_batch_size_limit(requests.len()) {
            Ok(_) => {
                // batch API call
                let futures = requests.into_iter().map(|req| {
                    rpc_request_handler(
                        req,
                        service.clone(),
                        Arc::clone(&registry),
                        ledger_info.clone(),
                        LABEL_BATCH,
                        trace_id,
                    )
                });
                let responses = join_all(futures).await;
                for resp in &responses {
                    log_response!(trace_id, &resp, true);
                }
                warp::reply::json(&responses)
            }
            Err(err) => {
                let mut resp = JsonRpcResponse::new(
                    service.chain_id(),
                    ledger_info.ledger_info().version(),
                    ledger_info.ledger_info().timestamp_usecs(),
                );
                set_response_error(&mut resp, err, LABEL_BATCH, "unknown");
                log_response!(trace_id, &resp, true);

                warp::reply::json(&resp)
            }
        }
    } else {
        // single API call
        let resp =
            rpc_request_handler(data, service, registry, ledger_info, LABEL_SINGLE, trace_id).await;
        log_response!(trace_id, &resp, false);

        warp::reply::json(&resp)
    });

    Ok(Box::new(resp) as Box<dyn warp::Reply>)
}

/// Handler of single RPC request
/// Performs validation and executes corresponding rpc handler
async fn rpc_request_handler(
    req: Value,
    service: JsonRpcService,
    registry: Arc<RpcRegistry>,
    ledger_info: LedgerInfoWithSignatures,
    request_type_label: &str,
    trace_id: u64,
) -> JsonRpcResponse {
    let request: Map<String, Value>;
    let mut response = JsonRpcResponse::new(
        service.chain_id(),
        ledger_info.ledger_info().version(),
        ledger_info.ledger_info().timestamp_usecs(),
    );

    match req {
        Value::Object(data) => {
            request = data;
        }
        _ => {
            set_response_error(
                &mut response,
                JsonRpcError::invalid_format(),
                request_type_label,
                "unknown",
            );
            return response;
        }
    }

    // parse request id
    match parse_request_id(&request) {
        Ok(request_id) => {
            response.id = Some(request_id);
        }
        Err(err) => {
            set_response_error(&mut response, err, request_type_label, "unknown");
            return response;
        }
    };

    // verify protocol version
    if let Err(err) = verify_protocol(&request) {
        set_response_error(&mut response, err, request_type_label, "unknown");
        return response;
    }

    // parse parameters
    let params;
    match request.get("params") {
        Some(Value::Array(parameters)) => {
            params = parameters.to_vec();
        }
        _ => {
            set_response_error(
                &mut response,
                JsonRpcError::invalid_params(None),
                request_type_label,
                "unknown",
            );
            return response;
        }
    }

    let request_params = JsonRpcRequest {
        trace_id,
        ledger_info,
        params,
    };
    // get rpc handler
    match request.get("method") {
        Some(Value::String(name)) => match registry.get(name) {
            Some(handler) => {
                let timer = counters::METHOD_LATENCY
                    .with_label_values(&[request_type_label, name])
                    .start_timer();
                match handler(service, request_params).await {
                    Ok(result) => {
                        response.result = Some(result);
                        counters::REQUESTS
                            .with_label_values(&[request_type_label, name, LABEL_SUCCESS])
                            .inc();
                    }
                    Err(err) => {
                        // check for custom error
                        set_response_error(
                            &mut response,
                            err.downcast_ref::<JsonRpcError>()
                                .cloned()
                                .unwrap_or_else(|| JsonRpcError::internal_error(err.to_string())),
                            request_type_label,
                            &name,
                        );
                        counters::REQUESTS
                            .with_label_values(&[request_type_label, name, LABEL_FAIL])
                            .inc();
                    }
                }
                timer.stop_and_record();
            }
            None => set_response_error(
                &mut response,
                JsonRpcError::method_not_found(),
                request_type_label,
                "not_found",
            ),
        },
        _ => set_response_error(
            &mut response,
            JsonRpcError::method_not_found(),
            request_type_label,
            "not_found",
        ),
    }

    response
}

// Sets the JSON RPC error value for a given response.
// If a counter label is supplied, also increments the invalid request counter using the label,
fn set_response_error(
    response: &mut JsonRpcResponse,
    error: JsonRpcError,
    request_type: &str,
    method: &str,
) {
    let err_code = error.code;
    if is_internal_error(&error.code) {
        counters::INTERNAL_ERRORS
            .with_label_values(&[request_type, method, &err_code.to_string()])
            .inc();
    } else {
        let label = match err_code {
            -32600 => "invalid_request",
            -32601 => "method_not_found",
            -32602 => "invalid_params",
            -32604 => "invalid_format",
            _ => "unexpected_code",
        };
        counters::INVALID_REQUESTS
            .with_label_values(&[request_type, method, label])
            .inc();
    }

    response.error = Some(error);
}

fn parse_request_id(request: &Map<String, Value>) -> Result<Value, JsonRpcError> {
    match request.get("id") {
        Some(req_id) => {
            if req_id.is_string() || req_id.is_number() || req_id.is_null() {
                Ok(req_id.clone())
            } else {
                Err(JsonRpcError::invalid_format())
            }
        }
        None => Ok(Value::Null),
    }
}

fn verify_protocol(request: &Map<String, Value>) -> Result<(), JsonRpcError> {
    if let Some(Value::String(protocol)) = request.get("jsonrpc") {
        if protocol == "2.0" {
            return Ok(());
        }
    }
    Err(JsonRpcError::invalid_request())
}

/// Warp rejection types
#[derive(Debug)]
struct DatabaseError;

impl Reject for DatabaseError {}
<|endoftext|>