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

use app_units::Au;
use core_graphics::base::kCGImageAlphaPremultipliedLast;
use core_graphics::color_space::CGColorSpace;
use core_graphics::context::CGContext;
use core_graphics::data_provider::CGDataProvider;
use core_graphics::font::{CGFont, CGGlyph};
use core_graphics::geometry::CGPoint;
use core_text::font::CTFont;
use core_text::font_descriptor::kCTFontDefaultOrientation;
use core_text;
use internal_types::FontRenderMode;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use webrender_traits::FontKey;

pub type NativeFontHandle = CGFont;

pub struct FontContext {
    cg_fonts: HashMap<FontKey, CGFont>,
    ct_fonts: HashMap<(FontKey, Au), CTFont>,
}

pub struct RasterizedGlyph {
    pub left: i32,
    pub top: i32,
    pub width: u32,
    pub height: u32,
    pub bytes: Vec<u8>,
}

impl RasterizedGlyph {
    pub fn blank() -> RasterizedGlyph {
        RasterizedGlyph {
            left: 0,
            top: 0,
            width: 0,
            height: 0,
            bytes: vec![],
        }
    }
}

impl FontContext {
    pub fn new() -> FontContext {
        FontContext {
            cg_fonts: HashMap::new(),
            ct_fonts: HashMap::new(),
        }
    }

    pub fn add_raw_font(&mut self, font_key: &FontKey, bytes: &[u8]) {
        if self.cg_fonts.contains_key(font_key) {
            return
        }

        let data_provider = CGDataProvider::from_buffer(bytes);
        let cg_font = match CGFont::from_data_provider(data_provider) {
            Err(_) => return,
            Ok(cg_font) => cg_font,
        };
        self.cg_fonts.insert((*font_key).clone(), cg_font);
    }

    pub fn add_native_font(&mut self, font_key: &FontKey, native_font_handle: CGFont) {
        if self.cg_fonts.contains_key(font_key) {
            return
        }

        self.cg_fonts.insert((*font_key).clone(), native_font_handle);
    }

    pub fn get_glyph(&mut self,
                     font_key: FontKey,
                     size: Au,
                     character: u32,
                     device_pixel_ratio: f32,
                     render_mode: FontRenderMode)
                     -> Option<RasterizedGlyph> {
        let ct_font = match self.ct_fonts.entry(((font_key).clone(), size)) {
            Entry::Occupied(entry) => (*entry.get()).clone(),
            Entry::Vacant(entry) => {
                let cg_font = match self.cg_fonts.get(&font_key) {
                    None => return Some(RasterizedGlyph::blank()),
                    Some(cg_font) => cg_font,
                };
                let ct_font = core_text::font::new_from_CGFont(
                        cg_font,
                        size.to_f64_px() * (device_pixel_ratio as f64));
                entry.insert(ct_font.clone());
                ct_font
            }
        };

        let glyph = character as CGGlyph;
        let bounds = ct_font.get_bounding_rects_for_glyphs(kCTFontDefaultOrientation, &[glyph]);

        let rasterized_left = bounds.origin.x.floor() as i32;
        let rasterized_width =
            (bounds.origin.x - (rasterized_left as f64) + bounds.size.width).ceil() as u32;
        let rasterized_descent = (-bounds.origin.y).ceil() as i32;
        let rasterized_ascent = (bounds.size.height + bounds.origin.y).ceil() as i32;
        let rasterized_height = (rasterized_descent + rasterized_ascent) as u32;
        if rasterized_width == 0 || rasterized_height == 0 {
            return Some(RasterizedGlyph::blank())
        }

        let mut cg_context = CGContext::create_bitmap_context(rasterized_width as usize,
                                                              rasterized_height as usize,
                                                              8,
                                                              rasterized_width as usize * 4,
                                                              &CGColorSpace::create_device_rgb(),
                                                              kCGImageAlphaPremultipliedLast);
        // TODO(gw): Add subpixel render mode support on mac.
        let enable_aa = render_mode != FontRenderMode::Mono;
        cg_context.set_allows_font_smoothing(enable_aa);
        cg_context.set_should_smooth_fonts(enable_aa);
        cg_context.set_allows_antialiasing(enable_aa);
        cg_context.set_should_antialias(enable_aa);
        cg_context.set_rgb_fill_color(1.0, 1.0, 1.0, 1.0);

        let rasterization_origin = CGPoint {
            x: -rasterized_left as f64,
            y: rasterized_descent as f64,
        };
        ct_font.draw_glyphs(&[glyph], &[rasterization_origin], cg_context.clone());

        let rasterized_area = (rasterized_width * rasterized_height) as usize;
        let mut rasterized_pixels = cg_context.data().to_vec();
        for i in 0..rasterized_area {
            let alpha = (rasterized_pixels[i * 4 + 3] as f32) / 255.0;
            for j in 0..3 {
                rasterized_pixels[i * 4 + j] =
                    ((rasterized_pixels[i * 4 + j] as f32) / alpha) as u8;
            }
        }

        Some(RasterizedGlyph {
            left: rasterized_left,
            top: (bounds.size.height + bounds.origin.y).ceil() as i32,
            width: rasterized_width,
            height: rasterized_height,
            bytes: rasterized_pixels,
        })
    }
}

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

use app_units::Au;
use core_graphics::base::kCGImageAlphaPremultipliedLast;
use core_graphics::color_space::CGColorSpace;
use core_graphics::context::CGContext;
use core_graphics::data_provider::CGDataProvider;
use core_graphics::font::{CGFont, CGGlyph};
use core_graphics::geometry::CGPoint;
use core_text::font::CTFont;
use core_text::font_descriptor::kCTFontDefaultOrientation;
use core_text;
use internal_types::FontRenderMode;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use webrender_traits::FontKey;

pub type NativeFontHandle = CGFont;

pub struct FontContext {
    cg_fonts: HashMap<FontKey, CGFont>,
    ct_fonts: HashMap<(FontKey, Au), CTFont>,
}

pub struct RasterizedGlyph {
    pub left: i32,
    pub top: i32,
    pub width: u32,
    pub height: u32,
    pub bytes: Vec<u8>,
}

impl RasterizedGlyph {
    pub fn blank() -> RasterizedGlyph {
        RasterizedGlyph {
            left: 0,
            top: 0,
            width: 0,
            height: 0,
            bytes: vec![],
        }
    }
}

struct GlyphMetrics {
    rasterized_left: i32,
    rasterized_descent: i32,
    rasterized_ascent: i32,
    rasterized_width: u32,
    rasterized_height: u32,
}

fn get_glyph_metrics(ct_font: &CTFont, glyph: CGGlyph) -> GlyphMetrics {
    let bounds = ct_font.get_bounding_rects_for_glyphs(kCTFontDefaultOrientation, &[glyph]);

    let rasterized_left = bounds.origin.x.floor() as i32;
    let rasterized_width =
        (bounds.origin.x - (rasterized_left as f64) + bounds.size.width).ceil() as u32;
    let rasterized_descent = (-bounds.origin.y).ceil() as i32;
    let rasterized_ascent = (bounds.size.height + bounds.origin.y).ceil() as i32;
    let rasterized_height = (rasterized_descent + rasterized_ascent) as u32;

    GlyphMetrics {
        rasterized_ascent: rasterized_ascent,
        rasterized_descent: rasterized_descent,
        rasterized_left: rasterized_left,
        rasterized_width: rasterized_width,
        rasterized_height: rasterized_height,
    }
}

impl FontContext {
    pub fn new() -> FontContext {
        FontContext {
            cg_fonts: HashMap::new(),
            ct_fonts: HashMap::new(),
        }
    }

    pub fn add_raw_font(&mut self, font_key: &FontKey, bytes: &[u8]) {
        if self.cg_fonts.contains_key(font_key) {
            return
        }

        let data_provider = CGDataProvider::from_buffer(bytes);
        let cg_font = match CGFont::from_data_provider(data_provider) {
            Err(_) => return,
            Ok(cg_font) => cg_font,
        };
        self.cg_fonts.insert((*font_key).clone(), cg_font);
    }

    pub fn add_native_font(&mut self, font_key: &FontKey, native_font_handle: CGFont) {
        if self.cg_fonts.contains_key(font_key) {
            return
        }

        self.cg_fonts.insert((*font_key).clone(), native_font_handle);
    }

    fn get_ct_font(&mut self,
                   font_key: FontKey,
                   size: Au,
                   device_pixel_ratio: f32) -> Option<CTFont> {
        match self.ct_fonts.entry(((font_key).clone(), size)) {
            Entry::Occupied(entry) => Some((*entry.get()).clone()),
            Entry::Vacant(entry) => {
                let cg_font = match self.cg_fonts.get(&font_key) {
                    None => return None,
                    Some(cg_font) => cg_font,
                };
                let ct_font = core_text::font::new_from_CGFont(
                        cg_font,
                        size.to_f64_px() * (device_pixel_ratio as f64));
                entry.insert(ct_font.clone());
                Some(ct_font)
            }
        }
    }

    #[allow(dead_code)]     // TODO(gw): Expose this to the public glyph dimensions API.
    pub fn get_glyph_dimensions(&mut self,
                                font_key: FontKey,
                                size: Au,
                                character: u32,
                                device_pixel_ratio: f32) -> Option<(Au, Au)> {
        self.get_ct_font(font_key, size, device_pixel_ratio).map(|ref ct_font| {
            let glyph = character as CGGlyph;
            let metrics = get_glyph_metrics(ct_font, glyph);
            (Au::from_px(metrics.rasterized_width as i32), Au::from_px(metrics.rasterized_height as i32))
        })
    }

