<commit_before>use gfx::display_list::DisplayList;
use gfx::compositor::{LayerBuffer, LayerBufferSet};

use azure::AzFloat;
use azure::azure_hl::DrawTarget;
use cairo::CAIRO_FORMAT_RGB24;
use cairo::cairo_hl::ImageSurface;
use core::libc::c_int;
use geom::matrix2d::Matrix2D;
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;

const TILE_SIZE: uint = 512;

pub struct RenderLayer {
    display_list: DisplayList,
    size: Size2D<uint>
}

/// Given a layer and a buffer, either reuses the buffer (if it's of the right size and format)
/// or creates a new buffer (if it's not of the appropriate size and format) and invokes the
/// given callback with the render layer and the buffer. Returns the resulting layer buffer (which
/// might be the old layer buffer if it had the appropriate size and format).
pub fn render_layers(layer: &RenderLayer,
                     buffer_set: LayerBufferSet,
                     f: &fn(layer: &RenderLayer, buffer: &LayerBuffer) -> bool) -> LayerBufferSet {
    let mut buffers = match move buffer_set { LayerBufferSet { buffers: move b } => move b };

    // FIXME: Try not to create a new array here.
    let new_buffers = dvec::DVec();

    // Divide up the layer into tiles.
    let mut y = 0;
    while y < layer.size.height {
        let mut x = 0;
        while x < layer.size.width {
            // Figure out the dimension of this tile.
            let right = uint::min(x + TILE_SIZE, layer.size.width);
            let bottom = uint::min(y + TILE_SIZE, layer.size.height);
            let width = right - x;
            let height = bottom - y;

            // Round the width up the nearest 32 pixels for DMA on the Mac.
            let mut stride = width;
            if stride % 32 != 0 {
                stride = (stride & !(32 - 1)) + 32;
            }
            assert stride % 32 == 0;
            assert stride >= width;

            let tile_rect = Rect(Point2D(x, y), Size2D(width, height));

            let buffer;
            // FIXME: Try harder to search for a matching tile.
            // FIXME: Don't use shift; it's bad for perf. Maybe reverse and pop.
            if buffers.len() != 0 && buffers[0].rect == tile_rect {
                debug!("reusing tile, (%u, %u)", x, y);
                buffer = buffers.shift();
            } else {
                // Create a new buffer.
                debug!("creating tile, (%u, %u)", x, y);

                let cairo_surface = ImageSurface(
                    CAIRO_FORMAT_RGB24, stride as c_int, height as c_int);
                let draw_target = DrawTarget(&cairo_surface);

                buffer = LayerBuffer {
                    cairo_surface: move cairo_surface,
                    draw_target: move draw_target,
                    rect: tile_rect,
                    stride: stride
                };
            }

            let _ = f(layer, &buffer);
            new_buffers.push(move buffer);

            x += TILE_SIZE;
        }
        y += TILE_SIZE;
    }

    return LayerBufferSet { buffers: move dvec::unwrap(move new_buffers) };
}

<commit_msg>gfx: Disable tiles for now due to bustage<commit_after>use gfx::display_list::DisplayList;
use gfx::compositor::{LayerBuffer, LayerBufferSet};

use azure::AzFloat;
use azure::azure_hl::DrawTarget;
use cairo::CAIRO_FORMAT_RGB24;
use cairo::cairo_hl::ImageSurface;
use core::libc::c_int;
use geom::matrix2d::Matrix2D;
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;

// FIXME: Tiles are busted. Disable them for now.
const TILE_SIZE: uint = 4096;

pub struct RenderLayer {
    display_list: DisplayList,
    size: Size2D<uint>
}

/// Given a layer and a buffer, either reuses the buffer (if it's of the right size and format)
/// or creates a new buffer (if it's not of the appropriate size and format) and invokes the
/// given callback with the render layer and the buffer. Returns the resulting layer buffer (which
/// might be the old layer buffer if it had the appropriate size and format).
pub fn render_layers(layer: &RenderLayer,
                     buffer_set: LayerBufferSet,
                     f: &fn(layer: &RenderLayer, buffer: &LayerBuffer) -> bool) -> LayerBufferSet {
    let mut buffers = match move buffer_set { LayerBufferSet { buffers: move b } => move b };

    // FIXME: Try not to create a new array here.
    let new_buffers = dvec::DVec();

    // Divide up the layer into tiles.
    let mut y = 0;
    while y < layer.size.height {
        let mut x = 0;
        while x < layer.size.width {
            // Figure out the dimension of this tile.
            let right = uint::min(x + TILE_SIZE, layer.size.width);
            let bottom = uint::min(y + TILE_SIZE, layer.size.height);
            let width = right - x;
            let height = bottom - y;

            // Round the width up the nearest 32 pixels for DMA on the Mac.
            let mut stride = width;
            if stride % 32 != 0 {
                stride = (stride & !(32 - 1)) + 32;
            }
            assert stride % 32 == 0;
            assert stride >= width;

            let tile_rect = Rect(Point2D(x, y), Size2D(width, height));

            let buffer;
            // FIXME: Try harder to search for a matching tile.
            // FIXME: Don't use shift; it's bad for perf. Maybe reverse and pop.
            if buffers.len() != 0 && buffers[0].rect == tile_rect {
                debug!("reusing tile, (%u, %u)", x, y);
                buffer = buffers.shift();
            } else {
                // Create a new buffer.
                debug!("creating tile, (%u, %u)", x, y);

                let cairo_surface = ImageSurface(
                    CAIRO_FORMAT_RGB24, stride as c_int, height as c_int);
                let draw_target = DrawTarget(&cairo_surface);

                buffer = LayerBuffer {
                    cairo_surface: move cairo_surface,
                    draw_target: move draw_target,
                    rect: tile_rect,
                    stride: stride
                };
            }

            let _ = f(layer, &buffer);
            new_buffers.push(move buffer);

            x += TILE_SIZE;
        }
        y += TILE_SIZE;
    }

    return LayerBufferSet { buffers: move dvec::unwrap(move new_buffers) };
}

<|endoftext|>


<commit_before>// run-pass
// ignore-wasm32-bare compiled with panic=abort by default

// rust-lang/rust#64655: with panic=unwind, a panic from a subroutine
// should still run destructors as it unwinds the stack. However,
// bugs with how the nounwind LLVM attribute was applied led to this
// simple case being mishandled *if* you had optimization *and* fat
// LTO turned on.

// This test is the closest thing to a "regression test" we can do
// without actually spawning subprocesses and comparing stderr
// results.
//
// This test takes the code from the above issue and adapts it to
// better fit our test infrastructure:
//
// * Instead of relying on `println!` to observe whether the destructor
//   is run, we instead run the code in a spawned thread and
//   communicate the destructor's operation via a synchronous atomic
//   in static memory.
//
// * To keep the output from confusing a casual user, we override the
//   panic hook to be a no-op (rather than printing a message to
//   stderr).
//
// (pnkfelix has confirmed by hand that these additions do not mask
// the underlying bug.)

// LTO settings cannot be combined with -C prefer-dynamic
// no-prefer-dynamic

// The revisions combine each lto setting with each optimization
// setting; pnkfelix observed three differing behaviors at opt-levels
// 0/1/2+3 for this test, so it seems prudent to be thorough.

// revisions: no0 no1 no2 no3 thin0 thin1 thin2 thin3 fat0 fat1 fat2  fat3

//[no0]compile-flags: -C opt-level=0 -C lto=no
//[no1]compile-flags: -C opt-level=1 -C lto=no
//[no2]compile-flags: -C opt-level=2 -C lto=no
//[no3]compile-flags: -C opt-level=3 -C lto=no
//[thin0]compile-flags: -C opt-level=0 -C lto=thin
//[thin1]compile-flags: -C opt-level=1 -C lto=thin
//[thin2]compile-flags: -C opt-level=2 -C lto=thin
//[thin3]compile-flags: -C opt-level=3 -C lto=thin
//[fat0]compile-flags: -C opt-level=0 -C lto=fat
//[fat1]compile-flags: -C opt-level=1 -C lto=fat
//[fat2]compile-flags: -C opt-level=2 -C lto=fat
//[fat3]compile-flags: -C opt-level=3 -C lto=fat

fn main() {
    use std::sync::atomic::{AtomicUsize, Ordering};

    static SHARED: AtomicUsize = AtomicUsize::new(0);

    assert_eq!(SHARED.fetch_add(0, Ordering::SeqCst), 0);

    let old_hook = std::panic::take_hook();

    std::panic::set_hook(Box::new(|_| { } )); // no-op on panic.

    let handle = std::thread::spawn(|| {
        struct Droppable;
        impl Drop for Droppable {
            fn drop(&mut self) {
                SHARED.fetch_add(1, Ordering::SeqCst);
            }
        }

        let _guard = Droppable;
        None::<()>.expect("???");
    });

    let wait = handle.join();

    // reinstate handler to ease observation of assertion failures.
    std::panic::set_hook(old_hook);

    assert!(wait.is_err());

    assert_eq!(SHARED.fetch_add(0, Ordering::SeqCst), 1);
}
<commit_msg>ignore-emcscripten as it does not support threads<commit_after>// run-pass
// ignore-wasm32-bare compiled with panic=abort by default
// ignore-emscripten no threads support

// rust-lang/rust#64655: with panic=unwind, a panic from a subroutine
// should still run destructors as it unwinds the stack. However,
// bugs with how the nounwind LLVM attribute was applied led to this
// simple case being mishandled *if* you had optimization *and* fat
// LTO turned on.

// This test is the closest thing to a "regression test" we can do
// without actually spawning subprocesses and comparing stderr
// results.
//
// This test takes the code from the above issue and adapts it to
// better fit our test infrastructure:
//
// * Instead of relying on `println!` to observe whether the destructor
//   is run, we instead run the code in a spawned thread and
//   communicate the destructor's operation via a synchronous atomic
//   in static memory.
//
// * To keep the output from confusing a casual user, we override the
//   panic hook to be a no-op (rather than printing a message to
//   stderr).
//
// (pnkfelix has confirmed by hand that these additions do not mask
// the underlying bug.)

// LTO settings cannot be combined with -C prefer-dynamic
// no-prefer-dynamic

// The revisions combine each lto setting with each optimization
// setting; pnkfelix observed three differing behaviors at opt-levels
// 0/1/2+3 for this test, so it seems prudent to be thorough.

