<commit_before>use std::cmp::max;
use unidiff::{PatchSet, PatchedFile, Hunk, Line};

#[derive(Debug)]
pub struct DiffOfDiffs {
    diff0: String,
    diff1: String,
    patch0: Option<PatchSet>,
    patch1: Option<PatchSet>,
}

impl DiffOfDiffs {
    pub fn new(diff0: &str, diff1: &str) -> DiffOfDiffs {
        DiffOfDiffs {
            diff0: diff0.into(),
            diff1: diff1.into(),
            patch0: parse_diff(diff0),
            patch1: parse_diff(diff1),
        }
    }

    pub fn are_equal(&self) -> bool {
        // try to parse diffs and if can't parse either,
        // then default to straight string comparison
        let patch0 = match self.patch0 {
            None => return self.diff0 == self.diff1,
            Some(ref p) => p,
        };
        let patch1 = match self.patch1 {
            None => return self.diff0 == self.diff1,
            Some(ref p) => p,
        };

        if patch0.is_empty() || patch1.is_empty() {
            return self.diff0 == self.diff1;
        }

        are_patch_sets_equal(patch0, patch1)
    }

    pub fn different_patch_files(&self) -> Vec<PatchedFile> {
        if let Some(ref patch0) = self.patch0 {
            if let Some(ref patch1) = self.patch1 {
                return different_patch_files(patch0, patch1);
            }
        }

        vec![]
    }
}

fn parse_diff(diff: &str) -> Option<PatchSet> {
    let mut patch = PatchSet::new();
    match patch.parse(diff) {
        Ok(_) => Some(patch),
        Err(e) => {
            debug!("Unable to parse patch set: {}\n---\n{}\n---\n", e, diff);
            None
        }
    }
}

fn different_patch_files(patch0: &PatchSet, patch1: &PatchSet) -> Vec<PatchedFile> {
    let count = max(patch0.len(), patch1.len());

    let mut different = vec![];
    for index in 0..count {
        if index > patch0.len() {
            // a file in patch1 not in patch0
            different.push(patch1[index].clone())
        } else if index > patch1.len() {
            // a file in patch0 not in patch1
            different.push(patch0[index].clone())
        } else if !are_patched_files_equal(&patch0[index], &patch1[index]) {
            // a file in both patch sets but different
            different.push(patch0[index].clone())
        }
    }

    different
}

fn are_patch_sets_equal(patch0: &PatchSet, patch1: &PatchSet) -> bool {
    // check equal number of patched files
    if patch0.len() != patch1.len() {
        return false;
    }

    for i in 0..patch0.len() {
        if !are_patched_files_equal(&patch0[i], &patch1[i]) {
            return false;
        }
    }

    true
}

fn are_patched_files_equal(file0: &PatchedFile, file1: &PatchedFile) -> bool {
    // check equal number of hunks in this patched file
    if file0.len() != file1.len() {
        return false;
    }

    for hunk_num in 0..file0.len() {
        if !are_patch_hunks_equal(&file0[hunk_num], &file1[hunk_num]) {
            return false;
        }
    }

    true
}

fn are_patch_hunks_equal(hunk0: &Hunk, hunk1: &Hunk) -> bool {
    // check equal number of lines in this hunk
    if hunk0.len() != hunk1.len() {
        return false;
    }

    for line_num in 0..hunk0.len() {
        if !are_patch_lines_equal(&hunk0[line_num], &hunk1[line_num]) {
            return false;
        }
    }

    true
}

fn are_patch_lines_equal(line0: &Line, line1: &Line) -> bool {
    // only check for line type and line value for equality,
    // ignore source/target/diff line numbers
    line0.line_type == line1.line_type && line0.value == line1.value
}

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

    #[test]
    fn test_non_diff_contents() {
        assert_eq!(true, DiffOfDiffs::new("some-non-diff", "some-non-diff").are_equal());

        let diffs = DiffOfDiffs::new("some-non-diff", "some-other-non-diff");
        assert_eq!(false, diffs.are_equal());
        assert_eq!(Vec::<PatchedFile>::new(), diffs.different_patch_files());
    }

    #[test]
    fn test_git_diff_and_diff_line_numbers() {
        let diff0 = r#"
diff --git a/Cargo.toml b/Cargo.toml
index 43f2e75..42eaec0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -31,6 +31,7 @@ thread-id = "3.0.0"
 threadpool = "1.3.2"
 time = "0.1.35"
 toml = "0.2.1"
+unidiff = "0.2.0"
 url = "1.2.4"

 [lib]
diff --git a/src/lib.rs b/src/lib.rs
index 4442a2c..007a2cf 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -11,6 +11,7 @@ extern crate serde;
 extern crate serde_json;
 extern crate threadpool;
 extern crate toml;
+extern crate unidiff;
 extern crate url;

 #[macro_use]
@@ -19,6 +20,7 @@ extern crate log;
 extern crate serde_derive;

 pub mod config;
+pub mod diffs;
 pub mod dir_pool;
 pub mod force_push;
 pub mod git;"#;

        let diffs = DiffOfDiffs::new(&diff0, &diff0);
        assert_eq!(true, diffs.are_equal());
        assert_eq!(Vec::<PatchedFile>::new(), diffs.different_patch_files());

        // same diff, but diff line numbers
        let diff1 = r#"
diff --git a/Cargo.toml b/Cargo.toml
index 43f2e75..42eaec0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -41,6 +41,7 @@ thread-id = "3.0.0"
 threadpool = "1.3.2"
 time = "0.1.35"
 toml = "0.2.1"
+unidiff = "0.2.0"
 url = "1.2.4"

 [lib]
diff --git a/src/lib.rs b/src/lib.rs
index 4442a2c..007a2cf 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,6 +1,7 @@ extern crate serde;
 extern crate serde_json;
 extern crate threadpool;
 extern crate toml;
+extern crate unidiff;
 extern crate url;

 #[macro_use]
@@ -119,6 +120,7 @@ extern crate log;
 extern crate serde_derive;

 pub mod config;
+pub mod diffs;
 pub mod dir_pool;
 pub mod force_push;
 pub mod git;"#;

        assert_eq!(true, DiffOfDiffs::new(&diff0, &diff1).are_equal());
    }

    #[test]
    fn test_different_diff() {
        let diff0 = r#"
diff --git a/Cargo.toml b/Cargo.toml
index 43f2e75..42eaec0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -31,6 +31,7 @@ thread-id = "3.0.0"
 threadpool = "1.3.2"
 time = "0.1.35"
 toml = "0.2.1"
+unidiff = "0.2.0"
 url = "1.2.4"

 [lib]
diff --git a/src/lib.rs b/src/lib.rs
index 4442a2c..007a2cf 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -11,6 +11,7 @@ extern crate serde;
 extern crate serde_json;
 extern crate threadpool;
 extern crate toml;
+extern crate unidiff;
 extern crate url;

 #[macro_use]
@@ -19,6 +20,7 @@ extern crate log;
 extern crate serde_derive;

 pub mod config;
+pub mod diffs;
 pub mod dir_pool;
 pub mod force_push;
 pub mod git;"#;

        let diff1 = r#"
diff --git a/Cargo.toml b/Cargo.toml
index 43f2e75..42eaec0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -41,6 +41,7 @@ thread-id = "3.0.0"
 threadpool = "1.3.2"
 time = "0.1.35"
 toml = "0.2.1"
+unidiff = "0.2.0"
 url = "1.2.4"

 [lib]
diff --git a/src/lib.rs b/src/lib.rs
index 4442a2c..007a2cf 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,6 +1,7 @@ extern crate serde;
 extern crate serde_json;
 extern crate threadpool;
 extern crate toml;
+extern crate unidiff;
 extern crate url;

 #[macro_use]
@@ -119,6 +120,7 @@ extern crate log;
 extern crate serde_derive;

 pub mod ** OTHER_THING_HERE **;
+pub mod diffs;
 pub mod dir_pool;
 pub mod force_push;
 pub mod git;"#;

        let diffs = DiffOfDiffs::new(&diff0, &diff1);
        assert_eq!(false, diffs.are_equal());

        let diff_files = diffs.different_patch_files();
        assert_eq!(1, diff_files.len());
        assert_eq!("a/src/lib.rs", diff_files[0].source_file);
        assert_eq!("b/src/lib.rs", diff_files[0].target_file);
        assert_eq!("src/lib.rs", diff_files[0].path());
    }
}
<commit_msg>Whoops. Fix bad index checks! (#62)<commit_after>use std::cmp::max;
use unidiff::{PatchSet, PatchedFile, Hunk, Line};

#[derive(Debug)]
pub struct DiffOfDiffs {
    diff0: String,
    diff1: String,
    patch0: Option<PatchSet>,
    patch1: Option<PatchSet>,
}

impl DiffOfDiffs {
    pub fn new(diff0: &str, diff1: &str) -> DiffOfDiffs {
        DiffOfDiffs {
            diff0: diff0.into(),
            diff1: diff1.into(),
            patch0: parse_diff(diff0),
            patch1: parse_diff(diff1),
        }
    }

    pub fn are_equal(&self) -> bool {
        // try to parse diffs and if can't parse either,
        // then default to straight string comparison
        let patch0 = match self.patch0 {
            None => return self.diff0 == self.diff1,
            Some(ref p) => p,
        };
        let patch1 = match self.patch1 {
            None => return self.diff0 == self.diff1,
            Some(ref p) => p,
        };

        if patch0.is_empty() || patch1.is_empty() {
            return self.diff0 == self.diff1;
        }

        are_patch_sets_equal(patch0, patch1)
    }

    pub fn different_patch_files(&self) -> Vec<PatchedFile> {
        if let Some(ref patch0) = self.patch0 {
            if let Some(ref patch1) = self.patch1 {
                return different_patch_files(patch0, patch1);
            }
        }

        vec![]
    }
}

fn parse_diff(diff: &str) -> Option<PatchSet> {
    let mut patch = PatchSet::new();
    match patch.parse(diff) {
        Ok(_) => Some(patch),
        Err(e) => {
            debug!("Unable to parse patch set: {}\n---\n{}\n---\n", e, diff);
            None
        }
    }
}

fn different_patch_files(patch0: &PatchSet, patch1: &PatchSet) -> Vec<PatchedFile> {
    let count = max(patch0.len(), patch1.len());

    let mut different = vec![];
    for index in 0..count {
        if index >= patch0.len() {
            // a file in patch1 not in patch0
            different.push(patch1[index].clone())
        } else if index >= patch1.len() {
            // a file in patch0 not in patch1
            different.push(patch0[index].clone())
        } else if !are_patched_files_equal(&patch0[index], &patch1[index]) {
            // a file in both patch sets but different
            different.push(patch0[index].clone())
        }
    }

    different
}

fn are_patch_sets_equal(patch0: &PatchSet, patch1: &PatchSet) -> bool {
    // check equal number of patched files
    if patch0.len() != patch1.len() {
        return false;
    }

    for i in 0..patch0.len() {
        if !are_patched_files_equal(&patch0[i], &patch1[i]) {
            return false;
        }
    }

    true
}

fn are_patched_files_equal(file0: &PatchedFile, file1: &PatchedFile) -> bool {
    // check equal number of hunks in this patched file
    if file0.len() != file1.len() {
        return false;
    }

    for hunk_num in 0..file0.len() {
        if !are_patch_hunks_equal(&file0[hunk_num], &file1[hunk_num]) {
            return false;
        }
    }

    true
}

fn are_patch_hunks_equal(hunk0: &Hunk, hunk1: &Hunk) -> bool {
    // check equal number of lines in this hunk
    if hunk0.len() != hunk1.len() {
        return false;
    }

    for line_num in 0..hunk0.len() {
        if !are_patch_lines_equal(&hunk0[line_num], &hunk1[line_num]) {
            return false;
        }
    }

    true
}

fn are_patch_lines_equal(line0: &Line, line1: &Line) -> bool {
    // only check for line type and line value for equality,
    // ignore source/target/diff line numbers
    line0.line_type == line1.line_type && line0.value == line1.value
}

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

    #[test]
    fn test_non_diff_contents() {
        assert_eq!(true, DiffOfDiffs::new("some-non-diff", "some-non-diff").are_equal());

        let diffs = DiffOfDiffs::new("some-non-diff", "some-other-non-diff");
        assert_eq!(false, diffs.are_equal());
        assert_eq!(Vec::<PatchedFile>::new(), diffs.different_patch_files());
    }

    #[test]
    fn test_git_diff_and_diff_line_numbers() {
        let diff0 = r#"
diff --git a/Cargo.toml b/Cargo.toml
index 43f2e75..42eaec0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -31,6 +31,7 @@ thread-id = "3.0.0"
 threadpool = "1.3.2"
 time = "0.1.35"
 toml = "0.2.1"
+unidiff = "0.2.0"
 url = "1.2.4"

 [lib]
diff --git a/src/lib.rs b/src/lib.rs
index 4442a2c..007a2cf 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -11,6 +11,7 @@ extern crate serde;
 extern crate serde_json;
 extern crate threadpool;
 extern crate toml;
+extern crate unidiff;
 extern crate url;

 #[macro_use]
@@ -19,6 +20,7 @@ extern crate log;
 extern crate serde_derive;

 pub mod config;
+pub mod diffs;
 pub mod dir_pool;
 pub mod force_push;
 pub mod git;"#;

        let diffs = DiffOfDiffs::new(&diff0, &diff0);
        assert_eq!(true, diffs.are_equal());
        assert_eq!(Vec::<PatchedFile>::new(), diffs.different_patch_files());

        // same diff, but diff line numbers
        let diff1 = r#"
diff --git a/Cargo.toml b/Cargo.toml
index 43f2e75..42eaec0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -41,6 +41,7 @@ thread-id = "3.0.0"
 threadpool = "1.3.2"
 time = "0.1.35"
 toml = "0.2.1"
+unidiff = "0.2.0"
 url = "1.2.4"

 [lib]
diff --git a/src/lib.rs b/src/lib.rs
index 4442a2c..007a2cf 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,6 +1,7 @@ extern crate serde;
 extern crate serde_json;
 extern crate threadpool;
 extern crate toml;
+extern crate unidiff;
 extern crate url;

 #[macro_use]
@@ -119,6 +120,7 @@ extern crate log;
 extern crate serde_derive;

 pub mod config;
+pub mod diffs;
 pub mod dir_pool;
 pub mod force_push;
 pub mod git;"#;

        assert_eq!(true, DiffOfDiffs::new(&diff0, &diff1).are_equal());
    }

    #[test]
    fn test_different_diff() {
        let diff0 = r#"
diff --git a/Cargo.toml b/Cargo.toml
index 43f2e75..42eaec0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -31,6 +31,7 @@ thread-id = "3.0.0"
 threadpool = "1.3.2"
 time = "0.1.35"
 toml = "0.2.1"
+unidiff = "0.2.0"
 url = "1.2.4"

 [lib]
diff --git a/src/lib.rs b/src/lib.rs
index 4442a2c..007a2cf 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -11,6 +11,7 @@ extern crate serde;
 extern crate serde_json;
 extern crate threadpool;
 extern crate toml;
+extern crate unidiff;
 extern crate url;

 #[macro_use]
@@ -19,6 +20,7 @@ extern crate log;
 extern crate serde_derive;

 pub mod config;
+pub mod diffs;
 pub mod dir_pool;
 pub mod force_push;
 pub mod git;"#;

        let diff1 = r#"
diff --git a/Cargo.toml b/Cargo.toml
index 43f2e75..42eaec0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -41,6 +41,7 @@ thread-id = "3.0.0"
 threadpool = "1.3.2"
 time = "0.1.35"
 toml = "0.2.1"
+unidiff = "0.2.0"
 url = "1.2.4"

 [lib]
diff --git a/src/lib.rs b/src/lib.rs
index 4442a2c..007a2cf 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,6 +1,7 @@ extern crate serde;
 extern crate serde_json;
 extern crate threadpool;
 extern crate toml;
+extern crate unidiff;
 extern crate url;

 #[macro_use]
@@ -119,6 +120,7 @@ extern crate log;
 extern crate serde_derive;

 pub mod ** OTHER_THING_HERE **;
+pub mod diffs;
 pub mod dir_pool;
 pub mod force_push;
 pub mod git;"
diff --git a/src/fake.rs b/src/fake.rs
index 4442a2c..007a2cf 100644
--- a/src/fake.rs
+++ b/src/fake.rs
@@ -1,6 +1,7 @@ extern crate serde;
 extern crate serde_json;
 extern crate threadpool;
 extern crate toml;
+extern crate unidiff;
 extern crate url;

 #[macro_use]
@@ -119,6 +120,7 @@ extern crate log;
 extern crate serde_derive;

 pub mod ** OTHER_THING_HERE **;
+pub mod diffs;
 pub mod dir_pool;
 pub mod force_push;
 pub mod git;"#;

        let diffs = DiffOfDiffs::new(&diff0, &diff1);
        assert_eq!(false, diffs.are_equal());

        let diff_files = diffs.different_patch_files();
        assert_eq!(2, diff_files.len());
        assert_eq!("a/src/lib.rs", diff_files[0].source_file);
        assert_eq!("b/src/lib.rs", diff_files[0].target_file);
        assert_eq!("src/lib.rs", diff_files[0].path());
        assert_eq!("src/fake.rs", diff_files[1].path());
    }
}
<|endoftext|>