    pub fn get_glyph(&mut self,
                     font_key: FontKey,
                     size: Au,
                     character: u32,
                     device_pixel_ratio: f32,
                     render_mode: FontRenderMode)
                     -> Option<RasterizedGlyph> {
        match self.get_ct_font(font_key, size, device_pixel_ratio) {
            Some(ref ct_font) => {
                let glyph = character as CGGlyph;
                let metrics = get_glyph_metrics(ct_font, glyph);
                if metrics.rasterized_width == 0 || metrics.rasterized_height == 0 {
                    return Some(RasterizedGlyph::blank())
                }

                let mut cg_context = CGContext::create_bitmap_context(metrics.rasterized_width as usize,
                                                                      metrics.rasterized_height as usize,
                                                                      8,
                                                                      metrics.rasterized_width as usize * 4,
                                                                      &CGColorSpace::create_device_rgb(),
                                                                      kCGImageAlphaPremultipliedLast);
                // TODO(gw): Add subpixel render mode support on mac.
                let enable_aa = render_mode != FontRenderMode::Mono;
                cg_context.set_allows_font_smoothing(enable_aa);
                cg_context.set_should_smooth_fonts(enable_aa);
                cg_context.set_allows_antialiasing(enable_aa);
                cg_context.set_should_antialias(enable_aa);
                cg_context.set_rgb_fill_color(1.0, 1.0, 1.0, 1.0);

                let rasterization_origin = CGPoint {
                    x: -metrics.rasterized_left as f64,
                    y: metrics.rasterized_descent as f64,
                };
                ct_font.draw_glyphs(&[glyph], &[rasterization_origin], cg_context.clone());

                let rasterized_area = (metrics.rasterized_width * metrics.rasterized_height) as usize;
                let mut rasterized_pixels = cg_context.data().to_vec();
                for i in 0..rasterized_area {
                    let alpha = (rasterized_pixels[i * 4 + 3] as f32) / 255.0;
                    for j in 0..3 {
                        rasterized_pixels[i * 4 + j] =
                            ((rasterized_pixels[i * 4 + j] as f32) / alpha) as u8;
                    }
                }

                Some(RasterizedGlyph {
                    left: metrics.rasterized_left,
                    top: metrics.rasterized_ascent,
                    width: metrics.rasterized_width,
                    height: metrics.rasterized_height,
                    bytes: rasterized_pixels,
                })
            }
            None => {
                return Some(RasterizedGlyph::blank());
            }
        }
    }
}

<|endoftext|>


<commit_before>use wasm_bindgen::prelude::*;

use anyhow::Error;
use lancelot::{
    analysis::dis::{self, zydis},
    arch::Arch,
    aspace::AddressSpace,
    loader::pe::{PEError, PE as lPE},
    module::ModuleError,
    pagemap::PageMapError,
    util::UtilError,
};

#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

#[wasm_bindgen]
pub fn version() -> String {
    String::from("v0.7.0")
}

#[wasm_bindgen(start)]
pub fn main_js() -> Result<(), JsValue> {
    #[cfg(debug_assertions)]
    console_error_panic_hook::set_once();

    Ok(())
}

// for better function signatures
// no functional difference.
type JsError = JsValue;

fn to_value_error(e: anyhow::Error) -> JsError {
    js_sys::Error::new(&format!("{}", e)).into()
}

fn to_js_err(e: Error) -> JsError {
    match e.downcast_ref::<PEError>() {
        Some(PEError::FormatNotSupported(_)) => return to_value_error(e),
        Some(PEError::MalformedPEFile(_)) => return to_value_error(e),
        None => (),
    };

    #[allow(clippy::single_match)]
    match e.downcast_ref::<ModuleError>() {
        Some(ModuleError::InvalidAddress(_)) => return to_value_error(e),
        None => (),
    };

    match e.downcast_ref::<UtilError>() {
        Some(UtilError::FileAccess) => return to_value_error(e),
        Some(UtilError::FileFormat) => return to_value_error(e),
        None => (),
    };

    #[allow(clippy::single_match)]
    match e.downcast_ref::<PageMapError>() {
        Some(PageMapError::NotMapped) => return to_value_error(e),
        None => (),
    };

    to_value_error(e)
}

#[wasm_bindgen]
pub fn from_bytes(buf: Vec<u8>) -> Result<PE, JsError> {
    let pe = lPE::from_bytes(&buf).map_err(to_js_err)?;
    let dec = dis::get_disassembler(&pe.module).map_err(to_js_err)?;
    let formatter = zydis::Formatter::new(zydis::FormatterStyle::INTEL).expect("valid formatter options");
    Ok(PE {
        inner: pe,
        decoder: dec,
        formatter,
    })
}

#[wasm_bindgen]
#[derive(Clone, Copy)]
pub struct AddressRange {
    pub start: u64,
    pub end:   u64,
}

#[wasm_bindgen]
impl AddressRange {
    #[wasm_bindgen(getter)]
    pub fn size(&self) -> u64 {
        self.end - self.start
    }
}

#[wasm_bindgen]
pub struct Section {
    pub physical_range: AddressRange,
    pub virtual_range:  AddressRange,
    // String are not clone, so we have to manually implement getters
    permissions:        String,
    name:               String,
}

#[wasm_bindgen]
impl Section {
    #[wasm_bindgen(getter)]
    pub fn permissions(&self) -> String {
        self.permissions.to_string()
    }

    #[wasm_bindgen(getter)]
    pub fn name(&self) -> String {
        self.name.clone()
    }
}

impl From<&lancelot::module::Section> for Section {
    fn from(other: &lancelot::module::Section) -> Section {
        Section {
            physical_range: AddressRange {
                start: other.physical_range.start,
                end:   other.physical_range.end,
            },
            virtual_range:  AddressRange {
                start: other.virtual_range.start,
                end:   other.virtual_range.end,
            },
            permissions:    other.permissions.to_string(),
            name:           other.name.to_string(),
        }
    }
}

#[wasm_bindgen]
pub struct Instruction {
    pub address: u64,
    pub size:    u8,
    bytes:       Vec<u8>,
    string:      String,
}

#[wasm_bindgen]
impl Instruction {
    #[wasm_bindgen(getter)]
    pub fn bytes(&self) -> Vec<u8> {
        self.bytes.clone()
    }

    #[wasm_bindgen(getter)]
    pub fn string(&self) -> String {
        self.string.to_string()
    }
}

#[allow(clippy::upper_case_acronyms)]
enum StringEncoding {
    ASCII,
    UTF16,
}

#[wasm_bindgen(js_name = "String")]
pub struct EncodedString {
    encoding:    StringEncoding,
    pub address: u64,
    pub size:    usize,
    string:      String,
}

#[wasm_bindgen(js_class = "String")]
impl EncodedString {
    #[wasm_bindgen(getter, typescript_type = "string")]
    pub fn encoding(&self) -> JsValue {
        match self.encoding {
            // we expect these strings may be returned many times,
            // so we intern them for perf
            // https://docs.rs/wasm-bindgen/0.2.78/wasm_bindgen/fn.intern.html
            StringEncoding::ASCII => JsValue::from(wasm_bindgen::intern("ASCII")),
            StringEncoding::UTF16 => JsValue::from(wasm_bindgen::intern("UTF-16")),
        }
    }

    #[wasm_bindgen(getter)]
    pub fn string(&self) -> String {
        self.string.to_string()
    }
}

#[wasm_bindgen]
#[derive(Clone)]
pub struct Flow {
    inner: lancelot::analysis::cfg::flow::Flow,
    /* type: String,
     * target: u64, */
}

#[wasm_bindgen]
impl Flow {
    #[wasm_bindgen(getter, js_name = "type", typescript_type = "string")]
    pub fn type_(&self) -> JsValue {
        match self.inner {
            // we expect these strings may be returned many times,
            // so we intern them for perf
            // https://docs.rs/wasm-bindgen/0.2.78/wasm_bindgen/fn.intern.html
            lancelot::analysis::cfg::flow::Flow::Call(_) => JsValue::from(wasm_bindgen::intern("call")),
            lancelot::analysis::cfg::flow::Flow::Fallthrough(_) => JsValue::from(wasm_bindgen::intern("fallthrough")),
            lancelot::analysis::cfg::flow::Flow::UnconditionalJump(_) => {
                JsValue::from(wasm_bindgen::intern("unconditional jump"))
            }
            lancelot::analysis::cfg::flow::Flow::ConditionalJump(_) => {
                JsValue::from(wasm_bindgen::intern("conditional jump"))
            }
            lancelot::analysis::cfg::flow::Flow::ConditionalMove(_) => {
                JsValue::from(wasm_bindgen::intern("conditional move"))
            }
        }
    }

    #[wasm_bindgen(getter)]
    pub fn target(&self) -> u64 {
        self.inner.va()
    }
}

impl From<lancelot::analysis::cfg::flow::Flow> for Flow {
    fn from(other: lancelot::analysis::cfg::flow::Flow) -> Flow {
        Flow { inner: other }
    }
}

#[wasm_bindgen]
#[derive(Clone)]
pub struct BasicBlock {
    pub address: u64,
    pub size:    u64,

    successors: Vec<lancelot::analysis::cfg::flow::Flow>,

    instructions: Vec<u64>,
}

#[wasm_bindgen]
impl BasicBlock {
    // Vec<Flow> cannot be serialized by wasm-bindgen
    // so we have to manually convert to Vec<JsValue>
    // and then annotate the TS type.
    //
    // NB: JS now owns the objects, must explicitly drop.
    #[wasm_bindgen(getter, typescript_type = "Array<Flow>")]
    pub fn successors(&self) -> Vec<JsValue> {
        self.successors
            .iter()
            .cloned()
            .map(Flow::from)
            .map(JsValue::from)
            .collect()
    }

    // Vec<u64> is not Copy so cannot be serialized by wasm-bindgen.
    #[wasm_bindgen(getter)]
    pub fn instructions(&self) -> Vec<u64> {
        self.instructions.clone()
    }
}

#[wasm_bindgen]
#[derive(Clone)]
pub struct Function {
    pub address:  u64,
    basic_blocks: Vec<BasicBlock>,
}

#[wasm_bindgen]
impl Function {
    // Vec<BasicBlock> cannot be serialized by wasm-bindgen
    // so we have to manually convert to Vec<JsValue>
    // and then annotate the TS type.
    //
    // NB: JS now owns the objects, must explicitly drop.
    #[wasm_bindgen(getter, typescript_type = "Array<BasicBlock>")]
    pub fn basic_blocks(&self) -> Vec<JsValue> {
        self.basic_blocks.iter().cloned().map(JsValue::from).collect()
    }
}

#[wasm_bindgen]
pub struct Layout {
    functions: Vec<Function>,
}

#[wasm_bindgen]
impl Layout {
    #[wasm_bindgen(getter, typescript_type = "Map<bigint, Function>")]
    pub fn functions(&self) -> JsValue {
        let ret = js_sys::Map::new();

        for f in self.functions.iter().cloned() {
            ret.set(&JsValue::from(f.address), &JsValue::from(f));
        }

        ret.into()
    }
}

#[wasm_bindgen]
pub struct PE {
    inner:     lPE,
    decoder:   zydis::Decoder,
    formatter: zydis::Formatter,
}

#[wasm_bindgen]
impl PE {
    #[wasm_bindgen(getter, typescript_type = "string")]
    pub fn arch(&self) -> JsValue {
        match self.inner.module.arch {
            // we expect these strings may be returned many times,
            // so we intern them for perf
            // https://docs.rs/wasm-bindgen/0.2.78/wasm_bindgen/fn.intern.html
            Arch::X32 => JsValue::from(wasm_bindgen::intern("x32")),
            Arch::X64 => JsValue::from(wasm_bindgen::intern("x64")),
        }
    }

    #[wasm_bindgen(getter)]
    pub fn base_address(&self) -> u64 {
        self.inner.module.address_space.base_address
    }

