<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//! Communicating with the running kernel using devicemapper.

#[allow(dead_code, non_camel_case_types)]
mod dm_ioctl;

use std::fs::File;
use std::io;
use std::os::unix::io::AsRawFd;
use std::mem;
use std::slice;
use std::slice::bytes::copy_memory;
use std::io::Error;
use std::io::ErrorKind::Other;

use byteorder::{LittleEndian, ByteOrder};

use nix::sys::ioctl;

use dm::dm_ioctl as dmi;
use lv::LV;
use vg::VG;
use util::align_to;
use pv::Device;

const DM_IOCTL: u8 = 0xfd;
const DM_CTL_PATH: &'static str= "/dev/mapper/control";

const DM_VERSION_MAJOR: u32 = 4;
const DM_VERSION_MINOR: u32 = 30;
const DM_VERSION_PATCHLEVEL: u32 = 0;

/// Context needed for communicating with devicemapper.
pub struct DM<'a> {
    file: File,
    vg: &'a VG,
}

impl <'a> DM<'a> {
    /// Create a new context for communicating about a given VG with DM.
    pub fn new(vg: &'a VG) -> io::Result<Self> {
        Ok(DM {
            file: try!(File::open(DM_CTL_PATH)),
            vg: vg,
        })
    }

    fn initialize_hdr(hdr: &mut dmi::Struct_dm_ioctl) -> () {
        hdr.version[0] = DM_VERSION_MAJOR;
        hdr.version[1] = DM_VERSION_MINOR;
        hdr.version[2] = DM_VERSION_PATCHLEVEL;

        hdr.data_start = mem::size_of::<dmi::Struct_dm_ioctl>() as u32;
    }

    fn hdr_set_name(hdr: &mut dmi::Struct_dm_ioctl, name: &[u8]) -> () {
        let name_dest: &mut [u8; 128] = unsafe { mem::transmute(&mut hdr.name) };
        copy_memory(name, &mut name_dest[..]);
    }

    fn hdr_set_uuid(hdr: &mut dmi::Struct_dm_ioctl, uuid: &[u8]) -> () {
        let uuid_dest: &mut [u8; 129] = unsafe { mem::transmute(&mut hdr.uuid) };
        copy_memory(uuid, &mut uuid_dest[..]);
    }

    /// Devicemapper version information: Major, Minor, and patchlevel versions.
    pub fn get_version(&self) -> io::Result<(u32, u32, u32)> {

        let mut hdr: dmi::Struct_dm_ioctl = Default::default();
        hdr.version[0] = DM_VERSION_MAJOR;
        hdr.version[1] = DM_VERSION_MINOR;
        hdr.version[2] = DM_VERSION_PATCHLEVEL;

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_VERSION_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => {},
        };

        Ok((hdr.version[0], hdr.version[1], hdr.version[2]))
    }

    /// Returns a list of tuples containing DM device names and their major/minor
    /// device numbers.
    pub fn list_devices(&self) -> io::Result<Vec<(String, Device)>> {
        let mut buf = [0u8; 16 * 1024];
        let mut hdr: &mut dmi::Struct_dm_ioctl = unsafe {mem::transmute(&mut buf)};

        Self::initialize_hdr(&mut hdr);
        hdr.data_size = buf.len() as u32;

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_LIST_DEVICES_CMD as u8, buf.len());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut buf) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => {},
        };

        let mut devs = Vec::new();
        if (hdr.data_size - hdr.data_start as u32) != 0 {
            let mut result = &buf[hdr.data_start as usize..];

            loop {
                let slc = slice_to_null(&result[12..]).expect("Bad data from ioctl");
                let devno = LittleEndian::read_u64(&result[..8]);
                devs.push((String::from_utf8_lossy(slc).into_owned(), Device::from(devno)));

                let next = LittleEndian::read_u32(&result[8..12]);
                if next == 0 { break }

                result = &result[next as usize..];
            }
        }

        Ok(devs)
    }

    fn create_device(&self, lv: &mut LV) -> io::Result<()> {
        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(&mut hdr);
        Self::hdr_set_name(&mut hdr, lv.name.as_bytes());
        // TODO: concat uuid or something?
        //Self::hdr_set_uuid(&mut hdr, lv.uuid);
        hdr.data_size = hdr.data_start;

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_DEV_CREATE_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => { }
        };

        lv.device = Some(Device::from(hdr.dev));

        Ok(())
    }

    fn load_device(&self, lv: &LV) -> io::Result<()> {
        let sectors_per_extent = self.vg.extent_size;
        let mut targs = Vec::new();

        // Construct targets first, since we need to know how many & size
        // before initializing the header.
        for seg in &lv.segments {
            for &(ref pvname, ref loc) in &seg.stripes {
                let err = || Error::new(Other, "dm load_device error");
                let pv = try!(self.vg.pvs.get(pvname).ok_or(err()));

                let mut targ: dmi::Struct_dm_target_spec = Default::default();
                targ.sector_start = seg.start_extent * sectors_per_extent;
                targ.length = seg.extent_count * sectors_per_extent;
                targ.status = 0;

                let mut dst: &mut [u8] = unsafe {
                    mem::transmute(&mut targ.target_type[..])
                };
                copy_memory(seg.ty.as_bytes(), &mut dst);

                let mut params = Vec::new();
                // TODO: only works for linear
                params.extend(
                    format!("{}:{} {}",
                            pv.device.major,
                            pv.device.minor,
                            (loc * sectors_per_extent) + pv.pe_start).as_bytes());

                let pad_bytes = align_to(
                    params.len() + 1usize, 8usize) - params.len();
                params.extend(vec![0; pad_bytes]);

                targ.next = (mem::size_of::<dmi::Struct_dm_target_spec>()
                           + params.len()) as u32;

                targs.push((targ, params));
            }
        }

        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(&mut hdr);
        Self::hdr_set_name(&mut hdr, lv.name.as_bytes());

        hdr.data_start = mem::size_of::<dmi::Struct_dm_ioctl>() as u32;
        hdr.data_size = hdr.data_start + targs.iter()
            .map(|&(t, _)| t.next)
            .sum::<u32>();
        hdr.target_count = targs.len() as u32;

        // Flatten into buf
        let mut buf: Vec<u8> = Vec::with_capacity(hdr.data_size as usize);
        unsafe {
            let ptr: *mut u8 = mem::transmute(&mut hdr);
            let slc = slice::from_raw_parts(ptr, hdr.data_start as usize);
            buf.extend(slc);
        }

        for (targ, param) in targs {
            unsafe {
                let ptr: *mut u8 = mem::transmute(&targ);
                let slc = slice::from_raw_parts(
                    ptr, mem::size_of::<dmi::Struct_dm_target_spec>());
                buf.extend(slc);
            }

            buf.extend(&param);
        }

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_TABLE_LOAD_CMD as u8, buf.len());

        match unsafe { ioctl::read_into_ptr(self.file.as_raw_fd(), op, buf.as_mut_ptr()) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => Ok(())
        }
    }

    fn resume_device(&self, lv: &LV) -> io::Result<()> {
        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(&mut hdr);
        hdr.data_size = hdr.data_start;
        Self::hdr_set_name(&mut hdr, lv.name.as_bytes());

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_DEV_SUSPEND_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => Ok(())
        }
    }

    /// Activate a Logical Volume.
    ///
    /// Also populates the LV's device field.
    pub fn activate_device(&self, lv: &mut LV) -> io::Result<()> {

        // TODO: name/uuid mangle?

        try!(self.create_device(lv));

        try!(self.load_device(lv));

        self.resume_device(lv)
    }

    /// Remove a Logical Volume.
    // UNTESTED
    pub fn remove_device(&self, lv: LV) -> io::Result<()> {
        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(&mut hdr);
        hdr.data_size = hdr.data_start;
        Self::hdr_set_name(&mut hdr, lv.name.as_bytes());

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_DEV_REMOVE_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => Ok(())
        }

    }
}

//
// Return up to the first \0, or None
//
fn slice_to_null(slc: &[u8]) -> Option<&[u8]> {
    for (i, c) in slc.iter().enumerate() {
        if *c == b'\0' { return Some(&slc[..i]) };
    }
    None
}
<commit_msg>dm: Implement is_dm_major()<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//! Communicating with the running kernel using devicemapper.

#[allow(dead_code, non_camel_case_types)]
mod dm_ioctl;

use std::fs::File;
use std::io;
use std::io::{BufReader, BufRead};
use std::os::unix::io::AsRawFd;
use std::mem;
use std::slice;
use std::slice::bytes::copy_memory;
use std::io::Error;
use std::io::ErrorKind::Other;
use std::collections::BTreeSet;

use byteorder::{LittleEndian, ByteOrder};

use nix::sys::ioctl;

use dm::dm_ioctl as dmi;
use lv::LV;
use vg::VG;
use util::align_to;
use pv::Device;

const DM_IOCTL: u8 = 0xfd;
const DM_CTL_PATH: &'static str= "/dev/mapper/control";

const DM_VERSION_MAJOR: u32 = 4;
const DM_VERSION_MINOR: u32 = 30;
const DM_VERSION_PATCHLEVEL: u32 = 0;

/// Context needed for communicating with devicemapper.
pub struct DM<'a> {
    file: File,
    vg: &'a VG,
}

impl <'a> DM<'a> {
    /// Create a new context for communicating about a given VG with DM.
    pub fn new(vg: &'a VG) -> io::Result<Self> {
        Ok(DM {
            file: try!(File::open(DM_CTL_PATH)),
            vg: vg,
        })
    }

    /// Determine if a major number is a major number used by DM.
    pub fn is_dm_major(major: u32) -> bool {
        let mut set = BTreeSet::new();

        let f = File::open("/proc/devices")
            .ok().expect("Could not open /proc/devices");

        let reader = BufReader::new(f);

        for line in reader.lines()
            .filter_map(|x| x.ok())
            .skip_while(|x| x != "Block devices:")
            .skip(1) {
                let spl: Vec<_> = line.split_whitespace().collect();

                if spl[1] == "device-mapper" {
                    set.insert(spl[0].parse::<u32>().unwrap());
                }
            }

        set.contains(&major)
    }