<commit_before>use crate::{context::Has, graphics::gpu::bind_group::BindGroupBuilder, GameError, GameResult};

use super::{
    context::GraphicsContext,
    draw::{DrawParam, DrawUniforms, Std140DrawUniforms},
    gpu::arc::{ArcBindGroup, ArcBindGroupLayout, ArcBuffer},
    internal_canvas::InstanceArrayView,
    transform_rect, Canvas, Draw, Drawable, Image, Mesh, Rect, WgpuContext,
};
use crevice::std140::AsStd140;
use std::{
    collections::BTreeMap,
    sync::{
        atomic::{AtomicBool, AtomicU32, Ordering::SeqCst},
        Mutex,
    },
};

/// Array of instances for fast rendering of many meshes.
///
/// Traditionally known as a "batch".
#[derive(Debug)]
pub struct InstanceArray {
    pub(crate) buffer: Mutex<ArcBuffer>,
    pub(crate) indices: Mutex<ArcBuffer>,
    pub(crate) bind_group: Mutex<ArcBindGroup>,
    pub(crate) bind_layout: ArcBindGroupLayout,
    pub(crate) image: Image,
    pub(crate) ordered: bool,
    dirty: AtomicBool,
    capacity: AtomicU32,
    uniforms: Vec<Std140DrawUniforms>,
    params: Vec<DrawParam>,
}

impl InstanceArray {
    /// Creates a new [InstanceArray] capable of storing up to n-`capacity` instances
    /// (this can be changed and is resized automatically when needed).
    ///
    /// If `image` is `None`, a 1x1 white image will be used which can be used to draw solid rectangles.
    ///
    /// `ordered` controls whether the z-order of instances is simply defined by their indices
    /// (unordered), or by the actual order of their [z-values] (ordered). Intuitively, creating such
    /// order by comparing the [z-values] comes at a slight performance cost.
    ///
    /// [z-values]:crate::graphics::ZIndex
    pub fn new(
        gfx: &impl Has<GraphicsContext>,
        image: impl Into<Option<Image>>,
        capacity: u32,
        ordered: bool,
    ) -> Self {
        let gfx = gfx.retrieve();
        InstanceArray::new_wgpu(
            &gfx.wgpu,
            gfx.instance_bind_layout.clone(),
            image.into().unwrap_or_else(|| gfx.white_image.clone()),
            capacity,
            ordered,
        )
    }

    fn new_wgpu(
        wgpu: &WgpuContext,
        bind_layout: ArcBindGroupLayout,
        image: Image,
        capacity: u32,
        ordered: bool,
    ) -> Self {
        assert!(capacity > 0);

        let buffer = ArcBuffer::new(wgpu.device.create_buffer(&wgpu::BufferDescriptor {
            label: None,
            size: DrawUniforms::std140_size_static() as u64 * capacity as u64,
            usage: wgpu::BufferUsages::STORAGE
                | wgpu::BufferUsages::COPY_DST
                | wgpu::BufferUsages::COPY_SRC,
            mapped_at_creation: false,
        }));

        let indices = ArcBuffer::new(wgpu.device.create_buffer(&wgpu::BufferDescriptor {
            label: None,
            size: if ordered {
                std::mem::size_of::<u32>() as u64 * capacity as u64
            } else {
                4 // min for layout
            },
            usage: wgpu::BufferUsages::STORAGE
                | wgpu::BufferUsages::COPY_SRC
                | wgpu::BufferUsages::COPY_DST,
            mapped_at_creation: false,
        }));

        let bind_group = BindGroupBuilder::new()
            .buffer(
                &buffer,
                0,
                wgpu::ShaderStages::VERTEX,
                wgpu::BufferBindingType::Storage { read_only: true },
                false,
                None,
            )
            .buffer(
                &indices,
                0,
                wgpu::ShaderStages::VERTEX,
                wgpu::BufferBindingType::Storage { read_only: true },
                false,
                None,
            );
        let bind_group =
            ArcBindGroup::new(wgpu.device.create_bind_group(&wgpu::BindGroupDescriptor {
                label: None,
                layout: &bind_layout,
                entries: bind_group.entries(),
            }));

        let uniforms = Vec::with_capacity(capacity as usize);
        let params = Vec::with_capacity(capacity as usize);

        InstanceArray {
            buffer: Mutex::new(buffer),
            indices: Mutex::new(indices),
            bind_group: Mutex::new(bind_group),
            bind_layout,
            image,
            ordered,
            dirty: AtomicBool::new(false),
            capacity: AtomicU32::new(capacity),
            uniforms,
            params,
        }
    }

