<commit_before>extern crate gl;
extern crate glfw;
extern crate time;

use gl::types::*;
use glfw::{Context, OpenGlProfileHint, WindowHint, WindowMode};
use std::mem::size_of;

macro_rules! gl_str {
    ($string_literal:expr) => (concat!($string_literal, '\0').as_bytes().as_ptr() as *const GLchar)
}

const VERTEX_SHADER_SOURCE: &'static str = "
    #version 150

    in vec2 position;
    in vec3 color;

    out vec3 Color;

    void main() {
        Color = color;
        gl_Position = vec4(position, 0.0, 1.0);
    }
";

const FRAGMENT_SHADER_SOURCE: &'static str = "
    #version 150

    in vec3 Color;

    out vec4 out_color;

    void main() {
        out_color = vec4(Color, 1.0);
    }
";

unsafe fn compile_shader(shader_type: GLenum, source: &str) -> Result<GLuint, String> {
    let shader = gl::CreateShader(shader_type);
    let source_ptr = source.as_bytes().as_ptr() as *const GLchar;
    let source_len = source.len() as GLint;
    gl::ShaderSource(shader, 1, &source_ptr, &source_len);
    gl::CompileShader(shader);

    let mut status = gl::FALSE as GLint;
    gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

    if status == gl::TRUE as GLint {
        Ok(shader)
    } else {
        let mut log_len = 0;
        gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut log_len);
        if log_len == 0 { return Err(String::new()) }

        let mut buf = Vec::with_capacity(log_len as usize);
        buf.set_len(log_len as usize - 1); // Subtract 1 to ignore the trailing null.
        gl::GetShaderInfoLog(shader, log_len, std::ptr::null_mut(),
        buf.as_mut_ptr() as *mut GLchar);

        Err(String::from_utf8_lossy(&buf).into_owned())
    }
}

fn main() {
    let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();

    glfw.window_hint(WindowHint::ContextVersion(3, 2));
    glfw.window_hint(WindowHint::OpenGlProfile(OpenGlProfileHint::Core));
    glfw.window_hint(WindowHint::OpenGlForwardCompat(true));
    glfw.window_hint(WindowHint::Resizable(false));

    let (mut window, events) = glfw.create_window(800, 600, "OpenGL", WindowMode::Windowed)
        .expect("Failed to create GLFW window.");

    // Listen for keyboard events on this window.
    window.set_key_polling(true);

    // Make this window's OpenGL context the current context. This must be done before calling
    // `gl::load_with`.
    window.make_current();

    // Load OpenGL function pointers.
    gl::load_with(|symbol| window.get_proc_address(symbol));

    let vertex_shader;
    let fragment_shader;
    let shader_program;
    let mut vao;
    let mut vbo;

    unsafe {
        // Create a vertex array object.
        vao = 0;
        gl::GenVertexArrays(1, &mut vao);
        gl::BindVertexArray(vao);

        // Create a vertex buffer object and copy the vertex data to it.
        vbo = 0;
        gl::GenBuffers(1, &mut vbo);

        #[derive(Copy, Clone, Debug, PartialEq)]
        #[repr(C, packed)]
        struct Vertex {
            // Position.
            x: GLfloat, y: GLfloat,

            // Color.
            r: GLfloat, g: GLfloat, b: GLfloat,
        }

        static VERTICES: [Vertex; 3] = [
            Vertex { x:  0.0, y:  0.5, r: 1.0, g: 0.0, b: 0.0 },
            Vertex { x:  0.5, y: -0.5, r: 0.0, g: 1.0, b: 0.0 },
            Vertex { x: -0.5, y: -0.5, r: 0.0, g: 0.0, b: 1.0 },
        ];

        gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
        gl::BufferData(gl::ARRAY_BUFFER,
                       (VERTICES.len() * size_of::<Vertex>()) as GLsizeiptr,
                       VERTICES.as_ptr() as *const GLvoid,
                       gl::STATIC_DRAW);

        // Compile the vertex and fragment shaders.
        vertex_shader = compile_shader(gl::VERTEX_SHADER, VERTEX_SHADER_SOURCE).unwrap();
        fragment_shader = compile_shader(gl::FRAGMENT_SHADER, FRAGMENT_SHADER_SOURCE).unwrap();

        // Link the vertex and fragment shaders into a shader program.
        shader_program = gl::CreateProgram();
        gl::AttachShader(shader_program, vertex_shader);
        gl::AttachShader(shader_program, fragment_shader);
        gl::BindFragDataLocation(shader_program, 0, gl_str!("out_color"));
        gl::LinkProgram(shader_program);
        gl::UseProgram(shader_program);

        // Specify the layout of the vertex data.
        let position_attrib = gl::GetAttribLocation(shader_program, gl_str!("position"));
        gl::EnableVertexAttribArray(position_attrib as GLuint);
        gl::VertexAttribPointer(position_attrib as GLuint, 2, gl::FLOAT, gl::FALSE,
                                size_of::<Vertex>() as GLint, std::ptr::null());

        let position_attrib = gl::GetAttribLocation(shader_program, gl_str!("color"));
        gl::EnableVertexAttribArray(position_attrib as GLuint);
        gl::VertexAttribPointer(position_attrib as GLuint, 3, gl::FLOAT, gl::FALSE,
                                size_of::<Vertex>() as GLint,
                                std::ptr::null().offset(2 * size_of::<GLfloat>() as isize));
    }

    while !window.should_close() {
        glfw.poll_events();
        for (_, event) in glfw::flush_messages(&events) {
            handle_window_event(&mut window, event);
        }

        unsafe {
            // Clear the screen to black.
            gl::ClearColor(0.0, 0.0, 0.0, 1.0);
            gl::Clear(gl::COLOR_BUFFER_BIT);

            // Draw a triangle from the 3 vertices.
            gl::DrawArrays(gl::TRIANGLES, 0, 3);
        }

        window.swap_buffers();
    }

    unsafe {
        gl::DeleteProgram(shader_program);
        gl::DeleteShader(fragment_shader);
        gl::DeleteShader(vertex_shader);
        gl::DeleteBuffers(1, &vbo);
        gl::DeleteVertexArrays(1, &vao);
    }
}

fn handle_window_event(window: &mut glfw::Window, event: glfw::WindowEvent) {
    use glfw::{Action, Key, WindowEvent};

    match event {
        WindowEvent::Key(Key::Escape, _, Action::Press, _) => {
            window.set_should_close(true);
        },
        _ => {},
    }
}
<commit_msg>Draw a rectangle with two triangles and redundant vertices.<commit_after>extern crate gl;
extern crate glfw;
extern crate time;

use gl::types::*;
use glfw::{Context, OpenGlProfileHint, WindowHint, WindowMode};
use std::mem::size_of;

macro_rules! gl_str {
    ($string_literal:expr) => (concat!($string_literal, '\0').as_bytes().as_ptr() as *const GLchar)
}

const VERTEX_SHADER_SOURCE: &'static str = "
    #version 150

    in vec2 position;
    in vec3 color;

    out vec3 Color;

    void main() {
        Color = color;
        gl_Position = vec4(position, 0.0, 1.0);
    }
";

const FRAGMENT_SHADER_SOURCE: &'static str = "
    #version 150

    in vec3 Color;

    out vec4 out_color;

    void main() {
        out_color = vec4(Color, 1.0);
    }
";

#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(C, packed)]
struct Vertex {
    // Position.
    x: GLfloat, y: GLfloat,

    // Color.
    r: GLfloat, g: GLfloat, b: GLfloat,
}

static VERTICES: [Vertex; 6] = [
    Vertex { x: -0.5, y:  0.5, r: 1.0, g: 0.0, b: 0.0 }, // Top-left
    Vertex { x:  0.5, y:  0.5, r: 0.0, g: 1.0, b: 0.0 }, // Top-right
    Vertex { x:  0.5, y: -0.5, r: 0.0, g: 0.0, b: 1.0 }, // Bottom-right

    Vertex { x:  0.5, y: -0.5, r: 0.0, g: 0.0, b: 1.0 }, // Bottom-right
    Vertex { x: -0.5, y: -0.5, r: 1.0, g: 1.0, b: 1.0 }, // Bottom-left
    Vertex { x: -0.5, y:  0.5, r: 1.0, g: 0.0, b: 0.0 }, // Top-left
];

unsafe fn compile_shader(shader_type: GLenum, source: &str) -> Result<GLuint, String> {
    let shader = gl::CreateShader(shader_type);
    let source_ptr = source.as_bytes().as_ptr() as *const GLchar;
    let source_len = source.len() as GLint;
    gl::ShaderSource(shader, 1, &source_ptr, &source_len);
    gl::CompileShader(shader);

    let mut status = gl::FALSE as GLint;
    gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

    if status == gl::TRUE as GLint {
        Ok(shader)
    } else {
        let mut log_len = 0;
        gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut log_len);
        if log_len == 0 { return Err(String::new()) }

        let mut buf = Vec::with_capacity(log_len as usize);
        buf.set_len(log_len as usize - 1); // Subtract 1 to ignore the trailing null.
        gl::GetShaderInfoLog(shader, log_len, std::ptr::null_mut(),
        buf.as_mut_ptr() as *mut GLchar);

        Err(String::from_utf8_lossy(&buf).into_owned())
    }
}

