<commit_before>use cairo::{self, MatrixTrait};
use cairo_sys;
use glib::translate::*;
use libc;
use std::cell::Cell;

use attributes::Attribute;
use coord_units::CoordUnits;
use draw;
use drawing_ctx::{self, RsvgDrawingCtx};
use handle::RsvgHandle;
use length::{LengthDir, RsvgLength};
use node::{boxed_node_new, NodeResult, NodeTrait, NodeType, RsvgCNodeImpl, RsvgNode};
use parsers::{parse, Parse};
use property_bag::PropertyBag;
use state::{ComputedValues, Opacity};

coord_units!(MaskUnits, CoordUnits::ObjectBoundingBox);
coord_units!(MaskContentUnits, CoordUnits::UserSpaceOnUse);

// remove this once cairo-rs has this mask_surface()
fn cairo_mask_surface(cr: &cairo::Context, surface: &cairo::Surface, x: f64, y: f64) {
    unsafe {
        let raw_cr = cr.to_glib_none().0;

        cairo_sys::cairo_mask_surface(raw_cr, surface.to_raw_none(), x, y);
    }
}

pub struct NodeMask {
    x: Cell<RsvgLength>,
    y: Cell<RsvgLength>,
    width: Cell<RsvgLength>,
    height: Cell<RsvgLength>,

    units: Cell<MaskUnits>,
    content_units: Cell<MaskContentUnits>,
}

impl NodeMask {
    fn new() -> NodeMask {
        NodeMask {
            x: Cell::new(NodeMask::get_default_pos(LengthDir::Horizontal)),
            y: Cell::new(NodeMask::get_default_pos(LengthDir::Vertical)),

            width: Cell::new(NodeMask::get_default_size(LengthDir::Horizontal)),
            height: Cell::new(NodeMask::get_default_size(LengthDir::Vertical)),

            units: Cell::new(MaskUnits::default()),
            content_units: Cell::new(MaskContentUnits::default()),
        }
    }

    fn get_default_pos(dir: LengthDir) -> RsvgLength {
        RsvgLength::parse("-10%", dir).unwrap()
    }

    fn get_default_size(dir: LengthDir) -> RsvgLength {
        RsvgLength::parse("120%", dir).unwrap()
    }

    pub fn generate_cairo_mask(&self, node: &RsvgNode, draw_ctx: *mut RsvgDrawingCtx) {
        let values = &node.get_computed_values();

        let width = drawing_ctx::get_width(draw_ctx) as i32;
        let height = drawing_ctx::get_height(draw_ctx) as i32;

        let mut surface = match cairo::ImageSurface::create(cairo::Format::ARgb32, width, height) {
            Ok(surface) => surface,
            Err(_) => return,
        };

        let mask_units = CoordUnits::from(self.units.get());
        let content_units = CoordUnits::from(self.content_units.get());

        if mask_units == CoordUnits::ObjectBoundingBox {
            drawing_ctx::push_view_box(draw_ctx, 1.0, 1.0);
        }

        let x = self.x.get().normalize(values, draw_ctx);
        let y = self.y.get().normalize(values, draw_ctx);
        let w = self.width.get().normalize(values, draw_ctx);
        let h = self.height.get().normalize(values, draw_ctx);

        if mask_units == CoordUnits::ObjectBoundingBox {
            drawing_ctx::pop_view_box(draw_ctx);
        }

        // Use a scope because mask_cr needs to release the
        // reference to the surface before we access the pixels
        {
            let save_cr = drawing_ctx::get_cairo_context(draw_ctx);
            let mask_cr = cairo::Context::new(&surface);
            drawing_ctx::set_cairo_context(draw_ctx, &mask_cr);

            if mask_units == CoordUnits::ObjectBoundingBox {
                let bbox = drawing_ctx::get_bbox(draw_ctx);
                let rect = bbox.rect.unwrap();

                draw::add_clipping_rect(
                    draw_ctx,
                    &node.get_transform(),
                    x * rect.width + rect.x,
                    y * rect.height + rect.y,
                    w * rect.width,
                    h * rect.height,
                );
            } else {
                draw::add_clipping_rect(draw_ctx, &node.get_transform(), x, y, w, h);
            }

            // Horribly dirty hack to have the bbox premultiplied to everything
            let node_state = node.get_state_mut();
            let affinesave = node_state.affine;
            if content_units == CoordUnits::ObjectBoundingBox {
                let bbox = drawing_ctx::get_bbox(draw_ctx);
                let rect = bbox.rect.unwrap();
                let mut bbtransform =
                    cairo::Matrix::new(rect.width, 0.0, 0.0, rect.height, rect.x, rect.y);

                bbtransform = cairo::Matrix::multiply(&bbtransform, &affinesave);
                node_state.affine = bbtransform;
                drawing_ctx::push_view_box(draw_ctx, 1.0, 1.0);
            }

            drawing_ctx::state_push(draw_ctx);
            node.draw_children(values, draw_ctx, 0, false);
            drawing_ctx::state_pop(draw_ctx);

            if content_units == CoordUnits::ObjectBoundingBox {
                drawing_ctx::pop_view_box(draw_ctx);
                node_state.affine = affinesave;
            }

            drawing_ctx::set_cairo_context(draw_ctx, &save_cr);
        }

        {
            let rowstride = surface.get_stride() as usize;
            let mut pixels = surface.get_data().unwrap();
            let opacity = {
                let Opacity(o) = values.opacity;
                u8::from(o)
            };

            for row in pixels.chunks_mut(rowstride) {
                for p in row[..4 * width as usize].chunks_mut(4) {
                    //  Assuming, the pixel is linear RGB (not sRGB)
                    //  y = luminance
                    //  Y = 0.2126 R + 0.7152 G + 0.0722 B
                    //  1.0 opacity = 255
                    //
                    //  When Y = 1.0, pixel for mask should be 0xFFFFFFFF
                    //    (you get 1.0 luminance from 255 from R, G and B)
                    //
                    // r_mult = 0xFFFFFFFF / (255.0 * 255.0) * .2126 = 14042.45  ~= 14042
                    // g_mult = 0xFFFFFFFF / (255.0 * 255.0) * .7152 = 47239.69  ~= 47240
                    // b_mult = 0xFFFFFFFF / (255.0 * 255.0) * .0722 =  4768.88  ~= 4769
                    //
                    // This allows for the following expected behaviour:
                    //    (we only care about the most sig byte)
                    // if pixel = 0x00FFFFFF, pixel' = 0xFF......
                    // if pixel = 0x00020202, pixel' = 0x02......
                    // if pixel = 0x00000000, pixel' = 0x00......
                    let (r, g, b, o) = (p[1] as u32, p[2] as u32, p[3] as u32, opacity as u32);
                    p[0] = (((r * 14042 + g * 47240 + b * 4769) * o) >> 24) as u8;
                }
            }
        }

        let cr = drawing_ctx::get_cairo_context(draw_ctx);
        cr.identity_matrix();

        let (xofs, yofs) = drawing_ctx::get_offset(draw_ctx);
        cairo_mask_surface(&cr, &surface, xofs, yofs);
    }
}

impl NodeTrait for NodeMask {
    fn set_atts(&self, _: &RsvgNode, _: *const RsvgHandle, pbag: &PropertyBag) -> NodeResult {
        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::X => self.x.set(parse("x", value, LengthDir::Horizontal, None)?),
                Attribute::Y => self.y.set(parse("y", value, LengthDir::Vertical, None)?),
                Attribute::Width => self.width.set(parse(
                    "width",
                    value,
                    LengthDir::Horizontal,
                    Some(RsvgLength::check_nonnegative),
                )?),
                Attribute::Height => self.height.set(parse(
                    "height",
                    value,
                    LengthDir::Vertical,
                    Some(RsvgLength::check_nonnegative),
                )?),

                Attribute::MaskUnits => self.units.set(parse("maskUnits", value, (), None)?),

                Attribute::MaskContentUnits => {
                    self.content_units
                        .set(parse("maskContentUnits", value, (), None)?)
                }

                _ => (),
            }
        }

        Ok(())
    }

    fn draw(&self, _: &RsvgNode, _: &ComputedValues, _: *mut RsvgDrawingCtx, _: i32, _: bool) {
        // nothing; masks are handled specially
    }

    fn get_c_impl(&self) -> *const RsvgCNodeImpl {
        unreachable!();
    }
}

#[no_mangle]
pub extern "C" fn rsvg_node_mask_new(
    _: *const libc::c_char,
    raw_parent: *const RsvgNode,
) -> *const RsvgNode {
    boxed_node_new(NodeType::Mask, raw_parent, Box::new(NodeMask::new()))
}
<commit_msg>mask.rs: Fixed affines, hopefully, and fixed endianness of the luminance computation<commit_after>use cairo::{self, MatrixTrait};
use cairo_sys;
use glib::translate::*;
use libc;
use std::cell::Cell;

use attributes::Attribute;
use coord_units::CoordUnits;
use draw;
use drawing_ctx::{self, RsvgDrawingCtx};
use handle::RsvgHandle;
use length::{LengthDir, RsvgLength};
use node::{boxed_node_new, NodeResult, NodeTrait, NodeType, RsvgCNodeImpl, RsvgNode};
use parsers::{parse, Parse};
use property_bag::PropertyBag;
use state::{ComputedValues, Opacity};

coord_units!(MaskUnits, CoordUnits::ObjectBoundingBox);
coord_units!(MaskContentUnits, CoordUnits::UserSpaceOnUse);

// remove this once cairo-rs has this mask_surface()
fn cairo_mask_surface(cr: &cairo::Context, surface: &cairo::Surface, x: f64, y: f64) {
    unsafe {
        let raw_cr = cr.to_glib_none().0;

        cairo_sys::cairo_mask_surface(raw_cr, surface.to_raw_none(), x, y);
    }
}

pub struct NodeMask {
    x: Cell<RsvgLength>,
    y: Cell<RsvgLength>,
    width: Cell<RsvgLength>,
    height: Cell<RsvgLength>,

    units: Cell<MaskUnits>,
    content_units: Cell<MaskContentUnits>,
}

impl NodeMask {
    fn new() -> NodeMask {
        NodeMask {
            x: Cell::new(NodeMask::get_default_pos(LengthDir::Horizontal)),
            y: Cell::new(NodeMask::get_default_pos(LengthDir::Vertical)),

            width: Cell::new(NodeMask::get_default_size(LengthDir::Horizontal)),
            height: Cell::new(NodeMask::get_default_size(LengthDir::Vertical)),

            units: Cell::new(MaskUnits::default()),
            content_units: Cell::new(MaskContentUnits::default()),
        }
    }

    fn get_default_pos(dir: LengthDir) -> RsvgLength {
        RsvgLength::parse("-10%", dir).unwrap()
    }

    fn get_default_size(dir: LengthDir) -> RsvgLength {
        RsvgLength::parse("120%", dir).unwrap()
    }