    /// Resets all the instance data to a set of `DrawParam`.
    pub fn set(&mut self, instances: impl IntoIterator<Item = DrawParam>) {
        self.dirty.store(true, SeqCst);
        self.params.clear();
        self.params.extend(instances);
        self.uniforms.clear();
        self.uniforms.extend(self.params.iter().map(|x| {
            DrawUniforms::from_param(
                x,
                [self.image.width() as f32, self.image.height() as f32].into(),
            )
            .as_std140()
        }));
    }

    /// Pushes a new instance onto the end.
    pub fn push(&mut self, instance: DrawParam) {
        self.dirty.store(true, SeqCst);
        self.uniforms.push(
            DrawUniforms::from_param(
                &instance,
                [self.image.width() as f32, self.image.height() as f32].into(),
            )
            .as_std140(),
        );
        self.params.push(instance);
    }

    /// Updates an existing instance at a given index, if it is valid.
    pub fn update(&mut self, index: u32, instance: DrawParam) {
        if let Some((uniform, param)) = self
            .uniforms
            .get_mut(index as usize)
            .and_then(|x| Some((x, self.params.get_mut(index as usize)?)))
        {
            self.dirty.store(true, SeqCst);
            *uniform = DrawUniforms::from_param(
                &instance,
                [self.image.width() as f32, self.image.height() as f32].into(),
            )
            .as_std140();
            *param = instance;
        }
    }

    /// Clears all instance data.
    pub fn clear(&mut self) {
        // don't need to set dirty here
        self.uniforms.clear();
        self.params.clear();
    }

    /// Returns whether the instance data has been changed without being flushed (i.e., uploaded to the GPU).
    #[inline]
    pub fn is_dirty(&self) -> bool {
        self.dirty.load(SeqCst)
    }

    /// Returns an immutable slice of all the instance data in this [`InstanceArray`].
    #[inline]
    pub fn instances(&self) -> &[DrawParam] {
        &self.params
    }

    #[allow(unsafe_code)]
    pub(crate) fn flush_wgpu(&self, wgpu: &WgpuContext) -> GameResult<()> {
        if !self.dirty.load(SeqCst) {
            return Ok(());
        } else {
            self.dirty.store(false, SeqCst);
        }

        let len = self.uniforms.len() as u32;
        if len > self.capacity.load(SeqCst) {
            let mut resized = InstanceArray::new_wgpu(
                wgpu,
                self.bind_layout.clone(),
                self.image.clone(),
                len,
                self.ordered,
            );
            *self.buffer.lock().map_err(|_| GameError::LockError)? =
                resized.buffer.get_mut().unwrap().clone();
            *self.indices.lock().map_err(|_| GameError::LockError)? =
                resized.indices.get_mut().unwrap().clone();
            *self.bind_group.lock().map_err(|_| GameError::LockError)? =
                resized.bind_group.get_mut().unwrap().clone();
            self.capacity.store(len, SeqCst);
        }

        wgpu.queue.write_buffer(
            &self.buffer.lock().unwrap(),
            0,
            bytemuck::cast_slice(self.uniforms.as_slice()),
        );

        if self.ordered {
            let mut layers = BTreeMap::<_, Vec<_>>::new();
            for (i, param) in self.params.iter().enumerate() {
                layers.entry(param.z).or_default().push(i);
            }
            let indices = layers.into_values().flatten().collect::<Vec<_>>();
            wgpu.queue.write_buffer(
                &self.indices.lock().unwrap(),
                0,
                bytemuck::cast_slice(indices.as_slice()),
            );
        }

        Ok(())
    }

    /// Changes the capacity of this `InstanceArray` while preserving instances.
    ///
    /// If `new_capacity` is less than the `len`, the instances will be truncated.
    pub fn resize(&mut self, gfx: &impl Has<GraphicsContext>, new_capacity: u32) {
        let resized = InstanceArray::new(gfx, self.image.clone(), new_capacity, self.ordered);
        self.buffer = resized.buffer;
        self.indices = resized.indices;
        self.capacity.store(new_capacity, SeqCst);
        self.dirty.store(true, SeqCst);
        self.uniforms.truncate(new_capacity as usize);
        self.params.truncate(new_capacity as usize);
        self.uniforms
            .reserve((new_capacity as usize).min(self.uniforms.len()) - self.uniforms.len());
        self.params
            .reserve((new_capacity as usize).min(self.params.len()) - self.params.len());
    }

    /// Returns this `InstanceArray`'s associated `image`.
    #[inline]
    pub fn image(&self) -> Image {
        self.image.clone()
    }

    /// Returns the number of instances this [InstanceArray] is capable of holding.
    /// This number was specified when creating the [InstanceArray], or if the [InstanceArray]
    /// was automatically resized, the greatest length of instances.
    #[inline]
    pub fn capacity(&self) -> usize {
        self.capacity.load(SeqCst) as usize
    }

    /// This is equivalent to `<InstanceArray as Drawable>::dimensions()` (see [`Drawable::dimensions()`]), but with a mesh taken into account.
    ///
    /// Essentially, consider `<InstanceArray as Drawable>::dimensions()` to be the bounds when the [`InstanceArray`] is drawn with `canvas.draw()`,
    /// and consider [`InstanceArray::dimensions_meshed()`] to be the bounds when the [`InstanceArray`] is drawn with `canvas.draw_instanced_mesh()`.
    pub fn dimensions_meshed(&self, gfx: &impl Has<GraphicsContext>, mesh: &Mesh) -> Option<Rect> {
        if self.params.is_empty() {
            return None;
        }
        let dimensions = mesh.dimensions(gfx)?;
        self.params
            .iter()
            .map(|&param| transform_rect(dimensions, param))
            .fold(None, |acc: Option<Rect>, rect| {
                Some(if let Some(acc) = acc {
                    acc.combine_with(rect)
                } else {
                    rect
                })
            })
    }
}

impl Drawable for InstanceArray {
    fn draw(&self, canvas: &mut Canvas, param: impl Into<DrawParam>) {
        self.flush_wgpu(&canvas.wgpu).unwrap();
        canvas.push_draw(
            Draw::MeshInstances {
                mesh: canvas.default_resources().mesh.clone(),
                instances: InstanceArrayView::from_instances(self).unwrap(),
            },
            param.into(),
        );
    }

    fn dimensions(&self, gfx: &impl Has<GraphicsContext>) -> Option<Rect> {
        let gfx = gfx.retrieve();
        if self.params.is_empty() {
            return None;
        }
        let dimensions = self.image.dimensions(gfx)?;
        self.params
            .iter()
            .map(|&param| transform_rect(dimensions, param))
            .fold(None, |acc: Option<Rect>, rect| {
                Some(if let Some(acc) = acc {
                    acc.combine_with(rect)
                } else {
                    rect
                })
            })
    }
}
<commit_msg>explicit cast u32 ordered instance idx<commit_after>use crate::{context::Has, graphics::gpu::bind_group::BindGroupBuilder, GameError, GameResult};

use super::{
    context::GraphicsContext,
    draw::{DrawParam, DrawUniforms, Std140DrawUniforms},
    gpu::arc::{ArcBindGroup, ArcBindGroupLayout, ArcBuffer},
    internal_canvas::InstanceArrayView,
    transform_rect, Canvas, Draw, Drawable, Image, Mesh, Rect, WgpuContext,
};
use crevice::std140::AsStd140;
use std::{
    collections::BTreeMap,
    sync::{
        atomic::{AtomicBool, AtomicU32, Ordering::SeqCst},
        Mutex,
    },
};

/// Array of instances for fast rendering of many meshes.
///
/// Traditionally known as a "batch".
#[derive(Debug)]
pub struct InstanceArray {
    pub(crate) buffer: Mutex<ArcBuffer>,
    pub(crate) indices: Mutex<ArcBuffer>,
    pub(crate) bind_group: Mutex<ArcBindGroup>,
    pub(crate) bind_layout: ArcBindGroupLayout,
    pub(crate) image: Image,
    pub(crate) ordered: bool,
    dirty: AtomicBool,
    capacity: AtomicU32,
    uniforms: Vec<Std140DrawUniforms>,
    params: Vec<DrawParam>,
}

impl InstanceArray {
    /// Creates a new [InstanceArray] capable of storing up to n-`capacity` instances
    /// (this can be changed and is resized automatically when needed).
    ///
    /// If `image` is `None`, a 1x1 white image will be used which can be used to draw solid rectangles.
    ///
    /// `ordered` controls whether the z-order of instances is simply defined by their indices
    /// (unordered), or by the actual order of their [z-values] (ordered). Intuitively, creating such
    /// order by comparing the [z-values] comes at a slight performance cost.
    ///
    /// [z-values]:crate::graphics::ZIndex
    pub fn new(
        gfx: &impl Has<GraphicsContext>,
        image: impl Into<Option<Image>>,
        capacity: u32,
        ordered: bool,
    ) -> Self {
        let gfx = gfx.retrieve();
        InstanceArray::new_wgpu(
            &gfx.wgpu,
            gfx.instance_bind_layout.clone(),
            image.into().unwrap_or_else(|| gfx.white_image.clone()),
            capacity,
            ordered,
        )
    }

