<commit_before>// Copyright (C) 2019 Sebastian Dröge <sebastian@centricular.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the
// Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
// Boston, MA 02110-1335, USA.

use super::ffi;

use std::ptr;

use glib::glib_wrapper;
use glib::prelude::*;
use glib::translate::*;

use std::mem;

glib_wrapper! {
    pub struct RTPJitterBuffer(Object<ffi::RTPJitterBuffer>);

    match fn {
        get_type => || ffi::rtp_jitter_buffer_get_type(),
    }
}

unsafe impl glib::SendUnique for RTPJitterBuffer {
    fn is_unique(&self) -> bool {
        self.ref_count() == 1
    }
}

impl ToGlib for RTPJitterBufferMode {
    type GlibType = ffi::RTPJitterBufferMode;

    fn to_glib(&self) -> ffi::RTPJitterBufferMode {
        match *self {
            RTPJitterBufferMode::None => ffi::RTP_JITTER_BUFFER_MODE_NONE,
            RTPJitterBufferMode::Slave => ffi::RTP_JITTER_BUFFER_MODE_SLAVE,
            RTPJitterBufferMode::Buffer => ffi::RTP_JITTER_BUFFER_MODE_BUFFER,
            RTPJitterBufferMode::Synced => ffi::RTP_JITTER_BUFFER_MODE_SYNCED,
            RTPJitterBufferMode::__Unknown(value) => value,
        }
    }
}

impl FromGlib<ffi::RTPJitterBufferMode> for RTPJitterBufferMode {
    fn from_glib(value: ffi::RTPJitterBufferMode) -> Self {
        match value {
            0 => RTPJitterBufferMode::None,
            1 => RTPJitterBufferMode::Slave,
            2 => RTPJitterBufferMode::Buffer,
            4 => RTPJitterBufferMode::Synced,
            value => RTPJitterBufferMode::__Unknown(value),
        }
    }
}

pub struct RTPJitterBufferItem(Option<ptr::NonNull<ffi::RTPJitterBufferItem>>);

unsafe impl Send for RTPJitterBufferItem {}

impl RTPJitterBufferItem {
    pub fn new(
        buffer: gst::Buffer,
        dts: gst::ClockTime,
        pts: gst::ClockTime,
        seqnum: Option<u16>,
        rtptime: u32,
    ) -> RTPJitterBufferItem {
        unsafe {
            let ptr = ptr::NonNull::new(glib::ffi::g_slice_alloc0(mem::size_of::<
                ffi::RTPJitterBufferItem,
            >()) as *mut ffi::RTPJitterBufferItem)
            .expect("Allocation failed");
            ptr::write(
                ptr.as_ptr(),
                ffi::RTPJitterBufferItem {
                    data: buffer.into_ptr() as *mut _,
                    next: ptr::null_mut(),
                    prev: ptr::null_mut(),
                    r#type: 0,
                    dts: dts.to_glib(),
                    pts: pts.to_glib(),
                    seqnum: seqnum.map(|s| s as u32).unwrap_or(std::u32::MAX),
                    count: 1,
                    rtptime,
                },
            );

            RTPJitterBufferItem(Some(ptr))
        }
    }

    pub fn into_buffer(mut self) -> gst::Buffer {
        unsafe {
            let item = self.0.take().expect("Invalid wrapper");
            let buf = item.as_ref().data as *mut gst::ffi::GstBuffer;
            glib::ffi::g_slice_free1(
                mem::size_of::<ffi::RTPJitterBufferItem>(),
                item.as_ptr() as *mut _,
            );
            from_glib_full(buf)
        }
    }

    pub fn get_dts(&self) -> gst::ClockTime {
        unsafe {
            let item = self.0.as_ref().expect("Invalid wrapper");
            if item.as_ref().dts == gst::ffi::GST_CLOCK_TIME_NONE {
                gst::CLOCK_TIME_NONE
            } else {
                gst::ClockTime(Some(item.as_ref().dts))
            }
        }
    }

    pub fn get_pts(&self) -> gst::ClockTime {
        unsafe {
            let item = self.0.as_ref().expect("Invalid wrapper");
            if item.as_ref().pts == gst::ffi::GST_CLOCK_TIME_NONE {
                gst::CLOCK_TIME_NONE
            } else {
                gst::ClockTime(Some(item.as_ref().pts))
            }
        }
    }

    pub fn get_seqnum(&self) -> Option<u16> {
        unsafe {
            let item = self.0.as_ref().expect("Invalid wrapper");
            if item.as_ref().seqnum == std::u32::MAX {
                None
            } else {
                Some(item.as_ref().seqnum as u16)
            }
        }
    }

    #[allow(dead_code)]
    pub fn get_rtptime(&self) -> u32 {
        unsafe {
            let item = self.0.as_ref().expect("Invalid wrapper");
            item.as_ref().rtptime
        }
    }
}

impl Drop for RTPJitterBufferItem {
    fn drop(&mut self) {
        unsafe {
            if let Some(ref item) = self.0 {
                if !item.as_ref().data.is_null() {
                    gst::ffi::gst_mini_object_unref(item.as_ref().data as *mut _);
                }

                glib::ffi::g_slice_free1(
                    mem::size_of::<ffi::RTPJitterBufferItem>(),
                    item.as_ptr() as *mut _,
                );
            }
        }
    }
}

pub struct RTPPacketRateCtx(Box<ffi::RTPPacketRateCtx>);

unsafe impl Send for RTPPacketRateCtx {}

impl RTPPacketRateCtx {
    pub fn new() -> RTPPacketRateCtx {
        unsafe {
            let mut ptr = std::mem::MaybeUninit::uninit();
            ffi::gst_rtp_packet_rate_ctx_reset(ptr.as_mut_ptr(), -1);
            RTPPacketRateCtx(Box::new(ptr.assume_init()))
        }
    }

    pub fn reset(&mut self, clock_rate: i32) {
        unsafe { ffi::gst_rtp_packet_rate_ctx_reset(&mut *self.0, clock_rate) }
    }

    pub fn update(&mut self, seqnum: u16, ts: u32) -> u32 {
        unsafe { ffi::gst_rtp_packet_rate_ctx_update(&mut *self.0, seqnum, ts) }
    }

    pub fn get_max_dropout(&mut self, time_ms: i32) -> u32 {
        unsafe { ffi::gst_rtp_packet_rate_ctx_get_max_dropout(&mut *self.0, time_ms) }
    }

    #[allow(dead_code)]
    pub fn get_max_disorder(&mut self, time_ms: i32) -> u32 {
        unsafe { ffi::gst_rtp_packet_rate_ctx_get_max_disorder(&mut *self.0, time_ms) }
    }
}

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

#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum RTPJitterBufferMode {
    r#None,
    Slave,
    Buffer,
    Synced,
    __Unknown(i32),
}

impl RTPJitterBuffer {
    pub fn new() -> RTPJitterBuffer {
        unsafe { from_glib_full(ffi::rtp_jitter_buffer_new()) }
    }

    #[allow(dead_code)]
    pub fn get_mode(&self) -> RTPJitterBufferMode {
        unsafe { from_glib(ffi::rtp_jitter_buffer_get_mode(self.to_glib_none().0)) }
    }

    #[allow(dead_code)]
    pub fn set_mode(&self, mode: RTPJitterBufferMode) {
        unsafe { ffi::rtp_jitter_buffer_set_mode(self.to_glib_none().0, mode.to_glib()) }
    }

    #[allow(dead_code)]
    pub fn get_delay(&self) -> gst::ClockTime {
        unsafe { from_glib(ffi::rtp_jitter_buffer_get_delay(self.to_glib_none().0)) }
    }

    pub fn set_delay(&self, delay: gst::ClockTime) {
        unsafe { ffi::rtp_jitter_buffer_set_delay(self.to_glib_none().0, delay.to_glib()) }
    }

    pub fn set_clock_rate(&self, clock_rate: u32) {
        unsafe { ffi::rtp_jitter_buffer_set_clock_rate(self.to_glib_none().0, clock_rate) }
    }

    #[allow(dead_code)]
    pub fn get_clock_rate(&self) -> u32 {
        unsafe { ffi::rtp_jitter_buffer_get_clock_rate(self.to_glib_none().0) }
    }

    pub fn calculate_pts(
        &self,
        dts: gst::ClockTime,
        estimated_dts: bool,
        rtptime: u32,
        base_time: gst::ClockTime,
        gap: i32,
        is_rtx: bool,
    ) -> gst::ClockTime {
        unsafe {
            let pts = ffi::rtp_jitter_buffer_calculate_pts(
                self.to_glib_none().0,
                dts.to_glib(),
                estimated_dts.to_glib(),
                rtptime,
                base_time.to_glib(),
                gap,
                is_rtx.to_glib(),
            );

            if pts == gst::ffi::GST_CLOCK_TIME_NONE {
                gst::CLOCK_TIME_NONE
            } else {
                pts.into()
            }
        }
    }

    pub fn insert(&self, mut item: RTPJitterBufferItem) -> (bool, bool, i32) {
        unsafe {
            let mut head = mem::MaybeUninit::uninit();
            let mut percent = mem::MaybeUninit::uninit();
            let ptr = item.0.take().expect("Invalid wrapper");
            let ret: bool = from_glib(ffi::rtp_jitter_buffer_insert(
                self.to_glib_none().0,
                ptr.as_ptr(),
                head.as_mut_ptr(),
                percent.as_mut_ptr(),
            ));
            if !ret {
                item.0 = Some(ptr);
            }
            (ret, from_glib(head.assume_init()), percent.assume_init())
        }
    }

    pub fn find_earliest(&self) -> (gst::ClockTime, Option<u16>) {
        unsafe {
            let mut pts = mem::MaybeUninit::uninit();
            let mut seqnum = mem::MaybeUninit::uninit();

            ffi::rtp_jitter_buffer_find_earliest(
                self.to_glib_none().0,
                pts.as_mut_ptr(),
                seqnum.as_mut_ptr(),
            );
            let pts = pts.assume_init();
            let seqnum = seqnum.assume_init();

            let seqnum = if seqnum == std::u32::MAX {
                None
            } else {
                Some(seqnum as u16)
            };

            if pts == gst::ffi::GST_CLOCK_TIME_NONE {
                (gst::CLOCK_TIME_NONE, seqnum)
            } else {
                (pts.into(), seqnum)
            }
        }
    }

