<commit_before>// Copyright 2015 Intecture Developers. See the COPYRIGHT file at the
// top-level directory of this distribution and at
// https://intecture.io/COPYRIGHT.
//
// Licensed under the Mozilla Public License 2.0 <LICENSE or
// https://www.tldrlegal.com/l/mpl-2.0>. This file may not be copied,
// modified, or distributed except according to those terms.

use {CommandResult, Result};
use error::Error;
use file::FileOwner;
use regex::Regex;
use std::{process, str};
use std::io::prelude::*;
use std::fs::File;
use target::bin_resolver::BinResolver;
use target::default_base as default;
use telemetry::Netif;

pub fn file_get_owner(path: &str) -> Result<FileOwner> {
    Ok(FileOwner {
        user_name: try!(default::file_stat(path, vec!["-c", "%U"])),
        user_uid: try!(default::file_stat(path, vec!["-c", "%u"])).parse::<u64>().unwrap(),
        group_name: try!(default::file_stat(path, vec!["-c", "%G"])),
        group_gid: try!(default::file_stat(path, vec!["-c", "%g"])).parse::<u64>().unwrap()
    })
}

pub fn file_get_mode(path: &str) -> Result<u16> {
    Ok(try!(default::file_stat(path, vec!["-c", "%a"])).parse::<u16>().unwrap())
}

pub fn using_systemd() -> Result<bool> {
    let output = process::Command::new(&try!(BinResolver::resolve("pidof"))).arg("systemd").output().unwrap();
    Ok(output.status.success())
}

pub fn service_systemd(name: &str, action: &str) -> Result<CommandResult> {
    default::command_exec(&format!("{} {} SERVICE.{}", &try!(BinResolver::resolve("systemctl")), action, name))
}

pub fn memory() -> Result<u64> {
    let output = process::Command::new(&try!(BinResolver::resolve("free"))).arg("-b").output().unwrap();

    if !output.status.success() {
        return Err(Error::Generic("Could not determine memory".to_string()));
    }

    let regex = Regex::new(r"(?m)^Mem:\s+([0-9]+)").unwrap();
    let capture = regex.captures(try!(str::from_utf8(&output.stdout)).trim());

    if capture.is_some() {
        Ok(capture.unwrap().at(1).unwrap().parse::<u64>().unwrap())
    } else {
        Err(Error::Generic("Invalid memory output".to_string()))
    }
}

pub fn cpu_vendor() -> Result<String> {
    get_cpu_item("vendor_id")
}

pub fn cpu_brand_string() -> Result<String> {
    get_cpu_item("model name")
}

pub fn cpu_cores() -> Result<u32> {
    Ok(try!(try!(get_cpu_item("cpu cores")).parse::<u32>()))
}

fn get_cpu_item(item: &str) -> Result<String> {
    // XXX This result should be cached
    let mut cpuinfo_f = try!(File::open("/proc/cpuinfo"));
    let mut cpuinfo = String::new();
    try!(cpuinfo_f.read_to_string(&mut cpuinfo));

    let pattern = format!(r"(?m)^{}\s+: (.+)$", item);
    let regex = Regex::new(&pattern).unwrap();
    let capture = regex.captures(&cpuinfo);

    if capture.is_some() {
        Ok(capture.unwrap().at(1).unwrap().to_string())
    } else {
        Err(Error::Generic(format!("Could not find CPU item: {}", item)))
    }
}

pub fn net() -> Result<Vec<Netif>> {
    let if_pattern = r"(?m)^(?P<if>[a-z0-9]+)\s+Link encap:(Ethernet|Local Loopback)(?P<content>(?s).+?)\n\n";
    let kv_pattern = r"^\s+(?P<key>[A-Za-z0-9]+)(?:\s|:)(?P<value>.+)";
    let ipv4_pattern = r"^addr:(?P<ip>(?:[0-9]{1,3}\.){3}[0-9]{1,3})\s+(?:Bcast:[0-9.]{7,15}\s+)?Mask:(?P<mask>(?:[0-9]{1,3}\.){3}[0-9]{1,3})";
    let ipv6_pattern = r"^addr:\s*(?P<ip>(?:[a-f0-9]{4}::(?:[a-f0-9]{1,4}:){3}[a-f0-9]{1,4})|(?:::1))/(?P<prefix>[0-9]+)\s+Scope:(?P<scope>[A-Za-z0-9]+)";
    default::parse_net(if_pattern, kv_pattern, ipv4_pattern, ipv6_pattern)
}

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

    #[test]
    fn test_memory() {
        // XXX Not a proper test. Requires mocking.
        assert!(memory().is_ok());
    }

    #[test]
    fn test_cpu_vendor() {
        // XXX Not a proper test. Requires mocking.
        assert!(cpu_vendor().is_ok());
    }

    #[test]
    fn test_cpu_brand_string() {
        // XXX Not a proper test. Requires mocking.
        assert!(cpu_brand_string().is_ok());
    }

    #[test]
    fn test_cpu_cores() {
        // XXX Not a proper test. Requires mocking.
        assert!(cpu_cores().is_ok());
    }

    #[test]
    fn test_get_cpu_item_fail() {
        assert!(get_cpu_item("moocow").is_err());
    }

    #[test]
    fn test_net() {
        // XXX Not a proper test. Requires mocking.
        assert!(net().is_ok());
    }
}
<commit_msg>Fixed bug with systemd support<commit_after>// Copyright 2015 Intecture Developers. See the COPYRIGHT file at the
// top-level directory of this distribution and at
// https://intecture.io/COPYRIGHT.
//
// Licensed under the Mozilla Public License 2.0 <LICENSE or
// https://www.tldrlegal.com/l/mpl-2.0>. This file may not be copied,
// modified, or distributed except according to those terms.

use {CommandResult, Result};
use error::Error;
use file::FileOwner;
use regex::Regex;
use std::{process, str};
use std::io::prelude::*;
use std::fs::File;
use target::bin_resolver::BinResolver;
use target::default_base as default;
use telemetry::Netif;

pub fn file_get_owner(path: &str) -> Result<FileOwner> {
    Ok(FileOwner {
        user_name: try!(default::file_stat(path, vec!["-c", "%U"])),
        user_uid: try!(default::file_stat(path, vec!["-c", "%u"])).parse::<u64>().unwrap(),
        group_name: try!(default::file_stat(path, vec!["-c", "%G"])),
        group_gid: try!(default::file_stat(path, vec!["-c", "%g"])).parse::<u64>().unwrap()
    })
}

pub fn file_get_mode(path: &str) -> Result<u16> {
    Ok(try!(default::file_stat(path, vec!["-c", "%a"])).parse::<u16>().unwrap())
}

pub fn using_systemd() -> Result<bool> {
    let output = process::Command::new(&try!(BinResolver::resolve("pidof"))).arg("systemd").output().unwrap();
    Ok(output.status.success())
}

pub fn service_systemd(name: &str, action: &str) -> Result<CommandResult> {
    default::command_exec(&format!("{} {} {}", &try!(BinResolver::resolve("systemctl")), action, name))
}

pub fn memory() -> Result<u64> {
    let output = process::Command::new(&try!(BinResolver::resolve("free"))).arg("-b").output().unwrap();

    if !output.status.success() {
        return Err(Error::Generic("Could not determine memory".to_string()));
    }

    let regex = Regex::new(r"(?m)^Mem:\s+([0-9]+)").unwrap();
    let capture = regex.captures(try!(str::from_utf8(&output.stdout)).trim());

    if capture.is_some() {
        Ok(capture.unwrap().at(1).unwrap().parse::<u64>().unwrap())
    } else {
        Err(Error::Generic("Invalid memory output".to_string()))
    }
}

pub fn cpu_vendor() -> Result<String> {
    get_cpu_item("vendor_id")
}

pub fn cpu_brand_string() -> Result<String> {
    get_cpu_item("model name")
}

pub fn cpu_cores() -> Result<u32> {
    Ok(try!(try!(get_cpu_item("cpu cores")).parse::<u32>()))
}

fn get_cpu_item(item: &str) -> Result<String> {
    // XXX This result should be cached
    let mut cpuinfo_f = try!(File::open("/proc/cpuinfo"));
    let mut cpuinfo = String::new();
    try!(cpuinfo_f.read_to_string(&mut cpuinfo));

    let pattern = format!(r"(?m)^{}\s+: (.+)$", item);
    let regex = Regex::new(&pattern).unwrap();
    let capture = regex.captures(&cpuinfo);

    if capture.is_some() {
        Ok(capture.unwrap().at(1).unwrap().to_string())
    } else {
        Err(Error::Generic(format!("Could not find CPU item: {}", item)))
    }
}

