<commit_before>#![allow(deprecated)]
use std::path::PathBuf;
use std::{io, fs, slice};
use curl::easy::Easy;
use memmap::{Mmap, Protection};
use std::io::{Read, Write};
use utils::data::{DatasetIntfRef, DatasetIntf};
use std::rc::Rc;
use tensor::{Tensor, TensorKind, NumLimits};
use torch;
use std::marker::PhantomData;

type Sample<T> = (Tensor<T>, i64);
type CollatedSample<T> = (Tensor<T>, Tensor<i64>);

static URLS: [&str; 4] = ["http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz",
                          "http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz",
                          "http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz",
                          "http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz"];
static RAW_FOLDER: &str = "raw";
static PROCESSED_FOLDER: &str = "processed";
static TRAINING_FILE: &str = "training.pt";
static TEST_FILE: &str = "test.pt";

fn create_dir_f(arg: PathBuf) -> io::Result<()> {
    let result = fs::create_dir(arg);
    if let Some(err) = result.err() {
        match err.kind() {
            io::ErrorKind::AlreadyExists => Ok(()),
            _ => Err(err),
        }
    } else {
        Ok(())
    }
}

fn check_exists(arg: &PathBuf) -> io::Result<bool> {
    let train = fs::metadata(arg.join(TRAINING_FILE))?;
    let test = fs::metadata(arg.join(TEST_FILE))?;

    return Ok(train.is_file() && test.is_file());
}

fn download(root: &String) -> io::Result<()> {
    let raw_path = PathBuf::from(root).join(RAW_FOLDER);
    let processed_path = PathBuf::from(root).join(PROCESSED_FOLDER);
    create_dir_f(raw_path.clone())?;
    create_dir_f(processed_path.clone())?;

    for url in URLS.iter() {
        println!("downloading {}", url);
        let paths: Vec<_> = url.split("/").collect();
        let mut fname: Vec<_> = paths[5].split("/").collect();
        fname = fname[0].split(".").collect();
        let file_path = raw_path.join(fname[0]);
        let mut file = fs::OpenOptions::new()
            .write(true)
            .create(true)
            .open(file_path)?;
        let mut data = Vec::new();
        let mut handle = Easy::new();
        handle.url(url).unwrap();
        {
            let mut transfer = handle.transfer();
            transfer
                .write_function(|new_data| {
                                    data.extend_from_slice(new_data);
                                    Ok(new_data.len())
                                })
                .unwrap();
            transfer.perform().unwrap();
        }
        let mut output = Vec::with_capacity(data.len() + 1100000);
        let mut gz = ::flate2::read::GzDecoder::new(data.as_slice())?;
        gz.read_to_end(&mut output)?;

        file.write_all(output.as_slice())?;
    }
    println!("Proceeding");

    let training_set =
        vec![read_image_file(raw_path.join("train-images-idx3-ubyte"))?,
                            read_label_file(raw_path.join("train-labels-idx1-ubyte"))?];
    let test_set = vec![read_image_file(raw_path.join("t10k-images-idx3-ubyte"))?,
                        read_label_file(raw_path.join("t10k-labels-idx1-ubyte"))?];

    torch::save(processed_path.join(TRAINING_FILE), &training_set)?;
    torch::save(processed_path.join(TEST_FILE), &test_set)?;

    Ok(())
}

fn read_image_file(path: PathBuf) -> io::Result<TensorKind> {
    let fp = Mmap::open_path(path, Protection::Read)?;
    let (length, nrows, ncols);
    {
        let data = unsafe { slice::from_raw_parts(fp.ptr() as *const i32, fp.len()) };
        assert_eq!(i32::from_be(data[0]), 2051);
        length = i32::from_be(data[1]) as usize;
        nrows = i32::from_be(data[2]) as usize;
        ncols = i32::from_be(data[3]) as usize;
    }
    let data = unsafe { fp.as_slice() };
    let mut images: Vec<Vec<u8>> = Vec::with_capacity(length);
    let mut idx = 16;
    for _ in 0..length {
        let mut img = Vec::with_capacity(nrows * ncols);
        for _ in 0..nrows * ncols {
            img.push(data[idx]);
            idx += 1;
        }
        images.push(img)
    }
    Ok(torch::byte_tensor(images).view(&[-1, 28, 28]).into())
}

fn read_label_file(path: PathBuf) -> io::Result<TensorKind> {
    let fp = Mmap::open_path(path, Protection::Read)?;
    let length;
    {
        let data = unsafe { slice::from_raw_parts(fp.ptr() as *const i32, fp.len()) };
        assert_eq!(i32::from_be(data[0]), 2049);
        length = i32::from_be(data[1]) as usize;
    }
    let data = unsafe { fp.as_slice() };
    let mut labels: Vec<u8> = Vec::with_capacity(length);
    for i in 0..length {
        labels.push(data[8 + i])
    }
    Ok(torch::byte_tensor(labels).into())
}

pub struct MNIST<T: NumLimits> {
    pub root: String,
    pub train: bool,
    pub data: Tensor<u8>,
    pub labels: Tensor<u8>,
    pub transform: Option<Xfrm>,
    phantom: PhantomData<T>,
}

#[builder(pattern="owned")]
#[derive(Builder)]
pub struct MNISTArgs {
    root: String,
    #[builder(default="true")]
    train: bool,
    #[builder(default="false")]
    download: bool,
}

type Xfrm = Box<fn(&TensorKind) -> TensorKind>;
impl MNISTArgsBuilder {
    pub fn done<T: NumLimits + 'static>(self,
                                        xfrm: Option<Xfrm>)
                                        -> DatasetIntfRef<CollatedSample<T>> {
        let args = self.build().unwrap();
        Rc::new(MNIST::new(args, xfrm))
    }
}

impl<T: NumLimits> MNIST<T> {
    pub fn build(root: &str) -> MNISTArgsBuilder {
        MNISTArgsBuilder::default().root(root.into())
    }
    pub fn new(args: MNISTArgs, xfrm: Option<Xfrm>) -> Self {
        if args.download {
            download(&args.root).expect("download failed");
        }
        let processed_path = PathBuf::from(&args.root).join(PROCESSED_FOLDER);
        if !check_exists(&processed_path).expect("Dataset not found, try downloading") {
            panic!("Dataset not found, use download=true to download it");
        }
        let mut v: Vec<TensorKind> = if args.train {
            torch::load(processed_path.join(TRAINING_FILE)).expect("torch load failed")
        } else {
            torch::load(processed_path.join(TEST_FILE)).expect("torch load failed")
        };
        let (data, labels) = (v.remove(0), v.remove(0));

        MNIST {
            root: args.root.clone(),
            train: args.train,
            data: data.into(),
            labels: labels.into(),
            transform: xfrm,
            phantom: PhantomData,
        }
    }
    fn index(&self, idx: usize) -> Sample<u8> {
        println!("index called idx: {}", idx);
        let img = self.data.s([idx]);
        let img = if let Some(ref transform) = self.transform {
            transform(&img.into()).into()
        } else {
            img.clone()
        };
        (img, self.labels[idx].clone() as i64)
    }
}

impl<T: NumLimits> DatasetIntf for MNIST<T> {
    type Batch = CollatedSample<T>;
    fn len(&self) -> usize {
        if self.train { 60000 } else { 10000 }
    }
    fn collate(&self, sample: Vec<usize>) -> Self::Batch {
        println!("collate called len: {}", sample.len());
        let v: Vec<Sample<u8>> = sample.into_iter().map(|i| self.index(i)).collect();
        let labels: Vec<i64> = v.iter().map(|&(_, ref t)| *t).collect();
        let imgs: Vec<Tensor<u8>> = v.into_iter().map(|(d, _)| d).collect();
        // XXX should check for case of double
        let img_batch: Tensor<T> = torch::tensor(imgs);
        let label_batch = torch::long_tensor(labels);
        (img_batch, label_batch)
    }
}
<commit_msg>add in channels dimension<commit_after>#![allow(deprecated)]
use std::path::PathBuf;
use std::{io, fs, slice};
use curl::easy::Easy;
use memmap::{Mmap, Protection};
use std::io::{Read, Write};
use utils::data::{DatasetIntfRef, DatasetIntf};
use std::rc::Rc;
use tensor::{Tensor, TensorKind, NumLimits};
use torch;
use std::marker::PhantomData;

type Sample<T> = (Tensor<T>, i64);
type CollatedSample<T> = (Tensor<T>, Tensor<i64>);

static URLS: [&str; 4] = ["http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz",
                          "http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz",
                          "http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz",
                          "http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz"];
static RAW_FOLDER: &str = "raw";
static PROCESSED_FOLDER: &str = "processed";
static TRAINING_FILE: &str = "training.pt";
static TEST_FILE: &str = "test.pt";
static NCHANNELS: isize = 1;

fn create_dir_f(arg: PathBuf) -> io::Result<()> {
    let result = fs::create_dir(arg);
    if let Some(err) = result.err() {
        match err.kind() {
            io::ErrorKind::AlreadyExists => Ok(()),
            _ => Err(err),
        }
    } else {
        Ok(())
    }
}

fn check_exists(arg: &PathBuf) -> io::Result<bool> {
    let train = fs::metadata(arg.join(TRAINING_FILE))?;
    let test = fs::metadata(arg.join(TEST_FILE))?;

    return Ok(train.is_file() && test.is_file());
}

fn download(root: &String) -> io::Result<()> {
    let raw_path = PathBuf::from(root).join(RAW_FOLDER);
    let processed_path = PathBuf::from(root).join(PROCESSED_FOLDER);
    create_dir_f(raw_path.clone())?;
    create_dir_f(processed_path.clone())?;

    for url in URLS.iter() {
        println!("downloading {}", url);
        let paths: Vec<_> = url.split("/").collect();
        let mut fname: Vec<_> = paths[5].split("/").collect();
        fname = fname[0].split(".").collect();
        let file_path = raw_path.join(fname[0]);
        let mut file = fs::OpenOptions::new()
            .write(true)
            .create(true)
            .open(file_path)?;
        let mut data = Vec::new();
        let mut handle = Easy::new();
        handle.url(url).unwrap();
        {
            let mut transfer = handle.transfer();
            transfer
                .write_function(|new_data| {
                                    data.extend_from_slice(new_data);
                                    Ok(new_data.len())
                                })
                .unwrap();
            transfer.perform().unwrap();
        }
        let mut output = Vec::with_capacity(data.len() + 1100000);
        let mut gz = ::flate2::read::GzDecoder::new(data.as_slice())?;
        gz.read_to_end(&mut output)?;

        file.write_all(output.as_slice())?;
    }
    println!("Proceeding");

    let training_set =
        vec![read_image_file(raw_path.join("train-images-idx3-ubyte"))?,
                            read_label_file(raw_path.join("train-labels-idx1-ubyte"))?];
    let test_set = vec![read_image_file(raw_path.join("t10k-images-idx3-ubyte"))?,
                        read_label_file(raw_path.join("t10k-labels-idx1-ubyte"))?];

    torch::save(processed_path.join(TRAINING_FILE), &training_set)?;
    torch::save(processed_path.join(TEST_FILE), &test_set)?;

    Ok(())
}

