<commit_before>//! client and server support for the varlink protocol

extern crate bytes;
extern crate itertools;

extern crate libc;
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;
extern crate unix_socket;

extern crate varlink_parser;

pub mod generator;
pub mod server;

use serde_json::Value;
use serde::ser::Serialize;

use std::convert::From;
use std::collections::HashMap;
use std::borrow::Cow;
use std::io::{self, BufRead, BufReader, Error, ErrorKind, Read, Write};

pub trait Interface {
    fn get_description(&self) -> &'static str;
    fn get_name(&self) -> &'static str;
    fn call(&self, &mut Call) -> io::Result<()>;
    fn call_upgraded(&self, &mut Call) -> io::Result<()>;
}

#[derive(Serialize, Deserialize, Debug, Default)]
pub struct Request {
    pub more: Option<bool>,
    pub oneshot: Option<bool>,
    pub upgrade: Option<bool>,
    pub method: Cow<'static, str>,
    pub parameters: Option<Value>,
}

pub trait VarlinkReply {}

#[derive(Serialize, Deserialize)]
pub struct Reply {
    continues: Option<bool>,
    upgraded: Option<bool>,
    error: Option<Cow<'static, str>>,
    parameters: Option<Value>,
}

impl Reply {
    pub fn parameters(parameters: Value) -> Self {
        Reply {
            continues: None,
            upgraded: None,
            error: None,
            parameters: Some(parameters),
        }
    }

    pub fn error(name: Cow<'static, str>, parameters: Option<Value>) -> Self {
        Reply {
            continues: None,
            upgraded: None,
            error: Some(name),
            parameters,
        }
    }
}

impl<T> From<T> for Reply
where
    T: VarlinkReply + Serialize,
{
    fn from(a: T) -> Self {
        Reply::parameters(serde_json::to_value(a).unwrap())
    }
}

pub struct Call<'a> {
    writer: &'a mut Write,
    pub request: Option<&'a Request>,
    pub continues: bool,
    pub upgraded: bool,
}

pub trait CallTrait {
    fn reply_struct(&mut self, Reply) -> io::Result<()>;

    fn reply_method_not_found(&mut self, arg: Option<String>) -> io::Result<()> {
        self.reply_struct(Reply::error(
            "org.varlink.service.MethodNotFound".into(),
            match arg {
                Some(a) => {
                    let s = format!("{{  \"method\" : \"{}\" }}", a);
                    Some(serde_json::from_str(s.as_ref()).unwrap())
                }
                None => None,
            },
        ))
    }

    fn reply_method_not_implemented(&mut self, arg: Option<String>) -> io::Result<()> {
        self.reply_struct(Reply::error(
            "org.varlink.service.MethodNotImplemented".into(),
            match arg {
                Some(a) => {
                    let s = format!("{{  \"method\" : \"{}\" }}", a);
                    Some(serde_json::from_str(s.as_ref()).unwrap())
                }
                None => None,
            },
        ))
    }

    fn reply_invalid_parameter(&mut self, arg: Option<String>) -> io::Result<()> {
        self.reply_struct(Reply::error(
            "org.varlink.service.InvalidParameter".into(),
            match arg {
                Some(a) => {
                    let s = format!("{{  \"parameter\" : \"{}\" }}", a);
                    Some(serde_json::from_str(s.as_ref()).unwrap())
                }
                None => None,
            },
        ))
    }
}

impl<'a> CallTrait for Call<'a> {
    fn reply_struct(&mut self, mut reply: Reply) -> io::Result<()> {
        if self.continues && !self.wants_more() {
            return Err(Error::new(
                ErrorKind::Other,
                "Call::reply() called with continues, but without more in the request",
            ));
        }
        if self.continues {
            reply.continues = Some(true);
        }
        let mut buf = serde_json::to_vec(&reply)?;
        buf.push(0);
        self.writer.write_all(&mut buf)?;
        self.writer.flush()?;
        Ok(())
    }
}

impl<'a> Call<'a> {
    fn new(writer: &'a mut Write, request: &'a Request) -> Self {
        Call {
            writer,
            request: Some(request),
            continues: false,
            upgraded: false,
        }
    }
    fn new_upgraded(writer: &'a mut Write) -> Self {
        Call {
            writer,
            request: None,
            continues: false,
            upgraded: false,
        }
    }
    pub fn is_oneshot(&self) -> bool {
        match self.request {
            Some(req) => {
                if let Some(val) = req.oneshot {
                    val
                } else {
                    false
                }
            }
            None => false,
        }
    }

    pub fn wants_more(&self) -> bool {
        match self.request {
            Some(req) => if let Some(val) = req.more {
                val
            } else {
                false
            },
            None => false,
        }
    }

    fn reply_interface_not_found(&mut self, arg: Option<String>) -> io::Result<()> {
        self.reply_struct(Reply::error(
            "org.varlink.service.InterfaceNotFound".into(),
            match arg {
                Some(a) => {
                    let s = format!("{{  \"interface\" : \"{}\" }}", a);
                    Some(serde_json::from_str(s.as_ref()).unwrap())
                }
                None => None,
            },
        ))
    }

    fn reply_parameters(&mut self, parameters: Value) -> io::Result<()> {
        let reply = Reply::parameters(parameters);
        let mut buf = serde_json::to_vec(&reply)?;
        buf.push(0);
        self.writer.write_all(&mut buf)?;
        self.writer.flush()?;
        Ok(())
    }
}

#[derive(Deserialize)]
struct GetInterfaceArgs {
    interface: Cow<'static, str>,
}

#[derive(Serialize, Deserialize)]
struct ServiceInfo {
    vendor: Cow<'static, str>,
    product: Cow<'static, str>,
    version: Cow<'static, str>,
    url: Cow<'static, str>,
    interfaces: Vec<Cow<'static, str>>,
}

pub struct VarlinkService {
    info: ServiceInfo,
    ifaces: HashMap<Cow<'static, str>, Box<Interface + Send + Sync>>,
}

impl Interface for VarlinkService {
    fn get_description(&self) -> &'static str {
        r#"
# The Varlink Service Interface is provided by every varlink service. It
# describes the service and the interfaces it implements.
interface org.varlink.service

# Get a list of all the interfaces a service provides and information
# about the implementation.
method GetInfo() -> (
  vendor: string,
  product: string,
  version: string,
  url: string,
  interfaces: string[]
)

# Get the description of an interface that is implemented by this service.
method GetInterfaceDescription(interface: string) -> (description: string)

# The requested interface was not found.
error InterfaceNotFound (interface: string)

# The requested method was not found
error MethodNotFound (method: string)

# The interface defines the requested method, but the service does not
# implement it.
error MethodNotImplemented (method: string)

# One of the passed parameters is invalid.
error InvalidParameter (parameter: string)
	"#
    }

    fn get_name(&self) -> &'static str {
        "org.varlink.service"
    }

    fn call(&self, call: &mut Call) -> io::Result<()> {
        let req = call.request.unwrap();
        match req.method.as_ref() {
            "org.varlink.service.GetInfo" => {
                return call.reply_parameters(serde_json::to_value(&self.info)?);
            }
            "org.varlink.service.GetInterfaceDescription" => {
                if req.parameters == None {
                    return call.reply_invalid_parameter(None);
                }
                if let Some(val) = req.parameters.clone() {
                    let args: GetInterfaceArgs = serde_json::from_value(val)?;
                    match args.interface.as_ref() {
                        "org.varlink.service" => {
                            return call.reply_parameters(
                                json!({"description": self.get_description()}),
                            );
                        }
                        key => {
                            if self.ifaces.contains_key(key) {
                                return call.reply_parameters(
                                    json!({"description": self.ifaces[key].get_description()}),
                                );
                            } else {
                                return call.reply_invalid_parameter(Some("interface".into()));
                            }
                        }
                    }
                } else {
                    return call.reply_invalid_parameter(Some("interface".into()));
                }
            }
            _ => {
                let method: String = req.method.clone().into();
                let n: usize = match method.rfind('.') {
                    None => 0,
                    Some(x) => x + 1,
                };
                let m = String::from(&method[n..]);
                return call.reply_method_not_found(Some(m.into()));
            }
        }
    }
    fn call_upgraded(&self, call: &mut Call) -> io::Result<()> {
        call.upgraded = false;
        Ok(())
    }
}

impl VarlinkService {
    pub fn new(
        vendor: &str,
        product: &str,
        version: &str,
        url: &str,
        ifaces: Vec<Box<Interface + Send + Sync>>,
    ) -> Self {
        let mut ifhashmap = HashMap::<Cow<'static, str>, Box<Interface + Send + Sync>>::new();
        for i in ifaces {
            ifhashmap.insert(i.get_name().into(), i);
        }
        let mut ifnames: Vec<Cow<'static, str>> = Vec::new();
        ifnames.push("org.varlink.service".into());
        ifnames.extend(
            ifhashmap
                .keys()
                .map(|i| Cow::<'static, str>::from(i.clone())),
        );
        VarlinkService {
            info: ServiceInfo {
                vendor: String::from(vendor).into(),
                product: String::from(product).into(),
                version: String::from(version).into(),
                url: String::from(url).into(),
                interfaces: ifnames,
            },
            ifaces: ifhashmap,
        }
    }

    fn call(&self, iface: String, call: &mut Call) -> io::Result<()> {
        match iface.as_ref() {
            "org.varlink.service" => return self::Interface::call(self, call),
            key => {
                if self.ifaces.contains_key(key) {
                    return self.ifaces[key].call(call);
                } else {
                    return call.reply_interface_not_found(Some(iface.clone().into()));
                }
            }
        }
    }

    fn call_upgraded(&self, iface: String, call: &mut Call) -> io::Result<()> {
        match iface.as_ref() {
            "org.varlink.service" => return self::Interface::call_upgraded(self, call),
            key => {
                if self.ifaces.contains_key(key) {
                    return self.ifaces[key].call_upgraded(call);
                } else {
                    return call.reply_interface_not_found(Some(iface.clone().into()));
                }
            }
        }
    }

    pub fn handle(&self, reader: &mut Read, writer: &mut Write) -> io::Result<()> {
        let mut bufreader = BufReader::new(reader);
        let mut upgraded = false;
        let mut last_iface = String::from("");
        loop {
            if !upgraded {
                let mut buf = Vec::new();
                let read_bytes = bufreader.read_until(b'\0', &mut buf)?;
                if read_bytes > 0 {
                    buf.pop();
                    let req: Request = serde_json::from_slice(&buf)?;
                    let mut call = Call::new(writer, &req);

                    let n: usize = match req.method.rfind('.') {
                        None => {
                            let method = req.method.clone();
                            return call.reply_interface_not_found(Some(method.into()));
                        }
                        Some(x) => x,
                    };

                    let iface = String::from(&req.method[..n]);

                    self.call(iface.clone(), &mut call)?;
                    upgraded = call.upgraded;
                    if upgraded {
                        last_iface = iface;
                    }
                } else {
                    break;
                }
            } else {
                let mut call = Call::new_upgraded(writer);
                self.call_upgraded(last_iface.clone(), &mut call)?;
                upgraded = call.upgraded;
            }
        }
        Ok(())
    }
}
<commit_msg>add Call.set_continues()<commit_after>//! client and server support for the varlink protocol

extern crate bytes;
extern crate itertools;

extern crate libc;
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;
extern crate unix_socket;

extern crate varlink_parser;

pub mod generator;
pub mod server;

use serde_json::Value;
use serde::ser::Serialize;

use std::convert::From;
use std::collections::HashMap;
use std::borrow::Cow;
use std::io::{self, BufRead, BufReader, Error, ErrorKind, Read, Write};

pub trait Interface {
    fn get_description(&self) -> &'static str;
    fn get_name(&self) -> &'static str;
    fn call(&self, &mut Call) -> io::Result<()>;
    fn call_upgraded(&self, &mut Call) -> io::Result<()>;
}

#[derive(Serialize, Deserialize, Debug, Default)]
pub struct Request {
    pub more: Option<bool>,
    pub oneshot: Option<bool>,
    pub upgrade: Option<bool>,
    pub method: Cow<'static, str>,
    pub parameters: Option<Value>,
}

pub trait VarlinkReply {}

#[derive(Serialize, Deserialize)]
pub struct Reply {
    continues: Option<bool>,
    upgraded: Option<bool>,
    error: Option<Cow<'static, str>>,
    parameters: Option<Value>,
}

impl Reply {
    pub fn parameters(parameters: Option<Value>) -> Self {
        Reply {
            continues: None,
            upgraded: None,
            error: None,
            parameters: parameters,
        }
    }

