<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Michael Debertol <michael.debertol..AT..gmail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

//! Sort big files by using auxiliary files for storing intermediate chunks.
//!
//! Files are read into chunks of memory which are then sorted individually and
//! written to temporary files. There are two threads: One sorter, and one reader/writer.
//! The buffers for the individual chunks are recycled. There are two buffers.

use std::cmp::Ordering;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
use std::{
    io::Read,
    sync::mpsc::{Receiver, SyncSender},
    thread,
};

use itertools::Itertools;

use crate::chunks::RecycledChunk;
use crate::merge::ClosedTmpFile;
use crate::merge::WriteableCompressedTmpFile;
use crate::merge::WriteablePlainTmpFile;
use crate::merge::WriteableTmpFile;
use crate::Output;
use crate::{
    chunks::{self, Chunk},
    compare_by, merge, sort_by, GlobalSettings,
};
use crate::{print_sorted, Line};
use tempfile::TempDir;

const START_BUFFER_SIZE: usize = 8_000;

/// Sort files by using auxiliary files for storing intermediate chunks (if needed), and output the result.
pub fn ext_sort(
    files: &mut impl Iterator<Item = Box<dyn Read + Send>>,
    settings: &GlobalSettings,
    output: Output,
) {
    let (sorted_sender, sorted_receiver) = std::sync::mpsc::sync_channel(1);
    let (recycled_sender, recycled_receiver) = std::sync::mpsc::sync_channel(1);
    thread::spawn({
        let settings = settings.clone();
        move || sorter(recycled_receiver, sorted_sender, settings)
    });
    if settings.compress_prog.is_some() {
        reader_writer::<_, WriteableCompressedTmpFile>(
            files,
            settings,
            sorted_receiver,
            recycled_sender,
            output,
        );
    } else {
        reader_writer::<_, WriteablePlainTmpFile>(
            files,
            settings,
            sorted_receiver,
            recycled_sender,
            output,
        );
    }
}

fn reader_writer<F: Iterator<Item = Box<dyn Read + Send>>, Tmp: WriteableTmpFile + 'static>(
    files: F,
    settings: &GlobalSettings,
    receiver: Receiver<Chunk>,
    sender: SyncSender<Chunk>,
    output: Output,
) {
    let separator = if settings.zero_terminated {
        b'\0'
    } else {
        b'\n'
    };

    // Heuristically chosen: Dividing by 10 seems to keep our memory usage roughly
    // around settings.buffer_size as a whole.
    let buffer_size = settings.buffer_size / 10;
    let read_result: ReadResult<Tmp> = read_write_loop(
        files,
        &settings.tmp_dir,
        separator,
        // Heuristically chosen: Dividing by 10 seems to keep our memory usage roughly
        // around settings.buffer_size as a whole.
        buffer_size,
        settings,
        receiver,
        sender,
    );
    match read_result {
        ReadResult::WroteChunksToFile { tmp_files, tmp_dir } => {
            let tmp_dir_size = tmp_files.len();
            let mut merger = merge::merge_with_file_limit::<_, _, Tmp>(
                tmp_files.into_iter().map(|c| c.reopen()),
                settings,
                Some((tmp_dir, tmp_dir_size)),
            );
            merger.write_all(settings, output);
        }
        ReadResult::SortedSingleChunk(chunk) => {
            if settings.unique {
                print_sorted(
                    chunk.lines().iter().dedup_by(|a, b| {
                        compare_by(a, b, settings, chunk.line_data(), chunk.line_data())
                            == Ordering::Equal
                    }),
                    settings,
                    output,
                );
            } else {
                print_sorted(chunk.lines().iter(), settings, output);
            }
        }
        ReadResult::SortedTwoChunks([a, b]) => {
            let merged_iter = a.lines().iter().map(|line| (line, &a)).merge_by(
                b.lines().iter().map(|line| (line, &b)),
                |(line_a, a), (line_b, b)| {
                    compare_by(line_a, line_b, settings, a.line_data(), b.line_data())
                        != Ordering::Greater
                },
            );
            if settings.unique {
                print_sorted(
                    merged_iter
                        .dedup_by(|(line_a, a), (line_b, b)| {
                            compare_by(line_a, line_b, settings, a.line_data(), b.line_data())
                                == Ordering::Equal
                        })
                        .map(|(line, _)| line),
                    settings,
                    output,
                );
            } else {
                print_sorted(merged_iter.map(|(line, _)| line), settings, output);
            }
        }
        ReadResult::EmptyInput => {
            // don't output anything
        }
    }
}

/// The function that is executed on the sorter thread.
fn sorter(receiver: Receiver<Chunk>, sender: SyncSender<Chunk>, settings: GlobalSettings) {
    while let Ok(mut payload) = receiver.recv() {
        payload.with_contents_mut(|contents| {
            sort_by(&mut contents.lines, &settings, &contents.line_data)
        });
        sender.send(payload).unwrap();
    }
}

/// Describes how we read the chunks from the input.
enum ReadResult<I: WriteableTmpFile> {
    /// The input was empty. Nothing was read.
    EmptyInput,
    /// The input fits into a single Chunk, which was kept in memory.
    SortedSingleChunk(Chunk),
    /// The input fits into two chunks, which were kept in memory.
    SortedTwoChunks([Chunk; 2]),
    /// The input was read into multiple chunks, which were written to auxiliary files.
    WroteChunksToFile {
        tmp_files: Vec<I::Closed>,
        tmp_dir: TempDir,
    },
}
/// The function that is executed on the reader/writer thread.
fn read_write_loop<I: WriteableTmpFile>(
    mut files: impl Iterator<Item = Box<dyn Read + Send>>,
    tmp_dir_parent: &Path,
    separator: u8,
    buffer_size: usize,
    settings: &GlobalSettings,
    receiver: Receiver<Chunk>,
    sender: SyncSender<Chunk>,
) -> ReadResult<I> {
    let mut file = files.next().unwrap();

    let mut carry_over = vec![];
    // kick things off with two reads
    for _ in 0..2 {
        let should_continue = chunks::read(
            &sender,
            RecycledChunk::new(if START_BUFFER_SIZE < buffer_size {
                START_BUFFER_SIZE
            } else {
                buffer_size
            }),
            Some(buffer_size),
            &mut carry_over,
            &mut file,
            &mut files,
            separator,
            settings,
        );

        if !should_continue {
            drop(sender);
            // We have already read the whole input. Since we are in our first two reads,
            // this means that we can fit the whole input into memory. Bypass writing below and
            // handle this case in a more straightforward way.
            return if let Ok(first_chunk) = receiver.recv() {
                if let Ok(second_chunk) = receiver.recv() {
                    ReadResult::SortedTwoChunks([first_chunk, second_chunk])
                } else {
                    ReadResult::SortedSingleChunk(first_chunk)
                }
            } else {
                ReadResult::EmptyInput
            };
        }
    }

    let tmp_dir = crash_if_err!(
        2,
        tempfile::Builder::new()
            .prefix("uutils_sort")
            .tempdir_in(tmp_dir_parent)
    );

    let mut sender_option = Some(sender);
    let mut file_number = 0;
    let mut tmp_files = vec![];
    loop {
        let mut chunk = match receiver.recv() {
            Ok(it) => it,
            _ => {
                return ReadResult::WroteChunksToFile { tmp_files, tmp_dir };
            }
        };

        let tmp_file = write::<I>(
            &mut chunk,
            tmp_dir.path().join(file_number.to_string()),
            settings.compress_prog.as_deref(),
            separator,
        );
        tmp_files.push(tmp_file);

        file_number += 1;

        let recycled_chunk = chunk.recycle();

        if let Some(sender) = &sender_option {
            let should_continue = chunks::read(
                sender,
                recycled_chunk,
                None,
                &mut carry_over,
                &mut file,
                &mut files,
                separator,
                settings,
            );
            if !should_continue {
                sender_option = None;
            }
        }
    }
}

/// Write the lines in `chunk` to `file`, separated by `separator`.
/// `compress_prog` is used to optionally compress file contents.
fn write<I: WriteableTmpFile>(
    chunk: &mut Chunk,
    file: PathBuf,
    compress_prog: Option<&str>,
    separator: u8,
) -> I::Closed {
    let mut tmp_file = I::create(file, compress_prog);
    write_lines(chunk.lines(), tmp_file.as_write(), separator);
    tmp_file.finished_writing()
}

fn write_lines<'a, T: Write>(lines: &[Line<'a>], writer: &mut T, separator: u8) {
    for s in lines {
        writer.write_all(s.line.as_bytes()).unwrap();
        writer.write_all(&[separator]).unwrap();
    }
}
<commit_msg>sort: remove redundant comment<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Michael Debertol <michael.debertol..AT..gmail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

//! Sort big files by using auxiliary files for storing intermediate chunks.
//!
//! Files are read into chunks of memory which are then sorted individually and
//! written to temporary files. There are two threads: One sorter, and one reader/writer.
//! The buffers for the individual chunks are recycled. There are two buffers.

use std::cmp::Ordering;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
use std::{
    io::Read,
    sync::mpsc::{Receiver, SyncSender},
    thread,
};

use itertools::Itertools;

use crate::chunks::RecycledChunk;
use crate::merge::ClosedTmpFile;
use crate::merge::WriteableCompressedTmpFile;
use crate::merge::WriteablePlainTmpFile;
use crate::merge::WriteableTmpFile;
use crate::Output;
use crate::{
    chunks::{self, Chunk},
    compare_by, merge, sort_by, GlobalSettings,
};
use crate::{print_sorted, Line};
use tempfile::TempDir;

const START_BUFFER_SIZE: usize = 8_000;

/// Sort files by using auxiliary files for storing intermediate chunks (if needed), and output the result.
pub fn ext_sort(
    files: &mut impl Iterator<Item = Box<dyn Read + Send>>,
    settings: &GlobalSettings,
    output: Output,
) {
    let (sorted_sender, sorted_receiver) = std::sync::mpsc::sync_channel(1);
    let (recycled_sender, recycled_receiver) = std::sync::mpsc::sync_channel(1);
    thread::spawn({
        let settings = settings.clone();
        move || sorter(recycled_receiver, sorted_sender, settings)
    });
    if settings.compress_prog.is_some() {
        reader_writer::<_, WriteableCompressedTmpFile>(
            files,
            settings,
            sorted_receiver,
            recycled_sender,
            output,
        );
    } else {
        reader_writer::<_, WriteablePlainTmpFile>(
            files,
            settings,
            sorted_receiver,
            recycled_sender,
            output,
        );
    }
}

