<commit_before>// Copyright 2014 Google Inc. All rights reserved.
//
// 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.

//! Local state for keys in the snapshot in progress (the "index").

use std::borrow::Cow;

use diesel;
use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;

use std::sync::mpsc;
use time::Duration;

use blob;
use hash;
use periodic_timer::PeriodicTimer;
use process::{Process, MsgHandler};
use util;

use super::schema;

error_type! {
    #[derive(Debug)]
    pub enum IndexError {
        Recv(mpsc::RecvError) {
            cause;
        },
        SqlMigration(diesel::migrations::MigrationError) {
            cause;
        },
        SqlRunMigration(diesel::migrations::RunMigrationsError) {
            cause;
        },
        SqlExecute(diesel::result::Error) {
            cause;
        },
        Message(Cow<'static, str>) {
            desc (e) &**e;
            from (s: &'static str) s.into();
            from (s: String) s.into();
        }
     }
}

#[derive(Clone, Debug)]
pub struct Entry {
    pub id: Option<u64>,
    pub parent_id: Option<u64>,

    pub name: Vec<u8>,

    pub created: Option<i64>,
    pub modified: Option<i64>,
    pub accessed: Option<i64>,

    pub permissions: Option<u64>,
    pub user_id: Option<u64>,
    pub group_id: Option<u64>,

    pub data_hash: Option<Vec<u8>>,
    pub data_length: Option<u64>,
}

pub type IndexProcess = Process<Msg, Result<Reply, IndexError>>;

pub enum Msg {
    /// Insert an entry in the key index.
    /// Returns `Id` with the new entry ID.
    Insert(Entry),

    /// Lookup an entry in the key index by its parent id and name.
    /// Returns either `Entry` with the found entry or `NotFound`.
    Lookup(Option<u64>, Vec<u8>),

    /// Update the `payload` and `persistent_ref` of an entry.
    /// Returns `UpdateOk`.
    UpdateDataHash(Entry, Option<hash::Hash>, Option<blob::ChunkRef>),

    /// List a directory (aka. `level`) in the index.
    /// Returns `ListResult` with all the entries under the given parent.
    ListDir(Option<u64>),

    /// Flush this key index.
    Flush,
}

pub enum Reply {
    Entry(Entry),
    NotFound,
    UpdateOk,
    ListResult(Vec<(Entry, Option<blob::ChunkRef>)>),
    FlushOk,
}

pub struct Index {
    path: String,
    conn: SqliteConnection,
    flush_timer: PeriodicTimer,
}

fn i64_to_u64_or_panic(x: i64) -> u64 {
    if x < 0 {
        panic!("expected u64, got {:?}", x);
    }
    return x as u64;
}

impl Index {
    pub fn new(path: String) -> Result<Index, IndexError> {
        let conn = SqliteConnection::establish(&path).expect("Could not open SQLite database");

        let ki = Index {
            path: path,
            conn: conn,
            flush_timer: PeriodicTimer::new(Duration::seconds(5)),
        };

        let dir = try!(diesel::migrations::find_migrations_directory());
        try!(diesel::migrations::run_pending_migrations_in_directory(&ki.conn,
                                                                     &dir,
                                                                     &mut util::InfoWriter));
        try!(ki.conn.begin_transaction());

        Ok(ki)
    }

    #[cfg(test)]
    pub fn new_for_testing() -> Result<Index, IndexError> {
        Index::new(":memory:".to_string())
    }

    fn last_insert_rowid(&self) -> i64 {
        diesel::select(diesel::expression::sql("last_insert_rowid()"))
            .first::<i64>(&self.conn)
            .unwrap()
    }

    pub fn maybe_flush(&mut self) -> Result<(), IndexError> {
        if self.flush_timer.did_fire() {
            try!(self.flush());
        }

        Ok(())
    }

    pub fn flush(&mut self) -> Result<(), IndexError> {
        try!(self.conn.commit_transaction());
        try!(self.conn.begin_transaction());

        Ok(())
    }
}

impl Clone for Index {
    fn clone(&self) -> Index {
        // We succeeded setup once, and so should succeed again unless the underlying database has
        // disappeared, in which case it is acceptable to terminate the program.
        Index::new(self.path.clone()).expect("Could not clone index")
    }
}

impl MsgHandler<Msg, Result<Reply, IndexError>> for Index {
    type Err = IndexError;

    fn handle(&mut self,
              msg: Msg,
              reply: Box<Fn(Result<Reply, IndexError>)>)
              -> Result<(), IndexError> {
        let reply_ok = |x| {
            reply(Ok(x));
            Ok(())
        };
        match msg {
            Msg::Insert(entry) => {
                use super::schema::keys::dsl::*;

                let entry = match entry.id {
                    Some(id_) => {
                        // Replace existing entry.
                        try!(diesel::update(keys.find(id_ as i64))
                                 .set((parent.eq(entry.parent_id.map(|x| x as i64)),
                                       name.eq(&entry.name[..]),
                                       created.eq(entry.created),
                                       modified.eq(entry.modified),
                                       accessed.eq(entry.accessed)))
                                 .execute(&self.conn));
                        entry
                    }
                    None => {
                        // Insert new entry.
                        {
                            let new = schema::NewKey {
                                parent: entry.parent_id.map(|x| x as i64),
                                name: &entry.name[..],
                                created: entry.created,
                                modified: entry.modified,
                                accessed: entry.accessed,
                                permissions: entry.permissions.map(|x| x as i64),
                                group_id: entry.group_id.map(|x| x as i64),
                                user_id: entry.user_id.map(|x| x as i64),
                                hash: None,
                                persistent_ref: None,
                            };

                            try!(diesel::insert(&new)
                                     .into(keys)
                                     .execute(&self.conn));
                        }
                        let mut entry = entry;
                        entry.id = Some(self.last_insert_rowid() as u64);
                        entry
                    }
                };

                return reply_ok(Reply::Entry(entry));
            }

            Msg::Lookup(parent_, name_) => {
                use super::schema::keys::dsl::*;

                let row_opt = match parent_ {
                    Some(p) => {
                        try!(keys.filter(parent.eq(p as i64))
                                 .filter(name.eq(&name_[..]))
                                 .first::<schema::Key>(&self.conn)
                                 .optional())
                    }
                    None => {
                        try!(keys.filter(parent.is_null())
                                 .filter(name.eq(&name_[..]))
                                 .first::<schema::Key>(&self.conn)
                                 .optional())
                    }
                };

                if let Some(row) = row_opt {
                    return reply_ok(Reply::Entry(Entry {
                        id: Some(row.id as u64),
                        parent_id: parent_,
                        name: name_,
                        created: row.created,
                        modified: row.modified,
                        accessed: row.accessed,
                        permissions: row.permissions.map(|x| x as u64),
                        user_id: row.user_id.map(|x| x as u64),
                        group_id: row.group_id.map(|x| x as u64),
                        data_hash: row.hash,
                        data_length: None,
                    }));
                }
                return reply_ok(Reply::NotFound);
            }

            Msg::UpdateDataHash(entry, hash_opt, persistent_ref_opt) => {
                use super::schema::keys::dsl::*;

                let id_ = entry.id.expect("Tried to update without an id") as i64;
                assert!(hash_opt.is_some() == persistent_ref_opt.is_some());

                let hash_bytes = hash_opt.map(|h| h.bytes);
                let persistent_ref_bytes = persistent_ref_opt.map(|p| p.as_bytes());

                if entry.modified.is_some() {
                    try!(diesel::update(keys.find(id_)
                                            .filter(modified.eq::<Option<i64>>(None)
                                                            .or(modified.le(entry.modified))))
                             .set((hash.eq(hash_bytes), persistent_ref.eq(persistent_ref_bytes)))
                             .execute(&self.conn));
                } else {
                    try!(diesel::update(keys.find(id_))
                             .set((hash.eq(hash_bytes), persistent_ref.eq(persistent_ref_bytes)))
                             .execute(&self.conn));
                }

                try!(self.maybe_flush());
                return reply_ok(Reply::UpdateOk);
            }

            Msg::Flush => {
                try!(self.flush());
                return reply_ok(Reply::FlushOk);
            }

            Msg::ListDir(parent_opt) => {
                use super::schema::keys::dsl::*;

                let rows = match parent_opt {
                    Some(p) => {
                        try!(keys.filter(parent.eq(p as i64))
                                 .load::<schema::Key>(&self.conn))
                    }
                    None => {
                        try!(keys.filter(parent.is_null())
                                 .load::<schema::Key>(&self.conn))
                    }
                };

                return reply_ok(Reply::ListResult(rows.into_iter()
                                                      .map(|mut r| {
                                                          (Entry {
                                                    id: Some(r.id as u64),
                                                    parent_id: r.parent.map(|x| x as u64),
                                                    name: r.name,
                                                    created: r.created,
                                                    modified: r.modified,
                                                    accessed: r.accessed,
                                                    permissions: r.permissions
                                                                  .map(|x| x as u64),
                                                    user_id: r.user_id.map(|x| x as u64),
                                                    group_id: r.group_id.map(|x| x as u64),
                                                    data_hash: r.hash,
                                                    data_length: None,
                                                },
                                                 r.persistent_ref.as_mut().map(|p| {
                                                    blob::ChunkRef::from_bytes(&mut &p[..]).unwrap()
                                                }))
                                                      })
                                                      .collect()));
            }
        }
    }
}
<commit_msg>Propagate most of remaining error cases.<commit_after>// Copyright 2014 Google Inc. All rights reserved.
//
// 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.

//! Local state for keys in the snapshot in progress (the "index").

use std::borrow::Cow;

use diesel;
use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;

use std::sync::mpsc;
use time::Duration;

use blob;
use hash;
use periodic_timer::PeriodicTimer;
use process::{Process, MsgHandler};
use util;

use super::schema;

error_type! {
    #[derive(Debug)]
    pub enum IndexError {
        Recv(mpsc::RecvError) {
            cause;
        },
        SqlConnection(diesel::ConnectionError) {
            cause;
        },
        SqlMigration(diesel::migrations::MigrationError) {
            cause;
        },
        SqlRunMigration(diesel::migrations::RunMigrationsError) {
            cause;
        },
        SqlExecute(diesel::result::Error) {
            cause;
        },
        Message(Cow<'static, str>) {
            desc (e) &**e;
            from (s: &'static str) s.into();
            from (s: String) s.into();
        }
     }
}

#[derive(Clone, Debug)]
pub struct Entry {
    pub id: Option<u64>,
    pub parent_id: Option<u64>,

    pub name: Vec<u8>,

    pub created: Option<i64>,
    pub modified: Option<i64>,
    pub accessed: Option<i64>,

    pub permissions: Option<u64>,
    pub user_id: Option<u64>,
    pub group_id: Option<u64>,

    pub data_hash: Option<Vec<u8>>,
    pub data_length: Option<u64>,
}

pub type IndexProcess = Process<Msg, Result<Reply, IndexError>>;

pub enum Msg {
    /// Insert an entry in the key index.
    /// Returns `Id` with the new entry ID.
    Insert(Entry),

    /// Lookup an entry in the key index by its parent id and name.
    /// Returns either `Entry` with the found entry or `NotFound`.
    Lookup(Option<u64>, Vec<u8>),

    /// Update the `payload` and `persistent_ref` of an entry.
    /// Returns `UpdateOk`.
    UpdateDataHash(Entry, Option<hash::Hash>, Option<blob::ChunkRef>),

    /// List a directory (aka. `level`) in the index.
    /// Returns `ListResult` with all the entries under the given parent.
    ListDir(Option<u64>),

    /// Flush this key index.
    Flush,
}

pub enum Reply {
    Entry(Entry),
    NotFound,
    UpdateOk,
    ListResult(Vec<(Entry, Option<blob::ChunkRef>)>),
    FlushOk,
}

pub struct Index {
    path: String,
    conn: SqliteConnection,
    flush_timer: PeriodicTimer,
}

impl Index {
    pub fn new(path: String) -> Result<Index, IndexError> {
        let conn = try!(SqliteConnection::establish(&path));

        let ki = Index {
            path: path,
            conn: conn,
            flush_timer: PeriodicTimer::new(Duration::seconds(5)),
        };

        let dir = try!(diesel::migrations::find_migrations_directory());
        try!(diesel::migrations::run_pending_migrations_in_directory(&ki.conn,
                                                                     &dir,
                                                                     &mut util::InfoWriter));
        try!(ki.conn.begin_transaction());

        Ok(ki)
    }

    #[cfg(test)]
    pub fn new_for_testing() -> Result<Index, IndexError> {
        Index::new(":memory:".to_string())
    }

    fn last_insert_rowid(&self) -> Result<i64, IndexError> {
        let id = try!(diesel::select(diesel::expression::sql("last_insert_rowid()"))
                          .first::<i64>(&self.conn));
        Ok(id)
    }

    pub fn maybe_flush(&mut self) -> Result<(), IndexError> {
        if self.flush_timer.did_fire() {
            try!(self.flush());
        }

        Ok(())
    }

    pub fn flush(&mut self) -> Result<(), IndexError> {
        try!(self.conn.commit_transaction());
        try!(self.conn.begin_transaction());

        Ok(())
    }
}

impl Clone for Index {
    fn clone(&self) -> Index {
        // We succeeded setup once, and so should succeed again unless the underlying database has
        // disappeared, in which case it is acceptable to terminate the program.
        Index::new(self.path.clone()).expect("Could not clone index")
    }
}

impl MsgHandler<Msg, Result<Reply, IndexError>> for Index {
    type Err = IndexError;

    fn handle(&mut self,
              msg: Msg,
              reply: Box<Fn(Result<Reply, IndexError>)>)
              -> Result<(), IndexError> {
        let reply_ok = |x| {
            reply(Ok(x));
            Ok(())
        };
        let reply_err = |e| {
            reply(Err(e));
            Ok(())
        };
        match msg {
            Msg::Insert(entry) => {
                use super::schema::keys::dsl::*;

                let entry = match entry.id {
                    Some(id_) => {
                        // Replace existing entry.
                        try!(diesel::update(keys.find(id_ as i64))
                                 .set((parent.eq(entry.parent_id.map(|x| x as i64)),
                                       name.eq(&entry.name[..]),
                                       created.eq(entry.created),
                                       modified.eq(entry.modified),
                                       accessed.eq(entry.accessed)))
                                 .execute(&self.conn));
                        entry
                    }
                    None => {
                        // Insert new entry.
                        {
                            let new = schema::NewKey {
                                parent: entry.parent_id.map(|x| x as i64),
                                name: &entry.name[..],
                                created: entry.created,
                                modified: entry.modified,
                                accessed: entry.accessed,
                                permissions: entry.permissions.map(|x| x as i64),
                                group_id: entry.group_id.map(|x| x as i64),
                                user_id: entry.user_id.map(|x| x as i64),
                                hash: None,
                                persistent_ref: None,
                            };

                            try!(diesel::insert(&new)
                                     .into(keys)
                                     .execute(&self.conn));
                        }
                        let mut entry = entry;
                        entry.id = Some(try!(self.last_insert_rowid()) as u64);
                        entry
                    }
                };

                return reply_ok(Reply::Entry(entry));
            }

            Msg::Lookup(parent_, name_) => {
                use super::schema::keys::dsl::*;

                let row_opt = match parent_ {
                    Some(p) => {
                        try!(keys.filter(parent.eq(p as i64))
                                 .filter(name.eq(&name_[..]))
                                 .first::<schema::Key>(&self.conn)
                                 .optional())
                    }
                    None => {
                        try!(keys.filter(parent.is_null())
                                 .filter(name.eq(&name_[..]))
                                 .first::<schema::Key>(&self.conn)
                                 .optional())
                    }
                };

                if let Some(row) = row_opt {
                    return reply_ok(Reply::Entry(Entry {
                        id: Some(row.id as u64),
                        parent_id: parent_,
                        name: name_,
                        created: row.created,
                        modified: row.modified,
                        accessed: row.accessed,
                        permissions: row.permissions.map(|x| x as u64),
                        user_id: row.user_id.map(|x| x as u64),
                        group_id: row.group_id.map(|x| x as u64),
                        data_hash: row.hash,
                        data_length: None,
                    }));
                }
                return reply_ok(Reply::NotFound);
            }

            Msg::UpdateDataHash(entry, hash_opt, persistent_ref_opt) => {
                use super::schema::keys::dsl::*;

                let id_ = match entry.id {
                    Some(i) => i as i64,
                    None => {
                        return reply_err(From::from("Tried to update data hash without an id"));
                    }
                };
                assert!(hash_opt.is_some() == persistent_ref_opt.is_some());

                let hash_bytes = hash_opt.map(|h| h.bytes);
                let persistent_ref_bytes = persistent_ref_opt.map(|p| p.as_bytes());

                if entry.modified.is_some() {
                    try!(diesel::update(keys.find(id_)
                                            .filter(modified.eq::<Option<i64>>(None)
                                                            .or(modified.le(entry.modified))))
                             .set((hash.eq(hash_bytes), persistent_ref.eq(persistent_ref_bytes)))
                             .execute(&self.conn));
                } else {
                    try!(diesel::update(keys.find(id_))
                             .set((hash.eq(hash_bytes), persistent_ref.eq(persistent_ref_bytes)))
                             .execute(&self.conn));
                }

                try!(self.maybe_flush());
                return reply_ok(Reply::UpdateOk);
            }

            Msg::Flush => {
                try!(self.flush());
                return reply_ok(Reply::FlushOk);
            }

            Msg::ListDir(parent_opt) => {
                use super::schema::keys::dsl::*;

                let rows = match parent_opt {
                    Some(p) => {
                        try!(keys.filter(parent.eq(p as i64))
                                 .load::<schema::Key>(&self.conn))
                    }
                    None => {
                        try!(keys.filter(parent.is_null())
                                 .load::<schema::Key>(&self.conn))
                    }
                };

                let res = rows.into_iter()
                              .map(|mut r| {
                                  (Entry {
                                      id: Some(r.id as u64),
                                      parent_id: r.parent.map(|x| x as u64),
                                      name: r.name,
                                      created: r.created,
                                      modified: r.modified,
                                      accessed: r.accessed,
                                      permissions: r.permissions
                                                    .map(|x| x as u64),
                                      user_id: r.user_id.map(|x| x as u64),
                                      group_id: r.group_id.map(|x| x as u64),
                                      data_hash: r.hash,
                                      data_length: None,
                                  },
                                   r.persistent_ref
                                    .as_mut()
                                    .map(|p| blob::ChunkRef::from_bytes(&mut &p[..]).unwrap()))
                              });
                return reply_ok(Reply::ListResult(res.collect()));
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Functions for computing [integral images](https://en.wikipedia.org/wiki/Summed_area_table)
//! and running sums of rows and columns.

extern crate image;

use image::{
    Luma,
    GenericImage,
    ImageBuffer
};

use definitions::{
    VecBuffer
};

use std::cmp;

/// Computes the integral image of an 8bpp grayscale image.
/// I is the integral image of an image F if I(x, y) is the
/// sum of F(x', y') for x' <= x, y' <= y. i.e. each pixel
/// in the integral image contains the sum of the pixel intensities
/// of all input pixels that are above it and to its left.
/// The integral image has the helpful property that it lets us
/// compute the sum of pixel intensities from any rectangular region
/// in the input image in constant time.
/// Specifically, given a rectangle in F with clockwise corners
/// A, B, C, D, with A at the upper left, the total pixel intensity
/// of this rectangle is I(C) - I(B) - I(D) + I(A).
// TODO: Support more formats.
// TODO: This is extremely slow. Fix that!
pub fn integral_image<I>(image: &I) -> VecBuffer<Luma<u32>>
    where I: GenericImage<Pixel=Luma<u8>> {
    padded_integral_image(image, 0, 0)
}

/// Computes the integral image of the result of padding image with
/// its boundary pixels for x_padding columns on either side and y_padding
/// rows at its top and bottom. Returned image has width image.width() + 2 * x_padding
/// and height image.height() + 2 * y_padding.
pub fn padded_integral_image<I>(image: &I, x_padding: u32, y_padding: u32) -> VecBuffer<Luma<u32>>
    where I: GenericImage<Pixel=Luma<u8>>
{
    let (in_width, in_height) = image.dimensions();
    let out_width = in_width + 2 * x_padding;
    let out_height = in_height + 2 * y_padding;

    let mut out: ImageBuffer<Luma<u32>, Vec<u32>> = ImageBuffer::new(out_width, out_height);

    if in_width == 0 || in_height == 0 {
        return out;
    }

    let mut top_sum = 0u32;
    for x in 0..out_width {
        let x_in = adjust_for_padding(x, x_padding, in_width);
        unsafe {
            top_sum += image.unsafe_get_pixel(x_in, 0)[0] as u32;
            out.unsafe_put_pixel(x, 0, Luma([top_sum]));
        }
    }

    for y in 1..out_height {
        let y_in = adjust_for_padding(y, y_padding, in_height);
        let mut sum = 0;

        for x in 0..out_width {
            let x_in = adjust_for_padding(x, x_padding, in_width);

            unsafe {
                sum += image.unsafe_get_pixel(x_in, y_in)[0] as u32;
                let above = out.unsafe_get_pixel(x, y - 1)[0];
                out.unsafe_put_pixel(x, y, Luma([above + sum]))
            }
        }
    }

    out
}

#[inline(always)]
fn adjust_for_padding(out_index: u32, padding: u32, upper: u32) -> u32 {
    if out_index < padding {
        return 0;
    } else if out_index >= upper + padding {
        return upper - 1;
    }
    return out_index - padding;
}

/// Computes the running sum of one row of image, padded
/// at the beginning and end. The padding is by continuity.
/// Takes a reference to buffer so that this can be reused
/// for all rows in an image.
// TODO: faster, more formats
pub fn row_running_sum<I>(image: &I, row: u32, buffer: &mut [u32], padding: u32)
    where I: GenericImage<Pixel=Luma<u8>> {

    let width = image.width();
    assert!(buffer.len() >= (width + 2 * padding) as usize,
        format!("Buffer length {} is less than {} + 2 * {}", buffer.len(), width, padding));
    assert!(row < image.height(),
        format!("row out of bound: {} >= {}", row, image.height()));

    for x in 0..padding {
        buffer[x as usize] = unsafe { image.unsafe_get_pixel(0, row)[0] as u32 };
    }

    for x in 0..width {
        let idx = (x + padding) as usize;
        buffer[idx] = unsafe { image.unsafe_get_pixel(x, row)[0] as u32 };
    }

    for x in 0..padding {
        let idx = (x + width + padding) as usize;
        buffer[idx] = unsafe { image.unsafe_get_pixel(width - 1, row)[0] as u32 };
    }

    for x in 1..width + 2 * padding {
        buffer[x as usize] += buffer[(x - 1) as usize] as u32;
    }
}

/// Computes the running sum of one column of image, padded
/// at the top and bottom. The padding is by continuity.
/// Takes a reference to buffer so that this can be reused
/// for all columns in an image.
// TODO: faster, more formats
pub fn column_running_sum<I>(image: &I, column: u32, buffer: &mut [u32], padding: u32)
    where I: GenericImage<Pixel=Luma<u8>> {

    let height = image.height();
    assert!(buffer.len() >= (height + 2 * padding) as usize,
        format!("Buffer length {} is less than {} + 2 * {}", buffer.len(), height, padding));
    assert!(column < image.width(),
        format!("column out of bound: {} >= {}", column, image.width()));

    for y in 0..padding {
        buffer[y as usize] = unsafe { image.unsafe_get_pixel(column, 0)[0] as u32 };
    }

    for y in 0..height {
        let idx = (y + padding) as usize;
        buffer[idx] = unsafe { image.unsafe_get_pixel(column, y)[0] as u32 };
    }

    for y in 0..padding {
        let idx = (y + height + padding) as usize;
        buffer[idx] = unsafe { image.unsafe_get_pixel(column, height - 1)[0] as u32 };
    }

    for y in 1..height + 2 * padding {
        buffer[y as usize] += buffer[(y - 1) as usize] as u32;
    }
}

#[cfg(test)]
mod test {

    use super::{
        column_running_sum,
        integral_image,
        padded_integral_image,
        row_running_sum
    };
    use utils::{
        gray_bench_image,
        GrayTestImage
    };
    use image::{
        GenericImage,
        GrayImage,
        ImageBuffer,
        Luma
    };
    use quickcheck::{
        quickcheck
    };
    use definitions::{
        VecBuffer
    };
    use test;

    #[test]
    fn test_integral_image() {
        let image: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            1, 2, 3,
            4, 5, 6]).unwrap();

        let expected: ImageBuffer<Luma<u32>, Vec<u32>>
            = ImageBuffer::from_raw(3, 2, vec![
            1,  3,  6,
            5, 12, 21]).unwrap();

        assert_pixels_eq!(integral_image(&image), expected);
    }

    #[bench]
    fn bench_integral_image(b: &mut test::Bencher) {
        let image = gray_bench_image(500, 500);
        b.iter(|| {
            let integral = integral_image(&image);
            test::black_box(integral);
            });
    }

    #[test]
    fn test_padded_integral_image() {
        let image: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            1, 2, 3,
            4, 5, 6]).unwrap();

        let expected: ImageBuffer<Luma<u32>, Vec<u32>>
            = ImageBuffer::from_raw(5, 4, vec![
              1,  2,   4,   7,  10,
              2,  4,   8,  14,  20,
              6, 12,  21,  33,  45,
             10, 20,  34,  52,  70]).unwrap();

        assert_pixels_eq!(padded_integral_image(&image, 1, 1), expected);
    }

    #[test]
    fn test_row_running_sum() {
        let image: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            1, 2, 3,
            4, 5, 6]).unwrap();

        let expected = [1, 2, 4, 7, 10];

        let mut buffer = [0; 5];
        row_running_sum(&image, 0, &mut buffer, 1);

        assert_eq!(buffer, expected);
    }

    #[bench]
    fn bench_row_running_sum(b: &mut test::Bencher) {
        let image = gray_bench_image(1000, 1);
        let mut buffer = [0; 1010];
        b.iter(|| {
            row_running_sum(&image, 0, &mut buffer, 5);
            });
    }

    #[test]
    fn test_column_running_sum() {
        let image: GrayImage = ImageBuffer::from_raw(2, 3, vec![
            1, 4,
            2, 5,
            3, 6]).unwrap();

        let expected = [1, 2, 4, 7, 10];

        let mut buffer = [0; 5];
        column_running_sum(&image, 0, &mut buffer, 1);

        assert_eq!(buffer, expected);
    }

    #[bench]
    fn bench_column_running_sum(b: &mut test::Bencher) {
        let image = gray_bench_image(100, 1000);
        let mut buffer = [0; 1010];
        b.iter(|| {
            column_running_sum(&image, 0, &mut buffer, 5);
            });
    }

    /// Simple implementation of integral_image to validate faster versions against.
    fn integral_image_ref<I>(image: &I) -> VecBuffer<Luma<u32>>
        where I: GenericImage<Pixel=Luma<u8>>
    {
        let (width, height) = image.dimensions();
        let mut out = ImageBuffer::new(width, height);

        for y in 0..height {
            for x in 0..width {
                let mut sum = 0u32;

                for iy in 0..(y + 1) {
                    for ix in 0..(x + 1) {
                        sum += image.get_pixel(ix, iy)[0] as u32;
                    }
                }

                out.put_pixel(x, y, Luma([sum]));
            }
        }

        out
    }

    #[test]
    fn test_integral_image_matches_reference_implementation() {
        fn prop(image: GrayTestImage) -> bool {
            let expected = integral_image_ref(&image.0);
            let actual = integral_image(&image.0);
            actual.pixels().eq(expected.pixels())
        }
        quickcheck(prop as fn(GrayTestImage) -> bool);
    }
}
<commit_msg>Remove unused import<commit_after>//! Functions for computing [integral images](https://en.wikipedia.org/wiki/Summed_area_table)
//! and running sums of rows and columns.

extern crate image;

use image::{
    Luma,
    GenericImage,
    ImageBuffer
};

use definitions::{
    VecBuffer
};

/// Computes the integral image of an 8bpp grayscale image.
/// I is the integral image of an image F if I(x, y) is the
/// sum of F(x', y') for x' <= x, y' <= y. i.e. each pixel
/// in the integral image contains the sum of the pixel intensities
/// of all input pixels that are above it and to its left.
/// The integral image has the helpful property that it lets us
/// compute the sum of pixel intensities from any rectangular region
/// in the input image in constant time.
/// Specifically, given a rectangle in F with clockwise corners
/// A, B, C, D, with A at the upper left, the total pixel intensity
/// of this rectangle is I(C) - I(B) - I(D) + I(A).
// TODO: Support more formats.
// TODO: This is extremely slow. Fix that!
pub fn integral_image<I>(image: &I) -> VecBuffer<Luma<u32>>
    where I: GenericImage<Pixel=Luma<u8>> {
    padded_integral_image(image, 0, 0)
}

/// Computes the integral image of the result of padding image with
/// its boundary pixels for x_padding columns on either side and y_padding
/// rows at its top and bottom. Returned image has width image.width() + 2 * x_padding
/// and height image.height() + 2 * y_padding.
pub fn padded_integral_image<I>(image: &I, x_padding: u32, y_padding: u32) -> VecBuffer<Luma<u32>>
    where I: GenericImage<Pixel=Luma<u8>>
{
    let (in_width, in_height) = image.dimensions();
    let out_width = in_width + 2 * x_padding;
    let out_height = in_height + 2 * y_padding;

    let mut out: ImageBuffer<Luma<u32>, Vec<u32>> = ImageBuffer::new(out_width, out_height);

    if in_width == 0 || in_height == 0 {
        return out;
    }

    let mut top_sum = 0u32;
    for x in 0..out_width {
        let x_in = adjust_for_padding(x, x_padding, in_width);
        unsafe {
            top_sum += image.unsafe_get_pixel(x_in, 0)[0] as u32;
            out.unsafe_put_pixel(x, 0, Luma([top_sum]));
        }
    }

    for y in 1..out_height {
        let y_in = adjust_for_padding(y, y_padding, in_height);
        let mut sum = 0;

        for x in 0..out_width {
            let x_in = adjust_for_padding(x, x_padding, in_width);

            unsafe {
                sum += image.unsafe_get_pixel(x_in, y_in)[0] as u32;
                let above = out.unsafe_get_pixel(x, y - 1)[0];
                out.unsafe_put_pixel(x, y, Luma([above + sum]))
            }
        }
    }

    out
}

#[inline(always)]
fn adjust_for_padding(out_index: u32, padding: u32, upper: u32) -> u32 {
    if out_index < padding {
        return 0;
    } else if out_index >= upper + padding {
        return upper - 1;
    }
    return out_index - padding;
}

/// Computes the running sum of one row of image, padded
/// at the beginning and end. The padding is by continuity.
/// Takes a reference to buffer so that this can be reused
/// for all rows in an image.
// TODO: faster, more formats
pub fn row_running_sum<I>(image: &I, row: u32, buffer: &mut [u32], padding: u32)
    where I: GenericImage<Pixel=Luma<u8>> {

    let width = image.width();
    assert!(buffer.len() >= (width + 2 * padding) as usize,
        format!("Buffer length {} is less than {} + 2 * {}", buffer.len(), width, padding));
    assert!(row < image.height(),
        format!("row out of bound: {} >= {}", row, image.height()));

    for x in 0..padding {
        buffer[x as usize] = unsafe { image.unsafe_get_pixel(0, row)[0] as u32 };
    }

    for x in 0..width {
        let idx = (x + padding) as usize;
        buffer[idx] = unsafe { image.unsafe_get_pixel(x, row)[0] as u32 };
    }

    for x in 0..padding {
        let idx = (x + width + padding) as usize;
        buffer[idx] = unsafe { image.unsafe_get_pixel(width - 1, row)[0] as u32 };
    }

    for x in 1..width + 2 * padding {
        buffer[x as usize] += buffer[(x - 1) as usize] as u32;
    }
}

/// Computes the running sum of one column of image, padded
/// at the top and bottom. The padding is by continuity.
/// Takes a reference to buffer so that this can be reused
/// for all columns in an image.
// TODO: faster, more formats
pub fn column_running_sum<I>(image: &I, column: u32, buffer: &mut [u32], padding: u32)
    where I: GenericImage<Pixel=Luma<u8>> {

    let height = image.height();
    assert!(buffer.len() >= (height + 2 * padding) as usize,
        format!("Buffer length {} is less than {} + 2 * {}", buffer.len(), height, padding));
    assert!(column < image.width(),
        format!("column out of bound: {} >= {}", column, image.width()));

    for y in 0..padding {
        buffer[y as usize] = unsafe { image.unsafe_get_pixel(column, 0)[0] as u32 };
    }

    for y in 0..height {
        let idx = (y + padding) as usize;
        buffer[idx] = unsafe { image.unsafe_get_pixel(column, y)[0] as u32 };
    }

    for y in 0..padding {
        let idx = (y + height + padding) as usize;
        buffer[idx] = unsafe { image.unsafe_get_pixel(column, height - 1)[0] as u32 };
    }

    for y in 1..height + 2 * padding {
        buffer[y as usize] += buffer[(y - 1) as usize] as u32;
    }
}

#[cfg(test)]
mod test {

    use super::{
        column_running_sum,
        integral_image,
        padded_integral_image,
        row_running_sum
    };
    use utils::{
        gray_bench_image,
        GrayTestImage
    };
    use image::{
        GenericImage,
        GrayImage,
        ImageBuffer,
        Luma
    };
    use quickcheck::{
        quickcheck
    };
    use definitions::{
        VecBuffer
    };
    use test;

    #[test]
    fn test_integral_image() {
        let image: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            1, 2, 3,
            4, 5, 6]).unwrap();

        let expected: ImageBuffer<Luma<u32>, Vec<u32>>
            = ImageBuffer::from_raw(3, 2, vec![
            1,  3,  6,
            5, 12, 21]).unwrap();

        assert_pixels_eq!(integral_image(&image), expected);
    }

    #[bench]
    fn bench_integral_image(b: &mut test::Bencher) {
        let image = gray_bench_image(500, 500);
        b.iter(|| {
            let integral = integral_image(&image);
            test::black_box(integral);
            });
    }

    #[test]
    fn test_padded_integral_image() {
        let image: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            1, 2, 3,
            4, 5, 6]).unwrap();

        let expected: ImageBuffer<Luma<u32>, Vec<u32>>
            = ImageBuffer::from_raw(5, 4, vec![
              1,  2,   4,   7,  10,
              2,  4,   8,  14,  20,
              6, 12,  21,  33,  45,
             10, 20,  34,  52,  70]).unwrap();

        assert_pixels_eq!(padded_integral_image(&image, 1, 1), expected);
    }

    #[test]
    fn test_row_running_sum() {
        let image: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            1, 2, 3,
            4, 5, 6]).unwrap();

        let expected = [1, 2, 4, 7, 10];

        let mut buffer = [0; 5];
        row_running_sum(&image, 0, &mut buffer, 1);

        assert_eq!(buffer, expected);
    }

    #[bench]
    fn bench_row_running_sum(b: &mut test::Bencher) {
        let image = gray_bench_image(1000, 1);
        let mut buffer = [0; 1010];
        b.iter(|| {
            row_running_sum(&image, 0, &mut buffer, 5);
            });
    }

    #[test]
    fn test_column_running_sum() {
        let image: GrayImage = ImageBuffer::from_raw(2, 3, vec![
            1, 4,
            2, 5,
            3, 6]).unwrap();

        let expected = [1, 2, 4, 7, 10];

        let mut buffer = [0; 5];
        column_running_sum(&image, 0, &mut buffer, 1);

        assert_eq!(buffer, expected);
    }

    #[bench]
    fn bench_column_running_sum(b: &mut test::Bencher) {
        let image = gray_bench_image(100, 1000);
        let mut buffer = [0; 1010];
        b.iter(|| {
            column_running_sum(&image, 0, &mut buffer, 5);
            });
    }

    /// Simple implementation of integral_image to validate faster versions against.
    fn integral_image_ref<I>(image: &I) -> VecBuffer<Luma<u32>>
        where I: GenericImage<Pixel=Luma<u8>>
    {
        let (width, height) = image.dimensions();
        let mut out = ImageBuffer::new(width, height);

        for y in 0..height {
            for x in 0..width {
                let mut sum = 0u32;

                for iy in 0..(y + 1) {
                    for ix in 0..(x + 1) {
                        sum += image.get_pixel(ix, iy)[0] as u32;
                    }
                }

                out.put_pixel(x, y, Luma([sum]));
            }
        }

        out
    }

    #[test]
    fn test_integral_image_matches_reference_implementation() {
        fn prop(image: GrayTestImage) -> bool {
            let expected = integral_image_ref(&image.0);
            let actual = integral_image(&image.0);
            actual.pixels().eq(expected.pixels())
        }
        quickcheck(prop as fn(GrayTestImage) -> bool);
    }
}
<|endoftext|>


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

