<commit_before>use std::rc::Rc;
use std::cell::{RefCell};

type Link<T> = Option<Rc<RefCell<Node<T>>>>;

pub struct DoubleLink<T> {
    head: Link<T>,
    tail: Link<T>,
}

pub struct Node<T> {
    prev: Link<T>,
    next: Link<T>,
    elem: T,
}

impl<T> Node<T> {
    fn new(elem: T) -> Rc<RefCell<Self>> {
        Rc::new(RefCell::new(Node {
            prev: None,
            next: None,
            elem: elem,
        }))
    }
}

impl<T> DoubleLink<T> {
    fn new() -> Self {
        DoubleLink {
            head: None,
            tail: None,
        }
    }

    fn push_head(&mut self, elem: T) -> &mut Self {
        let new_node = Node::new(elem);
        match self.head.take() {
            Some(prev_head) => {
                // set prev head's prev prop to new node
                prev_head.borrow_mut().prev = Some(new_node.clone());
                // set new_node's next prop to prev head
                new_node.borrow_mut().next = Some(prev_head);
                // set self's head to new node
                self.head = Some(new_node);
            },
            None => {
                self.head = Some(new_node.clone());
                self.tail = Some(new_node);
            }
        };
        self
    }

    fn push_tail(&mut self, elem: T) -> &mut Self {
        let new_node = Node::new(elem);
        match self.tail.take() {
            Some(prev_tail) => {
                prev_tail.borrow_mut().next = Some(new_node.clone());
                new_node.borrow_mut().prev = Some(prev_tail);
                self.tail = Some(new_node);
            },
            None => {
                self.head = Some(new_node.clone());
                self.tail = Some(new_node);
            }
        };
        self
    }

    fn pop_head(&mut self) -> Option<T> {
        self.head.take().map(|old_head| {
            match old_head.borrow_mut().next.take() {
                Some(new_head) => {
                    // remove the reference to the previous head.
                    new_head.borrow_mut().prev.take();
                    self.head = Some(new_head);
                },
                None => {
                    // If we've consumed the end of the list, we need to update the tail property. head has already been set to none via the 'take' call earlier.
                    self.tail.take();
                }
            };
            Rc::try_unwrap(old_head).ok().unwrap().into_inner().elem
        })
    }

    fn pop_tail(&mut self) -> Option<T> {
        self.tail.take().map(|old_tail| {
            match old_tail.borrow_mut().prev.take() {
                Some(new_tail) => {
                    new_tail.borrow_mut().next.take();
                    self.tail = Some(new_tail);
                },
                None => {
                    self.head.take();
                }
            };
            Rc::try_unwrap(old_tail).ok().unwrap().into_inner().elem
        })
    }
}

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

    #[test]
    fn basics() {
        let mut list = DoubleLink::new();

        // Check empty list behaves right
        assert_eq!(list.pop_head(), None);

        // Populate list
        list.push_head(1);
        list.push_head(2);
        list.push_head(3);

        // Check normal removal
        assert_eq!(list.pop_head(), Some(3));
        assert_eq!(list.pop_head(), Some(2));

        // Push some more just to make sure nothing's corrupted
        list.push_head(4);
        list.push_head(5);

        // Check normal removal
        assert_eq!(list.pop_head(), Some(5));
        assert_eq!(list.pop_head(), Some(4));

        // Check exhaustion
        assert_eq!(list.pop_head(), Some(1));
        assert_eq!(list.pop_head(), None);
    }

    #[test]
    fn backwards() {
        let mut list = DoubleLink::new();

        // Populate list
        list.push_tail(1);
        list.push_tail(2);
        list.push_tail(3);

        // check backwards removal
        assert_eq!(list.pop_tail(), Some(3));
        assert_eq!(list.pop_tail(), Some(2));

        // push after pops
        list.push_tail(4).push_tail(5);
        assert_eq!(list.pop_tail(), Some(5));
        assert_eq!(list.pop_tail(), Some(4));

        assert_eq!(list.pop_tail(), Some(1));
        assert_eq!(list.pop_tail(), None);
    }
}
<commit_msg>implements peek & peek mut<commit_after>use std::rc::Rc;
use std::cell::{Ref, RefMut, RefCell};

type Link<T> = Option<Rc<RefCell<Node<T>>>>;

pub struct DoubleLink<T> {
    head: Link<T>,
    tail: Link<T>,
}

pub struct Node<T> {
    prev: Link<T>,
    next: Link<T>,
    elem: T,
}

impl<T> Node<T> {
    fn new(elem: T) -> Rc<RefCell<Self>> {
        Rc::new(RefCell::new(Node {
            prev: None,
            next: None,
            elem: elem,
        }))
    }
}

impl<T> DoubleLink<T> {
    fn new() -> Self {
        DoubleLink {
            head: None,
            tail: None,
        }
    }

    fn push_head(&mut self, elem: T) -> &mut Self {
        let new_node = Node::new(elem);
        match self.head.take() {
            Some(prev_head) => {
                // set prev head's prev prop to new node
                prev_head.borrow_mut().prev = Some(new_node.clone());
                // set new_node's next prop to prev head
                new_node.borrow_mut().next = Some(prev_head);
                // set self's head to new node
                self.head = Some(new_node);
            }
            None => {
                self.head = Some(new_node.clone());
                self.tail = Some(new_node);
            }
        };
        self
    }

    fn push_tail(&mut self, elem: T) -> &mut Self {
        let new_node = Node::new(elem);
        match self.tail.take() {
            Some(prev_tail) => {
                prev_tail.borrow_mut().next = Some(new_node.clone());
                new_node.borrow_mut().prev = Some(prev_tail);
                self.tail = Some(new_node);
            }
            None => {
                self.head = Some(new_node.clone());
                self.tail = Some(new_node);
            }
        };
        self
    }

    fn pop_head(&mut self) -> Option<T> {
        self.head.take().map(|old_head| {
            match old_head.borrow_mut().next.take() {
                Some(new_head) => {
                    // remove the reference to the previous head.
                    new_head.borrow_mut().prev.take();
                    self.head = Some(new_head);
                }
                None => {
                    // If we've consumed the end of the list, we need to update the tail property.
                    // head has already been set to none via the 'take' call earlier.
                    self.tail.take();
                }
            };
            Rc::try_unwrap(old_head).ok().unwrap().into_inner().elem
        })
    }

    fn pop_tail(&mut self) -> Option<T> {
        self.tail.take().map(|old_tail| {
            match old_tail.borrow_mut().prev.take() {
                Some(new_tail) => {
                    new_tail.borrow_mut().next.take();
                    self.tail = Some(new_tail);
                }
                None => {
                    self.head.take();
                }
            };
            Rc::try_unwrap(old_tail).ok().unwrap().into_inner().elem
        })
    }

    fn peek_head(&mut self) -> Option<Ref<T>> {
        self.head.as_ref().map(|node| Ref::map(node.borrow(), |node| &node.elem))
    }

    fn peek_tail(&mut self) -> Option<Ref<T>> {
        self.tail.as_ref().map(|node| Ref::map(node.borrow(), |node| &node.elem))
    }

    fn peek_head_mut(&mut self) -> Option<RefMut<T>> {
        self.head.as_mut().map(|node| RefMut::map(node.borrow_mut(), |node| &mut node.elem))
    }

    fn peek_tail_mut(&mut self) -> Option<RefMut<T>> {
        self.tail.as_mut().map(|node| RefMut::map(node.borrow_mut(), |node| &mut node.elem))
    }
}

impl<T> Drop for DoubleLink<T> {
    fn drop(&mut self) {
        while self.pop_head().is_some() {}
    }
}

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

    #[test]
    fn basics() {
        let mut list = DoubleLink::new();

        // Check empty list behaves right
        assert_eq!(list.pop_head(), None);

        // Populate list
        list.push_head(1);
        list.push_head(2);
        list.push_head(3);

        // Check normal removal
        assert_eq!(list.pop_head(), Some(3));
        assert_eq!(list.pop_head(), Some(2));

        // Push some more just to make sure nothing's corrupted
        list.push_head(4);
        list.push_head(5);

        // Check normal removal
        assert_eq!(list.pop_head(), Some(5));
        assert_eq!(list.pop_head(), Some(4));

        // Check exhaustion
        assert_eq!(list.pop_head(), Some(1));
        assert_eq!(list.pop_head(), None);
    }

    #[test]
    fn backwards() {
        let mut list = DoubleLink::new();

        // Populate list
        list.push_tail(1);
        list.push_tail(2);
        list.push_tail(3);

        // check tailwards removal
        assert_eq!(list.pop_tail(), Some(3));
        assert_eq!(list.pop_tail(), Some(2));

        // push after pops
        list.push_tail(4).push_tail(5);
        assert_eq!(list.pop_tail(), Some(5));
        assert_eq!(list.pop_tail(), Some(4));

        assert_eq!(list.pop_tail(), Some(1));
        assert_eq!(list.pop_tail(), None);
    }

    #[test]
    fn peek() {
        let mut list = DoubleLink::new();
        assert!(list.peek_head().is_none());
        assert!(list.peek_tail().is_none());
        assert!(list.peek_head_mut().is_none());
        assert!(list.peek_tail_mut().is_none());

        list.push_head(1);
        list.push_head(2);
        list.push_head(3);

        assert_eq!(&*list.peek_head().unwrap(), &3);
        assert_eq!(&mut *list.peek_head_mut().unwrap(), &mut 3);
        assert_eq!(&*list.peek_tail().unwrap(), &1);
        assert_eq!(&mut *list.peek_tail_mut().unwrap(), &mut 1);
    }
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::ffi::{CStr, CString};
use std::os::raw::c_char;

use log::*;
use serde_json as json;
use serde_json::json;

use project::*;

const VERSION: &[u8] = b"0.1.0\0";

thread_local!(
    static PROJECT: RefCell<Option<Project>> = RefCell::new(None));

fn with_project<T>(func: &mut FnMut(&Project) -> T) -> T {
    PROJECT.with(|p| func(p.borrow().as_ref().unwrap()))
}

