<commit_before>#[macro_use]
extern crate serde_derive;
extern crate serde_xml_rs;

#[derive(Debug, Deserialize, Default)]
pub struct Mass {
    pub value: f64,
}

#[derive(Debug, Deserialize, Default)]
pub struct Inertia {
    pub ixx: f64,
    pub ixy: f64,
    pub ixz: f64,
    pub iyy: f64,
    pub iyz: f64,
    pub izz: f64,
}

#[derive(Debug, Deserialize, Default)]
pub struct Inertial {
    #[serde(default)]
    pub origin: Pose,
    pub mass: Mass,
    pub inertia: Inertia,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Geometry {
    Box { size: String },
    Cylinder { radius: f64, length: f64 },
    Sphere { radius: f64 },
    Mesh { filename: String, scale: f64 },
}

impl Default for Geometry {
    fn default() -> Geometry {
        Geometry::Box { size: "0 0 0".to_string() }
    }
}

#[derive(Debug, Deserialize)]
pub struct Color {
    pub rgba: String,
}

impl Default for Color {
    fn default() -> Color {
        Color { rgba: "0 0 0 0".to_string() }
    }
}

#[derive(Debug, Deserialize)]
pub struct Texture {
    pub filename: String,
}

impl Default for Texture {
    fn default() -> Texture {
        Texture { filename: "".to_string() }
    }
}

#[derive(Debug, Deserialize, Default)]
pub struct Material {
    #[serde(default)]
    pub name: String,
    #[serde(default)]
    pub color: Color,
    #[serde(default)]
    pub texture: Texture,
}

#[derive(Debug, Deserialize, Default)]
pub struct Visual {
    #[serde(default)]
    pub name: String,
    #[serde(default)]
    pub origin: Pose,
    pub geometry: Geometry,
    #[serde(default)]
    pub material: Material,
}

#[derive(Debug, Deserialize, Default)]
pub struct Collision {
    #[serde(default)]
    pub name: String,
    pub origin: Pose,
    pub geometry: Geometry,
}

#[derive(Debug, Deserialize)]
pub struct Link {
    pub name: String,
    #[serde(default)]
    pub inertial: Inertial,
    #[serde(default)]
    pub visual: Visual,
    #[serde(default)]
    pub collision: Collision,
}

#[derive(Debug, Deserialize)]
pub struct Axis {
    pub xyz: String,
}

fn parse_array_from_string(string: &str) -> [f64; 3] {
    let vec = string.split(" ").filter_map(|x| x.parse::<f64>().ok()).collect::<Vec<_>>();
    assert!(vec.len() == 3);
    let mut arr = [0.0f64; 3];
    for i in 0..3 {
        arr[i] = vec[i];
    }
    arr
}

impl Axis {
    pub fn xyz_as_array(&self) -> [f64; 3] {
        parse_array_from_string(&self.xyz)
    }
}

impl Default for Axis {
    fn default() -> Axis {
        Axis { xyz: "1 0 0".to_string() }
    }
}

#[derive(Debug, Deserialize)]
pub struct Pose {
    #[serde(default)]
    pub xyz: String,
    #[serde(default)]
    pub rpy: String,
}

impl Pose {
    pub fn xyz_as_array(&self) -> [f64; 3] {
        parse_array_from_string(&self.xyz)
    }
    pub fn rpy_as_array(&self) -> [f64; 3] {
        parse_array_from_string(&self.rpy)
    }
}

impl Default for Pose {
    fn default() -> Pose {
        Pose {
            xyz: "0 0 0".to_string(),
            rpy: "0 0 0".to_string(),
        }
    }
}

#[derive(Debug, Deserialize)]
pub struct LinkName {
    pub link: String,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum JointType {
    Revolute,
    Continuous,
    Prismatic,
    Fixed,
    Floating,
    Planar,
}

#[derive(Debug, Deserialize)]
pub struct JointLimit {
    #[serde(default)]
    pub lower: f64,
    #[serde(default)]
    pub upper: f64,
    pub effort: f64,
    pub velocity: f64,
}

#[derive(Debug, Deserialize, Default)]
pub struct Mimic {
    joint: String,
    multiplier: f64,
    offset: f64,
}

#[derive(Debug, Deserialize, Default)]
pub struct SafetyController {
    soft_lower_limit: f64,
    soft_upper_limit: f64,
    k_position: f64,
    k_velocity: f64,
}

#[derive(Debug, Deserialize)]
pub struct Joint {
    pub name: String,
    #[serde(rename = "type")]
    pub joint_type: JointType,
    #[serde(default)]
    pub origin: Pose,
    pub parent: LinkName,
    pub child: LinkName,
    #[serde(default)]
    pub axis: Axis,
    pub limit: JointLimit,
    #[serde(default)]
    pub dynamics: Dynamics,
    #[serde(default)]
    pub mimic: Mimic,
    #[serde(default)]
    pub safety_controller: SafetyController,
}

#[derive(Debug, Deserialize, Default)]
pub struct Dynamics {
    damping: f64,
    friction: f64,
}

#[derive(Debug, Deserialize)]
pub struct Robot {
    pub name: String,

    #[serde(rename = "link", default)]
    pub links: Vec<Link>,

    #[serde(rename = "joint", default)]
    pub joints: Vec<Joint>,
}

use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use std::error::Error;
use std::fmt;

#[derive(Debug)]
pub enum UrdfError {
    File(std::io::Error),
    Xml(serde_xml_rs::Error),
}

impl fmt::Display for UrdfError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            UrdfError::File(ref err) => err.fmt(f),
            UrdfError::Xml(ref err) => err.fmt(f),
        }
    }
}

impl Error for UrdfError {
    fn description(&self) -> &str {
        match *self {
            UrdfError::File(ref err) => err.description(),
            UrdfError::Xml(ref err) => err.description(),
        }
    }
}

impl From<std::io::Error> for UrdfError {
    fn from(err: std::io::Error) -> UrdfError {
        UrdfError::File(err)
    }
}

impl From<serde_xml_rs::Error> for UrdfError {
    fn from(err: serde_xml_rs::Error) -> UrdfError {
        UrdfError::Xml(err)
    }
}

pub fn read_file<P: AsRef<Path>>(path: P) -> Result<Robot, UrdfError> {
    let mut file = File::open(path)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    read_from_string(&contents)
}

pub fn read_from_string(string: &str) -> Result<Robot, UrdfError> {
    serde_xml_rs::deserialize(string.as_bytes())
        .map_err(From::from)
}

#[test]
fn it_works() {
    let s = r##"
        <robot name="robo">
            <link name="shoulder1">
                <inertial>
                    <origin xyz="0 0 0.5" rpy="0 0 0"/>
                    <mass value="1"/>
                    <inertia ixx="100"  ixy="0"  ixz="0" iyy="100" iyz="0" izz="100" />
                </inertial>
                <visual>
                    <origin xyz="0.1 0.2 0.3" rpy="-0.1 -0.2  -0.3" />
                    <geometry>
                        <box size="1 1 1" />
                    </geometry>
                    <material name="Cyan">
                        <color rgba="0 1.0 1.0 1.0"/>
                    </material>
                </visual>
                <collision>
                    <origin xyz="0 0 0" rpy="0 0 0"/>
                    <geometry>
                        <cylinder radius="1" length="0.5"/>
                    </geometry>
                </collision>
            </link>
            <link name="elbow1" />
            <link name="wrist1" />
            <joint name="shoulder_pitch" type="revolute">
                <origin xyz="0.0 0.0 0.1" />
                <parent link="shoulder1" />
                <child link="elbow1" />
                <axis xyz="0 1 -1" />
                <limit lower="-1" upper="1.0" effort="0" velocity="1.0"/>
            </joint>
            <joint name="shoulder_pitch" type="revolute">
                <origin xyz="0.0, 0.0, 0.0" />
                <parent link="elbow1" />
                <child link="wrist1" />
                <axis xyz="0 1 0" />
                <limit lower="-2" upper="1.0" effort="0" velocity="1.0"/>
            </joint>
        </robot>
    "##;
    let robo = read_from_string(s).unwrap();

    assert_eq!(robo.name, "robo");
    assert_eq!(robo.links.len(), 3);
    assert_eq!(robo.joints.len(), 2);
    assert_eq!(robo.links[0].visual.origin.xyz_as_array()[0], 0.1);
    assert_eq!(robo.links[0].visual.origin.xyz_as_array()[1], 0.2);
    assert_eq!(robo.links[0].visual.origin.xyz_as_array()[2], 0.3);
    assert_eq!(robo.links[0].visual.origin.rpy_as_array()[0], -0.1);
    assert_eq!(robo.links[0].visual.origin.rpy_as_array()[1], -0.2);
    assert_eq!(robo.links[0].visual.origin.rpy_as_array()[2], -0.3);

    assert_eq!(robo.joints[0].name, "shoulder_pitch");
    assert_eq!(robo.joints[0].axis.xyz, "0 1 -1");
    assert_eq!(robo.joints[0].axis.xyz_as_array()[0], 0.0f64);
    assert_eq!(robo.joints[0].axis.xyz_as_array()[1], 1.0f64);
    assert_eq!(robo.joints[0].axis.xyz_as_array()[2], -1.0f64);
    assert_eq!(robo.joints[0].origin.xyz, "0.0 0.0 0.1");
}
<commit_msg>Use custom serializer for float arrays<commit_after>#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_xml_rs;

#[derive(Debug, Deserialize, Default)]
pub struct Mass {
    pub value: f64,
}

#[derive(Debug, Deserialize, Default)]
pub struct Inertia {
    pub ixx: f64,
    pub ixy: f64,
    pub ixz: f64,
    pub iyy: f64,
    pub iyz: f64,
    pub izz: f64,
}