    fn new_wgpu(
        wgpu: &WgpuContext,
        bind_layout: ArcBindGroupLayout,
        image: Image,
        capacity: u32,
        ordered: bool,
    ) -> Self {
        assert!(capacity > 0);

        let buffer = ArcBuffer::new(wgpu.device.create_buffer(&wgpu::BufferDescriptor {
            label: None,
            size: DrawUniforms::std140_size_static() as u64 * capacity as u64,
            usage: wgpu::BufferUsages::STORAGE
                | wgpu::BufferUsages::COPY_DST
                | wgpu::BufferUsages::COPY_SRC,
            mapped_at_creation: false,
        }));

        let indices = ArcBuffer::new(wgpu.device.create_buffer(&wgpu::BufferDescriptor {
            label: None,
            size: if ordered {
                std::mem::size_of::<u32>() as u64 * capacity as u64
            } else {
                4 // min for layout
            },
            usage: wgpu::BufferUsages::STORAGE
                | wgpu::BufferUsages::COPY_SRC
                | wgpu::BufferUsages::COPY_DST,
            mapped_at_creation: false,
        }));

        let bind_group = BindGroupBuilder::new()
            .buffer(
                &buffer,
                0,
                wgpu::ShaderStages::VERTEX,
                wgpu::BufferBindingType::Storage { read_only: true },
                false,
                None,
            )
            .buffer(
                &indices,
                0,
                wgpu::ShaderStages::VERTEX,
                wgpu::BufferBindingType::Storage { read_only: true },
                false,
                None,
            );
        let bind_group =
            ArcBindGroup::new(wgpu.device.create_bind_group(&wgpu::BindGroupDescriptor {
                label: None,
                layout: &bind_layout,
                entries: bind_group.entries(),
            }));

        let uniforms = Vec::with_capacity(capacity as usize);
        let params = Vec::with_capacity(capacity as usize);

        InstanceArray {
            buffer: Mutex::new(buffer),
            indices: Mutex::new(indices),
            bind_group: Mutex::new(bind_group),
            bind_layout,
            image,
            ordered,
            dirty: AtomicBool::new(false),
            capacity: AtomicU32::new(capacity),
            uniforms,
            params,
        }
    }

    /// Resets all the instance data to a set of `DrawParam`.
    pub fn set(&mut self, instances: impl IntoIterator<Item = DrawParam>) {
        self.dirty.store(true, SeqCst);
        self.params.clear();
        self.params.extend(instances);
        self.uniforms.clear();
        self.uniforms.extend(self.params.iter().map(|x| {
            DrawUniforms::from_param(
                x,
                [self.image.width() as f32, self.image.height() as f32].into(),
            )
            .as_std140()
        }));
    }

    /// Pushes a new instance onto the end.
    pub fn push(&mut self, instance: DrawParam) {
        self.dirty.store(true, SeqCst);
        self.uniforms.push(
            DrawUniforms::from_param(
                &instance,
                [self.image.width() as f32, self.image.height() as f32].into(),
            )
            .as_std140(),
        );
        self.params.push(instance);
    }

    /// Updates an existing instance at a given index, if it is valid.
    pub fn update(&mut self, index: u32, instance: DrawParam) {
        if let Some((uniform, param)) = self
            .uniforms
            .get_mut(index as usize)
            .and_then(|x| Some((x, self.params.get_mut(index as usize)?)))
        {
            self.dirty.store(true, SeqCst);
            *uniform = DrawUniforms::from_param(
                &instance,
                [self.image.width() as f32, self.image.height() as f32].into(),
            )
            .as_std140();
            *param = instance;
        }
    }

    /// Clears all instance data.
    pub fn clear(&mut self) {
        // don't need to set dirty here
        self.uniforms.clear();
        self.params.clear();
    }

    /// Returns whether the instance data has been changed without being flushed (i.e., uploaded to the GPU).
    #[inline]
    pub fn is_dirty(&self) -> bool {
        self.dirty.load(SeqCst)
    }

    /// Returns an immutable slice of all the instance data in this [`InstanceArray`].
    #[inline]
    pub fn instances(&self) -> &[DrawParam] {
        &self.params
    }

    #[allow(unsafe_code)]
    pub(crate) fn flush_wgpu(&self, wgpu: &WgpuContext) -> GameResult<()> {
        if !self.dirty.load(SeqCst) {
            return Ok(());
        } else {
            self.dirty.store(false, SeqCst);
        }

        let len = self.uniforms.len() as u32;
        if len > self.capacity.load(SeqCst) {
            let mut resized = InstanceArray::new_wgpu(
                wgpu,
                self.bind_layout.clone(),
                self.image.clone(),
                len,
                self.ordered,
            );
            *self.buffer.lock().map_err(|_| GameError::LockError)? =
                resized.buffer.get_mut().unwrap().clone();
            *self.indices.lock().map_err(|_| GameError::LockError)? =
                resized.indices.get_mut().unwrap().clone();
            *self.bind_group.lock().map_err(|_| GameError::LockError)? =
                resized.bind_group.get_mut().unwrap().clone();
            self.capacity.store(len, SeqCst);
        }

        wgpu.queue.write_buffer(
            &self.buffer.lock().unwrap(),
            0,
            bytemuck::cast_slice(self.uniforms.as_slice()),
        );

        if self.ordered {
            let mut layers = BTreeMap::<_, Vec<_>>::new();
            for (i, param) in self.params.iter().enumerate() {
                layers.entry(param.z).or_default().push(i as u32);
            }
            let indices = layers.into_values().flatten().collect::<Vec<_>>();
            wgpu.queue.write_buffer(
                &self.indices.lock().unwrap(),
                0,
                bytemuck::cast_slice(indices.as_slice()),
            );
        }

        Ok(())
    }

    /// Changes the capacity of this `InstanceArray` while preserving instances.
    ///
    /// If `new_capacity` is less than the `len`, the instances will be truncated.
    pub fn resize(&mut self, gfx: &impl Has<GraphicsContext>, new_capacity: u32) {
        let resized = InstanceArray::new(gfx, self.image.clone(), new_capacity, self.ordered);
        self.buffer = resized.buffer;
        self.indices = resized.indices;
        self.capacity.store(new_capacity, SeqCst);
        self.dirty.store(true, SeqCst);
        self.uniforms.truncate(new_capacity as usize);
        self.params.truncate(new_capacity as usize);
        self.uniforms
            .reserve((new_capacity as usize).min(self.uniforms.len()) - self.uniforms.len());
        self.params
            .reserve((new_capacity as usize).min(self.params.len()) - self.params.len());
    }

    /// Returns this `InstanceArray`'s associated `image`.
    #[inline]
    pub fn image(&self) -> Image {
        self.image.clone()
    }

    /// Returns the number of instances this [InstanceArray] is capable of holding.
    /// This number was specified when creating the [InstanceArray], or if the [InstanceArray]
    /// was automatically resized, the greatest length of instances.
    #[inline]
    pub fn capacity(&self) -> usize {
        self.capacity.load(SeqCst) as usize
    }

    /// This is equivalent to `<InstanceArray as Drawable>::dimensions()` (see [`Drawable::dimensions()`]), but with a mesh taken into account.
    ///
    /// Essentially, consider `<InstanceArray as Drawable>::dimensions()` to be the bounds when the [`InstanceArray`] is drawn with `canvas.draw()`,
    /// and consider [`InstanceArray::dimensions_meshed()`] to be the bounds when the [`InstanceArray`] is drawn with `canvas.draw_instanced_mesh()`.
    pub fn dimensions_meshed(&self, gfx: &impl Has<GraphicsContext>, mesh: &Mesh) -> Option<Rect> {
        if self.params.is_empty() {
            return None;
        }
        let dimensions = mesh.dimensions(gfx)?;
        self.params
            .iter()
            .map(|&param| transform_rect(dimensions, param))
            .fold(None, |acc: Option<Rect>, rect| {
                Some(if let Some(acc) = acc {
                    acc.combine_with(rect)
                } else {
                    rect
                })
            })
    }
}

impl Drawable for InstanceArray {
    fn draw(&self, canvas: &mut Canvas, param: impl Into<DrawParam>) {
        self.flush_wgpu(&canvas.wgpu).unwrap();
        canvas.push_draw(
            Draw::MeshInstances {
                mesh: canvas.default_resources().mesh.clone(),
                instances: InstanceArrayView::from_instances(self).unwrap(),
            },
            param.into(),
        );
    }

    fn dimensions(&self, gfx: &impl Has<GraphicsContext>) -> Option<Rect> {
        let gfx = gfx.retrieve();
        if self.params.is_empty() {
            return None;
        }
        let dimensions = self.image.dimensions(gfx)?;
        self.params
            .iter()
            .map(|&param| transform_rect(dimensions, param))
            .fold(None, |acc: Option<Rect>, rect| {
                Some(if let Some(acc) = acc {
                    acc.combine_with(rect)
                } else {
                    rect
                })
            })
    }
}
<|endoftext|>


<commit_before>use std::cmp::Ordering;
use std::rc::Rc;
use std::hash::Hash;
use util::integerisable::Integerisable1;
use integeriser::{HashIntegeriser, Integeriser};

/// upside-down tree with a designated position (the *stack pointer*) and *nodes* of type `a`.
#[derive(Debug, Clone)]
pub struct TreeStack<A> {
    parent: Option<(usize, Rc<TreeStack<A>>)>,
    value: A,
    children: Vec<Option<Rc<TreeStack<A>>>>,
}

impl<A> TreeStack<A> {
    /// Creates a new `TreeStack<A>` with root label `a`.
    pub fn new(a: A) -> Self {
        TreeStack { value: a, children: Vec::new(), parent: None }
    }

    /// Applies a function `Fn(&A) -> B`to every node in a `TreeStack<A>`.
    pub fn map<F, B>(&self, f: &mut F) -> TreeStack<B>
        where F: FnMut(&A) -> B,
    {
        let new_value = f(&self.value);
        let new_parent = match self.parent {
            Some((i, ref p)) => Some((i, Rc::new(p.map(f)))),
            None => None,
        };
        let new_children = self.children.iter().map(|o| o.clone().map(|v| Rc::new(v.map(f)))).collect();
        TreeStack { parent: new_parent, value: new_value, children: new_children }
    }

    /// Returns `True` if the stack pointer points to the bottom node.
    pub fn is_at_bottom(&self) -> bool {
        self.parent.is_none()
    }

    /// Returns a reference to label of the current node.
    pub fn current_symbol(&self) -> &A {
        &self.value
    }

    /// Replaces the current value by the given value.
    pub fn set(mut self, a: A) -> Self {
        self.value = a;
        self
    }