    pub fn generate_cairo_mask(&self, node: &RsvgNode, draw_ctx: *mut RsvgDrawingCtx) {
        let values = &node.get_computed_values();

        let width = drawing_ctx::get_width(draw_ctx) as i32;
        let height = drawing_ctx::get_height(draw_ctx) as i32;

        let mut surface = match cairo::ImageSurface::create(cairo::Format::ARgb32, width, height) {
            Ok(surface) => surface,
            Err(_) => return,
        };

        let mask_units = CoordUnits::from(self.units.get());
        let content_units = CoordUnits::from(self.content_units.get());

        if mask_units == CoordUnits::ObjectBoundingBox {
            drawing_ctx::push_view_box(draw_ctx, 1.0, 1.0);
        }

        let x = self.x.get().normalize(values, draw_ctx);
        let y = self.y.get().normalize(values, draw_ctx);
        let w = self.width.get().normalize(values, draw_ctx);
        let h = self.height.get().normalize(values, draw_ctx);

        if mask_units == CoordUnits::ObjectBoundingBox {
            drawing_ctx::pop_view_box(draw_ctx);
        }

        // Use a scope because mask_cr needs to release the
        // reference to the surface before we access the pixels
        {
            let save_cr = drawing_ctx::get_cairo_context(draw_ctx);

            let mask_cr = cairo::Context::new(&surface);
            mask_cr.set_matrix(save_cr.get_matrix());

            drawing_ctx::set_cairo_context(draw_ctx, &mask_cr);

            if mask_units == CoordUnits::ObjectBoundingBox {
                let bbox = drawing_ctx::get_bbox(draw_ctx);
                let rect = bbox.rect.unwrap();

                draw::add_clipping_rect(
                    draw_ctx,
                    &node.get_transform(),
                    x * rect.width + rect.x,
                    y * rect.height + rect.y,
                    w * rect.width,
                    h * rect.height,
                );
            } else {
                draw::add_clipping_rect(draw_ctx, &node.get_transform(), x, y, w, h);
            }

            if content_units == CoordUnits::ObjectBoundingBox {
                let bbox = drawing_ctx::get_bbox(draw_ctx);
                let rect = bbox.rect.unwrap();
                let bbtransform =
                    cairo::Matrix::new(rect.width, 0.0, 0.0, rect.height, rect.x, rect.y);

                mask_cr.transform(bbtransform);

                drawing_ctx::push_view_box(draw_ctx, 1.0, 1.0);
            }

            drawing_ctx::state_push(draw_ctx);
            node.draw_children(values, draw_ctx, 0, false);
            drawing_ctx::state_pop(draw_ctx);

            if content_units == CoordUnits::ObjectBoundingBox {
                drawing_ctx::pop_view_box(draw_ctx);
            }

            drawing_ctx::set_cairo_context(draw_ctx, &save_cr);
        }

        {
            let rowstride = surface.get_stride() as usize;
            let mut pixels = surface.get_data().unwrap();
            let opacity = {
                let Opacity(o) = values.opacity;
                u8::from(o)
            };

            for row in pixels.chunks_mut(rowstride) {
                for p in row[..4 * width as usize].chunks_mut(4) {
                    //  Assuming, the pixel is linear RGB (not sRGB)
                    //  y = luminance
                    //  Y = 0.2126 R + 0.7152 G + 0.0722 B
                    //  1.0 opacity = 255
                    //
                    //  When Y = 1.0, pixel for mask should be 0xFFFFFFFF
                    //    (you get 1.0 luminance from 255 from R, G and B)
                    //
                    // r_mult = 0xFFFFFFFF / (255.0 * 255.0) * .2126 = 14042.45  ~= 14042
                    // g_mult = 0xFFFFFFFF / (255.0 * 255.0) * .7152 = 47239.69  ~= 47240
                    // b_mult = 0xFFFFFFFF / (255.0 * 255.0) * .0722 =  4768.88  ~= 4769
                    //
                    // This allows for the following expected behaviour:
                    //    (we only care about the most sig byte)
                    // if pixel = 0x00FFFFFF, pixel' = 0xFF......
                    // if pixel = 0x00020202, pixel' = 0x02......
                    // if pixel = 0x00000000, pixel' = 0x00......
                    //
                    // NOTE: the following assumes little-endian
                    let (r, g, b, o) = (p[2] as u32, p[1] as u32, p[0] as u32, opacity as u32);
                    p[3] = (((r * 14042 + g * 47240 + b * 4769) * o) >> 24) as u8;
                }
            }
        }

        let cr = drawing_ctx::get_cairo_context(draw_ctx);
        cr.save();
        cr.identity_matrix();

        let (xofs, yofs) = drawing_ctx::get_offset(draw_ctx);
        cairo_mask_surface(&cr, &surface, xofs, yofs);
        cr.restore();
    }
}

impl NodeTrait for NodeMask {
    fn set_atts(&self, _: &RsvgNode, _: *const RsvgHandle, pbag: &PropertyBag) -> NodeResult {
        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::X => self.x.set(parse("x", value, LengthDir::Horizontal, None)?),
                Attribute::Y => self.y.set(parse("y", value, LengthDir::Vertical, None)?),
                Attribute::Width => self.width.set(parse(
                    "width",
                    value,
                    LengthDir::Horizontal,
                    Some(RsvgLength::check_nonnegative),
                )?),
                Attribute::Height => self.height.set(parse(
                    "height",
                    value,
                    LengthDir::Vertical,
                    Some(RsvgLength::check_nonnegative),
                )?),

                Attribute::MaskUnits => self.units.set(parse("maskUnits", value, (), None)?),

                Attribute::MaskContentUnits => {
                    self.content_units
                        .set(parse("maskContentUnits", value, (), None)?)
                }

                _ => (),
            }
        }

        Ok(())
    }

    fn draw(&self, _: &RsvgNode, _: &ComputedValues, _: *mut RsvgDrawingCtx, _: i32, _: bool) {
        // nothing; masks are handled specially
    }

    fn get_c_impl(&self) -> *const RsvgCNodeImpl {
        unreachable!();
    }
}

#[no_mangle]
pub extern "C" fn rsvg_node_mask_new(
    _: *const libc::c_char,
    raw_parent: *const RsvgNode,
) -> *const RsvgNode {
    boxed_node_new(NodeType::Mask, raw_parent, Box::new(NodeMask::new()))
}
<|endoftext|>


<commit_before>use neon::{prelude::*, result::Throw};
use stencila::{
    eyre,
    serde::{Deserialize, Serialize},
    serde_json, tokio,
};

// We currently JSON serialize / deserialize objects when passing them to / from Rust
// and Node.js. That's because, at the time of writing, the `neon-serde` crate
// (which provides a more convenient mechanism) was not compatible with the most
// recent `neon` version. These function just reduce some boilerplate associated with that.

pub fn to_json<Type>(mut cx: FunctionContext, value: Type) -> JsResult<JsString>
where
    Type: Serialize,
{
    match serde_json::to_string(&value) {
        Ok(json) => Ok(cx.string(json)),
        Err(error) => cx.throw_error(error.to_string()),
    }
}

pub fn to_json_or_throw<Type>(
    mut cx: FunctionContext,
    result: eyre::Result<Type>,
) -> JsResult<JsString>
where
    Type: Serialize,
{
    match result {
        Ok(value) => to_json(cx, value),
        Err(error) => cx.throw_error(error.to_string()),
    }
}

pub fn from_json<'a, Type>(cx: &mut FunctionContext, json: &'a str) -> Result<Type, Throw>
where
    Type: Deserialize<'a>,
{
    match serde_json::from_str::<Type>(&json) {
        Ok(value) => Ok(value),
        Err(error) => cx.throw_error(error.to_string()),
    }
}

/// Create a async runtime to await on async functions
pub fn runtime(cx: &mut FunctionContext) -> Result<tokio::runtime::Runtime, Throw> {
    match tokio::runtime::Runtime::new() {
        Ok(runtime) => Ok(runtime),
        Err(error) => cx.throw_error(error.to_string()),
    }
}
<commit_msg>docs(Node.js): Add note on serde support<commit_after>use neon::{prelude::*, result::Throw};
use stencila::{
    eyre,
    serde::{Deserialize, Serialize},
    serde_json, tokio,
};

// We currently JSON serialize / deserialize objects when passing them to / from Rust
// and Node.js. That's because, at the time of writing, the `neon-serde` crate
// (which provides a more convenient mechanism) was not compatible with the most
// recent `neon` version. These function just reduce some boilerplate associated with that.
// See https://github.com/neon-bindings/neon/pull/701 for progress on "native" neon serde
// compatibility.

pub fn to_json<Type>(mut cx: FunctionContext, value: Type) -> JsResult<JsString>
where
    Type: Serialize,
{
    match serde_json::to_string(&value) {
        Ok(json) => Ok(cx.string(json)),
        Err(error) => cx.throw_error(error.to_string()),
    }
}

pub fn to_json_or_throw<Type>(
    mut cx: FunctionContext,
    result: eyre::Result<Type>,
) -> JsResult<JsString>
where
    Type: Serialize,
{
    match result {
        Ok(value) => to_json(cx, value),
        Err(error) => cx.throw_error(error.to_string()),
    }
}

pub fn from_json<'a, Type>(cx: &mut FunctionContext, json: &'a str) -> Result<Type, Throw>
where
    Type: Deserialize<'a>,
{
    match serde_json::from_str::<Type>(&json) {
        Ok(value) => Ok(value),
        Err(error) => cx.throw_error(error.to_string()),
    }
}

/// Create a async runtime to await on async functions
pub fn runtime(cx: &mut FunctionContext) -> Result<tokio::runtime::Runtime, Throw> {
    match tokio::runtime::Runtime::new() {
        Ok(runtime) => Ok(runtime),
        Err(error) => cx.throw_error(error.to_string()),
    }
}
<|endoftext|>


<commit_before>pub use super::*;

use rustc::mir::*;
use crate::dataflow::BitDenotation;

#[derive(Copy, Clone)]
pub struct MaybeStorageLive<'a, 'tcx: 'a> {
    mir: &'a Mir<'tcx>,
}

impl<'a, 'tcx: 'a> MaybeStorageLive<'a, 'tcx> {
    pub fn new(mir: &'a Mir<'tcx>)
               -> Self {
        MaybeStorageLive { mir }
    }

    pub fn mir(&self) -> &Mir<'tcx> {
        self.mir
    }
}

impl<'a, 'tcx> BitDenotation<'tcx> for MaybeStorageLive<'a, 'tcx> {
    type Idx = Local;
    fn name() -> &'static str { "maybe_storage_live" }
    fn bits_per_block(&self) -> usize {
        self.mir.local_decls.len()
    }

    fn start_block_effect(&self, _sets: &mut BitSet<Local>) {
        // Nothing is live on function entry
    }

    fn statement_effect(&self,
                        sets: &mut BlockSets<'_, Local>,
                        loc: Location) {
        let stmt = &self.mir[loc.block].statements[loc.statement_index];

        match stmt.kind {
            StatementKind::StorageLive(l) => sets.gen(l),
            StatementKind::StorageDead(l) => sets.kill(l),
            _ => (),
        }
    }

    fn terminator_effect(&self,
                         _sets: &mut BlockSets<'_, Local>,
                         _loc: Location) {
        // Terminators have no effect
    }

    fn propagate_call_return(
        &self,
        _in_out: &mut BitSet<Local>,
        _call_bb: mir::BasicBlock,
        _dest_bb: mir::BasicBlock,
        _dest_place: &mir::Place<'tcx>,
    ) {
        // Nothing to do when a call returns successfully
    }
}

impl<'a, 'tcx> BitSetOperator for MaybeStorageLive<'a, 'tcx> {
    #[inline]
    fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
        inout_set.union(in_set) // "maybe" means we union effects of both preds
    }
}

impl<'a, 'tcx> InitialFlow for MaybeStorageLive<'a, 'tcx> {
    #[inline]
    fn bottom_value() -> bool {
        false // bottom = dead
    }
}
<commit_msg>Make MaybeStorageLive drop-aware<commit_after>pub use super::*;

