<commit_before>// See FIPS Publication 46-3, Appendix 1

pub const INITIAL_PERMUTATION: [u8; 64] = [
    58, 50, 42, 34, 26, 18, 10, 2,
    60, 52, 44, 36, 28, 20, 12, 4,
    62, 54, 46, 38, 30, 22, 14, 6,
    64, 56, 48, 40, 32, 24, 16, 8,
    57, 49, 41, 33, 25, 17, 9,  1,
    59, 51, 43, 35, 27, 19, 11, 3,
    61, 53, 45, 37, 29, 21, 13, 5,
    63, 55, 47, 39, 31, 23, 15, 7
];

pub const FINAL_PERMUTATION: [u8; 64] = [
   40, 8, 48, 16, 56, 24, 64, 32,
   39, 7, 47, 15, 55, 23, 63, 31,
   38, 6, 46, 14, 54, 22, 62, 30,
   37, 5, 45, 13, 53, 21, 61, 29,
   36, 4, 44, 12, 52, 20, 60, 28,
   35, 3, 43, 11, 51, 19, 59, 27,
   34, 2, 42, 10, 50, 18, 58, 26,
   33, 1, 41, 9,  49, 17, 57, 25
];

pub const EXPANSION_PERMUTATION: [u8; 48] = [
    32, 1,  2,  3,  4,  5,
    4,  5,  6,  7,  8,  9,
    8,  9,  10, 11, 12, 13,
    12, 13, 14, 15, 16, 17,
    16, 17, 18, 19, 20, 21,
    20, 21, 22, 23, 24, 25,
    24, 25, 26, 27, 28, 29,
    28, 29, 30, 31, 32, 1
];

pub const ROUND_PERMUTATION: [u8; 32] = [
    16, 7,  20, 21, 29, 12, 28, 17,
    1,  15, 23, 26, 5,  18, 31, 10,
    2,  8,  24, 14, 32, 27, 3,  9,
    19, 13, 30, 6,  22, 11, 4,  25
];

// NB: These are in lexicographic order, not the outer-bit major order given in the spec.
pub const SUBSTITUTIONS: [[u8; 64]; 8] = [
    [
        14, 0,
        4,  15,
        13, 7,
        1,  4,
        2,  14,
        15, 2,
        11, 13,
        8,  1,
        3,  10,
        10, 6,
        6,  12,
        12, 11,
        5,  9,
        9,  5,
        0,  3,
        7,  8,

        4,  15,
        1,  12,
        14, 8,
        8,  2,
        13, 4,
        6,  9,
        2,  1,
        11, 7,
        15, 5,
        12, 11,
        9,  3,
        7,  14,
        3,  10,
        10, 0,
        5,  6,
        0,  13,
    ],
    [
        15, 3,
        1, 13,
        8, 4,
        14, 7,
        6, 15,
        11, 2,
        3, 8,
        4, 14,
        9, 12,
        7, 0,
        2, 1,
        13, 10,
        12, 6,
        0, 9,
        5, 11,
        10, 5,

        0, 13,
        14, 8,
        7, 10,
        11, 1,
        10, 3,
        4, 15,
        13, 4,
        1, 2,
        5, 11,
        8, 6,
        12, 7,
        6, 12,
        9, 0,
        3, 5,
        2, 14,
        15, 9,
    ],
    [
        10, 13,
        0, 7,
        9, 0,
        14, 9,
        6, 3,
        3, 4,
        15, 6,
        5, 10,
        1, 2,
        13, 8,
        12, 5,
        7, 14,
        11, 12,
        4, 11,
        2, 15,
        8, 1,

        13, 1,
        6, 10,
        4, 13,
        9, 0,
        8, 6,
        15, 9,
        3, 8,
        0, 7,
        11, 4,
        1, 15,
        2, 14,
        12, 3,
        5, 11,
        10, 5,
        14, 2,
        7, 12,
    ],
    [
        7, 13,
        13, 8,
        14, 11,
        3, 9,
        0, 3,
        6, 4,
        9, 6,
        10, 10,
        1, 2,
        2, 8,
        8, 5,
        5, 14,
        11, 12,
        12, 11,
        4, 15,
        15, 1,

        10, 3,
        6, 15,
        9, 0,
        9, 6,
        8, 10,
        15, 1,
        3, 13,
        0, 8,
        11, 9,
        1, 4,
        2, 5,
        12, 11,
        5, 12,
        10, 7,
        14, 2,
        7, 14,
    ],
    [
        2, 14,
        12, 11,
        4, 2,
        1, 9,
        7, 3,
        10, 4,
        11, 6,
        6, 10,
        8, 2,
        5, 8,
        3, 5,
        15, 14,
        13, 12,
        0, 11,
        14, 15,
        9, 1,

        4, 11,
        2, 8,
        1, 12,
        9, 7,
        8, 1,
        15, 14,
        3, 2,
        0, 13,
        11, 6,
        1, 15,
        2, 0,
        12, 9,
        5, 10,
        10, 4,
        14, 5,
        7, 3,
    ],
    [
        12, 10,
        1, 15,
        10, 4,
        15, 9,
        9, 3,
        2, 4,
        6, 6,
        8, 10,
        0, 2,
        13, 8,
        3, 5,
        4, 14,
        14, 12,
        7, 11,
        5, 15,
        11, 1,

        9, 4,
        14, 3,
        15, 2,
        9, 12,
        8, 9,
        15, 5,
        3, 15,
        0, 10,
        11, 11,
        1, 14,
        2, 1,
        12, 7,
        5, 6,
        10, 0,
        14, 8,
        7, 13,

    ],
    [
        4, 13,
        11, 0,
        2, 11,
        14, 9,
        15, 3,
        0, 4,
        8, 6,
        13, 10,
        3, 2,
        12, 8,
        9, 5,
        7, 14,
        5, 12,
        10, 11,
        6, 15,
        1, 1,

        1, 6,
        4, 11,
        11, 13,
        9, 8,
        8, 1,
        15, 4,
        3, 10,
        0, 7,
        11, 9,
        1, 5,
        2, 0,
        12, 15,
        5, 14,
        10, 2,
        14, 3,
        7, 12,
    ],
    [
        13, 1,
        2, 15,
        8, 13,
        4, 9,
        6, 3,
        15, 4,
        11, 6,
        1, 10,
        10, 2,
        9, 8,
        3, 5,
        14, 14,
        5, 12,
        0, 11,
        12, 15,
        7, 1,

        7, 2,
        11, 1,
        4, 14,
        9, 7,
        8, 4,
        15, 10,
        3, 8,
        0, 13,
        11, 15,
        1, 12,
        2, 9,
        12, 0,
        5, 3,
        10, 5,
        14, 6,
        7, 11,
    ]
];
<commit_msg>Align the DES substitution tables<commit_after>// See FIPS Publication 46-3, Appendix 1

pub const INITIAL_PERMUTATION: [u8; 64] = [
    58, 50, 42, 34, 26, 18, 10, 2,
    60, 52, 44, 36, 28, 20, 12, 4,
    62, 54, 46, 38, 30, 22, 14, 6,
    64, 56, 48, 40, 32, 24, 16, 8,
    57, 49, 41, 33, 25, 17, 9,  1,
    59, 51, 43, 35, 27, 19, 11, 3,
    61, 53, 45, 37, 29, 21, 13, 5,
    63, 55, 47, 39, 31, 23, 15, 7
];

pub const FINAL_PERMUTATION: [u8; 64] = [
   40, 8, 48, 16, 56, 24, 64, 32,
   39, 7, 47, 15, 55, 23, 63, 31,
   38, 6, 46, 14, 54, 22, 62, 30,
   37, 5, 45, 13, 53, 21, 61, 29,
   36, 4, 44, 12, 52, 20, 60, 28,
   35, 3, 43, 11, 51, 19, 59, 27,
   34, 2, 42, 10, 50, 18, 58, 26,
   33, 1, 41, 9,  49, 17, 57, 25
];

pub const EXPANSION_PERMUTATION: [u8; 48] = [
    32, 1,  2,  3,  4,  5,
    4,  5,  6,  7,  8,  9,
    8,  9,  10, 11, 12, 13,
    12, 13, 14, 15, 16, 17,
    16, 17, 18, 19, 20, 21,
    20, 21, 22, 23, 24, 25,
    24, 25, 26, 27, 28, 29,
    28, 29, 30, 31, 32, 1
];

pub const ROUND_PERMUTATION: [u8; 32] = [
    16, 7,  20, 21, 29, 12, 28, 17,
    1,  15, 23, 26, 5,  18, 31, 10,
    2,  8,  24, 14, 32, 27, 3,  9,
    19, 13, 30, 6,  22, 11, 4,  25
];

// NB: These are in lexicographic order, not the outer-bit major order given in the spec.
pub const SUBSTITUTIONS: [[u8; 64]; 8] = [
    [
        14, 0,
        4,  15,
        13, 7,
        1,  4,
        2,  14,
        15, 2,
        11, 13,
        8,  1,
        3,  10,
        10, 6,
        6,  12,
        12, 11,
        5,  9,
        9,  5,
        0,  3,
        7,  8,

        4,  15,
        1,  12,
        14, 8,
        8,  2,
        13, 4,
        6,  9,
        2,  1,
        11, 7,
        15, 5,
        12, 11,
        9,  3,
        7,  14,
        3,  10,
        10, 0,
        5,  6,
        0,  13,
    ],
    [
        15, 3,
        1,  13,
        8,  4,
        14, 7,
        6,  15,
        11, 2,
        3,  8,
        4,  14,
        9,  12,
        7,  0,
        2,  1,
        13, 10,
        12, 6,
        0,  9,
        5,  11,
        10, 5,

        0,  13,
        14, 8,
        7,  10,
        11, 1,
        10, 3,
        4,  15,
        13, 4,
        1,  2,
        5,  11,
        8,  6,
        12, 7,
        6,  12,
        9,  0,
        3,  5,
        2,  14,
        15, 9,
    ],
    [
        10, 13,
        0,  7,
        9,  0,
        14, 9,
        6,  3,
        3,  4,
        15, 6,
        5,  10,
        1,  2,
        13, 8,
        12, 5,
        7,  14,
        11, 12,
        4,  11,
        2,  15,
        8,  1,

        13, 1,
        6,  10,
        4,  13,
        9,  0,
        8,  6,
        15, 9,
        3,  8,
        0,  7,
        11, 4,
        1,  15,
        2,  14,
        12, 3,
        5,  11,
        10, 5,
        14, 2,
        7,  12,
    ],
    [
        7,  13,
        13, 8,
        14, 11,
        3,  9,
        0,  3,
        6,  4,
        9,  6,
        10, 10,
        1,  2,
        2,  8,
        8,  5,
        5,  14,
        11, 12,
        12, 11,
        4,  15,
        15, 1,

        10, 3,
        6,  15,
        9,  0,
        9,  6,
        8,  10,
        15, 1,
        3,  13,
        0,  8,
        11, 9,
        1,  4,
        2,  5,
        12, 11,
        5,  12,
        10, 7,
        14, 2,
        7,  14,
    ],
    [
        2,  14,
        12, 11,
        4,  2,
        1,  9,
        7,  3,
        10, 4,
        11, 6,
        6,  10,
        8,  2,
        5,  8,
        3,  5,
        15, 14,
        13, 12,
        0,  11,
        14, 15,
        9,  1,

        4,  11,
        2,  8,
        1,  12,
        9,  7,
        8,  1,
        15, 14,
        3,  2,
        0,  13,
        11, 6,
        1,  15,
        2,  0,
        12, 9,
        5,  10,
        10, 4,
        14, 5,
        7,  3,
    ],
    [
        12, 10,
        1,  15,
        10, 4,
        15, 9,
        9,  3,
        2,  4,
        6,  6,
        8,  10,
        0,  2,
        13, 8,
        3,  5,
        4,  14,
        14, 12,
        7,  11,
        5,  15,
        11, 1,

        9,  4,
        14, 3,
        15, 2,
        9,  12,
        8,  9,
        15, 5,
        3,  15,
        0,  10,
        11, 11,
        1,  14,
        2,  1,
        12, 7,
        5,  6,
        10, 0,
        14, 8,
        7,  13,

    ],
    [
        4,  13,
        11, 0,
        2,  11,
        14, 9,
        15, 3,
        0,  4,
        8,  6,
        13, 10,
        3,  2,
        12, 8,
        9,  5,
        7,  14,
        5,  12,
        10, 11,
        6,  15,
        1,  1,

        1,  6,
        4,  11,
        11, 13,
        9,  8,
        8,  1,
        15, 4,
        3,  10,
        0,  7,
        11, 9,
        1,  5,
        2,  0,
        12, 15,
        5,  14,
        10, 2,
        14, 3,
        7,  12,
    ],
    [
        13, 1,
        2,  15,
        8,  13,
        4,  9,
        6,  3,
        15, 4,
        11, 6,
        1,  10,
        10, 2,
        9,  8,
        3,  5,
        14, 14,
        5,  12,
        0,  11,
        12, 15,
        7,  1,

        7,  2,
        11, 1,
        4,  14,
        9,  7,
        8,  4,
        15, 10,
        3,  8,
        0,  13,
        11, 15,
        1,  12,
        2,  9,
        12, 0,
        5,  3,
        10, 5,
        14, 6,
        7,  11,
    ]
];
<|endoftext|>


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

