<commit_before>// Copyright 2021 Twitter, Inc.
// Licensed under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0

use crate::codec::*;
use crate::config::*;
use crate::config_file::{Protocol, Verb};
use crate::*;

use rand::rngs::SmallRng;
use rand::SeedableRng;

use std::cmp::Ordering;
use std::io::{BufRead, BufReader, Write};
use std::str;

pub enum Mode {
    Inline,
    Resp,
}

pub struct Redis {
    config: Arc<Config>,
    mode: Mode,
    rng: SmallRng,
}

impl Redis {
    pub fn new(config: Arc<Config>) -> Self {
        let mode = match config.general().protocol() {
            Protocol::Redis | Protocol::RedisInline => Mode::Inline,
            Protocol::RedisResp => Mode::Resp,
            unknown => {
                fatal!("protocol: {:?} is not a redis protocol", unknown);
            }
        };
        Self {
            config,
            mode,
            rng: SmallRng::from_entropy(),
        }
    }

    fn command(buf: &mut Session, mode: &Mode, command: &str, args: Vec<Vec<u8>>) {
        match mode {
            Mode::Inline => {
                let _ = buf.write_all(command.to_string().as_bytes());
                for arg in args {
                    let _ = buf.write_all(b" ");
                    let _ = buf.write_all(&arg);
                }
                let _ = buf.write_all(b"\r\n");
            }
            Mode::Resp => {
                let _ = buf.write_all(
                    format!("*{}\r\n${}\r\n{}", 1 + args.len(), command.len(), command).as_bytes(),
                );
                for arg in args {
                    let _ = buf.write_all(format!("\r\n${}\r\n", arg.len()).as_bytes());
                    let _ = buf.write_all(&arg);
                }
                let _ = buf.write_all(b"\r\n");
            }
        }
    }

    fn get(rng: &mut SmallRng, mode: &Mode, keyspace: &Keyspace, buf: &mut Session) {
        let args = vec![keyspace.generate_key(rng)];
        Redis::command(buf, mode, "get", args);
    }

    fn mget(rng: &mut SmallRng, mode: &Mode, keyspace: &Keyspace, buf: &mut Session) {
        let mut args = Vec::new();
        for _ in 0..keyspace.batch_size() {
            args.push(keyspace.generate_key(rng));
        }
        Redis::command(buf, mode, "mget", args);
    }

    fn set(rng: &mut SmallRng, mode: &Mode, keyspace: &Keyspace, buf: &mut Session) {
        let command = "set";
        let mut args = vec![
            keyspace.generate_key(rng),
            keyspace.generate_value(rng).unwrap_or_else(|| b"".to_vec()),
        ];
        let ttl = keyspace.ttl();
        if ttl != 0 {
            args.push(b"EX".to_vec());
            args.push(format!("{}", ttl).as_bytes().to_vec());
        }
        Redis::command(buf, mode, command, args);
    }

    fn del(rng: &mut SmallRng, mode: &Mode, keyspace: &Keyspace, buf: &mut Session) {
        let args = vec![keyspace.generate_key(rng)];
        Redis::command(buf, mode, "del", args);
    }

    fn hget(rng: &mut SmallRng, mode: &Mode, keyspace: &Keyspace, buf: &mut Session) {
        let command = "hget";
        let args = vec![
            keyspace.generate_key(rng),
            keyspace
                .generate_inner_key(rng)
                .unwrap_or_else(|| b"".to_vec()),
        ];
        Redis::command(buf, mode, command, args);
    }

    fn hset(rng: &mut SmallRng, mode: &Mode, keyspace: &Keyspace, buf: &mut Session) {
        let command = "hset";
        let args = vec![
            keyspace.generate_key(rng),
            keyspace
                .generate_inner_key(rng)
                .unwrap_or_else(|| b"".to_vec()),
            keyspace.generate_value(rng).unwrap_or_else(|| b"".to_vec()),
        ];
        Redis::command(buf, mode, command, args);
    }

    fn hsetnx(rng: &mut SmallRng, mode: &Mode, keyspace: &Keyspace, buf: &mut Session) {
        let command = "hsetnx";
        let args = vec![
            keyspace.generate_key(rng),
            keyspace
                .generate_inner_key(rng)
                .unwrap_or_else(|| b"".to_vec()),
            keyspace.generate_value(rng).unwrap_or_else(|| b"".to_vec()),
        ];
        Redis::command(buf, mode, command, args);
    }

    fn hdel(rng: &mut SmallRng, mode: &Mode, keyspace: &Keyspace, buf: &mut Session) {
        let command = "hdel";
        let args = vec![
            keyspace.generate_key(rng),
            keyspace
                .generate_inner_key(rng)
                .unwrap_or_else(|| b"".to_vec()),
        ];
        Redis::command(buf, mode, command, args);
    }
}

impl Codec for Redis {
    fn encode(&mut self, buf: &mut Session) {
        let keyspace = self.config.choose_keyspace(&mut self.rng);
        let command = keyspace.choose_command(&mut self.rng);
        match command.verb() {
            Verb::Get => {
                metrics::REQUEST_GET.increment();
                if keyspace.batch_size() == 1 {
                    Self::get(&mut self.rng, &self.mode, keyspace, buf)
                } else {
                    Self::mget(&mut self.rng, &self.mode, keyspace, buf)
                }
            }
            Verb::Set => Self::set(&mut self.rng, &self.mode, keyspace, buf),
            Verb::Delete => Self::del(&mut self.rng, &self.mode, keyspace, buf),
            Verb::Hget => {
                metrics::REQUEST_GET.increment();
                Self::hget(&mut self.rng, &self.mode, keyspace, buf)
            }
            Verb::Hset => Self::hset(&mut self.rng, &self.mode, keyspace, buf),
            Verb::Hsetnx => Self::hsetnx(&mut self.rng, &self.mode, keyspace, buf),
            Verb::Hdel => Self::hdel(&mut self.rng, &self.mode, keyspace, buf),
            _ => {
                unimplemented!()
            }
        }
    }