/*! The Clone trait for types that cannot be "implicitly copied"

In Rust, some simple types are "implicitly copyable" and when you
assign them or pass them as arguments, the receiver will get a copy,
leaving the original value in place. These types do not require
allocation to copy and do not have finalizers (i.e. they do not
contain owned boxes or implement `Drop`), so the compiler considers
them cheap and safe to copy and automatically implements the `Copy`
trait for them. For other types copies must be made explicitly,
by convention implementing the `Clone` trait and calling the
`clone` method.

*/

pub trait Clone {
    /// Return a deep copy of the owned object tree. Managed boxes are cloned with a shallow copy.
    fn clone(&self) -> Self;
}

impl Clone for () {
    /// Return a copy of the value.
    #[inline(always)]
    fn clone(&self) -> () { () }
}

impl<T:Clone> Clone for ~T {
    /// Return a deep copy of the owned box.
    #[inline(always)]
    fn clone(&self) -> ~T { ~(**self).clone() }
}

impl<T> Clone for @T {
    /// Return a shallow copy of the managed box.
    #[inline(always)]
    fn clone(&self) -> @T { *self }
}

impl<T> Clone for @mut T {
    /// Return a shallow copy of the managed box.
    #[inline(always)]
    fn clone(&self) -> @mut T { *self }
}

