<commit_before>
/// Parses a SPIR-V document.
pub fn parse_spirv(data: &[u8]) -> Result<Spirv, ParseError> {
    if data.len() < 20 {
        return Err(ParseError::MissingHeader);
    }

    // we need to determine whether we are in big endian order or little endian order depending
    // on the magic number at the start of the file
    let data = if data[0] == 0x07 && data[1] == 0x23 && data[2] == 0x02 && data[3] == 0x03 {
        // big endian
        data.chunks(4).map(|c| {
            ((c[0] as u32) << 24) | ((c[1] as u32) << 16) | ((c[2] as u32) << 8) | c[3] as u32
        }).collect::<Vec<_>>()

    } else if data[3] == 0x07 && data[2] == 0x23 && data[1] == 0x02 && data[0] == 0x03 {
        // little endian
        data.chunks(4).map(|c| {
            ((c[3] as u32) << 24) | ((c[2] as u32) << 16) | ((c[1] as u32) << 8) | c[0] as u32
        }).collect::<Vec<_>>()

    } else {
        return Err(ParseError::MissingHeader);
    };

    parse_u32s(&data)
}

/// Parses a SPIR-V document from a list of u32s.
///
/// Endianess has already been handled.
fn parse_u32s(i: &[u32]) -> Result<Spirv, ParseError> {
    if i.len() < 5 {
        return Err(ParseError::MissingHeader);
    }

    if i[0] != 0x07230203 {
        return Err(ParseError::WrongHeader);
    }

    let version = (((i[1] & 0x00ff0000) >> 16) as u8, ((i[1] & 0x0000ff00) >> 8) as u8);

    let instructions = {
        let mut ret = Vec::new();
        let mut i = &i[5..];
        while i.len() >= 1 {
            let (instruction, rest) = try!(parse_instruction(i));
            ret.push(instruction);
            i = rest;
        }
        ret
    };

    Ok(Spirv {
        version: version,
        bound: i[3],
        instructions: instructions,
    })
}

/// Error that can happen when parsing.
#[derive(Debug, Clone)]
pub enum ParseError {
    MissingHeader,
    WrongHeader,
    IncompleteInstruction,
    UnknownCapability(u32),
    UnknownExecutionModel(u32),
    UnknownStorageClass(u32),
}

#[derive(Debug, Clone)]
pub struct Spirv {
    pub version: (u8, u8),
    pub bound: u32,
    pub instructions: Vec<Instruction>,
}

#[derive(Debug, Clone)]
pub enum Instruction {
    Unknown(u16, Vec<u32>),
    Nop,
    Name { target_id: u32, name: String },
    MemberName { target_id: u32, member: u32, name: String },
    ExtInstImport { result_id: u32, name: String },
    EntryPoint { execution: ExecutionModel, id: u32, name: String, interface: Vec<u32> },
    Capability(Capability),
    TypeVoid { result_id: u32 },
    TypeBool { result_id: u32 },
    TypeInt { result_id: u32, width: u32, signedness: bool },
    TypeFloat { result_id: u32, width: u32 },
    TypeVector { result_id: u32, component_id: u32, count: u32 },
    TypeArray { result_id: u32, type_id: u32, length_id: u32 },
    TypeRuntimeArray { result_id: u32, type_id: u32 },
    TypeStruct { result_id: u32, member_types: Vec<u32> },
    TypeOpaque { result_id: u32, name: String },
    TypePointer { result_id: u32, storage_class: StorageClass, type_id: u32 },
    FunctionEnd,
    Variable { result_type_id: u32, result_id: u32, storage_class: StorageClass, initializer: Option<u32> },
    Kill,
    Return,
}

fn parse_instruction(i: &[u32]) -> Result<(Instruction, &[u32]), ParseError> {
    assert!(i.len() >= 1);

    let word_count = (i[0] >> 16) as usize;
    assert!(word_count >= 1);
    let opcode = (i[0] & 0xffff) as u16;

    if i.len() < word_count {
        return Err(ParseError::IncompleteInstruction);
    }

    let opcode = try!(decode_instruction(opcode, &i[1 .. word_count]));
    Ok((opcode, &i[word_count..]))
}

fn decode_instruction(opcode: u16, operands: &[u32]) -> Result<Instruction, ParseError> {
    Ok(match opcode {
        0 => Instruction::Nop,
        5 => Instruction::Name { target_id: operands[0], name: parse_string(&operands[1..]).0 },
        6 => Instruction::MemberName { target_id: operands[0], member: operands[1], name: parse_string(&operands[2..]).0 },
        11 => Instruction::ExtInstImport {
            result_id: operands[0],
            name: parse_string(&operands[1..]).0
        },
        15 => {
            let (n, r) = parse_string(&operands[2..]);
            Instruction::EntryPoint {
                execution: try!(ExecutionModel::from_num(operands[0])),
                id: operands[1],
                name: n,
                interface: r.to_owned(),
            }
        },
        17 => Instruction::Capability(try!(Capability::from_num(operands[0]))),
        19 => Instruction::TypeVoid { result_id: operands[0] },
        20 => Instruction::TypeBool { result_id: operands[0] },
        21 => Instruction::TypeInt { result_id: operands[0], width: operands[1], signedness: operands[2] != 0 },
        22 => Instruction::TypeFloat { result_id: operands[0], width: operands[1] },
        23 => Instruction::TypeVector { result_id: operands[0], component_id: operands[1], count: operands[2] },
        28 => Instruction::TypeArray { result_id: operands[0], type_id: operands[1], length_id: operands[2] },
        29 => Instruction::TypeRuntimeArray { result_id: operands[0], type_id: operands[1] },
        30 => Instruction::TypeStruct { result_id: operands[0], member_types: operands[1..].to_owned() },
        31 => Instruction::TypeOpaque { result_id: operands[0], name: parse_string(&operands[1..]).0 },
        32 => Instruction::TypePointer { result_id: operands[0], storage_class: try!(StorageClass::from_num(operands[1])), type_id: operands[2] },
        56 => Instruction::FunctionEnd,
        59 => Instruction::Variable {
            result_type_id: operands[0], result_id: operands[1],
            storage_class: try!(StorageClass::from_num(operands[2])),
            initializer: operands.get(3).map(|&v| v)
        },
        252 => Instruction::Kill,
        253 => Instruction::Return,
        _ => Instruction::Unknown(opcode, operands.to_owned()),
    })
}

fn parse_string(data: &[u32]) -> (String, &[u32]) {
    let bytes = data.iter().flat_map(|&n| {
        let b1 = (n & 0xff) as u8;
        let b2 = ((n >> 8) & 0xff) as u8;
        let b3 = ((n >> 16) & 0xff) as u8;
        let b4 = ((n >> 24) & 0xff) as u8;
        vec![b1, b2, b3, b4].into_iter()
    }).take_while(|&b| b != 0).collect::<Vec<u8>>();

    let r = 1 + bytes.len() / 4;
    let s = String::from_utf8(bytes).expect("Shader content is not UTF-8");

    (s, &data[r..])
}

#[derive(Debug, Clone)]
pub enum Capability {
    Matrix,
    Shader,
    Geometry,
    Tessellation,
    Addresses,
    Linkage,
    Kernel,
    Vector16,
    Float16Buffer,
    Float16,
    Float64,
    Int64,
    Int64Atomics,
    ImageBasic,
    ImageReadWrite,
    ImageMipmap,
    Pipes,
    Groups,
    DeviceEnqueue,
    LiteralSampler,
    AtomicStorage,
    Int16,
    TessellationPointSize,
    GeometryPointSize,
    ImageGatherExtended,
    StorageImageMultisample,
    UniformBufferArrayDynamicIndexing,
    SampledImageArrayDynamicIndexing,
    StorageBufferArrayDynamicIndexing,
    StorageImageArrayDynamicIndexing,
    ClipDistance,
    CullDistance,
    ImageCubeArray,
    SampleRateShading,
    ImageRect,
    SampledRect,
    GenericPointer,
    Int8,
    InputAttachment,
    SparseResidency,
    MinLod,
    Sampled1D,
    Image1D,
    SampledCubeArray,
    SampledBuffer,
    ImageBuffer,
    ImageMSArray,
    StorageImageExtendedFormats,
    ImageQuery,
    DerivativeControl,
    InterpolationFunction,
    TransformFeedback,
    GeometryStreams,
    StorageImageReadWithoutFormat,
    StorageImageWriteWithoutFormat,
}

impl Capability {
    fn from_num(num: u32) -> Result<Capability, ParseError> {
        match num {
            0 => Ok(Capability::Matrix),
            1 => Ok(Capability::Shader),
            2 => Ok(Capability::Geometry),
            3 => Ok(Capability::Tessellation),
            4 => Ok(Capability::Addresses),
            5 => Ok(Capability::Linkage),
            6 => Ok(Capability::Kernel),
            7 => Ok(Capability::Vector16),
            8 => Ok(Capability::Float16Buffer),
            9 => Ok(Capability::Float16),
            10 => Ok(Capability::Float64),
            11 => Ok(Capability::Int64),
            12 => Ok(Capability::Int64Atomics),
            13 => Ok(Capability::ImageBasic),
            14 => Ok(Capability::ImageReadWrite),
            15 => Ok(Capability::ImageMipmap),
            17 => Ok(Capability::Pipes),
            18 => Ok(Capability::Groups),
            19 => Ok(Capability::DeviceEnqueue),
            20 => Ok(Capability::LiteralSampler),
            21 => Ok(Capability::AtomicStorage),
            22 => Ok(Capability::Int16),
            23 => Ok(Capability::TessellationPointSize),
            24 => Ok(Capability::GeometryPointSize),
            25 => Ok(Capability::ImageGatherExtended),
            27 => Ok(Capability::StorageImageMultisample),
            28 => Ok(Capability::UniformBufferArrayDynamicIndexing),
            29 => Ok(Capability::SampledImageArrayDynamicIndexing),
            30 => Ok(Capability::StorageBufferArrayDynamicIndexing),
            31 => Ok(Capability::StorageImageArrayDynamicIndexing),
            32 => Ok(Capability::ClipDistance),
            33 => Ok(Capability::CullDistance),
            34 => Ok(Capability::ImageCubeArray),
            35 => Ok(Capability::SampleRateShading),
            36 => Ok(Capability::ImageRect),
            37 => Ok(Capability::SampledRect),
            38 => Ok(Capability::GenericPointer),
            39 => Ok(Capability::Int8),
            40 => Ok(Capability::InputAttachment),
            41 => Ok(Capability::SparseResidency),
            42 => Ok(Capability::MinLod),
            43 => Ok(Capability::Sampled1D),
            44 => Ok(Capability::Image1D),
            45 => Ok(Capability::SampledCubeArray),
            46 => Ok(Capability::SampledBuffer),
            47 => Ok(Capability::ImageBuffer),
            48 => Ok(Capability::ImageMSArray),
            49 => Ok(Capability::StorageImageExtendedFormats),
            50 => Ok(Capability::ImageQuery),
            51 => Ok(Capability::DerivativeControl),
            52 => Ok(Capability::InterpolationFunction),
            53 => Ok(Capability::TransformFeedback),
            54 => Ok(Capability::GeometryStreams),
            55 => Ok(Capability::StorageImageReadWithoutFormat),
            56 => Ok(Capability::StorageImageWriteWithoutFormat),
            _ => Err(ParseError::UnknownCapability(num)),
        }
    }
}

#[derive(Debug, Clone)]
pub enum ExecutionModel {
    Vertex,
    TessellationControl,
    TessellationEvaluation,
    Geometry,
    Fragment,
    GLCompute,
    Kernel,
}