    pub fn pop(&self) -> (Option<RTPJitterBufferItem>, i32) {
        unsafe {
            let mut percent = mem::MaybeUninit::uninit();
            let item = ffi::rtp_jitter_buffer_pop(self.to_glib_none().0, percent.as_mut_ptr());

            (
                if item.is_null() {
                    None
                } else {
                    Some(RTPJitterBufferItem(Some(ptr::NonNull::new_unchecked(item))))
                },
                percent.assume_init(),
            )
        }
    }

    pub fn peek(&self) -> (gst::ClockTime, Option<u16>) {
        unsafe {
            let item = ffi::rtp_jitter_buffer_peek(self.to_glib_none().0);
            if item.is_null() {
                (gst::CLOCK_TIME_NONE, None)
            } else {
                let seqnum = (*item).seqnum;
                let seqnum = if seqnum == std::u32::MAX {
                    None
                } else {
                    Some(seqnum as u16)
                };
                ((*item).pts.into(), seqnum)
            }
        }
    }

    pub fn flush(&self) {
        unsafe extern "C" fn free_item(item: glib::ffi::gpointer, _: glib::ffi::gpointer) {
            let _ =
                RTPJitterBufferItem(Some(ptr::NonNull::new(item as *mut _).expect("NULL item")));
        }

        unsafe {
            ffi::rtp_jitter_buffer_flush(self.to_glib_none().0, Some(free_item));
        }
    }

    pub fn reset_skew(&self) {
        unsafe { ffi::rtp_jitter_buffer_reset_skew(self.to_glib_none().0) }
    }
}

impl Default for RTPJitterBuffer {
    fn default() -> Self {
        RTPJitterBuffer::new()
    }
}
<commit_msg>jitterbuffer: mark from_glib fn as unsafe<commit_after>// Copyright (C) 2019 Sebastian Dröge <sebastian@centricular.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the
// Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
// Boston, MA 02110-1335, USA.

use super::ffi;

use std::ptr;

use glib::glib_wrapper;
use glib::prelude::*;
use glib::translate::*;

use std::mem;

glib_wrapper! {
    pub struct RTPJitterBuffer(Object<ffi::RTPJitterBuffer>);

    match fn {
        get_type => || ffi::rtp_jitter_buffer_get_type(),
    }
}

unsafe impl glib::SendUnique for RTPJitterBuffer {
    fn is_unique(&self) -> bool {
        self.ref_count() == 1
    }
}

impl ToGlib for RTPJitterBufferMode {
    type GlibType = ffi::RTPJitterBufferMode;

    fn to_glib(&self) -> ffi::RTPJitterBufferMode {
        match *self {
            RTPJitterBufferMode::None => ffi::RTP_JITTER_BUFFER_MODE_NONE,
            RTPJitterBufferMode::Slave => ffi::RTP_JITTER_BUFFER_MODE_SLAVE,
            RTPJitterBufferMode::Buffer => ffi::RTP_JITTER_BUFFER_MODE_BUFFER,
            RTPJitterBufferMode::Synced => ffi::RTP_JITTER_BUFFER_MODE_SYNCED,
            RTPJitterBufferMode::__Unknown(value) => value,
        }
    }
}

impl FromGlib<ffi::RTPJitterBufferMode> for RTPJitterBufferMode {
    unsafe fn from_glib(value: ffi::RTPJitterBufferMode) -> Self {
        match value {
            0 => RTPJitterBufferMode::None,
            1 => RTPJitterBufferMode::Slave,
            2 => RTPJitterBufferMode::Buffer,
            4 => RTPJitterBufferMode::Synced,
            value => RTPJitterBufferMode::__Unknown(value),
        }
    }
}

pub struct RTPJitterBufferItem(Option<ptr::NonNull<ffi::RTPJitterBufferItem>>);

unsafe impl Send for RTPJitterBufferItem {}

impl RTPJitterBufferItem {
    pub fn new(
        buffer: gst::Buffer,
        dts: gst::ClockTime,
        pts: gst::ClockTime,
        seqnum: Option<u16>,
        rtptime: u32,
    ) -> RTPJitterBufferItem {
        unsafe {
            let ptr = ptr::NonNull::new(glib::ffi::g_slice_alloc0(mem::size_of::<
                ffi::RTPJitterBufferItem,
            >()) as *mut ffi::RTPJitterBufferItem)
            .expect("Allocation failed");
            ptr::write(
                ptr.as_ptr(),
                ffi::RTPJitterBufferItem {
                    data: buffer.into_ptr() as *mut _,
                    next: ptr::null_mut(),
                    prev: ptr::null_mut(),
                    r#type: 0,
                    dts: dts.to_glib(),
                    pts: pts.to_glib(),
                    seqnum: seqnum.map(|s| s as u32).unwrap_or(std::u32::MAX),
                    count: 1,
                    rtptime,
                },
            );

            RTPJitterBufferItem(Some(ptr))
        }
    }

    pub fn into_buffer(mut self) -> gst::Buffer {
        unsafe {
            let item = self.0.take().expect("Invalid wrapper");
            let buf = item.as_ref().data as *mut gst::ffi::GstBuffer;
            glib::ffi::g_slice_free1(
                mem::size_of::<ffi::RTPJitterBufferItem>(),
                item.as_ptr() as *mut _,
            );
            from_glib_full(buf)
        }
    }

    pub fn get_dts(&self) -> gst::ClockTime {
        unsafe {
            let item = self.0.as_ref().expect("Invalid wrapper");
            if item.as_ref().dts == gst::ffi::GST_CLOCK_TIME_NONE {
                gst::CLOCK_TIME_NONE
            } else {
                gst::ClockTime(Some(item.as_ref().dts))
            }
        }
    }

    pub fn get_pts(&self) -> gst::ClockTime {
        unsafe {
            let item = self.0.as_ref().expect("Invalid wrapper");
            if item.as_ref().pts == gst::ffi::GST_CLOCK_TIME_NONE {
                gst::CLOCK_TIME_NONE
            } else {
                gst::ClockTime(Some(item.as_ref().pts))
            }
        }
    }

    pub fn get_seqnum(&self) -> Option<u16> {
        unsafe {
            let item = self.0.as_ref().expect("Invalid wrapper");
            if item.as_ref().seqnum == std::u32::MAX {
                None
            } else {
                Some(item.as_ref().seqnum as u16)
            }
        }
    }

    #[allow(dead_code)]
    pub fn get_rtptime(&self) -> u32 {
        unsafe {
            let item = self.0.as_ref().expect("Invalid wrapper");
            item.as_ref().rtptime
        }
    }
}

impl Drop for RTPJitterBufferItem {
    fn drop(&mut self) {
        unsafe {
            if let Some(ref item) = self.0 {
                if !item.as_ref().data.is_null() {
                    gst::ffi::gst_mini_object_unref(item.as_ref().data as *mut _);
                }

                glib::ffi::g_slice_free1(
                    mem::size_of::<ffi::RTPJitterBufferItem>(),
                    item.as_ptr() as *mut _,
                );
            }
        }
    }
}

pub struct RTPPacketRateCtx(Box<ffi::RTPPacketRateCtx>);

unsafe impl Send for RTPPacketRateCtx {}

impl RTPPacketRateCtx {
    pub fn new() -> RTPPacketRateCtx {
        unsafe {
            let mut ptr = std::mem::MaybeUninit::uninit();
            ffi::gst_rtp_packet_rate_ctx_reset(ptr.as_mut_ptr(), -1);
            RTPPacketRateCtx(Box::new(ptr.assume_init()))
        }
    }

    pub fn reset(&mut self, clock_rate: i32) {
        unsafe { ffi::gst_rtp_packet_rate_ctx_reset(&mut *self.0, clock_rate) }
    }

    pub fn update(&mut self, seqnum: u16, ts: u32) -> u32 {
        unsafe { ffi::gst_rtp_packet_rate_ctx_update(&mut *self.0, seqnum, ts) }
    }

    pub fn get_max_dropout(&mut self, time_ms: i32) -> u32 {
        unsafe { ffi::gst_rtp_packet_rate_ctx_get_max_dropout(&mut *self.0, time_ms) }
    }

    #[allow(dead_code)]
    pub fn get_max_disorder(&mut self, time_ms: i32) -> u32 {
        unsafe { ffi::gst_rtp_packet_rate_ctx_get_max_disorder(&mut *self.0, time_ms) }
    }
}

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

#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum RTPJitterBufferMode {
    r#None,
    Slave,
    Buffer,
    Synced,
    __Unknown(i32),
}

impl RTPJitterBuffer {
    pub fn new() -> RTPJitterBuffer {
        unsafe { from_glib_full(ffi::rtp_jitter_buffer_new()) }
    }

    #[allow(dead_code)]
    pub fn get_mode(&self) -> RTPJitterBufferMode {
        unsafe { from_glib(ffi::rtp_jitter_buffer_get_mode(self.to_glib_none().0)) }
    }

    #[allow(dead_code)]
    pub fn set_mode(&self, mode: RTPJitterBufferMode) {
        unsafe { ffi::rtp_jitter_buffer_set_mode(self.to_glib_none().0, mode.to_glib()) }
    }

    #[allow(dead_code)]
    pub fn get_delay(&self) -> gst::ClockTime {
        unsafe { from_glib(ffi::rtp_jitter_buffer_get_delay(self.to_glib_none().0)) }
    }

    pub fn set_delay(&self, delay: gst::ClockTime) {
        unsafe { ffi::rtp_jitter_buffer_set_delay(self.to_glib_none().0, delay.to_glib()) }
    }

    pub fn set_clock_rate(&self, clock_rate: u32) {
        unsafe { ffi::rtp_jitter_buffer_set_clock_rate(self.to_glib_none().0, clock_rate) }
    }

