<commit_before>//! Data structure for a small block of terrain.

use cgmath::{Point, Point3, Vector3, Aabb, Aabb3};
use isosurface_extraction::dual_contouring;
use num::iter::range_inclusive;
use std::f32;
use std::collections::HashMap;
use std::sync::Mutex;
use stopwatch;
use voxel_data;

use common::entity_id;
use common::id_allocator;
use common::voxel;
// TODO: Move the server-only parts to the server, like BLOCK_WIDTH and sample_info.

use block_position;
use lod;
use terrain_block;

/// Number of LODs
pub const LOD_COUNT: usize = 4;
/// lg(WIDTH)
pub const LG_WIDTH: i16 = 3;
/// The width of a block of terrain.
pub const WIDTH: i32 = 1 << LG_WIDTH;

/// lg(EDGE_SAMPLES)
pub const LG_EDGE_SAMPLES: [u16; LOD_COUNT] = [1, 1, 1, 0];
/// The number of voxels along an axis within a block, indexed by LOD.
pub const EDGE_SAMPLES: [u16; LOD_COUNT] = [
  1 << LG_EDGE_SAMPLES[0],
  1 << LG_EDGE_SAMPLES[1],
  1 << LG_EDGE_SAMPLES[2],
  1 << LG_EDGE_SAMPLES[3],
];

/// The width of a voxel within a block, indexed by LOD.
pub const LG_SAMPLE_SIZE: [i16; LOD_COUNT] = [
  LG_WIDTH - LG_EDGE_SAMPLES[0] as i16,
  LG_WIDTH - LG_EDGE_SAMPLES[1] as i16,
  LG_WIDTH - LG_EDGE_SAMPLES[2] as i16,
  LG_WIDTH - LG_EDGE_SAMPLES[3] as i16,
];

#[derive(Debug, Copy, Clone, RustcEncodable, RustcDecodable)]
/// [T; 3], but serializable.
pub struct Triangle<T> {
  #[allow(missing_docs)]
  pub v1: T,
  #[allow(missing_docs)]
  pub v2: T,
  #[allow(missing_docs)]
  pub v3: T,
}

/// Construct a triangle.
pub fn tri<T>(v1: T, v2: T, v3: T) -> Triangle<T> {
  Triangle {
    v1: v1,
    v2: v2,
    v3: v3,
  }
}

fn make_bounds(
  v1: &Point3<f32>,
  v2: &Point3<f32>,
  v3: &Point3<f32>,
) -> Aabb3<f32> {
  let minx = f32::min(v1.x, f32::min(v2.x, v3.x));
  let maxx = f32::max(v1.x, f32::max(v2.x, v3.x));

  let miny = f32::min(v1.y, f32::min(v2.y, v3.y));
  let maxy = f32::max(v1.y, f32::max(v2.y, v3.y));

  let minz = f32::min(v1.z, f32::min(v2.z, v3.z));
  let maxz = f32::max(v1.z, f32::max(v2.z, v3.z));

  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),
  )
}

pub fn voxels_in(bounds: &Aabb3<i32>, lg_size: i16) -> Vec<voxel_data::bounds::T> {
  let delta = bounds.max().sub_p(bounds.min());

  // assert that lg_size samples fit neatly into the bounds.
  let mod_size = (1 << lg_size) - 1;
  assert!(bounds.min().x & mod_size == 0);
  assert!(bounds.min().y & mod_size == 0);
  assert!(bounds.min().z & mod_size == 0);
  assert!(bounds.max().x & mod_size == 0);
  assert!(bounds.max().y & mod_size == 0);
  assert!(bounds.max().z & mod_size == 0);

  let mut voxels = Vec::new();
  for dx in 0 .. delta.x >> lg_size {
  for dy in 0 .. delta.y >> lg_size {
  for dz in 0 .. delta.z >> lg_size {
    let x = (bounds.min().x >> lg_size) + dx;
    let y = (bounds.min().y >> lg_size) + dy;
    let z = (bounds.min().z >> lg_size) + dz;
    voxels.push(voxel_data::bounds::new(x, y, z, lg_size));
  }}}
  voxels
}

mod voxel_storage {
  use voxel_data;
  use isosurface_extraction::dual_contouring;

  use common::voxel;

  use super::Partial;

  fn get_voxel<'a>(this: &'a mut Partial, bounds: &voxel_data::bounds::T) -> Option<&'a voxel::T> {
    debug!("Getting {:?} from {:?}", bounds, this.position);
    this.voxels.get(bounds)
  }

  impl<'a> dual_contouring::voxel_storage::T<voxel::Material> for Partial {
    fn get_material(&mut self, bounds: &voxel_data::bounds::T) -> Option<voxel::Material> {
      match get_voxel(self, bounds) {
        None => None,
        Some(&voxel::Surface(ref voxel)) => Some(voxel.corner.clone()),
        Some(&voxel::Volume(ref material)) => Some(material.clone()),
      }
    }

    fn get_voxel_data(&mut self, bounds: &voxel_data::bounds::T) -> Option<dual_contouring::voxel_storage::VoxelData> {
      match get_voxel(self, bounds) {
        None => None,
        Some(&voxel::Volume(_)) => panic!("Can't extract voxel data from a volume"),
        Some(&voxel::Surface(ref voxel)) =>
          Some({
            dual_contouring::voxel_storage::VoxelData {
              bounds: bounds.clone(),
              vertex: voxel.surface_vertex.to_world_vertex(&bounds),
              normal: voxel.normal.to_float_normal(),
            }
          })
      }
    }
  }
}

pub struct Partial {
  position: block_position::T,
  lod: lod::T,
  voxels: HashMap<voxel_data::bounds::T, voxel::T>,
}

impl Partial {
  pub fn new(position: block_position::T, lod: lod::T) -> Partial {
    Partial {
      position: position,
      lod: lod,
      voxels: HashMap::new(),
    }
  }

  pub fn lod(&self) -> lod::T {
    self.lod
  }

  pub fn add(&mut self, voxel: voxel::T, bounds: voxel_data::bounds::T) {
    self.voxels.insert(bounds, voxel);
  }

  pub fn finalize(
    &mut self,
    id_allocator: &Mutex<id_allocator::T<entity_id::T>>,
  ) -> Option<T>
  {
    // We load a buffer of one voxel around each chunk so we can render seams.
    // TODO: PartialBlock::voxels_to_fetch();
    let edge_samples = terrain_block::EDGE_SAMPLES[self.lod.0 as usize] as usize + 2;
    let samples = edge_samples * edge_samples * edge_samples;
    assert!(self.voxels.len() <= samples);
    trace!("len {:?} out of {:?}", self.voxels.len(), samples);
    if self.voxels.len() < samples {
      return None
    }

    stopwatch::time("terrain_block::finalize", || {
      let mut block = empty();

      let lg_edge_samples = LG_EDGE_SAMPLES[self.lod.0 as usize];
      let lg_sample_size = LG_SAMPLE_SIZE[self.lod.0 as usize];

      let low = self.position.as_pnt().clone();
      let high = low.add_v(&Vector3::new(1, 1, 1));
      let low =
        Point3::new(
          low.x << lg_edge_samples,
          low.y << lg_edge_samples,
          low.z << lg_edge_samples,
        );
      let high =
        Point3::new(
          high.x << lg_edge_samples,
          high.y << lg_edge_samples,
          high.z << lg_edge_samples,
        );

      trace!("low {:?}", low);
      trace!("high {:?}", high);

      {
        let mut edges = |direction, low_x, high_x, low_y, high_y, low_z, high_z| {
          for x in range_inclusive(low_x, high_x) {
          for y in range_inclusive(low_y, high_y) {
          for z in range_inclusive(low_z, high_z) {
            trace!("edge: {:?} {:?}", direction, Point3::new(x, y, z));
            let edge =
              dual_contouring::edge::T {
                low_corner: Point3::new(x, y, z),
                direction: direction,
                lg_size: lg_sample_size,
              };

            dual_contouring::edge::extract(
              self,
              &edge,
              &mut |polygon: dual_contouring::polygon::T<voxel::Material>| {
                let id = id_allocator.lock().unwrap().allocate();

                block.vertex_coordinates.push(tri(polygon.vertices[0], polygon.vertices[1], polygon.vertices[2]));
                block.normals.push(tri(polygon.normals[0], polygon.normals[1], polygon.normals[2]));
                block.materials.push(polygon.material as i32);
                block.ids.push(id);
                block.bounds.push((id, make_bounds(&polygon.vertices[0], &polygon.vertices[1], &polygon.vertices[2])));
              }
            );
          }}}
        };

        edges(
          dual_contouring::edge::Direction::X,
          low.x, high.x - 1,
          low.y, high.y - 1,
          low.z, high.z - 1,
        );
        edges(
          dual_contouring::edge::Direction::Y,
          low.x, high.x - 1,
          low.y, high.y - 1,
          low.z, high.z - 1,
        );
        edges(
          dual_contouring::edge::Direction::Z,
          low.x, high.x - 1,
          low.y, high.y - 1,
          low.z, high.z - 1,
        );
      }

      Some(block)
    })
  }
}

#[derive(Debug, Clone, RustcEncodable, RustcDecodable)]
/// A small continguous chunk of terrain.
pub struct T {
  // These Vecs must all be ordered the same way; each entry is the next triangle.

  /// Position of each vertex.
  pub vertex_coordinates: Vec<Triangle<Point3<f32>>>,
  /// Vertex normals. These should be normalized!
  pub normals: Vec<Triangle<Vector3<f32>>>,
  /// Entity IDs for each triangle.
  pub ids: Vec<entity_id::T>,
  /// Material IDs for each triangle.
  pub materials: Vec<i32>,
  // TODO: Change this back to a HashMap once initial capacity is zero for those.
  /// Per-triangle bounding boxes.
  pub bounds: Vec<(entity_id::T, Aabb3<f32>)>,
}

/// Construct an empty `T`.
pub fn empty() -> T {
  T {
    vertex_coordinates: Vec::new(),
    normals: Vec::new(),

    ids: Vec::new(),
    materials: Vec::new(),
    bounds: Vec::new(),
  }
}
<commit_msg>higher LOD again<commit_after>//! Data structure for a small block of terrain.

use cgmath::{Point, Point3, Vector3, Aabb, Aabb3};
use isosurface_extraction::dual_contouring;
use num::iter::range_inclusive;
use std::f32;
use std::collections::HashMap;
use std::sync::Mutex;
use stopwatch;
use voxel_data;

use common::entity_id;
use common::id_allocator;
use common::voxel;
// TODO: Move the server-only parts to the server, like BLOCK_WIDTH and sample_info.

use block_position;
use lod;
use terrain_block;

/// Number of LODs
pub const LOD_COUNT: usize = 4;
/// lg(WIDTH)
pub const LG_WIDTH: i16 = 3;
/// The width of a block of terrain.
pub const WIDTH: i32 = 1 << LG_WIDTH;

/// lg(EDGE_SAMPLES)
pub const LG_EDGE_SAMPLES: [u16; LOD_COUNT] = [3, 2, 1, 0];
/// The number of voxels along an axis within a block, indexed by LOD.
pub const EDGE_SAMPLES: [u16; LOD_COUNT] = [
  1 << LG_EDGE_SAMPLES[0],
  1 << LG_EDGE_SAMPLES[1],
  1 << LG_EDGE_SAMPLES[2],
  1 << LG_EDGE_SAMPLES[3],
];

/// The width of a voxel within a block, indexed by LOD.
pub const LG_SAMPLE_SIZE: [i16; LOD_COUNT] = [
  LG_WIDTH - LG_EDGE_SAMPLES[0] as i16,
  LG_WIDTH - LG_EDGE_SAMPLES[1] as i16,
  LG_WIDTH - LG_EDGE_SAMPLES[2] as i16,
  LG_WIDTH - LG_EDGE_SAMPLES[3] as i16,
];

#[derive(Debug, Copy, Clone, RustcEncodable, RustcDecodable)]
/// [T; 3], but serializable.
pub struct Triangle<T> {
  #[allow(missing_docs)]
  pub v1: T,
  #[allow(missing_docs)]
  pub v2: T,
  #[allow(missing_docs)]
  pub v3: T,
}

