<commit_before>extern crate glib;

extern crate gtk;
use gtk::{Inhibit, WidgetExt};

extern crate cairo;

use std::iter::FromIterator;

use std::rc::Rc;
use std::cell::RefCell;

use std::collections::vec_deque::{VecDeque};

use ::media::{Context, AudioBuffer};

pub struct AudioController {
    container: gtk::Container,
    drawingarea: gtk::DrawingArea,

    stream_duration: u64,
    sample_duration: u64,
    stream_initialized: bool,

    sample_buffer: VecDeque<f64>,
    ts_offset: u64,
    ts_relative_offset: u64,
    buffer_duration: u64,
    has_received_last_buffer: bool,

    ts_relative_pos: u64,
    sample_skip_step: u64,
    sample_per_step: u64,
    iter_since_adjust: usize,
    min_iter_before_adjust: usize,
}

impl AudioController {
    pub fn new(builder: &gtk::Builder) -> Rc<RefCell<Self>> {
        let this = Rc::new(RefCell::new(AudioController {
            container: builder.get_object("audio-container").unwrap(),
            drawingarea: builder.get_object("audio-drawingarea").unwrap(),

            stream_duration: 0,
            sample_duration: 0,
            stream_initialized: false,

            sample_buffer: VecDeque::with_capacity(12 * 48_000), // 12s @ 48kHz
            ts_offset: 0,
            ts_relative_offset: 0,
            buffer_duration: 0,
            has_received_last_buffer: false,

            ts_relative_pos: 0,
            sample_skip_step: 0,
            sample_per_step: 0,
            iter_since_adjust: 0,
            min_iter_before_adjust: 5,
        }));

        {
            let this_ref = this.borrow();
            let this_weak = Rc::downgrade(&this);
            this_ref.drawingarea.connect_draw(move |drawing_area, cairo_ctx| {
                let this = this_weak.upgrade()
                    .expect("Main controller is no longer available for select_media");
                let result = this.borrow_mut().draw(drawing_area, cairo_ctx);
                result
            });
        }

        this
    }

    pub fn clear(&mut self) {
        self.sample_buffer.clear();
        self.stream_initialized = false;
    }

    pub fn new_media(&mut self, ctx: &Context) {
        if !self.sample_buffer.is_empty() {
            let duration = ctx.get_duration();
            if duration.is_negative() {
                panic!("Audio controller found a negative stream duration");
            }
            self.stream_duration = duration as u64;
            let position = ctx.get_position();
            if position.is_negative() {
                panic!("Audio controller found a negative initial position");
            }
            self.ts_offset = position as u64;
            self.stream_initialized = true;
            self.drawingarea.queue_draw();
            self.container.show();
        }
        else {
            self.container.hide();
        }
    }

    pub fn have_buffer(&mut self, mut buffer: AudioBuffer) {
        // assume the buffers come in order
        if self.sample_buffer.is_empty() {
            self.ts_offset = 0;
            self.ts_relative_offset = 0;
            self.buffer_duration = 0;
            self.has_received_last_buffer = false;

            self.ts_relative_pos = 0;
            self.sample_skip_step = 1;
            self.sample_per_step = 1;
            self.iter_since_adjust = 0;

            self.sample_duration = buffer.caps.sample_duration;
        }

        if buffer.pts < self.ts_offset {
            panic!("unordered buffers");
        }

        let mut samples_vecdeque = VecDeque::from_iter(buffer.samples.drain(..));
        self.sample_buffer.append(&mut samples_vecdeque);

        self.buffer_duration = self.sample_buffer.len() as u64 * self.sample_duration;
        self.has_received_last_buffer = self.stream_initialized
            && self.ts_relative_offset + self.buffer_duration >= self.stream_duration;

        if !self.has_received_last_buffer && self.buffer_duration > 10_000_000_000
            && self.ts_relative_pos > 3_000_000_000
        { // buffer larger than 10s and current pos is sufficient to:
            // remove 2s worse of samples
            // can't purge big chunks otherwise it impacts drawing
            // need to comply with current sample_per_step in order to
            // avoid discontinuities during waveform rendering
            // TODO: make sure if we actually loose decimal part
            let sample_nb_to_remove =
                (2_000_000_000 / self.sample_duration / self.sample_per_step)
                * self.sample_per_step;
            self.sample_buffer.drain(..(sample_nb_to_remove as usize));

            let duration_removed = sample_nb_to_remove * self.sample_duration;
            self.buffer_duration -= duration_removed;
            self.ts_offset += duration_removed;
            self.ts_relative_offset += duration_removed;
            self.ts_relative_pos -= duration_removed;
            //println!("buffer duration: {}", self.buffer_duration);
        }
    }

    pub fn have_position(&mut self, position: i64) {
        if position.is_negative() {
            panic!("Audio controller received a negative position");
        }
        if (position as u64) < self.ts_offset {
            panic!("Audio controller received a position earlier than first offset position: {}, offset {}", position, self.ts_offset);
        }
        let ts_relative_pos = position as u64 - self.ts_offset;

        if self.ts_relative_pos != ts_relative_pos {
            self.ts_relative_pos = ts_relative_pos;
            self.drawingarea.queue_draw();
        }
    }

    pub fn force_redraw(&mut self) {
        self.sample_skip_step = 1;
        self.iter_since_adjust = 0;
        self.drawingarea.queue_draw();
    }

