<commit_before>// Copyright 2017-2018, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

//! Module containing infrastructure for subclassing `GObject`s and registering boxed types.
//!
//! # Example for registering a `glib::Object` subclass
//!
//! The following code implements a subclass of `glib::Object` with a
//! string-typed "name" property.
//!
//! ```rust
//! #[macro_use]
//! extern crate glib;
//! use glib::prelude::*;
//! use glib::subclass;
//! use glib::subclass::prelude::*;
//!
//! use std::cell::{Cell, RefCell};
//!
//! #[derive(Debug, Eq, PartialEq, Clone, Copy, GEnum)]
//! #[repr(u32)]
//! // type_name: GType name of the GEnum (mandatory)
//! #[genum(type_name = "SimpleObjectAnimal")]
//! enum Animal {
//!   Goat = 0,
//!   #[genum(name = "The Dog")]
//!   Dog = 1,
//!   // name: the name of the GEnumValue (optional, default to the enum name in CamelCase
//!   // nick: the nick of the GEnumValue (optional, default to the enum name in kebab-case
//!   #[genum(name = "The Cat", nick = "chat")]
//!   Cat = 2,
//! }
//!
//! impl Default for Animal {
//!     fn default() -> Self {
//!         Animal::Goat
//!     }
//! }
//!
//! // Static array for defining the properties of the new type.
//! static PROPERTIES: [subclass::Property; 2] = [
//!     subclass::Property("name", |name| {
//!         glib::ParamSpec::string(
//!             name,
//!             "Name",
//!             "Name of this object",
//!             None,
//!             glib::ParamFlags::READWRITE,
//!         )
//!     }),
//!     subclass::Property("animal", |name| {
//!         glib::ParamSpec::enum_(
//!             name,
//!             "Animal",
//!             "Animal",
//!             Animal::static_type(),
//!             Animal::Goat as i32,
//!             glib::ParamFlags::READWRITE,
//!         )
//!     }),
//! ];
//!
//! // This is the struct containing all state carried with
//! // the new type. Generally this has to make use of
//! // interior mutability.
//! pub struct SimpleObject {
//!     name: RefCell<Option<String>>,
//!     animal: Cell<Animal>,
//! }
//!
//! // ObjectSubclass is the trait that defines the new type and
//! // contains all information needed by the GObject type system,
//! // including the new type's name, parent type, etc.
//! impl ObjectSubclass for SimpleObject {
//!     // This type name must be unique per process.
//!     const NAME: &'static str = "SimpleObject";
//!
//!     // The parent type this one is inheriting from.
//!     type ParentType = glib::Object;
//!
//!     // The C/FFI instance and class structs. The simple ones
//!     // are enough in most cases and more is only needed to
//!     // expose public instance fields to C APIs or to provide
//!     // new virtual methods for subclasses of this type.
//!     type Instance = subclass::simple::InstanceStruct<Self>;
//!     type Class = subclass::simple::ClassStruct<Self>;
//!
//!     // This macro defines some boilerplate.
//!     glib_object_subclass!();
//!
//!     // Called right before the first time an instance of the new
//!     // type is created. Here class specific settings can be performed,
//!     // including installation of properties and registration of signals
//!     // for the new type.
//!     fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
//!         klass.install_properties(&PROPERTIES);
//!     }
//!
//!     // Called every time a new instance is created. This should return
//!     // a new instance of our type with its basic values.
//!     fn new() -> Self {
//!         Self {
//!             name: RefCell::new(None),
//!             animal: Cell::new(Animal::default()),
//!         }
//!     }
//! }
//!
//! // Trait that is used to override virtual methods of glib::Object.
//! impl ObjectImpl for SimpleObject {
//!     // This macro defines some boilerplate.
//!     glib_object_impl!();
//!
//!     // Called whenever a property is set on this instance. The id
//!     // is the same as the index of the property in the PROPERTIES array.
//!     fn set_property(&self, _obj: &glib::Object, id: usize, value: &glib::Value) {
//!         let prop = &PROPERTIES[id];
//!
//!         match *prop {
//!             subclass::Property("name", ..) => {
//!                 let name = value
//!                     .get()
//!                     .expect("type conformity checked by `Object::set_property`");
//!                 self.name.replace(name);
//!             },
//!             subclass::Property("animal", ..) => {
//!                 let animal = value
//!                     .get()
//!                     .expect("type conformity checked by `Object::set_property`");
//!                 self.animal.replace(animal.unwrap());
//!             },
//!             _ => unimplemented!(),
//!         }
//!     }
//!
//!     // Called whenever a property is retrieved from this instance. The id
//!     // is the same as the index of the property in the PROPERTIES array.
//!     fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> {
//!         let prop = &PROPERTIES[id];
//!
//!         match *prop {
//!             subclass::Property("name", ..) => Ok(self.name.borrow().to_value()),
//!             subclass::Property("animal", ..) => Ok(self.animal.get().to_value()),
//!             _ => unimplemented!(),
//!         }
//!     }
//!
//!     // Called right after construction of the instance.
//!     fn constructed(&self, obj: &glib::Object) {
//!         // Chain up to the parent type's implementation of this virtual
//!         // method.
//!         self.parent_constructed(obj);
//!
//!         // And here we could do our own initialization.
//!     }
//! }
//!
//! pub fn main() {
//!     // Create an object instance of the new type.
//!     let obj = glib::Object::new(SimpleObject::get_type(), &[]).unwrap();
//!
//!     // Get the name property and change its value.
//!     assert_eq!(obj.get_property("name").unwrap().get::<&str>(), Ok(None));
//!     obj.set_property("name", &"test").unwrap();
//!     assert_eq!(
//!         obj.get_property("name").unwrap().get::<&str>(),
//!         Ok(Some("test"))
//!     );
//!
//!     assert_eq!(obj.get_property("animal").unwrap().get::<Animal>(), Ok(Some(Animal::Goat)));
//!     obj.set_property("animal", &Animal::Cat).unwrap();
//!     assert_eq!(obj.get_property("animal").unwrap().get::<Animal>(), Ok(Some(Animal::Cat)));
//! }
//! ```
//!
//! # Example for registering a boxed type for a Rust struct
//!
//! The following code boxed type for a tuple struct around `String` and uses it in combination
//! with `glib::Value`.
//!
//! ```rust
//! #[macro_use]
//! extern crate glib;
//! use glib::prelude::*;
//! use glib::subclass;
//! use glib::subclass::prelude::*;
//!
//! #[derive(Clone, Debug, PartialEq, Eq, GBoxed)]
//! #[gboxed(type_name = "MyBoxed")]
//! struct MyBoxed(String);
//!
//! pub fn main() {
//!     assert_ne!(glib::Type::Invalid, MyBoxed::get_type());
//!
//!     let b = MyBoxed(String::from("abc"));
//!     let v = b.to_value();
//!     let b2 = v.get::<&MyBoxed>().unwrap().unwrap();
//!     assert_eq!(&b, b2);
//! }
//! ```

pub mod simple;
#[macro_use]
pub mod types;

#[macro_use]
pub mod interface;

#[macro_use]
pub mod object;

#[macro_use]
pub mod boxed;

pub mod prelude {
    //! Prelude that re-exports all important traits from this crate.
    pub use super::boxed::BoxedType;
    pub use super::interface::{ObjectInterface, ObjectInterfaceExt};
    pub use super::object::{ObjectClassSubclassExt, ObjectImpl, ObjectImplExt};
    pub use super::types::{
        ClassStruct, InstanceStruct, IsImplementable, IsSubclassable, ObjectSubclass,
    };
}

pub use self::boxed::register_boxed_type;
pub use self::interface::register_interface;
pub use self::object::Property;
pub use self::types::{
    register_type, InitializingType, SignalClassHandlerToken, SignalInvocationHint, TypeData,
};
<commit_msg>subclass: add doc example defining a GFlags property<commit_after>// Copyright 2017-2018, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