fn with_project_mut<T>(func: &mut FnMut(&mut Project) -> T) -> T {
    PROJECT.with(|p| func(p.borrow_mut().as_mut().unwrap()))
}

struct CallbackLogger {
    callback: extern "C" fn(line: *const c_char) -> (),
}

impl Log for CallbackLogger {
    fn enabled(&self, _metadata: &Metadata) -> bool {
        true
    }

    fn log(&self, record: &Record) {
        let line = format!(
            "[{}] {}: {}",
            time::strftime("%H:%M:%S.%f", &time::now()).expect("Failed to format time"),
            record.level(),
            record.args()
        );
        unsafe {
            (self.callback)(CString::from_vec_unchecked(line.into_bytes()).as_ptr());
        }
    }

    fn flush(&self) {}
}

#[no_mangle]
pub extern "C" fn version() -> *const c_char {
    unsafe {
        return CStr::from_bytes_with_nul_unchecked(VERSION).as_ptr();
    }
}

#[no_mangle]
pub extern "C" fn init(log_callback: extern "C" fn(line: *const c_char) -> ()) -> bool {
    log::set_boxed_logger(Box::new(CallbackLogger {
        callback: log_callback,
    }))
    .expect("Failed to set the logger");
    log::set_max_level(LevelFilter::Trace);
    trace!("Initialized logging");
    PROJECT.with(|p| *p.borrow_mut() = None);
    true
}

#[no_mangle]
pub extern "C" fn open_binary_file(path: *const c_char) -> bool {
    let path = match to_str(path) {
        Some(p) => p,
        None => return false,
    };
    trace!("open_binary_file: {}", path);
    let mut project = match Project::from_binary_file(path) {
        Ok(project) => project,
        Err(s) => {
            error!("{}", s);
            return false;
        }
    };
    project.analyze_asm();
    PROJECT.with(|p| *p.borrow_mut() = Some(project));
    trace!("open_binary_file: done");
    true
}

#[no_mangle]
pub extern "C" fn open_db(path: *const c_char) -> bool {
    let path = match to_str(path) {
        Some(p) => p,
        None => return false,
    };
    trace!("open_db: {}", path);
    let project = match Project::from_serialized_file(path) {
        Ok(project) => project,
        Err(s) => {
            error!("{}", s);
            return false;
        }
    };
    PROJECT.with(|p| *p.borrow_mut() = Some(project));
    trace!("open_db: done");
    true
}

#[no_mangle]
pub extern "C" fn save_db(path: *const c_char) {
    let path = match to_str(path) {
        Some(p) => p,
        None => return (),
    };
    trace!("save_db: {}", path);
    with_project(
        &mut |project| match Project::serialize_to_file(project, path) {
            Ok(()) => {}
            Err(s) => error!("{}", s),
        },
    );
    trace!("save_db: done");
}

#[no_mangle]
pub fn get_functions(ptr: *mut c_char, size: usize) {
    trace!("get_functions");
    with_project(&mut |project| {
        let mut functions = vec![];
        for (_, func) in project.functions_iter() {
            let mut callees = vec![];
            for callee in func.callees.iter() {
                callees.push(callee);
            }
            let mut callers = vec![];
            for caller in func.callers.iter() {
                callers.push(caller);
            }
            functions.push(json!({
                "address": func.address,
                "status": format!("{:?}", func.status),
                "callingConvention": format!("{:?}", func.calling_convention),
                "argumentsSize": func.arguments_size,
                "name": func.name,
                "callees": callees,
                "callers": callers,
            }));
        }
        copy_to_buffer(json::Value::Array(functions).to_string(), ptr, size);
    });
    trace!("get_functions: done");
}

#[no_mangle]
pub fn get_instructions(function: u64, ptr: *mut c_char, size: usize) {
    trace!("get_instructions: 0x{:x}", function);
    with_project(&mut |project| {
        let mut asm = vec![];
        for (_, insn) in project.graph().instructions_iter() {
            if function == 0 {
                asm.push(json!({
                    "address": insn.address,
                    "hex": project.graph().get_bytes_as_hex(insn),
                    "assembly": insn.assembly(),
                }));
            } else {
                match project.graph().get_extra_data(insn.address) {
                    Some(data) if data.function_address == function => {
                        asm.push(json!({
                            "address": insn.address,
                            "hex": project.graph().get_bytes_as_hex(insn),
                            "assembly": insn.assembly(),
                        }));
                    }
                    _ => {}
                }
            }
        }
        copy_to_buffer(json::Value::Array(asm).to_string(), ptr, size);
    });
    trace!("get_instructions: done");
}

#[no_mangle]
pub fn get_il(address: u64, ptr: *mut c_char, size: usize) -> bool {
    trace!("get_il: 0x{:x}", address);
    with_project(&mut |project| {
        let function = match project.get_function(address) {
            Some(f) => f,
            None => {
                error!("Failed to find a function with the given address");
                return false;
            }
        };
        match &function.il {
            Ok(il) => {
                let mut res = vec![];
                for (i, insn) in il.iter().enumerate() {
                    res.push(json!({
                        "address": i,
                        "text": insn.print_syntax(),
                    }));
                }
                copy_to_buffer(json::Value::Array(res).to_string(), ptr, size);
                trace!("get_il: done");
                true
            }
            Err(s) => {
                error!("get_il: ERROR: {}", s);
                false
            }
        }
    })
}

#[no_mangle]
pub fn decompile_il() {
    trace!("decompile_il");
    with_project_mut(&mut |project| {
        project.decompile_il();
    });
    trace!("decompile_il: done");
}

fn to_str<'a>(s: *const c_char) -> Option<&'a str> {
    let cstr = unsafe { CStr::from_ptr(s) };
    match cstr.to_str() {
        Ok(s) => Some(s),
        Err(_) => None,
    }
}

fn copy_to_buffer(s: String, buf: *mut c_char, size: usize) {
    let count = std::cmp::min(s.len() + 1, size);
    unsafe {
        let data = CString::from_vec_unchecked(s.into_bytes());
        std::ptr::copy_nonoverlapping(data.as_ptr(), buf, count - 1);
        std::slice::from_raw_parts_mut(buf, count)[count - 1] = 0;
    }
}
<commit_msg>Expose control flow analysis in the API<commit_after>use std::cell::RefCell;
use std::ffi::{CStr, CString};
use std::os::raw::c_char;

use log::*;
use serde_json as json;
use serde_json::json;

use flow_analyzer::Block;
use il::*;
use project::*;

const VERSION: &[u8] = b"0.1.0\0";

thread_local!(
    static PROJECT: RefCell<Option<Project>> = RefCell::new(None));

fn with_project<T>(func: &mut FnMut(&Project) -> T) -> T {
    PROJECT.with(|p| func(p.borrow().as_ref().unwrap()))
}

fn with_project_mut<T>(func: &mut FnMut(&mut Project) -> T) -> T {
    PROJECT.with(|p| func(p.borrow_mut().as_mut().unwrap()))
}

struct CallbackLogger {
    callback: extern "C" fn(line: *const c_char) -> (),
}

impl Log for CallbackLogger {
    fn enabled(&self, _metadata: &Metadata) -> bool {
        true
    }

    fn log(&self, record: &Record) {
        let line = format!(
            "[{}] {}: {}",
            time::strftime("%H:%M:%S.%f", &time::now()).expect("Failed to format time"),
            record.level(),
            record.args()
        );
        unsafe {
            (self.callback)(CString::from_vec_unchecked(line.into_bytes()).as_ptr());
        }
    }

    fn flush(&self) {}
}

#[no_mangle]
pub extern "C" fn version() -> *const c_char {
    unsafe {
        return CStr::from_bytes_with_nul_unchecked(VERSION).as_ptr();
    }
}

#[no_mangle]
pub extern "C" fn init(log_callback: extern "C" fn(line: *const c_char) -> ()) -> bool {
    log::set_boxed_logger(Box::new(CallbackLogger {
        callback: log_callback,
    }))
    .expect("Failed to set the logger");
    log::set_max_level(LevelFilter::Trace);
    trace!("Initialized logging");
    PROJECT.with(|p| *p.borrow_mut() = None);
    true
}

#[no_mangle]
pub extern "C" fn open_binary_file(path: *const c_char) -> bool {
    let path = match to_str(path) {
        Some(p) => p,
        None => return false,
    };
    trace!("open_binary_file: {}", path);
    let mut project = match Project::from_binary_file(path) {
        Ok(project) => project,
        Err(s) => {
            error!("{}", s);
            return false;
        }
    };
    project.analyze_asm();
    PROJECT.with(|p| *p.borrow_mut() = Some(project));
    trace!("open_binary_file: done");
    true
}

#[no_mangle]
pub extern "C" fn open_db(path: *const c_char) -> bool {
    let path = match to_str(path) {
        Some(p) => p,
        None => return false,
    };
    trace!("open_db: {}", path);
    let project = match Project::from_serialized_file(path) {
        Ok(project) => project,
        Err(s) => {
            error!("{}", s);
            return false;
        }
    };
    PROJECT.with(|p| *p.borrow_mut() = Some(project));
    trace!("open_db: done");
    true
}

#[no_mangle]
pub extern "C" fn save_db(path: *const c_char) {
    let path = match to_str(path) {
        Some(p) => p,
        None => return (),
    };
    trace!("save_db: {}", path);
    with_project(
        &mut |project| match Project::serialize_to_file(project, path) {
            Ok(()) => {}
            Err(s) => error!("{}", s),
        },
    );
    trace!("save_db: done");
}

