<commit_before>use std::io;
use std::collections::{BTreeMap, BTreeSet};
use std::collections::btree_map::{self, Entry};

use webidl::ast;

use utils::{multimap_insert, parse_defs};
use webgl_generators::Generator;

use super::types::{Primitive, Type, TypeKind};
use super::named::{Argument, Attribute, Callback, Const, Dictionary, Enum, Field, Interface,
                   Member, Mixin, NamedType, Operation};
use super::{Api, Exts, HIDDEN_NAMES, RENDERING_CONTEXTS};

#[derive(Debug, Default)]
pub struct Registry {
    pub types: BTreeMap<String, NamedType>,
    pub extensions: BTreeSet<String>,
}

/// Helper for iterating over specific kinds of named type
pub struct TypeIter<'a, T: 'a, F: FnMut(&'a NamedType) -> Option<&'a T>> {
    inner: btree_map::Iter<'a, String, NamedType>,
    f: F,
}

impl<'a, T: 'a, F: FnMut(&'a NamedType) -> Option<&'a T>> Iterator for TypeIter<'a, T, F> {
    type Item = (&'a String, &'a T);
    fn next(&mut self) -> Option<Self::Item> {
        // Implement a variation of `flat_map`
        while let Some((k, v)) = self.inner.next() {
            if let Some(t) = (self.f)(v) {
                return Some((k, t));
            }
        }
        None
    }
}

impl Registry {
    /// Construct a new registry given a maximum API version
    /// and a set of extensions to support.
    pub fn new(api: Api, exts: Exts) -> Registry {
        let mut result = Registry::default();

        // First load definitions from all API versions
        // up to the one requested.
        for idl_const in api.idl_consts() {
            for def in parse_defs(idl_const) {
                result.load_definition(def);
            }
        }

        // Next find all requested extensions
        for ext in exts.enumerate() {
            // Make a note that we included this extension
            result.extensions.insert(ext.name.clone());

            // Load the definitions
            for def in parse_defs(ext.idl.as_bytes()) {
                result.load_definition(def);
            }

            // Attach overview doc comment
            let ext_iface = result
                .types
                .get_mut(&ext.name)
                .and_then(NamedType::as_interface_mut)
                .expect(&ext.name);
            ext_iface.doc_comment = ext.overview;

            // Attach individual function doc comments
            for (name, new_fun) in ext.new_funs {
                let members = ext_iface.members.get_mut(&name).unwrap();
                for member in members {
                    if let Member::Operation(ref mut op) = *member {
                        op.doc_comment = new_fun.clone();
                    }
                }
            }
        }

        // Find the latest version of the rendering context, and generate a
        // version-independent rendering context helper type called "GLContext".
        for name in RENDERING_CONTEXTS.into_iter().rev() {
            if let Some(NamedType::Interface(mut iface)) = result.types.get(name.1).cloned() {
                iface.rendering_context = None;
                result
                    .types
                    .insert("GLContext".into(), NamedType::Interface(iface));
                break;
            }
        }

        // Hide types that are listed in our "hidden names" array
        for &hidden_name in HIDDEN_NAMES {
            if let Some(interface) = result
                .types
                .get_mut(hidden_name)
                .and_then(NamedType::as_interface_mut)
            {
                interface.is_hidden = true;
            }
            if let Some(dictionary) = result
                .types
                .get_mut(hidden_name)
                .and_then(NamedType::as_dictionary_mut)
            {
                dictionary.is_hidden = true;
            }
        }

        // Done!
        result
    }

    /// Iterator over types matched by a filtering function
    pub fn iter_types<'a, T, F: FnMut(&'a NamedType) -> Option<&'a T>>(
        &'a self,
        f: F,
    ) -> TypeIter<'a, T, F> {
        TypeIter {
            inner: self.types.iter(),
            f,
        }
    }

    /// Resolves a named type
    pub fn resolve_type(&self, name: &str) -> &NamedType {
        self.types.get(name).expect(name)
    }

    /// Use the specified generator to generate bindings from this registry
    /// and write them to a stream.
    pub fn write_bindings<W, G>(&self, generator: G, output: &mut W) -> io::Result<()>
    where
        G: Generator,
        W: io::Write,
    {
        generator.write(&self, output)
    }

    fn load_const(&mut self, const_: ast::Const) -> Option<(String, Member)> {
        use self::ast::{ConstType, ConstValue};

        let type_ = ast::Type {
            extended_attributes: Vec::new(),
            kind: match const_.type_ {
                ConstType::Boolean => ast::TypeKind::Boolean,
                ConstType::Byte => ast::TypeKind::Byte,
                ConstType::Octet => ast::TypeKind::Octet,
                ConstType::RestrictedDouble => ast::TypeKind::RestrictedDouble,
                ConstType::UnrestrictedDouble => ast::TypeKind::UnrestrictedDouble,
                ConstType::RestrictedFloat => ast::TypeKind::RestrictedFloat,
                ConstType::UnrestrictedFloat => ast::TypeKind::UnrestrictedFloat,
                ConstType::SignedLong => ast::TypeKind::SignedLong,
                ConstType::UnsignedLong => ast::TypeKind::UnsignedLong,
                ConstType::SignedLongLong => ast::TypeKind::SignedLongLong,
                ConstType::UnsignedLongLong => ast::TypeKind::UnsignedLongLong,
                ConstType::SignedShort => ast::TypeKind::SignedShort,
                ConstType::UnsignedShort => ast::TypeKind::UnsignedShort,
                ConstType::Identifier(s) => ast::TypeKind::Identifier(s),
            },
            nullable: const_.nullable,
        };

        Some((
            const_.name,
            Member::Const(Const {
                type_: self.load_type(type_),
                value: match const_.value {
                    ConstValue::BooleanLiteral(b) => format!("{:?}", b),
                    ConstValue::FloatLiteral(v) => format!("{:?}", v),
                    ConstValue::IntegerLiteral(v) => format!("{:?}", v),
                    ConstValue::Null => "None".into(),
                },
            }),
        ))
    }

    fn load_attribute(&mut self, attribute: ast::Attribute) -> Option<(String, Member)> {
        use self::ast::Attribute::*;
        match attribute {
            Regular(a) => {
                let type_ = self.load_type(*a.type_);
                Some((
                    a.name,
                    Member::Attribute(Attribute {
                        type_,
                        setter: !a.read_only,
                        getter: !a.inherits,
                    }),
                ))
            }
            _ => None,
        }
    }

    fn load_argument(&mut self, argument: ast::Argument) -> Argument {
        let type_ = self.load_type(*argument.type_);
        Argument {
            name: argument.name,
            optional: argument.optional,
            type_,
            variadic: argument.variadic,
        }
    }

    fn load_operation(&mut self, operation: ast::Operation) -> Option<(String, Member)> {
        use self::ast::Operation::*;
        use self::ast::ReturnType;
        match operation {
            Regular(o) => if let Some(name) = o.name {
                Some((
                    name,
                    Member::Operation(Operation {
                        args: o.arguments
                            .into_iter()
                            .map(|a| self.load_argument(a))
                            .collect(),
                        return_type: match o.return_type {
                            ReturnType::NonVoid(t) => Some(self.load_type(*t)),
                            ReturnType::Void => None,
                        },
                        doc_comment: String::new(),
                    }),
                ))
            } else {
                None
            },
            _ => None,
        }
    }

    fn load_mixin_member(&mut self, member: ast::MixinMember) -> Option<(String, Member)> {
        use self::ast::MixinMember::*;
        match member {
            Const(c) => self.load_const(c),
            Attribute(a) => self.load_attribute(a),
            Operation(o) => self.load_operation(o),
        }
    }

    fn load_mixin(&mut self, mixin: ast::NonPartialMixin) {
        let mut members = BTreeMap::new();
        for (name, member) in mixin
            .members
            .into_iter()
            .flat_map(|m| self.load_mixin_member(m))
        {
            multimap_insert(&mut members, name, member);
        }

        self.load_named_type(&mixin.name, NamedType::Mixin(Mixin { members }));
    }

    fn load_interface_member(&mut self, member: ast::InterfaceMember) -> Option<(String, Member)> {
        use self::ast::InterfaceMember::*;
        match member {
            Const(c) => self.load_const(c),
            Attribute(a) => self.load_attribute(a),
            Operation(o) => self.load_operation(o),
            _ => None,
        }
    }

    fn load_interface(&mut self, interface: ast::NonPartialInterface) {
        fn has_attr(attrs: &Vec<Box<ast::ExtendedAttribute>>, name: &str) -> bool {
            use self::ast::ExtendedAttribute::*;
            for attr in attrs {
                if let NoArguments(ref other) = **attr {
                    if let &ast::Other::Identifier(ref n) = other {
                        return n == name;
                    }
                }
            }
            false
        }

        let mut members = BTreeMap::new();
        for (name, member) in interface
            .members
            .into_iter()
            .flat_map(|m| self.load_interface_member(m))
        {
            multimap_insert(&mut members, name, member);
        }

        let mut result = Interface {
            inherits: interface.inherits,
            mixins: BTreeSet::new(),
            members,
            is_hidden: false,
            has_class: !has_attr(&interface.extended_attributes, "NoInterfaceObject"),
            rendering_context: None,
            doc_comment: String::new(),
        };

        for &(context_id, context_interface) in RENDERING_CONTEXTS {
            if context_interface == interface.name {
                result.rendering_context = Some(context_id);
                break;
            }
        }

        match self.types.entry(interface.name) {
            Entry::Vacant(v) => {
                v.insert(NamedType::Interface(result));
            }
            Entry::Occupied(o) => {
                assert!(
                    result.members.is_empty(),
                    "Duplicate interface: {}",
                    o.key()
                );
            }
        }
    }

    fn load_includes(&mut self, includes: ast::Includes) {
        if let Some(interface) = self.types
            .get_mut(&includes.includer)
            .and_then(NamedType::as_interface_mut)
        {
            interface.mixins.insert(includes.includee);
        }
    }

    // Load a type kind into the registry under a given name, and return
    // a reference to it.
    fn load_named_type(&mut self, name: &str, named_type: NamedType) -> Type {
        if !self.types.contains_key(name) {
            self.types.insert(name.into(), named_type);
        }
        name.into()
    }

    // Convert an AST type kind into a type kind the generator can understand
    fn load_type_kind(&mut self, kind: ast::TypeKind) -> TypeKind {
        use self::ast::TypeKind::*;

        match kind {
            // Primitives
            Boolean => TypeKind::Primitive(Primitive::Bool),
            Byte => TypeKind::Primitive(Primitive::I8),
            Octet => TypeKind::Primitive(Primitive::U8),
            SignedShort => TypeKind::Primitive(Primitive::I16),
            UnsignedShort => TypeKind::Primitive(Primitive::U16),
            SignedLong => TypeKind::Primitive(Primitive::I32),
            UnsignedLong => TypeKind::Primitive(Primitive::U32),
            SignedLongLong => TypeKind::Primitive(Primitive::I64),
            UnsignedLongLong => TypeKind::Primitive(Primitive::U64),
            RestrictedFloat | UnrestrictedFloat => TypeKind::Primitive(Primitive::F32),
            RestrictedDouble | UnrestrictedDouble => TypeKind::Primitive(Primitive::F64),

            // Strings
            DOMString | USVString => TypeKind::String,
            ByteString => unimplemented!(),

            // TypedArrays
            Int8Array => TypeKind::TypedArray(Primitive::I8),
            Uint8Array => TypeKind::TypedArray(Primitive::U8),
            Int16Array => TypeKind::TypedArray(Primitive::I16),
            Uint16Array => TypeKind::TypedArray(Primitive::U16),
            Int32Array => TypeKind::TypedArray(Primitive::I32),
            Uint32Array => TypeKind::TypedArray(Primitive::U32),
            Float32Array => TypeKind::TypedArray(Primitive::F32),
            Float64Array => TypeKind::TypedArray(Primitive::F64),

            // Sequence
            Sequence(inner) => TypeKind::Sequence(Box::new(self.load_type(*inner))),

            // Identifier
            Identifier(s) => match &*s {
                "BufferSource" => TypeKind::BufferSource,
                "HTMLCanvasElement" => TypeKind::CanvasElement,
                "ArrayBufferView" => TypeKind::ArrayBufferView,
                other => TypeKind::Named(other.into()),
            },

            // Composite
            Union(inners) => {
                if inners.len() <= 2 {
                    TypeKind::Union(
                        inners
                            .into_iter()
                            .map(|inner| self.load_type(*inner))
                            .collect(),
                    )
                } else {
                    TypeKind::Any
                }
            }

            // Misc
            ArrayBuffer => TypeKind::ArrayBuffer,
            Object => TypeKind::Object,
            _ => TypeKind::Any,
        }
    }

    // Convert an AST type into a type the generator can understand
    fn load_type(&mut self, t: ast::Type) -> Type {
        Type {
            kind: self.load_type_kind(t.kind),
            optional: t.nullable,
        }
    }

    fn load_typedef(&mut self, typedef: ast::Typedef) {
        let type_ = self.load_type(*typedef.type_);
        self.load_named_type(&typedef.name, NamedType::Typedef(type_));
    }

    fn load_field(&mut self, field: ast::DictionaryMember) -> Option<(String, Field)> {
        let type_ = self.load_type(*field.type_);

        Some((field.name, Field { type_ }))
    }

    fn load_dictionary(&mut self, dictionary: ast::NonPartialDictionary) {
        let fields = dictionary
            .members
            .into_iter()
            .flat_map(|m| self.load_field(m))
            .collect();

        match self.types.entry(dictionary.name) {
            Entry::Vacant(v) => {
                v.insert(NamedType::Dictionary(Dictionary {
                    inherits: dictionary.inherits,
                    fields,
                    is_hidden: false,
                }));
            }
            Entry::Occupied(mut o) => {
                let key = o.key().clone();
                let d = o.get_mut().as_dictionary_mut().unwrap();
                // Dictionary is being extended, so make these fields all optional
                for (k, mut field) in fields {
                    field.type_.optional = true;
                    if d.fields.insert(k.clone(), field).is_some() {
                        panic!("Duplicate field: {}.{}", key, k);
                    }
                }
            }
        }
    }

    fn load_enum(&mut self, enum_: ast::Enum) {
        let variants = enum_.variants.into_iter().collect();
        self.load_named_type(&enum_.name, NamedType::Enum(Enum { variants }));
    }

    fn load_callback(&mut self, callback: ast::Callback) {
        use self::ast::ReturnType;
        let args = callback
            .arguments
            .into_iter()
            .map(|a| self.load_argument(a))
            .collect();
        let return_type = match callback.return_type {
            ReturnType::NonVoid(t) => Some(self.load_type(*t)),
            ReturnType::Void => None,
        };
        self.load_named_type(
            &callback.name,
            NamedType::Callback(Callback { args, return_type }),
        );
    }

    fn load_definition(&mut self, def: ast::Definition) {
        use self::ast::Definition::*;
        match def {
            Mixin(ast::Mixin::NonPartial(m)) => self.load_mixin(m),
            Interface(ast::Interface::NonPartial(i)) => self.load_interface(i),
            Includes(i) => self.load_includes(i),
            Typedef(t) => self.load_typedef(t),
            Dictionary(ast::Dictionary::NonPartial(d)) => self.load_dictionary(d),
            Enum(e) => self.load_enum(e),
            Callback(c) => self.load_callback(c),
            _ => {}
        }
    }
}
<commit_msg>Remove unnecessary option<commit_after>use std::io;
use std::collections::{BTreeMap, BTreeSet};
use std::collections::btree_map::{self, Entry};

