<commit_before>use cgmath::{Aabb3, Point, Point3, Vector, Vector3};
use std::collections::hash_map;
use std::collections::HashMap;
use std::sync::Mutex;
use stopwatch::TimerSet;

use common::block_position::BlockPosition;
use common::entity::EntityId;
use common::id_allocator::IdAllocator;
use common::lod::LODIndex;
use common::terrain_block;
use common::terrain_block::{TerrainBlock, tri};

use terrain::heightmap;
use terrain;
use voxel;

pub fn generate_voxel(
  timers: &TimerSet,
  heightmap: &heightmap::T,
  voxel: &::voxel::Bounds,
) -> terrain::voxel::T
{
  timers.time("generate_voxel", || {
    let size = voxel.size();
    let (low, high) = voxel.corners();
    macro_rules! contained(($x:expr, $y:expr, $z:expr) => {{
      voxel::field::T::contains(heightmap, &Point3::new($x.x, $y.y, $z.z))
    }});
    // corners[x][y][z]
    let corners = [
      [
        [ contained!( low,  low, low), contained!( low,  low, high) ],
        [ contained!( low, high, low), contained!( low, high, high) ],
      ],
      [
        [ contained!(high,  low, low), contained!(high,  low, high) ],
        [ contained!(high, high, low), contained!(high, high, high) ],
      ],
    ];

    let corner;
    let mut any_inside = false;
    let mut all_inside = true;

    {
      let mut get_corner = |x1:usize, y1:usize, z1:usize| {
        let corner = corners[x1][y1][z1];
        any_inside = any_inside || corner;
        all_inside = all_inside && corner;
        corner
      };

      corner = get_corner(0,0,0);
      let _ = get_corner(0,0,1);
      let _ = get_corner(0,1,0);
      let _ = get_corner(0,1,1);
      let _ = get_corner(1,0,0);
      let _ = get_corner(1,0,1);
      let _ = get_corner(1,1,0);
      let _ = get_corner(1,1,1);
    }

    let all_corners_same = any_inside == all_inside;
    if all_corners_same {
      return terrain::voxel::T::Volume(all_inside)
    }

    let mut vertex: Vector3<u32> = Vector3::new(0, 0, 0);
    let mut n = 0;
    for (&x, corners) in [0, 0xFF].iter().zip(corners.iter()) {
    for (&y, corners) in [0, 0xFF].iter().zip(corners.iter()) {
    for (&z, &corner) in [0, 0xFF].iter().zip(corners.iter()) {
      if corner {
        vertex.add_self_v(&Vector3::new(x, y, z));
        n += 1;
      }
    }}}

    {
      // Sample in extra areas to help weight the vertex to the appropriate place.
      let mut sample_extra = |lg_s: u8| {
        let fs = 1.0 / ((1 << lg_s) as f32);
        let mfs = 1.0 - fs;
        let s = 0x100 >> lg_s;
        let ms = 0x100 - s;
        for &(wx, x) in
          [((voxel.x as f32 + fs) * size, s), ((voxel.x as f32 + mfs) * size, ms)].iter() {
        for &(wy, y) in
          [((voxel.y as f32 + fs) * size, s), ((voxel.y as f32 + mfs) * size, ms)].iter() {
        for &(wz, z) in
          [((voxel.z as f32 + fs) * size, s), ((voxel.z as f32 + mfs) * size, ms)].iter() {
          if voxel::field::T::contains(heightmap, &Point3::new(wx, wy, wz)) {
            vertex.add_self_v(&Vector3::new(x, y, z).mul_s(lg_s as u32));
            n += lg_s as u32;
          }
        }}}
      };

      sample_extra(2);
    }

    let vertex = vertex.div_s(n);
    let vertex =
      terrain::voxel::Vertex {
        x: terrain::voxel::Fracu8::of(vertex.x as u8),
        y: terrain::voxel::Fracu8::of(vertex.y as u8),
        z: terrain::voxel::Fracu8::of(vertex.z as u8),
      };

    let normal;
    {
      // Okay, this is silly to have right after we construct the vertex.
      let vertex = vertex.to_world_vertex(voxel);
      normal = terrain::voxel::Normal::of_float_normal(&voxel::field::T::normal_at(heightmap, 0.01, &vertex));
    }

    terrain::voxel::T::Surface(terrain::voxel::SurfaceStruct {
      inner_vertex: vertex,
      normal: normal,
      corner_inside_surface: corner,
    })
  })
}

#[inline]
fn partial_min<I, It>(mut it: It) -> Option<I>
where
  I: PartialOrd,
  It: Iterator<Item=I>,
{
  match it.next() {
    None => None,
    Some(mut best) => {
      for thing in it {
        if thing < best {
          best = thing;
        }
      }
      Some(best)
    },
  }
}

#[inline]
fn partial_max<I, It>(mut it: It) -> Option<I>
where
  I: PartialOrd,
  It: Iterator<Item=I>,
{
  match it.next() {
    None => None,
    Some(mut best) => {
      for thing in it {
        if thing > best {
          best = thing;
        }
      }
      Some(best)
    },
  }
}

fn make_bounds(
  v1: &Point3<f32>,
  v2: &Point3<f32>,
  v3: &Point3<f32>,
) -> Aabb3<f32> {
  let minx = *partial_min([v1.x, v2.x, v3.x].iter()).unwrap();
  let maxx = *partial_max([v1.x, v2.x, v3.x].iter()).unwrap();

  let miny = *partial_min([v1.y, v2.y, v3.y].iter()).unwrap();
  let maxy = *partial_max([v1.y, v2.y, v3.y].iter()).unwrap();

  let minz = *partial_min([v1.z, v2.z, v3.z].iter()).unwrap();
  let maxz = *partial_max([v1.z, v2.z, v3.z].iter()).unwrap();

  Aabb3::new(
    // TODO: Remove this - 1.0. It's a temporary hack until voxel collisions work,
    // to avoid zero-height Aabb3s.
    Point3::new(minx, miny - 1.0, minz),
    Point3::new(maxx, maxy, maxz),
  )
}

/// Generate a `TerrainBlock` based on a given position in a `terrain::voxel::tree::T`.
/// Any necessary voxels will be generated.
pub fn generate_block(
  timers: &TimerSet,
  id_allocator: &Mutex<IdAllocator<EntityId>>,
  heightmap: &heightmap::T,
  voxels: &mut terrain::voxel::tree::T,
  position: &BlockPosition,
  lod_index: LODIndex,
) -> TerrainBlock {
  timers.time("update.generate_block", || {
    let mut block = TerrainBlock::empty();

    let lateral_samples = terrain_block::EDGE_SAMPLES[lod_index.0 as usize] as i32;
    let lg_size = terrain_block::LG_SAMPLE_SIZE[lod_index.0 as usize] as i16;

    let bounds_at = |v: &Point3<i32>| {
      voxel::Bounds::new(v.x, v.y, v.z, lg_size)
    };

    let mut get_voxel = |bounds: &voxel::Bounds| {
      let branch = voxels.get_mut_or_create(bounds);
      let r;
      match branch {
        &mut terrain::voxel::tree::Empty => {
          r = generate_voxel(timers, heightmap, bounds);
          *branch =
            terrain::voxel::tree::Branch {
              data: Some(r),
              branches: Box::new(terrain::voxel::tree::Branches::empty()),
            };
        },
        &mut terrain::voxel::tree::Branch { ref mut data, branches: _ }  => {
          match data {
            &mut None => {
              r = generate_voxel(timers, heightmap, bounds);
              *data = Some(r);
            },
            &mut Some(ref data) => {
              r = *data;
            },
          }
        },
      };
      r
    };

    let lg_ratio = terrain_block::LG_WIDTH - lg_size;
    let block_position = position.as_pnt();
    let voxel_position =
      Point3::new(
        block_position.x << lg_ratio,
        block_position.y << lg_ratio,
        block_position.z << lg_ratio,
      );

    let mut coords = Vec::new();
    let mut normals = Vec::new();
    let mut indices = HashMap::new();
    let mut polys = Vec::new();

    for dx in 0..lateral_samples {
    for dy in 0..lateral_samples {
    for dz in 0..lateral_samples {
      let voxel_position = voxel_position.add_v(&Vector3::new(dx, dy, dz));
      let voxel;
      let bounds = bounds_at(&voxel_position);
      match get_voxel(&bounds) {
        terrain::voxel::T::Surface(v) => voxel = v,
        _ => continue,
      }
      let index = coords.len();
      let vertex = voxel.inner_vertex.to_world_vertex(&bounds);
      let normal = voxel.normal.to_float_normal();
      coords.push(vertex);
      normals.push(normal);
      indices.insert(voxel_position, index);

      let mut edge = |
        d_neighbor, // Vector to the neighbor to make an edge toward.
        d1, d2,     // Vector to the voxels adjacent to the edge.
      | {
        let neighbor_position = voxel_position.add_v(&d_neighbor);
        debug!("edge from {:?} to {:?}", voxel_position, neighbor_position);
        let neighbor_inside_surface;
        match get_voxel(&bounds_at(&neighbor_position)) {
          terrain::voxel::T::Surface(v) => neighbor_inside_surface = v.corner_inside_surface,
          terrain::voxel::T::Volume(inside) => neighbor_inside_surface = inside,
        }
        if voxel.corner_inside_surface == neighbor_inside_surface {
          // This edge doesn't cross the surface, and doesn't generate polys.

          return
        }

        let v1; let v2; let v3; let v4;
        let n1; let n2; let n3; let n4;
        let i1; let i2; let i3; let i4;

        {
          let mut voxel_index = |position: &Point3<i32>| {
            match indices.entry(*position) {
              hash_map::Entry::Occupied(entry) => {
                let i = *entry.get();
                (coords[i], normals[i], i)
              },
              hash_map::Entry::Vacant(entry) => {
                let bounds = bounds_at(position);
                match get_voxel(&bounds) {
                  terrain::voxel::T::Surface(voxel) => {
                    let i = coords.len();
                    let vertex = voxel.inner_vertex.to_world_vertex(&bounds);
                    let normal = voxel.normal.to_float_normal();
                    coords.push(vertex);
                    normals.push(normal);
                    entry.insert(i);
                    (vertex, normal, i)
                  },
                  voxel => {
                    panic!("Unexpected neighbor {:?} at {:?}", voxel, bounds)
                  }
                }
              },
            }
          };

          let (tv1, tn1, ti1) = voxel_index(&voxel_position.add_v(&d1).add_v(&d2));
          let (tv2, tn2, ti2) = voxel_index(&voxel_position.add_v(&d1));
          let (tv3, tn3, ti3) = (vertex, normal, index);
          let (tv4, tn4, ti4) = voxel_index(&voxel_position.add_v(&d2));

          v1 = tv1; v2 = tv2; v3 = tv3; v4 = tv4;
          n1 = tn1; n2 = tn2; n3 = tn3; n4 = tn4;
          i1 = ti1; i2 = ti2; i3 = ti3; i4 = ti4;
        }

        // Put a vertex at the average of the vertices.
        let v_center =
          v1.add_v(&v2.to_vec()).add_v(&v3.to_vec()).add_v(&v4.to_vec()).div_s(4.0);
        let n_center =
          n1.add_v(&n2).add_v(&n3).add_v(&n4).div_s(4.0);

        let i_center = coords.len();
        coords.push(v_center);
        normals.push(n_center);

        if voxel.corner_inside_surface {
          // The polys are visible from positive infinity.
          polys.push([i2, i1, i_center]);
          polys.push([i3, i2, i_center]);
          polys.push([i4, i3, i_center]);
          polys.push([i1, i4, i_center]);
        } else {
          // The polys are visible from negative infinity.
          polys.push([i1, i2, i_center]);
          polys.push([i2, i3, i_center]);
          polys.push([i3, i4, i_center]);
          polys.push([i4, i1, i_center]);
        }
      };

      edge(
        Vector3::new(1, 0, 0),
        Vector3::new(0, -1, 0),
        Vector3::new(0, 0, -1),
      );

      edge(
        Vector3::new(0, 1, 0),
        Vector3::new(0, 0, -1),
        Vector3::new(-1, 0, 0),
      );

      edge(
        Vector3::new(0, 0, 1),
        Vector3::new(-1, 0, 0),
        Vector3::new(0, -1, 0),
      );
    }}}

    for poly in polys.iter() {
      block.vertex_coordinates.push(tri(coords[poly[0]], coords[poly[1]], coords[poly[2]]));
      block.normals.push(tri(normals[poly[0]], normals[poly[1]], normals[poly[2]]));

      let id = id_allocator.lock().unwrap().allocate();
      block.ids.push(id);
      block.bounds.push((id, make_bounds(&coords[poly[0]], &coords[poly[1]], &coords[poly[2]])));
    }

    block
  })
}
<commit_msg>tweak vertex generation in terrain gen<commit_after>use cgmath::{Aabb3, Point, Point3, Vector, Vector3};
use std::collections::hash_map;
use std::collections::HashMap;
use std::f32;
use std::sync::Mutex;
use stopwatch::TimerSet;

