<commit_before>use gtk;

use std::collections::BTreeMap;

use std::ops::Deref;

#[derive(Clone, Debug)]
pub struct Chapter {
    pub title: String,
    pub iter: gtk::TreeIter,
}

impl PartialEq for Chapter {
    fn eq(&self, other: &Chapter) -> bool {
        self.title == other.title
    }
}

#[derive(Debug, PartialEq)]
pub struct SuccessiveChapters {
    pub prev: Option<Chapter>,
    pub next: Option<Chapter>,
}

pub struct ChaptersBoundaries(BTreeMap<u64, SuccessiveChapters>);

impl ChaptersBoundaries {
    pub fn new() -> Self {
        ChaptersBoundaries(BTreeMap::new())
    }

    pub fn clear(&mut self) {
        self.0.clear();
    }

    pub fn add_chapter(&mut self, start: u64, end: u64, title: &str, iter: &gtk::TreeIter) {
        debug!("add_chapter {}, {}, {}", start, end, title);

        // the chapter to add can share at most one boundary with a previous chapter
        let (start_exists, next_chapter) = match self.0.get_mut(&start) {
            Some(chapters_at_start) => {
                // a boundary already exists at start
                let next_chapter = chapters_at_start.next.take();
                chapters_at_start.next = Some(Chapter {
                    title: title.to_owned(),
                    iter: iter.clone(),
                });
                (true, next_chapter)
            }
            None => (false, None),
        };

        if start_exists {
            self.0.insert(
                end,
                SuccessiveChapters {
                    prev: Some(Chapter {
                        title: title.to_owned(),
                        iter: iter.clone(),
                    }),
                    next: next_chapter,
                },
            );
        } else {
            // no chapter at start
            let (end_exists, prev_chapter) = match self.0.get_mut(&end) {
                Some(chapters_at_end) => {
                    // a boundary already exists at end
                    let prev_chapter = chapters_at_end.prev.take();
                    chapters_at_end.prev = Some(Chapter {
                        title: title.to_owned(),
                        iter: iter.clone(),
                    });
                    (true, prev_chapter)
                }
                None => (false, None),
            };

            self.0.insert(
                start,
                SuccessiveChapters {
                    prev: prev_chapter,
                    next: Some(Chapter {
                        title: title.to_owned(),
                        iter: iter.clone(),
                    }),
                },
            );

            if !end_exists {
                self.0.insert(
                    end,
                    SuccessiveChapters {
                        prev: Some(Chapter {
                            title: title.to_owned(),
                            iter: iter.clone(),
                        }),
                        next: None,
                    },
                );
            }
        }
    }

    pub fn remove_chapter(&mut self, start: u64, end: u64) {
        debug!("remove_chapter {}, {}", start, end);

        let prev_chapter = self.0.get_mut(&start).unwrap().prev.take();
        self.0.remove(&start);

        let can_remove_end = {
            let chapters_at_end = self.0.get_mut(&end).unwrap();
            if prev_chapter.is_none() && chapters_at_end.next.is_none() {
                true
            } else {
                chapters_at_end.prev = prev_chapter;
                false
            }
        };
        if can_remove_end {
            self.0.remove(&end);
        }
    }

    pub fn rename_chapter(&mut self, start: u64, end: u64, new_title: &str) {
        debug!("rename_chapter {}, {}, {}", start, end, new_title);

        self.0.get_mut(&start).unwrap().next.as_mut().unwrap().title = new_title.to_owned();
        self.0.get_mut(&end).unwrap().prev.as_mut().unwrap().title = new_title.to_owned();
    }

    pub fn move_boundary(&mut self, boundary: u64, to_position: u64) {
        let chapters = self.0.remove(&boundary).unwrap();
        if self.0.insert(to_position, chapters).is_some() {
            panic!(
                "ChaptersBoundaries::move_boundary attempt to replace entry at {}",
                to_position
            );
        }
    }
}

impl Deref for ChaptersBoundaries {
    type Target = BTreeMap<u64, SuccessiveChapters>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

// Ignore these tests for now as it fails for Travis-CI's linux host
#[cfg(test)]
mod tests {
    use glib;
    use gtk;
    use gtk::TreeStoreExt;
    use super::*;

    fn new_chapter(store: &gtk::TreeStore, title: &str) -> Chapter {
        Chapter {
            title: title.to_owned(),
            iter: store.append(None),
        }
    }

    #[test]
    fn chapters_boundaries() {
        if gtk::init().is_err() {
            panic!("tests::chapters_boundaries failed to initialize GTK");
        }
        // fake store
        let store = gtk::TreeStore::new(&[glib::Type::Bool]);

        let mut boundaries = ChaptersBoundaries::new();

        assert!(boundaries.is_empty());

        // Add incrementally

        let chapter_1 = new_chapter(&store, "1");
        boundaries.add_chapter(0, 1, &chapter_1.title, &chapter_1.iter);
        assert_eq!(2, boundaries.len());
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: None,
                next: Some(chapter_1.clone()),
            }),
            boundaries.get(&0),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_1.clone()),
                next: None,
            }),
            boundaries.get(&1),
        );

        let chapter_2 = new_chapter(&store, "2");
        boundaries.add_chapter(1, 2, &chapter_2.title, &chapter_2.iter);
        assert_eq!(3, boundaries.len());
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: None,
                next: Some(chapter_1.clone()),
            }),
            boundaries.get(&0),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_1.clone()),
                next: Some(chapter_2.clone()),
            }),
            boundaries.get(&1),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_2.clone()),
                next: None,
            }),
            boundaries.get(&2),
        );

        let chapter_3 = new_chapter(&store, "3");
        boundaries.add_chapter(2, 4, &chapter_3.title, &chapter_3.iter);
        assert_eq!(4, boundaries.len());
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: None,
                next: Some(chapter_1.clone()),
            }),
            boundaries.get(&0),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_1.clone()),
                next: Some(chapter_2.clone()),
            }),
            boundaries.get(&1),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_2.clone()),
                next: Some(chapter_3.clone()),
            }),
            boundaries.get(&2),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_3.clone()),
                next: None,
            }),
            boundaries.get(&4),
        );

        // Rename
        let chapter_r2 = new_chapter(&store, "r2");
        boundaries.rename_chapter(1, 2, &chapter_r2.title);
        assert_eq!(4, boundaries.len());
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: None,
                next: Some(chapter_1.clone()),
            }),
            boundaries.get(&0),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_1.clone()),
                next: Some(chapter_r2.clone()),
            }),
            boundaries.get(&1),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_r2.clone()),
                next: Some(chapter_3.clone()),
            }),
            boundaries.get(&2),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_3.clone()),
                next: None,
            }),
            boundaries.get(&4),
        );

        let chapter_r1 = new_chapter(&store, "r1");
        boundaries.rename_chapter(0, 1, &chapter_r1.title);
        assert_eq!(4, boundaries.len());
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: None,
                next: Some(chapter_r1.clone()),
            }),
            boundaries.get(&0),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_r1.clone()),
                next: Some(chapter_r2.clone()),
            }),
            boundaries.get(&1),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_r2.clone()),
                next: Some(chapter_3.clone()),
            }),
            boundaries.get(&2),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_3.clone()),
                next: None,
            }),
            boundaries.get(&4),
        );

        let chapter_r3 = new_chapter(&store, "r3");
        boundaries.rename_chapter(2, 4, &chapter_r3.title);
        assert_eq!(4, boundaries.len());
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: None,
                next: Some(chapter_r1.clone()),
            }),
            boundaries.get(&0),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_r1.clone()),
                next: Some(chapter_r2.clone()),
            }),
            boundaries.get(&1),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_r2.clone()),
                next: Some(chapter_r3.clone()),
            }),
            boundaries.get(&2),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_r3.clone()),
                next: None,
            }),
            boundaries.get(&4),
        );

        // Remove in the middle
        boundaries.remove_chapter(1, 2);
        assert_eq!(3, boundaries.len());
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: None,
                next: Some(chapter_r1.clone()),
            }),
            boundaries.get(&0),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_r1.clone()),
                next: Some(chapter_r3.clone()),
            }),
            boundaries.get(&2),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_r3.clone()),
                next: None,
            }),
            boundaries.get(&4),
        );

        // Add in the middle
        let chapter_n2 = new_chapter(&store, "n2");
        boundaries.add_chapter(1, 2, &chapter_n2.title, &chapter_n2.iter);
        assert_eq!(4, boundaries.len());
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: None,
                next: Some(chapter_r1.clone()),
            }),
            boundaries.get(&0),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_r1.clone()),
                next: Some(chapter_n2.clone()),
            }),
            boundaries.get(&1),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_n2.clone()),
                next: Some(chapter_r3.clone()),
            }),
            boundaries.get(&2),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_r3.clone()),
                next: None,
            }),
            boundaries.get(&4),
        );

        // Remove first
        boundaries.remove_chapter(0, 1);
        assert_eq!(3, boundaries.len());
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: None,
                next: Some(chapter_n2.clone()),
            }),
            boundaries.get(&1),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_n2.clone()),
                next: Some(chapter_r3.clone()),
            }),
            boundaries.get(&2),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_r3.clone()),
                next: None,
            }),
            boundaries.get(&4),
        );

        // Add first
        let chapter_n1 = new_chapter(&store, "n1");
        boundaries.add_chapter(0, 1, &chapter_n1.title, &chapter_n1.iter);
        assert_eq!(4, boundaries.len());
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: None,
                next: Some(chapter_n1.clone()),
            }),
            boundaries.get(&0),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_n1.clone()),
                next: Some(chapter_n2.clone()),
            }),
            boundaries.get(&1),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_n2.clone()),
                next: Some(chapter_r3.clone()),
            }),
            boundaries.get(&2),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_r3.clone()),
                next: None,
            }),
            boundaries.get(&4),
        );

        // Remove last
        boundaries.remove_chapter(2, 4);
        assert_eq!(3, boundaries.len());
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: None,
                next: Some(chapter_n1.clone()),
            }),
            boundaries.get(&0),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_n1.clone()),
                next: Some(chapter_n2.clone()),
            }),
            boundaries.get(&1),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_n2.clone()),
                next: None,
            }),
            boundaries.get(&4),
        );
    }
}
<commit_msg>ChaptersBoundaries: ignore test if GTK init fails<commit_after>use gtk;

use std::collections::BTreeMap;

use std::ops::Deref;

#[derive(Clone, Debug)]
pub struct Chapter {
    pub title: String,
    pub iter: gtk::TreeIter,
}

impl PartialEq for Chapter {
    fn eq(&self, other: &Chapter) -> bool {
        self.title == other.title
    }
}

#[derive(Debug, PartialEq)]
pub struct SuccessiveChapters {
    pub prev: Option<Chapter>,
    pub next: Option<Chapter>,
}

pub struct ChaptersBoundaries(BTreeMap<u64, SuccessiveChapters>);

impl ChaptersBoundaries {
    pub fn new() -> Self {
        ChaptersBoundaries(BTreeMap::new())
    }