#![allow(non_camel_case_types)]

use convert::TryInto;
use io;
use os::raw::c_char;
use u64;

use libc::{c_int, c_void};

pub type mx_handle_t = i32;
pub type mx_vaddr_t = usize;
pub type mx_rights_t = u32;
pub type mx_status_t = i32;

pub type mx_size_t = usize;

pub const MX_HANDLE_INVALID: mx_handle_t = 0;

pub type mx_time_t = u64;
pub const MX_TIME_INFINITE : mx_time_t = u64::MAX;

pub type mx_signals_t = u32;

pub const MX_OBJECT_SIGNAL_3         : mx_signals_t = 1 << 3;

pub const MX_TASK_TERMINATED        : mx_signals_t = MX_OBJECT_SIGNAL_3;

pub const MX_RIGHT_SAME_RIGHTS  : mx_rights_t = 1 << 31;

pub type mx_object_info_topic_t = u32;

pub const MX_INFO_PROCESS         : mx_object_info_topic_t = 3;

pub fn mx_cvt<T>(t: T) -> io::Result<T> where T: TryInto<mx_status_t>+Copy {
    if let Ok(status) = TryInto::try_into(t) {
        if status < 0 {
            Err(io::Error::from_raw_os_error(status))
        } else {
            Ok(t)
        }
    } else {
        Err(io::Error::last_os_error())
    }
}

// Safe wrapper around mx_handle_t
pub struct Handle {
    raw: mx_handle_t,
}

impl Handle {
    pub fn new(raw: mx_handle_t) -> Handle {
        Handle {
            raw: raw,
        }
    }

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

impl Drop for Handle {
    fn drop(&mut self) {
        unsafe { mx_cvt(mx_handle_close(self.raw)).expect("Failed to close mx_handle_t"); }
    }
}

// Common MX_INFO header
#[derive(Default)]
#[repr(C)]
pub struct mx_info_header_t {
    pub topic: u32,              // identifies the info struct
    pub avail_topic_size: u16,   // “native” size of the struct
    pub topic_size: u16,         // size of the returned struct (<=topic_size)
    pub avail_count: u32,        // number of records the kernel has
    pub count: u32,              // number of records returned (limited by buffer size)
}

#[derive(Default)]
#[repr(C)]
pub struct mx_record_process_t {
    pub return_code: c_int,
}

// Returned for topic MX_INFO_PROCESS
#[derive(Default)]
#[repr(C)]
pub struct mx_info_process_t {
    pub hdr: mx_info_header_t,
    pub rec: mx_record_process_t,
}

extern {
    static __magenta_job_default: mx_handle_t;

    pub fn mx_task_kill(handle: mx_handle_t) -> mx_status_t;

    pub fn mx_handle_close(handle: mx_handle_t) -> mx_status_t;

    pub fn mx_handle_duplicate(handle: mx_handle_t, rights: mx_rights_t,
                               out: *const mx_handle_t) -> mx_handle_t;

    pub fn mx_object_wait_one(handle: mx_handle_t, signals: mx_signals_t, timeout: mx_time_t,
                              pending: *mut mx_signals_t) -> mx_status_t;

    pub fn mx_object_get_info(handle: mx_handle_t, topic: u32, buffer: *mut c_void,
                              buffer_size: mx_size_t, actual_size: *mut mx_size_t,
                              avail: *mut mx_size_t) -> mx_status_t;
}

pub fn mx_job_default() -> mx_handle_t {
    unsafe { return __magenta_job_default; }
}

// From `enum special_handles` in system/ulib/launchpad/launchpad.c
// HND_LOADER_SVC = 0
// HND_EXEC_VMO = 1
pub const HND_SPECIAL_COUNT: usize = 2;

#[repr(C)]
pub struct launchpad_t {
    argc: u32,
    envc: u32,
    args: *const c_char,
    args_len: usize,
    env: *const c_char,
    env_len: usize,

    handles: *mut mx_handle_t,
    handles_info: *mut u32,
    handle_count: usize,
    handle_alloc: usize,

    entry: mx_vaddr_t,
    base: mx_vaddr_t,
    vdso_base: mx_vaddr_t,

    stack_size: usize,

    special_handles: [mx_handle_t; HND_SPECIAL_COUNT],
    loader_message: bool,
}

extern {
    pub fn launchpad_create(job: mx_handle_t, name: *const c_char,
                            lp: *mut *mut launchpad_t) -> mx_status_t;

    pub fn launchpad_go(lp: *mut launchpad_t,
                        proc_handle: *mut mx_handle_t,
                        err_msg: *mut *const c_char) -> mx_status_t;

    pub fn launchpad_destroy(lp: *mut launchpad_t);

    pub fn launchpad_set_args(lp: *mut launchpad_t, argc: c_int,
                               argv: *const *const c_char) -> mx_status_t;

    pub fn launchpad_set_environ(lp: *mut launchpad_t, envp: *const *const c_char) -> mx_status_t;

    pub fn launchpad_clone(lp: *mut launchpad_t, what: u32) -> mx_status_t;

    pub fn launchpad_clone_fd(lp: *mut launchpad_t, fd: c_int, target_fd: c_int) -> mx_status_t;

    pub fn launchpad_transfer_fd(lp: *mut launchpad_t, fd: c_int, target_fd: c_int) -> mx_status_t;

    pub fn launchpad_elf_load(lp: *mut launchpad_t, vmo: mx_handle_t) -> mx_status_t;

    pub fn launchpad_add_vdso_vmo(lp: *mut launchpad_t) -> mx_status_t;

    pub fn launchpad_load_vdso(lp: *mut launchpad_t, vmo: mx_handle_t) -> mx_status_t;

    pub fn launchpad_vmo_from_file(filename: *const c_char) -> mx_handle_t;
}

// Launchpad clone constants

pub const LP_CLONE_MXIO_ROOT: u32 = 0x0001;
pub const LP_CLONE_MXIO_CWD: u32 = 0x0002;
// LP_CLONE_MXIO_STDIO = 0x0004
// LP_CLONE_MXIO_ALL = 0x00FF
// LP_CLONE_ENVIRON = 0x0100
// LP_CLONE_DEFAULT_JOB = 0x0200
// LP_CLONE_ALL = 0xFFFF

// Errors

#[allow(unused)] pub const ERR_INTERNAL: mx_status_t = -1;

// ERR_NOT_SUPPORTED: The operation is not implemented, supported,
// or enabled.
#[allow(unused)] pub const ERR_NOT_SUPPORTED: mx_status_t = -2;

// ERR_NO_RESOURCES: The system was not able to allocate some resource
// needed for the operation.
#[allow(unused)] pub const ERR_NO_RESOURCES: mx_status_t = -5;

// ERR_NO_MEMORY: The system was not able to allocate memory needed
// for the operation.
#[allow(unused)] pub const ERR_NO_MEMORY: mx_status_t = -4;

// ERR_CALL_FAILED: The second phase of mx_channel_call(; did not complete
// successfully.
#[allow(unused)] pub const ERR_CALL_FAILED: mx_status_t = -53;

// ======= Parameter errors =======
// ERR_INVALID_ARGS: an argument is invalid, ex. null pointer
#[allow(unused)] pub const ERR_INVALID_ARGS: mx_status_t = -10;

// ERR_WRONG_TYPE: The subject of the operation is the wrong type to
// perform the operation.
// Example: Attempting a message_read on a thread handle.
#[allow(unused)] pub const ERR_WRONG_TYPE: mx_status_t = -54;

// ERR_BAD_SYSCALL: The specified syscall number is invalid.
#[allow(unused)] pub const ERR_BAD_SYSCALL: mx_status_t = -11;

// ERR_BAD_HANDLE: A specified handle value does not refer to a handle.
#[allow(unused)] pub const ERR_BAD_HANDLE: mx_status_t = -12;

// ERR_OUT_OF_RANGE: An argument is outside the valid range for this
// operation.
#[allow(unused)] pub const ERR_OUT_OF_RANGE: mx_status_t = -13;

// ERR_BUFFER_TOO_SMALL: A caller provided buffer is too small for
// this operation.
#[allow(unused)] pub const ERR_BUFFER_TOO_SMALL: mx_status_t = -14;

// ======= Precondition or state errors =======
// ERR_BAD_STATE: operation failed because the current state of the
// object does not allow it, or a precondition of the operation is
// not satisfied
#[allow(unused)] pub const ERR_BAD_STATE: mx_status_t = -20;

// ERR_NOT_FOUND: The requested entity is not found.
#[allow(unused)] pub const ERR_NOT_FOUND: mx_status_t = -3;

// ERR_ALREADY_EXISTS: An object with the specified identifier
// already exists.
// Example: Attempting to create a file when a file already exists
// with that name.
#[allow(unused)] pub const ERR_ALREADY_EXISTS: mx_status_t = -15;

// ERR_ALREADY_BOUND: The operation failed because the named entity
// is already owned or controlled by another entity. The operation
// could succeed later if the current owner releases the entity.
#[allow(unused)] pub const ERR_ALREADY_BOUND: mx_status_t = -16;

// ERR_TIMED_OUT: The time limit for the operation elapsed before
// the operation completed.
#[allow(unused)] pub const ERR_TIMED_OUT: mx_status_t = -23;

// ERR_HANDLE_CLOSED: a handle being waited on was closed
#[allow(unused)] pub const ERR_HANDLE_CLOSED: mx_status_t = -24;

// ERR_REMOTE_CLOSED: The operation failed because the remote end
// of the subject of the operation was closed.
#[allow(unused)] pub const ERR_REMOTE_CLOSED: mx_status_t = -25;

// ERR_UNAVAILABLE: The subject of the operation is currently unable
// to perform the operation.
// Note: This is used when there's no direct way for the caller to
// observe when the subject will be able to perform the operation
// and should thus retry.
#[allow(unused)] pub const ERR_UNAVAILABLE: mx_status_t = -26;

// ERR_SHOULD_WAIT: The operation cannot be performed currently but
// potentially could succeed if the caller waits for a prerequisite
// to be satisfied, for example waiting for a handle to be readable
// or writable.
// Example: Attempting to read from a message pipe that has no
// messages waiting but has an open remote will return ERR_SHOULD_WAIT.
// Attempting to read from a message pipe that has no messages waiting
// and has a closed remote end will return ERR_REMOTE_CLOSED.
#[allow(unused)] pub const ERR_SHOULD_WAIT: mx_status_t = -27;

// ======= Permission check errors =======
// ERR_ACCESS_DENIED: The caller did not have permission to perform
// the specified operation.
#[allow(unused)] pub const ERR_ACCESS_DENIED: mx_status_t = -30;

// ======= Input-output errors =======
// ERR_IO: Otherwise unspecified error occurred during I/O.
#[allow(unused)] pub const ERR_IO: mx_status_t = -40;

// ERR_REFUSED: The entity the I/O operation is being performed on
// rejected the operation.
// Example: an I2C device NAK'ing a transaction or a disk controller
// rejecting an invalid command.
#[allow(unused)] pub const ERR_IO_REFUSED: mx_status_t = -41;

// ERR_IO_DATA_INTEGRITY: The data in the operation failed an integrity
// check and is possibly corrupted.
// Example: CRC or Parity error.
#[allow(unused)] pub const ERR_IO_DATA_INTEGRITY: mx_status_t = -42;

// ERR_IO_DATA_LOSS: The data in the operation is currently unavailable
// and may be permanently lost.
// Example: A disk block is irrecoverably damaged.
#[allow(unused)] pub const ERR_IO_DATA_LOSS: mx_status_t = -43;

// Filesystem specific errors
#[allow(unused)] pub const ERR_BAD_PATH: mx_status_t = -50;
#[allow(unused)] pub const ERR_NOT_DIR: mx_status_t = -51;
#[allow(unused)] pub const ERR_NOT_FILE: mx_status_t = -52;
<commit_msg>Update magenta error codes<commit_after>// Copyright 2016 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.

#![allow(non_camel_case_types)]

use convert::TryInto;
use io;
use os::raw::c_char;
use u64;

use libc::{c_int, c_void};

pub type mx_handle_t = i32;
pub type mx_vaddr_t = usize;
pub type mx_rights_t = u32;
pub type mx_status_t = i32;

pub type mx_size_t = usize;

pub const MX_HANDLE_INVALID: mx_handle_t = 0;

pub type mx_time_t = u64;
pub const MX_TIME_INFINITE : mx_time_t = u64::MAX;

pub type mx_signals_t = u32;

pub const MX_OBJECT_SIGNAL_3         : mx_signals_t = 1 << 3;

pub const MX_TASK_TERMINATED        : mx_signals_t = MX_OBJECT_SIGNAL_3;

pub const MX_RIGHT_SAME_RIGHTS  : mx_rights_t = 1 << 31;

pub type mx_object_info_topic_t = u32;

pub const MX_INFO_PROCESS         : mx_object_info_topic_t = 3;

pub fn mx_cvt<T>(t: T) -> io::Result<T> where T: TryInto<mx_status_t>+Copy {
    if let Ok(status) = TryInto::try_into(t) {
        if status < 0 {
            Err(io::Error::from_raw_os_error(status))
        } else {
            Ok(t)
        }
    } else {
        Err(io::Error::last_os_error())
    }
}

// Safe wrapper around mx_handle_t
pub struct Handle {
    raw: mx_handle_t,
}

impl Handle {
    pub fn new(raw: mx_handle_t) -> Handle {
        Handle {
            raw: raw,
        }
    }

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

impl Drop for Handle {
    fn drop(&mut self) {
        unsafe { mx_cvt(mx_handle_close(self.raw)).expect("Failed to close mx_handle_t"); }
    }
}

// Common MX_INFO header
#[derive(Default)]
#[repr(C)]
pub struct mx_info_header_t {
    pub topic: u32,              // identifies the info struct
    pub avail_topic_size: u16,   // “native” size of the struct
    pub topic_size: u16,         // size of the returned struct (<=topic_size)
    pub avail_count: u32,        // number of records the kernel has
    pub count: u32,              // number of records returned (limited by buffer size)
}

#[derive(Default)]
#[repr(C)]
pub struct mx_record_process_t {
    pub return_code: c_int,
}

// Returned for topic MX_INFO_PROCESS
#[derive(Default)]
#[repr(C)]
pub struct mx_info_process_t {
    pub hdr: mx_info_header_t,
    pub rec: mx_record_process_t,
}

extern {
    static __magenta_job_default: mx_handle_t;

    pub fn mx_task_kill(handle: mx_handle_t) -> mx_status_t;

    pub fn mx_handle_close(handle: mx_handle_t) -> mx_status_t;

    pub fn mx_handle_duplicate(handle: mx_handle_t, rights: mx_rights_t,
                               out: *const mx_handle_t) -> mx_handle_t;

    pub fn mx_object_wait_one(handle: mx_handle_t, signals: mx_signals_t, timeout: mx_time_t,
                              pending: *mut mx_signals_t) -> mx_status_t;

    pub fn mx_object_get_info(handle: mx_handle_t, topic: u32, buffer: *mut c_void,
                              buffer_size: mx_size_t, actual_size: *mut mx_size_t,
                              avail: *mut mx_size_t) -> mx_status_t;
}

pub fn mx_job_default() -> mx_handle_t {
    unsafe { return __magenta_job_default; }
}

// From `enum special_handles` in system/ulib/launchpad/launchpad.c
// HND_LOADER_SVC = 0
// HND_EXEC_VMO = 1
pub const HND_SPECIAL_COUNT: usize = 2;

#[repr(C)]
pub struct launchpad_t {
    argc: u32,
    envc: u32,
    args: *const c_char,
    args_len: usize,
    env: *const c_char,
    env_len: usize,

