<commit_before>use std::fmt;
use std::marker::PhantomData;
use gfx::{self, Resources, ProgramError};
use gfx::attrib::Floater;
use gfx::traits::{IntoCanvas, Factory, FactoryExt, Stream};
use gfx::shade::TextureParam;
use gfx::device::tex::TextureError;
use gfx::extra::canvas::Canvas;
use gfx::batch::OwnedBatch;
use gfx::batch::Error as BatchError;
use gfx_device_gl as dgl;
use gfx_window_glutin as gfxw;
use glutin::{CreationError, WindowBuilder};
use glutin::Event::{Closed, KeyboardInput};
use glutin::ElementState::Pressed;
use glutin::VirtualKeyCode as Key;
use gfx_text;
use ::ivf;
use ::vpx;

#[derive(Debug)]
pub enum Error {
    GlutinCreationError(CreationError),
    GfxProgramError(ProgramError),
    GfxTextureError(TextureError),
    GfxBatchError(BatchError),
    TextError(gfx_text::Error),
}

impl From<CreationError> for Error {
    fn from(e: CreationError) -> Error { Error::GlutinCreationError(e) }
}

impl From<ProgramError> for Error {
    fn from(e: ProgramError) -> Error { Error::GfxProgramError(e) }
}

impl From<TextureError> for Error {
    fn from(e: TextureError) -> Error { Error::GfxTextureError(e) }
}

impl From<BatchError> for Error {
    fn from(e: BatchError) -> Error { Error::GfxBatchError(e) }
}

impl From<gfx_text::Error> for Error {
    fn from(e: gfx_text::Error) -> Error { Error::TextError(e) }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let descr = match *self {
            Error::GlutinCreationError(ref err) => format!("{}", err),
            Error::GfxProgramError(ref err) => format!("{:?}", err),
            Error::GfxTextureError(ref err) => format!("{:?}", err),
            Error::GfxBatchError(ref err) => format!("{:?}", err),
            Error::TextError(ref err) => format!("{:?}", err),
        };
        f.write_str(&descr)
    }
}

gfx_vertex!( Vertex {
    a_Pos@ pos: [Floater<i8>; 2],
    a_TexCoord@ tex: [Floater<u8>; 2],
});

impl Vertex {
    fn new(pos: [i8; 2], tex: [u8; 2]) -> Self {
        Vertex {
            pos: Floater::cast2(pos),
            tex: Floater::cast2(tex),
        }
    }
}

gfx_parameters!( ShaderParams/ParamsLink {
    t_Color@ color: TextureParam<R>,
});

static VERTEX_SRC: &'static [u8] = b"
    #version 120

    attribute vec4 a_Pos;
    attribute vec2 a_TexCoord;
    varying vec2 v_TexCoord;

    void main() {
        v_TexCoord = a_TexCoord;
        gl_Position = a_Pos;
    }
";

static FRAGMENT_SRC: &'static [u8] = b"
    #version 120

    varying vec2 v_TexCoord;
    uniform sampler2D t_Color;

    void main() {
        gl_FragColor = texture2D(t_Color, v_TexCoord);
    }
";

const BACKGROUND: gfx::ClearData = gfx::ClearData {
    color: [0.0, 0.0, 0.0, 1.0],
    depth: 1.0,
    stencil: 0,
};
const TEXT_COLOR: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
const TEXT_HEIGHT: i32 = 16;

type CanvasT = Canvas<gfxw::Output<dgl::Resources>, dgl::Device, dgl::Factory>;
type BatchT = OwnedBatch<ShaderParams<dgl::Resources>>;
type TextRendererT = gfx_text::Renderer<dgl::Resources>;

pub struct Gui {
    reader: ivf::Reader,
    decoder: vpx::Decoder,
    viewport_width: u16,
    viewport_height: u16,
    canvas: CanvasT,
    batch: BatchT,
    text: TextRendererT,
}

pub fn init(reader: ivf::Reader, decoder: vpx::Decoder) -> Result<Gui, Error> {
    let viewport_width = reader.get_width();
    let viewport_height = reader.get_height();
    let mut canvas = {
        // TODO(Kagami): Fullscreen.
        let window = try!(WindowBuilder::new()
            .with_dimensions(viewport_width as u32, viewport_height as u32)
            // Use simple initial title to allow to match the window in tiling
            // window managers.
            .with_title(format!("vpxview"))
            .build());
        gfxw::init(window).into_canvas()
    };
    let vertex_data = [
        // 1
        // |\
        // | \
        // 2--3
        Vertex::new([-1,  1], [0, 0]),
        Vertex::new([-1, -1], [0, 1]),
        Vertex::new([ 1, -1], [1, 1]),
        // 1--3
        //  \ |
        //   \|
        //    2
        Vertex::new([-1,  1], [0, 0]),
        Vertex::new([ 1, -1], [1, 1]),
        Vertex::new([ 1,  1], [1, 0]),
    ];
    let batch = {
        let mesh = canvas.factory.create_mesh(&vertex_data);
        let program = try!(canvas.factory.link_program(VERTEX_SRC, FRAGMENT_SRC));
        let texture = try!(canvas.factory.create_texture_rgba8(
            reader.get_width(),
            reader.get_height()));
        let param = ShaderParams {color: (texture, None), _r: PhantomData};
        try!(OwnedBatch::new(mesh, program, param))
    };
    let text = try!(gfx_text::new(&mut canvas.factory).build());
    Ok(Gui {
        reader: reader,
        decoder: decoder,
        viewport_width: viewport_width,
        viewport_height: viewport_height,
        canvas: canvas,
        batch: batch,
        text: text,
    })
}

impl Gui {
    pub fn run(&mut self) {
        self.next_video_frame();
        loop {
            // Skip all pending events except the first because in some cases frame
            // decoding may take too long so interface will be brozen because of
            // big events queue.
            let maybe_event = {
                let mut iter = self.canvas.output.window.poll_events();
                let ev = iter.next();
                iter.count();  // Consume entire iterator
                ev
            };
            match maybe_event {
                Some(Closed) => break,
                Some(KeyboardInput(Pressed, _, Some(Key::Escape))) => break,
                Some(KeyboardInput(Pressed, _, Some(Key::Q))) => break,
                Some(KeyboardInput(Pressed, _, Some(Key::Left))) => {
                    // TODO(Kagami).
                },
                Some(KeyboardInput(Pressed, _, Some(Key::Right))) => {
                    self.next_video_frame();
                },
                _ => {},
            }
            self.canvas.clear(BACKGROUND);
            let draw_result = self.canvas.draw(&self.batch);
            try_print!(draw_result, "Error occured while drawing the frame: {:?}");
            self.render_hud();
            self.canvas.present();
        }
    }

    /// Read next IVF frame, decode VPx frame if possible and update the
    /// texture.
    fn next_video_frame(&mut self) {
        let maybe_frame = self.reader.next();
        self.update_title();
        let ivf_frame = maybe_print!(maybe_frame, "End of file");
        match self.decoder.decode_many(&ivf_frame) {
            Ok(mut iter) => {
                let image = maybe_print!(iter.next(), "No VPx frames in this IVF frame");
                // TODO(Kagami): IVF frame may consist of several VPx frames, we
                // correctly display only 1 IVF <-> 1 VPx case as for now.
                let remaining = iter.count();
                if remaining != 0 {
                    printerr!("Skipping {} other VPx frames", remaining);
                }
                // TODO(Kagami): Dimensions of decoded VPx image can vary from
                // frame to frame, we can adjust texture size accordingly.
                assert_eq!(image.get_display_width(), self.viewport_width);
                assert_eq!(image.get_display_height(), self.viewport_height);
                let texture = &self.batch.param.color.0;
                let update_result = self.canvas.factory.update_texture_raw(
                    texture,
                    &texture.get_info().to_image_info(),
                    &image.get_rgba8(),
                    None);
                try_print!(update_result, "Error occured while updating texture: {:?}");
            },
            Err(err) => {
                printerr!("Cannot decode IVF frame: {}", err);
            },
        };
    }

    fn get_frame_count(&self) -> String {
        self.reader.get_frame_count().map_or_else(|| "?".to_string(), |n| n.to_string())
    }

    fn update_title(&self) {
        let title = format!("vpxview - {} - {}/{}",
                            self.reader.get_filename(),
                            self.reader.get_frame_pos(),
                            self.get_frame_count());
        self.canvas.output.window.set_title(&title);
    }

    /// Draw given lines sequentially from top to bottom.
    fn draw_lines(&mut self, start_pos: [i32; 2], lines: &[String]) {
        let (x, mut y) = (start_pos[0], start_pos[1]);
        for line in lines {
            self.text.draw(line, [x, y], TEXT_COLOR);
            y += TEXT_HEIGHT;
        }
    }

    /// Render some VPx frame details on canvas.
    fn render_hud(&mut self) {
        let lines = [
            format!("Filename: {}", self.reader.get_filename()),
            format!("Frame: {}/{}", self.reader.get_frame_pos(), self.get_frame_count()),
        ];
        self.draw_lines([10, 10], &lines);
        let draw_result = self.text.draw_end(&mut self.canvas);
        try_print!(draw_result, "Error occured why drawing the text: {:?}");
    }
}
<commit_msg>Do not use attrib::Floater<commit_after>use std::fmt;
use std::marker::PhantomData;
use gfx::{self, Resources, ProgramError};
use gfx::traits::{IntoCanvas, Factory, FactoryExt, Stream};
use gfx::shade::TextureParam;
use gfx::device::tex::TextureError;
use gfx::extra::canvas::Canvas;
use gfx::batch::OwnedBatch;
use gfx::batch::Error as BatchError;
use gfx_device_gl as dgl;
use gfx_window_glutin as gfxw;
use glutin::{CreationError, WindowBuilder};
use glutin::Event::{Closed, KeyboardInput};
use glutin::ElementState::Pressed;
use glutin::VirtualKeyCode as Key;
use gfx_text;
use ::ivf;
use ::vpx;

#[derive(Debug)]
pub enum Error {
    GlutinCreationError(CreationError),
    GfxProgramError(ProgramError),
    GfxTextureError(TextureError),
    GfxBatchError(BatchError),
    TextError(gfx_text::Error),
}

impl From<CreationError> for Error {
    fn from(e: CreationError) -> Error { Error::GlutinCreationError(e) }
}

impl From<ProgramError> for Error {
    fn from(e: ProgramError) -> Error { Error::GfxProgramError(e) }
}

impl From<TextureError> for Error {
    fn from(e: TextureError) -> Error { Error::GfxTextureError(e) }
}

impl From<BatchError> for Error {
    fn from(e: BatchError) -> Error { Error::GfxBatchError(e) }
}

impl From<gfx_text::Error> for Error {
    fn from(e: gfx_text::Error) -> Error { Error::TextError(e) }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let descr = match *self {
            Error::GlutinCreationError(ref err) => format!("{}", err),
            Error::GfxProgramError(ref err) => format!("{:?}", err),
            Error::GfxTextureError(ref err) => format!("{:?}", err),
            Error::GfxBatchError(ref err) => format!("{:?}", err),
            Error::TextError(ref err) => format!("{:?}", err),
        };
        f.write_str(&descr)
    }
}

gfx_vertex!( Vertex {
    a_Pos@ pos: [f32; 2],
    a_TexCoord@ tex: [f32; 2],
});

gfx_parameters!( ShaderParams/ParamsLink {
    t_Color@ color: TextureParam<R>,
});