    pub fn clear(&mut self) {
        self.0.clear();
    }

    pub fn add_chapter(&mut self, start: u64, end: u64, title: &str, iter: &gtk::TreeIter) {
        debug!("add_chapter {}, {}, {}", start, end, title);

        // the chapter to add can share at most one boundary with a previous chapter
        let (start_exists, next_chapter) = match self.0.get_mut(&start) {
            Some(chapters_at_start) => {
                // a boundary already exists at start
                let next_chapter = chapters_at_start.next.take();
                chapters_at_start.next = Some(Chapter {
                    title: title.to_owned(),
                    iter: iter.clone(),
                });
                (true, next_chapter)
            }
            None => (false, None),
        };

        if start_exists {
            self.0.insert(
                end,
                SuccessiveChapters {
                    prev: Some(Chapter {
                        title: title.to_owned(),
                        iter: iter.clone(),
                    }),
                    next: next_chapter,
                },
            );
        } else {
            // no chapter at start
            let (end_exists, prev_chapter) = match self.0.get_mut(&end) {
                Some(chapters_at_end) => {
                    // a boundary already exists at end
                    let prev_chapter = chapters_at_end.prev.take();
                    chapters_at_end.prev = Some(Chapter {
                        title: title.to_owned(),
                        iter: iter.clone(),
                    });
                    (true, prev_chapter)
                }
                None => (false, None),
            };

            self.0.insert(
                start,
                SuccessiveChapters {
                    prev: prev_chapter,
                    next: Some(Chapter {
                        title: title.to_owned(),
                        iter: iter.clone(),
                    }),
                },
            );

            if !end_exists {
                self.0.insert(
                    end,
                    SuccessiveChapters {
                        prev: Some(Chapter {
                            title: title.to_owned(),
                            iter: iter.clone(),
                        }),
                        next: None,
                    },
                );
            }
        }
    }

    pub fn remove_chapter(&mut self, start: u64, end: u64) {
        debug!("remove_chapter {}, {}", start, end);

        let prev_chapter = self.0.get_mut(&start).unwrap().prev.take();
        self.0.remove(&start);

        let can_remove_end = {
            let chapters_at_end = self.0.get_mut(&end).unwrap();
            if prev_chapter.is_none() && chapters_at_end.next.is_none() {
                true
            } else {
                chapters_at_end.prev = prev_chapter;
                false
            }
        };
        if can_remove_end {
            self.0.remove(&end);
        }
    }

    pub fn rename_chapter(&mut self, start: u64, end: u64, new_title: &str) {
        debug!("rename_chapter {}, {}, {}", start, end, new_title);

        self.0.get_mut(&start).unwrap().next.as_mut().unwrap().title = new_title.to_owned();
        self.0.get_mut(&end).unwrap().prev.as_mut().unwrap().title = new_title.to_owned();
    }

    pub fn move_boundary(&mut self, boundary: u64, to_position: u64) {
        let chapters = self.0.remove(&boundary).unwrap();
        if self.0.insert(to_position, chapters).is_some() {
            panic!(
                "ChaptersBoundaries::move_boundary attempt to replace entry at {}",
                to_position
            );
        }
    }
}

impl Deref for ChaptersBoundaries {
    type Target = BTreeMap<u64, SuccessiveChapters>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

#[cfg(test)]
mod tests {
    use glib;
    use gtk;
    use gtk::TreeStoreExt;
    use super::*;

    fn new_chapter(store: &gtk::TreeStore, title: &str) -> Chapter {
        Chapter {
            title: title.to_owned(),
            iter: store.append(None),
        }
    }

    #[test]
    fn chapters_boundaries() {
        if gtk::init().is_err() {
            // GTK initialization failure on Travis-CI's linux host
            return;
        }
        // fake store
        let store = gtk::TreeStore::new(&[glib::Type::Bool]);

        let mut boundaries = ChaptersBoundaries::new();

        assert!(boundaries.is_empty());

        // Add incrementally

        let chapter_1 = new_chapter(&store, "1");
        boundaries.add_chapter(0, 1, &chapter_1.title, &chapter_1.iter);
        assert_eq!(2, boundaries.len());
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: None,
                next: Some(chapter_1.clone()),
            }),
            boundaries.get(&0),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_1.clone()),
                next: None,
            }),
            boundaries.get(&1),
        );

        let chapter_2 = new_chapter(&store, "2");
        boundaries.add_chapter(1, 2, &chapter_2.title, &chapter_2.iter);
        assert_eq!(3, boundaries.len());
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: None,
                next: Some(chapter_1.clone()),
            }),
            boundaries.get(&0),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_1.clone()),
                next: Some(chapter_2.clone()),
            }),
            boundaries.get(&1),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_2.clone()),
                next: None,
            }),
            boundaries.get(&2),
        );

        let chapter_3 = new_chapter(&store, "3");
        boundaries.add_chapter(2, 4, &chapter_3.title, &chapter_3.iter);
        assert_eq!(4, boundaries.len());
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: None,
                next: Some(chapter_1.clone()),
            }),
            boundaries.get(&0),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_1.clone()),
                next: Some(chapter_2.clone()),
            }),
            boundaries.get(&1),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_2.clone()),
                next: Some(chapter_3.clone()),
            }),
            boundaries.get(&2),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_3.clone()),
                next: None,
            }),
            boundaries.get(&4),
        );

        // Rename
        let chapter_r2 = new_chapter(&store, "r2");
        boundaries.rename_chapter(1, 2, &chapter_r2.title);
        assert_eq!(4, boundaries.len());
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: None,
                next: Some(chapter_1.clone()),
            }),
            boundaries.get(&0),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_1.clone()),
                next: Some(chapter_r2.clone()),
            }),
            boundaries.get(&1),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_r2.clone()),
                next: Some(chapter_3.clone()),
            }),
            boundaries.get(&2),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_3.clone()),
                next: None,
            }),
            boundaries.get(&4),
        );

        let chapter_r1 = new_chapter(&store, "r1");
        boundaries.rename_chapter(0, 1, &chapter_r1.title);
        assert_eq!(4, boundaries.len());
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: None,
                next: Some(chapter_r1.clone()),
            }),
            boundaries.get(&0),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_r1.clone()),
                next: Some(chapter_r2.clone()),
            }),
            boundaries.get(&1),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_r2.clone()),
                next: Some(chapter_3.clone()),
            }),
            boundaries.get(&2),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_3.clone()),
                next: None,
            }),
            boundaries.get(&4),
        );

        let chapter_r3 = new_chapter(&store, "r3");
        boundaries.rename_chapter(2, 4, &chapter_r3.title);
        assert_eq!(4, boundaries.len());
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: None,
                next: Some(chapter_r1.clone()),
            }),
            boundaries.get(&0),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_r1.clone()),
                next: Some(chapter_r2.clone()),
            }),
            boundaries.get(&1),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_r2.clone()),
                next: Some(chapter_r3.clone()),
            }),
            boundaries.get(&2),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_r3.clone()),
                next: None,
            }),
            boundaries.get(&4),
        );

        // Remove in the middle
        boundaries.remove_chapter(1, 2);
        assert_eq!(3, boundaries.len());
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: None,
                next: Some(chapter_r1.clone()),
            }),
            boundaries.get(&0),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_r1.clone()),
                next: Some(chapter_r3.clone()),
            }),
            boundaries.get(&2),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_r3.clone()),
                next: None,
            }),
            boundaries.get(&4),
        );

        // Add in the middle
        let chapter_n2 = new_chapter(&store, "n2");
        boundaries.add_chapter(1, 2, &chapter_n2.title, &chapter_n2.iter);
        assert_eq!(4, boundaries.len());
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: None,
                next: Some(chapter_r1.clone()),
            }),
            boundaries.get(&0),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_r1.clone()),
                next: Some(chapter_n2.clone()),
            }),
            boundaries.get(&1),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_n2.clone()),
                next: Some(chapter_r3.clone()),
            }),
            boundaries.get(&2),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_r3.clone()),
                next: None,
            }),
            boundaries.get(&4),
        );

        // Remove first
        boundaries.remove_chapter(0, 1);
        assert_eq!(3, boundaries.len());
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: None,
                next: Some(chapter_n2.clone()),
            }),
            boundaries.get(&1),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_n2.clone()),
                next: Some(chapter_r3.clone()),
            }),
            boundaries.get(&2),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_r3.clone()),
                next: None,
            }),
            boundaries.get(&4),
        );

        // Add first
        let chapter_n1 = new_chapter(&store, "n1");
        boundaries.add_chapter(0, 1, &chapter_n1.title, &chapter_n1.iter);
        assert_eq!(4, boundaries.len());
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: None,
                next: Some(chapter_n1.clone()),
            }),
            boundaries.get(&0),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_n1.clone()),
                next: Some(chapter_n2.clone()),
            }),
            boundaries.get(&1),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_n2.clone()),
                next: Some(chapter_r3.clone()),
            }),
            boundaries.get(&2),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_r3.clone()),
                next: None,
            }),
            boundaries.get(&4),
        );

        // Remove last
        boundaries.remove_chapter(2, 4);
        assert_eq!(3, boundaries.len());
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: None,
                next: Some(chapter_n1.clone()),
            }),
            boundaries.get(&0),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_n1.clone()),
                next: Some(chapter_n2.clone()),
            }),
            boundaries.get(&1),
        );
        assert_eq!(
            Some(&SuccessiveChapters{
                prev: Some(chapter_n2.clone()),
                next: None,
            }),
            boundaries.get(&4),
        );
    }
}
<|endoftext|>


<commit_before>/*!
Functionality for declaring Objective-C classes.

Classes can be declared using the `ClassDecl` struct. Instance variables and
methods can then be added before the class is ultimately registered.

# Example

The following example demonstrates declaring a class named `MyNumber` that has
one ivar, a `u32` named `_number` and a `number` method that returns it:

``` no_run
# #[macro_use] extern crate objc;
# use objc::declare::ClassDecl;
# use objc::runtime::{Class, Object, Sel};
# fn main() {
let superclass = Class::get("NSObject").unwrap();
let mut decl = ClassDecl::new("MyNumber", Some(superclass)).unwrap();

// Add an instance variable
decl.add_ivar::<u32>("_number");

// Add an ObjC method for getting the number
extern fn my_number_get(this: &Object, _cmd: Sel) -> u32 {
    unsafe { *this.get_ivar("_number") }
}
unsafe {
    decl.add_method(sel!(number),
        my_number_get as extern fn(&Object, Sel) -> u32);
}

decl.register();
# }
```
*/

use std::error::Error;
use std::ffi::CString;
use std::fmt;
use std::mem;
use std::ptr;

use runtime::{Class, Imp, NO, Object, Sel, self};
use {Encode, EncodeArguments, Message};
use to_c_str;

/// An error returned from `MethodImplementation::imp_for` to indicate that a
/// selector and function accept unequal numbers of arguments.
#[derive(Clone, PartialEq, Debug)]
pub struct UnequalArgsError {
    sel_args: usize,
    fn_args: usize,
}