    handles: *mut mx_handle_t,
    handles_info: *mut u32,
    handle_count: usize,
    handle_alloc: usize,

    entry: mx_vaddr_t,
    base: mx_vaddr_t,
    vdso_base: mx_vaddr_t,

    stack_size: usize,

    special_handles: [mx_handle_t; HND_SPECIAL_COUNT],
    loader_message: bool,
}

extern {
    pub fn launchpad_create(job: mx_handle_t, name: *const c_char,
                            lp: *mut *mut launchpad_t) -> mx_status_t;

    pub fn launchpad_go(lp: *mut launchpad_t,
                        proc_handle: *mut mx_handle_t,
                        err_msg: *mut *const c_char) -> mx_status_t;

    pub fn launchpad_destroy(lp: *mut launchpad_t);

    pub fn launchpad_set_args(lp: *mut launchpad_t, argc: c_int,
                               argv: *const *const c_char) -> mx_status_t;

    pub fn launchpad_set_environ(lp: *mut launchpad_t, envp: *const *const c_char) -> mx_status_t;

    pub fn launchpad_clone(lp: *mut launchpad_t, what: u32) -> mx_status_t;

    pub fn launchpad_clone_fd(lp: *mut launchpad_t, fd: c_int, target_fd: c_int) -> mx_status_t;

    pub fn launchpad_transfer_fd(lp: *mut launchpad_t, fd: c_int, target_fd: c_int) -> mx_status_t;

    pub fn launchpad_elf_load(lp: *mut launchpad_t, vmo: mx_handle_t) -> mx_status_t;

    pub fn launchpad_add_vdso_vmo(lp: *mut launchpad_t) -> mx_status_t;

    pub fn launchpad_load_vdso(lp: *mut launchpad_t, vmo: mx_handle_t) -> mx_status_t;

    pub fn launchpad_vmo_from_file(filename: *const c_char) -> mx_handle_t;
}

// Launchpad clone constants

pub const LP_CLONE_MXIO_ROOT: u32 = 0x0001;
pub const LP_CLONE_MXIO_CWD: u32 = 0x0002;
// LP_CLONE_MXIO_STDIO = 0x0004
// LP_CLONE_MXIO_ALL = 0x00FF
// LP_CLONE_ENVIRON = 0x0100
// LP_CLONE_DEFAULT_JOB = 0x0200
// LP_CLONE_ALL = 0xFFFF

// Errors

#[allow(unused)] pub const ERR_INTERNAL: mx_status_t = -1;

// ERR_NOT_SUPPORTED: The operation is not implemented, supported,
// or enabled.
#[allow(unused)] pub const ERR_NOT_SUPPORTED: mx_status_t = -2;

// ERR_NO_RESOURCES: The system was not able to allocate some resource
// needed for the operation.
#[allow(unused)] pub const ERR_NO_RESOURCES: mx_status_t = -3;

// ERR_NO_MEMORY: The system was not able to allocate memory needed
// for the operation.
#[allow(unused)] pub const ERR_NO_MEMORY: mx_status_t = -4;

// ERR_CALL_FAILED: The second phase of mx_channel_call(; did not complete
// successfully.
#[allow(unused)] pub const ERR_CALL_FAILED: mx_status_t = -5;

// ERR_INTERRUPTED_RETRY: The system call was interrupted, but should be
// retried.  This should not be seen outside of the VDSO.
#[allow(unused)] pub const ERR_INTERRUPTED_RETRY: mx_status_t = -6;

// ======= Parameter errors =======
// ERR_INVALID_ARGS: an argument is invalid, ex. null pointer
#[allow(unused)] pub const ERR_INVALID_ARGS: mx_status_t = -10;

// ERR_BAD_HANDLE: A specified handle value does not refer to a handle.
#[allow(unused)] pub const ERR_BAD_HANDLE: mx_status_t = -11;

// ERR_WRONG_TYPE: The subject of the operation is the wrong type to
// perform the operation.
// Example: Attempting a message_read on a thread handle.
#[allow(unused)] pub const ERR_WRONG_TYPE: mx_status_t = -12;

// ERR_BAD_SYSCALL: The specified syscall number is invalid.
#[allow(unused)] pub const ERR_BAD_SYSCALL: mx_status_t = -13;

// ERR_OUT_OF_RANGE: An argument is outside the valid range for this
// operation.
#[allow(unused)] pub const ERR_OUT_OF_RANGE: mx_status_t = -14;

// ERR_BUFFER_TOO_SMALL: A caller provided buffer is too small for
// this operation.
#[allow(unused)] pub const ERR_BUFFER_TOO_SMALL: mx_status_t = -15;

// ======= Precondition or state errors =======
// ERR_BAD_STATE: operation failed because the current state of the
// object does not allow it, or a precondition of the operation is
// not satisfied
#[allow(unused)] pub const ERR_BAD_STATE: mx_status_t = -20;

// ERR_TIMED_OUT: The time limit for the operation elapsed before
// the operation completed.
#[allow(unused)] pub const ERR_TIMED_OUT: mx_status_t = -21;

// ERR_SHOULD_WAIT: The operation cannot be performed currently but
// potentially could succeed if the caller waits for a prerequisite
// to be satisfied, for example waiting for a handle to be readable
// or writable.
// Example: Attempting to read from a message pipe that has no
// messages waiting but has an open remote will return ERR_SHOULD_WAIT.
// Attempting to read from a message pipe that has no messages waiting
// and has a closed remote end will return ERR_REMOTE_CLOSED.
#[allow(unused)] pub const ERR_SHOULD_WAIT: mx_status_t = -22;

// ERR_CANCELED: The in-progress operation (e.g. a wait) has been
// // canceled.
#[allow(unused)] pub const ERR_CANCELED: mx_status_t = -23;

// ERR_PEER_CLOSED: The operation failed because the remote end
// of the subject of the operation was closed.
#[allow(unused)] pub const ERR_PEER_CLOSED: mx_status_t = -24;

// ERR_NOT_FOUND: The requested entity is not found.
#[allow(unused)] pub const ERR_NOT_FOUND: mx_status_t = -25;

// ERR_ALREADY_EXISTS: An object with the specified identifier
// already exists.
// Example: Attempting to create a file when a file already exists
// with that name.
#[allow(unused)] pub const ERR_ALREADY_EXISTS: mx_status_t = -26;

// ERR_ALREADY_BOUND: The operation failed because the named entity
// is already owned or controlled by another entity. The operation
// could succeed later if the current owner releases the entity.
#[allow(unused)] pub const ERR_ALREADY_BOUND: mx_status_t = -27;

// ERR_UNAVAILABLE: The subject of the operation is currently unable
// to perform the operation.
// Note: This is used when there's no direct way for the caller to
// observe when the subject will be able to perform the operation
// and should thus retry.
#[allow(unused)] pub const ERR_UNAVAILABLE: mx_status_t = -28;

// ======= Permission check errors =======
// ERR_ACCESS_DENIED: The caller did not have permission to perform
// the specified operation.
#[allow(unused)] pub const ERR_ACCESS_DENIED: mx_status_t = -30;

// ======= Input-output errors =======
// ERR_IO: Otherwise unspecified error occurred during I/O.
#[allow(unused)] pub const ERR_IO: mx_status_t = -40;

// ERR_REFUSED: The entity the I/O operation is being performed on
// rejected the operation.
// Example: an I2C device NAK'ing a transaction or a disk controller
// rejecting an invalid command.
#[allow(unused)] pub const ERR_IO_REFUSED: mx_status_t = -41;

// ERR_IO_DATA_INTEGRITY: The data in the operation failed an integrity
// check and is possibly corrupted.
// Example: CRC or Parity error.
#[allow(unused)] pub const ERR_IO_DATA_INTEGRITY: mx_status_t = -42;

// ERR_IO_DATA_LOSS: The data in the operation is currently unavailable
// and may be permanently lost.
// Example: A disk block is irrecoverably damaged.
#[allow(unused)] pub const ERR_IO_DATA_LOSS: mx_status_t = -43;

// Filesystem specific errors
#[allow(unused)] pub const ERR_BAD_PATH: mx_status_t = -50;
#[allow(unused)] pub const ERR_NOT_DIR: mx_status_t = -51;
#[allow(unused)] pub const ERR_NOT_FILE: mx_status_t = -52;
// ERR_FILE_BIG: A file exceeds a filesystem-specific size limit.
#[allow(unused)] pub const ERR_FILE_BIG: mx_status_t = -53;
// ERR_NO_SPACE: Filesystem or device space is exhausted.
#[allow(unused)] pub const ERR_NO_SPACE: mx_status_t = -54;
<|endoftext|>


<commit_before>extern crate ammonia;
extern crate elasticlunr;

use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use std::path::Path;

use pulldown_cmark::*;
use serde_json;
use self::elasticlunr::Index;

use book::{Book, BookItem};
use config::Search;
use errors::*;
use utils;
use theme::searcher;

/// Creates all files required for search.
pub fn create_files(search_config: &Search, destination: &Path, book: &Book) -> Result<()> {
    let mut index = Index::new(&["title", "body", "breadcrumbs"]);
    let mut doc_urls = Vec::with_capacity(book.sections.len());

    for item in book.iter() {
        render_item(&mut index, &search_config, &mut doc_urls, item)?;
    }

    let index = write_to_js(index, &search_config, doc_urls)?;
    debug!("Writing search index ✓");

    if search_config.copy_js {
        utils::fs::write_file(destination, "searchindex.js", index.as_bytes())?;
        utils::fs::write_file(destination, "searcher.js", searcher::JS)?;
        utils::fs::write_file(destination, "mark.min.js", searcher::MARK_JS)?;
        utils::fs::write_file(destination, "elasticlunr.min.js", searcher::ELASTICLUNR_JS)?;
        debug!("Copying search files ✓");
    }

    Ok(())
}

/// Uses the given arguments to construct a search document, then inserts it to the given index.
fn add_doc(
    index: &mut Index,
    doc_urls: &mut Vec<String>,
    anchor_base: &str,
    section_id: &Option<String>,
    items: &[&str],
) {
    let doc_ref = if let &Some(ref id) = section_id {
        Cow::Owned(format!("{}#{}", anchor_base, id))
    } else {
        Cow::Borrowed(anchor_base.into())
    };
    let doc_ref = utils::collapse_whitespace(doc_ref.trim());
    let key = doc_urls.len();
    doc_urls.push(doc_ref.into());

    let items = items.iter().map(|&x| utils::collapse_whitespace(x.trim()));
    index.add_doc(&key.to_string(), items);
}

/// Renders markdown into flat unformatted text and adds it to the search index.
fn render_item(
    index: &mut Index,
    search_config: &Search,
    doc_urls: &mut Vec<String>,
    item: &BookItem,
) -> Result<()> {
    let chapter = match item {
        &BookItem::Chapter(ref ch) => ch,
        _ => return Ok(()),
    };

    let filepath = Path::new(&chapter.path).with_extension("html");
    let filepath = filepath
        .to_str()
        .chain_err(|| "Could not convert HTML path to str")?;
    let anchor_base = utils::fs::normalize_path(filepath);

    let mut opts = Options::empty();
    opts.insert(OPTION_ENABLE_TABLES);
    opts.insert(OPTION_ENABLE_FOOTNOTES);
    let p = Parser::new_ext(&chapter.content, opts);

    let mut in_header = false;
    let max_section_depth = search_config.heading_split_level as i32;
    let mut section_id = None;
    let mut heading = String::new();
    let mut body = String::new();
    let mut breadcrumbs = chapter.parent_names.clone();
    let mut footnote_numbers = HashMap::new();

    for event in p {
        match event {
            Event::Start(Tag::Header(i)) if i <= max_section_depth => {
                if heading.len() > 0 {
                    // Section finished, the next header is following now
                    // Write the data to the index, and clear it for the next section
                    add_doc(
                        index,
                        doc_urls,
                        &anchor_base,
                        &section_id,
                        &[&heading, &body, &breadcrumbs.join(" » ")],
                    );
                    section_id = None;
                    heading.clear();
                    body.clear();
                    breadcrumbs.pop();
                }

                in_header = true;
            }
            Event::End(Tag::Header(i)) if i <= max_section_depth => {
                in_header = false;
                section_id = Some(utils::id_from_content(&heading));
                breadcrumbs.push(heading.clone());
            }
            Event::Start(Tag::FootnoteDefinition(name)) => {
                let number = footnote_numbers.len() + 1;
                footnote_numbers.entry(name).or_insert(number);
            }
            Event::Start(_) | Event::End(_) | Event::SoftBreak | Event::HardBreak => {
                // Insert spaces where HTML output would usually seperate text
                // to ensure words don't get merged together
                if in_header {
                    heading.push(' ');
                } else {
                    body.push(' ');
                }
            }
            Event::Text(text) => {
                if in_header {
                    heading.push_str(&text);
                } else {
                    body.push_str(&text);
                }
            }
            Event::Html(html) | Event::InlineHtml(html) => {
                body.push_str(&clean_html(&html));
            }
            Event::FootnoteReference(name) => {
                let len = footnote_numbers.len() + 1;
                let number = footnote_numbers.entry(name).or_insert(len);
                body.push_str(&format!(" [{}] ", number));
            }
        }
    }

    if heading.len() > 0 {
        // Make sure the last section is added to the index
        add_doc(
            index,
            doc_urls,
            &anchor_base,
            &section_id,
            &[&heading, &body, &breadcrumbs.join(" » ")],
        );
    }

    Ok(())
}

/// Exports the index and search options to a JS script which stores the index in `window.search`.
/// Using a JS script is a workaround for CORS in `file://` URIs. It also removes the need for
/// downloading/parsing JSON in JS.
fn write_to_js(index: Index, search_config: &Search, doc_urls: Vec<String>) -> Result<String> {
    use std::collections::BTreeMap;
    use self::elasticlunr::config::{SearchBool, SearchOptions, SearchOptionsField};

    #[derive(Serialize)]
    struct ResultsOptions {
        limit_results: u32,
        teaser_word_count: u32,
    }

    #[derive(Serialize)]
    struct SearchindexJson {
        /// The options used for displaying search results
        results_options: ResultsOptions,
        /// The searchoptions for elasticlunr.js
        search_options: SearchOptions,
        /// Used to lookup a document's URL from an integer document ref.
        doc_urls: Vec<String>,
        /// The index for elasticlunr.js
        index: elasticlunr::Index,
    }

    let mut fields = BTreeMap::new();
    let mut opt = SearchOptionsField::default();
    opt.boost = Some(search_config.boost_title);
    fields.insert("title".into(), opt);
    opt.boost = Some(search_config.boost_paragraph);
    fields.insert("body".into(), opt);
    opt.boost = Some(search_config.boost_hierarchy);
    fields.insert("breadcrumbs".into(), opt);

    let search_options = SearchOptions {
        bool: if search_config.use_boolean_and {
            SearchBool::And
        } else {
            SearchBool::Or
        },
        expand: search_config.expand,
        fields,
    };

    let results_options = ResultsOptions {
        limit_results: search_config.limit_results,
        teaser_word_count: search_config.teaser_word_count,
    };

    let json_contents = SearchindexJson {
        results_options,
        search_options,
        doc_urls,
        index,
    };

    // By converting to serde_json::Value as an intermediary, we use a
    // BTreeMap internally and can force a stable ordering of map keys.
    let json_contents = serde_json::to_value(&json_contents)?;
    let json_contents = serde_json::to_string(&json_contents)?;

    Ok(format!("window.search = {};", json_contents))
}

fn clean_html(html: &str) -> String {
    lazy_static! {
        static ref AMMONIA: ammonia::Builder<'static> = {
            let mut clean_content = HashSet::new();
            clean_content.insert("script");
            clean_content.insert("style");
            let mut builder = ammonia::Builder::new();
            builder
                .tags(HashSet::new())
                .tag_attributes(HashMap::new())
                .generic_attributes(HashSet::new())
                .link_rel(None)
                .allowed_classes(HashMap::new())
                .clean_content_tags(clean_content);
            builder
        };
    }
    AMMONIA.clean(html).to_string()
}
<commit_msg>Warn when search index is >10MB<commit_after>extern crate ammonia;
extern crate elasticlunr;

use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use std::path::Path;

use pulldown_cmark::*;
use serde_json;
use self::elasticlunr::Index;

use book::{Book, BookItem};
use config::Search;
use errors::*;
use utils;
use theme::searcher;

/// Creates all files required for search.
pub fn create_files(search_config: &Search, destination: &Path, book: &Book) -> Result<()> {
    let mut index = Index::new(&["title", "body", "breadcrumbs"]);
    let mut doc_urls = Vec::with_capacity(book.sections.len());

    for item in book.iter() {
        render_item(&mut index, &search_config, &mut doc_urls, item)?;
    }

    let index = write_to_js(index, &search_config, doc_urls)?;
    debug!("Writing search index ✓");
    if index.len() > 10_000_000 {
        warn!("searchindex.js is very large ({} bytes)", index.len());
    }

    if search_config.copy_js {
        utils::fs::write_file(destination, "searchindex.js", index.as_bytes())?;
        utils::fs::write_file(destination, "searcher.js", searcher::JS)?;
        utils::fs::write_file(destination, "mark.min.js", searcher::MARK_JS)?;
        utils::fs::write_file(destination, "elasticlunr.min.js", searcher::ELASTICLUNR_JS)?;
        debug!("Copying search files ✓");
    }

    Ok(())
}

/// Uses the given arguments to construct a search document, then inserts it to the given index.
fn add_doc(
    index: &mut Index,
    doc_urls: &mut Vec<String>,
    anchor_base: &str,
    section_id: &Option<String>,
    items: &[&str],
) {
    let doc_ref = if let &Some(ref id) = section_id {
        Cow::Owned(format!("{}#{}", anchor_base, id))
    } else {
        Cow::Borrowed(anchor_base.into())
    };
    let doc_ref = utils::collapse_whitespace(doc_ref.trim());
    let key = doc_urls.len();
    doc_urls.push(doc_ref.into());

    let items = items.iter().map(|&x| utils::collapse_whitespace(x.trim()));
    index.add_doc(&key.to_string(), items);
}

/// Renders markdown into flat unformatted text and adds it to the search index.
fn render_item(
    index: &mut Index,
    search_config: &Search,
    doc_urls: &mut Vec<String>,
    item: &BookItem,
) -> Result<()> {
    let chapter = match item {
        &BookItem::Chapter(ref ch) => ch,
        _ => return Ok(()),
    };

    let filepath = Path::new(&chapter.path).with_extension("html");
    let filepath = filepath
        .to_str()
        .chain_err(|| "Could not convert HTML path to str")?;
    let anchor_base = utils::fs::normalize_path(filepath);

    let mut opts = Options::empty();
    opts.insert(OPTION_ENABLE_TABLES);
    opts.insert(OPTION_ENABLE_FOOTNOTES);
    let p = Parser::new_ext(&chapter.content, opts);

    let mut in_header = false;
    let max_section_depth = search_config.heading_split_level as i32;
    let mut section_id = None;
    let mut heading = String::new();
    let mut body = String::new();
    let mut breadcrumbs = chapter.parent_names.clone();
    let mut footnote_numbers = HashMap::new();

    for event in p {
        match event {
            Event::Start(Tag::Header(i)) if i <= max_section_depth => {
                if heading.len() > 0 {
                    // Section finished, the next header is following now
                    // Write the data to the index, and clear it for the next section
                    add_doc(
                        index,
                        doc_urls,
                        &anchor_base,
                        &section_id,
                        &[&heading, &body, &breadcrumbs.join(" » ")],
                    );
                    section_id = None;
                    heading.clear();
                    body.clear();
                    breadcrumbs.pop();
                }

                in_header = true;
            }
            Event::End(Tag::Header(i)) if i <= max_section_depth => {
                in_header = false;
                section_id = Some(utils::id_from_content(&heading));
                breadcrumbs.push(heading.clone());
            }
            Event::Start(Tag::FootnoteDefinition(name)) => {
                let number = footnote_numbers.len() + 1;
                footnote_numbers.entry(name).or_insert(number);
            }
            Event::Start(_) | Event::End(_) | Event::SoftBreak | Event::HardBreak => {
                // Insert spaces where HTML output would usually seperate text
                // to ensure words don't get merged together
                if in_header {
                    heading.push(' ');
                } else {
                    body.push(' ');
                }
            }
            Event::Text(text) => {
                if in_header {
                    heading.push_str(&text);
                } else {
                    body.push_str(&text);
                }
            }
            Event::Html(html) | Event::InlineHtml(html) => {
                body.push_str(&clean_html(&html));
            }
            Event::FootnoteReference(name) => {
                let len = footnote_numbers.len() + 1;
                let number = footnote_numbers.entry(name).or_insert(len);
                body.push_str(&format!(" [{}] ", number));
            }
        }
    }

    if heading.len() > 0 {
        // Make sure the last section is added to the index
        add_doc(
            index,
            doc_urls,
            &anchor_base,
            &section_id,
            &[&heading, &body, &breadcrumbs.join(" » ")],
        );
    }

    Ok(())
}

/// Exports the index and search options to a JS script which stores the index in `window.search`.
/// Using a JS script is a workaround for CORS in `file://` URIs. It also removes the need for
/// downloading/parsing JSON in JS.
fn write_to_js(index: Index, search_config: &Search, doc_urls: Vec<String>) -> Result<String> {
    use std::collections::BTreeMap;
    use self::elasticlunr::config::{SearchBool, SearchOptions, SearchOptionsField};

    #[derive(Serialize)]
    struct ResultsOptions {
        limit_results: u32,
        teaser_word_count: u32,
    }

    #[derive(Serialize)]
    struct SearchindexJson {
        /// The options used for displaying search results
        results_options: ResultsOptions,
        /// The searchoptions for elasticlunr.js
        search_options: SearchOptions,
        /// Used to lookup a document's URL from an integer document ref.
        doc_urls: Vec<String>,
        /// The index for elasticlunr.js
        index: elasticlunr::Index,
    }

    let mut fields = BTreeMap::new();
    let mut opt = SearchOptionsField::default();
    opt.boost = Some(search_config.boost_title);
    fields.insert("title".into(), opt);
    opt.boost = Some(search_config.boost_paragraph);
    fields.insert("body".into(), opt);
    opt.boost = Some(search_config.boost_hierarchy);
    fields.insert("breadcrumbs".into(), opt);

    let search_options = SearchOptions {
        bool: if search_config.use_boolean_and {
            SearchBool::And
        } else {
            SearchBool::Or
        },
        expand: search_config.expand,
        fields,
    };

    let results_options = ResultsOptions {
        limit_results: search_config.limit_results,
        teaser_word_count: search_config.teaser_word_count,
    };

    let json_contents = SearchindexJson {
        results_options,
        search_options,
        doc_urls,
        index,
    };

    // By converting to serde_json::Value as an intermediary, we use a
    // BTreeMap internally and can force a stable ordering of map keys.
    let json_contents = serde_json::to_value(&json_contents)?;
    let json_contents = serde_json::to_string(&json_contents)?;

    Ok(format!("window.search = {};", json_contents))
}

fn clean_html(html: &str) -> String {
    lazy_static! {
        static ref AMMONIA: ammonia::Builder<'static> = {
            let mut clean_content = HashSet::new();
            clean_content.insert("script");
            clean_content.insert("style");
            let mut builder = ammonia::Builder::new();
            builder
                .tags(HashSet::new())
                .tag_attributes(HashMap::new())
                .generic_attributes(HashSet::new())
                .link_rel(None)
                .allowed_classes(HashMap::new())
                .clean_content_tags(clean_content);
            builder
        };
    }
    AMMONIA.clean(html).to_string()
}
<|endoftext|>