macro_rules! clone_impl(
    ($t:ty) => {
        impl Clone for $t {
            /// Return a copy of the value.
            #[inline(always)]
            fn clone(&self) -> $t { *self }
        }
    }
)

clone_impl!(int)
clone_impl!(i8)
clone_impl!(i16)
clone_impl!(i32)
clone_impl!(i64)

clone_impl!(uint)
clone_impl!(u8)
clone_impl!(u16)
clone_impl!(u32)
clone_impl!(u64)

clone_impl!(float)
clone_impl!(f32)
clone_impl!(f64)

clone_impl!(bool)
clone_impl!(char)

#[test]
fn test_owned_clone() {
    let a: ~int = ~5i;
    let b: ~int = a.clone();
    assert!(a == b);
}

#[test]
fn test_managed_clone() {
    let a: @int = @5i;
    let b: @int = a.clone();
    assert!(a == b);
}

#[test]
fn test_managed_mut_clone() {
    let a: @mut int = @mut 5i;
    let b: @mut int = a.clone();
    assert!(a == b);
    *b = 10;
    assert!(a == b);
}
<commit_msg>clone: clarify docstring<commit_after>// Copyright 2012-2013 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.

/*! The Clone trait for types that cannot be "implicitly copied"

In Rust, some simple types are "implicitly copyable" and when you
assign them or pass them as arguments, the receiver will get a copy,
leaving the original value in place. These types do not require
allocation to copy and do not have finalizers (i.e. they do not
contain owned boxes or implement `Drop`), so the compiler considers
them cheap and safe to copy and automatically implements the `Copy`
trait for them. For other types copies must be made explicitly,
by convention implementing the `Clone` trait and calling the
`clone` method.

*/

