<commit_before>#![macro_escape]
macro_rules! stream_module (($stream_name:ident,
                             $xor_name:ident,
                             $keybytes:expr,
                             $noncebytes:expr) => (

#[link(name = "sodium")]
extern {
    fn $stream_name(c: *mut u8,
                    clen: c_ulonglong,
                    n: *const u8,
                    k: *const u8) -> c_int;
    fn $xor_name(c: *mut u8,
                 m: *const u8,
                 mlen: c_ulonglong,
                 n: *const u8,
                 k: *const u8) -> c_int;
}

pub const KEYBYTES: uint = $keybytes;
pub const NONCEBYTES: uint = $noncebytes;

/**
 * `Key` for symmetric encryption
 *
 * When a `Key` goes out of scope its contents
 * will be zeroed out
 */
pub struct Key(pub [u8, ..KEYBYTES]);
impl Drop for Key {
    fn drop(&mut self) {
        let &Key(ref mut k) = self;
        unsafe {
            volatile_set_memory(k.as_mut_ptr(), 0, k.len())
        }
    }
}

/**
 * `Nonce` for symmetric encryption
 */
pub struct Nonce(pub [u8, ..NONCEBYTES]);

/**
 * `gen_key()` randomly generates a key for symmetric encryption
 *
 * THREAD SAFETY: `gen_key()` is thread-safe provided that you have
 * called `sodiumoxide::init()` once before using any other function
 * from sodiumoxide.
 */
pub fn gen_key() -> Key {
    let mut key = [0, ..KEYBYTES];
    randombytes_into(key);
    Key(key)
}

/**
 * `gen_nonce()` randomly generates a nonce for symmetric encryption
 *
 * THREAD SAFETY: `gen_nonce()` is thread-safe provided that you have
 * called `sodiumoxide::init()` once before using any other function
 * from sodiumoxide.
 *
 * NOTE: When using primitives with short nonces (e.g. salsa20, salsa208, salsa2012)
 * do not use random nonces since the probability of nonce-collision is not negligible
 */
pub fn gen_nonce() -> Nonce {
    let mut nonce = [0, ..NONCEBYTES];
    randombytes_into(nonce);
    Nonce(nonce)
}

/**
 * `stream()` produces a `len`-byte stream `c` as a function of a
 * secret key `k` and a nonce `n`.
 */
pub fn stream(len: uint,
              &Nonce(n): &Nonce,
              &Key(k): &Key) -> Vec<u8> {
    unsafe {
        let mut c = Vec::from_elem(len, 0u8);
        $stream_name(c.as_mut_ptr(),
                     c.len() as c_ulonglong,
                     n.as_ptr(),
                     k.as_ptr());
        c
    }
}

/**
 * `stream_xor()` encrypts a message `m` using a secret key `k` and a nonce `n`.
 * The `stream_xor()` function returns the ciphertext `c`.
 *
 * `stream_xor()` guarantees that the ciphertext has the same length as the plaintext,
 * and is the plaintext xor the output of `stream()`.
 * Consequently `stream_xor()` can also be used to decrypt.
 */
pub fn stream_xor(m: &[u8],
                  &Nonce(n): &Nonce,
                  &Key(k): &Key) -> Vec<u8> {
    unsafe {
        let mut c = Vec::from_elem(m.len(), 0u8);
        $xor_name(c.as_mut_ptr(),
                  m.as_ptr(),
                  m.len() as c_ulonglong,
                  n.as_ptr(),
                  k.as_ptr());
        c
    }
}

/**
* `stream_xor_inplace` encrypts a message `m` using a secret key `k` and a nonce `n`.
* The `stream_xor_inplace()` function encrypts the message in place.
*
* `stream_xor_inplace()` guarantees that the ciphertext has the same length as
* the plaintext, and is the plaintext xor the output of `stream_inplace()`.
* Consequently `stream_xor_inplace()` can also be used to decrypt.
*/
pub fn stream_xor_inplace(m: &mut [u8],
                          &Nonce(n): &Nonce,
                          &Key(k): &Key) {
    unsafe {
        $xor_name(m.as_mut_ptr(),
                  m.as_ptr(),
                  m.len() as c_ulonglong,
                  n.as_ptr(),
                  k.as_ptr());
    }
}

#[test]
fn test_encrypt_decrypt() {
    use randombytes::randombytes;
    for i in range(0, 1024u) {
        let k = gen_key();
        let n = gen_nonce();
        let m = randombytes(i);
        let c = stream_xor(m.as_slice(), &n, &k);
        let m2 = stream_xor(c.as_slice(), &n, &k);
        assert!(m == m2);
    }
}

#[test]
fn test_stream_xor() {
    use randombytes::randombytes;
    for i in range(0, 1024u) {
        let k = gen_key();
        let n = gen_nonce();
        let m = randombytes(i);
        let mut c = m.clone();
        let s = stream(c.len(), &n, &k);
        for (e, v) in c.mut_iter().zip(s.iter()) {
            *e ^= *v;
        }
        let c2 = stream_xor(m.as_slice(), &n, &k);
        assert!(c == c2);
    }
}

#[test]
fn test_stream_xor_inplace() {
    use randombytes::randombytes;
    for i in range(0, 1024u) {
        let k = gen_key();
        let n = gen_nonce();
        let mut m = randombytes(i);
        let mut c = m.clone();
        let s = stream(c.len(), &n, &k);
        for (e, v) in c.mut_iter().zip(s.iter()) {
            *e ^= *v;
        }
        stream_xor_inplace(m.as_mut_slice(), &n, &k);
        assert!(c == m);
    }
}

))
<commit_msg>fix test build<commit_after>#![macro_escape]
macro_rules! stream_module (($stream_name:ident,
                             $xor_name:ident,
                             $keybytes:expr,
                             $noncebytes:expr) => (

#[link(name = "sodium")]
extern {
    fn $stream_name(c: *mut u8,
                    clen: c_ulonglong,
                    n: *const u8,
                    k: *const u8) -> c_int;
    fn $xor_name(c: *mut u8,
                 m: *const u8,
                 mlen: c_ulonglong,
                 n: *const u8,
                 k: *const u8) -> c_int;
}

pub const KEYBYTES: uint = $keybytes;
pub const NONCEBYTES: uint = $noncebytes;

/**
 * `Key` for symmetric encryption
 *
 * When a `Key` goes out of scope its contents
 * will be zeroed out
 */
pub struct Key(pub [u8, ..KEYBYTES]);
impl Drop for Key {
    fn drop(&mut self) {
        let &Key(ref mut k) = self;
        unsafe {
            volatile_set_memory(k.as_mut_ptr(), 0, k.len())
        }
    }
}

/**
 * `Nonce` for symmetric encryption
 */
pub struct Nonce(pub [u8, ..NONCEBYTES]);

/**
 * `gen_key()` randomly generates a key for symmetric encryption
 *
 * THREAD SAFETY: `gen_key()` is thread-safe provided that you have
 * called `sodiumoxide::init()` once before using any other function
 * from sodiumoxide.
 */
pub fn gen_key() -> Key {
    let mut key = [0, ..KEYBYTES];
    randombytes_into(key);
    Key(key)
}

/**
 * `gen_nonce()` randomly generates a nonce for symmetric encryption
 *
 * THREAD SAFETY: `gen_nonce()` is thread-safe provided that you have
 * called `sodiumoxide::init()` once before using any other function
 * from sodiumoxide.
 *
 * NOTE: When using primitives with short nonces (e.g. salsa20, salsa208, salsa2012)
 * do not use random nonces since the probability of nonce-collision is not negligible
 */
pub fn gen_nonce() -> Nonce {
    let mut nonce = [0, ..NONCEBYTES];
    randombytes_into(nonce);
    Nonce(nonce)
}

/**
 * `stream()` produces a `len`-byte stream `c` as a function of a
 * secret key `k` and a nonce `n`.
 */
pub fn stream(len: uint,
              &Nonce(n): &Nonce,
              &Key(k): &Key) -> Vec<u8> {
    unsafe {
        let mut c = Vec::from_elem(len, 0u8);
        $stream_name(c.as_mut_ptr(),
                     c.len() as c_ulonglong,
                     n.as_ptr(),
                     k.as_ptr());
        c
    }
}

/**
 * `stream_xor()` encrypts a message `m` using a secret key `k` and a nonce `n`.
 * The `stream_xor()` function returns the ciphertext `c`.
 *
 * `stream_xor()` guarantees that the ciphertext has the same length as the plaintext,
 * and is the plaintext xor the output of `stream()`.
 * Consequently `stream_xor()` can also be used to decrypt.
 */
pub fn stream_xor(m: &[u8],
                  &Nonce(n): &Nonce,
                  &Key(k): &Key) -> Vec<u8> {
    unsafe {
        let mut c = Vec::from_elem(m.len(), 0u8);
        $xor_name(c.as_mut_ptr(),
                  m.as_ptr(),
                  m.len() as c_ulonglong,
                  n.as_ptr(),
                  k.as_ptr());
        c
    }
}

/**
* `stream_xor_inplace` encrypts a message `m` using a secret key `k` and a nonce `n`.
* The `stream_xor_inplace()` function encrypts the message in place.
*
* `stream_xor_inplace()` guarantees that the ciphertext has the same length as
* the plaintext, and is the plaintext xor the output of `stream_inplace()`.
* Consequently `stream_xor_inplace()` can also be used to decrypt.
*/
pub fn stream_xor_inplace(m: &mut [u8],
                          &Nonce(n): &Nonce,
                          &Key(k): &Key) {
    unsafe {
        $xor_name(m.as_mut_ptr(),
                  m.as_ptr(),
                  m.len() as c_ulonglong,
                  n.as_ptr(),
                  k.as_ptr());
    }
}

#[test]
fn test_encrypt_decrypt() {
    use randombytes::randombytes;
    for i in range(0, 1024u) {
        let k = gen_key();
        let n = gen_nonce();
        let m = randombytes(i);
        let c = stream_xor(m.as_slice(), &n, &k);
        let m2 = stream_xor(c.as_slice(), &n, &k);
        assert!(m == m2);
    }
}

#[test]
fn test_stream_xor() {
    use randombytes::randombytes;
    for i in range(0, 1024u) {
        let k = gen_key();
        let n = gen_nonce();
        let m = randombytes(i);
        let mut c = m.clone();
        let s = stream(c.len(), &n, &k);
        for (e, v) in c.iter_mut().zip(s.iter()) {
            *e ^= *v;
        }
        let c2 = stream_xor(m.as_slice(), &n, &k);
        assert!(c == c2);
    }
}

#[test]
fn test_stream_xor_inplace() {
    use randombytes::randombytes;
    for i in range(0, 1024u) {
        let k = gen_key();
        let n = gen_nonce();
        let mut m = randombytes(i);
        let mut c = m.clone();
        let s = stream(c.len(), &n, &k);
        for (e, v) in c.iter_mut().zip(s.iter()) {
            *e ^= *v;
        }
        stream_xor_inplace(m.as_mut_slice(), &n, &k);
        assert!(c == m);
    }
}

))
<|endoftext|>