pub fn net() -> Result<Vec<Netif>> {
    let if_pattern = r"(?m)^(?P<if>[a-z0-9]+)\s+Link encap:(Ethernet|Local Loopback)(?P<content>(?s).+?)\n\n";
    let kv_pattern = r"^\s+(?P<key>[A-Za-z0-9]+)(?:\s|:)(?P<value>.+)";
    let ipv4_pattern = r"^addr:(?P<ip>(?:[0-9]{1,3}\.){3}[0-9]{1,3})\s+(?:Bcast:[0-9.]{7,15}\s+)?Mask:(?P<mask>(?:[0-9]{1,3}\.){3}[0-9]{1,3})";
    let ipv6_pattern = r"^addr:\s*(?P<ip>(?:[a-f0-9]{4}::(?:[a-f0-9]{1,4}:){3}[a-f0-9]{1,4})|(?:::1))/(?P<prefix>[0-9]+)\s+Scope:(?P<scope>[A-Za-z0-9]+)";
    default::parse_net(if_pattern, kv_pattern, ipv4_pattern, ipv6_pattern)
}

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

    #[test]
    fn test_memory() {
        // XXX Not a proper test. Requires mocking.
        assert!(memory().is_ok());
    }

    #[test]
    fn test_cpu_vendor() {
        // XXX Not a proper test. Requires mocking.
        assert!(cpu_vendor().is_ok());
    }

    #[test]
    fn test_cpu_brand_string() {
        // XXX Not a proper test. Requires mocking.
        assert!(cpu_brand_string().is_ok());
    }

    #[test]
    fn test_cpu_cores() {
        // XXX Not a proper test. Requires mocking.
        assert!(cpu_cores().is_ok());
    }

    #[test]
    fn test_get_cpu_item_fail() {
        assert!(get_cpu_item("moocow").is_err());
    }

    #[test]
    fn test_net() {
        // XXX Not a proper test. Requires mocking.
        assert!(net().is_ok());
    }
}
<|endoftext|>


<commit_before>//! Diagnostics produced by `hir_def`.

use std::any::Any;

use hir_expand::diagnostics::{Diagnostic, DiagnosticCode};
use syntax::{ast, AstPtr, SyntaxNodePtr};

use hir_expand::{HirFileId, InFile};

#[derive(Debug)]
pub struct UnresolvedModule {
    pub file: HirFileId,
    pub decl: AstPtr<ast::Module>,
    pub candidate: String,
}

impl Diagnostic for UnresolvedModule {
    fn code(&self) -> DiagnosticCode {
        DiagnosticCode("unresolved-module")
    }
    fn message(&self) -> String {
        "unresolved module".to_string()
    }
    fn display_source(&self) -> InFile<SyntaxNodePtr> {
        InFile::new(self.file, self.decl.clone().into())
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
}

#[derive(Debug)]
pub struct UnresolvedExternCrate {
    pub file: HirFileId,
    pub item: AstPtr<ast::ExternCrate>,
}

impl Diagnostic for UnresolvedExternCrate {
    fn code(&self) -> DiagnosticCode {
        DiagnosticCode("unresolved-extern-crate")
    }
    fn message(&self) -> String {
        "unresolved extern crate".to_string()
    }
    fn display_source(&self) -> InFile<SyntaxNodePtr> {
        InFile::new(self.file, self.item.clone().into())
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
}

#[derive(Debug)]
pub struct UnresolvedImport {
    pub file: HirFileId,
    pub node: AstPtr<ast::UseTree>,
}

impl Diagnostic for UnresolvedImport {
    fn code(&self) -> DiagnosticCode {
        DiagnosticCode("unresolved-import")
    }
    fn message(&self) -> String {
        "unresolved import".to_string()
    }
    fn display_source(&self) -> InFile<SyntaxNodePtr> {
        InFile::new(self.file, self.node.clone().into())
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
}
<commit_msg>Mark unresolved imports diagnostic as experimental<commit_after>//! Diagnostics produced by `hir_def`.

use std::any::Any;

use hir_expand::diagnostics::{Diagnostic, DiagnosticCode};
use syntax::{ast, AstPtr, SyntaxNodePtr};

use hir_expand::{HirFileId, InFile};

#[derive(Debug)]
pub struct UnresolvedModule {
    pub file: HirFileId,
    pub decl: AstPtr<ast::Module>,
    pub candidate: String,
}

impl Diagnostic for UnresolvedModule {
    fn code(&self) -> DiagnosticCode {
        DiagnosticCode("unresolved-module")
    }
    fn message(&self) -> String {
        "unresolved module".to_string()
    }
    fn display_source(&self) -> InFile<SyntaxNodePtr> {
        InFile::new(self.file, self.decl.clone().into())
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
}

#[derive(Debug)]
pub struct UnresolvedExternCrate {
    pub file: HirFileId,
    pub item: AstPtr<ast::ExternCrate>,
}

impl Diagnostic for UnresolvedExternCrate {
    fn code(&self) -> DiagnosticCode {
        DiagnosticCode("unresolved-extern-crate")
    }
    fn message(&self) -> String {
        "unresolved extern crate".to_string()
    }
    fn display_source(&self) -> InFile<SyntaxNodePtr> {
        InFile::new(self.file, self.item.clone().into())
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
}

#[derive(Debug)]
pub struct UnresolvedImport {
    pub file: HirFileId,
    pub node: AstPtr<ast::UseTree>,
}

impl Diagnostic for UnresolvedImport {
    fn code(&self) -> DiagnosticCode {
        DiagnosticCode("unresolved-import")
    }
    fn message(&self) -> String {
        "unresolved import".to_string()
    }
    fn display_source(&self) -> InFile<SyntaxNodePtr> {
        InFile::new(self.file, self.node.clone().into())
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
    fn is_experimental(&self) -> bool {
        // This currently results in false positives in the following cases:
        // - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly)
        // - `core::arch` (we don't handle `#[path = "../<path>"]` correctly)
        // - proc macros and/or proc macro generated code
        true
    }
}
<|endoftext|>


<commit_before>// Some crumminess to make sure we link correctly

#[cfg(target_os = "linux")]
#[nolink]
extern mod m { }

#[cfg(target_os = "macos")]
#[link_args = "-L. -lstdc++"]
#[nolink]
extern mod m { }
<commit_msg>Fixed some linking errors<commit_after>// Some crumminess to make sure we link correctly

#[cfg(target_os = "linux")]
#[link_args = "-lpthread -lstdc++"]
#[nolink]
extern mod m { }

#[cfg(target_os = "macos")]
#[link_args = "-L. -lstdc++"]
#[nolink]
extern mod m { }
<|endoftext|>


<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;

/// 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);

    let mut members_defs = Vec::with_capacity(members.len());

    // 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();
                members_defs.push(format!("pub _dummy: [u8; {}]", diff));       // FIXME: fix name if there are multiple dummies
                *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;
        }

        members_defs.push(format!("pub {name}: {ty} /* offset: {offset} */",
                                  name = member_name, ty = ty, offset = 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 {
            members_defs.push(format!("pub _dummy: [u8; {}]", diff));       // FIXME: fix name if there are multiple dummies
        }
    }

    // We can only derive common traits if there's no unsized member in the struct.
    let derive = if current_rust_offset.is_some() {
        "#[derive(Copy, Clone, Debug, Default)]\n"
    } else {
        ""
    };

    format!("#[repr(C)]\n{derive}\
             pub struct {name} {{\n\t{members}\n}} /* total_size: {t:?} */\n",
            derive = derive, name = name, members = members_defs.join(",\n\t"), t = spirv_req_total_size)
}

/// 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 => {
                // FIXME: width
                #[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>())
            },
            &parse::Instruction::TypeFloat { result_id, width } if result_id == searched => {
                // FIXME: width
                #[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>())
            },
            &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>Handle multiple dummy values<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
}

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

    // Strings of each member definition.
    let mut members_defs = 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;
                members_defs.push(format!("pub _dummy{}: [u8; {}]", padding_num, diff));
                *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;
        }