/// Construct a triangle.
pub fn tri<T>(v1: T, v2: T, v3: T) -> Triangle<T> {
  Triangle {
    v1: v1,
    v2: v2,
    v3: v3,
  }
}

fn make_bounds(
  v1: &Point3<f32>,
  v2: &Point3<f32>,
  v3: &Point3<f32>,
) -> Aabb3<f32> {
  let minx = f32::min(v1.x, f32::min(v2.x, v3.x));
  let maxx = f32::max(v1.x, f32::max(v2.x, v3.x));

  let miny = f32::min(v1.y, f32::min(v2.y, v3.y));
  let maxy = f32::max(v1.y, f32::max(v2.y, v3.y));

  let minz = f32::min(v1.z, f32::min(v2.z, v3.z));
  let maxz = f32::max(v1.z, f32::max(v2.z, v3.z));

  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),
  )
}

pub fn voxels_in(bounds: &Aabb3<i32>, lg_size: i16) -> Vec<voxel_data::bounds::T> {
  let delta = bounds.max().sub_p(bounds.min());

  // assert that lg_size samples fit neatly into the bounds.
  let mod_size = (1 << lg_size) - 1;
  assert!(bounds.min().x & mod_size == 0);
  assert!(bounds.min().y & mod_size == 0);
  assert!(bounds.min().z & mod_size == 0);
  assert!(bounds.max().x & mod_size == 0);
  assert!(bounds.max().y & mod_size == 0);
  assert!(bounds.max().z & mod_size == 0);

  let mut voxels = Vec::new();
  for dx in 0 .. delta.x >> lg_size {
  for dy in 0 .. delta.y >> lg_size {
  for dz in 0 .. delta.z >> lg_size {
    let x = (bounds.min().x >> lg_size) + dx;
    let y = (bounds.min().y >> lg_size) + dy;
    let z = (bounds.min().z >> lg_size) + dz;
    voxels.push(voxel_data::bounds::new(x, y, z, lg_size));
  }}}
  voxels
}

mod voxel_storage {
  use voxel_data;
  use isosurface_extraction::dual_contouring;

  use common::voxel;

  use super::Partial;

  fn get_voxel<'a>(this: &'a mut Partial, bounds: &voxel_data::bounds::T) -> Option<&'a voxel::T> {
    debug!("Getting {:?} from {:?}", bounds, this.position);
    this.voxels.get(bounds)
  }

  impl<'a> dual_contouring::voxel_storage::T<voxel::Material> for Partial {
    fn get_material(&mut self, bounds: &voxel_data::bounds::T) -> Option<voxel::Material> {
      match get_voxel(self, bounds) {
        None => None,
        Some(&voxel::Surface(ref voxel)) => Some(voxel.corner.clone()),
        Some(&voxel::Volume(ref material)) => Some(material.clone()),
      }
    }

    fn get_voxel_data(&mut self, bounds: &voxel_data::bounds::T) -> Option<dual_contouring::voxel_storage::VoxelData> {
      match get_voxel(self, bounds) {
        None => None,
        Some(&voxel::Volume(_)) => panic!("Can't extract voxel data from a volume"),
        Some(&voxel::Surface(ref voxel)) =>
          Some({
            dual_contouring::voxel_storage::VoxelData {
              bounds: bounds.clone(),
              vertex: voxel.surface_vertex.to_world_vertex(&bounds),
              normal: voxel.normal.to_float_normal(),
            }
          })
      }
    }
  }
}

pub struct Partial {
  position: block_position::T,
  lod: lod::T,
  voxels: HashMap<voxel_data::bounds::T, voxel::T>,
}

impl Partial {
  pub fn new(position: block_position::T, lod: lod::T) -> Partial {
    Partial {
      position: position,
      lod: lod,
      voxels: HashMap::new(),
    }
  }

  pub fn lod(&self) -> lod::T {
    self.lod
  }

  pub fn add(&mut self, voxel: voxel::T, bounds: voxel_data::bounds::T) {
    self.voxels.insert(bounds, voxel);
  }

  pub fn finalize(
    &mut self,
    id_allocator: &Mutex<id_allocator::T<entity_id::T>>,
  ) -> Option<T>
  {
    // We load a buffer of one voxel around each chunk so we can render seams.
    // TODO: PartialBlock::voxels_to_fetch();
    let edge_samples = terrain_block::EDGE_SAMPLES[self.lod.0 as usize] as usize + 2;
    let samples = edge_samples * edge_samples * edge_samples;
    assert!(self.voxels.len() <= samples);
    trace!("len {:?} out of {:?}", self.voxels.len(), samples);
    if self.voxels.len() < samples {
      return None
    }

    stopwatch::time("terrain_block::finalize", || {
      let mut block = empty();

      let lg_edge_samples = LG_EDGE_SAMPLES[self.lod.0 as usize];
      let lg_sample_size = LG_SAMPLE_SIZE[self.lod.0 as usize];

      let low = self.position.as_pnt().clone();
      let high = low.add_v(&Vector3::new(1, 1, 1));
      let low =
        Point3::new(
          low.x << lg_edge_samples,
          low.y << lg_edge_samples,
          low.z << lg_edge_samples,
        );
      let high =
        Point3::new(
          high.x << lg_edge_samples,
          high.y << lg_edge_samples,
          high.z << lg_edge_samples,
        );

      trace!("low {:?}", low);
      trace!("high {:?}", high);

      {
        let mut edges = |direction, low_x, high_x, low_y, high_y, low_z, high_z| {
          for x in range_inclusive(low_x, high_x) {
          for y in range_inclusive(low_y, high_y) {
          for z in range_inclusive(low_z, high_z) {
            trace!("edge: {:?} {:?}", direction, Point3::new(x, y, z));
            let edge =
              dual_contouring::edge::T {
                low_corner: Point3::new(x, y, z),
                direction: direction,
                lg_size: lg_sample_size,
              };

            dual_contouring::edge::extract(
              self,
              &edge,
              &mut |polygon: dual_contouring::polygon::T<voxel::Material>| {
                let id = id_allocator.lock().unwrap().allocate();

                block.vertex_coordinates.push(tri(polygon.vertices[0], polygon.vertices[1], polygon.vertices[2]));
                block.normals.push(tri(polygon.normals[0], polygon.normals[1], polygon.normals[2]));
                block.materials.push(polygon.material as i32);
                block.ids.push(id);
                block.bounds.push((id, make_bounds(&polygon.vertices[0], &polygon.vertices[1], &polygon.vertices[2])));
              }
            );
          }}}
        };

        edges(
          dual_contouring::edge::Direction::X,
          low.x, high.x - 1,
          low.y, high.y - 1,
          low.z, high.z - 1,
        );
        edges(
          dual_contouring::edge::Direction::Y,
          low.x, high.x - 1,
          low.y, high.y - 1,
          low.z, high.z - 1,
        );
        edges(
          dual_contouring::edge::Direction::Z,
          low.x, high.x - 1,
          low.y, high.y - 1,
          low.z, high.z - 1,
        );
      }

      Some(block)
    })
  }
}

#[derive(Debug, Clone, RustcEncodable, RustcDecodable)]
/// A small continguous chunk of terrain.
pub struct T {
  // These Vecs must all be ordered the same way; each entry is the next triangle.

  /// Position of each vertex.
  pub vertex_coordinates: Vec<Triangle<Point3<f32>>>,
  /// Vertex normals. These should be normalized!
  pub normals: Vec<Triangle<Vector3<f32>>>,
  /// Entity IDs for each triangle.
  pub ids: Vec<entity_id::T>,
  /// Material IDs for each triangle.
  pub materials: Vec<i32>,
  // TODO: Change this back to a HashMap once initial capacity is zero for those.
  /// Per-triangle bounding boxes.
  pub bounds: Vec<(entity_id::T, Aabb3<f32>)>,
}