    // not cheap, so its a function, not getter
    #[wasm_bindgen]
    pub fn functions(&self) -> Result<Vec<u64>, JsError> {
        Ok(lancelot::analysis::pe::find_functions(&self.inner)
            .map_err(to_js_err)?
            .into_iter()
            .filter(|f| matches!(f, lancelot::analysis::pe::Function::Local(_)))
            .map(|f| match f {
                lancelot::analysis::pe::Function::Local(va) => va,
                _ => unreachable!(),
            })
            .collect())
    }

    // Vec<T> cannot be serialized by wasm-bindgen
    // so we have to manually convert to Vec<JsValue>
    // and then annotate the TS type.
    //
    // NB: JS now owns the objects, must explicitly drop.
    #[wasm_bindgen(getter, typescript_type = "Array<Section>")]
    pub fn sections(&self) -> Vec<JsValue> {
        self.inner
            .module
            .sections
            .iter()
            .map(Section::from)
            .map(JsValue::from)
            .collect()
    }

    #[wasm_bindgen]
    pub fn read_bytes(&self, va: u64, size: usize) -> Result<Vec<u8>, JsError> {
        self.inner.module.address_space.read_bytes(va, size).map_err(to_js_err)
    }

    #[wasm_bindgen]
    pub fn read_insn(&self, va: u64) -> Result<Instruction, JsError> {
        let mut insn_buf = [0u8; 16];
        self.inner
            .module
            .address_space
            .read_into(va, &mut insn_buf)
            .map_err(to_js_err)?;

        if let Ok(Some(insn)) = self.decoder.decode(&insn_buf) {
            let mut out_buf = [0u8; 200];
            let mut out_buf = zydis::OutputBuffer::new(&mut out_buf[..]);
            self.formatter
                .format_instruction(&insn, &mut out_buf, Some(va), None)
                .map_err(|e| -> JsError {
                    js_sys::Error::new(&format!("failed to format instruction: {}", e)).into()
                })?;

            Ok(Instruction {
                address: va,
                size:    insn.length,
                bytes:   insn_buf[..insn.length as usize].to_vec(),
                string:  out_buf
                    .as_str()
                    .expect("disassembly should always be utf-8")
                    .to_string(),
            })
        } else {
            Err(js_sys::Error::new("invalid instruction").into())
        }
    }

    #[wasm_bindgen(typescript_type = "Array<EncodedString>")]
    pub fn strings(&self) -> Vec<JsValue> {
        let mut ret: Vec<EncodedString> = Default::default();

        for (range, s) in lancelot::util::find_ascii_strings(&self.inner.buf) {
            if let Ok(va) = self.inner.module.virtual_address(range.start as u64) {
                ret.push(EncodedString {
                    encoding: StringEncoding::ASCII,
                    address:  va,
                    size:     range.end - range.start,
                    string:   s,
                });
            }
        }

        for (range, s) in lancelot::util::find_unicode_strings(&self.inner.buf) {
            if let Ok(va) = self.inner.module.virtual_address(range.start as u64) {
                ret.push(EncodedString {
                    encoding: StringEncoding::UTF16,
                    address:  va,
                    size:     range.end - range.start,
                    string:   s,
                });
            }
        }

        ret.sort_by_key(|string| string.address);

        ret.into_iter().map(JsValue::from).collect()
    }

    #[wasm_bindgen]
    pub fn layout(&self) -> Result<Layout, JsValue> {
        use lancelot::{
            analysis::cfg::{InstructionIndex, CFG},
            aspace::AddressSpace,
        };

        let mut insns: InstructionIndex = Default::default();
        let functions = lancelot::analysis::pe::find_function_starts(&self.inner).map_err(to_js_err)?;
        for function in functions.iter() {
            insns.build_index(&self.inner.module, *function).map_err(to_js_err)?;
        }
        let cfg = CFG::from_instructions(insns).map_err(to_js_err)?;

        let functions = functions
            .into_iter()
            .map(|fva| {
                let mut basic_blocks = Vec::new();

                for bb in cfg.get_reachable_blocks(fva) {
                    // estimate each instruction is 2 bytes, which is probably too conservative,
                    // but enough to hint the allocation here.
                    //
                    // supported by here: https://ieeexplore.ieee.org/document/5645851
                    // > The results show that the average instruction size length is about 2 bytes.
                    let mut instructions = Vec::with_capacity((bb.length / 2) as usize);

                    let buf = self
                        .inner
                        .module
                        .address_space
                        .read_bytes(bb.address, bb.length as usize)
                        // programming error: this BB range must exist.
                        .expect("failed to read bb range");
                    for (offset, insn) in lancelot::analysis::dis::linear_disassemble(&self.decoder, &buf) {
                        if let Ok(Some(_)) = insn {
                            let insnva = bb.address + offset as u64;
                            instructions.push(insnva);
                        }
                    }

                    basic_blocks.push(BasicBlock {
                        address: bb.address,
                        size: bb.length,
                        successors: cfg.flows.flows_by_src[&bb.address_of_last_insn].to_vec(),
                        instructions,
                    })
                }

                Function {
                    address: fva,
                    basic_blocks,
                }
            })
            .collect();

        Ok(Layout { functions })
    }
}
<commit_msg>jslancelot: expose CFG, remove layout<commit_after>use wasm_bindgen::prelude::*;

use anyhow::Error;
use lancelot::{
    analysis::{
        cfg,
        dis::{self, zydis},
    },
    arch::Arch,
    aspace::AddressSpace,
    loader::pe::{PEError, PE as lPE},
    module::ModuleError,
    pagemap::PageMapError,
    util::UtilError,
};

#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

#[wasm_bindgen]
pub fn version() -> String {
    String::from("v0.7.0")
}

#[wasm_bindgen(start)]
pub fn main_js() -> Result<(), JsValue> {
    #[cfg(debug_assertions)]
    console_error_panic_hook::set_once();

    Ok(())
}

// for better function signatures
// no functional difference.
type JsError = JsValue;

fn to_value_error(e: anyhow::Error) -> JsError {
    js_sys::Error::new(&format!("{}", e)).into()
}

fn to_js_err(e: Error) -> JsError {
    match e.downcast_ref::<PEError>() {
        Some(PEError::FormatNotSupported(_)) => return to_value_error(e),
        Some(PEError::MalformedPEFile(_)) => return to_value_error(e),
        None => (),
    };

    #[allow(clippy::single_match)]
    match e.downcast_ref::<ModuleError>() {
        Some(ModuleError::InvalidAddress(_)) => return to_value_error(e),
        None => (),
    };

    match e.downcast_ref::<UtilError>() {
        Some(UtilError::FileAccess) => return to_value_error(e),
        Some(UtilError::FileFormat) => return to_value_error(e),
        None => (),
    };

    #[allow(clippy::single_match)]
    match e.downcast_ref::<PageMapError>() {
        Some(PageMapError::NotMapped) => return to_value_error(e),
        None => (),
    };

    to_value_error(e)
}

#[wasm_bindgen]
pub fn from_bytes(buf: Vec<u8>) -> Result<PE, JsError> {
    let pe = lPE::from_bytes(&buf).map_err(to_js_err)?;
    let dec = dis::get_disassembler(&pe.module).map_err(to_js_err)?;
    let formatter = zydis::Formatter::new(zydis::FormatterStyle::INTEL).expect("valid formatter options");
    Ok(PE {
        inner: pe,
        decoder: dec,
        formatter,
    })
}

#[wasm_bindgen]
#[derive(Clone, Copy)]
pub struct AddressRange {
    pub start: u64,
    pub end:   u64,
}

#[wasm_bindgen]
impl AddressRange {
    #[wasm_bindgen(getter)]
    pub fn size(&self) -> u64 {
        self.end - self.start
    }
}

#[wasm_bindgen]
pub struct Section {
    pub physical_range: AddressRange,
    pub virtual_range:  AddressRange,
    // String are not clone, so we have to manually implement getters
    permissions:        String,
    name:               String,
}

#[wasm_bindgen]
impl Section {
    #[wasm_bindgen(getter)]
    pub fn permissions(&self) -> String {
        self.permissions.to_string()
    }

    #[wasm_bindgen(getter)]
    pub fn name(&self) -> String {
        self.name.clone()
    }
}

impl From<&lancelot::module::Section> for Section {
    fn from(other: &lancelot::module::Section) -> Section {
        Section {
            physical_range: AddressRange {
                start: other.physical_range.start,
                end:   other.physical_range.end,
            },
            virtual_range:  AddressRange {
                start: other.virtual_range.start,
                end:   other.virtual_range.end,
            },
            permissions:    other.permissions.to_string(),
            name:           other.name.to_string(),
        }
    }
}

#[wasm_bindgen]
pub struct Instruction {
    pub address: u64,
    pub size:    u8,
    bytes:       Vec<u8>,
    string:      String,
}

#[wasm_bindgen]
impl Instruction {
    #[wasm_bindgen(getter)]
    pub fn bytes(&self) -> Vec<u8> {
        self.bytes.clone()
    }

    #[wasm_bindgen(getter)]
    pub fn string(&self) -> String {
        self.string.to_string()
    }
}

#[allow(clippy::upper_case_acronyms)]
enum StringEncoding {
    ASCII,
    UTF16,
}

#[wasm_bindgen(js_name = "String")]
pub struct EncodedString {
    encoding:    StringEncoding,
    pub address: u64,
    pub size:    usize,
    string:      String,
}

#[wasm_bindgen(js_class = "String")]
impl EncodedString {
    #[wasm_bindgen(getter, typescript_type = "string")]
    pub fn encoding(&self) -> JsValue {
        match self.encoding {
            // we expect these strings may be returned many times,
            // so we intern them for perf
            // https://docs.rs/wasm-bindgen/0.2.78/wasm_bindgen/fn.intern.html
            StringEncoding::ASCII => JsValue::from(wasm_bindgen::intern("ASCII")),
            StringEncoding::UTF16 => JsValue::from(wasm_bindgen::intern("UTF-16")),
        }
    }

    #[wasm_bindgen(getter)]
    pub fn string(&self) -> String {
        self.string.to_string()
    }
}

#[wasm_bindgen]
#[derive(Clone)]
pub struct Flow {
    inner: lancelot::analysis::cfg::flow::Flow,
    /* type: String,
     * target: u64, */
}