#[derive(Debug, Deserialize, Default)]
pub struct Inertial {
    #[serde(default)]
    pub origin: Pose,
    pub mass: Mass,
    pub inertia: Inertia,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Geometry {
    Box {
        #[serde(with = "urdf_vec3")]
        size: [f64; 3]
        //size: String
    },
    Cylinder { radius: f64, length: f64 },
    Sphere { radius: f64 },
    Mesh { filename: String, scale: f64 },
}

impl Default for Geometry {
    fn default() -> Geometry {
        Geometry::Box { size: [0.0f64, 0.0, 0.0] }
    }
}

#[derive(Debug, Deserialize, Default)]
pub struct Color {
    #[serde(with = "urdf_vec4")]
    #[serde(default="default_rgba")]
    pub rgba: [f64; 4],
}

#[derive(Debug, Deserialize)]
pub struct Texture {
    pub filename: String,
}

impl Default for Texture {
    fn default() -> Texture {
        Texture { filename: "".to_string() }
    }
}

#[derive(Debug, Deserialize, Default)]
pub struct Material {
    #[serde(default)]
    pub name: String,
    #[serde(default)]
    pub color: Color,
    #[serde(default)]
    pub texture: Texture,
}

#[derive(Debug, Deserialize, Default)]
pub struct Visual {
    #[serde(default)]
    pub name: String,
    #[serde(default)]
    pub origin: Pose,
    pub geometry: Geometry,
    #[serde(default)]
    pub material: Material,
}

#[derive(Debug, Deserialize, Default)]
pub struct Collision {
    #[serde(default)]
    pub name: String,
    pub origin: Pose,
    pub geometry: Geometry,
}

#[derive(Debug, Deserialize)]
pub struct Link {
    pub name: String,
    #[serde(default)]
    pub inertial: Inertial,
    #[serde(default)]
    pub visual: Visual,
    #[serde(default)]
    pub collision: Collision,
}

#[derive(Deserialize, Debug)]
pub struct Vec3 {
    #[serde(with = "urdf_vec3")]
    pub data: [f64; 3]
}

mod urdf_vec3 {
    use serde::{self, Deserialize, Deserializer};
    pub fn deserialize<D>(deserializer: D) -> Result<[f64; 3], D::Error>
    where D: Deserializer
{
        let s = String::deserialize(deserializer)?;
        let vec = s.split(" ").filter_map(|x| x.parse::<f64>().ok()).collect::<Vec<_>>();
        if vec.len() != 3 {
            return Err(serde::de::Error::custom(
                format!("failed to parse float array in {}", s)));
        }
        let mut arr = [0.0f64; 3];
        for i in 0..3 {
            arr[i] = vec[i];
        }
        Ok(arr)
    }
}

mod urdf_vec4 {
    use serde::{self, Deserialize, Deserializer};
    pub fn deserialize<D>(deserializer: D) -> Result<[f64; 4], D::Error>
    where D: Deserializer
{
        let s = String::deserialize(deserializer)?;
        let vec = s.split(" ").filter_map(|x| x.parse::<f64>().ok()).collect::<Vec<_>>();
        if vec.len() != 4 {
            return Err(serde::de::Error::custom(
                format!("failed to parse float array in {}", s)));
        }
        let mut arr = [0.0f64; 4];
        for i in 0..4 {
            arr[i] = vec[i];
        }
        Ok(arr)
    }
}

#[derive(Debug, Deserialize)]
pub struct Axis {
    #[serde(with = "urdf_vec3")]
    pub xyz: [f64; 3]
}

pub fn array_from(string: &str) -> Result<[f64; 3], UrdfError> {
    let vec = string.split(" ").filter_map(|x| x.parse::<f64>().ok()).collect::<Vec<_>>();
    if vec.len() != 3 {
        return Err(UrdfError::Parse(string.to_string()));
    }
    let mut arr = [0.0f64; 3];
    for i in 0..3 {
        arr[i] = vec[i];
    }
    Ok(arr)
}

impl Default for Axis {
    fn default() -> Axis {
        Axis { xyz: [1.0f64, 0.0, 0.0,] }
    }
}

#[derive(Debug, Deserialize)]
pub struct Pose {
    #[serde(with = "urdf_vec3")]
    #[serde(default="default_zero3")]
    pub xyz: [f64; 3],
    #[serde(with = "urdf_vec3")]
    #[serde(default="default_zero3")]
    pub rpy: [f64; 3],
}

pub fn default_zero3() -> [f64; 3] {
    [0.0f64, 0.0, 0.0]
}

pub fn default_rgba() -> [f64; 4] {
    [1.0f64, 1.0, 1.0, 1.0]
}

/*
impl Pose {
    pub fn xyz_as_array(&self) -> Result<[f64; 3], UrdfError> {
        array_from(&self.xyz)
    }
    pub fn rpy_as_array(&self) -> Result<[f64; 3], UrdfError> {
        array_from(&self.rpy)
    }
}
 */

impl Default for Pose {
    fn default() -> Pose {
        Pose {
            xyz: default_zero3(),
            rpy: default_zero3(),
        }
    }
}

#[derive(Debug, Deserialize)]
pub struct LinkName {
    pub link: String,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum JointType {
    Revolute,
    Continuous,
    Prismatic,
    Fixed,
    Floating,
    Planar,
}

#[derive(Debug, Deserialize)]
pub struct JointLimit {
    #[serde(default)]
    pub lower: f64,
    #[serde(default)]
    pub upper: f64,
    pub effort: f64,
    pub velocity: f64,
}

#[derive(Debug, Deserialize, Default)]
pub struct Mimic {
    joint: String,
    multiplier: f64,
    offset: f64,
}

#[derive(Debug, Deserialize, Default)]
pub struct SafetyController {
    soft_lower_limit: f64,
    soft_upper_limit: f64,
    k_position: f64,
    k_velocity: f64,
}

#[derive(Debug, Deserialize)]
pub struct Joint {
    pub name: String,
    #[serde(rename = "type")]
    pub joint_type: JointType,
    #[serde(default)]
    pub origin: Pose,
    pub parent: LinkName,
    pub child: LinkName,
    #[serde(default)]
    pub axis: Axis,
    pub limit: JointLimit,
    #[serde(default)]
    pub dynamics: Dynamics,
    #[serde(default)]
    pub mimic: Mimic,
    #[serde(default)]
    pub safety_controller: SafetyController,
}

#[derive(Debug, Deserialize, Default)]
pub struct Dynamics {
    damping: f64,
    friction: f64,
}

#[derive(Debug, Deserialize)]
pub struct Robot {
    pub name: String,

    #[serde(rename = "link", default)]
    pub links: Vec<Link>,

    #[serde(rename = "joint", default)]
    pub joints: Vec<Joint>,
}

use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use std::error::Error;
use std::fmt;

#[derive(Debug)]
pub enum UrdfError {
    File(std::io::Error),
    Xml(serde_xml_rs::Error),
    Parse(String),
}

impl fmt::Display for UrdfError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            UrdfError::File(ref err) => err.fmt(f),
            UrdfError::Xml(ref err) => err.fmt(f),
            UrdfError::Parse(ref msg) => write!(f, "parse error {}", msg),
        }
    }
}

impl Error for UrdfError {
    fn description(&self) -> &str {
        match *self {
            UrdfError::File(ref err) => err.description(),
            UrdfError::Xml(ref err) => err.description(),
            UrdfError::Parse(_) => "parse error",
        }
    }
}

impl From<std::io::Error> for UrdfError {
    fn from(err: std::io::Error) -> UrdfError {
        UrdfError::File(err)
    }
}

impl From<serde_xml_rs::Error> for UrdfError {
    fn from(err: serde_xml_rs::Error) -> UrdfError {
        UrdfError::Xml(err)
    }
}

pub fn read_file<P: AsRef<Path>>(path: P) -> Result<Robot, UrdfError> {
    let mut file = File::open(path)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    read_from_string(&contents)
}

pub fn read_from_string(string: &str) -> Result<Robot, UrdfError> {
    serde_xml_rs::deserialize(string.as_bytes())
        .map_err(From::from)
}

#[test]
fn it_works() {
    let s = r##"
        <robot name="robo">
            <link name="shoulder1">
                <inertial>
                    <origin xyz="0 0 0.5" rpy="0 0 0"/>
                    <mass value="1"/>
                    <inertia ixx="100"  ixy="0"  ixz="0" iyy="100" iyz="0" izz="100" />
                </inertial>
                <visual>
                    <origin xyz="0.1 0.2 0.3" rpy="-0.1 -0.2  -0.3" />
                    <geometry>
                        <box size="1.0 2.0 3.0" />
                    </geometry>
                    <material name="Cyan">
                        <color rgba="0 1.0 1.0 1.0"/>
                    </material>
                </visual>
                <collision>
                    <origin xyz="0 0 0" rpy="0 0 0"/>
                    <geometry>
                        <cylinder radius="1" length="0.5"/>
                    </geometry>
                </collision>
            </link>
            <link name="elbow1" />
            <link name="wrist1" />
            <joint name="shoulder_pitch" type="revolute">
                <origin xyz="0.0 0.0 0.1" />
                <parent link="shoulder1" />
                <child link="elbow1" />
                <axis xyz="0 1 -1" />
                <limit lower="-1" upper="1.0" effort="0" velocity="1.0"/>
            </joint>
            <joint name="shoulder_pitch" type="revolute">
                <origin xyz="0.0 0.0 0.0" />
                <parent link="elbow1" />
                <child link="wrist1" />
                <axis xyz="0 1 0" />
                <limit lower="-2" upper="1.0" effort="0" velocity="1.0"/>
            </joint>
        </robot>
    "##;
    let robo = read_from_string(s).unwrap();

    assert_eq!(robo.name, "robo");
    assert_eq!(robo.links.len(), 3);
    assert_eq!(robo.joints.len(), 2);
    let xyz = robo.links[0].visual.origin.xyz;
    assert_eq!(xyz[0], 0.1);
    assert_eq!(xyz[1], 0.2);
    assert_eq!(xyz[2], 0.3);
    let rpy = robo.links[0].visual.origin.rpy;
    assert_eq!(rpy[0], -0.1);
    assert_eq!(rpy[1], -0.2);
    assert_eq!(rpy[2], -0.3);

    match robo.links[0].visual.geometry {
        Geometry::Box{size} => {
            assert_eq!(size[0], 1.0f64);
            assert_eq!(size[1], 2.0f64);
            assert_eq!(size[2], 3.0f64);
        },
        _ => { panic!("geometry error") },
    }

    assert_eq!(robo.joints[0].name, "shoulder_pitch");
    let xyz = robo.joints[0].axis.xyz;
    assert_eq!(xyz[0], 0.0f64);
    assert_eq!(xyz[1], 1.0f64);
    assert_eq!(xyz[2], -1.0f64);
    let xyz = robo.joints[0].axis.xyz;
    //"0 1 -1"
    assert_eq!(xyz[0], 0.0);
    assert_eq!(xyz[1], 1.0);
    assert_eq!(xyz[2], -1.0);

}
<|endoftext|>


<commit_before>use std::mem;
use std::sync::Arc;
use std::cmp::min;
use {FieldId, ColumnId, RowId, PredictorId, Basics, Column, ExtendedTime};
use crossbeam::mem::epoch::{self, Atomic};

struct AtomicFieldInner <B: Basics, C: Column> {
  data: Option <C::FieldType>,
  when: Arc <EventRecord <B>>,
}

struct AtomicField <B: Basics, C: Column> {
  data: Atomic <AtomicFieldInner <B, C>>,

  #[cfg (debug_assertions)]
  column_id: ColumnId,
}

struct FieldHistory <B: Basics, C: Column> {
  current_data: AtomicField <B, C>,
  old_data: BackBiasedTreeDeque <AtomicField <B, C>>,

  // TODO: how often are these empty? Should we try to optimize size_of <FieldHistory>?
  dependent_events: BackBiasedTreeDeque <Arc <EventRecord <B>>>,
  dependent_bounded_predictions: BackBiasedTreeDeque <Arc <Prediction <B>>>,
  dependent_unbounded_predictions: AtomicBag <Arc <Prediction <B>>>,
}

struct EventRecord <B: Basics> {
  time: ExtendedTime <B>,
  fields_changed: Box <[FieldId]>,
  checksum: u64,
  predicted_by: Option <Arc <Prediction <B>>>
}
struct Prediction <B: Basics> {
  made_after: Arc <EventRecord <B>>,

}

struct EventTask <B: Basics> (Arc <Prediction <B>>);
struct PredictionTask <B: Basics> (PredictorId, RowId);
enum Task {
  Event (EventTask <B>),
  Prediction (PredictionTask <B>),
  Nothing,
}