// revisions: no0 no1 no2 no3 thin0 thin1 thin2 thin3 fat0 fat1 fat2  fat3

//[no0]compile-flags: -C opt-level=0 -C lto=no
//[no1]compile-flags: -C opt-level=1 -C lto=no
//[no2]compile-flags: -C opt-level=2 -C lto=no
//[no3]compile-flags: -C opt-level=3 -C lto=no
//[thin0]compile-flags: -C opt-level=0 -C lto=thin
//[thin1]compile-flags: -C opt-level=1 -C lto=thin
//[thin2]compile-flags: -C opt-level=2 -C lto=thin
//[thin3]compile-flags: -C opt-level=3 -C lto=thin
//[fat0]compile-flags: -C opt-level=0 -C lto=fat
//[fat1]compile-flags: -C opt-level=1 -C lto=fat
//[fat2]compile-flags: -C opt-level=2 -C lto=fat
//[fat3]compile-flags: -C opt-level=3 -C lto=fat

fn main() {
    use std::sync::atomic::{AtomicUsize, Ordering};

    static SHARED: AtomicUsize = AtomicUsize::new(0);

    assert_eq!(SHARED.fetch_add(0, Ordering::SeqCst), 0);

    let old_hook = std::panic::take_hook();

    std::panic::set_hook(Box::new(|_| { } )); // no-op on panic.

    let handle = std::thread::spawn(|| {
        struct Droppable;
        impl Drop for Droppable {
            fn drop(&mut self) {
                SHARED.fetch_add(1, Ordering::SeqCst);
            }
        }

        let _guard = Droppable;
        None::<()>.expect("???");
    });

    let wait = handle.join();

    // reinstate handler to ease observation of assertion failures.
    std::panic::set_hook(old_hook);

    assert!(wait.is_err());

    assert_eq!(SHARED.fetch_add(0, Ordering::SeqCst), 1);
}
<|endoftext|>


<commit_before>use geom::point::Point;
use renderer::Renderer;
use frame::Frame;
use draw::RGB;

use super::Scene;

pub struct DevelRenderer {
   scene: Scene,
   aliased: Vec<RGB>,
   rendered: Vec<RGB>,
}

const SUBDIVISIONS: i64 = 3;

impl DevelRenderer {
   #[inline]
   pub fn new(scene: Scene) -> Self {
      DevelRenderer {
         scene: scene,

         aliased: vec!(),
         rendered: vec!(),
      }
   }

   fn resize(&mut self, width: u32, height: u32) {
      self.aliased.resize((width * height) as usize * (SUBDIVISIONS * SUBDIVISIONS) as usize, RGB::new(0, 0, 0));
      self.rendered.resize((width * height) as usize, RGB::new(0, 0, 0));
   }

   #[inline]
   fn clear(&mut self) {
      let mut aliased: &mut Vec<RGB> = self.aliased.as_mut();

      for i in 0..aliased.len() {
         aliased[i] = RGB::default();
      }
   }

   #[inline]
   fn render_aliased(&mut self, frame: &mut Frame) {
      let mut aliased: &mut Vec<RGB> = self.aliased.as_mut();

      for poly in &self.scene.polys {
         let points = &poly.points;

         let (left, right) = get_left_right_edges(points);

         let mut left_i = 0;
         let mut right_i = 0;

         let mut left_edge = left[left_i];
         let mut right_edge = right[right_i];

         let mut left_x = left_edge.p1.x;
         let mut right_x = right_edge.p1.x;

         let left_last_i = left.len() - 1;
         let right_last_i = right.len() - 1;

         let mut left_advancer = YAxisAdvancer::new(&left_edge);
         let mut right_advancer = YAxisAdvancer::new(&right_edge);

         let min_y = left_edge.p1.y;
         let max_y = left[left_last_i].p2.y;

         for y in min_y..max_y + 1 {
            if left_edge.p2.y == y && left_i != left_last_i {
               left_i += 1;
               left_edge = left[left_i];
               left_advancer = YAxisAdvancer::new(&left_edge);
            }

            if right_edge.p2.y == y && right_i != right_last_i  {
               right_i += 1;
               right_edge = right[right_i];
               right_advancer = YAxisAdvancer::new(&right_edge);
            }

            for x in left_x..right_x {
               aliased[y as usize * frame.width as usize * SUBDIVISIONS as usize + x as usize] = poly.color;
            }

            left_x = left_advancer.advance();
            right_x = right_advancer.advance();
         }
      }
   }

   #[inline]
   fn downsample(&mut self, frame: &mut Frame) {
      let aliased: &mut Vec<RGB> = self.aliased.as_mut();

      let rendered: &mut Vec<RGB> = self.rendered.as_mut();

      let divisor = (SUBDIVISIONS * SUBDIVISIONS) as u16;

      for y in 0..frame.height as usize {
         for x in 0..frame.width as usize {
            let mut r: u16 = 0;
            let mut g: u16 = 0;
            let mut b: u16 = 0;
            for box_y in 0..SUBDIVISIONS as usize {
               for box_x in 0..SUBDIVISIONS as usize {
                  let pos = (box_y + y * SUBDIVISIONS as usize * frame.width as usize * SUBDIVISIONS as usize) + box_x + x * SUBDIVISIONS as usize;
                  let color = &aliased[pos];
                  r += color.r as u16;
                  g += color.g as u16;
                  b += color.b as u16;
               }
            }

            r /= divisor;
            g /= divisor;
            b /= divisor;

            let color = RGB::new(r as u8, g as u8, b as u8);

            frame.put_pixel(x as i32, y as i32, &color);

            rendered[y * frame.width as usize + x] = color;
         }
      }
   }
}

impl Renderer for DevelRenderer {
   #[inline]
   fn init(&mut self, width: u32, height: u32) {
      self.resize(width, height);
   }

   fn render(&mut self, frame: &mut Frame) {
      self.clear();

      self.render_aliased(frame);

      self.downsample(frame);
   }
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
struct Edge {
   p1: Point,
   p2: Point,
}

impl Edge {
   #[inline]
   fn new(p1: Point, p2: Point) -> Self {
      Edge {
         p1: p1,
         p2: p2,
      }
   }
}

struct YAxisAdvancer {
   delta: f64,
   f64x: f64,
}

impl YAxisAdvancer {
   fn new(edge: &Edge) -> Self {
      let dx = edge.p2.x - edge.p1.x;
      let dy = edge.p2.y - edge.p1.y;

      let delta = dx as f64 / dy as f64;
      let f64x = edge.p1.x as f64;

      YAxisAdvancer {
         delta: delta,
         f64x: f64x,
      }
   }

   fn advance(&mut self) -> i64 {
      self.f64x += self.delta;
      self.f64x.round() as i64
   }
}

fn get_left_right_edges(points: &Vec<Point>) -> (Vec<Edge>, Vec<Edge>){
   let (min_i, max_i) = find_min_max_y_index(points);

   let left = get_left_edges(points, min_i, max_i);
   let right = get_right_edges(points, min_i, max_i);

   (left, right)
}

fn get_left_edges(points: &Vec<Point>, min_i: usize, max_i: usize) -> Vec<Edge> {
   let mut edges = Vec::new();

   let mut curr_i = min_i;
   let mut prev_i = curr_i;

   loop {
      curr_i += 1;

      if curr_i == points.len() {
         curr_i = 0;
      }

      edges.push(Edge::new(points[prev_i], points[curr_i]));

      prev_i = curr_i;

      if curr_i == max_i {
         break;
      }
   }

   edges
}

fn get_right_edges(points: &Vec<Point>, min_i: usize, max_i: usize) -> Vec<Edge> {
   let mut edges = Vec::new();

   let mut curr_i = min_i;
   let mut prev_i = curr_i;

   loop {
      if curr_i == 0 {
         curr_i = points.len() - 1;
      } else {
         curr_i -= 1;
      }

      edges.push(Edge::new(points[prev_i], points[curr_i]));

      prev_i = curr_i;

      if curr_i == max_i {
         break;
      }
   }

   edges
}

fn find_min_max_y_index(points: &Vec<Point>) -> (usize, usize) {
   let (first, rest) = points.split_first().unwrap();

   let mut min_y = first.y;
   let mut max_y = min_y;
   let mut min_i = 0;
   let mut max_i = 0;

   for (i, point) in rest.iter().enumerate() {
      if point.y < min_y {
         min_i = i + 1;
         min_y = point.y;
      } else if point.y > max_y {
         max_i = i + 1;
         max_y = point.y;
      }
   }

   (min_i, max_i)
}

<commit_msg>Resize the buffers of the development rasterizer when frame dimentions change<commit_after>use geom::point::Point;
use renderer::Renderer;
use frame::Frame;
use draw::RGB;

use super::Scene;

pub struct DevelRenderer {
   scene: Scene,
   aliased: Vec<RGB>,
   rendered: Vec<RGB>,
}

const SUBDIVISIONS: i64 = 3;

impl DevelRenderer {
   #[inline]
   pub fn new(scene: Scene) -> Self {
      DevelRenderer {
         scene: scene,

         aliased: vec!(),
         rendered: vec!(),
      }
   }

   fn check_resize(&mut self, width: u32, height: u32) {
      if self.rendered.len() == (width * height) as usize {
         return;
      }

      self.aliased.resize((width * height) as usize * (SUBDIVISIONS * SUBDIVISIONS) as usize, RGB::new(0, 0, 0));
      self.rendered.resize((width * height) as usize, RGB::new(0, 0, 0));
   }

   #[inline]
   fn clear(&mut self) {
      let mut aliased: &mut Vec<RGB> = self.aliased.as_mut();

      for i in 0..aliased.len() {
         aliased[i] = RGB::default();
      }
   }

