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

//! See the struct `Context` for documentation on how the context is meant to be used.

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

use super::{VertexBufferHandle,IndexBufferHandle,UniformBufferHandle,VertexArrayHandle,ProgramHandle,ShaderHandle};
use super::handle::{new_handle,HandleAccess};
use super::program::{self,Program,ProgramEditor,ProgramInfoAccessor};
use super::shader::{self,Shader,ShaderInfoAccessor,ShaderType};
use super::buffer;
use super::buffer::vertexbuffer::{VertexBuffer,VertexBufferEditor};
use super::buffer::uniformbuffer::{UniformBuffer,UniformBufferEditor};
use super::buffer::indexbuffer::IndexBufferEditor;
use super::vertexarray::{VertexArray,VertexAttribute,VertexAttributeType};
use super::renderer::Renderer;
use super::tracker::{SimpleBindingTracker,RenderBindingTracker,TrackerIdGenerator};
use super::info::{ContextInfo,build_info};

/// Context is a central concept in OpenGL, even though it's not a concrete item in the GL API.
/// This struct is meant to be a stand-in for the GL context, but also the starting point for all
/// the functionality this library offers.
///
/// # Contexts
///
/// Even though you're not prevented from instantiating more than one Contexts, you most likely
/// shouldn't. If you do, and use both contexts when only one actual OpenGL context is active,
/// things will not work as intended. This is an intentional trade-off. As mentioned, OpenGL API
/// does not expose the context in any explicit way, but this is in practice a property of the
/// windowing system. (For example, see wglMakeCurrent and glXMakeCurrent.) In addition, the
/// contexts may very well be managed by a wrapper library, abstracting away all the low-level and
/// platform specific peculiarities. This library would have to either work with only one of them,
/// many of them, or force you to make some kind of integration between this library and the other
/// library, giving the burden to you anyway. So, the main point still is: just don't create more
/// than one Context and you should be fine.
///
/// To create a Context object, call `Context::new()`.
///
/// # Resources
///
/// To create other objects, like vertex buffers or shaders, use the other new_-prefixed functions.
/// For example, to create a vertex buffer, call `ctx.new_vertex_buffer()`.
///
/// The resources are not returned themselves, but are always accessed through handles. This is
/// somewhat analogous with the way OpenGL API itself works. The main difference is that there is
/// no explicit bind method in this library, instead to edit an vertex buffer you call the
/// `edit_vertex_buffer` method of Context:
///
///    let vbo = ctx.new_vertex_buffer();
///    {
///        let editor = ctx.edit_vertex_buffer(&vbo);
///        editor.data(&vertex_data)
///    }
///
/// A shorter version with chaining:
///
///    let vbo = ctx.new_vertex_buffer();
///    ctx.edit_vertex_buffer(&vbo).data(&vertices);
///
/// ## Resource handles
///
/// The handles can be cloned and extend the lifetime of the actual resource in the same way
/// `std::rc::Rc` does. This is because some OpenGL resources may "embed" a resource in themselves.
/// And example of this is vertex arrays and index buffers. An index buffer may be attached to many
/// vertex arrays, and it needs to live as long as any of the vertex arrays live.
///
/// ## Editing is exclusive
///
/// You should notice that the edit_* functions borrow the context mutably, that is, there can
/// exist only one editor object at once. This is to ensure at compile time that the functions that
/// actually edit the one resource (for example `data` in the example above) specified. After all,
/// the underlying API doesn't allow having multiple vertex buffers bound at once, for example.
/// (The rules are more complicated for textures as there are several slots for textures.) The
/// limitation also works as a way to minimize the actual glBind* calls within the library.
///
/// ## Rendering mode
///
/// While binding resources is otherwise not explicitly exposed through the API, there is a
/// "rendering mode" that also borrows the context mutably, thus you can't issue rendering commands
/// and editing commands intertwined, at least not without creating (and destroying) editor and
/// renderer objects in large amounts. You can access the rendering mode by calling the
/// `renderer()` method. The `Renderer` struct has use_* methods that are analogous to the glBind*
/// functions, and also naturally the draw methods et cetera. See the Renderer documentation for
/// more about rendering.
pub struct Context {
    info: ContextInfo,
    id_generator: TrackerIdGenerator,
    program_tracker: RenderBindingTracker<Program>,
    vbo_tracker: SimpleBindingTracker<VertexBuffer>,
    ubo_tracker: SimpleBindingTracker<UniformBuffer>,
    vao_tracker: RenderBindingTracker<VertexArray>,
    shared_state: Rc<RefCell<SharedContextState>>
}

impl Context {
    /// Creates a new Context. Do not create more than one (per actual OpenGL context, anyway).
    ///
    /// See the documentation for the struct for more details.
    pub fn new() -> Context {
        Context {
            info: build_info(),
            id_generator: TrackerIdGenerator::new(),
            program_tracker: RenderBindingTracker::new(),
            vbo_tracker: SimpleBindingTracker::new(),
            ubo_tracker: SimpleBindingTracker::new(),
            vao_tracker: RenderBindingTracker::new(),
            shared_state: Rc::new(RefCell::new(SharedContextState::new()))
        }
    }

    // Construct new objects

    /// Create a new vertex buffer object.
    ///
    /// Returns a handle to the created vertex buffer.
    pub fn new_vertex_buffer(&mut self) -> VertexBufferHandle {
        let registration = self.registration_handle();
        let id = self.id_generator.new_id();
        new_handle(buffer::vertexbuffer::new_vertex_buffer(id, registration))
    }

    /// Create a new index buffer object.
    ///
    /// Returns a handle to the created index buffer.
    pub fn new_index_buffer(&mut self) -> IndexBufferHandle {
        let registration = self.registration_handle();
        let id = self.id_generator.new_id();
        new_handle(buffer::indexbuffer::new_index_buffer(id, registration))
    }

    /// Create a new uniform buffer object.
    ///
    /// Returns a handle to the created uniform buffer.
    pub fn new_uniform_buffer(&mut self) -> UniformBufferHandle {
        let registration = self.registration_handle();
        let id = self.id_generator.new_id();
        new_handle(buffer::uniformbuffer::new_uniform_buffer(id, registration))
    }

    /// Create a new vertex array object.
    ///
    /// See the `glVertexAttribPointer` documentation for how the attributes are specified.
    /// This function takes a slice of vertex attributes at once - the created vertex array
    /// is immutable, you can't change the attributes afterwards!
    ///
    /// If an index buffer should be associated with the vertex array, give a handle to it as the
    /// third argument.
    pub fn new_vertex_array(&mut self,
                            attributes: &[VertexAttribute],
                            index_buffer: Option<IndexBufferHandle>) -> VertexArrayHandle {
        let registration = self.registration_handle();
        let id = self.id_generator.new_id();
        new_handle(VertexArray::new(self, id, attributes, index_buffer, registration))
    }

    /// Create a new vertex array object that only uses contents of one vertex buffer.
    ///
    /// See the `glVertexAttribPointer` documentation for how the attributes slice works.
    /// Otherwise, see the `new_vertex_array` documentation. The only vertex array the vertex
    /// attributes refer to, are specified with the vertex_buffer argument.
    pub fn new_vertex_array_simple(&mut self,
                                   attributes: &[(u8, VertexAttributeType, bool)],
                                   vertex_buffer: VertexBufferHandle,
                                   index_buffer: Option<IndexBufferHandle>) -> VertexArrayHandle {
        let registration = self.registration_handle();
        let id = self.id_generator.new_id();
        new_handle(VertexArray::new_single_vbo(self, id, attributes, vertex_buffer, index_buffer, registration))
    }

    /// Create and compile a new shader object.
    pub fn new_shader(&mut self, shader_type: ShaderType, source: &str) -> ShaderHandle {
        let registration = self.registration_handle();
        new_handle(Shader::new(shader_type, source, registration))
    }

    /// Create and link a shader program from the specified shaders.
    pub fn new_program(&mut self, shaders: &[ShaderHandle]) -> ProgramHandle {
        let registration = self.registration_handle();
        let id = self.id_generator.new_id();
        new_handle(Program::new(id, shaders, registration))
    }

    // Modify object contents with the help of editor objects

    /// Edit a vertex buffer. Returns an editor object that can be used to modify the buffer
    /// contents.
    pub fn edit_vertex_buffer<'a>(&'a mut self, vbo: &'a VertexBufferHandle) -> VertexBufferEditor {
        buffer::vertexbuffer::new_vertex_buffer_editor(self, vbo.access())
    }

    /// Edit an index buffer. Returns an editor object that can be used to modify the buffer
    /// contents.
    ///
    /// Note that this function is given a *vertex array* instead of the index buffer directly.
    /// This is because the core OpenGL specification requires index buffers to be associated with
    /// an vertex array. The returned value is wrapped in an Option, because vertex arrays do not
    /// necessarily contain an index buffer. Still, it would be silly to call this function with a
    /// VAO that does not have an index buffer attached to it.
    pub fn edit_index_buffer<'a>(&'a mut self, vao: &'a VertexArrayHandle) -> Option<IndexBufferEditor> {
        let vao = vao.access();
        match vao.index_buffer() {
            Some(_) => Some(buffer::indexbuffer::new_index_buffer_editor(self, vao)),
            None => None
        }
    }

    pub fn edit_uniform_buffer<'a>(&'a mut self, ubo: &'a UniformBufferHandle) -> UniformBufferEditor {
        buffer::uniformbuffer::new_uniform_buffer_editor(self, ubo.access())
    }

    pub fn edit_program<'a>(&'a mut self, program: &'a ProgramHandle) -> ProgramEditor {
        program::new_program_editor(self, program.access())
    }

    pub fn program_info<'a>(&'a self, program: &'a ProgramHandle) -> ProgramInfoAccessor {
        program::new_program_info_accessor(program.access())
    }

    pub fn shader_info<'a>(&'a self, shader: &'a ShaderHandle) -> ShaderInfoAccessor {
        shader::new_shader_info_accessor(shader.access())
    }

    // Commands that do not (directly) consume resources

    pub fn renderer<'a>(&'a mut self) -> Renderer {
        Renderer::new(self)
    }

    // Expose context info to user too!
    pub fn get_info(&self) -> &ContextInfo {
        &self.info
    }

    // Internal stuff

    fn registration_handle(&self) -> RegistrationHandle {
        RegistrationHandle::new(self.shared_state.clone())
    }
}

#[unsafe_destructor]
impl Drop for Context {
    fn drop(&mut self) {
        self.shared_state.borrow_mut().context_alive = false;
    }
}

pub trait ContextEditingSupport {
    fn bind_vbo_for_editing(&mut self, vbo: &VertexBuffer);
    fn bind_ubo_for_editing(&mut self, vbo: &UniformBuffer);
    fn bind_vao_for_editing(&mut self, vao: &VertexArray);
    fn bind_program_for_editing(&mut self, program: &Program);
}

impl ContextEditingSupport for Context {
    fn bind_vbo_for_editing(&mut self, vbo: &VertexBuffer) {
        self.vbo_tracker.bind(vbo);
    }

    fn bind_ubo_for_editing(&mut self, ubo: &UniformBuffer) {
        self.ubo_tracker.bind(ubo);
    }

    fn bind_vao_for_editing(&mut self, vao: &VertexArray) {
        self.vao_tracker.bind_for_editing(vao);
    }

    fn bind_program_for_editing(&mut self, program: &Program) {
        self.program_tracker.bind_for_editing(program);
    }
}

pub trait ContextRenderingSupport {
    fn bind_vao_for_rendering(&mut self, vao: &VertexArrayHandle);
    fn bind_program_for_rendering(&mut self, program: &ProgramHandle);
    fn prepare_for_rendering(&mut self);
}

impl ContextRenderingSupport for Context {
    fn bind_vao_for_rendering(&mut self, vao: &VertexArrayHandle) {
        self.vao_tracker.bind_for_rendering(vao.rc());
    }

    fn bind_program_for_rendering(&mut self, program: &ProgramHandle) {
        self.program_tracker.bind_for_rendering(program.rc());
    }