fn read_image_file(path: PathBuf) -> io::Result<TensorKind> {
    let fp = Mmap::open_path(path, Protection::Read)?;
    let (length, nrows, ncols);
    {
        let data = unsafe { slice::from_raw_parts(fp.ptr() as *const i32, fp.len()) };
        assert_eq!(i32::from_be(data[0]), 2051);
        length = i32::from_be(data[1]) as usize;
        nrows = i32::from_be(data[2]) as usize;
        ncols = i32::from_be(data[3]) as usize;
    }
    let data = unsafe { fp.as_slice() };
    let mut images: Vec<Vec<u8>> = Vec::with_capacity(length);
    let mut idx = 16;
    // XXX transpose here to be H x W ?
    for _ in 0..length {
        let mut img = Vec::with_capacity(nrows * ncols);
        for _ in 0..nrows * ncols {
            img.push(data[idx]);
            idx += 1;
        }
        images.push(img)
    }
    Ok(torch::byte_tensor(images).view(&[-1, NCHANNELS, 28, 28]).into())
}

fn read_label_file(path: PathBuf) -> io::Result<TensorKind> {
    let fp = Mmap::open_path(path, Protection::Read)?;
    let length;
    {
        let data = unsafe { slice::from_raw_parts(fp.ptr() as *const i32, fp.len()) };
        assert_eq!(i32::from_be(data[0]), 2049);
        length = i32::from_be(data[1]) as usize;
    }
    let data = unsafe { fp.as_slice() };
    let mut labels: Vec<u8> = Vec::with_capacity(length);
    for i in 0..length {
        labels.push(data[8 + i])
    }
    Ok(torch::byte_tensor(labels).into())
}

pub struct MNIST<T: NumLimits> {
    pub root: String,
    pub train: bool,
    pub data: Tensor<u8>,
    pub labels: Tensor<u8>,
    pub transform: Option<Xfrm>,
    phantom: PhantomData<T>,
}

#[builder(pattern="owned")]
#[derive(Builder)]
pub struct MNISTArgs {
    root: String,
    #[builder(default="true")]
    train: bool,
    #[builder(default="false")]
    download: bool,
}

type Xfrm = Box<fn(&TensorKind) -> TensorKind>;
impl MNISTArgsBuilder {
    pub fn done<T: NumLimits + 'static>(self,
                                        xfrm: Option<Xfrm>)
                                        -> DatasetIntfRef<CollatedSample<T>> {
        let args = self.build().unwrap();
        Rc::new(MNIST::new(args, xfrm))
    }
}

impl<T: NumLimits> MNIST<T> {
    pub fn build(root: &str) -> MNISTArgsBuilder {
        MNISTArgsBuilder::default().root(root.into())
    }
    pub fn new(args: MNISTArgs, xfrm: Option<Xfrm>) -> Self {
        if args.download {
            download(&args.root).expect("download failed");
        }
        let processed_path = PathBuf::from(&args.root).join(PROCESSED_FOLDER);
        if !check_exists(&processed_path).expect("Dataset not found, try downloading") {
            panic!("Dataset not found, use download=true to download it");
        }
        let mut v: Vec<TensorKind> = if args.train {
            torch::load(processed_path.join(TRAINING_FILE)).expect("torch load failed")
        } else {
            torch::load(processed_path.join(TEST_FILE)).expect("torch load failed")
        };
        let (data, labels) = (v.remove(0), v.remove(0));

        MNIST {
            root: args.root.clone(),
            train: args.train,
            data: data.into(),
            labels: labels.into(),
            transform: xfrm,
            phantom: PhantomData,
        }
    }
    fn index(&self, idx: usize) -> Sample<u8> {
        let img = self.data.s([idx]);
        let img = if let Some(ref transform) = self.transform {
            transform(&img.into()).into()
        } else {
            img.clone()
        };
        (img, self.labels[idx].clone() as i64)
    }
}

impl<T: NumLimits> DatasetIntf for MNIST<T> {
    type Batch = CollatedSample<T>;
    fn len(&self) -> usize {
        if self.train { 60000 } else { 10000 }
    }
    fn collate(&self, sample: Vec<usize>) -> Self::Batch {
        println!("collate called len: {}", sample.len());
        let v: Vec<Sample<u8>> = sample.into_iter().map(|i| self.index(i)).collect();
        let labels: Vec<i64> = v.iter().map(|&(_, ref t)| *t).collect();
        let imgs: Vec<Tensor<u8>> = v.into_iter().map(|(d, _)| d).collect();
        // XXX should check for case of double
        let img_batch: Tensor<T> = torch::tensor(imgs);
        let label_batch = torch::long_tensor(labels);
        (img_batch, label_batch)
    }
}
<|endoftext|>


<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/. */

#![feature(plugin)]
#![plugin(plugins)]

fn main() {
    let x = "foo".to_string();
    //~^ ERROR str.to_owned() is more efficient than str.to_string()

    let x = &x[..];
    x.to_string();
    //~^ ERROR str.to_owned() is more efficient than str.to_string()
}
<commit_msg>Remove str_to_string compile test<commit_after><|endoftext|>


<commit_before>//! Transaction store
//! This is now by experiment split in two files
//! to see if this improves output-reading performance
//!
//!
//! This is a bit messy for now as we're not using typed access to these store
//! as they are still WIP

use std::mem;

use buffer::*;
use config;
use store::flatfileset::FlatFileSet;
use store::TxPtr;

use transaction::Transaction;
use transaction::TransactionError;
use store::flatfileset::FlatFilePtr;

const MB:                 u64 = 1024 * 1024;
const FILE_SIZE:          u64 = 2 * 1024 * MB ;
const MAX_CONTENT_SIZE:   u64 = FILE_SIZE - 10 * MB ;

/// Transaction store
pub struct Transactions {

    // part1 stores the header& inputs
    transactions1: FlatFileSet<TxPtr>,

    // part2 stores the outputs
    transactions2: FlatFileSet<TxPtr>,

}

impl Clone for Transactions {

    // Explicit cloning can be used to allow concurrent access.
    fn clone(&self) -> Transactions {

        Transactions {

            transactions1: self.transactions1.clone(),
            transactions2: self.transactions2.clone()
        }

    }
}

impl Transactions
{
    /// Opens the transaction store at the location given in the config
    ///
    /// Creates a new fileset if needed
    pub fn new(cfg: &config::Config) -> Transactions {
        let dir1 = &cfg.root.clone().join("transactions1");
        let dir2 = &cfg.root.clone().join("transactions2");

        Transactions {
            transactions1: FlatFileSet::new(dir1, "t1-", FILE_SIZE, MAX_CONTENT_SIZE),
            transactions2: FlatFileSet::new(dir2, "t2-", FILE_SIZE, MAX_CONTENT_SIZE)
        }
    }

    /// Writes the transaction to the store
    pub fn write(&mut self, tx: &Transaction) -> TxPtr {

        // We're doing "manual" serialization for now;
        // to test performance
        // TODO use typed structure

        // Split the transaction
        let start = tx.txs_out_idx[0];
        let raw_tx = tx.to_raw();
        let raw_part1 = &raw_tx[..start as usize];
        let raw_part2 = &raw_tx[start as usize..];

        // write first part
        let part1_ptr = self.transactions1.write(raw_part1);

        let header = vec![
        part1_ptr.get_file_number() as u32,
        part1_ptr.get_file_offset() as u32,
        tx.txs_out_idx.len() as u32];

        // gather the bytes
        let part2: Vec<u8> =

        header.into_iter()

            // add output indices, with corrected start
            .chain(tx.txs_out_idx.iter().map(|idx| idx - start))

            // convert to bytes
            .flat_map(|x| u32_to_bytes(x).into_iter())

            // add raw outputs data
            .chain(raw_part2.iter().map(|x| *x))

            .collect();

        self.transactions2.write(part2.as_slice())
    }

    /// Reads the full transaction from the given pointer
    pub fn read(&mut self, ptr: TxPtr) -> Vec<u8> {
        let (tx,_) = self.next(ptr);
        tx
    }

    /// Reads the full transaction from the given pointer
    /// Returns the transaction and a pointer to the next one
    pub fn next(&mut self, ptr: TxPtr) -> (Vec<u8>, TxPtr) {

        let part2 = self.transactions2.read(ptr);

        let part1_ptr = TxPtr::new(
            bytes_to_u32(&part2[0..4]) as i16,
            bytes_to_u32(&part2[4..8]) as u64
        );

        let part1 = self.transactions1.read(part1_ptr);

        // strip header of part2
        let output_count = bytes_to_u32(&part2[8..12]) as usize;
        let header_size = 4 + 4 + 4 + output_count * 4;
        let part2 = &part2[header_size..];

        // gather
        let mut tx: Vec<u8> = part1.into_iter().map(|&x| x).collect();
        tx.extend_from_slice(part2);

        (tx, ptr.offset(part2.len() as u32 + 4))
    }

    /// Returns only an output from the given transaction
    /// The resulting Vec overflows until the end of the transaction
    pub fn read_output(&mut self, ptr: TxPtr, output_index: u32) -> Option<Vec<u8>> {

        // read all outputs
        let part2 = self.transactions2.read(ptr);
        let output_count = bytes_to_u32(&part2[8..12]);

        if output_count < output_index {
            return None;
        }

        let output_offset_pos = 12 + 4 * output_index as usize;
        let output_offset = bytes_to_u32(&part2[output_offset_pos..output_offset_pos+4]) as usize;

        let output_offset_from_part2 = output_offset + 12 + 4 * output_count as usize;

        println!("Finding output; full part2={:?}, offset_pos={},offset={}", part2, output_offset_pos, output_offset);

        Some(part2[output_offset_from_part2..].into_iter().map(|&x| x).collect())
    }
}

// helper
fn bytes_to_u32(x: &[u8]) -> u32 {
    ((x[0] as u32) << 24) |
        ((x[1] as u32) << 16) |
        ((x[2] as u32) << 8) |
        ((x[3] as u32) << 0)
}

// helper
fn u32_to_bytes(x: u32) -> Vec<u8> {
    vec![
    (x >> 24) as u8, (x >> 16) as u8, (x >> 8) as u8, (x >> 0) as u8,
    ]
}

#[cfg(test)]
mod tests {

    use super::*;
    use buffer::*;
    use buffer::ToRaw;

    #[macro_use]
    use builders::*;

    #[test]
    fn test_read_write() {
        tx_builder!(bld);

        let tx1 = tx!(bld; coinbase => a;12);

        let tx2 = tx!(bld; a     => b, c );
        let tx3 = tx!(bld; a,b   => c,d,e,f,g );

        let tx1p = Transaction::parse(&mut Buffer::new(&tx1)).unwrap();
        let tx2p = Transaction::parse(&mut Buffer::new(&tx2)).unwrap();
        let tx3p = Transaction::parse(&mut Buffer::new(&tx3)).unwrap();

        let mut store = ::store::Store::new(& test_cfg!());

        let ptr  = store.transactions.write(&tx1p);
        let read = store.transactions.read(ptr);
        assert_eq!(tx1, read.as_slice());

        let ptr = store.transactions.write(&tx2p);
        let read = store.transactions.read(ptr);
        assert_eq!(tx2, read.as_slice());

        let ptr = store.transactions.write(&tx3p);
        let read = store.transactions.read(ptr);
        assert_eq!(tx3, read.as_slice());

    }