use webidl::ast;

use utils::{multimap_insert, parse_defs};
use webgl_generators::Generator;

use super::types::{Primitive, Type, TypeKind};
use super::named::{Argument, Attribute, Callback, Const, Dictionary, Enum, Field, Interface,
                   Member, Mixin, NamedType, Operation};
use super::{Api, Exts, HIDDEN_NAMES, RENDERING_CONTEXTS};

#[derive(Debug, Default)]
pub struct Registry {
    pub types: BTreeMap<String, NamedType>,
    pub extensions: BTreeSet<String>,
}

/// Helper for iterating over specific kinds of named type
pub struct TypeIter<'a, T: 'a, F: FnMut(&'a NamedType) -> Option<&'a T>> {
    inner: btree_map::Iter<'a, String, NamedType>,
    f: F,
}

impl<'a, T: 'a, F: FnMut(&'a NamedType) -> Option<&'a T>> Iterator for TypeIter<'a, T, F> {
    type Item = (&'a String, &'a T);
    fn next(&mut self) -> Option<Self::Item> {
        // Implement a variation of `flat_map`
        while let Some((k, v)) = self.inner.next() {
            if let Some(t) = (self.f)(v) {
                return Some((k, t));
            }
        }
        None
    }
}

impl Registry {
    /// Construct a new registry given a maximum API version
    /// and a set of extensions to support.
    pub fn new(api: Api, exts: Exts) -> Registry {
        let mut result = Registry::default();

        // First load definitions from all API versions
        // up to the one requested.
        for idl_const in api.idl_consts() {
            for def in parse_defs(idl_const) {
                result.load_definition(def);
            }
        }

        // Next find all requested extensions
        for ext in exts.enumerate() {
            // Make a note that we included this extension
            result.extensions.insert(ext.name.clone());

            // Load the definitions
            for def in parse_defs(ext.idl.as_bytes()) {
                result.load_definition(def);
            }

            // Attach overview doc comment
            let ext_iface = result
                .types
                .get_mut(&ext.name)
                .and_then(NamedType::as_interface_mut)
                .expect(&ext.name);
            ext_iface.doc_comment = ext.overview;

            // Attach individual function doc comments
            for (name, new_fun) in ext.new_funs {
                let members = ext_iface.members.get_mut(&name).unwrap();
                for member in members {
                    if let Member::Operation(ref mut op) = *member {
                        op.doc_comment = new_fun.clone();
                    }
                }
            }
        }

        // Find the latest version of the rendering context, and generate a
        // version-independent rendering context helper type called "GLContext".
        for name in RENDERING_CONTEXTS.into_iter().rev() {
            if let Some(NamedType::Interface(mut iface)) = result.types.get(name.1).cloned() {
                iface.rendering_context = None;
                result
                    .types
                    .insert("GLContext".into(), NamedType::Interface(iface));
                break;
            }
        }

        // Hide types that are listed in our "hidden names" array
        for &hidden_name in HIDDEN_NAMES {
            if let Some(interface) = result
                .types
                .get_mut(hidden_name)
                .and_then(NamedType::as_interface_mut)
            {
                interface.is_hidden = true;
            }
            if let Some(dictionary) = result
                .types
                .get_mut(hidden_name)
                .and_then(NamedType::as_dictionary_mut)
            {
                dictionary.is_hidden = true;
            }
        }

        // Done!
        result
    }

    /// Iterator over types matched by a filtering function
    pub fn iter_types<'a, T, F: FnMut(&'a NamedType) -> Option<&'a T>>(
        &'a self,
        f: F,
    ) -> TypeIter<'a, T, F> {
        TypeIter {
            inner: self.types.iter(),
            f,
        }
    }

    /// Resolves a named type
    pub fn resolve_type(&self, name: &str) -> &NamedType {
        self.types.get(name).expect(name)
    }

    /// Use the specified generator to generate bindings from this registry
    /// and write them to a stream.
    pub fn write_bindings<W, G>(&self, generator: G, output: &mut W) -> io::Result<()>
    where
        G: Generator,
        W: io::Write,
    {
        generator.write(&self, output)
    }

    fn load_const(&mut self, const_: ast::Const) -> Option<(String, Member)> {
        use self::ast::{ConstType, ConstValue};

        let type_ = ast::Type {
            extended_attributes: Vec::new(),
            kind: match const_.type_ {
                ConstType::Boolean => ast::TypeKind::Boolean,
                ConstType::Byte => ast::TypeKind::Byte,
                ConstType::Octet => ast::TypeKind::Octet,
                ConstType::RestrictedDouble => ast::TypeKind::RestrictedDouble,
                ConstType::UnrestrictedDouble => ast::TypeKind::UnrestrictedDouble,
                ConstType::RestrictedFloat => ast::TypeKind::RestrictedFloat,
                ConstType::UnrestrictedFloat => ast::TypeKind::UnrestrictedFloat,
                ConstType::SignedLong => ast::TypeKind::SignedLong,
                ConstType::UnsignedLong => ast::TypeKind::UnsignedLong,
                ConstType::SignedLongLong => ast::TypeKind::SignedLongLong,
                ConstType::UnsignedLongLong => ast::TypeKind::UnsignedLongLong,
                ConstType::SignedShort => ast::TypeKind::SignedShort,
                ConstType::UnsignedShort => ast::TypeKind::UnsignedShort,
                ConstType::Identifier(s) => ast::TypeKind::Identifier(s),
            },
            nullable: const_.nullable,
        };

        Some((
            const_.name,
            Member::Const(Const {
                type_: self.load_type(type_),
                value: match const_.value {
                    ConstValue::BooleanLiteral(b) => format!("{:?}", b),
                    ConstValue::FloatLiteral(v) => format!("{:?}", v),
                    ConstValue::IntegerLiteral(v) => format!("{:?}", v),
                    ConstValue::Null => "None".into(),
                },
            }),
        ))
    }

    fn load_attribute(&mut self, attribute: ast::Attribute) -> Option<(String, Member)> {
        use self::ast::Attribute::*;
        match attribute {
            Regular(a) => {
                let type_ = self.load_type(*a.type_);
                Some((
                    a.name,
                    Member::Attribute(Attribute {
                        type_,
                        setter: !a.read_only,
                        getter: !a.inherits,
                    }),
                ))
            }
            _ => None,
        }
    }

    fn load_argument(&mut self, argument: ast::Argument) -> Argument {
        let type_ = self.load_type(*argument.type_);
        Argument {
            name: argument.name,
            optional: argument.optional,
            type_,
            variadic: argument.variadic,
        }
    }

    fn load_operation(&mut self, operation: ast::Operation) -> Option<(String, Member)> {
        use self::ast::Operation::*;
        use self::ast::ReturnType;
        match operation {
            Regular(o) => if let Some(name) = o.name {
                Some((
                    name,
                    Member::Operation(Operation {
                        args: o.arguments
                            .into_iter()
                            .map(|a| self.load_argument(a))
                            .collect(),
                        return_type: match o.return_type {
                            ReturnType::NonVoid(t) => Some(self.load_type(*t)),
                            ReturnType::Void => None,
                        },
                        doc_comment: String::new(),
                    }),
                ))
            } else {
                None
            },
            _ => None,
        }
    }

    fn load_mixin_member(&mut self, member: ast::MixinMember) -> Option<(String, Member)> {
        use self::ast::MixinMember::*;
        match member {
            Const(c) => self.load_const(c),
            Attribute(a) => self.load_attribute(a),
            Operation(o) => self.load_operation(o),
        }
    }

    fn load_mixin(&mut self, mixin: ast::NonPartialMixin) {
        let mut members = BTreeMap::new();
        for (name, member) in mixin
            .members
            .into_iter()
            .flat_map(|m| self.load_mixin_member(m))
        {
            multimap_insert(&mut members, name, member);
        }

        self.load_named_type(&mixin.name, NamedType::Mixin(Mixin { members }));
    }

    fn load_interface_member(&mut self, member: ast::InterfaceMember) -> Option<(String, Member)> {
        use self::ast::InterfaceMember::*;
        match member {
            Const(c) => self.load_const(c),
            Attribute(a) => self.load_attribute(a),
            Operation(o) => self.load_operation(o),
            _ => None,
        }
    }

    fn load_interface(&mut self, interface: ast::NonPartialInterface) {
        fn has_attr(attrs: &Vec<Box<ast::ExtendedAttribute>>, name: &str) -> bool {
            use self::ast::ExtendedAttribute::*;
            for attr in attrs {
                if let NoArguments(ref other) = **attr {
                    if let &ast::Other::Identifier(ref n) = other {
                        return n == name;
                    }
                }
            }
            false
        }

        let mut members = BTreeMap::new();
        for (name, member) in interface
            .members
            .into_iter()
            .flat_map(|m| self.load_interface_member(m))
        {
            multimap_insert(&mut members, name, member);
        }

        let mut result = Interface {
            inherits: interface.inherits,
            mixins: BTreeSet::new(),
            members,
            is_hidden: false,
            has_class: !has_attr(&interface.extended_attributes, "NoInterfaceObject"),
            rendering_context: None,
            doc_comment: String::new(),
        };

        for &(context_id, context_interface) in RENDERING_CONTEXTS {
            if context_interface == interface.name {
                result.rendering_context = Some(context_id);
                break;
            }
        }

        match self.types.entry(interface.name) {
            Entry::Vacant(v) => {
                v.insert(NamedType::Interface(result));
            }
            Entry::Occupied(o) => {
                assert!(
                    result.members.is_empty(),
                    "Duplicate interface: {}",
                    o.key()
                );
            }
        }
    }

    fn load_includes(&mut self, includes: ast::Includes) {
        if let Some(interface) = self.types
            .get_mut(&includes.includer)
            .and_then(NamedType::as_interface_mut)
        {
            interface.mixins.insert(includes.includee);
        }
    }

    // Load a type kind into the registry under a given name, and return
    // a reference to it.
    fn load_named_type(&mut self, name: &str, named_type: NamedType) -> Type {
        if !self.types.contains_key(name) {
            self.types.insert(name.into(), named_type);
        }
        name.into()
    }

    // Convert an AST type kind into a type kind the generator can understand
    fn load_type_kind(&mut self, kind: ast::TypeKind) -> TypeKind {
        use self::ast::TypeKind::*;

        match kind {
            // Primitives
            Boolean => TypeKind::Primitive(Primitive::Bool),
            Byte => TypeKind::Primitive(Primitive::I8),
            Octet => TypeKind::Primitive(Primitive::U8),
            SignedShort => TypeKind::Primitive(Primitive::I16),
            UnsignedShort => TypeKind::Primitive(Primitive::U16),
            SignedLong => TypeKind::Primitive(Primitive::I32),
            UnsignedLong => TypeKind::Primitive(Primitive::U32),
            SignedLongLong => TypeKind::Primitive(Primitive::I64),
            UnsignedLongLong => TypeKind::Primitive(Primitive::U64),
            RestrictedFloat | UnrestrictedFloat => TypeKind::Primitive(Primitive::F32),
            RestrictedDouble | UnrestrictedDouble => TypeKind::Primitive(Primitive::F64),

            // Strings
            DOMString | USVString => TypeKind::String,
            ByteString => unimplemented!(),

            // TypedArrays
            Int8Array => TypeKind::TypedArray(Primitive::I8),
            Uint8Array => TypeKind::TypedArray(Primitive::U8),
            Int16Array => TypeKind::TypedArray(Primitive::I16),
            Uint16Array => TypeKind::TypedArray(Primitive::U16),
            Int32Array => TypeKind::TypedArray(Primitive::I32),
            Uint32Array => TypeKind::TypedArray(Primitive::U32),
            Float32Array => TypeKind::TypedArray(Primitive::F32),
            Float64Array => TypeKind::TypedArray(Primitive::F64),

            // Sequence
            Sequence(inner) => TypeKind::Sequence(Box::new(self.load_type(*inner))),

            // Identifier
            Identifier(s) => match &*s {
                "BufferSource" => TypeKind::BufferSource,
                "HTMLCanvasElement" => TypeKind::CanvasElement,
                "ArrayBufferView" => TypeKind::ArrayBufferView,
                other => TypeKind::Named(other.into()),
            },

            // Composite
            Union(inners) => {
                if inners.len() <= 2 {
                    TypeKind::Union(
                        inners
                            .into_iter()
                            .map(|inner| self.load_type(*inner))
                            .collect(),
                    )
                } else {
                    TypeKind::Any
                }
            }

            // Misc
            ArrayBuffer => TypeKind::ArrayBuffer,
            Object => TypeKind::Object,
            _ => TypeKind::Any,
        }
    }

    // Convert an AST type into a type the generator can understand
    fn load_type(&mut self, t: ast::Type) -> Type {
        Type {
            kind: self.load_type_kind(t.kind),
            optional: t.nullable,
        }
    }

    fn load_typedef(&mut self, typedef: ast::Typedef) {
        let type_ = self.load_type(*typedef.type_);
        self.load_named_type(&typedef.name, NamedType::Typedef(type_));
    }

    fn load_field(&mut self, field: ast::DictionaryMember) -> (String, Field) {
        let type_ = self.load_type(*field.type_);

        (field.name, Field { type_ })
    }

    fn load_dictionary(&mut self, dictionary: ast::NonPartialDictionary) {
        let fields = dictionary
            .members
            .into_iter()
            .map(|m| self.load_field(m))
            .collect();

        match self.types.entry(dictionary.name) {
            Entry::Vacant(v) => {
                v.insert(NamedType::Dictionary(Dictionary {
                    inherits: dictionary.inherits,
                    fields,
                    is_hidden: false,
                }));
            }
            Entry::Occupied(mut o) => {
                let key = o.key().clone();
                let d = o.get_mut().as_dictionary_mut().unwrap();
                // Dictionary is being extended, so make these fields all optional
                for (k, mut field) in fields {
                    field.type_.optional = true;
                    if d.fields.insert(k.clone(), field).is_some() {
                        panic!("Duplicate field: {}.{}", key, k);
                    }
                }
            }
        }
    }

    fn load_enum(&mut self, enum_: ast::Enum) {
        let variants = enum_.variants.into_iter().collect();
        self.load_named_type(&enum_.name, NamedType::Enum(Enum { variants }));
    }

    fn load_callback(&mut self, callback: ast::Callback) {
        use self::ast::ReturnType;
        let args = callback
            .arguments
            .into_iter()
            .map(|a| self.load_argument(a))
            .collect();
        let return_type = match callback.return_type {
            ReturnType::NonVoid(t) => Some(self.load_type(*t)),
            ReturnType::Void => None,
        };
        self.load_named_type(
            &callback.name,
            NamedType::Callback(Callback { args, return_type }),
        );
    }

    fn load_definition(&mut self, def: ast::Definition) {
        use self::ast::Definition::*;
        match def {
            Mixin(ast::Mixin::NonPartial(m)) => self.load_mixin(m),
            Interface(ast::Interface::NonPartial(i)) => self.load_interface(i),
            Includes(i) => self.load_includes(i),
            Typedef(t) => self.load_typedef(t),
            Dictionary(ast::Dictionary::NonPartial(d)) => self.load_dictionary(d),
            Enum(e) => self.load_enum(e),
            Callback(c) => self.load_callback(c),
            _ => {}
        }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

/* automatically generated by rust-bindgen */

use libc;
use jsapi::*;
use jsfriendapi::JSJitInfo;
use jsval::JSVal;

type c_bool = libc::c_int;

pub struct ProxyTraps {
    pub getPropertyDescriptor: Option<extern "C" fn(*mut JSContext, *mut JSObject, jsid, c_bool, *mut JSPropertyDescriptor) -> c_bool>,
    pub getOwnPropertyDescriptor: Option<extern "C" fn(*mut JSContext, *mut JSObject, jsid, JSBool, *mut JSPropertyDescriptor) -> JSBool>,
    pub defineProperty: Option<extern "C" fn(*mut JSContext, *mut JSObject, jsid, *JSPropertyDescriptor) -> JSBool>,
    pub getOwnPropertyNames: *u8, //XXX need a representation for AutoIdVector&
    pub delete_: Option<extern "C" fn(*mut JSContext, *mut JSObject, jsid, *mut bool) -> JSBool>,
    pub enumerate: *u8, //XXX need a representation for AutoIdVector&

    pub has: Option<extern "C" fn(*mut JSContext, *mut JSObject, jsid, *mut JSBool) -> JSBool>,
    pub hasOwn: Option<extern "C" fn(*mut JSContext, *mut JSObject, jsid, *mut JSBool) -> JSBool>,
    pub get: Option<extern "C" fn(*mut JSContext, *mut JSObject, *mut JSObject, jsid, *mut JSVal) -> JSBool>,
    pub set: Option<extern "C" fn(*mut JSContext, *mut JSObject, *mut JSObject, jsid, JSBool, *mut JSVal) -> JSBool>,
    pub keys: *u8, //XXX need a representation for AutoIdVector&
    pub iterate: Option<extern "C" fn(*mut JSContext, *mut JSObject, uint, *mut JSVal) -> JSBool>,

    pub call: Option<extern "C" fn(*mut JSContext, *mut JSObject, uint, *mut JSVal) -> JSBool>,
    pub construct: Option<extern "C" fn(*mut JSContext, *mut JSObject, uint, *mut JSVal, *mut JSVal) -> JSBool>,
    pub nativeCall: *u8, //XXX need a representation for IsAcceptableThis, NativeImpl, and CallArgs
    pub hasInstance: Option<extern "C" fn(*mut JSContext, *mut JSObject, *mut JSVal, *mut JSBool) -> JSBool>,
    pub typeOf: Option<extern "C" fn(*mut JSContext, *mut JSObject) -> uint>, //XXX JSType enum
    pub objectClassIs: Option<extern "C" fn(*mut JSObject, uint, *mut JSContext) -> JSBool>, //XXX ESClassValue enum
    pub obj_toString: Option<extern "C" fn(*mut JSContext, *mut JSObject) -> *mut JSString>,
    pub fun_toString: Option<extern "C" fn(*mut JSContext, *mut JSObject, uint) -> *mut JSString>,
    //regexp_toShared: *u8,
    pub defaultValue: Option<extern "C" fn(*mut JSContext, *mut JSObject, uint, *mut JSVal) -> JSBool>, //XXX JSType enum
    pub iteratorNext: Option<extern "C" fn(*mut JSContext, *mut JSObject, *mut JSVal) -> JSBool>,
    pub finalize: Option<extern "C" fn(*mut JSFreeOp, *mut JSObject)>,
    pub getElementIfPresent: Option<extern "C" fn(*mut JSContext, *mut JSObject, *mut JSObject, u32, *mut JSVal, *mut JSBool) -> JSBool>,
    pub getPrototypeOf: Option<extern "C" fn(*mut JSContext, *mut JSObject, **mut JSObject) -> JSBool>,
    pub trace: Option<extern "C" fn(*mut JSTracer, *mut JSObject)>,
}

#[link(name = "jsglue")]
extern { }

#[cfg(target_os = "android")]
#[link_args = "-ljsglue -lstdc++ -lgcc -rdynamic"]
extern { }

extern {

//#[rust_stack]
pub fn RUST_JS_NumberValue(d: f64) -> JSVal;

//#[rust_stack]
pub fn CallJitPropertyOp(info: *JSJitInfo, cx: *mut JSContext, thisObj: *mut JSObject, specializedThis: *libc::c_void, vp: *mut JSVal) -> JSBool;

//#[rust_stack]
pub fn CallJitMethodOp(info: *JSJitInfo, cx: *mut JSContext, thisObj: *mut JSObject, specializedThis: *libc::c_void, argc: libc::c_uint, vp: *mut JSVal) -> JSBool;

//#[rust_stack]
pub fn RUST_FUNCTION_VALUE_TO_JITINFO(v: JSVal) -> *JSJitInfo;

pub fn SetFunctionNativeReserved(fun: *mut JSObject, which: libc::size_t, val: *mut JSVal);
pub fn GetFunctionNativeReserved(fun: *mut JSObject, which: libc::size_t) -> *mut JSVal;

pub fn CreateProxyHandler(traps: *ProxyTraps, extra: *libc::c_void) -> *libc::c_void;
pub fn CreateWrapperProxyHandler(traps: *ProxyTraps) -> *libc::c_void;
pub fn NewProxyObject(cx: *mut JSContext, handler: *libc::c_void, priv_: *JSVal,
                      proto: *mut JSObject, parent: *mut JSObject, call: *mut JSObject,
                      construct: *mut JSObject) -> *mut JSObject;
pub fn WrapperNew(cx: *mut JSContext, parent: *mut JSObject, handler: *libc::c_void) -> *mut JSObject;

pub fn GetProxyExtra(obj: *mut JSObject, slot: libc::c_uint) -> JSVal;
pub fn GetProxyPrivate(obj: *mut JSObject) -> JSVal;
pub fn SetProxyExtra(obj: *mut JSObject, slot: libc::c_uint, val: JSVal);

pub fn GetObjectProto(obj: *mut JSObject) -> *mut JSObject;
pub fn GetObjectParent(obj: *mut JSObject) -> *mut JSObject;

pub fn RUST_JSID_IS_INT(id: jsid) -> JSBool;
pub fn RUST_JSID_TO_INT(id: jsid) -> libc::c_int;
pub fn RUST_JSID_IS_STRING(id: jsid) -> JSBool;
pub fn RUST_JSID_TO_STRING(id: jsid) -> *mut JSString;

pub fn RUST_SET_JITINFO(func: *JSFunction, info: *JSJitInfo);

pub fn RUST_INTERNED_STRING_TO_JSID(cx: *mut JSContext, str: *mut JSString) -> jsid;

pub fn DefineFunctionWithReserved(cx: *mut JSContext, obj: *mut JSObject,
                                  name: *libc::c_char, call: JSNative, nargs: libc::c_uint,
                                  attrs: libc::c_uint) -> *mut JSObject;
pub fn GetObjectJSClass(obj: *mut JSObject) -> *JSClass;
pub fn RUST_js_GetErrorMessage(userRef: *libc::c_void, locale: *libc::c_char,
                               errorNumber: libc::c_uint) -> *JSErrorFormatString;
pub fn js_IsObjectProxyClass(obj: *mut JSObject) -> bool;
pub fn js_IsFunctionProxyClass(obj: *mut JSObject) -> bool;
pub fn IsProxyHandlerFamily(obj: *mut JSObject) -> bool;
pub fn GetProxyHandlerExtra(obj: *mut JSObject) -> *libc::c_void;
pub fn GetProxyHandler(obj: *mut JSObject) -> *libc::c_void;
pub fn InvokeGetOwnPropertyDescriptor(handler: *libc::c_void, cx: *mut JSContext, proxy: *mut JSObject, id: jsid, set: JSBool, desc: *mut JSPropertyDescriptor) -> JSBool;
pub fn GetGlobalForObjectCrossCompartment(obj: *mut JSObject) -> *mut JSObject;
pub fn ReportError(cx: *mut JSContext, error: *libc::c_char);
pub fn IsWrapper(obj: *mut JSObject) -> JSBool;
pub fn UnwrapObject(obj: *mut JSObject, stopAtOuter: JSBool, flags: *libc::c_uint) -> *mut JSObject;
}
<commit_msg>Make c_bool type public. Used in public sigs.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

/* automatically generated by rust-bindgen */

use libc;
use jsapi::*;
use jsfriendapi::JSJitInfo;
use jsval::JSVal;

pub type c_bool = libc::c_int;

pub struct ProxyTraps {
    pub getPropertyDescriptor: Option<extern "C" fn(*mut JSContext, *mut JSObject, jsid, c_bool, *mut JSPropertyDescriptor) -> c_bool>,
    pub getOwnPropertyDescriptor: Option<extern "C" fn(*mut JSContext, *mut JSObject, jsid, JSBool, *mut JSPropertyDescriptor) -> JSBool>,
    pub defineProperty: Option<extern "C" fn(*mut JSContext, *mut JSObject, jsid, *JSPropertyDescriptor) -> JSBool>,
    pub getOwnPropertyNames: *u8, //XXX need a representation for AutoIdVector&
    pub delete_: Option<extern "C" fn(*mut JSContext, *mut JSObject, jsid, *mut bool) -> JSBool>,
    pub enumerate: *u8, //XXX need a representation for AutoIdVector&

    pub has: Option<extern "C" fn(*mut JSContext, *mut JSObject, jsid, *mut JSBool) -> JSBool>,
    pub hasOwn: Option<extern "C" fn(*mut JSContext, *mut JSObject, jsid, *mut JSBool) -> JSBool>,
    pub get: Option<extern "C" fn(*mut JSContext, *mut JSObject, *mut JSObject, jsid, *mut JSVal) -> JSBool>,
    pub set: Option<extern "C" fn(*mut JSContext, *mut JSObject, *mut JSObject, jsid, JSBool, *mut JSVal) -> JSBool>,
    pub keys: *u8, //XXX need a representation for AutoIdVector&
    pub iterate: Option<extern "C" fn(*mut JSContext, *mut JSObject, uint, *mut JSVal) -> JSBool>,

    pub call: Option<extern "C" fn(*mut JSContext, *mut JSObject, uint, *mut JSVal) -> JSBool>,
    pub construct: Option<extern "C" fn(*mut JSContext, *mut JSObject, uint, *mut JSVal, *mut JSVal) -> JSBool>,
    pub nativeCall: *u8, //XXX need a representation for IsAcceptableThis, NativeImpl, and CallArgs
    pub hasInstance: Option<extern "C" fn(*mut JSContext, *mut JSObject, *mut JSVal, *mut JSBool) -> JSBool>,
    pub typeOf: Option<extern "C" fn(*mut JSContext, *mut JSObject) -> uint>, //XXX JSType enum
    pub objectClassIs: Option<extern "C" fn(*mut JSObject, uint, *mut JSContext) -> JSBool>, //XXX ESClassValue enum
    pub obj_toString: Option<extern "C" fn(*mut JSContext, *mut JSObject) -> *mut JSString>,
    pub fun_toString: Option<extern "C" fn(*mut JSContext, *mut JSObject, uint) -> *mut JSString>,
    //regexp_toShared: *u8,
    pub defaultValue: Option<extern "C" fn(*mut JSContext, *mut JSObject, uint, *mut JSVal) -> JSBool>, //XXX JSType enum
    pub iteratorNext: Option<extern "C" fn(*mut JSContext, *mut JSObject, *mut JSVal) -> JSBool>,
    pub finalize: Option<extern "C" fn(*mut JSFreeOp, *mut JSObject)>,
    pub getElementIfPresent: Option<extern "C" fn(*mut JSContext, *mut JSObject, *mut JSObject, u32, *mut JSVal, *mut JSBool) -> JSBool>,
    pub getPrototypeOf: Option<extern "C" fn(*mut JSContext, *mut JSObject, **mut JSObject) -> JSBool>,
    pub trace: Option<extern "C" fn(*mut JSTracer, *mut JSObject)>,
}

#[link(name = "jsglue")]
extern { }

#[cfg(target_os = "android")]
#[link_args = "-ljsglue -lstdc++ -lgcc -rdynamic"]
extern { }

extern {

//#[rust_stack]
pub fn RUST_JS_NumberValue(d: f64) -> JSVal;

//#[rust_stack]
pub fn CallJitPropertyOp(info: *JSJitInfo, cx: *mut JSContext, thisObj: *mut JSObject, specializedThis: *libc::c_void, vp: *mut JSVal) -> JSBool;

//#[rust_stack]
pub fn CallJitMethodOp(info: *JSJitInfo, cx: *mut JSContext, thisObj: *mut JSObject, specializedThis: *libc::c_void, argc: libc::c_uint, vp: *mut JSVal) -> JSBool;

//#[rust_stack]
pub fn RUST_FUNCTION_VALUE_TO_JITINFO(v: JSVal) -> *JSJitInfo;

pub fn SetFunctionNativeReserved(fun: *mut JSObject, which: libc::size_t, val: *mut JSVal);
pub fn GetFunctionNativeReserved(fun: *mut JSObject, which: libc::size_t) -> *mut JSVal;

pub fn CreateProxyHandler(traps: *ProxyTraps, extra: *libc::c_void) -> *libc::c_void;
pub fn CreateWrapperProxyHandler(traps: *ProxyTraps) -> *libc::c_void;
pub fn NewProxyObject(cx: *mut JSContext, handler: *libc::c_void, priv_: *JSVal,
                      proto: *mut JSObject, parent: *mut JSObject, call: *mut JSObject,
                      construct: *mut JSObject) -> *mut JSObject;
pub fn WrapperNew(cx: *mut JSContext, parent: *mut JSObject, handler: *libc::c_void) -> *mut JSObject;

pub fn GetProxyExtra(obj: *mut JSObject, slot: libc::c_uint) -> JSVal;
pub fn GetProxyPrivate(obj: *mut JSObject) -> JSVal;
pub fn SetProxyExtra(obj: *mut JSObject, slot: libc::c_uint, val: JSVal);

pub fn GetObjectProto(obj: *mut JSObject) -> *mut JSObject;
pub fn GetObjectParent(obj: *mut JSObject) -> *mut JSObject;

pub fn RUST_JSID_IS_INT(id: jsid) -> JSBool;
pub fn RUST_JSID_TO_INT(id: jsid) -> libc::c_int;
pub fn RUST_JSID_IS_STRING(id: jsid) -> JSBool;
pub fn RUST_JSID_TO_STRING(id: jsid) -> *mut JSString;

pub fn RUST_SET_JITINFO(func: *JSFunction, info: *JSJitInfo);

pub fn RUST_INTERNED_STRING_TO_JSID(cx: *mut JSContext, str: *mut JSString) -> jsid;

pub fn DefineFunctionWithReserved(cx: *mut JSContext, obj: *mut JSObject,
                                  name: *libc::c_char, call: JSNative, nargs: libc::c_uint,
                                  attrs: libc::c_uint) -> *mut JSObject;
pub fn GetObjectJSClass(obj: *mut JSObject) -> *JSClass;
pub fn RUST_js_GetErrorMessage(userRef: *libc::c_void, locale: *libc::c_char,
                               errorNumber: libc::c_uint) -> *JSErrorFormatString;
pub fn js_IsObjectProxyClass(obj: *mut JSObject) -> bool;
pub fn js_IsFunctionProxyClass(obj: *mut JSObject) -> bool;
pub fn IsProxyHandlerFamily(obj: *mut JSObject) -> bool;
pub fn GetProxyHandlerExtra(obj: *mut JSObject) -> *libc::c_void;
pub fn GetProxyHandler(obj: *mut JSObject) -> *libc::c_void;
pub fn InvokeGetOwnPropertyDescriptor(handler: *libc::c_void, cx: *mut JSContext, proxy: *mut JSObject, id: jsid, set: JSBool, desc: *mut JSPropertyDescriptor) -> JSBool;
pub fn GetGlobalForObjectCrossCompartment(obj: *mut JSObject) -> *mut JSObject;
pub fn ReportError(cx: *mut JSContext, error: *libc::c_char);
pub fn IsWrapper(obj: *mut JSObject) -> JSBool;
pub fn UnwrapObject(obj: *mut JSObject, stopAtOuter: JSBool, flags: *libc::c_uint) -> *mut JSObject;
}
<|endoftext|>


<commit_before>// binary.rs

pub trait Encode {
    fn encode(buf: &[const u8]) -> ~[u8];
}

pub trait Decode {
    fn decode(buf: &[const u8]) -> ~[u8];
}

pub trait Convert {
    static fn convert(buf: &[const u8], to: self, from: self) -> ~[u8];
}

pub trait Codec : Encode, Decode, Convert {
}

// `end` indicates whether the Decode method have encountered paddings or not.
struct DecodeResult {
    end: bool,
    ndecoded: uint,
}

trait BinaryEncoder {
    fn encode(&self, dst: &[mut u8], src: &[const u8]);
    fn encoded_len(&self, src_length: uint) -> uint;
    fn encode_bytes(&self, src: &[const u8]) -> ~[u8];
}

trait BinaryDecoder {
    fn decode(&self, dst: &[mut u8], src: &[const u8]) -> DecodeResult;
    fn decoded_len(&self, src_length: uint) -> uint;
    fn decode_bytes(&self, src: &[const u8]) -> ~[u8];
}

pub enum Binary {
    Base16,
    Base32,
    Base64,
    Base32Hex,
    Base64Url,
}

pub impl Binary : Codec {
    fn encode(buf: &[const u8]) -> ~[u8] {
        match self {
            Base16    => base16::encode(buf),
            Base32    => base32::encode(buf),
            Base64    => base64::encode(buf),
            Base32Hex => base32::hex_encode(buf),
            Base64Url => base64::urlsafe_encode(buf)
        }
    }

    fn decode(buf: &[const u8]) -> ~[u8] {
        match self {
            Base16    => base16::decode(buf),
            Base32    => base32::decode(buf),
            Base64    => base64::decode(buf),
            Base32Hex => base32::hex_decode(buf),
            Base64Url => base64::urlsafe_decode(buf)
        }
    }

    static fn convert(buf: &[const u8], to: Binary, from: Binary) -> ~[u8] {
        let buf = from.decode(buf);
        let buf = to.encode(buf);
        move buf
    }
}
<commit_msg>Use explicit self for binary::Codec<commit_after>// binary.rs

pub trait Encode {
    fn encode(&self, buf: &[const u8]) -> ~[u8];
}

pub trait Decode {
    fn decode(&self, buf: &[const u8]) -> ~[u8];
}

pub trait Convert {
    static fn convert(buf: &[const u8], to: self, from: self) -> ~[u8];
}

pub trait Codec : Encode, Decode, Convert {
}

// `end` indicates whether the Decode method have encountered paddings or not.
struct DecodeResult {
    end: bool,
    ndecoded: uint,
}

trait BinaryEncoder {
    fn encode(&self, dst: &[mut u8], src: &[const u8]);
    fn encoded_len(&self, src_length: uint) -> uint;
    fn encode_bytes(&self, src: &[const u8]) -> ~[u8];
}

trait BinaryDecoder {
    fn decode(&self, dst: &[mut u8], src: &[const u8]) -> DecodeResult;
    fn decoded_len(&self, src_length: uint) -> uint;
    fn decode_bytes(&self, src: &[const u8]) -> ~[u8];
}

pub enum Binary {
    Base16,
    Base32,
    Base64,
    Base32Hex,
    Base64Url,
}

pub impl Binary : Codec {
    fn encode(&self, buf: &[const u8]) -> ~[u8] {
        match *self {
            Base16    => base16::encode(buf),
            Base32    => base32::encode(buf),
            Base64    => base64::encode(buf),
            Base32Hex => base32::hex_encode(buf),
            Base64Url => base64::urlsafe_encode(buf)
        }
    }

    fn decode(&self, buf: &[const u8]) -> ~[u8] {
        match *self {
            Base16    => base16::decode(buf),
            Base32    => base32::decode(buf),
            Base64    => base64::decode(buf),
            Base32Hex => base32::hex_decode(buf),
            Base64Url => base64::urlsafe_decode(buf)
        }
    }

    static fn convert(buf: &[const u8], to: Binary, from: Binary) -> ~[u8] {
        let buf = from.decode(buf);
        let buf = to.encode(buf);
        move buf
    }
}
<|endoftext|>


<commit_before>//! This program reads the RLE sprite sheets and list files which contain the
//! id's for the sprite type, and converts them into an sqlite database. While
//! an sqlite database maybe isn't the most efficient, it's at least somewhat
//! portable and quick to iterate with. Let alone compressing and transferring.
//!
//! NOTES:
//!  - So it seems that the ID value in the list file isn't global to the entire
//!    game, and instead only global to the list file itself. So at this point
//!    I'm thinking that assigning a global ID might be a good idea, though
//!    this ID would just be for referencing the objects which we pull, and not
//!    between objects because they could change depending on the input data.
//!  - The best way it seems to match the data from the `rle` and `list` tables
//!    is to use the file number and file index

extern crate core_compat;
extern crate rusqlite as sql;

use std::path::Path;
use std::fs::File;
use std::fs::read_dir;
use std::io::Read;

use core_compat::rle::{ResourceFile, Resource};
use core_compat::lst::List;
use core_compat::error::Error;

use sql::Connection;

// This is the list of data folder's and list files for them
static FOLDER_ENTRIES: [(&'static str, &'static str, &'static str); 5] = [
    ("Bullets", "../data/RLEs/Bul", "../data/RLEs/bul.lst"),
    ("Icons", "../data/RLEs/Ico", "../data/RLEs/ico.lst"),
    ("Objects", "../data/RLEs/Obj", "../data/RLEs/obj.lst"),
    ("Tiles", "../data/RLEs/Tle", "../data/RLEs/tle.lst"),
    ("Interface", "../data/RLEs/Int", "../data/RLEs/int.lst"),
    // The sounds one is the only one which is a little different...
    // ("Sounds", "../data/RLEs/Snd", "../data/RLEs/snd.lst"),
];

fn main() {

    // create sqlite database
    // let connection = Connection::open_in_memory().unwrap();
    let mut connection = Connection::open(Path::new("./rm.sqlite")).unwrap();

    let _ = connection.execute("DROP TABLE list", &[]);
    let _ = connection.execute("DROP TABLE rle", &[]);

    connection.execute(
        "CREATE TABLE list (
            gid      INTEGER PRIMARY KEY,
            type     TEXT NOT NULL,
            file_num INTEGER,
            file_idx INTEGER,
            name     TEXT NOT NULL,
            list_id  INTEGER
        )", &[]).unwrap();

    connection.execute(
        "CREATE TABLE rle (
            gid      INTEGER PRIMARY KEY,
            type     TEXT NOT NULL,
            file_num INTEGER,
            file_idx INTEGER,
            length   INTEGER,
            offset_x INTEGER,
            offset_y INTEGER,
            width    INTEGER,
            height   INTEGER,
            image    BLOB
        )", &[]).unwrap();

    // parse the list file and insert them into the database
    for &(_type, folder, list) in FOLDER_ENTRIES.iter() {

        println!("file: {:?}", _type);

        // load the data from the list file
        let list_path = Path::new(list);
        let list = load_list_data(&list_path).unwrap();
        println!("list.items.len() == {:?}", list.items.len());

        // Commit all of the list objects in one transaction
        {
            let tx = connection.transaction().unwrap();
            for item in list.items {
                // insert the data into the database
                tx.execute(
                    "INSERT INTO list (type, name, list_id, file_num, file_idx)
                    VALUES (?1, ?2, ?3, ?4, ?5)",
                    &[&_type, &item.name, &item.id, &item.file_number, &item.index]
                ).unwrap();
            }
            tx.commit().unwrap();
        }

        // load the actual sprites into the database
        let rle_paths = read_dir(folder).unwrap();
        let mut resources = Vec::<Resource>::new();

        for path in rle_paths {

            // open and read the file
            let path = path.unwrap();
            let mut file = File::open(path.path()).unwrap();
            let mut bytes = Vec::<u8>::new();
            file.read_to_end(&mut bytes).unwrap();

            // parse the file number
            let mut file_num = 0xFFFF;
            if let Some(stem) = path.path().file_stem() {
                if let Some(stem) = stem.to_str() {
                    let num: String = stem.matches(char::is_numeric).collect();
                    file_num = num.parse().unwrap_or(0xFFFF);
                }
            }

            // parse && append results
            let res_file = ResourceFile::load(file_num, &mut bytes).unwrap();
            for resource in res_file.resources {
                resources.push(resource);
            }

        }

        // Commit all of the sprite objects in one transaction
        {
            let tx = connection.transaction().unwrap();
            for ref rle in &resources {

                // TODO: hack the Vec<Pixel> into a Vec<U8>
                let mut img = Vec::<u8>::new();
                for ref pixel in &rle.image {
                    img.push(pixel.r);
                    img.push(pixel.g);
                    img.push(pixel.b);
                    img.push(pixel.a);
                }

                // insert the data into the database
                tx.execute(
                    "INSERT INTO rle (
                        type,   file_num, file_idx,
                        length, offset_x, offset_y,
                        width,  height,   image)
                    VALUES (?1, ?2, ?3,
                            ?4, ?5, ?6,
                            ?7, ?8, ?9)",
                    &[&_type,   &rle.file_num, &rle.index,
                    &rle.len,   &rle.offset_x, &rle.offset_y,
                    &rle.width, &rle.height,   &img]
                ).unwrap();
            }
            tx.commit().unwrap();

        }
        println!("resources.len() == {:?}", &resources.len());
    }

    // check the # of entries in the database
    let mut stmt = connection.prepare("SELECT list_id, name FROM list").unwrap();
    let lst_itr = stmt.query_map(&[], |row| {
        let id: u32 = row.get(0);
        let name: String = row.get(1);
        (id, name)
    }).unwrap();
    let lst_vec = lst_itr.filter_map(|x| x.ok()).collect::<Vec<_>>();
    println!("lst_vec.len(): {:?}", lst_vec.len());
}

fn load_list_data(list_path: &Path) -> Result<List, Error> {
    let mut list_file = File::open(list_path)?;
    let mut list_bytes = Vec::<u8>::new();
    list_file.read_to_end(&mut list_bytes)?;
    List::load(&list_bytes, false)
}

// #[allow(dead_code)]
// fn parse_entries() {
//     // parse entries
//     for &(_type, folder, list) in FOLDER_ENTRIES.iter() {
//         println!("Parsing: {:?}", _type);
//         let mut map = HashSet::<u32>::new();
//
//         let data_folder = Path::new(folder);
//         let data_list = Path::new(list);
//
//         let entries = read_rle_dir(&data_list, &data_folder).unwrap();
//         let mut missing_ids = 0;
//
//         for entry in entries {
//             if let Some(id) = entry.id {
//                 let success = map.insert(id);
//                 if !success {
//                     // testing for ID doubles
//                     println!("double id: {:?}", &id);
//                 }
//             } else {
//                 // testing missing ID
//                 missing_ids += 1;
//             }
//         }
//         println!("\tentries      : {:?}", map.len());
//         println!("\tmissing id's : {:?}", missing_ids);
//     }
// }<commit_msg>remove old parse_entries function<commit_after>//! This program reads the RLE sprite sheets and list files which contain the
//! id's for the sprite type, and converts them into an sqlite database. While
//! an sqlite database maybe isn't the most efficient, it's at least somewhat
//! portable and quick to iterate with. Let alone compressing and transferring.
//!
//! NOTES:
//!  - So it seems that the ID value in the list file isn't global to the entire
//!    game, and instead only global to the list file itself. So at this point
//!    I'm thinking that assigning a global ID might be a good idea, though
//!    this ID would just be for referencing the objects which we pull, and not
//!    between objects because they could change depending on the input data.
//!  - The best way it seems to match the data from the `rle` and `list` tables
//!    is to use the file number and file index

extern crate core_compat;
extern crate rusqlite as sql;

use std::path::Path;
use std::fs::File;
use std::fs::read_dir;
use std::io::Read;

use core_compat::rle::{ResourceFile, Resource};
use core_compat::lst::List;
use core_compat::error::Error;

use sql::Connection;

// This is the list of data folder's and list files for them
static FOLDER_ENTRIES: [(&'static str, &'static str, &'static str); 5] = [
    ("Bullets", "../data/RLEs/Bul", "../data/RLEs/bul.lst"),
    ("Icons", "../data/RLEs/Ico", "../data/RLEs/ico.lst"),
    ("Objects", "../data/RLEs/Obj", "../data/RLEs/obj.lst"),
    ("Tiles", "../data/RLEs/Tle", "../data/RLEs/tle.lst"),
    ("Interface", "../data/RLEs/Int", "../data/RLEs/int.lst"),
    // The sounds one is the only one which is a little different...
    // ("Sounds", "../data/RLEs/Snd", "../data/RLEs/snd.lst"),
];

fn main() {

    // create sqlite database
    // let connection = Connection::open_in_memory().unwrap();
    let mut connection = Connection::open(Path::new("./rm.sqlite")).unwrap();

    let _ = connection.execute("DROP TABLE list", &[]);
    let _ = connection.execute("DROP TABLE rle", &[]);

    connection.execute(
        "CREATE TABLE list (
            gid      INTEGER PRIMARY KEY,
            type     TEXT NOT NULL,
            file_num INTEGER,
            file_idx INTEGER,
            name     TEXT NOT NULL,
            list_id  INTEGER
        )", &[]).unwrap();

    connection.execute(
        "CREATE TABLE rle (
            gid      INTEGER PRIMARY KEY,
            type     TEXT NOT NULL,
            file_num INTEGER,
            file_idx INTEGER,
            length   INTEGER,
            offset_x INTEGER,
            offset_y INTEGER,
            width    INTEGER,
            height   INTEGER,
            image    BLOB
        )", &[]).unwrap();

    // parse the list file and insert them into the database
    for &(_type, folder, list) in FOLDER_ENTRIES.iter() {

        println!("file: {:?}", _type);

        // load the data from the list file
        let list_path = Path::new(list);
        let list = load_list_data(&list_path).unwrap();
        println!("list.items.len() == {:?}", list.items.len());

        // Commit all of the list objects in one transaction
        {
            let tx = connection.transaction().unwrap();
            for item in list.items {
                // insert the data into the database
                tx.execute(
                    "INSERT INTO list (type, name, list_id, file_num, file_idx)
                    VALUES (?1, ?2, ?3, ?4, ?5)",
                    &[&_type, &item.name, &item.id, &item.file_number, &item.index]
                ).unwrap();
            }
            tx.commit().unwrap();
        }

        // load the actual sprites into the database
        let rle_paths = read_dir(folder).unwrap();
        let mut resources = Vec::<Resource>::new();

        for path in rle_paths {

            // open and read the file
            let path = path.unwrap();
            let mut file = File::open(path.path()).unwrap();
            let mut bytes = Vec::<u8>::new();
            file.read_to_end(&mut bytes).unwrap();

            // parse the file number
            let mut file_num = 0xFFFF;
            if let Some(stem) = path.path().file_stem() {
                if let Some(stem) = stem.to_str() {
                    let num: String = stem.matches(char::is_numeric).collect();
                    file_num = num.parse().unwrap_or(0xFFFF);
                }
            }

            // parse && append results
            let res_file = ResourceFile::load(file_num, &mut bytes).unwrap();
            for resource in res_file.resources {
                resources.push(resource);
            }

        }

        // Commit all of the sprite objects in one transaction
        {
            let tx = connection.transaction().unwrap();
            for ref rle in &resources {

                // TODO: hack the Vec<Pixel> into a Vec<U8>
                let mut img = Vec::<u8>::new();
                for ref pixel in &rle.image {
                    img.push(pixel.r);
                    img.push(pixel.g);
                    img.push(pixel.b);
                    img.push(pixel.a);
                }

                // insert the data into the database
                tx.execute(
                    "INSERT INTO rle (
                        type,   file_num, file_idx,
                        length, offset_x, offset_y,
                        width,  height,   image)
                    VALUES (?1, ?2, ?3,
                            ?4, ?5, ?6,
                            ?7, ?8, ?9)",
                    &[&_type,   &rle.file_num, &rle.index,
                    &rle.len,   &rle.offset_x, &rle.offset_y,
                    &rle.width, &rle.height,   &img]
                ).unwrap();
            }
            tx.commit().unwrap();

        }
        println!("resources.len() == {:?}", &resources.len());
    }

    // check the # of entries in the database
    let mut stmt = connection.prepare("SELECT list_id, name FROM list").unwrap();
    let lst_itr = stmt.query_map(&[], |row| {
        let id: u32 = row.get(0);
        let name: String = row.get(1);
        (id, name)
    }).unwrap();
    let lst_vec = lst_itr.filter_map(|x| x.ok()).collect::<Vec<_>>();
    println!("lst_vec.len(): {:?}", lst_vec.len());
}

fn load_list_data(list_path: &Path) -> Result<List, Error> {
    let mut list_file = File::open(list_path)?;
    let mut list_bytes = Vec::<u8>::new();
    list_file.read_to_end(&mut list_bytes)?;
    List::load(&list_bytes, false)
}
<|endoftext|>


<commit_before>use std::cmp::Ordering;

#[derive(PartialEq, Eq, Clone, Debug)]
pub struct Rev<T>(pub T);

impl<T: PartialOrd> PartialOrd for Rev<T> {
    fn partial_cmp(&self, other: &Rev<T>) -> Option<Ordering> {
        other.0.partial_cmp(&self.0)
    }
}

impl<T: Ord> Ord for Rev<T> {
    fn cmp(&self, other: &Rev<T>) -> Ordering {
        other.0.cmp(&self.0)
    }
}
<commit_msg>Test Rev<commit_after>use std::cmp::Ordering;

#[derive(PartialEq, Eq, Clone, Debug)]
pub struct Rev<T>(pub T);

impl<T: PartialOrd> PartialOrd for Rev<T> {
    fn partial_cmp(&self, other: &Rev<T>) -> Option<Ordering> {
        other.0.partial_cmp(&self.0)
    }
}

impl<T: Ord> Ord for Rev<T> {
    fn cmp(&self, other: &Rev<T>) -> Ordering {
        other.0.cmp(&self.0)
    }
}

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

    #[test]
    fn test_rev() {
        let mut xs = (0..5).map(|i| Rev((i * i * i % 13, i))).collect::<Vec<_>>();

        xs.sort();

        let xs = xs.into_iter().map(|Rev(t)| t).collect::<Vec<_>>();

        assert_eq!(xs, vec![(12, 4), (8, 2), (1, 3), (1, 1), (0, 0)]);
    }
}
<|endoftext|>