<commit_before>use routing;
use rpc;
use bus;

use rpc::Rpc;
use time;
use {SubotaiError, SubotaiResult};

use hash::Hash;
use std::net;
use std::sync::Arc;
use std::sync;
use node::*;

/// Node resources for synchronous operations.
///
/// All methods on this module are synchronous, and will wait for any
/// remote nodes queried to reply to the RPCs sent, up to the timeout
/// defined at `node::NETWORK_TIMEOUT_S`. The node layer above is in
/// charge of parallelizing those operations by spawning threads when
/// adequate.
pub struct Resources {
   pub id       : Hash,
   pub table    : routing::Table,
   pub outbound : net::UdpSocket,
   pub inbound  : net::UdpSocket,
   pub state    : sync::Mutex<State>,
   pub updates  : sync::Mutex<bus::Bus<Update>>,
}

#[derive(Clone)]
pub enum Update {
   RpcReceived(Rpc),
   Tick,
}

impl Resources {
   pub fn local_info(&self) -> routing::NodeInfo {
      routing::NodeInfo {
         id      : self.id.clone(),
         address : self.inbound.local_addr().unwrap(),
      }
   }

   pub fn ping(&self, id: Hash) -> SubotaiResult<()> {
      let node = match self.table.specific_node(&id) {
         None => Some(try!(self.find_node(&id))),
         Some(node) => Some(node),
      };

      if let Some(node) = node {
         let rpc = Rpc::ping(self.id.clone(), self.inbound.local_addr().unwrap().port());
         let packet = rpc.serialize();
         let responses = self.receptions().during(time::Duration::seconds(NETWORK_TIMEOUT_S))
            .rpc(receptions::RpcFilter::PingResponse).from(id.clone()).take(1);

         try!(self.outbound.send_to(&packet, node.address));

         for _ in responses {
            return Ok(());
         }
      }
      Err(SubotaiError::NoResponse)
   }