    fn decode(&self, buffer: &mut Session) -> Result<(), ParseError> {
        // no-copy borrow as a slice
        let buf: &[u8] = (*buffer).buffer();

        let end = &buf[buf.len() - 2..buf.len()];

        // All complete responses end in CRLF
        if end != b"\r\n" {
            return Err(ParseError::Incomplete);
        }

        let first_char = &buf[0..1];
        match str::from_utf8(first_char) {
            Ok("+") => {
                // simple string
                if buf.len() < 5 {
                    Err(ParseError::Incomplete)
                } else {
                    let msg = &buf[1..buf.len() - 2];
                    match str::from_utf8(msg) {
                        Ok("OK") | Ok("PONG") => {
                            let response_end = buf.len();
                            let _ = buffer.consume(response_end);
                            Ok(())
                        }
                        _ => Err(ParseError::Unknown),
                    }
                }
            }
            Ok("-") => {
                // error response
                Err(ParseError::Error)
            }
            Ok(":") => {
                // numeric response
                let msg = &buf[1..buf.len() - 2];
                match str::from_utf8(msg) {
                    Ok(msg) => match msg.parse::<i64>() {
                        Ok(_) => {
                            let response_end = buf.len();
                            let _ = buffer.consume(response_end);
                            Ok(())
                        }
                        Err(_) => Err(ParseError::Unknown),
                    },
                    Err(_) => Err(ParseError::Unknown),
                }
            }
            Ok("$") => {
                // bulk string
                let msg = &buf[1..buf.len() - 2];
                match str::from_utf8(msg) {
                    Ok("-1") => {
                        let response_end = buf.len();
                        let _ = buffer.consume(response_end);
                        Ok(())
                    }
                    Ok(_) => {
                        let reader = BufReader::new(buf);
                        let mut lines = reader.lines();
                        let mut line = lines.next().unwrap().unwrap();
                        let _ = line.remove(0);
                        match line.parse::<usize>() {
                            Ok(expected) => {
                                // data len = buf.len() - line.len() - 2x CRLF - 1
                                let have = buf.len() - line.len() - 5;
                                match have.cmp(&expected) {
                                    Ordering::Less => Err(ParseError::Incomplete),
                                    Ordering::Equal => {
                                        let response_end = buf.len();
                                        let _ = buffer.consume(response_end);
                                        metrics::RESPONSE_HIT.increment();
                                        Ok(())
                                    }
                                    Ordering::Greater => Err(ParseError::Error),
                                }
                            }
                            Err(_) => Err(ParseError::Unknown),
                        }
                    }
                    Err(_) => Err(ParseError::Unknown),
                }
            }
            Ok("*") => {
                // arrays
                let msg = &buf[1..buf.len() - 2];
                match str::from_utf8(msg) {
                    Ok("-1") => {
                        let response_end = buf.len();
                        let _ = buffer.consume(response_end);
                        Ok(())
                    }
                    Ok(_) => {
                        // TODO: implement array parsing
                        Err(ParseError::Unknown)
                    }
                    Err(_) => Err(ParseError::Unknown),
                }
            }
            _ => Err(ParseError::Unknown),
        }
    }
}
<commit_msg>support pipelined commands for redis (#370)<commit_after>// Copyright 2021 Twitter, Inc.
// Licensed under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0

use crate::codec::*;
use crate::config::*;
use crate::config_file::{Protocol, Verb};
use crate::*;

use rand::rngs::SmallRng;
use rand::SeedableRng;

use std::io::{BufRead, Write};
use std::str;

pub enum Mode {
    Inline,
    Resp,
}

pub struct Redis {
    config: Arc<Config>,
    mode: Mode,
    rng: SmallRng,
}

impl Redis {
    pub fn new(config: Arc<Config>) -> Self {
        let mode = match config.general().protocol() {
            Protocol::Redis | Protocol::RedisInline => Mode::Inline,
            Protocol::RedisResp => Mode::Resp,
            unknown => {
                fatal!("protocol: {:?} is not a redis protocol", unknown);
            }
        };
        Self {
            config,
            mode,
            rng: SmallRng::from_entropy(),
        }
    }

    fn command(buf: &mut Session, mode: &Mode, command: &str, args: Vec<Vec<u8>>) {
        match mode {
            Mode::Inline => {
                let _ = buf.write_all(command.to_string().as_bytes());
                for arg in args {
                    let _ = buf.write_all(b" ");
                    let _ = buf.write_all(&arg);
                }
                let _ = buf.write_all(b"\r\n");
            }
            Mode::Resp => {
                let _ = buf.write_all(
                    format!("*{}\r\n${}\r\n{}", 1 + args.len(), command.len(), command).as_bytes(),
                );
                for arg in args {
                    let _ = buf.write_all(format!("\r\n${}\r\n", arg.len()).as_bytes());
                    let _ = buf.write_all(&arg);
                }
                let _ = buf.write_all(b"\r\n");
            }
        }
    }

    fn get(rng: &mut SmallRng, mode: &Mode, keyspace: &Keyspace, buf: &mut Session) {
        let args = vec![keyspace.generate_key(rng)];
        Redis::command(buf, mode, "get", args);
    }

    fn mget(rng: &mut SmallRng, mode: &Mode, keyspace: &Keyspace, buf: &mut Session) {
        let mut args = Vec::new();
        for _ in 0..keyspace.batch_size() {
            args.push(keyspace.generate_key(rng));
        }
        Redis::command(buf, mode, "mget", args);
    }

    fn set(rng: &mut SmallRng, mode: &Mode, keyspace: &Keyspace, buf: &mut Session) {
        let command = "set";
        let mut args = vec![
            keyspace.generate_key(rng),
            keyspace.generate_value(rng).unwrap_or_else(|| b"".to_vec()),
        ];
        let ttl = keyspace.ttl();
        if ttl != 0 {
            args.push(b"EX".to_vec());
            args.push(format!("{}", ttl).as_bytes().to_vec());
        }
        Redis::command(buf, mode, command, args);
    }

    fn del(rng: &mut SmallRng, mode: &Mode, keyspace: &Keyspace, buf: &mut Session) {
        let args = vec![keyspace.generate_key(rng)];
        Redis::command(buf, mode, "del", args);
    }

    fn hget(rng: &mut SmallRng, mode: &Mode, keyspace: &Keyspace, buf: &mut Session) {
        let command = "hget";
        let args = vec![
            keyspace.generate_key(rng),
            keyspace
                .generate_inner_key(rng)
                .unwrap_or_else(|| b"".to_vec()),
        ];
        Redis::command(buf, mode, command, args);
    }

    fn hset(rng: &mut SmallRng, mode: &Mode, keyspace: &Keyspace, buf: &mut Session) {
        let command = "hset";
        let args = vec![
            keyspace.generate_key(rng),
            keyspace
                .generate_inner_key(rng)
                .unwrap_or_else(|| b"".to_vec()),
            keyspace.generate_value(rng).unwrap_or_else(|| b"".to_vec()),
        ];
        Redis::command(buf, mode, command, args);
    }

    fn hsetnx(rng: &mut SmallRng, mode: &Mode, keyspace: &Keyspace, buf: &mut Session) {
        let command = "hsetnx";
        let args = vec![
            keyspace.generate_key(rng),
            keyspace
                .generate_inner_key(rng)
                .unwrap_or_else(|| b"".to_vec()),
            keyspace.generate_value(rng).unwrap_or_else(|| b"".to_vec()),
        ];
        Redis::command(buf, mode, command, args);
    }

    fn hdel(rng: &mut SmallRng, mode: &Mode, keyspace: &Keyspace, buf: &mut Session) {
        let command = "hdel";
        let args = vec![
            keyspace.generate_key(rng),
            keyspace
                .generate_inner_key(rng)
                .unwrap_or_else(|| b"".to_vec()),
        ];
        Redis::command(buf, mode, command, args);
    }
}

impl Codec for Redis {
    fn encode(&mut self, buf: &mut Session) {
        let keyspace = self.config.choose_keyspace(&mut self.rng);
        let command = keyspace.choose_command(&mut self.rng);
        match command.verb() {
            Verb::Get => {
                metrics::REQUEST_GET.increment();
                if keyspace.batch_size() == 1 {
                    Self::get(&mut self.rng, &self.mode, keyspace, buf)
                } else {
                    Self::mget(&mut self.rng, &self.mode, keyspace, buf)
                }
            }
            Verb::Set => Self::set(&mut self.rng, &self.mode, keyspace, buf),
            Verb::Delete => Self::del(&mut self.rng, &self.mode, keyspace, buf),
            Verb::Hget => {
                metrics::REQUEST_GET.increment();
                Self::hget(&mut self.rng, &self.mode, keyspace, buf)
            }
            Verb::Hset => Self::hset(&mut self.rng, &self.mode, keyspace, buf),
            Verb::Hsetnx => Self::hsetnx(&mut self.rng, &self.mode, keyspace, buf),
            Verb::Hdel => Self::hdel(&mut self.rng, &self.mode, keyspace, buf),
            _ => {
                unimplemented!()
            }
        }
    }

    fn decode(&self, buffer: &mut Session) -> Result<(), ParseError> {
        // no-copy borrow as a slice
        let buf: &[u8] = (*buffer).buffer();

        if buf.len() < 1 {
            return Err(ParseError::Incomplete);
        }

        let first_char = &buf[0..1];
        match str::from_utf8(first_char) {
            Ok("+") => {
                // simple string response
                let mut lines = buf.windows(2);
                if let Some(line_end) = lines.position(|w| w == b"\r\n") {
                    let msg = &buf[1..line_end];
                    match str::from_utf8(msg) {
                        Ok("OK") | Ok("PONG") => {
                            let _ = buffer.consume(line_end + 2);
                            Ok(())
                        }
                        _ => Err(ParseError::Unknown),
                    }
                } else {
                    Err(ParseError::Incomplete)
                }
            }
            Ok("-") => {
                // error response
                Err(ParseError::Error)
            }
            Ok(":") => {
                // numeric response
                let mut lines = buf.windows(2);
                if let Some(line_end) = lines.position(|w| w == b"\r\n") {
                    let msg = &buf[1..line_end];
                    match str::from_utf8(msg) {
                        Ok(msg) => match msg.parse::<i64>() {
                            Ok(_) => {
                                let _ = buffer.consume(line_end + 2);
                                Ok(())
                            }
                            Err(_) => Err(ParseError::Unknown),
                        },
                        Err(_) => Err(ParseError::Unknown),
                    }
                } else {
                    Err(ParseError::Incomplete)
                }
            }
            Ok("$") => {
                // bulk string
                let mut lines = buf.windows(2);
                if let Some(line_end) = lines.position(|w| w == b"\r\n") {
                    let msg = &buf[1..line_end];
                    match str::from_utf8(msg) {
                        Ok("-1") => {
                            let _ = buffer.consume(line_end + 2);
                            Ok(())
                        }
                        Ok(n) => {
                            let len = n.parse::<usize>().map_err(|_| ParseError::Unknown)?;
                            let response_end = len + line_end + 2;
                            if response_end <= buf.len() {
                                metrics::RESPONSE_HIT.increment();
                                let _ = buffer.consume(response_end);
                                Ok(())
                            } else {
                                Err(ParseError::Incomplete)
                            }
                        }
                        Err(_) => Err(ParseError::Unknown),
                    }
                } else {
                    Err(ParseError::Incomplete)
                }
            }
            Ok("*") => {
                // arrays
                let msg = &buf[1..buf.len() - 2];
                match str::from_utf8(msg) {
                    Ok("-1") => {
                        let response_end = buf.len();
                        let _ = buffer.consume(response_end);
                        Ok(())
                    }
                    Ok(_) => {
                        // TODO: implement array parsing
                        Err(ParseError::Unknown)
                    }
                    Err(_) => Err(ParseError::Unknown),
                }
            }
            _ => Err(ParseError::Unknown),
        }
    }
}
<|endoftext|>


<commit_before>use document;
use document::{YamlNode, YamlNodeData};
use ffi::{YamlErrorType, YamlScalarStyle};
use error::{YamlMark, YamlError, YamlErrorContext};

use std::num::FromStrRadix;
use std::f64;
use std::char;
use regex::Regex;

pub trait YamlConstructor<T, E> {
    fn construct_scalar(&self, scalar: document::YamlScalarData) -> Result<T, E>;
    fn construct_sequence(&self, sequence: document::YamlSequenceData) -> Result<T, E>;
    fn construct_mapping(&self, mapping: document::YamlMappingData) -> Result<T, E>;

    fn construct<'r>(&self, node: document::YamlNode<'r>) -> Result<T, E> {
        match node {
            YamlNode::YamlScalarNode(scalar) => self.construct_scalar(scalar),
            YamlNode::YamlSequenceNode(sequence) => self.construct_sequence(sequence),
            YamlNode::YamlMappingNode(mapping) => self.construct_mapping(mapping)
        }
    }
}