<commit_before>/// Parser

use std::char;

#[deriving(ToStr)]
pub enum Expr {
    Empty,
    Range(char, char),
    Concatenate(~Expr, ~Expr),
    Alternate(~Expr, ~Expr),
    Repeat(~Expr, uint, Option<uint>, Greedy),
}

#[deriving(ToStr)]
pub enum Greedy {
    NonGreedy,
    Greedy,
}

/// Parse a regular expression into an AST.
pub fn parse(s: &str) -> Expr {
    let (e, s_) = parse_main(s);
    if s_.len() > 0 {
        fail!("invalid syntax")
    } else {
        e
    }
}

fn parse_main<'a>(s_init: &'a str) -> (Expr, &'a str) {
    let mut stack: ~[Expr] = ~[];
    let mut s: &'a str = s_init;
    loop {
        match uncons(s) {
            Some((c, s1)) => {
                match c {
                    '.' => {
                        stack.push(Range('\0', char::MAX));
                        s = s1;
                    },
                    '|' => {
                        let left = coalesce(&mut stack);
                        let (right, s_) = parse_main(s1);
                        stack.push(Alternate(~left, ~right));
                        s = s_;
                    },
                    '(' => {
                        // Collect everything before the parens
                        let before = coalesce(&mut stack);
                        // Parse inside the parens
                        let (inner, s_) = parse_main(s1);
                        // Match the closing paren
                        match uncons(s_) {
                            Some((')', s_1)) => {
                                stack.push(concatenate(before, inner));
                                s = s_1;
                            },
                            _ => fail!("unbalanced parenthesis")
                        }
                    },
                    ')' => break,
                    _ => {
                        stack.push(Range(c, c));
                        s = s1;
                    }
                }
            },
            None => break
        }
    }

    (coalesce(&mut stack), s)
}