    #[allow(dead_code)]
    pub fn get_clock_rate(&self) -> u32 {
        unsafe { ffi::rtp_jitter_buffer_get_clock_rate(self.to_glib_none().0) }
    }

    pub fn calculate_pts(
        &self,
        dts: gst::ClockTime,
        estimated_dts: bool,
        rtptime: u32,
        base_time: gst::ClockTime,
        gap: i32,
        is_rtx: bool,
    ) -> gst::ClockTime {
        unsafe {
            let pts = ffi::rtp_jitter_buffer_calculate_pts(
                self.to_glib_none().0,
                dts.to_glib(),
                estimated_dts.to_glib(),
                rtptime,
                base_time.to_glib(),
                gap,
                is_rtx.to_glib(),
            );

            if pts == gst::ffi::GST_CLOCK_TIME_NONE {
                gst::CLOCK_TIME_NONE
            } else {
                pts.into()
            }
        }
    }

    pub fn insert(&self, mut item: RTPJitterBufferItem) -> (bool, bool, i32) {
        unsafe {
            let mut head = mem::MaybeUninit::uninit();
            let mut percent = mem::MaybeUninit::uninit();
            let ptr = item.0.take().expect("Invalid wrapper");
            let ret: bool = from_glib(ffi::rtp_jitter_buffer_insert(
                self.to_glib_none().0,
                ptr.as_ptr(),
                head.as_mut_ptr(),
                percent.as_mut_ptr(),
            ));
            if !ret {
                item.0 = Some(ptr);
            }
            (ret, from_glib(head.assume_init()), percent.assume_init())
        }
    }

    pub fn find_earliest(&self) -> (gst::ClockTime, Option<u16>) {
        unsafe {
            let mut pts = mem::MaybeUninit::uninit();
            let mut seqnum = mem::MaybeUninit::uninit();

            ffi::rtp_jitter_buffer_find_earliest(
                self.to_glib_none().0,
                pts.as_mut_ptr(),
                seqnum.as_mut_ptr(),
            );
            let pts = pts.assume_init();
            let seqnum = seqnum.assume_init();

            let seqnum = if seqnum == std::u32::MAX {
                None
            } else {
                Some(seqnum as u16)
            };

            if pts == gst::ffi::GST_CLOCK_TIME_NONE {
                (gst::CLOCK_TIME_NONE, seqnum)
            } else {
                (pts.into(), seqnum)
            }
        }
    }

    pub fn pop(&self) -> (Option<RTPJitterBufferItem>, i32) {
        unsafe {
            let mut percent = mem::MaybeUninit::uninit();
            let item = ffi::rtp_jitter_buffer_pop(self.to_glib_none().0, percent.as_mut_ptr());

            (
                if item.is_null() {
                    None
                } else {
                    Some(RTPJitterBufferItem(Some(ptr::NonNull::new_unchecked(item))))
                },
                percent.assume_init(),
            )
        }
    }

    pub fn peek(&self) -> (gst::ClockTime, Option<u16>) {
        unsafe {
            let item = ffi::rtp_jitter_buffer_peek(self.to_glib_none().0);
            if item.is_null() {
                (gst::CLOCK_TIME_NONE, None)
            } else {
                let seqnum = (*item).seqnum;
                let seqnum = if seqnum == std::u32::MAX {
                    None
                } else {
                    Some(seqnum as u16)
                };
                ((*item).pts.into(), seqnum)
            }
        }
    }

    pub fn flush(&self) {
        unsafe extern "C" fn free_item(item: glib::ffi::gpointer, _: glib::ffi::gpointer) {
            let _ =
                RTPJitterBufferItem(Some(ptr::NonNull::new(item as *mut _).expect("NULL item")));
        }

        unsafe {
            ffi::rtp_jitter_buffer_flush(self.to_glib_none().0, Some(free_item));
        }
    }

    pub fn reset_skew(&self) {
        unsafe { ffi::rtp_jitter_buffer_reset_skew(self.to_glib_none().0) }
    }
}

impl Default for RTPJitterBuffer {
    fn default() -> Self {
        RTPJitterBuffer::new()
    }
}
<|endoftext|>


<commit_before>#![crate_id(name="cat", vers="1.0.0", author="Seldaek")]
#![feature(managed_boxes)]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/* last synced with: cat (GNU coreutils) 8.13 */

extern crate getopts;

use std::os;
use std::io::{print, File};
use std::io::stdio::{stdout_raw, stdin_raw};
use std::io::{BufferedWriter};

#[allow(dead_code)]
fn main() { uumain(os::args()); }

pub fn uumain(args: Vec<String>) {
    let program = args.get(0).as_slice();
    let opts = ~[
        getopts::optflag("A", "show-all", "equivalent to -vET"),
        getopts::optflag("b", "number-nonblank", "number nonempty output lines, overrides -n"),
        getopts::optflag("e", "", "equivalent to -vE"),
        getopts::optflag("E", "show-ends", "display $ at end of each line"),
        getopts::optflag("n", "number", "number all output lines"),
        getopts::optflag("s", "squeeze-blank", "suppress repeated empty output lines"),
        getopts::optflag("t", "", "equivalent to -vT"),
        getopts::optflag("T", "show-tabs", "display TAB characters as ^I"),
        getopts::optflag("v", "show-nonprinting", "use ^ and M- notation, except for LF (\\n) and TAB (\\t)"),
        getopts::optflag("h", "help", "display this help and exit"),
        getopts::optflag("V", "version", "output version information and exit"),
    ];
    let matches = match getopts::getopts(args.tail(), opts) {
        Ok(m) => m,
        Err(f) => fail!("Invalid options\n{}", f.to_err_msg())
    };
    if matches.opt_present("help") {
        println!("cat 1.0.0");
        println!("");
        println!("Usage:");
        println!("  {0:s} [OPTION]... [FILE]...", program);
        println!("");
        print(getopts::usage("Concatenate FILE(s), or standard input, to standard output.", opts).as_slice());
        println!("");
        println!("With no FILE, or when FILE is -, read standard input.");
        return;
    }
    if matches.opt_present("version") {
        println!("cat 1.0.0");
        return;
    }

    let mut number_mode = NumberNone;
    if matches.opt_present("number") {
        number_mode = NumberAll;
    }
    if matches.opt_present("number-nonblank") {
        number_mode = NumberNonEmpty;
    }
    let show_nonprint = matches.opts_present(["show-nonprinting".to_string(), "show-all".to_string(), "t".to_string(), "e".to_string()]);
    let show_ends = matches.opts_present(["show-ends".to_string(), "show-all".to_string(), "e".to_string()]);
    let show_tabs = matches.opts_present(["show-tabs".to_string(), "show-all".to_string(), "t".to_string()]);
    let squeeze_blank = matches.opt_present("squeeze-blank");
    let mut files = matches.free;
    if files.is_empty() {
        files = vec!("-".to_string());
    }

    exec(files, number_mode, show_nonprint, show_ends, show_tabs, squeeze_blank);
}

#[deriving(Eq)]
pub enum NumberingMode {
    NumberNone,
    NumberNonEmpty,
    NumberAll,
}

static TAB: u8 = '\t' as u8;
#[allow(dead_code)]
static CR: u8 = '\r' as u8;
static LF: u8 = '\n' as u8;

#[cfg(windows)]
fn is_newline_char(byte: u8) -> bool {
    byte == LF || byte == CR
}

#[cfg(unix)]
fn is_newline_char(byte: u8) -> bool {
    byte == LF
}

pub fn exec(files: Vec<String>, number: NumberingMode, show_nonprint: bool, show_ends: bool, show_tabs: bool, squeeze_blank: bool) {

    if NumberNone != number || show_nonprint || show_ends || show_tabs || squeeze_blank {
        let mut counter: uint = 1;
        let is_numbering = number == NumberAll || number == NumberNonEmpty;

        for path in files.iter() {
            let mut reader = match open(path.as_slice()) {
                Some(f) => f,
                None => { continue }
            };

            let mut writer = BufferedWriter::with_capacity(1024 * 8, stdout_raw());
            let mut at_line_start = true;
            let mut buf = ~[0, .. 1024 * 4];
            loop {
                // reading from a TTY seems to raise a condition on
                // EOF, rather than return Some(0) like a file.
                match reader.read(buf) {
                    Ok(n) if n != 0 => {
                        for &byte in buf.slice_to(n).iter() {
                            if at_line_start && (number == NumberAll || (number == NumberNonEmpty && !is_newline_char(byte))) {
                                (write!(&mut writer as &mut Writer, "{0:6u}\t", counter)).unwrap();
                                counter += 1;
                                at_line_start = false;
                            }
                            if is_numbering && byte == LF {
                                at_line_start = true;
                            }
                            if show_tabs && byte == TAB {
                                writer.write(bytes!("^I")).unwrap();
                            } else if show_ends && byte == LF {
                                writer.write(bytes!("$\n")).unwrap();
                            } else if show_nonprint && (byte < 32 || byte >= 127) && !is_newline_char(byte) {
                                let mut byte = byte;
                                if byte >= 128 {
                                    writer.write(bytes!("M-")).unwrap();
                                    byte = byte - 128;
                                }
                                if byte < 32 {
                                    writer.write(['^' as u8, byte + 64]).unwrap();
                                } else if byte == 127 {
                                    writer.write(['^' as u8, byte - 64]).unwrap();
                                } else {
                                    writer.write_u8(byte).unwrap();
                                }
                            } else {
                                writer.write_u8(byte).unwrap();
                            }
                        }
                    },
                    _ => break
                }
            }
        }
        return;
    }

    let mut writer = stdout_raw();
    let mut buf = ~[0, .. 1024 * 64];
    // passthru mode
    for path in files.iter() {
        let mut reader = match open(path.as_slice()) {
            Some(f) => f,
            None => { continue }
        };

        loop {
            // reading from a TTY seems to raise a condition on EOF,
            // rather than return Some(0) like a file.
            match reader.read(buf) {
                Ok(n) if n != 0 => {
                    writer.write(buf.slice_to(n)).unwrap();
                }, _ => break
            }
        }
    }
}