impl ExecutionModel {
    fn from_num(num: u32) -> Result<ExecutionModel, ParseError> {
        match num {
            0 => Ok(ExecutionModel::Vertex),
            1 => Ok(ExecutionModel::TessellationControl),
            2 => Ok(ExecutionModel::TessellationEvaluation),
            3 => Ok(ExecutionModel::Geometry),
            4 => Ok(ExecutionModel::Fragment),
            5 => Ok(ExecutionModel::GLCompute),
            6 => Ok(ExecutionModel::Kernel),
            _ => Err(ParseError::UnknownExecutionModel(num)),
        }
    }
}

#[derive(Debug, Clone)]
pub enum StorageClass {
    UniformConstant,
    Input,
    Uniform,
    Output,
    Workgroup,
    CrossWorkgroup,
    Private,
    Function,
    Generic,
    PushConstant,
    AtomicCounter,
    Image,
}

impl StorageClass {
    fn from_num(num: u32) -> Result<StorageClass, ParseError> {
        match num {
            0 => Ok(StorageClass::UniformConstant),
            1 => Ok(StorageClass::Input),
            2 => Ok(StorageClass::Uniform),
            3 => Ok(StorageClass::Output),
            4 => Ok(StorageClass::Workgroup),
            5 => Ok(StorageClass::CrossWorkgroup),
            6 => Ok(StorageClass::Private),
            7 => Ok(StorageClass::Function),
            8 => Ok(StorageClass::Generic),
            9 => Ok(StorageClass::PushConstant),
            10 => Ok(StorageClass::AtomicCounter),
            11 => Ok(StorageClass::Image),
            _ => Err(ParseError::UnknownStorageClass(num)),
        }
    }
}

#[cfg(test)]
mod test {
    use parse;

    #[test]
    fn test() {
        let data = include_bytes!("../examples/example.spv");
        println!("{:?}", parse::parse_spirv(data).unwrap());
    }
}
<commit_msg>More instructions<commit_after>
/// Parses a SPIR-V document.
pub fn parse_spirv(data: &[u8]) -> Result<Spirv, ParseError> {
    if data.len() < 20 {
        return Err(ParseError::MissingHeader);
    }

    // we need to determine whether we are in big endian order or little endian order depending
    // on the magic number at the start of the file
    let data = if data[0] == 0x07 && data[1] == 0x23 && data[2] == 0x02 && data[3] == 0x03 {
        // big endian
        data.chunks(4).map(|c| {
            ((c[0] as u32) << 24) | ((c[1] as u32) << 16) | ((c[2] as u32) << 8) | c[3] as u32
        }).collect::<Vec<_>>()

    } else if data[3] == 0x07 && data[2] == 0x23 && data[1] == 0x02 && data[0] == 0x03 {
        // little endian
        data.chunks(4).map(|c| {
            ((c[3] as u32) << 24) | ((c[2] as u32) << 16) | ((c[1] as u32) << 8) | c[0] as u32
        }).collect::<Vec<_>>()

    } else {
        return Err(ParseError::MissingHeader);
    };

    parse_u32s(&data)
}

/// Parses a SPIR-V document from a list of u32s.
///
/// Endianess has already been handled.
fn parse_u32s(i: &[u32]) -> Result<Spirv, ParseError> {
    if i.len() < 5 {
        return Err(ParseError::MissingHeader);
    }

    if i[0] != 0x07230203 {
        return Err(ParseError::WrongHeader);
    }

    let version = (((i[1] & 0x00ff0000) >> 16) as u8, ((i[1] & 0x0000ff00) >> 8) as u8);

    let instructions = {
        let mut ret = Vec::new();
        let mut i = &i[5..];
        while i.len() >= 1 {
            let (instruction, rest) = try!(parse_instruction(i));
            ret.push(instruction);
            i = rest;
        }
        ret
    };

    Ok(Spirv {
        version: version,
        bound: i[3],
        instructions: instructions,
    })
}

/// Error that can happen when parsing.
#[derive(Debug, Clone)]
pub enum ParseError {
    MissingHeader,
    WrongHeader,
    IncompleteInstruction,
    UnknownCapability(u32),
    UnknownExecutionModel(u32),
    UnknownStorageClass(u32),
    UnknownAddressingModel(u32),
    UnknownMemoryModel(u32),
}

#[derive(Debug, Clone)]
pub struct Spirv {
    pub version: (u8, u8),
    pub bound: u32,
    pub instructions: Vec<Instruction>,
}

#[derive(Debug, Clone)]
pub enum Instruction {
    Unknown(u16, Vec<u32>),
    Nop,
    Name { target_id: u32, name: String },
    MemberName { target_id: u32, member: u32, name: String },
    ExtInstImport { result_id: u32, name: String },
    MemoryModel(AddressingModel, MemoryModel),
    EntryPoint { execution: ExecutionModel, id: u32, name: String, interface: Vec<u32> },
    Capability(Capability),
    TypeVoid { result_id: u32 },
    TypeBool { result_id: u32 },
    TypeInt { result_id: u32, width: u32, signedness: bool },
    TypeFloat { result_id: u32, width: u32 },
    TypeVector { result_id: u32, component_id: u32, count: u32 },
    TypeArray { result_id: u32, type_id: u32, length_id: u32 },
    TypeRuntimeArray { result_id: u32, type_id: u32 },
    TypeStruct { result_id: u32, member_types: Vec<u32> },
    TypeOpaque { result_id: u32, name: String },
    TypePointer { result_id: u32, storage_class: StorageClass, type_id: u32 },
    FunctionEnd,
    Variable { result_type_id: u32, result_id: u32, storage_class: StorageClass, initializer: Option<u32> },
    Kill,
    Return,
}

fn parse_instruction(i: &[u32]) -> Result<(Instruction, &[u32]), ParseError> {
    assert!(i.len() >= 1);

    let word_count = (i[0] >> 16) as usize;
    assert!(word_count >= 1);
    let opcode = (i[0] & 0xffff) as u16;

    if i.len() < word_count {
        return Err(ParseError::IncompleteInstruction);
    }

    let opcode = try!(decode_instruction(opcode, &i[1 .. word_count]));
    Ok((opcode, &i[word_count..]))
}

fn decode_instruction(opcode: u16, operands: &[u32]) -> Result<Instruction, ParseError> {
    Ok(match opcode {
        0 => Instruction::Nop,
        5 => Instruction::Name { target_id: operands[0], name: parse_string(&operands[1..]).0 },
        6 => Instruction::MemberName { target_id: operands[0], member: operands[1], name: parse_string(&operands[2..]).0 },
        11 => Instruction::ExtInstImport {
            result_id: operands[0],
            name: parse_string(&operands[1..]).0
        },
        14 => Instruction::MemoryModel(try!(AddressingModel::from_num(operands[0])), try!(MemoryModel::from_num(operands[1]))),
        15 => {
            let (n, r) = parse_string(&operands[2..]);
            Instruction::EntryPoint {
                execution: try!(ExecutionModel::from_num(operands[0])),
                id: operands[1],
                name: n,
                interface: r.to_owned(),
            }
        },
        17 => Instruction::Capability(try!(Capability::from_num(operands[0]))),
        19 => Instruction::TypeVoid { result_id: operands[0] },
        20 => Instruction::TypeBool { result_id: operands[0] },
        21 => Instruction::TypeInt { result_id: operands[0], width: operands[1], signedness: operands[2] != 0 },
        22 => Instruction::TypeFloat { result_id: operands[0], width: operands[1] },
        23 => Instruction::TypeVector { result_id: operands[0], component_id: operands[1], count: operands[2] },
        28 => Instruction::TypeArray { result_id: operands[0], type_id: operands[1], length_id: operands[2] },
        29 => Instruction::TypeRuntimeArray { result_id: operands[0], type_id: operands[1] },
        30 => Instruction::TypeStruct { result_id: operands[0], member_types: operands[1..].to_owned() },
        31 => Instruction::TypeOpaque { result_id: operands[0], name: parse_string(&operands[1..]).0 },
        32 => Instruction::TypePointer { result_id: operands[0], storage_class: try!(StorageClass::from_num(operands[1])), type_id: operands[2] },
        56 => Instruction::FunctionEnd,
        59 => Instruction::Variable {
            result_type_id: operands[0], result_id: operands[1],
            storage_class: try!(StorageClass::from_num(operands[2])),
            initializer: operands.get(3).map(|&v| v)
        },
        252 => Instruction::Kill,
        253 => Instruction::Return,
        _ => Instruction::Unknown(opcode, operands.to_owned()),
    })
}

fn parse_string(data: &[u32]) -> (String, &[u32]) {
    let bytes = data.iter().flat_map(|&n| {
        let b1 = (n & 0xff) as u8;
        let b2 = ((n >> 8) & 0xff) as u8;
        let b3 = ((n >> 16) & 0xff) as u8;
        let b4 = ((n >> 24) & 0xff) as u8;
        vec![b1, b2, b3, b4].into_iter()
    }).take_while(|&b| b != 0).collect::<Vec<u8>>();

    let r = 1 + bytes.len() / 4;
    let s = String::from_utf8(bytes).expect("Shader content is not UTF-8");

    (s, &data[r..])
}

#[derive(Debug, Clone)]
pub enum Capability {
    Matrix,
    Shader,
    Geometry,
    Tessellation,
    Addresses,
    Linkage,
    Kernel,
    Vector16,
    Float16Buffer,
    Float16,
    Float64,
    Int64,
    Int64Atomics,
    ImageBasic,
    ImageReadWrite,
    ImageMipmap,
    Pipes,
    Groups,
    DeviceEnqueue,
    LiteralSampler,
    AtomicStorage,
    Int16,
    TessellationPointSize,
    GeometryPointSize,
    ImageGatherExtended,
    StorageImageMultisample,
    UniformBufferArrayDynamicIndexing,
    SampledImageArrayDynamicIndexing,
    StorageBufferArrayDynamicIndexing,
    StorageImageArrayDynamicIndexing,
    ClipDistance,
    CullDistance,
    ImageCubeArray,
    SampleRateShading,
    ImageRect,
    SampledRect,
    GenericPointer,
    Int8,
    InputAttachment,
    SparseResidency,
    MinLod,
    Sampled1D,
    Image1D,
    SampledCubeArray,
    SampledBuffer,
    ImageBuffer,
    ImageMSArray,
    StorageImageExtendedFormats,
    ImageQuery,
    DerivativeControl,
    InterpolationFunction,
    TransformFeedback,
    GeometryStreams,
    StorageImageReadWithoutFormat,
    StorageImageWriteWithoutFormat,
}