/// Fold the elements of the vector using `Concatenate`, clearing the
/// vector in the process.
fn coalesce(stack: &mut ~[Expr]) -> Expr {
    while stack.len() > 1 {
        let right = stack.pop();
        let left = stack.pop();
        stack.push(concatenate(left, right));
    }
    stack.pop_opt().unwrap_or(Empty)
}

/// Smart constructor for `Concatenate`.  If either of the children is
/// `Empty`, it is ignored.
fn concatenate(left: Expr, right: Expr) -> Expr {
    match left {
        Empty => right,
        _ => match right {
            Empty => left,
            _ => Concatenate(~left, ~right)
        }
    }
}

/// Return the first element and the rest of a `str`, or `None` if
/// empty.
fn uncons<'a>(s: &'a str) -> Option<(char, &'a str)> {
    if s.len() > 0 {
        Some(s.slice_shift_char())
    } else {
        None
    }
}
<commit_msg>Rewrite parser to use recursive descent<commit_after>/// Parser

use std::char;

#[deriving(ToStr)]
pub enum Expr {
    Empty,
    Range(char, char),
    Concatenate(~[Expr]),
    Alternate(~[Expr]),
    Repeat(~Expr, uint, Option<uint>, Greedy),
}

#[deriving(ToStr)]
pub enum Greedy {
    NonGreedy,
    Greedy,
}