fn open(path: &str) -> Option<Box<Reader>> {
    if "-" == path {
        return Some(box stdin_raw() as Box<Reader>);
    }

    match File::open(&std::path::Path::new(path.as_slice())) {
        Ok(fd) => return Some(box fd as Box<Reader>),
        Err(e) => fail!("cat: {0:s}: {1:s}", path, e.to_str())
    }
}
/* vim: set ai ts=4 sw=4 sts=4 et : */
<commit_msg>Improve cat performance<commit_after>#![crate_id(name="cat", vers="1.0.0", author="Seldaek")]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/* last synced with: cat (GNU coreutils) 8.13 */

extern crate getopts;

use std::os;
use std::io::{print, File};
use std::io::stdio::{stdout_raw, stdin_raw, stderr};
use std::io::{IoResult};
use std::ptr::{copy_nonoverlapping_memory};

#[allow(dead_code)]
fn main() { uumain(os::args()); }

pub fn uumain(args: Vec<String>) {
    let program = args.get(0).as_slice();
    let opts = [
        getopts::optflag("A", "show-all", "equivalent to -vET"),
        getopts::optflag("b", "number-nonblank",
                         "number nonempty output lines, overrides -n"),
        getopts::optflag("e", "", "equivalent to -vE"),
        getopts::optflag("E", "show-ends", "display $ at end of each line"),
        getopts::optflag("n", "number", "number all output lines"),
        getopts::optflag("s", "squeeze-blank", "suppress repeated empty output lines"),
        getopts::optflag("t", "", "equivalent to -vT"),
        getopts::optflag("T", "show-tabs", "display TAB characters as ^I"),
        getopts::optflag("v", "show-nonprinting",
                         "use ^ and M- notation, except for LF (\\n) and TAB (\\t)"),
        getopts::optflag("h", "help", "display this help and exit"),
        getopts::optflag("V", "version", "output version information and exit"),
    ];
    let matches = match getopts::getopts(args.tail(), opts) {
        Ok(m) => m,
        Err(f) => fail!("Invalid options\n{}", f.to_err_msg())
    };
    if matches.opt_present("help") {
        println!("cat 1.0.0");
        println!("");
        println!("Usage:");
        println!("  {0:s} [OPTION]... [FILE]...", program);
        println!("");
        print(getopts::usage("Concatenate FILE(s), or standard input, to \
                             standard output.", opts).as_slice());
        println!("");
        println!("With no FILE, or when FILE is -, read standard input.");
        return;
    }
    if matches.opt_present("version") {
        println!("cat 1.0.0");
        return;
    }

    let mut number_mode = NumberNone;
    if matches.opt_present("n") {
        number_mode = NumberAll;
    }
    if matches.opt_present("b") {
        number_mode = NumberNonEmpty;
    }
    let show_nonprint = matches.opts_present(["A".to_string(), "e".to_string(),
                                              "t".to_string(), "v".to_string()]);
    let show_ends = matches.opts_present(["E".to_string(), "A".to_string(),
                                          "e".to_string()]);
    let show_tabs = matches.opts_present(["A".to_string(), "T".to_string(),
                                          "t".to_string()]);
    let squeeze_blank = matches.opt_present("s");
    let mut files = matches.free;
    if files.is_empty() {
        files.push("-".to_string());
    }

    exec(files, number_mode, show_nonprint, show_ends, show_tabs, squeeze_blank);
}

#[deriving(Eq)]
enum NumberingMode {
    NumberNone,
    NumberNonEmpty,
    NumberAll,
}

fn write_lines(files: Vec<String>, number: NumberingMode, squeeze_blank: bool,
               show_ends: bool) {

    let mut line_counter: uint = 1;

    for path in files.iter() {
        let (mut reader, interactive) = match open(path.as_slice()) {
            Some(f) => f,
            None => continue,
        };

        let mut in_buf  = [0, .. 1024 * 31];
        let mut out_buf = [0, .. 1024 * 64];
        let mut writer = UnsafeWriter::new(out_buf.as_mut_slice(), stdout_raw());
        let mut at_line_start = true;
        loop {
            let n = match reader.read(in_buf) {
                Ok(n) if n != 0 => n,
                _ => break,
            };
            let in_buf = in_buf.slice_to(n);
            let mut buf_pos = range(0, n);
            loop {
                writer.possibly_flush();
                let pos = match buf_pos.next() {
                    Some(p) => p,
                    None => break,
                };
                if in_buf[pos] == '\n' as u8 {
                    if !at_line_start || !squeeze_blank {
                        if at_line_start && number == NumberAll {
                            (write!(&mut writer, "{0:6u}\t", line_counter)).unwrap();
                            line_counter += 1;
                        }
                        if show_ends {
                            writer.write_u8('$' as u8).unwrap();
                        }
                        writer.write_u8('\n' as u8).unwrap();
                        if interactive {
                            writer.flush().unwrap();
                        }
                    }
                    at_line_start = true;
                    continue;
                }
                if at_line_start && number != NumberNone {
                    (write!(&mut writer, "{0:6u}\t", line_counter)).unwrap();
                    line_counter += 1;
                }
                match in_buf.tailn(pos).iter().position(|c| *c == '\n' as u8) {
                    Some(p) => {
                        writer.write(in_buf.slice(pos, pos + p)).unwrap();
                        if show_ends {
                            writer.write_u8('$' as u8).unwrap();
                        }
                        writer.write_u8('\n' as u8).unwrap();
                        if interactive {
                            writer.flush().unwrap();
                        }
                        buf_pos = range(pos + p + 1, n);
                        at_line_start = true;
                    },
                    None => {
                        writer.write(in_buf.tailn(pos)).unwrap();
                        at_line_start = false;
                        break;
                    }
                };
            }
        }
    }
}

fn write_bytes(files: Vec<String>, number: NumberingMode, squeeze_blank: bool,
               show_ends: bool, show_nonprint: bool, show_tabs: bool) {

    let mut line_counter: uint = 1;

    for path in files.iter() {
        let (mut reader, interactive) = match open(path.as_slice()) {
            Some(f) => f,
            None => continue,
        };

        // Flush all 1024 iterations.
        let mut flush_counter = range(0, 1024);

        let mut in_buf  = [0, .. 1024 * 32];
        let mut out_buf = [0, .. 1024 * 64];
        let mut writer = UnsafeWriter::new(out_buf.as_mut_slice(), stdout_raw());
        let mut at_line_start = true;
        loop {
            let n = match reader.read(in_buf) {
                Ok(n) if n != 0 => n,
                _ => break,
            };
            for &byte in in_buf.slice_to(n).iter() {
                if flush_counter.next().is_none() {
                    writer.possibly_flush();
                    flush_counter = range(0, 1024);
                }
                if byte == '\n' as u8 {
                    if !at_line_start || !squeeze_blank {
                        if at_line_start && number == NumberAll {
                            (write!(&mut writer, "{0:6u}\t", line_counter)).unwrap();
                            line_counter += 1;
                        }
                        if show_ends {
                            writer.write_u8('$' as u8).unwrap();
                        }
                        writer.write_u8('\n' as u8).unwrap();
                        if interactive {
                            writer.flush().unwrap();
                        }
                    }
                    at_line_start = true;
                    continue;
                }
                if at_line_start && number != NumberNone {
                    (write!(&mut writer, "{0:6u}\t", line_counter)).unwrap();
                    line_counter += 1;
                    at_line_start = false;
                }
                // This code is slow because of the many branches. cat in glibc avoids
                // this by having the whole loop inside show_nonprint.
                if byte == '\t' as u8 {
                    if show_tabs {
                        writer.write_str("^I")
                    } else {
                        writer.write_u8(byte)
                    }
                } else if show_nonprint {
                    let byte = match byte {
                        128 .. 255 => {
                            writer.write_str("M-").unwrap();
                            byte - 128
                        },
                        _ => byte,
                    };
                    match byte {
                        0 .. 31 => writer.write(['^' as u8, byte + 64]),
                        127     => writer.write(['^' as u8, byte - 64]),
                        _       => writer.write_u8(byte),
                    }
                } else {
                    writer.write_u8(byte)
                }.unwrap();
            }
        }
    }
}

fn write_fast(files: Vec<String>) {
    let mut writer = stdout_raw();
    let mut in_buf = [0, .. 1024 * 64];

    for path in files.iter() {
        let (mut reader, _) = match open(path.as_slice()) {
            Some(x) => x,
            None => continue,
        };

        loop {
            match reader.read(in_buf) {
                Ok(n) if n != 0 => {
                    // This interface is completely broken.
                    writer.write(in_buf.slice_to(n)).unwrap();
                },
                _ => break
            }
        }
    }
}

fn exec(files: Vec<String>, number: NumberingMode, show_nonprint: bool,
        show_ends: bool, show_tabs: bool, squeeze_blank: bool) {

    if show_nonprint || show_tabs {
        write_bytes(files, number, squeeze_blank, show_ends, show_nonprint, show_tabs);
    } else if number != NumberNone || squeeze_blank || show_ends {
        write_lines(files, number, squeeze_blank, show_ends);
    } else {
        write_fast(files);
    }
}

fn open(path: &str) -> Option<(Box<Reader>, bool)> {
    if path == "-" {
        let stdin = stdin_raw();
        let interactive = stdin.isatty();
        return Some((box stdin as Box<Reader>, interactive));
    }

    match File::open(&std::path::Path::new(path)) {
        Ok(f) => return Some((box f as Box<Reader>, false)),
        Err(e) => {
            (writeln!(stderr(), "cat: {0:s}: {1:s}", path, e.to_str())).unwrap();
            return None;
        },
    };
}

struct UnsafeWriter<'a, W> {
    inner: W,
    buf: &'a mut [u8],
    pos: uint,
    threshold: uint,
}