/// Construct an empty `T`.
pub fn empty() -> T {
  T {
    vertex_coordinates: Vec::new(),
    normals: Vec::new(),

    ids: Vec::new(),
    materials: Vec::new(),
    bounds: Vec::new(),
  }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::path::Path;
use std::io::Read;
// use std::rand;
use std::env;

use display::Display;
use keypad::Keypad;

pub struct Cpu {
    opcode: u16,
    memory: [u8; 4096],
    v: [u8; 16],
    i: usize,
    pc: usize,
    stack: [u16; 16],
    sp: usize,
    delay_timer: u8,
    sound_timer: u8,
    pub keypad: Keypad,
    pub display: Display
}

impl Cpu {
    pub fn new() -> Cpu {
        let mut cpu = Cpu {
            opcode: 0,
            memory: [0; 4096],
            v: [0; 16],
            i: 0x200,
            pc: 0x200,
            stack: [0; 16],
            sp: 0,
            delay_timer: 0,
            sound_timer: 0,
            keypad: Keypad::new(),
            display: Display::new()
        };

        for i in 0..80 {
            cpu.memory[i] = fontset[i];
        }
        cpu
    }

    pub fn emulate_cycle(&mut self) {
        self.fetch_opcode();
        self.opcode_execute();

        if self.delay_timer > 0 {
            self.delay_timer -= 1;
        }

        if self.sound_timer > 0 {
            if self.sound_timer == 1 {
                println!("BEEP!\n");
            }
            self.sound_timer -= 1;
        }

        for _ in 0..10000 {
        }
    }

    pub fn load_game(&mut self, program: String) {
        let mut path = env::current_dir().unwrap();
        path.push(program.trim());
        let mut reader = File::open(&path).unwrap();
        // KG fixme: problem is here.
        self.load_to_memory(&mut reader);
    }

    fn load_to_memory(&mut self, reader: &mut File) {
        match reader.bytes().next() {
            Some(value) => {
                match value {
                    Ok(value) => {
                        self.memory[self.pc] = value;
                        self.pc += 1;
                        self.load_to_memory(reader)
                    },
                    Err(e) => {panic!("{:?}", e)}
                }
            },
            None => self.pc = 0x200,
        };
    }

    fn fetch_opcode(&mut self) {
        self.opcode = (self.memory[self.pc] as u16) << 8 | (self.memory[self.pc + 1] as u16);
    }

    fn opcode_execute(&mut self) {
        match (self.opcode & 0xf000) {
            // 0x0000 => self.op_0xxx(),
            // 0x1000 => self.op_1xxx(),
            // 0x2000 => self.op_2xxx(),
            // 0x3000 => self.op_3xxx(),
            // 0x4000 => self.op_4xxx(),
            // 0x5000 => self.op_5xxx(),
            // 0x6000 => self.op_6xxx(),
            // 0x7000 => self.op_7xxx(),
            // 0x8000 => self.op_8xxx(),
            // 0x9000 => self.op_9xxx(),
            // 0xA000 => self.op_Axxx(),
            // 0xB000 => self.op_Bxxx(),
            // 0xC000 => self.op_Cxxx(),
            // 0xD000 => self.op_Dxxx(),
            // 0xE000 => self.op_Exxx(),
            // 0xF000 => self.op_Fxxx(),
            _ => not_implemented(self.opcode as usize, self.pc),
        }
    }
}

fn not_implemented(op: usize, pc: usize) {
    println!("Not implemented:: op: {:x}, pc: {:x}", op, pc)
}

static fontset: [u8; 80] =
    [0xF0, 0x90, 0x90, 0x90, 0xF0, 0x20, 0x60, 0x20, 0x20, 0x70, 0xF0, 0x10, 0xF0, 0x80, 0xF0,
     0xF0, 0x10, 0xF0, 0x10, 0xF0, 0x90, 0x90, 0xF0, 0x10, 0x10, 0xF0, 0x80, 0xF0, 0x10, 0xF0,
     0xF0, 0x80, 0xF0, 0x90, 0xF0, 0xF0, 0x10, 0x20, 0x40, 0x40, 0xF0, 0x90, 0xF0, 0x90, 0xF0,
     0xF0, 0x90, 0xF0, 0x10, 0xF0, 0xF0, 0x90, 0xF0, 0x90, 0x90, 0xE0, 0x90, 0xE0, 0x90, 0xE0,
     0xF0, 0x80, 0x80, 0x80, 0xF0, 0xE0, 0x90, 0x90, 0x90, 0xE0, 0xF0, 0x80, 0xF0, 0x80, 0xF0,
     0xF0, 0x80, 0xF0, 0x80, 0x80];
<commit_msg>Fixed the error. Now for the opcodes<commit_after>use std::fs::File;
use std::path::Path;
use std::io::Read;
// use std::rand;
use std::env;

use display::Display;
use keypad::Keypad;

pub struct Cpu {
    opcode: u16,
    memory: [u8; 4096],
    v: [u8; 16],
    i: usize,
    pc: usize,
    stack: [u16; 16],
    sp: usize,
    delay_timer: u8,
    sound_timer: u8,
    pub keypad: Keypad,
    pub display: Display
}

impl Cpu {
    pub fn new() -> Cpu {
        let mut cpu = Cpu {
            opcode: 0,
            memory: [0; 4096],
            v: [0; 16],
            i: 0x200,
            pc: 0x200,
            stack: [0; 16],
            sp: 0,
            delay_timer: 0,
            sound_timer: 0,
            keypad: Keypad::new(),
            display: Display::new()
        };

        for i in 0..80 {
            cpu.memory[i] = fontset[i];
        }
        cpu
    }

    pub fn emulate_cycle(&mut self) {
        self.fetch_opcode();
        self.opcode_execute();

        if self.delay_timer > 0 {
            self.delay_timer -= 1;
        }

        if self.sound_timer > 0 {
            if self.sound_timer == 1 {
                println!("BEEP!\n");
            }
            self.sound_timer -= 1;
        }

        for _ in 0..10000 {
        }
    }

    pub fn load_game(&mut self, program: String) {
        let mut path = env::current_dir().unwrap();
        path.push(program.trim());
        let display = path.display();
        let mut reader = match File::open(&path) {
            Err(why) => panic!("Couldn't open {}: {}", display, why),
            Ok(reader) => reader,
        };
        self.load_to_memory(&mut reader);
    }

    fn load_to_memory(&mut self, reader: &mut File) {
        match reader.bytes().next() {
            Some(value) => {
                match value {
                    Ok(value) => {
                        self.memory[self.pc] = value;
                        self.pc += 1;
                        self.load_to_memory(reader)
                    },
                    Err(e) => {panic!("{:?}", e)}
                }
            },
            None => self.pc = 0x200,
        };
    }

    fn fetch_opcode(&mut self) {
        self.opcode = (self.memory[self.pc] as u16) << 8 | (self.memory[self.pc + 1] as u16);
    }

    fn opcode_execute(&mut self) {
        match (self.opcode & 0xf000) {
            // 0x0000 => self.op_0xxx(),
            // 0x1000 => self.op_1xxx(),
            // 0x2000 => self.op_2xxx(),
            // 0x3000 => self.op_3xxx(),
            // 0x4000 => self.op_4xxx(),
            // 0x5000 => self.op_5xxx(),
            // 0x6000 => self.op_6xxx(),
            // 0x7000 => self.op_7xxx(),
            // 0x8000 => self.op_8xxx(),
            // 0x9000 => self.op_9xxx(),
            // 0xA000 => self.op_Axxx(),
            // 0xB000 => self.op_Bxxx(),
            // 0xC000 => self.op_Cxxx(),
            // 0xD000 => self.op_Dxxx(),
            // 0xE000 => self.op_Exxx(),
            // 0xF000 => self.op_Fxxx(),
            _ => not_implemented(self.opcode as usize, self.pc),
        }
    }
}

fn not_implemented(op: usize, pc: usize) {
    println!("Not implemented:: op: {:x}, pc: {:x}", op, pc)
}

static fontset: [u8; 80] =
    [0xF0, 0x90, 0x90, 0x90, 0xF0, 0x20, 0x60, 0x20, 0x20, 0x70, 0xF0, 0x10, 0xF0, 0x80, 0xF0,
     0xF0, 0x10, 0xF0, 0x10, 0xF0, 0x90, 0x90, 0xF0, 0x10, 0x10, 0xF0, 0x80, 0xF0, 0x10, 0xF0,
     0xF0, 0x80, 0xF0, 0x90, 0xF0, 0xF0, 0x10, 0x20, 0x40, 0x40, 0xF0, 0x90, 0xF0, 0x90, 0xF0,
     0xF0, 0x90, 0xF0, 0x10, 0xF0, 0xF0, 0x90, 0xF0, 0x90, 0x90, 0xE0, 0x90, 0xE0, 0x90, 0xE0,
     0xF0, 0x80, 0x80, 0x80, 0xF0, 0xE0, 0x90, 0x90, 0x90, 0xE0, 0xF0, 0x80, 0xF0, 0x80, 0xF0,
     0xF0, 0x80, 0xF0, 0x80, 0x80];
<|endoftext|>


<commit_before>#![crate_name = "uu_more"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Martin Kysel <code@martinkysel.com>
 *
 * For the full copyright and license information, please view the LICENSE file
 * that was distributed with this source code.
 */

extern crate getopts;

#[macro_use]
extern crate uucore;

use getopts::Options;
use std::fs::File;
use std::io::{stdout, Read, Write};

#[cfg(all(unix, not(target_os = "fuchsia")))]
extern crate nix;
#[cfg(all(unix, not(target_os = "fuchsia")))]
use nix::sys::termios;

#[cfg(target_os = "redox")]
extern crate redox_termios;
#[cfg(target_os = "redox")]
extern crate syscall;

#[derive(Clone, Eq, PartialEq)]
pub enum Mode {
    More,
    Help,
    Version,
}

static NAME: &str = "more";
static VERSION: &str = env!("CARGO_PKG_VERSION");

pub fn uumain(args: Vec<String>) -> i32 {
    let mut opts = Options::new();

    opts.optflag("h", "help", "display this help and exit");
    opts.optflag("v", "version", "output version information and exit");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(e) => {
            show_error!("{}", e);
            panic!()
        }
    };
    let usage = opts.usage("more TARGET.");
    let mode = if matches.opt_present("version") {
        Mode::Version
    } else if matches.opt_present("help") {
        Mode::Help
    } else {
        Mode::More
    };

    match mode {
        Mode::More => more(matches),
        Mode::Help => help(&usage),
        Mode::Version => version(),
    }

    0
}

fn version() {
    println!("{} {}", NAME, VERSION);
}

fn help(usage: &str) {
    let msg = format!(
        "{0} {1}\n\n\
         Usage: {0} TARGET\n  \
         \n\
         {2}",
        NAME, VERSION, usage
    );
    println!("{}", msg);
}

#[cfg(all(unix, not(target_os = "fuchsia")))]
fn setup_term() -> termios::Termios {
    let mut term = termios::tcgetattr(0).unwrap();
    // Unset canonical mode, so we get characters immediately
    term.c_lflag.remove(termios::ICANON);
    // Disable local echo
    term.c_lflag.remove(termios::ECHO);
    termios::tcsetattr(0, termios::TCSADRAIN, &term).unwrap();
    term
}

#[cfg(any(windows, target_os = "fuchsia"))]
#[inline(always)]
fn setup_term() -> usize {
    0
}

#[cfg(target_os = "redox")]
fn setup_term() -> redox_termios::Termios {
    let mut term = redox_termios::Termios::default();
    let fd = syscall::dup(0, b"termios").unwrap();
    syscall::read(fd, &mut term).unwrap();
    term.c_lflag &= !redox_termios::ICANON;
    term.c_lflag &= !redox_termios::ECHO;
    syscall::write(fd, &term).unwrap();
    let _ = syscall::close(fd);
    term
}

#[cfg(all(unix, not(target_os = "fuchsia")))]
fn reset_term(term: &mut termios::Termios) {
    term.c_lflag.insert(termios::ICANON);
    term.c_lflag.insert(termios::ECHO);
    termios::tcsetattr(0, termios::TCSADRAIN, &term).unwrap();
}

#[cfg(any(windows, target_os = "fuchsia"))]
#[inline(always)]
fn reset_term(_: &mut usize) {}

#[cfg(any(target_os = "redox"))]
fn reset_term(term: &mut redox_termios::Termios) {
    let fd = syscall::dup(0, b"termios").unwrap();
    syscall::read(fd, term).unwrap();
    term.c_lflag |= redox_termios::ICANON;
    term.c_lflag |= redox_termios::ECHO;
    syscall::write(fd, &term).unwrap();
    let _ = syscall::close(fd);
}

fn more(matches: getopts::Matches) {
    let files = matches.free;
    let mut f = File::open(files.first().unwrap()).unwrap();
    let mut buffer = [0; 1024];

    let mut term = setup_term();

    let mut end = false;
    while let Ok(sz) = f.read(&mut buffer) {
        if sz == 0 {
            break;
        }
        stdout().write_all(&buffer[0..sz]).unwrap();
        for byte in std::io::stdin().bytes() {
            match byte.unwrap() {
                b' ' => break,
                b'q' | 27 => {
                    end = true;
                    break;
                }
                _ => (),
            }
        }

        if end {
            break;
        }
    }

    reset_term(&mut term);
    println!();
}
<commit_msg>fix(more) Return a proper error message when no argument is provided<commit_after>#![crate_name = "uu_more"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Martin Kysel <code@martinkysel.com>
 *
 * For the full copyright and license information, please view the LICENSE file
 * that was distributed with this source code.
 */

extern crate getopts;

#[macro_use]
extern crate uucore;

use getopts::Options;
use std::fs::File;
use std::io::{stdout, Read, Write};

#[cfg(all(unix, not(target_os = "fuchsia")))]
extern crate nix;
#[cfg(all(unix, not(target_os = "fuchsia")))]
use nix::sys::termios;

#[cfg(target_os = "redox")]
extern crate redox_termios;
#[cfg(target_os = "redox")]
extern crate syscall;

#[derive(Clone, Eq, PartialEq)]
pub enum Mode {
    More,
    Help,
    Version,
}

static NAME: &str = "more";
static VERSION: &str = env!("CARGO_PKG_VERSION");

pub fn uumain(args: Vec<String>) -> i32 {
    let mut opts = Options::new();

    // FixME: fail without panic for now; but `more` should work with no arguments (ie, for piped input)
    if args.len() < 2 {
        println!("{}: incorrect usage", args[0]);
        return 1;
    }

    opts.optflag("h", "help", "display this help and exit");
    opts.optflag("v", "version", "output version information and exit");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(e) => {
            show_error!("{}", e);
            panic!()
        }
    };
    let usage = opts.usage("more TARGET.");
    let mode = if matches.opt_present("version") {
        Mode::Version
    } else if matches.opt_present("help") {
        Mode::Help
    } else {
        Mode::More
    };

    match mode {
        Mode::More => more(matches),
        Mode::Help => help(&usage),
        Mode::Version => version(),
    }

    0
}

fn version() {
    println!("{} {}", NAME, VERSION);
}

fn help(usage: &str) {
    let msg = format!(
        "{0} {1}\n\n\
         Usage: {0} TARGET\n  \
         \n\
         {2}",
        NAME, VERSION, usage
    );
    println!("{}", msg);
}

#[cfg(all(unix, not(target_os = "fuchsia")))]
fn setup_term() -> termios::Termios {
    let mut term = termios::tcgetattr(0).unwrap();
    // Unset canonical mode, so we get characters immediately
    term.c_lflag.remove(termios::ICANON);
    // Disable local echo
    term.c_lflag.remove(termios::ECHO);
    termios::tcsetattr(0, termios::TCSADRAIN, &term).unwrap();
    term
}

#[cfg(any(windows, target_os = "fuchsia"))]
#[inline(always)]
fn setup_term() -> usize {
    0
}

#[cfg(target_os = "redox")]
fn setup_term() -> redox_termios::Termios {
    let mut term = redox_termios::Termios::default();
    let fd = syscall::dup(0, b"termios").unwrap();
    syscall::read(fd, &mut term).unwrap();
    term.c_lflag &= !redox_termios::ICANON;
    term.c_lflag &= !redox_termios::ECHO;
    syscall::write(fd, &term).unwrap();
    let _ = syscall::close(fd);
    term
}

#[cfg(all(unix, not(target_os = "fuchsia")))]
fn reset_term(term: &mut termios::Termios) {
    term.c_lflag.insert(termios::ICANON);
    term.c_lflag.insert(termios::ECHO);
    termios::tcsetattr(0, termios::TCSADRAIN, &term).unwrap();
}