//! Module containing infrastructure for subclassing `GObject`s and registering boxed types.
//!
//! # Example for registering a `glib::Object` subclass
//!
//! The following code implements a subclass of `glib::Object` with a
//! string-typed "name" property.
//!
//! ```rust
//! #[macro_use]
//! extern crate glib;
//! use glib::prelude::*;
//! use glib::subclass;
//! use glib::subclass::prelude::*;
//!
//! use std::cell::{Cell, RefCell};
//!
//! #[derive(Debug, Eq, PartialEq, Clone, Copy, GEnum)]
//! #[repr(u32)]
//! // type_name: GType name of the GEnum (mandatory)
//! #[genum(type_name = "SimpleObjectAnimal")]
//! enum Animal {
//!   Goat = 0,
//!   #[genum(name = "The Dog")]
//!   Dog = 1,
//!   // name: the name of the GEnumValue (optional, default to the enum name in CamelCase
//!   // nick: the nick of the GEnumValue (optional, default to the enum name in kebab-case
//!   #[genum(name = "The Cat", nick = "chat")]
//!   Cat = 2,
//! }
//!
//! impl Default for Animal {
//!     fn default() -> Self {
//!         Animal::Goat
//!     }
//! }
//!
//! #[gflags("MyFlags")]
//! enum MyFlags {
//!     #[gflags(name = "Flag A", nick = "nick-a")]
//!     A = 0b00000001,
//!     #[gflags(name = "Flag B")]
//!     B = 0b00000010,
//!     #[gflags(skip)]
//!     AB = Self::A.bits() | Self::B.bits(),
//!     C = 0b00000100,
//! }
//!
//! impl Default for MyFlags {
//!     fn default() -> Self {
//!         MyFlags::A
//!     }
//! }
//!
//! // Static array for defining the properties of the new type.
//! static PROPERTIES: [subclass::Property; 3] = [
//!     subclass::Property("name", |name| {
//!         glib::ParamSpec::string(
//!             name,
//!             "Name",
//!             "Name of this object",
//!             None,
//!             glib::ParamFlags::READWRITE,
//!         )
//!     }),
//!     subclass::Property("animal", |name| {
//!         glib::ParamSpec::enum_(
//!             name,
//!             "Animal",
//!             "Animal",
//!             Animal::static_type(),
//!             Animal::Goat as i32,
//!             glib::ParamFlags::READWRITE,
//!         )
//!     }),
//!     subclass::Property("flags", |name| {
//!         glib::ParamSpec::flags(
//!             name,
//!             "Flags",
//!             "Flags",
//!             MyFlags::static_type(),
//!             MyFlags::default().bits(),
//!             glib::ParamFlags::READWRITE,
//!         )
//!     }),
//! ];
//!
//! // This is the struct containing all state carried with
//! // the new type. Generally this has to make use of
//! // interior mutability.
//! pub struct SimpleObject {
//!     name: RefCell<Option<String>>,
//!     animal: Cell<Animal>,
//!     flags: Cell<MyFlags>,
//! }
//!
//! // ObjectSubclass is the trait that defines the new type and
//! // contains all information needed by the GObject type system,
//! // including the new type's name, parent type, etc.
//! impl ObjectSubclass for SimpleObject {
//!     // This type name must be unique per process.
//!     const NAME: &'static str = "SimpleObject";
//!
//!     // The parent type this one is inheriting from.
//!     type ParentType = glib::Object;
//!
//!     // The C/FFI instance and class structs. The simple ones
//!     // are enough in most cases and more is only needed to
//!     // expose public instance fields to C APIs or to provide
//!     // new virtual methods for subclasses of this type.
//!     type Instance = subclass::simple::InstanceStruct<Self>;
//!     type Class = subclass::simple::ClassStruct<Self>;
//!
//!     // This macro defines some boilerplate.
//!     glib_object_subclass!();
//!
//!     // Called right before the first time an instance of the new
//!     // type is created. Here class specific settings can be performed,
//!     // including installation of properties and registration of signals
//!     // for the new type.
//!     fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
//!         klass.install_properties(&PROPERTIES);
//!     }
//!
//!     // Called every time a new instance is created. This should return
//!     // a new instance of our type with its basic values.
//!     fn new() -> Self {
//!         Self {
//!             name: RefCell::new(None),
//!             animal: Cell::new(Animal::default()),
//!             flags: Cell::new(MyFlags::default()),
//!         }
//!     }
//! }
//!
//! // Trait that is used to override virtual methods of glib::Object.
//! impl ObjectImpl for SimpleObject {
//!     // This macro defines some boilerplate.
//!     glib_object_impl!();
//!
//!     // Called whenever a property is set on this instance. The id
//!     // is the same as the index of the property in the PROPERTIES array.
//!     fn set_property(&self, _obj: &glib::Object, id: usize, value: &glib::Value) {
//!         let prop = &PROPERTIES[id];
//!
//!         match *prop {
//!             subclass::Property("name", ..) => {
//!                 let name = value
//!                     .get()
//!                     .expect("type conformity checked by `Object::set_property`");
//!                 self.name.replace(name);
//!             },
//!             subclass::Property("animal", ..) => {
//!                 let animal = value
//!                     .get()
//!                     .expect("type conformity checked by `Object::set_property`");
//!                 self.animal.replace(animal.unwrap());
//!             },
//!             subclass::Property("flags", ..) => {
//!                 let flags = value
//!                     .get()
//!                     .expect("type conformity checked by `Object::set_property`");
//!                 self.flags.replace(flags.unwrap());
//!             },
//!             _ => unimplemented!(),
//!         }
//!     }
//!
//!     // Called whenever a property is retrieved from this instance. The id
//!     // is the same as the index of the property in the PROPERTIES array.
//!     fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> {
//!         let prop = &PROPERTIES[id];
//!
//!         match *prop {
//!             subclass::Property("name", ..) => Ok(self.name.borrow().to_value()),
//!             subclass::Property("animal", ..) => Ok(self.animal.get().to_value()),
//!             subclass::Property("flags", ..) => Ok(self.flags.get().to_value()),
//!             _ => unimplemented!(),
//!         }
//!     }
//!
//!     // Called right after construction of the instance.
//!     fn constructed(&self, obj: &glib::Object) {
//!         // Chain up to the parent type's implementation of this virtual
//!         // method.
//!         self.parent_constructed(obj);
//!
//!         // And here we could do our own initialization.
//!     }
//! }
//!
//! pub fn main() {
//!     // Create an object instance of the new type.
//!     let obj = glib::Object::new(SimpleObject::get_type(), &[]).unwrap();
//!
//!     // Get the name property and change its value.
//!     assert_eq!(obj.get_property("name").unwrap().get::<&str>(), Ok(None));
//!     obj.set_property("name", &"test").unwrap();
//!     assert_eq!(
//!         obj.get_property("name").unwrap().get::<&str>(),
//!         Ok(Some("test"))
//!     );
//!
//!     assert_eq!(obj.get_property("animal").unwrap().get::<Animal>(), Ok(Some(Animal::Goat)));
//!     obj.set_property("animal", &Animal::Cat).unwrap();
//!     assert_eq!(obj.get_property("animal").unwrap().get::<Animal>(), Ok(Some(Animal::Cat)));
//!
//!     assert_eq!(obj.get_property("flags").unwrap().get::<MyFlags>(), Ok(Some(MyFlags::A)));
//!     obj.set_property("flags", &MyFlags::B).unwrap();
//!     assert_eq!(obj.get_property("flags").unwrap().get::<MyFlags>(), Ok(Some(MyFlags::B)));
//! }
//! ```
//!
//! # Example for registering a boxed type for a Rust struct
//!
//! The following code boxed type for a tuple struct around `String` and uses it in combination
//! with `glib::Value`.
//!
//! ```rust
//! #[macro_use]
//! extern crate glib;
//! use glib::prelude::*;
//! use glib::subclass;
//! use glib::subclass::prelude::*;
//!
//! #[derive(Clone, Debug, PartialEq, Eq, GBoxed)]
//! #[gboxed(type_name = "MyBoxed")]
//! struct MyBoxed(String);
//!
//! pub fn main() {
//!     assert_ne!(glib::Type::Invalid, MyBoxed::get_type());
//!
//!     let b = MyBoxed(String::from("abc"));
//!     let v = b.to_value();
//!     let b2 = v.get::<&MyBoxed>().unwrap().unwrap();
//!     assert_eq!(&b, b2);
//! }
//! ```

pub mod simple;
#[macro_use]
pub mod types;

#[macro_use]
pub mod interface;

#[macro_use]
pub mod object;

#[macro_use]
pub mod boxed;

pub mod prelude {
    //! Prelude that re-exports all important traits from this crate.
    pub use super::boxed::BoxedType;
    pub use super::interface::{ObjectInterface, ObjectInterfaceExt};
    pub use super::object::{ObjectClassSubclassExt, ObjectImpl, ObjectImplExt};
    pub use super::types::{
        ClassStruct, InstanceStruct, IsImplementable, IsSubclassable, ObjectSubclass,
    };
}

pub use self::boxed::register_boxed_type;
pub use self::interface::register_interface;
pub use self::object::Property;
pub use self::types::{
    register_type, InitializingType, SignalClassHandlerToken, SignalInvocationHint, TypeData,
};
<|endoftext|>


<commit_before>use emulator::atom;

use std::cmp::{Ordering};
use term::lterm::*;
use term::classify;
use term::primary;
use term::immediate;

/// When comparing nested terms they might turn out to be equal. `CompareOp`
/// is stored in `stack` in `eq_terms()` function and tells where to resume
/// comparing the previous term.
enum ContinueCompare {
  // This begins the compare while not knowing types for `a` or `b`.
  AnyType(LTerm, LTerm),
  // Resume comparing Cons cells, we just reenter `eq_terms_cons`.
  Cons(LTerm, LTerm),
}

enum EqResult {
  /// Equality result is concluded to be the `bool` value.
  Concluded(Ordering),
  /// Equality is not concluded, but comparing these two terms will give the
  /// result (equivalent to `goto tailrecur_ne` in Erlang/OTP). This happens
  /// when a beginning elements of a nested structure compares equal but some
  /// members have to be checked recursively.
  CompareNested(LTerm, LTerm, ContinueCompare),
}

/// Compare two terms for equality, fail if types are different even if
/// coercion is otherwise possible.
pub fn cmp_terms(a: LTerm, b: LTerm, exact: bool) -> Ordering {
  // Comparison might want to recurse, to avoid stack growth, do a switch here
  // and continue comparing. We grow `stack` instead of a CPU stack.
  let mut stack = Vec::<ContinueCompare>::new();
  let mut op = ContinueCompare::AnyType(a, b);

  loop {
    // TODO: optimize me, identical code branches
    let eq_result = match op {
      ContinueCompare::AnyType(a1, b1) => {
        cmp_terms_any_type(a1, b1, exact)
      }
      ContinueCompare::Cons(a2, b2) => {
        cmp_terms_any_type(a2, b2, exact)
      },
    };

    match eq_result {
      EqResult::Concluded(result) => {
        if stack.is_empty() {
          println!("comparison {} {} concluded {:?}", a, b, result);
          return result
        } else {
          println!("comparison {} {} got intermediate result {:?}", a, b, result);
          op = stack.pop().unwrap();
          continue
        } // stack not empty
      },

      // Nested terms may accidentally compare equal, to be able to return and
      // continue comparing upper level term, we store a `continue_op` on
      // `stack`.
      EqResult::CompareNested(a3, b3, continue_op) => {
        stack.push(continue_op);
        op = ContinueCompare::AnyType(a3, b3);
        continue
      },
    }
  }
}

fn cmp_terms_any_type(a: LTerm, b: LTerm, exact: bool) -> EqResult {
  println!("cmp any type {} {}", a, b);

  // Compare type tags first
  if a.is_atom() && b.is_atom() {
    return EqResult::Concluded(cmp_atoms(a, b));
  }

  let a_is_small = a.is_small();
  let b_is_small = b.is_small();
  if a_is_small && b_is_small {
    let a_small = a.small_get_s();
    let b_small = b.small_get_s();
    return EqResult::Concluded(a_small.cmp(&b_small));
  }

  let a_is_float = unsafe { a.is_float() };
  let b_is_float = unsafe { b.is_float() };
  // If not exact then allow comparing float to int/bigint
  if !exact && (a_is_float || a_is_small) && (b_is_float || b_is_small) {
    return EqResult::Concluded(cmp_numbers_not_exact(a, b));
  } else if a_is_float && b_is_float {
    return EqResult::Concluded(cmp_floats(a, b))
  }

  // If types don't compare equal, we can stop comparing here?
  // TODO: Except when we compare numbers
//  let order = cmp_type_order(a, b);
//  if order != Ordering::Equal {
//    return EqResult::Concluded(order);
//  }

  cmp_terms_primary(a, b, exact)
}

fn cmp_floats(_a: LTerm, _b: LTerm) -> Ordering {
  panic!("TODO: eq_floats")
}

fn cmp_numbers_not_exact(_a: LTerm, _b: LTerm) -> Ordering {
  panic!("TODO: eq_numbers_not_exact")
}

/// Compare two atoms for equality.
fn cmp_atoms(a: LTerm, b: LTerm) -> Ordering {
  assert!(!a.is_nil());
  let atomp_a = atom::lookup(a);
  debug_assert!(atomp_a.is_null() == false,
                "cmp_atoms: atom lookup {} failed", a);

  assert!(!b.is_nil());
  let atomp_b = atom::lookup(b);
  debug_assert!(atomp_b.is_null() == false,
                "cmp_atoms: atom lookup {} failed", b);

  // This should really be safe, as pointers to Atom exist statically forever
  unsafe {
    let a_len = (*atomp_a).len;
    let b_len = (*atomp_b).len;
    if a_len == b_len {
      return (*atomp_a).name.cmp(&(*atomp_b).name);
    }
    a_len.cmp(&b_len)
  }
}

/// Compare order of two types without looking into their value.
fn cmp_type_order(a: LTerm, b: LTerm) -> Ordering {
  let aclass = classify::classify_term(a);
  let bclass = classify::classify_term(b);
  aclass.cmp(&bclass)
}

/// Switch between comparisons for equality by primary tag (immediate or boxes
/// or fail immediately for different primary tags).
fn cmp_terms_primary(a: LTerm, b: LTerm, exact: bool) -> EqResult {
  let a_val = a.raw();
  let a_prim_tag = primary::get_tag(a_val);

  let b_val = b.raw();
  let b_prim_tag = primary::get_tag(b_val);
  if b_prim_tag != a_prim_tag {
    // different primary types, compare their classes
    // This can be optimized a little but is there any value in optimization?
    return EqResult::Concluded(cmp_type_order(a, b));
  }

  match a_prim_tag {
    primary::TAG_IMMED => {
      return EqResult::Concluded(cmp_terms_immed(a, b, exact))
    },
    primary::TAG_CONS => unsafe {
      return cmp_cons(a, b)
    },
    primary::TAG_BOX => {
      return cmp_terms_box(a, b)
    },
    _ => panic!("Primary tag {} eq_terms unsupported", a_prim_tag)
  }
}

