<commit_before>#![crate_name = "uu_tail"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Morten Olsen Lysgaard <morten@lysgaard.no>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 */

extern crate getopts;

#[macro_use]
extern crate uucore;

use std::collections::VecDeque;
use std::fs::File;
use std::io::{BufRead, BufReader, Read, stdin, stdout, Write};
use std::path::Path;
use std::str::from_utf8;
use std::thread::sleep;
use std::time::Duration;

static NAME: &'static str = "tail";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");

pub fn uumain(args: Vec<String>) -> i32 {
    let mut beginning = false;
    let mut lines = true;
    let mut byte_count = 0usize;
    let mut line_count = 10usize;
    let mut sleep_msec = 1000u32;

    // handle obsolete -number syntax
    let options = match obsolete(&args[1..]) {
        (args, Some(n)) => { line_count = n; args },
        (args, None) => args
    };

    let args = options;

    let mut opts = getopts::Options::new();

    opts.optopt("c", "bytes", "Number of bytes to print", "k");
    opts.optopt("n", "lines", "Number of lines to print", "k");
    opts.optflag("f", "follow", "Print the file as it grows");
    opts.optopt("s", "sleep-interval", "Number or seconds to sleep between polling the file when running with -f", "n");
    opts.optflag("h", "help", "help");
    opts.optflag("V", "version", "version");

    let given_options = match opts.parse(&args) {
        Ok (m) => { m }
        Err(_) => {
            println!("{}", opts.usage(""));
            return 1;
        }
    };

    if given_options.opt_present("h") {
        println!("{}", opts.usage(""));
        return 0;
    }
    if given_options.opt_present("V") { version(); return 0 }

    let follow = given_options.opt_present("f");
    if follow {
        match given_options.opt_str("s") {
            Some(n) => {
                let parsed: Option<u32> = n.parse().ok();
                match parsed {
                    Some(m) => { sleep_msec = m * 1000 }
                    None => {}
                }
            }
            None => {}
        };
    }

    match given_options.opt_str("n") {
        Some(n) => {
            let mut slice: &str = n.as_ref();
            if slice.chars().next().unwrap_or('_') == '+' {
                beginning = true;
                slice = &slice[1..];
            }
            line_count = match parse_size(slice) {
                Some(m) => m,
                None => {
                    show_error!("invalid number of lines ({})", slice);
                    return 1;
                }
            };
        }
        None => match given_options.opt_str("c") {
            Some(n) => {
                let mut slice: &str = n.as_ref();
                if slice.chars().next().unwrap_or('_') == '+' {
                    beginning = true;
                    slice = &slice[1..];
                }
                byte_count = match parse_size(slice) {
                    Some(m) => m,
                    None => {
                        show_error!("invalid number of bytes ({})", slice);
                        return 1;
                    }
                };
                lines = false;
            }
            None => { }
        }
    };

    let files = given_options.free;

    if files.is_empty() {
        let mut buffer = BufReader::new(stdin());
        tail(&mut buffer, line_count, byte_count, beginning, lines, follow, sleep_msec);
    } else {
        let mut multiple = false;
        let mut firstime = true;

        if files.len() > 1 {
            multiple = true;
        }

        for file in files.iter() {
            if multiple {
                if !firstime { println!(""); }
                println!("==> {} <==", file);
            }
            firstime = false;

            let path = Path::new(file);
            let reader = File::open(&path).unwrap();
            let mut buffer = BufReader::new(reader);
            tail(&mut buffer, line_count, byte_count, beginning, lines, follow, sleep_msec);
        }
    }

    0
}

fn parse_size(mut size_slice: &str) -> Option<usize> {
    let mut base =
        if size_slice.chars().last().unwrap_or('_') == 'B' {
            size_slice = &size_slice[..size_slice.len() - 1];
            1000usize
        } else {
            1024usize
        };
    let exponent =
        if size_slice.len() > 0 {
            let mut has_suffix = true;
            let exp = match size_slice.chars().last().unwrap_or('_') {
                'K' => 1usize,
                'M' => 2usize,
                'G' => 3usize,
                'T' => 4usize,
                'P' => 5usize,
                'E' => 6usize,
                'Z' => 7usize,
                'Y' => 8usize,
                'b' => {
                    base = 512usize;
                    1usize
                }
                _ => {
                    has_suffix = false;
                    0usize
                }
            };
            if has_suffix {
                size_slice = &size_slice[..size_slice.len() - 1];
            }
            exp
        } else {
            0usize
        };

    let mut multiplier = 1usize;
    for _ in 0usize .. exponent {
        multiplier *= base;
    }
    if base == 1000usize && exponent == 0usize {
        // sole B is not a valid suffix
        None
    } else {
        let value: Option<usize> = size_slice.parse().ok();
        match value {
            Some(v) => Some(multiplier * v),
            _ => None
        }
    }
}

// It searches for an option in the form of -123123
//
// In case is found, the options vector will get rid of that object so that
// getopts works correctly.
fn obsolete(options: &[String]) -> (Vec<String>, Option<usize>) {
    let mut options: Vec<String> = options.to_vec();
    let mut a = 0;
    let b = options.len();

    while a < b {
        let current = options[a].clone();
        let current = current.as_bytes();

        if current.len() > 1 && current[0] == '-' as u8 {
            let len = current.len();
            for pos in 1 .. len {
                // Ensure that the argument is only made out of digits
                if !(current[pos] as char).is_numeric() { break; }

                // If this is the last number
                if pos == len - 1 {
                    options.remove(a);
                    let number: Option<usize> = from_utf8(&current[1..len]).unwrap().parse().ok();
                    return (options, Some(number.unwrap()));
                }
            }
        }

        a += 1;
    };

    (options, None)
}

macro_rules! tail_impl (
    ($kind:ty, $kindfn:ident, $kindprint:ident, $reader:ident, $count:ident, $beginning:ident) => ({
        // read through each line and store them in a ringbuffer that always contains
        // count lines/chars. When reaching the end of file, output the data in the
        // ringbuf.
        let mut ringbuf: VecDeque<$kind> = VecDeque::new();
        let data = $reader.$kindfn().skip(
            if $beginning {
                let temp = $count;
                $count = ::std::usize::MAX;
                temp - 1
            } else {
                0
            }
        );
        for io_datum in data {
            match io_datum {
                Ok(datum) => {
                    if $count <= ringbuf.len() {
                        ringbuf.pop_front();
                    }
                    ringbuf.push_back(datum);
                }
                Err(err) => panic!(err)
            }
        }
        let mut stdout = stdout();
        for datum in ringbuf.iter() {
            $kindprint(&mut stdout, datum);
        }
    })
);

fn tail<T: Read>(reader: &mut BufReader<T>, mut line_count: usize, mut byte_count: usize, beginning: bool, lines: bool, follow: bool, sleep_msec: u32) {
    if lines {
        tail_impl!(String, lines, print_string, reader, line_count, beginning);
    } else {
        tail_impl!(u8, bytes, print_byte, reader, byte_count, beginning);
    }

    // if we follow the file, sleep a bit and print the rest if the file has grown.
    while follow {
        sleep(Duration::new(0, sleep_msec*1000));
        for io_line in reader.lines() {
            match io_line {
                Ok(line) => print!("{}", line),
                Err(err) => panic!(err)
            }
        }
    }
}

#[inline]
fn print_byte<T: Write>(stdout: &mut T, ch: &u8) {
    if let Err(err) = stdout.write(&[*ch]) {
        crash!(1, "{}", err);
    }
}

#[inline]
fn print_string<T: Write>(_: &mut T, s: &String) {
    print!("{}", s);
}

fn version () {
    println!("{} {}", NAME, VERSION);
}
<commit_msg>Fix whitespace<commit_after>#![crate_name = "uu_tail"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Morten Olsen Lysgaard <morten@lysgaard.no>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 */

extern crate getopts;

#[macro_use]
extern crate uucore;

use std::collections::VecDeque;
use std::fs::File;
use std::io::{BufRead, BufReader, Read, stdin, stdout, Write};
use std::path::Path;
use std::str::from_utf8;
use std::thread::sleep;
use std::time::Duration;

static NAME: &'static str = "tail";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");

pub fn uumain(args: Vec<String>) -> i32 {
    let mut beginning = false;
    let mut lines = true;
    let mut byte_count = 0usize;
    let mut line_count = 10usize;
    let mut sleep_msec = 1000u32;

    // handle obsolete -number syntax
    let options = match obsolete(&args[1..]) {
        (args, Some(n)) => { line_count = n; args },
        (args, None) => args
    };

    let args = options;

    let mut opts = getopts::Options::new();

    opts.optopt("c", "bytes", "Number of bytes to print", "k");
    opts.optopt("n", "lines", "Number of lines to print", "k");
    opts.optflag("f", "follow", "Print the file as it grows");
    opts.optopt("s", "sleep-interval", "Number or seconds to sleep between polling the file when running with -f", "n");
    opts.optflag("h", "help", "help");
    opts.optflag("V", "version", "version");

    let given_options = match opts.parse(&args) {
        Ok (m) => { m }
        Err(_) => {
            println!("{}", opts.usage(""));
            return 1;
        }
    };

    if given_options.opt_present("h") {
        println!("{}", opts.usage(""));
        return 0;
    }
    if given_options.opt_present("V") { version(); return 0 }

    let follow = given_options.opt_present("f");
    if follow {
        match given_options.opt_str("s") {
            Some(n) => {
                let parsed: Option<u32> = n.parse().ok();
                match parsed {
                    Some(m) => { sleep_msec = m * 1000 }
                    None => {}
                }
            }
            None => {}
        };
    }

    match given_options.opt_str("n") {
        Some(n) => {
            let mut slice: &str = n.as_ref();
            if slice.chars().next().unwrap_or('_') == '+' {
                beginning = true;
                slice = &slice[1..];
            }
            line_count = match parse_size(slice) {
                Some(m) => m,
                None => {
                    show_error!("invalid number of lines ({})", slice);
                    return 1;
                }
            };
        }
        None => match given_options.opt_str("c") {
            Some(n) => {
                let mut slice: &str = n.as_ref();
                if slice.chars().next().unwrap_or('_') == '+' {
                    beginning = true;
                    slice = &slice[1..];
                }
                byte_count = match parse_size(slice) {
                    Some(m) => m,
                    None => {
                        show_error!("invalid number of bytes ({})", slice);
                        return 1;
                    }
                };
                lines = false;
            }
            None => { }
        }
    };

    let files = given_options.free;

    if files.is_empty() {
        let mut buffer = BufReader::new(stdin());
        tail(&mut buffer, line_count, byte_count, beginning, lines, follow, sleep_msec);
    } else {
        let mut multiple = false;
        let mut firstime = true;

        if files.len() > 1 {
            multiple = true;
        }

        for file in files.iter() {
            if multiple {
                if !firstime { println!(""); }
                println!("==> {} <==", file);
            }
            firstime = false;

            let path = Path::new(file);
            let reader = File::open(&path).unwrap();
            let mut buffer = BufReader::new(reader);
            tail(&mut buffer, line_count, byte_count, beginning, lines, follow, sleep_msec);
        }
    }

    0
}