#[no_mangle]
pub fn get_functions(ptr: *mut c_char, size: usize) {
    trace!("get_functions");
    with_project(&mut |project| {
        let mut functions = vec![];
        for (_, func) in project.functions_iter() {
            let mut callees = vec![];
            for callee in func.callees.iter() {
                callees.push(callee);
            }
            let mut callers = vec![];
            for caller in func.callers.iter() {
                callers.push(caller);
            }
            functions.push(json!({
                "address": func.address,
                "status": format!("{:?}", func.status),
                "callingConvention": format!("{:?}", func.calling_convention),
                "argumentsSize": func.arguments_size,
                "name": func.name,
                "callees": callees,
                "callers": callers,
            }));
        }
        copy_to_buffer(json::Value::Array(functions).to_string(), ptr, size);
    });
    trace!("get_functions: done");
}

#[no_mangle]
pub fn get_instructions(function: u64, ptr: *mut c_char, size: usize) {
    trace!("get_instructions: 0x{:x}", function);
    with_project(&mut |project| {
        let mut asm = vec![];
        for (_, insn) in project.graph().instructions_iter() {
            if function == 0 {
                asm.push(json!({
                    "address": insn.address,
                    "hex": project.graph().get_bytes_as_hex(insn),
                    "assembly": insn.assembly(),
                }));
            } else {
                match project.graph().get_extra_data(insn.address) {
                    Some(data) if data.function_address == function => {
                        asm.push(json!({
                            "address": insn.address,
                            "hex": project.graph().get_bytes_as_hex(insn),
                            "assembly": insn.assembly(),
                        }));
                    }
                    _ => {}
                }
            }
        }
        copy_to_buffer(json::Value::Array(asm).to_string(), ptr, size);
    });
    trace!("get_instructions: done");
}

#[no_mangle]
pub fn get_il(address: u64, ptr: *mut c_char, size: usize) -> bool {
    trace!("get_il: 0x{:x}", address);
    with_project(&mut |project| {
        let function = match project.get_function(address) {
            Some(f) => f,
            None => {
                error!("Failed to find a function with the given address");
                return false;
            }
        };
        match &function.il {
            Ok(il) => {
                let mut res = vec![];
                for (i, insn) in il.iter().enumerate() {
                    res.push(json!({
                        "address": i,
                        "text": insn.print_syntax(),
                    }));
                }
                copy_to_buffer(json::Value::Array(res).to_string(), ptr, size);
                trace!("get_il: done");
                true
            }
            Err(s) => {
                error!("get_il: ERROR: {}", s);
                false
            }
        }
    })
}

#[no_mangle]
pub fn get_flow_blocks(address: u64, ptr: *mut c_char, size: usize) -> bool {
    trace!("get_flow_blocks: 0x{:x}", address);
    with_project(&mut |project| {
        let function = match project.get_function(address) {
            Some(f) => f,
            None => {
                error!("Failed to find a function with the given address");
                return false;
            }
        };
        match &function.flow {
            Ok(flow) => {
                let res: serde_json::Value = flow.iter().map(|b| serialize_flow_block(b)).collect();
                copy_to_buffer(res.to_string(), ptr, size);
                trace!("get_il: done");
                true
            }
            Err(s) => {
                error!("get_il: ERROR: {}", s);
                false
            }
        }
    })
}

fn serialize_flow_block(block: &Block<ILOperand>) -> serde_json::Value {
    match block {
        Block::ConditionalBlock(block) => json!({
            "tag": "CONDITION",
            "condition": block.condition.iter().map(|i| i.print_syntax()).collect::<serde_json::Value>(),
            "trueBranch": block.true_branch.iter().map(|b| serialize_flow_block(b)).collect::<serde_json::Value>(),
            "falseBranch": block.false_branch.iter().map(|b| serialize_flow_block(b)).collect::<serde_json::Value>(),
        }),
        Block::DoWhileBlock(block) => json!({
            "tag": "DO_WHILE",
            "condition": block.condition.iter().map(|i| i.print_syntax()).collect::<serde_json::Value>(),
            "body": block.body.iter().map(|b| serialize_flow_block(b)).collect::<serde_json::Value>(),
        }),
        Block::ForBlock(block) => json!({
            "tag": "FOR",
            "condition": block.condition.iter().map(|i| i.print_syntax()).collect::<serde_json::Value>(),
            "modifier": block.modifier.iter().map(|i| i.print_syntax()).collect::<serde_json::Value>(),
            "body": block.body.iter().map(|b| serialize_flow_block(b)).collect::<serde_json::Value>(),
        }),
        Block::SequentialBlock(block) => json!({
            "tag": "SEQUENCE",
            "instructions": block.instructions.iter().map(|i| i.print_syntax()).collect::<serde_json::Value>(),
        }),
        Block::WhileBlock(block) => json!({
            "tag": "WHILE",
            "condition": block.condition.iter().map(|i| i.print_syntax()).collect::<serde_json::Value>(),
            "body": block.body.iter().map(|b| serialize_flow_block(b)).collect::<serde_json::Value>(),
        }),
    }
}

#[no_mangle]
pub fn decompile_il() {
    trace!("decompile_il");
    with_project_mut(&mut |project| {
        project.decompile_il();
    });
    trace!("decompile_il: done");
}

#[no_mangle]
pub fn analyze_control_flow() {
    trace!("analyze_control_flow");
    with_project_mut(&mut |project| {
        project.analyze_control_flow();
    });
    trace!("analyze_control_flow: done");
}

fn to_str<'a>(s: *const c_char) -> Option<&'a str> {
    let cstr = unsafe { CStr::from_ptr(s) };
    match cstr.to_str() {
        Ok(s) => Some(s),
        Err(_) => None,
    }
}

fn copy_to_buffer(s: String, buf: *mut c_char, size: usize) {
    let count = std::cmp::min(s.len() + 1, size);
    unsafe {
        let data = CString::from_vec_unchecked(s.into_bytes());
        std::ptr::copy_nonoverlapping(data.as_ptr(), buf, count - 1);
        std::slice::from_raw_parts_mut(buf, count)[count - 1] = 0;
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 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.

//! Various data structures used by the Rust compiler. The intention
//! is that code in here should be not be *specific* to rustc, so that
//! it can be easily unit tested and so forth.
//!
//! # Note
//!
//! This API is completely unstable and subject to change.

#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
      html_favicon_url = "https://www.rust-lang.org/favicon.ico",
      html_root_url = "https://doc.rust-lang.org/nightly/")]
#![deny(warnings)]

#![feature(collections_range)]
#![feature(nonzero)]
#![feature(unboxed_closures)]
#![feature(fn_traits)]
#![feature(unsize)]
#![feature(i128_type)]
#![feature(i128)]
#![feature(conservative_impl_trait)]
#![feature(specialization)]
#![feature(optin_builtin_traits)]
#![feature(underscore_lifetimes)]
#![feature(macro_vis_matcher)]
#![feature(allow_internal_unstable)]
#![feature(universal_impl_trait)]

#![cfg_attr(unix, feature(libc))]
#![cfg_attr(test, feature(test))]

extern crate core;
extern crate ena;
#[macro_use]
extern crate log;
extern crate serialize as rustc_serialize; // used by deriving
#[cfg(unix)]
extern crate libc;
extern crate parking_lot;
#[macro_use]
extern crate cfg_if;
extern crate stable_deref_trait;

pub use rustc_serialize::hex::ToHex;

pub mod array_vec;
pub mod accumulate_vec;
pub mod small_vec;
pub mod base_n;
pub mod bitslice;
pub mod bitvec;
pub mod graph;
pub mod indexed_set;
pub mod indexed_vec;
pub mod obligation_forest;
pub mod sip128;
pub mod snapshot_map;
pub use ena::snapshot_vec;
pub mod stable_hasher;
pub mod transitive_relation;
pub use ena::unify;
pub mod fx;
pub mod tuple_slice;
pub mod control_flow_graph;
pub mod flock;
pub mod sync;
pub mod owning_ref;

// See comments in src/librustc/lib.rs
#[doc(hidden)]
pub fn __noop_fix_for_27438() {}
<commit_msg>Add OnDrop<commit_after>// Copyright 2015 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.

//! Various data structures used by the Rust compiler. The intention
//! is that code in here should be not be *specific* to rustc, so that
//! it can be easily unit tested and so forth.
//!
//! # Note
//!
//! This API is completely unstable and subject to change.

#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
      html_favicon_url = "https://www.rust-lang.org/favicon.ico",
      html_root_url = "https://doc.rust-lang.org/nightly/")]
#![deny(warnings)]

#![feature(collections_range)]
#![feature(nonzero)]
#![feature(unboxed_closures)]
#![feature(fn_traits)]
#![feature(unsize)]
#![feature(i128_type)]
#![feature(i128)]
#![feature(conservative_impl_trait)]
#![feature(specialization)]
#![feature(optin_builtin_traits)]
#![feature(underscore_lifetimes)]
#![feature(macro_vis_matcher)]
#![feature(allow_internal_unstable)]
#![feature(universal_impl_trait)]

#![cfg_attr(unix, feature(libc))]
#![cfg_attr(test, feature(test))]

extern crate core;
extern crate ena;
#[macro_use]
extern crate log;
extern crate serialize as rustc_serialize; // used by deriving
#[cfg(unix)]
extern crate libc;
extern crate parking_lot;
#[macro_use]
extern crate cfg_if;
extern crate stable_deref_trait;

pub use rustc_serialize::hex::ToHex;

pub mod array_vec;
pub mod accumulate_vec;
pub mod small_vec;
pub mod base_n;
pub mod bitslice;
pub mod bitvec;
pub mod graph;
pub mod indexed_set;
pub mod indexed_vec;
pub mod obligation_forest;
pub mod sip128;
pub mod snapshot_map;
pub use ena::snapshot_vec;
pub mod stable_hasher;
pub mod transitive_relation;
pub use ena::unify;
pub mod fx;
pub mod tuple_slice;
pub mod control_flow_graph;
pub mod flock;
pub mod sync;
pub mod owning_ref;

pub struct OnDrop<F: Fn()>(pub F);

impl<F: Fn()> Drop for OnDrop<F> {
      fn drop(&mut self) {
            (self.0)();
      }
}

// See comments in src/librustc/lib.rs
#[doc(hidden)]
pub fn __noop_fix_for_27438() {}
<|endoftext|>