#[derive(PartialEq, Debug)]
pub enum YamlStandardData {
    YamlInteger(isize),
    YamlFloat(f64),
    YamlString(String),
    YamlNull,
    YamlBool(bool),
    YamlSequence(Vec<YamlStandardData>),
    YamlMapping(Vec<(YamlStandardData, YamlStandardData)>),
}

#[derive(Clone, Copy)]
pub struct YamlStandardConstructor;

fn standard_error(message: String, mark: &YamlMark) -> YamlError {
    let context = YamlErrorContext {
        byte_offset: mark.index,
        problem_mark: *mark,
        context: None,
        context_mark: *mark,
    };

    YamlError {
        kind: YamlErrorType::YAML_PARSER_ERROR,
        problem: Some(message),
        io_error: None,
        context: Some(context)
    }
}

fn take(iter: &mut Iterator<Item=char>, n: usize) -> String
{
    let mut s = String::new();
    for _ in 0..n {
        match iter.next() {
            Some(c) => s.push(c),
            None => break
        }
    }
    return s;
}

impl YamlStandardConstructor {
    pub fn new() -> YamlStandardConstructor {
        YamlStandardConstructor
    }

    fn parse_double_quoted(value: &str, mark: &YamlMark) -> Result<String, YamlError> {
        let mut buf = String::new();
        let mut it = value.chars();

        loop {
            match it.next() {
                None => return Ok(buf),
                Some('\\') => {
                    // escape sequences
                    match it.next() {
                        None => return Err(standard_error(
                                    "unexpected end of string after escape".to_string(),
                                    mark
                                )),
                        Some('0') => buf.push('\x00'),              // null
                        Some('a') => buf.push('\x07'),              // ASCII bell
                        Some('b') => buf.push('\x08'),              // backspace
                        Some('t') | Some('\t') => buf.push('\t'),   // horizontal tab
                        Some('n') => buf.push('\n'),                // linefeed
                        Some('v') => buf.push('\x0b'),              // vertical tab
                        Some('f') => buf.push('\x0c'),              // form feed
                        Some('r') => buf.push('\x0d'),              // carriage return
                        Some('e') => buf.push('\x1b'),              // ASCII escape
                        Some('N') => buf.push('\u{85}'),            // unicode next line
                        Some('_') => buf.push('\u{a0}'),            // unicode non-breaking space
                        Some('L') => buf.push('\u{2028}'),          // unicode line separator
                        Some('P') => buf.push('\u{2029}'),          // unicode paragraph separator
                        Some('x') => {
                            let code:String = take(&mut it, 2);
                            match parse_escape_sequence(&code[..], 2) {
                                Some(c) => buf.push(c),
                                None => return Err(standard_error(
                                            format!("invalid escape sequence {}", code),
                                            mark
                                        ))
                            }
                        },
                        Some('u') => {
                            let code:String = take(&mut it, 4);
                            match parse_escape_sequence(&code[..], 4) {
                                Some(c) => buf.push(c),
                                None => return Err(standard_error(
                                            format!("invalid escape sequence {}", code),
                                            mark
                                        ))
                            }
                        },
                        Some('U') => {
                            let code:String = take(&mut it, 8);
                            match parse_escape_sequence(&code[..], 8) {
                                Some(c) => buf.push(c),
                                None => return Err(standard_error(
                                            format!("invalid escape sequence {}", code),
                                            mark
                                        ))
                            }
                        },
                        Some(c) => buf.push(c)
                    }
                },
                Some(c) => buf.push(c)
            }
        }
    }
}

fn parse_escape_sequence(rep: &str, expected_len: usize) -> Option<char> {
    match FromStrRadix::from_str_radix(rep, 16) {
        Ok(code) if rep.len() == expected_len => char::from_u32(code),
        _ => None
    }
}

fn parse_int(sign: &str, data: &str, radix: u32) -> isize {
    let sign_flag = if sign == "-" {
            -1
        } else {
            1
        };

    let filtered:String = data.chars().filter(|&c| c != '_').collect();
    let unsigned:isize = FromStrRadix::from_str_radix(&filtered[..], radix).unwrap();
    return unsigned * sign_flag;
}

fn parse_float(sign: &str, data: &str) -> f64 {
    let unsigned:f64 = data.parse().unwrap();
    if sign == "-" {
        return -unsigned;
    } else {
        return unsigned;
    }
}

static DEC_INT:Regex = regex!(r"^[-+]?(0|[1-9][0-9_]*)$");
static OCT_INT:Regex = regex!(r"^([-+]?)0o?([0-7_]+)$");
static HEX_INT:Regex = regex!(r"^([-+]?)0x([0-9a-fA-F_]+)$");
static BIN_INT:Regex = regex!(r"^([-+]?)0b([0-1_]+)$");
static FLOAT_PATTERN:Regex = regex!(r"^([-+]?)(\.[0-9]+|[0-9]+(\.[0-9]*)?([eE][-+]?[0-9]+)?)$");
static POS_INF:Regex = regex!(r"^[+]?(\.inf|\.Inf|\.INF)$");
static NEG_INF:Regex = regex!(r"^-(\.inf|\.Inf|\.INF)$");
static NAN_PATTERN:Regex = regex!(r"^(\.nan|\.NaN|\.NAN)$");
static NULL_PATTERN:Regex = regex!(r"^(null|Null|NULL|~)$");
static TRUE_PATTERN:Regex = regex!(r"^(true|True|TRUE|yes|Yes|YES)$");
static FALSE_PATTERN:Regex = regex!(r"^(false|False|FALSE|no|No|NO)$");

impl YamlConstructor<YamlStandardData, YamlError> for YamlStandardConstructor {
    fn construct_scalar(&self, scalar: document::YamlScalarData) -> Result<YamlStandardData, YamlError> {
        let value = scalar.get_value();
        let mark = scalar.start_mark();

        match scalar.style() {
            YamlScalarStyle::YamlPlainScalarStyle => {
                match BIN_INT.captures(&value[..]) {
                    Some(caps) => return Ok(YamlStandardData::YamlInteger(parse_int(
                                caps.at(1).unwrap(), caps.at(2).unwrap(), 2))),
                    None => ()
                };
                match OCT_INT.captures(&value[..]) {
                    Some(caps) => return Ok(YamlStandardData::YamlInteger(parse_int(
                                caps.at(1).unwrap(), caps.at(2).unwrap(), 8))),
                    None => ()
                };
                match HEX_INT.captures(&value[..]) {
                    Some(caps) => return Ok(YamlStandardData::YamlInteger(parse_int(
                                caps.at(1).unwrap(), caps.at(2).unwrap(), 16))),
                    None => ()
                };

                if DEC_INT.is_match(&value[..]) {
                    return Ok(YamlStandardData::YamlInteger(parse_int("", &value[..], 10)));
                }

                match FLOAT_PATTERN.captures(&value[..]) {
                    Some(caps) => return Ok(YamlStandardData::YamlFloat(parse_float(
                                caps.at(1).unwrap(), caps.at(2).unwrap()))),
                    None => ()
                };

                if POS_INF.is_match(&value[..]) {
                    Ok(YamlStandardData::YamlFloat(f64::INFINITY))
                } else if NEG_INF.is_match(&value[..]) {
                    Ok(YamlStandardData::YamlFloat(f64::NEG_INFINITY))
                } else if NAN_PATTERN.is_match(&value[..]) {
                    Ok(YamlStandardData::YamlFloat(f64::NAN))
                } else if NULL_PATTERN.is_match(&value[..]) {
                    Ok(YamlStandardData::YamlNull)
                } else if TRUE_PATTERN.is_match(&value[..]) {
                    Ok(YamlStandardData::YamlBool(true))
                } else if FALSE_PATTERN.is_match(&value[..]) {
                    Ok(YamlStandardData::YamlBool(false))
                } else {
                    Ok(YamlStandardData::YamlString(value))
                }
            },
            YamlScalarStyle::YamlDoubleQuotedScalarStyle => {
                YamlStandardConstructor::parse_double_quoted(&value[..], &mark).map(YamlStandardData::YamlString)
            },
            _ => {
                Ok(YamlStandardData::YamlString(value))
            }
        }
    }