impl Capability {
    fn from_num(num: u32) -> Result<Capability, ParseError> {
        match num {
            0 => Ok(Capability::Matrix),
            1 => Ok(Capability::Shader),
            2 => Ok(Capability::Geometry),
            3 => Ok(Capability::Tessellation),
            4 => Ok(Capability::Addresses),
            5 => Ok(Capability::Linkage),
            6 => Ok(Capability::Kernel),
            7 => Ok(Capability::Vector16),
            8 => Ok(Capability::Float16Buffer),
            9 => Ok(Capability::Float16),
            10 => Ok(Capability::Float64),
            11 => Ok(Capability::Int64),
            12 => Ok(Capability::Int64Atomics),
            13 => Ok(Capability::ImageBasic),
            14 => Ok(Capability::ImageReadWrite),
            15 => Ok(Capability::ImageMipmap),
            17 => Ok(Capability::Pipes),
            18 => Ok(Capability::Groups),
            19 => Ok(Capability::DeviceEnqueue),
            20 => Ok(Capability::LiteralSampler),
            21 => Ok(Capability::AtomicStorage),
            22 => Ok(Capability::Int16),
            23 => Ok(Capability::TessellationPointSize),
            24 => Ok(Capability::GeometryPointSize),
            25 => Ok(Capability::ImageGatherExtended),
            27 => Ok(Capability::StorageImageMultisample),
            28 => Ok(Capability::UniformBufferArrayDynamicIndexing),
            29 => Ok(Capability::SampledImageArrayDynamicIndexing),
            30 => Ok(Capability::StorageBufferArrayDynamicIndexing),
            31 => Ok(Capability::StorageImageArrayDynamicIndexing),
            32 => Ok(Capability::ClipDistance),
            33 => Ok(Capability::CullDistance),
            34 => Ok(Capability::ImageCubeArray),
            35 => Ok(Capability::SampleRateShading),
            36 => Ok(Capability::ImageRect),
            37 => Ok(Capability::SampledRect),
            38 => Ok(Capability::GenericPointer),
            39 => Ok(Capability::Int8),
            40 => Ok(Capability::InputAttachment),
            41 => Ok(Capability::SparseResidency),
            42 => Ok(Capability::MinLod),
            43 => Ok(Capability::Sampled1D),
            44 => Ok(Capability::Image1D),
            45 => Ok(Capability::SampledCubeArray),
            46 => Ok(Capability::SampledBuffer),
            47 => Ok(Capability::ImageBuffer),
            48 => Ok(Capability::ImageMSArray),
            49 => Ok(Capability::StorageImageExtendedFormats),
            50 => Ok(Capability::ImageQuery),
            51 => Ok(Capability::DerivativeControl),
            52 => Ok(Capability::InterpolationFunction),
            53 => Ok(Capability::TransformFeedback),
            54 => Ok(Capability::GeometryStreams),
            55 => Ok(Capability::StorageImageReadWithoutFormat),
            56 => Ok(Capability::StorageImageWriteWithoutFormat),
            _ => Err(ParseError::UnknownCapability(num)),
        }
    }
}

#[derive(Debug, Clone)]
pub enum ExecutionModel {
    Vertex,
    TessellationControl,
    TessellationEvaluation,
    Geometry,
    Fragment,
    GLCompute,
    Kernel,
}

impl ExecutionModel {
    fn from_num(num: u32) -> Result<ExecutionModel, ParseError> {
        match num {
            0 => Ok(ExecutionModel::Vertex),
            1 => Ok(ExecutionModel::TessellationControl),
            2 => Ok(ExecutionModel::TessellationEvaluation),
            3 => Ok(ExecutionModel::Geometry),
            4 => Ok(ExecutionModel::Fragment),
            5 => Ok(ExecutionModel::GLCompute),
            6 => Ok(ExecutionModel::Kernel),
            _ => Err(ParseError::UnknownExecutionModel(num)),
        }
    }
}

#[derive(Debug, Clone)]
pub enum StorageClass {
    UniformConstant,
    Input,
    Uniform,
    Output,
    Workgroup,
    CrossWorkgroup,
    Private,
    Function,
    Generic,
    PushConstant,
    AtomicCounter,
    Image,
}

impl StorageClass {
    fn from_num(num: u32) -> Result<StorageClass, ParseError> {
        match num {
            0 => Ok(StorageClass::UniformConstant),
            1 => Ok(StorageClass::Input),
            2 => Ok(StorageClass::Uniform),
            3 => Ok(StorageClass::Output),
            4 => Ok(StorageClass::Workgroup),
            5 => Ok(StorageClass::CrossWorkgroup),
            6 => Ok(StorageClass::Private),
            7 => Ok(StorageClass::Function),
            8 => Ok(StorageClass::Generic),
            9 => Ok(StorageClass::PushConstant),
            10 => Ok(StorageClass::AtomicCounter),
            11 => Ok(StorageClass::Image),
            _ => Err(ParseError::UnknownStorageClass(num)),
        }
    }
}

#[derive(Debug, Clone)]
pub enum AddressingModel {
    Logical,
    Physical32,
    Physical64,
}

impl AddressingModel {
    fn from_num(num: u32) -> Result<AddressingModel, ParseError> {
        match num {
            0 => Ok(AddressingModel::Logical),
            1 => Ok(AddressingModel::Physical32),
            2 => Ok(AddressingModel::Physical64),
            _ => Err(ParseError::UnknownAddressingModel(num)),
        }
    }
}

#[derive(Debug, Clone)]
pub enum MemoryModel {
    Simple,
    Glsl450,
    OpenCL,
}

impl MemoryModel {
    fn from_num(num: u32) -> Result<MemoryModel, ParseError> {
        match num {
            0 => Ok(MemoryModel::Simple),
            1 => Ok(MemoryModel::Glsl450),
            2 => Ok(MemoryModel::OpenCL),
            _ => Err(ParseError::UnknownMemoryModel(num)),
        }
    }
}

#[cfg(test)]
mod test {
    use parse;

    #[test]
    fn test() {
        let data = include_bytes!("../examples/example.spv");
        println!("{:?}", parse::parse_spirv(data).unwrap());
    }
}
<|endoftext|>


<commit_before>//
// Copyright 2018 The Project Oak Authors
//
// 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 byteorder::{ReadBytesExt, WriteBytesExt};

use log::{debug, error};
use protobuf::ProtobufEnum;
use serde::{Deserialize, Serialize};

// Re-export ABI constants that are also visible as part of the SDK API.
pub use oak_abi::{ChannelReadStatus, OakStatus};

pub mod grpc;
pub mod io;
pub mod proto;
pub mod rand;
pub mod storage;
#[cfg(test)]
mod tests;

#[cfg(test)]
use assert_matches::assert_matches;
#[cfg(test)]
use serial_test_derive::serial;

/// Handle used to identify read or write channel halves.
///
/// These handles are used for all host function calls.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Handle {
    id: u64,
}

/// Serialize a `Handle` as the invalid handle value.
impl serde::Serialize for Handle {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        serializer.serialize_u64(oak_abi::INVALID_HANDLE)
    }
}

struct HandleVisitor;

/// Deserialize a `Handle` as the invalid handle value. Most likely, it will have been serialized to
/// an invalid handle in the first place anyways, but we are being extra cautions and even if the
/// serialized value was modified, we make sure that the resulting `Handle` is invalid.
impl<'de> serde::de::Visitor<'de> for HandleVisitor {
    type Value = Handle;
    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        formatter.write_str("a handle type")
    }

    fn visit_u64<E>(self, _v: u64) -> Result<Self::Value, E>
    where
        E: serde::de::Error,
    {
        Ok(Handle::invalid())
    }
}

impl<'de> serde::Deserialize<'de> for Handle {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        deserializer.deserialize_u64(HandleVisitor)
    }
}

impl Handle {
    /// When using the Oak SDK, this method should not need to be called directly
    /// as `Handles` are directly provided via functions such as `channel_create`.
    pub fn from_raw(id: u64) -> Handle {
        Handle { id }
    }

    /// Check this handle is valid.
    pub fn is_valid(self) -> bool {
        self.id != oak_abi::INVALID_HANDLE
    }

    /// Returns an intentionally invalid handle.
    pub fn invalid() -> Handle {
        Handle {
            id: oak_abi::INVALID_HANDLE,
        }
    }

    /// Pack a slice of `Handles` into the Wasm host ABI format.
    fn pack(handles: &[Handle]) -> Vec<u8> {
        let mut packed = Vec::with_capacity(handles.len() * 8);
        for handle in handles {
            packed
                .write_u64::<byteorder::LittleEndian>(handle.id)
                .unwrap();
        }
        packed
    }

    /// Unpack a slice of Handles from the Wasm host ABI format.
    fn unpack(bytes: &[u8], handle_count: u32, handles: &mut Vec<Handle>) {
        handles.clear();
        let mut reader = std::io::Cursor::new(bytes);
        for _ in 0..handle_count {
            handles.push(Handle {
                id: reader.read_u64::<byteorder::LittleEndian>().unwrap(),
            });
        }
    }
}

/// Wrapper for a handle to the read half of a channel.
///
/// For use when the underlying [`Handle`] is known to be for a receive half.
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub struct ReadHandle {
    pub handle: Handle,
}

/// Wrapper for a handle to the send half of a channel.
///
/// For use when the underlying [`Handle`] is known to be for a send half.
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub struct WriteHandle {
    pub handle: Handle,
}

// Build a chunk of memory that is suitable for passing to oak_abi::wait_on_channels,
// holding the given collection of channel handles.
fn new_handle_space(handles: &[ReadHandle]) -> Vec<u8> {
    let mut space = Vec::with_capacity(oak_abi::SPACE_BYTES_PER_HANDLE * handles.len());
    for handle in handles {
        space
            .write_u64::<byteorder::LittleEndian>(handle.handle.id)
            .unwrap();
        space.push(0x00);
    }
    space
}

// Prepare a handle space for polling by clearing all of the message-pending
// indicator bytes.
fn prep_handle_space(space: &mut [u8]) {
    let count = space.len() / 8;
    for i in 0..count {
        space[i * oak_abi::SPACE_BYTES_PER_HANDLE + (oak_abi::SPACE_BYTES_PER_HANDLE - 1)] = 0;
    }
}

/// Wait for one or more of the provided handles to become ready for reading
/// from.  On success, the returned vector of [`ChannelReadStatus`] values
/// will be in 1-1 correspondence with the passed-in vector of [`Handle`]s.
///
/// This is a convenience wrapper around the [`oak_abi::wait_on_channels`] host
/// function. This version is easier to use in Rust but is less efficient
/// (because the notification space is re-created on each invocation).
pub fn wait_on_channels(handles: &[ReadHandle]) -> Result<Vec<ChannelReadStatus>, OakStatus> {
    let mut space = new_handle_space(handles);
    unsafe {
        let status = oak_abi::wait_on_channels(space.as_mut_ptr(), handles.len() as u32);
        match OakStatus::from_i32(status as i32) {
            Some(OakStatus::OK) => (),
            Some(err) => return Err(err),
            None => return Err(OakStatus::OAK_STATUS_UNSPECIFIED),
        }
        let mut results = Vec::with_capacity(handles.len());
        for i in 0..handles.len() {
            match space
                .get(i * oak_abi::SPACE_BYTES_PER_HANDLE + (oak_abi::SPACE_BYTES_PER_HANDLE - 1))
                .cloned()
                .map(i32::from)
                .and_then(ChannelReadStatus::from_i32)
            {
                Some(status) => results.push(status),
                None => return Err(OakStatus::OAK_STATUS_UNSPECIFIED),
            }
        }
        Ok(results)
    }
}