        members_defs.push(format!("pub {name}: {ty} /* offset: {offset} */",
                                  name = member_name, ty = ty, offset = 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 {
            let padding_num = next_padding_num; next_padding_num += 1;
            members_defs.push(format!("pub _dummy{}: [u8; {}]", padding_num, diff));
        }
    }

    // We can only derive common traits if there's no unsized member in the struct.
    let derive = if current_rust_offset.is_some() {
        "#[derive(Copy, Clone, Debug, Default)]\n"
    } else {
        ""
    };

    format!("#[repr(C)]\n{derive}\
             pub struct {name} {{\n\t{members}\n}} /* total_size: {t:?} */\n",
            derive = derive, name = name, members = members_defs.join(",\n\t"), t = spirv_req_total_size)
}

/// 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 => {
                // FIXME: width
                #[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>())
            },
            &parse::Instruction::TypeFloat { result_id, width } if result_id == searched => {
                // FIXME: width
                #[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>())
            },
            &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 2017 The xi-editor 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.

//! Management of styles.

use std::collections::{BTreeMap, HashMap, HashSet};
use std::ffi::OsStr;
use std::fs;
use std::iter::FromIterator;
use std::path::{Path, PathBuf};

use serde_json::{self, Value};
use syntect::dumps::{dump_to_file, from_dump_file};
use syntect::highlighting::StyleModifier as SynStyleModifier;
use syntect::highlighting::{Color, Highlighter, Theme, ThemeSet};
use syntect::LoadingError;

pub use syntect::highlighting::ThemeSettings;

pub const N_RESERVED_STYLES: usize = 8;
const SYNTAX_PRIORITY_DEFAULT: u16 = 200;
const SYNTAX_PRIORITY_LOWEST: u16 = 0;
pub const DEFAULT_THEME: &str = "InspiredGitHub";

#[derive(Clone, PartialEq, Eq, Default, Hash, Debug, Serialize, Deserialize)]
/// A mergeable style. All values except priority are optional.
///
/// Note: A `None` value represents the absense of preference; in the case of
/// boolean options, `Some(false)` means that this style will override a lower
/// priority value in the same field.
pub struct Style {
    /// The priority of this style, in the range (0, 1000). Used to resolve
    /// conflicting fields when merging styles. The higher priority wins.
    #[serde(skip_serializing)]
    pub priority: u16,
    /// The foreground text color, in ARGB.
    pub fg_color: Option<u32>,
    /// The background text color, in ARGB.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub bg_color: Option<u32>,
    /// The font-weight, in the range 100-900, interpreted like the CSS
    /// font-weight property.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub weight: Option<u16>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub underline: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub italic: Option<bool>,
}

impl Style {
    /// Creates a new `Style` by converting from a `Syntect::StyleModifier`.
    pub fn from_syntect_style_mod(style: &SynStyleModifier) -> Self {
        let font_style = style.font_style.map(|s| s.bits()).unwrap_or_default();
        let weight = if (font_style & 1) != 0 { Some(700) } else { None };
        let underline = if (font_style & 2) != 0 { Some(true) } else { None };
        let italic = if (font_style & 4) != 0 { Some(true) } else { None };

        Self::new(
            SYNTAX_PRIORITY_DEFAULT,
            style.foreground.map(Self::rgba_from_syntect_color),
            style.background.map(Self::rgba_from_syntect_color),
            weight,
            underline,
            italic,
        )
    }

    pub fn new<O32, O16, OB>(
        priority: u16,
        fg_color: O32,
        bg_color: O32,
        weight: O16,
        underline: OB,
        italic: OB,
    ) -> Self
    where
        O32: Into<Option<u32>>,
        O16: Into<Option<u16>>,
        OB: Into<Option<bool>>,
    {
        assert!(priority <= 1000);
        Style {
            priority,
            fg_color: fg_color.into(),
            bg_color: bg_color.into(),
            weight: weight.into(),
            underline: underline.into(),
            italic: italic.into(),
        }
    }

    /// Returns the default style for the given `Theme`.
    pub fn default_for_theme(theme: &Theme) -> Self {
        let fg = theme.settings.foreground.unwrap_or(Color::BLACK);
        Style::new(
            SYNTAX_PRIORITY_LOWEST,
            Some(Self::rgba_from_syntect_color(fg)),
            None,
            None,
            None,
            None,
        )
    }

    /// Creates a new style by combining attributes of `self` and `other`.
    /// If both styles define an attribute, the highest priority wins; `other`
    /// wins in the case of a tie.
    ///
    /// Note: when merging multiple styles, apply them in increasing priority.
    pub fn merge(&self, other: &Style) -> Style {
        let (p1, p2) = if self.priority > other.priority { (self, other) } else { (other, self) };

        Style::new(
            p1.priority,
            p1.fg_color.or(p2.fg_color),
            p1.bg_color.or(p2.bg_color),
            p1.weight.or(p2.weight),
            p1.underline.or(p2.underline),
            p1.italic.or(p2.italic),
        )
    }

    /// Encode this `Style`, setting the `id` property.
    ///
    /// Note: this should only be used when sending the `def_style` RPC.
    pub fn to_json(&self, id: usize) -> Value {
        let mut as_val = serde_json::to_value(self).expect("failed to encode style");
        as_val["id"] = id.into();
        as_val
    }

    fn rgba_from_syntect_color(color: Color) -> u32 {
        let Color { r, g, b, a } = color;
        ((a as u32) << 24) | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32)
    }
}

/// A map from styles to client identifiers for a given `Theme`.
pub struct ThemeStyleMap {
    themes: ThemeSet,
    theme_name: String,
    theme: Theme,
    default_style: Style,
    map: HashMap<Style, usize>,
    path_map: BTreeMap<String, PathBuf>,

    // It's not obvious we actually have to store the style, we seem to only need it
    // as the key in the map.
    styles: Vec<Style>,
    themes_dir: Option<PathBuf>,
    cache_dir: Option<PathBuf>,
    caching_enabled: bool,

    // Maintaining all theme paths for comparison on an fs event.
    state: HashSet<PathBuf>,
}

impl ThemeStyleMap {
    pub fn new(themes_dir: Option<PathBuf>) -> ThemeStyleMap {
        let themes = ThemeSet::load_defaults();
        let theme_name = DEFAULT_THEME.to_owned();
        let theme = themes.themes.get(&theme_name).expect("missing theme").to_owned();
        let default_style = Style::default_for_theme(&theme);
        let cache_dir = None;
        let caching_enabled = true;

        ThemeStyleMap {
            themes,
            theme_name,
            theme,
            default_style,
            map: HashMap::new(),
            path_map: BTreeMap::new(),
            styles: Vec::new(),
            themes_dir,
            cache_dir,
            caching_enabled,
            state: HashSet::new(),
        }
    }

    pub fn get_default_style(&self) -> &Style {
        &self.default_style
    }

    pub fn get_highlighter(&self) -> Highlighter {
        Highlighter::new(&self.theme)
    }

    pub fn get_theme_name(&self) -> &str {
        &self.theme_name
    }

    pub fn get_theme_settings(&self) -> &ThemeSettings {
        &self.theme.settings
    }

    pub fn get_theme_names(&self) -> Vec<String> {
        self.path_map.keys().chain(self.themes.themes.keys()).cloned().collect()
    }

    pub fn contains_theme(&self, k: &str) -> bool {
        self.themes.themes.contains_key(k)
    }

