<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::mem;

use parse;
use enums;

struct Member {
    name: String,
    value: String,
    offset: Option<usize>
}

impl Member {
    fn declaration_text(&self) -> String {
        let offset = match self.offset {
            Some(o) => format!("/* offset: {} */", o),
            _ => "".to_owned(),
        };
        format!("    pub {}: {} {}", self.name, self.value, offset)
    }
    fn copy_text(&self) -> String {
        format!("            {name}: self.{name}", name = self.name)
    }
}

/// Translates all the structs that are contained in the SPIR-V document as Rust structs.
pub fn write_structs(doc: &parse::Spirv) -> String {
    let mut result = String::new();

    for instruction in &doc.instructions {
        match *instruction {
            parse::Instruction::TypeStruct { result_id, ref member_types } => {
                result.push_str(&write_struct(doc, result_id, member_types));
                result.push_str("\n");
            },
            _ => ()
        }
    }

    result
}

/// Writes a single struct.
fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> String {
    let name = ::name_from_id(doc, struct_id);

    // The members of this struct.
    let mut rust_members = Vec::with_capacity(members.len());

    // Padding structs will be named `_paddingN` where `N` is determined by this variable.
    let mut next_padding_num = 0;

    // Contains the offset of the next field.
    // Equals to `None` if there's a runtime-sized field in there.
    let mut current_rust_offset = Some(0);

    for (num, &member) in members.iter().enumerate() {
        // Compute infos about the member.
        let (ty, rust_size, rust_align) = type_from_id(doc, member);
        let member_name = ::member_name_from_id(doc, struct_id, num as u32);

        // Ignore the whole struct is a member is built in, which includes
        // `gl_Position` for example.
        if is_builtin_member(doc, struct_id, num as u32) {
            return String::new();
        }

        // Finding offset of the current member, as requested by the SPIR-V code.
        let spirv_offset = doc.instructions.iter().filter_map(|i| {
            match *i {
                parse::Instruction::MemberDecorate { target_id, member,
                                                   decoration: enums::Decoration::DecorationOffset,
                                                   ref params } if target_id == struct_id &&
                                                                   member as usize == num =>
                {
                    return Some(params[0]);
                },
                _ => ()
            };

            None
        }).next();

        // Some structs don't have `Offset` decorations, in the case they are used as local
        // variables only. Ignoring these.
        let spirv_offset = match spirv_offset {
            Some(o) => o as usize,
            None => return String::new()
        };

        // We need to add a dummy field if necessary.
        {
            let current_rust_offset = current_rust_offset.as_mut().expect("Found runtime-sized member in non-final position");

            // Updating current_rust_offset to take the alignment of the next field into account
            *current_rust_offset = if *current_rust_offset == 0 {
                0
            } else {
                (1 + (*current_rust_offset - 1) / rust_align) * rust_align
            };

            if spirv_offset != *current_rust_offset {
                let diff = spirv_offset.checked_sub(*current_rust_offset).unwrap();
                let padding_num = next_padding_num; next_padding_num += 1;
                rust_members.push(Member {
                        name: format!("_dummy{}", padding_num),
                        value: format!("[u8; {}]", diff),
                        offset: None,
                });
                *current_rust_offset += diff;
            }
        }

        // Updating `current_rust_offset`.
        if let Some(s) = rust_size {
            *current_rust_offset.as_mut().unwrap() += s;
        } else {
            current_rust_offset = None;
        }

        rust_members.push(Member {
                name: member_name.to_owned(),
                value: ty,
                offset: Some(spirv_offset),
        });
    }

    // Try determine the total size of the struct in order to add padding at the end of the struct.
    let spirv_req_total_size = doc.instructions.iter().filter_map(|i| {
        match *i {
            parse::Instruction::Decorate { target_id,
                                           decoration: enums::Decoration::DecorationArrayStride,
                                           ref params } =>
            {
                for inst in doc.instructions.iter() {
                    match *inst {
                        parse::Instruction::TypeArray { result_id, type_id, .. }
                            if result_id == target_id && type_id == struct_id =>
                        {
                            return Some(params[0]);
                        },
                        parse::Instruction::TypeRuntimeArray { result_id, type_id }
                            if result_id == target_id && type_id == struct_id =>
                        {
                            return Some(params[0]);
                        },
                        _ => ()
                    }
                }

                None
            },
            _ => None
        }
    }).fold(None, |a, b| if let Some(a) = a { assert_eq!(a, b); Some(a) } else { Some(b) });

    // Adding the final padding members.
    if let (Some(cur_size), Some(req_size)) = (current_rust_offset, spirv_req_total_size) {
        let diff = req_size.checked_sub(cur_size as u32).unwrap();
        if diff >= 1 {
            rust_members.push(Member {
                    name: format!("_dummy{}", next_padding_num),
                    value: format!("[u8; {}]", diff),
                    offset: None,
            });
        }
    }

    format!("#[repr(C)]\n\
             pub struct {name} {{\n{members}\n}} /* total_size: {t:?} */\n\n\
             impl Clone for {name} {{\n    fn clone(&self) -> Self {{\n        \
             {name} {{\n{copies}\n        }}\n    }}\n}}\n\n",
            name = name,
            members = rust_members.iter().map(Member::declaration_text).collect::<Vec<_>>().join(",\n"),
            t = spirv_req_total_size,
            copies = rust_members.iter().map(Member::copy_text).collect::<Vec<_>>().join(",\n"))
}

/// Returns true if a `BuiltIn` decorator is applied on a struct member.
fn is_builtin_member(doc: &parse::Spirv, id: u32, member_id: u32) -> bool {
    for instruction in &doc.instructions {
        match *instruction {
            parse::Instruction::MemberDecorate { target_id, member,
                                                 decoration: enums::Decoration::DecorationBuiltIn,
                                                 .. } if target_id == id && member == member_id =>
            {
                return true;
            },
            _ => ()
        }
    }

    false
}