use rustc::mir::*;
use crate::dataflow::BitDenotation;

#[derive(Copy, Clone)]
pub struct MaybeStorageLive<'a, 'tcx: 'a> {
    mir: &'a Mir<'tcx>,
}

impl<'a, 'tcx: 'a> MaybeStorageLive<'a, 'tcx> {
    pub fn new(mir: &'a Mir<'tcx>)
               -> Self {
        MaybeStorageLive { mir }
    }

    pub fn mir(&self) -> &Mir<'tcx> {
        self.mir
    }
}

impl<'a, 'tcx> BitDenotation<'tcx> for MaybeStorageLive<'a, 'tcx> {
    type Idx = Local;
    fn name() -> &'static str { "maybe_storage_live" }
    fn bits_per_block(&self) -> usize {
        self.mir.local_decls.len()
    }

    fn start_block_effect(&self, _sets: &mut BitSet<Local>) {
        // Nothing is live on function entry
    }

    fn statement_effect(&self,
                        sets: &mut BlockSets<'_, Local>,
                        loc: Location) {
        let stmt = &self.mir[loc.block].statements[loc.statement_index];

        match stmt.kind {
            StatementKind::StorageLive(l) => sets.gen(l),
            StatementKind::StorageDead(l) => sets.kill(l),
            _ => (),
        }
    }

    fn terminator_effect(&self,
                         sets: &mut BlockSets<'_, Local>,
                         loc: Location) {
        match &self.mir[loc.block].terminator().kind {
            TerminatorKind::Drop { location, .. } => if let Some(l) = location.local() {
                sets.kill(l);
            }
            _ => (),
        }
    }

    fn propagate_call_return(
        &self,
        _in_out: &mut BitSet<Local>,
        _call_bb: mir::BasicBlock,
        _dest_bb: mir::BasicBlock,
        _dest_place: &mir::Place<'tcx>,
    ) {
        // Nothing to do when a call returns successfully
    }
}

impl<'a, 'tcx> BitSetOperator for MaybeStorageLive<'a, 'tcx> {
    #[inline]
    fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
        inout_set.union(in_set) // "maybe" means we union effects of both preds
    }
}

impl<'a, 'tcx> InitialFlow for MaybeStorageLive<'a, 'tcx> {
    #[inline]
    fn bottom_value() -> bool {
        false // bottom = dead
    }
}
<|endoftext|>


<commit_before>use crate::errors::OscError;

use alloc::borrow::ToOwned;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use nom::branch::alt;
use nom::bytes::complete::{is_not, tag, take_till1, take, is_a, take_while1};
use nom::character::complete::{char, satisfy};
use nom::combinator::{all_consuming, complete, map_parser, recognize, verify};
use nom::multi::{many1, separated_list0, separated_list1};
use nom::sequence::{delimited, pair, preceded, separated_pair};
use nom::{IResult, Parser};
use nom::error::{ErrorKind, ParseError};
use regex::Regex;

/// With a Matcher OSC method addresses can be [matched](Matcher::match_address) against an OSC address pattern.
/// Refer to the OSC specification for details about OSC address spaces: <http://opensoundcontrol.org/spec-1_0.html#osc-address-spaces-and-osc-addresses>
#[derive(Debug)]
pub struct Matcher {
    pub pattern: String,
    pattern_parts: Vec<AddressPatternComponent>,
}

impl Matcher {
    /// Instantiates a new `Matcher` with the given address pattern.
    /// An error will be returned if the given address pattern is invalid.
    ///
    /// Matcher should be instantiated once per pattern and reused because its construction requires parsing the address pattern which is computationally expensive.
    ///
    /// A valid address pattern begins with a `/` and contains at least a method name, e.g. `/tempo`.
    /// OSC defines a couple of rules that look like regular expression but are subtly different:
    ///
    /// - `?` matches a single character
    /// - `*` matches zero or more characters
    /// - `[a-z]` are basically regex [character classes](https://www.regular-expressions.info/charclass.html)
    /// - `{foo,bar}` is an alternative, matching either `foo` or `bar`
    /// - everything else is matched literally
    ///
    /// Refer to the OSC specification for details about address pattern matching: <osc-message-dispatching-and-pattern-matching>.
    ///
    /// # Examples
    ///
    /// ```
    /// use rosc::address::Matcher;
    ///
    /// Matcher::new("/tempo").expect("valid address");
    /// Matcher::new("").expect_err("address does not start with a slash");
    /// ```
    pub fn new(pattern: &str) -> Result<Self, OscError> {
        verify_address_pattern(pattern)?;
        let pattern_parts = match all_consuming(many1(map_address_pattern_component))(pattern) {
            Ok((_, parts)) => { parts }
            // This should never happen because pattern is verified above
            Err(_) => panic!("Address pattern must be valid")
        };

        Ok(Matcher { pattern: String::from(pattern), pattern_parts })
    }

    /// Match an OSC address against an address pattern.
    /// If the address matches the pattern the result will be `true`, otherwise `false`.
    /// An error is returned if the given OSC address is not valid.
    ///
    /// A valid OSC address begins with a `/` and contains at least a method name, e.g. `/tempo`.
    /// Despite OSC address patterns a plain address must not include any of the following characters `#*,/?[]{}`.
    ///
    /// # Examples
    ///
    /// ```
    /// use rosc::address::Matcher;
    ///
    /// let matcher = Matcher::new("/oscillator/[0-9]/{frequency,phase}").unwrap();
    /// assert!(matcher.match_address("/oscillator/1/frequency").unwrap());
    /// assert!(matcher.match_address("/oscillator/8/phase").unwrap());
    /// assert_eq!(matcher.match_address("/oscillator/4/detune").unwrap(), false);
    /// ```
    pub fn match_address(&self, address: &str) -> Result<bool, OscError> {
        verify_address(address)?;   // TODO: Create an address struct so we don't have to re-check addresses every time we match
        // Trivial case
        if address == self.pattern {
            return Ok(true);
        }
        let mut remainder: &str = address;
        // Match the the address component by component
        for (index, part) in self.pattern_parts.as_slice().iter().enumerate() {
            let result = match part {
                AddressPatternComponent::Tag(s) => match_literally(remainder, s.as_str()),
                AddressPatternComponent::WildcardSingle => match_wildcard_single(remainder),
                AddressPatternComponent::Wildcard(l) => {
                    // Check if this is the last pattern component
                    if index < self.pattern_parts.len() - 1 {
                        let next = &self.pattern_parts[index + 1];
                        match next {
                            // If the next component is a '/', there are no more components in the current part and it can be wholly consumed
                            AddressPatternComponent::Tag(s) if s == "/" => match_wildcard(remainder, l.clone(), None),
                            _ => match_wildcard(remainder, l.clone(), Some(next))
                        }
                    } else {
                        match_wildcard(remainder, l.clone(), None)
                    }
                }
                AddressPatternComponent::CharacterClass(cc) => match_character_class(remainder, cc),
                AddressPatternComponent::Choice(s) => match_choice(remainder, s),
            };

            match result {
                Ok((i, _)) => remainder = i,
                Err(_) => return Ok(false)  // Component didn't match, goodbye
            };
        }

        // Address is only matched if it was consumed entirely
        return if remainder.len() == 0 {
            Ok(true)
        } else {
            Ok(false)
        };
    }
}

/// Check whether a character is an allowed address character
/// All printable ASCII characters except for a few special characters are allowed
fn is_address_character(x: char) -> bool {
    match x {
        ' ' | '#' | '*' | ',' | '/' | '?' | '[' | ']' | '{' | '}' => false,
        c => c > '\x20' && c < '\x7F'
    }
}

/// A characters class is defined by a set or range of characters that it matches.
/// For example, [a-z] matches all lowercase alphabetic letters. It can also contain multiple
/// ranges, like [a-zA-Z]. Instead of a range you can also directly provide the characters to match,
/// e.g. [abc123]. You can also combine this with ranges, like [a-z123].
/// If the first characters is an exclamation point, the match is negated, e.g. [!0-9] will match
/// anything except numbers.
#[derive(Debug)]
struct CharacterClass {
    pub negated: bool,
    pub characters: String,
}

/// Expand a character range like 'a-d' to all the letters contained in the range, e.g. 'abcd'
/// This is done by converting the characters to their ASCII values and then getting every ASCII
/// in between.
fn expand_character_range<'a>(first: char, second: char) -> String {
    let start = first as u8;
    let end = second as u8;

    let range;
    if start >= end {
        range = end..=start;
    } else {
        range = start..=end;
    }

    let mut out = String::from("");
    for c in range {
        // For funky ranges like [0-a], some illegal characters are contained
        if is_address_character(c as char) {
            out.push(c as char);
        }
    }
    out
}

impl CharacterClass {
    pub fn new(s: &str) -> Self {
        let mut input = s;
        let negated;
        match char::<_, nom::error::Error<&str>>('!')(input) {
            Ok((i, _)) => {
                negated = true;
                input = i;
            }
            Err(_) => negated = false
        }

        let characters = complete(many1(alt((
            // '!' besides at beginning has no special meaning, but is legal
            char::<_, nom::error::Error<&str>>('!').map(|_| String::from("")),
            // attempt to match a range like a-z or 0-9
            separated_pair(satisfy(is_address_character), char('-'), satisfy(is_address_character)).map(|(first, second)| expand_character_range(first, second)),
            // Match characters literally
            satisfy(is_address_character).map(|x| x.to_string()),
            // Trailing dash
            char('-').map(|_| { String::from("-") })
        ))))(input);

        match characters {
            // TODO: Deduplicate?
            Ok((_, o)) => CharacterClass { negated, characters: o.concat() },
            _ => { panic!("Invalid character class formatting {}", s) }
        }
    }
}

#[derive(Debug)]
enum AddressPatternComponent {
    Tag(String),
    Wildcard(usize),
    WildcardSingle,
    CharacterClass(CharacterClass),
    Choice(Vec<String>),
}

fn map_address_pattern_component(input: &str) -> IResult<&str, AddressPatternComponent>
{
    alt((
        // Anything that's alphanumeric gets matched literally
        take_while1(is_address_character).map(|s: &str| { AddressPatternComponent::Tag(String::from(s)) }),
        // Slashes must be seperated into their own tag for the non-greedy implementation of wildcards
        char('/').map(|c: char| { AddressPatternComponent::Tag(c.to_string()) }),
        tag("?").map(|_| { AddressPatternComponent::WildcardSingle }),
        // Combinations of wildcards are a bit tricky.
        // Multiple '*' wildcards in a row are equal to a single '*'.
        // A '*' wildcard followed by any number of '?' wildcards is also equal to '*' but must
        // match at least the same amount of characters as there are '?' wildcards in the combination.
        // For example, '*??' must match at least 2 characters.
        is_a("*?").map(|x: &str| { AddressPatternComponent::Wildcard(x.matches("?").count()) }),
        map_parser(
            delimited(char('{'), is_not("}"), char('}')),
            separated_list0(char(','.into()), take_while1(is_address_character)),
        ).map(|alternatives: Vec<&str>| { AddressPatternComponent::Choice(alternatives.iter().map(|x| x.to_string()).collect()) }),
        // TODO: prevent an empty character class, i.e. [!]
        delimited(char('['), take_while1(is_address_character), char(']')).map(|s: &str| { AddressPatternComponent::CharacterClass(CharacterClass::new(s)) })
    ))(input)
}