    fn prepare_for_rendering(&mut self) {
        self.vao_tracker.restore_rendering_state();
        self.program_tracker.restore_rendering_state();
    }
}

pub struct SharedContextState {
    pub context_alive: bool
}

impl SharedContextState {
    pub fn new() -> SharedContextState {
        SharedContextState {
            context_alive: true
        }
    }
}

pub struct RegistrationHandle {
    context_shared: Rc<RefCell<SharedContextState>>
}

impl RegistrationHandle {
    pub fn new(context_shared: Rc<RefCell<SharedContextState>>) -> RegistrationHandle {
        RegistrationHandle { context_shared: context_shared }
    }

    pub fn context_alive(&self) -> bool {
        self.context_shared.borrow().context_alive
    }
}<commit_msg>Add documentation to mod context.<commit_after>// Copyright 2015 Ilkka Rauta
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! See the struct `Context` for documentation on how the context is meant to be used.

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

use super::{VertexBufferHandle,IndexBufferHandle,UniformBufferHandle,VertexArrayHandle,ProgramHandle,ShaderHandle};
use super::handle::{new_handle,HandleAccess};
use super::program::{self,Program,ProgramEditor,ProgramInfoAccessor};
use super::shader::{self,Shader,ShaderInfoAccessor,ShaderType};
use super::buffer;
use super::buffer::vertexbuffer::{VertexBuffer,VertexBufferEditor};
use super::buffer::uniformbuffer::{UniformBuffer,UniformBufferEditor};
use super::buffer::indexbuffer::IndexBufferEditor;
use super::vertexarray::{VertexArray,VertexAttribute,VertexAttributeType};
use super::renderer::Renderer;
use super::tracker::{SimpleBindingTracker,RenderBindingTracker,TrackerIdGenerator};
use super::info::{ContextInfo,build_info};

/// Context is a central concept in OpenGL, even though it's not a concrete item in the GL API.
/// This struct is meant to be a stand-in for the GL context, but also the starting point for all
/// the functionality this library offers.
///
/// # Contexts
///
/// Even though you're not prevented from instantiating more than one Contexts, you most likely
/// shouldn't. If you do, and use both contexts when only one actual OpenGL context is active,
/// things will not work as intended. This is an intentional trade-off. As mentioned, OpenGL API
/// does not expose the context in any explicit way, but this is in practice a property of the
/// windowing system. (For example, see wglMakeCurrent and glXMakeCurrent.) In addition, the
/// contexts may very well be managed by a wrapper library, abstracting away all the low-level and
/// platform specific peculiarities. This library would have to either work with only one of them,
/// many of them, or force you to make some kind of integration between this library and the other
/// library, giving the burden to you anyway. So, the main point still is: just don't create more
/// than one Context and you should be fine.
///
/// To create a Context object, call `Context::new()`.
///
/// # Resources
///
/// To create other objects, like vertex buffers or shaders, use the other new_-prefixed functions.
/// For example, to create a vertex buffer, call `ctx.new_vertex_buffer()`.
///
/// The resources are not returned themselves, but are always accessed through handles. This is
/// somewhat analogous with the way OpenGL API itself works. The main difference is that there is
/// no explicit bind method in this library, instead to edit an vertex buffer you call the
/// `edit_vertex_buffer` method of Context:
///
///    let vbo = ctx.new_vertex_buffer();
///    {
///        let editor = ctx.edit_vertex_buffer(&vbo);
///        editor.data(&vertex_data)
///    }
///
/// A shorter version with chaining:
///
///    let vbo = ctx.new_vertex_buffer();
///    ctx.edit_vertex_buffer(&vbo).data(&vertices);
///
/// ## Resource handles
///
/// The handles can be cloned and extend the lifetime of the actual resource in the same way
/// `std::rc::Rc` does. This is because some OpenGL resources may "embed" a resource in themselves.
/// And example of this is vertex arrays and index buffers. An index buffer may be attached to many
/// vertex arrays, and it needs to live as long as any of the vertex arrays live.
///
/// ## Editing is exclusive
///
/// You should notice that the edit_* functions borrow the context mutably, that is, there can
/// exist only one editor object at once. This is to ensure at compile time that the functions that
/// actually edit the one resource (for example `data` in the example above) specified. After all,
/// the underlying API doesn't allow having multiple vertex buffers bound at once, for example.
/// (The rules are more complicated for textures as there are several slots for textures.) The
/// limitation also works as a way to minimize the actual glBind* calls within the library.
///
/// ## Rendering mode
///
/// While binding resources is otherwise not explicitly exposed through the API, there is a
/// "rendering mode" that also borrows the context mutably, thus you can't issue rendering commands
/// and editing commands intertwined, at least not without creating (and destroying) editor and
/// renderer objects in large amounts. You can access the rendering mode by calling the
/// `renderer()` method. The `Renderer` struct has use_* methods that are analogous to the glBind*
/// functions, and also naturally the draw methods et cetera. See the Renderer documentation for
/// more about rendering.
pub struct Context {
    info: ContextInfo,
    id_generator: TrackerIdGenerator,
    program_tracker: RenderBindingTracker<Program>,
    vbo_tracker: SimpleBindingTracker<VertexBuffer>,
    ubo_tracker: SimpleBindingTracker<UniformBuffer>,
    vao_tracker: RenderBindingTracker<VertexArray>,
    /// Shared state is a way for context to communicate things to resources - mainly that the
    /// context is alive (or is not)
    shared_state: Rc<RefCell<SharedContextState>>
}

impl Context {
    /// Creates a new Context. Do not create more than one (per actual OpenGL context, anyway).
    ///
    /// See the documentation for the struct for more details.
    pub fn new() -> Context {
        Context {
            info: build_info(),
            id_generator: TrackerIdGenerator::new(),
            program_tracker: RenderBindingTracker::new(),
            vbo_tracker: SimpleBindingTracker::new(),
            ubo_tracker: SimpleBindingTracker::new(),
            vao_tracker: RenderBindingTracker::new(),
            shared_state: Rc::new(RefCell::new(SharedContextState::new()))
        }
    }

    // Construct new objects

    /// Create a new vertex buffer object.
    ///
    /// Returns a handle to the created vertex buffer.
    pub fn new_vertex_buffer(&mut self) -> VertexBufferHandle {
        let registration = self.registration_handle();
        let id = self.id_generator.new_id();
        new_handle(buffer::vertexbuffer::new_vertex_buffer(id, registration))
    }

    /// Create a new index buffer object.
    ///
    /// Returns a handle to the created index buffer.
    pub fn new_index_buffer(&mut self) -> IndexBufferHandle {
        let registration = self.registration_handle();
        let id = self.id_generator.new_id();
        new_handle(buffer::indexbuffer::new_index_buffer(id, registration))
    }

    /// Create a new uniform buffer object.
    ///
    /// Returns a handle to the created uniform buffer.
    pub fn new_uniform_buffer(&mut self) -> UniformBufferHandle {
        let registration = self.registration_handle();
        let id = self.id_generator.new_id();
        new_handle(buffer::uniformbuffer::new_uniform_buffer(id, registration))
    }

    /// Create a new vertex array object.
    ///
    /// See the `glVertexAttribPointer` documentation for how the attributes are specified.
    /// This function takes a slice of vertex attributes at once - the created vertex array
    /// is immutable, you can't change the attributes afterwards!
    ///
    /// If an index buffer should be associated with the vertex array, give a handle to it as the
    /// third argument.
    pub fn new_vertex_array(&mut self,
                            attributes: &[VertexAttribute],
                            index_buffer: Option<IndexBufferHandle>) -> VertexArrayHandle {
        let registration = self.registration_handle();
        let id = self.id_generator.new_id();
        new_handle(VertexArray::new(self, id, attributes, index_buffer, registration))
    }

    /// Create a new vertex array object that only uses contents of one vertex buffer.
    ///
    /// See the `glVertexAttribPointer` documentation for how the attributes slice works.
    /// Otherwise, see the `new_vertex_array` documentation. The only vertex array the vertex
    /// attributes refer to, are specified with the vertex_buffer argument.
    pub fn new_vertex_array_simple(&mut self,
                                   attributes: &[(u8, VertexAttributeType, bool)],
                                   vertex_buffer: VertexBufferHandle,
                                   index_buffer: Option<IndexBufferHandle>) -> VertexArrayHandle {
        let registration = self.registration_handle();
        let id = self.id_generator.new_id();
        new_handle(VertexArray::new_single_vbo(self, id, attributes, vertex_buffer, index_buffer, registration))
    }

    /// Create and compile a new shader object.
    pub fn new_shader(&mut self, shader_type: ShaderType, source: &str) -> ShaderHandle {
        let registration = self.registration_handle();
        new_handle(Shader::new(shader_type, source, registration))
    }

    /// Create and link a shader program from the specified shaders.
    pub fn new_program(&mut self, shaders: &[ShaderHandle]) -> ProgramHandle {
        let registration = self.registration_handle();
        let id = self.id_generator.new_id();
        new_handle(Program::new(id, shaders, registration))
    }

    // Modify object contents with the help of editor objects

    /// Edit a vertex buffer. Returns an editor object that can be used to modify the buffer
    /// contents.
    pub fn edit_vertex_buffer<'a>(&'a mut self, vbo: &'a VertexBufferHandle) -> VertexBufferEditor {
        buffer::vertexbuffer::new_vertex_buffer_editor(self, vbo.access())
    }

    /// Edit an index buffer. Returns an editor object that can be used to modify the buffer
    /// contents.
    ///
    /// Note that this function is given a *vertex array* instead of the index buffer directly.
    /// This is because the core OpenGL specification requires index buffers to be associated with
    /// an vertex array. The returned value is wrapped in an Option, because vertex arrays do not
    /// necessarily contain an index buffer. Still, it would be silly to call this function with a
    /// VAO that does not have an index buffer attached to it.
    pub fn edit_index_buffer<'a>(&'a mut self, vao: &'a VertexArrayHandle) -> Option<IndexBufferEditor> {
        let vao = vao.access();
        match vao.index_buffer() {
            Some(_) => Some(buffer::indexbuffer::new_index_buffer_editor(self, vao)),
            None => None
        }
    }

    /// Edit an uniform buffer. Returns an editor object that can be used to modify the buffer
    /// contents.
    pub fn edit_uniform_buffer<'a>(&'a mut self, ubo: &'a UniformBufferHandle) -> UniformBufferEditor {
        buffer::uniformbuffer::new_uniform_buffer_editor(self, ubo.access())
    }

    /// Lets you edit uniform bindings of a program with the returned editor.
    pub fn edit_program<'a>(&'a mut self, program: &'a ProgramHandle) -> ProgramEditor {
        program::new_program_editor(self, program.access())
    }

    /// Returns and "info accessor" that can figure out the attribute, uniform and fragment data
    /// locations and other related information.
    pub fn program_info<'a>(&'a self, program: &'a ProgramHandle) -> ProgramInfoAccessor {
        program::new_program_info_accessor(program.access())
    }

    /// Returns an "info accessor" that can tell if shader compilation succeeded and return the
    /// compilation info log.
    pub fn shader_info<'a>(&'a self, shader: &'a ShaderHandle) -> ShaderInfoAccessor {
        shader::new_shader_info_accessor(shader.access())
    }

    // Commands that do not (directly) consume resources

    /// Return a renderer object. See `Renderer` documentation for info on usage.
    pub fn renderer<'a>(&'a mut self) -> Renderer {
        Renderer::new(self)
    }

    // Expose context info to user too!

    /// `ContextInfo` contains unchanging values related to the context, like
    /// GL_MAX_UNIFORM_BUFFER_BINDINGS (the path is ContextInfo.uniform_buffer.max_bindings).
    /// Currently definitely limited.
    pub fn get_info(&self) -> &ContextInfo {
        &self.info
    }

    // Internal stuff

    /// Resources get a handle to the shared state
    fn registration_handle(&self) -> RegistrationHandle {
        RegistrationHandle::new(self.shared_state.clone())
    }
}

#[unsafe_destructor]
impl Drop for Context {
    fn drop(&mut self) {
        self.shared_state.borrow_mut().context_alive = false;
    }
}