pub trait Clone {
    /// Return a deep copy of the owned object tree. Types with shared ownership like managed boxes
    /// are cloned with a shallow copy.
    fn clone(&self) -> Self;
}

impl Clone for () {
    /// Return a copy of the value.
    #[inline(always)]
    fn clone(&self) -> () { () }
}

impl<T: Clone> Clone for ~T {
    /// Return a deep copy of the owned box.
    #[inline(always)]
    fn clone(&self) -> ~T { ~(**self).clone() }
}

impl<T> Clone for @T {
    /// Return a shallow copy of the managed box.
    #[inline(always)]
    fn clone(&self) -> @T { *self }
}

impl<T> Clone for @mut T {
    /// Return a shallow copy of the managed box.
    #[inline(always)]
    fn clone(&self) -> @mut T { *self }
}

macro_rules! clone_impl(
    ($t:ty) => {
        impl Clone for $t {
            /// Return a copy of the value.
            #[inline(always)]
            fn clone(&self) -> $t { *self }
        }
    }
)

clone_impl!(int)
clone_impl!(i8)
clone_impl!(i16)
clone_impl!(i32)
clone_impl!(i64)

clone_impl!(uint)
clone_impl!(u8)
clone_impl!(u16)
clone_impl!(u32)
clone_impl!(u64)