    /// Writes a value to the specified child position (if the child position is vacant) and returns the resulting `TreeStack` in an `Ok`.
    /// Returns the unmodified `TreeStack` in an `Err` otherwise.
    pub fn push(mut self, n: usize, a: A) -> Result<Self, Self> {
        if n >= self.children.len() {
            let len = n - self.children.len() + 1;
            let filler = &mut vec![None; len];
            self.children.append(filler);
        }

        if self.children[n].is_none() {
            Ok(TreeStack { value: a,
                           children: Vec::new(),
                           parent: Some((n, Rc::new(self))) })
        } else {
            Err(self)
        }
    }
}

impl<A: Clone> TreeStack<A> {
    /// Goes up to a specific child position (if this position is occupied) and returns the resulting `TreeStack` in an `Ok`.
    /// Returns the unmodified `TreeStack` in an `Err` otherwise.
    pub fn up(mut self, n: usize) -> Result<Self, Self> {
        match {
            if self.children.len() > n {
                self.children.push(None);
                self.children.swap_remove(n)
            } else {
                None
            }
        } {
            Some(ref tn) => Ok(TreeStack { value: tn.value.clone(),
                                           children: tn.children.clone(),
                                           parent: Some((n, Rc::new(self))) }),
            _ => Err(self),
        }
    }

    /// Goes down to the parent position (if there is one) and returns the resulting `TreeStack` in an `Ok`.
    /// Returns the unmodified `TreeStack` in an `Err` otherwise.
    pub fn down(mut self) -> Result<Self, Self> {
        match self.parent.take() {
            Some((n, pn)) => {
                let mut new_pch = pn.children.clone();
                new_pch[n] = Some(Rc::new(self));
                Ok(TreeStack { value: pn.value.clone(),
                               children: new_pch,
                               parent: pn.parent.clone() })
            },
            None => Err(self),
        }
    }
}

impl<A: Clone + Eq + Hash> Integerisable1 for TreeStack<A> {
    type AInt = TreeStack<usize>;
    type I = HashIntegeriser<A>;

    fn integerise(&self, integeriser: &mut Self::I) -> Self::AInt {
        self.map(&mut move |v| integeriser.integerise(v.clone()))
    }

    fn un_integerise(aint: &Self::AInt, integeriser: &Self::I) -> Self {
        aint.map(&mut |&v| integeriser.find_value(v).unwrap().clone())
    }
}

impl<A: PartialEq> PartialEq for TreeStack<A> {
    fn eq(&self, other: &Self) -> bool {
        let comp = |p1, p2| Rc::ptr_eq(p1, p2) || p1 == p2;
        self.value == other.value
            && match (&self.parent, &other.parent) {
                (&Some((i1, ref p1)), &Some((i2, ref p2))) => i1 == i2 && comp(p1, p2),
                (&None, &None)                             => true,
                _                                          => false,
            }
            && self.children == other.children
    }
}

impl<A: Eq> Eq for TreeStack<A> {}

impl<A: PartialOrd> PartialOrd for TreeStack<A> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        match self.value.partial_cmp(&other.value) {
            None | Some(Ordering::Equal) =>
                match self.parent.partial_cmp(&other.parent) {
                    None | Some(Ordering::Equal) => self.children.partial_cmp(&other.children),
                    x => x,
                }
            x => x,
        }
    }
}

impl<A: Ord> Ord for TreeStack<A> {
    fn cmp(&self, other: &Self) -> Ordering {
        match self.value.cmp(&other.value) {
            Ordering::Equal =>
                match self.parent.cmp(&other.parent) {
                    Ordering::Equal => self.children.cmp(&other.children),
                    x => x,
                }
            x => x,
        }
    }
}

#[test]
fn test_tree_stack() {
    let mut ts: TreeStack<u8> = TreeStack::new(0);
    assert_eq!(&0, ts.current_symbol());

    ts = ts.push(1, 1).unwrap().clone();
    assert_eq!(&1, ts.current_symbol());

    ts = ts.down().unwrap().clone();
    assert_eq!(&0, ts.current_symbol());

    ts = ts.push(2, 2).unwrap().clone();
    assert_eq!(&2, ts.current_symbol());

    ts = ts.down().unwrap().clone();
    ts = ts.up(1).unwrap().clone();
    assert_eq!(&1, ts.current_symbol());

    let ts1 = ts.clone().push(1, 11);
    let ts2 = ts.push(1, 11);
    assert_eq!(ts1, ts2);
}
<commit_msg>src/tree_stack_automaton/tree_stack.rs: added to_tree()<commit_after>use std::cmp::Ordering;
use std::rc::Rc;
use std::hash::Hash;
use util::integerisable::Integerisable1;
use integeriser::{HashIntegeriser, Integeriser};

/// upside-down tree with a designated position (the *stack pointer*) and *nodes* of type `a`.
#[derive(Debug, Clone)]
pub struct TreeStack<A> {
    parent: Option<(usize, Rc<TreeStack<A>>)>,
    value: A,
    children: Vec<Option<Rc<TreeStack<A>>>>,
}

impl<A> TreeStack<A> {
    /// Creates a new `TreeStack<A>` with root label `a`.
    pub fn new(a: A) -> Self {
        TreeStack { value: a, children: Vec::new(), parent: None }
    }

    /// Applies a function `Fn(&A) -> B`to every node in a `TreeStack<A>`.
    pub fn map<F, B>(&self, f: &mut F) -> TreeStack<B>
        where F: FnMut(&A) -> B,
    {
        let new_value = f(&self.value);
        let new_parent = match self.parent {
            Some((i, ref p)) => Some((i, Rc::new(p.map(f)))),
            None => None,
        };
        let new_children = self.children.iter().map(|o| o.clone().map(|v| Rc::new(v.map(f)))).collect();
        TreeStack { parent: new_parent, value: new_value, children: new_children }
    }

    /// Returns `True` if the stack pointer points to the bottom node.
    pub fn is_at_bottom(&self) -> bool {
        self.parent.is_none()
    }

    /// Returns a reference to label of the current node.
    pub fn current_symbol(&self) -> &A {
        &self.value
    }

    /// Replaces the current value by the given value.
    pub fn set(mut self, a: A) -> Self {
        self.value = a;
        self
    }

    /// Writes a value to the specified child position (if the child position is vacant) and returns the resulting `TreeStack` in an `Ok`.
    /// Returns the unmodified `TreeStack` in an `Err` otherwise.
    pub fn push(mut self, n: usize, a: A) -> Result<Self, Self> {
        if n >= self.children.len() {
            let len = n - self.children.len() + 1;
            let filler = &mut vec![None; len];
            self.children.append(filler);
        }

        if self.children[n].is_none() {
            Ok(TreeStack { value: a,
                           children: Vec::new(),
                           parent: Some((n, Rc::new(self))) })
        } else {
            Err(self)
        }
    }
}

impl<A: Clone> TreeStack<A> {
    /// Goes up to a specific child position (if this position is occupied) and returns the resulting `TreeStack` in an `Ok`.
    /// Returns the unmodified `TreeStack` in an `Err` otherwise.
    pub fn up(mut self, n: usize) -> Result<Self, Self> {
        match {
            if self.children.len() > n {
                self.children.push(None);
                self.children.swap_remove(n)
            } else {
                None
            }
        } {
            Some(ref tn) => Ok(TreeStack { value: tn.value.clone(),
                                           children: tn.children.clone(),
                                           parent: Some((n, Rc::new(self))) }),
            _ => Err(self),
        }
    }

    /// Goes down to the parent position (if there is one) and returns the resulting `TreeStack` in an `Ok`.
    /// Returns the unmodified `TreeStack` in an `Err` otherwise.
    pub fn down(mut self) -> Result<Self, Self> {
        match self.parent.take() {
            Some((n, pn)) => {
                let mut new_pch = pn.children.clone();
                new_pch[n] = Some(Rc::new(self));
                Ok(TreeStack { value: pn.value.clone(),
                               children: new_pch,
                               parent: pn.parent.clone() })
            },
            None => Err(self),
        }
    }

    fn to_tree(&self) -> (BTreeMap<Vec<usize>, A>, Vec<usize>) {
        let mut tree_map = BTreeMap::new();
        let mut curr_path = Vec::new();

        if let Some((num, ref parent)) = self.parent {
            let (parent_map, parent_path) = parent.to_tree();
            curr_path = parent_path;
            curr_path.push(num);

            for (path, value) in parent_map.iter() {
                tree_map.insert(path.clone(), value.clone());
            }
        }

        tree_map.insert(curr_path.clone(), self.value.clone());

        for (num, maybe_child) in self.children.iter().enumerate() {
            if let &Some(ref child) = maybe_child {
                let (mut child_map, _) = child.to_tree();

                for (path, value) in child_map {
                    let mut new_path = curr_path.clone();
                    new_path.append(&mut path.clone());
                    new_path.push(num);
                    tree_map.insert(new_path, value.clone());
                }
            }
        }

        (tree_map, curr_path)
    }
}

impl<A: Clone + Eq + Hash> Integerisable1 for TreeStack<A> {
    type AInt = TreeStack<usize>;
    type I = HashIntegeriser<A>;

    fn integerise(&self, integeriser: &mut Self::I) -> Self::AInt {
        self.map(&mut move |v| integeriser.integerise(v.clone()))
    }

    fn un_integerise(aint: &Self::AInt, integeriser: &Self::I) -> Self {
        aint.map(&mut |&v| integeriser.find_value(v).unwrap().clone())
    }
}

impl<A: PartialEq> PartialEq for TreeStack<A> {
    fn eq(&self, other: &Self) -> bool {
        let comp = |p1, p2| Rc::ptr_eq(p1, p2) || p1 == p2;
        self.value == other.value
            && match (&self.parent, &other.parent) {
                (&Some((i1, ref p1)), &Some((i2, ref p2))) => i1 == i2 && comp(p1, p2),
                (&None, &None)                             => true,
                _                                          => false,
            }
            && self.children == other.children
    }
}

impl<A: Eq> Eq for TreeStack<A> {}

impl<A: PartialOrd> PartialOrd for TreeStack<A> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        match self.value.partial_cmp(&other.value) {
            None | Some(Ordering::Equal) =>
                match self.parent.partial_cmp(&other.parent) {
                    None | Some(Ordering::Equal) => self.children.partial_cmp(&other.children),
                    x => x,
                }
            x => x,
        }
    }
}