/// Returns the type name to put in the Rust struct, and its size and alignment.
///
/// The size can be `None` if it's only known at runtime.
fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>, usize) {
    for instruction in doc.instructions.iter() {
        match instruction {
            &parse::Instruction::TypeBool { result_id } if result_id == searched => {
                #[repr(C)] struct Foo { data: bool, after: u8 }
                let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                return ("bool".to_owned(), Some(size), mem::align_of::<Foo>())
            },
            &parse::Instruction::TypeInt { result_id, width, signedness } if result_id == searched => {
                match (width, signedness) {
                    (8, true) => {
                        #[repr(C)] struct Foo { data: i8, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("i8".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (8, false) => {
                        #[repr(C)] struct Foo { data: u8, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("u8".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (16, true) => {
                        #[repr(C)] struct Foo { data: i16, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("i16".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (16, false) => {
                        #[repr(C)] struct Foo { data: u16, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("u16".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (32, true) => {
                        #[repr(C)] struct Foo { data: i32, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("i32".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (32, false) => {
                        #[repr(C)] struct Foo { data: u32, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("u32".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (64, true) => {
                        #[repr(C)] struct Foo { data: i64, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("i64".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (64, false) => {
                        #[repr(C)] struct Foo { data: u64, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("u64".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    _ => panic!("No Rust equivalent for an integer of width {}", width)
                }
            },
            &parse::Instruction::TypeFloat { result_id, width } if result_id == searched => {
                match width {
                    32 => {
                        #[repr(C)] struct Foo { data: f32, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("f32".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    64 => {
                        #[repr(C)] struct Foo { data: f64, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("f64".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    _ => panic!("No Rust equivalent for a floating-point of width {}", width)
                }
            },
            &parse::Instruction::TypeVector { result_id, component_id, count } if result_id == searched => {
                debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
                let (t, t_size, t_align) = type_from_id(doc, component_id);
                return (format!("[{}; {}]", t, count), t_size.map(|s| s * count as usize), t_align);
            },
            &parse::Instruction::TypeMatrix { result_id, column_type_id, column_count } if result_id == searched => {
                // FIXME: row-major or column-major
                debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
                let (t, t_size, t_align) = type_from_id(doc, column_type_id);
                return (format!("[{}; {}]", t, column_count), t_size.map(|s| s * column_count as usize), t_align);
            },
            &parse::Instruction::TypeArray { result_id, type_id, length_id } if result_id == searched => {
                debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
                let (t, t_size, t_align) = type_from_id(doc, type_id);
                let len = doc.instructions.iter().filter_map(|e| {
                    match e { &parse::Instruction::Constant { result_id, ref data, .. } if result_id == length_id => Some(data.clone()), _ => None }
                }).next().expect("failed to find array length");
                let len = len.iter().rev().fold(0u64, |a, &b| (a << 32) | b as u64);
                return (format!("[{}; {}]", t, len), t_size.map(|s| s * len as usize), t_align);       // FIXME:
            },
            &parse::Instruction::TypeRuntimeArray { result_id, type_id } if result_id == searched => {
                debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
                let (t, _, t_align) = type_from_id(doc, type_id);
                return (format!("[{}]", t), None, t_align);
            },
            &parse::Instruction::TypeStruct { result_id, ref member_types } if result_id == searched => {
                let name = ::name_from_id(doc, result_id);
                let size = member_types.iter().filter_map(|&t| type_from_id(doc, t).1).fold(0, |a,b|a+b);
                let align = member_types.iter().map(|&t| type_from_id(doc, t).2).max().unwrap_or(1);
                return (name, Some(size), align);
            },
            _ => ()
        }
    }

    panic!("Type #{} not found", searched)
}
<commit_msg>Moved write_structs function back to top of file<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::mem;

use parse;
use enums;

/// Translates all the structs that are contained in the SPIR-V document as Rust structs.
pub fn write_structs(doc: &parse::Spirv) -> String {
    let mut result = String::new();

    for instruction in &doc.instructions {
        match *instruction {
            parse::Instruction::TypeStruct { result_id, ref member_types } => {
                result.push_str(&write_struct(doc, result_id, member_types));
                result.push_str("\n");
            },
            _ => ()
        }
    }

    result
}

/// Represents a rust struct member
struct Member {
    name: String,
    value: String,
    offset: Option<usize>
}

impl Member {
    fn declaration_text(&self) -> String {
        let offset = match self.offset {
            Some(o) => format!("/* offset: {} */", o),
            _ => "".to_owned(),
        };
        format!("    pub {}: {} {}", self.name, self.value, offset)
    }
    fn copy_text(&self) -> String {
        format!("            {name}: self.{name}", name = self.name)
    }
}

/// Writes a single struct.
fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> String {
    let name = ::name_from_id(doc, struct_id);

    // The members of this struct.
    let mut rust_members = Vec::with_capacity(members.len());

    // Padding structs will be named `_paddingN` where `N` is determined by this variable.
    let mut next_padding_num = 0;

    // Contains the offset of the next field.
    // Equals to `None` if there's a runtime-sized field in there.
    let mut current_rust_offset = Some(0);

    for (num, &member) in members.iter().enumerate() {
        // Compute infos about the member.
        let (ty, rust_size, rust_align) = type_from_id(doc, member);
        let member_name = ::member_name_from_id(doc, struct_id, num as u32);

        // Ignore the whole struct is a member is built in, which includes
        // `gl_Position` for example.
        if is_builtin_member(doc, struct_id, num as u32) {
            return String::new();
        }

        // Finding offset of the current member, as requested by the SPIR-V code.
        let spirv_offset = doc.instructions.iter().filter_map(|i| {
            match *i {
                parse::Instruction::MemberDecorate { target_id, member,
                                                   decoration: enums::Decoration::DecorationOffset,
                                                   ref params } if target_id == struct_id &&
                                                                   member as usize == num =>
                {
                    return Some(params[0]);
                },
                _ => ()
            };

            None
        }).next();

        // Some structs don't have `Offset` decorations, in the case they are used as local
        // variables only. Ignoring these.
        let spirv_offset = match spirv_offset {
            Some(o) => o as usize,
            None => return String::new()
        };

        // We need to add a dummy field if necessary.
        {
            let current_rust_offset = current_rust_offset.as_mut().expect("Found runtime-sized member in non-final position");

            // Updating current_rust_offset to take the alignment of the next field into account
            *current_rust_offset = if *current_rust_offset == 0 {
                0
            } else {
                (1 + (*current_rust_offset - 1) / rust_align) * rust_align
            };

            if spirv_offset != *current_rust_offset {
                let diff = spirv_offset.checked_sub(*current_rust_offset).unwrap();
                let padding_num = next_padding_num; next_padding_num += 1;
                rust_members.push(Member {
                        name: format!("_dummy{}", padding_num),
                        value: format!("[u8; {}]", diff),
                        offset: None,
                });
                *current_rust_offset += diff;
            }
        }

        // Updating `current_rust_offset`.
        if let Some(s) = rust_size {
            *current_rust_offset.as_mut().unwrap() += s;
        } else {
            current_rust_offset = None;
        }

        rust_members.push(Member {
                name: member_name.to_owned(),
                value: ty,
                offset: Some(spirv_offset),
        });
    }

    // Try determine the total size of the struct in order to add padding at the end of the struct.
    let spirv_req_total_size = doc.instructions.iter().filter_map(|i| {
        match *i {
            parse::Instruction::Decorate { target_id,
                                           decoration: enums::Decoration::DecorationArrayStride,
                                           ref params } =>
            {
                for inst in doc.instructions.iter() {
                    match *inst {
                        parse::Instruction::TypeArray { result_id, type_id, .. }
                            if result_id == target_id && type_id == struct_id =>
                        {
                            return Some(params[0]);
                        },
                        parse::Instruction::TypeRuntimeArray { result_id, type_id }
                            if result_id == target_id && type_id == struct_id =>
                        {
                            return Some(params[0]);
                        },
                        _ => ()
                    }
                }

                None
            },
            _ => None
        }
    }).fold(None, |a, b| if let Some(a) = a { assert_eq!(a, b); Some(a) } else { Some(b) });

    // Adding the final padding members.
    if let (Some(cur_size), Some(req_size)) = (current_rust_offset, spirv_req_total_size) {
        let diff = req_size.checked_sub(cur_size as u32).unwrap();
        if diff >= 1 {
            rust_members.push(Member {
                    name: format!("_dummy{}", next_padding_num),
                    value: format!("[u8; {}]", diff),
                    offset: None,
            });
        }
    }

    format!("#[repr(C)]\n\
             pub struct {name} {{\n{members}\n}} /* total_size: {t:?} */\n\n\
             impl Clone for {name} {{\n    fn clone(&self) -> Self {{\n        \
             {name} {{\n{copies}\n        }}\n    }}\n}}\n\n",
            name = name,
            members = rust_members.iter().map(Member::declaration_text).collect::<Vec<_>>().join(",\n"),
            t = spirv_req_total_size,
            copies = rust_members.iter().map(Member::copy_text).collect::<Vec<_>>().join(",\n"))
}

/// Returns true if a `BuiltIn` decorator is applied on a struct member.
fn is_builtin_member(doc: &parse::Spirv, id: u32, member_id: u32) -> bool {
    for instruction in &doc.instructions {
        match *instruction {
            parse::Instruction::MemberDecorate { target_id, member,
                                                 decoration: enums::Decoration::DecorationBuiltIn,
                                                 .. } if target_id == id && member == member_id =>
            {
                return true;
            },
            _ => ()
        }
    }

    false
}

/// Returns the type name to put in the Rust struct, and its size and alignment.
///
/// The size can be `None` if it's only known at runtime.
fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>, usize) {
    for instruction in doc.instructions.iter() {
        match instruction {
            &parse::Instruction::TypeBool { result_id } if result_id == searched => {
                #[repr(C)] struct Foo { data: bool, after: u8 }
                let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                return ("bool".to_owned(), Some(size), mem::align_of::<Foo>())
            },
            &parse::Instruction::TypeInt { result_id, width, signedness } if result_id == searched => {
                match (width, signedness) {
                    (8, true) => {
                        #[repr(C)] struct Foo { data: i8, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("i8".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (8, false) => {
                        #[repr(C)] struct Foo { data: u8, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("u8".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (16, true) => {
                        #[repr(C)] struct Foo { data: i16, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("i16".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (16, false) => {
                        #[repr(C)] struct Foo { data: u16, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("u16".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (32, true) => {
                        #[repr(C)] struct Foo { data: i32, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("i32".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (32, false) => {
                        #[repr(C)] struct Foo { data: u32, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("u32".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (64, true) => {
                        #[repr(C)] struct Foo { data: i64, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("i64".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (64, false) => {
                        #[repr(C)] struct Foo { data: u64, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("u64".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    _ => panic!("No Rust equivalent for an integer of width {}", width)
                }
            },
            &parse::Instruction::TypeFloat { result_id, width } if result_id == searched => {
                match width {
                    32 => {
                        #[repr(C)] struct Foo { data: f32, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("f32".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    64 => {
                        #[repr(C)] struct Foo { data: f64, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("f64".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    _ => panic!("No Rust equivalent for a floating-point of width {}", width)
                }
            },
            &parse::Instruction::TypeVector { result_id, component_id, count } if result_id == searched => {
                debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
                let (t, t_size, t_align) = type_from_id(doc, component_id);
                return (format!("[{}; {}]", t, count), t_size.map(|s| s * count as usize), t_align);
            },
            &parse::Instruction::TypeMatrix { result_id, column_type_id, column_count } if result_id == searched => {
                // FIXME: row-major or column-major
                debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
                let (t, t_size, t_align) = type_from_id(doc, column_type_id);
                return (format!("[{}; {}]", t, column_count), t_size.map(|s| s * column_count as usize), t_align);
            },
            &parse::Instruction::TypeArray { result_id, type_id, length_id } if result_id == searched => {
                debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
                let (t, t_size, t_align) = type_from_id(doc, type_id);
                let len = doc.instructions.iter().filter_map(|e| {
                    match e { &parse::Instruction::Constant { result_id, ref data, .. } if result_id == length_id => Some(data.clone()), _ => None }
                }).next().expect("failed to find array length");
                let len = len.iter().rev().fold(0u64, |a, &b| (a << 32) | b as u64);
                return (format!("[{}; {}]", t, len), t_size.map(|s| s * len as usize), t_align);       // FIXME:
            },
            &parse::Instruction::TypeRuntimeArray { result_id, type_id } if result_id == searched => {
                debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
                let (t, _, t_align) = type_from_id(doc, type_id);
                return (format!("[{}]", t), None, t_align);
            },
            &parse::Instruction::TypeStruct { result_id, ref member_types } if result_id == searched => {
                let name = ::name_from_id(doc, result_id);
                let size = member_types.iter().filter_map(|&t| type_from_id(doc, t).1).fold(0, |a,b|a+b);
                let align = member_types.iter().map(|&t| type_from_id(doc, t).2).max().unwrap_or(1);
                return (name, Some(size), align);
            },
            _ => ()
        }
    }

    panic!("Type #{} not found", searched)
}
<|endoftext|>


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

// Test that Vec::new() can be used for constants

const MY_VEC: Vec<usize> = Vec::new();

pub fn main() {}
<commit_msg>feature on test<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Test that Vec::new() can be used for constants

#![feature(const_vec_new)]

const MY_VEC: Vec<usize> = Vec::new();

pub fn main() {}
<|endoftext|>


<commit_before>extern crate glfw;

fn main() {
    let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();
    glfw.window_hint(glfw::WindowHint::ContextVersion(3, 3));
    glfw.window_hint(glfw::WindowHint::OpenGlProfile(glfw::OpenGlProfileHint::Core));
    #[cfg(target_os = "macos")]
    glfw.window_hint(glfw::WindowHint::OpenGlForwardCompat(true));
}
<commit_msg>1.1: few more steps<commit_after>extern crate glfw;
use self::glfw::Context;

extern crate gl;
use self::gl::types::*;

// settings
const SCR_WIDTH: u32 = 800;
const SCR_HEIGHT: u32 = 600;

#[allow(dead_code)]
fn main() {
    // glfw: initialize and configure
    // ------------------------------
    let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();
    glfw.window_hint(glfw::WindowHint::ContextVersion(3, 3));
    glfw.window_hint(glfw::WindowHint::OpenGlProfile(glfw::OpenGlProfileHint::Core));
    #[cfg(target_os = "macos")]
    glfw.window_hint(glfw::WindowHint::OpenGlForwardCompat(true));

    // glfw window creation
    // --------------------
    let (mut window, events) = glfw.create_window(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", glfw::WindowMode::Windowed)
        .expect("Failed to create GLFW window");

    window.make_current();

    // TODO?: glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    // gl: load all OpenGL function pointers
    // ---------------------------------------
    gl::load_with(|symbol| window.get_proc_address(symbol) as *const _);

    unsafe { gl::Viewport(0, 0, SCR_WIDTH as i32, SCR_HEIGHT as i32) }
}
<|endoftext|>


<commit_before>use gl::types::*;
use std::f32::consts::PI;
use std::ops::{Add, Index, IndexMut, Mul, Sub};

pub const TAU: f32 = 2.0 * PI;

macro_rules! define_vec {
    ($name:ident, $size:expr) => (
        #[derive(Copy, Clone, Debug, PartialEq)]
        pub struct $name(pub [f32; $size]);

        impl $name {
            pub fn zero() -> Self {
                $name([0.0; $size])
            }

            /// Calculate the square of the length (or norm) of the vector. Slightly faster than
            /// `length`.
            pub fn length_squared(self) -> f32 {
                self.dot(self)
            }

            /// Calculate the the length (or norm) of the vector.
            pub fn length(self) -> f32 {
                self.length_squared().sqrt()
            }

            /// Normalize the vector so that it has the same orientation but a length of 1.
            pub fn normalize(&mut self) {
                let length = self.length();

                for i in 0..$size {
                    self[i] /= length;
                }
            }

            /// Calculate the vector dot product.
            pub fn dot(self, other: Self) -> f32 {
                let mut result = 0.0;

                for i in 0..$size {
                    result += self[i] * other[i];
                }

                result
            }
        }

        impl Index<usize> for $name {
            type Output = f32;

            fn index(&self, i: usize) -> &f32 {
                &self.0[i]
            }
        }

        impl IndexMut<usize> for $name {
            fn index_mut(&mut self, i: usize) -> &mut f32 {
                &mut self.0[i]
            }
        }

        impl Add for $name {
            type Output = Self;

            fn add(self, other: Self) -> Self {
                let mut result = $name::zero();

                for i in 0..$size {
                    result[i] = self[i] + other[i];
                }

                result
            }
        }

        impl Sub for $name {
            type Output = Self;

            fn sub(self, other: Self) -> Self {
                let mut result = $name::zero();

                for i in 0..$size {
                    result[i] = self[i] - other[i];
                }

                result
            }
        }
    );
}

define_vec!(Vec3, 3);
define_vec!(Vec4, 4);

impl Vec3 {
    /// Calculate the vector cross product.
    pub fn cross(self, other: Self) -> Self {
        Vec3([
            self[1] * other[2] - self[2] * other[1],
            self[2] * other[0] - self[0] * other[2],
            self[0] * other[1] - self[1] * other[0],
        ])
    }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Mat4(pub [[f32; 4]; 4]);

impl Mat4 {
    /// The zero matrix.
    pub fn zero() -> Self {
        Mat4([
            [0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0],
        ])
    }

    /// The identity matrix.
    pub fn identity() -> Self {
        Mat4([
            [1.0, 0.0, 0.0, 0.0],
            [0.0, 1.0, 0.0, 0.0],
            [0.0, 0.0, 1.0, 0.0],
            [0.0, 0.0, 0.0, 1.0],
        ])
    }

    /// Build a matrix representing a scaling by the given factors.
    pub fn scale(x: f32, y: f32, z: f32) -> Self {
        Mat4([
            [x,   0.0, 0.0, 0.0],
            [0.0, y,   0.0, 0.0],
            [0.0, 0.0, z,   0.0],
            [0.0, 0.0, 0.0, 1.0],
        ])
    }

    /// Build a matrix representing a translation.
    pub fn translate(x: f32, y: f32, z: f32) -> Self {
        Mat4([
            [1.0, 0.0, 0.0, x  ],
            [0.0, 1.0, 0.0, y  ],
            [0.0, 0.0, 1.0, z  ],
            [0.0, 0.0, 0.0, 1.0],
        ])
    }

    /// Build a matrix representing a rotation around the X-axis by the given angle (in radians).
    pub fn rotate_x(angle: f32) -> Self {
        let cos = angle.cos();
        let sin = angle.sin();

        Mat4([
            [1.0, 0.0,  0.0, 0.0],
            [0.0, cos, -sin, 0.0],
            [0.0, sin,  cos, 0.0],
            [0.0, 0.0,  0.0, 1.0],
        ])
    }

    /// Build a matrix representing a rotation around the Y-axis by the given angle (in radians).
    pub fn rotate_y(angle: f32) -> Self {
        let cos = angle.cos();
        let sin = angle.sin();

        Mat4([
            [ cos, 0.0, sin, 0.0],
            [ 0.0, 1.0, 0.0, 0.0],
            [-sin, 0.0, cos, 0.0],
            [ 0.0, 0.0, 0.0, 1.0],
        ])
    }

    /// Build a matrix representing a rotation around the Z-axis by the given angle (in radians).
    pub fn rotate_z(angle: f32) -> Self {
        let cos = angle.cos();
        let sin = angle.sin();

        Mat4([
            [cos, -sin, 0.0, 0.0],
            [sin,  cos, 0.0, 0.0],
            [0.0,  0.0, 1.0, 0.0],
            [0.0,  0.0, 0.0, 1.0],
        ])
    }

    /// Build a camera view matrix with the camera at `eye` looking toward `center` with `up` as
    /// the vertical direction.
    pub fn look_at(eye: Vec3, center: Vec3, up: Vec3) -> Self {
        // Set the Z-axis to the unit vector pointing from the center toward the eye (the depth
        // axis).
        let mut z = eye - center;
        z.normalize();

        // Make the Y-axis the vertical direction.
        let mut y = up;

        // Make the X-axis perpendicular to Y and Z, pointing to the right.
        let mut x = y.cross(z);

        // Make the Y-axis perpendicular to Z and X.
        y = z.cross(x);

        // Normalize the axes to unit vectors.
        x.normalize();
        y.normalize();

        // Build the rotation/translation matrix that transforms coordinates to the new coordinate
        // system.
        Mat4([
            [ x[0],        y[0],        z[0],       0.0],
            [ x[1],        y[1],        z[1],       0.0],
            [ x[2],        y[2],        z[2],       0.0],
            [-x.dot(eye), -y.dot(eye), -z.dot(eye), 1.0],
        ])
    }

    /// Build a perspective projection matrix with the given vertical field of view (in radians),
    /// aspect ratio, and Z-axis clipping distances.
    pub fn perspective(fov_y: f32, aspect: f32, z_near: f32, z_far: f32) -> Self {
        assert!(aspect != 0.0);
        assert!(z_near != z_far);

        let f = 1.0 / (fov_y / 2.0).tan();
        let z_diff = z_near - z_far;

        let mut result = Mat4::zero();
        result[0][0] = f / aspect;
        result[1][1] = f;
        result[2][2] = (z_near + z_far) / z_diff;
        result[2][3] = -1.0;
        result[3][2] = (2.0 * z_near * z_far) / z_diff;
        result
    }
}

impl Index<usize> for Mat4 {
    type Output = [f32; 4];

    fn index(&self, i: usize) -> &[f32; 4] {
        &self.0[i]
    }
}

impl IndexMut<usize> for Mat4 {
    fn index_mut(&mut self, i: usize) -> &mut [f32; 4] {
        &mut self.0[i]
    }
}

impl Mul<Mat4> for Mat4 {
    type Output = Mat4;

    fn mul(self, other: Mat4) -> Mat4 {
        let mut result = Mat4::zero();

        for i in 0..4 {
            for j in 0..4 {
                for k in 0..4 {
                    result[i][j] += self[i][k] * other[k][j];
                }
            }
        }

        result
    }
}

impl Mul<Vec4> for Mat4 {
    type Output = Vec4;

    fn mul(self, vec: Vec4) -> Vec4 {
        let mut result = Vec4::zero();

        for i in 0..4 {
            for j in 0..4 {
                result[i] += self[i][j] * vec[j];
            }
        }

        result
    }
}

#[test]
fn test_math() {
    let scale = Mat4::scale(2.0, 2.0, 2.0);
    let trans = Mat4::translate(1.0, 2.0, 3.0);
    let combined = trans * scale;

    let original = Vec4([3.0, 3.0, 3.0, 1.0]);
    let expected = Vec4([7.0, 8.0, 9.0, 1.0]);

    assert_eq!(expected, combined * original);
}
<commit_msg>Remove unused `use`.<commit_after>use std::f32::consts::PI;
use std::ops::{Add, Index, IndexMut, Mul, Sub};

pub const TAU: f32 = 2.0 * PI;

macro_rules! define_vec {
    ($name:ident, $size:expr) => (
        #[derive(Copy, Clone, Debug, PartialEq)]
        pub struct $name(pub [f32; $size]);

        impl $name {
            pub fn zero() -> Self {
                $name([0.0; $size])
            }

            /// Calculate the square of the length (or norm) of the vector. Slightly faster than
            /// `length`.
            pub fn length_squared(self) -> f32 {
                self.dot(self)
            }

            /// Calculate the the length (or norm) of the vector.
            pub fn length(self) -> f32 {
                self.length_squared().sqrt()
            }

            /// Normalize the vector so that it has the same orientation but a length of 1.
            pub fn normalize(&mut self) {
                let length = self.length();

                for i in 0..$size {
                    self[i] /= length;
                }
            }

            /// Calculate the vector dot product.
            pub fn dot(self, other: Self) -> f32 {
                let mut result = 0.0;

                for i in 0..$size {
                    result += self[i] * other[i];
                }

                result
            }
        }

        impl Index<usize> for $name {
            type Output = f32;

            fn index(&self, i: usize) -> &f32 {
                &self.0[i]
            }
        }

        impl IndexMut<usize> for $name {
            fn index_mut(&mut self, i: usize) -> &mut f32 {
                &mut self.0[i]
            }
        }

        impl Add for $name {
            type Output = Self;

            fn add(self, other: Self) -> Self {
                let mut result = $name::zero();

                for i in 0..$size {
                    result[i] = self[i] + other[i];
                }

                result
            }
        }

        impl Sub for $name {
            type Output = Self;

            fn sub(self, other: Self) -> Self {
                let mut result = $name::zero();

                for i in 0..$size {
                    result[i] = self[i] - other[i];
                }

                result
            }
        }
    );
}

define_vec!(Vec3, 3);
define_vec!(Vec4, 4);

impl Vec3 {
    /// Calculate the vector cross product.
    pub fn cross(self, other: Self) -> Self {
        Vec3([
            self[1] * other[2] - self[2] * other[1],
            self[2] * other[0] - self[0] * other[2],
            self[0] * other[1] - self[1] * other[0],
        ])
    }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Mat4(pub [[f32; 4]; 4]);

impl Mat4 {
    /// The zero matrix.
    pub fn zero() -> Self {
        Mat4([
            [0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0],
        ])
    }

    /// The identity matrix.
    pub fn identity() -> Self {
        Mat4([
            [1.0, 0.0, 0.0, 0.0],
            [0.0, 1.0, 0.0, 0.0],
            [0.0, 0.0, 1.0, 0.0],
            [0.0, 0.0, 0.0, 1.0],
        ])
    }

    /// Build a matrix representing a scaling by the given factors.
    pub fn scale(x: f32, y: f32, z: f32) -> Self {
        Mat4([
            [x,   0.0, 0.0, 0.0],
            [0.0, y,   0.0, 0.0],
            [0.0, 0.0, z,   0.0],
            [0.0, 0.0, 0.0, 1.0],
        ])
    }

    /// Build a matrix representing a translation.
    pub fn translate(x: f32, y: f32, z: f32) -> Self {
        Mat4([
            [1.0, 0.0, 0.0, x  ],
            [0.0, 1.0, 0.0, y  ],
            [0.0, 0.0, 1.0, z  ],
            [0.0, 0.0, 0.0, 1.0],
        ])
    }

    /// Build a matrix representing a rotation around the X-axis by the given angle (in radians).
    pub fn rotate_x(angle: f32) -> Self {
        let cos = angle.cos();
        let sin = angle.sin();

        Mat4([
            [1.0, 0.0,  0.0, 0.0],
            [0.0, cos, -sin, 0.0],
            [0.0, sin,  cos, 0.0],
            [0.0, 0.0,  0.0, 1.0],
        ])
    }

    /// Build a matrix representing a rotation around the Y-axis by the given angle (in radians).
    pub fn rotate_y(angle: f32) -> Self {
        let cos = angle.cos();
        let sin = angle.sin();

        Mat4([
            [ cos, 0.0, sin, 0.0],
            [ 0.0, 1.0, 0.0, 0.0],
            [-sin, 0.0, cos, 0.0],
            [ 0.0, 0.0, 0.0, 1.0],
        ])
    }

    /// Build a matrix representing a rotation around the Z-axis by the given angle (in radians).
    pub fn rotate_z(angle: f32) -> Self {
        let cos = angle.cos();
        let sin = angle.sin();

        Mat4([
            [cos, -sin, 0.0, 0.0],
            [sin,  cos, 0.0, 0.0],
            [0.0,  0.0, 1.0, 0.0],
            [0.0,  0.0, 0.0, 1.0],
        ])
    }

    /// Build a camera view matrix with the camera at `eye` looking toward `center` with `up` as
    /// the vertical direction.
    pub fn look_at(eye: Vec3, center: Vec3, up: Vec3) -> Self {
        // Set the Z-axis to the unit vector pointing from the center toward the eye (the depth
        // axis).
        let mut z = eye - center;
        z.normalize();

        // Make the Y-axis the vertical direction.
        let mut y = up;

        // Make the X-axis perpendicular to Y and Z, pointing to the right.
        let mut x = y.cross(z);

        // Make the Y-axis perpendicular to Z and X.
        y = z.cross(x);

        // Normalize the axes to unit vectors.
        x.normalize();
        y.normalize();

        // Build the rotation/translation matrix that transforms coordinates to the new coordinate
        // system.
        Mat4([
            [ x[0],        y[0],        z[0],       0.0],
            [ x[1],        y[1],        z[1],       0.0],
            [ x[2],        y[2],        z[2],       0.0],
            [-x.dot(eye), -y.dot(eye), -z.dot(eye), 1.0],
        ])
    }

    /// Build a perspective projection matrix with the given vertical field of view (in radians),
    /// aspect ratio, and Z-axis clipping distances.
    pub fn perspective(fov_y: f32, aspect: f32, z_near: f32, z_far: f32) -> Self {
        assert!(aspect != 0.0);
        assert!(z_near != z_far);

        let f = 1.0 / (fov_y / 2.0).tan();
        let z_diff = z_near - z_far;

        let mut result = Mat4::zero();
        result[0][0] = f / aspect;
        result[1][1] = f;
        result[2][2] = (z_near + z_far) / z_diff;
        result[2][3] = -1.0;
        result[3][2] = (2.0 * z_near * z_far) / z_diff;
        result
    }
}

impl Index<usize> for Mat4 {
    type Output = [f32; 4];

    fn index(&self, i: usize) -> &[f32; 4] {
        &self.0[i]
    }
}

impl IndexMut<usize> for Mat4 {
    fn index_mut(&mut self, i: usize) -> &mut [f32; 4] {
        &mut self.0[i]
    }
}

impl Mul<Mat4> for Mat4 {
    type Output = Mat4;

    fn mul(self, other: Mat4) -> Mat4 {
        let mut result = Mat4::zero();

        for i in 0..4 {
            for j in 0..4 {
                for k in 0..4 {
                    result[i][j] += self[i][k] * other[k][j];
                }
            }
        }

        result
    }
}

impl Mul<Vec4> for Mat4 {
    type Output = Vec4;

    fn mul(self, vec: Vec4) -> Vec4 {
        let mut result = Vec4::zero();

        for i in 0..4 {
            for j in 0..4 {
                result[i] += self[i][j] * vec[j];
            }
        }

        result
    }
}

#[test]
fn test_math() {
    let scale = Mat4::scale(2.0, 2.0, 2.0);
    let trans = Mat4::translate(1.0, 2.0, 3.0);
    let combined = trans * scale;

    let original = Vec4([3.0, 3.0, 3.0, 1.0]);
    let expected = Vec4([7.0, 8.0, 9.0, 1.0]);

    assert_eq!(expected, combined * original);
}
<|endoftext|>


<commit_before>use analysis::c_type::{implements_c_type, rustify_pointers};
use analysis::namespaces;
use analysis::rust_type::{Result, TypeError};
use env::Env;
use library;
use library::*;
use traits::*;

// FIXME: This module needs redundant allocations audit
// TODO: ffi_type computations should be cached

pub fn ffi_type(env: &Env, tid: library::TypeId, c_type: &str) -> Result {
    let (ptr, inner) = rustify_pointers(c_type);
    let res = if ptr.is_empty() {
        if let Some(c_tid) = env.library.find_type(0, c_type) {
            // Fast track plain fundamental types avoiding some checks
            if env.library
                .type_(c_tid)
                .maybe_ref_as::<Fundamental>()
                .is_some()
            {
                match *env.library.type_(tid) {
                    Type::FixedArray(_, size) => ffi_inner(env, c_tid, c_type.into())
                        .map_any(|s| format!("[{}; {}]", s, size)),
                    Type::Class(Class {
                        c_type: ref expected,
                        ..
                    }) |
                    Type::Interface(Interface {
                        c_type: ref expected,
                        ..
                    }) if c_type == "gpointer" =>
                    {
                        info!("[c:type `gpointer` instead of `*mut {}`, fixing]", expected);
                        ffi_inner(env, tid, expected.clone()).map_any(|s| format!("*mut {}", s))
                    }
                    _ => ffi_inner(env, c_tid, c_type.into()),
                }
            } else {
                // c_type isn't fundamental
                ffi_inner(env, tid, inner)
            }
        } else {
            // c_type doesn't match any type in the library by name
            ffi_inner(env, tid, inner)
        }
    } else {
        // ptr not empty
        ffi_inner(env, tid, inner).map_any(|s| format!("{} {}", ptr, s))
    };
    trace!("ffi_type({:?}, {}) -> {:?}", tid, c_type, res);
    res
}

fn ffi_inner(env: &Env, tid: library::TypeId, mut inner: String) -> Result {
    let volatile = inner.starts_with("volatile ");
    if volatile {
        inner = inner["volatile ".len()..].into();
    }

    let typ = env.library.type_(tid);
    let res = match *typ {
        Type::Fundamental(fund) => {
            use library::Fundamental::*;
            let inner = match fund {
                None => "c_void",
                Boolean => "gboolean",
                Int8 => "i8",
                UInt8 => "u8",
                Int16 => "i16",
                UInt16 => "u16",
                Int32 => "i32",
                UInt32 => "u32",
                Int64 => "i64",
                UInt64 => "u64",
                Char => "c_char",
                UChar => "c_uchar",
                Short => "c_short",
                UShort => "c_ushort",
                Int => "c_int",
                UInt => "c_uint",
                Long => "c_long",
                ULong => "c_ulong",
                Size => "size_t",
                SSize => "ssize_t",
                Float => "c_float",
                Double => "c_double",
                UniChar => "u32",
                Utf8 => "c_char",
                Filename => "c_char",
                OsString => "c_char",
                Type => "GType",
                Pointer => {
                    match &inner[..] {
                        "void" => "c_void",
                        "tm" => return Err(TypeError::Unimplemented(inner)), //TODO: try use time:Tm
                        _ => &*inner,
                    }
                }
                IntPtr => "intptr_t",
                UIntPtr => "uintptr_t",
                Unsupported => return Err(TypeError::Unimplemented(inner)),
                VarArgs => panic!("Should not reach here"),
            };
            Ok(inner.into())
        }
        Type::Record(..) | Type::Alias(..) | Type::Function(..) => {
            if let Some(declared_c_type) = typ.get_glib_name() {
                if declared_c_type != inner {
                    let msg = format!(
                        "[c:type mismatch `{}` != `{}` of `{}`]",
                        inner,
                        declared_c_type,
                        typ.get_name()
                    );
                    warn!("{}", msg);
                    return Err(TypeError::Mismatch(msg));
                }
            } else {
                warn!("Type `{}` missing c_type", typ.get_name());
            }
            fix_name(env, tid, &inner)
        }
        Type::CArray(inner_tid) => ffi_inner(env, inner_tid, inner),
        Type::FixedArray(inner_tid, size) => {
            ffi_inner(env, inner_tid, inner).map_any(|s| format!("[{}; {}]", s, size))
        }
        Type::Array(..) |
        Type::PtrArray(..) |
        Type::List(..) |
        Type::SList(..) |
        Type::HashTable(..) => fix_name(env, tid, &inner),
        _ => if let Some(glib_name) = env.library.type_(tid).get_glib_name() {
            if inner != glib_name {
                if implements_c_type(env, tid, &inner) {
                    info!(
                        "[c:type {} of {} <: {}, fixing]",
                        glib_name,
                        env.library.type_(tid).get_name(),
                        inner
                    );
                    fix_name(env, tid, glib_name)
                } else {
                    let msg = format!(
                        "[c:type mismatch {} != {} of {}]",
                        inner,
                        glib_name,
                        env.library.type_(tid).get_name()
                    );
                    warn!("{}", msg);
                    Err(TypeError::Mismatch(msg))
                }
            } else {
                fix_name(env, tid, &inner)
            }
        } else {
            let msg = format!(
                "[Missing glib_name of {}, can't match != {}]",
                env.library.type_(tid).get_name(),
                inner
            );
            warn!("{}", msg);
            Err(TypeError::Mismatch(msg))
        },
    };

    if volatile {
        res.map(|s| format!("/*volatile*/{}", s))
    } else {
        res
    }
}

fn fix_name(env: &Env, type_id: library::TypeId, name: &str) -> Result {
    if type_id.ns_id == library::INTERNAL_NAMESPACE {
        match *env.library.type_(type_id) {
            Type::Array(..) |
            Type::PtrArray(..) |
            Type::List(..) |
            Type::SList(..) |
            Type::HashTable(..) => if env.namespaces.glib_ns_id == namespaces::MAIN {
                Ok(name.into())
            } else {
                Ok(format!(
                    "{}::{}",
                    &env.namespaces[env.namespaces.glib_ns_id].crate_name,
                    name
                ))
            },
            _ => Ok(name.into()),
        }
    } else {
        let name_with_prefix = if type_id.ns_id == namespaces::MAIN {
            name.into()
        } else {
            format!("{}::{}", &env.namespaces[type_id.ns_id].crate_name, name)
        };
        if env.type_status_sys(&type_id.full_name(&env.library))
            .ignored()
        {
            Err(TypeError::Ignored(name_with_prefix))
        } else {
            Ok(name_with_prefix)
        }
    }
}
<commit_msg>Fix generating array with c:type="gpointer*" and other actual type<commit_after>use analysis::c_type::{implements_c_type, rustify_pointers};
use analysis::namespaces;
use analysis::rust_type::{Result, TypeError};
use env::Env;
use library;
use library::*;
use traits::*;

// FIXME: This module needs redundant allocations audit
// TODO: ffi_type computations should be cached

pub fn ffi_type(env: &Env, tid: library::TypeId, c_type: &str) -> Result {
    let (ptr, inner) = rustify_pointers(c_type);
    let res = if ptr.is_empty() {
        if let Some(c_tid) = env.library.find_type(0, c_type) {
            // Fast track plain fundamental types avoiding some checks
            if env.library
                .type_(c_tid)
                .maybe_ref_as::<Fundamental>()
                .is_some()
            {
                match *env.library.type_(tid) {
                    Type::FixedArray(_, size) => ffi_inner(env, c_tid, c_type.into())
                        .map_any(|s| format!("[{}; {}]", s, size)),
                    Type::Class(Class {
                        c_type: ref expected,
                        ..
                    }) |
                    Type::Interface(Interface {
                        c_type: ref expected,
                        ..
                    }) if c_type == "gpointer" =>
                    {
                        info!("[c:type `gpointer` instead of `*mut {}`, fixing]", expected);
                        ffi_inner(env, tid, expected.clone()).map_any(|s| format!("*mut {}", s))
                    }
                    _ => ffi_inner(env, c_tid, c_type.into()),
                }
            } else {
                // c_type isn't fundamental
                ffi_inner(env, tid, inner)
            }
        } else {
            // c_type doesn't match any type in the library by name
            ffi_inner(env, tid, inner)
        }
    } else {
        // ptr not empty
        ffi_inner(env, tid, inner).map_any(|s| format!("{} {}", ptr, s))
    };
    trace!("ffi_type({:?}, {}) -> {:?}", tid, c_type, res);
    res
}

fn ffi_inner(env: &Env, tid: library::TypeId, mut inner: String) -> Result {
    let volatile = inner.starts_with("volatile ");
    if volatile {
        inner = inner["volatile ".len()..].into();
    }

    let typ = env.library.type_(tid);
    let res = match *typ {
        Type::Fundamental(fund) => {
            use library::Fundamental::*;
            let inner = match fund {
                None => "c_void",
                Boolean => "gboolean",
                Int8 => "i8",
                UInt8 => "u8",
                Int16 => "i16",
                UInt16 => "u16",
                Int32 => "i32",
                UInt32 => "u32",
                Int64 => "i64",
                UInt64 => "u64",
                Char => "c_char",
                UChar => "c_uchar",
                Short => "c_short",
                UShort => "c_ushort",
                Int => "c_int",
                UInt => "c_uint",
                Long => "c_long",
                ULong => "c_ulong",
                Size => "size_t",
                SSize => "ssize_t",
                Float => "c_float",
                Double => "c_double",
                UniChar => "u32",
                Utf8 => "c_char",
                Filename => "c_char",
                OsString => "c_char",
                Type => "GType",
                Pointer => {
                    match &inner[..] {
                        "void" => "c_void",
                        "tm" => return Err(TypeError::Unimplemented(inner)), //TODO: try use time:Tm
                        _ => &*inner,
                    }
                }
                IntPtr => "intptr_t",
                UIntPtr => "uintptr_t",
                Unsupported => return Err(TypeError::Unimplemented(inner)),
                VarArgs => panic!("Should not reach here"),
            };
            Ok(inner.into())
        }
        Type::Record(..) | Type::Alias(..) | Type::Function(..) => {
            if let Some(declared_c_type) = typ.get_glib_name() {
                if declared_c_type != inner {
                    let msg = format!(
                        "[c:type mismatch `{}` != `{}` of `{}`]",
                        inner,
                        declared_c_type,
                        typ.get_name()
                    );
                    warn!("{}", msg);
                    return Err(TypeError::Mismatch(msg));
                }
            } else {
                warn!("Type `{}` missing c_type", typ.get_name());
            }
            fix_name(env, tid, &inner)
        }
        Type::CArray(inner_tid) => ffi_inner(env, inner_tid, inner),
        Type::FixedArray(inner_tid, size) => {
            ffi_inner(env, inner_tid, inner).map_any(|s| format!("[{}; {}]", s, size))
        }
        Type::Array(..) |
        Type::PtrArray(..) |
        Type::List(..) |
        Type::SList(..) |
        Type::HashTable(..) => fix_name(env, tid, &inner),
        _ => if let Some(glib_name) = env.library.type_(tid).get_glib_name() {
            if inner != glib_name {
                if inner == "gpointer" {
                    fix_name(env, tid, glib_name).map_any(|s| format!("*mut {}", s))
                } else if implements_c_type(env, tid, &inner) {
                    info!(
                        "[c:type {} of {} <: {}, fixing]",
                        glib_name,
                        env.library.type_(tid).get_name(),
                        inner
                    );
                    fix_name(env, tid, glib_name)
                } else {
                    let msg = format!(
                        "[c:type mismatch {} != {} of {}]",
                        inner,
                        glib_name,
                        env.library.type_(tid).get_name()
                    );
                    warn!("{}", msg);
                    Err(TypeError::Mismatch(msg))
                }
            } else {
                fix_name(env, tid, &inner)
            }
        } else {
            let msg = format!(
                "[Missing glib_name of {}, can't match != {}]",
                env.library.type_(tid).get_name(),
                inner
            );
            warn!("{}", msg);
            Err(TypeError::Mismatch(msg))
        },
    };

    if volatile {
        res.map(|s| format!("/*volatile*/{}", s))
    } else {
        res
    }
}

fn fix_name(env: &Env, type_id: library::TypeId, name: &str) -> Result {
    if type_id.ns_id == library::INTERNAL_NAMESPACE {
        match *env.library.type_(type_id) {
            Type::Array(..) |
            Type::PtrArray(..) |
            Type::List(..) |
            Type::SList(..) |
            Type::HashTable(..) => if env.namespaces.glib_ns_id == namespaces::MAIN {
                Ok(name.into())
            } else {
                Ok(format!(
                    "{}::{}",
                    &env.namespaces[env.namespaces.glib_ns_id].crate_name,
                    name
                ))
            },
            _ => Ok(name.into()),
        }
    } else {
        let name_with_prefix = if type_id.ns_id == namespaces::MAIN {
            name.into()
        } else {
            format!("{}::{}", &env.namespaces[type_id.ns_id].crate_name, name)
        };
        if env.type_status_sys(&type_id.full_name(&env.library))
            .ignored()
        {
            Err(TypeError::Ignored(name_with_prefix))
        } else {
            Ok(name_with_prefix)
        }
    }
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::env;
use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};
use std::process::Command;

fn main() {
	let kernel_src = if has_feature("with_submodule") {
		KernelSrc::from_submodule()
	} else {
		KernelSrc::download()
	};

	kernel_src.build();
}

struct KernelSrc {
	src_dir: PathBuf,
}

impl KernelSrc {
	fn from_submodule() -> Self {
		let mut src_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
		src_dir.set_file_name("libhermit-rs");
		Self { src_dir }
	}

	fn download() -> Self {
		let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
		let src_dir = out_dir.join("rusty-hermit");

		if !src_dir.exists() {
			let status = Command::new("cargo")
				.current_dir(out_dir)
				.arg("download")
				.arg("--output")
				.arg(&src_dir)
				.arg("--extract")
				.arg("rusty-hermit")
				.status()
				.expect("failed to start kernel download");
			assert!(
				status.success(),
				"Unable to download rusty-hermit. Is cargo-download installed?"
			);
		}

		Self { src_dir }
	}

	fn build(self) {
		let target_dir = {
			let mut target_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
			target_dir.push("target");
			target_dir
		};
		let manifest_path = self.src_dir.join("Cargo.toml");
		assert!(
			manifest_path.exists(),
			"kernel manifest path `{}` does not exist",
			manifest_path.display()
		);
		let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
		let profile = env::var("PROFILE").expect("PROFILE was not set");
		let mut cmd = Command::new("cargo");

		let kernel_triple = match target_arch.as_str() {
			"x86_64" => "x86_64-unknown-none-hermitkernel",
			"aarch64" => "aarch64-unknown-none-hermitkernel",
			_ => panic!("Unsupported target arch: {}", target_arch),
		};

		cmd.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg(kernel_triple)
			.arg("--manifest-path")
			.arg("Cargo.toml");

		cmd.current_dir(&self.src_dir);

		cmd.env_remove("RUSTUP_TOOLCHAIN");
		if option_env!("RUSTC_WORKSPACE_WRAPPER")
			.unwrap_or_default()
			.ends_with("clippy-driver")
		{
			cmd.env("RUSTC_WORKSPACE_WRAPPER", "clippy-driver");
		}

		cmd.env("CARGO_TERM_COLOR", "always");

		cmd.arg("--target-dir").arg(&target_dir);

		if profile == "release" {
			cmd.arg("--release");
		}

		// Control enabled features via this crate's features
		cmd.arg("--no-default-features");
		forward_features(
			&mut cmd,
			["acpi", "fsgsbase", "pci", "smp", "vga"].into_iter(),
		);

		let mut rustflags = vec!["-Zmutable-noalias=no".to_string()];
		let outer_rustflags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap();

		if has_feature("instrument") {
			rustflags.push("-Zinstrument-mcount".to_string());
			// Add outer rustflags to command
			rustflags.push(outer_rustflags);
		} else {
			// If the `instrument` feature feature is not enabled,
			// filter it from outer rustflags before adding them to the command.
			if !outer_rustflags.is_empty() {
				let flags = outer_rustflags
					.split('\x1f')
					.filter(|&flag| !flag.contains("instrument-mcount"))
					.map(String::from);
				rustflags.extend(flags);
			}
		}

		cmd.env("CARGO_ENCODED_RUSTFLAGS", rustflags.join("\x1f"));

		let status = cmd.status().expect("failed to start kernel build");
		assert!(status.success());

		let lib_location = target_dir
			.join(kernel_triple)
			.join(&profile)
			.canonicalize()
			.unwrap();

		println!("Lib location: {}", lib_location.display());

		let lib = lib_location.join("libhermit.a");

		let mut symbols = vec!["rust_begin_unwind", "rust_oom"];

		if target_arch == "aarch64" {
			symbols.extend(include_str!("aarch64-duplicate-symbols").lines());
		} else if target_arch == "x86_64" {
			symbols.extend(include_str!("x86_64-duplicate-symbols").lines());
		}

		// Kernel and user space has its own versions of panic handler, oom handler, memcpy, memset, etc,
		// Consequently, we rename the functions in the libos to avoid collisions.
		rename_symbols(symbols.iter(), &lib);

		println!("cargo:rustc-link-search=native={}", lib_location.display());
		println!("cargo:rustc-link-lib=static=hermit");

		println!("cargo:rerun-if-changed={}", self.src_dir.display());
		// HERMIT_LOG_LEVEL_FILTER sets the log level filter at compile time
		println!("cargo:rerun-if-env-changed=HERMIT_LOG_LEVEL_FILTER");
	}
}

fn has_feature(feature: &str) -> bool {
	let mut var = "CARGO_FEATURE_".to_string();

	var.extend(feature.chars().map(|c| match c {
		'-' => '_',
		c => c.to_ascii_uppercase(),
	}));

	env::var_os(&var).is_some()
}

fn forward_features<'a>(cmd: &mut Command, features: impl Iterator<Item = &'a str>) {
	let features = features.filter(|f| has_feature(f)).collect::<Vec<_>>();
	if !features.is_empty() {
		cmd.arg("--features");
		cmd.arg(features.join(" "));
	}
}

fn rename_symbols(symbols: impl Iterator<Item = impl AsRef<OsStr>>, lib: &Path) {
	let args = symbols.into_iter().flat_map(|symbol| {
		let option = OsStr::new("--redefine-sym");
		let arg = [symbol.as_ref(), "=kernel-".as_ref(), symbol.as_ref()]
			.into_iter()
			.collect::<OsString>();
		[Cow::Borrowed(option), Cow::Owned(arg)]
	});

	let status = Command::new("rust-objcopy")
		.args(args)
		.arg(lib)
		.status()
		.expect("Failed to execute rust-objcopy. Is cargo-binutils installed?");
	assert!(status.success(), "rust-objcopy was not successful");
}
<commit_msg>build.rs: Check complete target triple<commit_after>use std::borrow::Cow;
use std::env;
use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};
use std::process::Command;

fn main() {
	let kernel_src = if has_feature("with_submodule") {
		KernelSrc::from_submodule()
	} else {
		KernelSrc::download()
	};

	kernel_src.build();
}

struct KernelSrc {
	src_dir: PathBuf,
}

impl KernelSrc {
	fn from_submodule() -> Self {
		let mut src_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
		src_dir.set_file_name("libhermit-rs");
		Self { src_dir }
	}

	fn download() -> Self {
		let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
		let src_dir = out_dir.join("rusty-hermit");

		if !src_dir.exists() {
			let status = Command::new("cargo")
				.current_dir(out_dir)
				.arg("download")
				.arg("--output")
				.arg(&src_dir)
				.arg("--extract")
				.arg("rusty-hermit")
				.status()
				.expect("failed to start kernel download");
			assert!(
				status.success(),
				"Unable to download rusty-hermit. Is cargo-download installed?"
			);
		}

		Self { src_dir }
	}

	fn build(self) {
		let target_dir = {
			let mut target_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
			target_dir.push("target");
			target_dir
		};
		let manifest_path = self.src_dir.join("Cargo.toml");
		assert!(
			manifest_path.exists(),
			"kernel manifest path `{}` does not exist",
			manifest_path.display()
		);
		let user_target = env::var("TARGET").unwrap();
		let profile = env::var("PROFILE").expect("PROFILE was not set");
		let mut cmd = Command::new("cargo");

		let kernel_target = match user_target.as_str() {
			"x86_64-unknown-hermit" => "x86_64-unknown-none-hermitkernel",
			"aarch64-unknonw-hermit" => "aarch64-unknown-none-hermitkernel",
			_ => panic!("Unsupported target: {}", user_target),
		};

		cmd.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg(kernel_target)
			.arg("--manifest-path")
			.arg("Cargo.toml");

		cmd.current_dir(&self.src_dir);

		cmd.env_remove("RUSTUP_TOOLCHAIN");
		if option_env!("RUSTC_WORKSPACE_WRAPPER")
			.unwrap_or_default()
			.ends_with("clippy-driver")
		{
			cmd.env("RUSTC_WORKSPACE_WRAPPER", "clippy-driver");
		}

		cmd.env("CARGO_TERM_COLOR", "always");

		cmd.arg("--target-dir").arg(&target_dir);

		if profile == "release" {
			cmd.arg("--release");
		}

		// Control enabled features via this crate's features
		cmd.arg("--no-default-features");
		forward_features(
			&mut cmd,
			["acpi", "fsgsbase", "pci", "smp", "vga"].into_iter(),
		);

		let mut rustflags = vec!["-Zmutable-noalias=no".to_string()];
		let outer_rustflags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap();

		if has_feature("instrument") {
			rustflags.push("-Zinstrument-mcount".to_string());
			// Add outer rustflags to command
			rustflags.push(outer_rustflags);
		} else {
			// If the `instrument` feature feature is not enabled,
			// filter it from outer rustflags before adding them to the command.
			if !outer_rustflags.is_empty() {
				let flags = outer_rustflags
					.split('\x1f')
					.filter(|&flag| !flag.contains("instrument-mcount"))
					.map(String::from);
				rustflags.extend(flags);
			}
		}

		cmd.env("CARGO_ENCODED_RUSTFLAGS", rustflags.join("\x1f"));

		let status = cmd.status().expect("failed to start kernel build");
		assert!(status.success());

		let lib_location = target_dir
			.join(kernel_target)
			.join(&profile)
			.canonicalize()
			.unwrap();

		println!("Lib location: {}", lib_location.display());

		let lib = lib_location.join("libhermit.a");

		let mut symbols = vec!["rust_begin_unwind", "rust_oom"];

		match kernel_target {
			"x86_64-unknown-none-hermitkernel" => {
				symbols.extend(include_str!("x86_64-duplicate-symbols").lines())
			}
			"aarch64-unknown-none-hermitkernel" => {
				symbols.extend(include_str!("aarch64-duplicate-symbols").lines())
			}
			_ => (),
		}

		// Kernel and user space has its own versions of panic handler, oom handler, memcpy, memset, etc,
		// Consequently, we rename the functions in the libos to avoid collisions.
		rename_symbols(symbols.iter(), &lib);

		println!("cargo:rustc-link-search=native={}", lib_location.display());
		println!("cargo:rustc-link-lib=static=hermit");

		println!("cargo:rerun-if-changed={}", self.src_dir.display());
		// HERMIT_LOG_LEVEL_FILTER sets the log level filter at compile time
		println!("cargo:rerun-if-env-changed=HERMIT_LOG_LEVEL_FILTER");
	}
}

fn has_feature(feature: &str) -> bool {
	let mut var = "CARGO_FEATURE_".to_string();

	var.extend(feature.chars().map(|c| match c {
		'-' => '_',
		c => c.to_ascii_uppercase(),
	}));

	env::var_os(&var).is_some()
}

fn forward_features<'a>(cmd: &mut Command, features: impl Iterator<Item = &'a str>) {
	let features = features.filter(|f| has_feature(f)).collect::<Vec<_>>();
	if !features.is_empty() {
		cmd.arg("--features");
		cmd.arg(features.join(" "));
	}
}

fn rename_symbols(symbols: impl Iterator<Item = impl AsRef<OsStr>>, lib: &Path) {
	let args = symbols.into_iter().flat_map(|symbol| {
		let option = OsStr::new("--redefine-sym");
		let arg = [symbol.as_ref(), "=kernel-".as_ref(), symbol.as_ref()]
			.into_iter()
			.collect::<OsString>();
		[Cow::Borrowed(option), Cow::Owned(arg)]
	});

	let status = Command::new("rust-objcopy")
		.args(args)
		.arg(lib)
		.status()
		.expect("Failed to execute rust-objcopy. Is cargo-binutils installed?");
	assert!(status.success(), "rust-objcopy was not successful");
}
<|endoftext|>


<commit_before>use core::sync::atomic::{AtomicUsize, Ordering};

use alloc::vec::Vec;

use crate::context::timeout;
use crate::device::{local_apic, ioapic, pic};
use crate::device::serial::{COM1, COM2};
use crate::ipi::{ipi, IpiKind, IpiTarget};
use crate::scheme::debug::debug_input;
use crate::{context, ptrace, time};

//resets to 0 in context::switch()
#[thread_local]
pub static PIT_TICKS: AtomicUsize = AtomicUsize::new(0);

// The only way to read PS2 data without race conditions is to allow a keyboard interrupt to happen
// and then read data while reading mouse data, since keyboard data overrides mouse data and
// reading the status register is not done atomically with reading the data. This is not possible
// from userspace, so we do this minimal part of the PS2 driver in the kernel.
#[inline(always)]
unsafe fn ps2_interrupt(index: usize) {
    use crate::scheme::serio::serio_input;

    let data: u8;
    let status: u8;
    asm!("
        sti
        nop
        cli
        in al, 0x64
        mov ah, al
        in al, 0x60
        "
        : "={al}"(data), "={ah}"(status)
        :
        : "memory"
        : "intel", "volatile"
    );

    if status & 1 != 0 {
        let status_index = if status & (1 << 5) == 0 {
            // Keyboard, according to status
            0
        } else {
            // Mouse, according to status
            1
        };
        serio_input(status_index, data);
    }
}

#[repr(u8)]
pub enum IrqMethod {
    Pic = 0,
    Apic = 1,
}

static SPURIOUS_COUNT_IRQ7: AtomicUsize = AtomicUsize::new(0);
static SPURIOUS_COUNT_IRQ15: AtomicUsize = AtomicUsize::new(0);

pub fn spurious_count_irq7() -> usize {
    SPURIOUS_COUNT_IRQ7.load(Ordering::Relaxed)
}
pub fn spurious_count_irq15() -> usize {
    SPURIOUS_COUNT_IRQ15.load(Ordering::Relaxed)
}
pub fn spurious_count() -> usize {
    spurious_count_irq7() + spurious_count_irq15()
}
pub fn spurious_irq_resource() -> syscall::Result<Vec<u8>> {
    match irq_method() {
        IrqMethod::Apic => Ok(Vec::from(&b"(not implemented for APIC yet)"[..])),
        IrqMethod::Pic => {
            Ok(format!("{}\tIRQ7\n{}\tIRQ15\n{}\ttotal\n", spurious_count_irq7(), spurious_count_irq15(), spurious_count()).into_bytes())
        }
    }
}

static IRQ_METHOD: AtomicUsize = AtomicUsize::new(IrqMethod::Pic as usize);

pub fn set_irq_method(method: IrqMethod) {
    IRQ_METHOD.store(method as usize, core::sync::atomic::Ordering::Release);
}

fn irq_method() -> IrqMethod {
    let raw = IRQ_METHOD.load(core::sync::atomic::Ordering::Acquire);

    match raw {
        0 => IrqMethod::Pic,
        1 => IrqMethod::Apic,
        _ => unreachable!(),
    }
}

extern {
    // triggers irq scheme
    fn irq_trigger(irq: u8);
}

/// Notify the IRQ scheme that an IRQ has been registered. This should mask the IRQ until the
/// scheme user unmasks it ("acknowledges" it).
unsafe fn trigger(irq: u8) {
    match irq_method() {
        IrqMethod::Pic => if irq < 16 { pic_mask(irq) },
        IrqMethod::Apic => ioapic_mask(irq),
    }
    irq_trigger(irq);
}

/// Unmask the IRQ. This is called from the IRQ scheme, which does this when a user process has
/// processed the IRQ.
pub unsafe fn acknowledge(irq: usize) {
    match irq_method() {
        IrqMethod::Pic => if irq < 16 { pic_unmask(irq) },
        IrqMethod::Apic => ioapic_unmask(irq),
    }
}

/// Sends an end-of-interrupt, so that the interrupt controller can go on to the next one.
pub unsafe fn eoi(irq: u8) {
    match irq_method() {
        IrqMethod::Pic => if irq < 16 { pic_eoi(irq) },
        IrqMethod::Apic => lapic_eoi(),
    }
}

unsafe fn pic_mask(irq: u8) {
    debug_assert!(irq < 16);

    if irq >= 8 {
        pic::SLAVE.mask_set(irq - 8);
    } else {
        pic::MASTER.mask_set(irq);
    }
}

unsafe fn ioapic_mask(irq: u8) {
    ioapic::mask(irq);
}

unsafe fn pic_eoi(irq: u8) {
    debug_assert!(irq < 16);

    if irq >= 8 {
        pic::MASTER.ack();
        pic::SLAVE.ack();
    } else {
        pic::MASTER.ack();
    }
}

unsafe fn lapic_eoi() {
    local_apic::LOCAL_APIC.eoi()
}

unsafe fn pic_unmask(irq: usize) {
    debug_assert!(irq < 16);

    if irq >= 8 {
        pic::SLAVE.mask_clear(irq as u8 - 8);
    } else {
        pic::MASTER.mask_clear(irq as u8);
    }
}

unsafe fn ioapic_unmask(irq: usize) {
    ioapic::unmask(irq as u8);
}

interrupt_stack!(pit, stack, {
    // Saves CPU time by not sending IRQ event irq_trigger(0);

    const PIT_RATE: u64 = 2_250_286;

    {
        let mut offset = time::OFFSET.lock();
        let sum = offset.1 + PIT_RATE;
        offset.1 = sum % 1_000_000_000;
        offset.0 += sum / 1_000_000_000;
    }

    eoi(0);

    // Wake up other CPUs
    ipi(IpiKind::Pit, IpiTarget::Other);

    // Any better way of doing this?
    timeout::trigger();

    if PIT_TICKS.fetch_add(1, Ordering::SeqCst) >= 10 {
        let _guard = ptrace::set_process_regs(stack);
        let _ = context::switch();
    }
});

interrupt!(keyboard, {
    ps2_interrupt(0);
    eoi(1);
});

interrupt!(cascade, {
    // No need to do any operations on cascade
    eoi(2);
});

interrupt!(com2, {
    while let Some(c) = COM2.lock().receive() {
        debug_input(c);
    }
    eoi(3);
});

interrupt!(com1, {
    while let Some(c) = COM1.lock().receive() {
        debug_input(c);
    }
    eoi(4);
});

interrupt!(lpt2, {
    trigger(5);
    eoi(5);
});

interrupt!(floppy, {
    trigger(6);
    eoi(6);
});

interrupt!(lpt1, {
    if pic::MASTER.isr() & (1 << 7) == 0 {
        // the IRQ was spurious, ignore it but increment a counter.
        SPURIOUS_COUNT_IRQ7.fetch_add(1, Ordering::Relaxed);
        return;
    }
    trigger(7);
    eoi(7);
});

interrupt!(rtc, {
    trigger(8);
    eoi(8);
});

interrupt!(pci1, {
    trigger(9);
    eoi(9);
});

interrupt!(pci2, {
    trigger(10);
    eoi(10);
});

interrupt!(pci3, {
    trigger(11);
    eoi(11);
});

interrupt!(mouse, {
    ps2_interrupt(1);
    eoi(12);
});

interrupt!(fpu, {
    trigger(13);
    eoi(13);
});

interrupt!(ata1, {
    trigger(14);
    eoi(14);
});

interrupt!(ata2, {
    if pic::SLAVE.isr() & (1 << 7) == 0 {
        SPURIOUS_COUNT_IRQ15.fetch_add(1, Ordering::Relaxed);
        pic::MASTER.ack();
        return
    }
    trigger(15);
    eoi(15);
});

interrupt!(lapic_timer, {
    println!("Local apic timer interrupt");
    lapic_eoi();
});

interrupt!(lapic_error, {
    println!("Local apic internal error: ESR={:#0x}", local_apic::LOCAL_APIC.esr());
    lapic_eoi();
});

interrupt!(calib_pit, {
    const PIT_RATE: u64 = 2_250_286;

    {
        let mut offset = time::OFFSET.lock();
        let sum = offset.1 + PIT_RATE;
        offset.1 = sum % 1_000_000_000;
        offset.0 += sum / 1_000_000_000;
    }

    eoi(0);
});
// XXX: This would look way prettier using const generics.

macro_rules! allocatable_irq(
    ( $number:literal, $name:ident ) => {
        interrupt!($name, {
            allocatable_irq_generic($number);
        });
    }
);

pub unsafe fn allocatable_irq_generic(number: u8) {
    irq_trigger(number - 32);
    lapic_eoi();
}

define_default_irqs!();
<commit_msg>Use the correct interrupt method for spurious IRQs.<commit_after>use core::sync::atomic::{AtomicUsize, Ordering};

use alloc::vec::Vec;

use crate::context::timeout;
use crate::device::{local_apic, ioapic, pic};
use crate::device::serial::{COM1, COM2};
use crate::ipi::{ipi, IpiKind, IpiTarget};
use crate::scheme::debug::debug_input;
use crate::{context, ptrace, time};

//resets to 0 in context::switch()
#[thread_local]
pub static PIT_TICKS: AtomicUsize = AtomicUsize::new(0);

// The only way to read PS2 data without race conditions is to allow a keyboard interrupt to happen
// and then read data while reading mouse data, since keyboard data overrides mouse data and
// reading the status register is not done atomically with reading the data. This is not possible
// from userspace, so we do this minimal part of the PS2 driver in the kernel.
#[inline(always)]
unsafe fn ps2_interrupt(index: usize) {
    use crate::scheme::serio::serio_input;

    let data: u8;
    let status: u8;
    asm!("
        sti
        nop
        cli
        in al, 0x64
        mov ah, al
        in al, 0x60
        "
        : "={al}"(data), "={ah}"(status)
        :
        : "memory"
        : "intel", "volatile"
    );

    if status & 1 != 0 {
        let status_index = if status & (1 << 5) == 0 {
            // Keyboard, according to status
            0
        } else {
            // Mouse, according to status
            1
        };
        serio_input(status_index, data);
    }
}

#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum IrqMethod {
    Pic = 0,
    Apic = 1,
}

static SPURIOUS_COUNT_IRQ7: AtomicUsize = AtomicUsize::new(0);
static SPURIOUS_COUNT_IRQ15: AtomicUsize = AtomicUsize::new(0);

pub fn spurious_count_irq7() -> usize {
    SPURIOUS_COUNT_IRQ7.load(Ordering::Relaxed)
}
pub fn spurious_count_irq15() -> usize {
    SPURIOUS_COUNT_IRQ15.load(Ordering::Relaxed)
}
pub fn spurious_count() -> usize {
    spurious_count_irq7() + spurious_count_irq15()
}
pub fn spurious_irq_resource() -> syscall::Result<Vec<u8>> {
    match irq_method() {
        IrqMethod::Apic => Ok(Vec::from(&b"(not implemented for APIC yet)"[..])),
        IrqMethod::Pic => {
            Ok(format!("{}\tIRQ7\n{}\tIRQ15\n{}\ttotal\n", spurious_count_irq7(), spurious_count_irq15(), spurious_count()).into_bytes())
        }
    }
}

static IRQ_METHOD: AtomicUsize = AtomicUsize::new(IrqMethod::Pic as usize);

pub fn set_irq_method(method: IrqMethod) {
    IRQ_METHOD.store(method as usize, core::sync::atomic::Ordering::Release);
}

fn irq_method() -> IrqMethod {
    let raw = IRQ_METHOD.load(core::sync::atomic::Ordering::Acquire);

    match raw {
        0 => IrqMethod::Pic,
        1 => IrqMethod::Apic,
        _ => unreachable!(),
    }
}

extern {
    // triggers irq scheme
    fn irq_trigger(irq: u8);
}

/// Notify the IRQ scheme that an IRQ has been registered. This should mask the IRQ until the
/// scheme user unmasks it ("acknowledges" it).
unsafe fn trigger(irq: u8) {
    match irq_method() {
        IrqMethod::Pic => if irq < 16 { pic_mask(irq) },
        IrqMethod::Apic => ioapic_mask(irq),
    }
    irq_trigger(irq);
}

/// Unmask the IRQ. This is called from the IRQ scheme, which does this when a user process has
/// processed the IRQ.
pub unsafe fn acknowledge(irq: usize) {
    match irq_method() {
        IrqMethod::Pic => if irq < 16 { pic_unmask(irq) },
        IrqMethod::Apic => ioapic_unmask(irq),
    }
}

/// Sends an end-of-interrupt, so that the interrupt controller can go on to the next one.
pub unsafe fn eoi(irq: u8) {
    match irq_method() {
        IrqMethod::Pic => if irq < 16 { pic_eoi(irq) },
        IrqMethod::Apic => lapic_eoi(),
    }
}

unsafe fn pic_mask(irq: u8) {
    debug_assert!(irq < 16);

    if irq >= 8 {
        pic::SLAVE.mask_set(irq - 8);
    } else {
        pic::MASTER.mask_set(irq);
    }
}

unsafe fn ioapic_mask(irq: u8) {
    ioapic::mask(irq);
}

unsafe fn pic_eoi(irq: u8) {
    debug_assert!(irq < 16);

    if irq >= 8 {
        pic::MASTER.ack();
        pic::SLAVE.ack();
    } else {
        pic::MASTER.ack();
    }
}

unsafe fn lapic_eoi() {
    local_apic::LOCAL_APIC.eoi()
}

unsafe fn pic_unmask(irq: usize) {
    debug_assert!(irq < 16);

    if irq >= 8 {
        pic::SLAVE.mask_clear(irq as u8 - 8);
    } else {
        pic::MASTER.mask_clear(irq as u8);
    }
}

unsafe fn ioapic_unmask(irq: usize) {
    ioapic::unmask(irq as u8);
}

interrupt_stack!(pit, stack, {
    // Saves CPU time by not sending IRQ event irq_trigger(0);

    const PIT_RATE: u64 = 2_250_286;

    {
        let mut offset = time::OFFSET.lock();
        let sum = offset.1 + PIT_RATE;
        offset.1 = sum % 1_000_000_000;
        offset.0 += sum / 1_000_000_000;
    }

    eoi(0);

    // Wake up other CPUs
    ipi(IpiKind::Pit, IpiTarget::Other);

    // Any better way of doing this?
    timeout::trigger();

    if PIT_TICKS.fetch_add(1, Ordering::SeqCst) >= 10 {
        let _guard = ptrace::set_process_regs(stack);
        let _ = context::switch();
    }
});

interrupt!(keyboard, {
    ps2_interrupt(0);
    eoi(1);
});

interrupt!(cascade, {
    // No need to do any operations on cascade
    eoi(2);
});

interrupt!(com2, {
    while let Some(c) = COM2.lock().receive() {
        debug_input(c);
    }
    eoi(3);
});

interrupt!(com1, {
    while let Some(c) = COM1.lock().receive() {
        debug_input(c);
    }
    eoi(4);
});

interrupt!(lpt2, {
    trigger(5);
    eoi(5);
});

interrupt!(floppy, {
    trigger(6);
    eoi(6);
});

interrupt!(lpt1, {
    if irq_method() == IrqMethod::Pic && pic::MASTER.isr() & (1 << 7) == 0 {
        // the IRQ was spurious, ignore it but increment a counter.
        SPURIOUS_COUNT_IRQ7.fetch_add(1, Ordering::Relaxed);
        return;
    }
    trigger(7);
    eoi(7);
});

interrupt!(rtc, {
    trigger(8);
    eoi(8);
});

interrupt!(pci1, {
    trigger(9);
    eoi(9);
});

interrupt!(pci2, {
    trigger(10);
    eoi(10);
});

interrupt!(pci3, {
    trigger(11);
    eoi(11);
});

interrupt!(mouse, {
    ps2_interrupt(1);
    eoi(12);
});

interrupt!(fpu, {
    trigger(13);
    eoi(13);
});

interrupt!(ata1, {
    trigger(14);
    eoi(14);
});

interrupt!(ata2, {
    if irq_method() == IrqMethod::Pic && pic::SLAVE.isr() & (1 << 7) == 0 {
        SPURIOUS_COUNT_IRQ15.fetch_add(1, Ordering::Relaxed);
        pic::MASTER.ack();
        return
    }
    trigger(15);
    eoi(15);
});

interrupt!(lapic_timer, {
    println!("Local apic timer interrupt");
    lapic_eoi();
});

interrupt!(lapic_error, {
    println!("Local apic internal error: ESR={:#0x}", local_apic::LOCAL_APIC.esr());
    lapic_eoi();
});

interrupt!(calib_pit, {
    const PIT_RATE: u64 = 2_250_286;

    {
        let mut offset = time::OFFSET.lock();
        let sum = offset.1 + PIT_RATE;
        offset.1 = sum % 1_000_000_000;
        offset.0 += sum / 1_000_000_000;
    }

    eoi(0);
});
// XXX: This would look way prettier using const generics.

macro_rules! allocatable_irq(
    ( $number:literal, $name:ident ) => {
        interrupt!($name, {
            allocatable_irq_generic($number);
        });
    }
);

pub unsafe fn allocatable_irq_generic(number: u8) {
    irq_trigger(number - 32);
    lapic_eoi();
}

define_default_irqs!();
<|endoftext|>