static VERTEX_SRC: &'static [u8] = b"
    #version 120

    attribute vec4 a_Pos;
    attribute vec2 a_TexCoord;
    varying vec2 v_TexCoord;

    void main() {
        v_TexCoord = a_TexCoord;
        gl_Position = a_Pos;
    }
";

static FRAGMENT_SRC: &'static [u8] = b"
    #version 120

    varying vec2 v_TexCoord;
    uniform sampler2D t_Color;

    void main() {
        gl_FragColor = texture2D(t_Color, v_TexCoord);
    }
";

const VERTEX_DATA: &'static [Vertex] = &[
    // 1
    // |\
    // | \
    // 2--3
    Vertex {pos: [-1.0,  1.0], tex: [0.0, 0.0]},
    Vertex {pos: [-1.0, -1.0], tex: [0.0, 1.0]},
    Vertex {pos: [ 1.0, -1.0], tex: [1.0, 1.0]},
    // 1--3
    //  \ |
    //   \|
    //    2
    Vertex {pos: [-1.0,  1.0], tex: [0.0, 0.0]},
    Vertex {pos: [ 1.0, -1.0], tex: [1.0, 1.0]},
    Vertex {pos: [ 1.0,  1.0], tex: [1.0, 0.0]},
];
const BACKGROUND: gfx::ClearData = gfx::ClearData {
    color: [0.0, 0.0, 0.0, 1.0],
    depth: 1.0,
    stencil: 0,
};
const TEXT_COLOR: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
const TEXT_HEIGHT: i32 = 16;

type CanvasT = Canvas<gfxw::Output<dgl::Resources>, dgl::Device, dgl::Factory>;
type BatchT = OwnedBatch<ShaderParams<dgl::Resources>>;
type TextRendererT = gfx_text::Renderer<dgl::Resources>;

pub struct Gui {
    reader: ivf::Reader,
    decoder: vpx::Decoder,
    viewport_width: u16,
    viewport_height: u16,
    canvas: CanvasT,
    batch: BatchT,
    text: TextRendererT,
}

pub fn init(reader: ivf::Reader, decoder: vpx::Decoder) -> Result<Gui, Error> {
    let viewport_width = reader.get_width();
    let viewport_height = reader.get_height();
    let mut canvas = {
        // TODO(Kagami): Fullscreen.
        let window = try!(WindowBuilder::new()
            .with_dimensions(viewport_width as u32, viewport_height as u32)
            // Use simple initial title to allow to match the window in tiling
            // window managers.
            .with_title(format!("vpxview"))
            .build());
        gfxw::init(window).into_canvas()
    };
    let batch = {
        let mesh = canvas.factory.create_mesh(VERTEX_DATA);
        let program = try!(canvas.factory.link_program(VERTEX_SRC, FRAGMENT_SRC));
        let texture = try!(canvas.factory.create_texture_rgba8(
            reader.get_width(),
            reader.get_height()));
        let param = ShaderParams {color: (texture, None), _r: PhantomData};
        try!(OwnedBatch::new(mesh, program, param))
    };
    let text = try!(gfx_text::new(&mut canvas.factory).build());
    Ok(Gui {
        reader: reader,
        decoder: decoder,
        viewport_width: viewport_width,
        viewport_height: viewport_height,
        canvas: canvas,
        batch: batch,
        text: text,
    })
}

impl Gui {
    pub fn run(&mut self) {
        self.next_video_frame();
        loop {
            // Skip all pending events except the first because in some cases frame
            // decoding may take too long so interface will be brozen because of
            // big events queue.
            let maybe_event = {
                let mut iter = self.canvas.output.window.poll_events();
                let ev = iter.next();
                iter.count();  // Consume entire iterator
                ev
            };
            match maybe_event {
                Some(Closed) => break,
                Some(KeyboardInput(Pressed, _, Some(Key::Escape))) => break,
                Some(KeyboardInput(Pressed, _, Some(Key::Q))) => break,
                Some(KeyboardInput(Pressed, _, Some(Key::Left))) => {
                    // TODO(Kagami).
                },
                Some(KeyboardInput(Pressed, _, Some(Key::Right))) => {
                    self.next_video_frame();
                },
                _ => {},
            }
            self.canvas.clear(BACKGROUND);
            let draw_result = self.canvas.draw(&self.batch);
            try_print!(draw_result, "Error occured while drawing the frame: {:?}");
            self.render_hud();
            self.canvas.present();
        }
    }

    /// Read next IVF frame, decode VPx frame if possible and update the
    /// texture.
    fn next_video_frame(&mut self) {
        let maybe_frame = self.reader.next();
        self.update_title();
        let ivf_frame = maybe_print!(maybe_frame, "End of file");
        match self.decoder.decode_many(&ivf_frame) {
            Ok(mut iter) => {
                let image = maybe_print!(iter.next(), "No VPx frames in this IVF frame");
                // TODO(Kagami): IVF frame may consist of several VPx frames, we
                // correctly display only 1 IVF <-> 1 VPx case as for now.
                let remaining = iter.count();
                if remaining != 0 {
                    printerr!("Skipping {} other VPx frames", remaining);
                }
                // TODO(Kagami): Dimensions of decoded VPx image can vary from
                // frame to frame, we can adjust texture size accordingly.
                assert_eq!(image.get_display_width(), self.viewport_width);
                assert_eq!(image.get_display_height(), self.viewport_height);
                let texture = &self.batch.param.color.0;
                let update_result = self.canvas.factory.update_texture_raw(
                    texture,
                    &texture.get_info().to_image_info(),
                    &image.get_rgba8(),
                    None);
                try_print!(update_result, "Error occured while updating texture: {:?}");
            },
            Err(err) => {
                printerr!("Cannot decode IVF frame: {}", err);
            },
        };
    }

    fn get_frame_count(&self) -> String {
        self.reader.get_frame_count().map_or_else(|| "?".to_string(), |n| n.to_string())
    }

    fn update_title(&self) {
        let title = format!("vpxview - {} - {}/{}",
                            self.reader.get_filename(),
                            self.reader.get_frame_pos(),
                            self.get_frame_count());
        self.canvas.output.window.set_title(&title);
    }

    /// Draw given lines sequentially from top to bottom.
    fn draw_lines(&mut self, start_pos: [i32; 2], lines: &[String]) {
        let (x, mut y) = (start_pos[0], start_pos[1]);
        for line in lines {
            self.text.draw(line, [x, y], TEXT_COLOR);
            y += TEXT_HEIGHT;
        }
    }

    /// Render some VPx frame details on canvas.
    fn render_hud(&mut self) {
        let lines = [
            format!("Filename: {}", self.reader.get_filename()),
            format!("Frame: {}/{}", self.reader.get_frame_pos(), self.get_frame_count()),
        ];
        self.draw_lines([10, 10], &lines);
        let draw_result = self.text.draw_end(&mut self.canvas);
        try_print!(draw_result, "Error occured why drawing the text: {:?}");
    }
}
<|endoftext|>


<commit_before>/*!

Sendable hash maps.  Very much a work in progress.

*/

/**
 * A function that returns a hash of a value
 *
 * The hash should concentrate entropy in the lower bits.
 */
type hashfn<K> = pure fn~(K) -> uint;
type eqfn<K> = pure fn~(K, K) -> bool;

/// Open addressing with linear probing.
mod linear {
    export linear_map, linear_map_with_capacity, public_methods;

    const initial_capacity: uint = 32u; // 2^5
    type bucket<K,V> = {hash: uint, key: K, value: V};
    enum linear_map<K,V> {
        linear_map_({
            hashfn: pure fn~(x: &K) -> uint,
            eqfn: pure fn~(x: &K, y: &K) -> bool,
            resize_at: uint,
            size: uint,
            buckets: ~[option<bucket<K,V>>]})
    }

    // FIXME(#2979) -- with #2979 we could rewrite found_entry
    // to have type option<&bucket<K,V>> which would be nifty
    enum search_result {
        found_entry(uint), found_hole(uint), table_full
    }

    fn resize_at(capacity: uint) -> uint {
        ((capacity as float) * 3. / 4.) as uint
    }

    fn linear_map<K,V>(
        +hashfn: pure fn~(x: &K) -> uint,
        +eqfn: pure fn~(x: &K, y: &K) -> bool) -> linear_map<K,V> {

        linear_map_with_capacity(hashfn, eqfn, 32)
    }

    fn linear_map_with_capacity<K,V>(
        +hashfn: pure fn~(x: &K) -> uint,
        +eqfn: pure fn~(x: &K, y: &K) -> bool,
        initial_capacity: uint) -> linear_map<K,V> {

        linear_map_({
            hashfn: hashfn,
            eqfn: eqfn,
            resize_at: resize_at(initial_capacity),
            size: 0,
            buckets: vec::from_fn(initial_capacity, |_i| none)})
    }

    // FIXME(#2979) would allow us to use region type for k
    unsafe fn borrow<K>(&&k: K) -> &K {
        let p: *K = ptr::addr_of(k);
        p as &K
    }

    impl private_methods<K,V> for &const linear_map<K,V> {
        #[inline(always)]
        pure fn to_bucket(h: uint) -> uint {
            // FIXME(#3041) borrow a more sophisticated technique here from
            // Gecko, for example borrowing from Knuth, as Eich so
            // colorfully argues for here:
            // https://bugzilla.mozilla.org/show_bug.cgi?id=743107#c22
            h % self.buckets.len()
        }

        #[inline(always)]
        pure fn next_bucket(idx: uint, len_buckets: uint) -> uint {
            let n = (idx + 1) % len_buckets;
            unsafe{ // argh. log not considered pure.
                debug!{"next_bucket(%?, %?) = %?", idx, len_buckets, n};
            }
            ret n;
        }

        #[inline(always)]
        pure fn bucket_sequence(hash: uint, op: fn(uint) -> bool) -> uint {
            let start_idx = self.to_bucket(hash);
            let len_buckets = self.buckets.len();
            let mut idx = start_idx;
            loop {
                if !op(idx) {
                    ret idx;
                }
                idx = self.next_bucket(idx, len_buckets);
                if idx == start_idx {
                    ret start_idx;
                }
            }
        }

        #[inline(always)]
        pure fn bucket_for_key(
            buckets: &[option<bucket<K,V>>],
            k: &K) -> search_result {

            let hash = self.hashfn(k);
            self.bucket_for_key_with_hash(buckets, hash, k)
        }

        #[inline(always)]
        pure fn bucket_for_key_with_hash(
            buckets: &[option<bucket<K,V>>],
            hash: uint,
            k: &K) -> search_result {

            let _ = for self.bucket_sequence(hash) |i| {
                alt buckets[i] {
                  some(bkt) {
                    if bkt.hash == hash && self.eqfn(k, &bkt.key) {
                        ret found_entry(i);
                    }
                  }
                  none => {
                    ret found_hole(i);
                  }
                }
            };
            ret table_full;
        }
    }

    impl private_methods<K,V> for &mut linear_map<K,V> {
        /// Expands the capacity of the array and re-inserts each
        /// of the existing buckets.
        fn expand() {
            let old_capacity = self.buckets.len();
            let new_capacity = old_capacity * 2;
            self.resize_at = ((new_capacity as float) * 3.0 / 4.0) as uint;

            let mut old_buckets = vec::from_fn(new_capacity, |_i| none);
            self.buckets <-> old_buckets;

            for uint::range(0, old_capacity) |i| {
                let mut bucket = none;
                bucket <-> old_buckets[i];
                if bucket.is_some() {
                    self.insert_bucket(bucket);
                }
            }
        }