    fn draw(&mut self, drawing_area: &gtk::DrawingArea, cr: &cairo::Context) -> Inhibit {
        if self.sample_buffer.is_empty() {
            return Inhibit(false);
        }
        let allocation = drawing_area.get_allocation();
        let width = allocation.width;
        if width.is_negative() {
            return Inhibit(false);
        }
        let width = width as u64;

        let display_window_duration = 2_000_000_000; // 2s TODO: adapt according to zoom
        let diplay_window_sample_nb = display_window_duration / self.sample_duration;

        let display_duration = self.buffer_duration.min(display_window_duration);
        let half_duration = display_duration / 2;
        let quarter_duration = half_duration / 2;
        //let three_quarter_duration = quarter_duration + half_duration;
        let diplay_sample_nb = display_duration / self.sample_duration;

        // TODO: make width and height adapt to zoom
        cr.scale((width / 2) as f64, (allocation.height / 2) as f64);
        cr.set_line_width(0.0015f64);

        // Take this opportunity to adjust sample skip step in order
        // to accomodate to computation requirements
        self.iter_since_adjust += 1;
        let first_display_pos = if
            !self.has_received_last_buffer && self.ts_relative_pos > self.buffer_duration
        {
            self.sample_skip_step += 1;
            self.iter_since_adjust = 0;
            self.buffer_duration - display_duration
        } else if self.ts_relative_pos > self.buffer_duration - half_duration {
            if !self.has_received_last_buffer && self.iter_since_adjust > self.min_iter_before_adjust
                && self.ts_relative_pos > self.buffer_duration - quarter_duration
            {
                self.sample_skip_step += 1;
                self.iter_since_adjust = 0;
            }
            self.buffer_duration - display_duration
        } else if self.ts_relative_pos > half_duration {
            // FIXME: need a real algorithm to stabilize
            /*if self.iter_since_adjust > self.min_iter_before_adjust
                && self.sample_skip_step > 1 && self.relative_pos < self.buffer_duration - three_quarter_duration
            {
                self.sample_skip_step -= 1;
                self.iter_since_adjust = 0;
                self.min_iter_before_adjust *= 2;
            }*/
            self.ts_relative_pos - half_duration
        } else {
            0
        };

        self.sample_per_step = if diplay_window_sample_nb > width / self.sample_skip_step {
            diplay_window_sample_nb / width * self.sample_skip_step
        } else {
            1
        };

        // Define the first sample as a multiple of self.sample_per_step
        // In order to avoid flickering when origin shifts between redraws
        let first_sample = (
            (first_display_pos / self.sample_duration / self.sample_per_step)
            * self.sample_per_step
        ) as usize;
        let last_sample = first_sample + diplay_sample_nb as usize;
        let sample_step = self.sample_per_step as usize;

        cr.set_source_rgb(0.8f64, 0.8f64, 0.8f64);

        let mut sample_relative_ts = 0f64;
        let duration_step = (self.sample_per_step * self.sample_duration) as f64
            / 1_000_000_000f64;

        let mut sample_idx = first_sample;

        cr.move_to(sample_relative_ts, self.sample_buffer[sample_idx]);
        sample_relative_ts += duration_step;
        sample_idx += sample_step;

        while sample_idx < last_sample {
            cr.line_to(sample_relative_ts, self.sample_buffer[sample_idx]);
            sample_relative_ts += duration_step;
            sample_idx += sample_step;
        }

        cr.stroke();

        // draw current pos
        let x = (self.ts_relative_pos - first_display_pos) as f64 / 1_000_000_000f64;
        cr.set_source_rgb(1f64, 1f64, 0f64);
        cr.set_line_width(0.004f64);
        cr.move_to(x, 0f64);
        cr.line_to(x, 2f64);
        cr.stroke();

        Inhibit(false)
    }
}
<commit_msg>Removed obsolete comment (#12)<commit_after>extern crate glib;

extern crate gtk;
use gtk::{Inhibit, WidgetExt};

extern crate cairo;

use std::iter::FromIterator;

use std::rc::Rc;
use std::cell::RefCell;

use std::collections::vec_deque::{VecDeque};

use ::media::{Context, AudioBuffer};

pub struct AudioController {
    container: gtk::Container,
    drawingarea: gtk::DrawingArea,

    stream_duration: u64,
    sample_duration: u64,
    stream_initialized: bool,

    sample_buffer: VecDeque<f64>,
    ts_offset: u64,
    ts_relative_offset: u64,
    buffer_duration: u64,
    has_received_last_buffer: bool,

    ts_relative_pos: u64,
    sample_skip_step: u64,
    sample_per_step: u64,
    iter_since_adjust: usize,
    min_iter_before_adjust: usize,
}

impl AudioController {
    pub fn new(builder: &gtk::Builder) -> Rc<RefCell<Self>> {
        let this = Rc::new(RefCell::new(AudioController {
            container: builder.get_object("audio-container").unwrap(),
            drawingarea: builder.get_object("audio-drawingarea").unwrap(),

            stream_duration: 0,
            sample_duration: 0,
            stream_initialized: false,

            sample_buffer: VecDeque::with_capacity(12 * 48_000), // 12s @ 48kHz
            ts_offset: 0,
            ts_relative_offset: 0,
            buffer_duration: 0,
            has_received_last_buffer: false,

            ts_relative_pos: 0,
            sample_skip_step: 0,
            sample_per_step: 0,
            iter_since_adjust: 0,
            min_iter_before_adjust: 5,
        }));

        {
            let this_ref = this.borrow();
            let this_weak = Rc::downgrade(&this);
            this_ref.drawingarea.connect_draw(move |drawing_area, cairo_ctx| {
                let this = this_weak.upgrade()
                    .expect("Main controller is no longer available for select_media");
                let result = this.borrow_mut().draw(drawing_area, cairo_ctx);
                result
            });
        }

        this
    }

    pub fn clear(&mut self) {
        self.sample_buffer.clear();
        self.stream_initialized = false;
    }

    pub fn new_media(&mut self, ctx: &Context) {
        if !self.sample_buffer.is_empty() {
            let duration = ctx.get_duration();
            if duration.is_negative() {
                panic!("Audio controller found a negative stream duration");
            }
            self.stream_duration = duration as u64;
            let position = ctx.get_position();
            if position.is_negative() {
                panic!("Audio controller found a negative initial position");
            }
            self.ts_offset = position as u64;
            self.stream_initialized = true;
            self.drawingarea.queue_draw();
            self.container.show();
        }
        else {
            self.container.hide();
        }
    }

    pub fn have_buffer(&mut self, mut buffer: AudioBuffer) {
        // assume the buffers come in order
        if self.sample_buffer.is_empty() {
            self.ts_offset = 0;
            self.ts_relative_offset = 0;
            self.buffer_duration = 0;
            self.has_received_last_buffer = false;

            self.ts_relative_pos = 0;
            self.sample_skip_step = 1;
            self.sample_per_step = 1;
            self.iter_since_adjust = 0;

            self.sample_duration = buffer.caps.sample_duration;
        }

        if buffer.pts < self.ts_offset {
            panic!("unordered buffers");
        }

        let mut samples_vecdeque = VecDeque::from_iter(buffer.samples.drain(..));
        self.sample_buffer.append(&mut samples_vecdeque);

        self.buffer_duration = self.sample_buffer.len() as u64 * self.sample_duration;
        self.has_received_last_buffer = self.stream_initialized
            && self.ts_relative_offset + self.buffer_duration >= self.stream_duration;

        if !self.has_received_last_buffer && self.buffer_duration > 10_000_000_000
            && self.ts_relative_pos > 3_000_000_000
        { // buffer larger than 10s and current pos is sufficient to:
            // remove 2s worse of samples
            // can't purge big chunks otherwise it impacts drawing
            // need to comply with current sample_per_step in order to
            // avoid discontinuities during waveform rendering
            let sample_nb_to_remove =
                (2_000_000_000 / self.sample_duration / self.sample_per_step)
                * self.sample_per_step;
            self.sample_buffer.drain(..(sample_nb_to_remove as usize));

            let duration_removed = sample_nb_to_remove * self.sample_duration;
            self.buffer_duration -= duration_removed;
            self.ts_offset += duration_removed;
            self.ts_relative_offset += duration_removed;
            self.ts_relative_pos -= duration_removed;
            //println!("buffer duration: {}", self.buffer_duration);
        }
    }

    pub fn have_position(&mut self, position: i64) {
        if position.is_negative() {
            panic!("Audio controller received a negative position");
        }
        if (position as u64) < self.ts_offset {
            panic!("Audio controller received a position earlier than first offset position: {}, offset {}", position, self.ts_offset);
        }
        let ts_relative_pos = position as u64 - self.ts_offset;

        if self.ts_relative_pos != ts_relative_pos {
            self.ts_relative_pos = ts_relative_pos;
            self.drawingarea.queue_draw();
        }
    }

    pub fn force_redraw(&mut self) {
        self.sample_skip_step = 1;
        self.iter_since_adjust = 0;
        self.drawingarea.queue_draw();
    }

    fn draw(&mut self, drawing_area: &gtk::DrawingArea, cr: &cairo::Context) -> Inhibit {
        if self.sample_buffer.is_empty() {
            return Inhibit(false);
        }
        let allocation = drawing_area.get_allocation();
        let width = allocation.width;
        if width.is_negative() {
            return Inhibit(false);
        }
        let width = width as u64;

        let display_window_duration = 2_000_000_000; // 2s TODO: adapt according to zoom
        let diplay_window_sample_nb = display_window_duration / self.sample_duration;

        let display_duration = self.buffer_duration.min(display_window_duration);
        let half_duration = display_duration / 2;
        let quarter_duration = half_duration / 2;
        //let three_quarter_duration = quarter_duration + half_duration;
        let diplay_sample_nb = display_duration / self.sample_duration;

        // TODO: make width and height adapt to zoom
        cr.scale((width / 2) as f64, (allocation.height / 2) as f64);
        cr.set_line_width(0.0015f64);

        // Take this opportunity to adjust sample skip step in order
        // to accomodate to computation requirements
        self.iter_since_adjust += 1;
        let first_display_pos = if
            !self.has_received_last_buffer && self.ts_relative_pos > self.buffer_duration
        {
            self.sample_skip_step += 1;
            self.iter_since_adjust = 0;
            self.buffer_duration - display_duration
        } else if self.ts_relative_pos > self.buffer_duration - half_duration {
            if !self.has_received_last_buffer && self.iter_since_adjust > self.min_iter_before_adjust
                && self.ts_relative_pos > self.buffer_duration - quarter_duration
            {
                self.sample_skip_step += 1;
                self.iter_since_adjust = 0;
            }
            self.buffer_duration - display_duration
        } else if self.ts_relative_pos > half_duration {
            // FIXME: need a real algorithm to stabilize
            /*if self.iter_since_adjust > self.min_iter_before_adjust
                && self.sample_skip_step > 1 && self.relative_pos < self.buffer_duration - three_quarter_duration
            {
                self.sample_skip_step -= 1;
                self.iter_since_adjust = 0;
                self.min_iter_before_adjust *= 2;
            }*/
            self.ts_relative_pos - half_duration
        } else {
            0
        };

        self.sample_per_step = if diplay_window_sample_nb > width / self.sample_skip_step {
            diplay_window_sample_nb / width * self.sample_skip_step
        } else {
            1
        };

        // Define the first sample as a multiple of self.sample_per_step
        // In order to avoid flickering when origin shifts between redraws
        let first_sample = (
            (first_display_pos / self.sample_duration / self.sample_per_step)
            * self.sample_per_step
        ) as usize;
        let last_sample = first_sample + diplay_sample_nb as usize;
        let sample_step = self.sample_per_step as usize;

        cr.set_source_rgb(0.8f64, 0.8f64, 0.8f64);

        let mut sample_relative_ts = 0f64;
        let duration_step = (self.sample_per_step * self.sample_duration) as f64
            / 1_000_000_000f64;

        let mut sample_idx = first_sample;

        cr.move_to(sample_relative_ts, self.sample_buffer[sample_idx]);
        sample_relative_ts += duration_step;
        sample_idx += sample_step;

        while sample_idx < last_sample {
            cr.line_to(sample_relative_ts, self.sample_buffer[sample_idx]);
            sample_relative_ts += duration_step;
            sample_idx += sample_step;
        }

        cr.stroke();

        // draw current pos
        let x = (self.ts_relative_pos - first_display_pos) as f64 / 1_000_000_000f64;
        cr.set_source_rgb(1f64, 1f64, 0f64);
        cr.set_line_width(0.004f64);
        cr.move_to(x, 0f64);
        cr.line_to(x, 2f64);
        cr.stroke();

        Inhibit(false)
    }
}
<|endoftext|>


<commit_before>mod iter;
mod coord;
mod test;

use traits::Grid;
use traits::Cell;
use traits::Nhood;
use traits::Coord;
use repr::CellRepr;
use repr::GridRepr;

use self::iter::Iter;
pub use self::coord::GridCoord;

pub struct SquareGrid<'a, C, N>
    where C: Cell,
          N: Nhood<Coord=GridCoord> {

    cells: Vec<C>,
    old_cells: Vec<C>,
    nhood: N,
    neighbors: Vec<Vec<Option<usize>>>,
    repr: GridRepr<'a, GridCoord>,
    rows: i32,
    cols: i32,
}

impl<'a, C, N> SquareGrid<'a, C, N>
    where C: Cell,
          N: Nhood<Coord=GridCoord> {

    pub fn new(rows: i32, cols: i32, nhood: N) -> Self {

        let len = (rows * cols) as usize;

        let cells = vec![C::default(); len];
        let old_cells = Vec::with_capacity(len);
        let neighbors = Vec::with_capacity(len);
        let repr = GridRepr::new(rows, cols, None);

        let mut grid = SquareGrid {
            cells: cells,
            old_cells: old_cells,
            nhood: nhood,
            neighbors: neighbors,
            repr: repr,
            rows: rows,
            cols: cols,
        };

        grid.init();

        grid
    }

    fn init(&mut self) {

        for (offset, cell) in self.cells.iter().enumerate() {

            let coord = GridCoord::from_offset(offset as i32, self.rows, self.cols);
            // init neighbors
            let neighbors = self.get_neighbors(&coord);
            self.neighbors.push(neighbors);

            // init representation
            let mut repr = CellRepr::new(coord, None);

            cell.repr(&mut repr.state);
            self.repr.cells.push(repr);
        }
    }

    fn get_neighbors(&self, coord: &GridCoord) -> Vec<Option<usize>> {

        let neighbors_count = self.nhood.neighbors_count();
        let mut neighbors = Vec::with_capacity(neighbors_count);

        for coord in self.nhood.neighbors(coord).iter() {

            if coord.x() >= 0 && coord.x() < self.cols &&
               coord.y() >= 0 && coord.y() < self.rows {

                neighbors.push(Some(self.offset(coord)));
            }
            else {
                neighbors.push(None);
            }
        }

        neighbors
    }

    fn neighbors_iter<'b>(&self,
                          cells: &'b Vec<C>,
                          neighbors: &'b Vec<Option<usize>>) -> Iter<'b, C> {

        Iter::new(cells, neighbors, self.nhood.neighbors_count())
    }

    #[inline]
    pub fn offset<Crd: Coord>(&self, coord: &Crd) -> usize {
        (coord.y() * self.cols + coord.x()) as usize
    }
}

impl<'a, C, N> Grid for SquareGrid<'a, C, N>
    where C: Cell,
          N: Nhood<Coord=GridCoord> {

    type Coord = GridCoord;

    fn step(&mut self) {
        self.old_cells = self.cells.clone();

        for (cell_no, cell) in self.old_cells.iter().enumerate() {

            let ref neighbors = self.neighbors[cell_no];
            let neighbors_iter = self.neighbors_iter(&self.old_cells, &neighbors);

            let new_cell = cell.step(neighbors_iter);

            // update representation
            let ref mut cell_repr = self.repr.cells[cell_no];
            new_cell.repr(&mut cell_repr.state);

            self.cells[cell_no] = new_cell;
        }
    }

    fn repr(&self) -> &GridRepr<GridCoord> {

        &self.repr
    }

    fn from_repr<'b: 'c, 'c, Crd: Coord>(&'b mut self, repr: &GridRepr<'c, Crd>) {

        debug_assert_eq!(self.rows, repr.rows);
        debug_assert_eq!(self.cols, repr.cols);

        for cell_repr in repr.cells.iter() {

            let offset = self.offset(&cell_repr.coord);

            let ref mut cell = self.cells[offset];
            cell.from_repr(&cell_repr.state);

            let ref mut inner_repr = self.repr.cells[offset];
            cell.repr(&mut inner_repr.state);
        }
    }
}
<commit_msg>Removed old repr system.<commit_after>mod iter;
mod coord;
mod test;

use traits::Grid;
use traits::Cell;
use traits::Nhood;
use traits::Coord;

use self::iter::Iter;
pub use self::coord::GridCoord;

pub struct SquareGrid<C, N>
    where C: Cell,
          N: Nhood<Coord=GridCoord> {

    cells: Vec<C>,
    old_cells: Vec<C>,
    nhood: N,
    neighbors: Vec<Vec<Option<usize>>>,
    dimensions: GridCoord,
    rows: i32,
    cols: i32
}

impl<C, N> SquareGrid<C, N>
    where C: Cell,
          N: Nhood<Coord=GridCoord> {

    pub fn new(rows: i32, cols: i32, nhood: N) -> Self {

        let len = (rows * cols) as usize;

        let cells = vec![C::default(); len];
        let old_cells = Vec::with_capacity(len);
        let neighbors = Vec::with_capacity(len);

        let mut grid = SquareGrid {
            cells: cells,
            old_cells: old_cells,
            nhood: nhood,
            neighbors: neighbors,
            rows: rows,
            cols: cols,
            dimensions: GridCoord::from_2d(cols, rows)
        };

        grid.init();

        grid
    }

    fn init(&mut self) {

        for offset in 0 .. self.cells.len() {

            let coord = GridCoord::from_offset(offset as i32, self.rows, self.cols);

            // init neighbors
            let neighbors = self.get_neighbors(&coord);
            self.neighbors.push(neighbors);
        }
    }

    fn get_neighbors(&self, coord: &GridCoord) -> Vec<Option<usize>> {

        let neighbors_count = self.nhood.neighbors_count();
        let mut neighbors = Vec::with_capacity(neighbors_count);

        for coord in self.nhood.neighbors(coord).iter() {

            if coord.x() >= 0 && coord.x() < self.cols &&
               coord.y() >= 0 && coord.y() < self.rows {

                neighbors.push(Some(self.offset(coord)));
            }
            else {
                neighbors.push(None);
            }
        }

        neighbors
    }

    fn neighbors_iter<'b>(&self,
                          cells: &'b Vec<C>,
                          neighbors: &'b Vec<Option<usize>>) -> Iter<'b, C> {

        Iter::new(cells, neighbors, self.nhood.neighbors_count())
    }

    #[inline]
    pub fn offset(&self, coord: &GridCoord) -> usize {
        (coord.y() * self.cols + coord.x()) as usize
    }
}

