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

// This test checks panic emitted from `mem::{uninitialized,zeroed}`.

#![feature(never_type)]
#![allow(deprecated, invalid_value)]

use std::{mem, panic};
use std::ptr::NonNull;

#[allow(dead_code)]
struct Foo {
    x: u8,
    y: !,
}

enum Bar {}

#[allow(dead_code)]
enum OneVariant { Variant(i32) }

fn test_panic_msg<T>(op: impl (FnOnce() -> T) + panic::UnwindSafe, msg: &str) {
    let err = panic::catch_unwind(op).err();
    assert_eq!(
        err.as_ref().and_then(|a| a.downcast_ref::<String>()).map(|s| &**s),
        Some(msg)
    );
}

fn main() {
    unsafe {
        // Uninitialized types
        test_panic_msg(
            || mem::uninitialized::<!>(),
            "attempted to instantiate uninhabited type `!`"
        );
        test_panic_msg(
            || mem::zeroed::<!>(),
            "attempted to instantiate uninhabited type `!`"
        );
        test_panic_msg(
            || mem::MaybeUninit::<!>::uninit().assume_init(),
            "attempted to instantiate uninhabited type `!`"
        );

        test_panic_msg(
            || mem::uninitialized::<Foo>(),
            "attempted to instantiate uninhabited type `Foo`"
        );
        test_panic_msg(
            || mem::zeroed::<Foo>(),
            "attempted to instantiate uninhabited type `Foo`"
        );
        test_panic_msg(
            || mem::MaybeUninit::<Foo>::uninit().assume_init(),
            "attempted to instantiate uninhabited type `Foo`"
        );

        test_panic_msg(
            || mem::uninitialized::<Bar>(),
            "attempted to instantiate uninhabited type `Bar`"
        );
        test_panic_msg(
            || mem::zeroed::<Bar>(),
            "attempted to instantiate uninhabited type `Bar`"
        );
        test_panic_msg(
            || mem::MaybeUninit::<Bar>::uninit().assume_init(),
            "attempted to instantiate uninhabited type `Bar`"
        );

        // Types that do not like zero-initialziation
        test_panic_msg(
            || mem::uninitialized::<fn()>(),
            "attempted to leave type `fn()` uninitialized, which is invalid"
        );
        test_panic_msg(
            || mem::zeroed::<fn()>(),
            "attempted to zero-initialize type `fn()`, which is invalid"
        );

        test_panic_msg(
            || mem::uninitialized::<*const dyn Send>(),
            "attempted to leave type `*const dyn std::marker::Send` uninitialized, which is invalid"
        );
        test_panic_msg(
            || mem::zeroed::<*const dyn Send>(),
            "attempted to zero-initialize type `*const dyn std::marker::Send`, which is invalid"
        );

        test_panic_msg(
            || mem::uninitialized::<(NonNull<u32>, u32, u32)>(),
            "attempted to leave type `(std::ptr::NonNull<u32>, u32, u32)` uninitialized, \
                which is invalid"
        );
        test_panic_msg(
            || mem::zeroed::<(NonNull<u32>, u32, u32)>(),
            "attempted to zero-initialize type `(std::ptr::NonNull<u32>, u32, u32)`, \
                which is invalid"
        );

        test_panic_msg(
            || mem::uninitialized::<bool>(),
            "attempted to leave type `bool` uninitialized, which is invalid"
        );

        // Some things that should work.
        let _val = mem::zeroed::<bool>();
        let _val = mem::zeroed::<OneVariant>();
        let _val = mem::zeroed::<Option<&'static i32>>();
    }
}
<commit_msg>test some more things that should not panic<commit_after>// run-pass
// ignore-wasm32-bare compiled with panic=abort by default

// This test checks panic emitted from `mem::{uninitialized,zeroed}`.

#![feature(never_type)]
#![allow(deprecated, invalid_value)]

use std::{
    mem::{self, MaybeUninit},
    panic,
    ptr::NonNull,
};

#[allow(dead_code)]
struct Foo {
    x: u8,
    y: !,
}

enum Bar {}

#[allow(dead_code)]
enum OneVariant { Variant(i32) }

fn test_panic_msg<T>(op: impl (FnOnce() -> T) + panic::UnwindSafe, msg: &str) {
    let err = panic::catch_unwind(op).err();
    assert_eq!(
        err.as_ref().and_then(|a| a.downcast_ref::<String>()).map(|s| &**s),
        Some(msg)
    );
}

fn main() {
    unsafe {
        // Uninitialized types
        test_panic_msg(
            || mem::uninitialized::<!>(),
            "attempted to instantiate uninhabited type `!`"
        );
        test_panic_msg(
            || mem::zeroed::<!>(),
            "attempted to instantiate uninhabited type `!`"
        );
        test_panic_msg(
            || MaybeUninit::<!>::uninit().assume_init(),
            "attempted to instantiate uninhabited type `!`"
        );

        test_panic_msg(
            || mem::uninitialized::<Foo>(),
            "attempted to instantiate uninhabited type `Foo`"
        );
        test_panic_msg(
            || mem::zeroed::<Foo>(),
            "attempted to instantiate uninhabited type `Foo`"
        );
        test_panic_msg(
            || MaybeUninit::<Foo>::uninit().assume_init(),
            "attempted to instantiate uninhabited type `Foo`"
        );

        test_panic_msg(
            || mem::uninitialized::<Bar>(),
            "attempted to instantiate uninhabited type `Bar`"
        );
        test_panic_msg(
            || mem::zeroed::<Bar>(),
            "attempted to instantiate uninhabited type `Bar`"
        );
        test_panic_msg(
            || MaybeUninit::<Bar>::uninit().assume_init(),
            "attempted to instantiate uninhabited type `Bar`"
        );

        // Types that do not like zero-initialziation
        test_panic_msg(
            || mem::uninitialized::<fn()>(),
            "attempted to leave type `fn()` uninitialized, which is invalid"
        );
        test_panic_msg(
            || mem::zeroed::<fn()>(),
            "attempted to zero-initialize type `fn()`, which is invalid"
        );

        test_panic_msg(
            || mem::uninitialized::<*const dyn Send>(),
            "attempted to leave type `*const dyn std::marker::Send` uninitialized, which is invalid"
        );
        test_panic_msg(
            || mem::zeroed::<*const dyn Send>(),
            "attempted to zero-initialize type `*const dyn std::marker::Send`, which is invalid"
        );

        test_panic_msg(
            || mem::uninitialized::<(NonNull<u32>, u32, u32)>(),
            "attempted to leave type `(std::ptr::NonNull<u32>, u32, u32)` uninitialized, \
                which is invalid"
        );
        test_panic_msg(
            || mem::zeroed::<(NonNull<u32>, u32, u32)>(),
            "attempted to zero-initialize type `(std::ptr::NonNull<u32>, u32, u32)`, \
                which is invalid"
        );

        test_panic_msg(
            || mem::uninitialized::<bool>(),
            "attempted to leave type `bool` uninitialized, which is invalid"
        );

        // Some things that should work.
        let _val = mem::zeroed::<bool>();
        let _val = mem::zeroed::<OneVariant>();
        let _val = mem::zeroed::<Option<&'static i32>>();
        let _val = mem::zeroed::<MaybeUninit<NonNull<u32>>>();
        let _val = mem::uninitialized::<MaybeUninit<bool>>();

        // We don't panic for these just to be conservative. They are UB as of now (2019-11-09).
        let _val = mem::uninitialized::<i32>();
        let _val = mem::uninitialized::<*const ()>();
    }
}
<|endoftext|>


<commit_before>use std::ffi::OsStr;
use std::path::{Component, Path};

use crate::prelude::*;

use rustc_span::{FileName, SourceFile, SourceFileAndLine, Pos, SourceFileHash, SourceFileHashAlgorithm};

use cranelift_codegen::binemit::CodeOffset;

use gimli::write::{
    Address, AttributeValue, FileId, LineProgram, LineString, FileInfo, LineStringTable, UnitEntryId,
};

// OPTIMIZATION: It is cheaper to do this in one pass than using `.parent()` and `.file_name()`.
fn split_path_dir_and_file(path: &Path) -> (&Path, &OsStr) {
    let mut iter = path.components();
    let file_name = match iter.next_back() {
        Some(Component::Normal(p)) => p,
        component => {
            panic!("Path component {:?} of path {} is an invalid filename", component, path.display());
        }
    };
    let parent = iter.as_path();
    (parent, file_name)
}

// OPTIMIZATION: Avoid UTF-8 validation on UNIX.
fn osstr_as_utf8_bytes(path: &OsStr) -> &[u8] {
    #[cfg(unix)] {
        use std::os::unix::ffi::OsStrExt;
        return path.as_bytes();
    }
    #[cfg(not(unix))] {
        return path.to_str().unwrap().as_bytes();
    }
}

pub(crate) const MD5_LEN: usize = 16;

pub fn make_file_info(hash: SourceFileHash) -> Option<FileInfo> {
    if hash.kind == SourceFileHashAlgorithm::Md5 {
        let mut buf = [0u8; MD5_LEN];
        buf.copy_from_slice(hash.hash_bytes());
        Some(FileInfo {
            timestamp: 0,
            size: 0,
            md5: buf,
        })
    } else {
        None
    }
}

fn line_program_add_file(
    line_program: &mut LineProgram,
    line_strings: &mut LineStringTable,
    file: &SourceFile,
) -> FileId {
    match &file.name {
        FileName::Real(path) => {
            let (dir_path, file_name) = split_path_dir_and_file(path);
            let dir_name = osstr_as_utf8_bytes(dir_path.as_os_str());
            let file_name = osstr_as_utf8_bytes(file_name);

            let dir_id = if !dir_name.is_empty() {
                let dir_name = LineString::new(dir_name, line_program.encoding(), line_strings);
                line_program.add_directory(dir_name)
            } else {
                line_program.default_directory()
            };
            let file_name = LineString::new(
                file_name,
                line_program.encoding(),
                line_strings,
            );

            let info = make_file_info(file.src_hash);

            line_program.file_has_md5 = info.is_some();
            line_program.add_file(file_name, dir_id, info)
        }
        // FIXME give more appropriate file names
        filename => {
            let dir_id = line_program.default_directory();
            let dummy_file_name = LineString::new(
                filename.to_string().into_bytes(),
                line_program.encoding(),
                line_strings,
            );
            line_program.add_file(dummy_file_name, dir_id, None)
        }
    }
}

impl<'tcx> DebugContext<'tcx> {
    pub(super) fn emit_location(&mut self, entry_id: UnitEntryId, span: Span) {
        let loc = self.tcx.sess.source_map().lookup_char_pos(span.lo());

        let file_id = line_program_add_file(
            &mut self.dwarf.unit.line_program,
            &mut self.dwarf.line_strings,
            &loc.file,
        );

        let entry = self.dwarf.unit.get_mut(entry_id);

        entry.set(
            gimli::DW_AT_decl_file,
            AttributeValue::FileIndex(Some(file_id)),
        );
        entry.set(
            gimli::DW_AT_decl_line,
            AttributeValue::Udata(loc.line as u64),
        );
        // FIXME: probably omit this
        entry.set(
            gimli::DW_AT_decl_column,
            AttributeValue::Udata(loc.col.to_usize() as u64),
        );
    }
}

