<commit_before>use fuse::{Request, ReplyEntry};
use libc::ENOENT;
use rustc_serialize::json;
use std::path::Path;

use fs::constants;
use fs::flags;
use fs::GoodDataFS;
use fs::helpers::{create_inode_directory_attributes, create_inode_file_attributes};
use fs::inode;
use gd;
use object;

fn feature_flags_json(fs: &mut GoodDataFS,
                      inode_parent: &inode::Inode,
                      _req: &Request,
                      _parent: u64,
                      _name: &Path,
                      reply: ReplyEntry) {
    let inode = inode::Inode::serialize(&inode::Inode {
        project: inode_parent.project,
        category: flags::Category::Internal as u8,
        item: 0,
        reserved: flags::ReservedFile::FeatureFlagsJson as u8,
    });

    let pid = (inode_parent.project - 1) as usize;
    let project: &object::Project = &fs.client().projects().as_ref().unwrap()[pid].clone();

    let feature_flags = project.feature_flags(&mut fs.client);
    if feature_flags.is_some() {
        let json: String = feature_flags.unwrap().into();

        let attr =
            create_inode_file_attributes(inode, json.len() as u64, constants::DEFAULT_CREATE_TIME);
        reply.entry(&constants::DEFAULT_TTL, &attr, 0);
    }
}

fn project_json(fs: &mut GoodDataFS,
                inode_parent: &inode::Inode,
                _req: &Request,
                _parent: u64,
                _name: &Path,
                reply: ReplyEntry) {
    let inode = inode::Inode::serialize(&inode::Inode {
        project: inode_parent.project,
        category: flags::Category::Internal as u8,
        item: 0,
        reserved: flags::ReservedFile::ProjectJson as u8,
    });

    let client: &gd::GoodDataClient = fs.client();
    let projects = client.projects().as_ref();
    let json = json::as_pretty_json(&projects.unwrap()[(inode_parent.project - 1) as usize])
        .to_string();
    let attr =
        create_inode_file_attributes(inode, json.len() as u64, constants::DEFAULT_CREATE_TIME);
    reply.entry(&constants::DEFAULT_TTL, &attr, 0);
}

fn permissions_json(fs: &mut GoodDataFS,
                    inode_parent: &inode::Inode,
                    _req: &Request,
                    _parent: u64,
                    _name: &Path,
                    reply: ReplyEntry) {
    let inode = inode::Inode::serialize(&inode::Inode {
        project: inode_parent.project,
        category: flags::Category::Internal as u8,
        item: 0,
        reserved: flags::ReservedFile::PermissionsJson as u8,
    });

    let pid = (inode_parent.project - 1) as usize;
    let project: &object::Project = &fs.client().projects().as_ref().unwrap()[pid].clone();
    let json: String = project.user_permissions(&mut fs.client).into();

    let attr =
        create_inode_file_attributes(inode, json.len() as u64, constants::DEFAULT_CREATE_TIME);
    reply.entry(&constants::DEFAULT_TTL, &attr, 0);
}

fn roles_json(fs: &mut GoodDataFS,
              inode_parent: &inode::Inode,
              _req: &Request,
              _parent: u64,
              _name: &Path,
              reply: ReplyEntry) {
    let inode = inode::Inode::serialize(&inode::Inode {
        project: inode_parent.project,
        category: flags::Category::Internal as u8,
        item: 0,
        reserved: flags::ReservedFile::RolesJson as u8,
    });

    let pid = (inode_parent.project - 1) as usize;
    let project: &object::Project = &fs.client().projects().as_ref().unwrap()[pid].clone();
    let json: String = project.user_roles(&mut fs.client).into();

    let attr =
        create_inode_file_attributes(inode, json.len() as u64, constants::DEFAULT_CREATE_TIME);
    reply.entry(&constants::DEFAULT_TTL, &attr, 0);
}

pub fn lookup(fs: &mut GoodDataFS, req: &Request, parent: u64, name: &Path, reply: ReplyEntry) {
    println!("GoodDataFS::lookup() - Reading parent {} - {:?}, path {:?}",
             parent,
             inode::Inode::deserialize(parent),
             name.to_str().unwrap());
    if parent == constants::INODE_ROOT && name.to_str() == Some(constants::USER_JSON_FILENAME) {
        reply.entry(&constants::DEFAULT_TTL, &fs.get_user_file_attributes(), 0);
    } else if parent == constants::INODE_ROOT && name.to_str() == Some(constants::PROJECTS_DIRNAME) {
        reply.entry(&constants::DEFAULT_TTL,
                    &fs.get_projects_dir_attributes(),
                    0);
    } else if parent == constants::INODE_PROJECTS &&
       name.to_str() == Some(constants::PROJECTS_JSON_FILENAME) {
        reply.entry(&constants::DEFAULT_TTL,
                    &fs.get_projects_file_attributes(),
                    0);
    } else if parent == constants::INODE_PROJECTS {
        let mut i: u64 = 0;
        let client: &gd::GoodDataClient = fs.client();
        let projects = client.projects().as_ref();
        for project in projects.unwrap().into_iter() {
            let title: &String = project.project()
                .meta()
                .title()
                .as_ref()
                .unwrap();

            if title == name.to_str().unwrap() {
                break;
            }
            i += 1;
        }

        let inode = (i + 1) << 48;
        println!("GoodDataFS::lookup() - Adding path {:?}, inode {} - {:?}",
                 name,
                 inode,
                 inode::Inode::deserialize(inode));
        let attr = create_inode_directory_attributes(inode);
        reply.entry(&constants::DEFAULT_TTL, &attr, 0);
    } else {
        let inode_parent = inode::Inode::deserialize(parent);
        if inode_parent.project > 0 {
            match name.to_str() {
                Some(constants::FEATURE_FLAGS_JSON_FILENAME) => {
                    feature_flags_json(fs, &inode_parent, req, parent, name, reply)
                }
                Some(constants::PROJECT_JSON_FILENAME) => {
                    project_json(fs, &inode_parent, req, parent, name, reply)
                }
                Some(constants::PERMISSIONS_JSON_FILENAME) => {
                    permissions_json(fs, &inode_parent, req, parent, name, reply)
                }
                Some(constants::ROLES_JSON_FILENAME) => {
                    roles_json(fs, &inode_parent, req, parent, name, reply)
                }
                _ => reply.error(ENOENT),
            }
        } else {
            reply.error(ENOENT);
        }
    }
}
<commit_msg>More match cosmetics<commit_after>use fuse::{Request, ReplyEntry};
use libc::ENOENT;
use rustc_serialize::json;
use std::path::Path;

use fs::constants;
use fs::flags;
use fs::GoodDataFS;
use fs::helpers::{create_inode_directory_attributes, create_inode_file_attributes};
use fs::inode;
use gd;
use object;

fn feature_flags_json(fs: &mut GoodDataFS, inode_parent: &inode::Inode, reply: ReplyEntry) {
    let inode = inode::Inode::serialize(&inode::Inode {
        project: inode_parent.project,
        category: flags::Category::Internal as u8,
        item: 0,
        reserved: flags::ReservedFile::FeatureFlagsJson as u8,
    });

    let pid = (inode_parent.project - 1) as usize;
    let project: &object::Project = &fs.client().projects().as_ref().unwrap()[pid].clone();

    let feature_flags = project.feature_flags(&mut fs.client);
    if feature_flags.is_some() {
        let json: String = feature_flags.unwrap().into();

        let attr =
            create_inode_file_attributes(inode, json.len() as u64, constants::DEFAULT_CREATE_TIME);
        reply.entry(&constants::DEFAULT_TTL, &attr, 0);
    }
}

fn project_json(fs: &mut GoodDataFS, inode_parent: &inode::Inode, reply: ReplyEntry) {
    let inode = inode::Inode::serialize(&inode::Inode {
        project: inode_parent.project,
        category: flags::Category::Internal as u8,
        item: 0,
        reserved: flags::ReservedFile::ProjectJson as u8,
    });

    let client: &gd::GoodDataClient = fs.client();
    let projects = client.projects().as_ref();
    let json = json::as_pretty_json(&projects.unwrap()[(inode_parent.project - 1) as usize])
        .to_string();
    let attr =
        create_inode_file_attributes(inode, json.len() as u64, constants::DEFAULT_CREATE_TIME);
    reply.entry(&constants::DEFAULT_TTL, &attr, 0);
}

fn permissions_json(fs: &mut GoodDataFS, inode_parent: &inode::Inode, reply: ReplyEntry) {
    let inode = inode::Inode::serialize(&inode::Inode {
        project: inode_parent.project,
        category: flags::Category::Internal as u8,
        item: 0,
        reserved: flags::ReservedFile::PermissionsJson as u8,
    });

    let pid = (inode_parent.project - 1) as usize;
    let project: &object::Project = &fs.client().projects().as_ref().unwrap()[pid].clone();
    let json: String = project.user_permissions(&mut fs.client).into();

    let attr =
        create_inode_file_attributes(inode, json.len() as u64, constants::DEFAULT_CREATE_TIME);
    reply.entry(&constants::DEFAULT_TTL, &attr, 0);
}

fn roles_json(fs: &mut GoodDataFS, inode_parent: &inode::Inode, reply: ReplyEntry) {
    let inode = inode::Inode::serialize(&inode::Inode {
        project: inode_parent.project,
        category: flags::Category::Internal as u8,
        item: 0,
        reserved: flags::ReservedFile::RolesJson as u8,
    });

    let pid = (inode_parent.project - 1) as usize;
    let project: &object::Project = &fs.client().projects().as_ref().unwrap()[pid].clone();
    let json: String = project.user_roles(&mut fs.client).into();

    let attr =
        create_inode_file_attributes(inode, json.len() as u64, constants::DEFAULT_CREATE_TIME);
    reply.entry(&constants::DEFAULT_TTL, &attr, 0);
}

pub fn lookup(fs: &mut GoodDataFS, _req: &Request, parent: u64, name: &Path, reply: ReplyEntry) {
    println!("GoodDataFS::lookup() - Reading parent {} - {:?}, path {:?}",
             parent,
             inode::Inode::deserialize(parent),
             name.to_str().unwrap());
    if parent == constants::INODE_ROOT && name.to_str() == Some(constants::USER_JSON_FILENAME) {
        reply.entry(&constants::DEFAULT_TTL, &fs.get_user_file_attributes(), 0);
    } else if parent == constants::INODE_ROOT && name.to_str() == Some(constants::PROJECTS_DIRNAME) {
        reply.entry(&constants::DEFAULT_TTL,
                    &fs.get_projects_dir_attributes(),
                    0);
    } else if parent == constants::INODE_PROJECTS &&
       name.to_str() == Some(constants::PROJECTS_JSON_FILENAME) {
        reply.entry(&constants::DEFAULT_TTL,
                    &fs.get_projects_file_attributes(),
                    0);
    } else if parent == constants::INODE_PROJECTS {
        let mut i: u64 = 0;
        let client: &gd::GoodDataClient = fs.client();
        let projects = client.projects().as_ref();
        for project in projects.unwrap().into_iter() {
            let title: &String = project.project()
                .meta()
                .title()
                .as_ref()
                .unwrap();

            if title == name.to_str().unwrap() {
                break;
            }
            i += 1;
        }

        let inode = (i + 1) << 48;
        println!("GoodDataFS::lookup() - Adding path {:?}, inode {} - {:?}",
                 name,
                 inode,
                 inode::Inode::deserialize(inode));
        let attr = create_inode_directory_attributes(inode);
        reply.entry(&constants::DEFAULT_TTL, &attr, 0);
    } else {
        let inode_parent = inode::Inode::deserialize(parent);
        if inode_parent.project > 0 {
            match name.to_str() {
                Some(constants::FEATURE_FLAGS_JSON_FILENAME) => {
                    feature_flags_json(fs, &inode_parent, reply)
                }
                Some(constants::PROJECT_JSON_FILENAME) => project_json(fs, &inode_parent, reply),
                Some(constants::PERMISSIONS_JSON_FILENAME) => {
                    permissions_json(fs, &inode_parent, reply)
                }
                Some(constants::ROLES_JSON_FILENAME) => roles_json(fs, &inode_parent, reply),
                _ => reply.error(ENOENT),
            }
        } else {
            reply.error(ENOENT);
        }
    }
}
<|endoftext|>


