<commit_before>use std::convert::From;

use binding::class;
use binding::global::rb_cObject;
use binding::util as binding_util;
use types::{Callback, Value, ValueType};
use util;

use {AnyObject, Object, VerifiedObject};

/// `Class`
#[derive(Debug, PartialEq)]
pub struct Class {
    value: Value,
}

impl Class {
    /// Creates a new `Class`.
    ///
    /// `superclass` can receive the following values:
    ///
    ///  - `None` to inherit from `Object` class
    ///     (standard Ruby behavior when superclass is not given explicitly);
    ///  - `Some(&class)` to inherit from the given class
    ///
    /// # Examples
    ///
    /// ```
    /// use ruru::{Class, VM};
    /// # VM::init();
    ///
    /// let new_class = Class::new("NewClass", None);
    /// assert_eq!(new_class, Class::from_existing("NewClass"));
    ///
    /// let another_class = Class::new("AnotherClass", Some(&new_class));
    /// assert_eq!(another_class, Class::from_existing("AnotherClass"));
    /// ```
    ///
    /// Ruby:
    ///
    /// ```ruby
    /// class Hello
    /// end
    ///
    /// # or
    ///
    /// Hello = Class.new
    /// ```
    pub fn new(name: &str, superclass: Option<&Self>) -> Self {
        let superclass = Self::superclass_to_value(superclass);

        Class { value: class::define_class(name, superclass) }
    }

    // TODO: replace rb_cObject with optional class
    /// Retrieves an existing `Class` object.
    ///
    /// # Examples
    ///
    /// ```
    /// use ruru::{Class, VM};
    /// # VM::init();
    ///
    /// let class = Class::new("Hello", None);
    ///
    /// assert_eq!(class, Class::from_existing("Hello"));
    /// ```
    ///
    /// Ruby:
    ///
    /// ```ruby
    /// Hello
    ///
    /// # or
    ///
    /// Object.const_get('Hello')
    /// ```
    pub fn from_existing(name: &str) -> Self {
        Class { value: binding_util::get_constant(name, rb_cObject) }
    }

    /// Creates a new instance of `Class`
    ///
    /// Arguments must be passed as a vector of `AnyObject` (see example).
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use ruru::{Class, Fixnum, Object};
    ///
    /// // Without arguments
    /// Class::from_existing("Hello").new_instance(vec![]);
    ///
    /// // With arguments passing arguments to constructor
    /// let arguments = vec![
    ///     Fixnum::new(1).to_any_object(),
    ///     Fixnum::new(2).to_any_object()
    /// ];
    ///
    /// Class::from_existing("Worker").new_instance(arguments);
    /// ```
    ///
    /// Ruby:
    ///
    /// ```ruby
    /// Hello.new
    ///
    /// Worker.new(1, 2)
    /// ```
    pub fn new_instance(&self, arguments: Vec<AnyObject>) -> AnyObject {
        let (argc, argv) = util::create_arguments(arguments);
        let instance = class::new_instance(self.value(), argc, argv.as_ptr());

        AnyObject::from(instance)
    }

    /// Returns a superclass of the current class
    ///
    /// # Examples
    ///
    /// ```
    /// use ruru::{Class, Object, VM};
    /// # VM::init();
    ///
    /// assert_eq!(
    ///     Class::from_existing("Array").superclass(),
    ///     Some(Class::from_existing("Object"))
    /// );
    ///
    /// assert_eq!(Class::from_existing("BasicObject").superclass(), None);
    /// ```
    pub fn superclass(&self) -> Option<Class> {
        let superclass_value = class::superclass(self.value());

        match superclass_value.is_nil() {
            true => None,
            false => Some(Self::from(superclass_value))
        }
    }

    /// Wraps calls to a class.
    ///
    /// Used to have Ruby-like class definition DSL.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// #[macro_use] extern crate ruru;
    ///
    /// use ruru::{AnyObject, Class, Fixnum, RString};
    ///
    /// class!(Hello);
    /// class!(Nested);
    ///
    /// methods!(
    ///     Hello,
    ///     itself,
    ///
    ///     fn greeting() -> RString {
    ///         RString::new("Greeting from class")
    ///     }
    ///
    ///     fn many_greetings() -> RString {
    ///         RString::new("Many greetings from instance")
    ///     }
    /// );
    ///
    /// methods!(
    ///     Nested,
    ///     itself,
    ///
    ///     fn nested_greeting() -> RString {
    ///         RString::new("Greeting from nested class")
    ///     }
    /// );
    ///
    /// fn main() {
    ///     Class::new("Hello", None).define(|itself| {
    ///         itself.def_self("greeting", greeting);
    ///         itself.def("many_greetings", many_greetings);
    ///
    ///         itself.define_nested_class("Nested", None).define(|itself| {
    ///             itself.def_self("nested_greeting", nested_greeting);
    ///         });
    ///     });
    /// }
    /// ```
    ///
    /// Ruby:
    ///
    /// ```ruby
    /// class Hello
    ///   def self.greeting
    ///     'Greeting from class'
    ///   end
    ///
    ///   def many_greetings
    ///     'Many greetings from instance'
    ///   end
    ///
    ///   class Nested
    ///     def self.nested_greeting
    ///       'Greeting from nested class'
    ///     end
    ///   end
    /// end
    /// ```
    pub fn define<F: Fn(&mut Self)>(&mut self, f: F) -> &Self {
        f(self);

        self
    }

    /// Defines an instance method for given class.
    ///
    /// # Arguments
    ///
    /// - `name` is name of the Ruby method
    ///
    /// - `callback` is the function which will be called by MRI when the method is called inside
    ///    Ruby
    ///
    /// ## Callback
    ///
    /// Use `methods!` macro instead of manually creating callbacks.
    ///
    /// `callback` must have the following signature:
    ///
    /// `pub type Callback<I: Object, O: Object> = extern fn(Argc, *const AnyObject, I) -> O;`
    ///
    /// The function must also have `#[no_mangle]` attribute.
    ///
    /// - First argument `argc: Argc` will receive the number of arguments passed to method
    ///
    /// - Second argument `argv: *const AnyObject` will receive the arguments passed to the method
    ///   as `AnyObject`s
    ///
    /// - Third value `itself: I` will receive the object which got the method call (Ruby `self`).
    ///   Can be any type which implements `Object` trait
    ///
    /// - Return type can be any type which implements `Object` trait
    ///
    /// If you need to receive and use arguments which are passed to the method, you can use
    /// `VM::parse_arguments()` function which processes a pointer to array (`*const AnyObject`)
    /// to a vector of `AnyObject`s (`Vec<AnyObject>`), see examples.
    ///
    /// # Examples
    ///
    /// ## Method receives no arguments
    ///
    /// In this case `argc` and `argv` can be ignored.
    ///
    /// Famous `String#blank?` example
    ///
    /// ```no_run
    /// use ruru::types::Argc;
    /// use ruru::{AnyObject, Boolean, Class, Object, RString};
    ///
    /// #[no_mangle]
    /// pub extern fn string_blank(_: Argc, _: *const AnyObject, itself: RString) -> Boolean {
    ///     Boolean::new(itself.to_string().chars().all(|c| c.is_whitespace()))
    /// }
    ///
    /// fn main() {
    ///     Class::from_existing("String").define_method("blank?", string_blank);
    /// }
    /// ```
    ///
    /// Ruby:
    ///
    /// ```ruby
    /// class String
    ///   def blank?
    ///     # simplified
    ///     self.chars.all? { |c| c == ' ' }
    ///   end
    /// end
    /// ```
    ///
    /// ## Method receives arguments
    ///
    /// Arguments should be processed to vector using `VM::parse_arguments()`.
    ///
    /// ```no_run
    /// use ruru::types::Argc;
    /// use ruru::{AnyObject, Boolean, Class, Object, RString, VM};
    ///
    /// #[no_mangle]
    /// pub extern fn string_eq(argc: Argc, argv: *const AnyObject, itself: RString) -> Boolean {
    ///     let argv = VM::parse_arguments(argc, argv);
    ///     let other_string = argv[0].try_convert_to::<RString>().unwrap();
    ///
    ///     Boolean::new(itself.to_string() == other_string.to_string())
    /// }
    ///
    /// fn main() {
    ///     Class::from_existing("String").define_method("==", string_eq);
    /// }
    /// ```
    ///
    /// Ruby:
    ///
    /// ```ruby
    /// class String
    ///   def ==(other_string)
    ///     # simplified
    ///     self.chars == other_string.chars
    ///   end
    /// end
    /// ```
    pub fn define_method<I: Object, O: Object>(&mut self, name: &str, callback: Callback<I, O>) {
        class::define_method(self.value, name, callback);
    }

    /// Defines a class method for given class.
    ///
    /// Function has the same requirements as `define_method`.
    /// Also the same rules are applied for `callback` (see above).
    ///
    /// Use `methods!` macro instead of manually creating callbacks.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use ruru::types::Argc;
    /// use ruru::{AnyObject, Class, Object, RString, Symbol, VM};
    ///
    /// #[no_mangle]
    /// pub extern fn symbol_from_string(argc: Argc,
    ///                                  argv: *const AnyObject,
    ///                                  itself: Class) -> Symbol {
    ///     let argv = VM::parse_arguments(argc, argv);
    ///     let string = argv[0].try_convert_to::<RString>().unwrap();
    ///
    ///     Symbol::new(&string.to_string())
    /// }
    ///
    /// fn main() {
    ///     Class::from_existing("Symbol")
    ///         .define_singleton_method("from_string", symbol_from_string);
    /// }
    /// ```
    ///
    /// Ruby:
    ///
    /// ```ruby
    /// class Symbol
    ///   def self.from_string(string)
    ///     # simplified
    ///     string.to_sym
    ///   end
    /// end
    /// ```
    pub fn define_singleton_method<I: Object, O: Object>(&mut self,
                                                         name: &str,
                                                         callback: Callback<I, O>) {
        class::define_singleton_method(self.value, name, callback);
    }

    /// Creates a new `Class` nested into current class.
    ///
    /// `superclass` can receive the following values:
    ///
    ///  - `None` to inherit from `Object` class
    ///     (standard Ruby behavior when superclass is not given explicitly);
    ///  - `Some(&class)` to inherit from the given class
    ///
    /// # Examples
    ///
    /// ```
    /// use ruru::{Class, VM};
    /// # VM::init();
    ///
    /// Class::new("Outer", None).define(|itself| {
    ///     itself.define_nested_class("Inner", None);
    /// });
    ///
    ///
    /// ```
    ///
    /// Ruby:
    ///
    /// ```ruby
    /// class Outer
    ///   class Inner
    ///   end
    /// end
    /// ```
    pub fn define_nested_class(&mut self, name: &str, superclass: Option<&Class>) -> Self {
        let superclass = Self::superclass_to_value(superclass);

        Class { value: class::define_nested_class(self.value(), name, superclass) }
    }

    /// An alias for `define_method` (similar to Ruby syntax `def some_method`).
    pub fn def<I: Object, O: Object>(&mut self, name: &str, callback: Callback<I, O>) {
        self.define_method(name, callback);
    }

    /// An alias for `define_singleton_method` (similar to Ruby `def self.some_method`).
    pub fn def_self<I: Object, O: Object>(&mut self, name: &str, callback: Callback<I, O>) {
        self.define_singleton_method(name, callback);
    }

    fn superclass_to_value(superclass: Option<&Class>) -> Value {
        match superclass {
            Some(class) => class.value(),
            None => rb_cObject
        }
    }
}

impl From<Value> for Class {
    fn from(value: Value) -> Self {
        Class { value: value }
    }
}