/// A trait with the purpose to expose only the editing functionality to other types in the
/// library, without exposing all the internals of `Context`. Specifically it facilitates
/// binding of resources *for editing*, something not exposed to outside users.
pub trait ContextEditingSupport {
    fn bind_vbo_for_editing(&mut self, vbo: &VertexBuffer);
    fn bind_ubo_for_editing(&mut self, vbo: &UniformBuffer);
    fn bind_vao_for_editing(&mut self, vao: &VertexArray);
    fn bind_program_for_editing(&mut self, program: &Program);
}

impl ContextEditingSupport for Context {
    fn bind_vbo_for_editing(&mut self, vbo: &VertexBuffer) {
        self.vbo_tracker.bind(vbo);
    }

    fn bind_ubo_for_editing(&mut self, ubo: &UniformBuffer) {
        self.ubo_tracker.bind(ubo);
    }

    fn bind_vao_for_editing(&mut self, vao: &VertexArray) {
        self.vao_tracker.bind_for_editing(vao);
    }

    fn bind_program_for_editing(&mut self, program: &Program) {
        self.program_tracker.bind_for_editing(program);
    }
}

/// See `ContextEditingSupport`. This trait is to expose binding functions used when
/// *rendering* things.
pub trait ContextRenderingSupport {
    fn bind_vao_for_rendering(&mut self, vao: &VertexArrayHandle);
    fn bind_program_for_rendering(&mut self, program: &ProgramHandle);
    fn prepare_for_rendering(&mut self);
}

impl ContextRenderingSupport for Context {
    fn bind_vao_for_rendering(&mut self, vao: &VertexArrayHandle) {
        self.vao_tracker.bind_for_rendering(vao.rc());
    }

    fn bind_program_for_rendering(&mut self, program: &ProgramHandle) {
        self.program_tracker.bind_for_rendering(program.rc());
    }

    fn prepare_for_rendering(&mut self) {
        self.vao_tracker.restore_rendering_state();
        self.program_tracker.restore_rendering_state();
    }
}

/// Things that need to be shared between `Context` and the resources it spawns.
/// This might be a bad idea, but allows the resource handles to live longer than the context,
/// without causing freeing of GL resources after GL context has died. Alternative would have been
/// to limit lifetimes of resource handles to strictly live within the lifetime of the context, but
/// that would "infect" everything with a lifetime annotation...
pub struct SharedContextState {
    pub context_alive: bool
}

impl SharedContextState {
    pub fn new() -> SharedContextState {
        SharedContextState {
            context_alive: true
        }
    }
}

/// Handle to the shared state, as used by the resources (and `Context`).
pub struct RegistrationHandle {
    context_shared: Rc<RefCell<SharedContextState>>
}

impl RegistrationHandle {
    pub fn new(context_shared: Rc<RefCell<SharedContextState>>) -> RegistrationHandle {
        RegistrationHandle { context_shared: context_shared }
    }

    pub fn context_alive(&self) -> bool {
        self.context_shared.borrow().context_alive
    }
}<|endoftext|>


<commit_before>// The CPU
#![allow(dead_code)]
#![allow(non_snake_case)]
mod opcodes;
use utils;
use std::mem;

struct Memory
{
    bytes: [u8;65536]
}

impl Memory
{
    pub fn new() -> Memory
    {
        Memory
        {
            bytes: [0;65536]
        }
    }

    pub fn reset(&mut self)
    {
        self.write_byte_be(0x0000, 0xFF);
        self.write_byte_be(0x0001, 0x07);
    }

    // Write to memory using little endian memory address
    pub fn write_byte_le(&mut self, addr_le: u16, value: u8)
    {
        let addr_be = ((addr_le << 8) & 0xFF) | ((addr_le >> 8) & 0xFF);
        self.bytes[addr_be as usize] = value;
    }

    // Read from memory using little endian memory address
    pub fn read_byte_le(&mut self, addr_le: u16) -> u8
    {
        let addr_be = ((addr_le << 8) & 0xFF) | ((addr_le >> 8) & 0xFF);
        self.bytes[addr_be as usize]
    }

    // Write to memory using big endian memory address
    pub fn write_byte_be(&mut self, addr_be: u16, value: u8)
    {
        self.bytes[addr_be as usize] = value;
    }

    // Read from memory using big endian memory address
    pub fn read_byte_be(&mut self, addr_be: u16) -> u8
    {
        self.bytes[addr_be as usize]
    }

    // Read a word from memory, convert it to little endian
    pub fn read_word_lele(&mut self, addr_le: u16) -> u16
    {
        let addr_be = ((addr_le << 8) & 0xFF) | ((addr_le >> 8) & 0xFF);
        let value_be: u16 = ((self.bytes[addr_be as usize] as u16) << 8 & 0xFF00) |
                            ((self.bytes[(addr_be+1) as usize] as u16) & 0x00FF);

        let value_le: u16 = ((value_be << 8) & 0xFF00) | ((value_be >> 8) & 0x00FF);
        value_le
    }

    pub fn read_word_bele(&mut self, addr_be: u16) -> u16
    {
        let value_be: u16 = ((self.bytes[addr_be as usize] as u16) << 8 & 0xFF00) |
                            ((self.bytes[(addr_be+1) as usize] as u16) & 0x00FF);

        let value_le: u16 = ((value_be << 8) & 0xFF00) | ((value_be >> 8) & 0x00FF);
        value_le
    }

    pub fn read_word_bebe(&mut self, addr_be: u16) -> u16
    {
        let value_be: u16 = ((self.bytes[addr_be as usize] as u16) << 8 & 0xFF00) |
                            ((self.bytes[(addr_be+1) as usize] as u16) & 0x00FF);
        value_be
    }

    pub fn read_word_lebe(&mut self, addr_le: u16) -> u16
    {
        let addr_be = ((addr_le << 8) & 0xFF) | ((addr_le >> 8) & 0xFF);
        let value_be: u16 = ((self.bytes[addr_be as usize] as u16) << 8 & 0xFF00) |
                            ((self.bytes[(addr_be+1) as usize] as u16) & 0x00FF);
        value_be
    }

    pub fn write_word_bele(&mut self, addr_be: u16, value_be: u16)
    {
        let value_le_hi: u8 = (((value_be << 8) & 0xFF00) >> 8 & 0xFF) as u8;
        let value_le_lo: u8 = ((value_be >> 8) & 0x00FF) as u8;

        self.bytes[addr_be as usize] = value_le_hi;
        self.bytes[(addr_be + 0x01) as usize] = value_le_lo;
    }

}

// status flags for P register
enum StatusFlag
{
    N = 1 << 0, // negative flag
    V = 1 << 1, // overflow flag
    X = 1 << 2, // unused flag
    B = 1 << 3, // break flag
    D = 1 << 4, // decimal mode flag
    I = 1 << 5, // interrupt disable flag
    Z = 1 << 6, // zero flag
    C = 1 << 7  // carry flag
}

pub struct CPU
{
    PC: u16, // program counter
    SP: u8,  // stack pointer
    P: u8,   // processor status
    A: u8,   // accumulator
    X: u8,   // index register
    Y: u8,   // index register
    mem: Memory
}

impl CPU
{
    pub fn new() -> CPU
    {
        CPU
        {
            PC: 0,
            SP: 0xFF,
            P: 0,
            A: 0,
            X: 0,
            Y: 0,
            mem: Memory::new()
        }
    }

    pub fn reset(&mut self)
    {
        // set the registers to initial state on power up
        self.mem.reset();

        // load basic
        let mut startAddress: u32 = 0xA000;
        let basic = utils::open_file("rom/basic.rom");

        for (i,addr) in (startAddress..0xC000).enumerate()
        {
            self.mem.write_byte_be(addr as u16, basic[i as usize]);
        }

        // load chargen
        startAddress = 0xD000;
        let chargen = utils::open_file("rom/chargen.rom");

        for (i,addr) in (startAddress..0xE000).enumerate()
        {
            self.mem.write_byte_be(addr as u16, chargen[i as usize]);
        }

        // load kernal
        startAddress = 0xE000;
        let kernal = utils::open_file("rom/kernal.rom");

        for (i,addr) in (startAddress..0x10000).enumerate()
        {
            self.mem.write_byte_be(addr as u16, kernal[i as usize]);
        }

        // reset program counter
        self.PC = self.mem.read_word_bele(0xFFFC);
    }

    pub fn update(&mut self)
    {
        let op = self.fetch_op();
        self.process_op(op);
        //self.process_op(15);
        //self.process_op(16);
        // process opcodes, to the cpu stuff
        //self.mem.bytes[0] = 1;

        //for i in (0..65536)
        //{
        //println!("{}", self.mem.bytes[i]);
        //}
    }

    fn fetch_op(&mut self) -> u8
    {
        let op = self.mem.read_byte_be(self.PC);
        self.PC += 1;
        op
    }

    // stack memory: $0100 - $01FF (256 byes)
    fn push_byte(&mut self, value: u8)
    {
        self.SP -= 0x01;

        if self.SP == 0xFF
            { panic!("Stack underflow"); }

        self.mem.write_byte_be(0x0100 + ((self.SP + 0x01) as u16) & 0x00FF, value);
    }

    fn pop_byte(&mut self) -> u8
    {
        let value = self.mem.read_byte_be(0x0100 + ((self.SP + 0x01) as u16) & 0x00FF);
        self.PC += 0x01;

        if self.SP == 0x00
            { panic!("Stack overflow"); }

        value
    }

    fn push_word(&mut self, value: u16)
    {
        self.SP -= 0x02;

        if self.SP == 0xFF || self.SP == 0xFE
            { panic!("Stack underflow"); }

        self.mem.write_word_bele(0x0100 + ((self.SP + 0x01) as u16) & 0x00FF, value);
    }

    fn pop_word(&mut self) -> u16
    {
        let value = self.mem.read_word_bele(0x0100 + ((self.SP + 0x01) as u16) & 0x00FF);
        self.PC += 0x02;

        if self.SP == 0x00 || self.SP == 0x01
            { panic!("Stack overflow"); }

        value
    }

    fn u8_to_enum(v: u8) -> opcodes::Opcodes
    {
        unsafe { mem::transmute(v) }
    }

    fn mem_dump(&mut self)
    {
        for i in (0..0x10000)
        {
            let val = self.mem.read_byte_be(i as u16);
            if val != 0
                { println!("Addr: ${:04X} -> 0x{:02X}", i, val); }
        }
    }

    fn process_op(&self, opcode: u8)
    {
        use cpu::opcodes::Opcodes;
        match CPU::u8_to_enum(opcode)
        {
            Opcodes::BRK => println!("TODO: {}", opcode),
            Opcodes::NOP_zp   => (),
            Opcodes::NOP_abs  => (),
            Opcodes::HLT1 => panic!("Received HLT1 instruction: 0x{:02X} at ${:02X}", opcode, self.PC),
            Opcodes::NOP_zpx  => (),
            Opcodes::NOP0     => (),
            Opcodes::NOP_abx  => (),
            Opcodes::HLT2 => panic!("Received HLT2 instruction: 0x{:02X} at ${:04X}", opcode, self.PC),
            Opcodes::HLT3 => panic!("Received HLT3 instruction: 0x{:02X} at ${:04X}", opcode, self.PC),
            Opcodes::NOP_zpx2 => (),
            Opcodes::NOP1     => (),
            Opcodes::NOP_abx2 => (),
            Opcodes::HLT4 => panic!("Received HLT4 instruction: 0x{:02X} at ${:04X}", opcode, self.PC),
            Opcodes::NOP2     => (),
            Opcodes::HLT5 => panic!("Received HLT5 instruction: 0x{:02X} at ${:04X}", opcode, self.PC),
            Opcodes::NOP_zpx3 => (),
            Opcodes::NOP3     => (),
            Opcodes::NOP_abx3 => (),
            Opcodes::HLT6 => panic!("Received HLT6 instruction: 0x{:02X} at ${:04X}", opcode, self.PC),
            Opcodes::NOP_zp2  => (),
            Opcodes::HLT7 => panic!("Received HLT7 instruction: 0x{:02X} at ${:04X}", opcode, self.PC),
            Opcodes::NOP_zpx4 => (),
            Opcodes::NOP4     => (),
            Opcodes::NOP_abx4 => (),
            Opcodes::NOP_imm  => (),
            Opcodes::NOP_imm2 => (),
            Opcodes::NOP_imm3 => (),
            Opcodes::HLT8 => panic!("Received HLT8 instruction: 0x{:02X} at ${:04X}", opcode, self.PC),
            Opcodes::HLT9 => panic!("Received HLT9 instruction: 0x{:02X} at ${:04X}", opcode, self.PC),
            Opcodes::NOP_imm4 => (),
            Opcodes::HLT10 => panic!("Received HLT10 instruction: 0x{:02X} at ${:04X}", opcode, self.PC),
            Opcodes::NOP_zpx5 => (),
            Opcodes::NOP5     => (),
            Opcodes::NOP_abx5 => (),
            Opcodes::NOP_imm5 => (),
            Opcodes::NOP      => (),
            Opcodes::HLT11 => panic!("Received HLT11 instruction: 0x{:02X} at ${:04X}", opcode, self.PC),
            Opcodes::NOP_zpx6 => (),
            Opcodes::NOP6     => (),
            Opcodes::NOP_abx6 => (),
            _ => println!("Unknown opcode: 0x{:02X} at ${:04X}", opcode, self.PC)
        }
    }
}
<commit_msg>change mem read/writes a bit<commit_after>// The CPU
#![allow(dead_code)]
#![allow(non_snake_case)]
mod opcodes;
use utils;
use std::mem;