   #[inline]
   fn render_aliased(&mut self, frame: &mut Frame) {
      let mut aliased: &mut Vec<RGB> = self.aliased.as_mut();

      for poly in &self.scene.polys {
         let points = &poly.points;

         let (left, right) = get_left_right_edges(points);

         let mut left_i = 0;
         let mut right_i = 0;

         let mut left_edge = left[left_i];
         let mut right_edge = right[right_i];

         let mut left_x = left_edge.p1.x;
         let mut right_x = right_edge.p1.x;

         let left_last_i = left.len() - 1;
         let right_last_i = right.len() - 1;

         let mut left_advancer = YAxisAdvancer::new(&left_edge);
         let mut right_advancer = YAxisAdvancer::new(&right_edge);

         let min_y = left_edge.p1.y;
         let max_y = left[left_last_i].p2.y;

         for y in min_y..max_y + 1 {
            if left_edge.p2.y == y && left_i != left_last_i {
               left_i += 1;
               left_edge = left[left_i];
               left_advancer = YAxisAdvancer::new(&left_edge);
            }

            if right_edge.p2.y == y && right_i != right_last_i  {
               right_i += 1;
               right_edge = right[right_i];
               right_advancer = YAxisAdvancer::new(&right_edge);
            }

            for x in left_x..right_x {
               aliased[y as usize * frame.width as usize * SUBDIVISIONS as usize + x as usize] = poly.color;
            }

            left_x = left_advancer.advance();
            right_x = right_advancer.advance();
         }
      }
   }

   #[inline]
   fn downsample(&mut self, frame: &mut Frame) {
      let aliased: &mut Vec<RGB> = self.aliased.as_mut();

      let rendered: &mut Vec<RGB> = self.rendered.as_mut();

      let divisor = (SUBDIVISIONS * SUBDIVISIONS) as u16;

      for y in 0..frame.height as usize {
         for x in 0..frame.width as usize {
            let mut r: u16 = 0;
            let mut g: u16 = 0;
            let mut b: u16 = 0;
            for box_y in 0..SUBDIVISIONS as usize {
               for box_x in 0..SUBDIVISIONS as usize {
                  let pos = (box_y + y * SUBDIVISIONS as usize * frame.width as usize * SUBDIVISIONS as usize) + box_x + x * SUBDIVISIONS as usize;
                  let color = &aliased[pos];
                  r += color.r as u16;
                  g += color.g as u16;
                  b += color.b as u16;
               }
            }

            r /= divisor;
            g /= divisor;
            b /= divisor;

            let color = RGB::new(r as u8, g as u8, b as u8);

            frame.put_pixel(x as i32, y as i32, &color);

            rendered[y * frame.width as usize + x] = color;
         }
      }
   }
}

impl Renderer for DevelRenderer {
   #[inline]
   fn init(&mut self, width: u32, height: u32) {
      self.check_resize(width, height);
   }

   fn render(&mut self, frame: &mut Frame) {
      self.check_resize(frame.width, frame.height);

      self.clear();

      self.render_aliased(frame);

      self.downsample(frame);
   }
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
struct Edge {
   p1: Point,
   p2: Point,
}

impl Edge {
   #[inline]
   fn new(p1: Point, p2: Point) -> Self {
      Edge {
         p1: p1,
         p2: p2,
      }
   }
}

struct YAxisAdvancer {
   delta: f64,
   f64x: f64,
}

impl YAxisAdvancer {
   fn new(edge: &Edge) -> Self {
      let dx = edge.p2.x - edge.p1.x;
      let dy = edge.p2.y - edge.p1.y;

      let delta = dx as f64 / dy as f64;
      let f64x = edge.p1.x as f64;

      YAxisAdvancer {
         delta: delta,
         f64x: f64x,
      }
   }

   fn advance(&mut self) -> i64 {
      self.f64x += self.delta;
      self.f64x.round() as i64
   }
}

fn get_left_right_edges(points: &Vec<Point>) -> (Vec<Edge>, Vec<Edge>){
   let (min_i, max_i) = find_min_max_y_index(points);

   let left = get_left_edges(points, min_i, max_i);
   let right = get_right_edges(points, min_i, max_i);

   (left, right)
}

fn get_left_edges(points: &Vec<Point>, min_i: usize, max_i: usize) -> Vec<Edge> {
   let mut edges = Vec::new();

   let mut curr_i = min_i;
   let mut prev_i = curr_i;

   loop {
      curr_i += 1;

      if curr_i == points.len() {
         curr_i = 0;
      }

      edges.push(Edge::new(points[prev_i], points[curr_i]));

      prev_i = curr_i;

      if curr_i == max_i {
         break;
      }
   }

   edges
}

fn get_right_edges(points: &Vec<Point>, min_i: usize, max_i: usize) -> Vec<Edge> {
   let mut edges = Vec::new();

   let mut curr_i = min_i;
   let mut prev_i = curr_i;

   loop {
      if curr_i == 0 {
         curr_i = points.len() - 1;
      } else {
         curr_i -= 1;
      }

      edges.push(Edge::new(points[prev_i], points[curr_i]));

      prev_i = curr_i;

      if curr_i == max_i {
         break;
      }
   }

   edges
}

fn find_min_max_y_index(points: &Vec<Point>) -> (usize, usize) {
   let (first, rest) = points.split_first().unwrap();

   let mut min_y = first.y;
   let mut max_y = min_y;
   let mut min_i = 0;
   let mut max_i = 0;

   for (i, point) in rest.iter().enumerate() {
      if point.y < min_y {
         min_i = i + 1;
         min_y = point.y;
      } else if point.y > max_y {
         max_i = i + 1;
         max_y = point.y;
      }
   }

   (min_i, max_i)
}

<|endoftext|>


<commit_before>use bvh::*;
use bounding_hierarchy::BHShape;
use std::collections::HashSet;

impl BVH {
    /// Optimizes the `BVH` by batch-reorganizing updated nodes.
    /// Based on https://github.com/jeske/SimpleScene/blob/master/SimpleScene/Util/ssBVH/ssBVH.cs
    ///
    /// Needs all the scene's shapes, plus the indices of the shapes that were updated.
    ///
    pub fn optimize<Shape: BHShape>(&mut self,
                                    refit_shape_indices: &HashSet<usize>,
                                    shapes: &[Shape]) {
        let mut refit_node_indices: HashSet<usize> = refit_shape_indices.iter()
            .map(|x| shapes[*x].bh_node_index())
            .collect();

        // As long as we have refit nodes left, take the list of refit nodes
        // with the highest depth (sweep nodes) and try to rotate them all
        while refit_node_indices.len() > 0 {
            let mut max_depth = 0;
            let mut sweep_node_indices: Vec<usize> = Vec::new();

            // Find max_depth and sweep_node_indices in one iteration
            for refit_node_index in refit_node_indices.iter() {
                let depth = match self.nodes[*refit_node_index] {
                    BVHNode::Node { depth, .. } => depth,
                    BVHNode::Leaf { depth, .. } => depth,
                    BVHNode::Dummy => panic!("Dummy node found during BVH optimization!"),
                };

                if depth > max_depth {
                    max_depth = depth;
                    sweep_node_indices.clear();
                    sweep_node_indices.push(*refit_node_index);
                } else if depth == max_depth {
                    sweep_node_indices.push(*refit_node_index);
                }
            }

            // Try to find a useful tree rotation with all nodes previously found
            for sweep_node_index in sweep_node_indices.iter() {
                // This node does not need to be checked again
                refit_node_indices.remove(&sweep_node_index);

                let new_refit_node_index = self.try_rotate(*sweep_node_index, shapes);

                // Instead of finding a useful tree rotation, we found another node
                // that we should check, so we add its index to the refit_node_indices.
                if let Some(index) = new_refit_node_index {
                    refit_node_indices.insert(index);
                }
            }
        }
    }

    /// Checks if there is a way to rotate a child and a grandchild node of
    /// the given node (specified by `node_index`) that would improve the `BVH`.
    /// If there is, the best rotation found is performed.
    ///
    /// Returns Some(usize) if a new node was found that should be used for optimization.
    ///
    fn try_rotate<Shape: BHShape>(&mut self, node_index: usize, shapes: &[Shape]) -> Option<usize> {
        let mut nodes = &mut self.nodes;

        let mut node_clone = nodes[node_index].clone();

        // Contains the surface area that would result from applying the currently favored rotation.
        // The rotation with the smallest SA will be applied in the end.
        // The value is calculated by child_l_aabb.surface_area() + child_r_aabb.surface_area()
        let mut best_SA = 0f32;

        // If this node is not a grandparent, update the AABB,
        // queue the parent for refitting, and bail out.
        // If it is a grandparent, calculate the current best_SA.
        match node_clone {
            BVHNode::Node { parent, child_l, child_r, child_l_aabb, child_r_aabb, .. } => {
                if let BVHNode::Leaf { shape, .. } = nodes[child_l] {
                    let shape_l_index = shape;
                    if let BVHNode::Leaf { shape, .. } = nodes[child_r] {
                        let shape_r_index = shape;

                        // Update the AABBs saved for the children
                        // since at least one of them changed
                        let mut node = &mut nodes[node_index];
                        match node {
                            &mut BVHNode::Node { ref mut child_l_aabb,
                                                 ref mut child_r_aabb,
                                                 .. } => {
                                *child_l_aabb = shapes[shape_l_index].aabb();
                                *child_r_aabb = shapes[shape_r_index].aabb();
                            }
                            _ => unreachable!(),
                        }

                        return Some(parent);
                    }
                }

                best_SA = child_l_aabb.surface_area() + child_r_aabb.surface_area();
            }
            BVHNode::Leaf { parent, .. } => {
                return Some(parent);
            }
            BVHNode::Dummy => panic!("Dummy node found during BVH optimization!"),
        }

        let mut best_rotation: Option<(usize, usize)> = None;

        // TODO Implement actual rotations
        println!("Potentially rotating node {}.", node_index);

        // TODO Don't forget to update AABBs
        None
    }