    pub fn set_theme(&mut self, theme_name: &str) -> Result<(), &'static str> {
        match self.load_theme(theme_name) {
            Ok(()) => {
                if let Some(new_theme) = self.themes.themes.get(theme_name) {
                    self.theme = new_theme.to_owned();
                    self.theme_name = theme_name.to_owned();
                    self.default_style = Style::default_for_theme(&self.theme);
                    self.map = HashMap::new();
                    self.styles = Vec::new();
                    Ok(())
                } else {
                    Err("unknown theme")
                }
            }
            Err(e) => {
                error!("Encountered error {:?} while trying to load {:?}", e, theme_name);
                Err("could not load theme")
            }
        }
    }

    pub fn merge_with_default(&self, style: &Style) -> Style {
        self.default_style.merge(style)
    }

    pub fn lookup(&self, style: &Style) -> Option<usize> {
        self.map.get(style).cloned()
    }

    pub fn add(&mut self, style: &Style) -> usize {
        let result = self.styles.len() + N_RESERVED_STYLES;
        self.map.insert(style.clone(), result);
        self.styles.push(style.clone());
        result
    }

    /// Delete key and the corresponding dump file from the themes map.
    pub(crate) fn remove_theme(&mut self, path: &Path) -> Option<String> {
        validate_theme_file(path).ok()?;

        let theme_name = path.file_stem().and_then(OsStr::to_str)?;
        self.themes.themes.remove(theme_name);
        self.path_map.remove(theme_name);
        self.state.remove(path);

        let dump_p = self.get_dump_path(theme_name)?;
        if dump_p.exists() {
            let _ = fs::remove_file(dump_p);
        }

        Some(theme_name.to_string())
    }

    /// Cache all themes names and their paths inside the given directory.
    pub(crate) fn load_theme_dir(&mut self) {
        if let Some(themes_dir) = self.themes_dir.clone() {
            match ThemeSet::discover_theme_paths(themes_dir) {
                Ok(themes) => {
                    self.caching_enabled = self.caching_enabled && self.init_cache_dir();
                    // We look through the theme folder here and cache their names/paths to a
                    // path hashmap.
                    for theme_p in &themes {
                        match self.load_theme_info_from_path(theme_p) {
                            Ok(_) => (),
                            Err(e) => error!("Encountered error {:?} loading theme at {:?}", e, theme_p),
                        }
                    }
                }
                Err(e) => error!("Error loading themes dir: {:?}", e),
            }
        }
    }

    /// A wrapper around `from_dump_file`
    /// to validate the state of dump file.
    /// Invalidates if mod time of dump is less
    /// than the original one.
    fn try_load_from_dump(&self, theme_p: &Path) -> Option<(String, Theme)> {
        if !self.caching_enabled {
            return None;
        }

        let theme_name = theme_p.file_stem().and_then(OsStr::to_str)?;

        let dump_p = self.get_dump_path(theme_name)?;

        if !&dump_p.exists() {
            return None;
        }

        //NOTE: `try_load_from_dump` will return `None` if the file at
        //`dump_p` or `theme_p` is deleted before the execution of this fn.
        let mod_t = fs::metadata(&dump_p).and_then(|md| md.modified()).ok()?;
        let mod_t_orig = fs::metadata(theme_p).and_then(|md| md.modified()).ok()?;

        if mod_t >= mod_t_orig {
            from_dump_file(&dump_p).ok().map(|t| (theme_name.to_owned(), t))
        } else {
            // Delete dump file
            let _ = fs::remove_file(&dump_p);
            None
        }
    }

    /// Loads a theme's name and its respective path into the theme path map.
    pub(crate) fn load_theme_info_from_path(
        &mut self,
        theme_p: &Path,
    ) -> Result<String, LoadingError> {
        validate_theme_file(theme_p)?;
        let theme_name =
            theme_p.file_stem().and_then(OsStr::to_str).ok_or(LoadingError::BadPath)?;

        self.path_map.insert(theme_name.to_string(), theme_p.to_path_buf());
        self.state.insert(theme_p.to_path_buf());

        Ok(theme_name.to_owned())
    }

    /// Loads theme using syntect's `get_theme` fn to our `theme` path map.
    /// Stores binary dump in a file with `tmdump` extension, only if
    /// caching is enabled.
    fn load_theme(&mut self, theme_name: &str) -> Result<(), LoadingError> {
        // If it's a default theme, or if the theme has already been loaded, we can move on.
        if self.contains_theme(theme_name) {
            return Ok(());
        }
        // If we haven't loaded the theme before, we try to load it from the dump if a dump
        // exists or load it from the theme file itself.
        let theme_p = &self.path_map.get(theme_name).cloned();
        if let Some(theme_p) = theme_p {
            match self.try_load_from_dump(theme_p) {
                Some((dump_theme_name, dump_theme_data)) => {
                    self.insert_to_map(dump_theme_name, dump_theme_data, theme_p);
                }
                None => {
                    let theme = ThemeSet::get_theme(theme_p)?;
                    if self.caching_enabled {
                        if let Some(dump_p) = self.get_dump_path(theme_name) {
                            let _ = dump_to_file(&theme, dump_p);
                        }
                    }
                    self.insert_to_map(theme_name.to_owned(), theme, theme_p);
                }
            }
            Ok(())
        } else {
            Err(LoadingError::BadPath)
        }
    }

    fn insert_to_map(&mut self, k: String, v: Theme, p: &Path) {
        self.themes.themes.insert(k, v);

        //Maintain a record for future syncing
        self.state.insert(p.to_path_buf());
    }

    /// Returns dump's path corresponding to the given theme name.
    fn get_dump_path(&self, theme_name: &str) -> Option<PathBuf> {
        self.cache_dir.as_ref().map(|p| p.join(theme_name).with_extension("tmdump"))
    }

    /// Compare the stored file paths in `self.state`
    /// to the present ones.
    pub(crate) fn sync_dir(&mut self, dir: Option<&Path>) {
        if let Some(themes_dir) = dir {
            if let Ok(paths) = ThemeSet::discover_theme_paths(themes_dir) {
                let current_state = HashSet::from_iter(paths.into_iter());
                let maintained_state = self.state.clone();

                let to_insert = current_state.difference(&maintained_state);
                for path in to_insert {
                    let _ = self.load_theme_info_from_path(path);
                }
                let to_remove = maintained_state.difference(&current_state);
                for path in to_remove {
                    self.remove_theme(path);
                }
            }
        }
    }

    /// Creates the cache dir returns true
    /// if it is successfully initialized or
    /// already exists.
    fn init_cache_dir(&mut self) -> bool {
        self.cache_dir = self.themes_dir.clone().map(|p| p.join("cache"));

        if let Some(ref p) = self.cache_dir {
            if p.exists() {
                return true;
            }
            fs::DirBuilder::new().create(&p).is_ok()
        } else {
            false
        }
    }
}

/// Used to remove files with extension other than `tmTheme`.
fn validate_theme_file(path: &Path) -> Result<bool, LoadingError> {
    path.extension().map(|e| e != "tmTheme").ok_or(LoadingError::BadPath)
}
<commit_msg>Remove redundant state path hashset<commit_after>// Copyright 2017 The xi-editor 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.

//! Management of styles.

use std::collections::{BTreeMap, HashMap, HashSet};
use std::ffi::OsStr;
use std::fs;
use std::iter::FromIterator;
use std::path::{Path, PathBuf};

use serde_json::{self, Value};
use syntect::dumps::{dump_to_file, from_dump_file};
use syntect::highlighting::StyleModifier as SynStyleModifier;
use syntect::highlighting::{Color, Highlighter, Theme, ThemeSet};
use syntect::LoadingError;

pub use syntect::highlighting::ThemeSettings;

pub const N_RESERVED_STYLES: usize = 8;
const SYNTAX_PRIORITY_DEFAULT: u16 = 200;
const SYNTAX_PRIORITY_LOWEST: u16 = 0;
pub const DEFAULT_THEME: &str = "InspiredGitHub";

#[derive(Clone, PartialEq, Eq, Default, Hash, Debug, Serialize, Deserialize)]
/// A mergeable style. All values except priority are optional.
///
/// Note: A `None` value represents the absense of preference; in the case of
/// boolean options, `Some(false)` means that this style will override a lower
/// priority value in the same field.
pub struct Style {
    /// The priority of this style, in the range (0, 1000). Used to resolve
    /// conflicting fields when merging styles. The higher priority wins.
    #[serde(skip_serializing)]
    pub priority: u16,
    /// The foreground text color, in ARGB.
    pub fg_color: Option<u32>,
    /// The background text color, in ARGB.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub bg_color: Option<u32>,
    /// The font-weight, in the range 100-900, interpreted like the CSS
    /// font-weight property.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub weight: Option<u16>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub underline: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub italic: Option<bool>,
}

impl Style {
    /// Creates a new `Style` by converting from a `Syntect::StyleModifier`.
    pub fn from_syntect_style_mod(style: &SynStyleModifier) -> Self {
        let font_style = style.font_style.map(|s| s.bits()).unwrap_or_default();
        let weight = if (font_style & 1) != 0 { Some(700) } else { None };
        let underline = if (font_style & 2) != 0 { Some(true) } else { None };
        let italic = if (font_style & 4) != 0 { Some(true) } else { None };

        Self::new(
            SYNTAX_PRIORITY_DEFAULT,
            style.foreground.map(Self::rgba_from_syntect_color),
            style.background.map(Self::rgba_from_syntect_color),
            weight,
            underline,
            italic,
        )
    }

