<commit_before>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

//! This module contains the `Cleaner` traits and various implementations of it.

use std::borrow::Cow;

/// Custom function because we don't really want to touch \t or \n
fn is_whitespace(c: char) -> bool {
    c == ' ' || c == ' ' || c == ' '
}

/// Trait for cleaning a string.
///
/// This trait must be called for text that is e.g. in a paragraph, a title,
/// NOT for code blocks, hyperlinks and so on!
pub trait Cleaner: Sync {
    /// Cleans a string. The default implementation is to remove multiple consecutive whitespaces
    ///
    /// # Argumets
    ///
    /// * `str`: the string that must be cleaned
    /// * `latex`: a bool specifying whether output is Latex code or not
    fn clean<'a>(&self, str: Cow<'a, str>, _latex: bool) -> Cow<'a, str> {
        str
    }
}

/// Cleaner implementation that does nothing
///
/// # Examples
///
/// ```
/// use crowbook::cleaner::Cleaner;
/// use crowbook::cleaner::Off;
/// use std::borrow::Cow;
/// let off = Off;
/// let s = off.clean(Cow::Borrowed("  A string   that won't be cleaned "), false);
/// assert_eq!(&s, "  A string   that won't be cleaned ");
/// ```
pub struct Off;
impl Cleaner for Off {}

/// Default implementation of cleaner trait.
///
/// Only removes unnecessary whitespaces.
///
/// # Examples
///
/// ```
/// use crowbook::cleaner::Cleaner;
/// use crowbook::cleaner::Default;
/// use std::borrow::Cow;
/// let s = Default.clean(Cow::Borrowed("  A  string   with   more   whitespaces  than  needed   "), false);
/// assert_eq!(&s, " A string with more whitespaces than needed ");
/// ```
pub struct Default;
impl Cleaner for Default {
    /// Remove unnecessary whitespaces
    fn clean<'a>(&self, s: Cow<'a, str>, _: bool) -> Cow<'a, str> {
        let mut new_s = String::with_capacity(s.len());
        let mut previous_space = false;
        for c in s.chars() {
            if is_whitespace(c) {
                if previous_space {
                    // previous char already a space, don't copy it
                } else {
                    new_s.push(c);
                    previous_space = true;
                }
            } else {
                previous_space = false;
                new_s.push(c);
            }
        }
        Cow::Owned(new_s)
    }
}

/// Implementation for french 'cleaning'
///
/// This implementation replaces spaces before some characters (e.g. `?` or `;` with non-breaking spaces
///
/// # Examples
///
/// ```
/// use crowbook::cleaner::Cleaner;
/// use crowbook::cleaner::French;
/// use std::borrow::Cow;
/// let s =  French.clean(Cow::Borrowed("  Bonjour ! Comment allez-vous   ?   "), true);
/// assert_eq!(&s, " Bonjour~! Comment allez-vous~? ");
/// ```
pub struct French;

const THRESHOLD_CURRENCY: usize = 3; // after that, assume it's not a currency
const THRESHOLD_UNIT: usize = 2; // after that, assume it's not a unit
const THRESHOLD_QUOTE: usize = 28; // after that, assume it's a dialogue
const THRESHOLD_REAL_WORD: usize = 3; // after that, can be reasonably sure it is not an abbreviation

impl Cleaner for French {
    /// Puts non breaking spaces before/after `:`, `;`, `?`, `!`, `«`, `»`, `—`
    fn clean<'a>(&self, s: Cow<'a, str>, latex: bool) -> Cow<'a, str> {
        fn is_trouble(c: char) -> bool {
            match c {
                '?'|'!'|';'|':'|'»'|'«'|'—'|'–' | '0'...'9' => true,
                _ => false
            }
        }

        let input = Default.clean(s, latex); // first pass with default impl
        // Find first character that is trouble
        let first = input.chars().position(is_trouble);
        if first.is_none() {
            return input;
        }
        let first = first.unwrap();
        let nb_char = if latex {
            '~'
        } else {
            ' ' // non breaking space
        };
        let nb_char_narrow = if latex {
            '~'
        } else {
            '\u{202F}' // narrow non breaking space
        };
        let nb_char_em = if latex {
            '~'
        } else {
            '\u{2002}' // demi em space
        };

        // Find first char `c` in slice `v` after index `n`
        fn find_next(v: &[char], c: char, n: usize) -> Option<usize> {
            for i in n..v.len() {
                if v[i] == c  {
                    return Some(i);
                }
            }
            None
        }

        // Return true if next non whitespace char in `v` after index `n` is uppercase
        fn is_next_char_uppercase(v: &[char], n: usize)-> bool {
            for i in n..v.len() {
                if v[i].is_whitespace() {
                    continue;
                }
                if v[i].is_uppercase() {
                    return true;
                }
                if v[i].is_lowercase() {
                    return false;
                }
            }
            false
        }

        // Return true(some) if a closing dash was found before what looks like the end of a sentence, None else
        fn find_closing_dash(v: &[char], n: usize) -> Option<usize> {
            let mut word = String::new();
            for j in n..v.len() {
                match v[j] {

                    '!' | '?' => if is_next_char_uppercase(v, j+1) {
                        return None;
                    },
                    '-' | '–' | '—' => if v[j-1].is_whitespace() {
                        return Some(j-1);
                    },
                    '.' => if !is_next_char_uppercase(v, j+1) {
                        continue;
                    } else {
                        if let Some(c) = word.chars().next() {
                            if !c.is_uppercase() {
                                return None;
                            } else {
                                if word.len() > THRESHOLD_REAL_WORD {
                                    return None;
                                }
                            }
                        }
                    },
                    c if c.is_whitespace() => word = String::new(),
                    c => word.push(c),
                }
            }
            return None;
        }

        /// Returns the next word in `v` starting from index `n`
        fn get_next_word(v: &[char], n: usize) -> &[char] {
            let mut beginning = n;
            let mut end = v.len();

            for i in n..v.len() {
                if v[i].is_alphabetic() {
                    beginning = i;
                    break;
                }
            }

            for i in beginning..v.len() {
                if v[i].is_whitespace() {
                    end = i-1;
                    break;
                }
            }

            &v[beginning..end]
        }

        /// Return true if the character is a symbol that is used after number and should have a nb_char before
        fn char_is_symbol(v: &[char], i: usize) -> bool {
            let is_next_letter = if i < v.len() - 1 {
                v[i+1].is_alphabetic()
            } else {
                false
            };
            if is_next_letter {
                match v[i] {
                    '°' => true,
                    c if c.is_uppercase() => {
                        let word = get_next_word(v, i);
                        if word.len() > THRESHOLD_CURRENCY {
                            // not a currency
                            false
                        } else {
                            // if all uppercase and less than THRESHOLD, assume it's a currency or a unit
                            word.iter().all(|c| c.is_uppercase())
                        }
                    },
                    c if c.is_alphabetic() => {
                        let word = get_next_word(v, i);
                        // if two letters, assume it is a unit
                        word.len() <= THRESHOLD_UNIT
                    },
                    _ => false
                }
            } else {
                match v[i] {
                    c if (!c.is_alphabetic() && !c.is_whitespace()) => true, // special symbol
                    c if c.is_uppercase() => true, //single uppercase letter
                    _ => false,
                }
            }
        }

        let mut found_opening_quote = false; // we didn't find an opening quote yet
        let mut chars = input.chars().collect::<Vec<_>>();
        let mut is_number_series = false;

        // Get back one step
        let first = if first > 1 {
            first - 1
        } else {
            0
        };

        for i in first..(chars.len()-1) {
            // Handle numbers (that's easy)
            let current = chars[i];
            let next = chars[i+1];

            match current {
                '0'...'9' => if i == 0 {
                    is_number_series = true;
                } else if !chars[i-1].is_alphabetic() {
                    is_number_series = true;
                },
                c if c.is_whitespace() => {
                    if is_number_series && (next.is_digit(10) || char_is_symbol(&chars, i+1)) {
                        // Next char is a number or symbol such as $, and previous was number
                        chars[i] = nb_char_narrow;
                    }
                },
                _ => { is_number_series = false; }
            }
        }

        for i in first..(chars.len()-1) {
            let current = chars[i];
            let next = chars[i+1];

            // Handle the rest (that's hard)
            if is_whitespace(current) {
                match next {
                    // handle narrow nb space before char
                    '?' | '!' | ';' => chars[i] = nb_char_narrow,
                    ':' => chars[i] = nb_char,
                    '»' => if current == ' ' {
                        // Assumne that if it isn't a normal space it was used here for good reason, don't replace it
                        if found_opening_quote {
                            // not the end of a dialogue
                            chars[i] = nb_char;
                        } else {
                            chars[i] = nb_char;
                        }
                    },
                    _ => (),
                }
            } else {
                match current {
                    // handle nb space after char
                    '—' | '«' | '-' | '–' => {
                        if is_whitespace(next) {
                            let replacing_char = match current {
                                '—' | '-' | '–' => {
                                    if i <= 1 {
                                        nb_char_em
                                    } else {
                                        if chars[i-1] == nb_char {
                                            // non breaking space before, so probably should have a breakable one after
                                            ' '
                                        } else {
                                            if let Some(closing) = find_closing_dash(&chars, i+1) {
                                                chars[closing] = nb_char;
                                            }
                                            nb_char
                                        }
                                    }
                                },
                                '«' => {
                                    found_opening_quote = true;
                                    if i <= 1 {
                                        nb_char
                                    } else {
                                        let j = find_next(&chars, '»', i);
                                        if let Some(j) = j {
                                            if chars[j-1].is_whitespace() {
                                                if j >= chars.len() - 1 {
                                                    // » is at the end, assume it is a dialogue
                                                    chars[j-1] = nb_char;
                                                    nb_char
                                                } else {
                                                    if j - i > THRESHOLD_QUOTE {
                                                        // It's a quote, so use large space?
                                                        chars[j-1] = nb_char;
                                                        nb_char
                                                    } else {
                                                        // Not long enough to be a quote, use narrow nb char
                                                        chars[j-1] = nb_char_narrow;
                                                        nb_char_narrow
                                                    }
                                                }
                                            } else {
                                                // wtf formatting?
                                                nb_char
                                            }
                                        } else {
                                            // No ending quote found, assume is a dialogue
                                            nb_char
                                        }
                                    }
                                }, // TODO: better heuristic: use narrow nb_char if not at front???
                                _ => unreachable!(),
                            };
                            chars[i+1] = replacing_char;
                        }
                    }
                    _ => (),
                }
            }
        }
        Cow::Owned(chars.into_iter().collect())
    }
}

<commit_msg>Separate getting of first 'suspicious' character for number and rest loops in french cleaner<commit_after>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

//! This module contains the `Cleaner` traits and various implementations of it.

use std::borrow::Cow;

/// Custom function because we don't really want to touch \t or \n
fn is_whitespace(c: char) -> bool {
    c == ' ' || c == ' ' || c == ' '
}

/// Trait for cleaning a string.
///
/// This trait must be called for text that is e.g. in a paragraph, a title,
/// NOT for code blocks, hyperlinks and so on!
pub trait Cleaner: Sync {
    /// Cleans a string. The default implementation is to remove multiple consecutive whitespaces
    ///
    /// # Argumets
    ///
    /// * `str`: the string that must be cleaned
    /// * `latex`: a bool specifying whether output is Latex code or not
    fn clean<'a>(&self, str: Cow<'a, str>, _latex: bool) -> Cow<'a, str> {
        str
    }
}

/// Cleaner implementation that does nothing
///
/// # Examples
///
/// ```
/// use crowbook::cleaner::Cleaner;
/// use crowbook::cleaner::Off;
/// use std::borrow::Cow;
/// let off = Off;
/// let s = off.clean(Cow::Borrowed("  A string   that won't be cleaned "), false);
/// assert_eq!(&s, "  A string   that won't be cleaned ");
/// ```
pub struct Off;
impl Cleaner for Off {}