impl fmt::Display for UnequalArgsError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Selector accepts {} arguments, but function accepts {}",
            self.sel_args, self.fn_args)
    }
}

impl Error for UnequalArgsError {
    fn description(&self) -> &str {
        "Selector and function accept unequal numbers of arguments"
    }
}

/// Types that can be used as the implementation of an Objective-C method.
pub trait MethodImplementation {
    /// The callee type of the method.
    type Callee: Message;
    /// The return type of the method.
    type Ret: Encode;

    type Args: EncodeArguments;

    /// Returns self as an `Imp` of a method for the given selector.
    ///
    /// Returns an error if self and the selector do not accept the same number
    /// of arguments.
    fn imp_for(self, sel: Sel) -> Result<Imp, UnequalArgsError>;
}

macro_rules! method_decl_impl {
    (-$s:ident, $r:ident, $f:ty, $($t:ident),*) => (
        impl<$s, $r $(, $t)*> MethodImplementation for $f
                where $s: Message, $r: Encode $(, $t: Encode)* {
            type Callee = $s;
            type Ret = $r;
            type Args = ($($t,)*);

            fn imp_for(self, sel: Sel) -> Result<Imp, UnequalArgsError> {
                // Add 2 to the arguments for self and _cmd
                let fn_args = 2 + count_idents!($($t),*);
                let sel_args = 2 + sel.name().chars().filter(|&c| c == ':').count();
                if sel_args == fn_args {
                    unsafe { Ok(mem::transmute(self)) }
                } else {
                    Err(UnequalArgsError { sel_args: sel_args, fn_args: fn_args })
                }
            }
        }
    );
    ($($t:ident),*) => (
        method_decl_impl!(-T, R, extern fn(&T, Sel $(, $t)*) -> R, $($t),*);
        method_decl_impl!(-T, R, extern fn(&mut T, Sel $(, $t)*) -> R, $($t),*);
    );
}

method_decl_impl!();
method_decl_impl!(A);
method_decl_impl!(A, B);
method_decl_impl!(A, B, C);
method_decl_impl!(A, B, C, D);
method_decl_impl!(A, B, C, D, E);
method_decl_impl!(A, B, C, D, E, F);
method_decl_impl!(A, B, C, D, E, F, G);
method_decl_impl!(A, B, C, D, E, F, G, H);
method_decl_impl!(A, B, C, D, E, F, G, H, I);
method_decl_impl!(A, B, C, D, E, F, G, H, I, J);
method_decl_impl!(A, B, C, D, E, F, G, H, I, J, K);
method_decl_impl!(A, B, C, D, E, F, G, H, I, J, K, L);

fn method_type_encoding<F>() -> CString where F: MethodImplementation {
    let mut types = F::Ret::encode().as_str().to_owned();
    types.extend(F::Args::encodings().as_ref().iter().map(|e| e.as_str()));
    CString::new(types).unwrap()
}

fn log2_align_of<T>() -> u8 {
    let align = mem::align_of::<T>();
    // Alignments are required to be powers of 2
    debug_assert!(align.count_ones() == 1);
    // log2 of a power of 2 is the number of trailing zeros
    align.trailing_zeros() as u8
}

/// A type for declaring a new class and adding new methods and ivars to it
/// before registering it.
pub struct ClassDecl {
    cls: *mut Class,
}

impl ClassDecl {
    /// Constructs a `ClassDecl` with the given superclass and name.
    /// Returns `None` if the class couldn't be allocated.
    pub fn new(name: &str, superclass: Option<&Class>) -> Option<ClassDecl> {
        let name = CString::new(name).unwrap();
        let super_ptr = superclass.map_or(ptr::null(), |c| c);
        let cls = unsafe {
            runtime::objc_allocateClassPair(super_ptr, to_c_str(&name), 0)
        };
        if cls.is_null() {
            None
        } else {
            Some(ClassDecl { cls: cls })
        }
    }

    /// Adds a method with the given name and implementation to self.
    /// Panics if the method wasn't sucessfully added
    /// or if the selector and function take different numbers of arguments.
    /// Unsafe because the caller must ensure that the types match those that
    /// are expected when the method is invoked from Objective-C.
    pub unsafe fn add_method<F>(&mut self, sel: Sel, func: F)
            where F: MethodImplementation<Callee=Object> {
        let types = method_type_encoding::<F>();
        let imp = match func.imp_for(sel) {
            Ok(imp) => imp,
            Err(err) => panic!("{}", err),
        };

        let success = runtime::class_addMethod(self.cls, sel, imp,
            to_c_str(&types));
        assert!(success != NO, "Failed to add method {:?}", sel);
    }

    /// Adds a class method with the given name and implementation to self.
    /// Panics if the method wasn't sucessfully added
    /// or if the selector and function take different numbers of arguments.
    /// Unsafe because the caller must ensure that the types match those that
    /// are expected when the method is invoked from Objective-C.
    pub unsafe fn add_class_method<F>(&mut self, sel: Sel, func: F)
            where F: MethodImplementation<Callee=Class> {
        let types = method_type_encoding::<F>();
        let imp = match func.imp_for(sel) {
            Ok(imp) => imp,
            Err(err) => panic!("{}", err),
        };

        let cls_obj = self.cls as *const Object;
        let metaclass = runtime::object_getClass(cls_obj) as *mut Class;
        let success = runtime::class_addMethod(metaclass, sel, imp,
            to_c_str(&types));
        assert!(success != NO, "Failed to add class method {:?}", sel);
    }

    /// Adds an ivar with type `T` and the provided name to self.
    /// Panics if the ivar wasn't successfully added.
    pub fn add_ivar<T>(&mut self, name: &str) where T: Encode {
        let c_name = CString::new(name).unwrap();
        let encoding = CString::new(T::encode().as_str()).unwrap();
        let size = mem::size_of::<T>();
        let align = log2_align_of::<T>();
        let success = unsafe {
            runtime::class_addIvar(self.cls, to_c_str(&c_name), size, align,
                to_c_str(&encoding))
        };
        assert!(success != NO, "Failed to add ivar {}", name);
    }

    /// Registers self, consuming it and returning a reference to the
    /// newly registered `Class`.
    pub fn register(self) -> &'static Class {
        unsafe {
            let cls = self.cls;
            runtime::objc_registerClassPair(cls);
            // Forget self otherwise the class will be disposed in drop
            mem::forget(self);
            &*cls
        }
    }
}

impl Drop for ClassDecl {
    fn drop(&mut self) {
        unsafe {
            runtime::objc_disposeClassPair(self.cls);
        }
    }
}

#[cfg(test)]
mod tests {
    use runtime::{Object, Sel};
    use test_utils;
    use super::MethodImplementation;

    #[test]
    fn test_custom_class() {
        // Registering the custom class is in test_utils
        let obj = test_utils::custom_object();
        unsafe {
            let _: () = msg_send![obj, setFoo:13u32];
            let result: u32 = msg_send![obj, foo];
            assert!(result == 13);
        }
    }

    #[test]
    fn test_class_method() {
        let cls = test_utils::custom_class();
        unsafe {
            let result: u32 = msg_send![cls, classFoo];
            assert!(result == 7);
        }
    }

    #[test]
    fn test_mismatched_args() {
        extern fn wrong_num_args_method(_obj: &Object, _cmd: Sel, _a: i32) { }

        let sel = sel!(doSomethingWithFoo:bar:);
        let f: extern fn(&Object, Sel, i32) = wrong_num_args_method;
        let imp = f.imp_for(sel);
        assert!(imp.is_err());
    }
}
<commit_msg>Made imp_for a provided method on MethodImplementation.<commit_after>/*!
Functionality for declaring Objective-C classes.

Classes can be declared using the `ClassDecl` struct. Instance variables and
methods can then be added before the class is ultimately registered.

# Example

The following example demonstrates declaring a class named `MyNumber` that has
one ivar, a `u32` named `_number` and a `number` method that returns it:

``` no_run
# #[macro_use] extern crate objc;
# use objc::declare::ClassDecl;
# use objc::runtime::{Class, Object, Sel};
# fn main() {
let superclass = Class::get("NSObject").unwrap();
let mut decl = ClassDecl::new("MyNumber", Some(superclass)).unwrap();

// Add an instance variable
decl.add_ivar::<u32>("_number");

// Add an ObjC method for getting the number
extern fn my_number_get(this: &Object, _cmd: Sel) -> u32 {
    unsafe { *this.get_ivar("_number") }
}
unsafe {
    decl.add_method(sel!(number),
        my_number_get as extern fn(&Object, Sel) -> u32);
}

decl.register();
# }
```
*/

use std::error::Error;
use std::ffi::CString;
use std::fmt;
use std::mem;
use std::ptr;

use runtime::{Class, Imp, NO, Object, Sel, self};
use {Encode, EncodeArguments, Message};
use to_c_str;

/// An error returned from `MethodImplementation::imp_for` to indicate that a
/// selector and function accept unequal numbers of arguments.
#[derive(Clone, PartialEq, Debug)]
pub struct UnequalArgsError {
    sel_args: usize,
    fn_args: usize,
}

impl fmt::Display for UnequalArgsError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Selector accepts {} arguments, but function accepts {}",
            self.sel_args, self.fn_args)
    }
}

impl Error for UnequalArgsError {
    fn description(&self) -> &str {
        "Selector and function accept unequal numbers of arguments"
    }
}

/// Types that can be used as the implementation of an Objective-C method.
pub trait MethodImplementation {
    /// The callee type of the method.
    type Callee: Message;
    /// The return type of the method.
    type Ret: Encode;

    type Args: EncodeArguments;

    /// Returns self as an `Imp` of a method.
    fn imp(self) -> Imp;

    /// Returns self as an `Imp` of a method for the given selector.
    ///
    /// Returns an error if self and the selector do not accept the same number
    /// of arguments.
    fn imp_for(self, sel: Sel) -> Result<Imp, UnequalArgsError>
            where Self: Sized {
        let fn_args = Self::Args::encodings().as_ref().len();
        let sel_args = count_args(sel);
        if sel_args == fn_args {
            Ok(self.imp())
        } else {
            Err(UnequalArgsError { sel_args: sel_args, fn_args: fn_args })
        }
    }
}

macro_rules! method_decl_impl {
    (-$s:ident, $r:ident, $f:ty, $($t:ident),*) => (
        impl<$s, $r $(, $t)*> MethodImplementation for $f
                where $s: Message, $r: Encode $(, $t: Encode)* {
            type Callee = $s;
            type Ret = $r;
            type Args = ($($t,)*);

            fn imp(self) -> Imp {
                unsafe { mem::transmute(self) }
            }
        }
    );
    ($($t:ident),*) => (
        method_decl_impl!(-T, R, extern fn(&T, Sel $(, $t)*) -> R, $($t),*);
        method_decl_impl!(-T, R, extern fn(&mut T, Sel $(, $t)*) -> R, $($t),*);
    );
}