<commit_before>use std::io::{Listener, Acceptor};
use std::io::net::tcp::TcpListener;

fn main() {
    let message = "{\"description\":\"Server is offline\",\"players\":{\"max\":0,\"online\":0},\"version\":{\"name\":\"1.8\",\"protocol\":47}}";
    let length = message.len().to_u16().unwrap();

    let mut acceptor = TcpListener::bind("0.0.0.0", 25565).listen().unwrap();
    println!("listening started, ready to accept");
    for opt_stream in acceptor.incoming() {
        spawn(proc() {
            let mut stream = opt_stream.unwrap();
            let ip = stream.peer_name().unwrap().ip;
            let cmd = stream.read_byte().unwrap();
            match cmd {
                0x0F => {
                    let request = stream.read_exact(15).unwrap();
                    match request[14] {
                        // Query
                        1 => {
                            stream.read_exact(2).unwrap();
                            let mut response = Vec::new();
                            response.push(length.to_u8().unwrap() + 2);
                            response.push((length >> 8).to_u8().unwrap());
                            response.push((length & 0xFF).to_u8().unwrap());
                            response.push_all(message.as_bytes());
                            stream.write(response.as_slice()).unwrap();

                            let ping = stream.read_exact(10).unwrap();
                            stream.write(ping.as_slice()).unwrap();
                        },
                        // Login
                        2 => {
                            stream.read_exact(2).unwrap();
                            let size = stream.read_byte().unwrap().to_uint().unwrap();
                            let name_vector = stream.read_exact(size).unwrap();
                            let name = std::str::from_utf8(name_vector.as_slice()).unwrap();
                            println!("Connection from {} ({})", name, ip);
                        },
                        _ => {}
                    }
                },

                // Everything else
                _ => { println!("Unknown packet {}", cmd); }
            }
        })
    }
}
<commit_msg>Print host and port when starting up<commit_after>use std::io::{Listener, Acceptor};
use std::io::net::tcp::TcpListener;

static HOST:&'static str = "0.0.0.0";
static PORT:u16          = 25565;

fn main() {
    let message = "{\"description\":\"Server is offline\",\"players\":{\"max\":0,\"online\":0},\"version\":{\"name\":\"1.8\",\"protocol\":47}}";
    let length = message.len().to_u16().unwrap();

    let mut acceptor = TcpListener::bind(HOST, PORT).listen().unwrap();
    println!("Listening on {}:{}", HOST, PORT);
    for opt_stream in acceptor.incoming() {
        spawn(proc() {
            let mut stream = opt_stream.unwrap();
            let ip = stream.peer_name().unwrap().ip;
            let cmd = stream.read_byte().unwrap();
            match cmd {
                0x0F => {
                    let request = stream.read_exact(15).unwrap();
                    match request[14] {
                        // Query
                        1 => {
                            stream.read_exact(2).unwrap();
                            let mut response = Vec::new();
                            response.push(length.to_u8().unwrap() + 2);
                            response.push((length >> 8).to_u8().unwrap());
                            response.push((length & 0xFF).to_u8().unwrap());
                            response.push_all(message.as_bytes());
                            stream.write(response.as_slice()).unwrap();

                            let ping = stream.read_exact(10).unwrap();
                            stream.write(ping.as_slice()).unwrap();
                        },
                        // Login
                        2 => {
                            stream.read_exact(2).unwrap();
                            let size = stream.read_byte().unwrap().to_uint().unwrap();
                            let name_vector = stream.read_exact(size).unwrap();
                            let name = std::str::from_utf8(name_vector.as_slice()).unwrap();
                            println!("Connection from {} ({})", name, ip);
                        },
                        _ => {}
                    }
                },

                // Everything else
                _ => { println!("Unknown packet {}", cmd); }
            }
        })
    }
}
<|endoftext|>


<commit_before>// error-pattern: unimplemented logging type

fn main() {
  log main;
}

<commit_msg>Remove log-type-error; everything is loggable.<commit_after><|endoftext|>


<commit_before>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// xfail-win32 Broken because of LLVM bug: http://llvm.org/bugs/show_bug.cgi?id=16249

// compile-flags:-Z extra-debug-info
// debugger:break zzz
// debugger:run
// debugger:finish

// debugger:print some
// check:$1 = {0x12345678}

// debugger:print none
// check:$2 = {0x0}

// debugger:print full
// check:$3 = {454545, 0x87654321, 9988}

// debugger:print empty
// check:$4 = {0, 0x0, 0}

// debugger:print droid
// check:$5 = {id = 675675, range = 10000001, internals = 0x43218765}

// debugger:print void_droid
// check:$6 = {id = 0, range = 0, internals = 0x0}

// If a struct has exactly two variants, one of them is empty, and the other one
// contains a non-nullable pointer, then this value is used as the discriminator.
// The test cases in this file make sure that something readable is generated for
// this kind of types.

enum MoreFields<'self> {
    Full(u32, &'self int, i16),
    Empty
}

enum NamedFields<'self> {
    Droid { id: i32, range: i64, internals: &'self int },
    Void
}

fn main() {

    let some: Option<&u32> = Some(unsafe { std::cast::transmute(0x12345678) });
    let none: Option<&u32> = None;

    let full = Full(454545, unsafe { std::cast::transmute(0x87654321) }, 9988);

    let int_val = 0;
    let mut empty = Full(0, &int_val, 0);
    empty = Empty;

    let droid = Droid { id: 675675, range: 10000001, internals: unsafe { std::cast::transmute(0x43218765) } };

    let mut void_droid = Droid { id: 0, range: 0, internals: &int_val };
    void_droid = Void;

    zzz();
}

fn zzz() {()}<commit_msg>xfail debug-info/option-like-enum<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// xfail-win32 Broken because of LLVM bug: http://llvm.org/bugs/show_bug.cgi?id=16249
// xfail-test broken in newrt?

// compile-flags:-Z extra-debug-info
// debugger:break zzz
// debugger:run
// debugger:finish

// debugger:print some
// check:$1 = {0x12345678}

// debugger:print none
// check:$2 = {0x0}

// debugger:print full
// check:$3 = {454545, 0x87654321, 9988}

// debugger:print empty
// check:$4 = {0, 0x0, 0}

// debugger:print droid
// check:$5 = {id = 675675, range = 10000001, internals = 0x43218765}

// debugger:print void_droid
// check:$6 = {id = 0, range = 0, internals = 0x0}

// If a struct has exactly two variants, one of them is empty, and the other one
// contains a non-nullable pointer, then this value is used as the discriminator.
// The test cases in this file make sure that something readable is generated for
// this kind of types.

enum MoreFields<'self> {
    Full(u32, &'self int, i16),
    Empty
}

enum NamedFields<'self> {
    Droid { id: i32, range: i64, internals: &'self int },
    Void
}

fn main() {

    let some: Option<&u32> = Some(unsafe { std::cast::transmute(0x12345678) });
    let none: Option<&u32> = None;

    let full = Full(454545, unsafe { std::cast::transmute(0x87654321) }, 9988);

    let int_val = 0;
    let mut empty = Full(0, &int_val, 0);
    empty = Empty;

    let droid = Droid { id: 675675, range: 10000001, internals: unsafe { std::cast::transmute(0x43218765) } };

    let mut void_droid = Droid { id: 0, range: 0, internals: &int_val };
    void_droid = Void;

    zzz();
}

fn zzz() {()}<|endoftext|>


<commit_before>extern crate gstreamer as gst;
extern crate gstreamer_audio as gst_audio;

use std::any::Any;

use std::mem;

use std::sync::{Arc, Mutex};

use super::{AudioBuffer, AudioChannel, SampleExtractor};

const EXTRACTION_THRESHOLD: usize = 1024;

// The DoubleBuffer is reponsible for ensuring a thread safe double buffering
// mechanism that receives samples from GStreamer, prepares an extraction of
// these samples and presents the most recent extraction to an external
// mechanism (e.g. UI).
// The DoubleBuffer hosts two SampleExtractors and an AudioBuffer:
//   - The SampleExtractor trait is designed to allow the extraction of samples
//   depending on certains conditions as defined in its concrete implementation.
//   - The AudioBuffer manages the container for all the samples received.
// The DoubleBuffer prepares the extraction in the working_buffer and exposes
// the exposed_buffer during that time. When the extration is done, the buffers
// are swapped.
pub struct DoubleAudioBuffer {
    audio_buffer: AudioBuffer,
    samples_since_last_extract: usize,
    exposed_buffer_mtx: Arc<Mutex<Box<SampleExtractor>>>,
    working_buffer: Option<Box<SampleExtractor>>,
    lower_to_keep: usize,
}

impl DoubleAudioBuffer {
    // need 2 arguments for new as we can't clone buffers as they are known
    // as trait SampleExtractor
    pub fn new(
        buffer_duration: u64,
        exposed_buffer: Box<SampleExtractor>,
        working_buffer: Box<SampleExtractor>,
    ) -> DoubleAudioBuffer {
        DoubleAudioBuffer {
            audio_buffer: AudioBuffer::new(buffer_duration),
            samples_since_last_extract: 0,
            exposed_buffer_mtx: Arc::new(Mutex::new(exposed_buffer)),
            working_buffer: Some(working_buffer),
            lower_to_keep: 0,
        }
    }

    // Get a reference on the exposed buffer mutex.
    pub fn get_exposed_buffer_mtx(&self) -> Arc<Mutex<Box<SampleExtractor>>> {
        Arc::clone(&self.exposed_buffer_mtx)
    }

    pub fn cleanup(&mut self) {
        self.audio_buffer.cleanup();
        self.samples_since_last_extract = 0;

        {
            let exposed_buffer = &mut self.exposed_buffer_mtx.lock().expect(
                "DoubleAudioBuffer: couldn't lock exposed_buffer_mtx while setting audio sink",
            );
            exposed_buffer.cleanup();
        }

        self.working_buffer
            .as_mut()
            .expect("DoubleAudioBuffer: couldn't get working_buffer while setting audio sink")
            .cleanup();
        self.lower_to_keep = 0;
    }