use common::block_position::BlockPosition;
use common::entity::EntityId;
use common::id_allocator::IdAllocator;
use common::lod::LODIndex;
use common::terrain_block;
use common::terrain_block::{TerrainBlock, tri};

use terrain::heightmap;
use terrain;
use voxel;

pub fn generate_voxel(
  timers: &TimerSet,
  heightmap: &heightmap::T,
  voxel: &::voxel::Bounds,
) -> terrain::voxel::T
{
  timers.time("generate_voxel", || {
    let (low, high) = voxel.corners();
    macro_rules! density(($x:expr, $y:expr, $z:expr) => {{
      voxel::field::T::density_at(heightmap, &Point3::new($x.x, $y.y, $z.z))
    }});
    // corners[x][y][z]
    let mut corners = [
      [
        [ density!( low,  low, low), density!( low,  low, high) ],
        [ density!( low, high, low), density!( low, high, high) ],
      ],
      [
        [ density!(high,  low, low), density!(high,  low, high) ],
        [ density!(high, high, low), density!(high, high, high) ],
      ],
    ];

    let corner;
    let mut any_inside = false;
    let mut all_inside = true;

    {
      let mut get_corner = |x1:usize, y1:usize, z1:usize| {
        let corner = corners[x1][y1][z1] >= 0.0;
        corners[x1][y1][z1] = 1.0 / (corners[x1][y1][z1].abs() + f32::EPSILON);
        any_inside = any_inside || corner;
        all_inside = all_inside && corner;
        corner
      };

      corner = get_corner(0,0,0);
      let _ = get_corner(0,0,1);
      let _ = get_corner(0,1,0);
      let _ = get_corner(0,1,1);
      let _ = get_corner(1,0,0);
      let _ = get_corner(1,0,1);
      let _ = get_corner(1,1,0);
      let _ = get_corner(1,1,1);
    }

    let all_corners_same = any_inside == all_inside;
    if all_corners_same {
      return terrain::voxel::T::Volume(all_inside)
    }

    let corner_coords = [0.0, 256.0];
    let mut total_weight = 0.0;
    macro_rules! weighted(($x:expr, $y:expr, $z:expr) => {{
      total_weight += corners[$x][$y][$z];
      Vector3::new(corner_coords[$x], corner_coords[$y], corner_coords[$z])
        .mul_s(corners[$x][$y][$z])
    }});

    let vertex =
      weighted!(0, 0, 0) +
      weighted!(0, 0, 1) +
      weighted!(0, 1, 0) +
      weighted!(0, 1, 1) +
      weighted!(1, 0, 0) +
      weighted!(1, 0, 1) +
      weighted!(1, 1, 0) +
      weighted!(1, 1, 1) +
      Vector3::new(0.0, 0.0, 0.0);
    let vertex = Point3::from_vec(&vertex.div_s(total_weight));
    let vertex =
      terrain::voxel::Vertex {
        x: terrain::voxel::Fracu8::of(f32::min(vertex.x, 255.0) as u8),
        y: terrain::voxel::Fracu8::of(f32::min(vertex.y, 255.0) as u8),
        z: terrain::voxel::Fracu8::of(f32::min(vertex.z, 255.0) as u8),
      };

    let normal;
    {
      // Okay, this is silly to have right after we construct the vertex.
      let vertex = vertex.to_world_vertex(voxel);
      normal = terrain::voxel::Normal::of_float_normal(&voxel::field::T::normal_at(heightmap, 0.01, &vertex));
    }

    terrain::voxel::T::Surface(terrain::voxel::SurfaceStruct {
      inner_vertex: vertex,
      normal: normal,
      corner_inside_surface: corner,
    })
  })
}

#[inline]
fn partial_min<I, It>(mut it: It) -> Option<I>
where
  I: PartialOrd,
  It: Iterator<Item=I>,
{
  match it.next() {
    None => None,
    Some(mut best) => {
      for thing in it {
        if thing < best {
          best = thing;
        }
      }
      Some(best)
    },
  }
}

#[inline]
fn partial_max<I, It>(mut it: It) -> Option<I>
where
  I: PartialOrd,
  It: Iterator<Item=I>,
{
  match it.next() {
    None => None,
    Some(mut best) => {
      for thing in it {
        if thing > best {
          best = thing;
        }
      }
      Some(best)
    },
  }
}

fn make_bounds(
  v1: &Point3<f32>,
  v2: &Point3<f32>,
  v3: &Point3<f32>,
) -> Aabb3<f32> {
  let minx = *partial_min([v1.x, v2.x, v3.x].iter()).unwrap();
  let maxx = *partial_max([v1.x, v2.x, v3.x].iter()).unwrap();

  let miny = *partial_min([v1.y, v2.y, v3.y].iter()).unwrap();
  let maxy = *partial_max([v1.y, v2.y, v3.y].iter()).unwrap();

  let minz = *partial_min([v1.z, v2.z, v3.z].iter()).unwrap();
  let maxz = *partial_max([v1.z, v2.z, v3.z].iter()).unwrap();

  Aabb3::new(
    // TODO: Remove this - 1.0. It's a temporary hack until voxel collisions work,
    // to avoid zero-height Aabb3s.
    Point3::new(minx, miny - 1.0, minz),
    Point3::new(maxx, maxy, maxz),
  )
}

/// Generate a `TerrainBlock` based on a given position in a `terrain::voxel::tree::T`.
/// Any necessary voxels will be generated.
pub fn generate_block(
  timers: &TimerSet,
  id_allocator: &Mutex<IdAllocator<EntityId>>,
  heightmap: &heightmap::T,
  voxels: &mut terrain::voxel::tree::T,
  position: &BlockPosition,
  lod_index: LODIndex,
) -> TerrainBlock {
  timers.time("update.generate_block", || {
    let mut block = TerrainBlock::empty();

    let lateral_samples = terrain_block::EDGE_SAMPLES[lod_index.0 as usize] as i32;
    let lg_size = terrain_block::LG_SAMPLE_SIZE[lod_index.0 as usize] as i16;

    let bounds_at = |v: &Point3<i32>| {
      voxel::Bounds::new(v.x, v.y, v.z, lg_size)
    };

    let mut get_voxel = |bounds: &voxel::Bounds| {
      let branch = voxels.get_mut_or_create(bounds);
      let r;
      match branch {
        &mut terrain::voxel::tree::Empty => {
          r = generate_voxel(timers, heightmap, bounds);
          *branch =
            terrain::voxel::tree::Branch {
              data: Some(r),
              branches: Box::new(terrain::voxel::tree::Branches::empty()),
            };
        },
        &mut terrain::voxel::tree::Branch { ref mut data, branches: _ }  => {
          match data {
            &mut None => {
              r = generate_voxel(timers, heightmap, bounds);
              *data = Some(r);
            },
            &mut Some(ref data) => {
              r = *data;
            },
          }
        },
      };
      r
    };

    let lg_ratio = terrain_block::LG_WIDTH - lg_size;
    let block_position = position.as_pnt();
    let voxel_position =
      Point3::new(
        block_position.x << lg_ratio,
        block_position.y << lg_ratio,
        block_position.z << lg_ratio,
      );

    let mut coords = Vec::new();
    let mut normals = Vec::new();
    let mut indices = HashMap::new();
    let mut polys = Vec::new();

    for dx in 0..lateral_samples {
    for dy in 0..lateral_samples {
    for dz in 0..lateral_samples {
      let voxel_position = voxel_position.add_v(&Vector3::new(dx, dy, dz));
      let voxel;
      let bounds = bounds_at(&voxel_position);
      match get_voxel(&bounds) {
        terrain::voxel::T::Surface(v) => voxel = v,
        _ => continue,
      }
      let index = coords.len();
      let vertex = voxel.inner_vertex.to_world_vertex(&bounds);
      let normal = voxel.normal.to_float_normal();
      coords.push(vertex);
      normals.push(normal);
      indices.insert(voxel_position, index);

      let mut edge = |
        d_neighbor, // Vector to the neighbor to make an edge toward.
        d1, d2,     // Vector to the voxels adjacent to the edge.
      | {
        let neighbor_position = voxel_position.add_v(&d_neighbor);
        debug!("edge from {:?} to {:?}", voxel_position, neighbor_position);
        let neighbor_inside_surface;
        match get_voxel(&bounds_at(&neighbor_position)) {
          terrain::voxel::T::Surface(v) => neighbor_inside_surface = v.corner_inside_surface,
          terrain::voxel::T::Volume(inside) => neighbor_inside_surface = inside,
        }
        if voxel.corner_inside_surface == neighbor_inside_surface {
          // This edge doesn't cross the surface, and doesn't generate polys.

          return
        }

        let v1; let v2; let v3; let v4;
        let n1; let n2; let n3; let n4;
        let i1; let i2; let i3; let i4;

        {
          let mut voxel_index = |position: &Point3<i32>| {
            match indices.entry(*position) {
              hash_map::Entry::Occupied(entry) => {
                let i = *entry.get();
                (coords[i], normals[i], i)
              },
              hash_map::Entry::Vacant(entry) => {
                let bounds = bounds_at(position);
                match get_voxel(&bounds) {
                  terrain::voxel::T::Surface(voxel) => {
                    let i = coords.len();
                    let vertex = voxel.inner_vertex.to_world_vertex(&bounds);
                    let normal = voxel.normal.to_float_normal();
                    coords.push(vertex);
                    normals.push(normal);
                    entry.insert(i);
                    (vertex, normal, i)
                  },
                  voxel => {
                    panic!("Unexpected neighbor {:?} at {:?}", voxel, bounds)
                  }
                }
              },
            }
          };

          let (tv1, tn1, ti1) = voxel_index(&voxel_position.add_v(&d1).add_v(&d2));
          let (tv2, tn2, ti2) = voxel_index(&voxel_position.add_v(&d1));
          let (tv3, tn3, ti3) = (vertex, normal, index);
          let (tv4, tn4, ti4) = voxel_index(&voxel_position.add_v(&d2));

          v1 = tv1; v2 = tv2; v3 = tv3; v4 = tv4;
          n1 = tn1; n2 = tn2; n3 = tn3; n4 = tn4;
          i1 = ti1; i2 = ti2; i3 = ti3; i4 = ti4;
        }

        // Put a vertex at the average of the vertices.
        let v_center =
          v1.add_v(&v2.to_vec()).add_v(&v3.to_vec()).add_v(&v4.to_vec()).div_s(4.0);
        let n_center =
          n1.add_v(&n2).add_v(&n3).add_v(&n4).div_s(4.0);

        let i_center = coords.len();
        coords.push(v_center);
        normals.push(n_center);

        if voxel.corner_inside_surface {
          // The polys are visible from positive infinity.
          polys.push([i2, i1, i_center]);
          polys.push([i3, i2, i_center]);
          polys.push([i4, i3, i_center]);
          polys.push([i1, i4, i_center]);
        } else {
          // The polys are visible from negative infinity.
          polys.push([i1, i2, i_center]);
          polys.push([i2, i3, i_center]);
          polys.push([i3, i4, i_center]);
          polys.push([i4, i1, i_center]);
        }
      };

      edge(
        Vector3::new(1, 0, 0),
        Vector3::new(0, -1, 0),
        Vector3::new(0, 0, -1),
      );

      edge(
        Vector3::new(0, 1, 0),
        Vector3::new(0, 0, -1),
        Vector3::new(-1, 0, 0),
      );

      edge(
        Vector3::new(0, 0, 1),
        Vector3::new(-1, 0, 0),
        Vector3::new(0, -1, 0),
      );
    }}}

    for poly in polys.iter() {
      block.vertex_coordinates.push(tri(coords[poly[0]], coords[poly[1]], coords[poly[2]]));
      block.normals.push(tri(normals[poly[0]], normals[poly[1]], normals[poly[2]]));

      let id = id_allocator.lock().unwrap().allocate();
      block.ids.push(id);
      block.bounds.push((id, make_bounds(&coords[poly[0]], &coords[poly[1]], &coords[poly[2]])));
    }

    block
  })
}
<|endoftext|>