    fn initialize_hdr(hdr: &mut dmi::Struct_dm_ioctl) -> () {
        hdr.version[0] = DM_VERSION_MAJOR;
        hdr.version[1] = DM_VERSION_MINOR;
        hdr.version[2] = DM_VERSION_PATCHLEVEL;

        hdr.data_start = mem::size_of::<dmi::Struct_dm_ioctl>() as u32;
    }

    fn hdr_set_name(hdr: &mut dmi::Struct_dm_ioctl, name: &[u8]) -> () {
        let name_dest: &mut [u8; 128] = unsafe { mem::transmute(&mut hdr.name) };
        copy_memory(name, &mut name_dest[..]);
    }

    fn hdr_set_uuid(hdr: &mut dmi::Struct_dm_ioctl, uuid: &[u8]) -> () {
        let uuid_dest: &mut [u8; 129] = unsafe { mem::transmute(&mut hdr.uuid) };
        copy_memory(uuid, &mut uuid_dest[..]);
    }

    /// Devicemapper version information: Major, Minor, and patchlevel versions.
    pub fn get_version(&self) -> io::Result<(u32, u32, u32)> {

        let mut hdr: dmi::Struct_dm_ioctl = Default::default();
        hdr.version[0] = DM_VERSION_MAJOR;
        hdr.version[1] = DM_VERSION_MINOR;
        hdr.version[2] = DM_VERSION_PATCHLEVEL;

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_VERSION_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => {},
        };

        Ok((hdr.version[0], hdr.version[1], hdr.version[2]))
    }

    /// Returns a list of tuples containing DM device names and their major/minor
    /// device numbers.
    pub fn list_devices(&self) -> io::Result<Vec<(String, Device)>> {
        let mut buf = [0u8; 16 * 1024];
        let mut hdr: &mut dmi::Struct_dm_ioctl = unsafe {mem::transmute(&mut buf)};

        Self::initialize_hdr(&mut hdr);
        hdr.data_size = buf.len() as u32;

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_LIST_DEVICES_CMD as u8, buf.len());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut buf) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => {},
        };

        let mut devs = Vec::new();
        if (hdr.data_size - hdr.data_start as u32) != 0 {
            let mut result = &buf[hdr.data_start as usize..];

            loop {
                let slc = slice_to_null(&result[12..]).expect("Bad data from ioctl");
                let devno = LittleEndian::read_u64(&result[..8]);
                devs.push((String::from_utf8_lossy(slc).into_owned(), Device::from(devno)));

                let next = LittleEndian::read_u32(&result[8..12]);
                if next == 0 { break }

                result = &result[next as usize..];
            }
        }

        Ok(devs)
    }

    fn create_device(&self, lv: &mut LV) -> io::Result<()> {
        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(&mut hdr);
        Self::hdr_set_name(&mut hdr, lv.name.as_bytes());
        // TODO: concat uuid or something?
        //Self::hdr_set_uuid(&mut hdr, lv.uuid);
        hdr.data_size = hdr.data_start;

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_DEV_CREATE_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => { }
        };

        lv.device = Some(Device::from(hdr.dev));

        Ok(())
    }

    fn load_device(&self, lv: &LV) -> io::Result<()> {
        let sectors_per_extent = self.vg.extent_size;
        let mut targs = Vec::new();

        // Construct targets first, since we need to know how many & size
        // before initializing the header.
        for seg in &lv.segments {
            for &(ref pvname, ref loc) in &seg.stripes {
                let err = || Error::new(Other, "dm load_device error");
                let pv = try!(self.vg.pvs.get(pvname).ok_or(err()));

                let mut targ: dmi::Struct_dm_target_spec = Default::default();
                targ.sector_start = seg.start_extent * sectors_per_extent;
                targ.length = seg.extent_count * sectors_per_extent;
                targ.status = 0;

                let mut dst: &mut [u8] = unsafe {
                    mem::transmute(&mut targ.target_type[..])
                };
                copy_memory(seg.ty.as_bytes(), &mut dst);

                let mut params = Vec::new();
                // TODO: only works for linear
                params.extend(
                    format!("{}:{} {}",
                            pv.device.major,
                            pv.device.minor,
                            (loc * sectors_per_extent) + pv.pe_start).as_bytes());

                let pad_bytes = align_to(
                    params.len() + 1usize, 8usize) - params.len();
                params.extend(vec![0; pad_bytes]);

                targ.next = (mem::size_of::<dmi::Struct_dm_target_spec>()
                           + params.len()) as u32;

                targs.push((targ, params));
            }
        }

        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(&mut hdr);
        Self::hdr_set_name(&mut hdr, lv.name.as_bytes());

        hdr.data_start = mem::size_of::<dmi::Struct_dm_ioctl>() as u32;
        hdr.data_size = hdr.data_start + targs.iter()
            .map(|&(t, _)| t.next)
            .sum::<u32>();
        hdr.target_count = targs.len() as u32;

        // Flatten into buf
        let mut buf: Vec<u8> = Vec::with_capacity(hdr.data_size as usize);
        unsafe {
            let ptr: *mut u8 = mem::transmute(&mut hdr);
            let slc = slice::from_raw_parts(ptr, hdr.data_start as usize);
            buf.extend(slc);
        }

        for (targ, param) in targs {
            unsafe {
                let ptr: *mut u8 = mem::transmute(&targ);
                let slc = slice::from_raw_parts(
                    ptr, mem::size_of::<dmi::Struct_dm_target_spec>());
                buf.extend(slc);
            }

            buf.extend(&param);
        }

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_TABLE_LOAD_CMD as u8, buf.len());

        match unsafe { ioctl::read_into_ptr(self.file.as_raw_fd(), op, buf.as_mut_ptr()) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => Ok(())
        }
    }

    fn resume_device(&self, lv: &LV) -> io::Result<()> {
        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(&mut hdr);
        hdr.data_size = hdr.data_start;
        Self::hdr_set_name(&mut hdr, lv.name.as_bytes());

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_DEV_SUSPEND_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => Ok(())
        }
    }

    /// Activate a Logical Volume.
    ///
    /// Also populates the LV's device field.
    pub fn activate_device(&self, lv: &mut LV) -> io::Result<()> {

        // TODO: name/uuid mangle?

        try!(self.create_device(lv));

        try!(self.load_device(lv));

        self.resume_device(lv)
    }

    /// Remove a Logical Volume.
    // UNTESTED
    pub fn remove_device(&self, lv: LV) -> io::Result<()> {
        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(&mut hdr);
        hdr.data_size = hdr.data_start;
        Self::hdr_set_name(&mut hdr, lv.name.as_bytes());

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_DEV_REMOVE_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => Ok(())
        }

    }
}

//
// Return up to the first \0, or None
//
fn slice_to_null(slc: &[u8]) -> Option<&[u8]> {
    for (i, c) in slc.iter().enumerate() {
        if *c == b'\0' { return Some(&slc[..i]) };
    }
    None
}
<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;
use std::mem;
use std::process::{Output, Command, Stdio, ExitStatus};
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;

use wait_timeout::ChildExt;

pub struct Container {
    id: String,
}

impl Container {
    pub fn new(cmd: &str,
               args: &[String],
               name: &str) -> io::Result<Container> {
        let out = try!(run(Command::new("docker")
                                   .arg("create")
                                   .arg("--cap-drop=ALL")
                                   .arg("--memory=128m")
                                   .arg("--net=none")
                                   .arg("--pids-limit=5")
                                   .arg("--security-opt=no-new-privileges")
                                   .arg("--interactive")
                                   .arg(name)
                                   .arg(cmd)
                                   .stderr(Stdio::inherit())
                                   .args(args)));
        let stdout = String::from_utf8_lossy(&out.stdout);
        Ok(Container {
            id: stdout.trim().to_string(),
        })
    }

    pub fn run(&self,
               input: &[u8],
               timeout: Duration)
               -> io::Result<(ExitStatus, Vec<u8>, bool)> {
        let mut cmd = Command::new("docker");
        cmd.arg("start")
           .arg("--attach")
           .arg("--interactive")
           .arg(&self.id)
           .stdin(Stdio::piped())
           .stdout(Stdio::piped())
           .stderr(Stdio::piped());
        debug!("attaching with {:?}", cmd);
        let mut cmd = try!(cmd.spawn());
        try!(cmd.stdin.take().unwrap().write_all(input));
        debug!("input written, now waiting");

        let mut stdout = cmd.stdout.take().unwrap();
        let mut stderr = cmd.stderr.take().unwrap();
        let sink = Arc::new(Mutex::new(Vec::new()));
        let sink2 = sink.clone();
        let stdout = thread::spawn(move || append(&sink2, &mut stdout));
        let sink2 = sink.clone();
        let stderr = thread::spawn(move || append(&sink2, &mut stderr));

        let (status, timeout) = match try!(cmd.wait_timeout(timeout)) {
            Some(status) => {
                debug!("finished before timeout");
                // TODO: document this
                (unsafe { mem::transmute(status) }, false)
            }
            None => {
                debug!("timeout, going to kill");
                try!(run(Command::new("docker").arg("kill").arg(&self.id)));
                (try!(cmd.wait()), true)
            }
        };
        stdout.join().unwrap();
        stderr.join().unwrap();
        let mut lock = sink.lock().unwrap();
        let output = mem::replace(&mut *lock, Vec::new());
        debug!("status: {}", status);
        debug!("output: {}", String::from_utf8_lossy(&output));
        Ok((status, output, timeout))
    }
}

fn append(into: &Mutex<Vec<u8>>, from: &mut Read) {
    let mut buf = [0; 1024];
    while let Ok(amt) = from.read(&mut buf) {
        if amt == 0 {
            break
        }
        into.lock().unwrap().extend_from_slice(&buf[..amt]);
    }
}

impl Drop for Container {
    fn drop(&mut self) {
        run(Command::new("docker")
                    .arg("rm")
                    .arg("--force")
                    .arg(&self.id)).unwrap();
    }
}