fn main() {
    let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();

    glfw.window_hint(WindowHint::ContextVersion(3, 2));
    glfw.window_hint(WindowHint::OpenGlProfile(OpenGlProfileHint::Core));
    glfw.window_hint(WindowHint::OpenGlForwardCompat(true));
    glfw.window_hint(WindowHint::Resizable(false));

    let (mut window, events) = glfw.create_window(800, 600, "OpenGL", WindowMode::Windowed)
        .expect("Failed to create GLFW window.");

    // Listen for keyboard events on this window.
    window.set_key_polling(true);

    // Make this window's OpenGL context the current context. This must be done before calling
    // `gl::load_with`.
    window.make_current();

    // Load OpenGL function pointers.
    gl::load_with(|symbol| window.get_proc_address(symbol));

    let vertex_shader;
    let fragment_shader;
    let shader_program;
    let mut vao = 0;
    let mut vbo = 0;

    unsafe {
        // Create a vertex array object.
        gl::GenVertexArrays(1, &mut vao);
        gl::BindVertexArray(vao);

        // Create a vertex buffer object and copy the vertex data to it.
        gl::GenBuffers(1, &mut vbo);
        gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
        gl::BufferData(gl::ARRAY_BUFFER,
                       (VERTICES.len() * size_of::<Vertex>()) as GLsizeiptr,
                       VERTICES.as_ptr() as *const GLvoid,
                       gl::STATIC_DRAW);

        // Compile the vertex and fragment shaders.
        vertex_shader = compile_shader(gl::VERTEX_SHADER, VERTEX_SHADER_SOURCE).unwrap();
        fragment_shader = compile_shader(gl::FRAGMENT_SHADER, FRAGMENT_SHADER_SOURCE).unwrap();

        // Link the vertex and fragment shaders into a shader program.
        shader_program = gl::CreateProgram();
        gl::AttachShader(shader_program, vertex_shader);
        gl::AttachShader(shader_program, fragment_shader);
        gl::BindFragDataLocation(shader_program, 0, gl_str!("out_color"));
        gl::LinkProgram(shader_program);
        gl::UseProgram(shader_program);

        // Specify the layout of the vertex data.
        let position_attrib = gl::GetAttribLocation(shader_program, gl_str!("position"));
        gl::EnableVertexAttribArray(position_attrib as GLuint);
        gl::VertexAttribPointer(position_attrib as GLuint, 2, gl::FLOAT, gl::FALSE,
                                size_of::<Vertex>() as GLint, std::ptr::null());

        let position_attrib = gl::GetAttribLocation(shader_program, gl_str!("color"));
        gl::EnableVertexAttribArray(position_attrib as GLuint);
        gl::VertexAttribPointer(position_attrib as GLuint, 3, gl::FLOAT, gl::FALSE,
                                size_of::<Vertex>() as GLint,
                                std::ptr::null().offset(2 * size_of::<GLfloat>() as isize));
    }

    while !window.should_close() {
        glfw.poll_events();
        for (_, event) in glfw::flush_messages(&events) {
            handle_window_event(&mut window, event);
        }

        unsafe {
            // Clear the screen to black.
            gl::ClearColor(0.0, 0.0, 0.0, 1.0);
            gl::Clear(gl::COLOR_BUFFER_BIT);

            // Draw a triangle from the 3 vertices.
            gl::DrawArrays(gl::TRIANGLES, 0, VERTICES.len() as GLint);
        }

        window.swap_buffers();
    }

    unsafe {
        gl::DeleteProgram(shader_program);
        gl::DeleteShader(fragment_shader);
        gl::DeleteShader(vertex_shader);
        gl::DeleteBuffers(1, &vbo);
        gl::DeleteVertexArrays(1, &vao);
    }
}

fn handle_window_event(window: &mut glfw::Window, event: glfw::WindowEvent) {
    use glfw::{Action, Key, WindowEvent};

    match event {
        WindowEvent::Key(Key::Escape, _, Action::Press, _) => {
            window.set_should_close(true);
        },
        _ => {},
    }
}
<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

#![deny(
    non_camel_case_types,
    non_snake_case,
    path_statements,
    trivial_numeric_casts,
    unstable_features,
    unused_allocation,
    unused_import_braces,
    unused_imports,
    unused_must_use,
    unused_mut,
    unused_qualifications,
    while_true,
)]

#[macro_use] extern crate log;
#[macro_use] extern crate version;
extern crate semver;
extern crate clap;

extern crate libimagstore;
extern crate libimagrt;
extern crate libimagentryref;
extern crate libimagerror;
extern crate libimagentrylist;
extern crate libimaginteraction;
extern crate libimagutil;

mod ui;
use ui::build_ui;

use std::path::PathBuf;

use libimagentryref::reference::Ref;
use libimagentryref::flags::RefFlags;
use libimagerror::trace::trace_error;
use libimagrt::setup::generate_runtime_setup;
use libimagrt::runtime::Runtime;

fn main() {
    let rt = generate_runtime_setup("imag-ref",
                                    &version!()[..],
                                    "Reference files outside of the store",
                                    build_ui);
    rt.cli()
        .subcommand_name()
        .map(|name| {
            debug!("Call: {}", name);
            match name {
                "add"    => add(&rt),
                "remove" => remove(&rt),
                "list"   => list(&rt),
                _        => {
                    debug!("Unknown command"); // More error handling
                },
            };
        });
}

fn add(rt: &Runtime) {
    let cmd  = rt.cli().subcommand_matches("add").unwrap();
    let path = cmd.value_of("path").map(PathBuf::from).unwrap(); // saved by clap

    let flags = RefFlags::default()
        .with_content_hashing(cmd.is_present("track-content"))
        .with_permission_tracking(cmd.is_present("track-permissions"));

    match Ref::create(rt.store(), path, flags) {
        Ok(r) => {
            debug!("Reference created: {:?}", r);
            info!("Ok");
        },
        Err(e) => {
            trace_error(&e);
            warn!("Failed to create reference");
        },
    }
}

fn remove(rt: &Runtime) {
    use libimaginteraction::ask::ask_bool;

    let cmd  = rt.cli().subcommand_matches("remove").unwrap();
    let hash = cmd.value_of("hash").map(String::from).unwrap(); // saved by clap
    let yes  = cmd.is_present("yes");

    if yes || ask_bool(&format!("Delete Ref with hash '{}'", hash)[..], None) {
        match Ref::delete_by_hash(rt.store(), hash) {
            Err(e) => trace_error(&e),
            Ok(_) => info!("Ok"),
        }
    } else {
        info!("Aborted");
    }

}

fn list(rt: &Runtime) {
    use std::process::exit;

    use libimagentrylist::lister::Lister;
    use libimagentryref::lister::RefLister;

    let cmd                      = rt.cli().subcommand_matches("list").unwrap();
    let do_check_dead            = cmd.is_present("check-dead");
    let do_check_changed         = cmd.is_present("check-changed");
    let do_check_changed_content = cmd.is_present("check-changed-content");
    let do_check_changed_permiss = cmd.is_present("check-changed-permissions");

    let iter = match rt.store().retrieve_for_module("ref") {
        Ok(iter) => iter.filter_map(|id| {
            match Ref::get(rt.store(), id) {
                Ok(r) => Some(r),
                Err(e) => {
                    trace_error(&e);
                    None
                },
            }
        }),
        Err(e) => {
            trace_error(&e);
            exit(1);
        }
    };

    RefLister::new()
        .check_dead(do_check_dead)
        .check_changed(do_check_changed)
        .check_changed_content(do_check_changed_content)
        .check_changed_permiss(do_check_changed_permiss)
        .list(iter.map(|e| e.into()))
        .ok();
}

<commit_msg>Fix imag-ref<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

#![deny(
    non_camel_case_types,
    non_snake_case,
    path_statements,
    trivial_numeric_casts,
    unstable_features,
    unused_allocation,
    unused_import_braces,
    unused_imports,
    unused_must_use,
    unused_mut,
    unused_qualifications,
    while_true,
)]

#[macro_use] extern crate log;
#[macro_use] extern crate version;
extern crate semver;
extern crate clap;

extern crate libimagstore;
extern crate libimagrt;
extern crate libimagentryref;
extern crate libimagerror;
extern crate libimagentrylist;
extern crate libimaginteraction;
extern crate libimagutil;

mod ui;
use ui::build_ui;

use std::path::PathBuf;

use libimagentryref::refstore::RefStore;
use libimagentryref::flags::RefFlags;
use libimagerror::trace::trace_error;
use libimagrt::setup::generate_runtime_setup;
use libimagrt::runtime::Runtime;

fn main() {
    let rt = generate_runtime_setup("imag-ref",
                                    &version!()[..],
                                    "Reference files outside of the store",
                                    build_ui);
    rt.cli()
        .subcommand_name()
        .map(|name| {
            debug!("Call: {}", name);
            match name {
                "add"    => add(&rt),
                "remove" => remove(&rt),
                "list"   => list(&rt),
                _        => {
                    debug!("Unknown command"); // More error handling
                },
            };
        });
}

fn add(rt: &Runtime) {
    let cmd  = rt.cli().subcommand_matches("add").unwrap();
    let path = cmd.value_of("path").map(PathBuf::from).unwrap(); // saved by clap

    let flags = RefFlags::default()
        .with_content_hashing(cmd.is_present("track-content"))
        .with_permission_tracking(cmd.is_present("track-permissions"));

    match RefStore::create(rt.store(), path, flags) {
        Ok(r) => {
            debug!("Reference created: {:?}", r);
            info!("Ok");
        },
        Err(e) => {
            trace_error(&e);
            warn!("Failed to create reference");
        },
    }
}

fn remove(rt: &Runtime) {
    use libimaginteraction::ask::ask_bool;

    let cmd  = rt.cli().subcommand_matches("remove").unwrap();
    let hash = cmd.value_of("hash").map(String::from).unwrap(); // saved by clap
    let yes  = cmd.is_present("yes");

    if yes || ask_bool(&format!("Delete Ref with hash '{}'", hash)[..], None) {
        match rt.store().delete_by_hash(hash) {
            Err(e) => trace_error(&e),
            Ok(_) => info!("Ok"),
        }
    } else {
        info!("Aborted");
    }

}

fn list(rt: &Runtime) {
    use std::process::exit;

    use libimagentrylist::lister::Lister;
    use libimagentryref::lister::RefLister;

    let cmd                      = rt.cli().subcommand_matches("list").unwrap();
    let do_check_dead            = cmd.is_present("check-dead");
    let do_check_changed         = cmd.is_present("check-changed");
    let do_check_changed_content = cmd.is_present("check-changed-content");
    let do_check_changed_permiss = cmd.is_present("check-changed-permissions");

    let iter = match rt.store().retrieve_for_module("ref") {
        Ok(iter) => iter.filter_map(|id| {
            match rt.store().get(id) {
                Ok(r) => Some(r),
                Err(e) => {
                    trace_error(&e);
                    None
                },
            }
        }),
        Err(e) => {
            trace_error(&e);
            exit(1);
        }
    };

    RefLister::new()
        .check_dead(do_check_dead)
        .check_changed(do_check_changed)
        .check_changed_content(do_check_changed_content)
        .check_changed_permiss(do_check_changed_permiss)
        .list(iter.filter_map(Into::into))
        .ok();
}

<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![no_std]
#![allow(unused_attributes)]
#![unstable(feature = "alloc_system",
            reason = "this library is unlikely to be stabilized in its current \
                      form or name",
            issue = "32838")]
#![feature(allocator_api)]
#![feature(core_intrinsics)]
#![feature(nll)]
#![feature(staged_api)]
#![feature(rustc_attrs)]
#![feature(alloc_layout_extra)]
#![cfg_attr(
    all(target_arch = "wasm32", not(target_os = "emscripten")),
    feature(integer_atomics, stdsimd)
)]
#![cfg_attr(any(unix, target_os = "cloudabi", target_os = "redox"), feature(libc))]
// The minimum alignment guaranteed by the architecture. This value is used to
// add fast paths for low alignment values.
#[cfg(all(any(target_arch = "x86",
              target_arch = "arm",
              target_arch = "mips",
              target_arch = "powerpc",
              target_arch = "powerpc64",
              target_arch = "asmjs",
              target_arch = "wasm32")))]