    pub fn error(name: Cow<'static, str>, parameters: Option<Value>) -> Self {
        Reply {
            continues: None,
            upgraded: None,
            error: Some(name),
            parameters,
        }
    }
}

impl<T> From<T> for Reply
where
    T: VarlinkReply + Serialize,
{
    fn from(a: T) -> Self {
        Reply::parameters(Some(serde_json::to_value(a).unwrap()))
    }
}

pub struct Call<'a> {
    writer: &'a mut Write,
    pub request: Option<&'a Request>,
    pub continues: bool,
    pub upgraded: bool,
}

pub trait CallTrait {
    fn reply_struct(&mut self, Reply) -> io::Result<()>;

    fn set_continues(&mut self, cont: bool);

    fn reply_method_not_found(&mut self, arg: Option<String>) -> io::Result<()> {
        self.reply_struct(Reply::error(
            "org.varlink.service.MethodNotFound".into(),
            match arg {
                Some(a) => {
                    let s = format!("{{  \"method\" : \"{}\" }}", a);
                    Some(serde_json::from_str(s.as_ref()).unwrap())
                }
                None => None,
            },
        ))
    }

    fn reply_method_not_implemented(&mut self, arg: Option<String>) -> io::Result<()> {
        self.reply_struct(Reply::error(
            "org.varlink.service.MethodNotImplemented".into(),
            match arg {
                Some(a) => {
                    let s = format!("{{  \"method\" : \"{}\" }}", a);
                    Some(serde_json::from_str(s.as_ref()).unwrap())
                }
                None => None,
            },
        ))
    }

    fn reply_invalid_parameter(&mut self, arg: Option<String>) -> io::Result<()> {
        self.reply_struct(Reply::error(
            "org.varlink.service.InvalidParameter".into(),
            match arg {
                Some(a) => {
                    let s = format!("{{  \"parameter\" : \"{}\" }}", a);
                    Some(serde_json::from_str(s.as_ref()).unwrap())
                }
                None => None,
            },
        ))
    }
}

impl<'a> CallTrait for Call<'a> {
    fn reply_struct(&mut self, mut reply: Reply) -> io::Result<()> {
        if self.continues && !self.wants_more() {
            return Err(Error::new(
                ErrorKind::Other,
                "Call::reply() called with continues, but without more in the request",
            ));
        }
        if self.continues {
            reply.continues = Some(true);
        }
        let mut buf = serde_json::to_vec(&reply)?;
        buf.push(0);
        self.writer.write_all(&mut buf)?;
        self.writer.flush()?;
        Ok(())
    }
    fn set_continues(&mut self, cont: bool) {
        self.continues = cont;
    }
}

impl<'a> Call<'a> {
    fn new(writer: &'a mut Write, request: &'a Request) -> Self {
        Call {
            writer,
            request: Some(request),
            continues: false,
            upgraded: false,
        }
    }
    fn new_upgraded(writer: &'a mut Write) -> Self {
        Call {
            writer,
            request: None,
            continues: false,
            upgraded: false,
        }
    }
    pub fn is_oneshot(&self) -> bool {
        match self.request {
            Some(req) => {
                if let Some(val) = req.oneshot {
                    val
                } else {
                    false
                }
            }
            None => false,
        }
    }

    pub fn wants_more(&self) -> bool {
        match self.request {
            Some(req) => if let Some(val) = req.more {
                val
            } else {
                false
            },
            None => false,
        }
    }

    fn reply_interface_not_found(&mut self, arg: Option<String>) -> io::Result<()> {
        self.reply_struct(Reply::error(
            "org.varlink.service.InterfaceNotFound".into(),
            match arg {
                Some(a) => {
                    let s = format!("{{  \"interface\" : \"{}\" }}", a);
                    Some(serde_json::from_str(s.as_ref()).unwrap())
                }
                None => None,
            },
        ))
    }

    fn reply_parameters(&mut self, parameters: Value) -> io::Result<()> {
        let reply = Reply::parameters(Some(parameters));
        let mut buf = serde_json::to_vec(&reply)?;
        buf.push(0);
        self.writer.write_all(&mut buf)?;
        self.writer.flush()?;
        Ok(())
    }
}

#[derive(Deserialize)]
struct GetInterfaceArgs {
    interface: Cow<'static, str>,
}

#[derive(Serialize, Deserialize)]
struct ServiceInfo {
    vendor: Cow<'static, str>,
    product: Cow<'static, str>,
    version: Cow<'static, str>,
    url: Cow<'static, str>,
    interfaces: Vec<Cow<'static, str>>,
}

pub struct VarlinkService {
    info: ServiceInfo,
    ifaces: HashMap<Cow<'static, str>, Box<Interface + Send + Sync>>,
}

impl Interface for VarlinkService {
    fn get_description(&self) -> &'static str {
        r#"
# The Varlink Service Interface is provided by every varlink service. It
# describes the service and the interfaces it implements.
interface org.varlink.service

# Get a list of all the interfaces a service provides and information
# about the implementation.
method GetInfo() -> (
  vendor: string,
  product: string,
  version: string,
  url: string,
  interfaces: string[]
)

# Get the description of an interface that is implemented by this service.
method GetInterfaceDescription(interface: string) -> (description: string)

# The requested interface was not found.
error InterfaceNotFound (interface: string)

# The requested method was not found
error MethodNotFound (method: string)

# The interface defines the requested method, but the service does not
# implement it.
error MethodNotImplemented (method: string)

# One of the passed parameters is invalid.
error InvalidParameter (parameter: string)
	"#
    }

    fn get_name(&self) -> &'static str {
        "org.varlink.service"
    }

    fn call(&self, call: &mut Call) -> io::Result<()> {
        let req = call.request.unwrap();
        match req.method.as_ref() {
            "org.varlink.service.GetInfo" => {
                return call.reply_parameters(serde_json::to_value(&self.info)?);
            }
            "org.varlink.service.GetInterfaceDescription" => {
                if req.parameters == None {
                    return call.reply_invalid_parameter(None);
                }
                if let Some(val) = req.parameters.clone() {
                    let args: GetInterfaceArgs = serde_json::from_value(val)?;
                    match args.interface.as_ref() {
                        "org.varlink.service" => {
                            return call.reply_parameters(
                                json!({"description": self.get_description()}),
                            );
                        }
                        key => {
                            if self.ifaces.contains_key(key) {
                                return call.reply_parameters(
                                    json!({"description": self.ifaces[key].get_description()}),
                                );
                            } else {
                                return call.reply_invalid_parameter(Some("interface".into()));
                            }
                        }
                    }
                } else {
                    return call.reply_invalid_parameter(Some("interface".into()));
                }
            }
            _ => {
                let method: String = req.method.clone().into();
                let n: usize = match method.rfind('.') {
                    None => 0,
                    Some(x) => x + 1,
                };
                let m = String::from(&method[n..]);
                return call.reply_method_not_found(Some(m.into()));
            }
        }
    }
    fn call_upgraded(&self, call: &mut Call) -> io::Result<()> {
        call.upgraded = false;
        Ok(())
    }
}