struct Core <B: Basics> {
  fields: ConcurrentHashMap <FieldId, FieldHistory <B>, BuildTrivialU64Hasher>,

  available_memory: AtomicUsize,

  memory_batch_size: usize,
  predictors: Predictors <B>,
}

struct Handle <B: Basics> {
  core: Arc <Core <B>>,

  /// Store a reserved quantity of memory locally in the handle,
  /// so that we don't have to make as many contested atomic operations
  /// on core.available_memory.
  local_memory: usize,
}

fn reserve_memory (handle: &mut Handle <B>, bytes: usize)->bool {
  if handle.local_memory >= bytes {
    handle.local_memory -= bytes;
    true
  }
  else {
    let mut available = handle.core.available_memory.load (Ordering::Relaxed);
    while available + handle.local_memory >= bytes {
      let seizure = min (available, bytes + handle.core.memory_batch_size);
      match handle.core.available_memory.compare_exchange (
        available, available - seizure, Ordering::Relaxed) {
        Ok (_) => {
          handle.local_memory = handle.local_memory + seizure - bytes;
          return true;
        }
        Err (previous) => {available = previous;},
      }
    }
    false
  }
}

fn free_memory<B: Basics> (handle: &mut Handle <B>, bytes: usize) {
  handle.local_memory += bytes;
  if handle.local_memory > handle.core.memory_batch_size*2 {
    handle.core.available_memory.fetch_add (handle.local_memory - handle.core.memory_batch_size, Ordering::Relaxed);
    handle.local_memory = handle.core.memory_batch_size;
  }
}

fn work<B: Basics>(handle: &mut Handle <B>, guard: &epoch::Guard) {
  garbage_collect(handle, guard);
  match handle.scheduler.pop() {
    Event (event) => try_event (handle, guard, event),
    Prediction (prediction) => try_prediction (handle, guard, prediction),
    Nothing => (),
  }
}

fn garbage_collect <B: Basics>(handle: &mut Handle <B>, guard: &epoch::Guard) {
  unimplemented!()
}

fn try_event<B: Basics>(handle: &mut Handle <B>, guard: &epoch::Guard, event: EventTask <B>) {
  let max_needed = max_event_memory_usage(event);
  if reserve_memory (handle, max_needed) {
    let memory_used = do_event (handle, guard, event);
    assert!(memory_used <= max_needed);
    free_memory (handle, max_needed - memory_used);
  }
}

fn try_prediction<B: Basics> (handle: &mut Handle <B>, guard: &epoch::Guard, prediction: PredictionTask <B>) {
  let max_needed = max_prediction_memory_usage(prediction);
  if reserve_memory (handle, max_needed) {
    let memory_used = do_prediction (handle, guard, prediction);
    assert!(memory_used <= max_needed);
    free_memory (handle, max_needed - memory_used);
  }
}

//fn max_event_memory_usage

fn do_event<B: Basics>(handle: &mut Handle <B>, guard: &epoch::Guard, event: EventTask <B>)->isize {
  let results = do_event_callback (handle, guard, event);
  let field_guards = Vec::with_capacity(results.fields.len());
  let mut modified_fields = 0;
  for (id, data) in results.fields {
    // Lock all fields for writing.
    // If we run into a field that is locked by another event,
    // just abort. Half the time, that means this event was invalidated anyway.
    // The other half, it's unfortunate, but the scheduler should try to optimize
    // for making that case be rare.
    let modified = data.new_value.last_change == event.time;
    if modified {modified_fields += 1;}
    match handle.core.fields.get (id).try_write() {
      Ok (field_guard) => {
        // Also, the field could have been modified AND UNLOCKED
        // in between when we read it and now.
        // So we need to check that it is consistent with the observed value.
        if modified && !field_options_are_equal (field_guard.value_before (event.time), data.old_value) {
          return 0;
        }
        field_guards.push ((id, data, field_guard));
      },
      Err (_) => return 0,
    }
  }

  // If we reach this point, the event is valid, and will remain valid
  // until the end of this function when we drop the guards,
  // so we're A-OK to make changes.
  let mut memory_used: isize = mem::size_of::<EventRecord>() + modified_fields*mem::size_of::<FieldId>();
  let modified_ids = Vec::with_capacity (modified_fields);
  let record: Arc<EventRecord> = ;
  for (id, data, field_guard) in field_guards {
    memory_used += field_guard.add_event_dependency (event.time);
    if data.new_value.last_change == event.time {
      modified_ids.push (id);
      memory_used += discard_field_data_before (handle, guard, field_guard, event.time);
      memory_used += push_field_data (handle, guard, field_guard, record.clone(), event.new_data)
    }
  }
  memory_used
}

fn do_prediction<B: Basics> (handle: &mut Handle <B>, guard: &epoch::Guard, prediction: PredictionTask <B>)->usize {
  unimplemented!()
}

<commit_msg>old uncomitted stuff<commit_after>use std::mem;
use std::sync::Arc;
use std::cmp::min;
use {FieldId, ColumnId, RowId, PredictorId, Basics, Column, ExtendedTime};
use crossbeam::mem::epoch::{self, Atomic};
use super::data_structures::{FieldsMap, BackBiasedTreeDeque, AtomicBag};

struct AtomicFieldInner <B: Basics, C: Column> {
  data: Option <C::FieldType>,
  when: Arc <EventRecord <B>>,
}

struct AtomicField <B: Basics, C: Column> {
  data: Atomic <AtomicFieldInner <B, C>>,

  #[cfg (debug_assertions)]
  column_id: ColumnId,
}

struct FieldHistory <B: Basics, C: Column> {
  current_data: AtomicField <B, C>,
  old_data: BackBiasedTreeDeque <AtomicField <B, C>>,

  // TODO: how often are these empty? Should we try to optimize size_of <FieldHistory>?
  dependent_events: BackBiasedTreeDeque <Arc <EventRecord <B>>>,
  dependent_bounded_predictions: BackBiasedTreeDeque <Arc <Prediction <B>>>,
  dependent_unbounded_predictions: AtomicBag <Arc <Prediction <B>>>,
}

struct EventRecord <B: Basics> {
  time: ExtendedTime <B>,
  fields_changed: Box <[FieldId]>,
  checksum: u64,
  predicted_by: Option <Arc <Prediction <B>>>
}
struct Prediction <B: Basics> {
  made_after: Arc <EventRecord <B>>,

}

struct EventTask <B: Basics> (Arc <Prediction <B>>);
struct PredictionTask <B: Basics> (PredictorId, RowId);
enum Task {
  Event (EventTask <B>),
  Prediction (PredictionTask <B>),
  Nothing,
}

struct Core <B: Basics> {
  fields: FieldsMap <FieldId, FieldHistory <B>, BuildTrivialU64Hasher>,

  available_memory: AtomicUsize,

  memory_batch_size: usize,
  predictors: Predictors <B>,
}

struct Handle <B: Basics> {
  core: Arc <Core <B>>,

  /// Store a reserved quantity of memory locally in the handle,
  /// so that we don't have to make as many contested atomic operations
  /// on core.available_memory.
  local_memory: usize,
}

fn reserve_memory (handle: &mut Handle <B>, bytes: usize)->bool {
  if handle.local_memory >= bytes {
    handle.local_memory -= bytes;
    true
  }
  else {
    let mut available = handle.core.available_memory.load (Ordering::Relaxed);
    while available + handle.local_memory >= bytes {
      let seizure = min (available, bytes + handle.core.memory_batch_size);
      match handle.core.available_memory.compare_exchange (
        available, available - seizure, Ordering::Relaxed) {
        Ok (_) => {
          handle.local_memory = handle.local_memory + seizure - bytes;
          return true;
        }
        Err (previous) => {available = previous;},
      }
    }
    false
  }
}

fn free_memory<B: Basics> (handle: &mut Handle <B>, bytes: usize) {
  handle.local_memory += bytes;
  if handle.local_memory > handle.core.memory_batch_size*2 {
    handle.core.available_memory.fetch_add (handle.local_memory - handle.core.memory_batch_size, Ordering::Relaxed);
    handle.local_memory = handle.core.memory_batch_size;
  }
}

fn work<B: Basics>(handle: &mut Handle <B>, guard: &epoch::Guard) {
  garbage_collect(handle, guard);
  match handle.scheduler.pop() {
    Event (event) => try_event (handle, guard, event),
    Prediction (prediction) => try_prediction (handle, guard, prediction),
    Nothing => (),
  }
}

fn garbage_collect <B: Basics>(handle: &mut Handle <B>, guard: &epoch::Guard) {
  unimplemented!()
}

fn try_event<B: Basics>(handle: &mut Handle <B>, guard: &epoch::Guard, event: EventTask <B>) {
  let max_needed = max_event_memory_usage(event);
  if reserve_memory (handle, max_needed) {
    let memory_used = do_event (handle, guard, event);
    assert!(memory_used <= max_needed);
    free_memory (handle, max_needed - memory_used);
  }
}

fn try_prediction<B: Basics> (handle: &mut Handle <B>, guard: &epoch::Guard, prediction: PredictionTask <B>) {
  let max_needed = max_prediction_memory_usage(prediction);
  if reserve_memory (handle, max_needed) {
    let memory_used = do_prediction (handle, guard, prediction);
    assert!(memory_used <= max_needed);
    free_memory (handle, max_needed - memory_used);
  }
}

//fn max_event_memory_usage

fn do_event<B: Basics>(handle: &mut Handle <B>, guard: &epoch::Guard, event: EventTask <B>)->isize {
  let results = do_event_callback (handle, guard, event);
  let field_guards = Vec::with_capacity(results.fields.len());
  let mut modified_fields = 0;
  for (id, data) in results.fields {
    // Lock all fields for writing.
    // If we run into a field that is locked by another event,
    // just abort. Half the time, that means this event was invalidated anyway.
    // The other half, it's unfortunate, but the scheduler should try to optimize
    // for making that case be rare.
    let modified = data.new_value.last_change == event.time;
    if modified {modified_fields += 1;}
    match handle.core.fields.get (id).try_write() {
      Ok (field_guard) => {
        // Also, the field could have been modified AND UNLOCKED
        // in between when we read it and now.
        // So we need to check that it is consistent with the observed value.
        if modified && !field_options_are_equal (field_guard.value_before (event.time), data.old_value) {
          return 0;
        }
        field_guards.push ((id, data, field_guard));
      },
      Err (_) => return 0,
    }
  }

  // If we reach this point, the event is valid, and will remain valid
  // until the end of this function when we drop the guards,
  // so we're A-OK to make changes.
  let mut memory_used: isize = mem::size_of::<EventRecord>() + modified_fields*mem::size_of::<FieldId>();
  let modified_ids = Vec::with_capacity (modified_fields);
  let record: Arc<EventRecord> = ;
  for (id, data, field_guard) in field_guards {
    memory_used += field_guard.add_event_dependency (event.time);
    if data.new_value.last_change == event.time {
      modified_ids.push (id);
      memory_used += discard_field_data_before (handle, guard, field_guard, event.time);
      memory_used += push_field_data (handle, guard, field_guard, record.clone(), event.new_data)
    }
  }
  memory_used
}

fn do_prediction<B: Basics> (handle: &mut Handle <B>, guard: &epoch::Guard, prediction: PredictionTask <B>)->usize {
  unimplemented!()
}

