<commit_before>//
// gash.rs
//
// Starting code for PS2
// Running on Rust 0.9
//
// University of Virginia - cs4414 Spring 2014
// Weilin Xu, David Evans
// Version 0.4
//

extern mod extra;

use std::{io, run, os};
use std::io::buffered::BufferedReader;
use std::io::stdin;
use extra::getopts;

struct Shell {
    cmd_prompt: ~str,
}

impl Shell {
    fn new(prompt_str: &str) -> Shell {
        Shell {
            cmd_prompt: prompt_str.to_owned(),
        }
    }

    fn run(&mut self) {
        let mut stdin = BufferedReader::new(stdin());

        loop {
            print(self.cmd_prompt);
            io::stdio::flush();

            let line = stdin.read_line().unwrap();
            let cmd_line = line.trim().to_owned();
            let program = cmd_line.splitn(' ', 1).nth(0).expect("no program");

            match program {
                ""      =>  { continue; }
                "exit"  =>  { return; }
                "cd"	=>  { self.changeDir(cmd_line); }
                _       =>  { self.run_cmdline(cmd_line); }
            }
        }
    }

    fn run_cmdline(&mut self, cmd_line: &str) {
        let mut argv: ~[~str] =
            cmd_line.split(' ').filter_map(|x| if x != "" { Some(x.to_owned()) } else { None }).to_owned_vec();

        if argv.len() > 0 {
            let program: ~str = argv.remove(0);
            self.run_cmd(program, argv);
        }
    }

    fn run_cmd(&mut self, program: &str, argv: &[~str]) {
        if self.cmd_exists(program) {
            run::process_status(program, argv);
        } else {
            println!("{:s}: command not found", program);
        }
    }

    fn cmd_exists(&mut self, cmd_path: &str) -> bool {
        let ret = run::process_output("which", [cmd_path.to_owned()]);
        return ret.expect("exit code error.").status.success();
    }

    //Justin's function for cd
    fn changeDir(&mut self, cmd_line: &str) {
	let mut argv: ~[~str] =
            cmd_line.split(' ').filter_map(|x| if x != "" { Some(x.to_owned()) } else { None }).to_owned_vec();

        if argv.len() > 1 {
            let pstring: ~str = argv.remove(1);
            println!("You want to change cwd to {:s}", pstring);
        }
    }
}

fn get_cmdline_from_args() -> Option<~str> {
    /* Begin processing program arguments and initiate the parameters. */
    let args = os::args();

    let opts = ~[
        getopts::optopt("c")
    ];

    let matches = match getopts::getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => { fail!(f.to_err_msg()) }
    };

    if matches.opt_present("c") {
        let cmd_str = match matches.opt_str("c") {
                                                Some(cmd_str) => {cmd_str.to_owned()},
                                                None => {~""}
                                              };
        return Some(cmd_str);
    } else {
        return None;
    }
}

fn main() {
    let opt_cmd_line = get_cmdline_from_args();

    match opt_cmd_line {
        Some(cmd_line) => Shell::new("").run_cmdline(cmd_line),
        None           => Shell::new("gash > ").run()
    }
}
<commit_msg>A dumb change to a string<commit_after>//
// gash.rs
//
// Starting code for PS2
// Running on Rust 0.9
//
// University of Virginia - cs4414 Spring 2014
// Weilin Xu, David Evans
// Version 0.4
//

extern mod extra;

use std::{io, run, os};
use std::io::buffered::BufferedReader;
use std::io::stdin;
use extra::getopts;

struct Shell {
    cmd_prompt: ~str,
}

impl Shell {
    fn new(prompt_str: &str) -> Shell {
        Shell {
            cmd_prompt: prompt_str.to_owned(),
        }
    }

    fn run(&mut self) {
        let mut stdin = BufferedReader::new(stdin());

        loop {
            print(self.cmd_prompt);
            io::stdio::flush();

            let line = stdin.read_line().unwrap();
            let cmd_line = line.trim().to_owned();
            let program = cmd_line.splitn(' ', 1).nth(0).expect("no program");

            match program {
                ""      =>  { continue; }
                "exit"  =>  { return; }
                "cd"	=>  { self.changeDir(cmd_line); }
                _       =>  { self.run_cmdline(cmd_line); }
            }
        }
    }

    fn run_cmdline(&mut self, cmd_line: &str) {
        let mut argv: ~[~str] =
            cmd_line.split(' ').filter_map(|x| if x != "" { Some(x.to_owned()) } else { None }).to_owned_vec();

        if argv.len() > 0 {
            let program: ~str = argv.remove(0);
            self.run_cmd(program, argv);
        }
    }

    fn run_cmd(&mut self, program: &str, argv: &[~str]) {
        if self.cmd_exists(program) {
            run::process_status(program, argv);
        } else {
            println!("{:s}: command not found", program);
        }
    }

    fn cmd_exists(&mut self, cmd_path: &str) -> bool {
        let ret = run::process_output("which", [cmd_path.to_owned()]);
        return ret.expect("exit code error.").status.success();
    }

    //Justin's function for cd
    fn changeDir(&mut self, cmd_line: &str) {
	let mut argv: ~[~str] =
            cmd_line.split(' ').filter_map(|x| if x != "" { Some(x.to_owned()) } else { None }).to_owned_vec();

        if argv.len() > 1 {
            let pstring: ~str = argv.remove(1);
            println!("You want to change cwd to {:s} that's cool", pstring);
        }
    }
}

fn get_cmdline_from_args() -> Option<~str> {
    /* Begin processing program arguments and initiate the parameters. */
    let args = os::args();

    let opts = ~[
        getopts::optopt("c")
    ];

    let matches = match getopts::getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => { fail!(f.to_err_msg()) }
    };

    if matches.opt_present("c") {
        let cmd_str = match matches.opt_str("c") {
                                                Some(cmd_str) => {cmd_str.to_owned()},
                                                None => {~""}
                                              };
        return Some(cmd_str);
    } else {
        return None;
    }
}

fn main() {
    let opt_cmd_line = get_cmdline_from_args();

    match opt_cmd_line {
        Some(cmd_line) => Shell::new("").run_cmdline(cmd_line),
        None           => Shell::new("gash > ").run()
    }
}
<|endoftext|>


<commit_before>// rust
// language version: 1.9.0
// to build: rustc http-threads-server.rs
// NOTE: this is my first Rust program, so much of it is probably crappy

use std::io::Read;
use std::io::Write;
use std::net::Shutdown;
use std::net::{TcpListener, TcpStream};
use std::str;
use std::thread;
use std::time::Duration;

const QUEUE_TIMEOUT_SECS: u32 = 4;
//const LISTEN_BACKLOG: u32 = 500;

const SERVER_NAME: &'static str = "http-threads-server.rs";
const HTTP_STATUS_OK: &'static str =  "200 OK";
const HTTP_STATUS_TIMEOUT: &'static str = "408 TIMEOUT";
const HTTP_STATUS_BAD_REQUEST: &'static str = "400 BAD REQUEST";

fn current_time_millis() -> u64 {
    //TODO: time::now()
    return 0;
}

fn simulated_file_read(disk_read_time_ms: u64) {
    thread::sleep(Duration::from_millis(disk_read_time_ms));
}