fn reader_writer<F: Iterator<Item = Box<dyn Read + Send>>, Tmp: WriteableTmpFile + 'static>(
    files: F,
    settings: &GlobalSettings,
    receiver: Receiver<Chunk>,
    sender: SyncSender<Chunk>,
    output: Output,
) {
    let separator = if settings.zero_terminated {
        b'\0'
    } else {
        b'\n'
    };

    // Heuristically chosen: Dividing by 10 seems to keep our memory usage roughly
    // around settings.buffer_size as a whole.
    let buffer_size = settings.buffer_size / 10;
    let read_result: ReadResult<Tmp> = read_write_loop(
        files,
        &settings.tmp_dir,
        separator,
        buffer_size,
        settings,
        receiver,
        sender,
    );
    match read_result {
        ReadResult::WroteChunksToFile { tmp_files, tmp_dir } => {
            let tmp_dir_size = tmp_files.len();
            let mut merger = merge::merge_with_file_limit::<_, _, Tmp>(
                tmp_files.into_iter().map(|c| c.reopen()),
                settings,
                Some((tmp_dir, tmp_dir_size)),
            );
            merger.write_all(settings, output);
        }
        ReadResult::SortedSingleChunk(chunk) => {
            if settings.unique {
                print_sorted(
                    chunk.lines().iter().dedup_by(|a, b| {
                        compare_by(a, b, settings, chunk.line_data(), chunk.line_data())
                            == Ordering::Equal
                    }),
                    settings,
                    output,
                );
            } else {
                print_sorted(chunk.lines().iter(), settings, output);
            }
        }
        ReadResult::SortedTwoChunks([a, b]) => {
            let merged_iter = a.lines().iter().map(|line| (line, &a)).merge_by(
                b.lines().iter().map(|line| (line, &b)),
                |(line_a, a), (line_b, b)| {
                    compare_by(line_a, line_b, settings, a.line_data(), b.line_data())
                        != Ordering::Greater
                },
            );
            if settings.unique {
                print_sorted(
                    merged_iter
                        .dedup_by(|(line_a, a), (line_b, b)| {
                            compare_by(line_a, line_b, settings, a.line_data(), b.line_data())
                                == Ordering::Equal
                        })
                        .map(|(line, _)| line),
                    settings,
                    output,
                );
            } else {
                print_sorted(merged_iter.map(|(line, _)| line), settings, output);
            }
        }
        ReadResult::EmptyInput => {
            // don't output anything
        }
    }
}

/// The function that is executed on the sorter thread.
fn sorter(receiver: Receiver<Chunk>, sender: SyncSender<Chunk>, settings: GlobalSettings) {
    while let Ok(mut payload) = receiver.recv() {
        payload.with_contents_mut(|contents| {
            sort_by(&mut contents.lines, &settings, &contents.line_data)
        });
        sender.send(payload).unwrap();
    }
}

/// Describes how we read the chunks from the input.
enum ReadResult<I: WriteableTmpFile> {
    /// The input was empty. Nothing was read.
    EmptyInput,
    /// The input fits into a single Chunk, which was kept in memory.
    SortedSingleChunk(Chunk),
    /// The input fits into two chunks, which were kept in memory.
    SortedTwoChunks([Chunk; 2]),
    /// The input was read into multiple chunks, which were written to auxiliary files.
    WroteChunksToFile {
        tmp_files: Vec<I::Closed>,
        tmp_dir: TempDir,
    },
}
/// The function that is executed on the reader/writer thread.
fn read_write_loop<I: WriteableTmpFile>(
    mut files: impl Iterator<Item = Box<dyn Read + Send>>,
    tmp_dir_parent: &Path,
    separator: u8,
    buffer_size: usize,
    settings: &GlobalSettings,
    receiver: Receiver<Chunk>,
    sender: SyncSender<Chunk>,
) -> ReadResult<I> {
    let mut file = files.next().unwrap();

    let mut carry_over = vec![];
    // kick things off with two reads
    for _ in 0..2 {
        let should_continue = chunks::read(
            &sender,
            RecycledChunk::new(if START_BUFFER_SIZE < buffer_size {
                START_BUFFER_SIZE
            } else {
                buffer_size
            }),
            Some(buffer_size),
            &mut carry_over,
            &mut file,
            &mut files,
            separator,
            settings,
        );

        if !should_continue {
            drop(sender);
            // We have already read the whole input. Since we are in our first two reads,
            // this means that we can fit the whole input into memory. Bypass writing below and
            // handle this case in a more straightforward way.
            return if let Ok(first_chunk) = receiver.recv() {
                if let Ok(second_chunk) = receiver.recv() {
                    ReadResult::SortedTwoChunks([first_chunk, second_chunk])
                } else {
                    ReadResult::SortedSingleChunk(first_chunk)
                }
            } else {
                ReadResult::EmptyInput
            };
        }
    }

    let tmp_dir = crash_if_err!(
        2,
        tempfile::Builder::new()
            .prefix("uutils_sort")
            .tempdir_in(tmp_dir_parent)
    );

    let mut sender_option = Some(sender);
    let mut file_number = 0;
    let mut tmp_files = vec![];
    loop {
        let mut chunk = match receiver.recv() {
            Ok(it) => it,
            _ => {
                return ReadResult::WroteChunksToFile { tmp_files, tmp_dir };
            }
        };

        let tmp_file = write::<I>(
            &mut chunk,
            tmp_dir.path().join(file_number.to_string()),
            settings.compress_prog.as_deref(),
            separator,
        );
        tmp_files.push(tmp_file);

        file_number += 1;

        let recycled_chunk = chunk.recycle();

        if let Some(sender) = &sender_option {
            let should_continue = chunks::read(
                sender,
                recycled_chunk,
                None,
                &mut carry_over,
                &mut file,
                &mut files,
                separator,
                settings,
            );
            if !should_continue {
                sender_option = None;
            }
        }
    }
}

/// Write the lines in `chunk` to `file`, separated by `separator`.
/// `compress_prog` is used to optionally compress file contents.
fn write<I: WriteableTmpFile>(
    chunk: &mut Chunk,
    file: PathBuf,
    compress_prog: Option<&str>,
    separator: u8,
) -> I::Closed {
    let mut tmp_file = I::create(file, compress_prog);
    write_lines(chunk.lines(), tmp_file.as_write(), separator);
    tmp_file.finished_writing()
}

fn write_lines<'a, T: Write>(lines: &[Line<'a>], writer: &mut T, separator: u8) {
    for s in lines {
        writer.write_all(s.line.as_bytes()).unwrap();
        writer.write_all(&[separator]).unwrap();
    }
}
<|endoftext|>


<commit_before>//! Provides an oscillator that generates periodic waveforms.

use std::f32::consts::PI;
use num::traits::Float;
use rand::random;

use types::{SAMPLE_RATE, AudioDevice, Sample, Time};

pub use self::AntialiasType::{Aliased, PolyBlep};
pub use self::Waveform::{Sine, Saw, Square, Tri, WhiteNoise, PulseTrain};

/// Defines the messages that the Oscillator supports
#[derive(Clone, Copy, Debug)]
pub enum OscillatorMessage {
    /// Sets the frequency in Hz
    SetFreq(f32)
}

/// Antialiasing method for certain waveforms.
///
/// Aliased waveforms will use naive methods that produce aliasing.
/// PolyBLEP (Polynomial Bandlimited Step) uses offsets to round off sharp edges
/// and reduce aliasing.
#[derive(Clone, Copy, Debug)]
pub enum AntialiasType {
    /// Naive, aliasing waveforms.
    Aliased,
    /// Antialiasing using PolyBLEP.
    PolyBlep
}

/// Oscillator waveforms.
///
/// `Saw`, `Square`, and `Tri` provides either aliased waveforms, or antialiased
/// waveforms using PolyBLEP. Aliased waveformsare useful for control signals,
/// but not for raw audio signals. For audible signals, instead used the
/// corresponding `PolyBlep` waveforms.
#[derive(Clone, Copy, Debug)]
pub enum Waveform {
    Sine,
    Saw(AntialiasType),
    Square(AntialiasType),
    Tri(AntialiasType),
    WhiteNoise,
    PulseTrain
}

/// An oscillator that generates a periodic waveform.
pub struct Oscillator {
    waveform: Waveform,
    phase: f32,
    phase_delta: f32,
    last_sample: Sample,
}

impl Oscillator {
    /// Returns an oscillator with the specified waveform at the specified
    /// frequency, in Hz.
    pub fn new(waveform: Waveform, freq: f32) -> Oscillator {
        Oscillator {
            waveform: waveform,
            phase: 0.0,
            phase_delta: freq*2.0*PI/(SAMPLE_RATE as f32),
            last_sample: 0.0
        }
    }

    /// Applies the message to the oscillator
    pub fn handle_message(&mut self, msg: OscillatorMessage) {
        match msg {
            OscillatorMessage::SetFreq(freq) => {
                self.phase_delta = freq*2.0*PI/(SAMPLE_RATE as f32);
            }
        }
    }
}

impl AudioDevice for Oscillator {
    fn num_inputs(&self) -> usize {
        0
    }

    fn num_outputs(&self) -> usize {
        1
    }

    fn tick(&mut self, _: Time, _: &[Sample], outputs: &mut[Sample]) {
        // Tick the phase
        self.phase += self.phase_delta;
        if self.phase >= 2.0*PI {
            self.phase -= 2.0*PI;
        }

        // Compute the next sample
        let s: Sample = match self.waveform.clone() {
            Sine => self.phase.sin(),
            Saw(aa) => {
                let mut out = self.phase/PI -1.0;
                match aa {
                    PolyBlep => {
                        out -= poly_belp_offset(self.phase/(2.0*PI),
                                                self.phase_delta/(2.0*PI));
                    },
                    _ => ()
                }
                out
            },
            Square(aa) => {
                let mut out = if self.phase < PI { 1.0 } else { -1.0 };
                match aa {
                    PolyBlep => {
                        // two discontinuities, at rising and falling edges
                        out += poly_belp_offset(self.phase/(2.0*PI),
                                                self.phase_delta/(2.0*PI));
                        out -= poly_belp_offset(fmod(self.phase/(2.0*PI)+0.5,
                                                     1.0),
                                                self.phase_delta/(2.0*PI));
                    },
                    _ => ()
                }
                out
            },
            Tri(aa) => {
                // Compute a square wave signal
                let mut out = if self.phase < PI { 1.0 } else { -1.0 };
                match aa {
                    PolyBlep => {
                        // two discontinuities, at rising and falling edges
                        out += poly_belp_offset(self.phase/(2.0*PI),
                                                self.phase_delta/(2.0*PI));
                        out -= poly_belp_offset(fmod(self.phase/(2.0*PI)+0.5,
                                                     1.0),
                                                self.phase_delta/(2.0*PI));
                    },
                    _ => ()
                }

                // Perform leaky integration
                self.phase_delta*out + (1.0-self.phase_delta)*self.last_sample
            },
            WhiteNoise => 2.0*random::<f32>() - 1.0,
            PulseTrain => {
                // If we wrapped around...
                if self.phase < self.phase_delta {
                    1.0
                } else {
                    0.0
                }
            }
        };

        // Push the sample out
        outputs[0] = s;
    }
}

/// Floating point modulus
fn fmod(n: f32, base: f32) -> f32 {
    assert!(base > 0.0);
    let mut out = n;
    while out < 0.0 {
        out += base;
    }
    while out >= base {
        out -= base;
    }
    out
}

/// Computes an offset for PolyBLEP antialiasing
///
/// `t` should be the current waveform phase, normalized
/// `dt` should be the change in phase for one sample time, normalized
fn poly_belp_offset(t: f32, dt: f32) -> f32 {
    if t < dt { // t ~= 0
        let t = t / dt;
        -t*t + 2.0*t - 1.0
    } else if t > 1.0-dt { // t ~= 1
        let t = (t-1.0) / dt;
        t*t + 2.0*t + 1.0
    } else {
        0.0
    }
}

// A couple of basic unit tests...
#[cfg(test)]
mod test {
    use utils::helpers::flt_eq;
    static EPSILON: f32 = 1e-6;