#[wasm_bindgen]
impl Flow {
    #[wasm_bindgen(getter, js_name = "type", typescript_type = "string")]
    pub fn type_(&self) -> JsValue {
        match self.inner {
            // we expect these strings may be returned many times,
            // so we intern them for perf
            // https://docs.rs/wasm-bindgen/0.2.78/wasm_bindgen/fn.intern.html
            lancelot::analysis::cfg::flow::Flow::Call(_) => JsValue::from(wasm_bindgen::intern("call")),
            lancelot::analysis::cfg::flow::Flow::Fallthrough(_) => JsValue::from(wasm_bindgen::intern("fallthrough")),
            lancelot::analysis::cfg::flow::Flow::UnconditionalJump(_) => {
                JsValue::from(wasm_bindgen::intern("unconditional jump"))
            }
            lancelot::analysis::cfg::flow::Flow::ConditionalJump(_) => {
                JsValue::from(wasm_bindgen::intern("conditional jump"))
            }
            lancelot::analysis::cfg::flow::Flow::ConditionalMove(_) => {
                JsValue::from(wasm_bindgen::intern("conditional move"))
            }
        }
    }

    #[wasm_bindgen(getter)]
    pub fn target(&self) -> u64 {
        self.inner.va()
    }
}

impl From<lancelot::analysis::cfg::flow::Flow> for Flow {
    fn from(other: lancelot::analysis::cfg::flow::Flow) -> Flow {
        Flow { inner: other }
    }
}

#[wasm_bindgen]
#[derive(Clone)]
pub struct BasicBlock {
    pub address: u64,
    pub size:    u64,

    successors: Vec<lancelot::analysis::cfg::flow::Flow>,

    instructions: Vec<u64>,
}

#[wasm_bindgen]
impl BasicBlock {
    // Vec<Flow> cannot be serialized by wasm-bindgen
    // so we have to manually convert to Vec<JsValue>
    // and then annotate the TS type.
    //
    // NB: JS now owns the objects, must explicitly drop.
    #[wasm_bindgen(getter, typescript_type = "Array<Flow>")]
    pub fn successors(&self) -> Vec<JsValue> {
        self.successors
            .iter()
            .cloned()
            .map(Flow::from)
            .map(JsValue::from)
            .collect()
    }

    // Vec<u64> is not Copy so cannot be serialized by wasm-bindgen.
    #[wasm_bindgen(getter)]
    pub fn instructions(&self) -> Vec<u64> {
        self.instructions.clone()
    }
}

#[wasm_bindgen]
pub struct CFG {
    basic_blocks: JsValue,
    inner:        cfg::CFG,
}

#[wasm_bindgen]
impl CFG {
    #[wasm_bindgen(getter, typescript_type = "Map<BigInt, BasicBlock>")]
    pub fn basic_blocks(&self) -> JsValue {
        // ugh double copy
        self.basic_blocks.clone()
    }

    pub fn get_reachable_blocks(&self, va: u64) -> Vec<u64> {
        self.inner.get_reachable_blocks(va).map(|bb| bb.address).collect()
    }
}

#[wasm_bindgen]
pub struct PE {
    inner:     lPE,
    decoder:   zydis::Decoder,
    formatter: zydis::Formatter,
}

#[wasm_bindgen]
impl PE {
    #[wasm_bindgen(getter, typescript_type = "string")]
    pub fn arch(&self) -> JsValue {
        match self.inner.module.arch {
            // we expect these strings may be returned many times,
            // so we intern them for perf
            // https://docs.rs/wasm-bindgen/0.2.78/wasm_bindgen/fn.intern.html
            Arch::X32 => JsValue::from(wasm_bindgen::intern("x32")),
            Arch::X64 => JsValue::from(wasm_bindgen::intern("x64")),
        }
    }

    #[wasm_bindgen(getter)]
    pub fn base_address(&self) -> u64 {
        self.inner.module.address_space.base_address
    }

    // not cheap, so its a function, not getter
    #[wasm_bindgen]
    pub fn functions(&self) -> Result<Vec<u64>, JsError> {
        Ok(lancelot::analysis::pe::find_functions(&self.inner)
            .map_err(to_js_err)?
            .into_iter()
            .filter(|f| matches!(f, lancelot::analysis::pe::Function::Local(_)))
            .map(|f| match f {
                lancelot::analysis::pe::Function::Local(va) => va,
                _ => unreachable!(),
            })
            .collect())
    }

    // Vec<T> cannot be serialized by wasm-bindgen
    // so we have to manually convert to Vec<JsValue>
    // and then annotate the TS type.
    //
    // NB: JS now owns the objects, must explicitly drop.
    #[wasm_bindgen(getter, typescript_type = "Array<Section>")]
    pub fn sections(&self) -> Vec<JsValue> {
        self.inner
            .module
            .sections
            .iter()
            .map(Section::from)
            .map(JsValue::from)
            .collect()
    }

    #[wasm_bindgen]
    pub fn read_bytes(&self, va: u64, size: usize) -> Result<Vec<u8>, JsError> {
        self.inner.module.address_space.read_bytes(va, size).map_err(to_js_err)
    }

    #[wasm_bindgen]
    pub fn read_insn(&self, va: u64) -> Result<Instruction, JsError> {
        let mut insn_buf = [0u8; 16];
        self.inner
            .module
            .address_space
            .read_into(va, &mut insn_buf)
            .map_err(to_js_err)?;

        if let Ok(Some(insn)) = self.decoder.decode(&insn_buf) {
            let mut out_buf = [0u8; 200];
            let mut out_buf = zydis::OutputBuffer::new(&mut out_buf[..]);
            self.formatter
                .format_instruction(&insn, &mut out_buf, Some(va), None)
                .map_err(|e| -> JsError {
                    js_sys::Error::new(&format!("failed to format instruction: {}", e)).into()
                })?;

            Ok(Instruction {
                address: va,
                size:    insn.length,
                bytes:   insn_buf[..insn.length as usize].to_vec(),
                string:  out_buf
                    .as_str()
                    .expect("disassembly should always be utf-8")
                    .to_string(),
            })
        } else {
            Err(js_sys::Error::new("invalid instruction").into())
        }
    }

    #[wasm_bindgen(typescript_type = "Array<EncodedString>")]
    pub fn strings(&self) -> Vec<JsValue> {
        let mut ret: Vec<EncodedString> = Default::default();

        for (range, s) in lancelot::util::find_ascii_strings(&self.inner.buf) {
            if let Ok(va) = self.inner.module.virtual_address(range.start as u64) {
                ret.push(EncodedString {
                    encoding: StringEncoding::ASCII,
                    address:  va,
                    size:     range.end - range.start,
                    string:   s,
                });
            }
        }

        for (range, s) in lancelot::util::find_unicode_strings(&self.inner.buf) {
            if let Ok(va) = self.inner.module.virtual_address(range.start as u64) {
                ret.push(EncodedString {
                    encoding: StringEncoding::UTF16,
                    address:  va,
                    size:     range.end - range.start,
                    string:   s,
                });
            }
        }

        ret.sort_by_key(|string| string.address);

        ret.into_iter().map(JsValue::from).collect()
    }

    #[wasm_bindgen]
    pub fn cfg(&self) -> Result<CFG, JsValue> {
        use lancelot::aspace::AddressSpace;
        let ret = js_sys::Map::new();

        let mut insns: cfg::InstructionIndex = Default::default();
        let functions = lancelot::analysis::pe::find_function_starts(&self.inner).map_err(to_js_err)?;
        for function in functions.iter() {
            insns.build_index(&self.inner.module, *function).map_err(to_js_err)?;
        }
        let cfg = cfg::CFG::from_instructions(insns).map_err(to_js_err)?;

        for bb in cfg.basic_blocks.blocks_by_address.values() {
            // estimate each instruction is 2 bytes, which is probably too conservative,
            // but enough to hint the allocation here.
            //
            // supported by here: https://ieeexplore.ieee.org/document/5645851
            // > The results show that the average instruction size length is about 2 bytes.
            let mut instructions = Vec::with_capacity((bb.length / 2) as usize);

            let buf = self
                .inner
                .module
                .address_space
                .read_bytes(bb.address, bb.length as usize)
                // programming error: this BB range must exist.
                .expect("failed to read bb range");
            for (offset, insn) in lancelot::analysis::dis::linear_disassemble(&self.decoder, &buf) {
                if let Ok(Some(_)) = insn {
                    let insnva = bb.address + offset as u64;
                    instructions.push(insnva);
                }
            }

            ret.set(
                &JsValue::from(bb.address),
                &JsValue::from(BasicBlock {
                    address: bb.address,
                    size: bb.length,
                    successors: cfg.flows.flows_by_src[&bb.address_of_last_insn].to_vec(),
                    instructions,
                }),
            );
        }

        Ok(CFG {
            inner:        cfg,
            basic_blocks: ret.into(),
        })
    }
}
<|endoftext|>


<commit_before>// The MIT License (MIT)

// Copyright (c) 2014 Y. T. CHUNG <zonyitoo@gmail.com>

// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

//! Relay server in local and server side implementations.

use std::io::{self, Read, Write};
use std::net::SocketAddr;

pub use self::local::RelayLocal;
pub use self::server::RelayServer;

use ip::IpAddr;

mod tcprelay;
#[cfg(feature = "enable-udp")]
mod udprelay;
pub mod local;
pub mod server;
mod loadbalancing;
pub mod socks5;

pub trait Relay {
    fn run(&self);
}

fn copy<R: Read, W: Write>(r: &mut R, w: &mut W, prefix: &str) -> io::Result<u64> {
    let mut buf = [0u8; 4096];
    let mut written = 0;
    loop {
        let len = match r.read(&mut buf) {
            Ok(0) => {
                trace!("{}: EOF from reader", prefix);
                return Ok(written);
            }
            Ok(len) => len,
            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
            Err(e) => {
                trace!("{}: Error from reader {:?}", prefix, e);
                return Err(e);
            }
        };
        trace!("{}: Read {} bytes from reader", prefix, len);
        match w.write_all(&buf[..len]) {
            Ok(..) => {},
            Err(err) => {
                trace!("{}: Error from writer {:?}", prefix, err);
                return Err(err);
            }
        }
        trace!("{}: Write {} bytes to writer", prefix, len);
        written += len as u64;
    }
}

fn take_ip_addr(sockaddr: &SocketAddr) -> IpAddr {
    match sockaddr {
        &SocketAddr::V4(ref v4) => IpAddr::V4(*v4.ip()),
        &SocketAddr::V6(ref v6) => IpAddr::V6(*v6.ip()),
    }
}
<commit_msg>flush immediately after write<commit_after>// The MIT License (MIT)

// Copyright (c) 2014 Y. T. CHUNG <zonyitoo@gmail.com>

// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

//! Relay server in local and server side implementations.

use std::io::{self, Read, Write};
use std::net::SocketAddr;

pub use self::local::RelayLocal;
pub use self::server::RelayServer;

use ip::IpAddr;

mod tcprelay;
#[cfg(feature = "enable-udp")]
mod udprelay;
pub mod local;
pub mod server;
mod loadbalancing;
pub mod socks5;