   /// Sends a ping and doesn't wait for a response. Used by the maintenance thread.
   pub fn ping_and_forget(&self, id: Hash) -> SubotaiResult<()> {
      let node = match self.table.specific_node(&id) {
         None => Some(try!(self.find_node(&id))),
         Some(node) => Some(node),
      };

      if let Some(node) = node {
         let rpc = Rpc::ping(self.id.clone(), self.inbound.local_addr().unwrap().port());
         let packet = rpc.serialize();
         try!(self.outbound.send_to(&packet, node.address));
      }
      Ok(())
   }

   pub fn receptions(&self) -> receptions::Receptions {
      receptions::Receptions::new(self)
   }

   /// Attempts to find a node through the network.
   pub fn find_node(&self, id_to_find: &Hash) -> SubotaiResult<routing::NodeInfo> {
      let mut queried_nodes = Vec::<Hash>::with_capacity(routing::K);

      while queried_nodes.len() < routing::K {
         match self.table.lookup(id_to_find, routing::ALPHA, Some(&queried_nodes)) {
            routing::LookupResult::Found(node) => return Ok(node),
            routing::LookupResult::ClosestNodes(nodes) => {
               try!(self.lookup_wave(id_to_find, &nodes, &mut queried_nodes));
            },
            _ => break,
         }
      }

      match self.table.specific_node(id_to_find) {
         Some(node) => Ok(node),
         None => Err(SubotaiError::NodeNotFound),
      }
   }

   pub fn bootstrap(&self, seed: routing::NodeInfo) -> SubotaiResult<()>  {
      self.table.insert_node(seed);
      let mut queried_ids = Vec::<Hash>::with_capacity(routing::K);

      while queried_ids.len() < routing::K {
         let unqueried_nodes: Vec<routing::NodeInfo> = self.table.all_nodes()
            .filter(|ref node_info| !queried_ids.contains(&node_info.id))
            .collect();

         if unqueried_nodes.is_empty() {
            break;
         }

         try!(self.bootstrap_wave(&unqueried_nodes, &mut queried_ids));
      }
      Ok(())
   }

   fn bootstrap_wave(&self, nodes_to_query: &[routing::NodeInfo], queried: &mut Vec<Hash>) -> SubotaiResult<()> {
      let responses = self.receptions()
         .during(time::Duration::seconds(NETWORK_TIMEOUT_S))
         .rpc(receptions::RpcFilter::BootstrapResponse)
         .take(usize::saturating_sub(nodes_to_query.len(), routing::IMPATIENCE));

      for node in nodes_to_query {
         let rpc = Rpc::bootstrap(self.id.clone(), self.inbound.local_addr().unwrap().port());
         let packet = rpc.serialize();
         try!(self.outbound.send_to(&packet, node.address));
         queried.push(node.id.clone());
      }

      responses.count();
      Ok(())
   }

   fn lookup_wave(&self, id_to_find: &Hash, nodes_to_query: &[routing::NodeInfo], queried: &mut Vec<Hash>) -> SubotaiResult<()> {
      let responses = self.receptions()
         .during(time::Duration::seconds(NETWORK_TIMEOUT_S))
         .filter(|rpc: &Rpc| {
             match rpc.kind {
                rpc::Kind::FindNodeResponse( ref payload ) => &payload.id_to_find == id_to_find,
                _ => false,
             }
         }).take(usize::saturating_sub(nodes_to_query.len(), routing::IMPATIENCE));

      for node in nodes_to_query {
         let rpc = Rpc::find_node(
            self.id.clone(),
            self.inbound.local_addr().unwrap().port(),
            id_to_find.clone(),
            routing::ALPHA,
         );
         let packet = rpc.serialize();
         try!(self.outbound.send_to(&packet, node.address));
         queried.push(node.id.clone());
      }

      for response in responses {
         if let rpc::Kind::FindNodeResponse(ref payload) = response.kind {
            match payload.result {
               routing::LookupResult::Myself | routing::LookupResult::Found(_) => return Ok(()),
               _ => (),
            }
         }
      }

      Ok(())
   }

   pub fn process_incoming_rpc(&self, rpc: Rpc, mut source: net::SocketAddr) -> SubotaiResult<()>{
      source.set_port(rpc.reply_port);
      let sender = routing::NodeInfo {
         id      : rpc.sender_id.clone(),
         address : source,
      };

      let result = match rpc.kind {
         rpc::Kind::Ping                           => self.handle_ping(sender),
         rpc::Kind::PingResponse                   => self.handle_ping_response(sender),
         rpc::Kind::FindNode(ref payload)          => self.handle_find_node(payload.clone(), sender),
         rpc::Kind::FindNodeResponse(ref payload)  => self.handle_find_node_response(payload.clone(), sender),
         rpc::Kind::Bootstrap                      => self.handle_bootstrap(sender),
         rpc::Kind::BootstrapResponse(ref payload) => self.handle_bootstrap_response(payload.clone(),sender),
         _ => unimplemented!(),
      };

      self.updates.lock().unwrap().broadcast(Update::RpcReceived(rpc));
      result
   }

   fn handle_ping(&self, sender: routing::NodeInfo) -> SubotaiResult<()> {
      self.table.insert_node(sender.clone());
      let rpc = Rpc::ping_response(self.id.clone(), self.inbound.local_addr().unwrap().port());
      let packet = rpc.serialize();
      try!(self.outbound.send_to(&packet, sender.address));
      Ok(())
   }

   fn handle_bootstrap(&self, sender: routing::NodeInfo) -> SubotaiResult<()> {
      self.table.insert_node(sender.clone());
      let closest_to_sender: Vec<_> = self.table.closest_nodes_to(&sender.id)
         .filter(|ref info| &info.id != &sender.id) // We don't want to reply with the sender itself
         .take(routing::ALPHA)
         .collect();

      let rpc = Rpc::bootstrap_response(self.id.clone(), self.inbound.local_addr().unwrap().port(), closest_to_sender);
      let packet = rpc.serialize();
      try!(self.outbound.send_to(&packet, sender.address));
      Ok(())
   }

   fn handle_bootstrap_response(&self, payload: Arc<rpc::BootstrapResponsePayload>, sender: routing::NodeInfo) -> SubotaiResult<()> {
      self.table.insert_node(sender.clone());
      for node in &payload.nodes {
         self.table.insert_node(node.clone());
      }
      Ok(())
   }

   fn handle_ping_response(&self, sender: routing::NodeInfo) -> SubotaiResult<()> {
      self.table.insert_node(sender);
      Ok(())
   }

   fn handle_find_node(&self, payload: Arc<rpc::FindNodePayload>, sender: routing::NodeInfo) -> SubotaiResult<()> {
      self.table.insert_node(sender.clone());
      let lookup_results = self.table.lookup(&payload.id_to_find, payload.nodes_wanted, None);
      let rpc = Rpc::find_node_response(self.id.clone(),
                                        self.inbound.local_addr().unwrap().port(),
                                        payload.id_to_find.clone(),
                                        lookup_results);
      let packet = rpc.serialize();
      try!(self.outbound.send_to(&packet, sender.address));
      Ok(())
   }

   fn handle_find_node_response(&self, payload: Arc<rpc::FindNodeResponsePayload>, sender: routing::NodeInfo) -> SubotaiResult<()> {
      self.table.insert_node(sender);
      match payload.result {
         routing::LookupResult::ClosestNodes(ref nodes) => for node in nodes { self.table.insert_node(node.clone()) },
         routing::LookupResult::Found(ref node) => self.table.insert_node(node.clone()),
         _ => (),
      }
      Ok(())
   }
}