    /// Tests fmod with many values
    #[test]
    fn test_fmod() {
        use super::fmod;

        // negatives...
        assert!(flt_eq(fmod(-1.5, 1.0), 0.5, EPSILON));
        assert!(flt_eq(fmod(-1.0, 1.0), 0.0, EPSILON));
        assert!(flt_eq(fmod(-0.5, 1.0), 0.5, EPSILON));

        // in range...
        assert!(flt_eq(fmod(0.0, 1.0), 0.0, EPSILON));
        assert!(flt_eq(fmod(0.5, 1.0), 0.5, EPSILON));
        assert!(flt_eq(fmod(0.9, 1.0), 0.9, EPSILON));

        // above...
        assert!(flt_eq(fmod(1.0, 1.0), 0.0, EPSILON));
        assert!(flt_eq(fmod(1.5, 1.0), 0.5, EPSILON));
        assert!(flt_eq(fmod(2.0, 1.0), 0.0, EPSILON));
        assert!(flt_eq(fmod(2.5, 1.0), 0.5, EPSILON));

        // different base...
        assert!(flt_eq(fmod(-0.5, 0.9), 0.4, EPSILON));
        assert!(flt_eq(fmod(0.0, 0.9), 0.0, EPSILON));
        assert!(flt_eq(fmod(0.5, 0.9), 0.5, EPSILON));
        assert!(flt_eq(fmod(0.9, 0.9), 0.0, EPSILON));
        assert!(flt_eq(fmod(1.0, 0.9), 0.1, EPSILON));
    }

    /// Tests square wave
    #[test]
    fn test_naive_square() {
        use super::{AntialiasType, Waveform, Oscillator};
        use types::AudioDevice;
        let mut osc = Oscillator::new(Waveform::Square(AntialiasType::Aliased),
                                      4410.0);
        let input = vec![];
        let mut output = vec![0.0];

        osc.tick(0, &input, &mut output); assert!(output[0] == 1.0);
        osc.tick(1, &input, &mut output); assert!(output[0] == 1.0);
        osc.tick(2, &input, &mut output); assert!(output[0] == 1.0);
        osc.tick(3, &input, &mut output); assert!(output[0] == 1.0);
        osc.tick(4, &input, &mut output); assert!(output[0] == -1.0);
        osc.tick(5, &input, &mut output); assert!(output[0] == -1.0);
        osc.tick(6, &input, &mut output); assert!(output[0] == -1.0);
        osc.tick(7, &input, &mut output); assert!(output[0] == -1.0);
        osc.tick(8, &input, &mut output); assert!(output[0] == -1.0);
    }

    /// Tests saw wave
    #[test]
    fn test_naive_saw() {
        use super::{AntialiasType, Waveform, Oscillator};
        use types::AudioDevice;
        let mut osc = Oscillator::new(Waveform::Saw(AntialiasType::Aliased),
                                      4410.0);
        let input = vec![];
        let mut output = vec![0.0];

        osc.tick(0, &input, &mut output); assert!(flt_eq(output[0], -0.8, EPSILON));
        osc.tick(1, &input, &mut output); assert!(flt_eq(output[0], -0.6, EPSILON));
        osc.tick(2, &input, &mut output); assert!(flt_eq(output[0], -0.4, EPSILON));
        osc.tick(3, &input, &mut output); assert!(flt_eq(output[0], -0.2, EPSILON));
        osc.tick(4, &input, &mut output); assert!(flt_eq(output[0], 0.0, EPSILON));
        osc.tick(5, &input, &mut output); assert!(flt_eq(output[0], 0.2, EPSILON));
        osc.tick(6, &input, &mut output); assert!(flt_eq(output[0], 0.4, EPSILON));
        osc.tick(7, &input, &mut output); assert!(flt_eq(output[0], 0.6, EPSILON));
        osc.tick(8, &input, &mut output); assert!(flt_eq(output[0], 0.8, EPSILON));
    }
}
<commit_msg>Add oscillator waveform setting<commit_after>//! Provides an oscillator that generates periodic waveforms.

use std::f32::consts::PI;
use num::traits::Float;
use rand::random;

use types::{SAMPLE_RATE, AudioDevice, Sample, Time};

pub use self::AntialiasType::{Aliased, PolyBlep};
pub use self::Waveform::{Sine, Saw, Square, Tri, WhiteNoise, PulseTrain};

/// Defines the messages that the Oscillator supports
#[derive(Clone, Copy, Debug)]
pub enum OscillatorMessage {
    /// Sets the frequency in Hz
    SetFreq(f32),
    /// Sets the waveform type
    SetWaveform(Waveform),
}

/// Antialiasing method for certain waveforms.
///
/// Aliased waveforms will use naive methods that produce aliasing.
/// PolyBLEP (Polynomial Bandlimited Step) uses offsets to round off sharp edges
/// and reduce aliasing.
#[derive(Clone, Copy, Debug)]
pub enum AntialiasType {
    /// Naive, aliasing waveforms.
    Aliased,
    /// Antialiasing using PolyBLEP.
    PolyBlep
}

/// Oscillator waveforms.
///
/// `Saw`, `Square`, and `Tri` provides either aliased waveforms, or antialiased
/// waveforms using PolyBLEP. Aliased waveformsare useful for control signals,
/// but not for raw audio signals. For audible signals, instead used the
/// corresponding `PolyBlep` waveforms.
#[derive(Clone, Copy, Debug)]
pub enum Waveform {
    Sine,
    Saw(AntialiasType),
    Square(AntialiasType),
    Tri(AntialiasType),
    WhiteNoise,
    PulseTrain
}

/// An oscillator that generates a periodic waveform.
pub struct Oscillator {
    waveform: Waveform,
    phase: f32,
    phase_delta: f32,
    last_sample: Sample,
}

impl Oscillator {
    /// Returns an oscillator with the specified waveform at the specified
    /// frequency, in Hz.
    pub fn new(waveform: Waveform, freq: f32) -> Oscillator {
        Oscillator {
            waveform: waveform,
            phase: 0.0,
            phase_delta: freq*2.0*PI/(SAMPLE_RATE as f32),
            last_sample: 0.0
        }
    }

    /// Applies the message to the oscillator
    pub fn handle_message(&mut self, msg: OscillatorMessage) {
        match msg {
            OscillatorMessage::SetFreq(freq) => {
                self.phase_delta = freq*2.0*PI/(SAMPLE_RATE as f32);
            },
            OscillatorMessage::SetWaveform(waveform) => {
                self.waveform = waveform;
            }
        }
    }
}

impl AudioDevice for Oscillator {
    fn num_inputs(&self) -> usize {
        0
    }

    fn num_outputs(&self) -> usize {
        1
    }

    fn tick(&mut self, _: Time, _: &[Sample], outputs: &mut[Sample]) {
        // Tick the phase
        self.phase += self.phase_delta;
        if self.phase >= 2.0*PI {
            self.phase -= 2.0*PI;
        }

        // Compute the next sample
        let s: Sample = match self.waveform.clone() {
            Sine => self.phase.sin(),
            Saw(aa) => {
                let mut out = self.phase/PI -1.0;
                match aa {
                    PolyBlep => {
                        out -= poly_belp_offset(self.phase/(2.0*PI),
                                                self.phase_delta/(2.0*PI));
                    },
                    _ => ()
                }
                out
            },
            Square(aa) => {
                let mut out = if self.phase < PI { 1.0 } else { -1.0 };
                match aa {
                    PolyBlep => {
                        // two discontinuities, at rising and falling edges
                        out += poly_belp_offset(self.phase/(2.0*PI),
                                                self.phase_delta/(2.0*PI));
                        out -= poly_belp_offset(fmod(self.phase/(2.0*PI)+0.5,
                                                     1.0),
                                                self.phase_delta/(2.0*PI));
                    },
                    _ => ()
                }
                out
            },
            Tri(aa) => {
                // Compute a square wave signal
                let mut out = if self.phase < PI { 1.0 } else { -1.0 };
                match aa {
                    PolyBlep => {
                        // two discontinuities, at rising and falling edges
                        out += poly_belp_offset(self.phase/(2.0*PI),
                                                self.phase_delta/(2.0*PI));
                        out -= poly_belp_offset(fmod(self.phase/(2.0*PI)+0.5,
                                                     1.0),
                                                self.phase_delta/(2.0*PI));
                    },
                    _ => ()
                }

                // Perform leaky integration
                self.phase_delta*out + (1.0-self.phase_delta)*self.last_sample
            },
            WhiteNoise => 2.0*random::<f32>() - 1.0,
            PulseTrain => {
                // If we wrapped around...
                if self.phase < self.phase_delta {
                    1.0
                } else {
                    0.0
                }
            }
        };

        // Push the sample out
        outputs[0] = s;
    }
}

/// Floating point modulus
fn fmod(n: f32, base: f32) -> f32 {
    assert!(base > 0.0);
    let mut out = n;
    while out < 0.0 {
        out += base;
    }
    while out >= base {
        out -= base;
    }
    out
}

/// Computes an offset for PolyBLEP antialiasing
///
/// `t` should be the current waveform phase, normalized
/// `dt` should be the change in phase for one sample time, normalized
fn poly_belp_offset(t: f32, dt: f32) -> f32 {
    if t < dt { // t ~= 0
        let t = t / dt;
        -t*t + 2.0*t - 1.0
    } else if t > 1.0-dt { // t ~= 1
        let t = (t-1.0) / dt;
        t*t + 2.0*t + 1.0
    } else {
        0.0
    }
}

// A couple of basic unit tests...
#[cfg(test)]
mod test {
    use utils::helpers::flt_eq;
    static EPSILON: f32 = 1e-6;

    /// Tests fmod with many values
    #[test]
    fn test_fmod() {
        use super::fmod;

        // negatives...
        assert!(flt_eq(fmod(-1.5, 1.0), 0.5, EPSILON));
        assert!(flt_eq(fmod(-1.0, 1.0), 0.0, EPSILON));
        assert!(flt_eq(fmod(-0.5, 1.0), 0.5, EPSILON));

        // in range...
        assert!(flt_eq(fmod(0.0, 1.0), 0.0, EPSILON));
        assert!(flt_eq(fmod(0.5, 1.0), 0.5, EPSILON));
        assert!(flt_eq(fmod(0.9, 1.0), 0.9, EPSILON));

        // above...
        assert!(flt_eq(fmod(1.0, 1.0), 0.0, EPSILON));
        assert!(flt_eq(fmod(1.5, 1.0), 0.5, EPSILON));
        assert!(flt_eq(fmod(2.0, 1.0), 0.0, EPSILON));
        assert!(flt_eq(fmod(2.5, 1.0), 0.5, EPSILON));

        // different base...
        assert!(flt_eq(fmod(-0.5, 0.9), 0.4, EPSILON));
        assert!(flt_eq(fmod(0.0, 0.9), 0.0, EPSILON));
        assert!(flt_eq(fmod(0.5, 0.9), 0.5, EPSILON));
        assert!(flt_eq(fmod(0.9, 0.9), 0.0, EPSILON));
        assert!(flt_eq(fmod(1.0, 0.9), 0.1, EPSILON));
    }