<commit_before>// force-host
// no-prefer-dynamic

#![crate_type="proc-macro"]
#![crate_name="some_macros"]

extern crate proc_macro;

use proc_macro::TokenStream;

// @has some_macros/index.html
// @has - '//h2' 'Macros'
// @has - '//h2' 'Attribute Macros'
// @has - '//h2' 'Derive Macros'
// @!has - '//h2' 'Functions'

// @has some_macros/all.html
// @has - '//a[@href="macro.some_proc_macro.html"]' 'some_proc_macro'
// @has - '//a[@href="attr.some_proc_attr.html"]' 'some_proc_attr'
// @has - '//a[@href="derive.SomeDerive.html"]' 'SomeDerive'
// @!has - '//a/@href' 'fn.some_proc_macro.html'
// @!has - '//a/@href' 'fn.some_proc_attr.html'
// @!has - '//a/@href' 'fn.some_derive.html'

// @has some_macros/index.html '//a/@href' 'macro.some_proc_macro.html'
// @!has - '//a/@href' 'fn.some_proc_macro.html'
// @has some_macros/macro.some_proc_macro.html
// @!has some_macros/fn.some_proc_macro.html
/// a proc-macro that swallows its input and does nothing.
#[proc_macro]
pub fn some_proc_macro(_input: TokenStream) -> TokenStream {
    TokenStream::new()
}

// @has some_macros/index.html '//a/@href' 'attr.some_proc_attr.html'
// @!has - '//a/@href' 'fn.some_proc_attr.html'
// @has some_macros/attr.some_proc_attr.html
// @!has some_macros/fn.some_proc_attr.html
/// a proc-macro attribute that passes its item through verbatim.
#[proc_macro_attribute]
pub fn some_proc_attr(_attr: TokenStream, item: TokenStream) -> TokenStream {
    item
}

// @has some_macros/index.html '//a/@href' 'derive.SomeDerive.html'
// @!has - '//a/@href' 'fn.some_derive.html'
// @has some_macros/derive.SomeDerive.html
// @!has some_macros/fn.some_derive.html
/// a derive attribute that adds nothing to its input.
#[proc_macro_derive(SomeDerive)]
pub fn some_derive(_item: TokenStream) -> TokenStream {
    TokenStream::new()
}

// @has some_macros/foo/index.html
pub mod foo {
    // @has - '//code' 'pub use some_proc_macro;'
    // @has - '//a/@href' '../../some_macros/macro.some_proc_macro.html'
    pub use some_proc_macro;
    // @has - '//code' 'pub use some_proc_attr;'
    // @has - '//a/@href' '../../some_macros/attr.some_proc_attr.html'
    pub use some_proc_attr;
    // @has - '//code' 'pub use some_derive;'
    // @has - '//a/@href' '../../some_macros/derive.SomeDerive.html'
    pub use some_derive;
}
<commit_msg>add intra-doc link test to proc-macro test<commit_after>// force-host
// no-prefer-dynamic

#![crate_type="proc-macro"]
#![crate_name="some_macros"]

// @has some_macros/index.html
// @has - '//a/[@href="attr.some_proc_attr.html"]' 'some_proc_attr'

//! include a link to [some_proc_attr] to make sure it works.

extern crate proc_macro;

use proc_macro::TokenStream;

// @has some_macros/index.html
// @has - '//h2' 'Macros'
// @has - '//h2' 'Attribute Macros'
// @has - '//h2' 'Derive Macros'
// @!has - '//h2' 'Functions'

// @has some_macros/all.html
// @has - '//a[@href="macro.some_proc_macro.html"]' 'some_proc_macro'
// @has - '//a[@href="attr.some_proc_attr.html"]' 'some_proc_attr'
// @has - '//a[@href="derive.SomeDerive.html"]' 'SomeDerive'
// @!has - '//a/@href' 'fn.some_proc_macro.html'
// @!has - '//a/@href' 'fn.some_proc_attr.html'
// @!has - '//a/@href' 'fn.some_derive.html'

// @has some_macros/index.html '//a/@href' 'macro.some_proc_macro.html'
// @!has - '//a/@href' 'fn.some_proc_macro.html'
// @has some_macros/macro.some_proc_macro.html
// @!has some_macros/fn.some_proc_macro.html
/// a proc-macro that swallows its input and does nothing.
#[proc_macro]
pub fn some_proc_macro(_input: TokenStream) -> TokenStream {
    TokenStream::new()
}

// @has some_macros/index.html '//a/@href' 'attr.some_proc_attr.html'
// @!has - '//a/@href' 'fn.some_proc_attr.html'
// @has some_macros/attr.some_proc_attr.html
// @!has some_macros/fn.some_proc_attr.html
/// a proc-macro attribute that passes its item through verbatim.
#[proc_macro_attribute]
pub fn some_proc_attr(_attr: TokenStream, item: TokenStream) -> TokenStream {
    item
}

// @has some_macros/index.html '//a/@href' 'derive.SomeDerive.html'
// @!has - '//a/@href' 'fn.some_derive.html'
// @has some_macros/derive.SomeDerive.html
// @!has some_macros/fn.some_derive.html
/// a derive attribute that adds nothing to its input.
#[proc_macro_derive(SomeDerive)]
pub fn some_derive(_item: TokenStream) -> TokenStream {
    TokenStream::new()
}

// @has some_macros/foo/index.html
pub mod foo {
    // @has - '//code' 'pub use some_proc_macro;'
    // @has - '//a/@href' '../../some_macros/macro.some_proc_macro.html'
    pub use some_proc_macro;
    // @has - '//code' 'pub use some_proc_attr;'
    // @has - '//a/@href' '../../some_macros/attr.some_proc_attr.html'
    pub use some_proc_attr;
    // @has - '//code' 'pub use some_derive;'
    // @has - '//a/@href' '../../some_macros/derive.SomeDerive.html'
    pub use some_derive;
}
<|endoftext|>


<commit_before>#![deny(rust_2018_idioms)]

use clap::{crate_version};

use std::env;
use std::path::{Path, PathBuf};

use clap::{App, ArgMatches, SubCommand, AppSettings};

use mdbook_1::{MDBook as MDBook1};
use mdbook_1::errors::{Result as Result1};

use mdbook::MDBook;
use mdbook::errors::{Result as Result3};

#[cfg(all(target_arch = "x86_64", target_os = "linux"))]
use mdbook::renderer::RenderContext;

#[cfg(all(target_arch = "x86_64", target_os = "linux"))]
use mdbook_linkcheck::{self, errors::BrokenLinks};
use failure::Error;

#[cfg(not(all(target_arch = "x86_64", target_os = "linux")))]
use failure::bail;

fn main() {
    let d_message = "-d, --dest-dir=[dest-dir]
'The output directory for your book{n}(Defaults to ./book when omitted)'";
    let dir_message = "[dir]
'A directory for your book{n}(Defaults to Current Directory when omitted)'";
    let vers_message = "-m, --mdbook-vers=[md-version]
'The version of mdbook to use for your book{n}(Defaults to 1 when omitted)'";

    let matches = App::new("rustbook")
                    .about("Build a book with mdBook")
                    .author("Steve Klabnik <steve@steveklabnik.com>")
                    .version(&*format!("v{}", crate_version!()))
                    .setting(AppSettings::SubcommandRequired)
                    .subcommand(SubCommand::with_name("build")
                        .about("Build the book from the markdown files")
                        .arg_from_usage(d_message)
                        .arg_from_usage(dir_message)
                        .arg_from_usage(vers_message))
                    .subcommand(SubCommand::with_name("linkcheck")
                        .about("Run linkcheck with mdBook 3")
                        .arg_from_usage(dir_message))
                    .get_matches();

    // Check which subcomamnd the user ran...
    match matches.subcommand() {
        ("build", Some(sub_matches)) => {
            match sub_matches.value_of("mdbook-vers") {
                None | Some("1") => {
                    if let Err(e) = build_1(sub_matches) {
                        eprintln!("Error: {}", e);

                        for cause in e.iter().skip(1) {
                            eprintln!("\tCaused By: {}", cause);
                        }

                        ::std::process::exit(101);
                    }
                }
                Some("2") | Some("3") => {
                    if let Err(e) = build(sub_matches) {
                        eprintln!("Error: {}", e);

                        for cause in e.iter().skip(1) {
                            eprintln!("\tCaused By: {}", cause);
                        }

                        ::std::process::exit(101);
                    }
                }
                _ => {
                    panic!("Invalid mdBook version! Select '1' or '2' or '3'");
                }
            };
        },
        ("linkcheck", Some(sub_matches)) => {
            if let Err(err) = linkcheck(sub_matches) {
                eprintln!("Error: {}", err);

                #[cfg(all(target_arch = "x86_64", target_os = "linux"))]
                {
                    if let Ok(broken_links) = err.downcast::<BrokenLinks>() {
                        for cause in broken_links.links().iter() {
                            eprintln!("\tCaused By: {}", cause);
                        }
                    }
                }

                ::std::process::exit(101);
            }
        },
        (_, _) => unreachable!(),
    };
}

#[cfg(all(target_arch = "x86_64", target_os = "linux"))]
pub fn linkcheck(args: &ArgMatches<'_>) -> Result<(), Error> {
    let book_dir = get_book_dir(args);
    let book = MDBook::load(&book_dir).unwrap();
    let cfg = book.config;
    let render_ctx = RenderContext::new(&book_dir, book.book, cfg, &book_dir);

    mdbook_linkcheck::check_links(&render_ctx)
}

#[cfg(not(all(target_arch = "x86_64", target_os = "linux")))]
pub fn linkcheck(_args: &ArgMatches<'_>) -> Result<(), Error> {
    bail!("mdbook-linkcheck only works on x86_64 linux targets.");
}