pub trait Relay {
    fn run(&self);
}

fn copy<R: Read, W: Write>(r: &mut R, w: &mut W, prefix: &str) -> io::Result<u64> {
    let mut buf = [0u8; 4096];
    let mut written = 0;
    loop {
        let len = match r.read(&mut buf) {
            Ok(0) => {
                trace!("{}: EOF from reader", prefix);
                return Ok(written);
            }
            Ok(len) => len,
            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
            Err(e) => {
                trace!("{}: Error from reader {:?}", prefix, e);
                return Err(e);
            }
        };
        trace!("{}: Read {} bytes from reader", prefix, len);
        match w.write_all(&buf[..len]).and_then(|_| w.flush()) {
            Ok(..) => {},
            Err(err) => {
                trace!("{}: Error from writer {:?}", prefix, err);
                return Err(err);
            }
        }
        trace!("{}: Write {} bytes to writer", prefix, len);
        written += len as u64;
    }
}

fn take_ip_addr(sockaddr: &SocketAddr) -> IpAddr {
    match sockaddr {
        &SocketAddr::V4(ref v4) => IpAddr::V4(*v4.ip()),
        &SocketAddr::V6(ref v6) => IpAddr::V6(*v6.ip()),
    }
}
<|endoftext|>


<commit_before>use futures::channel::{oneshot, mpsc};
use futures::executor::{block_on, block_on_stream};
use futures::sink::SinkExt;
use futures::stream::StreamExt;
use std::sync::mpsc as std_mpsc;
use std::thread;

#[test]
#[ignore] // FIXME: https://github.com/rust-lang/futures-rs/issues/1790
fn works() {
    const N: usize = 4;

    let (mut tx, rx) = mpsc::channel(1);

    let (tx2, rx2) = std_mpsc::channel();
    let (tx3, rx3) = std_mpsc::channel();
    let t1 = thread::spawn(move || {
        for _ in 0..=N {
            let (mytx, myrx) = oneshot::channel();
            block_on(tx.send(myrx)).unwrap();
            tx3.send(mytx).unwrap();
        }
        rx2.recv().unwrap();
        for _ in 0..N {
            let (mytx, myrx) = oneshot::channel();
            block_on(tx.send(myrx)).unwrap();
            tx3.send(mytx).unwrap();
        }
    });

    let (tx4, rx4) = std_mpsc::channel();
    let t2 = thread::spawn(move || {
        for item in block_on_stream(rx.buffer_unordered(N)) {
            tx4.send(item.unwrap()).unwrap();
        }
    });

    let o1 = rx3.recv().unwrap();
    let o2 = rx3.recv().unwrap();
    let o3 = rx3.recv().unwrap();
    let o4 = rx3.recv().unwrap();
    assert!(rx4.try_recv().is_err());

    o1.send(1).unwrap();
    assert_eq!(rx4.recv(), Ok(1));
    o3.send(3).unwrap();
    assert_eq!(rx4.recv(), Ok(3));
    tx2.send(()).unwrap();
    o2.send(2).unwrap();
    assert_eq!(rx4.recv(), Ok(2));
    o4.send(4).unwrap();
    assert_eq!(rx4.recv(), Ok(4));

    let o5 = rx3.recv().unwrap();
    let o6 = rx3.recv().unwrap();
    let o7 = rx3.recv().unwrap();
    let o8 = rx3.recv().unwrap();
    let o9 = rx3.recv().unwrap();

    o5.send(5).unwrap();
    assert_eq!(rx4.recv(), Ok(5));
    o8.send(8).unwrap();
    assert_eq!(rx4.recv(), Ok(8));
    o9.send(9).unwrap();
    assert_eq!(rx4.recv(), Ok(9));
    o7.send(7).unwrap();
    assert_eq!(rx4.recv(), Ok(7));
    o6.send(6).unwrap();
    assert_eq!(rx4.recv(), Ok(6));

    t1.join().unwrap();
    t2.join().unwrap();
}
<commit_msg>futures: tests/buffer_unordered.rs: Guard against compile fails w/o features<commit_after>#[cfg(all(feature = "alloc", feature = "std", feature = "executor"))]
#[test]
#[ignore] // FIXME: https://github.com/rust-lang/futures-rs/issues/1790
fn works() {
    use futures::channel::{oneshot, mpsc};
    use futures::executor::{block_on, block_on_stream};
    use futures::sink::SinkExt;
    use futures::stream::StreamExt;
    use std::sync::mpsc as std_mpsc;
    use std::thread;

    const N: usize = 4;

    let (mut tx, rx) = mpsc::channel(1);

    let (tx2, rx2) = std_mpsc::channel();
    let (tx3, rx3) = std_mpsc::channel();
    let t1 = thread::spawn(move || {
        for _ in 0..=N {
            let (mytx, myrx) = oneshot::channel();
            block_on(tx.send(myrx)).unwrap();
            tx3.send(mytx).unwrap();
        }
        rx2.recv().unwrap();
        for _ in 0..N {
            let (mytx, myrx) = oneshot::channel();
            block_on(tx.send(myrx)).unwrap();
            tx3.send(mytx).unwrap();
        }
    });

    let (tx4, rx4) = std_mpsc::channel();
    let t2 = thread::spawn(move || {
        for item in block_on_stream(rx.buffer_unordered(N)) {
            tx4.send(item.unwrap()).unwrap();
        }
    });

    let o1 = rx3.recv().unwrap();
    let o2 = rx3.recv().unwrap();
    let o3 = rx3.recv().unwrap();
    let o4 = rx3.recv().unwrap();
    assert!(rx4.try_recv().is_err());

    o1.send(1).unwrap();
    assert_eq!(rx4.recv(), Ok(1));
    o3.send(3).unwrap();
    assert_eq!(rx4.recv(), Ok(3));
    tx2.send(()).unwrap();
    o2.send(2).unwrap();
    assert_eq!(rx4.recv(), Ok(2));
    o4.send(4).unwrap();
    assert_eq!(rx4.recv(), Ok(4));

    let o5 = rx3.recv().unwrap();
    let o6 = rx3.recv().unwrap();
    let o7 = rx3.recv().unwrap();
    let o8 = rx3.recv().unwrap();
    let o9 = rx3.recv().unwrap();

    o5.send(5).unwrap();
    assert_eq!(rx4.recv(), Ok(5));
    o8.send(8).unwrap();
    assert_eq!(rx4.recv(), Ok(8));
    o9.send(9).unwrap();
    assert_eq!(rx4.recv(), Ok(9));
    o7.send(7).unwrap();
    assert_eq!(rx4.recv(), Ok(7));
    o6.send(6).unwrap();
    assert_eq!(rx4.recv(), Ok(6));

    t1.join().unwrap();
    t2.join().unwrap();
}
<|endoftext|>


<commit_before>#[macro_use]
mod utility;

pub mod streaminfo;
pub mod comments;
pub mod seektable;
pub mod picture;
<commit_msg>Export `list_block_names` in commands module<commit_after>#[macro_use]
mod utility;

pub mod streaminfo;
pub mod comments;
pub mod seektable;
pub mod picture;

pub use self::utility::list_block_names;
<|endoftext|>


<commit_before>use path;

use std::borrow::Cow;
use std::path::PathBuf;

use syn;

pub struct TemplateInput<'a> {
    pub ast: &'a syn::DeriveInput,
    pub meta: TemplateMeta<'a>,
    pub path: PathBuf,
    pub source: Cow<'a, str>,
}

impl<'a> TemplateInput<'a> {
    pub fn new(ast: &'a syn::DeriveInput) -> TemplateInput<'a> {
        let meta = TemplateMeta::new(ast);
        let (path, source) = match meta.source {
            Source::Source(s) => {
                let path = match meta.ext {
                    Some(v) => PathBuf::from(format!("_.{}", v)),
                    None => PathBuf::new(),
                };
                (path, Cow::Borrowed(s))
            },
            Source::Path(s) => {
                let path = path::find_template_from_path(s, None);
                let src = path::get_template_source(&path);
                (path, Cow::Owned(src))
            },
        };
        TemplateInput { ast, meta, path, source }
    }
}

// Holds metadata for the template, based on the `template()` attribute.
pub struct TemplateMeta<'a> {
    source: Source<'a>,
    pub print: Print,
    pub escaping: EscapeMode,
    pub ext: Option<&'a str>,
}