clone_impl!(float)
clone_impl!(f32)
clone_impl!(f64)

clone_impl!(bool)
clone_impl!(char)

#[test]
fn test_owned_clone() {
    let a: ~int = ~5i;
    let b: ~int = a.clone();
    assert!(a == b);
}

#[test]
fn test_managed_clone() {
    let a: @int = @5i;
    let b: @int = a.clone();
    assert!(a == b);
}

#[test]
fn test_managed_mut_clone() {
    let a: @mut int = @mut 5i;
    let b: @mut int = a.clone();
    assert!(a == b);
    *b = 10;
    assert!(a == b);
}
<|endoftext|>


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

//! Base64 binary-to-text encoding

use core::old_iter;
use core::str;
use core::vec;

pub trait ToBase64 {
    fn to_base64(&self) -> ~str;
}

static CHARS: [char, ..64] = [
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
    'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
    'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
];

impl<'self> ToBase64 for &'self [u8] {
    /**
     * Turn a vector of `u8` bytes into a base64 string.
     *
     * *Example*:
     *
     * ~~~~
     * extern mod std;
     * use std::base64::ToBase64;
     *
     * fn main () {
     *     let str = [52,32].to_base64();
     *     println(fmt!("%s", str));
     * }
     * ~~~~
     */
    fn to_base64(&self) -> ~str {
        let mut s = ~"";
        let len = self.len();
        str::reserve(&mut s, ((len + 3u) / 4u) * 3u);

        let mut i = 0u;

        while i < len - (len % 3u) {
            let n = (self[i] as uint) << 16u |
                    (self[i + 1u] as uint) << 8u |
                    (self[i + 2u] as uint);

            // This 24-bit number gets separated into four 6-bit numbers.
            str::push_char(&mut s, CHARS[(n >> 18u) & 63u]);
            str::push_char(&mut s, CHARS[(n >> 12u) & 63u]);
            str::push_char(&mut s, CHARS[(n >> 6u) & 63u]);
            str::push_char(&mut s, CHARS[n & 63u]);

            i += 3u;
        }

        // Heh, would be cool if we knew this was exhaustive
        // (the dream of bounded integer types)
        match len % 3 {
          0 => (),
          1 => {
            let n = (self[i] as uint) << 16u;
            str::push_char(&mut s, CHARS[(n >> 18u) & 63u]);
            str::push_char(&mut s, CHARS[(n >> 12u) & 63u]);
            str::push_char(&mut s, '=');
            str::push_char(&mut s, '=');
          }
          2 => {
            let n = (self[i] as uint) << 16u |
                (self[i + 1u] as uint) << 8u;
            str::push_char(&mut s, CHARS[(n >> 18u) & 63u]);
            str::push_char(&mut s, CHARS[(n >> 12u) & 63u]);
            str::push_char(&mut s, CHARS[(n >> 6u) & 63u]);
            str::push_char(&mut s, '=');
          }
          _ => fail!(~"Algebra is broken, please alert the math police")
        }
        s
    }
}

impl<'self> ToBase64 for &'self str {
    /**
     * Convert any string (literal, `@`, `&`, or `~`) to base64 encoding.
     *
     *
     * *Example*:
     *
     * ~~~~
     * extern mod std;
     * use std::base64::ToBase64;
     *
     * fn main () {
     *     let str = "Hello, World".to_base64();
     *     println(fmt!("%s",str));
     * }
     * ~~~~
     *
     */
    fn to_base64(&self) -> ~str {
        str::to_bytes(*self).to_base64()
    }
}

pub trait FromBase64 {
    fn from_base64(&self) -> ~[u8];
}

impl FromBase64 for ~[u8] {
    /**
     * Convert base64 `u8` vector into u8 byte values.
     * Every 4 encoded characters is converted into 3 octets, modulo padding.
     *
     * *Example*:
     *
     * ~~~~
     * extern mod std;
     * use std::base64::ToBase64;
     * use std::base64::FromBase64;
     *
     * fn main () {
     *     let str = [52,32].to_base64();
     *     println(fmt!("%s", str));
     *     let bytes = str.from_base64();
     *     println(fmt!("%?",bytes));
     * }
     * ~~~~
     */
    fn from_base64(&self) -> ~[u8] {
        if self.len() % 4u != 0u { fail!(~"invalid base64 length"); }

        let len = self.len();
        let mut padding = 0u;

        if len != 0u {
            if self[len - 1u] == '=' as u8 { padding += 1u; }
            if self[len - 2u] == '=' as u8 { padding += 1u; }
        }

        let mut r = vec::with_capacity((len / 4u) * 3u - padding);

        let mut i = 0u;
        while i < len {
            let mut n = 0u;

            for old_iter::repeat(4u) {
                let ch = self[i] as char;
                n <<= 6u;

                if ch >= 'A' && ch <= 'Z' {
                    n |= (ch as uint) - 0x41u;
                } else if ch >= 'a' && ch <= 'z' {
                    n |= (ch as uint) - 0x47u;
                } else if ch >= '0' && ch <= '9' {
                    n |= (ch as uint) + 0x04u;
                } else if ch == '+' {
                    n |= 0x3Eu;
                } else if ch == '/' {
                    n |= 0x3Fu;
                } else if ch == '=' {
                    match len - i {
                      1u => {
                        r.push(((n >> 16u) & 0xFFu) as u8);
                        r.push(((n >> 8u ) & 0xFFu) as u8);
                        return copy r;
                      }
                      2u => {
                        r.push(((n >> 10u) & 0xFFu) as u8);
                        return copy r;
                      }
                      _ => fail!(~"invalid base64 padding")
                    }
                } else {
                    fail!(~"invalid base64 character");
                }

                i += 1u;
            };

            r.push(((n >> 16u) & 0xFFu) as u8);
            r.push(((n >> 8u ) & 0xFFu) as u8);
            r.push(((n       ) & 0xFFu) as u8);
        }
        r
    }
}

impl FromBase64 for ~str {
    /**
     * Convert any base64 encoded string (literal, `@`, `&`, or `~`)
     * to the byte values it encodes.
     *
     * You can use the `from_bytes` function in `core::str`
     * to turn a `[u8]` into a string with characters corresponding to those values.
     *
     * *Example*:
     *
     * This converts a string literal to base64 and back.
     *
     * ~~~~
     * extern mod std;
     * use std::base64::ToBase64;
     * use std::base64::FromBase64;
     * use core::str;
     *
     * fn main () {
     *     let hello_str = "Hello, World".to_base64();
     *     println(fmt!("%s",hello_str));
     *     let bytes = hello_str.from_base64();
     *     println(fmt!("%?",bytes));
     *     let result_str = str::from_bytes(bytes);
     *     println(fmt!("%s",result_str));
     * }
     * ~~~~
     */
    fn from_base64(&self) -> ~[u8] {
        str::to_bytes(*self).from_base64()
    }
}

#[cfg(test)]
mod tests {
    use core::str;

    #[test]
    fn test_to_base64() {
        assert!((~"").to_base64()       == ~"");
        assert!((~"f").to_base64()      == ~"Zg==");
        assert!((~"fo").to_base64()     == ~"Zm8=");
        assert!((~"foo").to_base64()    == ~"Zm9v");
        assert!((~"foob").to_base64()   == ~"Zm9vYg==");
        assert!((~"fooba").to_base64()  == ~"Zm9vYmE=");
        assert!((~"foobar").to_base64() == ~"Zm9vYmFy");
    }

    #[test]
    fn test_from_base64() {
        assert!((~"").from_base64() == str::to_bytes(~""));
        assert!((~"Zg==").from_base64() == str::to_bytes(~"f"));
        assert!((~"Zm8=").from_base64() == str::to_bytes(~"fo"));
        assert!((~"Zm9v").from_base64() == str::to_bytes(~"foo"));
        assert!((~"Zm9vYg==").from_base64() == str::to_bytes(~"foob"));
        assert!((~"Zm9vYmE=").from_base64() == str::to_bytes(~"fooba"));
        assert!((~"Zm9vYmFy").from_base64() == str::to_bytes(~"foobar"));
    }
}
<commit_msg>libstd: convert chained ifs to a match in base64.<commit_after>// Copyright 2012-2013 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.

//! Base64 binary-to-text encoding

use core::old_iter;
use core::str;
use core::vec;

pub trait ToBase64 {
    fn to_base64(&self) -> ~str;
}

static CHARS: [char, ..64] = [
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
    'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
    'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
];