/// Parse a regular expression into an AST.
pub fn parse(s: &str) -> Expr {
    let (e, s_) = p_alternate(s);
    if s_.len() > 0 {
        // p_alternate() only terminates on an empty string or an extra
        // paren.  Since the string isn't empty, we infer the latter.
        fail!("unbalanced parenthesis")
    } else {
        e
    }
}

fn p_alternate<'a>(s_init: &'a str) -> (Expr, &'a str) {
    let mut items: ~[Expr] = ~[];
    let mut s: &'a str = s_init;

    loop {
        let (e, s_) = p_concatenate(s);
        items.push(e);
        s = s_;
        match uncons(s_) {
            Some((c, s_1)) => {
                match c {
                    '|' => { s = s_1; },
                    ')' => break,
                    _ => fail!("something bad happened; it's really bad")
                }
            },
            None => break
        }
    }

    (match items {
        [] => Empty,
        [e] => e,
        _ => Alternate(items)
    }, s)
}

fn p_concatenate<'a>(s_init: &'a str) -> (Expr, &'a str) {
    let mut items: ~[Expr] = ~[];
    let mut s: &'a str = s_init;

    loop { match uncons(s) {
        Some((c, s1)) => match c {
            '|' | ')' => break,
            '(' => {
                // Parse inside the parens
                let (e, s_) = p_alternate(s1);
                // Match the closing paren
                match uncons(s_) {
                    Some((')', s_1)) => {
                        push_ignore_empty(&mut items, e);
                        s = s_1;
                    },
                    _ => fail!("mismatched parenthesis")
                }
            },
            '.' => {
                items.push(Range('\0', char::MAX));
                s = s1;
            },
            _ => {
                items.push(Range(c, c));
                s = s1;
            }
        },
        None => break
    }}

    (match items {
        [] => Empty,
        [e] => e,
        _ => Concatenate(items)
    }, s)
}