<commit_before>use cgmath::{Point, Vector, Ray3};
use std::cmp::Ordering;

use voxel;
use voxel::tree::{TreeBody, Branches};

// Time-of-intersection. Implements `Ord` for sanity reasons;
// let's hope the floating-points are all valid.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
struct TOI(pub f32);

impl Eq for TOI {}

impl Ord for TOI {
  fn cmp(&self, other: &TOI) -> Ordering {
    self.partial_cmp(other).unwrap()
  }
}

#[derive(Debug, Copy, Clone)]
/// Information about a ray entering a voxel.
pub struct Entry {
  /// Index of a side of a rectangular-prismic voxel.
  side: usize,
  // (Roughly) when the side was intersected.
  toi: TOI,
}

impl Entry {
  pub fn from_exit(exit: Exit) -> Entry {
    Entry {
      side:
        if exit.side < 3 {
          exit.side + 3
        } else {
          exit.side - 3
        },
      toi: exit.toi,
    }
  }
}

#[derive(Debug, Copy, Clone)]
/// Information about a ray exit a voxel.
pub struct Exit {
  /// Index of a side of a rectangular-prismic voxel.
  side: usize,
  // (Roughly) when the side was intersected.
  toi: TOI,
}

// TODO: Audit all the divisions for divide-by-zeros.

