<commit_before>use std::sync::Arc;
use std::sync::mpsc::{self, Sender, Receiver};
use std::thread;

use bip_util::sha::ShaHash;
use crossbeam::sync::MsQueue;

use accessor::Accessor;
use builder::buffer::{PieceBuffers, PieceBuffer};
use error::ParseResult;

/// Messages sent to the master hasher.
pub enum MasterMessage {
    /// Accepts the piece hash with the given piece index.
    AcceptPiece(usize, ShaHash),
    /// One of our workers has finished and shut down.
    WorkerFinished,
}

/// Message sent to a worker hasher.
enum WorkerMessage {
    /// Start processing the given memory region.
    HashPiece(usize, PieceBuffer),
    /// Worker should exit it's thread.
    Finish,
}

/// Starts a number of hasher workers which will generate the hash pieces for the files we send to it.
pub fn start_hasher_workers<A, C>(accessor: A,
                                  piece_length: usize,
                                  num_pieces: u64,
                                  num_workers: usize,
                                  progress: C)
                                  -> ParseResult<Vec<(usize, ShaHash)>>
    where A: Accessor,
          C: FnMut(f64) + Send + 'static
{
    // Create channels to communicate with the master
    let (master_send, master_recv) = mpsc::channel();
    let (prog_send, prog_recv) = mpsc::channel();

    // Create queue to push work to and pull work from
    let work_queue = Arc::new(MsQueue::new());

    // Create buffer allocator to reuse pre allocated buffers
    let piece_buffers = Arc::new(PieceBuffers::new(piece_length, num_workers));

    // Create n worker threads that pull work from the queue
    for _ in 0..num_workers {
        let share_master_send = master_send.clone();
        let share_work_queue = work_queue.clone();
        let share_piece_buffers = piece_buffers.clone();

        thread::spawn(move || {
            start_hash_worker(share_master_send, share_work_queue, share_piece_buffers);
        });
    }

    // Create a worker thread to execute the user callback for the progress update
    thread::spawn(move || {
        start_progress_updater(prog_recv, num_pieces, progress);
    });

    // Create the master worker to coordinate between the workers
    start_hash_master(accessor,
                      num_workers,
                      master_recv,
                      work_queue,
                      piece_buffers,
                      prog_send)
}

// ----------------------------------------------------------------------------//

/// Start a master hasher which will take care of chunking sequential/overlapping pieces from the data given to it and giving
/// updates to the hasher workers.
fn start_hash_master<A>(accessor: A,
                        num_workers: usize,
                        recv: Receiver<MasterMessage>,
                        work: Arc<MsQueue<WorkerMessage>>,
                        buffers: Arc<PieceBuffers>,
                        progress_sender: Sender<usize>)
                        -> ParseResult<Vec<(usize, ShaHash)>>
    where A: Accessor
{
    let mut pieces = Vec::new();
    let mut piece_index = 0;

    // Our closure may be called multiple times, save partial pieces buffers between calls
    let mut opt_piece_buffer = None;
    try!(accessor.access_pieces(|piece_region| {
        let mut curr_piece_buffer = if let Some(piece_buffer) = opt_piece_buffer.take() {
            piece_buffer
        } else {
            buffers.checkout()
        };

        let mut end_of_region = false;
        while !end_of_region {
            end_of_region =
                try!(curr_piece_buffer.write_bytes(|buffer| piece_region.read(buffer))) == 0;

            if curr_piece_buffer.is_whole() {
                println!("ASD");
                work.push(WorkerMessage::HashPiece(piece_index, curr_piece_buffer));

                piece_index += 1;
                curr_piece_buffer = buffers.checkout();

                if progress_sender.send(piece_index).is_err() {
                    // TODO: Add logging here
                }
            }
        }

        opt_piece_buffer = Some(curr_piece_buffer);

        Ok(())
    }));

    // If we still have a partial piece left over, push it to the workers
    if let Some(piece_buffer) = opt_piece_buffer {
        if !piece_buffer.is_empty() {
            work.push(WorkerMessage::HashPiece(piece_index, piece_buffer));

            piece_index += 1;
            if progress_sender.send(piece_index).is_err() {
                // TODO: Add logging here
            }
        }
    }

    // No more entries, tell workers to shut down
    for _ in 0..num_workers {
        work.push(WorkerMessage::Finish);
    }

    // Wait for all of the workers to finish up the last pieces
    let mut workers_finished = 0;
    while workers_finished < num_workers {
        match recv.recv() {
            Ok(MasterMessage::AcceptPiece(index, piece)) => pieces.push((index, piece)),
            Ok(MasterMessage::WorkerFinished) => workers_finished += 1,
            Err(_) => panic!("bip_metainfo: Master failed to verify all workers shutdown..."),
        }
    }

    // Sort our list to make sure the pieces are in order before we send them off
    pieces.sort_by(|one, two| one.0.cmp(&two.0));

    Ok(pieces)
}

// ----------------------------------------------------------------------------//

fn start_progress_updater<C>(recv: Receiver<usize>, num_pieces: u64, mut progress: C)
    where C: FnMut(f64)
{
    for finished_piece in recv {
        let percent_complete = (finished_piece as f64) / (num_pieces as f64);

        progress(percent_complete);
    }
}

// ----------------------------------------------------------------------------//

/// Starts a hasher worker which will hash all of the buffers it receives.
fn start_hash_worker(send: Sender<MasterMessage>,
                     work: Arc<MsQueue<WorkerMessage>>,
                     buffers: Arc<PieceBuffers>) {
    let mut work_to_do = true;

    // Loop until we are instructed to stop working
    while work_to_do {
        let work_item = work.pop();

        match work_item {
            WorkerMessage::Finish => {
                work_to_do = false;
            }
            WorkerMessage::HashPiece(index, buffer) => {
                let hash = ShaHash::from_bytes(buffer.as_slice());

                send.send(MasterMessage::AcceptPiece(index, hash)).unwrap();
                buffers.checkin(buffer);
            }
        }
    }

    // Let the master know we have exited our thread
    send.send(MasterMessage::WorkerFinished).unwrap();
}

#[cfg(test)]
mod tests {
    use std::ops::{Range, Index};
    use std::io::{self, Cursor, Read};
    use std::path::Path;
    use std::sync::mpsc;

    use bip_util::sha::ShaHash;
    use rand::{self, Rng};

    use accessor::Accessor;
    use builder::worker;

    // Keep these numbers fairly small to avoid lengthy tests
    const DEFAULT_PIECE_LENGTH: usize = 1024;
    const DEFAULT_NUM_PIECES: usize = 300;

    // Mock object for providing direct access to bytes via DataEntry.
    #[derive(Clone)]
    struct MockAccessor {
        buffer_ranges: Vec<Range<usize>>,
        contiguous_buffer: Vec<u8>,
    }

    impl MockAccessor {
        fn new() -> MockAccessor {
            MockAccessor {
                buffer_ranges: Vec::new(),
                contiguous_buffer: Vec::new(),
            }
        }

        fn create_region(&mut self, num_bytes: usize) {
            let mut buffer = vec![0u8; num_bytes];
            let mut rng = rand::thread_rng();

            rng.fill_bytes(&mut buffer);

            let (begin, end) = (self.contiguous_buffer.len(),
                                self.contiguous_buffer.len() + buffer.len());

            self.contiguous_buffer.extend_from_slice(&buffer);
            self.buffer_ranges.push(begin..end);
        }

        fn as_slice(&self) -> &[u8] {
            &self.contiguous_buffer
        }
    }

    impl Accessor for MockAccessor {
        /// Access the directory that all files should be relative to.
        fn access_directory(&self) -> Option<&Path> {
            panic!("Accessor::access_directory should not be called with MockAccessor...");
        }

        /// Access the metadata for all files including their length and path.
        fn access_metadata<C>(&self, _: C) -> io::Result<()>
            where C: FnMut(u64, &Path)
        {
            panic!("Accessor::access_metadata should not be called with MockAccessor...");
        }

        /// Access the sequential pieces that make up all of the files.
        fn access_pieces<C>(&self, mut callback: C) -> io::Result<()>
            where C: FnMut(&mut Read) -> io::Result<()>
        {
            for range in self.buffer_ranges.iter() {
                let mut next_region = Cursor::new(self.contiguous_buffer.index(range.clone()));

                try!(callback(&mut next_region));
            }

            Ok(())
        }
    }

    fn validate_entries_pieces(accessor: MockAccessor, piece_length: usize, num_threads: usize) {
        let (prog_send, prog_recv) = mpsc::channel();

        let total_num_pieces = ((accessor.as_slice().len() as f64) / (piece_length as f64))
            .ceil() as u64;
        let received_pieces = worker::start_hasher_workers(&accessor,
                                                           piece_length,
                                                           total_num_pieces,
                                                           num_threads,
                                                           move |update| {
                                                               prog_send.send(update).unwrap();
                                                           })
            .unwrap();

        let computed_pieces = accessor.as_slice()
            .chunks(piece_length)
            .enumerate()
            .map(|(index, chunk)| (index, ShaHash::from_bytes(chunk)))
            .collect::<Vec<(usize, ShaHash)>>();

        let updates_received = prog_recv.iter().count() as u64;

        assert_eq!(total_num_pieces, updates_received);
        assert_eq!(received_pieces, computed_pieces);
    }

    #[test]
    fn positive_piece_length_divisible_region_single_thread() {
        let mut accessor = MockAccessor::new();

        let region_length = DEFAULT_PIECE_LENGTH * DEFAULT_NUM_PIECES;
        accessor.create_region(region_length);

        validate_entries_pieces(accessor, DEFAULT_PIECE_LENGTH, 1);
    }

    #[test]
    fn positive_piece_length_divisible_region_multiple_threads() {
        let mut accessor = MockAccessor::new();

        let region_length = DEFAULT_PIECE_LENGTH * DEFAULT_NUM_PIECES;
        accessor.create_region(region_length);

        validate_entries_pieces(accessor, DEFAULT_PIECE_LENGTH, 4);
    }

    #[test]
    fn positive_piece_length_undivisible_region_single_thread() {
        let mut accessor = MockAccessor::new();

        let region_length = DEFAULT_PIECE_LENGTH * DEFAULT_NUM_PIECES + 1;
        accessor.create_region(region_length);

        validate_entries_pieces(accessor, DEFAULT_PIECE_LENGTH, 1);
    }

    #[test]
    fn positive_piece_length_undivisible_region_multiple_threads() {
        let mut accessor = MockAccessor::new();

        let region_length = DEFAULT_PIECE_LENGTH * DEFAULT_NUM_PIECES + 1;
        accessor.create_region(region_length);

        validate_entries_pieces(accessor, DEFAULT_PIECE_LENGTH, 4);
    }

    #[test]
    fn positive_piece_length_divisible_regions_single_thread() {
        let mut accessor = MockAccessor::new();

        let region_lengths = [DEFAULT_PIECE_LENGTH * DEFAULT_NUM_PIECES,
                              DEFAULT_PIECE_LENGTH * 1,
                              DEFAULT_PIECE_LENGTH * 50];
        for &region_length in region_lengths.into_iter() {
            accessor.create_region(region_length);
        }

        validate_entries_pieces(accessor, DEFAULT_PIECE_LENGTH, 1);
    }