        fn insert_bucket(+bucket: option<bucket<K,V>>) {
            let {hash, key, value} <- option::unwrap(bucket);
            let _ = self.insert_internal(hash, key, value);
        }

        /// Inserts the key value pair into the buckets.
        /// Assumes that there will be a bucket.
        /// True if there was no previous entry with that key
        fn insert_internal(hash: uint, +k: K, +v: V) -> bool {
            alt self.bucket_for_key_with_hash(self.buckets, hash,
                                              unsafe{borrow(k)}) {
              table_full => {fail ~"Internal logic error";}
              found_hole(idx) {
                debug!{"insert fresh (%?->%?) at idx %?, hash %?",
                       k, v, idx, hash};
                self.buckets[idx] = some({hash: hash, key: k, value: v});
                self.size += 1;
                ret true;
              }
              found_entry(idx) => {
                debug!{"insert overwrite (%?->%?) at idx %?, hash %?",
                       k, v, idx, hash};
                self.buckets[idx] = some({hash: hash, key: k, value: v});
                ret false;
              }
            }
        }
    }

    impl public_methods<K,V> for &mut linear_map<K,V> {
        fn insert(+k: K, +v: V) -> bool {
            if self.size >= self.resize_at {
                // n.b.: We could also do this after searching, so
                // that we do not resize if this call to insert is
                // simply going to update a key in place.  My sense
                // though is that it's worse to have to search through
                // buckets to find the right spot twice than to just
                // resize in this corner case.
                self.expand();
            }

            let hash = self.hashfn(unsafe{borrow(k)});
            self.insert_internal(hash, k, v)
        }

        fn remove(k: &K) -> bool {
            // Removing from an open-addressed hashtable
            // is, well, painful.  The problem is that
            // the entry may lie on the probe path for other
            // entries, so removing it would make you think that
            // those probe paths are empty.
            //
            // To address this we basically have to keep walking,
            // re-inserting entries we find until we reach an empty
            // bucket.  We know we will eventually reach one because
            // we insert one ourselves at the beginning (the removed
            // entry).
            //
            // I found this explanation elucidating:
            // http://www.maths.lse.ac.uk/Courses/MA407/del-hash.pdf

            let mut idx = alt self.bucket_for_key(self.buckets, k) {
              table_full | found_hole(_) => {
                ret false;
              }
              found_entry(idx) => {
                idx
              }
            };

            let len_buckets = self.buckets.len();
            self.buckets[idx] = none;
            idx = self.next_bucket(idx, len_buckets);
            while self.buckets[idx].is_some() {
                let mut bucket = none;
                bucket <-> self.buckets[idx];
                self.insert_bucket(bucket);
                idx = self.next_bucket(idx, len_buckets);
            }
            ret true;
        }
    }

    impl private_methods<K,V> for &linear_map<K,V> {
        fn search(hash: uint, op: fn(x: &option<bucket<K,V>>) -> bool) {
            let _ = self.bucket_sequence(hash, |i| op(&self.buckets[i]));
        }
    }

    impl public_methods<K,V> for &const linear_map<K,V> {
        fn size() -> uint {
            self.size
        }

        fn contains_key(k: &K) -> bool {
            alt self.bucket_for_key(self.buckets, k) {
              found_entry(_) => {true}
              table_full | found_hole(_) => {false}
            }
        }
    }

    impl public_methods<K,V: copy> for &const linear_map<K,V> {
        fn find(k: &K) -> option<V> {
            alt self.bucket_for_key(self.buckets, k) {
              found_entry(idx) => {
                alt check self.buckets[idx] {
                  some(bkt) => {some(copy bkt.value)}
                }
              }
              table_full | found_hole(_) => {
                none
              }
            }
        }

        fn get(k: &K) -> V {
            let value = self.find(k);
            if value.is_none() {
                fail fmt!{"No entry found for key: %?", k};
            }
            option::unwrap(value)
        }

        fn [](k: &K) -> V {
            self.get(k)
        }
    }

    impl imm_methods<K,V> for &linear_map<K,V> {
        /*
        FIXME --- #2979 must be fixed to typecheck this
        fn find_ptr(k: K) -> option<&V> {
            //XXX this should not type check as written, but it should
            //be *possible* to typecheck it...
            self.with_ptr(k, |v| v)
        }
        */

        fn each(blk: fn(k: &K, v: &V) -> bool) {
            for vec::each(self.buckets) |slot| {
                let mut broke = false;
                do slot.iter |bucket| {
                    if !blk(&bucket.key, &bucket.value) {
                        broke = true; // FIXME(#3064) just write "break;"
                    }
                }
                if broke { break; }
            }
        }
        fn each_key(blk: fn(k: &K) -> bool) {
            self.each(|k, _v| blk(k))
        }
        fn each_value(blk: fn(v: &V) -> bool) {
            self.each(|_k, v| blk(v))
        }
    }
}

#[test]
mod test {

    import linear::linear_map;

    pure fn uint_hash(x: &uint) -> uint { *x }
    pure fn uint_eq(x: &uint, y: &uint) -> bool { *x == *y }

    fn int_linear_map<V>() -> linear_map<uint,V> {
        ret linear_map(uint_hash, uint_eq);
    }

    #[test]
    fn inserts() {
        let mut m = ~int_linear_map();
        assert m.insert(1, 2);
        assert m.insert(2, 4);
        assert m.get(&1) == 2;
        assert m.get(&2) == 4;
    }

    #[test]
    fn overwrite() {
        let mut m = ~int_linear_map();
        assert m.insert(1, 2);
        assert m.get(&1) == 2;
        assert !m.insert(1, 3);
        assert m.get(&1) == 3;
    }

    #[test]
    fn conflicts() {
        let mut m = ~linear::linear_map_with_capacity(uint_hash, uint_eq, 4);
        assert m.insert(1, 2);
        assert m.insert(5, 3);
        assert m.insert(9, 4);
        assert m.get(&9) == 4;
        assert m.get(&5) == 3;
        assert m.get(&1) == 2;
    }

    #[test]
    fn conflict_remove() {
        let mut m = ~linear::linear_map_with_capacity(uint_hash, uint_eq, 4);
        assert m.insert(1, 2);
        assert m.insert(5, 3);
        assert m.insert(9, 4);
        assert m.remove(&1);
        assert m.get(&9) == 4;
        assert m.get(&5) == 3;
    }
}
<commit_msg>send_map: each -> each_ref, and add each* copying versions; add 'iterate' test<commit_after>/*!

Sendable hash maps.  Very much a work in progress.

*/

/**
 * A function that returns a hash of a value
 *
 * The hash should concentrate entropy in the lower bits.
 */
type hashfn<K> = pure fn~(K) -> uint;
type eqfn<K> = pure fn~(K, K) -> bool;

/// Open addressing with linear probing.
mod linear {
    export linear_map, linear_map_with_capacity, public_methods;

    const initial_capacity: uint = 32u; // 2^5
    type bucket<K,V> = {hash: uint, key: K, value: V};
    enum linear_map<K,V> {
        linear_map_({
            hashfn: pure fn~(x: &K) -> uint,
            eqfn: pure fn~(x: &K, y: &K) -> bool,
            resize_at: uint,
            size: uint,
            buckets: ~[option<bucket<K,V>>]})
    }

    // FIXME(#2979) -- with #2979 we could rewrite found_entry
    // to have type option<&bucket<K,V>> which would be nifty
    enum search_result {
        found_entry(uint), found_hole(uint), table_full
    }

    fn resize_at(capacity: uint) -> uint {
        ((capacity as float) * 3. / 4.) as uint
    }

    fn linear_map<K,V>(
        +hashfn: pure fn~(x: &K) -> uint,
        +eqfn: pure fn~(x: &K, y: &K) -> bool) -> linear_map<K,V> {

        linear_map_with_capacity(hashfn, eqfn, 32)
    }

    fn linear_map_with_capacity<K,V>(
        +hashfn: pure fn~(x: &K) -> uint,
        +eqfn: pure fn~(x: &K, y: &K) -> bool,
        initial_capacity: uint) -> linear_map<K,V> {

        linear_map_({
            hashfn: hashfn,
            eqfn: eqfn,
            resize_at: resize_at(initial_capacity),
            size: 0,
            buckets: vec::from_fn(initial_capacity, |_i| none)})
    }

    // FIXME(#2979) would allow us to use region type for k
    unsafe fn borrow<K>(&&k: K) -> &K {
        let p: *K = ptr::addr_of(k);
        p as &K
    }

    impl private_methods<K,V> for &const linear_map<K,V> {
        #[inline(always)]
        pure fn to_bucket(h: uint) -> uint {
            // FIXME(#3041) borrow a more sophisticated technique here from
            // Gecko, for example borrowing from Knuth, as Eich so
            // colorfully argues for here:
            // https://bugzilla.mozilla.org/show_bug.cgi?id=743107#c22
            h % self.buckets.len()
        }

        #[inline(always)]
        pure fn next_bucket(idx: uint, len_buckets: uint) -> uint {
            let n = (idx + 1) % len_buckets;
            unsafe{ // argh. log not considered pure.
                debug!{"next_bucket(%?, %?) = %?", idx, len_buckets, n};
            }
            ret n;
        }

        #[inline(always)]
        pure fn bucket_sequence(hash: uint, op: fn(uint) -> bool) -> uint {
            let start_idx = self.to_bucket(hash);
            let len_buckets = self.buckets.len();
            let mut idx = start_idx;
            loop {
                if !op(idx) {
                    ret idx;
                }
                idx = self.next_bucket(idx, len_buckets);
                if idx == start_idx {
                    ret start_idx;
                }
            }
        }

        #[inline(always)]
        pure fn bucket_for_key(
            buckets: &[option<bucket<K,V>>],
            k: &K) -> search_result {

            let hash = self.hashfn(k);
            self.bucket_for_key_with_hash(buckets, hash, k)
        }

        #[inline(always)]
        pure fn bucket_for_key_with_hash(
            buckets: &[option<bucket<K,V>>],
            hash: uint,
            k: &K) -> search_result {

            let _ = for self.bucket_sequence(hash) |i| {
                alt buckets[i] {
                  some(bkt) {
                    if bkt.hash == hash && self.eqfn(k, &bkt.key) {
                        ret found_entry(i);
                    }
                  }
                  none => {
                    ret found_hole(i);
                  }
                }
            };
            ret table_full;
        }
    }

    impl private_methods<K,V> for &mut linear_map<K,V> {
        /// Expands the capacity of the array and re-inserts each
        /// of the existing buckets.
        fn expand() {
            let old_capacity = self.buckets.len();
            let new_capacity = old_capacity * 2;
            self.resize_at = ((new_capacity as float) * 3.0 / 4.0) as uint;

            let mut old_buckets = vec::from_fn(new_capacity, |_i| none);
            self.buckets <-> old_buckets;

            for uint::range(0, old_capacity) |i| {
                let mut bucket = none;
                bucket <-> old_buckets[i];
                if bucket.is_some() {
                    self.insert_bucket(bucket);
                }
            }
        }

        fn insert_bucket(+bucket: option<bucket<K,V>>) {
            let {hash, key, value} <- option::unwrap(bucket);
            let _ = self.insert_internal(hash, key, value);
        }