impl Object for Class {
    fn value(&self) -> Value {
        self.value
    }
}

impl VerifiedObject for Class {
    fn is_correct_type<T: Object>(object: &T) -> bool {
        object.value().ty() == ValueType::Class
    }

    fn error_message() -> String {
        "Error converting to Class".to_string()
    }
}
<commit_msg>Update examples for Class::new()<commit_after>use std::convert::From;

use binding::class;
use binding::global::rb_cObject;
use binding::util as binding_util;
use types::{Callback, Value, ValueType};
use util;

use {AnyObject, Object, VerifiedObject};

/// `Class`
#[derive(Debug, PartialEq)]
pub struct Class {
    value: Value,
}

impl Class {
    /// Creates a new `Class`.
    ///
    /// `superclass` can receive the following values:
    ///
    ///  - `None` to inherit from `Object` class
    ///     (standard Ruby behavior when superclass is not given explicitly);
    ///  - `Some(&Class)` to inherit from the given class
    ///
    /// # Examples
    ///
    /// ```
    /// use ruru::{Class, VM};
    /// # VM::init();
    ///
    /// let basic_record_class = Class::new("BasicRecord", None);
    ///
    /// assert_eq!(basic_record_class, Class::from_existing("BasicRecord"));
    /// assert_eq!(basic_record_class.superclass(), Some(Class::from_existing("Object")));
    ///
    /// let record_class = Class::new("Record", Some(&basic_record_class));
    ///
    /// assert_eq!(record_class, Class::from_existing("Record"));
    /// assert_eq!(record_class.superclass(), Some(Class::from_existing("BasicRecord")));
    /// ```
    ///
    /// Ruby:
    ///
    /// ```ruby
    /// class BasicRecord
    /// end
    ///
    /// class Record < BasicRecord
    /// end
    ///
    /// BasicRecord.superclass == Object
    /// Record.superclass == BasicRecord
    /// ```
    pub fn new(name: &str, superclass: Option<&Self>) -> Self {
        let superclass = Self::superclass_to_value(superclass);

        Class { value: class::define_class(name, superclass) }
    }

    // TODO: replace rb_cObject with optional class
    /// Retrieves an existing `Class` object.
    ///
    /// # Examples
    ///
    /// ```
    /// use ruru::{Class, VM};
    /// # VM::init();
    ///
    /// let class = Class::new("Record", None);
    ///
    /// assert_eq!(class, Class::from_existing("Record"));
    /// ```
    ///
    /// Ruby:
    ///
    /// ```ruby
    /// class Record
    /// end
    ///
    /// # get class
    ///
    /// Record
    ///
    /// # or
    ///
    /// Object.const_get('Record')
    /// ```
    pub fn from_existing(name: &str) -> Self {
        Class { value: binding_util::get_constant(name, rb_cObject) }
    }

    /// Creates a new instance of `Class`
    ///
    /// Arguments must be passed as a vector of `AnyObject` (see example).
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use ruru::{Class, Fixnum, Object};
    ///
    /// // Without arguments
    /// Class::from_existing("Hello").new_instance(vec![]);
    ///
    /// // With arguments passing arguments to constructor
    /// let arguments = vec![
    ///     Fixnum::new(1).to_any_object(),
    ///     Fixnum::new(2).to_any_object()
    /// ];
    ///
    /// Class::from_existing("Worker").new_instance(arguments);
    /// ```
    ///
    /// Ruby:
    ///
    /// ```ruby
    /// Hello.new
    ///
    /// Worker.new(1, 2)
    /// ```
    pub fn new_instance(&self, arguments: Vec<AnyObject>) -> AnyObject {
        let (argc, argv) = util::create_arguments(arguments);
        let instance = class::new_instance(self.value(), argc, argv.as_ptr());

        AnyObject::from(instance)
    }

    /// Returns a superclass of the current class
    ///
    /// # Examples
    ///
    /// ```
    /// use ruru::{Class, Object, VM};
    /// # VM::init();
    ///
    /// assert_eq!(
    ///     Class::from_existing("Array").superclass(),
    ///     Some(Class::from_existing("Object"))
    /// );
    ///
    /// assert_eq!(Class::from_existing("BasicObject").superclass(), None);
    /// ```
    pub fn superclass(&self) -> Option<Class> {
        let superclass_value = class::superclass(self.value());

        match superclass_value.is_nil() {
            true => None,
            false => Some(Self::from(superclass_value))
        }
    }

    /// Wraps calls to a class.
    ///
    /// Used to have Ruby-like class definition DSL.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// #[macro_use] extern crate ruru;
    ///
    /// use ruru::{AnyObject, Class, Fixnum, RString};
    ///
    /// class!(Hello);
    /// class!(Nested);
    ///
    /// methods!(
    ///     Hello,
    ///     itself,
    ///
    ///     fn greeting() -> RString {
    ///         RString::new("Greeting from class")
    ///     }
    ///
    ///     fn many_greetings() -> RString {
    ///         RString::new("Many greetings from instance")
    ///     }
    /// );
    ///
    /// methods!(
    ///     Nested,
    ///     itself,
    ///
    ///     fn nested_greeting() -> RString {
    ///         RString::new("Greeting from nested class")
    ///     }
    /// );
    ///
    /// fn main() {
    ///     Class::new("Hello", None).define(|itself| {
    ///         itself.def_self("greeting", greeting);
    ///         itself.def("many_greetings", many_greetings);
    ///
    ///         itself.define_nested_class("Nested", None).define(|itself| {
    ///             itself.def_self("nested_greeting", nested_greeting);
    ///         });
    ///     });
    /// }
    /// ```
    ///
    /// Ruby:
    ///
    /// ```ruby
    /// class Hello
    ///   def self.greeting
    ///     'Greeting from class'
    ///   end
    ///
    ///   def many_greetings
    ///     'Many greetings from instance'
    ///   end
    ///
    ///   class Nested
    ///     def self.nested_greeting
    ///       'Greeting from nested class'
    ///     end
    ///   end
    /// end
    /// ```
    pub fn define<F: Fn(&mut Self)>(&mut self, f: F) -> &Self {
        f(self);

        self
    }

    /// Defines an instance method for given class.
    ///
    /// # Arguments
    ///
    /// - `name` is name of the Ruby method
    ///
    /// - `callback` is the function which will be called by MRI when the method is called inside
    ///    Ruby
    ///
    /// ## Callback
    ///
    /// Use `methods!` macro instead of manually creating callbacks.
    ///
    /// `callback` must have the following signature:
    ///
    /// `pub type Callback<I: Object, O: Object> = extern fn(Argc, *const AnyObject, I) -> O;`
    ///
    /// The function must also have `#[no_mangle]` attribute.
    ///
    /// - First argument `argc: Argc` will receive the number of arguments passed to method
    ///
    /// - Second argument `argv: *const AnyObject` will receive the arguments passed to the method
    ///   as `AnyObject`s
    ///
    /// - Third value `itself: I` will receive the object which got the method call (Ruby `self`).
    ///   Can be any type which implements `Object` trait
    ///
    /// - Return type can be any type which implements `Object` trait
    ///
    /// If you need to receive and use arguments which are passed to the method, you can use
    /// `VM::parse_arguments()` function which processes a pointer to array (`*const AnyObject`)
    /// to a vector of `AnyObject`s (`Vec<AnyObject>`), see examples.
    ///
    /// # Examples
    ///
    /// ## Method receives no arguments
    ///
    /// In this case `argc` and `argv` can be ignored.
    ///
    /// Famous `String#blank?` example
    ///
    /// ```no_run
    /// use ruru::types::Argc;
    /// use ruru::{AnyObject, Boolean, Class, Object, RString};
    ///
    /// #[no_mangle]
    /// pub extern fn string_blank(_: Argc, _: *const AnyObject, itself: RString) -> Boolean {
    ///     Boolean::new(itself.to_string().chars().all(|c| c.is_whitespace()))
    /// }
    ///
    /// fn main() {
    ///     Class::from_existing("String").define_method("blank?", string_blank);
    /// }
    /// ```
    ///
    /// Ruby:
    ///
    /// ```ruby
    /// class String
    ///   def blank?
    ///     # simplified
    ///     self.chars.all? { |c| c == ' ' }
    ///   end
    /// end
    /// ```
    ///
    /// ## Method receives arguments
    ///
    /// Arguments should be processed to vector using `VM::parse_arguments()`.
    ///
    /// ```no_run
    /// use ruru::types::Argc;
    /// use ruru::{AnyObject, Boolean, Class, Object, RString, VM};
    ///
    /// #[no_mangle]
    /// pub extern fn string_eq(argc: Argc, argv: *const AnyObject, itself: RString) -> Boolean {
    ///     let argv = VM::parse_arguments(argc, argv);
    ///     let other_string = argv[0].try_convert_to::<RString>().unwrap();
    ///
    ///     Boolean::new(itself.to_string() == other_string.to_string())
    /// }
    ///
    /// fn main() {
    ///     Class::from_existing("String").define_method("==", string_eq);
    /// }
    /// ```
    ///
    /// Ruby:
    ///
    /// ```ruby
    /// class String
    ///   def ==(other_string)
    ///     # simplified
    ///     self.chars == other_string.chars
    ///   end
    /// end
    /// ```
    pub fn define_method<I: Object, O: Object>(&mut self, name: &str, callback: Callback<I, O>) {
        class::define_method(self.value, name, callback);
    }

    /// Defines a class method for given class.
    ///
    /// Function has the same requirements as `define_method`.
    /// Also the same rules are applied for `callback` (see above).
    ///
    /// Use `methods!` macro instead of manually creating callbacks.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use ruru::types::Argc;
    /// use ruru::{AnyObject, Class, Object, RString, Symbol, VM};
    ///
    /// #[no_mangle]
    /// pub extern fn symbol_from_string(argc: Argc,
    ///                                  argv: *const AnyObject,
    ///                                  itself: Class) -> Symbol {
    ///     let argv = VM::parse_arguments(argc, argv);
    ///     let string = argv[0].try_convert_to::<RString>().unwrap();
    ///
    ///     Symbol::new(&string.to_string())
    /// }
    ///
    /// fn main() {
    ///     Class::from_existing("Symbol")
    ///         .define_singleton_method("from_string", symbol_from_string);
    /// }
    /// ```
    ///
    /// Ruby:
    ///
    /// ```ruby
    /// class Symbol
    ///   def self.from_string(string)
    ///     # simplified
    ///     string.to_sym
    ///   end
    /// end
    /// ```
    pub fn define_singleton_method<I: Object, O: Object>(&mut self,
                                                         name: &str,
                                                         callback: Callback<I, O>) {
        class::define_singleton_method(self.value, name, callback);
    }

    /// Creates a new `Class` nested into current class.
    ///
    /// `superclass` can receive the following values:
    ///
    ///  - `None` to inherit from `Object` class
    ///     (standard Ruby behavior when superclass is not given explicitly);
    ///  - `Some(&class)` to inherit from the given class
    ///
    /// # Examples
    ///
    /// ```
    /// use ruru::{Class, VM};
    /// # VM::init();
    ///
    /// Class::new("Outer", None).define(|itself| {
    ///     itself.define_nested_class("Inner", None);
    /// });
    ///
    ///
    /// ```
    ///
    /// Ruby:
    ///
    /// ```ruby
    /// class Outer
    ///   class Inner
    ///   end
    /// end
    /// ```
    pub fn define_nested_class(&mut self, name: &str, superclass: Option<&Class>) -> Self {
        let superclass = Self::superclass_to_value(superclass);

        Class { value: class::define_nested_class(self.value(), name, superclass) }
    }