    /// Switch two nodes by rewiring the involved indices (not by moving them in the nodes slice)
    fn rotate(&mut self, node_a_index: usize, node_b_index: usize) {
        let mut nodes = &mut self.nodes;

        macro_rules! should_not_happen {
            () => ( panic!("While rotating BVH nodes, something unexpected happened."); );
            ($s:expr) => ( panic!("While rotating BVH nodes, something unexpected happened: {}", $s); );
        }

        #[allow(dead_code)] // The compiler falsely detects dead code here
        fn get_parent_index(nodes: &Vec<BVHNode>, node_index: usize) -> usize {
            let node = &nodes[node_index];

            match *node {
                BVHNode::Node { parent, .. } |
                BVHNode::Leaf { parent, .. } => parent,
                _ => should_not_happen!(),
            }
        }

        let node_a_parent_index = get_parent_index(nodes, node_a_index);
        let node_b_parent_index = get_parent_index(nodes, node_b_index);

        #[allow(dead_code)] // The compiler falsely detects dead code here
        fn get_is_left_child(nodes: &Vec<BVHNode>,
                             node_index: usize,
                             node_parent_index: usize)
                             -> bool {
            let node_parent = &nodes[node_parent_index];

            match *node_parent {
                BVHNode::Node { child_l, .. } => child_l == node_index,
                _ => should_not_happen!(),
            }
        }

        let node_a_is_left_child = get_is_left_child(nodes, node_a_index, node_a_parent_index);
        let node_b_is_left_child = get_is_left_child(nodes, node_b_index, node_b_parent_index);

        #[allow(dead_code)] // The compiler falsely detects dead code here
        fn connect_nodes(nodes: &mut Vec<BVHNode>,
                         child_index: usize,
                         parent_index: usize,
                         left_child: bool) {
            // Set parent's child and get its depth
            let parent_depth = {
                let mut parent = &mut nodes[parent_index];
                match parent {
                    &mut BVHNode::Node { ref mut child_l, ref mut child_r, depth, .. } => {
                        if left_child {
                            *child_l = child_index;
                        } else {
                            *child_r = child_index;
                        }
                        depth
                    }
                    _ => should_not_happen!(),
                }
            };

            // Set child's parent and depth
            {
                let mut child = &mut nodes[child_index];
                match child {
                    &mut BVHNode::Node { ref mut parent, ref mut depth, .. } |
                    &mut BVHNode::Leaf { ref mut parent, ref mut depth, .. } => {
                        *parent = parent_index;
                        *depth = parent_depth + 1;
                    }
                    _ => should_not_happen!(),
                };
            }
        }

        connect_nodes(nodes,
                      node_a_index,
                      node_b_parent_index,
                      node_b_is_left_child);
        connect_nodes(nodes,
                      node_b_index,
                      node_a_parent_index,
                      node_a_is_left_child);
    }
}

#[cfg(test)]
pub mod tests {
    use bvh::BVH;

    #[test]
    /// Tests if the optimize function tries to change a fresh BVH even though it shouldn't
    fn test_optimizing_new_bvh() {
        let (mut shapes, mut bvh) = build_some_bh::<BVH>();

        let refit_shape_indices: HashSet<usize> = (0..shapes.len()).collect();
        bvh.optimize(&refit_shape_indices, &mut shapes);
    }

    // TODO Add tests for:
    // * correct parent
    // * correct depth
    // * correct BVH after optimizing
    // * correct parent and depth after optimizing
}
<commit_msg>WIP: Add consider_rotation macro mockup<commit_after>use bvh::*;
use bounding_hierarchy::BHShape;
use std::collections::HashSet;

impl BVH {
    /// Optimizes the `BVH` by batch-reorganizing updated nodes.
    /// Based on https://github.com/jeske/SimpleScene/blob/master/SimpleScene/Util/ssBVH/ssBVH.cs
    ///
    /// Needs all the scene's shapes, plus the indices of the shapes that were updated.
    ///
    pub fn optimize<Shape: BHShape>(&mut self,
                                    refit_shape_indices: &HashSet<usize>,
                                    shapes: &[Shape]) {
        let mut refit_node_indices: HashSet<usize> = refit_shape_indices.iter()
            .map(|x| shapes[*x].bh_node_index())
            .collect();

        // As long as we have refit nodes left, take the list of refit nodes
        // with the highest depth (sweep nodes) and try to rotate them all
        while refit_node_indices.len() > 0 {
            let mut max_depth = 0;
            let mut sweep_node_indices: Vec<usize> = Vec::new();

            // Find max_depth and sweep_node_indices in one iteration
            for refit_node_index in refit_node_indices.iter() {
                let depth = match self.nodes[*refit_node_index] {
                    BVHNode::Node { depth, .. } => depth,
                    BVHNode::Leaf { depth, .. } => depth,
                    BVHNode::Dummy => panic!("Dummy node found during BVH optimization!"),
                };

                if depth > max_depth {
                    max_depth = depth;
                    sweep_node_indices.clear();
                    sweep_node_indices.push(*refit_node_index);
                } else if depth == max_depth {
                    sweep_node_indices.push(*refit_node_index);
                }
            }

            // Try to find a useful tree rotation with all nodes previously found
            for sweep_node_index in sweep_node_indices.iter() {
                // This node does not need to be checked again
                refit_node_indices.remove(&sweep_node_index);

                let new_refit_node_index = self.try_rotate(*sweep_node_index, shapes);

                // Instead of finding a useful tree rotation, we found another node
                // that we should check, so we add its index to the refit_node_indices.
                if let Some(index) = new_refit_node_index {
                    refit_node_indices.insert(index);
                }
            }
        }
    }

    /// Checks if there is a way to rotate a child and a grandchild node of
    /// the given node (specified by `node_index`) that would improve the `BVH`.
    /// If there is, the best rotation found is performed.
    /// Relies on the children nodes of the given node having correct AABBs.
    ///
    /// Returns Some(usize) if a new node was found that should be used for optimization.
    ///
    fn try_rotate<Shape: BHShape>(&mut self, node_index: usize, shapes: &[Shape]) -> Option<usize> {
        let mut nodes = &mut self.nodes;

        let mut node_clone = nodes[node_index].clone();

        // Contains the surface area that would result from applying the currently favored rotation.
        // The rotation with the smallest SA will be applied in the end.
        // The value is calculated by child_l_aabb.surface_area() + child_r_aabb.surface_area()
        let mut best_SA = 0f32;

        // If this node is not a grandparent, update the AABB,
        // queue the parent for refitting, and bail out.
        // If it is a grandparent, calculate the current best_SA.
        match node_clone {
            BVHNode::Node { parent, child_l, child_r, child_l_aabb, child_r_aabb, .. } => {
                if let BVHNode::Leaf { shape, .. } = nodes[child_l] {
                    let shape_l_index = shape;
                    if let BVHNode::Leaf { shape, .. } = nodes[child_r] {
                        let shape_r_index = shape;

                        // Update the AABBs saved for the children
                        // since at least one of them changed
                        let mut node = &mut nodes[node_index];
                        match node {
                            &mut BVHNode::Node { ref mut child_l_aabb,
                                                 ref mut child_r_aabb,
                                                 .. } => {
                                *child_l_aabb = shapes[shape_l_index].aabb();
                                *child_r_aabb = shapes[shape_r_index].aabb();
                            }
                            _ => unreachable!(),
                        }

                        return Some(parent);
                    }
                }

                best_SA = child_l_aabb.surface_area() + child_r_aabb.surface_area();
            }
            BVHNode::Leaf { parent, .. } => {
                return Some(parent);
            }
            BVHNode::Dummy => panic!("Dummy node found during BVH optimization!"),
        }

        // Stores the Rotation that would result in the surface area best_SA,
        // thus being the favored rotation that will be executed after considering all rotations.
        let mut best_rotation: Option<(usize, usize)> = None;

        macro_rules! consider_rotation {
            ($a:expr, $b:expr) => {
                // TODO Calculate surface area that would result from rotating the given nodes.
                // TODO If the result SA is smaller than the current best, save as best_SA and
                // the nodes as best_rotation;
                unimplemented!();
            };
        }

        // Child to grandchild rotations
        consider_rotation!(child_l, child_rl);
        consider_rotation!(child_l, child_rr);
        consider_rotation!(child_r, child_ll);
        consider_rotation!(child_r, child_lr);
        // Grandchild to grandchild rotations
        consider_rotation!(child_ll, child_rl);
        consider_rotation!(child_ll, child_rr);

        // TODO Don't forget to update AABBs
        None
    }

    /// Switch two nodes by rewiring the involved indices (not by moving them in the nodes slice)
    fn rotate(&mut self, node_a_index: usize, node_b_index: usize) {
        let mut nodes = &mut self.nodes;

        macro_rules! should_not_happen {
            () => ( panic!("While rotating BVH nodes, something unexpected happened."); );
            ($s:expr) => ( panic!("While rotating BVH nodes, something unexpected happened: {}", $s); );
        }

        #[allow(dead_code)] // The compiler falsely detects dead code here
        fn get_parent_index(nodes: &Vec<BVHNode>, node_index: usize) -> usize {
            let node = &nodes[node_index];

            match *node {
                BVHNode::Node { parent, .. } |
                BVHNode::Leaf { parent, .. } => parent,
                _ => should_not_happen!(),
            }
        }

        let node_a_parent_index = get_parent_index(nodes, node_a_index);
        let node_b_parent_index = get_parent_index(nodes, node_b_index);

        #[allow(dead_code)] // The compiler falsely detects dead code here
        fn get_is_left_child(nodes: &Vec<BVHNode>,
                             node_index: usize,
                             node_parent_index: usize)
                             -> bool {
            let node_parent = &nodes[node_parent_index];

            match *node_parent {
                BVHNode::Node { child_l, .. } => child_l == node_index,
                _ => should_not_happen!(),
            }
        }

        let node_a_is_left_child = get_is_left_child(nodes, node_a_index, node_a_parent_index);
        let node_b_is_left_child = get_is_left_child(nodes, node_b_index, node_b_parent_index);

        #[allow(dead_code)] // The compiler falsely detects dead code here
        fn connect_nodes(nodes: &mut Vec<BVHNode>,
                         child_index: usize,
                         parent_index: usize,
                         left_child: bool) {
            // Set parent's child and get its depth
            let parent_depth = {
                let mut parent = &mut nodes[parent_index];
                match parent {
                    &mut BVHNode::Node { ref mut child_l, ref mut child_r, depth, .. } => {
                        if left_child {
                            *child_l = child_index;
                        } else {
                            *child_r = child_index;
                        }
                        depth
                    }
                    _ => should_not_happen!(),
                }
            };

            // Set child's parent and depth
            {
                let mut child = &mut nodes[child_index];
                match child {
                    &mut BVHNode::Node { ref mut parent, ref mut depth, .. } |
                    &mut BVHNode::Leaf { ref mut parent, ref mut depth, .. } => {
                        *parent = parent_index;
                        *depth = parent_depth + 1;
                    }
                    _ => should_not_happen!(),
                };
            }
        }

        connect_nodes(nodes,
                      node_a_index,
                      node_b_parent_index,
                      node_b_is_left_child);
        connect_nodes(nodes,
                      node_b_index,
                      node_a_parent_index,
                      node_a_is_left_child);
    }
}

#[cfg(test)]
pub mod tests {
    use bvh::BVH;