<commit_before>// TODO: Implement `libtock_runtime` and `libtock_unittest`, which are
// referenced in the comment on `RawSyscalls`.

/// `RawSyscalls` allows a fake Tock kernel to be injected into components for
/// unit testing. It is implemented by `libtock_runtime::TockSyscalls` and
/// `libtock_unittest::FakeSyscalls`. Components should not use `RawSyscalls`
/// directly; instead, use the `Syscalls` trait, which provides higher-level
/// interfaces to the system calls.

// RawSyscalls is designed to minimize the amount of handwritten assembly code
// needed without generating unnecessary instructions. This comment describes
// the thought process that led to the choice of methods for RawSyscalls. There
// are a few major considerations affecting its design:
//
//     1. Most system calls only clobber r0-r4 (*), while yield has a far longer
//        clobber list. As such, yield must have its own assembly
//        implementation.
//     2. The compiler is unable to optimize away unused arguments. For example,
//        memop's "get process RAM start address" operation only needs r0 set,
//        while memop's "break" operation needs both r0 and r1 set. If our
//        inline assembly calls "get process RAM start address" but sets both r0
//        and r1, the compiler doesn't know that r1 will be
//        ignored so setting that register will not be optimized away. Therefore
//        we want to set the minimum number of argument registers possible.
//     3. The cost of specifying unused return registers is only that of
//        unnecessarily marking a register as clobbered. Explanation: After
//        inlining, an unused register is marked as "changed by the assembly"
//        but can immediately be re-used by the compiler, which is the same as a
//        clobbered register. System calls should generally be
//        inlined -- and even if they aren't, the unused return values will
//        probably be passed in caller-saved registers (this is true for the C
//        ABI, so probably true for the Rust ABI), which are treated as
//        clobbered regardless.
//
// (*) When this file refers to registers, it uses the same naming convention as
// the Tock 2.0 syscalls TRD. Registers r0-r4 correspond to ARM registers r0-r4
// and RISC-V registers a0-a4.
//
// Currently, yield takes exactly one argument (to specify what yield type to
// do). Therefore we only need one raw yield call.
//
// Based on these considerations, it would make sense to have the following
// methods:
//     yield
//     zero_arg_syscall
//     one_arg_syscall
//     two_arg_syscall
//     three_arg_syscall
//     four_arg_syscall
//
// However, there are no system calls that take 0 or 3 arguments, so we do not
// need the corresponding methods. This leaves yield, 1-arg, 2-arg, and 4-arg
// system calls.
//
// The 1-arg and 2-arg system calls are only used for memop. Memop currently has
// the property that none of its operations can lead to undefined behavior.
// Therefore, we can rename the 1-arg and 2-arg system calls to zero_arg_memop
// and one_arg_memop and make them safe methods (the argument counts change
// because the number of system call arguments is one greater than the number of
// arguments passed to the specific op).
//
// This only leaves four_arg_syscall, which is used to implement subscribe,
// command, read-write allow, and read-only allow.
//
// Therefore the final design has 4 methods:
//     yield
//     zero_arg_memop
//     one_arg_memop
//     four_arg_syscall
//
// If a new system call class that uses fewer than four arguments is added, then
// the above list will need to be revised.
//
// Note that `command` always needs to use four_arg_syscall, even when calling a
// command with fewer arguments, because we don't want to leak the
// (possibly secret) values in the r2 and r3 registers to untrusted capsules.
// Yield and memop do not have this concern and can leave arbitrary data in r2
// and r3, because they are implemented by the core kernel, which is trusted.
//
// The success type for Memop calls depends on the operation performed. However,
// all *currently defined* memop operations return either Success or Success
// with u32. Therefore, the memop implementations only need to mark r0 and r1 as
// clobbered, not r2 and r3. This choice of clobbers will need to be revisited
// if and when a memop operation that returns more data is added.
//
// The decision of where to use u32 and usize can be a bit tricky. The Tock
// syscall ABI is currently only specified for 32-bit systems, so on real Tock
// systems both types match the size of a register, but the unit test
// environment can be either 32 bit or 64 bit. This interface uses usize for
// values that can contain pointers, so that pointers are not truncated in the
// unit test environment. To keep types as consistent as possible, it uses u32
// for all values that cannot be pointers.
pub trait RawSyscalls {
    // raw_yield should:
    //     1. Call syscall class 0
    //     2. Use register r0 for input and output as an inlateout register,
    //        passing in r0_in and returning its value.
    //     3. Mark all caller-saved registers as lateout clobbers.
    //     4. NOT provide any of the following options:
    //            pure             (yield has side effects)
    //            nomem            (a callback can read + write globals)
    //            readonly         (a callback can write globals)
    //            preserves_flags  (a callback can change flags)
    //            noreturn         (yield is expected to return)
    //            nostack          (a callback needs the stack)
    //
    // Design note: This is safe because the yield types that currently exist
    // are safe. If an unsafe yield type is added, we will need to make
    // raw_yield unsafe. Although raw_yield shouldn't be called by code outside
    // this crate, it can be, so that is a backwards-incompatible change. We
    // pass YieldType rather than a usize because if we used usize directly then
    // this API becomes unsound if the kernel adds support for an unsafe yield
    // type (or even one that takes one more argument).
    fn raw_yield(r0_in: YieldType) -> u32;

    // four_arg_syscall is used to invoke the subscribe, command, read-write
    // allow, and read-only allow system calls.
    //
    // four_arg_syscall's inline assembly should have the following properties:
    //     1. Calls the syscall class specified by class
    //     2. Passes r0-r3 in the corresponding registers as inlateout
    //        registers. Returns r0-r3 in order.
    //     3. Does not mark any registers as clobbered.
    //     4. Has all of the following options:
    //            preserves_flags  (these system calls do not touch flags)
    //            nostack          (these system calls do not touch the stack)
    //     5. Does NOT have any of the following options:
    //            pure      (these system calls have side effects)
    //            nomem     (the compiler needs to write to globals before allow)
    //            readonly  (rw allow can modify memory)
    //            noreturn  (all these system calls are expected to return)
    //
    // Note that subscribe's application data argument can potentially contain a
    // pointer, so r3 can contain a pointer (in addition to r1 and r2, which
    // more obviously contain pointers for subscribe and memop).
    //
    // For subscribe(), the callback pointer should be either 0 (for the null
    // callback) or an `unsafe extern fn(u32, u32, u32, usize)`.
    /// # Safety
    /// `four_arg_syscall` must NOT be used to invoke yield. Otherwise, it has
    /// the same safety invariants as the underlying system call, which varies
    /// depending on the system call class.
    unsafe fn four_arg_syscall(
        r0: u32,
        r1: u32,
        r2: usize,
        r3: usize,
        class: u8,
    ) -> (u32, usize, usize, usize);

    // zero_arg_memop is used to invoke memop operations that do not accept an
    // argument register. Because there are no memop commands that set r2 or r3,
    // this only needs to return r0 and r1.
    //
    // Memop commands may panic in the unit test environment, as not all memop
    // calls can be sensibly implemented in that environment.
    //
    // zero_arg_memop's inline assembly should have the following properties:
    //     1. Calls syscall class 5
    //     2. Specifies r0 as an inlateout register, and r1 as a lateout
    //        register.
    //     3. Does not mark any registers as clobbered.
    //     4. Has all of the following options:
    //            preserves_flags
    //            nostack
    //            nomem            (it is okay for the compiler to cache globals
    //                              across memop calls)
    //     5. Does NOT have any of the following options:
    //            pure      (two invocations of the same memop can return
    //                       different values)
    //            readonly  (incompatible with nomem)
    //            noreturn
    //
    // Design note: like raw_yield, this is safe because memops that currently
    // exist are safe. zero_arg_memop takes a ZeroArgMemop rather than a u32 so
    // that if the kernel adds an unsafe memop -- or one that can clobber r2/r3
    // --  this API doesn't become unsound.
    fn zero_arg_memop(r0_in: ZeroArgMemop) -> (u32, usize);

    // one_arg_memop is used to invoke memop operations that take an argument.
    // Because there are no memop operations that set r2 or r3, this only needs
    // to return r0 and r1.
    //
    // one_arg_memop's inline assembly should:
    //     1. Call syscall class 5
    //     2. Specify r0 and r1 as inlateout registers, and return (r0, r1)
    //     3. Not mark any registers as clobbered.
    //     4. Have all of the following options:
    //            preserves_flags
    //            nostack
    //            nomem            (the compiler can cache globals across memop
    //                              calls)
    //     5. Does NOT have any of the following options:
    //            pure      Two invocations of sbrk can return different values
    //            readonly  Incompatible with nomem
    //            noreturn
    //
    // Design note: like raw_yield, this is safe because memops that currently
    // exist are safe. zero_arg_memop takes a ZeroArgMemop rather than a u32 so
    // that if the kernel adds an unsafe memop -- or one that can clobber r2/r3
    // -- this API doesn't become unsound.
    fn one_arg_memop(r0_in: OneArgMemop, r1: usize) -> (u32, usize);
}

#[non_exhaustive]
#[repr(u32)]
pub enum OneArgMemop {
    Brk = 0,
    Sbrk = 1,
    FlashRegionStart = 8,
    FlashRegionEnd = 9,
    SpecifyStackTop = 10,
    SpecifyHeapStart = 11,
    // Note: before adding new memop operations, make sure the assumptions in
    // the design notes on `one_arg_memop` are valid for the new operation type.
}

// TODO: When the numeric values (0 and 1) are assigned to the yield types,
// specify those values here.
#[non_exhaustive]
#[repr(u32)]
pub enum YieldType {
    Wait,
    NoWait,
}

#[non_exhaustive]
#[repr(u32)]
pub enum ZeroArgMemop {
    MemoryStart = 2,
    MemoryEnd = 3,
    FlashStart = 4,
    FlashEnd = 5,
    GrantStart = 6,
    FlashRegions = 7,
    // Note: before adding new memop operations, make sure the assumptions in
    // the design notes on `zero_arg_memop` are valid for the new operation
    // type.
}
<commit_msg>Core WG meeting 2021-01-29 conclusion: document that `RawSyscalls`' methods should only be called by `libtock_platform`.<commit_after>// TODO: Implement `libtock_unittest`, which is referenced in the comment on
// `RawSyscalls`.

/// `RawSyscalls` allows a fake Tock kernel to be injected into components for
/// unit testing. It is implemented by `libtock_runtime::TockSyscalls` and
/// `libtock_unittest::FakeSyscalls`. **Components should not use `RawSyscalls`
/// directly; instead, use the `Syscalls` trait, which provides higher-level
/// interfaces to the system calls.**