    #[test]
    fn positive_piece_length_divisible_regions_multiple_threads() {
        let mut accessor = MockAccessor::new();

        let region_lengths = [DEFAULT_PIECE_LENGTH * DEFAULT_NUM_PIECES,
                              DEFAULT_PIECE_LENGTH * 1,
                              DEFAULT_PIECE_LENGTH * 50];
        for &region_length in region_lengths.into_iter() {
            accessor.create_region(region_length);
        }

        validate_entries_pieces(accessor, DEFAULT_PIECE_LENGTH, 4);
    }

    #[test]
    fn positive_piece_length_undivisible_regions_single_thread() {
        let mut accessor = MockAccessor::new();

        let region_lengths = [DEFAULT_PIECE_LENGTH / 2 * DEFAULT_NUM_PIECES,
                              DEFAULT_PIECE_LENGTH / 4 * DEFAULT_NUM_PIECES,
                              DEFAULT_PIECE_LENGTH * 1,
                              (DEFAULT_PIECE_LENGTH * 2 - 1) * 2];
        for &region_length in region_lengths.into_iter() {
            accessor.create_region(region_length);
        }

        validate_entries_pieces(accessor, DEFAULT_PIECE_LENGTH, 1);
    }

    #[test]
    fn positive_piece_length_undivisible_regions_multiple_threads() {
        let mut accessor = MockAccessor::new();

        let region_lengths = [DEFAULT_PIECE_LENGTH / 2 * DEFAULT_NUM_PIECES,
                              DEFAULT_PIECE_LENGTH / 4 * DEFAULT_NUM_PIECES,
                              DEFAULT_PIECE_LENGTH * 1,
                              (DEFAULT_PIECE_LENGTH * 2 - 1) * 2];
        for &region_length in region_lengths.into_iter() {
            accessor.create_region(region_length);
        }

        validate_entries_pieces(accessor, DEFAULT_PIECE_LENGTH, 4);
    }
}
<commit_msg>Remove println Statement From Hash Worker bip_metainfo<commit_after>use std::sync::Arc;
use std::sync::mpsc::{self, Sender, Receiver};
use std::thread;

use bip_util::sha::ShaHash;
use crossbeam::sync::MsQueue;

use accessor::Accessor;
use builder::buffer::{PieceBuffers, PieceBuffer};
use error::ParseResult;

/// Messages sent to the master hasher.
pub enum MasterMessage {
    /// Accepts the piece hash with the given piece index.
    AcceptPiece(usize, ShaHash),
    /// One of our workers has finished and shut down.
    WorkerFinished,
}

/// Message sent to a worker hasher.
enum WorkerMessage {
    /// Start processing the given memory region.
    HashPiece(usize, PieceBuffer),
    /// Worker should exit it's thread.
    Finish,
}

/// Starts a number of hasher workers which will generate the hash pieces for the files we send to it.
pub fn start_hasher_workers<A, C>(accessor: A,
                                  piece_length: usize,
                                  num_pieces: u64,
                                  num_workers: usize,
                                  progress: C)
                                  -> ParseResult<Vec<(usize, ShaHash)>>
    where A: Accessor,
          C: FnMut(f64) + Send + 'static
{
    // Create channels to communicate with the master
    let (master_send, master_recv) = mpsc::channel();
    let (prog_send, prog_recv) = mpsc::channel();

    // Create queue to push work to and pull work from
    let work_queue = Arc::new(MsQueue::new());

    // Create buffer allocator to reuse pre allocated buffers
    let piece_buffers = Arc::new(PieceBuffers::new(piece_length, num_workers));

    // Create n worker threads that pull work from the queue
    for _ in 0..num_workers {
        let share_master_send = master_send.clone();
        let share_work_queue = work_queue.clone();
        let share_piece_buffers = piece_buffers.clone();

        thread::spawn(move || {
            start_hash_worker(share_master_send, share_work_queue, share_piece_buffers);
        });
    }

    // Create a worker thread to execute the user callback for the progress update
    thread::spawn(move || {
        start_progress_updater(prog_recv, num_pieces, progress);
    });

    // Create the master worker to coordinate between the workers
    start_hash_master(accessor,
                      num_workers,
                      master_recv,
                      work_queue,
                      piece_buffers,
                      prog_send)
}

// ----------------------------------------------------------------------------//

/// Start a master hasher which will take care of chunking sequential/overlapping pieces from the data given to it and giving
/// updates to the hasher workers.
fn start_hash_master<A>(accessor: A,
                        num_workers: usize,
                        recv: Receiver<MasterMessage>,
                        work: Arc<MsQueue<WorkerMessage>>,
                        buffers: Arc<PieceBuffers>,
                        progress_sender: Sender<usize>)
                        -> ParseResult<Vec<(usize, ShaHash)>>
    where A: Accessor
{
    let mut pieces = Vec::new();
    let mut piece_index = 0;

    // Our closure may be called multiple times, save partial pieces buffers between calls
    let mut opt_piece_buffer = None;
    try!(accessor.access_pieces(|piece_region| {
        let mut curr_piece_buffer = if let Some(piece_buffer) = opt_piece_buffer.take() {
            piece_buffer
        } else {
            buffers.checkout()
        };

        let mut end_of_region = false;
        while !end_of_region {
            end_of_region =
                try!(curr_piece_buffer.write_bytes(|buffer| piece_region.read(buffer))) == 0;

            if curr_piece_buffer.is_whole() {
                work.push(WorkerMessage::HashPiece(piece_index, curr_piece_buffer));

                piece_index += 1;
                curr_piece_buffer = buffers.checkout();

                if progress_sender.send(piece_index).is_err() {
                    // TODO: Add logging here
                }
            }
        }

        opt_piece_buffer = Some(curr_piece_buffer);

        Ok(())
    }));

    // If we still have a partial piece left over, push it to the workers
    if let Some(piece_buffer) = opt_piece_buffer {
        if !piece_buffer.is_empty() {
            work.push(WorkerMessage::HashPiece(piece_index, piece_buffer));

            piece_index += 1;
            if progress_sender.send(piece_index).is_err() {
                // TODO: Add logging here
            }
        }
    }

    // No more entries, tell workers to shut down
    for _ in 0..num_workers {
        work.push(WorkerMessage::Finish);
    }

    // Wait for all of the workers to finish up the last pieces
    let mut workers_finished = 0;
    while workers_finished < num_workers {
        match recv.recv() {
            Ok(MasterMessage::AcceptPiece(index, piece)) => pieces.push((index, piece)),
            Ok(MasterMessage::WorkerFinished) => workers_finished += 1,
            Err(_) => panic!("bip_metainfo: Master failed to verify all workers shutdown..."),
        }
    }

    // Sort our list to make sure the pieces are in order before we send them off
    pieces.sort_by(|one, two| one.0.cmp(&two.0));

    Ok(pieces)
}

// ----------------------------------------------------------------------------//

fn start_progress_updater<C>(recv: Receiver<usize>, num_pieces: u64, mut progress: C)
    where C: FnMut(f64)
{
    for finished_piece in recv {
        let percent_complete = (finished_piece as f64) / (num_pieces as f64);

        progress(percent_complete);
    }
}

// ----------------------------------------------------------------------------//

/// Starts a hasher worker which will hash all of the buffers it receives.
fn start_hash_worker(send: Sender<MasterMessage>,
                     work: Arc<MsQueue<WorkerMessage>>,
                     buffers: Arc<PieceBuffers>) {
    let mut work_to_do = true;

    // Loop until we are instructed to stop working
    while work_to_do {
        let work_item = work.pop();

        match work_item {
            WorkerMessage::Finish => {
                work_to_do = false;
            }
            WorkerMessage::HashPiece(index, buffer) => {
                let hash = ShaHash::from_bytes(buffer.as_slice());

                send.send(MasterMessage::AcceptPiece(index, hash)).unwrap();
                buffers.checkin(buffer);
            }
        }
    }

    // Let the master know we have exited our thread
    send.send(MasterMessage::WorkerFinished).unwrap();
}

#[cfg(test)]
mod tests {
    use std::ops::{Range, Index};
    use std::io::{self, Cursor, Read};
    use std::path::Path;
    use std::sync::mpsc;

    use bip_util::sha::ShaHash;
    use rand::{self, Rng};

    use accessor::Accessor;
    use builder::worker;

    // Keep these numbers fairly small to avoid lengthy tests
    const DEFAULT_PIECE_LENGTH: usize = 1024;
    const DEFAULT_NUM_PIECES: usize = 300;

    // Mock object for providing direct access to bytes via DataEntry.
    #[derive(Clone)]
    struct MockAccessor {
        buffer_ranges: Vec<Range<usize>>,
        contiguous_buffer: Vec<u8>,
    }

    impl MockAccessor {
        fn new() -> MockAccessor {
            MockAccessor {
                buffer_ranges: Vec::new(),
                contiguous_buffer: Vec::new(),
            }
        }

        fn create_region(&mut self, num_bytes: usize) {
            let mut buffer = vec![0u8; num_bytes];
            let mut rng = rand::thread_rng();

            rng.fill_bytes(&mut buffer);

            let (begin, end) = (self.contiguous_buffer.len(),
                                self.contiguous_buffer.len() + buffer.len());

            self.contiguous_buffer.extend_from_slice(&buffer);
            self.buffer_ranges.push(begin..end);
        }

        fn as_slice(&self) -> &[u8] {
            &self.contiguous_buffer
        }
    }

    impl Accessor for MockAccessor {
        /// Access the directory that all files should be relative to.
        fn access_directory(&self) -> Option<&Path> {
            panic!("Accessor::access_directory should not be called with MockAccessor...");
        }

        /// Access the metadata for all files including their length and path.
        fn access_metadata<C>(&self, _: C) -> io::Result<()>
            where C: FnMut(u64, &Path)
        {
            panic!("Accessor::access_metadata should not be called with MockAccessor...");
        }

        /// Access the sequential pieces that make up all of the files.
        fn access_pieces<C>(&self, mut callback: C) -> io::Result<()>
            where C: FnMut(&mut Read) -> io::Result<()>
        {
            for range in self.buffer_ranges.iter() {
                let mut next_region = Cursor::new(self.contiguous_buffer.index(range.clone()));

                try!(callback(&mut next_region));
            }

            Ok(())
        }
    }

    fn validate_entries_pieces(accessor: MockAccessor, piece_length: usize, num_threads: usize) {
        let (prog_send, prog_recv) = mpsc::channel();

        let total_num_pieces = ((accessor.as_slice().len() as f64) / (piece_length as f64))
            .ceil() as u64;
        let received_pieces = worker::start_hasher_workers(&accessor,
                                                           piece_length,
                                                           total_num_pieces,
                                                           num_threads,
                                                           move |update| {
                                                               prog_send.send(update).unwrap();
                                                           }).unwrap();

        let computed_pieces = accessor.as_slice()
            .chunks(piece_length)
            .enumerate()
            .map(|(index, chunk)| (index, ShaHash::from_bytes(chunk)))
            .collect::<Vec<(usize, ShaHash)>>();

        let updates_received = prog_recv.iter().count() as u64;

        assert_eq!(total_num_pieces, updates_received);
        assert_eq!(received_pieces, computed_pieces);
    }