#[allow(dead_code)]
const MIN_ALIGN: usize = 8;
#[cfg(all(any(target_arch = "x86_64",
              target_arch = "aarch64",
              target_arch = "mips64",
              target_arch = "s390x",
              target_arch = "sparc64")))]
#[allow(dead_code)]
const MIN_ALIGN: usize = 16;

/// The default memory allocator provided by the operating system.
///
/// This is based on `malloc` on Unix platforms and `HeapAlloc` on Windows,
/// plus related functions.
///
/// This type can be used in a `static` item
/// with the `#[global_allocator]` attribute
/// to force the global allocator to be the system’s one.
/// (The default is jemalloc for executables, on some platforms.)
///
/// ```rust
/// use std::alloc::System;
///
/// #[global_allocator]
/// static A: System = System;
///
/// fn main() {
///     let a = Box::new(4); // Allocates from the system allocator.
///     println!("{}", a);
/// }
/// ```
///
/// It can also be used directly to allocate memory
/// independently of the standard library’s global allocator.
#[stable(feature = "alloc_system_type", since = "1.28.0")]
pub struct System;
#[cfg(any(windows, unix, target_os = "cloudabi", target_os = "redox"))]
mod realloc_fallback {
    use core::alloc::{GlobalAlloc, Layout};
    use core::cmp;
    use core::ptr;
    impl super::System {
        pub(crate) unsafe fn realloc_fallback(&self, ptr: *mut u8, old_layout: Layout,
                                              new_size: usize) -> *mut u8 {
            // Docs for GlobalAlloc::realloc require this to be valid:
            let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align());
            let new_ptr = GlobalAlloc::alloc(self, new_layout);
            if !new_ptr.is_null() {
                let size = cmp::min(old_layout.size(), new_size);
                ptr::copy_nonoverlapping(ptr, new_ptr, size);
                GlobalAlloc::dealloc(self, ptr, old_layout);
            }
            new_ptr
        }
    }
}
#[cfg(any(unix, target_os = "cloudabi", target_os = "redox"))]
mod platform {
    extern crate libc;
    use core::ptr;
    use MIN_ALIGN;
    use System;
    use core::alloc::{GlobalAlloc, Layout};
    #[stable(feature = "alloc_system_type", since = "1.28.0")]
    unsafe impl GlobalAlloc for System {
        #[inline]
        unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
            if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
                libc::malloc(layout.size()) as *mut u8
            } else {
                #[cfg(target_os = "macos")]
                {
                    if layout.align() > (1 << 31) {
                        return ptr::null_mut()
                    }
                }
                aligned_malloc(&layout)
            }
        }
        #[inline]
        unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
            if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
                libc::calloc(layout.size(), 1) as *mut u8
            } else {
                let ptr = self.alloc(layout.clone());
                if !ptr.is_null() {
                    ptr::write_bytes(ptr, 0, layout.size());
                }
                ptr
            }
        }
        #[inline]
        unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
            libc::free(ptr as *mut libc::c_void)
        }
        #[inline]
        unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
            if layout.align() <= MIN_ALIGN && layout.align() <= new_size {
                libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8
            } else {
                self.realloc_fallback(ptr, layout, new_size)
            }
        }
    }
    #[cfg(any(target_os = "android",
              target_os = "hermit",
              target_os = "redox",
              target_os = "solaris"))]
    #[inline]
    unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
        // On android we currently target API level 9 which unfortunately
        // doesn't have the `posix_memalign` API used below. Instead we use
        // `memalign`, but this unfortunately has the property on some systems
        // where the memory returned cannot be deallocated by `free`!
        //
        // Upon closer inspection, however, this appears to work just fine with
        // Android, so for this platform we should be fine to call `memalign`
        // (which is present in API level 9). Some helpful references could
        // possibly be chromium using memalign [1], attempts at documenting that
        // memalign + free is ok [2] [3], or the current source of chromium
        // which still uses memalign on android [4].
        //
        // [1]: https://codereview.chromium.org/10796020/
        // [2]: https://code.google.com/p/android/issues/detail?id=35391
        // [3]: https://bugs.chromium.org/p/chromium/issues/detail?id=138579
        // [4]: https://chromium.googlesource.com/chromium/src/base/+/master/
        //                                       /memory/aligned_memory.cc
        libc::memalign(layout.align(), layout.size()) as *mut u8
    }
    #[cfg(not(any(target_os = "android",
                  target_os = "hermit",
                  target_os = "redox",
                  target_os = "solaris")))]
    #[inline]
    unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
        let mut out = ptr::null_mut();
        let ret = libc::posix_memalign(&mut out, layout.align(), layout.size());
        if ret != 0 {
            ptr::null_mut()
        } else {
            out as *mut u8
        }
    }
}
#[cfg(windows)]
#[allow(nonstandard_style)]
mod platform {
    use MIN_ALIGN;
    use System;
    use core::alloc::{GlobalAlloc, Layout};
    type LPVOID = *mut u8;
    type HANDLE = LPVOID;
    type SIZE_T = usize;
    type DWORD = u32;
    type BOOL = i32;
    extern "system" {
        fn GetProcessHeap() -> HANDLE;
        fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID;
        fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID, dwBytes: SIZE_T) -> LPVOID;
        fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL;
        fn GetLastError() -> DWORD;
    }
    #[repr(C)]
    struct Header(*mut u8);
    const HEAP_ZERO_MEMORY: DWORD = 0x00000008;
    unsafe fn get_header<'a>(ptr: *mut u8) -> &'a mut Header {
        &mut *(ptr as *mut Header).offset(-1)
    }
    unsafe fn align_ptr(ptr: *mut u8, align: usize) -> *mut u8 {
        let aligned = ptr.add(align - (ptr as usize & (align - 1)));
        *get_header(aligned) = Header(ptr);
        aligned
    }
    #[inline]
    unsafe fn allocate_with_flags(layout: Layout, flags: DWORD) -> *mut u8 {
        let ptr = if layout.align() <= MIN_ALIGN {
            HeapAlloc(GetProcessHeap(), flags, layout.size())
        } else {
            let size = layout.size() + layout.align();
            let ptr = HeapAlloc(GetProcessHeap(), flags, size);
            if ptr.is_null() {
                ptr
            } else {
                align_ptr(ptr, layout.align())
            }
        };
        ptr as *mut u8
    }
    #[stable(feature = "alloc_system_type", since = "1.28.0")]
    unsafe impl GlobalAlloc for System {
        #[inline]
        unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
            allocate_with_flags(layout, 0)
        }
        #[inline]
        unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
            allocate_with_flags(layout, HEAP_ZERO_MEMORY)
        }
        #[inline]
        unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
            if layout.align() <= MIN_ALIGN {
                let err = HeapFree(GetProcessHeap(), 0, ptr as LPVOID);
                debug_assert!(err != 0, "Failed to free heap memory: {}",
                              GetLastError());
            } else {
                let header = get_header(ptr);
                let err = HeapFree(GetProcessHeap(), 0, header.0 as LPVOID);
                debug_assert!(err != 0, "Failed to free heap memory: {}",
                              GetLastError());
            }
        }
        #[inline]
        unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
            if layout.align() <= MIN_ALIGN {
                HeapReAlloc(GetProcessHeap(), 0, ptr as LPVOID, new_size) as *mut u8
            } else {
                self.realloc_fallback(ptr, layout, new_size)
            }
        }
    }
}
// This is an implementation of a global allocator on the wasm32 platform when
// emscripten is not in use. In that situation there's no actual runtime for us
// to lean on for allocation, so instead we provide our own!
//
// The wasm32 instruction set has two instructions for getting the current
// amount of memory and growing the amount of memory. These instructions are the
// foundation on which we're able to build an allocator, so we do so! Note that
// the instructions are also pretty "global" and this is the "global" allocator
// after all!
//
// The current allocator here is the `dlmalloc` crate which we've got included
// in the rust-lang/rust repository as a submodule. The crate is a port of
// dlmalloc.c from C to Rust and is basically just so we can have "pure Rust"
// for now which is currently technically required (can't link with C yet).
//
// The crate itself provides a global allocator which on wasm has no
// synchronization as there are no threads!
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
mod platform {
    extern crate dlmalloc;
    use core::alloc::{GlobalAlloc, Layout};
    use System;
    static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::DLMALLOC_INIT;
    #[stable(feature = "alloc_system_type", since = "1.28.0")]
    unsafe impl GlobalAlloc for System {
        #[inline]
        unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
            let _lock = lock::lock();
            DLMALLOC.malloc(layout.size(), layout.align())
        }
        #[inline]
        unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
            let _lock = lock::lock();
            DLMALLOC.calloc(layout.size(), layout.align())
        }
        #[inline]
        unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
            let _lock = lock::lock();
            DLMALLOC.free(ptr, layout.size(), layout.align())
        }
        #[inline]
        unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
            let _lock = lock::lock();
            DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size)
        }
    }
    #[cfg(target_feature = "atomics")]
    mod lock {
        use core::arch::wasm32;
        use core::sync::atomic::{AtomicI32, Ordering::SeqCst};
        static LOCKED: AtomicI32 = AtomicI32::new(0);
        pub struct DropLock;
        pub fn lock() -> DropLock {
            loop {
                if LOCKED.swap(1, SeqCst) == 0 {
                    return DropLock
                }
                unsafe {
                    let r = wasm32::atomic::wait_i32(
                        &LOCKED as *const AtomicI32 as *mut i32,
                        1,  // expected value
                        -1, // timeout
                    );
                    debug_assert!(r == 0 || r == 1);
                }
            }
        }
        impl Drop for DropLock {
            fn drop(&mut self) {
                let r = LOCKED.swap(0, SeqCst);
                debug_assert_eq!(r, 1);
                unsafe {
                    wasm32::atomic::wake(
                        &LOCKED as *const AtomicI32 as *mut i32,
                        1, // only one thread
                    );
                }
            }
        }
    }
    #[cfg(not(target_feature = "atomics"))]
    mod lock {
        #[inline]
        pub fn lock() {} // no atomics, no threads, that's easy!
    }
}
<commit_msg>Sync from rust c9228570668803e3e6402770d55f23a12c9ae686<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![no_std]
#![allow(unused_attributes)]
#![unstable(feature = "alloc_system",
            reason = "this library is unlikely to be stabilized in its current \
                      form or name",
            issue = "32838")]
#![feature(allocator_api)]
#![feature(core_intrinsics)]
#![feature(nll)]
#![feature(staged_api)]
#![feature(rustc_attrs)]
#![feature(alloc_layout_extra)]
#![cfg_attr(
    all(target_arch = "wasm32", not(target_os = "emscripten")),
    feature(integer_atomics, stdsimd)
)]
#![cfg_attr(any(unix, target_os = "redox"), feature(libc))]
// The minimum alignment guaranteed by the architecture. This value is used to
// add fast paths for low alignment values.
#[cfg(all(any(target_arch = "x86",
              target_arch = "arm",
              target_arch = "mips",
              target_arch = "powerpc",
              target_arch = "powerpc64",
              target_arch = "asmjs",
              target_arch = "wasm32")))]