impl<'a> TemplateMeta<'a> {
    fn new(ast: &'a syn::DeriveInput) -> TemplateMeta<'a> {
        let attr = ast.attrs.iter().find(|a| a.name() == "template");
        if attr.is_none() {
            let msg = format!("'template' attribute not found on struct '{}'",
                              ast.ident.as_ref());
            panic!(msg);
        }

        let attr = attr.unwrap();
        let mut source = None;
        let mut print = Print::None;
        let mut escaping = EscapeMode::Html;
        let mut ext = None;
        if let syn::MetaItem::List(_, ref inner) = attr.value {
            for nm_item in inner {
                if let syn::NestedMetaItem::MetaItem(ref item) = *nm_item {
                    if let syn::MetaItem::NameValue(ref key, ref val) = *item {
                        match key.as_ref() {
                            "path" => if let syn::Lit::Str(ref s, _) = *val {
                                if source.is_some() {
                                    panic!("must specify 'source' or 'path', not both");
                                }
                                source = Some(Source::Path(s.as_ref()));
                            } else {
                                panic!("template path must be string literal");
                            },
                            "source" => if let syn::Lit::Str(ref s, _) = *val {
                                if source.is_some() {
                                    panic!("must specify 'source' or 'path', not both");
                                }
                                source = Some(Source::Source(s.as_ref()));
                            } else {
                                panic!("template source must be string literal");
                            },
                            "print" => if let syn::Lit::Str(ref s, _) = *val {
                                print = (s.as_ref() as &str).into();
                            } else {
                                panic!("print value must be string literal");
                            },
                            "escape" => if let syn::Lit::Str(ref s, _) = *val {
                                escaping = (s.as_ref() as &str).into();
                            } else {
                                panic!("escape value must be string literal");
                            },
                            "ext" => if let syn::Lit::Str(ref s, _) = *val {
                                ext = Some((s.as_ref() as &str).into());
                            } else {
                                panic!("ext value must be string literal");
                            },
                            _ => { panic!("unsupported annotation key found") }
                        }
                    }
                }
            }
        }

        let source = source.expect("template path or source not found in attributes");
        match (&source, ext.is_some()) {
            (&Source::Path(_), true) => {
                panic!("'ext' attribute cannot be used with 'path' attribute")
            },
            (&Source::Source(_), false) => {
                panic!("must include 'ext' attribute when using 'source' attribute")
            },
            _ => {},
        }
        TemplateMeta { source, print, escaping, ext }
    }
}

enum Source<'a> {
    Path(&'a str),
    Source(&'a str),
}

#[derive(PartialEq)]
pub enum EscapeMode {
    Html,
    None,
}

impl<'a> From<&'a str> for EscapeMode {
    fn from(s: &'a str) -> EscapeMode {
        use self::EscapeMode::*;
        match s {
            "html" => Html,
            "none" => None,
            v => panic!("invalid value for escape option: {}", v),
        }
    }
}

#[derive(PartialEq)]
pub enum Print {
    All,
    Ast,
    Code,
    None,
}

impl<'a> From<&'a str> for Print {
    fn from(s: &'a str) -> Print {
        use self::Print::*;
        match s {
            "all" => All,
            "ast" => Ast,
            "code" => Code,
            "none" => None,
            v => panic!("invalid value for print option: {}", v),
        }
    }
}
<commit_msg>Infer escaping mode based on template extension<commit_after>use path;

use std::borrow::Cow;
use std::path::{Path, PathBuf};

use syn;

pub struct TemplateInput<'a> {
    pub ast: &'a syn::DeriveInput,
    pub meta: TemplateMeta<'a>,
    pub path: PathBuf,
    pub source: Cow<'a, str>,
}

impl<'a> TemplateInput<'a> {
    pub fn new(ast: &'a syn::DeriveInput) -> TemplateInput<'a> {
        let meta = TemplateMeta::new(ast);
        let (path, source) = match meta.source {
            Source::Source(s) => {
                let path = match meta.ext {
                    Some(v) => PathBuf::from(format!("_.{}", v)),
                    None => PathBuf::new(),
                };
                (path, Cow::Borrowed(s))
            },
            Source::Path(s) => {
                let path = path::find_template_from_path(s, None);
                let src = path::get_template_source(&path);
                (path, Cow::Owned(src))
            },
        };
        TemplateInput { ast, meta, path, source }
    }
}

// Holds metadata for the template, based on the `template()` attribute.
pub struct TemplateMeta<'a> {
    source: Source<'a>,
    pub print: Print,
    pub escaping: EscapeMode,
    pub ext: Option<&'a str>,
}

impl<'a> TemplateMeta<'a> {
    fn new(ast: &'a syn::DeriveInput) -> TemplateMeta<'a> {
        let attr = ast.attrs.iter().find(|a| a.name() == "template");
        if attr.is_none() {
            let msg = format!("'template' attribute not found on struct '{}'",
                              ast.ident.as_ref());
            panic!(msg);
        }

        let attr = attr.unwrap();
        let mut source = None;
        let mut print = Print::None;
        let mut escaping = None;
        let mut ext = None;
        if let syn::MetaItem::List(_, ref inner) = attr.value {
            for nm_item in inner {
                if let syn::NestedMetaItem::MetaItem(ref item) = *nm_item {
                    if let syn::MetaItem::NameValue(ref key, ref val) = *item {
                        match key.as_ref() {
                            "path" => if let syn::Lit::Str(ref s, _) = *val {
                                if source.is_some() {
                                    panic!("must specify 'source' or 'path', not both");
                                }
                                source = Some(Source::Path(s.as_ref()));
                            } else {
                                panic!("template path must be string literal");
                            },
                            "source" => if let syn::Lit::Str(ref s, _) = *val {
                                if source.is_some() {
                                    panic!("must specify 'source' or 'path', not both");
                                }
                                source = Some(Source::Source(s.as_ref()));
                            } else {
                                panic!("template source must be string literal");
                            },
                            "print" => if let syn::Lit::Str(ref s, _) = *val {
                                print = (s.as_ref() as &str).into();
                            } else {
                                panic!("print value must be string literal");
                            },
                            "escape" => if let syn::Lit::Str(ref s, _) = *val {
                                escaping = Some((s.as_ref() as &str).into());
                            } else {
                                panic!("escape value must be string literal");
                            },
                            "ext" => if let syn::Lit::Str(ref s, _) = *val {
                                ext = Some((s.as_ref() as &str).into());
                            } else {
                                panic!("ext value must be string literal");
                            },
                            _ => { panic!("unsupported annotation key found") }
                        }
                    }
                }
            }
        }

        let source = source.expect("template path or source not found in attributes");
        match (&source, ext.is_some()) {
            (&Source::Path(_), true) => {
                panic!("'ext' attribute cannot be used with 'path' attribute")
            },
            (&Source::Source(_), false) => {
                panic!("must include 'ext' attribute when using 'source' attribute")
            },
            _ => {},
        }
        let escaping = match escaping {
            Some(m) => m,
            None => {
                let ext = match source {
                    Source::Path(p) => {
                        Path::new(p).extension().map(|s| s.to_str().unwrap()).unwrap_or("")
                    },
                    Source::Source(_) => ext.unwrap(), // Already panicked if None
                };
                if HTML_EXTENSIONS.contains(&ext) {
                    EscapeMode::Html
                } else {
                    EscapeMode::None
                }
            }
        };
        TemplateMeta { source, print, escaping, ext }
    }
}

enum Source<'a> {
    Path(&'a str),
    Source(&'a str),
}

#[derive(PartialEq)]
pub enum EscapeMode {
    Html,
    None,
}

impl<'a> From<&'a str> for EscapeMode {
    fn from(s: &'a str) -> EscapeMode {
        use self::EscapeMode::*;
        match s {
            "html" => Html,
            "none" => None,
            v => panic!("invalid value for escape option: {}", v),
        }
    }
}

#[derive(PartialEq)]
pub enum Print {
    All,
    Ast,
    Code,
    None,
}

impl<'a> From<&'a str> for Print {
    fn from(s: &'a str) -> Print {
        use self::Print::*;
        match s {
            "all" => All,
            "ast" => Ast,
            "code" => Code,
            "none" => None,
            v => panic!("invalid value for print option: {}", v),
        }
    }
}

const HTML_EXTENSIONS: [&str; 3] = ["html", "htm", "xml"];
<|endoftext|>


<commit_before>/* TOOD: Implement for other kqueue based systems
 */

use {Errno, Result};
#[cfg(not(target_os = "netbsd"))]
use libc::{timespec, time_t, c_int, c_long, intptr_t, uintptr_t};
#[cfg(target_os = "netbsd")]
use libc::{timespec, time_t, c_long, intptr_t, uintptr_t, size_t};
use libc;
use std::os::unix::io::RawFd;
use std::ptr;
use std::mem;

// Redefine kevent in terms of programmer-friendly enums and bitfields.
#[derive(Clone, Copy)]
#[repr(C)]
pub struct KEvent {
    kevent: libc::kevent,
}

#[cfg(any(target_os = "dragonfly", target_os = "freebsd",
          target_os = "ios", target_os = "macos",
          target_os = "openbsd"))]
type type_of_udata = *mut libc::c_void;
#[cfg(any(target_os = "dragonfly", target_os = "freebsd",
          target_os = "ios", target_os = "macos"))]
type type_of_data = libc::intptr_t;
#[cfg(any(target_os = "netbsd"))]
type type_of_udata = intptr_t;
#[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
type type_of_data = libc::int64_t;

#[cfg(not(target_os = "netbsd"))]
type type_of_event_filter = i16;
#[cfg(not(target_os = "netbsd"))]
#[repr(i16)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum EventFilter {
    EVFILT_AIO = libc::EVFILT_AIO,
    #[cfg(target_os = "dragonfly")]
    EVFILT_EXCEPT = libc::EVFILT_EXCEPT,
    #[cfg(any(target_os = "macos", target_os = "ios",
              target_os = "dragonfly",
              target_os = "freebsd"))]
    EVFILT_FS = libc::EVFILT_FS,
    #[cfg(target_os = "freebsd")]
    EVFILT_LIO = libc::EVFILT_LIO,
    #[cfg(any(target_os = "macos", target_os = "ios"))]
    EVFILT_MACHPORT = libc::EVFILT_MACHPORT,
    EVFILT_PROC = libc::EVFILT_PROC,
    EVFILT_READ = libc::EVFILT_READ,
    EVFILT_SIGNAL = libc::EVFILT_SIGNAL,
    EVFILT_TIMER = libc::EVFILT_TIMER,
    #[cfg(any(target_os = "macos",
              target_os = "ios",
              target_os = "dragonfly",
              target_os = "freebsd"))]
    EVFILT_USER = libc::EVFILT_USER,
    #[cfg(any(target_os = "macos", target_os = "ios"))]
    EVFILT_VM = libc::EVFILT_VM,
    EVFILT_VNODE = libc::EVFILT_VNODE,
    EVFILT_WRITE = libc::EVFILT_WRITE,
}

#[cfg(target_os = "netbsd")]
type type_of_event_filter = libc::uint32_t;
#[cfg(target_os = "netbsd")]
#[repr(i32)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum EventFilter {
    EVFILT_READ = libc::EVFILT_READ,
    EVFILT_WRITE = libc::EVFILT_WRITE,
    EVFILT_AIO = libc::EVFILT_AIO,
    EVFILT_VNODE = libc::EVFILT_VNODE,
    EVFILT_PROC = libc::EVFILT_PROC,
    EVFILT_SIGNAL = libc::EVFILT_SIGNAL,
    EVFILT_TIMER = libc::EVFILT_TIMER,
}

#[cfg(any(target_os = "dragonfly", target_os = "freebsd",
          target_os = "ios", target_os = "macos",
          target_os = "openbsd"))]
pub type type_of_event_flag = u16;
#[cfg(any(target_os = "netbsd"))]
pub type type_of_event_flag = u32;
libc_bitflags!{
    pub struct EventFlag: type_of_event_flag {
        EV_ADD;
        EV_CLEAR;
        EV_DELETE;
        EV_DISABLE;
        // No released version of OpenBSD supports EV_DISPATCH or EV_RECEIPT.
        // These have been commited to the -current branch though and are
        // expected to be part of the OpenBSD 6.2 release in Nov 2017.
        // See: https://marc.info/?l=openbsd-tech&m=149621427511219&w=2
        // https://github.com/rust-lang/libc/pull/613
        #[cfg(any(target_os = "dragonfly", target_os = "freebsd",
                  target_os = "ios", target_os = "macos",
                  target_os = "netbsd"))]
        EV_DISPATCH;
        #[cfg(target_os = "freebsd")]
        EV_DROP;
        EV_ENABLE;
        EV_EOF;
        EV_ERROR;
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        EV_FLAG0;
        EV_FLAG1;
        #[cfg(target_os = "dragonfly")]
        EV_NODATA;
        EV_ONESHOT;
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        EV_OOBAND;
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        EV_POLL;
        #[cfg(any(target_os = "dragonfly", target_os = "freebsd",
                  target_os = "ios", target_os = "macos",
                  target_os = "netbsd"))]
        EV_RECEIPT;
        EV_SYSFLAGS;
    }
}