    #[test]
    fn positive_piece_length_divisible_region_single_thread() {
        let mut accessor = MockAccessor::new();

        let region_length = DEFAULT_PIECE_LENGTH * DEFAULT_NUM_PIECES;
        accessor.create_region(region_length);

        validate_entries_pieces(accessor, DEFAULT_PIECE_LENGTH, 1);
    }

    #[test]
    fn positive_piece_length_divisible_region_multiple_threads() {
        let mut accessor = MockAccessor::new();

        let region_length = DEFAULT_PIECE_LENGTH * DEFAULT_NUM_PIECES;
        accessor.create_region(region_length);

        validate_entries_pieces(accessor, DEFAULT_PIECE_LENGTH, 4);
    }

    #[test]
    fn positive_piece_length_undivisible_region_single_thread() {
        let mut accessor = MockAccessor::new();

        let region_length = DEFAULT_PIECE_LENGTH * DEFAULT_NUM_PIECES + 1;
        accessor.create_region(region_length);

        validate_entries_pieces(accessor, DEFAULT_PIECE_LENGTH, 1);
    }

    #[test]
    fn positive_piece_length_undivisible_region_multiple_threads() {
        let mut accessor = MockAccessor::new();

        let region_length = DEFAULT_PIECE_LENGTH * DEFAULT_NUM_PIECES + 1;
        accessor.create_region(region_length);

        validate_entries_pieces(accessor, DEFAULT_PIECE_LENGTH, 4);
    }

    #[test]
    fn positive_piece_length_divisible_regions_single_thread() {
        let mut accessor = MockAccessor::new();

        let region_lengths = [DEFAULT_PIECE_LENGTH * DEFAULT_NUM_PIECES,
                              DEFAULT_PIECE_LENGTH * 1,
                              DEFAULT_PIECE_LENGTH * 50];
        for &region_length in region_lengths.into_iter() {
            accessor.create_region(region_length);
        }

        validate_entries_pieces(accessor, DEFAULT_PIECE_LENGTH, 1);
    }

    #[test]
    fn positive_piece_length_divisible_regions_multiple_threads() {
        let mut accessor = MockAccessor::new();

        let region_lengths = [DEFAULT_PIECE_LENGTH * DEFAULT_NUM_PIECES,
                              DEFAULT_PIECE_LENGTH * 1,
                              DEFAULT_PIECE_LENGTH * 50];
        for &region_length in region_lengths.into_iter() {
            accessor.create_region(region_length);
        }

        validate_entries_pieces(accessor, DEFAULT_PIECE_LENGTH, 4);
    }

    #[test]
    fn positive_piece_length_undivisible_regions_single_thread() {
        let mut accessor = MockAccessor::new();

        let region_lengths = [DEFAULT_PIECE_LENGTH / 2 * DEFAULT_NUM_PIECES,
                              DEFAULT_PIECE_LENGTH / 4 * DEFAULT_NUM_PIECES,
                              DEFAULT_PIECE_LENGTH * 1,
                              (DEFAULT_PIECE_LENGTH * 2 - 1) * 2];
        for &region_length in region_lengths.into_iter() {
            accessor.create_region(region_length);
        }

        validate_entries_pieces(accessor, DEFAULT_PIECE_LENGTH, 1);
    }

    #[test]
    fn positive_piece_length_undivisible_regions_multiple_threads() {
        let mut accessor = MockAccessor::new();

        let region_lengths = [DEFAULT_PIECE_LENGTH / 2 * DEFAULT_NUM_PIECES,
                              DEFAULT_PIECE_LENGTH / 4 * DEFAULT_NUM_PIECES,
                              DEFAULT_PIECE_LENGTH * 1,
                              (DEFAULT_PIECE_LENGTH * 2 - 1) * 2];
        for &region_length in region_lengths.into_iter() {
            accessor.create_region(region_length);
        }

        validate_entries_pieces(accessor, DEFAULT_PIECE_LENGTH, 4);
    }
}
<|endoftext|>


<commit_before>use crate::alloc::Box;
#[cfg(no_maybe_uninit)]
use core::marker::PhantomData;
use core::mem;
#[cfg(not(no_maybe_uninit))]
use core::mem::MaybeUninit;
use core::ptr;

#[cfg(feature = "unstable-debug")]
use core::any;

pub struct Any {
    value: Value,
    drop: fn(&mut Value),
    fingerprint: Fingerprint,

    /// For panic messages only. Not used for comparison.
    #[cfg(feature = "unstable-debug")]
    type_name: &'static str,
}

union Value {
    ptr: *mut (),
    inline: [MaybeUninit<usize>; 2],
}

fn is_small<T>() -> bool {
    cfg!(not(no_maybe_uninit))
        && mem::size_of::<T>() <= mem::size_of::<Value>()
        && mem::align_of::<T>() <= mem::align_of::<Value>()
}

// These functions are all unsafe. They are not exposed to the user. Declaring
// them as `unsafe fn` would not make the rest of erased-serde any safer or more
// readable.
impl Any {
    // This is unsafe -- caller must not hold on to the Any beyond the lifetime
    // of T.
    //
    // Example of bad code:
    //
    //    let s = "bad".to_owned();
    //    let a = Any::new(&s);
    //    drop(s);
    //
    // Now `a.view()` and `a.take()` return references to a dead String.
    pub(crate) fn new<T>(t: T) -> Self {
        let value: Value;
        let drop: fn(&mut Value);
        let fingerprint = Fingerprint::of::<T>();

        if is_small::<T>() {
            let mut inline = [MaybeUninit::uninit(); 2];
            unsafe { ptr::write(inline.as_mut_ptr() as *mut T, t) };
            value = Value { inline };
            drop = |value| unsafe { ptr::drop_in_place(value.inline.as_mut_ptr() as *mut T) };
        } else {
            let ptr = Box::into_raw(Box::new(t)) as *mut ();
            value = Value { ptr };
            drop = |value| mem::drop(unsafe { Box::from_raw(value.ptr as *mut T) });
        };

        // Once attributes on struct literal fields are stable, do that instead.
        // https://github.com/rust-lang/rust/issues/41681
        #[cfg(not(feature = "unstable-debug"))]
        {
            Any {
                value,
                drop,
                fingerprint,
            }
        }

        #[cfg(feature = "unstable-debug")]
        {
            let type_name = any::type_name::<T>();
            Any {
                value,
                drop,
                fingerprint,
                type_name,
            }
        }
    }

    // This is unsafe -- caller is responsible that T is the correct type.
    pub(crate) fn view<T>(&mut self) -> &mut T {
        if self.fingerprint != Fingerprint::of::<T>() {
            self.invalid_cast_to::<T>();
        }

        let ptr = if is_small::<T>() {
            unsafe { self.value.inline.as_mut_ptr() as *mut T }
        } else {
            unsafe { self.value.ptr as *mut T }
        };

        unsafe { &mut *ptr }
    }

    // This is unsafe -- caller is responsible that T is the correct type.
    pub(crate) fn take<T>(mut self) -> T {
        if self.fingerprint != Fingerprint::of::<T>() {
            self.invalid_cast_to::<T>();
        }

        if is_small::<T>() {
            let ptr = unsafe { self.value.inline.as_mut_ptr() as *mut T };
            let value = unsafe { ptr::read(ptr) };
            mem::forget(self);
            value
        } else {
            let ptr = unsafe { self.value.ptr as *mut T };
            let box_t = unsafe { Box::from_raw(ptr) };
            mem::forget(self);
            *box_t
        }
    }

    #[cfg(not(feature = "unstable-debug"))]
    fn invalid_cast_to<T>(&self) -> ! {
        panic!("invalid cast; enable `unstable-debug` feature to debug");
    }

    #[cfg(feature = "unstable-debug")]
    fn invalid_cast_to<T>(&self) -> ! {
        let from = self.type_name;
        let to = any::type_name::<T>();
        panic!("invalid cast: {} to {}", from, to);
    }
}

impl Drop for Any {
    fn drop(&mut self) {
        (self.drop)(&mut self.value);
    }
}

#[cfg(no_maybe_uninit)]
#[derive(Copy, Clone)]
struct MaybeUninit<T>(PhantomData<T>);

#[cfg(no_maybe_uninit)]
impl<T> MaybeUninit<T> {
    fn uninit() -> Self {
        MaybeUninit(PhantomData)
    }
}

#[derive(Debug, Eq, PartialEq)]
struct Fingerprint {
    size: usize,
    align: usize,
    id: usize,
}

impl Fingerprint {
    fn of<T>() -> Fingerprint {
        Fingerprint {
            size: mem::size_of::<T>(),
            align: mem::align_of::<T>(),
            // This is not foolproof -- theoretically Rust or LLVM could
            // deduplicate some or all of these methods. But in practice it's
            // great and I am comfortable relying on this in debug mode to catch
            // bugs early.
            id: Fingerprint::of::<T> as usize,
        }
    }
}