impl<C, N> Grid for SquareGrid<C, N>
    where C: Cell,
          N: Nhood<Coord=GridCoord> {

    type Cell = C;
    type Coord = GridCoord;

    fn step(&mut self) {
        self.old_cells = self.cells.clone();

        for (cell_no, cell) in self.old_cells.iter().enumerate() {

            let ref neighbors = self.neighbors[cell_no];
            let neighbors_iter = self.neighbors_iter(&self.old_cells, &neighbors);

            let new_cell = cell.step(neighbors_iter);

            self.cells[cell_no] = new_cell;
        }
    }

    fn restore<G: Grid>(&mut self, other: &G) {

        debug_assert_eq!(self.dimensions().x(), other.dimensions().x());
        debug_assert_eq!(self.dimensions().y(), other.dimensions().y());

    }

    fn cells(&self) -> &Vec<C> {
        &self.cells
    }

    fn dimensions(&self) -> Self::Coord {
        self.dimensions.clone()
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Servo Project Developers. See the
// COPYRIGHT file at the top-level directory of this distribution.
//
// 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 syntax::codemap::Span;
use syntax::ast::{TokenTree, TTTok};
use syntax::ast;
use syntax::ext::base::{ExtCtxt, MacResult, MacExpr};
use syntax::parse::token::{get_ident, InternedString, LIT_STR, IDENT};

use std::iter::Chain;
use std::slice::{Items, Found, NotFound};
use std::gc::Gc;
use std::collections::HashMap;
use std::ascii::AsciiExt;

mod data;

#[path="../../../shared/static_atom.rs"]
mod static_atom;

// Build a PhfOrderedSet of static atoms.
// Takes no arguments.
pub fn expand_static_atom_set(cx: &mut ExtCtxt, sp: Span, tt: &[TokenTree]) -> Box<MacResult+'static> {
    bail_if!(tt.len() != 0, cx, sp, "Usage: static_atom_map!()");
    let tts: Vec<TokenTree> = data::atoms.iter().flat_map(|k| {
        (quote_tokens!(&mut *cx, $k,)).move_iter()
    }).collect();
    MacExpr::new(quote_expr!(&mut *cx, phf_ordered_set!($tts)))
}

fn atom_tok_to_str(t: &TokenTree) -> Option<InternedString> {
    Some(get_ident(match *t {
        TTTok(_, IDENT(s, _)) => s,
        TTTok(_, LIT_STR(s)) => s.ident(),
        _ => return None,
    }))
}

// Build a map from atoms to IDs for use in implementing the atom!() macro.
lazy_static! {
    static ref STATIC_ATOM_MAP: HashMap<&'static str, uint> = {
        let mut m = HashMap::new();
        for (i, x) in data::atoms.iter().enumerate() {
            m.insert(*x, i);
        }
        m
    };
}

struct AtomResult {
    expr: Gc<ast::Expr>,
    pat: Gc<ast::Pat>,
}

impl MacResult for AtomResult {
    fn make_expr(&self) -> Option<Gc<ast::Expr>> {
        Some(self.expr)
    }

    fn make_pat(&self) -> Option<Gc<ast::Pat>> {
        Some(self.pat)
    }
}

fn expand_atom_str(cx: &mut ExtCtxt, sp: Span, name: &str) -> Box<MacResult+'static> {
    let i = expect!(cx, sp, STATIC_ATOM_MAP.find_equiv(&name),
        format!("Unknown static atom {:s}", name).as_slice());

    let data = static_atom::add_tag(*i as u32);

    box AtomResult {
        expr: quote_expr!(&mut *cx, ::string_cache::atom::Atom { data: $data }),
        pat: quote_pat!(&mut *cx, ::string_cache::atom::Atom { data: $data }),
    } as Box<MacResult>
}

// Translate `atom!(title)` or `atom!("font-weight")` into an `Atom` constant or pattern.
pub fn expand_atom(cx: &mut ExtCtxt, sp: Span, tt: &[TokenTree]) -> Box<MacResult+'static> {
    let usage = "Usage: atom!(html) or atom!(\"font-weight\")";
    let name = match tt {
        [ref t] => expect!(cx, sp, atom_tok_to_str(t), usage),
        _ => bail!(cx, sp, usage),
    };
    expand_atom_str(cx, sp, name.get())
}

// Translate `ns!(HTML)` into `atom!("http://www.w3.org/1999/xhtml")`.
// The argument is ASCII-case-insensitive.
pub fn expand_ns(cx: &mut ExtCtxt, sp: Span, tt: &[TokenTree]) -> Box<MacResult+'static> {
    static all_ns: &'static [(&'static str, &'static str)] = [
        ("html", "http://www.w3.org/1999/xhtml"),
        ("xml", "http://www.w3.org/XML/1998/namespace"),
        ("xmlns", "http://www.w3.org/2000/xmlns/"),
        ("xlink", "http://www.w3.org/1999/xlink"),
        ("svg", "http://www.w3.org/2000/svg"),
        ("mathml", "http://www.w3.org/1998/Math/MathML"),
    ];

    let name = match tt {
        [ref t] => atom_tok_to_str(t),
        _ => None,
    };

    match name {
        Some(name) => {
            for &(short, url) in all_ns.iter() {
                if short.eq_ignore_ascii_case(name.get()) {
                    return expand_atom_str(cx, sp, url);
                }
            }
        }
        None => (),
    }

    let ns_names: Vec<&'static str> = all_ns.iter().map(|&(x, _)| x).collect();
    bail!(cx, sp, format!("Usage: ns!(HTML), case-insensitive. Known namespaces: {:s}",
        ns_names.connect(" ")).as_slice());
}
<commit_msg>Upgrade to rustc 2014-09-15<commit_after>// Copyright 2014 The Servo Project Developers. See the
// COPYRIGHT file at the top-level directory of this distribution.
//
// 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 syntax::ptr::P;
use syntax::codemap::Span;
use syntax::ast::{TokenTree, TTTok};
use syntax::ast;
use syntax::ext::base::{ExtCtxt, MacResult, MacExpr};
use syntax::parse::token::{get_ident, InternedString, LIT_STR, IDENT};

use std::iter::Chain;
use std::slice::{Items, Found, NotFound};
use std::collections::HashMap;
use std::ascii::AsciiExt;

mod data;

#[path="../../../shared/static_atom.rs"]
mod static_atom;

// Build a PhfOrderedSet of static atoms.
// Takes no arguments.
pub fn expand_static_atom_set(cx: &mut ExtCtxt, sp: Span, tt: &[TokenTree]) -> Box<MacResult+'static> {
    bail_if!(tt.len() != 0, cx, sp, "Usage: static_atom_map!()");
    let tts: Vec<TokenTree> = data::atoms.iter().flat_map(|k| {
        (quote_tokens!(&mut *cx, $k,)).move_iter()
    }).collect();
    MacExpr::new(quote_expr!(&mut *cx, phf_ordered_set!($tts)))
}

fn atom_tok_to_str(t: &TokenTree) -> Option<InternedString> {
    Some(get_ident(match *t {
        TTTok(_, IDENT(s, _)) => s,
        TTTok(_, LIT_STR(s)) => s.ident(),
        _ => return None,
    }))
}

// Build a map from atoms to IDs for use in implementing the atom!() macro.
lazy_static! {
    static ref STATIC_ATOM_MAP: HashMap<&'static str, uint> = {
        let mut m = HashMap::new();
        for (i, x) in data::atoms.iter().enumerate() {
            m.insert(*x, i);
        }
        m
    };
}

struct AtomResult {
    expr: P<ast::Expr>,
    pat: P<ast::Pat>,
}

impl MacResult for AtomResult {
    fn make_expr(self: Box<AtomResult>) -> Option<P<ast::Expr>> {
        Some(self.expr)
    }

    fn make_pat(self: Box<AtomResult>) -> Option<P<ast::Pat>> {
        Some(self.pat)
    }
}

fn expand_atom_str(cx: &mut ExtCtxt, sp: Span, name: &str) -> Box<MacResult+'static> {
    let i = expect!(cx, sp, STATIC_ATOM_MAP.find_equiv(&name),
        format!("Unknown static atom {:s}", name).as_slice());

    let data = static_atom::add_tag(*i as u32);

    box AtomResult {
        expr: quote_expr!(&mut *cx, ::string_cache::atom::Atom { data: $data }),
        pat: quote_pat!(&mut *cx, ::string_cache::atom::Atom { data: $data }),
    } as Box<MacResult>
}

// Translate `atom!(title)` or `atom!("font-weight")` into an `Atom` constant or pattern.
pub fn expand_atom(cx: &mut ExtCtxt, sp: Span, tt: &[TokenTree]) -> Box<MacResult+'static> {
    let usage = "Usage: atom!(html) or atom!(\"font-weight\")";
    let name = match tt {
        [ref t] => expect!(cx, sp, atom_tok_to_str(t), usage),
        _ => bail!(cx, sp, usage),
    };
    expand_atom_str(cx, sp, name.get())
}

// Translate `ns!(HTML)` into `atom!("http://www.w3.org/1999/xhtml")`.
// The argument is ASCII-case-insensitive.
pub fn expand_ns(cx: &mut ExtCtxt, sp: Span, tt: &[TokenTree]) -> Box<MacResult+'static> {
    static all_ns: &'static [(&'static str, &'static str)] = [
        ("html", "http://www.w3.org/1999/xhtml"),
        ("xml", "http://www.w3.org/XML/1998/namespace"),
        ("xmlns", "http://www.w3.org/2000/xmlns/"),
        ("xlink", "http://www.w3.org/1999/xlink"),
        ("svg", "http://www.w3.org/2000/svg"),
        ("mathml", "http://www.w3.org/1998/Math/MathML"),
    ];

    let name = match tt {
        [ref t] => atom_tok_to_str(t),
        _ => None,
    };

    match name {
        Some(name) => {
            for &(short, url) in all_ns.iter() {
                if short.eq_ignore_ascii_case(name.get()) {
                    return expand_atom_str(cx, sp, url);
                }
            }
        }
        None => (),
    }

    let ns_names: Vec<&'static str> = all_ns.iter().map(|&(x, _)| x).collect();
    bail!(cx, sp, format!("Usage: ns!(HTML), case-insensitive. Known namespaces: {:s}",
        ns_names.connect(" ")).as_slice());
}
<|endoftext|>


<commit_before>use memory::Memory;
use std::fmt;

/// Data object for a single instruction.
///
/// An instruction consists of 4 words á 32 bit. Each instruction is a set of 4
/// addresses ``op_a``, ``op_b``, ``target`` and ``jmp``. All 4 words are
/// *signed* words. If a word is negative, it is considered indirect. The
/// execution of an instruction is described by the following pseudo-code:
///
/// ```text
/// if(op_a < 0) op_a = *(-op_a)
/// if(op_b < 0) op_b = *(-op_b)
/// if(target < 0) target = *(-target)
/// if(jmp < 0) jmp = *(-jmp)
///
/// *target := *op_a - *op_b
/// if (*target <= 0)
///   GOTO jmp;
/// ```
///
/// # Examples
/// ```
/// use osciemu::memory::SliceMemory;
/// use osciemu::instruction::Instruction;
///
/// let mut ip = 0;
/// let mut m = SliceMemory::from_slice_u32(16, [
///     0, 4, 8, 128,
/// ]);
/// Instruction::execute_at(&mut ip, &mut m);
/// assert_eq!(ip, 128);
/// ```
pub struct Instruction {
    /// Address of operand A
    pub op_a: u32,
    /// Address of operand B
    pub op_b: u32,
    /// Address to store result of `*A - *B`
    pub target: u32,
    /// Address to jump to when `*target <= 0`
    pub jmp: u32,
}