// Build command implementation
pub fn build_1(args: &ArgMatches<'_>) -> Result1<()> {
    let book_dir = get_book_dir(args);
    let mut book = MDBook1::load(&book_dir)?;

    // Set this to allow us to catch bugs in advance.
    book.config.build.create_missing = false;

    if let Some(dest_dir) = args.value_of("dest-dir") {
        book.config.build.build_dir = PathBuf::from(dest_dir);
    }

    book.build()?;

    Ok(())
}

// Build command implementation
pub fn build(args: &ArgMatches<'_>) -> Result3<()> {
    let book_dir = get_book_dir(args);
    let mut book = MDBook::load(&book_dir)?;

    // Set this to allow us to catch bugs in advance.
    book.config.build.create_missing = false;

    if let Some(dest_dir) = args.value_of("dest-dir") {
        book.config.build.build_dir = PathBuf::from(dest_dir);
    }

    book.build()?;

    Ok(())
}

fn get_book_dir(args: &ArgMatches<'_>) -> PathBuf {
    if let Some(dir) = args.value_of("dir") {
        // Check if path is relative from current dir, or absolute...
        let p = Path::new(dir);
        if p.is_relative() {
            env::current_dir().unwrap().join(dir)
        } else {
            p.to_path_buf()
        }
    } else {
        env::current_dir().unwrap()
    }
}
<commit_msg>Don't return an error from linkcheck when it's not supported'<commit_after>#![deny(rust_2018_idioms)]

use clap::{crate_version};

use std::env;
use std::path::{Path, PathBuf};

use clap::{App, ArgMatches, SubCommand, AppSettings};

use mdbook_1::{MDBook as MDBook1};
use mdbook_1::errors::{Result as Result1};

use mdbook::MDBook;
use mdbook::errors::{Result as Result3};

#[cfg(all(target_arch = "x86_64", target_os = "linux"))]
use mdbook::renderer::RenderContext;

#[cfg(all(target_arch = "x86_64", target_os = "linux"))]
use mdbook_linkcheck::{self, errors::BrokenLinks};
use failure::Error;

fn main() {
    let d_message = "-d, --dest-dir=[dest-dir]
'The output directory for your book{n}(Defaults to ./book when omitted)'";
    let dir_message = "[dir]
'A directory for your book{n}(Defaults to Current Directory when omitted)'";
    let vers_message = "-m, --mdbook-vers=[md-version]
'The version of mdbook to use for your book{n}(Defaults to 1 when omitted)'";

    let matches = App::new("rustbook")
                    .about("Build a book with mdBook")
                    .author("Steve Klabnik <steve@steveklabnik.com>")
                    .version(&*format!("v{}", crate_version!()))
                    .setting(AppSettings::SubcommandRequired)
                    .subcommand(SubCommand::with_name("build")
                        .about("Build the book from the markdown files")
                        .arg_from_usage(d_message)
                        .arg_from_usage(dir_message)
                        .arg_from_usage(vers_message))
                    .subcommand(SubCommand::with_name("linkcheck")
                        .about("Run linkcheck with mdBook 3")
                        .arg_from_usage(dir_message))
                    .get_matches();

    // Check which subcomamnd the user ran...
    match matches.subcommand() {
        ("build", Some(sub_matches)) => {
            match sub_matches.value_of("mdbook-vers") {
                None | Some("1") => {
                    if let Err(e) = build_1(sub_matches) {
                        eprintln!("Error: {}", e);

                        for cause in e.iter().skip(1) {
                            eprintln!("\tCaused By: {}", cause);
                        }

                        ::std::process::exit(101);
                    }
                }
                Some("2") | Some("3") => {
                    if let Err(e) = build(sub_matches) {
                        eprintln!("Error: {}", e);

                        for cause in e.iter().skip(1) {
                            eprintln!("\tCaused By: {}", cause);
                        }

                        ::std::process::exit(101);
                    }
                }
                _ => {
                    panic!("Invalid mdBook version! Select '1' or '2' or '3'");
                }
            };
        },
        ("linkcheck", Some(sub_matches)) => {
            if let Err(err) = linkcheck(sub_matches) {
                eprintln!("Error: {}", err);

                #[cfg(all(target_arch = "x86_64", target_os = "linux"))]
                {
                    if let Ok(broken_links) = err.downcast::<BrokenLinks>() {
                        for cause in broken_links.links().iter() {
                            eprintln!("\tCaused By: {}", cause);
                        }
                    }
                }

                ::std::process::exit(101);
            }
        },
        (_, _) => unreachable!(),
    };
}

#[cfg(all(target_arch = "x86_64", target_os = "linux"))]
pub fn linkcheck(args: &ArgMatches<'_>) -> Result<(), Error> {
    let book_dir = get_book_dir(args);
    let book = MDBook::load(&book_dir).unwrap();
    let cfg = book.config;
    let render_ctx = RenderContext::new(&book_dir, book.book, cfg, &book_dir);

    mdbook_linkcheck::check_links(&render_ctx)
}

#[cfg(not(all(target_arch = "x86_64", target_os = "linux")))]
pub fn linkcheck(_args: &ArgMatches<'_>) -> Result<(), Error> {
    println!("mdbook-linkcheck only works on x86_64 linux targets.");
    Ok(())
}

// Build command implementation
pub fn build_1(args: &ArgMatches<'_>) -> Result1<()> {
    let book_dir = get_book_dir(args);
    let mut book = MDBook1::load(&book_dir)?;

    // Set this to allow us to catch bugs in advance.
    book.config.build.create_missing = false;

    if let Some(dest_dir) = args.value_of("dest-dir") {
        book.config.build.build_dir = PathBuf::from(dest_dir);
    }

    book.build()?;

    Ok(())
}

// Build command implementation
pub fn build(args: &ArgMatches<'_>) -> Result3<()> {
    let book_dir = get_book_dir(args);
    let mut book = MDBook::load(&book_dir)?;

    // Set this to allow us to catch bugs in advance.
    book.config.build.create_missing = false;

    if let Some(dest_dir) = args.value_of("dest-dir") {
        book.config.build.build_dir = PathBuf::from(dest_dir);
    }

    book.build()?;

    Ok(())
}

fn get_book_dir(args: &ArgMatches<'_>) -> PathBuf {
    if let Some(dir) = args.value_of("dir") {
        // Check if path is relative from current dir, or absolute...
        let p = Path::new(dir);
        if p.is_relative() {
            env::current_dir().unwrap().join(dir)
        } else {
            p.to_path_buf()
        }
    } else {
        env::current_dir().unwrap()
    }
}
<|endoftext|>


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

const BUCKET_DEPTH : usize = 20;

/// Kademlia routing table, with 160 buckets of `BUCKET_DEPTH` (k) node
/// identifiers each.
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.
struct Bucket {
   entries  : VecDeque<NodeInfo>,
}

/// Represents a conflict derived from attempting to insert a node in a full
/// bucket.
struct EvictionConflict {
   evicted  : NodeInfo,
   inserted : NodeInfo
}

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

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.clone(),
         buckets : buckets,
         conflicts  : Vec::new()
      }
   }

   /// Inserts a node in the routing table. Employs least-recently-seen eviction
   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);
      }
   }

   /// 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
   }

   /// 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> {
       Hash160::xor_distance(&self.parent_key, key).height()
   }

}

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));
    }

    #[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);

       for i in 0..super::BUCKET_DEPTH {
          let mut key = Hash160::blank();
          key.raw[0] = i as u8;
          let info = NodeInfo {
             key  : key,
             ip   : any_ip.clone(),
             port : 0
          };
          table.insert_node(info);
       }
       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());
    }
}
<commit_msg>table lookup<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.
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>)
}

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, or a list of up to the K closest nodes;
   pub fn lookup(&self, key : &Hash160) -> LookupResult {
      unimplemented!();
   }

   /// 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
   }

   /// 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> {
       Hash160::xor_distance(&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));
    }

    fn fill_with_low_keys(table : &mut RoutingTable) {
       let any_ip = net::IpAddr::from_str("0.0.0.0").unwrap();
       for i in 0..super::BUCKET_DEPTH {
          let mut key = Hash160::blank();
          key.raw[0] = i as u8;
          let info = NodeInfo {
             key  : key,
             ip   : any_ip.clone(),
             port : 0
          };
          table.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);

       fill_with_low_keys(&mut table);
       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);

       fill_with_low_keys(&mut table);
       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), LookupResult::Found(node));
    }
}
<|endoftext|>


<commit_before>// Copyright 2012 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.

//! Managed vectors

use cast::transmute;
use kinds::Copy;
use iter;
use option::Option;
use ptr::addr_of;
use sys;
use uint;
use vec;

/// Code for dealing with @-vectors. This is pretty incomplete, and
/// contains a bunch of duplication from the code for ~-vectors.

pub mod rustrt {
    use libc;
    use sys;
    use vec;

    #[abi = "cdecl"]
    #[link_name = "rustrt"]
    pub extern {
        pub unsafe fn vec_reserve_shared_actual(++t: *sys::TypeDesc,
                                                ++v: **vec::raw::VecRepr,
                                                ++n: libc::size_t);
    }
}

/// Returns the number of elements the vector can hold without reallocating
#[inline(always)]
pub fn capacity<T>(v: @[T]) -> uint {
    unsafe {
        let repr: **raw::VecRepr =
            ::cast::reinterpret_cast(&addr_of(&v));
        (**repr).unboxed.alloc / sys::size_of::<T>()
    }
}

/**
 * Builds a vector by calling a provided function with an argument
 * function that pushes an element to the back of a vector.
 * This version takes an initial size for the vector.
 *
 * # Arguments
 *
 * * size - An initial size of the vector to reserve
 * * builder - A function that will construct the vector. It recieves
 *             as an argument a function that will push an element
 *             onto the vector being constructed.
 */
#[inline(always)]
pub fn build_sized<A>(size: uint, builder: &fn(push: &fn(v: A))) -> @[A] {
    let mut vec: @[A] = @[];
    unsafe { raw::reserve(&mut vec, size); }
    builder(|+x| unsafe { raw::push(&mut vec, x) });
    return unsafe { transmute(vec) };
}