struct Memory
{
    bytes: [u8;65536]
}

impl Memory
{
    pub fn new() -> Memory
    {
        Memory
        {
            bytes: [0;65536]
        }
    }

    pub fn reset(&mut self)
    {
        self.write_byte(0x0000, 0xFF);
        self.write_byte(0x0001, 0x07);
    }

    // Write to memory
    pub fn write_byte(&mut self, addr: u16, value: u8)
    {
        self.bytes[addr as usize] = value;
    }

    // Read from memory
    pub fn read_byte(&mut self, addr: u16) -> u8
    {
        self.bytes[addr as usize]
    }

    // Read a word from memory, convert it to little endian
    pub fn read_word_le(&mut self, addr: u16) -> u16
    {
        let value_be: u16 = ((self.bytes[addr as usize] as u16) << 8 & 0xFF00) |
                            ((self.bytes[(addr + 0x0001) as usize] as u16) & 0x00FF);

        let value_le: u16 = ((value_be << 8) & 0xFF00) | ((value_be >> 8) & 0x00FF);
        value_le
    }

    // read a word from memory as big endian
    pub fn read_word_be(&mut self, addr: u16) -> u16
    {
        let value_be: u16 = ((self.bytes[addr as usize] as u16) << 8 & 0xFF00) |
                            ((self.bytes[(addr + 0x0001) as usize] as u16) & 0x00FF);
        value_be
    }

    pub fn write_word_le(&mut self, addr: u16, value: u16)
    {
        let value_le_lo: u8 = (((value << 8) & 0xFF00) >> 8 & 0xFF) as u8;
        let value_le_hi: u8 = ((value >> 8) & 0x00FF) as u8;

        self.bytes[addr as usize] = value_le_lo;
        self.bytes[(addr + 0x0001) as usize] = value_le_hi;
    }

    pub fn write_word_be(&mut self, addr: u16, value: u16)
    {
        let value_le_lo: u8 = (((value << 8) & 0xFF00) >> 8 & 0xFF) as u8;
        let value_le_hi: u8 = ((value >> 8) & 0x00FF) as u8;

        self.bytes[addr as usize] = value_le_hi;
        self.bytes[(addr + 0x0001) as usize] = value_le_lo;
    }
}

// status flags for P register
enum StatusFlag
{
    N = 1 << 0, // negative flag
    V = 1 << 1, // overflow flag
    X = 1 << 2, // unused flag
    B = 1 << 3, // break flag
    D = 1 << 4, // decimal mode flag
    I = 1 << 5, // interrupt disable flag
    Z = 1 << 6, // zero flag
    C = 1 << 7  // carry flag
}

pub struct CPU
{
    PC: u16, // program counter
    SP: u8,  // stack pointer
    P: u8,   // processor status
    A: u8,   // accumulator
    X: u8,   // index register
    Y: u8,   // index register
    mem: Memory
}

impl CPU
{
    pub fn new() -> CPU
    {
        CPU
        {
            PC: 0,
            SP: 0xFF,
            P: 0,
            A: 0,
            X: 0,
            Y: 0,
            mem: Memory::new()
        }
    }

    pub fn reset(&mut self)
    {
        // set the registers to initial state on power up
        self.mem.reset();

        // load basic
        let mut startAddress: u32 = 0xA000;
        let basic = utils::open_file("rom/basic.rom");

        for (i,addr) in (startAddress..0xC000).enumerate()
        {
            self.mem.write_byte(addr as u16, basic[i as usize]);
        }

        // load chargen
        startAddress = 0xD000;
        let chargen = utils::open_file("rom/chargen.rom");

        for (i,addr) in (startAddress..0xE000).enumerate()
        {
            self.mem.write_byte(addr as u16, chargen[i as usize]);
        }

        // load kernal
        startAddress = 0xE000;
        let kernal = utils::open_file("rom/kernal.rom");

        for (i,addr) in (startAddress..0x10000).enumerate()
        {
            self.mem.write_byte(addr as u16, kernal[i as usize]);
        }

        // reset program counter
        self.PC = self.mem.read_word_le(0xFFFC);
    }

    pub fn update(&mut self)
    {
        let op = self.fetch_op();
        self.process_op(op);
        //self.process_op(15);
        //self.process_op(16);
        // process opcodes, to the cpu stuff
        //self.mem.bytes[0] = 1;

        //for i in (0..65536)
        //{
        //println!("{}", self.mem.bytes[i]);
        //}
    }

    fn fetch_op(&mut self) -> u8
    {
        let op = self.mem.read_byte(self.PC);
        self.PC += 1;
        op
    }

    // stack memory: $0100 - $01FF (256 byes)
    fn push_byte(&mut self, value: u8)
    {
        self.SP -= 0x01;

        if self.SP == 0xFF
            { panic!("Stack underflow"); }

        self.mem.write_byte(0x0100 + ((self.SP + 0x01) as u16) & 0x00FF, value);
    }

    fn pop_byte(&mut self) -> u8
    {
        let value = self.mem.read_byte(0x0100 + ((self.SP + 0x01) as u16) & 0x00FF);
        self.PC += 0x01;

        if self.SP == 0x00
            { panic!("Stack overflow"); }

        value
    }

    fn push_word(&mut self, value: u16)
    {
        self.SP -= 0x02;

        if self.SP == 0xFF || self.SP == 0xFE
            { panic!("Stack underflow"); }

        self.mem.write_word_le(0x0100 + ((self.SP + 0x01) as u16) & 0x00FF, value);
    }

    fn pop_word(&mut self) -> u16
    {
        let value = self.mem.read_word_le(0x0100 + ((self.SP + 0x01) as u16) & 0x00FF);
        self.PC += 0x02;

        if self.SP == 0x00 || self.SP == 0x01
            { panic!("Stack overflow"); }

        value
    }

    fn u8_to_enum(v: u8) -> opcodes::Opcodes
    {
        unsafe { mem::transmute(v) }
    }

    fn mem_dump(&mut self)
    {
        for i in (0..0x10000)
        {
            let val = self.mem.read_byte(i as u16);
            if val != 0
                { println!("Addr: ${:04X} -> 0x{:02X}", i, val); }
        }
    }

    fn process_op(&self, opcode: u8)
    {
        use cpu::opcodes::Opcodes;
        match CPU::u8_to_enum(opcode)
        {
            Opcodes::BRK => println!("TODO: {}", opcode),
            Opcodes::NOP_zp   => (),
            Opcodes::NOP_abs  => (),
            Opcodes::HLT1 => panic!("Received HLT1 instruction: 0x{:02X} at ${:02X}", opcode, self.PC),
            Opcodes::NOP_zpx  => (),
            Opcodes::NOP0     => (),
            Opcodes::NOP_abx  => (),
            Opcodes::HLT2 => panic!("Received HLT2 instruction: 0x{:02X} at ${:04X}", opcode, self.PC),
            Opcodes::HLT3 => panic!("Received HLT3 instruction: 0x{:02X} at ${:04X}", opcode, self.PC),
            Opcodes::NOP_zpx2 => (),
            Opcodes::NOP1     => (),
            Opcodes::NOP_abx2 => (),
            Opcodes::HLT4 => panic!("Received HLT4 instruction: 0x{:02X} at ${:04X}", opcode, self.PC),
            Opcodes::NOP2     => (),
            Opcodes::HLT5 => panic!("Received HLT5 instruction: 0x{:02X} at ${:04X}", opcode, self.PC),
            Opcodes::NOP_zpx3 => (),
            Opcodes::NOP3     => (),
            Opcodes::NOP_abx3 => (),
            Opcodes::HLT6 => panic!("Received HLT6 instruction: 0x{:02X} at ${:04X}", opcode, self.PC),
            Opcodes::NOP_zp2  => (),
            Opcodes::HLT7 => panic!("Received HLT7 instruction: 0x{:02X} at ${:04X}", opcode, self.PC),
            Opcodes::NOP_zpx4 => (),
            Opcodes::NOP4     => (),
            Opcodes::NOP_abx4 => (),
            Opcodes::NOP_imm  => (),
            Opcodes::NOP_imm2 => (),
            Opcodes::NOP_imm3 => (),
            Opcodes::HLT8 => panic!("Received HLT8 instruction: 0x{:02X} at ${:04X}", opcode, self.PC),
            Opcodes::HLT9 => panic!("Received HLT9 instruction: 0x{:02X} at ${:04X}", opcode, self.PC),
            Opcodes::NOP_imm4 => (),
            Opcodes::HLT10 => panic!("Received HLT10 instruction: 0x{:02X} at ${:04X}", opcode, self.PC),
            Opcodes::NOP_zpx5 => (),
            Opcodes::NOP5     => (),
            Opcodes::NOP_abx5 => (),
            Opcodes::NOP_imm5 => (),
            Opcodes::NOP      => (),
            Opcodes::HLT11 => panic!("Received HLT11 instruction: 0x{:02X} at ${:04X}", opcode, self.PC),
            Opcodes::NOP_zpx6 => (),
            Opcodes::NOP6     => (),
            Opcodes::NOP_abx6 => (),
            _ => println!("Unknown opcode: 0x{:02X} at ${:04X}", opcode, self.PC)
        }
    }
}
<|endoftext|>


<commit_before>use super::*;
use crate::ram::cell::*;
use crate::ram::chunk::Chunks;
use crate::ram::entry::{EntryContent, EntryType};
use crate::ram::schema::Field;
use crate::ram::schema::*;
use crate::ram::types::*;
use crate::server::ServerMeta;
use lightning::map::Map;
use env_logger;
use std;
use std::collections::HashSet;
use std::sync::Arc;

pub const DATA_SIZE: usize = 1000 * 1024; // nearly 1MB
const MAX_SEGMENT_SIZE: usize = 8 * 1024 * 1024;

fn default_cell(id: &Id) -> Cell {
    let data: Vec<_> = std::iter::repeat(id.lower as u8).take(DATA_SIZE).collect();
    Cell {
        header: CellHeader::new(0, 0, id),
        data: data_map_value!(id: id.lower as i32, data: data),
    }
}

fn default_fields() -> Field {
    Field::new(
        "*",
        0,
        false,
        false,
        Some(vec![
            Field::new("id", type_id_of(Type::I32), false, false, None, vec![]),
            Field::new("data", type_id_of(Type::U8), false, true, None, vec![]),
        ]),
        vec![],
    )
}