    /// Tests square wave
    #[test]
    fn test_naive_square() {
        use super::{AntialiasType, Waveform, Oscillator};
        use types::AudioDevice;
        let mut osc = Oscillator::new(Waveform::Square(AntialiasType::Aliased),
                                      4410.0);
        let input = vec![];
        let mut output = vec![0.0];

        osc.tick(0, &input, &mut output); assert!(output[0] == 1.0);
        osc.tick(1, &input, &mut output); assert!(output[0] == 1.0);
        osc.tick(2, &input, &mut output); assert!(output[0] == 1.0);
        osc.tick(3, &input, &mut output); assert!(output[0] == 1.0);
        osc.tick(4, &input, &mut output); assert!(output[0] == -1.0);
        osc.tick(5, &input, &mut output); assert!(output[0] == -1.0);
        osc.tick(6, &input, &mut output); assert!(output[0] == -1.0);
        osc.tick(7, &input, &mut output); assert!(output[0] == -1.0);
        osc.tick(8, &input, &mut output); assert!(output[0] == -1.0);
    }

    /// Tests saw wave
    #[test]
    fn test_naive_saw() {
        use super::{AntialiasType, Waveform, Oscillator};
        use types::AudioDevice;
        let mut osc = Oscillator::new(Waveform::Saw(AntialiasType::Aliased),
                                      4410.0);
        let input = vec![];
        let mut output = vec![0.0];

        osc.tick(0, &input, &mut output); assert!(flt_eq(output[0], -0.8, EPSILON));
        osc.tick(1, &input, &mut output); assert!(flt_eq(output[0], -0.6, EPSILON));
        osc.tick(2, &input, &mut output); assert!(flt_eq(output[0], -0.4, EPSILON));
        osc.tick(3, &input, &mut output); assert!(flt_eq(output[0], -0.2, EPSILON));
        osc.tick(4, &input, &mut output); assert!(flt_eq(output[0], 0.0, EPSILON));
        osc.tick(5, &input, &mut output); assert!(flt_eq(output[0], 0.2, EPSILON));
        osc.tick(6, &input, &mut output); assert!(flt_eq(output[0], 0.4, EPSILON));
        osc.tick(7, &input, &mut output); assert!(flt_eq(output[0], 0.6, EPSILON));
        osc.tick(8, &input, &mut output); assert!(flt_eq(output[0], 0.8, EPSILON));
    }
}
<|endoftext|>


<commit_before>use std::mem;
use std::ptr;
use std::cell::RefCell;
use std::sync::mpsc::Sender;
use std::sync::{Arc, Mutex};
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;

use WindowAttributes;
use CursorState;
use Event;
use super::event;
use super::WindowState;

use user32;
use shell32;
use winapi;

/// There's no parameters passed to the callback function, so it needs to get
/// its context (the HWND, the Sender for events, etc.) stashed in
/// a thread-local variable.
thread_local!(pub static CONTEXT_STASH: RefCell<Option<ThreadLocalData>> = RefCell::new(None));

pub struct ThreadLocalData {
    pub win: winapi::HWND,
    pub sender: Sender<Event>,
    pub window_state: Arc<Mutex<WindowState>>
}

struct MinMaxInfo {
    reserved: winapi::POINT, // Do not use/change
    max_size: winapi::POINT,
    max_position: winapi::POINT,
    min_track: winapi::POINT,
    max_track: winapi::POINT
}

/// Checks that the window is the good one, and if so send the event to it.
fn send_event(input_window: winapi::HWND, event: Event) {
    CONTEXT_STASH.with(|context_stash| {
        let context_stash = context_stash.borrow();
        let stored = match *context_stash {
            None => return,
            Some(ref v) => v
        };

        let &ThreadLocalData { ref win, ref sender, .. } = stored;

        if win != &input_window {
            return;
        }

        sender.send(event).ok();  // ignoring if closed
    });
}

/// This is the callback that is called by `DispatchMessage` in the events loop.
///
/// Returning 0 tells the Win32 API that the message has been processed.
// FIXME: detect WM_DWMCOMPOSITIONCHANGED and call DwmEnableBlurBehindWindow if necessary
pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
                                       wparam: winapi::WPARAM, lparam: winapi::LPARAM)
                                       -> winapi::LRESULT
{
    match msg {
        winapi::WM_DESTROY => {
            use events::Event::Closed;

            CONTEXT_STASH.with(|context_stash| {
                let context_stash = context_stash.borrow();
                let stored = match *context_stash {
                    None => return,
                    Some(ref v) => v
                };

                let &ThreadLocalData { ref win, .. } = stored;

                if win == &window {
                    user32::PostQuitMessage(0);
                }
            });

            send_event(window, Closed);
            0
        },

        winapi::WM_ERASEBKGND => {
            1
        },

        winapi::WM_SIZE => {
            use events::Event::Resized;
            let w = winapi::LOWORD(lparam as winapi::DWORD) as u32;
            let h = winapi::HIWORD(lparam as winapi::DWORD) as u32;
            send_event(window, Resized(w, h));
            0
        },

        winapi::WM_MOVE => {
            use events::Event::Moved;
            let x = winapi::LOWORD(lparam as winapi::DWORD) as i32;
            let y = winapi::HIWORD(lparam as winapi::DWORD) as i32;
            send_event(window, Moved(x, y));
            0
        },

        winapi::WM_CHAR => {
            use std::mem;
            use events::Event::ReceivedCharacter;
            let chr: char = mem::transmute(wparam as u32);
            send_event(window, ReceivedCharacter(chr));
            0
        },

        // Prevents default windows menu hotkeys playing unwanted
        // "ding" sounds. Alternatively could check for WM_SYSCOMMAND
        // with wparam being SC_KEYMENU, but this may prevent some
        // other unwanted default hotkeys as well.
        winapi::WM_SYSCHAR => {
            0
        }

        winapi::WM_MOUSEMOVE => {
            use events::Event::MouseMoved;

            let x = winapi::GET_X_LPARAM(lparam) as i32;
            let y = winapi::GET_Y_LPARAM(lparam) as i32;

            send_event(window, MouseMoved(x, y));

            0
        },

        winapi::WM_MOUSEWHEEL => {
            use events::Event::MouseWheel;
            use events::MouseScrollDelta::LineDelta;
            use events::TouchPhase;

            let value = (wparam >> 16) as i16;
            let value = value as i32;
            let value = value as f32 / winapi::WHEEL_DELTA as f32;

            send_event(window, MouseWheel(LineDelta(0.0, value), TouchPhase::Moved));

            0
        },

        winapi::WM_KEYDOWN | winapi::WM_SYSKEYDOWN => {
            use events::Event::KeyboardInput;
            use events::ElementState::Pressed;
            if msg == winapi::WM_SYSKEYDOWN && wparam as i32 == winapi::VK_F4 {
                user32::DefWindowProcW(window, msg, wparam, lparam)
            } else {
                let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam);
                send_event(window, KeyboardInput(Pressed, scancode, vkey));
                0
            }
        },

        winapi::WM_KEYUP | winapi::WM_SYSKEYUP => {
            use events::Event::KeyboardInput;
            use events::ElementState::Released;
            let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam);
            send_event(window, KeyboardInput(Released, scancode, vkey));
            0
        },

        winapi::WM_LBUTTONDOWN => {
            use events::Event::MouseInput;
            use events::MouseButton::Left;
            use events::ElementState::Pressed;
            send_event(window, MouseInput(Pressed, Left, None));
            0
        },

        winapi::WM_LBUTTONUP => {
            use events::Event::MouseInput;
            use events::MouseButton::Left;
            use events::ElementState::Released;
            send_event(window, MouseInput(Released, Left, None));
            0
        },

        winapi::WM_RBUTTONDOWN => {
            use events::Event::MouseInput;
            use events::MouseButton::Right;
            use events::ElementState::Pressed;
            send_event(window, MouseInput(Pressed, Right, None));
            0
        },

        winapi::WM_RBUTTONUP => {
            use events::Event::MouseInput;
            use events::MouseButton::Right;
            use events::ElementState::Released;
            send_event(window, MouseInput(Released, Right, None));
            0
        },

        winapi::WM_MBUTTONDOWN => {
            use events::Event::MouseInput;
            use events::MouseButton::Middle;
            use events::ElementState::Pressed;
            send_event(window, MouseInput(Pressed, Middle, None));
            0
        },

        winapi::WM_MBUTTONUP => {
            use events::Event::MouseInput;
            use events::MouseButton::Middle;
            use events::ElementState::Released;
            send_event(window, MouseInput(Released, Middle, None));
            0
        },

        winapi::WM_XBUTTONDOWN => {
            use events::Event::MouseInput;
            use events::MouseButton::Other;
            use events::ElementState::Pressed;
            let xbutton = winapi::HIWORD(wparam as winapi::DWORD) as winapi::c_int; // waiting on PR for winapi to add GET_XBUTTON_WPARAM
            send_event(window, MouseInput(Pressed, Other(xbutton as u8)));
            0
        },

        winapi::WM_XBUTTONUP => {
            use events::Event::MouseInput;
            use events::MouseButton::Other;
            use events::ElementState::Released;
            let xbutton = winapi::HIWORD(wparam as winapi::DWORD) as winapi::c_int;
            send_event(window, MouseInput(Released, Other(xbutton as u8), None));
            0
        },

        winapi::WM_INPUT => {
            let mut data: winapi::RAWINPUT = mem::uninitialized();
            let mut data_size = mem::size_of::<winapi::RAWINPUT>() as winapi::UINT;
            user32::GetRawInputData(mem::transmute(lparam), winapi::RID_INPUT,
                                    mem::transmute(&mut data), &mut data_size,
                                    mem::size_of::<winapi::RAWINPUTHEADER>() as winapi::UINT);

            if data.header.dwType == winapi::RIM_TYPEMOUSE {
                let _x = data.mouse.lLastX;  // FIXME: this is not always the relative movement
                let _y = data.mouse.lLastY;
                // TODO:
                //send_event(window, Event::MouseRawMovement { x: x, y: y });

                0

            } else {
                user32::DefWindowProcW(window, msg, wparam, lparam)
            }
        },

        winapi::WM_SETFOCUS => {
            use events::Event::Focused;
            send_event(window, Focused(true));
            0
        },

        winapi::WM_KILLFOCUS => {
            use events::Event::Focused;
            send_event(window, Focused(false));
            0
        },

        winapi::WM_SETCURSOR => {
            CONTEXT_STASH.with(|context_stash| {
                let cstash = context_stash.borrow();
                let cstash = cstash.as_ref();
                // there's a very bizarre borrow checker bug
                // possibly related to rust-lang/rust/#23338
                let _cursor_state = if let Some(cstash) = cstash {
                    if let Ok(window_state) = cstash.window_state.lock() {
                        match window_state.cursor_state {
                            CursorState::Normal => {
                                user32::SetCursor(user32::LoadCursorW(
                                        ptr::null_mut(),
                                        window_state.cursor));
                            },
                            CursorState::Grab | CursorState::Hide => {
                                user32::SetCursor(ptr::null_mut());
                            }
                        }
                    }
                } else {
                    return
                };

//                let &ThreadLocalData { ref cursor_state, .. } = stored;
            });
            0
        },

        winapi::WM_DROPFILES => {
            use events::Event::DroppedFile;

            let hdrop = wparam as winapi::HDROP;
            let mut pathbuf: [u16; winapi::MAX_PATH] = mem::uninitialized();
            let num_drops = shell32::DragQueryFileW(hdrop, 0xFFFFFFFF, ptr::null_mut(), 0);

            for i in 0..num_drops {
                let nch = shell32::DragQueryFileW(hdrop, i, pathbuf.as_mut_ptr(),
                                                  winapi::MAX_PATH as u32) as usize;
                if nch > 0 {
                    send_event(window, DroppedFile(OsString::from_wide(&pathbuf[0..nch]).into()));
                }
            }

            shell32::DragFinish(hdrop);
            0
        },

        winapi::WM_GETMINMAXINFO => {
            let mut mmi = lparam as *mut MinMaxInfo;
            //(*mmi).max_position = winapi::POINT { x: -8, y: -8 }; // The upper left corner of the window if it were maximized on the primary monitor.
            //(*mmi).max_size = winapi::POINT { x: .., y: .. }; // The dimensions of the primary monitor.

            CONTEXT_STASH.with(|context_stash| {
                match context_stash.borrow().as_ref() {
                    Some(cstash) => {
                        let window_state = cstash.window_state.lock().unwrap();

                        match window_state.attributes.min_dimensions {
                            Some((width, height)) => {
                                (*mmi).min_track = winapi::POINT { x: width as i32, y: height as i32 };
                            },
                            None => { }
                        }

                        match window_state.attributes.max_dimensions {
                            Some((width, height)) => {
                                (*mmi).max_track = winapi::POINT { x: width as i32, y: height as i32 };
                            },
                            None => { }
                        }
                    },
                    None => { }
                }
            });
            0
        },

        x if x == *super::WAKEUP_MSG_ID => {
            use events::Event::Awakened;
            send_event(window, Awakened);
            0
        },

        _ => {
            user32::DefWindowProcW(window, msg, wparam, lparam)
        }
    }
}
<commit_msg>Fix Windows fallout from 51ec6bb29a1d967b2ba5abc79927ed52effe3885.<commit_after>use std::mem;
use std::ptr;
use std::cell::RefCell;
use std::sync::mpsc::Sender;
use std::sync::{Arc, Mutex};
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;