    #[test]
    /// Tests if the optimize function tries to change a fresh BVH even though it shouldn't
    fn test_optimizing_new_bvh() {
        let (mut shapes, mut bvh) = build_some_bh::<BVH>();

        let refit_shape_indices: HashSet<usize> = (0..shapes.len()).collect();
        bvh.optimize(&refit_shape_indices, &mut shapes);
    }

    // TODO Add tests for:
    // * correct parent
    // * correct depth
    // * correct BVH after optimizing
    // * correct parent and depth after optimizing
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::io::prelude::*;
use diesel::deserialize::{self, FromSql};
use diesel::expression::{AsExpression, Expression};
use diesel::pg::Pg;
use diesel::serialize::{self, IsNull, Output, ToSql};
use diesel::sql_types::Cidr;
use crate::{IpNetwork, Ipv4Network, Ipv6Network};
use crate::postgres_common;

type BoxedError = Box<Error + Sync + Send>;

impl FromSql<Cidr, Pg> for Ipv4Network {
    fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> {
        bytes
            .ok_or_else(|| "Input for Ipv4Network::from_sql is empty".into())
            .and_then(postgres_common::from_sql_ipv4_network)
    }
}

impl FromSql<Cidr, Pg> for Ipv6Network {
    fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> {
        bytes
            .ok_or_else(|| "Input for Ipv6Network::from_sql is empty".into())
            .and_then(postgres_common::from_sql_ipv6_network)
    }
}

impl FromSql<Cidr, Pg> for IpNetwork {
    fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> {
        let raw = bytes
            .ok_or_else::<BoxedError, _>(|| "Input for IpNetwork::from_sql is empty".into())?;
        match raw[0] {
            postgres_common::IPV4_TYPE => Ok(IpNetwork::V4(Ipv4Network::from_sql(bytes)?)),
            postgres_common::IPV6_TYPE => Ok(IpNetwork::V6(Ipv6Network::from_sql(bytes)?)),
            _ => Err("CIDR is not IP version 4 or 6".into()),
        }
    }
}

impl ToSql<Cidr, Pg> for Ipv4Network {
    fn to_sql<W: Write>(&self, out: &mut Output<W, Pg>) -> serialize::Result {
        let data = postgres_common::to_sql_ipv4_network(*self);
        out.write_all(&data).map(|_| IsNull::No).map_err(Into::into)
    }
}

impl ToSql<Cidr, Pg> for Ipv6Network {
    fn to_sql<W: Write>(&self, out: &mut Output<W, Pg>) -> serialize::Result {
        let data = postgres_common::to_sql_ipv6_network(*self);
        out.write_all(&data).map(|_| IsNull::No).map_err(Into::into)
    }
}

impl ToSql<Cidr, Pg> for IpNetwork {
    fn to_sql<W: Write>(&self, out: &mut Output<W, Pg>) -> serialize::Result {
        match *self {
            IpNetwork::V4(ref network) => {
                let data = postgres_common::to_sql_ipv4_network(*network);
                out.write_all(&data).map(|_| IsNull::No).map_err(Into::into)
            }
            IpNetwork::V6(ref network) => {
                let data = postgres_common::to_sql_ipv6_network(*network);
                out.write_all(&data).map(|_| IsNull::No).map_err(Into::into)
            }
        }
    }
}

#[allow(dead_code)]
mod foreign_derives {
    use super::*;

    #[derive(FromSqlRow, AsExpression)]
    #[diesel(foreign_derive)]
    #[sql_type = "Cidr"]
    struct IpNetworkProxy(IpNetwork);

    #[derive(FromSqlRow, AsExpression)]
    #[diesel(foreign_derive)]
    #[sql_type = "Cidr"]
    struct Ipv4NetworkProxy(Ipv4Network);

    #[derive(FromSqlRow, AsExpression)]
    #[diesel(foreign_derive)]
    #[sql_type = "Cidr"]
    struct Ipv6NetworkProxy(Ipv6Network);
}

diesel_infix_operator!(IsContainedBy, " << ", backend: Pg);
diesel_infix_operator!(IsContainedByOrEquals, " <<= ", backend: Pg);
diesel_infix_operator!(Contains, " >> ", backend: Pg);
diesel_infix_operator!(ContainsOrEquals, " >>= ", backend: Pg);
diesel_infix_operator!(ContainsOrIsContainedBy, " && ", backend: Pg);

/// Support for PostgreSQL Network Address Operators for Diesel
///
/// See [PostgreSQL documentation for details](https://www.postgresql.org/docs/current/static/functions-net.html).
pub trait PqCidrExtensionMethods: Expression<SqlType = Cidr> + Sized {
    /// Creates a SQL `<<` expression.
    fn is_contained_by<T>(self, other: T) -> IsContainedBy<Self, T::Expression>
    where
        T: AsExpression<Self::SqlType>,
    {
        IsContainedBy::new(self, other.as_expression())
    }

    /// Creates a SQL `<<=` expression.
    fn is_contained_by_or_equals<T>(self, other: T) -> IsContainedByOrEquals<Self, T::Expression>
    where
        T: AsExpression<Self::SqlType>,
    {
        IsContainedByOrEquals::new(self, other.as_expression())
    }

    /// Creates a SQL `>>` expression.
    fn contains<T>(self, other: T) -> Contains<Self, T::Expression>
    where
        T: AsExpression<Self::SqlType>,
    {
        Contains::new(self, other.as_expression())
    }

    /// Creates a SQL `>>=` expression.
    fn contains_or_equals<T>(self, other: T) -> ContainsOrEquals<Self, T::Expression>
    where
        T: AsExpression<Self::SqlType>,
    {
        ContainsOrEquals::new(self, other.as_expression())
    }

    /// Creates a SQL `&&` expression.
    fn contains_or_is_contained_by<T>(
        self,
        other: T,
    ) -> ContainsOrIsContainedBy<Self, T::Expression>
    where
        T: AsExpression<Self::SqlType>,
    {
        ContainsOrIsContainedBy::new(self, other.as_expression())
    }
}

impl<T> PqCidrExtensionMethods for T where T: Expression<SqlType = Cidr> {}

#[cfg(test)]
mod tests {
    use super::PqCidrExtensionMethods;
    use super::{IpNetwork, Ipv4Network, Ipv6Network};
    use std::net::Ipv4Addr;

    table! {
        test {
            id -> Integer,
            ip_network -> Cidr,
            ipv4_network -> Cidr,
            ipv6_network -> Cidr,
        }
    }

    #[derive(Insertable)]
    #[table_name = "test"]
    pub struct NewPost {
        pub id: i32,
        pub ip_network: IpNetwork,
        pub ipv4_network: Ipv4Network,
        pub ipv6_network: Ipv6Network,
    }

    #[test]
    fn operators() {
        let ip = IpNetwork::new(Ipv4Addr::new(127, 0, 0, 1), 32).unwrap();
        test::ip_network.is_contained_by(&ip);
        test::ip_network.is_contained_by_or_equals(&ip);
        test::ip_network.contains(&ip);
        test::ip_network.contains_or_equals(&ip);
        test::ip_network.contains_or_is_contained_by(&ip);
    }
}
<commit_msg>Simplified Diesel's ToSql for IpNetwork and added tests<commit_after>use std::error::Error;
use std::io::prelude::*;
use diesel::deserialize::{self, FromSql};
use diesel::expression::{AsExpression, Expression};
use diesel::pg::Pg;
use diesel::serialize::{self, IsNull, Output, ToSql};
use diesel::sql_types::Cidr;
use crate::{IpNetwork, Ipv4Network, Ipv6Network};
use crate::postgres_common;

type BoxedError = Box<Error + Sync + Send>;

impl FromSql<Cidr, Pg> for Ipv4Network {
    fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> {
        bytes
            .ok_or_else(|| "Input for Ipv4Network::from_sql is empty".into())
            .and_then(postgres_common::from_sql_ipv4_network)
    }
}

impl FromSql<Cidr, Pg> for Ipv6Network {
    fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> {
        bytes
            .ok_or_else(|| "Input for Ipv6Network::from_sql is empty".into())
            .and_then(postgres_common::from_sql_ipv6_network)
    }
}

impl FromSql<Cidr, Pg> for IpNetwork {
    fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> {
        let raw = bytes
            .ok_or_else::<BoxedError, _>(|| "Input for IpNetwork::from_sql is empty".into())?;
        match raw[0] {
            postgres_common::IPV4_TYPE => Ok(IpNetwork::V4(Ipv4Network::from_sql(bytes)?)),
            postgres_common::IPV6_TYPE => Ok(IpNetwork::V6(Ipv6Network::from_sql(bytes)?)),
            _ => Err("CIDR is not IP version 4 or 6".into()),
        }
    }
}

impl ToSql<Cidr, Pg> for Ipv4Network {
    fn to_sql<W: Write>(&self, out: &mut Output<W, Pg>) -> serialize::Result {
        let data = postgres_common::to_sql_ipv4_network(*self);
        out.write_all(&data).map(|_| IsNull::No).map_err(Into::into)
    }
}

impl ToSql<Cidr, Pg> for Ipv6Network {
    fn to_sql<W: Write>(&self, out: &mut Output<W, Pg>) -> serialize::Result {
        let data = postgres_common::to_sql_ipv6_network(*self);
        out.write_all(&data).map(|_| IsNull::No).map_err(Into::into)
    }
}

impl ToSql<Cidr, Pg> for IpNetwork {
    fn to_sql<W: Write>(&self, out: &mut Output<W, Pg>) -> serialize::Result {
        match self {
            IpNetwork::V4(network) => ToSql::<Cidr, Pg>::to_sql(network, out),
            IpNetwork::V6(network) => ToSql::<Cidr, Pg>::to_sql(network, out),
        }
    }
}

#[allow(dead_code)]
mod foreign_derives {
    use super::*;

    #[derive(FromSqlRow, AsExpression)]
    #[diesel(foreign_derive)]
    #[sql_type = "Cidr"]
    struct IpNetworkProxy(IpNetwork);