// RawSyscalls is designed to minimize the amount of handwritten assembly code
// needed without generating unnecessary instructions. This comment describes
// the thought process that led to the choice of methods for RawSyscalls. There
// are a few major considerations affecting its design:
//
//     1. Most system calls only clobber r0-r4 (*), while yield has a far longer
//        clobber list. As such, yield must have its own assembly
//        implementation.
//     2. The compiler is unable to optimize away unused arguments. For example,
//        memop's "get process RAM start address" operation only needs r0 set,
//        while memop's "break" operation needs both r0 and r1 set. If our
//        inline assembly calls "get process RAM start address" but sets both r0
//        and r1, the compiler doesn't know that r1 will be
//        ignored so setting that register will not be optimized away. Therefore
//        we want to set the minimum number of argument registers possible.
//     3. The cost of specifying unused return registers is only that of
//        unnecessarily marking a register as clobbered. Explanation: After
//        inlining, an unused register is marked as "changed by the assembly"
//        but can immediately be re-used by the compiler, which is the same as a
//        clobbered register. System calls should generally be
//        inlined -- and even if they aren't, the unused return values will
//        probably be passed in caller-saved registers (this is true for the C
//        ABI, so probably true for the Rust ABI), which are treated as
//        clobbered regardless.
//
// (*) When this file refers to registers, it uses the same naming convention as
// the Tock 2.0 syscalls TRD. Registers r0-r4 correspond to ARM registers r0-r4
// and RISC-V registers a0-a4.
//
// Currently, yield takes exactly one argument (to specify what yield type to
// do). Therefore we only need one raw yield call.
//
// Based on these considerations, it would make sense to have the following
// methods:
//     yield
//     zero_arg_syscall
//     one_arg_syscall
//     two_arg_syscall
//     three_arg_syscall
//     four_arg_syscall
//
// However, there are no system calls that take 0 or 3 arguments, so we do not
// need the corresponding methods. This leaves yield, 1-arg, 2-arg, and 4-arg
// system calls.
//
// The 1-arg and 2-arg system calls are only used for memop. Memop currently has
// the property that none of its operations can lead to undefined behavior.
// Therefore, we can rename the 1-arg and 2-arg system calls to zero_arg_memop
// and one_arg_memop and make them safe methods (the argument counts change
// because the number of system call arguments is one greater than the number of
// arguments passed to the specific op).
//
// This only leaves four_arg_syscall, which is used to implement subscribe,
// command, read-write allow, and read-only allow.
//
// Therefore the final design has 4 methods:
//     yield
//     zero_arg_memop
//     one_arg_memop
//     four_arg_syscall
//
// If a new system call class that uses fewer than four arguments is added, then
// the above list will need to be revised.
//
// Note that `command` always needs to use four_arg_syscall, even when calling a
// command with fewer arguments, because we don't want to leak the
// (possibly secret) values in the r2 and r3 registers to untrusted capsules.
// Yield and memop do not have this concern and can leave arbitrary data in r2
// and r3, because they are implemented by the core kernel, which is trusted.
//
// The success type for Memop calls depends on the operation performed. However,
// all *currently defined* memop operations return either Success or Success
// with u32. Therefore, the memop implementations only need to mark r0 and r1 as
// clobbered, not r2 and r3. This choice of clobbers will need to be revisited
// if and when a memop operation that returns more data is added.
//
// The decision of where to use u32 and usize can be a bit tricky. The Tock
// syscall ABI is currently only specified for 32-bit systems, so on real Tock
// systems both types match the size of a register, but the unit test
// environment can be either 32 bit or 64 bit. This interface uses usize for
// values that can contain pointers, so that pointers are not truncated in the
// unit test environment. To keep types as consistent as possible, it uses u32
// for all values that cannot be pointers.
pub trait RawSyscalls {
    // raw_yield should:
    //     1. Call syscall class 0
    //     2. Use register r0 for input and output as an inlateout register,
    //        passing in r0_in and returning its value.
    //     3. Mark all caller-saved registers as lateout clobbers.
    //     4. NOT provide any of the following options:
    //            pure             (yield has side effects)
    //            nomem            (a callback can read + write globals)
    //            readonly         (a callback can write globals)
    //            preserves_flags  (a callback can change flags)
    //            noreturn         (yield is expected to return)
    //            nostack          (a callback needs the stack)
    //
    // Design note: This is safe because the yield types that currently exist
    // are safe. If an unsafe yield type is added, we will need to make
    // raw_yield unsafe. Although raw_yield shouldn't be called by code outside
    // this crate, it can be, so that is a backwards-incompatible change. We
    // pass YieldType rather than a usize because if we used usize directly then
    // this API becomes unsound if the kernel adds support for an unsafe yield
    // type (or even one that takes one more argument).
    /// `raw_yield` should only be called by `libtock_platform`.
    fn raw_yield(r0_in: YieldType) -> u32;

    // four_arg_syscall is used to invoke the subscribe, command, read-write
    // allow, and read-only allow system calls.
    //
    // four_arg_syscall's inline assembly should have the following properties:
    //     1. Calls the syscall class specified by class
    //     2. Passes r0-r3 in the corresponding registers as inlateout
    //        registers. Returns r0-r3 in order.
    //     3. Does not mark any registers as clobbered.
    //     4. Has all of the following options:
    //            preserves_flags  (these system calls do not touch flags)
    //            nostack          (these system calls do not touch the stack)
    //     5. Does NOT have any of the following options:
    //            pure      (these system calls have side effects)
    //            nomem     (the compiler needs to write to globals before allow)
    //            readonly  (rw allow can modify memory)
    //            noreturn  (all these system calls are expected to return)
    //
    // Note that subscribe's application data argument can potentially contain a
    // pointer, so r3 can contain a pointer (in addition to r1 and r2, which
    // more obviously contain pointers for subscribe and memop).
    //
    // For subscribe(), the callback pointer should be either 0 (for the null
    // callback) or an `unsafe extern fn(u32, u32, u32, usize)`.
    /// `four_arg_syscall` should only be called by `libtock_platform`.
    ///
    /// # Safety
    /// `four_arg_syscall` must NOT be used to invoke yield. Otherwise, it has
    /// the same safety invariants as the underlying system call, which varies
    /// depending on the system call class.
    unsafe fn four_arg_syscall(
        r0: u32,
        r1: u32,
        r2: usize,
        r3: usize,
        class: u8,
    ) -> (u32, usize, usize, usize);

    // zero_arg_memop is used to invoke memop operations that do not accept an
    // argument register. Because there are no memop commands that set r2 or r3,
    // this only needs to return r0 and r1.
    //
    // Memop commands may panic in the unit test environment, as not all memop
    // calls can be sensibly implemented in that environment.
    //
    // zero_arg_memop's inline assembly should have the following properties:
    //     1. Calls syscall class 5
    //     2. Specifies r0 as an inlateout register, and r1 as a lateout
    //        register.
    //     3. Does not mark any registers as clobbered.
    //     4. Has all of the following options:
    //            preserves_flags
    //            nostack
    //            nomem            (it is okay for the compiler to cache globals
    //                              across memop calls)
    //     5. Does NOT have any of the following options:
    //            pure      (two invocations of the same memop can return
    //                       different values)
    //            readonly  (incompatible with nomem)
    //            noreturn
    //
    // Design note: like raw_yield, this is safe because memops that currently
    // exist are safe. zero_arg_memop takes a ZeroArgMemop rather than a u32 so
    // that if the kernel adds an unsafe memop -- or one that can clobber r2/r3
    // --  this API doesn't become unsound.
    /// `four_arg_syscall` should only be called by `libtock_platform`.
    fn zero_arg_memop(r0_in: ZeroArgMemop) -> (u32, usize);

    // one_arg_memop is used to invoke memop operations that take an argument.
    // Because there are no memop operations that set r2 or r3, this only needs
    // to return r0 and r1.
    //
    // one_arg_memop's inline assembly should:
    //     1. Call syscall class 5
    //     2. Specify r0 and r1 as inlateout registers, and return (r0, r1)
    //     3. Not mark any registers as clobbered.
    //     4. Have all of the following options:
    //            preserves_flags
    //            nostack
    //            nomem            (the compiler can cache globals across memop
    //                              calls)
    //     5. Does NOT have any of the following options:
    //            pure      Two invocations of sbrk can return different values
    //            readonly  Incompatible with nomem
    //            noreturn
    //
    // Design note: like raw_yield, this is safe because memops that currently
    // exist are safe. zero_arg_memop takes a ZeroArgMemop rather than a u32 so
    // that if the kernel adds an unsafe memop -- or one that can clobber r2/r3
    // -- this API doesn't become unsound.
    /// `four_arg_syscall` should only be called by `libtock_platform`.
    fn one_arg_memop(r0_in: OneArgMemop, r1: usize) -> (u32, usize);
}

#[non_exhaustive]
#[repr(u32)]
pub enum OneArgMemop {
    Brk = 0,
    Sbrk = 1,
    FlashRegionStart = 8,
    FlashRegionEnd = 9,
    SpecifyStackTop = 10,
    SpecifyHeapStart = 11,
    // Note: before adding new memop operations, make sure the assumptions in
    // the design notes on `one_arg_memop` are valid for the new operation type.
}

// TODO: When the numeric values (0 and 1) are assigned to the yield types,
// specify those values here.
#[non_exhaustive]
#[repr(u32)]
pub enum YieldType {
    Wait,
    NoWait,
}

#[non_exhaustive]
#[repr(u32)]
pub enum ZeroArgMemop {
    MemoryStart = 2,
    MemoryEnd = 3,
    FlashStart = 4,
    FlashEnd = 5,
    GrantStart = 6,
    FlashRegions = 7,
    // Note: before adding new memop operations, make sure the assumptions in
    // the design notes on `zero_arg_memop` are valid for the new operation
    // type.
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::iter::AdditiveIterator;

use super::levenshtein_similarity;

type FuzzyOverlapMap = HashMap<usize, (usize, f64)>;

pub fn fuzzy_jaccard_similarity(s1: &str, s2: &str, delta: f64) -> f64 {
    let (overlap, s1_token_count, s2_token_count) = fuzzy_overlap(s1, s2, delta);
    let weight = fuzzy_overlap_weight(overlap);

    weight / (s1_token_count as f64 + s2_token_count as f64 - weight)
}

fn fuzzy_overlap(s1: &str, s2: &str, delta: f64) -> (FuzzyOverlapMap, usize, usize) {
    let s1_tokens = s1.split(|&: c: char| c.is_whitespace()).collect::<Vec<&str>>();
    let s2_tokens = s2.split(|&: c: char| c.is_whitespace()).collect::<Vec<&str>>();

    let mut similarities : FuzzyOverlapMap = HashMap::new();

    for (i, &t2) in s2_tokens.iter().enumerate() {
        let mut max_sim = 0f64;
        let mut max_j = 0;

        for (j, &t1) in s1_tokens.iter().enumerate() {
            let lev_sim = levenshtein_similarity(t1, t2);

            if lev_sim >= delta && lev_sim > max_sim {
                max_sim = lev_sim;
                max_j = j;
            }
        }

        // FIXME: clone
        match similarities.get(&max_j).clone() {
            Some(&(_, sim)) if sim < max_sim => similarities[max_j] = (i, max_sim),
            None => {
                similarities.insert(max_j, (i, max_sim));
            },
            _ => ()
        }
    }

    (similarities, s1_tokens.len(), s2_tokens.len())
}

fn fuzzy_overlap_weight(map: FuzzyOverlapMap) -> f64 {
    map.values().map(|&(_, sim)| sim).sum()
}

#[cfg(test)]
mod test {
    use test::Bencher;
    use super::fuzzy_jaccard_similarity;

    #[test]
    fn value_from_paper() {
        assert_eq!(15f64 / 17f64, fuzzy_jaccard_similarity("nba mcgrady", "macgrady nba", 0.8));
    }