        /// Inserts the key value pair into the buckets.
        /// Assumes that there will be a bucket.
        /// True if there was no previous entry with that key
        fn insert_internal(hash: uint, +k: K, +v: V) -> bool {
            alt self.bucket_for_key_with_hash(self.buckets, hash,
                                              unsafe{borrow(k)}) {
              table_full => {fail ~"Internal logic error";}
              found_hole(idx) {
                debug!{"insert fresh (%?->%?) at idx %?, hash %?",
                       k, v, idx, hash};
                self.buckets[idx] = some({hash: hash, key: k, value: v});
                self.size += 1;
                ret true;
              }
              found_entry(idx) => {
                debug!{"insert overwrite (%?->%?) at idx %?, hash %?",
                       k, v, idx, hash};
                self.buckets[idx] = some({hash: hash, key: k, value: v});
                ret false;
              }
            }
        }
    }

    impl public_methods<K,V> for &mut linear_map<K,V> {
        fn insert(+k: K, +v: V) -> bool {
            if self.size >= self.resize_at {
                // n.b.: We could also do this after searching, so
                // that we do not resize if this call to insert is
                // simply going to update a key in place.  My sense
                // though is that it's worse to have to search through
                // buckets to find the right spot twice than to just
                // resize in this corner case.
                self.expand();
            }

            let hash = self.hashfn(unsafe{borrow(k)});
            self.insert_internal(hash, k, v)
        }

        fn remove(k: &K) -> bool {
            // Removing from an open-addressed hashtable
            // is, well, painful.  The problem is that
            // the entry may lie on the probe path for other
            // entries, so removing it would make you think that
            // those probe paths are empty.
            //
            // To address this we basically have to keep walking,
            // re-inserting entries we find until we reach an empty
            // bucket.  We know we will eventually reach one because
            // we insert one ourselves at the beginning (the removed
            // entry).
            //
            // I found this explanation elucidating:
            // http://www.maths.lse.ac.uk/Courses/MA407/del-hash.pdf

            let mut idx = alt self.bucket_for_key(self.buckets, k) {
              table_full | found_hole(_) => {
                ret false;
              }
              found_entry(idx) => {
                idx
              }
            };

            let len_buckets = self.buckets.len();
            self.buckets[idx] = none;
            idx = self.next_bucket(idx, len_buckets);
            while self.buckets[idx].is_some() {
                let mut bucket = none;
                bucket <-> self.buckets[idx];
                self.insert_bucket(bucket);
                idx = self.next_bucket(idx, len_buckets);
            }
            ret true;
        }
    }

    impl private_methods<K,V> for &linear_map<K,V> {
        fn search(hash: uint, op: fn(x: &option<bucket<K,V>>) -> bool) {
            let _ = self.bucket_sequence(hash, |i| op(&self.buckets[i]));
        }
    }

    impl public_methods<K,V> for &const linear_map<K,V> {
        fn size() -> uint {
            self.size
        }

        fn contains_key(k: &K) -> bool {
            alt self.bucket_for_key(self.buckets, k) {
              found_entry(_) => {true}
              table_full | found_hole(_) => {false}
            }
        }
    }

    impl public_methods<K,V: copy> for &const linear_map<K,V> {
        fn find(k: &K) -> option<V> {
            alt self.bucket_for_key(self.buckets, k) {
              found_entry(idx) => {
                alt check self.buckets[idx] {
                  some(bkt) => {some(copy bkt.value)}
                }
              }
              table_full | found_hole(_) => {
                none
              }
            }
        }

        fn get(k: &K) -> V {
            let value = self.find(k);
            if value.is_none() {
                fail fmt!{"No entry found for key: %?", k};
            }
            option::unwrap(value)
        }

        fn [](k: &K) -> V {
            self.get(k)
        }
    }

    impl imm_methods<K,V> for &linear_map<K,V> {
        /*
        FIXME --- #2979 must be fixed to typecheck this
        fn find_ptr(k: K) -> option<&V> {
            //XXX this should not type check as written, but it should
            //be *possible* to typecheck it...
            self.with_ptr(k, |v| v)
        }
        */

        fn each_ref(blk: fn(k: &K, v: &V) -> bool) {
            for vec::each(self.buckets) |slot| {
                let mut broke = false;
                do slot.iter |bucket| {
                    if !blk(&bucket.key, &bucket.value) {
                        broke = true; // FIXME(#3064) just write "break;"
                    }
                }
                if broke { break; }
            }
        }
        fn each_key_ref(blk: fn(k: &K) -> bool) {
            self.each_ref(|k, _v| blk(k))
        }
        fn each_value_ref(blk: fn(v: &V) -> bool) {
            self.each_ref(|_k, v| blk(v))
        }
    }

    impl public_methods<K: copy, V: copy> for &linear_map<K,V> {
        fn each(blk: fn(+K,+V) -> bool) {
            self.each_ref(|k,v| blk(copy *k, copy *v));
        }
    }
    impl public_methods<K: copy, V> for &linear_map<K,V> {
        fn each_key(blk: fn(+K) -> bool) {
            self.each_key_ref(|k| blk(copy *k));
        }
    }
    impl public_methods<K, V: copy> for &linear_map<K,V> {
        fn each_value(blk: fn(+V) -> bool) {
            self.each_value_ref(|v| blk(copy *v));
        }
    }
}

#[test]
mod test {

    import linear::linear_map;

    pure fn uint_hash(x: &uint) -> uint { *x }
    pure fn uint_eq(x: &uint, y: &uint) -> bool { *x == *y }

    fn int_linear_map<V>() -> linear_map<uint,V> {
        ret linear_map(uint_hash, uint_eq);
    }

    #[test]
    fn inserts() {
        let mut m = ~int_linear_map();
        assert m.insert(1, 2);
        assert m.insert(2, 4);
        assert m.get(&1) == 2;
        assert m.get(&2) == 4;
    }

    #[test]
    fn overwrite() {
        let mut m = ~int_linear_map();
        assert m.insert(1, 2);
        assert m.get(&1) == 2;
        assert !m.insert(1, 3);
        assert m.get(&1) == 3;
    }

    #[test]
    fn conflicts() {
        let mut m = ~linear::linear_map_with_capacity(uint_hash, uint_eq, 4);
        assert m.insert(1, 2);
        assert m.insert(5, 3);
        assert m.insert(9, 4);
        assert m.get(&9) == 4;
        assert m.get(&5) == 3;
        assert m.get(&1) == 2;
    }

    #[test]
    fn conflict_remove() {
        let mut m = ~linear::linear_map_with_capacity(uint_hash, uint_eq, 4);
        assert m.insert(1, 2);
        assert m.insert(5, 3);
        assert m.insert(9, 4);
        assert m.remove(&1);
        assert m.get(&9) == 4;
        assert m.get(&5) == 3;
    }