    /// An alias for `define_method` (similar to Ruby syntax `def some_method`).
    pub fn def<I: Object, O: Object>(&mut self, name: &str, callback: Callback<I, O>) {
        self.define_method(name, callback);
    }

    /// An alias for `define_singleton_method` (similar to Ruby `def self.some_method`).
    pub fn def_self<I: Object, O: Object>(&mut self, name: &str, callback: Callback<I, O>) {
        self.define_singleton_method(name, callback);
    }

    fn superclass_to_value(superclass: Option<&Class>) -> Value {
        match superclass {
            Some(class) => class.value(),
            None => rb_cObject
        }
    }
}

impl From<Value> for Class {
    fn from(value: Value) -> Self {
        Class { value: value }
    }
}

impl Object for Class {
    fn value(&self) -> Value {
        self.value
    }
}

impl VerifiedObject for Class {
    fn is_correct_type<T: Object>(object: &T) -> bool {
        object.value().ty() == ValueType::Class
    }

    fn error_message() -> String {
        "Error converting to Class".to_string()
    }
}
<|endoftext|>


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

//! ## The Cleanup module
//!
//! The cleanup module tracks what values need to be cleaned up as scopes
//! are exited, either via panic or just normal control flow.
//!
//! Cleanup items can be scheduled into any of the scopes on the stack.
//! Typically, when a scope is finished, we generate the cleanup code. This
//! corresponds to a normal exit from a block (for example, an expression
//! completing evaluation successfully without panic).

use llvm::{BasicBlockRef, ValueRef};
use base;
use common::{BlockAndBuilder, FunctionContext, Funclet};
use glue;
use type_::Type;
use value::Value;
use rustc::ty::Ty;

pub struct CleanupScope<'tcx> {
    // Cleanup to run upon scope exit.
    cleanup: Option<DropValue<'tcx>>,

    // Computed on creation if compiling with landing pads (!sess.no_landing_pads)
    pub landing_pad: Option<BasicBlockRef>,
}

#[derive(Copy, Clone)]
pub struct DropValue<'tcx> {
    val: ValueRef,
    ty: Ty<'tcx>,
    skip_dtor: bool,
}

impl<'tcx> DropValue<'tcx> {
    fn trans<'a>(&self, funclet: Option<&'a Funclet>, bcx: &BlockAndBuilder<'a, 'tcx>) {
        glue::call_drop_glue(bcx, self.val, self.ty, self.skip_dtor, funclet)
    }

    /// Creates a landing pad for the top scope. The landing pad will perform all cleanups necessary
    /// for an unwind and then `resume` to continue error propagation:
    ///
    ///     landing_pad -> ... cleanups ... -> [resume]
    ///
    /// This should only be called once per function, as it creates an alloca for the landingpad.
    fn get_landing_pad<'a>(&self, fcx: &FunctionContext<'a, 'tcx>) -> BasicBlockRef {
        debug!("get_landing_pad");
        let bcx = fcx.build_new_block("cleanup_unwind");
        let llpersonality = bcx.ccx.eh_personality();
        bcx.set_personality_fn(llpersonality);

        if base::wants_msvc_seh(fcx.ccx.sess()) {
            // Insert cleanup instructions into the cleanup block
            let funclet = Some(Funclet::new(bcx.cleanup_pad(None, &[])));
            self.trans(funclet.as_ref(), &bcx);

            bcx.cleanup_ret(bcx.cleanup_pad(None, &[]), None);
        } else {
            // The landing pad return type (the type being propagated). Not sure
            // what this represents but it's determined by the personality
            // function and this is what the EH proposal example uses.
            let llretty = Type::struct_(fcx.ccx, &[Type::i8p(fcx.ccx), Type::i32(fcx.ccx)], false);

            // The only landing pad clause will be 'cleanup'
            let llretval = bcx.landing_pad(llretty, llpersonality, 1, bcx.fcx().llfn);

            // The landing pad block is a cleanup
            bcx.set_cleanup(llretval);

            // Insert cleanup instructions into the cleanup block
            self.trans(None, &bcx);

            if !bcx.sess().target.target.options.custom_unwind_resume {
                bcx.resume(llretval);
            } else {
                let exc_ptr = bcx.extract_value(llretval, 0);
                bcx.call(bcx.ccx.eh_unwind_resume(), &[exc_ptr], None);
                bcx.unreachable();
            }
        }

        bcx.llbb()
    }
}

impl<'a, 'tcx> FunctionContext<'a, 'tcx> {
    /// Schedules a (deep) drop of `val`, which is a pointer to an instance of `ty`
    pub fn schedule_drop_mem(&self, val: ValueRef, ty: Ty<'tcx>) -> CleanupScope<'tcx> {
        if !self.ccx.shared().type_needs_drop(ty) { return CleanupScope::noop(); }
        let drop = DropValue {
            val: val,
            ty: ty,
            skip_dtor: false,
        };

        debug!("schedule_drop_mem(val={:?}, ty={:?}) skip_dtor={}", Value(val), ty, drop.skip_dtor);

        CleanupScope::new(self, drop)
    }

    /// Issue #23611: Schedules a (deep) drop of the contents of
    /// `val`, which is a pointer to an instance of struct/enum type
    /// `ty`. The scheduled code handles extracting the discriminant
    /// and dropping the contents associated with that variant
    /// *without* executing any associated drop implementation.
    pub fn schedule_drop_adt_contents(&self, val: ValueRef, ty: Ty<'tcx>) -> CleanupScope<'tcx> {
        // `if` below could be "!contents_needs_drop"; skipping drop
        // is just an optimization, so sound to be conservative.
        if !self.ccx.shared().type_needs_drop(ty) { return CleanupScope::noop(); }

        let drop = DropValue {
            val: val,
            ty: ty,
            skip_dtor: true,
        };

        debug!("schedule_drop_adt_contents(val={:?}, ty={:?}) skip_dtor={}",
               Value(val), ty, drop.skip_dtor);

        CleanupScope::new(self, drop)
    }
}

impl<'tcx> CleanupScope<'tcx> {
    fn new<'a>(fcx: &FunctionContext<'a, 'tcx>, drop_val: DropValue<'tcx>) -> CleanupScope<'tcx> {
        CleanupScope {
            cleanup: Some(drop_val),
            landing_pad: if !fcx.ccx.sess().no_landing_pads() {
                Some(drop_val.get_landing_pad(fcx))
            } else {
                None
            },
        }
    }

    pub fn noop() -> CleanupScope<'tcx> {
        CleanupScope {
            cleanup: None,
            landing_pad: None,
        }
    }

    pub fn trans<'a>(self, bcx: &'a BlockAndBuilder<'a, 'tcx>) {
        if let Some(cleanup) = self.cleanup {
            cleanup.trans(None, &bcx);
        }
    }
}
<commit_msg>Reuse cleanup pad declared at start of block.<commit_after>// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! ## The Cleanup module
//!
//! The cleanup module tracks what values need to be cleaned up as scopes
//! are exited, either via panic or just normal control flow.
//!
//! Cleanup items can be scheduled into any of the scopes on the stack.
//! Typically, when a scope is finished, we generate the cleanup code. This
//! corresponds to a normal exit from a block (for example, an expression
//! completing evaluation successfully without panic).

use llvm::{BasicBlockRef, ValueRef};
use base;
use common::{BlockAndBuilder, FunctionContext, Funclet};
use glue;
use type_::Type;
use value::Value;
use rustc::ty::Ty;

pub struct CleanupScope<'tcx> {
    // Cleanup to run upon scope exit.
    cleanup: Option<DropValue<'tcx>>,

    // Computed on creation if compiling with landing pads (!sess.no_landing_pads)
    pub landing_pad: Option<BasicBlockRef>,
}

#[derive(Copy, Clone)]
pub struct DropValue<'tcx> {
    val: ValueRef,
    ty: Ty<'tcx>,
    skip_dtor: bool,
}

impl<'tcx> DropValue<'tcx> {
    fn trans<'a>(&self, funclet: Option<&'a Funclet>, bcx: &BlockAndBuilder<'a, 'tcx>) {
        glue::call_drop_glue(bcx, self.val, self.ty, self.skip_dtor, funclet)
    }

    /// Creates a landing pad for the top scope. The landing pad will perform all cleanups necessary
    /// for an unwind and then `resume` to continue error propagation:
    ///
    ///     landing_pad -> ... cleanups ... -> [resume]
    ///
    /// This should only be called once per function, as it creates an alloca for the landingpad.
    fn get_landing_pad<'a>(&self, fcx: &FunctionContext<'a, 'tcx>) -> BasicBlockRef {
        debug!("get_landing_pad");
        let bcx = fcx.build_new_block("cleanup_unwind");
        let llpersonality = bcx.ccx.eh_personality();
        bcx.set_personality_fn(llpersonality);

        if base::wants_msvc_seh(fcx.ccx.sess()) {
            let pad = bcx.cleanup_pad(None, &[]);
            let funclet = Some(Funclet::new(pad));
            self.trans(funclet.as_ref(), &bcx);

            bcx.cleanup_ret(pad, None);
        } else {
            // The landing pad return type (the type being propagated). Not sure
            // what this represents but it's determined by the personality
            // function and this is what the EH proposal example uses.
            let llretty = Type::struct_(fcx.ccx, &[Type::i8p(fcx.ccx), Type::i32(fcx.ccx)], false);

            // The only landing pad clause will be 'cleanup'
            let llretval = bcx.landing_pad(llretty, llpersonality, 1, bcx.fcx().llfn);

            // The landing pad block is a cleanup
            bcx.set_cleanup(llretval);

            // Insert cleanup instructions into the cleanup block
            self.trans(None, &bcx);

            if !bcx.sess().target.target.options.custom_unwind_resume {
                bcx.resume(llretval);
            } else {
                let exc_ptr = bcx.extract_value(llretval, 0);
                bcx.call(bcx.ccx.eh_unwind_resume(), &[exc_ptr], None);
                bcx.unreachable();
            }
        }

        bcx.llbb()
    }
}

impl<'a, 'tcx> FunctionContext<'a, 'tcx> {
    /// Schedules a (deep) drop of `val`, which is a pointer to an instance of `ty`
    pub fn schedule_drop_mem(&self, val: ValueRef, ty: Ty<'tcx>) -> CleanupScope<'tcx> {
        if !self.ccx.shared().type_needs_drop(ty) { return CleanupScope::noop(); }
        let drop = DropValue {
            val: val,
            ty: ty,
            skip_dtor: false,
        };

        debug!("schedule_drop_mem(val={:?}, ty={:?}) skip_dtor={}", Value(val), ty, drop.skip_dtor);

        CleanupScope::new(self, drop)
    }

    /// Issue #23611: Schedules a (deep) drop of the contents of
    /// `val`, which is a pointer to an instance of struct/enum type
    /// `ty`. The scheduled code handles extracting the discriminant
    /// and dropping the contents associated with that variant
    /// *without* executing any associated drop implementation.
    pub fn schedule_drop_adt_contents(&self, val: ValueRef, ty: Ty<'tcx>) -> CleanupScope<'tcx> {
        // `if` below could be "!contents_needs_drop"; skipping drop
        // is just an optimization, so sound to be conservative.
        if !self.ccx.shared().type_needs_drop(ty) { return CleanupScope::noop(); }

        let drop = DropValue {
            val: val,
            ty: ty,
            skip_dtor: true,
        };

        debug!("schedule_drop_adt_contents(val={:?}, ty={:?}) skip_dtor={}",
               Value(val), ty, drop.skip_dtor);

        CleanupScope::new(self, drop)
    }
}

impl<'tcx> CleanupScope<'tcx> {
    fn new<'a>(fcx: &FunctionContext<'a, 'tcx>, drop_val: DropValue<'tcx>) -> CleanupScope<'tcx> {
        CleanupScope {
            cleanup: Some(drop_val),
            landing_pad: if !fcx.ccx.sess().no_landing_pads() {
                Some(drop_val.get_landing_pad(fcx))
            } else {
                None
            },
        }
    }