#[inline]
fn push_ignore_empty(items: &mut ~[Expr], e: Expr) {
    match e {
        Empty => {},
        _ => items.push(e)
    }
}

/// Return the first element and the rest of a `str`, or `None` if
/// empty.
fn uncons<'a>(s: &'a str) -> Option<(char, &'a str)> {
    if s.len() > 0 {
        Some(s.slice_shift_char())
    } else {
        None
    }
}
<|endoftext|>


<commit_before>mod common;

use std::env::{self, VarError};
use std::mem::size_of;
use std::process::{exit, Child, Command, ExitStatus};
use std::thread;
use std::time::Duration;

use rand::Rng;

use sled::Config;

use common::cleanup;

const TEST_ENV_VAR: &str = "SLED_CRASH_TEST";
const N_TESTS: usize = 100;
const CYCLE: usize = 256;

// test names, also used as dir names
const RECOVERY_WITH_SNAPSHOT: &str = "crash_recovery_with_runtime_snapshot";
const RECOVERY_NO_SNAPSHOT: &str = "crash_recovery_no_runtime_snapshot";

fn main() {
    match env::var(TEST_ENV_VAR) {
        Err(VarError::NotPresent) => {
            test_crash_recovery_with_runtime_snapshot();
            test_crash_recovery_no_runtime_snapshot();
        }

        Ok(ref s) if s == RECOVERY_WITH_SNAPSHOT => run_with_snapshot(s),
        Ok(ref s) if s == RECOVERY_NO_SNAPSHOT => run_without_snapshot(s),

        Ok(_) | Err(_) => panic!("invalid crash test case"),
    }
}