#[test]
fn test_fingerprint() {
    assert_eq!(Fingerprint::of::<usize>(), Fingerprint::of::<usize>());
    assert_eq!(Fingerprint::of::<&str>(), Fingerprint::of::<&'static str>());

    assert_ne!(Fingerprint::of::<usize>(), Fingerprint::of::<isize>());
    assert_ne!(Fingerprint::of::<usize>(), Fingerprint::of::<&usize>());
    assert_ne!(Fingerprint::of::<&usize>(), Fingerprint::of::<&&usize>());
    assert_ne!(Fingerprint::of::<&usize>(), Fingerprint::of::<&mut usize>());

    struct A;
    struct B;
    assert_ne!(Fingerprint::of::<A>(), Fingerprint::of::<B>());
}
<commit_msg>Disable fingerprint checks on miri<commit_after>use crate::alloc::Box;
#[cfg(no_maybe_uninit)]
use core::marker::PhantomData;
use core::mem;
#[cfg(not(no_maybe_uninit))]
use core::mem::MaybeUninit;
use core::ptr;

#[cfg(feature = "unstable-debug")]
use core::any;

pub struct Any {
    value: Value,
    drop: fn(&mut Value),
    fingerprint: Fingerprint,

    /// For panic messages only. Not used for comparison.
    #[cfg(feature = "unstable-debug")]
    type_name: &'static str,
}

union Value {
    ptr: *mut (),
    inline: [MaybeUninit<usize>; 2],
}

fn is_small<T>() -> bool {
    cfg!(not(no_maybe_uninit))
        && mem::size_of::<T>() <= mem::size_of::<Value>()
        && mem::align_of::<T>() <= mem::align_of::<Value>()
}

// These functions are all unsafe. They are not exposed to the user. Declaring
// them as `unsafe fn` would not make the rest of erased-serde any safer or more
// readable.
impl Any {
    // This is unsafe -- caller must not hold on to the Any beyond the lifetime
    // of T.
    //
    // Example of bad code:
    //
    //    let s = "bad".to_owned();
    //    let a = Any::new(&s);
    //    drop(s);
    //
    // Now `a.view()` and `a.take()` return references to a dead String.
    pub(crate) fn new<T>(t: T) -> Self {
        let value: Value;
        let drop: fn(&mut Value);
        let fingerprint = Fingerprint::of::<T>();

        if is_small::<T>() {
            let mut inline = [MaybeUninit::uninit(); 2];
            unsafe { ptr::write(inline.as_mut_ptr() as *mut T, t) };
            value = Value { inline };
            drop = |value| unsafe { ptr::drop_in_place(value.inline.as_mut_ptr() as *mut T) };
        } else {
            let ptr = Box::into_raw(Box::new(t)) as *mut ();
            value = Value { ptr };
            drop = |value| mem::drop(unsafe { Box::from_raw(value.ptr as *mut T) });
        };

        // Once attributes on struct literal fields are stable, do that instead.
        // https://github.com/rust-lang/rust/issues/41681
        #[cfg(not(feature = "unstable-debug"))]
        {
            Any {
                value,
                drop,
                fingerprint,
            }
        }

        #[cfg(feature = "unstable-debug")]
        {
            let type_name = any::type_name::<T>();
            Any {
                value,
                drop,
                fingerprint,
                type_name,
            }
        }
    }

    // This is unsafe -- caller is responsible that T is the correct type.
    pub(crate) fn view<T>(&mut self) -> &mut T {
        if cfg!(not(miri)) && self.fingerprint != Fingerprint::of::<T>() {
            self.invalid_cast_to::<T>();
        }

        let ptr = if is_small::<T>() {
            unsafe { self.value.inline.as_mut_ptr() as *mut T }
        } else {
            unsafe { self.value.ptr as *mut T }
        };

        unsafe { &mut *ptr }
    }

    // This is unsafe -- caller is responsible that T is the correct type.
    pub(crate) fn take<T>(mut self) -> T {
        if cfg!(not(miri)) && self.fingerprint != Fingerprint::of::<T>() {
            self.invalid_cast_to::<T>();
        }

        if is_small::<T>() {
            let ptr = unsafe { self.value.inline.as_mut_ptr() as *mut T };
            let value = unsafe { ptr::read(ptr) };
            mem::forget(self);
            value
        } else {
            let ptr = unsafe { self.value.ptr as *mut T };
            let box_t = unsafe { Box::from_raw(ptr) };
            mem::forget(self);
            *box_t
        }
    }

    #[cfg(not(feature = "unstable-debug"))]
    fn invalid_cast_to<T>(&self) -> ! {
        panic!("invalid cast; enable `unstable-debug` feature to debug");
    }

    #[cfg(feature = "unstable-debug")]
    fn invalid_cast_to<T>(&self) -> ! {
        let from = self.type_name;
        let to = any::type_name::<T>();
        panic!("invalid cast: {} to {}", from, to);
    }
}

impl Drop for Any {
    fn drop(&mut self) {
        (self.drop)(&mut self.value);
    }
}

#[cfg(no_maybe_uninit)]
#[derive(Copy, Clone)]
struct MaybeUninit<T>(PhantomData<T>);

#[cfg(no_maybe_uninit)]
impl<T> MaybeUninit<T> {
    fn uninit() -> Self {
        MaybeUninit(PhantomData)
    }
}

#[derive(Debug, Eq, PartialEq)]
struct Fingerprint {
    size: usize,
    align: usize,
    id: usize,
}

impl Fingerprint {
    fn of<T>() -> Fingerprint {
        Fingerprint {
            size: mem::size_of::<T>(),
            align: mem::align_of::<T>(),
            // This is not foolproof -- theoretically Rust or LLVM could
            // deduplicate some or all of these methods. But in practice it's
            // great and I am comfortable relying on this in debug mode to catch
            // bugs early.
            id: Fingerprint::of::<T> as usize,
        }
    }
}

#[cfg(not(miri))]
#[test]
fn test_fingerprint() {
    assert_eq!(Fingerprint::of::<usize>(), Fingerprint::of::<usize>());
    assert_eq!(Fingerprint::of::<&str>(), Fingerprint::of::<&'static str>());

    assert_ne!(Fingerprint::of::<usize>(), Fingerprint::of::<isize>());
    assert_ne!(Fingerprint::of::<usize>(), Fingerprint::of::<&usize>());
    assert_ne!(Fingerprint::of::<&usize>(), Fingerprint::of::<&&usize>());
    assert_ne!(Fingerprint::of::<&usize>(), Fingerprint::of::<&mut usize>());

    struct A;
    struct B;
    assert_ne!(Fingerprint::of::<A>(), Fingerprint::of::<B>());
}
<|endoftext|>


<commit_before>//!
//! The API for communicating with devices.
//!
//! This API is provided as Traits to be implemented:
//!
//! - by the low-level layers of the FoxBox, including the adapters;
//! - by test suites and tools that need to simulate connected devices.
//!
//! In turn, this API is used to implement:
//!
//! - the public-facing REST and WebSocket API;
//! - the rules API (ThinkerBell).
//!
//!

use devices::*;
use requests::*;
use values::Value;

/// An error produced by one of the APIs in this module.
pub enum Error {
    /// There is no such node connected to the Foxbox, even indirectly.
    NoSuchNode(NodeId),

    /// There is no such service connected to the Foxbox, even indirectly.
    NoSuchService(ServiceId),

    /// Attempting to set a value with the wrong type
    TypeError,
}

/// An event during watching.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum WatchEvent {
    /// A new value is available.
    Value {
        /// The service that sent the value.
        from: ServiceId,

        /// The actual value.
        value: Value
    },

    /// The set of devices being watched has changed, typically either
    /// because a tag was edited or because a device was
    /// removed. Payload is the id of the device that was removed.
    InputRemoved(ServiceId),

    /// The set of devices being watched has changed, typically either
    /// because a tag was edited or because a device was
    /// added. Payload is the id of the device that was added.
    InputAdded(ServiceId),
}

/// The public API.
pub trait API {
    /// Get the metadata on nodes matching some conditions.
    ///
    /// A call to `API::get_nodes(vec![req1, req2, ...])` will return
    /// the metadata on all nodes matching _either_ `req1` or `req2`
    /// or ...
    ///
    /// # REST API
    ///
    /// `GET /api/v1/nodes`
    ///
    /// ## Inputs
    ///
    /// Any JSON that can be deserialized to a `Vec<NodeRequest>`. See
    /// the implementation of `NodeRequest` for details.
    ///
    /// ### Example
    ///
    /// Request all doors in the entrance (tags `door`, `entrance`)
    /// that support output service `OpenClose`
    ///
    /// ```json
    /// [{
    ///   "tags": ["entrance", "door"],
    ///   "inputs": [
    ///     {
    ///       "kind": {
    ///         "Exactly": {
    ///           "OpenClose": []
    ///         }
    ///       }
    ///     }
    ///   ]
    /// }]
    /// ```
    ///
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing an array of `Node`. See the implementation
    /// of `Node` for details.
    ///
    /// ### Example
    ///
    /// ```json
    /// [{
    ///   "tags": ["entrance", "door", "somevendor"],
    ///   "id: "some-node-id",
    ///   "inputs": [],
    ///   "outputs": [
    ///     "tags": [...],
    ///     "id": "some-service-id",
    ///     "node": "some-node-id",
    ///     "last_seen": "some-date",
    ///     "mechanism": {
    ///       "Output":  {
    ///         "kind": {
    ///           "OnOff": []
    ///         },
    ///         "push": [5000],
    ///         "updated": "some-date",
    ///       }
    ///     }
    ///   ]
    /// }]
    /// ```
    fn get_nodes(&Vec<NodeRequest>) -> Vec<Node>;

    /// Label a set of nodes with a set of tags.
    ///
    /// A call to `API::put_node_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will label all the nodes matching _either_ `req1` or
    /// `req2` or ... with `tag1`, ... and return the number of nodes
    /// matching any of the requests.
    ///
    /// Some of the nodes may already be labelled with `tag1`, or
    /// `tag2`, ... They will not change state. They are counted in
    /// the resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if nodes
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `POST /api/v1/nodes/tag`
    ///
    /// ## Inputs
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```rust
    /// {
    ///   set: Vec<NodeRequest>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON string representing a number.
    fn put_node_tag(set: &Vec<NodeRequest>, tags: &Vec<String>) -> usize;

    /// Remove a set of tags from a set of nodes.
    ///
    /// A call to `API::delete_node_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will remove from all the nodes matching _either_ `req1` or
    /// `req2` or ... all of the tags `tag1`, ... and return the number of nodes
    /// matching any of the requests.
    ///
    /// Some of the nodes may not be labelled with `tag1`, or `tag2`,
    /// ... They will not change state. They are counted in the
    /// resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if nodes
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/nodes/tag`
    ///
    /// ## Inputs
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```rust
    /// {
    ///   set: Vec<NodeRequest>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing a number.
    fn delete_node_tag(set: &Vec<NodeRequest>, tags: String) -> usize;

    /// Get a list of inputs matching some conditions
    ///
    /// # REST API
    ///
    /// `GET /api/v1/services`
    fn get_input_services(&Vec<InputRequest>) -> Vec<Service<Input>>;
    fn get_output_services(&Vec<OutputRequest>) -> Vec<Service<Output>>;

    /// Label a set of services with a set of tags.
    ///
    /// A call to `API::put_{input, output}_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will label all the services matching _either_ `req1` or
    /// `req2` or ... with `tag1`, ... and return the number of services
    /// matching any of the requests.
    ///
    /// Some of the services may already be labelled with `tag1`, or
    /// `tag2`, ... They will not change state. They are counted in
    /// the resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if services
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `POST /api/v1/services/tag`
    ///
    /// ## Inputs
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```rust
    /// {
    ///   set: Vec<InputRequest>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    /// or
    /// ```rust
    /// {
    ///   set: Vec<OutputRequest>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing a number.
    fn put_input_tag(&Vec<InputRequest>, &Vec<String>) -> usize;
    fn put_output_tag(&Vec<OutputRequest>, &Vec<String>) -> usize;

    /// Remove a set of tags from a set of services.
    ///
    /// A call to `API::delete_{input, output}_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will remove from all the services matching _either_ `req1` or
    /// `req2` or ... all of the tags `tag1`, ... and return the number of services
    /// matching any of the requests.
    ///
    /// Some of the services may not be labelled with `tag1`, or `tag2`,
    /// ... They will not change state. They are counted in the
    /// resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if services
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/services/tag`
    ///
    /// ## Inputs
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```rust
    /// {
    ///   set: Vec<InputRequest>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    /// or
    /// ```rust
    /// {
    ///   set: Vec<OutputRequest>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing a number.
    fn delete_input_tag(&Vec<InputRequest>, &Vec<String>) -> usize;
    fn delete_output_tag(&Vec<InputRequest>, &Vec<String>) -> usize;

    /// Read the latest value from a set of services
    ///
    /// # REST API
    ///
    /// `GET /api/v1/services/value`
    fn get_service_value(&Vec<InputRequest>) -> Vec<(ServiceId, Result<Value, Error>)>;

    /// Send one value to a set of services
    ///
    /// # REST API
    ///
    /// `POST /api/v1/services/value`
    fn put_service_value(&Vec<OutputRequest>, Value) -> Vec<(ServiceId, Result<(), Error>)>;

    /// Watch for any change
    ///
    /// # WebSocket API
    ///
    /// `/api/v1/services/watch`
    fn register_service_watch<F>(Vec<WatchOptions>, cb: F) -> Self::WatchGuard
        where F: FnMut(WatchEvent) + Send + Sync;

    /// A value that causes a disconnection once it is dropped.
    type WatchGuard;
}

/// Options for watching changes in one or more services.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct WatchOptions {
    /// The set of inputs to watch. Note that the actual inputs in the
    /// set may change over time.
    source: InputRequest,

    /// If `true`, watch as new values become available.
    should_watch_values: bool,

    /// If `true`, watch as nodes are connected/disconnected.
    should_watch_topology: bool,
}