#[cfg(any(windows, target_os = "fuchsia"))]
#[inline(always)]
fn reset_term(_: &mut usize) {}

#[cfg(any(target_os = "redox"))]
fn reset_term(term: &mut redox_termios::Termios) {
    let fd = syscall::dup(0, b"termios").unwrap();
    syscall::read(fd, term).unwrap();
    term.c_lflag |= redox_termios::ICANON;
    term.c_lflag |= redox_termios::ECHO;
    syscall::write(fd, &term).unwrap();
    let _ = syscall::close(fd);
}

fn more(matches: getopts::Matches) {
    let files = matches.free;
    let mut f = File::open(files.first().unwrap()).unwrap();
    let mut buffer = [0; 1024];

    let mut term = setup_term();

    let mut end = false;
    while let Ok(sz) = f.read(&mut buffer) {
        if sz == 0 {
            break;
        }
        stdout().write_all(&buffer[0..sz]).unwrap();
        for byte in std::io::stdin().bytes() {
            match byte.unwrap() {
                b' ' => break,
                b'q' | 27 => {
                    end = true;
                    break;
                }
                _ => (),
            }
        }

        if end {
            break;
        }
    }

    reset_term(&mut term);
    println!();
}
<|endoftext|>


<commit_before>use egui::{
    plot::{Bar, BarChart, Line, Plot, Value, Values},
    Color32, Context, Grid, Ui, Visuals,
};
use gba::{Command, Gba, GbaAudioSampler};
use parking_lot::Mutex;
use pyrite::{CallbackId, GbaHandle, GbaThreadState};
use std::{sync::Arc, time::Duration};
use util::circular::CircularBuffer;

#[derive(Default)]
pub struct Debugger {
    performance_pane: PerformancePane,
    audio_pane: AudioPane,

    has_initialized: bool,

    gba_data: GbaData,
    gba_data_buffer: Arc<Mutex<GbaData>>,
    frame_callback: Option<CallbackId>,
}

impl Debugger {
    pub fn render(&mut self, ctx: &Context, gba: &GbaHandle) {
        if !self.has_initialized {
            self.init(ctx, gba);
            self.has_initialized = true;
        } else {
            self.fetch_updated_data();

            if self.gba_data.updated {
                ctx.request_repaint();
            }
        }

        egui::CentralPanel::default().show(ctx, |ui| {
            // self.performance_pane.render(ui, &mut self.gba_data);
            self.audio_pane.render(ui, &mut self.gba_data);
        });
    }

    fn fetch_updated_data(&mut self) {
        let mut locked = self.gba_data_buffer.lock();
        self.gba_data.copy_data(&mut *locked);
    }

    #[cold]
    pub fn init(&mut self, ctx: &Context, gba: &GbaHandle) {
        let mut visuals = Visuals::dark();
        visuals.override_text_color = Some(Color32::from_rgb(0xe9, 0xec, 0xef));
        ctx.set_visuals(visuals);

        let gba_data_buffer = Arc::clone(&self.gba_data_buffer);
        let callback_id = gba.on_frame(move |gba, state| {
            let mut locked = gba_data_buffer.lock();
            pull_data_from_gba(&mut *locked, gba, state);
        });

        self.frame_callback = Some(callback_id);
    }

    pub fn destroy(&mut self, gba: &GbaHandle) {
        if let Some(cb) = self.frame_callback.take() {
            gba.remove_on_frame(cb);
        }
    }
}

#[derive(Default)]
struct AudioPane {
    samples_l: CircularBuffer<f32, { Self::BUFFER_SIZE }>,
    samples_r: CircularBuffer<f32, { Self::BUFFER_SIZE }>,
    commands_buffer_sizes: CircularBuffer<u32, { Self::FRAMES }>,
}

impl AudioPane {
    const RENDER_SAMPLES: u32 = 1024;
    const FRAMES: usize = 16;
    const BUFFER_SIZE: usize = Self::FRAMES * Self::RENDER_SAMPLES as usize;

    fn render(&mut self, ui: &mut Ui, data: &mut GbaData) {
        self.get_data(data);
        Grid::new("GBA Frame Durations Grid")
            .num_columns(2)
            .striped(true)
            .show(ui, |ui| {
                ui.label("Output");
                self.render_samples_plot(ui);
                ui.end_row();

                ui.label("Commands");
                self.render_commands_buffer_plot(ui);
                ui.end_row();
            });
    }

    fn render_commands_buffer_plot(&mut self, ui: &mut Ui) {
        let mut bars = Vec::with_capacity(self.commands_buffer_sizes.len());
        for (idx, &size) in self.commands_buffer_sizes.iter().enumerate() {
            bars.push(Bar::new(idx as f64, size as f64));
        }

        let chart = BarChart::new(bars).name("GBA Frame Processing Duration");
        Plot::new("Command Buffer Sizes")
            .allow_drag(false)
            .allow_zoom(false)
            .show(ui, |plot_ui| {
                plot_ui.bar_chart(chart);
            });
    }

    fn render_samples_plot(&mut self, ui: &mut Ui) {
        let samples_l = self
            .samples_l
            .iter()
            .enumerate()
            .map(|(idx, &s)| Value::new(idx as f64, s));
        let samples_r = self
            .samples_r
            .iter()
            .enumerate()
            .map(|(idx, &s)| Value::new(idx as f64, s));

        let line_l = Line::new(Values::from_values_iter(samples_l)).color(rgb(0x228be6));
        let line_r = Line::new(Values::from_values_iter(samples_r)).color(rgb(0xae3ec9));

        Plot::new("Samples")
            .allow_drag(true)
            .allow_zoom(true)
            .include_y(1.0)
            .include_y(-1.0)
            .show(ui, |plot_ui| {
                plot_ui.line(line_l);
                plot_ui.line(line_r);
            });
    }

    fn get_data(&mut self, data: &mut GbaData) {
        if std::mem::take(&mut data.has_audio_commands) {
            self.commands_buffer_sizes
                .push(data.audio_commands.len() as u32);
            let mut sampler = GbaAudioSampler::new(Self::RENDER_SAMPLES);
            for _ in 0..Self::RENDER_SAMPLES {
                while sampler.needs_commands() {
                    if let Some(command) = data.audio_commands.pop() {
                        sampler.command(command);
                    } else {
                        break;
                    }
                }

                let (l, r) = sampler.frame(1.0);
                self.samples_l.push(l);
                self.samples_r.push(r);
            }
        }
        data.requests.audio_data = true;
    }
}

#[derive(Default)]
struct PerformancePane {
    frame_times: CircularBuffer<f64, 32>,
    frame_processing_times: CircularBuffer<f64, 32>,
}

impl PerformancePane {
    const GBA_MAX_FRAME_DUR: f64 = 1000.0 / 60.0;

    const GOOD_FRAME_COLOR: Color32 = Color32::from_rgb(0x37, 0xb2, 0x4d);
    const BAD_FRAME_COLOR: Color32 = Color32::from_rgb(0xf0, 0x3e, 0x3e);

    fn render(&mut self, ui: &mut Ui, data: &mut GbaData) {
        if let Some(duration) = data.frame_duration.take() {
            self.frame_times.push(duration.as_secs_f64() * 1000.0);
        }

        if let Some(duration) = data.frame_processing_duration.take() {
            self.frame_processing_times
                .push(duration.as_secs_f64() * 1000.0);
        }

        self.render_frame_times(ui);
        data.requests.frame_duration = true;
    }

    fn render_frame_times_text(&mut self, ui: &mut Ui) {
        let average_dur =
            self.frame_times.iter().copied().sum::<f64>() / self.frame_times.len() as f64;
        ui.label("Average GBA Frame Duration");
        ui.label(format!("{average_dur:0.2}ms"));
        ui.end_row();

        let average_fps = 1000.0 / average_dur;
        ui.label("Average GBA FPS");
        ui.label(format!("{average_fps:0.2}"));
        ui.end_row();

        let average_perf = (Self::GBA_MAX_FRAME_DUR / average_dur) * 100.0;
        ui.label("Average GBA Performance");
        ui.label(format!("{average_perf:0.2}%"));
        ui.end_row();
    }

    fn render_processing_times_plot(&mut self, ui: &mut Ui) {
        use egui::plot::{BarChart, Plot};
        const PROCESSING_COLOR: Color32 = Color32::from_rgb(0x1c, 0x7e, 0xd6);
        let mut bars = Vec::with_capacity(self.frame_processing_times.len());
        for (idx, &t) in self.frame_processing_times.iter().skip(1).enumerate() {
            bars.push(Bar::new(idx as f64, t).fill(PROCESSING_COLOR));
        }

        if !self.frame_processing_times.is_empty() {
            bars.push(Bar::new(bars.len() as f64, 0.0));
        }

        let chart = BarChart::new(bars).name("GBA Frame Processing Duration");
        Plot::new("GBA Processing Durations")
            .show_x(false)
            .show_axes([false, false])
            .allow_drag(false)
            .allow_zoom(false)
            .include_y(Self::GBA_MAX_FRAME_DUR)
            .show(ui, |plot_ui| {
                plot_ui.bar_chart(chart);
            });
    }

    fn render_frame_times_plot(&mut self, ui: &mut Ui) {
        use egui::plot::{BarChart, HLine, Plot};

        let bar_color = |t: f64| -> Color32 {
            if t > 4.0 {
                Self::BAD_FRAME_COLOR
            } else {
                Self::GOOD_FRAME_COLOR
            }
        };

        let bars = self
            .frame_times
            .iter()
            .enumerate()
            .map(|(idx, &t)| Bar::new(idx as f64, t).fill(bar_color(t)))
            .collect();
        let chart = BarChart::new(bars).name("GBA Frame Processing Duration");
        Plot::new("GBA Frame Durations")
            .show_x(false)
            .show_axes([false, false])
            .allow_drag(false)
            .allow_zoom(false)
            .include_y(Self::GBA_MAX_FRAME_DUR)
            .show(ui, |plot_ui| {
                plot_ui.hline(HLine::new(4.0).color(Self::BAD_FRAME_COLOR));
                plot_ui.bar_chart(chart);
            });
    }

    fn render_frame_times(&mut self, ui: &mut Ui) {
        Grid::new("GBA Frame Durations Grid")
            .num_columns(2)
            .striped(true)
            .show(ui, |ui| {
                self.render_frame_times_text(ui);

                ui.label("GBA Frame Durations");
                self.render_frame_times_plot(ui);
                ui.end_row();

                ui.label("GBA Processing Durations");
                self.render_processing_times_plot(ui);
                ui.end_row();
            });
    }
}

#[derive(Default)]
struct GbaData {
    frame_duration: Option<Duration>,
    frame_processing_duration: Option<Duration>,

    audio_commands: Vec<Command>,
    has_audio_commands: bool,

    updated: bool,
    requests: GbaDataRequests,
}

impl GbaData {
    fn copy_data(&mut self, source: &mut GbaData) {
        if !source.updated {
            self.updated = false;
            return;
        }
        self.updated = true;
        source.updated = false;

        self.frame_duration = source.frame_duration.take();
        self.frame_processing_duration = source.frame_processing_duration.take();

        if self.requests.audio_data {
            self.audio_commands.clear();
            self.audio_commands
                .extend(source.audio_commands.iter().rev().copied());
            self.has_audio_commands = source.has_audio_commands;
        } else {
            self.has_audio_commands = false;
        }

        source.requests = std::mem::take(&mut self.requests);
    }
}

#[derive(Default)]
struct GbaDataRequests {
    frame_duration: bool,
    audio_data: bool,
}

fn pull_data_from_gba(data: &mut GbaData, gba: &mut Gba, state: &mut GbaThreadState) {
    if data.requests.frame_duration {
        data.frame_duration = Some(state.frame_duration());
        data.frame_processing_duration = Some(state.frame_processing_duration());
    }

    if data.requests.audio_data {
        data.audio_commands.clear();
        data.audio_commands.extend(gba.audio().commands());
        data.has_audio_commands = true;
    } else {
        data.has_audio_commands = false;
    }

    data.updated = true;
}