/// Read a message from a channel.
///
/// The provided vectors for received data and associated handles will be
/// resized to accomodate the information in the message.
pub fn channel_read(half: ReadHandle, buf: &mut Vec<u8>, handles: &mut Vec<Handle>) -> OakStatus {
    // Try reading from the channel twice: first with provided vectors, then
    // with vectors that have been resized to meet size requirements.

    // We cannot deserialize directly into the handles vector because `Handle` may
    // not have the correct memory layout.
    let mut handles_buf = Vec::with_capacity(handles.capacity() * 8);
    for resized in &[false, true] {
        let mut actual_size: u32 = 0;
        let mut actual_handle_count: u32 = 0;
        let status = OakStatus::from_i32(unsafe {
            oak_abi::channel_read(
                half.handle.id,
                buf.as_mut_ptr(),
                buf.capacity(),
                &mut actual_size,
                handles_buf.as_mut_ptr(),
                handles_buf.capacity() as u32 / 8, // Handle count, not byte count
                &mut actual_handle_count,
            ) as i32
        });

        match status {
            Some(s) => match s {
                OakStatus::OK | OakStatus::ERR_CHANNEL_EMPTY => {
                    unsafe {
                        buf.set_len(actual_size as usize);

                        // actual_handle_count is number of handles not bytes
                        handles_buf.set_len(actual_handle_count as usize * 8);
                        Handle::unpack(&handles_buf, actual_handle_count, handles);
                    };
                    return s;
                }

                OakStatus::ERR_BUFFER_TOO_SMALL | OakStatus::ERR_HANDLE_SPACE_TOO_SMALL
                    if !(*resized) =>
                {
                    // Extend the vectors to be large enough for the message
                    debug!(
                        "Got space for {} bytes, need {}",
                        buf.capacity(),
                        actual_size
                    );
                    if (actual_size as usize) > buf.capacity() {
                        let extra = (actual_size as usize) - buf.len();
                        buf.reserve(extra);
                    }

                    let handles_capacity = handles_buf.capacity() / 8;
                    debug!(
                        "Got space for {} handles, need {}",
                        handles_capacity, actual_handle_count
                    );
                    if (actual_handle_count as usize) > handles_capacity {
                        let extra = (actual_handle_count as usize * 8) - handles_buf.len();
                        handles_buf.reserve(extra);
                    }

                    // Try again with a buffer resized to cope with expected size of data.
                    continue;
                }
                s => {
                    return s;
                }
            },
            None => {
                return OakStatus::ERR_INTERNAL;
            }
        }
    }
    error!("unreachable code reached");
    OakStatus::ERR_INTERNAL
}

/// Write a message to a channel.
pub fn channel_write(half: WriteHandle, buf: &[u8], handles: &[Handle]) -> OakStatus {
    let handle_buf = Handle::pack(handles);
    match OakStatus::from_i32(unsafe {
        oak_abi::channel_write(
            half.handle.id,
            buf.as_ptr(),
            buf.len(),
            handle_buf.as_ptr(),
            handles.len() as u32, // Number of handles, not bytes
        ) as i32
    }) {
        Some(s) => s,
        None => OakStatus::ERR_INTERNAL,
    }
}

/// Create a new unidirectional channel.
///
/// On success, returns [`WriteHandle`] and a [`ReadHandle`] values for the
/// write and read halves (respectively).
pub fn channel_create() -> Result<(WriteHandle, ReadHandle), OakStatus> {
    let mut write = WriteHandle {
        handle: Handle::invalid(),
    };
    let mut read = ReadHandle {
        handle: Handle::invalid(),
    };
    match OakStatus::from_i32(unsafe {
        oak_abi::channel_create(
            &mut write.handle.id as *mut u64,
            &mut read.handle.id as *mut u64,
        ) as i32
    }) {
        Some(OakStatus::OK) => Ok((write, read)),
        Some(err) => Err(err),
        None => Err(OakStatus::OAK_STATUS_UNSPECIFIED),
    }
}

/// Close the specified channel [`Handle`].
pub fn channel_close(handle: Handle) -> OakStatus {
    match OakStatus::from_i32(unsafe { oak_abi::channel_close(handle.id) as i32 }) {
        Some(s) => s,
        None => OakStatus::OAK_STATUS_UNSPECIFIED,
    }
}

/// Create a new Node running the configuration identified by `config_name`,
/// passing it the given handle.
pub fn node_create(config_name: &str, half: ReadHandle) -> OakStatus {
    match OakStatus::from_i32(unsafe {
        oak_abi::node_create(config_name.as_ptr(), config_name.len(), half.handle.id) as i32
    }) {
        Some(s) => s,
        None => OakStatus::OAK_STATUS_UNSPECIFIED,
    }
}

/// Fill a buffer with random data.
pub fn random_get(buf: &mut [u8]) -> OakStatus {
    match OakStatus::from_i32(unsafe { oak_abi::random_get(buf.as_mut_ptr(), buf.len()) as i32 }) {
        Some(s) => s,
        None => OakStatus::OAK_STATUS_UNSPECIFIED,
    }
}

/// Install a panic hook that logs [panic information].
///
/// Logs panic infomation to the logging channel, if one is set.
///
/// [panic information]: std::panic::PanicInfo
pub fn set_panic_hook() {
    std::panic::set_hook(Box::new(|panic_info| {
        let msg = panic_info
            .payload()
            .downcast_ref::<&str>()
            .unwrap_or(&"<UNKNOWN MESSAGE>");
        let (file, line) = match panic_info.location() {
            Some(location) => (location.file(), location.line()),
            None => ("<UNKNOWN FILE>", 0),
        };
        error!(
            "panic occurred in file '{}' at line {}: {}",
            file, line, msg
        );
    }));
}
<commit_msg>sdk: more comments for FFI vector manipulation<commit_after>//
// Copyright 2018 The Project Oak Authors
//
// 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 byteorder::{ReadBytesExt, WriteBytesExt};

use log::{debug, error};
use protobuf::ProtobufEnum;
use serde::{Deserialize, Serialize};

// Re-export ABI constants that are also visible as part of the SDK API.
pub use oak_abi::{ChannelReadStatus, OakStatus};

pub mod grpc;
pub mod io;
pub mod proto;
pub mod rand;
pub mod storage;
#[cfg(test)]
mod tests;

#[cfg(test)]
use assert_matches::assert_matches;
#[cfg(test)]
use serial_test_derive::serial;

/// Handle used to identify read or write channel halves.
///
/// These handles are used for all host function calls.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Handle {
    id: u64,
}

/// Serialize a `Handle` as the invalid handle value.
impl serde::Serialize for Handle {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        serializer.serialize_u64(oak_abi::INVALID_HANDLE)
    }
}

struct HandleVisitor;

/// Deserialize a `Handle` as the invalid handle value. Most likely, it will have been serialized to
/// an invalid handle in the first place anyways, but we are being extra cautions and even if the
/// serialized value was modified, we make sure that the resulting `Handle` is invalid.
impl<'de> serde::de::Visitor<'de> for HandleVisitor {
    type Value = Handle;
    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        formatter.write_str("a handle type")
    }

    fn visit_u64<E>(self, _v: u64) -> Result<Self::Value, E>
    where
        E: serde::de::Error,
    {
        Ok(Handle::invalid())
    }
}

impl<'de> serde::Deserialize<'de> for Handle {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        deserializer.deserialize_u64(HandleVisitor)
    }
}

impl Handle {
    /// When using the Oak SDK, this method should not need to be called directly
    /// as `Handles` are directly provided via functions such as `channel_create`.
    pub fn from_raw(id: u64) -> Handle {
        Handle { id }
    }

    /// Check this handle is valid.
    pub fn is_valid(self) -> bool {
        self.id != oak_abi::INVALID_HANDLE
    }

    /// Returns an intentionally invalid handle.
    pub fn invalid() -> Handle {
        Handle {
            id: oak_abi::INVALID_HANDLE,
        }
    }

    /// Pack a slice of `Handles` into the Wasm host ABI format.
    fn pack(handles: &[Handle]) -> Vec<u8> {
        let mut packed = Vec::with_capacity(handles.len() * 8);
        for handle in handles {
            packed
                .write_u64::<byteorder::LittleEndian>(handle.id)
                .unwrap();
        }
        packed
    }

    /// Unpack a slice of Handles from the Wasm host ABI format.
    fn unpack(bytes: &[u8], handle_count: u32, handles: &mut Vec<Handle>) {
        handles.clear();
        let mut reader = std::io::Cursor::new(bytes);
        for _ in 0..handle_count {
            handles.push(Handle {
                id: reader.read_u64::<byteorder::LittleEndian>().unwrap(),
            });
        }
    }
}

/// Wrapper for a handle to the read half of a channel.
///
/// For use when the underlying [`Handle`] is known to be for a receive half.
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub struct ReadHandle {
    pub handle: Handle,
}

/// Wrapper for a handle to the send half of a channel.
///
/// For use when the underlying [`Handle`] is known to be for a send half.
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub struct WriteHandle {
    pub handle: Handle,
}

// Build a chunk of memory that is suitable for passing to oak_abi::wait_on_channels,
// holding the given collection of channel handles.
fn new_handle_space(handles: &[ReadHandle]) -> Vec<u8> {
    let mut space = Vec::with_capacity(oak_abi::SPACE_BYTES_PER_HANDLE * handles.len());
    for handle in handles {
        space
            .write_u64::<byteorder::LittleEndian>(handle.handle.id)
            .unwrap();
        space.push(0x00);
    }
    space
}

// Prepare a handle space for polling by clearing all of the message-pending
// indicator bytes.
fn prep_handle_space(space: &mut [u8]) {
    let count = space.len() / 8;
    for i in 0..count {
        space[i * oak_abi::SPACE_BYTES_PER_HANDLE + (oak_abi::SPACE_BYTES_PER_HANDLE - 1)] = 0;
    }
}

/// Wait for one or more of the provided handles to become ready for reading
/// from.  On success, the returned vector of [`ChannelReadStatus`] values
/// will be in 1-1 correspondence with the passed-in vector of [`Handle`]s.
///
/// This is a convenience wrapper around the [`oak_abi::wait_on_channels`] host
/// function. This version is easier to use in Rust but is less efficient
/// (because the notification space is re-created on each invocation).
pub fn wait_on_channels(handles: &[ReadHandle]) -> Result<Vec<ChannelReadStatus>, OakStatus> {
    let mut space = new_handle_space(handles);
    unsafe {
        let status = oak_abi::wait_on_channels(space.as_mut_ptr(), handles.len() as u32);
        match OakStatus::from_i32(status as i32) {
            Some(OakStatus::OK) => (),
            Some(err) => return Err(err),
            None => return Err(OakStatus::OAK_STATUS_UNSPECIFIED),
        }
        let mut results = Vec::with_capacity(handles.len());
        for i in 0..handles.len() {
            match space
                .get(i * oak_abi::SPACE_BYTES_PER_HANDLE + (oak_abi::SPACE_BYTES_PER_HANDLE - 1))
                .cloned()
                .map(i32::from)
                .and_then(ChannelReadStatus::from_i32)
            {
                Some(status) => results.push(status),
                None => return Err(OakStatus::OAK_STATUS_UNSPECIFIED),
            }
        }
        Ok(results)
    }
}