#[allow(dead_code)]
const MIN_ALIGN: usize = 8;
#[cfg(all(any(target_arch = "x86_64",
              target_arch = "aarch64",
              target_arch = "mips64",
              target_arch = "s390x",
              target_arch = "sparc64")))]
#[allow(dead_code)]
const MIN_ALIGN: usize = 16;

/// The default memory allocator provided by the operating system.
///
/// This is based on `malloc` on Unix platforms and `HeapAlloc` on Windows,
/// plus related functions.
///
/// This type can be used in a `static` item
/// with the `#[global_allocator]` attribute
/// to force the global allocator to be the system’s one.
/// (The default is jemalloc for executables, on some platforms.)
///
/// ```rust
/// use std::alloc::System;
///
/// #[global_allocator]
/// static A: System = System;
///
/// fn main() {
///     let a = Box::new(4); // Allocates from the system allocator.
///     println!("{}", a);
/// }
/// ```
///
/// It can also be used directly to allocate memory
/// independently of the standard library’s global allocator.
#[stable(feature = "alloc_system_type", since = "1.28.0")]
pub struct System;
#[cfg(any(windows, unix, target_os = "redox"))]
mod realloc_fallback {
    use core::alloc::{GlobalAlloc, Layout};
    use core::cmp;
    use core::ptr;
    impl super::System {
        pub(crate) unsafe fn realloc_fallback(&self, ptr: *mut u8, old_layout: Layout,
                                              new_size: usize) -> *mut u8 {
            // Docs for GlobalAlloc::realloc require this to be valid:
            let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align());
            let new_ptr = GlobalAlloc::alloc(self, new_layout);
            if !new_ptr.is_null() {
                let size = cmp::min(old_layout.size(), new_size);
                ptr::copy_nonoverlapping(ptr, new_ptr, size);
                GlobalAlloc::dealloc(self, ptr, old_layout);
            }
            new_ptr
        }
    }
}
#[cfg(any(unix, target_os = "redox"))]
mod platform {
    extern crate libc;
    use core::ptr;
    use MIN_ALIGN;
    use System;
    use core::alloc::{GlobalAlloc, Layout};
    #[stable(feature = "alloc_system_type", since = "1.28.0")]
    unsafe impl GlobalAlloc for System {
        #[inline]
        unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
            if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
                libc::malloc(layout.size()) as *mut u8
            } else {
                #[cfg(target_os = "macos")]
                {
                    if layout.align() > (1 << 31) {
                        return ptr::null_mut()
                    }
                }
                aligned_malloc(&layout)
            }
        }
        #[inline]
        unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
            if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
                libc::calloc(layout.size(), 1) as *mut u8
            } else {
                let ptr = self.alloc(layout.clone());
                if !ptr.is_null() {
                    ptr::write_bytes(ptr, 0, layout.size());
                }
                ptr
            }
        }
        #[inline]
        unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
            libc::free(ptr as *mut libc::c_void)
        }
        #[inline]
        unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
            if layout.align() <= MIN_ALIGN && layout.align() <= new_size {
                libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8
            } else {
                self.realloc_fallback(ptr, layout, new_size)
            }
        }
    }
    #[cfg(any(target_os = "android",
              target_os = "hermit",
              target_os = "redox",
              target_os = "solaris"))]
    #[inline]
    unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
        // On android we currently target API level 9 which unfortunately
        // doesn't have the `posix_memalign` API used below. Instead we use
        // `memalign`, but this unfortunately has the property on some systems
        // where the memory returned cannot be deallocated by `free`!
        //
        // Upon closer inspection, however, this appears to work just fine with
        // Android, so for this platform we should be fine to call `memalign`
        // (which is present in API level 9). Some helpful references could
        // possibly be chromium using memalign [1], attempts at documenting that
        // memalign + free is ok [2] [3], or the current source of chromium
        // which still uses memalign on android [4].
        //
        // [1]: https://codereview.chromium.org/10796020/
        // [2]: https://code.google.com/p/android/issues/detail?id=35391
        // [3]: https://bugs.chromium.org/p/chromium/issues/detail?id=138579
        // [4]: https://chromium.googlesource.com/chromium/src/base/+/master/
        //                                       /memory/aligned_memory.cc
        libc::memalign(layout.align(), layout.size()) as *mut u8
    }
    #[cfg(not(any(target_os = "android",
                  target_os = "hermit",
                  target_os = "redox",
                  target_os = "solaris")))]
    #[inline]
    unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
        let mut out = ptr::null_mut();
        let ret = libc::posix_memalign(&mut out, layout.align(), layout.size());
        if ret != 0 {
            ptr::null_mut()
        } else {
            out as *mut u8
        }
    }
}
#[cfg(windows)]
#[allow(nonstandard_style)]
mod platform {
    use MIN_ALIGN;
    use System;
    use core::alloc::{GlobalAlloc, Layout};
    type LPVOID = *mut u8;
    type HANDLE = LPVOID;
    type SIZE_T = usize;
    type DWORD = u32;
    type BOOL = i32;
    extern "system" {
        fn GetProcessHeap() -> HANDLE;
        fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID;
        fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID, dwBytes: SIZE_T) -> LPVOID;
        fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL;
        fn GetLastError() -> DWORD;
    }
    #[repr(C)]
    struct Header(*mut u8);
    const HEAP_ZERO_MEMORY: DWORD = 0x00000008;
    unsafe fn get_header<'a>(ptr: *mut u8) -> &'a mut Header {
        &mut *(ptr as *mut Header).offset(-1)
    }
    unsafe fn align_ptr(ptr: *mut u8, align: usize) -> *mut u8 {
        let aligned = ptr.add(align - (ptr as usize & (align - 1)));
        *get_header(aligned) = Header(ptr);
        aligned
    }
    #[inline]
    unsafe fn allocate_with_flags(layout: Layout, flags: DWORD) -> *mut u8 {
        let ptr = if layout.align() <= MIN_ALIGN {
            HeapAlloc(GetProcessHeap(), flags, layout.size())
        } else {
            let size = layout.size() + layout.align();
            let ptr = HeapAlloc(GetProcessHeap(), flags, size);
            if ptr.is_null() {
                ptr
            } else {
                align_ptr(ptr, layout.align())
            }
        };
        ptr as *mut u8
    }
    #[stable(feature = "alloc_system_type", since = "1.28.0")]
    unsafe impl GlobalAlloc for System {
        #[inline]
        unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
            allocate_with_flags(layout, 0)
        }
        #[inline]
        unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
            allocate_with_flags(layout, HEAP_ZERO_MEMORY)
        }
        #[inline]
        unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
            if layout.align() <= MIN_ALIGN {
                let err = HeapFree(GetProcessHeap(), 0, ptr as LPVOID);
                debug_assert!(err != 0, "Failed to free heap memory: {}",
                              GetLastError());
            } else {
                let header = get_header(ptr);
                let err = HeapFree(GetProcessHeap(), 0, header.0 as LPVOID);
                debug_assert!(err != 0, "Failed to free heap memory: {}",
                              GetLastError());
            }
        }
        #[inline]
        unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
            if layout.align() <= MIN_ALIGN {
                HeapReAlloc(GetProcessHeap(), 0, ptr as LPVOID, new_size) as *mut u8
            } else {
                self.realloc_fallback(ptr, layout, new_size)
            }
        }
    }
}
// This is an implementation of a global allocator on the wasm32 platform when
// emscripten is not in use. In that situation there's no actual runtime for us
// to lean on for allocation, so instead we provide our own!
//
// The wasm32 instruction set has two instructions for getting the current
// amount of memory and growing the amount of memory. These instructions are the
// foundation on which we're able to build an allocator, so we do so! Note that
// the instructions are also pretty "global" and this is the "global" allocator
// after all!
//
// The current allocator here is the `dlmalloc` crate which we've got included
// in the rust-lang/rust repository as a submodule. The crate is a port of
// dlmalloc.c from C to Rust and is basically just so we can have "pure Rust"
// for now which is currently technically required (can't link with C yet).
//
// The crate itself provides a global allocator which on wasm has no
// synchronization as there are no threads!
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
mod platform {
    extern crate dlmalloc;
    use core::alloc::{GlobalAlloc, Layout};
    use System;
    static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::DLMALLOC_INIT;
    #[stable(feature = "alloc_system_type", since = "1.28.0")]
    unsafe impl GlobalAlloc for System {
        #[inline]
        unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
            let _lock = lock::lock();
            DLMALLOC.malloc(layout.size(), layout.align())
        }
        #[inline]
        unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
            let _lock = lock::lock();
            DLMALLOC.calloc(layout.size(), layout.align())
        }
        #[inline]
        unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
            let _lock = lock::lock();
            DLMALLOC.free(ptr, layout.size(), layout.align())
        }
        #[inline]
        unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
            let _lock = lock::lock();
            DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size)
        }
    }
    #[cfg(target_feature = "atomics")]
    mod lock {
        use core::arch::wasm32;
        use core::sync::atomic::{AtomicI32, Ordering::SeqCst};
        static LOCKED: AtomicI32 = AtomicI32::new(0);
        pub struct DropLock;
        pub fn lock() -> DropLock {
            loop {
                if LOCKED.swap(1, SeqCst) == 0 {
                    return DropLock
                }
                unsafe {
                    let r = wasm32::atomic::wait_i32(
                        &LOCKED as *const AtomicI32 as *mut i32,
                        1,  // expected value
                        -1, // timeout
                    );
                    debug_assert!(r == 0 || r == 1);
                }
            }
        }
        impl Drop for DropLock {
            fn drop(&mut self) {
                let r = LOCKED.swap(0, SeqCst);
                debug_assert_eq!(r, 1);
                unsafe {
                    wasm32::atomic::wake(
                        &LOCKED as *const AtomicI32 as *mut i32,
                        1, // only one thread
                    );
                }
            }
        }
    }
    #[cfg(not(target_feature = "atomics"))]
    mod lock {
        #[inline]
        pub fn lock() {} // no atomics, no threads, that's easy!
    }
}
<|endoftext|>


<commit_before>use super::stack_with_trace::Op;

#[derive(Eq, PartialEq, Debug)]
pub enum Node {
    Add(Box<Node>, Box<Node>),
    ReadLocal(u8),
    LitI8(i8),
}

#[derive(Eq, PartialEq, Debug)]
pub enum Stmt {
    BranchLt(Node, Node, i8 /* bc offset */, bool /* taken */),
    Ret(Node, u8 /* nlocals */),
    Seq(Vec<Stmt>),
}