    pub fn noop() -> CleanupScope<'tcx> {
        CleanupScope {
            cleanup: None,
            landing_pad: None,
        }
    }

    pub fn trans<'a>(self, bcx: &'a BlockAndBuilder<'a, 'tcx>) {
        if let Some(cleanup) = self.cleanup {
            cleanup.trans(None, &bcx);
        }
    }
}
<|endoftext|>


<commit_before>// Regression test for rust-lang/rust#63154
//
// Before, we would ICE after faiing to normalize the destination type
// when checking call destinations and also when checking MIR
// assignment statements.

// check-pass

trait HasAssocType {
    type Inner;
}

impl HasAssocType for () {
    type Inner = ();
}

trait Tr<I, T>: Fn(I) -> Option<T> {}
impl<I, T, Q: Fn(I) -> Option<T>> Tr<I, T> for Q {}

fn f<T: HasAssocType>() -> impl Tr<T, T::Inner> {
    |_| None
}

fn g<T, Y>(f: impl Tr<T, Y>) -> impl Tr<T, Y> {
    f
}

fn h() {
    g(f())(());
}

fn main() {
    h();
}
<commit_msg>Update src/test/ui/nll/issue-63154-normalize.rs<commit_after>// Regression test for rust-lang/rust#63154
//
// Before, we would ICE after failing to normalize the destination type
// when checking call destinations and also when checking MIR
// assignment statements.

// check-pass

trait HasAssocType {
    type Inner;
}

impl HasAssocType for () {
    type Inner = ();
}

trait Tr<I, T>: Fn(I) -> Option<T> {}
impl<I, T, Q: Fn(I) -> Option<T>> Tr<I, T> for Q {}

fn f<T: HasAssocType>() -> impl Tr<T, T::Inner> {
    |_| None
}

fn g<T, Y>(f: impl Tr<T, Y>) -> impl Tr<T, Y> {
    f
}

fn h() {
    g(f())(());
}

fn main() {
    h();
}
<|endoftext|>


<commit_before>use std::{convert};
use std::io::{Cursor, Read};

use byteorder::{self, ReadBytesExt};

use utils::{Integer, ObjectIdentifier};

#[derive(Debug, PartialEq, Eq)]
pub enum DeserializationError {
    UnexpectedTag,
    ShortData,
    ExtraData,
    IntegerOverflow,
    InvalidValue,
}

impl convert::From<byteorder::Error> for DeserializationError {
    fn from(e: byteorder::Error) -> DeserializationError {
        return match e {
            byteorder::Error::UnexpectedEOF => DeserializationError::ShortData,
            _ => panic!("Unexpected error!"),
        }
    }
}

pub type DeserializationResult<T> = Result<T, DeserializationError>;

fn _read_base128_int(reader: &mut Cursor<Vec<u8>>) -> DeserializationResult<u32> {
    let mut ret = 0u32;
    for _ in 0..4 {
        let b = try!(reader.read_u8());
        ret <<= 7;
        ret |= (b & 0x7f) as u32;
        if b & 0x80 == 0 {
            return Ok(ret);
        }
    }
    return Err(DeserializationError::InvalidValue);
}

pub struct Deserializer {
    reader: Cursor<Vec<u8>>,
}

impl Deserializer {
    pub fn new(data: Vec<u8>) -> Deserializer {
        return Deserializer{
            reader: Cursor::new(data),
        }
    }

    fn _read_length(&mut self) -> DeserializationResult<usize> {
        let b = try!(self.reader.read_u8());
        // TODO: handle lengths greater than 128
        assert!(b & 0x80 == 0);
        return Ok((b & 0x7f) as usize);
    }

    fn _read_with_tag<T, F>(&mut self, expected_tag: u8, body: F) -> DeserializationResult<T>
            where F: Fn(Vec<u8>) -> DeserializationResult<T> {
        let tag = try!(self.reader.read_u8());
        // TODO: only some of the bits in the first byte are for the tag
        if tag != expected_tag {
            return Err(DeserializationError::UnexpectedTag);
        }
        let length = try!(self._read_length());
        let mut data = vec![0; length];
        let n = self.reader.read(&mut data).unwrap();
        if n != length {
            return Err(DeserializationError::ShortData);
        }
        return body(data);
    }

    pub fn finish(self) -> DeserializationResult<()> {
        if self.reader.position() as usize != self.reader.get_ref().len() {
            return Err(DeserializationError::ExtraData);
        }
        return Ok(());
    }

    pub fn read_bool(&mut self) -> DeserializationResult<bool> {
        return self._read_with_tag(1, |data| {
            if data == b"\x00" {
                return Ok(false);
            } else if data == b"\xff" {
                return Ok(true)
            } else {
                return Err(DeserializationError::InvalidValue);
            }
        });
    }

    pub fn read_int<T>(&mut self) -> DeserializationResult<T> where T: Integer {
        return self._read_with_tag(2, |data| {
            if data.len() > 1 {
                match (data[0], data[1] & 0x80) {
                    (0xff, 0x80) | (0x00, 0x00) => return Err(DeserializationError::InvalidValue),
                    _ => {},
                }
            }
            return T::decode(data);
        });
    }

    pub fn read_octet_string(&mut self) -> DeserializationResult<Vec<u8>> {
        return self._read_with_tag(4, |data| {
            return Ok(data);
        });
    }

    pub fn read_object_identifier(&mut self) -> DeserializationResult<ObjectIdentifier> {
        return self._read_with_tag(6, |data| {
            if data.is_empty() {
                return Err(DeserializationError::InvalidValue);
            }
            let mut reader = Cursor::new(data);
            let mut s = vec![];
            let v = try!(_read_base128_int(&mut reader));

            if v < 80 {
                s.push(v / 40);
                s.push(v % 40);
            } else {
                s.push(2);
                s.push(v - 80);
            }

            while (reader.position() as usize) < reader.get_ref().len() {
                s.push(try!(_read_base128_int(&mut reader)));
            }

            return Ok(ObjectIdentifier::new(s).unwrap());
        });
    }

    pub fn read_sequence<F, T>(&mut self, v: F) -> DeserializationResult<T>
            where F: Fn(&mut Deserializer) -> DeserializationResult<T> {
        return self._read_with_tag(48, |data| {
            return from_vec(data, &v);
        });
    }
}

pub fn from_vec<F, T>(data: Vec<u8>, f: F) -> DeserializationResult<T>
        where F: Fn(&mut Deserializer) -> DeserializationResult<T> {
    let mut deserializer = Deserializer::new(data);
    let result = try!(f(&mut deserializer));
    try!(deserializer.finish());
    return Ok(result);
}

#[cfg(test)]
mod tests {
    use std;
    use std::{fmt};

    use utils::{ObjectIdentifier};
    use super::{Deserializer, DeserializationError, DeserializationResult, from_vec};

    fn assert_deserializes<T, F>(values: Vec<(DeserializationResult<T>, Vec<u8>)>, f: F)
            where T: Eq + fmt::Debug, F: Fn(&mut Deserializer) -> DeserializationResult<T> {
        for (expected, value) in values {
            let result = from_vec(value, &f);
            assert_eq!(result, expected);
        }
    }
    #[test]
    fn test_read_extra_data() {
        assert_deserializes(vec![
            (Err(DeserializationError::ExtraData), b"\x00".to_vec()),
        ], |_| {
            return Ok(());
        });
    }

    #[test]
    fn test_read_bool() {
        assert_deserializes(vec![
            (Ok(true), b"\x01\x01\xff".to_vec()),
            (Ok(false), b"\x01\x01\x00".to_vec()),
            (Err(DeserializationError::InvalidValue), b"\x01\x00".to_vec()),
            (Err(DeserializationError::InvalidValue), b"\x01\x01\x01".to_vec()),
            (Err(DeserializationError::InvalidValue), b"\x01\x02\x00\x00".to_vec()),
            (Err(DeserializationError::InvalidValue), b"\x01\x02\xff\x01".to_vec()),
        ], |deserializer| {
            return deserializer.read_bool();
        });
    }

    #[test]
    fn test_read_int_i64() {
        assert_deserializes(vec![
            (Ok(0), b"\x02\x01\x00".to_vec()),
            (Ok(127), b"\x02\x01\x7f".to_vec()),
            (Ok(128), b"\x02\x02\x00\x80".to_vec()),
            (Ok(256), b"\x02\x02\x01\x00".to_vec()),
            (Ok(-128), b"\x02\x01\x80".to_vec()),
            (Ok(-129), b"\x02\x02\xff\x7f".to_vec()),
            (Ok(-256), b"\x02\x02\xff\x00".to_vec()),
            (Ok(std::i64::MAX), b"\x02\x08\x7f\xff\xff\xff\xff\xff\xff\xff".to_vec()),
            (Err(DeserializationError::UnexpectedTag), b"\x03".to_vec()),
            (Err(DeserializationError::ShortData), b"\x02\x02\x00".to_vec()),
            (Err(DeserializationError::ShortData), b"".to_vec()),
            (Err(DeserializationError::ShortData), b"\x02".to_vec()),
            (
                Err(DeserializationError::IntegerOverflow),
                b"\x02\x09\x02\x00\x00\x00\x00\x00\x00\x00\x00".to_vec()
            ),
            (Err(DeserializationError::InvalidValue), b"\x02\x05\x00\x00\x00\x00\x01".to_vec()),
            (Err(DeserializationError::InvalidValue), b"\x02\x02\xff\x80".to_vec()),
        ], |deserializer| {
            return deserializer.read_int();
        });
    }

    #[test]
    fn test_read_int_i8() {
        assert_deserializes(vec![
            (Ok(0i8), b"\x02\x01\x00".to_vec()),
            (Ok(127i8), b"\x02\x01\x7f".to_vec()),
            (Ok(-128i8), b"\x02\x01\x80".to_vec()),
            (
                Err(DeserializationError::IntegerOverflow),
                b"\x02\x02\x02\x00".to_vec()
            ),
        ], |deserializer| {
            return deserializer.read_int();
        });
    }

    #[test]
    fn test_read_octet_string() {
        assert_deserializes(vec![
            (Ok(b"".to_vec()), b"\x04\x00".to_vec()),
            (Ok(b"\x01\x02\x03".to_vec()), b"\x04\x03\x01\x02\x03".to_vec()),
            (Err(DeserializationError::ShortData), b"\x04\x03\x01\x02".to_vec()),
        ], |deserializer| {
            return deserializer.read_octet_string();
        });
    }