impl Instruction {
    /// Deserializes an instruction from memory at the given address.
    pub fn from_memory(addr: usize, mem: &Memory) -> Instruction {
        Instruction {
            op_a: mem.get(addr),
            op_b: mem.get(addr + 4),
            target: mem.get(addr + 8),
            jmp: mem.get(addr + 12),
        }
    }

    /// Serializes the instruction to memory at the given adress.
    pub fn serialize(&self, addr: usize, mem: &mut Memory) {
        mem.set(addr + 0, self.op_a);
        mem.set(addr + 4, self.op_b);
        mem.set(addr + 8, self.target);
        mem.set(addr + 12, self.jmp);
    }

    /// Executes the instruction using `mem` for reads and writes and
    /// adjusting `ip` appropriately.
    pub fn execute(&self, ip: &mut usize, mem: &mut Memory) {
        let a = mem.get(self.op_a as usize) as i32;
        let b = mem.get(self.op_b as usize) as i32;
        let r = a - b;
        mem.set(self.target as usize, r as u32);
        *ip = if r <= 0 { self.jmp as usize } else { *ip + 16 }
    }

    /// Executes the instruction in memory at the given address, adjusting the
    // `ip` appropriately.
    pub fn execute_at(ip: &mut usize, mem: &mut Memory) {
        let instr = Instruction::from_memory(*ip, mem);
        instr.execute(ip, mem);
    }
}

impl fmt::Debug for Instruction {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "Instruction {{ op_a: 0x{:08X}, op_b: 0x{:08X}, target: 0x{:08X}, jmp: 0x{:08X} }}",
               self.op_a,
               self.op_b,
               self.target,
               self.jmp)
    }
}

#[cfg(test)]
mod tests {
    use memory::{Memory, SliceMemory};

    #[test]
    fn execute() {
        let mut ip = 0;
        let mut m = SliceMemory::from_slice(Box::new([1, 2, 0, 0]));
        let i1 = super::Instruction {
            op_a: 0,
            op_b: 4,
            target: 8,
            jmp: 128,
        };
        i1.execute(&mut ip, &mut m);
        assert_eq!(m.get(8) as i32, -1);
        assert_eq!(ip, 128);

        ip = 0;
        let i2 = super::Instruction {
            op_a: 4,
            op_b: 0,
            target: 8,
            jmp: 128,
        };
        i2.execute(&mut ip, &mut m);
        assert_eq!(m.get(8), 1);
        assert_eq!(ip, 16);
    }

    #[test]
    fn execute_at() {
        let mut ip = 0;
        let mut m = SliceMemory::from_slice(Box::new([48, 52, 56, 60, 48, 48, 56, 60, 0, 0, 0, 0, 2, 1, 0, 0]));
        super::Instruction::execute_at(&mut ip, &mut m);
        assert_eq!(m.get(56), 1);
        assert_eq!(ip, 16);
        super::Instruction::execute_at(&mut ip, &mut m);
        assert_eq!(m.get(56), 0);
        assert_eq!(ip, 60);

    }
}
<commit_msg>refactur(instruction): Update for new memory design<commit_after>use memory::Memory;
use std::fmt;

/// Data object for a single instruction.
///
/// An instruction consists of 4 words á 32 bit. Each instruction is a set of 4
/// addresses ``op_a``, ``op_b``, ``target`` and ``jmp``. All 4 words are
/// *signed* words. If a word is negative, it is considered indirect. The
/// execution of an instruction is described by the following pseudo-code:
///
/// ```text
/// if(op_a < 0) op_a = *(-op_a)
/// if(op_b < 0) op_b = *(-op_b)
/// if(target < 0) target = *(-target)
/// if(jmp < 0) jmp = *(-jmp)
///
/// *target := *op_a - *op_b
/// if (*target <= 0)
///   GOTO jmp;
/// ```
///
/// # Examples
/// ```
/// use osciemu::memory::SliceMemory;
/// use osciemu::instruction::Instruction;
///
/// let mut ip = 0;
/// let mut m = SliceMemory::from_slice(Box::new([
///     0, 1, 2, 128,
/// ]));
/// Instruction::execute_at(&mut ip, &mut m);
/// assert_eq!(ip, 128);
/// ```
pub struct Instruction {
    /// Address of operand A
    pub op_a: u32,
    /// Address of operand B
    pub op_b: u32,
    /// Address to store result of `*A - *B`
    pub target: u32,
    /// Address to jump to when `*target <= 0`
    pub jmp: u32,
}

impl Instruction {
    /// Deserializes an instruction from memory at the given address.
    pub fn from_memory(addr: usize, mem: &Memory) -> Instruction {
        Instruction {
            op_a: mem.get(addr),
            op_b: mem.get(addr + 1),
            target: mem.get(addr + 2),
            jmp: mem.get(addr + 3),
        }
    }

    /// Serializes the instruction to memory at the given adress.
    pub fn serialize(&self, addr: usize, mem: &mut Memory) {
        mem.set(addr + 0, self.op_a);
        mem.set(addr + 1, self.op_b);
        mem.set(addr + 2, self.target);
        mem.set(addr + 3, self.jmp);
    }

    /// Executes the instruction using `mem` for reads and writes and
    /// adjusting `ip` appropriately.
    pub fn execute(&self, ip: &mut usize, mem: &mut Memory) {
        let a = mem.get(self.op_a as usize) as i32;
        let b = mem.get(self.op_b as usize) as i32;
        let r = a - b;
        mem.set(self.target as usize, r as u32);
        *ip = if r <= 0 { self.jmp as usize } else { *ip + 4 }
    }

    /// Executes the instruction in memory at the given address, adjusting the
    // `ip` appropriately.
    pub fn execute_at(ip: &mut usize, mem: &mut Memory) {
        let instr = Instruction::from_memory(*ip, mem);
        instr.execute(ip, mem);
    }
}

impl fmt::Debug for Instruction {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "Instruction {{ op_a: 0x{:08X}, op_b: 0x{:08X}, target: 0x{:08X}, jmp: 0x{:08X} }}",
               self.op_a,
               self.op_b,
               self.target,
               self.jmp)
    }
}

#[cfg(test)]
mod tests {
    use memory::{Memory, SliceMemory};

    #[test]
    fn execute() {
        let mut ip = 0;
        let mut m = SliceMemory::from_slice(Box::new([1, 2, 0, 0]));
        let i1 = super::Instruction {
            op_a: 0,
            op_b: 1,
            target: 2,
            jmp: 128,
        };
        i1.execute(&mut ip, &mut m);
        assert_eq!(m.get(2) as i32, -1);
        assert_eq!(ip, 128);

        ip = 0;
        let i2 = super::Instruction {
            op_a: 1,
            op_b: 0,
            target: 2,
            jmp: 128,
        };
        i2.execute(&mut ip, &mut m);
        assert_eq!(m.get(2), 1);
        assert_eq!(ip, 4);
    }

    #[test]
    fn execute_at() {
        let mut ip = 0;
        let mut m = SliceMemory::from_slice(Box::new([12, 13, 14, 15, 12, 12, 14, 15, 0, 0, 0, 0, 2, 1, 0, 0]));
        super::Instruction::execute_at(&mut ip, &mut m);
        assert_eq!(m.get(14), 1);
        assert_eq!(ip, 4);
        super::Instruction::execute_at(&mut ip, &mut m);
        assert_eq!(m.get(14), 0);
        assert_eq!(ip, 15);
    }
}
<|endoftext|>


<commit_before>//! `SourceBinder` is the main entry point for getting info about source code.
//! It's main task is to map source syntax trees to hir-level IDs.

use hir_def::{
    child_by_source::ChildBySource,
    dyn_map::DynMap,
    keys::{self, Key},
    ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, ModuleId,
    StaticId, StructFieldId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, VariantId,
};
use hir_expand::{name::AsName, AstId, InFile, MacroDefId, MacroDefKind};
use ra_db::FileId;
use ra_prof::profile;
use ra_syntax::{
    ast::{self, NameOwner},
    match_ast, AstNode, SyntaxNode,
};
use rustc_hash::FxHashMap;

use crate::{db::HirDatabase, Module};

pub(crate) struct SourceBinder {
    child_by_source_cache: FxHashMap<ChildContainer, DynMap>,
}

impl SourceBinder {
    pub(crate) fn new() -> SourceBinder {
        SourceBinder { child_by_source_cache: FxHashMap::default() }
    }

    pub(crate) fn to_module_def(&mut self, db: &impl HirDatabase, file: FileId) -> Option<Module> {
        let _p = profile("SourceBinder::to_module_def");
        let (krate, local_id) = db.relevant_crates(file).iter().find_map(|&crate_id| {
            let crate_def_map = db.crate_def_map(crate_id);
            let local_id = crate_def_map.modules_for_file(file).next()?;
            Some((crate_id, local_id))
        })?;
        Some(Module { id: ModuleId { krate, local_id } })
    }

    pub(crate) fn to_id<T: ToId>(
        &mut self,
        db: &impl HirDatabase,
        src: InFile<T>,
    ) -> Option<T::ID> {
        T::to_id(db, self, src)
    }

    pub(crate) fn find_container(
        &mut self,
        db: &impl HirDatabase,
        src: InFile<&SyntaxNode>,
    ) -> Option<ChildContainer> {
        for container in src.cloned().ancestors_with_macros(db).skip(1) {
            let res: ChildContainer = match_ast! {
                match (container.value) {
                    ast::TraitDef(it) => {
                        let def: TraitId = self.to_id(db, container.with_value(it))?;
                        def.into()
                    },
                    ast::ImplBlock(it) => {
                        let def: ImplId = self.to_id(db, container.with_value(it))?;
                        def.into()
                    },
                    ast::FnDef(it) => {
                        let def: FunctionId = self.to_id(db, container.with_value(it))?;
                        DefWithBodyId::from(def).into()
                    },
                    ast::StaticDef(it) => {
                        let def: StaticId = self.to_id(db, container.with_value(it))?;
                        DefWithBodyId::from(def).into()
                    },
                    ast::ConstDef(it) => {
                        let def: ConstId = self.to_id(db, container.with_value(it))?;
                        DefWithBodyId::from(def).into()
                    },
                    ast::EnumDef(it) => {
                        let def: EnumId = self.to_id(db, container.with_value(it))?;
                        def.into()
                    },
                    ast::StructDef(it) => {
                        let def: StructId = self.to_id(db, container.with_value(it))?;
                        VariantId::from(def).into()
                    },
                    ast::UnionDef(it) => {
                        let def: UnionId = self.to_id(db, container.with_value(it))?;
                        VariantId::from(def).into()
                    },
                    ast::Module(it) => {
                        let def: ModuleId = self.to_id(db, container.with_value(it))?;
                        def.into()
                    },
                    _ => { continue },
                }
            };
            return Some(res);
        }

        let c = self.to_module_def(db, src.file_id.original_file(db))?;
        Some(c.id.into())
    }

    fn child_by_source(&mut self, db: &impl HirDatabase, container: ChildContainer) -> &DynMap {
        self.child_by_source_cache.entry(container).or_insert_with(|| match container {
            ChildContainer::DefWithBodyId(it) => it.child_by_source(db),
            ChildContainer::ModuleId(it) => it.child_by_source(db),
            ChildContainer::TraitId(it) => it.child_by_source(db),
            ChildContainer::ImplId(it) => it.child_by_source(db),
            ChildContainer::EnumId(it) => it.child_by_source(db),
            ChildContainer::VariantId(it) => it.child_by_source(db),
            ChildContainer::GenericDefId(it) => it.child_by_source(db),
        })
    }
}