impl<A: Ord> Ord for TreeStack<A> {
    fn cmp(&self, other: &Self) -> Ordering {
        match self.value.cmp(&other.value) {
            Ordering::Equal =>
                match self.parent.cmp(&other.parent) {
                    Ordering::Equal => self.children.cmp(&other.children),
                    x => x,
                }
            x => x,
        }
    }
}

#[test]
fn test_tree_stack() {
    let mut ts: TreeStack<u8> = TreeStack::new(0);
    assert_eq!(&0, ts.current_symbol());

    ts = ts.push(1, 1).unwrap().clone();
    assert_eq!(&1, ts.current_symbol());

    ts = ts.down().unwrap().clone();
    assert_eq!(&0, ts.current_symbol());

    ts = ts.push(2, 2).unwrap().clone();
    assert_eq!(&2, ts.current_symbol());

    ts = ts.down().unwrap().clone();
    ts = ts.up(1).unwrap().clone();
    assert_eq!(&1, ts.current_symbol());

    let ts1 = ts.clone().push(1, 11);
    let ts2 = ts.push(1, 11);
    assert_eq!(ts1, ts2);
}
<|endoftext|>


<commit_before>use gui::*;
use gui::div;

use std::cell::{Ref, RefCell};
use std::time::Instant;
use std::collections::HashMap;

struct CachedDiv {
    /// Node id of the cached div.
    id: it::NodeId,
    /// Visibility of the cached div.
    visibility: div::ComputedVisibility,
    /// The parent id and bbox of the cached div.
    parent: Option<(it::NodeId, Bbox)>,
}

pub struct View<'a> {
    bbox: Bbox,
    arena: it::Arena<div::SpaceDiv<'a>>,
    root_div: it::NodeId,
    children: Vec<it::NodeId>,
    cache: RefCell<Option<Vec<CachedDiv>>>,
    time_start: Instant,
    /// The scrollbars which need to be drawn per div.
    pending_scrollbars: RefCell<HashMap<it::NodeId, (Option<Bbox>, Option<Bbox>)>>,
}

impl<'a> View<'a> {
    pub fn new(bbox: Bbox) -> Self {
        let mut arena = it::Arena::new();
        let root_div = arena.new_node(
            div::SpaceDiv::full()
                .vertical()
                .vert_align(div::Alignment::Min)
                .build(),
        );
        Self {
            bbox,
            arena,
            root_div,
            children: Vec::new(),
            cache: RefCell::new(None),
            time_start: Instant::now(),
            pending_scrollbars: RefCell::new(HashMap::new()),
        }
    }

    pub fn without_bbox() -> Self {
        let zero = Point::new(0, 0);
        Self::new(Bbox::new(zero, zero))
    }

    pub fn add_div(&mut self, parent: Option<it::NodeId>, div: div::SpaceDiv<'a>) -> it::NodeId {
        let node = self.arena.new_node(div);
        parent
            .unwrap_or(self.root_div)
            .append(node, &mut self.arena);
        self.children.push(node);
        *self.cache.borrow_mut() = None;
        node
    }

    pub fn bbox(&self) -> &Bbox {
        &self.bbox
    }

    pub fn set_bbox(&mut self, bbox: Bbox) {
        if bbox != self.bbox {
            *self.cache.borrow_mut() = None;
        }

        self.bbox = bbox;
    }

    /// Returns the root space div of this view.
    pub fn space_div(&self) -> &div::SpaceDiv<'a> {
        &self.arena[self.root_div].data
    }

    /// Draw the entire frame.
    pub fn draw(&self, frame: &nanovg::Frame) {
        self.pending_scrollbars.borrow_mut().clear();

        if let Some(ref divs) = *self.divs() {
            for cached_div in divs.iter() {
                self.draw_div(
                    &self.arena[cached_div.id].data,
                    cached_div.visibility,
                    cached_div.parent,
                    frame,
                );
            }
        }

        self.draw_scrollbars(frame);
    }

    fn draw_scrollbars(&self, frame: &nanovg::Frame) {
        let draw_bbox = |bbox: Bbox| {
            frame.path(
                |path| {
                    let origin = (bbox.min.x as f32, bbox.min.y as f32);
                    let size = (bbox.size().x as f32, bbox.size().y as f32);
                    path.rect(origin, size);
                    path.fill(nanovg::FillStyle {
                        coloring_style: nanovg::ColoringStyle::Color(
                            Color::rgba(0.0, 1.0, 0.0, 0.4).into(),
                        ),
                        ..Default::default()
                    });
                },
                Default::default(),
            );
        };

        for &(hori_bbox, vert_bbox) in self.pending_scrollbars.borrow().values() {
            if let Some(hori_bbox) = hori_bbox {
                draw_bbox(hori_bbox);
            }
            if let Some(vert_bbox) = vert_bbox {
                draw_bbox(vert_bbox);
            }
        }
    }

    /// Draw a single div.
    fn draw_div(
        &self,
        div: &div::SpaceDiv,
        div_visibility: div::ComputedVisibility,
        parent_bbox: Option<(it::NodeId, Bbox)>,
        frame: &nanovg::Frame,
    ) {
        let (div_bbox, x, y) = match div_visibility {
            div::ComputedVisibility::Invisible => return,
            div::ComputedVisibility::Visible { bbox, x, y } => (bbox, x, y),
        };

        // Build clip bbox
        let clip = {
            let mut clip = div_bbox;
            if let Some((min_val, max_val)) = x.min_max() {
                clip.min.x = min_val;
                clip.max.x = max_val;
            }
            if let Some((min_val, max_val)) = y.min_max() {
                clip.min.y = min_val;
                clip.max.y = max_val;
            }
            clip
        };

        // Draw background color if we have one
        if let Some(color) = div.background_color {
            frame.path(
                |path| {
                    let origin = (clip.min.x as f32, clip.min.y as f32);
                    let size = (clip.size().x as f32, clip.size().y as f32);
                    path.rect(origin, size);
                    path.fill(nanovg::FillStyle {
                        coloring_style: nanovg::ColoringStyle::Color(color.into()),
                        ..Default::default()
                    });
                },
                Default::default(),
            );
        }

        // Generate scrollbars
        if let Some((parent, parent_bbox)) = parent_bbox {
            if let Some(sx) = x.scroll() {
                let width = max(parent_bbox.size().x - sx, 8);
                let height = 16;
                let origin = Point::new(
                    parent_bbox.min.x + div.scroll.get().x,
                    parent_bbox.max.y - height,
                );
                let size = Point::new(width, height);
                let bbox = Bbox::with_size(origin, size);
                let mut bars = self.pending_scrollbars.borrow_mut();
                bars.entry(parent).or_insert((None, None)).0 = Some(bbox);
            }
            if let Some(sy) = y.scroll() {
                let width = 16;
                let height = max(parent_bbox.size().y - sy, 8);
                let origin = Point::new(
                    parent_bbox.max.x - width,
                    parent_bbox.min.y + div.scroll.get().y,
                );
                let size = Point::new(width, height);
                let bbox = Bbox::with_size(origin, size);
                let mut bars = self.pending_scrollbars.borrow_mut();
                bars.entry(parent).or_insert((None, None)).1 = Some(bbox);
            }
        }

        let scrolled_div_bbox = {
            let mut bbox = div_bbox;
            let scroll = div.scroll.get();
            if let div::AxisOverflowBehaviour::Scroll { .. } = x {
                bbox.min.x -= scroll.x;
                bbox.max.x -= scroll.x;
            }
            if let div::AxisOverflowBehaviour::Scroll { .. } = y {
                bbox.min.y -= scroll.y;
                bbox.max.y -= scroll.y;
            }
            bbox
        };

        // Draw the widget
        if let Some(ref widget) = div.widget {
            let mut widget = widget.borrow_mut();
            widget.update();
            widget.draw(scrolled_div_bbox, clip, frame);
        }

        let max_scroll = {
            let x = x.scroll().unwrap_or(0);
            let y = y.scroll().unwrap_or(0);
            Point::new(x, y)
        };

        // Scroll right 1 pixel per frame for testing the scrolling.
        let delta = self.time_start.elapsed();
        let delta = (delta.as_secs() * 1000 + delta.subsec_millis() as u64) as f32 / 1000.0;
        let delta = ((delta.sin() * 0.5 + 0.5) * max_scroll.x as f32) as i32;
        div.scroll.set(point_min(Point::new(delta, 0), max_scroll));
    }

    /// Recursively visit all space divs of this view.
    fn visit_divs<F>(
        &self,
        id: it::NodeId,
        visibility: div::ComputedVisibility,
        parent: Option<(it::NodeId, Bbox)>,
        visitor: &mut F,
    ) where
        F: FnMut(CachedDiv),
    {
        if let div::ComputedVisibility::Visible { bbox, .. } = visibility {
            visitor(CachedDiv {
                id,
                visibility,
                parent,
            });

            let visible_children = self.arena[id].data.children(&self.arena, id, bbox).filter(
                |&(_, vis)| match vis {
                    div::ComputedVisibility::Visible { .. } => true,
                    div::ComputedVisibility::Invisible => false,
                },
            );

            for (c, vis) in visible_children {
                self.visit_divs(c, vis, Some((id, bbox)), visitor);
            }
        }
    }

    fn divs(&self) -> Ref<Option<Vec<CachedDiv>>> {
        let is_none = { self.cache.borrow().is_none() };
        if is_none {
            // Build the cache
            let mut vec = self.cache.borrow_mut();
            let vec = vec.get_or_insert_with(|| Vec::with_capacity(16));
            vec.clear();
            self.visit_divs(
                self.root_div,
                div::ComputedVisibility::Visible {
                    bbox: self.bbox,
                    x: div::AxisOverflowBehaviour::Overflow,
                    y: div::AxisOverflowBehaviour::Overflow,
                },
                None,
                &mut |cached_div| vec.push(cached_div),
            );
        }
        self.cache.borrow()
    }
}
<commit_msg>Bump initial cache capacity to 32 items<commit_after>use gui::*;
use gui::div;

use std::cell::{Ref, RefCell};
use std::time::Instant;
use std::collections::HashMap;

struct CachedDiv {
    /// Node id of the cached div.
    id: it::NodeId,
    /// Visibility of the cached div.
    visibility: div::ComputedVisibility,
    /// The parent id and bbox of the cached div.
    parent: Option<(it::NodeId, Bbox)>,
}