fn run(cmd: &mut Command) -> io::Result<Output> {
    debug!("spawning: {:?}", cmd);
    let out = try!(cmd.output());
    debug!("output: {:?}", out);
    if !out.status.success() {
        let msg = format!("process failed: {:?}\n{:?}", cmd, out);
        return Err(io::Error::new(io::ErrorKind::Other, msg))
    }
    Ok(out)
}
<commit_msg>Don't pass --pids-limit for now<commit_after>use std::io;
use std::io::prelude::*;
use std::mem;
use std::process::{Output, Command, Stdio, ExitStatus};
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;

use wait_timeout::ChildExt;

pub struct Container {
    id: String,
}

impl Container {
    pub fn new(cmd: &str,
               args: &[String],
               name: &str) -> io::Result<Container> {
        let out = try!(run(Command::new("docker")
                                   .arg("create")
                                   .arg("--cap-drop=ALL")
                                   .arg("--memory=128m")
                                   .arg("--net=none")
                                   .arg("--security-opt=no-new-privileges")
                                   .arg("--interactive")
                                   .arg(name)
                                   .arg(cmd)
                                   .stderr(Stdio::inherit())
                                   .args(args)));
        let stdout = String::from_utf8_lossy(&out.stdout);
        Ok(Container {
            id: stdout.trim().to_string(),
        })
    }

    pub fn run(&self,
               input: &[u8],
               timeout: Duration)
               -> io::Result<(ExitStatus, Vec<u8>, bool)> {
        let mut cmd = Command::new("docker");
        cmd.arg("start")
           .arg("--attach")
           .arg("--interactive")
           .arg(&self.id)
           .stdin(Stdio::piped())
           .stdout(Stdio::piped())
           .stderr(Stdio::piped());
        debug!("attaching with {:?}", cmd);
        let mut cmd = try!(cmd.spawn());
        try!(cmd.stdin.take().unwrap().write_all(input));
        debug!("input written, now waiting");

        let mut stdout = cmd.stdout.take().unwrap();
        let mut stderr = cmd.stderr.take().unwrap();
        let sink = Arc::new(Mutex::new(Vec::new()));
        let sink2 = sink.clone();
        let stdout = thread::spawn(move || append(&sink2, &mut stdout));
        let sink2 = sink.clone();
        let stderr = thread::spawn(move || append(&sink2, &mut stderr));

        let (status, timeout) = match try!(cmd.wait_timeout(timeout)) {
            Some(status) => {
                debug!("finished before timeout");
                // TODO: document this
                (unsafe { mem::transmute(status) }, false)
            }
            None => {
                debug!("timeout, going to kill");
                try!(run(Command::new("docker").arg("kill").arg(&self.id)));
                (try!(cmd.wait()), true)
            }
        };
        stdout.join().unwrap();
        stderr.join().unwrap();
        let mut lock = sink.lock().unwrap();
        let output = mem::replace(&mut *lock, Vec::new());
        debug!("status: {}", status);
        debug!("output: {}", String::from_utf8_lossy(&output));
        Ok((status, output, timeout))
    }
}

fn append(into: &Mutex<Vec<u8>>, from: &mut Read) {
    let mut buf = [0; 1024];
    while let Ok(amt) = from.read(&mut buf) {
        if amt == 0 {
            break
        }
        into.lock().unwrap().extend_from_slice(&buf[..amt]);
    }
}

impl Drop for Container {
    fn drop(&mut self) {
        run(Command::new("docker")
                    .arg("rm")
                    .arg("--force")
                    .arg(&self.id)).unwrap();
    }
}

fn run(cmd: &mut Command) -> io::Result<Output> {
    debug!("spawning: {:?}", cmd);
    let out = try!(cmd.output());
    debug!("output: {:?}", out);
    if !out.status.success() {
        let msg = format!("process failed: {:?}\n{:?}", cmd, out);
        return Err(io::Error::new(io::ErrorKind::Other, msg))
    }
    Ok(out)
}
<|endoftext|>


<commit_before>//! Define table formatting utilities

use std::io::{Write, Error};

use encode_unicode::Utf8Char;

use super::utils::NEWLINE;

/// Alignment for cell's content
#[derive(Clone, Debug, PartialEq, Copy)]
pub enum Alignment {
    LEFT,
    CENTER,
    RIGHT,
}

/// Position of a line separator in a table
#[derive(Clone, Debug, PartialEq, Copy)]
pub enum LinePosition {
    Top,
    Title,
    Intern,
    Bottom,
}

/// Position of a column separator in a row
#[derive(Clone, Debug, PartialEq, Copy)]
pub enum ColumnPosition {
    Left,
    Intern,
    Right,
}

/// Contains the character used for printing a line separator
#[derive(Clone, Debug, Copy)]
pub struct LineSeparator {
    /// Line separator
    line: char,
    /// Internal junction separator
    junc: char,
    /// Left junction separator
    ljunc: char,
    /// Right junction separator
    rjunc: char,
}

impl LineSeparator {
    /// Create a new line separator instance where `line` is the character used to separate 2 lines
    /// and `junc` is the one used for junctions between columns and lines
    pub fn new(line: char, junc: char, ljunc: char, rjunc: char) -> LineSeparator {
        LineSeparator {
            line: line,
            junc: junc,
            ljunc: ljunc,
            rjunc: rjunc,
        }
    }

    /// Print a full line separator to `out`. `col_width` is a slice containing the width of each column
    pub fn print<T: Write + ?Sized>(&self,
                                    out: &mut T,
                                    col_width: &[usize],
                                    padding: (usize, usize),
                                    colsep: bool,
                                    lborder: bool,
                                    rborder: bool)
                                    -> Result<(), Error> {
        if lborder {
            try!(out.write_all(Utf8Char::from(self.ljunc).as_bytes()));
        }
        let mut iter = col_width.into_iter().peekable();
        while let Some(width) = iter.next() {
            for _ in 0..width + padding.0 + padding.1 {
                try!(out.write_all(Utf8Char::from(self.line).as_bytes()));
            }
            if colsep && iter.peek().is_some() {
                try!(out.write_all(Utf8Char::from(self.junc).as_bytes()));
            }
        }
        if rborder {
            try!(out.write_all(Utf8Char::from(self.rjunc).as_bytes()));
        }
        out.write_all(NEWLINE)
    }
}

impl Default for LineSeparator {
    fn default() -> Self {
        LineSeparator::new('-', '+', '+', '+')
    }
}

/// Contains the table formatting rules
#[derive(Clone, Debug, Copy)]
pub struct TableFormat {
    /// Optional column separator character
    csep: Option<char>,
    /// Optional left border character
    lborder: Option<char>,
    /// Optional right border character
    rborder: Option<char>,
    /// Optional internal line separator
    lsep: Option<LineSeparator>,
    /// Optional title line separator
    tsep: Option<LineSeparator>,
    /// Optional top line separator
    top_sep: Option<LineSeparator>,
    /// Optional bottom line separator
    bottom_sep: Option<LineSeparator>,
    /// Left padding
    pad_left: usize,
    /// Right padding
    pad_right: usize,
}

impl TableFormat {
    /// Create a new empty TableFormat.
    pub fn new() -> TableFormat {
        TableFormat {
            csep: None,
            lborder: None,
            rborder: None,
            lsep: None,
            tsep: None,
            top_sep: None,
            bottom_sep: None,
            pad_left: 0,
            pad_right: 0,
        }
    }

    /// Return a tuple with left and right padding
    pub fn get_padding(&self) -> (usize, usize) {
        (self.pad_left, self.pad_right)
    }

    /// Set left and right padding
    pub fn padding(&mut self, left: usize, right: usize) {
        self.pad_left = left;
        self.pad_right = right;
    }

    /// Set the character used for internal column separation
    pub fn column_separator(&mut self, separator: char) {
        self.csep = Some(separator);
    }

    /// Set the character used for table borders
    pub fn borders(&mut self, border: char) {
        self.lborder = Some(border);
        self.rborder = Some(border);
    }

    /// Set a line separator
    pub fn separator(&mut self, what: LinePosition, separator: LineSeparator) {
        *match what {
             LinePosition::Top => &mut self.top_sep,
             LinePosition::Bottom => &mut self.bottom_sep,
             LinePosition::Title => &mut self.tsep,
             LinePosition::Intern => &mut self.lsep,
         } = Some(separator);
    }

    /// Set format for multiple kind of line separator
    pub fn separators(&mut self, what: &[LinePosition], separator: LineSeparator) {
        for pos in what {
            self.separator(*pos, separator);
        }
    }

    fn get_sep_for_line(&self, pos: LinePosition) -> &Option<LineSeparator> {
        match pos {
            LinePosition::Intern => return &self.lsep,
            LinePosition::Top => return &self.top_sep,
            LinePosition::Bottom => return &self.bottom_sep,
            LinePosition::Title => {
                match &self.tsep {
                    s @ &Some(_) => s,
                    &None => &self.lsep,
                }
            }
        }
    }

    /// Print a full line separator to `out`. `col_width` is a slice containing the width of each column
    pub fn print_line_separator<T: Write + ?Sized>(&self,
                                                   out: &mut T,
                                                   col_width: &[usize],
                                                   pos: LinePosition)
                                                   -> Result<(), Error> {
        match *self.get_sep_for_line(pos) {
            Some(ref l) => {
                l.print(out,
                        col_width,
                        self.get_padding(),
                        self.csep.is_some(),
                        self.lborder.is_some(),
                        self.rborder.is_some())
            }
            None => Ok(()),
        }
    }

    pub fn get_column_separator(&self, pos: ColumnPosition) -> Option<char> {
        match pos {
            ColumnPosition::Left => self.lborder,
            ColumnPosition::Intern => self.csep,
            ColumnPosition::Right => self.rborder,
        }
    }

    /// Print a column separator or a table border
    pub fn print_column_separator<T: Write + ?Sized>(&self,
                                                     out: &mut T,
                                                     pos: ColumnPosition)
                                                     -> Result<(), Error> {
        match self.get_column_separator(pos) {
            Some(s) => out.write_all(Utf8Char::from(s).as_bytes()),
            None => Ok(()),
        }
    }
}

impl Default for TableFormat {
    fn default() -> Self {
        TableFormat::new()
    }
}