impl<'a, 'tcx> FunctionDebugContext<'a, 'tcx> {
    pub(super) fn create_debug_lines(
        &mut self,
        context: &Context,
        isa: &dyn cranelift_codegen::isa::TargetIsa,
        source_info_set: &indexmap::IndexSet<SourceInfo>,
    ) -> CodeOffset {
        let tcx = self.debug_context.tcx;

        let line_program = &mut self.debug_context.dwarf.unit.line_program;

        line_program.begin_sequence(Some(Address::Symbol {
            symbol: self.symbol,
            addend: 0,
        }));

        let encinfo = isa.encoding_info();
        let func = &context.func;
        let mut blocks = func.layout.blocks().collect::<Vec<_>>();
        blocks.sort_by_key(|block| func.offsets[*block]); // Ensure inst offsets always increase

        let line_strings = &mut self.debug_context.dwarf.line_strings;
        let function_span = self.mir.span;
        let mut last_span = None;
        let mut last_file = None;
        let mut create_row_for_span = |line_program: &mut LineProgram, span: Span| {
            if let Some(last_span) = last_span {
                if span == last_span {
                    line_program.generate_row();
                    return;
                }
            }
            last_span = Some(span);

            // Based on https://github.com/rust-lang/rust/blob/e369d87b015a84653343032833d65d0545fd3f26/src/librustc_codegen_ssa/mir/mod.rs#L116-L131
            // In order to have a good line stepping behavior in debugger, we overwrite debug
            // locations of macro expansions with that of the outermost expansion site
            // (unless the crate is being compiled with `-Z debug-macros`).
            let span = if !span.from_expansion() ||
                tcx.sess.opts.debugging_opts.debug_macros {
                span
            } else {
                // Walk up the macro expansion chain until we reach a non-expanded span.
                // We also stop at the function body level because no line stepping can occur
                // at the level above that.
                rustc_span::hygiene::walk_chain(span, function_span.ctxt())
            };

            let (file, line, col) = match tcx.sess.source_map().lookup_line(span.lo()) {
                Ok(SourceFileAndLine { sf: file, line }) => {
                    let line_pos = file.line_begin_pos(span.lo());

                    (file, u64::try_from(line).unwrap() + 1, u64::from((span.lo() - line_pos).to_u32()) + 1)
                }
                Err(file) => (file, 0, 0)
            };

            // line_program_add_file is very slow.
            // Optimize for the common case of the current file not being changed.
            let current_file_changed = if let Some(last_file) = &last_file {
                // If the allocations are not equal, then the files may still be equal, but that
                // is not a problem, as this is just an optimization.
                !rustc_data_structures::sync::Lrc::ptr_eq(last_file, &file)
            } else {
                true
            };
            if current_file_changed {
                let file_id = line_program_add_file(line_program, line_strings, &file);
                line_program.row().file = file_id;
                last_file = Some(file.clone());
            }

            line_program.row().line = line;
            line_program.row().column = col;
            line_program.generate_row();
        };

        let mut end = 0;
        for block in blocks {
            for (offset, inst, size) in func.inst_offsets(block, &encinfo) {
                let srcloc = func.srclocs[inst];
                line_program.row().address_offset = offset as u64;
                if !srcloc.is_default() {
                    let source_info = *source_info_set.get_index(srcloc.bits() as usize).unwrap();
                    create_row_for_span(line_program, source_info.span);
                } else {
                    create_row_for_span(line_program, self.mir.span);
                }
                end = offset + size;
            }
        }

        line_program.end_sequence(end as u64);

        let entry = self.debug_context.dwarf.unit.get_mut(self.entry_id);
        entry.set(
            gimli::DW_AT_low_pc,
            AttributeValue::Address(Address::Symbol {
                symbol: self.symbol,
                addend: 0,
            }),
        );
        entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(end as u64));

        self.debug_context
            .emit_location(self.entry_id, self.mir.span);

        end
    }
}
<commit_msg>Use md5 dwarf hash, only if all files has it<commit_after>use std::ffi::OsStr;
use std::path::{Component, Path};

use crate::prelude::*;

use rustc_span::{FileName, SourceFile, SourceFileAndLine, Pos, SourceFileHash, SourceFileHashAlgorithm};

use cranelift_codegen::binemit::CodeOffset;

use gimli::write::{
    Address, AttributeValue, FileId, LineProgram, LineString, FileInfo, LineStringTable, UnitEntryId,
};

// OPTIMIZATION: It is cheaper to do this in one pass than using `.parent()` and `.file_name()`.
fn split_path_dir_and_file(path: &Path) -> (&Path, &OsStr) {
    let mut iter = path.components();
    let file_name = match iter.next_back() {
        Some(Component::Normal(p)) => p,
        component => {
            panic!("Path component {:?} of path {} is an invalid filename", component, path.display());
        }
    };
    let parent = iter.as_path();
    (parent, file_name)
}

// OPTIMIZATION: Avoid UTF-8 validation on UNIX.
fn osstr_as_utf8_bytes(path: &OsStr) -> &[u8] {
    #[cfg(unix)] {
        use std::os::unix::ffi::OsStrExt;
        return path.as_bytes();
    }
    #[cfg(not(unix))] {
        return path.to_str().unwrap().as_bytes();
    }
}

pub(crate) const MD5_LEN: usize = 16;

pub fn make_file_info(hash: SourceFileHash) -> Option<FileInfo> {
    if hash.kind == SourceFileHashAlgorithm::Md5 {
        let mut buf = [0u8; MD5_LEN];
        buf.copy_from_slice(hash.hash_bytes());
        Some(FileInfo {
            timestamp: 0,
            size: 0,
            md5: buf,
        })
    } else {
        None
    }
}

fn line_program_add_file(
    line_program: &mut LineProgram,
    line_strings: &mut LineStringTable,
    file: &SourceFile,
) -> FileId {
    match &file.name {
        FileName::Real(path) => {
            let (dir_path, file_name) = split_path_dir_and_file(path);
            let dir_name = osstr_as_utf8_bytes(dir_path.as_os_str());
            let file_name = osstr_as_utf8_bytes(file_name);

            let dir_id = if !dir_name.is_empty() {
                let dir_name = LineString::new(dir_name, line_program.encoding(), line_strings);
                line_program.add_directory(dir_name)
            } else {
                line_program.default_directory()
            };
            let file_name = LineString::new(
                file_name,
                line_program.encoding(),
                line_strings,
            );

            let info = make_file_info(file.src_hash);

            line_program.file_has_md5 &= info.is_some();
            line_program.add_file(file_name, dir_id, info)
        }
        // FIXME give more appropriate file names
        filename => {
            let dir_id = line_program.default_directory();
            let dummy_file_name = LineString::new(
                filename.to_string().into_bytes(),
                line_program.encoding(),
                line_strings,
            );
            line_program.add_file(dummy_file_name, dir_id, None)
        }
    }
}

impl<'tcx> DebugContext<'tcx> {
    pub(super) fn emit_location(&mut self, entry_id: UnitEntryId, span: Span) {
        let loc = self.tcx.sess.source_map().lookup_char_pos(span.lo());

        let file_id = line_program_add_file(
            &mut self.dwarf.unit.line_program,
            &mut self.dwarf.line_strings,
            &loc.file,
        );

        let entry = self.dwarf.unit.get_mut(entry_id);

        entry.set(
            gimli::DW_AT_decl_file,
            AttributeValue::FileIndex(Some(file_id)),
        );
        entry.set(
            gimli::DW_AT_decl_line,
            AttributeValue::Udata(loc.line as u64),
        );
        // FIXME: probably omit this
        entry.set(
            gimli::DW_AT_decl_column,
            AttributeValue::Udata(loc.col.to_usize() as u64),
        );
    }
}

impl<'a, 'tcx> FunctionDebugContext<'a, 'tcx> {
    pub(super) fn create_debug_lines(
        &mut self,
        context: &Context,
        isa: &dyn cranelift_codegen::isa::TargetIsa,
        source_info_set: &indexmap::IndexSet<SourceInfo>,
    ) -> CodeOffset {
        let tcx = self.debug_context.tcx;

        let line_program = &mut self.debug_context.dwarf.unit.line_program;

        line_program.begin_sequence(Some(Address::Symbol {
            symbol: self.symbol,
            addend: 0,
        }));

        let encinfo = isa.encoding_info();
        let func = &context.func;
        let mut blocks = func.layout.blocks().collect::<Vec<_>>();
        blocks.sort_by_key(|block| func.offsets[*block]); // Ensure inst offsets always increase

        let line_strings = &mut self.debug_context.dwarf.line_strings;
        let function_span = self.mir.span;
        let mut last_span = None;
        let mut last_file = None;
        let mut create_row_for_span = |line_program: &mut LineProgram, span: Span| {
            if let Some(last_span) = last_span {
                if span == last_span {
                    line_program.generate_row();
                    return;
                }
            }
            last_span = Some(span);

            // Based on https://github.com/rust-lang/rust/blob/e369d87b015a84653343032833d65d0545fd3f26/src/librustc_codegen_ssa/mir/mod.rs#L116-L131
            // In order to have a good line stepping behavior in debugger, we overwrite debug
            // locations of macro expansions with that of the outermost expansion site
            // (unless the crate is being compiled with `-Z debug-macros`).
            let span = if !span.from_expansion() ||
                tcx.sess.opts.debugging_opts.debug_macros {
                span
            } else {
                // Walk up the macro expansion chain until we reach a non-expanded span.
                // We also stop at the function body level because no line stepping can occur
                // at the level above that.
                rustc_span::hygiene::walk_chain(span, function_span.ctxt())
            };

            let (file, line, col) = match tcx.sess.source_map().lookup_line(span.lo()) {
                Ok(SourceFileAndLine { sf: file, line }) => {
                    let line_pos = file.line_begin_pos(span.lo());

                    (file, u64::try_from(line).unwrap() + 1, u64::from((span.lo() - line_pos).to_u32()) + 1)
                }
                Err(file) => (file, 0, 0)
            };

            // line_program_add_file is very slow.
            // Optimize for the common case of the current file not being changed.
            let current_file_changed = if let Some(last_file) = &last_file {
                // If the allocations are not equal, then the files may still be equal, but that
                // is not a problem, as this is just an optimization.
                !rustc_data_structures::sync::Lrc::ptr_eq(last_file, &file)
            } else {
                true
            };
            if current_file_changed {
                let file_id = line_program_add_file(line_program, line_strings, &file);
                line_program.row().file = file_id;
                last_file = Some(file.clone());
            }

            line_program.row().line = line;
            line_program.row().column = col;
            line_program.generate_row();
        };

        let mut end = 0;
        for block in blocks {
            for (offset, inst, size) in func.inst_offsets(block, &encinfo) {
                let srcloc = func.srclocs[inst];
                line_program.row().address_offset = offset as u64;
                if !srcloc.is_default() {
                    let source_info = *source_info_set.get_index(srcloc.bits() as usize).unwrap();
                    create_row_for_span(line_program, source_info.span);
                } else {
                    create_row_for_span(line_program, self.mir.span);
                }
                end = offset + size;
            }
        }

        line_program.end_sequence(end as u64);

        let entry = self.debug_context.dwarf.unit.get_mut(self.entry_id);
        entry.set(
            gimli::DW_AT_low_pc,
            AttributeValue::Address(Address::Symbol {
                symbol: self.symbol,
                addend: 0,
            }),
        );
        entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(end as u64));

        self.debug_context
            .emit_location(self.entry_id, self.mir.span);

        end
    }
}
<|endoftext|>


<commit_before>
use glium::{Program, Surface};

use glium::VertexBuffer;
use glium::index;
use glium;
use glutin;

use render::shader::ShaderPair;
use render::texture_array::{TextureDirectory, TextureArrayDimensions};

use input;
use HashMap;
use input::InputState;
use color::Color;

use glium::texture::srgb_texture2d_array::SrgbTexture2dArray;

use std::sync::mpsc::{channel, Receiver};

use notify::{RecommendedWatcher, Watcher, RecursiveMode, RawEvent};

use super::window;
use super::program;
use render::command::*;
use render::command::Command::*;

use dimensions::Dimensions;
use render::vertex::Vertex;

use font::*;

use {JamResult, JamError};

use std::hash::Hash;

use image;