pub struct View<'a> {
    bbox: Bbox,
    arena: it::Arena<div::SpaceDiv<'a>>,
    root_div: it::NodeId,
    children: Vec<it::NodeId>,
    cache: RefCell<Option<Vec<CachedDiv>>>,
    time_start: Instant,
    /// The scrollbars which need to be drawn per div.
    pending_scrollbars: RefCell<HashMap<it::NodeId, (Option<Bbox>, Option<Bbox>)>>,
}

impl<'a> View<'a> {
    pub fn new(bbox: Bbox) -> Self {
        let mut arena = it::Arena::new();
        let root_div = arena.new_node(
            div::SpaceDiv::full()
                .vertical()
                .vert_align(div::Alignment::Min)
                .build(),
        );
        Self {
            bbox,
            arena,
            root_div,
            children: Vec::new(),
            cache: RefCell::new(None),
            time_start: Instant::now(),
            pending_scrollbars: RefCell::new(HashMap::new()),
        }
    }

    pub fn without_bbox() -> Self {
        let zero = Point::new(0, 0);
        Self::new(Bbox::new(zero, zero))
    }

    pub fn add_div(&mut self, parent: Option<it::NodeId>, div: div::SpaceDiv<'a>) -> it::NodeId {
        let node = self.arena.new_node(div);
        parent
            .unwrap_or(self.root_div)
            .append(node, &mut self.arena);
        self.children.push(node);
        *self.cache.borrow_mut() = None;
        node
    }

    pub fn bbox(&self) -> &Bbox {
        &self.bbox
    }

    pub fn set_bbox(&mut self, bbox: Bbox) {
        if bbox != self.bbox {
            *self.cache.borrow_mut() = None;
        }

        self.bbox = bbox;
    }

    /// Returns the root space div of this view.
    pub fn space_div(&self) -> &div::SpaceDiv<'a> {
        &self.arena[self.root_div].data
    }

    /// Draw the entire frame.
    pub fn draw(&self, frame: &nanovg::Frame) {
        self.pending_scrollbars.borrow_mut().clear();

        if let Some(ref divs) = *self.divs() {
            for cached_div in divs.iter() {
                self.draw_div(
                    &self.arena[cached_div.id].data,
                    cached_div.visibility,
                    cached_div.parent,
                    frame,
                );
            }
        }

        self.draw_scrollbars(frame);
    }

    fn draw_scrollbars(&self, frame: &nanovg::Frame) {
        let draw_bbox = |bbox: Bbox| {
            frame.path(
                |path| {
                    let origin = (bbox.min.x as f32, bbox.min.y as f32);
                    let size = (bbox.size().x as f32, bbox.size().y as f32);
                    path.rect(origin, size);
                    path.fill(nanovg::FillStyle {
                        coloring_style: nanovg::ColoringStyle::Color(
                            Color::rgba(0.0, 1.0, 0.0, 0.4).into(),
                        ),
                        ..Default::default()
                    });
                },
                Default::default(),
            );
        };

        for &(hori_bbox, vert_bbox) in self.pending_scrollbars.borrow().values() {
            if let Some(hori_bbox) = hori_bbox {
                draw_bbox(hori_bbox);
            }
            if let Some(vert_bbox) = vert_bbox {
                draw_bbox(vert_bbox);
            }
        }
    }

    /// Draw a single div.
    fn draw_div(
        &self,
        div: &div::SpaceDiv,
        div_visibility: div::ComputedVisibility,
        parent_bbox: Option<(it::NodeId, Bbox)>,
        frame: &nanovg::Frame,
    ) {
        let (div_bbox, x, y) = match div_visibility {
            div::ComputedVisibility::Invisible => return,
            div::ComputedVisibility::Visible { bbox, x, y } => (bbox, x, y),
        };

        // Build clip bbox
        let clip = {
            let mut clip = div_bbox;
            if let Some((min_val, max_val)) = x.min_max() {
                clip.min.x = min_val;
                clip.max.x = max_val;
            }
            if let Some((min_val, max_val)) = y.min_max() {
                clip.min.y = min_val;
                clip.max.y = max_val;
            }
            clip
        };

        // Draw background color if we have one
        if let Some(color) = div.background_color {
            frame.path(
                |path| {
                    let origin = (clip.min.x as f32, clip.min.y as f32);
                    let size = (clip.size().x as f32, clip.size().y as f32);
                    path.rect(origin, size);
                    path.fill(nanovg::FillStyle {
                        coloring_style: nanovg::ColoringStyle::Color(color.into()),
                        ..Default::default()
                    });
                },
                Default::default(),
            );
        }

        // Generate scrollbars
        if let Some((parent, parent_bbox)) = parent_bbox {
            if let Some(sx) = x.scroll() {
                let width = max(parent_bbox.size().x - sx, 8);
                let height = 16;
                let origin = Point::new(
                    parent_bbox.min.x + div.scroll.get().x,
                    parent_bbox.max.y - height,
                );
                let size = Point::new(width, height);
                let bbox = Bbox::with_size(origin, size);
                let mut bars = self.pending_scrollbars.borrow_mut();
                bars.entry(parent).or_insert((None, None)).0 = Some(bbox);
            }
            if let Some(sy) = y.scroll() {
                let width = 16;
                let height = max(parent_bbox.size().y - sy, 8);
                let origin = Point::new(
                    parent_bbox.max.x - width,
                    parent_bbox.min.y + div.scroll.get().y,
                );
                let size = Point::new(width, height);
                let bbox = Bbox::with_size(origin, size);
                let mut bars = self.pending_scrollbars.borrow_mut();
                bars.entry(parent).or_insert((None, None)).1 = Some(bbox);
            }
        }

        let scrolled_div_bbox = {
            let mut bbox = div_bbox;
            let scroll = div.scroll.get();
            if let div::AxisOverflowBehaviour::Scroll { .. } = x {
                bbox.min.x -= scroll.x;
                bbox.max.x -= scroll.x;
            }
            if let div::AxisOverflowBehaviour::Scroll { .. } = y {
                bbox.min.y -= scroll.y;
                bbox.max.y -= scroll.y;
            }
            bbox
        };

        // Draw the widget
        if let Some(ref widget) = div.widget {
            let mut widget = widget.borrow_mut();
            widget.update();
            widget.draw(scrolled_div_bbox, clip, frame);
        }

        let max_scroll = {
            let x = x.scroll().unwrap_or(0);
            let y = y.scroll().unwrap_or(0);
            Point::new(x, y)
        };

        // Scroll right 1 pixel per frame for testing the scrolling.
        let delta = self.time_start.elapsed();
        let delta = (delta.as_secs() * 1000 + delta.subsec_millis() as u64) as f32 / 1000.0;
        let delta = ((delta.sin() * 0.5 + 0.5) * max_scroll.x as f32) as i32;
        div.scroll.set(point_min(Point::new(delta, 0), max_scroll));
    }

    /// Recursively visit all space divs of this view.
    fn visit_divs<F>(
        &self,
        id: it::NodeId,
        visibility: div::ComputedVisibility,
        parent: Option<(it::NodeId, Bbox)>,
        visitor: &mut F,
    ) where
        F: FnMut(CachedDiv),
    {
        if let div::ComputedVisibility::Visible { bbox, .. } = visibility {
            visitor(CachedDiv {
                id,
                visibility,
                parent,
            });

            let visible_children = self.arena[id].data.children(&self.arena, id, bbox).filter(
                |&(_, vis)| match vis {
                    div::ComputedVisibility::Visible { .. } => true,
                    div::ComputedVisibility::Invisible => false,
                },
            );

            for (c, vis) in visible_children {
                self.visit_divs(c, vis, Some((id, bbox)), visitor);
            }
        }
    }

    fn divs(&self) -> Ref<Option<Vec<CachedDiv>>> {
        let is_none = { self.cache.borrow().is_none() };
        if is_none {
            // Build the cache
            let mut vec = self.cache.borrow_mut();
            let vec = vec.get_or_insert_with(|| Vec::with_capacity(32));
            vec.clear();
            self.visit_divs(
                self.root_div,
                div::ComputedVisibility::Visible {
                    bbox: self.bbox,
                    x: div::AxisOverflowBehaviour::Overflow,
                    y: div::AxisOverflowBehaviour::Overflow,
                },
                None,
                &mut |cached_div| vec.push(cached_div),
            );
        }
        self.cache.borrow()
    }
}
<|endoftext|>


<commit_before>use std::path::Path;
use std::io::{Reader, IoResult, standard_error, EndOfFile, OtherIoError};
use std::option::{Some, Option};
//use std::c_str::CString;
use std::os::last_os_error;
use libc::types::os::arch::c95::{c_char, c_int, size_t};

enum GzFile {}

#[link(name="z")]
extern {
    fn gzopen(filename: *const c_char, mode: *const c_char) -> *const GzFile;
    fn gzread(file: *const GzFile, buf: *mut u8, len: size_t) -> c_int;
    fn gzclose(file: *const GzFile) -> c_int;
    //fn gzerror(file: *GzFile, errnum: *c_int) -> *c_char;
}

pub struct GzipReader {
    file: *const GzFile,
}

impl GzipReader {
    pub fn open(path: &Path) -> Option<GzipReader> {
        let file = unsafe {
            gzopen(path.to_c_str().unwrap(), "rb".to_c_str().unwrap())
        };
        if file.is_null() {
            fail!("ERROR opening '{}': {}", path.as_str(), last_os_error());
        }
        Some(GzipReader{file: file})
    }
}

impl Reader for GzipReader {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
        unsafe {
            match gzread(self.file, buf.as_mut_ptr(), buf.len() as size_t) {
                size if size > 0 => Ok(size as uint),
                0 => {
                    // TODO: We can define constants for all the error values
                    // and check gzclose() errors
                    gzclose(self.file);
                    Err(standard_error(EndOfFile))
                }
                _ => {
                    // FIXME: See below
                    //let errnum: c_int = 0;
                    //let s = CString::new(gzerror(self.file, &errnum), true);
                    // TODO: We can define constants for all the error values
                    // and check gzclose() errors
                    // FIXME: See above
                    //fail!("ERROR reading file: {}", s.as_str().unwrap());
                    gzclose(self.file);
                    Err(standard_error(OtherIoError))
                }
            }
        }
    }
}
<commit_msg>Better error reporting for gzreader<commit_after>use std::io::{IoResult, standard_error, EndOfFile, OtherIoError, IoError};
use std::c_str::CString;
use std::os::last_os_error;
use libc::{c_int, c_char, c_uint};

enum GzFile {}