impl WatchOptions {
    pub fn new() -> Self {
        WatchOptions {
            source: InputRequest::new(),
            should_watch_values: false,
            should_watch_topology: false,
        }
    }

    /// Restrict to input services in a given set.
    ///
    /// Also note that the actual input services that are part of the
    /// set may change with time, for instance if devices are added
    /// ore removed.  The request _is live_, i.e. the service watch
    /// will continue watching any input services that match `req`.
    pub fn with_inputs(self, req: InputRequest) -> Self {
        WatchOptions {
            source: self.source.and(req),
            ..self
        }
    }

    pub fn with_watch_values(self, should: bool) -> Self {
        WatchOptions {
            should_watch_values: should,
            ..self
        }
    }

    pub fn with_watch_topology(self, should: bool) -> Self {
        WatchOptions {
            should_watch_topology: should,
            ..self
        }
    }
}
<commit_msg>API::register_service_watch now requires 'static instead of Sync<commit_after>//!
//! The API for communicating with devices.
//!
//! This API is provided as Traits to be implemented:
//!
//! - by the low-level layers of the FoxBox, including the adapters;
//! - by test suites and tools that need to simulate connected devices.
//!
//! In turn, this API is used to implement:
//!
//! - the public-facing REST and WebSocket API;
//! - the rules API (ThinkerBell).
//!
//!

use devices::*;
use requests::*;
use values::Value;

/// An error produced by one of the APIs in this module.
pub enum Error {
    /// There is no such node connected to the Foxbox, even indirectly.
    NoSuchNode(NodeId),

    /// There is no such service connected to the Foxbox, even indirectly.
    NoSuchService(ServiceId),

    /// Attempting to set a value with the wrong type
    TypeError,
}

/// An event during watching.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum WatchEvent {
    /// A new value is available.
    Value {
        /// The service that sent the value.
        from: ServiceId,

        /// The actual value.
        value: Value
    },

    /// The set of devices being watched has changed, typically either
    /// because a tag was edited or because a device was
    /// removed. Payload is the id of the device that was removed.
    InputRemoved(ServiceId),

    /// The set of devices being watched has changed, typically either
    /// because a tag was edited or because a device was
    /// added. Payload is the id of the device that was added.
    InputAdded(ServiceId),
}

/// The public API.
pub trait API {
    /// Get the metadata on nodes matching some conditions.
    ///
    /// A call to `API::get_nodes(vec![req1, req2, ...])` will return
    /// the metadata on all nodes matching _either_ `req1` or `req2`
    /// or ...
    ///
    /// # REST API
    ///
    /// `GET /api/v1/nodes`
    ///
    /// ## Inputs
    ///
    /// Any JSON that can be deserialized to a `Vec<NodeRequest>`. See
    /// the implementation of `NodeRequest` for details.
    ///
    /// ### Example
    ///
    /// Request all doors in the entrance (tags `door`, `entrance`)
    /// that support output service `OpenClose`
    ///
    /// ```json
    /// [{
    ///   "tags": ["entrance", "door"],
    ///   "inputs": [
    ///     {
    ///       "kind": {
    ///         "Exactly": {
    ///           "OpenClose": []
    ///         }
    ///       }
    ///     }
    ///   ]
    /// }]
    /// ```
    ///
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing an array of `Node`. See the implementation
    /// of `Node` for details.
    ///
    /// ### Example
    ///
    /// ```json
    /// [{
    ///   "tags": ["entrance", "door", "somevendor"],
    ///   "id: "some-node-id",
    ///   "inputs": [],
    ///   "outputs": [
    ///     "tags": [...],
    ///     "id": "some-service-id",
    ///     "node": "some-node-id",
    ///     "last_seen": "some-date",
    ///     "mechanism": {
    ///       "Output":  {
    ///         "kind": {
    ///           "OnOff": []
    ///         },
    ///         "push": [5000],
    ///         "updated": "some-date",
    ///       }
    ///     }
    ///   ]
    /// }]
    /// ```
    fn get_nodes(&Vec<NodeRequest>) -> Vec<Node>;

    /// Label a set of nodes with a set of tags.
    ///
    /// A call to `API::put_node_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will label all the nodes matching _either_ `req1` or
    /// `req2` or ... with `tag1`, ... and return the number of nodes
    /// matching any of the requests.
    ///
    /// Some of the nodes may already be labelled with `tag1`, or
    /// `tag2`, ... They will not change state. They are counted in
    /// the resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if nodes
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `POST /api/v1/nodes/tag`
    ///
    /// ## Inputs
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```rust
    /// {
    ///   set: Vec<NodeRequest>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON string representing a number.
    fn put_node_tag(set: &Vec<NodeRequest>, tags: &Vec<String>) -> usize;

    /// Remove a set of tags from a set of nodes.
    ///
    /// A call to `API::delete_node_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will remove from all the nodes matching _either_ `req1` or
    /// `req2` or ... all of the tags `tag1`, ... and return the number of nodes
    /// matching any of the requests.
    ///
    /// Some of the nodes may not be labelled with `tag1`, or `tag2`,
    /// ... They will not change state. They are counted in the
    /// resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if nodes
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/nodes/tag`
    ///
    /// ## Inputs
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```rust
    /// {
    ///   set: Vec<NodeRequest>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing a number.
    fn delete_node_tag(set: &Vec<NodeRequest>, tags: String) -> usize;

    /// Get a list of inputs matching some conditions
    ///
    /// # REST API
    ///
    /// `GET /api/v1/services`
    fn get_input_services(&Vec<InputRequest>) -> Vec<Service<Input>>;
    fn get_output_services(&Vec<OutputRequest>) -> Vec<Service<Output>>;

    /// Label a set of services with a set of tags.
    ///
    /// A call to `API::put_{input, output}_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will label all the services matching _either_ `req1` or
    /// `req2` or ... with `tag1`, ... and return the number of services
    /// matching any of the requests.
    ///
    /// Some of the services may already be labelled with `tag1`, or
    /// `tag2`, ... They will not change state. They are counted in
    /// the resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if services
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `POST /api/v1/services/tag`
    ///
    /// ## Inputs
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```rust
    /// {
    ///   set: Vec<InputRequest>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    /// or
    /// ```rust
    /// {
    ///   set: Vec<OutputRequest>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing a number.
    fn put_input_tag(&Vec<InputRequest>, &Vec<String>) -> usize;
    fn put_output_tag(&Vec<OutputRequest>, &Vec<String>) -> usize;

    /// Remove a set of tags from a set of services.
    ///
    /// A call to `API::delete_{input, output}_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will remove from all the services matching _either_ `req1` or
    /// `req2` or ... all of the tags `tag1`, ... and return the number of services
    /// matching any of the requests.
    ///
    /// Some of the services may not be labelled with `tag1`, or `tag2`,
    /// ... They will not change state. They are counted in the
    /// resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if services
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/services/tag`
    ///
    /// ## Inputs
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```rust
    /// {
    ///   set: Vec<InputRequest>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    /// or
    /// ```rust
    /// {
    ///   set: Vec<OutputRequest>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing a number.
    fn delete_input_tag(&Vec<InputRequest>, &Vec<String>) -> usize;
    fn delete_output_tag(&Vec<InputRequest>, &Vec<String>) -> usize;

    /// Read the latest value from a set of services
    ///
    /// # REST API
    ///
    /// `GET /api/v1/services/value`
    fn get_service_value(&Vec<InputRequest>) -> Vec<(ServiceId, Result<Value, Error>)>;

    /// Send one value to a set of services
    ///
    /// # REST API
    ///
    /// `POST /api/v1/services/value`
    fn put_service_value(&Vec<OutputRequest>, Value) -> Vec<(ServiceId, Result<(), Error>)>;

    /// Watch for any change
    ///
    /// # WebSocket API
    ///
    /// `/api/v1/services/watch`
    fn register_service_watch<F>(Vec<WatchOptions>, cb: F) -> Self::WatchGuard
        where F: FnMut(WatchEvent) + Send + 'static;

    /// A value that causes a disconnection once it is dropped.
    type WatchGuard;
}

/// Options for watching changes in one or more services.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct WatchOptions {
    /// The set of inputs to watch. Note that the actual inputs in the
    /// set may change over time.
    source: InputRequest,

    /// If `true`, watch as new values become available.
    should_watch_values: bool,

    /// If `true`, watch as nodes are connected/disconnected.
    should_watch_topology: bool,
}

impl WatchOptions {
    pub fn new() -> Self {
        WatchOptions {
            source: InputRequest::new(),
            should_watch_values: false,
            should_watch_topology: false,
        }
    }

    /// Restrict to input services in a given set.
    ///
    /// Also note that the actual input services that are part of the
    /// set may change with time, for instance if devices are added
    /// ore removed.  The request _is live_, i.e. the service watch
    /// will continue watching any input services that match `req`.
    pub fn with_inputs(self, req: InputRequest) -> Self {
        WatchOptions {
            source: self.source.and(req),
            ..self
        }
    }

    pub fn with_watch_values(self, should: bool) -> Self {
        WatchOptions {
            should_watch_values: should,
            ..self
        }
    }

    pub fn with_watch_topology(self, should: bool) -> Self {
        WatchOptions {
            should_watch_topology: should,
            ..self
        }
    }
}
<|endoftext|>


<commit_before>use std::iter::Iterator;

use runtime::Runtime;
use storage::file::File;

pub trait FilePrinter {

    fn new(verbose: bool, debug: bool) -> Self;

    /*
     * Print a single file
     */
    fn print_file(&self, &File);

    /*
     * Print a list of files
     */
    fn print_files<'a, I: Iterator<Item = File<'a>>>(&self, files: I) {
        for file in files {
            self.print_file(&file);
        }
    }

}

struct DebugPrinter {
    debug: bool,
}

impl FilePrinter for DebugPrinter {

    fn new(verbose: bool, debug: bool) -> DebugPrinter {
        DebugPrinter {
            debug: debug,
        }
    }

    fn print_file(&self, f: &File) {
        if self.debug {
            debug!("[DebugPrinter] ->\n{:?}", f);
        }
    }

}

struct SimplePrinter {
    verbose:    bool,
    debug:      bool,
}

impl FilePrinter for SimplePrinter {

    fn new(verbose: bool, debug: bool) -> SimplePrinter {
        SimplePrinter {
            debug:      debug,
            verbose:    verbose,
        }
    }

    fn print_file(&self, f: &File) {
        if self.debug {
            debug!("{:?}", f);
        } else if self.verbose {
            info!("{}", f);
        } else {
            info!("[File]: {}", f.id());
        }
    }

}

struct TablePrinter {
    verbose:    bool,
    debug:      bool,
    sp:         SimplePrinter,
}

impl FilePrinter for TablePrinter {

    fn new(verbose: bool, debug: bool) -> TablePrinter {
        TablePrinter {
            debug:      debug,
            verbose:    verbose,
            sp:         SimplePrinter::new(verbose, debug),
        }
    }