    #[bench]
    fn bench(b: &mut Bencher) {
        b.iter(|| fuzzy_jaccard_similarity("nba mcgrady", "macgrady nba", 0.8))
    }
}

<commit_msg>remove obsolete closure syntax<commit_after>use std::collections::HashMap;
use std::iter::AdditiveIterator;

use super::levenshtein_similarity;

type FuzzyOverlapMap = HashMap<usize, (usize, f64)>;

pub fn fuzzy_jaccard_similarity(s1: &str, s2: &str, delta: f64) -> f64 {
    let (overlap, s1_token_count, s2_token_count) = fuzzy_overlap(s1, s2, delta);
    let weight = fuzzy_overlap_weight(overlap);

    weight / (s1_token_count as f64 + s2_token_count as f64 - weight)
}

fn fuzzy_overlap(s1: &str, s2: &str, delta: f64) -> (FuzzyOverlapMap, usize, usize) {
    let s1_tokens = s1.split(|c: char| c.is_whitespace()).collect::<Vec<&str>>();
    let s2_tokens = s2.split(|c: char| c.is_whitespace()).collect::<Vec<&str>>();

    let mut similarities : FuzzyOverlapMap = HashMap::new();

    for (i, &t2) in s2_tokens.iter().enumerate() {
        let mut max_sim = 0f64;
        let mut max_j = 0;

        for (j, &t1) in s1_tokens.iter().enumerate() {
            let lev_sim = levenshtein_similarity(t1, t2);

            if lev_sim >= delta && lev_sim > max_sim {
                max_sim = lev_sim;
                max_j = j;
            }
        }

        // FIXME: clone
        match similarities.get(&max_j).clone() {
            Some(&(_, sim)) if sim < max_sim => similarities[max_j] = (i, max_sim),
            None => {
                similarities.insert(max_j, (i, max_sim));
            },
            _ => ()
        }
    }

    (similarities, s1_tokens.len(), s2_tokens.len())
}

fn fuzzy_overlap_weight(map: FuzzyOverlapMap) -> f64 {
    map.values().map(|&(_, sim)| sim).sum()
}

#[cfg(test)]
mod test {
    use test::Bencher;
    use super::fuzzy_jaccard_similarity;

    #[test]
    fn value_from_paper() {
        assert_eq!(15f64 / 17f64, fuzzy_jaccard_similarity("nba mcgrady", "macgrady nba", 0.8));
    }

    #[bench]
    fn bench(b: &mut Bencher) {
        b.iter(|| fuzzy_jaccard_similarity("nba mcgrady", "macgrady nba", 0.8))
    }
}

<|endoftext|>


<commit_before>// Copyright 2016 Revolution Solid & Contributors.
// author(s): sysnett, carlos-lopez-garces
// rust-monster is licensed under an MIT License.

//! GA Scaling Schemes
//!
//! Scales the raw score of a Population's individuals.

use super::ga_core::GASolution;
use super::ga_population::GAPopulation;

/// Scaling Scheme Trait
///
/// Embeeded in the population, scales the values of raw score in a
/// GASolution to set their fitness score
pub trait GAScaling<T: GASolution>
{
    fn evaluate(&self, pop: &mut GAPopulation<T>);
}

/// No Scaling - Raw and Scaled are the same
pub struct GANoScaling;

impl<T: GASolution> GAScaling<T> for GANoScaling
{
    fn evaluate(&self, pop: &mut GAPopulation<T>)
    {
        // TODO: This is why we need iterators :(
        let pop_vec = pop.population();
        for ind in pop_vec
        {
            let rs = ind.score();
            ind.set_fitness(rs);
        }
    }
}

/// Linear Scaling
/// Uses a simple ```a*score+ b``` scaling.
/// ```a``` and ```b``` are the intersect of the linear function and are calculated
/// based on Goldberg's book implementation
pub struct GALinearScaling
{
    multiplier: f32
}

#[allow(unused_variables)]
const GA_LINEAR_SCALING_MULTIPLIER : f32 = 2.0;
impl GALinearScaling
{
    fn new(mult: f32) -> GALinearScaling
    {
        GALinearScaling{ multiplier: scaling }
    }

    fn prescale(&self, max: f32, min: f32, avg: f32) -> (f32, f32)
    {
        let m = self.multiplier;
        let a;
        let b;
        let delta;

        if min > ((m*avg - max) / (m - 1.0))
        {
            delta = max - avg;
            a = (m - 1.0) * avg / delta;
            b = avg * (max - m * avg) / delta;
        }
        else
        {
            delta = avg - min;
            a = avg / delta;
            b = (-1.0*min*avg) / delta;
        }

        (a, b)
    }
}

impl<T: GASolution> GAScaling<T> for GALinearScaling
{
    fn evaluate(&self, pop : &mut GAPopulation<T>)
    {
        let max = pop.best_by_raw_score().score();
        let min = pop.worst_by_raw_score().score();

        // TODO: avg should be part of GAPopulation
        let avg = (max - min) / 2.0;

        let (a, b) = self.prescale(max, min, avg);

        let pop_vec = pop.population();
        for ind in pop_vec
        {
            let rs = ind.score();
            ind.set_fitness(a*rs+b);
        }
    }
}

////////////////////////////////////////
// Tests
#[cfg(test)]
mod test
{
    use super::*;
    use super::super::ga_core::*;
    use super::super::ga_population::*;
    use super::super::ga_test::*;

    #[test]
    fn no_scaling()
    {
        ga_test_setup("ga_scaling::no_scaling");
        let f = GA_TEST_FITNESS_VAL;
        let mut population = GAPopulation::new(vec![GATestSolution::new(f)], GAPopulationSortOrder::HighIsBest);
        population.sort();

        let scaler = GANoScaling{};

        scaler.evaluate(&mut population);

        assert_eq!(population.individual(0, GAPopulationSortBasis::Raw).fitness(),
                   population.individual(0, GAPopulationSortBasis::Raw).score());

        ga_test_teardown();
    }

    #[test]
    fn linear_scaling()
    {
        ga_test_setup("ga_scaling::no_scaling");
        let f = GA_TEST_FITNESS_VAL;
        let mut population = GAPopulation::new(vec![GATestSolution::new(f)], GAPopulationSortOrder::HighIsBest);
        population.sort();

        let scaler = GALinearScaling{ multiplier: super::GA_LINEAR_SCALING_MULTIPLIER };

        scaler.evaluate(&mut population);

        // TODO: Real test
        assert!(population.individual(0, GAPopulationSortBasis::Raw).fitness() !=
                population.individual(0, GAPopulationSortBasis::Raw).score());

        ga_test_teardown();
    }

}
<commit_msg>fix it<commit_after>// Copyright 2016 Revolution Solid & Contributors.
// author(s): sysnett, carlos-lopez-garces
// rust-monster is licensed under an MIT License.

//! GA Scaling Schemes
//!
//! Scales the raw score of a Population's individuals.

use super::ga_core::GASolution;
use super::ga_population::GAPopulation;

/// Scaling Scheme Trait
///
/// Embeeded in the population, scales the values of raw score in a
/// GASolution to set their fitness score
pub trait GAScaling<T: GASolution>
{
    fn evaluate(&self, pop: &mut GAPopulation<T>);
}

/// No Scaling - Raw and Scaled are the same
pub struct GANoScaling;

impl<T: GASolution> GAScaling<T> for GANoScaling
{
    fn evaluate(&self, pop: &mut GAPopulation<T>)
    {
        // TODO: This is why we need iterators :(
        let pop_vec = pop.population();
        for ind in pop_vec
        {
            let rs = ind.score();
            ind.set_fitness(rs);
        }
    }
}

/// Linear Scaling
/// Uses a simple ```a*score+ b``` scaling.
/// ```a``` and ```b``` are the intersect of the linear function and are calculated
/// based on Goldberg's book implementation
pub struct GALinearScaling
{
    multiplier: f32
}

#[allow(unused_variables)]
const GA_LINEAR_SCALING_MULTIPLIER : f32 = 2.0;
impl GALinearScaling
{
    fn new(mult: f32) -> GALinearScaling
    {
        GALinearScaling{ multiplier: mult }
    }

    fn prescale(&self, max: f32, min: f32, avg: f32) -> (f32, f32)
    {
        let m = self.multiplier;
        let a;
        let b;
        let delta;

        if min > ((m*avg - max) / (m - 1.0))
        {
            delta = max - avg;
            a = (m - 1.0) * avg / delta;
            b = avg * (max - m * avg) / delta;
        }
        else
        {
            delta = avg - min;
            a = avg / delta;
            b = (-1.0*min*avg) / delta;
        }

        (a, b)
    }
}

impl<T: GASolution> GAScaling<T> for GALinearScaling
{
    fn evaluate(&self, pop : &mut GAPopulation<T>)
    {
        let max = pop.best_by_raw_score().score();
        let min = pop.worst_by_raw_score().score();

        // TODO: avg should be part of GAPopulation
        let avg = (max - min) / 2.0;

        let (a, b) = self.prescale(max, min, avg);

        let pop_vec = pop.population();
        for ind in pop_vec
        {
            let rs = ind.score();
            ind.set_fitness(a*rs+b);
        }
    }
}

////////////////////////////////////////
// Tests
#[cfg(test)]
mod test
{
    use super::*;
    use super::super::ga_core::*;
    use super::super::ga_population::*;
    use super::super::ga_test::*;

    #[test]
    fn no_scaling()
    {
        ga_test_setup("ga_scaling::no_scaling");
        let f = GA_TEST_FITNESS_VAL;
        let mut population = GAPopulation::new(vec![GATestSolution::new(f)], GAPopulationSortOrder::HighIsBest);
        population.sort();

        let scaler = GANoScaling{};

        scaler.evaluate(&mut population);

        assert_eq!(population.individual(0, GAPopulationSortBasis::Raw).fitness(),
                   population.individual(0, GAPopulationSortBasis::Raw).score());

        ga_test_teardown();
    }

    #[test]
    fn linear_scaling()
    {
        ga_test_setup("ga_scaling::no_scaling");
        let f = GA_TEST_FITNESS_VAL;
        let mut population = GAPopulation::new(vec![GATestSolution::new(f)], GAPopulationSortOrder::HighIsBest);
        population.sort();

        let scaler = GALinearScaling{ multiplier: super::GA_LINEAR_SCALING_MULTIPLIER };

        scaler.evaluate(&mut population);

        // TODO: Real test
        assert!(population.individual(0, GAPopulationSortBasis::Raw).fitness() !=
                population.individual(0, GAPopulationSortBasis::Raw).score());

        ga_test_teardown();
    }

}
<|endoftext|>


<commit_before>use std::{
    collections::{HashMap, HashSet},
    path::{Path, PathBuf},
};

use log::trace;
use petgraph::graph::{EdgeIndex, NodeIndex};
use ra_ap_hir::{self as hir, Crate, ModuleSource};
use ra_ap_ide_db::RootDatabase;
use ra_ap_vfs::Vfs;

use crate::graph::{
    edge::{Edge, EdgeKind},
    node::{
        attr::{NodeAttrs, NodeCfgAttr, NodeTestAttr},
        visibility::NodeVisibility,
        Node, NodeKind,
    },
    orphans::add_orphan_nodes_to,
    util, Graph,
};

#[derive(Clone, PartialEq, Debug)]
pub struct Options {
    pub focus_on: Option<String>,
    pub max_depth: Option<usize>,
    pub with_types: bool,
    pub with_tests: bool,
    pub with_orphans: bool,
    pub with_uses: bool,
    pub with_externs: bool,
}

#[derive(Debug)]
pub struct Builder<'a> {
    options: Options,
    db: &'a RootDatabase,
    vfs: &'a Vfs,
    krate: hir::Crate,
    graph: Graph,
    nodes: HashMap<Vec<String>, NodeIndex>,
    edges: HashMap<(NodeIndex, EdgeKind, NodeIndex), EdgeIndex>,
}