/// Read a message from a channel.
///
/// The provided vectors for received data and associated handles will be
/// resized to accomodate the information in the message; any data already
/// held in the vectors will be overwritten.
pub fn channel_read(half: ReadHandle, buf: &mut Vec<u8>, handles: &mut Vec<Handle>) -> OakStatus {
    // Try reading from the channel twice: first with provided vectors, making
    // use of their available capacity, then with vectors whose capacity has
    // been extended to meet size requirements.

    // We cannot deserialize directly into the handles vector because `Handle`
    // may not have the correct memory layout, so create a separate buffer with
    // equivalent capacity.
    let mut handles_buf = Vec::with_capacity(handles.capacity() * 8);
    for resized in &[false, true] {
        let mut actual_size: u32 = 0;
        let mut actual_handle_count: u32 = 0;
        let status = OakStatus::from_i32(unsafe {
            oak_abi::channel_read(
                half.handle.id,
                buf.as_mut_ptr(),
                buf.capacity(),
                &mut actual_size,
                handles_buf.as_mut_ptr(),
                handles_buf.capacity() as u32 / 8, // Handle count, not byte count
                &mut actual_handle_count,
            ) as i32
        });

        match status {
            Some(s) => match s {
                OakStatus::OK | OakStatus::ERR_CHANNEL_EMPTY => {
                    unsafe {
                        // The read operation succeeded, and overwrote some fraction
                        // of the vectors' available capacity with returned data (possibly
                        // zero).  As the data is already present in the vectors, set
                        // their length to match what's available.
                        buf.set_len(actual_size as usize);

                        // actual_handle_count is number of handles not bytes
                        handles_buf.set_len(actual_handle_count as usize * 8);
                    }
                    Handle::unpack(&handles_buf, actual_handle_count, handles);
                    return s;
                }

                OakStatus::ERR_BUFFER_TOO_SMALL | OakStatus::ERR_HANDLE_SPACE_TOO_SMALL
                    if !(*resized) =>
                {
                    // Extend the vectors to be large enough for the message
                    debug!(
                        "Got space for {} bytes, need {}",
                        buf.capacity(),
                        actual_size
                    );
                    if (actual_size as usize) > buf.capacity() {
                        let extra = (actual_size as usize) - buf.len();
                        buf.reserve(extra);
                    }

                    let handles_capacity = handles_buf.capacity() / 8;
                    debug!(
                        "Got space for {} handles, need {}",
                        handles_capacity, actual_handle_count
                    );
                    if (actual_handle_count as usize) > handles_capacity {
                        let extra = (actual_handle_count as usize * 8) - handles_buf.len();
                        handles_buf.reserve(extra);
                    }

                    // Try again with a buffer resized to cope with expected size of data.
                    continue;
                }
                s => {
                    return s;
                }
            },
            None => {
                return OakStatus::ERR_INTERNAL;
            }
        }
    }
    error!("unreachable code reached");
    OakStatus::ERR_INTERNAL
}

/// Write a message to a channel.
pub fn channel_write(half: WriteHandle, buf: &[u8], handles: &[Handle]) -> OakStatus {
    let handle_buf = Handle::pack(handles);
    match OakStatus::from_i32(unsafe {
        oak_abi::channel_write(
            half.handle.id,
            buf.as_ptr(),
            buf.len(),
            handle_buf.as_ptr(),
            handles.len() as u32, // Number of handles, not bytes
        ) as i32
    }) {
        Some(s) => s,
        None => OakStatus::ERR_INTERNAL,
    }
}

/// Create a new unidirectional channel.
///
/// On success, returns [`WriteHandle`] and a [`ReadHandle`] values for the
/// write and read halves (respectively).
pub fn channel_create() -> Result<(WriteHandle, ReadHandle), OakStatus> {
    let mut write = WriteHandle {
        handle: Handle::invalid(),
    };
    let mut read = ReadHandle {
        handle: Handle::invalid(),
    };
    match OakStatus::from_i32(unsafe {
        oak_abi::channel_create(
            &mut write.handle.id as *mut u64,
            &mut read.handle.id as *mut u64,
        ) as i32
    }) {
        Some(OakStatus::OK) => Ok((write, read)),
        Some(err) => Err(err),
        None => Err(OakStatus::OAK_STATUS_UNSPECIFIED),
    }
}

/// Close the specified channel [`Handle`].
pub fn channel_close(handle: Handle) -> OakStatus {
    match OakStatus::from_i32(unsafe { oak_abi::channel_close(handle.id) as i32 }) {
        Some(s) => s,
        None => OakStatus::OAK_STATUS_UNSPECIFIED,
    }
}

/// Create a new Node running the configuration identified by `config_name`,
/// passing it the given handle.
pub fn node_create(config_name: &str, half: ReadHandle) -> OakStatus {
    match OakStatus::from_i32(unsafe {
        oak_abi::node_create(config_name.as_ptr(), config_name.len(), half.handle.id) as i32
    }) {
        Some(s) => s,
        None => OakStatus::OAK_STATUS_UNSPECIFIED,
    }
}

/// Fill a buffer with random data.
pub fn random_get(buf: &mut [u8]) -> OakStatus {
    match OakStatus::from_i32(unsafe { oak_abi::random_get(buf.as_mut_ptr(), buf.len()) as i32 }) {
        Some(s) => s,
        None => OakStatus::OAK_STATUS_UNSPECIFIED,
    }
}

/// Install a panic hook that logs [panic information].
///
/// Logs panic infomation to the logging channel, if one is set.
///
/// [panic information]: std::panic::PanicInfo
pub fn set_panic_hook() {
    std::panic::set_hook(Box::new(|panic_info| {
        let msg = panic_info
            .payload()
            .downcast_ref::<&str>()
            .unwrap_or(&"<UNKNOWN MESSAGE>");
        let (file, line) = match panic_info.location() {
            Some(location) => (location.file(), location.line()),
            None => ("<UNKNOWN FILE>", 0),
        };
        error!(
            "panic occurred in file '{}' at line {}: {}",
            file, line, msg
        );
    }));
}
<|endoftext|>


<commit_before>/// Deserialization

mod error;
mod maybeskipcomma;
mod msg_visitor;
mod pkt_deserializer;

<commit_msg>Make PktDeserializer public at mod level<commit_after>/// Deserialization

mod error;
mod maybeskipcomma;
mod msg_visitor;
mod pkt_deserializer;

pub use self::pkt_deserializer::PktDeserializer;

<|endoftext|>


<commit_before>//! Memory layout of matrices
//!
//! Different from ndarray format which consists of shape and strides,
//! matrix format in LAPACK consists of row or column size and leading dimension.
//!
//! ndarray format and stride
//! --------------------------
//!
//! Let us consider 3-dimensional array for explaining ndarray structure.
//! The address of `(x,y,z)`-element in ndarray satisfies following relation:
//!
//! ```text
//! shape = [Nx, Ny, Nz]
//!     where Nx > 0, Ny > 0, Nz > 0
//! stride = [Sx, Sy, Sz]
//!
//! &data[(x, y, z)] = &data[(0, 0, 0)] + Sx*x + Sy*y + Sz*z
//!     for x < Nx, y < Ny, z < Nz
//! ```
//!
//! The array is called
//!
//! - C-continuous if `[Sx, Sy, Sz] = [Nz*Ny, Nz, 1]`
//! - F(Fortran)-continuous if `[Sx, Sy, Sz] = [1, Nx, Nx*Ny]`
//!
//! Strides of ndarray `[Sx, Sy, Sz]` take arbitrary value,
//! e.g. it can be non-ordered `Sy > Sx > Sz`, or can be negative `Sx < 0`.
//! If the minimum of `[Sx, Sy, Sz]` equals to `1`,
//! the value of elements fills `data` memory region and called "continuous".
//! Non-continuous ndarray is useful to get sub-array without copying data.
//!
//! Matrix layout for LAPACK
//! -------------------------
//!
//! LAPACK interface focuses on the linear algebra operations for F-continuous 2-dimensional array.
//! Under this restriction, stride becomes far simpler; we only have to consider the case `[1, S]`
//! This `S` for a matrix `A` is called "leading dimension of the array A" in LAPACK document, and denoted by `lda`.
//!

pub type LDA = i32;
pub type LEN = i32;
pub type Col = i32;
pub type Row = i32;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MatrixLayout {
    C { row: i32, lda: i32 },
    F { col: i32, lda: i32 },
}

impl MatrixLayout {
    pub fn size(&self) -> (Row, Col) {
        match *self {
            MatrixLayout::C { row, lda } => (row, lda),
            MatrixLayout::F { col, lda } => (lda, col),
        }
    }

    pub fn resized(&self, row: Row, col: Col) -> MatrixLayout {
        match *self {
            MatrixLayout::C { .. } => MatrixLayout::C { row, lda: col },
            MatrixLayout::F { .. } => MatrixLayout::F { col, lda: row },
        }
    }

    pub fn lda(&self) -> LDA {
        std::cmp::max(
            1,
            match *self {
                MatrixLayout::C { lda, .. } | MatrixLayout::F { lda, .. } => lda,
            },
        )
    }

    pub fn len(&self) -> LEN {
        match *self {
            MatrixLayout::C { row, .. } => row,
            MatrixLayout::F { col, .. } => col,
        }
    }

    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    pub fn lapacke_layout(&self) -> lapacke::Layout {
        match *self {
            MatrixLayout::C { .. } => lapacke::Layout::RowMajor,
            MatrixLayout::F { .. } => lapacke::Layout::ColumnMajor,
        }
    }

    pub fn same_order(&self, other: &MatrixLayout) -> bool {
        self.lapacke_layout() == other.lapacke_layout()
    }

    pub fn toggle_order(&self) -> Self {
        match *self {
            MatrixLayout::C { row, lda } => MatrixLayout::F { lda: row, col: lda },
            MatrixLayout::F { col, lda } => MatrixLayout::C { row: lda, lda: col },
        }
    }
}
<commit_msg>Remove type rename<commit_after>//! Memory layout of matrices
//!
//! Different from ndarray format which consists of shape and strides,
//! matrix format in LAPACK consists of row or column size and leading dimension.
//!
//! ndarray format and stride
//! --------------------------
//!
//! Let us consider 3-dimensional array for explaining ndarray structure.
//! The address of `(x,y,z)`-element in ndarray satisfies following relation:
//!
//! ```text
//! shape = [Nx, Ny, Nz]
//!     where Nx > 0, Ny > 0, Nz > 0
//! stride = [Sx, Sy, Sz]
//!
//! &data[(x, y, z)] = &data[(0, 0, 0)] + Sx*x + Sy*y + Sz*z
//!     for x < Nx, y < Ny, z < Nz
//! ```
//!
//! The array is called
//!
//! - C-continuous if `[Sx, Sy, Sz] = [Nz*Ny, Nz, 1]`
//! - F(Fortran)-continuous if `[Sx, Sy, Sz] = [1, Nx, Nx*Ny]`
//!
//! Strides of ndarray `[Sx, Sy, Sz]` take arbitrary value,
//! e.g. it can be non-ordered `Sy > Sx > Sz`, or can be negative `Sx < 0`.
//! If the minimum of `[Sx, Sy, Sz]` equals to `1`,
//! the value of elements fills `data` memory region and called "continuous".
//! Non-continuous ndarray is useful to get sub-array without copying data.
//!
//! Matrix layout for LAPACK
//! -------------------------
//!
//! LAPACK interface focuses on the linear algebra operations for F-continuous 2-dimensional array.
//! Under this restriction, stride becomes far simpler; we only have to consider the case `[1, S]`
//! This `S` for a matrix `A` is called "leading dimension of the array A" in LAPACK document, and denoted by `lda`.
//!

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MatrixLayout {
    C { row: i32, lda: i32 },
    F { col: i32, lda: i32 },
}

impl MatrixLayout {
    pub fn size(&self) -> (i32, i32) {
        match *self {
            MatrixLayout::C { row, lda } => (row, lda),
            MatrixLayout::F { col, lda } => (lda, col),
        }
    }

    pub fn resized(&self, row: i32, col: i32) -> MatrixLayout {
        match *self {
            MatrixLayout::C { .. } => MatrixLayout::C { row, lda: col },
            MatrixLayout::F { .. } => MatrixLayout::F { col, lda: row },
        }
    }

    pub fn lda(&self) -> i32 {
        std::cmp::max(
            1,
            match *self {
                MatrixLayout::C { lda, .. } | MatrixLayout::F { lda, .. } => lda,
            },
        )
    }