fn handle_socket_request(stream: &mut TcpStream,
                         receipt_timestamp: u64) {
    // read request from client
    let mut request_buffer = [0; 1024];
    let mut rc = HTTP_STATUS_BAD_REQUEST;
    let mut file_path = "";
    let mut tot_request_time_ms: u64 = 0;
    let mut server_queue_time_ms: u64 = 0;
    let mut disk_read_time_ms: u64 = 0;

    if stream.read(&mut request_buffer).unwrap() > 0 {
        let request_text = str::from_utf8(&request_buffer).unwrap();
        // did we get anything from client?
        if request_text.trim().len() > 0 {
            // determine how long the request has waited in queue
            let start_processing_timestamp = current_time_millis();
            server_queue_time_ms =
                start_processing_timestamp - receipt_timestamp;
            let x = server_queue_time_ms / 1000;
            let server_queue_time_secs = x as u32;

            // has this request already timed out?
            if server_queue_time_secs >= QUEUE_TIMEOUT_SECS {
                println!("timeout (queue)");
                rc = HTTP_STATUS_TIMEOUT;
            } else {
                let line_tokens: Vec<&str> = request_text.split(' ').collect();
                if line_tokens.len() > 1 {
                    //let request_method = line_tokens[0];
                    let args_text = line_tokens[1];
                    let http_status_code: u32;
                    let fields: Vec<&str> = args_text.split(',').collect();
                    if fields.len() == 3 {
                        let parse_rc_result = fields[0].parse();
                        match parse_rc_result {
                            Err(_) => {},
                            Ok(rc_input) => {
                                let parse_time_result = fields[1].parse();
                                match parse_time_result {
                                    Err(_) => {},
                                    Ok(req_response_time_ms) => {
                                        http_status_code = rc_input;
                                        if http_status_code > 0 {
                                            disk_read_time_ms = req_response_time_ms;
                                            file_path = fields[2];
                                            simulated_file_read(disk_read_time_ms);
                                            rc = HTTP_STATUS_OK;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        // total request time is sum of time spent in queue and the
        // simulated disk read time
        tot_request_time_ms =
            server_queue_time_ms + disk_read_time_ms;
    }

    // create response
    let response_body = format!("{0},{1}",
        tot_request_time_ms,
        file_path);

    let response_headers = format!("HTTP/1.1 {0}\n \
                                   Server: {1}\n \
                                   Content-Length: {2}\n \
                                   Connection: close\n \
                                   \n",
                                   rc,
                                   SERVER_NAME,
                                   response_body.len());

    // return response to client
    let _ = stream.write_all(response_headers.as_bytes()); //.unwrap();
    let _ = stream.write_all(response_body.as_bytes()); //.unwrap();

    let _ = stream.shutdown(Shutdown::Both);
}

fn main() {
    let server_port = 7000;
    //TODO: build string using server_port
    let listener = TcpListener::bind("127.0.0.1:7000").unwrap();
    println!("server ({0}) listening on port {1}", SERVER_NAME, server_port);

    for stream in listener.incoming() {
        match stream {
            Err(_) => {},
            Ok(mut stream) => {
                let receipt_timestamp = current_time_millis();
                thread::spawn(move|| {
                    handle_socket_request(&mut stream, receipt_timestamp);
                });
            }
        }
    }
}

<commit_msg>remove leading '/' from request arguments<commit_after>// rust
// language version: 1.9.0
// to build: rustc http-threads-server.rs
// NOTE: this is my first Rust program, so much of it is probably crappy

use std::io::Read;
use std::io::Write;
use std::net::Shutdown;
use std::net::{TcpListener, TcpStream};
use std::str;
use std::thread;
use std::time::Duration;

const QUEUE_TIMEOUT_SECS: u32 = 4;
//const LISTEN_BACKLOG: u32 = 500;

const SERVER_NAME: &'static str = "http-threads-server.rs";
const HTTP_STATUS_OK: &'static str =  "200 OK";
const HTTP_STATUS_TIMEOUT: &'static str = "408 TIMEOUT";
const HTTP_STATUS_BAD_REQUEST: &'static str = "400 BAD REQUEST";

fn current_time_millis() -> u64 {
    //TODO: time::now()
    return 0;
}

fn simulated_file_read(disk_read_time_ms: u64) {
    thread::sleep(Duration::from_millis(disk_read_time_ms));
}

fn handle_socket_request(stream: &mut TcpStream,
                         receipt_timestamp: u64) {
    // read request from client
    let mut request_buffer = [0; 1024];
    let mut rc = HTTP_STATUS_BAD_REQUEST;
    let mut file_path = "";
    let mut tot_request_time_ms: u64 = 0;
    let mut server_queue_time_ms: u64 = 0;
    let mut disk_read_time_ms: u64 = 0;

    if stream.read(&mut request_buffer).unwrap() > 0 {
        let request_text = str::from_utf8(&request_buffer).unwrap();
        // did we get anything from client?
        if request_text.trim().len() > 0 {
            // determine how long the request has waited in queue
            let start_processing_timestamp = current_time_millis();
            server_queue_time_ms =
                start_processing_timestamp - receipt_timestamp;
            let x = server_queue_time_ms / 1000;
            let server_queue_time_secs = x as u32;

            // has this request already timed out?
            if server_queue_time_secs >= QUEUE_TIMEOUT_SECS {
                println!("timeout (queue)");
                rc = HTTP_STATUS_TIMEOUT;
            } else {
                let line_tokens: Vec<&str> = request_text.split(' ').collect();
                if line_tokens.len() > 1 {
                    //let request_method = line_tokens[0];
                    let args_text = line_tokens[1];
                    let http_status_code: u32;
                    let fields: Vec<&str> = args_text.split(',').collect();
                    if fields.len() == 3 {
                        let mut rc_as_string = fields[0];
                        rc_as_string = rc_as_string.trim_matches('/');
                        let parse_rc_result = rc_as_string.parse();
                        match parse_rc_result {
                            Err(_) => {},
                            Ok(rc_input) => {
                                let parse_time_result = fields[1].parse();
                                match parse_time_result {
                                    Err(_) => {},
                                    Ok(req_response_time_ms) => {
                                        http_status_code = rc_input;
                                        if http_status_code > 0 {
                                            disk_read_time_ms = req_response_time_ms;
                                            file_path = fields[2];
                                            simulated_file_read(disk_read_time_ms);
                                            rc = HTTP_STATUS_OK;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        // total request time is sum of time spent in queue and the
        // simulated disk read time
        tot_request_time_ms =
            server_queue_time_ms + disk_read_time_ms;
    }

    // create response
    let response_body = format!("{0},{1}",
        tot_request_time_ms,
        file_path);

    let response_headers = format!("HTTP/1.1 {0}\n \
                                   Server: {1}\n \
                                   Content-Length: {2}\n \
                                   Connection: close\n \
                                   \n",
                                   rc,
                                   SERVER_NAME,
                                   response_body.len());

    //println!("rc: {0}", rc);

    // return response to client
    let _ = stream.write_all(response_headers.as_bytes()); //.unwrap();
    let _ = stream.write_all(response_body.as_bytes()); //.unwrap();

    let _ = stream.shutdown(Shutdown::Both);
}

fn main() {
    let server_port = 7000;
    //TODO: build string using server_port
    let listener = TcpListener::bind("127.0.0.1:7000").unwrap();
    println!("server ({0}) listening on port {1}", SERVER_NAME, server_port);

    for stream in listener.incoming() {
        match stream {
            Err(_) => {},
            Ok(mut stream) => {
                let receipt_timestamp = current_time_millis();
                thread::spawn(move|| {
                    handle_socket_request(&mut stream, receipt_timestamp);
                });
            }
        }
    }
}

<|endoftext|>


<commit_before>use core::cmp::min;
use core::mem::size_of;
use core::ptr;

use common::scheduler::*;

pub const PAGE_DIRECTORY: usize = 0x300000;
pub const PAGE_TABLE_SIZE: usize = 1024;
pub const PAGE_TABLES: usize = PAGE_DIRECTORY + PAGE_TABLE_SIZE * 4;
pub const PAGE_SIZE: usize = 4 * 1024;

pub const CLUSTER_ADDRESS: usize = PAGE_TABLES + PAGE_TABLE_SIZE * PAGE_TABLE_SIZE * 4 ;
pub const CLUSTER_COUNT: usize = 1024 * 1024; // 4 GiB
pub const CLUSTER_SIZE: usize = 4 * 1024; // Of 4 K chunks

#[repr(packed)]
struct MemoryMapEntry {
    base: u64,
    len: u64,
    class: u32,
    acpi: u32,
}

struct Memory {
    address: usize,
}

impl Memory {
    pub fn new(size: usize) -> Option<Self> {
        let alloc = unsafe { alloc(size) };
        if alloc > 0 {
            Some(Memory { address: alloc })
        }
        else { None }
    }

    pub fn renew(&self, size: usize) -> Option<Self> {
        let realloc = unsafe { realloc(self.address, size) };
        if realloc > 0 {
            Some(Memory { address: realloc })
        }
        else { None }
    }

    pub fn size(self) -> usize {
        unsafe { alloc_size(self.address) }
    }
}

impl Drop for Memory {
    fn drop(&mut self) {
        unsafe { unalloc(self.address) }
    }
}

const MEMORY_MAP: *const MemoryMapEntry = 0x500 as *const MemoryMapEntry;

pub unsafe fn cluster(number: usize) -> usize {
    if number < CLUSTER_COUNT {
        ptr::read((CLUSTER_ADDRESS + number * size_of::<usize>()) as *const usize)
    } else {
        0
    }
}

pub unsafe fn set_cluster(number: usize, address: usize) {
    if number < CLUSTER_COUNT {
        ptr::write((CLUSTER_ADDRESS + number * size_of::<usize>()) as *mut usize,
                   address);
    }
}

pub unsafe fn address_to_cluster(address: usize) -> usize {
    if address >= CLUSTER_ADDRESS + CLUSTER_COUNT * size_of::<usize>() {
        (address - CLUSTER_ADDRESS - CLUSTER_COUNT * size_of::<usize>()) / CLUSTER_SIZE
    } else {
        0
    }
}

pub unsafe fn cluster_to_address(number: usize) -> usize {
    CLUSTER_ADDRESS + CLUSTER_COUNT * size_of::<usize>() + number * CLUSTER_SIZE
}

pub unsafe fn cluster_init() {
    //First, set all clusters to the not present value
    for cluster in 0..CLUSTER_COUNT {
        set_cluster(cluster, 0xFFFFFFFF);
    }

    //Next, set all valid clusters to the free value
    //TODO: Optimize this function
    for i in 0..((0x5000 - 0x500) / size_of::<MemoryMapEntry>()) {
        let entry = &*MEMORY_MAP.offset(i as isize);
        if entry.len > 0 && entry.class == 1 {
            for cluster in 0..CLUSTER_COUNT {
                let address = cluster_to_address(cluster);
                if address as u64 >= entry.base &&
                   (address as u64 + CLUSTER_SIZE as u64) <= (entry.base + entry.len) {
                    set_cluster(cluster, 0);
                }
            }
        }
    }
}

pub unsafe fn alloc(size: usize) -> usize {
    let mut ret = 0;

    //Memory allocation must be atomic
    let reenable = start_no_ints();

    if size > 0 {
        let mut number = 0;
        let mut count = 0;

        for i in 0..CLUSTER_COUNT {
            if cluster(i) == 0 {
                if count == 0 {
                    number = i;
                }
                count += 1;
                if count * CLUSTER_SIZE > size {
                    break;
                }
            } else {
                count = 0;
            }
        }
        if count * CLUSTER_SIZE > size {
            let address = cluster_to_address(number);
            for i in number..number + count {
                set_cluster(i, address);
            }
            ret = address;
        }
    }

    //Memory allocation must be atomic
    end_no_ints(reenable);

    ret
}

pub unsafe fn alloc_aligned(size: usize, align: usize) -> usize {
    let mut ret = 0;

    //Memory allocation must be atomic
    let reenable = start_no_ints();

    if size > 0 {
        let mut number = 0;
        let mut count = 0;

        for i in 0..CLUSTER_COUNT {
            if cluster(i) == 0 && cluster_to_address(i) % align == 0 {
                if count == 0 {
                    number = i;
                }
                count += 1;
                if count * CLUSTER_SIZE > size {
                    break;
                }
            } else {
                count = 0;
            }
        }
        if count * CLUSTER_SIZE > size {
            let address = cluster_to_address(number);
            for i in number..number + count {
                set_cluster(i, address);
            }
            ret = address;
        }
    }

    //Memory allocation must be atomic
    end_no_ints(reenable);

    ret
}

pub unsafe fn alloc_type<T>() -> *mut T {
    alloc(size_of::<T>()) as *mut T
}

pub unsafe fn alloc_size(ptr: usize) -> usize {
    let mut size = 0;

    //Memory allocation must be atomic
    let reenable = start_no_ints();

    if ptr > 0 {
        for i in address_to_cluster(ptr)..CLUSTER_COUNT {
            if cluster(i) == ptr {
                size += CLUSTER_SIZE;
            } else {
                break;
            }
        }
    }

    //Memory allocation must be atomic
    end_no_ints(reenable);

    size
}

pub unsafe fn unalloc(ptr: usize) {
    //Memory allocation must be atomic
    let reenable = start_no_ints();

    if ptr > 0 {
        for i in address_to_cluster(ptr)..CLUSTER_COUNT {
            if cluster(i) == ptr {
                set_cluster(i, 0);
            } else {
                break;
            }
        }
    }

    //Memory allocation must be atomic
    end_no_ints(reenable);
}

pub unsafe fn realloc(ptr: usize, size: usize) -> usize {
    let mut ret = 0;

    //Memory allocation must be atomic
    let reenable = start_no_ints();

    if size == 0 {
        if ptr > 0 {
            unalloc(ptr);
        }
    } else {
        let old_size = alloc_size(ptr);
        if size <= old_size {
            ret = ptr;
        } else {
            ret = alloc(size);
            if ptr > 0 {
                if ret > 0 {
                    let copy_size = min(old_size, size);

                    ::memmove(ret as *mut u8, ptr as *const u8, copy_size);
                }
                unalloc(ptr);
            }
        }
    }

    end_no_ints(reenable);

    ret
}

pub unsafe fn realloc_inplace(ptr: usize, size: usize) -> usize {
    let old_size = alloc_size(ptr);
    if size <= old_size {
        size
    } else {
        old_size
    }
}

pub fn memory_used() -> usize {
    let mut ret = 0;
    unsafe {
        //Memory allocation must be atomic
        let reenable = start_no_ints();

        for i in 0..CLUSTER_COUNT {
            if cluster(i) != 0 && cluster(i) != 0xFFFFFFFF {
                ret += CLUSTER_SIZE;
            }
        }

        //Memory allocation must be atomic
        end_no_ints(reenable);
    }
    ret
}

pub fn memory_free() -> usize {
    let mut ret = 0;
    unsafe {
        //Memory allocation must be atomic
        let reenable = start_no_ints();

        for i in 0..CLUSTER_COUNT {
            if cluster(i) == 0 {
                ret += CLUSTER_SIZE;
            }
        }

        //Memory allocation must be atomic
        end_no_ints(reenable);
    }
    ret
}
<commit_msg>type T for memory<commit_after>use core::cmp::min;
use core::intrinsics;
use core::mem::size_of;
use core::ptr;

use common::scheduler::*;

pub const PAGE_DIRECTORY: usize = 0x300000;
pub const PAGE_TABLE_SIZE: usize = 1024;
pub const PAGE_TABLES: usize = PAGE_DIRECTORY + PAGE_TABLE_SIZE * 4;
pub const PAGE_SIZE: usize = 4 * 1024;

pub const CLUSTER_ADDRESS: usize = PAGE_TABLES + PAGE_TABLE_SIZE * PAGE_TABLE_SIZE * 4 ;
pub const CLUSTER_COUNT: usize = 1024 * 1024; // 4 GiB
pub const CLUSTER_SIZE: usize = 4 * 1024; // Of 4 K chunks

#[repr(packed)]
struct MemoryMapEntry {
    base: u64,
    len: u64,
    class: u32,
    acpi: u32,
}

struct Memory<T> {
    ptr: *mut T,
}

impl<T> Memory<T> {
    pub fn new(count: usize) -> Option<Self> {
        let alloc = unsafe { alloc(count * size_of::<T>()) };
        if alloc > 0 {
            Some(Memory { ptr: alloc as *mut T })
        }
        else { None }
    }

    pub fn renew(&mut self, count: usize) -> bool {
        let address = unsafe { realloc(self.ptr as usize, count * size_of::<T>()) };
        if address > 0 {
            self.ptr = address as *mut T;
            true
        }
        else { false }
    }

    pub fn length(self) -> usize {
        unsafe { alloc_size(self.ptr as usize)/size_of::<T>() }
    }

    pub unsafe fn address(&self) -> usize {
        self.ptr as usize
    }

    pub unsafe fn read(&self, i: usize) -> T {
        ptr::read(self.ptr.offset(i as isize))
    }

    pub unsafe fn load(&self, i: usize) -> T {
        intrinsics::volatile_load(self.ptr.offset(i as isize))
    }

    pub unsafe fn write(&mut self, i: usize, value: T) {
        ptr::write(self.ptr.offset(i as isize), value);
    }

    pub unsafe fn store(&mut self, i: usize, value: T) {
        intrinsics::volatile_store(self.ptr.offset(i as isize), value);
    }
}

impl<T> Drop for Memory<T> {
    fn drop(&mut self) {
        unsafe { unalloc(self.ptr as usize) }
    }
}

const MEMORY_MAP: *const MemoryMapEntry = 0x500 as *const MemoryMapEntry;

pub unsafe fn cluster(number: usize) -> usize {
    if number < CLUSTER_COUNT {
        ptr::read((CLUSTER_ADDRESS + number * size_of::<usize>()) as *const usize)
    } else {
        0
    }
}

pub unsafe fn set_cluster(number: usize, address: usize) {
    if number < CLUSTER_COUNT {
        ptr::write((CLUSTER_ADDRESS + number * size_of::<usize>()) as *mut usize,
                   address);
    }
}

pub unsafe fn address_to_cluster(address: usize) -> usize {
    if address >= CLUSTER_ADDRESS + CLUSTER_COUNT * size_of::<usize>() {
        (address - CLUSTER_ADDRESS - CLUSTER_COUNT * size_of::<usize>()) / CLUSTER_SIZE
    } else {
        0
    }
}

pub unsafe fn cluster_to_address(number: usize) -> usize {
    CLUSTER_ADDRESS + CLUSTER_COUNT * size_of::<usize>() + number * CLUSTER_SIZE
}

pub unsafe fn cluster_init() {
    //First, set all clusters to the not present value
    for cluster in 0..CLUSTER_COUNT {
        set_cluster(cluster, 0xFFFFFFFF);
    }

    //Next, set all valid clusters to the free value
    //TODO: Optimize this function
    for i in 0..((0x5000 - 0x500) / size_of::<MemoryMapEntry>()) {
        let entry = &*MEMORY_MAP.offset(i as isize);
        if entry.len > 0 && entry.class == 1 {
            for cluster in 0..CLUSTER_COUNT {
                let address = cluster_to_address(cluster);
                if address as u64 >= entry.base &&
                   (address as u64 + CLUSTER_SIZE as u64) <= (entry.base + entry.len) {
                    set_cluster(cluster, 0);
                }
            }
        }
    }
}

pub unsafe fn alloc(size: usize) -> usize {
    let mut ret = 0;

    //Memory allocation must be atomic
    let reenable = start_no_ints();

    if size > 0 {
        let mut number = 0;
        let mut count = 0;

        for i in 0..CLUSTER_COUNT {
            if cluster(i) == 0 {
                if count == 0 {
                    number = i;
                }
                count += 1;
                if count * CLUSTER_SIZE > size {
                    break;
                }
            } else {
                count = 0;
            }
        }
        if count * CLUSTER_SIZE > size {
            let address = cluster_to_address(number);
            for i in number..number + count {
                set_cluster(i, address);
            }
            ret = address;
        }
    }

    //Memory allocation must be atomic
    end_no_ints(reenable);

    ret
}

pub unsafe fn alloc_aligned(size: usize, align: usize) -> usize {
    let mut ret = 0;

    //Memory allocation must be atomic
    let reenable = start_no_ints();

    if size > 0 {
        let mut number = 0;
        let mut count = 0;

        for i in 0..CLUSTER_COUNT {
            if cluster(i) == 0 && cluster_to_address(i) % align == 0 {
                if count == 0 {
                    number = i;
                }
                count += 1;
                if count * CLUSTER_SIZE > size {
                    break;
                }
            } else {
                count = 0;
            }
        }
        if count * CLUSTER_SIZE > size {
            let address = cluster_to_address(number);
            for i in number..number + count {
                set_cluster(i, address);
            }
            ret = address;
        }
    }

    //Memory allocation must be atomic
    end_no_ints(reenable);

    ret
}

pub unsafe fn alloc_type<T>() -> *mut T {
    alloc(size_of::<T>()) as *mut T
}

pub unsafe fn alloc_size(ptr: usize) -> usize {
    let mut size = 0;

    //Memory allocation must be atomic
    let reenable = start_no_ints();

    if ptr > 0 {
        for i in address_to_cluster(ptr)..CLUSTER_COUNT {
            if cluster(i) == ptr {
                size += CLUSTER_SIZE;
            } else {
                break;
            }
        }
    }

    //Memory allocation must be atomic
    end_no_ints(reenable);

    size
}

pub unsafe fn unalloc(ptr: usize) {
    //Memory allocation must be atomic
    let reenable = start_no_ints();

    if ptr > 0 {
        for i in address_to_cluster(ptr)..CLUSTER_COUNT {
            if cluster(i) == ptr {
                set_cluster(i, 0);
            } else {
                break;
            }
        }
    }

    //Memory allocation must be atomic
    end_no_ints(reenable);
}

pub unsafe fn realloc(ptr: usize, size: usize) -> usize {
    let mut ret = 0;

    //Memory allocation must be atomic
    let reenable = start_no_ints();

    if size == 0 {
        if ptr > 0 {
            unalloc(ptr);
        }
    } else {
        let old_size = alloc_size(ptr);
        if size <= old_size {
            ret = ptr;
        } else {
            ret = alloc(size);
            if ptr > 0 {
                if ret > 0 {
                    let copy_size = min(old_size, size);

                    ::memmove(ret as *mut u8, ptr as *const u8, copy_size);
                }
                unalloc(ptr);
            }
        }
    }

    end_no_ints(reenable);

    ret
}

pub unsafe fn realloc_inplace(ptr: usize, size: usize) -> usize {
    let old_size = alloc_size(ptr);
    if size <= old_size {
        size
    } else {
        old_size
    }
}

pub fn memory_used() -> usize {
    let mut ret = 0;
    unsafe {
        //Memory allocation must be atomic
        let reenable = start_no_ints();

        for i in 0..CLUSTER_COUNT {
            if cluster(i) != 0 && cluster(i) != 0xFFFFFFFF {
                ret += CLUSTER_SIZE;
            }
        }

        //Memory allocation must be atomic
        end_no_ints(reenable);
    }
    ret
}

pub fn memory_free() -> usize {
    let mut ret = 0;
    unsafe {
        //Memory allocation must be atomic
        let reenable = start_no_ints();

        for i in 0..CLUSTER_COUNT {
            if cluster(i) == 0 {
                ret += CLUSTER_SIZE;
            }
        }

        //Memory allocation must be atomic
        end_no_ints(reenable);
    }
    ret
}
<|endoftext|>


<commit_before>use crate::utils::{is_allowed, snippet, span_help_and_lint};
use rustc::hir::*;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};
use syntax::ast::LitKind;
use syntax::source_map::Span;
use unicode_normalization::UnicodeNormalization;

declare_clippy_lint! {
    /// **What it does:** Checks for the Unicode zero-width space in the code.
    ///
    /// **Why is this bad?** Having an invisible character in the code makes for all
    /// sorts of April fools, but otherwise is very much frowned upon.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:** You don't see it, but there may be a zero-width space
    /// somewhere in this text.
    pub ZERO_WIDTH_SPACE,
    correctness,
    "using a zero-width space in a string literal, which is confusing"
}

declare_clippy_lint! {
    /// **What it does:** Checks for non-ASCII characters in string literals.
    ///
    /// **Why is this bad?** Yeah, we know, the 90's called and wanted their charset
    /// back. Even so, there still are editors and other programs out there that
    /// don't work well with Unicode. So if the code is meant to be used
    /// internationally, on multiple operating systems, or has other portability
    /// requirements, activating this lint could be useful.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// let x = "Hä?"
    /// ```
    pub NON_ASCII_LITERAL,
    pedantic,
    "using any literal non-ASCII chars in a string literal instead of using the `\\u` escape"
}

declare_clippy_lint! {
    /// **What it does:** Checks for string literals that contain Unicode in a form
    /// that is not equal to its
    /// [NFC-recomposition](http://www.unicode.org/reports/tr15/#Norm_Forms).
    ///
    /// **Why is this bad?** If such a string is compared to another, the results
    /// may be surprising.
    ///
    /// **Known problems** None.
    ///
    /// **Example:** You may not see it, but “à” and “à” aren't the same string. The
    /// former when escaped is actually `"a\u{300}"` while the latter is `"\u{e0}"`.
    pub UNICODE_NOT_NFC,
    pedantic,
    "using a unicode literal not in NFC normal form (see [unicode tr15](http://www.unicode.org/reports/tr15/) for further information)"
}

declare_lint_pass!(Unicode => [ZERO_WIDTH_SPACE, NON_ASCII_LITERAL, UNICODE_NOT_NFC]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Unicode {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
        if let ExprKind::Lit(ref lit) = expr.node {
            if let LitKind::Str(_, _) = lit.node {
                check_str(cx, lit.span, expr.hir_id)
            }
        }
    }
}

fn escape<T: Iterator<Item = char>>(s: T) -> String {
    let mut result = String::new();
    for c in s {
        if c as u32 > 0x7F {
            for d in c.escape_unicode() {
                result.push(d)
            }
        } else {
            result.push(c);
        }
    }
    result
}

fn check_str(cx: &LateContext<'_, '_>, span: Span, id: HirId) {
    let string = snippet(cx, span, "");
    if string.contains('\u{200B}') {
        span_help_and_lint(
            cx,
            ZERO_WIDTH_SPACE,
            span,
            "zero-width space detected",
            &format!(
                "Consider replacing the string with:\n\"{}\"",
                string.replace("\u{200B}", "\\u{200B}")
            ),
        );
    }
    if string.chars().any(|c| c as u32 > 0x7F) {
        span_help_and_lint(
            cx,
            NON_ASCII_LITERAL,
            span,
            "literal non-ASCII character detected",
            &format!(
                "Consider replacing the string with:\n\"{}\"",
                if is_allowed(cx, UNICODE_NOT_NFC, id) {
                    escape(string.chars())
                } else {
                    escape(string.nfc())
                }
            ),
        );
    }
    if is_allowed(cx, NON_ASCII_LITERAL, id) && string.chars().zip(string.nfc()).any(|(a, b)| a != b) {
        span_help_and_lint(
            cx,
            UNICODE_NOT_NFC,
            span,
            "non-nfc unicode sequence detected",
            &format!(
                "Consider replacing the string with:\n\"{}\"",
                string.nfc().collect::<String>()
            ),
        );
    }
}
<commit_msg>Improve verbosity of non_ascii_literal lint example<commit_after>use crate::utils::{is_allowed, snippet, span_help_and_lint};
use rustc::hir::*;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};
use syntax::ast::LitKind;
use syntax::source_map::Span;
use unicode_normalization::UnicodeNormalization;

declare_clippy_lint! {
    /// **What it does:** Checks for the Unicode zero-width space in the code.
    ///
    /// **Why is this bad?** Having an invisible character in the code makes for all
    /// sorts of April fools, but otherwise is very much frowned upon.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:** You don't see it, but there may be a zero-width space
    /// somewhere in this text.
    pub ZERO_WIDTH_SPACE,
    correctness,
    "using a zero-width space in a string literal, which is confusing"
}

declare_clippy_lint! {
    /// **What it does:** Checks for non-ASCII characters in string literals.
    ///
    /// **Why is this bad?** Yeah, we know, the 90's called and wanted their charset
    /// back. Even so, there still are editors and other programs out there that
    /// don't work well with Unicode. So if the code is meant to be used
    /// internationally, on multiple operating systems, or has other portability
    /// requirements, activating this lint could be useful.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// let x = String::from("€");
    /// ```
    /// Could be written as:
    /// ```rust
    /// let x = String::from("\u{20ac}");
    /// ```
    pub NON_ASCII_LITERAL,
    pedantic,
    "using any literal non-ASCII chars in a string literal instead of using the `\\u` escape"
}

declare_clippy_lint! {
    /// **What it does:** Checks for string literals that contain Unicode in a form
    /// that is not equal to its
    /// [NFC-recomposition](http://www.unicode.org/reports/tr15/#Norm_Forms).
    ///
    /// **Why is this bad?** If such a string is compared to another, the results
    /// may be surprising.
    ///
    /// **Known problems** None.
    ///
    /// **Example:** You may not see it, but “à” and “à” aren't the same string. The
    /// former when escaped is actually `"a\u{300}"` while the latter is `"\u{e0}"`.
    pub UNICODE_NOT_NFC,
    pedantic,
    "using a unicode literal not in NFC normal form (see [unicode tr15](http://www.unicode.org/reports/tr15/) for further information)"
}

declare_lint_pass!(Unicode => [ZERO_WIDTH_SPACE, NON_ASCII_LITERAL, UNICODE_NOT_NFC]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Unicode {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
        if let ExprKind::Lit(ref lit) = expr.node {
            if let LitKind::Str(_, _) = lit.node {
                check_str(cx, lit.span, expr.hir_id)
            }
        }
    }
}

fn escape<T: Iterator<Item = char>>(s: T) -> String {
    let mut result = String::new();
    for c in s {
        if c as u32 > 0x7F {
            for d in c.escape_unicode() {
                result.push(d)
            }
        } else {
            result.push(c);
        }
    }
    result
}

fn check_str(cx: &LateContext<'_, '_>, span: Span, id: HirId) {
    let string = snippet(cx, span, "");
    if string.contains('\u{200B}') {
        span_help_and_lint(
            cx,
            ZERO_WIDTH_SPACE,
            span,
            "zero-width space detected",
            &format!(
                "Consider replacing the string with:\n\"{}\"",
                string.replace("\u{200B}", "\\u{200B}")
            ),
        );
    }
    if string.chars().any(|c| c as u32 > 0x7F) {
        span_help_and_lint(
            cx,
            NON_ASCII_LITERAL,
            span,
            "literal non-ASCII character detected",
            &format!(
                "Consider replacing the string with:\n\"{}\"",
                if is_allowed(cx, UNICODE_NOT_NFC, id) {
                    escape(string.chars())
                } else {
                    escape(string.nfc())
                }
            ),
        );
    }
    if is_allowed(cx, NON_ASCII_LITERAL, id) && string.chars().zip(string.nfc()).any(|(a, b)| a != b) {
        span_help_and_lint(
            cx,
            UNICODE_NOT_NFC,
            span,
            "non-nfc unicode sequence detected",
            &format!(
                "Consider replacing the string with:\n\"{}\"",
                string.nfc().collect::<String>()
            ),
        );
    }
}
<|endoftext|>


<commit_before>//! Set of allocatable registers as a bit vector of register units.
//!
//! While allocating registers, we need to keep track of which registers are available and which
//! registers are in use. Since registers can alias in different ways, we track this via the
//! "register unit" abstraction. Every register contains one or more register units. Registers that
//! share a register unit can't be in use at the same time.

use isa::registers::{RegClass, RegInfo, RegUnit, RegUnitMask};
use std::char;
use std::fmt;
use std::iter::ExactSizeIterator;
use std::mem::size_of_val;

/// Set of registers available for allocation.
#[derive(Clone)]
pub struct RegisterSet {
    avail: RegUnitMask,
}

// Given a register class and a register unit in the class, compute a word index and a bit mask of
// register units representing that register.
//
// Note that a register is not allowed to straddle words.
fn bitmask(rc: RegClass, reg: RegUnit) -> (usize, u32) {
    // Bit mask representing the register. It is `rc.width` consecutive units.
    let width_bits = (1 << rc.width) - 1;
    // Index into avail[] of the word containing `reg`.
    let word_index = (reg / 32) as usize;
    // The actual bits in the word that cover `reg`.
    let reg_bits = width_bits << (reg % 32);

    (word_index, reg_bits)
}

impl RegisterSet {
    /// Create a new register set with all registers available.
    ///
    /// Note that this includes *all* registers. Query the `TargetIsa` object to get a set of
    /// allocatable registers where reserved registers have been filtered out.
    pub fn new() -> Self {
        Self { avail: [!0; 3] }
    }

    /// Create a new register set with no registers available.
    pub fn empty() -> Self {
        Self { avail: [0; 3] }
    }

    /// Returns `true` if the specified register is available.
    pub fn is_avail(&self, rc: RegClass, reg: RegUnit) -> bool {
        let (idx, bits) = bitmask(rc, reg);
        (self.avail[idx] & bits) == bits
    }

    /// Allocate `reg` from `rc` so it is no longer available.
    ///
    /// It is an error to take a register that doesn't have all of its register units available.
    pub fn take(&mut self, rc: RegClass, reg: RegUnit) {
        let (idx, bits) = bitmask(rc, reg);
        debug_assert!(
            (self.avail[idx] & bits) == bits,
            "{}:{} not available in {}",
            rc,
            rc.info.display_regunit(reg),
            self.display(rc.info)
        );
        self.avail[idx] &= !bits;
    }

    /// Return `reg` and all of its register units to the set of available registers.
    pub fn free(&mut self, rc: RegClass, reg: RegUnit) {
        let (idx, bits) = bitmask(rc, reg);
        debug_assert!(
            (self.avail[idx] & bits) == 0,
            "{}:{} not allocated in {}",
            rc,
            rc.info.display_regunit(reg),
            self.display(rc.info)
        );
        self.avail[idx] |= bits;
    }

    /// Return an iterator over all available registers belonging to the register class `rc`.
    ///
    /// This doesn't allocate anything from the set; use `take()` for that.
    pub fn iter(&self, rc: RegClass) -> RegSetIter {
        // Start by copying the RC mask. It is a single set bit for each register in the class.
        let mut rsi = RegSetIter { regs: rc.mask };

        // Mask out the unavailable units.
        for idx in 0..self.avail.len() {
            // If a single unit in a register is unavailable, the whole register can't be used.
            // If a register straddles a word boundary, it will be marked as unavailable.
            // There's an assertion in `cdsl/registers.py` to check for that.
            for i in 0..rc.width {
                rsi.regs[idx] &= self.avail[idx] >> i;
            }
        }
        rsi
    }

    /// Check if any register units allocated out of this set interferes with units allocated out
    /// of `other`.
    ///
    /// This assumes that unused bits are 1.
    pub fn interferes_with(&self, other: &Self) -> bool {
        self.avail
            .iter()
            .zip(&other.avail)
            .any(|(&x, &y)| (x | y) != !0)
    }

    /// Intersect this set of registers with `other`. This has the effect of removing any register
    /// units from this set that are not in `other`.
    pub fn intersect(&mut self, other: &Self) {
        for (x, &y) in self.avail.iter_mut().zip(&other.avail) {
            *x &= y;
        }
    }

    /// Return an object that can display this register set, using the register info from the
    /// target ISA.
    pub fn display<'a, R: Into<Option<&'a RegInfo>>>(&self, regs: R) -> DisplayRegisterSet<'a> {
        DisplayRegisterSet(self.clone(), regs.into())
    }
}

/// Iterator over available registers in a register class.
pub struct RegSetIter {
    regs: RegUnitMask,
}

impl Iterator for RegSetIter {
    type Item = RegUnit;

    fn next(&mut self) -> Option<RegUnit> {
        let mut unit_offset = 0;

        // Find the first set bit in `self.regs`.
        for word in &mut self.regs {
            if *word != 0 {
                // Compute the register unit number from the lowest set bit in the word.
                let unit = unit_offset + word.trailing_zeros() as RegUnit;

                // Clear that lowest bit so we won't find it again.
                *word &= *word - 1;

                return Some(unit);
            }
            // How many register units was there in the word? This is a constant 32 for `u32` etc.
            unit_offset += 8 * size_of_val(word) as RegUnit;
        }

        // All of `self.regs` is 0.
        None
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let bits = self.regs.iter().map(|&w| w.count_ones() as usize).sum();
        (bits, Some(bits))
    }
}

impl ExactSizeIterator for RegSetIter {}

/// Displaying an `RegisterSet` correctly requires the associated `RegInfo` from the target ISA.
pub struct DisplayRegisterSet<'a>(RegisterSet, Option<&'a RegInfo>);

impl<'a> fmt::Display for DisplayRegisterSet<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[")?;
        match self.1 {
            None => {
                for w in &self.0.avail {
                    write!(f, " #{:08x}", w)?;
                }
            }
            Some(reginfo) => {
                let toprcs = reginfo
                    .banks
                    .iter()
                    .map(|b| b.first_toprc + b.num_toprcs)
                    .max()
                    .expect("No register banks");
                for rc in &reginfo.classes[0..toprcs] {
                    if rc.width == 1 {
                        let bank = &reginfo.banks[rc.bank as usize];
                        write!(f, " {}: ", rc)?;
                        for offset in 0..bank.units {
                            let reg = bank.first_unit + offset;
                            if !rc.contains(reg) {
                                continue;
                            }
                            if !self.0.is_avail(rc, reg) {
                                write!(f, "-")?;
                                continue;
                            }
                            // Display individual registers as either the second letter of their
                            // name or the last digit of their number.
                            // This works for x86 (rax, rbx, ...) and for numbered regs.
                            write!(
                                f,
                                "{}",
                                bank.names
                                    .get(offset as usize)
                                    .and_then(|name| name.chars().nth(1))
                                    .unwrap_or_else(|| char::from_digit(
                                        u32::from(offset % 10),
                                        10
                                    ).unwrap())
                            )?;
                        }
                    }
                }
            }
        }
        write!(f, " ]")
    }
}

impl fmt::Display for RegisterSet {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.display(None).fmt(f)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use isa::registers::{RegClass, RegClassData};
    use std::vec::Vec;

    // Register classes for testing.
    const GPR: RegClass = &RegClassData {
        name: "GPR",
        index: 0,
        width: 1,
        bank: 0,
        toprc: 0,
        first: 28,
        subclasses: 0,
        mask: [0xf0000000, 0x0000000f, 0],
        info: &INFO,
    };

    const DPR: RegClass = &RegClassData {
        name: "DPR",
        index: 0,
        width: 2,
        bank: 0,
        toprc: 0,
        first: 28,
        subclasses: 0,
        mask: [0x50000000, 0x0000000a, 0],
        info: &INFO,
    };

    const INFO: RegInfo = RegInfo {
        banks: &[],
        classes: &[],
    };

    #[test]
    fn put_and_take() {
        let mut regs = RegisterSet::new();

        // `GPR` has units 28-36.
        assert_eq!(regs.iter(GPR).len(), 8);
        assert_eq!(regs.iter(GPR).count(), 8);
        assert_eq!(regs.iter(DPR).collect::<Vec<_>>(), [28, 30, 33, 35]);

        assert!(regs.is_avail(GPR, 29));
        regs.take(&GPR, 29);
        assert!(!regs.is_avail(GPR, 29));

        assert_eq!(regs.iter(GPR).count(), 7);
        assert_eq!(regs.iter(DPR).collect::<Vec<_>>(), [30, 33, 35]);

        assert!(regs.is_avail(GPR, 30));
        regs.take(&GPR, 30);
        assert!(!regs.is_avail(GPR, 30));

        assert_eq!(regs.iter(GPR).count(), 6);
        assert_eq!(regs.iter(DPR).collect::<Vec<_>>(), [33, 35]);

        assert!(regs.is_avail(GPR, 32));
        regs.take(&GPR, 32);
        assert!(!regs.is_avail(GPR, 32));

        assert_eq!(regs.iter(GPR).count(), 5);
        assert_eq!(regs.iter(DPR).collect::<Vec<_>>(), [33, 35]);

        regs.free(&GPR, 30);
        assert!(regs.is_avail(GPR, 30));
        assert!(!regs.is_avail(GPR, 29));
        assert!(!regs.is_avail(GPR, 32));

        assert_eq!(regs.iter(GPR).count(), 6);
        assert_eq!(regs.iter(DPR).collect::<Vec<_>>(), [30, 33, 35]);

        regs.free(&GPR, 32);
        assert!(regs.is_avail(GPR, 31));
        assert!(!regs.is_avail(GPR, 29));
        assert!(regs.is_avail(GPR, 32));

        assert_eq!(regs.iter(GPR).count(), 7);
        assert_eq!(regs.iter(DPR).collect::<Vec<_>>(), [30, 33, 35]);
    }

    #[test]
    fn interference() {
        let mut regs1 = RegisterSet::new();
        let mut regs2 = RegisterSet::new();

        assert!(!regs1.interferes_with(&regs2));
        regs1.take(&GPR, 32);
        assert!(!regs1.interferes_with(&regs2));
        regs2.take(&GPR, 31);
        assert!(!regs1.interferes_with(&regs2));
        regs1.intersect(&regs2);
        assert!(regs1.interferes_with(&regs2));
    }
}
<commit_msg>Clarify an assertion error message.<commit_after>//! Set of allocatable registers as a bit vector of register units.
//!
//! While allocating registers, we need to keep track of which registers are available and which
//! registers are in use. Since registers can alias in different ways, we track this via the
//! "register unit" abstraction. Every register contains one or more register units. Registers that
//! share a register unit can't be in use at the same time.

use isa::registers::{RegClass, RegInfo, RegUnit, RegUnitMask};
use std::char;
use std::fmt;
use std::iter::ExactSizeIterator;
use std::mem::size_of_val;

/// Set of registers available for allocation.
#[derive(Clone)]
pub struct RegisterSet {
    avail: RegUnitMask,
}

// Given a register class and a register unit in the class, compute a word index and a bit mask of
// register units representing that register.
//
// Note that a register is not allowed to straddle words.
fn bitmask(rc: RegClass, reg: RegUnit) -> (usize, u32) {
    // Bit mask representing the register. It is `rc.width` consecutive units.
    let width_bits = (1 << rc.width) - 1;
    // Index into avail[] of the word containing `reg`.
    let word_index = (reg / 32) as usize;
    // The actual bits in the word that cover `reg`.
    let reg_bits = width_bits << (reg % 32);

    (word_index, reg_bits)
}

impl RegisterSet {
    /// Create a new register set with all registers available.
    ///
    /// Note that this includes *all* registers. Query the `TargetIsa` object to get a set of
    /// allocatable registers where reserved registers have been filtered out.
    pub fn new() -> Self {
        Self { avail: [!0; 3] }
    }

    /// Create a new register set with no registers available.
    pub fn empty() -> Self {
        Self { avail: [0; 3] }
    }

    /// Returns `true` if the specified register is available.
    pub fn is_avail(&self, rc: RegClass, reg: RegUnit) -> bool {
        let (idx, bits) = bitmask(rc, reg);
        (self.avail[idx] & bits) == bits
    }

    /// Allocate `reg` from `rc` so it is no longer available.
    ///
    /// It is an error to take a register that doesn't have all of its register units available.
    pub fn take(&mut self, rc: RegClass, reg: RegUnit) {
        let (idx, bits) = bitmask(rc, reg);
        debug_assert!(
            (self.avail[idx] & bits) == bits,
            "{}:{} not available in {}",
            rc,
            rc.info.display_regunit(reg),
            self.display(rc.info)
        );
        self.avail[idx] &= !bits;
    }

    /// Return `reg` and all of its register units to the set of available registers.
    pub fn free(&mut self, rc: RegClass, reg: RegUnit) {
        let (idx, bits) = bitmask(rc, reg);
        debug_assert!(
            (self.avail[idx] & bits) == 0,
            "{}:{} is already free in {}",
            rc,
            rc.info.display_regunit(reg),
            self.display(rc.info)
        );
        self.avail[idx] |= bits;
    }

    /// Return an iterator over all available registers belonging to the register class `rc`.
    ///
    /// This doesn't allocate anything from the set; use `take()` for that.
    pub fn iter(&self, rc: RegClass) -> RegSetIter {
        // Start by copying the RC mask. It is a single set bit for each register in the class.
        let mut rsi = RegSetIter { regs: rc.mask };

        // Mask out the unavailable units.
        for idx in 0..self.avail.len() {
            // If a single unit in a register is unavailable, the whole register can't be used.
            // If a register straddles a word boundary, it will be marked as unavailable.
            // There's an assertion in `cdsl/registers.py` to check for that.
            for i in 0..rc.width {
                rsi.regs[idx] &= self.avail[idx] >> i;
            }
        }
        rsi
    }

    /// Check if any register units allocated out of this set interferes with units allocated out
    /// of `other`.
    ///
    /// This assumes that unused bits are 1.
    pub fn interferes_with(&self, other: &Self) -> bool {
        self.avail
            .iter()
            .zip(&other.avail)
            .any(|(&x, &y)| (x | y) != !0)
    }

    /// Intersect this set of registers with `other`. This has the effect of removing any register
    /// units from this set that are not in `other`.
    pub fn intersect(&mut self, other: &Self) {
        for (x, &y) in self.avail.iter_mut().zip(&other.avail) {
            *x &= y;
        }
    }

    /// Return an object that can display this register set, using the register info from the
    /// target ISA.
    pub fn display<'a, R: Into<Option<&'a RegInfo>>>(&self, regs: R) -> DisplayRegisterSet<'a> {
        DisplayRegisterSet(self.clone(), regs.into())
    }
}

/// Iterator over available registers in a register class.
pub struct RegSetIter {
    regs: RegUnitMask,
}

impl Iterator for RegSetIter {
    type Item = RegUnit;

    fn next(&mut self) -> Option<RegUnit> {
        let mut unit_offset = 0;

        // Find the first set bit in `self.regs`.
        for word in &mut self.regs {
            if *word != 0 {
                // Compute the register unit number from the lowest set bit in the word.
                let unit = unit_offset + word.trailing_zeros() as RegUnit;

                // Clear that lowest bit so we won't find it again.
                *word &= *word - 1;

                return Some(unit);
            }
            // How many register units was there in the word? This is a constant 32 for `u32` etc.
            unit_offset += 8 * size_of_val(word) as RegUnit;
        }

        // All of `self.regs` is 0.
        None
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let bits = self.regs.iter().map(|&w| w.count_ones() as usize).sum();
        (bits, Some(bits))
    }
}

impl ExactSizeIterator for RegSetIter {}

/// Displaying an `RegisterSet` correctly requires the associated `RegInfo` from the target ISA.
pub struct DisplayRegisterSet<'a>(RegisterSet, Option<&'a RegInfo>);

impl<'a> fmt::Display for DisplayRegisterSet<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[")?;
        match self.1 {
            None => {
                for w in &self.0.avail {
                    write!(f, " #{:08x}", w)?;
                }
            }
            Some(reginfo) => {
                let toprcs = reginfo
                    .banks
                    .iter()
                    .map(|b| b.first_toprc + b.num_toprcs)
                    .max()
                    .expect("No register banks");
                for rc in &reginfo.classes[0..toprcs] {
                    if rc.width == 1 {
                        let bank = &reginfo.banks[rc.bank as usize];
                        write!(f, " {}: ", rc)?;
                        for offset in 0..bank.units {
                            let reg = bank.first_unit + offset;
                            if !rc.contains(reg) {
                                continue;
                            }
                            if !self.0.is_avail(rc, reg) {
                                write!(f, "-")?;
                                continue;
                            }
                            // Display individual registers as either the second letter of their
                            // name or the last digit of their number.
                            // This works for x86 (rax, rbx, ...) and for numbered regs.
                            write!(
                                f,
                                "{}",
                                bank.names
                                    .get(offset as usize)
                                    .and_then(|name| name.chars().nth(1))
                                    .unwrap_or_else(|| char::from_digit(
                                        u32::from(offset % 10),
                                        10
                                    ).unwrap())
                            )?;
                        }
                    }
                }
            }
        }
        write!(f, " ]")
    }
}

impl fmt::Display for RegisterSet {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.display(None).fmt(f)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use isa::registers::{RegClass, RegClassData};
    use std::vec::Vec;

    // Register classes for testing.
    const GPR: RegClass = &RegClassData {
        name: "GPR",
        index: 0,
        width: 1,
        bank: 0,
        toprc: 0,
        first: 28,
        subclasses: 0,
        mask: [0xf0000000, 0x0000000f, 0],
        info: &INFO,
    };

    const DPR: RegClass = &RegClassData {
        name: "DPR",
        index: 0,
        width: 2,
        bank: 0,
        toprc: 0,
        first: 28,
        subclasses: 0,
        mask: [0x50000000, 0x0000000a, 0],
        info: &INFO,
    };

    const INFO: RegInfo = RegInfo {
        banks: &[],
        classes: &[],
    };

    #[test]
    fn put_and_take() {
        let mut regs = RegisterSet::new();

        // `GPR` has units 28-36.
        assert_eq!(regs.iter(GPR).len(), 8);
        assert_eq!(regs.iter(GPR).count(), 8);
        assert_eq!(regs.iter(DPR).collect::<Vec<_>>(), [28, 30, 33, 35]);

        assert!(regs.is_avail(GPR, 29));
        regs.take(&GPR, 29);
        assert!(!regs.is_avail(GPR, 29));

        assert_eq!(regs.iter(GPR).count(), 7);
        assert_eq!(regs.iter(DPR).collect::<Vec<_>>(), [30, 33, 35]);

        assert!(regs.is_avail(GPR, 30));
        regs.take(&GPR, 30);
        assert!(!regs.is_avail(GPR, 30));

        assert_eq!(regs.iter(GPR).count(), 6);
        assert_eq!(regs.iter(DPR).collect::<Vec<_>>(), [33, 35]);

        assert!(regs.is_avail(GPR, 32));
        regs.take(&GPR, 32);
        assert!(!regs.is_avail(GPR, 32));

        assert_eq!(regs.iter(GPR).count(), 5);
        assert_eq!(regs.iter(DPR).collect::<Vec<_>>(), [33, 35]);

        regs.free(&GPR, 30);
        assert!(regs.is_avail(GPR, 30));
        assert!(!regs.is_avail(GPR, 29));
        assert!(!regs.is_avail(GPR, 32));

        assert_eq!(regs.iter(GPR).count(), 6);
        assert_eq!(regs.iter(DPR).collect::<Vec<_>>(), [30, 33, 35]);

        regs.free(&GPR, 32);
        assert!(regs.is_avail(GPR, 31));
        assert!(!regs.is_avail(GPR, 29));
        assert!(regs.is_avail(GPR, 32));

        assert_eq!(regs.iter(GPR).count(), 7);
        assert_eq!(regs.iter(DPR).collect::<Vec<_>>(), [30, 33, 35]);
    }

    #[test]
    fn interference() {
        let mut regs1 = RegisterSet::new();
        let mut regs2 = RegisterSet::new();

        assert!(!regs1.interferes_with(&regs2));
        regs1.take(&GPR, 32);
        assert!(!regs1.interferes_with(&regs2));
        regs2.take(&GPR, 31);
        assert!(!regs1.interferes_with(&regs2));
        regs1.intersect(&regs2);
        assert!(regs1.interferes_with(&regs2));
    }
}
<|endoftext|>


<commit_before>mod render;

#[cfg(test)]
mod tests;

use std::iter;

use either::Either;
use hir::{HasSource, Semantics};
use ide_db::{
    base_db::FileRange,
    defs::{Definition, IdentClass},
    famous_defs::FamousDefs,
    helpers::pick_best_token,
    FxIndexSet, RootDatabase,
};
use itertools::Itertools;
use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxNode, SyntaxToken, T};

use crate::{
    doc_links::token_as_doc_comment,
    markup::Markup,
    runnables::{runnable_fn, runnable_mod},
    FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, TryToNav,
};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HoverConfig {
    pub links_in_hover: bool,
    pub documentation: Option<HoverDocFormat>,
}

impl HoverConfig {
    fn markdown(&self) -> bool {
        matches!(self.documentation, Some(HoverDocFormat::Markdown))
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum HoverDocFormat {
    Markdown,
    PlainText,
}

#[derive(Debug, Clone)]
pub enum HoverAction {
    Runnable(Runnable),
    Implementation(FilePosition),
    Reference(FilePosition),
    GoToType(Vec<HoverGotoTypeData>),
}

impl HoverAction {
    fn goto_type_from_targets(db: &RootDatabase, targets: Vec<hir::ModuleDef>) -> Self {
        let targets = targets
            .into_iter()
            .filter_map(|it| {
                Some(HoverGotoTypeData {
                    mod_path: render::path(
                        db,
                        it.module(db)?,
                        it.name(db).map(|name| name.to_string()),
                    ),
                    nav: it.try_to_nav(db)?,
                })
            })
            .collect();
        HoverAction::GoToType(targets)
    }
}

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct HoverGotoTypeData {
    pub mod_path: String,
    pub nav: NavigationTarget,
}

/// Contains the results when hovering over an item
#[derive(Debug, Default)]
pub struct HoverResult {
    pub markup: Markup,
    pub actions: Vec<HoverAction>,
}

// Feature: Hover
//
// Shows additional information, like the type of an expression or the documentation for a definition when "focusing" code.
// Focusing is usually hovering with a mouse, but can also be triggered with a shortcut.
//
// image::https://user-images.githubusercontent.com/48062697/113020658-b5f98b80-917a-11eb-9f88-3dbc27320c95.gif[]
pub(crate) fn hover(
    db: &RootDatabase,
    FileRange { file_id, range }: FileRange,
    config: &HoverConfig,
) -> Option<RangeInfo<HoverResult>> {
    let sema = &hir::Semantics::new(db);
    let file = sema.parse(file_id).syntax().clone();

    if !range.is_empty() {
        return hover_ranged(&file, range, sema, config);
    }
    let offset = range.start();

    let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
        IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] | T![Self] => 3,
        T!['('] | T![')'] => 2,
        kind if kind.is_trivia() => 0,
        _ => 1,
    })?;

    if let Some(doc_comment) = token_as_doc_comment(&original_token) {
        cov_mark::hit!(no_highlight_on_comment_hover);
        return doc_comment.get_definition_with_descend_at(sema, offset, |def, node, range| {
            let res = hover_for_definition(sema, file_id, def, &node, config)?;
            Some(RangeInfo::new(range, res))
        });
    }

    let in_attr = matches!(original_token.parent().and_then(ast::TokenTree::cast), Some(tt) if tt.syntax().ancestors().any(|it| ast::Meta::can_cast(it.kind())));
    let descended = if in_attr {
        [sema.descend_into_macros_with_kind_preference(original_token.clone())].into()
    } else {
        sema.descend_into_macros_with_same_text(original_token.clone())
    };

    // FIXME: Definition should include known lints and the like instead of having this special case here
    let hovered_lint = descended.iter().find_map(|token| {
        let attr = token.parent_ancestors().find_map(ast::Attr::cast)?;
        render::try_for_lint(&attr, token)
    });
    if let Some(res) = hovered_lint {
        return Some(RangeInfo::new(original_token.text_range(), res));
    }

    let result = descended
        .iter()
        .filter_map(|token| {
            let node = token.parent()?;
            let class = IdentClass::classify_token(sema, token)?;
            Some(class.definitions().into_iter().zip(iter::once(node).cycle()))
        })
        .flatten()
        .unique_by(|&(def, _)| def)
        .filter_map(|(def, node)| hover_for_definition(sema, file_id, def, &node, config))
        .reduce(|mut acc: HoverResult, HoverResult { markup, actions }| {
            acc.actions.extend(actions);
            acc.markup = Markup::from(format!("{}\n---\n{}", acc.markup, markup));
            acc
        });

    if result.is_none() {
        // fallbacks, show keywords or types

        let res = descended.iter().find_map(|token| render::keyword(sema, config, token));
        if let Some(res) = res {
            return Some(RangeInfo::new(original_token.text_range(), res));
        }
        let res = descended
            .iter()
            .find_map(|token| hover_type_fallback(sema, config, token, &original_token));
        if let Some(_) = res {
            return res;
        }
    }
    result.map(|mut res: HoverResult| {
        res.actions = dedupe_or_merge_hover_actions(res.actions);
        RangeInfo::new(original_token.text_range(), res)
    })
}

pub(crate) fn hover_for_definition(
    sema: &Semantics<'_, RootDatabase>,
    file_id: FileId,
    definition: Definition,
    node: &SyntaxNode,
    config: &HoverConfig,
) -> Option<HoverResult> {
    let famous_defs = match &definition {
        Definition::BuiltinType(_) => Some(FamousDefs(sema, sema.scope(node)?.krate())),
        _ => None,
    };
    render::definition(sema.db, definition, famous_defs.as_ref(), config).map(|markup| {
        HoverResult {
            markup: render::process_markup(sema.db, definition, &markup, config),
            actions: show_implementations_action(sema.db, definition)
                .into_iter()
                .chain(show_fn_references_action(sema.db, definition))
                .chain(runnable_action(sema, definition, file_id))
                .chain(goto_type_action_for_def(sema.db, definition))
                .collect(),
        }
    })
}

fn hover_ranged(
    file: &SyntaxNode,
    range: syntax::TextRange,
    sema: &Semantics<'_, RootDatabase>,
    config: &HoverConfig,
) -> Option<RangeInfo<HoverResult>> {
    // FIXME: make this work in attributes
    let expr_or_pat = file.covering_element(range).ancestors().find_map(|it| {
        match_ast! {
            match it {
                ast::Expr(expr) => Some(Either::Left(expr)),
                ast::Pat(pat) => Some(Either::Right(pat)),
                _ => None,
            }
        }
    })?;
    let res = match &expr_or_pat {
        Either::Left(ast::Expr::TryExpr(try_expr)) => render::try_expr(sema, config, try_expr),
        Either::Left(ast::Expr::PrefixExpr(prefix_expr))
            if prefix_expr.op_kind() == Some(ast::UnaryOp::Deref) =>
        {
            render::deref_expr(sema, config, prefix_expr)
        }
        _ => None,
    };
    let res = res.or_else(|| render::type_info(sema, config, &expr_or_pat));
    res.map(|it| {
        let range = match expr_or_pat {
            Either::Left(it) => it.syntax().text_range(),
            Either::Right(it) => it.syntax().text_range(),
        };
        RangeInfo::new(range, it)
    })
}

fn hover_type_fallback(
    sema: &Semantics<'_, RootDatabase>,
    config: &HoverConfig,
    token: &SyntaxToken,
    original_token: &SyntaxToken,
) -> Option<RangeInfo<HoverResult>> {
    let node = token
        .parent_ancestors()
        .take_while(|it| !ast::Item::can_cast(it.kind()))
        .find(|n| ast::Expr::can_cast(n.kind()) || ast::Pat::can_cast(n.kind()))?;

    let expr_or_pat = match_ast! {
        match node {
            ast::Expr(it) => Either::Left(it),
            ast::Pat(it) => Either::Right(it),
            // If this node is a MACRO_CALL, it means that `descend_into_macros_many` failed to resolve.
            // (e.g expanding a builtin macro). So we give up here.
            ast::MacroCall(_it) => return None,
            _ => return None,
        }
    };

    let res = render::type_info(sema, config, &expr_or_pat)?;
    let range = sema
        .original_range_opt(&node)
        .map(|frange| frange.range)
        .unwrap_or_else(|| original_token.text_range());
    Some(RangeInfo::new(range, res))
}

fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
    fn to_action(nav_target: NavigationTarget) -> HoverAction {
        HoverAction::Implementation(FilePosition {
            file_id: nav_target.file_id,
            offset: nav_target.focus_or_full_range().start(),
        })
    }

    let adt = match def {
        Definition::Trait(it) => return it.try_to_nav(db).map(to_action),
        Definition::Adt(it) => Some(it),
        Definition::SelfType(it) => it.self_ty(db).as_adt(),
        _ => None,
    }?;
    adt.try_to_nav(db).map(to_action)
}

fn show_fn_references_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
    match def {
        Definition::Function(it) => it.try_to_nav(db).map(|nav_target| {
            HoverAction::Reference(FilePosition {
                file_id: nav_target.file_id,
                offset: nav_target.focus_or_full_range().start(),
            })
        }),
        _ => None,
    }
}

fn runnable_action(
    sema: &hir::Semantics<'_, RootDatabase>,
    def: Definition,
    file_id: FileId,
) -> Option<HoverAction> {
    match def {
        Definition::Module(it) => runnable_mod(sema, it).map(HoverAction::Runnable),
        Definition::Function(func) => {
            let src = func.source(sema.db)?;
            if src.file_id != file_id.into() {
                cov_mark::hit!(hover_macro_generated_struct_fn_doc_comment);
                cov_mark::hit!(hover_macro_generated_struct_fn_doc_attr);
                return None;
            }

            runnable_fn(sema, func).map(HoverAction::Runnable)
        }
        _ => None,
    }
}

fn goto_type_action_for_def(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
    let mut targets: Vec<hir::ModuleDef> = Vec::new();
    let mut push_new_def = |item: hir::ModuleDef| {
        if !targets.contains(&item) {
            targets.push(item);
        }
    };

    if let Definition::GenericParam(hir::GenericParam::TypeParam(it)) = def {
        it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into()));
    } else {
        let ty = match def {
            Definition::Local(it) => it.ty(db),
            Definition::GenericParam(hir::GenericParam::ConstParam(it)) => it.ty(db),
            Definition::Field(field) => field.ty(db),
            Definition::Function(function) => function.ret_type(db),
            _ => return None,
        };

        walk_and_push_ty(db, &ty, &mut push_new_def);
    }

    Some(HoverAction::goto_type_from_targets(db, targets))
}

fn walk_and_push_ty(
    db: &RootDatabase,
    ty: &hir::Type,
    push_new_def: &mut dyn FnMut(hir::ModuleDef),
) {
    ty.walk(db, |t| {
        if let Some(adt) = t.as_adt() {
            push_new_def(adt.into());
        } else if let Some(trait_) = t.as_dyn_trait() {
            push_new_def(trait_.into());
        } else if let Some(traits) = t.as_impl_traits(db) {
            traits.for_each(|it| push_new_def(it.into()));
        } else if let Some(trait_) = t.as_associated_type_parent_trait(db) {
            push_new_def(trait_.into());
        }
    });
}

fn dedupe_or_merge_hover_actions(actions: Vec<HoverAction>) -> Vec<HoverAction> {
    let mut deduped_actions = Vec::with_capacity(actions.len());
    let mut go_to_type_targets = FxIndexSet::default();

    let mut seen_implementation = false;
    let mut seen_reference = false;
    let mut seen_runnable = false;
    for action in actions {
        match action {
            HoverAction::GoToType(targets) => {
                go_to_type_targets.extend(targets);
            }
            HoverAction::Implementation(..) => {
                if !seen_implementation {
                    seen_implementation = true;
                    deduped_actions.push(action);
                }
            }
            HoverAction::Reference(..) => {
                if !seen_reference {
                    seen_reference = true;
                    deduped_actions.push(action);
                }
            }
            HoverAction::Runnable(..) => {
                if !seen_runnable {
                    seen_runnable = true;
                    deduped_actions.push(action);
                }
            }
        };
    }

    if !go_to_type_targets.is_empty() {
        deduped_actions.push(HoverAction::GoToType(go_to_type_targets.into_iter().collect()));
    }

    deduped_actions
}
<commit_msg>Auto merge of #12939 - jean-santos:errors-ide-hover-wrong-place, r=Veykril<commit_after>mod render;

#[cfg(test)]
mod tests;

use std::iter;

use either::Either;
use hir::{HasSource, Semantics};
use ide_db::{
    base_db::FileRange,
    defs::{Definition, IdentClass},
    famous_defs::FamousDefs,
    helpers::pick_best_token,
    FxIndexSet, RootDatabase,
};
use itertools::Itertools;
use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxNode, SyntaxToken, T};

use crate::{
    doc_links::token_as_doc_comment,
    markup::Markup,
    runnables::{runnable_fn, runnable_mod},
    FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, TryToNav,
};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HoverConfig {
    pub links_in_hover: bool,
    pub documentation: Option<HoverDocFormat>,
}

impl HoverConfig {
    fn markdown(&self) -> bool {
        matches!(self.documentation, Some(HoverDocFormat::Markdown))
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum HoverDocFormat {
    Markdown,
    PlainText,
}

#[derive(Debug, Clone)]
pub enum HoverAction {
    Runnable(Runnable),
    Implementation(FilePosition),
    Reference(FilePosition),
    GoToType(Vec<HoverGotoTypeData>),
}

impl HoverAction {
    fn goto_type_from_targets(db: &RootDatabase, targets: Vec<hir::ModuleDef>) -> Self {
        let targets = targets
            .into_iter()
            .filter_map(|it| {
                Some(HoverGotoTypeData {
                    mod_path: render::path(
                        db,
                        it.module(db)?,
                        it.name(db).map(|name| name.to_string()),
                    ),
                    nav: it.try_to_nav(db)?,
                })
            })
            .collect();
        HoverAction::GoToType(targets)
    }
}

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct HoverGotoTypeData {
    pub mod_path: String,
    pub nav: NavigationTarget,
}

/// Contains the results when hovering over an item
#[derive(Debug, Default)]
pub struct HoverResult {
    pub markup: Markup,
    pub actions: Vec<HoverAction>,
}

// Feature: Hover
//
// Shows additional information, like the type of an expression or the documentation for a definition when "focusing" code.
// Focusing is usually hovering with a mouse, but can also be triggered with a shortcut.
//
// image::https://user-images.githubusercontent.com/48062697/113020658-b5f98b80-917a-11eb-9f88-3dbc27320c95.gif[]
pub(crate) fn hover(
    db: &RootDatabase,
    FileRange { file_id, range }: FileRange,
    config: &HoverConfig,
) -> Option<RangeInfo<HoverResult>> {
    let sema = &hir::Semantics::new(db);
    let file = sema.parse(file_id).syntax().clone();

    if !range.is_empty() {
        return hover_ranged(&file, range, sema, config);
    }
    let offset = range.start();

    let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
        IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] | T![Self] => 3,
        T!['('] | T![')'] => 2,
        kind if kind.is_trivia() => 0,
        _ => 1,
    })?;

    if let Some(doc_comment) = token_as_doc_comment(&original_token) {
        cov_mark::hit!(no_highlight_on_comment_hover);
        return doc_comment.get_definition_with_descend_at(sema, offset, |def, node, range| {
            let res = hover_for_definition(sema, file_id, def, &node, config)?;
            Some(RangeInfo::new(range, res))
        });
    }

    let in_attr = matches!(original_token.parent().and_then(ast::TokenTree::cast), Some(tt) if tt.syntax().ancestors().any(|it| ast::Meta::can_cast(it.kind())));
    let descended = if in_attr {
        [sema.descend_into_macros_with_kind_preference(original_token.clone())].into()
    } else {
        sema.descend_into_macros_with_same_text(original_token.clone())
    };

    // FIXME: Definition should include known lints and the like instead of having this special case here
    let hovered_lint = descended.iter().find_map(|token| {
        let attr = token.parent_ancestors().find_map(ast::Attr::cast)?;
        render::try_for_lint(&attr, token)
    });
    if let Some(res) = hovered_lint {
        return Some(RangeInfo::new(original_token.text_range(), res));
    }

    let result = descended
        .iter()
        .filter_map(|token| {
            let node = token.parent()?;
            let class = IdentClass::classify_token(sema, token)?;
            Some(class.definitions().into_iter().zip(iter::once(node).cycle()))
        })
        .flatten()
        .unique_by(|&(def, _)| def)
        .filter_map(|(def, node)| hover_for_definition(sema, file_id, def, &node, config))
        .reduce(|mut acc: HoverResult, HoverResult { markup, actions }| {
            acc.actions.extend(actions);
            acc.markup = Markup::from(format!("{}\n---\n{}", acc.markup, markup));
            acc
        });

    if result.is_none() {
        // fallbacks, show keywords or types

        let res = descended.iter().find_map(|token| render::keyword(sema, config, token));
        if let Some(res) = res {
            return Some(RangeInfo::new(original_token.text_range(), res));
        }
        let res = descended
            .iter()
            .find_map(|token| hover_type_fallback(sema, config, token, &original_token));
        if let Some(_) = res {
            return res;
        }
    }
    result.map(|mut res: HoverResult| {
        res.actions = dedupe_or_merge_hover_actions(res.actions);
        RangeInfo::new(original_token.text_range(), res)
    })
}