use WindowAttributes;
use CursorState;
use Event;
use super::event;
use super::WindowState;

use user32;
use shell32;
use winapi;

/// There's no parameters passed to the callback function, so it needs to get
/// its context (the HWND, the Sender for events, etc.) stashed in
/// a thread-local variable.
thread_local!(pub static CONTEXT_STASH: RefCell<Option<ThreadLocalData>> = RefCell::new(None));

pub struct ThreadLocalData {
    pub win: winapi::HWND,
    pub sender: Sender<Event>,
    pub window_state: Arc<Mutex<WindowState>>
}

struct MinMaxInfo {
    reserved: winapi::POINT, // Do not use/change
    max_size: winapi::POINT,
    max_position: winapi::POINT,
    min_track: winapi::POINT,
    max_track: winapi::POINT
}

/// Checks that the window is the good one, and if so send the event to it.
fn send_event(input_window: winapi::HWND, event: Event) {
    CONTEXT_STASH.with(|context_stash| {
        let context_stash = context_stash.borrow();
        let stored = match *context_stash {
            None => return,
            Some(ref v) => v
        };

        let &ThreadLocalData { ref win, ref sender, .. } = stored;

        if win != &input_window {
            return;
        }

        sender.send(event).ok();  // ignoring if closed
    });
}

/// This is the callback that is called by `DispatchMessage` in the events loop.
///
/// Returning 0 tells the Win32 API that the message has been processed.
// FIXME: detect WM_DWMCOMPOSITIONCHANGED and call DwmEnableBlurBehindWindow if necessary
pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
                                       wparam: winapi::WPARAM, lparam: winapi::LPARAM)
                                       -> winapi::LRESULT
{
    match msg {
        winapi::WM_DESTROY => {
            use events::Event::Closed;

            CONTEXT_STASH.with(|context_stash| {
                let context_stash = context_stash.borrow();
                let stored = match *context_stash {
                    None => return,
                    Some(ref v) => v
                };

                let &ThreadLocalData { ref win, .. } = stored;

                if win == &window {
                    user32::PostQuitMessage(0);
                }
            });

            send_event(window, Closed);
            0
        },

        winapi::WM_ERASEBKGND => {
            1
        },

        winapi::WM_SIZE => {
            use events::Event::Resized;
            let w = winapi::LOWORD(lparam as winapi::DWORD) as u32;
            let h = winapi::HIWORD(lparam as winapi::DWORD) as u32;
            send_event(window, Resized(w, h));
            0
        },

        winapi::WM_MOVE => {
            use events::Event::Moved;
            let x = winapi::LOWORD(lparam as winapi::DWORD) as i32;
            let y = winapi::HIWORD(lparam as winapi::DWORD) as i32;
            send_event(window, Moved(x, y));
            0
        },

        winapi::WM_CHAR => {
            use std::mem;
            use events::Event::ReceivedCharacter;
            let chr: char = mem::transmute(wparam as u32);
            send_event(window, ReceivedCharacter(chr));
            0
        },

        // Prevents default windows menu hotkeys playing unwanted
        // "ding" sounds. Alternatively could check for WM_SYSCOMMAND
        // with wparam being SC_KEYMENU, but this may prevent some
        // other unwanted default hotkeys as well.
        winapi::WM_SYSCHAR => {
            0
        }

        winapi::WM_MOUSEMOVE => {
            use events::Event::MouseMoved;

            let x = winapi::GET_X_LPARAM(lparam) as i32;
            let y = winapi::GET_Y_LPARAM(lparam) as i32;

            send_event(window, MouseMoved(x, y));

            0
        },

        winapi::WM_MOUSEWHEEL => {
            use events::Event::MouseWheel;
            use events::MouseScrollDelta::LineDelta;
            use events::TouchPhase;

            let value = (wparam >> 16) as i16;
            let value = value as i32;
            let value = value as f32 / winapi::WHEEL_DELTA as f32;

            send_event(window, MouseWheel(LineDelta(0.0, value), TouchPhase::Moved));

            0
        },

        winapi::WM_KEYDOWN | winapi::WM_SYSKEYDOWN => {
            use events::Event::KeyboardInput;
            use events::ElementState::Pressed;
            if msg == winapi::WM_SYSKEYDOWN && wparam as i32 == winapi::VK_F4 {
                user32::DefWindowProcW(window, msg, wparam, lparam)
            } else {
                let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam);
                send_event(window, KeyboardInput(Pressed, scancode, vkey));
                0
            }
        },

        winapi::WM_KEYUP | winapi::WM_SYSKEYUP => {
            use events::Event::KeyboardInput;
            use events::ElementState::Released;
            let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam);
            send_event(window, KeyboardInput(Released, scancode, vkey));
            0
        },

        winapi::WM_LBUTTONDOWN => {
            use events::Event::MouseInput;
            use events::MouseButton::Left;
            use events::ElementState::Pressed;
            send_event(window, MouseInput(Pressed, Left, None));
            0
        },

        winapi::WM_LBUTTONUP => {
            use events::Event::MouseInput;
            use events::MouseButton::Left;
            use events::ElementState::Released;
            send_event(window, MouseInput(Released, Left, None));
            0
        },

        winapi::WM_RBUTTONDOWN => {
            use events::Event::MouseInput;
            use events::MouseButton::Right;
            use events::ElementState::Pressed;
            send_event(window, MouseInput(Pressed, Right, None));
            0
        },

        winapi::WM_RBUTTONUP => {
            use events::Event::MouseInput;
            use events::MouseButton::Right;
            use events::ElementState::Released;
            send_event(window, MouseInput(Released, Right, None));
            0
        },

        winapi::WM_MBUTTONDOWN => {
            use events::Event::MouseInput;
            use events::MouseButton::Middle;
            use events::ElementState::Pressed;
            send_event(window, MouseInput(Pressed, Middle, None));
            0
        },

        winapi::WM_MBUTTONUP => {
            use events::Event::MouseInput;
            use events::MouseButton::Middle;
            use events::ElementState::Released;
            send_event(window, MouseInput(Released, Middle, None));
            0
        },

        winapi::WM_XBUTTONDOWN => {
            use events::Event::MouseInput;
            use events::MouseButton::Other;
            use events::ElementState::Pressed;
            let xbutton = winapi::HIWORD(wparam as winapi::DWORD) as winapi::c_int; // waiting on PR for winapi to add GET_XBUTTON_WPARAM
            send_event(window, MouseInput(Pressed, Other(xbutton as u8), None));
            0
        },

        winapi::WM_XBUTTONUP => {
            use events::Event::MouseInput;
            use events::MouseButton::Other;
            use events::ElementState::Released;
            let xbutton = winapi::HIWORD(wparam as winapi::DWORD) as winapi::c_int;
            send_event(window, MouseInput(Released, Other(xbutton as u8), None));
            0
        },

        winapi::WM_INPUT => {
            let mut data: winapi::RAWINPUT = mem::uninitialized();
            let mut data_size = mem::size_of::<winapi::RAWINPUT>() as winapi::UINT;
            user32::GetRawInputData(mem::transmute(lparam), winapi::RID_INPUT,
                                    mem::transmute(&mut data), &mut data_size,
                                    mem::size_of::<winapi::RAWINPUTHEADER>() as winapi::UINT);

            if data.header.dwType == winapi::RIM_TYPEMOUSE {
                let _x = data.mouse.lLastX;  // FIXME: this is not always the relative movement
                let _y = data.mouse.lLastY;
                // TODO:
                //send_event(window, Event::MouseRawMovement { x: x, y: y });

                0

            } else {
                user32::DefWindowProcW(window, msg, wparam, lparam)
            }
        },

        winapi::WM_SETFOCUS => {
            use events::Event::Focused;
            send_event(window, Focused(true));
            0
        },

        winapi::WM_KILLFOCUS => {
            use events::Event::Focused;
            send_event(window, Focused(false));
            0
        },

        winapi::WM_SETCURSOR => {
            CONTEXT_STASH.with(|context_stash| {
                let cstash = context_stash.borrow();
                let cstash = cstash.as_ref();
                // there's a very bizarre borrow checker bug
                // possibly related to rust-lang/rust/#23338
                let _cursor_state = if let Some(cstash) = cstash {
                    if let Ok(window_state) = cstash.window_state.lock() {
                        match window_state.cursor_state {
                            CursorState::Normal => {
                                user32::SetCursor(user32::LoadCursorW(
                                        ptr::null_mut(),
                                        window_state.cursor));
                            },
                            CursorState::Grab | CursorState::Hide => {
                                user32::SetCursor(ptr::null_mut());
                            }
                        }
                    }
                } else {
                    return
                };

//                let &ThreadLocalData { ref cursor_state, .. } = stored;
            });
            0
        },

        winapi::WM_DROPFILES => {
            use events::Event::DroppedFile;

            let hdrop = wparam as winapi::HDROP;
            let mut pathbuf: [u16; winapi::MAX_PATH] = mem::uninitialized();
            let num_drops = shell32::DragQueryFileW(hdrop, 0xFFFFFFFF, ptr::null_mut(), 0);

            for i in 0..num_drops {
                let nch = shell32::DragQueryFileW(hdrop, i, pathbuf.as_mut_ptr(),
                                                  winapi::MAX_PATH as u32) as usize;
                if nch > 0 {
                    send_event(window, DroppedFile(OsString::from_wide(&pathbuf[0..nch]).into()));
                }
            }

            shell32::DragFinish(hdrop);
            0
        },

        winapi::WM_GETMINMAXINFO => {
            let mut mmi = lparam as *mut MinMaxInfo;
            //(*mmi).max_position = winapi::POINT { x: -8, y: -8 }; // The upper left corner of the window if it were maximized on the primary monitor.
            //(*mmi).max_size = winapi::POINT { x: .., y: .. }; // The dimensions of the primary monitor.

            CONTEXT_STASH.with(|context_stash| {
                match context_stash.borrow().as_ref() {
                    Some(cstash) => {
                        let window_state = cstash.window_state.lock().unwrap();

                        match window_state.attributes.min_dimensions {
                            Some((width, height)) => {
                                (*mmi).min_track = winapi::POINT { x: width as i32, y: height as i32 };
                            },
                            None => { }
                        }

                        match window_state.attributes.max_dimensions {
                            Some((width, height)) => {
                                (*mmi).max_track = winapi::POINT { x: width as i32, y: height as i32 };
                            },
                            None => { }
                        }
                    },
                    None => { }
                }
            });
            0
        },

        x if x == *super::WAKEUP_MSG_ID => {
            use events::Event::Awakened;
            send_event(window, Awakened);
            0
        },

        _ => {
            user32::DefWindowProcW(window, msg, wparam, lparam)
        }
    }
}
<|endoftext|>