    #[test]
    fn test_read_output() {
        tx_builder!(bld);

        let _tx1 = tx!(bld; coinbase => a;12);

        let _tx2 = tx!(bld; a     => b, c );
        let tx3 = tx!(bld; a,b   => c,d,e,f,g );

        println!("Transaction={:?}", tx3);

        let tx3p = Transaction::parse(&mut Buffer::new(&tx3)).unwrap();

        let mut store = ::store::Store::new(& test_cfg!());

        let ptr  = store.transactions.write(&tx3p);

        // read only one output
        let read_out_bytes = store.transactions.read_output(ptr, 3).unwrap();
        let read_out = ::transaction::TxOutput::parse(&mut Buffer::new(&read_out_bytes)).unwrap();

        assert_eq!(read_out, tx3p.txs_out[3]);

    }

}<commit_msg>Fix pruning for split-up transactions<commit_after>//! Transaction store
//! This is now by experiment split in two files
//! to see if this improves output-reading performance
//!
//!
//! This is a bit messy for now as we're not using typed access to these store
//! as they are still WIP

use std::mem;

use buffer::*;
use config;
use store::flatfileset::FlatFileSet;
use store::TxPtr;

use transaction::Transaction;
use transaction::TransactionError;
use store::flatfileset::FlatFilePtr;

const MB:                 u64 = 1024 * 1024;
const FILE_SIZE:          u64 = 2 * 1024 * MB ;
const MAX_CONTENT_SIZE:   u64 = FILE_SIZE - 10 * MB ;

/// Transaction store
pub struct Transactions {

    // part1 stores the header& inputs
    transactions1: FlatFileSet<TxPtr>,

    // part2 stores the outputs
    transactions2: FlatFileSet<TxPtr>,

}

impl Clone for Transactions {

    // Explicit cloning can be used to allow concurrent access.
    fn clone(&self) -> Transactions {

        Transactions {

            transactions1: self.transactions1.clone(),
            transactions2: self.transactions2.clone()
        }

    }
}

impl Transactions
{
    /// Opens the transaction store at the location given in the config
    ///
    /// Creates a new fileset if needed
    pub fn new(cfg: &config::Config) -> Transactions {
        let dir1 = &cfg.root.clone().join("transactions1");
        let dir2 = &cfg.root.clone().join("transactions2");

        Transactions {
            transactions1: FlatFileSet::new(dir1, "t1-", FILE_SIZE, MAX_CONTENT_SIZE),
            transactions2: FlatFileSet::new(dir2, "t2-", FILE_SIZE, MAX_CONTENT_SIZE)
        }
    }

    /// Writes the transaction to the store
    pub fn write(&mut self, tx: &Transaction) -> TxPtr {

        // We're doing "manual" serialization for now;
        // to test performance
        // TODO use typed structure

        // Split the transaction
        let start = tx.txs_out_idx[0];
        let raw_tx = tx.to_raw();
        let raw_part1 = &raw_tx[..start as usize];
        let raw_part2 = &raw_tx[start as usize..];

        // write first part
        let part1_ptr = self.transactions1.write(raw_part1);

        let header = vec![
        part1_ptr.get_file_number() as u32,
        part1_ptr.get_file_offset() as u32,
        tx.txs_out_idx.len() as u32];

        // gather the bytes
        let part2: Vec<u8> =

        header.into_iter()

            // add output indices, with corrected start
            .chain(tx.txs_out_idx.iter().map(|idx| idx - start))

            // convert to bytes
            .flat_map(|x| u32_to_bytes(x).into_iter())

            // add raw outputs data
            .chain(raw_part2.iter().map(|x| *x))

            .collect();

        self.transactions2.write(part2.as_slice())
    }

    /// Reads the full transaction from the given pointer
    pub fn read(&mut self, ptr: TxPtr) -> Vec<u8> {
        let (tx,_) = self.next(ptr);
        tx
    }

    /// Reads the full transaction from the given pointer
    /// Returns the transaction and a pointer to the next one
    pub fn next(&mut self, ptr: TxPtr) -> (Vec<u8>, TxPtr) {

        let part2 = self.transactions2.read(ptr);
        let len = part2.len() as u32;

        let part1_ptr = TxPtr::new(
            bytes_to_u32(&part2[0..4]) as i16,
            bytes_to_u32(&part2[4..8]) as u64
        );

        let part1 = self.transactions1.read(part1_ptr);

        // strip header of part2
        let output_count = bytes_to_u32(&part2[8..12]) as usize;
        let header_size = 4 + 4 + 4 + output_count * 4;
        let part2 = &part2[header_size..];

        // gather
        let mut tx: Vec<u8> = part1.into_iter().map(|&x| x).collect();
        tx.extend_from_slice(part2);

        (tx, ptr.offset(len + 4))
    }

    /// Returns only an output from the given transaction
    /// The resulting Vec overflows until the end of the transaction
    pub fn read_output(&mut self, ptr: TxPtr, output_index: u32) -> Option<Vec<u8>> {

        // read all outputs
        let part2 = self.transactions2.read(ptr);
        let output_count = bytes_to_u32(&part2[8..12]);

        if output_count < output_index {
            return None;
        }

        let output_offset_pos = 12 + 4 * output_index as usize;
        let output_offset = bytes_to_u32(&part2[output_offset_pos..output_offset_pos+4]) as usize;

        let output_offset_from_part2 = output_offset + 12 + 4 * output_count as usize;

        println!("Finding output; full part2={:?}, offset_pos={},offset={}", part2, output_offset_pos, output_offset);

        Some(part2[output_offset_from_part2..].into_iter().map(|&x| x).collect())
    }
}

// helper
fn bytes_to_u32(x: &[u8]) -> u32 {
    ((x[0] as u32) << 24) |
        ((x[1] as u32) << 16) |
        ((x[2] as u32) << 8) |
        ((x[3] as u32) << 0)
}

// helper
fn u32_to_bytes(x: u32) -> Vec<u8> {
    vec![
    (x >> 24) as u8, (x >> 16) as u8, (x >> 8) as u8, (x >> 0) as u8,
    ]
}

#[cfg(test)]
mod tests {

    use super::*;
    use buffer::*;
    use buffer::ToRaw;

    #[macro_use]
    use builders::*;

    #[test]
    fn test_read_write() {
        tx_builder!(bld);

        let tx1 = tx!(bld; coinbase => a;12);

        let tx2 = tx!(bld; a     => b, c );
        let tx3 = tx!(bld; a,b   => c,d,e,f,g );

        let tx1p = Transaction::parse(&mut Buffer::new(&tx1)).unwrap();
        let tx2p = Transaction::parse(&mut Buffer::new(&tx2)).unwrap();
        let tx3p = Transaction::parse(&mut Buffer::new(&tx3)).unwrap();

        let mut store = ::store::Store::new(& test_cfg!());

        let ptr  = store.transactions.write(&tx1p);
        let read = store.transactions.read(ptr);
        assert_eq!(tx1, read.as_slice());

        let ptr = store.transactions.write(&tx2p);
        let read = store.transactions.read(ptr);
        assert_eq!(tx2, read.as_slice());

        let ptr = store.transactions.write(&tx3p);
        let read = store.transactions.read(ptr);
        assert_eq!(tx3, read.as_slice());

    }

    #[test]
    fn test_read_output() {
        tx_builder!(bld);

        let _tx1 = tx!(bld; coinbase => a;12);

        let _tx2 = tx!(bld; a     => b, c );
        let tx3 = tx!(bld; a,b   => c,d,e,f,g );

        println!("Transaction={:?}", tx3);

        let tx3p = Transaction::parse(&mut Buffer::new(&tx3)).unwrap();

        let mut store = ::store::Store::new(& test_cfg!());

        let ptr  = store.transactions.write(&tx3p);

        // read only one output
        let read_out_bytes = store.transactions.read_output(ptr, 3).unwrap();
        let read_out = ::transaction::TxOutput::parse(&mut Buffer::new(&read_out_bytes)).unwrap();

        assert_eq!(read_out, tx3p.txs_out[3]);

    }

}<|endoftext|>


<commit_before>//! Unix peer credentials.

// NOTE: Code in this file is heavily based on work done in PR 13 from the tokio-uds repository on
//       GitHub.
//
//       For reference, the link is here: https://github.com/tokio-rs/tokio-uds/pull/13
//       Credit to Martin Habovštiak (GitHub username Kixunil) and contributors for this work.

use libc::{gid_t, pid_t, uid_t};

/// Credentials for a UNIX process for credentials passing.
#[unstable(feature = "peer_credentials_unix_socket", issue = "42839", reason = "unstable")]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct UCred {
    pub uid: uid_t,
    pub gid: gid_t,
    // pid field is an option because it is not supported on some platforms.
    pub pid: Option<pid_t>,
}

#[cfg(any(target_os = "android", target_os = "linux"))]
pub use self::impl_linux::peer_cred;

#[cfg(any(
    target_os = "dragonfly",
    target_os = "freebsd",
    target_os = "ios",
    target_os = "macos",
    target_os = "openbsd"
))]
pub use self::impl_bsd::peer_cred;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod impl_linux {
    use super::UCred;
    use crate::os::unix::io::AsRawFd;
    use crate::os::unix::net::UnixStream;
    use crate::{io, mem};

    pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> {
        use libc::{c_void, ucred};

        let ucred_size = mem::size_of::<ucred>();

        // Trivial sanity checks.
        assert!(mem::size_of::<u32>() <= mem::size_of::<usize>());
        assert!(ucred_size <= u32::MAX as usize);

        let mut ucred_size = ucred_size as u32;
        let mut ucred: ucred = ucred { pid: 1, uid: 1, gid: 1 };

        unsafe {
            let ret = libc::getsockopt(
                socket.as_raw_fd(),
                libc::SOL_SOCKET,
                libc::SO_PEERCRED,
                &mut ucred as *mut ucred as *mut c_void,
                &mut ucred_size,
            );

            if ret == 0 && ucred_size as usize == mem::size_of::<ucred>() {
                Ok(UCred { uid: ucred.uid, gid: ucred.gid, pid: Some(ucred.pid) })
            } else {
                Err(io::Error::last_os_error())
            }
        }
    }
}

#[cfg(any(
    target_os = "dragonfly",
    target_os = "macos",
    target_os = "ios",
    target_os = "freebsd",
    target_os = "openbsd"
))]
pub mod impl_bsd {
    use super::UCred;
    use crate::io;
    use crate::os::unix::io::AsRawFd;
    use crate::os::unix::net::UnixStream;

    pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> {
        let mut cred = UCred { uid: 1, gid: 1, pid: None };
        unsafe {
            let ret = libc::getpeereid(socket.as_raw_fd(), &mut cred.uid, &mut cred.gid);

            if ret == 0 { Ok(cred) } else { Err(io::Error::last_os_error()) }
        }
    }
}
<commit_msg>Add documentation to public fields of UCred struct<commit_after>//! Unix peer credentials.

// NOTE: Code in this file is heavily based on work done in PR 13 from the tokio-uds repository on
//       GitHub.
//
//       For reference, the link is here: https://github.com/tokio-rs/tokio-uds/pull/13
//       Credit to Martin Habovštiak (GitHub username Kixunil) and contributors for this work.

use libc::{gid_t, pid_t, uid_t};

/// Credentials for a UNIX process for credentials passing.
#[unstable(feature = "peer_credentials_unix_socket", issue = "42839", reason = "unstable")]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct UCred {
    /// The UID part of the peer credential. This is the effective UID of the process at the domain
    /// socket's endpoint.
    pub uid: uid_t,
    /// The GID part of the peer credential. This is the effective GID of the process at the domain
    /// socket's endpoint.
    pub gid: gid_t,
    /// The PID part of the peer credential. This field is optional because the PID part of the
    /// peer credentials is not supported on every platform. On platforms where the mechanism to
    /// discover the PID exists, this field will be populated to the PID of the process at the
    /// domain socket's endpoint. Otherwise, it will be set to None.
    pub pid: Option<pid_t>,
}

#[cfg(any(target_os = "android", target_os = "linux"))]
pub use self::impl_linux::peer_cred;

#[cfg(any(
    target_os = "dragonfly",
    target_os = "freebsd",
    target_os = "ios",
    target_os = "macos",
    target_os = "openbsd"
))]
pub use self::impl_bsd::peer_cred;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod impl_linux {
    use super::UCred;
    use crate::os::unix::io::AsRawFd;
    use crate::os::unix::net::UnixStream;
    use crate::{io, mem};

    pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> {
        use libc::{c_void, ucred};

        let ucred_size = mem::size_of::<ucred>();

        // Trivial sanity checks.
        assert!(mem::size_of::<u32>() <= mem::size_of::<usize>());
        assert!(ucred_size <= u32::MAX as usize);

        let mut ucred_size = ucred_size as u32;
        let mut ucred: ucred = ucred { pid: 1, uid: 1, gid: 1 };

        unsafe {
            let ret = libc::getsockopt(
                socket.as_raw_fd(),
                libc::SOL_SOCKET,
                libc::SO_PEERCRED,
                &mut ucred as *mut ucred as *mut c_void,
                &mut ucred_size,
            );

            if ret == 0 && ucred_size as usize == mem::size_of::<ucred>() {
                Ok(UCred { uid: ucred.uid, gid: ucred.gid, pid: Some(ucred.pid) })
            } else {
                Err(io::Error::last_os_error())
            }
        }
    }
}

#[cfg(any(
    target_os = "dragonfly",
    target_os = "macos",
    target_os = "ios",
    target_os = "freebsd",
    target_os = "openbsd"
))]
pub mod impl_bsd {
    use super::UCred;
    use crate::io;
    use crate::os::unix::io::AsRawFd;
    use crate::os::unix::net::UnixStream;

    pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> {
        let mut cred = UCred { uid: 1, gid: 1, pid: None };
        unsafe {
            let ret = libc::getpeereid(socket.as_raw_fd(), &mut cred.uid, &mut cred.gid);

            if ret == 0 { Ok(cred) } else { Err(io::Error::last_os_error()) }
        }
    }
}
<|endoftext|>


<commit_before>use runtime::Runtime;

/**
 * A function which lets the user provide content by editing a temp files which gets removed after
 * the function got the content from it.
 */
pub fn let_user_provide_content(rt: &Runtime) -> Option<String> {
    use std::io::Read;
    use std::fs::File;
    use std::process::Command;

    let filepath        = "/tmp/imag-tmp.md";
    let file_created    = File::create(filepath)
                                .map(|_| true)
                                .unwrap_or(false);

    if !file_created {
        warn!("Could not create temporary file for user input!");
        return None;
    }

    let output = {
        let mut cmd = rt.editor();
        cmd.arg(filepath);
        debug!("cmd = {:?}", cmd);
        cmd.spawn()
           .and_then(|child| {
               child.wait_with_output()
            })
    };

    let process_out = output.map_err(|e| {
        error!("Editor call failed");
        debug!("Editor call failed: {:?}", e);
        return None as Option<String>;
    }).unwrap();

    if !process_out.status.success() {
        error!("Editor call failed");
        debug!("status = {:?}", process_out.status);
        debug!("stdout = {:?}", String::from_utf8(process_out.stdout));
        debug!("stderr = {:?}", String::from_utf8(process_out.stderr));
        return None;
    }

    let mut contents = String::new();
    File::open(filepath).map(|mut file| {
        file.read_to_string(&mut contents);
        Some(contents)
    }).unwrap_or(None)
}

/**
 * Edit some content in a temporary file. If anything failes within this routine, it returns the
 * old content and false.
 * If the editing succeeded, it returns the new content and true
 */
pub fn edit_content(rt: &Runtime, old_content: String) -> (String, bool) {
    use std::io::Read;
    use std::io::Write;
    use std::fs::File;
    use std::process::Command;
    use std::process::exit;

    let filepath = "/tmp/imag-tmp.md";
    {
        let mut file = match File::create(filepath) {
            Ok(f) => f,
            Err(e) => {
                error!("Error creating file {}", filepath);
                debug!("Error creating file at '{}', error = {}", filepath, e);
                exit(1);
            }
        };

        file.write(old_content.as_ref());
    }
    debug!("Ready with putting old content into the file");

    let output = {
        let mut cmd = rt.editor();
        cmd.arg(filepath);
        debug!("cmd = {:?}", cmd);
        cmd.spawn()
           .and_then(|child| child.wait_with_output())
    };

    let process_out = output.map_err(|e| {
        error!("Editor call failed");
        debug!("Editor call failed: {:?}", e);
        return None as Option<String>;
    }).unwrap();

    if !process_out.status.success() {
        error!("Editor call failed");
        debug!("status = {:?}", process_out.status);
        debug!("stdout = {:?}", String::from_utf8(process_out.stdout));
        debug!("stderr = {:?}", String::from_utf8(process_out.stderr));
        return (old_content, false);
    }

    let mut contents = String::new();
    File::open(filepath).map(|mut file| {
        file.read_to_string(&mut contents).map_err(|e| {
            error!("Error reading content: {}", e);
            debug!("Error reading content: {:?}", e);
            exit(1);
        }).is_ok();
        (contents, true)
    }).unwrap_or((old_content, false))
}

<commit_msg>Use return value: fail if content could not be written<commit_after>use runtime::Runtime;

/**
 * A function which lets the user provide content by editing a temp files which gets removed after
 * the function got the content from it.
 */
pub fn let_user_provide_content(rt: &Runtime) -> Option<String> {
    use std::io::Read;
    use std::fs::File;
    use std::process::Command;

    let filepath        = "/tmp/imag-tmp.md";
    let file_created    = File::create(filepath)
                                .map(|_| true)
                                .unwrap_or(false);

    if !file_created {
        warn!("Could not create temporary file for user input!");
        return None;
    }

    let output = {
        let mut cmd = rt.editor();
        cmd.arg(filepath);
        debug!("cmd = {:?}", cmd);
        cmd.spawn()
           .and_then(|child| {
               child.wait_with_output()
            })
    };

    let process_out = output.map_err(|e| {
        error!("Editor call failed");
        debug!("Editor call failed: {:?}", e);
        return None as Option<String>;
    }).unwrap();

    if !process_out.status.success() {
        error!("Editor call failed");
        debug!("status = {:?}", process_out.status);
        debug!("stdout = {:?}", String::from_utf8(process_out.stdout));
        debug!("stderr = {:?}", String::from_utf8(process_out.stderr));
        return None;
    }

    let mut contents = String::new();
    File::open(filepath).map(|mut file| {
        file.read_to_string(&mut contents);
        Some(contents)
    }).unwrap_or(None)
}

/**
 * Edit some content in a temporary file. If anything failes within this routine, it returns the
 * old content and false.
 * If the editing succeeded, it returns the new content and true
 */
pub fn edit_content(rt: &Runtime, old_content: String) -> (String, bool) {
    use std::io::Read;
    use std::io::Write;
    use std::fs::File;
    use std::process::Command;
    use std::process::exit;

    let filepath = "/tmp/imag-tmp.md";
    {
        let mut file = match File::create(filepath) {
            Ok(f) => f,
            Err(e) => {
                error!("Error creating file {}", filepath);
                debug!("Error creating file at '{}', error = {}", filepath, e);
                exit(1);
            }
        };

        file.write(old_content.as_ref())
            .map_err(|e| {
                error!("Error writing content: {}", e);
                debug!("Error writing content: {:?}", e);
                exit(1);
            }).is_ok();
    }
    debug!("Ready with putting old content into the file");

    let output = {
        let mut cmd = rt.editor();
        cmd.arg(filepath);
        debug!("cmd = {:?}", cmd);
        cmd.spawn()
           .and_then(|child| child.wait_with_output())
    };

    let process_out = output.map_err(|e| {
        error!("Editor call failed");
        debug!("Editor call failed: {:?}", e);
        return None as Option<String>;
    }).unwrap();

    if !process_out.status.success() {
        error!("Editor call failed");
        debug!("status = {:?}", process_out.status);
        debug!("stdout = {:?}", String::from_utf8(process_out.stdout));
        debug!("stderr = {:?}", String::from_utf8(process_out.stderr));
        return (old_content, false);
    }

    let mut contents = String::new();
    File::open(filepath).map(|mut file| {
        file.read_to_string(&mut contents).map_err(|e| {
            error!("Error reading content: {}", e);
            debug!("Error reading content: {:?}", e);
            exit(1);
        }).is_ok();
        (contents, true)
    }).unwrap_or((old_content, false))
}

<|endoftext|>


<commit_before>extern crate glib;
extern crate gstreamer as gst;
extern crate gtk;

#[cfg(any(feature = "profiling-listener", feature = "profiling-tracker"))]
use chrono::Utc;

use std::rc::Rc;
use std::cell::RefCell;

use std::path::PathBuf;

use std::sync::Arc;
use std::sync::mpsc::{channel, Receiver};

use gtk::prelude::*;

use media::{PlaybackContext, ContextMessage};
use media::ContextMessage::*;

use super::{AudioController, ExportController, InfoController, VideoController};

#[derive(Clone, Debug, PartialEq)]
pub enum ControllerState {
    EOS,
    Ready,
    Paused,
    PendingExportToc,
    PendingSelectMedia,
    Playing,
    PlayingRange(u64),
    Stopped,
    Seeking(bool, bool), // (must_switch_to_play, was_paused)
}