#[test]
pub fn full_clean_cycle() {
    let _ = env_logger::try_init();
    let schema = Schema::new("cleaner_test", None, default_fields(), false);
    let schemas = LocalSchemasCache::new_local("");
    schemas.new_schema(schema);
    let chunks = Chunks::new(
        1,                    // single chunk
        MAX_SEGMENT_SIZE * 2, // chunk two segments
        Arc::new(ServerMeta { schemas }),
        None,
        None,
        None,
    );
    let chunk = &chunks.list[0];

    // provision test data
    {
        assert_eq!(chunk.segments().len(), 1);

        // put 16 cells to fill up all of those segments allocated
        for i in 0..16 {
            let mut cell = default_cell(&Id::new(0, i));
            chunks.write_cell(&mut cell).unwrap();
        }

        assert_eq!(chunk.segments().len(), 2);
        assert_eq!(chunk.cell_index.len(), 16);

        println!("trying to delete cells");

        assert_eq!(chunk.seg_count(), 2);
        assert_eq!(chunk.cell_count(), 16);

        for i in 0..8 {
            chunks.remove_cell(&Id::new(0, i * 2)).unwrap();
        }

        assert_eq!(chunk.segments().len(), 2);

        //count entries, including dead ones
        assert_eq!(chunk.segs.get(&0).unwrap().entry_iter().count(), 8); // all 8 cells
        assert_eq!(chunk.segs.get(&1).unwrap().entry_iter().count(), 16); // 8 cells and 8 tombstones

        // try to scan first segment expect no panic
        println!("Scanning first segment...");
        chunk
            .live_entries(&chunk.segs.get(&0).unwrap())
            .for_each(|_| {});

        println!("Scanning second segment for tombstones...");
        let seg = &chunk.segs.get(&1).unwrap();
        let live_entries = chunk.live_entries(seg);
        let tombstones: Vec<_> = live_entries
            .filter(|e| e.meta.entry_header.entry_type == EntryType::TOMBSTONE)
            .collect();
        assert_eq!(tombstones.len(), 8);
        for i in 0..tombstones.len() {
            let hash = (i * 2) as u64;
            let e = &tombstones[i];
            assert_eq!(e.meta.entry_header.entry_type, EntryType::TOMBSTONE);
            if let EntryContent::Tombstone(ref t) = e.content {
                assert_eq!(t.hash, hash);
                assert_eq!(t.partition, 0);
            } else {
                panic!();
            }
        }

        assert_eq!(chunk.cell_count(), 8);
    }

    // check integrity
    let _ = chunk
        .live_entries(&chunk.segs.get(&0).unwrap())
        .collect::<Vec<_>>();
    let _ = chunk
        .live_entries(&chunk.segs.get(&1).unwrap())
        .collect::<Vec<_>>();

    // compact
    {
        // Compact all segments order by id
        chunk.segments().into_iter().for_each(|seg| {
            compact::CompactCleaner::clean_segment(chunk, &seg);
        });

        assert_eq!(chunk.seg_count(), 2);
        assert_eq!(chunk.cell_count(), 8);

        // scan segments to check entries
        let seg0 = &chunk.segs.get(&0).unwrap();
        let seg1 = &chunk.segs.get(&1).unwrap();
        let compacted_segment_0_entries = chunk.live_entries(seg0).collect::<Vec<_>>();
        let compacted_segment_1_entries = chunk.live_entries(seg1).collect::<Vec<_>>();
        let compacted_segment_0_ids = (0..4).map(|num| num as u64 * 2 + 1);
        let compacted_segment_1_ids = (4..8)
            .map(|num| num as i64 * 2 + 1)
            .chain((0..8).map(|i| -1 * i * 2));
        assert_eq!(seg0.id, 0);
        assert_eq!(seg1.id, 1);
        // check for cells continuity
        compacted_segment_0_entries
            .iter()
            .zip(compacted_segment_0_ids)
            .for_each(|(entry, hash)| {
                assert_eq!(entry.meta.entry_header.entry_type, EntryType::CELL);
                if let EntryContent::Cell(header) = entry.content {
                    assert_eq!(header.hash, hash)
                } else {
                    panic!();
                }
            });
        assert_eq!(compacted_segment_0_entries.len(), 4);
        assert_eq!(seg0.entry_iter().count(), 4);

        // check for cells and 4 tombstones
        compacted_segment_1_entries
            .iter()
            .zip(compacted_segment_1_ids)
            .for_each(|(entry, hash)| {
                if hash > 1 {
                    // cell
                    assert_eq!(entry.meta.entry_header.entry_type, EntryType::CELL);
                    if let EntryContent::Cell(header) = entry.content {
                        assert_eq!(header.hash, hash as u64)
                    } else {
                        panic!();
                    }
                } else {
                    // tombstone
                    assert_eq!(entry.meta.entry_header.entry_type, EntryType::TOMBSTONE);
                    let tombstone = if let EntryContent::Tombstone(ref tombstone) = entry.content {
                        tombstone
                    } else {
                        panic!()
                    };
                    assert_eq!((hash * -1) as u64, tombstone.hash);
                }
            });
        // 4 remaining cells and 8 deleted cell tombstones
        assert_eq!(compacted_segment_1_entries.len(), 12);
        assert_eq!(seg1.entry_iter().count(), 12);
    }

    // combine
    {
        combine::CombinedCleaner::combine_segments(chunk, &chunk.segments());
        let survival_cells: HashSet<_> = chunk
            .live_entries(&chunk.segments()[0])
            .map(|entry| {
                assert_eq!(entry.meta.entry_header.entry_type, EntryType::CELL);
                if let EntryContent::Cell(ref header) = entry.content {
                    return header.hash;
                } else {
                    panic!()
                }
            })
            .collect();
        (0..8)
            .map(|n| n as u64 * 2 + 1)
            .for_each(|hash| assert!(survival_cells.contains(&hash)));
        assert_eq!(chunk.segments().len(), 1);
        assert_eq!(survival_cells.len(), 8);
        assert_eq!(chunk.segments()[0].entry_iter().count(), 8);
    }

    // validate cells
    (0..8).map(|n| n * 2 + 1).for_each(|id| {
        let id = Id::new(0, id);
        let cell = chunks.read_cell(&id).unwrap();
        assert_eq!(cell.data, default_cell(&id).data);
    });
}
<commit_msg>cargo fmt<commit_after>use super::*;
use crate::ram::cell::*;
use crate::ram::chunk::Chunks;
use crate::ram::entry::{EntryContent, EntryType};
use crate::ram::schema::Field;
use crate::ram::schema::*;
use crate::ram::types::*;
use crate::server::ServerMeta;
use env_logger;
use lightning::map::Map;
use std;
use std::collections::HashSet;
use std::sync::Arc;

pub const DATA_SIZE: usize = 1000 * 1024; // nearly 1MB
const MAX_SEGMENT_SIZE: usize = 8 * 1024 * 1024;

fn default_cell(id: &Id) -> Cell {
    let data: Vec<_> = std::iter::repeat(id.lower as u8).take(DATA_SIZE).collect();
    Cell {
        header: CellHeader::new(0, 0, id),
        data: data_map_value!(id: id.lower as i32, data: data),
    }
}

fn default_fields() -> Field {
    Field::new(
        "*",
        0,
        false,
        false,
        Some(vec![
            Field::new("id", type_id_of(Type::I32), false, false, None, vec![]),
            Field::new("data", type_id_of(Type::U8), false, true, None, vec![]),
        ]),
        vec![],
    )
}

#[test]
pub fn full_clean_cycle() {
    let _ = env_logger::try_init();
    let schema = Schema::new("cleaner_test", None, default_fields(), false);
    let schemas = LocalSchemasCache::new_local("");
    schemas.new_schema(schema);
    let chunks = Chunks::new(
        1,                    // single chunk
        MAX_SEGMENT_SIZE * 2, // chunk two segments
        Arc::new(ServerMeta { schemas }),
        None,
        None,
        None,
    );
    let chunk = &chunks.list[0];

    // provision test data
    {
        assert_eq!(chunk.segments().len(), 1);

        // put 16 cells to fill up all of those segments allocated
        for i in 0..16 {
            let mut cell = default_cell(&Id::new(0, i));
            chunks.write_cell(&mut cell).unwrap();
        }

        assert_eq!(chunk.segments().len(), 2);
        assert_eq!(chunk.cell_index.len(), 16);

        println!("trying to delete cells");

        assert_eq!(chunk.seg_count(), 2);
        assert_eq!(chunk.cell_count(), 16);

        for i in 0..8 {
            chunks.remove_cell(&Id::new(0, i * 2)).unwrap();
        }

        assert_eq!(chunk.segments().len(), 2);

        //count entries, including dead ones
        assert_eq!(chunk.segs.get(&0).unwrap().entry_iter().count(), 8); // all 8 cells
        assert_eq!(chunk.segs.get(&1).unwrap().entry_iter().count(), 16); // 8 cells and 8 tombstones

        // try to scan first segment expect no panic
        println!("Scanning first segment...");
        chunk
            .live_entries(&chunk.segs.get(&0).unwrap())
            .for_each(|_| {});

        println!("Scanning second segment for tombstones...");
        let seg = &chunk.segs.get(&1).unwrap();
        let live_entries = chunk.live_entries(seg);
        let tombstones: Vec<_> = live_entries
            .filter(|e| e.meta.entry_header.entry_type == EntryType::TOMBSTONE)
            .collect();
        assert_eq!(tombstones.len(), 8);
        for i in 0..tombstones.len() {
            let hash = (i * 2) as u64;
            let e = &tombstones[i];
            assert_eq!(e.meta.entry_header.entry_type, EntryType::TOMBSTONE);
            if let EntryContent::Tombstone(ref t) = e.content {
                assert_eq!(t.hash, hash);
                assert_eq!(t.partition, 0);
            } else {
                panic!();
            }
        }

        assert_eq!(chunk.cell_count(), 8);
    }

    // check integrity
    let _ = chunk
        .live_entries(&chunk.segs.get(&0).unwrap())
        .collect::<Vec<_>>();
    let _ = chunk
        .live_entries(&chunk.segs.get(&1).unwrap())
        .collect::<Vec<_>>();

    // compact
    {
        // Compact all segments order by id
        chunk.segments().into_iter().for_each(|seg| {
            compact::CompactCleaner::clean_segment(chunk, &seg);
        });

        assert_eq!(chunk.seg_count(), 2);
        assert_eq!(chunk.cell_count(), 8);

        // scan segments to check entries
        let seg0 = &chunk.segs.get(&0).unwrap();
        let seg1 = &chunk.segs.get(&1).unwrap();
        let compacted_segment_0_entries = chunk.live_entries(seg0).collect::<Vec<_>>();
        let compacted_segment_1_entries = chunk.live_entries(seg1).collect::<Vec<_>>();
        let compacted_segment_0_ids = (0..4).map(|num| num as u64 * 2 + 1);
        let compacted_segment_1_ids = (4..8)
            .map(|num| num as i64 * 2 + 1)
            .chain((0..8).map(|i| -1 * i * 2));
        assert_eq!(seg0.id, 0);
        assert_eq!(seg1.id, 1);
        // check for cells continuity
        compacted_segment_0_entries
            .iter()
            .zip(compacted_segment_0_ids)
            .for_each(|(entry, hash)| {
                assert_eq!(entry.meta.entry_header.entry_type, EntryType::CELL);
                if let EntryContent::Cell(header) = entry.content {
                    assert_eq!(header.hash, hash)
                } else {
                    panic!();
                }
            });
        assert_eq!(compacted_segment_0_entries.len(), 4);
        assert_eq!(seg0.entry_iter().count(), 4);

        // check for cells and 4 tombstones
        compacted_segment_1_entries
            .iter()
            .zip(compacted_segment_1_ids)
            .for_each(|(entry, hash)| {
                if hash > 1 {
                    // cell
                    assert_eq!(entry.meta.entry_header.entry_type, EntryType::CELL);
                    if let EntryContent::Cell(header) = entry.content {
                        assert_eq!(header.hash, hash as u64)
                    } else {
                        panic!();
                    }
                } else {
                    // tombstone
                    assert_eq!(entry.meta.entry_header.entry_type, EntryType::TOMBSTONE);
                    let tombstone = if let EntryContent::Tombstone(ref tombstone) = entry.content {
                        tombstone
                    } else {
                        panic!()
                    };
                    assert_eq!((hash * -1) as u64, tombstone.hash);
                }
            });
        // 4 remaining cells and 8 deleted cell tombstones
        assert_eq!(compacted_segment_1_entries.len(), 12);
        assert_eq!(seg1.entry_iter().count(), 12);
    }

    // combine
    {
        combine::CombinedCleaner::combine_segments(chunk, &chunk.segments());
        let survival_cells: HashSet<_> = chunk
            .live_entries(&chunk.segments()[0])
            .map(|entry| {
                assert_eq!(entry.meta.entry_header.entry_type, EntryType::CELL);
                if let EntryContent::Cell(ref header) = entry.content {
                    return header.hash;
                } else {
                    panic!()
                }
            })
            .collect();
        (0..8)
            .map(|n| n as u64 * 2 + 1)
            .for_each(|hash| assert!(survival_cells.contains(&hash)));
        assert_eq!(chunk.segments().len(), 1);
        assert_eq!(survival_cells.len(), 8);
        assert_eq!(chunk.segments()[0].entry_iter().count(), 8);
    }

    // validate cells
    (0..8).map(|n| n * 2 + 1).for_each(|id| {
        let id = Id::new(0, id);
        let cell = chunks.read_cell(&id).unwrap();
        assert_eq!(cell.data, default_cell(&id).data);
    });
}
<|endoftext|>