/// A builder to create a `TableFormat`
pub struct FormatBuilder {
    format: Box<TableFormat>,
}

impl FormatBuilder {
    pub fn new() -> FormatBuilder {
        FormatBuilder { format: Box::new(TableFormat::new()) }
    }

    /// Set left and right padding
    pub fn padding(mut self, left: usize, right: usize) -> Self {
        self.format.padding(left, right);
        self
    }

    /// Set the character used for internal column separation
    pub fn column_separator(mut self, separator: char) -> Self {
        self.format.column_separator(separator);
        self
    }

    /// Set the character used for table borders
    pub fn borders(mut self, border: char) -> Self {
        self.format.borders(border);
        self
    }

    /// Set a line separator format
    pub fn separator(mut self, what: LinePosition, separator: LineSeparator) -> Self {
        self.format.separator(what, separator);
        self
    }

    /// Set separator format for multiple kind of line separators
    pub fn separators(mut self, what: &[LinePosition], separator: LineSeparator) -> Self {
        self.format.separators(what, separator);
        self
    }

    /// Consume this builder and return the generated `TableFormat`
    pub fn build(self) -> TableFormat {
        *self.format
    }
}

/// Predifined formats. Those constants are lazily evaluated when
/// the corresponding struct is dereferenced
pub mod consts {
    use super::{TableFormat, LineSeparator, FormatBuilder, LinePosition};

    lazy_static! {
        /// A line separator made of `-` and `+`
        static ref MINUS_PLUS_SEP: LineSeparator = LineSeparator::new('-', '+', '+', '+');
        /// A line separator made of `=` and `+`
        static ref EQU_PLUS_SEP: LineSeparator = LineSeparator::new('=', '+', '+', '+');

        /// Default table format
        ///
        /// # Example
        /// ```text
        /// +----+----+
        /// | T1 | T2 |
        /// +====+====+
        /// | a  | b  |
        /// +----+----+
        /// | d  | c  |
        /// +----+----+
        /// ```
        pub static ref FORMAT_DEFAULT: TableFormat = FormatBuilder::new()
                                                                    .column_separator('|')
                                                                    .borders('|')
                                                                    .separator(LinePosition::Intern, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Title, *EQU_PLUS_SEP)
                                                                    .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Top, *MINUS_PLUS_SEP)
                                                                    .padding(1, 1)
                                                                    .build();

        /// Similar to `FORMAT_DEFAULT` but without special separator after title line
        ///
        /// # Example
        /// ```text
        /// +----+----+
        /// | T1 | T2 |
        /// +----+----+
        /// | a  | b  |
        /// +----+----+
        /// | c  | d  |
        /// +----+----+
        /// ```
        pub static ref FORMAT_NO_TITLE: TableFormat = FormatBuilder::new()
                                                                    .column_separator('|')
                                                                    .borders('|')
                                                                    .separator(LinePosition::Intern, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Title, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Top, *MINUS_PLUS_SEP)
                                                                    .padding(1, 1)
                                                                    .build();

        /// With no line separator, but with title separator
        ///
        /// # Example
        /// ```text
        /// +----+----+
        /// | T1 | T2 |
        /// +----+----+
        /// | a  | b  |
        /// | c  | d  |
        /// +----+----+
        /// ```
        pub static ref FORMAT_NO_LINESEP_WITH_TITLE: TableFormat = FormatBuilder::new()
                                                                    .column_separator('|')
                                                                    .borders('|')
                                                                    .separator(LinePosition::Title, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Top, *MINUS_PLUS_SEP)
                                                                    .padding(1, 1)
                                                                    .build();

        /// With no line or title separator
        ///
        /// # Example
        /// ```text
        /// +----+----+
        /// | T1 | T2 |
        /// | a  | b  |
        /// | c  | d  |
        /// +----+----+
        /// ```
        pub static ref FORMAT_NO_LINESEP: TableFormat = FormatBuilder::new()
                                                                    .column_separator('|')
                                                                    .borders('|')
                                                                    .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Top, *MINUS_PLUS_SEP)
                                                                    .padding(1, 1)
                                                                    .build();

        /// No column separator
        ///
        /// # Example
        /// ```text
        /// --------
        ///  T1  T2
        /// ========
        ///  a   b
        /// --------
        ///  d   c
        /// --------
        /// ```
        pub static ref FORMAT_NO_COLSEP: TableFormat = FormatBuilder::new()
                                                                    .separator(LinePosition::Intern, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Title, *EQU_PLUS_SEP)
                                                                    .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Top, *MINUS_PLUS_SEP)
                                                                    .padding(1, 1)
                                                                    .build();

        /// Format for printing a table without any separators (only alignment)
        ///
        /// # Example
        /// ```text
        ///  T1  T2
        ///  a   b
        ///  d   c
        /// ```
        pub static ref FORMAT_CLEAN: TableFormat = FormatBuilder::new()
                                                                    .padding(1, 1)
                                                                    .build();

        /// Format for a table with only external borders and title separator
        ///
        /// # Example
        /// ```text
        /// +--------+
        /// | T1  T2 |
        /// +========+
        /// | a   b  |
        /// | c   d  |
        /// +--------+
        /// ```
        pub static ref FORMAT_BORDERS_ONLY: TableFormat = FormatBuilder::new()
                                                                    .padding(1, 1)
                                                                    .separator(LinePosition::Title, *EQU_PLUS_SEP)
                                                                    .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Top, *MINUS_PLUS_SEP)
                                                                    .borders('|')
                                                                    .build();

        /// A table with no external border
        ///
        /// # Example
        /// ```text
        ///  T1 | T2
        /// ====+====
        ///  a  | b
        /// ----+----
        ///  c  | d
        /// ```
        pub static ref FORMAT_NO_BORDER: TableFormat = FormatBuilder::new()
                                                                    .padding(1, 1)
                                                                    .separator(LinePosition::Intern, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Title, *EQU_PLUS_SEP)
                                                                    .column_separator('|')
                                                                    .build();

        /// A table with no external border and no line separation
        ///
        /// # Example
        /// ```text
        ///  T1 | T2
        /// ----+----
        ///  a  | b
        ///  c  | d
        /// ```
        pub static ref FORMAT_NO_BORDER_LINE_SEPARATOR: TableFormat = FormatBuilder::new()
                                                                    .padding(1, 1)
                                                                    .separator(LinePosition::Title, *MINUS_PLUS_SEP)
                                                                    .column_separator('|')
                                                                    .build();
    }
}
<commit_msg>Retain existing public API but deprecate it<commit_after>//! Define table formatting utilities

use std::io::{Write, Error};

use encode_unicode::Utf8Char;

use super::utils::NEWLINE;

/// Alignment for cell's content
#[derive(Clone, Debug, PartialEq, Copy)]
pub enum Alignment {
    LEFT,
    CENTER,
    RIGHT,
}

/// Position of a line separator in a table
#[derive(Clone, Debug, PartialEq, Copy)]
pub enum LinePosition {
    Top,
    Title,
    Intern,
    Bottom,
}

/// Position of a column separator in a row
#[derive(Clone, Debug, PartialEq, Copy)]
pub enum ColumnPosition {
    Left,
    Intern,
    Right,
}

/// Contains the character used for printing a line separator
#[derive(Clone, Debug, Copy)]
pub struct LineSeparator {
    /// Line separator
    line: char,
    /// Internal junction separator
    junc: char,
    /// Left junction separator
    ljunc: char,
    /// Right junction separator
    rjunc: char,
}

impl LineSeparator {
    /// Create a new line separator instance where `line` is the character used to separate 2 lines
    /// and `junc` is the one used for junctions between columns and lines
    pub fn new(line: char, junc: char, ljunc: char, rjunc: char) -> LineSeparator {
        LineSeparator {
            line: line,
            junc: junc,
            ljunc: ljunc,
            rjunc: rjunc,
        }
    }

    /// Print a full line separator to `out`. `col_width` is a slice containing the width of each column
    #[deprecated(since = "0.6.7", note = "assumes padding is always (1, 1)")]
    pub fn print<T: Write + ?Sized>(&self,
                                    out: &mut T,
                                    col_width: &[usize],
                                    colsep: bool,
                                    lborder: bool,
                                    rborder: bool)
                                    -> Result<(), Error> {
        self.real_print(out, col_width, (1, 1), colsep, lborder, rborder)
    }

    fn real_print<T: Write + ?Sized>(&self,
                                     out: &mut T,
                                     col_width: &[usize],
                                     padding: (usize, usize),
                                     colsep: bool,
                                     lborder: bool,
                                     rborder: bool)
                                     -> Result<(), Error> {
        if lborder {
            try!(out.write_all(Utf8Char::from(self.ljunc).as_bytes()));
        }
        let mut iter = col_width.into_iter().peekable();
        while let Some(width) = iter.next() {
            for _ in 0..width + padding.0 + padding.1 {
                try!(out.write_all(Utf8Char::from(self.line).as_bytes()));
            }
            if colsep && iter.peek().is_some() {
                try!(out.write_all(Utf8Char::from(self.junc).as_bytes()));
            }
        }
        if rborder {
            try!(out.write_all(Utf8Char::from(self.rjunc).as_bytes()));
        }
        out.write_all(NEWLINE)
    }
}

impl Default for LineSeparator {
    fn default() -> Self {
        LineSeparator::new('-', '+', '+', '+')
    }
}

/// Contains the table formatting rules
#[derive(Clone, Debug, Copy)]
pub struct TableFormat {
    /// Optional column separator character
    csep: Option<char>,
    /// Optional left border character
    lborder: Option<char>,
    /// Optional right border character
    rborder: Option<char>,
    /// Optional internal line separator
    lsep: Option<LineSeparator>,
    /// Optional title line separator
    tsep: Option<LineSeparator>,
    /// Optional top line separator
    top_sep: Option<LineSeparator>,
    /// Optional bottom line separator
    bottom_sep: Option<LineSeparator>,
    /// Left padding
    pad_left: usize,
    /// Right padding
    pad_right: usize,
}