const fn rgb(col: u32) -> Color32 {
    Color32::from_rgb((col >> 16) as u8, (col >> 8) as u8, col as u8)
}
<commit_msg>Add UI for switching between debugger panes.<commit_after>use egui::{
    plot::{Bar, BarChart, HLine, Line, Plot, Value, Values},
    Color32, Context, Grid, Ui, Visuals,
};
use gba::{Command, Gba, GbaAudioSampler};
use parking_lot::Mutex;
use pyrite::{CallbackId, GbaHandle, GbaThreadState};
use std::{sync::Arc, time::Duration};
use util::circular::CircularBuffer;

#[derive(Default)]
pub struct Debugger {
    current_pane: Pane,
    performance_pane: PerformancePane,
    audio_pane: AudioPane,

    has_initialized: bool,

    gba_data: GbaData,
    gba_data_buffer: Arc<Mutex<GbaData>>,
    frame_callback: Option<CallbackId>,
}

impl Debugger {
    pub fn render(&mut self, ctx: &Context, gba: &GbaHandle) {
        if !self.has_initialized {
            self.init(ctx, gba);
            self.has_initialized = true;
        } else {
            self.fetch_updated_data();

            if self.gba_data.updated {
                ctx.request_repaint();
            }
        }

        egui::CentralPanel::default().show(ctx, |ui| {
            ui.horizontal(|ui| {
                ui.selectable_value(&mut self.current_pane, Pane::Performance, "Performance");
                ui.selectable_value(&mut self.current_pane, Pane::Audio, "Audio");
            });

            match self.current_pane {
                Pane::Performance => self.performance_pane.render(ui, &mut self.gba_data),
                Pane::Audio => self.audio_pane.render(ui, &mut self.gba_data),
            }
        });
    }

    fn fetch_updated_data(&mut self) {
        let mut locked = self.gba_data_buffer.lock();
        self.gba_data.copy_data(&mut *locked);
    }

    #[cold]
    pub fn init(&mut self, ctx: &Context, gba: &GbaHandle) {
        let mut visuals = Visuals::dark();
        visuals.override_text_color = Some(Color32::from_rgb(0xe9, 0xec, 0xef));
        ctx.set_visuals(visuals);

        let gba_data_buffer = Arc::clone(&self.gba_data_buffer);
        let callback_id = gba.on_frame(move |gba, state| {
            let mut locked = gba_data_buffer.lock();
            pull_data_from_gba(&mut *locked, gba, state);
        });

        self.frame_callback = Some(callback_id);
    }

    pub fn destroy(&mut self, gba: &GbaHandle) {
        if let Some(cb) = self.frame_callback.take() {
            gba.remove_on_frame(cb);
        }
    }
}

#[derive(PartialEq, Clone, Copy)]
enum Pane {
    Performance,
    Audio,
}

impl Default for Pane {
    fn default() -> Self {
        Self::Performance
    }
}

#[derive(Default)]
struct AudioPane {
    samples_l: CircularBuffer<f32, { Self::BUFFER_SIZE }>,
    samples_r: CircularBuffer<f32, { Self::BUFFER_SIZE }>,
    commands_buffer_sizes: CircularBuffer<u32, { Self::FRAMES }>,
}

impl AudioPane {
    const RENDER_SAMPLES: u32 = 1024;
    const FRAMES: usize = 16;
    const BUFFER_SIZE: usize = Self::FRAMES * Self::RENDER_SAMPLES as usize;

    fn render(&mut self, ui: &mut Ui, data: &mut GbaData) {
        self.get_data(data);
        Grid::new("GBA Frame Durations Grid")
            .num_columns(2)
            .striped(true)
            .show(ui, |ui| {
                ui.label("Output");
                self.render_samples_plot(ui);
                ui.end_row();

                ui.label("Commands");
                self.render_commands_buffer_plot(ui);
                ui.end_row();
            });
    }

    fn render_commands_buffer_plot(&mut self, ui: &mut Ui) {
        let mut bars = Vec::with_capacity(self.commands_buffer_sizes.len());
        for (idx, &size) in self.commands_buffer_sizes.iter().enumerate() {
            bars.push(Bar::new(idx as f64, size as f64));
        }

        let chart = BarChart::new(bars).name("Command Buffer Size Chart");
        Plot::new("Command Buffer Sizes")
            .allow_drag(false)
            .allow_zoom(false)
            .show(ui, |plot_ui| {
                plot_ui.bar_chart(chart);
            });
    }

    fn render_samples_plot(&mut self, ui: &mut Ui) {
        let samples_l = self
            .samples_l
            .iter()
            .enumerate()
            .map(|(idx, &s)| Value::new(idx as f64, s));
        let samples_r = self
            .samples_r
            .iter()
            .enumerate()
            .map(|(idx, &s)| Value::new(idx as f64, s));

        let line_l = Line::new(Values::from_values_iter(samples_l)).color(rgb(0x228be6));
        let line_r = Line::new(Values::from_values_iter(samples_r)).color(rgb(0xae3ec9));

        Plot::new("Samples")
            .show_axes([false, false])
            .allow_drag(true)
            .allow_zoom(true)
            .include_y(1.0)
            .include_y(-1.0)
            .show(ui, |plot_ui| {
                plot_ui.line(line_l);
                plot_ui.line(line_r);
            });
    }

    fn get_data(&mut self, data: &mut GbaData) {
        if std::mem::take(&mut data.has_audio_commands) {
            self.commands_buffer_sizes
                .push(data.audio_commands.len() as u32);
            let mut sampler = GbaAudioSampler::new(Self::RENDER_SAMPLES);
            for _ in 0..Self::RENDER_SAMPLES {
                while sampler.needs_commands() {
                    if let Some(command) = data.audio_commands.pop() {
                        sampler.command(command);
                    } else {
                        break;
                    }
                }

                let (l, r) = sampler.frame();
                self.samples_l.push(l);
                self.samples_r.push(r);
            }
        }
        data.requests.audio_data = true;
    }
}

#[derive(Default)]
struct PerformancePane {
    frame_times: CircularBuffer<f64, 32>,
    frame_processing_times: CircularBuffer<f64, 32>,
}

impl PerformancePane {
    const GBA_MAX_FRAME_DUR: f64 = 1000.0 / 60.0;

    const GOOD_FRAME_COLOR: Color32 = Color32::from_rgb(0x37, 0xb2, 0x4d);
    const BAD_FRAME_COLOR: Color32 = Color32::from_rgb(0xf0, 0x3e, 0x3e);

    fn render(&mut self, ui: &mut Ui, data: &mut GbaData) {
        if let Some(duration) = data.frame_duration.take() {
            self.frame_times.push(duration.as_secs_f64() * 1000.0);
        }

        if let Some(duration) = data.frame_processing_duration.take() {
            self.frame_processing_times
                .push(duration.as_secs_f64() * 1000.0);
        }

        self.render_frame_times(ui);
        data.requests.frame_duration = true;
    }

    fn render_frame_times_text(&mut self, ui: &mut Ui) {
        let average_dur =
            self.frame_times.iter().copied().sum::<f64>() / self.frame_times.len() as f64;
        ui.label("Average GBA Frame Duration");
        ui.label(format!("{average_dur:0.2}ms"));
        ui.end_row();

        let average_fps = 1000.0 / average_dur;
        ui.label("Average GBA FPS");
        ui.label(format!("{average_fps:0.2}"));
        ui.end_row();

        let average_perf = (Self::GBA_MAX_FRAME_DUR / average_dur) * 100.0;
        ui.label("Average GBA Performance");
        ui.label(format!("{average_perf:0.2}%"));
        ui.end_row();
    }

    fn render_processing_times_plot(&mut self, ui: &mut Ui) {
        const PROCESSING_COLOR: Color32 = Color32::from_rgb(0x1c, 0x7e, 0xd6);
        let mut bars = Vec::with_capacity(self.frame_processing_times.len());
        for (idx, &t) in self.frame_processing_times.iter().skip(1).enumerate() {
            bars.push(Bar::new(idx as f64, t).fill(PROCESSING_COLOR));
        }

        if !self.frame_processing_times.is_empty() {
            bars.push(Bar::new(bars.len() as f64, 0.0));
        }

        let chart = BarChart::new(bars).name("GBA Frame Processing Duration");
        Plot::new("GBA Processing Durations")
            .show_x(false)
            .show_axes([false, false])
            .allow_drag(false)
            .allow_zoom(false)
            .include_y(Self::GBA_MAX_FRAME_DUR)
            .show(ui, |plot_ui| {
                plot_ui.bar_chart(chart);
            });
    }

    fn render_frame_times_plot(&mut self, ui: &mut Ui) {
        let bar_color = |t: f64| -> Color32 {
            if t > 4.0 {
                Self::BAD_FRAME_COLOR
            } else {
                Self::GOOD_FRAME_COLOR
            }
        };

        let bars = self
            .frame_times
            .iter()
            .enumerate()
            .map(|(idx, &t)| Bar::new(idx as f64, t).fill(bar_color(t)))
            .collect();
        let chart = BarChart::new(bars).name("GBA Frame Processing Duration");
        Plot::new("GBA Frame Durations")
            .show_x(false)
            .show_axes([false, false])
            .allow_drag(false)
            .allow_zoom(false)
            .include_y(Self::GBA_MAX_FRAME_DUR)
            .show(ui, |plot_ui| {
                plot_ui.hline(HLine::new(4.0).color(Self::BAD_FRAME_COLOR));
                plot_ui.bar_chart(chart);
            });
    }

    fn render_frame_times(&mut self, ui: &mut Ui) {
        Grid::new("GBA Frame Durations Grid")
            .num_columns(2)
            .striped(true)
            .show(ui, |ui| {
                self.render_frame_times_text(ui);

                ui.label("GBA Frame Durations");
                self.render_frame_times_plot(ui);
                ui.end_row();

                ui.label("GBA Processing Durations");
                self.render_processing_times_plot(ui);
                ui.end_row();
            });
    }
}

#[derive(Default)]
struct GbaData {
    frame_duration: Option<Duration>,
    frame_processing_duration: Option<Duration>,

    audio_commands: Vec<Command>,
    has_audio_commands: bool,

    updated: bool,
    requests: GbaDataRequests,
}

impl GbaData {
    fn copy_data(&mut self, source: &mut GbaData) {
        if !source.updated {
            self.updated = false;
            return;
        }
        self.updated = true;
        source.updated = false;

        self.frame_duration = source.frame_duration.take();
        self.frame_processing_duration = source.frame_processing_duration.take();

        if self.requests.audio_data {
            self.audio_commands.clear();
            self.audio_commands
                .extend(source.audio_commands.iter().rev().copied());
            self.has_audio_commands = source.has_audio_commands;
        } else {
            self.has_audio_commands = false;
        }

        source.requests = std::mem::take(&mut self.requests);
    }
}

#[derive(Default)]
struct GbaDataRequests {
    frame_duration: bool,
    audio_data: bool,
}

fn pull_data_from_gba(data: &mut GbaData, gba: &mut Gba, state: &mut GbaThreadState) {
    if data.requests.frame_duration {
        data.frame_duration = Some(state.frame_duration());
        data.frame_processing_duration = Some(state.frame_processing_duration());
    }

    if data.requests.audio_data {
        data.audio_commands.clear();
        data.audio_commands.extend(gba.audio().commands());
        data.has_audio_commands = true;
    } else {
        data.has_audio_commands = false;
    }

    data.updated = true;
}

const fn rgb(col: u32) -> Color32 {
    Color32::from_rgb((col >> 16) as u8, (col >> 8) as u8, col as u8)
}
<|endoftext|>


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

use remove_dir_all::remove_dir_all;
use std::mem;
use std::path::{self, Path, PathBuf};
use std::{fmt, fs, io};

use crate::error::IoResultExt;
use crate::Builder;

/// Create a new temporary directory.
///
/// The `tempdir` function creates a directory in the file system
/// and returns a [`TempDir`].
/// The directory will be automatically deleted when the `TempDir`s
/// destructor is run.
///
/// # Resource Leaking
///
/// See [the resource leaking][resource-leaking] docs on `TempDir`.
///
/// # Errors
///
/// If the directory can not be created, `Err` is returned.
///
/// # Examples
///
/// ```
/// use tempfile::tempdir;
/// use std::fs::File;
/// use std::io::{self, Write};
///
/// # fn main() {
/// #     if let Err(_) = run() {
/// #         ::std::process::exit(1);
/// #     }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// // Create a directory inside of `std::env::temp_dir()`
/// let dir = tempdir()?;
///
/// let file_path = dir.path().join("my-temporary-note.txt");
/// let mut file = File::create(file_path)?;
/// writeln!(file, "Brian was here. Briefly.")?;
///
/// // `tmp_dir` goes out of scope, the directory as well as
/// // `tmp_file` will be deleted here.
/// drop(file);
/// dir.close()?;
/// # Ok(())
/// # }
/// ```
///
/// [`TempDir`]: struct.TempDir.html
/// [resource-leaking]: struct.TempDir.html#resource-leaking
pub fn tempdir() -> io::Result<TempDir> {
    TempDir::new()
}