impl<'a> Builder<'a> {
    pub fn new(options: Options, db: &'a RootDatabase, vfs: &'a Vfs, krate: hir::Crate) -> Self {
        let graph = Graph::default();
        let nodes = HashMap::default();
        let edges = HashMap::default();

        Self {
            options,
            db,
            vfs,
            krate,
            graph,
            nodes,
            edges,
        }
    }

    pub fn build(mut self, krate: Crate) -> anyhow::Result<Graph> {
        trace!("Scanning project ...");

        self.process_crate(krate)?;

        Ok(self.graph)
    }

    fn process_crate(&mut self, krate: Crate) -> anyhow::Result<()> {
        let root_module = krate.root_module(self.db);
        let root_module_def = hir::ModuleDef::Module(root_module);

        self.process_owned_module_def(None, root_module_def)?;

        Ok(())
    }

    fn process_owned_module_def(
        &mut self,
        owner: Option<(hir::Module, NodeIndex)>,
        owned_module_def: hir::ModuleDef,
    ) -> anyhow::Result<Option<NodeIndex>> {
        if !self.options.with_types {
            // Check if target is a type (i.e. not a module).
            let is_module = matches!(owned_module_def, hir::ModuleDef::Module(_));

            // If it is a type we bail out:
            if !is_module {
                return Ok(None);
            }
        }

        let (_owner_module, owner_idx) = match owner {
            Some((module, idx)) => (Some(module), Some(idx)),
            None => (None, None),
        };

        if !self.options.with_tests {
            match owned_module_def {
                hir::ModuleDef::Module(owned_module) => {
                    if util::is_test_module(owned_module, self.db) {
                        return Ok(None);
                    }
                }
                hir::ModuleDef::Function(owned_function) => {
                    if util::is_test_function(owned_function, self.db) {
                        return Ok(None);
                    }
                }
                _ => {}
            }
        }

        let owned_idx = match self.add_node(owned_module_def) {
            Some(owned_idx) => owned_idx,
            None => return Ok(None),
        };

        if let Some(owner_idx) = owner_idx {
            let edge = Edge {
                kind: EdgeKind::Owns,
            };
            self.add_edge(owner_idx, owned_idx, edge);
        }

        if let hir::ModuleDef::Module(owned_module) = owned_module_def {
            trace!(
                "Scanning module {:?}",
                util::path(owned_module_def, self.db)
            );

            let mut local_definitions: HashSet<hir::ModuleDef> = HashSet::new();

            for module_sub_def in owned_module.declarations(self.db) {
                local_definitions.insert(module_sub_def);

                self.process_owned_module_def(Some((owned_module, owned_idx)), module_sub_def)?;
            }

            if self.options.with_orphans && local_definitions.is_empty() {
                add_orphan_nodes_to(&mut self.graph, owned_idx);
            }

            for (_name, scope_def) in owned_module.scope(self.db, None) {
                if let hir::ScopeDef::ModuleDef(scope_module_def) = scope_def {
                    // Skip local child declarations:
                    if local_definitions.contains(&scope_module_def) {
                        continue;
                    }

                    self.process_used_module_def((owned_module, owned_idx), scope_module_def)?;
                }
            }
        }

        if let Some(owner_idx) = owner_idx {
            if self.options.with_orphans {
                add_orphan_nodes_to(&mut self.graph, owner_idx);
            }
        }

        Ok(Some(owned_idx))
    }

    fn process_used_module_def(
        &mut self,
        user: (hir::Module, NodeIndex),
        used_module_def: hir::ModuleDef,
    ) -> anyhow::Result<Option<NodeIndex>> {
        if !self.options.with_uses {
            return Ok(None);
        }

        let (_user_module, user_idx) = user;

        let mut resolved_module_def = Some(used_module_def);

        if !self.options.with_types {
            // Check if target is a type (i.e. not a module).
            let is_module = matches!(used_module_def, hir::ModuleDef::Module(_));

            // If it is a type we need to resolve to its parent module instead:
            if !is_module {
                let parent_module = used_module_def.module(self.db);
                resolved_module_def = parent_module.map(hir::ModuleDef::Module);
            }
        }

        let module_def_krate = util::krate(used_module_def, self.db);

        // Check if target is from an extern crate.
        // If it is we need to resolve to its parent module instead:
        if module_def_krate != Some(self.krate) {
            resolved_module_def = if self.options.with_externs {
                module_def_krate.map(|krate| hir::ModuleDef::Module(krate.root_module(self.db)))
            } else {
                None
            };
        }

        // Depending on the options we might not want to add the target as is,
        // but resolve it to its module or crate, e.g., so we do that here:
        let resolved_module_def = match resolved_module_def {
            Some(resolved_module_def) => resolved_module_def,
            None => return Ok(None),
        };

        Ok(self.add_node(resolved_module_def).map(|used_idx| {
            let edge = Edge {
                kind: EdgeKind::Uses,
            };
            self.add_edge(user_idx, used_idx, edge);
            used_idx
        }))
    }

    fn add_node(&mut self, module_def: hir::ModuleDef) -> Option<NodeIndex> {
        let node_id = util::path(module_def, self.db);

        trace!("Adding module node: {:?}", node_id);

        // Check if we already added an equivalent node:
        match self.nodes.get(&node_id) {
            Some(node_idx) => {
                // If we did indeed already process it, then retrieve its index:
                Some(*node_idx)
            }
            None => {
                // Otherwise try to add a node:
                let node = match self.node_weight(module_def) {
                    Some(node) => node,
                    None => return None,
                };

                let node_idx = self.graph.add_node(node);
                self.nodes.insert(node_id, node_idx);

                Some(node_idx)
            }
        }
    }

    fn add_edge(&mut self, source_idx: NodeIndex, target_idx: NodeIndex, edge: Edge) -> EdgeIndex {
        let edge_id = (source_idx, edge.kind, target_idx);

        trace!(
            "Adding edge: {:?} --({:?})-> {:?}",
            edge_id.0,
            edge_id.1,
            edge_id.2
        );

        // Check if we already added an equivalent edge:
        match self.edges.get(&edge_id) {
            Some(edge_idx) => {
                // If we did indeed already process it, then retrieve its index:
                *edge_idx
            }
            None => {
                // Otherwise add an edge:
                let edge_idx = self.graph.add_edge(source_idx, target_idx, edge);
                self.edges.insert(edge_id, edge_idx);

                edge_idx
            }
        }
    }

    fn node_weight(&self, module_def: hir::ModuleDef) -> Option<Node> {
        let krate = {
            let krate = util::krate(module_def, self.db);
            krate.map(|krate| util::krate_name(krate, self.db))
        };

        let path = util::path(module_def, self.db);
        let file_path = {
            match module_def {
                hir::ModuleDef::Module(module) => Some(module),
                _ => None,
            }
            .and_then(|module| {
                self.module_file(module.definition_source(self.db))
                    .map(Into::into)
            })
        };

        let kind = match NodeKind::from(module_def, self.db) {
            Some(kind) => kind,
            None => return None,
        };

        let visibility = Some(NodeVisibility::new(module_def, self.db));

        let attrs = {
            let cfgs: Vec<_> = self.cfg_attrs(module_def);
            let test = self.test_attr(module_def);
            NodeAttrs { cfgs, test }
        };

        Some(Node {
            krate,
            path,
            file_path,
            kind,
            visibility,
            attrs,
        })
    }

    fn cfg_attrs(&self, module_def: hir::ModuleDef) -> Vec<NodeCfgAttr> {
        util::cfgs(module_def, self.db)
            .into_iter()
            .filter_map(NodeCfgAttr::new)
            .collect()
    }

    fn test_attr(&self, module_def: hir::ModuleDef) -> Option<NodeTestAttr> {
        let function = match module_def {
            hir::ModuleDef::Function(function) => function,
            _ => return None,
        };

        if util::is_test_function(function, self.db) {
            Some(NodeTestAttr)
        } else {
            None
        }
    }

    fn module_file(&self, module_source: hir::InFile<hir::ModuleSource>) -> Option<PathBuf> {
        let is_file_module: bool = match &module_source.value {
            ModuleSource::SourceFile(_) => true,
            ModuleSource::Module(_) => false,
            ModuleSource::BlockExpr(_) => false,
        };

        if !is_file_module {
            return None;
        }

        let file_id = module_source.file_id.original_file(self.db);
        let vfs_path = self.vfs.file_path(file_id);
        let abs_path = vfs_path.as_path().expect("Could not convert to path");

        let path: &Path = &*abs_path;

        let file_extension = path.extension().and_then(|ext| ext.to_str());

        if file_extension != Some("rs") {
            return None;
        }

        Some(path.to_owned())
    }
}
<commit_msg>Fixed clippy warning<commit_after>use std::{
    collections::{HashMap, HashSet},
    path::{Path, PathBuf},
};

use log::trace;
use petgraph::graph::{EdgeIndex, NodeIndex};
use ra_ap_hir::{self as hir, Crate, ModuleSource};
use ra_ap_ide_db::RootDatabase;
use ra_ap_vfs::Vfs;

use crate::graph::{
    edge::{Edge, EdgeKind},
    node::{
        attr::{NodeAttrs, NodeCfgAttr, NodeTestAttr},
        visibility::NodeVisibility,
        Node, NodeKind,
    },
    orphans::add_orphan_nodes_to,
    util, Graph,
};

#[derive(Clone, PartialEq, Debug)]
pub struct Options {
    pub focus_on: Option<String>,
    pub max_depth: Option<usize>,
    pub with_types: bool,
    pub with_tests: bool,
    pub with_orphans: bool,
    pub with_uses: bool,
    pub with_externs: bool,
}

#[derive(Debug)]
pub struct Builder<'a> {
    options: Options,
    db: &'a RootDatabase,
    vfs: &'a Vfs,
    krate: hir::Crate,
    graph: Graph,
    nodes: HashMap<Vec<String>, NodeIndex>,
    edges: HashMap<(NodeIndex, EdgeKind, NodeIndex), EdgeIndex>,
}

impl<'a> Builder<'a> {
    pub fn new(options: Options, db: &'a RootDatabase, vfs: &'a Vfs, krate: hir::Crate) -> Self {
        let graph = Graph::default();
        let nodes = HashMap::default();
        let edges = HashMap::default();

        Self {
            options,
            db,
            vfs,
            krate,
            graph,
            nodes,
            edges,
        }
    }

    pub fn build(mut self, krate: Crate) -> anyhow::Result<Graph> {
        trace!("Scanning project ...");

        self.process_crate(krate)?;

        Ok(self.graph)
    }

    fn process_crate(&mut self, krate: Crate) -> anyhow::Result<()> {
        let root_module = krate.root_module(self.db);
        let root_module_def = hir::ModuleDef::Module(root_module);

        self.process_owned_module_def(None, root_module_def)?;

        Ok(())
    }