<commit_before>extern crate byteorder;
extern crate chrono;
use std::io::Read;
use std::fs;
use std::path::Path;
use byteorder::{ByteOrder, BigEndian, LittleEndian, ReadBytesExt};
use std::collections::HashMap;
use chrono::*;
use super::fit_header::FitHeader;
use super::global_message_table::GlobalMessage;
use super::file_id_message::FileIdMessage;
use super::field_definition::FieldDefinition;

pub struct Decoder {
    pub file_name: String,
    pub fit_buf: Vec<u8>,
    pub header: FitHeader,
    pub global_mesage_table: GlobalMessage,
    pub file_id_table: FileIdMessage,
    read_pos: u64,
}

impl Decoder {
    pub fn new(file_name: String) -> Decoder {
        return Decoder {
            file_name: file_name,
            fit_buf: Vec::new(),
            header: FitHeader::default(),
            global_mesage_table: GlobalMessage::new(),
            file_id_table: FileIdMessage::new(),
            read_pos: 0,
        }
    }

    pub fn decode(&mut self) {
        self.fit_buf = ::read_bin(&self.file_name);

        self.decode_header();
        self.decode_records();
    }

    fn decode_header(&mut self) {
        let header = self.read_header();

        let header_size = header[0];

        let protocol_version = header[1];

        let protocol_profile_major = header[2];

        let protocol_profile_minor = header[3];

        let full_profile_version: u16 = (header[3] as u16) << 8 | (header[2] as u16);

        let record_size_in_bytes: u32 = (header[7] as u32) << 24 |
            (header[6] as u32) << 16 |
            (header[5] as u32) << 8 |
            (header[4] as u32);

        let mut fit_str = String::new();
        fit_str.push(header[8] as char);
        fit_str.push(header[9] as char);
        fit_str.push(header[10] as char);
        fit_str.push(header[11] as char);

        let crc: u16 = (header[13] as u16) << 8 | (header[12] as u16);

       self.header.size = header_size;
       self.header.protocol_version = protocol_version;
       self.header.profile_version = full_profile_version;
       self.header.fit_str = fit_str;
       self.header.data_size = record_size_in_bytes;
       self.header.crc = crc;

       println!("{:?}", self.header);
    }

    fn read_header(&self) -> Vec<u8> {
        let word0 = self.fit_buf[0];
        let word1 = self.fit_buf[1];

        let headerVec: Vec<u8> = self.fit_buf.iter().take(14).map(|b| *b).collect();

        return headerVec;
    }

    fn decode_records(&self) {
        let record_header = self.fit_buf[14];
        println!("{:#b}", record_header);
        let arch = self.fit_buf[16];
        println!("{:#b}", arch);

        let global_message_number: u16 = (self.fit_buf[18] as u16) << 8 | (self.fit_buf[17] as u16);
        println!("Global Message Number: {}", global_message_number);

        let global_message = self.global_mesage_table.get(global_message_number);
        println!("Global Message: {}", global_message);

        let num_fields = self.fit_buf[19];
        println!("Number of Fields: {}", num_fields);

        let index: usize = 20;
        let fields: usize = num_fields as usize;
        let mut pos = index;
        for f in 0..fields {
            let current_index: usize = index + (3 * f);
            let field_def_num = self.fit_buf[current_index];
            let size = self.fit_buf[current_index + 1];
            let base_type = self.fit_buf[current_index + 2];

            let field_kind = self.file_id_table.get(field_def_num);

            println!("{}: {}  {}  {}", field_kind, field_def_num, size, base_type);
            pos += 3;
        }

        //TODO: Handle pos better
        let data_header = self.fit_buf[pos];
        pos += 1;
        println!("{:#b}", data_header);

        let serial_number = LittleEndian::read_u32(&self.fit_buf[pos..]);
        println!("Serial Number: {}", serial_number);
        pos += 4;

        let time_created = LittleEndian::read_u32(&self.fit_buf[pos..]);
        println!("Time Created: {}", time_created);

        let d = UTC.ymd(1989, 12, 31);
        let sum = d + Duration::seconds(time_created as i64);
        println!("Date: {}", sum);
        pos += 4;

        let manufacturer = LittleEndian::read_u16(&self.fit_buf[pos..]);
        println!("Manf: {}", manufacturer);
        pos += 2;

        let product = LittleEndian::read_u16(&self.fit_buf[pos..]);
        println!("Product: {}", product);
        pos += 2;

        let t = self.fit_buf[pos];
        println!("Type: {}", t);
    }

    fn read_u8(&mut self) -> u8 {
        let byte = self.fit_buf[self.read_pos as usize];
        self.read_pos += 1;
        byte
    }

    fn read_byte(&mut self) -> u8{
        let byte = self.fit_buf[self.read_pos as usize];
        self.read_pos += 1;
        byte
    }

    fn read_bytes(&mut self, amount: u32) -> Vec<u8> {
        let mut result = Vec::new();
        for _ in 0..amount {
            let read0 = self.fit_buf[self.read_pos as usize];
            result.push(read0);
            self.read_pos += 1;
        }

        result
    }
}

/*fn read_bin<P: AsRef<Path>>(path: P) -> Vec<u8> {
    let mut file = fs::File::open(path).unwrap();
    let mut file_buf = Vec::new();
    file.read_to_end(&mut file_buf).unwrap();
    file_buf
}
*/
<commit_msg>Using read methods instead of directly indexing into the buffer<commit_after>extern crate byteorder;
extern crate chrono;
use std::io::Read;
use std::fs;
use std::path::Path;
use byteorder::{ByteOrder, BigEndian, LittleEndian, ReadBytesExt};
use std::collections::HashMap;
use chrono::*;
use super::fit_header::FitHeader;
use super::global_message_table::GlobalMessage;
use super::file_id_message::FileIdMessage;
use super::field_definition::FieldDefinition;

pub struct Decoder {
    pub file_name: String,
    pub fit_buf: Vec<u8>,
    pub header: FitHeader,
    pub global_mesage_table: GlobalMessage,
    pub file_id_table: FileIdMessage,
    read_pos: u64,
}

impl Decoder {
    pub fn new(file_name: String) -> Decoder {
        return Decoder {
            file_name: file_name,
            fit_buf: Vec::new(),
            header: FitHeader::default(),
            global_mesage_table: GlobalMessage::new(),
            file_id_table: FileIdMessage::new(),
            read_pos: 0,
        }
    }

    pub fn decode(&mut self) {
        self.fit_buf = super::read_bin(&self.file_name);

        self.decode_header();
        self.decode_records();
    }

    fn decode_header(&mut self) {
        let header = self.read_header();

        let header_size = header[0];

        let protocol_version = header[1];

        let protocol_profile_major = header[2];

        let protocol_profile_minor = header[3];

        let full_profile_version: u16 = (header[3] as u16) << 8 | (header[2] as u16);

        let record_size_in_bytes: u32 = (header[7] as u32) << 24 |
            (header[6] as u32) << 16 |
            (header[5] as u32) << 8 |
            (header[4] as u32);

        let mut fit_str = String::new();
        fit_str.push(header[8] as char);
        fit_str.push(header[9] as char);
        fit_str.push(header[10] as char);
        fit_str.push(header[11] as char);

        let crc: u16 = (header[13] as u16) << 8 | (header[12] as u16);

       self.header.size = header_size;
       self.header.protocol_version = protocol_version;
       self.header.profile_version = full_profile_version;
       self.header.fit_str = fit_str;
       self.header.data_size = record_size_in_bytes;
       self.header.crc = crc;

       println!("{:?}", self.header);
    }

    fn read_header(&mut self) -> Vec<u8> {
        //let word0 = self.fit_buf[0];
        //let word1 = self.fit_buf[1];

        let headerVec: Vec<u8> = self.fit_buf.iter().take(14).map(|b| *b).collect();
        self.read_pos += 14;

        return headerVec;
    }

    fn decode_records(&mut self) {
        // let record_header = self.fit_buf[14];
        let record_header = self.read_u8();
        self.read_u8(); // read and ignore a since the next byte is reserved
        println!("{:#b}", record_header);
        let arch = self.read_u8();
        println!("{:#b}", arch);

        let global_message_number: u16 = (self.read_u8() as u16) << 8 | (self.read_u8() as u16);
        println!("Global Message Number: {}", global_message_number);

        let global_message = self.global_mesage_table.get(global_message_number);
        println!("Global Message: {}", global_message);

        let num_fields = self.read_u8();
        println!("Number of Fields: {}", num_fields);

        let index: usize = 20;
        let fields: usize = num_fields as usize;
        let mut pos = index;
        for f in 0..fields {
            // let current_index: usize = index + (3 * f);
            // let field_def_num = self.fit_buf[current_index];
            let field_def_num = self.read_u8();
            // let size = self.fit_buf[current_index + 1];
            let size = self.read_u8();
            // let base_type = self.fit_buf[current_index + 2];
            let base_type = self.read_u8();

            let field_kind = self.file_id_table.get(field_def_num);

            println!("{}: {}  {}  {}", field_kind, field_def_num, size, base_type);
            pos += 3;
        }

        //TODO: Handle pos better
        let data_header = self.read_u8();
        pos += 1;
        println!("{:#b}", data_header);

        // let serial_number = LittleEndian::read_u32(&self.fit_buf[pos..]);
        let mut four_bytes = self.read_bytes(4);
        let serial_number = LittleEndian::read_u32(&four_bytes);
        println!("Serial Number: {}", serial_number);
        pos += 4;

        // let time_created = LittleEndian::read_u32(&self.fit_buf[pos..]);
        four_bytes = self.read_bytes(4);
        let time_created = LittleEndian::read_u32(&four_bytes);
        println!("Time Created: {}", time_created);

        let d = UTC.ymd(1989, 12, 31);
        let sum = d + Duration::seconds(time_created as i64);
        println!("Date: {}", sum);
        pos += 4;

        // let manufacturer = LittleEndian::read_u16(&self.fit_buf[pos..]);
        let mut two_bytes = self.read_bytes(2);
        let manufacturer = LittleEndian::read_u16(&two_bytes);
        println!("Manf: {}", manufacturer);
        pos += 2;

        // let product = LittleEndian::read_u16(&self.fit_buf[pos..]);
        two_bytes = self.read_bytes(2);
        let product = LittleEndian::read_u16(&two_bytes);
        println!("Product: {}", product);
        pos += 2;

        // let t = self.fit_buf[pos];
        let t = self.read_byte();
        println!("Type: {}", t);
    }

    fn read_u8(&mut self) -> u8 {
        let byte = self.fit_buf[self.read_pos as usize];
        self.read_pos += 1;
        byte
    }

    fn read_byte(&mut self) -> u8{
        let byte = self.fit_buf[self.read_pos as usize];
        self.read_pos += 1;
        byte
    }