pub struct Renderer<BufferKey> where BufferKey : Hash + Eq {
    pub shader_pair : ShaderPair,
    pub texture_directory: TextureDirectory,
    pub font_directory: FontDirectory,
    pub resource_file_watcher : RecommendedWatcher,
    pub resource_file_change_events: Receiver<RawEvent>,
    pub display: glium::Display,
    pub input_state: InputState,
    pub program : Option<Program>,
    pub texture : Option<(SrgbTexture2dArray, TextureArrayDimensions)>,
    pub vertex_buffers : HashMap<BufferKey, VertexBuffer<Vertex>>,
    pub last_dimensions : Dimensions,
    pub fonts: Vec<LoadedBitmapFont>,
}

fn dimensions_for(display : &glium::Display) -> Dimensions {
    let (width_pixels, height_pixels) = display.get_framebuffer_dimensions();

    let scale : f64 = display.get_window().map(|w| w.hidpi_factor() as f64).unwrap_or(1.0);

    Dimensions {
        pixels: (width_pixels, height_pixels),
        scale: scale,
    }
}

impl <BufferKey> Renderer<BufferKey> where BufferKey : Hash + Eq + Clone {
    pub fn new(shader_pair : ShaderPair, texture_directory: TextureDirectory, font_directory: FontDirectory, initial_dimensions: (u32, u32), window_name: String) -> JamResult<Renderer<BufferKey>> { //
        let (tx, notifier_rx) = channel::<RawEvent>();

        let mut resource_file_watcher : RecommendedWatcher = Watcher::new_raw(tx).expect("a watcher");
        resource_file_watcher.watch(&shader_pair.vertex_path, RecursiveMode::Recursive).expect("watching shader vertex path");
        resource_file_watcher.watch(&shader_pair.fragment_path, RecursiveMode::Recursive).expect("watching shader fragment path");
        resource_file_watcher.watch(&texture_directory.path, RecursiveMode::Recursive).expect("watching texture directory path");

        let display = window::create_window(&window_name, true, initial_dimensions)?;

        let dimensions = dimensions_for(&display);

        Ok(Renderer {
            shader_pair : shader_pair,
            texture_directory: texture_directory,
            font_directory: font_directory,
            resource_file_watcher : resource_file_watcher,
            resource_file_change_events: notifier_rx,
            display: display,
            input_state: InputState::default(),
            program : None,
            texture : None,
            vertex_buffers : HashMap::default(),
            last_dimensions : dimensions,
            fonts: Vec::new(),
        })
    }

    pub fn load_font(&mut self, font_description: &FontDescription) -> JamResult<()> {
        let found_font = self.fonts.iter().any(|lf| &lf.font.description == font_description); // { f.font.description == font_description }

        if found_font {
            Ok(())
        } else if let Some(dimensions) = self.texture_array_dimensions() {
            println!("doing the fonty thing!!!");
            let mut full_path = self.font_directory.path.clone();
            full_path.push(font_description.family.clone());
            full_path.set_extension("ttf");

            println!("load font {:?} with full_path -> {:?}", font_description, full_path);

            let font = build_font(full_path.as_path(), font_description, dimensions.width)?;

            println!("we've loaded the font, unloading the texture");
            self.fonts.push(font);
            self.texture = None;

            Ok(())
        } else {
            println!("must load texture before font, you doophus");
            Err(JamError::MustLoadTextureBeforeFont)
        }
    }

    pub fn get_font(&self, font_description: &FontDescription) -> Option<(&BitmapFont, u32)> {
        if let Some(dimensions) = self.texture_array_dimensions() {
            if let Some(font_position) = self.fonts.iter().position(|loaded_font| &loaded_font.font.description == font_description) {
                let font_count = self.fonts.len();
                let layer = dimensions.layers as usize - font_count + font_position;
                Some((&self.fonts[font_position].font, layer as u32))
            } else {
                None
            }
        } else {
            None
        }
    }

    pub fn texture_array_dimensions(&self) -> Option<TextureArrayDimensions> {
       self.texture.as_ref().map(|&(_, d)| d )
    }

    pub fn clear_fonts(&mut self) {
        if !self.fonts.is_empty() {
            self.fonts.clear();
        }
    }

    pub fn screenshot(&mut self) -> image::DynamicImage {
        let image: glium::texture::RawImage2d<u8> = self.display.read_front_buffer();
        let image = image::ImageBuffer::from_raw(image.width, image.height, image.data.into_owned()).unwrap();
        let image = image::DynamicImage::ImageRgba8(image).flipv();
        image
    }

    pub fn load_resources(&mut self) {
        let (reload_program, reload_texture) = check_reload(&self.resource_file_change_events, &self.shader_pair, &self.texture_directory);

        if reload_program || self.program.is_none() {
            let program_load_result = self.shader_pair.load().and_then(|shader_data| shader_data.load(&self.display));
            println!("program load result -> {:?}", program_load_result);
            self.program = program_load_result.ok();
        }

        if reload_texture || self.texture.is_none() {
            println!("reload texture");
            let texture_load_result = self.texture_directory.load().and_then(|mut texture_data| {
                for loaded_font in &self.fonts {
                    println!("copying in font -> {:?} to texture array",loaded_font.font.description);
                    texture_data.data.push(loaded_font.image.clone().into_raw());
                }
                let mut dimensions = texture_data.dimensions;
                dimensions.layers += self.fonts.len() as u32;
                texture_data.load(&self.display).map(|t| (t, dimensions))
            });
            println!("texture load result -> {:?}", texture_load_result);
            self.texture = texture_load_result.ok();
        }
    }

    pub fn begin(&mut self) -> (Dimensions, InputState) {
        self.load_resources();

        let events : Vec<glutin::Event> = self.display.poll_events().collect();
        self.input_state = input::produce(&self.input_state, &events);

        let new_dimensions = dimensions_for(&self.display);

        if new_dimensions != self.last_dimensions { // this is a fix for Glutin/WINIT, when changing windows with different scales it does (but same points size), it won't properly resize
            println!("resize detected from {:?} to {:?}", self.last_dimensions, new_dimensions);
            if Dimensions::approx_equal_point_size(new_dimensions, self.last_dimensions)  {
                println!("missed resize case detected");
                if let Some(window) = self.display.get_window() {
                    if let Some((w, h)) = window.get_inner_size_points() {
                        println!("jiggling a little");
                        window.set_inner_size(w+1, h);
                        window.set_inner_size(w, h);
                    }
                }
            }

            self.last_dimensions = new_dimensions;
        }

        (new_dimensions, self.input_state.clone())
    }

    pub fn render(&mut self, passes: Vec<Pass<BufferKey>>, clear_color: Color) -> JamResult<()> {
        if let (&Some(ref pr), &Some((ref tr, _))) = (&self.program, &self.texture) {
            let mut target = self.display.draw();

            target.clear_color_srgb_and_depth(clear_color.float_tup(), 1.0);

            let tex = tr.sampled().magnify_filter(glium::uniforms::MagnifySamplerFilter::Nearest);

            for pass in passes {
                let blend = program::draw_params_for_blend(pass.blend);
                if pass.clear_depth {
                    target.clear_depth(1.0);
                }

                for command in pass.commands {
                    // println!("received command -> {:?}", command);
                    match command {
                        Delete { key } => {
                            let _ = self.vertex_buffers.remove(&key);
                        },
                        DeleteMatching { pred } => {
                            let keys_to_delete : Vec<_>= self.vertex_buffers.keys().filter(|e| pred(e)).cloned().collect();
                            for key in keys_to_delete.iter() {
                                self.vertex_buffers.remove(key);
                            }
                        }
                        Update { key, vertices } => {
                            let new_vertex_buffer = VertexBuffer::persistent(&self.display,&vertices).unwrap();
                            self.vertex_buffers.insert(key, new_vertex_buffer);
                        },
                        Draw { key, uniforms } => {
                            if let Some(vertex_buffer) = self.vertex_buffers.get(&key) {
                                let uniforms = uniform! {
                                    u_matrix: uniforms.transform,
                                    u_texture_array: tex,
                                    u_color: uniforms.color.float_raw(),
                                    u_alpha_minimum: 0.01_f32,
                                };
                                target.draw(vertex_buffer, &index::NoIndices(index::PrimitiveType::TrianglesList), &pr, &uniforms, &blend).unwrap();
                            } else {
                                // println!("couldnt draw for {:?}", key);
                            }
                        },
                        DrawNew { key , vertices, uniforms } => {
                            let new_vertex_buffer = VertexBuffer::persistent(&self.display,&vertices).unwrap();

                            let uniforms = uniform! {
                                u_matrix: uniforms.transform,
                                u_texture_array: tex,
                                u_color: uniforms.color.float_raw(),
                                u_alpha_minimum: 0.01_f32,
                            };

                            target.draw(&new_vertex_buffer, &index::NoIndices(index::PrimitiveType::TrianglesList), &pr, &uniforms, &blend).unwrap();

                            if let Some(name) = key {
                                self.vertex_buffers.insert(name,new_vertex_buffer);
                            }
                        },
                    }
                }
            }
            target.finish().map_err(JamError::SwapBufferError)
        } else {
            Err(JamError::RenderingPipelineIncomplete)
        }
    }
}

pub fn check_reload(rx: &Receiver<RawEvent>, shader_pair:&ShaderPair, texture_directory: &TextureDirectory) -> (bool, bool) {
    let mut reload_program = false;
    let mut reload_texture = false;

    'fs: loop {
        match rx.try_recv() {
            Ok(RawEvent { path, op:_, cookie:_ }) => {
                if let Some(p) = path {
                    if shader_pair.contains(&p) {
                        reload_program = true;
                    } else if texture_directory.contains(&p) {
                        reload_texture = true;
                    } else {
                        use std::path;
                        let components: Vec<path::Component> = p.components().collect();
                        println!("fs event {:?} -> {:?}", p, components);
                    }
                }
            },
            Err(_) => break 'fs,
        }
    }

     (reload_program, reload_texture)
}<commit_msg>Removed some debug<commit_after>
use glium::{Program, Surface};

use glium::VertexBuffer;
use glium::index;
use glium;
use glutin;

use render::shader::ShaderPair;
use render::texture_array::{TextureDirectory, TextureArrayDimensions};

use input;
use HashMap;
use input::InputState;
use color::Color;

use glium::texture::srgb_texture2d_array::SrgbTexture2dArray;

use std::sync::mpsc::{channel, Receiver};

use notify::{RecommendedWatcher, Watcher, RecursiveMode, RawEvent};

use super::window;
use super::program;
use render::command::*;
use render::command::Command::*;

use dimensions::Dimensions;
use render::vertex::Vertex;

use font::*;

use {JamResult, JamError};

use std::hash::Hash;

use image;

pub struct Renderer<BufferKey> where BufferKey : Hash + Eq {
    pub shader_pair : ShaderPair,
    pub texture_directory: TextureDirectory,
    pub font_directory: FontDirectory,
    pub resource_file_watcher : RecommendedWatcher,
    pub resource_file_change_events: Receiver<RawEvent>,
    pub display: glium::Display,
    pub input_state: InputState,
    pub program : Option<Program>,
    pub texture : Option<(SrgbTexture2dArray, TextureArrayDimensions)>,
    pub vertex_buffers : HashMap<BufferKey, VertexBuffer<Vertex>>,
    pub last_dimensions : Dimensions,
    pub fonts: Vec<LoadedBitmapFont>,
}

fn dimensions_for(display : &glium::Display) -> Dimensions {
    let (width_pixels, height_pixels) = display.get_framebuffer_dimensions();

    let scale : f64 = display.get_window().map(|w| w.hidpi_factor() as f64).unwrap_or(1.0);

    Dimensions {
        pixels: (width_pixels, height_pixels),
        scale: scale,
    }
}