<commit_msg>Fixed impatience in find waves<commit_after>use routing;
use rpc;
use bus;

use rpc::Rpc;
use time;
use {SubotaiError, SubotaiResult};

use hash::Hash;
use std::net;
use std::sync::Arc;
use std::sync;
use node::*;

/// Node resources for synchronous operations.
///
/// All methods on this module are synchronous, and will wait for any
/// remote nodes queried to reply to the RPCs sent, up to the timeout
/// defined at `node::NETWORK_TIMEOUT_S`. The node layer above is in
/// charge of parallelizing those operations by spawning threads when
/// adequate.
pub struct Resources {
   pub id       : Hash,
   pub table    : routing::Table,
   pub outbound : net::UdpSocket,
   pub inbound  : net::UdpSocket,
   pub state    : sync::Mutex<State>,
   pub updates  : sync::Mutex<bus::Bus<Update>>,
}

#[derive(Clone)]
pub enum Update {
   RpcReceived(Rpc),
   Tick,
}

impl Resources {
   pub fn local_info(&self) -> routing::NodeInfo {
      routing::NodeInfo {
         id      : self.id.clone(),
         address : self.inbound.local_addr().unwrap(),
      }
   }

   pub fn ping(&self, id: Hash) -> SubotaiResult<()> {
      let node = match self.table.specific_node(&id) {
         None => Some(try!(self.find_node(&id))),
         Some(node) => Some(node),
      };

      if let Some(node) = node {
         let rpc = Rpc::ping(self.id.clone(), self.inbound.local_addr().unwrap().port());
         let packet = rpc.serialize();
         let responses = self.receptions().during(time::Duration::seconds(NETWORK_TIMEOUT_S))
            .rpc(receptions::RpcFilter::PingResponse).from(id.clone()).take(1);

         try!(self.outbound.send_to(&packet, node.address));

         for _ in responses {
            return Ok(());
         }
      }
      Err(SubotaiError::NoResponse)
   }

   /// Sends a ping and doesn't wait for a response. Used by the maintenance thread.
   pub fn ping_and_forget(&self, id: Hash) -> SubotaiResult<()> {
      let node = match self.table.specific_node(&id) {
         None => Some(try!(self.find_node(&id))),
         Some(node) => Some(node),
      };

      if let Some(node) = node {
         let rpc = Rpc::ping(self.id.clone(), self.inbound.local_addr().unwrap().port());
         let packet = rpc.serialize();
         try!(self.outbound.send_to(&packet, node.address));
      }
      Ok(())
   }

   pub fn receptions(&self) -> receptions::Receptions {
      receptions::Receptions::new(self)
   }

   /// Attempts to find a node through the network.
   pub fn find_node(&self, id_to_find: &Hash) -> SubotaiResult<routing::NodeInfo> {
      let mut queried_nodes = Vec::<Hash>::with_capacity(routing::K);

      while queried_nodes.len() < routing::K {
         match self.table.lookup(id_to_find, routing::ALPHA, Some(&queried_nodes)) {
            routing::LookupResult::Found(node) => return Ok(node),
            routing::LookupResult::ClosestNodes(nodes) => {
               try!(self.lookup_wave(id_to_find, &nodes, &mut queried_nodes));
            },
            _ => break,
         }
      }

      // Extra wait due to impatience factor
      for response in self.receptions().during(time::Duration::seconds(NETWORK_TIMEOUT_S)); {
         if let rpc::Kind::FindNodeResponse(ref payload) = response.kind {
            match payload.result {
               routing::LookupResult::Myself | routing::LookupResult::Found(_) => break,
               _ => (),
            }
         }
      }

      match self.table.specific_node(id_to_find) {
         Some(node) => Ok(node),
         None => Err(SubotaiError::NodeNotFound),
      }
   }

   pub fn bootstrap(&self, seed: routing::NodeInfo) -> SubotaiResult<()>  {
      self.table.insert_node(seed);
      let mut queried_ids = Vec::<Hash>::with_capacity(routing::K);

      while queried_ids.len() < routing::K {
         let unqueried_nodes: Vec<routing::NodeInfo> = self.table.all_nodes()
            .filter(|ref node_info| !queried_ids.contains(&node_info.id))
            .collect();

         if unqueried_nodes.is_empty() {
            break;
         }

         try!(self.bootstrap_wave(&unqueried_nodes, &mut queried_ids));
      }
      Ok(())
   }

   fn bootstrap_wave(&self, nodes_to_query: &[routing::NodeInfo], queried: &mut Vec<Hash>) -> SubotaiResult<()> {
      let responses = self.receptions()
         .during(time::Duration::seconds(NETWORK_TIMEOUT_S))
         .rpc(receptions::RpcFilter::BootstrapResponse)
         .take(usize::saturating_sub(nodes_to_query.len(), routing::IMPATIENCE));

      for node in nodes_to_query {
         let rpc = Rpc::bootstrap(self.id.clone(), self.inbound.local_addr().unwrap().port());
         let packet = rpc.serialize();
         try!(self.outbound.send_to(&packet, node.address));
         queried.push(node.id.clone());
      }

      responses.count();
      Ok(())
   }

   fn lookup_wave(&self, id_to_find: &Hash, nodes_to_query: &[routing::NodeInfo], queried: &mut Vec<Hash>) -> SubotaiResult<()> {
      let responses = self.receptions()
         .during(time::Duration::seconds(NETWORK_TIMEOUT_S))
         .filter(|rpc: &Rpc| {
             match rpc.kind {
                rpc::Kind::FindNodeResponse( ref payload ) => &payload.id_to_find == id_to_find,
                _ => false,
             }
         }).take(usize::saturating_sub(nodes_to_query.len(), routing::IMPATIENCE));

      for node in nodes_to_query {
         let rpc = Rpc::find_node(
            self.id.clone(),
            self.inbound.local_addr().unwrap().port(),
            id_to_find.clone(),
            routing::ALPHA,
         );
         let packet = rpc.serialize();
         try!(self.outbound.send_to(&packet, node.address));
         queried.push(node.id.clone());
      }

      for response in responses {
         if let rpc::Kind::FindNodeResponse(ref payload) = response.kind {
            match payload.result {
               routing::LookupResult::Myself | routing::LookupResult::Found(_) => return Ok(()),
               _ => (),
            }
         }
      }

      Ok(())
   }

   pub fn process_incoming_rpc(&self, rpc: Rpc, mut source: net::SocketAddr) -> SubotaiResult<()>{
      source.set_port(rpc.reply_port);
      let sender = routing::NodeInfo {
         id      : rpc.sender_id.clone(),
         address : source,
      };

      let result = match rpc.kind {
         rpc::Kind::Ping                           => self.handle_ping(sender),
         rpc::Kind::PingResponse                   => self.handle_ping_response(sender),
         rpc::Kind::FindNode(ref payload)          => self.handle_find_node(payload.clone(), sender),
         rpc::Kind::FindNodeResponse(ref payload)  => self.handle_find_node_response(payload.clone(), sender),
         rpc::Kind::Bootstrap                      => self.handle_bootstrap(sender),
         rpc::Kind::BootstrapResponse(ref payload) => self.handle_bootstrap_response(payload.clone(),sender),
         _ => unimplemented!(),
      };

      self.updates.lock().unwrap().broadcast(Update::RpcReceived(rpc));
      result
   }

   fn handle_ping(&self, sender: routing::NodeInfo) -> SubotaiResult<()> {
      self.table.insert_node(sender.clone());
      let rpc = Rpc::ping_response(self.id.clone(), self.inbound.local_addr().unwrap().port());
      let packet = rpc.serialize();
      try!(self.outbound.send_to(&packet, sender.address));
      Ok(())
   }

   fn handle_bootstrap(&self, sender: routing::NodeInfo) -> SubotaiResult<()> {
      self.table.insert_node(sender.clone());
      let closest_to_sender: Vec<_> = self.table.closest_nodes_to(&sender.id)
         .filter(|ref info| &info.id != &sender.id) // We don't want to reply with the sender itself
         .take(routing::ALPHA)
         .collect();

      let rpc = Rpc::bootstrap_response(self.id.clone(), self.inbound.local_addr().unwrap().port(), closest_to_sender);
      let packet = rpc.serialize();
      try!(self.outbound.send_to(&packet, sender.address));
      Ok(())
   }

   fn handle_bootstrap_response(&self, payload: Arc<rpc::BootstrapResponsePayload>, sender: routing::NodeInfo) -> SubotaiResult<()> {
      self.table.insert_node(sender.clone());
      for node in &payload.nodes {
         self.table.insert_node(node.clone());
      }
      Ok(())
   }

   fn handle_ping_response(&self, sender: routing::NodeInfo) -> SubotaiResult<()> {
      self.table.insert_node(sender);
      Ok(())
   }

   fn handle_find_node(&self, payload: Arc<rpc::FindNodePayload>, sender: routing::NodeInfo) -> SubotaiResult<()> {
      self.table.insert_node(sender.clone());
      let lookup_results = self.table.lookup(&payload.id_to_find, payload.nodes_wanted, None);
      let rpc = Rpc::find_node_response(self.id.clone(),
                                        self.inbound.local_addr().unwrap().port(),
                                        payload.id_to_find.clone(),
                                        lookup_results);
      let packet = rpc.serialize();
      try!(self.outbound.send_to(&packet, sender.address));
      Ok(())
   }

   fn handle_find_node_response(&self, payload: Arc<rpc::FindNodeResponsePayload>, sender: routing::NodeInfo) -> SubotaiResult<()> {
      self.table.insert_node(sender);
      match payload.result {
         routing::LookupResult::ClosestNodes(ref nodes) => for node in nodes { self.table.insert_node(node.clone()) },
         routing::LookupResult::Found(ref node) => self.table.insert_node(node.clone()),
         _ => (),
      }
      Ok(())
   }
}

<|endoftext|>


<commit_before><commit_msg>Remove deprecated usage of mem::uninitialized<commit_after><|endoftext|>


<commit_before>#![crate_type = "bin"]

extern crate cairo;
extern crate pango_sys;
extern crate gdk;
extern crate glib;
extern crate gtk;
extern crate gtk_sys;
extern crate libc;
extern crate sysinfo;

use sysinfo::*;

use gtk::prelude::*;
use gtk::{AboutDialog, Button, Dialog, Entry, MenuBar, MessageDialog};

use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::ffi::CString;
use std::rc::Rc;
use std::time::Duration;
use std::thread::sleep;

use display_sysinfo::DisplaySysInfo;
use notebook::NoteBook;
use procs::{create_and_fill_model, Procs};

mod color;
mod display_sysinfo;
mod notebook;
mod process_dialog;
mod procs;
mod utils;

fn update_window(list: &gtk::ListStore, system: &Rc<RefCell<sysinfo::System>>,
                 info: &mut DisplaySysInfo) {
    let mut system = system.borrow_mut();
    system.refresh_all();
    info.update_ram_display(&system);
    info.update_process_display(&system);
    let entries: &HashMap<usize, Process> = system.get_process_list();
    let mut seen: HashSet<usize> = HashSet::new();

    if let Some(mut iter) = list.get_iter_first() {
        let mut valid = true;
        while valid {
            let pid = list.get_value(&iter, 0).get::<i64>().unwrap() as usize;
            if let Some(p) = entries.get(&(pid)) {
                list.set(&iter,
                         &[2, 3, 5],
                         &[&format!("{:.1}", p.cpu_usage), &p.memory, &p.cpu_usage]);
                valid = list.iter_next(&mut iter);
                seen.insert(pid);
            } else {
                valid = list.remove(&mut iter);
            }
        }
    }

    for (pid, pro) in entries.iter() {
        if !seen.contains(pid) {
            create_and_fill_model(list, pro.pid, &pro.cmd, &pro.name, pro.cpu_usage, pro.memory);
        }
    }
}

fn parse_quote(line: &str, quote: char) -> Vec<CString> {
    let args = line.split(quote).collect::<Vec<&str>>();
    let mut out_args = vec!();

    for (num, arg) in args.iter().enumerate() {
        if num != 1 {
            out_args.extend_from_slice(&parse_entry(arg));
        } else {
            out_args.push(CString::new(*arg).unwrap());
        }
    }
    out_args
}