method_decl_impl!();
method_decl_impl!(A);
method_decl_impl!(A, B);
method_decl_impl!(A, B, C);
method_decl_impl!(A, B, C, D);
method_decl_impl!(A, B, C, D, E);
method_decl_impl!(A, B, C, D, E, F);
method_decl_impl!(A, B, C, D, E, F, G);
method_decl_impl!(A, B, C, D, E, F, G, H);
method_decl_impl!(A, B, C, D, E, F, G, H, I);
method_decl_impl!(A, B, C, D, E, F, G, H, I, J);
method_decl_impl!(A, B, C, D, E, F, G, H, I, J, K);
method_decl_impl!(A, B, C, D, E, F, G, H, I, J, K, L);

fn count_args(sel: Sel) -> usize {
    // Add 2 to the arguments for self and _cmd
    2 + sel.name().chars().filter(|&c| c == ':').count()
}

fn method_type_encoding<F>() -> CString where F: MethodImplementation {
    let mut types = F::Ret::encode().as_str().to_owned();
    types.extend(F::Args::encodings().as_ref().iter().map(|e| e.as_str()));
    CString::new(types).unwrap()
}

fn log2_align_of<T>() -> u8 {
    let align = mem::align_of::<T>();
    // Alignments are required to be powers of 2
    debug_assert!(align.count_ones() == 1);
    // log2 of a power of 2 is the number of trailing zeros
    align.trailing_zeros() as u8
}

/// A type for declaring a new class and adding new methods and ivars to it
/// before registering it.
pub struct ClassDecl {
    cls: *mut Class,
}

impl ClassDecl {
    /// Constructs a `ClassDecl` with the given superclass and name.
    /// Returns `None` if the class couldn't be allocated.
    pub fn new(name: &str, superclass: Option<&Class>) -> Option<ClassDecl> {
        let name = CString::new(name).unwrap();
        let super_ptr = superclass.map_or(ptr::null(), |c| c);
        let cls = unsafe {
            runtime::objc_allocateClassPair(super_ptr, to_c_str(&name), 0)
        };
        if cls.is_null() {
            None
        } else {
            Some(ClassDecl { cls: cls })
        }
    }

    /// Adds a method with the given name and implementation to self.
    /// Panics if the method wasn't sucessfully added
    /// or if the selector and function take different numbers of arguments.
    /// Unsafe because the caller must ensure that the types match those that
    /// are expected when the method is invoked from Objective-C.
    pub unsafe fn add_method<F>(&mut self, sel: Sel, func: F)
            where F: MethodImplementation<Callee=Object> {
        let types = method_type_encoding::<F>();
        let imp = match func.imp_for(sel) {
            Ok(imp) => imp,
            Err(err) => panic!("{}", err),
        };

        let success = runtime::class_addMethod(self.cls, sel, imp,
            to_c_str(&types));
        assert!(success != NO, "Failed to add method {:?}", sel);
    }

    /// Adds a class method with the given name and implementation to self.
    /// Panics if the method wasn't sucessfully added
    /// or if the selector and function take different numbers of arguments.
    /// Unsafe because the caller must ensure that the types match those that
    /// are expected when the method is invoked from Objective-C.
    pub unsafe fn add_class_method<F>(&mut self, sel: Sel, func: F)
            where F: MethodImplementation<Callee=Class> {
        let types = method_type_encoding::<F>();
        let imp = match func.imp_for(sel) {
            Ok(imp) => imp,
            Err(err) => panic!("{}", err),
        };

        let cls_obj = self.cls as *const Object;
        let metaclass = runtime::object_getClass(cls_obj) as *mut Class;
        let success = runtime::class_addMethod(metaclass, sel, imp,
            to_c_str(&types));
        assert!(success != NO, "Failed to add class method {:?}", sel);
    }

    /// Adds an ivar with type `T` and the provided name to self.
    /// Panics if the ivar wasn't successfully added.
    pub fn add_ivar<T>(&mut self, name: &str) where T: Encode {
        let c_name = CString::new(name).unwrap();
        let encoding = CString::new(T::encode().as_str()).unwrap();
        let size = mem::size_of::<T>();
        let align = log2_align_of::<T>();
        let success = unsafe {
            runtime::class_addIvar(self.cls, to_c_str(&c_name), size, align,
                to_c_str(&encoding))
        };
        assert!(success != NO, "Failed to add ivar {}", name);
    }

    /// Registers self, consuming it and returning a reference to the
    /// newly registered `Class`.
    pub fn register(self) -> &'static Class {
        unsafe {
            let cls = self.cls;
            runtime::objc_registerClassPair(cls);
            // Forget self otherwise the class will be disposed in drop
            mem::forget(self);
            &*cls
        }
    }
}

impl Drop for ClassDecl {
    fn drop(&mut self) {
        unsafe {
            runtime::objc_disposeClassPair(self.cls);
        }
    }
}

#[cfg(test)]
mod tests {
    use runtime::{Object, Sel};
    use test_utils;
    use super::MethodImplementation;

    #[test]
    fn test_custom_class() {
        // Registering the custom class is in test_utils
        let obj = test_utils::custom_object();
        unsafe {
            let _: () = msg_send![obj, setFoo:13u32];
            let result: u32 = msg_send![obj, foo];
            assert!(result == 13);
        }
    }

    #[test]
    fn test_class_method() {
        let cls = test_utils::custom_class();
        unsafe {
            let result: u32 = msg_send![cls, classFoo];
            assert!(result == 7);
        }
    }

    #[test]
    fn test_mismatched_args() {
        extern fn wrong_num_args_method(_obj: &Object, _cmd: Sel, _a: i32) { }

        let sel = sel!(doSomethingWithFoo:bar:);
        let f: extern fn(&Object, Sel, i32) = wrong_num_args_method;
        let imp = f.imp_for(sel);
        assert!(imp.is_err());
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The tiny-http Contributors
// Copyright 2015 The rust-chunked-transfer Contributors
//
// 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.

use std::error::Error;
use std::fmt;
use std::io::Error as IoError;
use std::io::ErrorKind;
use std::io::Read;
use std::io::Result as IoResult;

/// Reads HTTP chunks and sends back real data.
///
/// # Example
///
/// ```
/// use chunked_transfer::Decoder;
/// use std::io::Read;
///
/// let encoded = b"3\r\nhel\r\nb\r\nlo world!!!\r\n0\r\n\r\n";
/// let mut decoded = String::new();
///
/// let mut decoder = Decoder::new(encoded as &[u8]);
/// decoder.read_to_string(&mut decoded);
///
/// assert_eq!(decoded, "hello world!!!");
/// ```
pub struct Decoder<R> {
    // where the chunks come from
    source: R,

    // remaining size of the chunk being read
    // none if we are not in a chunk
    remaining_chunks_size: Option<usize>,
}

impl<R> Decoder<R>
{
    pub fn new(source: R) -> Decoder<R> {
        Decoder {
            source,
            remaining_chunks_size: None,
        }
    }

    /// Unwraps the inner read source.
    pub fn unwrap(self) -> R {
        self.source
    }
}

impl<R> Decoder<R>
where
    R: Read,
{
    fn read_chunk_size(&mut self) -> IoResult<usize> {
        let mut chunk_size_bytes = Vec::new();
        let mut has_ext = false;

        loop {
            let byte = match self.source.by_ref().bytes().next() {
                Some(b) => b?,
                None => return Err(IoError::new(ErrorKind::InvalidInput, DecoderError)),
            };

            if byte == b'\r' {
                break;
            }

            if byte == b';' {
                has_ext = true;
                break;
            }

            chunk_size_bytes.push(byte);
        }

        // Ignore extensions for now
        if has_ext {
            loop {
                let byte = match self.source.by_ref().bytes().next() {
                    Some(b) => b?,
                    None => return Err(IoError::new(ErrorKind::InvalidInput, DecoderError)),
                };
                if byte == b'\r' {
                    break;
                }
            }
        }

        self.read_line_feed()?;

        let chunk_size = String::from_utf8(chunk_size_bytes)
            .ok()
            .and_then(|c| usize::from_str_radix(c.trim(), 16).ok())
            .ok_or(IoError::new(ErrorKind::InvalidInput, DecoderError))?;

        Ok(chunk_size)
    }

    fn read_carriage_return(&mut self) -> IoResult<()> {
        match self.source.by_ref().bytes().next() {
            Some(Ok(b'\r')) => Ok(()),
            _ => Err(IoError::new(ErrorKind::InvalidInput, DecoderError)),
        }
    }

    fn read_line_feed(&mut self) -> IoResult<()> {
        match self.source.by_ref().bytes().next() {
            Some(Ok(b'\n')) => Ok(()),
            _ => Err(IoError::new(ErrorKind::InvalidInput, DecoderError)),
        }
    }
}

impl<R> Read for Decoder<R>
where
    R: Read,
{
    fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
        let remaining_chunks_size = match self.remaining_chunks_size {
            Some(c) => c,
            None => {
                // first possibility: we are not in a chunk, so we'll attempt to determine
                // the chunks size
                let chunk_size = self.read_chunk_size()?;

                // if the chunk size is 0, we are at EOF
                if chunk_size == 0 {
                    self.read_carriage_return()?;
                    self.read_line_feed()?;
                    return Ok(0);
                }

                chunk_size
            }
        };

        // second possibility: we continue reading from a chunk
        if buf.len() < remaining_chunks_size {
            let read = self.source.read(buf)?;
            self.remaining_chunks_size = Some(remaining_chunks_size - read);
            return Ok(read);
        }

        // third possibility: the read request goes further than the current chunk
        // we simply read until the end of the chunk and return
        assert!(buf.len() >= remaining_chunks_size);

        let buf = &mut buf[..remaining_chunks_size];
        let read = self.source.read(buf)?;

        self.remaining_chunks_size = if read == remaining_chunks_size {
            self.read_carriage_return()?;
            self.read_line_feed()?;
            None
        } else {
            Some(remaining_chunks_size - read)
        };

        Ok(read)
    }
}

#[derive(Debug, Copy, Clone)]
struct DecoderError;

impl fmt::Display for DecoderError {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        write!(fmt, "Error while decoding chunks")
    }
}

impl Error for DecoderError {
    fn description(&self) -> &str {
        "Error while decoding chunks"
    }
}

#[cfg(test)]
mod test {
    use super::Decoder;
    use std::io;
    use std::io::Read;