pub(crate) fn hover_for_definition(
    sema: &Semantics<'_, RootDatabase>,
    file_id: FileId,
    definition: Definition,
    node: &SyntaxNode,
    config: &HoverConfig,
) -> Option<HoverResult> {
    let famous_defs = match &definition {
        Definition::BuiltinType(_) => Some(FamousDefs(sema, sema.scope(node)?.krate())),
        _ => None,
    };
    render::definition(sema.db, definition, famous_defs.as_ref(), config).map(|markup| {
        HoverResult {
            markup: render::process_markup(sema.db, definition, &markup, config),
            actions: show_implementations_action(sema.db, definition)
                .into_iter()
                .chain(show_fn_references_action(sema.db, definition))
                .chain(runnable_action(sema, definition, file_id))
                .chain(goto_type_action_for_def(sema.db, definition))
                .collect(),
        }
    })
}

fn hover_ranged(
    file: &SyntaxNode,
    range: syntax::TextRange,
    sema: &Semantics<'_, RootDatabase>,
    config: &HoverConfig,
) -> Option<RangeInfo<HoverResult>> {
    // FIXME: make this work in attributes
    let expr_or_pat = file.covering_element(range).ancestors().find_map(|it| {
        match_ast! {
            match it {
                ast::Expr(expr) => Some(Either::Left(expr)),
                ast::Pat(pat) => Some(Either::Right(pat)),
                _ => None,
            }
        }
    })?;
    let res = match &expr_or_pat {
        Either::Left(ast::Expr::TryExpr(try_expr)) => render::try_expr(sema, config, try_expr),
        Either::Left(ast::Expr::PrefixExpr(prefix_expr))
            if prefix_expr.op_kind() == Some(ast::UnaryOp::Deref) =>
        {
            render::deref_expr(sema, config, prefix_expr)
        }
        _ => None,
    };
    let res = res.or_else(|| render::type_info(sema, config, &expr_or_pat));
    res.map(|it| {
        let range = match expr_or_pat {
            Either::Left(it) => it.syntax().text_range(),
            Either::Right(it) => it.syntax().text_range(),
        };
        RangeInfo::new(range, it)
    })
}