    fn read_bytes(&mut self, amount: u32) -> Vec<u8> {
        let mut result = Vec::new();
        for _ in 0..amount {
            let read0 = self.fit_buf[self.read_pos as usize];
            result.push(read0);
            self.read_pos += 1;
        }

        result
    }
}

/*fn read_bin<P: AsRef<Path>>(path: P) -> Vec<u8> {
    let mut file = fs::File::open(path).unwrap();
    let mut file_buf = Vec::new();
    file.read_to_end(&mut file_buf).unwrap();
    file_buf
}
*/
<|endoftext|>


<commit_before>mod alphabet;
mod combined;

use std::fmt;
use std::str::FromStr;

use crate::Lang;

pub trait NormalizedOutcome {
    fn normalized_scores(&self) -> &[(Lang, f64)];
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Method {
    Trigram,
    Alphabet,
    Combined,
}

impl FromStr for Method {
    // TODO: implement normal error
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().trim() {
            "trigram" => Ok(Method::Trigram),
            "alphabet" => Ok(Method::Alphabet),
            _ => Err(format!("Cannot obtain Method from {:?}", s)),
        }
    }
}

impl fmt::Display for Method {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = match self {
            Method::Trigram => "Trigram",
            Method::Alphabet => "Alphabet",
            Method::Combined => "Combined",
        };
        write!(f, "{}", name)
    }
}

pub fn detect_by_method(text: &str, method: Method) -> Option<Lang> {
    if let Some(script) = crate::detect_script(text) {
        match method {
            Method::Trigram => crate::detect_lang(text),
            Method::Alphabet => alphabet::detect_by_alphabet(text, script),
            Method::Combined => combined::detect_by_combined(text, script),
        }
    } else {
        None
    }
}

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

    #[test]
    fn test_from_str() {
        assert_eq!("trigram".parse::<Method>().unwrap(), Method::Trigram);
        assert_eq!("ALPHABET".parse::<Method>().unwrap(), Method::Alphabet);

        let result = "foobar".parse::<Method>();
        assert!(result.is_err());
    }
}
<commit_msg>Fix Method::Combined<commit_after>mod alphabet;
mod combined;

use std::fmt;
use std::str::FromStr;

use crate::Lang;

pub trait NormalizedOutcome {
    fn normalized_scores(&self) -> &[(Lang, f64)];
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Method {
    Trigram,
    Alphabet,
    Combined,
}

impl FromStr for Method {
    // TODO: implement normal error
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().trim() {
            "trigram" => Ok(Method::Trigram),
            "alphabet" => Ok(Method::Alphabet),
            "combined" => Ok(Method::Combined),
            _ => Err(format!("Cannot obtain Method from {:?}", s)),
        }
    }
}

impl fmt::Display for Method {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = match self {
            Method::Trigram => "Trigram",
            Method::Alphabet => "Alphabet",
            Method::Combined => "Combined",
        };
        write!(f, "{}", name)
    }
}

pub fn detect_by_method(text: &str, method: Method) -> Option<Lang> {
    if let Some(script) = crate::detect_script(text) {
        match method {
            Method::Trigram => crate::detect_lang(text),
            Method::Alphabet => alphabet::detect_by_alphabet(text, script),
            Method::Combined => combined::detect_by_combined(text, script),
        }
    } else {
        None
    }
}

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

    #[test]
    fn test_from_str() {
        assert_eq!("trigram".parse::<Method>().unwrap(), Method::Trigram);
        assert_eq!("ALPHABET".parse::<Method>().unwrap(), Method::Alphabet);

        let result = "foobar".parse::<Method>();
        assert!(result.is_err());
    }
}
<|endoftext|>


<commit_before>extern crate indicatif;
extern crate rand;

use std::thread;
use std::time::{Instant, Duration};
use rand::Rng;

use indicatif::{ProgressBar, ProgressStyle, MultiProgress, HumanDuration, style};

static PACKAGES: &'static [&'static str] = &[
    "fs-events",
    "my-awesome-module",
    "emoji-speaker",
    "wrap-ansi",
    "stream-browserify",
    "acorn-dynamic-import",
];

static COMMANDS: &'static [&'static str] = &[
    "cmake .",
    "make",
    "make clean",
    "gcc foo.c -o foo",
    "gcc bar.c -o bar",
    "./helper.sh rebuild-cache",
    "make all-clean",
    "make test",
];

pub fn main() {
    let mut rng = rand::thread_rng();
    let started = Instant::now();
    let spinner_style = ProgressStyle::default_spinner()
        .tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈ ")
        .template("{prefix:.bold.dim} {spinner} {msg}");
    let progress_style = ProgressStyle::default_spinner()
        .template("{prefix:.bold.dim} {msg}\n{wide_bar} {pos}/{len}");

    println!("{} 🔍  Resolving packages...", style("[1/4]").bold().dim());
    println!("{} 🚚  Fetching packages...", style("[2/4]").bold().dim());

    let deps = 1232;
    let pb = ProgressBar::new(deps);
    pb.set_style(progress_style);
    pb.set_prefix("[3/4]");
    pb.set_message("🔗  Linking dependencies...");

    for _ in 0..deps {
        pb.inc(1);
        thread::sleep(Duration::from_millis(3));
    }

    pb.finish_and_clear();
    println!("{} 🔗  Linking dependencies...", style("[3/4]").bold().dim());

    println!("{} 📃  Building fresh packages...", style("[4/4]").bold().dim());
    let m = MultiProgress::new();
    for i in 0..4 {
        let count = rng.gen_range(30, 80);
        let pb = m.add(ProgressBar::new(count));
        pb.set_style(spinner_style.clone());
        pb.set_prefix(&format!("[{}/?]", i + 1));
        let _ = thread::spawn(move || {
            let mut rng = rand::thread_rng();
            let pkg = rng.choose(PACKAGES).unwrap();
            for _ in 0..count {
                let cmd = rng.choose(COMMANDS).unwrap();
                pb.set_message(&format!("{}: {}", pkg, cmd));
                pb.inc(1);
                thread::sleep(Duration::from_millis(rng.gen_range(25, 200)));
            }
            pb.finish_with_message("waiting...");
        });
    }
    m.join_and_clear().unwrap();

    println!("✨  Done in {}", HumanDuration(started.elapsed()));
}
<commit_msg>Simplified progress bar<commit_after>extern crate indicatif;
extern crate rand;

use std::thread;
use std::time::{Instant, Duration};
use rand::Rng;

use indicatif::{ProgressBar, ProgressStyle, MultiProgress, HumanDuration, style};

static PACKAGES: &'static [&'static str] = &[
    "fs-events",
    "my-awesome-module",
    "emoji-speaker",
    "wrap-ansi",
    "stream-browserify",
    "acorn-dynamic-import",
];

static COMMANDS: &'static [&'static str] = &[
    "cmake .",
    "make",
    "make clean",
    "gcc foo.c -o foo",
    "gcc bar.c -o bar",
    "./helper.sh rebuild-cache",
    "make all-clean",
    "make test",
];

pub fn main() {
    let mut rng = rand::thread_rng();
    let started = Instant::now();
    let spinner_style = ProgressStyle::default_spinner()
        .tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈ ")
        .template("{prefix:.bold.dim} {spinner} {msg}");

    println!("{} 🔍  Resolving packages...", style("[1/4]").bold().dim());
    println!("{} 🚚  Fetching packages...", style("[2/4]").bold().dim());

    println!("{} 🔗  Linking dependencies...", style("[3/4]").bold().dim());
    let deps = 1232;
    let pb = ProgressBar::new(deps);
    for _ in 0..deps {
        pb.inc(1);
        thread::sleep(Duration::from_millis(3));
    }
    pb.finish_and_clear();

    println!("{} 📃  Building fresh packages...", style("[4/4]").bold().dim());
    let m = MultiProgress::new();
    for i in 0..4 {
        let count = rng.gen_range(30, 80);
        let pb = m.add(ProgressBar::new(count));
        pb.set_style(spinner_style.clone());
        pb.set_prefix(&format!("[{}/?]", i + 1));
        let _ = thread::spawn(move || {
            let mut rng = rand::thread_rng();
            let pkg = rng.choose(PACKAGES).unwrap();
            for _ in 0..count {
                let cmd = rng.choose(COMMANDS).unwrap();
                pb.set_message(&format!("{}: {}", pkg, cmd));
                pb.inc(1);
                thread::sleep(Duration::from_millis(rng.gen_range(25, 200)));
            }
            pb.finish_with_message("waiting...");
        });
    }
    m.join_and_clear().unwrap();

    println!("✨  Done in {}", HumanDuration(started.elapsed()));
}
<|endoftext|>


<commit_before>use sled;

use std::ffi::CString;
use std::mem;
use std::ptr;
use std::slice;

use libc::*;

use sled::{Config, Db, Iter};

fn leak_buf(v: Vec<u8>, vallen: *mut size_t) -> *mut c_char {
    unsafe {
        *vallen = v.len();
    }
    let mut bsv = v.into_boxed_slice();
    let val = bsv.as_mut_ptr() as *mut _;
    mem::forget(bsv);
    val
}

/// Create a new configuration.
#[no_mangle]
pub unsafe extern "C" fn sled_create_config() -> *mut Config {
    Box::into_raw(Box::new(Config::new()))
}

/// Destroy a configuration.
#[no_mangle]
pub unsafe extern "C" fn sled_free_config(config: *mut Config) {
    drop(Box::from_raw(config));
}

/// Set the configured file path. The caller is responsible for freeing the path
/// string after calling this (it is copied in this function).
#[no_mangle]
pub unsafe extern "C" fn sled_config_set_path(
    config: *mut Config,
    path: *const c_char,
) -> *mut Config {
    let c_str = CString::from_raw(path as *mut _);
    let value = c_str.into_string().unwrap();

    let config = Box::from_raw(config);
    Box::into_raw(Box::from(config.path(value)))
}

/// Configure read-only mode.
#[no_mangle]
pub unsafe extern "C" fn sled_config_read_only(
    config: *mut Config,
    read_only: c_uchar,
) -> *mut Config {
    let config = Box::from_raw(config);
    Box::into_raw(Box::from(config.read_only(read_only == 1)))
}

/// Set the configured cache capacity in bytes.
#[no_mangle]
pub unsafe extern "C" fn sled_config_set_cache_capacity(
    config: *mut Config,
    capacity: size_t,
) -> *mut Config {
    let config = Box::from_raw(config);
    Box::into_raw(Box::from(config.cache_capacity(capacity as u64)))
}

/// Configure the use of the zstd compression library.
#[no_mangle]
pub unsafe extern "C" fn sled_config_use_compression(
    config: *mut Config,
    use_compression: c_uchar,
) -> *mut Config {
    let config = Box::from_raw(config);
    Box::into_raw(Box::from(config.use_compression(use_compression == 1)))
}

/// Set the configured IO buffer flush interval in milliseconds.
#[no_mangle]
pub unsafe extern "C" fn sled_config_flush_every_ms(
    config: *mut Config,
    flush_every: c_int,
) -> *mut Config {
    let val = if flush_every < 0 { None } else { Some(flush_every as u64) };
    let config = Box::from_raw(config);
    Box::into_raw(Box::from(config.flush_every_ms(val)))
}

/// Open a sled lock-free log-structured tree. Consumes the passed-in config.
#[no_mangle]
pub unsafe extern "C" fn sled_open_db(config: *mut Config) -> *mut Db {
    let config = Box::from_raw(config);
    Box::into_raw(Box::new(config.open().unwrap()))
}

/// Close a sled lock-free log-structured tree.
#[no_mangle]
pub unsafe extern "C" fn sled_close(db: *mut Db) {
    drop(Box::from_raw(db));
}

/// Free a buffer originally allocated by sled.
#[no_mangle]
pub unsafe extern "C" fn sled_free_buf(buf: *mut c_char, sz: size_t) {
    drop(Vec::from_raw_parts(buf, sz, sz));
}

/// Free an iterator.
#[no_mangle]
pub unsafe extern "C" fn sled_free_iter(iter: *mut Iter) {
    drop(Box::from_raw(iter));
}