    #[test]
    fn iterate() {
        let mut m = linear::linear_map_with_capacity(uint_hash, uint_eq, 4);
        for uint::range(0, 32) |i| {
            assert (&mut m).insert(i, i*2);
        }
        let mut observed = 0;
        for (&m).each |k, v| {
            assert v == k*2;
            observed |= (1 << k);
        }
        assert observed == 0xFFFF_FFFF;
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::ascii::AsciiExt;
use std::cmp;

#[derive(Debug, Clone)]
pub struct LogDirective {
    pub name: Option<String>,
    pub level: u32,
}

pub const LOG_LEVEL_NAMES: [&'static str; 4] = ["ERROR", "WARN", "INFO", "DEBUG"];

/// Parse an individual log level that is either a number or a symbolic log level
fn parse_log_level(level: &str) -> Option<u32> {
    level.parse::<u32>()
         .ok()
         .or_else(|| {
             let pos = LOG_LEVEL_NAMES.iter().position(|&name| name.eq_ignore_ascii_case(level));
             pos.map(|p| p as u32 + 1)
         })
         .map(|p| cmp::min(p, ::MAX_LOG_LEVEL))
}

/// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=1/foo")
/// and return a vector with log directives.
///
/// Valid log levels are 0-255, with the most likely ones being 1-4 (defined in
/// std::).  Also supports string log levels of error, warn, info, and debug
pub fn parse_logging_spec(spec: &str) -> (Vec<LogDirective>, Option<String>) {
    let mut dirs = Vec::new();

    let mut parts = spec.split('/');
    let mods = parts.next();
    let filter = parts.next();
    if parts.next().is_some() {
        println!("warning: invalid logging spec '{}', ignoring it (too many '/'s)",
                 spec);
        return (dirs, None);
    }
    if let Some(m) = mods {
        for s in m.split(',') {
            if s.is_empty() {
                continue
            }
            let mut parts = s.split('=');
            let (log_level, name) = match (parts.next(),
                                           parts.next().map(|s| s.trim()),
                                           parts.next()) {
                (Some(part0), None, None) => {
                    // if the single argument is a log-level string or number,
                    // treat that as a global fallback
                    match parse_log_level(part0) {
                        Some(num) => (num, None),
                        None => (::MAX_LOG_LEVEL, Some(part0)),
                    }
                }
                (Some(part0), Some(""), None) => (::MAX_LOG_LEVEL, Some(part0)),
                (Some(part0), Some(part1), None) => {
                    match parse_log_level(part1) {
                        Some(num) => (num, Some(part0)),
                        _ => {
                            println!("warning: invalid logging spec '{}', ignoring it", part1);
                            continue
                        }
                    }
                }
                _ => {
                    println!("warning: invalid logging spec '{}', ignoring it", s);
                    continue
                }
            };
            dirs.push(LogDirective {
                name: name.map(str::to_owned),
                level: log_level,
            });
        }
    }

    (dirs, filter.map(str::to_owned))
}

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

    #[test]
    fn parse_logging_spec_valid() {
        let (dirs, filter) = parse_logging_spec("crate1::mod1=1,crate1::mod2,crate2=4");
        assert_eq!(dirs.len(), 3);
        assert_eq!(dirs[0].name, Some("crate1::mod1".to_owned()));
        assert_eq!(dirs[0].level, 1);

        assert_eq!(dirs[1].name, Some("crate1::mod2".to_owned()));
        assert_eq!(dirs[1].level, ::MAX_LOG_LEVEL);

        assert_eq!(dirs[2].name, Some("crate2".to_owned()));
        assert_eq!(dirs[2].level, 4);
        assert!(filter.is_none());
    }

    #[test]
    fn parse_logging_spec_invalid_crate() {
        // test parse_logging_spec with multiple = in specification
        let (dirs, filter) = parse_logging_spec("crate1::mod1=1=2,crate2=4");
        assert_eq!(dirs.len(), 1);
        assert_eq!(dirs[0].name, Some("crate2".to_owned()));
        assert_eq!(dirs[0].level, 4);
        assert!(filter.is_none());
    }

    #[test]
    fn parse_logging_spec_invalid_log_level() {
        // test parse_logging_spec with 'noNumber' as log level
        let (dirs, filter) = parse_logging_spec("crate1::mod1=noNumber,crate2=4");
        assert_eq!(dirs.len(), 1);
        assert_eq!(dirs[0].name, Some("crate2".to_owned()));
        assert_eq!(dirs[0].level, 4);
        assert!(filter.is_none());
    }

    #[test]
    fn parse_logging_spec_string_log_level() {
        // test parse_logging_spec with 'warn' as log level
        let (dirs, filter) = parse_logging_spec("crate1::mod1=wrong,crate2=warn");
        assert_eq!(dirs.len(), 1);
        assert_eq!(dirs[0].name, Some("crate2".to_owned()));
        assert_eq!(dirs[0].level, ::WARN);
        assert!(filter.is_none());
    }

    #[test]
    fn parse_logging_spec_empty_log_level() {
        // test parse_logging_spec with '' as log level
        let (dirs, filter) = parse_logging_spec("crate1::mod1=wrong,crate2=");
        assert_eq!(dirs.len(), 1);
        assert_eq!(dirs[0].name, Some("crate2".to_owned()));
        assert_eq!(dirs[0].level, ::MAX_LOG_LEVEL);
        assert!(filter.is_none());
    }

    #[test]
    fn parse_logging_spec_global() {
        // test parse_logging_spec with no crate
        let (dirs, filter) = parse_logging_spec("warn,crate2=4");
        assert_eq!(dirs.len(), 2);
        assert_eq!(dirs[0].name, None);
        assert_eq!(dirs[0].level, 2);
        assert_eq!(dirs[1].name, Some("crate2".to_owned()));
        assert_eq!(dirs[1].level, 4);
        assert!(filter.is_none());
    }

    #[test]
    fn parse_logging_spec_valid_filter() {
        let (dirs, filter) = parse_logging_spec("crate1::mod1=1,crate1::mod2,crate2=4/abc");
        assert_eq!(dirs.len(), 3);
        assert_eq!(dirs[0].name, Some("crate1::mod1".to_owned()));
        assert_eq!(dirs[0].level, 1);

        assert_eq!(dirs[1].name, Some("crate1::mod2".to_owned()));
        assert_eq!(dirs[1].level, ::MAX_LOG_LEVEL);

        assert_eq!(dirs[2].name, Some("crate2".to_owned()));
        assert_eq!(dirs[2].level, 4);
        assert!(filter.is_some() && filter.unwrap().to_owned() == "abc");
    }

    #[test]
    fn parse_logging_spec_invalid_crate_filter() {
        let (dirs, filter) = parse_logging_spec("crate1::mod1=1=2,crate2=4/a.c");
        assert_eq!(dirs.len(), 1);
        assert_eq!(dirs[0].name, Some("crate2".to_owned()));
        assert_eq!(dirs[0].level, 4);
        assert!(filter.is_some() && filter.unwrap().to_owned() == "a.c");
    }

    #[test]
    fn parse_logging_spec_empty_with_filter() {
        let (dirs, filter) = parse_logging_spec("crate1/a*c");
        assert_eq!(dirs.len(), 1);
        assert_eq!(dirs[0].name, Some("crate1".to_owned()));
        assert_eq!(dirs[0].level, ::MAX_LOG_LEVEL);
        assert!(filter.is_some() && filter.unwrap().to_owned() == "a*c");
    }
}
<commit_msg>Auto merge of #30012 - jonas-schievink:log-level-trace, r=alexcrichton<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::ascii::AsciiExt;
use std::cmp;

#[derive(Debug, Clone)]
pub struct LogDirective {
    pub name: Option<String>,
    pub level: u32,
}

pub const LOG_LEVEL_NAMES: [&'static str; 5] = ["ERROR", "WARN", "INFO", "DEBUG", "TRACE"];

/// Parse an individual log level that is either a number or a symbolic log level
fn parse_log_level(level: &str) -> Option<u32> {
    level.parse::<u32>()
         .ok()
         .or_else(|| {
             let pos = LOG_LEVEL_NAMES.iter().position(|&name| name.eq_ignore_ascii_case(level));
             pos.map(|p| p as u32 + 1)
         })
         .map(|p| cmp::min(p, ::MAX_LOG_LEVEL))
}

/// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=1/foo")
/// and return a vector with log directives.
///
/// Valid log levels are 0-255, with the most likely ones being 1-4 (defined in
/// std::).  Also supports string log levels of error, warn, info, and debug
pub fn parse_logging_spec(spec: &str) -> (Vec<LogDirective>, Option<String>) {
    let mut dirs = Vec::new();

    let mut parts = spec.split('/');
    let mods = parts.next();
    let filter = parts.next();
    if parts.next().is_some() {
        println!("warning: invalid logging spec '{}', ignoring it (too many '/'s)",
                 spec);
        return (dirs, None);
    }
    if let Some(m) = mods {
        for s in m.split(',') {
            if s.is_empty() {
                continue
            }
            let mut parts = s.split('=');
            let (log_level, name) = match (parts.next(),
                                           parts.next().map(|s| s.trim()),
                                           parts.next()) {
                (Some(part0), None, None) => {
                    // if the single argument is a log-level string or number,
                    // treat that as a global fallback
                    match parse_log_level(part0) {
                        Some(num) => (num, None),
                        None => (::MAX_LOG_LEVEL, Some(part0)),
                    }
                }
                (Some(part0), Some(""), None) => (::MAX_LOG_LEVEL, Some(part0)),
                (Some(part0), Some(part1), None) => {
                    match parse_log_level(part1) {
                        Some(num) => (num, Some(part0)),
                        _ => {
                            println!("warning: invalid logging spec '{}', ignoring it", part1);
                            continue
                        }
                    }
                }
                _ => {
                    println!("warning: invalid logging spec '{}', ignoring it", s);
                    continue
                }
            };
            dirs.push(LogDirective {
                name: name.map(str::to_owned),
                level: log_level,
            });
        }
    }

    (dirs, filter.map(str::to_owned))
}

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

    #[test]
    fn parse_logging_spec_valid() {
        let (dirs, filter) = parse_logging_spec("crate1::mod1=1,crate1::mod2,crate2=4");
        assert_eq!(dirs.len(), 3);
        assert_eq!(dirs[0].name, Some("crate1::mod1".to_owned()));
        assert_eq!(dirs[0].level, 1);

        assert_eq!(dirs[1].name, Some("crate1::mod2".to_owned()));
        assert_eq!(dirs[1].level, ::MAX_LOG_LEVEL);

        assert_eq!(dirs[2].name, Some("crate2".to_owned()));
        assert_eq!(dirs[2].level, 4);
        assert!(filter.is_none());
    }

    #[test]
    fn parse_logging_spec_invalid_crate() {
        // test parse_logging_spec with multiple = in specification
        let (dirs, filter) = parse_logging_spec("crate1::mod1=1=2,crate2=4");
        assert_eq!(dirs.len(), 1);
        assert_eq!(dirs[0].name, Some("crate2".to_owned()));
        assert_eq!(dirs[0].level, 4);
        assert!(filter.is_none());
    }

    #[test]
    fn parse_logging_spec_invalid_log_level() {
        // test parse_logging_spec with 'noNumber' as log level
        let (dirs, filter) = parse_logging_spec("crate1::mod1=noNumber,crate2=4");
        assert_eq!(dirs.len(), 1);
        assert_eq!(dirs[0].name, Some("crate2".to_owned()));
        assert_eq!(dirs[0].level, 4);
        assert!(filter.is_none());
    }

    #[test]
    fn parse_logging_spec_string_log_level() {
        // test parse_logging_spec with 'warn' as log level
        let (dirs, filter) = parse_logging_spec("crate1::mod1=wrong,crate2=warn");
        assert_eq!(dirs.len(), 1);
        assert_eq!(dirs[0].name, Some("crate2".to_owned()));
        assert_eq!(dirs[0].level, ::WARN);
        assert!(filter.is_none());
    }

    #[test]
    fn parse_logging_spec_empty_log_level() {
        // test parse_logging_spec with '' as log level
        let (dirs, filter) = parse_logging_spec("crate1::mod1=wrong,crate2=");
        assert_eq!(dirs.len(), 1);
        assert_eq!(dirs[0].name, Some("crate2".to_owned()));
        assert_eq!(dirs[0].level, ::MAX_LOG_LEVEL);
        assert!(filter.is_none());
    }

    #[test]
    fn parse_logging_spec_global() {
        // test parse_logging_spec with no crate
        let (dirs, filter) = parse_logging_spec("warn,crate2=4");
        assert_eq!(dirs.len(), 2);
        assert_eq!(dirs[0].name, None);
        assert_eq!(dirs[0].level, 2);
        assert_eq!(dirs[1].name, Some("crate2".to_owned()));
        assert_eq!(dirs[1].level, 4);
        assert!(filter.is_none());
    }

    #[test]
    fn parse_logging_spec_valid_filter() {
        let (dirs, filter) = parse_logging_spec("crate1::mod1=1,crate1::mod2,crate2=4/abc");
        assert_eq!(dirs.len(), 3);
        assert_eq!(dirs[0].name, Some("crate1::mod1".to_owned()));
        assert_eq!(dirs[0].level, 1);

        assert_eq!(dirs[1].name, Some("crate1::mod2".to_owned()));
        assert_eq!(dirs[1].level, ::MAX_LOG_LEVEL);

        assert_eq!(dirs[2].name, Some("crate2".to_owned()));
        assert_eq!(dirs[2].level, 4);
        assert!(filter.is_some() && filter.unwrap().to_owned() == "abc");
    }

    #[test]
    fn parse_logging_spec_invalid_crate_filter() {
        let (dirs, filter) = parse_logging_spec("crate1::mod1=1=2,crate2=4/a.c");
        assert_eq!(dirs.len(), 1);
        assert_eq!(dirs[0].name, Some("crate2".to_owned()));
        assert_eq!(dirs[0].level, 4);
        assert!(filter.is_some() && filter.unwrap().to_owned() == "a.c");
    }

    #[test]
    fn parse_logging_spec_empty_with_filter() {
        let (dirs, filter) = parse_logging_spec("crate1/a*c");
        assert_eq!(dirs.len(), 1);
        assert_eq!(dirs[0].name, Some("crate1".to_owned()));
        assert_eq!(dirs[0].level, ::MAX_LOG_LEVEL);
        assert!(filter.is_some() && filter.unwrap().to_owned() == "a*c");
    }
}
<|endoftext|>


<commit_before>//! Generating types from the opentype spec

use log::debug;
use proc_macro2::TokenStream;
use quote::quote;

mod error;
mod fields;
mod flags_enums;
mod parsing;
mod record;
mod table;

use parsing::{Item, Items};

pub use error::ErrorReport;

use crate::parsing::Phase;

/// Codegeneration mode.
#[derive(Debug, Clone, Copy, serde::Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Mode {
    /// Generate parsing code
    Parse,
    /// Generate compilation code
    Compile,
}

fn touchup_code(tables: proc_macro2::TokenStream) -> Result<String, syn::Error> {
    // if this is not valid code just pass it through directly, and then we
    // can see the compiler errors
    let source_str = match rustfmt_wrapper::rustfmt(&tables) {
        Ok(s) => s,
        Err(_) => return Ok(tables.to_string()),
    };
    // convert doc comment attributes into normal doc comments
    let doc_comments = regex::Regex::new(r#"#\[doc = "(.*)"\]"#).unwrap();
    let source_str = doc_comments.replace_all(&source_str, "///$1");
    let newlines_before_docs = regex::Regex::new(r#"([;\}])\n( *)(///|pub|impl|#)"#).unwrap();
    let source_str = newlines_before_docs.replace_all(&source_str, "$1\n\n$2$3");

    // add newlines after top-level items
    let re2 = regex::Regex::new(r"\n\}").unwrap();
    let source_str = re2.replace_all(&source_str, "\n}\n\n");
    Ok(rustfmt_wrapper::rustfmt(source_str).unwrap())
}

pub fn generate_code(code_str: &str, mode: Mode) -> Result<String, syn::Error> {
    // Generation is done in phases (https://github.com/googlefonts/fontations/issues/71):
    // 1. Parse
    debug!("Parse (mode {:?})", mode);
    let mut items: Items = syn::parse_str(code_str)?;
    items.sanity_check(Phase::Parse)?;

    // 2. Contemplate (semantic analysis)
    debug!("Analyze (mode {:?})", mode);
    items.resolve_pending()?;
    items.sanity_check(Phase::Analysis)?;

    // 3. Generate
    debug!("Generate (mode {:?})", mode);
    let tables = match &mode {
        Mode::Parse => generate_parse_module(&items),
        Mode::Compile => generate_compile_module(&items),
    }?;

    // 4. Touchup
    debug!("Touchup (mode {:?})", mode);
    let source_str = touchup_code(tables)?;

    Ok(format!(
        "\
    // THIS FILE IS AUTOGENERATED.\n\
    // Any changes to this file will be overwritten.\n\
    // For more information about how codegen works, see font-codegen/README.md\n\n{}",
        source_str,
    ))
}

pub(crate) fn generate_parse_module(items: &Items) -> Result<proc_macro2::TokenStream, syn::Error> {
    let mut code = Vec::new();
    for item in &items.items {
        let item_code = match item {
            Item::Record(item) => record::generate(item)?,
            Item::Table(item) => table::generate(item)?,
            Item::GenericGroup(item) => table::generate_group(item)?,
            Item::Format(item) => table::generate_format_group(item)?,
            Item::RawEnum(item) => flags_enums::generate_raw_enum(item),
            Item::Flags(item) => flags_enums::generate_flags(item),
            Item::Extern(..) => TokenStream::new(),
        };
        code.push(item_code);
    }

    Ok(quote! {
        #[allow(unused_imports)]
        use crate::codegen_prelude::*;
        #(#code)*
    })
}

pub(crate) fn generate_compile_module(
    items: &Items,
) -> Result<proc_macro2::TokenStream, syn::Error> {
    let code = items
        .items
        .iter()
        .map(|item| match item {
            Item::Record(item) => record::generate_compile(item, &items.parse_module_path),
            Item::Table(item) => table::generate_compile(item, &items.parse_module_path),
            Item::GenericGroup(item) => {
                table::generate_group_compile(item, &items.parse_module_path)
            }
            Item::Format(item) => table::generate_format_compile(item, &items.parse_module_path),
            Item::RawEnum(item) => Ok(flags_enums::generate_raw_enum_compile(item)),
            Item::Flags(item) => Ok(flags_enums::generate_flags_compile(item)),
            Item::Extern(..) => Ok(TokenStream::new()),
        })
        .collect::<Result<Vec<_>, _>>()?;

    Ok(quote! {
        #[allow(unused_imports)]
        use crate::codegen_prelude::*;

        #( #code )*
    })
}

impl std::str::FromStr for Mode {
    type Err = miette::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "parse" => Ok(Self::Parse),
            "compile" => Ok(Self::Compile),
            other => Err(miette::Error::msg(format!(
                "expected one of 'parse' or 'compile' (found {other})"
            ))),
        }
    }
}
<commit_msg>Update font-codegen/src/lib.rs<commit_after>//! Generating types from the opentype spec

use log::debug;
use proc_macro2::TokenStream;
use quote::quote;

mod error;
mod fields;
mod flags_enums;
mod parsing;
mod record;
mod table;

use parsing::{Item, Items};

pub use error::ErrorReport;

use crate::parsing::Phase;

/// Codegeneration mode.
#[derive(Debug, Clone, Copy, serde::Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Mode {
    /// Generate parsing code
    Parse,
    /// Generate compilation code
    Compile,
}