    pub fn len(&self) -> i32 {
        match *self {
            MatrixLayout::C { row, .. } => row,
            MatrixLayout::F { col, .. } => col,
        }
    }

    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    pub fn lapacke_layout(&self) -> lapacke::Layout {
        match *self {
            MatrixLayout::C { .. } => lapacke::Layout::RowMajor,
            MatrixLayout::F { .. } => lapacke::Layout::ColumnMajor,
        }
    }

    pub fn same_order(&self, other: &MatrixLayout) -> bool {
        self.lapacke_layout() == other.lapacke_layout()
    }

    pub fn toggle_order(&self) -> Self {
        match *self {
            MatrixLayout::C { row, lda } => MatrixLayout::F { lda: row, col: lda },
            MatrixLayout::F { col, lda } => MatrixLayout::C { row: lda, lda: col },
        }
    }
}
<|endoftext|>


<commit_before>extern crate types;

use types::{
    ValidatorRecord,
    ValidatorStatus,
};

pub fn validator_is_active(v: &ValidatorRecord) -> bool {
    v.status == ValidatorStatus::Active as u8
}

/// Returns the indicies of each active validator in a given vec of validators.
pub fn active_validator_indices(validators: &[ValidatorRecord])
    -> Vec<usize>
{
    validators.iter()
        .enumerate()
        .filter_map(|(i, validator)| {
            if validator_is_active(&validator) {
                Some(i)
            } else {
                None
            }
        })
        .collect()
}
<commit_msg>Add test for utils/active-validators<commit_after>extern crate types;

use types::{
    ValidatorRecord,
    ValidatorStatus,
};

pub fn validator_is_active(v: &ValidatorRecord) -> bool {
    v.status == ValidatorStatus::Active as u8
}

/// Returns the indicies of each active validator in a given vec of validators.
pub fn active_validator_indices(validators: &[ValidatorRecord])
    -> Vec<usize>
{
    validators.iter()
        .enumerate()
        .filter_map(|(i, validator)| {
            if validator_is_active(&validator) {
                Some(i)
            } else {
                None
            }
        })
        .collect()
}

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

    #[test]
    fn test_active_validator() {
        let mut validators = vec![];

        let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair();
        v.status = ValidatorStatus::Active as u8;
        assert!(validator_is_active(&v));
        validators.push(v);

        let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair();
        v.status = ValidatorStatus::PendingActivation as u8;
        assert!(!validator_is_active(&v));
        validators.push(v);

        let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair();
        v.status = ValidatorStatus::PendingExit as u8;
        assert!(!validator_is_active(&v));
        validators.push(v);

        let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair();
        v.status = ValidatorStatus::PendingWithdraw as u8;
        assert!(!validator_is_active(&v));
        validators.push(v);

        let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair();
        v.status = ValidatorStatus::Withdrawn as u8;
        assert!(!validator_is_active(&v));
        validators.push(v);

        let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair();
        v.status = ValidatorStatus::Penalized as u8;
        assert!(!validator_is_active(&v));
        validators.push(v);

        assert_eq!(active_validator_indices(&validators), vec![0]);
    }
}
<|endoftext|>


<commit_before>use super::event_manager::*;
use glium::glutin::{ElementState, Event, VirtualKeyCode};
use std::f32::consts;
use base::math::*;

#[derive(Debug)]
pub struct DayTime {
    time_year: u32,
    time_day: u32,
    time_on_day: f32,
    speed: f32,
}

// `DEFAULT_TIME_SPEED` is 1.0 so the time goes at normal speed
// `PLUS_TIME_SPEED` is the factor with which the time is sped up, when the
// speed-up key is pressed
const DEFAULT_TIME_SPEED: f32 = 1.0;
const PLUS_TIME_SPEED: f32 = 200.0;

impl Default for DayTime {
    fn default() -> DayTime {
        DayTime {
            time_year: 0,
            time_day: 0,
            time_on_day: 0.0,
            speed: DEFAULT_TIME_SPEED,
        }
    }
}

const DAY_LENGTH: f32 = 720.0;
const YEAR_LENGTH: u32 = 12;

const MONTH_DIFFERENCE: f32 = 3.1415 / 18.0;

impl DayTime {
    pub fn set_time(&mut self, time_year: u32, time_day: u32, time_on_day: f32) {
        self.time_year = time_year;
        self.time_day = time_day;
        self.time_on_day = time_on_day;
        self.speed = DEFAULT_TIME_SPEED;
    }

    pub fn get_time_year(&self) -> u32 {
        self.time_year
    }

    pub fn get_time_day(&self) -> u32 {
        self.time_day
    }

    pub fn get_time_on_day(&self) -> f32 {
        self.time_on_day
    }

    /// Updates time with the use of `delta` as additionally passed time
    /// `DAY_LENGTH` defines the length of a day in real-life seconds
    /// `YEAR_LENGTH` defines the length of a year in `DAY_LENGTH`s
    pub fn update(&mut self, delta: f32) {
        // Output of Time
        debug!("Year: {} Day: {} Time: {}",
               self.time_year,
               self.time_day,
               self.time_on_day);

        // Checks if one day has passed
        self.time_on_day += delta * self.speed;
        if (self.time_on_day) >= DAY_LENGTH {
            self.time_on_day -= DAY_LENGTH; // Removes one day from time_on_day
            self.time_day += 1;
            if (self.time_day) >= YEAR_LENGTH {
                self.time_day = 0;
                self.time_year += 1;
            }
        }

    }

    /// returns the position of the sun corresponding to time
    /// only mid summer
    pub fn get_sun_position(&self) -> Vector3f {
        // 0 degrees for mid summer
        // 60 degrees for hard winter
        let half_day = DAY_LENGTH as f32 / 2.0;

        let theta;
        let phi;
        if self.time_on_day < half_day {
            // pre noon
            // sun rising
            theta = consts::PI - consts::PI * (self.time_on_day / half_day);
            phi = 0.0;
        } else {
            // after noon
            // sun going down
            theta = consts::PI * ((self.time_on_day - half_day) / half_day);
            phi = consts::PI;
        }

        // for debugging
        // info!("THETA: {} PHI: {}", theta, phi);

        // returns sun position in cartesian coordinates
        Vector3f::new(theta.sin() * phi.cos(),
                      theta.sin() * phi.sin(),
                      theta.cos())

    }

    /// returns the position of the sun corresponding to time
    /// does not work right now
    pub fn get_sun_position_real(&self) -> Vector3f {
        // 0 degrees for mid summer
        // 60 degrees for hard winter
        let half_year = YEAR_LENGTH as f32 / 2.0;
        let half_day = DAY_LENGTH as f32 / 2.0;

        let mut theta = self.time_day as f32 - half_year;
        if theta < 0.0 {
            theta *= -1.0;
        }
        // theta now is the day difference from the highest day, sunposition-wise
        theta *= MONTH_DIFFERENCE;

        if self.time_on_day < half_day {
            // pre noon
            // sun rising
            theta += consts::PI - consts::PI * (self.time_on_day / half_day)
        } else {
            // after noon
            // sun going down
            theta += consts::PI * ((self.time_on_day - half_day) / half_day)
        }

        let phi = self.time_on_day / DAY_LENGTH as f32 * 2.0 * consts::PI;

        // for debugging
        // info!("THETA: {} PHI: {}", theta, phi);

        // returns sun position in cartesian coordinates
        Vector3f::new(theta.sin() * phi.cos(),
                      theta.sin() * phi.sin(),
                      theta.cos())

    }

    /// returns the Vector3f for the directional sunlight
    pub fn get_sun_light_vector(&self) -> Vector3f {
        Vector3f::new(0.0, 0.0, 0.0) - self.get_sun_position()
    }
}

/// Handler to speed up time with use of '+' key
impl EventHandler for DayTime {
    fn handle_event(&mut self, e: &Event) -> EventResponse {
        match *e {
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::Add)) => {
                self.speed = PLUS_TIME_SPEED;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::Add)) => {
                self.speed = DEFAULT_TIME_SPEED;
                EventResponse::Continue
            }
            _ => EventResponse::NotHandled,
        }

    }
}
<commit_msg>improve get_sun_position function in daytime<commit_after>use super::event_manager::*;
use glium::glutin::{ElementState, Event, VirtualKeyCode};
use std::f32::consts;
use base::math::*;

#[derive(Debug)]
pub struct DayTime {
    time_year: u32,
    time_day: u32,
    time_on_day: f32,
    speed: f32,
}

// `DEFAULT_TIME_SPEED` is 1.0 so the time goes at normal speed
// `PLUS_TIME_SPEED` is the factor with which the time is sped up, when the
// speed-up key is pressed
const DEFAULT_TIME_SPEED: f32 = 1.0;
const PLUS_TIME_SPEED: f32 = 200.0;

impl Default for DayTime {
    fn default() -> DayTime {
        DayTime {
            time_year: 0,
            time_day: 0,
            time_on_day: 0.0,
            speed: DEFAULT_TIME_SPEED,
        }
    }
}

const DAY_LENGTH: f32 = 720.0;
const YEAR_LENGTH: u32 = 12;

// lengthens the day by a static offset
const DAY_LENGTHER: f32 = 0.0;

impl DayTime {
    pub fn set_time(&mut self, time_year: u32, time_day: u32, time_on_day: f32) {
        self.time_year = time_year;
        self.time_day = time_day;
        self.time_on_day = time_on_day;
        self.speed = DEFAULT_TIME_SPEED;
    }

    pub fn get_time_year(&self) -> u32 {
        self.time_year
    }

    pub fn get_time_day(&self) -> u32 {
        self.time_day
    }

    pub fn get_time_on_day(&self) -> f32 {
        self.time_on_day
    }

    /// Updates time with the use of `delta` as additionally passed time
    /// `DAY_LENGTH` defines the length of a day in real-life seconds
    /// `YEAR_LENGTH` defines the length of a year in `DAY_LENGTH`s
    pub fn update(&mut self, delta: f32) {
        // Output of Time
        debug!("Year: {} Day: {} Time: {}",
               self.time_year,
               self.time_day,
               self.time_on_day);

        // Checks if one day has passed
        self.time_on_day += delta * self.speed;
        if (self.time_on_day) >= DAY_LENGTH {
            self.time_on_day -= DAY_LENGTH; // Removes one day from time_on_day
            self.time_day += 1;
            if (self.time_day) >= YEAR_LENGTH {
                self.time_day = 0;
                self.time_year += 1;
            }
        }

    }

    /// returns the position of the sun corresponding to time
    /// only mid summer
    pub fn get_sun_position(&self) -> Vector3f {

        let half_year = YEAR_LENGTH as f32 / 2.0; //
        let half_day = DAY_LENGTH as f32 / 2.0;

        let theta;
        let phi;

        let mut month_diff = self.time_day as f32 - half_year;//
        if month_diff < 0.0 {
            //
            month_diff *= -1.0//
        }//

        if self.time_on_day < half_day {
            // pre noon
            // sun rising
            theta = consts::PI - consts::PI * (self.time_on_day / half_day);
            phi = 0.0;
        } else {
            // after noon
            // sun going down
            theta = consts::PI * ((self.time_on_day - half_day) / half_day);
            phi = consts::PI;
        }

        // for debugging
        // info!("THETA: {} PHI: {}", theta, phi);

        // returns sun position in cartesian coordinates
        // uses `YEAR_LENGTH` and the current day of the year (month) to influence the
        // path the sun moves on
        Vector3f::new(theta.sin() * phi.cos(),
                      theta.sin() * phi.sin() + month_diff / (0.75 * YEAR_LENGTH as f32),
                      theta.cos() - month_diff / (0.75 * YEAR_LENGTH as f32) + DAY_LENGTHER)
            .normalize()

    }