#[link(name="z")]
extern {
    fn gzopen(filename: *const c_char, mode: *const c_char) -> *const GzFile;
    fn gzread(file: *const GzFile, buf: *mut u8, len: c_uint) -> c_int;
    fn gzclose(file: *const GzFile) -> c_int;
    fn gzerror(file: *const GzFile, errnum: *mut c_int) -> *const c_char;
}

pub struct GzipReader {
    file: *const GzFile,
    path: Path,
    closed: bool,
}

impl GzipReader {
    pub fn open(path: &Path) -> Option<GzipReader> {
        let file = unsafe {
            gzopen(path.to_c_str().unwrap(), "rb".to_c_str().unwrap())
        };
        if file.is_null() {
            fail!("ERROR opening '{}': {}", path.as_str(), last_os_error());
        }
        Some(GzipReader{file: file, path: path.clone(), closed: false})
    }

    fn get_error(&mut self) -> Option<IoError> {
        unsafe {
            let mut errnum: c_int = 0;
            let msg = CString::new(gzerror(self.file, &mut errnum), true);
            if errnum < 0 {
                let detail = self.path.as_str().unwrap().into_string()
                             + msg.as_str().unwrap();
                Some(IoError{kind: OtherIoError,
                             desc: "GZip reader error",
                             detail: Some(detail)
                            })
            } else {
                None
            }
        }
    }

    fn close(&mut self) -> Option<IoError> {
        if !self.closed {
            self.closed = true;
            unsafe {
                match gzclose(self.file) {
                    0 => None,
                    _ => self.get_error()
                }
            }
        } else {
            None
        }
    }
}

impl Drop for GzipReader {
    fn drop(&mut self) {
        self.close();
    }
}

impl Reader for GzipReader {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
        unsafe {
            match gzread(self.file, buf.as_mut_ptr(), buf.len() as c_uint) {
                size if size > 0 => Ok(size as uint),
                0 => {
                    match self.close() {
                        None => Err(standard_error(EndOfFile)),
                        Some(err) => Err(err)
                    }
                }
                _ => {
                    let err = self.get_error().unwrap();
                    self.close();
                    Err(err)
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::marker::PhantomData;
use std::sync::{Arc, Mutex};

use common::err::*;
use common::TypeClass;

use url::{Url, UrlParser, SchemeType, whatwg_scheme_type_mapper};

/// for HDFS URL scheme (i.e., hdfs://)
fn hdfs_scheme_handler(scheme: &str) -> SchemeType {
  match scheme {
    "file" => SchemeType::FileLike,
    "hdfs" => SchemeType::Relative(50070),
    _ => whatwg_scheme_type_mapper(scheme)
  }
}

/// storage handler mapper for StorageManager
pub fn default_space_handlers<'b> (url: &Url) -> Box<TableSpace<'b>> {

	match url.scheme.as_str() {
		"file" => Box::new( LocalFS::new(&url.serialize()) ) ,
		_ => panic!("No supported: {}", url.serialize())
	}
}

pub trait TableSpace<'a> {
	fn uri(&'a self) -> &'a str;

	fn format(&self) -> Void;

	fn create_table(&self) -> Void ;

	fn drop_table(&self, purge: bool) -> Void;

	fn rename_table(&self) -> Void;

	fn truncate_table(&self) -> Void;

	fn available_capacity(&self) -> u64;

	fn total_capacity(&self) -> u64;
}

pub struct LocalFS {
	url: String
}

impl LocalFS {
	pub fn new(url: &str) -> LocalFS {
		LocalFS {url: url.to_owned()}
	}
}

impl<'a> TableSpace<'a> for LocalFS {
	fn uri(&'a self) -> &'a str {
		self.url.as_str()
	}

	fn format(&self) -> Void {
		void_ok()
	}

	fn create_table(&self) -> Void {
		void_ok()
	}

	fn drop_table(&self, purge: bool) -> Void {
		void_ok()
	}

	fn rename_table(&self) -> Void {
		void_ok()
	}

	fn truncate_table(&self) -> Void {
		void_ok()
	}

	fn available_capacity(&self) -> u64 {
		0
	}

	fn total_capacity(&self) -> u64 {
		0
	}
}

pub trait DataFormat {
	fn name(&self) -> String;

	fn splittable() -> bool;

	fn compression_support() -> bool;

	fn random_accessable() -> bool;

	fn indexable() -> bool;

	fn supported_types() -> Vec<TypeClass>;
}

pub trait Storage {
	fn uri (&self) -> String;

	fn available_capacity(&self) -> u64;

	fn total_capacity(&self) -> u64;
}

pub trait BlockStorage: Storage {
	fn split(&self, url: String, size : u64);

	fn list_files(url : String);
}

pub struct StorageManager<'a> {
	space_handler: fn(uri: &'a Url) -> Box<TableSpace>,
	space_map: Mutex<HashMap<String, Box<TableSpace<'a>>>>,
	url_parser: UrlParser<'a>,
	marker: PhantomData<&'a ()>
}

impl<'a> StorageManager<'a> {

	pub fn new() -> StorageManager<'a> {
		StorageManager::new_with_handler(default_space_handlers)
	}

	pub fn new_with_handler(space_handler: fn(uri: &Url) -> Box<TableSpace>) -> StorageManager<'a> {

		let mut url_parser = UrlParser::new();
    url_parser.scheme_type_mapper(hdfs_scheme_handler);

		StorageManager {
			space_handler: space_handler,
			space_map: Mutex::new(HashMap::new()),
			url_parser: url_parser,
			marker: PhantomData
		}
	}

	pub fn get_space_url(&'a self, url: &str) -> Option<String> {
		let map = self.space_map.lock().unwrap();
		let mut it = map.keys().filter(|&u| u == url);

		match it.next() {
			Some(u) => Some(u.to_owned()),
			None => None
		}
	}

	pub fn get_space(&'a self, url: &str) -> TResult<&'a TableSpace> {

		let mut guard = match self.space_map.lock() {
			Ok(guard) => guard,
			Err(e) => { error!("{}", e); panic!("poisoned") }
		};

		Err(Error::Unknown)
	}
}<commit_msg>Implement StorageManager::get_space().<commit_after>use std::cell::UnsafeCell;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::marker::PhantomData;
use std::sync::{Arc, Mutex};

use common::err::*;
use common::TypeClass;

use url::{Url, UrlParser, SchemeType, whatwg_scheme_type_mapper};

/// for HDFS URL scheme (i.e., hdfs://)
fn hdfs_scheme_handler(scheme: &str) -> SchemeType {
  match scheme {
    "file" => SchemeType::FileLike,
    "hdfs" => SchemeType::Relative(50070),
    _ => whatwg_scheme_type_mapper(scheme)
  }
}

/// storage handler mapper for StorageManager
pub fn default_space_handlers<'a>(url: Url) -> Box<TableSpace<'a>> {
	match url.scheme.as_str() {
		"file" => Box::new( LocalFS::new(&url.serialize()) ) ,
		_ => panic!("No supported: {}", url.serialize())
	}
}

pub trait TableSpace<'a> {
	fn url(&'a self) -> &'a str;

	fn format(&self) -> Void;

	fn create_table(&self) -> Void ;

	fn drop_table(&self, purge: bool) -> Void;

	fn rename_table(&self) -> Void;

	fn truncate_table(&self) -> Void;

	fn available_capacity(&self) -> u64;

	fn total_capacity(&self) -> u64;

	// fn new_scanner(&self, url: &str) -> TResult<Box<Executor>>;

	// fn new_appender(&self, url: &str) -> TResult<Box<Executor>>;
}

pub struct LocalFS {
	pub url: String
}

impl LocalFS {
	pub fn new(url: &str) -> LocalFS {
		LocalFS {url: url.to_owned()}
	}
}

impl<'a> TableSpace<'a> for LocalFS {
	fn url(&'a self) -> &'a str {
		self.url.as_str()
	}

	fn format(&self) -> Void {
		void_ok()
	}

	fn create_table(&self) -> Void {
		void_ok()
	}

	fn drop_table(&self, purge: bool) -> Void {
		void_ok()
	}

	fn rename_table(&self) -> Void {
		void_ok()
	}

	fn truncate_table(&self) -> Void {
		void_ok()
	}

	fn available_capacity(&self) -> u64 {
		0
	}

	fn total_capacity(&self) -> u64 {
		0
	}
}

pub trait DataFormat {
	fn name(&self) -> String;

	fn splittable() -> bool;

	fn compression_support() -> bool;

	fn random_accessable() -> bool;

	fn indexable() -> bool;

	fn supported_types() -> Vec<TypeClass>;
}

pub trait Storage {
	fn uri (&self) -> String;

	fn available_capacity(&self) -> u64;

	fn total_capacity(&self) -> u64;
}

pub trait BlockStorage: Storage {
	fn split(&self, url: String, size : u64);

	fn list_files(url : String);
}

/// Manages TableSpaces
pub struct StorageManager<'a> {
	space_handler: fn(uri: Url) -> Box<TableSpace<'a>>,
	space_map: UnsafeCell<HashMap<String, Box<TableSpace<'a>>>>,
	lock: Mutex<()>,
	url_parser: UrlParser<'a>,
	marker: PhantomData<&'a ()>
}

impl<'a> StorageManager<'a> {

	pub fn new() -> StorageManager<'a> {
		StorageManager::new_with_handler(default_space_handlers)
	}

	pub fn new_with_handler(space_handler: fn(uri: Url) -> Box<TableSpace<'a>>) -> StorageManager<'a> {

		let mut url_parser = UrlParser::new();
    url_parser.scheme_type_mapper(hdfs_scheme_handler);

		StorageManager {
			space_handler: space_handler,
			space_map: UnsafeCell::new(HashMap::new()),
			lock: Mutex::new(()),
			url_parser: url_parser,
			marker: PhantomData
		}
	}

	#[allow(unused_must_use)]
	pub fn get_space(&'a self, url: &str) -> TResult<&'a TableSpace> {
		self.lock.lock();

		unsafe {
			let key = url.to_owned();
			match (*self.space_map.get()).entry(key) {
				Entry::Vacant(entry) => {
					let res = self.url_parser.parse(url);
					if res.is_err() {
						panic!("Invalid space url: {}", url)
					}

					let parsed_url = res.unwrap();
					let handler:Box<TableSpace<'a>> = (self.space_handler)(parsed_url);
					Ok(&(**entry.insert(handler)))
				},
				Entry::Occupied(entry) => {
					let x: &Box<TableSpace<'a>> = entry.get();
					Err(Error::Unknown)
				}
			}
		}
	}
}<|endoftext|>