    // Initialize buffer with audio stream capabilities
    // and GStreamer element for position reference
    pub fn set_audio_caps_and_ref(&mut self, caps: &gst::Caps, audio_ref: &gst::Element) {
        let audio_info = gst_audio::AudioInfo::from_caps(caps)
            .expect("DoubleAudioBuffer::set_audio_caps_and_ref unable to get AudioInfo");

        let rate = u64::from(audio_info.rate());
        let channels = audio_info.channels() as usize;
        self.audio_buffer.init(rate, channels);

        let sample_duration = 1_000_000_000 / rate;
        let duration_for_1000_samples = 1_000_000_000_000f64 / (rate as f64);

        let mut channels: Vec<AudioChannel> = Vec::with_capacity(channels);
        if let Some(positions) = audio_info.positions() {
            for position in positions {
                channels.push(AudioChannel::new(position));
            }
        };

        {
            let exposed_buffer = &mut self.exposed_buffer_mtx.lock().expect(
                "DoubleAudioBuffer: couldn't lock exposed_buffer_mtx while setting audio sink",
            );
            exposed_buffer.set_audio_sink(audio_ref.clone());
            exposed_buffer.set_sample_duration(sample_duration, duration_for_1000_samples);
            exposed_buffer.set_channels(&channels);
        }

        let working_buffer = self.working_buffer
            .as_mut()
            .expect("DoubleAudioBuffer: couldn't get working_buffer while setting audio sink");
        working_buffer.set_audio_sink(audio_ref.clone());
        working_buffer.set_sample_duration(sample_duration, duration_for_1000_samples);
        working_buffer.set_channels(&channels);
    }

    pub fn handle_eos(&mut self) {
        self.audio_buffer.handle_eos();
        // extract last samples and swap
        self.extract_samples();
    }

    pub fn push_gst_sample(&mut self, sample: gst::Sample) {
        // store incoming samples
        self.samples_since_last_extract += self.audio_buffer
            .push_gst_sample(sample, self.lower_to_keep);

        if self.samples_since_last_extract >= EXTRACTION_THRESHOLD {
            // extract new samples and swap
            self.extract_samples();
            self.samples_since_last_extract = 0;
        }
    }

    // Update the working extractor with new samples and swap.
    pub fn extract_samples(&mut self) {
        let mut working_buffer = self.working_buffer
            .take()
            .expect("DoubleAudioBuffer::extract_samples: failed to take working buffer");
        working_buffer.extract_samples(&self.audio_buffer);

        // swap buffers
        {
            let exposed_buffer_box = &mut *self.exposed_buffer_mtx
                .lock()
                .expect("DoubleAudioBuffer::extract_samples: failed to lock the exposed buffer");
            // get latest state from the previously exposed buffer
            // in order to smoothen rendering between frames
            working_buffer.update_concrete_state(exposed_buffer_box.as_mut());
            mem::swap(exposed_buffer_box, &mut working_buffer);
        }

        self.lower_to_keep = working_buffer.get_lower();

        self.working_buffer = Some(working_buffer);
        // self.working_buffer is now the buffer previously in
        // self.exposed_buffer_mtx
    }

    pub fn refresh(&mut self) {
        // refresh with current conditions
        let mut working_buffer = self.working_buffer
            .take()
            .expect("DoubleAudioBuffer::refresh: failed to take working buffer");

        {
            let exposed_buffer_box = &mut *self.exposed_buffer_mtx
                .lock()
                .expect("DoubleAudioBuffer:::refresh: failed to lock the exposed buffer");
            // get latest state from the previously exposed buffer
            working_buffer.update_concrete_state(exposed_buffer_box.as_mut());

            // refresh working buffer
            working_buffer.refresh(&self.audio_buffer);

            // swap buffers
            mem::swap(exposed_buffer_box, &mut working_buffer);
        }

        self.lower_to_keep = working_buffer.get_lower();

        self.working_buffer = Some(working_buffer);
        // self.working_buffer is now the buffer previously in
        // self.exposed_buffer_mtx
    }