    pub fn new<O32, O16, OB>(
        priority: u16,
        fg_color: O32,
        bg_color: O32,
        weight: O16,
        underline: OB,
        italic: OB,
    ) -> Self
    where
        O32: Into<Option<u32>>,
        O16: Into<Option<u16>>,
        OB: Into<Option<bool>>,
    {
        assert!(priority <= 1000);
        Style {
            priority,
            fg_color: fg_color.into(),
            bg_color: bg_color.into(),
            weight: weight.into(),
            underline: underline.into(),
            italic: italic.into(),
        }
    }

    /// Returns the default style for the given `Theme`.
    pub fn default_for_theme(theme: &Theme) -> Self {
        let fg = theme.settings.foreground.unwrap_or(Color::BLACK);
        Style::new(
            SYNTAX_PRIORITY_LOWEST,
            Some(Self::rgba_from_syntect_color(fg)),
            None,
            None,
            None,
            None,
        )
    }

    /// Creates a new style by combining attributes of `self` and `other`.
    /// If both styles define an attribute, the highest priority wins; `other`
    /// wins in the case of a tie.
    ///
    /// Note: when merging multiple styles, apply them in increasing priority.
    pub fn merge(&self, other: &Style) -> Style {
        let (p1, p2) = if self.priority > other.priority { (self, other) } else { (other, self) };

        Style::new(
            p1.priority,
            p1.fg_color.or(p2.fg_color),
            p1.bg_color.or(p2.bg_color),
            p1.weight.or(p2.weight),
            p1.underline.or(p2.underline),
            p1.italic.or(p2.italic),
        )
    }

    /// Encode this `Style`, setting the `id` property.
    ///
    /// Note: this should only be used when sending the `def_style` RPC.
    pub fn to_json(&self, id: usize) -> Value {
        let mut as_val = serde_json::to_value(self).expect("failed to encode style");
        as_val["id"] = id.into();
        as_val
    }

    fn rgba_from_syntect_color(color: Color) -> u32 {
        let Color { r, g, b, a } = color;
        ((a as u32) << 24) | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32)
    }
}

/// A map from styles to client identifiers for a given `Theme`.
pub struct ThemeStyleMap {
    themes: ThemeSet,
    theme_name: String,
    theme: Theme,
    default_style: Style,
    map: HashMap<Style, usize>,

    // Maintains the map of found themes and their paths.
    path_map: BTreeMap<String, PathBuf>,

    // It's not obvious we actually have to store the style, we seem to only need it
    // as the key in the map.
    styles: Vec<Style>,
    themes_dir: Option<PathBuf>,
    cache_dir: Option<PathBuf>,
    caching_enabled: bool,
}

impl ThemeStyleMap {
    pub fn new(themes_dir: Option<PathBuf>) -> ThemeStyleMap {
        let themes = ThemeSet::load_defaults();
        let theme_name = DEFAULT_THEME.to_owned();
        let theme = themes.themes.get(&theme_name).expect("missing theme").to_owned();
        let default_style = Style::default_for_theme(&theme);
        let cache_dir = None;
        let caching_enabled = true;

        ThemeStyleMap {
            themes,
            theme_name,
            theme,
            default_style,
            map: HashMap::new(),
            path_map: BTreeMap::new(),
            styles: Vec::new(),
            themes_dir,
            cache_dir,
            caching_enabled,
        }
    }

    pub fn get_default_style(&self) -> &Style {
        &self.default_style
    }

    pub fn get_highlighter(&self) -> Highlighter {
        Highlighter::new(&self.theme)
    }

    pub fn get_theme_name(&self) -> &str {
        &self.theme_name
    }

    pub fn get_theme_settings(&self) -> &ThemeSettings {
        &self.theme.settings
    }

    pub fn get_theme_names(&self) -> Vec<String> {
        self.path_map.keys().chain(self.themes.themes.keys()).cloned().collect()
    }

    pub fn contains_theme(&self, k: &str) -> bool {
        self.themes.themes.contains_key(k)
    }

    pub fn set_theme(&mut self, theme_name: &str) -> Result<(), &'static str> {
        match self.load_theme(theme_name) {
            Ok(()) => {
                if let Some(new_theme) = self.themes.themes.get(theme_name) {
                    self.theme = new_theme.to_owned();
                    self.theme_name = theme_name.to_owned();
                    self.default_style = Style::default_for_theme(&self.theme);
                    self.map = HashMap::new();
                    self.styles = Vec::new();
                    Ok(())
                } else {
                    Err("unknown theme")
                }
            }
            Err(e) => {
                error!("Encountered error {:?} while trying to load {:?}", e, theme_name);
                Err("could not load theme")
            }
        }
    }

    pub fn merge_with_default(&self, style: &Style) -> Style {
        self.default_style.merge(style)
    }

    pub fn lookup(&self, style: &Style) -> Option<usize> {
        self.map.get(style).cloned()
    }

    pub fn add(&mut self, style: &Style) -> usize {
        let result = self.styles.len() + N_RESERVED_STYLES;
        self.map.insert(style.clone(), result);
        self.styles.push(style.clone());
        result
    }

    /// Delete key and the corresponding dump file from the themes map.
    pub(crate) fn remove_theme(&mut self, path: &Path) -> Option<String> {
        validate_theme_file(path).ok()?;

        let theme_name = path.file_stem().and_then(OsStr::to_str)?;
        self.themes.themes.remove(theme_name);
        self.path_map.remove(theme_name);

        let dump_p = self.get_dump_path(theme_name)?;
        if dump_p.exists() {
            let _ = fs::remove_file(dump_p);
        }

        Some(theme_name.to_string())
    }

    /// Cache all themes names and their paths inside the given directory.
    pub(crate) fn load_theme_dir(&mut self) {
        if let Some(themes_dir) = self.themes_dir.clone() {
            match ThemeSet::discover_theme_paths(themes_dir) {
                Ok(themes) => {
                    self.caching_enabled = self.caching_enabled && self.init_cache_dir();
                    // We look through the theme folder here and cache their names/paths to a
                    // path hashmap.
                    for theme_p in &themes {
                        match self.load_theme_info_from_path(theme_p) {
                            Ok(_) => (),
                            Err(e) => {
                                error!("Encountered error {:?} loading theme at {:?}", e, theme_p)
                            }
                        }
                    }
                }
                Err(e) => error!("Error loading themes dir: {:?}", e),
            }
        }
    }

    /// A wrapper around `from_dump_file`
    /// to validate the state of dump file.
    /// Invalidates if mod time of dump is less
    /// than the original one.
    fn try_load_from_dump(&self, theme_p: &Path) -> Option<(String, Theme)> {
        if !self.caching_enabled {
            return None;
        }

        let theme_name = theme_p.file_stem().and_then(OsStr::to_str)?;

        let dump_p = self.get_dump_path(theme_name)?;

        if !&dump_p.exists() {
            return None;
        }

        //NOTE: `try_load_from_dump` will return `None` if the file at
        //`dump_p` or `theme_p` is deleted before the execution of this fn.
        let mod_t = fs::metadata(&dump_p).and_then(|md| md.modified()).ok()?;
        let mod_t_orig = fs::metadata(theme_p).and_then(|md| md.modified()).ok()?;

        if mod_t >= mod_t_orig {
            from_dump_file(&dump_p).ok().map(|t| (theme_name.to_owned(), t))
        } else {
            // Delete dump file
            let _ = fs::remove_file(&dump_p);
            None
        }
    }

    /// Loads a theme's name and its respective path into the theme path map.
    pub(crate) fn load_theme_info_from_path(
        &mut self,
        theme_p: &Path,
    ) -> Result<String, LoadingError> {
        validate_theme_file(theme_p)?;
        let theme_name =
            theme_p.file_stem().and_then(OsStr::to_str).ok_or(LoadingError::BadPath)?;

        self.path_map.insert(theme_name.to_string(), theme_p.to_path_buf());

        Ok(theme_name.to_owned())
    }

    /// Loads theme using syntect's `get_theme` fn to our `theme` path map.
    /// Stores binary dump in a file with `tmdump` extension, only if
    /// caching is enabled.
    fn load_theme(&mut self, theme_name: &str) -> Result<(), LoadingError> {
        // If it's a default theme, or if the theme has already been loaded, we can move on.
        if self.contains_theme(theme_name) {
            return Ok(());
        }
        // If we haven't loaded the theme before, we try to load it from the dump if a dump
        // exists or load it from the theme file itself.
        let theme_p = &self.path_map.get(theme_name).cloned();
        if let Some(theme_p) = theme_p {
            match self.try_load_from_dump(theme_p) {
                Some((dump_theme_name, dump_theme_data)) => {
                    self.insert_to_map(dump_theme_name, dump_theme_data);
                }
                None => {
                    let theme = ThemeSet::get_theme(theme_p)?;
                    if self.caching_enabled {
                        if let Some(dump_p) = self.get_dump_path(theme_name) {
                            let _ = dump_to_file(&theme, dump_p);
                        }
                    }
                    self.insert_to_map(theme_name.to_owned(), theme);
                }
            }
            Ok(())
        } else {
            Err(LoadingError::BadPath)
        }
    }

    fn insert_to_map(&mut self, k: String, v: Theme) {
        self.themes.themes.insert(k, v);
    }

    /// Returns dump's path corresponding to the given theme name.
    fn get_dump_path(&self, theme_name: &str) -> Option<PathBuf> {
        self.cache_dir.as_ref().map(|p| p.join(theme_name).with_extension("tmdump"))
    }

    /// Compare the stored file paths in `self.state`
    /// to the present ones.
    pub(crate) fn sync_dir(&mut self, dir: Option<&Path>) {
        if let Some(themes_dir) = dir {
            if let Ok(paths) = ThemeSet::discover_theme_paths(themes_dir) {
                let current_state: HashSet<PathBuf> = HashSet::from_iter(paths.into_iter());
                let maintained_state: HashSet<PathBuf> =
                    HashSet::from_iter(self.path_map.values().cloned());

                let to_insert = current_state.difference(&maintained_state);
                for path in to_insert {
                    let _ = self.load_theme_info_from_path(path);
                }
                let to_remove = maintained_state.difference(&current_state);
                for path in to_remove {
                    self.remove_theme(path);
                }
            }
        }
    }

    /// Creates the cache dir returns true
    /// if it is successfully initialized or
    /// already exists.
    fn init_cache_dir(&mut self) -> bool {
        self.cache_dir = self.themes_dir.clone().map(|p| p.join("cache"));

        if let Some(ref p) = self.cache_dir {
            if p.exists() {
                return true;
            }
            fs::DirBuilder::new().create(&p).is_ok()
        } else {
            false
        }
    }
}