const LISTENER_PERIOD: u32 = 250; // 250 ms (4 Hz)
const TRACKER_PERIOD: u32 = 17; //  17 ms (60 Hz)

pub struct MainController {
    window: gtk::ApplicationWindow,
    header_bar: gtk::HeaderBar,
    play_pause_btn: gtk::ToolButton,
    export_toc_btn: gtk::Button,

    video_ctrl: VideoController,
    info_ctrl: Rc<RefCell<InfoController>>,
    audio_ctrl: Rc<RefCell<AudioController>>,
    export_ctrl: Rc<RefCell<ExportController>>,

    context: Option<PlaybackContext>,
    state: ControllerState,

    requires_async_dialog: bool, // when pipeline contains video, dialogs must wait for
    // asyncdone before opening a dialog otherwise the listener
    // may borrow the MainController while the dialog is already
    // using it leading to a borrowing conflict
    this_opt: Option<Rc<RefCell<MainController>>>,
    keep_going: bool,
    listener_src: Option<glib::SourceId>,
    tracker_src: Option<glib::SourceId>,
}

impl MainController {
    pub fn new(builder: &gtk::Builder) -> Rc<RefCell<Self>> {
        let export_toc_btn: gtk::Button = builder.get_object("export_toc-btn").unwrap();
        export_toc_btn.set_sensitive(false);

        let this = Rc::new(RefCell::new(MainController {
            window: builder.get_object("application-window").unwrap(),
            header_bar: builder.get_object("header-bar").unwrap(),
            play_pause_btn: builder.get_object("play_pause-toolbutton").unwrap(),
            export_toc_btn: export_toc_btn,

            video_ctrl: VideoController::new(builder),
            info_ctrl: InfoController::new(builder),
            audio_ctrl: AudioController::new(builder),
            export_ctrl: ExportController::new(builder),

            context: None,
            state: ControllerState::Stopped,

            requires_async_dialog: false,

            this_opt: None,
            keep_going: true,
            listener_src: None,
            tracker_src: None,
        }));

        {
            let mut this_mut = this.borrow_mut();

            let this_rc = Rc::clone(&this);
            this_mut.this_opt = Some(this_rc);

            this_mut.window.connect_delete_event(|_, _| {
                gtk::main_quit();
                Inhibit(false)
            });
            this_mut.window.set_titlebar(&this_mut.header_bar);

            let this_rc = Rc::clone(&this);
            this_mut.play_pause_btn.connect_clicked(move |_| {
                this_rc.borrow_mut().play_pause();
            });

            // TODO: add key bindings to seek by steps
            // play/pause, etc.

            let this_rc = Rc::clone(&this);
            this_mut.export_toc_btn.connect_clicked(move |_| {
                let mut this = this_rc.borrow_mut();

                if this.requires_async_dialog && this.state == ControllerState::Playing {
                    this.hold();
                    this.state = ControllerState::PendingExportToc;
                } else {
                    this.hold();
                    this.export_toc();
                }
            });

            this_mut.video_ctrl.register_callbacks(&this);
            InfoController::register_callbacks(&this_mut.info_ctrl, &this);
            AudioController::register_callbacks(&this_mut.audio_ctrl, &this);
            ExportController::register_callbacks(&this_mut.export_ctrl, &this);
        }

        let open_btn: gtk::Button = builder.get_object("open-btn").unwrap();
        let this_rc = Rc::clone(&this);
        open_btn.connect_clicked(move |_| {
            let mut this = this_rc.borrow_mut();

            if this.requires_async_dialog && this.state == ControllerState::Playing {
                this.hold();
                this.state = ControllerState::PendingSelectMedia;
            } else {
                this.hold();
                this.select_media();
            }
        });

        this
    }

    pub fn show_all(&self) {
        self.window.show_all();
    }

    pub fn get_state(&self) -> &ControllerState {
        &self.state
    }

    fn play_pause(&mut self) {
        let context = match self.context.take() {
            Some(context) => context,
            None => {
                self.select_media();
                return;
            }
        };

        if self.state != ControllerState::EOS {
            match context.get_state() {
                gst::State::Paused => {
                    self.register_tracker();
                    self.play_pause_btn.set_icon_name("media-playback-pause");
                    self.state = ControllerState::Playing;
                    context.play().unwrap();
                    self.context = Some(context);
                }
                gst::State::Playing => {
                    context.pause().unwrap();
                    self.play_pause_btn.set_icon_name("media-playback-start");
                    self.remove_tracker();
                    self.state = ControllerState::Paused;
                    self.context = Some(context);
                }
                _ => {
                    self.context = Some(context);
                    self.select_media();
                }
            };
        } else {
            // Restart the stream from the begining
            self.context = Some(context);
            self.seek(0, true); // accurate (slow)
        }
    }

    pub fn seek(&mut self, position: u64, accurate: bool) {
        if self.state != ControllerState::Stopped {
            if self.state == ControllerState::Playing || self.state == ControllerState::Paused {
                self.info_ctrl.borrow_mut().seek(position, &self.state);
                self.audio_ctrl.borrow_mut().seek(position, &self.state);
            }

            let must_switch_to_play = (self.state == ControllerState::EOS) ||
                (self.state == ControllerState::Ready);
            self.state = ControllerState::Seeking(
                must_switch_to_play,
                self.state == ControllerState::Paused,
            );

            self.context
                .as_ref()
                .expect("MainController::seek no context")
                .seek(position, accurate);
        }
    }

    pub fn play_range(&mut self, start: u64, end: u64, pos_to_restore: u64) {
        if self.state == ControllerState::Paused {
            self.info_ctrl.borrow_mut().start_play_range();
            self.audio_ctrl.borrow_mut().start_play_range();

            self.state = ControllerState::PlayingRange(pos_to_restore);

            self.context
                .as_ref()
                .expect("MainController::play_range no context")
                .seek_range(start, end);

            self.register_tracker();
        }
    }

    pub fn get_position(&mut self) -> u64 {
        self.context
            .as_mut()
            .expect("MainController::get_position no context")
            .get_position()
    }

    fn hold(&mut self) {
        self.remove_tracker();
        if let Some(context) = self.context.as_mut() {
            context.pause().unwrap();
        };

        self.play_pause_btn.set_icon_name("media-playback-start");
        self.window.set_sensitive(false);
    }

    fn export_toc(&mut self) {
        if let Some(mut context) = self.context.take() {
            self.info_ctrl.borrow().export_chapters(&mut context);
            self.export_ctrl.borrow_mut().open(context);
            self.state = ControllerState::Paused;
        }
    }

    pub fn restore_context(&mut self, context: PlaybackContext) {
        self.context = Some(context);
        self.state = ControllerState::Paused;
        self.window.set_sensitive(true);
    }

    fn remove_listener(&mut self) {
        if let Some(source_id) = self.listener_src.take() {
            glib::source_remove(source_id);
        }
    }

    fn register_listener(&mut self, timeout: u32, ui_rx: Receiver<ContextMessage>) {
        if self.listener_src.is_some() {
            return;
        }

        let this_rc = Rc::clone(self.this_opt.as_ref().unwrap());

        self.listener_src = Some(gtk::timeout_add(timeout, move || {
            let mut keep_going = true;

            for message in ui_rx.try_iter() {
                match message {
                    AsyncDone => {
                        let mut this = this_rc.borrow_mut();
                        match this.state {
                            ControllerState::PendingSelectMedia => this.select_media(),
                            ControllerState::PendingExportToc => this.export_toc(),
                            ControllerState::Seeking(must_switch_to_play, was_paused) => {
                                if must_switch_to_play {
                                    this.context
                                        .as_ref()
                                        .expect("MainController::listener(AsyncDone) no context")
                                        .play()
                                        .unwrap();
                                    this.register_tracker();
                                    this.play_pause_btn.set_icon_name("media-playback-pause");
                                    this.state = ControllerState::Playing;
                                } else if was_paused {
                                    this.state = ControllerState::Paused;
                                } else {
                                    this.state = ControllerState::Playing;
                                }
                            }
                            _ => (),
                        }
                    }
                    InitDone => {
                        let mut this = this_rc.borrow_mut();

                        let context = this.context.take().expect(
                            "MainController: InitDone but no context available",
                        );

                        this.requires_async_dialog = context
                            .info
                            .lock()
                            .expect("MainController::listener(InitDone) failed to lock media info")
                            .video_best
                            .is_some();

                        this.header_bar.set_subtitle(
                            Some(context.file_name.as_str()),
                        );

                        this.video_ctrl.new_media(&context);
                        this.info_ctrl.borrow_mut().new_media(&context);
                        this.audio_ctrl.borrow_mut().new_media(&context);

                        this.context = Some(context);

                        this.state = ControllerState::Ready;
                    }
                    Eos => {
                        let mut this = this_rc.borrow_mut();
                        match this.state {
                            ControllerState::PlayingRange(pos_to_restore) => {
                                // end of range => pause and seek back to pos_to_restore
                                this.context
                                    .as_ref()
                                    .expect("MainController::listener(eos) no context")
                                    .pause()
                                    .unwrap();
                                this.state = ControllerState::Paused;
                                this.remove_tracker();
                                this.seek(pos_to_restore, true); // accurate
                            }
                            ControllerState::Playing => { // regular EOS
                                let position = this.get_position();
                                this.info_ctrl.borrow_mut().tick(position, true);

                                this.audio_ctrl.borrow_mut().tick();

                                #[cfg(feature = "trace-main-controller")]
                                println!("MainController::listener(eos)");

                                this.play_pause_btn.set_icon_name("media-playback-start");
                                this.state = ControllerState::EOS;

                                // The tracker will be register again in case of a seek
                                this.remove_tracker();
                            }
                            _ => { // inconsistent state
                                println!("MainController::listener(eos) inconsistent state {:?}",
                                    this.state);
                                this.state = ControllerState::Paused;
                            }
                        }
                    }
                    FailedToOpenMedia => {
                        eprintln!("ERROR: failed to open media");

                        let mut this = this_rc.borrow_mut();

                        this.context = None;
                        this.keep_going = false;
                        keep_going = false;
                    }
                    _ => (),
                };

                if !keep_going {
                    break;
                }
            }

            if !keep_going {
                let mut this = this_rc.borrow_mut();
                this.listener_src = None;
                this.tracker_src = None;
            }

            glib::Continue(keep_going)
        }));
    }

    fn remove_tracker(&mut self) {
        if let Some(source_id) = self.tracker_src.take() {
            glib::source_remove(source_id);
        }
    }

    fn register_tracker(&mut self) {
        if self.tracker_src.is_some() {
            return;
        }

        let this_rc = Rc::clone(self.this_opt.as_ref().unwrap());

        self.tracker_src = Some(gtk::timeout_add(TRACKER_PERIOD, move || {
            #[cfg(feature = "profiling-tracker")]
            let start = Utc::now();

            let mut this = this_rc.borrow_mut();

            #[cfg(feature = "profiling-tracker")]
            let before_tick = Utc::now();

            {
                let position = this.context
                    .as_mut()
                    .expect("MainController::tracker no context while getting position")
                    .get_position();
                this.info_ctrl.borrow_mut().tick(position, false);
                this.audio_ctrl.borrow_mut().tick();
            }

            #[cfg(feature = "profiling-tracker")]
            let end = Utc::now();

            #[cfg(feature = "profiling-tracker")]
            println!(
                "tracker,{},{},{}",
                start.time().format("%H:%M:%S%.6f"),
                before_tick.time().format("%H:%M:%S%.6f"),
                end.time().format("%H:%M:%S%.6f"),
            );

            glib::Continue(this.keep_going)
        }));
    }