pub fn trace_to_node(ops: &[*const u8]) -> Stmt {
    let mut i = 0;
    let mut stack = vec![];
    let mut stmts = vec![];

    fn next_op(ops: &[*const u8], i: &mut usize) -> Op {
        let op = Op::from(unsafe { *ops[*i] });
        *i += 1;
        op
    }

    fn next_oparg(ops: &[*const u8], i: &mut usize) -> u8 {
        let oparg = unsafe { *ops[*i] };
        *i += 1;
        oparg
    }

    fn figure_out_next_op_ptr(ops: &[*const u8], i: usize) -> *const u8 {
        if i >= ops.len() {
            ops[0]
        } else {
            ops[i]
        }
    }

    while i < ops.len() {
        match next_op(ops, &mut i) {
            Op::LoadI8 => {
                stack.push(Node::LitI8(next_oparg(ops, &mut i) as i8));
                i += 2;
            }
            Op::Ret => {
                // Call / Ret should be handled in two flavors:
                // - A ret paired with a call can be inlined
                // - An unpaired call or ret means recursive activation
                //   and should be treated differently.
                let nlocals = next_oparg(ops, &mut i) as usize;
                if stack.len() >= nlocals + 1 {
                    // Should be paired
                    for _ in 0..nlocals {
                        stack.pop();
                    }
                    let top = stack.pop().unwrap();
                    stmts.push(Stmt::Ret(top, nlocals as u8));
                } else if stack.len() >= 1 {
                    // Can only get the return value
                    let top = stack.pop().unwrap();
                    stmts.push(Stmt::Ret(top, nlocals as u8));
                } else {
                    // Nothing on the stack
                }
            }
            Op::Add => {
                let r = stack.pop().unwrap();
                let l = stack.pop().unwrap();
                stack.push(Node::Add(box r, box l));
            }
            Op::BranchLt => {
                let op_ptr = ops[i - 1];
                let pc_incr = next_oparg(ops, &mut i) as isize;
                let r = stack.pop().unwrap();
                let l = stack.pop().unwrap();
                let branch_target = figure_out_next_op_ptr(ops, i);
                let taken = unsafe {
                    if branch_target == op_ptr.offset(2 + pc_incr) {
                        true
                    } else if branch_target == op_ptr.offset(2) {
                        false
                    } else {
                        panic!("invalid branch target");
                    }
                };
                stmts.push(Stmt::BranchLt(r, l, pc_incr as i8, taken));
            }
            Op::LoadL => {
                // Have the same problem: the value might not be pushed in this trace...
            }
            Op::Call => {
                let op_ptr = ops[i - 1];
                let pc_incr = next_oparg(ops, &mut i) as isize;
                // XXX: Consider using a saner calling convention...
            }
            Op::Enter => {
                let nargs = next_oparg(ops, &mut i) as isize;
            }
        }
    }
    Stmt::Seq(stmts)
}
<commit_msg>shenmegui<commit_after>use super::stack_with_trace::Op;

#[derive(Eq, PartialEq, Debug)]
pub enum Node {
    Add(Box<Node>, Box<Node>),
    ReadLocal(u8),
    LitI8(i8),
}

#[derive(Eq, PartialEq, Debug)]
pub enum Stmt {
    BranchLt(Node, Node, i8 /* bc offset */, bool /* taken */),
    Ret(Node, u8 /* nlocals */),
    Seq(Vec<Stmt>),
}

pub fn trace_to_node(ops: &[*const u8]) -> Stmt {
    let mut i = 0;
    let mut stack = vec![];
    let mut stmts = vec![];

    fn next_op(ops: &[*const u8], i: &mut usize) -> Op {
        let op = Op::from(unsafe { *ops[*i] });
        *i += 1;
        op
    }

    fn next_oparg(ops: &[*const u8], i: &mut usize) -> u8 {
        let oparg = unsafe { *ops[*i] };
        *i += 1;
        oparg
    }

    fn figure_out_next_op_ptr(ops: &[*const u8], i: usize) -> *const u8 {
        if i >= ops.len() {
            ops[0]
        } else {
            ops[i]
        }
    }

    while i < ops.len() {
        match next_op(ops, &mut i) {
            Op::LoadI8 => {
                stack.push(Node::LitI8(next_oparg(ops, &mut i) as i8));
                i += 2;
            }
            Op::Ret => {
                // Call / Ret should be handled in two flavors:
                // - A ret paired with a call can be inlined
                // - An unpaired call or ret means recursive activation
                //   and should be treated differently.
                let nlocals = next_oparg(ops, &mut i) as usize;
                if stack.len() >= nlocals + 1 {
                    // Should be paired
                    for _ in 0..nlocals {
                        stack.pop();
                    }
                    let top = stack.pop().unwrap();
                    stmts.push(Stmt::Ret(top, nlocals as u8));
                } else if stack.len() >= 1 {
                    // Can only get the return value
                    let top = stack.pop().unwrap();
                    stmts.push(Stmt::Ret(top, nlocals as u8));
                } else {
                    // Nothing on the stack
                }
            }
            Op::Add => {
                let r = stack.pop().unwrap();
                let l = stack.pop().unwrap();
                stack.push(Node::Add(box r, box l));
            }
            Op::BranchLt => {
                let op_ptr = ops[i - 1];
                let pc_incr = next_oparg(ops, &mut i) as isize;
                let r = stack.pop().unwrap();
                let l = stack.pop().unwrap();
                let branch_target = figure_out_next_op_ptr(ops, i);
                let taken = unsafe {
                    if branch_target == op_ptr.offset(2 + pc_incr) {
                        true
                    } else if branch_target == op_ptr.offset(2) {
                        false
                    } else {
                        panic!("invalid branch target");
                    }
                };
                stmts.push(Stmt::BranchLt(r, l, pc_incr as i8, taken));
            }
            Op::LoadL => {
                // Have the same problem: the value might not be pushed in this trace...
            }
            Op::Call => {
                let op_ptr = ops[i - 1];
                let pc_incr = next_oparg(ops, &mut i) as isize;
                // XXX: Consider using a saner calling convention...
            }
            Op::Enter => {
                let nargs = next_oparg(ops, &mut i) as isize;
            }
            Op::Halt => {
                panic!("Shouldn't happen in a trace");
            }
        }
    }
    Stmt::Seq(stmts)
}
<|endoftext|>


<commit_before>use crate::config::Config;
use crate::err::Result;
use crate::file::{FileType, SaveDir, SaveFile};
use anime::remote::{RemoteService, SeriesEntry, SeriesInfo, Status};
use chrono::{Local, NaiveDate};
use serde_derive::{Deserialize, Serialize};
use std::borrow::Cow;

#[derive(Debug, Deserialize, Serialize)]
pub struct EntryState {
    entry: SeriesEntry,
    needs_sync: bool,
}

impl EntryState {
    pub fn new(entry: SeriesEntry) -> EntryState {
        EntryState {
            entry,
            needs_sync: false,
        }
    }

    pub fn force_sync_changes_to_remote<R, S>(&mut self, remote: &R, name: S) -> Result<()>
    where
        R: RemoteService + ?Sized,
        S: AsRef<str>,
    {
        if remote.is_offline() {
            self.save_with_id(self.entry.id, name.as_ref())?;
            return Ok(());
        }

        remote.update_list_entry(&self.entry)?;

        self.needs_sync = false;
        self.save_with_id(self.entry.id, name.as_ref())?;

        Ok(())
    }

    pub fn sync_changes_to_remote<R, S>(&mut self, remote: &R, name: S) -> Result<()>
    where
        R: RemoteService + ?Sized,
        S: AsRef<str>,
    {
        if !self.needs_sync {
            return Ok(());
        }

        self.force_sync_changes_to_remote(remote, name)
    }

    pub fn force_sync_changes_from_remote<R, S>(&mut self, remote: &R, name: S) -> Result<()>
    where
        R: RemoteService + ?Sized,
        S: AsRef<str>,
    {
        if remote.is_offline() {
            return Ok(());
        }

        let entry = match remote.get_list_entry(self.entry.id)? {
            Some(entry) => entry,
            None => SeriesEntry::new(self.entry.id),
        };

        self.entry = entry;
        self.needs_sync = false;
        self.save_with_id(self.entry.id, name.as_ref())?;

        Ok(())
    }

    pub fn sync_changes_from_remote<R, S>(&mut self, remote: &R, name: S) -> Result<()>
    where
        R: RemoteService + ?Sized,
        S: AsRef<str>,
    {
        if self.needs_sync {
            return Ok(());
        }

        self.force_sync_changes_from_remote(remote, name)
    }

    pub fn mark_as_dropped(&mut self, config: &Config) {
        if self.end_date().is_none()
            || (self.status() == Status::Rewatching && config.reset_dates_on_rewatch)
        {
            self.entry.end_date = Some(Local::today().naive_local());
        }

        self.entry.status = Status::Dropped;
        self.needs_sync = true;
    }

    pub fn mark_as_on_hold(&mut self) {
        self.entry.status = Status::OnHold;
        self.needs_sync = true;
    }

    #[inline(always)]
    pub fn needs_sync(&self) -> bool {
        self.needs_sync
    }

    #[inline(always)]
    pub fn watched_eps(&self) -> u32 {
        self.entry.watched_eps
    }

    #[inline(always)]
    pub fn set_watched_eps(&mut self, watched_eps: u32) {
        self.entry.watched_eps = watched_eps;
        self.needs_sync = true;
    }

    #[inline(always)]
    pub fn score(&self) -> Option<u8> {
        self.entry.score
    }

    #[inline(always)]
    pub fn set_score(&mut self, score: Option<u8>) {
        self.entry.score = score;
        self.needs_sync = true;
    }

    #[inline(always)]
    pub fn status(&self) -> Status {
        self.entry.status
    }

    #[inline(always)]
    pub fn set_status(&mut self, status: Status) {
        self.entry.status = status;
        self.needs_sync = true;
    }

    #[inline(always)]
    pub fn times_rewatched(&self) -> u32 {
        self.entry.times_rewatched
    }

    #[inline(always)]
    pub fn set_times_rewatched(&mut self, times_rewatched: u32) {
        self.entry.times_rewatched = times_rewatched;
        self.needs_sync = true;
    }

    #[inline(always)]
    pub fn start_date(&self) -> Option<NaiveDate> {
        self.entry.start_date
    }

    #[inline(always)]
    pub fn set_start_date(&mut self, start_date: Option<NaiveDate>) {
        self.entry.start_date = start_date;
        self.needs_sync = true;
    }

    #[inline(always)]
    pub fn end_date(&self) -> Option<NaiveDate> {
        self.entry.end_date
    }

    #[inline(always)]
    pub fn set_end_date(&mut self, end_date: Option<NaiveDate>) {
        self.entry.end_date = end_date;
        self.needs_sync = true;
    }
}