/// Default implementation of cleaner trait.
///
/// Only removes unnecessary whitespaces.
///
/// # Examples
///
/// ```
/// use crowbook::cleaner::Cleaner;
/// use crowbook::cleaner::Default;
/// use std::borrow::Cow;
/// let s = Default.clean(Cow::Borrowed("  A  string   with   more   whitespaces  than  needed   "), false);
/// assert_eq!(&s, " A string with more whitespaces than needed ");
/// ```
pub struct Default;
impl Cleaner for Default {
    /// Remove unnecessary whitespaces
    fn clean<'a>(&self, s: Cow<'a, str>, _: bool) -> Cow<'a, str> {
        let mut new_s = String::with_capacity(s.len());
        let mut previous_space = false;
        for c in s.chars() {
            if is_whitespace(c) {
                if previous_space {
                    // previous char already a space, don't copy it
                } else {
                    new_s.push(c);
                    previous_space = true;
                }
            } else {
                previous_space = false;
                new_s.push(c);
            }
        }
        Cow::Owned(new_s)
    }
}

/// Implementation for french 'cleaning'
///
/// This implementation replaces spaces before some characters (e.g. `?` or `;` with non-breaking spaces
///
/// # Examples
///
/// ```
/// use crowbook::cleaner::Cleaner;
/// use crowbook::cleaner::French;
/// use std::borrow::Cow;
/// let s =  French.clean(Cow::Borrowed("  Bonjour ! Comment allez-vous   ?   "), true);
/// assert_eq!(&s, " Bonjour~! Comment allez-vous~? ");
/// ```
pub struct French;

const THRESHOLD_CURRENCY: usize = 3; // after that, assume it's not a currency
const THRESHOLD_UNIT: usize = 2; // after that, assume it's not a unit
const THRESHOLD_QUOTE: usize = 28; // after that, assume it's a dialogue
const THRESHOLD_REAL_WORD: usize = 3; // after that, can be reasonably sure it is not an abbreviation

impl Cleaner for French {
    /// Puts non breaking spaces before/after `:`, `;`, `?`, `!`, `«`, `»`, `—`
    fn clean<'a>(&self, s: Cow<'a, str>, latex: bool) -> Cow<'a, str> {
        fn is_trouble(c: char) -> bool {
            match c {
                '?'|'!'|';'|':'|'»'|'«'|'—'|'–' => true,
                _ => false
            }
        }

        let input = Default.clean(s, latex); // first pass with default impl
        // Find first character that is trouble
        let first = input.chars().position(is_trouble);
        let first_number = input.chars().position(|c| c.is_digit(10));
        if first.is_none() && first_number.is_none() {
            return input;
        }

        let nb_char = if latex {
            '~'
        } else {
            ' ' // non breaking space
        };
        let nb_char_narrow = if latex {
            '~'
        } else {
            '\u{202F}' // narrow non breaking space
        };
        let nb_char_em = if latex {
            '~'
        } else {
            '\u{2002}' // demi em space
        };

        // Find first char `c` in slice `v` after index `n`
        fn find_next(v: &[char], c: char, n: usize) -> Option<usize> {
            for i in n..v.len() {
                if v[i] == c  {
                    return Some(i);
                }
            }
            None
        }

        // Return true if next non whitespace char in `v` after index `n` is uppercase
        fn is_next_char_uppercase(v: &[char], n: usize)-> bool {
            for i in n..v.len() {
                if v[i].is_whitespace() {
                    continue;
                }
                if v[i].is_uppercase() {
                    return true;
                }
                if v[i].is_lowercase() {
                    return false;
                }
            }
            false
        }

        // Return true(some) if a closing dash was found before what looks like the end of a sentence, None else
        fn find_closing_dash(v: &[char], n: usize) -> Option<usize> {
            let mut word = String::new();
            for j in n..v.len() {
                match v[j] {

                    '!' | '?' => if is_next_char_uppercase(v, j+1) {
                        return None;
                    },
                    '-' | '–' | '—' => if v[j-1].is_whitespace() {
                        return Some(j-1);
                    },
                    '.' => if !is_next_char_uppercase(v, j+1) {
                        continue;
                    } else {
                        if let Some(c) = word.chars().next() {
                            if !c.is_uppercase() {
                                return None;
                            } else {
                                if word.len() > THRESHOLD_REAL_WORD {
                                    return None;
                                }
                            }
                        }
                    },
                    c if c.is_whitespace() => word = String::new(),
                    c => word.push(c),
                }
            }
            return None;
        }

        /// Returns the next word in `v` starting from index `n`
        fn get_next_word(v: &[char], n: usize) -> &[char] {
            let mut beginning = n;
            let mut end = v.len();

            for i in n..v.len() {
                if v[i].is_alphabetic() {
                    beginning = i;
                    break;
                }
            }

            for i in beginning..v.len() {
                if v[i].is_whitespace() {
                    end = i-1;
                    break;
                }
            }

            &v[beginning..end]
        }

        /// Return true if the character is a symbol that is used after number and should have a nb_char before
        fn char_is_symbol(v: &[char], i: usize) -> bool {
            let is_next_letter = if i < v.len() - 1 {
                v[i+1].is_alphabetic()
            } else {
                false
            };
            if is_next_letter {
                match v[i] {
                    '°' => true,
                    c if c.is_uppercase() => {
                        let word = get_next_word(v, i);
                        if word.len() > THRESHOLD_CURRENCY {
                            // not a currency
                            false
                        } else {
                            // if all uppercase and less than THRESHOLD, assume it's a currency or a unit
                            word.iter().all(|c| c.is_uppercase())
                        }
                    },
                    c if c.is_alphabetic() => {
                        let word = get_next_word(v, i);
                        // if two letters, assume it is a unit
                        word.len() <= THRESHOLD_UNIT
                    },
                    _ => false
                }
            } else {
                match v[i] {
                    c if (!c.is_alphabetic() && !c.is_whitespace()) => true, // special symbol
                    c if c.is_uppercase() => true, //single uppercase letter
                    _ => false,
                }
            }
        }

        let mut found_opening_quote = false; // we didn't find an opening quote yet
        let mut chars = input.chars().collect::<Vec<_>>();
        let mut is_number_series = false;

        if let Some(first) = first_number {
            // Go back one step
            let first = if first > 1 {
                first - 1
            } else {
                0
            };
            for i in first..(chars.len()-1) {
                // Handle numbers (that's easy)
                let current = chars[i];
                let next = chars[i+1];

                match current {
                    '0'...'9' => if i == 0 {
                        is_number_series = true;
                    } else if !chars[i-1].is_alphabetic() {
                        is_number_series = true;
                    },
                    c if c.is_whitespace() => {
                        if is_number_series && (next.is_digit(10) || char_is_symbol(&chars, i+1)) {
                            // Next char is a number or symbol such as $, and previous was number
                            chars[i] = nb_char_narrow;
                        }
                    },
                    _ => { is_number_series = false; }
                }
            }
        }

        if let Some(first) = first {
            // Go back one step
            let first = if first > 1 {
                first - 1
            } else {
                0
            };
            for i in first..(chars.len()-1) {
                let current = chars[i];
                let next = chars[i+1];

                // Handle the rest (that's hard)
                if is_whitespace(current) {
                    match next {
                        // handle narrow nb space before char
                        '?' | '!' | ';' => chars[i] = nb_char_narrow,
                        ':' => chars[i] = nb_char,
                        '»' => if current == ' ' {
                            // Assumne that if it isn't a normal space it was used here for good reason, don't replace it
                            if found_opening_quote {
                                // not the end of a dialogue
                                chars[i] = nb_char;
                            } else {
                                chars[i] = nb_char;
                            }
                        },
                        _ => (),
                    }
                } else {
                    match current {
                        // handle nb space after char
                        '—' | '«' | '-' | '–' => {
                            if is_whitespace(next) {
                                let replacing_char = match current {
                                    '—' | '-' | '–' => {
                                        if i <= 1 {
                                            nb_char_em
                                        } else {
                                            if chars[i-1] == nb_char {
                                                // non breaking space before, so probably should have a breakable one after
                                                ' '
                                            } else {
                                                if let Some(closing) = find_closing_dash(&chars, i+1) {
                                                    chars[closing] = nb_char;
                                                }
                                                nb_char
                                            }
                                        }
                                    },
                                    '«' => {
                                        found_opening_quote = true;
                                        if i <= 1 {
                                            nb_char
                                        } else {
                                            let j = find_next(&chars, '»', i);
                                            if let Some(j) = j {
                                            if chars[j-1].is_whitespace() {
                                                if j >= chars.len() - 1 {
                                                    // » is at the end, assume it is a dialogue
                                                    chars[j-1] = nb_char;
                                                    nb_char
                                                } else {
                                                    if j - i > THRESHOLD_QUOTE {
                                                        // It's a quote, so use large space?
                                                        chars[j-1] = nb_char;
                                                        nb_char
                                                    } else {
                                                        // Not long enough to be a quote, use narrow nb char
                                                        chars[j-1] = nb_char_narrow;
                                                        nb_char_narrow
                                                    }
                                                }
                                            } else {
                                                // wtf formatting?
                                                nb_char
                                            }
                                        } else {
                                                // No ending quote found, assume is a dialogue
                                                nb_char
                                            }
                                        }
                                    }, // TODO: better heuristic: use narrow nb_char if not at front???
                                    _ => unreachable!(),
                                };
                                chars[i+1] = replacing_char;
                            }
                        }
                        _ => (),
                    }
                }
            }
        }
        Cow::Owned(chars.into_iter().collect())
    }
}

<|endoftext|>


<commit_before>#![crate_name = "neovim"]
#![crate_type = "lib"]
#![crate_type = "rlib"]
#![allow(raw_pointer_deriving)]

extern crate libc;

use std::c_str::CString;
use std::string::String;
use std::vec::Vec;

#[cfg(target_os="macos")]
mod platform {
    #[link(name = "nvim")]
    #[link(name = "uv")]
    #[link(name = "msgpack")]
    #[link(name = "curses")]
    #[link(name = "iconv")]
    extern{}
}

#[cfg(target_os="linux")]
mod platform {
    #[link(name = "nvim")]
    #[link(name = "uv")]
    #[link(name = "msgpack")]
    #[link(name = "curses")]
    extern{}
}

#[cfg(target_os="windows")]
mod platform {
    #[link(name = "nvim")]
    #[link(name = "uv")]
    #[link(name = "msgpack")]
    #[link(name = "curses")]
    extern{}
}

mod ffi {
    pub use libc::{c_char, c_double, c_int, int64_t, uint64_t};
    pub use libc::types::os::arch::c95::size_t;

    #[repr(C)]
    #[deriving(Copy)]
    pub struct C_msgpack_sbuffer {
        pub size: size_t,
        pub data: *const c_char,
        pub alloc: size_t,
    }
    #[repr(C)]
    #[deriving(Copy)]
    pub struct C_Object;
    #[repr(C)]
    #[deriving(Copy)]
    pub struct C_KeyValuePair;
    #[repr(C)]
    #[deriving(Copy)]
    pub struct C_String {
        pub data: *const c_char,
        pub size: size_t,
    }
    #[repr(C)]
    #[deriving(Copy)]
    pub struct C_Array {
        pub items: *mut C_Object,
        pub size: size_t,
        pub capacity: size_t,
    }
    #[repr(C)]
    #[deriving(Copy)]
    pub struct C_Dictionary {
        pub items: *mut C_KeyValuePair,
        pub size: size_t,
        pub capacity: size_t,
    }

    pub type Buffer = uint64_t;
    pub type Window = uint64_t;
    pub type Tabpage = uint64_t;
    pub type Integer = int64_t;
    pub type Boolean = bool;
    pub type Float = c_double;