/// Used to remove files with extension other than `tmTheme`.
fn validate_theme_file(path: &Path) -> Result<bool, LoadingError> {
    path.extension().map(|e| e != "tmTheme").ok_or(LoadingError::BadPath)
}
<|endoftext|>


<commit_before>use rustc_hir::intravisit;
use rustc_hir::{self, Body, FnDecl, HirId, HirIdSet, ItemKind, Node};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span;
use rustc_target::abi::LayoutOf;
use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceWithHirId, PlaceBase};

use crate::utils::span_lint;

#[derive(Copy, Clone)]
pub struct BoxedLocal {
    pub too_large_for_stack: u64,
}

declare_clippy_lint! {
    /// **What it does:** Checks for usage of `Box<T>` where an unboxed `T` would
    /// work fine.
    ///
    /// **Why is this bad?** This is an unnecessary allocation, and bad for
    /// performance. It is only necessary to allocate if you wish to move the box
    /// into something.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// # fn foo(bar: usize) {}
    ///
    /// // Bad
    /// let x = Box::new(1);
    /// foo(*x);
    /// println!("{}", *x);
    ///
    /// // Good
    /// let x = 1;
    /// foo(x);
    /// println!("{}", x);
    /// ```
    pub BOXED_LOCAL,
    perf,
    "using `Box<T>` where unnecessary"
}

fn is_non_trait_box(ty: Ty<'_>) -> bool {
    ty.is_box() && !ty.boxed_ty().is_trait()
}

struct EscapeDelegate<'a, 'tcx> {
    cx: &'a LateContext<'a, 'tcx>,
    set: HirIdSet,
    too_large_for_stack: u64,
}

impl_lint_pass!(BoxedLocal => [BOXED_LOCAL]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoxedLocal {
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        _: intravisit::FnKind<'tcx>,
        _: &'tcx FnDecl<'_>,
        body: &'tcx Body<'_>,
        _: Span,
        hir_id: HirId,
    ) {
        // If the method is an impl for a trait, don't warn.
        let parent_id = cx.tcx.hir().get_parent_item(hir_id);
        let parent_node = cx.tcx.hir().find(parent_id);

        if let Some(Node::Item(item)) = parent_node {
            if let ItemKind::Impl { of_trait: Some(_), .. } = item.kind {
                return;
            }
        }

        let mut v = EscapeDelegate {
            cx,
            set: HirIdSet::default(),
            too_large_for_stack: self.too_large_for_stack,
        };

        let fn_def_id = cx.tcx.hir().local_def_id(hir_id);
        cx.tcx.infer_ctxt().enter(|infcx| {
            ExprUseVisitor::new(&mut v, &infcx, fn_def_id, cx.param_env, cx.tables).consume_body(body);
        });

        for node in v.set {
            span_lint(
                cx,
                BOXED_LOCAL,
                cx.tcx.hir().span(node),
                "local variable doesn't need to be boxed here",
            );
        }
    }
}

// TODO: Replace with Map::is_argument(..) when it's fixed
fn is_argument(map: rustc_middle::hir::map::Map<'_>, id: HirId) -> bool {
    match map.find(id) {
        Some(Node::Binding(_)) => (),
        _ => return false,
    }

    match map.find(map.get_parent_node(id)) {
        Some(Node::Param(_)) => true,
        _ => false,
    }
}

impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
    fn consume(&mut self, cmt: &PlaceWithHirId<'tcx>, mode: ConsumeMode) {
        if cmt.place.projections.is_empty() {
            if let PlaceBase::Local(lid) = cmt.place.base {
                if let ConsumeMode::Move = mode {
                    // moved out or in. clearly can't be localized
                    self.set.remove(&lid);
                }
                let map = &self.cx.tcx.hir();
                if let Some(Node::Binding(_)) = map.find(cmt.hir_id) {
                    if self.set.contains(&lid) {
                        // let y = x where x is known
                        // remove x, insert y
                        self.set.insert(cmt.hir_id);
                        self.set.remove(&lid);
                    }
                }
            }
        }
    }

    fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: ty::BorrowKind) {
        if cmt.place.projections.is_empty() {
            if let PlaceBase::Local(lid) = cmt.place.base {
                self.set.remove(&lid);
            }
        }
    }

    fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>) {
        if cmt.place.projections.is_empty() {
            let map = &self.cx.tcx.hir();
            if is_argument(*map, cmt.hir_id) {
                // Skip closure arguments
                let parent_id = map.get_parent_node(cmt.hir_id);
                if let Some(Node::Expr(..)) = map.find(map.get_parent_node(parent_id)) {
                    return;
                }

                if is_non_trait_box(cmt.place.ty) && !self.is_large_box(cmt.place.ty) {
                    self.set.insert(cmt.hir_id);
                }
                return;
            }
        }
    }
}

impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> {
    fn is_large_box(&self, ty: Ty<'tcx>) -> bool {
        // Large types need to be boxed to avoid stack overflows.
        if ty.is_box() {
            self.cx.layout_of(ty.boxed_ty()).map_or(0, |l| l.size.bytes()) > self.too_large_for_stack
        } else {
            false
        }
    }
}
<commit_msg>typeck: adding type information to projection<commit_after>use rustc_hir::intravisit;
use rustc_hir::{self, Body, FnDecl, HirId, HirIdSet, ItemKind, Node};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span;
use rustc_target::abi::LayoutOf;
use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceWithHirId, PlaceBase};

use crate::utils::span_lint;

#[derive(Copy, Clone)]
pub struct BoxedLocal {
    pub too_large_for_stack: u64,
}

declare_clippy_lint! {
    /// **What it does:** Checks for usage of `Box<T>` where an unboxed `T` would
    /// work fine.
    ///
    /// **Why is this bad?** This is an unnecessary allocation, and bad for
    /// performance. It is only necessary to allocate if you wish to move the box
    /// into something.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// # fn foo(bar: usize) {}
    ///
    /// // Bad
    /// let x = Box::new(1);
    /// foo(*x);
    /// println!("{}", *x);
    ///
    /// // Good
    /// let x = 1;
    /// foo(x);
    /// println!("{}", x);
    /// ```
    pub BOXED_LOCAL,
    perf,
    "using `Box<T>` where unnecessary"
}

fn is_non_trait_box(ty: Ty<'_>) -> bool {
    ty.is_box() && !ty.boxed_ty().is_trait()
}