impl SaveFile for EntryState {
    fn filename() -> &'static str {
        "entry_state.mpack"
    }

    fn save_dir() -> SaveDir {
        SaveDir::LocalData
    }

    fn file_type() -> FileType {
        FileType::MessagePack
    }
}

impl From<u32> for EntryState {
    fn from(id: u32) -> EntryState {
        let entry = SeriesEntry::new(id);
        EntryState::new(entry)
    }
}

#[derive(Debug)]
pub struct SeriesTracker<'a> {
    pub name: String,
    pub info: Cow<'a, SeriesInfo>,
    pub entry: EntryState,
}

impl<'a> SeriesTracker<'a> {
    pub fn init<I, S>(info: I, name: S) -> Result<SeriesTracker<'a>>
    where
        I: Into<Cow<'a, SeriesInfo>>,
        S: Into<String>,
    {
        let info = info.into();
        let name = name.into();

        let entry = match EntryState::load_with_id(info.id, name.as_ref()) {
            Ok(entry) => entry,
            Err(ref err) if err.is_file_nonexistant() => EntryState::from(info.id),
            Err(err) => return Err(err),
        };

        Ok(SeriesTracker { info, entry, name })
    }

    pub fn begin_watching<R>(&mut self, remote: &R, config: &Config) -> Result<()>
    where
        R: RemoteService + ?Sized,
    {
        let entry = &mut self.entry;
        entry.sync_changes_from_remote(remote, &self.name)?;

        let last_status = entry.status();

        match last_status {
            Status::Watching | Status::Rewatching => {
                // There is an edge case where all episodes have been watched, but the status
                // is still set to watching / rewatching. Here we just start a rewatch
                if entry.watched_eps() >= self.info.episodes {
                    entry.set_status(Status::Rewatching);
                    entry.set_watched_eps(0);

                    if last_status == Status::Rewatching {
                        entry.set_times_rewatched(entry.times_rewatched() + 1);
                    }
                }
            }
            Status::Completed => {
                entry.set_status(Status::Rewatching);
                entry.set_watched_eps(0);
            }
            Status::PlanToWatch => entry.set_status(Status::Watching),
            Status::OnHold | Status::Dropped => {
                entry.set_status(Status::Watching);
                entry.set_watched_eps(0);
            }
        }

        if entry.start_date().is_none()
            || (last_status == Status::Completed
                && entry.status() == Status::Rewatching
                && config.reset_dates_on_rewatch)
        {
            entry.set_start_date(Some(Local::today().naive_local()));
        }

        entry.sync_changes_to_remote(remote, &self.name)?;

        Ok(())
    }

    pub fn episode_completed<R>(&mut self, remote: &R, config: &Config) -> Result<()>
    where
        R: RemoteService + ?Sized,
    {
        let entry = &mut self.entry;
        let new_progress = entry.watched_eps() + 1;

        if new_progress >= self.info.episodes {
            // The watched episode range is inclusive, so it's fine to bump the watched count
            // if we're at exactly at the last episode
            if new_progress == self.info.episodes {
                entry.set_watched_eps(new_progress);
            }

            return self.series_complete(remote, config);
        }

        entry.set_watched_eps(new_progress);
        entry.sync_changes_to_remote(remote, &self.name)
    }

    pub fn episode_regressed<R>(&mut self, remote: &R) -> Result<()>
    where
        R: RemoteService + ?Sized,
    {
        let entry = &mut self.entry;

        entry.set_watched_eps(entry.watched_eps().saturating_sub(1));

        let new_status = match entry.status() {
            Status::Completed if entry.times_rewatched() > 0 => Status::Rewatching,
            Status::Rewatching => Status::Rewatching,
            _ => Status::Watching,
        };

        entry.set_status(new_status);
        entry.sync_changes_to_remote(remote, &self.name)
    }

    pub fn series_complete<R>(&mut self, remote: &R, config: &Config) -> Result<()>
    where
        R: RemoteService + ?Sized,
    {
        let entry = &mut self.entry;

        // A rewatch is typically only counted once the series is completed again
        if entry.status() == Status::Rewatching {
            entry.set_times_rewatched(entry.times_rewatched() + 1);
        }

        if entry.end_date().is_none()
            || (entry.status() == Status::Rewatching && config.reset_dates_on_rewatch)
        {
            entry.set_end_date(Some(Local::today().naive_local()));
        }

        entry.set_status(Status::Completed);
        entry.sync_changes_to_remote(remote, &self.name)?;

        Ok(())
    }
}
<commit_msg>[anup] Don't reset the watched episode count when watching a series that was on hold<commit_after>use crate::config::Config;
use crate::err::Result;
use crate::file::{FileType, SaveDir, SaveFile};
use anime::remote::{RemoteService, SeriesEntry, SeriesInfo, Status};
use chrono::{Local, NaiveDate};
use serde_derive::{Deserialize, Serialize};
use std::borrow::Cow;

#[derive(Debug, Deserialize, Serialize)]
pub struct EntryState {
    entry: SeriesEntry,
    needs_sync: bool,
}

impl EntryState {
    pub fn new(entry: SeriesEntry) -> EntryState {
        EntryState {
            entry,
            needs_sync: false,
        }
    }

    pub fn force_sync_changes_to_remote<R, S>(&mut self, remote: &R, name: S) -> Result<()>
    where
        R: RemoteService + ?Sized,
        S: AsRef<str>,
    {
        if remote.is_offline() {
            self.save_with_id(self.entry.id, name.as_ref())?;
            return Ok(());
        }

        remote.update_list_entry(&self.entry)?;

        self.needs_sync = false;
        self.save_with_id(self.entry.id, name.as_ref())?;

        Ok(())
    }

    pub fn sync_changes_to_remote<R, S>(&mut self, remote: &R, name: S) -> Result<()>
    where
        R: RemoteService + ?Sized,
        S: AsRef<str>,
    {
        if !self.needs_sync {
            return Ok(());
        }

        self.force_sync_changes_to_remote(remote, name)
    }

    pub fn force_sync_changes_from_remote<R, S>(&mut self, remote: &R, name: S) -> Result<()>
    where
        R: RemoteService + ?Sized,
        S: AsRef<str>,
    {
        if remote.is_offline() {
            return Ok(());
        }

        let entry = match remote.get_list_entry(self.entry.id)? {
            Some(entry) => entry,
            None => SeriesEntry::new(self.entry.id),
        };

        self.entry = entry;
        self.needs_sync = false;
        self.save_with_id(self.entry.id, name.as_ref())?;

        Ok(())
    }

    pub fn sync_changes_from_remote<R, S>(&mut self, remote: &R, name: S) -> Result<()>
    where
        R: RemoteService + ?Sized,
        S: AsRef<str>,
    {
        if self.needs_sync {
            return Ok(());
        }

        self.force_sync_changes_from_remote(remote, name)
    }

    pub fn mark_as_dropped(&mut self, config: &Config) {
        if self.end_date().is_none()
            || (self.status() == Status::Rewatching && config.reset_dates_on_rewatch)
        {
            self.entry.end_date = Some(Local::today().naive_local());
        }

        self.entry.status = Status::Dropped;
        self.needs_sync = true;
    }

    pub fn mark_as_on_hold(&mut self) {
        self.entry.status = Status::OnHold;
        self.needs_sync = true;
    }

    #[inline(always)]
    pub fn needs_sync(&self) -> bool {
        self.needs_sync
    }

    #[inline(always)]
    pub fn watched_eps(&self) -> u32 {
        self.entry.watched_eps
    }

    #[inline(always)]
    pub fn set_watched_eps(&mut self, watched_eps: u32) {
        self.entry.watched_eps = watched_eps;
        self.needs_sync = true;
    }

    #[inline(always)]
    pub fn score(&self) -> Option<u8> {
        self.entry.score
    }

    #[inline(always)]
    pub fn set_score(&mut self, score: Option<u8>) {
        self.entry.score = score;
        self.needs_sync = true;
    }

    #[inline(always)]
    pub fn status(&self) -> Status {
        self.entry.status
    }

    #[inline(always)]
    pub fn set_status(&mut self, status: Status) {
        self.entry.status = status;
        self.needs_sync = true;
    }

    #[inline(always)]
    pub fn times_rewatched(&self) -> u32 {
        self.entry.times_rewatched
    }

    #[inline(always)]
    pub fn set_times_rewatched(&mut self, times_rewatched: u32) {
        self.entry.times_rewatched = times_rewatched;
        self.needs_sync = true;
    }

    #[inline(always)]
    pub fn start_date(&self) -> Option<NaiveDate> {
        self.entry.start_date
    }

    #[inline(always)]
    pub fn set_start_date(&mut self, start_date: Option<NaiveDate>) {
        self.entry.start_date = start_date;
        self.needs_sync = true;
    }

    #[inline(always)]
    pub fn end_date(&self) -> Option<NaiveDate> {
        self.entry.end_date
    }

    #[inline(always)]
    pub fn set_end_date(&mut self, end_date: Option<NaiveDate>) {
        self.entry.end_date = end_date;
        self.needs_sync = true;
    }
}

impl SaveFile for EntryState {
    fn filename() -> &'static str {
        "entry_state.mpack"
    }

    fn save_dir() -> SaveDir {
        SaveDir::LocalData
    }

    fn file_type() -> FileType {
        FileType::MessagePack
    }
}

impl From<u32> for EntryState {
    fn from(id: u32) -> EntryState {
        let entry = SeriesEntry::new(id);
        EntryState::new(entry)
    }
}

#[derive(Debug)]
pub struct SeriesTracker<'a> {
    pub name: String,
    pub info: Cow<'a, SeriesInfo>,
    pub entry: EntryState,
}

impl<'a> SeriesTracker<'a> {
    pub fn init<I, S>(info: I, name: S) -> Result<SeriesTracker<'a>>
    where
        I: Into<Cow<'a, SeriesInfo>>,
        S: Into<String>,
    {
        let info = info.into();
        let name = name.into();

        let entry = match EntryState::load_with_id(info.id, name.as_ref()) {
            Ok(entry) => entry,
            Err(ref err) if err.is_file_nonexistant() => EntryState::from(info.id),
            Err(err) => return Err(err),
        };

        Ok(SeriesTracker { info, entry, name })
    }