    extern "C" {
        pub fn nvim_main (argc: c_int, argv: *const *const c_char,
                          read_fd: c_int, write_fd: c_int) -> c_int;
        pub fn vim_array_new () -> *mut C_Array;
        pub fn vim_array_free (arr: *mut C_Array);
        pub fn vim_array_add_buffer (val: Buffer, arr: *mut C_Array);
        pub fn vim_array_add_window (val: Window, arr: *mut C_Array);
        pub fn vim_array_add_tabpage (val: Tabpage, arr: *mut C_Array);
        pub fn vim_array_add_nil (arr: *mut C_Array);
        pub fn vim_array_add_boolean (val: Boolean, arr: *mut C_Array);
        pub fn vim_array_add_integer (val: Integer, arr: *mut C_Array);
        pub fn vim_array_add_float (val: Float, arr: *mut C_Array);
        pub fn vim_array_add_string (val: C_String, arr: *mut C_Array);
        pub fn vim_array_add_array (val: C_Array, arr: *mut C_Array);
        pub fn vim_array_add_dictionary (val: C_Dictionary, arr: *mut C_Array);
        pub fn vim_msgpack_new () -> *mut C_msgpack_sbuffer;
        pub fn vim_msgpack_free (buf: *mut C_msgpack_sbuffer);
        pub fn vim_serialize_request (request_id: uint64_t, method: C_String, args: C_Array, buf: *mut C_msgpack_sbuffer);
    }
}

#[deriving(Copy)]
pub struct Array {
    pointer: *mut ffi::C_Array
}

impl Array {
    pub fn new() -> Array {
        Array {
            pointer: ::std::ptr::null_mut()
        }
    }

    pub fn add_buffer(&mut self, val: ffi::Buffer) {
        self.check_pointer();
        unsafe { ffi::vim_array_add_buffer(val, self.pointer) }
    }

    pub fn add_window(&mut self, val: ffi::Window) {
        self.check_pointer();
        unsafe { ffi::vim_array_add_window(val, self.pointer) }
    }

    pub fn add_tabpage(&mut self, val: ffi::Tabpage) {
        self.check_pointer();
        unsafe { ffi::vim_array_add_tabpage(val, self.pointer) }
    }

    pub fn add_nil(&mut self) {
        self.check_pointer();
        unsafe { ffi::vim_array_add_nil(self.pointer) }
    }

    pub fn add_boolean(&mut self, val: ffi::Boolean) {
        self.check_pointer();
        unsafe { ffi::vim_array_add_boolean(val, self.pointer) }
    }

    pub fn add_integer(&mut self, val: ffi::Integer) {
        self.check_pointer();
        unsafe { ffi::vim_array_add_integer(val, self.pointer) }
    }

    pub fn add_float(&mut self, val: ffi::Float) {
        self.check_pointer();
        unsafe { ffi::vim_array_add_float(val, self.pointer) }
    }

    pub fn add_string(&mut self, val: &str) {
        self.check_pointer();
        unsafe {
            ffi::vim_array_add_string(ffi::C_String {data: val.to_c_str().as_ptr(), size: val.len() as u64}, self.pointer)
        }
    }

    pub fn add_array(&mut self, val: Array) {
        self.check_pointer();
        unsafe { ffi::vim_array_add_array(*val.get_pointer(), self.pointer) }
    }

    pub fn add_dictionary(&mut self, val: ffi::C_Dictionary) {
        self.check_pointer();
        unsafe { ffi::vim_array_add_dictionary(val, self.pointer) }
    }

    pub fn serialize_request(&mut self, id: u64, method: &'static str) -> String {
        unsafe {
            let buf = ffi::vim_msgpack_new();
            ffi::vim_serialize_request(id, ffi::C_String {data: method.to_c_str().as_ptr(), size: method.len() as u64}, *self.pointer, buf);
            let s = String::from_raw_buf_len((*buf).data as *const u8, (*buf).size as uint);
            ffi::vim_msgpack_free(buf);
            s
        }
    }

    pub fn clear(&mut self) {
        if !self.pointer.is_null() {
            unsafe { ffi::vim_array_free(self.pointer) };
        }
        self.pointer = ::std::ptr::null_mut();
    }

    pub fn len(&self) -> u64 {
        if !self.pointer.is_null() {
            unsafe { (*self.pointer).size }
        } else {
            0
        }
    }

    #[doc(hidden)]
    pub fn get_pointer(&self) -> *mut ffi::C_Array {
        self.pointer
    }

    #[doc(hidden)]
    pub fn wrap_pointer(c_array: *mut ffi::C_Array) -> Array {
        Array {
            pointer: c_array
        }
    }

    fn check_pointer(&mut self) {
        if self.pointer.is_null() {
            self.pointer = unsafe { ffi::vim_array_new() };
        }
    }
}

impl Drop for Array {
    fn drop(&mut self) {
        self.clear();
    }
}

#[test]
fn test_request() {
    let mut a = Array::new();
    a.add_integer(80);
    a.add_integer(24);
    println!("{}", a.serialize_request(1, "attach_ui"));
}

pub fn run_with_fds(args: Vec<String>, read_fd: i32, write_fd: i32) -> i32 {
    let v: Vec<CString> = args.iter().map(|s| s.as_slice().to_c_str()).collect();
    let vp: Vec<*const ffi::c_char> = v.iter().map(|s| s.as_ptr()).collect();
    let p_vp: *const *const ffi::c_char = vp.as_ptr();

    unsafe { ffi::nvim_main(vp.len() as i32, p_vp, read_fd, write_fd) }
}

pub fn run(args: Vec<String>) -> i32 {
    run_with_fds(args, -1, -1)
}
<commit_msg>Move run functions above Array code<commit_after>#![crate_name = "neovim"]
#![crate_type = "lib"]
#![crate_type = "rlib"]
#![allow(raw_pointer_deriving)]

extern crate libc;

use std::c_str::CString;
use std::string::String;
use std::vec::Vec;

#[cfg(target_os="macos")]
mod platform {
    #[link(name = "nvim")]
    #[link(name = "uv")]
    #[link(name = "msgpack")]
    #[link(name = "curses")]
    #[link(name = "iconv")]
    extern{}
}

#[cfg(target_os="linux")]
mod platform {
    #[link(name = "nvim")]
    #[link(name = "uv")]
    #[link(name = "msgpack")]
    #[link(name = "curses")]
    extern{}
}

#[cfg(target_os="windows")]
mod platform {
    #[link(name = "nvim")]
    #[link(name = "uv")]
    #[link(name = "msgpack")]
    #[link(name = "curses")]
    extern{}
}

mod ffi {
    pub use libc::{c_char, c_double, c_int, int64_t, uint64_t};
    pub use libc::types::os::arch::c95::size_t;

    #[repr(C)]
    #[deriving(Copy)]
    pub struct C_msgpack_sbuffer {
        pub size: size_t,
        pub data: *const c_char,
        pub alloc: size_t,
    }
    #[repr(C)]
    #[deriving(Copy)]
    pub struct C_Object;
    #[repr(C)]
    #[deriving(Copy)]
    pub struct C_KeyValuePair;
    #[repr(C)]
    #[deriving(Copy)]
    pub struct C_String {
        pub data: *const c_char,
        pub size: size_t,
    }
    #[repr(C)]
    #[deriving(Copy)]
    pub struct C_Array {
        pub items: *mut C_Object,
        pub size: size_t,
        pub capacity: size_t,
    }
    #[repr(C)]
    #[deriving(Copy)]
    pub struct C_Dictionary {
        pub items: *mut C_KeyValuePair,
        pub size: size_t,
        pub capacity: size_t,
    }

    pub type Buffer = uint64_t;
    pub type Window = uint64_t;
    pub type Tabpage = uint64_t;
    pub type Integer = int64_t;
    pub type Boolean = bool;
    pub type Float = c_double;

    extern "C" {
        pub fn nvim_main (argc: c_int, argv: *const *const c_char,
                          read_fd: c_int, write_fd: c_int) -> c_int;
        pub fn vim_array_new () -> *mut C_Array;
        pub fn vim_array_free (arr: *mut C_Array);
        pub fn vim_array_add_buffer (val: Buffer, arr: *mut C_Array);
        pub fn vim_array_add_window (val: Window, arr: *mut C_Array);
        pub fn vim_array_add_tabpage (val: Tabpage, arr: *mut C_Array);
        pub fn vim_array_add_nil (arr: *mut C_Array);
        pub fn vim_array_add_boolean (val: Boolean, arr: *mut C_Array);
        pub fn vim_array_add_integer (val: Integer, arr: *mut C_Array);
        pub fn vim_array_add_float (val: Float, arr: *mut C_Array);
        pub fn vim_array_add_string (val: C_String, arr: *mut C_Array);
        pub fn vim_array_add_array (val: C_Array, arr: *mut C_Array);
        pub fn vim_array_add_dictionary (val: C_Dictionary, arr: *mut C_Array);
        pub fn vim_msgpack_new () -> *mut C_msgpack_sbuffer;
        pub fn vim_msgpack_free (buf: *mut C_msgpack_sbuffer);
        pub fn vim_serialize_request (request_id: uint64_t, method: C_String, args: C_Array, buf: *mut C_msgpack_sbuffer);
    }
}

pub fn run_with_fds(args: Vec<String>, read_fd: i32, write_fd: i32) -> i32 {
    let v: Vec<CString> = args.iter().map(|s| s.as_slice().to_c_str()).collect();
    let vp: Vec<*const ffi::c_char> = v.iter().map(|s| s.as_ptr()).collect();
    let p_vp: *const *const ffi::c_char = vp.as_ptr();

    unsafe { ffi::nvim_main(vp.len() as i32, p_vp, read_fd, write_fd) }
}

pub fn run(args: Vec<String>) -> i32 {
    run_with_fds(args, -1, -1)
}

#[deriving(Copy)]
pub struct Array {
    pointer: *mut ffi::C_Array
}

impl Array {
    pub fn new() -> Array {
        Array {
            pointer: ::std::ptr::null_mut()
        }
    }

    pub fn add_buffer(&mut self, val: ffi::Buffer) {
        self.check_pointer();
        unsafe { ffi::vim_array_add_buffer(val, self.pointer) }
    }

    pub fn add_window(&mut self, val: ffi::Window) {
        self.check_pointer();
        unsafe { ffi::vim_array_add_window(val, self.pointer) }
    }

    pub fn add_tabpage(&mut self, val: ffi::Tabpage) {
        self.check_pointer();
        unsafe { ffi::vim_array_add_tabpage(val, self.pointer) }
    }

    pub fn add_nil(&mut self) {
        self.check_pointer();
        unsafe { ffi::vim_array_add_nil(self.pointer) }
    }

    pub fn add_boolean(&mut self, val: ffi::Boolean) {
        self.check_pointer();
        unsafe { ffi::vim_array_add_boolean(val, self.pointer) }
    }

    pub fn add_integer(&mut self, val: ffi::Integer) {
        self.check_pointer();
        unsafe { ffi::vim_array_add_integer(val, self.pointer) }
    }

    pub fn add_float(&mut self, val: ffi::Float) {
        self.check_pointer();
        unsafe { ffi::vim_array_add_float(val, self.pointer) }
    }

    pub fn add_string(&mut self, val: &str) {
        self.check_pointer();
        unsafe {
            ffi::vim_array_add_string(ffi::C_String {data: val.to_c_str().as_ptr(), size: val.len() as u64}, self.pointer)
        }
    }

    pub fn add_array(&mut self, val: Array) {
        self.check_pointer();
        unsafe { ffi::vim_array_add_array(*val.get_pointer(), self.pointer) }
    }

    pub fn add_dictionary(&mut self, val: ffi::C_Dictionary) {
        self.check_pointer();
        unsafe { ffi::vim_array_add_dictionary(val, self.pointer) }
    }