    #[test]
    fn test_read_object_identifier() {
        assert_deserializes(vec![
            (Ok(ObjectIdentifier::new(vec![2, 5]).unwrap()), b"\x06\x01\x55".to_vec()),
            (Ok(ObjectIdentifier::new(vec![2, 5, 2]).unwrap()), b"\x06\x02\x55\x02".to_vec()),
            (
                Ok(ObjectIdentifier::new(vec![1, 2, 840, 113549]).unwrap()),
                b"\x06\x06\x2a\x86\x48\x86\xf7\x0d".to_vec()
            ),
            (
                Ok(ObjectIdentifier::new(vec![1, 2, 3, 4]).unwrap()),
                b"\x06\x03\x2a\x03\x04".to_vec(),
            ),
            (
                Ok(ObjectIdentifier::new(vec![1, 2, 840, 133549, 1, 1, 5]).unwrap()),
                b"\x06\x09\x2a\x86\x48\x88\x93\x2d\x01\x01\x05".to_vec(),
            ),
            (
                Ok(ObjectIdentifier::new(vec![2, 100, 3]).unwrap()),
                b"\x06\x03\x81\x34\x03".to_vec(),
            ),
            (Err(DeserializationError::InvalidValue), b"\x06\x00".to_vec()),
            (
                Err(DeserializationError::InvalidValue),
                b"\x06\x07\x55\x02\xc0\x80\x80\x80\x80".to_vec()
            ),
            (Err(DeserializationError::ShortData), b"\x06\x02\x2a\x86".to_vec()),
        ], |deserializer| {
            return deserializer.read_object_identifier();
        });
    }

    #[test]
    fn test_read_sequence() {
        assert_deserializes(vec![
            (Ok((1, 2)), b"\x30\x06\x02\x01\x01\x02\x01\x02".to_vec()),
            (Err(DeserializationError::ShortData), b"\x30\x03\x02\x01\x01".to_vec()),
            (
                Err(DeserializationError::ExtraData),
                b"\x30\x07\x02\x01\x01\x02\x01\x02\x00".to_vec()
            ),
        ], |deserializer| {
            return deserializer.read_sequence(|deserializer| {
                return Ok((
                    try!(deserializer.read_int()),
                    try!(deserializer.read_int())
                ));
            });
        });
    }
}
<commit_msg>test for i32<commit_after>use std::{convert};
use std::io::{Cursor, Read};

use byteorder::{self, ReadBytesExt};

use utils::{Integer, ObjectIdentifier};

#[derive(Debug, PartialEq, Eq)]
pub enum DeserializationError {
    UnexpectedTag,
    ShortData,
    ExtraData,
    IntegerOverflow,
    InvalidValue,
}

impl convert::From<byteorder::Error> for DeserializationError {
    fn from(e: byteorder::Error) -> DeserializationError {
        return match e {
            byteorder::Error::UnexpectedEOF => DeserializationError::ShortData,
            _ => panic!("Unexpected error!"),
        }
    }
}

pub type DeserializationResult<T> = Result<T, DeserializationError>;

fn _read_base128_int(reader: &mut Cursor<Vec<u8>>) -> DeserializationResult<u32> {
    let mut ret = 0u32;
    for _ in 0..4 {
        let b = try!(reader.read_u8());
        ret <<= 7;
        ret |= (b & 0x7f) as u32;
        if b & 0x80 == 0 {
            return Ok(ret);
        }
    }
    return Err(DeserializationError::InvalidValue);
}

pub struct Deserializer {
    reader: Cursor<Vec<u8>>,
}

impl Deserializer {
    pub fn new(data: Vec<u8>) -> Deserializer {
        return Deserializer{
            reader: Cursor::new(data),
        }
    }

    fn _read_length(&mut self) -> DeserializationResult<usize> {
        let b = try!(self.reader.read_u8());
        // TODO: handle lengths greater than 128
        assert!(b & 0x80 == 0);
        return Ok((b & 0x7f) as usize);
    }

    fn _read_with_tag<T, F>(&mut self, expected_tag: u8, body: F) -> DeserializationResult<T>
            where F: Fn(Vec<u8>) -> DeserializationResult<T> {
        let tag = try!(self.reader.read_u8());
        // TODO: only some of the bits in the first byte are for the tag
        if tag != expected_tag {
            return Err(DeserializationError::UnexpectedTag);
        }
        let length = try!(self._read_length());
        let mut data = vec![0; length];
        let n = self.reader.read(&mut data).unwrap();
        if n != length {
            return Err(DeserializationError::ShortData);
        }
        return body(data);
    }

    pub fn finish(self) -> DeserializationResult<()> {
        if self.reader.position() as usize != self.reader.get_ref().len() {
            return Err(DeserializationError::ExtraData);
        }
        return Ok(());
    }

    pub fn read_bool(&mut self) -> DeserializationResult<bool> {
        return self._read_with_tag(1, |data| {
            if data == b"\x00" {
                return Ok(false);
            } else if data == b"\xff" {
                return Ok(true)
            } else {
                return Err(DeserializationError::InvalidValue);
            }
        });
    }

    pub fn read_int<T>(&mut self) -> DeserializationResult<T> where T: Integer {
        return self._read_with_tag(2, |data| {
            if data.len() > 1 {
                match (data[0], data[1] & 0x80) {
                    (0xff, 0x80) | (0x00, 0x00) => return Err(DeserializationError::InvalidValue),
                    _ => {},
                }
            }
            return T::decode(data);
        });
    }

    pub fn read_octet_string(&mut self) -> DeserializationResult<Vec<u8>> {
        return self._read_with_tag(4, |data| {
            return Ok(data);
        });
    }

    pub fn read_object_identifier(&mut self) -> DeserializationResult<ObjectIdentifier> {
        return self._read_with_tag(6, |data| {
            if data.is_empty() {
                return Err(DeserializationError::InvalidValue);
            }
            let mut reader = Cursor::new(data);
            let mut s = vec![];
            let v = try!(_read_base128_int(&mut reader));

            if v < 80 {
                s.push(v / 40);
                s.push(v % 40);
            } else {
                s.push(2);
                s.push(v - 80);
            }

            while (reader.position() as usize) < reader.get_ref().len() {
                s.push(try!(_read_base128_int(&mut reader)));
            }

            return Ok(ObjectIdentifier::new(s).unwrap());
        });
    }

    pub fn read_sequence<F, T>(&mut self, v: F) -> DeserializationResult<T>
            where F: Fn(&mut Deserializer) -> DeserializationResult<T> {
        return self._read_with_tag(48, |data| {
            return from_vec(data, &v);
        });
    }
}

pub fn from_vec<F, T>(data: Vec<u8>, f: F) -> DeserializationResult<T>
        where F: Fn(&mut Deserializer) -> DeserializationResult<T> {
    let mut deserializer = Deserializer::new(data);
    let result = try!(f(&mut deserializer));
    try!(deserializer.finish());
    return Ok(result);
}

#[cfg(test)]
mod tests {
    use std;
    use std::{fmt};

    use utils::{ObjectIdentifier};
    use super::{Deserializer, DeserializationError, DeserializationResult, from_vec};

    fn assert_deserializes<T, F>(values: Vec<(DeserializationResult<T>, Vec<u8>)>, f: F)
            where T: Eq + fmt::Debug, F: Fn(&mut Deserializer) -> DeserializationResult<T> {
        for (expected, value) in values {
            let result = from_vec(value, &f);
            assert_eq!(result, expected);
        }
    }
    #[test]
    fn test_read_extra_data() {
        assert_deserializes(vec![
            (Err(DeserializationError::ExtraData), b"\x00".to_vec()),
        ], |_| {
            return Ok(());
        });
    }

    #[test]
    fn test_read_bool() {
        assert_deserializes(vec![
            (Ok(true), b"\x01\x01\xff".to_vec()),
            (Ok(false), b"\x01\x01\x00".to_vec()),
            (Err(DeserializationError::InvalidValue), b"\x01\x00".to_vec()),
            (Err(DeserializationError::InvalidValue), b"\x01\x01\x01".to_vec()),
            (Err(DeserializationError::InvalidValue), b"\x01\x02\x00\x00".to_vec()),
            (Err(DeserializationError::InvalidValue), b"\x01\x02\xff\x01".to_vec()),
        ], |deserializer| {
            return deserializer.read_bool();
        });
    }

    #[test]
    fn test_read_int_i64() {
        assert_deserializes(vec![
            (Ok(0), b"\x02\x01\x00".to_vec()),
            (Ok(127), b"\x02\x01\x7f".to_vec()),
            (Ok(128), b"\x02\x02\x00\x80".to_vec()),
            (Ok(256), b"\x02\x02\x01\x00".to_vec()),
            (Ok(-128), b"\x02\x01\x80".to_vec()),
            (Ok(-129), b"\x02\x02\xff\x7f".to_vec()),
            (Ok(-256), b"\x02\x02\xff\x00".to_vec()),
            (Ok(std::i64::MAX), b"\x02\x08\x7f\xff\xff\xff\xff\xff\xff\xff".to_vec()),
            (Err(DeserializationError::UnexpectedTag), b"\x03".to_vec()),
            (Err(DeserializationError::ShortData), b"\x02\x02\x00".to_vec()),
            (Err(DeserializationError::ShortData), b"".to_vec()),
            (Err(DeserializationError::ShortData), b"\x02".to_vec()),
            (
                Err(DeserializationError::IntegerOverflow),
                b"\x02\x09\x02\x00\x00\x00\x00\x00\x00\x00\x00".to_vec()
            ),
            (Err(DeserializationError::InvalidValue), b"\x02\x05\x00\x00\x00\x00\x01".to_vec()),
            (Err(DeserializationError::InvalidValue), b"\x02\x02\xff\x80".to_vec()),
        ], |deserializer| {
            return deserializer.read_int();
        });
    }

    #[test]
    fn test_read_int_i32() {
        assert_deserializes(vec![
            (Ok(0i32), b"\x02\x01\x00".to_vec()),
            (Ok(127i32), b"\x02\x01\x7f".to_vec()),
            (Ok(128i32), b"\x02\x02\x00\x80".to_vec()),
            (Ok(256i32), b"\x02\x02\x01\x00".to_vec()),
            (Ok(-128i32), b"\x02\x01\x80".to_vec()),
            (Ok(-129i32), b"\x02\x02\xff\x7f".to_vec()),
            (Ok(-256i32), b"\x02\x02\xff\x00".to_vec()),
            (Ok(std::i32::MAX), b"\x02\x04\x7f\xff\xff\xff".to_vec()),
            (
                Err(DeserializationError::IntegerOverflow),
                b"\x02\x05\x02\x00\x00\x00\x00".to_vec()
            ),
        ], |deserializer| {
            return deserializer.read_int();
        });
    }

    #[test]
    fn test_read_int_i8() {
        assert_deserializes(vec![
            (Ok(0i8), b"\x02\x01\x00".to_vec()),
            (Ok(127i8), b"\x02\x01\x7f".to_vec()),
            (Ok(-128i8), b"\x02\x01\x80".to_vec()),
            (
                Err(DeserializationError::IntegerOverflow),
                b"\x02\x02\x02\x00".to_vec()
            ),
        ], |deserializer| {
            return deserializer.read_int();
        });
    }

    #[test]
    fn test_read_octet_string() {
        assert_deserializes(vec![
            (Ok(b"".to_vec()), b"\x04\x00".to_vec()),
            (Ok(b"\x01\x02\x03".to_vec()), b"\x04\x03\x01\x02\x03".to_vec()),
            (Err(DeserializationError::ShortData), b"\x04\x03\x01\x02".to_vec()),
        ], |deserializer| {
            return deserializer.read_octet_string();
        });
    }