impl<'a, W: Writer> UnsafeWriter<'a, W> {
    fn new(buf: &'a mut [u8], inner: W) -> UnsafeWriter<'a, W> {
        let threshold = buf.len()/2;
        UnsafeWriter {
            inner: inner,
            buf: buf,
            pos: 0,
            threshold: threshold,
        }
    }

    fn flush_buf(&mut self) -> IoResult<()> {
        if self.pos != 0 {
            let ret = self.inner.write(self.buf.slice_to(self.pos));
            self.pos = 0;
            ret
        } else {
            Ok(())
        }
    }

    fn possibly_flush(&mut self) {
        if self.pos > self.threshold {
            self.inner.write(self.buf.slice_to(self.pos)).unwrap();
            self.pos = 0;
        }
    }
}

#[inline(never)]
fn fail() -> ! {
    fail!("assertion failed");
}

impl<'a, W: Writer> Writer for UnsafeWriter<'a, W> {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        let dst = self.buf.mut_slice_from(self.pos);
        if buf.len() > dst.len() {
            fail();
        }
        unsafe {
            copy_nonoverlapping_memory(dst.as_mut_ptr(), buf.as_ptr(), buf.len())
        }
        self.pos += buf.len();
        Ok(())
    }

    fn flush(&mut self) -> IoResult<()> {
        self.flush_buf().and_then(|()| self.inner.flush())
    }
}

#[unsafe_destructor]
impl<'a, W: Writer> Drop for UnsafeWriter<'a, W> {
    fn drop(&mut self) {
        let _ = self.flush_buf();
    }
}

/* vim: set ai ts=4 sw=4 sts=4 et : */
<|endoftext|>


<commit_before>extern crate turingarena;
extern crate turingarena_task_maker;

use juniper::{FieldError, FieldResult};

use schema::problems;
use turingarena::problem::driver::{ProblemDriver, ProblemPack};
use turingarena::problem::material::Material;
use turingarena::problem::ProblemName;
use user::UserId;

use super::*;

#[derive(Insertable)]
#[table_name = "problems"]
struct ProblemDataInput<'a> {
    name: &'a str,
}

#[derive(Queryable)]
pub struct ProblemData {
    name: String,
}

/// A problem in the contest
pub struct Problem {
    /// Raw database data of the contest
    pub data: ProblemData,

    /// Id of the user (if specified)
    pub user_id: Option<UserId>,
}

/// A problem in a contest
#[juniper::object(Context = Context)]
impl Problem {
    /// Name of this problem. Unique in the current contest.
    fn name(&self) -> ProblemName {
        ProblemName(self.data.name.clone())
    }

    /// Material of this problem
    fn material(&self, ctx: &Context) -> FieldResult<Material> {
        turingarena_task_maker::driver::IoiProblemDriver::gen_material(self.pack(ctx))
            .map_err(FieldError::from)
    }

    /// Scorables of the current user (if to be shown)
    fn scores(&self, ctx: &Context) -> FieldResult<Option<Vec<evaluation::MaxScore>>> {
        let result = if let Some(UserId(user_id)) = &self.user_id {
            Some(evaluation::query_scorables_of_user_and_problem(
                &ctx.connect_db()?,
                &user_id,
                &self.data.name,
            )?)
        } else {
            None
        };
        Ok(result)
    }

    /// Submissions of the current user (if to be shown)
    fn submissions(&self, ctx: &Context) -> FieldResult<Option<Vec<submission::Submission>>> {
        let result = if let Some(UserId(user_id)) = &self.user_id {
            Some(submission::of_user_and_problem(
                &ctx.connect_db()?,
                &user_id,
                &self.data.name,
            )?)
        } else {
            None
        };
        Ok(result)
    }

    /// Submit a solution to the problem
    fn submit(
        &self,
        ctx: &Context,
        files: Vec<submission::FileInput>,
    ) -> FieldResult<submission::Submission> {
        if let Some(UserId(user_id)) = &self.user_id {
            let submission =
                submission::insert(&ctx.connect_db()?, &user_id, &self.data.name, files)?;
            evaluation::evaluate(self.pack(ctx), &submission, ctx.connect_db()?)?;
            Ok(submission)
        } else {
            Err(FieldError::from("Must specify a user id"))
        }
    }
}

impl Problem {
    /// Path of the problem
    fn path(&self, ctx: &Context) -> PathBuf {
        ctx.problems_dir.join(&self.data.name)
    }

    /// return the problem pack object
    fn pack(&self, ctx: &Context) -> ProblemPack {
        ProblemPack(std::path::PathBuf::from(&self.path(ctx)))
    }
}

/// Get a problem data by its name
pub fn by_name(conn: &SqliteConnection, name: ProblemName) -> QueryResult<ProblemData> {
    problems::table.find(name.0).first(conn)
}

/// Get all the problems data in the database
pub fn all(conn: &SqliteConnection) -> QueryResult<Vec<ProblemData>> {
    problems::table.load(conn)
}

/// Insert a problem in the database
pub fn insert(conn: &SqliteConnection, name: ProblemName) -> QueryResult<()> {
    let problem = ProblemDataInput { name: &name.0 };
    diesel::insert_into(schema::problems::table)
        .values(problem)
        .execute(conn)?;
    Ok(())
}

/// Delete a problem from the database
pub fn delete(conn: &SqliteConnection, name: ProblemName) -> QueryResult<()> {
    diesel::delete(problems::table.find(name.0)).execute(conn)?;
    Ok(())
}
<commit_msg>Add can submit<commit_after>extern crate turingarena;
extern crate turingarena_task_maker;

use juniper::{FieldError, FieldResult};

use schema::problems;
use turingarena::problem::driver::{ProblemDriver, ProblemPack};
use turingarena::problem::material::Material;
use turingarena::problem::ProblemName;
use user::UserId;

use super::*;

#[derive(Insertable)]
#[table_name = "problems"]
struct ProblemDataInput<'a> {
    name: &'a str,
}

#[derive(Queryable)]
pub struct ProblemData {
    name: String,
}

/// A problem in the contest
pub struct Problem {
    /// Raw database data of the contest
    pub data: ProblemData,

    /// Id of the user (if specified)
    pub user_id: Option<UserId>,
}

/// A problem in a contest
#[juniper::object(Context = Context)]
impl Problem {
    /// Name of this problem. Unique in the current contest.
    fn name(&self) -> ProblemName {
        ProblemName(self.data.name.clone())
    }

    /// Material of this problem
    fn material(&self, ctx: &Context) -> FieldResult<Material> {
        turingarena_task_maker::driver::IoiProblemDriver::gen_material(self.pack(ctx))
            .map_err(FieldError::from)
    }

    /// Scorables of the current user (if to be shown)
    fn scores(&self, ctx: &Context) -> FieldResult<Option<Vec<evaluation::MaxScore>>> {
        let result = if let Some(UserId(user_id)) = &self.user_id {
            Some(evaluation::query_scorables_of_user_and_problem(
                &ctx.connect_db()?,
                &user_id,
                &self.data.name,
            )?)
        } else {
            None
        };
        Ok(result)
    }

    /// Submissions of the current user (if to be shown)
    fn submissions(&self, ctx: &Context) -> FieldResult<Option<Vec<submission::Submission>>> {
        let result = if let Some(UserId(user_id)) = &self.user_id {
            Some(submission::of_user_and_problem(
                &ctx.connect_db()?,
                &user_id,
                &self.data.name,
            )?)
        } else {
            None
        };
        Ok(result)
    }

    /// Submit a solution to the problem
    fn submit(
        &self,
        ctx: &Context,
        files: Vec<submission::FileInput>,
    ) -> FieldResult<submission::Submission> {
        if let Some(UserId(user_id)) = &self.user_id {
            let submission =
                submission::insert(&ctx.connect_db()?, &user_id, &self.data.name, files)?;
            evaluation::evaluate(self.pack(ctx), &submission, ctx.connect_db()?)?;
            Ok(submission)
        } else {
            Err(FieldError::from("Must specify a user id"))
        }
    }

    /// Indicates if the user can submit to this problem
    fn can_submit(&self) -> bool {
        self.user_id.is_some()
    }
}

impl Problem {
    /// Path of the problem
    fn path(&self, ctx: &Context) -> PathBuf {
        ctx.problems_dir.join(&self.data.name)
    }

    /// return the problem pack object
    fn pack(&self, ctx: &Context) -> ProblemPack {
        ProblemPack(std::path::PathBuf::from(&self.path(ctx)))
    }
}

/// Get a problem data by its name
pub fn by_name(conn: &SqliteConnection, name: ProblemName) -> QueryResult<ProblemData> {
    problems::table.find(name.0).first(conn)
}

/// Get all the problems data in the database
pub fn all(conn: &SqliteConnection) -> QueryResult<Vec<ProblemData>> {
    problems::table.load(conn)
}

/// Insert a problem in the database
pub fn insert(conn: &SqliteConnection, name: ProblemName) -> QueryResult<()> {
    let problem = ProblemDataInput { name: &name.0 };
    diesel::insert_into(schema::problems::table)
        .values(problem)
        .execute(conn)?;
    Ok(())
}

/// Delete a problem from the database
pub fn delete(conn: &SqliteConnection, name: ProblemName) -> QueryResult<()> {
    diesel::delete(problems::table.find(name.0)).execute(conn)?;
    Ok(())
}
<|endoftext|>


<commit_before>use super::super::io;
use std::fmt;
use std::net::Ipv4Addr;
use std::convert::From;

/// IP header using SSE
//#[repr(C, packed)]
#[derive(Debug)]
#[repr(simd)]
pub struct IpHeader {
    version_to_len: u32,
    id_to_foffset: u32,
    ttl_to_csum: u32,
    src_ip: u32,
    dst_ip: u32,
}

impl fmt::Display for IpHeader {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let src = Ipv4Addr::from(self.src());
        let dst = Ipv4Addr::from(self.dst());
        write!(f, "{} > {} len: {} ttl: {} proto: {} csum: {}",
               src, dst,
               u16::from_be(self.length()), self.ttl(), self.protocol(), self.csum())
    }
}

impl io::EndOffset for IpHeader {
    #[inline]
    fn offset(&self) -> usize {
        self.ihl() as usize * 4
    }
}

impl IpHeader {
    #[inline]
    pub fn new() -> IpHeader {
        IpHeader{version_to_len: 0, id_to_foffset:0, ttl_to_csum:0, src_ip: 0, dst_ip:0}
    }