impl TableFormat {
    /// Create a new empty TableFormat.
    pub fn new() -> TableFormat {
        TableFormat {
            csep: None,
            lborder: None,
            rborder: None,
            lsep: None,
            tsep: None,
            top_sep: None,
            bottom_sep: None,
            pad_left: 0,
            pad_right: 0,
        }
    }

    /// Return a tuple with left and right padding
    pub fn get_padding(&self) -> (usize, usize) {
        (self.pad_left, self.pad_right)
    }

    /// Set left and right padding
    pub fn padding(&mut self, left: usize, right: usize) {
        self.pad_left = left;
        self.pad_right = right;
    }

    /// Set the character used for internal column separation
    pub fn column_separator(&mut self, separator: char) {
        self.csep = Some(separator);
    }

    /// Set the character used for table borders
    pub fn borders(&mut self, border: char) {
        self.lborder = Some(border);
        self.rborder = Some(border);
    }

    /// Set a line separator
    pub fn separator(&mut self, what: LinePosition, separator: LineSeparator) {
        *match what {
             LinePosition::Top => &mut self.top_sep,
             LinePosition::Bottom => &mut self.bottom_sep,
             LinePosition::Title => &mut self.tsep,
             LinePosition::Intern => &mut self.lsep,
         } = Some(separator);
    }

    /// Set format for multiple kind of line separator
    pub fn separators(&mut self, what: &[LinePosition], separator: LineSeparator) {
        for pos in what {
            self.separator(*pos, separator);
        }
    }

    fn get_sep_for_line(&self, pos: LinePosition) -> &Option<LineSeparator> {
        match pos {
            LinePosition::Intern => return &self.lsep,
            LinePosition::Top => return &self.top_sep,
            LinePosition::Bottom => return &self.bottom_sep,
            LinePosition::Title => {
                match &self.tsep {
                    s @ &Some(_) => s,
                    &None => &self.lsep,
                }
            }
        }
    }

    /// Print a full line separator to `out`. `col_width` is a slice containing the width of each column
    pub fn print_line_separator<T: Write + ?Sized>(&self,
                                                   out: &mut T,
                                                   col_width: &[usize],
                                                   pos: LinePosition)
                                                   -> Result<(), Error> {
        match *self.get_sep_for_line(pos) {
            Some(ref l) => {
                l.real_print(out,
                             col_width,
                             self.get_padding(),
                             self.csep.is_some(),
                             self.lborder.is_some(),
                             self.rborder.is_some())
            }
            None => Ok(()),
        }
    }

    pub fn get_column_separator(&self, pos: ColumnPosition) -> Option<char> {
        match pos {
            ColumnPosition::Left => self.lborder,
            ColumnPosition::Intern => self.csep,
            ColumnPosition::Right => self.rborder,
        }
    }

    /// Print a column separator or a table border
    pub fn print_column_separator<T: Write + ?Sized>(&self,
                                                     out: &mut T,
                                                     pos: ColumnPosition)
                                                     -> Result<(), Error> {
        match self.get_column_separator(pos) {
            Some(s) => out.write_all(Utf8Char::from(s).as_bytes()),
            None => Ok(()),
        }
    }
}

impl Default for TableFormat {
    fn default() -> Self {
        TableFormat::new()
    }
}

/// A builder to create a `TableFormat`
pub struct FormatBuilder {
    format: Box<TableFormat>,
}

impl FormatBuilder {
    pub fn new() -> FormatBuilder {
        FormatBuilder { format: Box::new(TableFormat::new()) }
    }

    /// Set left and right padding
    pub fn padding(mut self, left: usize, right: usize) -> Self {
        self.format.padding(left, right);
        self
    }

    /// Set the character used for internal column separation
    pub fn column_separator(mut self, separator: char) -> Self {
        self.format.column_separator(separator);
        self
    }

    /// Set the character used for table borders
    pub fn borders(mut self, border: char) -> Self {
        self.format.borders(border);
        self
    }

    /// Set a line separator format
    pub fn separator(mut self, what: LinePosition, separator: LineSeparator) -> Self {
        self.format.separator(what, separator);
        self
    }

    /// Set separator format for multiple kind of line separators
    pub fn separators(mut self, what: &[LinePosition], separator: LineSeparator) -> Self {
        self.format.separators(what, separator);
        self
    }

    /// Consume this builder and return the generated `TableFormat`
    pub fn build(self) -> TableFormat {
        *self.format
    }
}

/// Predifined formats. Those constants are lazily evaluated when
/// the corresponding struct is dereferenced
pub mod consts {
    use super::{TableFormat, LineSeparator, FormatBuilder, LinePosition};

    lazy_static! {
        /// A line separator made of `-` and `+`
        static ref MINUS_PLUS_SEP: LineSeparator = LineSeparator::new('-', '+', '+', '+');
        /// A line separator made of `=` and `+`
        static ref EQU_PLUS_SEP: LineSeparator = LineSeparator::new('=', '+', '+', '+');

        /// Default table format
        ///
        /// # Example
        /// ```text
        /// +----+----+
        /// | T1 | T2 |
        /// +====+====+
        /// | a  | b  |
        /// +----+----+
        /// | d  | c  |
        /// +----+----+
        /// ```
        pub static ref FORMAT_DEFAULT: TableFormat = FormatBuilder::new()
                                                                    .column_separator('|')
                                                                    .borders('|')
                                                                    .separator(LinePosition::Intern, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Title, *EQU_PLUS_SEP)
                                                                    .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Top, *MINUS_PLUS_SEP)
                                                                    .padding(1, 1)
                                                                    .build();

        /// Similar to `FORMAT_DEFAULT` but without special separator after title line
        ///
        /// # Example
        /// ```text
        /// +----+----+
        /// | T1 | T2 |
        /// +----+----+
        /// | a  | b  |
        /// +----+----+
        /// | c  | d  |
        /// +----+----+
        /// ```
        pub static ref FORMAT_NO_TITLE: TableFormat = FormatBuilder::new()
                                                                    .column_separator('|')
                                                                    .borders('|')
                                                                    .separator(LinePosition::Intern, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Title, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Top, *MINUS_PLUS_SEP)
                                                                    .padding(1, 1)
                                                                    .build();

        /// With no line separator, but with title separator
        ///
        /// # Example
        /// ```text
        /// +----+----+
        /// | T1 | T2 |
        /// +----+----+
        /// | a  | b  |
        /// | c  | d  |
        /// +----+----+
        /// ```
        pub static ref FORMAT_NO_LINESEP_WITH_TITLE: TableFormat = FormatBuilder::new()
                                                                    .column_separator('|')
                                                                    .borders('|')
                                                                    .separator(LinePosition::Title, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Top, *MINUS_PLUS_SEP)
                                                                    .padding(1, 1)
                                                                    .build();

        /// With no line or title separator
        ///
        /// # Example
        /// ```text
        /// +----+----+
        /// | T1 | T2 |
        /// | a  | b  |
        /// | c  | d  |
        /// +----+----+
        /// ```
        pub static ref FORMAT_NO_LINESEP: TableFormat = FormatBuilder::new()
                                                                    .column_separator('|')
                                                                    .borders('|')
                                                                    .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Top, *MINUS_PLUS_SEP)
                                                                    .padding(1, 1)
                                                                    .build();

        /// No column separator
        ///
        /// # Example
        /// ```text
        /// --------
        ///  T1  T2
        /// ========
        ///  a   b
        /// --------
        ///  d   c
        /// --------
        /// ```
        pub static ref FORMAT_NO_COLSEP: TableFormat = FormatBuilder::new()
                                                                    .separator(LinePosition::Intern, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Title, *EQU_PLUS_SEP)
                                                                    .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Top, *MINUS_PLUS_SEP)
                                                                    .padding(1, 1)
                                                                    .build();

        /// Format for printing a table without any separators (only alignment)
        ///
        /// # Example
        /// ```text
        ///  T1  T2
        ///  a   b
        ///  d   c
        /// ```
        pub static ref FORMAT_CLEAN: TableFormat = FormatBuilder::new()
                                                                    .padding(1, 1)
                                                                    .build();

        /// Format for a table with only external borders and title separator
        ///
        /// # Example
        /// ```text
        /// +--------+
        /// | T1  T2 |
        /// +========+
        /// | a   b  |
        /// | c   d  |
        /// +--------+
        /// ```
        pub static ref FORMAT_BORDERS_ONLY: TableFormat = FormatBuilder::new()
                                                                    .padding(1, 1)
                                                                    .separator(LinePosition::Title, *EQU_PLUS_SEP)
                                                                    .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Top, *MINUS_PLUS_SEP)
                                                                    .borders('|')
                                                                    .build();

        /// A table with no external border
        ///
        /// # Example
        /// ```text
        ///  T1 | T2
        /// ====+====
        ///  a  | b
        /// ----+----
        ///  c  | d
        /// ```
        pub static ref FORMAT_NO_BORDER: TableFormat = FormatBuilder::new()
                                                                    .padding(1, 1)
                                                                    .separator(LinePosition::Intern, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Title, *EQU_PLUS_SEP)
                                                                    .column_separator('|')
                                                                    .build();

        /// A table with no external border and no line separation
        ///
        /// # Example
        /// ```text
        ///  T1 | T2
        /// ----+----
        ///  a  | b
        ///  c  | d
        /// ```
        pub static ref FORMAT_NO_BORDER_LINE_SEPARATOR: TableFormat = FormatBuilder::new()
                                                                    .padding(1, 1)
                                                                    .separator(LinePosition::Title, *MINUS_PLUS_SEP)
                                                                    .column_separator('|')
                                                                    .build();
    }
}
<|endoftext|>