/**
 * Builds a vector by calling a provided function with an argument
 * function that pushes an element to the back of a vector.
 *
 * # Arguments
 *
 * * builder - A function that will construct the vector. It recieves
 *             as an argument a function that will push an element
 *             onto the vector being constructed.
 */
#[inline(always)]
pub fn build<A>(builder: &fn(push: &fn(v: A))) -> @[A] {
    build_sized(4, builder)
}

/**
 * Builds a vector by calling a provided function with an argument
 * function that pushes an element to the back of a vector.
 * This version takes an initial size for the vector.
 *
 * # Arguments
 *
 * * size - An option, maybe containing initial size of the vector to reserve
 * * builder - A function that will construct the vector. It recieves
 *             as an argument a function that will push an element
 *             onto the vector being constructed.
 */
#[inline(always)]
pub fn build_sized_opt<A>(size: Option<uint>,
                          builder: &fn(push: &fn(v: A)))
                       -> @[A] {
    build_sized(size.get_or_default(4), builder)
}

// Appending
#[inline(always)]
pub fn append<T:Copy>(lhs: @[T], rhs: &const [T]) -> @[T] {
    do build_sized(lhs.len() + rhs.len()) |push| {
        for vec::each(lhs) |x| { push(*x); }
        for uint::range(0, rhs.len()) |i| { push(rhs[i]); }
    }
}

/// Apply a function to each element of a vector and return the results
pub fn map<T, U>(v: &[T], f: &fn(x: &T) -> U) -> @[U] {
    do build_sized(v.len()) |push| {
        for vec::each(v) |elem| {
            push(f(elem));
        }
    }
}

/**
 * Creates and initializes an immutable vector.
 *
 * Creates an immutable vector of size `n_elts` and initializes the elements
 * to the value returned by the function `op`.
 */
pub fn from_fn<T>(n_elts: uint, op: iter::InitOp<T>) -> @[T] {
    do build_sized(n_elts) |push| {
        let mut i: uint = 0u;
        while i < n_elts { push(op(i)); i += 1u; }
    }
}

/**
 * Creates and initializes an immutable vector.
 *
 * Creates an immutable vector of size `n_elts` and initializes the elements
 * to the value `t`.
 */
pub fn from_elem<T:Copy>(n_elts: uint, t: T) -> @[T] {
    do build_sized(n_elts) |push| {
        let mut i: uint = 0u;
        while i < n_elts { push(copy t); i += 1u; }
    }
}

/**
 * Creates and initializes an immutable managed vector by moving all the
 * elements from an owned vector.
 */
pub fn from_owned<T>(v: ~[T]) -> @[T] {
    let mut av = @[];
    unsafe {
        raw::reserve(&mut av, v.len());
        do vec::consume(v) |_i, x| {
            raw::push(&mut av, x);
        }
        transmute(av)
    }
}

/**
 * Creates and initializes an immutable managed vector by copying all the
 * elements of a slice.
 */
pub fn from_slice<T:Copy>(v: &[T]) -> @[T] {
    from_fn(v.len(), |i| v[i])
}

#[cfg(notest)]
pub mod traits {
    use at_vec::append;
    use kinds::Copy;
    use ops::Add;

    impl<'self,T:Copy> Add<&'self const [T],@[T]> for @[T] {
        #[inline(always)]
        fn add(&self, rhs: & &'self const [T]) -> @[T] {
            append(*self, (*rhs))
        }
    }
}

#[cfg(test)]
pub mod traits {}

pub mod raw {
    use at_vec::{capacity, rustrt};
    use cast::transmute;
    use libc;
    use unstable::intrinsics::{move_val_init};
    use ptr::addr_of;
    use ptr;
    use sys;
    use uint;
    use vec;

    pub type VecRepr = vec::raw::VecRepr;
    pub type SliceRepr = vec::raw::SliceRepr;

    /**
     * Sets the length of a vector
     *
     * This will explicitly set the size of the vector, without actually
     * modifing its buffers, so it is up to the caller to ensure that
     * the vector is actually the specified size.
     */
    #[inline(always)]
    pub unsafe fn set_len<T>(v: @[T], new_len: uint) {
        let repr: **mut VecRepr = ::cast::reinterpret_cast(&addr_of(&v));
        (**repr).unboxed.fill = new_len * sys::size_of::<T>();
    }

    #[inline(always)]
    pub unsafe fn push<T>(v: &mut @[T], initval: T) {
        let repr: **VecRepr = ::cast::reinterpret_cast(&v);
        let fill = (**repr).unboxed.fill;
        if (**repr).unboxed.alloc > fill {
            push_fast(v, initval);
        }
        else {
            push_slow(v, initval);
        }
    }

    #[inline(always)] // really pretty please
    pub unsafe fn push_fast<T>(v: &mut @[T], initval: T) {
        let repr: **mut VecRepr = ::cast::reinterpret_cast(&v);
        let fill = (**repr).unboxed.fill;
        (**repr).unboxed.fill += sys::size_of::<T>();
        let p = addr_of(&((**repr).unboxed.data));
        let p = ptr::offset(p, fill) as *mut T;
        move_val_init(&mut(*p), initval);
    }

    pub unsafe fn push_slow<T>(v: &mut @[T], initval: T) {
        reserve_at_least(&mut *v, v.len() + 1u);
        push_fast(v, initval);
    }

    /**
     * Reserves capacity for exactly `n` elements in the given vector.
     *
     * If the capacity for `v` is already equal to or greater than the
     * requested capacity, then no action is taken.
     *
     * # Arguments
     *
     * * v - A vector
     * * n - The number of elements to reserve space for
     */
    pub unsafe fn reserve<T>(v: &mut @[T], n: uint) {
        // Only make the (slow) call into the runtime if we have to
        if capacity(*v) < n {
            let ptr: **VecRepr = transmute(v);
            rustrt::vec_reserve_shared_actual(sys::get_type_desc::<T>(),
                                              ptr, n as libc::size_t);
        }
    }

    /**
     * Reserves capacity for at least `n` elements in the given vector.
     *
     * This function will over-allocate in order to amortize the
     * allocation costs in scenarios where the caller may need to
     * repeatedly reserve additional space.
     *
     * If the capacity for `v` is already equal to or greater than the
     * requested capacity, then no action is taken.
     *
     * # Arguments
     *
     * * v - A vector
     * * n - The number of elements to reserve space for
     */
    pub unsafe fn reserve_at_least<T>(v: &mut @[T], n: uint) {
        reserve(v, uint::next_power_of_two(n));
    }
}

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

    #[test]
    fn test() {
        // Some code that could use that, then:
        fn seq_range(lo: uint, hi: uint) -> @[uint] {
            do build |push| {
                for uint::range(lo, hi) |i| {
                    push(i);
                }
            }
        }

        assert_eq!(seq_range(10, 15), @[10, 11, 12, 13, 14]);
        assert!(from_fn(5, |x| x+1) == @[1, 2, 3, 4, 5]);
        assert!(from_elem(5, 3.14) == @[3.14, 3.14, 3.14, 3.14, 3.14]);
    }

    #[test]
    fn append_test() {
        assert!(@[1,2,3] + @[4,5,6] == @[1,2,3,4,5,6]);
    }

    #[test]
    fn test_from_owned() {
        assert!(from_owned::<int>(~[]) == @[]);
        assert!(from_owned(~[true]) == @[true]);
        assert!(from_owned(~[1, 2, 3, 4, 5]) == @[1, 2, 3, 4, 5]);
        assert!(from_owned(~[~"abc", ~"123"]) == @[~"abc", ~"123"]);
        assert!(from_owned(~[~[42]]) == @[~[42]]);
    }

    #[test]
    fn test_from_slice() {
        assert!(from_slice::<int>([]) == @[]);
        assert!(from_slice([true]) == @[true]);
        assert!(from_slice([1, 2, 3, 4, 5]) == @[1, 2, 3, 4, 5]);
        assert!(from_slice([@"abc", @"123"]) == @[@"abc", @"123"]);
        assert!(from_slice([@[42]]) == @[@[42]]);
    }
}<commit_msg>libcore: missed an import for a test<commit_after>// Copyright 2012 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.

//! Managed vectors

use cast::transmute;
use kinds::Copy;
use iter;
use option::Option;
use ptr::addr_of;
use sys;
use uint;
use vec;

/// Code for dealing with @-vectors. This is pretty incomplete, and
/// contains a bunch of duplication from the code for ~-vectors.

pub mod rustrt {
    use libc;
    use sys;
    use vec;

    #[abi = "cdecl"]
    #[link_name = "rustrt"]
    pub extern {
        pub unsafe fn vec_reserve_shared_actual(++t: *sys::TypeDesc,
                                                ++v: **vec::raw::VecRepr,
                                                ++n: libc::size_t);
    }
}

/// Returns the number of elements the vector can hold without reallocating
#[inline(always)]
pub fn capacity<T>(v: @[T]) -> uint {
    unsafe {
        let repr: **raw::VecRepr =
            ::cast::reinterpret_cast(&addr_of(&v));
        (**repr).unboxed.alloc / sys::size_of::<T>()
    }
}

/**
 * Builds a vector by calling a provided function with an argument
 * function that pushes an element to the back of a vector.
 * This version takes an initial size for the vector.
 *
 * # Arguments
 *
 * * size - An initial size of the vector to reserve
 * * builder - A function that will construct the vector. It recieves
 *             as an argument a function that will push an element
 *             onto the vector being constructed.
 */
#[inline(always)]
pub fn build_sized<A>(size: uint, builder: &fn(push: &fn(v: A))) -> @[A] {
    let mut vec: @[A] = @[];
    unsafe { raw::reserve(&mut vec, size); }
    builder(|+x| unsafe { raw::push(&mut vec, x) });
    return unsafe { transmute(vec) };
}

/**
 * Builds a vector by calling a provided function with an argument
 * function that pushes an element to the back of a vector.
 *
 * # Arguments
 *
 * * builder - A function that will construct the vector. It recieves
 *             as an argument a function that will push an element
 *             onto the vector being constructed.
 */