    #[inline]
    pub fn src(&self) -> u32 {
        let ip = u32::from_be(self.src_ip);
        ip
    }

    #[inline]
    pub fn set_src(&mut self, src: u32) {
        self.src_ip = u32::to_be(src)
    }

    #[inline]
    pub fn dst(&self) -> u32 {
        let ip = u32::from_be(self.dst_ip);
        ip
    }

    #[inline]
    pub fn set_dst(&mut self, dst: u32) {
        self.dst_ip = u32::to_be(dst);
    }

    #[inline]
    pub fn ttl(&self) -> u8 {
        let ttlpcsum = self.ttl_to_csum;
        (ttlpcsum & 0x000000ff) as u8
    }

    #[inline]
    pub fn set_ttl(&mut self, ttl: u8) {
        let ttlpcsum = self.ttl_to_csum;
        let blanked = ttlpcsum & !0x000000ff;
        self.ttl_to_csum = blanked | (ttl as u32);
    }

    #[inline]
    pub fn protocol(&self) -> u8 {
        let ttlpcsum = self.ttl_to_csum;
        ((ttlpcsum & 0xff00) >> 8) as u8
    }

    #[inline]
    pub fn set_protocol(&mut self, protocol: u8) {
        let ttlpcsum = self.ttl_to_csum;
        let blanked = ttlpcsum & !0xff00;
        self.ttl_to_csum = blanked | ((protocol as u32) << 8);
    }

    #[inline]
    pub fn csum(&self) -> u16 {
        let ttlpcsum = self.ttl_to_csum;
        ((ttlpcsum & 0xffff0000) >> 16) as u16
    }

    #[inline]
    pub fn set_csum(&mut self, csum: u16) {
        let ttlpcsum = self.ttl_to_csum;
        let blanked = ttlpcsum & !0xffff0000;
        self.ttl_to_csum = blanked | ((u16::to_be(csum) as u32) << 16);
    }

    #[inline]
    pub fn id(&self) -> u16 {
        let id_flag_fragment = self.id_to_foffset;
        u16::from_be((id_flag_fragment & 0xffff) as u16)
    }

    #[inline]
    pub fn set_id(&mut self, id: u16) {
        let id_flag_fragment = self.id_to_foffset;
        let blanked = id_flag_fragment & !0xffff;
        self.id_to_foffset = blanked | (u16::to_be(id) as u32);
    }

    #[inline]
    pub fn flags(&self) -> u8 {
        let id_flag_fragment = self.id_to_foffset;
        let flag_fragment = (id_flag_fragment >> 21) as u16;
        (flag_fragment & 0x7) as u8
    }

    #[inline]
    pub fn set_flags(&mut self, flags: u8) {
        self.id_to_foffset = (self.id_to_foffset & !0x00e00000) |
            (((flags & 0x7) as u32) << 16 + 5);
    }

    #[inline]
    pub fn fragment_offset(&self) -> u16 {
        let id_flag_fragment = self.id_to_foffset;
        let flag_fragment = (id_flag_fragment & 0xffff) as u16;
        u16::from_be(((flag_fragment & !0xe) >> 3))
    }

    #[inline]
    pub fn set_fragment_offset(&mut self, offset: u16) {
        let offset_correct = offset as u32;
        self.id_to_foffset = (self.id_to_foffset & !0x001f0000) |
                                ((offset_correct & 0x1f00) << 11);
        self.id_to_foffset = (self.id_to_foffset & !0xff000000) |
                                ((offset_correct & 0xff) << 24);
    }

    #[inline]
    pub fn version(&self) -> u8 {
        let vihl = ((self.version_to_len & 0xf0)  as u8) >> 4;
        vihl
    }

    #[inline]
    pub fn set_version(&mut self, version: u8) {
        self.version_to_len = (self.version_to_len & !0xf0) | (((version & 0xf0) as u32) << 4);
    }

    #[inline]
    pub fn ihl(&self) -> u8 {
        let ihl = (self.version_to_len & 0xf) as u8;
        ihl
    }

    #[inline]
    pub fn set_ihl(&mut self, ihl: u8) {
        self.version_to_len = (self.version_to_len & !0xf) | ((ihl & 0xf) as u32);
    }

    #[inline]
    pub fn dscp(&self) -> u8 {
        let dscp_ecn = ((self.version_to_len & 0xfc00) >> 10) as u8;
        dscp_ecn
    }

    #[inline]
    pub fn set_dscp(&mut self, dscp: u8) {
        self.version_to_len = (self.version_to_len & !0xfc00) | (((dscp & 0x3f) as u32) << 10);
    }

    #[inline]
    pub fn ecn(&self) -> u8 {
        let ecn = ((self.version_to_len & 0x0300) >> 8) as u8;
        ecn
    }

    #[inline]
    pub fn set_ecn(&mut self, ecn: u8) {
        self.version_to_len = (self.version_to_len & !0x0300) | (((ecn & 0x03) as u32) << 8);
    }

    #[inline]
    pub fn length(&self) -> u16 {
        ((self.version_to_len & 0xffff0000) >> 16) as u16
    }

    #[inline]
    pub fn set_length(&mut self, len: u16) {
        self.version_to_len = (self.version_to_len & !0xffff0000) | ((u16::to_be(len) as u32) << 16);
    }

    // FIXME: Make sure this uses SIMD
    #[inline]
    pub fn apply(&mut self, from: &Self) {
        self.version_to_len = from.version_to_len;
        self.id_to_foffset = from.id_to_foffset;
        self.ttl_to_csum = from.ttl_to_csum;
        self.src_ip = from.src_ip;
        self.dst_ip = from.dst_ip;
    }
}
<commit_msg>Simplify offset set code<commit_after>use super::super::io;
use std::fmt;
use std::net::Ipv4Addr;
use std::convert::From;

/// IP header using SSE
//#[repr(C, packed)]
#[derive(Debug)]
#[repr(simd)]
pub struct IpHeader {
    version_to_len: u32,
    id_to_foffset: u32,
    ttl_to_csum: u32,
    src_ip: u32,
    dst_ip: u32,
}

impl fmt::Display for IpHeader {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let src = Ipv4Addr::from(self.src());
        let dst = Ipv4Addr::from(self.dst());
        write!(f, "{} > {} len: {} ttl: {} proto: {} csum: {}",
               src, dst,
               u16::from_be(self.length()), self.ttl(), self.protocol(), self.csum())
    }
}

impl io::EndOffset for IpHeader {
    #[inline]
    fn offset(&self) -> usize {
        self.ihl() as usize * 4
    }
}

impl IpHeader {
    #[inline]
    pub fn new() -> IpHeader {
        IpHeader{version_to_len: 0, id_to_foffset:0, ttl_to_csum:0, src_ip: 0, dst_ip:0}
    }

    #[inline]
    pub fn src(&self) -> u32 {
        let ip = u32::from_be(self.src_ip);
        ip
    }

    #[inline]
    pub fn set_src(&mut self, src: u32) {
        self.src_ip = u32::to_be(src)
    }

    #[inline]
    pub fn dst(&self) -> u32 {
        let ip = u32::from_be(self.dst_ip);
        ip
    }

    #[inline]
    pub fn set_dst(&mut self, dst: u32) {
        self.dst_ip = u32::to_be(dst);
    }

    #[inline]
    pub fn ttl(&self) -> u8 {
        let ttlpcsum = self.ttl_to_csum;
        (ttlpcsum & 0x000000ff) as u8
    }

    #[inline]
    pub fn set_ttl(&mut self, ttl: u8) {
        let ttlpcsum = self.ttl_to_csum;
        let blanked = ttlpcsum & !0x000000ff;
        self.ttl_to_csum = blanked | (ttl as u32);
    }

    #[inline]
    pub fn protocol(&self) -> u8 {
        let ttlpcsum = self.ttl_to_csum;
        ((ttlpcsum & 0xff00) >> 8) as u8
    }

    #[inline]
    pub fn set_protocol(&mut self, protocol: u8) {
        let ttlpcsum = self.ttl_to_csum;
        let blanked = ttlpcsum & !0xff00;
        self.ttl_to_csum = blanked | ((protocol as u32) << 8);
    }

    #[inline]
    pub fn csum(&self) -> u16 {
        let ttlpcsum = self.ttl_to_csum;
        ((ttlpcsum & 0xffff0000) >> 16) as u16
    }

    #[inline]
    pub fn set_csum(&mut self, csum: u16) {
        let ttlpcsum = self.ttl_to_csum;
        let blanked = ttlpcsum & !0xffff0000;
        self.ttl_to_csum = blanked | ((u16::to_be(csum) as u32) << 16);
    }

    #[inline]
    pub fn id(&self) -> u16 {
        let id_flag_fragment = self.id_to_foffset;
        u16::from_be((id_flag_fragment & 0xffff) as u16)
    }

    #[inline]
    pub fn set_id(&mut self, id: u16) {
        let id_flag_fragment = self.id_to_foffset;
        let blanked = id_flag_fragment & !0xffff;
        self.id_to_foffset = blanked | (u16::to_be(id) as u32);
    }

    #[inline]
    pub fn flags(&self) -> u8 {
        let id_flag_fragment = self.id_to_foffset;
        let flag_fragment = (id_flag_fragment >> 21) as u16;
        (flag_fragment & 0x7) as u8
    }

    #[inline]
    pub fn set_flags(&mut self, flags: u8) {
        self.id_to_foffset = (self.id_to_foffset & !0x00e00000) |
            (((flags & 0x7) as u32) << 16 + 5);
    }

    #[inline]
    pub fn fragment_offset(&self) -> u16 {
        let id_flag_fragment = self.id_to_foffset;
        let flag_fragment = (id_flag_fragment & 0xffff) as u16;
        u16::from_be(((flag_fragment & !0xe) >> 3))
    }

    #[inline]
    pub fn set_fragment_offset(&mut self, offset: u16) {
        let offset_correct = offset as u32;
        let id_to_offset_le = u32::from_be(self.id_to_foffset);
        self.id_to_foffset = u32::to_be(id_to_offset_le & !0x1fff | offset_correct);
    }