libc_bitflags!(
    pub struct FilterFlag: u32 {
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        NOTE_ABSOLUTE;
        NOTE_ATTRIB;
        NOTE_CHILD;
        NOTE_DELETE;
        #[cfg(target_os = "openbsd")]
        NOTE_EOF;
        NOTE_EXEC;
        NOTE_EXIT;
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        NOTE_EXIT_REPARENTED;
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        NOTE_EXITSTATUS;
        NOTE_EXTEND;
        #[cfg(any(target_os = "macos",
                  target_os = "ios",
                  target_os = "freebsd",
                  target_os = "dragonfly"))]
        NOTE_FFAND;
        #[cfg(any(target_os = "macos",
                  target_os = "ios",
                  target_os = "freebsd",
                  target_os = "dragonfly"))]
        NOTE_FFCOPY;
        #[cfg(any(target_os = "macos",
                  target_os = "ios",
                  target_os = "freebsd",
                  target_os = "dragonfly"))]
        NOTE_FFCTRLMASK;
        #[cfg(any(target_os = "macos",
                  target_os = "ios",
                  target_os = "freebsd",
                  target_os = "dragonfly"))]
        NOTE_FFLAGSMASK;
        #[cfg(any(target_os = "macos",
                  target_os = "ios",
                  target_os = "freebsd",
                  target_os = "dragonfly"))]
        NOTE_FFNOP;
        #[cfg(any(target_os = "macos",
                  target_os = "ios",
                  target_os = "freebsd",
                  target_os = "dragonfly"))]
        NOTE_FFOR;
        NOTE_FORK;
        NOTE_LINK;
        NOTE_LOWAT;
        #[cfg(target_os = "freebsd")]
        NOTE_MSECONDS;
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        NOTE_NONE;
        #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
        NOTE_NSECONDS;
        #[cfg(target_os = "dragonfly")]
        NOTE_OOB;
        NOTE_PCTRLMASK;
        NOTE_PDATAMASK;
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        NOTE_REAP;
        NOTE_RENAME;
        NOTE_REVOKE;
        #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
        NOTE_SECONDS;
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        NOTE_SIGNAL;
        NOTE_TRACK;
        NOTE_TRACKERR;
        #[cfg(any(target_os = "macos",
                  target_os = "ios",
                  target_os = "freebsd",
                  target_os = "dragonfly"))]
        NOTE_TRIGGER;
        #[cfg(target_os = "openbsd")]
        NOTE_TRUNCATE;
        #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
        NOTE_USECONDS;
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        NOTE_VM_ERROR;
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        NOTE_VM_PRESSURE;
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        NOTE_VM_PRESSURE_SUDDEN_TERMINATE;
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        NOTE_VM_PRESSURE_TERMINATE;
        NOTE_WRITE;
    }
);

pub fn kqueue() -> Result<RawFd> {
    let res = unsafe { libc::kqueue() };

    Errno::result(res)
}

// KEvent can't derive Send because on some operating systems, udata is defined
// as a void*.  However, KEvent's public API always treats udata as an intptr_t,
// which is safe to Send.
unsafe impl Send for KEvent {
}

impl KEvent {
    pub fn new(ident: uintptr_t, filter: EventFilter, flags: EventFlag,
               fflags:FilterFlag, data: intptr_t, udata: intptr_t) -> KEvent {
        KEvent { kevent: libc::kevent {
            ident: ident,
            filter: filter as type_of_event_filter,
            flags: flags.bits(),
            fflags: fflags.bits(),
            data: data as type_of_data,
            udata: udata as type_of_udata
        } }
    }

    pub fn ident(&self) -> uintptr_t {
        self.kevent.ident
    }

    pub fn filter(&self) -> EventFilter {
        unsafe { mem::transmute(self.kevent.filter as type_of_event_filter) }
    }

    pub fn flags(&self) -> EventFlag {
        EventFlag::from_bits(self.kevent.flags).unwrap()
    }

    pub fn fflags(&self) -> FilterFlag {
        FilterFlag::from_bits(self.kevent.fflags).unwrap()
    }

    pub fn data(&self) -> intptr_t {
        self.kevent.data as intptr_t
    }

    pub fn udata(&self) -> intptr_t {
        self.kevent.udata as intptr_t
    }
}

pub fn kevent(kq: RawFd,
              changelist: &[KEvent],
              eventlist: &mut [KEvent],
              timeout_ms: usize) -> Result<usize> {

    // Convert ms to timespec
    let timeout = timespec {
        tv_sec: (timeout_ms / 1000) as time_t,
        tv_nsec: ((timeout_ms % 1000) * 1_000_000) as c_long
    };

    kevent_ts(kq, changelist, eventlist, Some(timeout))
}

#[cfg(any(target_os = "macos",
          target_os = "ios",
          target_os = "freebsd",
          target_os = "dragonfly",
          target_os = "openbsd"))]
type type_of_nchanges = c_int;
#[cfg(target_os = "netbsd")]
type type_of_nchanges = size_t;

pub fn kevent_ts(kq: RawFd,
              changelist: &[KEvent],
              eventlist: &mut [KEvent],
              timeout_opt: Option<timespec>) -> Result<usize> {

    let res = unsafe {
        libc::kevent(
            kq,
            changelist.as_ptr() as *const libc::kevent,
            changelist.len() as type_of_nchanges,
            eventlist.as_mut_ptr() as *mut libc::kevent,
            eventlist.len() as type_of_nchanges,
            if let Some(ref timeout) = timeout_opt {timeout as *const timespec} else {ptr::null()})
    };

    Errno::result(res).map(|r| r as usize)
}

#[inline]
pub fn ev_set(ev: &mut KEvent,
              ident: usize,
              filter: EventFilter,
              flags: EventFlag,
              fflags: FilterFlag,
              udata: intptr_t) {

    ev.kevent.ident  = ident as uintptr_t;
    ev.kevent.filter = filter as type_of_event_filter;
    ev.kevent.flags  = flags.bits();
    ev.kevent.fflags = fflags.bits();
    ev.kevent.data   = 0;
    ev.kevent.udata  = udata as type_of_udata;
}

#[test]
fn test_struct_kevent() {
    let udata : intptr_t = 12345;

    let expected = libc::kevent{ident: 0xdeadbeef,
                                filter: libc::EVFILT_READ,
                                flags: libc::EV_ONESHOT | libc::EV_ADD,
                                fflags: libc::NOTE_CHILD | libc::NOTE_EXIT,
                                data: 0x1337,
                                udata: udata as type_of_udata};
    let actual = KEvent::new(0xdeadbeef,
                             EventFilter::EVFILT_READ,
                             EV_ONESHOT | EV_ADD,
                             NOTE_CHILD | NOTE_EXIT,
                             0x1337,
                             udata);
    assert!(expected.ident == actual.ident());
    assert!(expected.filter == actual.filter() as type_of_event_filter);
    assert!(expected.flags == actual.flags().bits());
    assert!(expected.fflags == actual.fflags().bits());
    assert!(expected.data == actual.data() as type_of_data);
    assert!(expected.udata == actual.udata() as type_of_udata);
    assert!(mem::size_of::<libc::kevent>() == mem::size_of::<KEvent>());
}
<commit_msg>Fix kevent for netbsd filter datatype<commit_after>/* TOOD: Implement for other kqueue based systems
 */

use {Errno, Result};
#[cfg(not(target_os = "netbsd"))]
use libc::{timespec, time_t, c_int, c_long, intptr_t, uintptr_t};
#[cfg(target_os = "netbsd")]
use libc::{timespec, time_t, c_long, intptr_t, uintptr_t, size_t};
use libc;
use std::os::unix::io::RawFd;
use std::ptr;
use std::mem;

// Redefine kevent in terms of programmer-friendly enums and bitfields.
#[derive(Clone, Copy)]
#[repr(C)]
pub struct KEvent {
    kevent: libc::kevent,
}

#[cfg(any(target_os = "dragonfly", target_os = "freebsd",
          target_os = "ios", target_os = "macos",
          target_os = "openbsd"))]
type type_of_udata = *mut libc::c_void;
#[cfg(any(target_os = "dragonfly", target_os = "freebsd",
          target_os = "ios", target_os = "macos"))]
type type_of_data = libc::intptr_t;
#[cfg(any(target_os = "netbsd"))]
type type_of_udata = intptr_t;
#[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
type type_of_data = libc::int64_t;

#[cfg(target_os = "netbsd")]
type type_of_event_filter = u32;
#[cfg(not(target_os = "netbsd"))]
type type_of_event_filter = i16;
libc_enum!{
    #[cfg_attr(target_os = "netbsd", repr(u32))]
    #[cfg_attr(not(target_os = "netbsd"), repr(i16))]
    pub enum EventFilter {
        EVFILT_AIO,
        #[cfg(target_os = "dragonfly")]
        EVFILT_EXCEPT,
        #[cfg(any(target_os = "dragonfly",
                  target_os = "freebsd",
                  target_os = "ios",
                  target_os = "macos"))]
        EVFILT_FS,
        #[cfg(target_os = "freebsd")]
        EVFILT_LIO,
        #[cfg(any(target_os = "ios", target_os = "macos"))]
        EVFILT_MACHPORT,
        EVFILT_PROC,
        EVFILT_READ,
        EVFILT_SIGNAL,
        EVFILT_TIMER,
        #[cfg(any(target_os = "dragonfly",
                  target_os = "freebsd",
                  target_os = "ios",
                  target_os = "macos"))]
        EVFILT_USER,
        #[cfg(any(target_os = "ios", target_os = "macos"))]
        EVFILT_VM,
        EVFILT_VNODE,
        EVFILT_WRITE,
    }
}

#[cfg(any(target_os = "dragonfly", target_os = "freebsd",
          target_os = "ios", target_os = "macos",
          target_os = "openbsd"))]
pub type type_of_event_flag = u16;
#[cfg(any(target_os = "netbsd"))]
pub type type_of_event_flag = u32;
libc_bitflags!{
    pub struct EventFlag: type_of_event_flag {
        EV_ADD;
        EV_CLEAR;
        EV_DELETE;
        EV_DISABLE;
        // No released version of OpenBSD supports EV_DISPATCH or EV_RECEIPT.
        // These have been commited to the -current branch though and are
        // expected to be part of the OpenBSD 6.2 release in Nov 2017.
        // See: https://marc.info/?l=openbsd-tech&m=149621427511219&w=2
        // https://github.com/rust-lang/libc/pull/613
        #[cfg(any(target_os = "dragonfly", target_os = "freebsd",
                  target_os = "ios", target_os = "macos",
                  target_os = "netbsd"))]
        EV_DISPATCH;
        #[cfg(target_os = "freebsd")]
        EV_DROP;
        EV_ENABLE;
        EV_EOF;
        EV_ERROR;
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        EV_FLAG0;
        EV_FLAG1;
        #[cfg(target_os = "dragonfly")]
        EV_NODATA;
        EV_ONESHOT;
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        EV_OOBAND;
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        EV_POLL;
        #[cfg(any(target_os = "dragonfly", target_os = "freebsd",
                  target_os = "ios", target_os = "macos",
                  target_os = "netbsd"))]
        EV_RECEIPT;
        EV_SYSFLAGS;
    }
}