fn parse_size(mut size_slice: &str) -> Option<usize> {
    let mut base =
        if size_slice.chars().last().unwrap_or('_') == 'B' {
            size_slice = &size_slice[..size_slice.len() - 1];
            1000usize
        } else {
            1024usize
        };
    let exponent =
        if size_slice.len() > 0 {
            let mut has_suffix = true;
            let exp = match size_slice.chars().last().unwrap_or('_') {
                'K' => 1usize,
                'M' => 2usize,
                'G' => 3usize,
                'T' => 4usize,
                'P' => 5usize,
                'E' => 6usize,
                'Z' => 7usize,
                'Y' => 8usize,
                'b' => {
                    base = 512usize;
                    1usize
                }
                _ => {
                    has_suffix = false;
                    0usize
                }
            };
            if has_suffix {
                size_slice = &size_slice[..size_slice.len() - 1];
            }
            exp
        } else {
            0usize
        };

    let mut multiplier = 1usize;
    for _ in 0usize .. exponent {
        multiplier *= base;
    }
    if base == 1000usize && exponent == 0usize {
        // sole B is not a valid suffix
        None
    } else {
        let value: Option<usize> = size_slice.parse().ok();
        match value {
            Some(v) => Some(multiplier * v),
            _ => None
        }
    }
}

// It searches for an option in the form of -123123
//
// In case is found, the options vector will get rid of that object so that
// getopts works correctly.
fn obsolete(options: &[String]) -> (Vec<String>, Option<usize>) {
    let mut options: Vec<String> = options.to_vec();
    let mut a = 0;
    let b = options.len();

    while a < b {
        let current = options[a].clone();
        let current = current.as_bytes();

        if current.len() > 1 && current[0] == '-' as u8 {
            let len = current.len();
            for pos in 1 .. len {
                // Ensure that the argument is only made out of digits
                if !(current[pos] as char).is_numeric() { break; }

                // If this is the last number
                if pos == len - 1 {
                    options.remove(a);
                    let number: Option<usize> = from_utf8(&current[1..len]).unwrap().parse().ok();
                    return (options, Some(number.unwrap()));
                }
            }
        }

        a += 1;
    };

    (options, None)
}

macro_rules! tail_impl (
    ($kind:ty, $kindfn:ident, $kindprint:ident, $reader:ident, $count:ident, $beginning:ident) => ({
        // read through each line and store them in a ringbuffer that always contains
        // count lines/chars. When reaching the end of file, output the data in the
        // ringbuf.
        let mut ringbuf: VecDeque<$kind> = VecDeque::new();
        let data = $reader.$kindfn().skip(
            if $beginning {
                let temp = $count;
                $count = ::std::usize::MAX;
                temp - 1
            } else {
                0
            }
        );
        for io_datum in data {
            match io_datum {
                Ok(datum) => {
                    if $count <= ringbuf.len() {
                        ringbuf.pop_front();
                    }
                    ringbuf.push_back(datum);
                }
                Err(err) => panic!(err)
            }
        }
        let mut stdout = stdout();
        for datum in ringbuf.iter() {
            $kindprint(&mut stdout, datum);
        }
    })
);

fn tail<T: Read>(reader: &mut BufReader<T>, mut line_count: usize, mut byte_count: usize, beginning: bool, lines: bool, follow: bool, sleep_msec: u32) {
    if lines {
        tail_impl!(String, lines, print_string, reader, line_count, beginning);
    } else {
        tail_impl!(u8, bytes, print_byte, reader, byte_count, beginning);
    }

    // if we follow the file, sleep a bit and print the rest if the file has grown.
    while follow {
        sleep(Duration::new(0, sleep_msec*1000));
        for io_line in reader.lines() {
            match io_line {
                Ok(line) => print!("{}", line),
                Err(err) => panic!(err)
            }
        }
    }
}

#[inline]
fn print_byte<T: Write>(stdout: &mut T, ch: &u8) {
    if let Err(err) = stdout.write(&[*ch]) {
        crash!(1, "{}", err);
    }
}

#[inline]
fn print_string<T: Write>(_: &mut T, s: &String) {
    print!("{}", s);
}

fn version() {
    println!("{} {}", NAME, VERSION);
}
<|endoftext|>


<commit_before>// This whole file is strongly inspired by: https://github.com/jeaye/q3/blob/master/src/client/ui/ttf/font.rs
// available under the BSD-3 licence.
// It has been modified to work with gl-rs, nalgebra, and rust-freetype

use std::mem;
use std::slice;
use std::ffi::CString;
use std::rc::Rc;
use std::cmp;
use std::ptr;
use std::path::Path;
use libc::c_uint;
use gl;
use gl::types::*;
use freetype::ffi;
use na::Vec2;
use na;
use text::Glyph;

#[path = "../error.rs"]
mod error;

/// A ttf font.
pub struct Font {
    library:          ffi::FT_Library,
    face:             ffi::FT_Face,
    texture_atlas:    GLuint,
    atlas_dimensions: Vec2<usize>,
    glyphs:           Vec<Option<Glyph>>,
    height:           i32,
}

impl Font {
    /// Loads a new ttf font from the memory.
    pub fn from_memory(font: &[u8], size: i32) -> Rc<Font> {
        Font::do_new(None, font, size)
    }

    /// Loads a new ttf font from a file.
    pub fn new(path: &Path, size: i32) -> Rc<Font> {
        Font::do_new(Some(path), &[], size)
    }

    /// Loads a new ttf font from a file.
    pub fn do_new(path: Option<&Path>, memory: &[u8], size: i32) -> Rc<Font> {
        let mut font = Font {
            library:          ptr::null_mut(),
            face:             ptr::null_mut(),
            texture_atlas:    0,
            atlas_dimensions: na::zero(),
            glyphs:           (0 .. 128).map(|_:isize| None).collect(),
            height:           0
        };

        unsafe {
            let _ = ffi::FT_Init_FreeType(&mut font.library);

            match path {
                Some(path) => {
                    let path = path.to_str().expect("Invalid path.");
                    let c_str = CString::new(path.as_bytes()).unwrap();
                    if ffi::FT_New_Face(font.library, c_str.as_ptr(), 0, &mut font.face) != 0 {
                        panic!("Failed to create TTF face.");
                    }
                },
                None => {
                    if ffi::FT_New_Memory_Face(font.library, &memory[0], memory.len() as i64, 0, &mut font.face) != 0 {
                        panic!("Failed to create TTF face.");
                    }
                }
            }

            let _ = ffi::FT_Set_Pixel_Sizes(font.face, 0, size as c_uint);
            verify!(gl::ActiveTexture(gl::TEXTURE0));

            let     ft_glyph   = (*font.face).glyph;
            let     max_width  = 1024;
            let mut row_width  = 0;
            let mut row_height = 0;

            for curr in (0usize .. 128) {
                if ffi::FT_Load_Char(font.face, curr as u64, ffi::FT_LOAD_RENDER) != 0 {
                    continue;
                }

                /* If we've exhausted the width for this row, add another. */
                if row_width + (*ft_glyph).bitmap.width + 1 >= max_width {
                    font.atlas_dimensions.x = cmp::max(font.atlas_dimensions.x, row_width as usize);
                    font.atlas_dimensions.y = font.atlas_dimensions.y + row_height;
                    row_width = 0; row_height = 0;
                }

                let advance    = Vec2::new(((*ft_glyph).advance.x >> 6) as f32, ((*ft_glyph).advance.y >> 6) as f32);
                let dimensions = Vec2::new((*ft_glyph).bitmap.width as f32, (*ft_glyph).bitmap.rows as f32);
                let offset     = Vec2::new((*ft_glyph).bitmap_left as f32, (*ft_glyph).bitmap_top as f32);
                let buf_len    = (dimensions.x * dimensions.y) as usize;
                let buffer     = slice::from_raw_parts(&*(*ft_glyph).bitmap.buffer, buf_len).to_vec();
                let glyph      = Glyph::new(na::zero(), advance, dimensions, offset, buffer);

                row_width   = row_width + (dimensions.x + 1.0) as i32;
                row_height  = cmp::max(row_height, (*ft_glyph).bitmap.rows as usize);
                font.height = cmp::max(font.height, row_height as i32);

                font.glyphs[curr] = Some(glyph);
            }

            font.atlas_dimensions.x = (cmp::max(font.atlas_dimensions.x, row_width as usize)).next_power_of_two();
            font.atlas_dimensions.y = (font.atlas_dimensions.y + row_height).next_power_of_two();

            /* We're using 1 byte alignment buffering. */
            verify!(gl::PixelStorei(gl::UNPACK_ALIGNMENT, 1));

            verify!(gl::GenTextures(1, &mut font.texture_atlas));
            verify!(gl::BindTexture(gl::TEXTURE_2D, font.texture_atlas));
            verify!(gl::TexImage2D(gl::TEXTURE_2D, 0, gl::RGB as GLint,
                                   font.atlas_dimensions.x as i32, font.atlas_dimensions.y as i32,
                                   0, gl::RED, gl::UNSIGNED_BYTE, ptr::null()));

            /* Clamp to the edge to avoid artifacts when scaling. */
            verify!(gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32));
            verify!(gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32));