pub(crate) trait ToId: Sized {
    type ID: Sized + Copy + 'static;
    fn to_id<DB: HirDatabase>(
        db: &DB,
        sb: &mut SourceBinder,
        src: InFile<Self>,
    ) -> Option<Self::ID>;
}

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub(crate) enum ChildContainer {
    DefWithBodyId(DefWithBodyId),
    ModuleId(ModuleId),
    TraitId(TraitId),
    ImplId(ImplId),
    EnumId(EnumId),
    VariantId(VariantId),
    /// XXX: this might be the same def as, for example an `EnumId`. However,
    /// here the children generic parameters, and not, eg enum variants.
    GenericDefId(GenericDefId),
}
impl_froms! {
    ChildContainer:
    DefWithBodyId,
    ModuleId,
    TraitId,
    ImplId,
    EnumId,
    VariantId,
    GenericDefId
}

pub(crate) trait ToIdByKey: Sized + AstNode + 'static {
    type ID: Sized + Copy + 'static;
    const KEY: Key<Self, Self::ID>;
}

impl<T: ToIdByKey> ToId for T {
    type ID = <T as ToIdByKey>::ID;
    fn to_id<DB: HirDatabase>(
        db: &DB,
        sb: &mut SourceBinder,
        src: InFile<Self>,
    ) -> Option<Self::ID> {
        let container = sb.find_container(db, src.as_ref().map(|it| it.syntax()))?;
        let dyn_map =
            &*sb.child_by_source_cache.entry(container).or_insert_with(|| match container {
                ChildContainer::DefWithBodyId(it) => it.child_by_source(db),
                ChildContainer::ModuleId(it) => it.child_by_source(db),
                ChildContainer::TraitId(it) => it.child_by_source(db),
                ChildContainer::ImplId(it) => it.child_by_source(db),
                ChildContainer::EnumId(it) => it.child_by_source(db),
                ChildContainer::VariantId(it) => it.child_by_source(db),
                ChildContainer::GenericDefId(it) => it.child_by_source(db),
            });
        dyn_map[T::KEY].get(&src).copied()
    }
}

macro_rules! to_id_key_impls {
    ($(($id:ident, $ast:path, $key:path)),* ,) => {$(
        impl ToIdByKey for $ast {
            type ID = $id;
            const KEY: Key<Self, Self::ID> = $key;
        }
    )*}
}

to_id_key_impls![
    (StructId, ast::StructDef, keys::STRUCT),
    (UnionId, ast::UnionDef, keys::UNION),
    (EnumId, ast::EnumDef, keys::ENUM),
    (TraitId, ast::TraitDef, keys::TRAIT),
    (FunctionId, ast::FnDef, keys::FUNCTION),
    (StaticId, ast::StaticDef, keys::STATIC),
    (ConstId, ast::ConstDef, keys::CONST),
    (TypeAliasId, ast::TypeAliasDef, keys::TYPE_ALIAS),
    (ImplId, ast::ImplBlock, keys::IMPL),
    (StructFieldId, ast::RecordFieldDef, keys::RECORD_FIELD),
    (EnumVariantId, ast::EnumVariant, keys::ENUM_VARIANT),
];

// FIXME: use DynMap as well?
impl ToId for ast::MacroCall {
    type ID = MacroDefId;
    fn to_id<DB: HirDatabase>(
        db: &DB,
        sb: &mut SourceBinder,
        src: InFile<Self>,
    ) -> Option<Self::ID> {
        let kind = MacroDefKind::Declarative;

        let krate = sb.to_module_def(db, src.file_id.original_file(db))?.id.krate;

        let ast_id = Some(AstId::new(src.file_id, db.ast_id_map(src.file_id).ast_id(&src.value)));

        Some(MacroDefId { krate: Some(krate), ast_id, kind })
    }
}

impl ToId for ast::TypeParam {
    type ID = TypeParamId;

    fn to_id<DB: HirDatabase>(
        db: &DB,
        sb: &mut SourceBinder,
        src: InFile<Self>,
    ) -> Option<Self::ID> {
        let file_id = src.file_id;
        let parent: GenericDefId = src.value.syntax().ancestors().find_map(|it| {
            let res = match_ast! {
                match it {
                    ast::FnDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() },
                    ast::StructDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() },
                    ast::EnumDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() },
                    ast::TraitDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() },
                    ast::TypeAliasDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() },
                    ast::ImplBlock(value) => { sb.to_id(db, InFile { value, file_id})?.into() },
                    _ => return None,
                }
            };
            Some(res)
        })?;
        sb.child_by_source(db, parent.into())[keys::TYPE_PARAM].get(&src).copied()
    }
}

impl ToId for ast::Module {
    type ID = ModuleId;

    fn to_id<DB: HirDatabase>(
        db: &DB,
        sb: &mut SourceBinder,
        src: InFile<ast::Module>,
    ) -> Option<ModuleId> {
        {
            let _p = profile("ast::Module::to_def");
            let parent_declaration = src
                .as_ref()
                .map(|it| it.syntax())
                .cloned()
                .ancestors_with_macros(db)
                .skip(1)
                .find_map(|it| {
                    let m = ast::Module::cast(it.value.clone())?;
                    Some(it.with_value(m))
                });

            let parent_module = match parent_declaration {
                Some(parent_declaration) => sb.to_id(db, parent_declaration)?,
                None => {
                    let file_id = src.file_id.original_file(db);
                    sb.to_module_def(db, file_id)?.id
                }
            };

            let child_name = src.value.name()?.as_name();
            let def_map = db.crate_def_map(parent_module.krate);
            let child_id = *def_map[parent_module.local_id].children.get(&child_name)?;
            Some(ModuleId { krate: parent_module.krate, local_id: child_id })
        }
    }
}
<commit_msg>Small cleanup<commit_after>//! `SourceBinder` is the main entry point for getting info about source code.
//! It's main task is to map source syntax trees to hir-level IDs.

use hir_def::{
    child_by_source::ChildBySource,
    dyn_map::DynMap,
    keys::{self, Key},
    ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, ModuleId,
    StaticId, StructFieldId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, VariantId,
};
use hir_expand::{name::AsName, AstId, InFile, MacroDefId, MacroDefKind};
use ra_db::FileId;
use ra_prof::profile;
use ra_syntax::{
    ast::{self, NameOwner},
    match_ast, AstNode, SyntaxNode,
};
use rustc_hash::FxHashMap;

use crate::{db::HirDatabase, Module};

pub(crate) struct SourceBinder {
    child_by_source_cache: FxHashMap<ChildContainer, DynMap>,
}

impl SourceBinder {
    pub(crate) fn new() -> SourceBinder {
        SourceBinder { child_by_source_cache: FxHashMap::default() }
    }

    pub(crate) fn to_module_def(&mut self, db: &impl HirDatabase, file: FileId) -> Option<Module> {
        let _p = profile("SourceBinder::to_module_def");
        let (krate, local_id) = db.relevant_crates(file).iter().find_map(|&crate_id| {
            let crate_def_map = db.crate_def_map(crate_id);
            let local_id = crate_def_map.modules_for_file(file).next()?;
            Some((crate_id, local_id))
        })?;
        Some(Module { id: ModuleId { krate, local_id } })
    }

    pub(crate) fn to_id<T: ToId>(
        &mut self,
        db: &impl HirDatabase,
        src: InFile<T>,
    ) -> Option<T::ID> {
        T::to_id(db, self, src)
    }

    pub(crate) fn find_container(
        &mut self,
        db: &impl HirDatabase,
        src: InFile<&SyntaxNode>,
    ) -> Option<ChildContainer> {
        for container in src.cloned().ancestors_with_macros(db).skip(1) {
            let res: ChildContainer = match_ast! {
                match (container.value) {
                    ast::TraitDef(it) => {
                        let def: TraitId = self.to_id(db, container.with_value(it))?;
                        def.into()
                    },
                    ast::ImplBlock(it) => {
                        let def: ImplId = self.to_id(db, container.with_value(it))?;
                        def.into()
                    },
                    ast::FnDef(it) => {
                        let def: FunctionId = self.to_id(db, container.with_value(it))?;
                        DefWithBodyId::from(def).into()
                    },
                    ast::StaticDef(it) => {
                        let def: StaticId = self.to_id(db, container.with_value(it))?;
                        DefWithBodyId::from(def).into()
                    },
                    ast::ConstDef(it) => {
                        let def: ConstId = self.to_id(db, container.with_value(it))?;
                        DefWithBodyId::from(def).into()
                    },
                    ast::EnumDef(it) => {
                        let def: EnumId = self.to_id(db, container.with_value(it))?;
                        def.into()
                    },
                    ast::StructDef(it) => {
                        let def: StructId = self.to_id(db, container.with_value(it))?;
                        VariantId::from(def).into()
                    },
                    ast::UnionDef(it) => {
                        let def: UnionId = self.to_id(db, container.with_value(it))?;
                        VariantId::from(def).into()
                    },
                    ast::Module(it) => {
                        let def: ModuleId = self.to_id(db, container.with_value(it))?;
                        def.into()
                    },
                    _ => { continue },
                }
            };
            return Some(res);
        }

        let c = self.to_module_def(db, src.file_id.original_file(db))?;
        Some(c.id.into())
    }

    fn child_by_source(&mut self, db: &impl HirDatabase, container: ChildContainer) -> &DynMap {
        self.child_by_source_cache.entry(container).or_insert_with(|| container.child_by_source(db))
    }
}

pub(crate) trait ToId: Sized {
    type ID: Sized + Copy + 'static;
    fn to_id<DB: HirDatabase>(
        db: &DB,
        sb: &mut SourceBinder,
        src: InFile<Self>,
    ) -> Option<Self::ID>;
}

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub(crate) enum ChildContainer {
    DefWithBodyId(DefWithBodyId),
    ModuleId(ModuleId),
    TraitId(TraitId),
    ImplId(ImplId),
    EnumId(EnumId),
    VariantId(VariantId),
    /// XXX: this might be the same def as, for example an `EnumId`. However,
    /// here the children generic parameters, and not, eg enum variants.
    GenericDefId(GenericDefId),
}
impl_froms! {
    ChildContainer:
    DefWithBodyId,
    ModuleId,
    TraitId,
    ImplId,
    EnumId,
    VariantId,
    GenericDefId
}

impl ChildContainer {
    fn child_by_source(self, db: &impl HirDatabase) -> DynMap {
        match self {
            ChildContainer::DefWithBodyId(it) => it.child_by_source(db),
            ChildContainer::ModuleId(it) => it.child_by_source(db),
            ChildContainer::TraitId(it) => it.child_by_source(db),
            ChildContainer::ImplId(it) => it.child_by_source(db),
            ChildContainer::EnumId(it) => it.child_by_source(db),
            ChildContainer::VariantId(it) => it.child_by_source(db),
            ChildContainer::GenericDefId(it) => it.child_by_source(db),
        }
    }
}

pub(crate) trait ToIdByKey: Sized + AstNode + 'static {
    type ID: Sized + Copy + 'static;
    const KEY: Key<Self, Self::ID>;
}

impl<T: ToIdByKey> ToId for T {
    type ID = <T as ToIdByKey>::ID;
    fn to_id<DB: HirDatabase>(
        db: &DB,
        sb: &mut SourceBinder,
        src: InFile<Self>,
    ) -> Option<Self::ID> {
        let container = sb.find_container(db, src.as_ref().map(|it| it.syntax()))?;
        let dyn_map =
            &*sb.child_by_source_cache.entry(container).or_insert_with(|| match container {
                ChildContainer::DefWithBodyId(it) => it.child_by_source(db),
                ChildContainer::ModuleId(it) => it.child_by_source(db),
                ChildContainer::TraitId(it) => it.child_by_source(db),
                ChildContainer::ImplId(it) => it.child_by_source(db),
                ChildContainer::EnumId(it) => it.child_by_source(db),
                ChildContainer::VariantId(it) => it.child_by_source(db),
                ChildContainer::GenericDefId(it) => it.child_by_source(db),
            });
        dyn_map[T::KEY].get(&src).copied()
    }
}

macro_rules! to_id_key_impls {
    ($(($id:ident, $ast:path, $key:path)),* ,) => {$(
        impl ToIdByKey for $ast {
            type ID = $id;
            const KEY: Key<Self, Self::ID> = $key;
        }
    )*}
}