fn match_literally<'a>(input: &'a str, pattern: &str) -> IResult<&'a str, &'a str>
{
    tag(pattern)(input)
}

fn match_wildcard_single(input: &str) -> IResult<&str, &str>
{
    // TODO: this has to be possible with a simpler parser?
    verify(take(1usize), |s: &str| s.chars().all(is_address_character))(input)
}

fn match_character_class<'a>(input: &'a str, character_class: &'a CharacterClass) -> IResult<&'a str, &'a str> {
    if character_class.negated {
        is_not(character_class.characters.as_str())(input)
    } else {
        is_a(character_class.characters.as_str())(input)
    }
}

/// Try all the tags in a choice element
/// Example choice element: '{foo,bar}'
/// It will get parsed into a vector containing the strings "foo" and "bar", which are then matched
fn match_choice<'a>(input: &'a str, choices: &Vec<String>) -> IResult<&'a str, &'a str> {
    for choice in choices
    {
        match tag::<_, _, nom::error::Error<&str>>(choice.as_str())(input) {
            Ok((i, o)) => return Ok((i, o)),
            Err(_) => {}
        }
    }
    return Err(nom::Err::Error(nom::error::Error::from_error_kind(input, ErrorKind::Tag)));
}

/// Match Wildcard '*' by either consuming the rest of the part, or, if it's not the last component
/// in the part, by looking ahead and matching the next component
fn match_wildcard<'a>(input: &'a str, minimum_length: usize, next: Option<&AddressPatternComponent>) -> IResult<&'a str, &'a str>
{
    match next {
        // No next component, consume all allowed characters until end or next '/'
        None => verify(take_while1(is_address_character), |s: &str| s.len() >= minimum_length)(input),
        // There is another element in this part, so logic gets a bit more complicated
        Some(component) => {
            // Wildcards can only match within the current address part, discard the rest
            let address_part = match input.split_once("/") {
                Some((p, _)) => p,
                None => input
            };

            // Attempt to find the latest matching occurrence of the next pattern component
            // This is a greedy wildcard implementation
            let mut longest: usize = 0;
            for i in 0..address_part.len() {
                let (_, substring) = input.split_at(i);
                let result: IResult<_, _, nom::error::Error<&str>> = match component {
                    AddressPatternComponent::Tag(s) => match_literally(substring, s.as_str()),
                    AddressPatternComponent::CharacterClass(cc) => match_character_class(substring, cc),
                    AddressPatternComponent::Choice(s) => match_choice(substring, s),
                    // These two cases are prevented from happening by map_address_pattern_component
                    AddressPatternComponent::WildcardSingle => panic!("Single wildcard ('?') must not follow wildcard ('*')"),
                    AddressPatternComponent::Wildcard(_) => panic!("Double wildcards must be condensed into one"),
                };

                match result {
                    Ok(_) => longest = i,
                    _ => {}
                }
            }
            verify(take(longest), |s: &str| s.len() >= minimum_length)(input)
        }
    }
}

/// Verify that an address is valid
///
/// # Examples
/// ```
/// use rosc::address::verify_address;
///
/// match verify_address("/oscillator/1") {
///     Ok(()) => println!("Address is valid"),
///     Err(e) => println!("Address is not valid")
/// }
/// ```
pub fn verify_address(input: &str) -> Result<(), OscError>
{
    match all_consuming::<_,_,nom::error::Error<&str>,_>(
        many1(pair(tag("/"), take_while1(is_address_character)))
    )(input) {
        Ok((_)) => Ok(()),
        Err(_) => Err(OscError::BadAddress("Invalid address".to_string()))
    }
}

/// Parse an address pattern's part until the next '/' or the end
fn address_pattern_part_parser(input: &str) -> IResult<&str, Vec<&str>> {
    many1::<_, _, nom::error::Error<&str>, _>(
        alt((
            take_while1(is_address_character),
            tag("?"),
            tag("*"),
            recognize(delimited(char('{'), separated_list1(tag(","), take_while1(is_address_character)), char('}'))),
            delimited(char('['), take_while1(is_address_character), char(']')),
        ))
    )(input)
}

/// Verify that an address pattern is valid
///
/// # Examples
/// ```
/// use rosc::address::verify_address_pattern;
///
/// match verify_address_pattern("/oscillator/[0-9]/*") {
///     Ok(()) => println!("Address is valid"),
///     Err(e) => println!("Address is not valid")
/// }
/// ```
pub fn verify_address_pattern(input: &str) -> Result<(), OscError>
{
    match all_consuming(many1(
        // Each part must start with a '/'. This automatically also prevents a trailing '/'
        pair(
            tag("/"),
            address_pattern_part_parser.map(|x| x.concat()),
        )
    ))(input)
    {
        Ok((_)) => Ok(()),
        Err(_) => Err(OscError::BadAddress("Invalid address pattern".to_string()))
    }
}<commit_msg>Add choice parser<commit_after>use crate::errors::OscError;

use alloc::borrow::ToOwned;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use nom::branch::alt;
use nom::bytes::complete::{is_not, tag, take_till1, take, is_a, take_while1};
use nom::character::complete::{char, satisfy};
use nom::combinator::{all_consuming, complete, map_parser, recognize, verify};
use nom::multi::{many1, separated_list0, separated_list1};
use nom::sequence::{delimited, pair, preceded, separated_pair};
use nom::{IResult, Parser};
use nom::error::{ErrorKind, ParseError};
use regex::Regex;

/// With a Matcher OSC method addresses can be [matched](Matcher::match_address) against an OSC address pattern.
/// Refer to the OSC specification for details about OSC address spaces: <http://opensoundcontrol.org/spec-1_0.html#osc-address-spaces-and-osc-addresses>
#[derive(Debug)]
pub struct Matcher {
    pub pattern: String,
    pattern_parts: Vec<AddressPatternComponent>,
}

impl Matcher {
    /// Instantiates a new `Matcher` with the given address pattern.
    /// An error will be returned if the given address pattern is invalid.
    ///
    /// Matcher should be instantiated once per pattern and reused because its construction requires parsing the address pattern which is computationally expensive.
    ///
    /// A valid address pattern begins with a `/` and contains at least a method name, e.g. `/tempo`.
    /// OSC defines a couple of rules that look like regular expression but are subtly different:
    ///
    /// - `?` matches a single character
    /// - `*` matches zero or more characters
    /// - `[a-z]` are basically regex [character classes](https://www.regular-expressions.info/charclass.html)
    /// - `{foo,bar}` is an alternative, matching either `foo` or `bar`
    /// - everything else is matched literally
    ///
    /// Refer to the OSC specification for details about address pattern matching: <osc-message-dispatching-and-pattern-matching>.
    ///
    /// # Examples
    ///
    /// ```
    /// use rosc::address::Matcher;
    ///
    /// Matcher::new("/tempo").expect("valid address");
    /// Matcher::new("").expect_err("address does not start with a slash");
    /// ```
    pub fn new(pattern: &str) -> Result<Self, OscError> {
        verify_address_pattern(pattern)?;
        let pattern_parts = match all_consuming(many1(map_address_pattern_component))(pattern) {
            Ok((_, parts)) => { parts }
            // This should never happen because pattern is verified above
            Err(_) => panic!("Address pattern must be valid")
        };

        Ok(Matcher { pattern: String::from(pattern), pattern_parts })
    }

    /// Match an OSC address against an address pattern.
    /// If the address matches the pattern the result will be `true`, otherwise `false`.
    /// An error is returned if the given OSC address is not valid.
    ///
    /// A valid OSC address begins with a `/` and contains at least a method name, e.g. `/tempo`.
    /// Despite OSC address patterns a plain address must not include any of the following characters `#*,/?[]{}`.
    ///
    /// # Examples
    ///
    /// ```
    /// use rosc::address::Matcher;
    ///
    /// let matcher = Matcher::new("/oscillator/[0-9]/{frequency,phase}").unwrap();
    /// assert!(matcher.match_address("/oscillator/1/frequency").unwrap());
    /// assert!(matcher.match_address("/oscillator/8/phase").unwrap());
    /// assert_eq!(matcher.match_address("/oscillator/4/detune").unwrap(), false);
    /// ```
    pub fn match_address(&self, address: &str) -> Result<bool, OscError> {
        verify_address(address)?;   // TODO: Create an address struct so we don't have to re-check addresses every time we match
        // Trivial case
        if address == self.pattern {
            return Ok(true);
        }
        let mut remainder: &str = address;
        // Match the the address component by component
        for (index, part) in self.pattern_parts.as_slice().iter().enumerate() {
            let result = match part {
                AddressPatternComponent::Tag(s) => match_literally(remainder, s.as_str()),
                AddressPatternComponent::WildcardSingle => match_wildcard_single(remainder),
                AddressPatternComponent::Wildcard(l) => {
                    // Check if this is the last pattern component
                    if index < self.pattern_parts.len() - 1 {
                        let next = &self.pattern_parts[index + 1];
                        match next {
                            // If the next component is a '/', there are no more components in the current part and it can be wholly consumed
                            AddressPatternComponent::Tag(s) if s == "/" => match_wildcard(remainder, l.clone(), None),
                            _ => match_wildcard(remainder, l.clone(), Some(next))
                        }
                    } else {
                        match_wildcard(remainder, l.clone(), None)
                    }
                }
                AddressPatternComponent::CharacterClass(cc) => match_character_class(remainder, cc),
                AddressPatternComponent::Choice(s) => match_choice(remainder, s),
            };

            match result {
                Ok((i, _)) => remainder = i,
                Err(_) => return Ok(false)  // Component didn't match, goodbye
            };
        }

        // Address is only matched if it was consumed entirely
        return if remainder.len() == 0 {
            Ok(true)
        } else {
            Ok(false)
        };
    }
}

/// Check whether a character is an allowed address character
/// All printable ASCII characters except for a few special characters are allowed
fn is_address_character(x: char) -> bool {
    match x {
        ' ' | '#' | '*' | ',' | '/' | '?' | '[' | ']' | '{' | '}' => false,
        c => c > '\x20' && c < '\x7F'
    }
}

/// Parser to turn a choice like '{foo,bar}' into a vector containing the choices, like ["foo", "bar"]
fn pattern_choice(input: &str) -> IResult<&str, Vec<&str>>
{
    delimited(char('{'), separated_list1(tag(","), take_while1(is_address_character)), char('}'))(input)
}

/// A characters class is defined by a set or range of characters that it matches.
/// For example, [a-z] matches all lowercase alphabetic letters. It can also contain multiple
/// ranges, like [a-zA-Z]. Instead of a range you can also directly provide the characters to match,
/// e.g. [abc123]. You can also combine this with ranges, like [a-z123].
/// If the first characters is an exclamation point, the match is negated, e.g. [!0-9] will match
/// anything except numbers.
#[derive(Debug)]
struct CharacterClass {
    pub negated: bool,
    pub characters: String,
}

/// Expand a character range like 'a-d' to all the letters contained in the range, e.g. 'abcd'
/// This is done by converting the characters to their ASCII values and then getting every ASCII
/// in between.
fn expand_character_range<'a>(first: char, second: char) -> String {
    let start = first as u8;
    let end = second as u8;

    let range;
    if start >= end {
        range = end..=start;
    } else {
        range = start..=end;
    }

    let mut out = String::from("");
    for c in range {
        // For funky ranges like [0-a], some illegal characters are contained
        if is_address_character(c as char) {
            out.push(c as char);
        }
    }
    out
}