    pub fn begin_watching<R>(&mut self, remote: &R, config: &Config) -> Result<()>
    where
        R: RemoteService + ?Sized,
    {
        let entry = &mut self.entry;
        entry.sync_changes_from_remote(remote, &self.name)?;

        let last_status = entry.status();

        match last_status {
            Status::Watching | Status::Rewatching => {
                // There is an edge case where all episodes have been watched, but the status
                // is still set to watching / rewatching. Here we just start a rewatch
                if entry.watched_eps() >= self.info.episodes {
                    entry.set_status(Status::Rewatching);
                    entry.set_watched_eps(0);

                    if last_status == Status::Rewatching {
                        entry.set_times_rewatched(entry.times_rewatched() + 1);
                    }
                }
            }
            Status::Completed => {
                entry.set_status(Status::Rewatching);
                entry.set_watched_eps(0);
            }
            Status::PlanToWatch | Status::OnHold => entry.set_status(Status::Watching),
            Status::Dropped => {
                entry.set_status(Status::Watching);
                entry.set_watched_eps(0);
            }
        }

        if entry.start_date().is_none()
            || (last_status == Status::Completed
                && entry.status() == Status::Rewatching
                && config.reset_dates_on_rewatch)
        {
            entry.set_start_date(Some(Local::today().naive_local()));
        }

        entry.sync_changes_to_remote(remote, &self.name)?;

        Ok(())
    }

    pub fn episode_completed<R>(&mut self, remote: &R, config: &Config) -> Result<()>
    where
        R: RemoteService + ?Sized,
    {
        let entry = &mut self.entry;
        let new_progress = entry.watched_eps() + 1;

        if new_progress >= self.info.episodes {
            // The watched episode range is inclusive, so it's fine to bump the watched count
            // if we're at exactly at the last episode
            if new_progress == self.info.episodes {
                entry.set_watched_eps(new_progress);
            }

            return self.series_complete(remote, config);
        }

        entry.set_watched_eps(new_progress);
        entry.sync_changes_to_remote(remote, &self.name)
    }

    pub fn episode_regressed<R>(&mut self, remote: &R) -> Result<()>
    where
        R: RemoteService + ?Sized,
    {
        let entry = &mut self.entry;

        entry.set_watched_eps(entry.watched_eps().saturating_sub(1));

        let new_status = match entry.status() {
            Status::Completed if entry.times_rewatched() > 0 => Status::Rewatching,
            Status::Rewatching => Status::Rewatching,
            _ => Status::Watching,
        };

        entry.set_status(new_status);
        entry.sync_changes_to_remote(remote, &self.name)
    }