#[inline(always)]
pub fn build<A>(builder: &fn(push: &fn(v: A))) -> @[A] {
    build_sized(4, builder)
}

/**
 * Builds a vector by calling a provided function with an argument
 * function that pushes an element to the back of a vector.
 * This version takes an initial size for the vector.
 *
 * # Arguments
 *
 * * size - An option, maybe containing initial size of the vector to reserve
 * * builder - A function that will construct the vector. It recieves
 *             as an argument a function that will push an element
 *             onto the vector being constructed.
 */
#[inline(always)]
pub fn build_sized_opt<A>(size: Option<uint>,
                          builder: &fn(push: &fn(v: A)))
                       -> @[A] {
    build_sized(size.get_or_default(4), builder)
}

// Appending
#[inline(always)]
pub fn append<T:Copy>(lhs: @[T], rhs: &const [T]) -> @[T] {
    do build_sized(lhs.len() + rhs.len()) |push| {
        for vec::each(lhs) |x| { push(*x); }
        for uint::range(0, rhs.len()) |i| { push(rhs[i]); }
    }
}

/// Apply a function to each element of a vector and return the results
pub fn map<T, U>(v: &[T], f: &fn(x: &T) -> U) -> @[U] {
    do build_sized(v.len()) |push| {
        for vec::each(v) |elem| {
            push(f(elem));
        }
    }
}

/**
 * Creates and initializes an immutable vector.
 *
 * Creates an immutable vector of size `n_elts` and initializes the elements
 * to the value returned by the function `op`.
 */
pub fn from_fn<T>(n_elts: uint, op: iter::InitOp<T>) -> @[T] {
    do build_sized(n_elts) |push| {
        let mut i: uint = 0u;
        while i < n_elts { push(op(i)); i += 1u; }
    }
}

/**
 * Creates and initializes an immutable vector.
 *
 * Creates an immutable vector of size `n_elts` and initializes the elements
 * to the value `t`.
 */
pub fn from_elem<T:Copy>(n_elts: uint, t: T) -> @[T] {
    do build_sized(n_elts) |push| {
        let mut i: uint = 0u;
        while i < n_elts { push(copy t); i += 1u; }
    }
}

/**
 * Creates and initializes an immutable managed vector by moving all the
 * elements from an owned vector.
 */
pub fn from_owned<T>(v: ~[T]) -> @[T] {
    let mut av = @[];
    unsafe {
        raw::reserve(&mut av, v.len());
        do vec::consume(v) |_i, x| {
            raw::push(&mut av, x);
        }
        transmute(av)
    }
}

/**
 * Creates and initializes an immutable managed vector by copying all the
 * elements of a slice.
 */
pub fn from_slice<T:Copy>(v: &[T]) -> @[T] {
    from_fn(v.len(), |i| v[i])
}

#[cfg(notest)]
pub mod traits {
    use at_vec::append;
    use kinds::Copy;
    use ops::Add;

    impl<'self,T:Copy> Add<&'self const [T],@[T]> for @[T] {
        #[inline(always)]
        fn add(&self, rhs: & &'self const [T]) -> @[T] {
            append(*self, (*rhs))
        }
    }
}

#[cfg(test)]
pub mod traits {}

pub mod raw {
    use at_vec::{capacity, rustrt};
    use cast::transmute;
    use libc;
    use unstable::intrinsics::{move_val_init};
    use ptr::addr_of;
    use ptr;
    use sys;
    use uint;
    use vec;

    pub type VecRepr = vec::raw::VecRepr;
    pub type SliceRepr = vec::raw::SliceRepr;

    /**
     * Sets the length of a vector
     *
     * This will explicitly set the size of the vector, without actually
     * modifing its buffers, so it is up to the caller to ensure that
     * the vector is actually the specified size.
     */
    #[inline(always)]
    pub unsafe fn set_len<T>(v: @[T], new_len: uint) {
        let repr: **mut VecRepr = ::cast::reinterpret_cast(&addr_of(&v));
        (**repr).unboxed.fill = new_len * sys::size_of::<T>();
    }

    #[inline(always)]
    pub unsafe fn push<T>(v: &mut @[T], initval: T) {
        let repr: **VecRepr = ::cast::reinterpret_cast(&v);
        let fill = (**repr).unboxed.fill;
        if (**repr).unboxed.alloc > fill {
            push_fast(v, initval);
        }
        else {
            push_slow(v, initval);
        }
    }

    #[inline(always)] // really pretty please
    pub unsafe fn push_fast<T>(v: &mut @[T], initval: T) {
        let repr: **mut VecRepr = ::cast::reinterpret_cast(&v);
        let fill = (**repr).unboxed.fill;
        (**repr).unboxed.fill += sys::size_of::<T>();
        let p = addr_of(&((**repr).unboxed.data));
        let p = ptr::offset(p, fill) as *mut T;
        move_val_init(&mut(*p), initval);
    }

    pub unsafe fn push_slow<T>(v: &mut @[T], initval: T) {
        reserve_at_least(&mut *v, v.len() + 1u);
        push_fast(v, initval);
    }

    /**
     * Reserves capacity for exactly `n` elements in the given vector.
     *
     * If the capacity for `v` is already equal to or greater than the
     * requested capacity, then no action is taken.
     *
     * # Arguments
     *
     * * v - A vector
     * * n - The number of elements to reserve space for
     */
    pub unsafe fn reserve<T>(v: &mut @[T], n: uint) {
        // Only make the (slow) call into the runtime if we have to
        if capacity(*v) < n {
            let ptr: **VecRepr = transmute(v);
            rustrt::vec_reserve_shared_actual(sys::get_type_desc::<T>(),
                                              ptr, n as libc::size_t);
        }
    }

    /**
     * Reserves capacity for at least `n` elements in the given vector.
     *
     * This function will over-allocate in order to amortize the
     * allocation costs in scenarios where the caller may need to
     * repeatedly reserve additional space.
     *
     * If the capacity for `v` is already equal to or greater than the
     * requested capacity, then no action is taken.
     *
     * # Arguments
     *
     * * v - A vector
     * * n - The number of elements to reserve space for
     */
    pub unsafe fn reserve_at_least<T>(v: &mut @[T], n: uint) {
        reserve(v, uint::next_power_of_two(n));
    }
}

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

    #[test]
    fn test() {
        // Some code that could use that, then:
        fn seq_range(lo: uint, hi: uint) -> @[uint] {
            do build |push| {
                for uint::range(lo, hi) |i| {
                    push(i);
                }
            }
        }

        assert_eq!(seq_range(10, 15), @[10, 11, 12, 13, 14]);
        assert!(from_fn(5, |x| x+1) == @[1, 2, 3, 4, 5]);
        assert!(from_elem(5, 3.14) == @[3.14, 3.14, 3.14, 3.14, 3.14]);
    }

    #[test]
    fn append_test() {
        assert!(@[1,2,3] + @[4,5,6] == @[1,2,3,4,5,6]);
    }

    #[test]
    fn test_from_owned() {
        assert!(from_owned::<int>(~[]) == @[]);
        assert!(from_owned(~[true]) == @[true]);
        assert!(from_owned(~[1, 2, 3, 4, 5]) == @[1, 2, 3, 4, 5]);
        assert!(from_owned(~[~"abc", ~"123"]) == @[~"abc", ~"123"]);
        assert!(from_owned(~[~[42]]) == @[~[42]]);
    }

    #[test]
    fn test_from_slice() {
        assert!(from_slice::<int>([]) == @[]);
        assert!(from_slice([true]) == @[true]);
        assert!(from_slice([1, 2, 3, 4, 5]) == @[1, 2, 3, 4, 5]);
        assert!(from_slice([@"abc", @"123"]) == @[@"abc", @"123"]);
        assert!(from_slice([@[42]]) == @[@[42]]);
    }
}<|endoftext|>


<commit_before>//! Shared Vectors

import ptr::addr_of;

export init_op;
export capacity;
export build_sized, build;
export map;
export from_fn, from_elem;
export unsafe;

/// Code for dealing with @-vectors. This is pretty incomplete, and
/// contains a bunch of duplication from the code for ~-vectors.

#[abi = "cdecl"]
extern mod rustrt {
    fn vec_reserve_shared_actual(++t: *sys::type_desc,
                                 ++v: **vec::unsafe::vec_repr,
                                 ++n: libc::size_t);
}

#[abi = "rust-intrinsic"]
extern mod rusti {
    fn move_val_init<T>(&dst: T, -src: T);
}

/// A function used to initialize the elements of a vector
type init_op<T> = fn(uint) -> T;

/// Returns the number of elements the vector can hold without reallocating
#[inline(always)]
pure fn capacity<T>(&&v: @[const T]) -> uint {
    unsafe {
        let repr: **unsafe::vec_repr =
            ::unsafe::reinterpret_cast(addr_of(v));
        (**repr).alloc / sys::size_of::<T>()
    }
}

/**
 * Builds a vector by calling a provided function with an argument
 * function that pushes an element to the back of a vector.
 * This version takes an initial size for the vector.
 *
 * # Arguments
 *
 * * size - An initial size of the vector to reserve
 * * builder - A function that will construct the vector. It recieves
 *             as an argument a function that will push an element
 *             onto the vector being constructed.
 */
#[inline(always)]
pure fn build_sized<A>(size: uint, builder: fn(push: pure fn(+A))) -> @[A] {
    let mut vec = @[];
    unsafe {
        unsafe::reserve(vec, size);
        // This is an awful hack to be able to make the push function
        // pure. Is there a better way?
        ::unsafe::reinterpret_cast::
            <fn(push: pure fn(+A)), fn(push: fn(+A))>
            (builder)(|+x| unsafe::push(vec, x));
    }
    ret vec;
}

/**
 * Builds a vector by calling a provided function with an argument
 * function that pushes an element to the back of a vector.
 *
 * # Arguments
 *
 * * builder - A function that will construct the vector. It recieves
 *             as an argument a function that will push an element
 *             onto the vector being constructed.
 */