fn hover_type_fallback(
    sema: &Semantics<'_, RootDatabase>,
    config: &HoverConfig,
    token: &SyntaxToken,
    original_token: &SyntaxToken,
) -> Option<RangeInfo<HoverResult>> {
    let node =
        token.parent_ancestors().take_while(|it| !ast::Item::can_cast(it.kind())).find(|n| {
            ast::Expr::can_cast(n.kind())
                || ast::Pat::can_cast(n.kind())
                || ast::Type::can_cast(n.kind())
        })?;

    let expr_or_pat = match_ast! {
        match node {
            ast::Expr(it) => Either::Left(it),
            ast::Pat(it) => Either::Right(it),
            // If this node is a MACRO_CALL, it means that `descend_into_macros_many` failed to resolve.
            // (e.g expanding a builtin macro). So we give up here.
            ast::MacroCall(_it) => return None,
            _ => return None,
        }
    };

    let res = render::type_info(sema, config, &expr_or_pat)?;
    let range = sema
        .original_range_opt(&node)
        .map(|frange| frange.range)
        .unwrap_or_else(|| original_token.text_range());
    Some(RangeInfo::new(range, res))
}

fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
    fn to_action(nav_target: NavigationTarget) -> HoverAction {
        HoverAction::Implementation(FilePosition {
            file_id: nav_target.file_id,
            offset: nav_target.focus_or_full_range().start(),
        })
    }

    let adt = match def {
        Definition::Trait(it) => return it.try_to_nav(db).map(to_action),
        Definition::Adt(it) => Some(it),
        Definition::SelfType(it) => it.self_ty(db).as_adt(),
        _ => None,
    }?;
    adt.try_to_nav(db).map(to_action)
}