            /* Linear filtering usually looks best for text. */
            verify!(gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32));
            verify!(gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32));

            /* Copy all glyphs into the texture atlas. */
            let mut offset: Vec2<i32> = na::zero();
            row_height = 0;
            for curr in (0usize .. 128) {
                let glyph = match *&mut font.glyphs[curr] {
                    Some(ref mut g) => g,
                    None            => continue
                };

                if offset.x + (glyph.dimensions.x as i32) + 1 >= max_width {
                    offset.y   = offset.y + row_height as i32;
                    row_height = 0;
                    offset.x   = 0;
                }

                if !glyph.buffer.is_empty() {
                    verify!(gl::TexSubImage2D(
                                gl::TEXTURE_2D, 0, offset.x, offset.y,
                                glyph.dimensions.x as i32, glyph.dimensions.y as i32,
                                gl::RED, gl::UNSIGNED_BYTE, mem::transmute(&glyph.buffer[0])));
                }

                /* Calculate the position in the texture. */
                glyph.tex.x = offset.x as f32 / (font.atlas_dimensions.x as f32);
                glyph.tex.y = offset.y as f32 / (font.atlas_dimensions.y as f32);

                offset.x   = offset.x + glyph.dimensions.x as i32;
                row_height = cmp::max(row_height, glyph.dimensions.y as usize);
            }
        }

        /* Reset the state. */
        verify!(gl::PixelStorei(gl::UNPACK_ALIGNMENT, 4));

        assert!(font.height > 0);

        Rc::new(font)
    }

    /// The opengl id to the texture atlas of this font.
    #[inline]
    pub fn texture_atlas(&self) -> GLuint {
        self.texture_atlas
    }

    /// The dimensions of the texture atlas of this font.
    #[inline]
    pub fn atlas_dimensions(&self) -> Vec2<usize> {
        self.atlas_dimensions
    }

    /// The glyphs of the this font.
    #[inline]
    pub fn glyphs(&self) -> &[Option<Glyph>] {
        &self.glyphs[..]
    }

    /// The height of this font.
    #[inline]
    pub fn height(&self) -> i32 {
        self.height
    }
}

impl Drop for Font {
    fn drop(&mut self) {
        unsafe {
            let _ = ffi::FT_Done_FreeType(self.library);
            verify!(gl::DeleteTextures(1, &self.texture_atlas));
        }
    }
}
<commit_msg>Cast to FT_Long and FT_ULong instead of i64 and u64<commit_after>// This whole file is strongly inspired by: https://github.com/jeaye/q3/blob/master/src/client/ui/ttf/font.rs
// available under the BSD-3 licence.
// It has been modified to work with gl-rs, nalgebra, and rust-freetype

use std::mem;
use std::slice;
use std::ffi::CString;
use std::rc::Rc;
use std::cmp;
use std::ptr;
use std::path::Path;
use libc::c_uint;
use gl;
use gl::types::*;
use freetype::ffi;
use na::Vec2;
use na;
use text::Glyph;

#[path = "../error.rs"]
mod error;

/// A ttf font.
pub struct Font {
    library:          ffi::FT_Library,
    face:             ffi::FT_Face,
    texture_atlas:    GLuint,
    atlas_dimensions: Vec2<usize>,
    glyphs:           Vec<Option<Glyph>>,
    height:           i32,
}

impl Font {
    /// Loads a new ttf font from the memory.
    pub fn from_memory(font: &[u8], size: i32) -> Rc<Font> {
        Font::do_new(None, font, size)
    }

    /// Loads a new ttf font from a file.
    pub fn new(path: &Path, size: i32) -> Rc<Font> {
        Font::do_new(Some(path), &[], size)
    }

    /// Loads a new ttf font from a file.
    pub fn do_new(path: Option<&Path>, memory: &[u8], size: i32) -> Rc<Font> {
        let mut font = Font {
            library:          ptr::null_mut(),
            face:             ptr::null_mut(),
            texture_atlas:    0,
            atlas_dimensions: na::zero(),
            glyphs:           (0 .. 128).map(|_:isize| None).collect(),
            height:           0
        };

        unsafe {
            let _ = ffi::FT_Init_FreeType(&mut font.library);

            match path {
                Some(path) => {
                    let path = path.to_str().expect("Invalid path.");
                    let c_str = CString::new(path.as_bytes()).unwrap();
                    if ffi::FT_New_Face(font.library, c_str.as_ptr(), 0, &mut font.face) != 0 {
                        panic!("Failed to create TTF face.");
                    }
                },
                None => {
                    if ffi::FT_New_Memory_Face(font.library, &memory[0], memory.len() as ffi::FT_Long, 0, &mut font.face) != 0 {
                        panic!("Failed to create TTF face.");
                    }
                }
            }

            let _ = ffi::FT_Set_Pixel_Sizes(font.face, 0, size as c_uint);
            verify!(gl::ActiveTexture(gl::TEXTURE0));

            let     ft_glyph   = (*font.face).glyph;
            let     max_width  = 1024;
            let mut row_width  = 0;
            let mut row_height = 0;

            for curr in (0usize .. 128) {
                if ffi::FT_Load_Char(font.face, curr as ffi::FT_ULong, ffi::FT_LOAD_RENDER) != 0 {
                    continue;
                }

                /* If we've exhausted the width for this row, add another. */
                if row_width + (*ft_glyph).bitmap.width + 1 >= max_width {
                    font.atlas_dimensions.x = cmp::max(font.atlas_dimensions.x, row_width as usize);
                    font.atlas_dimensions.y = font.atlas_dimensions.y + row_height;
                    row_width = 0; row_height = 0;
                }

                let advance    = Vec2::new(((*ft_glyph).advance.x >> 6) as f32, ((*ft_glyph).advance.y >> 6) as f32);
                let dimensions = Vec2::new((*ft_glyph).bitmap.width as f32, (*ft_glyph).bitmap.rows as f32);
                let offset     = Vec2::new((*ft_glyph).bitmap_left as f32, (*ft_glyph).bitmap_top as f32);
                let buf_len    = (dimensions.x * dimensions.y) as usize;
                let buffer     = slice::from_raw_parts(&*(*ft_glyph).bitmap.buffer, buf_len).to_vec();
                let glyph      = Glyph::new(na::zero(), advance, dimensions, offset, buffer);

                row_width   = row_width + (dimensions.x + 1.0) as i32;
                row_height  = cmp::max(row_height, (*ft_glyph).bitmap.rows as usize);
                font.height = cmp::max(font.height, row_height as i32);

                font.glyphs[curr] = Some(glyph);
            }

            font.atlas_dimensions.x = (cmp::max(font.atlas_dimensions.x, row_width as usize)).next_power_of_two();
            font.atlas_dimensions.y = (font.atlas_dimensions.y + row_height).next_power_of_two();

            /* We're using 1 byte alignment buffering. */
            verify!(gl::PixelStorei(gl::UNPACK_ALIGNMENT, 1));

            verify!(gl::GenTextures(1, &mut font.texture_atlas));
            verify!(gl::BindTexture(gl::TEXTURE_2D, font.texture_atlas));
            verify!(gl::TexImage2D(gl::TEXTURE_2D, 0, gl::RGB as GLint,
                                   font.atlas_dimensions.x as i32, font.atlas_dimensions.y as i32,
                                   0, gl::RED, gl::UNSIGNED_BYTE, ptr::null()));

            /* Clamp to the edge to avoid artifacts when scaling. */
            verify!(gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32));
            verify!(gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32));

            /* Linear filtering usually looks best for text. */
            verify!(gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32));
            verify!(gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32));

            /* Copy all glyphs into the texture atlas. */
            let mut offset: Vec2<i32> = na::zero();
            row_height = 0;
            for curr in (0usize .. 128) {
                let glyph = match *&mut font.glyphs[curr] {
                    Some(ref mut g) => g,
                    None            => continue
                };

                if offset.x + (glyph.dimensions.x as i32) + 1 >= max_width {
                    offset.y   = offset.y + row_height as i32;
                    row_height = 0;
                    offset.x   = 0;
                }

                if !glyph.buffer.is_empty() {
                    verify!(gl::TexSubImage2D(
                                gl::TEXTURE_2D, 0, offset.x, offset.y,
                                glyph.dimensions.x as i32, glyph.dimensions.y as i32,
                                gl::RED, gl::UNSIGNED_BYTE, mem::transmute(&glyph.buffer[0])));
                }

                /* Calculate the position in the texture. */
                glyph.tex.x = offset.x as f32 / (font.atlas_dimensions.x as f32);
                glyph.tex.y = offset.y as f32 / (font.atlas_dimensions.y as f32);

                offset.x   = offset.x + glyph.dimensions.x as i32;
                row_height = cmp::max(row_height, glyph.dimensions.y as usize);
            }
        }

        /* Reset the state. */
        verify!(gl::PixelStorei(gl::UNPACK_ALIGNMENT, 4));

        assert!(font.height > 0);

        Rc::new(font)
    }

    /// The opengl id to the texture atlas of this font.
    #[inline]
    pub fn texture_atlas(&self) -> GLuint {
        self.texture_atlas
    }

    /// The dimensions of the texture atlas of this font.
    #[inline]
    pub fn atlas_dimensions(&self) -> Vec2<usize> {
        self.atlas_dimensions
    }

    /// The glyphs of the this font.
    #[inline]
    pub fn glyphs(&self) -> &[Option<Glyph>] {
        &self.glyphs[..]
    }

    /// The height of this font.
    #[inline]
    pub fn height(&self) -> i32 {
        self.height
    }
}