<|endoftext|>


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

//! Tidy check to ensure that unstable features are all in order
//!
//! This check will ensure properties like:
//!
//! * All stability attributes look reasonably well formed
//! * The set of library features is disjoint from the set of language features
//! * Library features have at most one stability level
//! * Library features have at most one `since` value
//! * All unstable lang features have tests to ensure they are actually unstable

use std::collections::HashMap;
use std::fmt;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

#[derive(Debug, PartialEq, Clone)]
pub enum Status {
    Stable,
    Removed,
    Unstable,
}

impl fmt::Display for Status {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let as_str = match *self {
            Status::Stable => "stable",
            Status::Unstable => "unstable",
            Status::Removed => "removed",
        };
        fmt::Display::fmt(as_str, f)
    }
}

#[derive(Debug, Clone)]
pub struct Feature {
    pub level: Status,
    pub since: String,
    pub has_gate_test: bool,
    pub tracking_issue: Option<u32>,
}

impl Feature {
    fn check_match(&self, other: &Feature)-> Result<(), Vec<&'static str>> {
        let mut mismatches = Vec::new();
        if self.level != other.level {
            mismatches.push("stability level");
        }
        if self.level == Status::Stable || other.level == Status::Stable {
            // As long as a feature is unstable, the since field tracks
            // when the given part of the feature has been implemented.
            // Mismatches are tolerable as features evolve and functionality
            // gets added.
            // Once a feature is stable, the since field tracks the first version
            // it was part of the stable distribution, and mismatches are disallowed.
            if self.since != other.since {
                mismatches.push("since");
            }
        }
        if self.tracking_issue != other.tracking_issue {
            mismatches.push("tracking issue");
        }
        if mismatches.is_empty() {
            Ok(())
        } else {
            Err(mismatches)
        }
    }
}

pub type Features = HashMap<String, Feature>;

pub fn check(path: &Path, bad: &mut bool, quiet: bool) {
    let mut features = collect_lang_features(path);
    assert!(!features.is_empty());

    let lib_features = get_and_check_lib_features(path, bad, &features);
    assert!(!lib_features.is_empty());

    let mut contents = String::new();

    super::walk_many(&[&path.join("test/compile-fail"),
                       &path.join("test/compile-fail-fulldeps"),
                       &path.join("test/parse-fail"),],
                     &mut |path| super::filter_dirs(path),
                     &mut |file| {
        let filename = file.file_name().unwrap().to_string_lossy();
        if !filename.ends_with(".rs") || filename == "features.rs" ||
           filename == "diagnostic_list.rs" {
            return;
        }

        let filen_underscore = filename.replace("-","_").replace(".rs","");
        let filename_is_gate_test = test_filen_gate(&filen_underscore, &mut features);

        contents.truncate(0);
        t!(t!(File::open(&file), &file).read_to_string(&mut contents));

        for (i, line) in contents.lines().enumerate() {
            let mut err = |msg: &str| {
                tidy_error!(bad, "{}:{}: {}", file.display(), i + 1, msg);
            };

            let gate_test_str = "gate-test-";

            if !line.contains(gate_test_str) {
                continue;
            }

            let feature_name = match line.find(gate_test_str) {
                Some(i) => {
                    &line[i+gate_test_str.len()..line[i+1..].find(' ').unwrap_or(line.len())]
                },
                None => continue,
            };
            match features.get_mut(feature_name) {
                Some(f) => {
                    if filename_is_gate_test {
                        err(&format!("The file is already marked as gate test \
                                      through its name, no need for a \
                                      'gate-test-{}' comment",
                                     feature_name));
                    }
                    f.has_gate_test = true;
                }
                None => {
                    err(&format!("gate-test test found referencing a nonexistent feature '{}'",
                                 feature_name));
                }
            }
        }
    });

    // Only check the number of lang features.
    // Obligatory testing for library features is dumb.
    let gate_untested = features.iter()
                                .filter(|&(_, f)| f.level == Status::Unstable)
                                .filter(|&(_, f)| !f.has_gate_test)
                                .collect::<Vec<_>>();

    for &(name, _) in gate_untested.iter() {
        println!("Expected a gate test for the feature '{}'.", name);
        println!("Hint: create a file named 'feature-gate-{}.rs' in the compile-fail\
                \n      test suite, with its failures due to missing usage of\
                \n      #![feature({})].", name, name);
        println!("Hint: If you already have such a test and don't want to rename it,\
                \n      you can also add a // gate-test-{} line to the test file.",
                 name);
    }

    if gate_untested.len() > 0 {
        tidy_error!(bad, "Found {} features without a gate test.", gate_untested.len());
    }

    if *bad {
        return;
    }
    if quiet {
        println!("* {} features", features.len());
        return;
    }

    let mut lines = Vec::new();
    for (name, feature) in features.iter() {
        lines.push(format!("{:<32} {:<8} {:<12} {:<8}",
                           name,
                           "lang",
                           feature.level,
                           feature.since));
    }
    for (name, feature) in lib_features {
        lines.push(format!("{:<32} {:<8} {:<12} {:<8}",
                           name,
                           "lib",
                           feature.level,
                           feature.since));
    }

    lines.sort();
    for line in lines {
        println!("* {}", line);
    }
}

fn find_attr_val<'a>(line: &'a str, attr: &str) -> Option<&'a str> {
    line.find(attr)
        .and_then(|i| line[i..].find('"').map(|j| i + j + 1))
        .and_then(|i| line[i..].find('"').map(|j| (i, i + j)))
        .map(|(i, j)| &line[i..j])
}

fn test_filen_gate(filen_underscore: &str, features: &mut Features) -> bool {
    if filen_underscore.starts_with("feature_gate") {
        for (n, f) in features.iter_mut() {
            if filen_underscore == format!("feature_gate_{}", n) {
                f.has_gate_test = true;
                return true;
            }
        }
    }
    return false;
}

pub fn collect_lang_features(base_src_path: &Path) -> Features {
    let mut contents = String::new();
    let path = base_src_path.join("libsyntax/feature_gate.rs");
    t!(t!(File::open(path)).read_to_string(&mut contents));

    contents.lines()
        .filter_map(|line| {
            let mut parts = line.trim().split(",");
            let level = match parts.next().map(|l| l.trim().trim_left_matches('(')) {
                Some("active") => Status::Unstable,
                Some("removed") => Status::Removed,
                Some("accepted") => Status::Stable,
                _ => return None,
            };
            let name = parts.next().unwrap().trim();
            let since = parts.next().unwrap().trim().trim_matches('"');
            let issue_str = parts.next().unwrap().trim();
            let tracking_issue = if issue_str.starts_with("None") {
                None
            } else {
                let s = issue_str.split("(").nth(1).unwrap().split(")").nth(0).unwrap();
                Some(s.parse().unwrap())
            };
            Some((name.to_owned(),
                Feature {
                    level,
                    since: since.to_owned(),
                    has_gate_test: false,
                    tracking_issue,
                }))
        })
        .collect()
}

pub fn collect_lib_features(base_src_path: &Path) -> Features {
    let mut lib_features = Features::new();

    // This library feature is defined in the `compiler_builtins` crate, which
    // has been moved out-of-tree. Now it can no longer be auto-discovered by
    // `tidy`, because we need to filter out its (submodule) directory. Manually
    // add it to the set of known library features so we can still generate docs.
    lib_features.insert("compiler_builtins_lib".to_owned(), Feature {
        level: Status::Unstable,
        since: "".to_owned(),
        has_gate_test: false,
        tracking_issue: None,
    });

    map_lib_features(base_src_path,
                     &mut |res, _, _| {
        match res {
            Ok((name, feature)) => {
                if lib_features.get(name).is_some() {
                    return;
                }
                lib_features.insert(name.to_owned(), feature);
            },
            Err(_) => (),
        }
    });
   lib_features
}

fn get_and_check_lib_features(base_src_path: &Path,
                              bad: &mut bool,
                              lang_features: &Features) -> Features {
    let mut lib_features = Features::new();
    map_lib_features(base_src_path,
                     &mut |res, file, line| {
            match res {
                Ok((name, f)) => {
                    let mut check_features = |f: &Feature, list: &Features, display: &str| {
                        if let Some(ref s) = list.get(name) {
                            if let Err(m) = (&f).check_match(s) {
                                tidy_error!(bad,
                                            "{}:{}: mismatches to {} in: {:?}",
                                            file.display(),
                                            line,
                                            display,
                                            &m);
                            }
                        }
                    };
                    check_features(&f, &lang_features, "corresponding lang feature");
                    check_features(&f, &lib_features, "previous");
                    lib_features.insert(name.to_owned(), f);
                },
                Err(msg) => {
                    tidy_error!(bad, "{}:{}: {}", file.display(), line, msg);
                },
            }

    });
    lib_features
}

fn map_lib_features(base_src_path: &Path,
                    mf: &mut FnMut(Result<(&str, Feature), &str>, &Path, usize)) {
    let mut contents = String::new();
    super::walk(base_src_path,
                &mut |path| super::filter_dirs(path) || path.ends_with("src/test"),
                &mut |file| {
        let filename = file.file_name().unwrap().to_string_lossy();
        if !filename.ends_with(".rs") || filename == "features.rs" ||
           filename == "diagnostic_list.rs" {
            return;
        }

        contents.truncate(0);
        t!(t!(File::open(&file), &file).read_to_string(&mut contents));

        let mut becoming_feature: Option<(String, Feature)> = None;
        for (i, line) in contents.lines().enumerate() {
            macro_rules! err {
                ($msg:expr) => {{
                    mf(Err($msg), file, i + 1);
                    continue;
                }};
            };
            if let Some((ref name, ref mut f)) = becoming_feature {
                if f.tracking_issue.is_none() {
                    f.tracking_issue = find_attr_val(line, "issue")
                    .map(|s| s.parse().unwrap());
                }
                if line.ends_with("]") {
                    mf(Ok((name, f.clone())), file, i + 1);
                } else if !line.ends_with(",") && !line.ends_with("\\") {
                    // We need to bail here because we might have missed the
                    // end of a stability attribute above because the "]"
                    // might not have been at the end of the line.
                    // We could then get into the very unfortunate situation that
                    // we continue parsing the file assuming the current stability
                    // attribute has not ended, and ignoring possible feature
                    // attributes in the process.
                    err!("malformed stability attribute");
                } else {
                    continue;
                }
            }
            becoming_feature = None;
            let level = if line.contains("[unstable(") {
                Status::Unstable
            } else if line.contains("[stable(") {
                Status::Stable
            } else {
                continue;
            };
            let feature_name = match find_attr_val(line, "feature") {
                Some(name) => name,
                None => err!("malformed stability attribute"),
            };
            let since = match find_attr_val(line, "since") {
                Some(name) => name,
                None if level == Status::Stable => {
                    err!("malformed stability attribute");
                }
                None => "None",
            };
            let tracking_issue = find_attr_val(line, "issue").map(|s| s.parse().unwrap());

            let feature = Feature {
                level,
                since: since.to_owned(),
                has_gate_test: false,
                tracking_issue,
            };
            if line.contains("]") {
                mf(Ok((feature_name, feature)), file, i + 1);
            } else {
                becoming_feature = Some((feature_name.to_owned(), feature));
            }
        }
    });
}
<commit_msg>Tidy: track rustc_const_unstable feature gates as well<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Tidy check to ensure that unstable features are all in order
//!
//! This check will ensure properties like:
//!
//! * All stability attributes look reasonably well formed
//! * The set of library features is disjoint from the set of language features
//! * Library features have at most one stability level
//! * Library features have at most one `since` value
//! * All unstable lang features have tests to ensure they are actually unstable