impl CharacterClass {
    pub fn new(s: &str) -> Self {
        let mut input = s;
        let negated;
        match char::<_, nom::error::Error<&str>>('!')(input) {
            Ok((i, _)) => {
                negated = true;
                input = i;
            }
            Err(_) => negated = false
        }

        let characters = complete(many1(alt((
            // '!' besides at beginning has no special meaning, but is legal
            char::<_, nom::error::Error<&str>>('!').map(|_| String::from("")),
            // attempt to match a range like a-z or 0-9
            separated_pair(satisfy(is_address_character), char('-'), satisfy(is_address_character)).map(|(first, second)| expand_character_range(first, second)),
            // Match characters literally
            satisfy(is_address_character).map(|x| x.to_string()),
            // Trailing dash
            char('-').map(|_| { String::from("-") })
        ))))(input);

        match characters {
            // TODO: Deduplicate?
            Ok((_, o)) => CharacterClass { negated, characters: o.concat() },
            _ => { panic!("Invalid character class formatting {}", s) }
        }
    }
}

#[derive(Debug)]
enum AddressPatternComponent {
    Tag(String),
    Wildcard(usize),
    WildcardSingle,
    CharacterClass(CharacterClass),
    Choice(Vec<String>),
}

fn map_address_pattern_component(input: &str) -> IResult<&str, AddressPatternComponent>
{
    alt((
        // Anything that's alphanumeric gets matched literally
        take_while1(is_address_character).map(|s: &str| { AddressPatternComponent::Tag(String::from(s)) }),
        // Slashes must be seperated into their own tag for the non-greedy implementation of wildcards
        char('/').map(|c: char| { AddressPatternComponent::Tag(c.to_string()) }),
        tag("?").map(|_| { AddressPatternComponent::WildcardSingle }),
        // Combinations of wildcards are a bit tricky.
        // Multiple '*' wildcards in a row are equal to a single '*'.
        // A '*' wildcard followed by any number of '?' wildcards is also equal to '*' but must
        // match at least the same amount of characters as there are '?' wildcards in the combination.
        // For example, '*??' must match at least 2 characters.
        is_a("*?").map(|x: &str| { AddressPatternComponent::Wildcard(x.matches("?").count()) }),
        pattern_choice.map(|choices: Vec<&str>| { AddressPatternComponent::Choice(choices.iter().map(|x| x.to_string()).collect()) }),
        // TODO: prevent an empty character class, i.e. [!]
        delimited(char('['), take_while1(is_address_character), char(']')).map(|s: &str| { AddressPatternComponent::CharacterClass(CharacterClass::new(s)) })
    ))(input)
}

fn match_literally<'a>(input: &'a str, pattern: &str) -> IResult<&'a str, &'a str>
{
    tag(pattern)(input)
}

fn match_wildcard_single(input: &str) -> IResult<&str, &str>
{
    // TODO: this has to be possible with a simpler parser?
    verify(take(1usize), |s: &str| s.chars().all(is_address_character))(input)
}

fn match_character_class<'a>(input: &'a str, character_class: &'a CharacterClass) -> IResult<&'a str, &'a str> {
    if character_class.negated {
        is_not(character_class.characters.as_str())(input)
    } else {
        is_a(character_class.characters.as_str())(input)
    }
}

/// Try all the tags in a choice element
/// Example choice element: '{foo,bar}'
/// It will get parsed into a vector containing the strings "foo" and "bar", which are then matched
fn match_choice<'a>(input: &'a str, choices: &Vec<String>) -> IResult<&'a str, &'a str> {
    for choice in choices
    {
        match tag::<_, _, nom::error::Error<&str>>(choice.as_str())(input) {
            Ok((i, o)) => return Ok((i, o)),
            Err(_) => {}
        }
    }
    return Err(nom::Err::Error(nom::error::Error::from_error_kind(input, ErrorKind::Tag)));
}

/// Match Wildcard '*' by either consuming the rest of the part, or, if it's not the last component
/// in the part, by looking ahead and matching the next component
fn match_wildcard<'a>(input: &'a str, minimum_length: usize, next: Option<&AddressPatternComponent>) -> IResult<&'a str, &'a str>
{
    match next {
        // No next component, consume all allowed characters until end or next '/'
        None => verify(take_while1(is_address_character), |s: &str| s.len() >= minimum_length)(input),
        // There is another element in this part, so logic gets a bit more complicated
        Some(component) => {
            // Wildcards can only match within the current address part, discard the rest
            let address_part = match input.split_once("/") {
                Some((p, _)) => p,
                None => input
            };

            // Attempt to find the latest matching occurrence of the next pattern component
            // This is a greedy wildcard implementation
            let mut longest: usize = 0;
            for i in 0..address_part.len() {
                let (_, substring) = input.split_at(i);
                let result: IResult<_, _, nom::error::Error<&str>> = match component {
                    AddressPatternComponent::Tag(s) => match_literally(substring, s.as_str()),
                    AddressPatternComponent::CharacterClass(cc) => match_character_class(substring, cc),
                    AddressPatternComponent::Choice(s) => match_choice(substring, s),
                    // These two cases are prevented from happening by map_address_pattern_component
                    AddressPatternComponent::WildcardSingle => panic!("Single wildcard ('?') must not follow wildcard ('*')"),
                    AddressPatternComponent::Wildcard(_) => panic!("Double wildcards must be condensed into one"),
                };

                match result {
                    Ok(_) => longest = i,
                    _ => {}
                }
            }
            verify(take(longest), |s: &str| s.len() >= minimum_length)(input)
        }
    }
}

/// Verify that an address is valid
///
/// # Examples
/// ```
/// use rosc::address::verify_address;
///
/// match verify_address("/oscillator/1") {
///     Ok(()) => println!("Address is valid"),
///     Err(e) => println!("Address is not valid")
/// }
/// ```
pub fn verify_address(input: &str) -> Result<(), OscError>
{
    match all_consuming::<_,_,nom::error::Error<&str>,_>(
        many1(pair(tag("/"), take_while1(is_address_character)))
    )(input) {
        Ok((_)) => Ok(()),
        Err(_) => Err(OscError::BadAddress("Invalid address".to_string()))
    }
}

/// Parse an address pattern's part until the next '/' or the end
fn address_pattern_part_parser(input: &str) -> IResult<&str, Vec<&str>> {
    many1::<_, _, nom::error::Error<&str>, _>(
        alt((
            take_while1(is_address_character),
            tag("?"),
            tag("*"),
            recognize(pattern_choice),
            // TODO: Turn into reusable parser together with map_address_pattern_component
            delimited(char('['), take_while1(is_address_character), char(']')),
        ))
    )(input)
}

/// Verify that an address pattern is valid
///
/// # Examples
/// ```
/// use rosc::address::verify_address_pattern;
///
/// match verify_address_pattern("/oscillator/[0-9]/*") {
///     Ok(()) => println!("Address is valid"),
///     Err(e) => println!("Address is not valid")
/// }
/// ```
pub fn verify_address_pattern(input: &str) -> Result<(), OscError>
{
    match all_consuming(many1(
        // Each part must start with a '/'. This automatically also prevents a trailing '/'
        pair(
            tag("/"),
            address_pattern_part_parser.map(|x| x.concat()),
        )
    ))(input)
    {
        Ok((_)) => Ok(()),
        Err(_) => Err(OscError::BadAddress("Invalid address pattern".to_string()))
    }
}<|endoftext|>


<commit_before>use std::sync::Arc;
use std::default::Default;
use graphics::{self, ImageSize, BackEnd};
use glium::{Display, Surface, Texture2d, Texture, Program, VertexBuffer,
            DrawParameters, BlendingFunction, LinearBlendingFactor};
use glium::index::{NoIndices, PrimitiveType};
use shader;

#[derive(Clone)]
pub struct DrawTexture {
    texture: Arc<Texture2d>,
}

impl DrawTexture {
    pub fn new(texture: Texture2d) -> DrawTexture {
        DrawTexture { texture: Arc::new(texture) }
    }
}

impl ImageSize for DrawTexture {
    fn get_size(&self) -> (u32, u32) {
        let ref tex = self.texture;
        (tex.get_width(), tex.get_height().unwrap())
    }
}

#[vertex_format]
#[derive(Copy, Clone)]
struct PlainVertex {
    position: [f32; 2],
}

#[vertex_format]
#[derive(Copy, Clone)]
struct TexturedVertex {
    position: [f32; 2],
    texcoord: [f32; 2],
}

pub struct GliumBackendSystem {
    plain_buffer: VertexBuffer<PlainVertex>,
    textured_buffer: VertexBuffer<TexturedVertex>,
    shader_texture: Program,
    shader_color: Program,
}

impl GliumBackendSystem {
    pub fn new(display: &Display) -> GliumBackendSystem {
        // FIXME: create empty buffers when glium supports them
        let plain_data = ::std::iter::repeat(PlainVertex { position: [0.0, 0.0] })
                                .take(graphics::BACK_END_MAX_VERTEX_COUNT).collect();
        let textured_data = ::std::iter::repeat(TexturedVertex { position: [0.0, 0.0], texcoord: [0.0, 0.0] })
                                .take(graphics::BACK_END_MAX_VERTEX_COUNT).collect();

        GliumBackendSystem {
            plain_buffer: VertexBuffer::new(display, plain_data),
            textured_buffer: VertexBuffer::new(display, textured_data),
            shader_texture: Program::from_source(display,
                            shader::VS_TEXTURED_120, shader::FS_TEXTURED_120, None)
                            .ok().expect("failed to initialize textured shader"),
            shader_color: Program::from_source(&display,
                            shader::VS_COLORED_120, shader::FS_COLORED_120, None)
                            .ok().expect("failed to initialize colored shader"),
        }
    }
}

pub struct GliumSurfaceBackEnd<'d, 's, S: 's> {
    system: &'d mut GliumBackendSystem,
    surface: &'s mut S,
    draw_texture: Option<DrawTexture>,
    draw_color: Option<[f32; 4]>,
}

impl<'d, 's, S> GliumSurfaceBackEnd<'d, 's, S> {
    pub fn new(system: &'d mut GliumBackendSystem, surface: &'s mut S)
               -> GliumSurfaceBackEnd<'d, 's, S> {
        GliumSurfaceBackEnd {
            system: system,
            surface: surface,
            draw_texture: None,
            draw_color: None,
        }
    }
}

/// Implemented by all graphics back-ends.
impl<'d, 's, S: Surface> BackEnd for GliumSurfaceBackEnd<'d, 's, S> {
    type Texture = DrawTexture;

    /// Clears background with a color.
    fn clear(&mut self, color: [f32; 4]) {
        let [r, g, b, a] = color;
        self.surface.clear_color(r, g, b, a);
    }

    /// Sets the texture.
    fn enable_texture(&mut self, texture: &DrawTexture) {
        self.draw_texture = Some(texture.clone());
    }

    /// Disables texture.
    fn disable_texture(&mut self) {
        self.draw_texture = None;
    }

    /// Sets the current color.
    fn color(&mut self, color: [f32; 4]) {
        self.draw_color = Some(color);
    }

    /// Renders list of 2d triangles.
    fn tri_list(&mut self, vertices: &[f32]) {
        let slice = self.system.plain_buffer.slice(0, vertices.len() / 2).unwrap();

        slice.write({
            (0..vertices.len() / 2)
                .map(|i| PlainVertex {
                    position: [vertices[2 * i], vertices[2 * i + 1]],
                })
                .collect()
        });

        self.surface.draw(
            slice,
            &NoIndices(PrimitiveType::TrianglesList),
            &self.system.shader_color,
            &uniform! { color: self.draw_color.unwrap_or([1., 1., 1., 1.]) },
            &DrawParameters {
                blending_function: Some(BlendingFunction::Addition {
                    source: LinearBlendingFactor::SourceAlpha,
                    destination: LinearBlendingFactor::OneMinusSourceAlpha,
                }),
                .. Default::default()
            },
        )
        .ok()
        .expect("failed to draw triangle list");
    }