    #[inline]
    pub fn version(&self) -> u8 {
        let vihl = ((self.version_to_len & 0xf0)  as u8) >> 4;
        vihl
    }

    #[inline]
    pub fn set_version(&mut self, version: u8) {
        self.version_to_len = (self.version_to_len & !0xf0) | (((version & 0xf0) as u32) << 4);
    }

    #[inline]
    pub fn ihl(&self) -> u8 {
        let ihl = (self.version_to_len & 0xf) as u8;
        ihl
    }

    #[inline]
    pub fn set_ihl(&mut self, ihl: u8) {
        self.version_to_len = (self.version_to_len & !0xf) | ((ihl & 0xf) as u32);
    }

    #[inline]
    pub fn dscp(&self) -> u8 {
        let dscp_ecn = ((self.version_to_len & 0xfc00) >> 10) as u8;
        dscp_ecn
    }

    #[inline]
    pub fn set_dscp(&mut self, dscp: u8) {
        self.version_to_len = (self.version_to_len & !0xfc00) | (((dscp & 0x3f) as u32) << 10);
    }

    #[inline]
    pub fn ecn(&self) -> u8 {
        let ecn = ((self.version_to_len & 0x0300) >> 8) as u8;
        ecn
    }

    #[inline]
    pub fn set_ecn(&mut self, ecn: u8) {
        self.version_to_len = (self.version_to_len & !0x0300) | (((ecn & 0x03) as u32) << 8);
    }

    #[inline]
    pub fn length(&self) -> u16 {
        ((self.version_to_len & 0xffff0000) >> 16) as u16
    }

    #[inline]
    pub fn set_length(&mut self, len: u16) {
        self.version_to_len = (self.version_to_len & !0xffff0000) | ((u16::to_be(len) as u32) << 16);
    }

    // FIXME: Make sure this uses SIMD
    #[inline]
    pub fn apply(&mut self, from: &Self) {
        self.version_to_len = from.version_to_len;
        self.id_to_foffset = from.id_to_foffset;
        self.ttl_to_csum = from.ttl_to_csum;
        self.src_ip = from.src_ip;
        self.dst_ip = from.dst_ip;
    }
}
<|endoftext|>


<commit_before>mod figure;

use stamp;

use summary::Summary;

macro_rules! plot {
    ($p: expr) => {
        match $p {
            Ok(t) => Ok(t),
            Err(_) => Err("Unable to plot sample data"),
        }
    }
}

struct Boxplot {
    box_lo: f64,
    box_mid: f64,
    box_hi: f64,
    marker: f64,
    wh_lo: f64,
    wh_hi: f64,
}

impl Boxplot {
    fn from_summary(summary: &Summary) -> Self {
        let range = summary.max() - summary.min();
        let n = |x| (x - summary.min()) / range;

        Boxplot {
            box_lo: n(summary.lower_quartile()),
            box_mid: n(summary.median()),
            box_hi: n(summary.upper_quartile()),
            marker: n(summary.mean()),
            wh_lo: n(summary.min()),
            wh_hi: n(summary.max()),
        }
    }
}

struct BoxplotCols {
    box_lo: usize,
    box_mid: usize,
    box_hi: usize,
    marker: usize,
    wh_lo: usize,
    wh_hi: usize,
}

impl BoxplotCols {
    fn new(data: &Boxplot, width: usize) -> Self {
        let max_col = (width - 1) as f64;
        let to_col = |x: f64| (x * max_col).floor() as usize;

        BoxplotCols {
            box_lo: to_col(data.box_lo),
            box_mid: to_col(data.box_mid),
            box_hi: to_col(data.box_hi),
            marker: to_col(data.marker),
            wh_lo: to_col(data.wh_lo),
            wh_hi: to_col(data.wh_hi),
        }
    }
}

struct RowChars {
    wh_lo: &'static str,
    wh_lo_box_lo_fill: &'static str,
    box_lo: &'static str,
    box_lo_box_mid_fill: &'static str,
    box_mid: &'static str,
    box_mid_box_hi_fill: &'static str,
    box_hi: &'static str,
    box_hi_wh_hi_fill: &'static str,
    wh_hi: &'static str,
}

impl RowChars {
    pub fn render(&self, row: &mut Vec<String>, cols: &BoxplotCols) {
        row[cols.wh_lo] = self.wh_lo.to_string();
        for i in (cols.wh_lo + 1)..cols.box_lo {
            row[i] = self.wh_lo_box_lo_fill.to_string();
        }
        row[cols.box_lo] = self.box_lo.to_string();
        for i in (cols.box_lo + 1)..cols.box_mid {
            row[i] = self.box_lo_box_mid_fill.to_string();
        }
        row[cols.box_mid] = self.box_mid.to_string();
        for i in (cols.box_mid + 1)..cols.box_hi {
            row[i] = self.box_mid_box_hi_fill.to_string();
        }
        row[cols.box_hi] = self.box_hi.to_string();
        for i in (cols.box_hi + 1)..cols.wh_hi {
            row[i] = self.box_hi_wh_hi_fill.to_string();
        }
        row[cols.wh_hi] = self.wh_hi.to_string();
    }
}

struct BoxplotChars {
    marker: &'static str,
    rows: [RowChars; 3],
}

impl BoxplotChars {
    pub fn render(&self, summary: &Summary, width: usize) -> Result<String, &'static str> {
        let data = Boxplot::from_summary(summary);
        let cols = BoxplotCols::new(&data, width);

        let mut plot = Plot::new(width);

        self.rows[0].render(&mut plot.0, &cols);
        self.rows[1].render(&mut plot.1, &cols);
        self.rows[2].render(&mut plot.2, &cols);

        let no_marker = plot.render();

        let base = plot!(stamp::Stamp::new(&no_marker))?;
        let marker = plot!(stamp::Stamp::new(self.marker))?;
        let layered = plot!(base.layer(&marker, cols.marker, 1))?;

        Ok(layered.render())
    }
}

static ASCII_CHARS: BoxplotChars = BoxplotChars {
    marker: "x",
    rows: [
        RowChars {
            wh_lo: " ",
            wh_lo_box_lo_fill: " ",
            box_lo: "+",
            box_lo_box_mid_fill: "-",
            box_mid: "+",
            box_mid_box_hi_fill: "-",
            box_hi: "+",
            box_hi_wh_hi_fill: " ",
            wh_hi: " ",
        },
        RowChars {
            wh_lo: "|",
            wh_lo_box_lo_fill: "-",
            box_lo: "|",
            box_lo_box_mid_fill: " ",
            box_mid: "|",
            box_mid_box_hi_fill: " ",
            box_hi: "|",
            box_hi_wh_hi_fill: "-",
            wh_hi: "|",
        },
        RowChars {
            wh_lo: " ",
            wh_lo_box_lo_fill: " ",
            box_lo: "+",
            box_lo_box_mid_fill: "-",
            box_mid: "+",
            box_mid_box_hi_fill: "-",
            box_hi: "+",
            box_hi_wh_hi_fill: " ",
            wh_hi: " ",
        },
    ],
};

static UNICODE_CHARS: BoxplotChars = BoxplotChars {
    marker: "✕",
    rows: [
        RowChars {
            wh_lo: "┬",
            wh_lo_box_lo_fill: " ",
            box_lo: "┌",
            box_lo_box_mid_fill: "─",
            box_mid: "┬",
            box_mid_box_hi_fill: "─",
            box_hi: "┐",
            box_hi_wh_hi_fill: " ",
            wh_hi: "┬",
        },
        RowChars {
            wh_lo: "├",
            wh_lo_box_lo_fill: "─",
            box_lo: "┤",
            box_lo_box_mid_fill: " ",
            box_mid: "│",
            box_mid_box_hi_fill: " ",
            box_hi: "├",
            box_hi_wh_hi_fill: "─",
            wh_hi: "┤",
        },
        RowChars {
            wh_lo: "┴",
            wh_lo_box_lo_fill: " ",
            box_lo: "└",
            box_lo_box_mid_fill: "─",
            box_mid: "┴",
            box_mid_box_hi_fill: "─",
            box_hi: "┘",
            box_hi_wh_hi_fill: " ",
            wh_hi: "┴",
        },
    ],
};

fn make_row(width: usize) -> Vec<String> {
    use std::iter::repeat;

    let mut row = vec![];
    row.extend(repeat(String::from("")).take(width));

    row
}

struct Plot(Vec<String>, Vec<String>, Vec<String>);

impl Plot {
    fn new(width: usize) -> Self {
        Plot(make_row(width), make_row(width), make_row(width))
    }

    fn render(&self) -> String {
        let rows = vec![
            self.0.join(""),
            self.1.join(""),
            self.2.join(""),
        ];

        rows.join("\n")
    }
}

pub fn summary_plot(summary: &Summary, width: usize, ascii: bool) -> Result<String, &'static str> {
    let plot_style = if ascii { &ASCII_CHARS } else { &UNICODE_CHARS };

    plot_style.render(summary, width)
}