#[inline(always)]
pure fn build<A>(builder: fn(push: pure fn(+A))) -> @[A] {
    build_sized(4, builder)
}

/// Apply a function to each element of a vector and return the results
pure fn map<T, U>(v: &[T], f: fn(T) -> U) -> @[U] {
    do build_sized(v.len()) |push| {
        for vec::each(v) |elem| {
            push(f(elem));
        }
    }
}

/**
 * Creates and initializes an immutable vector.
 *
 * Creates an immutable vector of size `n_elts` and initializes the elements
 * to the value returned by the function `op`.
 */
pure fn from_fn<T>(n_elts: uint, op: init_op<T>) -> @[T] {
    do build_sized(n_elts) |push| {
        let mut i: uint = 0u;
        while i < n_elts { push(op(i)); i += 1u; }
    }
}

/**
 * Creates and initializes an immutable vector.
 *
 * Creates an immutable vector of size `n_elts` and initializes the elements
 * to the value `t`.
 */
pure fn from_elem<T: copy>(n_elts: uint, t: T) -> @[T] {
    do build_sized(n_elts) |push| {
        let mut i: uint = 0u;
        while i < n_elts { push(t); i += 1u; }
    }
}

mod unsafe {
    type vec_repr = vec::unsafe::vec_repr;
    type slice_repr = vec::unsafe::slice_repr;

    /**
     * Sets the length of a vector
     *
     * This will explicitly set the size of the vector, without actually
     * modifing its buffers, so it is up to the caller to ensure that
     * the vector is actually the specified size.
     */
    #[inline(always)]
    unsafe fn set_len<T>(&&v: @[const T], new_len: uint) {
        let repr: **vec_repr = ::unsafe::reinterpret_cast(addr_of(v));
        (**repr).fill = new_len * sys::size_of::<T>();
    }

    /// Append an element to a vector
    #[inline(always)]
    unsafe fn push<T>(&v: @[const T], +initval: T) {
        let repr: **vec_repr = ::unsafe::reinterpret_cast(addr_of(v));
        let fill = (**repr).fill;
        if (**repr).alloc > fill {
            (**repr).fill += sys::size_of::<T>();
            let p = addr_of((**repr).data);
            let p = ptr::offset(p, fill) as *mut T;
            rusti::move_val_init(*p, initval);
        }
        else {
            push_slow(v, initval);
        }
    }
    unsafe fn push_slow<T>(&v: @[const T], +initval: T) {
        reserve_at_least(v, v.len() + 1u);
        push(v, initval);
    }
    /**
     * Reserves capacity for exactly `n` elements in the given vector.
     *
     * If the capacity for `v` is already equal to or greater than the
     * requested capacity, then no action is taken.
     *
     * # Arguments
     *
     * * v - A vector
     * * n - The number of elements to reserve space for
     */
    unsafe fn reserve<T>(&v: @[const T], n: uint) {
        // Only make the (slow) call into the runtime if we have to
        if capacity(v) < n {
            let ptr = addr_of(v) as **vec_repr;
            rustrt::vec_reserve_shared_actual(sys::get_type_desc::<T>(),
                                              ptr, n as libc::size_t);
        }
    }

    /**
     * Reserves capacity for at least `n` elements in the given vector.
     *
     * This function will over-allocate in order to amortize the
     * allocation costs in scenarios where the caller may need to
     * repeatedly reserve additional space.
     *
     * If the capacity for `v` is already equal to or greater than the
     * requested capacity, then no action is taken.
     *
     * # Arguments
     *
     * * v - A vector
     * * n - The number of elements to reserve space for
     */
    unsafe fn reserve_at_least<T>(&v: @[const T], n: uint) {
        reserve(v, uint::next_power_of_two(n));
    }

}
<commit_msg>Make at_vec push functions more like the current vec ones.<commit_after>//! Shared Vectors

import ptr::addr_of;

export init_op;
export capacity;
export build_sized, build;
export map;
export from_fn, from_elem;
export unsafe;

/// Code for dealing with @-vectors. This is pretty incomplete, and
/// contains a bunch of duplication from the code for ~-vectors.

#[abi = "cdecl"]
extern mod rustrt {
    fn vec_reserve_shared_actual(++t: *sys::type_desc,
                                 ++v: **vec::unsafe::vec_repr,
                                 ++n: libc::size_t);
}

#[abi = "rust-intrinsic"]
extern mod rusti {
    fn move_val_init<T>(&dst: T, -src: T);
}

/// A function used to initialize the elements of a vector
type init_op<T> = fn(uint) -> T;

/// Returns the number of elements the vector can hold without reallocating
#[inline(always)]
pure fn capacity<T>(&&v: @[const T]) -> uint {
    unsafe {
        let repr: **unsafe::vec_repr =
            ::unsafe::reinterpret_cast(addr_of(v));
        (**repr).alloc / sys::size_of::<T>()
    }
}

/**
 * Builds a vector by calling a provided function with an argument
 * function that pushes an element to the back of a vector.
 * This version takes an initial size for the vector.
 *
 * # Arguments
 *
 * * size - An initial size of the vector to reserve
 * * builder - A function that will construct the vector. It recieves
 *             as an argument a function that will push an element
 *             onto the vector being constructed.
 */
#[inline(always)]
pure fn build_sized<A>(size: uint, builder: fn(push: pure fn(+A))) -> @[A] {
    let mut vec = @[];
    unsafe {
        unsafe::reserve(vec, size);
        // This is an awful hack to be able to make the push function
        // pure. Is there a better way?
        ::unsafe::reinterpret_cast::
            <fn(push: pure fn(+A)), fn(push: fn(+A))>
            (builder)(|+x| unsafe::push(vec, x));
    }
    ret vec;
}

/**
 * Builds a vector by calling a provided function with an argument
 * function that pushes an element to the back of a vector.
 *
 * # Arguments
 *
 * * builder - A function that will construct the vector. It recieves
 *             as an argument a function that will push an element
 *             onto the vector being constructed.
 */
#[inline(always)]
pure fn build<A>(builder: fn(push: pure fn(+A))) -> @[A] {
    build_sized(4, builder)
}

/// Apply a function to each element of a vector and return the results
pure fn map<T, U>(v: &[T], f: fn(T) -> U) -> @[U] {
    do build_sized(v.len()) |push| {
        for vec::each(v) |elem| {
            push(f(elem));
        }
    }
}

/**
 * Creates and initializes an immutable vector.
 *
 * Creates an immutable vector of size `n_elts` and initializes the elements
 * to the value returned by the function `op`.
 */
pure fn from_fn<T>(n_elts: uint, op: init_op<T>) -> @[T] {
    do build_sized(n_elts) |push| {
        let mut i: uint = 0u;
        while i < n_elts { push(op(i)); i += 1u; }
    }
}

/**
 * Creates and initializes an immutable vector.
 *
 * Creates an immutable vector of size `n_elts` and initializes the elements
 * to the value `t`.
 */
pure fn from_elem<T: copy>(n_elts: uint, t: T) -> @[T] {
    do build_sized(n_elts) |push| {
        let mut i: uint = 0u;
        while i < n_elts { push(t); i += 1u; }
    }
}

mod unsafe {
    type vec_repr = vec::unsafe::vec_repr;
    type slice_repr = vec::unsafe::slice_repr;

    /**
     * Sets the length of a vector
     *
     * This will explicitly set the size of the vector, without actually
     * modifing its buffers, so it is up to the caller to ensure that
     * the vector is actually the specified size.
     */
    #[inline(always)]
    unsafe fn set_len<T>(&&v: @[const T], new_len: uint) {
        let repr: **vec_repr = ::unsafe::reinterpret_cast(addr_of(v));
        (**repr).fill = new_len * sys::size_of::<T>();
    }

    #[inline(always)]
    unsafe fn push<T>(&v: @[const T], +initval: T) {
        let repr: **vec_repr = ::unsafe::reinterpret_cast(addr_of(v));
        let fill = (**repr).fill;
        if (**repr).alloc > fill {
            push_fast(v, initval);
        }
        else {
            push_slow(v, initval);
        }
    }
    // This doesn't bother to make sure we have space.
    #[inline(always)] // really pretty please
    unsafe fn push_fast<T>(&v: @[const T], +initval: T) {
        let repr: **vec_repr = ::unsafe::reinterpret_cast(addr_of(v));
        let fill = (**repr).fill;
        (**repr).fill += sys::size_of::<T>();
        let p = ptr::addr_of((**repr).data);
        let p = ptr::offset(p, fill) as *mut T;
        rusti::move_val_init(*p, initval);
    }

    unsafe fn push_slow<T>(&v: @[const T], +initval: T) {
        reserve_at_least(v, v.len() + 1u);
        push_fast(v, initval);
    }

    /**
     * Reserves capacity for exactly `n` elements in the given vector.
     *
     * If the capacity for `v` is already equal to or greater than the
     * requested capacity, then no action is taken.
     *
     * # Arguments
     *
     * * v - A vector
     * * n - The number of elements to reserve space for
     */
    unsafe fn reserve<T>(&v: @[const T], n: uint) {
        // Only make the (slow) call into the runtime if we have to
        if capacity(v) < n {
            let ptr = addr_of(v) as **vec_repr;
            rustrt::vec_reserve_shared_actual(sys::get_type_desc::<T>(),
                                              ptr, n as libc::size_t);
        }
    }

    /**
     * Reserves capacity for at least `n` elements in the given vector.
     *
     * This function will over-allocate in order to amortize the
     * allocation costs in scenarios where the caller may need to
     * repeatedly reserve additional space.
     *
     * If the capacity for `v` is already equal to or greater than the
     * requested capacity, then no action is taken.
     *
     * # Arguments
     *
     * * v - A vector
     * * n - The number of elements to reserve space for
     */
    unsafe fn reserve_at_least<T>(&v: @[const T], n: uint) {
        reserve(v, uint::next_power_of_two(n));
    }

}
<|endoftext|>