    // Refresh the working buffer with the provided conditions and swap.
    // Conditions concrete type must conform to a struct expected
    // by the concrete implementation of the SampleExtractor.
    pub fn refresh_with_conditions<T: Any + Clone>(&mut self, conditions: Box<T>) {
        let mut working_buffer = self.working_buffer
            .take()
            .expect("DoubleAudioBuffer::refresh: failed to take working buffer");

        {
            let exposed_buffer_box = &mut *self.exposed_buffer_mtx
                .lock()
                .expect("DoubleAudioBuffer:::refresh: failed to lock the exposed buffer");
            // get latest state from the previously exposed buffer
            working_buffer.update_concrete_state(exposed_buffer_box.as_mut());

            // refresh working buffer
            working_buffer.refresh_with_conditions(&self.audio_buffer, conditions.clone());

            // swap buffers
            mem::swap(exposed_buffer_box, &mut working_buffer);
        }

        // working buffer is last exposed buffer
        // refresh with new conditions
        working_buffer.refresh_with_conditions(&self.audio_buffer, conditions);

        self.lower_to_keep = working_buffer.get_lower();

        self.working_buffer = Some(working_buffer);
        // self.working_buffer is now the buffer previously in
        // self.exposed_buffer_mtx
    }
}
<commit_msg>DoubleAudioBuffer: update both buffers when eos is reached (#65)<commit_after>extern crate gstreamer as gst;
extern crate gstreamer_audio as gst_audio;

use std::any::Any;

use std::mem;

use std::sync::{Arc, Mutex};

use super::{AudioBuffer, AudioChannel, SampleExtractor};

const EXTRACTION_THRESHOLD: usize = 1024;

// The DoubleBuffer is reponsible for ensuring a thread safe double buffering
// mechanism that receives samples from GStreamer, prepares an extraction of
// these samples and presents the most recent extraction to an external
// mechanism (e.g. UI).
// The DoubleBuffer hosts two SampleExtractors and an AudioBuffer:
//   - The SampleExtractor trait is designed to allow the extraction of samples
//   depending on certains conditions as defined in its concrete implementation.
//   - The AudioBuffer manages the container for all the samples received.
// The DoubleBuffer prepares the extraction in the working_buffer and exposes
// the exposed_buffer during that time. When the extration is done, the buffers
// are swapped.
pub struct DoubleAudioBuffer {
    audio_buffer: AudioBuffer,
    samples_since_last_extract: usize,
    exposed_buffer_mtx: Arc<Mutex<Box<SampleExtractor>>>,
    working_buffer: Option<Box<SampleExtractor>>,
    lower_to_keep: usize,
}

impl DoubleAudioBuffer {
    // need 2 arguments for new as we can't clone buffers as they are known
    // as trait SampleExtractor
    pub fn new(
        buffer_duration: u64,
        exposed_buffer: Box<SampleExtractor>,
        working_buffer: Box<SampleExtractor>,
    ) -> DoubleAudioBuffer {
        DoubleAudioBuffer {
            audio_buffer: AudioBuffer::new(buffer_duration),
            samples_since_last_extract: 0,
            exposed_buffer_mtx: Arc::new(Mutex::new(exposed_buffer)),
            working_buffer: Some(working_buffer),
            lower_to_keep: 0,
        }
    }

    // Get a reference on the exposed buffer mutex.
    pub fn get_exposed_buffer_mtx(&self) -> Arc<Mutex<Box<SampleExtractor>>> {
        Arc::clone(&self.exposed_buffer_mtx)
    }

    pub fn cleanup(&mut self) {
        self.audio_buffer.cleanup();
        self.samples_since_last_extract = 0;

        {
            let exposed_buffer = &mut self.exposed_buffer_mtx.lock().expect(
                "DoubleAudioBuffer: couldn't lock exposed_buffer_mtx while setting audio sink",
            );
            exposed_buffer.cleanup();
        }

        self.working_buffer
            .as_mut()
            .expect("DoubleAudioBuffer: couldn't get working_buffer while setting audio sink")
            .cleanup();
        self.lower_to_keep = 0;
    }

    // Initialize buffer with audio stream capabilities
    // and GStreamer element for position reference
    pub fn set_audio_caps_and_ref(&mut self, caps: &gst::Caps, audio_ref: &gst::Element) {
        let audio_info = gst_audio::AudioInfo::from_caps(caps)
            .expect("DoubleAudioBuffer::set_audio_caps_and_ref unable to get AudioInfo");

        let rate = u64::from(audio_info.rate());
        let channels = audio_info.channels() as usize;
        self.audio_buffer.init(rate, channels);

        let sample_duration = 1_000_000_000 / rate;
        let duration_for_1000_samples = 1_000_000_000_000f64 / (rate as f64);

        let mut channels: Vec<AudioChannel> = Vec::with_capacity(channels);
        if let Some(positions) = audio_info.positions() {
            for position in positions {
                channels.push(AudioChannel::new(position));
            }
        };

        {
            let exposed_buffer = &mut self.exposed_buffer_mtx.lock().expect(
                "DoubleAudioBuffer: couldn't lock exposed_buffer_mtx while setting audio sink",
            );
            exposed_buffer.set_audio_sink(audio_ref.clone());
            exposed_buffer.set_sample_duration(sample_duration, duration_for_1000_samples);
            exposed_buffer.set_channels(&channels);
        }

        let working_buffer = self.working_buffer
            .as_mut()
            .expect("DoubleAudioBuffer: couldn't get working_buffer while setting audio sink");
        working_buffer.set_audio_sink(audio_ref.clone());
        working_buffer.set_sample_duration(sample_duration, duration_for_1000_samples);
        working_buffer.set_channels(&channels);
    }

    pub fn handle_eos(&mut self) {
        self.audio_buffer.handle_eos();
        // extract last samples and swap
        self.extract_samples();
        // do it again to update second extractor too
        // this is required in case of a subsequent seek
        // in the extractors' range
        self.extract_samples();
    }

    pub fn push_gst_sample(&mut self, sample: gst::Sample) {
        // store incoming samples
        self.samples_since_last_extract += self.audio_buffer
            .push_gst_sample(sample, self.lower_to_keep);

        if self.samples_since_last_extract >= EXTRACTION_THRESHOLD {
            // extract new samples and swap
            self.extract_samples();
            self.samples_since_last_extract = 0;
        }
    }

    // Update the working extractor with new samples and swap.
    pub fn extract_samples(&mut self) {
        let mut working_buffer = self.working_buffer
            .take()
            .expect("DoubleAudioBuffer::extract_samples: failed to take working buffer");
        working_buffer.extract_samples(&self.audio_buffer);

        // swap buffers
        {
            let exposed_buffer_box = &mut *self.exposed_buffer_mtx
                .lock()
                .expect("DoubleAudioBuffer::extract_samples: failed to lock the exposed buffer");
            // get latest state from the previously exposed buffer
            // in order to smoothen rendering between frames
            working_buffer.update_concrete_state(exposed_buffer_box.as_mut());
            mem::swap(exposed_buffer_box, &mut working_buffer);
        }

        self.lower_to_keep = working_buffer.get_lower();

        self.working_buffer = Some(working_buffer);
        // self.working_buffer is now the buffer previously in
        // self.exposed_buffer_mtx
    }

    pub fn refresh(&mut self) {
        // refresh with current conditions
        let mut working_buffer = self.working_buffer
            .take()
            .expect("DoubleAudioBuffer::refresh: failed to take working buffer");

        {
            let exposed_buffer_box = &mut *self.exposed_buffer_mtx
                .lock()
                .expect("DoubleAudioBuffer:::refresh: failed to lock the exposed buffer");
            // get latest state from the previously exposed buffer
            working_buffer.update_concrete_state(exposed_buffer_box.as_mut());

            // refresh working buffer
            working_buffer.refresh(&self.audio_buffer);

            // swap buffers
            mem::swap(exposed_buffer_box, &mut working_buffer);
        }

        self.lower_to_keep = working_buffer.get_lower();

        self.working_buffer = Some(working_buffer);
        // self.working_buffer is now the buffer previously in
        // self.exposed_buffer_mtx
    }

    // Refresh the working buffer with the provided conditions and swap.
    // Conditions concrete type must conform to a struct expected
    // by the concrete implementation of the SampleExtractor.
    pub fn refresh_with_conditions<T: Any + Clone>(&mut self, conditions: Box<T>) {
        let mut working_buffer = self.working_buffer
            .take()
            .expect("DoubleAudioBuffer::refresh: failed to take working buffer");

        {
            let exposed_buffer_box = &mut *self.exposed_buffer_mtx
                .lock()
                .expect("DoubleAudioBuffer:::refresh: failed to lock the exposed buffer");
            // get latest state from the previously exposed buffer
            working_buffer.update_concrete_state(exposed_buffer_box.as_mut());

            // refresh working buffer
            working_buffer.refresh_with_conditions(&self.audio_buffer, conditions.clone());

            // swap buffers
            mem::swap(exposed_buffer_box, &mut working_buffer);
        }

        // working buffer is last exposed buffer
        // refresh with new conditions
        working_buffer.refresh_with_conditions(&self.audio_buffer, conditions);

        self.lower_to_keep = working_buffer.get_lower();

        self.working_buffer = Some(working_buffer);
        // self.working_buffer is now the buffer previously in
        // self.exposed_buffer_mtx
    }
}
<|endoftext|>


<commit_before>extern crate gstreamer as gst;
use gstreamer::prelude::*;
use gstreamer::{ClockTime, QueryView};

extern crate glib;

use std::sync::mpsc::Sender;

use std::path::Path;

use std::sync::atomic::AtomicBool;

use super::ContextMessage;
use metadata;

pub struct SplitterContext {
    pipeline: gst::Pipeline,
    position_ref: Option<gst::Element>,
    position_query: gst::Query,

    format: metadata::Format,
    tags: gst::TagList,
}

impl SplitterContext {
    pub fn new(
        input_path: &Path,
        output_path: &Path,
        format: &metadata::Format,
        start: u64,
        end: u64,
        tags: gst::TagList,
        ctx_tx: Sender<ContextMessage>,
    ) -> Result<SplitterContext, String> {
        println!("  - exporting {:?} to {:?}...", input_path, output_path);

        let mut this = SplitterContext {
            pipeline: gst::Pipeline::new("pipeline"),
            position_ref: None,
            position_query: gst::Query::new_position(gst::Format::Time),

            format: format.clone(),
            tags: tags,
        };

        this.build_pipeline(input_path, output_path, start, end);
        this.register_bus_inspector(ctx_tx);

        match this.pipeline.set_state(gst::State::Paused) {
            gst::StateChangeReturn::Failure => Err("Could not set media in Paused state".into()),
            _ => Ok(this),
        }
    }

    pub fn get_position(&mut self) -> u64 {
        self.position_ref
            .as_ref()
            .unwrap()
            .query(self.position_query.get_mut().unwrap());
        match self.position_query.view() {
            QueryView::Position(ref position) => position.get_result().get_value() as u64,
            _ => unreachable!(),
        }
    }

    // TODO: handle errors
    fn build_pipeline(&mut self, input_path: &Path, output_path: &Path, start: u64, end: u64) {
        /* There are multiple showstoppers to implementing something ideal
         * to export splitted chapters with audio and video (and subtitles):
         * 1. matroska-mux drops seek events explicitely (a message states: "discard for now").
         * This means that it is currently not possible to build a pipeline that would allow
         * seeking in a matroska media (using demux to interprete timestamps) and mux back to
         * to matroska. One solution would be to export streams to files and mux back
         * crossing fingers to make sure everything remains in sync.
         * 2. nlesrc (from gstreamer-editing-services) allows extracting frames from a starting
         * positiong for a given duration. However, it is designed to work with single stream
         * medias and decodes to raw formats.
         * 3. filesink can't change the file location without setting the pipeline to Null
         * which also unlinks elements.
         * 4. wavenc doesn't send header after a second seek in segment mode.
         * 5. flacenc can't handle a seek.
         *
         * Issues 3 & 4 lead to building a new pipeline for each chapter.
         *
         * Until I design a GUI for the user to select which stream to export to which codec,
         * current solution is to keep only the audio track and to save it as a flac file
         * which matches the initial purpose of this application */

        // Input
        let filesrc = gst::ElementFactory::make("filesrc", None).unwrap();
        filesrc
            .set_property("location", &gst::Value::from(input_path.to_str().unwrap()))
            .unwrap();
        let decodebin = gst::ElementFactory::make("decodebin", None).unwrap();

        self.pipeline.add_many(&[&filesrc, &decodebin]).unwrap();

        filesrc.link(&decodebin).unwrap();
        decodebin.sync_state_with_parent().unwrap();

        self.position_ref = Some(decodebin.clone());

        // Audio encoder
        let audio_enc = match self.format {
            metadata::Format::Flac => gst::ElementFactory::make("flacenc", None).unwrap(),
            metadata::Format::Wave => gst::ElementFactory::make("wavenc", None).unwrap(),
            metadata::Format::Opus => gst::ElementFactory::make("opusenc", None).unwrap(),
            metadata::Format::Vorbis => gst::ElementFactory::make("vorbisenc", None).unwrap(),
            metadata::Format::MP3 => gst::ElementFactory::make("lamemp3enc", None).unwrap(),
            _ => panic!(
                "SplitterContext::build_pipeline unsupported format: {:?}",
                self.format
            ),
        };

        // Catch events and drop the upstream Tags & TOC
        let audio_enc_sink_pad = audio_enc.get_static_pad("sink").unwrap();
        audio_enc_sink_pad.add_probe(gst::PadProbeType::EVENT_DOWNSTREAM, |_pad, probe_info| {
            if let Some(ref data) = probe_info.data {
                if let gst::PadProbeData::Event(ref event) = *data {
                    match event.view() {
                        gst::EventView::Tag(ref _tag) => return gst::PadProbeReturn::Drop,
                        gst::EventView::Toc(ref _toc) => return gst::PadProbeReturn::Drop,
                        _ => (),
                    }
                }
            }
            gst::PadProbeReturn::Ok
        });

        // Some encoders (such as flacenc) starts encoding the preroll buffers when switching to
        // paused mode. When the seek is performed buffers from the new segments are appended
        // to the ones from the preroll. Moreover, flacenc doesn't handle discontinuities.
        // As a workaround, we will drop buffers before the seek. The first buffer with the Discont
        // flag show that it is possible to seek. The next buffer with the Discont flag corresponds
        // to the first buffer from the target segment
        let mut seek_done = AtomicBool::new(false);
        let pipeline = self.pipeline.clone();
        audio_enc_sink_pad.add_probe(gst::PadProbeType::BUFFER, move |_pad, probe_info| {
            if let Some(ref data) = probe_info.data {
                if let gst::PadProbeData::Buffer(ref buffer) = *data {
                    if buffer.get_flags() & gst::BufferFlags::DISCONT == gst::BufferFlags::DISCONT {
                        let mut seek_done_lck = seek_done.get_mut();
                        if !*seek_done_lck {
                            // First buffer before seek
                            // let's seek and drop buffers until seek start sending new segment
                            match pipeline.seek(
                                1f64,
                                gst::SeekFlags::FLUSH | gst::SeekFlags::ACCURATE,
                                gst::SeekType::Set,
                                ClockTime::from(start),
                                gst::SeekType::Set,
                                ClockTime::from(end),
                            ) {
                                Ok(_) => (),
                                Err(_) => {
                                    eprintln!("Error: Failed to seek");
                                }
                            };
                            *seek_done_lck = true;
                        } else {
                            // First Discont buffer after seek => stop dropping buffers
                            return gst::PadProbeReturn::Remove;
                        }
                    }
                }
            }
            gst::PadProbeReturn::Drop
        });

        self.pipeline.add(&audio_enc).unwrap();

        // add a muxer when required
        let (tag_setter, audio_muxer) = match self.format {
            metadata::Format::Flac | metadata::Format::Wave => {
                (audio_enc.clone(), audio_enc.clone())
            }
            metadata::Format::Opus | metadata::Format::Vorbis => {
                let ogg_muxer = gst::ElementFactory::make("oggmux", None).unwrap();
                self.pipeline.add(&ogg_muxer).unwrap();
                audio_enc.link(&ogg_muxer).unwrap();
                (audio_enc.clone(), ogg_muxer)
            }
            metadata::Format::MP3 => {
                let id3v2_muxer = gst::ElementFactory::make("id3v2mux", None).unwrap();
                self.pipeline.add(&id3v2_muxer).unwrap();
                audio_enc.link(&id3v2_muxer).unwrap();
                (id3v2_muxer.clone(), id3v2_muxer)
            }
            _ => panic!(
                "SplitterContext::build_pipeline unsupported format: {:?}",
                self.format
            ),
        };

        //self.tag_setter = Some(tag_setter);
        let tag_setter = tag_setter
            .clone()
            .dynamic_cast::<gst::TagSetter>()
            .expect("SplitterContext::build_pipeline tag_setter is not a TagSetter");

        tag_setter.merge_tags(&self.tags, gst::TagMergeMode::ReplaceAll);

        // Output sink
        let outsink = gst::ElementFactory::make("filesink", "filesink").unwrap();
        outsink
            .set_property("location", &gst::Value::from(output_path.to_str().unwrap()))
            .unwrap();

        self.pipeline.add(&outsink).unwrap();
        audio_muxer.link(&outsink).unwrap();
        outsink.sync_state_with_parent().unwrap();

        let pipeline_cb = self.pipeline.clone();
        decodebin.connect_pad_added(move |_element, pad| {
            let caps = pad.get_current_caps().unwrap();
            let structure = caps.get_structure(0).unwrap();
            let name = structure.get_name();

            let queue = gst::ElementFactory::make("queue", None).unwrap();
            pipeline_cb.add(&queue).unwrap();
            let queue_sink_pad = queue.get_static_pad("sink").unwrap();
            assert_eq!(pad.link(&queue_sink_pad), gst::PadLinkReturn::Ok);
            queue.sync_state_with_parent().unwrap();
            let queue_src_pad = queue.get_static_pad("src").unwrap();

            if name.starts_with("audio/") && pipeline_cb.get_by_name("audioconvert").is_none() {
                let audio_conv = gst::ElementFactory::make("audioconvert", "audioconvert").unwrap();
                pipeline_cb.add(&audio_conv).unwrap();
                gst::Element::link_many(&[&queue, &audio_conv, &audio_enc]).unwrap();
                audio_conv.sync_state_with_parent().unwrap();
                audio_enc.sync_state_with_parent().unwrap();
            } else {
                let fakesink = gst::ElementFactory::make("fakesink", None).unwrap();
                pipeline_cb.add(&fakesink).unwrap();
                let fakesink_sink_pad = fakesink.get_static_pad("sink").unwrap();
                assert_eq!(
                    queue_src_pad.link(&fakesink_sink_pad),
                    gst::PadLinkReturn::Ok
                );
                fakesink.sync_state_with_parent().unwrap();
            }
        });
    }

    // Uses ctx_tx to notify the UI controllers
    fn register_bus_inspector(&self, ctx_tx: Sender<ContextMessage>) {
        let pipeline = self.pipeline.clone();
        self.pipeline.get_bus().unwrap().add_watch(move |_, msg| {
            match msg.view() {
                gst::MessageView::Eos(..) => {
                    if pipeline.set_state(gst::State::Null) == gst::StateChangeReturn::Failure {
                        ctx_tx
                            .send(ContextMessage::FailedToExport)
                            .expect("Error: Failed to notify UI");
                    }
                    ctx_tx
                        .send(ContextMessage::Eos)
                        .expect("Eos: Failed to notify UI");
                    return glib::Continue(false);
                }
                gst::MessageView::Error(err) => {
                    eprintln!(
                        "Error from {}: {} ({:?})",
                        msg.get_src()
                            .map(|s| s.get_path_string(),)
                            .unwrap_or_else(|| String::from("None"),),
                        err.get_error(),
                        err.get_debug()
                    );
                    ctx_tx
                        .send(ContextMessage::FailedToExport)
                        .expect("Error: Failed to notify UI");
                    return glib::Continue(false);
                }
                gst::MessageView::AsyncDone(_) => {
                    // Start splitting
                    if pipeline.set_state(gst::State::Playing) == gst::StateChangeReturn::Failure {
                        ctx_tx
                            .send(ContextMessage::FailedToExport)
                            .expect("Error: Failed to notify UI");
                    }
                }
                _ => (),
            }

            glib::Continue(true)
        });
    }
}
<commit_msg>Splitter: revert to using Arc<Mutex<bool>> instead of AtomicBool<commit_after>extern crate gstreamer as gst;
use gstreamer::prelude::*;
use gstreamer::{ClockTime, QueryView};

extern crate glib;

use std::sync::mpsc::Sender;

use std::path::Path;

use std::sync::{Arc, Mutex};

use super::ContextMessage;
use metadata;

pub struct SplitterContext {
    pipeline: gst::Pipeline,
    position_ref: Option<gst::Element>,
    position_query: gst::Query,

    format: metadata::Format,
    tags: gst::TagList,
}

impl SplitterContext {
    pub fn new(
        input_path: &Path,
        output_path: &Path,
        format: &metadata::Format,
        start: u64,
        end: u64,
        tags: gst::TagList,
        ctx_tx: Sender<ContextMessage>,
    ) -> Result<SplitterContext, String> {
        println!("  - exporting {:?} to {:?}...", input_path, output_path);

        let mut this = SplitterContext {
            pipeline: gst::Pipeline::new("pipeline"),
            position_ref: None,
            position_query: gst::Query::new_position(gst::Format::Time),

            format: format.clone(),
            tags: tags,
        };

        this.build_pipeline(input_path, output_path, start, end);
        this.register_bus_inspector(ctx_tx);

        match this.pipeline.set_state(gst::State::Paused) {
            gst::StateChangeReturn::Failure => Err("Could not set media in Paused state".into()),
            _ => Ok(this),
        }
    }

    pub fn get_position(&mut self) -> u64 {
        self.position_ref
            .as_ref()
            .unwrap()
            .query(self.position_query.get_mut().unwrap());
        match self.position_query.view() {
            QueryView::Position(ref position) => position.get_result().get_value() as u64,
            _ => unreachable!(),
        }
    }

    // TODO: handle errors
    #[cfg_attr(feature = "cargo-clippy", allow(mutex_atomic))]
    fn build_pipeline(&mut self, input_path: &Path, output_path: &Path, start: u64, end: u64) {
        /* There are multiple showstoppers to implementing something ideal
         * to export splitted chapters with audio and video (and subtitles):
         * 1. matroska-mux drops seek events explicitely (a message states: "discard for now").
         * This means that it is currently not possible to build a pipeline that would allow
         * seeking in a matroska media (using demux to interprete timestamps) and mux back to
         * to matroska. One solution would be to export streams to files and mux back
         * crossing fingers to make sure everything remains in sync.
         * 2. nlesrc (from gstreamer-editing-services) allows extracting frames from a starting
         * positiong for a given duration. However, it is designed to work with single stream
         * medias and decodes to raw formats.
         * 3. filesink can't change the file location without setting the pipeline to Null
         * which also unlinks elements.
         * 4. wavenc doesn't send header after a second seek in segment mode.
         * 5. flacenc can't handle a seek.
         *
         * Issues 3 & 4 lead to building a new pipeline for each chapter.
         *
         * Until I design a GUI for the user to select which stream to export to which codec,
         * current solution is to keep only the audio track and to save it as a flac file
         * which matches the initial purpose of this application */

        // Input
        let filesrc = gst::ElementFactory::make("filesrc", None).unwrap();
        filesrc
            .set_property("location", &gst::Value::from(input_path.to_str().unwrap()))
            .unwrap();
        let decodebin = gst::ElementFactory::make("decodebin", None).unwrap();

        self.pipeline.add_many(&[&filesrc, &decodebin]).unwrap();

        filesrc.link(&decodebin).unwrap();
        decodebin.sync_state_with_parent().unwrap();

        self.position_ref = Some(decodebin.clone());

        // Audio encoder
        let audio_enc = match self.format {
            metadata::Format::Flac => gst::ElementFactory::make("flacenc", None).unwrap(),
            metadata::Format::Wave => gst::ElementFactory::make("wavenc", None).unwrap(),
            metadata::Format::Opus => gst::ElementFactory::make("opusenc", None).unwrap(),
            metadata::Format::Vorbis => gst::ElementFactory::make("vorbisenc", None).unwrap(),
            metadata::Format::MP3 => gst::ElementFactory::make("lamemp3enc", None).unwrap(),
            _ => panic!(
                "SplitterContext::build_pipeline unsupported format: {:?}",
                self.format
            ),
        };

        // Catch events and drop the upstream Tags & TOC
        let audio_enc_sink_pad = audio_enc.get_static_pad("sink").unwrap();
        audio_enc_sink_pad.add_probe(gst::PadProbeType::EVENT_DOWNSTREAM, |_pad, probe_info| {
            if let Some(ref data) = probe_info.data {
                if let gst::PadProbeData::Event(ref event) = *data {
                    match event.view() {
                        gst::EventView::Tag(ref _tag) => return gst::PadProbeReturn::Drop,
                        gst::EventView::Toc(ref _toc) => return gst::PadProbeReturn::Drop,
                        _ => (),
                    }
                }
            }
            gst::PadProbeReturn::Ok
        });

        // Some encoders (such as flacenc) starts encoding the preroll buffers when switching to
        // paused mode. When the seek is performed buffers from the new segments are appended
        // to the ones from the preroll. Moreover, flacenc doesn't handle discontinuities.
        // As a workaround, we will drop buffers before the seek. The first buffer with the Discont
        // flag show that it is possible to seek. The next buffer with the Discont flag corresponds
        // to the first buffer from the target segment

        // Note: can't use AtomicBool here as pad probes are multithreaded so the function is Fn
        // not FnMut. See: https://github.com/sdroege/gstreamer-rs/pull/71
        let seek_done = Arc::new(Mutex::new(false));
        let pipeline = self.pipeline.clone();
        audio_enc_sink_pad.add_probe(gst::PadProbeType::BUFFER, move |_pad, probe_info| {
            if let Some(ref data) = probe_info.data {
                if let gst::PadProbeData::Buffer(ref buffer) = *data {
                    if buffer.get_flags() & gst::BufferFlags::DISCONT == gst::BufferFlags::DISCONT {
                        let mut seek_done_grp = seek_done
                            .lock()
                            .expect("audio_enc_sink_pad(buffer):: couldn't lock seek_done");
                        if !*seek_done_grp {
                            // First buffer before seek
                            // let's seek and drop buffers until seek start sending new segment
                            match pipeline.seek(
                                1f64,
                                gst::SeekFlags::FLUSH | gst::SeekFlags::ACCURATE,
                                gst::SeekType::Set,
                                ClockTime::from(start),
                                gst::SeekType::Set,
                                ClockTime::from(end),
                            ) {
                                Ok(_) => (),
                                Err(_) => {
                                    eprintln!("Error: Failed to seek");
                                }
                            };
                            *seek_done_grp = true;
                        } else {
                            // First Discont buffer after seek => stop dropping buffers
                            return gst::PadProbeReturn::Remove;
                        }
                    }
                }
            }
            gst::PadProbeReturn::Drop
        });

        self.pipeline.add(&audio_enc).unwrap();

        // add a muxer when required
        let (tag_setter, audio_muxer) = match self.format {
            metadata::Format::Flac | metadata::Format::Wave => {
                (audio_enc.clone(), audio_enc.clone())
            }
            metadata::Format::Opus | metadata::Format::Vorbis => {
                let ogg_muxer = gst::ElementFactory::make("oggmux", None).unwrap();
                self.pipeline.add(&ogg_muxer).unwrap();
                audio_enc.link(&ogg_muxer).unwrap();
                (audio_enc.clone(), ogg_muxer)
            }
            metadata::Format::MP3 => {
                let id3v2_muxer = gst::ElementFactory::make("id3v2mux", None).unwrap();
                self.pipeline.add(&id3v2_muxer).unwrap();
                audio_enc.link(&id3v2_muxer).unwrap();
                (id3v2_muxer.clone(), id3v2_muxer)
            }
            _ => panic!(
                "SplitterContext::build_pipeline unsupported format: {:?}",
                self.format
            ),
        };

        //self.tag_setter = Some(tag_setter);
        let tag_setter = tag_setter
            .clone()
            .dynamic_cast::<gst::TagSetter>()
            .expect("SplitterContext::build_pipeline tag_setter is not a TagSetter");

        tag_setter.merge_tags(&self.tags, gst::TagMergeMode::ReplaceAll);

        // Output sink
        let outsink = gst::ElementFactory::make("filesink", "filesink").unwrap();
        outsink
            .set_property("location", &gst::Value::from(output_path.to_str().unwrap()))
            .unwrap();

        self.pipeline.add(&outsink).unwrap();
        audio_muxer.link(&outsink).unwrap();
        outsink.sync_state_with_parent().unwrap();

        let pipeline_cb = self.pipeline.clone();
        decodebin.connect_pad_added(move |_element, pad| {
            let caps = pad.get_current_caps().unwrap();
            let structure = caps.get_structure(0).unwrap();
            let name = structure.get_name();

            let queue = gst::ElementFactory::make("queue", None).unwrap();
            pipeline_cb.add(&queue).unwrap();
            let queue_sink_pad = queue.get_static_pad("sink").unwrap();
            assert_eq!(pad.link(&queue_sink_pad), gst::PadLinkReturn::Ok);
            queue.sync_state_with_parent().unwrap();
            let queue_src_pad = queue.get_static_pad("src").unwrap();

            if name.starts_with("audio/") && pipeline_cb.get_by_name("audioconvert").is_none() {
                let audio_conv = gst::ElementFactory::make("audioconvert", "audioconvert").unwrap();
                pipeline_cb.add(&audio_conv).unwrap();
                gst::Element::link_many(&[&queue, &audio_conv, &audio_enc]).unwrap();
                audio_conv.sync_state_with_parent().unwrap();
                audio_enc.sync_state_with_parent().unwrap();
            } else {
                let fakesink = gst::ElementFactory::make("fakesink", None).unwrap();
                pipeline_cb.add(&fakesink).unwrap();
                let fakesink_sink_pad = fakesink.get_static_pad("sink").unwrap();
                assert_eq!(
                    queue_src_pad.link(&fakesink_sink_pad),
                    gst::PadLinkReturn::Ok
                );
                fakesink.sync_state_with_parent().unwrap();
            }
        });
    }

    // Uses ctx_tx to notify the UI controllers
    fn register_bus_inspector(&self, ctx_tx: Sender<ContextMessage>) {
        let pipeline = self.pipeline.clone();
        self.pipeline.get_bus().unwrap().add_watch(move |_, msg| {
            match msg.view() {
                gst::MessageView::Eos(..) => {
                    if pipeline.set_state(gst::State::Null) == gst::StateChangeReturn::Failure {
                        ctx_tx
                            .send(ContextMessage::FailedToExport)
                            .expect("Error: Failed to notify UI");
                    }
                    ctx_tx
                        .send(ContextMessage::Eos)
                        .expect("Eos: Failed to notify UI");
                    return glib::Continue(false);
                }
                gst::MessageView::Error(err) => {
                    eprintln!(
                        "Error from {}: {} ({:?})",
                        msg.get_src()
                            .map(|s| s.get_path_string(),)
                            .unwrap_or_else(|| String::from("None"),),
                        err.get_error(),
                        err.get_debug()
                    );
                    ctx_tx
                        .send(ContextMessage::FailedToExport)
                        .expect("Error: Failed to notify UI");
                    return glib::Continue(false);
                }
                gst::MessageView::AsyncDone(_) => {
                    // Start splitting
                    if pipeline.set_state(gst::State::Playing) == gst::StateChangeReturn::Failure {
                        ctx_tx
                            .send(ContextMessage::FailedToExport)
                            .expect("Error: Failed to notify UI");
                    }
                }
                _ => (),
            }

            glib::Continue(true)
        });
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

type FreeVariable = u64;
type VariableName<'a> = &'a str;

/// AST node expression
#[derive(Copy, Clone, Debug, PartialEq)]
enum Expression<'a> {
    Number(f64),
    Boolean(bool),

    /// simple variable
    Variable(VariableName<'a>),

    /// fn (x, y, z) -> expr
    Lambda(&'a [VariableName<'a>], &'a Expression<'a>),

    /// let x = expr in expr
    Let(VariableName<'a>, &'a Expression<'a>, &'a Expression<'a>),

    /// some_fn (x, y, z)
    FnCall(&'a Expression<'a>, &'a [ &'a Expression<'a>])
}

#[derive(Clone, Debug, PartialEq)]
enum Type {
    Number,
    Boolean,
    Unbound(FreeVariable),
    // FIXME: can I avoid having this be boxed and vec'd?
    Lambda(Vec<Type>, Box<Type>),
}

type TypeEnv<'a> = HashMap<VariableName<'a>, Type>;

/// Bind previously unbound type variables to a specified type.
fn bind_type<'a>(env: &mut TypeEnv<'a>, var: FreeVariable, ty: Type) -> Type {
    println!("substitute unbound({}) for {:?}", var, ty);

    for val in env.values_mut() {
        if *val == Type::Unbound(var) {
            *val = ty.clone();
        }
    }

    ty
}

#[derive(Clone, Copy)]
struct VariableGen { current: FreeVariable }
impl VariableGen {
    fn next(&mut self) -> FreeVariable { self.current += 1; self.current }
}

type UnificationError = &'static str;
type TypeError = &'static str;

/// modifies the environment to unify the given types (or returns false if not possible)
fn unify<'a>(a: Type, b: Type, env: &mut TypeEnv<'a>) -> Result<Type, UnificationError> {
    match (a, b) {
        // Easy. Literals match with themselves
        (Type::Number, Type::Number) => Ok(Type::Number),
        (Type::Boolean, Type::Boolean) => Ok(Type::Boolean),

        (Type::Unbound(n), ty) | (ty, Type::Unbound(n)) => Ok(bind_type(env, n, ty)),

        (Type::Lambda(args1, body1), Type::Lambda(args2, body2)) => {
            if args1.len() != args2.len() {
                return Err("lambdas take differing numbers of arguments")
            }

            let mut unified_args = Vec::with_capacity(args1.len());

            for iter in args1.into_iter().zip(args2.into_iter()) {
                let (a1, a2) = iter;

                // TODO: should try to avoid the clone here
                match unify(a1, a2, env) {
                    Ok(ty) => unified_args.push(ty),
                    err => return err
                }
            }

            match unify(*body1, *body2, env) {
                Ok(body_ty) => Ok(Type::Lambda(unified_args, Box::new(body_ty))),
                err => err
            }
        }

        _ => Err("could not unify types")
    }
}

/// Run algorithm W on expr with given type env, yielding the resolved
/// type of the expression, or the type error encountered.
fn w<'a>(expr: &Expression<'a>, env: &mut TypeEnv<'a>, inst: &mut VariableGen) -> Type {
    match *expr {
        // simplest case: literals have immediately defined types.
        Expression::Number(_) => Type::Number,
        Expression::Boolean(_) => Type::Boolean,

        // variables we need to look up inside our type environment
        Expression::Variable(name) => match env.get(name) {
            Some(typ) => typ.clone(),
            None => panic!("undefined variable, {}", name)
        },

        Expression::Lambda(args, body) => {
            let args_ty = args
                .into_iter()
                .map(|arg| {
                    let ty = inst.next();
                    env.insert(arg, Type::Unbound(ty));
                    Type::Unbound(ty)
                })
                .collect::<Vec<Type>>();

            let body_ty = w(body, env, inst);

            Type::Lambda(args_ty, Box::new(body_ty))
        },

        // TODO: need an occurs check here.
        Expression::Let(name, expr, body) => {
            let expr_ty = w(expr, env, inst);
            env.insert(name, expr_ty);

            w(body, env, inst)
        },

        // 1. ensure that the callable in this function call is truly callable.
        // 2. ensure that the number and type of args match
        // 3. return type of body of lambda
        Expression::FnCall(callable, args) => {
            // FIXME: this seems broken.
            let mut env_ = env.clone();
            let mut inst_ = inst.clone();

            let type_args = args
                .iter()
                .map(|arg| {
                    w(arg, &mut env_, &mut inst_)
                })
                .collect::<Vec<Type>>();

            // Try to unify the lambda we are calling with the way we are calling it.
            unify(w(callable, &mut env_, inst),
                  Type::Lambda(type_args, Box::new(Type::Unbound(inst.next()))),
                  &mut env_)
                .map(move |ty|{
                    match ty {
                        Type::Lambda(_, return_type) => *return_type,
                        _ => panic!("BUG: expected unify to return lambda, not {:?}", ty)
                    }
                })
                .or(Err("could not unify lambda types"))
                // FIXME: this should eventually return a result anyway.
                .unwrap()
        }
    }
}

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

    fn infer(exp: Expression) -> Type {
        let mut inst = VariableGen { current: 0 };
        let mut env = HashMap::new();

        w(&exp, &mut env, &mut inst)
    }

    #[test]
    #[should_panic]
    fn undefined_variable() {
        let var = Expression::Variable("x");

        // FIXME: until we return a result type this will panic.
        infer(var);
    }

    #[test]
    fn literal_inference() {
        let num = Expression::Number(123.0);
        let boolean = Expression::Boolean(true);

        assert_eq!(infer(num), Type::Number);
        assert_eq!(infer(boolean), Type::Boolean);
    }

    #[test]
    fn lambda_expression_inference() {
        let args = ["x"];
        let num = Expression::Number(123.0);

        // \x -> 123.0
        let lambda = Expression::Lambda(&args, &num);

        assert_eq!(infer(lambda), Type::Lambda(vec![Type::Unbound(1)], Box::new(Type::Number)));

        // \x -> \y -> 123.0
        let lambdalambda = Expression::Lambda(&args, &lambda);
        assert_eq!(infer(lambdalambda),
                   Type::Lambda(vec![Type::Unbound(1)], Box::new(
                       Type::Lambda(vec![Type::Unbound(2)], Box::new(Type::Number)))));
    }

    #[test]
    fn let_inference() {
        let boolean = Expression::Boolean(false);
        let var_x = Expression::Variable("x");
        let var_y = Expression::Variable("y");

        // let x = false in x
        let let_ = Expression::Let("x", &boolean, &var_x);

        assert_eq!(infer(let_), Type::Boolean);

        // let y = (let x = false in x) in y
        let nested_let = Expression::Let("y", &let_, &var_y);
        assert_eq!(infer(nested_let), Type::Boolean);
    }

    #[test]
    fn fn_call_inference() {
        let args = ["x"];
        let num = Expression::Number(123.0);
        let boolean = Expression::Boolean(false);

        // \x -> 123.0
        let lambda = Expression::Lambda(&args, &num);

        let app_args = [&boolean];
        let application = Expression::FnCall(&lambda, &app_args);

        assert_eq!(infer(application), Type::Number);

        // let
        //    x = (\a -> a)
        // in
        //    let y = (\a -> a)
        //    in
        //       x (y)
        // ===> \a -> a
        let var_a = Expression::Variable("a");
        let var_x = Expression::Variable("x");
        let var_y = Expression::Variable("y");
        let args_a = ["a"];
        let args_y = [&var_y];
        let lambda_x = Expression::Lambda(&args_a, &var_a);
        let lambda_y = Expression::Lambda(&args_a, &var_a);

        let fncall = Expression::FnCall(&var_x, &args_y);

        let let_y = Expression::Let("y", &lambda_y, &fncall);
        let let_x = Expression::Let("x", &lambda_x, &let_y);

        assert_eq!(infer(let_x), Type::Lambda(vec![Type::Unbound(3)], Box::new(Type::Unbound(3))))
    }

    #[test]
    fn test_unify() {
        let mut env = HashMap::new();

        assert_eq!(unify(Type::Number, Type::Number, &mut env), Ok(Type::Number));
        assert!(unify(Type::Number, Type::Boolean, &mut env).is_err());
        assert_eq!(unify(Type::Unbound(1), Type::Number, &mut env), Ok(Type::Number));

        assert_eq!(unify(Type::Lambda(vec![Type::Unbound(1), Type::Unbound(2)], Box::new(Type::Number)),
                         Type::Lambda(vec![Type::Unbound(3), Type::Unbound(4)], Box::new(Type::Number)),
                         &mut env),
                   Ok(Type::Lambda(vec![Type::Unbound(3), Type::Unbound(4)], Box::new(Type::Number))));
    }
}