impl <BufferKey> Renderer<BufferKey> where BufferKey : Hash + Eq + Clone {
    pub fn new(shader_pair : ShaderPair, texture_directory: TextureDirectory, font_directory: FontDirectory, initial_dimensions: (u32, u32), window_name: String) -> JamResult<Renderer<BufferKey>> { //
        let (tx, notifier_rx) = channel::<RawEvent>();

        let mut resource_file_watcher : RecommendedWatcher = Watcher::new_raw(tx).expect("a watcher");
        resource_file_watcher.watch(&shader_pair.vertex_path, RecursiveMode::Recursive).expect("watching shader vertex path");
        resource_file_watcher.watch(&shader_pair.fragment_path, RecursiveMode::Recursive).expect("watching shader fragment path");
        resource_file_watcher.watch(&texture_directory.path, RecursiveMode::Recursive).expect("watching texture directory path");

        let display = window::create_window(&window_name, true, initial_dimensions)?;

        let dimensions = dimensions_for(&display);

        Ok(Renderer {
            shader_pair : shader_pair,
            texture_directory: texture_directory,
            font_directory: font_directory,
            resource_file_watcher : resource_file_watcher,
            resource_file_change_events: notifier_rx,
            display: display,
            input_state: InputState::default(),
            program : None,
            texture : None,
            vertex_buffers : HashMap::default(),
            last_dimensions : dimensions,
            fonts: Vec::new(),
        })
    }

    pub fn load_font(&mut self, font_description: &FontDescription) -> JamResult<()> {
        let found_font = self.fonts.iter().any(|lf| &lf.font.description == font_description); // { f.font.description == font_description }

        if found_font {
            Ok(())
        } else if let Some(dimensions) = self.texture_array_dimensions() {
            let mut full_path = self.font_directory.path.clone();
            full_path.push(font_description.family.clone());
            full_path.set_extension("ttf");

            // println!("load font {:?} with full_path -> {:?}", font_description, full_path);

            let font = build_font(full_path.as_path(), font_description, dimensions.width)?;

            self.fonts.push(font);
            self.texture = None;

            Ok(())
        } else {
            // println!("must load texture before font, you doophus");
            Err(JamError::MustLoadTextureBeforeFont)
        }
    }

    pub fn get_font(&self, font_description: &FontDescription) -> Option<(&BitmapFont, u32)> {
        if let Some(dimensions) = self.texture_array_dimensions() {
            if let Some(font_position) = self.fonts.iter().position(|loaded_font| &loaded_font.font.description == font_description) {
                let font_count = self.fonts.len();
                let layer = dimensions.layers as usize - font_count + font_position;
                Some((&self.fonts[font_position].font, layer as u32))
            } else {
                None
            }
        } else {
            None
        }
    }

    pub fn texture_array_dimensions(&self) -> Option<TextureArrayDimensions> {
       self.texture.as_ref().map(|&(_, d)| d )
    }

    pub fn clear_fonts(&mut self) {
        if !self.fonts.is_empty() {
            self.fonts.clear();
        }
    }

    pub fn screenshot(&mut self) -> image::DynamicImage {
        let image: glium::texture::RawImage2d<u8> = self.display.read_front_buffer();
        let image = image::ImageBuffer::from_raw(image.width, image.height, image.data.into_owned()).unwrap();
        let image = image::DynamicImage::ImageRgba8(image).flipv();
        image
    }

    pub fn load_resources(&mut self) {
        let (reload_program, reload_texture) = check_reload(&self.resource_file_change_events, &self.shader_pair, &self.texture_directory);

        if reload_program || self.program.is_none() {
            let program_load_result = self.shader_pair.load().and_then(|shader_data| shader_data.load(&self.display));
            println!("program load result -> {:?}", program_load_result);
            self.program = program_load_result.ok();
        }

        if reload_texture || self.texture.is_none() {
            println!("reload texture");
            let texture_load_result = self.texture_directory.load().and_then(|mut texture_data| {
                for loaded_font in &self.fonts {
                    println!("copying in font -> {:?} to texture array",loaded_font.font.description);
                    texture_data.data.push(loaded_font.image.clone().into_raw());
                }
                let mut dimensions = texture_data.dimensions;
                dimensions.layers += self.fonts.len() as u32;
                texture_data.load(&self.display).map(|t| (t, dimensions))
            });
            println!("texture load result -> {:?}", texture_load_result);
            self.texture = texture_load_result.ok();
        }
    }

    pub fn begin(&mut self) -> (Dimensions, InputState) {
        self.load_resources();

        let events : Vec<glutin::Event> = self.display.poll_events().collect();
        self.input_state = input::produce(&self.input_state, &events);

        let new_dimensions = dimensions_for(&self.display);

        if new_dimensions != self.last_dimensions { // this is a fix for Glutin/WINIT, when changing windows with different scales it does (but same points size), it won't properly resize
            println!("resize detected from {:?} to {:?}", self.last_dimensions, new_dimensions);
            if Dimensions::approx_equal_point_size(new_dimensions, self.last_dimensions)  {
                println!("missed resize case detected");
                if let Some(window) = self.display.get_window() {
                    if let Some((w, h)) = window.get_inner_size_points() {
                        println!("jiggling a little");
                        window.set_inner_size(w+1, h);
                        window.set_inner_size(w, h);
                    }
                }
            }

            self.last_dimensions = new_dimensions;
        }

        (new_dimensions, self.input_state.clone())
    }

    pub fn render(&mut self, passes: Vec<Pass<BufferKey>>, clear_color: Color) -> JamResult<()> {
        if let (&Some(ref pr), &Some((ref tr, _))) = (&self.program, &self.texture) {
            let mut target = self.display.draw();

            target.clear_color_srgb_and_depth(clear_color.float_tup(), 1.0);

            let tex = tr.sampled().magnify_filter(glium::uniforms::MagnifySamplerFilter::Nearest);

            for pass in passes {
                let blend = program::draw_params_for_blend(pass.blend);
                if pass.clear_depth {
                    target.clear_depth(1.0);
                }

                for command in pass.commands {
                    // println!("received command -> {:?}", command);
                    match command {
                        Delete { key } => {
                            let _ = self.vertex_buffers.remove(&key);
                        },
                        DeleteMatching { pred } => {
                            let keys_to_delete : Vec<_>= self.vertex_buffers.keys().filter(|e| pred(e)).cloned().collect();
                            for key in keys_to_delete.iter() {
                                self.vertex_buffers.remove(key);
                            }
                        }
                        Update { key, vertices } => {
                            let new_vertex_buffer = VertexBuffer::persistent(&self.display,&vertices).unwrap();
                            self.vertex_buffers.insert(key, new_vertex_buffer);
                        },
                        Draw { key, uniforms } => {
                            if let Some(vertex_buffer) = self.vertex_buffers.get(&key) {
                                let uniforms = uniform! {
                                    u_matrix: uniforms.transform,
                                    u_texture_array: tex,
                                    u_color: uniforms.color.float_raw(),
                                    u_alpha_minimum: 0.01_f32,
                                };
                                target.draw(vertex_buffer, &index::NoIndices(index::PrimitiveType::TrianglesList), &pr, &uniforms, &blend).unwrap();
                            } else {
                                // println!("couldnt draw for {:?}", key);
                            }
                        },
                        DrawNew { key , vertices, uniforms } => {
                            let new_vertex_buffer = VertexBuffer::persistent(&self.display,&vertices).unwrap();

                            let uniforms = uniform! {
                                u_matrix: uniforms.transform,
                                u_texture_array: tex,
                                u_color: uniforms.color.float_raw(),
                                u_alpha_minimum: 0.01_f32,
                            };

                            target.draw(&new_vertex_buffer, &index::NoIndices(index::PrimitiveType::TrianglesList), &pr, &uniforms, &blend).unwrap();

                            if let Some(name) = key {
                                self.vertex_buffers.insert(name,new_vertex_buffer);
                            }
                        },
                    }
                }
            }
            target.finish().map_err(JamError::SwapBufferError)
        } else {
            Err(JamError::RenderingPipelineIncomplete)
        }
    }
}

pub fn check_reload(rx: &Receiver<RawEvent>, shader_pair:&ShaderPair, texture_directory: &TextureDirectory) -> (bool, bool) {
    let mut reload_program = false;
    let mut reload_texture = false;

    'fs: loop {
        match rx.try_recv() {
            Ok(RawEvent { path, op:_, cookie:_ }) => {
                if let Some(p) = path {
                    if shader_pair.contains(&p) {
                        reload_program = true;
                    } else if texture_directory.contains(&p) {
                        reload_texture = true;
                    } else {
                        use std::path;
                        let components: Vec<path::Component> = p.components().collect();
                        println!("fs event {:?} -> {:?}", p, components);
                    }
                }
            },
            Err(_) => break 'fs,
        }
    }

     (reload_program, reload_texture)
}<|endoftext|>


<commit_before>use std::iter;

extern crate itertools;
use self::itertools::Itertools;

pub fn main(input: &str) -> Vec<String> {
    let s1 = house_count(&input);
    let s2 = robo_santa(&input);
    vec![s1.to_string(), s2.to_string()]
}

fn directions(c: char) -> (i32, i32) {
    match c {
        '>' => (1, 0),
        '<' => (-1, 0),
        '^' => (0, 1),
        'v' => (0, -1),
        _ => panic!("invalid input"),
    }
}

fn visit_houses<'a>(input: &'a str) -> Box<Iterator<Item = (i32, i32)> + 'a> {
    Box::new(input.chars()
                  .map(directions)
                  .scan((0, 0), |&mut (ref mut x, ref mut y), (dx, dy)| {
                      *x += dx;
                      *y += dy;
                      Some((*x, *y))
                  })
                  .chain(iter::once((0, 0))))
}

fn house_count(input: &str) -> usize {
    visit_houses(input)
        .unique()
        .count()
}

#[test]
fn test_house_count() {
    assert_eq!(house_count(">"), 2);
    assert_eq!(house_count("^>v<"), 4);
    assert_eq!(house_count("^v^v^v^v^v"), 2);
}

fn robo_santa(input: &str) -> usize {
    let odd: String = input.chars().step(2).collect();
    let even: String = input.chars().skip(1).step(2).collect();

    return visit_houses(&odd)
               .chain(visit_houses(&even))
               .unique()
               .count();
}

#[test]
fn test_robo_santa() {
    assert_eq!(robo_santa("^v"), 3);
    assert_eq!(robo_santa("^>v<"), 3);
    assert_eq!(robo_santa("^v^v^v^v^v"), 11);
}
<commit_msg>multi santa<commit_after>use std::iter;

extern crate itertools;
use self::itertools::Itertools;

pub fn main(input: &str) -> Vec<String> {
    let s1 = multi_santa(&input, 1);
    let s2 = multi_santa(&input, 2);
    vec![s1.to_string(), s2.to_string()]
}

fn directions(c: char) -> (i32, i32) {
    match c {
        '>' => (1, 0),
        '<' => (-1, 0),
        '^' => (0, 1),
        'v' => (0, -1),
        _ => panic!("invalid input"),
    }
}

fn visit_houses<'a>(input: &'a str) -> Box<Iterator<Item = (i32, i32)> + 'a> {
    Box::new(input.chars()
                  .map(directions)
                  .scan((0, 0), |&mut (ref mut x, ref mut y), (dx, dy)| {
                      *x += dx;
                      *y += dy;
                      Some((*x, *y))
                  }))
    // .chain(iter::once((0, 0))))
}

fn multi_santa(input: &str, how_many: usize) -> usize {
    let santas = (0..how_many)
                     .map(|n| input.chars().skip(n).step(how_many).collect())
                     .collect::<Vec<String>>();
    return santas.iter()
                 .fold(Box::new(iter::once((0, 0))) as Box<Iterator<Item = (i32, i32)>>,
                       |itr, directions| Box::new(itr.chain(visit_houses(&directions))))
                 .unique()
                 .count();
}