impl Drop for Font {
    fn drop(&mut self) {
        unsafe {
            let _ = ffi::FT_Done_FreeType(self.library);
            verify!(gl::DeleteTextures(1, &self.texture_atlas));
        }
    }
}
<|endoftext|>


<commit_before>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! A tokenizer is a pattern matcher that is intended to turn a stream of symbols into another stream of symbols based on the patterns
//! that are matched. Every pattern can produce a different output symbol. If two input strings can ndfa in two different output
//! symbols, then the output symbol that is ordered lower is the one that's produced (ie, if the output symbols are numbers, then '0' will
//! be produced instead of '1' in the event of a clash)
//!

use super::countable::*;
use super::symbol_range::*;
use super::regular_pattern::*;
use super::state_machine::*;
use super::ndfa::*;
use super::prepare::*;
use super::symbol_range_dfa::*;
use super::symbol_reader::*;
use super::pattern_matcher::*;
use super::matches::*;
use super::tape::*;

///
/// Used for generating tokenizing pattern matchers
///
pub struct TokenMatcher<InputSymbol: Clone+Ord+Countable, OutputSymbol: Clone+Ord> {
    patterns: Vec<(Pattern<InputSymbol>, OutputSymbol)>
}

impl<InputSymbol: Clone+Ord+Countable+'static, OutputSymbol: Clone+Ord+'static> TokenMatcher<InputSymbol, OutputSymbol> {
    ///
    /// Creates a new TokenMatcher
    ///
    pub fn new() -> TokenMatcher<InputSymbol, OutputSymbol> {
        TokenMatcher { patterns: vec![] }
    }

    ///
    /// Adds a new pattern that will generate the specified output symbol
    ///
    pub fn add_pattern<TPattern: ToPattern<InputSymbol>>(&mut self, pattern: TPattern, output: OutputSymbol) {
        self.patterns.push((pattern.to_pattern(), output));
    }

    ///
    /// Compiles an NDFA from this TokenMatcher
    ///
    pub fn to_ndfa(&self) -> Box<StateMachine<SymbolRange<InputSymbol>, OutputSymbol>> {
        let mut ndfa = Ndfa::new();

        for &(ref pattern, ref output) in &self.patterns {
            // Compile each pattern starting at state 0
            let end_state = pattern.compile(&mut ndfa, 0);

            // Set the output for this pattern
            ndfa.set_output_symbol(end_state, output.clone());
        }

        // Clear out any overlapping ranges so we can build a valid DFA
        ndfa.fix_overlapping_ranges();

        Box::new(ndfa)
    }
}

impl<'a, InputSymbol: Clone+Ord+Countable+'static, OutputSymbol: Clone+Ord+'static> PrepareToMatch<SymbolRangeDfa<InputSymbol, OutputSymbol>>
for &'a TokenMatcher<InputSymbol, OutputSymbol> {
    #[inline]
    fn prepare_to_match(self) -> SymbolRangeDfa<InputSymbol, OutputSymbol> {
        let ndfa = self.to_ndfa();

        ndfa.prepare_to_match()
    }
}

///
/// A tokenizer is a type of symbol stream that uses a pattern matcher to convert a symbol stream into a stream of tokens
///
pub struct Tokenizer<InputSymbol: Clone+Ord+Countable, OutputSymbol: Clone+Ord, Reader: SymbolReader<InputSymbol>> {
    /// The pattern matcher for this tokenizer
    dfa: SymbolRangeDfa<InputSymbol, OutputSymbol>,

    /// Tape of input symbols that will be used to generate the result
    tape: Tape<InputSymbol, Reader>,
}

impl<InputSymbol: Clone+Ord+Countable, OutputSymbol: Clone+Ord, Reader: SymbolReader<InputSymbol>> Tokenizer<InputSymbol, OutputSymbol, Reader> {
    ///
    /// Creates a new tokenizer from a pattern (usually a TokenMatcher)
    ///
    pub fn new<Prepare: PrepareToMatch<SymbolRangeDfa<InputSymbol, OutputSymbol>>>(source: Reader, pattern: Prepare) -> Tokenizer<InputSymbol, OutputSymbol, Reader> {
        Tokenizer { dfa: pattern.prepare_to_match(), tape: Tape::new(source) }
    }

    ///
    /// Creates a new tokenizer from a prepared pattern
    ///
    pub fn new_prepared(source: Reader, pattern: SymbolRangeDfa<InputSymbol, OutputSymbol>) -> Tokenizer<InputSymbol, OutputSymbol, Reader> {
        Tokenizer { dfa: pattern, tape: Tape::new(source) }
    }

    ///
    /// Returns the current position in the source (the position after the last matched symbol)
    ///
    pub fn get_source_position(&self) -> usize {
        self.tape.get_source_position()
    }

    ///
    /// Skips an input symbol (returning the symbol that was skipped)
    ///
    pub fn skip_input(&mut self) -> Option<InputSymbol> {
        self.tape.next_symbol()
    }

    ///
    /// True if we've reached the end of the source reader
    ///
    /// If `next_symbol` returns `None` and `at_end_of_reader` is false, then the input stream does not contain a symbol matching the DFA
    ///
    pub fn at_end_of_reader(&self) -> bool {
        self.tape.at_end_of_reader()
    }
}

impl<InputSymbol: Clone+Ord+Countable, OutputSymbol: Clone+Ord+'static, Reader: SymbolReader<InputSymbol>> SymbolReader<OutputSymbol> for Tokenizer<InputSymbol, OutputSymbol, Reader> {
    fn next_symbol(&mut self) -> Option<OutputSymbol> {
        // Start of the next symbol
        let start_pos = self.tape.get_source_position();

        // Match against it
        let match_result = match_pattern(self.dfa.start(), &mut self.tape);

        let end_pos = self.tape.get_source_position();
        match match_result {
            Accept(length, outputsymbol) => {
                if length > 0 {
                    // Rewind the tape to after the accepted symbol
                    self.tape.rewind(end_pos-start_pos - length);

                    // Won't try to match anything before this position
                    self.tape.cut();

                    // Result is the oputput symbol
                    Some(outputsymbol.clone())
                } else {
                    // Zero-length match
                    // If we accepted matches of length 0 we'd get an infinite stream when we hit a symbol that doesn't match
                    self.tape.rewind(end_pos-start_pos);
                    self.tape.next_symbol();

                    None
                }
            },

            Reject => {
                // Rewind back to the start position
                self.tape.rewind(end_pos-start_pos);

                // No match
                None
            },

            _ => {
                panic!("Unexpected output state of ");
            }
        }
    }
}

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

    #[test]
    fn can_match_tokens_like_any_other_pattern() {
        #[derive(Ord, PartialOrd, Eq, PartialEq, Clone)]
        enum TestToken {
            AllAs,
            AllBs
        }

        let mut token_matcher = TokenMatcher::new();
        token_matcher.add_pattern("a".repeat_forever(1), TestToken::AllAs);
        token_matcher.add_pattern("b".repeat_forever(1), TestToken::AllBs);

        assert!(matches("aaaa", &token_matcher) == Some(4));
        assert!(matches("bbbbb", &token_matcher) == Some(5));
        assert!(matches("abbb", &token_matcher) == Some(1));
        assert!(matches("bbaaa", &token_matcher) == Some(2));
    }

    #[test]
    fn can_distinguish_simple_tokens() {
        #[derive(Ord, PartialOrd, Eq, PartialEq, Clone)]
        enum TestToken {
            AllAs,
            AllBs
        }

        let mut token_matcher = TokenMatcher::new();
        token_matcher.add_pattern("a".repeat_forever(1), TestToken::AllAs);
        token_matcher.add_pattern("b".repeat_forever(1), TestToken::AllBs);

        let matcher = token_matcher.prepare_to_match();

        assert!(match_pattern(matcher.start(), &mut "aaaaa".read_symbols()).is_accepted(&TestToken::AllAs));
        assert!(match_pattern(matcher.start(), &mut "bbbb".read_symbols()).is_accepted(&TestToken::AllBs));
    }

    #[test]
    fn clashes_producer_lower_tokens() {
        #[derive(Ord, PartialOrd, Eq, PartialEq, Clone)]
        enum TestToken {
            Abbb,
            Aaab
        }

        let mut token_matcher = TokenMatcher::new();
        token_matcher.add_pattern("a".repeat_forever(1).append("b"), TestToken::Aaab);
        token_matcher.add_pattern("a".append("b".repeat_forever(1)), TestToken::Abbb);

        let matcher = token_matcher.prepare_to_match();

        assert!(match_pattern(matcher.start(), &mut "aaab".read_symbols()).is_accepted(&TestToken::Aaab));
        assert!(match_pattern(matcher.start(), &mut "ab".read_symbols()).is_accepted(&TestToken::Abbb));
        assert!(match_pattern(matcher.start(), &mut "abbbb".read_symbols()).is_accepted(&TestToken::Abbb));
    }

    #[test]
    fn can_match_number_stream() {
        #[derive(Ord, PartialOrd, Eq, PartialEq, Clone)]
        enum TestToken {
            Digit,
            Whitespace
        }

        let mut token_matcher = TokenMatcher::new();
        token_matcher.add_pattern(MatchRange('0', '9').repeat_forever(1), TestToken::Digit);
        token_matcher.add_pattern(" ".repeat_forever(1), TestToken::Whitespace);

        let mut tokenizer = Tokenizer::new("12 390  32 ".read_symbols(), &token_matcher);

        assert!(tokenizer.next_symbol() == Some(TestToken::Digit));
        assert!(tokenizer.get_source_position() == 2);
        assert!(!tokenizer.at_end_of_reader());
        assert!(tokenizer.next_symbol() == Some(TestToken::Whitespace));
        assert!(tokenizer.get_source_position() == 3);
        assert!(tokenizer.next_symbol() == Some(TestToken::Digit));
        assert!(tokenizer.get_source_position() == 6);
        assert!(tokenizer.next_symbol() == Some(TestToken::Whitespace));
        assert!(tokenizer.get_source_position() == 8);
        assert!(tokenizer.next_symbol() == Some(TestToken::Digit));
        assert!(tokenizer.get_source_position() == 10);
        assert!(!tokenizer.at_end_of_reader());
        assert!(tokenizer.next_symbol() == Some(TestToken::Whitespace));
        assert!(tokenizer.at_end_of_reader());
        assert!(tokenizer.next_symbol() == None);
    }
}
<commit_msg>Add tests for skipping symbols and dealing with zero-length matches<commit_after>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! A tokenizer is a pattern matcher that is intended to turn a stream of symbols into another stream of symbols based on the patterns
//! that are matched. Every pattern can produce a different output symbol. If two input strings can ndfa in two different output
//! symbols, then the output symbol that is ordered lower is the one that's produced (ie, if the output symbols are numbers, then '0' will
//! be produced instead of '1' in the event of a clash)
//!