    fn construct_sequence(&self, sequence: document::YamlSequenceData) -> Result<YamlStandardData, YamlError> {
        let res:Result<Vec<YamlStandardData>, YamlError> = sequence.values().map(|node| { self.construct(node) }).collect();
        res.map(|list| YamlStandardData::YamlSequence(list))
    }

    fn construct_mapping(&self, mapping: document::YamlMappingData) -> Result<YamlStandardData, YamlError> {
        let pairs = mapping.pairs().map(|(key_node, value_node)| {
            match self.construct(key_node) {
                Ok(key) => match self.construct(value_node) {
                    Ok(value) => Ok((key, value)),
                    Err(e) => Err(e)
                },
                Err(e) => Err(e)
            }
        });
        let res:Result<Vec<(YamlStandardData, YamlStandardData)>, YamlError> = pairs.collect();
        res.map(YamlStandardData::YamlMapping)
    }
}

#[cfg(test)]
mod test {
    use super::YamlStandardData::*;
    use parser::{YamlParser, YamlByteParser};
    use std::f64;
    use std::num::Float;
    use ffi::YamlEncoding::YamlUtf8Encoding;
    use constructor::{YamlConstructor, YamlStandardConstructor};

    #[test]
    fn test_standard_constructor() {
        let data = "[1, 2, 3]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlInteger(1), YamlInteger(2), YamlInteger(3)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("unexpected result")
        }
    }

    #[test]
    fn test_integer_parser() {
        let data = "[0o10, 0x21, -30]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlInteger(0o10), YamlInteger(0x21), YamlInteger(-30)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("unexpected result")
        }
    }

    #[test]
    fn test_float_parser() {
        let data = "[0.3, -.4, 1e+2, -1.2e-3]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                let value = ctor.construct(doc.root().unwrap());
                match value {
                    Ok(YamlSequence(seq)) => {
                        match &seq[..] {
                            [YamlFloat(f1), YamlFloat(f2), YamlFloat(f3), YamlFloat(f4)] => {
                                assert!((f1 - 0.3).abs() < 1.0e-6);
                                assert!((f2 + 0.4) < 1.0e-6);
                                assert!((f3 - 1e+2) < 1.0e-6);
                                assert!((f4 + 1.2e-3) < 1.0e-6);
                            },
                            _ => panic!("unexpected sequence")
                        }
                    },
                    _ => panic!("unexpected result")
                }
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_inf_parser() {
        let data = "[.inf, -.INF]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlFloat(f64::INFINITY), YamlFloat(f64::NEG_INFINITY)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_misc_parser() {
        let data = "[yes, False, ~]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlBool(true), YamlBool(false), YamlNull])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_double_quoted_parser() {
        let data = r#""hello, \"world\"""#;
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlString("hello, \"world\"".to_string())), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_single_quoted_parser() {
        let data = r#"'here''s to "quotes"'"#;
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlString(r#"here's to "quotes""#.to_string())), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_underlined_integer() {
        let data = "[1_000, -2_000_000]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlInteger(1000), YamlInteger(-2000000)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_negative_radix() {
        let data = "[-0x30, -0700, -0b110]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlInteger(-48), YamlInteger(-448), YamlInteger(-6)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }
}
<commit_msg>Rust changes: FromStrRadix deprecated<commit_after>use document;
use document::{YamlNode, YamlNodeData};
use ffi::{YamlErrorType, YamlScalarStyle};
use error::{YamlMark, YamlError, YamlErrorContext};

use std::f64;
use std::char;
use regex::Regex;

pub trait YamlConstructor<T, E> {
    fn construct_scalar(&self, scalar: document::YamlScalarData) -> Result<T, E>;
    fn construct_sequence(&self, sequence: document::YamlSequenceData) -> Result<T, E>;
    fn construct_mapping(&self, mapping: document::YamlMappingData) -> Result<T, E>;

    fn construct<'r>(&self, node: document::YamlNode<'r>) -> Result<T, E> {
        match node {
            YamlNode::YamlScalarNode(scalar) => self.construct_scalar(scalar),
            YamlNode::YamlSequenceNode(sequence) => self.construct_sequence(sequence),
            YamlNode::YamlMappingNode(mapping) => self.construct_mapping(mapping)
        }
    }
}

#[derive(PartialEq, Debug)]
pub enum YamlStandardData {
    YamlInteger(isize),
    YamlFloat(f64),
    YamlString(String),
    YamlNull,
    YamlBool(bool),
    YamlSequence(Vec<YamlStandardData>),
    YamlMapping(Vec<(YamlStandardData, YamlStandardData)>),
}

#[derive(Clone, Copy)]
pub struct YamlStandardConstructor;

fn standard_error(message: String, mark: &YamlMark) -> YamlError {
    let context = YamlErrorContext {
        byte_offset: mark.index,
        problem_mark: *mark,
        context: None,
        context_mark: *mark,
    };

    YamlError {
        kind: YamlErrorType::YAML_PARSER_ERROR,
        problem: Some(message),
        io_error: None,
        context: Some(context)
    }
}

fn take(iter: &mut Iterator<Item=char>, n: usize) -> String
{
    let mut s = String::new();
    for _ in 0..n {
        match iter.next() {
            Some(c) => s.push(c),
            None => break
        }
    }
    return s;
}

impl YamlStandardConstructor {
    pub fn new() -> YamlStandardConstructor {
        YamlStandardConstructor
    }

    fn parse_double_quoted(value: &str, mark: &YamlMark) -> Result<String, YamlError> {
        let mut buf = String::new();
        let mut it = value.chars();

        loop {
            match it.next() {
                None => return Ok(buf),
                Some('\\') => {
                    // escape sequences
                    match it.next() {
                        None => return Err(standard_error(
                                    "unexpected end of string after escape".to_string(),
                                    mark
                                )),
                        Some('0') => buf.push('\x00'),              // null
                        Some('a') => buf.push('\x07'),              // ASCII bell
                        Some('b') => buf.push('\x08'),              // backspace
                        Some('t') | Some('\t') => buf.push('\t'),   // horizontal tab
                        Some('n') => buf.push('\n'),                // linefeed
                        Some('v') => buf.push('\x0b'),              // vertical tab
                        Some('f') => buf.push('\x0c'),              // form feed
                        Some('r') => buf.push('\x0d'),              // carriage return
                        Some('e') => buf.push('\x1b'),              // ASCII escape
                        Some('N') => buf.push('\u{85}'),            // unicode next line
                        Some('_') => buf.push('\u{a0}'),            // unicode non-breaking space
                        Some('L') => buf.push('\u{2028}'),          // unicode line separator
                        Some('P') => buf.push('\u{2029}'),          // unicode paragraph separator
                        Some('x') => {
                            let code:String = take(&mut it, 2);
                            match parse_escape_sequence(&code[..], 2) {
                                Some(c) => buf.push(c),
                                None => return Err(standard_error(
                                            format!("invalid escape sequence {}", code),
                                            mark
                                        ))
                            }
                        },
                        Some('u') => {
                            let code:String = take(&mut it, 4);
                            match parse_escape_sequence(&code[..], 4) {
                                Some(c) => buf.push(c),
                                None => return Err(standard_error(
                                            format!("invalid escape sequence {}", code),
                                            mark
                                        ))
                            }
                        },
                        Some('U') => {
                            let code:String = take(&mut it, 8);
                            match parse_escape_sequence(&code[..], 8) {
                                Some(c) => buf.push(c),
                                None => return Err(standard_error(
                                            format!("invalid escape sequence {}", code),
                                            mark
                                        ))
                            }
                        },
                        Some(c) => buf.push(c)
                    }
                },
                Some(c) => buf.push(c)
            }
        }
    }
}

fn parse_escape_sequence(rep: &str, expected_len: usize) -> Option<char> {
    match u32::from_str_radix(rep, 16) {
        Ok(code) if rep.len() == expected_len => char::from_u32(code),
        _ => None
    }
}

fn parse_int(sign: &str, data: &str, radix: u32) -> isize {
    let sign_flag = if sign == "-" {
            -1
        } else {
            1
        };

    let filtered:String = data.chars().filter(|&c| c != '_').collect();
    let unsigned:isize = isize::from_str_radix(&filtered[..], radix).unwrap();
    return unsigned * sign_flag;
}

fn parse_float(sign: &str, data: &str) -> f64 {
    let unsigned:f64 = data.parse().unwrap();
    if sign == "-" {
        return -unsigned;
    } else {
        return unsigned;
    }
}

static DEC_INT:Regex = regex!(r"^[-+]?(0|[1-9][0-9_]*)$");
static OCT_INT:Regex = regex!(r"^([-+]?)0o?([0-7_]+)$");
static HEX_INT:Regex = regex!(r"^([-+]?)0x([0-9a-fA-F_]+)$");
static BIN_INT:Regex = regex!(r"^([-+]?)0b([0-1_]+)$");
static FLOAT_PATTERN:Regex = regex!(r"^([-+]?)(\.[0-9]+|[0-9]+(\.[0-9]*)?([eE][-+]?[0-9]+)?)$");
static POS_INF:Regex = regex!(r"^[+]?(\.inf|\.Inf|\.INF)$");
static NEG_INF:Regex = regex!(r"^-(\.inf|\.Inf|\.INF)$");
static NAN_PATTERN:Regex = regex!(r"^(\.nan|\.NaN|\.NAN)$");
static NULL_PATTERN:Regex = regex!(r"^(null|Null|NULL|~)$");
static TRUE_PATTERN:Regex = regex!(r"^(true|True|TRUE|yes|Yes|YES)$");
static FALSE_PATTERN:Regex = regex!(r"^(false|False|FALSE|no|No|NO)$");

impl YamlConstructor<YamlStandardData, YamlError> for YamlStandardConstructor {
    fn construct_scalar(&self, scalar: document::YamlScalarData) -> Result<YamlStandardData, YamlError> {
        let value = scalar.get_value();
        let mark = scalar.start_mark();

        match scalar.style() {
            YamlScalarStyle::YamlPlainScalarStyle => {
                match BIN_INT.captures(&value[..]) {
                    Some(caps) => return Ok(YamlStandardData::YamlInteger(parse_int(
                                caps.at(1).unwrap(), caps.at(2).unwrap(), 2))),
                    None => ()
                };
                match OCT_INT.captures(&value[..]) {
                    Some(caps) => return Ok(YamlStandardData::YamlInteger(parse_int(
                                caps.at(1).unwrap(), caps.at(2).unwrap(), 8))),
                    None => ()
                };
                match HEX_INT.captures(&value[..]) {
                    Some(caps) => return Ok(YamlStandardData::YamlInteger(parse_int(
                                caps.at(1).unwrap(), caps.at(2).unwrap(), 16))),
                    None => ()
                };

                if DEC_INT.is_match(&value[..]) {
                    return Ok(YamlStandardData::YamlInteger(parse_int("", &value[..], 10)));
                }

                match FLOAT_PATTERN.captures(&value[..]) {
                    Some(caps) => return Ok(YamlStandardData::YamlFloat(parse_float(
                                caps.at(1).unwrap(), caps.at(2).unwrap()))),
                    None => ()
                };

                if POS_INF.is_match(&value[..]) {
                    Ok(YamlStandardData::YamlFloat(f64::INFINITY))
                } else if NEG_INF.is_match(&value[..]) {
                    Ok(YamlStandardData::YamlFloat(f64::NEG_INFINITY))
                } else if NAN_PATTERN.is_match(&value[..]) {
                    Ok(YamlStandardData::YamlFloat(f64::NAN))
                } else if NULL_PATTERN.is_match(&value[..]) {
                    Ok(YamlStandardData::YamlNull)
                } else if TRUE_PATTERN.is_match(&value[..]) {
                    Ok(YamlStandardData::YamlBool(true))
                } else if FALSE_PATTERN.is_match(&value[..]) {
                    Ok(YamlStandardData::YamlBool(false))
                } else {
                    Ok(YamlStandardData::YamlString(value))
                }
            },
            YamlScalarStyle::YamlDoubleQuotedScalarStyle => {
                YamlStandardConstructor::parse_double_quoted(&value[..], &mark).map(YamlStandardData::YamlString)
            },
            _ => {
                Ok(YamlStandardData::YamlString(value))
            }
        }
    }

    fn construct_sequence(&self, sequence: document::YamlSequenceData) -> Result<YamlStandardData, YamlError> {
        let res:Result<Vec<YamlStandardData>, YamlError> = sequence.values().map(|node| { self.construct(node) }).collect();
        res.map(|list| YamlStandardData::YamlSequence(list))
    }

    fn construct_mapping(&self, mapping: document::YamlMappingData) -> Result<YamlStandardData, YamlError> {
        let pairs = mapping.pairs().map(|(key_node, value_node)| {
            match self.construct(key_node) {
                Ok(key) => match self.construct(value_node) {
                    Ok(value) => Ok((key, value)),
                    Err(e) => Err(e)
                },
                Err(e) => Err(e)
            }
        });
        let res:Result<Vec<(YamlStandardData, YamlStandardData)>, YamlError> = pairs.collect();
        res.map(YamlStandardData::YamlMapping)
    }
}

#[cfg(test)]
mod test {
    use super::YamlStandardData::*;
    use parser::{YamlParser, YamlByteParser};
    use std::f64;
    use std::num::Float;
    use ffi::YamlEncoding::YamlUtf8Encoding;
    use constructor::{YamlConstructor, YamlStandardConstructor};

    #[test]
    fn test_standard_constructor() {
        let data = "[1, 2, 3]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlInteger(1), YamlInteger(2), YamlInteger(3)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("unexpected result")
        }
    }

    #[test]
    fn test_integer_parser() {
        let data = "[0o10, 0x21, -30]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlInteger(0o10), YamlInteger(0x21), YamlInteger(-30)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("unexpected result")
        }
    }

    #[test]
    fn test_float_parser() {
        let data = "[0.3, -.4, 1e+2, -1.2e-3]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                let value = ctor.construct(doc.root().unwrap());
                match value {
                    Ok(YamlSequence(seq)) => {
                        match &seq[..] {
                            [YamlFloat(f1), YamlFloat(f2), YamlFloat(f3), YamlFloat(f4)] => {
                                assert!((f1 - 0.3).abs() < 1.0e-6);
                                assert!((f2 + 0.4) < 1.0e-6);
                                assert!((f3 - 1e+2) < 1.0e-6);
                                assert!((f4 + 1.2e-3) < 1.0e-6);
                            },
                            _ => panic!("unexpected sequence")
                        }
                    },
                    _ => panic!("unexpected result")
                }
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_inf_parser() {
        let data = "[.inf, -.INF]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlFloat(f64::INFINITY), YamlFloat(f64::NEG_INFINITY)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_misc_parser() {
        let data = "[yes, False, ~]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlBool(true), YamlBool(false), YamlNull])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_double_quoted_parser() {
        let data = r#""hello, \"world\"""#;
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlString("hello, \"world\"".to_string())), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_single_quoted_parser() {
        let data = r#"'here''s to "quotes"'"#;
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlString(r#"here's to "quotes""#.to_string())), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_underlined_integer() {
        let data = "[1_000, -2_000_000]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlInteger(1000), YamlInteger(-2000000)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }

    #[test]
    fn test_negative_radix() {
        let data = "[-0x30, -0700, -0b110]";
        let parser = YamlByteParser::init(data.as_bytes(), YamlUtf8Encoding);

        match parser.load().next() {
            Some(Ok(doc)) => {
                let ctor = YamlStandardConstructor::new();
                assert_eq!(Ok(YamlSequence(vec![YamlInteger(-48), YamlInteger(-448), YamlInteger(-6)])), ctor.construct(doc.root().unwrap()))
            },
            _ => panic!("document parse failure")
        }
    }
}
<|endoftext|>


<commit_before>use io::file::FileStatus;
use io::redraw::RedrawTask;
use state::editor::{BufferInfo, BufferManager, Editor};
use edit::buffer::{Buffer, SplitBuffer};

use std::process::exit;

enum BufferCommand {
    SwitchToBuffer(usize),
}

fn try_get_buffer_command(c: &str) -> Option<BufferCommand> {
    if !c.starts_with("b") {
        return None;
    }

    let rest : String = c.chars().skip(1).collect();

    //TODO more buffer commands

    if let Ok(number) = rest.parse::<usize>() {
        Some(BufferCommand::SwitchToBuffer(number))
    } else {
        None
    }
}

impl Editor {
    /// Invoke a command in the prompt
    pub fn invoke(&mut self, cmd: String) {
        let mut split = cmd.split(' ');
        let base_cmd = split.nth(0).unwrap_or("");
        let sec_cmd = split.nth(0).unwrap_or("");

        match base_cmd {
            "set" => {
                self.status_bar.msg = match self.options.set(sec_cmd) {
                    Ok(()) => format!("Option set: {}", sec_cmd),
                    Err(()) => format!("Option does not exist: {}", sec_cmd),
                }
            },
            "unset" => {
                self.status_bar.msg = match self.options.unset(sec_cmd) {
                    Ok(()) => format!("Option unset: {}", sec_cmd),
                    Err(()) => format!("Option does not exist: {}", sec_cmd),
                }
            },
            "toggle" | "tog" => {
                self.status_bar.msg = match self.options.toggle(sec_cmd) {
                    Ok(()) => format!("Option toggled: {}", sec_cmd),
                    Err(()) => format!("Option does not exist: {}", sec_cmd),
                }
            },
            "get" => {
                self.status_bar.msg = match self.options.get(sec_cmd) {
                    Some(true) => format!("Option set: {}", sec_cmd),
                    Some(false) => format!("Option unset: {}", sec_cmd),
                    None => format!("Option does not exist: {}", sec_cmd),
                }
            },
            "o" | "open" => {
                self.status_bar.msg = match self.open(sec_cmd) {
                    FileStatus::NotFound => format!("File {} could not be opened", sec_cmd),
                    FileStatus::Ok => format!("File {} opened", sec_cmd),
                    _ => unreachable!(),
                }
            },
            "w" | "write" => {
                self.status_bar.msg = match self.write(sec_cmd) {
                    FileStatus::NotFound => format!("File {} could not be opened", sec_cmd),
                    FileStatus::Ok => format!("File {} written", sec_cmd),
                    FileStatus::Other => format!("Couldn't write {}", sec_cmd),
                }
            },
            "ls" => {
                let description = get_buffers_description(&self.buffers);
                let mut new_buffer : BufferInfo = SplitBuffer::from_str(&description).into();
                new_buffer.title = Some("<Buffers>".into());
                new_buffer.is_transient = true; //delete the buffer when the user switches away

                let new_buffer_index = self.buffers.new_buffer(new_buffer);
                self.buffers.switch_to(new_buffer_index);
                self.redraw_task = RedrawTask::Full;
            },
            "help" => {
                self.open("/apps/sodium/help.txt");
            },
            "q" | "quit" => {
                exit(0);
            },
            c => {
                if let Some(buffer_command) = try_get_buffer_command(c) {
                    match buffer_command {
                        BufferCommand::SwitchToBuffer(n) => {
                            if n >= self.buffers.len() {
                                self.status_bar.msg = format!("Invalid buffer #{}", n);
                            } else {
                                self.buffers.switch_to(n);
                                self.redraw_task = RedrawTask::Full;
                                self.status_bar.msg = format!("Switched to buffer #{}", n);
                            }
                        },
                    }
                } else {
                    self.status_bar.msg = format!("Unknown command: {}", c);
                }
            }
        }

        self.hint();
    }
}

fn get_buffers_description(buffers: &BufferManager) -> String {
    fn print_buffer(i: usize, b: &BufferInfo) -> String {
        let title = b.title.as_ref().map(|s| s.as_str()).unwrap_or("<No Title>");

        format!("b{}\t\t\t{}", i, title)
    }

    let descriptions =
        buffers
            .iter()
            // don't include transient buffers like the one
            // this is going to be shown in
            .filter(|b| !b.is_transient)
            .enumerate()
            .map(|(i, b)| print_buffer(i, b))
            .collect::<Vec<_>>();

    descriptions.join("\n")
}
<commit_msg>Make the `ls` output prettier<commit_after>use io::file::FileStatus;
use io::redraw::RedrawTask;
use state::editor::{BufferInfo, BufferManager, Editor};
use edit::buffer::{Buffer, SplitBuffer};

use std::process::exit;

enum BufferCommand {
    SwitchToBuffer(usize),
}

fn try_get_buffer_command(c: &str) -> Option<BufferCommand> {
    if !c.starts_with("b") {
        return None;
    }

    let rest : String = c.chars().skip(1).collect();

    //TODO more buffer commands

    if let Ok(number) = rest.parse::<usize>() {
        Some(BufferCommand::SwitchToBuffer(number))
    } else {
        None
    }
}

impl Editor {
    /// Invoke a command in the prompt
    pub fn invoke(&mut self, cmd: String) {
        let mut split = cmd.split(' ');
        let base_cmd = split.nth(0).unwrap_or("");
        let sec_cmd = split.nth(0).unwrap_or("");

        match base_cmd {
            "set" => {
                self.status_bar.msg = match self.options.set(sec_cmd) {
                    Ok(()) => format!("Option set: {}", sec_cmd),
                    Err(()) => format!("Option does not exist: {}", sec_cmd),
                }
            },
            "unset" => {
                self.status_bar.msg = match self.options.unset(sec_cmd) {
                    Ok(()) => format!("Option unset: {}", sec_cmd),
                    Err(()) => format!("Option does not exist: {}", sec_cmd),
                }
            },
            "toggle" | "tog" => {
                self.status_bar.msg = match self.options.toggle(sec_cmd) {
                    Ok(()) => format!("Option toggled: {}", sec_cmd),
                    Err(()) => format!("Option does not exist: {}", sec_cmd),
                }
            },
            "get" => {
                self.status_bar.msg = match self.options.get(sec_cmd) {
                    Some(true) => format!("Option set: {}", sec_cmd),
                    Some(false) => format!("Option unset: {}", sec_cmd),
                    None => format!("Option does not exist: {}", sec_cmd),
                }
            },
            "o" | "open" => {
                self.status_bar.msg = match self.open(sec_cmd) {
                    FileStatus::NotFound => format!("File {} could not be opened", sec_cmd),
                    FileStatus::Ok => format!("File {} opened", sec_cmd),
                    _ => unreachable!(),
                }
            },
            "w" | "write" => {
                self.status_bar.msg = match self.write(sec_cmd) {
                    FileStatus::NotFound => format!("File {} could not be opened", sec_cmd),
                    FileStatus::Ok => format!("File {} written", sec_cmd),
                    FileStatus::Other => format!("Couldn't write {}", sec_cmd),
                }
            },
            "ls" => {
                let description = get_buffers_description(&self.buffers);
                let mut new_buffer : BufferInfo = SplitBuffer::from_str(&description).into();
                new_buffer.title = Some("<Buffers>".into());
                new_buffer.is_transient = true; //delete the buffer when the user switches away

                let new_buffer_index = self.buffers.new_buffer(new_buffer);
                self.buffers.switch_to(new_buffer_index);
                self.redraw_task = RedrawTask::Full;
            },
            "help" => {
                self.open("/apps/sodium/help.txt");
            },
            "q" | "quit" => {
                exit(0);
            },
            c => {
                if let Some(buffer_command) = try_get_buffer_command(c) {
                    match buffer_command {
                        BufferCommand::SwitchToBuffer(n) => {
                            if n >= self.buffers.len() {
                                self.status_bar.msg = format!("Invalid buffer #{}", n);
                            } else {
                                self.buffers.switch_to(n);
                                self.redraw_task = RedrawTask::Full;
                                self.status_bar.msg = format!("Switched to buffer #{}", n);
                            }
                        },
                    }
                } else {
                    self.status_bar.msg = format!("Unknown command: {}", c);
                }
            }
        }

        self.hint();
    }
}

fn get_buffers_description(buffers: &BufferManager) -> String {
    fn print_buffer(i: usize, b: &BufferInfo) -> String {
        let title = b.title.as_ref().map(|s| s.as_str()).unwrap_or("<No Title>");

        format!("b{}\t\t\t{}", i, title)
    }

    let descriptions =
        buffers
            .iter()
            // don't include transient buffers like the one
            // this is going to be shown in
            .filter(|b| !b.is_transient)
            .enumerate()
            .map(|(i, b)| print_buffer(i, b))
            .collect::<Vec<_>>()
            .join("\n");

    format!("Buffers\n=====================================\n\n{}", descriptions)
}
<|endoftext|>


<commit_before>use ::block::Block;

use ::core::mem::size_of;

//const MIN_ALLOCATION: usize = 1;
//const MIN_BLOCK_SIZE: usize = size_of::<Block>() + MIN_ALLOCATION;
const MIN_BLOCK_SIZE: usize = 32;

// WIP NOTES...
// 1. Start with one massive block, break this up with every allocation. Walk for first fit.
// 2. Deallocate just flag as free
// 3. Merge free neighbours? - Maybe a GC function that runs periodically??
//
//
//

pub struct Allocator {
    heap: &'static mut [u8],
    size: usize,
    block_head: *mut Block
}

impl Allocator {

    // Create a new Allocator for a given heap. The heap must be at-least the size of a block
    // header.
    pub fn new(heap: &'static mut [u8], size: usize) -> Allocator {
        assert!(size > size_of::<Block>());
        let mut block = unsafe {
            let mut b = (&mut heap[0] as *mut u8) as *mut Block;
            (*b).prev = None;
            (*b).next = None;
            (*b).size = size - size_of::<Block>();
            (*b).free = true;
            b
        };

        Allocator {
            heap: heap,
            size: size,
            block_head: block
        }
    }

    // Allocate 'size' byes
    pub fn alloc(&mut self, size: usize, align: usize) -> Option<*mut u8> {

        // TODO implement the alignment side of things

        // Find the next fitting block
        let mut block_head = self.block_head;
        let mut block_ptr = self.next_fit(size, block_head);
        let mut block = unsafe { block_ptr.as_mut().expect("Null Block Pointer") };

        // Found a block. We now need to see how big it is. If after allocation it is going to
        // leave unused memory larger than MIN_BLOCK_SIZE then we chunk it up and create a new
        // free block in the space.
        if (block.size - size) >= MIN_BLOCK_SIZE {
            let next_block_size = block.size - size - (size_of::<Block>() * 2);

            // The order of the next steps are crucial...

            // Set block to allocate size
            block.size = size;

            // Get a pointer to the next block and set it to point to whatever the original 'next'
            // was. This is because we are slotting this block in between the allocated block
            // and its neighbour.
            let mut next_block = unsafe { block.next_ptr() };
            unsafe {
                (*next_block).size = next_block_size;
                (*next_block).prev = Some(block as *mut Block);
                (*next_block).next = block.next;
            }

            // Finally set the allocated block to point to our new block and complete the chain.
            block.next = Some(next_block);
        }

        // Finally we mark the allocated block as used and return the data_pointer to the caller
        block.free = false;
        let mut alloc_pointer = unsafe { block.data_pointer() };
        Some(alloc_pointer)
    }

    pub fn dealloc(&mut self, ptr: *mut u8, size: usize, align: usize) {
        unimplemented!();
    }

    pub fn usable_size(&mut self, size: usize, align: usize) -> usize {
        unimplemented!();
    }

    pub fn realloc(&mut self, ptr: *mut u8, size: usize,
        new_size: usize, align: usize) -> *mut u8 {
        unimplemented!();
    }

    pub fn realloc_inplace(&mut self, ptr: *mut u8, size: usize,
        new_size: usize, align: usize) -> usize {
        unimplemented!();
    }

    fn next_fit(&mut self, size: usize, current: *mut Block) -> *mut Block {
        let current_ref = unsafe { current.as_ref().expect("Null Block Pointer") };

        if current_ref.free && current_ref.size >= size {
            current
        } else {
            let mut block = match current_ref.next {
                Some(next) => next,
                None => panic!("Out Of Memory"),
            };

            self.next_fit(size, block)
        }
    }
}
<commit_msg>Fix another bug in allocator todo with size of block calculations<commit_after>use ::block::Block;

use ::core::mem::size_of;

//const MIN_ALLOCATION: usize = 1;
//const MIN_BLOCK_SIZE: usize = size_of::<Block>() + MIN_ALLOCATION;
const MIN_BLOCK_SIZE: usize = 32;

// WIP NOTES...
// 1. Start with one massive block, break this up with every allocation. Walk for first fit.
// 2. Deallocate just flag as free
// 3. Merge free neighbours? - Maybe a GC function that runs periodically??
//
//
//

pub struct Allocator {
    heap: &'static mut [u8],
    size: usize,
    block_head: *mut Block
}

impl Allocator {

    // Create a new Allocator for a given heap. The heap must be at-least the size of a block
    // header.
    pub fn new(heap: &'static mut [u8], size: usize) -> Allocator {
        assert!(size > size_of::<Block>());
        let mut block = unsafe {
            let mut b = (&mut heap[0] as *mut u8) as *mut Block;
            (*b).prev = None;
            (*b).next = None;
            (*b).size = size - size_of::<Block>();
            (*b).free = true;
            b
        };

        Allocator {
            heap: heap,
            size: size,
            block_head: block
        }
    }

    // Allocate 'size' byes
    pub fn alloc(&mut self, size: usize, align: usize) -> Option<*mut u8> {

        // TODO implement the alignment side of things

        // Find the next fitting block
        let mut block_head = self.block_head;
        let mut block_ptr = self.next_fit(size, block_head);
        let mut block = unsafe { block_ptr.as_mut().expect("Null Block Pointer") };

        // Found a block. We now need to see how big it is. If after allocation it is going to
        // leave unused memory larger than MIN_BLOCK_SIZE then we chunk it up and create a new
        // free block in the space.
        if (block.size - size) >= MIN_BLOCK_SIZE {
            let next_block_size = block.size - size - size_of::<Block>();

            // The order of the next steps are crucial...

            // Set block to allocate size
            block.size = size;

            // Get a pointer to the next block and set it to point to whatever the original 'next'
            // was. This is because we are slotting this block in between the allocated block
            // and its neighbour.
            let mut next_block = unsafe { block.next_ptr() };
            unsafe {
                (*next_block).size = next_block_size;
                (*next_block).prev = Some(block as *mut Block);
                (*next_block).next = block.next;
                (*next_block).free = true;
            }

            // Finally set the allocated block to point to our new block and complete the chain.
            block.next = Some(next_block);
        }

        // Finally we mark the allocated block as used and return the data_pointer to the caller
        block.free = false;
        let mut alloc_pointer = unsafe { block.data_pointer() };
        Some(alloc_pointer)
    }

    pub fn dealloc(&mut self, ptr: *mut u8, size: usize, align: usize) {
        unimplemented!();
    }

    pub fn usable_size(&mut self, size: usize, align: usize) -> usize {
        unimplemented!();
    }

    pub fn realloc(&mut self, ptr: *mut u8, size: usize,
        new_size: usize, align: usize) -> *mut u8 {
        unimplemented!();
    }

    pub fn realloc_inplace(&mut self, ptr: *mut u8, size: usize,
        new_size: usize, align: usize) -> usize {
        unimplemented!();
    }

    fn next_fit(&mut self, size: usize, current: *mut Block) -> *mut Block {
        let current_ref = unsafe { current.as_ref().expect("Null Block Pointer") };

        if current_ref.free && current_ref.size >= size {
            current
        } else {
            let mut block = match current_ref.next {
                Some(next) => next,
                None => panic!("Out Of Memory"),
            };

            self.next_fit(size, block)
        }
    }
}
<|endoftext|>


<commit_before>use common::debug::*;
use common::memory::*;
use common::pci::*;
use common::pio::*;
use common::string::*;
use common::url::*;

use drivers::disk::*;

use programs::session::*;

pub struct IDEScheme {
    pub bus: usize,
    pub slot: usize,
    pub func: usize,
    pub base: usize,
    pub memory_mapped: bool
}

impl SessionScheme for IDEScheme {
    fn scheme(&self) -> String {
        return "ide".to_string();
    }

    #[allow(unused_variables)]
    fn on_url(&mut self, session: &Session, url: &URL) -> String {
        unsafe {
            let destination = alloc(512);
            let disk = Disk::new();
            disk.read_dma(1, 1, destination, self.base as u16);
        }

        return "TEST".to_string();
    }
}

pub struct IDE {
    pub bus: usize,
    pub slot: usize,
    pub func: usize,
    pub base: usize,
    pub memory_mapped: bool
}

impl SessionDevice for IDE {
    #[allow(unused_variables)]
    fn on_irq(&mut self, session: &Session, updates: &mut SessionUpdates, irq: u8){
        if irq == 0xE || irq == 0xF {
            unsafe {
                let base = self.base as u16;

                let command = inb(base);
                let status = inb(base + 2);
                if status & 4 == 4 {
                    d("IDE handle");

                    if command & 1 == 1 {
                        d(" Command ");
                        dbh(command);

                        outb(base, command & 0xFE);

                        d(" to ");
                        dbh(inb(base));

                        d(" Status ");
                        dbh(status);

                        outb(base + 0x2, 1);

                        d(" to ");
                        dbh(inb(base + 0x2));

                        let prdt = ind(base + 0x4) as usize;
                        if prdt > 0 {
                            let prdte = &mut *(prdt as *mut PRDTE);
                            d(" PTR ");
                            dh(prdte.ptr as usize);

                            d(" SIZE ");
                            dd(prdte.size as usize);

                            d(" RESERVED ");
                            dh(prdte.reserved as usize);

                            d(" DATA ");
                            for i in 0..4 {
                                dc(*(prdte.ptr as *const u8).offset(i) as char);
                            }

                            unalloc(prdte.ptr as usize);
                            prdte.ptr = 0;
                        }
                        unalloc(prdt);

                        outd(base + 0x4, 0);
                    }

                    dl();
                }
            }
        }
    }
}

impl IDE {
    pub unsafe fn init(&self){
        d("IDE Controller on ");
        dh(self.base);
        if self.memory_mapped {
            d(" memory mapped");
        }else{
            d(" port mapped");
        }
        dl();

        pci_write(self.bus, self.slot, self.func, 0x04, pci_read(self.bus, self.slot, self.func, 0x04) | (1 << 2)); // Bus mastering

        let base = self.base as u16;

        d("PDTR: ");
        dh(ind(base + 0x4) as usize);
        dl();

        d("COMMAND: ");
        dbh(inb(base));
        dl();

        d("STATUS: ");
        dbh(inb(base + 0x2));
        dl();
    }
}
<commit_msg>DMA WIP<commit_after>use core::result::Result;

use common::debug::*;
use common::memory::*;
use common::pci::*;
use common::pio::*;
use common::string::*;
use common::url::*;

use drivers::disk::*;

use programs::session::*;

pub struct IDEScheme {
    pub bus: usize,
    pub slot: usize,
    pub func: usize,
    pub base: usize,
    pub memory_mapped: bool
}

impl SessionScheme for IDEScheme {
    fn scheme(&self) -> String {
        return "ide".to_string();
    }

    #[allow(unused_variables)]
    fn on_url(&mut self, session: &Session, url: &URL) -> String {
        let mut ret = String::new();

        let mut sector = 1;
        match url.path.get(0) {
            Result::Ok(part) => {
                sector = part.to_num();
            },
            Result::Err(_) => ()
        }

        let mut count = 1;
        match url.path.get(1) {
            Result::Ok(part) => {
                count = part.to_num();
            },
            Result::Err(_) => ()
        }

        unsafe {
            let base = self.base as u16;

            if count < 1 {
                count = 1;
            }

            if count > 0x7F {
                count = 0x7F;
            }

            let destination = alloc(count * 512);
            if destination > 0 {
                let disk = Disk::new();
                //disk.read(sector as u64, count as u16, destination);
                disk.read_dma(sector as u64, count as u16, destination, base);

                while inb(base + 2) & 4 != 4{
                    d("DISK WAIT\n");
                }

                ret = String::from_c_str(destination as *const u8);
                unalloc(destination);
            }
        }

        return ret;
    }
}

pub struct IDE {
    pub bus: usize,
    pub slot: usize,
    pub func: usize,
    pub base: usize,
    pub memory_mapped: bool
}

impl SessionDevice for IDE {
    #[allow(unused_variables)]
    fn on_irq(&mut self, session: &Session, updates: &mut SessionUpdates, irq: u8){
        if irq == 0xE || irq == 0xF {
            unsafe {
                let base = self.base as u16;

                let command = inb(base);
                let status = inb(base + 2);
                if status & 4 == 4 {
                    d("IDE handle");

                    if command & 1 == 1 {
                        d(" DMA Command ");
                        dbh(command);

                        outb(base, command & 0xFE);

                        d(" to ");
                        dbh(inb(base));

                        d(" Status ");
                        dbh(status);

                        outb(base + 0x2, 4);

                        d(" to ");
                        dbh(inb(base + 0x2));

                        let prdt = ind(base + 0x4) as usize;
                        outd(base + 0x4, 0);

                        d(" PRDT ");
                        dh(prdt);

                        unalloc(prdt);
                    }else{
                        d(" PIO Command ");
                        dbh(command);

                        d(" Status ");
                        dbh(status);

                        outb(base + 0x2, 4);

                        d(" to ");
                        dbh(inb(base + 0x2));
                    }

                    dl();
                }
            }
        }
    }
}

impl IDE {
    pub unsafe fn init(&self){
        d("IDE Controller on ");
        dh(self.base);
        if self.memory_mapped {
            d(" memory mapped");
        }else{
            d(" port mapped");
        }
        dl();

        pci_write(self.bus, self.slot, self.func, 0x04, pci_read(self.bus, self.slot, self.func, 0x04) | (1 << 2)); // Bus mastering

        let base = self.base as u16;

        d("PDTR: ");
        dh(ind(base + 0x4) as usize);
        dl();

        d("COMMAND: ");
        dbh(inb(base));
        dl();

        d("STATUS: ");
        dbh(inb(base + 0x2));
        dl();
    }
}
<|endoftext|>


<commit_before>#![feature(core_intrinsics)]

use std::panic::Location;

#[track_caller]
fn tracked() -> &'static Location<'static> {
    Location::caller() // most importantly, we never get line 7
}

fn nested_intrinsic() -> &'static Location<'static> {
    Location::caller()
}

fn nested_tracked() -> &'static Location<'static> {
    tracked()
}

macro_rules! caller_location_from_macro {
    () => (core::panic::Location::caller());
}

fn test_fn_ptr() {
    fn pass_to_ptr_call<T>(f: fn(T), x: T) {
        f(x);
    }

    #[track_caller]
    fn tracked_unit(_: ()) {
        let expected_line = line!() - 1;
        let location = std::panic::Location::caller();
        assert_eq!(location.file(), file!());
        assert_eq!(location.line(), expected_line, "call shims report location as fn definition");
    }

    pass_to_ptr_call(tracked_unit, ());
}

fn main() {
    let location = Location::caller();
    let expected_line = line!() - 1;
    assert_eq!(location.file(), file!());
    assert_eq!(location.line(), expected_line);
    assert_eq!(location.column(), 20);

    let tracked = tracked();
    let expected_line = line!() - 1;
    assert_eq!(tracked.file(), file!());
    assert_eq!(tracked.line(), expected_line);
    assert_eq!(tracked.column(), 19);

    let nested = nested_intrinsic();
    assert_eq!(nested.file(), file!());
    assert_eq!(nested.line(), 11);
    assert_eq!(nested.column(), 5);

    let contained = nested_tracked();
    assert_eq!(contained.file(), file!());
    assert_eq!(contained.line(), 15);
    assert_eq!(contained.column(), 5);

    // `Location::caller()` in a macro should behave similarly to `file!` and `line!`,
    // i.e. point to where the macro was invoked, instead of the macro itself.
    let inmacro = caller_location_from_macro!();
    let expected_line = line!() - 1;
    assert_eq!(inmacro.file(), file!());
    assert_eq!(inmacro.line(), expected_line);
    assert_eq!(inmacro.column(), 19);

    let intrinsic = core::intrinsics::caller_location();
    let expected_line = line!() - 1;
    assert_eq!(intrinsic.file(), file!());
    assert_eq!(intrinsic.line(), expected_line);
    assert_eq!(intrinsic.column(), 21);

    test_fn_ptr();
}
<commit_msg>Auto merge of #1493 - RalfJung:track-test, r=RalfJung<commit_after>#![feature(core_intrinsics)]

use std::panic::Location;

#[track_caller]
fn tracked() -> &'static Location<'static> {
    Location::caller() // most importantly, we never get line 7
}

fn nested_intrinsic() -> &'static Location<'static> {
    Location::caller()
}

fn nested_tracked() -> &'static Location<'static> {
    tracked()
}

macro_rules! caller_location_from_macro {
    () => (core::panic::Location::caller());
}

fn test_fn_ptr() {
    fn pass_to_ptr_call<T>(f: fn(T), x: T) {
        f(x);
    }

    #[track_caller]
    fn tracked_unit(_: ()) {
        let expected_line = line!() - 1;
        let location = std::panic::Location::caller();
        assert_eq!(location.file(), file!());
        assert_eq!(location.line(), expected_line, "call shims report location as fn definition");
    }

    pass_to_ptr_call(tracked_unit, ());
}

fn test_trait_obj() {
    trait Tracked {
        #[track_caller]
        fn handle(&self) { // `fn` here is what the `location` should point at.
            let location = std::panic::Location::caller();
            assert_eq!(location.file(), file!());
            // we only call this via trait object, so the def site should *always* be returned
            assert_eq!(location.line(), line!() - 4);
            assert_eq!(location.column(), 9);
        }
    }

    impl Tracked for () {}
    impl Tracked for u8 {}

    let tracked: &dyn Tracked = &5u8;
    tracked.handle();

    const TRACKED: &dyn Tracked = &();
    TRACKED.handle();
}

fn main() {
    let location = Location::caller();
    let expected_line = line!() - 1;
    assert_eq!(location.file(), file!());
    assert_eq!(location.line(), expected_line);
    assert_eq!(location.column(), 20);

    let tracked = tracked();
    let expected_line = line!() - 1;
    assert_eq!(tracked.file(), file!());
    assert_eq!(tracked.line(), expected_line);
    assert_eq!(tracked.column(), 19);

    let nested = nested_intrinsic();
    assert_eq!(nested.file(), file!());
    assert_eq!(nested.line(), 11);
    assert_eq!(nested.column(), 5);

    let contained = nested_tracked();
    assert_eq!(contained.file(), file!());
    assert_eq!(contained.line(), 15);
    assert_eq!(contained.column(), 5);

    // `Location::caller()` in a macro should behave similarly to `file!` and `line!`,
    // i.e. point to where the macro was invoked, instead of the macro itself.
    let inmacro = caller_location_from_macro!();
    let expected_line = line!() - 1;
    assert_eq!(inmacro.file(), file!());
    assert_eq!(inmacro.line(), expected_line);
    assert_eq!(inmacro.column(), 19);

    let intrinsic = core::intrinsics::caller_location();
    let expected_line = line!() - 1;
    assert_eq!(intrinsic.file(), file!());
    assert_eq!(intrinsic.line(), expected_line);
    assert_eq!(intrinsic.column(), 21);

    test_fn_ptr();
    test_trait_obj();
}
<|endoftext|>