#[test]
fn test_house_count() {
    assert_eq!(multi_santa(">", 1), 2);
    assert_eq!(multi_santa("^>v<", 1), 4);
    assert_eq!(multi_santa("^v^v^v^v^v", 1), 2);
}

#[test]
fn test_robo_santa() {
    assert_eq!(multi_santa("^v", 2), 3);
    assert_eq!(multi_santa("^>v<", 2), 3);
    assert_eq!(multi_santa("^v^v^v^v^v", 2), 11);
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

extern crate i2cdev;

use super::*;

use self::i2cdev::core::I2CDevice;
use self::i2cdev::linux::{LinuxI2CDevice, LinuxI2CError};

const MODE_REGISTER: u8 = 0x00;
const FRAME_REGISTER: u8 = 0x01;
const AUTOPLAY1_REGISTER: u8 = 0x02;
const AUTOPLAY2_REGISTER: u8 = 0x03;
const BLINK_REGISTER: u8 = 0x05;
const AUDIOSYNC_REGISTER: u8 = 0x06;
const BREATH1_REGISTER: u8 = 0x08;
const BREATH2_REGISTER: u8 = 0x09;
const SHUTDOWN_REGISTER: u8 = 0x0A;
const GAIN_REGISTER: u8 = 0x0B;
const ADC_REGISTER: u8 = 0x0C;

const CONFIG_BANK: u8 = 0x0B;
const BANK_ADDRESS: u8 = 0xFD;

const PICTURE_MODE: u8 = 0x00;
const AUTOPLAY_MODE: u8 = 0x08;
const AUDIOPLAY_MODE: u8 = 0x18;

const ENABLE_OFFSET: u8 = 0x00;
const BLINK_OFFSET: u8 = 0x12;
const COLOR_OFFSET: u8 = 0x24;

const ADDRESS: u16 = 0x74;

/// A Scroll pHAT HD device connected over I2C bus (e.g. on a Raspberry Pi).
pub struct I2CDisplay {
    device: LinuxI2CDevice,
    frame: u8,
}

impl I2CDisplay {
    /// Creates a new I2CDisplay device using the I2C device identified by the provided
    /// `device_id` (normally 1 or 2).
    pub fn new(device_id: u8) -> I2CDisplay {
        let device_path = format!("/dev/i2c-{}", device_id);
        let d = LinuxI2CDevice::new(device_path, ADDRESS).unwrap();
        let mut display = I2CDisplay {
            device: d,
            frame: 0,
        };

        display.init_display().unwrap();

        display
    }

    fn bank(&mut self, bank: u8) -> Result<(), LinuxI2CError> {
        self.write_data(BANK_ADDRESS, &[bank])
    }

    fn register(&mut self, bank: u8, register: u8, value: u8) -> Result<(), LinuxI2CError> {
        self.bank(bank)?;
        self.write_data(register, &[value])
    }

    fn frame(&mut self, frame: u8) -> Result<(), LinuxI2CError> {
        self.register(CONFIG_BANK, FRAME_REGISTER, frame)
    }

    fn write_data(&mut self, base_address: u8, data: &[u8]) -> Result<(), LinuxI2CError> {
        let mut offset = 0;
        for chunk in data.chunks(32) {
            self.device.smbus_process_block(base_address + offset, chunk)?;
            offset += 32;
        }

        Ok(())
    }

    fn reset_display(&mut self) -> Result<(), LinuxI2CError> {
        self.sleep(true)?;
        std::thread::sleep(std::time::Duration::from_millis(10));
        self.sleep(false)?;
        Ok(())
    }

    fn init_display(&mut self) -> Result<(), LinuxI2CError> {
        self.reset_display()?;

        // Switch to Picture Mode.
        self.register(CONFIG_BANK, MODE_REGISTER, PICTURE_MODE)?;

        // Disable audio sync.
        self.register(CONFIG_BANK, AUDIOSYNC_REGISTER, 0)?;

        // Initialize frames 0 and 1.
        for frame in 0..2 {
            self.write_data(BANK_ADDRESS, &[frame])?;

            // Turn off blinking for all LEDs.
            self.write_data(BLINK_OFFSET, &[0; LED_COLUMNS * LED_ROWS])?;

            // Set the PWM duty cycle for all LEDs to 0%.
            self.write_data(COLOR_OFFSET, &[0; LED_COLUMNS * LED_ROWS])?;

            // Turn all LEDs "on".
            self.write_data(ENABLE_OFFSET, &[127; LED_COLUMNS * LED_ROWS])?;
        }

        Ok(())
    }

    fn sleep(&mut self, value: bool) -> Result<(), LinuxI2CError> {
        self.register(CONFIG_BANK, SHUTDOWN_REGISTER, if value { 0 } else { 1 })
    }
}

impl Display for I2CDisplay {
    fn show(&mut self, buffer: &[Column]) -> Result<(), Error> {
        // Double buffering with frames 0 and 1.
        let new_frame = (self.frame + 1) % 2;
        self.bank(new_frame)?;
        for y in 0..DISPLAY_HEIGHT {
            for x in 0..DISPLAY_WIDTH {
                let offset = if x >= 8 {
                    (x - 8) * 16 + y
                } else {
                    (8 - x) * 16 - (y + 2)
                };
                let value = match buffer.get(x as usize) {
                    Some(column) => column[y as usize],
                    None => 0,
                };
                self.write_data(COLOR_OFFSET + offset as u8, &[value])?;
            }
        }
        self.frame(new_frame)?;
        self.frame = new_frame;
        Ok(())
    }
}
<commit_msg>Do chunking without mutability<commit_after>#![allow(dead_code)]

extern crate i2cdev;

use super::*;

use self::i2cdev::core::I2CDevice;
use self::i2cdev::linux::{LinuxI2CDevice, LinuxI2CError};

const MODE_REGISTER: u8 = 0x00;
const FRAME_REGISTER: u8 = 0x01;
const AUTOPLAY1_REGISTER: u8 = 0x02;
const AUTOPLAY2_REGISTER: u8 = 0x03;
const BLINK_REGISTER: u8 = 0x05;
const AUDIOSYNC_REGISTER: u8 = 0x06;
const BREATH1_REGISTER: u8 = 0x08;
const BREATH2_REGISTER: u8 = 0x09;
const SHUTDOWN_REGISTER: u8 = 0x0A;
const GAIN_REGISTER: u8 = 0x0B;
const ADC_REGISTER: u8 = 0x0C;

const CONFIG_BANK: u8 = 0x0B;
const BANK_ADDRESS: u8 = 0xFD;

const PICTURE_MODE: u8 = 0x00;
const AUTOPLAY_MODE: u8 = 0x08;
const AUDIOPLAY_MODE: u8 = 0x18;

const ENABLE_OFFSET: u8 = 0x00;
const BLINK_OFFSET: u8 = 0x12;
const COLOR_OFFSET: u8 = 0x24;

const ADDRESS: u16 = 0x74;

/// A Scroll pHAT HD device connected over I2C bus (e.g. on a Raspberry Pi).
pub struct I2CDisplay {
    device: LinuxI2CDevice,
    frame: u8,
}

impl I2CDisplay {
    /// Creates a new I2CDisplay device using the I2C device identified by the provided
    /// `device_id` (normally 1 or 2).
    pub fn new(device_id: u8) -> I2CDisplay {
        let device_path = format!("/dev/i2c-{}", device_id);
        let d = LinuxI2CDevice::new(device_path, ADDRESS).unwrap();
        let mut display = I2CDisplay {
            device: d,
            frame: 0,
        };

        display.init_display().unwrap();

        display
    }

    fn bank(&mut self, bank: u8) -> Result<(), LinuxI2CError> {
        self.write_data(BANK_ADDRESS, &[bank])
    }

    fn register(&mut self, bank: u8, register: u8, value: u8) -> Result<(), LinuxI2CError> {
        self.bank(bank)?;
        self.write_data(register, &[value])
    }

    fn frame(&mut self, frame: u8) -> Result<(), LinuxI2CError> {
        self.register(CONFIG_BANK, FRAME_REGISTER, frame)
    }

    fn write_data(&mut self, base_address: u8, data: &[u8]) -> Result<(), LinuxI2CError> {
        const CHUNK_SIZE: usize = 32;
        for (i, chunk) in data.chunks(CHUNK_SIZE).enumerate() {
            self.device
                .smbus_process_block(base_address + (i * CHUNK_SIZE) as u8, chunk)?;
        }
        Ok(())
    }

    fn reset_display(&mut self) -> Result<(), LinuxI2CError> {
        self.sleep(true)?;
        std::thread::sleep(std::time::Duration::from_millis(10));
        self.sleep(false)?;
        Ok(())
    }

    fn init_display(&mut self) -> Result<(), LinuxI2CError> {
        self.reset_display()?;

        // Switch to Picture Mode.
        self.register(CONFIG_BANK, MODE_REGISTER, PICTURE_MODE)?;

        // Disable audio sync.
        self.register(CONFIG_BANK, AUDIOSYNC_REGISTER, 0)?;

        // Initialize frames 0 and 1.
        for frame in 0..2 {
            self.write_data(BANK_ADDRESS, &[frame])?;

            // Turn off blinking for all LEDs.
            self.write_data(BLINK_OFFSET, &[0; LED_COLUMNS * LED_ROWS])?;

            // Set the PWM duty cycle for all LEDs to 0%.
            self.write_data(COLOR_OFFSET, &[0; LED_COLUMNS * LED_ROWS])?;

            // Turn all LEDs "on".
            self.write_data(ENABLE_OFFSET, &[127; LED_COLUMNS * LED_ROWS])?;
        }

        Ok(())
    }

    fn sleep(&mut self, value: bool) -> Result<(), LinuxI2CError> {
        self.register(CONFIG_BANK, SHUTDOWN_REGISTER, if value { 0 } else { 1 })
    }
}

impl Display for I2CDisplay {
    fn show(&mut self, buffer: &[Column]) -> Result<(), Error> {
        // Double buffering with frames 0 and 1.
        let new_frame = (self.frame + 1) % 2;
        self.bank(new_frame)?;
        for y in 0..DISPLAY_HEIGHT {
            for x in 0..DISPLAY_WIDTH {
                let offset = if x >= 8 {
                    (x - 8) * 16 + y
                } else {
                    (8 - x) * 16 - (y + 2)
                };
                let value = match buffer.get(x as usize) {
                    Some(column) => column[y as usize],
                    None => 0,
                };
                self.write_data(COLOR_OFFSET + offset as u8, &[value])?;
            }
        }
        self.frame(new_frame)?;
        self.frame = new_frame;
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::cmp::{min, max};

use super::frame::Frame;

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct RGB {
   pub r: u8,
   pub g: u8,
   pub b: u8
}

impl RGB {
   pub fn new(r: u8, g: u8, b: u8) -> Self {
      RGB {
         r: r,
         g: g,
         b: b
      }
   }
}

pub fn bresenham(frame: &mut Frame, x1: i32, y1: i32, x2: i32, y2: i32, color: &RGB) {
   let dx = x2 - x1;
   let dy = y2 - y1;

   let ix = ((dx > 0) as i32) - ((dx < 0) as i32);
   let iy = ((dy > 0) as i32) - ((dy < 0) as i32);

   let dabs2x = dx.abs() * 2;
   let dabs2y = dy.abs() * 2;

   let (mut x, mut y) = (x1, y1);

   frame.put_pixel(x, y, color);

   if dabs2x >= dabs2y {
      let mut error = dabs2y - (dabs2x / 2);

      while x != x2 {
         if error >= 0 && ((error != 0) || (ix > 0)) {
            error -= dabs2x;
            y += iy;
         }

         error += dabs2y;
         x += ix;

         frame.put_pixel(x, y, color);
      }
   } else {
      let mut error = dabs2x - (dabs2y / 2);

      while y != y2 {
         if error >= 0 && ((error != 0) || (iy > 0)) {
            error -= dabs2y;
            x += ix;
         }

         error += dabs2x;
         y += iy;

         frame.put_pixel(x, y, color);
      }
   }
}

pub fn hline(frame: &mut Frame, x1: i32, x2: i32, y: i32, color: &RGB) {
   let min_x = min(x1, x2);
   let max_x = max(x1, x2);

   for x in min_x..max_x + 1 {
      frame.put_pixel(x, y, color);
   }
}

pub fn vline(frame: &mut Frame, x: i32, y1: i32, y2: i32, color: &RGB) {
   let min_y = min(y1, y2);
   let max_y = max(y1, y2);

   for y in min_y..max_y + 1 {
      frame.put_pixel(x, y, color);
   }
}
<commit_msg>Default implementation for the RGB<commit_after>use std::cmp::{min, max};

use super::frame::Frame;

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct RGB {
   pub r: u8,
   pub g: u8,
   pub b: u8
}

impl RGB {
   pub fn new(r: u8, g: u8, b: u8) -> Self {
      RGB {
         r: r,
         g: g,
         b: b
      }
   }
}

impl Default for RGB {
   fn default() -> RGB {
      RGB::new(0, 0, 0)
   }
}

pub fn bresenham(frame: &mut Frame, x1: i32, y1: i32, x2: i32, y2: i32, color: &RGB) {
   let dx = x2 - x1;
   let dy = y2 - y1;

   let ix = ((dx > 0) as i32) - ((dx < 0) as i32);
   let iy = ((dy > 0) as i32) - ((dy < 0) as i32);

   let dabs2x = dx.abs() * 2;
   let dabs2y = dy.abs() * 2;

   let (mut x, mut y) = (x1, y1);

   frame.put_pixel(x, y, color);

   if dabs2x >= dabs2y {
      let mut error = dabs2y - (dabs2x / 2);

      while x != x2 {
         if error >= 0 && ((error != 0) || (ix > 0)) {
            error -= dabs2x;
            y += iy;
         }

         error += dabs2y;
         x += ix;

         frame.put_pixel(x, y, color);
      }
   } else {
      let mut error = dabs2x - (dabs2y / 2);

      while y != y2 {
         if error >= 0 && ((error != 0) || (iy > 0)) {
            error -= dabs2y;
            x += ix;
         }

         error += dabs2x;
         y += iy;

         frame.put_pixel(x, y, color);
      }
   }
}

pub fn hline(frame: &mut Frame, x1: i32, x2: i32, y: i32, color: &RGB) {
   let min_x = min(x1, x2);
   let max_x = max(x1, x2);

   for x in min_x..max_x + 1 {
      frame.put_pixel(x, y, color);
   }
}

pub fn vline(frame: &mut Frame, x: i32, y1: i32, y2: i32, color: &RGB) {
   let min_y = min(y1, y2);
   let max_y = max(y1, y2);

   for y in min_y..max_y + 1 {
      frame.put_pixel(x, y, color);
   }
}
<|endoftext|>


<commit_before>//! API wrappers for common use cases like highlighting strings and
//! files without caring about intermediate semantic representation
//! and caching.

use crate::parsing::{ScopeStack, ParseState, SyntaxReference, SyntaxSet, ScopeStackOp};
use crate::highlighting::{Highlighter, HighlightState, HighlightIterator, Theme, Style};
use std::io::{self, BufReader};
use std::fs::File;
use std::path::Path;
// use util::debug_print_ops;

/// Simple way to go directly from lines of text to colored tokens.
///
/// Depending on how you load the syntaxes (see the [`SyntaxSet`] docs), this can either take
/// strings with trailing `\n`s or without.
///
/// [`SyntaxSet`]: ../parsing/struct.SyntaxSet.html
///
/// # Examples
///
/// Prints colored lines of a string to the terminal
///
/// ```
/// use syntect::easy::HighlightLines;
/// use syntect::parsing::SyntaxSet;
/// use syntect::highlighting::{ThemeSet, Style};
/// use syntect::util::{as_24_bit_terminal_escaped, LinesWithEndings};
///
/// // Load these once at the start of your program
/// let ps = SyntaxSet::load_defaults_newlines();
/// let ts = ThemeSet::load_defaults();
///
/// let syntax = ps.find_syntax_by_extension("rs").unwrap();
/// let mut h = HighlightLines::new(syntax, &ts.themes["base16-ocean.dark"]);
/// let s = "pub struct Wow { hi: u64 }\nfn blah() -> u64 {}";
/// for line in LinesWithEndings::from(s) { // LinesWithEndings enables use of newlines mode
///     let ranges: Vec<(Style, &str)> = h.highlight(line, &ps);
///     let escaped = as_24_bit_terminal_escaped(&ranges[..], true);
///     print!("{}", escaped);
/// }
/// ```
pub struct HighlightLines<'a> {
    highlighter: Highlighter<'a>,
    parse_state: ParseState,
    highlight_state: HighlightState,
}

impl<'a> HighlightLines<'a> {
    pub fn new(syntax: &SyntaxReference, theme: &'a Theme) -> HighlightLines<'a> {
        let highlighter = Highlighter::new(theme);
        let highlight_state = HighlightState::new(&highlighter, ScopeStack::new());
        HighlightLines {
            highlighter,
            parse_state: ParseState::new(syntax),
            highlight_state,
        }
    }

    /// Highlights a line of a file
    pub fn highlight<'b>(&mut self, line: &'b str, syntax_set: &SyntaxSet) -> Vec<(Style, &'b str)> {
        // println!("{}", self.highlight_state.path);
        let ops = self.parse_state.parse_line(line, syntax_set);
        // use util::debug_print_ops;
        // debug_print_ops(line, &ops);
        let iter =
            HighlightIterator::new(&mut self.highlight_state, &ops[..], line, &self.highlighter);
        iter.collect()
    }
}

/// Convenience struct containing everything you need to highlight a file
///
/// Use the `reader` to get the lines of the file and the `highlight_lines` to highlight them. See
/// the [`new`] method docs for more information.
///
/// [`new`]: #method.new
pub struct HighlightFile<'a> {
    pub reader: BufReader<File>,
    pub highlight_lines: HighlightLines<'a>,
}

impl<'a> HighlightFile<'a> {
    /// Constructs a file reader and a line highlighter to get you reading files as fast as possible.
    ///
    /// This auto-detects the syntax from the extension and constructs a [`HighlightLines`] with the
    /// correct syntax and theme.
    ///
    /// [`HighlightLines`]: struct.HighlightLines.html
    ///
    /// # Examples
    ///
    /// Using the `newlines` mode is a bit involved but yields more robust and glitch-free highlighting,
    /// as well as being slightly faster since it can re-use a line buffer.
    ///
    /// ```
    /// use syntect::parsing::SyntaxSet;
    /// use syntect::highlighting::{ThemeSet, Style};
    /// use syntect::util::as_24_bit_terminal_escaped;
    /// use syntect::easy::HighlightFile;
    /// use std::io::BufRead;
    ///
    /// # use std::io;
    /// # fn foo() -> io::Result<()> {
    /// let ss = SyntaxSet::load_defaults_newlines();
    /// let ts = ThemeSet::load_defaults();
    ///
    /// let mut highlighter = HighlightFile::new("testdata/highlight_test.erb", &ss, &ts.themes["base16-ocean.dark"]).unwrap();
    /// let mut line = String::new();
    /// while highlighter.reader.read_line(&mut line)? > 0 {
    ///     {
    ///         let regions: Vec<(Style, &str)> = highlighter.highlight_lines.highlight(&line, &ss);
    ///         print!("{}", as_24_bit_terminal_escaped(&regions[..], true));
    ///     } // until NLL this scope is needed so we can clear the buffer after
    ///     line.clear(); // read_line appends so we need to clear between lines
    /// }
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// This example uses `reader.lines()` to get lines without a newline character, it's simpler but may break on rare tricky cases.
    ///
    /// ```
    /// use syntect::parsing::SyntaxSet;
    /// use syntect::highlighting::{ThemeSet, Style};
    /// use syntect::util::as_24_bit_terminal_escaped;
    /// use syntect::easy::HighlightFile;
    /// use std::io::BufRead;
    ///
    /// let ss = SyntaxSet::load_defaults_nonewlines();
    /// let ts = ThemeSet::load_defaults();
    ///
    /// let mut highlighter = HighlightFile::new("testdata/highlight_test.erb", &ss, &ts.themes["base16-ocean.dark"]).unwrap();
    /// for maybe_line in highlighter.reader.lines() {
    ///     let line = maybe_line.unwrap();
    ///     let regions: Vec<(Style, &str)> = highlighter.highlight_lines.highlight(&line, &ss);
    ///     println!("{}", as_24_bit_terminal_escaped(&regions[..], true));
    /// }
    /// ```
    pub fn new<P: AsRef<Path>>(path_obj: P,
                               ss: &SyntaxSet,
                               theme: &'a Theme)
                               -> io::Result<HighlightFile<'a>> {
        let path: &Path = path_obj.as_ref();
        let f = File::open(path)?;
        let syntax = ss.find_syntax_for_file(path)?
            .unwrap_or_else(|| ss.find_syntax_plain_text());

        Ok(HighlightFile {
            reader: BufReader::new(f),
            highlight_lines: HighlightLines::new(syntax, theme),
        })
    }
}

/// Iterator over the ranges of a line which a given the operation from the parser applies.
///
/// Use [`ScopeRegionIterator`] to obtain directly regions (`&str`s) from the line.
///
/// To use, just keep your own [`ScopeStack`] and then `ScopeStack.apply(op)` the operation that is
/// yielded at the top of your `for` loop over this iterator. Now you have a substring of the line
/// and the scope stack for that token.
///
/// See the `synstats.rs` example for an example of using this iterator.
///
/// **Note:** This will often return empty ranges, just `continue` after applying the op if you
/// don't want them.
///
/// [`ScopeStack`]: ../parsing/struct.ScopeStack.html
/// [`ScopeRegionIterator`]: ./struct.ScopeRegionIterator.html
#[derive(Debug)]
pub struct ScopeRangeIterator<'a> {
    ops: &'a [(usize, ScopeStackOp)],
    line: &'a str,
    index: usize,
    last_str_index: usize,
}