    #[derive(FromSqlRow, AsExpression)]
    #[diesel(foreign_derive)]
    #[sql_type = "Cidr"]
    struct Ipv4NetworkProxy(Ipv4Network);

    #[derive(FromSqlRow, AsExpression)]
    #[diesel(foreign_derive)]
    #[sql_type = "Cidr"]
    struct Ipv6NetworkProxy(Ipv6Network);
}

diesel_infix_operator!(IsContainedBy, " << ", backend: Pg);
diesel_infix_operator!(IsContainedByOrEquals, " <<= ", backend: Pg);
diesel_infix_operator!(Contains, " >> ", backend: Pg);
diesel_infix_operator!(ContainsOrEquals, " >>= ", backend: Pg);
diesel_infix_operator!(ContainsOrIsContainedBy, " && ", backend: Pg);

/// Support for PostgreSQL Network Address Operators for Diesel
///
/// See [PostgreSQL documentation for details](https://www.postgresql.org/docs/current/static/functions-net.html).
pub trait PqCidrExtensionMethods: Expression<SqlType = Cidr> + Sized {
    /// Creates a SQL `<<` expression.
    fn is_contained_by<T>(self, other: T) -> IsContainedBy<Self, T::Expression>
    where
        T: AsExpression<Self::SqlType>,
    {
        IsContainedBy::new(self, other.as_expression())
    }

    /// Creates a SQL `<<=` expression.
    fn is_contained_by_or_equals<T>(self, other: T) -> IsContainedByOrEquals<Self, T::Expression>
    where
        T: AsExpression<Self::SqlType>,
    {
        IsContainedByOrEquals::new(self, other.as_expression())
    }

    /// Creates a SQL `>>` expression.
    fn contains<T>(self, other: T) -> Contains<Self, T::Expression>
    where
        T: AsExpression<Self::SqlType>,
    {
        Contains::new(self, other.as_expression())
    }

    /// Creates a SQL `>>=` expression.
    fn contains_or_equals<T>(self, other: T) -> ContainsOrEquals<Self, T::Expression>
    where
        T: AsExpression<Self::SqlType>,
    {
        ContainsOrEquals::new(self, other.as_expression())
    }

    /// Creates a SQL `&&` expression.
    fn contains_or_is_contained_by<T>(
        self,
        other: T,
    ) -> ContainsOrIsContainedBy<Self, T::Expression>
    where
        T: AsExpression<Self::SqlType>,
    {
        ContainsOrIsContainedBy::new(self, other.as_expression())
    }
}

impl<T> PqCidrExtensionMethods for T where T: Expression<SqlType = Cidr> {}

#[cfg(test)]
mod tests {
    use std::net::{Ipv4Addr, Ipv6Addr};
    use diesel::sql_types::Cidr;
    use diesel::pg::Pg;
    use diesel::serialize::{Output, ToSql};
    use diesel::deserialize::FromSql;
    use super::PqCidrExtensionMethods;
    use super::{IpNetwork, Ipv4Network, Ipv6Network};

    table! {
        test {
            id -> Integer,
            ip_network -> Cidr,
            ipv4_network -> Cidr,
            ipv6_network -> Cidr,
        }
    }

    #[derive(Insertable)]
    #[table_name = "test"]
    pub struct NewPost {
        pub id: i32,
        pub ip_network: IpNetwork,
        pub ipv4_network: Ipv4Network,
        pub ipv6_network: Ipv6Network,
    }

    fn test_output() -> Output<'static, Vec<u8>, Pg> {
        use std::mem;
        Output::new(Vec::new(), unsafe { mem::uninitialized() })
    }

    #[test]
    fn ipv4_network() {
        let mut bytes = test_output();
        let ipv4_network = Ipv4Network::new(Ipv4Addr::new(1, 2, 3, 4), 32).unwrap();
        ToSql::<Cidr, Pg>::to_sql(&ipv4_network, &mut bytes).unwrap();
        let converted: Ipv4Network = FromSql::<Cidr, Pg>::from_sql(Some(bytes.as_ref())).unwrap();
        assert_eq!(ipv4_network, converted);
    }

    #[test]
    fn ipv6_network() {
        let mut bytes = test_output();
        let ipv6_network = Ipv6Network::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 128).unwrap();
        ToSql::<Cidr, Pg>::to_sql(&ipv6_network, &mut bytes).unwrap();
        let converted: Ipv6Network = FromSql::<Cidr, Pg>::from_sql(Some(bytes.as_ref())).unwrap();
        assert_eq!(ipv6_network, converted);
    }

    #[test]
    fn ip_network() {
        let mut bytes = test_output();
        let ip_network = IpNetwork::V6(Ipv6Network::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 128).unwrap());
        ToSql::<Cidr, Pg>::to_sql(&ip_network, &mut bytes).unwrap();
        let converted: IpNetwork = FromSql::<Cidr, Pg>::from_sql(Some(bytes.as_ref())).unwrap();
        assert_eq!(ip_network, converted);
    }

    #[test]
    fn operators() {
        let ip = IpNetwork::new(Ipv4Addr::new(127, 0, 0, 1), 32).unwrap();
        test::ip_network.is_contained_by(&ip);
        test::ip_network.is_contained_by_or_equals(&ip);
        test::ip_network.contains(&ip);
        test::ip_network.contains_or_equals(&ip);
        test::ip_network.contains_or_is_contained_by(&ip);
    }
}
<|endoftext|>


<commit_before>// VirtIO block device driver
// https://wiki.osdev.org/Virtio

use volatile;
use core::mem;
use core::ptr;
use alloc::prelude::*;

use mem_map::MEM_PAGE_SIZE_BYTES;

use crate::virtio;

use super::BlockDevice;

bitflags! {
    /// http://docs.oasis-open.org/virtio/virtio/v1.0/cs04/virtio-v1.0-cs04.html#x1-2050003
    struct Features: u32 {
        const LEGACY_BARRIER      = 1 << 0;
        const SIZE_MAX            = 1 << 1;
        const SEG_MAX             = 1 << 2;
        const GEOMETRY            = 1 << 4;
        const READ_ONLY           = 1 << 5;
        const BLOCK_SIZE          = 1 << 6;
        const LEAGCY_SCSI         = 1 << 9;
        const CACHE_FLUSH         = 1 << 9;
        const TOPOLOGY            = 1 << 10;
        const CONFIG_WCE          = 1 << 11;
    }
}

#[derive(Copy, Clone)]
#[repr(C)]
struct BlockRequest {
    type_: u32, // 0: read, 1: write
    ioprio: u32, // IO priority, unused in 1.0 spec
    sector: u64, // sector number
    data: [u8; 0x200],
    status: u8, // 0: ok, 1: error, 2: unsupported
}
impl BlockRequest {
    pub const HEADER_SIZE: usize = 16;
    pub const CONTENT_SIZE: usize = 0x200;
    pub const FOOTER_SIZE: usize = 1;

    pub fn new_read(sector: u64) -> BlockRequest {
        BlockRequest {
            type_: 0,
            ioprio: 0,
            sector,
            data: [0; 0x200],
            status: 0xff,
        }
    }
    pub fn new_write(sector: u64) -> BlockRequest {
        BlockRequest {type_: 1, ..Self::new_read(sector)}
    }
    pub fn new_flush() -> BlockRequest {
        BlockRequest {type_: 4, ..Self::new_read(0)}
    }
}

pub struct VirtioBlock {
    device: virtio::VirtioDevice,
    queues: Vec<virtio::VirtQueue>,
}
impl VirtioBlock {
    pub fn try_new() -> Option<Box<BlockDevice>> {
        if let Some(mut device) = virtio::VirtioDevice::try_new(virtio::DeviceType::BlockDevice) {
            // Update device state
            device.set_status(virtio::DeviceStatus::ACKNOWLEDGE);

            Some(box VirtioBlock {
                device,
                queues: Vec::new(),
            })
        }
        else {
            None
        }
    }
}
impl BlockDevice for VirtioBlock {
    fn init(&mut self) -> bool {
        // Tell the device that it's supported by this driver
        self.device.set_status(virtio::DeviceStatus::STATE_LOADED);

        // Feature negotiation
        let mut feature_bits = self.device.features();
        // TODO: Accept Read-Only device, and mark this as read-only
        // feature_bits ??? Features::READ_ONLY;

        if !self.device.set_features(feature_bits) {
            return false;
        }

        // Memory queues
        self.queues = self.device.init_queues();
        assert_eq!(self.queues.len(), 1);

        // Update device state
        self.device.set_status(virtio::DeviceStatus::STATE_READY);

        rprintln!("VirtIO-blk: Device ready");

        // Success
        true
    }

    fn capacity_bytes(&mut self) -> u64 {
        self.device.read_dev_config::<u64>(0x00) * 0x200
    }

    fn read(&mut self, sector: u64) -> Vec<u8> {
        let req = volatile::ReadOnly::new(BlockRequest::new_read(sector));

        let mut queue = &mut self.queues[0];
        let buf_indices = queue.find_free_n(2).expect("VirtIO-blk: Not enough queue slots free");

        // Set buffer descriptor
        let mut desc0 = virtio::VirtQueueDesc::new_read(
            (&req) as *const _ as u64,
            BlockRequest::HEADER_SIZE as u32
        );
        let desc1 = virtio::VirtQueueDesc::new_write(
            (&req) as *const _ as u64 + BlockRequest::HEADER_SIZE as u64,
            BlockRequest::CONTENT_SIZE as u32 + BlockRequest::FOOTER_SIZE as u32
        );

        desc0.chain(buf_indices[1]);

        queue.set_desc_at(buf_indices[0], desc0);
        queue.set_desc_at(buf_indices[1], desc1);

        // Add it in the available ring
        let mut ah = queue.available_header();
        let index = ah.index % queue.queue_size;
        queue.set_available_item(index, buf_indices[0]);
        ah.index = ah.index.wrapping_add(1);
        queue.set_available_header(ah);

        // Notify the device about the change
        self.device.notify(0);

        rprintln!("VirtIO-blk: Read: Poll");

        // Poll status byte
        let data = loop {
            let req_done = req.read();
            match req_done.status {
                0xff => {},
                0 => break req_done.data,
                1 => panic!("VirtIO read failed (1 - IOERR)"),
                2 => panic!("VirtIO read failed (2 - UNSUPP)"),
                n => panic!("VirtIO read failed ({} - ?)", n),
            }

            use time::sleep_ms;
            sleep_ms(1);
        };

        rprintln!("VirtIO-blk: Read: Ok");

        data.to_vec()
    }