    fn print_file(&self, f: &File) {
        self.sp.print_file(f);
    }

    fn print_files<'a, I: Iterator<Item = File<'a>>>(&self, files: I) {
        use prettytable::Table;
        use prettytable::row::Row;
        use prettytable::cell::Cell;

        let titles = row!["File#", "Owner", "ID"];

        let mut tab = Table::new();
        tab.set_titles(titles);

        let mut i = 0;
        for file in files {
            i += 1;
            let cell_i  = Cell::new(&format!("{}", i)[..]);
            let cell_o  = Cell::new(&format!("{}", file.owner().name())[..]);
            let cell_id = Cell::new(&file.id()[..]);
            let row = Row::new(vec![cell_i, cell_o, cell_id]);
            tab.add_row(row);
        }

        tab.printstd();
    }

}
<commit_msg>TablePrinter should be pub<commit_after>use std::iter::Iterator;

use runtime::Runtime;
use storage::file::File;

pub trait FilePrinter {

    fn new(verbose: bool, debug: bool) -> Self;

    /*
     * Print a single file
     */
    fn print_file(&self, &File);

    /*
     * Print a list of files
     */
    fn print_files<'a, I: Iterator<Item = File<'a>>>(&self, files: I) {
        for file in files {
            self.print_file(&file);
        }
    }

}

struct DebugPrinter {
    debug: bool,
}

impl FilePrinter for DebugPrinter {

    fn new(verbose: bool, debug: bool) -> DebugPrinter {
        DebugPrinter {
            debug: debug,
        }
    }

    fn print_file(&self, f: &File) {
        if self.debug {
            debug!("[DebugPrinter] ->\n{:?}", f);
        }
    }

}

struct SimplePrinter {
    verbose:    bool,
    debug:      bool,
}

impl FilePrinter for SimplePrinter {

    fn new(verbose: bool, debug: bool) -> SimplePrinter {
        SimplePrinter {
            debug:      debug,
            verbose:    verbose,
        }
    }

    fn print_file(&self, f: &File) {
        if self.debug {
            debug!("{:?}", f);
        } else if self.verbose {
            info!("{}", f);
        } else {
            info!("[File]: {}", f.id());
        }
    }

}

pub struct TablePrinter {
    verbose:    bool,
    debug:      bool,
    sp:         SimplePrinter,
}

impl FilePrinter for TablePrinter {

    fn new(verbose: bool, debug: bool) -> TablePrinter {
        TablePrinter {
            debug:      debug,
            verbose:    verbose,
            sp:         SimplePrinter::new(verbose, debug),
        }
    }

    fn print_file(&self, f: &File) {
        self.sp.print_file(f);
    }

    fn print_files<'a, I: Iterator<Item = File<'a>>>(&self, files: I) {
        use prettytable::Table;
        use prettytable::row::Row;
        use prettytable::cell::Cell;

        let titles = row!["File#", "Owner", "ID"];

        let mut tab = Table::new();
        tab.set_titles(titles);

        let mut i = 0;
        for file in files {
            i += 1;
            let cell_i  = Cell::new(&format!("{}", i)[..]);
            let cell_o  = Cell::new(&format!("{}", file.owner().name())[..]);
            let cell_id = Cell::new(&file.id()[..]);
            let row = Row::new(vec![cell_i, cell_o, cell_id]);
            tab.add_row(row);
        }

        tab.printstd();
    }

}
<|endoftext|>


<commit_before>use std::io::Read;
use std::path::Path;
use std::fs;
use std::env;

use flate2::read::GzDecoder;
use tar::Archive;
use curl::http;
use rustc_serialize::json;

use error::TldrError::{self, UpdateError};

#[derive(Debug, RustcEncodable, RustcDecodable)]
struct TldrIndex {
    commands: Vec<TldrCommand>,
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
struct TldrCommand {
    name: String,
    platform: Vec<String>,
}

impl TldrIndex {
    /// Return vec of all occuring platforms, without duplicates.
    fn unique_platforms(&self) -> Vec<String> {
        let mut flattened: Vec<String> = self.commands
                                             .iter()
                                             .flat_map(|cmd| cmd.platform.clone())
                                             .collect();
        flattened.sort();
        flattened.dedup();
        flattened
    }
}

#[derive(Debug)]
pub struct Updater {
    url: String,
}

impl Updater {

    pub fn new<S>(url: S) -> Updater where S: Into<String> {
        Updater {
            url: url.into(),
        }
    }

    /// Download the archive
    fn download(&self) -> Result<http::Response, TldrError> {
        let resp = try!(
            http::handle()
                 .follow_location(1)
                 .get(&self.url[..])
                 .exec()
        );
        Ok(resp)
    }

    /// Decompress and open the archive
    fn decompress<R: Read>(&self, reader: R) -> Result<Archive<GzDecoder<R>>, TldrError> {
        let decoder = try!(GzDecoder::new(reader).map_err(|_| UpdateError("Could not decode gzip data".into())));
        Ok(Archive::new(decoder))
    }

    /// Given the path to the `pages` directory, return `TldrIndex` instances.
    fn get_index(&self, path: &Path) -> Result<TldrIndex, TldrError> {
        let mut buffer = String::new();
        let mut index = try!(fs::File::open(path.join("index.json"))
            .map_err(|_| UpdateError("Could not open index.json".into())));
        try!(index.read_to_string(&mut buffer)
            .map_err(|_| UpdateError("Could not read index.json".into())));
        let deserialized: TldrIndex = try!(json::decode(&buffer)
            .map_err(|e| UpdateError(format!("Could not deserialize index.json: {}", e))));
        Ok(deserialized)
    }

    /// Copy all pages to the cache. Return the number of copied pages.
    fn copy_pages(&self, src_dir: &Path, dst_dir: &Path, index: &TldrIndex) -> Result<u64, TldrError> {
        // Make sure all platform directories exist
        for platform in &index.unique_platforms() {
            debug!("Ensure platform directory {:?} exists", &dst_dir.join(platform));
            try!(fs::create_dir_all(&dst_dir.join(platform)).map_err(|e| {
                UpdateError(format!("Could not create platform directory: {}", e))
            }));
        }

        let mut copied = 0u64;
        for page in &index.commands {
            for platform in &page.platform {
                let relpath = Path::new(&platform).join(format!("{}.md", &page.name));
                let bytes = fs::copy(&src_dir.join(&relpath), &dst_dir.join(&relpath)).unwrap_or_else(|e| {
                    debug!("Could not copy {:?} to {:?}: {}",
                           &src_dir.join(&relpath), &dst_dir.join(&relpath), e);
                    println!("Warning: Could not copy the tldr page for the \"{}\" command.", &page.name);
                    0u64
                });
                if bytes > 0 {
                    copied += 1;
                };
            }
        };

        debug!("Copied {} pages to {:?}", copied, &dst_dir);
        Ok(copied)
    }

    /// Update the pages cache. Return the number of cached pages.
    pub fn update(&self) -> Result<(), TldrError> {
        // First, download the compressed data
        let response = try!(self.download());

        // Decompress the response body into an `Archive`
        let mut archive = try!(self.decompress(response.get_body()));

        // Determine paths
        let home_dir = try!(env::home_dir().ok_or(UpdateError("Could not determine home directory".into())));
        let cache_dir = home_dir.join(".cache").join("tldr-rs");

        // Extract archive
        try!(archive.unpack(&cache_dir).map_err(|e| {
            UpdateError(format!("Could not unpack compressed data: {}", e))
        }));

        // Make sure that cache directory exists
        debug!("Ensure cache directory {:?} exists", &cache_dir);
        try!(fs::create_dir_all(&cache_dir).map_err(|e| {
            UpdateError(format!("Could not create cache directory: {}", e))
        }));

        Ok(())
    }

}
<commit_msg>Remove obsolete functions in updater<commit_after>use std::io::Read;
use std::fs;
use std::env;

use flate2::read::GzDecoder;
use tar::Archive;
use curl::http;

use error::TldrError::{self, UpdateError};

#[derive(Debug)]
pub struct Updater {
    url: String,
}

impl Updater {

    pub fn new<S>(url: S) -> Updater where S: Into<String> {
        Updater {
            url: url.into(),
        }
    }

    /// Download the archive
    fn download(&self) -> Result<http::Response, TldrError> {
        let resp = try!(
            http::handle()
                 .follow_location(1)
                 .get(&self.url[..])
                 .exec()
        );
        Ok(resp)
    }

    /// Decompress and open the archive
    fn decompress<R: Read>(&self, reader: R) -> Result<Archive<GzDecoder<R>>, TldrError> {
        let decoder = try!(GzDecoder::new(reader).map_err(|_| UpdateError("Could not decode gzip data".into())));
        Ok(Archive::new(decoder))
    }

    /// Update the pages cache. Return the number of cached pages.
    pub fn update(&self) -> Result<(), TldrError> {
        // First, download the compressed data
        let response = try!(self.download());

        // Decompress the response body into an `Archive`
        let mut archive = try!(self.decompress(response.get_body()));

        // Determine paths
        let home_dir = try!(env::home_dir().ok_or(UpdateError("Could not determine home directory".into())));
        let cache_dir = home_dir.join(".cache").join("tldr-rs");

        // Extract archive
        try!(archive.unpack(&cache_dir).map_err(|e| {
            UpdateError(format!("Could not unpack compressed data: {}", e))
        }));

        // Make sure that cache directory exists
        debug!("Ensure cache directory {:?} exists", &cache_dir);
        try!(fs::create_dir_all(&cache_dir).map_err(|e| {
            UpdateError(format!("Could not create cache directory: {}", e))
        }));

        Ok(())
    }

}
<|endoftext|>


<commit_before>use nix::Error;
use nix::errno::*;
use nix::fcntl::{openat, open, OFlag, readlink, readlinkat, renameat};
use nix::sys::stat::Mode;
use nix::unistd::{close, read};
use tempfile::{self, NamedTempFile};
use std::fs::File;
use std::io::prelude::*;
use std::os::unix::fs;

#[test]
fn test_openat() {
    const CONTENTS: &[u8] = b"abcd";
    let mut tmp = NamedTempFile::new().unwrap();
    tmp.write_all(CONTENTS).unwrap();

    let dirfd = open(tmp.path().parent().unwrap(),
                     OFlag::empty(),
                     Mode::empty()).unwrap();
    let fd = openat(dirfd,
                    tmp.path().file_name().unwrap(),
                    OFlag::O_RDONLY,
                    Mode::empty()).unwrap();

    let mut buf = [0u8; 1024];
    assert_eq!(4, read(fd, &mut buf).unwrap());
    assert_eq!(CONTENTS, &buf[0..4]);

    close(fd).unwrap();
    close(dirfd).unwrap();
}

#[test]
fn test_renameat() {
    let old_dir = tempfile::tempdir().unwrap();
    let old_dirfd = open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
    let old_path = old_dir.path().join("old");
    File::create(&old_path).unwrap();
    let new_dir = tempfile::tempdir().unwrap();
    let new_dirfd = open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
    renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap();
    assert_eq!(renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap_err(),
               Error::Sys(Errno::ENOENT));
    close(old_dirfd).unwrap();
    close(new_dirfd).unwrap();
    assert!(new_dir.path().join("new").exists());
}

#[test]
fn test_readlink() {
    let tempdir = tempfile::tempdir().unwrap();
    let src = tempdir.path().join("a");
    let dst = tempdir.path().join("b");
    println!("a: {:?}, b: {:?}", &src, &dst);
    fs::symlink(&src.as_path(), &dst.as_path()).unwrap();
    let dirfd = open(tempdir.path(),
                     OFlag::empty(),
                     Mode::empty()).unwrap();
    let expected_dir = src.to_str().unwrap();

    assert_eq!(readlink(&dst).unwrap().to_str().unwrap(), expected_dir);
    assert_eq!(readlinkat(dirfd, "b").unwrap().to_str().unwrap(), expected_dir);

}

#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux_android {
    use std::fs::File;
    use std::io::prelude::*;
    use std::io::{BufRead, BufReader, SeekFrom};
    use std::os::unix::prelude::*;