/// Create a new temporary directory.
///
/// The `tempdir` function creates a directory in the file system
/// and returns a [`TempDir`].
/// The directory will be automatically deleted when the `TempDir`s
/// destructor is run.
///
/// # Resource Leaking
///
/// See [the resource leaking][resource-leaking] docs on `TempDir`.
///
/// # Errors
///
/// If the directory can not be created, `Err` is returned.
///
/// # Examples
///
/// ```
/// use tempfile::tempdir;
/// use std::fs::File;
/// use std::io::{self, Write};
///
/// # fn main() {
/// #     if let Err(_) = run() {
/// #         ::std::process::exit(1);
/// #     }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// // Create a directory inside of `std::env::temp_dir()`,
/// let dir = tempdir()?;
///
/// let file_path = dir.path().join("my-temporary-note.txt");
/// let mut file = File::create(file_path)?;
/// writeln!(file, "Brian was here. Briefly.")?;
///
/// // `tmp_dir` goes out of scope, the directory as well as
/// // `tmp_file` will be deleted here.
/// drop(file);
/// dir.close()?;
/// # Ok(())
/// # }
/// ```
///
/// [`TempDir`]: struct.TempDir.html
/// [resource-leaking]: struct.TempDir.html#resource-leaking
pub fn tempdir_in<P: AsRef<Path>>(dir: P) -> io::Result<TempDir> {
    TempDir::new_in(dir)
}

/// A directory in the filesystem that is automatically deleted when
/// it goes out of scope.
///
/// The [`TempDir`] type creates a directory on the file system that
/// is deleted once it goes out of scope. At construction, the
/// `TempDir` creates a new directory with a randomly generated name.
///
/// The default constructor, [`TempDir::new()`], creates directories in
/// the location returned by [`std::env::temp_dir()`], but `TempDir`
/// can be configured to manage a temporary directory in any location
/// by constructing with a [`Builder`].
///
/// After creating a `TempDir`, work with the file system by doing
/// standard [`std::fs`] file system operations on its [`Path`],
/// which can be retrieved with [`TempDir::path()`]. Once the `TempDir`
/// value is dropped, the directory at the path will be deleted, along
/// with any files and directories it contains. It is your responsibility
/// to ensure that no further file system operations are attempted
/// inside the temporary directory once it has been deleted.
///
/// # Resource Leaking
///
/// Various platform-specific conditions may cause `TempDir` to fail
/// to delete the underlying directory. It's important to ensure that
/// handles (like [`File`] and [`ReadDir`]) to files inside the
/// directory are dropped before the `TempDir` goes out of scope. The
/// `TempDir` destructor will silently ignore any errors in deleting
/// the directory; to instead handle errors call [`TempDir::close()`].
///
/// Note that if the program exits before the `TempDir` destructor is
/// run, such as via [`std::process::exit()`], by segfaulting, or by
/// receiving a signal like `SIGINT`, then the temporary directory
/// will not be deleted.
///
/// # Examples
///
/// Create a temporary directory with a generated name:
///
/// ```
/// use std::fs::File;
/// use std::io::Write;
/// use tempfile::TempDir;
///
/// # use std::io;
/// # fn run() -> Result<(), io::Error> {
/// // Create a directory inside of `std::env::temp_dir()`
/// let tmp_dir = TempDir::new()?;
/// # Ok(())
/// # }
/// ```
///
/// Create a temporary directory with a prefix in its name:
///
/// ```
/// use std::fs::File;
/// use std::io::Write;
/// use tempfile::Builder;
///
/// # use std::io;
/// # fn run() -> Result<(), io::Error> {
/// // Create a directory inside of `std::env::temp_dir()`,
/// // whose name will begin with 'example'.
/// let tmp_dir = Builder::new().prefix("example").tempdir()?;
/// # Ok(())
/// # }
/// ```
///
/// [`File`]: http://doc.rust-lang.org/std/fs/struct.File.html
/// [`Path`]: http://doc.rust-lang.org/std/path/struct.Path.html
/// [`ReadDir`]: http://doc.rust-lang.org/std/fs/struct.ReadDir.html
/// [`Builder`]: struct.Builder.html
/// [`TempDir::close()`]: struct.TempDir.html#method.close
/// [`TempDir::new()`]: struct.TempDir.html#method.new
/// [`TempDir::path()`]: struct.TempDir.html#method.path
/// [`TempDir`]: struct.TempDir.html
/// [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html
/// [`std::fs`]: http://doc.rust-lang.org/std/fs/index.html
/// [`std::process::exit()`]: http://doc.rust-lang.org/std/process/fn.exit.html
pub struct TempDir {
    path: Box<Path>,
}

impl TempDir {
    /// Attempts to make a temporary directory inside of `env::temp_dir()`.
    ///
    /// See [`Builder`] for more configuration.
    ///
    /// The directory and everything inside it will be automatically deleted
    /// once the returned `TempDir` is destroyed.
    ///
    /// # Errors
    ///
    /// If the directory can not be created, `Err` is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::File;
    /// use std::io::Write;
    /// use tempfile::TempDir;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// // Create a directory inside of `std::env::temp_dir()`
    /// let tmp_dir = TempDir::new()?;
    ///
    /// let file_path = tmp_dir.path().join("my-temporary-note.txt");
    /// let mut tmp_file = File::create(file_path)?;
    /// writeln!(tmp_file, "Brian was here. Briefly.")?;
    ///
    /// // `tmp_dir` goes out of scope, the directory as well as
    /// // `tmp_file` will be deleted here.
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// [`Builder`]: struct.Builder.html
    pub fn new() -> io::Result<TempDir> {
        Builder::new().tempdir()
    }

    /// Attempts to make a temporary directory inside of `dir`.
    /// The directory and everything inside it will be automatically
    /// deleted once the returned `TempDir` is destroyed.
    ///
    /// # Errors
    ///
    /// If the directory can not be created, `Err` is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::{self, File};
    /// use std::io::Write;
    /// use tempfile::TempDir;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// // Create a directory inside of the current directory
    /// let tmp_dir = TempDir::new_in(".")?;
    /// let file_path = tmp_dir.path().join("my-temporary-note.txt");
    /// let mut tmp_file = File::create(file_path)?;
    /// writeln!(tmp_file, "Brian was here. Briefly.")?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn new_in<P: AsRef<Path>>(dir: P) -> io::Result<TempDir> {
        Builder::new().tempdir_in(dir)
    }

    /// Accesses the [`Path`] to the temporary directory.
    ///
    /// [`Path`]: http://doc.rust-lang.org/std/path/struct.Path.html
    ///
    /// # Examples
    ///
    /// ```
    /// use tempfile::TempDir;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// let tmp_path;
    ///
    /// {
    ///    let tmp_dir = TempDir::new()?;
    ///    tmp_path = tmp_dir.path().to_owned();
    ///
    ///    // Check that the temp directory actually exists.
    ///    assert!(tmp_path.exists());
    ///
    ///    // End of `tmp_dir` scope, directory will be deleted
    /// }
    ///
    /// // Temp directory should be deleted by now
    /// assert_eq!(tmp_path.exists(), false);
    /// # Ok(())
    /// # }
    /// ```
    #[must_use]
    pub fn path(&self) -> &path::Path {
        self.path.as_ref()
    }

    /// Persist the temporary directory to disk, returning the [`PathBuf`] where it is located.
    ///
    /// This consumes the [`TempDir`] without deleting directory on the filesystem, meaning that
    /// the directory will no longer be automatically deleted.
    ///
    /// [`TempDir`]: struct.TempDir.html
    /// [`PathBuf`]: http://doc.rust-lang.org/std/path/struct.PathBuf.html
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs;
    /// use tempfile::TempDir;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// let tmp_dir = TempDir::new()?;
    ///
    /// // Persist the temporary directory to disk,
    /// // getting the path where it is.
    /// let tmp_path = tmp_dir.into_path();
    ///
    /// // Delete the temporary directory ourselves.
    /// fs::remove_dir_all(tmp_path)?;
    /// # Ok(())
    /// # }
    /// ```
    #[must_use]
    pub fn into_path(self) -> PathBuf {
        // Prevent the Drop impl from being called.
        let mut this = mem::ManuallyDrop::new(self);

        // replace this.path with an empty Box, since an empty Box does not
        // allocate any heap memory.
        mem::replace(&mut this.path, PathBuf::new().into_boxed_path()).into()
    }

    /// Closes and removes the temporary directory, returning a `Result`.
    ///
    /// Although `TempDir` removes the directory on drop, in the destructor
    /// any errors are ignored. To detect errors cleaning up the temporary
    /// directory, call `close` instead.
    ///
    /// # Errors
    ///
    /// This function may return a variety of [`std::io::Error`]s that result from deleting
    /// the files and directories contained with the temporary directory,
    /// as well as from deleting the temporary directory itself. These errors
    /// may be platform specific.
    ///
    /// [`std::io::Error`]: http://doc.rust-lang.org/std/io/struct.Error.html
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::File;
    /// use std::io::Write;
    /// use tempfile::TempDir;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// // Create a directory inside of `std::env::temp_dir()`.
    /// let tmp_dir = TempDir::new()?;
    /// let file_path = tmp_dir.path().join("my-temporary-note.txt");
    /// let mut tmp_file = File::create(file_path)?;
    /// writeln!(tmp_file, "Brian was here. Briefly.")?;
    ///
    /// // By closing the `TempDir` explicitly we can check that it has
    /// // been deleted successfully. If we don't close it explicitly,
    /// // the directory will still be deleted when `tmp_dir` goes out
    /// // of scope, but we won't know whether deleting the directory
    /// // succeeded.
    /// drop(tmp_file);
    /// tmp_dir.close()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn close(mut self) -> io::Result<()> {
        let result = remove_dir_all(self.path()).with_err_path(|| self.path());

        // Set self.path to empty Box to release the memory, since an empty
        // Box does not allocate any heap memory.
        self.path = PathBuf::new().into_boxed_path();

        // Prevent the Drop impl from being called.
        mem::forget(self);

        result
    }
}

impl AsRef<Path> for TempDir {
    fn as_ref(&self) -> &Path {
        self.path()
    }
}

impl fmt::Debug for TempDir {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("TempDir")
            .field("path", &self.path())
            .finish()
    }
}

impl Drop for TempDir {
    fn drop(&mut self) {
        let _ = remove_dir_all(self.path());
    }
}

pub(crate) fn create(path: PathBuf) -> io::Result<TempDir> {
    fs::create_dir(&path)
        .with_err_path(|| &path)
        .map(|_| TempDir {
            path: path.into_boxed_path(),
        })
}
<commit_msg>doc: fix tempdir_in documentation<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use remove_dir_all::remove_dir_all;
use std::mem;
use std::path::{self, Path, PathBuf};
use std::{fmt, fs, io};

use crate::error::IoResultExt;
use crate::Builder;

/// Create a new temporary directory.
///
/// The `tempdir` function creates a directory in the file system
/// and returns a [`TempDir`].
/// The directory will be automatically deleted when the `TempDir`s
/// destructor is run.
///
/// # Resource Leaking
///
/// See [the resource leaking][resource-leaking] docs on `TempDir`.
///
/// # Errors
///
/// If the directory can not be created, `Err` is returned.
///
/// # Examples
///
/// ```
/// use tempfile::tempdir;
/// use std::fs::File;
/// use std::io::{self, Write};
///
/// # fn main() {
/// #     if let Err(_) = run() {
/// #         ::std::process::exit(1);
/// #     }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// // Create a directory inside of `std::env::temp_dir()`
/// let dir = tempdir()?;
///
/// let file_path = dir.path().join("my-temporary-note.txt");
/// let mut file = File::create(file_path)?;
/// writeln!(file, "Brian was here. Briefly.")?;
///
/// // `tmp_dir` goes out of scope, the directory as well as
/// // `tmp_file` will be deleted here.
/// drop(file);
/// dir.close()?;
/// # Ok(())
/// # }
/// ```
///
/// [`TempDir`]: struct.TempDir.html
/// [resource-leaking]: struct.TempDir.html#resource-leaking
pub fn tempdir() -> io::Result<TempDir> {
    TempDir::new()
}