use super::countable::*;
use super::symbol_range::*;
use super::regular_pattern::*;
use super::state_machine::*;
use super::ndfa::*;
use super::prepare::*;
use super::symbol_range_dfa::*;
use super::symbol_reader::*;
use super::pattern_matcher::*;
use super::matches::*;
use super::tape::*;

///
/// Used for generating tokenizing pattern matchers
///
pub struct TokenMatcher<InputSymbol: Clone+Ord+Countable, OutputSymbol: Clone+Ord> {
    patterns: Vec<(Pattern<InputSymbol>, OutputSymbol)>
}

impl<InputSymbol: Clone+Ord+Countable+'static, OutputSymbol: Clone+Ord+'static> TokenMatcher<InputSymbol, OutputSymbol> {
    ///
    /// Creates a new TokenMatcher
    ///
    pub fn new() -> TokenMatcher<InputSymbol, OutputSymbol> {
        TokenMatcher { patterns: vec![] }
    }

    ///
    /// Adds a new pattern that will generate the specified output symbol
    ///
    pub fn add_pattern<TPattern: ToPattern<InputSymbol>>(&mut self, pattern: TPattern, output: OutputSymbol) {
        self.patterns.push((pattern.to_pattern(), output));
    }

    ///
    /// Compiles an NDFA from this TokenMatcher
    ///
    pub fn to_ndfa(&self) -> Box<StateMachine<SymbolRange<InputSymbol>, OutputSymbol>> {
        let mut ndfa = Ndfa::new();

        for &(ref pattern, ref output) in &self.patterns {
            // Compile each pattern starting at state 0
            let end_state = pattern.compile(&mut ndfa, 0);

            // Set the output for this pattern
            ndfa.set_output_symbol(end_state, output.clone());
        }

        // Clear out any overlapping ranges so we can build a valid DFA
        ndfa.fix_overlapping_ranges();

        Box::new(ndfa)
    }
}

impl<'a, InputSymbol: Clone+Ord+Countable+'static, OutputSymbol: Clone+Ord+'static> PrepareToMatch<SymbolRangeDfa<InputSymbol, OutputSymbol>>
for &'a TokenMatcher<InputSymbol, OutputSymbol> {
    #[inline]
    fn prepare_to_match(self) -> SymbolRangeDfa<InputSymbol, OutputSymbol> {
        let ndfa = self.to_ndfa();

        ndfa.prepare_to_match()
    }
}

///
/// A tokenizer is a type of symbol stream that uses a pattern matcher to convert a symbol stream into a stream of tokens
///
pub struct Tokenizer<InputSymbol: Clone+Ord+Countable, OutputSymbol: Clone+Ord, Reader: SymbolReader<InputSymbol>> {
    /// The pattern matcher for this tokenizer
    dfa: SymbolRangeDfa<InputSymbol, OutputSymbol>,

    /// Tape of input symbols that will be used to generate the result
    tape: Tape<InputSymbol, Reader>,
}

impl<InputSymbol: Clone+Ord+Countable, OutputSymbol: Clone+Ord, Reader: SymbolReader<InputSymbol>> Tokenizer<InputSymbol, OutputSymbol, Reader> {
    ///
    /// Creates a new tokenizer from a pattern (usually a TokenMatcher)
    ///
    pub fn new<Prepare: PrepareToMatch<SymbolRangeDfa<InputSymbol, OutputSymbol>>>(source: Reader, pattern: Prepare) -> Tokenizer<InputSymbol, OutputSymbol, Reader> {
        Tokenizer { dfa: pattern.prepare_to_match(), tape: Tape::new(source) }
    }

    ///
    /// Creates a new tokenizer from a prepared pattern
    ///
    pub fn new_prepared(source: Reader, pattern: SymbolRangeDfa<InputSymbol, OutputSymbol>) -> Tokenizer<InputSymbol, OutputSymbol, Reader> {
        Tokenizer { dfa: pattern, tape: Tape::new(source) }
    }

    ///
    /// Returns the current position in the source (the position after the last matched symbol)
    ///
    pub fn get_source_position(&self) -> usize {
        self.tape.get_source_position()
    }

    ///
    /// Skips an input symbol (returning the symbol that was skipped)
    ///
    pub fn skip_input(&mut self) -> Option<InputSymbol> {
        self.tape.next_symbol()
    }

    ///
    /// True if we've reached the end of the source reader
    ///
    /// If `next_symbol` returns `None` and `at_end_of_reader` is false, then the input stream does not contain a symbol matching the DFA
    ///
    pub fn at_end_of_reader(&self) -> bool {
        self.tape.at_end_of_reader()
    }
}

impl<InputSymbol: Clone+Ord+Countable, OutputSymbol: Clone+Ord+'static, Reader: SymbolReader<InputSymbol>> SymbolReader<OutputSymbol> for Tokenizer<InputSymbol, OutputSymbol, Reader> {
    fn next_symbol(&mut self) -> Option<OutputSymbol> {
        // Start of the next symbol
        let start_pos = self.tape.get_source_position();

        // Match against it
        let match_result = match_pattern(self.dfa.start(), &mut self.tape);

        let end_pos = self.tape.get_source_position();
        match match_result {
            Accept(length, outputsymbol) => {
                if length > 0 {
                    // Rewind the tape to after the accepted symbol
                    self.tape.rewind(end_pos-start_pos - length);

                    // Won't try to match anything before this position
                    self.tape.cut();

                    // Result is the oputput symbol
                    Some(outputsymbol.clone())
                } else {
                    // Zero-length match
                    // If we accepted matches of length 0 we'd get an infinite stream when we hit a symbol that doesn't match
                    self.tape.rewind(end_pos-start_pos);
                    self.tape.next_symbol();
                    self.tape.cut();

                    None
                }
            },

            Reject => {
                // Rewind back to the start position
                self.tape.rewind(end_pos-start_pos);

                // No match
                None
            },

            _ => {
                panic!("Unexpected output state of ");
            }
        }
    }
}

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

    #[test]
    fn can_match_tokens_like_any_other_pattern() {
        #[derive(Ord, PartialOrd, Eq, PartialEq, Clone)]
        enum TestToken {
            AllAs,
            AllBs
        }

        let mut token_matcher = TokenMatcher::new();
        token_matcher.add_pattern("a".repeat_forever(1), TestToken::AllAs);
        token_matcher.add_pattern("b".repeat_forever(1), TestToken::AllBs);

        assert!(matches("aaaa", &token_matcher) == Some(4));
        assert!(matches("bbbbb", &token_matcher) == Some(5));
        assert!(matches("abbb", &token_matcher) == Some(1));
        assert!(matches("bbaaa", &token_matcher) == Some(2));
    }

    #[test]
    fn can_distinguish_simple_tokens() {
        #[derive(Ord, PartialOrd, Eq, PartialEq, Clone)]
        enum TestToken {
            AllAs,
            AllBs
        }

        let mut token_matcher = TokenMatcher::new();
        token_matcher.add_pattern("a".repeat_forever(1), TestToken::AllAs);
        token_matcher.add_pattern("b".repeat_forever(1), TestToken::AllBs);

        let matcher = token_matcher.prepare_to_match();

        assert!(match_pattern(matcher.start(), &mut "aaaaa".read_symbols()).is_accepted(&TestToken::AllAs));
        assert!(match_pattern(matcher.start(), &mut "bbbb".read_symbols()).is_accepted(&TestToken::AllBs));
    }

    #[test]
    fn clashes_producer_lower_tokens() {
        #[derive(Ord, PartialOrd, Eq, PartialEq, Clone)]
        enum TestToken {
            Abbb,
            Aaab
        }

        let mut token_matcher = TokenMatcher::new();
        token_matcher.add_pattern("a".repeat_forever(1).append("b"), TestToken::Aaab);
        token_matcher.add_pattern("a".append("b".repeat_forever(1)), TestToken::Abbb);

        let matcher = token_matcher.prepare_to_match();

        assert!(match_pattern(matcher.start(), &mut "aaab".read_symbols()).is_accepted(&TestToken::Aaab));
        assert!(match_pattern(matcher.start(), &mut "ab".read_symbols()).is_accepted(&TestToken::Abbb));
        assert!(match_pattern(matcher.start(), &mut "abbbb".read_symbols()).is_accepted(&TestToken::Abbb));
    }

    #[test]
    fn can_match_number_stream() {
        #[derive(Ord, PartialOrd, Eq, PartialEq, Clone)]
        enum TestToken {
            Digit,
            Whitespace
        }

        let mut token_matcher = TokenMatcher::new();
        token_matcher.add_pattern(MatchRange('0', '9').repeat_forever(1), TestToken::Digit);
        token_matcher.add_pattern(" ".repeat_forever(1), TestToken::Whitespace);

        let mut tokenizer = Tokenizer::new("12 390  32 ".read_symbols(), &token_matcher);

        assert!(tokenizer.next_symbol() == Some(TestToken::Digit));
        assert!(tokenizer.get_source_position() == 2);
        assert!(!tokenizer.at_end_of_reader());
        assert!(tokenizer.next_symbol() == Some(TestToken::Whitespace));
        assert!(tokenizer.get_source_position() == 3);
        assert!(tokenizer.next_symbol() == Some(TestToken::Digit));
        assert!(tokenizer.get_source_position() == 6);
        assert!(tokenizer.next_symbol() == Some(TestToken::Whitespace));
        assert!(tokenizer.get_source_position() == 8);
        assert!(tokenizer.next_symbol() == Some(TestToken::Digit));
        assert!(tokenizer.get_source_position() == 10);
        assert!(!tokenizer.at_end_of_reader());
        assert!(tokenizer.next_symbol() == Some(TestToken::Whitespace));
        assert!(tokenizer.at_end_of_reader());
        assert!(tokenizer.next_symbol() == None);
    }

    #[test]
    fn will_skip_bad_symbols() {
        #[derive(Ord, PartialOrd, Eq, PartialEq, Clone)]
        enum TestToken {
            Digit,
            Whitespace
        }

        let mut token_matcher = TokenMatcher::new();
        token_matcher.add_pattern(MatchRange('0', '9').repeat_forever(1), TestToken::Digit);
        token_matcher.add_pattern(" ".repeat_forever(1), TestToken::Whitespace);

        let mut tokenizer = Tokenizer::new("12 ab 12".read_symbols(), &token_matcher);

        assert!(tokenizer.next_symbol() == Some(TestToken::Digit));
        assert!(tokenizer.next_symbol() == Some(TestToken::Whitespace));
        assert!(tokenizer.next_symbol() == None);
        assert!(!tokenizer.at_end_of_reader());
        assert!(tokenizer.next_symbol() == None);
        assert!(!tokenizer.at_end_of_reader());
        assert!(tokenizer.next_symbol() == Some(TestToken::Whitespace));
        assert!(tokenizer.next_symbol() == Some(TestToken::Digit));
        assert!(tokenizer.next_symbol() == None);
        assert!(tokenizer.at_end_of_reader());
    }

    #[test]
    fn wont_match_zero_length() {
        #[derive(Ord, PartialOrd, Eq, PartialEq, Clone)]
        enum TestToken {
            Digit,
            Whitespace
        }

        let mut token_matcher = TokenMatcher::new();
        token_matcher.add_pattern(MatchRange('0', '9').repeat_forever(0), TestToken::Digit);
        token_matcher.add_pattern(" ".repeat_forever(0), TestToken::Whitespace);

        let mut tokenizer = Tokenizer::new("12 ab 12".read_symbols(), &token_matcher);

        assert!(tokenizer.next_symbol() == Some(TestToken::Digit));
        assert!(tokenizer.next_symbol() == Some(TestToken::Whitespace));
        assert!(tokenizer.next_symbol() == None);
        assert!(!tokenizer.at_end_of_reader());
        assert!(tokenizer.next_symbol() == None);
        assert!(!tokenizer.at_end_of_reader());
        assert!(tokenizer.next_symbol() == Some(TestToken::Whitespace));
        assert!(tokenizer.next_symbol() == Some(TestToken::Digit));
        assert!(tokenizer.next_symbol() == None);
        assert!(tokenizer.at_end_of_reader());
    }
}
<|endoftext|>