    use libc::loff_t;

    use nix::fcntl::*;
    use nix::sys::stat::fstat;
    use nix::sys::uio::IoVec;
    use nix::unistd::{close, pipe, read, write};

    use tempfile::{tempfile, NamedTempFile};

    /// This test creates a temporary file containing the contents
    /// 'foobarbaz' and uses the `copy_file_range` call to transfer
    /// 3 bytes at offset 3 (`bar`) to another empty file at offset 0. The
    /// resulting file is read and should contain the contents `bar`.
    /// The from_offset should be updated by the call to reflect
    /// the 3 bytes read (6).
    ///
    /// FIXME: This test is disabled for linux based builds, because Travis
    /// Linux version is too old for `copy_file_range`.
    #[test]
    #[ignore]
    fn test_copy_file_range() {
        const CONTENTS: &[u8] = b"foobarbaz";

        let mut tmp1 = tempfile().unwrap();
        let mut tmp2 = tempfile().unwrap();

        tmp1.write_all(CONTENTS).unwrap();
        tmp1.flush().unwrap();

        let mut from_offset: i64 = 3;
        copy_file_range(
            tmp1.as_raw_fd(),
            Some(&mut from_offset),
            tmp2.as_raw_fd(),
            None,
            3,
        )
        .unwrap();

        let mut res: String = String::new();
        tmp2.seek(SeekFrom::Start(0)).unwrap();
        tmp2.read_to_string(&mut res).unwrap();

        assert_eq!(res, String::from("bar"));
        assert_eq!(from_offset, 6);
    }

    #[test]
    fn test_splice() {
        const CONTENTS: &[u8] = b"abcdef123456";
        let mut tmp = tempfile().unwrap();
        tmp.write_all(CONTENTS).unwrap();

        let (rd, wr) = pipe().unwrap();
        let mut offset: loff_t = 5;
        let res = splice(tmp.as_raw_fd(), Some(&mut offset),
            wr, None, 2, SpliceFFlags::empty()).unwrap();

        assert_eq!(2, res);

        let mut buf = [0u8; 1024];
        assert_eq!(2, read(rd, &mut buf).unwrap());
        assert_eq!(b"f1", &buf[0..2]);
        assert_eq!(7, offset);

        close(rd).unwrap();
        close(wr).unwrap();
    }

    #[test]
    fn test_tee() {
        let (rd1, wr1) = pipe().unwrap();
        let (rd2, wr2) = pipe().unwrap();

        write(wr1, b"abc").unwrap();
        let res = tee(rd1, wr2, 2, SpliceFFlags::empty()).unwrap();

        assert_eq!(2, res);

        let mut buf = [0u8; 1024];

        // Check the tee'd bytes are at rd2.
        assert_eq!(2, read(rd2, &mut buf).unwrap());
        assert_eq!(b"ab", &buf[0..2]);

        // Check all the bytes are still at rd1.
        assert_eq!(3, read(rd1, &mut buf).unwrap());
        assert_eq!(b"abc", &buf[0..3]);

        close(rd1).unwrap();
        close(wr1).unwrap();
        close(rd2).unwrap();
        close(wr2).unwrap();
    }

    #[test]
    fn test_vmsplice() {
        let (rd, wr) = pipe().unwrap();

        let buf1 = b"abcdef";
        let buf2 = b"defghi";
        let mut iovecs = Vec::with_capacity(2);
        iovecs.push(IoVec::from_slice(&buf1[0..3]));
        iovecs.push(IoVec::from_slice(&buf2[0..3]));

        let res = vmsplice(wr, &iovecs[..], SpliceFFlags::empty()).unwrap();

        assert_eq!(6, res);

        // Check the bytes can be read at rd.
        let mut buf = [0u8; 32];
        assert_eq!(6, read(rd, &mut buf).unwrap());
        assert_eq!(b"abcdef", &buf[0..6]);

        close(rd).unwrap();
        close(wr).unwrap();
    }

    #[test]
    fn test_fallocate() {
        let tmp = NamedTempFile::new().unwrap();

        let fd = tmp.as_raw_fd();
        fallocate(fd, FallocateFlags::empty(), 0, 100).unwrap();

        // Check if we read exactly 100 bytes
        let mut buf = [0u8; 200];
        assert_eq!(100, read(fd, &mut buf).unwrap());
    }

    // This test is disabled for the target architectures below
    // due to OFD locks not being available in the kernel/libc
    // versions used in the CI environment.
    #[test]
    #[cfg(not(any(target_arch = "aarch64",
                  target_arch = "arm",
                  target_arch = "armv7",
                  target_arch = "x86",
                  target_arch = "mips",
                  target_arch = "mips64",
                  target_arch = "mips64el",
                  target_arch = "powerpc64",
                  target_arch = "powerpc64le")))]
    fn test_ofd_locks() {
        let tmp = NamedTempFile::new().unwrap();

        let fd = tmp.as_raw_fd();
        let inode = fstat(fd).expect("fstat failed").st_ino as usize;

        let mut flock = libc::flock {
            l_type: libc::F_WRLCK as libc::c_short,
            l_whence: libc::SEEK_SET as libc::c_short,
            l_start: 0,
            l_len: 0,
            l_pid: 0,
        };
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write lock failed");
        assert_eq!(
            Some(("OFDLCK".to_string(), "WRITE".to_string())),
            lock_info(inode)
        );

        flock.l_type = libc::F_UNLCK as libc::c_short;
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write unlock failed");
        assert_eq!(None, lock_info(inode));

        flock.l_type = libc::F_RDLCK as libc::c_short;
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read lock failed");
        assert_eq!(
            Some(("OFDLCK".to_string(), "READ".to_string())),
            lock_info(inode)
        );

        flock.l_type = libc::F_UNLCK as libc::c_short;
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read unlock failed");
        assert_eq!(None, lock_info(inode));
    }

    fn lock_info(inode: usize) -> Option<(String, String)> {
        let file = File::open("/proc/locks").expect("open /proc/locks failed");
        let buf = BufReader::new(file);

        for line in buf.lines() {
            let line = line.unwrap();
            let parts: Vec<_> = line.split_whitespace().collect();
            let lock_type = parts[1];
            let lock_access = parts[3];
            let ino_parts: Vec<_> = parts[5].split(':').collect();
            let ino: usize = ino_parts[2].parse().unwrap();
            if ino == inode {
                return Some((lock_type.to_string(), lock_access.to_string()));
            }
        }
        None
    }
}

#[cfg(any(target_os = "linux",
          target_os = "android",
          target_os = "emscripten",
          target_os = "fuchsia",
          any(target_os = "wasi", target_env = "wasi"),
          target_env = "uclibc",
          target_env = "freebsd"))]
mod test_posix_fadvise {

    use tempfile::NamedTempFile;
    use std::os::unix::io::{RawFd, AsRawFd};
    use nix::errno::Errno;
    use nix::fcntl::*;
    use nix::unistd::pipe;

    #[test]
    fn test_success() {
        let tmp = NamedTempFile::new().unwrap();
        let fd = tmp.as_raw_fd();
        let res = posix_fadvise(fd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED).unwrap();

        assert_eq!(res, 0);
    }

    #[test]
    fn test_errno() {
        let (rd, _wr) = pipe().unwrap();
        let errno = posix_fadvise(rd as RawFd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED)
                                 .unwrap();
        assert_eq!(errno, Errno::ESPIPE as i32);
    }
}

#[cfg(any(target_os = "linux",
          target_os = "android",
          target_os = "emscripten",
          target_os = "fuchsia",
          any(target_os = "wasi", target_env = "wasi"),
          target_os = "freebsd"))]
mod test_posix_fallocate {

    use tempfile::NamedTempFile;
    use std::{io::Read, os::unix::io::{RawFd, AsRawFd}};
    use nix::errno::Errno;
    use nix::fcntl::*;
    use nix::unistd::pipe;

    #[test]
    fn success() {
        const LEN: usize = 100;
        let mut tmp = NamedTempFile::new().unwrap();
        let fd = tmp.as_raw_fd();
        let res = posix_fallocate(fd, 0, LEN as libc::off_t);
        match res {
            Ok(_) => {
                let mut data = [1u8; LEN];
                assert_eq!(tmp.read(&mut data).expect("read failure"), LEN);
                assert_eq!(&data[..], &[0u8; LEN][..]);
            }
            Err(nix::Error::Sys(Errno::EINVAL)) => {
                // POSIX requires posix_fallocate to return EINVAL both for
                // invalid arguments (i.e. len < 0) and if the operation is not
                // supported by the file system.
                // There's no way to tell for sure whether the file system
                // supports posix_fallocate, so we must pass the test if it
                // returns EINVAL.
            }
            _ => res.unwrap(),
        }
    }

    #[test]
    fn errno() {
        let (rd, _wr) = pipe().unwrap();
        let err = posix_fallocate(rd as RawFd, 0, 100).unwrap_err();
        use nix::Error::Sys;
        match err {
            Sys(Errno::EINVAL)
                | Sys(Errno::ENODEV)
                | Sys(Errno::ESPIPE)
                | Sys(Errno::EBADF) => (),
            errno =>
                panic!(
                    "unexpected errno {}",
                    errno,
                ),
        }
    }
}
<commit_msg>Split read and write OFD lock tests<commit_after>use nix::Error;
use nix::errno::*;
use nix::fcntl::{openat, open, OFlag, readlink, readlinkat, renameat};
use nix::sys::stat::Mode;
use nix::unistd::{close, read};
use tempfile::{self, NamedTempFile};
use std::fs::File;
use std::io::prelude::*;
use std::os::unix::fs;

#[test]
fn test_openat() {
    const CONTENTS: &[u8] = b"abcd";
    let mut tmp = NamedTempFile::new().unwrap();
    tmp.write_all(CONTENTS).unwrap();

    let dirfd = open(tmp.path().parent().unwrap(),
                     OFlag::empty(),
                     Mode::empty()).unwrap();
    let fd = openat(dirfd,
                    tmp.path().file_name().unwrap(),
                    OFlag::O_RDONLY,
                    Mode::empty()).unwrap();

    let mut buf = [0u8; 1024];
    assert_eq!(4, read(fd, &mut buf).unwrap());
    assert_eq!(CONTENTS, &buf[0..4]);

    close(fd).unwrap();
    close(dirfd).unwrap();
}