// super simple parsing
fn parse_entry(line: &str) -> Vec<CString> {
    match (line.find('\''), line.find('"')) {
        (Some(x), Some(y)) => {
            if x < y {
                parse_quote(line, '\'')
            } else {
                parse_quote(line, '"')
            }
        }
        (Some(_), None) => parse_quote(line, '\''),
        (None, Some(_)) => parse_quote(line, '"'),
        (None, None) => line.split(' ').into_iter()
                                       .map(|s| CString::new(s).unwrap())
                                       .collect::<Vec<CString>>(),
    }
}

fn start_detached_process(line: &str) -> Option<i32> {
    let args = parse_entry(line);
    let command = args[0].clone();
    let mut args = args.into_iter()
                       .map(|s| s.as_ptr())
                       .collect::<Vec<*const libc::c_char>>();
    args.push(std::ptr::null());

    unsafe {
        let pid = libc::fork();

        if pid < 0 {
            libc::exit(2); // error
        } else if pid == 0 {
            libc::umask(0);
            if libc::setsid() < 0 {
                libc::exit(3);
            }
            libc::chdir("/".as_ptr() as *const i8);
            /*let stdin = libc::fdopen(0, "r".as_ptr() as *const i8);
            let stdout = libc::fdopen(1, "w".as_ptr() as *const i8);
            let stderr = libc::fdopen(2, "w".as_ptr() as *const i8);
            libc::freopen("/dev/null".as_ptr() as *const i8, "r".as_ptr() as *const i8, stdin);
            libc::freopen("/dev/null".as_ptr() as *const i8, "w".as_ptr() as *const i8, stdout);
            libc::freopen("/dev/null".as_ptr() as *const i8, "w".as_ptr() as *const i8, stderr);*/
            libc::execv(command.as_ptr(), args.as_ptr());
            None
        } else {
            sleep(Duration::from_millis(100));
            if libc::kill(pid, 0) != 0 {
                let mut status = 0i32;
                // the process isn't running, getting error code
                libc::wait(&mut status as *mut i32);
                Some(status)
            } else {
                None
            }
        }
    }
}

fn run_command(input: &Entry, window: &gtk::Window, d: &Dialog) {
    if let Some(text) = input.get_text() {
        let x = if let Some(x) = start_detached_process(&text) {
            x
        } else {
            0
        };
        d.destroy();
        let m = MessageDialog::new(Some(window),
                                   gtk::DIALOG_DESTROY_WITH_PARENT,
                                   gtk::MessageType::Info,
                                   gtk::ButtonsType::Ok,
                                   &format!("The command exited with {} code", x));
        m.show_all();
    }
}

fn main() {
    gtk::init().expect("GTK couldn't start normally");

    let window = gtk::Window::new(gtk::WindowType::Toplevel);
    let sys = sysinfo::System::new();
    let start_time = unsafe { sys.get_process(libc::getpid() as i64).unwrap().start_time };
    let running_since = Rc::new(RefCell::new(0));
    let sys = Rc::new(RefCell::new(sys));
    let mut note = NoteBook::new();
    let procs = Procs::new(sys.borrow().get_process_list(), &mut note);
    let current_pid = procs.current_pid.clone();
    let current_pid2 = procs.current_pid.clone();
    let sys1 = sys.clone();
    let sys2 = sys.clone();
    let sys3 = sys.clone();
    let info_button = procs.info_button.clone();

    window.set_title("Process viewer");
    window.set_position(gtk::WindowPosition::Center);

    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(false)
    });

    sys.borrow_mut().refresh_all();
    procs.kill_button.connect_clicked(move |_| {
        let sys = sys1.borrow();
        if let Some(process) = current_pid.get().and_then(|pid| sys.get_process(pid)) {
            process.kill(Signal::Kill);
        }
    });

    let mut display_tab = DisplaySysInfo::new(sys.clone(), &mut note, &window);

    let v_box = gtk::Box::new(gtk::Orientation::Vertical, 0);
    let menu_bar = MenuBar::new();
    let menu = gtk::Menu::new();
    let more_menu = gtk::Menu::new();
    let file = gtk::MenuItem::new_with_label("File");
    let new_task = gtk::MenuItem::new_with_label("Launch new executable");
    let quit = gtk::MenuItem::new_with_label("Quit");
    let more = gtk::MenuItem::new_with_label("?");
    let about = gtk::MenuItem::new_with_label("About");

    menu.append(&new_task);
    menu.append(&quit);
    file.set_submenu(Some(&menu));
    menu_bar.append(&file);
    more_menu.append(&about);
    more.set_submenu(Some(&more_menu));
    menu_bar.append(&more);

    v_box.pack_start(&menu_bar, false, false, 0);
    v_box.pack_start(&note.notebook, true, true, 0);

    window.add(&v_box);
    window.show_all();
    let window1 = window.clone();
    let window2 = window.clone();

    let list_store = procs.list_store.clone();
    let run1 = running_since.clone();
    let run2 = running_since.clone();
    gtk::timeout_add(1000, move || {
        *run1.borrow_mut() += 1;
        // first part, deactivate sorting
        let sorted = list_store.get_sort_column_id();
        list_store.set_unsorted();

        // we update the tree view
        update_window(&list_store, &sys, &mut display_tab);

        // we re-enable the sorting
        if let Some((col, order)) = sorted {
            list_store.set_sort_column_id(col, order);
        }
        window1.queue_draw();
        glib::Continue(true)
    });
    info_button.connect_clicked(move |_| {
        let sys = sys2.borrow();
        if let Some(process) = current_pid2.get().and_then(|pid| sys.get_process(pid)) {
            process_dialog::create_process_dialog(&process, &window2, start_time,
                                                  run2.borrow().clone());
        }
    });
    let window2 = window.clone();
    procs.left_tree.connect_row_activated(move |tree_view, path, _| {
        let model = tree_view.get_model().unwrap();
        let iter = model.get_iter(path).unwrap();
        let sys = sys3.borrow();
        if let Some(process) = sys.get_process(model.get_value(&iter, 0).get().unwrap()) {
            process_dialog::create_process_dialog(&process, &window2, start_time,
                                                  running_since.borrow().clone());
        }
    });

    quit.connect_activate(|_| {
        gtk::main_quit();
    });
    let window3 = window.clone();
    about.connect_activate(move |_| {
        let p = AboutDialog::new();
        p.set_authors(&["Guillaume Gomez"]);
        p.set_website_label(Some("my website"));
        p.set_website(Some("http://guillaume-gomez.fr/"));
        p.set_comments(Some("A process viewer GUI wrote with gtk-rs"));
        p.set_copyright(Some("This is under MIT license"));
        p.set_transient_for(Some(&window3));
        p.run();
        p.destroy();
    });
    new_task.connect_activate(move |_| {
        let d = Dialog::new();
        d.set_title("Launch new executable");
        let content_area = d.get_content_area();
        let input = Entry::new();
        let run = Button::new_with_label("Run");
        let cancel = Button::new_with_label("Cancel");
        let v_box = gtk::Box::new(gtk::Orientation::Vertical, 0);
        let h_box = gtk::Box::new(gtk::Orientation::Horizontal, 0);
        h_box.pack_start(&run, true, true, 0);
        h_box.pack_start(&cancel, true, true, 0);
        v_box.pack_start(&input, true, true, 0);
        v_box.pack_start(&h_box, true, true, 0);
        content_area.add(&v_box);
        let window2 = window.clone();
        d.set_transient_for(Some(&window2));
        d.set_size_request(400, 70);
        d.show_all();

        run.set_sensitive(false);
        let run2 = run.clone();
        let input2 = input.clone();
        input.connect_changed(move |_| {
            match input2.get_text() {
                Some(ref x) if x.len() > 0 => run2.set_sensitive(true),
                _ => run2.set_sensitive(false),
            }
        });
        let d2 = d.clone();
        cancel.connect_clicked(move |_| {
            d2.destroy();
        });
        let window3 = window.clone();
        let input3 = input.clone();
        let d3 = d.clone();
        input.connect_activate(move |_| {
            run_command(&input3, &window3, &d3);
        });
        let window4 = window.clone();
        run.connect_clicked(move |_| {
            run_command(&input, &window4, &d);
        });
    });

    gtk::main();
}
<commit_msg>Add icons and mnemonic<commit_after>#![crate_type = "bin"]
#![feature(process_exec)]

extern crate cairo;
extern crate pango_sys;
extern crate gdk;
extern crate glib;
extern crate gtk;
extern crate gtk_sys;
extern crate libc;
extern crate sysinfo;

use sysinfo::*;

use gtk::prelude::*;
use gtk::{AboutDialog, Button, Dialog, Entry, IconSize, Image, Label, MenuBar, MenuItem, MessageDialog};

use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::process::{Command, Stdio};
use std::os::unix::process::CommandExt;
use std::rc::Rc;

use display_sysinfo::DisplaySysInfo;
use notebook::NoteBook;
use procs::{create_and_fill_model, Procs};

mod color;
mod display_sysinfo;
mod notebook;
mod process_dialog;
mod procs;
mod utils;

fn update_window(list: &gtk::ListStore, system: &Rc<RefCell<sysinfo::System>>,
                 info: &mut DisplaySysInfo) {
    let mut system = system.borrow_mut();
    system.refresh_all();
    info.update_ram_display(&system);
    info.update_process_display(&system);
    let entries: &HashMap<usize, Process> = system.get_process_list();
    let mut seen: HashSet<usize> = HashSet::new();

    if let Some(mut iter) = list.get_iter_first() {
        let mut valid = true;
        while valid {
            let pid = list.get_value(&iter, 0).get::<i64>().unwrap() as usize;
            if let Some(p) = entries.get(&(pid)) {
                list.set(&iter,
                         &[2, 3, 5],
                         &[&format!("{:.1}", p.cpu_usage), &p.memory, &p.cpu_usage]);
                valid = list.iter_next(&mut iter);
                seen.insert(pid);
            } else {
                valid = list.remove(&mut iter);
            }
        }
    }

    for (pid, pro) in entries.iter() {
        if !seen.contains(pid) {
            create_and_fill_model(list, pro.pid, &pro.cmd, &pro.name, pro.cpu_usage, pro.memory);
        }
    }
}

fn parse_quote(line: &str, quote: char) -> Vec<String> {
    let args = line.split(quote).collect::<Vec<&str>>();
    let mut out_args = vec!();

    for (num, arg) in args.iter().enumerate() {
        if num != 1 {
            out_args.extend_from_slice(&parse_entry(arg));
        } else {
            out_args.push((*arg).to_owned());
        }
    }
    out_args
}

// super simple parsing
fn parse_entry(line: &str) -> Vec<String> {
    match (line.find('\''), line.find('"')) {
        (Some(x), Some(y)) => {
            if x < y {
                parse_quote(line, '\'')
            } else {
                parse_quote(line, '"')
            }
        }
        (Some(_), None) => parse_quote(line, '\''),
        (None, Some(_)) => parse_quote(line, '"'),
        (None, None) => line.split(' ').into_iter()
                                       .map(|s| s.to_owned())
                                       .collect::<Vec<String>>(),
    }
}

fn start_detached_process(line: &str) -> Option<String> {
    let args = parse_entry(line);
    let command = args[0].clone();
    /*let args = args.into_iter()
                   .map(|s| s.as_ref())
                   .collect::<Vec<OsStr>>();*/
    if let Err(_) = Command::new(&command)
                            .args(&args)
                            .before_exec(|| {
                                unsafe { libc::setsid() };
                                Ok(())
                            })
                            .stdin(Stdio::null())
                            .stderr(Stdio::null())
                            .stdout(Stdio::null())
                            .spawn() {
        Some(format!("Failed to start '{}'", &command))
    } else {
        None
    }
}

fn run_command(input: &Entry, window: &gtk::Window, d: &Dialog) {
    if let Some(text) = input.get_text() {
        let x = if let Some(x) = start_detached_process(&text) {
            x
        } else {
            "The command started successfully".to_owned()
        };
        d.destroy();
        let m = MessageDialog::new(Some(window),
                                   gtk::DIALOG_DESTROY_WITH_PARENT,
                                   gtk::MessageType::Info,
                                   gtk::ButtonsType::Ok,
                                   &x);
        m.run();
        m.destroy();
    }
}

fn create_image_menu_item(label: &str, icon: &str) -> MenuItem {
    let item = gtk::MenuItem::new();
    let _box = gtk::Box::new(gtk::Orientation::Horizontal, 6);
    let image = Image::new_from_icon_name(icon, IconSize::Menu.into());
    let label = Label::new(Some(label));

    _box.add(&image);
    _box.add(&label);
    item.add(&_box);
    item
}