#[inline]
pub fn cast_ray_branches<'a, Voxel, MakeBounds, Act, R>(
  this: &'a Branches<Voxel>,
  ray: &Ray3<f32>,
  mut entry: Option<Entry>,
  mut coords: [usize; 3],
  make_bounds: &mut MakeBounds,
  act: &mut Act,
) -> Result<R, Exit>
  where
    MakeBounds: FnMut([usize; 3]) -> voxel::Bounds,
    Act: FnMut(voxel::Bounds, &'a Voxel) -> Option<R>,
{
  loop {
    let child = &this.as_array()[coords[0]][coords[1]][coords[2]];
    let bounds = make_bounds(coords);

    match cast_ray(child, ray, bounds, entry, act) {
      Ok(r) => return Ok(r),
      Err(exit) => {
        let dim = exit.side % 3;
        if ray.direction[dim] < 0.0 {
          if coords[dim] == 0 {
            return Err(exit)
          }
          coords[dim] = 0;
        } else {
          if coords[dim] == 1 {
            return Err(exit)
          }
          coords[dim] = 1;
        }
        entry = Some(Entry::from_exit(exit));
      },
    }
  }
}

/// Precondition: the ray passes through `this`.
pub fn cast_ray<'a, Voxel, Act, R>(
  this: &'a TreeBody<Voxel>,
  ray: &Ray3<f32>,
  bounds: voxel::Bounds,
  entry: Option<Entry>,
  act: &mut Act,
) -> Result<R, Exit>
  where
    Act: FnMut(voxel::Bounds, &'a Voxel) -> Option<R>
{
  match this {
    &TreeBody::Empty => {
      // We pass through empty voxels; fall through.
    },
    &TreeBody::Branch { ref data, ref branches } => {
      match data {
        &None => {},
        &Some(ref voxel) => {
          if let Some(r) = act(bounds, voxel) {
            return Ok(r)
          }
        },
      }

      let mid = bounds.center();

      let mut make_bounds = |coords: [usize; 3]| {
        let mut bounds = bounds;
        bounds.lg_size -= 1;
        bounds.x <<= 1;
        bounds.y <<= 1;
        bounds.z <<= 1;
        bounds.x += coords[0] as i32;
        bounds.y += coords[1] as i32;
        bounds.z += coords[2] as i32;
        bounds
      };

      let entry_toi = entry.map(|entry| entry.toi.0).unwrap_or(0.0);
      let intersect = ray.origin.add_v(&ray.direction.mul_s(entry_toi));
      let coords = [
        if intersect.x >= mid.x {1} else {0},
        if intersect.y >= mid.y {1} else {0},
        if intersect.z >= mid.z {1} else {0},
      ];

      return cast_ray_branches(
        branches,
        ray,
        entry,
        coords,
        &mut make_bounds,
        act,
      )
    }
  };

  // We pass through this voxel; calculate the exit.

  let sides = [
    (ray.origin.x, ray.direction.x, bounds.x),
    (ray.origin.y, ray.direction.y, bounds.y),
    (ray.origin.z, ray.direction.z, bounds.z),
    (ray.origin.x, ray.direction.x, bounds.x + 1),
    (ray.origin.y, ray.direction.y, bounds.y + 1),
    (ray.origin.z, ray.direction.z, bounds.z + 1),
  ];

  let voxel_size = bounds.size();
  let next_toi = |(side, &(origin, direction, bound))| {
    let bound = bound as f32 * voxel_size;
    if direction == 0.0 {
      None
    } else {
      let toi = (bound - origin) / direction;
      if entry.map(|entry| entry.toi.0 <= toi).unwrap_or(toi >= 0.0) {
        Some(Exit {
          side: side,
          toi: TOI(toi),
        })
      } else {
        None
      }
    }
  };

  let exit =
    match entry {
      None =>
        sides.iter()
        .enumerate()
        .filter_map(next_toi)
        .min_by(|&exit| exit.toi).unwrap(),
      Some(entry) =>
        sides.iter()
        .enumerate()
        .filter(|&(i, _)| i != entry.side)
        .filter_map(next_toi)
        .min_by(|&exit| exit.toi).unwrap(),
    };
  Err(exit)
}
<commit_msg>raycasting uses only coarsest LOD<commit_after>use cgmath::{Point, Vector, Ray3};
use std::cmp::Ordering;

use voxel;
use voxel::tree::{TreeBody, Branches};

// Time-of-intersection. Implements `Ord` for sanity reasons;
// let's hope the floating-points are all valid.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
struct TOI(pub f32);

impl Eq for TOI {}

impl Ord for TOI {
  fn cmp(&self, other: &TOI) -> Ordering {
    self.partial_cmp(other).unwrap()
  }
}

#[derive(Debug, Copy, Clone)]
/// Information about a ray entering a voxel.
pub struct Entry {
  /// Index of a side of a rectangular-prismic voxel.
  side: usize,
  // (Roughly) when the side was intersected.
  toi: TOI,
}

impl Entry {
  pub fn from_exit(exit: Exit) -> Entry {
    Entry {
      side:
        if exit.side < 3 {
          exit.side + 3
        } else {
          exit.side - 3
        },
      toi: exit.toi,
    }
  }
}

#[derive(Debug, Copy, Clone)]
/// Information about a ray exit a voxel.
pub struct Exit {
  /// Index of a side of a rectangular-prismic voxel.
  side: usize,
  // (Roughly) when the side was intersected.
  toi: TOI,
}

// TODO: Audit all the divisions for divide-by-zeros.

#[inline]
pub fn cast_ray_branches<'a, Voxel, MakeBounds, Act, R>(
  this: &'a Branches<Voxel>,
  ray: &Ray3<f32>,
  mut entry: Option<Entry>,
  mut coords: [usize; 3],
  make_bounds: &mut MakeBounds,
  act: &mut Act,
) -> Result<R, Exit>
  where
    MakeBounds: FnMut([usize; 3]) -> voxel::Bounds,
    Act: FnMut(voxel::Bounds, &'a Voxel) -> Option<R>,
{
  loop {
    let child = &this.as_array()[coords[0]][coords[1]][coords[2]];
    let bounds = make_bounds(coords);

    match cast_ray(child, ray, bounds, entry, act) {
      Ok(r) => return Ok(r),
      Err(exit) => {
        let dim = exit.side % 3;
        if ray.direction[dim] < 0.0 {
          if coords[dim] == 0 {
            return Err(exit)
          }
          coords[dim] = 0;
        } else {
          if coords[dim] == 1 {
            return Err(exit)
          }
          coords[dim] = 1;
        }
        entry = Some(Entry::from_exit(exit));
      },
    }
  }
}

/// Precondition: the ray passes through `this`.
pub fn cast_ray<'a, Voxel, Act, R>(
  this: &'a TreeBody<Voxel>,
  ray: &Ray3<f32>,
  bounds: voxel::Bounds,
  entry: Option<Entry>,
  act: &mut Act,
) -> Result<R, Exit>
  where
    Act: FnMut(voxel::Bounds, &'a Voxel) -> Option<R>
{
  match this {
    &TreeBody::Empty => {
      // We pass through empty voxels; fall through.
    },
    &TreeBody::Branch { ref data, ref branches } => {
      match data {
        &Some(ref voxel) => {
          if let Some(r) = act(bounds, voxel) {
            return Ok(r)
          }
        },
        &None => {
          let mid = bounds.center();

          let mut make_bounds = |coords: [usize; 3]| {
            let mut bounds = bounds;
            bounds.lg_size -= 1;
            bounds.x <<= 1;
            bounds.y <<= 1;
            bounds.z <<= 1;
            bounds.x += coords[0] as i32;
            bounds.y += coords[1] as i32;
            bounds.z += coords[2] as i32;
            bounds
          };

          let entry_toi = entry.map(|entry| entry.toi.0).unwrap_or(0.0);
          let intersect = ray.origin.add_v(&ray.direction.mul_s(entry_toi));
          let coords = [
            if intersect.x >= mid.x {1} else {0},
            if intersect.y >= mid.y {1} else {0},
            if intersect.z >= mid.z {1} else {0},
          ];

          return cast_ray_branches(
            branches,
            ray,
            entry,
            coords,
            &mut make_bounds,
            act,
          )
        },
      }
    }
  };

  // We pass through this voxel; calculate the exit.

  let sides = [
    (ray.origin.x, ray.direction.x, bounds.x),
    (ray.origin.y, ray.direction.y, bounds.y),
    (ray.origin.z, ray.direction.z, bounds.z),
    (ray.origin.x, ray.direction.x, bounds.x + 1),
    (ray.origin.y, ray.direction.y, bounds.y + 1),
    (ray.origin.z, ray.direction.z, bounds.z + 1),
  ];

  let voxel_size = bounds.size();
  let next_toi = |(side, &(origin, direction, bound))| {
    let bound = bound as f32 * voxel_size;
    if direction == 0.0 {
      None
    } else {
      let toi = (bound - origin) / direction;
      if entry.map(|entry| entry.toi.0 <= toi).unwrap_or(toi >= 0.0) {
        Some(Exit {
          side: side,
          toi: TOI(toi),
        })
      } else {
        None
      }
    }
  };

  let exit =
    match entry {
      None =>
        sides.iter()
        .enumerate()
        .filter_map(next_toi)
        .min_by(|&exit| exit.toi).unwrap(),
      Some(entry) =>
        sides.iter()
        .enumerate()
        .filter(|&(i, _)| i != entry.side)
        .filter_map(next_toi)
        .min_by(|&exit| exit.toi).unwrap(),
    };
  Err(exit)
}
<|endoftext|>


<commit_before>//! Extra streaming decompression functionality.
//!
//! As of now this is mainly inteded for use to build a higher-level wrapper.
use alloc::boxed::Box;
use core::{cmp, mem};

use crate::inflate::core::{decompress, inflate_flags, DecompressorOxide, TINFL_LZ_DICT_SIZE};
use crate::inflate::TINFLStatus;
use crate::{DataFormat, MZError, MZFlush, MZResult, MZStatus, StreamResult};

/// A struct that compbines a decompressor with extra data for streaming decompression.
///
pub struct InflateState {
    /// Inner decompressor struct
    decomp: DecompressorOxide,

    /// Buffer of input bytes for matches.
    /// TODO: Could probably do this a bit cleaner with some
    /// Cursor-like class.
    /// We may also look into whether we need to keep a buffer here, or just one in the
    /// decompressor struct.
    dict: [u8; TINFL_LZ_DICT_SIZE],
    /// Where in the buffer are we currently at?
    dict_ofs: usize,
    /// How many bytes of data to be flushed is there currently in the buffer?
    dict_avail: usize,

    first_call: bool,
    has_flushed: bool,

    /// Whether the input data is wrapped in a zlib header and checksum.
    /// TODO: This should be stored in the decompressor.
    data_format: DataFormat,
    last_status: TINFLStatus,
}

impl Default for InflateState {
    fn default() -> Self {
        InflateState {
            decomp: DecompressorOxide::default(),
            dict: [0; TINFL_LZ_DICT_SIZE],
            dict_ofs: 0,
            dict_avail: 0,
            first_call: true,
            has_flushed: false,
            data_format: DataFormat::Raw,
            last_status: TINFLStatus::NeedsMoreInput,
        }
    }
}
impl InflateState {
    /// Create a new state.
    ///
    /// Note that this struct is quite large due to internal buffers, and as such storing it on
    /// the stack is not recommended.
    ///
    /// # Parameters
    /// `data_format`: Determines whether the compressed data is assumed to wrapped with zlib
    /// metadata.
    pub fn new(data_format: DataFormat) -> InflateState {
        let mut b = InflateState::default();
        b.data_format = data_format;
        b
    }

    /// Create a new state on the heap.
    ///
    /// # Parameters
    /// `data_format`: Determines whether the compressed data is assumed to wrapped with zlib
    /// metadata.
    pub fn new_boxed(data_format: DataFormat) -> Box<InflateState> {
        let mut b: Box<InflateState> = Box::default();
        b.data_format = data_format;
        b
    }

    /// Access the innner decompressor.
    pub fn decompressor(&mut self) -> &mut DecompressorOxide {
        &mut self.decomp
    }

    /// Return the status of the last call to `inflate` with this `InflateState`.
    pub fn last_status(&self) -> TINFLStatus {
        self.last_status
    }

    /// Create a new state using miniz/zlib style window bits parameter.
    ///
    /// The decompressor does not support different window sizes. As such,
    /// any positive (>0) value will set the zlib header flag, while a negative one
    /// will not.
    pub fn new_boxed_with_window_bits(window_bits: i32) -> Box<InflateState> {
        let mut b: Box<InflateState> = Box::default();
        b.data_format = DataFormat::from_window_bits(window_bits);
        b
    }

    /// Reset the decompressor without re-allocating memory, using the given
    /// data format.
    pub fn reset(&mut self, data_format: DataFormat) {
        self.decompressor().init();
        self.dict = [0; TINFL_LZ_DICT_SIZE];
        self.dict_ofs = 0;
        self.dict_avail = 0;
        self.first_call = true;
        self.has_flushed = false;
        self.data_format = data_format;
        self.last_status = TINFLStatus::NeedsMoreInput;
    }
}

/// Try to decompress from `input` to `output` with the given `InflateState`
///
/// # Errors
///
/// Returns `MZError::Buf` If the size of the `output` slice is empty or no progress was made due to
/// lack of expected input data or called after the decompression was
/// finished without MZFlush::Finish.
///
/// Returns `MZError::Param` if the compressor parameters are set wrong.
pub fn inflate(
    state: &mut InflateState,
    input: &[u8],
    output: &mut [u8],
    flush: MZFlush,
) -> StreamResult {
    let mut bytes_consumed = 0;
    let mut bytes_written = 0;
    let mut next_in = input;
    let mut next_out = output;

    if flush == MZFlush::Full {
        return StreamResult::error(MZError::Stream);
    }

    let mut decomp_flags = inflate_flags::TINFL_FLAG_COMPUTE_ADLER32;
    if state.data_format == DataFormat::Zlib {
        decomp_flags |= inflate_flags::TINFL_FLAG_PARSE_ZLIB_HEADER;
    }

    let first_call = state.first_call;
    state.first_call = false;
    if (state.last_status as i32) < 0 {
        return StreamResult::error(MZError::Data);
    }

    if state.has_flushed && (flush != MZFlush::Finish) {
        return StreamResult::error(MZError::Stream);
    }
    state.has_flushed |= flush == MZFlush::Finish;

    if (flush == MZFlush::Finish) && first_call {
        decomp_flags |= inflate_flags::TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF;

        let status = decompress(&mut state.decomp, next_in, next_out, 0, decomp_flags);
        let in_bytes = status.1;
        let out_bytes = status.2;
        let status = status.0;

        state.last_status = status;

        bytes_consumed += in_bytes;
        bytes_written += out_bytes;

        let ret_status = {
            if (status as i32) < 0 {
                Err(MZError::Data)
            } else if status != TINFLStatus::Done {
                state.last_status = TINFLStatus::Failed;
                Err(MZError::Buf)
            } else {
                Ok(MZStatus::StreamEnd)
            }
        };
        return StreamResult {
            bytes_consumed,
            bytes_written,
            status: ret_status,
        };
    }

    if flush != MZFlush::Finish {
        decomp_flags |= inflate_flags::TINFL_FLAG_HAS_MORE_INPUT;
    }

    if state.dict_avail != 0 {
        bytes_written += push_dict_out(state, &mut next_out);
        return StreamResult {
            bytes_consumed,
            bytes_written,
            status: Ok(
                if (state.last_status == TINFLStatus::Done) && (state.dict_avail == 0) {
                    MZStatus::StreamEnd
                } else {
                    MZStatus::Ok
                },
            ),
        };
    }

    let status = inflate_loop(
        state,
        &mut next_in,
        &mut next_out,
        &mut bytes_consumed,
        &mut bytes_written,
        decomp_flags,
        flush,
    );
    StreamResult {
        bytes_consumed,
        bytes_written,
        status,
    }
}

fn inflate_loop(
    state: &mut InflateState,
    next_in: &mut &[u8],
    next_out: &mut &mut [u8],
    total_in: &mut usize,
    total_out: &mut usize,
    decomp_flags: u32,
    flush: MZFlush,
) -> MZResult {
    let orig_in_len = next_in.len();
    loop {
        let status = decompress(
            &mut state.decomp,
            *next_in,
            &mut state.dict,
            state.dict_ofs,
            decomp_flags,
        );

        let in_bytes = status.1;
        let out_bytes = status.2;
        let status = status.0;

        state.last_status = status;

        *next_in = &next_in[in_bytes..];
        *total_in += in_bytes;

        state.dict_avail = out_bytes;
        *total_out += push_dict_out(state, next_out);

        // The stream was corrupted, and decompression failed.
        if (status as i32) < 0 {
            return Err(MZError::Data);
        }

        // The decompressor has flushed all it's data and is waiting for more input, but
        // there was no more input provided.
        if (status == TINFLStatus::NeedsMoreInput) && orig_in_len == 0 {
            return Err(MZError::Buf);
        }

        if flush == MZFlush::Finish {
            if status == TINFLStatus::Done {
                // There is not enough space in the output buffer to flush the remaining
                // decompressed data in the internal buffer.
                return if state.dict_avail != 0 {
                    Err(MZError::Buf)
                } else {
                    Ok(MZStatus::StreamEnd)
                };
            // No more space in the output buffer, but we're not done.
            } else if next_out.is_empty() {
                return Err(MZError::Buf);
            }
        } else {
            // We're not expected to finish, so it's fine if we can't flush everything yet.
            let empty_buf = next_in.is_empty() || next_out.is_empty();
            if (status == TINFLStatus::Done) || empty_buf || (state.dict_avail != 0) {
                return if (status == TINFLStatus::Done) && (state.dict_avail == 0) {
                    // No more data left, we're done.
                    Ok(MZStatus::StreamEnd)
                } else {
                    // Ok for now, still waiting for more input data or output space.
                    Ok(MZStatus::Ok)
                };
            }
        }
    }
}

fn push_dict_out(state: &mut InflateState, next_out: &mut &mut [u8]) -> usize {
    let n = cmp::min(state.dict_avail as usize, next_out.len());
    (next_out[..n]).copy_from_slice(&state.dict[state.dict_ofs..state.dict_ofs + n]);
    *next_out = &mut mem::replace(next_out, &mut [])[n..];
    state.dict_avail -= n;
    state.dict_ofs = (state.dict_ofs + (n)) & (TINFL_LZ_DICT_SIZE - 1);
    n
}

#[cfg(test)]
mod test {
    use super::{inflate, InflateState};
    use crate::{DataFormat, MZFlush, MZStatus};
    use std::vec;

    #[test]
    fn test_state() {
        let encoded = [
            120u8, 156, 243, 72, 205, 201, 201, 215, 81, 168, 202, 201, 76, 82, 4, 0, 27, 101, 4,
            19,
        ];
        let mut out = vec![0; 50];
        let mut state = InflateState::new_boxed(DataFormat::Zlib);
        let res = inflate(&mut state, &encoded, &mut out, MZFlush::Finish);
        let status = res.status.expect("Failed to decompress!");
        assert_eq!(status, MZStatus::StreamEnd);
        assert_eq!(out[..res.bytes_written as usize], b"Hello, zlib!"[..]);
        assert_eq!(res.bytes_consumed, encoded.len());

        state.reset(DataFormat::Zlib);
        let status = res.status.expect("Failed to decompress!");
        assert_eq!(status, MZStatus::StreamEnd);
        assert_eq!(out[..res.bytes_written as usize], b"Hello, zlib!"[..]);
        assert_eq!(res.bytes_consumed, encoded.len());
    }
}
<commit_msg>Introduce reset policy to control InflateState::reset (#91)<commit_after>//! Extra streaming decompression functionality.
//!
//! As of now this is mainly inteded for use to build a higher-level wrapper.
use alloc::boxed::Box;
use core::{cmp, mem};

use crate::inflate::core::{decompress, inflate_flags, DecompressorOxide, TINFL_LZ_DICT_SIZE};
use crate::inflate::TINFLStatus;
use crate::{DataFormat, MZError, MZFlush, MZResult, MZStatus, StreamResult};

/// Tag that determines reset policy of [InflateState](struct.InflateState.html)
pub trait ResetPolicy {
    /// Performs reset
    fn reset(&self, state: &mut InflateState);
}

/// Resets state, without performing expensive ops (e.g. zeroing buffer)
///
/// Note that not zeroing buffer can lead to security issues when dealing with untrusted input.
pub struct MinReset;

impl ResetPolicy for MinReset {
    fn reset(&self, state: &mut InflateState) {
        state.decompressor().init();
        state.dict_ofs = 0;
        state.dict_avail = 0;
        state.first_call = true;
        state.has_flushed = false;
        state.last_status = TINFLStatus::NeedsMoreInput;
    }
}

/// Resets state and zero memory, continuing to use the same data format.
pub struct ZeroReset;

impl ResetPolicy for ZeroReset {
    #[inline]
    fn reset(&self, state: &mut InflateState) {
        MinReset.reset(state);
        state.dict = [0; TINFL_LZ_DICT_SIZE];
    }
}

/// Full reset of the state, including zeroing memory.
///
/// Requires to provide new data format.
pub struct FullReset(DataFormat);

impl ResetPolicy for FullReset {
    #[inline]
    fn reset(&self, state: &mut InflateState) {
        ZeroReset.reset(state);
        state.data_format = self.0;
    }
}

/// A struct that compbines a decompressor with extra data for streaming decompression.
///
pub struct InflateState {
    /// Inner decompressor struct
    decomp: DecompressorOxide,

    /// Buffer of input bytes for matches.
    /// TODO: Could probably do this a bit cleaner with some
    /// Cursor-like class.
    /// We may also look into whether we need to keep a buffer here, or just one in the
    /// decompressor struct.
    dict: [u8; TINFL_LZ_DICT_SIZE],
    /// Where in the buffer are we currently at?
    dict_ofs: usize,
    /// How many bytes of data to be flushed is there currently in the buffer?
    dict_avail: usize,

    first_call: bool,
    has_flushed: bool,

    /// Whether the input data is wrapped in a zlib header and checksum.
    /// TODO: This should be stored in the decompressor.
    data_format: DataFormat,
    last_status: TINFLStatus,
}

impl Default for InflateState {
    fn default() -> Self {
        InflateState {
            decomp: DecompressorOxide::default(),
            dict: [0; TINFL_LZ_DICT_SIZE],
            dict_ofs: 0,
            dict_avail: 0,
            first_call: true,
            has_flushed: false,
            data_format: DataFormat::Raw,
            last_status: TINFLStatus::NeedsMoreInput,
        }
    }
}
impl InflateState {
    /// Create a new state.
    ///
    /// Note that this struct is quite large due to internal buffers, and as such storing it on
    /// the stack is not recommended.
    ///
    /// # Parameters
    /// `data_format`: Determines whether the compressed data is assumed to wrapped with zlib
    /// metadata.
    pub fn new(data_format: DataFormat) -> InflateState {
        let mut b = InflateState::default();
        b.data_format = data_format;
        b
    }

    /// Create a new state on the heap.
    ///
    /// # Parameters
    /// `data_format`: Determines whether the compressed data is assumed to wrapped with zlib
    /// metadata.
    pub fn new_boxed(data_format: DataFormat) -> Box<InflateState> {
        let mut b: Box<InflateState> = Box::default();
        b.data_format = data_format;
        b
    }

    /// Access the innner decompressor.
    pub fn decompressor(&mut self) -> &mut DecompressorOxide {
        &mut self.decomp
    }

    /// Return the status of the last call to `inflate` with this `InflateState`.
    pub fn last_status(&self) -> TINFLStatus {
        self.last_status
    }

    /// Create a new state using miniz/zlib style window bits parameter.
    ///
    /// The decompressor does not support different window sizes. As such,
    /// any positive (>0) value will set the zlib header flag, while a negative one
    /// will not.
    pub fn new_boxed_with_window_bits(window_bits: i32) -> Box<InflateState> {
        let mut b: Box<InflateState> = Box::default();
        b.data_format = DataFormat::from_window_bits(window_bits);
        b
    }

    #[inline]
    /// Reset the decompressor without re-allocating memory, using the given
    /// data format.
    pub fn reset(&mut self, data_format: DataFormat) {
        self.reset_as(FullReset(data_format));
    }

    #[inline]
    /// Resets the state according to specified policy.
    pub fn reset_as<T: ResetPolicy>(&mut self, policy: T) {
        policy.reset(self)
    }
}

/// Try to decompress from `input` to `output` with the given `InflateState`
///
/// # Errors
///
/// Returns `MZError::Buf` If the size of the `output` slice is empty or no progress was made due to
/// lack of expected input data or called after the decompression was
/// finished without MZFlush::Finish.
///
/// Returns `MZError::Param` if the compressor parameters are set wrong.
pub fn inflate(
    state: &mut InflateState,
    input: &[u8],
    output: &mut [u8],
    flush: MZFlush,
) -> StreamResult {
    let mut bytes_consumed = 0;
    let mut bytes_written = 0;
    let mut next_in = input;
    let mut next_out = output;

    if flush == MZFlush::Full {
        return StreamResult::error(MZError::Stream);
    }

    let mut decomp_flags = inflate_flags::TINFL_FLAG_COMPUTE_ADLER32;
    if state.data_format == DataFormat::Zlib {
        decomp_flags |= inflate_flags::TINFL_FLAG_PARSE_ZLIB_HEADER;
    }

    let first_call = state.first_call;
    state.first_call = false;
    if (state.last_status as i32) < 0 {
        return StreamResult::error(MZError::Data);
    }

    if state.has_flushed && (flush != MZFlush::Finish) {
        return StreamResult::error(MZError::Stream);
    }
    state.has_flushed |= flush == MZFlush::Finish;

    if (flush == MZFlush::Finish) && first_call {
        decomp_flags |= inflate_flags::TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF;

        let status = decompress(&mut state.decomp, next_in, next_out, 0, decomp_flags);
        let in_bytes = status.1;
        let out_bytes = status.2;
        let status = status.0;

        state.last_status = status;

        bytes_consumed += in_bytes;
        bytes_written += out_bytes;

        let ret_status = {
            if (status as i32) < 0 {
                Err(MZError::Data)
            } else if status != TINFLStatus::Done {
                state.last_status = TINFLStatus::Failed;
                Err(MZError::Buf)
            } else {
                Ok(MZStatus::StreamEnd)
            }
        };
        return StreamResult {
            bytes_consumed,
            bytes_written,
            status: ret_status,
        };
    }

    if flush != MZFlush::Finish {
        decomp_flags |= inflate_flags::TINFL_FLAG_HAS_MORE_INPUT;
    }

    if state.dict_avail != 0 {
        bytes_written += push_dict_out(state, &mut next_out);
        return StreamResult {
            bytes_consumed,
            bytes_written,
            status: Ok(
                if (state.last_status == TINFLStatus::Done) && (state.dict_avail == 0) {
                    MZStatus::StreamEnd
                } else {
                    MZStatus::Ok
                },
            ),
        };
    }

    let status = inflate_loop(
        state,
        &mut next_in,
        &mut next_out,
        &mut bytes_consumed,
        &mut bytes_written,
        decomp_flags,
        flush,
    );
    StreamResult {
        bytes_consumed,
        bytes_written,
        status,
    }
}

fn inflate_loop(
    state: &mut InflateState,
    next_in: &mut &[u8],
    next_out: &mut &mut [u8],
    total_in: &mut usize,
    total_out: &mut usize,
    decomp_flags: u32,
    flush: MZFlush,
) -> MZResult {
    let orig_in_len = next_in.len();
    loop {
        let status = decompress(
            &mut state.decomp,
            *next_in,
            &mut state.dict,
            state.dict_ofs,
            decomp_flags,
        );

        let in_bytes = status.1;
        let out_bytes = status.2;
        let status = status.0;

        state.last_status = status;

        *next_in = &next_in[in_bytes..];
        *total_in += in_bytes;

        state.dict_avail = out_bytes;
        *total_out += push_dict_out(state, next_out);

        // The stream was corrupted, and decompression failed.
        if (status as i32) < 0 {
            return Err(MZError::Data);
        }

        // The decompressor has flushed all it's data and is waiting for more input, but
        // there was no more input provided.
        if (status == TINFLStatus::NeedsMoreInput) && orig_in_len == 0 {
            return Err(MZError::Buf);
        }

        if flush == MZFlush::Finish {
            if status == TINFLStatus::Done {
                // There is not enough space in the output buffer to flush the remaining
                // decompressed data in the internal buffer.
                return if state.dict_avail != 0 {
                    Err(MZError::Buf)
                } else {
                    Ok(MZStatus::StreamEnd)
                };
            // No more space in the output buffer, but we're not done.
            } else if next_out.is_empty() {
                return Err(MZError::Buf);
            }
        } else {
            // We're not expected to finish, so it's fine if we can't flush everything yet.
            let empty_buf = next_in.is_empty() || next_out.is_empty();
            if (status == TINFLStatus::Done) || empty_buf || (state.dict_avail != 0) {
                return if (status == TINFLStatus::Done) && (state.dict_avail == 0) {
                    // No more data left, we're done.
                    Ok(MZStatus::StreamEnd)
                } else {
                    // Ok for now, still waiting for more input data or output space.
                    Ok(MZStatus::Ok)
                };
            }
        }
    }
}

fn push_dict_out(state: &mut InflateState, next_out: &mut &mut [u8]) -> usize {
    let n = cmp::min(state.dict_avail as usize, next_out.len());
    (next_out[..n]).copy_from_slice(&state.dict[state.dict_ofs..state.dict_ofs + n]);
    *next_out = &mut mem::replace(next_out, &mut [])[n..];
    state.dict_avail -= n;
    state.dict_ofs = (state.dict_ofs + (n)) & (TINFL_LZ_DICT_SIZE - 1);
    n
}

#[cfg(test)]
mod test {
    use super::{inflate, InflateState};
    use crate::{DataFormat, MZFlush, MZStatus};
    use std::vec;

    #[test]
    fn test_state() {
        let encoded = [
            120u8, 156, 243, 72, 205, 201, 201, 215, 81, 168, 202, 201, 76, 82, 4, 0, 27, 101, 4,
            19,
        ];
        let mut out = vec![0; 50];
        let mut state = InflateState::new_boxed(DataFormat::Zlib);
        let res = inflate(&mut state, &encoded, &mut out, MZFlush::Finish);
        let status = res.status.expect("Failed to decompress!");
        assert_eq!(status, MZStatus::StreamEnd);
        assert_eq!(out[..res.bytes_written as usize], b"Hello, zlib!"[..]);
        assert_eq!(res.bytes_consumed, encoded.len());

        state.reset_as(super::ZeroReset);
        out.iter_mut().map(|x| *x = 0).count();
        let res = inflate(&mut state, &encoded, &mut out, MZFlush::Finish);
        let status = res.status.expect("Failed to decompress!");
        assert_eq!(status, MZStatus::StreamEnd);
        assert_eq!(out[..res.bytes_written as usize], b"Hello, zlib!"[..]);
        assert_eq!(res.bytes_consumed, encoded.len());

        state.reset_as(super::MinReset);
        out.iter_mut().map(|x| *x = 0).count();
        let res = inflate(&mut state, &encoded, &mut out, MZFlush::Finish);
        let status = res.status.expect("Failed to decompress!");
        assert_eq!(status, MZStatus::StreamEnd);
        assert_eq!(out[..res.bytes_written as usize], b"Hello, zlib!"[..]);
        assert_eq!(res.bytes_consumed, encoded.len());
    }
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use admission_control_proto::proto::{
    admission_control::{
        AdmissionControlStatusCode, SubmitTransactionRequest,
        SubmitTransactionResponse as ProtoSubmitTransactionResponse,
    },
    admission_control_grpc::AdmissionControlClient,
};
use client::AccountStatus;
use failure::prelude::*;
use futures::{
    stream::{self, Stream},
    Future,
};
use grpcio::{self, CallOption};
use logger::prelude::*;
use proto_conv::{FromProto, IntoProto};
use std::slice::Chunks;
use types::{
    account_address::AccountAddress,
    account_config::get_account_resource_or_default,
    get_with_proof::{RequestItem, UpdateToLatestLedgerRequest, UpdateToLatestLedgerResponse},
};

use crate::OP_COUNTER;

/// Return a parameter that controls how "patient" AC clients are,
/// who are waiting the response from AC for this amount of time.
fn get_default_grpc_call_option() -> CallOption {
    CallOption::default()
        .wait_for_ready(true)
        .timeout(std::time::Duration::from_millis(8_000))
}

/// Divide generic items into a vector of chunks of nearly equal size.
pub fn divide_items<T>(items: &[T], num_chunks: usize) -> Chunks<T> {
    let chunk_size = if (num_chunks == 0) || (items.len() / num_chunks == 0) {
        std::cmp::max(1, items.len())
    } else {
        items.len() / num_chunks
    };
    items.chunks(chunk_size)
}

/// ---------------------------------------------------------- ///
///  Transaction async request and response handling helpers.  ///
/// ---------------------------------------------------------- ///

/// By checking 1) ac status, 2) vm status, and 3) mempool status, decide whether the reponse
/// from AC is accepted. If not, classify what the error type is.
fn check_ac_response(resp: &ProtoSubmitTransactionResponse) -> bool {
    if resp.has_ac_status() {
        let status = resp.get_ac_status().get_code();
        if status == AdmissionControlStatusCode::Accepted {
            OP_COUNTER.inc(&format!("submit_txns.{:?}", status));
            true
        } else {
            OP_COUNTER.inc(&format!("submit_txns.{:?}", status));
            error!("Request rejected by AC: {:?}", resp);
            false
        }
    } else if resp.has_vm_status() {
        OP_COUNTER.inc(&format!("submit_txns.{:?}", resp.get_vm_status()));
        error!("Request causes error on VM: {:?}", resp);
        false
    } else if resp.has_mempool_status() {
        OP_COUNTER.inc(&format!(
            "submit_txns.{:?}",
            resp.get_mempool_status().get_code()
        ));
        error!("Request causes error on mempool: {:?}", resp);
        false
    } else {
        OP_COUNTER.inc("submit_txns.Unknown");
        error!("Request rejected by AC for unknown error: {:?}", resp);
        false
    }
}

/// Send TXN requests to AC async, wait for and check the responses from AC.
/// Return only the responses of accepted TXN requests.
/// Ignore but count both gRPC-failed submissions and AC-rejected TXNs.
pub fn submit_and_wait_txn_requests(
    client: &AdmissionControlClient,
    txn_requests: &[SubmitTransactionRequest],
) -> Vec<ProtoSubmitTransactionResponse> {
    let futures: Vec<_> = txn_requests
        .iter()
        .filter_map(|req| {
            match client.submit_transaction_async_opt(&req, get_default_grpc_call_option()) {
                Ok(future) => Some(future),
                Err(e) => {
                    OP_COUNTER.inc(&format!("submit_txns.{:?}", e));
                    error!("Failed to send gRPC request: {:?}", e);
                    None
                }
            }
        })
        .collect();
    // Wait all the futures unorderedly, then pick only accepted responses.
    stream::futures_unordered(futures)
        .wait()
        .filter_map(|future_result| match future_result {
            Ok(proto_resp) => {
                if check_ac_response(&proto_resp) {
                    Some(proto_resp)
                } else {
                    None
                }
            }
            Err(e) => {
                OP_COUNTER.inc(&format!("submit_txns.{:?}", e));
                error!("Failed to receive gRPC response: {:?}", e);
                None
            }
        })
        .collect()
}

/// ------------------------------------------------------------ ///
///  Account state async request and response handling helpers.  ///
/// ------------------------------------------------------------ ///

/// Send account state request async with a AC client.
fn get_account_state_async(
    client: &AdmissionControlClient,
    address: &AccountAddress,
) -> Result<impl Future<Item = UpdateToLatestLedgerResponse, Error = failure::Error>> {
    let requested_item = RequestItem::GetAccountState { address: *address };
    let requested_items = vec![requested_item];
    let req = UpdateToLatestLedgerRequest::new(0, requested_items);
    let proto_req = req.into_proto();
    let ret = client
        .update_to_latest_ledger_async_opt(&proto_req, get_default_grpc_call_option())?
        .then(move |account_state_proof_resp| {
            let resp = UpdateToLatestLedgerResponse::from_proto(account_state_proof_resp?)?;
            Ok(resp)
        });
    Ok(ret)
}

/// Wait and process the response of account state request.
/// For valid response, return account's sequence number and status.
fn handle_account_state_response(
    future_resp: impl Future<Item = UpdateToLatestLedgerResponse, Error = failure::Error>,
) -> Result<(u64, AccountStatus)> {
    let mut response = future_resp.wait()?;
    let account_state_proof = response
        .response_items
        .remove(0)
        .into_get_account_state_response()?;
    if let Some(account_state_blob) = account_state_proof.blob {
        let account_resource = get_account_resource_or_default(&Some(account_state_blob))?;
        Ok((account_resource.sequence_number(), AccountStatus::Persisted))
    } else {
        bail!("failed to get account state because account doesn't exist")
    }
}

/// Query a bunch of accounts' sequence numbers and status from an validator.
/// If operation for one account fails, during either querying or handling, return immediately.
/// When everything is fine, the return vector, whose item is (sequence_number, account_status),
/// is ensured to be in consistent order with the given account_address vector.
pub fn get_account_states(
    client: &AdmissionControlClient,
    account_address: &[AccountAddress],
) -> Result<Vec<(u64, AccountStatus)>> {
    let future_resps: Result<Vec<_>> = account_address
        .iter()
        .map(|address| get_account_state_async(client, address))
        .collect();
    future_resps?
        .into_iter()
        .map(handle_account_state_response)
        .collect()
}

#[cfg(test)]
mod tests {
    use crate::divide_items;

    #[test]
    fn test_divide_items() {
        let items: Vec<_> = (0..4).collect();
        let mut iter1 = divide_items(&items, 3);
        assert_eq!(iter1.next().unwrap(), &[0]);
        assert_eq!(iter1.next().unwrap(), &[1]);
        assert_eq!(iter1.next().unwrap(), &[2]);
        assert_eq!(iter1.next().unwrap(), &[3]);

        let mut iter2 = divide_items(&items, 2);
        assert_eq!(iter2.next().unwrap(), &[0, 1]);
        assert_eq!(iter2.next().unwrap(), &[2, 3]);

        let mut iter3 = divide_items(&items, 0);
        assert_eq!(iter3.next().unwrap(), &[0, 1, 2, 3]);

        let empty_slice: Vec<u32> = vec![];
        let mut empty_iter = divide_items(&empty_slice, 3);
        assert!(empty_iter.next().is_none());
        let mut empty_iter = divide_items(&empty_slice, 0);
        assert!(empty_iter.next().is_none());
    }
}
<commit_msg>[benchmark] Replace hardcoded number with macro GRPC_TIMEOUT_MS<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use admission_control_proto::proto::{
    admission_control::{
        AdmissionControlStatusCode, SubmitTransactionRequest,
        SubmitTransactionResponse as ProtoSubmitTransactionResponse,
    },
    admission_control_grpc::AdmissionControlClient,
};
use client::AccountStatus;
use failure::prelude::*;
use futures::{
    stream::{self, Stream},
    Future,
};
use grpcio::{self, CallOption};
use logger::prelude::*;
use proto_conv::{FromProto, IntoProto};
use std::slice::Chunks;
use types::{
    account_address::AccountAddress,
    account_config::get_account_resource_or_default,
    get_with_proof::{RequestItem, UpdateToLatestLedgerRequest, UpdateToLatestLedgerResponse},
};

use crate::OP_COUNTER;

/// Timeout duration for grpc call option.
const GRPC_TIMEOUT_MS: u64 = 8_000;

/// Return a parameter that controls how "patient" AC clients are,
/// who are waiting the response from AC for this amount of time.
fn get_default_grpc_call_option() -> CallOption {
    CallOption::default()
        .wait_for_ready(true)
        .timeout(std::time::Duration::from_millis(GRPC_TIMEOUT_MS))
}

/// Divide generic items into a vector of chunks of nearly equal size.
pub fn divide_items<T>(items: &[T], num_chunks: usize) -> Chunks<T> {
    let chunk_size = if (num_chunks == 0) || (items.len() / num_chunks == 0) {
        std::cmp::max(1, items.len())
    } else {
        items.len() / num_chunks
    };
    items.chunks(chunk_size)
}

/// ---------------------------------------------------------- ///
///  Transaction async request and response handling helpers.  ///
/// ---------------------------------------------------------- ///

/// By checking 1) ac status, 2) vm status, and 3) mempool status, decide whether the reponse
/// from AC is accepted. If not, classify what the error type is.
fn check_ac_response(resp: &ProtoSubmitTransactionResponse) -> bool {
    if resp.has_ac_status() {
        let status = resp.get_ac_status().get_code();
        if status == AdmissionControlStatusCode::Accepted {
            OP_COUNTER.inc(&format!("submit_txns.{:?}", status));
            true
        } else {
            OP_COUNTER.inc(&format!("submit_txns.{:?}", status));
            error!("Request rejected by AC: {:?}", resp);
            false
        }
    } else if resp.has_vm_status() {
        OP_COUNTER.inc(&format!("submit_txns.{:?}", resp.get_vm_status()));
        error!("Request causes error on VM: {:?}", resp);
        false
    } else if resp.has_mempool_status() {
        OP_COUNTER.inc(&format!(
            "submit_txns.{:?}",
            resp.get_mempool_status().get_code()
        ));
        error!("Request causes error on mempool: {:?}", resp);
        false
    } else {
        OP_COUNTER.inc("submit_txns.Unknown");
        error!("Request rejected by AC for unknown error: {:?}", resp);
        false
    }
}

/// Send TXN requests to AC async, wait for and check the responses from AC.
/// Return only the responses of accepted TXN requests.
/// Ignore but count both gRPC-failed submissions and AC-rejected TXNs.
pub fn submit_and_wait_txn_requests(
    client: &AdmissionControlClient,
    txn_requests: &[SubmitTransactionRequest],
) -> Vec<ProtoSubmitTransactionResponse> {
    let futures: Vec<_> = txn_requests
        .iter()
        .filter_map(|req| {
            match client.submit_transaction_async_opt(&req, get_default_grpc_call_option()) {
                Ok(future) => Some(future),
                Err(e) => {
                    OP_COUNTER.inc(&format!("submit_txns.{:?}", e));
                    error!("Failed to send gRPC request: {:?}", e);
                    None
                }
            }
        })
        .collect();
    // Wait all the futures unorderedly, then pick only accepted responses.
    stream::futures_unordered(futures)
        .wait()
        .filter_map(|future_result| match future_result {
            Ok(proto_resp) => {
                if check_ac_response(&proto_resp) {
                    Some(proto_resp)
                } else {
                    None
                }
            }
            Err(e) => {
                OP_COUNTER.inc(&format!("submit_txns.{:?}", e));
                error!("Failed to receive gRPC response: {:?}", e);
                None
            }
        })
        .collect()
}

/// ------------------------------------------------------------ ///
///  Account state async request and response handling helpers.  ///
/// ------------------------------------------------------------ ///

/// Send account state request async with a AC client.
fn get_account_state_async(
    client: &AdmissionControlClient,
    address: &AccountAddress,
) -> Result<impl Future<Item = UpdateToLatestLedgerResponse, Error = failure::Error>> {
    let requested_item = RequestItem::GetAccountState { address: *address };
    let requested_items = vec![requested_item];
    let req = UpdateToLatestLedgerRequest::new(0, requested_items);
    let proto_req = req.into_proto();
    let ret = client
        .update_to_latest_ledger_async_opt(&proto_req, get_default_grpc_call_option())?
        .then(move |account_state_proof_resp| {
            let resp = UpdateToLatestLedgerResponse::from_proto(account_state_proof_resp?)?;
            Ok(resp)
        });
    Ok(ret)
}

/// Wait and process the response of account state request.
/// For valid response, return account's sequence number and status.
fn handle_account_state_response(
    future_resp: impl Future<Item = UpdateToLatestLedgerResponse, Error = failure::Error>,
) -> Result<(u64, AccountStatus)> {
    let mut response = future_resp.wait()?;
    let account_state_proof = response
        .response_items
        .remove(0)
        .into_get_account_state_response()?;
    if let Some(account_state_blob) = account_state_proof.blob {
        let account_resource = get_account_resource_or_default(&Some(account_state_blob))?;
        Ok((account_resource.sequence_number(), AccountStatus::Persisted))
    } else {
        bail!("failed to get account state because account doesn't exist")
    }
}

/// Query a bunch of accounts' sequence numbers and status from an validator.
/// If operation for one account fails, during either querying or handling, return immediately.
/// When everything is fine, the return vector, whose item is (sequence_number, account_status),
/// is ensured to be in consistent order with the given account_address vector.
pub fn get_account_states(
    client: &AdmissionControlClient,
    account_address: &[AccountAddress],
) -> Result<Vec<(u64, AccountStatus)>> {
    let future_resps: Result<Vec<_>> = account_address
        .iter()
        .map(|address| get_account_state_async(client, address))
        .collect();
    future_resps?
        .into_iter()
        .map(handle_account_state_response)
        .collect()
}

#[cfg(test)]
mod tests {
    use crate::divide_items;

    #[test]
    fn test_divide_items() {
        let items: Vec<_> = (0..4).collect();
        let mut iter1 = divide_items(&items, 3);
        assert_eq!(iter1.next().unwrap(), &[0]);
        assert_eq!(iter1.next().unwrap(), &[1]);
        assert_eq!(iter1.next().unwrap(), &[2]);
        assert_eq!(iter1.next().unwrap(), &[3]);

        let mut iter2 = divide_items(&items, 2);
        assert_eq!(iter2.next().unwrap(), &[0, 1]);
        assert_eq!(iter2.next().unwrap(), &[2, 3]);

        let mut iter3 = divide_items(&items, 0);
        assert_eq!(iter3.next().unwrap(), &[0, 1, 2, 3]);

        let empty_slice: Vec<u32> = vec![];
        let mut empty_iter = divide_items(&empty_slice, 3);
        assert!(empty_iter.next().is_none());
        let mut empty_iter = divide_items(&empty_slice, 0);
        assert!(empty_iter.next().is_none());
    }
}
<|endoftext|>


<commit_before>use redox::ops::DerefMut;
use redox::string::*;
use redox::vec::Vec;
use redox::boxed::Box;
use redox::fs::file::*;
use redox::io::*;
use redox::console::*;
use redox::env::*;
use redox::to_num::*;

/* Magic Macros { */
static mut application: *mut Application = 0 as *mut Application;

/// Execute a command
macro_rules! exec {
    ($cmd:expr) => ({
        unsafe {
            (*application).on_command(&$cmd.to_string());
        }
    })
}
/* } Magic Macros */

/// A command
pub struct Command {
    pub name: String,
    pub main: Box<Fn(&Vec<String>)>,
}

impl Command {
    /// Return the vector of the commands
    // TODO: Use a more efficient collection instead
    pub fn vec() -> Vec<Self> {
        let mut commands: Vec<Self> = Vec::new();
        commands.push(Command {
            name: "echo".to_string(),
            main: box |args: &Vec<String>| {
                let mut echo = String::new();
                let mut first = true;
                for i in 1..args.len() {
                    if let Some(arg) = args.get(i) {
                        if first {
                            first = false
                        } else {
                            echo = echo + " ";
                        }
                        echo = echo + arg;
                    }
                }
                println!("{}", echo);
            },
        });

        commands.push(Command {
            name: "open".to_string(),
            main: box |args: &Vec<String>| {
                if let Some(arg) = args.get(1) {
                    File::exec(arg);
                }
            },
        });

        commands.push(Command {
            name: "run".to_string(),
            main: box |args: &Vec<String>| {
                if let Some(arg) = args.get(1) {
                    let path = arg.clone();
                    println!("URL: {}", path);

                    let mut commands = String::new();
                    if let Some(mut file) = File::open(&path) {
                        file.read_to_string(&mut commands);
                    }

                    for command in commands.split('\n') {
                        exec!(command);
                    }
                }
            },
        });

        commands.push(Command {
            name: "send".to_string(),
            main: box |args: &Vec<String>| {
                let path;
                match args.get(1) {
                    Some(arg) => path = arg.clone(),
                    None => path = String::new(),
                }
                println!("URL: {}", path);

                if let Some(mut file) = File::open(&path) {
                    let mut string = String::new();
                    for i in 2..args.len() {
                        if let Some(arg) = args.get(i) {
                            if i >= 3 {
                                string.push_str(" ");
                            }
                            string.push_str(arg);
                        }
                    }
                    string.push_str("\r\n\r\n");

                    match file.write(&string.as_bytes()) {
                        Some(size) => println!("Wrote {} bytes", size),
                        None => println!("Failed to write"),
                    }

                    string = String::new();
                    match file.read_to_string(&mut string) {
                        Some(_) => println!("{}", string),
                        None => println!("Failed to read"),
                    }
                }
            },
        });

        commands.push(Command {
            name: "url".to_string(),
            main: box |args: &Vec<String>| {
                let path;
                match args.get(1) {
                    Some(arg) => path = arg.clone(),
                    None => path = String::new(),
                }
                println!("URL: {}", path);

                if let Some(mut file) = File::open(&path) {
                    let mut string = String::new();
                    match file.read_to_string(&mut string) {
                        Some(_) => println!("{}", string),
                        None => println!("Failed to read"),
                    }
                }
            },
        });

        commands.push(Command {
            name: "url_hex".to_string(),
            main: box |args: &Vec<String>| {
                let path;
                match args.get(1) {
                    Some(arg) => path = arg.clone(),
                    None => path = String::new(),
                }
                println!("URL: {}", path);

                if let Some(mut file) = File::open(&path) {
                    let mut vec: Vec<u8> = Vec::new();
                    match file.read_to_end(&mut vec) {
                        Some(_) => {
                            let mut line = "HEX:".to_string();
                            for byte in vec.iter() {
                                line = line + " " + &format!("{:X}", *byte);
                            }
                            println!("{}", line);
                        }
                        None => println!("Failed to read"),
                    }
                }
            },
        });

        commands.push(Command {
            name: "wget".to_string(),
            main: box |args: &Vec<String>| {
                if let Some(host) = args.get(1) {
                    if let Some(req) = args.get(2) {
                        if let Some(mut con) = File::open(&("tcp://".to_string() + host)) {
                            con.write(("GET ".to_string() + req + " HTTP/1.1").as_bytes());

                            let mut res = Vec::new();
                            con.read_to_end(&mut res);

                            if let Some(mut file) = File::open(&req) {
                                file.write(&res);
                            }
                        }
                    } else {
                        println!("No request given");
                    }
                } else {
                    println!("No url given");
                }
            },
        });

        let command_list = commands.iter().fold(String::new(), |l , c| l + " " + &c.name);

        commands.push(Command {
            name: "help".to_string(),
            main: box move |args: &Vec<String>| {
                println!("Commands:{}", command_list);
            },
         });

        commands
    }
}

/// A (env) variable
pub struct Variable {
    pub name: String,
    pub value: String,
}

pub struct Mode {
    value: bool,
}

/// An application
pub struct Application {
    commands: Vec<Command>,
    variables: Vec<Variable>,
    modes: Vec<Mode>,
}

impl Application {
    /// Create a new empty application
    pub fn new() -> Self {
        return Application {
            commands: Command::vec(),
            variables: Vec::new(),
            modes: Vec::new(),
        };
    }

    fn on_command(&mut self, command_string: &String) {
        //Comment
        if command_string.starts_with('#') {
            return;
        }

        //Show variables
        if *command_string == "$" {
            let mut variables = String::new();
            for variable in self.variables.iter() {
                variables = variables + "\n" + &variable.name + "=" + &variable.value;
            }
            println!("{}", variables);
            return;
        }

        //Explode into arguments, replace variables
        let mut args: Vec<String> = Vec::<String>::new();
        for arg in command_string.split(' ') {
            if arg.len() > 0 {
                if arg.starts_with('$') {
                    let name = arg[1 .. arg.len()].to_string();
                    for variable in self.variables.iter() {
                        if variable.name == name {
                            args.push(variable.value.clone());
                            break;
                        }
                    }
                } else {
                    args.push(arg.to_string());
                }
            }
        }

        //Execute commands
        if let Some(cmd) = args.get(0) {
            if cmd == "if" {
                let mut value = false;

                if let Some(left) = args.get(1) {
                    if let Some(cmp) = args.get(2) {
                        if let Some(right) = args.get(3) {
                            if cmp == "==" {
                                value = *left == *right;
                            } else if cmp == "!=" {
                                value = *left != *right;
                            } else if cmp == ">" {
                                value = left.to_num_signed() > right.to_num_signed();
                            } else if cmp == ">=" {
                                value = left.to_num_signed() >= right.to_num_signed();
                            } else if cmp == "<" {
                                value = left.to_num_signed() < right.to_num_signed();
                            } else if cmp == "<=" {
                                value = left.to_num_signed() <= right.to_num_signed();
                            } else {
                                println!("Unknown comparison: {}", cmp);
                            }
                        }
                    }
                }

                self.modes.insert(0, Mode { value: value });
                return;
            }

            if cmd == "else" {
                let mut syntax_error = false;
                match self.modes.get_mut(0) {
                    Some(mode) => mode.value = !mode.value,
                    None => syntax_error = true,
                }
                if syntax_error {
                    println!("Syntax error: else found with no previous if");
                }
                return;
            }

            if cmd == "fi" {
                let mut syntax_error = false;
                if self.modes.len() > 0 {
                    self.modes.remove(0);
                } else {
                    syntax_error = true;
                }
                if syntax_error {
                    println!("Syntax error: fi found with no previous if");
                }
                return;
            }

            for mode in self.modes.iter() {
                if !mode.value {
                    return;
                }
            }

            //Set variables
            if let Some(i) = cmd.find('=') {
                let name = cmd[0 .. i].to_string();
                let mut value = cmd[i + 1 .. cmd.len()].to_string();

                if name.len() == 0 {
                    return;
                }

                for i in 1..args.len() {
                    if let Some(arg) = args.get(i) {
                        value = value + " " + &arg;
                    }
                }

                if value.len() == 0 {
                    let mut remove = -1;
                    for i in 0..self.variables.len() {
                        match self.variables.get(i) {
                            Some(variable) => if variable.name == name {
                                remove = i as isize;
                                break;
                            },
                            None => break,
                        }
                    }

                    if remove >= 0 {
                        self.variables.remove(remove as usize);
                    }
                } else {
                    for variable in self.variables.iter_mut() {
                        if variable.name == name {
                            variable.value = value;
                            return;
                        }
                    }

                    self.variables.push(Variable {
                        name: name,
                        value: value,
                    });
                }
                return;
            }

            //Commands
            for command in self.commands.iter() {
                if &command.name == cmd {
                    (*command.main)(&args);
                    return;
                }
            }

            println!("Unknown command: '{}'", cmd);
        }
    }

    /// Run the application
    pub fn main(&mut self) {
        console_title("Terminal");

        println!("Type help for a command list");
        if let Some(arg) = args().get(1) {
            let command = "run ".to_string() + arg;
            println!("# {}", command);
            self.on_command(&command);
        }

        while let Some(command) = readln!() {
            println!("# {}", command);
            if command.len() > 0 {
                self.on_command(&command);
            }
        }
    }
}

pub fn main() {
    unsafe {
        let mut app = box Application::new();
        application = app.deref_mut();
        app.main();
    }
}
<commit_msg>Add `exit` command to terminal<commit_after>use redox::ops::DerefMut;
use redox::string::*;
use redox::vec::Vec;
use redox::boxed::Box;
use redox::fs::file::*;
use redox::io::*;
use redox::console::*;
use redox::env::*;
use redox::to_num::*;
use redox::syscall;

/* Magic Macros { */
static mut application: *mut Application = 0 as *mut Application;

/// Execute a command
macro_rules! exec {
    ($cmd:expr) => ({
        unsafe {
            (*application).on_command(&$cmd.to_string());
        }
    })
}
/* } Magic Macros */

/// A command
pub struct Command {
    pub name: String,
    pub main: Box<Fn(&Vec<String>)>,
}

impl Command {
    /// Return the vector of the commands
    // TODO: Use a more efficient collection instead
    pub fn vec() -> Vec<Self> {
        let mut commands: Vec<Self> = Vec::new();
        commands.push(Command {
            name: "echo".to_string(),
            main: box |args: &Vec<String>| {
                let mut echo = String::new();
                let mut first = true;
                for i in 1..args.len() {
                    if let Some(arg) = args.get(i) {
                        if first {
                            first = false
                        } else {
                            echo = echo + " ";
                        }
                        echo = echo + arg;
                    }
                }
                println!("{}", echo);
            },
        });

        commands.push(Command {
            name: "open".to_string(),
            main: box |args: &Vec<String>| {
                if let Some(arg) = args.get(1) {
                    File::exec(arg);
                }
            },
        });

        commands.push(Command {
            name: "run".to_string(),
            main: box |args: &Vec<String>| {
                if let Some(arg) = args.get(1) {
                    let path = arg.clone();
                    println!("URL: {}", path);

                    let mut commands = String::new();
                    if let Some(mut file) = File::open(&path) {
                        file.read_to_string(&mut commands);
                    }

                    for command in commands.split('\n') {
                        exec!(command);
                    }
                }
            },
        });

        commands.push(Command {
            name: "send".to_string(),
            main: box |args: &Vec<String>| {
                let path;
                match args.get(1) {
                    Some(arg) => path = arg.clone(),
                    None => path = String::new(),
                }
                println!("URL: {}", path);

                if let Some(mut file) = File::open(&path) {
                    let mut string = String::new();
                    for i in 2..args.len() {
                        if let Some(arg) = args.get(i) {
                            if i >= 3 {
                                string.push_str(" ");
                            }
                            string.push_str(arg);
                        }
                    }
                    string.push_str("\r\n\r\n");

                    match file.write(&string.as_bytes()) {
                        Some(size) => println!("Wrote {} bytes", size),
                        None => println!("Failed to write"),
                    }

                    string = String::new();
                    match file.read_to_string(&mut string) {
                        Some(_) => println!("{}", string),
                        None => println!("Failed to read"),
                    }
                }
            },
        });

        commands.push(Command {
            name: "url".to_string(),
            main: box |args: &Vec<String>| {
                let path;
                match args.get(1) {
                    Some(arg) => path = arg.clone(),
                    None => path = String::new(),
                }
                println!("URL: {}", path);

                if let Some(mut file) = File::open(&path) {
                    let mut string = String::new();
                    match file.read_to_string(&mut string) {
                        Some(_) => println!("{}", string),
                        None => println!("Failed to read"),
                    }
                }
            },
        });

        commands.push(Command {
            name: "url_hex".to_string(),
            main: box |args: &Vec<String>| {
                let path;
                match args.get(1) {
                    Some(arg) => path = arg.clone(),
                    None => path = String::new(),
                }
                println!("URL: {}", path);

                if let Some(mut file) = File::open(&path) {
                    let mut vec: Vec<u8> = Vec::new();
                    match file.read_to_end(&mut vec) {
                        Some(_) => {
                            let mut line = "HEX:".to_string();
                            for byte in vec.iter() {
                                line = line + " " + &format!("{:X}", *byte);
                            }
                            println!("{}", line);
                        }
                        None => println!("Failed to read"),
                    }
                }
            },
        });

        commands.push(Command {
            name: "wget".to_string(),
            main: box |args: &Vec<String>| {
                if let Some(host) = args.get(1) {
                    if let Some(req) = args.get(2) {
                        if let Some(mut con) = File::open(&("tcp://".to_string() + host)) {
                            con.write(("GET ".to_string() + req + " HTTP/1.1").as_bytes());

                            let mut res = Vec::new();
                            con.read_to_end(&mut res);

                            if let Some(mut file) = File::open(&req) {
                                file.write(&res);
                            }
                        }
                    } else {
                        println!("No request given");
                    }
                } else {
                    println!("No url given");
                }
            },
        });

        let command_list = commands.iter().fold(String::new(), |l , c| l + " " + &c.name) + " exit";

        commands.push(Command {
            name: "help".to_string(),
            main: box move |args: &Vec<String>| {
                println!("Commands:{}", command_list);
            },
         });

        commands
    }
}

/// A (env) variable
pub struct Variable {
    pub name: String,
    pub value: String,
}

pub struct Mode {
    value: bool,
}

/// An application
pub struct Application {
    commands: Vec<Command>,
    variables: Vec<Variable>,
    modes: Vec<Mode>,
}

impl Application {
    /// Create a new empty application
    pub fn new() -> Self {
        return Application {
            commands: Command::vec(),
            variables: Vec::new(),
            modes: Vec::new(),
        };
    }

    fn on_command(&mut self, command_string: &String) {
        //Comment
        if command_string.starts_with('#') {
            return;
        }

        //Show variables
        if *command_string == "$" {
            let mut variables = String::new();
            for variable in self.variables.iter() {
                variables = variables + "\n" + &variable.name + "=" + &variable.value;
            }
            println!("{}", variables);
            return;
        }

        //Explode into arguments, replace variables
        let mut args: Vec<String> = Vec::<String>::new();
        for arg in command_string.split(' ') {
            if arg.len() > 0 {
                if arg.starts_with('$') {
                    let name = arg[1 .. arg.len()].to_string();
                    for variable in self.variables.iter() {
                        if variable.name == name {
                            args.push(variable.value.clone());
                            break;
                        }
                    }
                } else {
                    args.push(arg.to_string());
                }
            }
        }

        //Execute commands
        if let Some(cmd) = args.get(0) {
            if cmd == "if" {
                let mut value = false;

                if let Some(left) = args.get(1) {
                    if let Some(cmp) = args.get(2) {
                        if let Some(right) = args.get(3) {
                            if cmp == "==" {
                                value = *left == *right;
                            } else if cmp == "!=" {
                                value = *left != *right;
                            } else if cmp == ">" {
                                value = left.to_num_signed() > right.to_num_signed();
                            } else if cmp == ">=" {
                                value = left.to_num_signed() >= right.to_num_signed();
                            } else if cmp == "<" {
                                value = left.to_num_signed() < right.to_num_signed();
                            } else if cmp == "<=" {
                                value = left.to_num_signed() <= right.to_num_signed();
                            } else {
                                println!("Unknown comparison: {}", cmp);
                            }
                        }
                    }
                }

                self.modes.insert(0, Mode { value: value });
                return;
            }

            if cmd == "else" {
                let mut syntax_error = false;
                match self.modes.get_mut(0) {
                    Some(mode) => mode.value = !mode.value,
                    None => syntax_error = true,
                }
                if syntax_error {
                    println!("Syntax error: else found with no previous if");
                }
                return;
            }

            if cmd == "fi" {
                let mut syntax_error = false;
                if self.modes.len() > 0 {
                    self.modes.remove(0);
                } else {
                    syntax_error = true;
                }
                if syntax_error {
                    println!("Syntax error: fi found with no previous if");
                }
                return;
            }

            for mode in self.modes.iter() {
                if !mode.value {
                    return;
                }
            }

            //Set variables
            if let Some(i) = cmd.find('=') {
                let name = cmd[0 .. i].to_string();
                let mut value = cmd[i + 1 .. cmd.len()].to_string();

                if name.len() == 0 {
                    return;
                }

                for i in 1..args.len() {
                    if let Some(arg) = args.get(i) {
                        value = value + " " + &arg;
                    }
                }

                if value.len() == 0 {
                    let mut remove = -1;
                    for i in 0..self.variables.len() {
                        match self.variables.get(i) {
                            Some(variable) => if variable.name == name {
                                remove = i as isize;
                                break;
                            },
                            None => break,
                        }
                    }

                    if remove >= 0 {
                        self.variables.remove(remove as usize);
                    }
                } else {
                    for variable in self.variables.iter_mut() {
                        if variable.name == name {
                            variable.value = value;
                            return;
                        }
                    }

                    self.variables.push(Variable {
                        name: name,
                        value: value,
                    });
                }
                return;
            }

            //Commands
            for command in self.commands.iter() {
                if &command.name == cmd {
                    (*command.main)(&args);
                    return;
                }
            }

            println!("Unknown command: '{}'", cmd);
        }
    }

    /// Run the application
    pub fn main(&mut self) {
        console_title("Terminal");

        println!("Type help for a command list");
        if let Some(arg) = args().get(1) {
            let command = "run ".to_string() + arg;
            println!("# {}", command);
            self.on_command(&command);
        }

        while let Some(command) = readln!() {
            println!("# {}", command);
            if command == "exit" {
                break;
            } else if command.len() > 0 {
                self.on_command(&command);
            }
        }
    }
}

pub fn main() {
    unsafe {
        let mut app = box Application::new();
        application = app.deref_mut();
        app.main();
    }
}
<|endoftext|>


<commit_before>use core::ops::DerefMut;
use redox::ion::command::{Application};

pub fn main() {
    unsafe {
        let mut app = box Application::new();
        application = app.deref_mut();
        app.main();
    }
}
<commit_msg>Fixed issue regarding terminal not compiling<commit_after>use core::ops::DerefMut;
use redox::ion::command::{Application};
static mut application: *mut Application = 0 as *mut Application;
pub fn main() {
    unsafe {
        let mut app = box Application::new();
        application = app.deref_mut();
        app.main();
    }
}
<|endoftext|>