libc_bitflags!(
    pub struct FilterFlag: u32 {
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        NOTE_ABSOLUTE;
        NOTE_ATTRIB;
        NOTE_CHILD;
        NOTE_DELETE;
        #[cfg(target_os = "openbsd")]
        NOTE_EOF;
        NOTE_EXEC;
        NOTE_EXIT;
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        NOTE_EXIT_REPARENTED;
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        NOTE_EXITSTATUS;
        NOTE_EXTEND;
        #[cfg(any(target_os = "macos",
                  target_os = "ios",
                  target_os = "freebsd",
                  target_os = "dragonfly"))]
        NOTE_FFAND;
        #[cfg(any(target_os = "macos",
                  target_os = "ios",
                  target_os = "freebsd",
                  target_os = "dragonfly"))]
        NOTE_FFCOPY;
        #[cfg(any(target_os = "macos",
                  target_os = "ios",
                  target_os = "freebsd",
                  target_os = "dragonfly"))]
        NOTE_FFCTRLMASK;
        #[cfg(any(target_os = "macos",
                  target_os = "ios",
                  target_os = "freebsd",
                  target_os = "dragonfly"))]
        NOTE_FFLAGSMASK;
        #[cfg(any(target_os = "macos",
                  target_os = "ios",
                  target_os = "freebsd",
                  target_os = "dragonfly"))]
        NOTE_FFNOP;
        #[cfg(any(target_os = "macos",
                  target_os = "ios",
                  target_os = "freebsd",
                  target_os = "dragonfly"))]
        NOTE_FFOR;
        NOTE_FORK;
        NOTE_LINK;
        NOTE_LOWAT;
        #[cfg(target_os = "freebsd")]
        NOTE_MSECONDS;
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        NOTE_NONE;
        #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
        NOTE_NSECONDS;
        #[cfg(target_os = "dragonfly")]
        NOTE_OOB;
        NOTE_PCTRLMASK;
        NOTE_PDATAMASK;
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        NOTE_REAP;
        NOTE_RENAME;
        NOTE_REVOKE;
        #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
        NOTE_SECONDS;
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        NOTE_SIGNAL;
        NOTE_TRACK;
        NOTE_TRACKERR;
        #[cfg(any(target_os = "macos",
                  target_os = "ios",
                  target_os = "freebsd",
                  target_os = "dragonfly"))]
        NOTE_TRIGGER;
        #[cfg(target_os = "openbsd")]
        NOTE_TRUNCATE;
        #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
        NOTE_USECONDS;
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        NOTE_VM_ERROR;
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        NOTE_VM_PRESSURE;
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        NOTE_VM_PRESSURE_SUDDEN_TERMINATE;
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        NOTE_VM_PRESSURE_TERMINATE;
        NOTE_WRITE;
    }
);

pub fn kqueue() -> Result<RawFd> {
    let res = unsafe { libc::kqueue() };

    Errno::result(res)
}

// KEvent can't derive Send because on some operating systems, udata is defined
// as a void*.  However, KEvent's public API always treats udata as an intptr_t,
// which is safe to Send.
unsafe impl Send for KEvent {
}

impl KEvent {
    pub fn new(ident: uintptr_t, filter: EventFilter, flags: EventFlag,
               fflags:FilterFlag, data: intptr_t, udata: intptr_t) -> KEvent {
        KEvent { kevent: libc::kevent {
            ident: ident,
            filter: filter as type_of_event_filter,
            flags: flags.bits(),
            fflags: fflags.bits(),
            data: data as type_of_data,
            udata: udata as type_of_udata
        } }
    }

    pub fn ident(&self) -> uintptr_t {
        self.kevent.ident
    }

    pub fn filter(&self) -> EventFilter {
        unsafe { mem::transmute(self.kevent.filter as type_of_event_filter) }
    }

    pub fn flags(&self) -> EventFlag {
        EventFlag::from_bits(self.kevent.flags).unwrap()
    }

    pub fn fflags(&self) -> FilterFlag {
        FilterFlag::from_bits(self.kevent.fflags).unwrap()
    }

    pub fn data(&self) -> intptr_t {
        self.kevent.data as intptr_t
    }

    pub fn udata(&self) -> intptr_t {
        self.kevent.udata as intptr_t
    }
}

pub fn kevent(kq: RawFd,
              changelist: &[KEvent],
              eventlist: &mut [KEvent],
              timeout_ms: usize) -> Result<usize> {

    // Convert ms to timespec
    let timeout = timespec {
        tv_sec: (timeout_ms / 1000) as time_t,
        tv_nsec: ((timeout_ms % 1000) * 1_000_000) as c_long
    };

    kevent_ts(kq, changelist, eventlist, Some(timeout))
}

#[cfg(any(target_os = "macos",
          target_os = "ios",
          target_os = "freebsd",
          target_os = "dragonfly",
          target_os = "openbsd"))]
type type_of_nchanges = c_int;
#[cfg(target_os = "netbsd")]
type type_of_nchanges = size_t;

pub fn kevent_ts(kq: RawFd,
              changelist: &[KEvent],
              eventlist: &mut [KEvent],
              timeout_opt: Option<timespec>) -> Result<usize> {

    let res = unsafe {
        libc::kevent(
            kq,
            changelist.as_ptr() as *const libc::kevent,
            changelist.len() as type_of_nchanges,
            eventlist.as_mut_ptr() as *mut libc::kevent,
            eventlist.len() as type_of_nchanges,
            if let Some(ref timeout) = timeout_opt {timeout as *const timespec} else {ptr::null()})
    };

    Errno::result(res).map(|r| r as usize)
}

#[inline]
pub fn ev_set(ev: &mut KEvent,
              ident: usize,
              filter: EventFilter,
              flags: EventFlag,
              fflags: FilterFlag,
              udata: intptr_t) {

    ev.kevent.ident  = ident as uintptr_t;
    ev.kevent.filter = filter as type_of_event_filter;
    ev.kevent.flags  = flags.bits();
    ev.kevent.fflags = fflags.bits();
    ev.kevent.data   = 0;
    ev.kevent.udata  = udata as type_of_udata;
}

#[test]
fn test_struct_kevent() {
    let udata : intptr_t = 12345;

    let expected = libc::kevent{ident: 0xdeadbeef,
                                filter: libc::EVFILT_READ,
                                flags: libc::EV_ONESHOT | libc::EV_ADD,
                                fflags: libc::NOTE_CHILD | libc::NOTE_EXIT,
                                data: 0x1337,
                                udata: udata as type_of_udata};
    let actual = KEvent::new(0xdeadbeef,
                             EventFilter::EVFILT_READ,
                             EV_ONESHOT | EV_ADD,
                             NOTE_CHILD | NOTE_EXIT,
                             0x1337,
                             udata);
    assert!(expected.ident == actual.ident());
    assert!(expected.filter == actual.filter() as type_of_event_filter);
    assert!(expected.flags == actual.flags().bits());
    assert!(expected.fflags == actual.fflags().bits());
    assert!(expected.data == actual.data() as type_of_data);
    assert!(expected.udata == actual.udata() as type_of_udata);
    assert!(mem::size_of::<libc::kevent>() == mem::size_of::<KEvent>());
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 by Stacy Prowell.  All rights reserved.
//
// Licensed under the BSD 2-Clause license.  See the file LICENSE
// that is part of this distribution.  This file may not be copied,
// modified, or distributed except according to those terms.extern

//! Provide basic definitions of terms.

use std::collections::BitVec;
//use std::tuple::Tuple1;

/// Represents root terms.
struct Root ( () );
struct Any ( () );
struct ENone ( () );
#[derive(Debug)]
struct EString( String ) ;
struct Symbol( String );
#[derive(Debug)]
struct Integer( isize );
struct Float( f64 );
struct BitString( BitVec );
struct Boolean( bool );

trait Termable<T>{
    fn new(value: T) -> Self;
    fn native(&self) -> &T;
}

impl Termable<String> for EString{
    fn new(value: String) -> Self{
        EString(value)
    }
    fn native(&self) -> &String {
        &self.0
    }
}

#[derive(Debug)]
struct Term<T> {
    value: T,
}

impl<T> Term<T>
{
    pub fn new(t: T) -> Term<T> {

        Term { value: t }
    }
    pub fn unwrap(&self) -> &T {
        &self.value
    }
}

#[test]
fn term_type_check_test() -> (){
    let stringterm = Term::new(EString::new("test".to_string()));
    let nestedterm = Term::new(Term::new(Integer(3)));
    //how to make this access not require .0?
    let stringval = &stringterm.unwrap().native();
    panic!("Intentionial panic. {:?}", stringval);
}

<commit_msg>Got Termable working as a Bound on Term.<commit_after>// Copyright (c) 2015 by Stacy Prowell.  All rights reserved.
//
// Licensed under the BSD 2-Clause license.  See the file LICENSE
// that is part of this distribution.  This file may not be copied,
// modified, or distributed except according to those terms.extern

//! Provide basic definitions of terms.

use std::collections::BitVec;
//use std::tuple::Tuple1;

/// Represents root terms.
struct Root ( () );
struct Any ( () );
struct ENone ( () );
#[derive(Debug)]
struct EString( String ) ;
struct Symbol( String );
#[derive(Debug)]
struct Integer( isize );
struct Float( f64 );
struct BitString( BitVec );
struct Boolean( bool );

trait Termable<T>{
    fn new(value: T) -> Self;
    fn native(&self) -> &T;
}

impl Termable<String> for EString{
    fn new(value: String) -> EString{
        EString(value)
    }
    fn native(&self) -> &String {
        &self.0
    }
}

#[derive(Debug)]
struct Term<T> {
    value: T,
}

impl<T> Term<T>
{
    pub fn new<U>(t: U) -> Term<U> where U: Termable<T> {
        Term { value: t }
    }
    pub fn unwrap(&self) -> &T {
        &self.value
    }
}

#[test]
fn term_type_check_test() -> (){
    let termable = EString::new("Test".to_string());
    let stringterm: Term<EString> = Term::new(termable);
   // let nestedterm = Term::new(Term::new(Integer(3)));
    let stringval = &stringterm.unwrap().native();
    panic!("Intentionial panic. {:?}", stringval);
}

<|endoftext|>