impl<'self> ToBase64 for &'self [u8] {
    /**
     * Turn a vector of `u8` bytes into a base64 string.
     *
     * *Example*:
     *
     * ~~~~
     * extern mod std;
     * use std::base64::ToBase64;
     *
     * fn main () {
     *     let str = [52,32].to_base64();
     *     println(fmt!("%s", str));
     * }
     * ~~~~
     */
    fn to_base64(&self) -> ~str {
        let mut s = ~"";
        let len = self.len();
        str::reserve(&mut s, ((len + 3u) / 4u) * 3u);

        let mut i = 0u;

        while i < len - (len % 3u) {
            let n = (self[i] as uint) << 16u |
                    (self[i + 1u] as uint) << 8u |
                    (self[i + 2u] as uint);

            // This 24-bit number gets separated into four 6-bit numbers.
            str::push_char(&mut s, CHARS[(n >> 18u) & 63u]);
            str::push_char(&mut s, CHARS[(n >> 12u) & 63u]);
            str::push_char(&mut s, CHARS[(n >> 6u) & 63u]);
            str::push_char(&mut s, CHARS[n & 63u]);

            i += 3u;
        }

        // Heh, would be cool if we knew this was exhaustive
        // (the dream of bounded integer types)
        match len % 3 {
          0 => (),
          1 => {
            let n = (self[i] as uint) << 16u;
            str::push_char(&mut s, CHARS[(n >> 18u) & 63u]);
            str::push_char(&mut s, CHARS[(n >> 12u) & 63u]);
            str::push_char(&mut s, '=');
            str::push_char(&mut s, '=');
          }
          2 => {
            let n = (self[i] as uint) << 16u |
                (self[i + 1u] as uint) << 8u;
            str::push_char(&mut s, CHARS[(n >> 18u) & 63u]);
            str::push_char(&mut s, CHARS[(n >> 12u) & 63u]);
            str::push_char(&mut s, CHARS[(n >> 6u) & 63u]);
            str::push_char(&mut s, '=');
          }
          _ => fail!(~"Algebra is broken, please alert the math police")
        }
        s
    }
}

impl<'self> ToBase64 for &'self str {
    /**
     * Convert any string (literal, `@`, `&`, or `~`) to base64 encoding.
     *
     *
     * *Example*:
     *
     * ~~~~
     * extern mod std;
     * use std::base64::ToBase64;
     *
     * fn main () {
     *     let str = "Hello, World".to_base64();
     *     println(fmt!("%s",str));
     * }
     * ~~~~
     *
     */
    fn to_base64(&self) -> ~str {
        str::to_bytes(*self).to_base64()
    }
}

pub trait FromBase64 {
    fn from_base64(&self) -> ~[u8];
}

impl FromBase64 for ~[u8] {
    /**
     * Convert base64 `u8` vector into u8 byte values.
     * Every 4 encoded characters is converted into 3 octets, modulo padding.
     *
     * *Example*:
     *
     * ~~~~
     * extern mod std;
     * use std::base64::ToBase64;
     * use std::base64::FromBase64;
     *
     * fn main () {
     *     let str = [52,32].to_base64();
     *     println(fmt!("%s", str));
     *     let bytes = str.from_base64();
     *     println(fmt!("%?",bytes));
     * }
     * ~~~~
     */
    fn from_base64(&self) -> ~[u8] {
        if self.len() % 4u != 0u { fail!(~"invalid base64 length"); }

        let len = self.len();
        let mut padding = 0u;

        if len != 0u {
            if self[len - 1u] == '=' as u8 { padding += 1u; }
            if self[len - 2u] == '=' as u8 { padding += 1u; }
        }

        let mut r = vec::with_capacity((len / 4u) * 3u - padding);

        let mut i = 0u;
        while i < len {
            let mut n = 0u;

            for old_iter::repeat(4u) {
                let ch = self[i] as char;
                n <<= 6u;

                match ch {
                    'A'..'Z' => n |= (ch as uint) - 0x41,
                    'a'..'z' => n |= (ch as uint) - 0x47,
                    '0'..'9' => n |= (ch as uint) + 0x04,
                    '+'      => n |= 0x3E,
                    '/'      => n |= 0x3F,
                    '='      => {
                        match len - i {
                            1u => {
                                r.push(((n >> 16u) & 0xFFu) as u8);
                                r.push(((n >> 8u ) & 0xFFu) as u8);
                                return copy r;
                            }
                            2u => {
                                r.push(((n >> 10u) & 0xFFu) as u8);
                                return copy r;
                            }
                            _ => fail!(~"invalid base64 padding")
                        }
                    }
                    _ => fail!(~"invalid base64 character")
                }

                i += 1u;
            };

            r.push(((n >> 16u) & 0xFFu) as u8);
            r.push(((n >> 8u ) & 0xFFu) as u8);
            r.push(((n       ) & 0xFFu) as u8);
        }
        r
    }
}

impl FromBase64 for ~str {
    /**
     * Convert any base64 encoded string (literal, `@`, `&`, or `~`)
     * to the byte values it encodes.
     *
     * You can use the `from_bytes` function in `core::str`
     * to turn a `[u8]` into a string with characters corresponding to those values.
     *
     * *Example*:
     *
     * This converts a string literal to base64 and back.
     *
     * ~~~~
     * extern mod std;
     * use std::base64::ToBase64;
     * use std::base64::FromBase64;
     * use core::str;
     *
     * fn main () {
     *     let hello_str = "Hello, World".to_base64();
     *     println(fmt!("%s",hello_str));
     *     let bytes = hello_str.from_base64();
     *     println(fmt!("%?",bytes));
     *     let result_str = str::from_bytes(bytes);
     *     println(fmt!("%s",result_str));
     * }
     * ~~~~
     */
    fn from_base64(&self) -> ~[u8] {
        str::to_bytes(*self).from_base64()
    }
}

#[cfg(test)]
mod tests {
    use core::str;

    #[test]
    fn test_to_base64() {
        assert!((~"").to_base64()       == ~"");
        assert!((~"f").to_base64()      == ~"Zg==");
        assert!((~"fo").to_base64()     == ~"Zm8=");
        assert!((~"foo").to_base64()    == ~"Zm9v");
        assert!((~"foob").to_base64()   == ~"Zm9vYg==");
        assert!((~"fooba").to_base64()  == ~"Zm9vYmE=");
        assert!((~"foobar").to_base64() == ~"Zm9vYmFy");
    }

    #[test]
    fn test_from_base64() {
        assert!((~"").from_base64() == str::to_bytes(~""));
        assert!((~"Zg==").from_base64() == str::to_bytes(~"f"));
        assert!((~"Zm8=").from_base64() == str::to_bytes(~"fo"));
        assert!((~"Zm9v").from_base64() == str::to_bytes(~"foo"));
        assert!((~"Zm9vYg==").from_base64() == str::to_bytes(~"foob"));
        assert!((~"Zm9vYmE=").from_base64() == str::to_bytes(~"fooba"));
        assert!((~"Zm9vYmFy").from_base64() == str::to_bytes(~"foobar"));
    }
}
<|endoftext|>


<commit_before>#[doc="
Types/fns concerning Internet Protocol (IP), versions 4 & 6
"];

import vec;
import uint;

import sockaddr_in = uv::ll::sockaddr_in;
import sockaddr_in6 = uv::ll::sockaddr_in6;
import uv_ip4_addr = uv::ll::ip4_addr;
import uv_ip4_name = uv::ll::ip4_name;
import uv_ip6_addr = uv::ll::ip6_addr;
import uv_ip6_name = uv::ll::ip6_name;

export ip_addr, parse_addr_err;
export format_addr;
export v4;

#[doc = "An IP address"]
enum ip_addr {
    #[doc="An IPv4 address"]
    ipv4(sockaddr_in),
    ipv6(sockaddr_in6)
}

#[doc="
Human-friendly feedback on why a parse_addr attempt failed
"]
type parse_addr_err = {
    err_msg: str
};

#[doc="
Convert a `ip_addr` to a str

# Arguments

* ip - a `std::net::ip::ip_addr`
"]
fn format_addr(ip: ip_addr) -> str {
    alt ip {
      ipv4(addr) {
        unsafe {
            let result = uv_ip4_name(&addr);
            if result == "" {
                fail "failed to convert inner sockaddr_in address to str"
            }
            result
        }
      }
      ipv6(addr) {
        unsafe {
            let result = uv_ip6_name(&addr);
            if result == "" {
                fail "failed to convert inner sockaddr_in address to str"
            }
            result
        }
      }
    }
}

mod v4 {
    #[doc = "
    Convert a str to `ip_addr`

    # Failure

    Fails if the string is not a valid IPv4 address

    # Arguments

    * ip - a string of the format `x.x.x.x`

    # Returns

    * an `ip_addr` of the `ipv4` variant
    "]
    fn parse_addr(ip: str) -> ip_addr {
        alt try_parse_addr(ip) {
          // FIXME: more copies brought to light to due the implicit
          // copy compiler warning.. what can be done? out pointers,
          // ala c#?
          result::ok(addr) { copy(addr) }
          result::err(err_data) {
            fail err_data.err_msg
          }
        }
    }
    fn try_parse_addr(ip: str) -> result::result<ip_addr,parse_addr_err> {
        unsafe {
            // need to figure out how to establish a parse failure..
            let new_addr = uv_ip4_addr(ip, 22);
            let reformatted_name = uv_ip4_name(&new_addr);
            log(debug, #fmt("try_parse_addr: input ip: %s reparsed ip: %s",
                            ip, reformatted_name));
            // here we're going to
            let inaddr_none_val = "255.255.255.255";
            if ip != inaddr_none_val && reformatted_name == inaddr_none_val {
                result::err({err_msg:#fmt("failed to parse '%s'",
                                           ip)})
            }
            else {
                result::ok(ipv4(new_addr))
            }
        }
    }
}
mod v6 {
    #[doc = "
    Convert a str to `ip_addr`

    # Failure

    Fails if the string is not a valid IPv6 address

    # Arguments

    * ip - an ipv6 string. See RFC2460 for spec.