<commit_before>pub mod ast;
pub mod compiler;
pub mod env;
pub mod errors;
pub mod grammar;
pub mod instructions;
pub mod memory;
pub mod parser;
pub mod primitives;
pub mod repl;
pub mod runtime;
pub mod values;
pub mod vm;
<commit_msg>Removed pub from externally unused modules.<commit_after>mod ast;
mod compiler;
mod env;
pub mod errors;
mod grammar;
mod instructions;
mod memory;
mod parser;
mod primitives;
pub mod repl;
pub mod runtime;
pub mod values;
mod vm;
<|endoftext|>


<commit_before>use std::mem;

use libc::{c_ushort, c_uchar, size_t, c_uint, malloc, free, c_void};

use symbols::{ZOPFLI_CACHE_LENGTH};

// Cache used by ZopfliFindLongestMatch to remember previously found length/dist
// values.
// This is needed because the squeeze runs will ask these values multiple times for
// the same position.
// Uses large amounts of memory, since it has to remember the distance belonging
// to every possible shorter-than-the-best length (the so called "sublen" array).
pub struct ZopfliLongestMatchCache {
    length: *mut c_ushort,
    dist: *mut c_ushort,
    sublen: *mut c_uchar,
}

impl ZopfliLongestMatchCache {
    pub fn new(blocksize: size_t) -> ZopfliLongestMatchCache {
        unsafe {
            let lmc = ZopfliLongestMatchCache {
                length: malloc(mem::size_of::<c_ushort>() as size_t * blocksize) as *mut c_ushort,
                dist: malloc(mem::size_of::<c_ushort>() as size_t * blocksize) as *mut c_ushort,
                /* Rather large amount of memory. */
                sublen: malloc(ZOPFLI_CACHE_LENGTH * 3 * blocksize) as *mut c_uchar,
            };
            /* length > 0 and dist 0 is invalid combination, which indicates on purpose
            that this cache value is not filled in yet. */
            for i in 0..blocksize as isize {
                *lmc.length.offset(i) = 1;
                *lmc.dist.offset(i) = 0;
            }

            for i in 0..(ZOPFLI_CACHE_LENGTH * blocksize * 3) as isize {
                *lmc.sublen.offset(i) = 0;
            }
            lmc
        }
    }
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliInitCache(blocksize: size_t) -> *mut ZopfliLongestMatchCache {
    Box::into_raw(Box::new(ZopfliLongestMatchCache::new(blocksize)))
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliCleanCache(lmc_ptr: *mut ZopfliLongestMatchCache) {
    let lmc = unsafe {
        assert!(!lmc_ptr.is_null());
        &mut *lmc_ptr
    };
    unsafe {
        free(lmc.length as *mut c_void);
        free(lmc.dist as *mut c_void);
        free(lmc.sublen as *mut c_void);
    }
}

/// Returns the length up to which could be stored in the cache.
#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliMaxCachedSublen(lmc_ptr: *mut ZopfliLongestMatchCache, pos: size_t, _length: size_t) -> c_uint {

    let lmc = unsafe {
        assert!(!lmc_ptr.is_null());
        &mut *lmc_ptr
    };

    unsafe {
        let cache = lmc.sublen.offset((ZOPFLI_CACHE_LENGTH * pos * 3) as isize);
        if *cache.offset(1) == 0 && *cache.offset(2) == 0 {
            return 0;  // No sublen cached.
        }
        *cache.offset(((ZOPFLI_CACHE_LENGTH - 1) * 3) as isize) as c_uint + 3
    }
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliCacheToSublen(lmc_ptr: *mut ZopfliLongestMatchCache, pos: size_t, length: size_t, sublen: *mut c_ushort) {
    let lmc = unsafe {
        assert!(!lmc_ptr.is_null());
        &mut *lmc_ptr
    };

    let maxlength = ZopfliMaxCachedSublen(lmc_ptr, pos, length);
    let mut prevlength = 0;

    if length < 3 {
        return;
    }

    unsafe {
        let cache = lmc.sublen.offset((ZOPFLI_CACHE_LENGTH * pos * 3) as isize);

        for j in 0..ZOPFLI_CACHE_LENGTH {
            let length = *cache.offset((j * 3) as isize) as c_uint + 3;
            let dist = *cache.offset((j * 3 + 1) as isize) as c_ushort + 256 * *cache.offset((j * 3 + 2) as isize) as c_ushort;
            let mut i = prevlength;
            while i <= length {
                *sublen.offset(i as isize) = dist;
                i += 1;
            }
            if length == maxlength {
                break;
            }
            prevlength = length + 1;
        }
    }
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliSublenToCache(sublen: *mut c_ushort, pos: size_t, length: size_t, lmc_ptr: *mut ZopfliLongestMatchCache) {
    let lmc = unsafe {
        assert!(!lmc_ptr.is_null());
        &mut *lmc_ptr
    };

    let mut j: isize = 0;
    let mut bestlength: c_uint = 0;

    if length < 3 {
        return;
    }

    unsafe {

        let cache = lmc.sublen.offset((ZOPFLI_CACHE_LENGTH * pos * 3) as isize);

        let mut i: isize = 3;
        while i <= length as isize {
            if i == length as isize || *sublen.offset(i) != *sublen.offset(i + 1) {
                *cache.offset(j * 3) = (i - 3) as c_uchar;
                *cache.offset(j * 3 + 1) = (*sublen.offset(i)).wrapping_rem(256) as c_uchar;
                *cache.offset(j * 3 + 2) = ((*sublen.offset(i) >> 8)).wrapping_rem(256) as c_uchar;
                bestlength = i as c_uint;
                j += 1;
                if j >= ZOPFLI_CACHE_LENGTH as isize {
                    break;
                }
            }
            i += 1;
        }

        if j < ZOPFLI_CACHE_LENGTH as isize {
            assert!(bestlength == length as c_uint);
            *cache.offset(((ZOPFLI_CACHE_LENGTH - 1) * 3) as isize) = (bestlength - 3) as c_uchar;
        } else {
            assert!(bestlength <= length as c_uint);
        }
        assert!(bestlength == ZopfliMaxCachedSublen(lmc, pos, length));
    }
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliCacheLengthAt(lmc_ptr: *mut ZopfliLongestMatchCache, pos: size_t) -> c_ushort {
    let lmc = unsafe {
        assert!(!lmc_ptr.is_null());
        &mut *lmc_ptr
    };
    unsafe {
        *lmc.length.offset(pos as isize)
    }
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliCacheDistAt(lmc_ptr: *mut ZopfliLongestMatchCache, pos: size_t) -> c_ushort {
    let lmc = unsafe {
        assert!(!lmc_ptr.is_null());
        &mut *lmc_ptr
    };
    unsafe {
        *lmc.dist.offset(pos as isize)
    }
}
<commit_msg>Refactor the way the cache offset is calculated/used<commit_after>use std::mem;

use libc::{c_ushort, c_uchar, size_t, c_uint, malloc, free, c_void};

use symbols::{ZOPFLI_CACHE_LENGTH};

// Cache used by ZopfliFindLongestMatch to remember previously found length/dist
// values.
// This is needed because the squeeze runs will ask these values multiple times for
// the same position.
// Uses large amounts of memory, since it has to remember the distance belonging
// to every possible shorter-than-the-best length (the so called "sublen" array).
pub struct ZopfliLongestMatchCache {
    length: *mut c_ushort,
    dist: *mut c_ushort,
    sublen: *mut c_uchar,
}

impl ZopfliLongestMatchCache {
    pub fn new(blocksize: size_t) -> ZopfliLongestMatchCache {
        unsafe {
            let lmc = ZopfliLongestMatchCache {
                length: malloc(mem::size_of::<c_ushort>() as size_t * blocksize) as *mut c_ushort,
                dist: malloc(mem::size_of::<c_ushort>() as size_t * blocksize) as *mut c_ushort,
                /* Rather large amount of memory. */
                sublen: malloc(ZOPFLI_CACHE_LENGTH * 3 * blocksize) as *mut c_uchar,
            };
            /* length > 0 and dist 0 is invalid combination, which indicates on purpose
            that this cache value is not filled in yet. */
            for i in 0..blocksize as isize {
                *lmc.length.offset(i) = 1;
                *lmc.dist.offset(i) = 0;
            }

            for i in 0..(ZOPFLI_CACHE_LENGTH * blocksize * 3) as isize {
                *lmc.sublen.offset(i) = 0;
            }
            lmc
        }
    }
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliInitCache(blocksize: size_t) -> *mut ZopfliLongestMatchCache {
    Box::into_raw(Box::new(ZopfliLongestMatchCache::new(blocksize)))
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliCleanCache(lmc_ptr: *mut ZopfliLongestMatchCache) {
    let lmc = unsafe {
        assert!(!lmc_ptr.is_null());
        &mut *lmc_ptr
    };
    unsafe {
        free(lmc.length as *mut c_void);
        free(lmc.dist as *mut c_void);
        free(lmc.sublen as *mut c_void);
    }
}

/// Returns the length up to which could be stored in the cache.
#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliMaxCachedSublen(lmc_ptr: *mut ZopfliLongestMatchCache, pos: size_t, _length: size_t) -> c_uint {

    let lmc = unsafe {
        assert!(!lmc_ptr.is_null());
        &mut *lmc_ptr
    };

    unsafe {
        let start = (ZOPFLI_CACHE_LENGTH * pos * 3) as isize;
        if *lmc.sublen.offset(start + 1) == 0 && *lmc.sublen.offset(start + 2) == 0 {
            return 0;  // No sublen cached.
        }
        *lmc.sublen.offset(start + ((ZOPFLI_CACHE_LENGTH - 1) * 3) as isize) as c_uint + 3
    }
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliCacheToSublen(lmc_ptr: *mut ZopfliLongestMatchCache, pos: size_t, length: size_t, sublen: *mut c_ushort) {
    let lmc = unsafe {
        assert!(!lmc_ptr.is_null());
        &mut *lmc_ptr
    };

    let maxlength = ZopfliMaxCachedSublen(lmc_ptr, pos, length);
    let mut prevlength = 0;

    if length < 3 {
        return;
    }

    unsafe {
        let start = (ZOPFLI_CACHE_LENGTH * pos * 3) as isize;

        for j in 0..ZOPFLI_CACHE_LENGTH {
            let length = *lmc.sublen.offset(start + (j * 3) as isize) as c_uint + 3;
            let dist = *lmc.sublen.offset(start + (j * 3 + 1) as isize) as c_ushort + 256 * *lmc.sublen.offset(start + (j * 3 + 2) as isize) as c_ushort;

            let mut i = prevlength;
            while i <= length {
                *sublen.offset(i as isize) = dist;
                i += 1;
            }
            if length == maxlength {
                break;
            }
            prevlength = length + 1;
        }
    }
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliSublenToCache(sublen: *mut c_ushort, pos: size_t, length: size_t, lmc_ptr: *mut ZopfliLongestMatchCache) {
    let lmc = unsafe {
        assert!(!lmc_ptr.is_null());
        &mut *lmc_ptr
    };

    let mut j: isize = 0;
    let mut bestlength: c_uint = 0;

    if length < 3 {
        return;
    }

    unsafe {
        let start = (ZOPFLI_CACHE_LENGTH * pos * 3) as isize;

        let mut i: isize = 3;
        while i <= length as isize {
            if i == length as isize || *sublen.offset(i) != *sublen.offset(i + 1) {
                *lmc.sublen.offset(start + (j * 3) as isize) = (i - 3) as c_uchar;
                *lmc.sublen.offset(start + (j * 3 + 1) as isize) = (*sublen.offset(i)).wrapping_rem(256) as c_uchar;
                *lmc.sublen.offset(start + (j * 3 + 2) as isize) = ((*sublen.offset(i) >> 8)).wrapping_rem(256) as c_uchar;
                bestlength = i as c_uint;
                j += 1;
                if j >= ZOPFLI_CACHE_LENGTH as isize {
                    break;
                }
            }
            i += 1;
        }

        if j < ZOPFLI_CACHE_LENGTH as isize {
            assert!(bestlength == length as c_uint);
            *lmc.sublen.offset(start + ((ZOPFLI_CACHE_LENGTH - 1) * 3) as isize) = (bestlength - 3) as c_uchar;
        } else {
            assert!(bestlength <= length as c_uint);
        }
        assert!(bestlength == ZopfliMaxCachedSublen(lmc, pos, length));
    }
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliCacheLengthAt(lmc_ptr: *mut ZopfliLongestMatchCache, pos: size_t) -> c_ushort {
    let lmc = unsafe {
        assert!(!lmc_ptr.is_null());
        &mut *lmc_ptr
    };
    unsafe {
        *lmc.length.offset(pos as isize)
    }
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliCacheDistAt(lmc_ptr: *mut ZopfliLongestMatchCache, pos: size_t) -> c_ushort {
    let lmc = unsafe {
        assert!(!lmc_ptr.is_null());
        &mut *lmc_ptr
    };
    unsafe {
        *lmc.dist.offset(pos as isize)
    }
}
<|endoftext|>


<commit_before>use log::trace;
use parking_lot::{Mutex, MutexGuard};

use std::{
  collections::VecDeque,
  fmt,
  sync::Arc,
};

use crate::{
  BasicProperties,
  message::Delivery,
  types::ShortString,
  wait::NotifyReady,
};

pub trait ConsumerDelegate: fmt::Debug + Send + Sync {
  fn new_delivery(&self, delivery: Delivery);
  fn drop_prefetched_messages(&self);
  fn cancel(&self);
}

#[derive(Clone)]
pub struct Consumer {
  inner: Arc<Mutex<ConsumerInner>>,
}

impl Consumer {
  pub(crate) fn new(consumer_tag: ShortString) -> Consumer {
    Consumer {
      inner: Arc::new(Mutex::new(ConsumerInner::new(consumer_tag))),
    }
  }

  pub fn inner(&self) -> MutexGuard<'_, ConsumerInner> {
    self.inner.lock()
  }

  pub fn set_delegate(&self, delegate: Box<dyn ConsumerDelegate>) {
    let mut inner = self.inner();
    for delivery in inner.deliveries.drain(..) {
      delegate.new_delivery(delivery);
    }
    inner.delegate = Some(delegate);
  }

  pub(crate) fn start_new_delivery(&mut self, delivery: Delivery) {
    self.inner().current_message = Some(delivery)
  }

  pub(crate) fn set_delivery_properties(&mut self, properties: BasicProperties) {
    if let Some(delivery) = self.inner().current_message.as_mut() {
      delivery.properties = properties;
    }
  }

  pub(crate) fn receive_delivery_content(&mut self, payload: Vec<u8>) {
    if let Some(delivery) = self.inner().current_message.as_mut() {
      delivery.receive_content(payload);
    }
  }

  pub(crate) fn new_delivery_complete(&mut self) {
    let mut inner = self.inner();
    if let Some(delivery) = inner.current_message.take() {
      inner.new_delivery(delivery);
    }
  }

  pub(crate) fn drop_prefetched_messages(&self) {
    self.inner().drop_prefetched_messages();
  }

  pub(crate) fn cancel(&self) {
    self.inner().cancel();
  }
}

pub struct ConsumerInner {
  current_message: Option<Delivery>,
  deliveries:      VecDeque<Delivery>,
  task:            Option<Box<dyn NotifyReady + Send>>,
  canceled:        bool,
  tag:             ShortString,
  delegate:        Option<Box<dyn ConsumerDelegate>>,
}

impl ConsumerInner {
  fn new(consumer_tag: ShortString) -> Self {
    Self {
      current_message: None,
      deliveries:      VecDeque::new(),
      task:            None,
      canceled:        false,
      tag:             consumer_tag,
      delegate:        None,
    }
  }

  pub fn next_delivery(&mut self) -> Option<Delivery> {
    self.deliveries.pop_front()
  }

  pub fn set_task(&mut self, task: Box<dyn NotifyReady + Send>) {
    self.task = Some(task);
  }

  pub fn has_task(&self) -> bool {
    self.task.is_some()
  }

  pub fn canceled(&self) -> bool {
    self.canceled
  }

  pub fn tag(&self) -> &ShortString {
    &self.tag
  }

  fn new_delivery(&mut self, delivery: Delivery) {
    trace!("new_delivery; consumer_tag={}", self.tag);
    if let Some(delegate) = self.delegate.as_ref() {
      delegate.new_delivery(delivery);
    } else {
      self.deliveries.push_back(delivery);
    }
    if let Some(task) = self.task.as_ref() {
      task.notify();
    }
  }

  fn drop_prefetched_messages(&mut self) {
    trace!("drop_prefetched_messages; consumer_tag={}", self.tag);
    if let Some(delegate) = self.delegate.as_ref() {
      delegate.drop_prefetched_messages();
    }
    self.deliveries.clear();
  }

  fn cancel(&mut self) {
    trace!("cancel; consumer_tag={}", self.tag);
    if let Some(delegate) = self.delegate.as_ref() {
      delegate.cancel();
    }
    self.deliveries.clear();
    self.canceled = true;
    self.task.take();
  }
}

impl fmt::Debug for Consumer {
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    write!(f, "Consumer({})", self.inner().tag())
  }
}

#[cfg(feature = "futures")]
mod futures {
  use super::*;

  use ::futures::stream::Stream;

  use std::{
    pin::Pin,
    task::{Context, Poll},
  };

  use crate::confirmation::futures::Watcher;

  impl Stream for Consumer {
    type Item = Delivery;

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
      trace!("consumer poll; polling transport");
      let mut inner = self.inner();
      trace!("consumer poll; acquired inner lock, consumer_tag={}", inner.tag());
      if !inner.has_task() {
        inner.set_task(Box::new(Watcher(cx.waker().clone())));
      }
      if let Some(delivery) = inner.next_delivery() {
        trace!("delivery; consumer_tag={}, delivery_tag={:?}", inner.tag(), delivery.delivery_tag);
        Poll::Ready(Some(delivery))
      } else if inner.canceled() {
        trace!("consumer canceled; consumer_tag={}", inner.tag());
        Poll::Ready(None)
      } else {
        trace!("delivery; status=NotReady, consumer_tag={}", inner.tag());
        Poll::Pending
      }
    }
  }
}
<commit_msg>ConsumerDelegate: do not require fmt::Debug<commit_after>use log::trace;
use parking_lot::{Mutex, MutexGuard};

use std::{
  collections::VecDeque,
  fmt,
  sync::Arc,
};

use crate::{
  BasicProperties,
  message::Delivery,
  types::ShortString,
  wait::NotifyReady,
};

pub trait ConsumerDelegate: Send + Sync {
  fn new_delivery(&self, delivery: Delivery);
  fn drop_prefetched_messages(&self);
  fn cancel(&self);
}

#[derive(Clone)]
pub struct Consumer {
  inner: Arc<Mutex<ConsumerInner>>,
}

impl Consumer {
  pub(crate) fn new(consumer_tag: ShortString) -> Consumer {
    Consumer {
      inner: Arc::new(Mutex::new(ConsumerInner::new(consumer_tag))),
    }
  }

  pub fn inner(&self) -> MutexGuard<'_, ConsumerInner> {
    self.inner.lock()
  }

  pub fn set_delegate(&self, delegate: Box<dyn ConsumerDelegate>) {
    let mut inner = self.inner();
    for delivery in inner.deliveries.drain(..) {
      delegate.new_delivery(delivery);
    }
    inner.delegate = Some(delegate);
  }

  pub(crate) fn start_new_delivery(&mut self, delivery: Delivery) {
    self.inner().current_message = Some(delivery)
  }

  pub(crate) fn set_delivery_properties(&mut self, properties: BasicProperties) {
    if let Some(delivery) = self.inner().current_message.as_mut() {
      delivery.properties = properties;
    }
  }

  pub(crate) fn receive_delivery_content(&mut self, payload: Vec<u8>) {
    if let Some(delivery) = self.inner().current_message.as_mut() {
      delivery.receive_content(payload);
    }
  }

  pub(crate) fn new_delivery_complete(&mut self) {
    let mut inner = self.inner();
    if let Some(delivery) = inner.current_message.take() {
      inner.new_delivery(delivery);
    }
  }

  pub(crate) fn drop_prefetched_messages(&self) {
    self.inner().drop_prefetched_messages();
  }

  pub(crate) fn cancel(&self) {
    self.inner().cancel();
  }
}

pub struct ConsumerInner {
  current_message: Option<Delivery>,
  deliveries:      VecDeque<Delivery>,
  task:            Option<Box<dyn NotifyReady + Send>>,
  canceled:        bool,
  tag:             ShortString,
  delegate:        Option<Box<dyn ConsumerDelegate>>,
}

impl fmt::Debug for ConsumerInner {
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    write!(f, "ConsumerInnder({})", self.tag)
  }
}

impl ConsumerInner {
  fn new(consumer_tag: ShortString) -> Self {
    Self {
      current_message: None,
      deliveries:      VecDeque::new(),
      task:            None,
      canceled:        false,
      tag:             consumer_tag,
      delegate:        None,
    }
  }

  pub fn next_delivery(&mut self) -> Option<Delivery> {
    self.deliveries.pop_front()
  }

  pub fn set_task(&mut self, task: Box<dyn NotifyReady + Send>) {
    self.task = Some(task);
  }

  pub fn has_task(&self) -> bool {
    self.task.is_some()
  }

  pub fn canceled(&self) -> bool {
    self.canceled
  }

  pub fn tag(&self) -> &ShortString {
    &self.tag
  }

  fn new_delivery(&mut self, delivery: Delivery) {
    trace!("new_delivery; consumer_tag={}", self.tag);
    if let Some(delegate) = self.delegate.as_ref() {
      delegate.new_delivery(delivery);
    } else {
      self.deliveries.push_back(delivery);
    }
    if let Some(task) = self.task.as_ref() {
      task.notify();
    }
  }

  fn drop_prefetched_messages(&mut self) {
    trace!("drop_prefetched_messages; consumer_tag={}", self.tag);
    if let Some(delegate) = self.delegate.as_ref() {
      delegate.drop_prefetched_messages();
    }
    self.deliveries.clear();
  }

  fn cancel(&mut self) {
    trace!("cancel; consumer_tag={}", self.tag);
    if let Some(delegate) = self.delegate.as_ref() {
      delegate.cancel();
    }
    self.deliveries.clear();
    self.canceled = true;
    self.task.take();
  }
}

impl fmt::Debug for Consumer {
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    write!(f, "Consumer({})", self.inner().tag())
  }
}

#[cfg(feature = "futures")]
mod futures {
  use super::*;

  use ::futures::stream::Stream;

  use std::{
    pin::Pin,
    task::{Context, Poll},
  };

  use crate::confirmation::futures::Watcher;

  impl Stream for Consumer {
    type Item = Delivery;

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
      trace!("consumer poll; polling transport");
      let mut inner = self.inner();
      trace!("consumer poll; acquired inner lock, consumer_tag={}", inner.tag());
      if !inner.has_task() {
        inner.set_task(Box::new(Watcher(cx.waker().clone())));
      }
      if let Some(delivery) = inner.next_delivery() {
        trace!("delivery; consumer_tag={}, delivery_tag={:?}", inner.tag(), delivery.delivery_tag);
        Poll::Ready(Some(delivery))
      } else if inner.canceled() {
        trace!("consumer canceled; consumer_tag={}", inner.tag());
        Poll::Ready(None)
      } else {
        trace!("delivery; status=NotReady, consumer_tag={}", inner.tag());
        Poll::Pending
      }
    }
  }
}
<|endoftext|>


<commit_before>#[cfg(feature="bigdecimal")]
mod bigdecimal {
    extern crate num_traits;
    extern crate num_bigint;
    extern crate num_integer;
    extern crate bigdecimal;

    use std::error::Error;
    use std::io::prelude::*;

    use pg::Pg;

    use self::num_traits::{Signed, Zero, ToPrimitive};
    use self::num_bigint::{Sign, BigInt, BigUint, ToBigInt};
    use self::num_integer::Integer;
    use self::bigdecimal::BigDecimal;

    use pg::data_types::PgNumeric;
    use types::{self, FromSql, ToSql, IsNull};

    impl ToSql<types::Numeric, Pg> for BigDecimal {
        fn to_sql<W: Write>(&self, out: &mut W) -> Result<IsNull, Box<Error + Send + Sync>> {
            use std::str::FromStr;

            // The encoding of the BigDecimal type for PostgreSQL is a bit complicated:
            // PostgreSQL expects the data in base-10000 (so two bytes per 10k),
            // and the decimal point should lie on a boundary (as per definition of "base-10000").

            // BigDecimal, internally, holds an int vector (base-256, one byte per byte),
            // and a base (u64, base-10) shift.

            // Therefore, we split up the encoding in three parts:
            // the sign, the (integer) part before the decimal, and the part after the decimal.

            let absolute = self.abs();

            let mut digits = vec![];
            let ten_k = BigInt::from(10000);
            let mut integer_part = absolute.to_bigint().expect("Can always take integer part of BigDecimal");
            let decimal_part = &absolute;
            let mut decimal_part = decimal_part - absolute.with_scale(0);
            // scale is the amount of digits to print. to_string() includes a "0.",
            // that's why the -2 is there.
            let scale = if decimal_part == Zero::zero() {
                0
            } else {
                decimal_part.to_string().len() as u16 - 2
            };
            let mut weight = 0;

            // Encode the integer part
            while ten_k < integer_part {
                weight += 1;
                // digit is integer_part REM 10_000
                let (div, digit) = integer_part.div_rem(&ten_k);
                digits.push(digit.to_u16().expect("digit < 10000, but cannot fit in i16") as i16);
                integer_part = div;
            }
            digits.push(integer_part.to_string().parse::<i16>().expect("digit < 10000, but cannot fit in i16"));

            digits.reverse();

            // Encode the decimal part
            let ten_k = BigDecimal::from_str("10000").expect("Could not parse into BigDecimal");
            while decimal_part != BigDecimal::zero() {
                decimal_part *= &ten_k;
                let digit = decimal_part.to_bigint().expect("Can always take integer part of BigDecimal");
                // This can be simplified when github.com/akubera/bigdecimal-rs/issues/13 gets
                // solved; decimal_part -= &digit; should suffice by then.
                decimal_part -= BigDecimal::new(digit.clone(), 0);
                digits.push(digit.to_u16().expect("digit < 10000, but cannot fit in i16") as i16);
            }

            let numeric = match self.sign() {
                Sign::Plus => PgNumeric::Positive {
                    digits, scale, weight
                },
                Sign::Minus => PgNumeric::Negative {
                    digits, scale, weight
                },
                Sign::NoSign => PgNumeric::Positive {
                    digits: vec![0],
                    scale: 0,
                    weight: 0,
                },
            };
            ToSql::<types::Numeric, Pg>::to_sql(&numeric, out)
        }
    }

    impl FromSql<types::Numeric, Pg> for BigDecimal {
        fn from_sql(numeric: Option<&[u8]>) -> Result<Self, Box<Error+Send+Sync>> {
            let (sign, weight, _, digits) = match PgNumeric::from_sql(numeric)? {
                PgNumeric::Positive { weight, scale, digits } => (Sign::Plus, weight, scale, digits),
                PgNumeric::Negative { weight, scale, digits } => (Sign::Minus, weight, scale, digits),
                PgNumeric::NaN => return Err(Box::from("NaN is not (yet) supported in BigDecimal")),
            };
            let mut result = BigUint::default();
            let count = digits.len() as i64;
            for digit in digits {
                result = result * BigUint::from(10_000u64);
                result = result + BigUint::from(digit as u64);
            }
            // First digit got factor 10_000^(digits.len() - 1), but should get 10_000^weight
            let correction_exp = 4 * ( (weight as i64) - count + 1);
            // FIXME: `scale` allows to drop some insignificant figures, which is currently unimplemented.
            // This means that e.g. PostgreSQL 0.01 will be interpreted as 0.0100
            let result = BigDecimal::new(BigInt::from_biguint(sign, result), -correction_exp);
            Ok(result)
        }
    }
}
<commit_msg>Cut T'Sql into pieces<commit_after>#[cfg(feature="bigdecimal")]
mod bigdecimal {
    extern crate num_traits;
    extern crate num_bigint;
    extern crate num_integer;
    extern crate bigdecimal;

    use std::error::Error;
    use std::io::prelude::*;

    use pg::Pg;

    use self::num_traits::{Signed, Zero, ToPrimitive};
    use self::num_bigint::{Sign, BigInt, BigUint, ToBigInt};
    use self::num_integer::Integer;
    use self::bigdecimal::BigDecimal;

    use pg::data_types::PgNumeric;
    use types::{self, FromSql, ToSql, IsNull};

    type Digits = Vec<i16>;

    fn bigdec_add_integer_part(digits: &mut Digits, absolute: &BigDecimal) -> i16 {
        let mut weight = 0;
        let ten_k = BigInt::from(10000);

        let mut integer_part = absolute.to_bigint().expect("Can always take integer part of BigDecimal");

        while ten_k < integer_part {
            weight += 1;
            // digit is integer_part REM 10_000
            let (div, digit) = integer_part.div_rem(&ten_k);
            digits.push(digit.to_u16().expect("digit < 10000, but cannot fit in i16") as i16);
            integer_part = div;
        }
        digits.push(integer_part.to_string().parse::<i16>().expect("digit < 10000, but cannot fit in i16"));

        digits.reverse();

        weight
    }

    fn bigdec_add_decimal_part(digits: &mut Digits, absolute: &BigDecimal) -> u16 {
        use std::str::FromStr;

        let ten_k = BigDecimal::from_str("10000").expect("Could not parse into BigDecimal");

        let decimal_part = absolute;
        let mut decimal_part = decimal_part - absolute.with_scale(0);
        // scale is the amount of digits to print. to_string() includes a "0.",
        // that's why the -2 is there.
        let scale = if decimal_part == Zero::zero() {
            0
        } else {
            decimal_part.to_string().len() as u16 - 2
        };

        while decimal_part != BigDecimal::zero() {
            decimal_part *= &ten_k;
            let digit = decimal_part.to_bigint().expect("Can always take integer part of BigDecimal");

            // This can be simplified when github.com/akubera/bigdecimal-rs/issues/13 gets
            // solved; decimal_part -= &digit; should suffice by then.
            decimal_part -= BigDecimal::new(digit.clone(), 0);
            digits.push(digit.to_u16().expect("digit < 10000, but cannot fit in i16") as i16);
        }

        scale
    }

    impl ToSql<types::Numeric, Pg> for BigDecimal {
        fn to_sql<W: Write>(&self, out: &mut W) -> Result<IsNull, Box<Error + Send + Sync>> {
            // The encoding of the BigDecimal type for PostgreSQL is a bit complicated:
            // PostgreSQL expects the data in base-10000 (so two bytes per 10k),
            // and the decimal point should lie on a boundary (as per definition of "base-10000").

            // BigDecimal, internally, holds an int vector (base-256, one byte per byte),
            // and a base (u64, base-10) shift.

            // Therefore, we split up the encoding in three parts:
            // the sign, the (integer) part before the decimal, and the part after the decimal.

            let absolute = self.abs();
            let mut digits = vec![];

            // Encode the integer part
            let weight = bigdec_add_integer_part(&mut digits, &absolute);

            // Encode the decimal part
            let scale = bigdec_add_decimal_part(&mut digits, &absolute);

            let numeric = match self.sign() {
                Sign::Plus => PgNumeric::Positive {
                    digits, scale, weight
                },
                Sign::Minus => PgNumeric::Negative {
                    digits, scale, weight
                },
                Sign::NoSign => PgNumeric::Positive {
                    digits: vec![0],
                    scale: 0,
                    weight: 0,
                },
            };
            ToSql::<types::Numeric, Pg>::to_sql(&numeric, out)
        }
    }

    impl FromSql<types::Numeric, Pg> for BigDecimal {
        fn from_sql(numeric: Option<&[u8]>) -> Result<Self, Box<Error+Send+Sync>> {
            let (sign, weight, _, digits) = match PgNumeric::from_sql(numeric)? {
                PgNumeric::Positive { weight, scale, digits } => (Sign::Plus, weight, scale, digits),
                PgNumeric::Negative { weight, scale, digits } => (Sign::Minus, weight, scale, digits),
                PgNumeric::NaN => return Err(Box::from("NaN is not (yet) supported in BigDecimal")),
            };
            let mut result = BigUint::default();
            let count = digits.len() as i64;
            for digit in digits {
                result = result * BigUint::from(10_000u64);
                result = result + BigUint::from(digit as u64);
            }
            // First digit got factor 10_000^(digits.len() - 1), but should get 10_000^weight
            let correction_exp = 4 * ( (weight as i64) - count + 1);
            // FIXME: `scale` allows to drop some insignificant figures, which is currently unimplemented.
            // This means that e.g. PostgreSQL 0.01 will be interpreted as 0.0100
            let result = BigDecimal::new(BigInt::from_biguint(sign, result), -correction_exp);
            Ok(result)
        }
    }
}
<|endoftext|>