    /// Renders list of 2d triangles.
    ///
    /// A texture coordinate is assigned per vertex.
    /// The texture coordinates refers to the current texture.
    fn tri_list_uv(&mut self, vertices: &[f32], texture_coords: &[f32]) {
        use std::cmp::min;

        let len = min(vertices.len(), texture_coords.len()) / 2;
        let slice = self.system.textured_buffer.slice(0, len).unwrap();

        slice.write({
            (0..len)
                .map(|i| TexturedVertex {
                    position: [vertices[2 * i], vertices[2 * i + 1]],
                    // FIXME: The `1.0 - ...` is because of a wrong convention
                    texcoord: [texture_coords[2 * i], 1.0 - texture_coords[2 * i + 1]],
                })
                .collect()
        });

        let texture = &*(self.draw_texture.as_ref().unwrap().texture);
        self.surface.draw(
            slice,
            &NoIndices(PrimitiveType::TrianglesList),
            &self.system.shader_texture,
            &uniform! {
                color: self.draw_color.unwrap_or([1., 1., 1., 1.]),
                s_texture: texture
            },
            &DrawParameters {
                blending_function: Some(BlendingFunction::Addition {
                    source: LinearBlendingFactor::SourceAlpha,
                    destination: LinearBlendingFactor::OneMinusSourceAlpha,
                }),
                .. Default::default()
            },
        )
        .ok()
        .expect("failed to draw triangle list");
    }
}
<commit_msg>Use two vertex buffers and swap between them<commit_after>use std::sync::Arc;
use std::default::Default;
use graphics::{self, ImageSize, BackEnd};
use glium::{Display, Surface, Texture2d, Texture, Program, VertexBuffer,
            DrawParameters, BlendingFunction, LinearBlendingFactor};
use glium::index::{NoIndices, PrimitiveType};
use shader;

#[derive(Clone)]
pub struct DrawTexture {
    texture: Arc<Texture2d>,
}

impl DrawTexture {
    pub fn new(texture: Texture2d) -> DrawTexture {
        DrawTexture { texture: Arc::new(texture) }
    }
}

impl ImageSize for DrawTexture {
    fn get_size(&self) -> (u32, u32) {
        let ref tex = self.texture;
        (tex.get_width(), tex.get_height().unwrap())
    }
}

#[vertex_format]
#[derive(Copy, Clone)]
struct PlainVertex {
    position: [f32; 2],
}

#[vertex_format]
#[derive(Copy, Clone)]
struct TexturedVertex {
    position: [f32; 2],
    texcoord: [f32; 2],
}

pub struct GliumBackendSystem {
    next_plain_buffer: u32,
    plain_buffer1: VertexBuffer<PlainVertex>,
    plain_buffer2: VertexBuffer<PlainVertex>,
    next_textured_buffer: u32,
    textured_buffer1: VertexBuffer<TexturedVertex>,
    textured_buffer2: VertexBuffer<TexturedVertex>,
    shader_texture: Program,
    shader_color: Program,
}

impl GliumBackendSystem {
    pub fn new(display: &Display) -> GliumBackendSystem {
        // FIXME: create empty buffers when glium supports them
        let plain_data = ::std::iter::repeat(PlainVertex { position: [0.0, 0.0] })
                                .take(graphics::BACK_END_MAX_VERTEX_COUNT).collect::<Vec<_>>();
        let textured_data = ::std::iter::repeat(TexturedVertex { position: [0.0, 0.0], texcoord: [0.0, 0.0] })
                                .take(graphics::BACK_END_MAX_VERTEX_COUNT).collect::<Vec<_>>();

        GliumBackendSystem {
            next_plain_buffer: 0,
            plain_buffer1: VertexBuffer::new(display, plain_data.clone()),
            plain_buffer2: VertexBuffer::new(display, plain_data),
            next_textured_buffer: 0,
            textured_buffer1: VertexBuffer::new(display, textured_data.clone()),
            textured_buffer2: VertexBuffer::new(display, textured_data),
            shader_texture: Program::from_source(display,
                            shader::VS_TEXTURED_120, shader::FS_TEXTURED_120, None)
                            .ok().expect("failed to initialize textured shader"),
            shader_color: Program::from_source(&display,
                            shader::VS_COLORED_120, shader::FS_COLORED_120, None)
                            .ok().expect("failed to initialize colored shader"),
        }
    }
}

pub struct GliumSurfaceBackEnd<'d, 's, S: 's> {
    system: &'d mut GliumBackendSystem,
    surface: &'s mut S,
    draw_texture: Option<DrawTexture>,
    draw_color: Option<[f32; 4]>,
}

impl<'d, 's, S> GliumSurfaceBackEnd<'d, 's, S> {
    pub fn new(system: &'d mut GliumBackendSystem, surface: &'s mut S)
               -> GliumSurfaceBackEnd<'d, 's, S> {
        GliumSurfaceBackEnd {
            system: system,
            surface: surface,
            draw_texture: None,
            draw_color: None,
        }
    }
}

/// Implemented by all graphics back-ends.
impl<'d, 's, S: Surface> BackEnd for GliumSurfaceBackEnd<'d, 's, S> {
    type Texture = DrawTexture;

    /// Clears background with a color.
    fn clear(&mut self, color: [f32; 4]) {
        let [r, g, b, a] = color;
        self.surface.clear_color(r, g, b, a);
    }

    /// Sets the texture.
    fn enable_texture(&mut self, texture: &DrawTexture) {
        self.draw_texture = Some(texture.clone());
    }

    /// Disables texture.
    fn disable_texture(&mut self) {
        self.draw_texture = None;
    }

    /// Sets the current color.
    fn color(&mut self, color: [f32; 4]) {
        self.draw_color = Some(color);
    }

    /// Renders list of 2d triangles.
    fn tri_list(&mut self, vertices: &[f32]) {
        let slice = match self.system.next_plain_buffer {
            0 => {
                self.system.next_plain_buffer = 1;
                self.system.plain_buffer1.slice(0, vertices.len() / 2).unwrap()
            },
            1 => {
                self.system.next_plain_buffer = 0;
                self.system.plain_buffer2.slice(0, vertices.len() / 2).unwrap()
            },
            _ => unreachable!()
        };

        slice.write({
            (0..vertices.len() / 2)
                .map(|i| PlainVertex {
                    position: [vertices[2 * i], vertices[2 * i + 1]],
                })
                .collect()
        });

        self.surface.draw(
            slice,
            &NoIndices(PrimitiveType::TrianglesList),
            &self.system.shader_color,
            &uniform! { color: self.draw_color.unwrap_or([1., 1., 1., 1.]) },
            &DrawParameters {
                blending_function: Some(BlendingFunction::Addition {
                    source: LinearBlendingFactor::SourceAlpha,
                    destination: LinearBlendingFactor::OneMinusSourceAlpha,
                }),
                .. Default::default()
            },
        )
        .ok()
        .expect("failed to draw triangle list");
    }

    /// Renders list of 2d triangles.
    ///
    /// A texture coordinate is assigned per vertex.
    /// The texture coordinates refers to the current texture.
    fn tri_list_uv(&mut self, vertices: &[f32], texture_coords: &[f32]) {
        use std::cmp::min;

        let len = min(vertices.len(), texture_coords.len()) / 2;

        let slice = match self.system.next_textured_buffer {
            0 => {
                self.system.next_textured_buffer = 1;
                self.system.textured_buffer1.slice(0, len).unwrap()
            },
            1 => {
                self.system.next_textured_buffer = 0;
                self.system.textured_buffer2.slice(0, len).unwrap()
            },
            _ => unreachable!()
        };

        slice.write({
            (0..len)
                .map(|i| TexturedVertex {
                    position: [vertices[2 * i], vertices[2 * i + 1]],
                    // FIXME: The `1.0 - ...` is because of a wrong convention
                    texcoord: [texture_coords[2 * i], 1.0 - texture_coords[2 * i + 1]],
                })
                .collect()
        });

        let texture = &*(self.draw_texture.as_ref().unwrap().texture);
        self.surface.draw(
            slice,
            &NoIndices(PrimitiveType::TrianglesList),
            &self.system.shader_texture,
            &uniform! {
                color: self.draw_color.unwrap_or([1., 1., 1., 1.]),
                s_texture: texture
            },
            &DrawParameters {
                blending_function: Some(BlendingFunction::Addition {
                    source: LinearBlendingFactor::SourceAlpha,
                    destination: LinearBlendingFactor::OneMinusSourceAlpha,
                }),
                .. Default::default()
            },
        )
        .ok()
        .expect("failed to draw triangle list");
    }
}
<|endoftext|>


<commit_before>use command::Command;
use vm::VM;

pub struct Program {
    instructions: Vec<Command>,
    instruction_pointer: Option<usize>,
    current_depth: u64,
    pub status: ProgramStatus,
}

#[derive(Clone, Copy, PartialEq, Debug)]
pub enum ProgramStatus {
    Normal,
    Seeking(u64),
}

impl Program {
    pub fn new() -> Program {
        Program {
            instructions: Vec::new(),
            instruction_pointer: None,
            current_depth: 0,
            status: ProgramStatus::Normal,
        }
    }

    pub fn append(&mut self, instructions: &[Command]) {
        self.instructions.extend(instructions.iter().cloned());
        if self.instruction_pointer.is_none() {
            self.instruction_pointer = Some(0);
        }
    }

    pub fn execute(&mut self, vm: &mut VM) {
        match (self.instruction_pointer, self.status) {
            (Some(mut index), ProgramStatus::Normal) => {
                while index < self.instructions.len() {
                    match self.instructions[index] {
                        Command::JumpForward => self.handle_jump_forward(vm, &mut index),
                        Command::JumpBackward => self.handle_jump_backward(vm, &mut index),
                        command => {
                            vm.apply(command);
                            index += 1;
                        }
                    }
                }
                self.instruction_pointer = Some(index);
            }

            (Some(mut index), ProgramStatus::Seeking(_)) => {
                self.handle_jump_forward(vm, &mut index);
                self.execute(vm);
            }

            _ => {}
        }
    }

    fn handle_jump_backward(&mut self, vm: &VM, index_ref: &mut usize) {
        match vm.cells[vm.data_pointer] {
            0 => {
                self.current_depth -= 1;
                *index_ref += 1;
            }

            _ => {
                let goal_depth = self.current_depth;
                for index in (0..*index_ref).rev() {
                    match self.instructions[index] {
                        Command::JumpBackward => self.current_depth += 1,
                        Command::JumpForward => {
                            if self.current_depth == goal_depth {
                                *index_ref = index + 1;
                                return;
                            }
                            self.current_depth -= 1;
                        }
                        _ => {}
                    }
                }
                panic!("No starting brace found!");
            }
        }
    }

    fn seek_forward(&mut self, index_ref: &mut usize) {
        if let ProgramStatus::Seeking(goal_depth) = self.status {
            for index in (*index_ref)..self.instructions.len() {
                match self.instructions[index] {
                    Command::JumpForward => self.current_depth += 1,
                    Command::JumpBackward => self.current_depth -= 1,
                    _ => {}
                }
                if self.current_depth == goal_depth {
                    *index_ref = index + 1;
                    self.status = ProgramStatus::Normal;
                    return;
                }
            }

            *index_ref = self.instructions.len();
            self.status = ProgramStatus::Seeking(goal_depth);
        }
    }