<commit_before>
use vertex::{RawVertex, Vertex};
use edge::{Edge, EdgeProxy};
use graph::TraversableToVertex;
use path::{Path, GraphElement};
// define a traversal as a series of stages

pub struct GraphQuery {
	// many paths
    pub paths: Vec<Path>
}

impl GraphQuery {
    pub fn new(vertices: Vec<Vertex>) -> GraphQuery {
        // create a new path for each proxy, add the proxy
        let mut paths = Vec::new();
        for p in vertices {
			let mut path = Path::new(p);
            paths.push(path)
        }
        GraphQuery{paths:paths}
    }

    pub fn empty() -> GraphQuery {
        // return an empty graph query
        GraphQuery{paths:Vec::new()}
    }

	pub fn get_paths(&self) -> &Vec<Path> {
		&self.paths
	}
	pub fn push(&mut self, path:Path) {
		self.paths.push(path)
	}

	/*
	generic mapper for our queries
	future this may use worker threads to perform
	traversals
	 */
    fn full_map<F: Fn(&Path) -> Vec<Path>>(&self, closure: F) -> GraphQuery  {
        let mut result = GraphQuery::empty(); // result
		for x in self.paths.iter() {
			let mut tmp = closure(x);
			result.paths.append(&mut tmp)
		}
		result
    }

	/*
	accepts an Path and a closure
	applies the closure to the path
	gets an Option back: a bunch of paths or None
	if None comes back we don't include the paths in the new GraphQuery
	that is returned
	*/
	fn map<F: Fn(&Path) -> Option<Vec<Path>>>(&self, closure: F)
		-> GraphQuery {
		let mut result = GraphQuery::empty();
		for path in self.paths.iter() {
			// apply the closure to the last element in each map
			// if nothing is returned, we should not use the path anymore
			let mut tmp = closure(&path);
			let new_path = path.clone();
			result.push(new_path);
		}
		result
	}

	/*
        TODO: rewrite to use map()
	 */
    pub fn outV(&self) -> GraphQuery {
		let f = |path: &Path| {
			println!("applying outV");
			let mut result : Vec<Path> = Vec::new();
			//take the final element in the path
			let element = path.path.last().unwrap();

			match element {
				&GraphElement::RawVertex(ref v) => {
					println!("matched vertex");
					let mut tmp = v.outV();
					for vertex in tmp {
						println!("adding path based on vertex match");
						let new_path = path.clone();
						result.push(new_path);
					}
				},
				&GraphElement::Edge(ref e) => {

				}
			}
			// apply outV

			result
		};
		self.full_map(f)
    }

//	pub fn inV(&self) -> GraphQuery {
//		let g = GraphQuery::empty();
//		let f = |element: &GraphElement| {
//			element.inV()
//		};
//		self.map(f)
//	}
}
<commit_msg>using last instead of path.last<commit_after>
use vertex::{RawVertex, Vertex};
use edge::{Edge, EdgeProxy};
use graph::TraversableToVertex;
use path::{Path, GraphElement};
// define a traversal as a series of stages

pub struct GraphQuery {
	// many paths
    pub paths: Vec<Path>
}

impl GraphQuery {
    pub fn new(vertices: Vec<Vertex>) -> GraphQuery {
        // create a new path for each proxy, add the proxy
        let mut paths = Vec::new();
        for p in vertices {
			let mut path = Path::new(p);
            paths.push(path)
        }
        GraphQuery{paths:paths}
    }

    pub fn empty() -> GraphQuery {
        // return an empty graph query
        GraphQuery{paths:Vec::new()}
    }

	pub fn get_paths(&self) -> &Vec<Path> {
		&self.paths
	}
	pub fn push(&mut self, path:Path) {
		self.paths.push(path)
	}

	/*
	generic mapper for our queries
	future this may use worker threads to perform
	traversals
	 */
    fn full_map<F: Fn(&Path) -> Vec<Path>>(&self, closure: F) -> GraphQuery  {
        let mut result = GraphQuery::empty(); // result
		for x in self.paths.iter() {
			let mut tmp = closure(x);
			result.paths.append(&mut tmp)
		}
		result
    }

	/*
	accepts an Path and a closure
	applies the closure to the path
	gets an Option back: a bunch of paths or None
	if None comes back we don't include the paths in the new GraphQuery
	that is returned
	*/
	fn map<F: Fn(&Path) -> Option<Vec<Path>>>(&self, closure: F)
		-> GraphQuery {
		let mut result = GraphQuery::empty();
		for path in self.paths.iter() {
			// apply the closure to the last element in each map
			// if nothing is returned, we should not use the path anymore
			let mut tmp = closure(&path);
			let new_path = path.clone();
			result.push(new_path);
		}
		result
	}

	/*
        TODO: rewrite to use map()
	 */
    pub fn outV(&self) -> GraphQuery {
		let f = |path: &Path| {
			println!("applying outV");
			let mut result : Vec<Path> = Vec::new();
			//take the final element in the path
			let element = path.last().unwrap();

			match element {
				&GraphElement::RawVertex(ref v) => {
					println!("matched vertex");
					let mut tmp = v.outV();
					for vertex in tmp {
						println!("adding path based on vertex match");
						let new_path = path.clone();
						result.push(new_path);
					}
				},
				&GraphElement::Edge(ref e) => {

				}
			}
			// apply outV

			result
		};
		self.full_map(f)
    }

//	pub fn inV(&self) -> GraphQuery {
//		let g = GraphQuery::empty();
//		let f = |element: &GraphElement| {
//			element.inV()
//		};
//		self.map(f)
//	}
}
<|endoftext|>


<commit_before>//! `UnionFind<K>` is a disjoint-set data structure.

use super::graph::IndexType;

/// `UnionFind<K>` is a disjoint-set data structure. It tracks set membership of *n* elements
/// indexed from *0* to *n - 1*. The scalar type is `K` which must be an unsigned integer type.
///
/// http://en.wikipedia.org/wiki/Disjoint-set_data_structure
///
/// Too awesome not to quote:
///
/// “The amortized time per operation is **O(α(n))** where **α(n)** is the
/// inverse of **f(x) = A(x, x)** with **A** being the extremely fast-growing Ackermann function.”
#[derive(Debug, Clone)]
pub struct UnionFind<K>
{
    // For element at index *i*, store the index of its parent; the representative itself
    // stores its own index. This forms equivalence classes which are the disjoint sets, each
    // with a unique representative.
    parent: Vec<K>,
    // It is a balancing tree structure,
    // so the ranks are logarithmic in the size of the container -- a byte is more than enough.
    //
    // Rank is separated out both to save space and to save cache in when searching in the parent
    // vector.
    rank: Vec<u8>,
}