    fn process_owned_module_def(
        &mut self,
        owner: Option<(hir::Module, NodeIndex)>,
        owned_module_def: hir::ModuleDef,
    ) -> anyhow::Result<Option<NodeIndex>> {
        if !self.options.with_types {
            // Check if target is a type (i.e. not a module).
            let is_module = matches!(owned_module_def, hir::ModuleDef::Module(_));

            // If it is a type we bail out:
            if !is_module {
                return Ok(None);
            }
        }

        let (_owner_module, owner_idx) = match owner {
            Some((module, idx)) => (Some(module), Some(idx)),
            None => (None, None),
        };

        if !self.options.with_tests {
            match owned_module_def {
                hir::ModuleDef::Module(owned_module) => {
                    if util::is_test_module(owned_module, self.db) {
                        return Ok(None);
                    }
                }
                hir::ModuleDef::Function(owned_function) => {
                    if util::is_test_function(owned_function, self.db) {
                        return Ok(None);
                    }
                }
                _ => {}
            }
        }

        let owned_idx = match self.add_node(owned_module_def) {
            Some(owned_idx) => owned_idx,
            None => return Ok(None),
        };

        if let Some(owner_idx) = owner_idx {
            let edge = Edge {
                kind: EdgeKind::Owns,
            };
            self.add_edge(owner_idx, owned_idx, edge);
        }

        if let hir::ModuleDef::Module(owned_module) = owned_module_def {
            trace!(
                "Scanning module {:?}",
                util::path(owned_module_def, self.db)
            );

            let mut local_definitions: HashSet<hir::ModuleDef> = HashSet::new();

            for module_sub_def in owned_module.declarations(self.db) {
                local_definitions.insert(module_sub_def);

                self.process_owned_module_def(Some((owned_module, owned_idx)), module_sub_def)?;
            }

            if self.options.with_orphans && local_definitions.is_empty() {
                add_orphan_nodes_to(&mut self.graph, owned_idx);
            }

            for (_name, scope_def) in owned_module.scope(self.db, None) {
                if let hir::ScopeDef::ModuleDef(scope_module_def) = scope_def {
                    // Skip local child declarations:
                    if local_definitions.contains(&scope_module_def) {
                        continue;
                    }

                    self.process_used_module_def((owned_module, owned_idx), scope_module_def);
                }
            }
        }

        if let Some(owner_idx) = owner_idx {
            if self.options.with_orphans {
                add_orphan_nodes_to(&mut self.graph, owner_idx);
            }
        }

        Ok(Some(owned_idx))
    }

    fn process_used_module_def(
        &mut self,
        user: (hir::Module, NodeIndex),
        used_module_def: hir::ModuleDef,
    ) -> Option<NodeIndex> {
        if !self.options.with_uses {
            return None;
        }

        let (_user_module, user_idx) = user;

        let mut resolved_module_def = Some(used_module_def);

        if !self.options.with_types {
            // Check if target is a type (i.e. not a module).
            let is_module = matches!(used_module_def, hir::ModuleDef::Module(_));

            // If it is a type we need to resolve to its parent module instead:
            if !is_module {
                let parent_module = used_module_def.module(self.db);
                resolved_module_def = parent_module.map(hir::ModuleDef::Module);
            }
        }

        let module_def_krate = util::krate(used_module_def, self.db);

        // Check if target is from an extern crate.
        // If it is we need to resolve to its parent module instead:
        if module_def_krate != Some(self.krate) {
            resolved_module_def = if self.options.with_externs {
                module_def_krate.map(|krate| hir::ModuleDef::Module(krate.root_module(self.db)))
            } else {
                None
            };
        }

        // Depending on the options we might not want to add the target as is,
        // but resolve it to its module or crate, e.g., so we do that here:
        let resolved_module_def = match resolved_module_def {
            Some(resolved_module_def) => resolved_module_def,
            None => return None,
        };

        self.add_node(resolved_module_def).map(|used_idx| {
            let edge = Edge {
                kind: EdgeKind::Uses,
            };
            self.add_edge(user_idx, used_idx, edge);
            used_idx
        })
    }

    fn add_node(&mut self, module_def: hir::ModuleDef) -> Option<NodeIndex> {
        let node_id = util::path(module_def, self.db);

        trace!("Adding module node: {:?}", node_id);

        // Check if we already added an equivalent node:
        match self.nodes.get(&node_id) {
            Some(node_idx) => {
                // If we did indeed already process it, then retrieve its index:
                Some(*node_idx)
            }
            None => {
                // Otherwise try to add a node:
                let node = match self.node_weight(module_def) {
                    Some(node) => node,
                    None => return None,
                };

                let node_idx = self.graph.add_node(node);
                self.nodes.insert(node_id, node_idx);

                Some(node_idx)
            }
        }
    }

    fn add_edge(&mut self, source_idx: NodeIndex, target_idx: NodeIndex, edge: Edge) -> EdgeIndex {
        let edge_id = (source_idx, edge.kind, target_idx);

        trace!(
            "Adding edge: {:?} --({:?})-> {:?}",
            edge_id.0,
            edge_id.1,
            edge_id.2
        );

        // Check if we already added an equivalent edge:
        match self.edges.get(&edge_id) {
            Some(edge_idx) => {
                // If we did indeed already process it, then retrieve its index:
                *edge_idx
            }
            None => {
                // Otherwise add an edge:
                let edge_idx = self.graph.add_edge(source_idx, target_idx, edge);
                self.edges.insert(edge_id, edge_idx);

                edge_idx
            }
        }
    }

    fn node_weight(&self, module_def: hir::ModuleDef) -> Option<Node> {
        let krate = {
            let krate = util::krate(module_def, self.db);
            krate.map(|krate| util::krate_name(krate, self.db))
        };

        let path = util::path(module_def, self.db);
        let file_path = {
            match module_def {
                hir::ModuleDef::Module(module) => Some(module),
                _ => None,
            }
            .and_then(|module| {
                self.module_file(module.definition_source(self.db))
                    .map(Into::into)
            })
        };

        let kind = match NodeKind::from(module_def, self.db) {
            Some(kind) => kind,
            None => return None,
        };

        let visibility = Some(NodeVisibility::new(module_def, self.db));

        let attrs = {
            let cfgs: Vec<_> = self.cfg_attrs(module_def);
            let test = self.test_attr(module_def);
            NodeAttrs { cfgs, test }
        };

        Some(Node {
            krate,
            path,
            file_path,
            kind,
            visibility,
            attrs,
        })
    }

    fn cfg_attrs(&self, module_def: hir::ModuleDef) -> Vec<NodeCfgAttr> {
        util::cfgs(module_def, self.db)
            .into_iter()
            .filter_map(NodeCfgAttr::new)
            .collect()
    }

    fn test_attr(&self, module_def: hir::ModuleDef) -> Option<NodeTestAttr> {
        let function = match module_def {
            hir::ModuleDef::Function(function) => function,
            _ => return None,
        };

        if util::is_test_function(function, self.db) {
            Some(NodeTestAttr)
        } else {
            None
        }
    }

    fn module_file(&self, module_source: hir::InFile<hir::ModuleSource>) -> Option<PathBuf> {
        let is_file_module: bool = match &module_source.value {
            ModuleSource::SourceFile(_) => true,
            ModuleSource::Module(_) => false,
            ModuleSource::BlockExpr(_) => false,
        };

        if !is_file_module {
            return None;
        }

        let file_id = module_source.file_id.original_file(self.db);
        let vfs_path = self.vfs.file_path(file_id);
        let abs_path = vfs_path.as_path().expect("Could not convert to path");

        let path: &Path = &*abs_path;

        let file_extension = path.extension().and_then(|ext| ext.to_str());

        if file_extension != Some("rs") {
            return None;
        }

        Some(path.to_owned())
    }
}
<|endoftext|>


<commit_before>use criterion::{criterion_group, criterion_main, Criterion};
use once_cell::sync::Lazy;
use rusttype::gpu_cache::*;
use rusttype::*;

/// Busy wait 2us
fn mock_gpu_upload(_region: Rect<u32>, _bytes: &[u8]) {
    use std::time::{Duration, Instant};

    let now = Instant::now();
    while now.elapsed() < Duration::from_micros(2) {}
}

fn test_glyphs<'a>(font: &Font<'a>, string: &str) -> Vec<PositionedGlyph<'a>> {
    let mut glyphs = vec![];
    // Set of scales, found through brute force, to reproduce GlyphNotCached issue
    // Cache settings also affect this, it occurs when position_tolerance is < 1.0
    for scale in &[25_f32, 24.5, 25.01, 24.7, 24.99] {
        for glyph in layout_paragraph(font, Scale::uniform(*scale), 500, string) {
            glyphs.push(glyph);
        }
    }
    glyphs
}

fn layout_paragraph<'a>(
    font: &Font<'a>,
    scale: Scale,
    width: u32,
    text: &str,
) -> Vec<PositionedGlyph<'a>> {
    let mut result = Vec::new();
    let v_metrics = font.v_metrics(scale);
    let advance_height = v_metrics.ascent - v_metrics.descent + v_metrics.line_gap;
    let mut caret = point(0.0, v_metrics.ascent);
    let mut last_glyph_id = None;
    for c in text.chars() {
        if c.is_control() {
            if c == '\n' {
                caret = point(0.0, caret.y + advance_height)
            }
            continue;
        }
        let base_glyph = font.glyph(c);
        if let Some(id) = last_glyph_id.take() {
            caret.x += font.pair_kerning(scale, id, base_glyph.id());
        }
        last_glyph_id = Some(base_glyph.id());
        let mut glyph = base_glyph.scaled(scale).positioned(caret);
        if let Some(bb) = glyph.pixel_bounding_box() {
            if bb.max.x > width as i32 {
                caret = point(0.0, caret.y + advance_height);
                glyph.set_position(caret);
                last_glyph_id = None;
            }
        }
        caret.x += glyph.unpositioned().h_metrics().advance_width;
        result.push(glyph);
    }
    result
}

static FONTS: Lazy<Vec<Font<'static>>> = Lazy::new(|| {
    vec![
        include_bytes!("../fonts/wqy-microhei/WenQuanYiMicroHei.ttf") as &[u8],
        include_bytes!("../fonts/dejavu/DejaVuSansMono.ttf") as &[u8],
        include_bytes!("../fonts/opensans/OpenSans-Italic.ttf") as &[u8],
    ]
    .into_iter()
    .map(|bytes| Font::try_from_bytes(bytes).unwrap())
    .collect()
});

const TEST_STR: &str = include_str!("../tests/lipsum.txt");

// **************************************************************************
// General use benchmarks.
// **************************************************************************

/// Benchmark using a single font at "don't care" position tolerance
fn bench_high_position_tolerance(c: &mut Criterion) {
    let font_id = 0;
    let glyphs = test_glyphs(&FONTS[font_id], TEST_STR);
    let mut cache = Cache::builder()
        .dimensions(1024, 1024)
        .scale_tolerance(0.1)
        .position_tolerance(1.0)
        .build();

    c.bench_function("high_position_tolerance", |b| {
        b.iter(|| {
            for glyph in &glyphs {
                cache.queue_glyph(font_id, glyph.clone());
            }

            cache.cache_queued(|_, _| {}).expect("cache_queued");

            for (index, glyph) in glyphs.iter().enumerate() {
                let rect = cache.rect_for(font_id, glyph);
                assert!(
                    rect.is_ok(),
                    "Gpu cache rect lookup failed ({:?}) for glyph index {}, id {}",
                    rect,
                    index,
                    glyph.id().0
                );
            }
        })
    });
}

/// Benchmark using a single font with default tolerances
fn bench_single_font(c: &mut Criterion) {
    let font_id = 0;
    let glyphs = test_glyphs(&FONTS[font_id], TEST_STR);
    let mut cache = Cache::builder().dimensions(1024, 1024).build();

    c.bench_function("single_font", |b| {
        b.iter(|| {
            for glyph in &glyphs {
                cache.queue_glyph(font_id, glyph.clone());
            }

            cache.cache_queued(|_, _| {}).expect("cache_queued");

            for (index, glyph) in glyphs.iter().enumerate() {
                let rect = cache.rect_for(font_id, glyph);
                assert!(
                    rect.is_ok(),
                    "Gpu cache rect lookup failed ({:?}) for glyph index {}, id {}",
                    rect,
                    index,
                    glyph.id().0
                );
            }
        })
    });
}