use std::collections::HashMap;
use std::fmt;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

#[derive(Debug, PartialEq, Clone)]
pub enum Status {
    Stable,
    Removed,
    Unstable,
}

impl fmt::Display for Status {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let as_str = match *self {
            Status::Stable => "stable",
            Status::Unstable => "unstable",
            Status::Removed => "removed",
        };
        fmt::Display::fmt(as_str, f)
    }
}

#[derive(Debug, Clone)]
pub struct Feature {
    pub level: Status,
    pub since: String,
    pub has_gate_test: bool,
    pub tracking_issue: Option<u32>,
}

impl Feature {
    fn check_match(&self, other: &Feature)-> Result<(), Vec<&'static str>> {
        let mut mismatches = Vec::new();
        if self.level != other.level {
            mismatches.push("stability level");
        }
        if self.level == Status::Stable || other.level == Status::Stable {
            // As long as a feature is unstable, the since field tracks
            // when the given part of the feature has been implemented.
            // Mismatches are tolerable as features evolve and functionality
            // gets added.
            // Once a feature is stable, the since field tracks the first version
            // it was part of the stable distribution, and mismatches are disallowed.
            if self.since != other.since {
                mismatches.push("since");
            }
        }
        if self.tracking_issue != other.tracking_issue {
            mismatches.push("tracking issue");
        }
        if mismatches.is_empty() {
            Ok(())
        } else {
            Err(mismatches)
        }
    }
}

pub type Features = HashMap<String, Feature>;

pub fn check(path: &Path, bad: &mut bool, quiet: bool) {
    let mut features = collect_lang_features(path);
    assert!(!features.is_empty());

    let lib_features = get_and_check_lib_features(path, bad, &features);
    assert!(!lib_features.is_empty());

    let mut contents = String::new();

    super::walk_many(&[&path.join("test/compile-fail"),
                       &path.join("test/compile-fail-fulldeps"),
                       &path.join("test/parse-fail"),],
                     &mut |path| super::filter_dirs(path),
                     &mut |file| {
        let filename = file.file_name().unwrap().to_string_lossy();
        if !filename.ends_with(".rs") || filename == "features.rs" ||
           filename == "diagnostic_list.rs" {
            return;
        }

        let filen_underscore = filename.replace("-","_").replace(".rs","");
        let filename_is_gate_test = test_filen_gate(&filen_underscore, &mut features);

        contents.truncate(0);
        t!(t!(File::open(&file), &file).read_to_string(&mut contents));

        for (i, line) in contents.lines().enumerate() {
            let mut err = |msg: &str| {
                tidy_error!(bad, "{}:{}: {}", file.display(), i + 1, msg);
            };

            let gate_test_str = "gate-test-";

            if !line.contains(gate_test_str) {
                continue;
            }

            let feature_name = match line.find(gate_test_str) {
                Some(i) => {
                    &line[i+gate_test_str.len()..line[i+1..].find(' ').unwrap_or(line.len())]
                },
                None => continue,
            };
            match features.get_mut(feature_name) {
                Some(f) => {
                    if filename_is_gate_test {
                        err(&format!("The file is already marked as gate test \
                                      through its name, no need for a \
                                      'gate-test-{}' comment",
                                     feature_name));
                    }
                    f.has_gate_test = true;
                }
                None => {
                    err(&format!("gate-test test found referencing a nonexistent feature '{}'",
                                 feature_name));
                }
            }
        }
    });

    // Only check the number of lang features.
    // Obligatory testing for library features is dumb.
    let gate_untested = features.iter()
                                .filter(|&(_, f)| f.level == Status::Unstable)
                                .filter(|&(_, f)| !f.has_gate_test)
                                .collect::<Vec<_>>();

    for &(name, _) in gate_untested.iter() {
        println!("Expected a gate test for the feature '{}'.", name);
        println!("Hint: create a file named 'feature-gate-{}.rs' in the compile-fail\
                \n      test suite, with its failures due to missing usage of\
                \n      #![feature({})].", name, name);
        println!("Hint: If you already have such a test and don't want to rename it,\
                \n      you can also add a // gate-test-{} line to the test file.",
                 name);
    }

    if gate_untested.len() > 0 {
        tidy_error!(bad, "Found {} features without a gate test.", gate_untested.len());
    }

    if *bad {
        return;
    }
    if quiet {
        println!("* {} features", features.len());
        return;
    }

    let mut lines = Vec::new();
    for (name, feature) in features.iter() {
        lines.push(format!("{:<32} {:<8} {:<12} {:<8}",
                           name,
                           "lang",
                           feature.level,
                           feature.since));
    }
    for (name, feature) in lib_features {
        lines.push(format!("{:<32} {:<8} {:<12} {:<8}",
                           name,
                           "lib",
                           feature.level,
                           feature.since));
    }

    lines.sort();
    for line in lines {
        println!("* {}", line);
    }
}

fn find_attr_val<'a>(line: &'a str, attr: &str) -> Option<&'a str> {
    line.find(attr)
        .and_then(|i| line[i..].find('"').map(|j| i + j + 1))
        .and_then(|i| line[i..].find('"').map(|j| (i, i + j)))
        .map(|(i, j)| &line[i..j])
}

fn test_filen_gate(filen_underscore: &str, features: &mut Features) -> bool {
    if filen_underscore.starts_with("feature_gate") {
        for (n, f) in features.iter_mut() {
            if filen_underscore == format!("feature_gate_{}", n) {
                f.has_gate_test = true;
                return true;
            }
        }
    }
    return false;
}

pub fn collect_lang_features(base_src_path: &Path) -> Features {
    let mut contents = String::new();
    let path = base_src_path.join("libsyntax/feature_gate.rs");
    t!(t!(File::open(path)).read_to_string(&mut contents));

    contents.lines()
        .filter_map(|line| {
            let mut parts = line.trim().split(",");
            let level = match parts.next().map(|l| l.trim().trim_left_matches('(')) {
                Some("active") => Status::Unstable,
                Some("removed") => Status::Removed,
                Some("accepted") => Status::Stable,
                _ => return None,
            };
            let name = parts.next().unwrap().trim();
            let since = parts.next().unwrap().trim().trim_matches('"');
            let issue_str = parts.next().unwrap().trim();
            let tracking_issue = if issue_str.starts_with("None") {
                None
            } else {
                let s = issue_str.split("(").nth(1).unwrap().split(")").nth(0).unwrap();
                Some(s.parse().unwrap())
            };
            Some((name.to_owned(),
                Feature {
                    level,
                    since: since.to_owned(),
                    has_gate_test: false,
                    tracking_issue,
                }))
        })
        .collect()
}

pub fn collect_lib_features(base_src_path: &Path) -> Features {
    let mut lib_features = Features::new();

    // This library feature is defined in the `compiler_builtins` crate, which
    // has been moved out-of-tree. Now it can no longer be auto-discovered by
    // `tidy`, because we need to filter out its (submodule) directory. Manually
    // add it to the set of known library features so we can still generate docs.
    lib_features.insert("compiler_builtins_lib".to_owned(), Feature {
        level: Status::Unstable,
        since: "".to_owned(),
        has_gate_test: false,
        tracking_issue: None,
    });

    map_lib_features(base_src_path,
                     &mut |res, _, _| {
        match res {
            Ok((name, feature)) => {
                if lib_features.get(name).is_some() {
                    return;
                }
                lib_features.insert(name.to_owned(), feature);
            },
            Err(_) => (),
        }
    });
   lib_features
}

fn get_and_check_lib_features(base_src_path: &Path,
                              bad: &mut bool,
                              lang_features: &Features) -> Features {
    let mut lib_features = Features::new();
    map_lib_features(base_src_path,
                     &mut |res, file, line| {
            match res {
                Ok((name, f)) => {
                    let mut check_features = |f: &Feature, list: &Features, display: &str| {
                        if let Some(ref s) = list.get(name) {
                            if let Err(m) = (&f).check_match(s) {
                                tidy_error!(bad,
                                            "{}:{}: mismatches to {} in: {:?}",
                                            file.display(),
                                            line,
                                            display,
                                            &m);
                            }
                        }
                    };
                    check_features(&f, &lang_features, "corresponding lang feature");
                    check_features(&f, &lib_features, "previous");
                    lib_features.insert(name.to_owned(), f);
                },
                Err(msg) => {
                    tidy_error!(bad, "{}:{}: {}", file.display(), line, msg);
                },
            }

    });
    lib_features
}

fn map_lib_features(base_src_path: &Path,
                    mf: &mut FnMut(Result<(&str, Feature), &str>, &Path, usize)) {
    let mut contents = String::new();
    super::walk(base_src_path,
                &mut |path| super::filter_dirs(path) || path.ends_with("src/test"),
                &mut |file| {
        let filename = file.file_name().unwrap().to_string_lossy();
        if !filename.ends_with(".rs") || filename == "features.rs" ||
           filename == "diagnostic_list.rs" {
            return;
        }

        contents.truncate(0);
        t!(t!(File::open(&file), &file).read_to_string(&mut contents));

        let mut becoming_feature: Option<(String, Feature)> = None;
        for (i, line) in contents.lines().enumerate() {
            macro_rules! err {
                ($msg:expr) => {{
                    mf(Err($msg), file, i + 1);
                    continue;
                }};
            };
            if let Some((ref name, ref mut f)) = becoming_feature {
                if f.tracking_issue.is_none() {
                    f.tracking_issue = find_attr_val(line, "issue")
                    .map(|s| s.parse().unwrap());
                }
                if line.ends_with("]") {
                    mf(Ok((name, f.clone())), file, i + 1);
                } else if !line.ends_with(",") && !line.ends_with("\\") {
                    // We need to bail here because we might have missed the
                    // end of a stability attribute above because the "]"
                    // might not have been at the end of the line.
                    // We could then get into the very unfortunate situation that
                    // we continue parsing the file assuming the current stability
                    // attribute has not ended, and ignoring possible feature
                    // attributes in the process.
                    err!("malformed stability attribute");
                } else {
                    continue;
                }
            }
            becoming_feature = None;
            if line.contains("rustc_const_unstable(") && line.contains("#[") {
                // const fn features are handled specially
                let feature_name = match find_attr_val(line, "feature") {
                    Some(name) => name,
                    None => err!("malformed stability attribute"),
                };
                let feature = Feature {
                    level: Status::Unstable,
                    since: "None".to_owned(),
                    has_gate_test: false,
                    // Whether there is a common tracking issue
                    // for these feature gates remains an open question
                    // https://github.com/rust-lang/rust/issues/24111#issuecomment-340283184
                    // But we take 24111 otherwise they will be shown as
                    // "internal to the compiler" which they are not.
                    tracking_issue: Some(24111),
                };
                mf(Ok((feature_name, feature)), file, i + 1);
                continue;
            }
            let level = if line.contains("[unstable(") {
                Status::Unstable
            } else if line.contains("[stable(") {
                Status::Stable
            } else {
                continue;
            };
            let feature_name = match find_attr_val(line, "feature") {
                Some(name) => name,
                None => err!("malformed stability attribute"),
            };
            let since = match find_attr_val(line, "since") {
                Some(name) => name,
                None if level == Status::Stable => {
                    err!("malformed stability attribute");
                }
                None => "None",
            };
            let tracking_issue = find_attr_val(line, "issue").map(|s| s.parse().unwrap());

            let feature = Feature {
                level,
                since: since.to_owned(),
                has_gate_test: false,
                tracking_issue,
            };
            if line.contains("]") {
                mf(Ok((feature_name, feature)), file, i + 1);
            } else {
                becoming_feature = Some((feature_name.to_owned(), feature));
            }
        }
    });
}
<|endoftext|>