    pub fn serialize_request(&mut self, id: u64, method: &'static str) -> String {
        unsafe {
            let buf = ffi::vim_msgpack_new();
            ffi::vim_serialize_request(id, ffi::C_String {data: method.to_c_str().as_ptr(), size: method.len() as u64}, *self.pointer, buf);
            let s = String::from_raw_buf_len((*buf).data as *const u8, (*buf).size as uint);
            ffi::vim_msgpack_free(buf);
            s
        }
    }

    pub fn clear(&mut self) {
        if !self.pointer.is_null() {
            unsafe { ffi::vim_array_free(self.pointer) };
        }
        self.pointer = ::std::ptr::null_mut();
    }

    pub fn len(&self) -> u64 {
        if !self.pointer.is_null() {
            unsafe { (*self.pointer).size }
        } else {
            0
        }
    }

    #[doc(hidden)]
    pub fn get_pointer(&self) -> *mut ffi::C_Array {
        self.pointer
    }

    #[doc(hidden)]
    pub fn wrap_pointer(c_array: *mut ffi::C_Array) -> Array {
        Array {
            pointer: c_array
        }
    }

    fn check_pointer(&mut self) {
        if self.pointer.is_null() {
            self.pointer = unsafe { ffi::vim_array_new() };
        }
    }
}

impl Drop for Array {
    fn drop(&mut self) {
        self.clear();
    }
}

#[test]
fn test_request() {
    let mut a = Array::new();
    a.add_integer(80);
    a.add_integer(24);
    println!("{}", a.serialize_request(1, "attach_ui"));
}
<|endoftext|>


<commit_before>#![warn(missing_docs)]

//! rust-vst2 is a rust implementation of the VST2.4 API
//!
//! # Plugins
//! All Plugins must implement the `Vst` trait and `std::default::Default`. The `vst_main!` macro
//! must also be called in order to export the necessary functions for the VST to function.
//!
//! ## `Vst` Trait
//! All methods in this trait have a default implementation except for the `get_info` method which
//! must be implemented by the Vst object. Any of the default implementations may be overriden for
//! custom functionality; the defaults do nothing on their own.
//!
//! ## `vst_main!` macro
//! `vst_main!` will export the necessary functions to create a proper VST. This must be called
//! with your VST struct name in order for the vst to work.
//!
//! ## Example plugin
//! A barebones VST plugin:
//!
//! ```no_run
//! #[macro_use]
//! extern crate vst2;
//!
//! use vst2::{Vst, Info};
//!
//! #[derive(Default)]
//! struct BasicVst;
//!
//! impl Vst for BasicVst {
//!     fn get_info(&self) -> Info {
//!         Info {
//!             name: "BasicVst".to_string(),
//!
//!             ..Default::default()
//!         }
//!     }
//! }
//!
//! vst_main!(BasicVst); //Important!
//! # fn main() {} //no_run
//! ```
//!
//! # Hosts
//! Hosts are currently not supported. TODO

extern crate libc;
extern crate num;
#[macro_use] extern crate log;
#[macro_use] extern crate bitflags;

use std::default::Default;
use std::{ptr, mem};
use std::iter::IntoIterator;

use libc::c_void;

pub mod buffer;
pub mod enums;
pub mod api;
pub mod editor;
pub mod channels;
mod interfaces;

use enums::flags::plugin::*;
use enums::{PluginCategory, CanDo, Supported};
use api::{HostCallback, AEffect};
pub use buffer::AudioBuffer;
use editor::Editor;
use channels::ChannelInfo;

/// VST plugins are identified by a magic number. This corresponds to 0x56737450.
pub const VST_MAGIC: i32 = ('V' as i32) << 24 |
                           ('s' as i32) << 16 |
                           ('t' as i32) << 8  |
                           ('P' as i32) << 0  ;

/// Exports the necessary symbols for the plugin to be used by a vst host.
///
/// This macro takes a type which must implement the traits `Vst` and `std::default::Default`.
#[macro_export]
macro_rules! vst_main {
    ($t:ty) => {
        #[cfg(target_os = "mac")]
        pub extern "system" fn main_macho (callback: $crate::api::HostCallback) -> *mut $crate::api::AEffect {
            VSTPluginMain(callback)
        }

        #[cfg(target_os = "windows")]
        pub extern "system" fn MAIN (callback: $crate::api::HostCallback) -> *mut $crate::api::AEffect {
            VSTPluginMain(callback)
        }

        #[allow(non_snake_case, unused_variables)]
        #[no_mangle]
        pub extern "system" fn VSTPluginMain(callback: $crate::api::HostCallback) -> *mut $crate::api::AEffect {
            $crate::main::<$t>(callback)
        }
    }
}

/// Initializes a VST plugin and returns a raw pointer to an AEffect struct.
#[doc(hidden)]
pub fn main<T: Vst + Default>(callback: HostCallback) -> *mut AEffect {
    // 1 is audioMasterVersion TODO: abstract this
    if callback(ptr::null_mut(), 1, 0, 0, ptr::null_mut(), 0.0) == 0 {
        return ptr::null_mut();
    }

    let mut vst: T = Default::default();
    let info = vst.get_info().clone();
    trace!("Creating VST plugin instance...");

    unsafe { mem::transmute(Box::new(AEffect {
        magic: VST_MAGIC,
        dispatcher: interfaces::dispatch, // fn pointer

        _process: interfaces::process_deprecated, // fn pointer

        setParameter: interfaces::set_parameter, // fn pointer
        getParameter: interfaces::get_parameter, // fn pointer

        numPrograms: info.presets,
        numParams: info.parameters,
        numInputs: info.inputs,
        numOutputs: info.outputs,

        flags: {
            let mut flag = CAN_REPLACING;

            if info.f64_precision {
                flag = flag | CAN_DOUBLE_REPLACING;
            }

            if vst.get_editor().is_some() {
                flag = flag | HAS_EDITOR;
            }

            if info.preset_chunks {
                flag = flag | PROGRAM_CHUNKS;
            }

            if let PluginCategory::Synth = info.category {
                flag = flag | IS_SYNTH;
            }

            flag.bits()
        },

        reserved1: 0,
        reserved2: 0,

        initialDelay: 0,

        _realQualities: 0,
        _offQualities: 0,
        _ioRatio: 0.0,

        object: mem::transmute(Box::new(Box::new(vst) as Box<Vst>)),
        user: ptr::null_mut(),

        uniqueId: 0,
        version: 0001,

        processReplacing: interfaces::process_replacing, // fn pointer
        processReplacingF64: interfaces::process_replacing_f64, //fn pointer

        future: [0u8; 56]
    })) }
}

/// A structure representing static plugin information.
#[derive(Clone, Debug)]
pub struct Info {
    /// Plugin Name.
    pub name: String,

    /// Plugin Vendor.
    pub vendor: String,

    /// Number of different presets.
    pub presets: i32,

    /// Number of parameters.
    pub parameters: i32,

    /// Number of inputs.
    pub inputs: i32,

    /// Number of outputs.
    pub outputs: i32,

    /// Unique plugin ID. Can be registered with Steinberg to prevent conflicts with other plugins.
    ///
    /// This ID is used to identify a plugin during save and load of a preset and project.
    pub unique_id: i32,

    /// Plugin version (e.g. 0001 = `v0.0.0.1`, 1283 = `v1.2.8.3`).
    pub version: i32,

    /// Plugin category. Possible values are found in `enums::PluginCategory`.
    pub category: PluginCategory,

    //TODO: Doc
    pub initial_delay: i32,

    /// Indicates whether preset data is handled in formatless chunks. If false,
    /// host saves and restores plugins by reading/writing parameter data. If true, it is up to
    /// the plugin to manage saving preset data by implementing  the
    /// `{get, load}_{preset, bank}_chunks()` methods. Default is `false`.
    pub preset_chunks: bool,

    /// Indicates whether this plugin can process f64 based `AudioBuffer` buffers. Default is
    /// `false`.
    pub f64_precision: bool,

    //no_sound_in_stop: bool, //TODO: Implement this somehow
}

impl Default for Info {
    fn default() -> Info {
        Info {
            name: "VST".to_string(),
            vendor: String::new(),

            presets: 1, // default preset
            parameters: 0,
            inputs: 2, // Stereo in,out
            outputs: 2,

            unique_id: 0,
            version: 0001, // v0.0.0.1

            category: PluginCategory::Effect,

            initial_delay: 0,

            preset_chunks: false,
            f64_precision: false,
        }
    }
}

/// Must be implemented by all VST plugins.
///
/// All methods except `get_info` provide a default implementation which does nothing and can be
/// safely overridden.
#[allow(unused_variables)]
pub trait Vst {
    /// This method must return an `Info` struct.
    fn get_info(&self) -> Info;

    /// Called when VST is initialized.
    fn init(&mut self) { trace!("Initialized vst plugin."); }

    /// Set the current preset to the index specified by `preset`.
    fn change_preset(&mut self, preset: i32) { }

    /// Get the current preset index.
    fn get_preset_num(&self) -> i32 { 0 }

    /// Set the current preset name.
    fn set_preset_name(&self, name: String) { }

    /// Get the name of the preset at the index specified by `preset`.
    fn get_preset_name(&self, preset: i32) -> String { "".to_string() }

    /// Get parameter label for parameter at `index` (e.g. "db", "sec", "ms", "%").
    fn get_parameter_label(&self, index: i32) -> String { "".to_string() }

    /// Get the parameter value for parameter at `index` (e.g. "1.0", "150", "Plate", "Off").
    fn get_parameter_text(&self, index: i32) -> String {
        format!("{:.3}", self.get_parameter(index))
    }

    /// Get the name of parameter at `index`.
    fn get_parameter_name(&self, index: i32) -> String { format!("Param {}", index) }

    /// Get the value of paramater at `index`. Should be value between 0.0 and 1.0.
    fn get_parameter(&self, index: i32) -> f32 { 0.0 }

    /// Set the value of parameter at `index`. `value` is between 0.0 and 1.0.
    fn set_parameter(&mut self, index: i32, value: f32) { }

    /// Return whether parameter at `index` can be automated.
    fn can_be_automated(&self, index: i32) -> bool { false }

    /// Called when sample rate is changed by host.
    fn sample_rate_changed(&mut self, rate: f32) { }

    /// Called when block size is changed by host.
    fn block_size_changed(&mut self, size: i64) { }

    /// Called when plugin is turned on.
    fn on_resume(&mut self) { }

    /// Called when plugin is turned off.
    fn on_suspend(&mut self) { }

    /// Vendor specific handling.
    fn vendor_specific(&mut self, index: i32, value: isize, ptr: *mut c_void, opt: f32) { }

    /// Return whether plugin supports specified action.
    fn can_do(&self, can_do: CanDo) -> Supported {
        info!("Host is asking if plugin can: {:?}.", can_do);
        Supported::Maybe
    }

    /// Get the tail size of plugin when it is stopped. Used in offline processing as well.
    fn get_tail_size(&self) -> isize { 0 }

    /// Process an audio buffer containing `f32` values. TODO: Examples
    fn process(&mut self, buffer: AudioBuffer<f32>) {
        // For each input and output
        for (input, output) in buffer.zip() {
            // For each input sample and output sample in buffer
            for (in_frame, out_frame) in input.into_iter().zip(output.into_iter()) {
                *out_frame = *in_frame;
            }
        }
    }

    /// Process an audio buffer containing `f64` values. TODO: Examples
    fn process_f64(&mut self, buffer: AudioBuffer<f64>) {
        // For each input and output
        for (input, output) in buffer.zip() {
            // For each input sample and output sample in buffer
            for (in_frame, out_frame) in input.into_iter().zip(output.into_iter()) {
                *out_frame = *in_frame;
            }
        }
    }

    /// Return handle to plugin editor if supported.
    fn get_editor(&mut self) -> Option<&mut Editor> { None }