fn cmp_terms_immed(a: LTerm, b: LTerm, _exact: bool) -> Ordering {
  let av = a.raw();
  let bv = b.raw();

  if a.is_nil() || a.is_empty_tuple() || a.is_empty_binary() {
    if a.raw() == b.raw() {
      return Ordering::Equal;
    }
  }

  if a.is_immediate1() {
    if a.is_local_port() {
      if b.is_local_port() {
        panic!("TODO: cmp local vs local port")
      } else if b.is_external_port() {
        panic!("TODO: cmp local vs ext port")
      } else {
        return cmp_mixed_types(a, b)
      }
    }

    if a.is_local_pid() {
      if b.is_local_pid() {
        panic!("TODO: cmp local vs local pid")
      } else if b.is_external_pid() {
        panic!("TODO: cmp local vs ext pid")
      } else {
        return cmp_mixed_types(a, b)
      }
    }
  }

  if a.is_cons() {
    if !b.is_cons() {
      return cmp_mixed_types(a, b)
    }

    panic!("TODO: invoke cmp_cons correctly from here")
    //return cmp_cons(a, b)
  }

  if a.is_box() {
    if unsafe { a.is_tuple() } {
      if unsafe { b.is_tuple() } {
        panic!("TODO: cmp tuple vs tuple")
      } else {
        return cmp_mixed_types(a, b)
      }
    } else if a.is_map() {
      if a.is_flat_map() {
        if !b.is_flat_map() {
          if b.is_hash_map() {
            let b_size = b.map_size();
            return a.map_size().cmp(&b_size)
          }
        } else {
          // Compare two flat maps
          panic!("TODO: cmp flatmap vs flatmap (+exact)")
        }
      } else {
        if !b.is_hash_map() {
          if b.is_flat_map() {
            let b_size = b.map_size();
            return a.map_size().cmp(&b_size)
          }
        } else {
          // Compare two hash maps
          panic!("TODO: cmp flatmap vs flatmap (+exact)")
        }
      }
      //Hashmap compare strategy:
      //Phase 1. While keys are identical
      //    Do synchronous stepping through leafs of both trees in hash
      //    order. Maintain value compare result of minimal key.
      //
      //Phase 2. If key diff was found in phase 1
      //    Ignore values from now on.
      //    Continue iterate trees by always advancing the one
      //    lagging behind hash-wise. Identical keys are skipped.
      //    A minimal key can only be candidate as tie-breaker if we
      //    have passed that hash value in the other tree (which means
      //    the key did not exist in the other tree).
    } else if unsafe { a.is_float() } {
      if !unsafe { b.is_float() } {
        // TODO: If b is integer and we don't do exact comparison?
        return cmp_mixed_types(a, b)
      } else {
        let af = unsafe { a.float_get() };
        let bf = unsafe { b.float_get() };
        return af.partial_cmp(&bf).unwrap()
      }
    } else if a.is_bignum() {
      if !b.is_bignum() {
        return cmp_mixed_types(a, b)
      }
    } else if a.is_export() {
      if !b.is_export() {
        return cmp_mixed_types(a, b)
      }
      // Compare two exports: from utils.c line ~2918
      // cmp atoms a.module and b.module
      // cmp atoms a.fn and b.fn
      // cmp arity
      panic!("TODO compare 2 exports")
    } else if a.is_header() {
      if !a.is_fun() {
        return cmp_mixed_types(a, b)
      }
      // Compare 2 function objects: from utils.c line ~2937
      // compare a.module, b.module
      // compare old_index
      // compare old_uniq
      // compare num_Free
      panic!("TODO compare 2 fun objects")
    } else if a.is_external_pid() {
      if b.is_local_pid() {
        panic!("TODO compare ext vs local pid")
      } else if b.is_external_pid() {
        panic!("TODO compare ext vs ext pid")
      } else {
        return cmp_mixed_types(a, b)
      }
    } else if a.is_external_port() {
      if b.is_local_port() {
        panic!("TODO compare ext vs local port")
      } else if b.is_external_port() {
        panic!("TODO compare ext vs ext port")
      } else {
        return cmp_mixed_types(a, b)
      }
    } else if a.is_local_ref() {
      if b.is_local_ref() {
        panic!("TODO compare local vs local ref")
      } else if b.is_external_ref() {
        panic!("TODO compare local vs ext ref")
      } else {
        return cmp_mixed_types(a, b)
      }
    } else if a.is_external_ref() {
      if b.is_local_ref() {
        panic!("TODO compare ext vs local ref")
      } else if b.is_external_ref() {
        panic!("TODO compare ext vs ext ref")
      } else {
        return cmp_mixed_types(a, b)
      }
    } else {
      // must be a binary
      assert!(unsafe { a.is_binary() });
      if !unsafe { b.is_binary() } {
        return cmp_mixed_types(a, b)
      }
      panic!("TODO cmp binaries")
    }
  }

  // if both are immediate3, it is hard to distunguish because all imm3 values
  // are special
  if a.is_immediate3() && b.is_immediate3() {
    let a3t = immediate::get_imm3_tag(av);
    let b3t = immediate::get_imm3_tag(bv);
    // If imm3 tags are same, we can conclude with comparing their values
    if a3t == b3t {
      let a3v = immediate::get_imm3_value(av);
      let b3v = immediate::get_imm3_value(bv);
      return a3v.cmp(&b3v);
    }
    return a3t.cmp(&b3t);
  }

  panic!("TODO: eq_terms_immed {} {}", a, b)
}

/// Deeper comparison of two values with different types
fn cmp_mixed_types(_a: LTerm, _b: LTerm) -> Ordering {
  panic!("TODO: cmp_mixed_types(a, b)")
}

/// Compare two boxed or immediate terms. In case when nested terms need to be
/// recursively compared, we return `EqResult::CompareNested` to change the
/// values `a` and `b` and perform another comparison without growing the stack.
unsafe fn cmp_cons(a: LTerm, b: LTerm) -> EqResult {
  let mut aa = a.cons_get_ptr();
  let mut bb = b.cons_get_ptr();

  loop {
    // Check the heads
    let ahd = aa.hd();
    let bhd = bb.hd();

    if LTerm::is_same(ahd, bhd) == false {
      println!("cmp_cons ahd {} bhd {}", ahd, bhd);
      // Recurse into a.hd and b.hd, but push a.tl and b.tl to continue
      let continue_op = ContinueCompare::Cons(aa.tl(), bb.tl());
      return EqResult::CompareNested(ahd, bhd, continue_op)
    }

    // See the tails
    let atl = aa.tl();
    let btl = bb.tl();

    if LTerm::is_same(atl, btl) {
      return EqResult::Concluded(Ordering::Equal)
    }
    if !atl.is_list() || !btl.is_list() {
      // Just do a regular compare of `a.tl` vs `b.tl`
      let continue_op = ContinueCompare::AnyType(atl, btl);
      return EqResult::CompareNested(atl, btl, continue_op)
    }

    // Take the next linked cons cell and continue comparing
    aa = atl.cons_get_ptr();
    bb = btl.cons_get_ptr();
  }
}

fn cmp_terms_box(_a: LTerm, _b: LTerm) -> EqResult {
  panic!("TODO: eq_terms_box")
}
<commit_msg>Fixed inequality outcome for comparison of nested terms<commit_after>use emulator::atom;

use std::cmp::{Ordering};
use term::lterm::*;
use term::classify;
use term::primary;
use term::immediate;

/// When comparing nested terms they might turn out to be equal. `CompareOp`
/// is stored in `stack` in `eq_terms()` function and tells where to resume
/// comparing the previous term.
enum ContinueCompare {
  // This begins the compare while not knowing types for `a` or `b`.
  AnyType(LTerm, LTerm),
  // Resume comparing Cons cells, we just reenter `eq_terms_cons`.
  Cons(LTerm, LTerm),
}

enum EqResult {
  /// Equality result is concluded to be the `bool` value.
  Concluded(Ordering),
  /// Equality is not concluded, but comparing these two terms will give the
  /// result (equivalent to `goto tailrecur_ne` in Erlang/OTP). This happens
  /// when a beginning elements of a nested structure compares equal but some
  /// members have to be checked recursively.
  CompareNested(LTerm, LTerm, ContinueCompare),
}

/// Compare two terms for equality, fail if types are different even if
/// coercion is otherwise possible.
pub fn cmp_terms(a: LTerm, b: LTerm, exact: bool) -> Ordering {
  // Comparison might want to recurse, to avoid stack growth, do a switch here
  // and continue comparing. We grow `stack` instead of a CPU stack.
  let mut stack = Vec::<ContinueCompare>::new();
  let mut op = ContinueCompare::AnyType(a, b);

  loop {
    // TODO: optimize me, identical code branches
    let eq_result = match op {
      ContinueCompare::AnyType(a1, b1) |
      ContinueCompare::Cons(a1, b1) => {
        cmp_terms_any_type(a1, b1, exact)
      },
    };

    match eq_result {
      EqResult::Concluded(result) if result == Ordering::Equal => {
        if stack.is_empty() {
          println!("comparison {} {} concluded {:?}", a, b, result);
          return result
        } else {
          println!("comparison {} {} got intermediate result {:?}", a, b, result);
          op = stack.pop().unwrap();
          continue
        } // stack not empty
      },

      EqResult::Concluded(result) => return result,

      // Nested terms may accidentally compare equal, to be able to return and
      // continue comparing upper level term, we store a `continue_op` on
      // `stack`.
      EqResult::CompareNested(a3, b3, continue_op) => {
        stack.push(continue_op);
        op = ContinueCompare::AnyType(a3, b3);
        continue
      },
    }
  }
}

fn cmp_terms_any_type(a: LTerm, b: LTerm, exact: bool) -> EqResult {
  println!("cmp any type {} {}", a, b);

  // Compare type tags first
  if a.is_atom() && b.is_atom() {
    return EqResult::Concluded(cmp_atoms(a, b));
  }

  let a_is_small = a.is_small();
  let b_is_small = b.is_small();
  if a_is_small && b_is_small {
    let a_small = a.small_get_s();
    let b_small = b.small_get_s();
    return EqResult::Concluded(a_small.cmp(&b_small));
  }

  let a_is_float = unsafe { a.is_float() };
  let b_is_float = unsafe { b.is_float() };
  // If not exact then allow comparing float to int/bigint
  if !exact && (a_is_float || a_is_small) && (b_is_float || b_is_small) {
    return EqResult::Concluded(cmp_numbers_not_exact(a, b));
  } else if a_is_float && b_is_float {
    return EqResult::Concluded(cmp_floats(a, b))
  }

  // If types don't compare equal, we can stop comparing here?
  // TODO: Except when we compare numbers
//  let order = cmp_type_order(a, b);
//  if order != Ordering::Equal {
//    return EqResult::Concluded(order);
//  }

  cmp_terms_primary(a, b, exact)
}

fn cmp_floats(_a: LTerm, _b: LTerm) -> Ordering {
  panic!("TODO: eq_floats")
}