impl<'a> ScopeRangeIterator<'a> {
    pub fn new(ops: &'a [(usize, ScopeStackOp)], line: &'a str) -> ScopeRangeIterator<'a> {
        ScopeRangeIterator {
            ops,
            line,
            index: 0,
            last_str_index: 0,
        }
    }
}

static NOOP_OP: ScopeStackOp = ScopeStackOp::Noop;

impl<'a> Iterator for ScopeRangeIterator<'a> {
    type Item = (std::ops::Range<usize>, &'a ScopeStackOp);
    fn next(&mut self) -> Option<Self::Item> {
        if self.index > self.ops.len() {
            return None;
        }

        // region extends up to next operation (ops[index]) or string end if there is none
        // note the next operation may be at, last_str_index, in which case the region is empty
        let next_str_i = if self.index == self.ops.len() {
            self.line.len()
        } else {
            self.ops[self.index].0
        };
        let range = self.last_str_index..next_str_i;
        self.last_str_index = next_str_i;

        // the first region covers everything before the first op, which may be empty
        let op = if self.index == 0 {
            &NOOP_OP
        } else {
            &self.ops[self.index - 1].1
        };

        self.index += 1;
        Some((range, op))
    }
}

/// A convenience wrapper over [`ScopeRangeIterator`] to return `&str`s directly.
///
/// To use, just keep your own [`ScopeStack`] and then `ScopeStack.apply(op)` the operation that is
/// yielded at the top of your `for` loop over this iterator. Now you have a substring of the line
/// and the scope stack for that token.
///
/// See the `synstats.rs` example for an example of using this iterator.
///
/// **Note:** This will often return empty regions, just `continue` after applying the op if you
/// don't want them.
///
/// [`ScopeStack`]: ../parsing/struct.ScopeStack.html
/// [`ScopeRangeIterator`]: ./struct.ScopeRangeIterator.html
#[derive(Debug)]
pub struct ScopeRegionIterator<'a> {
    range_iter: ScopeRangeIterator<'a>,
}