    /// If `preset_chunks` is set to true in plugin info, this should return the raw chunk data for
    /// the current preset.
    fn get_preset_data(&mut self) -> Vec<u8> { Vec::new() }

    /// If `preset_chunks` is set to true in plugin info, this should return the raw chunk data for
    /// the current plugin bank.
    fn get_bank_data(&mut self) -> Vec<u8> { Vec::new() }

    /// If `preset_chunks` is set to true in plugin info, this should load a preset from the given
    /// chunk data.
    fn load_preset_data(&mut self, data: Vec<u8>) {}

    /// If `preset_chunks` is set to true in plugin info, this should load a preset bank from the
    /// given chunk data.
    fn load_bank_data(&mut self, data: Vec<u8>) {}

    /// Get information about an input channel. Only used by some hosts.
    fn get_input_info(&self, input: i32) -> ChannelInfo {
        ChannelInfo::new(format!("Input channel {}", input),
                         Some(format!("In {}", input)),
                         true, None)
    }

    /// Get information about an output channel. Only used by some hosts.
    fn get_output_info(&self, output: i32) -> ChannelInfo {
        ChannelInfo::new(format!("Output channel {}", output),
                         Some(format!("Out {}", output)),
                         true, None)
    }
}

#[cfg(test)]
mod tests {
    use std::default::Default;
    use std::ptr;

    use libc::c_void;

    use Vst;
    use Info;
    use main;
    use api::AEffect;

    #[derive(Default)]
    struct TestVst;

    impl Vst for TestVst {
        fn get_info(&self) -> Info {
            Info {
                name: "TestVST".to_string(),
                ..Default::default()
            }
        }
    }

    fn pass_callback(_effect: *mut AEffect, _opcode: i32, _index: i32, _value: isize, _ptr: *mut c_void, _opt: f32) -> isize {
        1
    }

    fn fail_callback(_effect: *mut AEffect, _opcode: i32, _index: i32, _value: isize, _ptr: *mut c_void, _opt: f32) -> isize {
        0
    }

    #[test]
    fn host_callback() {
        assert_eq!(main::<TestVst>(fail_callback), ptr::null_mut());
    }

    #[test]
    fn aeffect_created() {
        let aeffect = main::<TestVst>(pass_callback);
        assert!(!aeffect.is_null());
    }

    #[test]
    fn vst_drop() {
        static mut drop_test: bool = false;

        impl Drop for TestVst {
            fn drop(&mut self) {
                unsafe { drop_test = true; }
            }
        }

        let aeffect = main::<TestVst>(pass_callback);
        assert!(!aeffect.is_null());

        unsafe { (*aeffect).drop_vst() };

        // Assert that the VST is shut down and dropped.
        assert!(unsafe { drop_test });
    }

    #[test]
    fn vst_no_drop() {
        let aeffect = main::<TestVst>(pass_callback);
        assert!(!aeffect.is_null());

        // Make sure this doesn't crash.
        unsafe { (*aeffect).drop_vst() };
    }

    #[test]
    fn vst_deref() {
        let aeffect = main::<TestVst>(pass_callback);
        assert!(!aeffect.is_null());

        let vst = unsafe { (*aeffect).get_vst() };
        // Assert that deref works correctly.
        assert!(vst.get_info().name == "TestVST");
    }
}
<commit_msg>Fix and add tests for vst_main macro<commit_after>#![warn(missing_docs)]

//! rust-vst2 is a rust implementation of the VST2.4 API
//!
//! # Plugins
//! All Plugins must implement the `Vst` trait and `std::default::Default`. The `vst_main!` macro
//! must also be called in order to export the necessary functions for the VST to function.
//!
//! ## `Vst` Trait
//! All methods in this trait have a default implementation except for the `get_info` method which
//! must be implemented by the Vst object. Any of the default implementations may be overriden for
//! custom functionality; the defaults do nothing on their own.
//!
//! ## `vst_main!` macro
//! `vst_main!` will export the necessary functions to create a proper VST. This must be called
//! with your VST struct name in order for the vst to work.
//!
//! ## Example plugin
//! A barebones VST plugin:
//!
//! ```no_run
//! #[macro_use]
//! extern crate vst2;
//!
//! use vst2::{Vst, Info};
//!
//! #[derive(Default)]
//! struct BasicVst;
//!
//! impl Vst for BasicVst {
//!     fn get_info(&self) -> Info {
//!         Info {
//!             name: "BasicVst".to_string(),
//!
//!             ..Default::default()
//!         }
//!     }
//! }
//!
//! vst_main!(BasicVst); //Important!
//! # fn main() {} //no_run
//! ```
//!
//! # Hosts
//! Hosts are currently not supported. TODO

extern crate libc;
extern crate num;
#[macro_use] extern crate log;
#[macro_use] extern crate bitflags;

use std::default::Default;
use std::{ptr, mem};
use std::iter::IntoIterator;

use libc::c_void;

pub mod buffer;
pub mod enums;
pub mod api;
pub mod editor;
pub mod channels;
mod interfaces;

use enums::flags::plugin::*;
use enums::{PluginCategory, CanDo, Supported};
use api::{HostCallback, AEffect};
pub use buffer::AudioBuffer;
use editor::Editor;
use channels::ChannelInfo;

/// VST plugins are identified by a magic number. This corresponds to 0x56737450.
pub const VST_MAGIC: i32 = ('V' as i32) << 24 |
                           ('s' as i32) << 16 |
                           ('t' as i32) << 8  |
                           ('P' as i32) << 0  ;

/// Exports the necessary symbols for the plugin to be used by a vst host.
///
/// This macro takes a type which must implement the traits `Vst` and `std::default::Default`.
#[macro_export]
macro_rules! vst_main {
    ($t:ty) => {
        #[cfg(target_os = "macos")]
        #[no_mangle]
        pub extern "system" fn main_macho (callback: $crate::api::HostCallback) -> *mut $crate::api::AEffect {
            VSTPluginMain(callback)
        }

        #[cfg(target_os = "windows")]
        #[allow(non_snake_case)]
        #[no_mangle]
        pub extern "system" fn MAIN (callback: $crate::api::HostCallback) -> *mut $crate::api::AEffect {
            VSTPluginMain(callback)
        }

        #[allow(non_snake_case)]
        #[no_mangle]
        pub extern "system" fn VSTPluginMain(callback: $crate::api::HostCallback) -> *mut $crate::api::AEffect {
            $crate::main::<$t>(callback)
        }
    }
}

/// Initializes a VST plugin and returns a raw pointer to an AEffect struct.
#[doc(hidden)]
pub fn main<T: Vst + Default>(callback: HostCallback) -> *mut AEffect {
    // 1 is audioMasterVersion TODO: abstract this
    if callback(ptr::null_mut(), 1, 0, 0, ptr::null_mut(), 0.0) == 0 {
        return ptr::null_mut();
    }

    let mut vst: T = Default::default();
    let info = vst.get_info().clone();
    trace!("Creating VST plugin instance...");

    unsafe { mem::transmute(Box::new(AEffect {
        magic: VST_MAGIC,
        dispatcher: interfaces::dispatch, // fn pointer

        _process: interfaces::process_deprecated, // fn pointer

        setParameter: interfaces::set_parameter, // fn pointer
        getParameter: interfaces::get_parameter, // fn pointer

        numPrograms: info.presets,
        numParams: info.parameters,
        numInputs: info.inputs,
        numOutputs: info.outputs,

        flags: {
            let mut flag = CAN_REPLACING;

            if info.f64_precision {
                flag = flag | CAN_DOUBLE_REPLACING;
            }

            if vst.get_editor().is_some() {
                flag = flag | HAS_EDITOR;
            }

            if info.preset_chunks {
                flag = flag | PROGRAM_CHUNKS;
            }

            if let PluginCategory::Synth = info.category {
                flag = flag | IS_SYNTH;
            }

            flag.bits()
        },

        reserved1: 0,
        reserved2: 0,

        initialDelay: 0,

        _realQualities: 0,
        _offQualities: 0,
        _ioRatio: 0.0,

        object: mem::transmute(Box::new(Box::new(vst) as Box<Vst>)),
        user: ptr::null_mut(),

        uniqueId: 0,
        version: 0001,

        processReplacing: interfaces::process_replacing, // fn pointer
        processReplacingF64: interfaces::process_replacing_f64, //fn pointer

        future: [0u8; 56]
    })) }
}

/// A structure representing static plugin information.
#[derive(Clone, Debug)]
pub struct Info {
    /// Plugin Name.
    pub name: String,

    /// Plugin Vendor.
    pub vendor: String,

    /// Number of different presets.
    pub presets: i32,

    /// Number of parameters.
    pub parameters: i32,

    /// Number of inputs.
    pub inputs: i32,

    /// Number of outputs.
    pub outputs: i32,

    /// Unique plugin ID. Can be registered with Steinberg to prevent conflicts with other plugins.
    ///
    /// This ID is used to identify a plugin during save and load of a preset and project.
    pub unique_id: i32,

    /// Plugin version (e.g. 0001 = `v0.0.0.1`, 1283 = `v1.2.8.3`).
    pub version: i32,

    /// Plugin category. Possible values are found in `enums::PluginCategory`.
    pub category: PluginCategory,

    //TODO: Doc
    pub initial_delay: i32,

    /// Indicates whether preset data is handled in formatless chunks. If false,
    /// host saves and restores plugins by reading/writing parameter data. If true, it is up to
    /// the plugin to manage saving preset data by implementing  the
    /// `{get, load}_{preset, bank}_chunks()` methods. Default is `false`.
    pub preset_chunks: bool,

    /// Indicates whether this plugin can process f64 based `AudioBuffer` buffers. Default is
    /// `false`.
    pub f64_precision: bool,

    //no_sound_in_stop: bool, //TODO: Implement this somehow
}

impl Default for Info {
    fn default() -> Info {
        Info {
            name: "VST".to_string(),
            vendor: String::new(),

            presets: 1, // default preset
            parameters: 0,
            inputs: 2, // Stereo in,out
            outputs: 2,

            unique_id: 0,
            version: 0001, // v0.0.0.1

            category: PluginCategory::Effect,

            initial_delay: 0,

            preset_chunks: false,
            f64_precision: false,
        }
    }
}

/// Must be implemented by all VST plugins.
///
/// All methods except `get_info` provide a default implementation which does nothing and can be
/// safely overridden.
#[allow(unused_variables)]
pub trait Vst {
    /// This method must return an `Info` struct.
    fn get_info(&self) -> Info;

    /// Called when VST is initialized.
    fn init(&mut self) { trace!("Initialized vst plugin."); }

    /// Set the current preset to the index specified by `preset`.
    fn change_preset(&mut self, preset: i32) { }

    /// Get the current preset index.
    fn get_preset_num(&self) -> i32 { 0 }

    /// Set the current preset name.
    fn set_preset_name(&self, name: String) { }

    /// Get the name of the preset at the index specified by `preset`.
    fn get_preset_name(&self, preset: i32) -> String { "".to_string() }

    /// Get parameter label for parameter at `index` (e.g. "db", "sec", "ms", "%").
    fn get_parameter_label(&self, index: i32) -> String { "".to_string() }

    /// Get the parameter value for parameter at `index` (e.g. "1.0", "150", "Plate", "Off").
    fn get_parameter_text(&self, index: i32) -> String {
        format!("{:.3}", self.get_parameter(index))
    }

    /// Get the name of parameter at `index`.
    fn get_parameter_name(&self, index: i32) -> String { format!("Param {}", index) }

    /// Get the value of paramater at `index`. Should be value between 0.0 and 1.0.
    fn get_parameter(&self, index: i32) -> f32 { 0.0 }

    /// Set the value of parameter at `index`. `value` is between 0.0 and 1.0.
    fn set_parameter(&mut self, index: i32, value: f32) { }

    /// Return whether parameter at `index` can be automated.
    fn can_be_automated(&self, index: i32) -> bool { false }