fn cmp_numbers_not_exact(_a: LTerm, _b: LTerm) -> Ordering {
  panic!("TODO: eq_numbers_not_exact")
}

/// Compare two atoms for equality.
fn cmp_atoms(a: LTerm, b: LTerm) -> Ordering {
  assert!(!a.is_nil());
  let atomp_a = atom::lookup(a);
  debug_assert!(atomp_a.is_null() == false,
                "cmp_atoms: atom lookup {} failed", a);

  assert!(!b.is_nil());
  let atomp_b = atom::lookup(b);
  debug_assert!(atomp_b.is_null() == false,
                "cmp_atoms: atom lookup {} failed", b);

  // This should really be safe, as pointers to Atom exist statically forever
  unsafe {
    let a_len = (*atomp_a).len;
    let b_len = (*atomp_b).len;
    if a_len == b_len {
      return (*atomp_a).name.cmp(&(*atomp_b).name);
    }
    a_len.cmp(&b_len)
  }
}

/// Compare order of two types without looking into their value.
fn cmp_type_order(a: LTerm, b: LTerm) -> Ordering {
  let aclass = classify::classify_term(a);
  let bclass = classify::classify_term(b);
  aclass.cmp(&bclass)
}

/// Switch between comparisons for equality by primary tag (immediate or boxes
/// or fail immediately for different primary tags).
fn cmp_terms_primary(a: LTerm, b: LTerm, exact: bool) -> EqResult {
  let a_val = a.raw();
  let a_prim_tag = primary::get_tag(a_val);

  let b_val = b.raw();
  let b_prim_tag = primary::get_tag(b_val);
  if b_prim_tag != a_prim_tag {
    // different primary types, compare their classes
    // This can be optimized a little but is there any value in optimization?
    return EqResult::Concluded(cmp_type_order(a, b));
  }

  match a_prim_tag {
    primary::TAG_IMMED => {
      return EqResult::Concluded(cmp_terms_immed(a, b, exact))
    },
    primary::TAG_CONS => unsafe {
      return cmp_cons(a, b)
    },
    primary::TAG_BOX => {
      return cmp_terms_box(a, b)
    },
    _ => panic!("Primary tag {} eq_terms unsupported", a_prim_tag)
  }
}

fn cmp_terms_immed(a: LTerm, b: LTerm, _exact: bool) -> Ordering {
  let av = a.raw();
  let bv = b.raw();

  if a.is_nil() || a.is_empty_tuple() || a.is_empty_binary() {
    if a.raw() == b.raw() {
      return Ordering::Equal;
    }
  }

  if a.is_immediate1() {
    if a.is_local_port() {
      if b.is_local_port() {
        panic!("TODO: cmp local vs local port")
      } else if b.is_external_port() {
        panic!("TODO: cmp local vs ext port")
      } else {
        return cmp_mixed_types(a, b)
      }
    }

    if a.is_local_pid() {
      if b.is_local_pid() {
        panic!("TODO: cmp local vs local pid")
      } else if b.is_external_pid() {
        panic!("TODO: cmp local vs ext pid")
      } else {
        return cmp_mixed_types(a, b)
      }
    }
  }

  if a.is_cons() {
    if !b.is_cons() {
      return cmp_mixed_types(a, b)
    }

    panic!("TODO: invoke cmp_cons correctly from here")
    //return cmp_cons(a, b)
  }

  if a.is_box() {
    if unsafe { a.is_tuple() } {
      if unsafe { b.is_tuple() } {
        panic!("TODO: cmp tuple vs tuple")
      } else {
        return cmp_mixed_types(a, b)
      }
    } else if a.is_map() {
      if a.is_flat_map() {
        if !b.is_flat_map() {
          if b.is_hash_map() {
            let b_size = b.map_size();
            return a.map_size().cmp(&b_size)
          }
        } else {
          // Compare two flat maps
          panic!("TODO: cmp flatmap vs flatmap (+exact)")
        }
      } else {
        if !b.is_hash_map() {
          if b.is_flat_map() {
            let b_size = b.map_size();
            return a.map_size().cmp(&b_size)
          }
        } else {
          // Compare two hash maps
          panic!("TODO: cmp flatmap vs flatmap (+exact)")
        }
      }
      //Hashmap compare strategy:
      //Phase 1. While keys are identical
      //    Do synchronous stepping through leafs of both trees in hash
      //    order. Maintain value compare result of minimal key.
      //
      //Phase 2. If key diff was found in phase 1
      //    Ignore values from now on.
      //    Continue iterate trees by always advancing the one
      //    lagging behind hash-wise. Identical keys are skipped.
      //    A minimal key can only be candidate as tie-breaker if we
      //    have passed that hash value in the other tree (which means
      //    the key did not exist in the other tree).
    } else if unsafe { a.is_float() } {
      if !unsafe { b.is_float() } {
        // TODO: If b is integer and we don't do exact comparison?
        return cmp_mixed_types(a, b)
      } else {
        let af = unsafe { a.float_get() };
        let bf = unsafe { b.float_get() };
        return af.partial_cmp(&bf).unwrap()
      }
    } else if a.is_bignum() {
      if !b.is_bignum() {
        return cmp_mixed_types(a, b)
      }
    } else if a.is_export() {
      if !b.is_export() {
        return cmp_mixed_types(a, b)
      }
      // Compare two exports: from utils.c line ~2918
      // cmp atoms a.module and b.module
      // cmp atoms a.fn and b.fn
      // cmp arity
      panic!("TODO compare 2 exports")
    } else if a.is_header() {
      if !a.is_fun() {
        return cmp_mixed_types(a, b)
      }
      // Compare 2 function objects: from utils.c line ~2937
      // compare a.module, b.module
      // compare old_index
      // compare old_uniq
      // compare num_Free
      panic!("TODO compare 2 fun objects")
    } else if a.is_external_pid() {
      if b.is_local_pid() {
        panic!("TODO compare ext vs local pid")
      } else if b.is_external_pid() {
        panic!("TODO compare ext vs ext pid")
      } else {
        return cmp_mixed_types(a, b)
      }
    } else if a.is_external_port() {
      if b.is_local_port() {
        panic!("TODO compare ext vs local port")
      } else if b.is_external_port() {
        panic!("TODO compare ext vs ext port")
      } else {
        return cmp_mixed_types(a, b)
      }
    } else if a.is_local_ref() {
      if b.is_local_ref() {
        panic!("TODO compare local vs local ref")
      } else if b.is_external_ref() {
        panic!("TODO compare local vs ext ref")
      } else {
        return cmp_mixed_types(a, b)
      }
    } else if a.is_external_ref() {
      if b.is_local_ref() {
        panic!("TODO compare ext vs local ref")
      } else if b.is_external_ref() {
        panic!("TODO compare ext vs ext ref")
      } else {
        return cmp_mixed_types(a, b)
      }
    } else {
      // must be a binary
      assert!(unsafe { a.is_binary() });
      if !unsafe { b.is_binary() } {
        return cmp_mixed_types(a, b)
      }
      panic!("TODO cmp binaries")
    }
  }

  // if both are immediate3, it is hard to distunguish because all imm3 values
  // are special
  if a.is_immediate3() && b.is_immediate3() {
    let a3t = immediate::get_imm3_tag(av);
    let b3t = immediate::get_imm3_tag(bv);
    // If imm3 tags are same, we can conclude with comparing their values
    if a3t == b3t {
      let a3v = immediate::get_imm3_value(av);
      let b3v = immediate::get_imm3_value(bv);
      return a3v.cmp(&b3v);
    }
    return a3t.cmp(&b3t);
  }

  panic!("TODO: eq_terms_immed {} {}", a, b)
}

/// Deeper comparison of two values with different types
fn cmp_mixed_types(_a: LTerm, _b: LTerm) -> Ordering {
  panic!("TODO: cmp_mixed_types(a, b)")
}

/// Compare two cons (list) cells. In case when first elements are equal and
/// a deeper comparison is required, we will return `EqResult::CompareNested`.
/// This will be pushed to a helper stack by the caller (`cmp_terms()`).
unsafe fn cmp_cons(a: LTerm, b: LTerm) -> EqResult {
  let mut aa = a.cons_get_ptr();
  let mut bb = b.cons_get_ptr();

  loop {
    // Check the heads
    let ahd = aa.hd();
    let bhd = bb.hd();

    if LTerm::is_same(ahd, bhd) == false {
      println!("cmp_cons ahd {} bhd {}", ahd, bhd);
      // Recurse into a.hd and b.hd, but push a.tl and b.tl to continue
      let continue_op = ContinueCompare::Cons(aa.tl(), bb.tl());
      return EqResult::CompareNested(ahd, bhd, continue_op)
    }

    // See the tails
    let atl = aa.tl();
    let btl = bb.tl();

    if LTerm::is_same(atl, btl) {
      return EqResult::Concluded(Ordering::Equal)
    }
    if !atl.is_list() || !btl.is_list() {
      // Just do a regular compare of `a.tl` vs `b.tl`
      let continue_op = ContinueCompare::AnyType(atl, btl);
      return EqResult::CompareNested(atl, btl, continue_op)
    }

    // Take the next linked cons cell and continue comparing
    aa = atl.cons_get_ptr();
    bb = btl.cons_get_ptr();
  }
}

fn cmp_terms_box(_a: LTerm, _b: LTerm) -> EqResult {
  panic!("TODO: eq_terms_box")
}
<|endoftext|>


<commit_before>use crate::builtins::Blob;
use crate::instructions::Expression;
use crate::sections::CustomSection;
use crate::sections::Func;
use crate::sections::{ExportDesc, ImportDesc, ImportPath, Section};
use crate::types::FuncType;
use crate::types::GlobalType;
use crate::types::MemType;
use crate::types::TableType;
use custom_debug::CustomDebug;
use std::cell::{Ref, RefCell, RefMut};
use std::convert::TryFrom;
use std::iter::{Extend, FromIterator};
use std::rc::Rc;

fn to_item<T>(value: T) -> Option<Rc<RefCell<T>>> {
    Some(Rc::new(RefCell::new(value)))
}

pub struct IndexedCollection<T> {
    items: Vec<Option<Rc<RefCell<T>>>>,
    deleted: Vec<u32>,
}

impl<T> Default for IndexedCollection<T> {
    fn default() -> Self {
        Self {
            items: Vec::new(),
            deleted: Vec::new(),
        }
    }
}

impl<T> FromIterator<T> for IndexedCollection<T> {
    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
        IndexedCollection {
            items: iter.into_iter().map(to_item).collect(),
            deleted: Vec::new(),
        }
    }
}

impl<T> IndexedCollection<T> {
    fn get_rc_refcell(&self, index: u32) -> Option<&Rc<RefCell<T>>> {
        self.items.get(usize::try_from(index).unwrap())?.as_ref()
    }

    fn get_refcell(&self, index: u32) -> Option<&RefCell<T>> {
        self.get_rc_refcell(index).map(|r| &**r)
    }

    pub fn get(&self, index: u32) -> Option<Ref<T>> {
        self.get_refcell(index).map(RefCell::borrow)
    }

    pub fn get_mut(&mut self, index: u32) -> Option<RefMut<T>> {
        self.get_refcell(index).map(RefCell::borrow_mut)
    }

    pub fn push(&mut self, value: T) -> u32 {
        match self.deleted.pop() {
            Some(index) => {
                self.items[usize::try_from(index).unwrap()] = to_item(value);
                index
            }
            None => {
                let index = u32::try_from(self.items.len()).unwrap();
                self.items.push(to_item(value));
                index
            }
        }
    }