fn main() {
    println!("patience.");
}
<commit_msg>Don't clone env for fn call<commit_after>use std::collections::HashMap;

type FreeVariable = u64;
type VariableName<'a> = &'a str;

/// AST node expression
#[derive(Copy, Clone, Debug, PartialEq)]
enum Expression<'a> {
    Number(f64),
    Boolean(bool),

    /// simple variable
    Variable(VariableName<'a>),

    /// fn (x, y, z) -> expr
    Lambda(&'a [VariableName<'a>], &'a Expression<'a>),

    /// let x = expr in expr
    Let(VariableName<'a>, &'a Expression<'a>, &'a Expression<'a>),

    /// some_fn (x, y, z)
    FnCall(&'a Expression<'a>, &'a [ &'a Expression<'a>])
}

#[derive(Clone, Debug, PartialEq)]
enum Type {
    Number,
    Boolean,
    Unbound(FreeVariable),
    // FIXME: can I avoid having this be boxed and vec'd?
    Lambda(Vec<Type>, Box<Type>),
}

type TypeEnv<'a> = HashMap<VariableName<'a>, Type>;

/// Bind previously unbound type variables to a specified type.
fn bind_type<'a>(env: &mut TypeEnv<'a>, var: FreeVariable, ty: Type) -> Type {
    println!("substitute unbound({}) for {:?}", var, ty);

    for val in env.values_mut() {
        if *val == Type::Unbound(var) {
            *val = ty.clone();
        }
    }

    ty
}