    /// returns the Vector3f for the directional sunlight
    pub fn get_sun_light_vector(&self) -> Vector3f {
        Vector3f::new(0.0, 0.0, 0.0) - self.get_sun_position()
    }
}

/// Handler to speed up time with use of '+' key
impl EventHandler for DayTime {
    fn handle_event(&mut self, e: &Event) -> EventResponse {
        match *e {
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::Add)) => {
                self.speed = PLUS_TIME_SPEED;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::Add)) => {
                self.speed = DEFAULT_TIME_SPEED;
                EventResponse::Continue
            }
            _ => EventResponse::NotHandled,
        }

    }
}
<|endoftext|>


<commit_before>extern crate protoc;

fn main() {
    protoc::run(protoc::Args {
        lang: "rust",
        out_dir: "src",
        plugin: Some("../../target/debug/protoc-gen-rust"),
        input: &["src/data.proto"],
        ..Default::default()
    }).expect("protoc");
}
<commit_msg>Fix test-protoc on Windows<commit_after>extern crate protoc;

use std::path::PathBuf;

fn main() {
    let exe_suffix = if cfg!(windows) {
        ".exe"
    } else if cfg!(unix) {
        ""
    } else {
        panic!("unknown OS")
    };

    let protoc_gen_rust = PathBuf::from(format!("../../target/debug/protoc-gen-rust{}", exe_suffix));
    let protoc_gen_rust = protoc_gen_rust.canonicalize().expect("canonicalize");

    assert!(protoc_gen_rust.is_file(), "{:?}", protoc_gen_rust);

    protoc::run(protoc::Args {
        lang: "rust",
        out_dir: "src",
        plugin: Some(&format!("protoc-gen-rust={}", protoc_gen_rust.as_os_str().to_str().unwrap())),
        input: &["src/data.proto"],
        ..Default::default()
    }).expect("protoc");
}
<|endoftext|>


<commit_before>use crate::consts::{constant, Constant};
use if_chain::if_chain;
use rustc_ast::ast::RangeLimits;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Spanned;
use std::cmp::Ordering;

use crate::utils::sugg::Sugg;
use crate::utils::{get_parent_expr, is_integer_const, snippet, snippet_opt, span_lint, span_lint_and_then};
use crate::utils::{higher, SpanlessEq};

declare_clippy_lint! {
    /// **What it does:** Checks for zipping a collection with the range of
    /// `0.._.len()`.
    ///
    /// **Why is this bad?** The code is better expressed with `.enumerate()`.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// # let x = vec![1];
    /// x.iter().zip(0..x.len());
    /// ```
    /// Could be written as
    /// ```rust
    /// # let x = vec![1];
    /// x.iter().enumerate();
    /// ```
    pub RANGE_ZIP_WITH_LEN,
    complexity,
    "zipping iterator with a range when `enumerate()` would do"
}

declare_clippy_lint! {
    /// **What it does:** Checks for exclusive ranges where 1 is added to the
    /// upper bound, e.g., `x..(y+1)`.
    ///
    /// **Why is this bad?** The code is more readable with an inclusive range
    /// like `x..=y`.
    ///
    /// **Known problems:** Will add unnecessary pair of parentheses when the
    /// expression is not wrapped in a pair but starts with a opening parenthesis
    /// and ends with a closing one.
    /// I.e., `let _ = (f()+1)..(f()+1)` results in `let _ = ((f()+1)..=f())`.
    ///
    /// Also in many cases, inclusive ranges are still slower to run than
    /// exclusive ranges, because they essentially add an extra branch that
    /// LLVM may fail to hoist out of the loop.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// for x..(y+1) { .. }
    /// ```
    /// Could be written as
    /// ```rust,ignore
    /// for x..=y { .. }
    /// ```
    pub RANGE_PLUS_ONE,
    pedantic,
    "`x..(y+1)` reads better as `x..=y`"
}

declare_clippy_lint! {
    /// **What it does:** Checks for inclusive ranges where 1 is subtracted from
    /// the upper bound, e.g., `x..=(y-1)`.
    ///
    /// **Why is this bad?** The code is more readable with an exclusive range
    /// like `x..y`.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// for x..=(y-1) { .. }
    /// ```
    /// Could be written as
    /// ```rust,ignore
    /// for x..y { .. }
    /// ```
    pub RANGE_MINUS_ONE,
    complexity,
    "`x..=(y-1)` reads better as `x..y`"
}

declare_clippy_lint! {
    /// **What it does:** Checks for range expressions `x..y` where both `x` and `y`
    /// are constant and `x` is greater or equal to `y`.
    ///
    /// **Why is this bad?** Empty ranges yield no values so iterating them is a no-op.
    /// Moreover, trying to use a reversed range to index a slice will panic at run-time.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust,no_run
    /// fn main() {
    ///     (10..=0).for_each(|x| println!("{}", x));
    ///
    ///     let arr = [1, 2, 3, 4, 5];
    ///     let sub = &arr[3..1];
    /// }
    /// ```
    /// Use instead:
    /// ```rust
    /// fn main() {
    ///     (0..=10).rev().for_each(|x| println!("{}", x));
    ///
    ///     let arr = [1, 2, 3, 4, 5];
    ///     let sub = &arr[1..3];
    /// }
    /// ```
    pub REVERSED_EMPTY_RANGES,
    correctness,
    "reversing the limits of range expressions, resulting in empty ranges"
}

declare_lint_pass!(Ranges => [
    RANGE_ZIP_WITH_LEN,
    RANGE_PLUS_ONE,
    RANGE_MINUS_ONE,
    REVERSED_EMPTY_RANGES,
]);

impl<'tcx> LateLintPass<'tcx> for Ranges {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind {
            let name = path.ident.as_str();
            if name == "zip" && args.len() == 2 {
                let iter = &args[0].kind;
                let zip_arg = &args[1];
                if_chain! {
                    // `.iter()` call
                    if let ExprKind::MethodCall(ref iter_path, _, ref iter_args , _) = *iter;
                    if iter_path.ident.name == sym!(iter);
                    // range expression in `.zip()` call: `0..x.len()`
                    if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(cx, zip_arg);
                    if is_integer_const(cx, start, 0);
                    // `.len()` call
                    if let ExprKind::MethodCall(ref len_path, _, ref len_args, _) = end.kind;
                    if len_path.ident.name == sym!(len) && len_args.len() == 1;
                    // `.iter()` and `.len()` called on same `Path`
                    if let ExprKind::Path(QPath::Resolved(_, ref iter_path)) = iter_args[0].kind;
                    if let ExprKind::Path(QPath::Resolved(_, ref len_path)) = len_args[0].kind;
                    if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments);
                     then {
                         span_lint(cx,
                                   RANGE_ZIP_WITH_LEN,
                                   expr.span,
                                   &format!("It is more idiomatic to use `{}.iter().enumerate()`",
                                            snippet(cx, iter_args[0].span, "_")));
                    }
                }
            }
        }

        check_exclusive_range_plus_one(cx, expr);
        check_inclusive_range_minus_one(cx, expr);
        check_reversed_empty_range(cx, expr);
    }
}

// exclusive range plus one: `x..(y+1)`
fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
    if_chain! {
        if let Some(higher::Range {
            start,
            end: Some(end),
            limits: RangeLimits::HalfOpen
        }) = higher::range(cx, expr);
        if let Some(y) = y_plus_one(cx, end);
        then {
            let span = if expr.span.from_expansion() {
                expr.span
                    .ctxt()
                    .outer_expn_data()
                    .call_site
            } else {
                expr.span
            };
            span_lint_and_then(
                cx,
                RANGE_PLUS_ONE,
                span,
                "an inclusive range would be more readable",
                |diag| {
                    let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").to_string());
                    let end = Sugg::hir(cx, y, "y");
                    if let Some(is_wrapped) = &snippet_opt(cx, span) {
                        if is_wrapped.starts_with('(') && is_wrapped.ends_with(')') {
                            diag.span_suggestion(
                                span,
                                "use",
                                format!("({}..={})", start, end),
                                Applicability::MaybeIncorrect,
                            );
                        } else {
                            diag.span_suggestion(
                                span,
                                "use",
                                format!("{}..={}", start, end),
                                Applicability::MachineApplicable, // snippet
                            );
                        }
                    }
                },
            );
        }
    }
}

// inclusive range minus one: `x..=(y-1)`
fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
    if_chain! {
        if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::Closed }) = higher::range(cx, expr);
        if let Some(y) = y_minus_one(cx, end);
        then {
            span_lint_and_then(
                cx,
                RANGE_MINUS_ONE,
                expr.span,
                "an exclusive range would be more readable",
                |diag| {
                    let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").to_string());
                    let end = Sugg::hir(cx, y, "y");
                    diag.span_suggestion(
                        expr.span,
                        "use",
                        format!("{}..{}", start, end),
                        Applicability::MachineApplicable, // snippet
                    );
                },
            );
        }
    }
}

fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) {
    fn inside_indexing_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
        matches!(
            get_parent_expr(cx, expr),
            Some(Expr {
                kind: ExprKind::Index(..),
                ..
            })
        )
    }

    fn is_for_loop_arg(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
        let mut cur_expr = expr;
        while let Some(parent_expr) = get_parent_expr(cx, cur_expr) {
            match higher::for_loop(parent_expr) {
                Some((_, args, _)) if args.hir_id == expr.hir_id => return true,
                _ => cur_expr = parent_expr,
            }
        }

        false
    }

    fn is_empty_range(limits: RangeLimits, ordering: Ordering) -> bool {
        match limits {
            RangeLimits::HalfOpen => ordering != Ordering::Less,
            RangeLimits::Closed => ordering == Ordering::Greater,
        }
    }

    if_chain! {
        if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::range(cx, expr);
        let ty = cx.tables().expr_ty(start);
        if let ty::Int(_) | ty::Uint(_) = ty.kind;
        if let Some((start_idx, _)) = constant(cx, cx.tables(), start);
        if let Some((end_idx, _)) = constant(cx, cx.tables(), end);
        if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx);
        if is_empty_range(limits, ordering);
        then {
            if inside_indexing_expr(cx, expr) {
                // Avoid linting `N..N` as it has proven to be useful, see #5689 and #5628 ...
                if ordering != Ordering::Equal {
                    span_lint(
                        cx,
                        REVERSED_EMPTY_RANGES,
                        expr.span,
                        "this range is reversed and using it to index a slice will panic at run-time",
                    );
                }
            // ... except in for loop arguments for backwards compatibility with `reverse_range_loop`
            } else if ordering != Ordering::Equal || is_for_loop_arg(cx, expr) {
                span_lint_and_then(
                    cx,
                    REVERSED_EMPTY_RANGES,
                    expr.span,
                    "this range is empty so it will yield no values",
                    |diag| {
                        if ordering != Ordering::Equal {
                            let start_snippet = snippet(cx, start.span, "_");
                            let end_snippet = snippet(cx, end.span, "_");
                            let dots = match limits {
                                RangeLimits::HalfOpen => "..",
                                RangeLimits::Closed => "..="
                            };

                            diag.span_suggestion(
                                expr.span,
                                "consider using the following if you are attempting to iterate over this \
                                 range in reverse",
                                format!("({}{}{}).rev()", end_snippet, dots, start_snippet),
                                Applicability::MaybeIncorrect,
                            );
                        }
                    },
                );
            }
        }
    }
}