    /// Called when sample rate is changed by host.
    fn sample_rate_changed(&mut self, rate: f32) { }

    /// Called when block size is changed by host.
    fn block_size_changed(&mut self, size: i64) { }

    /// Called when plugin is turned on.
    fn on_resume(&mut self) { }

    /// Called when plugin is turned off.
    fn on_suspend(&mut self) { }

    /// Vendor specific handling.
    fn vendor_specific(&mut self, index: i32, value: isize, ptr: *mut c_void, opt: f32) { }

    /// Return whether plugin supports specified action.
    fn can_do(&self, can_do: CanDo) -> Supported {
        info!("Host is asking if plugin can: {:?}.", can_do);
        Supported::Maybe
    }

    /// Get the tail size of plugin when it is stopped. Used in offline processing as well.
    fn get_tail_size(&self) -> isize { 0 }

    /// Process an audio buffer containing `f32` values. TODO: Examples
    fn process(&mut self, buffer: AudioBuffer<f32>) {
        // For each input and output
        for (input, output) in buffer.zip() {
            // For each input sample and output sample in buffer
            for (in_frame, out_frame) in input.into_iter().zip(output.into_iter()) {
                *out_frame = *in_frame;
            }
        }
    }

    /// Process an audio buffer containing `f64` values. TODO: Examples
    fn process_f64(&mut self, buffer: AudioBuffer<f64>) {
        // For each input and output
        for (input, output) in buffer.zip() {
            // For each input sample and output sample in buffer
            for (in_frame, out_frame) in input.into_iter().zip(output.into_iter()) {
                *out_frame = *in_frame;
            }
        }
    }

    /// Return handle to plugin editor if supported.
    fn get_editor(&mut self) -> Option<&mut Editor> { None }

    /// If `preset_chunks` is set to true in plugin info, this should return the raw chunk data for
    /// the current preset.
    fn get_preset_data(&mut self) -> Vec<u8> { Vec::new() }

    /// If `preset_chunks` is set to true in plugin info, this should return the raw chunk data for
    /// the current plugin bank.
    fn get_bank_data(&mut self) -> Vec<u8> { Vec::new() }

    /// If `preset_chunks` is set to true in plugin info, this should load a preset from the given
    /// chunk data.
    fn load_preset_data(&mut self, data: Vec<u8>) {}

    /// If `preset_chunks` is set to true in plugin info, this should load a preset bank from the
    /// given chunk data.
    fn load_bank_data(&mut self, data: Vec<u8>) {}

    /// Get information about an input channel. Only used by some hosts.
    fn get_input_info(&self, input: i32) -> ChannelInfo {
        ChannelInfo::new(format!("Input channel {}", input),
                         Some(format!("In {}", input)),
                         true, None)
    }

    /// Get information about an output channel. Only used by some hosts.
    fn get_output_info(&self, output: i32) -> ChannelInfo {
        ChannelInfo::new(format!("Output channel {}", output),
                         Some(format!("Out {}", output)),
                         true, None)
    }
}

#[cfg(test)]
#[allow(private_no_mangle_fns)] // For `vst_main!`
mod tests {
    use std::default::Default;
    use std::ptr;

    use libc::c_void;

    use Vst;
    use Info;
    use api::AEffect;

    #[derive(Default)]
    struct TestVst;

    impl Vst for TestVst {
        fn get_info(&self) -> Info {
            Info {
                name: "TestVST".to_string(),
                ..Default::default()
            }
        }
    }

    vst_main!(TestVst);

    fn pass_callback(_effect: *mut AEffect, _opcode: i32, _index: i32, _value: isize, _ptr: *mut c_void, _opt: f32) -> isize {
        1
    }

    fn fail_callback(_effect: *mut AEffect, _opcode: i32, _index: i32, _value: isize, _ptr: *mut c_void, _opt: f32) -> isize {
        0
    }

    #[cfg(target_os = "windows")]
    #[test]
    fn old_hosts() {
        assert_eq!(MAIN(fail_callback), ptr::null_mut());
    }

    #[cfg(target_os = "macos")]
    #[test]
    fn old_hosts() {
        assert_eq!(main_macho(fail_callback), ptr::null_mut());
    }

    #[test]
    fn host_callback() {
        assert_eq!(VSTPluginMain(fail_callback), ptr::null_mut());
    }

    #[test]
    fn aeffect_created() {
        let aeffect = VSTPluginMain(pass_callback);
        assert!(!aeffect.is_null());
    }

    #[test]
    fn vst_drop() {
        static mut drop_test: bool = false;

        impl Drop for TestVst {
            fn drop(&mut self) {
                unsafe { drop_test = true; }
            }
        }

        let aeffect = VSTPluginMain(pass_callback);
        assert!(!aeffect.is_null());

        unsafe { (*aeffect).drop_vst() };

        // Assert that the VST is shut down and dropped.
        assert!(unsafe { drop_test });
    }

    #[test]
    fn vst_no_drop() {
        let aeffect = VSTPluginMain(pass_callback);
        assert!(!aeffect.is_null());

        // Make sure this doesn't crash.
        unsafe { (*aeffect).drop_vst() };
    }

    #[test]
    fn vst_deref() {
        let aeffect = VSTPluginMain(pass_callback);
        assert!(!aeffect.is_null());

        let vst = unsafe { (*aeffect).get_vst() };
        // Assert that deref works correctly.
        assert!(vst.get_info().name == "TestVST");
    }
}
<|endoftext|>


<commit_before>#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]

#[macro_use] extern crate log;
#[macro_use] extern crate quick_error;
#[cfg(test)] extern crate env_logger;

extern crate serde;
extern crate serde_json;
extern crate sodiumoxide;
extern crate rustc_serialize;
extern crate chrono;

pub mod key;
pub mod signedjson;
pub mod ser;

use rustc_serialize::base64;

pub const UNPADDED_BASE64 : base64::Config = base64::Config {
    char_set: base64::CharacterSet::Standard,
    newline: base64::Newline::LF,
    pad: false,
    line_length: None,
};

#[derive(Serialize)]
struct SignedRequest<'a, T: serde::Serialize + 'a> {
    method: &'a str,
    uri: &'a str,
    origin: &'a str,
    destination: &'a str,
    #[serde(skip_serializing_if="Option::is_none")]
    content: Option<&'a T>
}

/// Compute the signature for an outgoing federation request
pub fn sign_request_b64(
    key: &signedjson::SigningKey,
    method: &str,
    uri: &str,
    origin: &str,
    destination: &str,
    content: Option<&signedjson::Object>,
) -> Result<String, signedjson::SigningJsonError> {
    let req = SignedRequest {
        method: method,
        uri: uri,
        origin: origin,
        destination: destination,
        content: content,
    };

    Ok(try!(signedjson::get_sig_for_json_b64(key, &req)))
}

/// Generate a Matrix Authorization header.
pub fn generate_auth_header(
    key: &signedjson::SigningKey,
    method: &str,
    uri: &str,
    origin: &str,
    destination: &str,
    content: Option<&signedjson::Object>,
) -> Result<String, signedjson::SigningJsonError> {
    let sig = try!(sign_request_b64(key, method, uri, origin, destination, content));
    Ok(format!(
        r#"X-Matrix origin={},key="{}",sig="{}""#,
        &origin, key.key_id, sig,
    ))
}

#[test]
fn test_sign_request() {
    let key = signedjson::SigningKey::from_seed(&[0u8; 32], "ed25519:test".to_string()).unwrap();
    let sig = sign_request_b64(&key, "GET", "/", "jki.re", "matrix.org", None).unwrap();
    assert_eq!(sig, "PmWN0xPpzEWR97AIQ8Q+/dysu7uZBPMnUzcK9rwqgae3t8LIDSdpg4moQ4qzdkwjqv7+JIcyeFumPeDFLvDxAw");
}

#[test]
fn test_auth_header() {
    let key = signedjson::SigningKey::from_seed(&[0u8; 32], "ed25519:test".to_string()).unwrap();
    let header = generate_auth_header(&key, "GET", "/", "jki.re", "matrix.org", None).unwrap();
    assert_eq!(
        header,
        r#"X-Matrix origin=jki.re,key="ed25519:test",sig="PmWN0xPpzEWR97AIQ8Q+/dysu7uZBPMnUzcK9rwqgae3t8LIDSdpg4moQ4qzdkwjqv7+JIcyeFumPeDFLvDxAw""#
    );
}
<commit_msg>Stuff<commit_after>#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]

#[macro_use] extern crate log;
#[macro_use] extern crate quick_error;
#[cfg(test)] extern crate env_logger;

extern crate serde;
extern crate serde_json;
extern crate sodiumoxide;
extern crate rustc_serialize;
extern crate chrono;

pub mod key;
pub mod signedjson;
pub mod ser;

use rustc_serialize::base64;
use rustc_serialize::base64::ToBase64;

use sodiumoxide::crypto::sign;

pub const UNPADDED_BASE64 : base64::Config = base64::Config {
    char_set: base64::CharacterSet::Standard,
    newline: base64::Newline::LF,
    pad: false,
    line_length: None,
};

#[derive(Serialize)]
struct SignedRequest<'a, T: serde::Serialize + 'a> {
    method: &'a str,
    uri: &'a str,
    origin: &'a str,
    destination: &'a str,
    #[serde(skip_serializing_if="Option::is_none")]
    content: Option<&'a T>
}

/// Compute the signature for an outgoing federation request
pub fn sign_request(
    key: &signedjson::SigningKey,
    method: &str,
    uri: &str,
    origin: &str,
    destination: &str,
) -> Result<sign::Signature, signedjson::SigningJsonError> {
    signedjson::get_sig_for_json(key, &SignedRequest {
        method: method,
        uri: uri,
        origin: origin,
        destination: destination,
        content: None as Option<&u8>,  // As the option needs a type.
    })
}

/// Compute the signature for an outgoing federation request
pub fn sign_request_with_content<T: serde::Serialize>(
    key: &signedjson::SigningKey,
    method: &str,
    uri: &str,
    origin: &str,
    destination: &str,
    content: Option<&T>,
) -> Result<sign::Signature, signedjson::SigningJsonError> {
    signedjson::get_sig_for_json(key, &SignedRequest {
        method: method,
        uri: uri,
        origin: origin,
        destination: destination,
        content: content,
    })
}

/// Generate a Matrix Authorization header.
pub fn generate_auth_header<T: serde::Serialize>(
    key: &signedjson::SigningKey,
    method: &str,
    uri: &str,
    origin: &str,
    destination: &str,
    content: Option<&T>,
) -> Result<String, signedjson::SigningJsonError> {
    let sig = try!(sign_request_with_content(key, method, uri, origin, destination, content));
    Ok(format!(
        r#"X-Matrix origin={},key="{}",sig="{}""#,
        &origin, key.key_id, sig.0.to_base64(UNPADDED_BASE64),
    ))
}

#[cfg(test)]
mod tests {
    use super::*;
    use rustc_serialize::base64::FromBase64;

    #[test]
    fn test_sign_request() {
        let key = signedjson::SigningKey::from_seed(&[0u8; 32], "ed25519:test".to_string()).unwrap();
        let sig = sign_request(&key, "GET", "/", "jki.re", "matrix.org").unwrap();
        let expected = "PmWN0xPpzEWR97AIQ8Q+/dysu7uZBPMnUzcK9rwqgae3t8LIDSdpg4moQ4qzdkwjqv7+JIcyeFumPeDFLvDxAw".from_base64().unwrap();
        assert_eq!(&sig[..], &expected[..]);
    }