    fn handle_jump_forward(&mut self, vm: &VM, index: &mut usize) {
        match (vm.cells[vm.data_pointer], self.status) {
            (0, ProgramStatus::Normal) => {
                self.status = ProgramStatus::Seeking(self.current_depth);
                self.seek_forward(index);
            }

            (_, ProgramStatus::Normal) => {
                *index += 1;
                self.current_depth += 1;
                self.status = ProgramStatus::Normal;
            }

            (_, ProgramStatus::Seeking(_)) => self.seek_forward(index),

        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    pub fn test_seek_forward_normal() {
        let mut program = Program {
            instruction_pointer: None,
            current_depth: 0,
            status: ProgramStatus::Seeking(0),
            instructions: vec![
                Command::JumpForward,
                Command::JumpForward,
                Command::JumpBackward,
                Command::JumpBackward,
            ],
        };
        let mut index = 0;
        program.seek_forward(&mut index);
        assert_eq!(index, 4);
        assert_eq!(program.status, ProgramStatus::Normal);
    }

    #[test]
    pub fn test_seek_forward_seeking() {
        let mut index = 0;
        let mut program = Program {
            instruction_pointer: None,
            current_depth: 0,
            status: ProgramStatus::Seeking(0),
            instructions: vec![
                Command::JumpForward,
                Command::JumpForward,
                Command::JumpBackward,
            ],
        };
        program.seek_forward(&mut index);
        assert_eq!(index, 3);
        assert_eq!(program.status, ProgramStatus::Seeking(0));
    }
}
<commit_msg>Limit variable scope<commit_after>use command::Command;
use vm::VM;

pub struct Program {
    instructions: Vec<Command>,
    instruction_pointer: Option<usize>,
    current_depth: u64,
    pub status: ProgramStatus,
}

#[derive(Clone, Copy, PartialEq, Debug)]
pub enum ProgramStatus {
    Normal,
    Seeking(u64),
}

impl Program {
    pub fn new() -> Program {
        Program {
            instructions: Vec::new(),
            instruction_pointer: None,
            current_depth: 0,
            status: ProgramStatus::Normal,
        }
    }

    pub fn append(&mut self, instructions: &[Command]) {
        self.instructions.extend(instructions.iter().cloned());
        if self.instruction_pointer.is_none() {
            self.instruction_pointer = Some(0);
        }
    }

    pub fn execute(&mut self, vm: &mut VM) {
        match (self.instruction_pointer, self.status) {
            (Some(mut index), ProgramStatus::Normal) => {
                while index < self.instructions.len() {
                    match self.instructions[index] {
                        Command::JumpForward => self.handle_jump_forward(vm, &mut index),
                        Command::JumpBackward => self.handle_jump_backward(vm, &mut index),
                        command => {
                            vm.apply(command);
                            index += 1;
                        }
                    }
                }
                self.instruction_pointer = Some(index);
            }

            (Some(mut index), ProgramStatus::Seeking(_)) => {
                self.handle_jump_forward(vm, &mut index);
                self.execute(vm);
            }

            _ => {}
        }
    }

    fn handle_jump_backward(&mut self, vm: &VM, index_ref: &mut usize) {
        match vm.cells[vm.data_pointer] {
            0 => {
                self.current_depth -= 1;
                *index_ref += 1;
            }

            _ => {
                let goal_depth = self.current_depth;
                for index in (0..*index_ref).rev() {
                    match self.instructions[index] {
                        Command::JumpBackward => self.current_depth += 1,
                        Command::JumpForward => {
                            if self.current_depth == goal_depth {
                                *index_ref = index + 1;
                                return;
                            }
                            self.current_depth -= 1;
                        }
                        _ => {}
                    }
                }
                panic!("No starting brace found!");
            }
        }
    }

    fn seek_forward(&mut self, index_ref: &mut usize) {
        if let ProgramStatus::Seeking(goal_depth) = self.status {
            for index in (*index_ref)..self.instructions.len() {
                match self.instructions[index] {
                    Command::JumpForward => self.current_depth += 1,
                    Command::JumpBackward => self.current_depth -= 1,
                    _ => {}
                }
                if self.current_depth == goal_depth {
                    *index_ref = index + 1;
                    self.status = ProgramStatus::Normal;
                    return;
                }
            }

            *index_ref = self.instructions.len();
            self.status = ProgramStatus::Seeking(goal_depth);
        }
    }

    fn handle_jump_forward(&mut self, vm: &VM, index: &mut usize) {
        match (vm.cells[vm.data_pointer], self.status) {
            (0, ProgramStatus::Normal) => {
                self.status = ProgramStatus::Seeking(self.current_depth);
                self.seek_forward(index);
            }

            (_, ProgramStatus::Normal) => {
                *index += 1;
                self.current_depth += 1;
                self.status = ProgramStatus::Normal;
            }

            (_, ProgramStatus::Seeking(_)) => self.seek_forward(index),

        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    pub fn test_seek_forward_normal() {
        let mut program = Program {
            instruction_pointer: None,
            current_depth: 0,
            status: ProgramStatus::Seeking(0),
            instructions: vec![
                Command::JumpForward,
                Command::JumpForward,
                Command::JumpBackward,
                Command::JumpBackward,
            ],
        };
        let mut index = 0;
        program.seek_forward(&mut index);
        assert_eq!(index, 4);
        assert_eq!(program.status, ProgramStatus::Normal);
    }

    #[test]
    pub fn test_seek_forward_seeking() {
        let mut program = Program {
            instruction_pointer: None,
            current_depth: 0,
            status: ProgramStatus::Seeking(0),
            instructions: vec![
                Command::JumpForward,
                Command::JumpForward,
                Command::JumpBackward,
            ],
        };
        let mut index = 0;
        program.seek_forward(&mut index);
        assert_eq!(index, 3);
        assert_eq!(program.status, ProgramStatus::Seeking(0));
    }
}
<|endoftext|>


<commit_before>/// Generates a variation of the suffix array for fast codepoint name searching
/// by substring.
///
/// https://en.wikipedia.org/wiki/Suffix_array (note: Wikipedia uses 1-based
/// indexing but here we will use 0-based for implementation parity)
///
/// Suffix arrays provide fast substring lookup into a string by allowing one
/// to binary search for any suffix in said string, however for our purposes
/// we need to search multiple strings (each Unicode character name), matching
/// all codepoints whose names contain said substring. To achieve this we
/// concatenate all the names together, storing the codepoint alongside the
/// indexes.
///
/// For example the imaginary codepoints "one" as \x01 and "five" as \x05 would
/// be joined to form S = "one$five$". '$' is a sentinel character that is
/// lexicographically smaller than all other characters, in the implementation
/// a newline character is used to make the generated file look nicer.
///
/// Because no pattern contains the sentinel, the suffix "ne$five$" is
/// equivalent to "ne$", we also omit suffixes that begin with a sentinel, e.g.
/// "$five$" (i = 3) or "$" (i = 8) to save some bytes.
///
/// Starting with the suffixes of S
///
/// | Suffix | i | c |
/// | ------ | - | - |
/// | one$   | 0 | 1 |
/// | ne$    | 1 | 1 |
/// | e$     | 2 | 1 |
/// | five$  | 4 | 5 |
/// | ive$   | 5 | 5 |
/// | ve$    | 6 | 5 |
/// | e$     | 7 | 5 |
///
/// They are then sorted lexicographically by suffix to become
///
/// | Suffix | i | c |
/// | ------ | - | - |
/// | e$     | 2 | 1 |
/// | e$     | 7 | 5 |
/// | five$  | 4 | 5 |
/// | ive$   | 5 | 5 |
/// | ne$    | 1 | 1 |
/// | one$   | 0 | 1 |
/// | ve$    | 6 | 5 |
///
/// Since we don't need to know where in the combined string our suffix is, only
/// which codepoint is corresponds to we can join the entries for "e$", losing
/// the information that there is a suffix at index 7. This costs a few bytes
/// per entry since the codepoint array is now an array of arrays, however since
/// there are around 18x fewer entries in total for this data set the trade-off
/// is well worth it.
///
/// After removing duplicates and the suffixes the remaining data is:
///
/// - The combined string S = "one$five$",
/// - The suffix array A,
/// - The corresponding codepoints C
///
/// | i | A[i] | C[i] |
/// | - | ---- | ---- |
/// | 0 |    2 | 1, 5 |
/// | 1 |    4 |    5 |
/// | 2 |    5 |    5 |
/// | 3 |    1 |    1 |
/// | 4 |    0 |    1 |
/// | 5 |    6 |    5 |
///
/// This is the final table that we will use to search for name matches. The
/// suffix for each entry at i can be enumerated by iterating through the string
/// S beginning at index A[i].
///
/// | i | S[A[i]..] | A[i] | C[i] |
/// | - | -------   | ---- | ---- |
/// | 0 | e$five$   |    2 | 1, 5 |
/// | 1 | five$     |    4 |    5 |
/// | 2 | ive$      |    5 |    5 |
/// | 3 | ne$five$  |    1 |    1 |
/// | 4 | one$five$ |    0 |    1 |
/// | 5 | ve$       |    6 |    5 |
///
/// To find all the matches we have to find all the suffixes that begin with the
/// input pattern. Since the table is sorted these suffixes are all contiguous,
/// to locate them we perform two binary searches. One search to find the first
/// matching suffix and for the last.
///
/// https://en.wikipedia.org/wiki/Suffix_array#Applications
///
/// With the range of suffixes found the list of codepoints can be enumerated
/// for the interval C[first..last].
///
/// # Extra
///
/// Not shown is a further optimisation where each Unicode character name is
/// split into individual words, e.g. "DIGIT ONE" adds "DIGIT$" and "ONE$" to
/// the table separately. This allows us to deduplicate the many occurrences of
/// words such as "LETTER" that appear in the middle of names. This reduction
/// makes both the tables A and C smaller as well as the combined string S,
/// drastically reducing the total size requirement.
///
/// Additionally a small extra saving can be had by removing duplicate suffixes
/// from the same codepoint, for example in "LEFT SQUARE BRACKET" when split
/// into words "LEFT$", "SQUARE$" and "BRACKET$" the suffix "T$" appears twice.
/// Only one entry is needed to discover the codepoint contains the suffix "T$",
/// so the other is removed.
///
/// This means that multiword searches will no longer work directly since no
/// suffixes contain spaces. Instead we also split the input pattern into words
/// and search individually once for each word. Each word gives us a set of
/// codepoints, the final result in the union of all word subresults.
///
/// For example "LATIN LETTER" is split into patterns "LATIN" and "LETTER",
/// the final result is codepoints that match both "LATIN" and "LETTER". This
/// means that word order of the input pattern no longer has an effect on the
/// result, allowing "LATIN LETTER" to match "LATIN SMALL LETTER A".

extern crate ucd_raw;

use std::collections::HashMap;
use std::fs::create_dir_all;
use std::fs::File;
use std::io::BufWriter;
use std::io::Write;
use std::time::Instant;
use ucd_raw::Character;
use ucd_raw::CHARACTERS;

#[derive(Debug)]
struct Entry {
    index: usize,
    codepoints: Vec<u32>,
}

#[derive(Debug)]
struct TempSuffix<'a> {
    suffix: &'a str,
    entry: Entry,
}

fn char_iter() -> impl Iterator<Item = &'static Character> {
    CHARACTERS
        .iter()
        .filter(|character| match character.codepoint {
            0x03400...0x04DB5 => false,
            0x04E00...0x09FEA => false,
            0x20000...0x2A6D6 => false,
            0x2A700...0x2B734 => false,
            0x2B740...0x2B81D => false,
            0x2B820...0x2CEA1 => false,
            0x2CEB0...0x2EBE0 => false,
            0x17000...0x187EC => false,
            0x1B170...0x1B2FB => false,
            0x0F900...0x0FA6D => false,
            0x0FA70...0x0FAD9 => false,
            0x2F800...0x2FA1D => false,
            _ => true,
        })
}

fn suffixes<'a>(string: &'a str) -> impl Iterator<Item = (usize, &'a str)> {
    (0..string.len()).map(move |i| &string[i..]).enumerate()
}

fn main() {
    println!("indexing {} codepoints...", char_iter().count());

    let start = Instant::now();

    let mut combined = String::new();
    let mut visited_words = HashMap::new();
    let mut temp_suffixes = Vec::new();

    create_dir_all("src/generated").unwrap();
    let out = File::create("src/generated/generated.rs").unwrap();
    let mut out = BufWriter::new(out);

    for character in char_iter() {
        for word in character.name.split_whitespace() {
            let mut start;

            if visited_words.contains_key(word) {
                start = visited_words[word];
            } else {
                start = combined.len();

                visited_words.insert(word, start);
                combined.push_str(word);
                combined.push('\n');
            }

            for (offset, suffix) in suffixes(word) {
                temp_suffixes.push(TempSuffix {
                    suffix,
                    entry: Entry {
                        index: start + offset,
                        codepoints: vec![character.codepoint],
                    },
                });
            }
        }
    }

    temp_suffixes.sort_unstable_by_key(|temp_suffix| temp_suffix.suffix);

    println!("length before dedup: {}", temp_suffixes.len());

    temp_suffixes.dedup_by(|a, b| {
        let equal = a.suffix == b.suffix;

        if equal {
            b.entry.codepoints.extend_from_slice(&a.entry.codepoints);
        }

        equal
    });

    for suffix in &mut temp_suffixes {
        let codepoints = &mut suffix.entry.codepoints;

        codepoints.sort_unstable();
        codepoints.dedup();
    }

    println!("after: {}", temp_suffixes.len());

    writeln!(out, "// Auto-generated by `src/bin/gen`\n").unwrap();
    writeln!(out, "use table::Entry;").unwrap();

    writeln!(
        out,
        "pub const ENTRIES: &[Entry] = &{:#?};",
        temp_suffixes
            .into_iter()
            .map(|temp_suffix| temp_suffix.entry)
            .collect::<Vec<_>>()
    ).unwrap();

    writeln!(out, "pub const COMBINED: &[u8] = b\"{}\";", combined).unwrap();

    println!("Generated in {:?}", start.elapsed());
}
<commit_msg>Clarify same codepoint deduplication<commit_after>/// Generates a variation of the suffix array for fast codepoint name searching
/// by substring.
///
/// https://en.wikipedia.org/wiki/Suffix_array (note: Wikipedia uses 1-based
/// indexing but here we will use 0-based for implementation parity)
///
/// Suffix arrays provide fast substring lookup into a string by allowing one
/// to binary search for any suffix in said string, however for our purposes
/// we need to search multiple strings (each Unicode character name), matching
/// all codepoints whose names contain said substring. To achieve this we
/// concatenate all the names together, storing the codepoint alongside the
/// indexes.
///
/// For example the imaginary codepoints "one" as \x01 and "five" as \x05 would
/// be joined to form S = "one$five$". '$' is a sentinel character that is
/// lexicographically smaller than all other characters, in the implementation
/// a newline character is used to make the generated file look nicer.
///
/// Because no pattern contains the sentinel, the suffix "ne$five$" is
/// equivalent to "ne$", we also omit suffixes that begin with a sentinel, e.g.
/// "$five$" (i = 3) or "$" (i = 8) to save some bytes.
///
/// Starting with the suffixes of S
///
/// | Suffix | i | c |
/// | ------ | - | - |
/// | one$   | 0 | 1 |
/// | ne$    | 1 | 1 |
/// | e$     | 2 | 1 |
/// | five$  | 4 | 5 |
/// | ive$   | 5 | 5 |
/// | ve$    | 6 | 5 |
/// | e$     | 7 | 5 |
///
/// They are then sorted lexicographically by suffix to become
///
/// | Suffix | i | c |
/// | ------ | - | - |
/// | e$     | 2 | 1 |
/// | e$     | 7 | 5 |
/// | five$  | 4 | 5 |
/// | ive$   | 5 | 5 |
/// | ne$    | 1 | 1 |
/// | one$   | 0 | 1 |
/// | ve$    | 6 | 5 |
///
/// Since we don't need to know where in the combined string our suffix is, only
/// which codepoint it corresponds to we can join the entries for "e$", losing
/// the information that there is a suffix at index 7. This costs a few bytes
/// per entry since the codepoint array is now an array of arrays, however since
/// there are around 18x fewer entries in total for this dataset the trade-off
/// is well worth it.
///
/// After removing duplicates and the temporary suffixes the remaining data is:
///
/// - The combined string S = "one$five$",
/// - The suffix array A,
/// - The corresponding codepoints C
///
/// | i | A[i] | C[i] |
/// | - | ---- | ---- |
/// | 0 |    2 | 1, 5 |
/// | 1 |    4 |    5 |
/// | 2 |    5 |    5 |
/// | 3 |    1 |    1 |
/// | 4 |    0 |    1 |
/// | 5 |    6 |    5 |
///
/// This is the final table that we will use to search for name matches. The
/// suffix for each entry at i can be enumerated by iterating through the string
/// S beginning at index A[i].
///
/// | i | S[A[i]..] | A[i] | C[i] |
/// | - | -------   | ---- | ---- |
/// | 0 | e$five$   |    2 | 1, 5 |
/// | 1 | five$     |    4 |    5 |
/// | 2 | ive$      |    5 |    5 |
/// | 3 | ne$five$  |    1 |    1 |
/// | 4 | one$five$ |    0 |    1 |
/// | 5 | ve$       |    6 |    5 |
///
/// To find all the matches we have to find all the suffixes that begin with the
/// input pattern. Since the table is sorted these suffixes are all contiguous,
/// to locate them we perform two binary searches. One search to find the first
/// matching suffix and for the last.
///
/// https://en.wikipedia.org/wiki/Suffix_array#Applications
///
/// With the range of suffixes found the list of codepoints can be enumerated
/// for the interval C[first..last].
///
/// # Extra
///
/// Not shown is a further optimisation where each Unicode character name is
/// split into individual words, e.g. "DIGIT ONE" adds "DIGIT$" and "ONE$" to
/// the table separately. This allows us to deduplicate the many occurrences of
/// words such as "LETTER" that appear in the middle of names. This reduction
/// makes both the tables A and C smaller as well as the combined string S,
/// drastically reducing the space requirement.
///
/// This means that multiword searches will no longer work directly since no
/// suffixes contain spaces. Instead we also split the input pattern into words
/// and search individually once for each word. Each word gives us a set of
/// codepoints, the final result in the union of all word subresults.
///
/// For example "LATIN LETTER" is split into patterns "LATIN" and "LETTER",
/// the final result is codepoints that match both "LATIN" and "LETTER". This
/// means that word order of the input pattern no longer has an effect on the
/// result, allowing "LATIN LETTER" to match "LATIN SMALL LETTER A".
///
/// Additionally, a small extra saving can be had by removing duplicate
/// codepoints for a single suffix.  For example in "LEFT SQUARE BRACKET" when
/// split into words "LEFT$", "SQUARE$" and "BRACKET$" the suffix "T$" appears
/// twice. The "T$" suffix is already deduplicated however the codepoint for
/// LEFT SQUARE BRACKET will appear twice in C[i], this is simply removed.

extern crate ucd_raw;

use std::collections::HashMap;
use std::fs::create_dir_all;
use std::fs::File;
use std::io::BufWriter;
use std::io::Write;
use std::time::Instant;
use ucd_raw::Character;
use ucd_raw::CHARACTERS;

#[derive(Debug)]
struct Entry {
    index: usize,
    codepoints: Vec<u32>,
}

#[derive(Debug)]
struct TempSuffix<'a> {
    suffix: &'a str,
    entry: Entry,
}