#[derive(Clone, Copy)]
struct VariableGen { current: FreeVariable }
impl VariableGen {
    fn next(&mut self) -> FreeVariable { self.current += 1; self.current }
}

type UnificationError = &'static str;
type TypeError = &'static str;

/// modifies the environment to unify the given types (or returns false if not possible)
fn unify<'a>(a: Type, b: Type, env: &mut TypeEnv<'a>) -> Result<Type, UnificationError> {
    match (a, b) {
        // Easy. Literals match with themselves
        (Type::Number, Type::Number) => Ok(Type::Number),
        (Type::Boolean, Type::Boolean) => Ok(Type::Boolean),

        (Type::Unbound(n), ty) | (ty, Type::Unbound(n)) => Ok(bind_type(env, n, ty)),

        (Type::Lambda(args1, body1), Type::Lambda(args2, body2)) => {
            if args1.len() != args2.len() {
                return Err("lambdas take differing numbers of arguments")
            }

            let mut unified_args = Vec::with_capacity(args1.len());

            for iter in args1.into_iter().zip(args2.into_iter()) {
                let (a1, a2) = iter;

                // TODO: should try to avoid the clone here
                match unify(a1, a2, env) {
                    Ok(ty) => unified_args.push(ty),
                    err => return err
                }
            }

            match unify(*body1, *body2, env) {
                Ok(body_ty) => Ok(Type::Lambda(unified_args, Box::new(body_ty))),
                err => err
            }
        }

        _ => Err("could not unify types")
    }
}