impl VarlinkService {
    pub fn new(
        vendor: &str,
        product: &str,
        version: &str,
        url: &str,
        ifaces: Vec<Box<Interface + Send + Sync>>,
    ) -> Self {
        let mut ifhashmap = HashMap::<Cow<'static, str>, Box<Interface + Send + Sync>>::new();
        for i in ifaces {
            ifhashmap.insert(i.get_name().into(), i);
        }
        let mut ifnames: Vec<Cow<'static, str>> = Vec::new();
        ifnames.push("org.varlink.service".into());
        ifnames.extend(
            ifhashmap
                .keys()
                .map(|i| Cow::<'static, str>::from(i.clone())),
        );
        VarlinkService {
            info: ServiceInfo {
                vendor: String::from(vendor).into(),
                product: String::from(product).into(),
                version: String::from(version).into(),
                url: String::from(url).into(),
                interfaces: ifnames,
            },
            ifaces: ifhashmap,
        }
    }

    fn call(&self, iface: String, call: &mut Call) -> io::Result<()> {
        match iface.as_ref() {
            "org.varlink.service" => return self::Interface::call(self, call),
            key => {
                if self.ifaces.contains_key(key) {
                    return self.ifaces[key].call(call);
                } else {
                    return call.reply_interface_not_found(Some(iface.clone().into()));
                }
            }
        }
    }

    fn call_upgraded(&self, iface: String, call: &mut Call) -> io::Result<()> {
        match iface.as_ref() {
            "org.varlink.service" => return self::Interface::call_upgraded(self, call),
            key => {
                if self.ifaces.contains_key(key) {
                    return self.ifaces[key].call_upgraded(call);
                } else {
                    return call.reply_interface_not_found(Some(iface.clone().into()));
                }
            }
        }
    }

    pub fn handle(&self, reader: &mut Read, writer: &mut Write) -> io::Result<()> {
        let mut bufreader = BufReader::new(reader);
        let mut upgraded = false;
        let mut last_iface = String::from("");
        loop {
            if !upgraded {
                let mut buf = Vec::new();
                let read_bytes = bufreader.read_until(b'\0', &mut buf)?;
                if read_bytes > 0 {
                    buf.pop();
                    let req: Request = serde_json::from_slice(&buf)?;
                    let mut call = Call::new(writer, &req);

                    let n: usize = match req.method.rfind('.') {
                        None => {
                            let method = req.method.clone();
                            return call.reply_interface_not_found(Some(method.into()));
                        }
                        Some(x) => x,
                    };

                    let iface = String::from(&req.method[..n]);

                    self.call(iface.clone(), &mut call)?;
                    upgraded = call.upgraded;
                    if upgraded {
                        last_iface = iface;
                    }
                } else {
                    break;
                }
            } else {
                let mut call = Call::new_upgraded(writer);
                self.call_upgraded(last_iface.clone(), &mut call)?;
                upgraded = call.upgraded;
            }
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>extern crate structopt;
#[macro_use]
extern crate structopt_derive;

use structopt::StructOpt;

type Position = (i64, i64);

fn location(num: u64) -> Position {
    if num < 2 {
        // 0 and 1 are kind of special cases
        return (0, 0);
    }
    // edge_len is next even number after square root
    // it is defined such that the "circumference" of the square = 4 * edge_len
    // note that this means a 5x5 square has edge_len 4
    let edge_len = {
        let x = (num as f64).sqrt().ceil() as u64;
        if x % 2 == 0 {
            x
        } else {
            x - 1
        }
    };

    // last point on that square. Next square "starts" one point to the right.
    let square_end = (edge_len as i64 / 2, -(edge_len as i64 / 2));
    // amount "into" this square that num is--i.e. subtract off previous squares
    let into_square = num - (edge_len - 1) * (edge_len - 1);
    let y_diff = if into_square < edge_len {
        into_square
    } else if into_square < edge_len * 2 {
        edge_len
    } else if into_square < edge_len * 3 {
        edge_len - (into_square - edge_len * 2)
    } else {
        0
    };
    // make this positive, even though it is in negative direction
    let x_diff = if into_square < edge_len {
        0
    } else if into_square < edge_len * 2 {
        into_square - edge_len
    } else if into_square < edge_len * 3 {
        edge_len
    } else {
        edge_len - (into_square - edge_len * 3)
    };
    (square_end.0 - x_diff as i64, square_end.1 + y_diff as i64)
}

fn manhattan_distance(a: Position, b: Position) -> u64 {
    ((a.0 - b.0).abs() + (a.1 - b.1).abs()) as u64
}

fn part1(input: u64) -> u64 {
    let loc = location(input);
    manhattan_distance(loc, (0, 0))
}

fn main() {
    let opt = Opt::from_args();
    println!(
        "Part 1: {}",
        part1(
            opt.input
                .parse::<u64>()
                .expect("could not parse argument as unsigned integer")
        )
    );
}

#[derive(StructOpt, Debug)]
#[structopt(name = "day03", about = "Advent of code 2017 day 03")]
struct Opt {
    #[structopt(help = "Input")] input: String,
}

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

    #[test]
    fn location_test() {
        assert_eq!(location(1), (0, 0));
        assert_eq!(location(10), (2, -1));
        assert_eq!(location(13), (2, 2));
        assert_eq!(location(14), (1, 2));
        assert_eq!(location(17), (-2, 2));
        assert_eq!(location(18), (-2, 1));
        assert_eq!(location(21), (-2, -2));
        assert_eq!(location(22), (-1, -2));
        assert_eq!(location(25), (2, -2));
    }

    #[test]
    fn part1_test() {
        assert_eq!(part1(1), 0);
        assert_eq!(part1(12), 3);
        assert_eq!(part1(23), 2);
        assert_eq!(part1(1024), 31);
    }
}
<commit_msg>2017 day 03 part 02<commit_after>extern crate structopt;
#[macro_use]
extern crate structopt_derive;

use structopt::StructOpt;

use std::collections::BTreeMap;

type Position = (i64, i64);

fn location(num: u64) -> Position {
    if num < 2 {
        // 0 and 1 are kind of special cases
        return (0, 0);
    }
    // edge_len is next even number after square root
    // it is defined such that the "circumference" of the square = 4 * edge_len
    // note that this means a 5x5 square has edge_len 4
    let edge_len = {
        let x = (num as f64).sqrt().ceil() as u64;
        if x % 2 == 0 {
            x
        } else {
            x - 1
        }
    };

    // last point on that square. Next square "starts" one point to the right.
    let square_end = (edge_len as i64 / 2, -(edge_len as i64 / 2));
    // amount "into" this square that num is--i.e. subtract off previous squares
    let into_square = num - (edge_len - 1) * (edge_len - 1);
    let y_diff = if into_square < edge_len {
        into_square
    } else if into_square < edge_len * 2 {
        edge_len
    } else if into_square < edge_len * 3 {
        edge_len - (into_square - edge_len * 2)
    } else {
        0
    };
    // make this positive, even though it is in negative direction
    let x_diff = if into_square < edge_len {
        0
    } else if into_square < edge_len * 2 {
        into_square - edge_len
    } else if into_square < edge_len * 3 {
        edge_len
    } else {
        edge_len - (into_square - edge_len * 3)
    };
    (square_end.0 - x_diff as i64, square_end.1 + y_diff as i64)
}

fn manhattan_distance(a: Position, b: Position) -> u64 {
    ((a.0 - b.0).abs() + (a.1 - b.1).abs()) as u64
}

fn part1(input: u64) -> u64 {
    let loc = location(input);
    manhattan_distance(loc, (0, 0))
}

fn index(loc: Position) -> u64 {
    if loc == (0, 0) {
        // special case
        return 1;
    }
    let edge_dist = i64::max(loc.0.abs(), loc.1.abs());
    let edge_len = edge_dist * 2;
    let square_sum = (edge_len + 1) * (edge_len + 1);
    (if loc.1 == -edge_dist {
        // bottom edge
        square_sum - edge_dist + loc.0
    } else if loc.0 == -edge_dist {
        // left edge
        square_sum - edge_len - edge_dist - loc.1
    } else if loc.1 == edge_dist {
        // upper edge
        square_sum - edge_len * 2 - edge_dist - loc.0
    } else {
        // right edge
        square_sum - edge_len * 3 - edge_dist + loc.1
    }) as u64
}

fn value(loc: Position, mut memo: &mut BTreeMap<Position, u64>) -> u64 {
    if loc == (0, 0) {
        // base case
        return 1;
    }
    if memo.contains_key(&loc) {
        return memo[&loc];
    }
    let neighbors = vec![
        (loc.0 - 1, loc.1 + 1),
        (loc.0, loc.1 + 1),
        (loc.0 + 1, loc.1 + 1),
        (loc.0 - 1, loc.1),
        (loc.0 + 1, loc.1),
        (loc.0 - 1, loc.1 - 1),
        (loc.0, loc.1 - 1),
        (loc.0 + 1, loc.1 - 1),
    ];
    let val = neighbors
        .into_iter()
        .filter(|pos| index(*pos) < index(loc))
        .map(|pos| value(pos, &mut memo))
        .sum();
    memo.insert(loc, val);
    val
}

fn part2(target: u64) -> u64 {
    let mut memo = BTreeMap::new();
    let mut idx = 1;
    loop {
        let loc = location(idx);
        let val = value(loc, &mut memo);
        if val > target {
            return val;
        }
        idx += 1;
    }
}

fn main() {
    let opt = Opt::from_args();
    let input = opt.input
        .parse::<u64>()
        .expect("could not parse argument as unsigned integer");
    println!("Part 1: {}", part1(input));
    println!("Part 2: {}", part2(input));
}

#[derive(StructOpt, Debug)]
#[structopt(name = "day03", about = "Advent of code 2017 day 03")]
struct Opt {
    #[structopt(help = "Input")] input: String,
}

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

    #[test]
    fn location_test() {
        assert_eq!(location(1), (0, 0));
        assert_eq!(location(10), (2, -1));
        assert_eq!(location(13), (2, 2));
        assert_eq!(location(14), (1, 2));
        assert_eq!(location(17), (-2, 2));
        assert_eq!(location(18), (-2, 1));
        assert_eq!(location(21), (-2, -2));
        assert_eq!(location(22), (-1, -2));
        assert_eq!(location(25), (2, -2));
    }

    #[test]
    fn part1_test() {
        assert_eq!(part1(1), 0);
        assert_eq!(part1(12), 3);
        assert_eq!(part1(23), 2);
        assert_eq!(part1(1024), 31);
    }

    #[test]
    fn index_test() {
        assert_eq!(index((0, 0)), 1);
        assert_eq!(index((2, -1)), 10);
        assert_eq!(index((2, 2)), 13);
        assert_eq!(index((1, 2)), 14);
        assert_eq!(index((-2, 2)), 17);
        assert_eq!(index((-2, 1)), 18);
        assert_eq!(index((-2, -2)), 21);
        assert_eq!(index((-1, -2)), 22);
        assert_eq!(index((2, -2)), 25);
    }

    #[test]
    fn value_test() {
        assert_eq!(value((0, 0), &mut BTreeMap::new()), 1);
        assert_eq!(value((2, -1), &mut BTreeMap::new()), 26);
        assert_eq!(value((2, 2), &mut BTreeMap::new()), 59);
        assert_eq!(value((1, 2), &mut BTreeMap::new()), 122);
        assert_eq!(value((-2, 2), &mut BTreeMap::new()), 147);
        assert_eq!(value((-2, 1), &mut BTreeMap::new()), 304);
        assert_eq!(value((-2, -2), &mut BTreeMap::new()), 362);
        assert_eq!(value((-1, -2), &mut BTreeMap::new()), 747);
    }
}
<|endoftext|>


<commit_before>pub const MAX_FIXED_ORDER: usize = 4;

pub struct SubFrame {
  pub data: Data,
  pub wasted_bits: u32,
}

pub enum Data {
  Constant(i32),
  Verbatim(Vec<i32>),
  Fixed(Fixed),
}

pub struct Fixed {
  pub entropy_coding_method: EntropyCodingMethod,
  pub order: u8,
  pub warmup: [i32; MAX_FIXED_ORDER],
  pub residual: Vec<i32>,
}

pub struct EntropyCodingMethod {
  pub method_type: CodingMethod,
  pub data: PartitionedRice,
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum CodingMethod {
  PartitionedRice,
  PartitionedRice2,
}

pub struct PartitionedRice {
  pub order: u32,
  pub contents: PartitionedRiceContents,
}

pub struct PartitionedRiceContents {
  pub parameters: Vec<u32>,
  pub raw_bits: Vec<u32>,
  pub capacity_by_order: u32,
}
<commit_msg>Add new initializer for `PartitionedRiceContents`<commit_after>pub const MAX_FIXED_ORDER: usize = 4;

pub struct SubFrame {
  pub data: Data,
  pub wasted_bits: u32,
}

pub enum Data {
  Constant(i32),
  Verbatim(Vec<i32>),
  Fixed(Fixed),
}

pub struct Fixed {
  pub entropy_coding_method: EntropyCodingMethod,
  pub order: u8,
  pub warmup: [i32; MAX_FIXED_ORDER],
  pub residual: Vec<i32>,
}

pub struct EntropyCodingMethod {
  pub method_type: CodingMethod,
  pub data: PartitionedRice,
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum CodingMethod {
  PartitionedRice,
  PartitionedRice2,
}

pub struct PartitionedRice {
  pub order: u32,
  pub contents: PartitionedRiceContents,
}

pub struct PartitionedRiceContents {
  pub parameters: Vec<u32>,
  pub raw_bits: Vec<u32>,
  pub capacity_by_order: u32,
}

impl PartitionedRiceContents {
  pub fn new(capacity_by_order: u32) -> PartitionedRiceContents {
    let capacity       = 2_usize.pow(capacity_by_order);
    let mut parameters = Vec::with_capacity(capacity);
    let mut raw_bits   = Vec::with_capacity(capacity);

    unsafe {
      parameters.set_len(capacity);
      raw_bits.set_len(capacity);
    }

    PartitionedRiceContents {
      parameters: parameters,
      raw_bits: raw_bits,
      capacity_by_order: capacity_by_order,
    }
  }
}
<|endoftext|>


<commit_before>//! Various data structures used by the Rust compiler. The intention
//! is that code in here should be not be *specific* to rustc, so that
//! it can be easily unit tested and so forth.
//!
//! # Note
//!
//! This API is completely unstable and subject to change.

#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(array_windows)]
#![feature(control_flow_enum)]
#![feature(in_band_lifetimes)]
#![feature(unboxed_closures)]
#![feature(generator_trait)]
#![feature(fn_traits)]
#![feature(int_bits_const)]
#![feature(min_specialization)]
#![feature(optin_builtin_traits)]
#![feature(nll)]
#![feature(allow_internal_unstable)]
#![feature(hash_raw_entry)]
#![feature(stmt_expr_attributes)]
#![feature(core_intrinsics)]
#![feature(test)]
#![feature(associated_type_bounds)]
#![feature(thread_id_value)]
#![feature(extend_one)]
#![feature(const_panic)]
#![feature(min_const_generics)]
#![feature(once_cell)]
#![feature(maybe_uninit_uninit_array)]
#![allow(rustc::default_hash_types)]

#[macro_use]
extern crate tracing;
#[macro_use]
extern crate cfg_if;
#[macro_use]
extern crate rustc_macros;

#[inline(never)]
#[cold]
pub fn cold_path<F: FnOnce() -> R, R>(f: F) -> R {
    f()
}

#[macro_export]
macro_rules! likely {
    ($e:expr) => {
        #[allow(unused_unsafe)]
        {
            unsafe { std::intrinsics::likely($e) }
        }
    };
}

#[macro_export]
macro_rules! unlikely {
    ($e:expr) => {
        #[allow(unused_unsafe)]
        {
            unsafe { std::intrinsics::unlikely($e) }
        }
    };
}

pub mod base_n;
pub mod binary_search_util;
pub mod box_region;
pub mod captures;
pub mod const_cstr;
pub mod flock;
pub mod fx;
pub mod graph;
pub mod jobserver;
pub mod macros;
pub mod map_in_place;
pub mod obligation_forest;
pub mod owning_ref;
pub mod ptr_key;
pub mod sip128;
pub mod small_c_str;
pub mod snapshot_map;
pub mod stable_map;
pub mod svh;
pub use ena::snapshot_vec;
pub mod sorted_map;
pub mod stable_set;
#[macro_use]
pub mod stable_hasher;
mod atomic_ref;
pub mod fingerprint;
pub mod profiling;
pub mod sharded;
pub mod stack;
pub mod sync;
pub mod thin_vec;
pub mod tiny_list;
pub mod transitive_relation;
pub mod vec_linked_list;
pub mod work_queue;
pub use atomic_ref::AtomicRef;
pub mod frozen;
pub mod sso;
pub mod tagged_ptr;
pub mod temp_dir;
pub mod unhash;

pub use ena::undo_log;
pub use ena::unify;

pub struct OnDrop<F: Fn()>(pub F);

impl<F: Fn()> OnDrop<F> {
    /// Forgets the function which prevents it from running.
    /// Ensure that the function owns no memory, otherwise it will be leaked.
    #[inline]
    pub fn disable(self) {
        std::mem::forget(self);
    }
}

impl<F: Fn()> Drop for OnDrop<F> {
    #[inline]
    fn drop(&mut self) {
        (self.0)();
    }
}

// See comments in src/librustc_middle/lib.rs
#[doc(hidden)]
pub fn __noop_fix_for_27438() {}
<commit_msg>Move likely/unlikely argument outside of invisible unsafe block<commit_after>//! Various data structures used by the Rust compiler. The intention
//! is that code in here should be not be *specific* to rustc, so that
//! it can be easily unit tested and so forth.
//!
//! # Note
//!
//! This API is completely unstable and subject to change.

#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(array_windows)]
#![feature(control_flow_enum)]
#![feature(in_band_lifetimes)]
#![feature(unboxed_closures)]
#![feature(generator_trait)]
#![feature(fn_traits)]
#![feature(int_bits_const)]
#![feature(min_specialization)]
#![feature(optin_builtin_traits)]
#![feature(nll)]
#![feature(allow_internal_unstable)]
#![feature(hash_raw_entry)]
#![feature(stmt_expr_attributes)]
#![feature(core_intrinsics)]
#![feature(test)]
#![feature(associated_type_bounds)]
#![feature(thread_id_value)]
#![feature(extend_one)]
#![feature(const_panic)]
#![feature(min_const_generics)]
#![feature(once_cell)]
#![feature(maybe_uninit_uninit_array)]
#![allow(rustc::default_hash_types)]

#[macro_use]
extern crate tracing;
#[macro_use]
extern crate cfg_if;
#[macro_use]
extern crate rustc_macros;

#[inline(never)]
#[cold]
pub fn cold_path<F: FnOnce() -> R, R>(f: F) -> R {
    f()
}

#[macro_export]
macro_rules! likely {
    ($e:expr) => {
        match $e {
            #[allow(unused_unsafe)]
            e => unsafe { std::intrinsics::likely(e) },
        }
    };
}

#[macro_export]
macro_rules! unlikely {
    ($e:expr) => {
        match $e {
            #[allow(unused_unsafe)]
            e => unsafe { std::intrinsics::unlikely(e) },
        }
    };
}

pub mod base_n;
pub mod binary_search_util;
pub mod box_region;
pub mod captures;
pub mod const_cstr;
pub mod flock;
pub mod fx;
pub mod graph;
pub mod jobserver;
pub mod macros;
pub mod map_in_place;
pub mod obligation_forest;
pub mod owning_ref;
pub mod ptr_key;
pub mod sip128;
pub mod small_c_str;
pub mod snapshot_map;
pub mod stable_map;
pub mod svh;
pub use ena::snapshot_vec;
pub mod sorted_map;
pub mod stable_set;
#[macro_use]
pub mod stable_hasher;
mod atomic_ref;
pub mod fingerprint;
pub mod profiling;
pub mod sharded;
pub mod stack;
pub mod sync;
pub mod thin_vec;
pub mod tiny_list;
pub mod transitive_relation;
pub mod vec_linked_list;
pub mod work_queue;
pub use atomic_ref::AtomicRef;
pub mod frozen;
pub mod sso;
pub mod tagged_ptr;
pub mod temp_dir;
pub mod unhash;

pub use ena::undo_log;
pub use ena::unify;

pub struct OnDrop<F: Fn()>(pub F);

impl<F: Fn()> OnDrop<F> {
    /// Forgets the function which prevents it from running.
    /// Ensure that the function owns no memory, otherwise it will be leaked.
    #[inline]
    pub fn disable(self) {
        std::mem::forget(self);
    }
}

impl<F: Fn()> Drop for OnDrop<F> {
    #[inline]
    fn drop(&mut self) {
        (self.0)();
    }
}

// See comments in src/librustc_middle/lib.rs
#[doc(hidden)]
pub fn __noop_fix_for_27438() {}
<|endoftext|>


<commit_before>#![allow(unused)]

use interpreter::command::ExpressionOutput;
use std::time::Instant;
use std::fs::File;
use std::io::Read;
use interpreter::ast::*;
use interpreter::{Ident, DebugInfo};
use interpreter::Memory;

macro_rules! register_builtin(
    ($memory:ident, $name:ident, $($arg:ident),+) => (
        $memory.insert(
            Ident(concat!("$", stringify!($name)).to_string()),
            Expr::new(
                Node::Function(Box::new(Function {
                    name: Ident(concat!("$", stringify!($name)).to_string()),
                    args: vec![$(Ident(concat!("$", stringify!($arg)).to_string()))+],
                    body: vec![
                        Expr::new(Node::Builtin(Builtin($name)), DebugInfo::new(concat!("$", stringify!($name)), 0)),
                    ],
                })),
                DebugInfo::new(concat!("$", stringify!($name)), 0),
            ),
        );
    )
);

pub fn register_builtins(memory: &mut Memory) {
    register_builtin!(memory, exit, exitCode);
    register_builtin!(memory, echo, val);
    register_builtin!(memory, panic, val);
    register_builtin!(memory, type_of, val);
    register_builtin!(memory, string, val);
    register_builtin!(memory, int, val);
    register_builtin!(memory, float, val);
    register_builtin!(memory, boolean, val);
}

pub fn exit(memory: &mut Memory) -> Result<Expr, String> {
    let code = match Ident("$exitCode".to_string()).run(&mut DebugInfo::none(), memory)?.node {
        Node::Int(int) => int,
        _ => 0,
    };

    ::std::process::exit(code as i32);
}

pub fn string(memory: &mut Memory) -> Result<Expr, String> {
    let val = Ident("$val".to_string()).run(&mut DebugInfo::none(), memory)?;
    let str_val = match val.node {
        Node::Int(int) => int.to_string(),
        Node::Float(float) => float.to_string(),
        Node::Str(ref str_val) => str_val.clone(),
        Node::Bool(bool_val) => bool_val.to_string(),
        _ => {
            Err(
                val.debug.to_string() + ", which cannot be converted into a string",
            )?
        }
    };

    Ok(Expr::new(Node::Str(str_val), DebugInfo::none()))
}

pub fn int(memory: &mut Memory) -> Result<Expr, String> {
    let val = Ident("$val".to_string()).run(&mut DebugInfo::none(), memory)?;
    let int_val = match val.node {
        Node::Int(int) => int,
        Node::Float(float) => float as i64,
        Node::Str(ref str_val) => {
            str_val.parse().map_err(|_| {
                val.debug.to_string() + " with value " + str_val + " cannot be converted to an int."
            })?
        }
        Node::Bool(bool_val) => if bool_val { 1 } else { 0 },
        _ => {
            Err(
                val.debug.to_string() + ", which cannot be converted into an int",
            )?
        }
    };

    Ok(Expr::new(Node::Int(int_val), DebugInfo::none()))
}

pub fn float(memory: &mut Memory) -> Result<Expr, String> {
    let val = Ident("$val".to_string()).run(&mut DebugInfo::none(), memory)?;
    let float_val = match val.node {
        Node::Int(int) => int as f64,
        Node::Float(float) => float,
        Node::Str(ref str_val) => {
            str_val.parse().map_err(|_| {
                val.debug.to_string() + " with value " + str_val +
                    " cannot be converted to a float."
            })?
        }
        Node::Bool(bool_val) => if bool_val { 1.0 } else { 0.0 },
        _ => {
            Err(
                val.debug.to_string() + ", which cannot be converted into a float",
            )?
        }
    };

    Ok(Expr::new(Node::Float(float_val), DebugInfo::none()))
}

pub fn boolean(memory: &mut Memory) -> Result<Expr, String> {
    let val = Ident("$val".to_string()).run(&mut DebugInfo::none(), memory)?;
    let bool_val = match val.node {
        Node::Str(ref str_val) => {
            str_val.to_lowercase().parse().map_err(|_| {
                val.debug.to_string() + " with value " + str_val +
                    " cannot be converted to a boolean."
            })?
        }
        Node::Bool(bool_val) => bool_val,
        _ => {
            Err(
                val.debug.to_string() + ", which cannot be converted into a boolean",
            )?
        }
    };

    Ok(Expr::new(Node::Bool(bool_val), DebugInfo::none()))
}

pub fn echo(memory: &mut Memory) -> Result<Expr, String> {
    let val = Ident("$val".to_string()).run(&mut DebugInfo::none(), memory)?;
    let str_val = match val.node {
        Node::Int(int) => int.to_string(),
        Node::Float(float) => float.to_string(),
        Node::Str(ref str_val) => str_val.clone(),
        Node::Bool(bool_val) => bool_val.to_string(),
        ref node => format!("{:#?}", node),
    };

    println!("{}", str_val);

    Ok(Expr::new(Node::Noop, DebugInfo::none()))
}

pub fn panic(memory: &mut Memory) -> Result<Expr, String> {
    let val = Ident("$val".to_string()).run(&mut DebugInfo::none(), memory)?;
    let str_val = match val.node {
        Node::Str(ref str_val) => str_val.clone(),
        ref node => format!("{:#?}", node),
    };

    Err(format!("script panicked with error message \"{}\"", str_val))
}

pub fn type_of(memory: &mut Memory) -> Result<Expr, String> {
    let val = Ident("$val".to_string()).run(&mut DebugInfo::none(), memory)?;
    let str_val = match val.node {
        Node::Int(_) => "int".to_string(),
        Node::Float(_) => "float".to_string(),
        Node::Str(_) => "string".to_string(),
        Node::Bool(_) => "boolean".to_string(),
        Node::Array(_) => "array".to_string(),
        Node::Function(_) => "function".to_string(),
        ref node => format!("{:#?}", node),
    };

    Ok(Expr::new(Node::Str(str_val), DebugInfo::none()))
}

// pub fn echo(buffer: &str, args: Vec<&str>) -> Result<ExpressionOutput, String> {
//     let output = args.join(" ");
//     Ok(ExpressionOutput {
//         command: buffer.to_string(),
//         stdout: output.clone(),
//         stderr: "".to_string(),
//         interleaved: output,
//         started: Instant::now(),
//         completed: Instant::now(),
//     })
// }

// pub fn cat(buffer: &str, args: Vec<&str>) -> Result<ExpressionOutput, String> {
//     let mut output = String::new();
//     for file in args {
//         File::open(file)
//             .and_then(|mut file| file.read_to_string(&mut output))
//             .map_err(|err| format!("Error reading {}: {}", file, err))?;
//     }
//     Ok(ExpressionOutput {
//         command: buffer.to_string(),
//         stdout: output.clone(),
//         stderr: "".to_string(),
//         interleaved: output,
//         started: Instant::now(),
//         completed: Instant::now(),
//     })
// }
<commit_msg>add 'len' built-in to get length of array<commit_after>#![allow(unused)]

use interpreter::command::ExpressionOutput;
use std::time::Instant;
use std::fs::File;
use std::io::Read;
use interpreter::ast::*;
use interpreter::{Ident, DebugInfo};
use interpreter::Memory;

macro_rules! register_builtin(
    ($memory:ident, $name:ident, $($arg:ident),+) => (
        $memory.insert(
            Ident(concat!("$", stringify!($name)).to_string()),
            Expr::new(
                Node::Function(Box::new(Function {
                    name: Ident(concat!("$", stringify!($name)).to_string()),
                    args: vec![$(Ident(concat!("$", stringify!($arg)).to_string()))+],
                    body: vec![
                        Expr::new(Node::Builtin(Builtin($name)), DebugInfo::new(concat!("$", stringify!($name)), 0)),
                    ],
                })),
                DebugInfo::new(concat!("$", stringify!($name)), 0),
            ),
        );
    )
);

pub fn register_builtins(memory: &mut Memory) {
    register_builtin!(memory, exit, exitCode);
    register_builtin!(memory, echo, val);
    register_builtin!(memory, panic, msg);
    register_builtin!(memory, type_of, val);
    register_builtin!(memory, len, array);
    register_builtin!(memory, string, val);
    register_builtin!(memory, int, val);
    register_builtin!(memory, float, val);
    register_builtin!(memory, boolean, val);
}

pub fn exit(memory: &mut Memory) -> Result<Expr, String> {
    let code = match Ident("$exitCode".to_string()).run(&mut DebugInfo::none(), memory)?.node {
        Node::Int(int) => int,
        _ => 0,
    };

    ::std::process::exit(code as i32);
}

pub fn string(memory: &mut Memory) -> Result<Expr, String> {
    let val = Ident("$val".to_string()).run(&mut DebugInfo::none(), memory)?;
    let str_val = match val.node {
        Node::Int(int) => int.to_string(),
        Node::Float(float) => float.to_string(),
        Node::Str(ref str_val) => str_val.clone(),
        Node::Bool(bool_val) => bool_val.to_string(),
        _ => {
            Err(
                val.debug.to_string() + ", which cannot be converted into a string",
            )?
        }
    };

    Ok(Expr::new(Node::Str(str_val), DebugInfo::none()))
}

pub fn int(memory: &mut Memory) -> Result<Expr, String> {
    let val = Ident("$val".to_string()).run(&mut DebugInfo::none(), memory)?;
    let int_val = match val.node {
        Node::Int(int) => int,
        Node::Float(float) => float as i64,
        Node::Str(ref str_val) => {
            str_val.parse().map_err(|_| {
                val.debug.to_string() + " with value " + str_val + " cannot be converted to an int."
            })?
        }
        Node::Bool(bool_val) => if bool_val { 1 } else { 0 },
        _ => {
            Err(
                val.debug.to_string() + ", which cannot be converted into an int",
            )?
        }
    };

    Ok(Expr::new(Node::Int(int_val), DebugInfo::none()))
}

pub fn float(memory: &mut Memory) -> Result<Expr, String> {
    let val = Ident("$val".to_string()).run(&mut DebugInfo::none(), memory)?;
    let float_val = match val.node {
        Node::Int(int) => int as f64,
        Node::Float(float) => float,
        Node::Str(ref str_val) => {
            str_val.parse().map_err(|_| {
                val.debug.to_string() + " with value " + str_val +
                    " cannot be converted to a float."
            })?
        }
        Node::Bool(bool_val) => if bool_val { 1.0 } else { 0.0 },
        _ => {
            Err(
                val.debug.to_string() + ", which cannot be converted into a float",
            )?
        }
    };

    Ok(Expr::new(Node::Float(float_val), DebugInfo::none()))
}

pub fn boolean(memory: &mut Memory) -> Result<Expr, String> {
    let val = Ident("$val".to_string()).run(&mut DebugInfo::none(), memory)?;
    let bool_val = match val.node {
        Node::Str(ref str_val) => {
            str_val.to_lowercase().parse().map_err(|_| {
                val.debug.to_string() + " with value " + str_val +
                    " cannot be converted to a boolean."
            })?
        }
        Node::Bool(bool_val) => bool_val,
        _ => {
            Err(
                val.debug.to_string() + ", which cannot be converted into a boolean",
            )?
        }
    };

    Ok(Expr::new(Node::Bool(bool_val), DebugInfo::none()))
}

pub fn echo(memory: &mut Memory) -> Result<Expr, String> {
    let val = Ident("$val".to_string()).run(&mut DebugInfo::none(), memory)?;
    let str_val = match val.node {
        Node::Int(int) => int.to_string(),
        Node::Float(float) => float.to_string(),
        Node::Str(ref str_val) => str_val.clone(),
        Node::Bool(bool_val) => bool_val.to_string(),
        ref node => format!("{:#?}", node),
    };

    println!("{}", str_val);

    Ok(Expr::new(Node::Noop, DebugInfo::none()))
}

pub fn panic(memory: &mut Memory) -> Result<Expr, String> {
    let val = Ident("$msg".to_string()).run(&mut DebugInfo::none(), memory)?;
    let str_val = match val.node {
        Node::Str(ref str_val) => str_val.clone(),
        ref node => format!("{:#?}", node),
    };

    Err(format!("script panicked with error message \"{}\"", str_val))
}

pub fn type_of(memory: &mut Memory) -> Result<Expr, String> {
    let val = Ident("$val".to_string()).run(&mut DebugInfo::none(), memory)?;
    let str_val = match val.node {
        Node::Int(_) => "int".to_string(),
        Node::Float(_) => "float".to_string(),
        Node::Str(_) => "string".to_string(),
        Node::Bool(_) => "boolean".to_string(),
        Node::Array(_) => "array".to_string(),
        Node::Function(_) => "function".to_string(),
        ref node => format!("{:#?}", node),
    };

    Ok(Expr::new(Node::Str(str_val), DebugInfo::none()))
}

pub fn len(memory: &mut Memory) -> Result<Expr, String> {
    let val = Ident("$array".to_string()).run(&mut DebugInfo::none(), memory)?;
    let arr_len = match val.node {
        Node::Array(ref arr) => arr.len(),
        _ => {
            Err(
                val.debug.to_string() + ", which does not have a length",
            )?
        }
    };

    Ok(Expr::new(Node::Int(arr_len as i64), DebugInfo::none()))
}

// pub fn echo(buffer: &str, args: Vec<&str>) -> Result<ExpressionOutput, String> {
//     let output = args.join(" ");
//     Ok(ExpressionOutput {
//         command: buffer.to_string(),
//         stdout: output.clone(),
//         stderr: "".to_string(),
//         interleaved: output,
//         started: Instant::now(),
//         completed: Instant::now(),
//     })
// }

// pub fn cat(buffer: &str, args: Vec<&str>) -> Result<ExpressionOutput, String> {
//     let mut output = String::new();
//     for file in args {
//         File::open(file)
//             .and_then(|mut file| file.read_to_string(&mut output))
//             .map_err(|err| format!("Error reading {}: {}", file, err))?;
//     }
//     Ok(ExpressionOutput {
//         command: buffer.to_string(),
//         stdout: output.clone(),
//         stderr: "".to_string(),
//         interleaved: output,
//         started: Instant::now(),
//         completed: Instant::now(),
//     })
// }
<|endoftext|>


<commit_before>extern crate gimli;
extern crate getopts;
extern crate memmap;
extern crate object;

use object::Object;
use std::env;
use std::io;
use std::io::Write;
use std::fs;
use std::process;

#[derive(Default)]
struct Flags {
    info: bool,
    line: bool,
    aranges: bool,
    raw: bool,
}

fn print_usage(opts: &getopts::Options) -> ! {
    let brief = format!("Usage: {} <options> <file>", env::args().next().unwrap());
    write!(&mut io::stderr(), "{}", opts.usage(&brief)).ok();
    process::exit(1);
}

fn main() {
    let mut opts = getopts::Options::new();
    opts.optflag("i", "", "print .debug_info and .debug_types sections");
    opts.optflag("l", "", "print .debug_line section");
    opts.optflag("r", "", "print .debug_aranges section");
    opts.optflag("", "raw", "print raw data values");

    let matches = match opts.parse(env::args().skip(1)) {
        Ok(m) => m,
        Err(e) => {
            writeln!(&mut io::stderr(), "{:?}\n", e).ok();
            print_usage(&opts);
        }
    };
    if matches.free.is_empty() {
        print_usage(&opts);
    }

    let mut all = true;
    let mut flags = Flags::default();
    if matches.opt_present("i") {
        flags.info = true;
        all = false;
    }
    if matches.opt_present("l") {
        flags.line = true;
        all = false;
    }
    if matches.opt_present("r") {
        flags.aranges = true;
        all = false;
    }
    if matches.opt_present("raw") {
        flags.raw = true;
    }
    if all {
        flags.info = true;
        flags.line = true;
        flags.aranges = true;
    }

    for file_path in &matches.free {
        if matches.free.len() != 1 {
            println!("{}", file_path);
            println!("");
        }

        let file = fs::File::open(&file_path).expect("Should open file");
        let file = memmap::Mmap::open(&file, memmap::Protection::Read)
            .expect("Should create a mmap for file");
        let file = object::File::parse(unsafe { file.as_slice() });

        if file.is_little_endian() {
            dump_file::<gimli::LittleEndian>(file, &flags);
        } else {
            dump_file::<gimli::BigEndian>(file, &flags);
        }
    }
}

fn dump_file<Endian>(file: object::File, flags: &Flags)
    where Endian: gimli::Endianity
{
    let debug_abbrev = file.get_section(".debug_abbrev").unwrap_or(&[]);
    let debug_abbrev = gimli::DebugAbbrev::<Endian>::new(debug_abbrev);
    let debug_str = file.get_section(".debug_str").unwrap_or(&[]);
    let debug_str = gimli::DebugStr::<Endian>::new(debug_str);

    if flags.info {
        dump_info(&file, debug_abbrev, debug_str, flags);
        dump_types(&file, debug_abbrev, debug_str, flags);
    }
    if flags.line {
        dump_line(&file, debug_abbrev);
    }
    if flags.aranges {
        dump_aranges::<Endian>(&file);
    }
}

fn dump_info<Endian>(file: &object::File,
                     debug_abbrev: gimli::DebugAbbrev<Endian>,
                     debug_str: gimli::DebugStr<Endian>,
                     flags: &Flags)
    where Endian: gimli::Endianity
{
    if let Some(debug_info) = file.get_section(".debug_info") {
        println!(".debug_info");
        println!("");

        let debug_info = gimli::DebugInfo::<Endian>::new(&debug_info);

        let mut iter = debug_info.units();
        while let Some(unit) = iter.next().expect("Should parse compilation unit") {
            let abbrevs = unit.abbreviations(debug_abbrev)
                .expect("Error parsing abbreviations");

            dump_entries(unit.entries(&abbrevs), debug_str, flags);
        }
    }
}

fn dump_types<Endian>(file: &object::File,
                      debug_abbrev: gimli::DebugAbbrev<Endian>,
                      debug_str: gimli::DebugStr<Endian>,
                      flags: &Flags)
    where Endian: gimli::Endianity
{
    if let Some(debug_types) = file.get_section(".debug_types") {
        println!(".debug_types");
        println!("");

        let debug_types = gimli::DebugTypes::<Endian>::new(&debug_types);

        let mut iter = debug_types.units();
        while let Some(unit) = iter.next().expect("Should parse the unit OK") {
            let abbrevs = unit.abbreviations(debug_abbrev)
                .expect("Error parsing abbreviations");

            dump_entries(unit.entries(&abbrevs), debug_str, flags);
        }
    }
}

fn dump_entries<Endian>(mut entries: gimli::EntriesCursor<Endian>,
                        debug_str: gimli::DebugStr<Endian>,
                        flags: &Flags)
    where Endian: gimli::Endianity
{
    let mut depth = 0;
    while let Some((delta_depth, entry)) = entries.next_dfs().expect("Should parse next dfs") {
        depth += delta_depth;
        assert!(depth >= 0);
        let indent = depth as usize * 2 + 2;
        println!("<{:2}><0x{:08x}>{:indent$}{}",
                 depth,
                 entry.offset(),
                 "",
                 entry.tag(),
                 indent = indent);

        let mut attrs = entry.attrs();
        while let Some(attr) = attrs.next().expect("Should parse attribute OK") {
            print!("{:indent$}{:28}", "", attr.name(), indent = indent + 18);
            if flags.raw {
                println!("{:?}", attr.raw_value());
            } else {
                dump_attr_value(attr, debug_str);
            }
        }
    }
}

fn dump_attr_value<Endian>(attr: gimli::Attribute<Endian>, debug_str: gimli::DebugStr<Endian>)
    where Endian: gimli::Endianity
{
    let value = attr.value();
    match value {
        gimli::AttributeValue::Addr(address) => {
            println!("0x{:08x}", address);
        }
        gimli::AttributeValue::Sdata(data) => {
            println!("0x{:08x}", data);
        }
        gimli::AttributeValue::Udata(data) => {
            match attr.name() {
                gimli::DW_AT_high_pc => {
                    println!("<offset-from-lowpc>{}", data);
                }
                gimli::DW_AT_data_member_location |
                gimli::DW_AT_lower_bound |
                gimli::DW_AT_upper_bound => {
                    println!("{}", data);
                }
                _ => {
                    println!("0x{:08x}", data);
                }
            };
        }
        gimli::AttributeValue::Flag(true) => {
            // We don't record what the value was, so assume 1.
            println!("yes(1)");
        }
        gimli::AttributeValue::Flag(false) => {
            println!("no");
        }
        gimli::AttributeValue::SecOffset(offset) => {
            println!("0x{:08x}", offset);
        }
        gimli::AttributeValue::UnitRef(gimli::UnitOffset(offset)) => {
            println!("<0x{:08x}>", offset);
        }
        gimli::AttributeValue::DebugInfoRef(gimli::DebugInfoOffset(offset)) => {
            println!("0x{:08x}", offset);
        }
        gimli::AttributeValue::DebugLineRef(gimli::DebugLineOffset(offset)) => {
            println!("0x{:08x}", offset);
        }
        gimli::AttributeValue::DebugTypesRef(gimli::DebugTypesOffset(offset)) => {
            println!("0x{:08x}", offset);
        }
        gimli::AttributeValue::DebugStrRef(offset) => {
            if let Ok(s) = debug_str.get_str(offset) {
                println!("\"{}\"", s.to_string_lossy());
            } else {
                println!("{:?}", value);
            }
        }
        gimli::AttributeValue::String(s) => {
            println!("\"{}\"", s.to_string_lossy());
        }
        gimli::AttributeValue::Encoding(value) => {
            println!("{}", value);
        }
        gimli::AttributeValue::DecimalSign(value) => {
            println!("{}", value);
        }
        gimli::AttributeValue::Endianity(value) => {
            println!("{}", value);
        }
        gimli::AttributeValue::Accessibility(value) => {
            println!("{}", value);
        }
        gimli::AttributeValue::Visibility(value) => {
            println!("{}", value);
        }
        gimli::AttributeValue::Virtuality(value) => {
            println!("{}", value);
        }
        gimli::AttributeValue::Language(value) => {
            println!("{}", value);
        }
        gimli::AttributeValue::AddressClass(value) => {
            println!("{}", value);
        }
        gimli::AttributeValue::IdentifierCase(value) => {
            println!("{}", value);
        }
        gimli::AttributeValue::CallingConvention(value) => {
            println!("{}", value);
        }
        gimli::AttributeValue::Inline(value) => {
            println!("{}", value);
        }
        gimli::AttributeValue::Ordering(value) => {
            println!("{}", value);
        }
        gimli::AttributeValue::DiscrList(value) => {
            println!("{}", value);
        }
        _ => println!("{:?}", value),
    }
}

fn dump_line<Endian>(file: &object::File, debug_abbrev: gimli::DebugAbbrev<Endian>)
    where Endian: gimli::Endianity
{
    let debug_line = file.get_section(".debug_line");
    let debug_info = file.get_section(".debug_info");

    if let (Some(debug_line), Some(debug_info)) = (debug_line, debug_info) {
        println!(".debug_line");
        println!("");

        let debug_line = gimli::DebugLine::<Endian>::new(&debug_line);
        let debug_info = gimli::DebugInfo::<Endian>::new(&debug_info);

        let mut iter = debug_info.units();
        while let Some(unit) = iter.next().expect("Should parse unit header OK") {
            let abbrevs = unit.abbreviations(debug_abbrev)
                .expect("Error parsing abbreviations");

            let mut cursor = unit.entries(&abbrevs);
            cursor.next_dfs().expect("Should parse next dfs");

            let root = cursor.current().expect("Should have a root DIE");
            let offset = match root.attr_value(gimli::DW_AT_stmt_list) {
                Some(gimli::AttributeValue::DebugLineRef(offset)) => offset,
                _ => continue,
            };

            let header =
                gimli::LineNumberProgramHeader::new(debug_line, offset, unit.address_size());
            if let Ok(header) = header {
                println!("");
                println!("Offset:                             0x{:x}", offset.0);
                println!("Length:                             {}",
                         header.unit_length());
                println!("DWARF version:                      {}", header.version());
                println!("Prologue length:                    {}",
                         header.header_length());
                println!("Minimum instruction length:         {}",
                         header.minimum_instruction_length());
                println!("Maximum operations per instruction: {}",
                         header.maximum_operations_per_instruction());
                println!("Default is_stmt:                    {}",
                         header.default_is_stmt());
                println!("Line base:                          {}", header.line_base());
                println!("Line range:                         {}",
                         header.line_range());
                println!("Opcode base:                        {}",
                         header.opcode_base());

                println!("");
                println!("Opcodes:");
                for (i, length) in header.standard_opcode_lengths().iter().enumerate() {
                    println!("  Opcode {} as {} args", i + 1, length);
                }

                println!("");
                println!("The Directory Table:");
                for (i, dir) in header.include_directories().iter().enumerate() {
                    println!("  {} {}", i + 1, dir.to_string_lossy());
                }

                println!("");
                println!("The File Name Table");
                println!("  Entry\tDir\tTime\tSize\tName");
                for (i, file) in header.file_names().iter().enumerate() {
                    println!("  {}\t{}\t{}\t{}\t{}",
                             i + 1,
                             file.directory_index(),
                             file.last_modification(),
                             file.length(),
                             file.path_name().to_string_lossy());
                }

                println!("");
                println!("Line Number Statements:");
                let mut opcodes = header.opcodes();
                while let Some(opcode) = opcodes.next_opcode(&header)
                    .expect("Should parse opcode OK") {
                    println!("  {}", opcode);
                }

                println!("");
                println!("Line Number Rows:");
                println!("<pc>        [lno,col]");
                let mut state_machine = gimli::StateMachine::new(header);
                let mut file_index = 0;
                while let Some(row) = state_machine.next_row()
                    .expect("Should parse row OK") {
                    let line = row.line().unwrap_or(0);
                    let column = match row.column() {
                        gimli::ColumnType::Column(column) => column,
                        gimli::ColumnType::LeftEdge => 0,
                    };
                    print!("0x{:08x}  [{:4},{:2}]", row.address(), line, column);
                    if row.is_stmt() {
                        print!(" NS");
                    }
                    if row.basic_block() {
                        print!(" BB");
                    }
                    if row.end_sequence() {
                        print!(" ET");
                    }
                    if row.prologue_end() {
                        print!(" PE");
                    }
                    if row.epilogue_begin() {
                        print!(" EB");
                    }
                    if row.isa() != 0 {
                        print!(" IS={}", row.isa());
                    }
                    if row.discriminator() != 0 {
                        print!(" DI={}", row.discriminator());
                    }
                    if file_index != row.file_index() {
                        file_index = row.file_index();
                        if let Ok(file) = row.file() {
                            if let Some(directory) = file.directory(row.header()) {
                                print!(" uri: \"{}/{}\"",
                                       directory.to_string_lossy(),
                                       file.path_name().to_string_lossy());
                            } else {
                                print!(" uri: \"{}\"", file.path_name().to_string_lossy());
                            }
                        }
                    }
                    println!("");
                }
            }
        }
    }
}

fn dump_aranges<Endian>(file: &object::File)
    where Endian: gimli::Endianity
{
    let debug_aranges = file.get_section(".debug_aranges");
    let debug_info = file.get_section(".debug_info");

    if let (Some(debug_aranges), Some(debug_info)) = (debug_aranges, debug_info) {
        println!(".debug_aranges");
        println!("");

        let debug_aranges = gimli::DebugAranges::<Endian>::new(debug_aranges);
        let debug_info = gimli::DebugInfo::<Endian>::new(debug_info);

        let mut cu_die_offset = gimli::DebugInfoOffset(0);
        let mut prev_cu_offset = None;
        let mut aranges = debug_aranges.items();
        while let Some(arange) = aranges.next().expect("Should parse arange OK") {
            let cu_offset = arange.debug_info_offset();
            if Some(cu_offset) != prev_cu_offset {
                let cu = debug_info.header_from_offset(cu_offset)
                    .expect("Should parse unit header OK");
                cu_die_offset = gimli::DebugInfoOffset(cu_offset.0 + cu.header_size() as u64);
                prev_cu_offset = Some(cu_offset);
            }
            if let Some(segment) = arange.segment() {
                print!("arange starts at seg,off 0x{:08x},0x{:08x}, ",
                       segment,
                       arange.address());
            } else {
                print!("arange starts at 0x{:08x}, ", arange.address());
            }
            println!("length of 0x{:08x}, cu_die_offset = 0x{:08x}",
                     arange.length(),
                     cu_die_offset.0);
        }
    }
}
<commit_msg>dwarfdump: fixes to match libdwarf-dwarfdump 20160613<commit_after>extern crate gimli;
extern crate getopts;
extern crate memmap;
extern crate object;

use object::Object;
use std::env;
use std::io;
use std::io::Write;
use std::fs;
use std::process;

#[derive(Default)]
struct Flags {
    info: bool,
    line: bool,
    aranges: bool,
    raw: bool,
}

fn print_usage(opts: &getopts::Options) -> ! {
    let brief = format!("Usage: {} <options> <file>", env::args().next().unwrap());
    write!(&mut io::stderr(), "{}", opts.usage(&brief)).ok();
    process::exit(1);
}

fn main() {
    let mut opts = getopts::Options::new();
    opts.optflag("i", "", "print .debug_info and .debug_types sections");
    opts.optflag("l", "", "print .debug_line section");
    opts.optflag("r", "", "print .debug_aranges section");
    opts.optflag("", "raw", "print raw data values");

    let matches = match opts.parse(env::args().skip(1)) {
        Ok(m) => m,
        Err(e) => {
            writeln!(&mut io::stderr(), "{:?}\n", e).ok();
            print_usage(&opts);
        }
    };
    if matches.free.is_empty() {
        print_usage(&opts);
    }

    let mut all = true;
    let mut flags = Flags::default();
    if matches.opt_present("i") {
        flags.info = true;
        all = false;
    }
    if matches.opt_present("l") {
        flags.line = true;
        all = false;
    }
    if matches.opt_present("r") {
        flags.aranges = true;
        all = false;
    }
    if matches.opt_present("raw") {
        flags.raw = true;
    }
    if all {
        flags.info = true;
        flags.line = true;
        flags.aranges = true;
    }

    for file_path in &matches.free {
        if matches.free.len() != 1 {
            println!("{}", file_path);
            println!("");
        }

        let file = fs::File::open(&file_path).expect("Should open file");
        let file = memmap::Mmap::open(&file, memmap::Protection::Read)
            .expect("Should create a mmap for file");
        let file = object::File::parse(unsafe { file.as_slice() });

        if file.is_little_endian() {
            dump_file::<gimli::LittleEndian>(file, &flags);
        } else {
            dump_file::<gimli::BigEndian>(file, &flags);
        }
    }
}

fn dump_file<Endian>(file: object::File, flags: &Flags)
    where Endian: gimli::Endianity
{
    let debug_abbrev = file.get_section(".debug_abbrev").unwrap_or(&[]);
    let debug_abbrev = gimli::DebugAbbrev::<Endian>::new(debug_abbrev);
    let debug_str = file.get_section(".debug_str").unwrap_or(&[]);
    let debug_str = gimli::DebugStr::<Endian>::new(debug_str);

    if flags.info {
        dump_info(&file, debug_abbrev, debug_str, flags);
        dump_types(&file, debug_abbrev, debug_str, flags);
    }
    if flags.line {
        dump_line(&file, debug_abbrev);
    }
    if flags.aranges {
        dump_aranges::<Endian>(&file);
    }
}

fn dump_info<Endian>(file: &object::File,
                     debug_abbrev: gimli::DebugAbbrev<Endian>,
                     debug_str: gimli::DebugStr<Endian>,
                     flags: &Flags)
    where Endian: gimli::Endianity
{
    if let Some(debug_info) = file.get_section(".debug_info") {
        println!(".debug_info");
        println!("");

        let debug_info = gimli::DebugInfo::<Endian>::new(&debug_info);

        let mut iter = debug_info.units();
        while let Some(unit) = iter.next().expect("Should parse compilation unit") {
            let abbrevs = unit.abbreviations(debug_abbrev)
                .expect("Error parsing abbreviations");

            dump_entries(unit.entries(&abbrevs), debug_str, flags);
        }
    }
}

fn dump_types<Endian>(file: &object::File,
                      debug_abbrev: gimli::DebugAbbrev<Endian>,
                      debug_str: gimli::DebugStr<Endian>,
                      flags: &Flags)
    where Endian: gimli::Endianity
{
    if let Some(debug_types) = file.get_section(".debug_types") {
        println!(".debug_types");
        println!("");

        let debug_types = gimli::DebugTypes::<Endian>::new(&debug_types);

        let mut iter = debug_types.units();
        while let Some(unit) = iter.next().expect("Should parse the unit OK") {
            let abbrevs = unit.abbreviations(debug_abbrev)
                .expect("Error parsing abbreviations");

            dump_entries(unit.entries(&abbrevs), debug_str, flags);
        }
    }
}

fn dump_entries<Endian>(mut entries: gimli::EntriesCursor<Endian>,
                        debug_str: gimli::DebugStr<Endian>,
                        flags: &Flags)
    where Endian: gimli::Endianity
{
    let mut depth = 0;
    while let Some((delta_depth, entry)) = entries.next_dfs().expect("Should parse next dfs") {
        depth += delta_depth;
        assert!(depth >= 0);
        let indent = depth as usize * 2 + 2;
        println!("<{:2}><0x{:08x}>{:indent$}{}",
                 depth,
                 entry.offset(),
                 "",
                 entry.tag(),
                 indent = indent);

        let mut attrs = entry.attrs();
        while let Some(attr) = attrs.next().expect("Should parse attribute OK") {
            print!("{:indent$}{:28}", "", attr.name(), indent = indent + 18);
            if flags.raw {
                println!("{:?}", attr.raw_value());
            } else {
                dump_attr_value(attr, debug_str);
            }
        }
    }
}

fn dump_attr_value<Endian>(attr: gimli::Attribute<Endian>, debug_str: gimli::DebugStr<Endian>)
    where Endian: gimli::Endianity
{
    let value = attr.value();
    match value {
        gimli::AttributeValue::Addr(address) => {
            println!("0x{:08x}", address);
        }
        gimli::AttributeValue::Sdata(data) => {
            println!("0x{:08x}", data);
        }
        gimli::AttributeValue::Udata(data) => {
            match attr.name() {
                gimli::DW_AT_high_pc => {
                    println!("<offset-from-lowpc>{}", data);
                }
                gimli::DW_AT_data_member_location |
                gimli::DW_AT_lower_bound |
                gimli::DW_AT_upper_bound => {
                    println!("{}", data);
                }
                _ => {
                    println!("0x{:08x}", data);
                }
            };
        }
        gimli::AttributeValue::Flag(true) => {
            // We don't record what the value was, so assume 1.
            println!("yes(1)");
        }
        gimli::AttributeValue::Flag(false) => {
            println!("no");
        }
        gimli::AttributeValue::SecOffset(offset) => {
            println!("0x{:08x}", offset);
        }
        gimli::AttributeValue::UnitRef(gimli::UnitOffset(offset)) => {
            println!("<0x{:08x}>", offset);
        }
        gimli::AttributeValue::DebugInfoRef(gimli::DebugInfoOffset(offset)) => {
            println!("<GOFF=0x{:08x}>", offset);
        }
        gimli::AttributeValue::DebugLineRef(gimli::DebugLineOffset(offset)) => {
            println!("0x{:08x}", offset);
        }
        gimli::AttributeValue::DebugTypesRef(gimli::DebugTypesOffset(offset)) => {
            println!("0x{:08x}", offset);
        }
        gimli::AttributeValue::DebugStrRef(offset) => {
            if let Ok(s) = debug_str.get_str(offset) {
                println!("{}", s.to_string_lossy());
            } else {
                println!("{:?}", value);
            }
        }
        gimli::AttributeValue::String(s) => {
            println!("{}", s.to_string_lossy());
        }
        gimli::AttributeValue::Encoding(value) => {
            println!("{}", value);
        }
        gimli::AttributeValue::DecimalSign(value) => {
            println!("{}", value);
        }
        gimli::AttributeValue::Endianity(value) => {
            println!("{}", value);
        }
        gimli::AttributeValue::Accessibility(value) => {
            println!("{}", value);
        }
        gimli::AttributeValue::Visibility(value) => {
            println!("{}", value);
        }
        gimli::AttributeValue::Virtuality(value) => {
            println!("{}", value);
        }
        gimli::AttributeValue::Language(value) => {
            println!("{}", value);
        }
        gimli::AttributeValue::AddressClass(value) => {
            println!("{}", value);
        }
        gimli::AttributeValue::IdentifierCase(value) => {
            println!("{}", value);
        }
        gimli::AttributeValue::CallingConvention(value) => {
            println!("{}", value);
        }
        gimli::AttributeValue::Inline(value) => {
            println!("{}", value);
        }
        gimli::AttributeValue::Ordering(value) => {
            println!("{}", value);
        }
        gimli::AttributeValue::DiscrList(value) => {
            println!("{}", value);
        }
        _ => println!("{:?}", value),
    }
}

fn dump_line<Endian>(file: &object::File, debug_abbrev: gimli::DebugAbbrev<Endian>)
    where Endian: gimli::Endianity
{
    let debug_line = file.get_section(".debug_line");
    let debug_info = file.get_section(".debug_info");

    if let (Some(debug_line), Some(debug_info)) = (debug_line, debug_info) {
        println!(".debug_line");
        println!("");

        let debug_line = gimli::DebugLine::<Endian>::new(&debug_line);
        let debug_info = gimli::DebugInfo::<Endian>::new(&debug_info);

        let mut iter = debug_info.units();
        while let Some(unit) = iter.next().expect("Should parse unit header OK") {
            let abbrevs = unit.abbreviations(debug_abbrev)
                .expect("Error parsing abbreviations");

            let mut cursor = unit.entries(&abbrevs);
            cursor.next_dfs().expect("Should parse next dfs");

            let root = cursor.current().expect("Should have a root DIE");
            let offset = match root.attr_value(gimli::DW_AT_stmt_list) {
                Some(gimli::AttributeValue::DebugLineRef(offset)) => offset,
                _ => continue,
            };

            let header =
                gimli::LineNumberProgramHeader::new(debug_line, offset, unit.address_size());
            if let Ok(header) = header {
                println!("");
                println!("Offset:                             0x{:x}", offset.0);
                println!("Length:                             {}",
                         header.unit_length());
                println!("DWARF version:                      {}", header.version());
                println!("Prologue length:                    {}",
                         header.header_length());
                println!("Minimum instruction length:         {}",
                         header.minimum_instruction_length());
                println!("Maximum operations per instruction: {}",
                         header.maximum_operations_per_instruction());
                println!("Default is_stmt:                    {}",
                         header.default_is_stmt());
                println!("Line base:                          {}", header.line_base());
                println!("Line range:                         {}",
                         header.line_range());
                println!("Opcode base:                        {}",
                         header.opcode_base());

                println!("");
                println!("Opcodes:");
                for (i, length) in header.standard_opcode_lengths().iter().enumerate() {
                    println!("  Opcode {} as {} args", i + 1, length);
                }

                println!("");
                println!("The Directory Table:");
                for (i, dir) in header.include_directories().iter().enumerate() {
                    println!("  {} {}", i + 1, dir.to_string_lossy());
                }

                println!("");
                println!("The File Name Table");
                println!("  Entry\tDir\tTime\tSize\tName");
                for (i, file) in header.file_names().iter().enumerate() {
                    println!("  {}\t{}\t{}\t{}\t{}",
                             i + 1,
                             file.directory_index(),
                             file.last_modification(),
                             file.length(),
                             file.path_name().to_string_lossy());
                }

                println!("");
                println!("Line Number Statements:");
                let mut opcodes = header.opcodes();
                while let Some(opcode) = opcodes.next_opcode(&header)
                    .expect("Should parse opcode OK") {
                    println!("  {}", opcode);
                }

                println!("");
                println!("Line Number Rows:");
                println!("<pc>        [lno,col]");
                let mut state_machine = gimli::StateMachine::new(header);
                let mut file_index = 0;
                while let Some(row) = state_machine.next_row()
                    .expect("Should parse row OK") {
                    let line = row.line().unwrap_or(0);
                    let column = match row.column() {
                        gimli::ColumnType::Column(column) => column,
                        gimli::ColumnType::LeftEdge => 0,
                    };
                    print!("0x{:08x}  [{:4},{:2}]", row.address(), line, column);
                    if row.is_stmt() {
                        print!(" NS");
                    }
                    if row.basic_block() {
                        print!(" BB");
                    }
                    if row.end_sequence() {
                        print!(" ET");
                    }
                    if row.prologue_end() {
                        print!(" PE");
                    }
                    if row.epilogue_begin() {
                        print!(" EB");
                    }
                    if row.isa() != 0 {
                        print!(" IS={}", row.isa());
                    }
                    if row.discriminator() != 0 {
                        print!(" DI={}", row.discriminator());
                    }
                    if file_index != row.file_index() {
                        file_index = row.file_index();
                        if let Ok(file) = row.file() {
                            if let Some(directory) = file.directory(row.header()) {
                                print!(" uri: \"{}/{}\"",
                                       directory.to_string_lossy(),
                                       file.path_name().to_string_lossy());
                            } else {
                                print!(" uri: \"{}\"", file.path_name().to_string_lossy());
                            }
                        }
                    }
                    println!("");
                }
            }
        }
    }
}

fn dump_aranges<Endian>(file: &object::File)
    where Endian: gimli::Endianity
{
    let debug_aranges = file.get_section(".debug_aranges");
    let debug_info = file.get_section(".debug_info");

    if let (Some(debug_aranges), Some(debug_info)) = (debug_aranges, debug_info) {
        println!(".debug_aranges");
        println!("");

        let debug_aranges = gimli::DebugAranges::<Endian>::new(debug_aranges);
        let debug_info = gimli::DebugInfo::<Endian>::new(debug_info);

        let mut cu_die_offset = gimli::DebugInfoOffset(0);
        let mut prev_cu_offset = None;
        let mut aranges = debug_aranges.items();
        while let Some(arange) = aranges.next().expect("Should parse arange OK") {
            let cu_offset = arange.debug_info_offset();
            if Some(cu_offset) != prev_cu_offset {
                let cu = debug_info.header_from_offset(cu_offset)
                    .expect("Should parse unit header OK");
                cu_die_offset = gimli::DebugInfoOffset(cu_offset.0 + cu.header_size() as u64);
                prev_cu_offset = Some(cu_offset);
            }
            if let Some(segment) = arange.segment() {
                print!("arange starts at seg,off 0x{:08x},0x{:08x}, ",
                       segment,
                       arange.address());
            } else {
                print!("arange starts at 0x{:08x}, ", arange.address());
            }
            println!("length of 0x{:08x}, cu_die_offset = 0x{:08x}",
                     arange.length(),
                     cu_die_offset.0);
        }
    }
}
<|endoftext|>