fn show_fn_references_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
    match def {
        Definition::Function(it) => it.try_to_nav(db).map(|nav_target| {
            HoverAction::Reference(FilePosition {
                file_id: nav_target.file_id,
                offset: nav_target.focus_or_full_range().start(),
            })
        }),
        _ => None,
    }
}

fn runnable_action(
    sema: &hir::Semantics<'_, RootDatabase>,
    def: Definition,
    file_id: FileId,
) -> Option<HoverAction> {
    match def {
        Definition::Module(it) => runnable_mod(sema, it).map(HoverAction::Runnable),
        Definition::Function(func) => {
            let src = func.source(sema.db)?;
            if src.file_id != file_id.into() {
                cov_mark::hit!(hover_macro_generated_struct_fn_doc_comment);
                cov_mark::hit!(hover_macro_generated_struct_fn_doc_attr);
                return None;
            }

            runnable_fn(sema, func).map(HoverAction::Runnable)
        }
        _ => None,
    }
}

fn goto_type_action_for_def(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
    let mut targets: Vec<hir::ModuleDef> = Vec::new();
    let mut push_new_def = |item: hir::ModuleDef| {
        if !targets.contains(&item) {
            targets.push(item);
        }
    };

    if let Definition::GenericParam(hir::GenericParam::TypeParam(it)) = def {
        it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into()));
    } else {
        let ty = match def {
            Definition::Local(it) => it.ty(db),
            Definition::GenericParam(hir::GenericParam::ConstParam(it)) => it.ty(db),
            Definition::Field(field) => field.ty(db),
            Definition::Function(function) => function.ret_type(db),
            _ => return None,
        };

        walk_and_push_ty(db, &ty, &mut push_new_def);
    }

    Some(HoverAction::goto_type_from_targets(db, targets))
}