    #[test]
    fn test_auth_header() {
        let key = signedjson::SigningKey::from_seed(&[0u8; 32], "ed25519:test".to_string()).unwrap();
        let header = generate_auth_header::<u8>(&key, "GET", "/", "jki.re", "matrix.org", None).unwrap();
        assert_eq!(
            header,
            r#"X-Matrix origin=jki.re,key="ed25519:test",sig="PmWN0xPpzEWR97AIQ8Q+/dysu7uZBPMnUzcK9rwqgae3t8LIDSdpg4moQ4qzdkwjqv7+JIcyeFumPeDFLvDxAw""#
        );
    }
}
<|endoftext|>


<commit_before>#![cfg_attr(any(feature = "libc_stub", all(target_arch = "wasm32", not(target_os = "emscripten"))), feature(allocator_api))]

extern crate crc;
#[cfg(not(any(feature = "libc_stub", all(target_arch = "wasm32", not(target_os = "emscripten")))))]
extern crate libc;
#[cfg(any(feature = "libc_stub", all(target_arch = "wasm32", not(target_os = "emscripten"))))]
mod libc {
    #![allow(non_camel_case_types)]

    use std::alloc::{Alloc, Global, Layout};
    use std::mem;
    use std::ptr::NonNull;

    pub type c_void = u8;
    pub type c_int = i32;
    pub type c_uint = u32;
    pub type c_ulong = u64;
    pub type c_char = i8;
    pub type size_t = usize;

    pub unsafe fn malloc(a: size_t) -> *mut c_void {
        let size = a + mem::size_of::<size_t>();
        let layout = match Layout::from_size_align(size, mem::align_of::<size_t>()) {
            Ok(n) => n,
            Err(_) => return 0 as *mut c_void,
        };
        let ptr = match Global.alloc(layout) {
            Ok(addr) => addr.as_ptr() as *mut size_t,
            Err(_) => return 0 as *mut c_void,
        };
        *ptr.offset(0) = size;
        ptr.offset(1) as *mut c_void
    }

    pub unsafe fn realloc(ptr: *mut c_void, a: size_t) -> *mut c_void {
        let new_size = a + mem::size_of::<size_t>();
        let ptr = (ptr as *mut size_t).offset(-1);
        let old_size = *ptr.offset(0);
        let layout = Layout::from_size_align_unchecked(old_size, mem::size_of::<size_t>());
        let ptr = match Global.realloc(NonNull::new_unchecked(ptr as *mut _), layout, new_size) {
            Ok(addr) => addr.as_ptr() as *mut size_t,
            Err(_) => return 0 as *mut c_void,
        };
        *ptr.offset(0) = new_size;
        ptr.offset(1) as *mut c_void
    }

    pub unsafe fn free(ptr: *mut c_void) {
        let ptr = (ptr as *mut size_t).offset(-1);
        let size = *ptr.offset(0);
        let align = mem::size_of::<size_t>();
        let layout = Layout::from_size_align_unchecked(size, align);
        Global.dealloc(NonNull::new_unchecked(ptr as *mut _), layout);
    }
}
extern crate miniz_oxide;

use std::{cmp, ptr, slice};
use std::panic::{catch_unwind, AssertUnwindSafe};
use crc::{Hasher32, crc32};

use libc::{c_int, c_uint, c_ulong, c_void, size_t};

use miniz_oxide::{MZError, MZResult};
#[allow(bad_style)]
pub use tdef::tdefl_compressor;

pub use miniz_oxide::mz_adler32_oxide;
use miniz_oxide::deflate::CompressionLevel;
use miniz_oxide::deflate::core::CompressionStrategy;

#[macro_use]
mod unmangle;

pub mod lib_oxide;
use lib_oxide::*;

mod tinfl;
pub use tinfl::{tinfl_decompress, tinfl_decompress_mem_to_heap,
                tinfl_decompress_mem_to_mem, tinfl_decompressor};

mod tdef;
pub use tdef::{tdefl_compress, tdefl_compress_buffer, tdefl_compress_mem_to_heap,
               tdefl_compress_mem_to_mem, tdefl_compress_mem_to_output,
               tdefl_create_comp_flags_from_zip_params, tdefl_get_prev_return_status, tdefl_init,
               tdefl_allocate, tdefl_deallocate, tdefl_get_adler32};

pub use tdef::flush_modes::*;
pub use tdef::strategy::*;

pub fn mz_crc32_oxide(crc32: c_uint, data: &[u8]) -> c_uint {
    let mut digest = crc32::Digest::new_with_initial(crc32::IEEE, crc32);
    digest.write(data);
    digest.sum32()
}

pub const MZ_DEFLATED: c_int = 8;
pub const MZ_DEFAULT_WINDOW_BITS: c_int = 15;

pub const MZ_ADLER32_INIT: c_ulong = 1;
pub const MZ_CRC32_INIT: c_ulong = 0;

fn as_c_return_code(r: MZResult) -> c_int {
    match r {
        Err(status) => status as c_int,
        Ok(status) => status as c_int,
    }
}

unmangle!(
pub unsafe extern "C" fn miniz_def_alloc_func(
    _opaque: *mut c_void,
    items: size_t,
    size: size_t,
) -> *mut c_void {
    libc::malloc(items * size)
}

pub unsafe extern "C" fn miniz_def_free_func(_opaque: *mut c_void, address: *mut c_void) {
    libc::free(address)
}

pub unsafe extern "C" fn miniz_def_realloc_func(
    _opaque: *mut c_void,
    address: *mut c_void,
    items: size_t,
    size: size_t,
) -> *mut c_void {
    libc::realloc(address, items * size)
}

pub unsafe extern "C" fn mz_adler32(adler: c_ulong, ptr: *const u8, buf_len: usize) -> c_ulong {
    ptr.as_ref().map_or(MZ_ADLER32_INIT, |r| {
        let data = slice::from_raw_parts(r, buf_len);
        mz_adler32_oxide(adler as c_uint, data) as c_ulong
    })
}

pub unsafe extern "C" fn mz_crc32(crc: c_ulong, ptr: *const u8, buf_len: size_t) -> c_ulong {
    ptr.as_ref().map_or(MZ_CRC32_INIT, |r| {
        let data = slice::from_raw_parts(r, buf_len);
        mz_crc32_oxide(crc as c_uint, data) as c_ulong
    })
}
);

macro_rules! oxidize {
    ($mz_func:ident, $mz_func_oxide:ident; $($arg_name:ident: $type_name:ident),*) => {
        unmangle!(
        pub unsafe extern "C" fn $mz_func(stream: *mut mz_stream, $($arg_name: $type_name),*)
                                          -> c_int {
            match stream.as_mut() {
                None => MZError::Stream as c_int,
                Some(stream) => {
                    // Make sure we catch a potential panic, as
                    // this is called from C.
                    match catch_unwind(AssertUnwindSafe(|| {
                        let mut stream_oxide = StreamOxide::new(&mut *stream);
                        let status = $mz_func_oxide(&mut stream_oxide, $($arg_name),*);
                        *stream = stream_oxide.into_mz_stream();
                        as_c_return_code(status)
                    })) {
                        Ok(res) => res,
                        Err(_) => {
                            println!("FATAL ERROR: Caught panic!");
                            MZError::Stream as c_int},
                    }
                }
            }
        });
    };
}

oxidize!(mz_deflateInit2, mz_deflate_init2_oxide;
         level: c_int, method: c_int, window_bits: c_int, mem_level: c_int, strategy: c_int);
oxidize!(mz_deflate, mz_deflate_oxide;
         flush: c_int);
oxidize!(mz_deflateEnd, mz_deflate_end_oxide;);
oxidize!(mz_deflateReset, mz_deflate_reset_oxide;);

oxidize!(mz_inflateInit2, mz_inflate_init2_oxide;
         window_bits: c_int);
oxidize!(mz_inflate, mz_inflate_oxide;
         flush: c_int);
oxidize!(mz_inflateEnd, mz_inflate_end_oxide;);

unmangle!(
pub unsafe extern "C" fn mz_deflateInit(stream: *mut mz_stream, level: c_int) -> c_int {
    mz_deflateInit2(
        stream,
        level,
        MZ_DEFLATED,
        MZ_DEFAULT_WINDOW_BITS,
        9,
        CompressionStrategy::Default as c_int,
    )
}

pub unsafe extern "C" fn mz_compress(
    dest: *mut u8,
    dest_len: *mut c_ulong,
    source: *const u8,
    source_len: c_ulong,
) -> c_int {
    mz_compress2(
        dest,
        dest_len,
        source,
        source_len,
        CompressionLevel::DefaultCompression as c_int,
    )
}

pub unsafe extern "C" fn mz_compress2(
    dest: *mut u8,
    dest_len: *mut c_ulong,
    source: *const u8,
    source_len: c_ulong,
    level: c_int,
) -> c_int {
    dest_len.as_mut().map_or(
        MZError::Param as c_int,
        |dest_len| {
            if buffer_too_large(source_len, *dest_len) {
                return MZError::Param as c_int;
            }

            let mut stream: mz_stream = mz_stream {
                next_in: source,
                avail_in: source_len as c_uint,
                next_out: dest,
                avail_out: (*dest_len) as c_uint,
                ..Default::default()
            };

            let mut stream_oxide = StreamOxide::new(&mut stream);
            as_c_return_code(mz_compress2_oxide(&mut stream_oxide, level, dest_len))
        },
    )
}

pub extern "C" fn mz_deflateBound(_stream: *mut mz_stream, source_len: c_ulong) -> c_ulong {
    cmp::max(
        128 + (source_len * 110) / 100,
        128 + source_len + ((source_len / (31 * 1024)) + 1) * 5,
    )
}

pub unsafe extern "C" fn mz_inflateInit(stream: *mut mz_stream) -> c_int {
    mz_inflateInit2(stream, MZ_DEFAULT_WINDOW_BITS)
}

pub unsafe extern "C" fn mz_uncompress(
    dest: *mut u8,
    dest_len: *mut c_ulong,
    source: *const u8,
    source_len: c_ulong,
) -> c_int {
    dest_len.as_mut().map_or(
        MZError::Param as c_int,
        |dest_len| {
            if buffer_too_large(source_len, *dest_len) {
                return MZError::Param as c_int;
            }

            let mut stream: mz_stream = mz_stream {
                next_in: source,
                avail_in: source_len as c_uint,
                next_out: dest,
                avail_out: (*dest_len) as c_uint,
                ..Default::default()
            };

            let mut stream_oxide = StreamOxide::new(&mut stream);
            as_c_return_code(mz_uncompress2_oxide(&mut stream_oxide, dest_len))
        },
    )
}

pub extern "C" fn mz_compressBound(source_len: c_ulong) -> c_ulong {
    mz_deflateBound(ptr::null_mut(), source_len)
}

);

#[cfg(target_bit_width = "64")]
#[inline]
fn buffer_too_large(source_len: c_ulong, dest_len: c_ulong) -> bool {
    (source_len | dest_len) > 0xFFFFFFFF
}