/// Set a key to a value.
#[no_mangle]
pub unsafe extern "C" fn sled_set(
    db: *mut Db,
    key: *const c_uchar,
    keylen: size_t,
    val: *const c_uchar,
    vallen: size_t,
) {
    let k = slice::from_raw_parts(key, keylen).to_vec();
    let v = slice::from_raw_parts(val, vallen).to_vec();
    (*db).insert(k, v).unwrap();
}

/// Get the value of a key.
/// Caller is responsible for freeing the returned value with `sled_free_buf` if
/// it's non-null.
#[no_mangle]
pub unsafe extern "C" fn sled_get(
    db: *mut Db,
    key: *const c_char,
    keylen: size_t,
    vallen: *mut size_t,
) -> *mut c_char {
    let k = slice::from_raw_parts(key as *const u8, keylen);
    let res = (*db).get(k);
    match res {
        Ok(Some(v)) => leak_buf(v.to_vec(), vallen),
        Ok(None) => ptr::null_mut(),
        // TODO proper error propagation
        Err(e) => panic!("{:?}", e),
    }
}

/// Delete the value of a key.
#[no_mangle]
pub unsafe extern "C" fn sled_del(
    db: *mut Db,
    key: *const c_char,
    keylen: size_t,
) {
    let k = slice::from_raw_parts(key as *const u8, keylen);
    (*db).remove(k).unwrap();
}

/// Compare and swap.
/// Returns 1 if successful, 0 if unsuccessful.
/// Otherwise sets `actual_val` and `actual_vallen` to the current value,
/// which must be freed using `sled_free_buf` by the caller if non-null.
/// `actual_val` will be null and `actual_vallen` 0 if the current value is not
/// set.
#[no_mangle]
pub unsafe extern "C" fn sled_compare_and_swap(
    db: *mut Db,
    key: *const c_char,
    keylen: size_t,
    old_val: *const c_uchar,
    old_vallen: size_t,
    new_val: *const c_uchar,
    new_vallen: size_t,
    actual_val: *mut *const c_uchar,
    actual_vallen: *mut size_t,
) -> c_uchar {
    let k = slice::from_raw_parts(key as *const u8, keylen).to_vec();

    let old = if old_vallen == 0 {
        None
    } else {
        let old_slice = slice::from_raw_parts(old_val as *const u8, old_vallen);
        let copy = old_slice.to_vec();
        Some(copy)
    };

    let new = if new_vallen == 0 {
        None
    } else {
        let new_slice = slice::from_raw_parts(new_val as *const u8, new_vallen);
        let copy = new_slice.to_vec();
        Some(copy)
    };

    let res = (*db).compare_and_swap(k, old, new);

    match res {
        Ok(Ok(())) => 1,
        Ok(Err(sled::CompareAndSwapError { current: None, .. })) => {
            *actual_vallen = 0;
            0
        }
        Ok(Err(sled::CompareAndSwapError { current: Some(v), .. })) => {
            *actual_val = leak_buf(v.to_vec(), actual_vallen) as *const u8;
            0
        }
        // TODO proper error propagation
        Err(e) => panic!("{:?}", e),
    }
}

/// Iterate over tuples which have specified key prefix.
/// Caller is responsible for freeing the returned iterator with
/// `sled_free_iter`.
#[no_mangle]
pub unsafe extern "C" fn sled_scan_prefix(
    db: *mut Db,
    key: *const c_char,
    keylen: size_t,
) -> *mut Iter {
    let k = slice::from_raw_parts(key as *const u8, keylen);
    Box::into_raw(Box::new((*db).scan_prefix(k)))
}

/// Get they next kv pair from an iterator.
/// Caller is responsible for freeing the key and value with `sled_free_buf`.
/// Returns 0 when exhausted.
#[no_mangle]
pub unsafe extern "C" fn sled_iter_next(
    iter: *mut Iter,
    key: *mut *const c_char,
    keylen: *mut size_t,
    val: *mut *const c_char,
    vallen: *mut size_t,
) -> c_uchar {
    match (*iter).next() {
        Some(Ok((k, v))) => {
            *key = leak_buf(k.to_vec(), keylen);
            *val = leak_buf(v.to_vec(), vallen);
            1
        }
        // TODO proper error propagation
        Some(Err(e)) => panic!("{:?}", e),
        None => 0,
    }
}
<commit_msg>Convert to IVecs instead of Vecs<commit_after>use sled;

use std::ffi::CString;
use std::mem;
use std::ptr;
use std::slice;

use libc::*;

use sled::{Config, Db, IVec, Iter};

fn leak_buf(v: Vec<u8>, vallen: *mut size_t) -> *mut c_char {
    unsafe {
        *vallen = v.len();
    }
    let mut bsv = v.into_boxed_slice();
    let val = bsv.as_mut_ptr() as *mut _;
    mem::forget(bsv);
    val
}

/// Create a new configuration.
#[no_mangle]
pub unsafe extern "C" fn sled_create_config() -> *mut Config {
    Box::into_raw(Box::new(Config::new()))
}

/// Destroy a configuration.
#[no_mangle]
pub unsafe extern "C" fn sled_free_config(config: *mut Config) {
    drop(Box::from_raw(config));
}

/// Set the configured file path. The caller is responsible for freeing the path
/// string after calling this (it is copied in this function).
#[no_mangle]
pub unsafe extern "C" fn sled_config_set_path(
    config: *mut Config,
    path: *const c_char,
) -> *mut Config {
    let c_str = CString::from_raw(path as *mut _);
    let value = c_str.into_string().unwrap();

    let config = Box::from_raw(config);
    Box::into_raw(Box::from(config.path(value)))
}

/// Configure read-only mode.
#[no_mangle]
pub unsafe extern "C" fn sled_config_read_only(
    config: *mut Config,
    read_only: c_uchar,
) -> *mut Config {
    let config = Box::from_raw(config);
    Box::into_raw(Box::from(config.read_only(read_only == 1)))
}

/// Set the configured cache capacity in bytes.
#[no_mangle]
pub unsafe extern "C" fn sled_config_set_cache_capacity(
    config: *mut Config,
    capacity: size_t,
) -> *mut Config {
    let config = Box::from_raw(config);
    Box::into_raw(Box::from(config.cache_capacity(capacity as u64)))
}

/// Configure the use of the zstd compression library.
#[no_mangle]
pub unsafe extern "C" fn sled_config_use_compression(
    config: *mut Config,
    use_compression: c_uchar,
) -> *mut Config {
    let config = Box::from_raw(config);
    Box::into_raw(Box::from(config.use_compression(use_compression == 1)))
}

/// Set the configured IO buffer flush interval in milliseconds.
#[no_mangle]
pub unsafe extern "C" fn sled_config_flush_every_ms(
    config: *mut Config,
    flush_every: c_int,
) -> *mut Config {
    let val = if flush_every < 0 { None } else { Some(flush_every as u64) };
    let config = Box::from_raw(config);
    Box::into_raw(Box::from(config.flush_every_ms(val)))
}

/// Open a sled lock-free log-structured tree. Consumes the passed-in config.
#[no_mangle]
pub unsafe extern "C" fn sled_open_db(config: *mut Config) -> *mut Db {
    let config = Box::from_raw(config);
    Box::into_raw(Box::new(config.open().unwrap()))
}

/// Close a sled lock-free log-structured tree.
#[no_mangle]
pub unsafe extern "C" fn sled_close(db: *mut Db) {
    drop(Box::from_raw(db));
}

/// Free a buffer originally allocated by sled.
#[no_mangle]
pub unsafe extern "C" fn sled_free_buf(buf: *mut c_char, sz: size_t) {
    drop(Vec::from_raw_parts(buf, sz, sz));
}

/// Free an iterator.
#[no_mangle]
pub unsafe extern "C" fn sled_free_iter(iter: *mut Iter) {
    drop(Box::from_raw(iter));
}

/// Set a key to a value.
#[no_mangle]
pub unsafe extern "C" fn sled_set(
    db: *mut Db,
    key: *const c_uchar,
    keylen: size_t,
    val: *const c_uchar,
    vallen: size_t,
) {
    let k = IVec::from(slice::from_raw_parts(key, keylen));
    let v = IVec::from(slice::from_raw_parts(val, vallen));
    (*db).insert(k, v).unwrap();
}

/// Get the value of a key.
/// Caller is responsible for freeing the returned value with `sled_free_buf` if
/// it's non-null.
#[no_mangle]
pub unsafe extern "C" fn sled_get(
    db: *mut Db,
    key: *const c_char,
    keylen: size_t,
    vallen: *mut size_t,
) -> *mut c_char {
    let k = slice::from_raw_parts(key as *const u8, keylen);
    let res = (*db).get(k);
    match res {
        Ok(Some(v)) => leak_buf(v.to_vec(), vallen),
        Ok(None) => ptr::null_mut(),
        // TODO proper error propagation
        Err(e) => panic!("{:?}", e),
    }
}

/// Delete the value of a key.
#[no_mangle]
pub unsafe extern "C" fn sled_del(
    db: *mut Db,
    key: *const c_char,
    keylen: size_t,
) {
    let k = slice::from_raw_parts(key as *const u8, keylen);
    (*db).remove(k).unwrap();
}

/// Compare and swap.
/// Returns 1 if successful, 0 if unsuccessful.
/// Otherwise sets `actual_val` and `actual_vallen` to the current value,
/// which must be freed using `sled_free_buf` by the caller if non-null.
/// `actual_val` will be null and `actual_vallen` 0 if the current value is not
/// set.
#[no_mangle]
pub unsafe extern "C" fn sled_compare_and_swap(
    db: *mut Db,
    key: *const c_char,
    keylen: size_t,
    old_val: *const c_uchar,
    old_vallen: size_t,
    new_val: *const c_uchar,
    new_vallen: size_t,
    actual_val: *mut *const c_uchar,
    actual_vallen: *mut size_t,
) -> c_uchar {
    let k = IVec::from(slice::from_raw_parts(key as *const u8, keylen));

    let old = if old_vallen == 0 {
        None
    } else {
        let copy =
            IVec::from(slice::from_raw_parts(old_val as *const u8, old_vallen));
        Some(copy)
    };

    let new = if new_vallen == 0 {
        None
    } else {
        let copy =
            IVec::from(slice::from_raw_parts(new_val as *const u8, new_vallen));
        Some(copy)
    };

    let res = (*db).compare_and_swap(k, old, new);

    match res {
        Ok(Ok(())) => 1,
        Ok(Err(sled::CompareAndSwapError { current: None, .. })) => {
            *actual_vallen = 0;
            0
        }
        Ok(Err(sled::CompareAndSwapError { current: Some(v), .. })) => {
            *actual_val = leak_buf(v.to_vec(), actual_vallen) as *const u8;
            0
        }
        // TODO proper error propagation
        Err(e) => panic!("{:?}", e),
    }
}

/// Iterate over tuples which have specified key prefix.
/// Caller is responsible for freeing the returned iterator with
/// `sled_free_iter`.
#[no_mangle]
pub unsafe extern "C" fn sled_scan_prefix(
    db: *mut Db,
    key: *const c_char,
    keylen: size_t,
) -> *mut Iter {
    let k = slice::from_raw_parts(key as *const u8, keylen);
    Box::into_raw(Box::new((*db).scan_prefix(k)))
}

/// Get they next kv pair from an iterator.
/// Caller is responsible for freeing the key and value with `sled_free_buf`.
/// Returns 0 when exhausted.
#[no_mangle]
pub unsafe extern "C" fn sled_iter_next(
    iter: *mut Iter,
    key: *mut *const c_char,
    keylen: *mut size_t,
    val: *mut *const c_char,
    vallen: *mut size_t,
) -> c_uchar {
    match (*iter).next() {
        Some(Ok((k, v))) => {
            *key = leak_buf(k.to_vec(), keylen);
            *val = leak_buf(v.to_vec(), vallen);
            1
        }
        // TODO proper error propagation
        Some(Err(e)) => panic!("{:?}", e),
        None => 0,
    }
}
<|endoftext|>