#[inline]
unsafe fn get_unchecked<K>(xs: &[K], index: usize) -> &K
{
    debug_assert!(index < xs.len());
    xs.get_unchecked(index)
}

impl<K> UnionFind<K>
    where K: IndexType
{
    /// Create a new `UnionFind` of `n` disjoint sets.
    pub fn new(n: usize) -> Self
    {
        let rank = vec![0; n];
        let parent = (0..n).map(K::new).collect::<Vec<K>>();

        UnionFind{parent: parent, rank: rank}
    }

    /// Return the representative for `x`.
    ///
    /// **Panics** if `x` is out of bounds.
    pub fn find(&self, x: K) -> K
    {
        assert!(x.index() < self.parent.len());
        unsafe {
            let mut x = x;
            loop {
                // Use unchecked indexing because we can trust the internal set ids.
                let xparent = *get_unchecked(&self.parent, x.index());
                if xparent == x {
                    break
                }
                x = xparent;
            }
            x
        }
    }

    /// Return the representative for `x`.
    ///
    /// Write back the found representative, flattening the internal
    /// datastructure in the process and quicken future lookups.
    ///
    /// **Panics** if `x` is out of bounds.
    pub fn find_mut(&mut self, x: K) -> K
    {
        assert!(x.index() < self.parent.len());
        unsafe {
            self.find_mut_recursive(x)
        }
    }

    unsafe fn find_mut_recursive(&mut self, x: K) -> K
    {
        let xparent = *get_unchecked(&self.parent, x.index());
        if xparent != x {
            let xrep = self.find_mut_recursive(xparent);
            let xparent = self.parent.get_unchecked_mut(x.index());
            *xparent = xrep;
            *xparent
        } else {
            xparent
        }
    }

    /// Unify the two sets containing `x` and `y`.
    ///
    /// Return `false` if the sets were already the same, `true` if they were unified.
    ///
    /// **Panics** if `x` or `y` is out of bounds.
    pub fn union(&mut self, x: K, y: K) -> bool
    {
        if x == y {
            return false
        }
        let xrep = self.find_mut(x);
        let yrep = self.find_mut(y);

        if xrep == yrep {
            return false
        }

        let xrepu = xrep.index();
        let yrepu = yrep.index();
        let xrank = self.rank[xrepu];
        let yrank = self.rank[yrepu];

        // The rank corresponds roughly to the depth of the treeset, so put the
        // smaller set below the larger
        if xrank < yrank {
            self.parent[xrepu] = yrep;
        } else if xrank > yrank {
            self.parent[yrepu] = xrep;
        } else {
            // put y below x when equal.
            self.parent[yrepu] = xrep;
            self.rank[xrepu] += 1;
        }
        true
    }

    /// Return a vector mapping each element to its representative.
    pub fn into_labeling(mut self) -> Vec<K>
    {
        // write in the labeling of each element
        unsafe {
            for ix in 0..self.parent.len() {
                let k = *get_unchecked(&self.parent, ix);
                let xrep = self.find_mut_recursive(k);
                *self.parent.get_unchecked_mut(ix) = xrep;
            }
        }
        self.parent
    }
}
<commit_msg>Fix markdown warning with current Rust.<commit_after>//! `UnionFind<K>` is a disjoint-set data structure.

use super::graph::IndexType;

/// `UnionFind<K>` is a disjoint-set data structure. It tracks set membership of *n* elements
/// indexed from *0* to *n - 1*. The scalar type is `K` which must be an unsigned integer type.
///
/// <http://en.wikipedia.org/wiki/Disjoint-set_data_structure>
///
/// Too awesome not to quote:
///
/// “The amortized time per operation is **O(α(n))** where **α(n)** is the
/// inverse of **f(x) = A(x, x)** with **A** being the extremely fast-growing Ackermann function.”
#[derive(Debug, Clone)]
pub struct UnionFind<K>
{
    // For element at index *i*, store the index of its parent; the representative itself
    // stores its own index. This forms equivalence classes which are the disjoint sets, each
    // with a unique representative.
    parent: Vec<K>,
    // It is a balancing tree structure,
    // so the ranks are logarithmic in the size of the container -- a byte is more than enough.
    //
    // Rank is separated out both to save space and to save cache in when searching in the parent
    // vector.
    rank: Vec<u8>,
}

#[inline]
unsafe fn get_unchecked<K>(xs: &[K], index: usize) -> &K
{
    debug_assert!(index < xs.len());
    xs.get_unchecked(index)
}

impl<K> UnionFind<K>
    where K: IndexType
{
    /// Create a new `UnionFind` of `n` disjoint sets.
    pub fn new(n: usize) -> Self
    {
        let rank = vec![0; n];
        let parent = (0..n).map(K::new).collect::<Vec<K>>();

        UnionFind{parent: parent, rank: rank}
    }

    /// Return the representative for `x`.
    ///
    /// **Panics** if `x` is out of bounds.
    pub fn find(&self, x: K) -> K
    {
        assert!(x.index() < self.parent.len());
        unsafe {
            let mut x = x;
            loop {
                // Use unchecked indexing because we can trust the internal set ids.
                let xparent = *get_unchecked(&self.parent, x.index());
                if xparent == x {
                    break
                }
                x = xparent;
            }
            x
        }
    }

    /// Return the representative for `x`.
    ///
    /// Write back the found representative, flattening the internal
    /// datastructure in the process and quicken future lookups.
    ///
    /// **Panics** if `x` is out of bounds.
    pub fn find_mut(&mut self, x: K) -> K
    {
        assert!(x.index() < self.parent.len());
        unsafe {
            self.find_mut_recursive(x)
        }
    }

    unsafe fn find_mut_recursive(&mut self, x: K) -> K
    {
        let xparent = *get_unchecked(&self.parent, x.index());
        if xparent != x {
            let xrep = self.find_mut_recursive(xparent);
            let xparent = self.parent.get_unchecked_mut(x.index());
            *xparent = xrep;
            *xparent
        } else {
            xparent
        }
    }

    /// Unify the two sets containing `x` and `y`.
    ///
    /// Return `false` if the sets were already the same, `true` if they were unified.
    ///
    /// **Panics** if `x` or `y` is out of bounds.
    pub fn union(&mut self, x: K, y: K) -> bool
    {
        if x == y {
            return false
        }
        let xrep = self.find_mut(x);
        let yrep = self.find_mut(y);

        if xrep == yrep {
            return false
        }

        let xrepu = xrep.index();
        let yrepu = yrep.index();
        let xrank = self.rank[xrepu];
        let yrank = self.rank[yrepu];

        // The rank corresponds roughly to the depth of the treeset, so put the
        // smaller set below the larger
        if xrank < yrank {
            self.parent[xrepu] = yrep;
        } else if xrank > yrank {
            self.parent[yrepu] = xrep;
        } else {
            // put y below x when equal.
            self.parent[yrepu] = xrep;
            self.rank[xrepu] += 1;
        }
        true
    }

    /// Return a vector mapping each element to its representative.
    pub fn into_labeling(mut self) -> Vec<K>
    {
        // write in the labeling of each element
        unsafe {
            for ix in 0..self.parent.len() {
                let k = *get_unchecked(&self.parent, ix);
                let xrep = self.find_mut_recursive(k);
                *self.parent.get_unchecked_mut(ix) = xrep;
            }
        }
        self.parent
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The HTML5 for Rust Project Developers. See the
// COPYRIGHT file at the top-level directory of this distribution.
//
// 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 phf::PhfMap;

use std::mem::replace;

static static_atom_map: PhfMap<uint> = static_atom_map!();
static static_atom_array: &'static [&'static str] = static_atom_array!();

// Assume that a string which can be interned always is.
// FIXME: Revisit this assumption when we have dynamic interning.
/// Interned string.
///
/// Don't pattern-match this directly! Use the `atom!()` pattern macro.
#[deriving(Clone, Show, PartialEq, Eq)]
pub enum Atom {
    Static(uint),
    // dynamic interning goes here
    Owned(String),
}

impl Atom {
    pub fn from_str(s: &str) -> Atom {
        match static_atom_map.find(&s) {
            Some(&k) => Static(k),
            None => Owned(s.to_string()),
        }
    }

    pub fn from_buf(s: String) -> Atom {
        match static_atom_map.find(&s.as_slice()) {
            Some(&k) => Static(k),
            None => Owned(s),
        }
    }

    /// Like `Atom::from_buf(replace(s, String::new()))` but avoids
    /// allocating a new `String` when the string is interned --
    /// just truncates the old one.
    pub fn take_from_buf(s: &mut String) -> Atom {
        match static_atom_map.find(&s.as_slice()) {
            Some(&k) => {
                s.truncate(0);
                Static(k)
            }
            None => {
                Owned(replace(s, String::new()))
            }
        }
    }

    #[inline(always)]
    fn fast_partial_eq(&self, other: &Atom) -> Option<bool> {
        match (self, other) {
            (&Static(x), &Static(y)) => Some(x == y),
            _ => None,
        }
    }
}

fn get_static(i: uint) -> &'static str {
    *static_atom_array.get(i).expect("bad static atom")
}

impl Str for Atom {
    fn as_slice<'t>(&'t self) -> &'t str {
        match *self {
            Static(i) => get_static(i),
            Owned(ref s) => s.as_slice(),
        }
    }
}

impl StrAllocating for Atom {
    fn into_string(self) -> String {
        match self {
            Static(i) => get_static(i).to_string(),
            Owned(s) => s.into_string(),
        }
    }

    fn to_string(&self) -> String {
        match *self {
            Static(i) => get_static(i).to_string(),
            Owned(ref s) => s.clone(),
        }
    }

    fn into_owned(self) -> String {
        match self {
            Static(i) => get_static(i).to_string(),
            Owned(s) => s,
        }
    }
}

impl PartialOrd for Atom {
    fn lt(&self, other: &Atom) -> bool {
        match self.fast_partial_eq(other) {
            Some(true) => false,
            _ => self.as_slice() < other.as_slice(),
        }
    }
}