    #[test]
    fn test_read_object_identifier() {
        assert_deserializes(vec![
            (Ok(ObjectIdentifier::new(vec![2, 5]).unwrap()), b"\x06\x01\x55".to_vec()),
            (Ok(ObjectIdentifier::new(vec![2, 5, 2]).unwrap()), b"\x06\x02\x55\x02".to_vec()),
            (
                Ok(ObjectIdentifier::new(vec![1, 2, 840, 113549]).unwrap()),
                b"\x06\x06\x2a\x86\x48\x86\xf7\x0d".to_vec()
            ),
            (
                Ok(ObjectIdentifier::new(vec![1, 2, 3, 4]).unwrap()),
                b"\x06\x03\x2a\x03\x04".to_vec(),
            ),
            (
                Ok(ObjectIdentifier::new(vec![1, 2, 840, 133549, 1, 1, 5]).unwrap()),
                b"\x06\x09\x2a\x86\x48\x88\x93\x2d\x01\x01\x05".to_vec(),
            ),
            (
                Ok(ObjectIdentifier::new(vec![2, 100, 3]).unwrap()),
                b"\x06\x03\x81\x34\x03".to_vec(),
            ),
            (Err(DeserializationError::InvalidValue), b"\x06\x00".to_vec()),
            (
                Err(DeserializationError::InvalidValue),
                b"\x06\x07\x55\x02\xc0\x80\x80\x80\x80".to_vec()
            ),
            (Err(DeserializationError::ShortData), b"\x06\x02\x2a\x86".to_vec()),
        ], |deserializer| {
            return deserializer.read_object_identifier();
        });
    }

    #[test]
    fn test_read_sequence() {
        assert_deserializes(vec![
            (Ok((1, 2)), b"\x30\x06\x02\x01\x01\x02\x01\x02".to_vec()),
            (Err(DeserializationError::ShortData), b"\x30\x03\x02\x01\x01".to_vec()),
            (
                Err(DeserializationError::ExtraData),
                b"\x30\x07\x02\x01\x01\x02\x01\x02\x00".to_vec()
            ),
        ], |deserializer| {
            return deserializer.read_sequence(|deserializer| {
                return Ok((
                    try!(deserializer.read_int()),
                    try!(deserializer.read_int())
                ));
            });
        });
    }
}
<|endoftext|>


<commit_before>//! Possible ZIP compression methods.

use std::fmt;

/// Compression methods for the contents of a ZIP file.
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum CompressionMethod {
    /// The file is stored (no compression)
    Stored,
    /// Deflate in pure rust
    #[cfg(feature = "deflate")]
    Deflated,
    /// File is compressed using BZIP2 algorithm
    #[cfg(feature = "bzip2")]
    Bzip2,
    /// Unsupported compression method
    Unsupported(u16),
}

impl CompressionMethod {
    /// Converts an u16 to its corresponding CompressionMethod
    pub fn from_u16(val: u16) -> CompressionMethod {
        match val {
            0 => CompressionMethod::Stored,
            #[cfg(feature = "deflate")]
            8 => CompressionMethod::Deflated,
            #[cfg(feature = "bzip2")]
            12 => CompressionMethod::Bzip2,
            v => CompressionMethod::Unsupported(v),
        }
    }

    /// Converts a CompressionMethod to a u16
    pub fn to_u16(self) -> u16 {
        match self {
            CompressionMethod::Stored => 0,
            #[cfg(feature = "deflate")]
            CompressionMethod::Deflated => 8,
            #[cfg(feature = "bzip2")]
            CompressionMethod::Bzip2 => 12,
            CompressionMethod::Unsupported(v) => v,
        }
    }
}

impl fmt::Display for CompressionMethod {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // Just duplicate what the Debug format looks like, i.e, the enum key:
        write!(f, "{:?}", self)
    }
}

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

    #[test]
    fn from_eq_to() {
        for v in 0..(::std::u16::MAX as u32 + 1) {
            let from = CompressionMethod::from_u16(v as u16);
            let to = from.to_u16() as u32;
            assert_eq!(v, to);
        }
    }

    fn methods() -> Vec<CompressionMethod> {
        let mut methods = Vec::new();
        methods.push(CompressionMethod::Stored);
        #[cfg(feature = "deflate")]
        methods.push(CompressionMethod::Deflated);
        #[cfg(feature = "bzip2")]
        methods.push(CompressionMethod::Bzip2);
        methods
    }

    #[test]
    fn to_eq_from() {
        fn check_match(method: CompressionMethod) {
            let to = method.to_u16();
            let from = CompressionMethod::from_u16(to);
            let back = from.to_u16();
            assert_eq!(to, back);
        }

        for method in methods() {
            check_match(method);
        }
    }

    #[test]
    fn to_display_fmt() {
        fn check_match(method: CompressionMethod) {
            let debug_str = format!("{:?}", method);
            let display_str = format!("{}", method);
            assert_eq!(debug_str, display_str);
        }

        for method in methods() {
            check_match(method);
        }
    }
}
<commit_msg>docs: deprecate unnecessary API<commit_after>//! Possible ZIP compression methods.

use std::fmt;

/// Compression methods for the contents of a ZIP file.
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum CompressionMethod {
    /// The file is stored (no compression)
    Stored,
    /// Deflate in pure rust
    #[cfg(feature = "deflate")]
    Deflated,
    /// File is compressed using BZIP2 algorithm
    #[cfg(feature = "bzip2")]
    Bzip2,
    /// Unsupported compression method
    #[deprecated(
        since = "0.5.7",
        note = "implementation details are being removed from the public API"
    )]
    Unsupported(u16),
}

impl CompressionMethod {
    /// Converts an u16 to its corresponding CompressionMethod
    #[deprecated(
        since = "0.5.7",
        note = "implementation details are being removed from the public API"
    )]
    pub fn from_u16(val: u16) -> CompressionMethod {
        match val {
            0 => CompressionMethod::Stored,
            #[cfg(feature = "deflate")]
            8 => CompressionMethod::Deflated,
            #[cfg(feature = "bzip2")]
            12 => CompressionMethod::Bzip2,
            v => CompressionMethod::Unsupported(v),
        }
    }

    /// Converts a CompressionMethod to a u16
    #[deprecated(
        since = "0.5.7",
        note = "implementation details are being removed from the public API"
    )]
    pub fn to_u16(self) -> u16 {
        match self {
            CompressionMethod::Stored => 0,
            #[cfg(feature = "deflate")]
            CompressionMethod::Deflated => 8,
            #[cfg(feature = "bzip2")]
            CompressionMethod::Bzip2 => 12,
            CompressionMethod::Unsupported(v) => v,
        }
    }
}

impl fmt::Display for CompressionMethod {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // Just duplicate what the Debug format looks like, i.e, the enum key:
        write!(f, "{:?}", self)
    }
}

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

    #[test]
    fn from_eq_to() {
        for v in 0..(::std::u16::MAX as u32 + 1) {
            let from = CompressionMethod::from_u16(v as u16);
            let to = from.to_u16() as u32;
            assert_eq!(v, to);
        }
    }

    fn methods() -> Vec<CompressionMethod> {
        let mut methods = Vec::new();
        methods.push(CompressionMethod::Stored);
        #[cfg(feature = "deflate")]
        methods.push(CompressionMethod::Deflated);
        #[cfg(feature = "bzip2")]
        methods.push(CompressionMethod::Bzip2);
        methods
    }

    #[test]
    fn to_eq_from() {
        fn check_match(method: CompressionMethod) {
            let to = method.to_u16();
            let from = CompressionMethod::from_u16(to);
            let back = from.to_u16();
            assert_eq!(to, back);
        }

        for method in methods() {
            check_match(method);
        }
    }

    #[test]
    fn to_display_fmt() {
        fn check_match(method: CompressionMethod) {
            let debug_str = format!("{:?}", method);
            let display_str = format!("{}", method);
            assert_eq!(debug_str, display_str);
        }

        for method in methods() {
            check_match(method);
        }
    }
}
<|endoftext|>


<commit_before>//! Possible ZIP compression methods.

use std::fmt;

#[allow(deprecated)]
/// Identifies the storage format used to compress a file within a ZIP archive.
///
/// Each file's compression method is stored alongside it, allowing the
/// contents to be read without context.
///
/// When creating ZIP files, you may choose the method to use with
/// [`crate::write::FileOptions::compression_method`]
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum CompressionMethod {
    /// Store the file as is
    Stored,
    /// Compress the file using Deflate
    #[cfg(any(
        feature = "deflate",
        feature = "deflate-miniz",
        feature = "deflate-zlib"
    ))]
    Deflated,
    /// Compress the file using BZIP2
    #[cfg(feature = "bzip2")]
    Bzip2,
    /// Compress the file using ZStandard
    #[cfg(feature = "zstd")]
    Zstd,
    /// Unsupported compression method
    #[deprecated(since = "0.5.7", note = "use the constants instead")]
    Unsupported(u16),
}
#[allow(deprecated, missing_docs)]
/// All compression methods defined for the ZIP format
impl CompressionMethod {
    pub const STORE: Self = CompressionMethod::Stored;
    pub const SHRINK: Self = CompressionMethod::Unsupported(1);
    pub const REDUCE_1: Self = CompressionMethod::Unsupported(2);
    pub const REDUCE_2: Self = CompressionMethod::Unsupported(3);
    pub const REDUCE_3: Self = CompressionMethod::Unsupported(4);
    pub const REDUCE_4: Self = CompressionMethod::Unsupported(5);
    pub const IMPLODE: Self = CompressionMethod::Unsupported(6);
    #[cfg(any(
        feature = "deflate",
        feature = "deflate-miniz",
        feature = "deflate-zlib"
    ))]
    pub const DEFLATE: Self = CompressionMethod::Deflated;
    #[cfg(not(any(
        feature = "deflate",
        feature = "deflate-miniz",
        feature = "deflate-zlib"
    )))]
    pub const DEFLATE: Self = CompressionMethod::Unsupported(8);
    pub const DEFLATE64: Self = CompressionMethod::Unsupported(9);
    pub const PKWARE_IMPLODE: Self = CompressionMethod::Unsupported(10);
    #[cfg(feature = "bzip2")]
    pub const BZIP2: Self = CompressionMethod::Bzip2;
    #[cfg(not(feature = "bzip2"))]
    pub const BZIP2: Self = CompressionMethod::Unsupported(12);
    pub const LZMA: Self = CompressionMethod::Unsupported(14);
    pub const IBM_ZOS_CMPSC: Self = CompressionMethod::Unsupported(16);
    pub const IBM_TERSE: Self = CompressionMethod::Unsupported(18);
    pub const ZSTD_DEPRECATED: Self = CompressionMethod::Unsupported(20);
    #[cfg(feature = "zstd")]
    pub const ZSTD: Self = CompressionMethod::Zstd;
    #[cfg(not(feature = "zstd"))]
    pub const ZSTD: Self = CompressionMethod::Unsupported(93);
    pub const MP3: Self = CompressionMethod::Unsupported(94);
    pub const XZ: Self = CompressionMethod::Unsupported(95);
    pub const JPEG: Self = CompressionMethod::Unsupported(96);
    pub const WAVPACK: Self = CompressionMethod::Unsupported(97);
    pub const PPMD: Self = CompressionMethod::Unsupported(98);
}
impl CompressionMethod {
    /// Converts an u16 to its corresponding CompressionMethod
    #[deprecated(
        since = "0.5.7",
        note = "use a constant to construct a compression method"
    )]
    pub fn from_u16(val: u16) -> CompressionMethod {
        #[allow(deprecated)]
        match val {
            0 => CompressionMethod::Stored,
            #[cfg(any(
                feature = "deflate",
                feature = "deflate-miniz",
                feature = "deflate-zlib"
            ))]
            8 => CompressionMethod::Deflated,
            #[cfg(feature = "bzip2")]
            12 => CompressionMethod::Bzip2,
            #[cfg(feature = "zstd")]
            93 => CompressionMethod::Zstd,

            v => CompressionMethod::Unsupported(v),
        }
    }

    /// Converts a CompressionMethod to a u16
    #[deprecated(
        since = "0.5.7",
        note = "to match on other compression methods, use a constant"
    )]
    pub fn to_u16(self) -> u16 {
        #[allow(deprecated)]
        match self {
            CompressionMethod::Stored => 0,
            #[cfg(any(
                feature = "deflate",
                feature = "deflate-miniz",
                feature = "deflate-zlib"
            ))]
            CompressionMethod::Deflated => 8,
            #[cfg(feature = "bzip2")]
            CompressionMethod::Bzip2 => 12,
            #[cfg(feature = "zstd")]
            CompressionMethod::Zstd => 93,

            CompressionMethod::Unsupported(v) => v,
        }
    }
}