to_id_key_impls![
    (StructId, ast::StructDef, keys::STRUCT),
    (UnionId, ast::UnionDef, keys::UNION),
    (EnumId, ast::EnumDef, keys::ENUM),
    (TraitId, ast::TraitDef, keys::TRAIT),
    (FunctionId, ast::FnDef, keys::FUNCTION),
    (StaticId, ast::StaticDef, keys::STATIC),
    (ConstId, ast::ConstDef, keys::CONST),
    (TypeAliasId, ast::TypeAliasDef, keys::TYPE_ALIAS),
    (ImplId, ast::ImplBlock, keys::IMPL),
    (StructFieldId, ast::RecordFieldDef, keys::RECORD_FIELD),
    (EnumVariantId, ast::EnumVariant, keys::ENUM_VARIANT),
];

// FIXME: use DynMap as well?
impl ToId for ast::MacroCall {
    type ID = MacroDefId;
    fn to_id<DB: HirDatabase>(
        db: &DB,
        sb: &mut SourceBinder,
        src: InFile<Self>,
    ) -> Option<Self::ID> {
        let kind = MacroDefKind::Declarative;

        let krate = sb.to_module_def(db, src.file_id.original_file(db))?.id.krate;

        let ast_id = Some(AstId::new(src.file_id, db.ast_id_map(src.file_id).ast_id(&src.value)));

        Some(MacroDefId { krate: Some(krate), ast_id, kind })
    }
}

impl ToId for ast::TypeParam {
    type ID = TypeParamId;

    fn to_id<DB: HirDatabase>(
        db: &DB,
        sb: &mut SourceBinder,
        src: InFile<Self>,
    ) -> Option<Self::ID> {
        let file_id = src.file_id;
        let parent: GenericDefId = src.value.syntax().ancestors().find_map(|it| {
            let res = match_ast! {
                match it {
                    ast::FnDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() },
                    ast::StructDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() },
                    ast::EnumDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() },
                    ast::TraitDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() },
                    ast::TypeAliasDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() },
                    ast::ImplBlock(value) => { sb.to_id(db, InFile { value, file_id})?.into() },
                    _ => return None,
                }
            };
            Some(res)
        })?;
        sb.child_by_source(db, parent.into())[keys::TYPE_PARAM].get(&src).copied()
    }
}

impl ToId for ast::Module {
    type ID = ModuleId;

    fn to_id<DB: HirDatabase>(
        db: &DB,
        sb: &mut SourceBinder,
        src: InFile<ast::Module>,
    ) -> Option<ModuleId> {
        {
            let _p = profile("ast::Module::to_def");
            let parent_declaration = src
                .as_ref()
                .map(|it| it.syntax())
                .cloned()
                .ancestors_with_macros(db)
                .skip(1)
                .find_map(|it| {
                    let m = ast::Module::cast(it.value.clone())?;
                    Some(it.with_value(m))
                });

            let parent_module = match parent_declaration {
                Some(parent_declaration) => sb.to_id(db, parent_declaration)?,
                None => {
                    let file_id = src.file_id.original_file(db);
                    sb.to_module_def(db, file_id)?.id
                }
            };

            let child_name = src.value.name()?.as_name();
            let def_map = db.crate_def_map(parent_module.krate);
            let child_id = *def_map[parent_module.local_id].children.get(&child_name)?;
            Some(ModuleId { krate: parent_module.krate, local_id: child_id })
        }
    }
}
<|endoftext|>


<commit_before>//! Collects lang items: items marked with `#[lang = "..."]` attribute.
//!
//! This attribute to tell the compiler about semi built-in std library
//! features, such as Fn family of traits.
use std::sync::Arc;

use ra_syntax::SmolStr;
use rustc_hash::FxHashMap;

use crate::{
    db::DefDatabase, AdtId, AttrDefId, CrateId, EnumId, FunctionId, ImplId, ModuleDefId, ModuleId,
    StaticId, StructId, TraitId,
};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LangItemTarget {
    EnumId(EnumId),
    FunctionId(FunctionId),
    ImplBlockId(ImplId),
    StaticId(StaticId),
    StructId(StructId),
    TraitId(TraitId),
}

#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct LangItems {
    items: FxHashMap<SmolStr, LangItemTarget>,
}

impl LangItems {
    pub fn target<'a>(&'a self, item: &str) -> Option<&'a LangItemTarget> {
        self.items.get(item)
    }

    /// Salsa query. This will look for lang items in a specific crate.
    pub(crate) fn crate_lang_items_query(db: &impl DefDatabase, krate: CrateId) -> Arc<LangItems> {
        let mut lang_items = LangItems::default();

        let crate_def_map = db.crate_def_map(krate);

        crate_def_map
            .modules
            .iter()
            .filter_map(|(local_id, _)| db.module_lang_items(ModuleId { krate, local_id }))
            .for_each(|it| lang_items.items.extend(it.items.iter().map(|(k, v)| (k.clone(), *v))));

        Arc::new(lang_items)
    }

    pub(crate) fn module_lang_items_query(
        db: &impl DefDatabase,
        module: ModuleId,
    ) -> Option<Arc<LangItems>> {
        let mut lang_items = LangItems::default();
        lang_items.collect_lang_items(db, module);
        if lang_items.items.is_empty() {
            None
        } else {
            Some(Arc::new(lang_items))
        }
    }

    /// Salsa query. Look for a lang item, starting from the specified crate and recursively
    /// traversing its dependencies.
    pub(crate) fn lang_item_query(
        db: &impl DefDatabase,
        start_crate: CrateId,
        item: SmolStr,
    ) -> Option<LangItemTarget> {
        let lang_items = db.crate_lang_items(start_crate);
        let start_crate_target = lang_items.items.get(&item);
        if let Some(target) = start_crate_target {
            return Some(*target);
        }
        db.crate_graph()
            .dependencies(start_crate)
            .find_map(|dep| db.lang_item(dep.crate_id, item.clone()))
    }

    fn collect_lang_items(&mut self, db: &impl DefDatabase, module: ModuleId) {
        // Look for impl targets
        let def_map = db.crate_def_map(module.krate);
        let module_data = &def_map[module.local_id];
        for impl_block in module_data.scope.impls() {
            self.collect_lang_item(db, impl_block, LangItemTarget::ImplBlockId)
        }

        for def in module_data.scope.declarations() {
            match def {
                ModuleDefId::TraitId(trait_) => {
                    self.collect_lang_item(db, trait_, LangItemTarget::TraitId)
                }
                ModuleDefId::AdtId(AdtId::EnumId(e)) => {
                    self.collect_lang_item(db, e, LangItemTarget::EnumId)
                }
                ModuleDefId::AdtId(AdtId::StructId(s)) => {
                    self.collect_lang_item(db, s, LangItemTarget::StructId)
                }
                ModuleDefId::FunctionId(f) => {
                    self.collect_lang_item(db, f, LangItemTarget::FunctionId)
                }
                ModuleDefId::StaticId(s) => self.collect_lang_item(db, s, LangItemTarget::StaticId),
                _ => {}
            }
        }
    }

    fn collect_lang_item<T>(
        &mut self,
        db: &impl DefDatabase,
        item: T,
        constructor: fn(T) -> LangItemTarget,
    ) where
        T: Into<AttrDefId> + Copy,
    {
        let attrs = db.attrs(item.into());
        if let Some(lang_item_name) = attrs.by_key("lang").string_value() {
            self.items.entry(lang_item_name.clone()).or_insert_with(|| constructor(item));
        }
    }
}
<commit_msg>Add helpers for unpacking lang items<commit_after>//! Collects lang items: items marked with `#[lang = "..."]` attribute.
//!
//! This attribute to tell the compiler about semi built-in std library
//! features, such as Fn family of traits.
use std::sync::Arc;

use ra_syntax::SmolStr;
use rustc_hash::FxHashMap;

use crate::{
    db::DefDatabase, AdtId, AttrDefId, CrateId, EnumId, FunctionId, ImplId, ModuleDefId, ModuleId,
    StaticId, StructId, TraitId,
};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LangItemTarget {
    EnumId(EnumId),
    FunctionId(FunctionId),
    ImplBlockId(ImplId),
    StaticId(StaticId),
    StructId(StructId),
    TraitId(TraitId),
}

impl LangItemTarget {
    pub fn as_enum(self) -> Option<EnumId> {
        match self {
            LangItemTarget::EnumId(id) => Some(id),
            _ => None,
        }
    }

    pub fn as_function(self) -> Option<FunctionId> {
        match self {
            LangItemTarget::FunctionId(id) => Some(id),
            _ => None,
        }
    }

    pub fn as_impl_block(self) -> Option<ImplId> {
        match self {
            LangItemTarget::ImplBlockId(id) => Some(id),
            _ => None,
        }
    }

    pub fn as_static(self) -> Option<StaticId> {
        match self {
            LangItemTarget::StaticId(id) => Some(id),
            _ => None,
        }
    }

    pub fn as_struct(self) -> Option<StructId> {
        match self {
            LangItemTarget::StructId(id) => Some(id),
            _ => None,
        }
    }

    pub fn as_trait(self) -> Option<TraitId> {
        match self {
            LangItemTarget::TraitId(id) => Some(id),
            _ => None,
        }
    }
}

#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct LangItems {
    items: FxHashMap<SmolStr, LangItemTarget>,
}