    // Don't allow removals for referenced elements.
    // Will return a number of references as error (excluding the owner).
    pub fn try_remove(&mut self, index: u32) -> Option<Result<T, usize>> {
        let place = self.items.get_mut(usize::try_from(index).unwrap())?;
        Some(match Rc::try_unwrap(place.take()?) {
            Ok(value) => {
                self.deleted.push(index);
                Ok(RefCell::into_inner(value))
            }
            Err(rc) => {
                let count = Rc::strong_count(&rc) - 1;
                *place = Some(rc);
                Err(count)
            }
        })
    }

    pub fn iter(&self) -> IndexedCollectionIter<'_, T> {
        self.into_iter()
    }

    pub fn iter_mut(&mut self) -> IndexedCollectionIterMut<'_, T> {
        self.into_iter()
    }
}

impl<T: std::fmt::Debug> std::fmt::Debug for IndexedCollection<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        f.debug_list().entries(self.iter()).finish()
    }
}

pub struct IndexedCollectionIter<'a, T> {
    inner: std::slice::Iter<'a, Option<Rc<RefCell<T>>>>,
}

impl<'a, T> Iterator for IndexedCollectionIter<'a, T> {
    type Item = Ref<'a, T>;

    fn next(&mut self) -> Option<Self::Item> {
        Some(self.inner.next()?.as_deref()?.borrow())
    }
}

impl<'a, T> IntoIterator for &'a IndexedCollection<T> {
    type Item = Ref<'a, T>;
    type IntoIter = IndexedCollectionIter<'a, T>;

    fn into_iter(self) -> Self::IntoIter {
        IndexedCollectionIter {
            inner: self.items.iter(),
        }
    }
}

pub struct IndexedCollectionIterMut<'a, T> {
    inner: std::slice::IterMut<'a, Option<Rc<RefCell<T>>>>,
}

impl<'a, T> Iterator for IndexedCollectionIterMut<'a, T> {
    type Item = RefMut<'a, T>;

    fn next(&mut self) -> Option<Self::Item> {
        Some(self.inner.next()?.as_deref()?.borrow_mut())
    }
}

impl<'a, T> IntoIterator for &'a mut IndexedCollection<T> {
    type Item = RefMut<'a, T>;
    type IntoIter = IndexedCollectionIterMut<'a, T>;

    fn into_iter(self) -> Self::IntoIter {
        IndexedCollectionIterMut {
            inner: self.items.iter_mut(),
        }
    }
}

#[derive(Debug)]
pub enum MaybeImported<T> {
    Imported(ImportPath),
    Local(T),
}

#[derive(Debug)]
pub struct Element {
    pub offset: Expression,
    pub init: Vec<Rc<RefCell<Function>>>,
}

#[derive(CustomDebug)]
pub struct Data {
    pub offset: Expression,
    #[debug(with = "custom_debug::hexbuf_str")]
    pub init: Blob<Vec<u8>>,
}

#[derive(Debug)]
pub struct Function {
    pub ty: Rc<RefCell<FuncType>>,
    pub body: MaybeImported<Blob<Func>>,
    pub export_name: Option<String>,
}

#[derive(Debug)]
pub struct Global {
    pub ty: GlobalType,
    pub init: MaybeImported<Expression>,
    pub export_name: Option<String>,
}

#[derive(Debug)]
pub struct Memory {
    pub ty: MemType,
    pub import_path: Option<ImportPath>,
    pub export_name: Option<String>,
    pub init: Vec<Data>,
}

#[derive(Debug)]
pub struct Table {
    pub ty: TableType,
    pub import_path: Option<ImportPath>,
    pub export_name: Option<String>,
    pub init: Vec<Element>,
}

#[derive(Debug, Default)]
pub struct Module {
    pub types: IndexedCollection<FuncType>,
    pub functions: IndexedCollection<Function>,
    pub tables: IndexedCollection<Table>,
    pub memories: IndexedCollection<Memory>,
    pub globals: IndexedCollection<Global>,
    pub start: Option<Rc<RefCell<Function>>>,
    pub custom: Vec<Blob<CustomSection>>,
}

impl From<super::module::Module> for Module {
    fn from(src: super::module::Module) -> Self {
        let mut dest = Self::default();
        for section in src.sections {
            match section {
                Section::Custom(custom) => {
                    dest.custom.push(custom);
                }
                Section::Type(Blob(types)) => {
                    dest.types = IndexedCollection::from_iter(types);
                }
                Section::Import(Blob(imports)) => {
                    for import in imports {
                        macro_rules! import {
                            ($dest:ident, $expr:expr) => {
                                dest.$dest.items.push(to_item($expr))
                            };
                        }
                        match import.desc {
                            ImportDesc::Func(type_idx) => import!(
                                functions,
                                Function {
                                    ty: Rc::clone(
                                        dest.types.get_rc_refcell(type_idx.index).unwrap()
                                    ),
                                    body: MaybeImported::Imported(import.path),
                                    export_name: None,
                                }
                            ),
                            ImportDesc::Table(ty) => import!(
                                tables,
                                Table {
                                    ty,
                                    import_path: Some(import.path),
                                    init: Vec::new(),
                                    export_name: None,
                                }
                            ),
                            ImportDesc::Mem(ty) => import!(
                                memories,
                                Memory {
                                    ty,
                                    import_path: Some(import.path),
                                    init: Vec::new(),
                                    export_name: None,
                                }
                            ),
                            ImportDesc::Global(ty) => import!(
                                globals,
                                Global {
                                    ty,
                                    init: MaybeImported::Imported(import.path),
                                    export_name: None,
                                }
                            ),
                        }
                    }
                }
                Section::Function(Blob(type_indices)) => {
                    let types = &dest.types;
                    dest.functions
                        .items
                        .extend(type_indices.into_iter().map(|type_idx| {
                            to_item(Function {
                                ty: Rc::clone(types.get_rc_refcell(type_idx.index).unwrap()),
                                body: MaybeImported::Local(Default::default()),
                                export_name: None,
                            })
                        }));
                }
                Section::Table(Blob(table_types)) => {
                    dest.tables.items.extend(table_types.into_iter().map(|ty| {
                        to_item(Table {
                            ty,
                            import_path: None,
                            export_name: None,
                            init: Vec::new(),
                        })
                    }));
                }
                Section::Memory(Blob(mem_types)) => {
                    dest.memories.items.extend(mem_types.into_iter().map(|ty| {
                        to_item(Memory {
                            ty,
                            import_path: None,
                            export_name: None,
                            init: Vec::new(),
                        })
                    }));
                }
                Section::Global(Blob(globals)) => {
                    dest.globals.items.extend(globals.into_iter().map(|global| {
                        to_item(Global {
                            ty: global.ty,
                            init: MaybeImported::Local(global.init),
                            export_name: None,
                        })
                    }));
                }
                Section::Export(Blob(exports)) => {
                    for export in exports {
                        macro_rules! export {
                            ($dest:ident, $idx:expr) => {
                                dest.$dest.get_mut($idx.index).unwrap().export_name =
                                    Some(export.name)
                            };
                        }
                        match export.desc {
                            ExportDesc::Func(idx) => export!(functions, idx),
                            ExportDesc::Table(idx) => export!(tables, idx),
                            ExportDesc::Mem(idx) => export!(memories, idx),
                            ExportDesc::Global(idx) => export!(globals, idx),
                        }
                    }
                }
                Section::Start(Blob(func_idx)) => {
                    dest.start = Some(Rc::clone(
                        dest.functions.get_rc_refcell(func_idx.index).unwrap(),
                    ));
                }
                Section::Element(Blob(elements)) => {
                    let functions = &dest.functions;
                    for elem in elements {
                        dest.tables
                            .get_mut(elem.table.index)
                            .unwrap()
                            .init
                            .push(Element {
                                offset: elem.offset,
                                init: elem
                                    .init
                                    .into_iter()
                                    .map(|func_idx| {
                                        Rc::clone(functions.get_rc_refcell(func_idx.index).unwrap())
                                    })
                                    .collect(),
                            })
                    }
                }
                Section::Code(Blob(code)) => {
                    for (code, mut func) in code.into_iter().zip(&mut dest.functions) {
                        func.body = MaybeImported::Local(code);
                    }
                }
                Section::Data(Blob(data)) => {
                    for data in data {
                        dest.memories
                            .get_mut(data.memory.index)
                            .unwrap()
                            .init
                            .push(Data {
                                offset: data.offset,
                                init: data.init,
                            })
                    }
                }
            }
        }
        dest
    }
}
<commit_msg>Add type alias for Item<commit_after>use crate::builtins::Blob;
use crate::instructions::Expression;
use crate::sections::CustomSection;
use crate::sections::Func;
use crate::sections::{ExportDesc, ImportDesc, ImportPath, Section};
use crate::types::FuncType;
use crate::types::GlobalType;
use crate::types::MemType;
use crate::types::TableType;
use custom_debug::CustomDebug;
use std::cell::{Ref, RefCell, RefMut};
use std::convert::TryFrom;
use std::iter::{Extend, FromIterator};
use std::rc::Rc;

type Item<T> = Rc<RefCell<T>>;

fn to_item<T>(value: T) -> Option<Item<T>> {
    Some(Rc::new(RefCell::new(value)))
}

pub struct IndexedCollection<T> {
    items: Vec<Option<Item<T>>>,
    deleted: Vec<u32>,
}

impl<T> Default for IndexedCollection<T> {
    fn default() -> Self {
        Self {
            items: Vec::new(),
            deleted: Vec::new(),
        }
    }
}

impl<T> FromIterator<T> for IndexedCollection<T> {
    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
        IndexedCollection {
            items: iter.into_iter().map(to_item).collect(),
            deleted: Vec::new(),
        }
    }
}

impl<T> IndexedCollection<T> {
    fn get_rc_refcell(&self, index: u32) -> Option<&Item<T>> {
        self.items.get(usize::try_from(index).unwrap())?.as_ref()
    }

    fn get_refcell(&self, index: u32) -> Option<&RefCell<T>> {
        self.get_rc_refcell(index).map(|r| &**r)
    }

    pub fn get(&self, index: u32) -> Option<Ref<T>> {
        self.get_refcell(index).map(RefCell::borrow)
    }

    pub fn get_mut(&mut self, index: u32) -> Option<RefMut<T>> {
        self.get_refcell(index).map(RefCell::borrow_mut)
    }

    pub fn push(&mut self, value: T) -> u32 {
        match self.deleted.pop() {
            Some(index) => {
                self.items[usize::try_from(index).unwrap()] = to_item(value);
                index
            }
            None => {
                let index = u32::try_from(self.items.len()).unwrap();
                self.items.push(to_item(value));
                index
            }
        }
    }

    // Don't allow removals for referenced elements.
    // Will return a number of references as error (excluding the owner).
    pub fn try_remove(&mut self, index: u32) -> Option<Result<T, usize>> {
        let place = self.items.get_mut(usize::try_from(index).unwrap())?;
        Some(match Rc::try_unwrap(place.take()?) {
            Ok(value) => {
                self.deleted.push(index);
                Ok(RefCell::into_inner(value))
            }
            Err(rc) => {
                let count = Rc::strong_count(&rc) - 1;
                *place = Some(rc);
                Err(count)
            }
        })
    }