/// Run algorithm W on expr with given type env, yielding the resolved
/// type of the expression, or the type error encountered.
fn w<'a>(expr: &Expression<'a>, env: &mut TypeEnv<'a>, inst: &mut VariableGen) -> Type {
    match *expr {
        // simplest case: literals have immediately defined types.
        Expression::Number(_) => Type::Number,
        Expression::Boolean(_) => Type::Boolean,

        // variables we need to look up inside our type environment
        Expression::Variable(name) => match env.get(name) {
            Some(typ) => typ.clone(),
            None => panic!("undefined variable, {}", name)
        },

        Expression::Lambda(args, body) => {
            let args_ty = args
                .into_iter()
                .map(|arg| {
                    let ty = inst.next();
                    env.insert(arg, Type::Unbound(ty));
                    Type::Unbound(ty)
                })
                .collect::<Vec<Type>>();

            let body_ty = w(body, env, inst);

            Type::Lambda(args_ty, Box::new(body_ty))
        },

        // TODO: need an occurs check here.
        Expression::Let(name, expr, body) => {
            let expr_ty = w(expr, env, inst);
            env.insert(name, expr_ty);

            w(body, env, inst)
        },

        // 1. ensure that the callable in this function call is truly callable.
        // 2. ensure that the number and type of args match
        // 3. return type of body of lambda
        Expression::FnCall(callable, args) => {
            let type_args = args
                .iter()
                .map(|arg| {
                    w(arg, env, inst)
                })
                .collect::<Vec<Type>>();

            // Try to unify the lambda we are calling with the way we are calling it.
            unify(w(callable, env, inst),
                  Type::Lambda(type_args, Box::new(Type::Unbound(inst.next()))),
                  env)
                .map(move |ty|{
                    match ty {
                        Type::Lambda(_, return_type) => *return_type,
                        _ => panic!("BUG: expected unify to return lambda, not {:?}", ty)
                    }
                })
                .or(Err("could not unify lambda types"))
                // FIXME: this should eventually return a result anyway.
                .unwrap()
        }
    }
}

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

    fn infer(exp: Expression) -> Type {
        let mut inst = VariableGen { current: 0 };
        let mut env = HashMap::new();

        w(&exp, &mut env, &mut inst)
    }

    #[test]
    #[should_panic]
    fn undefined_variable() {
        let var = Expression::Variable("x");

        // FIXME: until we return a result type this will panic.
        infer(var);
    }

    #[test]
    fn literal_inference() {
        let num = Expression::Number(123.0);
        let boolean = Expression::Boolean(true);

        assert_eq!(infer(num), Type::Number);
        assert_eq!(infer(boolean), Type::Boolean);
    }

    #[test]
    fn lambda_expression_inference() {
        let args = ["x"];
        let num = Expression::Number(123.0);

        // \x -> 123.0
        let lambda = Expression::Lambda(&args, &num);

        assert_eq!(infer(lambda), Type::Lambda(vec![Type::Unbound(1)], Box::new(Type::Number)));

        // \x -> \y -> 123.0
        let lambdalambda = Expression::Lambda(&args, &lambda);
        assert_eq!(infer(lambdalambda),
                   Type::Lambda(vec![Type::Unbound(1)], Box::new(
                       Type::Lambda(vec![Type::Unbound(2)], Box::new(Type::Number)))));
    }

    #[test]
    fn let_inference() {
        let boolean = Expression::Boolean(false);
        let var_x = Expression::Variable("x");
        let var_y = Expression::Variable("y");

        // let x = false in x
        let let_ = Expression::Let("x", &boolean, &var_x);

        assert_eq!(infer(let_), Type::Boolean);

        // let y = (let x = false in x) in y
        let nested_let = Expression::Let("y", &let_, &var_y);
        assert_eq!(infer(nested_let), Type::Boolean);
    }

    #[test]
    fn fn_call_inference() {
        let args = ["x"];
        let num = Expression::Number(123.0);
        let boolean = Expression::Boolean(false);

        // \x -> 123.0
        let lambda = Expression::Lambda(&args, &num);

        let app_args = [&boolean];
        let application = Expression::FnCall(&lambda, &app_args);

        assert_eq!(infer(application), Type::Number);

        // let
        //    x = (\a -> a)
        // in
        //    let y = (\a -> a)
        //    in
        //       x (y)
        // ===> \a -> a
        let var_a = Expression::Variable("a");
        let var_x = Expression::Variable("x");
        let var_y = Expression::Variable("y");
        let args_a = ["a"];
        let args_y = [&var_y];
        let lambda_x = Expression::Lambda(&args_a, &var_a);
        let lambda_y = Expression::Lambda(&args_a, &var_a);

        let fncall = Expression::FnCall(&var_x, &args_y);

        let let_y = Expression::Let("y", &lambda_y, &fncall);
        let let_x = Expression::Let("x", &lambda_x, &let_y);

        assert_eq!(infer(let_x), Type::Lambda(vec![Type::Unbound(3)], Box::new(Type::Unbound(3))))
    }

    #[test]
    fn test_unify() {
        let mut env = HashMap::new();

        assert_eq!(unify(Type::Number, Type::Number, &mut env), Ok(Type::Number));
        assert!(unify(Type::Number, Type::Boolean, &mut env).is_err());
        assert_eq!(unify(Type::Unbound(1), Type::Number, &mut env), Ok(Type::Number));

        assert_eq!(unify(Type::Lambda(vec![Type::Unbound(1), Type::Unbound(2)], Box::new(Type::Number)),
                         Type::Lambda(vec![Type::Unbound(3), Type::Unbound(4)], Box::new(Type::Number)),
                         &mut env),
                   Ok(Type::Lambda(vec![Type::Unbound(3), Type::Unbound(4)], Box::new(Type::Number))));
    }
}