fn y_plus_one<'t>(cx: &LateContext<'_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'t>> {
    match expr.kind {
        ExprKind::Binary(
            Spanned {
                node: BinOpKind::Add, ..
            },
            ref lhs,
            ref rhs,
        ) => {
            if is_integer_const(cx, lhs, 1) {
                Some(rhs)
            } else if is_integer_const(cx, rhs, 1) {
                Some(lhs)
            } else {
                None
            }
        },
        _ => None,
    }
}

fn y_minus_one<'t>(cx: &LateContext<'_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'t>> {
    match expr.kind {
        ExprKind::Binary(
            Spanned {
                node: BinOpKind::Sub, ..
            },
            ref lhs,
            ref rhs,
        ) if is_integer_const(cx, rhs, 1) => Some(lhs),
        _ => None,
    }
}
<commit_msg>Move range_minus_one to pedantic<commit_after>use crate::consts::{constant, Constant};
use if_chain::if_chain;
use rustc_ast::ast::RangeLimits;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Spanned;
use std::cmp::Ordering;

use crate::utils::sugg::Sugg;
use crate::utils::{get_parent_expr, is_integer_const, snippet, snippet_opt, span_lint, span_lint_and_then};
use crate::utils::{higher, SpanlessEq};

declare_clippy_lint! {
    /// **What it does:** Checks for zipping a collection with the range of
    /// `0.._.len()`.
    ///
    /// **Why is this bad?** The code is better expressed with `.enumerate()`.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// # let x = vec![1];
    /// x.iter().zip(0..x.len());
    /// ```
    /// Could be written as
    /// ```rust
    /// # let x = vec![1];
    /// x.iter().enumerate();
    /// ```
    pub RANGE_ZIP_WITH_LEN,
    complexity,
    "zipping iterator with a range when `enumerate()` would do"
}

declare_clippy_lint! {
    /// **What it does:** Checks for exclusive ranges where 1 is added to the
    /// upper bound, e.g., `x..(y+1)`.
    ///
    /// **Why is this bad?** The code is more readable with an inclusive range
    /// like `x..=y`.
    ///
    /// **Known problems:** Will add unnecessary pair of parentheses when the
    /// expression is not wrapped in a pair but starts with a opening parenthesis
    /// and ends with a closing one.
    /// I.e., `let _ = (f()+1)..(f()+1)` results in `let _ = ((f()+1)..=f())`.
    ///
    /// Also in many cases, inclusive ranges are still slower to run than
    /// exclusive ranges, because they essentially add an extra branch that
    /// LLVM may fail to hoist out of the loop.
    ///
    /// This will cause a warning that cannot be fixed if the consumer of the
    /// range only accepts a specific range type, instead of the generic
    /// `RangeBounds` trait
    /// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).
    ///
    /// **Example:**
    /// ```rust,ignore
    /// for x..(y+1) { .. }
    /// ```
    /// Could be written as
    /// ```rust,ignore
    /// for x..=y { .. }
    /// ```
    pub RANGE_PLUS_ONE,
    pedantic,
    "`x..(y+1)` reads better as `x..=y`"
}

declare_clippy_lint! {
    /// **What it does:** Checks for inclusive ranges where 1 is subtracted from
    /// the upper bound, e.g., `x..=(y-1)`.
    ///
    /// **Why is this bad?** The code is more readable with an exclusive range
    /// like `x..y`.
    ///
    /// **Known problems:** This will cause a warning that cannot be fixed if
    /// the consumer of the range only accepts a specific range type, instead of
    /// the generic `RangeBounds` trait
    /// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).
    ///
    /// **Example:**
    /// ```rust,ignore
    /// for x..=(y-1) { .. }
    /// ```
    /// Could be written as
    /// ```rust,ignore
    /// for x..y { .. }
    /// ```
    pub RANGE_MINUS_ONE,
    pedantic,
    "`x..=(y-1)` reads better as `x..y`"
}

declare_clippy_lint! {
    /// **What it does:** Checks for range expressions `x..y` where both `x` and `y`
    /// are constant and `x` is greater or equal to `y`.
    ///
    /// **Why is this bad?** Empty ranges yield no values so iterating them is a no-op.
    /// Moreover, trying to use a reversed range to index a slice will panic at run-time.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust,no_run
    /// fn main() {
    ///     (10..=0).for_each(|x| println!("{}", x));
    ///
    ///     let arr = [1, 2, 3, 4, 5];
    ///     let sub = &arr[3..1];
    /// }
    /// ```
    /// Use instead:
    /// ```rust
    /// fn main() {
    ///     (0..=10).rev().for_each(|x| println!("{}", x));
    ///
    ///     let arr = [1, 2, 3, 4, 5];
    ///     let sub = &arr[1..3];
    /// }
    /// ```
    pub REVERSED_EMPTY_RANGES,
    correctness,
    "reversing the limits of range expressions, resulting in empty ranges"
}

declare_lint_pass!(Ranges => [
    RANGE_ZIP_WITH_LEN,
    RANGE_PLUS_ONE,
    RANGE_MINUS_ONE,
    REVERSED_EMPTY_RANGES,
]);

impl<'tcx> LateLintPass<'tcx> for Ranges {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind {
            let name = path.ident.as_str();
            if name == "zip" && args.len() == 2 {
                let iter = &args[0].kind;
                let zip_arg = &args[1];
                if_chain! {
                    // `.iter()` call
                    if let ExprKind::MethodCall(ref iter_path, _, ref iter_args , _) = *iter;
                    if iter_path.ident.name == sym!(iter);
                    // range expression in `.zip()` call: `0..x.len()`
                    if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(cx, zip_arg);
                    if is_integer_const(cx, start, 0);
                    // `.len()` call
                    if let ExprKind::MethodCall(ref len_path, _, ref len_args, _) = end.kind;
                    if len_path.ident.name == sym!(len) && len_args.len() == 1;
                    // `.iter()` and `.len()` called on same `Path`
                    if let ExprKind::Path(QPath::Resolved(_, ref iter_path)) = iter_args[0].kind;
                    if let ExprKind::Path(QPath::Resolved(_, ref len_path)) = len_args[0].kind;
                    if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments);
                     then {
                         span_lint(cx,
                                   RANGE_ZIP_WITH_LEN,
                                   expr.span,
                                   &format!("It is more idiomatic to use `{}.iter().enumerate()`",
                                            snippet(cx, iter_args[0].span, "_")));
                    }
                }
            }
        }

        check_exclusive_range_plus_one(cx, expr);
        check_inclusive_range_minus_one(cx, expr);
        check_reversed_empty_range(cx, expr);
    }
}

// exclusive range plus one: `x..(y+1)`
fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
    if_chain! {
        if let Some(higher::Range {
            start,
            end: Some(end),
            limits: RangeLimits::HalfOpen
        }) = higher::range(cx, expr);
        if let Some(y) = y_plus_one(cx, end);
        then {
            let span = if expr.span.from_expansion() {
                expr.span
                    .ctxt()
                    .outer_expn_data()
                    .call_site
            } else {
                expr.span
            };
            span_lint_and_then(
                cx,
                RANGE_PLUS_ONE,
                span,
                "an inclusive range would be more readable",
                |diag| {
                    let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").to_string());
                    let end = Sugg::hir(cx, y, "y");
                    if let Some(is_wrapped) = &snippet_opt(cx, span) {
                        if is_wrapped.starts_with('(') && is_wrapped.ends_with(')') {
                            diag.span_suggestion(
                                span,
                                "use",
                                format!("({}..={})", start, end),
                                Applicability::MaybeIncorrect,
                            );
                        } else {
                            diag.span_suggestion(
                                span,
                                "use",
                                format!("{}..={}", start, end),
                                Applicability::MachineApplicable, // snippet
                            );
                        }
                    }
                },
            );
        }
    }
}

// inclusive range minus one: `x..=(y-1)`
fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
    if_chain! {
        if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::Closed }) = higher::range(cx, expr);
        if let Some(y) = y_minus_one(cx, end);
        then {
            span_lint_and_then(
                cx,
                RANGE_MINUS_ONE,
                expr.span,
                "an exclusive range would be more readable",
                |diag| {
                    let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").to_string());
                    let end = Sugg::hir(cx, y, "y");
                    diag.span_suggestion(
                        expr.span,
                        "use",
                        format!("{}..{}", start, end),
                        Applicability::MachineApplicable, // snippet
                    );
                },
            );
        }
    }
}

fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) {
    fn inside_indexing_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
        matches!(
            get_parent_expr(cx, expr),
            Some(Expr {
                kind: ExprKind::Index(..),
                ..
            })
        )
    }

    fn is_for_loop_arg(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
        let mut cur_expr = expr;
        while let Some(parent_expr) = get_parent_expr(cx, cur_expr) {
            match higher::for_loop(parent_expr) {
                Some((_, args, _)) if args.hir_id == expr.hir_id => return true,
                _ => cur_expr = parent_expr,
            }
        }

        false
    }

    fn is_empty_range(limits: RangeLimits, ordering: Ordering) -> bool {
        match limits {
            RangeLimits::HalfOpen => ordering != Ordering::Less,
            RangeLimits::Closed => ordering == Ordering::Greater,
        }
    }

    if_chain! {
        if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::range(cx, expr);
        let ty = cx.tables().expr_ty(start);
        if let ty::Int(_) | ty::Uint(_) = ty.kind;
        if let Some((start_idx, _)) = constant(cx, cx.tables(), start);
        if let Some((end_idx, _)) = constant(cx, cx.tables(), end);
        if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx);
        if is_empty_range(limits, ordering);
        then {
            if inside_indexing_expr(cx, expr) {
                // Avoid linting `N..N` as it has proven to be useful, see #5689 and #5628 ...
                if ordering != Ordering::Equal {
                    span_lint(
                        cx,
                        REVERSED_EMPTY_RANGES,
                        expr.span,
                        "this range is reversed and using it to index a slice will panic at run-time",
                    );
                }
            // ... except in for loop arguments for backwards compatibility with `reverse_range_loop`
            } else if ordering != Ordering::Equal || is_for_loop_arg(cx, expr) {
                span_lint_and_then(
                    cx,
                    REVERSED_EMPTY_RANGES,
                    expr.span,
                    "this range is empty so it will yield no values",
                    |diag| {
                        if ordering != Ordering::Equal {
                            let start_snippet = snippet(cx, start.span, "_");
                            let end_snippet = snippet(cx, end.span, "_");
                            let dots = match limits {
                                RangeLimits::HalfOpen => "..",
                                RangeLimits::Closed => "..="
                            };

                            diag.span_suggestion(
                                expr.span,
                                "consider using the following if you are attempting to iterate over this \
                                 range in reverse",
                                format!("({}{}{}).rev()", end_snippet, dots, start_snippet),
                                Applicability::MaybeIncorrect,
                            );
                        }
                    },
                );
            }
        }
    }
}

fn y_plus_one<'t>(cx: &LateContext<'_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'t>> {
    match expr.kind {
        ExprKind::Binary(
            Spanned {
                node: BinOpKind::Add, ..
            },
            ref lhs,
            ref rhs,
        ) => {
            if is_integer_const(cx, lhs, 1) {
                Some(rhs)
            } else if is_integer_const(cx, rhs, 1) {
                Some(lhs)
            } else {
                None
            }
        },
        _ => None,
    }
}

fn y_minus_one<'t>(cx: &LateContext<'_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'t>> {
    match expr.kind {
        ExprKind::Binary(
            Spanned {
                node: BinOpKind::Sub, ..
            },
            ref lhs,
            ref rhs,
        ) if is_integer_const(cx, rhs, 1) => Some(lhs),
        _ => None,
    }
}
<|endoftext|>