fn touchup_code(tables: proc_macro2::TokenStream) -> Result<String, syn::Error> {
    // if this is not valid code just pass it through directly, and then we
    // can see the compiler errors
    let source_str = match rustfmt_wrapper::rustfmt(&tables) {
        Ok(s) => s,
        Err(_) => return Ok(tables.to_string()),
    };
    // convert doc comment attributes into normal doc comments
    let doc_comments = regex::Regex::new(r#"#\[doc = "(.*)"\]"#).unwrap();
    let source_str = doc_comments.replace_all(&source_str, "///$1");
    let newlines_before_docs = regex::Regex::new(r#"([;\}])\n( *)(///|pub|impl|#)"#).unwrap();
    let source_str = newlines_before_docs.replace_all(&source_str, "$1\n\n$2$3");

    // add newlines after top-level items
    let re2 = regex::Regex::new(r"\n\}").unwrap();
    let source_str = re2.replace_all(&source_str, "\n}\n\n");
    Ok(rustfmt_wrapper::rustfmt(source_str).unwrap())
}

pub fn generate_code(code_str: &str, mode: Mode) -> Result<String, syn::Error> {
    // Generation is done in phases (https://github.com/googlefonts/fontations/issues/71):
    // 1. Parse
    debug!("Parse (mode {:?})", mode);
    let mut items: Items = syn::parse_str(code_str)?;
    items.sanity_check(Phase::Parse)?;

    // 2. Contemplate (semantic analysis)
    debug!("Analyze (mode {:?})", mode);
    items.resolve_pending()?;
    items.sanity_check(Phase::Analysis)?;

    // 3. Generate
    debug!("Generate (mode {:?})", mode);
    let tables = match &mode {
        Mode::Parse => generate_parse_module(&items),
        Mode::Compile => generate_compile_module(&items),
    }?;

    // 4. Touchup
    debug!("Touchup (mode {:?})", mode);
    let source_str = touchup_code(tables)?;

    Ok(format!(
        "\
    // THIS FILE IS AUTOGENERATED.\n\
    // Any changes to this file will be overwritten.\n\
    // For more information about how codegen works, see font-codegen/README.md\n\n{}",
        source_str,
    ))
}

pub(crate) fn generate_parse_module(items: &Items) -> Result<proc_macro2::TokenStream, syn::Error> {
    let mut code = Vec::new();
    for item in &items.items {
        let item_code = match item {
            Item::Record(item) => record::generate(item)?,
            Item::Table(item) => table::generate(item)?,
            Item::GenericGroup(item) => table::generate_group(item)?,
            Item::Format(item) => table::generate_format_group(item)?,
            Item::RawEnum(item) => flags_enums::generate_raw_enum(item),
            Item::Flags(item) => flags_enums::generate_flags(item),
            Item::Extern(..) => Default::default(),
        };
        code.push(item_code);
    }

    Ok(quote! {
        #[allow(unused_imports)]
        use crate::codegen_prelude::*;
        #(#code)*
    })
}

pub(crate) fn generate_compile_module(
    items: &Items,
) -> Result<proc_macro2::TokenStream, syn::Error> {
    let code = items
        .items
        .iter()
        .map(|item| match item {
            Item::Record(item) => record::generate_compile(item, &items.parse_module_path),
            Item::Table(item) => table::generate_compile(item, &items.parse_module_path),
            Item::GenericGroup(item) => {
                table::generate_group_compile(item, &items.parse_module_path)
            }
            Item::Format(item) => table::generate_format_compile(item, &items.parse_module_path),
            Item::RawEnum(item) => Ok(flags_enums::generate_raw_enum_compile(item)),
            Item::Flags(item) => Ok(flags_enums::generate_flags_compile(item)),
            Item::Extern(..) => Ok(TokenStream::new()),
        })
        .collect::<Result<Vec<_>, _>>()?;

    Ok(quote! {
        #[allow(unused_imports)]
        use crate::codegen_prelude::*;

        #( #code )*
    })
}

impl std::str::FromStr for Mode {
    type Err = miette::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "parse" => Ok(Self::Parse),
            "compile" => Ok(Self::Compile),
            other => Err(miette::Error::msg(format!(
                "expected one of 'parse' or 'compile' (found {other})"
            ))),
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// ignore-lexer-test FIXME #15679

/*!
 * Unicode-intensive string manipulations.
 *
 * This module provides functionality to `str` that requires the Unicode
 * methods provided by the UnicodeChar trait.
 */

use core::clone::Clone;
use core::cmp;
use core::slice::ImmutableSlice;
use core::iter::{Filter, AdditiveIterator, Iterator, DoubleEndedIterator};
use core::kinds::Sized;
use core::option::{Option, None, Some};
use core::str::{CharSplits, StrSlice};
use u_char;
use u_char::UnicodeChar;
use tables::grapheme::GraphemeCat;

/// An iterator over the words of a string, separated by a sequence of whitespace
pub type Words<'a> =
    Filter<'a, &'a str, CharSplits<'a, extern "Rust" fn(char) -> bool>>;

/// Methods for Unicode string slices
pub trait UnicodeStrSlice for Sized? {
    /// Returns an iterator over the
    /// [grapheme clusters](http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries)
    /// of the string.
    ///
    /// If `is_extended` is true, the iterator is over the *extended grapheme clusters*;
    /// otherwise, the iterator is over the *legacy grapheme clusters*.
    /// [UAX#29](http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries)
    /// recommends extended grapheme cluster boundaries for general processing.
    ///
    /// # Example
    ///
    /// ```rust
    /// let gr1 = "a\u0310e\u0301o\u0308\u0332".graphemes(true).collect::<Vec<&str>>();
    /// let b: &[_] = &["a\u0310", "e\u0301", "o\u0308\u0332"];
    /// assert_eq!(gr1.as_slice(), b);
    /// let gr2 = "a\r\nb🇷🇺🇸🇹".graphemes(true).collect::<Vec<&str>>();
    /// let b: &[_] = &["a", "\r\n", "b", "🇷🇺🇸🇹"];
    /// assert_eq!(gr2.as_slice(), b);
    /// ```
    fn graphemes<'a>(&'a self, is_extended: bool) -> Graphemes<'a>;

    /// Returns an iterator over the grapheme clusters of self and their byte offsets.
    /// See `graphemes()` method for more information.
    ///
    /// # Example
    ///
    /// ```rust
    /// let gr_inds = "a̐éö̲\r\n".grapheme_indices(true).collect::<Vec<(uint, &str)>>();
    /// let b: &[_] = &[(0u, "a̐"), (3, "é"), (6, "ö̲"), (11, "\r\n")];
    /// assert_eq!(gr_inds.as_slice(), b);
    /// ```
    fn grapheme_indices<'a>(&'a self, is_extended: bool) -> GraphemeIndices<'a>;

    /// An iterator over the words of a string (subsequences separated
    /// by any sequence of whitespace). Sequences of whitespace are
    /// collapsed, so empty "words" are not included.
    ///
    /// # Example
    ///
    /// ```rust
    /// let some_words = " Mary   had\ta little  \n\t lamb";
    /// let v: Vec<&str> = some_words.words().collect();
    /// assert_eq!(v, vec!["Mary", "had", "a", "little", "lamb"]);
    /// ```
    fn words<'a>(&'a self) -> Words<'a>;

    /// Returns true if the string contains only whitespace.
    ///
    /// Whitespace characters are determined by `char::is_whitespace`.
    ///
    /// # Example
    ///
    /// ```rust
    /// assert!(" \t\n".is_whitespace());
    /// assert!("".is_whitespace());
    ///
    /// assert!( !"abc".is_whitespace());
    /// ```
    fn is_whitespace(&self) -> bool;

    /// Returns true if the string contains only alphanumeric code
    /// points.
    ///
    /// Alphanumeric characters are determined by `char::is_alphanumeric`.
    ///
    /// # Example
    ///
    /// ```rust
    /// assert!("Löwe老虎Léopard123".is_alphanumeric());
    /// assert!("".is_alphanumeric());
    ///
    /// assert!( !" &*~".is_alphanumeric());
    /// ```
    fn is_alphanumeric(&self) -> bool;

    /// Returns a string's displayed width in columns, treating control
    /// characters as zero-width.
    ///
    /// `is_cjk` determines behavior for characters in the Ambiguous category:
    /// if `is_cjk` is `true`, these are 2 columns wide; otherwise, they are 1.
    /// In CJK locales, `is_cjk` should be `true`, else it should be `false`.
    /// [Unicode Standard Annex #11](http://www.unicode.org/reports/tr11/)
    /// recommends that these characters be treated as 1 column (i.e.,
    /// `is_cjk` = `false`) if the locale is unknown.
    fn width(&self, is_cjk: bool) -> uint;

    /// Returns a string with leading and trailing whitespace removed.
    fn trim<'a>(&'a self) -> &'a str;

    /// Returns a string with leading whitespace removed.
    fn trim_left<'a>(&'a self) -> &'a str;

    /// Returns a string with trailing whitespace removed.
    fn trim_right<'a>(&'a self) -> &'a str;
}