fn main() {
    gtk::init().expect("GTK couldn't start normally");

    let window = gtk::Window::new(gtk::WindowType::Toplevel);
    let sys = sysinfo::System::new();
    let start_time = unsafe { sys.get_process(libc::getpid() as i64).unwrap().start_time };
    let running_since = Rc::new(RefCell::new(0));
    let sys = Rc::new(RefCell::new(sys));
    let mut note = NoteBook::new();
    let procs = Procs::new(sys.borrow().get_process_list(), &mut note);
    let current_pid = procs.current_pid.clone();
    let current_pid2 = procs.current_pid.clone();
    let sys1 = sys.clone();
    let sys2 = sys.clone();
    let sys3 = sys.clone();
    let info_button = procs.info_button.clone();

    window.set_title("Process viewer");
    window.set_position(gtk::WindowPosition::Center);

    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(false)
    });

    sys.borrow_mut().refresh_all();
    procs.kill_button.connect_clicked(move |_| {
        let sys = sys1.borrow();
        if let Some(process) = current_pid.get().and_then(|pid| sys.get_process(pid)) {
            process.kill(Signal::Kill);
        }
    });

    let mut display_tab = DisplaySysInfo::new(sys.clone(), &mut note, &window);

    let v_box = gtk::Box::new(gtk::Orientation::Vertical, 0);
    let menu_bar = MenuBar::new();
    let menu = gtk::Menu::new();
    let more_menu = gtk::Menu::new();
    let file = gtk::MenuItem::new_with_mnemonic("_File");
    let new_task = create_image_menu_item("Launch new executable", "system-run");
    let quit = create_image_menu_item("Quit", "application-exit");
    let more = gtk::MenuItem::new_with_label("?");
    let about = create_image_menu_item("About", "help-about");

    menu.append(&new_task);
    menu.append(&quit);
    file.set_submenu(Some(&menu));
    menu_bar.append(&file);
    more_menu.append(&about);
    more.set_submenu(Some(&more_menu));
    menu_bar.append(&more);

    v_box.pack_start(&menu_bar, false, false, 0);
    v_box.pack_start(&note.notebook, true, true, 0);

    window.add(&v_box);
    window.show_all();
    let window1 = window.clone();
    let window2 = window.clone();

    let list_store = procs.list_store.clone();
    let run1 = running_since.clone();
    let run2 = running_since.clone();
    gtk::timeout_add(1000, move || {
        *run1.borrow_mut() += 1;
        // first part, deactivate sorting
        let sorted = list_store.get_sort_column_id();
        list_store.set_unsorted();

        // we update the tree view
        update_window(&list_store, &sys, &mut display_tab);

        // we re-enable the sorting
        if let Some((col, order)) = sorted {
            list_store.set_sort_column_id(col, order);
        }
        window1.queue_draw();
        glib::Continue(true)
    });
    info_button.connect_clicked(move |_| {
        let sys = sys2.borrow();
        if let Some(process) = current_pid2.get().and_then(|pid| sys.get_process(pid)) {
            process_dialog::create_process_dialog(&process, &window2, start_time,
                                                  run2.borrow().clone());
        }
    });
    let window2 = window.clone();
    procs.left_tree.connect_row_activated(move |tree_view, path, _| {
        let model = tree_view.get_model().unwrap();
        let iter = model.get_iter(path).unwrap();
        let sys = sys3.borrow();
        if let Some(process) = sys.get_process(model.get_value(&iter, 0).get().unwrap()) {
            process_dialog::create_process_dialog(&process, &window2, start_time,
                                                  running_since.borrow().clone());
        }
    });

    quit.connect_activate(|_| {
        gtk::main_quit();
    });
    let window3 = window.clone();
    about.connect_activate(move |_| {
        let p = AboutDialog::new();
        p.set_authors(&["Guillaume Gomez"]);
        p.set_website_label(Some("my website"));
        p.set_website(Some("http://guillaume-gomez.fr/"));
        p.set_comments(Some("A process viewer GUI wrote with gtk-rs"));
        p.set_copyright(Some("This is under MIT license"));
        p.set_transient_for(Some(&window3));
        p.run();
        p.destroy();
    });
    new_task.connect_activate(move |_| {
        let d = Dialog::new();
        d.set_title("Launch new executable");
        let content_area = d.get_content_area();
        let input = Entry::new();
        let run = Button::new_with_label("Run");
        let cancel = Button::new_with_label("Cancel");
        let v_box = gtk::Box::new(gtk::Orientation::Vertical, 0);
        let h_box = gtk::Box::new(gtk::Orientation::Horizontal, 0);
        h_box.pack_start(&run, true, true, 0);
        h_box.pack_start(&cancel, true, true, 0);
        v_box.pack_start(&input, true, true, 0);
        v_box.pack_start(&h_box, true, true, 0);
        content_area.add(&v_box);
        let window2 = window.clone();
        d.set_transient_for(Some(&window2));
        d.set_size_request(400, 70);
        d.show_all();

        run.set_sensitive(false);
        let run2 = run.clone();
        let input2 = input.clone();
        input.connect_changed(move |_| {
            match input2.get_text() {
                Some(ref x) if x.len() > 0 => run2.set_sensitive(true),
                _ => run2.set_sensitive(false),
            }
        });
        let d2 = d.clone();
        cancel.connect_clicked(move |_| {
            d2.destroy();
        });
        let window3 = window.clone();
        let input3 = input.clone();
        let d3 = d.clone();
        input.connect_activate(move |_| {
            run_command(&input3, &window3, &d3);
        });
        let window4 = window.clone();
        run.connect_clicked(move |_| {
            run_command(&input, &window4, &d);
        });
    });

    gtk::main();
}
<|endoftext|>


<commit_before>// ignore-windows: File handling is not implemented yet
// compile-flags: -Zmiri-disable-isolation

use std::fs::File;
use std::io::Read;

fn main() {
    let mut file = File::open("./tests/hello.txt").unwrap();
    let mut contents = String::new();
    file.read_to_string(&mut contents).unwrap();
    assert_eq!("Hello, World!\n", contents);
}
<commit_msg>Add FIXME to file reading test<commit_after>// ignore-windows: File handling is not implemented yet
// compile-flags: -Zmiri-disable-isolation

use std::fs::File;
use std::io::Read;

fn main() {
    // FIXME: create the file and delete it when `rm` is implemented.
    let mut file = File::open("./tests/hello.txt").unwrap();
    let mut contents = String::new();
    file.read_to_string(&mut contents).unwrap();
    assert_eq!("Hello, World!\n", contents);
}
<|endoftext|>


<commit_before>#![feature(arbitrary_self_types, futures_api)]
#![allow(unused)]

use std::future::Future;
use std::pin::Pin;
use std::sync::{
    Arc,
    atomic::{self, AtomicUsize},
};
use std::task::{
    Poll, Waker, RawWaker, RawWakerVTable,
};

macro_rules! waker_vtable {
    ($ty:ident) => {
        &RawWakerVTable {
            clone: clone_arc_raw::<$ty>,
            drop: drop_arc_raw::<$ty>,
            wake: wake_arc_raw::<$ty>,
        }
    };
}

pub trait ArcWake {
    fn wake(arc_self: &Arc<Self>);

    fn into_waker(wake: Arc<Self>) -> Waker where Self: Sized
    {
        let ptr = Arc::into_raw(wake) as *const();

        unsafe {
            Waker::new_unchecked(RawWaker{
                data: ptr,
                vtable: waker_vtable!(Self),
            })
        }
    }
}

unsafe fn increase_refcount<T: ArcWake>(data: *const()) {
    // Retain Arc by creating a copy
    let arc: Arc<T> = Arc::from_raw(data as *const T);
    let arc_clone = arc.clone();
    // Forget the Arcs again, so that the refcount isn't decrased
    let _ = Arc::into_raw(arc);
    let _ = Arc::into_raw(arc_clone);
}

unsafe fn clone_arc_raw<T: ArcWake>(data: *const()) -> RawWaker {
    increase_refcount::<T>(data);
    RawWaker {
        data: data,
        vtable: waker_vtable!(T),
    }
}

unsafe fn drop_arc_raw<T: ArcWake>(data: *const()) {
    // Drop Arc
    let _: Arc<T> = Arc::from_raw(data as *const T);
}

unsafe fn wake_arc_raw<T: ArcWake>(data: *const()) {
    let arc: Arc<T> = Arc::from_raw(data as *const T);
    ArcWake::wake(&arc);
    let _ = Arc::into_raw(arc);
}

struct Counter {
    wakes: AtomicUsize,
}

impl Wake for Counter {
    fn wake(arc_self: &Arc<Self>) {
        arc_self.wakes.fetch_add(1, atomic::Ordering::SeqCst);
    }
}

struct MyFuture;

impl Future for MyFuture {
    type Output = ();
    fn poll(self: Pin<&mut Self>, waker: &Waker) -> Poll<Self::Output> {
        // Wake twice
        waker.wake();
        waker.wake();
        Poll::Ready(())
    }
}

fn test_waker() {
    let counter = Arc::new(Counter {
        wakes: AtomicUsize::new(0),
    });
    let waker = ArcWake::into_waker(counter.clone());
    assert_eq!(2, Arc::strong_count(&counter));

    assert_eq!(Poll::Ready(()), Pin::new(&mut MyFuture).poll(&waker));
    assert_eq!(2, counter.wakes.load(atomic::Ordering::SeqCst));

    drop(waker);
    assert_eq!(1, Arc::strong_count(&counter));
}

fn main() {
    test_waker();
}
<commit_msg>Fix test<commit_after>#![feature(arbitrary_self_types, futures_api)]
#![allow(unused)]

use std::future::Future;
use std::pin::Pin;
use std::sync::{
    Arc,
    atomic::{self, AtomicUsize},
};
use std::task::{
    Poll, Waker, RawWaker, RawWakerVTable,
};

macro_rules! waker_vtable {
    ($ty:ident) => {
        &RawWakerVTable {
            clone: clone_arc_raw::<$ty>,
            drop: drop_arc_raw::<$ty>,
            wake: wake_arc_raw::<$ty>,
        }
    };
}

pub trait ArcWake {
    fn wake(arc_self: &Arc<Self>);

    fn into_waker(wake: Arc<Self>) -> Waker where Self: Sized
    {
        let ptr = Arc::into_raw(wake) as *const();

        unsafe {
            Waker::new_unchecked(RawWaker{
                data: ptr,
                vtable: waker_vtable!(Self),
            })
        }
    }
}

unsafe fn increase_refcount<T: ArcWake>(data: *const()) {
    // Retain Arc by creating a copy
    let arc: Arc<T> = Arc::from_raw(data as *const T);
    let arc_clone = arc.clone();
    // Forget the Arcs again, so that the refcount isn't decrased
    let _ = Arc::into_raw(arc);
    let _ = Arc::into_raw(arc_clone);
}

unsafe fn clone_arc_raw<T: ArcWake>(data: *const()) -> RawWaker {
    increase_refcount::<T>(data);
    RawWaker {
        data: data,
        vtable: waker_vtable!(T),
    }
}

unsafe fn drop_arc_raw<T: ArcWake>(data: *const()) {
    // Drop Arc
    let _: Arc<T> = Arc::from_raw(data as *const T);
}

unsafe fn wake_arc_raw<T: ArcWake>(data: *const()) {
    let arc: Arc<T> = Arc::from_raw(data as *const T);
    ArcWake::wake(&arc);
    let _ = Arc::into_raw(arc);
}

struct Counter {
    wakes: AtomicUsize,
}

impl ArcWake for Counter {
    fn wake(arc_self: &Arc<Self>) {
        arc_self.wakes.fetch_add(1, atomic::Ordering::SeqCst);
    }
}

struct MyFuture;

impl Future for MyFuture {
    type Output = ();
    fn poll(self: Pin<&mut Self>, waker: &Waker) -> Poll<Self::Output> {
        // Wake twice
        waker.wake();
        waker.wake();
        Poll::Ready(())
    }
}

fn test_waker() {
    let counter = Arc::new(Counter {
        wakes: AtomicUsize::new(0),
    });
    let waker = ArcWake::into_waker(counter.clone());
    assert_eq!(2, Arc::strong_count(&counter));

    assert_eq!(Poll::Ready(()), Pin::new(&mut MyFuture).poll(&waker));
    assert_eq!(2, counter.wakes.load(atomic::Ordering::SeqCst));

    drop(waker);
    assert_eq!(1, Arc::strong_count(&counter));
}

fn main() {
    test_waker();
}
<|endoftext|>