fn walk_and_push_ty(
    db: &RootDatabase,
    ty: &hir::Type,
    push_new_def: &mut dyn FnMut(hir::ModuleDef),
) {
    ty.walk(db, |t| {
        if let Some(adt) = t.as_adt() {
            push_new_def(adt.into());
        } else if let Some(trait_) = t.as_dyn_trait() {
            push_new_def(trait_.into());
        } else if let Some(traits) = t.as_impl_traits(db) {
            traits.for_each(|it| push_new_def(it.into()));
        } else if let Some(trait_) = t.as_associated_type_parent_trait(db) {
            push_new_def(trait_.into());
        }
    });
}

fn dedupe_or_merge_hover_actions(actions: Vec<HoverAction>) -> Vec<HoverAction> {
    let mut deduped_actions = Vec::with_capacity(actions.len());
    let mut go_to_type_targets = FxIndexSet::default();

    let mut seen_implementation = false;
    let mut seen_reference = false;
    let mut seen_runnable = false;
    for action in actions {
        match action {
            HoverAction::GoToType(targets) => {
                go_to_type_targets.extend(targets);
            }
            HoverAction::Implementation(..) => {
                if !seen_implementation {
                    seen_implementation = true;
                    deduped_actions.push(action);
                }
            }
            HoverAction::Reference(..) => {
                if !seen_reference {
                    seen_reference = true;
                    deduped_actions.push(action);
                }
            }
            HoverAction::Runnable(..) => {
                if !seen_runnable {
                    seen_runnable = true;
                    deduped_actions.push(action);
                }
            }
        };
    }

    if !go_to_type_targets.is_empty() {
        deduped_actions.push(HoverAction::GoToType(go_to_type_targets.into_iter().collect()));
    }

    deduped_actions
}
<|endoftext|>


