<commit_before>// Copyright 2016 The Gfx-rs Developers.
//
// 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.

use std::{mem, ptr};
use d3dcompiler;
use dxguid;
use winapi;
use gfx_core as core;
use gfx_core::shade as s;

pub fn reflect_shader(code: &[u8]) -> *mut winapi::ID3D11ShaderReflection {
    let mut reflection = ptr::null_mut();
    let hr = unsafe {
        d3dcompiler::D3DReflect(code.as_ptr() as *const winapi::VOID,
            code.len() as winapi::SIZE_T, &dxguid::IID_ID3D11ShaderReflection, &mut reflection)
    };
    if winapi::SUCCEEDED(hr) {
        reflection as *mut winapi::ID3D11ShaderReflection
    }else {
        panic!("Shader reflection failed with code {:x}", hr);
    }
}

fn convert_str(pchar: *const i8) -> String {
    use std::ffi::CStr;
    unsafe {
        CStr::from_ptr(pchar).to_string_lossy().into_owned()
    }
}

fn map_base_type_from_component(ct: winapi::D3D_REGISTER_COMPONENT_TYPE) -> s::BaseType {
    match ct {
        winapi::D3D_REGISTER_COMPONENT_UINT32 => s::BaseType::U32,
        winapi::D3D_REGISTER_COMPONENT_SINT32 => s::BaseType::I32,
        winapi::D3D_REGISTER_COMPONENT_FLOAT32 => s::BaseType::F32,
        winapi::D3D_REGISTER_COMPONENT_TYPE(t) => {
            error!("Unknown register component type {} detected!", t);
            s::BaseType::F32
        }
    }
}

fn map_base_type_from_return(rt: winapi::D3D_RESOURCE_RETURN_TYPE) -> s::BaseType {
    match rt {
        winapi::D3D_RETURN_TYPE_UINT => s::BaseType::U32,
        winapi::D3D_RETURN_TYPE_SINT => s::BaseType::I32,
        winapi::D3D_RETURN_TYPE_FLOAT => s::BaseType::F32,
        winapi::D3D_RESOURCE_RETURN_TYPE(t) => {
            error!("Unknown return type {} detected!", t);
            s::BaseType::F32
        }
    }
}

fn map_texture_type(tt: winapi::D3D_SRV_DIMENSION) -> s::TextureType {
    use winapi::*;
    use gfx_core::shade::IsArray::*;
    use gfx_core::shade::IsMultiSample::*;
    match tt {
        D3D_SRV_DIMENSION_BUFFER            => s::TextureType::Buffer,
        D3D_SRV_DIMENSION_TEXTURE1D         => s::TextureType::D1(NoArray),
        D3D_SRV_DIMENSION_TEXTURE1DARRAY    => s::TextureType::D1(Array),
        D3D_SRV_DIMENSION_TEXTURE2D         => s::TextureType::D2(NoArray, NoMultiSample),
        D3D_SRV_DIMENSION_TEXTURE2DARRAY    => s::TextureType::D2(Array, NoMultiSample),
        D3D_SRV_DIMENSION_TEXTURE2DMS       => s::TextureType::D2(NoArray, MultiSample),
        D3D_SRV_DIMENSION_TEXTURE2DMSARRAY  => s::TextureType::D2(Array, MultiSample),
        D3D_SRV_DIMENSION_TEXTURE3D         => s::TextureType::D3,
        D3D_SRV_DIMENSION_TEXTURECUBE       => s::TextureType::Cube(NoArray),
        D3D_SRV_DIMENSION_TEXTURECUBEARRAY  => s::TextureType::Cube(Array),
        D3D_SRV_DIMENSION(t) => {
            error!("Unknow texture dimension {}", t);
            s::TextureType::Buffer
        }
    }
}

pub fn populate_info(info: &mut s::ProgramInfo, stage: s::Stage,
                     reflection: *mut winapi::ID3D11ShaderReflection) {
    use winapi::{UINT, SUCCEEDED};
    let usage = stage.into();
    let (shader_desc, _feature_level) = unsafe {
        let mut desc = mem::zeroed();
        let mut level = winapi::D3D_FEATURE_LEVEL_9_1;
        (*reflection).GetDesc(&mut desc);
        (*reflection).GetMinFeatureLevel(&mut level);
        (desc, level)
    };
    fn mask_to_vector(mask: u8) -> s::ContainerType {
        s::ContainerType::Vector(match mask {
            0...1 => 1,
            2...3 => 2,
            4...7 => 3,
            _ => 4,
        })
    }
    if stage == s::Stage::Vertex {
        // record vertex attributes
        for i in 0 .. shader_desc.InputParameters {
            let (hr, desc) = unsafe {
                let mut desc = mem::zeroed();
                let hr = (*reflection).GetInputParameterDesc(i as UINT, &mut desc);
                (hr, desc)
            };
            assert!(SUCCEEDED(hr));
            info!("\tAttribute {}, system type {:?}, mask {}, read-write mask {}",
                convert_str(desc.SemanticName), desc.SystemValueType, desc.Mask, desc.ReadWriteMask);
            if desc.SystemValueType != winapi::D3D_NAME_UNDEFINED {
                // system value semantic detected, skipping
                continue
            }
            if desc.Mask == 0 {
                // not used, skipping
                continue
            }
            let name = convert_str(desc.SemanticName);
            if desc.SemanticIndex != 0 {
                error!("Semantic {} has non-zero index {} - not supported by the backend", name, desc.SemanticIndex);
            }
            info.vertex_attributes.push(s::AttributeVar {
                name: name,
                slot: desc.Register as core::AttributeSlot,
                base_type: map_base_type_from_component(desc.ComponentType),
                container: mask_to_vector(desc.Mask),
            });
        }
    }
    if stage == s::Stage::Pixel {
        // record pixel outputs
        for i in 0 .. shader_desc.OutputParameters {
            let (hr, desc) = unsafe {
                let mut desc = mem::zeroed();
                let hr = (*reflection).GetOutputParameterDesc(i as UINT, &mut desc);
                (hr, desc)
            };
            assert!(SUCCEEDED(hr));
            let name = convert_str(desc.SemanticName);
            info!("\tOutput {}, system type {:?}, mask {}, read-write mask {}",
                name, desc.SystemValueType, desc.Mask, desc.ReadWriteMask);
            match desc.SystemValueType {
                winapi::D3D_NAME_TARGET =>
                    info.outputs.push(s::OutputVar {
                        name: format!("Target{}", desc.SemanticIndex), //care!
                        slot: desc.Register as core::ColorSlot,
                        base_type: map_base_type_from_component(desc.ComponentType),
                        container: mask_to_vector(desc.Mask),
                    }),
                winapi::D3D_NAME_DEPTH => info.output_depth = true,
                winapi::D3D_NAME_UNDEFINED =>
                    warn!("Custom PS output semantic is ignored: {}", name),
                _ =>
                    warn!("Unhandled PS output semantic {} of type {:?}", name, desc.SystemValueType),
            }
        }
    }
    // record resources
    for i in 0 .. shader_desc.BoundResources {
        let (hr, res_desc) = unsafe {
            let mut desc = mem::zeroed();
            let hr = (*reflection).GetResourceBindingDesc(i as UINT, &mut desc);
            (hr, desc)
        };
        assert!(SUCCEEDED(hr));
        let name = convert_str(res_desc.Name);
        info!("\tResource {}, type {:?}", name, res_desc.Type);
        if res_desc.Type == winapi::D3D_SIT_CBUFFER {
            if let Some(cb) = info.constant_buffers.iter_mut().find(|cb| cb.name == name) {
                cb.usage = cb.usage | usage;
                continue;
            }
            let cbuf = unsafe {
                (*reflection).GetConstantBufferByName(res_desc.Name)
            };
            let desc = unsafe {
                let mut desc = mem::zeroed();
                let hr = (*cbuf).GetDesc(&mut desc);
                assert!(SUCCEEDED(hr));
                desc
            };
            let mut elements = Vec::new();
            for i in 0 .. desc.Variables {
                let var = unsafe {
                    (*cbuf).GetVariableByIndex(i)
                };
                let var_desc = unsafe {
                    let mut vd = mem::zeroed();
                    let hr1 = (*var).GetDesc(&mut vd);
                    assert!(SUCCEEDED(hr1));
                    vd
                };
                let vtype = unsafe {
                    (*var).GetType()
                };
                let vtype_desc = unsafe {
                    let mut vtd = mem::zeroed();
                    let hr2 = (*vtype).GetDesc(&mut vtd);
                    assert!(SUCCEEDED(hr2));
                    vtd
                };
                let el_name = convert_str(var_desc.Name);
                debug!("\t\tElement at {}\t= '{}'", var_desc.StartOffset, el_name);
                if vtype_desc.Class == winapi::D3D_SVC_STRUCT {
                    for j in 0 .. vtype_desc.Members {
                        let member = unsafe {
                            (*vtype).GetMemberTypeByIndex(j)
                        };
                        let mem_name_ptr = unsafe {
                            (*vtype).GetMemberTypeName(j)
                        };
                        let mem_desc = unsafe {
                            let mut mtd = mem::zeroed();
                            let hr3 = (*member).GetDesc(&mut mtd);
                            assert!(SUCCEEDED(hr3));
                            mtd
                        };
                        let mem_name = convert_str(mem_name_ptr); //mem_desc.Name
                        debug!("\t\t\tMember at {}\t= '{}'", mem_desc.Offset, mem_name);
                        let base_offset = var_desc.StartOffset + j * var_desc.Size;
                        elements.push(s::ConstVar {
                            name: format!("{}[{}].{}", el_name, j, mem_name),
                            location: (base_offset + mem_desc.Offset) as s::Location,
                            count: mem_desc.Elements as usize,
                            base_type: s::BaseType::F32, //TODO
                            container: s::ContainerType::Single, //TODO
                        })
                    }
                } else {
                    elements.push(s::ConstVar {
                        name: el_name,
                        location: var_desc.StartOffset as s::Location,
                        count: vtype_desc.Elements as usize,
                        base_type: s::BaseType::F32, //TODO
                        container: s::ContainerType::Single, //TODO
                    })
                }
            }
            info.constant_buffers.push(s::ConstantBufferVar {
                name: name,
                slot: res_desc.BindPoint as core::ConstantBufferSlot,
                size: desc.Size as usize,
                usage: usage,
                elements: elements,
            });
        }else if res_desc.Type == winapi::D3D_SIT_TEXTURE {
            if let Some(t) = info.textures.iter_mut().find(|t| t.name == name) {
                t.usage = t.usage | usage;
                continue;
            }
            info.textures.push(s::TextureVar {
                name: name,
                slot: res_desc.BindPoint as core::ResourceViewSlot,
                base_type: map_base_type_from_return(res_desc.ReturnType),
                ty: map_texture_type(res_desc.Dimension),
                usage: usage,
            });
        }else if res_desc.Type == winapi::D3D_SIT_SAMPLER {
            let name = name.trim_right_matches('_');
            if let Some(s) = info.samplers.iter_mut().find(|s| s.name == name) {
                s.usage = s.usage | usage;
                continue;
            }
            let cmp = if res_desc.uFlags & winapi::D3D_SIF_COMPARISON_SAMPLER.0 != 0 {
                s::IsComparison::Compare
            }else {
                s::IsComparison::NoCompare
            };
            info.samplers.push(s::SamplerVar {
                name: name.to_owned(),
                slot: res_desc.BindPoint as core::SamplerSlot,
                ty: s::SamplerType(cmp, s::IsRect::NoRect),
                usage: usage,
            });
        }else {
            error!("Unsupported resource type {:?} for {}", res_desc.Type, name);
        }
    }
}
<commit_msg>[DX11] Container type reflection for cbuf members<commit_after>// Copyright 2016 The Gfx-rs Developers.
//
// 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.