    # Returns

    * an `ip_addr` of the `ipv6` variant
    "]
    fn parse_addr(ip: str) -> ip_addr {
        alt try_parse_addr(ip) {
          // FIXME: more copies brought to light to due the implicit
          // copy compiler warning.. what can be done? out pointers,
          // ala c#?
          result::ok(addr) { copy(addr) }
          result::err(err_data) {
            fail err_data.err_msg
          }
        }
    }
    fn try_parse_addr(ip: str) -> result::result<ip_addr,parse_addr_err> {
        unsafe {
            // need to figure out how to establish a parse failure..
            let new_addr = uv_ip6_addr(ip, 22);
            let reparsed_name = uv_ip6_name(&new_addr);
            log(debug, #fmt("v6::try_parse_addr ip: '%s' reparsed '%s'",
                            ip, reparsed_name));
            // '::' appears to be uv_ip6_name() returns for bogus
            // parses..
            if  ip != "::" && reparsed_name == "::" {
                result::err({err_msg:#fmt("failed to parse '%s'",
                                           ip)})
            }
            else {
                result::ok(ipv6(new_addr))
            }
        }
    }
}

//#[cfg(test)]
mod test {
    #[test]
    fn test_ipv4_parse_and_format_ip() {
        let localhost_str = "127.0.0.1";
        assert (format_addr(v4::parse_addr(localhost_str))
                == localhost_str)
    }
    #[test]
    fn test_ipv6_parse_and_format_ip() {
        let localhost_str = "::1";
        let format_result = format_addr(v6::parse_addr(localhost_str));
        log(debug, #fmt("results: expected: '%s' actual: '%s'",
            localhost_str, format_result));
        assert format_result == localhost_str;
    }
    #[test]
    fn test_ipv4_bad_parse() {
        alt v4::try_parse_addr("b4df00d") {
          result::err(err_info) {
            log(debug, #fmt("got error as expected %?", err_info));
            assert true;
          }
          result::ok(addr) {
            fail #fmt("Expected failure, but got addr %?", addr);
          }
        }
    }
    #[test]
    fn test_ipv6_bad_parse() {
        alt v6::try_parse_addr("::,~2234k;") {
          result::err(err_info) {
            log(debug, #fmt("got error as expected %?", err_info));
            assert true;
          }
          result::ok(addr) {
            fail #fmt("Expected failure, but got addr %?", addr);
          }
        }
    }
}<commit_msg>std: roughcut impl of net::ip::get_addr()<commit_after>#[doc="
Types/fns concerning Internet Protocol (IP), versions 4 & 6
"];

import vec;
import uint;
import iotask = uv::iotask::iotask;
import interact = uv::iotask::interact;
import comm::methods;

import sockaddr_in = uv::ll::sockaddr_in;
import sockaddr_in6 = uv::ll::sockaddr_in6;
import addrinfo = uv::ll::addrinfo;
import uv_getaddrinfo_t = uv::ll::uv_getaddrinfo_t;
import uv_ip4_addr = uv::ll::ip4_addr;
import uv_ip4_name = uv::ll::ip4_name;
import uv_ip6_addr = uv::ll::ip6_addr;
import uv_ip6_name = uv::ll::ip6_name;
import uv_getaddrinfo = uv::ll::getaddrinfo;
import create_uv_getaddrinfo_t = uv::ll::getaddrinfo_t;
import set_data_for_uv_handle = uv::ll::set_data_for_uv_handle;
import get_data_for_uv_handle = uv::ll::get_data_for_uv_handle;
import ll = uv::ll;

export ip_addr, parse_addr_err;
export format_addr;
export v4, v6;
export get_addr;

#[doc = "An IP address"]
enum ip_addr {
    #[doc="An IPv4 address"]
    ipv4(sockaddr_in),
    ipv6(sockaddr_in6)
}

#[doc="
Human-friendly feedback on why a parse_addr attempt failed
"]
type parse_addr_err = {
    err_msg: str
};

#[doc="
Convert a `ip_addr` to a str

# Arguments

* ip - a `std::net::ip::ip_addr`
"]
fn format_addr(ip: ip_addr) -> str {
    alt ip {
      ipv4(addr) {
        unsafe {
            let result = uv_ip4_name(&addr);
            if result == "" {
                fail "failed to convert inner sockaddr_in address to str"
            }
            result
        }
      }
      ipv6(addr) {
        unsafe {
            let result = uv_ip6_name(&addr);
            if result == "" {
                fail "failed to convert inner sockaddr_in address to str"
            }
            result
        }
      }
    }
}

type get_addr_data = {
    output_ch: comm::chan<result::result<[ip_addr],ip_get_addr_err>>
};

crust fn get_addr_cb(handle: *uv_getaddrinfo_t, status: libc::c_int,
                     res: *addrinfo) unsafe {
    let handle_data = get_data_for_uv_handle(handle) as
        *get_addr_data;
    if status == 0i32 {
        if res != (ptr::null::<addrinfo>()) {
            let mut out_vec = [];
            let mut curr_addr = res;
            loop {
                if ll::is_ipv4_addrinfo(curr_addr) {
                    out_vec +=
                        [ipv4(copy((
                            *ll::addrinfo_as_sockaddr_in(curr_addr))))];
                }
                else {
                    out_vec +=
                        [ipv6(copy((
                            *ll::addrinfo_as_sockaddr_in6(curr_addr))))];
                }

                let next_addr = ll::get_next_addrinfo(curr_addr);
                if next_addr == ptr::null::<addrinfo>() as *addrinfo {
                    break;
                }
                else {
                    curr_addr = next_addr
                }
            }
            (*handle_data).output_ch.send(result::ok(out_vec));
        }
        else {
            (*handle_data).output_ch.send(
                result::err(get_addr_unknown_error));
        }
    }
    else {
        (*handle_data).output_ch.send(
            result::err(get_addr_unknown_error));
    }
}

#[doc="
"]
enum ip_get_addr_err {
    get_addr_unknown_error
}

#[doc="
"]
fn get_addr(++node: str, iotask: iotask)
        -> result::result<[ip_addr], ip_get_addr_err> unsafe {
    comm::listen {|output_ch|
        str::unpack_slice(node) {|node_ptr, len|
            log(debug, #fmt("sliace len %?", len));
            let handle = create_uv_getaddrinfo_t();
            let handle_ptr = ptr::addr_of(handle);
            let handle_data: get_addr_data = {
                output_ch: output_ch
            };
            let handle_data_ptr = ptr::addr_of(handle_data);
            interact(iotask) {|loop_ptr|
                let result = uv_getaddrinfo(
                    loop_ptr,
                    handle_ptr,
                    get_addr_cb,
                    node_ptr,
                    ptr::null(),
                    ptr::null());
                alt result {
                  0i32 {
                    set_data_for_uv_handle(handle_ptr, handle_data_ptr);
                  }
                  _ {
                    output_ch.send(result::err(get_addr_unknown_error));
                  }
                }
            };
            output_ch.recv()
        }
    }
}

mod v4 {
    #[doc = "
    Convert a str to `ip_addr`

    # Failure

    Fails if the string is not a valid IPv4 address

    # Arguments

    * ip - a string of the format `x.x.x.x`

    # Returns

    * an `ip_addr` of the `ipv4` variant
    "]
    fn parse_addr(ip: str) -> ip_addr {
        alt try_parse_addr(ip) {
          // FIXME: more copies brought to light to due the implicit
          // copy compiler warning.. what can be done? out pointers,
          // ala c#?
          result::ok(addr) { copy(addr) }
          result::err(err_data) {
            fail err_data.err_msg
          }
        }
    }
    fn try_parse_addr(ip: str) -> result::result<ip_addr,parse_addr_err> {
        unsafe {
            // need to figure out how to establish a parse failure..
            let new_addr = uv_ip4_addr(ip, 22);
            let reformatted_name = uv_ip4_name(&new_addr);
            log(debug, #fmt("try_parse_addr: input ip: %s reparsed ip: %s",
                            ip, reformatted_name));
            // here we're going to
            let inaddr_none_val = "255.255.255.255";
            if ip != inaddr_none_val && reformatted_name == inaddr_none_val {
                result::err({err_msg:#fmt("failed to parse '%s'",
                                           ip)})
            }
            else {
                result::ok(ipv4(new_addr))
            }
        }
    }
}
mod v6 {
    #[doc = "
    Convert a str to `ip_addr`

    # Failure

    Fails if the string is not a valid IPv6 address

    # Arguments

    * ip - an ipv6 string. See RFC2460 for spec.

    # Returns

    * an `ip_addr` of the `ipv6` variant
    "]
    fn parse_addr(ip: str) -> ip_addr {
        alt try_parse_addr(ip) {
          // FIXME: more copies brought to light to due the implicit
          // copy compiler warning.. what can be done? out pointers,
          // ala c#?
          result::ok(addr) { copy(addr) }
          result::err(err_data) {
            fail err_data.err_msg
          }
        }
    }
    fn try_parse_addr(ip: str) -> result::result<ip_addr,parse_addr_err> {
        unsafe {
            // need to figure out how to establish a parse failure..
            let new_addr = uv_ip6_addr(ip, 22);
            let reparsed_name = uv_ip6_name(&new_addr);
            log(debug, #fmt("v6::try_parse_addr ip: '%s' reparsed '%s'",
                            ip, reparsed_name));
            // '::' appears to be uv_ip6_name() returns for bogus
            // parses..
            if  ip != "::" && reparsed_name == "::" {
                result::err({err_msg:#fmt("failed to parse '%s'",
                                           ip)})
            }
            else {
                result::ok(ipv6(new_addr))
            }
        }
    }
}