<commit_before>macro_rules! ast_struct {
    (
        @@ [$($attrs_pub:tt)*]
        struct $name:ident #full $($rest:tt)*
    ) => {
        #[cfg(feature = "full")]
        #[cfg_attr(feature = "extra-traits", derive(Debug, Eq, PartialEq, Hash))]
        #[cfg_attr(feature = "clone-impls", derive(Clone))]
        $($attrs_pub)* struct $name $($rest)*

        #[cfg(not(feature = "full"))]
        #[cfg_attr(feature = "extra-traits", derive(Debug, Eq, PartialEq, Hash))]
        #[cfg_attr(feature = "clone-impls", derive(Clone))]
        $($attrs_pub)* struct $name {
            _noconstruct: (),
        }

        #[cfg(all(not(feature = "full"), feature = "printing"))]
        impl ::quote::ToTokens for $name {
            fn to_tokens(&self, _: &mut ::proc_macro2::TokenStream) {
                unreachable!()
            }
        }
    };

    (
        @@ [$($attrs_pub:tt)*]
        struct $name:ident #manual_extra_traits $($rest:tt)*
    ) => {
        #[cfg_attr(feature = "extra-traits", derive(Debug))]
        #[cfg_attr(feature = "clone-impls", derive(Clone))]
        $($attrs_pub)* struct $name $($rest)*
    };

    (
        @@ [$($attrs_pub:tt)*]
        struct $name:ident $($rest:tt)*
    ) => {
        #[cfg_attr(feature = "extra-traits", derive(Debug, Eq, PartialEq, Hash))]
        #[cfg_attr(feature = "clone-impls", derive(Clone))]
        $($attrs_pub)* struct $name $($rest)*
    };

    ($($t:tt)*) => {
        strip_attrs_pub!(ast_struct!($($t)*));
    };
}

macro_rules! ast_enum {
    // Drop the `#no_visit` attribute, if present.
    (
        @@ [$($attrs_pub:tt)*]
        enum $name:ident #no_visit $($rest:tt)*
    ) => (
        ast_enum!(@@ [$($attrs_pub)*] enum $name $($rest)*);
    );

    (
        @@ [$($attrs_pub:tt)*]
        enum $name:ident #manual_extra_traits $($rest:tt)*
    ) => (
        #[cfg_attr(feature = "extra-traits", derive(Debug))]
        #[cfg_attr(feature = "clone-impls", derive(Clone))]
        $($attrs_pub)* enum $name $($rest)*
    );

    (
        @@ [$($attrs_pub:tt)*]
        enum $name:ident $($rest:tt)*
    ) => (
        #[cfg_attr(feature = "extra-traits", derive(Debug, Eq, PartialEq, Hash))]
        #[cfg_attr(feature = "clone-impls", derive(Clone))]
        $($attrs_pub)* enum $name $($rest)*
    );

    ($($t:tt)*) => {
        strip_attrs_pub!(ast_enum!($($t)*));
    };
}