/// Benchmark using multiple fonts with default tolerances
fn bench_multi_font(c: &mut Criterion) {
    // Use a smaller amount of the test string, to offset the extra font-glyph
    // bench load
    let up_to_index = TEST_STR
        .char_indices()
        .nth(TEST_STR.chars().count() / FONTS.len())
        .unwrap()
        .0;
    let string = &TEST_STR[..up_to_index];

    let font_glyphs: Vec<_> = FONTS
        .iter()
        .enumerate()
        .map(|(id, font)| (id, test_glyphs(font, string)))
        .collect();
    let mut cache = Cache::builder().dimensions(1024, 1024).build();

    c.bench_function("mutli_font", |b| {
        b.iter(|| {
            for &(font_id, ref glyphs) in &font_glyphs {
                for glyph in glyphs {
                    cache.queue_glyph(font_id, glyph.clone());
                }
            }

            cache.cache_queued(|_, _| {}).expect("cache_queued");

            for &(font_id, ref glyphs) in &font_glyphs {
                for (index, glyph) in glyphs.iter().enumerate() {
                    let rect = cache.rect_for(font_id, glyph);
                    assert!(
                        rect.is_ok(),
                        "Gpu cache rect lookup failed ({:?}) for font {} glyph index {}, id {}",
                        rect,
                        font_id,
                        index,
                        glyph.id().0
                    );
                }
            }
        })
    });
}

/// Benchmark using multiple fonts with default tolerances, clears the
/// cache each run to test the population "first run" performance
fn bench_multi_font_population(c: &mut Criterion) {
    // Use a much smaller amount of the test string, to offset the extra font-glyph
    // bench load & much slower performance of fresh population each run
    let up_to_index = TEST_STR.char_indices().nth(70).unwrap().0;
    let string = &TEST_STR[..up_to_index];

    let font_glyphs: Vec<_> = FONTS
        .iter()
        .enumerate()
        .map(|(id, font)| (id, test_glyphs(font, string)))
        .collect();

    c.bench_function("multi_font_population", |b| {
        b.iter(|| {
            let mut cache = Cache::builder().dimensions(1024, 1024).build();

            for &(font_id, ref glyphs) in &font_glyphs {
                for glyph in glyphs {
                    cache.queue_glyph(font_id, glyph.clone());
                }
            }

            cache.cache_queued(|_, _| {}).expect("cache_queued");

            for &(font_id, ref glyphs) in &font_glyphs {
                for (index, glyph) in glyphs.iter().enumerate() {
                    let rect = cache.rect_for(font_id, glyph);
                    assert!(
                        rect.is_ok(),
                        "Gpu cache rect lookup failed ({:?}) for font {} glyph index {}, id {}",
                        rect,
                        font_id,
                        index,
                        glyph.id().0
                    );
                }
            }
        })
    });
}

/// Benchmark using multiple fonts and a different text group of glyphs
/// each run
fn bench_moving_text(c: &mut Criterion) {
    let chars: Vec<_> = TEST_STR.chars().collect();
    let subsection_len = chars.len() / FONTS.len();
    let distinct_subsection: Vec<_> = chars.windows(subsection_len).collect();

    let mut first_glyphs = vec![];
    let mut middle_glyphs = vec![];
    let mut last_glyphs = vec![];

    for (id, font) in FONTS.iter().enumerate() {
        let first_str: String = distinct_subsection[0].iter().collect();
        first_glyphs.push((id, test_glyphs(font, &first_str)));

        let middle_str: String = distinct_subsection[distinct_subsection.len() / 2]
            .iter()
            .collect();
        middle_glyphs.push((id, test_glyphs(font, &middle_str)));

        let last_str: String = distinct_subsection[distinct_subsection.len() - 1]
            .iter()
            .collect();
        last_glyphs.push((id, test_glyphs(font, &last_str)));
    }

    let test_variants = [first_glyphs, middle_glyphs, last_glyphs];
    let mut test_variants = test_variants.iter().cycle();

    let mut cache = Cache::builder()
        .dimensions(1500, 1500)
        .scale_tolerance(0.1)
        .position_tolerance(0.1)
        .build();

    c.bench_function("moving_text", |b| {
        b.iter(|| {
            // switch text variant each run to force cache to deal with moving text
            // requirements
            let glyphs = test_variants.next().unwrap();
            for &(font_id, ref glyphs) in glyphs {
                for glyph in glyphs {
                    cache.queue_glyph(font_id, glyph.clone());
                }
            }

            cache.cache_queued(|_, _| {}).expect("cache_queued");

            for &(font_id, ref glyphs) in glyphs {
                for (index, glyph) in glyphs.iter().enumerate() {
                    let rect = cache.rect_for(font_id, glyph);
                    assert!(
                        rect.is_ok(),
                        "Gpu cache rect lookup failed ({:?}) for font {} glyph index {}, id {}",
                        rect,
                        font_id,
                        index,
                        glyph.id().0
                    );
                }
            }
        })
    });
}

// **************************************************************************
// Benchmarks for cases that should generally be avoided by the cache user if
// at all possible (ie by picking a better initial cache size).
// **************************************************************************

/// Cache isn't large enough for a queue so a new cache is created to hold
/// the queue.
fn bench_resizing(c: &mut Criterion) {
    let up_to_index = TEST_STR.char_indices().nth(120).unwrap().0;
    let string = &TEST_STR[..up_to_index];

    let font_glyphs: Vec<_> = FONTS
        .iter()
        .enumerate()
        .map(|(id, font)| (id, test_glyphs(font, string)))
        .collect();

    c.bench_function("resizing", |b| {
        b.iter(|| {
            let mut cache = Cache::builder().dimensions(256, 256).build();

            for &(font_id, ref glyphs) in &font_glyphs {
                for glyph in glyphs {
                    cache.queue_glyph(font_id, glyph.clone());
                }
            }

            cache
                .cache_queued(mock_gpu_upload)
                .expect_err("shouldn't fit");

            cache.to_builder().dimensions(512, 512).rebuild(&mut cache);

            cache.cache_queued(mock_gpu_upload).expect("should fit now");

            for &(font_id, ref glyphs) in &font_glyphs {
                for (index, glyph) in glyphs.iter().enumerate() {
                    let rect = cache.rect_for(font_id, glyph);
                    assert!(
                        rect.is_ok(),
                        "Gpu cache rect lookup failed ({:?}) for font {} glyph index {}, id {}",
                        rect,
                        font_id,
                        index,
                        glyph.id().0
                    );
                }
            }
        })
    });
}

/// Benchmark using multiple fonts and a different text group of glyphs
/// each run. The cache is only large enough to fit each run if it is
/// cleared and re-built.
fn bench_moving_text_thrashing(c: &mut Criterion) {
    let chars: Vec<_> = TEST_STR.chars().collect();
    let subsection_len = 60;
    let distinct_subsection: Vec<_> = chars.windows(subsection_len).collect();

    let mut first_glyphs = vec![];
    let mut middle_glyphs = vec![];
    let mut last_glyphs = vec![];

    for (id, font) in FONTS.iter().enumerate() {
        let first_str: String = distinct_subsection[0].iter().collect();
        first_glyphs.push((id, test_glyphs(font, &first_str)));

        let middle_str: String = distinct_subsection[distinct_subsection.len() / 2]
            .iter()
            .collect();
        middle_glyphs.push((id, test_glyphs(font, &middle_str)));

        let last_str: String = distinct_subsection[distinct_subsection.len() - 1]
            .iter()
            .collect();
        last_glyphs.push((id, test_glyphs(font, &last_str)));
    }

    let test_variants = [first_glyphs, middle_glyphs, last_glyphs];

    // Cache is only a little larger than each variants size meaning a lot of
    // re-ordering, re-rasterization & re-uploading has to occur.
    let mut cache = Cache::builder()
        .dimensions(450, 450)
        .scale_tolerance(0.1)
        .position_tolerance(0.1)
        .build();

    c.bench_function("moving_text_thrashing", |b| {
        b.iter(|| {
            // switch text variant each run to force cache to deal with moving text
            // requirements
            for glyphs in &test_variants {
                for &(font_id, ref glyphs) in glyphs {
                    for glyph in glyphs {
                        cache.queue_glyph(font_id, glyph.clone());
                    }
                }

                cache.cache_queued(mock_gpu_upload).expect("cache_queued");

                for &(font_id, ref glyphs) in glyphs {
                    for (index, glyph) in glyphs.iter().enumerate() {
                        let rect = cache.rect_for(font_id, glyph);
                        assert!(
                            rect.is_ok(),
                            "Gpu cache rect lookup failed ({:?}) for font {} glyph index {}, id {}",
                            rect,
                            font_id,
                            index,
                            glyph.id().0
                        );
                    }
                }
            }
        })
    });
}

criterion_group!(
    benches,
    bench_high_position_tolerance,
    bench_single_font,
    bench_multi_font,
    bench_multi_font_population,
    bench_moving_text,
    bench_resizing,
    bench_moving_text_thrashing,
);

criterion_main!(benches);
<commit_msg>Update gpu_cache benchmarks to include an otf font.<commit_after>use criterion::{criterion_group, criterion_main, Criterion};
use once_cell::sync::Lazy;
use rusttype::gpu_cache::*;
use rusttype::*;

/// Busy wait 2us
fn mock_gpu_upload(_region: Rect<u32>, _bytes: &[u8]) {
    use std::time::{Duration, Instant};

    let now = Instant::now();
    while now.elapsed() < Duration::from_micros(2) {}
}

fn test_glyphs<'a>(font: &Font<'a>, string: &str) -> Vec<PositionedGlyph<'a>> {
    let mut glyphs = vec![];
    // Set of scales, found through brute force, to reproduce GlyphNotCached issue
    // Cache settings also affect this, it occurs when position_tolerance is < 1.0
    for scale in &[25_f32, 24.5, 25.01, 24.7, 24.99] {
        for glyph in layout_paragraph(font, Scale::uniform(*scale), 500, string) {
            glyphs.push(glyph);
        }
    }
    glyphs
}

fn layout_paragraph<'a>(
    font: &Font<'a>,
    scale: Scale,
    width: u32,
    text: &str,
) -> Vec<PositionedGlyph<'a>> {
    let mut result = Vec::new();
    let v_metrics = font.v_metrics(scale);
    let advance_height = v_metrics.ascent - v_metrics.descent + v_metrics.line_gap;
    let mut caret = point(0.0, v_metrics.ascent);
    let mut last_glyph_id = None;
    for c in text.chars() {
        if c.is_control() {
            if c == '\n' {
                caret = point(0.0, caret.y + advance_height)
            }
            continue;
        }
        let base_glyph = font.glyph(c);
        if let Some(id) = last_glyph_id.take() {
            caret.x += font.pair_kerning(scale, id, base_glyph.id());
        }
        last_glyph_id = Some(base_glyph.id());
        let mut glyph = base_glyph.scaled(scale).positioned(caret);
        if let Some(bb) = glyph.pixel_bounding_box() {
            if bb.max.x > width as i32 {
                caret = point(0.0, caret.y + advance_height);
                glyph.set_position(caret);
                last_glyph_id = None;
            }
        }
        caret.x += glyph.unpositioned().h_metrics().advance_width;
        result.push(glyph);
    }
    result
}

static FONTS: Lazy<Vec<Font<'static>>> = Lazy::new(|| {
    vec![
        include_bytes!("../fonts/wqy-microhei/WenQuanYiMicroHei.ttf") as &[u8],
        include_bytes!("../fonts/opensans/OpenSans-Italic.ttf") as &[u8],
        include_bytes!("../fonts/Exo2-Light.otf") as &[u8],
    ]
    .into_iter()
    .map(|bytes| Font::try_from_bytes(bytes).unwrap())
    .collect()
});

const TEST_STR: &str = include_str!("../tests/lipsum.txt");

// **************************************************************************
// General use benchmarks.
// **************************************************************************