#[test]
fn test_renameat() {
    let old_dir = tempfile::tempdir().unwrap();
    let old_dirfd = open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
    let old_path = old_dir.path().join("old");
    File::create(&old_path).unwrap();
    let new_dir = tempfile::tempdir().unwrap();
    let new_dirfd = open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
    renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap();
    assert_eq!(renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap_err(),
               Error::Sys(Errno::ENOENT));
    close(old_dirfd).unwrap();
    close(new_dirfd).unwrap();
    assert!(new_dir.path().join("new").exists());
}

#[test]
fn test_readlink() {
    let tempdir = tempfile::tempdir().unwrap();
    let src = tempdir.path().join("a");
    let dst = tempdir.path().join("b");
    println!("a: {:?}, b: {:?}", &src, &dst);
    fs::symlink(&src.as_path(), &dst.as_path()).unwrap();
    let dirfd = open(tempdir.path(),
                     OFlag::empty(),
                     Mode::empty()).unwrap();
    let expected_dir = src.to_str().unwrap();

    assert_eq!(readlink(&dst).unwrap().to_str().unwrap(), expected_dir);
    assert_eq!(readlinkat(dirfd, "b").unwrap().to_str().unwrap(), expected_dir);

}

#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux_android {
    use std::fs::File;
    use std::io::prelude::*;
    use std::io::{BufRead, BufReader, SeekFrom};
    use std::os::unix::prelude::*;

    use libc::loff_t;

    use nix::fcntl::*;
    use nix::sys::stat::fstat;
    use nix::sys::uio::IoVec;
    use nix::unistd::{close, pipe, read, write};

    use tempfile::{tempfile, NamedTempFile};

    /// This test creates a temporary file containing the contents
    /// 'foobarbaz' and uses the `copy_file_range` call to transfer
    /// 3 bytes at offset 3 (`bar`) to another empty file at offset 0. The
    /// resulting file is read and should contain the contents `bar`.
    /// The from_offset should be updated by the call to reflect
    /// the 3 bytes read (6).
    ///
    /// FIXME: This test is disabled for linux based builds, because Travis
    /// Linux version is too old for `copy_file_range`.
    #[test]
    #[ignore]
    fn test_copy_file_range() {
        const CONTENTS: &[u8] = b"foobarbaz";

        let mut tmp1 = tempfile().unwrap();
        let mut tmp2 = tempfile().unwrap();

        tmp1.write_all(CONTENTS).unwrap();
        tmp1.flush().unwrap();

        let mut from_offset: i64 = 3;
        copy_file_range(
            tmp1.as_raw_fd(),
            Some(&mut from_offset),
            tmp2.as_raw_fd(),
            None,
            3,
        )
        .unwrap();

        let mut res: String = String::new();
        tmp2.seek(SeekFrom::Start(0)).unwrap();
        tmp2.read_to_string(&mut res).unwrap();

        assert_eq!(res, String::from("bar"));
        assert_eq!(from_offset, 6);
    }

    #[test]
    fn test_splice() {
        const CONTENTS: &[u8] = b"abcdef123456";
        let mut tmp = tempfile().unwrap();
        tmp.write_all(CONTENTS).unwrap();

        let (rd, wr) = pipe().unwrap();
        let mut offset: loff_t = 5;
        let res = splice(tmp.as_raw_fd(), Some(&mut offset),
            wr, None, 2, SpliceFFlags::empty()).unwrap();

        assert_eq!(2, res);

        let mut buf = [0u8; 1024];
        assert_eq!(2, read(rd, &mut buf).unwrap());
        assert_eq!(b"f1", &buf[0..2]);
        assert_eq!(7, offset);

        close(rd).unwrap();
        close(wr).unwrap();
    }

    #[test]
    fn test_tee() {
        let (rd1, wr1) = pipe().unwrap();
        let (rd2, wr2) = pipe().unwrap();

        write(wr1, b"abc").unwrap();
        let res = tee(rd1, wr2, 2, SpliceFFlags::empty()).unwrap();

        assert_eq!(2, res);

        let mut buf = [0u8; 1024];

        // Check the tee'd bytes are at rd2.
        assert_eq!(2, read(rd2, &mut buf).unwrap());
        assert_eq!(b"ab", &buf[0..2]);

        // Check all the bytes are still at rd1.
        assert_eq!(3, read(rd1, &mut buf).unwrap());
        assert_eq!(b"abc", &buf[0..3]);

        close(rd1).unwrap();
        close(wr1).unwrap();
        close(rd2).unwrap();
        close(wr2).unwrap();
    }

    #[test]
    fn test_vmsplice() {
        let (rd, wr) = pipe().unwrap();

        let buf1 = b"abcdef";
        let buf2 = b"defghi";
        let mut iovecs = Vec::with_capacity(2);
        iovecs.push(IoVec::from_slice(&buf1[0..3]));
        iovecs.push(IoVec::from_slice(&buf2[0..3]));

        let res = vmsplice(wr, &iovecs[..], SpliceFFlags::empty()).unwrap();

        assert_eq!(6, res);

        // Check the bytes can be read at rd.
        let mut buf = [0u8; 32];
        assert_eq!(6, read(rd, &mut buf).unwrap());
        assert_eq!(b"abcdef", &buf[0..6]);

        close(rd).unwrap();
        close(wr).unwrap();
    }

    #[test]
    fn test_fallocate() {
        let tmp = NamedTempFile::new().unwrap();

        let fd = tmp.as_raw_fd();
        fallocate(fd, FallocateFlags::empty(), 0, 100).unwrap();

        // Check if we read exactly 100 bytes
        let mut buf = [0u8; 200];
        assert_eq!(100, read(fd, &mut buf).unwrap());
    }

    // The tests below are disabled for the listed targets
    // due to OFD locks not being available in the kernel/libc
    // versions used in the CI environment, probably because
    // they run under QEMU.

    #[test]
    #[cfg(not(any(target_arch = "aarch64",
                  target_arch = "arm",
                  target_arch = "armv7",
                  target_arch = "x86",
                  target_arch = "mips",
                  target_arch = "mips64",
                  target_arch = "mips64el",
                  target_arch = "powerpc64",
                  target_arch = "powerpc64le")))]
    fn test_ofd_write_lock() {
        let tmp = NamedTempFile::new().unwrap();

        let fd = tmp.as_raw_fd();
        let inode = fstat(fd).expect("fstat failed").st_ino as usize;

        let mut flock = libc::flock {
            l_type: libc::F_WRLCK as libc::c_short,
            l_whence: libc::SEEK_SET as libc::c_short,
            l_start: 0,
            l_len: 0,
            l_pid: 0,
        };
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write lock failed");
        assert_eq!(
            Some(("OFDLCK".to_string(), "WRITE".to_string())),
            lock_info(inode)
        );

        flock.l_type = libc::F_UNLCK as libc::c_short;
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write unlock failed");
        assert_eq!(None, lock_info(inode));
    }

    #[test]
    #[cfg(not(any(target_arch = "aarch64",
                  target_arch = "arm",
                  target_arch = "armv7",
                  target_arch = "x86",
                  target_arch = "mips",
                  target_arch = "mips64",
                  target_arch = "mips64el",
                  target_arch = "powerpc64",
                  target_arch = "powerpc64le")))]
    fn test_ofd_read_lock() {
        let tmp = NamedTempFile::new().unwrap();

        let fd = tmp.as_raw_fd();
        let inode = fstat(fd).expect("fstat failed").st_ino as usize;

        let mut flock = libc::flock {
            l_type: libc::F_RDLCK as libc::c_short,
            l_whence: libc::SEEK_SET as libc::c_short,
            l_start: 0,
            l_len: 0,
            l_pid: 0,
        };
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read lock failed");
        assert_eq!(
            Some(("OFDLCK".to_string(), "READ".to_string())),
            lock_info(inode)
        );

        flock.l_type = libc::F_UNLCK as libc::c_short;
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read unlock failed");
        assert_eq!(None, lock_info(inode));
    }

    fn lock_info(inode: usize) -> Option<(String, String)> {
        let file = File::open("/proc/locks").expect("open /proc/locks failed");
        let buf = BufReader::new(file);

        for line in buf.lines() {
            let line = line.unwrap();
            let parts: Vec<_> = line.split_whitespace().collect();
            let lock_type = parts[1];
            let lock_access = parts[3];
            let ino_parts: Vec<_> = parts[5].split(':').collect();
            let ino: usize = ino_parts[2].parse().unwrap();
            if ino == inode {
                return Some((lock_type.to_string(), lock_access.to_string()));
            }
        }
        None
    }
}

#[cfg(any(target_os = "linux",
          target_os = "android",
          target_os = "emscripten",
          target_os = "fuchsia",
          any(target_os = "wasi", target_env = "wasi"),
          target_env = "uclibc",
          target_env = "freebsd"))]
mod test_posix_fadvise {

    use tempfile::NamedTempFile;
    use std::os::unix::io::{RawFd, AsRawFd};
    use nix::errno::Errno;
    use nix::fcntl::*;
    use nix::unistd::pipe;

    #[test]
    fn test_success() {
        let tmp = NamedTempFile::new().unwrap();
        let fd = tmp.as_raw_fd();
        let res = posix_fadvise(fd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED).unwrap();

        assert_eq!(res, 0);
    }

    #[test]
    fn test_errno() {
        let (rd, _wr) = pipe().unwrap();
        let errno = posix_fadvise(rd as RawFd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED)
                                 .unwrap();
        assert_eq!(errno, Errno::ESPIPE as i32);
    }
}

#[cfg(any(target_os = "linux",
          target_os = "android",
          target_os = "emscripten",
          target_os = "fuchsia",
          any(target_os = "wasi", target_env = "wasi"),
          target_os = "freebsd"))]
mod test_posix_fallocate {

    use tempfile::NamedTempFile;
    use std::{io::Read, os::unix::io::{RawFd, AsRawFd}};
    use nix::errno::Errno;
    use nix::fcntl::*;
    use nix::unistd::pipe;

    #[test]
    fn success() {
        const LEN: usize = 100;
        let mut tmp = NamedTempFile::new().unwrap();
        let fd = tmp.as_raw_fd();
        let res = posix_fallocate(fd, 0, LEN as libc::off_t);
        match res {
            Ok(_) => {
                let mut data = [1u8; LEN];
                assert_eq!(tmp.read(&mut data).expect("read failure"), LEN);
                assert_eq!(&data[..], &[0u8; LEN][..]);
            }
            Err(nix::Error::Sys(Errno::EINVAL)) => {
                // POSIX requires posix_fallocate to return EINVAL both for
                // invalid arguments (i.e. len < 0) and if the operation is not
                // supported by the file system.
                // There's no way to tell for sure whether the file system
                // supports posix_fallocate, so we must pass the test if it
                // returns EINVAL.
            }
            _ => res.unwrap(),
        }
    }

    #[test]
    fn errno() {
        let (rd, _wr) = pipe().unwrap();
        let err = posix_fallocate(rd as RawFd, 0, 100).unwrap_err();
        use nix::Error::Sys;
        match err {
            Sys(Errno::EINVAL)
                | Sys(Errno::ENODEV)
                | Sys(Errno::ESPIPE)
                | Sys(Errno::EBADF) => (),
            errno =>
                panic!(
                    "unexpected errno {}",
                    errno,
                ),
        }
    }
}
<|endoftext|>