    pub fn iter(&self) -> IndexedCollectionIter<'_, T> {
        self.into_iter()
    }

    pub fn iter_mut(&mut self) -> IndexedCollectionIterMut<'_, T> {
        self.into_iter()
    }
}

impl<T: std::fmt::Debug> std::fmt::Debug for IndexedCollection<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        f.debug_list().entries(self.iter()).finish()
    }
}

pub struct IndexedCollectionIter<'a, T> {
    inner: std::slice::Iter<'a, Option<Item<T>>>,
}

impl<'a, T> Iterator for IndexedCollectionIter<'a, T> {
    type Item = Ref<'a, T>;

    fn next(&mut self) -> Option<Self::Item> {
        Some(self.inner.next()?.as_deref()?.borrow())
    }
}

impl<'a, T> IntoIterator for &'a IndexedCollection<T> {
    type Item = Ref<'a, T>;
    type IntoIter = IndexedCollectionIter<'a, T>;

    fn into_iter(self) -> Self::IntoIter {
        IndexedCollectionIter {
            inner: self.items.iter(),
        }
    }
}

pub struct IndexedCollectionIterMut<'a, T> {
    inner: std::slice::IterMut<'a, Option<Item<T>>>,
}

impl<'a, T> Iterator for IndexedCollectionIterMut<'a, T> {
    type Item = RefMut<'a, T>;

    fn next(&mut self) -> Option<Self::Item> {
        Some(self.inner.next()?.as_deref()?.borrow_mut())
    }
}

impl<'a, T> IntoIterator for &'a mut IndexedCollection<T> {
    type Item = RefMut<'a, T>;
    type IntoIter = IndexedCollectionIterMut<'a, T>;

    fn into_iter(self) -> Self::IntoIter {
        IndexedCollectionIterMut {
            inner: self.items.iter_mut(),
        }
    }
}

#[derive(Debug)]
pub enum MaybeImported<T> {
    Imported(ImportPath),
    Local(T),
}

#[derive(Debug)]
pub struct Element {
    pub offset: Expression,
    pub init: Vec<Item<Function>>,
}

#[derive(CustomDebug)]
pub struct Data {
    pub offset: Expression,
    #[debug(with = "custom_debug::hexbuf_str")]
    pub init: Blob<Vec<u8>>,
}

#[derive(Debug)]
pub struct Function {
    pub ty: Item<FuncType>,
    pub body: MaybeImported<Blob<Func>>,
    pub export_name: Option<String>,
}

#[derive(Debug)]
pub struct Global {
    pub ty: GlobalType,
    pub init: MaybeImported<Expression>,
    pub export_name: Option<String>,
}

#[derive(Debug)]
pub struct Memory {
    pub ty: MemType,
    pub import_path: Option<ImportPath>,
    pub export_name: Option<String>,
    pub init: Vec<Data>,
}

#[derive(Debug)]
pub struct Table {
    pub ty: TableType,
    pub import_path: Option<ImportPath>,
    pub export_name: Option<String>,
    pub init: Vec<Element>,
}

#[derive(Debug, Default)]
pub struct Module {
    pub types: IndexedCollection<FuncType>,
    pub functions: IndexedCollection<Function>,
    pub tables: IndexedCollection<Table>,
    pub memories: IndexedCollection<Memory>,
    pub globals: IndexedCollection<Global>,
    pub start: Option<Item<Function>>,
    pub custom: Vec<Blob<CustomSection>>,
}