fn main() {
    println!("patience.");
}
<|endoftext|>


<commit_before>use regex::Regex;
use std::fs::File;
use std::io::{self, BufRead};
use std::str::FromStr;

#[macro_use]
extern crate lazy_static;

struct State<T>(Box<dyn Iterator<Item = T>>, Option<T>);
pub struct Merge {
    count: usize,
    states: Vec<State<u32>>,
}

impl Merge {
    pub fn new(filename: &str) -> Self {
        let count = get_number(filename).unwrap();
        let states: Vec<State<u32>> = (0..count).map(|i| {
            let collection = Box::new(get_collection(filename, i).unwrap());
            State(collection, None)
        }).collect();
        Merge {
            count: count,
            states: states,
        }
    }
}

impl Iterator for Merge {
    type Item = u32;
    fn next(&mut self) -> Option<Self::Item> {
        let count = self.count;
        let states = &mut self.states;
        loop {
            for i in 0..count {
                let state = &mut states[i];
                if state.1.is_none() {
                    state.1 = state.0.next();
                }
            }
            let not_done = states.iter().any(|s| s.1.is_some());
            if !not_done {
                return None;
            }

            let (mut min, mut min_index) = (u32::MAX, usize::MIN);
            for i in 0..count {
                if let Some(val) = states[i].1 {
                    if val < min {
                        min = val;
                        min_index = i;
                    }
                }
            }
            states[min_index].1 = None;
            return Some(min);
        }
    }
}

pub fn get_number(filename: &str) -> io::Result<usize> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"NUMBER=(\d+)").unwrap();
    }
    let mut matches = match_lines(filename, &*RE)?;
    return Ok(matches.next().unwrap().parse().unwrap());
}

pub fn get_output<T: FromStr>(filename: &str) -> io::Result<impl Iterator<Item = T>> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"OUTPUT:(.*)").unwrap();
    }
    get_numbers(filename, &*RE)
}

pub fn get_collection<T: FromStr>(filename: &str, idx: usize) -> io::Result<impl Iterator<Item = T>> {
    let regex: Regex = {
        let r = format!("COLLECTION{}:(.*)", idx);
        Regex::new(&r).unwrap()
    };
    get_numbers(filename, &regex)
}

fn get_numbers<T: FromStr>(filename: &str, regex: &Regex) -> io::Result<impl Iterator<Item = T>> {
    let matches = match_lines(filename, regex)?;
    let to_int = |v: &str| -> Option<T> {
        if let Ok(i) = v.parse::<T>() {
            Some(i)
        } else {
            None
        }
    };
    let values = matches
        .map(move |s| {
            let numbers = s.split(',').filter_map(|v| to_int(&v));
            numbers.collect::<Vec<_>>()
        })
        .flatten();
    Ok(values)
}

fn match_lines(filename: &str, regex: &Regex) -> io::Result<impl Iterator<Item = String>> {
    let read_lines = move || -> io::Result<_> {
        let file = File::open(filename)?;
        Ok(io::BufReader::new(file).lines().map(|line| line.unwrap()))
    };
    let lines = read_lines()?;
    let r = regex.clone(); // TODO
    let matches =
        lines.filter_map(move |line| r.captures(&line).map(|captures| captures[1].to_string()));
    Ok(matches)
}

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

    #[test]
    fn test_number() {
        assert_eq!(get_number("../data/test.txt").unwrap(), 3);
    }

    #[test]
    fn test_output() {
        let output = [1, 1, 2, 2, 3, 4, 4, 6, 7, 9, 9, 20, 21];
        assert_eq!(
            get_output("../data/test.txt").unwrap().collect::<Vec<u32>>(),
            output
        );
    }

    #[test]
    fn test_collection() {
        let output = [2, 4, 6, 9, 20];
        assert_eq!(
            get_collection("../data/test.txt", 1)
                .unwrap()
                .collect::<Vec<u32>>(),
            output
        );
    }

    fn merge(filename: &str) -> (impl Iterator<Item = u32>, impl Iterator<Item = u32>) {
        let result = Merge::new(filename);
        let output = get_output(filename).unwrap();
        (result, output)
    }

    fn assert_iterator(
        iter1: &mut impl Iterator<Item = u32>,
        iter2: &mut impl Iterator<Item = u32>,
    ) -> bool {
        loop {
            match (iter1.next(), iter2.next()) {
                (None, None) => return true,
                (Some(v1), Some(v2)) if v1 == v2 => continue,
                _ => return false,
            }
        }
    }

    #[test]
    fn test_merge() {
        let filename = "../data/test.txt";
        let (mut result, mut output) = merge(filename);
        assert!(assert_iterator(&mut result, &mut output));
    }

    #[test]
    #[should_panic]
    fn test_bad_merge() {
        let filename = "../data/bad.txt";
        let (_result, _output) = merge(filename);
    }

    #[test]
    fn test_zero_merge() {
        let filename = "../data/zero.txt";
        let (mut result, mut output) = merge(filename);
        assert!(result.next().is_none());
        assert!(output.next().is_none());
    }

    #[test]
    fn test_err() {
        let filename = "../data/err.txt";
        let (mut result, mut output) = merge(filename);
        assert!(!assert_iterator(&mut result, &mut output));
    }
}
<commit_msg>minor<commit_after>use regex::Regex;
use std::fs::File;
use std::io::{self, BufRead};
use std::str::FromStr;

#[macro_use]
extern crate lazy_static;

struct State<T>(Box<dyn Iterator<Item = T>>, Option<T>);
pub struct Merge {
    count: usize,
    states: Vec<State<u32>>,
}

impl Merge {
    pub fn new(filename: &str) -> Self {
        let count = get_number(filename).unwrap();
        let states = (0..count).map(|i| {
            let collection = Box::new(get_collection(filename, i).unwrap());
            State(collection, None)
        }).collect();
        Merge {
            count: count,
            states: states,
        }
    }
}

impl Iterator for Merge {
    type Item = u32;
    fn next(&mut self) -> Option<Self::Item> {
        let count = self.count;
        let states = &mut self.states;
        loop {
            for i in 0..count {
                let state = &mut states[i];
                if state.1.is_none() {
                    state.1 = state.0.next();
                }
            }
            let not_done = states.iter().any(|s| s.1.is_some());
            if !not_done {
                return None;
            }

            let (mut min, mut min_index) = (u32::MAX, usize::MIN);
            for i in 0..count {
                if let Some(val) = states[i].1 {
                    if val < min {
                        min = val;
                        min_index = i;
                    }
                }
            }
            states[min_index].1 = None;
            return Some(min);
        }
    }
}

pub fn get_number(filename: &str) -> io::Result<usize> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"NUMBER=(\d+)").unwrap();
    }
    let mut matches = match_lines(filename, &*RE)?;
    return Ok(matches.next().unwrap().parse().unwrap());
}

pub fn get_output<T: FromStr>(filename: &str) -> io::Result<impl Iterator<Item = T>> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"OUTPUT:(.*)").unwrap();
    }
    get_numbers(filename, &*RE)
}

pub fn get_collection<T: FromStr>(filename: &str, idx: usize) -> io::Result<impl Iterator<Item = T>> {
    let regex: Regex = {
        let r = format!("COLLECTION{}:(.*)", idx);
        Regex::new(&r).unwrap()
    };
    get_numbers(filename, &regex)
}

fn get_numbers<T: FromStr>(filename: &str, regex: &Regex) -> io::Result<impl Iterator<Item = T>> {
    let matches = match_lines(filename, regex)?;
    let to_int = |v: &str| -> Option<T> {
        if let Ok(i) = v.parse::<T>() {
            Some(i)
        } else {
            None
        }
    };
    let values = matches
        .map(move |s| {
            let numbers = s.split(',').filter_map(|v| to_int(&v));
            numbers.collect::<Vec<_>>()
        })
        .flatten();
    Ok(values)
}

fn match_lines(filename: &str, regex: &Regex) -> io::Result<impl Iterator<Item = String>> {
    let read_lines = move || -> io::Result<_> {
        let file = File::open(filename)?;
        Ok(io::BufReader::new(file).lines().map(|line| line.unwrap()))
    };
    let lines = read_lines()?;
    let r = regex.clone(); // TODO
    let matches =
        lines.filter_map(move |line| r.captures(&line).map(|captures| captures[1].to_string()));
    Ok(matches)
}

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

    #[test]
    fn test_number() {
        assert_eq!(get_number("../data/test.txt").unwrap(), 3);
    }

    #[test]
    fn test_output() {
        let output = [1, 1, 2, 2, 3, 4, 4, 6, 7, 9, 9, 20, 21];
        assert_eq!(
            get_output("../data/test.txt").unwrap().collect::<Vec<u32>>(),
            output
        );
    }

    #[test]
    fn test_collection() {
        let output = [2, 4, 6, 9, 20];
        assert_eq!(
            get_collection("../data/test.txt", 1)
                .unwrap()
                .collect::<Vec<u32>>(),
            output
        );
    }

    fn merge(filename: &str) -> (impl Iterator<Item = u32>, impl Iterator<Item = u32>) {
        let result = Merge::new(filename);
        let output = get_output(filename).unwrap();
        (result, output)
    }

    fn assert_iterator(
        iter1: &mut impl Iterator<Item = u32>,
        iter2: &mut impl Iterator<Item = u32>,
    ) -> bool {
        loop {
            match (iter1.next(), iter2.next()) {
                (None, None) => return true,
                (Some(v1), Some(v2)) if v1 == v2 => continue,
                _ => return false,
            }
        }
    }

    #[test]
    fn test_merge() {
        let filename = "../data/test.txt";
        let (mut result, mut output) = merge(filename);
        assert!(assert_iterator(&mut result, &mut output));
    }

    #[test]
    #[should_panic]
    fn test_bad_merge() {
        let filename = "../data/bad.txt";
        let (_result, _output) = merge(filename);
    }

    #[test]
    fn test_zero_merge() {
        let filename = "../data/zero.txt";
        let (mut result, mut output) = merge(filename);
        assert!(result.next().is_none());
        assert!(output.next().is_none());
    }

    #[test]
    fn test_err() {
        let filename = "../data/err.txt";
        let (mut result, mut output) = merge(filename);
        assert!(!assert_iterator(&mut result, &mut output));
    }
}
<|endoftext|>