struct EscapeDelegate<'a, 'tcx> {
    cx: &'a LateContext<'a, 'tcx>,
    set: HirIdSet,
    too_large_for_stack: u64,
}

impl_lint_pass!(BoxedLocal => [BOXED_LOCAL]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoxedLocal {
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        _: intravisit::FnKind<'tcx>,
        _: &'tcx FnDecl<'_>,
        body: &'tcx Body<'_>,
        _: Span,
        hir_id: HirId,
    ) {
        // If the method is an impl for a trait, don't warn.
        let parent_id = cx.tcx.hir().get_parent_item(hir_id);
        let parent_node = cx.tcx.hir().find(parent_id);

        if let Some(Node::Item(item)) = parent_node {
            if let ItemKind::Impl { of_trait: Some(_), .. } = item.kind {
                return;
            }
        }

        let mut v = EscapeDelegate {
            cx,
            set: HirIdSet::default(),
            too_large_for_stack: self.too_large_for_stack,
        };

        let fn_def_id = cx.tcx.hir().local_def_id(hir_id);
        cx.tcx.infer_ctxt().enter(|infcx| {
            ExprUseVisitor::new(&mut v, &infcx, fn_def_id, cx.param_env, cx.tables).consume_body(body);
        });

        for node in v.set {
            span_lint(
                cx,
                BOXED_LOCAL,
                cx.tcx.hir().span(node),
                "local variable doesn't need to be boxed here",
            );
        }
    }
}

// TODO: Replace with Map::is_argument(..) when it's fixed
fn is_argument(map: rustc_middle::hir::map::Map<'_>, id: HirId) -> bool {
    match map.find(id) {
        Some(Node::Binding(_)) => (),
        _ => return false,
    }

    match map.find(map.get_parent_node(id)) {
        Some(Node::Param(_)) => true,
        _ => false,
    }
}

impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
    fn consume(&mut self, cmt: &PlaceWithHirId<'tcx>, mode: ConsumeMode) {
        if cmt.place.projections.is_empty() {
            if let PlaceBase::Local(lid) = cmt.place.base {
                if let ConsumeMode::Move = mode {
                    // moved out or in. clearly can't be localized
                    self.set.remove(&lid);
                }
                let map = &self.cx.tcx.hir();
                if let Some(Node::Binding(_)) = map.find(cmt.hir_id) {
                    if self.set.contains(&lid) {
                        // let y = x where x is known
                        // remove x, insert y
                        self.set.insert(cmt.hir_id);
                        self.set.remove(&lid);
                    }
                }
            }
        }
    }

    fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: ty::BorrowKind) {
        if cmt.place.projections.is_empty() {
            if let PlaceBase::Local(lid) = cmt.place.base {
                self.set.remove(&lid);
            }
        }
    }

    fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>) {
        if cmt.place.projections.is_empty() {
            let map = &self.cx.tcx.hir();
            if is_argument(*map, cmt.hir_id) {
                // Skip closure arguments
                let parent_id = map.get_parent_node(cmt.hir_id);
                if let Some(Node::Expr(..)) = map.find(map.get_parent_node(parent_id)) {
                    return;
                }

                if is_non_trait_box(cmt.place.ty()) && !self.is_large_box(cmt.place.ty()) {
                    self.set.insert(cmt.hir_id);
                }
                return;
            }
        }
    }
}

impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> {
    fn is_large_box(&self, ty: Ty<'tcx>) -> bool {
        // Large types need to be boxed to avoid stack overflows.
        if ty.is_box() {
            self.cx.layout_of(ty.boxed_ty()).map_or(0, |l| l.size.bytes()) > self.too_large_for_stack
        } else {
            false
        }
    }
}
<|endoftext|>


<commit_before>use itertools::Itertools;

use crate::ram::schema::{Field, Schema};
use crate::ram::types;
use crate::ram::types::{bool_io, u32_io, SharedMap, SharedValue, Type};

use super::writer::{ARRAY_TYPE_MASK, NULL_PLACEHOLDER};
use dovahkiin::types::key_hash;
use std::collections::HashMap;

fn read_field(
    base_ptr: usize,
    field: &Field,
    selected: Option<&[u64]>,
    is_var: bool,
    tail_offset: &mut usize,
) -> SharedValue {
    let mut rec_field_offset = field.offset.unwrap_or(0);
    let field_offset = if is_var {
        // Is inside size variable field, read directly from the address
        tail_offset
    } else if !field.is_var() {
        // Is not inside var and field not var, read from the offset in field
        &mut rec_field_offset
    } else {
        // Is not inside var and field is var, read the pointer and direct to it
        *tail_offset = *u32_io::read(base_ptr + field.offset.unwrap()) as usize;
        tail_offset
    };
    if field.nullable {
        let null_byte = *bool_io::read(base_ptr + *field_offset);
        *field_offset += 1;
        if null_byte {
            return SharedValue::Null;
        }
    }
    if field.is_array {
        let len = *u32_io::read(base_ptr + *field_offset);
        trace!("Got array length {}", len);
        let mut sub_field = field.clone();
        sub_field.is_array = false;
        *field_offset += u32_io::type_size();
        if field.sub_fields.is_none() {
            // maybe primitive array
            let mut ptr = base_ptr + *field_offset;
            let val = types::get_shared_prim_array_val(field.data_type, len as usize, &mut ptr);
            *field_offset = ptr - base_ptr;
            if let Some(prim_arr) = val {
                SharedValue::PrimArray(prim_arr)
            } else {
                panic!(
                    "type cannot been convert to prim array: {:?}",
                    field.data_type
                )
            }
        } else {
            let mut vals = Vec::<SharedValue>::new();
            for _ in 0..len {
                let nxt_val = read_field(base_ptr, &sub_field, None, true, field_offset);
                vals.push(nxt_val);
            }
            SharedValue::Array(vals)
        }
    } else if let Some(ref subs) = field.sub_fields {
        let mut map = SharedMap::new();
        let mut read_sub = |sub: &Field| {
            map.insert_key_id(sub.name_id, read_field(base_ptr, &sub, selected, is_var, field_offset));
        };
        if let Some(field_ids) = selected {
            for sub in subs {
                let mut selected_pos = 0;
                if field_ids[selected_pos] == sub.name_id {
                    read_sub(sub);
                    selected_pos += 1;
                    if field_ids.len() <= selected_pos {
                        return SharedValue::Map(map);
                    }
                }
            }
        } else {
            for sub in subs {
                read_sub(sub);
            }
        }
        map.fields = subs.iter().map(|sub| &sub.name).cloned().collect();
        SharedValue::Map(map)
    } else {
        let field_ptr = base_ptr + *field_offset;
        *field_offset += types::get_size(field.data_type, field_ptr);
        types::get_shared_val(field.data_type, field_ptr)
    }
}

pub fn read_attach_dynamic_part(mut tail_ptr: usize, dest: &mut SharedValue) {
    let src = read_dynamic_value(&mut tail_ptr);
    if let &mut SharedValue::Map(ref mut map_dest) = dest {
        if let SharedValue::Map(mut map_src) = src {
            map_dest.fields.append(&mut map_src.fields);
            for (k, v) in map_src.map.into_iter() {
                map_dest.insert_key_id(k, v);
            }
        }
    }
}

const MAP_TYPE_ID: u8 = Type::Map.id();

fn read_dynamic_value(ptr: &mut usize) -> SharedValue {
    let type_id = types::get_shared_val(Type::U8, *ptr).u8().unwrap();
    let is_array = type_id & ARRAY_TYPE_MASK == ARRAY_TYPE_MASK;
    *ptr += types::u8_io::type_size();
    if is_array {
        let base_type = type_id & (!ARRAY_TYPE_MASK);
        let len = types::get_shared_val(Type::U32, *ptr).u32().unwrap();
        *ptr += types::u32_io::type_size();
        if base_type != MAP_TYPE_ID {
            // Primitive array
            if let Some(prim_arr) =
                types::get_shared_prim_array_val(Type::from_id(base_type), *len as usize, ptr)
            {
                // ptr have been moved by `get_shared_prim_array_val`
                return SharedValue::PrimArray(prim_arr);
            } else {
                panic!("Cannot read prim array for dynamic field");
            }
        } else {
            let array = (0..*len).map(|_| read_dynamic_value(ptr)).collect();
            // ptr have been moved by recursion
            return SharedValue::Array(array);
        }
    } else if *type_id == MAP_TYPE_ID {
        // Map
        let len = types::get_shared_val(Type::U32, *ptr).u32().unwrap();
        *ptr += types::u32_io::type_size();
        let field_value_pair = (0..*len)
            .map(|_| {
                let name = types::get_shared_val(Type::String, *ptr).string().unwrap();
                *ptr += types::string_io::size_at(*ptr);
                let value = read_dynamic_value(ptr);
                (name, value)
            })
            .collect_vec();
        let mut fields = Vec::with_capacity(field_value_pair.len());
        let mut map = HashMap::with_capacity(field_value_pair.len());
        for (name, value) in field_value_pair {
            let id = key_hash(&name);
            fields.push(name.to_owned());
            map.insert(id, value);
        }
        return SharedValue::Map(SharedMap { fields, map });
    } else if *type_id == NULL_PLACEHOLDER {
        return SharedValue::Null;
    } else {
        let ty = Type::from_id(*type_id);
        let value = types::get_shared_val(ty, *ptr);
        *ptr += types::get_size(ty, *ptr);
        return value;
    }
}