    fn write(&mut self, sector: u64, data: Vec<u8>) {}
}
<commit_msg>Update documentation comments<commit_after>// VirtIO block device driver
// https://wiki.osdev.org/Virtio

use volatile;
use core::mem;
use core::ptr;
use alloc::prelude::*;

use mem_map::MEM_PAGE_SIZE_BYTES;

use crate::virtio;

use super::BlockDevice;

bitflags! {
    /// http://docs.oasis-open.org/virtio/virtio/v1.0/cs04/virtio-v1.0-cs04.html#x1-2050003
    struct Features: u32 {
        const LEGACY_BARRIER      = 1 << 0;
        const SIZE_MAX            = 1 << 1;
        const SEG_MAX             = 1 << 2;
        const GEOMETRY            = 1 << 4;
        const READ_ONLY           = 1 << 5;
        const BLOCK_SIZE          = 1 << 6;
        const LEAGCY_SCSI         = 1 << 9;
        const CACHE_FLUSH         = 1 << 9;
        const TOPOLOGY            = 1 << 10;
        const CONFIG_WCE          = 1 << 11;
    }
}

#[derive(Copy, Clone)]
#[repr(C)]
struct BlockRequest {
    type_: u32, // 0: read, 1: write, 5: flush
    ioprio: u32, // IO priority, unused in 1.0 spec
    sector: u64, // sector number
    data: [u8; 0x200],
    status: u8, // 0: ok, 1: error, 2: unsupported
}
impl BlockRequest {
    pub const HEADER_SIZE: usize = 16;
    pub const CONTENT_SIZE: usize = 0x200;
    pub const FOOTER_SIZE: usize = 1;

    pub fn new_read(sector: u64) -> BlockRequest {
        BlockRequest {
            type_: 0,
            ioprio: 0,
            sector,
            data: [0; 0x200],
            status: 0xff,
        }
    }
    pub fn new_write(sector: u64) -> BlockRequest {
        BlockRequest {type_: 1, ..Self::new_read(sector)}
    }
    pub fn new_flush() -> BlockRequest {
        BlockRequest {type_: 4, ..Self::new_read(0)}
    }
}

pub struct VirtioBlock {
    device: virtio::VirtioDevice,
    queues: Vec<virtio::VirtQueue>,
}
impl VirtioBlock {
    pub fn try_new() -> Option<Box<BlockDevice>> {
        if let Some(mut device) = virtio::VirtioDevice::try_new(virtio::DeviceType::BlockDevice) {
            // Update device state
            device.set_status(virtio::DeviceStatus::ACKNOWLEDGE);

            Some(box VirtioBlock {
                device,
                queues: Vec::new(),
            })
        }
        else {
            None
        }
    }
}
impl BlockDevice for VirtioBlock {
    fn init(&mut self) -> bool {
        // Tell the device that it's supported by this driver
        self.device.set_status(virtio::DeviceStatus::STATE_LOADED);

        // Feature negotiation
        let mut feature_bits = self.device.features();
        // TODO: Accept Read-Only device, and mark this as read-only
        // feature_bits ??? Features::READ_ONLY;

        if !self.device.set_features(feature_bits) {
            return false;
        }

        // Memory queues
        self.queues = self.device.init_queues();
        assert_eq!(self.queues.len(), 1);

        // Update device state
        self.device.set_status(virtio::DeviceStatus::STATE_READY);

        rprintln!("VirtIO-blk: Device ready");

        // Success
        true
    }

    fn capacity_bytes(&mut self) -> u64 {
        self.device.read_dev_config::<u64>(0x00) * 0x200
    }

    fn read(&mut self, sector: u64) -> Vec<u8> {
        let req = volatile::ReadOnly::new(BlockRequest::new_read(sector));

        let mut queue = &mut self.queues[0];
        let buf_indices = queue.find_free_n(2).expect("VirtIO-blk: Not enough queue slots free");

        // Set buffer descriptor
        let mut desc0 = virtio::VirtQueueDesc::new_read(
            (&req) as *const _ as u64,
            BlockRequest::HEADER_SIZE as u32
        );
        let desc1 = virtio::VirtQueueDesc::new_write(
            (&req) as *const _ as u64 + BlockRequest::HEADER_SIZE as u64,
            BlockRequest::CONTENT_SIZE as u32 + BlockRequest::FOOTER_SIZE as u32
        );

        desc0.chain(buf_indices[1]);

        queue.set_desc_at(buf_indices[0], desc0);
        queue.set_desc_at(buf_indices[1], desc1);

        // Add it in the available ring
        let mut ah = queue.available_header();
        let index = ah.index % queue.queue_size;
        queue.set_available_item(index, buf_indices[0]);
        ah.index = ah.index.wrapping_add(1);
        queue.set_available_header(ah);

        // Notify the device about the change
        self.device.notify(0);

        rprintln!("VirtIO-blk: Read: Poll");

        // Poll status byte
        let data = loop {
            let req_done = req.read();
            match req_done.status {
                0xff => {},
                0 => break req_done.data,
                1 => panic!("VirtIO read failed (1 - IOERR)"),
                2 => panic!("VirtIO read failed (2 - UNSUPP)"),
                n => panic!("VirtIO read failed ({} - ?)", n),
            }

            use time::sleep_ms;
            sleep_ms(1);
        };

        rprintln!("VirtIO-blk: Read: Ok");

        data.to_vec()
    }

    fn write(&mut self, sector: u64, data: Vec<u8>) {}
}
<|endoftext|>


<commit_before>#![feature(test)] // compiletest_rs requires this attribute

use compiletest_rs as compiletest;
use compiletest_rs::common::Mode as TestMode;

use std::env::{self, set_var};
use std::ffi::OsStr;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};

mod cargo;

fn host_lib() -> PathBuf {
    if let Some(path) = option_env!("HOST_LIBS") {
        PathBuf::from(path)
    } else {
        cargo::CARGO_TARGET_DIR.join(env!("PROFILE"))
    }
}

fn clippy_driver_path() -> PathBuf {
    if let Some(path) = option_env!("CLIPPY_DRIVER_PATH") {
        PathBuf::from(path)
    } else {
        cargo::TARGET_LIB.join("clippy-driver")
    }
}

// When we'll want to use `extern crate ..` for a dependency that is used
// both by the crate and the compiler itself, we can't simply pass -L flags
// as we'll get a duplicate matching versions. Instead, disambiguate with
// `--extern dep=path`.
// See https://github.com/rust-lang/rust-clippy/issues/4015.
//
// FIXME: We cannot use `cargo build --message-format=json` to resolve to dependency files.
//        Because it would force-rebuild if the options passed to `build` command is not the same
//        as what we manually pass to `cargo` invocation
fn third_party_crates() -> String {
    use std::collections::HashMap;
    static CRATES: &[&str] = &["serde", "serde_derive", "regex", "clippy_lints", "syn", "quote"];
    let dep_dir = cargo::TARGET_LIB.join("deps");
    let mut crates: HashMap<&str, PathBuf> = HashMap::with_capacity(CRATES.len());
    for entry in fs::read_dir(dep_dir).unwrap() {
        let path = match entry {
            Ok(entry) => entry.path(),
            Err(_) => continue,
        };
        if let Some(name) = path.file_name().and_then(OsStr::to_str) {
            for dep in CRATES {
                if name.starts_with(&format!("lib{}-", dep)) && name.ends_with(".rlib") {
                    crates.entry(dep).or_insert(path);
                    break;
                }
            }
        }
    }

    let v: Vec<_> = crates
        .into_iter()
        .map(|(dep, path)| format!("--extern {}={}", dep, path.display()))
        .collect();
    v.join(" ")
}

fn default_config() -> compiletest::Config {
    let mut config = compiletest::Config::default();

    if let Ok(name) = env::var("TESTNAME") {
        config.filter = Some(name);
    }

    if let Some(path) = option_env!("RUSTC_LIB_PATH") {
        let path = PathBuf::from(path);
        config.run_lib_path = path.clone();
        config.compile_lib_path = path;
    }

    config.target_rustcflags = Some(format!(
        "-L {0} -L {1} -Dwarnings -Zui-testing {2}",
        host_lib().join("deps").display(),
        cargo::TARGET_LIB.join("deps").display(),
        third_party_crates(),
    ));

    config.build_base = if cargo::is_rustc_test_suite() {
        // This make the stderr files go to clippy OUT_DIR on rustc repo build dir
        let mut path = PathBuf::from(env!("OUT_DIR"));
        path.push("test_build_base");
        path
    } else {
        host_lib().join("test_build_base")
    };
    config.rustc_path = clippy_driver_path();
    config
}

fn run_mode(cfg: &mut compiletest::Config) {
    cfg.mode = TestMode::Ui;
    cfg.src_base = Path::new("tests").join("ui");
    compiletest::run_tests(&cfg);
}

fn run_ui_toml(config: &mut compiletest::Config) {
    fn run_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>) -> Result<bool, io::Error> {
        let mut result = true;
        let opts = compiletest::test_opts(config);
        for dir in fs::read_dir(&config.src_base)? {
            let dir = dir?;
            if !dir.file_type()?.is_dir() {
                continue;
            }
            let dir_path = dir.path();
            set_var("CARGO_MANIFEST_DIR", &dir_path);
            for file in fs::read_dir(&dir_path)? {
                let file = file?;
                let file_path = file.path();
                if file.file_type()?.is_dir() {
                    continue;
                }
                if file_path.extension() != Some(OsStr::new("rs")) {
                    continue;
                }
                let paths = compiletest::common::TestPaths {
                    file: file_path,
                    base: config.src_base.clone(),
                    relative_dir: dir_path.file_name().unwrap().into(),
                };
                let test_name = compiletest::make_test_name(&config, &paths);
                let index = tests
                    .iter()
                    .position(|test| test.desc.name == test_name)
                    .expect("The test should be in there");
                result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
            }
        }
        Ok(result)
    }

    config.mode = TestMode::Ui;
    config.src_base = Path::new("tests").join("ui-toml").canonicalize().unwrap();

    let tests = compiletest::make_tests(&config);

    let res = run_tests(&config, tests);
    match res {
        Ok(true) => {},
        Ok(false) => panic!("Some tests failed"),
        Err(e) => {
            panic!("I/O failure during tests: {:?}", e);
        },
    }
}