impl fmt::Display for CompressionMethod {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // Just duplicate what the Debug format looks like, i.e, the enum key:
        write!(f, "{:?}", self)
    }
}

/// The compression methods which have been implemented.
pub const SUPPORTED_COMPRESSION_METHODS: &[CompressionMethod] = &[
        CompressionMethod::Stored,
        #[cfg(any(
            feature = "deflate",
            feature = "deflate-miniz",
            feature = "deflate-zlib"
        ))]
        CompressionMethod::Deflated,
        #[cfg(feature = "bzip2")]
        CompressionMethod::Bzip2,
        #[cfg(feature = "zstd")]
        CompressionMethod::Zstd,
];

#[cfg(test)]
mod test {
    use super::{CompressionMethod, SUPPORTED_COMPRESSION_METHODS};

    #[test]
    fn from_eq_to() {
        for v in 0..(u16::MAX as u32 + 1) {
            #[allow(deprecated)]
            let from = CompressionMethod::from_u16(v as u16);
            #[allow(deprecated)]
            let to = from.to_u16() as u32;
            assert_eq!(v, to);
        }
    }

    #[test]
    fn to_eq_from() {
        fn check_match(method: CompressionMethod) {
            #[allow(deprecated)]
            let to = method.to_u16();
            #[allow(deprecated)]
            let from = CompressionMethod::from_u16(to);
            #[allow(deprecated)]
            let back = from.to_u16();
            assert_eq!(to, back);
        }

        for &method in SUPPORTED_COMPRESSION_METHODS {
            check_match(method);
        }
    }

    #[test]
    fn to_display_fmt() {
        fn check_match(method: CompressionMethod) {
            let debug_str = format!("{:?}", method);
            let display_str = format!("{}", method);
            assert_eq!(debug_str, display_str);
        }

        for &method in SUPPORTED_COMPRESSION_METHODS {
            check_match(method);
        }
    }
}
<commit_msg>Apply formatter fixes<commit_after>//! Possible ZIP compression methods.

use std::fmt;

#[allow(deprecated)]
/// Identifies the storage format used to compress a file within a ZIP archive.
///
/// Each file's compression method is stored alongside it, allowing the
/// contents to be read without context.
///
/// When creating ZIP files, you may choose the method to use with
/// [`crate::write::FileOptions::compression_method`]
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum CompressionMethod {
    /// Store the file as is
    Stored,
    /// Compress the file using Deflate
    #[cfg(any(
        feature = "deflate",
        feature = "deflate-miniz",
        feature = "deflate-zlib"
    ))]
    Deflated,
    /// Compress the file using BZIP2
    #[cfg(feature = "bzip2")]
    Bzip2,
    /// Compress the file using ZStandard
    #[cfg(feature = "zstd")]
    Zstd,
    /// Unsupported compression method
    #[deprecated(since = "0.5.7", note = "use the constants instead")]
    Unsupported(u16),
}
#[allow(deprecated, missing_docs)]
/// All compression methods defined for the ZIP format
impl CompressionMethod {
    pub const STORE: Self = CompressionMethod::Stored;
    pub const SHRINK: Self = CompressionMethod::Unsupported(1);
    pub const REDUCE_1: Self = CompressionMethod::Unsupported(2);
    pub const REDUCE_2: Self = CompressionMethod::Unsupported(3);
    pub const REDUCE_3: Self = CompressionMethod::Unsupported(4);
    pub const REDUCE_4: Self = CompressionMethod::Unsupported(5);
    pub const IMPLODE: Self = CompressionMethod::Unsupported(6);
    #[cfg(any(
        feature = "deflate",
        feature = "deflate-miniz",
        feature = "deflate-zlib"
    ))]
    pub const DEFLATE: Self = CompressionMethod::Deflated;
    #[cfg(not(any(
        feature = "deflate",
        feature = "deflate-miniz",
        feature = "deflate-zlib"
    )))]
    pub const DEFLATE: Self = CompressionMethod::Unsupported(8);
    pub const DEFLATE64: Self = CompressionMethod::Unsupported(9);
    pub const PKWARE_IMPLODE: Self = CompressionMethod::Unsupported(10);
    #[cfg(feature = "bzip2")]
    pub const BZIP2: Self = CompressionMethod::Bzip2;
    #[cfg(not(feature = "bzip2"))]
    pub const BZIP2: Self = CompressionMethod::Unsupported(12);
    pub const LZMA: Self = CompressionMethod::Unsupported(14);
    pub const IBM_ZOS_CMPSC: Self = CompressionMethod::Unsupported(16);
    pub const IBM_TERSE: Self = CompressionMethod::Unsupported(18);
    pub const ZSTD_DEPRECATED: Self = CompressionMethod::Unsupported(20);
    #[cfg(feature = "zstd")]
    pub const ZSTD: Self = CompressionMethod::Zstd;
    #[cfg(not(feature = "zstd"))]
    pub const ZSTD: Self = CompressionMethod::Unsupported(93);
    pub const MP3: Self = CompressionMethod::Unsupported(94);
    pub const XZ: Self = CompressionMethod::Unsupported(95);
    pub const JPEG: Self = CompressionMethod::Unsupported(96);
    pub const WAVPACK: Self = CompressionMethod::Unsupported(97);
    pub const PPMD: Self = CompressionMethod::Unsupported(98);
}
impl CompressionMethod {
    /// Converts an u16 to its corresponding CompressionMethod
    #[deprecated(
        since = "0.5.7",
        note = "use a constant to construct a compression method"
    )]
    pub fn from_u16(val: u16) -> CompressionMethod {
        #[allow(deprecated)]
        match val {
            0 => CompressionMethod::Stored,
            #[cfg(any(
                feature = "deflate",
                feature = "deflate-miniz",
                feature = "deflate-zlib"
            ))]
            8 => CompressionMethod::Deflated,
            #[cfg(feature = "bzip2")]
            12 => CompressionMethod::Bzip2,
            #[cfg(feature = "zstd")]
            93 => CompressionMethod::Zstd,

            v => CompressionMethod::Unsupported(v),
        }
    }

    /// Converts a CompressionMethod to a u16
    #[deprecated(
        since = "0.5.7",
        note = "to match on other compression methods, use a constant"
    )]
    pub fn to_u16(self) -> u16 {
        #[allow(deprecated)]
        match self {
            CompressionMethod::Stored => 0,
            #[cfg(any(
                feature = "deflate",
                feature = "deflate-miniz",
                feature = "deflate-zlib"
            ))]
            CompressionMethod::Deflated => 8,
            #[cfg(feature = "bzip2")]
            CompressionMethod::Bzip2 => 12,
            #[cfg(feature = "zstd")]
            CompressionMethod::Zstd => 93,

            CompressionMethod::Unsupported(v) => v,
        }
    }
}

impl fmt::Display for CompressionMethod {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // Just duplicate what the Debug format looks like, i.e, the enum key:
        write!(f, "{:?}", self)
    }
}

/// The compression methods which have been implemented.
pub const SUPPORTED_COMPRESSION_METHODS: &[CompressionMethod] = &[
    CompressionMethod::Stored,
    #[cfg(any(
        feature = "deflate",
        feature = "deflate-miniz",
        feature = "deflate-zlib"
    ))]
    CompressionMethod::Deflated,
    #[cfg(feature = "bzip2")]
    CompressionMethod::Bzip2,
    #[cfg(feature = "zstd")]
    CompressionMethod::Zstd,
];

#[cfg(test)]
mod test {
    use super::{CompressionMethod, SUPPORTED_COMPRESSION_METHODS};

    #[test]
    fn from_eq_to() {
        for v in 0..(u16::MAX as u32 + 1) {
            #[allow(deprecated)]
            let from = CompressionMethod::from_u16(v as u16);
            #[allow(deprecated)]
            let to = from.to_u16() as u32;
            assert_eq!(v, to);
        }
    }

    #[test]
    fn to_eq_from() {
        fn check_match(method: CompressionMethod) {
            #[allow(deprecated)]
            let to = method.to_u16();
            #[allow(deprecated)]
            let from = CompressionMethod::from_u16(to);
            #[allow(deprecated)]
            let back = from.to_u16();
            assert_eq!(to, back);
        }

        for &method in SUPPORTED_COMPRESSION_METHODS {
            check_match(method);
        }
    }

    #[test]
    fn to_display_fmt() {
        fn check_match(method: CompressionMethod) {
            let debug_str = format!("{:?}", method);
            let display_str = format!("{}", method);
            assert_eq!(debug_str, display_str);
        }

        for &method in SUPPORTED_COMPRESSION_METHODS {
            check_match(method);
        }
    }
}
<|endoftext|>


<commit_before>//! Elements of the WebAssembly binary format.

use std::error;
use std::fmt;
use std::io;

mod module;
mod section;
mod primitives;
mod types;
mod import_entry;
mod export_entry;
mod global_entry;
mod ops;
mod func;
mod segment;

pub use self::module::{Module, peek_size};
pub use self::section::{
    Section, FunctionSection, CodeSection, MemorySection, DataSection,
    ImportSection, ExportSection, GlobalSection, TypeSection, ElementSection,
    TableSection, CustomSection,
};
pub use self::import_entry::{ImportEntry, ResizableLimits, MemoryType, TableType, GlobalType, External};
pub use self::export_entry::{ExportEntry, Internal};
pub use self::global_entry::GlobalEntry;
pub use self::primitives::{
    VarUint32, VarUint7, VarUint1, VarInt7, Uint32, VarInt32, VarInt64,
    Uint64, VarUint64, CountedList, CountedWriter, CountedListWriter,
};
pub use self::types::{Type, ValueType, BlockType, FunctionType, TableElementType};
pub use self::ops::{Opcode, Opcodes, InitExpr};
pub use self::func::{Func, FuncBody, Local};
pub use self::segment::{ElementSegment, DataSegment};

/// Deserialization from serial i/o
pub trait Deserialize : Sized {
    /// Serialization error produced by deserialization routine.
    type Error;
    /// Deserialize type from serial i/o
    fn deserialize<R: io::Read>(reader: &mut R) -> Result<Self, Self::Error>;
}

/// Serialization to serial i/o
pub trait Serialize {
    /// Serialization error produced by serialization routine.
    type Error;
    /// Serialize type to serial i/o
    fn serialize<W: io::Write>(self, writer: &mut W) -> Result<(), Self::Error>;
}