/// Benchmark using a single font at "don't care" position tolerance
fn bench_high_position_tolerance(c: &mut Criterion) {
    let font_id = 0;
    let glyphs = test_glyphs(&FONTS[font_id], TEST_STR);
    let mut cache = Cache::builder()
        .dimensions(1024, 1024)
        .scale_tolerance(0.1)
        .position_tolerance(1.0)
        .build();

    c.bench_function("high_position_tolerance", |b| {
        b.iter(|| {
            for glyph in &glyphs {
                cache.queue_glyph(font_id, glyph.clone());
            }

            cache.cache_queued(|_, _| {}).expect("cache_queued");

            for (index, glyph) in glyphs.iter().enumerate() {
                let rect = cache.rect_for(font_id, glyph);
                assert!(
                    rect.is_ok(),
                    "Gpu cache rect lookup failed ({:?}) for glyph index {}, id {}",
                    rect,
                    index,
                    glyph.id().0
                );
            }
        })
    });
}

/// Benchmark using a single font with default tolerances
fn bench_single_ttf(c: &mut Criterion) {
    let font_id = 0;
    let glyphs = test_glyphs(&FONTS[font_id], TEST_STR);
    let mut cache = Cache::builder().dimensions(1024, 1024).build();

    c.bench_function("single_ttf", |b| {
        b.iter(|| {
            for glyph in &glyphs {
                cache.queue_glyph(font_id, glyph.clone());
            }

            cache.cache_queued(|_, _| {}).expect("cache_queued");

            for (index, glyph) in glyphs.iter().enumerate() {
                let rect = cache.rect_for(font_id, glyph);
                assert!(
                    rect.is_ok(),
                    "Gpu cache rect lookup failed ({:?}) for glyph index {}, id {}",
                    rect,
                    index,
                    glyph.id().0
                );
            }
        })
    });
}

/// Benchmark using a single font with default tolerances
fn bench_single_otf(c: &mut Criterion) {
    let font_id = 2;
    let glyphs = test_glyphs(&FONTS[font_id], TEST_STR);
    let mut cache = Cache::builder().dimensions(1024, 1024).build();

    c.bench_function("single_otf", |b| {
        b.iter(|| {
            for glyph in &glyphs {
                cache.queue_glyph(font_id, glyph.clone());
            }

            cache.cache_queued(|_, _| {}).expect("cache_queued");

            for (index, glyph) in glyphs.iter().enumerate() {
                let rect = cache.rect_for(font_id, glyph);
                assert!(
                    rect.is_ok(),
                    "Gpu cache rect lookup failed ({:?}) for glyph index {}, id {}",
                    rect,
                    index,
                    glyph.id().0
                );
            }
        })
    });
}

/// Benchmark using multiple fonts with default tolerances
fn bench_multi_font(c: &mut Criterion) {
    // Use a smaller amount of the test string, to offset the extra font-glyph
    // bench load
    let up_to_index = TEST_STR
        .char_indices()
        .nth(TEST_STR.chars().count() / FONTS.len())
        .unwrap()
        .0;
    let string = &TEST_STR[..up_to_index];

    let font_glyphs: Vec<_> = FONTS
        .iter()
        .enumerate()
        .map(|(id, font)| (id, test_glyphs(font, string)))
        .collect();
    let mut cache = Cache::builder().dimensions(1024, 1024).build();

    c.bench_function("mutli_font", |b| {
        b.iter(|| {
            for &(font_id, ref glyphs) in &font_glyphs {
                for glyph in glyphs {
                    cache.queue_glyph(font_id, glyph.clone());
                }
            }

            cache.cache_queued(|_, _| {}).expect("cache_queued");

            for &(font_id, ref glyphs) in &font_glyphs {
                for (index, glyph) in glyphs.iter().enumerate() {
                    let rect = cache.rect_for(font_id, glyph);
                    assert!(
                        rect.is_ok(),
                        "Gpu cache rect lookup failed ({:?}) for font {} glyph index {}, id {}",
                        rect,
                        font_id,
                        index,
                        glyph.id().0
                    );
                }
            }
        })
    });
}

/// Benchmark using multiple fonts with default tolerances, clears the
/// cache each run to test the population "first run" performance
fn bench_multi_font_population(c: &mut Criterion) {
    // Use a much smaller amount of the test string, to offset the extra font-glyph
    // bench load & much slower performance of fresh population each run
    let up_to_index = TEST_STR.char_indices().nth(70).unwrap().0;
    let string = &TEST_STR[..up_to_index];

    let font_glyphs: Vec<_> = FONTS
        .iter()
        .enumerate()
        .map(|(id, font)| (id, test_glyphs(font, string)))
        .collect();

    c.bench_function("multi_font_population", |b| {
        b.iter(|| {
            let mut cache = Cache::builder().dimensions(1024, 1024).build();

            for &(font_id, ref glyphs) in &font_glyphs {
                for glyph in glyphs {
                    cache.queue_glyph(font_id, glyph.clone());
                }
            }

            cache.cache_queued(|_, _| {}).expect("cache_queued");

            for &(font_id, ref glyphs) in &font_glyphs {
                for (index, glyph) in glyphs.iter().enumerate() {
                    let rect = cache.rect_for(font_id, glyph);
                    assert!(
                        rect.is_ok(),
                        "Gpu cache rect lookup failed ({:?}) for font {} glyph index {}, id {}",
                        rect,
                        font_id,
                        index,
                        glyph.id().0
                    );
                }
            }
        })
    });
}

/// Benchmark using multiple fonts and a different text group of glyphs
/// each run
fn bench_moving_text(c: &mut Criterion) {
    let chars: Vec<_> = TEST_STR.chars().collect();
    let subsection_len = chars.len() / FONTS.len();
    let distinct_subsection: Vec<_> = chars.windows(subsection_len).collect();

    let mut first_glyphs = vec![];
    let mut middle_glyphs = vec![];
    let mut last_glyphs = vec![];

    for (id, font) in FONTS.iter().enumerate() {
        let first_str: String = distinct_subsection[0].iter().collect();
        first_glyphs.push((id, test_glyphs(font, &first_str)));

        let middle_str: String = distinct_subsection[distinct_subsection.len() / 2]
            .iter()
            .collect();
        middle_glyphs.push((id, test_glyphs(font, &middle_str)));

        let last_str: String = distinct_subsection[distinct_subsection.len() - 1]
            .iter()
            .collect();
        last_glyphs.push((id, test_glyphs(font, &last_str)));
    }

    let test_variants = [first_glyphs, middle_glyphs, last_glyphs];
    let mut test_variants = test_variants.iter().cycle();

    let mut cache = Cache::builder()
        .dimensions(1500, 1500)
        .scale_tolerance(0.1)
        .position_tolerance(0.1)
        .build();

    c.bench_function("moving_text", |b| {
        b.iter(|| {
            // switch text variant each run to force cache to deal with moving text
            // requirements
            let glyphs = test_variants.next().unwrap();
            for &(font_id, ref glyphs) in glyphs {
                for glyph in glyphs {
                    cache.queue_glyph(font_id, glyph.clone());
                }
            }

            cache.cache_queued(|_, _| {}).expect("cache_queued");

            for &(font_id, ref glyphs) in glyphs {
                for (index, glyph) in glyphs.iter().enumerate() {
                    let rect = cache.rect_for(font_id, glyph);
                    assert!(
                        rect.is_ok(),
                        "Gpu cache rect lookup failed ({:?}) for font {} glyph index {}, id {}",
                        rect,
                        font_id,
                        index,
                        glyph.id().0
                    );
                }
            }
        })
    });
}

// **************************************************************************
// Benchmarks for cases that should generally be avoided by the cache user if
// at all possible (ie by picking a better initial cache size).
// **************************************************************************

/// Cache isn't large enough for a queue so a new cache is created to hold
/// the queue.
fn bench_resizing(c: &mut Criterion) {
    let up_to_index = TEST_STR.char_indices().nth(120).unwrap().0;
    let string = &TEST_STR[..up_to_index];

    let font_glyphs: Vec<_> = FONTS
        .iter()
        .enumerate()
        .map(|(id, font)| (id, test_glyphs(font, string)))
        .collect();

    c.bench_function("resizing", |b| {
        b.iter(|| {
            let mut cache = Cache::builder().dimensions(256, 256).build();

            for &(font_id, ref glyphs) in &font_glyphs {
                for glyph in glyphs {
                    cache.queue_glyph(font_id, glyph.clone());
                }
            }

            cache
                .cache_queued(mock_gpu_upload)
                .expect_err("shouldn't fit");

            cache.to_builder().dimensions(512, 512).rebuild(&mut cache);

            cache.cache_queued(mock_gpu_upload).expect("should fit now");

            for &(font_id, ref glyphs) in &font_glyphs {
                for (index, glyph) in glyphs.iter().enumerate() {
                    let rect = cache.rect_for(font_id, glyph);
                    assert!(
                        rect.is_ok(),
                        "Gpu cache rect lookup failed ({:?}) for font {} glyph index {}, id {}",
                        rect,
                        font_id,
                        index,
                        glyph.id().0
                    );
                }
            }
        })
    });
}

/// Benchmark using multiple fonts and a different text group of glyphs
/// each run. The cache is only large enough to fit each run if it is
/// cleared and re-built.
fn bench_moving_text_thrashing(c: &mut Criterion) {
    let chars: Vec<_> = TEST_STR.chars().collect();
    let subsection_len = 60;
    let distinct_subsection: Vec<_> = chars.windows(subsection_len).collect();

    let mut first_glyphs = vec![];
    let mut middle_glyphs = vec![];
    let mut last_glyphs = vec![];

    for (id, font) in FONTS.iter().enumerate() {
        let first_str: String = distinct_subsection[0].iter().collect();
        first_glyphs.push((id, test_glyphs(font, &first_str)));

        let middle_str: String = distinct_subsection[distinct_subsection.len() / 2]
            .iter()
            .collect();
        middle_glyphs.push((id, test_glyphs(font, &middle_str)));

        let last_str: String = distinct_subsection[distinct_subsection.len() - 1]
            .iter()
            .collect();
        last_glyphs.push((id, test_glyphs(font, &last_str)));
    }

    let test_variants = [first_glyphs, middle_glyphs, last_glyphs];

    // Cache is only a little larger than each variants size meaning a lot of
    // re-ordering, re-rasterization & re-uploading has to occur.
    let mut cache = Cache::builder()
        .dimensions(320, 320)
        .scale_tolerance(0.1)
        .position_tolerance(0.1)
        .build();

    c.bench_function("moving_text_thrashing", |b| {
        b.iter(|| {
            // switch text variant each run to force cache to deal with moving text
            // requirements
            for glyphs in &test_variants {
                for &(font_id, ref glyphs) in glyphs {
                    for glyph in glyphs {
                        cache.queue_glyph(font_id, glyph.clone());
                    }
                }

                cache.cache_queued(mock_gpu_upload).expect("cache_queued");

                for &(font_id, ref glyphs) in glyphs {
                    for (index, glyph) in glyphs.iter().enumerate() {
                        let rect = cache.rect_for(font_id, glyph);
                        assert!(
                            rect.is_ok(),
                            "Gpu cache rect lookup failed ({:?}) for font {} glyph index {}, id {}",
                            rect,
                            font_id,
                            index,
                            glyph.id().0
                        );
                    }
                }
            }
        })
    });
}

criterion_group!(
    benches,
    bench_high_position_tolerance,
    bench_single_ttf,
    bench_single_otf,
    bench_multi_font,
    bench_multi_font_population,
    bench_moving_text,
    bench_resizing,
    bench_moving_text_thrashing,
);

criterion_main!(benches);
<|endoftext|>