use std::{mem, ptr};
use d3dcompiler;
use dxguid;
use winapi;
use gfx_core as core;
use gfx_core::shade as s;

pub fn reflect_shader(code: &[u8]) -> *mut winapi::ID3D11ShaderReflection {
    let mut reflection = ptr::null_mut();
    let hr = unsafe {
        d3dcompiler::D3DReflect(code.as_ptr() as *const winapi::VOID,
            code.len() as winapi::SIZE_T, &dxguid::IID_ID3D11ShaderReflection, &mut reflection)
    };
    if winapi::SUCCEEDED(hr) {
        reflection as *mut winapi::ID3D11ShaderReflection
    }else {
        panic!("Shader reflection failed with code {:x}", hr);
    }
}

fn convert_str(pchar: *const i8) -> String {
    use std::ffi::CStr;
    unsafe {
        CStr::from_ptr(pchar).to_string_lossy().into_owned()
    }
}

fn map_base_type_from_component(ct: winapi::D3D_REGISTER_COMPONENT_TYPE) -> s::BaseType {
    match ct {
        winapi::D3D_REGISTER_COMPONENT_UINT32 => s::BaseType::U32,
        winapi::D3D_REGISTER_COMPONENT_SINT32 => s::BaseType::I32,
        winapi::D3D_REGISTER_COMPONENT_FLOAT32 => s::BaseType::F32,
        winapi::D3D_REGISTER_COMPONENT_TYPE(t) => {
            error!("Unknown register component type {} detected!", t);
            s::BaseType::F32
        }
    }
}

fn map_base_type_from_return(rt: winapi::D3D_RESOURCE_RETURN_TYPE) -> s::BaseType {
    match rt {
        winapi::D3D_RETURN_TYPE_UINT => s::BaseType::U32,
        winapi::D3D_RETURN_TYPE_SINT => s::BaseType::I32,
        winapi::D3D_RETURN_TYPE_FLOAT => s::BaseType::F32,
        winapi::D3D_RESOURCE_RETURN_TYPE(t) => {
            error!("Unknown return type {} detected!", t);
            s::BaseType::F32
        }
    }
}

fn map_texture_type(tt: winapi::D3D_SRV_DIMENSION) -> s::TextureType {
    use winapi::*;
    use gfx_core::shade::IsArray::*;
    use gfx_core::shade::IsMultiSample::*;
    match tt {
        D3D_SRV_DIMENSION_BUFFER            => s::TextureType::Buffer,
        D3D_SRV_DIMENSION_TEXTURE1D         => s::TextureType::D1(NoArray),
        D3D_SRV_DIMENSION_TEXTURE1DARRAY    => s::TextureType::D1(Array),
        D3D_SRV_DIMENSION_TEXTURE2D         => s::TextureType::D2(NoArray, NoMultiSample),
        D3D_SRV_DIMENSION_TEXTURE2DARRAY    => s::TextureType::D2(Array, NoMultiSample),
        D3D_SRV_DIMENSION_TEXTURE2DMS       => s::TextureType::D2(NoArray, MultiSample),
        D3D_SRV_DIMENSION_TEXTURE2DMSARRAY  => s::TextureType::D2(Array, MultiSample),
        D3D_SRV_DIMENSION_TEXTURE3D         => s::TextureType::D3,
        D3D_SRV_DIMENSION_TEXTURECUBE       => s::TextureType::Cube(NoArray),
        D3D_SRV_DIMENSION_TEXTURECUBEARRAY  => s::TextureType::Cube(Array),
        D3D_SRV_DIMENSION(t) => {
            error!("Unknow texture dimension {}", t);
            s::TextureType::Buffer
        }
    }
}

fn map_container(class: winapi::D3D_SHADER_VARIABLE_CLASS, rows: u32, cols: u32) -> s::ContainerType {
    use gfx_core::shade::Dimension as Dim;
    //TODO: use `match` when winapi allows
    if class == winapi::D3D_SVC_SCALAR {
        s::ContainerType::Single
    } else if class == winapi::D3D_SVC_VECTOR {
        s::ContainerType::Vector(cols as Dim)
    } else if class == winapi::D3D_SVC_MATRIX_ROWS {
        s::ContainerType::Matrix(s::MatrixFormat::RowMajor, rows as Dim, cols as Dim)
    } else if class == winapi::D3D_SVC_MATRIX_COLUMNS {
        s::ContainerType::Matrix(s::MatrixFormat::ColumnMajor, rows as Dim, cols as Dim)
    } else  {
        error!("Unexpected class to classify as container: {:?}", class);
        s::ContainerType::Single
    }
}

fn map_base_type(_svt: winapi::D3D_SHADER_VARIABLE_TYPE) -> s::BaseType {
    s::BaseType::F32 //TODO
}