impl UnicodeStrSlice for str {
    #[inline]
    fn graphemes(&self, is_extended: bool) -> Graphemes {
        Graphemes { string: self, extended: is_extended, cat: None, catb: None }
    }

    #[inline]
    fn grapheme_indices(&self, is_extended: bool) -> GraphemeIndices {
        GraphemeIndices { start_offset: self.as_ptr() as uint, iter: self.graphemes(is_extended) }
    }

    #[inline]
    fn words(&self) -> Words {
        self.split(u_char::is_whitespace).filter(|s| !s.is_empty())
    }

    #[inline]
    fn is_whitespace(&self) -> bool { self.chars().all(u_char::is_whitespace) }

    #[inline]
    fn is_alphanumeric(&self) -> bool { self.chars().all(u_char::is_alphanumeric) }

    #[inline]
    fn width(&self, is_cjk: bool) -> uint {
        self.chars().map(|c| c.width(is_cjk).unwrap_or(0)).sum()
    }

    #[inline]
    fn trim(&self) -> &str {
        self.trim_left().trim_right()
    }

    #[inline]
    fn trim_left(&self) -> &str {
        self.trim_left_chars(u_char::is_whitespace)
    }

    #[inline]
    fn trim_right(&self) -> &str {
        self.trim_right_chars(u_char::is_whitespace)
    }
}

/// External iterator for grapheme clusters and byte offsets.
#[deriving(Clone)]
pub struct GraphemeIndices<'a> {
    start_offset: uint,
    iter: Graphemes<'a>,
}

impl<'a> Iterator<(uint, &'a str)> for GraphemeIndices<'a> {
    #[inline]
    fn next(&mut self) -> Option<(uint, &'a str)> {
        self.iter.next().map(|s| (s.as_ptr() as uint - self.start_offset, s))
    }

    #[inline]
    fn size_hint(&self) -> (uint, Option<uint>) {
        self.iter.size_hint()
    }
}

impl<'a> DoubleEndedIterator<(uint, &'a str)> for GraphemeIndices<'a> {
    #[inline]
    fn next_back(&mut self) -> Option<(uint, &'a str)> {
        self.iter.next_back().map(|s| (s.as_ptr() as uint - self.start_offset, s))
    }
}

/// External iterator for a string's
/// [grapheme clusters](http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries).
#[deriving(Clone)]
pub struct Graphemes<'a> {
    string: &'a str,
    extended: bool,
    cat: Option<GraphemeCat>,
    catb: Option<GraphemeCat>,
}

// state machine for cluster boundary rules
#[deriving(PartialEq,Eq)]
enum GraphemeState {
    Start,
    FindExtend,
    HangulL,
    HangulLV,
    HangulLVT,
    Regional,
}

impl<'a> Iterator<&'a str> for Graphemes<'a> {
    #[inline]
    fn size_hint(&self) -> (uint, Option<uint>) {
        let slen = self.string.len();
        (cmp::min(slen, 1u), Some(slen))
    }

    #[inline]
    fn next(&mut self) -> Option<&'a str> {
        use tables::grapheme as gr;
        if self.string.len() == 0 {
            return None;
        }

        let mut take_curr = true;
        let mut idx = 0;
        let mut state = Start;
        let mut cat = gr::GC_Any;
        for (curr, ch) in self.string.char_indices() {
            idx = curr;

            // retrieve cached category, if any
            // We do this because most of the time we would end up
            // looking up each character twice.
            cat = match self.cat {
                None => gr::grapheme_category(ch),
                _ => self.cat.take().unwrap()
            };

            if match cat {
                gr::GC_Extend => true,
                gr::GC_SpacingMark if self.extended => true,
                _ => false
            } {
                    state = FindExtend;     // rule GB9/GB9a
                    continue;
            }

            state = match state {
                Start if '\r' == ch => {
                    let slen = self.string.len();
                    let nidx = idx + 1;
                    if nidx != slen && self.string.char_at(nidx) == '\n' {
                        idx = nidx;             // rule GB3
                    }
                    break;                      // rule GB4
                }
                Start => match cat {
                    gr::GC_Control => break,
                    gr::GC_L => HangulL,
                    gr::GC_LV | gr::GC_V => HangulLV,
                    gr::GC_LVT | gr::GC_T => HangulLVT,
                    gr::GC_RegionalIndicator => Regional,
                    _ => FindExtend
                },
                FindExtend => {         // found non-extending when looking for extending
                    take_curr = false;
                    break;
                },
                HangulL => match cat {      // rule GB6: L x (L|V|LV|LVT)
                    gr::GC_L => continue,
                    gr::GC_LV | gr::GC_V => HangulLV,
                    gr::GC_LVT => HangulLVT,
                    _ => {
                        take_curr = false;
                        break;
                    }
                },
                HangulLV => match cat {     // rule GB7: (LV|V) x (V|T)
                    gr::GC_V => continue,
                    gr::GC_T => HangulLVT,
                    _ => {
                        take_curr = false;
                        break;
                    }
                },
                HangulLVT => match cat {    // rule GB8: (LVT|T) x T
                    gr::GC_T => continue,
                    _ => {
                        take_curr = false;
                        break;
                    }
                },
                Regional => match cat {     // rule GB8a
                    gr::GC_RegionalIndicator => continue,
                    _ => {
                        take_curr = false;
                        break;
                    }
                }
            }
        }

        self.cat = if take_curr {
            idx = self.string.char_range_at(idx).next;
            None
        } else {
            Some(cat)
        };

        let retstr = self.string.slice_to(idx);
        self.string = self.string.slice_from(idx);
        Some(retstr)
    }
}

impl<'a> DoubleEndedIterator<&'a str> for Graphemes<'a> {
    #[inline]
    fn next_back(&mut self) -> Option<&'a str> {
        use tables::grapheme as gr;
        if self.string.len() == 0 {
            return None;
        }

        let mut take_curr = true;
        let mut idx = self.string.len();
        let mut previdx = idx;
        let mut state = Start;
        let mut cat = gr::GC_Any;
        for (curr, ch) in self.string.char_indices().rev() {
            previdx = idx;
            idx = curr;

            // cached category, if any
            cat = match self.catb {
                None => gr::grapheme_category(ch),
                _ => self.catb.take().unwrap()
            };

            // a matching state machine that runs *backwards* across an input string
            // note that this has some implications for the Hangul matching, since
            // we now need to know what the rightward letter is:
            //
            // Right to left, we have:
            //      L x L
            //      V x (L|V|LV)
            //      T x (V|T|LV|LVT)
            // HangulL means the letter to the right is L
            // HangulLV means the letter to the right is V
            // HangulLVT means the letter to the right is T
            state = match state {
                Start if '\n' == ch => {
                    if idx > 0 && '\r' == self.string.char_at_reverse(idx) {
                        idx -= 1;       // rule GB3
                    }
                    break;              // rule GB4
                },
                Start | FindExtend => match cat {
                    gr::GC_Extend => FindExtend,
                    gr::GC_SpacingMark if self.extended => FindExtend,
                    gr::GC_L | gr::GC_LV | gr::GC_LVT => HangulL,
                    gr::GC_V => HangulLV,
                    gr::GC_T => HangulLVT,
                    gr::GC_RegionalIndicator => Regional,
                    gr::GC_Control => {
                        take_curr = Start == state;
                        break;
                    },
                    _ => break
                },
                HangulL => match cat {      // char to right is an L
                    gr::GC_L => continue,               // L x L is the only legal match
                    _ => {
                        take_curr = false;
                        break;
                    }
                },
                HangulLV => match cat {     // char to right is a V
                    gr::GC_V => continue,               // V x V, right char is still V
                    gr::GC_L | gr::GC_LV => HangulL,    // (L|V) x V, right char is now L
                    _ => {
                        take_curr = false;
                        break;
                    }
                },
                HangulLVT => match cat {    // char to right is a T
                    gr::GC_T => continue,               // T x T, right char is still T
                    gr::GC_V => HangulLV,               // V x T, right char is now V
                    gr::GC_LV | gr::GC_LVT => HangulL,  // (LV|LVT) x T, right char is now L
                    _ => {
                        take_curr = false;
                        break;
                    }
                },
                Regional => match cat {     // rule GB8a
                    gr::GC_RegionalIndicator => continue,
                    _ => {
                        take_curr = false;
                        break;
                    }
                }
            }
        }

        self.catb = if take_curr {
            None
        } else  {
            idx = previdx;
            Some(cat)
        };

        let retstr = self.string.slice_from(idx);
        self.string = self.string.slice_to(idx);
        Some(retstr)
    }
}
<commit_msg>unicode: Fix fallout of changing `#[deriving(Clone)]`<commit_after>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// ignore-lexer-test FIXME #15679

/*!
 * Unicode-intensive string manipulations.
 *
 * This module provides functionality to `str` that requires the Unicode
 * methods provided by the UnicodeChar trait.
 */

use core::cmp;
use core::slice::ImmutableSlice;
use core::iter::{Filter, AdditiveIterator, Iterator, DoubleEndedIterator};
use core::kinds::Sized;
use core::option::{Option, None, Some};
use core::str::{CharSplits, StrSlice};
use u_char;
use u_char::UnicodeChar;
use tables::grapheme::GraphemeCat;

/// An iterator over the words of a string, separated by a sequence of whitespace
pub type Words<'a> =
    Filter<'a, &'a str, CharSplits<'a, extern "Rust" fn(char) -> bool>>;