pub fn read_by_schema(ptr: usize, schema: &Schema) -> SharedValue {
    let mut tail_offset = schema.static_bound;
    let mut schema_value = read_field(ptr, &schema.fields, None, false, &mut tail_offset);
    if schema.is_dynamic {
        read_attach_dynamic_part(ptr + tail_offset, &mut schema_value)
    }
    schema_value
}

pub fn read_by_schema_selected(ptr: usize, schema: &Schema, fields: &[u64]) -> SharedValue {
    let mut tail_offset = schema.static_bound;
    read_field(ptr, &schema.fields, Some(fields), false, &mut tail_offset)
}
<commit_msg>more efficient selective read<commit_after>use itertools::Itertools;

use crate::ram::schema::{Field, Schema};
use crate::ram::types;
use crate::ram::types::{bool_io, u32_io, SharedMap, SharedValue, Type};

use super::writer::{ARRAY_TYPE_MASK, NULL_PLACEHOLDER};
use dovahkiin::types::key_hash;
use std::collections::HashMap;

fn read_field(
    base_ptr: usize,
    field: &Field,
    is_var: bool,
    tail_offset: &mut usize,
) -> SharedValue {
    let mut rec_field_offset = field.offset.unwrap_or(0);
    let field_offset = if is_var {
        // Is inside size variable field, read directly from the address
        tail_offset
    } else if !field.is_var() {
        // Is not inside var and field not var, read from the offset in field
        &mut rec_field_offset
    } else {
        // Is not inside var and field is var, read the pointer and direct to it
        *tail_offset = *u32_io::read(base_ptr + field.offset.unwrap()) as usize;
        tail_offset
    };
    if field.nullable {
        let null_byte = *bool_io::read(base_ptr + *field_offset);
        *field_offset += 1;
        if null_byte {
            return SharedValue::Null;
        }
    }
    if field.is_array {
        let len = *u32_io::read(base_ptr + *field_offset);
        trace!("Got array length {}", len);
        let mut sub_field = field.clone();
        sub_field.is_array = false;
        *field_offset += u32_io::type_size();
        if field.sub_fields.is_none() {
            // maybe primitive array
            let mut ptr = base_ptr + *field_offset;
            let val = types::get_shared_prim_array_val(field.data_type, len as usize, &mut ptr);
            *field_offset = ptr - base_ptr;
            if let Some(prim_arr) = val {
                SharedValue::PrimArray(prim_arr)
            } else {
                panic!(
                    "type cannot been convert to prim array: {:?}",
                    field.data_type
                )
            }
        } else {
            let mut vals = Vec::<SharedValue>::new();
            for _ in 0..len {
                let nxt_val = read_field(base_ptr, &sub_field, true, field_offset);
                vals.push(nxt_val);
            }
            SharedValue::Array(vals)
        }
    } else if let Some(ref subs) = field.sub_fields {
        let mut map = SharedMap::new();
        for sub in subs {
            map.insert_key_id(sub.name_id, read_field(base_ptr, &sub, is_var, field_offset));
        }
        map.fields = subs.iter().map(|sub| &sub.name).cloned().collect();
        SharedValue::Map(map)
    } else {
        let field_ptr = base_ptr + *field_offset;
        *field_offset += types::get_size(field.data_type, field_ptr);
        types::get_shared_val(field.data_type, field_ptr)
    }
}

pub fn read_attach_dynamic_part(mut tail_ptr: usize, dest: &mut SharedValue) {
    let src = read_dynamic_value(&mut tail_ptr);
    if let &mut SharedValue::Map(ref mut map_dest) = dest {
        if let SharedValue::Map(mut map_src) = src {
            map_dest.fields.append(&mut map_src.fields);
            for (k, v) in map_src.map.into_iter() {
                map_dest.insert_key_id(k, v);
            }
        }
    }
}

const MAP_TYPE_ID: u8 = Type::Map.id();

fn read_dynamic_value(ptr: &mut usize) -> SharedValue {
    let type_id = types::get_shared_val(Type::U8, *ptr).u8().unwrap();
    let is_array = type_id & ARRAY_TYPE_MASK == ARRAY_TYPE_MASK;
    *ptr += types::u8_io::type_size();
    if is_array {
        let base_type = type_id & (!ARRAY_TYPE_MASK);
        let len = types::get_shared_val(Type::U32, *ptr).u32().unwrap();
        *ptr += types::u32_io::type_size();
        if base_type != MAP_TYPE_ID {
            // Primitive array
            if let Some(prim_arr) =
                types::get_shared_prim_array_val(Type::from_id(base_type), *len as usize, ptr)
            {
                // ptr have been moved by `get_shared_prim_array_val`
                return SharedValue::PrimArray(prim_arr);
            } else {
                panic!("Cannot read prim array for dynamic field");
            }
        } else {
            let array = (0..*len).map(|_| read_dynamic_value(ptr)).collect();
            // ptr have been moved by recursion
            return SharedValue::Array(array);
        }
    } else if *type_id == MAP_TYPE_ID {
        // Map
        let len = types::get_shared_val(Type::U32, *ptr).u32().unwrap();
        *ptr += types::u32_io::type_size();
        let field_value_pair = (0..*len)
            .map(|_| {
                let name = types::get_shared_val(Type::String, *ptr).string().unwrap();
                *ptr += types::string_io::size_at(*ptr);
                let value = read_dynamic_value(ptr);
                (name, value)
            })
            .collect_vec();
        let mut fields = Vec::with_capacity(field_value_pair.len());
        let mut map = HashMap::with_capacity(field_value_pair.len());
        for (name, value) in field_value_pair {
            let id = key_hash(&name);
            fields.push(name.to_owned());
            map.insert(id, value);
        }
        return SharedValue::Map(SharedMap { fields, map });
    } else if *type_id == NULL_PLACEHOLDER {
        return SharedValue::Null;
    } else {
        let ty = Type::from_id(*type_id);
        let value = types::get_shared_val(ty, *ptr);
        *ptr += types::get_size(ty, *ptr);
        return value;
    }
}

pub fn read_by_schema(ptr: usize, schema: &Schema) -> SharedValue {
    let mut tail_offset = schema.static_bound;
    let mut schema_value = read_field(ptr, &schema.fields, false, &mut tail_offset);
    if schema.is_dynamic {
        read_attach_dynamic_part(ptr + tail_offset, &mut schema_value)
    }
    schema_value
}

pub fn read_by_schema_selected(ptr: usize, schema: &Schema, fields: &[u64]) -> SharedValue {
    let mut tail_offset = schema.static_bound;
    if fields.is_empty() {
        return read_by_schema(ptr, schema);
    }
    if let Some(schema_fields) = &schema.fields.sub_fields {
        let mut map = SharedMap::new();
        'SEARCH:
        for field in fields {
            if let Some(index_path) = schema.id_index.get(field) {
                if index_path.is_empty() {
                    continue;
                }
                if let Some(mut field) = schema_fields.get(index_path[0]) {
                    for i in index_path.iter().skip(1) {
                        if let Some(Some(sub_field)) = field.sub_fields.as_ref().map(|sub| sub.get(*i)) {
                            field = sub_field;
                        } else {
                            continue 'SEARCH;
                        }
                    }
                    map.insert_key_id(field.name_id, read_field(ptr, field, false, &mut tail_offset));
                }
            }
        }
        return SharedValue::Map(map)
    }
    SharedValue::Null
}
<|endoftext|>