//#[cfg(test)]
mod test {
    #[test]
    fn test_ipv4_parse_and_format_ip() {
        let localhost_str = "127.0.0.1";
        assert (format_addr(v4::parse_addr(localhost_str))
                == localhost_str)
    }
    #[test]
    fn test_ipv6_parse_and_format_ip() {
        let localhost_str = "::1";
        let format_result = format_addr(v6::parse_addr(localhost_str));
        log(debug, #fmt("results: expected: '%s' actual: '%s'",
            localhost_str, format_result));
        assert format_result == localhost_str;
    }
    #[test]
    fn test_ipv4_bad_parse() {
        alt v4::try_parse_addr("b4df00d") {
          result::err(err_info) {
            log(debug, #fmt("got error as expected %?", err_info));
            assert true;
          }
          result::ok(addr) {
            fail #fmt("Expected failure, but got addr %?", addr);
          }
        }
    }
    #[test]
    fn test_ipv6_bad_parse() {
        alt v6::try_parse_addr("::,~2234k;") {
          result::err(err_info) {
            log(debug, #fmt("got error as expected %?", err_info));
            assert true;
          }
          result::ok(addr) {
            fail #fmt("Expected failure, but got addr %?", addr);
          }
        }
    }
}<|endoftext|>


<commit_before>use regex::Regex;
use yaml_rust::{yaml, Yaml, YamlLoader};

pub fn parse(raw: &str) -> Yaml {
    let (front, _) = split(raw);
    if front.is_none() {
        return Yaml::Hash(yaml::Hash::new());
    }

    match match YamlLoader::load_from_str(&front.unwrap()) {
        Ok(mut doc) => match doc.pop() {
            None => None,
            Some(doc) => match doc {
                Yaml::Hash(_) => Some(doc),
                _ => None
            }
        },
        Err(_) => None
    } {
        Some(doc) => doc,
        None => Yaml::Hash(yaml::Hash::new())
    }
}

pub fn strip(raw: &str) -> String {
    let (_, content) = split(raw);
    content
}

fn split(raw: &str) -> (Option<String>, String) {
    lazy_static! {
        static ref MATTER: Regex = Regex::new(r"(?xm)^---$").unwrap();
    }

    let matters: Vec<&str> = MATTER.splitn(raw, 3).collect();
    match matters.len() {
        3 => match matters[0] {
            "" => (Some(String::from(matters[1])), String::from(matters[2])),
            _ => (None, String::from(matters.join("---")))
        },
        _ => (None, String::from(raw))
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use yaml_rust::{yaml, Yaml};

    #[test]
    fn parse_empty_input() {
        assert_eq!(parse(""), Yaml::Hash(yaml::Hash::new()));
    }

    #[test]
    fn parse_no_front_matter() {
        assert_eq!(parse("Foo\nBar\nbazzzz"), Yaml::Hash(yaml::Hash::new()));
    }

    #[test]
    fn parse_dashes_after_the_top() {
        assert_eq!(parse("Blah blah\n---\n---"), Yaml::Hash(yaml::Hash::new()));
    }

    #[test]
    fn parse_matter_after_spaces() {
        assert_eq!(parse("  ---\npage: true\n---"), Yaml::Hash(yaml::Hash::new()));
    }

    #[test]
    fn parse_matter_after_newline() {
        assert_eq!(parse("\n---\npage: true\n---"), Yaml::Hash(yaml::Hash::new()));
    }

    #[test]
    fn parse_empty_front_matter() {
        assert_eq!(parse("---\n---"), Yaml::Hash(yaml::Hash::new()));
    }

    #[test]
    fn parse_array_front_matter() {
        assert_eq!(parse("---\n- foo\n- bar\n---"), Yaml::Hash(yaml::Hash::new()));
    }

    #[test]
    fn parse_string_front_matter() {
        assert_eq!(parse("---\nstring\n---"), Yaml::Hash(yaml::Hash::new()));
    }

    #[test]
    fn parse_hash_front_matter() {
        let mut matter = yaml::Hash::new();
        matter.insert(
            Yaml::String(String::from("page")),
            Yaml::Boolean(true)
        );
        matter.insert(
            Yaml::String(String::from("tags")),
            Yaml::Array(vec![
                Yaml::String(String::from("post"))
            ])
        );

        assert_eq!(
            parse("---\npage: true\ntags:\n  - post\n---"),
            Yaml::Hash(matter)
        );
    }

    #[test]
    fn strip_empty_input() {
        assert_eq!(strip(""), String::from(""));
    }

    #[test]
    fn strip_no_front_matter() {
        assert_eq!(strip("Foo\nBar\nbazzzz"),
            String::from("Foo\nBar\nbazzzz"));
    }

    #[test]
    fn strip_dashes_after_the_top() {
        assert_eq!(strip("Blah blah\n---\n---"),
            String::from("Blah blah\n---\n---"));
    }

    #[test]
    fn strip_matter_after_spaces() {
        assert_eq!(strip("  ---\npage: true\n---"),
            String::from("  ---\npage: true\n---"));
    }

    #[test]
    fn strip_matter_after_newline() {
        assert_eq!(strip("\n---\npage: true\n---"),
            String::from("\n---\npage: true\n---"));
    }

    #[test]
    fn strip_empty_front_matter() {
        assert_eq!(strip("---\n---\nipsum"),
            String::from("\nipsum"));
    }

    #[test]
    fn strip_front_matter() {
        assert_eq!(strip("---\npage: true\n---\nlorem"),
            String::from("\nlorem"));
    }
}
<commit_msg>Add tests for vim modeline<commit_after>use regex::Regex;
use yaml_rust::{yaml, Yaml, YamlLoader};

pub fn parse(raw: &str) -> Yaml {
    let (front, _) = split(raw);
    if front.is_none() {
        return Yaml::Hash(yaml::Hash::new());
    }

    match match YamlLoader::load_from_str(&front.unwrap()) {
        Ok(mut doc) => match doc.pop() {
            None => None,
            Some(doc) => match doc {
                Yaml::Hash(_) => Some(doc),
                _ => None
            }
        },
        Err(_) => None
    } {
        Some(doc) => doc,
        None => Yaml::Hash(yaml::Hash::new())
    }
}

pub fn strip(raw: &str) -> String {
    let (_, content) = split(raw);
    content
}

fn split(raw: &str) -> (Option<String>, String) {
    lazy_static! {
        static ref MATTER: Regex = Regex::new(r"(?xm)^---$").unwrap();
    }

    let matters: Vec<&str> = MATTER.splitn(raw, 3).collect();
    match matters.len() {
        3 => match matters[0] {
            "" => (Some(String::from(matters[1])), String::from(matters[2])),
            _ => (None, String::from(matters.join("---")))
        },
        _ => (None, String::from(raw))
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use yaml_rust::{yaml, Yaml};

    #[test]
    fn parse_empty_input() {
        assert_eq!(parse(""), Yaml::Hash(yaml::Hash::new()));
    }

    #[test]
    fn parse_no_front_matter() {
        assert_eq!(parse("Foo\nBar\nbazzzz"), Yaml::Hash(yaml::Hash::new()));
    }

    #[test]
    fn parse_dashes_after_the_top() {
        assert_eq!(parse("Blah blah\n---\n---"), Yaml::Hash(yaml::Hash::new()));
    }

    #[test]
    fn parse_matter_after_spaces() {
        assert_eq!(parse("  ---\npage: true\n---"), Yaml::Hash(yaml::Hash::new()));
    }

    #[test]
    fn parse_matter_after_newline() {
        assert_eq!(parse("\n---\npage: true\n---"), Yaml::Hash(yaml::Hash::new()));
    }

    #[test]
    fn parse_empty_front_matter() {
        assert_eq!(parse("---\n---"), Yaml::Hash(yaml::Hash::new()));
    }

    #[test]
    fn parse_empty_front_matter_with_vim_modeline() {
        assert_eq!(parse("---\n# vim: tw=80\n---"), Yaml::Hash(yaml::Hash::new()));
    }

    #[test]
    fn parse_array_front_matter() {
        assert_eq!(parse("---\n- foo\n- bar\n---"), Yaml::Hash(yaml::Hash::new()));
    }

    #[test]
    fn parse_string_front_matter() {
        assert_eq!(parse("---\nstring\n---"), Yaml::Hash(yaml::Hash::new()));
    }

    #[test]
    fn parse_hash_front_matter() {
        let mut matter = yaml::Hash::new();
        matter.insert(
            Yaml::String(String::from("page")),
            Yaml::Boolean(true)
        );
        matter.insert(
            Yaml::String(String::from("tags")),
            Yaml::Array(vec![
                Yaml::String(String::from("post"))
            ])
        );

        assert_eq!(
            parse("---\npage: true\ntags:\n  - post\n---"),
            Yaml::Hash(matter)
        );
    }

    #[test]
    fn parse_hash_front_matter_with_vim_modeline() {
        let mut matter = yaml::Hash::new();
        matter.insert(
            Yaml::String(String::from("page")),
            Yaml::Boolean(true)
        );
        matter.insert(
            Yaml::String(String::from("tags")),
            Yaml::Array(vec![
                Yaml::String(String::from("post"))
            ])
        );

        assert_eq!(
            parse("---\n# vim: tw=80\npage: true\ntags:\n  - post\n---"),
            Yaml::Hash(matter)
        );
    }

    #[test]
    fn strip_empty_input() {
        assert_eq!(strip(""), String::from(""));
    }

    #[test]
    fn strip_no_front_matter() {
        assert_eq!(strip("Foo\nBar\nbazzzz"),
            String::from("Foo\nBar\nbazzzz"));
    }

    #[test]
    fn strip_dashes_after_the_top() {
        assert_eq!(strip("Blah blah\n---\n---"),
            String::from("Blah blah\n---\n---"));
    }

    #[test]
    fn strip_matter_after_spaces() {
        assert_eq!(strip("  ---\npage: true\n---"),
            String::from("  ---\npage: true\n---"));
    }

    #[test]
    fn strip_matter_after_newline() {
        assert_eq!(strip("\n---\npage: true\n---"),
            String::from("\n---\npage: true\n---"));
    }

    #[test]
    fn strip_empty_front_matter() {
        assert_eq!(strip("---\n---\nipsum"),
            String::from("\nipsum"));
    }

    #[test]
    fn strip_front_matter() {
        assert_eq!(strip("---\npage: true\n---\nlorem"),
            String::from("\nlorem"));
    }
}
<|endoftext|>