/// Deserialization/serialization error
#[derive(Debug)]
pub enum Error {
    /// Unexpected end of input
    UnexpectedEof,
    /// Invalid magic
    InvalidMagic,
    /// Unsupported version
    UnsupportedVersion(u32),
    /// Inconsistence between declared and actual length
    InconsistentLength {
        /// Expected length of the definition
        expected: usize,
        /// Actual length of the definition
        actual: usize
    },
    /// Other static error
    Other(&'static str),
    /// Other allocated error
    HeapOther(String),
    /// Invalid/unknown value type declaration
    UnknownValueType(i8),
    /// Invalid/unknown table element type declaration
    UnknownTableElementType(i8),
    /// Non-utf8 string
    NonUtf8String,
    /// Unknown external kind code
    UnknownExternalKind(u8),
    /// Unknown internal kind code
    UnknownInternalKind(u8),
    /// Unknown opcode encountered
    UnknownOpcode(u8),
    /// Invalid VarUint1 value
    InvalidVarUint1(u8),
    /// Invalid VarInt32 value
    InvalidVarInt32,
    /// Invalid VarInt64 value
    InvalidVarInt64,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::UnexpectedEof => write!(f, "Unexpected end of input"),
            Error::InvalidMagic => write!(f, "Invalid magic number at start of file"),
            Error::UnsupportedVersion(v) => write!(f, "Unsupported wasm version {}", v),
            Error::InconsistentLength { expected, actual } => {
                write!(f, "Expected length {}, found {}", expected, actual)
            }
            Error::Other(msg) => write!(f, "{}", msg),
            Error::HeapOther(ref msg) => write!(f, "{}", msg),
            Error::UnknownValueType(ty) => write!(f, "Invalid or unknown value type {}", ty),
            Error::UnknownTableElementType(ty) => write!(f, "Unknown table element type {}", ty),
            Error::NonUtf8String => write!(f, "Non-UTF-8 string"),
            Error::UnknownExternalKind(kind) => write!(f, "Unknown external kind {}", kind),
            Error::UnknownInternalKind(kind) => write!(f, "Unknown internal kind {}", kind),
            Error::UnknownOpcode(opcode) => write!(f, "Unknown opcode {}", opcode),
            Error::InvalidVarUint1(val) => write!(f, "Not a unsigned 1-bit integer: {}", val),
            Error::InvalidVarInt32 => write!(f, "Not a signed 32-bit integer"),
            Error::InvalidVarInt64 => write!(f, "Not a signed 32-bit integer"),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::UnexpectedEof => "Unexpected end of input",
            Error::InvalidMagic => "Invalid magic number at start of file",
            Error::UnsupportedVersion(_) => "Unsupported wasm version",
            Error::InconsistentLength { .. } => "Inconsistent length",
            Error::Other(msg) => msg,
            Error::HeapOther(ref msg) => &msg[..],
            Error::UnknownValueType(_) => "Invalid or unknown value type",
            Error::UnknownTableElementType(_) => "Unknown table element type",
            Error::NonUtf8String => "Non-UTF-8 string",
            Error::UnknownExternalKind(_) => "Unknown external kind",
            Error::UnknownInternalKind(_) => "Unknown internal kind",
            Error::UnknownOpcode(_) => "Unknown opcode",
            Error::InvalidVarUint1(_) => "Not a unsigned 1-bit integer",
            Error::InvalidVarInt32 => "Not a signed 32-bit integer",
            Error::InvalidVarInt64 => "Not a signed 32-bit integer",
        }
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Self {
        Error::HeapOther(format!("I/O Error: {}", err))
    }
}

/// Unparsed part of the module/section
pub struct Unparsed(pub Vec<u8>);

impl Deserialize for Unparsed {
    type Error = Error;

    fn deserialize<R: io::Read>(reader: &mut R) -> Result<Self, Self::Error> {
        let len = VarUint32::deserialize(reader)?.into();
        let mut vec = vec![0u8; len];
        reader.read_exact(&mut vec[..])?;
        Ok(Unparsed(vec))
    }
}

impl From<Unparsed> for Vec<u8> {
    fn from(u: Unparsed) -> Vec<u8> {
        u.0
    }
}

/// Deserialize module from file.
pub fn deserialize_file<P: AsRef<::std::path::Path>>(p: P) -> Result<Module, Error> {
    use std::io::Read;

    let mut contents = Vec::new();
    ::std::fs::File::open(p)?.read_to_end(&mut contents)?;

    deserialize_buffer(&contents)
}

/// Deserialize deserializable type from buffer.
pub fn deserialize_buffer<T: Deserialize>(contents: &[u8]) -> Result<T, T::Error> {
    let mut reader = io::Cursor::new(contents);
    T::deserialize(&mut reader)
}

/// Create buffer with serialized value.
pub fn serialize<T: Serialize>(val: T) -> Result<Vec<u8>, T::Error> {
    let mut buf = Vec::new();
    val.serialize(&mut buf)?;
    Ok(buf)
}

/// Serialize module to the file
pub fn serialize_to_file<P: AsRef<::std::path::Path>>(p: P, module: Module) -> Result<(), Error>
{
    let mut io = ::std::fs::File::create(p)?;
    module.serialize(&mut io)
}
<commit_msg>Fix typos in error messages<commit_after>//! Elements of the WebAssembly binary format.

use std::error;
use std::fmt;
use std::io;

mod module;
mod section;
mod primitives;
mod types;
mod import_entry;
mod export_entry;
mod global_entry;
mod ops;
mod func;
mod segment;

pub use self::module::{Module, peek_size};
pub use self::section::{
    Section, FunctionSection, CodeSection, MemorySection, DataSection,
    ImportSection, ExportSection, GlobalSection, TypeSection, ElementSection,
    TableSection, CustomSection,
};
pub use self::import_entry::{ImportEntry, ResizableLimits, MemoryType, TableType, GlobalType, External};
pub use self::export_entry::{ExportEntry, Internal};
pub use self::global_entry::GlobalEntry;
pub use self::primitives::{
    VarUint32, VarUint7, VarUint1, VarInt7, Uint32, VarInt32, VarInt64,
    Uint64, VarUint64, CountedList, CountedWriter, CountedListWriter,
};
pub use self::types::{Type, ValueType, BlockType, FunctionType, TableElementType};
pub use self::ops::{Opcode, Opcodes, InitExpr};
pub use self::func::{Func, FuncBody, Local};
pub use self::segment::{ElementSegment, DataSegment};

/// Deserialization from serial i/o
pub trait Deserialize : Sized {
    /// Serialization error produced by deserialization routine.
    type Error;
    /// Deserialize type from serial i/o
    fn deserialize<R: io::Read>(reader: &mut R) -> Result<Self, Self::Error>;
}

/// Serialization to serial i/o
pub trait Serialize {
    /// Serialization error produced by serialization routine.
    type Error;
    /// Serialize type to serial i/o
    fn serialize<W: io::Write>(self, writer: &mut W) -> Result<(), Self::Error>;
}

/// Deserialization/serialization error
#[derive(Debug)]
pub enum Error {
    /// Unexpected end of input
    UnexpectedEof,
    /// Invalid magic
    InvalidMagic,
    /// Unsupported version
    UnsupportedVersion(u32),
    /// Inconsistence between declared and actual length
    InconsistentLength {
        /// Expected length of the definition
        expected: usize,
        /// Actual length of the definition
        actual: usize
    },
    /// Other static error
    Other(&'static str),
    /// Other allocated error
    HeapOther(String),
    /// Invalid/unknown value type declaration
    UnknownValueType(i8),
    /// Invalid/unknown table element type declaration
    UnknownTableElementType(i8),
    /// Non-utf8 string
    NonUtf8String,
    /// Unknown external kind code
    UnknownExternalKind(u8),
    /// Unknown internal kind code
    UnknownInternalKind(u8),
    /// Unknown opcode encountered
    UnknownOpcode(u8),
    /// Invalid VarUint1 value
    InvalidVarUint1(u8),
    /// Invalid VarInt32 value
    InvalidVarInt32,
    /// Invalid VarInt64 value
    InvalidVarInt64,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::UnexpectedEof => write!(f, "Unexpected end of input"),
            Error::InvalidMagic => write!(f, "Invalid magic number at start of file"),
            Error::UnsupportedVersion(v) => write!(f, "Unsupported wasm version {}", v),
            Error::InconsistentLength { expected, actual } => {
                write!(f, "Expected length {}, found {}", expected, actual)
            }
            Error::Other(msg) => write!(f, "{}", msg),
            Error::HeapOther(ref msg) => write!(f, "{}", msg),
            Error::UnknownValueType(ty) => write!(f, "Invalid or unknown value type {}", ty),
            Error::UnknownTableElementType(ty) => write!(f, "Unknown table element type {}", ty),
            Error::NonUtf8String => write!(f, "Non-UTF-8 string"),
            Error::UnknownExternalKind(kind) => write!(f, "Unknown external kind {}", kind),
            Error::UnknownInternalKind(kind) => write!(f, "Unknown internal kind {}", kind),
            Error::UnknownOpcode(opcode) => write!(f, "Unknown opcode {}", opcode),
            Error::InvalidVarUint1(val) => write!(f, "Not an unsigned 1-bit integer: {}", val),
            Error::InvalidVarInt32 => write!(f, "Not a signed 32-bit integer"),
            Error::InvalidVarInt64 => write!(f, "Not a signed 64-bit integer"),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::UnexpectedEof => "Unexpected end of input",
            Error::InvalidMagic => "Invalid magic number at start of file",
            Error::UnsupportedVersion(_) => "Unsupported wasm version",
            Error::InconsistentLength { .. } => "Inconsistent length",
            Error::Other(msg) => msg,
            Error::HeapOther(ref msg) => &msg[..],
            Error::UnknownValueType(_) => "Invalid or unknown value type",
            Error::UnknownTableElementType(_) => "Unknown table element type",
            Error::NonUtf8String => "Non-UTF-8 string",
            Error::UnknownExternalKind(_) => "Unknown external kind",
            Error::UnknownInternalKind(_) => "Unknown internal kind",
            Error::UnknownOpcode(_) => "Unknown opcode",
            Error::InvalidVarUint1(_) => "Not an unsigned 1-bit integer",
            Error::InvalidVarInt32 => "Not a signed 32-bit integer",
            Error::InvalidVarInt64 => "Not a signed 64-bit integer",
        }
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Self {
        Error::HeapOther(format!("I/O Error: {}", err))
    }
}

/// Unparsed part of the module/section
pub struct Unparsed(pub Vec<u8>);

impl Deserialize for Unparsed {
    type Error = Error;

    fn deserialize<R: io::Read>(reader: &mut R) -> Result<Self, Self::Error> {
        let len = VarUint32::deserialize(reader)?.into();
        let mut vec = vec![0u8; len];
        reader.read_exact(&mut vec[..])?;
        Ok(Unparsed(vec))
    }
}

impl From<Unparsed> for Vec<u8> {
    fn from(u: Unparsed) -> Vec<u8> {
        u.0
    }
}

/// Deserialize module from file.
pub fn deserialize_file<P: AsRef<::std::path::Path>>(p: P) -> Result<Module, Error> {
    use std::io::Read;

    let mut contents = Vec::new();
    ::std::fs::File::open(p)?.read_to_end(&mut contents)?;

    deserialize_buffer(&contents)
}

/// Deserialize deserializable type from buffer.
pub fn deserialize_buffer<T: Deserialize>(contents: &[u8]) -> Result<T, T::Error> {
    let mut reader = io::Cursor::new(contents);
    T::deserialize(&mut reader)
}

/// Create buffer with serialized value.
pub fn serialize<T: Serialize>(val: T) -> Result<Vec<u8>, T::Error> {
    let mut buf = Vec::new();
    val.serialize(&mut buf)?;
    Ok(buf)
}

/// Serialize module to the file
pub fn serialize_to_file<P: AsRef<::std::path::Path>>(p: P, module: Module) -> Result<(), Error>
{
    let mut io = ::std::fs::File::create(p)?;
    module.serialize(&mut io)
}
<|endoftext|>