    fn select_media(&mut self) {
        let file_dlg = gtk::FileChooserDialog::new(
            Some("Open a media file"),
            Some(&self.window),
            gtk::FileChooserAction::Open,
        );
        // Note: couldn't find equivalents for STOCK_OK
        file_dlg.add_button("Open", gtk::ResponseType::Ok.into());

        if file_dlg.run() == gtk::ResponseType::Ok.into() {
            if let Some(ref context) = self.context {
                context.stop();
            }
            self.open_media(file_dlg.get_filename().unwrap());
        } else {
            self.state = ControllerState::Paused;
            self.window.set_sensitive(true);
        }

        file_dlg.close();
    }

    fn open_media(&mut self, filepath: PathBuf) {
        self.remove_listener();

        self.info_ctrl.borrow_mut().cleanup();
        self.audio_ctrl.borrow_mut().cleanup();
        self.header_bar.set_subtitle("");
        self.export_toc_btn.set_sensitive(false);

        let (ctx_tx, ui_rx) = channel();

        self.keep_going = true;
        self.register_listener(LISTENER_PERIOD, ui_rx);

        let dbl_buffer_mtx = Arc::clone(&self.audio_ctrl.borrow().get_dbl_buffer_mtx());
        match PlaybackContext::new(filepath, dbl_buffer_mtx, ctx_tx) {
            Ok(context) => {
                self.context = Some(context);
                self.export_toc_btn.set_sensitive(true);
            }
            Err(error) => {
                eprintln!("Error opening media: {}", error);
            }
        };

        self.window.set_sensitive(true);
    }
}
<commit_msg>Discard seeking during range playback (#84)<commit_after>extern crate glib;
extern crate gstreamer as gst;
extern crate gtk;

#[cfg(any(feature = "profiling-listener", feature = "profiling-tracker"))]
use chrono::Utc;

use std::rc::Rc;
use std::cell::RefCell;

use std::path::PathBuf;

use std::sync::Arc;
use std::sync::mpsc::{channel, Receiver};

use gtk::prelude::*;

use media::{PlaybackContext, ContextMessage};
use media::ContextMessage::*;

use super::{AudioController, ExportController, InfoController, VideoController};

#[derive(Clone, Debug, PartialEq)]
pub enum ControllerState {
    EOS,
    Ready,
    Paused,
    PendingExportToc,
    PendingSelectMedia,
    Playing,
    PlayingRange(u64),
    Stopped,
    Seeking(bool, bool), // (must_switch_to_play, must_keep_paused)
}

const LISTENER_PERIOD: u32 = 250; // 250 ms (4 Hz)
const TRACKER_PERIOD: u32 = 17; //  17 ms (60 Hz)

pub struct MainController {
    window: gtk::ApplicationWindow,
    header_bar: gtk::HeaderBar,
    play_pause_btn: gtk::ToolButton,
    export_toc_btn: gtk::Button,

    video_ctrl: VideoController,
    info_ctrl: Rc<RefCell<InfoController>>,
    audio_ctrl: Rc<RefCell<AudioController>>,
    export_ctrl: Rc<RefCell<ExportController>>,

    context: Option<PlaybackContext>,
    state: ControllerState,

    requires_async_dialog: bool, // when pipeline contains video, dialogs must wait for
    // asyncdone before opening a dialog otherwise the listener
    // may borrow the MainController while the dialog is already
    // using it leading to a borrowing conflict
    this_opt: Option<Rc<RefCell<MainController>>>,
    keep_going: bool,
    listener_src: Option<glib::SourceId>,
    tracker_src: Option<glib::SourceId>,
}

impl MainController {
    pub fn new(builder: &gtk::Builder) -> Rc<RefCell<Self>> {
        let export_toc_btn: gtk::Button = builder.get_object("export_toc-btn").unwrap();
        export_toc_btn.set_sensitive(false);

        let this = Rc::new(RefCell::new(MainController {
            window: builder.get_object("application-window").unwrap(),
            header_bar: builder.get_object("header-bar").unwrap(),
            play_pause_btn: builder.get_object("play_pause-toolbutton").unwrap(),
            export_toc_btn: export_toc_btn,

            video_ctrl: VideoController::new(builder),
            info_ctrl: InfoController::new(builder),
            audio_ctrl: AudioController::new(builder),
            export_ctrl: ExportController::new(builder),

            context: None,
            state: ControllerState::Stopped,

            requires_async_dialog: false,

            this_opt: None,
            keep_going: true,
            listener_src: None,
            tracker_src: None,
        }));

        {
            let mut this_mut = this.borrow_mut();

            let this_rc = Rc::clone(&this);
            this_mut.this_opt = Some(this_rc);

            this_mut.window.connect_delete_event(|_, _| {
                gtk::main_quit();
                Inhibit(false)
            });
            this_mut.window.set_titlebar(&this_mut.header_bar);

            let this_rc = Rc::clone(&this);
            this_mut.play_pause_btn.connect_clicked(move |_| {
                this_rc.borrow_mut().play_pause();
            });

            // TODO: add key bindings to seek by steps
            // play/pause, etc.

            let this_rc = Rc::clone(&this);
            this_mut.export_toc_btn.connect_clicked(move |_| {
                let mut this = this_rc.borrow_mut();

                if this.requires_async_dialog && this.state == ControllerState::Playing {
                    this.hold();
                    this.state = ControllerState::PendingExportToc;
                } else {
                    this.hold();
                    this.export_toc();
                }
            });

            this_mut.video_ctrl.register_callbacks(&this);
            InfoController::register_callbacks(&this_mut.info_ctrl, &this);
            AudioController::register_callbacks(&this_mut.audio_ctrl, &this);
            ExportController::register_callbacks(&this_mut.export_ctrl, &this);
        }

        let open_btn: gtk::Button = builder.get_object("open-btn").unwrap();
        let this_rc = Rc::clone(&this);
        open_btn.connect_clicked(move |_| {
            let mut this = this_rc.borrow_mut();

            if this.requires_async_dialog && this.state == ControllerState::Playing {
                this.hold();
                this.state = ControllerState::PendingSelectMedia;
            } else {
                this.hold();
                this.select_media();
            }
        });

        this
    }

    pub fn show_all(&self) {
        self.window.show_all();
    }

    pub fn get_state(&self) -> &ControllerState {
        &self.state
    }

    fn play_pause(&mut self) {
        let context = match self.context.take() {
            Some(context) => context,
            None => {
                self.select_media();
                return;
            }
        };

        if self.state != ControllerState::EOS {
            match context.get_state() {
                gst::State::Paused => {
                    self.register_tracker();
                    self.play_pause_btn.set_icon_name("media-playback-pause");
                    self.state = ControllerState::Playing;
                    context.play().unwrap();
                    self.context = Some(context);
                }
                gst::State::Playing => {
                    context.pause().unwrap();
                    self.play_pause_btn.set_icon_name("media-playback-start");
                    self.remove_tracker();
                    self.state = ControllerState::Paused;
                    self.context = Some(context);
                }
                _ => {
                    self.context = Some(context);
                    self.select_media();
                }
            };
        } else {
            // Restart the stream from the begining
            self.context = Some(context);
            self.seek(0, true); // accurate (slow)
        }
    }

    pub fn seek(&mut self, position: u64, accurate: bool) {
        match self.state {
            ControllerState::Stopped | ControllerState::PlayingRange(_) => (),
            _ => {
                if self.state == ControllerState::Playing || self.state == ControllerState::Paused {
                    self.info_ctrl.borrow_mut().seek(position, &self.state);
                    self.audio_ctrl.borrow_mut().seek(position, &self.state);
                }

                let (must_switch_to_play, must_keep_paused) = match self.state {
                    ControllerState::EOS | ControllerState::Ready => (true, false),
                    ControllerState::Paused => (false, true),
                    _ => (false, false),
                };
                self.state = ControllerState::Seeking(must_switch_to_play, must_keep_paused);

                self.context
                    .as_ref()
                    .expect("MainController::seek no context")
                    .seek(position, accurate);
            }
        }
    }

    pub fn play_range(&mut self, start: u64, end: u64, pos_to_restore: u64) {
        if self.state == ControllerState::Paused {
            self.info_ctrl.borrow_mut().start_play_range();
            self.audio_ctrl.borrow_mut().start_play_range();

            self.state = ControllerState::PlayingRange(pos_to_restore);

            self.context
                .as_ref()
                .expect("MainController::play_range no context")
                .seek_range(start, end);

            self.register_tracker();
        }
    }

    pub fn get_position(&mut self) -> u64 {
        self.context
            .as_mut()
            .expect("MainController::get_position no context")
            .get_position()
    }

    fn hold(&mut self) {
        self.remove_tracker();
        if let Some(context) = self.context.as_mut() {
            context.pause().unwrap();
        };

        self.play_pause_btn.set_icon_name("media-playback-start");
        self.window.set_sensitive(false);
    }

    fn export_toc(&mut self) {
        if let Some(mut context) = self.context.take() {
            self.info_ctrl.borrow().export_chapters(&mut context);
            self.export_ctrl.borrow_mut().open(context);
            self.state = ControllerState::Paused;
        }
    }

    pub fn restore_context(&mut self, context: PlaybackContext) {
        self.context = Some(context);
        self.state = ControllerState::Paused;
        self.window.set_sensitive(true);
    }

    fn remove_listener(&mut self) {
        if let Some(source_id) = self.listener_src.take() {
            glib::source_remove(source_id);
        }
    }

    fn register_listener(&mut self, timeout: u32, ui_rx: Receiver<ContextMessage>) {
        if self.listener_src.is_some() {
            return;
        }

        let this_rc = Rc::clone(self.this_opt.as_ref().unwrap());

        self.listener_src = Some(gtk::timeout_add(timeout, move || {
            let mut keep_going = true;

            for message in ui_rx.try_iter() {
                match message {
                    AsyncDone => {
                        let mut this = this_rc.borrow_mut();
                        match this.state {
                            ControllerState::PendingSelectMedia => this.select_media(),
                            ControllerState::PendingExportToc => this.export_toc(),
                            ControllerState::Seeking(must_switch_to_play, must_keep_paused) => {
                                if must_switch_to_play {
                                    this.context
                                        .as_ref()
                                        .expect("MainController::listener(AsyncDone) no context")
                                        .play()
                                        .unwrap();
                                    this.register_tracker();
                                    this.play_pause_btn.set_icon_name("media-playback-pause");
                                    this.state = ControllerState::Playing;
                                } else if must_keep_paused {
                                    this.state = ControllerState::Paused;
                                } else {
                                    this.state = ControllerState::Playing;
                                }
                            }
                            _ => (),
                        }
                    }
                    InitDone => {
                        let mut this = this_rc.borrow_mut();

                        let context = this.context.take().expect(
                            "MainController: InitDone but no context available",
                        );

                        this.requires_async_dialog = context
                            .info
                            .lock()
                            .expect("MainController::listener(InitDone) failed to lock media info")
                            .video_best
                            .is_some();

                        this.header_bar.set_subtitle(
                            Some(context.file_name.as_str()),
                        );

                        this.video_ctrl.new_media(&context);
                        this.info_ctrl.borrow_mut().new_media(&context);
                        this.audio_ctrl.borrow_mut().new_media(&context);

                        this.context = Some(context);

                        this.state = ControllerState::Ready;
                    }
                    Eos => {
                        let mut this = this_rc.borrow_mut();
                        match this.state {
                            ControllerState::PlayingRange(pos_to_restore) => {
                                // end of range => pause and seek back to pos_to_restore
                                this.context
                                    .as_ref()
                                    .expect("MainController::listener(eos) no context")
                                    .pause()
                                    .unwrap();
                                this.state = ControllerState::Paused;
                                this.remove_tracker();
                                this.seek(pos_to_restore, true); // accurate
                            }
                            ControllerState::Playing => { // regular EOS
                                let position = this.get_position();
                                this.info_ctrl.borrow_mut().tick(position, true);

                                this.audio_ctrl.borrow_mut().tick();

                                #[cfg(feature = "trace-main-controller")]
                                println!("MainController::listener(eos)");

                                this.play_pause_btn.set_icon_name("media-playback-start");
                                this.state = ControllerState::EOS;

                                // The tracker will be register again in case of a seek
                                this.remove_tracker();
                            }
                            _ => { // inconsistent state
                                println!("MainController::listener(eos) inconsistent state {:?}",
                                    this.state);
                                this.state = ControllerState::Paused;
                            }
                        }
                    }
                    FailedToOpenMedia => {
                        eprintln!("ERROR: failed to open media");

                        let mut this = this_rc.borrow_mut();

                        this.context = None;
                        this.keep_going = false;
                        keep_going = false;
                    }
                    _ => (),
                };

                if !keep_going {
                    break;
                }
            }

            if !keep_going {
                let mut this = this_rc.borrow_mut();
                this.listener_src = None;
                this.tracker_src = None;
            }

            glib::Continue(keep_going)
        }));
    }

    fn remove_tracker(&mut self) {
        if let Some(source_id) = self.tracker_src.take() {
            glib::source_remove(source_id);
        }
    }

    fn register_tracker(&mut self) {
        if self.tracker_src.is_some() {
            return;
        }

        let this_rc = Rc::clone(self.this_opt.as_ref().unwrap());

        self.tracker_src = Some(gtk::timeout_add(TRACKER_PERIOD, move || {
            #[cfg(feature = "profiling-tracker")]
            let start = Utc::now();

            let mut this = this_rc.borrow_mut();

            #[cfg(feature = "profiling-tracker")]
            let before_tick = Utc::now();

            {
                let position = this.context
                    .as_mut()
                    .expect("MainController::tracker no context while getting position")
                    .get_position();
                this.info_ctrl.borrow_mut().tick(position, false);
                this.audio_ctrl.borrow_mut().tick();
            }

            #[cfg(feature = "profiling-tracker")]
            let end = Utc::now();

            #[cfg(feature = "profiling-tracker")]
            println!(
                "tracker,{},{},{}",
                start.time().format("%H:%M:%S%.6f"),
                before_tick.time().format("%H:%M:%S%.6f"),
                end.time().format("%H:%M:%S%.6f"),
            );

            glib::Continue(this.keep_going)
        }));
    }

    fn select_media(&mut self) {
        let file_dlg = gtk::FileChooserDialog::new(
            Some("Open a media file"),
            Some(&self.window),
            gtk::FileChooserAction::Open,
        );
        // Note: couldn't find equivalents for STOCK_OK
        file_dlg.add_button("Open", gtk::ResponseType::Ok.into());

        if file_dlg.run() == gtk::ResponseType::Ok.into() {
            if let Some(ref context) = self.context {
                context.stop();
            }
            self.open_media(file_dlg.get_filename().unwrap());
        } else {
            self.state = ControllerState::Paused;
            self.window.set_sensitive(true);
        }

        file_dlg.close();
    }

    fn open_media(&mut self, filepath: PathBuf) {
        self.remove_listener();

        self.info_ctrl.borrow_mut().cleanup();
        self.audio_ctrl.borrow_mut().cleanup();
        self.header_bar.set_subtitle("");
        self.export_toc_btn.set_sensitive(false);

        let (ctx_tx, ui_rx) = channel();

        self.keep_going = true;
        self.register_listener(LISTENER_PERIOD, ui_rx);

        let dbl_buffer_mtx = Arc::clone(&self.audio_ctrl.borrow().get_dbl_buffer_mtx());
        match PlaybackContext::new(filepath, dbl_buffer_mtx, ctx_tx) {
            Ok(context) => {
                self.context = Some(context);
                self.export_toc_btn.set_sensitive(true);
            }
            Err(error) => {
                eprintln!("Error opening media: {}", error);
            }
        };

        self.window.set_sensitive(true);
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! A handler and associated types for the startup action.
//!
//! The startup action collects basic information about the system (e.g. its
//! boot time) and metadata about the agent (e.g. it's name and version). It is
//! special in a sense that generally it should be not invoked by flows, but be
//! called explicitly during agent startup.

use std::time::{Duration, SystemTime, UNIX_EPOCH};

use log::error;

/// A response type for the startup action.
pub struct Response {
    /// Time of last system boot.
    boot_time: SystemTime,
    /// Name of the GRR agent.
    name: String,
    /// Description of the GRR agent.
    description: String,
    /// Version of the GRR agent.
    version: Version,
}

impl super::Response for Response {

    const RDF_NAME: Option<&'static str> = Some("StartupInfo");

    type Proto = rrg_proto::StartupInfo;

    fn into_proto(self) -> rrg_proto::StartupInfo {
        let boot_time_micros = match rrg_proto::micros(self.boot_time) {
            Ok(boot_time_micros) => boot_time_micros,
            Err(error) => {
                error!("failed to convert boot time: {}", error);
                0
            }
        };

        rrg_proto::StartupInfo {
            client_info: Some(rrg_proto::ClientInformation {
                client_name: Some(self.name),
                client_version: Some(self.version.as_numeric()),
                client_description: Some(self.description),
                ..Default::default()
            }),
            boot_time: Some(boot_time_micros),
        }
    }
}

/// Handles requests for the startup action.
pub fn handle(_: ()) -> Result<Response, super::Error> {
    Ok(Response {
        boot_time: boot_time()?,
        name: String::from(env!("CARGO_PKG_NAME")),
        description: String::from(env!("CARGO_PKG_DESCRIPTION")),
        version: Version::from_crate(),
    })
}

/// Returns information about the system boot time.
fn boot_time() -> Result<SystemTime, sys_info::Error> {
    let timeval = sys_info::boottime()?;

    let secs = timeval.tv_sec as u64;
    let micros = timeval.tv_usec as u64;

    Ok(UNIX_EPOCH + Duration::from_secs(secs) + Duration::from_micros(micros))
}

/// A type for representing version metadata.
struct Version {
    pub major: u8,
    pub minor: u8,
    pub patch: u8,
    pub revision: u8,
}

impl Version {

    /// Constructs version metadata from Crate information.
    ///
    /// This function assumes that are relevant crate information is correctly
    /// specified in the `Cargo.toml` file.
    fn from_crate() -> Version {
        Version {
            major: env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap_or(0),
            minor: env!("CARGO_PKG_VERSION_MINOR").parse().unwrap_or(0),
            patch: env!("CARGO_PKG_VERSION_PATCH").parse().unwrap_or(0),
            revision: env!("CARGO_PKG_VERSION_PRE").parse().unwrap_or(0),
        }
    }

    /// Returns a numeric representation of version metadata.
    ///
    /// This function assumes that all version components are smaller than 10.
    /// In other cases, the output is undefined (but the function call itself
    /// does not panic).
    ///
    /// # Examples
    ///
    /// ```
    /// use rrg::action::startup::Version;
    ///
    /// let version = Version {
    ///     major: 1,
    ///     minor: 2,
    ///     patch: 3,
    ///     revision: 4,
    /// };
    ///
    /// assert_eq!(version.as_numeric(), 1234)
    /// ```
    fn as_numeric(&self) -> u32 {
        let mut result = 0;
        result = 10 * result + self.major as u32;
        result = 10 * result + self.minor as u32;
        result = 10 * result + self.patch as u32;
        result = 10 * result + self.revision as u32;
        result
    }
}
<commit_msg>Push startup response conversion to the bottom.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! A handler and associated types for the startup action.
//!
//! The startup action collects basic information about the system (e.g. its
//! boot time) and metadata about the agent (e.g. it's name and version). It is
//! special in a sense that generally it should be not invoked by flows, but be
//! called explicitly during agent startup.

use std::time::{Duration, SystemTime, UNIX_EPOCH};

use log::error;

/// A response type for the startup action.
pub struct Response {
    /// Time of last system boot.
    boot_time: SystemTime,
    /// Name of the GRR agent.
    name: String,
    /// Description of the GRR agent.
    description: String,
    /// Version of the GRR agent.
    version: Version,
}

/// Handles requests for the startup action.
pub fn handle(_: ()) -> Result<Response, super::Error> {
    Ok(Response {
        boot_time: boot_time()?,
        name: String::from(env!("CARGO_PKG_NAME")),
        description: String::from(env!("CARGO_PKG_DESCRIPTION")),
        version: Version::from_crate(),
    })
}

/// Returns information about the system boot time.
fn boot_time() -> Result<SystemTime, sys_info::Error> {
    let timeval = sys_info::boottime()?;

    let secs = timeval.tv_sec as u64;
    let micros = timeval.tv_usec as u64;

    Ok(UNIX_EPOCH + Duration::from_secs(secs) + Duration::from_micros(micros))
}

/// A type for representing version metadata.
struct Version {
    pub major: u8,
    pub minor: u8,
    pub patch: u8,
    pub revision: u8,
}

impl Version {

    /// Constructs version metadata from Crate information.
    ///
    /// This function assumes that are relevant crate information is correctly
    /// specified in the `Cargo.toml` file.
    fn from_crate() -> Version {
        Version {
            major: env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap_or(0),
            minor: env!("CARGO_PKG_VERSION_MINOR").parse().unwrap_or(0),
            patch: env!("CARGO_PKG_VERSION_PATCH").parse().unwrap_or(0),
            revision: env!("CARGO_PKG_VERSION_PRE").parse().unwrap_or(0),
        }
    }

    /// Returns a numeric representation of version metadata.
    ///
    /// This function assumes that all version components are smaller than 10.
    /// In other cases, the output is undefined (but the function call itself
    /// does not panic).
    ///
    /// # Examples
    ///
    /// ```
    /// use rrg::action::startup::Version;
    ///
    /// let version = Version {
    ///     major: 1,
    ///     minor: 2,
    ///     patch: 3,
    ///     revision: 4,
    /// };
    ///
    /// assert_eq!(version.as_numeric(), 1234)
    /// ```
    fn as_numeric(&self) -> u32 {
        let mut result = 0;
        result = 10 * result + self.major as u32;
        result = 10 * result + self.minor as u32;
        result = 10 * result + self.patch as u32;
        result = 10 * result + self.revision as u32;
        result
    }
}

impl super::Response for Response {

    const RDF_NAME: Option<&'static str> = Some("StartupInfo");

    type Proto = rrg_proto::StartupInfo;

    fn into_proto(self) -> rrg_proto::StartupInfo {
        let boot_time_micros = match rrg_proto::micros(self.boot_time) {
            Ok(boot_time_micros) => boot_time_micros,
            Err(error) => {
                error!("failed to convert boot time: {}", error);
                0
            }
        };

        rrg_proto::StartupInfo {
            client_info: Some(rrg_proto::ClientInformation {
                client_name: Some(self.name),
                client_version: Some(self.version.as_numeric()),
                client_description: Some(self.description),
                ..Default::default()
            }),
            boot_time: Some(boot_time_micros),
        }
    }
}
<|endoftext|>


<commit_before>use na::storage::Storage;
use na::{self, Point3, Real, U6, Vector, Vector3, Vector6};
use std::mem;
use std::ops::{Add, AddAssign, Mul, Neg, Sub, SubAssign};

/// A force with a linear and agular (torque) component.
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct Force3<N: Real> {
    /// The linear force.
    pub linear: Vector3<N>,
    /// The linear force.
    pub angular: Vector3<N>,
}

impl<N: Real> Force3<N> {
    /// Creates a force from its linear and angular components.
    #[inline]
    pub fn new(linear: Vector3<N>, angular: Vector3<N>) -> Self {
        Force3 { linear, angular }
    }

    /// A zero force.
    #[inline]
    pub fn zero() -> Self {
        Self::new(na::zero(), na::zero())
    }

    /// Create a force from a slice where the linear part are stored first.
    #[inline]
    pub fn from_slice(data: &[N]) -> Self {
        Self::new(
            Vector3::new(data[0], data[1], data[2]),
            Vector3::new(data[3], data[4], data[5]),
        )
    }

    /// Create a force from a vector where the linear part are stored first.
    #[inline]
    pub fn from_vector<S: Storage<N, U6>>(data: &Vector<N, U6, S>) -> Self {
        Self::new(
            Vector3::new(data[0], data[1], data[2]),
            Vector3::new(data[3], data[4], data[5]),
        )
    }

    /// Create a pure torque.
    #[inline]
    pub fn torque(torque: Vector3<N>) -> Self {
        Self::new(na::zero(), torque)
    }

    /// Create a pure torque.
    #[inline]
    pub fn torque_from_vector(torque: Vector3<N>) -> Self {
        Self::new(na::zero(), torque)
    }

    /// Creates the resultant of a torque applied at the given point (relative to the center of mass).
    #[inline]
    pub fn torque_at_point(torque: Vector3<N>, point: &Point3<N>) -> Self {
        Self::new(-torque.cross(&point.coords), torque)
    }

    /// Creates the resultant of a torque applied at the given point (relative to the center of mass).
    #[inline]
    pub fn torque_from_vector_at_point(torque: Vector3<N>, point: &Point3<N>) -> Self {
        Self::torque_at_point(torque, point)
    }

    /// Create a pure linear force.
    #[inline]
    pub fn linear(linear: Vector3<N>) -> Self {
        Self::new(linear, na::zero())
    }

    /// Creates the resultant of a linear force applied at the given point (relative to the center of mass).
    #[inline]
    pub fn linear_at_point(linear: Vector3<N>, point: &Point3<N>) -> Self {
        Self::new(linear, point.coords.cross(&linear))
    }

    /// The angular part of the force.
    #[inline]
    pub fn angular_vector(&self) -> Vector3<N> {
        self.angular
    }

    /// This force seen as a slice.
    ///
    /// The two first entries contain the linear part and the third entry contais the angular part.
    #[inline]
    pub fn as_slice(&self) -> &[N] {
        self.as_vector().as_slice()
    }

    /// This force seen as a vector.
    ///
    /// The linear part of the force are stored first.
    #[inline]
    pub fn as_vector(&self) -> &Vector6<N> {
        unsafe { mem::transmute(self) }
    }

    /// This force seen as a mutable vector.
    ///
    /// The linear part of the force are stored first.
    #[inline]
    pub fn as_vector_mut(&mut self) -> &mut Vector6<N> {
        unsafe { mem::transmute(self) }
    }
}

impl<N: Real> Add<Force3<N>> for Force3<N> {
    type Output = Self;

    #[inline]
    fn add(self, rhs: Self) -> Self {
        Force3::new(self.linear + rhs.linear, self.angular + rhs.angular)
    }
}

impl<N: Real> AddAssign<Force3<N>> for Force3<N> {
    #[inline]
    fn add_assign(&mut self, rhs: Self) {
        self.linear += rhs.linear;
        self.angular += rhs.angular;
    }
}

impl<N: Real> Sub<Force3<N>> for Force3<N> {
    type Output = Self;

    #[inline]
    fn sub(self, rhs: Self) -> Self {
        Force3::new(self.linear - rhs.linear, self.angular - rhs.angular)
    }
}

impl<N: Real> SubAssign<Force3<N>> for Force3<N> {
    #[inline]
    fn sub_assign(&mut self, rhs: Self) {
        self.linear -= rhs.linear;
        self.angular -= rhs.angular;
    }
}

impl<N: Real> Mul<N> for Force3<N> {
    type Output = Self;

    #[inline]
    fn mul(self, rhs: N) -> Self {
        Force3::new(self.linear * rhs, self.angular * rhs)
    }
}

impl<N: Real> Neg for Force3<N> {
    type Output = Self;

    #[inline]
    fn neg(self) -> Self {
        Force3::new(-self.linear, -self.angular)
    }
}
<commit_msg>Fix typo<commit_after>use na::storage::Storage;
use na::{self, Point3, Real, U6, Vector, Vector3, Vector6};
use std::mem;
use std::ops::{Add, AddAssign, Mul, Neg, Sub, SubAssign};

/// A force with a linear and angular (torque) component.
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct Force3<N: Real> {
    /// The linear force.
    pub linear: Vector3<N>,
    /// The linear force.
    pub angular: Vector3<N>,
}

impl<N: Real> Force3<N> {
    /// Creates a force from its linear and angular components.
    #[inline]
    pub fn new(linear: Vector3<N>, angular: Vector3<N>) -> Self {
        Force3 { linear, angular }
    }

    /// A zero force.
    #[inline]
    pub fn zero() -> Self {
        Self::new(na::zero(), na::zero())
    }

    /// Create a force from a slice where the linear part are stored first.
    #[inline]
    pub fn from_slice(data: &[N]) -> Self {
        Self::new(
            Vector3::new(data[0], data[1], data[2]),
            Vector3::new(data[3], data[4], data[5]),
        )
    }

    /// Create a force from a vector where the linear part are stored first.
    #[inline]
    pub fn from_vector<S: Storage<N, U6>>(data: &Vector<N, U6, S>) -> Self {
        Self::new(
            Vector3::new(data[0], data[1], data[2]),
            Vector3::new(data[3], data[4], data[5]),
        )
    }

    /// Create a pure torque.
    #[inline]
    pub fn torque(torque: Vector3<N>) -> Self {
        Self::new(na::zero(), torque)
    }

    /// Create a pure torque.
    #[inline]
    pub fn torque_from_vector(torque: Vector3<N>) -> Self {
        Self::new(na::zero(), torque)
    }

    /// Creates the resultant of a torque applied at the given point (relative to the center of mass).
    #[inline]
    pub fn torque_at_point(torque: Vector3<N>, point: &Point3<N>) -> Self {
        Self::new(-torque.cross(&point.coords), torque)
    }

    /// Creates the resultant of a torque applied at the given point (relative to the center of mass).
    #[inline]
    pub fn torque_from_vector_at_point(torque: Vector3<N>, point: &Point3<N>) -> Self {
        Self::torque_at_point(torque, point)
    }

    /// Create a pure linear force.
    #[inline]
    pub fn linear(linear: Vector3<N>) -> Self {
        Self::new(linear, na::zero())
    }

    /// Creates the resultant of a linear force applied at the given point (relative to the center of mass).
    #[inline]
    pub fn linear_at_point(linear: Vector3<N>, point: &Point3<N>) -> Self {
        Self::new(linear, point.coords.cross(&linear))
    }

    /// The angular part of the force.
    #[inline]
    pub fn angular_vector(&self) -> Vector3<N> {
        self.angular
    }

    /// This force seen as a slice.
    ///
    /// The two first entries contain the linear part and the third entry contais the angular part.
    #[inline]
    pub fn as_slice(&self) -> &[N] {
        self.as_vector().as_slice()
    }

    /// This force seen as a vector.
    ///
    /// The linear part of the force are stored first.
    #[inline]
    pub fn as_vector(&self) -> &Vector6<N> {
        unsafe { mem::transmute(self) }
    }

    /// This force seen as a mutable vector.
    ///
    /// The linear part of the force are stored first.
    #[inline]
    pub fn as_vector_mut(&mut self) -> &mut Vector6<N> {
        unsafe { mem::transmute(self) }
    }
}

impl<N: Real> Add<Force3<N>> for Force3<N> {
    type Output = Self;

    #[inline]
    fn add(self, rhs: Self) -> Self {
        Force3::new(self.linear + rhs.linear, self.angular + rhs.angular)
    }
}

impl<N: Real> AddAssign<Force3<N>> for Force3<N> {
    #[inline]
    fn add_assign(&mut self, rhs: Self) {
        self.linear += rhs.linear;
        self.angular += rhs.angular;
    }
}

impl<N: Real> Sub<Force3<N>> for Force3<N> {
    type Output = Self;

    #[inline]
    fn sub(self, rhs: Self) -> Self {
        Force3::new(self.linear - rhs.linear, self.angular - rhs.angular)
    }
}

impl<N: Real> SubAssign<Force3<N>> for Force3<N> {
    #[inline]
    fn sub_assign(&mut self, rhs: Self) {
        self.linear -= rhs.linear;
        self.angular -= rhs.angular;
    }
}

impl<N: Real> Mul<N> for Force3<N> {
    type Output = Self;

    #[inline]
    fn mul(self, rhs: N) -> Self {
        Force3::new(self.linear * rhs, self.angular * rhs)
    }
}

impl<N: Real> Neg for Force3<N> {
    type Output = Self;

    #[inline]
    fn neg(self) -> Self {
        Force3::new(-self.linear, -self.angular)
    }
}
<|endoftext|>