/// Verifies that the keys in the tree are correctly recovered.
/// Panics if they are incorrect.
/// Returns the key that should be resumed at, and the current cycle value.
fn verify(tree: &sled::Tree) -> (u32, u32) {
    // key 0 should always be the highest value, as that's where we increment
    // at some point, it might go down by one
    // it should never return, or go down again after that
    let mut iter = tree.iter();
    let highest = match iter.next() {
        Some(Ok((_k, v))) => slice_to_u32(&*v),
        Some(Err(e)) => panic!("{:?}", e),
        None => return (0, 0),
    };

    let highest_vec = u32_to_vec(highest);

    // find how far we got
    let mut contiguous: u32 = 0;
    let mut lowest = 0;
    for res in iter {
        let (_k, v) = res.unwrap();
        if &v[..4] == &highest_vec[..4] {
            contiguous += 1;
        } else {
            let expected = if highest == 0 {
                CYCLE as u32 - 1
            } else {
                (highest - 1) % CYCLE as u32
            };
            let actual = slice_to_u32(&*v);
            assert_eq!(expected, actual);
            lowest = actual;
            break;
        }
    }

    // ensure nothing changes after this point
    let low_beginning = u32_to_vec(contiguous + 1);

    for res in tree.range(&*low_beginning..) {
        let (k, v): (sled::IVec, _) = res.unwrap();
        assert_eq!(
            slice_to_u32(&*v),
            lowest,
            "expected key {} to have value {}, instead it had value {} in db: {:?}",
            slice_to_u32(&*k),
            lowest,
            slice_to_u32(&*v),
            tree
        );
    }

    (contiguous, highest)
}

fn u32_to_vec(u: u32) -> Vec<u8> {
    let buf: [u8; size_of::<u32>()] = u.to_be_bytes();
    buf.to_vec()
}

fn slice_to_u32(b: &[u8]) -> u32 {
    let mut buf = [0u8; size_of::<u32>()];
    buf.copy_from_slice(&b[..size_of::<u32>()]);

    u32::from_be_bytes(buf)
}