pub fn comparison_plot(
    summaries: &[&Summary],
    width: usize,
    ascii: bool,
    border: bool,
) -> Result<String, &'static str> {
    if summaries.is_empty() {
        return Err("Cannot plot empty list of summaries");
    }

    let padding = if border { 2 } else { 0 };
    let content_width = (width - 2 * padding) as f64;
    let border_style = if ascii {
        figure::ASCII_BORDER
    } else {
        figure::UNICODE_BORDER
    };

    use std::f64;
    let min = summaries.iter().map(|s| s.min()).fold(f64::MAX, |x, y| x.min(y));
    let max = summaries.iter().map(|s| s.max()).fold(f64::MIN, |x, y| x.max(y));

    // Used to compute relative widths of boxplots from their own ranges.
    let range = max - min;

    let mut plots = vec![];

    for s in summaries {
        // Proportion of total content width spanned by this plot.
        let p = (s.max() - s.min()) / range;

        // Boxplot content width in cols.
        let w = (content_width * p).floor().max(1.0);
        assert!(1.0 <= w);
        assert!(w <= content_width);

        let plot = plot!(stamp::Stamp::new(&summary_plot(s, w as usize, ascii)?))?;

        assert!(min <= s.min());
        let offset_p = (s.min() - min) / range;

        let offset = (offset_p * content_width).min(content_width - w);
        assert!(offset + w <= content_width);

        plots.push((plot, padding + (offset as usize)));
    }

    let height = &plots
        .iter()
        .map(|&(ref p, _)| p.height())
        .sum() + (padding * 2);

    let base = if border {
        figure::Border::new(border_style, width, height).render()
    } else {
        figure::Filled::blank(width, height).render()
    };

    let mut all_plots = plot!(stamp::Stamp::new(&base))?;

    for (i, &(ref plot, left_offset)) in plots.iter().enumerate() {
        all_plots = plot!(all_plots.layer(&plot, left_offset, padding + i * plot.height()))?;
    }

    Ok(all_plots.render())
}
<commit_msg>Adjust render precedence of boxplot elements<commit_after>mod figure;

use stamp;

use summary::Summary;

macro_rules! plot {
    ($p: expr) => {
        match $p {
            Ok(t) => Ok(t),
            Err(_) => Err("Unable to plot sample data"),
        }
    }
}

struct Boxplot {
    box_lo: f64,
    box_mid: f64,
    box_hi: f64,
    marker: f64,
    wh_lo: f64,
    wh_hi: f64,
}

impl Boxplot {
    fn from_summary(summary: &Summary) -> Self {
        let range = summary.max() - summary.min();
        let n = |x| (x - summary.min()) / range;

        Boxplot {
            box_lo: n(summary.lower_quartile()),
            box_mid: n(summary.median()),
            box_hi: n(summary.upper_quartile()),
            marker: n(summary.mean()),
            wh_lo: n(summary.min()),
            wh_hi: n(summary.max()),
        }
    }
}

struct BoxplotCols {
    box_lo: usize,
    box_mid: usize,
    box_hi: usize,
    marker: usize,
    wh_lo: usize,
    wh_hi: usize,
}

impl BoxplotCols {
    fn new(data: &Boxplot, width: usize) -> Self {
        let max_col = (width - 1) as f64;
        let to_col = |x: f64| (x * max_col).floor() as usize;

        BoxplotCols {
            box_lo: to_col(data.box_lo),
            box_mid: to_col(data.box_mid),
            box_hi: to_col(data.box_hi),
            marker: to_col(data.marker),
            wh_lo: to_col(data.wh_lo),
            wh_hi: to_col(data.wh_hi),
        }
    }
}

struct RowChars {
    wh_lo: &'static str,
    wh_lo_box_lo_fill: &'static str,
    box_lo: &'static str,
    box_lo_box_mid_fill: &'static str,
    box_mid: &'static str,
    box_mid_box_hi_fill: &'static str,
    box_hi: &'static str,
    box_hi_wh_hi_fill: &'static str,
    wh_hi: &'static str,
}

impl RowChars {
    pub fn render(&self, row: &mut Vec<String>, cols: &BoxplotCols) {
        // Lower whisker extent.
        for i in (cols.wh_lo + 1)..cols.box_lo {
            row[i] = self.wh_lo_box_lo_fill.to_string();
        }

        // Upper whisker extent.
        for i in (cols.box_hi + 1)..cols.wh_hi {
            row[i] = self.box_hi_wh_hi_fill.to_string();
        }

        // Lower box extent.
        for i in (cols.box_lo + 1)..cols.box_mid {
            row[i] = self.box_lo_box_mid_fill.to_string();
        }

        // Upper box extent.
        for i in (cols.box_mid + 1)..cols.box_hi {
            row[i] = self.box_mid_box_hi_fill.to_string();
        }

        // Lower box end.
        row[cols.box_lo] = self.box_lo.to_string();

        // Upper box end.
        row[cols.box_hi] = self.box_hi.to_string();

        // Lower whisker end.
        row[cols.wh_lo] = self.wh_lo.to_string();

        // Upper whisker end.
        row[cols.wh_hi] = self.wh_hi.to_string();

        // Middle line.
        row[cols.box_mid] = self.box_mid.to_string();
    }
}

struct BoxplotChars {
    marker: &'static str,
    rows: [RowChars; 3],
}

impl BoxplotChars {
    pub fn render(&self, summary: &Summary, width: usize) -> Result<String, &'static str> {
        let data = Boxplot::from_summary(summary);
        let cols = BoxplotCols::new(&data, width);

        let mut plot = Plot::new(width);

        self.rows[0].render(&mut plot.0, &cols);
        self.rows[1].render(&mut plot.1, &cols);
        self.rows[2].render(&mut plot.2, &cols);

        let no_marker = plot.render();

        let base = plot!(stamp::Stamp::new(&no_marker))?;
        let marker = plot!(stamp::Stamp::new(self.marker))?;
        let layered = plot!(base.layer(&marker, cols.marker, 1))?;

        Ok(layered.render())
    }
}

static ASCII_CHARS: BoxplotChars = BoxplotChars {
    marker: "x",
    rows: [
        RowChars {
            wh_lo: " ",
            wh_lo_box_lo_fill: " ",
            box_lo: "+",
            box_lo_box_mid_fill: "-",
            box_mid: "+",
            box_mid_box_hi_fill: "-",
            box_hi: "+",
            box_hi_wh_hi_fill: " ",
            wh_hi: " ",
        },
        RowChars {
            wh_lo: "|",
            wh_lo_box_lo_fill: "-",
            box_lo: "|",
            box_lo_box_mid_fill: " ",
            box_mid: "|",
            box_mid_box_hi_fill: " ",
            box_hi: "|",
            box_hi_wh_hi_fill: "-",
            wh_hi: "|",
        },
        RowChars {
            wh_lo: " ",
            wh_lo_box_lo_fill: " ",
            box_lo: "+",
            box_lo_box_mid_fill: "-",
            box_mid: "+",
            box_mid_box_hi_fill: "-",
            box_hi: "+",
            box_hi_wh_hi_fill: " ",
            wh_hi: " ",
        },
    ],
};

static UNICODE_CHARS: BoxplotChars = BoxplotChars {
    marker: "✕",
    rows: [
        RowChars {
            wh_lo: "┬",
            wh_lo_box_lo_fill: " ",
            box_lo: "┌",
            box_lo_box_mid_fill: "─",
            box_mid: "┬",
            box_mid_box_hi_fill: "─",
            box_hi: "┐",
            box_hi_wh_hi_fill: " ",
            wh_hi: "┬",
        },
        RowChars {
            wh_lo: "├",
            wh_lo_box_lo_fill: "─",
            box_lo: "┤",
            box_lo_box_mid_fill: " ",
            box_mid: "│",
            box_mid_box_hi_fill: " ",
            box_hi: "├",
            box_hi_wh_hi_fill: "─",
            wh_hi: "┤",
        },
        RowChars {
            wh_lo: "┴",
            wh_lo_box_lo_fill: " ",
            box_lo: "└",
            box_lo_box_mid_fill: "─",
            box_mid: "┴",
            box_mid_box_hi_fill: "─",
            box_hi: "┘",
            box_hi_wh_hi_fill: " ",
            wh_hi: "┴",
        },
    ],
};

fn make_row(width: usize) -> Vec<String> {
    use std::iter::repeat;

    let mut row = vec![];
    row.extend(repeat(String::from("")).take(width));

    row
}

struct Plot(Vec<String>, Vec<String>, Vec<String>);

impl Plot {
    fn new(width: usize) -> Self {
        Plot(make_row(width), make_row(width), make_row(width))
    }

    fn render(&self) -> String {
        let rows = vec![
            self.0.join(""),
            self.1.join(""),
            self.2.join(""),
        ];

        rows.join("\n")
    }
}

pub fn summary_plot(summary: &Summary, width: usize, ascii: bool) -> Result<String, &'static str> {
    let plot_style = if ascii { &ASCII_CHARS } else { &UNICODE_CHARS };

    plot_style.render(summary, width)
}

pub fn comparison_plot(
    summaries: &[&Summary],
    width: usize,
    ascii: bool,
    border: bool,
) -> Result<String, &'static str> {
    if summaries.is_empty() {
        return Err("Cannot plot empty list of summaries");
    }

    let padding = if border { 2 } else { 0 };
    let content_width = (width - 2 * padding) as f64;
    let border_style = if ascii {
        figure::ASCII_BORDER
    } else {
        figure::UNICODE_BORDER
    };

    use std::f64;
    let min = summaries.iter().map(|s| s.min()).fold(f64::MAX, |x, y| x.min(y));
    let max = summaries.iter().map(|s| s.max()).fold(f64::MIN, |x, y| x.max(y));

    // Used to compute relative widths of boxplots from their own ranges.
    let range = max - min;

    let mut plots = vec![];

    for s in summaries {
        // Proportion of total content width spanned by this plot.
        let p = (s.max() - s.min()) / range;

        // Boxplot content width in cols.
        let w = (content_width * p).floor().max(1.0);
        assert!(1.0 <= w);
        assert!(w <= content_width);

        let plot = plot!(stamp::Stamp::new(&summary_plot(s, w as usize, ascii)?))?;

        assert!(min <= s.min());
        let offset_p = (s.min() - min) / range;

        let offset = (offset_p * content_width).min(content_width - w);
        assert!(offset + w <= content_width);

        plots.push((plot, padding + (offset as usize)));
    }

    let height = &plots
        .iter()
        .map(|&(ref p, _)| p.height())
        .sum() + (padding * 2);

    let base = if border {
        figure::Border::new(border_style, width, height).render()
    } else {
        figure::Filled::blank(width, height).render()
    };

    let mut all_plots = plot!(stamp::Stamp::new(&base))?;

    for (i, &(ref plot, left_offset)) in plots.iter().enumerate() {
        all_plots = plot!(all_plots.layer(&plot, left_offset, padding + i * plot.height()))?;
    }

    Ok(all_plots.render())
}
<|endoftext|>