/// Create a new temporary directory in a specific directory.
///
/// The `tempdir_in` function creates a directory in the specified directory
/// and returns a [`TempDir`].
/// The directory will be automatically deleted when the `TempDir`s
/// destructor is run.
///
/// # Resource Leaking
///
/// See [the resource leaking][resource-leaking] docs on `TempDir`.
///
/// # Errors
///
/// If the directory can not be created, `Err` is returned.
///
/// # Examples
///
/// ```
/// use tempfile::tempdir_in;
/// use std::fs::File;
/// use std::io::{self, Write};
///
/// # fn main() {
/// #     if let Err(_) = run() {
/// #         ::std::process::exit(1);
/// #     }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// // Create a directory inside of the current directory.
/// let dir = tempdir_in(".")?;
///
/// let file_path = dir.path().join("my-temporary-note.txt");
/// let mut file = File::create(file_path)?;
/// writeln!(file, "Brian was here. Briefly.")?;
///
/// // `tmp_dir` goes out of scope, the directory as well as
/// // `tmp_file` will be deleted here.
/// drop(file);
/// dir.close()?;
/// # Ok(())
/// # }
/// ```
///
/// [`TempDir`]: struct.TempDir.html
/// [resource-leaking]: struct.TempDir.html#resource-leaking
pub fn tempdir_in<P: AsRef<Path>>(dir: P) -> io::Result<TempDir> {
    TempDir::new_in(dir)
}

/// A directory in the filesystem that is automatically deleted when
/// it goes out of scope.
///
/// The [`TempDir`] type creates a directory on the file system that
/// is deleted once it goes out of scope. At construction, the
/// `TempDir` creates a new directory with a randomly generated name.
///
/// The default constructor, [`TempDir::new()`], creates directories in
/// the location returned by [`std::env::temp_dir()`], but `TempDir`
/// can be configured to manage a temporary directory in any location
/// by constructing with a [`Builder`].
///
/// After creating a `TempDir`, work with the file system by doing
/// standard [`std::fs`] file system operations on its [`Path`],
/// which can be retrieved with [`TempDir::path()`]. Once the `TempDir`
/// value is dropped, the directory at the path will be deleted, along
/// with any files and directories it contains. It is your responsibility
/// to ensure that no further file system operations are attempted
/// inside the temporary directory once it has been deleted.
///
/// # Resource Leaking
///
/// Various platform-specific conditions may cause `TempDir` to fail
/// to delete the underlying directory. It's important to ensure that
/// handles (like [`File`] and [`ReadDir`]) to files inside the
/// directory are dropped before the `TempDir` goes out of scope. The
/// `TempDir` destructor will silently ignore any errors in deleting
/// the directory; to instead handle errors call [`TempDir::close()`].
///
/// Note that if the program exits before the `TempDir` destructor is
/// run, such as via [`std::process::exit()`], by segfaulting, or by
/// receiving a signal like `SIGINT`, then the temporary directory
/// will not be deleted.
///
/// # Examples
///
/// Create a temporary directory with a generated name:
///
/// ```
/// use std::fs::File;
/// use std::io::Write;
/// use tempfile::TempDir;
///
/// # use std::io;
/// # fn run() -> Result<(), io::Error> {
/// // Create a directory inside of `std::env::temp_dir()`
/// let tmp_dir = TempDir::new()?;
/// # Ok(())
/// # }
/// ```
///
/// Create a temporary directory with a prefix in its name:
///
/// ```
/// use std::fs::File;
/// use std::io::Write;
/// use tempfile::Builder;
///
/// # use std::io;
/// # fn run() -> Result<(), io::Error> {
/// // Create a directory inside of `std::env::temp_dir()`,
/// // whose name will begin with 'example'.
/// let tmp_dir = Builder::new().prefix("example").tempdir()?;
/// # Ok(())
/// # }
/// ```
///
/// [`File`]: http://doc.rust-lang.org/std/fs/struct.File.html
/// [`Path`]: http://doc.rust-lang.org/std/path/struct.Path.html
/// [`ReadDir`]: http://doc.rust-lang.org/std/fs/struct.ReadDir.html
/// [`Builder`]: struct.Builder.html
/// [`TempDir::close()`]: struct.TempDir.html#method.close
/// [`TempDir::new()`]: struct.TempDir.html#method.new
/// [`TempDir::path()`]: struct.TempDir.html#method.path
/// [`TempDir`]: struct.TempDir.html
/// [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html
/// [`std::fs`]: http://doc.rust-lang.org/std/fs/index.html
/// [`std::process::exit()`]: http://doc.rust-lang.org/std/process/fn.exit.html
pub struct TempDir {
    path: Box<Path>,
}

impl TempDir {
    /// Attempts to make a temporary directory inside of `env::temp_dir()`.
    ///
    /// See [`Builder`] for more configuration.
    ///
    /// The directory and everything inside it will be automatically deleted
    /// once the returned `TempDir` is destroyed.
    ///
    /// # Errors
    ///
    /// If the directory can not be created, `Err` is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::File;
    /// use std::io::Write;
    /// use tempfile::TempDir;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// // Create a directory inside of `std::env::temp_dir()`
    /// let tmp_dir = TempDir::new()?;
    ///
    /// let file_path = tmp_dir.path().join("my-temporary-note.txt");
    /// let mut tmp_file = File::create(file_path)?;
    /// writeln!(tmp_file, "Brian was here. Briefly.")?;
    ///
    /// // `tmp_dir` goes out of scope, the directory as well as
    /// // `tmp_file` will be deleted here.
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// [`Builder`]: struct.Builder.html
    pub fn new() -> io::Result<TempDir> {
        Builder::new().tempdir()
    }

    /// Attempts to make a temporary directory inside of `dir`.
    /// The directory and everything inside it will be automatically
    /// deleted once the returned `TempDir` is destroyed.
    ///
    /// # Errors
    ///
    /// If the directory can not be created, `Err` is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::{self, File};
    /// use std::io::Write;
    /// use tempfile::TempDir;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// // Create a directory inside of the current directory
    /// let tmp_dir = TempDir::new_in(".")?;
    /// let file_path = tmp_dir.path().join("my-temporary-note.txt");
    /// let mut tmp_file = File::create(file_path)?;
    /// writeln!(tmp_file, "Brian was here. Briefly.")?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn new_in<P: AsRef<Path>>(dir: P) -> io::Result<TempDir> {
        Builder::new().tempdir_in(dir)
    }

    /// Accesses the [`Path`] to the temporary directory.
    ///
    /// [`Path`]: http://doc.rust-lang.org/std/path/struct.Path.html
    ///
    /// # Examples
    ///
    /// ```
    /// use tempfile::TempDir;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// let tmp_path;
    ///
    /// {
    ///    let tmp_dir = TempDir::new()?;
    ///    tmp_path = tmp_dir.path().to_owned();
    ///
    ///    // Check that the temp directory actually exists.
    ///    assert!(tmp_path.exists());
    ///
    ///    // End of `tmp_dir` scope, directory will be deleted
    /// }
    ///
    /// // Temp directory should be deleted by now
    /// assert_eq!(tmp_path.exists(), false);
    /// # Ok(())
    /// # }
    /// ```
    #[must_use]
    pub fn path(&self) -> &path::Path {
        self.path.as_ref()
    }

    /// Persist the temporary directory to disk, returning the [`PathBuf`] where it is located.
    ///
    /// This consumes the [`TempDir`] without deleting directory on the filesystem, meaning that
    /// the directory will no longer be automatically deleted.
    ///
    /// [`TempDir`]: struct.TempDir.html
    /// [`PathBuf`]: http://doc.rust-lang.org/std/path/struct.PathBuf.html
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs;
    /// use tempfile::TempDir;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// let tmp_dir = TempDir::new()?;
    ///
    /// // Persist the temporary directory to disk,
    /// // getting the path where it is.
    /// let tmp_path = tmp_dir.into_path();
    ///
    /// // Delete the temporary directory ourselves.
    /// fs::remove_dir_all(tmp_path)?;
    /// # Ok(())
    /// # }
    /// ```
    #[must_use]
    pub fn into_path(self) -> PathBuf {
        // Prevent the Drop impl from being called.
        let mut this = mem::ManuallyDrop::new(self);

        // replace this.path with an empty Box, since an empty Box does not
        // allocate any heap memory.
        mem::replace(&mut this.path, PathBuf::new().into_boxed_path()).into()
    }

    /// Closes and removes the temporary directory, returning a `Result`.
    ///
    /// Although `TempDir` removes the directory on drop, in the destructor
    /// any errors are ignored. To detect errors cleaning up the temporary
    /// directory, call `close` instead.
    ///
    /// # Errors
    ///
    /// This function may return a variety of [`std::io::Error`]s that result from deleting
    /// the files and directories contained with the temporary directory,
    /// as well as from deleting the temporary directory itself. These errors
    /// may be platform specific.
    ///
    /// [`std::io::Error`]: http://doc.rust-lang.org/std/io/struct.Error.html
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::File;
    /// use std::io::Write;
    /// use tempfile::TempDir;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// // Create a directory inside of `std::env::temp_dir()`.
    /// let tmp_dir = TempDir::new()?;
    /// let file_path = tmp_dir.path().join("my-temporary-note.txt");
    /// let mut tmp_file = File::create(file_path)?;
    /// writeln!(tmp_file, "Brian was here. Briefly.")?;
    ///
    /// // By closing the `TempDir` explicitly we can check that it has
    /// // been deleted successfully. If we don't close it explicitly,
    /// // the directory will still be deleted when `tmp_dir` goes out
    /// // of scope, but we won't know whether deleting the directory
    /// // succeeded.
    /// drop(tmp_file);
    /// tmp_dir.close()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn close(mut self) -> io::Result<()> {
        let result = remove_dir_all(self.path()).with_err_path(|| self.path());

        // Set self.path to empty Box to release the memory, since an empty
        // Box does not allocate any heap memory.
        self.path = PathBuf::new().into_boxed_path();

        // Prevent the Drop impl from being called.
        mem::forget(self);

        result
    }
}

impl AsRef<Path> for TempDir {
    fn as_ref(&self) -> &Path {
        self.path()
    }
}

impl fmt::Debug for TempDir {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("TempDir")
            .field("path", &self.path())
            .finish()
    }
}

impl Drop for TempDir {
    fn drop(&mut self) {
        let _ = remove_dir_all(self.path());
    }
}

pub(crate) fn create(path: PathBuf) -> io::Result<TempDir> {
    fs::create_dir(&path)
        .with_err_path(|| &path)
        .map(|_| TempDir {
            path: path.into_boxed_path(),
        })
}
<|endoftext|>


<commit_before>use {Error, NixPath, Result};
use errno::Errno;
use fcntl::{self, OFlag};
use libc;
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
use std::{ffi, ptr};
use sys;

#[cfg(target_os = "linux")]
use libc::{dirent64 as dirent, readdir64_r as readdir_r};

#[cfg(not(target_os = "linux"))]
use libc::{dirent, readdir_r};

/// An open directory.
///
/// This is a lower-level interface than `std::fs::ReadDir`. Notable differences:
///    * can be opened from a file descriptor (as returned by `openat`, perhaps before knowing
///      if the path represents a file or directory).
///    * implements `AsRawFd`, so it can be passed to `fstat`, `openat`, etc.
///      The file descriptor continues to be owned by the `Dir`, so callers must not keep a `RawFd`
///      after the `Dir` is dropped.
///    * can be iterated through multiple times without closing and reopening the file
///      descriptor. Each iteration rewinds when finished.
///    * returns entries for `.` (current directory) and `..` (parent directory).
///    * returns entries' names as a `CStr` (no allocation or conversion beyond whatever libc
///      does).
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Dir(
    // This could be ptr::NonNull once nix requires Rust 1.25.
    *mut libc::DIR
);