#[cfg(not(target_bit_width = "64"))]
#[inline]
fn buffer_too_large(_source_len: c_ulong, _dest_len: c_ulong) -> bool {
    false
}
<commit_msg>Remove "allocator_api" unstable feature usage (#40)<commit_after>extern crate crc;
#[cfg(not(any(feature = "libc_stub", all(target_arch = "wasm32", not(target_os = "emscripten")))))]
extern crate libc;
#[cfg(any(feature = "libc_stub", all(target_arch = "wasm32", not(target_os = "emscripten"))))]
mod libc {
    #![allow(non_camel_case_types)]

    use std::alloc::{alloc as rust_alloc, realloc as rust_realloc, dealloc as rust_dealloc, Layout};
    use std::mem;
    use std::ptr::NonNull;

    pub type c_void = u8;
    pub type c_int = i32;
    pub type c_uint = u32;
    pub type c_ulong = u64;
    pub type c_char = i8;
    pub type size_t = usize;

    pub unsafe fn malloc(a: size_t) -> *mut c_void {
        let size = a + mem::size_of::<size_t>();
        let layout = match Layout::from_size_align(size, mem::align_of::<size_t>()) {
            Ok(n) => n,
            Err(_) => return 0 as *mut c_void,
        };
        let ptr = rust_alloc(layout) as *mut size_t;
        *ptr.offset(0) = size;
        ptr.offset(1) as *mut c_void
    }

    pub unsafe fn realloc(ptr: *mut c_void, a: size_t) -> *mut c_void {
        let new_size = a + mem::size_of::<size_t>();
        let ptr = (ptr as *mut size_t).offset(-1);
        let old_size = *ptr.offset(0);
        let layout = Layout::from_size_align_unchecked(old_size, mem::size_of::<size_t>());
        let ptr = rust_realloc(ptr as *mut _, layout, new_size) as *mut size_t;
        *ptr.offset(0) = new_size;
        ptr.offset(1) as *mut c_void
    }

    pub unsafe fn free(ptr: *mut c_void) {
        let ptr = (ptr as *mut size_t).offset(-1);
        let size = *ptr.offset(0);
        let align = mem::size_of::<size_t>();
        let layout = Layout::from_size_align_unchecked(size, align);
        rust_dealloc(ptr as *mut _, layout);
    }
}
extern crate miniz_oxide;

use std::{cmp, ptr, slice};
use std::panic::{catch_unwind, AssertUnwindSafe};
use crc::{Hasher32, crc32};

use libc::{c_int, c_uint, c_ulong, c_void, size_t};

use miniz_oxide::{MZError, MZResult};
#[allow(bad_style)]
pub use tdef::tdefl_compressor;

pub use miniz_oxide::mz_adler32_oxide;
use miniz_oxide::deflate::CompressionLevel;
use miniz_oxide::deflate::core::CompressionStrategy;

#[macro_use]
mod unmangle;

pub mod lib_oxide;
use lib_oxide::*;

mod tinfl;
pub use tinfl::{tinfl_decompress, tinfl_decompress_mem_to_heap,
                tinfl_decompress_mem_to_mem, tinfl_decompressor};

mod tdef;
pub use tdef::{tdefl_compress, tdefl_compress_buffer, tdefl_compress_mem_to_heap,
               tdefl_compress_mem_to_mem, tdefl_compress_mem_to_output,
               tdefl_create_comp_flags_from_zip_params, tdefl_get_prev_return_status, tdefl_init,
               tdefl_allocate, tdefl_deallocate, tdefl_get_adler32};

pub use tdef::flush_modes::*;
pub use tdef::strategy::*;

pub fn mz_crc32_oxide(crc32: c_uint, data: &[u8]) -> c_uint {
    let mut digest = crc32::Digest::new_with_initial(crc32::IEEE, crc32);
    digest.write(data);
    digest.sum32()
}

pub const MZ_DEFLATED: c_int = 8;
pub const MZ_DEFAULT_WINDOW_BITS: c_int = 15;

pub const MZ_ADLER32_INIT: c_ulong = 1;
pub const MZ_CRC32_INIT: c_ulong = 0;

fn as_c_return_code(r: MZResult) -> c_int {
    match r {
        Err(status) => status as c_int,
        Ok(status) => status as c_int,
    }
}

unmangle!(
pub unsafe extern "C" fn miniz_def_alloc_func(
    _opaque: *mut c_void,
    items: size_t,
    size: size_t,
) -> *mut c_void {
    libc::malloc(items * size)
}

pub unsafe extern "C" fn miniz_def_free_func(_opaque: *mut c_void, address: *mut c_void) {
    libc::free(address)
}

pub unsafe extern "C" fn miniz_def_realloc_func(
    _opaque: *mut c_void,
    address: *mut c_void,
    items: size_t,
    size: size_t,
) -> *mut c_void {
    libc::realloc(address, items * size)
}

pub unsafe extern "C" fn mz_adler32(adler: c_ulong, ptr: *const u8, buf_len: usize) -> c_ulong {
    ptr.as_ref().map_or(MZ_ADLER32_INIT, |r| {
        let data = slice::from_raw_parts(r, buf_len);
        mz_adler32_oxide(adler as c_uint, data) as c_ulong
    })
}

pub unsafe extern "C" fn mz_crc32(crc: c_ulong, ptr: *const u8, buf_len: size_t) -> c_ulong {
    ptr.as_ref().map_or(MZ_CRC32_INIT, |r| {
        let data = slice::from_raw_parts(r, buf_len);
        mz_crc32_oxide(crc as c_uint, data) as c_ulong
    })
}
);

macro_rules! oxidize {
    ($mz_func:ident, $mz_func_oxide:ident; $($arg_name:ident: $type_name:ident),*) => {
        unmangle!(
        pub unsafe extern "C" fn $mz_func(stream: *mut mz_stream, $($arg_name: $type_name),*)
                                          -> c_int {
            match stream.as_mut() {
                None => MZError::Stream as c_int,
                Some(stream) => {
                    // Make sure we catch a potential panic, as
                    // this is called from C.
                    match catch_unwind(AssertUnwindSafe(|| {
                        let mut stream_oxide = StreamOxide::new(&mut *stream);
                        let status = $mz_func_oxide(&mut stream_oxide, $($arg_name),*);
                        *stream = stream_oxide.into_mz_stream();
                        as_c_return_code(status)
                    })) {
                        Ok(res) => res,
                        Err(_) => {
                            println!("FATAL ERROR: Caught panic!");
                            MZError::Stream as c_int},
                    }
                }
            }
        });
    };
}

oxidize!(mz_deflateInit2, mz_deflate_init2_oxide;
         level: c_int, method: c_int, window_bits: c_int, mem_level: c_int, strategy: c_int);
oxidize!(mz_deflate, mz_deflate_oxide;
         flush: c_int);
oxidize!(mz_deflateEnd, mz_deflate_end_oxide;);
oxidize!(mz_deflateReset, mz_deflate_reset_oxide;);

oxidize!(mz_inflateInit2, mz_inflate_init2_oxide;
         window_bits: c_int);
oxidize!(mz_inflate, mz_inflate_oxide;
         flush: c_int);
oxidize!(mz_inflateEnd, mz_inflate_end_oxide;);

unmangle!(
pub unsafe extern "C" fn mz_deflateInit(stream: *mut mz_stream, level: c_int) -> c_int {
    mz_deflateInit2(
        stream,
        level,
        MZ_DEFLATED,
        MZ_DEFAULT_WINDOW_BITS,
        9,
        CompressionStrategy::Default as c_int,
    )
}

pub unsafe extern "C" fn mz_compress(
    dest: *mut u8,
    dest_len: *mut c_ulong,
    source: *const u8,
    source_len: c_ulong,
) -> c_int {
    mz_compress2(
        dest,
        dest_len,
        source,
        source_len,
        CompressionLevel::DefaultCompression as c_int,
    )
}

pub unsafe extern "C" fn mz_compress2(
    dest: *mut u8,
    dest_len: *mut c_ulong,
    source: *const u8,
    source_len: c_ulong,
    level: c_int,
) -> c_int {
    dest_len.as_mut().map_or(
        MZError::Param as c_int,
        |dest_len| {
            if buffer_too_large(source_len, *dest_len) {
                return MZError::Param as c_int;
            }

            let mut stream: mz_stream = mz_stream {
                next_in: source,
                avail_in: source_len as c_uint,
                next_out: dest,
                avail_out: (*dest_len) as c_uint,
                ..Default::default()
            };

            let mut stream_oxide = StreamOxide::new(&mut stream);
            as_c_return_code(mz_compress2_oxide(&mut stream_oxide, level, dest_len))
        },
    )
}

pub extern "C" fn mz_deflateBound(_stream: *mut mz_stream, source_len: c_ulong) -> c_ulong {
    cmp::max(
        128 + (source_len * 110) / 100,
        128 + source_len + ((source_len / (31 * 1024)) + 1) * 5,
    )
}

pub unsafe extern "C" fn mz_inflateInit(stream: *mut mz_stream) -> c_int {
    mz_inflateInit2(stream, MZ_DEFAULT_WINDOW_BITS)
}

pub unsafe extern "C" fn mz_uncompress(
    dest: *mut u8,
    dest_len: *mut c_ulong,
    source: *const u8,
    source_len: c_ulong,
) -> c_int {
    dest_len.as_mut().map_or(
        MZError::Param as c_int,
        |dest_len| {
            if buffer_too_large(source_len, *dest_len) {
                return MZError::Param as c_int;
            }

            let mut stream: mz_stream = mz_stream {
                next_in: source,
                avail_in: source_len as c_uint,
                next_out: dest,
                avail_out: (*dest_len) as c_uint,
                ..Default::default()
            };

            let mut stream_oxide = StreamOxide::new(&mut stream);
            as_c_return_code(mz_uncompress2_oxide(&mut stream_oxide, dest_len))
        },
    )
}

pub extern "C" fn mz_compressBound(source_len: c_ulong) -> c_ulong {
    mz_deflateBound(ptr::null_mut(), source_len)
}

);

#[cfg(target_bit_width = "64")]
#[inline]
fn buffer_too_large(source_len: c_ulong, dest_len: c_ulong) -> bool {
    (source_len | dest_len) > 0xFFFFFFFF
}

#[cfg(not(target_bit_width = "64"))]
#[inline]
fn buffer_too_large(_source_len: c_ulong, _dest_len: c_ulong) -> bool {
    false
}
<|endoftext|>


<commit_before>pub mod parse;

#[cfg(test)]
mod tests {
    use :: parse;
    use ::parse::{Parser,BlockKind,SrcBlock,LogicKind,SrcKind,VarKind};

    fn test_block () -> &'static str {
        "root\n
    unequipped !some_item\n
    has_weight some_weight < 5.0\n
\n
    if unequipped \"you're looking for something?\"\n
\n
    if all \"welcome, \nlook around\"\n
;"
    }

    #[test]
    fn parse_block() {
        let src = test_block();
        let block = parse::Parser::parse_blocks(src);

        let block_ = [BlockKind::Src(
            SrcBlock {
                name: "root".to_owned(),
                src: vec![SrcKind::Logic("unequipped".to_owned(),
                                         LogicKind::IsNot("some_item".to_owned())),

                          SrcKind::Logic("has_weight".to_owned(),
                                         LogicKind::LT("some_weight".to_owned(), 5.0f32)),

                          SrcKind::Return(VarKind::String("welcome, \nlook around".to_owned()))]
            })];

        for (n,n_) in block.iter().zip(block_.iter()) {
            assert_eq!(n,n_);
        }
    }
}
<commit_msg>update test<commit_after>pub mod parse;

#[cfg(test)]
mod tests {
    use ::parse::{Parser,BlockKind,SrcBlock,
                  LogicKind,SrcKind,VarKind,ExpectKind};

    fn test_block () -> &'static str {
        "root\n
    unequipped !some_item\n
    has_weight some_weight < 5.0\n
\n
    if unequipped \"you're looking for something?\"\n
\n
    if all \"welcome, \nlook around\"\n
;"
    }

    #[test]
    fn parse_block() {
        let src = test_block();
        let block = Parser::parse_blocks(src);

        let block_ = [BlockKind::Src(
            SrcBlock {
                name: "root".to_owned(),
                src: vec![SrcKind::Logic("unequipped".to_owned(),
                                         LogicKind::IsNot("some_item".to_owned())),

                          SrcKind::Logic("has_weight".to_owned(),
                                         LogicKind::LT("some_weight".to_owned(), 5.0f32)),

                          SrcKind::If(ExpectKind::Ref("unequipped".to_owned()),
                                      VarKind::String("you're looking for something?".to_owned())),
                          SrcKind::If(ExpectKind::All,
                                      VarKind::String("welcome, \nlook around".to_owned()))]
            })];

        for (n,n_) in block.iter().zip(block_.iter()) {
            assert_eq!(n,n_);
        }
    }
}
<|endoftext|>