    pub fn series_complete<R>(&mut self, remote: &R, config: &Config) -> Result<()>
    where
        R: RemoteService + ?Sized,
    {
        let entry = &mut self.entry;

        // A rewatch is typically only counted once the series is completed again
        if entry.status() == Status::Rewatching {
            entry.set_times_rewatched(entry.times_rewatched() + 1);
        }

        if entry.end_date().is_none()
            || (entry.status() == Status::Rewatching && config.reset_dates_on_rewatch)
        {
            entry.set_end_date(Some(Local::today().naive_local()));
        }

        entry.set_status(Status::Completed);
        entry.sync_changes_to_remote(remote, &self.name)?;

        Ok(())
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! A windowing implementation using GLFW.

use windowing::{ApplicationMethods, WindowEvent, WindowMethods};
use windowing::{IdleWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent, MouseWindowEventClass};
use windowing::{ScrollWindowEvent, ZoomWindowEvent, NavigationWindowEvent, FinishedWindowEvent};
use windowing::{QuitWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent};
use windowing::{Forward, Back};

use alert::{Alert, AlertMethods};
use std::libc::c_int;
use std::local_data;
use geom::point::Point2D;
use geom::size::Size2D;
use servo_msg::compositor_msg::{IdleRenderState, RenderState, RenderingRenderState};
use servo_msg::compositor_msg::{FinishedLoading, Blank, Loading, PerformingLayout, ReadyState};

use glfw;

static THROBBER: [char, ..8] = [ '⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷' ];

/// A structure responsible for setting up and tearing down the entire windowing system.
pub struct Application;

impl ApplicationMethods for Application {
    fn new() -> Application {
        glfw::init();
        Application
    }
}

impl Drop for Application {
    fn drop(&self) {
        glfw::terminate();
        drop_local_window();
    }
}

/// The type of a window.
pub struct Window {
    glfw_window: glfw::Window,

    event_queue: @mut ~[WindowEvent],

    drag_origin: Point2D<c_int>,

    mouse_down_button: @mut c_int,
    mouse_down_point: @mut Point2D<c_int>,

    ready_state: ReadyState,
    render_state: RenderState,
    throbber_frame: u8,
}

impl WindowMethods<Application> for Window {
    /// Creates a new window.
    fn new(_: &Application) -> @mut Window {
        // Create the GLFW window.
        let glfw_window = glfw::Window::create(800, 600, "Servo", glfw::Windowed).unwrap();
        glfw_window.make_context_current();

        // Create our window object.
        let window = @mut Window {
            glfw_window: glfw_window,

            event_queue: @mut ~[],

            drag_origin: Point2D(0 as c_int, 0),

            mouse_down_button: @mut 0,
            mouse_down_point: @mut Point2D(0 as c_int, 0),

            ready_state: Blank,
            render_state: IdleRenderState,
            throbber_frame: 0,
        };

        install_local_window(window);

        // Register event handlers.
        do window.glfw_window.set_framebuffer_size_callback |_win, width, height| {
            local_window().event_queue.push(ResizeWindowEvent(width as uint, height as uint))
        }
        do window.glfw_window.set_key_callback |_win, key, _scancode, action, mods| {
            if action == glfw::PRESS {
                local_window().handle_key(key, mods)
            }
        }
        do window.glfw_window.set_mouse_button_callback |win, button, action, _mods| {
            let (x, y) = win.get_cursor_pos();
            //handle hidpi displays, since GLFW returns non-hi-def coordinates.
            let (backing_size, _) = win.get_framebuffer_size();
            let (window_size, _) = win.get_size();
            let hidpi = (backing_size as f32) / (window_size as f32);
            let x = x as f32 * hidpi;
            let y = y as f32 * hidpi;
            if button < 3 {
                local_window().handle_mouse(button, action, x as i32, y as i32);
            }
        }
        do window.glfw_window.set_scroll_callback |win, x_offset, y_offset| {
            let dx = (x_offset as f32) * 30.0;
            let dy = (y_offset as f32) * 30.0;

            let (x, y) = win.get_cursor_pos();
            //handle hidpi displays, since GLFW returns non-hi-def coordinates.
            let (backing_size, _) = win.get_framebuffer_size();
            let (window_size, _) = win.get_size();
            let hidpi = (backing_size as f32) / (window_size as f32);
            let x = x as f32 * hidpi;
            let y = y as f32 * hidpi;

            local_window().event_queue.push(ScrollWindowEvent(Point2D(dx, dy), Point2D(x as i32, y as i32)));
        }

        window
    }

    /// Returns the size of the window.
    fn size(&self) -> Size2D<f32> {
        let (width, height) = self.glfw_window.get_framebuffer_size();
        Size2D(width as f32, height as f32)
    }

    /// Presents the window to the screen (perhaps by page flipping).
    fn present(&mut self) {
        self.glfw_window.swap_buffers();
    }

    fn recv(@mut self) -> WindowEvent {
        if !self.event_queue.is_empty() {
            return self.event_queue.shift()
        }
        glfw::poll_events();
        self.throbber_frame = (self.throbber_frame + 1) % (THROBBER.len() as u8);
        self.update_window_title();
        if self.glfw_window.should_close() {
            QuitWindowEvent
        } else if !self.event_queue.is_empty() {
            self.event_queue.shift()
        } else {
            IdleWindowEvent
        }
    }

    /// Sets the ready state.
    fn set_ready_state(@mut self, ready_state: ReadyState) {
        self.ready_state = ready_state;
        self.update_window_title()
    }

    /// Sets the render state.
    fn set_render_state(@mut self, render_state: RenderState) {
        if self.ready_state == FinishedLoading &&
            self.render_state == RenderingRenderState &&
            render_state == IdleRenderState {
            // page loaded
            self.event_queue.push(FinishedWindowEvent);
        }

        self.render_state = render_state;
        self.update_window_title()
    }

    fn hidpi_factor(@mut self) -> f32 {
        let (backing_size, _) = self.glfw_window.get_framebuffer_size();
        let (window_size, _) = self.glfw_window.get_size();
        (backing_size as f32) / (window_size as f32)
    }
}

impl Window {
    /// Helper function to set the window title in accordance with the ready state.
    fn update_window_title(&self) {
        let throbber = THROBBER[self.throbber_frame];
        match self.ready_state {
            Blank => {
                self.glfw_window.set_title(fmt!("blank — Servo"));
            }
            Loading => {
                self.glfw_window.set_title(fmt!("%c Loading — Servo", throbber))
            }
            PerformingLayout => {
                self.glfw_window.set_title(fmt!("%c Performing Layout — Servo", throbber))
            }
            FinishedLoading => {
                match self.render_state {
                    RenderingRenderState => {
                        self.glfw_window.set_title(fmt!("%c Rendering — Servo", throbber))
                    }
                    IdleRenderState => self.glfw_window.set_title("Servo"),
                }
            }
        }
    }

    /// Helper function to handle keyboard events.
    fn handle_key(&self, key: c_int, mods: glfw::KeyMods) {
        let mods = *mods;
        match key {
            glfw::KEY_ESCAPE => self.glfw_window.set_should_close(true),
            glfw::KEY_L if mods & glfw::MOD_CONTROL != 0 => self.load_url(), // Ctrl+L
            glfw::KEY_EQUAL if mods & glfw::MOD_CONTROL != 0 => { // Ctrl-+
                self.event_queue.push(ZoomWindowEvent(1.1));
            }
            glfw::KEY_MINUS if mods & glfw::MOD_CONTROL != 0 => { // Ctrl--
                self.event_queue.push(ZoomWindowEvent(0.90909090909));
            }
            glfw::KEY_BACKSPACE if mods & glfw::MOD_SHIFT != 0 => { // Shift-Backspace
                self.event_queue.push(NavigationWindowEvent(Forward));
            }
            glfw::KEY_BACKSPACE => { // Backspace
                self.event_queue.push(NavigationWindowEvent(Back));
            }
            _ => {}
        }
    }

    /// Helper function to handle a click
    fn handle_mouse(&self, button: c_int, action: c_int, x: c_int, y: c_int) {
        // FIXME(tkuehn): max pixel dist should be based on pixel density
        let max_pixel_dist = 10f;
        let event = match action {
            glfw::PRESS => {
                *self.mouse_down_point = Point2D(x, y);
                *self.mouse_down_button = button;
                MouseWindowMouseDownEvent(button as uint, Point2D(x as f32, y as f32))
            }
            glfw::RELEASE => {
                if *self.mouse_down_button == button {
                    let pixel_dist = *self.mouse_down_point - Point2D(x, y);
                    let pixel_dist = ((pixel_dist.x * pixel_dist.x +
                                       pixel_dist.y * pixel_dist.y) as float).sqrt();
                    if pixel_dist < max_pixel_dist {
                        let click_event = MouseWindowClickEvent(button as uint,
                                                           Point2D(x as f32, y as f32));
                        self.event_queue.push(MouseWindowEventClass(click_event));
                    }
                }
                MouseWindowMouseUpEvent(button as uint, Point2D(x as f32, y as f32))
            }
            _ => fail!("I cannot recognize the type of mouse action that occured. :-(")
        };
        self.event_queue.push(MouseWindowEventClass(event));
    }

    /// Helper function to pop up an alert box prompting the user to load a URL.
    fn load_url(&self) {
        let mut alert: Alert = AlertMethods::new("Navigate to:");
        alert.add_prompt();
        alert.run();
        let value = alert.prompt_value();
        if "" == value {    // To avoid crashing on Linux.
            self.event_queue.push(LoadUrlWindowEvent(~"http://purple.com/"))
        } else {
            self.event_queue.push(LoadUrlWindowEvent(value))
        }
    }
}

static TLS_KEY: local_data::Key<@mut Window> = &local_data::Key;

fn install_local_window(window: @mut Window) {
    local_data::set(TLS_KEY, window);
}

fn drop_local_window() {
    local_data::pop(TLS_KEY);
}

fn local_window() -> @mut Window {
    local_data::get(TLS_KEY, |v| *v.unwrap())
}
<commit_msg>auto merge of #968 : luisbg/servo/master, r=larsbergstrom<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! A windowing implementation using GLFW.

use windowing::{ApplicationMethods, WindowEvent, WindowMethods};
use windowing::{IdleWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent, MouseWindowEventClass};
use windowing::{ScrollWindowEvent, ZoomWindowEvent, NavigationWindowEvent, FinishedWindowEvent};
use windowing::{QuitWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent};
use windowing::{Forward, Back};

use alert::{Alert, AlertMethods};
use std::libc::c_int;
use std::local_data;
use geom::point::Point2D;
use geom::size::Size2D;
use servo_msg::compositor_msg::{IdleRenderState, RenderState, RenderingRenderState};
use servo_msg::compositor_msg::{FinishedLoading, Blank, Loading, PerformingLayout, ReadyState};

use glfw;

static THROBBER: [char, ..8] = [ '⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷' ];

/// A structure responsible for setting up and tearing down the entire windowing system.
pub struct Application;

impl ApplicationMethods for Application {
    fn new() -> Application {
        glfw::init();
        Application
    }
}

impl Drop for Application {
    fn drop(&self) {
        glfw::terminate();
        drop_local_window();
    }
}

/// The type of a window.
pub struct Window {
    glfw_window: glfw::Window,

    event_queue: @mut ~[WindowEvent],

    drag_origin: Point2D<c_int>,

    mouse_down_button: @mut c_int,
    mouse_down_point: @mut Point2D<c_int>,

    ready_state: ReadyState,
    render_state: RenderState,
    throbber_frame: u8,
}

impl WindowMethods<Application> for Window {
    /// Creates a new window.
    fn new(_: &Application) -> @mut Window {
        // Create the GLFW window.
        let glfw_window = glfw::Window::create(800, 600, "Servo", glfw::Windowed).unwrap();
        glfw_window.make_context_current();

        // Create our window object.
        let window = @mut Window {
            glfw_window: glfw_window,

            event_queue: @mut ~[],

            drag_origin: Point2D(0 as c_int, 0),

            mouse_down_button: @mut 0,
            mouse_down_point: @mut Point2D(0 as c_int, 0),

            ready_state: Blank,
            render_state: IdleRenderState,
            throbber_frame: 0,
        };

        install_local_window(window);

        // Register event handlers.
        do window.glfw_window.set_framebuffer_size_callback |_win, width, height| {
            local_window().event_queue.push(ResizeWindowEvent(width as uint, height as uint))
        }
        do window.glfw_window.set_key_callback |_win, key, _scancode, action, mods| {
            if action == glfw::PRESS {
                local_window().handle_key(key, mods)
            }
        }
        do window.glfw_window.set_mouse_button_callback |win, button, action, _mods| {
            let (x, y) = win.get_cursor_pos();
            //handle hidpi displays, since GLFW returns non-hi-def coordinates.
            let (backing_size, _) = win.get_framebuffer_size();
            let (window_size, _) = win.get_size();
            let hidpi = (backing_size as f32) / (window_size as f32);
            let x = x as f32 * hidpi;
            let y = y as f32 * hidpi;
            if button < 3 {
                local_window().handle_mouse(button, action, x as i32, y as i32);
            }
        }
        do window.glfw_window.set_scroll_callback |win, x_offset, y_offset| {
            let dx = (x_offset as f32) * 30.0;
            let dy = (y_offset as f32) * 30.0;

            let (x, y) = win.get_cursor_pos();
            //handle hidpi displays, since GLFW returns non-hi-def coordinates.
            let (backing_size, _) = win.get_framebuffer_size();
            let (window_size, _) = win.get_size();
            let hidpi = (backing_size as f32) / (window_size as f32);
            let x = x as f32 * hidpi;
            let y = y as f32 * hidpi;

            local_window().event_queue.push(ScrollWindowEvent(Point2D(dx, dy), Point2D(x as i32, y as i32)));
        }

        window
    }

    /// Returns the size of the window.
    fn size(&self) -> Size2D<f32> {
        let (width, height) = self.glfw_window.get_framebuffer_size();
        Size2D(width as f32, height as f32)
    }

    /// Presents the window to the screen (perhaps by page flipping).
    fn present(&mut self) {
        self.glfw_window.swap_buffers();
    }

    fn recv(@mut self) -> WindowEvent {
        if !self.event_queue.is_empty() {
            return self.event_queue.shift()
        }
        glfw::poll_events();
        self.throbber_frame = (self.throbber_frame + 1) % (THROBBER.len() as u8);
        self.update_window_title(false);
        if self.glfw_window.should_close() {
            QuitWindowEvent
        } else if !self.event_queue.is_empty() {
            self.event_queue.shift()
        } else {
            IdleWindowEvent
        }
    }

    /// Sets the ready state.
    fn set_ready_state(@mut self, ready_state: ReadyState) {
        self.ready_state = ready_state;
        self.update_window_title(true)
    }

    /// Sets the render state.
    fn set_render_state(@mut self, render_state: RenderState) {
        if self.ready_state == FinishedLoading &&
            self.render_state == RenderingRenderState &&
            render_state == IdleRenderState {
            // page loaded
            self.event_queue.push(FinishedWindowEvent);
        }

        self.render_state = render_state;
        self.update_window_title(true)
    }

    fn hidpi_factor(@mut self) -> f32 {
        let (backing_size, _) = self.glfw_window.get_framebuffer_size();
        let (window_size, _) = self.glfw_window.get_size();
        (backing_size as f32) / (window_size as f32)
    }
}

impl Window {
    /// Helper function to set the window title in accordance with the ready state.
    fn update_window_title(&self, state_change: bool) {
        let throbber = THROBBER[self.throbber_frame];
        match self.ready_state {
            Blank => {
                if state_change { self.glfw_window.set_title(fmt!("blank — Servo")) }
            }
            Loading => {
                self.glfw_window.set_title(fmt!("%c Loading — Servo", throbber))
            }
            PerformingLayout => {
                self.glfw_window.set_title(fmt!("%c Performing Layout — Servo", throbber))
            }
            FinishedLoading => {
                match self.render_state {
                    RenderingRenderState => {
                        self.glfw_window.set_title(fmt!("%c Rendering — Servo", throbber))
                    }
                    IdleRenderState => {
                        if state_change { self.glfw_window.set_title("Servo") }
                    }
                }
            }
        }
    }

    /// Helper function to handle keyboard events.
    fn handle_key(&self, key: c_int, mods: glfw::KeyMods) {
        let mods = *mods;
        match key {
            glfw::KEY_ESCAPE => self.glfw_window.set_should_close(true),
            glfw::KEY_L if mods & glfw::MOD_CONTROL != 0 => self.load_url(), // Ctrl+L
            glfw::KEY_EQUAL if mods & glfw::MOD_CONTROL != 0 => { // Ctrl-+
                self.event_queue.push(ZoomWindowEvent(1.1));
            }
            glfw::KEY_MINUS if mods & glfw::MOD_CONTROL != 0 => { // Ctrl--
                self.event_queue.push(ZoomWindowEvent(0.90909090909));
            }
            glfw::KEY_BACKSPACE if mods & glfw::MOD_SHIFT != 0 => { // Shift-Backspace
                self.event_queue.push(NavigationWindowEvent(Forward));
            }
            glfw::KEY_BACKSPACE => { // Backspace
                self.event_queue.push(NavigationWindowEvent(Back));
            }
            _ => {}
        }
    }

    /// Helper function to handle a click
    fn handle_mouse(&self, button: c_int, action: c_int, x: c_int, y: c_int) {
        // FIXME(tkuehn): max pixel dist should be based on pixel density
        let max_pixel_dist = 10f;
        let event = match action {
            glfw::PRESS => {
                *self.mouse_down_point = Point2D(x, y);
                *self.mouse_down_button = button;
                MouseWindowMouseDownEvent(button as uint, Point2D(x as f32, y as f32))
            }
            glfw::RELEASE => {
                if *self.mouse_down_button == button {
                    let pixel_dist = *self.mouse_down_point - Point2D(x, y);
                    let pixel_dist = ((pixel_dist.x * pixel_dist.x +
                                       pixel_dist.y * pixel_dist.y) as float).sqrt();
                    if pixel_dist < max_pixel_dist {
                        let click_event = MouseWindowClickEvent(button as uint,
                                                           Point2D(x as f32, y as f32));
                        self.event_queue.push(MouseWindowEventClass(click_event));
                    }
                }
                MouseWindowMouseUpEvent(button as uint, Point2D(x as f32, y as f32))
            }
            _ => fail!("I cannot recognize the type of mouse action that occured. :-(")
        };
        self.event_queue.push(MouseWindowEventClass(event));
    }

    /// Helper function to pop up an alert box prompting the user to load a URL.
    fn load_url(&self) {
        let mut alert: Alert = AlertMethods::new("Navigate to:");
        alert.add_prompt();
        alert.run();
        let value = alert.prompt_value();
        if "" == value {    // To avoid crashing on Linux.
            self.event_queue.push(LoadUrlWindowEvent(~"http://purple.com/"))
        } else {
            self.event_queue.push(LoadUrlWindowEvent(value))
        }
    }
}

static TLS_KEY: local_data::Key<@mut Window> = &local_data::Key;

fn install_local_window(window: @mut Window) {
    local_data::set(TLS_KEY, window);
}

fn drop_local_window() {
    local_data::pop(TLS_KEY);
}

fn local_window() -> @mut Window {
    local_data::get(TLS_KEY, |v| *v.unwrap())
}
<|endoftext|>