pub fn populate_info(info: &mut s::ProgramInfo, stage: s::Stage,
                     reflection: *mut winapi::ID3D11ShaderReflection) {
    use winapi::{UINT, SUCCEEDED};
    let usage = stage.into();
    let (shader_desc, _feature_level) = unsafe {
        let mut desc = mem::zeroed();
        let mut level = winapi::D3D_FEATURE_LEVEL_9_1;
        (*reflection).GetDesc(&mut desc);
        (*reflection).GetMinFeatureLevel(&mut level);
        (desc, level)
    };
    fn mask_to_vector(mask: u8) -> s::ContainerType {
        s::ContainerType::Vector(match mask {
            0...1 => 1,
            2...3 => 2,
            4...7 => 3,
            _ => 4,
        })
    }
    if stage == s::Stage::Vertex {
        // record vertex attributes
        for i in 0 .. shader_desc.InputParameters {
            let (hr, desc) = unsafe {
                let mut desc = mem::zeroed();
                let hr = (*reflection).GetInputParameterDesc(i as UINT, &mut desc);
                (hr, desc)
            };
            assert!(SUCCEEDED(hr));
            info!("\tAttribute {}, system type {:?}, mask {}, read-write mask {}",
                convert_str(desc.SemanticName), desc.SystemValueType, desc.Mask, desc.ReadWriteMask);
            if desc.SystemValueType != winapi::D3D_NAME_UNDEFINED {
                // system value semantic detected, skipping
                continue
            }
            if desc.Mask == 0 {
                // not used, skipping
                continue
            }
            let name = convert_str(desc.SemanticName);
            if desc.SemanticIndex != 0 {
                error!("Semantic {} has non-zero index {} - not supported by the backend", name, desc.SemanticIndex);
            }
            info.vertex_attributes.push(s::AttributeVar {
                name: name,
                slot: desc.Register as core::AttributeSlot,
                base_type: map_base_type_from_component(desc.ComponentType),
                container: mask_to_vector(desc.Mask),
            });
        }
    }
    if stage == s::Stage::Pixel {
        // record pixel outputs
        for i in 0 .. shader_desc.OutputParameters {
            let (hr, desc) = unsafe {
                let mut desc = mem::zeroed();
                let hr = (*reflection).GetOutputParameterDesc(i as UINT, &mut desc);
                (hr, desc)
            };
            assert!(SUCCEEDED(hr));
            let name = convert_str(desc.SemanticName);
            info!("\tOutput {}, system type {:?}, mask {}, read-write mask {}",
                name, desc.SystemValueType, desc.Mask, desc.ReadWriteMask);
            match desc.SystemValueType {
                winapi::D3D_NAME_TARGET =>
                    info.outputs.push(s::OutputVar {
                        name: format!("Target{}", desc.SemanticIndex), //care!
                        slot: desc.Register as core::ColorSlot,
                        base_type: map_base_type_from_component(desc.ComponentType),
                        container: mask_to_vector(desc.Mask),
                    }),
                winapi::D3D_NAME_DEPTH => info.output_depth = true,
                winapi::D3D_NAME_UNDEFINED =>
                    warn!("Custom PS output semantic is ignored: {}", name),
                _ =>
                    warn!("Unhandled PS output semantic {} of type {:?}", name, desc.SystemValueType),
            }
        }
    }
    // record resources
    for i in 0 .. shader_desc.BoundResources {
        let (hr, res_desc) = unsafe {
            let mut desc = mem::zeroed();
            let hr = (*reflection).GetResourceBindingDesc(i as UINT, &mut desc);
            (hr, desc)
        };
        assert!(SUCCEEDED(hr));
        let name = convert_str(res_desc.Name);
        info!("\tResource {}, type {:?}", name, res_desc.Type);
        if res_desc.Type == winapi::D3D_SIT_CBUFFER {
            if let Some(cb) = info.constant_buffers.iter_mut().find(|cb| cb.name == name) {
                cb.usage = cb.usage | usage;
                continue;
            }
            let cbuf = unsafe {
                (*reflection).GetConstantBufferByName(res_desc.Name)
            };
            let desc = unsafe {
                let mut desc = mem::zeroed();
                let hr = (*cbuf).GetDesc(&mut desc);
                assert!(SUCCEEDED(hr));
                desc
            };
            let mut elements = Vec::new();
            for i in 0 .. desc.Variables {
                let var = unsafe {
                    (*cbuf).GetVariableByIndex(i)
                };
                let var_desc = unsafe {
                    let mut vd = mem::zeroed();
                    let hr1 = (*var).GetDesc(&mut vd);
                    assert!(SUCCEEDED(hr1));
                    vd
                };
                let vtype = unsafe {
                    (*var).GetType()
                };
                let vtype_desc = unsafe {
                    let mut vtd = mem::zeroed();
                    let hr2 = (*vtype).GetDesc(&mut vtd);
                    assert!(SUCCEEDED(hr2));
                    vtd
                };
                let el_name = convert_str(var_desc.Name);
                debug!("\t\tElement at {}\t= '{}'", var_desc.StartOffset, el_name);
                if vtype_desc.Class == winapi::D3D_SVC_STRUCT {
                    for j in 0 .. vtype_desc.Members {
                        let member = unsafe {
                            (*vtype).GetMemberTypeByIndex(j)
                        };
                        let mem_name_ptr = unsafe {
                            (*vtype).GetMemberTypeName(j)
                        };
                        let mem_desc = unsafe {
                            let mut mtd = mem::zeroed();
                            let hr3 = (*member).GetDesc(&mut mtd);
                            assert!(SUCCEEDED(hr3));
                            mtd
                        };
                        let mem_name = convert_str(mem_name_ptr); //mem_desc.Name
                        debug!("\t\t\tMember at {}\t= '{}'", mem_desc.Offset, mem_name);
                        let base_offset = var_desc.StartOffset + j * var_desc.Size;
                        elements.push(s::ConstVar {
                            name: format!("{}[{}].{}", el_name, j, mem_name),
                            location: (base_offset + mem_desc.Offset) as s::Location,
                            count: mem_desc.Elements as usize,
                            base_type: map_base_type(mem_desc.Type),
                            container: map_container(mem_desc.Class, mem_desc.Rows, mem_desc.Columns),
                        })
                    }
                } else {
                    elements.push(s::ConstVar {
                        name: el_name,
                        location: var_desc.StartOffset as s::Location,
                        count: vtype_desc.Elements as usize,
                        base_type: map_base_type(vtype_desc.Type),
                        container: map_container(vtype_desc.Class, vtype_desc.Rows, vtype_desc.Columns),
                    })
                }
            }
            info.constant_buffers.push(s::ConstantBufferVar {
                name: name,
                slot: res_desc.BindPoint as core::ConstantBufferSlot,
                size: desc.Size as usize,
                usage: usage,
                elements: elements,
            });
        }else if res_desc.Type == winapi::D3D_SIT_TEXTURE {
            if let Some(t) = info.textures.iter_mut().find(|t| t.name == name) {
                t.usage = t.usage | usage;
                continue;
            }
            info.textures.push(s::TextureVar {
                name: name,
                slot: res_desc.BindPoint as core::ResourceViewSlot,
                base_type: map_base_type_from_return(res_desc.ReturnType),
                ty: map_texture_type(res_desc.Dimension),
                usage: usage,
            });
        }else if res_desc.Type == winapi::D3D_SIT_SAMPLER {
            let name = name.trim_right_matches('_');
            if let Some(s) = info.samplers.iter_mut().find(|s| s.name == name) {
                s.usage = s.usage | usage;
                continue;
            }
            let cmp = if res_desc.uFlags & winapi::D3D_SIF_COMPARISON_SAMPLER.0 != 0 {
                s::IsComparison::Compare
            }else {
                s::IsComparison::NoCompare
            };
            info.samplers.push(s::SamplerVar {
                name: name.to_owned(),
                slot: res_desc.BindPoint as core::SamplerSlot,
                ty: s::SamplerType(cmp, s::IsRect::NoRect),
                usage: usage,
            });
        }else {
            error!("Unsupported resource type {:?} for {}", res_desc.Type, name);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(non_snake_case)]

// Error messages for EXXXX errors.
// Each message should start and end with a new line, and be wrapped to 80 characters.
// In vim you can `:set tw=80` and use `gq` to wrap paragraphs. Use `:set tw=0` to disable.
register_long_diagnostics! {

E0154: r##"
Imports (`use` statements) are not allowed after non-item statements, such as
variable declarations and expression statements.

Wrong example:
```
fn f() {
    // Variable declaration before import
    let x = 0;
    use std::io::Read;
    ...
}
```

The solution is to declare the imports at the top of the block, function, or
file.

Here is the previous example again, with the correct order:
```
fn f() {
    use std::io::Read;
    let x = 0;
    ...
}
```

See the Declaration Statements section of the reference for more information
about what constitutes an Item declaration and what does not:

http://doc.rust-lang.org/reference.html#statements
"##,

E0259: r##"
The name chosen for an external crate conflicts with another external crate that
has been imported into the current module.

Wrong example:
```
extern a;
extern crate_a as a;
```

The solution is to choose a different name that doesn't conflict with any
external crate imported into the current module.

Correct example:
```
extern a;
extern crate_a as other_name;
```
"##,

E0260: r##"
The name for an item declaration conflicts with an external crate's name.

For instance,
```
extern abc;

struct abc;
```

There are two possible solutions:

Solution #1: Rename the item.

```
extern abc;

struct xyz;
```

Solution #2: Import the crate with a different name.

```
extern abc as xyz;

struct abc;
```

See the Declaration Statements section of the reference for more information
about what constitutes an Item declaration and what does not:

http://doc.rust-lang.org/reference.html#statements
"##,

E0317: r##"
User-defined types or type parameters cannot shadow the primitive types.
This error indicates you tried to define a type, struct or enum with the same
name as an existing primitive type, and is therefore invalid.

See the Types section of the reference for more information about the primitive
types:

http://doc.rust-lang.org/reference.html#types
"##

}

register_diagnostics! {
    E0157,
    E0153,
    E0251, // a named type or value has already been imported in this module
    E0252, // a named type or value has already been imported in this module
    E0253, // not directly importable
    E0254, // import conflicts with imported crate in this module
    E0255, // import conflicts with value in this module
    E0256, // import conflicts with type in this module
    E0257, // inherent implementations are only allowed on types defined in the current module
    E0258, // import conflicts with existing submodule
    E0364, // item is private
    E0365  // item is private
}
<commit_msg>Improve wording in error explanation.<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(non_snake_case)]

// Error messages for EXXXX errors.
// Each message should start and end with a new line, and be wrapped to 80 characters.
// In vim you can `:set tw=80` and use `gq` to wrap paragraphs. Use `:set tw=0` to disable.
register_long_diagnostics! {

E0154: r##"
Imports (`use` statements) are not allowed after non-item statements, such as
variable declarations and expression statements.

Here is an example that demonstrates the error:
```
fn f() {
    // Variable declaration before import
    let x = 0;
    use std::io::Read;
    ...
}
```

The solution is to declare the imports at the top of the block, function, or
file.

Here is the previous example again, with the correct order:
```
fn f() {
    use std::io::Read;
    let x = 0;
    ...
}
```

See the Declaration Statements section of the reference for more information
about what constitutes an Item declaration and what does not:

http://doc.rust-lang.org/reference.html#statements
"##,

E0259: r##"
The name chosen for an external crate conflicts with another external crate that
has been imported into the current module.

Wrong example:
```
extern a;
extern crate_a as a;
```

The solution is to choose a different name that doesn't conflict with any
external crate imported into the current module.

Correct example:
```
extern a;
extern crate_a as other_name;
```
"##,

E0260: r##"
The name for an item declaration conflicts with an external crate's name.

For instance,
```
extern abc;

struct abc;
```

There are two possible solutions:

Solution #1: Rename the item.

```
extern abc;

struct xyz;
```

Solution #2: Import the crate with a different name.

```
extern abc as xyz;

struct abc;
```

See the Declaration Statements section of the reference for more information
about what constitutes an Item declaration and what does not:

http://doc.rust-lang.org/reference.html#statements
"##,

E0317: r##"
User-defined types or type parameters cannot shadow the primitive types.
This error indicates you tried to define a type, struct or enum with the same
name as an existing primitive type.

See the Types section of the reference for more information about the primitive
types:

http://doc.rust-lang.org/reference.html#types
"##

}

register_diagnostics! {
    E0157,
    E0153,
    E0251, // a named type or value has already been imported in this module
    E0252, // a named type or value has already been imported in this module
    E0253, // not directly importable
    E0254, // import conflicts with imported crate in this module
    E0255, // import conflicts with value in this module
    E0256, // import conflicts with type in this module
    E0257, // inherent implementations are only allowed on types defined in the current module
    E0258, // import conflicts with existing submodule
    E0364, // item is private
    E0365  // item is private
}
<|endoftext|>


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

use crate::conversion::api::Layout;
use crate::types::make_ident;
use proc_macro2::{Ident, Span};
use quote::quote;
use syn::parse::Parser;
use syn::punctuated::Punctuated;
use syn::{parse_quote, Field, Fields, GenericParam, ItemStruct, LitInt};

pub(crate) fn new_non_pod_struct(id: Ident) -> ItemStruct {
    let mut s = parse_quote! {
        pub struct #id {
        }
    };
    make_non_pod(&mut s, None);
    s
}

pub(crate) fn make_non_pod(s: &mut ItemStruct, layout: Option<Layout>) {
    // Keep only doc attrs, plus add a #[repr(C,packed)].
    // Thanks to dtolnay@ for this explanation of why the following
    // is needed:
    // If the real alignment of the C++ type is smaller and a reference
    // is returned from C++ to Rust, mere existence of an insufficiently
    // aligned reference in Rust causes UB even if never dereferenced
    // by Rust code
    // (see https://doc.rust-lang.org/1.47.0/reference/behavior-considered-undefined.html).
    // Rustc can use least-significant bits of the reference for other storage.
    let attrs = s
        .attrs
        .iter()
        .filter(|a| a.path.get_ident().iter().any(|p| *p == "doc"))
        .cloned();
    let repr_attr = if let Some(layout) = &layout {
        let align = make_lit_int(layout.align);
        Some(if layout.packed {
            parse_quote! {
                #[repr(C,align(#align),packed)]
            }
        } else {
            parse_quote! {
                #[repr(C,align(#align))]
            }
        })
    } else {
        None
    }
    .into_iter();
    let attrs = attrs.chain(repr_attr);
    s.attrs = attrs.collect();
    // Now fill in fields. Usually, we just want a single field
    // but if this is a generic type we need to faff a bit.
    let generic_type_fields = s
        .generics
        .params
        .iter()
        .enumerate()
        .filter_map(|(counter, gp)| match gp {
            GenericParam::Type(gpt) => {
                let id = &gpt.ident;
                let field_name = make_ident(&format!("_phantom_{}", counter));
                let toks = quote! {
                    #field_name: ::std::marker::PhantomData<::std::cell::UnsafeCell< #id >>
                };
                Some(Field::parse_named.parse2(toks).unwrap())
            }
            _ => None,
        });
    let data_field = if let Some(layout) = layout {
        let size = make_lit_int(layout.size);
        Some(
            syn::Field::parse_named
                .parse2(quote! {
                    _data: [u8; #size]
                })
                .unwrap(),
        )
    } else {
        None
    }
    .into_iter();
    let pin_field = syn::Field::parse_named
        .parse2(quote! {
            _pinned: core::marker::PhantomData<core::marker::PhantomPinned>
        })
        .unwrap();

    let non_send_sync_field = syn::Field::parse_named
        .parse2(quote! {
            _non_send_sync: core::marker::PhantomData<[*const u8;0]>
        })
        .unwrap();
    let all_fields: Punctuated<_, syn::token::Comma> = std::iter::once(pin_field)
        .chain(std::iter::once(non_send_sync_field))
        .chain(generic_type_fields)
        .chain(data_field)
        .collect();
    s.fields = Fields::Named(parse_quote! { {
        #all_fields
    } })
}

fn make_lit_int(val: usize) -> LitInt {
    let size = LitInt::new(&val.to_string(), Span::call_site());
    size
}
<commit_msg>Further commentage<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::conversion::api::Layout;
use crate::types::make_ident;
use proc_macro2::{Ident, Span};
use quote::quote;
use syn::parse::Parser;
use syn::punctuated::Punctuated;
use syn::{parse_quote, Field, Fields, GenericParam, ItemStruct, LitInt};

pub(crate) fn new_non_pod_struct(id: Ident) -> ItemStruct {
    let mut s = parse_quote! {
        pub struct #id {
        }
    };
    make_non_pod(&mut s, None);
    s
}