fn run_ui_cargo(config: &mut compiletest::Config) {
    fn run_tests(
        config: &compiletest::Config,
        filter: &Option<String>,
        mut tests: Vec<tester::TestDescAndFn>,
    ) -> Result<bool, io::Error> {
        let mut result = true;
        let opts = compiletest::test_opts(config);

        for dir in fs::read_dir(&config.src_base)? {
            let dir = dir?;
            if !dir.file_type()?.is_dir() {
                continue;
            }

            // Use the filter if provided
            let dir_path = dir.path();
            match &filter {
                Some(name) if !dir_path.ends_with(name) => continue,
                _ => {},
            }

            for case in fs::read_dir(&dir_path)? {
                let case = case?;
                if !case.file_type()?.is_dir() {
                    continue;
                }

                let src_path = case.path().join("src");
                env::set_current_dir(&src_path)?;

                for file in fs::read_dir(&src_path)? {
                    let file = file?;
                    if file.file_type()?.is_dir() {
                        continue;
                    }

                    // Search for the main file to avoid running a test for each file in the project
                    let file_path = file.path();
                    match file_path.file_name().and_then(OsStr::to_str) {
                        Some("main.rs") => {},
                        _ => continue,
                    }

                    let paths = compiletest::common::TestPaths {
                        file: file_path,
                        base: config.src_base.clone(),
                        relative_dir: src_path.strip_prefix(&config.src_base).unwrap().into(),
                    };
                    let test_name = compiletest::make_test_name(&config, &paths);
                    let index = tests
                        .iter()
                        .position(|test| test.desc.name == test_name)
                        .expect("The test should be in there");
                    result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
                }
            }
        }
        Ok(result)
    }

    config.mode = TestMode::Ui;
    config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap();

    let tests = compiletest::make_tests(&config);

    let current_dir = env::current_dir().unwrap();
    let filter = env::var("TESTNAME").ok();
    let res = run_tests(&config, &filter, tests);
    env::set_current_dir(current_dir).unwrap();

    match res {
        Ok(true) => {},
        Ok(false) => panic!("Some tests failed"),
        Err(e) => {
            panic!("I/O failure during tests: {:?}", e);
        },
    }
}

fn prepare_env() {
    set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
    set_var("CLIPPY_TESTS", "true");
    //set_var("RUST_BACKTRACE", "0");
}

#[test]
fn compile_test() {
    prepare_env();
    let mut config = default_config();
    run_mode(&mut config);
    run_ui_toml(&mut config);
    run_ui_cargo(&mut config);
}
<commit_msg>Temp fix: don't run cargo lint tests in rustc test suite<commit_after>#![feature(test)] // compiletest_rs requires this attribute

use compiletest_rs as compiletest;
use compiletest_rs::common::Mode as TestMode;

use std::env::{self, set_var};
use std::ffi::OsStr;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};

mod cargo;

fn host_lib() -> PathBuf {
    if let Some(path) = option_env!("HOST_LIBS") {
        PathBuf::from(path)
    } else {
        cargo::CARGO_TARGET_DIR.join(env!("PROFILE"))
    }
}

fn clippy_driver_path() -> PathBuf {
    if let Some(path) = option_env!("CLIPPY_DRIVER_PATH") {
        PathBuf::from(path)
    } else {
        cargo::TARGET_LIB.join("clippy-driver")
    }
}

// When we'll want to use `extern crate ..` for a dependency that is used
// both by the crate and the compiler itself, we can't simply pass -L flags
// as we'll get a duplicate matching versions. Instead, disambiguate with
// `--extern dep=path`.
// See https://github.com/rust-lang/rust-clippy/issues/4015.
//
// FIXME: We cannot use `cargo build --message-format=json` to resolve to dependency files.
//        Because it would force-rebuild if the options passed to `build` command is not the same
//        as what we manually pass to `cargo` invocation
fn third_party_crates() -> String {
    use std::collections::HashMap;
    static CRATES: &[&str] = &["serde", "serde_derive", "regex", "clippy_lints", "syn", "quote"];
    let dep_dir = cargo::TARGET_LIB.join("deps");
    let mut crates: HashMap<&str, PathBuf> = HashMap::with_capacity(CRATES.len());
    for entry in fs::read_dir(dep_dir).unwrap() {
        let path = match entry {
            Ok(entry) => entry.path(),
            Err(_) => continue,
        };
        if let Some(name) = path.file_name().and_then(OsStr::to_str) {
            for dep in CRATES {
                if name.starts_with(&format!("lib{}-", dep)) && name.ends_with(".rlib") {
                    crates.entry(dep).or_insert(path);
                    break;
                }
            }
        }
    }

    let v: Vec<_> = crates
        .into_iter()
        .map(|(dep, path)| format!("--extern {}={}", dep, path.display()))
        .collect();
    v.join(" ")
}

fn default_config() -> compiletest::Config {
    let mut config = compiletest::Config::default();

    if let Ok(name) = env::var("TESTNAME") {
        config.filter = Some(name);
    }

    if let Some(path) = option_env!("RUSTC_LIB_PATH") {
        let path = PathBuf::from(path);
        config.run_lib_path = path.clone();
        config.compile_lib_path = path;
    }

    config.target_rustcflags = Some(format!(
        "-L {0} -L {1} -Dwarnings -Zui-testing {2}",
        host_lib().join("deps").display(),
        cargo::TARGET_LIB.join("deps").display(),
        third_party_crates(),
    ));

    config.build_base = if cargo::is_rustc_test_suite() {
        // This make the stderr files go to clippy OUT_DIR on rustc repo build dir
        let mut path = PathBuf::from(env!("OUT_DIR"));
        path.push("test_build_base");
        path
    } else {
        host_lib().join("test_build_base")
    };
    config.rustc_path = clippy_driver_path();
    config
}

fn run_mode(cfg: &mut compiletest::Config) {
    cfg.mode = TestMode::Ui;
    cfg.src_base = Path::new("tests").join("ui");
    compiletest::run_tests(&cfg);
}

fn run_ui_toml(config: &mut compiletest::Config) {
    fn run_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>) -> Result<bool, io::Error> {
        let mut result = true;
        let opts = compiletest::test_opts(config);
        for dir in fs::read_dir(&config.src_base)? {
            let dir = dir?;
            if !dir.file_type()?.is_dir() {
                continue;
            }
            let dir_path = dir.path();
            set_var("CARGO_MANIFEST_DIR", &dir_path);
            for file in fs::read_dir(&dir_path)? {
                let file = file?;
                let file_path = file.path();
                if file.file_type()?.is_dir() {
                    continue;
                }
                if file_path.extension() != Some(OsStr::new("rs")) {
                    continue;
                }
                let paths = compiletest::common::TestPaths {
                    file: file_path,
                    base: config.src_base.clone(),
                    relative_dir: dir_path.file_name().unwrap().into(),
                };
                let test_name = compiletest::make_test_name(&config, &paths);
                let index = tests
                    .iter()
                    .position(|test| test.desc.name == test_name)
                    .expect("The test should be in there");
                result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
            }
        }
        Ok(result)
    }

    config.mode = TestMode::Ui;
    config.src_base = Path::new("tests").join("ui-toml").canonicalize().unwrap();

    let tests = compiletest::make_tests(&config);

    let res = run_tests(&config, tests);
    match res {
        Ok(true) => {},
        Ok(false) => panic!("Some tests failed"),
        Err(e) => {
            panic!("I/O failure during tests: {:?}", e);
        },
    }
}

fn run_ui_cargo(config: &mut compiletest::Config) {
    if cargo::is_rustc_test_suite() {
        return;
    }
    fn run_tests(
        config: &compiletest::Config,
        filter: &Option<String>,
        mut tests: Vec<tester::TestDescAndFn>,
    ) -> Result<bool, io::Error> {
        let mut result = true;
        let opts = compiletest::test_opts(config);

        for dir in fs::read_dir(&config.src_base)? {
            let dir = dir?;
            if !dir.file_type()?.is_dir() {
                continue;
            }

            // Use the filter if provided
            let dir_path = dir.path();
            match &filter {
                Some(name) if !dir_path.ends_with(name) => continue,
                _ => {},
            }

            for case in fs::read_dir(&dir_path)? {
                let case = case?;
                if !case.file_type()?.is_dir() {
                    continue;
                }

                let src_path = case.path().join("src");
                env::set_current_dir(&src_path)?;

                for file in fs::read_dir(&src_path)? {
                    let file = file?;
                    if file.file_type()?.is_dir() {
                        continue;
                    }

                    // Search for the main file to avoid running a test for each file in the project
                    let file_path = file.path();
                    match file_path.file_name().and_then(OsStr::to_str) {
                        Some("main.rs") => {},
                        _ => continue,
                    }

                    let paths = compiletest::common::TestPaths {
                        file: file_path,
                        base: config.src_base.clone(),
                        relative_dir: src_path.strip_prefix(&config.src_base).unwrap().into(),
                    };
                    let test_name = compiletest::make_test_name(&config, &paths);
                    let index = tests
                        .iter()
                        .position(|test| test.desc.name == test_name)
                        .expect("The test should be in there");
                    result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
                }
            }
        }
        Ok(result)
    }

    config.mode = TestMode::Ui;
    config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap();

    let tests = compiletest::make_tests(&config);

    let current_dir = env::current_dir().unwrap();
    let filter = env::var("TESTNAME").ok();
    let res = run_tests(&config, &filter, tests);
    env::set_current_dir(current_dir).unwrap();

    match res {
        Ok(true) => {},
        Ok(false) => panic!("Some tests failed"),
        Err(e) => {
            panic!("I/O failure during tests: {:?}", e);
        },
    }
}

fn prepare_env() {
    set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
    set_var("CLIPPY_TESTS", "true");
    //set_var("RUST_BACKTRACE", "0");
}

#[test]
fn compile_test() {
    prepare_env();
    let mut config = default_config();
    run_mode(&mut config);
    run_ui_toml(&mut config);
    run_ui_cargo(&mut config);
}
<|endoftext|>