    /// This unit test is taken from from Hyper
    /// https://github.com/hyperium/hyper
    /// Copyright (c) 2014 Sean McArthur
    #[test]
    fn test_read_chunk_size() {
        fn read(s: &str, expected: usize) {
            let mut decoded = Decoder::new(s.as_bytes());
            let actual = decoded.read_chunk_size().unwrap();
            assert_eq!(expected, actual);
        }

        fn read_err(s: &str) {
            let mut decoded = Decoder::new(s.as_bytes());
            let err_kind = decoded.read_chunk_size().unwrap_err().kind();
            assert_eq!(err_kind, io::ErrorKind::InvalidInput);
        }

        read("1\r\n", 1);
        read("01\r\n", 1);
        read("0\r\n", 0);
        read("00\r\n", 0);
        read("A\r\n", 10);
        read("a\r\n", 10);
        read("Ff\r\n", 255);
        read("Ff   \r\n", 255);
        // Missing LF or CRLF
        read_err("F\rF");
        read_err("F");
        // Invalid hex digit
        read_err("X\r\n");
        read_err("1X\r\n");
        read_err("-\r\n");
        read_err("-1\r\n");
        // Acceptable (if not fully valid) extensions do not influence the size
        read("1;extension\r\n", 1);
        read("a;ext name=value\r\n", 10);
        read("1;extension;extension2\r\n", 1);
        read("1;;;  ;\r\n", 1);
        read("2; extension...\r\n", 2);
        read("3   ; extension=123\r\n", 3);
        read("3   ;\r\n", 3);
        read("3   ;   \r\n", 3);
        // Invalid extensions cause an error
        read_err("1 invalid extension\r\n");
        read_err("1 A\r\n");
        read_err("1;no CRLF");
    }

    #[test]
    fn test_valid_chunk_decode() {
        let source = io::Cursor::new(
            "3\r\nhel\r\nb\r\nlo world!!!\r\n0\r\n\r\n"
                .to_string()
                .into_bytes(),
        );
        let mut decoded = Decoder::new(source);

        let mut string = String::new();
        decoded.read_to_string(&mut string).unwrap();

        assert_eq!(string, "hello world!!!");
    }

    #[test]
    fn test_decode_zero_length() {
        let mut decoder = Decoder::new(b"0\r\n\r\n" as &[u8]);

        let mut decoded = String::new();
        decoder.read_to_string(&mut decoded).unwrap();

        assert_eq!(decoded, "");
    }

    #[test]
    fn test_decode_invalid_chunk_length() {
        let mut decoder = Decoder::new(b"m\r\n\r\n" as &[u8]);

        let mut decoded = String::new();
        assert!(decoder.read_to_string(&mut decoded).is_err());
    }

    #[test]
    fn invalid_input1() {
        let source = io::Cursor::new(
            "2\r\nhel\r\nb\r\nlo world!!!\r\n0\r\n"
                .to_string()
                .into_bytes(),
        );
        let mut decoded = Decoder::new(source);

        let mut string = String::new();
        decoded.read_to_string(&mut string).is_err();
    }

    #[test]
    fn invalid_input2() {
        let source = io::Cursor::new(
            "3\rhel\r\nb\r\nlo world!!!\r\n0\r\n"
                .to_string()
                .into_bytes(),
        );
        let mut decoded = Decoder::new(source);

        let mut string = String::new();
        decoded.read_to_string(&mut string).is_err();
    }
}
<commit_msg>Rename to into_inner.<commit_after>// Copyright 2015 The tiny-http Contributors
// Copyright 2015 The rust-chunked-transfer Contributors
//
// 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.

use std::error::Error;
use std::fmt;
use std::io::Error as IoError;
use std::io::ErrorKind;
use std::io::Read;
use std::io::Result as IoResult;

/// Reads HTTP chunks and sends back real data.
///
/// # Example
///
/// ```
/// use chunked_transfer::Decoder;
/// use std::io::Read;
///
/// let encoded = b"3\r\nhel\r\nb\r\nlo world!!!\r\n0\r\n\r\n";
/// let mut decoded = String::new();
///
/// let mut decoder = Decoder::new(encoded as &[u8]);
/// decoder.read_to_string(&mut decoded);
///
/// assert_eq!(decoded, "hello world!!!");
/// ```
pub struct Decoder<R> {
    // where the chunks come from
    source: R,

    // remaining size of the chunk being read
    // none if we are not in a chunk
    remaining_chunks_size: Option<usize>,
}

impl<R> Decoder<R>
{
    pub fn new(source: R) -> Decoder<R> {
        Decoder {
            source,
            remaining_chunks_size: None,
        }
    }

    /// Unwraps the Decoder into its inner `Read` source.
    pub fn into_inner(self) -> R {
        self.source
    }
}

impl<R> Decoder<R>
where
    R: Read,
{
    fn read_chunk_size(&mut self) -> IoResult<usize> {
        let mut chunk_size_bytes = Vec::new();
        let mut has_ext = false;

        loop {
            let byte = match self.source.by_ref().bytes().next() {
                Some(b) => b?,
                None => return Err(IoError::new(ErrorKind::InvalidInput, DecoderError)),
            };

            if byte == b'\r' {
                break;
            }

            if byte == b';' {
                has_ext = true;
                break;
            }

            chunk_size_bytes.push(byte);
        }

        // Ignore extensions for now
        if has_ext {
            loop {
                let byte = match self.source.by_ref().bytes().next() {
                    Some(b) => b?,
                    None => return Err(IoError::new(ErrorKind::InvalidInput, DecoderError)),
                };
                if byte == b'\r' {
                    break;
                }
            }
        }

        self.read_line_feed()?;

        let chunk_size = String::from_utf8(chunk_size_bytes)
            .ok()
            .and_then(|c| usize::from_str_radix(c.trim(), 16).ok())
            .ok_or(IoError::new(ErrorKind::InvalidInput, DecoderError))?;

        Ok(chunk_size)
    }

    fn read_carriage_return(&mut self) -> IoResult<()> {
        match self.source.by_ref().bytes().next() {
            Some(Ok(b'\r')) => Ok(()),
            _ => Err(IoError::new(ErrorKind::InvalidInput, DecoderError)),
        }
    }

    fn read_line_feed(&mut self) -> IoResult<()> {
        match self.source.by_ref().bytes().next() {
            Some(Ok(b'\n')) => Ok(()),
            _ => Err(IoError::new(ErrorKind::InvalidInput, DecoderError)),
        }
    }
}

impl<R> Read for Decoder<R>
where
    R: Read,
{
    fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
        let remaining_chunks_size = match self.remaining_chunks_size {
            Some(c) => c,
            None => {
                // first possibility: we are not in a chunk, so we'll attempt to determine
                // the chunks size
                let chunk_size = self.read_chunk_size()?;

                // if the chunk size is 0, we are at EOF
                if chunk_size == 0 {
                    self.read_carriage_return()?;
                    self.read_line_feed()?;
                    return Ok(0);
                }

                chunk_size
            }
        };

        // second possibility: we continue reading from a chunk
        if buf.len() < remaining_chunks_size {
            let read = self.source.read(buf)?;
            self.remaining_chunks_size = Some(remaining_chunks_size - read);
            return Ok(read);
        }

        // third possibility: the read request goes further than the current chunk
        // we simply read until the end of the chunk and return
        assert!(buf.len() >= remaining_chunks_size);

        let buf = &mut buf[..remaining_chunks_size];
        let read = self.source.read(buf)?;

        self.remaining_chunks_size = if read == remaining_chunks_size {
            self.read_carriage_return()?;
            self.read_line_feed()?;
            None
        } else {
            Some(remaining_chunks_size - read)
        };

        Ok(read)
    }
}

#[derive(Debug, Copy, Clone)]
struct DecoderError;

impl fmt::Display for DecoderError {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        write!(fmt, "Error while decoding chunks")
    }
}

impl Error for DecoderError {
    fn description(&self) -> &str {
        "Error while decoding chunks"
    }
}

#[cfg(test)]
mod test {
    use super::Decoder;
    use std::io;
    use std::io::Read;

    /// This unit test is taken from from Hyper
    /// https://github.com/hyperium/hyper
    /// Copyright (c) 2014 Sean McArthur
    #[test]
    fn test_read_chunk_size() {
        fn read(s: &str, expected: usize) {
            let mut decoded = Decoder::new(s.as_bytes());
            let actual = decoded.read_chunk_size().unwrap();
            assert_eq!(expected, actual);
        }

        fn read_err(s: &str) {
            let mut decoded = Decoder::new(s.as_bytes());
            let err_kind = decoded.read_chunk_size().unwrap_err().kind();
            assert_eq!(err_kind, io::ErrorKind::InvalidInput);
        }

        read("1\r\n", 1);
        read("01\r\n", 1);
        read("0\r\n", 0);
        read("00\r\n", 0);
        read("A\r\n", 10);
        read("a\r\n", 10);
        read("Ff\r\n", 255);
        read("Ff   \r\n", 255);
        // Missing LF or CRLF
        read_err("F\rF");
        read_err("F");
        // Invalid hex digit
        read_err("X\r\n");
        read_err("1X\r\n");
        read_err("-\r\n");
        read_err("-1\r\n");
        // Acceptable (if not fully valid) extensions do not influence the size
        read("1;extension\r\n", 1);
        read("a;ext name=value\r\n", 10);
        read("1;extension;extension2\r\n", 1);
        read("1;;;  ;\r\n", 1);
        read("2; extension...\r\n", 2);
        read("3   ; extension=123\r\n", 3);
        read("3   ;\r\n", 3);
        read("3   ;   \r\n", 3);
        // Invalid extensions cause an error
        read_err("1 invalid extension\r\n");
        read_err("1 A\r\n");
        read_err("1;no CRLF");
    }

    #[test]
    fn test_valid_chunk_decode() {
        let source = io::Cursor::new(
            "3\r\nhel\r\nb\r\nlo world!!!\r\n0\r\n\r\n"
                .to_string()
                .into_bytes(),
        );
        let mut decoded = Decoder::new(source);

        let mut string = String::new();
        decoded.read_to_string(&mut string).unwrap();

        assert_eq!(string, "hello world!!!");
    }

    #[test]
    fn test_decode_zero_length() {
        let mut decoder = Decoder::new(b"0\r\n\r\n" as &[u8]);

        let mut decoded = String::new();
        decoder.read_to_string(&mut decoded).unwrap();

        assert_eq!(decoded, "");
    }

    #[test]
    fn test_decode_invalid_chunk_length() {
        let mut decoder = Decoder::new(b"m\r\n\r\n" as &[u8]);

        let mut decoded = String::new();
        assert!(decoder.read_to_string(&mut decoded).is_err());
    }

    #[test]
    fn invalid_input1() {
        let source = io::Cursor::new(
            "2\r\nhel\r\nb\r\nlo world!!!\r\n0\r\n"
                .to_string()
                .into_bytes(),
        );
        let mut decoded = Decoder::new(source);

        let mut string = String::new();
        decoded.read_to_string(&mut string).is_err();
    }

    #[test]
    fn invalid_input2() {
        let source = io::Cursor::new(
            "3\rhel\r\nb\r\nlo world!!!\r\n0\r\n"
                .to_string()
                .into_bytes(),
        );
        let mut decoded = Decoder::new(source);

        let mut string = String::new();
        decoded.read_to_string(&mut string).is_err();
    }
}
<|endoftext|>


<commit_before>use std::f64::{MIN};
use std::iter::FromIterator;

use super::{CharIndex, ToChar};
use super::enigma::Enigma;