/// Methods for Unicode string slices
pub trait UnicodeStrSlice for Sized? {
    /// Returns an iterator over the
    /// [grapheme clusters](http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries)
    /// of the string.
    ///
    /// If `is_extended` is true, the iterator is over the *extended grapheme clusters*;
    /// otherwise, the iterator is over the *legacy grapheme clusters*.
    /// [UAX#29](http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries)
    /// recommends extended grapheme cluster boundaries for general processing.
    ///
    /// # Example
    ///
    /// ```rust
    /// let gr1 = "a\u0310e\u0301o\u0308\u0332".graphemes(true).collect::<Vec<&str>>();
    /// let b: &[_] = &["a\u0310", "e\u0301", "o\u0308\u0332"];
    /// assert_eq!(gr1.as_slice(), b);
    /// let gr2 = "a\r\nb🇷🇺🇸🇹".graphemes(true).collect::<Vec<&str>>();
    /// let b: &[_] = &["a", "\r\n", "b", "🇷🇺🇸🇹"];
    /// assert_eq!(gr2.as_slice(), b);
    /// ```
    fn graphemes<'a>(&'a self, is_extended: bool) -> Graphemes<'a>;

    /// Returns an iterator over the grapheme clusters of self and their byte offsets.
    /// See `graphemes()` method for more information.
    ///
    /// # Example
    ///
    /// ```rust
    /// let gr_inds = "a̐éö̲\r\n".grapheme_indices(true).collect::<Vec<(uint, &str)>>();
    /// let b: &[_] = &[(0u, "a̐"), (3, "é"), (6, "ö̲"), (11, "\r\n")];
    /// assert_eq!(gr_inds.as_slice(), b);
    /// ```
    fn grapheme_indices<'a>(&'a self, is_extended: bool) -> GraphemeIndices<'a>;

    /// An iterator over the words of a string (subsequences separated
    /// by any sequence of whitespace). Sequences of whitespace are
    /// collapsed, so empty "words" are not included.
    ///
    /// # Example
    ///
    /// ```rust
    /// let some_words = " Mary   had\ta little  \n\t lamb";
    /// let v: Vec<&str> = some_words.words().collect();
    /// assert_eq!(v, vec!["Mary", "had", "a", "little", "lamb"]);
    /// ```
    fn words<'a>(&'a self) -> Words<'a>;

    /// Returns true if the string contains only whitespace.
    ///
    /// Whitespace characters are determined by `char::is_whitespace`.
    ///
    /// # Example
    ///
    /// ```rust
    /// assert!(" \t\n".is_whitespace());
    /// assert!("".is_whitespace());
    ///
    /// assert!( !"abc".is_whitespace());
    /// ```
    fn is_whitespace(&self) -> bool;

    /// Returns true if the string contains only alphanumeric code
    /// points.
    ///
    /// Alphanumeric characters are determined by `char::is_alphanumeric`.
    ///
    /// # Example
    ///
    /// ```rust
    /// assert!("Löwe老虎Léopard123".is_alphanumeric());
    /// assert!("".is_alphanumeric());
    ///
    /// assert!( !" &*~".is_alphanumeric());
    /// ```
    fn is_alphanumeric(&self) -> bool;

    /// Returns a string's displayed width in columns, treating control
    /// characters as zero-width.
    ///
    /// `is_cjk` determines behavior for characters in the Ambiguous category:
    /// if `is_cjk` is `true`, these are 2 columns wide; otherwise, they are 1.
    /// In CJK locales, `is_cjk` should be `true`, else it should be `false`.
    /// [Unicode Standard Annex #11](http://www.unicode.org/reports/tr11/)
    /// recommends that these characters be treated as 1 column (i.e.,
    /// `is_cjk` = `false`) if the locale is unknown.
    fn width(&self, is_cjk: bool) -> uint;

    /// Returns a string with leading and trailing whitespace removed.
    fn trim<'a>(&'a self) -> &'a str;

    /// Returns a string with leading whitespace removed.
    fn trim_left<'a>(&'a self) -> &'a str;

    /// Returns a string with trailing whitespace removed.
    fn trim_right<'a>(&'a self) -> &'a str;
}

impl UnicodeStrSlice for str {
    #[inline]
    fn graphemes(&self, is_extended: bool) -> Graphemes {
        Graphemes { string: self, extended: is_extended, cat: None, catb: None }
    }

    #[inline]
    fn grapheme_indices(&self, is_extended: bool) -> GraphemeIndices {
        GraphemeIndices { start_offset: self.as_ptr() as uint, iter: self.graphemes(is_extended) }
    }

    #[inline]
    fn words(&self) -> Words {
        self.split(u_char::is_whitespace).filter(|s| !s.is_empty())
    }

    #[inline]
    fn is_whitespace(&self) -> bool { self.chars().all(u_char::is_whitespace) }

    #[inline]
    fn is_alphanumeric(&self) -> bool { self.chars().all(u_char::is_alphanumeric) }

    #[inline]
    fn width(&self, is_cjk: bool) -> uint {
        self.chars().map(|c| c.width(is_cjk).unwrap_or(0)).sum()
    }

    #[inline]
    fn trim(&self) -> &str {
        self.trim_left().trim_right()
    }

    #[inline]
    fn trim_left(&self) -> &str {
        self.trim_left_chars(u_char::is_whitespace)
    }

    #[inline]
    fn trim_right(&self) -> &str {
        self.trim_right_chars(u_char::is_whitespace)
    }
}

/// External iterator for grapheme clusters and byte offsets.
#[deriving(Clone)]
pub struct GraphemeIndices<'a> {
    start_offset: uint,
    iter: Graphemes<'a>,
}

impl<'a> Iterator<(uint, &'a str)> for GraphemeIndices<'a> {
    #[inline]
    fn next(&mut self) -> Option<(uint, &'a str)> {
        self.iter.next().map(|s| (s.as_ptr() as uint - self.start_offset, s))
    }

    #[inline]
    fn size_hint(&self) -> (uint, Option<uint>) {
        self.iter.size_hint()
    }
}

impl<'a> DoubleEndedIterator<(uint, &'a str)> for GraphemeIndices<'a> {
    #[inline]
    fn next_back(&mut self) -> Option<(uint, &'a str)> {
        self.iter.next_back().map(|s| (s.as_ptr() as uint - self.start_offset, s))
    }
}

/// External iterator for a string's
/// [grapheme clusters](http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries).
#[deriving(Clone)]
pub struct Graphemes<'a> {
    string: &'a str,
    extended: bool,
    cat: Option<GraphemeCat>,
    catb: Option<GraphemeCat>,
}

// state machine for cluster boundary rules
#[deriving(PartialEq,Eq)]
enum GraphemeState {
    Start,
    FindExtend,
    HangulL,
    HangulLV,
    HangulLVT,
    Regional,
}

impl<'a> Iterator<&'a str> for Graphemes<'a> {
    #[inline]
    fn size_hint(&self) -> (uint, Option<uint>) {
        let slen = self.string.len();
        (cmp::min(slen, 1u), Some(slen))
    }

    #[inline]
    fn next(&mut self) -> Option<&'a str> {
        use tables::grapheme as gr;
        if self.string.len() == 0 {
            return None;
        }

        let mut take_curr = true;
        let mut idx = 0;
        let mut state = Start;
        let mut cat = gr::GC_Any;
        for (curr, ch) in self.string.char_indices() {
            idx = curr;

            // retrieve cached category, if any
            // We do this because most of the time we would end up
            // looking up each character twice.
            cat = match self.cat {
                None => gr::grapheme_category(ch),
                _ => self.cat.take().unwrap()
            };

            if match cat {
                gr::GC_Extend => true,
                gr::GC_SpacingMark if self.extended => true,
                _ => false
            } {
                    state = FindExtend;     // rule GB9/GB9a
                    continue;
            }

            state = match state {
                Start if '\r' == ch => {
                    let slen = self.string.len();
                    let nidx = idx + 1;
                    if nidx != slen && self.string.char_at(nidx) == '\n' {
                        idx = nidx;             // rule GB3
                    }
                    break;                      // rule GB4
                }
                Start => match cat {
                    gr::GC_Control => break,
                    gr::GC_L => HangulL,
                    gr::GC_LV | gr::GC_V => HangulLV,
                    gr::GC_LVT | gr::GC_T => HangulLVT,
                    gr::GC_RegionalIndicator => Regional,
                    _ => FindExtend
                },
                FindExtend => {         // found non-extending when looking for extending
                    take_curr = false;
                    break;
                },
                HangulL => match cat {      // rule GB6: L x (L|V|LV|LVT)
                    gr::GC_L => continue,
                    gr::GC_LV | gr::GC_V => HangulLV,
                    gr::GC_LVT => HangulLVT,
                    _ => {
                        take_curr = false;
                        break;
                    }
                },
                HangulLV => match cat {     // rule GB7: (LV|V) x (V|T)
                    gr::GC_V => continue,
                    gr::GC_T => HangulLVT,
                    _ => {
                        take_curr = false;
                        break;
                    }
                },
                HangulLVT => match cat {    // rule GB8: (LVT|T) x T
                    gr::GC_T => continue,
                    _ => {
                        take_curr = false;
                        break;
                    }
                },
                Regional => match cat {     // rule GB8a
                    gr::GC_RegionalIndicator => continue,
                    _ => {
                        take_curr = false;
                        break;
                    }
                }
            }
        }

        self.cat = if take_curr {
            idx = self.string.char_range_at(idx).next;
            None
        } else {
            Some(cat)
        };

        let retstr = self.string.slice_to(idx);
        self.string = self.string.slice_from(idx);
        Some(retstr)
    }
}

impl<'a> DoubleEndedIterator<&'a str> for Graphemes<'a> {
    #[inline]
    fn next_back(&mut self) -> Option<&'a str> {
        use tables::grapheme as gr;
        if self.string.len() == 0 {
            return None;
        }

        let mut take_curr = true;
        let mut idx = self.string.len();
        let mut previdx = idx;
        let mut state = Start;
        let mut cat = gr::GC_Any;
        for (curr, ch) in self.string.char_indices().rev() {
            previdx = idx;
            idx = curr;

            // cached category, if any
            cat = match self.catb {
                None => gr::grapheme_category(ch),
                _ => self.catb.take().unwrap()
            };

            // a matching state machine that runs *backwards* across an input string
            // note that this has some implications for the Hangul matching, since
            // we now need to know what the rightward letter is:
            //
            // Right to left, we have:
            //      L x L
            //      V x (L|V|LV)
            //      T x (V|T|LV|LVT)
            // HangulL means the letter to the right is L
            // HangulLV means the letter to the right is V
            // HangulLVT means the letter to the right is T
            state = match state {
                Start if '\n' == ch => {
                    if idx > 0 && '\r' == self.string.char_at_reverse(idx) {
                        idx -= 1;       // rule GB3
                    }
                    break;              // rule GB4
                },
                Start | FindExtend => match cat {
                    gr::GC_Extend => FindExtend,
                    gr::GC_SpacingMark if self.extended => FindExtend,
                    gr::GC_L | gr::GC_LV | gr::GC_LVT => HangulL,
                    gr::GC_V => HangulLV,
                    gr::GC_T => HangulLVT,
                    gr::GC_RegionalIndicator => Regional,
                    gr::GC_Control => {
                        take_curr = Start == state;
                        break;
                    },
                    _ => break
                },
                HangulL => match cat {      // char to right is an L
                    gr::GC_L => continue,               // L x L is the only legal match
                    _ => {
                        take_curr = false;
                        break;
                    }
                },
                HangulLV => match cat {     // char to right is a V
                    gr::GC_V => continue,               // V x V, right char is still V
                    gr::GC_L | gr::GC_LV => HangulL,    // (L|V) x V, right char is now L
                    _ => {
                        take_curr = false;
                        break;
                    }
                },
                HangulLVT => match cat {    // char to right is a T
                    gr::GC_T => continue,               // T x T, right char is still T
                    gr::GC_V => HangulLV,               // V x T, right char is now V
                    gr::GC_LV | gr::GC_LVT => HangulL,  // (LV|LVT) x T, right char is now L
                    _ => {
                        take_curr = false;
                        break;
                    }
                },
                Regional => match cat {     // rule GB8a
                    gr::GC_RegionalIndicator => continue,
                    _ => {
                        take_curr = false;
                        break;
                    }
                }
            }
        }

        self.catb = if take_curr {
            None
        } else  {
            idx = previdx;
            Some(cat)
        };

        let retstr = self.string.slice_from(idx);
        self.string = self.string.slice_to(idx);
        Some(retstr)
    }
}
<|endoftext|>