pub(crate) fn make_non_pod(s: &mut ItemStruct, layout: Option<Layout>) {
    // Make an opaque struct. If we have layout information, we pass
    // that through to Rust. We keep only doc attrs, plus add a #[repr(C)]
    // if necessary.
    // Constraints here (thanks to dtolnay@ for this explanation of why the
    // following is needed:)
    // (1) If the real alignment of the C++ type is smaller and a reference
    // is returned from C++ to Rust, mere existence of an insufficiently
    // aligned reference in Rust causes UB even if never dereferenced
    // by Rust code
    // (see https://doc.rust-lang.org/1.47.0/reference/behavior-considered-undefined.html).
    // Rustc can use least-significant bits of the reference for other storage.
    // (if we have layout information from bindgen we use that instead)
    // (2) We want to ensure the type is !Unpin
    // (3) We want to ensure it's not Send or Sync
    //
    // For opaque types, the Rusty opaque structure could in fact be generated
    // by three different things:
    // a) bindgen, using its --opaque-type command line argument or the library
    //    equivalent;
    // b) us (autocxx), which is what this code does
    // c) cxx, using "type B;" in an "extern "C++"" section
    // We never use (a) because bindgen requires an allowlist of opaque types.
    // Furthermore, it sometimes then discards struct definitions entirely
    // and says "type A = [u8;2];" or something else which makes our life
    // much more difficult.
    // We use (c) for abstract types. For everything else, we do it ourselves
    // for maximal control. See codegen_rs/mod.rs generate_type for more notes.
    // First work out attributes.
    let doc_attr = s
        .attrs
        .iter()
        .filter(|a| a.path.get_ident().iter().any(|p| *p == "doc"))
        .cloned();
    let repr_attr = if let Some(layout) = &layout {
        let align = make_lit_int(layout.align);
        if layout.packed {
            parse_quote! {
                #[repr(C,align(#align),packed)]
            }
        } else {
            parse_quote! {
                #[repr(C,align(#align))]
            }
        }
    } else {
        parse_quote! {
            #[repr(C, packed)]
        }
    };
    let attrs = doc_attr.chain(std::iter::once(repr_attr));
    s.attrs = attrs.collect();
    // Now fill in fields. Usually, we just want a single field
    // but if this is a generic type we need to faff a bit.
    let generic_type_fields = s
        .generics
        .params
        .iter()
        .enumerate()
        .filter_map(|(counter, gp)| match gp {
            GenericParam::Type(gpt) => {
                let id = &gpt.ident;
                let field_name = make_ident(&format!("_phantom_{}", counter));
                let toks = quote! {
                    #field_name: ::std::marker::PhantomData<::std::cell::UnsafeCell< #id >>
                };
                Some(Field::parse_named.parse2(toks).unwrap())
            }
            _ => None,
        });
    let data_field = if let Some(layout) = layout {
        let size = make_lit_int(layout.size);
        Some(
            syn::Field::parse_named
                .parse2(quote! {
                    _data: [u8; #size]
                })
                .unwrap(),
        )
    } else {
        None
    }
    .into_iter();
    let pin_field = syn::Field::parse_named
        .parse2(quote! {
            _pinned: core::marker::PhantomData<core::marker::PhantomPinned>
        })
        .unwrap();

    let non_send_sync_field = syn::Field::parse_named
        .parse2(quote! {
            _non_send_sync: core::marker::PhantomData<[*const u8;0]>
        })
        .unwrap();
    let all_fields: Punctuated<_, syn::token::Comma> = std::iter::once(pin_field)
        .chain(std::iter::once(non_send_sync_field))
        .chain(generic_type_fields)
        .chain(data_field)
        .collect();
    s.fields = Fields::Named(parse_quote! { {
        #all_fields
    } })
}

fn make_lit_int(val: usize) -> LitInt {
    let size = LitInt::new(&val.to_string(), Span::call_site());
    size
}
<|endoftext|>


<commit_before>use crate::aliased_command;
use cargo::util::errors::CargoResult;
use cargo::util::paths::resolve_executable;
use cargo::Config;
use flate2::read::GzDecoder;
use std::ffi::OsString;
use std::io::Read;
use std::io::Write;
use std::path::Path;

const COMPRESSED_MAN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/man.tgz"));

/// Checks if the `help` command is being issued.
///
/// This runs before clap processing, because it needs to intercept the `help`
/// command if a man page is available.
///
/// Returns `true` if a man page was displayed. In this case, Cargo should
/// exit.
pub fn handle_embedded_help(config: &Config) -> bool {
    match try_help(config) {
        Ok(true) => true,
        Ok(false) => false,
        Err(e) => {
            log::warn!("man failed: {:?}", e);
            false
        }
    }
}

fn try_help(config: &Config) -> CargoResult<bool> {
    let mut args = std::env::args_os()
        .skip(1)
        .skip_while(|arg| arg.to_str().map_or(false, |s| s.starts_with('-')));
    if !args
        .next()
        .map_or(false, |arg| arg.to_str() == Some("help"))
    {
        return Ok(false);
    }
    let subcommand = match args.next() {
        Some(arg) => arg,
        None => return Ok(false),
    };
    let subcommand = match subcommand.to_str() {
        Some(s) => s,
        None => return Ok(false),
    };
    // Check if this is a built-in command (or alias);
    let subcommand = match check_alias(config, subcommand) {
        Some(s) => s,
        None => return Ok(false),
    };
    if resolve_executable(Path::new("man")).is_ok() {
        let man = match extract_man(&subcommand, "1")? {
            Some(man) => man,
            None => return Ok(false),
        };
        write_and_spawn(&man, "man")?;
    } else {
        let txt = match extract_man(&subcommand, "txt")? {
            Some(txt) => txt,
            None => return Ok(false),
        };
        if resolve_executable(Path::new("less")).is_ok() {
            write_and_spawn(&txt, "less")?;
        } else if resolve_executable(Path::new("more")).is_ok() {
            write_and_spawn(&txt, "more")?;
        } else {
            drop(std::io::stdout().write_all(&txt));
        }
    }
    Ok(true)
}

/// Checks if the given subcommand is a built-in command (possibly via an alias).
///
/// Returns None if it is not a built-in command.
fn check_alias(config: &Config, subcommand: &str) -> Option<String> {
    if super::builtin_exec(subcommand).is_some() {
        return Some(subcommand.to_string());
    }
    match aliased_command(config, subcommand) {
        Ok(Some(alias)) => {
            let alias = alias.into_iter().next()?;
            if super::builtin_exec(&alias).is_some() {
                Some(alias)
            } else {
                None
            }
        }
        _ => None,
    }
}

/// Extracts the given man page from the compressed archive.
///
/// Returns None if the command wasn't found.
fn extract_man(subcommand: &str, extension: &str) -> CargoResult<Option<Vec<u8>>> {
    let extract_name = OsString::from(format!("cargo-{}.{}", subcommand, extension));
    let gz = GzDecoder::new(COMPRESSED_MAN);
    let mut ar = tar::Archive::new(gz);
    for entry in ar.entries().unwrap() {
        let mut entry = entry.unwrap();
        let path = entry.path().unwrap();
        if path.file_name().unwrap() != extract_name {
            continue;
        }
        let mut result = Vec::new();
        entry.read_to_end(&mut result).unwrap();
        return Ok(Some(result));
    }
    Ok(None)
}

/// Write the contents of a man page to disk and spawn the given command to
/// display it.
fn write_and_spawn(contents: &[u8], command: &str) -> CargoResult<()> {
    let mut tmp = tempfile::Builder::new().prefix("cargo-man").tempfile()?;
    let f = tmp.as_file_mut();
    f.write_all(&contents)?;
    f.flush()?;
    let mut cmd = std::process::Command::new(command)
        .arg(tmp.path())
        .spawn()?;
    drop(cmd.wait());
    Ok(())
}
<commit_msg>Change extract_man to not return a Result (not needed).<commit_after>use crate::aliased_command;
use cargo::util::errors::CargoResult;
use cargo::util::paths::resolve_executable;
use cargo::Config;
use flate2::read::GzDecoder;
use std::ffi::OsString;
use std::io::Read;
use std::io::Write;
use std::path::Path;

const COMPRESSED_MAN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/man.tgz"));

/// Checks if the `help` command is being issued.
///
/// This runs before clap processing, because it needs to intercept the `help`
/// command if a man page is available.
///
/// Returns `true` if a man page was displayed. In this case, Cargo should
/// exit.
pub fn handle_embedded_help(config: &Config) -> bool {
    match try_help(config) {
        Ok(true) => true,
        Ok(false) => false,
        Err(e) => {
            log::warn!("man failed: {:?}", e);
            false
        }
    }
}

fn try_help(config: &Config) -> CargoResult<bool> {
    let mut args = std::env::args_os()
        .skip(1)
        .skip_while(|arg| arg.to_str().map_or(false, |s| s.starts_with('-')));
    if !args
        .next()
        .map_or(false, |arg| arg.to_str() == Some("help"))
    {
        return Ok(false);
    }
    let subcommand = match args.next() {
        Some(arg) => arg,
        None => return Ok(false),
    };
    let subcommand = match subcommand.to_str() {
        Some(s) => s,
        None => return Ok(false),
    };
    // Check if this is a built-in command (or alias);
    let subcommand = match check_alias(config, subcommand) {
        Some(s) => s,
        None => return Ok(false),
    };
    if resolve_executable(Path::new("man")).is_ok() {
        let man = match extract_man(&subcommand, "1") {
            Some(man) => man,
            None => return Ok(false),
        };
        write_and_spawn(&man, "man")?;
    } else {
        let txt = match extract_man(&subcommand, "txt") {
            Some(txt) => txt,
            None => return Ok(false),
        };
        if resolve_executable(Path::new("less")).is_ok() {
            write_and_spawn(&txt, "less")?;
        } else if resolve_executable(Path::new("more")).is_ok() {
            write_and_spawn(&txt, "more")?;
        } else {
            drop(std::io::stdout().write_all(&txt));
        }
    }
    Ok(true)
}

/// Checks if the given subcommand is a built-in command (possibly via an alias).
///
/// Returns None if it is not a built-in command.
fn check_alias(config: &Config, subcommand: &str) -> Option<String> {
    if super::builtin_exec(subcommand).is_some() {
        return Some(subcommand.to_string());
    }
    match aliased_command(config, subcommand) {
        Ok(Some(alias)) => {
            let alias = alias.into_iter().next()?;
            if super::builtin_exec(&alias).is_some() {
                Some(alias)
            } else {
                None
            }
        }
        _ => None,
    }
}

/// Extracts the given man page from the compressed archive.
///
/// Returns None if the command wasn't found.
fn extract_man(subcommand: &str, extension: &str) -> Option<Vec<u8>> {
    let extract_name = OsString::from(format!("cargo-{}.{}", subcommand, extension));
    let gz = GzDecoder::new(COMPRESSED_MAN);
    let mut ar = tar::Archive::new(gz);
    // Unwraps should be safe here, since this is a static archive generated
    // by our build script. It should never be an invalid format!
    for entry in ar.entries().unwrap() {
        let mut entry = entry.unwrap();
        let path = entry.path().unwrap();
        if path.file_name().unwrap() != extract_name {
            continue;
        }
        let mut result = Vec::new();
        entry.read_to_end(&mut result).unwrap();
        return Some(result);
    }
    None
}

/// Write the contents of a man page to disk and spawn the given command to
/// display it.
fn write_and_spawn(contents: &[u8], command: &str) -> CargoResult<()> {
    let mut tmp = tempfile::Builder::new().prefix("cargo-man").tempfile()?;
    let f = tmp.as_file_mut();
    f.write_all(&contents)?;
    f.flush()?;
    let mut cmd = std::process::Command::new(command)
        .arg(tmp.path())
        .spawn()?;
    drop(cmd.wait());
    Ok(())
}
<|endoftext|>


<commit_before>extern crate img_hash;
extern crate rayon;

use img_hash::demo::*;
use img_hash::{HasherConfig, ImageHash};

fn hash_to_string(hash: &ImageHash) -> String {
    hash.as_bytes().iter().map(|b| format!("{:08b}", b)).collect()
}

fn main() -> Result<(), String> {
    let hash_width = 4;
    let hash_height = 4;
    let purpose = "Demos comparing hashes generated for FILE 1 and FILE 2";
    let ctxt = DemoCtxt::init("demo_compare_hashes", purpose, 2)?;
    let gif_height = ctxt.width / 2;

    let hasher = HasherConfig::new().hash_size(hash_width, hash_height).to_hasher();

    let img_1 = &ctxt.images[0];
    let img_2 = &ctxt.images[1];

    let hash_1 = hasher.hash_image(img_1);
    let hash_2 = hasher.hash_image(img_2);

    let thumb_width = fmul(ctxt.width, 0.1);
    let thumb_height = thumb_width;

    let (thumb_1_x, thumb_1_y) = (fmul(ctxt.width, 0.1), fmul(gif_height, 0.1));
    // align the second image below the first
    let (thumb_2_x, thumb_2_y) = (thumb_1_x, thumb_1_y + fmul(thumb_height, 1.5));

    let (img_1_width, img_1_height) = dimen_fill_area(img_1.dimensions(), (ctxt.width, gif_height));
    let img_1_large = imageops::resize(img_1, img_1_width, img_1_height, Lanczos3);

    let (img_1_start_x, img_1_start_y) = (
        (ctxt.width - img_1_width) / 2,
        (gif_height - img_1_height) / 2
    );

    let lerp_1_start = [img_1_start_x, img_1_start_y, img_1_width, img_1_height];
    let lerp_1_end = [thumb_1_x, thumb_1_y, thumb_width, thumb_height];

    println!("generating first resize animation");
    let resize_anim_1: Vec<_> = lerp_iter(lerp_1_start, lerp_1_end, 1000, 15).map(
        |([x, y, width, height], frame_delay)| {
            let mut frame = rgba_fill_white(ctxt.width, gif_height);
            let resized = imageops::resize(&img_1_large, width, height, Lanczos3);
            imageops::overlay(&mut frame, &resized, x, y);
            Frame::from_parts(frame, 0, 0, frame_delay.into())
        }
    ).collect();

    let mut hash_1_str = hash_to_string(&hash_1);
    hash_1_str.insert_str(0, ":");

    let (hash_1_x, hash_1_y) = (thumb_1_x + fmul(thumb_width, 1.05), thumb_1_y);

    let hash_1_layout = ctxt.font.layout(&hash_1_str, Scale::uniform(thumb_width as f32 / 1.5),
                                         point_f32(hash_1_x, hash_1_y));

    let (hash_1_width, hash_1_height) = size_of_text(&hash_1_layout);
    let hash_1_y = thumb_1_y + (thumb_width - hash_1_height) / 2;

    println!("generating first hash animation");
    let hash_1_anim: Vec<_> = lerp_iter(hash_1_x, hash_1_x + hash_1_width, 500, 25).map(
        |(alpha_x, frame_delay)| {
            let mut frame = resize_anim_1.last().unwrap().buffer().clone();

            for g in hash_1_layout.clone() {
                let Point { x, .. } = g.position();
                let g = g.into_unpositioned().positioned(Point { x, y: hash_1_y as f32 });

                draw_glyph_sampled(&mut frame, &g, |x, _|
                    if x > alpha_x { WHITE_A } else { BLACK_A }
                )
            }

            Frame::from_parts(frame, 0, 0, frame_delay.into())
        }
    ).collect();

    let (img_2_width, img_2_height) = dimen_fill_area(img_2.dimensions(), (ctxt.width, gif_height));
    let img_2_large = imageops::resize(img_2, img_2_width, img_2_height, Lanczos3);

    let (img_2_start_x, img_2_start_y) = (
        (ctxt.width - img_2_width) / 2,
        (gif_height - img_2_height) / 2
    );

    let lerp_2_start = [img_2_start_x, img_2_start_y, img_2_width, img_2_height];
    let lerp_2_end = [thumb_2_x, thumb_2_y, thumb_width, thumb_height];

    println!("generating second resize animation");
    let resize_anim_2: Vec<_> = lerp_iter(lerp_2_start, lerp_2_end, 1000, 15).map(
        |([x, y, width, height], frame_delay)| {
            let mut frame = hash_1_anim[0].buffer().clone();
            let resized = imageops::resize(&img_2_large, width, height, Lanczos3);
            imageops::overlay(&mut frame, &resized, x, y);
            Frame::from_parts(frame, 0, 0, frame_delay.into())
        }
    ).collect();

    let mut hash_2_str = hash_to_string(&hash_2);
    hash_2_str.insert_str(0, ":");

    let (hash_2_x, hash_2_y) = (hash_1_x, thumb_2_y);

    let hash_2_layout = ctxt.font.layout(&hash_2_str, Scale::uniform(thumb_width as f32 / 1.5),
                                         point_f32(hash_2_x, hash_2_y));

    let (hash_2_width, hash_2_height) = size_of_text(&hash_2_layout);
    let hash_2_y = thumb_2_y + (thumb_width - hash_2_height) / 2;

    println!("generating first hash animation");
    let hash_2_anim: Vec<_> = lerp_iter(hash_2_x, hash_2_x + hash_2_width, 500, 25).map(
        |(alpha_x, frame_delay)| {
            let mut frame = resize_anim_2.last().unwrap().buffer().clone();

            for g in hash_2_layout.clone() {
                let Point { x, .. } = g.position();
                let g = g.into_unpositioned().positioned(Point { x, y: hash_2_y as f32 });

                draw_glyph_sampled(&mut frame, &g, |x, _|
                    if x > alpha_x { WHITE_A } else { BLACK_A }
                )
            }

            Frame::from_parts(frame, 0, 0, frame_delay.into())
        }
    ).collect();

    println!("generating comparison animation");
    let char_bb = hash_1_layout.clone().last().and_then(|g| g.pixel_bounding_box())
        .expect("hash should have a character bounding box");

    let Vector { x: char_width, y: char_height } = (char_bb.max - char_bb.min);
    let (char_width, char_height) = (char_width as u32, char_height as u32);

    let outline = Outline::new(char_width as u32, thumb_2_y - thumb_1_y + char_height,
                               fmul(char_width as u32, 0.1));

    let compare_text_y = fmul(thumb_2_y, 1.5);
    let mut count = 0;
    let compare_anim: Vec<_> = hash_1_layout.skip(1).enumerate().map(|(i, g)| {
        let mut frame = hash_2_anim[0].buffer().clone();

        let Point { x, .. } = g.position();

        let left = hash_1_str.chars().nth(i as usize + 1).unwrap();
        let right = hash_2_str.chars().nth(i as usize + 1).unwrap();

        let bit = left != right;
        if bit {
            count += 1;
        }

        let color = if bit { GREEN } else { BLACK };

        outline.draw(&mut frame, x as u32, hash_1_y, color);

        let text = format!("{}{}{} {}{}", left, if bit { "≠" } else { "=" }, right, count,
                            if bit {"+"} else {""});

        let layout = center_text_in_area(ctxt.layout_text(&text, 0, compare_text_y),
            ctxt.width, gif_height - compare_text_y
        );

        for g in layout {
            draw_glyph(&mut frame, &g, &color);
        }

        Frame::from_parts(frame, 0, 0, 60.into())
    }).collect();

    println!("combining stages");
    let frames = resize_anim_1.into_iter()
        .chain(hash_1_anim)
        .chain(resize_anim_2)
        .chain(hash_2_anim)
        .chain(compare_anim)
        .collect();

    println!("saving comparison gif");
    ctxt.save_gif("compare_hashes", frames)
}
<commit_msg>clean up hash comparison demo<commit_after>extern crate img_hash;
extern crate rayon;

use img_hash::demo::*;
use img_hash::{HasherConfig, ImageHash};

fn hash_to_string(hash: &ImageHash) -> String {
    hash.as_bytes().iter().map(|b| format!("{:08b}", b)).collect()
}

fn main() -> Result<(), String> {
    let hash_width = 4;
    let hash_height = 4;
    let purpose = "Demos comparing hashes generated for FILE 1 and FILE 2";
    let ctxt = DemoCtxt::init("demo_compare_hashes", purpose, 2)?;
    let gif_height = ctxt.width / 2;

    let hasher = HasherConfig::new().hash_size(hash_width, hash_height).to_hasher();

    let img_1 = &ctxt.images[0];
    let img_2 = &ctxt.images[1];

    let hash_1 = hasher.hash_image(img_1);
    let hash_2 = hasher.hash_image(img_2);

    let thumb_width = fmul(ctxt.width, 0.1);
    let thumb_height = thumb_width;

    let (thumb_1_x, thumb_1_y) = (fmul(ctxt.width, 0.1), fmul(gif_height, 0.1));
    // align the second image below the first
    let (thumb_2_x, thumb_2_y) = (thumb_1_x, thumb_1_y + fmul(thumb_height, 1.5));

    let (img_1_width, img_1_height) = dimen_fill_area(img_1.dimensions(), (ctxt.width, gif_height));
    let img_1_large = imageops::resize(img_1, img_1_width, img_1_height, Lanczos3);

    let (img_1_start_x, img_1_start_y) = (
        (ctxt.width - img_1_width) / 2,
        (gif_height - img_1_height) / 2
    );

    let lerp_1_start = [img_1_start_x, img_1_start_y, img_1_width, img_1_height];
    let lerp_1_end = [thumb_1_x, thumb_1_y, thumb_width, thumb_height];

    println!("generating first resize animation");
    let resize_anim_1: Vec<_> = lerp_iter(lerp_1_start, lerp_1_end, 1000, 15).map(
        |([x, y, width, height], frame_delay)| {
            let mut frame = rgba_fill_white(ctxt.width, gif_height);
            let resized = imageops::resize(&img_1_large, width, height, Lanczos3);
            imageops::overlay(&mut frame, &resized, x, y);
            Frame::from_parts(frame, 0, 0, frame_delay.into())
        }
    ).collect();

    let mut hash_1_str = hash_to_string(&hash_1);
    hash_1_str.insert_str(0, ":");

    let (hash_1_x, hash_1_y) = (thumb_1_x + fmul(thumb_width, 1.05), thumb_1_y);
    let hash_text_size = Scale::uniform(thumb_width as f32 / 1.5);

    let hash_1_layout = ctxt.font.layout(&hash_1_str, hash_text_size,
                                         point_f32(hash_1_x, hash_1_y));

    let (hash_1_width, hash_1_height) = size_of_text(&hash_1_layout);
    let hash_1_y = thumb_1_y + (thumb_width - hash_1_height) / 2;

    println!("generating first hash animation");
    let hash_1_anim: Vec<_> = lerp_iter(hash_1_x, hash_1_x + hash_1_width, 500, 25).map(
        |(alpha_x, frame_delay)| {
            let mut frame = resize_anim_1.last().unwrap().buffer().clone();

            for g in hash_1_layout.clone() {
                let Point { x, .. } = g.position();
                let g = g.into_unpositioned().positioned(Point { x, y: hash_1_y as f32 });

                draw_glyph_sampled(&mut frame, &g, |x, _|
                    if x > alpha_x { WHITE_A } else { BLACK_A }
                )
            }

            Frame::from_parts(frame, 0, 0, frame_delay.into())
        }
    ).collect();

    let (img_2_width, img_2_height) = dimen_fill_area(img_2.dimensions(), (ctxt.width, gif_height));
    let img_2_large = imageops::resize(img_2, img_2_width, img_2_height, Lanczos3);

    let (img_2_start_x, img_2_start_y) = (
        (ctxt.width - img_2_width) / 2,
        (gif_height - img_2_height) / 2
    );

    let lerp_2_start = [img_2_start_x, img_2_start_y, img_2_width, img_2_height];
    let lerp_2_end = [thumb_2_x, thumb_2_y, thumb_width, thumb_height];

    println!("generating second resize animation");
    let resize_anim_2: Vec<_> = lerp_iter(lerp_2_start, lerp_2_end, 1000, 15).map(
        |([x, y, width, height], frame_delay)| {
            let mut frame = hash_1_anim[0].buffer().clone();
            let resized = imageops::resize(&img_2_large, width, height, Lanczos3);
            imageops::overlay(&mut frame, &resized, x, y);
            Frame::from_parts(frame, 0, 0, frame_delay.into())
        }
    ).collect();

    let mut hash_2_str = hash_to_string(&hash_2);
    hash_2_str.insert_str(0, ":");

    let (hash_2_x, hash_2_y) = (hash_1_x, thumb_2_y);

    let hash_2_layout = ctxt.font.layout(&hash_2_str, hash_text_size,
                                         point_f32(hash_2_x, hash_2_y));

    let (hash_2_width, hash_2_height) = size_of_text(&hash_2_layout);
    let hash_2_y = thumb_2_y + (thumb_width - hash_2_height) / 2;

    println!("generating second hash animation");
    let hash_2_anim: Vec<_> = lerp_iter(hash_2_x, hash_2_x + hash_2_width, 500, 25).map(
        |(alpha_x, frame_delay)| {
            let mut frame = resize_anim_2.last().unwrap().buffer().clone();

            for g in hash_2_layout.clone() {
                let Point { x, .. } = g.position();
                let g = g.into_unpositioned().positioned(Point { x, y: hash_2_y as f32 });

                draw_glyph_sampled(&mut frame, &g, |x, _|
                    if x > alpha_x { WHITE_A } else { BLACK_A }
                )
            }

            Frame::from_parts(frame, 0, 0, frame_delay.into())
        }
    ).collect();

    println!("generating comparison animation");
    let char_bb = hash_1_layout.clone().last().and_then(|g| g.pixel_bounding_box())
        .expect("hash should have a character bounding box");

    let Vector { x: char_width, y: char_height } = (char_bb.max - char_bb.min);
    let (char_width, char_height) = (char_width as u32, char_height as u32);

    let outline = Outline::new(char_width as u32, thumb_2_y - thumb_1_y + char_height,
                               fmul(char_width as u32, 0.1));

    let compare_frame_delay = 60;
    let compare_text_y = fmul(thumb_2_y, 1.5);
    let mut count = 0;
    let mut compare_anim: Vec<_> = hash_1_layout.skip(1).enumerate().map(|(i, g)| {
        let mut frame = hash_2_anim[0].buffer().clone();

        let Point { x, .. } = g.position();

        let left = hash_1_str.chars().nth(i as usize + 1).unwrap();
        let right = hash_2_str.chars().nth(i as usize + 1).unwrap();

        let bit = left != right;
        if bit {
            count += 1;
        }

        let color = if bit { GREEN } else { BLACK };

        outline.draw(&mut frame, x as u32, hash_1_y, color);

        let text = format!("{}{}{} {}{}", left, if bit { "≠" } else { "=" }, right, count,
                            if bit {"+"} else {""});

        let layout = center_text_in_area(
            ctxt.font.layout(&text, hash_text_size, point_f32(0, compare_text_y)),
            ctxt.width, gif_height - compare_text_y
        );

        for g in layout {
            draw_glyph(&mut frame, &g, &color);
        }

        Frame::from_parts(frame, 0, 0, compare_frame_delay.into())
    }).collect();

    let mut final_frame = hash_2_anim[0].buffer().clone();

    // NB: if you change `hash_width * hash_height` this needs to be updated
    let (desc_text, color) = match count {
        0 => ("STRONG MATCH", GREEN),
        1 ... 4 => ("VERY SIMILAR", GREEN),
        _ => ("DIFFERENT", RED),
    };

    let text = format!("={} {}", count, desc_text);

    let layout = center_text_in_area(
        ctxt.font.layout(&text, hash_text_size, point_f32(0, compare_text_y)),
        ctxt.width, gif_height - compare_text_y
    );

    for g in layout {
        draw_glyph(&mut final_frame, &g, &color);
    }

    compare_anim.push(Frame::from_parts(final_frame, 0, 0, compare_frame_delay.into()));

    println!("combining stages");
    let frames = resize_anim_1.into_iter()
        .chain(hash_1_anim)
        .chain(resize_anim_2)
        .chain(hash_2_anim)
        .chain(compare_anim)
        .collect();

    println!("saving comparison gif");
    ctxt.save_gif("compare_hashes", frames)
}
<|endoftext|>


<commit_before>use rand::seq::SliceRandom;

extern crate phf;

// build.rs produces a codegen.rs file defining WORDLIST:
// static WORDLIST: phf::Map<&str,&str>
include!(concat!(env!("OUT_DIR"), "/codegen.rs"));

pub fn get_word(roll: &str) -> Option<String> {
    WORDLIST.get(roll).map(|s| s.to_string())
}

pub fn roll() -> String {
    let mut results = Vec::<&str>::with_capacity(5);
    let mut rng = rand::thread_rng();
    let choices = vec!["1", "2", "3", "4", "5", "6"];
    results.push(choices.choose(&mut rng).unwrap());
    results.push(choices.choose(&mut rng).unwrap());
    results.push(choices.choose(&mut rng).unwrap());
    results.push(choices.choose(&mut rng).unwrap());
    results.push(choices.choose(&mut rng).unwrap());
    let result = results.join("").to_string();
    result
}
<commit_msg>Adjust RNG style of rolls.<commit_after>use rand::Rng;

extern crate phf;

// build.rs produces a codegen.rs file defining WORDLIST:
// static WORDLIST: phf::Map<&str,&str>
include!(concat!(env!("OUT_DIR"), "/codegen.rs"));

pub fn get_word(roll: &str) -> Option<String> {
    WORDLIST.get(roll).map(|s| s.to_string())
}

pub fn roll() -> String {
    let mut rng = rand::thread_rng();
    let mut results = Vec::<String>::with_capacity(5);
    for _ in 0..5 {
        let r = rng.gen_range(1, 7);
        results.push(r.to_string())
    }
    let result = results.join("");
    result
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::borrow::Cow;

static SHELL_SPECIAL: &'static str = r#"\$'"`!"#;

/// Escape characters that may have special meaning in a shell.
pub fn shell_escape(s: Cow<str>) -> Cow<str> {
    let escape_char = '\\';
    // check if string needs to be escaped
    let clean = SHELL_SPECIAL.chars().all(|sp_char| !s.contains(sp_char));
    if clean {
        return s
    }
    let mut es = String::with_capacity(s.len());
    for ch in s.chars() {
        if SHELL_SPECIAL.contains(ch) {
            es.push(escape_char);
        }
        es.push(ch)
    }
    es.into()
}

#[test]
fn test_shell_escape() {
    assert_eq!(shell_escape("--aaa=bbb ccc".into()), "--aaa=bbb ccc");
    assert_eq!(shell_escape(r#"--features="default""#.into()),
                            r#"--features=\"default\""#);
    assert_eq!(shell_escape(r#"'!\$` \\ \n"#.into()),
                            r#"\'\!\\\$\` \\\\ \\n"#);
}
<commit_msg>Also escape spaces in shell_escape<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::borrow::Cow;

static SHELL_SPECIAL: &'static str = r#" \$'"`!"#;

/// Escape characters that may have special meaning in a shell,
/// including spaces.
pub fn shell_escape(s: Cow<str>) -> Cow<str> {
    let escape_char = '\\';
    // check if string needs to be escaped
    let clean = SHELL_SPECIAL.chars().all(|sp_char| !s.contains(sp_char));
    if clean {
        return s
    }
    let mut es = String::with_capacity(s.len());
    for ch in s.chars() {
        if SHELL_SPECIAL.contains(ch) {
            es.push(escape_char);
        }
        es.push(ch)
    }
    es.into()
}

#[test]
fn test_shell_escape() {
    assert_eq!(shell_escape("--aaa=bbb-ccc".into()), "--aaa=bbb-ccc");
    assert_eq!(shell_escape("linker=gcc -L/foo -Wl,bar".into()),
                            r#"linker=gcc\ -L/foo\ -Wl,bar"#);
    assert_eq!(shell_escape(r#"--features="default""#.into()),
                            r#"--features=\"default\""#);
    assert_eq!(shell_escape(r#"'!\$`\\\n "#.into()),
                            r#"\'\!\\\$\`\\\\\\n\ "#);
}
<|endoftext|>


<commit_before>extern crate shared;

use shared::assets::*;
use std::env;

#[test]
fn test_binary() {
    let mut asset_dir = env::current_dir().expect("Unable to find executable path.");
    asset_dir.push("tests");
    asset_dir.push("data");

    print!("{:?}", asset_dir);

    load_from_dir(asset_dir);

    if let Some(Asset::Binary(ref vec)) = get_asset("test") {
        if vec != &vec![104, 114, 114, 114, 114, 109, 10] {
            panic!("Binary data did not match test file.");
        }
    } else {
        panic!("Unable to get test file.");
    }
}

#[test]
fn test_rsi() {
    let mut asset_dir = env::current_dir().expect("Unable to find executable path.");
    asset_dir.push("tests");
    asset_dir.push("data");

    print!("{:?}", asset_dir);

    load_from_dir(asset_dir);

    if let Some(Asset::Rsi(ref rsi)) = get_asset("testrsi.rs.rsi") {
        if format!("{:?}", rsi) != "Rsi { size: (32, 32), states: {\"ByeThere\": State { full name: ByeThere, size: (32, 32), dir: 1, flags: []}, \"HiThere\": State { full name: HiThere, size: (32, 32), dir: 1, flags: []}} }" {
            panic!("RSI metadata incorrect.");
        }
    } else {
        panic!("Unable to get test file.");
    }
}
<commit_msg>Please work, thanks.<commit_after>extern crate shared;

use shared::assets::*;
use std::env;

#[test]
fn test_binary() {
    let mut asset_dir = env::current_dir().expect("Unable to find executable path.");
    asset_dir.push("tests");
    asset_dir.push("data");

    print!("{:?}", asset_dir);

    load_from_dir(asset_dir);

    if let Some(Asset::Binary(ref vec)) = get_asset("test") {
        if vec != &vec![104, 114, 114, 114, 114, 109, 10] {
            panic!("Binary data did not match test file.");
        }
    } else {
        panic!("Unable to get test file.");
    }
}

#[test]
fn test_rsi() {
    let mut asset_dir = env::current_dir().expect("Unable to find executable path.");
    asset_dir.push("tests");
    asset_dir.push("data");

    print!("{:?}", asset_dir);

    load_from_dir(asset_dir);

    if let Some(Asset::Rsi(ref rsi)) = get_asset("testrsi.rs.rsi") {
        let a = format!("{:?}", rsi);
        let b = "Rsi { size: (32, 32), states: {\"ByeThere\": State { full name: ByeThere, size: (32, 32), dir: 1, flags: []}, \"HiThere\": State { full name: HiThere, size: (32, 32), dir: 1, flags: []}} }";
        if a != b {
            println!("{:?}\n{:?}", a, b);
            panic!("RSI metadata incorrect.");
        }
    } else {
        panic!("Unable to get test file.");
    }
}
<|endoftext|>


<commit_before>use nom::{
  be_u8, be_u16,
  IResult,
  ErrorCode, Err,
};

use frame::{
  ChannelAssignment, NumberType,
  Frame,
  Header, Footer,
};

use metadata::StreamInfo;
use utility::{crc8, crc16, to_u32};

pub fn frame_parser<'a>(input: &'a [u8], stream_info: &StreamInfo)
                        -> IResult<'a, &'a [u8], Frame> {
  let result = chain!(input,
    frame_header: apply!(header, stream_info) ~
    frame_footer: footer,
    || {
      Frame {
        header: frame_header,
        footer: frame_footer,
      }
    }
  );

  match result {
    IResult::Done(i, frame)   => {
      // All frame bytes before the crc-16
      let end         = (input.len() - i.len()) - 2;
      let Footer(crc) = frame.footer;

      if crc16(&input[0..end]) == crc {
        IResult::Done(i, frame)
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn blocking_strategy(input: &[u8]) -> IResult<&[u8], bool> {
  match take!(input, 2) {
    IResult::Done(i, bytes)   => {
      let sync_code = ((bytes[0] as u16) << 6) +
                      (bytes[1] as u16) >> 2;
      let is_valid  = ((bytes[1] >> 1) & 0b01) == 0;

      if sync_code == 0b11111111111110 && is_valid {
        let is_variable_block_size = (bytes[1] & 0b01) == 1;

        IResult::Done(i, is_variable_block_size)
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn block_sample(input: &[u8]) -> IResult<&[u8], (u8, u8)> {
  match be_u8(input) {
    IResult::Done(i, byte)    => {
      let block_byte  = byte >> 4;
      let sample_byte = byte & 0x0f;
      let is_valid    = block_byte != 0b0000 && sample_byte != 0b1111;

      if is_valid {
        IResult::Done(i, (block_byte, sample_byte))
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn channel_bits(input: &[u8]) -> IResult<&[u8], (ChannelAssignment, u8)> {
  match be_u8(input) {
    IResult::Done(i, byte)    => {
      let channel_byte       = byte >> 4;
      let channel_assignment = match channel_byte {
        0b0000...0b0111 => ChannelAssignment::Independent,
        0b1000          => ChannelAssignment::LeftSide,
        0b1001          => ChannelAssignment::RightSide,
        0b1010          => ChannelAssignment::MiddleSide,
        _               => ChannelAssignment::Independent,
      };
      let size_byte = (byte >> 1) & 0b0111;

      // Checks the validity of:
      //
      // * (4 bits) channel assignment
      // * (3 bits) sample size
      // * (1 bit) last bit
      //
      // All these checks are whether of not they are reserved and should
      // return an error if so.
      let is_valid = channel_byte < 0b1011 &&
                     (size_byte != 0b0011 && size_byte != 0b0111) &&
                     (byte & 0b01) == 0;

      if is_valid {
        IResult::Done(i, (channel_assignment, size_byte))
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn utf8_size(input: &[u8], is_u64: bool)
             -> IResult<&[u8], Option<(usize, u8)>> {
  map!(input, be_u8, |utf8_header| {
    match utf8_header {
      0b00000000...0b01111111 => Some((0, utf8_header)),
      0b11000000...0b11011111 => Some((1, utf8_header & 0b00011111)),
      0b11100000...0b11101111 => Some((2, utf8_header & 0b00001111)),
      0b11110000...0b11110111 => Some((3, utf8_header & 0b00000111)),
      0b11111000...0b11111011 => Some((4, utf8_header & 0b00000011)),
      0b11111100...0b11111101 => Some((5, utf8_header & 0b00000001)),
      0b11111110              => if is_u64 { Some((6, 0)) } else { None },
      _                       => None,
    }
  })
}

fn number_type(input: &[u8], is_sample: bool,
               (size, value): (usize, u8))
               -> IResult<&[u8], NumberType> {
  let mut result   = value as u64;
  let mut is_error = false;

  match take!(input, size) {
    IResult::Done(i, bytes)   => {
      for i in 0..size {
        let byte = bytes[i] as u64;

        if byte >= 0b10000000 && byte <= 0b10111111 {
          result = (result << 6) + (byte & 0b00111111);
        } else {
          is_error = true;
          break;
        }
      }

      if is_error {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      } else if is_sample {
        IResult::Done(i, NumberType::Sample(result))
      } else {
        IResult::Done(i, NumberType::Frame(result as u32))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn secondary_block_size(input: &[u8], block_byte: u8)
                        -> IResult<&[u8], Option<u32>> {
  match block_byte {
    0b0110 => opt!(input, map!(take!(1), to_u32)),
    0b0111 => opt!(input, map!(take!(2), to_u32)),
    _      => IResult::Done(input, None)
  }
}

fn secondary_sample_rate(input: &[u8], sample_byte: u8)
                        -> IResult<&[u8], Option<u32>> {
  match sample_byte {
    0b1100 => opt!(input, map!(take!(1), to_u32)),
    0b1101 => opt!(input, map!(take!(2), to_u32)),
    0b1110 => opt!(input, map!(take!(2), to_u32)),
    _      => IResult::Done(input, None)
  }
}

pub fn header<'a>(input: &'a [u8], stream_info: &StreamInfo)
                  -> IResult<'a, &'a [u8], Header> {
  let result = chain!(input,
    is_variable_block_size: blocking_strategy ~
    tuple0: block_sample ~
    tuple1: channel_bits ~
    number_opt: apply!(utf8_size, is_variable_block_size) ~
    number_length: expr_opt!(number_opt) ~
    number: apply!(number_type, is_variable_block_size, number_length) ~
    alt_block_size: apply!(secondary_block_size, tuple0.0) ~
    alt_sample_rate: apply!(secondary_sample_rate, tuple0.1) ~
    crc: be_u8,
    || {
      let (block_byte, sample_byte)       = tuple0;
      let (channel_assignment, size_byte) = tuple1;

      let block_size = match block_byte {
        0b0001          => 192,
        0b0010...0b0101 => 576 * 2_u32.pow(block_byte as u32 - 2),
        0b0110 | 0b0111 => alt_block_size.unwrap() + 1,
        0b1000...0b1111 => 256 * 2_u32.pow(block_byte as u32 - 8),
        _               => unreachable!(),
      };

      let sample_rate = match sample_byte {
        0b0000 => stream_info.sample_rate,
        0b0001 => 88200,
        0b0010 => 176400,
        0b0011 => 192000,
        0b0100 => 8000,
        0b0101 => 16000,
        0b0110 => 22050,
        0b0111 => 24000,
        0b1000 => 32000,
        0b1001 => 44100,
        0b1010 => 48000,
        0b1011 => 96000,
        0b1100 => alt_sample_rate.unwrap() * 1000,
        0b1101 => alt_sample_rate.unwrap(),
        0b1110 => alt_sample_rate.unwrap() * 10,
        _      => unreachable!(),
      };

      let bits_per_sample = match size_byte {
        0b0000 => stream_info.bits_per_sample as usize,
        0b0001 => 8,
        0b0010 => 12,
        0b0100 => 16,
        0b0101 => 20,
        0b0110 => 24,
        _      => unreachable!(),
      };

      Header {
        block_size: block_size,
        sample_rate: sample_rate,
        channel_assignment: channel_assignment,
        bits_per_sample: bits_per_sample,
        number: number,
        crc: crc,
      }
    }
  );

  match result {
    IResult::Done(i, frame_header) => {
      // All header bytes before the crc-8
      let end = (input.len() - i.len()) - 1;

      if crc8(&input[0..end]) == frame_header.crc {
        IResult::Done(i, frame_header)
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      }
    }
    IResult::Error(error)          => IResult::Error(error),
    IResult::Incomplete(need)      => IResult::Incomplete(need),
  }
}

named!(pub footer <&[u8], Footer>, map!(be_u16, Footer));

#[cfg(test)]
mod tests {
  use super::*;
  use frame::{
    Header, Footer,
    ChannelAssignment, NumberType,
  };
  use metadata::StreamInfo;
  use nom::IResult;

  #[test]
  fn test_header() {
    let input  = b"\xff\xf8\x53\x1c\xf0\x90\x80\x80\x2e";
    let info   = StreamInfo::new();
    let result = Header {
      block_size: 4608,
      sample_rate: 192000,
      channel_assignment: ChannelAssignment::Independent,
      bits_per_sample: 24,
      number: NumberType::Frame(65536),
      crc: 0x2e,
    };

    assert_eq!(header(input, &info), IResult::Done(&[][..], result));
  }

  #[test]
  fn test_footer() {
    let input  = b"\x03\xe8";
    let result = IResult::Done(&[][..], Footer(0x03e8));

    assert_eq!(footer(input), result);
  }
}
<commit_msg>Fix test for header parser<commit_after>use nom::{
  be_u8, be_u16,
  IResult,
  ErrorCode, Err,
};

use frame::{
  ChannelAssignment, NumberType,
  Frame,
  Header, Footer,
};

use metadata::StreamInfo;
use utility::{crc8, crc16, to_u32};

pub fn frame_parser<'a>(input: &'a [u8], stream_info: &StreamInfo)
                        -> IResult<'a, &'a [u8], Frame> {
  let result = chain!(input,
    frame_header: apply!(header, stream_info) ~
    frame_footer: footer,
    || {
      Frame {
        header: frame_header,
        footer: frame_footer,
      }
    }
  );

  match result {
    IResult::Done(i, frame)   => {
      // All frame bytes before the crc-16
      let end         = (input.len() - i.len()) - 2;
      let Footer(crc) = frame.footer;

      if crc16(&input[0..end]) == crc {
        IResult::Done(i, frame)
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn blocking_strategy(input: &[u8]) -> IResult<&[u8], bool> {
  match take!(input, 2) {
    IResult::Done(i, bytes)   => {
      let sync_code = ((bytes[0] as u16) << 6) +
                      ((bytes[1] as u16) >> 2);
      let is_valid  = ((bytes[1] >> 1) & 0b01) == 0;

      if sync_code == 0b11111111111110 && is_valid {
        let is_variable_block_size = (bytes[1] & 0b01) == 1;

        IResult::Done(i, is_variable_block_size)
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn block_sample(input: &[u8]) -> IResult<&[u8], (u8, u8)> {
  match be_u8(input) {
    IResult::Done(i, byte)    => {
      let block_byte  = byte >> 4;
      let sample_byte = byte & 0x0f;
      let is_valid    = block_byte != 0b0000 && sample_byte != 0b1111;

      if is_valid {
        IResult::Done(i, (block_byte, sample_byte))
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn channel_bits(input: &[u8]) -> IResult<&[u8], (ChannelAssignment, u8)> {
  match be_u8(input) {
    IResult::Done(i, byte)    => {
      let channel_byte       = byte >> 4;
      let channel_assignment = match channel_byte {
        0b0000...0b0111 => ChannelAssignment::Independent,
        0b1000          => ChannelAssignment::LeftSide,
        0b1001          => ChannelAssignment::RightSide,
        0b1010          => ChannelAssignment::MiddleSide,
        _               => ChannelAssignment::Independent,
      };
      let size_byte = (byte >> 1) & 0b0111;

      // Checks the validity of:
      //
      // * (4 bits) channel assignment
      // * (3 bits) sample size
      // * (1 bit) last bit
      //
      // All these checks are whether of not they are reserved and should
      // return an error if so.
      let is_valid = channel_byte < 0b1011 &&
                     (size_byte != 0b0011 && size_byte != 0b0111) &&
                     (byte & 0b01) == 0;

      if is_valid {
        IResult::Done(i, (channel_assignment, size_byte))
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn utf8_size(input: &[u8], is_u64: bool)
             -> IResult<&[u8], Option<(usize, u8)>> {
  map!(input, be_u8, |utf8_header| {
    match utf8_header {
      0b00000000...0b01111111 => Some((0, utf8_header)),
      0b11000000...0b11011111 => Some((1, utf8_header & 0b00011111)),
      0b11100000...0b11101111 => Some((2, utf8_header & 0b00001111)),
      0b11110000...0b11110111 => Some((3, utf8_header & 0b00000111)),
      0b11111000...0b11111011 => Some((4, utf8_header & 0b00000011)),
      0b11111100...0b11111101 => Some((5, utf8_header & 0b00000001)),
      0b11111110              => if is_u64 { Some((6, 0)) } else { None },
      _                       => None,
    }
  })
}

fn number_type(input: &[u8], is_sample: bool,
               (size, value): (usize, u8))
               -> IResult<&[u8], NumberType> {
  let mut result   = value as u64;
  let mut is_error = false;

  match take!(input, size) {
    IResult::Done(i, bytes)   => {
      for i in 0..size {
        let byte = bytes[i] as u64;

        if byte >= 0b10000000 && byte <= 0b10111111 {
          result = (result << 6) + (byte & 0b00111111);
        } else {
          is_error = true;
          break;
        }
      }

      if is_error {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      } else if is_sample {
        IResult::Done(i, NumberType::Sample(result))
      } else {
        IResult::Done(i, NumberType::Frame(result as u32))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn secondary_block_size(input: &[u8], block_byte: u8)
                        -> IResult<&[u8], Option<u32>> {
  match block_byte {
    0b0110 => opt!(input, map!(take!(1), to_u32)),
    0b0111 => opt!(input, map!(take!(2), to_u32)),
    _      => IResult::Done(input, None)
  }
}

fn secondary_sample_rate(input: &[u8], sample_byte: u8)
                        -> IResult<&[u8], Option<u32>> {
  match sample_byte {
    0b1100 => opt!(input, map!(take!(1), to_u32)),
    0b1101 => opt!(input, map!(take!(2), to_u32)),
    0b1110 => opt!(input, map!(take!(2), to_u32)),
    _      => IResult::Done(input, None)
  }
}

pub fn header<'a>(input: &'a [u8], stream_info: &StreamInfo)
                  -> IResult<'a, &'a [u8], Header> {
  let result = chain!(input,
    is_variable_block_size: blocking_strategy ~
    tuple0: block_sample ~
    tuple1: channel_bits ~
    number_opt: apply!(utf8_size, is_variable_block_size) ~
    number_length: expr_opt!(number_opt) ~
    number: apply!(number_type, is_variable_block_size, number_length) ~
    alt_block_size: apply!(secondary_block_size, tuple0.0) ~
    alt_sample_rate: apply!(secondary_sample_rate, tuple0.1) ~
    crc: be_u8,
    || {
      let (block_byte, sample_byte)       = tuple0;
      let (channel_assignment, size_byte) = tuple1;

      let block_size = match block_byte {
        0b0001          => 192,
        0b0010...0b0101 => 576 * 2_u32.pow(block_byte as u32 - 2),
        0b0110 | 0b0111 => alt_block_size.unwrap() + 1,
        0b1000...0b1111 => 256 * 2_u32.pow(block_byte as u32 - 8),
        _               => unreachable!(),
      };

      let sample_rate = match sample_byte {
        0b0000 => stream_info.sample_rate,
        0b0001 => 88200,
        0b0010 => 176400,
        0b0011 => 192000,
        0b0100 => 8000,
        0b0101 => 16000,
        0b0110 => 22050,
        0b0111 => 24000,
        0b1000 => 32000,
        0b1001 => 44100,
        0b1010 => 48000,
        0b1011 => 96000,
        0b1100 => alt_sample_rate.unwrap() * 1000,
        0b1101 => alt_sample_rate.unwrap(),
        0b1110 => alt_sample_rate.unwrap() * 10,
        _      => unreachable!(),
      };

      let bits_per_sample = match size_byte {
        0b0000 => stream_info.bits_per_sample as usize,
        0b0001 => 8,
        0b0010 => 12,
        0b0100 => 16,
        0b0101 => 20,
        0b0110 => 24,
        _      => unreachable!(),
      };

      Header {
        block_size: block_size,
        sample_rate: sample_rate,
        channel_assignment: channel_assignment,
        bits_per_sample: bits_per_sample,
        number: number,
        crc: crc,
      }
    }
  );

  match result {
    IResult::Done(i, frame_header) => {
      // All header bytes before the crc-8
      let end = (input.len() - i.len()) - 1;

      if crc8(&input[0..end]) == frame_header.crc {
        IResult::Done(i, frame_header)
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      }
    }
    IResult::Error(error)          => IResult::Error(error),
    IResult::Incomplete(need)      => IResult::Incomplete(need),
  }
}

named!(pub footer <&[u8], Footer>, map!(be_u16, Footer));

#[cfg(test)]
mod tests {
  use super::*;
  use frame::{
    Header, Footer,
    ChannelAssignment, NumberType,
  };
  use metadata::StreamInfo;
  use nom::IResult;

  #[test]
  fn test_header() {
    let input  = b"\xff\xf8\x53\x1c\xf0\x90\x80\x80\x2e";
    let info   = StreamInfo::new();
    let result = Header {
      block_size: 4608,
      sample_rate: 192000,
      channel_assignment: ChannelAssignment::Independent,
      bits_per_sample: 24,
      number: NumberType::Frame(65536),
      crc: 0x2e,
    };

    assert_eq!(header(input, &info), IResult::Done(&[][..], result));
  }

  #[test]
  fn test_footer() {
    let input  = b"\x03\xe8";
    let result = IResult::Done(&[][..], Footer(0x03e8));

    assert_eq!(footer(input), result);
  }
}
<|endoftext|>