impl LangItems {
    pub fn target<'a>(&'a self, item: &str) -> Option<&'a LangItemTarget> {
        self.items.get(item)
    }

    /// Salsa query. This will look for lang items in a specific crate.
    pub(crate) fn crate_lang_items_query(db: &impl DefDatabase, krate: CrateId) -> Arc<LangItems> {
        let mut lang_items = LangItems::default();

        let crate_def_map = db.crate_def_map(krate);

        crate_def_map
            .modules
            .iter()
            .filter_map(|(local_id, _)| db.module_lang_items(ModuleId { krate, local_id }))
            .for_each(|it| lang_items.items.extend(it.items.iter().map(|(k, v)| (k.clone(), *v))));

        Arc::new(lang_items)
    }

    pub(crate) fn module_lang_items_query(
        db: &impl DefDatabase,
        module: ModuleId,
    ) -> Option<Arc<LangItems>> {
        let mut lang_items = LangItems::default();
        lang_items.collect_lang_items(db, module);
        if lang_items.items.is_empty() {
            None
        } else {
            Some(Arc::new(lang_items))
        }
    }

    /// Salsa query. Look for a lang item, starting from the specified crate and recursively
    /// traversing its dependencies.
    pub(crate) fn lang_item_query(
        db: &impl DefDatabase,
        start_crate: CrateId,
        item: SmolStr,
    ) -> Option<LangItemTarget> {
        let lang_items = db.crate_lang_items(start_crate);
        let start_crate_target = lang_items.items.get(&item);
        if let Some(target) = start_crate_target {
            return Some(*target);
        }
        db.crate_graph()
            .dependencies(start_crate)
            .find_map(|dep| db.lang_item(dep.crate_id, item.clone()))
    }

    fn collect_lang_items(&mut self, db: &impl DefDatabase, module: ModuleId) {
        // Look for impl targets
        let def_map = db.crate_def_map(module.krate);
        let module_data = &def_map[module.local_id];
        for impl_block in module_data.scope.impls() {
            self.collect_lang_item(db, impl_block, LangItemTarget::ImplBlockId)
        }

        for def in module_data.scope.declarations() {
            match def {
                ModuleDefId::TraitId(trait_) => {
                    self.collect_lang_item(db, trait_, LangItemTarget::TraitId)
                }
                ModuleDefId::AdtId(AdtId::EnumId(e)) => {
                    self.collect_lang_item(db, e, LangItemTarget::EnumId)
                }
                ModuleDefId::AdtId(AdtId::StructId(s)) => {
                    self.collect_lang_item(db, s, LangItemTarget::StructId)
                }
                ModuleDefId::FunctionId(f) => {
                    self.collect_lang_item(db, f, LangItemTarget::FunctionId)
                }
                ModuleDefId::StaticId(s) => self.collect_lang_item(db, s, LangItemTarget::StaticId),
                _ => {}
            }
        }
    }

    fn collect_lang_item<T>(
        &mut self,
        db: &impl DefDatabase,
        item: T,
        constructor: fn(T) -> LangItemTarget,
    ) where
        T: Into<AttrDefId> + Copy,
    {
        let attrs = db.attrs(item.into());
        if let Some(lang_item_name) = attrs.by_key("lang").string_value() {
            self.items.entry(lang_item_name.clone()).or_insert_with(|| constructor(item));
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![feature(uniform_paths)]
//! Simple efficient composable primitives for pretty printing.
//!
//! The implementation is derived from the paper ["A New Approach to Optimal
//! Code Formatting"][pp-paper] by Phillip M. Yelland.
//!
//! [pp-paper]: https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/44667.pdf

extern crate unicode_width;

mod knot_column;
mod knot_set;
mod linear_value;
mod resolved_layout;
mod shared_string;

use std::borrow::Borrow;
use std::collections as col;
use std::fmt;
use std::rc;

use shared_string::SharedString;

const OVERFLOW_COST: f32 = 100.0;
const NEWLINE_COST: f32 = 1.0;

/// The primary representation of pretty-printed text. Logically, it represents
/// a set of possible permissible layouts of text.
///
/// Layouts are constructed with four different primitives:
///
/// 1. Text
///
///    A literal string which is laid out as written. Its length is obtained
/// using the    `unicode-width` package.
///
/// 2. Stack
///
///    Vertically stacks two layouts. The left margins of the two layouts will
/// be    aligned.
///
/// 3. Juxtapose
///
///    Horizontally juxtapose two layouts. The left margin of the second layout
///    will be aligned with the end of the first layout's last line.
///
/// 4. Choice
///
///    Creates a choice between two or more layouts. The choice is made at
/// layout    time based on the cost of each possible layout. See the `layout`
/// method.
#[derive(Clone)]
pub struct Layout(rc::Rc<LayoutContents>);

impl Layout {
  fn with_contents(contents: LayoutContents) -> Layout {
    Layout(rc::Rc::new(contents))
  }

  fn contents(&self) -> &LayoutContents {
    &*self.0
  }

  /// Create a text layout value. This is a literal string with no layout
  /// variance.
  pub fn text(lit_text: impl Into<String>) -> Layout {
    Layout::with_contents(LayoutContents::Text(lit_text.into().into()))
  }

  fn choice_pair(first: &Layout, second: &Layout) -> Layout {
    // We should canonicalize this so that the choices are always in the same
    // order.
    Layout::with_contents(LayoutContents::Choice(first.clone(), second.clone()))
  }

  fn stack_pair(top: &Layout, bottom: &Layout) -> Layout {
    Layout::with_contents(LayoutContents::Stack(top.clone(), bottom.clone()))
  }

  /// Create a layout that is the juxtaposition of two other layouts.
  ///
  /// This lays out the latter layout immediately to the right of the last line
  /// of the left layout.
  pub fn juxtapose_pair(left: &Layout, right: &Layout) -> Layout {
    let mut builder = JuxtaposeBuilder::new(right);
    builder.juxtapose(left)
  }

  /// Creates a layout that is the lowest-cost choice of layouts provided.
  pub fn choices<S, T>(items: S) -> Layout
  where
    S: AsRef<[T]>,
    T: Borrow<Layout>,
  {
    let items = items.as_ref();
    assert!(items.len() > 0);
    let mut curr_layout = items[0].borrow().clone();
    for item in &items[1..] {
      curr_layout = Layout::choice_pair(&curr_layout, item.borrow());
    }

    curr_layout
  }

  /// Create a stack of the layouts, where each of the layouts are
  /// vertically stacked on top of each other in the order provided.
  pub fn stack<'a, S, T>(items: S) -> Layout
  where
    S: AsRef<[T]>,
    T: Borrow<Layout>,
  {
    let items = items.as_ref();
    if items.len() == 0 {
      return Layout::text("");
    }
    assert!(items.len() > 0);
    let mut curr_layout = items[0].borrow().clone();
    for item in &items[1..] {
      curr_layout = Layout::stack_pair(&curr_layout, item.borrow());
    }
    curr_layout
  }

  /// Creates a juxtoposition of layouts, with each layout immediately
  /// following the last line of the previous layout.
  pub fn juxtapose<S, T>(items: S) -> Layout
  where
    S: AsRef<[T]>,
    T: Borrow<Layout>,
  {
    let items = items.as_ref();
    assert!(items.len() > 0);
    let mut curr_layout = items[0].borrow().clone();
    for item in &items[1..] {
      curr_layout = Layout::juxtapose_pair(&curr_layout, item.borrow());
    }
    curr_layout
  }

  /// Lays out the contents of this layout for a text buffer of width margin.
  pub fn layout(&self, margin: u16) -> String {
    self.layout_with_costs(margin, OVERFLOW_COST, NEWLINE_COST)
  }

  /// Lays out the contents of this layout for a text buffer of width margin,
  /// and with newline costs and overflow costs as provided.
  ///
  /// For each character over the margin width, the overflow cost is incurred.
  /// For each newline in the result, the newline cost is incurred. The layout
  /// chosen is that with the smallest overall cost.
  pub fn layout_with_costs(
    &self,
    margin: u16,
    overflow_cost: f32,
    newline_cost: f32,
  ) -> String {
    let final_layout =
      knot_set::do_layout(self, overflow_cost, newline_cost, margin);
    return final_layout.to_text(0);
  }

  #[doc(hidden)]
  pub fn debug_num_nodes(&self) -> usize {
    let mut counter = NodeCounter::new();
    counter.visit_node(self);
    counter.get_count()
  }

  fn wrap_inner<T: Borrow<Layout>>(
    layouts: &[T],
  ) -> (Layout, Vec<(Layout, Option<Layout>)>) {
    if layouts.len() == 0 {
      (Layout::text(""), vec![])
    } else if layouts.len() == 1 {
      let singleton = layouts[0].borrow();
      (
        singleton.borrow().clone(),
        vec![(singleton.borrow().clone(), None)],
      )
    } else {
      let first = layouts[0].borrow();
      let rest = &layouts[1..];
      let (block, line_blocks) = Layout::wrap_inner(rest);

      let mut new_line_pairs: Vec<_> = line_blocks
        .into_iter()
        .map(|(l, b)| {
          (
            Layout::juxtapose(&[first.borrow(), &Layout::text(" "), &l]),
            b,
          )
        })
        .collect();
      new_line_pairs.push((first.borrow().clone(), Some(block)));

      let new_choices: Vec<_> = new_line_pairs
        .iter()
        .map(|(k, v)| match v {
          Some(b) => Layout::stack_pair(k, b),
          None => k.clone(),
        })
        .collect();

      (Layout::choices(&new_choices), new_line_pairs)
    }
  }

  pub fn wrap<S, T>(layouts: S) -> Layout
  where
    S: AsRef<[T]>,
    T: Borrow<Layout>,
  {
    let slice = layouts.as_ref();
    let (result, _) = Layout::wrap_inner(slice);
    result
  }
}

impl fmt::Debug for Layout {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    <LayoutContents as fmt::Debug>::fmt(&*self.0, f)
  }
}

#[derive(Debug)]
enum LayoutContents {
  Text(SharedString),
  Choice(Layout, Layout),
  Stack(Layout, Layout),
  Juxtapose(SharedString, Layout),
}

struct JuxtaposeBuilder {
  right: Layout,
  memo: col::BTreeMap<*const LayoutContents, Layout>,
}

impl JuxtaposeBuilder {
  fn new(right: &Layout) -> Self {
    JuxtaposeBuilder {
      right: right.clone(),
      memo: col::BTreeMap::new(),
    }
  }

  fn juxtapose(&mut self, left: &Layout) -> Layout {
    if let Some(v) = self.memo.get(&(left.contents() as *const LayoutContents))
    {
      return v.clone();
    }

    let new_layout = match left.contents() {
      LayoutContents::Text(t) => Layout::with_contents(
        LayoutContents::Juxtapose(t.clone(), self.right.clone()),
      ),
      LayoutContents::Choice(first, second) => Layout::with_contents(
        LayoutContents::Choice(self.juxtapose(first), self.juxtapose(second)),
      ),
      LayoutContents::Stack(top, bottom) => Layout::with_contents(
        LayoutContents::Stack(top.clone(), self.juxtapose(bottom)),
      ),
      LayoutContents::Juxtapose(text, jux_right) => Layout::with_contents(
        LayoutContents::Juxtapose(text.clone(), self.juxtapose(jux_right)),
      ),
    };

    self
      .memo
      .insert(left.contents() as *const LayoutContents, new_layout.clone());
    new_layout
  }
}

struct NodeCounter {
  seen: col::BTreeSet<*const LayoutContents>,
}

impl NodeCounter {
  fn new() -> Self {
    NodeCounter {
      seen: col::BTreeSet::new(),
    }
  }
  fn visit_node(&mut self, layout: &Layout) {
    if self.seen.insert(layout.contents() as *const LayoutContents) {
      use LayoutContents::*;
      match layout.contents() {
        Text(_) => {}
        Choice(first, second) => {
          self.visit_node(first);
          self.visit_node(second);
        }
        Stack(top, bottom) => {
          self.visit_node(top);
          self.visit_node(bottom);
        }
        Juxtapose(_left, right) => {
          self.visit_node(right);
        }
      }
    }
  }

  fn get_count(&self) -> usize {
    self.seen.len()
  }
}

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

  #[test]
  fn test_simple_set() {
    let foo = Layout::text("foo");
    let pair = Layout::juxtapose(&[&foo, &foo]);
    assert_eq!(pair.layout(10), "foofoo");
  }

  #[test]
  fn test_simple_stack() {
    let foo = Layout::text("foo");
    let pair = Layout::stack(&[&foo, &foo]);
    assert_eq!(pair.layout(10), "foo\nfoo");
  }

  #[test]
  fn test_simple_choice() {
    let foo = Layout::text("foo");
    let pair = Layout::choices(&[
      &Layout::juxtapose(&[&foo, &foo]),
      &Layout::stack(&[&foo, &foo]),
    ]);
    assert_eq!(pair.layout(10), "foofoo");
    assert_eq!(pair.layout(4), "foo\nfoo");
  }

  #[test]
  fn test_wrap() {
    let lorem = &"Lorem ipsum dolor sit amet, consectetur adipiscing elit.
    Nullam suscipit dolor sed dolor facilisis ultrices ut id nisl. Nam
    malesuada malesuada dolor, eget dapibus nibh euismod at. Vestibulum
    aliquam aliquam mi at tincidunt. Sed imperdiet eget purus in egestas.
    Suspendisse id odio in augue cursus tempor. Duis imperdiet imperdiet eros
    et lobortis. Vestibulum vestibulum condimentum dui, sit amet fringilla libero
    commodo at. Sed aliquam mauris vitae egestas convallis. Integer sed est vitae
    nibh pellentesque sodales. Suspendisse sit amet dignissim arcu. Quisque hendrerit
    pellentesque egestas. Nulla at elementum est. Maecenas lectus turpis, placerat
    vitae est sit amet, mattis pharetra orci. Praesent.";
    let words: Vec<_> =
      lorem.split_whitespace().map(|k| Layout::text(k)).collect();
    let layout = Layout::wrap(words);
    println!("{}", layout.debug_num_nodes());
    assert_eq!(layout.layout(80).chars().filter(|c| c == &'\n').count(), 9);
    assert_eq!(layout.layout(100).chars().filter(|c| c == &'\n').count(), 7);
  }

  #[test]
  fn test_large_stack() {
    let foo = Layout::text("foo");
    let bar = Layout::text("bar");
    let foo_stack = Layout::stack(&[&foo, &bar]);
    let foo_jux = Layout::juxtapose(&[&foo, &bar]);
    let foo_choice = Layout::choices(&[&foo_stack, &foo_jux]);

    let choice = Layout::juxtapose(&vec![&foo_choice; 1000]);

    println!("Done creating layout.");
    println!("Num nodes: {}", choice.debug_num_nodes());

    choice.layout(500);
  }
}
<commit_msg>Bring up to date to Rust 2018<commit_after>// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Simple efficient composable primitives for pretty printing.
//!
//! The implementation is derived from the paper ["A New Approach to Optimal
//! Code Formatting"][pp-paper] by Phillip M. Yelland.
//!
//! [pp-paper]: https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/44667.pdf