impl<'a> ScopeRegionIterator<'a> {
    pub fn new(ops: &'a [(usize, ScopeStackOp)], line: &'a str) -> ScopeRegionIterator<'a> {
        ScopeRegionIterator {
            range_iter: ScopeRangeIterator::new(ops, line),
        }
    }
}

impl<'a> Iterator for ScopeRegionIterator<'a> {
    type Item = (&'a str, &'a ScopeStackOp);
    fn next(&mut self) -> Option<Self::Item> {
        let (range, op) = self.range_iter.next()?;
        Some((&self.range_iter.line[range], op))
    }
}

#[cfg(all(feature = "default-syntaxes", feature = "default-themes"))]
#[cfg(test)]
mod tests {
    use super::*;
    use crate::parsing::{SyntaxSet, ParseState, ScopeStack};
    use crate::highlighting::ThemeSet;
    use std::str::FromStr;

    #[test]
    fn can_highlight_lines() {
        let ss = SyntaxSet::load_defaults_nonewlines();
        let ts = ThemeSet::load_defaults();
        let syntax = ss.find_syntax_by_extension("rs").unwrap();
        let mut h = HighlightLines::new(syntax, &ts.themes["base16-ocean.dark"]);
        let ranges = h.highlight("pub struct Wow { hi: u64 }", &ss);
        assert!(ranges.len() > 4);
    }

    #[test]
    fn can_highlight_file() {
        let ss = SyntaxSet::load_defaults_nonewlines();
        let ts = ThemeSet::load_defaults();
        HighlightFile::new("testdata/highlight_test.erb",
                           &ss,
                           &ts.themes["base16-ocean.dark"])
            .unwrap();
    }

    #[test]
    fn can_find_regions() {
        let ss = SyntaxSet::load_defaults_nonewlines();
        let mut state = ParseState::new(ss.find_syntax_by_extension("rb").unwrap());
        let line = "lol =5+2";
        let ops = state.parse_line(line, &ss);

        let mut stack = ScopeStack::new();
        let mut token_count = 0;
        for (s, op) in ScopeRegionIterator::new(&ops, line) {
            stack.apply(op);
            if s.is_empty() { // in this case we don't care about blank tokens
                continue;
            }
            if token_count == 1 {
                assert_eq!(stack, ScopeStack::from_str("source.ruby keyword.operator.assignment.ruby").unwrap());
                assert_eq!(s, "=");
            }
            token_count += 1;
            println!("{:?} {}", s, stack);
        }
        assert_eq!(token_count, 5);
    }

    #[test]
    fn can_find_regions_with_trailing_newline() {
        let ss = SyntaxSet::load_defaults_newlines();
        let mut state = ParseState::new(ss.find_syntax_by_extension("rb").unwrap());
        let lines = ["# hello world\n", "lol=5+2\n"];
        let mut stack = ScopeStack::new();

        for line in lines.iter() {
            let ops = state.parse_line(line, &ss);
            println!("{:?}", ops);

            let mut iterated_ops: Vec<&ScopeStackOp> = Vec::new();
            for (_, op) in ScopeRegionIterator::new(&ops, line) {
                stack.apply(op);
                iterated_ops.push(op);
                println!("{:?}", op);
            }

            let all_ops = ops.iter().map(|t| &t.1);
            assert_eq!(all_ops.count(), iterated_ops.len() - 1); // -1 because we want to ignore the NOOP
        }
    }
}
<commit_msg>src/easy.rs: Enable testing `default-syntaxes` on its own<commit_after>//! API wrappers for common use cases like highlighting strings and
//! files without caring about intermediate semantic representation
//! and caching.

use crate::parsing::{ScopeStack, ParseState, SyntaxReference, SyntaxSet, ScopeStackOp};
use crate::highlighting::{Highlighter, HighlightState, HighlightIterator, Theme, Style};
use std::io::{self, BufReader};
use std::fs::File;
use std::path::Path;
// use util::debug_print_ops;

/// Simple way to go directly from lines of text to colored tokens.
///
/// Depending on how you load the syntaxes (see the [`SyntaxSet`] docs), this can either take
/// strings with trailing `\n`s or without.
///
/// [`SyntaxSet`]: ../parsing/struct.SyntaxSet.html
///
/// # Examples
///
/// Prints colored lines of a string to the terminal
///
/// ```
/// use syntect::easy::HighlightLines;
/// use syntect::parsing::SyntaxSet;
/// use syntect::highlighting::{ThemeSet, Style};
/// use syntect::util::{as_24_bit_terminal_escaped, LinesWithEndings};
///
/// // Load these once at the start of your program
/// let ps = SyntaxSet::load_defaults_newlines();
/// let ts = ThemeSet::load_defaults();
///
/// let syntax = ps.find_syntax_by_extension("rs").unwrap();
/// let mut h = HighlightLines::new(syntax, &ts.themes["base16-ocean.dark"]);
/// let s = "pub struct Wow { hi: u64 }\nfn blah() -> u64 {}";
/// for line in LinesWithEndings::from(s) { // LinesWithEndings enables use of newlines mode
///     let ranges: Vec<(Style, &str)> = h.highlight(line, &ps);
///     let escaped = as_24_bit_terminal_escaped(&ranges[..], true);
///     print!("{}", escaped);
/// }
/// ```
pub struct HighlightLines<'a> {
    highlighter: Highlighter<'a>,
    parse_state: ParseState,
    highlight_state: HighlightState,
}

impl<'a> HighlightLines<'a> {
    pub fn new(syntax: &SyntaxReference, theme: &'a Theme) -> HighlightLines<'a> {
        let highlighter = Highlighter::new(theme);
        let highlight_state = HighlightState::new(&highlighter, ScopeStack::new());
        HighlightLines {
            highlighter,
            parse_state: ParseState::new(syntax),
            highlight_state,
        }
    }

    /// Highlights a line of a file
    pub fn highlight<'b>(&mut self, line: &'b str, syntax_set: &SyntaxSet) -> Vec<(Style, &'b str)> {
        // println!("{}", self.highlight_state.path);
        let ops = self.parse_state.parse_line(line, syntax_set);
        // use util::debug_print_ops;
        // debug_print_ops(line, &ops);
        let iter =
            HighlightIterator::new(&mut self.highlight_state, &ops[..], line, &self.highlighter);
        iter.collect()
    }
}

/// Convenience struct containing everything you need to highlight a file
///
/// Use the `reader` to get the lines of the file and the `highlight_lines` to highlight them. See
/// the [`new`] method docs for more information.
///
/// [`new`]: #method.new
pub struct HighlightFile<'a> {
    pub reader: BufReader<File>,
    pub highlight_lines: HighlightLines<'a>,
}

impl<'a> HighlightFile<'a> {
    /// Constructs a file reader and a line highlighter to get you reading files as fast as possible.
    ///
    /// This auto-detects the syntax from the extension and constructs a [`HighlightLines`] with the
    /// correct syntax and theme.
    ///
    /// [`HighlightLines`]: struct.HighlightLines.html
    ///
    /// # Examples
    ///
    /// Using the `newlines` mode is a bit involved but yields more robust and glitch-free highlighting,
    /// as well as being slightly faster since it can re-use a line buffer.
    ///
    /// ```
    /// use syntect::parsing::SyntaxSet;
    /// use syntect::highlighting::{ThemeSet, Style};
    /// use syntect::util::as_24_bit_terminal_escaped;
    /// use syntect::easy::HighlightFile;
    /// use std::io::BufRead;
    ///
    /// # use std::io;
    /// # fn foo() -> io::Result<()> {
    /// let ss = SyntaxSet::load_defaults_newlines();
    /// let ts = ThemeSet::load_defaults();
    ///
    /// let mut highlighter = HighlightFile::new("testdata/highlight_test.erb", &ss, &ts.themes["base16-ocean.dark"]).unwrap();
    /// let mut line = String::new();
    /// while highlighter.reader.read_line(&mut line)? > 0 {
    ///     {
    ///         let regions: Vec<(Style, &str)> = highlighter.highlight_lines.highlight(&line, &ss);
    ///         print!("{}", as_24_bit_terminal_escaped(&regions[..], true));
    ///     } // until NLL this scope is needed so we can clear the buffer after
    ///     line.clear(); // read_line appends so we need to clear between lines
    /// }
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// This example uses `reader.lines()` to get lines without a newline character, it's simpler but may break on rare tricky cases.
    ///
    /// ```
    /// use syntect::parsing::SyntaxSet;
    /// use syntect::highlighting::{ThemeSet, Style};
    /// use syntect::util::as_24_bit_terminal_escaped;
    /// use syntect::easy::HighlightFile;
    /// use std::io::BufRead;
    ///
    /// let ss = SyntaxSet::load_defaults_nonewlines();
    /// let ts = ThemeSet::load_defaults();
    ///
    /// let mut highlighter = HighlightFile::new("testdata/highlight_test.erb", &ss, &ts.themes["base16-ocean.dark"]).unwrap();
    /// for maybe_line in highlighter.reader.lines() {
    ///     let line = maybe_line.unwrap();
    ///     let regions: Vec<(Style, &str)> = highlighter.highlight_lines.highlight(&line, &ss);
    ///     println!("{}", as_24_bit_terminal_escaped(&regions[..], true));
    /// }
    /// ```
    pub fn new<P: AsRef<Path>>(path_obj: P,
                               ss: &SyntaxSet,
                               theme: &'a Theme)
                               -> io::Result<HighlightFile<'a>> {
        let path: &Path = path_obj.as_ref();
        let f = File::open(path)?;
        let syntax = ss.find_syntax_for_file(path)?
            .unwrap_or_else(|| ss.find_syntax_plain_text());

        Ok(HighlightFile {
            reader: BufReader::new(f),
            highlight_lines: HighlightLines::new(syntax, theme),
        })
    }
}

/// Iterator over the ranges of a line which a given the operation from the parser applies.
///
/// Use [`ScopeRegionIterator`] to obtain directly regions (`&str`s) from the line.
///
/// To use, just keep your own [`ScopeStack`] and then `ScopeStack.apply(op)` the operation that is
/// yielded at the top of your `for` loop over this iterator. Now you have a substring of the line
/// and the scope stack for that token.
///
/// See the `synstats.rs` example for an example of using this iterator.
///
/// **Note:** This will often return empty ranges, just `continue` after applying the op if you
/// don't want them.
///
/// [`ScopeStack`]: ../parsing/struct.ScopeStack.html
/// [`ScopeRegionIterator`]: ./struct.ScopeRegionIterator.html
#[derive(Debug)]
pub struct ScopeRangeIterator<'a> {
    ops: &'a [(usize, ScopeStackOp)],
    line: &'a str,
    index: usize,
    last_str_index: usize,
}

impl<'a> ScopeRangeIterator<'a> {
    pub fn new(ops: &'a [(usize, ScopeStackOp)], line: &'a str) -> ScopeRangeIterator<'a> {
        ScopeRangeIterator {
            ops,
            line,
            index: 0,
            last_str_index: 0,
        }
    }
}

static NOOP_OP: ScopeStackOp = ScopeStackOp::Noop;