fn spawn_killah() {
    thread::spawn(|| {
        let runtime = rand::thread_rng().gen_range(0, 200);
        thread::sleep(Duration::from_millis(runtime));
        exit(9);
    });
}

fn run(config: Config) {
    common::setup_logger();

    let crash_during_initialization = rand::thread_rng().gen_bool(0.1);

    if crash_during_initialization {
        spawn_killah();
    }

    let tree = config.open().unwrap();

    if !crash_during_initialization {
        spawn_killah();
    }

    let (key, highest) = verify(&tree);

    let mut hu = ((highest as usize) * CYCLE) + key as usize;
    assert_eq!(hu % CYCLE, key as usize);
    assert_eq!(hu / CYCLE, highest as usize);

    loop {
        hu += 1;

        if hu / CYCLE >= CYCLE {
            hu = 0;
        }

        let key = u32_to_vec((hu % CYCLE) as u32);

        let mut value = u32_to_vec((hu / CYCLE) as u32);
        let additional_len = rand::thread_rng().gen_range(0, 1000);
        value.append(&mut vec![0u8; additional_len]);

        tree.insert(&key, value).unwrap();
    }
}

fn run_with_snapshot(dir: &str) {
    let config = Config::new()
        .cache_capacity(128 * 1024 * 1024)
        .flush_every_ms(Some(100))
        .path(dir.to_string())
        .snapshot_after_ops(5000)
        .segment_size(1024);

    match thread::spawn(|| run(config)).join() {
        Err(e) => {
            println!("worker thread failed: {:?}", e);
            std::process::exit(15);
        }
        _ => {}
    }
}

fn run_without_snapshot(dir: &str) {
    let config = Config::new()
        .cache_capacity(128 * 1024 * 1024)
        .flush_every_ms(Some(100))
        .path(dir.to_string())
        .snapshot_after_ops(1 << 56)
        .segment_size(1024);

    match thread::spawn(|| run(config)).join() {
        Err(e) => {
            println!("worker thread failed: {:?}", e);
            std::process::exit(15);
        }
        _ => {}
    }
}

fn run_child_process(test_name: &str) -> Child {
    let bin = env::current_exe().expect("could not get test binary path");

    env::set_var(TEST_ENV_VAR, test_name);

    Command::new(bin).env(TEST_ENV_VAR, test_name).spawn().expect(&format!(
        "could not spawn child process for {} test",
        test_name
    ))
}

fn handle_child_exit_status(dir: &str, status: ExitStatus) {
    let code = status.code();

    if code.is_none() || code.unwrap() != 9 {
        cleanup(dir);
        panic!("{} test child exited abnormally", dir);
    }
}

fn handle_child_wait_err(dir: &str, e: std::io::Error) {
    cleanup(dir);

    panic!("error waiting for {} test child: {}", dir, e);
}

fn test_crash_recovery_with_runtime_snapshot() {
    let dir = RECOVERY_WITH_SNAPSHOT;
    cleanup(dir);

    for _ in 0..N_TESTS {
        let mut child = run_child_process(RECOVERY_WITH_SNAPSHOT);

        child
            .wait()
            .map(|status| handle_child_exit_status(dir, status))
            .map_err(|e| handle_child_wait_err(dir, e))
            .unwrap();
    }

    cleanup(dir);
}

fn test_crash_recovery_no_runtime_snapshot() {
    let dir = RECOVERY_NO_SNAPSHOT;
    cleanup(dir);

    for _ in 0..N_TESTS {
        let mut child = run_child_process(RECOVERY_NO_SNAPSHOT);

        child
            .wait()
            .map(|status| handle_child_exit_status(dir, status))
            .map_err(|e| handle_child_wait_err(dir, e))
            .unwrap();
    }

    cleanup(dir);
}
<commit_msg>Use `abort` instead of `exit`<commit_after>mod common;

use std::env::{self, VarError};
use std::mem::size_of;
use std::process::{abort, Child, Command, ExitStatus};
use std::thread;
use std::time::Duration;

use rand::Rng;

use sled::Config;

use common::cleanup;

const TEST_ENV_VAR: &str = "SLED_CRASH_TEST";
const N_TESTS: usize = 100;
const CYCLE: usize = 256;

// test names, also used as dir names
const RECOVERY_WITH_SNAPSHOT: &str = "crash_recovery_with_runtime_snapshot";
const RECOVERY_NO_SNAPSHOT: &str = "crash_recovery_no_runtime_snapshot";

fn main() {
    match env::var(TEST_ENV_VAR) {
        Err(VarError::NotPresent) => {
            test_crash_recovery_with_runtime_snapshot();
            test_crash_recovery_no_runtime_snapshot();
        }

        Ok(ref s) if s == RECOVERY_WITH_SNAPSHOT => run_with_snapshot(s),
        Ok(ref s) if s == RECOVERY_NO_SNAPSHOT => run_without_snapshot(s),

        Ok(_) | Err(_) => panic!("invalid crash test case"),
    }
}

/// Verifies that the keys in the tree are correctly recovered.
/// Panics if they are incorrect.
/// Returns the key that should be resumed at, and the current cycle value.
fn verify(tree: &sled::Tree) -> (u32, u32) {
    // key 0 should always be the highest value, as that's where we increment
    // at some point, it might go down by one
    // it should never return, or go down again after that
    let mut iter = tree.iter();
    let highest = match iter.next() {
        Some(Ok((_k, v))) => slice_to_u32(&*v),
        Some(Err(e)) => panic!("{:?}", e),
        None => return (0, 0),
    };

    let highest_vec = u32_to_vec(highest);

    // find how far we got
    let mut contiguous: u32 = 0;
    let mut lowest = 0;
    for res in iter {
        let (_k, v) = res.unwrap();
        if &v[..4] == &highest_vec[..4] {
            contiguous += 1;
        } else {
            let expected = if highest == 0 {
                CYCLE as u32 - 1
            } else {
                (highest - 1) % CYCLE as u32
            };
            let actual = slice_to_u32(&*v);
            assert_eq!(expected, actual);
            lowest = actual;
            break;
        }
    }

    // ensure nothing changes after this point
    let low_beginning = u32_to_vec(contiguous + 1);

    for res in tree.range(&*low_beginning..) {
        let (k, v): (sled::IVec, _) = res.unwrap();
        assert_eq!(
            slice_to_u32(&*v),
            lowest,
            "expected key {} to have value {}, instead it had value {} in db: {:?}",
            slice_to_u32(&*k),
            lowest,
            slice_to_u32(&*v),
            tree
        );
    }

    (contiguous, highest)
}

fn u32_to_vec(u: u32) -> Vec<u8> {
    let buf: [u8; size_of::<u32>()] = u.to_be_bytes();
    buf.to_vec()
}

fn slice_to_u32(b: &[u8]) -> u32 {
    let mut buf = [0u8; size_of::<u32>()];
    buf.copy_from_slice(&b[..size_of::<u32>()]);

    u32::from_be_bytes(buf)
}

fn spawn_killah() {
    thread::spawn(|| {
        let runtime = rand::thread_rng().gen_range(0, 200);
        thread::sleep(Duration::from_millis(runtime));
        abort();
    });
}

fn run(config: Config) {
    common::setup_logger();

    let crash_during_initialization = rand::thread_rng().gen_bool(0.1);

    if crash_during_initialization {
        spawn_killah();
    }

    let tree = config.open().unwrap();

    if !crash_during_initialization {
        spawn_killah();
    }

    let (key, highest) = verify(&tree);

    let mut hu = ((highest as usize) * CYCLE) + key as usize;
    assert_eq!(hu % CYCLE, key as usize);
    assert_eq!(hu / CYCLE, highest as usize);

    loop {
        hu += 1;

        if hu / CYCLE >= CYCLE {
            hu = 0;
        }

        let key = u32_to_vec((hu % CYCLE) as u32);

        let mut value = u32_to_vec((hu / CYCLE) as u32);
        let additional_len = rand::thread_rng().gen_range(0, 1000);
        value.append(&mut vec![0u8; additional_len]);

        tree.insert(&key, value).unwrap();
    }
}

fn run_with_snapshot(dir: &str) {
    let config = Config::new()
        .cache_capacity(128 * 1024 * 1024)
        .flush_every_ms(Some(100))
        .path(dir.to_string())
        .snapshot_after_ops(5000)
        .segment_size(1024);

    match thread::spawn(|| run(config)).join() {
        Err(e) => {
            println!("worker thread failed: {:?}", e);
            std::process::exit(15);
        }
        _ => {}
    }
}

fn run_without_snapshot(dir: &str) {
    let config = Config::new()
        .cache_capacity(128 * 1024 * 1024)
        .flush_every_ms(Some(100))
        .path(dir.to_string())
        .snapshot_after_ops(1 << 56)
        .segment_size(1024);

    match thread::spawn(|| run(config)).join() {
        Err(e) => {
            println!("worker thread failed: {:?}", e);
            std::process::exit(15);
        }
        _ => {}
    }
}

fn run_child_process(test_name: &str) -> Child {
    let bin = env::current_exe().expect("could not get test binary path");

    env::set_var(TEST_ENV_VAR, test_name);

    Command::new(bin).env(TEST_ENV_VAR, test_name).spawn().expect(&format!(
        "could not spawn child process for {} test",
        test_name
    ))
}

fn handle_child_exit_status(dir: &str, status: ExitStatus) {
    let code = status.code();

    if code.is_none() || code.unwrap() != 9 {
        cleanup(dir);
        panic!("{} test child exited abnormally", dir);
    }
}

fn handle_child_wait_err(dir: &str, e: std::io::Error) {
    cleanup(dir);

    panic!("error waiting for {} test child: {}", dir, e);
}

fn test_crash_recovery_with_runtime_snapshot() {
    let dir = RECOVERY_WITH_SNAPSHOT;
    cleanup(dir);

    for _ in 0..N_TESTS {
        let mut child = run_child_process(RECOVERY_WITH_SNAPSHOT);

        child
            .wait()
            .map(|status| handle_child_exit_status(dir, status))
            .map_err(|e| handle_child_wait_err(dir, e))
            .unwrap();
    }

    cleanup(dir);
}

fn test_crash_recovery_no_runtime_snapshot() {
    let dir = RECOVERY_NO_SNAPSHOT;
    cleanup(dir);

    for _ in 0..N_TESTS {
        let mut child = run_child_process(RECOVERY_NO_SNAPSHOT);

        child
            .wait()
            .map(|status| handle_child_exit_status(dir, status))
            .map_err(|e| handle_child_wait_err(dir, e))
            .unwrap();
    }

    cleanup(dir);
}
<|endoftext|>