<commit_before>use std::ptr;

use ::util::{PackedStr, pack_str, unpack_str, write_fixed_leb16_at_idx, write_fixed_leb32_at_idx, write_leb_u32};
use ::wasm_opcodes as op;

pub const FN0_TYPE_INDEX: u8 = 0;
pub const FN1_TYPE_INDEX: u8 = 1;
pub const FN2_TYPE_INDEX: u8 = 2;
pub const FN3_TYPE_INDEX: u8 = 3;

pub const FN0_RET_TYPE_INDEX: u8 = 4;
pub const FN1_RET_TYPE_INDEX: u8 = 5;

#[allow(dead_code)]
pub const FN2_RET_TYPE_INDEX: u8 = 6;

pub const NR_FN_TYPE_INDEXES: u8 = 7;

static mut MODULE_PTR: *mut WasmBuilder = ptr::null_mut();

#[no_mangle]
pub fn setup() {
    let wm = Box::new(WasmBuilder::new());
    unsafe {
        MODULE_PTR = Box::into_raw(wm);
    }
    get_module().init();
}

#[no_mangle]
pub fn get_module<'a>() -> &'a mut WasmBuilder {
    unsafe {
        MODULE_PTR.as_mut().expect("getting module")
    }
}

pub struct WasmBuilder {
    pub op: Vec<u8>,
    pub cs: Vec<u8>,
    pub instruction_body: Vec<u8>,

    idx_import_table_size: usize, // for rewriting once finished
    idx_import_count: usize, // for rewriting once finished
    idx_import_entries: usize, // for searching the imports

    import_table_size: usize, // the current import table size (to avoid reading 2 byte leb)
    import_count: u16, // same as above

    initial_static_size: usize, // size of module after initialization, rest is drained on reset
}

impl WasmBuilder {
    pub fn new() -> Self {
        WasmBuilder {
            op: Vec::with_capacity(256),
            cs: Vec::with_capacity(256),
            instruction_body: Vec::with_capacity(256),

            idx_import_table_size: 0,
            idx_import_count: 0,
            idx_import_entries: 0,

            import_table_size: 2,
            import_count: 0,

            initial_static_size: 0,
        }
    }

    pub fn init(&mut self) {
        self.op.extend("\0asm".as_bytes());

        // wasm version in leb128, 4 bytes
        self.op.push(op::WASM_VERSION); self.op.push(0); self.op.push(0); self.op.push(0);

        self.write_type_section();
        self.write_import_section_preamble();

        // store state of current pointers etc. so we can reset them later
        self.initial_static_size = self.op.len();
    }

    pub fn reset(&mut self) {
        self.op.drain(self.initial_static_size..);
        self.set_import_table_size(2);
        self.set_import_count(0);
        self.cs.clear();
        self.instruction_body.clear();
    }

    pub fn finish(&mut self, no_of_locals_i32: u8) -> usize {
        self.write_memory_import();
        self.write_function_section(1);
        self.write_export_section();

        // write code section preamble
        self.op.push(op::SC_CODE);

        let idx_code_section_size = self.op.len(); // we will write to this location later
        self.op.push(0); self.op.push(0); // write temp val for now using 4 bytes
        self.op.push(0); self.op.push(0);

        self.op.push(1); // number of function bodies: just 1

        // same as above but for body size of the function
        let idx_fn_body_size = self.op.len();
        self.op.push(0); self.op.push(0);
        self.op.push(0); self.op.push(0);

        self.op.push(1); // count of local blocks
        dbg_assert!(no_of_locals_i32 < 128);
        self.op.push(no_of_locals_i32); self.op.push(op::TYPE_I32);

        self.op.append(&mut self.cs);

        self.op.push(op::OP_END);

        // write the actual sizes to the pointer locations stored above. We subtract 4 from the actual
        // value because the ptr itself points to four bytes
        let fn_body_size = (self.op.len() - idx_fn_body_size - 4) as u32;
        write_fixed_leb32_at_idx(&mut self.op, idx_fn_body_size, fn_body_size);

        let code_section_size = (self.op.len() - idx_code_section_size - 4) as u32;
        write_fixed_leb32_at_idx(&mut self.op, idx_code_section_size, code_section_size);

        self.op.len()
    }

    pub fn write_type_section(&mut self) {
        self.op.push(op::SC_TYPE);

        let idx_section_size = self.op.len();
        self.op.push(0);

        self.op.push(NR_FN_TYPE_INDEXES); // number of type descriptors

        // FN0
        self.op.push(op::TYPE_FUNC);
        self.op.push(0); // no args
        self.op.push(0); // no return val

        // FN1
        self.op.push(op::TYPE_FUNC);
        self.op.push(1);
        self.op.push(op::TYPE_I32);
        self.op.push(0);

        // FN2
        self.op.push(op::TYPE_FUNC);
        self.op.push(2);
        self.op.push(op::TYPE_I32);
        self.op.push(op::TYPE_I32);
        self.op.push(0);

        // FN3
        self.op.push(op::TYPE_FUNC);
        self.op.push(3);
        self.op.push(op::TYPE_I32);
        self.op.push(op::TYPE_I32);
        self.op.push(op::TYPE_I32);
        self.op.push(0);

        // FN0_RET
        self.op.push(op::TYPE_FUNC);
        self.op.push(0);
        self.op.push(1);
        self.op.push(op::TYPE_I32);

        // FN1_RET
        self.op.push(op::TYPE_FUNC);
        self.op.push(1);
        self.op.push(op::TYPE_I32);
        self.op.push(1);
        self.op.push(op::TYPE_I32);

        // FN2_RET
        self.op.push(op::TYPE_FUNC);
        self.op.push(2);
        self.op.push(op::TYPE_I32);
        self.op.push(op::TYPE_I32);
        self.op.push(1);
        self.op.push(op::TYPE_I32);

        let new_len = self.op.len();
        let size = (new_len - 1) - idx_section_size;
        self.op[idx_section_size] = size as u8;
    }

    /// Goes over the import block to find index of an import entry by function name
    pub fn get_import_index(&self, fn_name: PackedStr) -> Option<u16> {
        let fn_name = unpack_str(fn_name);
        let mut offset = self.idx_import_entries;
        for i in 0..self.import_count {
            offset += 1; // skip length of module name
            offset += 1; // skip module name itself
            let len = self.op[offset] as usize;
            offset += 1;
            let name = self.op.get(offset..(offset + len)).expect("get function name");
            if name == fn_name.as_bytes() {
                return Some(i);
            }
            offset += len; // skip the string
            offset += 1; // skip import kind
            offset += 1; // skip type index
        }
        None
    }

    pub fn set_import_count(&mut self, count: u16) {
        dbg_assert!(count < 0x4000);
        self.import_count = count;
        let idx_import_count = self.idx_import_count;
        write_fixed_leb16_at_idx(&mut self.op, idx_import_count, count);
    }

    pub fn set_import_table_size(&mut self, size: usize) {
        dbg_assert!(size < 0x4000);
        self.import_table_size = size;
        let idx_import_table_size = self.idx_import_table_size;
        write_fixed_leb16_at_idx(&mut self.op, idx_import_table_size, size as u16);
    }

    pub fn write_import_section_preamble(&mut self) {
        self.op.push(op::SC_IMPORT);

        self.idx_import_table_size = self.op.len();
        self.op.push(1 | 0b10000000); self.op.push(2); // 2 in 2 byte leb

        self.idx_import_count = self.op.len();
        self.op.push(1 | 0b10000000); self.op.push(0); // 0 in 2 byte leb

        // here after starts the actual list of imports
        self.idx_import_entries = self.op.len();
    }

    pub fn write_memory_import(&mut self) {
        self.op.push(1);
        self.op.push('e' as u8);
        self.op.push(1);
        self.op.push('m' as u8);

        self.op.push(op::EXT_MEMORY);

        self.op.push(0); // memory flag, 0 for no maximum memory limit present
        write_leb_u32(&mut self.op, 256); // initial memory length of 256 pages, takes 2 bytes in leb128

        let new_import_count = self.import_count + 1;
        self.set_import_count(new_import_count);

        let new_table_size = self.import_table_size + 8;
        self.set_import_table_size(new_table_size);
    }

    pub fn write_import_entry(&mut self, fn_name: PackedStr, type_index: u8) -> u16 {
        self.op.push(1); // length of module name
        self.op.push('e' as u8); // module name
        let fn_name = unpack_str(fn_name);
        self.op.push(fn_name.len() as u8);
        self.op.extend(fn_name.as_bytes());
        self.op.push(op::EXT_FUNCTION);
        self.op.push(type_index);

        let new_import_count = self.import_count + 1;
        self.set_import_count(new_import_count);

        let new_table_size = self.import_table_size + 1 + 1 + 1 + fn_name.len() + 1 + 1;
        self.set_import_table_size(new_table_size);

        self.import_count - 1
    }

    pub fn write_function_section(&mut self, count: u8) {
        self.op.push(op::SC_FUNCTION);
        self.op.push(1 + count); // length of this section
        self.op.push(count); // count of signature indices
        for _ in 0..count {
            self.op.push(FN1_TYPE_INDEX);
        }
    }

    pub fn write_export_section(&mut self) {
        self.op.push(op::SC_EXPORT);
        self.op.push(1 + 1 + 1 + 1 + 2); // size of this section
        self.op.push(1); // count of table: just one function exported

        self.op.push(1); // length of exported function name
        self.op.push('f' as u8); // function name
        self.op.push(op::EXT_FUNCTION);

        // index of the exported function
        // function space starts with imports. index of last import is import count - 1
        // the last import however is a memory, so we subtract one from that
        let next_op_idx = self.op.len();
        self.op.push(0); self.op.push(0); // add 2 bytes for writing 16 byte val
        write_fixed_leb16_at_idx(&mut self.op, next_op_idx, self.import_count - 1);
    }

    pub fn get_fn_index(&mut self, fn_name: PackedStr, type_index: u8) -> u16 {
        dbg_log!("getting fn idx for '{}'", unpack_str(fn_name));
        match self.get_import_index(fn_name) {
            Some(idx) => {
                dbg_log!("found existing entry at idx {}", idx);
                idx
            },
            None => {
                let idx = self.write_import_entry(fn_name, type_index);
                dbg_log!("wrote new import entry at idx {}", idx);
                idx
            },
        }
    }

    pub fn get_op_ptr(&self) -> *const u8 {
        self.op.as_ptr()
    }