lazy_static! {
    static ref QGRAMS: Vec<f64> = {
        let f = include_str!("data/quadgrams.txt");
        let mut qgrams = vec![0.0; 456_976];  // 26^4 = 456,976

        for line in f.lines() {
            let line: Vec<_> = line.split(' ').collect();
            let qgram: &str = line[0];
            let count: f64 = line[1].parse().unwrap();

            let index = qgram.chars().fold(0, |acc, c| 26 * acc + c.index());
            qgrams[index] = count.ln();
        }

        qgrams
    };
}

/// Decrypts the given string by iterating through multiple possible Enigma
/// configurations, returning the tuple `(plaintext, key, ring, rotor)`
/// corresponding to the most probable plaintext.
pub fn decrypt(msg: &str) -> (String, String, String, String) {
    let mut best_score = MIN;
    let mut best_msg = msg.to_owned();
    let mut best_key = "AAA".to_owned();
    let mut best_ring = "AAA".to_owned();
    let mut best_rotor = "123".to_owned();

    // Rotor and key settings (60*26^3 == 1,054,560 decryptions)
    let r = "12345".chars();

    for (slow, mid, fast) in iproduct!(r.clone(), r.clone(), r.clone()) {
        // Skip rotor combinations that contain duplicates
        if (slow == mid) || (slow == fast) || (mid == fast) {
            continue;
        }

        let rotor = String::from_iter(vec![slow, mid, fast]);

        for (a, b, c) in iproduct!(0..26, 0..26, 0..26) {
            let key = String::from_iter(vec![a.to_char(), b.to_char(), c.to_char()]);

            let mut enigma = Enigma::new(&rotor, &key, "AAA", 'B', "");
            let plaintext = enigma.encrypt(msg);
            let score = qgram_score(&plaintext);

            if score > best_score {
                best_score = score;
                best_msg = plaintext;
                best_key = key;
                best_rotor = rotor.clone();
            }
        }
    }

    let first_key = best_key.chars().nth(0).unwrap();

    // Key and ring settings (26^4 == 456,976 decryptions)
    for (key2, key3, ring2, ring3) in iproduct!(0..26, 0..26, 0..26, 0..26) {
        let key = String::from_iter(vec![first_key, key2.to_char(), key3.to_char()]);
        let ring = String::from_iter(vec!['A', ring2.to_char(), ring3.to_char()]);

        let mut enigma = Enigma::new(&best_rotor, &key, &ring, 'B', "");
        let plaintext = enigma.encrypt(msg);
        let score = qgram_score(&plaintext);

        if score > best_score {
            best_score = score;
            best_msg = plaintext;
            best_key = key;
            best_ring = ring;
        }
    }

    (best_msg, best_key, best_ring, best_rotor)
}

/// Strips all non-alphabetic characters from the given message string and
/// returns the sum of the log-probabilities of each quadgram substring.
pub fn qgram_score(msg: &str) -> f64 {
    let msg: Vec<usize> = msg.chars()
        .filter(|&c| c.is_alphabetic())
        .map(|c| c.index())
        .collect();

    if msg.len() < 4 {
        panic!("Input string must be longer than 4 characters.");
    }

    let mut sum = 0.0;

    for i in 0..(msg.len() - 3) {
        let a = msg[i];
        let b = msg[i + 1];
        let c = msg[i + 2];
        let d = msg[i + 3];

        let index = (((a * 26) + b) * 26 + c) * 26 + d;
        sum += QGRAMS[index];
    }

    sum
}

#[cfg(test)]
mod tests {
    use super::{QGRAMS, qgram_score};

    macro_rules! assert_approx_eq {
        ($a:expr, $b:expr) => {
            let (a, b) = (&$a, &$b);
            assert!((*a - *b).abs() < 1.0e-6, "{} is not approximately equal to {}", *a, *b);
        }
    }

    #[test]
    fn qgram_estimates() {
        assert_approx_eq!(QGRAMS[0], 8.81060879);
        assert_approx_eq!(qgram_score("THE QUICK BROWN FOX"), 149.80102862);
    }

    #[test]
    fn sensible_qgram_scores() {
        assert!(qgram_score("AN ENGLISH PHRASE") > qgram_score("ESARHP HSILGNE NA"));
    }

    #[test]
    #[should_panic]
    fn invalid_qgram_check() {
        qgram_score("ABC");
    }
}
<commit_msg>Simplify qgram_score computation<commit_after>use std::f64::{MIN};
use std::iter::FromIterator;

use super::{CharIndex, ToChar};
use super::enigma::Enigma;

lazy_static! {
    static ref QGRAMS: Vec<f64> = {
        let f = include_str!("data/quadgrams.txt");
        let mut qgrams = vec![0.0; 456_976];  // 26^4 = 456,976

        for line in f.lines() {
            let line: Vec<_> = line.split(' ').collect();
            let qgram: &str = line[0];
            let count: f64 = line[1].parse().unwrap();

            let index = qgram.chars().fold(0, |acc, c| 26 * acc + c.index());
            qgrams[index] = count.ln();
        }

        qgrams
    };
}

/// Decrypts the given string by iterating through multiple possible Enigma
/// configurations, returning the tuple `(plaintext, key, ring, rotor)`
/// corresponding to the most probable plaintext.
pub fn decrypt(msg: &str) -> (String, String, String, String) {
    let mut best_score = MIN;
    let mut best_msg = msg.to_owned();
    let mut best_key = "AAA".to_owned();
    let mut best_ring = "AAA".to_owned();
    let mut best_rotor = "123".to_owned();

    // Rotor and key settings (60*26^3 == 1,054,560 decryptions)
    let r = "12345".chars();

    for (slow, mid, fast) in iproduct!(r.clone(), r.clone(), r.clone()) {
        // Skip rotor combinations that contain duplicates
        if (slow == mid) || (slow == fast) || (mid == fast) {
            continue;
        }

        let rotor = String::from_iter(vec![slow, mid, fast]);

        for (a, b, c) in iproduct!(0..26, 0..26, 0..26) {
            let key = String::from_iter(vec![a.to_char(), b.to_char(), c.to_char()]);

            let mut enigma = Enigma::new(&rotor, &key, "AAA", 'B', "");
            let plaintext = enigma.encrypt(msg);
            let score = qgram_score(&plaintext);

            if score > best_score {
                best_score = score;
                best_msg = plaintext;
                best_key = key;
                best_rotor = rotor.clone();
            }
        }
    }

    let first_key = best_key.chars().nth(0).unwrap();

    // Key and ring settings (26^4 == 456,976 decryptions)
    for (key2, key3, ring2, ring3) in iproduct!(0..26, 0..26, 0..26, 0..26) {
        let key = String::from_iter(vec![first_key, key2.to_char(), key3.to_char()]);
        let ring = String::from_iter(vec!['A', ring2.to_char(), ring3.to_char()]);

        let mut enigma = Enigma::new(&best_rotor, &key, &ring, 'B', "");
        let plaintext = enigma.encrypt(msg);
        let score = qgram_score(&plaintext);

        if score > best_score {
            best_score = score;
            best_msg = plaintext;
            best_key = key;
            best_ring = ring;
        }
    }

    (best_msg, best_key, best_ring, best_rotor)
}

/// Strips all non-alphabetic characters from the given message string and
/// returns the sum of the log-probabilities of each quadgram substring.
pub fn qgram_score(msg: &str) -> f64 {
    let char_indices: Vec<usize> = msg.chars()
        .filter(|&c| c.is_alphabetic())
        .map(|c| c.index())
        .collect();

    if char_indices.len() < 4 {
        panic!("Input string must be longer than 4 characters.");
    }

    char_indices.windows(4)
        .map(|w| w.iter().fold(0, |acc, x| 26 * acc + x))
        .map(|i| QGRAMS[i])
        .sum()
}

#[cfg(test)]
mod tests {
    use super::{QGRAMS, qgram_score};

    macro_rules! assert_approx_eq {
        ($a:expr, $b:expr) => {
            let (a, b) = (&$a, &$b);
            assert!((*a - *b).abs() < 1.0e-6, "{} is not approximately equal to {}", *a, *b);
        }
    }

    #[test]
    fn qgram_estimates() {
        assert_approx_eq!(QGRAMS[0], 8.81060879);
        assert_approx_eq!(qgram_score("THE QUICK BROWN FOX"), 149.80102862);
    }

    #[test]
    fn sensible_qgram_scores() {
        assert!(qgram_score("AN ENGLISH PHRASE") > qgram_score("ESARHP HSILGNE NA"));
    }

    #[test]
    #[should_panic]
    fn invalid_qgram_check() {
        qgram_score("ABC");
    }
}
<|endoftext|>


<commit_before>extern crate num;

use std::error::Error;
use std::fmt::{Display, Formatter};
use std::fmt::Error as FmtError;
use self::num::Complex;

/// Error codes
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum AfError {
    /// The function returned successfully
    SUCCESS            =   0,
    // 100-199 Errors in environment
    /// The system or device ran out of memory
    ERR_NO_MEM         = 101,
    /// There was an error in the device driver
    ERR_DRIVER         = 102,
    /// There was an error with the runtime environment
    ERR_RUNTIME        = 103,
    // 200-299 Errors in input parameters
    /// The input array is not a valid Array object
    ERR_INVALID_ARRAY  = 201,
    /// One of the function arguments is incorrect
    ERR_ARG            = 202,
    /// The size is incorrect
    ERR_SIZE           = 203,
    /// The type is not suppported by this function
    ERR_TYPE           = 204,
    /// The type of the input arrays are not compatible
    ERR_DIFF_TYPE      = 205,
    /// Function does not support GFOR / batch mode
    ERR_BATCH          = 207,
    /// Input does not belong to the current device
    ERR_DEVICE         = 208,
    // 300-399 Errors for missing software features
    /// The option is not supported
    ERR_NOT_SUPPORTED  = 301,
    /// This build of ArrayFire does not support this feature
    ERR_NOT_CONFIGURED = 302,
    // 400-499 Errors for missing hardware features
    /// This device does not support double
    ERR_NO_DBL         = 401,
    /// This build of ArrayFire was not built with graphics or this device does
    /// not support graphics
    ERR_NO_GFX         = 402,
    // 900-999 Errors from upstream libraries and runtimes
    /// There was an internal error either in ArrayFire or in a project
    /// upstream
    ERR_INTERNAL       = 998,
    /// Unknown Error
    ERR_UNKNOWN        = 999
}

/// Compute/Acceleration Backend
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Backend {
    /// Default backend order: OpenCL -> CUDA -> CPU
    DEFAULT = 0,
    /// CPU a.k.a sequential algorithms
    CPU     = 1,
    /// CUDA Compute Backend
    CUDA    = 2,
    /// OpenCL Compute Backend
    OPENCL  = 4
}

impl Display for Backend {
    fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
        let text = match *self {
            Backend::OPENCL  => "OpenCL",
            Backend::CUDA    => "Cuda",
            Backend::CPU     => "CPU",
            Backend::DEFAULT => "Default",
        };
        write!(f, "{}", text)
    }
}