<commit_before>#![allow(non_camel_case_types)]

extern crate libc;

pub use self::libc::{c_char, c_int, c_void, uint32_t};

pub mod protocol;
pub mod util;

#[repr(C)]
pub struct wl_list {
    pub prev: *mut wl_list,
    pub next: *mut wl_list,
}

pub type wl_compositor = c_void;
pub type wl_display = c_void;
pub type wl_event_queue = c_void;
pub type wl_proxy = c_void;
pub type wl_registry = c_void;

#[link(name = "wayland-client")]
extern {
    pub static wl_registry_interface: util::wl_interface;

    pub fn wl_display_connect(name: *const c_char) -> *mut wl_display;
    pub fn wl_display_disconnect(display: *mut wl_display);
    pub fn wl_display_dispatch(display: *mut wl_display) -> c_int;
    pub fn wl_display_dispatch_queue(display: *mut wl_display,
                                     queue: *mut wl_event_queue) -> c_int;
    pub fn wl_display_dispatch_queue_pending(display: *mut wl_display,
                                             queue: *mut wl_event_queue)
                                             -> c_int;
    pub fn wl_display_dispatch_pending(display: *mut wl_display) -> c_int;
    pub fn wl_display_get_error(display: *mut wl_display) -> c_int;
    pub fn wl_display_flush(display: *mut wl_display) -> c_int;
    pub fn wl_display_roundtrip_queue(display: *mut wl_display,
                                      queue: *mut wl_event_queue) -> c_int;
    pub fn wl_display_roundtrip(display: *mut wl_display) -> c_int;
    pub fn wl_proxy_add_listener(
        proxy: *mut wl_proxy,
        implementation: *mut extern fn(c_void) -> c_void,
        data: *mut c_void) -> c_int;
    pub fn wl_proxy_destroy(proxy: *mut wl_proxy);
    pub fn wl_proxy_marshal_constructor(proxy: *mut wl_proxy,
                                        opcode: uint32_t,
                                        interface: *const util::wl_interface,
                                        ...) -> *mut wl_proxy;

    pub fn wl_list_init(list: *mut wl_list);
    pub fn wl_list_insert(list: *mut wl_list, elm: *mut wl_list);
    pub fn wl_list_remove(elm: *mut wl_list);
    pub fn wl_list_length(elm: *const wl_list) -> c_int;
    pub fn wl_list_empty(elm: *const wl_list) -> c_int;
    pub fn wl_list_insert_list(list: *mut wl_list, other: *mut wl_list);
}
<commit_msg>Changed the formatting of multiline function declarations<commit_after>#![allow(non_camel_case_types)]