impl From<super::module::Module> for Module {
    fn from(src: super::module::Module) -> Self {
        let mut dest = Self::default();
        for section in src.sections {
            match section {
                Section::Custom(custom) => {
                    dest.custom.push(custom);
                }
                Section::Type(Blob(types)) => {
                    dest.types = IndexedCollection::from_iter(types);
                }
                Section::Import(Blob(imports)) => {
                    for import in imports {
                        macro_rules! import {
                            ($dest:ident, $expr:expr) => {
                                dest.$dest.items.push(to_item($expr))
                            };
                        }
                        match import.desc {
                            ImportDesc::Func(type_idx) => import!(
                                functions,
                                Function {
                                    ty: Rc::clone(
                                        dest.types.get_rc_refcell(type_idx.index).unwrap()
                                    ),
                                    body: MaybeImported::Imported(import.path),
                                    export_name: None,
                                }
                            ),
                            ImportDesc::Table(ty) => import!(
                                tables,
                                Table {
                                    ty,
                                    import_path: Some(import.path),
                                    init: Vec::new(),
                                    export_name: None,
                                }
                            ),
                            ImportDesc::Mem(ty) => import!(
                                memories,
                                Memory {
                                    ty,
                                    import_path: Some(import.path),
                                    init: Vec::new(),
                                    export_name: None,
                                }
                            ),
                            ImportDesc::Global(ty) => import!(
                                globals,
                                Global {
                                    ty,
                                    init: MaybeImported::Imported(import.path),
                                    export_name: None,
                                }
                            ),
                        }
                    }
                }
                Section::Function(Blob(type_indices)) => {
                    let types = &dest.types;
                    dest.functions
                        .items
                        .extend(type_indices.into_iter().map(|type_idx| {
                            to_item(Function {
                                ty: Rc::clone(types.get_rc_refcell(type_idx.index).unwrap()),
                                body: MaybeImported::Local(Default::default()),
                                export_name: None,
                            })
                        }));
                }
                Section::Table(Blob(table_types)) => {
                    dest.tables.items.extend(table_types.into_iter().map(|ty| {
                        to_item(Table {
                            ty,
                            import_path: None,
                            export_name: None,
                            init: Vec::new(),
                        })
                    }));
                }
                Section::Memory(Blob(mem_types)) => {
                    dest.memories.items.extend(mem_types.into_iter().map(|ty| {
                        to_item(Memory {
                            ty,
                            import_path: None,
                            export_name: None,
                            init: Vec::new(),
                        })
                    }));
                }
                Section::Global(Blob(globals)) => {
                    dest.globals.items.extend(globals.into_iter().map(|global| {
                        to_item(Global {
                            ty: global.ty,
                            init: MaybeImported::Local(global.init),
                            export_name: None,
                        })
                    }));
                }
                Section::Export(Blob(exports)) => {
                    for export in exports {
                        macro_rules! export {
                            ($dest:ident, $idx:expr) => {
                                dest.$dest.get_mut($idx.index).unwrap().export_name =
                                    Some(export.name)
                            };
                        }
                        match export.desc {
                            ExportDesc::Func(idx) => export!(functions, idx),
                            ExportDesc::Table(idx) => export!(tables, idx),
                            ExportDesc::Mem(idx) => export!(memories, idx),
                            ExportDesc::Global(idx) => export!(globals, idx),
                        }
                    }
                }
                Section::Start(Blob(func_idx)) => {
                    dest.start = Some(Rc::clone(
                        dest.functions.get_rc_refcell(func_idx.index).unwrap(),
                    ));
                }
                Section::Element(Blob(elements)) => {
                    let functions = &dest.functions;
                    for elem in elements {
                        dest.tables
                            .get_mut(elem.table.index)
                            .unwrap()
                            .init
                            .push(Element {
                                offset: elem.offset,
                                init: elem
                                    .init
                                    .into_iter()
                                    .map(|func_idx| {
                                        Rc::clone(functions.get_rc_refcell(func_idx.index).unwrap())
                                    })
                                    .collect(),
                            })
                    }
                }
                Section::Code(Blob(code)) => {
                    for (code, mut func) in code.into_iter().zip(&mut dest.functions) {
                        func.body = MaybeImported::Local(code);
                    }
                }
                Section::Data(Blob(data)) => {
                    for data in data {
                        dest.memories
                            .get_mut(data.memory.index)
                            .unwrap()
                            .init
                            .push(Data {
                                offset: data.offset,
                                init: data.init,
                            })
                    }
                }
            }
        }
        dest
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate glium;
extern crate calx_system;
extern crate calx_window;
extern crate cgmath;

use std::default::Default;
use calx_window::{WindowBuilder, Event, Key};
use cgmath::Angle;

/// Cube face.
#[derive(Copy, Clone)]
enum Face { East, North, Up, West, South, Down }

impl Face {
    /// Normal vector for the face.
    pub fn normal(&self) -> [f32; 3] {
        match *self {
            Face::East =>  [ 1.0,  0.0,  0.0],
            Face::North => [ 0.0,  1.0,  0.0],
            Face::Up =>    [ 0.0,  0.0,  1.0],
            Face::West =>  [-1.0,  0.0,  0.0],
            Face::South => [ 0.0, -1.0,  0.0],
            Face::Down =>  [ 0.0,  0.0, -1.0],
        }
    }

    /// Counter-clockwise vertex list using cube vertex indices.
#[inline]
    pub fn vertices(&self) -> [usize; 4] {
        [[1, 3, 7, 5],
         [2, 6, 7, 3],
         [4, 5, 7, 6],
         [0, 4, 6, 2],
         [0, 1, 5, 4],
         [0, 2, 3, 1]][*self as usize]
    }
}

/// Vertex of a default [0, 0, 0] - [1, 1, 1] cube.
///
/// There are 8 indices, corresponding to 3 bits. The low bit denotes x-axis
/// extension, the middle bit y-axis extension and the high bit z-axis
/// extension.
///
/// ```notrust
///     6 --- 7
///    /:    /|
///   4 --- 5 |
///   | :   | |      Z
///   | 2 ..|.3      | Y
///   |.    |/       |/
///   0 --- 1        o---X
///
/// ```
#[inline]
fn cube_vertex(index: usize) -> [f32; 3] {
    [[0.0, 0.0, 0.0],
     [0.1, 0.0, 0.0],
     [0.0, 0.1, 0.0],
     [0.1, 0.1, 0.0],
     [0.0, 0.0, 0.1],
     [0.1, 0.0, 0.1],
     [0.0, 0.1, 0.1],
     [0.1, 0.1, 0.1]][index]
}

#[derive(Copy, Clone)]
pub struct Vertex {
    pub pos: [f32; 3],
    pub normal: [f32; 3],
    //pub color: [f32; 4],
}
implement_vertex!(Vertex, pos, normal, color);

struct Voxel {
    color: [f32; 4]
}

fn get_voxel(voxelPos: [i32; 3]) -> Option<Voxel> {
    // TODO: Perlin noise landscape or something.
    if voxelPos[2] < 0 {
        Some(Voxel { color: [0.0, 1.0, 0.0, 1.0] })
    } else {
        None
    }
}

fn main() {
    let mut window = WindowBuilder::new().set_title("Voxel demo").build();

    let shader = program!(&window.display,
            150 => {
            vertex: "
                #version 150 core

                uniform mat4 projection;
                uniform mat4 modelview;

                in vec3 position;
                in vec3 normal;
                out vec3 v_position;
                out vec3 v_normal;

                void main() {
                    v_position = position;
                    v_normal = normal;
                    gl_Position = projection * modelview * vec4(v_position, 1.0);
                }
            ",

            fragment: "
                #version 150 core

                in vec3 v_normal;
                out vec4 f_color;
                const vec3 LIGHT = vec3(-0.2, 0.8, 0.1);

                void main() {
                    float lum = max(dot(normalize(v_normal), normalize(LIGHT)), 0.0);
                    vec3 color = (0.3 + 0.7 * lum) * vec3(1.0, 1.0, 1.0);
                    f_color = vec4(color, 1.0);
                }
            ",
            }
        ).unwrap();

    loop {
        for e in window.events().into_iter() {
            match e {
                Event::Quit => return,
                Event::KeyPress(Key::Escape) => return,
                Event::KeyPress(Key::F12) => {
                    calx_system::save_screenshot("calx", window.screenshot())
                        .unwrap();
                }
                _ => (),
            }
        }
        window.clear(0x7799DDFF);

        // Camera setup

        let projection: cgmath::Matrix4<f32> = cgmath::PerspectiveFov {
            fovy: cgmath::Deg::new(90.0).into(),
            aspect: 380.0 / 640.0, // XXX: Hardcoding
            near: 1.0,
            far: 1024.0,
        }.into();

        let modelview: cgmath::Matrix4<f32> = cgmath::Matrix4::look_at(
                cgmath::Point3::new(0.0, -10.0, 10.0),
                cgmath::Point3::new(0.0, 0.0, 0.0),
                cgmath::vec3(0.0, 0.0, 1.0));

        // Convert to the format the shader expects.
        let projection: [[f32; 4]; 4] = projection.into();
        let modelview: [[f32; 4]; 4] = modelview.into();

        let uniforms = uniform! {
            projection: projection,
            modelview: modelview,
        };

        // Draw parameters

        let params = glium::DrawParameters {
            depth: glium::Depth {
                test: glium::DepthTest::IfLess,
                write: true,
                .. Default::default()
            },
            .. Default::default()
        };

        // Setup the cube model
        let faces = [Face::East, Face::North, Face::Up, Face::West, Face::South, Face::Down];
        let mut vertices = Vec::new();
        let mut indices = Vec::new();
        for f in 0..6 {
            normal = faces[f].normal();
            let idx = vertices.len() as u16;
            for v_idx in faces[f].vertices().iter() {
                vertices.push(
                        Vertex {
                            pos: cube_vertex(v_idx),
                            normal: normal
                        });

            }
            indices.push(idx);
            indices.push(idx + 1);
            indices.push(idx + 2);
            indices.push(idx);
            indices.push(idx + 2);
            indices.push(idx + 3);
        }
        let v_buf = glium::VertexBuffer::new(&window.display, &vertices);
        let i_buf = glium::IndexBuffer::new(&window.display, glium::index::PrimitiveType::TrianglesList, &indices).unwrap();

        // Draw the thing

        let mut target = window.display.draw();
        target.draw(&v_buf, &i_buf, &shader, &uniforms, &params).unwrap();
        target.finish().unwrap();

        // window.display(&mut room);

        window.end_frame();
    }
}
<commit_msg>Rustfmt<commit_after>#[macro_use]
extern crate glium;
extern crate calx_system;
extern crate calx_window;
extern crate cgmath;

use std::default::Default;
use calx_window::{WindowBuilder, Event, Key};
use cgmath::Angle;

/// Cube face.
#[derive(Copy, Clone)]
enum Face {
    East,
    North,
    Up,
    West,
    South,
    Down,
}

impl Face {
    /// Normal vector for the face.
    pub fn normal(&self) -> [f32; 3] {
        match *self {
            Face::East => [1.0, 0.0, 0.0],
            Face::North => [0.0, 1.0, 0.0],
            Face::Up => [0.0, 0.0, 1.0],
            Face::West => [-1.0, 0.0, 0.0],
            Face::South => [0.0, -1.0, 0.0],
            Face::Down => [0.0, 0.0, -1.0],
        }
    }

    /// Counter-clockwise vertex list using cube vertex indices.
    #[inline]
    pub fn vertices(&self) -> [usize; 4] {
        [[1, 3, 7, 5],
         [2, 6, 7, 3],
         [4, 5, 7, 6],
         [0, 4, 6, 2],
         [0, 1, 5, 4],
         [0, 2, 3, 1]][*self as usize]
    }
}

/// Vertex of a default [0, 0, 0] - [1, 1, 1] cube.
///
/// There are 8 indices, corresponding to 3 bits. The low bit denotes x-axis
/// extension, the middle bit y-axis extension and the high bit z-axis
/// extension.
///
/// ```notrust
///     6 --- 7
///    /:    /|
///   4 --- 5 |
///   | :   | |      Z
///   | 2 ..|.3      | Y
///   |.    |/       |/
///   0 --- 1        o---X
///
/// ```
#[inline]
fn cube_vertex(index: usize) -> [f32; 3] {
    [[0.0, 0.0, 0.0],
     [0.1, 0.0, 0.0],
     [0.0, 0.1, 0.0],
     [0.1, 0.1, 0.0],
     [0.0, 0.0, 0.1],
     [0.1, 0.0, 0.1],
     [0.0, 0.1, 0.1],
     [0.1, 0.1, 0.1]][index]
}

#[derive(Copy, Clone)]
pub struct Vertex {
    pub pos: [f32; 3],
    pub normal: [f32; 3], // pub color: [f32; 4],
}
implement_vertex!(Vertex, pos, normal, color);

struct Voxel {
    color: [f32; 4],
}

fn get_voxel(voxelPos: [i32; 3]) -> Option<Voxel> {
    // TODO: Perlin noise landscape or something.
    if voxelPos[2] < 0 {
        Some(Voxel { color: [0.0, 1.0, 0.0, 1.0] })
    } else {
        None
    }
}

fn main() {
    let mut window = WindowBuilder::new().set_title("Voxel demo").build();

    let shader = program!(&window.display,
            150 => {
            vertex: "
                #version 150 core

                uniform mat4 projection;
                uniform mat4 modelview;

                in vec3 position;
                in vec3 normal;
                out vec3 v_position;
                out vec3 v_normal;

                void main() {
                    v_position = position;
                    v_normal = normal;
                    gl_Position = projection * modelview * vec4(v_position, 1.0);
                }
            ",

            fragment: "
                #version 150 core

                in vec3 v_normal;
                out vec4 f_color;
                const vec3 LIGHT = vec3(-0.2, 0.8, 0.1);

                void main() {
                    float lum = max(dot(normalize(v_normal), normalize(LIGHT)), 0.0);
                    vec3 color = (0.3 + 0.7 * lum) * vec3(1.0, 1.0, 1.0);
                    f_color = vec4(color, 1.0);
                }
            ",
            }
        ).unwrap();

    loop {
        for e in window.events().into_iter() {
            match e {
                Event::Quit => return,
                Event::KeyPress(Key::Escape) => return,
                Event::KeyPress(Key::F12) => {
                    calx_system::save_screenshot("calx", window.screenshot())
                        .unwrap();
                }
                _ => (),
            }
        }
        window.clear(0x7799DDFF);

        // Camera setup

        let projection: cgmath::Matrix4<f32> =
            cgmath::PerspectiveFov {
                fovy: cgmath::Deg::new(90.0).into(),
                aspect: 380.0 / 640.0, // XXX: Hardcoding
                near: 1.0,
                far: 1024.0,
            }
            .into();

        let modelview: cgmath::Matrix4<f32> =
            cgmath::Matrix4::look_at(cgmath::Point3::new(0.0, -10.0, 10.0),
                                     cgmath::Point3::new(0.0, 0.0, 0.0),
                                     cgmath::vec3(0.0, 0.0, 1.0));

        // Convert to the format the shader expects.
        let projection: [[f32; 4]; 4] = projection.into();
        let modelview: [[f32; 4]; 4] = modelview.into();

        let uniforms = uniform! {
            projection: projection,
            modelview: modelview,
        };

        // Draw parameters

        let params = glium::DrawParameters {
            depth: glium::Depth {
                test: glium::DepthTest::IfLess,
                write: true,
                ..Default::default()
            },
            ..Default::default()
        };

        // Setup the cube model
        let faces = [Face::East,
                     Face::North,
                     Face::Up,
                     Face::West,
                     Face::South,
                     Face::Down];
        let mut vertices = Vec::new();
        let mut indices = Vec::new();
        for f in 0..6 {
            normal = faces[f].normal();
            let idx = vertices.len() as u16;
            for v_idx in faces[f].vertices().iter() {
                vertices.push(Vertex {
                    pos: cube_vertex(v_idx),
                    normal: normal,
                });

            }
            indices.push(idx);
            indices.push(idx + 1);
            indices.push(idx + 2);
            indices.push(idx);
            indices.push(idx + 2);
            indices.push(idx + 3);
        }
        let v_buf = glium::VertexBuffer::new(&window.display, &vertices);
        let i_buf =
            glium::IndexBuffer::new(&window.display,
                                    glium::index::PrimitiveType::TrianglesList,
                                    &indices)
                .unwrap();

        // Draw the thing

        let mut target = window.display.draw();
        target.draw(&v_buf, &i_buf, &shader, &uniforms, &params).unwrap();
        target.finish().unwrap();

        // window.display(&mut room);

        window.end_frame();
    }
}
<|endoftext|>


<commit_before>use std::sync::{Arc, atomic::{Ordering, AtomicUsize, AtomicBool}};
use std::net::SocketAddr;
use parking_lot::Mutex;
use tokio::prelude::*;
use log::warn;

use crate::blockchain::{Blockchain, SignedBlock};
use crate::producer::Minter;

use super::peer::PeerType;
use super::client::*;
use super::rpc::*;

pub struct PeerPool {
    peer_addresses: Vec<SocketAddr>,
    peers: Mutex<Vec<PeerState>>,
    peer_iter_count: AtomicUsize
}

impl PeerPool {
    pub fn new(addrs: &[&str]) -> PeerPool {
        let peer_addresses = addrs.into_iter().map(|s| {
            (*s).parse().map_err(|e| {
                format!("failed to parse address: {} {}", *s, e)
            }).unwrap()
        }).collect::<Vec<SocketAddr>>();
        let peers = Mutex::new(Vec::with_capacity(peer_addresses.len()));
        let peer_iter_count = AtomicUsize::new(0);

        PeerPool {
            peer_addresses,
            peers,
            peer_iter_count
        }
    }

    pub fn start(&self, blockchain: &Arc<Blockchain>, minter: &Arc<Option<Minter>>) {
        assert!(self.peers.lock().is_empty(), "peer pool already started");
        for addr in self.peer_addresses.clone() {
            let (tx, rx) = connect_loop(addr, PeerType::NODE);
            let state = PeerState {
                tx,
                rx,
                connected: Arc::new(AtomicBool::new(false)),
                id: Arc::new(AtomicUsize::new(1))
            };
            let blockchain = Arc::clone(blockchain);
            let minter = Arc::clone(minter);
            self.handle_client_peer(addr, blockchain, minter, state);
        }
    }

    pub fn get_block(&self, height: u64) -> Option<impl Future<Item = Option<SignedBlock>, Error = ()>> {
        let peer = self.get_next_peer();
        if let Some(peer) = peer {
            let payload = Box::new(RpcPayload {
                id: peer.id.fetch_add(1, Ordering::AcqRel) as u32,
                msg: Some(RpcMsg::Block(Box::new(RpcVariant::Req(height))))
            });

            let fut = peer.tx.send(ClientEvent::Message(payload)).and_then(|rx| {
                Ok(rx.unwrap())
            }).and_then(|rx| {
                rx
            }).map(|payload| {
                match payload.msg {
                    Some(msg) => {
                        match msg {
                            RpcMsg::Block(var) => match var.res() {
                                Some(block) => block,
                                None => None
                            },
                            _ => None
                        }
                    },
                    None => None
                }
            }).map_err(|_| {});
            return Some(fut)
        }
        None
    }

    fn handle_client_peer(&self,
                            addr: SocketAddr,
                            blockchain: Arc<Blockchain>,
                            minter: Arc<Option<Minter>>,
                            state: PeerState) {
        macro_rules! quick_send {
            ($state:expr, $id:expr, $msg:expr) => {
                $state.tx.send(ClientEvent::Message(Box::new(RpcPayload {
                    id: $id,
                    msg: Some($msg)
                })))
            };
            ($state:expr, $id:expr) => {
                $state.tx.send(ClientEvent::Message(Box::new(RpcPayload {
                    id: $id,
                    msg: None
                })))
            };
        }

        self.peers.lock().push(state.clone());
        tokio::spawn(state.rx.clone().for_each(move |evt| {
            match evt {
                ClientEvent::Connect => {
                    state.connected.store(true, Ordering::Release);
                },
                ClientEvent::Disconnect => {
                    state.connected.store(false, Ordering::Release);
                },
                ClientEvent::Message(rpc) => {
                    let id = rpc.id;
                    let msg = match rpc.msg {
                        Some(msg) => msg,
                        None => return Ok(())
                    };
                    match msg {
                        RpcMsg::Handshake(_) => {
                            warn!("[{}] Invalid handshake message sent from peer", addr);
                        }
                        RpcMsg::Error(_) => {},
                        RpcMsg::Event(evt) => {
                            if let Some(minter) = &*minter {
                                match *evt {
                                    RpcEvent::Block(block) => {
                                        let _ = minter.add_block(block);
                                    },
                                    RpcEvent::Tx(tx) => {
                                        let _ = minter.add_tx(tx);
                                    }
                                }
                            }
                        },
                        RpcMsg::Broadcast(tx) => {
                            if let Some(minter) = &*minter {
                                match minter.add_tx(tx) {
                                    Ok(_) => {
                                        quick_send!(state, id).wait().unwrap();
                                    },
                                    Err(s) => {
                                        quick_send!(state, id, RpcMsg::Error(s)).wait().unwrap();
                                    }
                                }
                            }
                        },
                        RpcMsg::Properties(var) => {
                            if var.req().is_some() {
                                let props = blockchain.get_properties();
                                let var = RpcVariant::Res(props);
                                quick_send!(state, id, RpcMsg::Properties(var)).wait().unwrap();
                            }
                        },
                        RpcMsg::Block(var) => {
                            if let Some(height) = var.req() {
                                let block = match blockchain.get_block(height) {
                                    Some(block) => Some((&*block).clone()),
                                    None => None
                                };
                                let var = Box::new(RpcVariant::Res(block));
                                quick_send!(state, id, RpcMsg::Block(var)).wait().unwrap();
                            }
                        },
                        RpcMsg::Balance(var) => {
                            if let Some(addr) = var.req() {
                                let bal = blockchain.get_balance(&addr);
                                let var = RpcVariant::Res(bal);
                                quick_send!(state, id, RpcMsg::Balance(var)).wait().unwrap();
                            }
                        },
                        RpcMsg::TotalFee(var) => {
                            if let Some(addr) = var.req() {
                                let fee = blockchain.get_total_fee(&addr);
                                match fee {
                                    Some(fee) => {
                                        let var = RpcVariant::Res(fee);
                                        quick_send!(state, id, RpcMsg::TotalFee(var)).wait().unwrap();
                                    },
                                    None => {
                                        let err = "failed to retrieve total fee".to_string();
                                        quick_send!(state, id, RpcMsg::Error(err)).wait().unwrap();
                                    }
                                }
                            }
                        }
                    }
                }
            }
            Ok(())
        }));
    }

    fn get_next_peer(&self) -> Option<PeerState> {
        let peers = self.peers.lock();
        let len = peers.len();
        if len < 1 { return None }

        let first_count = self.peer_iter_count.fetch_add(1, Ordering::AcqRel);
        loop {
            let count = self.peer_iter_count.fetch_add(1, Ordering::AcqRel);
            let peer = &peers[count % len];
            if peer.connected.load(Ordering::Acquire) { return Some(peer.clone()) }
            if count == first_count { break; }
        }

        None
    }
}

#[derive(Clone)]
struct PeerState {
    tx: ClientSender,
    rx: ClientReceiver,
    connected: Arc<AtomicBool>,
    id: Arc<AtomicUsize>
}
<commit_msg>Fix clippy warning<commit_after>use std::sync::{Arc, atomic::{Ordering, AtomicUsize, AtomicBool}};
use std::net::SocketAddr;
use parking_lot::Mutex;
use tokio::prelude::*;
use log::warn;

use crate::blockchain::{Blockchain, SignedBlock};
use crate::producer::Minter;

use super::peer::PeerType;
use super::client::*;
use super::rpc::*;

pub struct PeerPool {
    peer_addresses: Vec<SocketAddr>,
    peers: Mutex<Vec<PeerState>>,
    peer_iter_count: AtomicUsize
}

impl PeerPool {
    pub fn new(addrs: &[&str]) -> PeerPool {
        let peer_addresses = addrs.iter().map(|s| {
            (*s).parse().map_err(|e| {
                format!("failed to parse address: {} {}", *s, e)
            }).unwrap()
        }).collect::<Vec<SocketAddr>>();
        let peers = Mutex::new(Vec::with_capacity(peer_addresses.len()));
        let peer_iter_count = AtomicUsize::new(0);

        PeerPool {
            peer_addresses,
            peers,
            peer_iter_count
        }
    }

    pub fn start(&self, blockchain: &Arc<Blockchain>, minter: &Arc<Option<Minter>>) {
        assert!(self.peers.lock().is_empty(), "peer pool already started");
        for addr in self.peer_addresses.clone() {
            let (tx, rx) = connect_loop(addr, PeerType::NODE);
            let state = PeerState {
                tx,
                rx,
                connected: Arc::new(AtomicBool::new(false)),
                id: Arc::new(AtomicUsize::new(1))
            };
            let blockchain = Arc::clone(blockchain);
            let minter = Arc::clone(minter);
            self.handle_client_peer(addr, blockchain, minter, state);
        }
    }

    pub fn get_block(&self, height: u64) -> Option<impl Future<Item = Option<SignedBlock>, Error = ()>> {
        let peer = self.get_next_peer();
        if let Some(peer) = peer {
            let payload = Box::new(RpcPayload {
                id: peer.id.fetch_add(1, Ordering::AcqRel) as u32,
                msg: Some(RpcMsg::Block(Box::new(RpcVariant::Req(height))))
            });

            let fut = peer.tx.send(ClientEvent::Message(payload)).and_then(|rx| {
                Ok(rx.unwrap())
            }).and_then(|rx| {
                rx
            }).map(|payload| {
                match payload.msg {
                    Some(msg) => {
                        match msg {
                            RpcMsg::Block(var) => match var.res() {
                                Some(block) => block,
                                None => None
                            },
                            _ => None
                        }
                    },
                    None => None
                }
            }).map_err(|_| {});
            return Some(fut)
        }
        None
    }

    fn handle_client_peer(&self,
                            addr: SocketAddr,
                            blockchain: Arc<Blockchain>,
                            minter: Arc<Option<Minter>>,
                            state: PeerState) {
        macro_rules! quick_send {
            ($state:expr, $id:expr, $msg:expr) => {
                $state.tx.send(ClientEvent::Message(Box::new(RpcPayload {
                    id: $id,
                    msg: Some($msg)
                })))
            };
            ($state:expr, $id:expr) => {
                $state.tx.send(ClientEvent::Message(Box::new(RpcPayload {
                    id: $id,
                    msg: None
                })))
            };
        }

        self.peers.lock().push(state.clone());
        tokio::spawn(state.rx.clone().for_each(move |evt| {
            match evt {
                ClientEvent::Connect => {
                    state.connected.store(true, Ordering::Release);
                },
                ClientEvent::Disconnect => {
                    state.connected.store(false, Ordering::Release);
                },
                ClientEvent::Message(rpc) => {
                    let id = rpc.id;
                    let msg = match rpc.msg {
                        Some(msg) => msg,
                        None => return Ok(())
                    };
                    match msg {
                        RpcMsg::Handshake(_) => {
                            warn!("[{}] Invalid handshake message sent from peer", addr);
                        }
                        RpcMsg::Error(_) => {},
                        RpcMsg::Event(evt) => {
                            if let Some(minter) = &*minter {
                                match *evt {
                                    RpcEvent::Block(block) => {
                                        let _ = minter.add_block(block);
                                    },
                                    RpcEvent::Tx(tx) => {
                                        let _ = minter.add_tx(tx);
                                    }
                                }
                            }
                        },
                        RpcMsg::Broadcast(tx) => {
                            if let Some(minter) = &*minter {
                                match minter.add_tx(tx) {
                                    Ok(_) => {
                                        quick_send!(state, id).wait().unwrap();
                                    },
                                    Err(s) => {
                                        quick_send!(state, id, RpcMsg::Error(s)).wait().unwrap();
                                    }
                                }
                            }
                        },
                        RpcMsg::Properties(var) => {
                            if var.req().is_some() {
                                let props = blockchain.get_properties();
                                let var = RpcVariant::Res(props);
                                quick_send!(state, id, RpcMsg::Properties(var)).wait().unwrap();
                            }
                        },
                        RpcMsg::Block(var) => {
                            if let Some(height) = var.req() {
                                let block = match blockchain.get_block(height) {
                                    Some(block) => Some((&*block).clone()),
                                    None => None
                                };
                                let var = Box::new(RpcVariant::Res(block));
                                quick_send!(state, id, RpcMsg::Block(var)).wait().unwrap();
                            }
                        },
                        RpcMsg::Balance(var) => {
                            if let Some(addr) = var.req() {
                                let bal = blockchain.get_balance(&addr);
                                let var = RpcVariant::Res(bal);
                                quick_send!(state, id, RpcMsg::Balance(var)).wait().unwrap();
                            }
                        },
                        RpcMsg::TotalFee(var) => {
                            if let Some(addr) = var.req() {
                                let fee = blockchain.get_total_fee(&addr);
                                match fee {
                                    Some(fee) => {
                                        let var = RpcVariant::Res(fee);
                                        quick_send!(state, id, RpcMsg::TotalFee(var)).wait().unwrap();
                                    },
                                    None => {
                                        let err = "failed to retrieve total fee".to_string();
                                        quick_send!(state, id, RpcMsg::Error(err)).wait().unwrap();
                                    }
                                }
                            }
                        }
                    }
                }
            }
            Ok(())
        }));
    }

    fn get_next_peer(&self) -> Option<PeerState> {
        let peers = self.peers.lock();
        let len = peers.len();
        if len < 1 { return None }

        let first_count = self.peer_iter_count.fetch_add(1, Ordering::AcqRel);
        loop {
            let count = self.peer_iter_count.fetch_add(1, Ordering::AcqRel);
            let peer = &peers[count % len];
            if peer.connected.load(Ordering::Acquire) { return Some(peer.clone()) }
            if count == first_count { break; }
        }

        None
    }
}

#[derive(Clone)]
struct PeerState {
    tx: ClientSender,
    rx: ClientReceiver,
    connected: Arc<AtomicBool>,
    id: Arc<AtomicUsize>
}
<|endoftext|>