    pub fn get_op_len(&self) -> usize {
        self.op.len()
    }

    pub fn commit_instruction_body_cs(&mut self) {
        self.cs.append(&mut self.instruction_body);
    }

}

#[cfg(test)]
mod tests {
    use ::module_init::*;

    #[test]
    fn import_table_management() {
        let mut w = WasmBuilder::new();
        w.init();
        assert_eq!(0, w.get_fn_index(pack_str("foo"), FN0_TYPE_INDEX));
        assert_eq!(1, w.get_fn_index(pack_str("bar"), FN1_TYPE_INDEX));
        assert_eq!(0, w.get_fn_index(pack_str("foo"), FN0_TYPE_INDEX));
        assert_eq!(2, w.get_fn_index(pack_str("baz"), FN2_TYPE_INDEX));
    }

}
<commit_msg>use NonNull instead of plain ptr::null_mut<commit_after>use std::ptr::{NonNull, null_mut};

use ::util::{PackedStr, pack_str, unpack_str, write_fixed_leb16_at_idx, write_fixed_leb32_at_idx, write_leb_u32};
use ::wasm_opcodes as op;

pub const FN0_TYPE_INDEX: u8 = 0;
pub const FN1_TYPE_INDEX: u8 = 1;
pub const FN2_TYPE_INDEX: u8 = 2;
pub const FN3_TYPE_INDEX: u8 = 3;

pub const FN0_RET_TYPE_INDEX: u8 = 4;
pub const FN1_RET_TYPE_INDEX: u8 = 5;

#[allow(dead_code)]
pub const FN2_RET_TYPE_INDEX: u8 = 6;

pub const NR_FN_TYPE_INDEXES: u8 = 7;

static mut MODULE_PTR: NonNull<WasmBuilder> = unsafe { NonNull::new_unchecked(null_mut()) };

#[no_mangle]
pub fn setup() {
    let wm = Box::new(WasmBuilder::new());
    unsafe {
        MODULE_PTR = NonNull::new(Box::into_raw(wm)).expect("assigning module ptr");
    }
    get_module().init();
}

#[no_mangle]
pub fn get_module<'a>() -> &'a mut WasmBuilder {
    unsafe {
        MODULE_PTR.as_mut()
    }
}

pub struct WasmBuilder {
    pub op: Vec<u8>,
    pub cs: Vec<u8>,
    pub instruction_body: Vec<u8>,

    idx_import_table_size: usize, // for rewriting once finished
    idx_import_count: usize, // for rewriting once finished
    idx_import_entries: usize, // for searching the imports

    import_table_size: usize, // the current import table size (to avoid reading 2 byte leb)
    import_count: u16, // same as above

    initial_static_size: usize, // size of module after initialization, rest is drained on reset
}

impl WasmBuilder {
    pub fn new() -> Self {
        WasmBuilder {
            op: Vec::with_capacity(256),
            cs: Vec::with_capacity(256),
            instruction_body: Vec::with_capacity(256),

            idx_import_table_size: 0,
            idx_import_count: 0,
            idx_import_entries: 0,

            import_table_size: 2,
            import_count: 0,

            initial_static_size: 0,
        }
    }

    pub fn init(&mut self) {
        self.op.extend("\0asm".as_bytes());

        // wasm version in leb128, 4 bytes
        self.op.push(op::WASM_VERSION); self.op.push(0); self.op.push(0); self.op.push(0);

        self.write_type_section();
        self.write_import_section_preamble();

        // store state of current pointers etc. so we can reset them later
        self.initial_static_size = self.op.len();
    }

    pub fn reset(&mut self) {
        self.op.drain(self.initial_static_size..);
        self.set_import_table_size(2);
        self.set_import_count(0);
        self.cs.clear();
        self.instruction_body.clear();
    }

    pub fn finish(&mut self, no_of_locals_i32: u8) -> usize {
        self.write_memory_import();
        self.write_function_section(1);
        self.write_export_section();

        // write code section preamble
        self.op.push(op::SC_CODE);

        let idx_code_section_size = self.op.len(); // we will write to this location later
        self.op.push(0); self.op.push(0); // write temp val for now using 4 bytes
        self.op.push(0); self.op.push(0);

        self.op.push(1); // number of function bodies: just 1

        // same as above but for body size of the function
        let idx_fn_body_size = self.op.len();
        self.op.push(0); self.op.push(0);
        self.op.push(0); self.op.push(0);

        self.op.push(1); // count of local blocks
        dbg_assert!(no_of_locals_i32 < 128);
        self.op.push(no_of_locals_i32); self.op.push(op::TYPE_I32);

        self.op.append(&mut self.cs);

        self.op.push(op::OP_END);

        // write the actual sizes to the pointer locations stored above. We subtract 4 from the actual
        // value because the ptr itself points to four bytes
        let fn_body_size = (self.op.len() - idx_fn_body_size - 4) as u32;
        write_fixed_leb32_at_idx(&mut self.op, idx_fn_body_size, fn_body_size);

        let code_section_size = (self.op.len() - idx_code_section_size - 4) as u32;
        write_fixed_leb32_at_idx(&mut self.op, idx_code_section_size, code_section_size);

        self.op.len()
    }

    pub fn write_type_section(&mut self) {
        self.op.push(op::SC_TYPE);

        let idx_section_size = self.op.len();
        self.op.push(0);

        self.op.push(NR_FN_TYPE_INDEXES); // number of type descriptors

        // FN0
        self.op.push(op::TYPE_FUNC);
        self.op.push(0); // no args
        self.op.push(0); // no return val

        // FN1
        self.op.push(op::TYPE_FUNC);
        self.op.push(1);
        self.op.push(op::TYPE_I32);
        self.op.push(0);

        // FN2
        self.op.push(op::TYPE_FUNC);
        self.op.push(2);
        self.op.push(op::TYPE_I32);
        self.op.push(op::TYPE_I32);
        self.op.push(0);

        // FN3
        self.op.push(op::TYPE_FUNC);
        self.op.push(3);
        self.op.push(op::TYPE_I32);
        self.op.push(op::TYPE_I32);
        self.op.push(op::TYPE_I32);
        self.op.push(0);

        // FN0_RET
        self.op.push(op::TYPE_FUNC);
        self.op.push(0);
        self.op.push(1);
        self.op.push(op::TYPE_I32);

        // FN1_RET
        self.op.push(op::TYPE_FUNC);
        self.op.push(1);
        self.op.push(op::TYPE_I32);
        self.op.push(1);
        self.op.push(op::TYPE_I32);

        // FN2_RET
        self.op.push(op::TYPE_FUNC);
        self.op.push(2);
        self.op.push(op::TYPE_I32);
        self.op.push(op::TYPE_I32);
        self.op.push(1);
        self.op.push(op::TYPE_I32);

        let new_len = self.op.len();
        let size = (new_len - 1) - idx_section_size;
        self.op[idx_section_size] = size as u8;
    }

    /// Goes over the import block to find index of an import entry by function name
    pub fn get_import_index(&self, fn_name: PackedStr) -> Option<u16> {
        let fn_name = unpack_str(fn_name);
        let mut offset = self.idx_import_entries;
        for i in 0..self.import_count {
            offset += 1; // skip length of module name
            offset += 1; // skip module name itself
            let len = self.op[offset] as usize;
            offset += 1;
            let name = self.op.get(offset..(offset + len)).expect("get function name");
            if name == fn_name.as_bytes() {
                return Some(i);
            }
            offset += len; // skip the string
            offset += 1; // skip import kind
            offset += 1; // skip type index
        }
        None
    }

    pub fn set_import_count(&mut self, count: u16) {
        dbg_assert!(count < 0x4000);
        self.import_count = count;
        let idx_import_count = self.idx_import_count;
        write_fixed_leb16_at_idx(&mut self.op, idx_import_count, count);
    }

    pub fn set_import_table_size(&mut self, size: usize) {
        dbg_assert!(size < 0x4000);
        self.import_table_size = size;
        let idx_import_table_size = self.idx_import_table_size;
        write_fixed_leb16_at_idx(&mut self.op, idx_import_table_size, size as u16);
    }

    pub fn write_import_section_preamble(&mut self) {
        self.op.push(op::SC_IMPORT);

        self.idx_import_table_size = self.op.len();
        self.op.push(1 | 0b10000000); self.op.push(2); // 2 in 2 byte leb

        self.idx_import_count = self.op.len();
        self.op.push(1 | 0b10000000); self.op.push(0); // 0 in 2 byte leb

        // here after starts the actual list of imports
        self.idx_import_entries = self.op.len();
    }

    pub fn write_memory_import(&mut self) {
        self.op.push(1);
        self.op.push('e' as u8);
        self.op.push(1);
        self.op.push('m' as u8);

        self.op.push(op::EXT_MEMORY);

        self.op.push(0); // memory flag, 0 for no maximum memory limit present
        write_leb_u32(&mut self.op, 256); // initial memory length of 256 pages, takes 2 bytes in leb128

        let new_import_count = self.import_count + 1;
        self.set_import_count(new_import_count);

        let new_table_size = self.import_table_size + 8;
        self.set_import_table_size(new_table_size);
    }

    pub fn write_import_entry(&mut self, fn_name: PackedStr, type_index: u8) -> u16 {
        self.op.push(1); // length of module name
        self.op.push('e' as u8); // module name
        let fn_name = unpack_str(fn_name);
        self.op.push(fn_name.len() as u8);
        self.op.extend(fn_name.as_bytes());
        self.op.push(op::EXT_FUNCTION);
        self.op.push(type_index);

        let new_import_count = self.import_count + 1;
        self.set_import_count(new_import_count);

        let new_table_size = self.import_table_size + 1 + 1 + 1 + fn_name.len() + 1 + 1;
        self.set_import_table_size(new_table_size);

        self.import_count - 1
    }

    pub fn write_function_section(&mut self, count: u8) {
        self.op.push(op::SC_FUNCTION);
        self.op.push(1 + count); // length of this section
        self.op.push(count); // count of signature indices
        for _ in 0..count {
            self.op.push(FN1_TYPE_INDEX);
        }
    }

    pub fn write_export_section(&mut self) {
        self.op.push(op::SC_EXPORT);
        self.op.push(1 + 1 + 1 + 1 + 2); // size of this section
        self.op.push(1); // count of table: just one function exported

        self.op.push(1); // length of exported function name
        self.op.push('f' as u8); // function name
        self.op.push(op::EXT_FUNCTION);

        // index of the exported function
        // function space starts with imports. index of last import is import count - 1
        // the last import however is a memory, so we subtract one from that
        let next_op_idx = self.op.len();
        self.op.push(0); self.op.push(0); // add 2 bytes for writing 16 byte val
        write_fixed_leb16_at_idx(&mut self.op, next_op_idx, self.import_count - 1);
    }

    pub fn get_fn_index(&mut self, fn_name: PackedStr, type_index: u8) -> u16 {
        dbg_log!("getting fn idx for '{}'", unpack_str(fn_name));
        match self.get_import_index(fn_name) {
            Some(idx) => {
                dbg_log!("found existing entry at idx {}", idx);
                idx
            },
            None => {
                let idx = self.write_import_entry(fn_name, type_index);
                dbg_log!("wrote new import entry at idx {}", idx);
                idx
            },
        }
    }