// Unfortunately, at this time, we can't make the generated enum here have the
// correct span. The way that the span for the overall enum decl is calculated
// is using `lo.to(prev_span)` [1].
//
// If the beginning and ending spans of the enum item don't have the same macro
// context, fallback code is run [2]. This code chooses the span within the
// macro context to avoid firing spurious diagnostics [3].
//
// This means `[src]` links in rustdoc will point to the macro, instead of the
// actual declaration, if either the first or last token of our declaration has
// a span from the macro. With `macro_rules!` there is no way to preserve the
// span of a `{}` block while changing its contents, so we're forced to have the
// span of our final token point to our macro.
//
// [1]: https://github.com/rust-lang/rust/blob/9a90d03ad171856dc016c2dcc19292ec49a8a26f/src/libsyntax/parse/parser.rs#L7377
// [2]: https://github.com/rust-lang/rust/blob/9a90d03ad171856dc016c2dcc19292ec49a8a26f/src/libsyntax_pos/lib.rs#L467-L478
// [3]: https://github.com/rust-lang/rust/pull/47942
macro_rules! ast_enum_of_structs {
    (
        $(#[$enum_attr:meta])*
        $pub:ident $enum:ident $name:ident $(# $tags:ident)* {
            $(
                $(#[$variant_attr:meta])*
                $variant:ident $( ($member:ident) )*,
            )*
        }

        $($remaining:tt)*
    ) => (
        check_keyword_matches!(pub $pub);
        check_keyword_matches!(enum $enum);

        ast_enum! {
            $(#[$enum_attr])*
            $pub $enum $name $(# $tags)* {
                $(
                    $(#[$variant_attr])*
                    $variant $( ($member) )*,
                )*
            }
        }

        $(
            $(
                impl From<$member> for $name {
                    fn from(e: $member) -> $name {
                        $name::$variant(e)
                    }
                }
            )*
        )*

        #[cfg(feature = "printing")]
        generate_to_tokens! {
            $($remaining)*
            ()
            tokens
            $name { $($variant $($member)*,)* }
        }
    )
}

#[cfg(feature = "printing")]
macro_rules! generate_to_tokens {
    (do_not_generate_to_tokens $($foo:tt)*) => ();

    (($($arms:tt)*) $tokens:ident $name:ident { $variant:ident, $($next:tt)*}) => {
        generate_to_tokens!(
            ($($arms)* $name::$variant => {})
            $tokens $name { $($next)* }
        );
    };

    (($($arms:tt)*) $tokens:ident $name:ident { $variant:ident $member:ident, $($next:tt)*}) => {
        generate_to_tokens!(
            ($($arms)* $name::$variant(ref _e) => _e.to_tokens($tokens),)
            $tokens $name { $($next)* }
        );
    };

    (($($arms:tt)*) $tokens:ident $name:ident {}) => {
        impl ::quote::ToTokens for $name {
            fn to_tokens(&self, $tokens: &mut ::proc_macro2::TokenStream) {
                match *self {
                    $($arms)*
                }
            }
        }
    };
}

macro_rules! strip_attrs_pub {
    ($mac:ident!($(#[$m:meta])* $pub:ident $($t:tt)*)) => {
        check_keyword_matches!(pub $pub);

        $mac!(@@ [$(#[$m])* $pub] $($t)*);
    };
}

macro_rules! check_keyword_matches {
    (struct struct) => {};
    (enum enum) => {};
    (pub pub) => {};
}
<commit_msg>Make ast_enum_of_structs doc link point to the right place<commit_after>macro_rules! ast_struct {
    (
        @@ [$($attrs_pub:tt)*]
        struct $name:ident #full $($rest:tt)*
    ) => {
        #[cfg(feature = "full")]
        #[cfg_attr(feature = "extra-traits", derive(Debug, Eq, PartialEq, Hash))]
        #[cfg_attr(feature = "clone-impls", derive(Clone))]
        $($attrs_pub)* struct $name $($rest)*

        #[cfg(not(feature = "full"))]
        #[cfg_attr(feature = "extra-traits", derive(Debug, Eq, PartialEq, Hash))]
        #[cfg_attr(feature = "clone-impls", derive(Clone))]
        $($attrs_pub)* struct $name {
            _noconstruct: (),
        }

        #[cfg(all(not(feature = "full"), feature = "printing"))]
        impl ::quote::ToTokens for $name {
            fn to_tokens(&self, _: &mut ::proc_macro2::TokenStream) {
                unreachable!()
            }
        }
    };

    (
        @@ [$($attrs_pub:tt)*]
        struct $name:ident #manual_extra_traits $($rest:tt)*
    ) => {
        #[cfg_attr(feature = "extra-traits", derive(Debug))]
        #[cfg_attr(feature = "clone-impls", derive(Clone))]
        $($attrs_pub)* struct $name $($rest)*
    };

    (
        @@ [$($attrs_pub:tt)*]
        struct $name:ident $($rest:tt)*
    ) => {
        #[cfg_attr(feature = "extra-traits", derive(Debug, Eq, PartialEq, Hash))]
        #[cfg_attr(feature = "clone-impls", derive(Clone))]
        $($attrs_pub)* struct $name $($rest)*
    };

    ($($t:tt)*) => {
        strip_attrs_pub!(ast_struct!($($t)*));
    };
}

macro_rules! ast_enum {
    // Drop the `#no_visit` attribute, if present.
    (
        @@ [$($attrs_pub:tt)*]
        enum $name:ident #no_visit $($rest:tt)*
    ) => (
        ast_enum!(@@ [$($attrs_pub)*] enum $name $($rest)*);
    );

    (
        @@ [$($attrs_pub:tt)*]
        enum $name:ident #manual_extra_traits $($rest:tt)*
    ) => (
        #[cfg_attr(feature = "extra-traits", derive(Debug))]
        #[cfg_attr(feature = "clone-impls", derive(Clone))]
        $($attrs_pub)* enum $name $($rest)*
    );

    (
        @@ [$($attrs_pub:tt)*]
        enum $name:ident $($rest:tt)*
    ) => (
        #[cfg_attr(feature = "extra-traits", derive(Debug, Eq, PartialEq, Hash))]
        #[cfg_attr(feature = "clone-impls", derive(Clone))]
        $($attrs_pub)* enum $name $($rest)*
    );

    ($($t:tt)*) => {
        strip_attrs_pub!(ast_enum!($($t)*));
    };
}

macro_rules! ast_enum_of_structs {
    (
        $(#[$enum_attr:meta])*
        $pub:ident $enum:ident $name:ident #$tag:ident $body:tt
        $($remaining:tt)*
    ) => {
        ast_enum!($(#[$enum_attr])* $pub $enum $name #$tag $body);
        ast_enum_of_structs_impl!($pub $enum $name $body $($remaining)*);
    };

    (
        $(#[$enum_attr:meta])*
        $pub:ident $enum:ident $name:ident $body:tt
        $($remaining:tt)*
    ) => {
        ast_enum!($(#[$enum_attr])* $pub $enum $name $body);
        ast_enum_of_structs_impl!($pub $enum $name $body $($remaining)*);
    };
}

macro_rules! ast_enum_of_structs_impl {
    (
        $pub:ident $enum:ident $name:ident {
            $(
                $(#[$variant_attr:meta])*
                $variant:ident $( ($member:ident) )*,
            )*
        }

        $($remaining:tt)*
    ) => {
        check_keyword_matches!(pub $pub);
        check_keyword_matches!(enum $enum);

        $(
            $(
                impl From<$member> for $name {
                    fn from(e: $member) -> $name {
                        $name::$variant(e)
                    }
                }
            )*
        )*

        #[cfg(feature = "printing")]
        generate_to_tokens! {
            $($remaining)*
            ()
            tokens
            $name { $($variant $($member)*,)* }
        }
    };
}

#[cfg(feature = "printing")]
macro_rules! generate_to_tokens {
    (do_not_generate_to_tokens $($foo:tt)*) => ();

    (($($arms:tt)*) $tokens:ident $name:ident { $variant:ident, $($next:tt)*}) => {
        generate_to_tokens!(
            ($($arms)* $name::$variant => {})
            $tokens $name { $($next)* }
        );
    };

    (($($arms:tt)*) $tokens:ident $name:ident { $variant:ident $member:ident, $($next:tt)*}) => {
        generate_to_tokens!(
            ($($arms)* $name::$variant(ref _e) => _e.to_tokens($tokens),)
            $tokens $name { $($next)* }
        );
    };

    (($($arms:tt)*) $tokens:ident $name:ident {}) => {
        impl ::quote::ToTokens for $name {
            fn to_tokens(&self, $tokens: &mut ::proc_macro2::TokenStream) {
                match *self {
                    $($arms)*
                }
            }
        }
    };
}

macro_rules! strip_attrs_pub {
    ($mac:ident!($(#[$m:meta])* $pub:ident $($t:tt)*)) => {
        check_keyword_matches!(pub $pub);

        $mac!(@@ [$(#[$m])* $pub] $($t)*);
    };
}

macro_rules! check_keyword_matches {
    (struct struct) => {};
    (enum enum) => {};
    (pub pub) => {};
}
<|endoftext|>


<commit_before>#![doc="Defines generic traits for numbers on which
SciRust library works.
"]

// Imports
use std::fmt::Show;

// complex numbers
use external::complex::{Complex32, Complex64};

/// Defines all the traits which a matrix element must support
pub trait Number : Num+Copy+Show {

}

/// Indicate that i8 fits all requirements for being a matrix element.
impl Number for i8 {

}
/// Indicate that i16 fits all requirements for being a matrix element.
impl Number for i16 {

}
/// Indicate that i32 fits all requirements for being a matrix element.
impl Number for i32 {

}
/// Indicate that i64 fits all requirements for being a matrix element.
impl Number for i64 {

}

/// Indicate that int fits all requirements for being a matrix element.
impl Number for int {

}

/// Indicate that u8 fits all requirements for being a matrix element.
impl Number for u8 {

}
/// Indicate that u16 fits all requirements for being a matrix element.
impl Number for u16 {

}
/// Indicate that u32 fits all requirements for being a matrix element.
impl Number for u32 {

}

/// Indicate that u64 fits all requirements for being a matrix element.
impl Number for u64 {

}

/// Indicate that uint fits all requirements for being a matrix element.
impl Number for uint {

}

/// Indicate that f32 fits all requirements for being a matrix element.
impl Number for f32 {

}

/// Indicate that f64 fits all requirements for being a matrix element.
impl Number for f64 {

}

/// Indicate that Complex32 fits all requirements for being a matrix element.
impl Number for Complex32 {

}

/// Indicate that Complex64 fits all requirements for being a matrix element.
impl Number for Complex64 {

}
<commit_msg>Some introspection capabilities<commit_after>#![doc="Defines generic traits for numbers on which
SciRust library works.
"]

// Imports
use std::fmt::Show;

// complex numbers
use external::complex::{Complex32, Complex64};

/// Defines all the traits which a matrix element must support
pub trait Number : Num+Copy+Show {

    fn is_float() -> bool {
        false
    }

    fn is_int() -> bool {
        false
    }

    fn is_complex() -> bool {
        false
    }

    fn is_signed() -> bool;
}

/// Indicate that i8 fits all requirements for being a matrix element.
impl Number for i8 {

    #[inline]
    fn is_signed() -> bool {
        true
    }

}
/// Indicate that i16 fits all requirements for being a matrix element.
impl Number for i16 {

    #[inline]
    fn is_signed() -> bool {
        true
    }

    fn is_int() -> bool {
        true
    }

}
/// Indicate that i32 fits all requirements for being a matrix element.
impl Number for i32 {

    #[inline]
    fn is_signed() -> bool {
        true
    }

    fn is_int() -> bool {
        true
    }

}
/// Indicate that i64 fits all requirements for being a matrix element.
impl Number for i64 {

    #[inline]
    fn is_signed() -> bool {
        true
    }

    fn is_int() -> bool {
        true
    }

}

/// Indicate that int fits all requirements for being a matrix element.
impl Number for int {

    #[inline]
    fn is_signed() -> bool {
        true
    }

    fn is_int() -> bool {
        true
    }

}

/// Indicate that u8 fits all requirements for being a matrix element.
impl Number for u8 {

    #[inline]
    fn is_signed() -> bool {
        false
    }

    fn is_int() -> bool {
        true
    }

}
/// Indicate that u16 fits all requirements for being a matrix element.
impl Number for u16 {

    #[inline]
    fn is_signed() -> bool {
        false
    }

    fn is_int() -> bool {
        true
    }

}
/// Indicate that u32 fits all requirements for being a matrix element.
impl Number for u32 {

    #[inline]
    fn is_signed() -> bool {
        false
    }

    fn is_int() -> bool {
        true
    }

}

/// Indicate that u64 fits all requirements for being a matrix element.
impl Number for u64 {

    #[inline]
    fn is_signed() -> bool {
        false
    }

    fn is_int() -> bool {
        true
    }

}

/// Indicate that uint fits all requirements for being a matrix element.
impl Number for uint {

    #[inline]
    fn is_signed() -> bool {
        false
    }

    fn is_int() -> bool {
        true
    }

}

/// Indicate that f32 fits all requirements for being a matrix element.
impl Number for f32 {

    #[inline]
    fn is_signed() -> bool {
        true
    }

    fn is_float() -> bool {
        true
    }

}

/// Indicate that f64 fits all requirements for being a matrix element.
impl Number for f64 {

    #[inline]
    fn is_signed() -> bool {
        true
    }

    fn is_float() -> bool {
        true
    }

}

/// Indicate that Complex32 fits all requirements for being a matrix element.
impl Number for Complex32 {

    #[inline]
    fn is_signed() -> bool {
        true
    }

}

/// Indicate that Complex64 fits all requirements for being a matrix element.
impl Number for Complex64 {

    #[inline]
    fn is_signed() -> bool {
        true
    }

}

//TODO: figure out how to make this work.
pub fn describe<T: Number>(value : T ){
    //println!("Signed: {}", T.is_signed());
}
<|endoftext|>


<commit_before>use std;
use std::ptr;

use gc::Gc;
use mem;
use mem::ptr::Ptr;

// String in Dora is immutable
// length + string content is stored in one contiguous memory
pub struct Str {
    ptr: Ptr
}

impl Str {
    pub fn len(&self) -> usize {
        unsafe {
            *(self.ptr.raw() as *const usize)
        }
    }

    pub fn ptr(&self) -> Ptr {
        self.ptr
    }

    pub fn set_len(&self, len: usize) {
        unsafe {
            *(self.ptr.raw() as *mut usize) = len;
        }
    }

    pub fn data(&self) -> *mut u8 {
        unsafe {
            self.ptr.raw().offset(mem::ptr_width() as isize) as *mut u8
        }
    }
}

impl Str {
    pub fn from_buffer(gc: &mut Gc, buf: &[u8]) -> Str {
        let string = Str::new(gc, buf.len());

        unsafe {
            // write len of Str (excluding 0 at end)
            string.set_len(buf.len());

            // copy buffer content into Str
            ptr::copy_nonoverlapping(buf.as_ptr(), string.data(), buf.len());

            // string should end with 0 for C compatibility
            *(string.data().offset(buf.len() as isize)) = 0;
        }

        string
    }

    pub fn new(gc: &mut Gc, len: usize) -> Str {
        let size = mem::ptr_width() as usize + len + 1;

        Str { ptr: gc.alloc(size) }
    }

    pub fn concat(gc: &mut Gc, lhs: Str, rhs: Str) -> Str {
        let len = lhs.len() + rhs.len();
        let string = Str::new(gc, len);

        unsafe {
            string.set_len(len);

            ptr::copy_nonoverlapping(lhs.data(), string.data(), lhs.len());
            ptr::copy_nonoverlapping(rhs.data(),
                string.data().offset(lhs.len() as isize), rhs.len());

            *(string.data().offset(len as isize)) = 0;
        }

        string
    }
}

pub struct IntArray {
    ptr: *mut i32,
    length: usize,
}

impl IntArray {
    pub fn empty() -> IntArray {
        IntArray {
            ptr: ptr::null_mut(),
            length: 0,
        }
    }

    pub fn with_element(gc: &mut Gc, length: usize, elem: isize) -> IntArray {
        if length < 0 {
            panic!("length needs to be greater or equal to 0.");
        }

        let ptr = if length > 0 {
            gc.alloc(std::mem::size_of::<i32>() * length).raw() as *mut i32
        } else {
            ptr::null_mut()
        };

        for i in 0..length {
            unsafe {
                *ptr.offset(i as isize) = elem as i32;
            }
        }

        IntArray {
            ptr: ptr,
            length: length,
        }
    }

    pub fn size() -> usize {
        std::mem::size_of::<IntArray>()
    }

    pub fn len(&self) -> usize {
        self.length
    }

    pub fn get(&self, ind: i32) -> i32 {
        if ind < 0 || ind as usize >= self.length {
            panic!("index out of bounds");
        }

        unsafe {
            *self.ptr.offset(ind as isize)
        }
    }

    pub fn set(&self, ind: i32, value: i32) {
        if ind < 0 || ind as usize >= self.length {
            panic!("index out of bounds");
        }

        unsafe {
            *self.ptr.offset(ind as isize) = value;
        }
    }
}

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

    use gc::Gc;
    use libc;

    #[test]
    pub fn test_create_string() {
        let mut gc = Gc::new();
        let string = Str::from_buffer(&mut gc, "hello world!".as_bytes());

        unsafe {
            assert_eq!(12, string.len());

            let expected = "hello world!".as_ptr() as *const i8;
            let value = string.data() as *const i8;
            assert_eq!(0, libc::strncmp(expected, value, string.len()));
        }
    }
}
<commit_msg>created Header<commit_after>use std;
use std::ptr;

use class::Class;
use gc::Gc;
use mem;
use mem::ptr::Ptr;

pub struct Header {
    class: *mut Class<'static>,
}

// String in Dora is immutable
// length + string content is stored in one contiguous memory
pub struct Str {
    ptr: Ptr
}

impl Str {
    pub fn len(&self) -> usize {
        unsafe {
            *(self.ptr.raw() as *const usize)
        }
    }

    pub fn ptr(&self) -> Ptr {
        self.ptr
    }

    pub fn set_len(&self, len: usize) {
        unsafe {
            *(self.ptr.raw() as *mut usize) = len;
        }
    }

    pub fn data(&self) -> *mut u8 {
        unsafe {
            self.ptr.raw().offset(mem::ptr_width() as isize) as *mut u8
        }
    }
}

impl Str {
    pub fn from_buffer(gc: &mut Gc, buf: &[u8]) -> Str {
        let string = Str::new(gc, buf.len());

        unsafe {
            // write len of Str (excluding 0 at end)
            string.set_len(buf.len());

            // copy buffer content into Str
            ptr::copy_nonoverlapping(buf.as_ptr(), string.data(), buf.len());

            // string should end with 0 for C compatibility
            *(string.data().offset(buf.len() as isize)) = 0;
        }

        string
    }

    pub fn new(gc: &mut Gc, len: usize) -> Str {
        let size = mem::ptr_width() as usize + len + 1;

        Str { ptr: gc.alloc(size) }
    }

    pub fn concat(gc: &mut Gc, lhs: Str, rhs: Str) -> Str {
        let len = lhs.len() + rhs.len();
        let string = Str::new(gc, len);

        unsafe {
            string.set_len(len);

            ptr::copy_nonoverlapping(lhs.data(), string.data(), lhs.len());
            ptr::copy_nonoverlapping(rhs.data(),
                string.data().offset(lhs.len() as isize), rhs.len());

            *(string.data().offset(len as isize)) = 0;
        }

        string
    }
}

pub struct IntArray {
    ptr: *mut i32,
    length: usize,
}

impl IntArray {
    pub fn empty() -> IntArray {
        IntArray {
            ptr: ptr::null_mut(),
            length: 0,
        }
    }

    pub fn with_element(gc: &mut Gc, length: usize, elem: isize) -> IntArray {
        if length < 0 {
            panic!("length needs to be greater or equal to 0.");
        }

        let ptr = if length > 0 {
            gc.alloc(std::mem::size_of::<i32>() * length).raw() as *mut i32
        } else {
            ptr::null_mut()
        };

        for i in 0..length {
            unsafe {
                *ptr.offset(i as isize) = elem as i32;
            }
        }

        IntArray {
            ptr: ptr,
            length: length,
        }
    }

    pub fn size() -> usize {
        std::mem::size_of::<IntArray>()
    }

    pub fn len(&self) -> usize {
        self.length
    }

    pub fn get(&self, ind: i32) -> i32 {
        if ind < 0 || ind as usize >= self.length {
            panic!("index out of bounds");
        }

        unsafe {
            *self.ptr.offset(ind as isize)
        }
    }

    pub fn set(&self, ind: i32, value: i32) {
        if ind < 0 || ind as usize >= self.length {
            panic!("index out of bounds");
        }

        unsafe {
            *self.ptr.offset(ind as isize) = value;
        }
    }
}

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

    use gc::Gc;
    use libc;

    #[test]
    pub fn test_create_string() {
        let mut gc = Gc::new();
        let string = Str::from_buffer(&mut gc, "hello world!".as_bytes());

        unsafe {
            assert_eq!(12, string.len());

            let expected = "hello world!".as_ptr() as *const i8;
            let value = string.data() as *const i8;
            assert_eq!(0, libc::strncmp(expected, value, string.len()));
        }
    }
}
<|endoftext|>


<commit_before>use std::ffi::{CString, c_str_to_bytes};

use ll::*;

pub struct Object {
    raw: *mut Tcl_Obj
}

impl Object {

    pub fn new() -> Object {
        Object {
            raw: unsafe {
                let raw = Tcl_NewObj();
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    pub fn new_boolean(val: bool) -> Object {
        Object {
            raw: unsafe {
                let raw = Tcl_NewBooleanObj(if val { 1 } else { 0 });
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    pub fn new_integer(val: i32) -> Object {
        Object {
            raw: unsafe {
                let raw = Tcl_NewIntObj(val);
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    pub fn new_long(val: i64) -> Object {
        Object {
            raw: unsafe {
                let raw = Tcl_NewLongObj(val);
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    //TODO: WideInt
    //TODO: BigNum

    pub fn new_double(val: f64) -> Object {
        Object {
            raw: unsafe {
                let raw = Tcl_NewDoubleObj(val);
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    pub fn new_string(val: &str) -> Object {
        let buf = CString::from_slice(val.as_bytes()).as_ptr();
        Object {
            raw: unsafe {
                let raw = Tcl_NewStringObj(buf, val.len() as i32);
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    pub fn new_byte_array(val: &[u8]) -> Object {
        Object {
            raw: unsafe {
                let raw = Tcl_NewByteArrayObj(val.as_ptr(), val.len() as i32);
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    // Setters

    pub fn set_boolean(&mut self, val: bool) {
        unsafe {
            Tcl_SetBooleanObj(self.raw, if val { 1 } else { 0 });
        }
    }

    pub fn set_integer(&mut self, val: i32) {
        unsafe {
            Tcl_SetIntObj(self.raw, val);
        }
    }

    pub fn set_long(&mut self, val: i64) {
        unsafe {
            Tcl_SetLongObj(self.raw, val);
        }
    }

    //TODO: WideInt
    //TODO: BigNum

    pub fn set_double(&mut self, val: f64) {
        unsafe {
            Tcl_SetDoubleObj(self.raw, val);
        }
    }

    pub fn set_string(&mut self, val: &str) {
        let buf = CString::from_slice(val.as_bytes()).as_ptr();
        unsafe {
            Tcl_SetStringObj(self.raw, buf, val.len() as i32);
        }
    }

    pub fn set_byte_array(&mut self, val: &[u8]) {
        unsafe {
            Tcl_SetByteArrayObj(self.raw, val.as_ptr(), val.len() as i32);
        }
    }

    // Getters

    pub fn get_string(&mut self) -> String {
        unsafe {
            let mut raw_string_length = 0;
            let raw_string_ptr = Tcl_GetStringFromObj(self.raw, &mut raw_string_length);
            String::from_utf8_lossy(c_str_to_bytes(&(raw_string_ptr as *const i8))).to_string()
        }
    }
}

impl Drop for Object {
    fn drop(&mut self) {
        unsafe { Tcl_DecrRefCount(self.raw) };
    }
}
<commit_msg>Objects are now bound to a Tcl lifetime<commit_after>use std::ffi::{CString, c_str_to_bytes};

use ll::*;
use tcl::TclEnvironment;

pub struct Object <'env> {
    _env: &'env TclEnvironment,
    raw: *mut Tcl_Obj
}

impl <'env> Object <'env> {

    pub fn new(env: &TclEnvironment) -> Object {
        Object {
            _env: env,
            raw: unsafe {
                let raw = Tcl_NewObj();
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    pub fn new_boolean(env: &TclEnvironment, val: bool) -> Object {
        Object {
            _env: env,
            raw: unsafe {
                let raw = Tcl_NewBooleanObj(if val { 1 } else { 0 });
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    pub fn new_integer(env: &TclEnvironment, val: i32) -> Object {
        Object {
            _env: env,
            raw: unsafe {
                let raw = Tcl_NewIntObj(val);
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    pub fn new_long(env: &TclEnvironment, val: i64) -> Object {
        Object {
            _env: env,
            raw: unsafe {
                let raw = Tcl_NewLongObj(val);
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    //TODO: WideInt
    //TODO: BigNum

    pub fn new_double(env: &TclEnvironment, val: f64) -> Object {
        Object {
            _env: env,
            raw: unsafe {
                let raw = Tcl_NewDoubleObj(val);
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    pub fn new_string(env: &'env TclEnvironment, val: &str) -> Object<'env> {
        let buf = CString::from_slice(val.as_bytes()).as_ptr();
        Object {
            _env: env,
            raw: unsafe {
                let raw = Tcl_NewStringObj(buf, val.len() as i32);
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    pub fn new_byte_array(env: &'env TclEnvironment, val: &[u8]) -> Object<'env> {
        Object {
            _env: env,
            raw: unsafe {
                let raw = Tcl_NewByteArrayObj(val.as_ptr(), val.len() as i32);
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    // Setters

    pub fn set_boolean(&mut self, val: bool) {
        unsafe {
            Tcl_SetBooleanObj(self.raw, if val { 1 } else { 0 });
        }
    }

    pub fn set_integer(&mut self, val: i32) {
        unsafe {
            Tcl_SetIntObj(self.raw, val);
        }
    }

    pub fn set_long(&mut self, val: i64) {
        unsafe {
            Tcl_SetLongObj(self.raw, val);
        }
    }

    //TODO: WideInt
    //TODO: BigNum

    pub fn set_double(&mut self, val: f64) {
        unsafe {
            Tcl_SetDoubleObj(self.raw, val);
        }
    }

    pub fn set_string(&mut self, val: &str) {
        let buf = CString::from_slice(val.as_bytes()).as_ptr();
        unsafe {
            Tcl_SetStringObj(self.raw, buf, val.len() as i32);
        }
    }

    pub fn set_byte_array(&mut self, val: &[u8]) {
        unsafe {
            Tcl_SetByteArrayObj(self.raw, val.as_ptr(), val.len() as i32);
        }
    }

    // Getters

    pub fn get_string(&mut self) -> String {
        unsafe {
            let mut raw_string_length = 0;
            let raw_string_ptr = Tcl_GetStringFromObj(self.raw, &mut raw_string_length);
            String::from_utf8_lossy(c_str_to_bytes(&(raw_string_ptr as *const i8))).to_string()
        }
    }
}

#[unsafe_destructor]
impl <'env> Drop for Object <'env> {
    fn drop(&mut self) {
        unsafe { Tcl_DecrRefCount(self.raw) };
    }
}
<|endoftext|>