impl Display for AfError {
    fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
        write!(f, "{}", self.description())
    }
}

impl Error for AfError {
    fn description(&self) -> &str {
        match *self {
            AfError::SUCCESS            => "Function returned successfully",
            AfError::ERR_NO_MEM         => "System or Device ran out of memory",
            AfError::ERR_DRIVER         => "Error in the device driver",
            AfError::ERR_RUNTIME        => "Error with the runtime environment",
            AfError::ERR_INVALID_ARRAY  => "Iput Array is not a valid object",
            AfError::ERR_ARG            => "One of the function arguments is incorrect",
            AfError::ERR_SIZE           => "Size is incorrect",
            AfError::ERR_TYPE           => "Type is not suppported by this function",
            AfError::ERR_DIFF_TYPE      => "Type of the input arrays are not compatible",
            AfError::ERR_BATCH          => "Function does not support GFOR / batch mode",
            AfError::ERR_DEVICE         => "Input does not belong to the current device",
            AfError::ERR_NOT_SUPPORTED  => "Unsupported operation/parameter option",
            AfError::ERR_NOT_CONFIGURED => "This build of ArrayFire does not support this feature",
            AfError::ERR_NO_DBL         => "This device does not support double",
            AfError::ERR_NO_GFX         => "This build of ArrayFire has no graphics support",
            AfError::ERR_INTERNAL       => "Eror either in ArrayFire or in a project upstream",
            AfError::ERR_UNKNOWN        => "Unknown Error",
        }
    }
}

/// Types of Array data type
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum DType {
    /// 32 bit float
    F32 = 0,
    /// 32 bit complex float
    C32 = 1,
    /// 64 bit float
    F64 = 2,
    /// 64 bit complex float
    C64 = 3,
    /// 8 bit boolean
    B8  = 4,
    /// 32 bit signed integer
    S32 = 5,
    /// 32 bit unsigned integer
    U32 = 6,
    /// 8 bit unsigned integer
    U8  = 7,
    /// 64 bit signed integer
    S64 = 8,
    /// 64 bit unsigned integer
    U64 = 9,
    /// 16 bit signed integer
    S16 = 10,
    /// 16 bit unsigned integer
    U16 = 11,
}

/// Dictates the interpolation method to be used by a function
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum InterpType {
    /// Nearest Neighbor interpolation method
    NEAREST = 0,
    /// Linear interpolation method
    LINEAR  = 1,
    /// Bilinear interpolation method
    BILINEAR= 2,
    /// Cubic interpolation method
    CUBIC   = 3,
    /// Floor indexed
    LOWER   = 4,
    /// Linear interpolation with cosine smoothing
    LINEAR_COSINE   = 5,
    /// Bilinear interpolation with cosine smoothing
    BILINEAR_COSINE = 6,
    /// Bicubic interpolation
    BICUBIC         = 7,
    /// Cubic interpolation with Catmull-Rom splines
    CUBIC_SPLINE    = 8,
    /// Bicubic interpolation with Catmull-Rom splines
    BICUBIC_SPLINE  = 9
}

/// Helps determine how to pad kernels along borders
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum BorderType {
    /// Pad using zeros
    ZERO = 0,
    /// Pad using mirrored values along border
    SYMMETRIC = 1,
}

/// Used by `regions` function to identify type of connectivity
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Connectivity {
    /// North-East-South-West (N-E-S-W) connectivity from given pixel/point
    FOUR = 4,
    /// N-NE-E-SE-S-SW-W-NW connectivity from given pixel/point
    EIGHT = 8
}

/// Helps determine the size of output of convolution
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ConvMode {
    /// Default convolution mode where output size is same as input size
    DEFAULT = 0,
    /// Output of convolution is expanded based on signal and filter sizes
    EXPAND  = 1,
}

/// Helps determine if convolution is in Spatial or Frequency domain
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ConvDomain {
    /// ArrayFire chooses whether the convolution will be in spatial domain or frequency domain
    AUTO     = 0,
    /// Convoltion in spatial domain
    SPATIAL  = 1,
    /// Convolution in frequency domain
    FREQUENCY= 2,
}

/// Error metric used by `matchTemplate` function
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum MatchType {
    /// Sum of Absolute Differences
    SAD = 0,
    /// Zero-mean Sum of Absolute Differences
    ZSAD= 1,
    /// Locally scaled Sum of Absolute Differences
    LSAD= 2,
    /// Sum of Squared Differences
    SSD = 3,
    /// Zero-mean Sum of Squared Differences
    ZSSD= 4,
    /// Localy scaled Sum of Squared Differences
    LSSD= 5,
    /// Normalized Cross Correlation
    NCC = 6,
    /// Zero-mean Normalized Cross Correlation
    ZNCC= 7,
    /// Sum of Hamming Distances
    SHD = 8,
}

/// Identify the color space of given image(Array)
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ColorSpace {
    /// Grayscale color space
    GRAY = 0,
    /// Red-Green-Blue color space
    RGB  = 1,
    /// Hue-Saturation-value color space
    HSV  = 2,
}

/// Helps determine the type of a Matrix
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum MatProp {
    /// Default (no-op)
    NONE,
    /// Data needs to be transposed
    TRANS,
    /// Data needs to be conjugate transposed
    CTRANS,
    /// Matrix is upper triangular
    UPPER,
    /// Matrix is lower triangular
    LOWER,
    /// Matrix diagonal has unitary values
    DIAGUNIT,
    /// Matrix is symmetric
    SYM,
    /// Matrix is positive definite
    POSDEF,
    /// Matrix is orthogonal
    ORTHOG,
    /// Matrix is tri-diagonal
    TRIDIAG,
    /// Matrix is block-diagonal
    BLOCKDIAG,
}

/// Norm type
#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum NormType {
    /// Treats input as a vector and return sum of absolute values
    VECTOR_1    = 0,
    /// Treats input as vector and return max of absolute values
    VECTOR_INF  = 1,
    /// Treats input as vector and returns euclidean norm
    VECTOR_2    = 2,
    /// Treats input as vector and returns the p-norm
    VECTOR_P    = 3,
    /// Return the max of column sums
    MATRIX_1    = 4,
    /// Return the max of row sums
    MATRIX_INF  = 5,
    /// Returns the max singular value (Currently not supported)
    MATRIX_2    = 6,
    /// Returns Lpq-norm
    MATRIX_L_PQ = 7,
}

/// Dictates what color map is used for Image rendering
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ColorMap {
    /// Default color map is grayscale range [0-1]
    DEFAULT = 0,
    /// Visible spectrum color map
    SPECTRUM= 1,
    /// Colors
    COLORS  = 2,
    /// Red hue map
    RED     = 3,
    /// Mood color map
    MOOD    = 4,
    /// Heat color map
    HEAT    = 5,
    /// Blue hue map
    BLUE    = 6,
}

/// YCbCr Standards
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum YCCStd {
    /// ITU-R BT.601 (formerly CCIR 601) standard
    YCC_601 = 601,
    /// ITU-R BT.709 standard
    YCC_709 = 709,
    /// ITU-R BT.2020 standard
    YCC_2020 = 2020,
}

/// Homography type
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum HomographyType {
    /// RANdom SAmple Consensus algorithm
    RANSAC = 0,
    /// Least Median of Squares
    LMEDS  = 1,
}

/// Plotting markers
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum MarkerType {
    NONE     = 0,
    POINT    = 1,
    CIRCLE   = 2,
    SQUARE   = 3,
    TRIANGLE = 4,
    CROSS    = 5,
    PLUS     = 6,
    STAR     = 7
}

/// Image moment types
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum MomentType {
    M00 = 1,    // 1<<0
    M01 = 2,    // 1<<1
    M10 = 4,    // 1<<2
    M11 = 8,    // 1<<3
    FIRST_ORDER = 1<<0 | 1<<1 | 1<<2 | 1<<3
}

/// Sparse storage format type
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum SparseFormat {
    /// Dense format
    DENSE = 0,
    /// Compressed sparse row format
    CSR   = 1,
    /// Compressed sparse coloumn format
    CSC   = 2,
    /// Coordinate list (row, coloumn, value) tuples.
    COO   = 3
}

/// Binary operation types for generalized scan functions
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum BinaryOp {
    ADD = 0,
    MUL = 1,
    MIN = 2,
    MAX = 3
}

/// Random engine types
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum RandomEngineType {
    ///Philox variant with N=4, W=32 and Rounds=10
    PHILOX_4X32_10      = 100,
    ///Threefry variant with N=2, W=32 and Rounds=16
    THREEFRY_2X32_16    = 200,
    ///Mersenne variant with MEXP = 11213
    MERSENNE_GP11213    = 300
}

pub const PHILOX   : RandomEngineType = RandomEngineType::PHILOX_4X32_10;
pub const THREEFRY : RandomEngineType = RandomEngineType::THREEFRY_2X32_16;
pub const MERSENNE : RandomEngineType = RandomEngineType::MERSENNE_GP11213;
pub const DEFAULT_RANDOM_ENGINE : RandomEngineType = PHILOX;

/// Scalar value types
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Scalar {
    /// 32 bit float
    F32(f32),
    /// 32 bit complex float
    C32(Complex<f32>),
    /// 64 bit float
    F64(f64),
    /// 64 bit complex float
    C64(Complex<f64>),
    /// 8 bit boolean
    B8(bool),
    /// 32 bit signed integer
    S32(i32),
    /// 32 bit unsigned integer
    U32(u32),
    /// 8 bit unsigned integer
    U8(u8),
    /// 64 bit signed integer
    S64(i64),
    /// 64 bit unsigned integer
    U64(u64),
    /// 16 bit signed integer
    S16(i16),
    /// 16 bit unsigned integer
    U16(u16),
}
<commit_msg>Fix a typo on error message<commit_after>extern crate num;

use std::error::Error;
use std::fmt::{Display, Formatter};
use std::fmt::Error as FmtError;
use self::num::Complex;

/// Error codes
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum AfError {
    /// The function returned successfully
    SUCCESS            =   0,
    // 100-199 Errors in environment
    /// The system or device ran out of memory
    ERR_NO_MEM         = 101,
    /// There was an error in the device driver
    ERR_DRIVER         = 102,
    /// There was an error with the runtime environment
    ERR_RUNTIME        = 103,
    // 200-299 Errors in input parameters
    /// The input array is not a valid Array object
    ERR_INVALID_ARRAY  = 201,
    /// One of the function arguments is incorrect
    ERR_ARG            = 202,
    /// The size is incorrect
    ERR_SIZE           = 203,
    /// The type is not suppported by this function
    ERR_TYPE           = 204,
    /// The type of the input arrays are not compatible
    ERR_DIFF_TYPE      = 205,
    /// Function does not support GFOR / batch mode
    ERR_BATCH          = 207,
    /// Input does not belong to the current device
    ERR_DEVICE         = 208,
    // 300-399 Errors for missing software features
    /// The option is not supported
    ERR_NOT_SUPPORTED  = 301,
    /// This build of ArrayFire does not support this feature
    ERR_NOT_CONFIGURED = 302,
    // 400-499 Errors for missing hardware features
    /// This device does not support double
    ERR_NO_DBL         = 401,
    /// This build of ArrayFire was not built with graphics or this device does
    /// not support graphics
    ERR_NO_GFX         = 402,
    // 900-999 Errors from upstream libraries and runtimes
    /// There was an internal error either in ArrayFire or in a project
    /// upstream
    ERR_INTERNAL       = 998,
    /// Unknown Error
    ERR_UNKNOWN        = 999
}