    pub fn get_op_ptr(&self) -> *const u8 {
        self.op.as_ptr()
    }

    pub fn get_op_len(&self) -> usize {
        self.op.len()
    }

    pub fn commit_instruction_body_cs(&mut self) {
        self.cs.append(&mut self.instruction_body);
    }

}

#[cfg(test)]
mod tests {
    use ::module_init::*;

    #[test]
    fn import_table_management() {
        let mut w = WasmBuilder::new();
        w.init();
        assert_eq!(0, w.get_fn_index(pack_str("foo"), FN0_TYPE_INDEX));
        assert_eq!(1, w.get_fn_index(pack_str("bar"), FN1_TYPE_INDEX));
        assert_eq!(0, w.get_fn_index(pack_str("foo"), FN0_TYPE_INDEX));
        assert_eq!(2, w.get_fn_index(pack_str("baz"), FN2_TYPE_INDEX));
    }

}
<|endoftext|>


<commit_before>use std::io;
use byteorder::WriteBytesExt;
use super::{MessageType, ReadFrom, ReadError, WriteTo};
use super::messages::{AddEntry, EntryAdded, EntryExpired, RemoveEntry, SetWatchMode, ErrorMessage};

#[derive(Debug)]
pub enum Message {
    Ping,
    Pong,
    AddEntry(AddEntry),
    EntryAdded(EntryAdded),
    RemoveEntry(RemoveEntry),
    // `EntryRemoved` should also contain the entry's data. However, this requires changing
    // libradium, because the frontend does not block when adding or removing entries.
    EntryRemoved,
    EntryExpired(EntryExpired),
    SetWatchMode(SetWatchMode),
    Ok,
    // TODO: Error message must contain an error value
    Error(ErrorMessage),
}

impl Message {
    pub fn message_type(&self) -> MessageType {
        match self {
            &Message::Ping => MessageType::Ping,
            &Message::Pong => MessageType::Pong,
            &Message::AddEntry(..) => MessageType::AddEntry,
            &Message::EntryAdded(..) => MessageType::EntryAdded,
            &Message::RemoveEntry(..) => MessageType::RemoveEntry,
            &Message::EntryRemoved => MessageType::EntryRemoved,
            &Message::EntryExpired(..) => MessageType::EntryExpired,
            &Message::SetWatchMode(..) => MessageType::SetWatchMode,
            &Message::Ok => MessageType::Ok,
            &Message::Error(..) => MessageType::Error,
        }
    }

    pub fn is_command(&self) -> bool {
        self.message_type().is_command()
    }
}

impl ReadFrom for Message {
    fn read_from<R: io::Read>(source: &mut R) -> Result<Self, ReadError> {
        let msg_type = MessageType::read_from(source)?;

        match msg_type {
            MessageType::Ping => Ok(Message::Ping),
            MessageType::Pong => Ok(Message::Pong),
            MessageType::AddEntry => Ok(Message::AddEntry(AddEntry::read_from(source)?)),
            MessageType::EntryAdded => Ok(Message::EntryAdded(EntryAdded::read_from(source)?)),
            MessageType::RemoveEntry => Ok(Message::RemoveEntry(RemoveEntry::read_from(source)?)),
            MessageType::EntryRemoved => Ok(Message::EntryRemoved),
            MessageType::EntryExpired => Ok(Message::EntryExpired(EntryExpired::read_from(source)?)),
            MessageType::SetWatchMode => Ok(Message::SetWatchMode(SetWatchMode::read_from(source)?)),
            MessageType::Ok => Ok(Message::Ok),
            MessageType::Error => Ok(Message::Error(ErrorMessage::read_from(source)?)),
        }
    }
}

impl WriteTo for Message {
    fn write_to<W: io::Write>(&self, target: &mut W) -> io::Result<()> {
        target.write_u8(self.message_type().into())?;

        match self {
            &Message::Ping => Ok(()),
            &Message::Pong => Ok(()),
            &Message::AddEntry(ref msg) => msg.write_to(target),
            &Message::EntryAdded(ref msg) => msg.write_to(target),
            &Message::RemoveEntry(ref msg) => msg.write_to(target),
            &Message::EntryRemoved => Ok(()),
            &Message::EntryExpired(ref msg) => msg.write_to(target),
            &Message::SetWatchMode(ref msg) => msg.write_to(target),
            &Message::Ok => Ok(()),
            &Message::Error(ref msg) => msg.write_to(target),
        }
    }
}

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

    macro_rules! test_message {
        ($test:ident, $ty:ident) => {
            test_message!($test, Message::$ty, MessageType::$ty);
        };
        ($test:ident, $msg:expr, $ty:expr) => {
            #[test]
            fn $test() {
                let msg = $msg;

                assert_eq!($ty, msg.message_type());

                let mut vec = Vec::new();
                assert!(msg.write_to(&mut vec).is_ok());
                assert_eq!(msg.message_type().to_u8(), vec[0]);

                let read_msg = Message::read_from(&mut vec.as_mut_slice().as_ref()).unwrap();
                assert_eq!(msg.message_type(), read_msg.message_type());
            }
        };
    }

    test_message!(test_ping, Ping);
    test_message!(test_pong, Pong);

    test_message!(test_add_entry,
                  Message::AddEntry(AddEntry::new(0, vec![])),
                  MessageType::AddEntry);

    test_message!(test_entry_added,
                  Message::EntryAdded(EntryAdded::new(0, 0)),
                  MessageType::EntryAdded);

    test_message!(test_remove_entry,
                  Message::RemoveEntry(RemoveEntry::new(0, 0)),
                  MessageType::RemoveEntry);

    test_message!(test_entry_removed, EntryRemoved);

    test_message!(test_entry_expired,
                  Message::EntryExpired(EntryExpired::new(0, 7, vec![])),
                  MessageType::EntryExpired);

    test_message!(test_ok, Ok);

    test_message!(test_set_watch_mode,
                  Message::SetWatchMode(SetWatchMode::new(WatchMode::None)),
                  MessageType::SetWatchMode);

    test_message!(test_error, Message::Error, MessageType::Error);
}<commit_msg>Fix tests<commit_after>use std::io;
use byteorder::WriteBytesExt;
use super::{MessageType, ReadFrom, ReadError, WriteTo};
use super::messages::{AddEntry, EntryAdded, EntryExpired, RemoveEntry, SetWatchMode, ErrorMessage};

#[derive(Debug)]
pub enum Message {
    Ping,
    Pong,
    AddEntry(AddEntry),
    EntryAdded(EntryAdded),
    RemoveEntry(RemoveEntry),
    // `EntryRemoved` should also contain the entry's data. However, this requires changing
    // libradium, because the frontend does not block when adding or removing entries.
    EntryRemoved,
    EntryExpired(EntryExpired),
    SetWatchMode(SetWatchMode),
    Ok,
    // TODO: Error message must contain an error value
    Error(ErrorMessage),
}

impl Message {
    pub fn message_type(&self) -> MessageType {
        match self {
            &Message::Ping => MessageType::Ping,
            &Message::Pong => MessageType::Pong,
            &Message::AddEntry(..) => MessageType::AddEntry,
            &Message::EntryAdded(..) => MessageType::EntryAdded,
            &Message::RemoveEntry(..) => MessageType::RemoveEntry,
            &Message::EntryRemoved => MessageType::EntryRemoved,
            &Message::EntryExpired(..) => MessageType::EntryExpired,
            &Message::SetWatchMode(..) => MessageType::SetWatchMode,
            &Message::Ok => MessageType::Ok,
            &Message::Error(..) => MessageType::Error,
        }
    }

    pub fn is_command(&self) -> bool {
        self.message_type().is_command()
    }
}

impl ReadFrom for Message {
    fn read_from<R: io::Read>(source: &mut R) -> Result<Self, ReadError> {
        let msg_type = MessageType::read_from(source)?;

        match msg_type {
            MessageType::Ping => Ok(Message::Ping),
            MessageType::Pong => Ok(Message::Pong),
            MessageType::AddEntry => Ok(Message::AddEntry(AddEntry::read_from(source)?)),
            MessageType::EntryAdded => Ok(Message::EntryAdded(EntryAdded::read_from(source)?)),
            MessageType::RemoveEntry => Ok(Message::RemoveEntry(RemoveEntry::read_from(source)?)),
            MessageType::EntryRemoved => Ok(Message::EntryRemoved),
            MessageType::EntryExpired => Ok(Message::EntryExpired(EntryExpired::read_from(source)?)),
            MessageType::SetWatchMode => Ok(Message::SetWatchMode(SetWatchMode::read_from(source)?)),
            MessageType::Ok => Ok(Message::Ok),
            MessageType::Error => Ok(Message::Error(ErrorMessage::read_from(source)?)),
        }
    }
}

impl WriteTo for Message {
    fn write_to<W: io::Write>(&self, target: &mut W) -> io::Result<()> {
        target.write_u8(self.message_type().into())?;

        match self {
            &Message::Ping => Ok(()),
            &Message::Pong => Ok(()),
            &Message::AddEntry(ref msg) => msg.write_to(target),
            &Message::EntryAdded(ref msg) => msg.write_to(target),
            &Message::RemoveEntry(ref msg) => msg.write_to(target),
            &Message::EntryRemoved => Ok(()),
            &Message::EntryExpired(ref msg) => msg.write_to(target),
            &Message::SetWatchMode(ref msg) => msg.write_to(target),
            &Message::Ok => Ok(()),
            &Message::Error(ref msg) => msg.write_to(target),
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use super::super::WatchMode;
    use super::super::messages::ErrorCode;

    macro_rules! test_message {
        ($test:ident, $ty:ident) => {
            test_message!($test, Message::$ty, MessageType::$ty);
        };
        ($test:ident, $msg:expr, $ty:expr) => {
            #[test]
            fn $test() {
                let msg = $msg;

                assert_eq!($ty, msg.message_type());

                let mut vec = Vec::new();
                assert!(msg.write_to(&mut vec).is_ok());
                assert_eq!(msg.message_type().to_u8(), vec[0]);

                let read_msg = Message::read_from(&mut vec.as_mut_slice().as_ref()).unwrap();
                assert_eq!(msg.message_type(), read_msg.message_type());
            }
        };
    }

    test_message!(test_ping, Ping);
    test_message!(test_pong, Pong);

    test_message!(test_add_entry,
                  Message::AddEntry(AddEntry::new(0, vec![])),
                  MessageType::AddEntry);

    test_message!(test_entry_added,
                  Message::EntryAdded(EntryAdded::new(0, 0)),
                  MessageType::EntryAdded);

    test_message!(test_remove_entry,
                  Message::RemoveEntry(RemoveEntry::new(0, 0)),
                  MessageType::RemoveEntry);

    test_message!(test_entry_removed, EntryRemoved);

    test_message!(test_entry_expired,
                  Message::EntryExpired(EntryExpired::new(0, 7, vec![])),
                  MessageType::EntryExpired);

    test_message!(test_ok, Ok);

    test_message!(test_set_watch_mode,
                  Message::SetWatchMode(SetWatchMode::new(WatchMode::None)),
                  MessageType::SetWatchMode);

    test_message!(test_error, Message::Error(ErrorMessage::new(ErrorCode::ClientRejected)), MessageType::Error);
}<|endoftext|>