impl Dir {
    /// Opens the given path as with `fcntl::open`.
    pub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag,
                                     mode: sys::stat::Mode) -> Result<Self> {
        let fd = fcntl::open(path, oflag, mode)?;
        Dir::from_fd(fd)
    }

    /// Opens the given path as with `fcntl::openat`.
    pub fn openat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P, oflag: OFlag,
                                       mode: sys::stat::Mode) -> Result<Self> {
        let fd = fcntl::openat(dirfd, path, oflag, mode)?;
        Dir::from_fd(fd)
    }

    /// Converts from a descriptor-based object, closing the descriptor on success or failure.
    #[inline]
    pub fn from<F: IntoRawFd>(fd: F) -> Result<Self> {
        Dir::from_fd(fd.into_raw_fd())
    }

    /// Converts from a file descriptor, closing it on success or failure.
    pub fn from_fd(fd: RawFd) -> Result<Self> {
        let d = unsafe { libc::fdopendir(fd) };
        if d.is_null() {
            let e = Error::last();
            unsafe { libc::close(fd) };
            return Err(e);
        };
        Ok(Dir(d))
    }

    /// Returns an iterator of `Result<Entry>` which rewinds when finished.
    pub fn iter(&mut self) -> Iter {
        Iter(self)
    }
}

// `Dir` is not `Sync`. With the current implementation, it could be, but according to
// https://www.gnu.org/software/libc/manual/html_node/Reading_002fClosing-Directory.html,
// future versions of POSIX are likely to obsolete `readdir_r` and specify that it's unsafe to
// call `readdir` simultaneously from multiple threads.
//
// `Dir` is safe to pass from one thread to another, as it's not reference-counted.
unsafe impl Send for Dir {}

impl AsRawFd for Dir {
    fn as_raw_fd(&self) -> RawFd {
        unsafe { libc::dirfd(self.0) }
    }
}

impl Drop for Dir {
    fn drop(&mut self) {
        unsafe { libc::closedir(self.0) };
    }
}

#[derive(Debug, Eq, Hash, PartialEq)]
pub struct Iter<'d>(&'d mut Dir);

impl<'d> Iterator for Iter<'d> {
    type Item = Result<Entry>;

    fn next(&mut self) -> Option<Self::Item> {
        unsafe {
            // Note: POSIX specifies that portable applications should dynamically allocate a
            // buffer with room for a `d_name` field of size `pathconf(..., _PC_NAME_MAX)` plus 1
            // for the NUL byte. It doesn't look like the std library does this; it just uses
            // fixed-sized buffers (and libc's dirent seems to be sized so this is appropriate).
            // Probably fine here too then.
            let mut ent: Entry = Entry(::std::mem::uninitialized());
            let mut result = ptr::null_mut();
            if let Err(e) = Errno::result(readdir_r((self.0).0, &mut ent.0, &mut result)) {
                return Some(Err(e));
            }
            if result == ptr::null_mut() {
                return None;
            }
            assert_eq!(result, &mut ent.0 as *mut dirent);
            return Some(Ok(ent));
        }
    }
}

impl<'d> Drop for Iter<'d> {
    fn drop(&mut self) {
        unsafe { libc::rewinddir((self.0).0) }
    }
}

/// A directory entry, similar to `std::fs::DirEntry`.
///
/// Note that unlike the std version, this may represent the `.` or `..` entries.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub struct Entry(dirent);

#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub enum Type {
    Fifo,
    CharacterDevice,
    Directory,
    BlockDevice,
    File,
    Symlink,
    Socket,
}

impl Entry {
    /// Returns the inode number (`d_ino`) of the underlying `dirent`.
    #[cfg(any(target_os = "android",
              target_os = "emscripten",
              target_os = "fuchsia",
              target_os = "haiku",
              target_os = "ios",
              target_os = "l4re",
              target_os = "linux",
              target_os = "macos",
              target_os = "solaris"))]
    pub fn ino(&self) -> u64 {
        self.0.d_ino as u64
    }

    /// Returns the inode number (`d_fileno`) of the underlying `dirent`.
    #[cfg(not(any(target_os = "android",
                  target_os = "emscripten",
                  target_os = "fuchsia",
                  target_os = "haiku",
                  target_os = "ios",
                  target_os = "l4re",
                  target_os = "linux",
                  target_os = "macos",
                  target_os = "solaris")))]
    pub fn ino(&self) -> u64 {
        self.0.d_fileno as u64
    }

    /// Returns the bare file name of this directory entry without any other leading path component.
    pub fn file_name(&self) -> &ffi::CStr {
        unsafe { ::std::ffi::CStr::from_ptr(self.0.d_name.as_ptr()) }
    }

    /// Returns the type of this directory entry, if known.
    ///
    /// See platform `readdir(3)` or `dirent(5)` manpage for when the file type is known;
    /// notably, some Linux filesystems don't implement this. The caller should use `stat` or
    /// `fstat` if this returns `None`.
    pub fn file_type(&self) -> Option<Type> {
        match self.0.d_type {
            libc::DT_FIFO => Some(Type::Fifo),
            libc::DT_CHR => Some(Type::CharacterDevice),
            libc::DT_DIR => Some(Type::Directory),
            libc::DT_BLK => Some(Type::BlockDevice),
            libc::DT_REG => Some(Type::File),
            libc::DT_LNK => Some(Type::Symlink),
            libc::DT_SOCK => Some(Type::Socket),
            /* libc::DT_UNKNOWN | */ _ => None,
        }
    }
}
<commit_msg>Use ptr::NonNull for Dir<commit_after>use {Error, NixPath, Result};
use errno::Errno;
use fcntl::{self, OFlag};
use libc;
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
use std::{ffi, ptr};
use sys;

#[cfg(target_os = "linux")]
use libc::{dirent64 as dirent, readdir64_r as readdir_r};

#[cfg(not(target_os = "linux"))]
use libc::{dirent, readdir_r};

/// An open directory.
///
/// This is a lower-level interface than `std::fs::ReadDir`. Notable differences:
///    * can be opened from a file descriptor (as returned by `openat`, perhaps before knowing
///      if the path represents a file or directory).
///    * implements `AsRawFd`, so it can be passed to `fstat`, `openat`, etc.
///      The file descriptor continues to be owned by the `Dir`, so callers must not keep a `RawFd`
///      after the `Dir` is dropped.
///    * can be iterated through multiple times without closing and reopening the file
///      descriptor. Each iteration rewinds when finished.
///    * returns entries for `.` (current directory) and `..` (parent directory).
///    * returns entries' names as a `CStr` (no allocation or conversion beyond whatever libc
///      does).
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Dir(
    ptr::NonNull<libc::DIR>
);

impl Dir {
    /// Opens the given path as with `fcntl::open`.
    pub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag,
                                     mode: sys::stat::Mode) -> Result<Self> {
        let fd = fcntl::open(path, oflag, mode)?;
        Dir::from_fd(fd)
    }

    /// Opens the given path as with `fcntl::openat`.
    pub fn openat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P, oflag: OFlag,
                                       mode: sys::stat::Mode) -> Result<Self> {
        let fd = fcntl::openat(dirfd, path, oflag, mode)?;
        Dir::from_fd(fd)
    }

    /// Converts from a descriptor-based object, closing the descriptor on success or failure.
    #[inline]
    pub fn from<F: IntoRawFd>(fd: F) -> Result<Self> {
        Dir::from_fd(fd.into_raw_fd())
    }

    /// Converts from a file descriptor, closing it on success or failure.
    pub fn from_fd(fd: RawFd) -> Result<Self> {
        let d = unsafe { libc::fdopendir(fd) };
        if d.is_null() {
            let e = Error::last();
            unsafe { libc::close(fd) };
            return Err(e);
        };
        // Always guaranteed to be non-null by the previous check
        Ok(Dir(ptr::NonNull::new(d).unwrap()))
    }

    /// Returns an iterator of `Result<Entry>` which rewinds when finished.
    pub fn iter(&mut self) -> Iter {
        Iter(self)
    }
}

// `Dir` is not `Sync`. With the current implementation, it could be, but according to
// https://www.gnu.org/software/libc/manual/html_node/Reading_002fClosing-Directory.html,
// future versions of POSIX are likely to obsolete `readdir_r` and specify that it's unsafe to
// call `readdir` simultaneously from multiple threads.
//
// `Dir` is safe to pass from one thread to another, as it's not reference-counted.
unsafe impl Send for Dir {}

impl AsRawFd for Dir {
    fn as_raw_fd(&self) -> RawFd {
        unsafe { libc::dirfd(self.0.as_ptr()) }
    }
}

impl Drop for Dir {
    fn drop(&mut self) {
        unsafe { libc::closedir(self.0.as_ptr()) };
    }
}

#[derive(Debug, Eq, Hash, PartialEq)]
pub struct Iter<'d>(&'d mut Dir);

impl<'d> Iterator for Iter<'d> {
    type Item = Result<Entry>;

    fn next(&mut self) -> Option<Self::Item> {
        unsafe {
            // Note: POSIX specifies that portable applications should dynamically allocate a
            // buffer with room for a `d_name` field of size `pathconf(..., _PC_NAME_MAX)` plus 1
            // for the NUL byte. It doesn't look like the std library does this; it just uses
            // fixed-sized buffers (and libc's dirent seems to be sized so this is appropriate).
            // Probably fine here too then.
            let mut ent: Entry = Entry(::std::mem::uninitialized());
            let mut result = ptr::null_mut();
            if let Err(e) = Errno::result(readdir_r((self.0).0.as_ptr(), &mut ent.0, &mut result)) {
                return Some(Err(e));
            }
            if result == ptr::null_mut() {
                return None;
            }
            assert_eq!(result, &mut ent.0 as *mut dirent);
            return Some(Ok(ent));
        }
    }
}

impl<'d> Drop for Iter<'d> {
    fn drop(&mut self) {
        unsafe { libc::rewinddir((self.0).0.as_ptr()) }
    }
}

/// A directory entry, similar to `std::fs::DirEntry`.
///
/// Note that unlike the std version, this may represent the `.` or `..` entries.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub struct Entry(dirent);

#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub enum Type {
    Fifo,
    CharacterDevice,
    Directory,
    BlockDevice,
    File,
    Symlink,
    Socket,
}

impl Entry {
    /// Returns the inode number (`d_ino`) of the underlying `dirent`.
    #[cfg(any(target_os = "android",
              target_os = "emscripten",
              target_os = "fuchsia",
              target_os = "haiku",
              target_os = "ios",
              target_os = "l4re",
              target_os = "linux",
              target_os = "macos",
              target_os = "solaris"))]
    pub fn ino(&self) -> u64 {
        self.0.d_ino as u64
    }

    /// Returns the inode number (`d_fileno`) of the underlying `dirent`.
    #[cfg(not(any(target_os = "android",
                  target_os = "emscripten",
                  target_os = "fuchsia",
                  target_os = "haiku",
                  target_os = "ios",
                  target_os = "l4re",
                  target_os = "linux",
                  target_os = "macos",
                  target_os = "solaris")))]
    pub fn ino(&self) -> u64 {
        self.0.d_fileno as u64
    }

    /// Returns the bare file name of this directory entry without any other leading path component.
    pub fn file_name(&self) -> &ffi::CStr {
        unsafe { ::std::ffi::CStr::from_ptr(self.0.d_name.as_ptr()) }
    }

    /// Returns the type of this directory entry, if known.
    ///
    /// See platform `readdir(3)` or `dirent(5)` manpage for when the file type is known;
    /// notably, some Linux filesystems don't implement this. The caller should use `stat` or
    /// `fstat` if this returns `None`.
    pub fn file_type(&self) -> Option<Type> {
        match self.0.d_type {
            libc::DT_FIFO => Some(Type::Fifo),
            libc::DT_CHR => Some(Type::CharacterDevice),
            libc::DT_DIR => Some(Type::Directory),
            libc::DT_BLK => Some(Type::BlockDevice),
            libc::DT_REG => Some(Type::File),
            libc::DT_LNK => Some(Type::Symlink),
            libc::DT_SOCK => Some(Type::Socket),
            /* libc::DT_UNKNOWN | */ _ => None,
        }
    }
}
<|endoftext|>