impl Ord for Atom {
    fn cmp(&self, other: &Atom) -> Ordering {
        match self.fast_partial_eq(other) {
            Some(true) => Equal,
            _ => self.as_slice().cmp(&other.as_slice()),
        }
    }
}

#[cfg(test)]
#[allow(non_snake_case_functions)]
mod test {
    use super::*; // public items
    use super::{get_static}; // private items

    #[test]
    fn interned() {
        match Atom::from_str("body") {
            Static(i) => assert_eq!(get_static(i), "body"),
            _ => fail!("wrong interning"),
        }
    }

    #[test]
    fn not_interned() {
        match Atom::from_str("asdfghjk") {
            Owned(b) => assert_eq!(b.as_slice(), "asdfghjk"),
            _ => fail!("wrong interning"),
        }
    }

    #[test]
    fn as_slice() {
        assert_eq!(Atom::from_str("").as_slice(), "");
        assert_eq!(Atom::from_str("body").as_slice(), "body");
        assert_eq!(Atom::from_str("asdfghjk").as_slice(), "asdfghjk");
    }

    #[test]
    fn into_string() {
        assert_eq!(Atom::from_str("").into_string(), "".to_string());
        assert_eq!(Atom::from_str("body").into_string(), "body".to_string());
        assert_eq!(Atom::from_str("asdfghjk").into_string(), "asdfghjk".to_string());
    }

    #[test]
    fn to_string() {
        assert_eq!(Atom::from_str("").to_string(), "".to_string());
        assert_eq!(Atom::from_str("body").to_string(), "body".to_string());
        assert_eq!(Atom::from_str("asdfghjk").to_string(), "asdfghjk".to_string());
    }

    #[test]
    fn take_from_buf_interned() {
        let mut b = "body".to_string();
        let a = Atom::take_from_buf(&mut b);
        assert_eq!(a, Atom::from_str("body"));
        assert_eq!(b, String::new());
    }

    #[test]
    fn take_from_buf_not_interned() {
        let mut b = "asdfghjk".to_string();
        let a = Atom::take_from_buf(&mut b);
        assert_eq!(a, Atom::from_str("asdfghjk"));
        assert_eq!(b, String::new());
    }

    #[test]
    fn ord() {
        fn check(x: &str, y: &str) {
            assert_eq!(x < y, Atom::from_str(x) < Atom::from_str(y));
            assert_eq!(x.cmp(&y), Atom::from_str(x).cmp(&Atom::from_str(y)));
        }

        check("a", "body");
        check("asdf", "body");
        check("zasdf", "body");
        check("z", "body");

        check("a", "bbbbb");
        check("asdf", "bbbbb");
        check("zasdf", "bbbbb");
        check("z", "bbbbb");
    }

    #[test]
    fn atom_macro() {
        assert_eq!(atom!(body), Atom::from_str("body"));
        assert_eq!(atom!("body"), Atom::from_str("body"));
        assert_eq!(atom!("font-weight"), Atom::from_str("font-weight"));
    }

    #[test]
    fn match_atom() {
        assert_eq!(2, match Atom::from_str("head") {
            atom!(br) => 1,
            atom!(html) | atom!(head) => 2,
            _ => 3,
        });

        assert_eq!(3, match Atom::from_str("body") {
            atom!(br) => 1,
            atom!(html) | atom!(head) => 2,
            _ => 3,
        });

        assert_eq!(3, match Atom::from_str("zzzzzz") {
            atom!(br) => 1,
            atom!(html) | atom!(head) => 2,
            _ => 3,
        });
    }
}
<commit_msg>Upgrade to rustc 0.11.0-pre (6a3695d54f14491c39da7910082df9ba9c833b37 2014-06-29 00:31:30 +0000)<commit_after>// Copyright 2014 The HTML5 for Rust Project Developers. See the
// COPYRIGHT file at the top-level directory of this distribution.
//
// 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 phf::PhfMap;

use std::mem::replace;

static static_atom_map: PhfMap<uint> = static_atom_map!();
static static_atom_array: &'static [&'static str] = static_atom_array!();

// Assume that a string which can be interned always is.
// FIXME: Revisit this assumption when we have dynamic interning.
/// Interned string.
///
/// Don't pattern-match this directly! Use the `atom!()` pattern macro.
#[deriving(Clone, Show, PartialEq, Eq)]
pub enum Atom {
    Static(uint),
    // dynamic interning goes here
    Owned(String),
}

impl Atom {
    pub fn from_str(s: &str) -> Atom {
        match static_atom_map.find(&s) {
            Some(&k) => Static(k),
            None => Owned(s.to_string()),
        }
    }

    pub fn from_buf(s: String) -> Atom {
        match static_atom_map.find(&s.as_slice()) {
            Some(&k) => Static(k),
            None => Owned(s),
        }
    }

    /// Like `Atom::from_buf(replace(s, String::new()))` but avoids
    /// allocating a new `String` when the string is interned --
    /// just truncates the old one.
    pub fn take_from_buf(s: &mut String) -> Atom {
        match static_atom_map.find(&s.as_slice()) {
            Some(&k) => {
                s.truncate(0);
                Static(k)
            }
            None => {
                Owned(replace(s, String::new()))
            }
        }
    }

    #[inline(always)]
    fn fast_partial_eq(&self, other: &Atom) -> Option<bool> {
        match (self, other) {
            (&Static(x), &Static(y)) => Some(x == y),
            _ => None,
        }
    }
}

fn get_static(i: uint) -> &'static str {
    *static_atom_array.get(i).expect("bad static atom")
}

impl Str for Atom {
    fn as_slice<'t>(&'t self) -> &'t str {
        match *self {
            Static(i) => get_static(i),
            Owned(ref s) => s.as_slice(),
        }
    }
}

impl StrAllocating for Atom {
    fn into_string(self) -> String {
        match self {
            Static(i) => get_static(i).to_string(),
            Owned(s) => s.into_string(),
        }
    }

    fn to_string(&self) -> String {
        match *self {
            Static(i) => get_static(i).to_string(),
            Owned(ref s) => s.clone(),
        }
    }

    fn into_owned(self) -> String {
        match self {
            Static(i) => get_static(i).to_string(),
            Owned(s) => s,
        }
    }
}

impl PartialOrd for Atom {
    fn lt(&self, other: &Atom) -> bool {
        match self.fast_partial_eq(other) {
            Some(true) => false,
            _ => self.as_slice() < other.as_slice(),
        }
    }
}

impl Ord for Atom {
    fn cmp(&self, other: &Atom) -> Ordering {
        match self.fast_partial_eq(other) {
            Some(true) => Equal,
            _ => self.as_slice().cmp(&other.as_slice()),
        }
    }
}

#[cfg(test)]
#[allow(non_snake_case_functions)]
mod test {
    use super::*; // public items
    use super::{get_static}; // private items

    #[test]
    fn interned() {
        match Atom::from_str("body") {
            Static(i) => assert_eq!(get_static(i), "body"),
            _ => fail!("wrong interning"),
        }
    }

    #[test]
    fn not_interned() {
        match Atom::from_str("asdfghjk") {
            Owned(b) => assert_eq!(b.as_slice(), "asdfghjk"),
            _ => fail!("wrong interning"),
        }
    }

    #[test]
    fn as_slice() {
        assert_eq!(Atom::from_str("").as_slice(), "");
        assert_eq!(Atom::from_str("body").as_slice(), "body");
        assert_eq!(Atom::from_str("asdfghjk").as_slice(), "asdfghjk");
    }

    #[test]
    fn into_string() {
        assert_eq!(Atom::from_str("").into_string(), "".to_string());
        assert_eq!(Atom::from_str("body").into_string(), "body".to_string());
        assert_eq!(Atom::from_str("asdfghjk").into_string(), "asdfghjk".to_string());
    }

    #[test]
    fn to_string() {
        assert_eq!(Atom::from_str("").to_string(), "".to_string());
        assert_eq!(Atom::from_str("body").to_string(), "body".to_string());
        assert_eq!(Atom::from_str("asdfghjk").to_string(), "asdfghjk".to_string());
    }

    #[test]
    fn take_from_buf_interned() {
        let mut b = "body".to_string();
        let a = Atom::take_from_buf(&mut b);
        assert_eq!(a, Atom::from_str("body"));
        assert_eq!(b, String::new());
    }

    #[test]
    fn take_from_buf_not_interned() {
        let mut b = "asdfghjk".to_string();
        let a = Atom::take_from_buf(&mut b);
        assert_eq!(a, Atom::from_str("asdfghjk"));
        assert_eq!(b, String::new());
    }

    #[test]
    fn ord() {
        fn check(x: &str, y: &str) {
            assert_eq!(x < y, Atom::from_str(x) < Atom::from_str(y));
            assert_eq!(x.cmp(&y), Atom::from_str(x).cmp(&Atom::from_str(y)));
        }

        check("a", "body");
        check("asdf", "body");
        check("zasdf", "body");
        check("z", "body");

        check("a", "bbbbb");
        check("asdf", "bbbbb");
        check("zasdf", "bbbbb");
        check("z", "bbbbb");
    }

    #[test]
    fn atom_macro() {
        assert_eq!(atom!(body), Atom::from_str("body"));
        assert_eq!(atom!("body"), Atom::from_str("body"));
        assert_eq!(atom!("font-weight"), Atom::from_str("font-weight"));
    }

    #[test]
    fn match_atom() {
        assert_eq!(2u, match Atom::from_str("head") {
            atom!(br) => 1u,
            atom!(html) | atom!(head) => 2u,
            _ => 3u,
        });

        assert_eq!(3u, match Atom::from_str("body") {
            atom!(br) => 1u,
            atom!(html) | atom!(head) => 2u,
            _ => 3u,
        });

        assert_eq!(3u, match Atom::from_str("zzzzzz") {
            atom!(br) => 1u,
            atom!(html) | atom!(head) => 2u,
            _ => 3u,
        });
    }
}
<|endoftext|>