extern crate unicode_width;

mod knot_column;
mod knot_set;
mod linear_value;
mod resolved_layout;
mod shared_string;

use std::borrow::Borrow;
use std::collections as col;
use std::fmt;
use std::rc;

use shared_string::SharedString;

const OVERFLOW_COST: f32 = 100.0;
const NEWLINE_COST: f32 = 1.0;

/// The primary representation of pretty-printed text. Logically, it represents
/// a set of possible permissible layouts of text.
///
/// Layouts are constructed with four different primitives:
///
/// 1. Text
///
///    A literal string which is laid out as written. Its length is obtained
/// using the    `unicode-width` package.
///
/// 2. Stack
///
///    Vertically stacks two layouts. The left margins of the two layouts will
/// be    aligned.
///
/// 3. Juxtapose
///
///    Horizontally juxtapose two layouts. The left margin of the second layout
///    will be aligned with the end of the first layout's last line.
///
/// 4. Choice
///
///    Creates a choice between two or more layouts. The choice is made at
/// layout    time based on the cost of each possible layout. See the `layout`
/// method.
#[derive(Clone)]
pub struct Layout(rc::Rc<LayoutContents>);

impl Layout {
  fn with_contents(contents: LayoutContents) -> Layout {
    Layout(rc::Rc::new(contents))
  }

  fn contents(&self) -> &LayoutContents {
    &*self.0
  }

  /// Create a text layout value. This is a literal string with no layout
  /// variance.
  pub fn text(lit_text: impl Into<String>) -> Layout {
    Layout::with_contents(LayoutContents::Text(lit_text.into().into()))
  }

  fn choice_pair(first: &Layout, second: &Layout) -> Layout {
    // We should canonicalize this so that the choices are always in the same
    // order.
    Layout::with_contents(LayoutContents::Choice(first.clone(), second.clone()))
  }

  fn stack_pair(top: &Layout, bottom: &Layout) -> Layout {
    Layout::with_contents(LayoutContents::Stack(top.clone(), bottom.clone()))
  }

  /// Create a layout that is the juxtaposition of two other layouts.
  ///
  /// This lays out the latter layout immediately to the right of the last line
  /// of the left layout.
  pub fn juxtapose_pair(left: &Layout, right: &Layout) -> Layout {
    let mut builder = JuxtaposeBuilder::new(right);
    builder.juxtapose(left)
  }

  /// Creates a layout that is the lowest-cost choice of layouts provided.
  pub fn choices<S, T>(items: S) -> Layout
  where
    S: AsRef<[T]>,
    T: Borrow<Layout>,
  {
    let items = items.as_ref();
    assert!(items.len() > 0);
    let mut curr_layout = items[0].borrow().clone();
    for item in &items[1..] {
      curr_layout = Layout::choice_pair(&curr_layout, item.borrow());
    }

    curr_layout
  }

  /// Create a stack of the layouts, where each of the layouts are
  /// vertically stacked on top of each other in the order provided.
  pub fn stack<'a, S, T>(items: S) -> Layout
  where
    S: AsRef<[T]>,
    T: Borrow<Layout>,
  {
    let items = items.as_ref();
    if items.len() == 0 {
      return Layout::text("");
    }
    assert!(items.len() > 0);
    let mut curr_layout = items[0].borrow().clone();
    for item in &items[1..] {
      curr_layout = Layout::stack_pair(&curr_layout, item.borrow());
    }
    curr_layout
  }

  /// Creates a juxtoposition of layouts, with each layout immediately
  /// following the last line of the previous layout.
  pub fn juxtapose<S, T>(items: S) -> Layout
  where
    S: AsRef<[T]>,
    T: Borrow<Layout>,
  {
    let items = items.as_ref();
    assert!(items.len() > 0);
    let mut curr_layout = items[0].borrow().clone();
    for item in &items[1..] {
      curr_layout = Layout::juxtapose_pair(&curr_layout, item.borrow());
    }
    curr_layout
  }

  /// Lays out the contents of this layout for a text buffer of width margin.
  pub fn layout(&self, margin: u16) -> String {
    self.layout_with_costs(margin, OVERFLOW_COST, NEWLINE_COST)
  }

  /// Lays out the contents of this layout for a text buffer of width margin,
  /// and with newline costs and overflow costs as provided.
  ///
  /// For each character over the margin width, the overflow cost is incurred.
  /// For each newline in the result, the newline cost is incurred. The layout
  /// chosen is that with the smallest overall cost.
  pub fn layout_with_costs(
    &self,
    margin: u16,
    overflow_cost: f32,
    newline_cost: f32,
  ) -> String {
    let final_layout =
      knot_set::do_layout(self, overflow_cost, newline_cost, margin);
    return final_layout.to_text(0);
  }

  #[doc(hidden)]
  pub fn debug_num_nodes(&self) -> usize {
    let mut counter = NodeCounter::new();
    counter.visit_node(self);
    counter.get_count()
  }

  fn wrap_inner<T: Borrow<Layout>>(
    layouts: &[T],
  ) -> (Layout, Vec<(Layout, Option<Layout>)>) {
    if layouts.len() == 0 {
      (Layout::text(""), vec![])
    } else if layouts.len() == 1 {
      let singleton = layouts[0].borrow();
      (
        singleton.borrow().clone(),
        vec![(singleton.borrow().clone(), None)],
      )
    } else {
      let first = layouts[0].borrow();
      let rest = &layouts[1..];
      let (block, line_blocks) = Layout::wrap_inner(rest);

      let mut new_line_pairs: Vec<_> = line_blocks
        .into_iter()
        .map(|(l, b)| {
          (
            Layout::juxtapose(&[first.borrow(), &Layout::text(" "), &l]),
            b,
          )
        })
        .collect();
      new_line_pairs.push((first.borrow().clone(), Some(block)));

      let new_choices: Vec<_> = new_line_pairs
        .iter()
        .map(|(k, v)| match v {
          Some(b) => Layout::stack_pair(k, b),
          None => k.clone(),
        })
        .collect();

      (Layout::choices(&new_choices), new_line_pairs)
    }
  }

  pub fn wrap<S, T>(layouts: S) -> Layout
  where
    S: AsRef<[T]>,
    T: Borrow<Layout>,
  {
    let slice = layouts.as_ref();
    let (result, _) = Layout::wrap_inner(slice);
    result
  }
}

impl fmt::Debug for Layout {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    <LayoutContents as fmt::Debug>::fmt(&*self.0, f)
  }
}

#[derive(Debug)]
enum LayoutContents {
  Text(SharedString),
  Choice(Layout, Layout),
  Stack(Layout, Layout),
  Juxtapose(SharedString, Layout),
}

struct JuxtaposeBuilder {
  right: Layout,
  memo: col::BTreeMap<*const LayoutContents, Layout>,
}

impl JuxtaposeBuilder {
  fn new(right: &Layout) -> Self {
    JuxtaposeBuilder {
      right: right.clone(),
      memo: col::BTreeMap::new(),
    }
  }

  fn juxtapose(&mut self, left: &Layout) -> Layout {
    if let Some(v) = self.memo.get(&(left.contents() as *const LayoutContents))
    {
      return v.clone();
    }

    let new_layout = match left.contents() {
      LayoutContents::Text(t) => Layout::with_contents(
        LayoutContents::Juxtapose(t.clone(), self.right.clone()),
      ),
      LayoutContents::Choice(first, second) => Layout::with_contents(
        LayoutContents::Choice(self.juxtapose(first), self.juxtapose(second)),
      ),
      LayoutContents::Stack(top, bottom) => Layout::with_contents(
        LayoutContents::Stack(top.clone(), self.juxtapose(bottom)),
      ),
      LayoutContents::Juxtapose(text, jux_right) => Layout::with_contents(
        LayoutContents::Juxtapose(text.clone(), self.juxtapose(jux_right)),
      ),
    };

    self
      .memo
      .insert(left.contents() as *const LayoutContents, new_layout.clone());
    new_layout
  }
}

struct NodeCounter {
  seen: col::BTreeSet<*const LayoutContents>,
}

impl NodeCounter {
  fn new() -> Self {
    NodeCounter {
      seen: col::BTreeSet::new(),
    }
  }
  fn visit_node(&mut self, layout: &Layout) {
    if self.seen.insert(layout.contents() as *const LayoutContents) {
      use LayoutContents::*;
      match layout.contents() {
        Text(_) => {}
        Choice(first, second) => {
          self.visit_node(first);
          self.visit_node(second);
        }
        Stack(top, bottom) => {
          self.visit_node(top);
          self.visit_node(bottom);
        }
        Juxtapose(_left, right) => {
          self.visit_node(right);
        }
      }
    }
  }

  fn get_count(&self) -> usize {
    self.seen.len()
  }
}

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

  #[test]
  fn test_simple_set() {
    let foo = Layout::text("foo");
    let pair = Layout::juxtapose(&[&foo, &foo]);
    assert_eq!(pair.layout(10), "foofoo");
  }

  #[test]
  fn test_simple_stack() {
    let foo = Layout::text("foo");
    let pair = Layout::stack(&[&foo, &foo]);
    assert_eq!(pair.layout(10), "foo\nfoo");
  }

  #[test]
  fn test_simple_choice() {
    let foo = Layout::text("foo");
    let pair = Layout::choices(&[
      &Layout::juxtapose(&[&foo, &foo]),
      &Layout::stack(&[&foo, &foo]),
    ]);
    assert_eq!(pair.layout(10), "foofoo");
    assert_eq!(pair.layout(4), "foo\nfoo");
  }

  #[test]
  fn test_wrap() {
    let lorem = &"Lorem ipsum dolor sit amet, consectetur adipiscing elit.
    Nullam suscipit dolor sed dolor facilisis ultrices ut id nisl. Nam
    malesuada malesuada dolor, eget dapibus nibh euismod at. Vestibulum
    aliquam aliquam mi at tincidunt. Sed imperdiet eget purus in egestas.
    Suspendisse id odio in augue cursus tempor. Duis imperdiet imperdiet eros
    et lobortis. Vestibulum vestibulum condimentum dui, sit amet fringilla libero
    commodo at. Sed aliquam mauris vitae egestas convallis. Integer sed est vitae
    nibh pellentesque sodales. Suspendisse sit amet dignissim arcu. Quisque hendrerit
    pellentesque egestas. Nulla at elementum est. Maecenas lectus turpis, placerat
    vitae est sit amet, mattis pharetra orci. Praesent.";
    let words: Vec<_> =
      lorem.split_whitespace().map(|k| Layout::text(k)).collect();
    let layout = Layout::wrap(words);
    println!("{}", layout.debug_num_nodes());
    assert_eq!(layout.layout(80).chars().filter(|c| c == &'\n').count(), 9);
    assert_eq!(layout.layout(100).chars().filter(|c| c == &'\n').count(), 7);
  }

  #[test]
  fn test_large_stack() {
    let foo = Layout::text("foo");
    let bar = Layout::text("bar");
    let foo_stack = Layout::stack(&[&foo, &bar]);
    let foo_jux = Layout::juxtapose(&[&foo, &bar]);
    let foo_choice = Layout::choices(&[&foo_stack, &foo_jux]);

    let choice = Layout::juxtapose(&vec![&foo_choice; 1000]);

    println!("Done creating layout.");
    println!("Num nodes: {}", choice.debug_num_nodes());

    choice.layout(500);
  }
}
<|endoftext|>