/// Compute/Acceleration Backend
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Backend {
    /// Default backend order: OpenCL -> CUDA -> CPU
    DEFAULT = 0,
    /// CPU a.k.a sequential algorithms
    CPU     = 1,
    /// CUDA Compute Backend
    CUDA    = 2,
    /// OpenCL Compute Backend
    OPENCL  = 4
}

impl Display for Backend {
    fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
        let text = match *self {
            Backend::OPENCL  => "OpenCL",
            Backend::CUDA    => "Cuda",
            Backend::CPU     => "CPU",
            Backend::DEFAULT => "Default",
        };
        write!(f, "{}", text)
    }
}

impl Display for AfError {
    fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
        write!(f, "{}", self.description())
    }
}

impl Error for AfError {
    fn description(&self) -> &str {
        match *self {
            AfError::SUCCESS            => "Function returned successfully",
            AfError::ERR_NO_MEM         => "System or Device ran out of memory",
            AfError::ERR_DRIVER         => "Error in the device driver",
            AfError::ERR_RUNTIME        => "Error with the runtime environment",
            AfError::ERR_INVALID_ARRAY  => "Iput Array is not a valid object",
            AfError::ERR_ARG            => "One of the function arguments is incorrect",
            AfError::ERR_SIZE           => "Size is incorrect",
            AfError::ERR_TYPE           => "Type is not suppported by this function",
            AfError::ERR_DIFF_TYPE      => "Type of the input arrays are not compatible",
            AfError::ERR_BATCH          => "Function does not support GFOR / batch mode",
            AfError::ERR_DEVICE         => "Input does not belong to the current device",
            AfError::ERR_NOT_SUPPORTED  => "Unsupported operation/parameter option",
            AfError::ERR_NOT_CONFIGURED => "This build of ArrayFire does not support this feature",
            AfError::ERR_NO_DBL         => "This device does not support double",
            AfError::ERR_NO_GFX         => "This build of ArrayFire has no graphics support",
            AfError::ERR_INTERNAL       => "Error either in ArrayFire or in a project upstream",
            AfError::ERR_UNKNOWN        => "Unknown Error",
        }
    }
}

/// Types of Array data type
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum DType {
    /// 32 bit float
    F32 = 0,
    /// 32 bit complex float
    C32 = 1,
    /// 64 bit float
    F64 = 2,
    /// 64 bit complex float
    C64 = 3,
    /// 8 bit boolean
    B8  = 4,
    /// 32 bit signed integer
    S32 = 5,
    /// 32 bit unsigned integer
    U32 = 6,
    /// 8 bit unsigned integer
    U8  = 7,
    /// 64 bit signed integer
    S64 = 8,
    /// 64 bit unsigned integer
    U64 = 9,
    /// 16 bit signed integer
    S16 = 10,
    /// 16 bit unsigned integer
    U16 = 11,
}

/// Dictates the interpolation method to be used by a function
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum InterpType {
    /// Nearest Neighbor interpolation method
    NEAREST = 0,
    /// Linear interpolation method
    LINEAR  = 1,
    /// Bilinear interpolation method
    BILINEAR= 2,
    /// Cubic interpolation method
    CUBIC   = 3,
    /// Floor indexed
    LOWER   = 4,
    /// Linear interpolation with cosine smoothing
    LINEAR_COSINE   = 5,
    /// Bilinear interpolation with cosine smoothing
    BILINEAR_COSINE = 6,
    /// Bicubic interpolation
    BICUBIC         = 7,
    /// Cubic interpolation with Catmull-Rom splines
    CUBIC_SPLINE    = 8,
    /// Bicubic interpolation with Catmull-Rom splines
    BICUBIC_SPLINE  = 9
}

/// Helps determine how to pad kernels along borders
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum BorderType {
    /// Pad using zeros
    ZERO = 0,
    /// Pad using mirrored values along border
    SYMMETRIC = 1,
}

/// Used by `regions` function to identify type of connectivity
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Connectivity {
    /// North-East-South-West (N-E-S-W) connectivity from given pixel/point
    FOUR = 4,
    /// N-NE-E-SE-S-SW-W-NW connectivity from given pixel/point
    EIGHT = 8
}

/// Helps determine the size of output of convolution
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ConvMode {
    /// Default convolution mode where output size is same as input size
    DEFAULT = 0,
    /// Output of convolution is expanded based on signal and filter sizes
    EXPAND  = 1,
}

/// Helps determine if convolution is in Spatial or Frequency domain
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ConvDomain {
    /// ArrayFire chooses whether the convolution will be in spatial domain or frequency domain
    AUTO     = 0,
    /// Convoltion in spatial domain
    SPATIAL  = 1,
    /// Convolution in frequency domain
    FREQUENCY= 2,
}

/// Error metric used by `matchTemplate` function
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum MatchType {
    /// Sum of Absolute Differences
    SAD = 0,
    /// Zero-mean Sum of Absolute Differences
    ZSAD= 1,
    /// Locally scaled Sum of Absolute Differences
    LSAD= 2,
    /// Sum of Squared Differences
    SSD = 3,
    /// Zero-mean Sum of Squared Differences
    ZSSD= 4,
    /// Localy scaled Sum of Squared Differences
    LSSD= 5,
    /// Normalized Cross Correlation
    NCC = 6,
    /// Zero-mean Normalized Cross Correlation
    ZNCC= 7,
    /// Sum of Hamming Distances
    SHD = 8,
}

/// Identify the color space of given image(Array)
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ColorSpace {
    /// Grayscale color space
    GRAY = 0,
    /// Red-Green-Blue color space
    RGB  = 1,
    /// Hue-Saturation-value color space
    HSV  = 2,
}

/// Helps determine the type of a Matrix
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum MatProp {
    /// Default (no-op)
    NONE,
    /// Data needs to be transposed
    TRANS,
    /// Data needs to be conjugate transposed
    CTRANS,
    /// Matrix is upper triangular
    UPPER,
    /// Matrix is lower triangular
    LOWER,
    /// Matrix diagonal has unitary values
    DIAGUNIT,
    /// Matrix is symmetric
    SYM,
    /// Matrix is positive definite
    POSDEF,
    /// Matrix is orthogonal
    ORTHOG,
    /// Matrix is tri-diagonal
    TRIDIAG,
    /// Matrix is block-diagonal
    BLOCKDIAG,
}

/// Norm type
#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum NormType {
    /// Treats input as a vector and return sum of absolute values
    VECTOR_1    = 0,
    /// Treats input as vector and return max of absolute values
    VECTOR_INF  = 1,
    /// Treats input as vector and returns euclidean norm
    VECTOR_2    = 2,
    /// Treats input as vector and returns the p-norm
    VECTOR_P    = 3,
    /// Return the max of column sums
    MATRIX_1    = 4,
    /// Return the max of row sums
    MATRIX_INF  = 5,
    /// Returns the max singular value (Currently not supported)
    MATRIX_2    = 6,
    /// Returns Lpq-norm
    MATRIX_L_PQ = 7,
}

/// Dictates what color map is used for Image rendering
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ColorMap {
    /// Default color map is grayscale range [0-1]
    DEFAULT = 0,
    /// Visible spectrum color map
    SPECTRUM= 1,
    /// Colors
    COLORS  = 2,
    /// Red hue map
    RED     = 3,
    /// Mood color map
    MOOD    = 4,
    /// Heat color map
    HEAT    = 5,
    /// Blue hue map
    BLUE    = 6,
}

/// YCbCr Standards
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum YCCStd {
    /// ITU-R BT.601 (formerly CCIR 601) standard
    YCC_601 = 601,
    /// ITU-R BT.709 standard
    YCC_709 = 709,
    /// ITU-R BT.2020 standard
    YCC_2020 = 2020,
}

/// Homography type
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum HomographyType {
    /// RANdom SAmple Consensus algorithm
    RANSAC = 0,
    /// Least Median of Squares
    LMEDS  = 1,
}

/// Plotting markers
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum MarkerType {
    NONE     = 0,
    POINT    = 1,
    CIRCLE   = 2,
    SQUARE   = 3,
    TRIANGLE = 4,
    CROSS    = 5,
    PLUS     = 6,
    STAR     = 7
}

/// Image moment types
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum MomentType {
    M00 = 1,    // 1<<0
    M01 = 2,    // 1<<1
    M10 = 4,    // 1<<2
    M11 = 8,    // 1<<3
    FIRST_ORDER = 1<<0 | 1<<1 | 1<<2 | 1<<3
}

/// Sparse storage format type
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum SparseFormat {
    /// Dense format
    DENSE = 0,
    /// Compressed sparse row format
    CSR   = 1,
    /// Compressed sparse coloumn format
    CSC   = 2,
    /// Coordinate list (row, coloumn, value) tuples.
    COO   = 3
}

/// Binary operation types for generalized scan functions
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum BinaryOp {
    ADD = 0,
    MUL = 1,
    MIN = 2,
    MAX = 3
}

/// Random engine types
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum RandomEngineType {
    ///Philox variant with N=4, W=32 and Rounds=10
    PHILOX_4X32_10      = 100,
    ///Threefry variant with N=2, W=32 and Rounds=16
    THREEFRY_2X32_16    = 200,
    ///Mersenne variant with MEXP = 11213
    MERSENNE_GP11213    = 300
}

pub const PHILOX   : RandomEngineType = RandomEngineType::PHILOX_4X32_10;
pub const THREEFRY : RandomEngineType = RandomEngineType::THREEFRY_2X32_16;
pub const MERSENNE : RandomEngineType = RandomEngineType::MERSENNE_GP11213;
pub const DEFAULT_RANDOM_ENGINE : RandomEngineType = PHILOX;

/// Scalar value types
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Scalar {
    /// 32 bit float
    F32(f32),
    /// 32 bit complex float
    C32(Complex<f32>),
    /// 64 bit float
    F64(f64),
    /// 64 bit complex float
    C64(Complex<f64>),
    /// 8 bit boolean
    B8(bool),
    /// 32 bit signed integer
    S32(i32),
    /// 32 bit unsigned integer
    U32(u32),
    /// 8 bit unsigned integer
    U8(u8),
    /// 64 bit signed integer
    S64(i64),
    /// 64 bit unsigned integer
    U64(u64),
    /// 16 bit signed integer
    S16(i16),
    /// 16 bit unsigned integer
    U16(u16),
}
<|endoftext|>