impl<'a> Iterator for ScopeRangeIterator<'a> {
    type Item = (std::ops::Range<usize>, &'a ScopeStackOp);
    fn next(&mut self) -> Option<Self::Item> {
        if self.index > self.ops.len() {
            return None;
        }

        // region extends up to next operation (ops[index]) or string end if there is none
        // note the next operation may be at, last_str_index, in which case the region is empty
        let next_str_i = if self.index == self.ops.len() {
            self.line.len()
        } else {
            self.ops[self.index].0
        };
        let range = self.last_str_index..next_str_i;
        self.last_str_index = next_str_i;

        // the first region covers everything before the first op, which may be empty
        let op = if self.index == 0 {
            &NOOP_OP
        } else {
            &self.ops[self.index - 1].1
        };

        self.index += 1;
        Some((range, op))
    }
}

/// A convenience wrapper over [`ScopeRangeIterator`] to return `&str`s directly.
///
/// To use, just keep your own [`ScopeStack`] and then `ScopeStack.apply(op)` the operation that is
/// yielded at the top of your `for` loop over this iterator. Now you have a substring of the line
/// and the scope stack for that token.
///
/// See the `synstats.rs` example for an example of using this iterator.
///
/// **Note:** This will often return empty regions, just `continue` after applying the op if you
/// don't want them.
///
/// [`ScopeStack`]: ../parsing/struct.ScopeStack.html
/// [`ScopeRangeIterator`]: ./struct.ScopeRangeIterator.html
#[derive(Debug)]
pub struct ScopeRegionIterator<'a> {
    range_iter: ScopeRangeIterator<'a>,
}

impl<'a> ScopeRegionIterator<'a> {
    pub fn new(ops: &'a [(usize, ScopeStackOp)], line: &'a str) -> ScopeRegionIterator<'a> {
        ScopeRegionIterator {
            range_iter: ScopeRangeIterator::new(ops, line),
        }
    }
}

impl<'a> Iterator for ScopeRegionIterator<'a> {
    type Item = (&'a str, &'a ScopeStackOp);
    fn next(&mut self) -> Option<Self::Item> {
        let (range, op) = self.range_iter.next()?;
        Some((&self.range_iter.line[range], op))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::parsing::{SyntaxSet, ParseState, ScopeStack};
    #[cfg(feature = "default-themes")]
    use crate::highlighting::ThemeSet;
    use std::str::FromStr;

    #[cfg(all(feature = "default-syntaxes", feature = "default-themes"))]
    #[test]
    fn can_highlight_lines() {
        let ss = SyntaxSet::load_defaults_nonewlines();
        let ts = ThemeSet::load_defaults();
        let syntax = ss.find_syntax_by_extension("rs").unwrap();
        let mut h = HighlightLines::new(syntax, &ts.themes["base16-ocean.dark"]);
        let ranges = h.highlight("pub struct Wow { hi: u64 }", &ss);
        assert!(ranges.len() > 4);
    }

    #[cfg(all(feature = "default-syntaxes", feature = "default-themes"))]
    #[test]
    fn can_highlight_file() {
        let ss = SyntaxSet::load_defaults_nonewlines();
        let ts = ThemeSet::load_defaults();
        HighlightFile::new("testdata/highlight_test.erb",
                           &ss,
                           &ts.themes["base16-ocean.dark"])
            .unwrap();
    }

    #[cfg(feature = "default-syntaxes")]
    #[test]
    fn can_find_regions() {
        let ss = SyntaxSet::load_defaults_nonewlines();
        let mut state = ParseState::new(ss.find_syntax_by_extension("rb").unwrap());
        let line = "lol =5+2";
        let ops = state.parse_line(line, &ss);

        let mut stack = ScopeStack::new();
        let mut token_count = 0;
        for (s, op) in ScopeRegionIterator::new(&ops, line) {
            stack.apply(op);
            if s.is_empty() { // in this case we don't care about blank tokens
                continue;
            }
            if token_count == 1 {
                assert_eq!(stack, ScopeStack::from_str("source.ruby keyword.operator.assignment.ruby").unwrap());
                assert_eq!(s, "=");
            }
            token_count += 1;
            println!("{:?} {}", s, stack);
        }
        assert_eq!(token_count, 5);
    }

    #[cfg(feature = "default-syntaxes")]
    #[test]
    fn can_find_regions_with_trailing_newline() {
        let ss = SyntaxSet::load_defaults_newlines();
        let mut state = ParseState::new(ss.find_syntax_by_extension("rb").unwrap());
        let lines = ["# hello world\n", "lol=5+2\n"];
        let mut stack = ScopeStack::new();

        for line in lines.iter() {
            let ops = state.parse_line(line, &ss);
            println!("{:?}", ops);

            let mut iterated_ops: Vec<&ScopeStackOp> = Vec::new();
            for (_, op) in ScopeRegionIterator::new(&ops, line) {
                stack.apply(op);
                iterated_ops.push(op);
                println!("{:?}", op);
            }

            let all_ops = ops.iter().map(|t| &t.1);
            assert_eq!(all_ops.count(), iterated_ops.len() - 1); // -1 because we want to ignore the NOOP
        }
    }
}
<|endoftext|>


<commit_before>pub mod symbol_table;
use std::collections::HashMap;

pub trait StaticSymbolTable<'a>
{
	/// Return all symbols in this table with the specified name (in any namespace)
	fn lookup_by_name(&self, name: &str) -> Vec<&Symbol<'a>>;

	/// Return all symbols in this table with the specified namespace
	fn lookup_by_namespace(&self, namespace: &str) -> Vec<&Symbol<'a>>;

	/// Lookup a variable by its name and namespace. Duplicate symbols are not allowed, so the result will be unique
	/// @return the specified symbol or None if the specified symbol is not in this namespace
	fn lookup_variable(&self, namespace: &str, name: &str) -> Option<&Symbol<'a>>;

	/// Lookup a function by its name and namespace. Functions with the same signature are not allowed, so the result will be unique
	/// If no result is found in the direct namespace, the parent namespaces will be searched
	/// @return the specified symbol or None if the specified symbol is not in this namespace or a parent namespace
	fn lookup_function(&self, namespace: &str, name: &str, argument_types: &Vec<String>) -> Option<&Symbol<'a>>;

	/// Lookup a structure (class, enum) by its name and namespace.
	/// Duplicate classes in the same namespace are not allowed, so the result will be unique
	/// If no result is found in the direct namespace, the parent namespaces will be searched
	/// @return the specified symbol or None if the specified symbol is not in this namespace or a parent namespace
	fn lookup_structure(&self, namespace: &str, name: &str) -> Option<(&Symbol<'a>)>;

	/// Adds a symbol to this table and allocates it's location
	/// Returns true if the symbol could be added; false otherwise
	/// Duplicate symbols are not allowed
	/// TODO: support overloaded methods
	fn add(&mut self, class: SymbolClass<'a>, namespace: &'a str, name: &'a str) -> bool;
}

pub enum SymbolLocation<'a>
{
	/// Indicates that a register has been reserved for a specific use (e.g. by a variable)
	/// tuple: (name)
	Register(&'a str),

	/// Indicates a location in memory (e.g. for static variables)
	/// tuple: (address)
	Memory(MemoryAddress<'a>),

	/// Indicates an offset location from a structured entity (e.g. a member variable of a class)
	/// tuple: offset
	InstancedMemory(u16),

	/// Indicates that the symbol should not be accessed, as it represents a structured entity
	Structured
}

pub enum SymbolClass<'a>
{
	/// (variable_type)
	Variable(&'a str),

	/// Function signature (return_type, argument_types) //TODO: support exceptions
	Function(&'a str, &'a Vec<&'a str>),

	/// Includes class, enum, and interface
	/// (subtype)
	Structure(&'a str),
}

pub struct Symbol<'a>
{
	/// Namespace of this symbol, without the final "." or the name of this symbol
	pub namespace: &'a str,

	pub is_static: bool,

	/// Identifier for the symbol (e.g. name of variable, function, class, etc. without it's namespace)
	pub name: &'a str,

	/// What this symbol represents (class, enum, variable, function, etc)
	pub symbol_class: SymbolClass<'a>,

	/// Memory location of this symbol. Methods will always map to a SymbolLocation::Memory label with a 0 offset
	pub location: SymbolLocation<'a>,
}

pub struct MemoryAddress<'a>
{
	/// Label marking the base address where the symbol is stored
	pub label_name: &'a str,

	/// Offset from the base address to access the symbol. Methods will always have an offset of 0
	pub offset: u16,
}
<commit_msg>Add Method Argument<commit_after>pub mod symbol_table;
use std::collections::HashMap;

pub trait StaticSymbolTable<'a>
{
	/// Return all symbols in this table with the specified name (in any namespace)
	fn lookup_by_name(&self, name: &str) -> Vec<&Symbol<'a>>;

	/// Return all symbols in this table with the specified namespace
	fn lookup_by_namespace(&self, namespace: &str) -> Vec<&Symbol<'a>>;

	/// Lookup a variable by its name and namespace. Duplicate symbols are not allowed, so the result will be unique
	/// @return the specified symbol or None if the specified symbol is not in this namespace
	fn lookup_variable(&self, namespace: &str, name: &str) -> Option<&Symbol<'a>>;

	/// Lookup a function by its name and namespace. Functions with the same signature are not allowed, so the result will be unique
	/// If no result is found in the direct namespace, the parent namespaces will be searched
	/// @return the specified symbol or None if the specified symbol is not in this namespace or a parent namespace
	fn lookup_function(&self, namespace: &str, name: &str, argument_types: &Vec<String>) -> Option<&Symbol<'a>>;

	/// Lookup a structure (class, enum) by its name and namespace.
	/// Duplicate classes in the same namespace are not allowed, so the result will be unique
	/// If no result is found in the direct namespace, the parent namespaces will be searched
	/// @return the specified symbol or None if the specified symbol is not in this namespace or a parent namespace
	fn lookup_structure(&self, namespace: &str, name: &str) -> Option<(&Symbol<'a>)>;

	/// Adds a symbol to this table and allocates it's location
	/// Returns true if the symbol could be added; false otherwise
	/// Duplicate symbols are not allowed
	/// TODO: support overloaded methods
	fn add(&mut self, class: SymbolClass<'a>, namespace: &'a str, name: &'a str) -> bool;
}

pub enum SymbolLocation<'a>
{
	/// Indicates that a register has been reserved for a specific use (e.g. by a variable)
	/// tuple: (name)
	Register(&'a str),

	/// Indicates a location in memory (e.g. for static variables)
	/// tuple: (address)
	Memory(MemoryAddress<'a>),

	/// Indicates an offset location from a structured entity (e.g. a member variable of a class)
	/// tuple: offset
	InstancedMemory(u16),

	MethodArgument(u16),

	/// Indicates that the symbol should not be accessed, as it represents a structured entity
	Structured
}

pub enum SymbolClass<'a>
{
	/// (variable_type)
	Variable(&'a str),

	/// Function signature (return_type, argument_types) //TODO: support exceptions
	Function(&'a str, &'a Vec<&'a str>),

	/// Includes class, enum, and interface
	/// (subtype)
	Structure(&'a str),
}

pub struct Symbol<'a>
{
	/// Namespace of this symbol, without the final "." or the name of this symbol
	pub namespace: &'a str,

	pub is_static: bool,

	/// Identifier for the symbol (e.g. name of variable, function, class, etc. without it's namespace)
	pub name: &'a str,

	/// What this symbol represents (class, enum, variable, function, etc)
	pub symbol_class: SymbolClass<'a>,

	/// Memory location of this symbol. Methods will always map to a SymbolLocation::Memory label with a 0 offset
	pub location: SymbolLocation<'a>,
}

pub struct MemoryAddress<'a>
{
	/// Label marking the base address where the symbol is stored
	pub label_name: &'a str,

	/// Offset from the base address to access the symbol. Methods will always have an offset of 0
	pub offset: u16,
}
<|endoftext|>