extern crate libc;

pub use self::libc::{c_char, c_int, c_void, uint32_t};

pub mod protocol;
pub mod util;

#[repr(C)]
pub struct wl_list {
    pub prev: *mut wl_list,
    pub next: *mut wl_list,
}

pub type wl_compositor = c_void;
pub type wl_display = c_void;
pub type wl_event_queue = c_void;
pub type wl_proxy = c_void;
pub type wl_registry = c_void;

#[link(name = "wayland-client")]
extern {
    pub static wl_registry_interface: util::wl_interface;

    pub fn wl_display_connect(name: *const c_char) -> *mut wl_display;
    pub fn wl_display_disconnect(display: *mut wl_display);
    pub fn wl_display_dispatch(display: *mut wl_display) -> c_int;
    pub fn wl_display_dispatch_queue(
        display: *mut wl_display,
        queue: *mut wl_event_queue
    ) -> c_int;
    pub fn wl_display_dispatch_queue_pending(
        display: *mut wl_display,
        queue: *mut wl_event_queue
    ) -> c_int;
    pub fn wl_display_dispatch_pending(display: *mut wl_display) -> c_int;
    pub fn wl_display_get_error(display: *mut wl_display) -> c_int;
    pub fn wl_display_flush(display: *mut wl_display) -> c_int;
    pub fn wl_display_roundtrip_queue(
        display: *mut wl_display,
        queue: *mut wl_event_queue
    ) -> c_int;
    pub fn wl_display_roundtrip(display: *mut wl_display) -> c_int;
    pub fn wl_proxy_add_listener(
        proxy: *mut wl_proxy,
        implementation: *mut extern fn(c_void) -> c_void,
        data: *mut c_void
    ) -> c_int;
    pub fn wl_proxy_destroy(proxy: *mut wl_proxy);
    pub fn wl_proxy_marshal_constructor(
        proxy: *mut wl_proxy,
        opcode: uint32_t,
        interface: *const util::wl_interface,
        ...
    ) -> *mut wl_proxy;
    pub fn wl_list_init(list: *mut wl_list);
    pub fn wl_list_insert(list: *mut wl_list, elm: *mut wl_list);
    pub fn wl_list_remove(elm: *mut wl_list);
    pub fn wl_list_length(elm: *const wl_list) -> c_int;
    pub fn wl_list_empty(elm: *const wl_list) -> c_int;
    pub fn wl_list_insert_list(list: *mut wl_list, other: *mut wl_list);
}
<|endoftext|>