fn char_iter() -> impl Iterator<Item = &'static Character> {
    CHARACTERS
        .iter()
        .filter(|character| match character.codepoint {
            0x03400...0x04DB5 => false,
            0x04E00...0x09FEA => false,
            0x20000...0x2A6D6 => false,
            0x2A700...0x2B734 => false,
            0x2B740...0x2B81D => false,
            0x2B820...0x2CEA1 => false,
            0x2CEB0...0x2EBE0 => false,
            0x17000...0x187EC => false,
            0x1B170...0x1B2FB => false,
            0x0F900...0x0FA6D => false,
            0x0FA70...0x0FAD9 => false,
            0x2F800...0x2FA1D => false,
            _ => true,
        })
}

fn suffixes<'a>(string: &'a str) -> impl Iterator<Item = (usize, &'a str)> {
    (0..string.len()).map(move |i| &string[i..]).enumerate()
}

fn main() {
    println!("indexing {} codepoints...", char_iter().count());

    let start = Instant::now();

    let mut combined = String::new();
    let mut visited_words = HashMap::new();
    let mut temp_suffixes = Vec::new();

    create_dir_all("src/generated").unwrap();
    let out = File::create("src/generated/generated.rs").unwrap();
    let mut out = BufWriter::new(out);

    for character in char_iter() {
        for word in character.name.split_whitespace() {
            let mut start;

            if visited_words.contains_key(word) {
                start = visited_words[word];
            } else {
                start = combined.len();

                visited_words.insert(word, start);
                combined.push_str(word);
                combined.push('\n');
            }

            for (offset, suffix) in suffixes(word) {
                temp_suffixes.push(TempSuffix {
                    suffix,
                    entry: Entry {
                        index: start + offset,
                        codepoints: vec![character.codepoint],
                    },
                });
            }
        }
    }

    temp_suffixes.sort_unstable_by_key(|temp_suffix| temp_suffix.suffix);

    println!("length before dedup: {}", temp_suffixes.len());

    temp_suffixes.dedup_by(|a, b| {
        let equal = a.suffix == b.suffix;

        if equal {
            b.entry.codepoints.extend_from_slice(&a.entry.codepoints);
        }

        equal
    });

    for suffix in &mut temp_suffixes {
        let codepoints = &mut suffix.entry.codepoints;

        codepoints.sort_unstable();
        codepoints.dedup();
    }

    println!("after: {}", temp_suffixes.len());

    writeln!(out, "// Auto-generated by `src/bin/gen`\n").unwrap();
    writeln!(out, "use table::Entry;").unwrap();

    writeln!(
        out,
        "pub const ENTRIES: &[Entry] = &{:#?};",
        temp_suffixes
            .into_iter()
            .map(|temp_suffix| temp_suffix.entry)
            .collect::<Vec<_>>()
    ).unwrap();

    writeln!(out, "pub const COMBINED: &[u8] = b\"{}\";", combined).unwrap();

    println!("Generated in {:?}", start.elapsed());
}
<|endoftext|>