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

//! Memory allocation APIs
//!
//! In a given program, the standard library has one “global” memory allocator
//! that is used for example by `Box<T>` and `Vec<T>`.
//!
//! Currently the default global allocator is unspecified.
//! The compiler may link to a version of [jemalloc] on some platforms,
//! but this is not guaranteed.
//! Libraries, however, like `cdylib`s and `staticlib`s are guaranteed
//! to use the [`System`] by default.
//!
//! [jemalloc]: https://github.com/jemalloc/jemalloc
//! [`System`]: struct.System.html
//!
//! # The `#[global_allocator]` attribute
//!
//! This attribute allows configuring the choice of global allocator.
//! You can use this to implement a completely custom global allocator
//! to route all default allocation requests to a custom object.
//!
//! ```rust
//! use std::alloc::{GlobalAlloc, System, Layout};
//!
//! struct MyAllocator;
//!
//! unsafe impl GlobalAlloc for MyAllocator {
//!     unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
//!         System.alloc(layout)
//!     }
//!
//!     unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
//!         System.dealloc(ptr, layout)
//!     }
//! }
//!
//! #[global_allocator]
//! static GLOBAL: MyAllocator = MyAllocator;
//!
//! fn main() {
//!     // This `Vec` will allocate memory through `GLOBAL` above
//!     let mut v = Vec::new();
//!     v.push(1);
//! }
//! ```
//!
//! The attribute is used on a `static` item whose type implements the
//! [`GlobalAlloc`] trait. This type can be provided by an external library:
//!
//! [`GlobalAlloc`]: ../../core/alloc/trait.GlobalAlloc.html
//!
//! ```rust,ignore (demonstrates crates.io usage)
//! extern crate jemallocator;
//!
//! use jemallacator::Jemalloc;
//!
//! #[global_allocator]
//! static GLOBAL: Jemalloc = Jemalloc;
//!
//! fn main() {}
//! ```
//!
//! The `#[global_allocator]` can only be used once in a crate
//! or its recursive dependencies.

#![stable(feature = "alloc_module", since = "1.28.0")]

use core::sync::atomic::{AtomicPtr, Ordering};
use core::{mem, ptr};
use sys_common::util::dumb_print;

#[stable(feature = "alloc_module", since = "1.28.0")]
#[doc(inline)]
pub use alloc_crate::alloc::*;

#[stable(feature = "alloc_system_type", since = "1.28.0")]
#[doc(inline)]
pub use alloc_system::System;

static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut());

/// Registers a custom allocation error hook, replacing any that was previously registered.
///
/// The allocation error hook is invoked when an infallible memory allocation fails, before
/// the runtime aborts. The default hook prints a message to standard error,
/// but this behavior can be customized with the [`set_alloc_error_hook`] and
/// [`take_alloc_error_hook`] functions.
///
/// The hook is provided with a `Layout` struct which contains information
/// about the allocation that failed.
///
/// The allocation error hook is a global resource.
#[unstable(feature = "alloc_error_hook", issue = "51245")]
pub fn set_alloc_error_hook(hook: fn(Layout)) {
    HOOK.store(hook as *mut (), Ordering::SeqCst);
}

/// Unregisters the current allocation error hook, returning it.
///
/// *See also the function [`set_alloc_error_hook`].*
///
/// If no custom hook is registered, the default hook will be returned.
#[unstable(feature = "alloc_error_hook", issue = "51245")]
pub fn take_alloc_error_hook() -> fn(Layout) {
    let hook = HOOK.swap(ptr::null_mut(), Ordering::SeqCst);
    if hook.is_null() {
        default_alloc_error_hook
    } else {
        unsafe { mem::transmute(hook) }
    }
}

fn default_alloc_error_hook(layout: Layout) {
    dumb_print(format_args!("memory allocation of {} bytes failed", layout.size()));
}

#[cfg(not(test))]
#[doc(hidden)]
#[lang = "oom"]
#[unstable(feature = "alloc_internals", issue = "0")]
pub extern fn rust_oom(layout: Layout) -> ! {
    let hook = HOOK.load(Ordering::SeqCst);
    let hook: fn(Layout) = if hook.is_null() {
        default_alloc_error_hook
    } else {
        unsafe { mem::transmute(hook) }
    };
    hook(layout);
    unsafe { ::sys::abort_internal(); }
}

#[cfg(not(test))]
#[doc(hidden)]
#[allow(unused_attributes)]
#[unstable(feature = "alloc_internals", issue = "0")]
pub mod __default_lib_allocator {
    use super::{System, Layout, GlobalAlloc};
    // for symbol names src/librustc/middle/allocator.rs
    // for signatures src/librustc_allocator/lib.rs

    // linkage directives are provided as part of the current compiler allocator
    // ABI

    #[no_mangle]
    #[rustc_std_internal_symbol]
    pub unsafe extern fn __rdl_alloc(size: usize, align: usize) -> *mut u8 {
        let layout = Layout::from_size_align_unchecked(size, align);
        System.alloc(layout)
    }

    #[no_mangle]
    #[rustc_std_internal_symbol]
    pub unsafe extern fn __rdl_dealloc(ptr: *mut u8,
                                       size: usize,
                                       align: usize) {
        System.dealloc(ptr, Layout::from_size_align_unchecked(size, align))
    }

    #[no_mangle]
    #[rustc_std_internal_symbol]
    pub unsafe extern fn __rdl_realloc(ptr: *mut u8,
                                       old_size: usize,
                                       align: usize,
                                       new_size: usize) -> *mut u8 {
        let old_layout = Layout::from_size_align_unchecked(old_size, align);
        System.realloc(ptr, old_layout, new_size)
    }

    #[no_mangle]
    #[rustc_std_internal_symbol]
    pub unsafe extern fn __rdl_alloc_zeroed(size: usize, align: usize) -> *mut u8 {
        let layout = Layout::from_size_align_unchecked(size, align);
        System.alloc_zeroed(layout)
    }
}
<commit_msg>Rollup merge of #51890 - Ixrec:patch-3, r=alexcrichton<commit_after>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Memory allocation APIs
//!
//! In a given program, the standard library has one “global” memory allocator
//! that is used for example by `Box<T>` and `Vec<T>`.
//!
//! Currently the default global allocator is unspecified.
//! The compiler may link to a version of [jemalloc] on some platforms,
//! but this is not guaranteed.
//! Libraries, however, like `cdylib`s and `staticlib`s are guaranteed
//! to use the [`System`] by default.
//!
//! [jemalloc]: https://github.com/jemalloc/jemalloc
//! [`System`]: struct.System.html
//!
//! # The `#[global_allocator]` attribute
//!
//! This attribute allows configuring the choice of global allocator.
//! You can use this to implement a completely custom global allocator
//! to route all default allocation requests to a custom object.
//!
//! ```rust
//! use std::alloc::{GlobalAlloc, System, Layout};
//!
//! struct MyAllocator;
//!
//! unsafe impl GlobalAlloc for MyAllocator {
//!     unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
//!         System.alloc(layout)
//!     }
//!
//!     unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
//!         System.dealloc(ptr, layout)
//!     }
//! }
//!
//! #[global_allocator]
//! static GLOBAL: MyAllocator = MyAllocator;
//!
//! fn main() {
//!     // This `Vec` will allocate memory through `GLOBAL` above
//!     let mut v = Vec::new();
//!     v.push(1);
//! }
//! ```
//!
//! The attribute is used on a `static` item whose type implements the
//! [`GlobalAlloc`] trait. This type can be provided by an external library:
//!
//! [`GlobalAlloc`]: ../../core/alloc/trait.GlobalAlloc.html
//!
//! ```rust,ignore (demonstrates crates.io usage)
//! extern crate jemallocator;
//!
//! use jemallocator::Jemalloc;
//!
//! #[global_allocator]
//! static GLOBAL: Jemalloc = Jemalloc;
//!
//! fn main() {}
//! ```
//!
//! The `#[global_allocator]` can only be used once in a crate
//! or its recursive dependencies.

#![stable(feature = "alloc_module", since = "1.28.0")]

use core::sync::atomic::{AtomicPtr, Ordering};
use core::{mem, ptr};
use sys_common::util::dumb_print;

#[stable(feature = "alloc_module", since = "1.28.0")]
#[doc(inline)]
pub use alloc_crate::alloc::*;

#[stable(feature = "alloc_system_type", since = "1.28.0")]
#[doc(inline)]
pub use alloc_system::System;

static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut());

/// Registers a custom allocation error hook, replacing any that was previously registered.
///
/// The allocation error hook is invoked when an infallible memory allocation fails, before
/// the runtime aborts. The default hook prints a message to standard error,
/// but this behavior can be customized with the [`set_alloc_error_hook`] and
/// [`take_alloc_error_hook`] functions.
///
/// The hook is provided with a `Layout` struct which contains information
/// about the allocation that failed.
///
/// The allocation error hook is a global resource.
#[unstable(feature = "alloc_error_hook", issue = "51245")]
pub fn set_alloc_error_hook(hook: fn(Layout)) {
    HOOK.store(hook as *mut (), Ordering::SeqCst);
}

/// Unregisters the current allocation error hook, returning it.
///
/// *See also the function [`set_alloc_error_hook`].*
///
/// If no custom hook is registered, the default hook will be returned.
#[unstable(feature = "alloc_error_hook", issue = "51245")]
pub fn take_alloc_error_hook() -> fn(Layout) {
    let hook = HOOK.swap(ptr::null_mut(), Ordering::SeqCst);
    if hook.is_null() {
        default_alloc_error_hook
    } else {
        unsafe { mem::transmute(hook) }
    }
}

fn default_alloc_error_hook(layout: Layout) {
    dumb_print(format_args!("memory allocation of {} bytes failed", layout.size()));
}

#[cfg(not(test))]
#[doc(hidden)]
#[lang = "oom"]
#[unstable(feature = "alloc_internals", issue = "0")]
pub extern fn rust_oom(layout: Layout) -> ! {
    let hook = HOOK.load(Ordering::SeqCst);
    let hook: fn(Layout) = if hook.is_null() {
        default_alloc_error_hook
    } else {
        unsafe { mem::transmute(hook) }
    };
    hook(layout);
    unsafe { ::sys::abort_internal(); }
}

#[cfg(not(test))]
#[doc(hidden)]
#[allow(unused_attributes)]
#[unstable(feature = "alloc_internals", issue = "0")]
pub mod __default_lib_allocator {
    use super::{System, Layout, GlobalAlloc};
    // for symbol names src/librustc/middle/allocator.rs
    // for signatures src/librustc_allocator/lib.rs

    // linkage directives are provided as part of the current compiler allocator
    // ABI

    #[no_mangle]
    #[rustc_std_internal_symbol]
    pub unsafe extern fn __rdl_alloc(size: usize, align: usize) -> *mut u8 {
        let layout = Layout::from_size_align_unchecked(size, align);
        System.alloc(layout)
    }

    #[no_mangle]
    #[rustc_std_internal_symbol]
    pub unsafe extern fn __rdl_dealloc(ptr: *mut u8,
                                       size: usize,
                                       align: usize) {
        System.dealloc(ptr, Layout::from_size_align_unchecked(size, align))
    }

    #[no_mangle]
    #[rustc_std_internal_symbol]
    pub unsafe extern fn __rdl_realloc(ptr: *mut u8,
                                       old_size: usize,
                                       align: usize,
                                       new_size: usize) -> *mut u8 {
        let old_layout = Layout::from_size_align_unchecked(old_size, align);
        System.realloc(ptr, old_layout, new_size)
    }

    #[no_mangle]
    #[rustc_std_internal_symbol]
    pub unsafe extern fn __rdl_alloc_zeroed(size: usize, align: usize) -> *mut u8 {
        let layout = Layout::from_size_align_unchecked(size, align);
        System.alloc_zeroed(layout)
    }
}
<|endoftext|>


<commit_before>//! Tests!
extern crate fern;
extern crate log;
extern crate tempdir;

use std::io::prelude::*;
use std::{fs, io};

use log::Level::*;

mod support;

use support::manual_log;

#[test]
fn test_basic_logging_file_logging() {
    // Create a temporary directory to put a log file into for testing
    let temp_log_dir = tempdir::TempDir::new("fern").expect("Failed to set up temporary directory");
    let log_file = temp_log_dir.path().join("test.log");

    // Create a basic logger configuration
    let (_max_level, logger) = fern::Dispatch::new()
        .format(|out, msg, record| {
            out.finish(format_args!("[{}] {}", record.level(), msg))
        })
        // Only log messages Info and above
        .level(log::LevelFilter::Info)
        // Output to stdout and the log file in the temporary directory we made above to test
        .chain(io::stdout())
        .chain(fern::log_file(log_file).expect("Failed to open log file"))
        .into_log();

    let l = &*logger;
    manual_log(l, Trace, "SHOULD NOT DISPLAY");
    manual_log(l, Debug, "SHOULD NOT DISPLAY");
    manual_log(l, Info, "Test information message");
    manual_log(l, Warn, "Test warning message");
    manual_log(l, Error, "Test error message");

    // ensure all File objects are dropped and OS buffers are flushed.
    log::logger().flush();

    {
        let result = {
            let mut log_read = fs::File::open(&temp_log_dir.path().join("test.log")).unwrap();
            let mut buf = String::new();
            log_read.read_to_string(&mut buf).unwrap();
            buf
        };
        assert!(
            !result.contains("SHOULD NOT DISPLAY"),
            "expected result not including \"SHOULD_NOT_DISPLAY\", found:\n```\n{}\n```\n",
            result
        );
        assert!(
            result.contains("[INFO] Test information message"),
            "expected result including \"[INFO] Test information message\", found:\n```\n{}\n```\n",
            result
        );
        assert!(
            result.contains("[WARN] Test warning message"),
            "expected result including \"[WARN] Test warning message\", found:\n```\n{}\n```\n",
            result
        );
        assert!(
            result.contains("[ERROR] Test error message"),
            "expected result to not include \"[ERROR] Test error message\", found:\n```\n{}\n```\n",
            result
        );
    }

    drop(logger); // manual drop so file is freed before temp dir close

    temp_log_dir
        .close()
        .expect("Failed to clean up temporary directory");
}

#[test]
fn test_custom_line_separators() {
    // Create a temporary directory to put a log file into for testing
    let temp_log_dir = tempdir::TempDir::new("fern").expect("Failed to set up temporary directory");
    let log_file = temp_log_dir.path().join("test_custom_line_sep.log");

    // Create a basic logger configuration
    let (_max_level, logger) = fern::Dispatch::new()
        // default format is just the message if not specified
        // default log level is 'trace' if not specified (logs all messages)
        // output to the log file with the "\r\n" line separator.
        .chain(fern::Output::file(fern::log_file(&log_file).expect("Failed to open log file"), "\r\n"))
        .into_log();

    let l = &*logger;
    manual_log(l, Info, "message1");
    manual_log(l, Info, "message2");

    // ensure all File objects are dropped and OS buffers are flushed.
    logger.flush();

    {
        let result = {
            let mut log_read = fs::File::open(&temp_log_dir.path().join("test_custom_line_sep.log")).unwrap();
            let mut buf = String::new();
            log_read.read_to_string(&mut buf).unwrap();
            buf
        };
        assert_eq!(&result, "message1\r\nmessage2\r\n");
    }

    drop(logger); // manual drop so file is freed before temp dir close

    temp_log_dir
        .close()
        .expect("Failed to clean up temporary directory");
}
<commit_msg>Fix builds on Windows one last time.<commit_after>//! Tests!
extern crate fern;
extern crate log;
extern crate tempdir;

use std::io::prelude::*;
use std::{fs, io};

use log::Level::*;

mod support;

use support::manual_log;

#[test]
fn test_basic_logging_file_logging() {
    // Create a temporary directory to put a log file into for testing
    let temp_log_dir = tempdir::TempDir::new("fern").expect("Failed to set up temporary directory");
    let log_file = temp_log_dir.path().join("test.log");

    {
        // Create a basic logger configuration
        let (_max_level, logger) = fern::Dispatch::new()
        .format(|out, msg, record| {
            out.finish(format_args!("[{}] {}", record.level(), msg))
        })
        // Only log messages Info and above
        .level(log::LevelFilter::Info)
        // Output to stdout and the log file in the temporary directory we made above to test
        .chain(io::stdout())
        .chain(fern::log_file(log_file).expect("Failed to open log file"))
        .into_log();

        let l = &*logger;
        manual_log(l, Trace, "SHOULD NOT DISPLAY");
        manual_log(l, Debug, "SHOULD NOT DISPLAY");
        manual_log(l, Info, "Test information message");
        manual_log(l, Warn, "Test warning message");
        manual_log(l, Error, "Test error message");

        // ensure all File objects are dropped and OS buffers are flushed.
        log::logger().flush();

        {
            let result = {
                let mut log_read = fs::File::open(&temp_log_dir.path().join("test.log")).unwrap();
                let mut buf = String::new();
                log_read.read_to_string(&mut buf).unwrap();
                buf
            };
            assert!(
                !result.contains("SHOULD NOT DISPLAY"),
                "expected result not including \"SHOULD_NOT_DISPLAY\", found:\n```\n{}\n```\n",
                result
            );
            assert!(
                result.contains("[INFO] Test information message"),
                "expected result including \"[INFO] Test information message\", found:\n```\n{}\n```\n",
                result
            );
            assert!(
                result.contains("[WARN] Test warning message"),
                "expected result including \"[WARN] Test warning message\", found:\n```\n{}\n```\n",
                result
            );
            assert!(
                result.contains("[ERROR] Test error message"),
                "expected result to not include \"[ERROR] Test error message\", found:\n```\n{}\n```\n",
                result
            );
        }
    } // ensure logger is dropped before temp dir

    temp_log_dir
        .close()
        .expect("Failed to clean up temporary directory");
}

#[test]
fn test_custom_line_separators() {
    // Create a temporary directory to put a log file into for testing
    let temp_log_dir = tempdir::TempDir::new("fern").expect("Failed to set up temporary directory");
    let log_file = temp_log_dir.path().join("test_custom_line_sep.log");

    {
        // Create a basic logger configuration
        let (_max_level, logger) = fern::Dispatch::new()
        // default format is just the message if not specified
        // default log level is 'trace' if not specified (logs all messages)
        // output to the log file with the "\r\n" line separator.
        .chain(fern::Output::file(fern::log_file(&log_file).expect("Failed to open log file"), "\r\n"))
        .into_log();

        let l = &*logger;
        manual_log(l, Info, "message1");
        manual_log(l, Info, "message2");

        // ensure all File objects are dropped and OS buffers are flushed.
        logger.flush();

        {
            let result = {
                let mut log_read = fs::File::open(&temp_log_dir.path().join("test_custom_line_sep.log")).unwrap();
                let mut buf = String::new();
                log_read.read_to_string(&mut buf).unwrap();
                buf
            };
            assert_eq!(&result, "message1\r\nmessage2\r\n");
        }
    } // ensure logger is dropped before temp dir

    temp_log_dir
        .close()
        .expect("Failed to clean up temporary directory");
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use python::{Python, PythonObject, ToPythonPointer, PyClone, PyDrop};
use err::{self, PyErr, PyResult};
use super::object::PyObject;
use ffi::{self, Py_ssize_t};
use conversion::{ToPyObject, FromPyObject};

/// Represents a Python `list`.
pub struct PyList(PyObject);

pyobject_newtype!(PyList, PyList_Check, PyList_Type);

impl PyList {
    /// Construct a new list with the given elements.
    pub fn new(py: Python, elements: &[PyObject]) -> PyList {
        unsafe {
            let ptr = ffi::PyList_New(elements.len() as Py_ssize_t);
            let t = err::result_from_owned_ptr(py, ptr).unwrap().unchecked_cast_into::<PyList>();
            for (i, e) in elements.iter().enumerate() {
                ffi::PyList_SetItem(ptr, i as Py_ssize_t, e.steal_ptr(py));
            }
            t
        }
    }

    /// Gets the length of the list.
    #[inline]
    pub fn len(&self, _py: Python) -> usize {
        // non-negative Py_ssize_t should always fit into Rust usize
        unsafe {
            ffi::PyList_Size(self.0.as_ptr()) as usize
        }
    }

    /// Gets the item at the specified index.
    ///
    /// Panics if the index is out of range.
    pub fn get_item(&self, py: Python, index: usize) -> PyObject {
        // TODO: do we really want to panic here?
        assert!(index < self.len(py));
        unsafe {
            PyObject::from_borrowed_ptr(py, ffi::PyList_GetItem(self.0.as_ptr(), index as Py_ssize_t))
        }
    }

    /// Sets the item at the specified index.
    ///
    /// Panics if the index is out of range.
    pub fn set_item(&self, _py: Python, index: usize, item: PyObject) {
        let r = unsafe { ffi::PyList_SetItem(self.0.as_ptr(), index as Py_ssize_t, item.steal_ptr()) };
        assert!(r == 0);
    }

    /// Inserts an item at the specified index.
    ///
    /// Panics if the index is out of range.
    pub fn insert(&self, _py: Python, index: usize, item: PyObject) {
        let r = unsafe { ffi::PyList_Insert(self.0.as_ptr(), index as Py_ssize_t, item.as_ptr()) };
        assert!(r == 0);
    }

    // Old name for `insert`.
    #[deprecated(since="0.3.1", note="use list.insert() instead")]
    #[doc(hidden)]
    pub fn insert_item(&self, py: Python, index: usize, item: PyObject) {
        self.insert(py, index, item);
    }

    #[inline]
    pub fn iter<'a, 'p>(&'a self, py: Python<'p>) -> PyListIterator<'a, 'p> {
        PyListIterator { py: py, list: self, index: 0 }
    }
}

/// Used by `PyList::iter()`.
pub struct PyListIterator<'a, 'p> {
    py: Python<'p>,
    list: &'a PyList,
    index: usize
}

impl <'a, 'p> Iterator for PyListIterator<'a, 'p> {
    type Item = PyObject;

    #[inline]
    fn next(&mut self) -> Option<PyObject> {
        if self.index < self.list.len(self.py) {
            let item = self.list.get_item(self.py, self.index);
            self.index += 1;
            Some(item)
        } else {
            None
        }
    }

    // Note: we cannot implement size_hint because the length of the list
    // might change during the iteration.
}

/// Converts a Rust slice to a Python `list`.
///
/// Note: this conversion can be inefficient since a Python object is created
/// for each element of the list. For primitive types `T`, consider using
/// the buffer protocol instead.
impl <T> ToPyObject for [T] where T: ToPyObject {
    type ObjectType = PyList;

    fn to_py_object(&self, py: Python) -> PyList {
        unsafe {
            let ptr = ffi::PyList_New(self.len() as Py_ssize_t);
            let t = err::cast_from_owned_ptr_or_panic(py, ptr);
            for (i, e) in self.iter().enumerate() {
                let obj = e.to_py_object(py).into_object();
                ffi::PyList_SetItem(ptr, i as Py_ssize_t, obj.steal_ptr());
            }
            t
        }
    }
}

/// Converts a Rust slice to a Python `list`.
///
/// Note: this conversion can be inefficient since a Python object is created
/// for each element of the list. For primitive types `T`, consider using
/// the buffer protocol instead.
impl <T> ToPyObject for Vec<T> where T: ToPyObject {
    type ObjectType = PyList;

    fn to_py_object(&self, py: Python) -> PyList {
        self.as_slice().to_py_object(py)
    }

    fn into_py_object(self, py: Python) -> PyList {
        unsafe {
            let ptr = ffi::PyList_New(self.len() as Py_ssize_t);
            let t = err::cast_from_owned_ptr_or_panic(py, ptr);
            for (i, e) in self.into_iter().enumerate() {
                let obj = e.into_py_object(py).into_object();
                ffi::PyList_SetItem(ptr, i as Py_ssize_t, obj.steal_ptr());
            }
            t
        }
    }
}

#[cfg(test)]
mod test {
    use python::{Python, PythonObject};
    use conversion::ToPyObject;
    use objects::PyList;

    #[test]
    fn test_len() {
        let gil = Python::acquire_gil();
        let py = gil.python();
        let v = vec![1,2,3,4];
        let list = v.to_py_object(py);
        assert_eq!(4, list.len(py));
    }

    #[test]
    fn test_get_item() {
        let gil = Python::acquire_gil();
        let py = gil.python();
        let v = vec![2, 3, 5, 7];
        let list = v.to_py_object(py);
        assert_eq!(2, list.get_item(py, 0).extract::<i32>(py).unwrap());
        assert_eq!(3, list.get_item(py, 1).extract::<i32>(py).unwrap());
        assert_eq!(5, list.get_item(py, 2).extract::<i32>(py).unwrap());
        assert_eq!(7, list.get_item(py, 3).extract::<i32>(py).unwrap());
    }

    #[test]
    fn test_set_item() {
        let gil = Python::acquire_gil();
        let py = gil.python();
        let v = vec![2, 3, 5, 7];
        let list = v.to_py_object(py);
        let val = 42i32.to_py_object(py).into_object();
        assert_eq!(2, list.get_item(py, 0).extract::<i32>(py).unwrap());
        list.set_item(py, 0, val);
        assert_eq!(42, list.get_item(py, 0).extract::<i32>(py).unwrap());
    }

    #[test]
    fn test_insert() {
        let gil = Python::acquire_gil();
        let py = gil.python();
        let v = vec![2, 3, 5, 7];
        let list = v.to_py_object(py);
        let val = 42i32.to_py_object(py).into_object();
        assert_eq!(4, list.len(py));
        assert_eq!(2, list.get_item(py, 0).extract::<i32>(py).unwrap());
        list.insert(py, 0, val);
        assert_eq!(5, list.len(py));
        assert_eq!(42, list.get_item(py, 0).extract::<i32>(py).unwrap());
        assert_eq!(2, list.get_item(py, 1).extract::<i32>(py).unwrap());
    }

    #[test]
    fn test_iter() {
        let gil = Python::acquire_gil();
        let py = gil.python();
        let v = vec![2, 3, 5, 7];
        let list = v.to_py_object(py);
        let mut idx = 0;
        for el in list.iter(py) {
            assert_eq!(v[idx], el.extract::<i32>(py).unwrap());
            idx += 1;
        }
        assert_eq!(idx, v.len());
    }

    #[test]
    fn test_extract() {
        let gil = Python::acquire_gil();
        let py = gil.python();
        let v = vec![2, 3, 5, 7];
        let list = v.to_py_object(py);
        let v2 = list.into_object().extract::<Vec<i32>>(py).unwrap();
        assert_eq!(v, v2);
    }
}
<commit_msg>add append method to PyList<commit_after>// Copyright (c) 2015 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use super::object::PyObject;
use conversion::{FromPyObject, ToPyObject};
use err::{self, PyErr, PyResult};
use ffi::{self, Py_ssize_t};
use python::{PyClone, PyDrop, Python, PythonObject, ToPythonPointer};

/// Represents a Python `list`.
pub struct PyList(PyObject);

pyobject_newtype!(PyList, PyList_Check, PyList_Type);

impl PyList {
    /// Construct a new list with the given elements.
    pub fn new(py: Python, elements: &[PyObject]) -> PyList {
        unsafe {
            let ptr = ffi::PyList_New(elements.len() as Py_ssize_t);
            let t = err::result_from_owned_ptr(py, ptr)
                .unwrap()
                .unchecked_cast_into::<PyList>();
            for (i, e) in elements.iter().enumerate() {
                ffi::PyList_SetItem(ptr, i as Py_ssize_t, e.steal_ptr(py));
            }
            t
        }
    }

    /// Gets the length of the list.
    #[inline]
    pub fn len(&self, _py: Python) -> usize {
        // non-negative Py_ssize_t should always fit into Rust usize
        unsafe { ffi::PyList_Size(self.0.as_ptr()) as usize }
    }

    /// Gets the item at the specified index.
    ///
    /// Panics if the index is out of range.
    pub fn get_item(&self, py: Python, index: usize) -> PyObject {
        // TODO: do we really want to panic here?
        assert!(index < self.len(py));
        unsafe {
            PyObject::from_borrowed_ptr(
                py,
                ffi::PyList_GetItem(self.0.as_ptr(), index as Py_ssize_t),
            )
        }
    }

    /// Sets the item at the specified index.
    ///
    /// Panics if the index is out of range.
    pub fn set_item(&self, _py: Python, index: usize, item: PyObject) {
        let r =
            unsafe { ffi::PyList_SetItem(self.0.as_ptr(), index as Py_ssize_t, item.steal_ptr()) };
        assert!(r == 0);
    }

    /// Inserts an item at the specified index.
    ///
    /// Panics if the index is out of range.
    pub fn insert(&self, _py: Python, index: usize, item: PyObject) {
        let r = unsafe { ffi::PyList_Insert(self.0.as_ptr(), index as Py_ssize_t, item.as_ptr()) };
        assert!(r == 0);
    }

    // Old name for `insert`.
    #[deprecated(since="0.3.1", note="use list.insert() instead")]
    #[doc(hidden)]
    pub fn insert_item(&self, py: Python, index: usize, item: PyObject) {
        self.insert(py, index, item);
    }

    /// Appends an item to the end of the list
    pub fn append(&self, _py: Python, item: PyObject) {
        unsafe { ffi::PyList_Append(self.0.as_ptr(), item.as_ptr()) };
    }

    #[inline]
    pub fn iter<'a, 'p>(&'a self, py: Python<'p>) -> PyListIterator<'a, 'p> {
        PyListIterator {
            py: py,
            list: self,
            index: 0,
        }
    }
}

/// Used by `PyList::iter()`.
pub struct PyListIterator<'a, 'p> {
    py: Python<'p>,
    list: &'a PyList,
    index: usize,
}

impl<'a, 'p> Iterator for PyListIterator<'a, 'p> {
    type Item = PyObject;

    #[inline]
    fn next(&mut self) -> Option<PyObject> {
        if self.index < self.list.len(self.py) {
            let item = self.list.get_item(self.py, self.index);
            self.index += 1;
            Some(item)
        } else {
            None
        }
    }

    // Note: we cannot implement size_hint because the length of the list
    // might change during the iteration.
}

/// Converts a Rust slice to a Python `list`.
///
/// Note: this conversion can be inefficient since a Python object is created
/// for each element of the list. For primitive types `T`, consider using
/// the buffer protocol instead.
impl<T> ToPyObject for [T]
where
    T: ToPyObject,
{
    type ObjectType = PyList;

    fn to_py_object(&self, py: Python) -> PyList {
        unsafe {
            let ptr = ffi::PyList_New(self.len() as Py_ssize_t);
            let t = err::cast_from_owned_ptr_or_panic(py, ptr);
            for (i, e) in self.iter().enumerate() {
                let obj = e.to_py_object(py).into_object();
                ffi::PyList_SetItem(ptr, i as Py_ssize_t, obj.steal_ptr());
            }
            t
        }
    }
}

/// Converts a Rust slice to a Python `list`.
///
/// Note: this conversion can be inefficient since a Python object is created
/// for each element of the list. For primitive types `T`, consider using
/// the buffer protocol instead.
impl<T> ToPyObject for Vec<T>
where
    T: ToPyObject,
{
    type ObjectType = PyList;

    fn to_py_object(&self, py: Python) -> PyList {
        self.as_slice().to_py_object(py)
    }

    fn into_py_object(self, py: Python) -> PyList {
        unsafe {
            let ptr = ffi::PyList_New(self.len() as Py_ssize_t);
            let t = err::cast_from_owned_ptr_or_panic(py, ptr);
            for (i, e) in self.into_iter().enumerate() {
                let obj = e.into_py_object(py).into_object();
                ffi::PyList_SetItem(ptr, i as Py_ssize_t, obj.steal_ptr());
            }
            t
        }
    }
}

#[cfg(test)]
mod test {
    use conversion::ToPyObject;
    use objects::PyList;
    use python::{Python, PythonObject};

    #[test]
    fn test_len() {
        let gil = Python::acquire_gil();
        let py = gil.python();
        let v = vec![1, 2, 3, 4];
        let list = v.to_py_object(py);
        assert_eq!(4, list.len(py));
    }

    #[test]
    fn test_get_item() {
        let gil = Python::acquire_gil();
        let py = gil.python();
        let v = vec![2, 3, 5, 7];
        let list = v.to_py_object(py);
        assert_eq!(2, list.get_item(py, 0).extract::<i32>(py).unwrap());
        assert_eq!(3, list.get_item(py, 1).extract::<i32>(py).unwrap());
        assert_eq!(5, list.get_item(py, 2).extract::<i32>(py).unwrap());
        assert_eq!(7, list.get_item(py, 3).extract::<i32>(py).unwrap());
    }

    #[test]
    fn test_set_item() {
        let gil = Python::acquire_gil();
        let py = gil.python();
        let v = vec![2, 3, 5, 7];
        let list = v.to_py_object(py);
        let val = 42i32.to_py_object(py).into_object();
        assert_eq!(2, list.get_item(py, 0).extract::<i32>(py).unwrap());
        list.set_item(py, 0, val);
        assert_eq!(42, list.get_item(py, 0).extract::<i32>(py).unwrap());
    }

    #[test]
    fn test_insert() {
        let gil = Python::acquire_gil();
        let py = gil.python();
        let v = vec![2, 3, 5, 7];
        let list = v.to_py_object(py);
        let val = 42i32.to_py_object(py).into_object();
        assert_eq!(4, list.len(py));
        assert_eq!(2, list.get_item(py, 0).extract::<i32>(py).unwrap());
        list.insert(py, 0, val);
        assert_eq!(5, list.len(py));
        assert_eq!(42, list.get_item(py, 0).extract::<i32>(py).unwrap());
        assert_eq!(2, list.get_item(py, 1).extract::<i32>(py).unwrap());
    }

    #[test]
    fn test_append() {
        let gil = Python::acquire_gil();
        let py = gil.python();
        let v = vec![2, 3, 5, 7];
        let list = v.to_py_object(py);
        let val = 42i32.to_py_object(py).into_object();
        assert_eq!(4, list.len(py));
        list.append(py, val);
        assert_eq!(5, list.len(py));
        assert_eq!(42, list.get_item(py, 4).extract::<i32>(py).unwrap());
    }

    #[test]
    fn test_iter() {
        let gil = Python::acquire_gil();
        let py = gil.python();
        let v = vec![2, 3, 5, 7];
        let list = v.to_py_object(py);
        let mut idx = 0;
        for el in list.iter(py) {
            assert_eq!(v[idx], el.extract::<i32>(py).unwrap());
            idx += 1;
        }
        assert_eq!(idx, v.len());
    }

    #[test]
    fn test_extract() {
        let gil = Python::acquire_gil();
        let py = gil.python();
        let v = vec![2, 3, 5, 7];
        let list = v.to_py_object(py);
        let v2 = list.into_object().extract::<Vec<i32>>(py).unwrap();
        assert_eq!(v, v2);
    }
}
<|endoftext|>


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

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

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

use std::path::PathBuf;

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

use gtk::prelude::*;

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

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

pub struct MainController {
    window: gtk::ApplicationWindow,
    header_bar: gtk::HeaderBar,
    play_pause_btn: gtk::ToolButton,
    info_ctrl: InfoController,
    video_ctrl: VideoController,
    audio_ctrl: Rc<RefCell<AudioController>>,
    audio_drawingarea: gtk::DrawingArea,

    context: Option<Context>,

    this_opt: Option<Rc<RefCell<MainController>>>,
    keep_going: bool,
    listener_src: Option<glib::SourceId>,
    tracker_src: Option<glib::SourceId>,
}

impl MainController {
    pub fn new(builder: gtk::Builder) -> Rc<RefCell<Self>> {
        let audio_controller = AudioController::new(&builder);
        let audio_drawingarea = audio_controller.borrow().drawingarea.clone();

        let this = Rc::new(RefCell::new(MainController {
            window: builder.get_object("application-window").unwrap(),
            header_bar: builder.get_object("header-bar").unwrap(),
            play_pause_btn: builder.get_object("play_pause-toolbutton").unwrap(),
            info_ctrl: InfoController::new(&builder),
            video_ctrl: VideoController::new(&builder),
            audio_ctrl: audio_controller,
            audio_drawingarea: audio_drawingarea,

            context: None,

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

        {
            let mut this_mut = this.borrow_mut();

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

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

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

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

        this
    }

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

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

        match context.get_state() {
            gst::State::Paused => {
                self.register_tracker(17); // 60 Hz
                self.play_pause_btn.set_icon_name("media-playback-pause");
                context.play().unwrap();
            }
            gst::State::Playing => {
                context.pause().unwrap();
                self.play_pause_btn.set_icon_name("media-playback-start");
                self.remove_tracker();
            },
            state => println!("Can't play/pause in state {:?}", state),
        };

        self.context = Some(context);
    }

    pub fn stop(&mut self) {
        if let Some(context) = self.context.as_mut() {
            context.stop();
        };

        // remove callbacks in order to avoid conflict on borrowing of self
        self.remove_listener();
        self.remove_tracker();

        self.audio_ctrl.borrow_mut().cleanup();

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

    fn select_media(&mut self) {
        self.stop();

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

        if file_dlg.run() == gtk::ResponseType::Ok.into() {
            self.open_media(file_dlg.get_filename().unwrap());
        }

        file_dlg.close();
    }

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

    fn register_listener(&mut self,
        timeout: u32,
        ui_rx: Receiver<ContextMessage>,
    ) {
        let this_rc = self.this_opt.as_ref()
            .unwrap()
            .clone();

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

            let mut message_iter = ui_rx.try_iter();

            let mut keep_going = true;

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

            for message in message_iter.next() {
                match message {
                    AsyncDone => {
                        println!("Received AsyncDone");
                    },
                    InitDone => {
                        println!("Received InitDone");

                        let mut this_mut = this_rc.borrow_mut();

                        let context = this_mut.context.take()
                            .expect("... but no context available");

                        this_mut.header_bar.set_subtitle(
                            Some(context.file_name.as_str())
                        );

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

                        this_mut.context = Some(context);
                    },
                    Eos => {
                        println!("Received Eos");

                        let mut this_mut = this_rc.borrow_mut();

                        this_mut.audio_drawingarea.queue_draw();

                        this_mut.play_pause_btn.set_icon_name("media-playback-start");

                        this_mut.keep_going = false;
                        keep_going = false;
                    },
                    FailedToOpenMedia => {
                        eprintln!("ERROR: failed to open media");

                        let mut this_mut = this_rc.borrow_mut();

                        this_mut.context = None;
                        this_mut.keep_going = false;
                        keep_going = false;
                    },
                };

                if !keep_going { break; }
            }

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

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

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

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

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

    fn register_tracker(&mut self, timeout: u32) {
        let this_rc = self.this_opt.as_ref()
            .unwrap()
            .clone();

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

            let mut this_mut = this_rc.borrow_mut();

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

            let position = this_mut.context.as_mut()
                .expect("No context in tracker")
                .get_position();

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

            this_mut.info_ctrl.tic(position);
            this_mut.audio_drawingarea.queue_draw();

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

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

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

    fn open_media(&mut self, filepath: PathBuf) {
        assert_eq!(self.listener_src, None);

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

        let (ctx_tx, ui_rx) = channel();

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

        match Context::new(
            filepath,
            5_000_000_000,
            DoubleWaveformBuffer::new(),
            self.video_ctrl.video_box.clone(),
            ctx_tx
        ) {
            Ok(context) => {
                self.context = Some(context);
            },
            Err(error) => eprintln!("Error opening media: {}", error),
        };
    }
}
<commit_msg>Open media file dialog when clicking on play with no opened media (#29)<commit_after>extern crate gtk;
extern crate glib;
extern crate gstreamer as gst;

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

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

use std::path::PathBuf;

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

use gtk::prelude::*;

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

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

pub struct MainController {
    window: gtk::ApplicationWindow,
    header_bar: gtk::HeaderBar,
    play_pause_btn: gtk::ToolButton,
    info_ctrl: InfoController,
    video_ctrl: VideoController,
    audio_ctrl: Rc<RefCell<AudioController>>,
    audio_drawingarea: gtk::DrawingArea,

    context: Option<Context>,

    this_opt: Option<Rc<RefCell<MainController>>>,
    keep_going: bool,
    listener_src: Option<glib::SourceId>,
    tracker_src: Option<glib::SourceId>,
}

impl MainController {
    pub fn new(builder: gtk::Builder) -> Rc<RefCell<Self>> {
        let audio_controller = AudioController::new(&builder);
        let audio_drawingarea = audio_controller.borrow().drawingarea.clone();

        let this = Rc::new(RefCell::new(MainController {
            window: builder.get_object("application-window").unwrap(),
            header_bar: builder.get_object("header-bar").unwrap(),
            play_pause_btn: builder.get_object("play_pause-toolbutton").unwrap(),
            info_ctrl: InfoController::new(&builder),
            video_ctrl: VideoController::new(&builder),
            audio_ctrl: audio_controller,
            audio_drawingarea: audio_drawingarea,

            context: None,

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

        {
            let mut this_mut = this.borrow_mut();

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

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

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

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

        this
    }

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

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

        match context.get_state() {
            gst::State::Paused => {
                self.register_tracker(17); // 60 Hz
                self.play_pause_btn.set_icon_name("media-playback-pause");
                context.play().unwrap();
            }
            gst::State::Playing => {
                context.pause().unwrap();
                self.play_pause_btn.set_icon_name("media-playback-start");
                self.remove_tracker();
            },
            state => println!("Can't play/pause in state {:?}", state),
        };

        self.context = Some(context);
    }

    pub fn stop(&mut self) {
        if let Some(context) = self.context.as_mut() {
            context.stop();
        };

        // remove callbacks in order to avoid conflict on borrowing of self
        self.remove_listener();
        self.remove_tracker();

        self.audio_ctrl.borrow_mut().cleanup();

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

    fn select_media(&mut self) {
        self.stop();

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

        if file_dlg.run() == gtk::ResponseType::Ok.into() {
            self.open_media(file_dlg.get_filename().unwrap());
        }

        file_dlg.close();
    }

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

    fn register_listener(&mut self,
        timeout: u32,
        ui_rx: Receiver<ContextMessage>,
    ) {
        let this_rc = self.this_opt.as_ref()
            .unwrap()
            .clone();

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

            let mut message_iter = ui_rx.try_iter();

            let mut keep_going = true;

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

            for message in message_iter.next() {
                match message {
                    AsyncDone => {
                        println!("Received AsyncDone");
                    },
                    InitDone => {
                        println!("Received InitDone");

                        let mut this_mut = this_rc.borrow_mut();

                        let context = this_mut.context.take()
                            .expect("... but no context available");

                        this_mut.header_bar.set_subtitle(
                            Some(context.file_name.as_str())
                        );

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

                        this_mut.context = Some(context);
                    },
                    Eos => {
                        println!("Received Eos");

                        let mut this_mut = this_rc.borrow_mut();

                        this_mut.audio_drawingarea.queue_draw();

                        this_mut.play_pause_btn.set_icon_name("media-playback-start");

                        this_mut.keep_going = false;
                        keep_going = false;
                    },
                    FailedToOpenMedia => {
                        eprintln!("ERROR: failed to open media");

                        let mut this_mut = this_rc.borrow_mut();

                        this_mut.context = None;
                        this_mut.keep_going = false;
                        keep_going = false;
                    },
                };

                if !keep_going { break; }
            }

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

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

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

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

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

    fn register_tracker(&mut self, timeout: u32) {
        let this_rc = self.this_opt.as_ref()
            .unwrap()
            .clone();

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

            let mut this_mut = this_rc.borrow_mut();

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

            let position = this_mut.context.as_mut()
                .expect("No context in tracker")
                .get_position();

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

            this_mut.info_ctrl.tic(position);
            this_mut.audio_drawingarea.queue_draw();

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

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

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

    fn open_media(&mut self, filepath: PathBuf) {
        assert_eq!(self.listener_src, None);

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

        let (ctx_tx, ui_rx) = channel();

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

        match Context::new(
            filepath,
            5_000_000_000,
            DoubleWaveformBuffer::new(),
            self.video_ctrl.video_box.clone(),
            ctx_tx
        ) {
            Ok(context) => {
                self.context = Some(context);
            },
            Err(error) => eprintln!("Error opening media: {}", error),
        };
    }
}
<|endoftext|>


<commit_before>use std::borrow::{Borrow, BorrowMut};
use std::error::Error;
use std::hash::{Hash, Hasher};
use std::ops::{Deref, DerefMut};
use std::{fmt, mem, ptr, str};
use odds::char::{encode_utf8, EncodeUtf8Error};
use ::utf8::PascalStr;
use ::PASCAL_STRING_BUF_SIZE;

/// An owned `PascalString`. This string type stores its data the stack. It is always 256 bytes long, with
/// the first byte storing the length *of the number of bytes used*.
///
/// Note that because this string type is utf8 encoded, the first byte will not store the number of characters
/// in the string.
pub struct PascalString {
    /// The number of bytes used in the string.
    len: u8,
    /// The internal character buffer, encoded in utf8
    chars_buf: [u8; PASCAL_STRING_BUF_SIZE]
}

impl PascalString {
    #[inline]
    pub fn new() -> Self {
        Default::default()
    }

    #[inline]
    pub fn from(s: &str) -> Self {
        unimplemented!()
    }

    #[inline]
    pub fn push(&mut self, ch: char) {
        self.try_push(ch).unwrap()
    }

    #[inline]
    pub fn try_push(&mut self, ch: char) -> Result<(), EncodeUtf8Error> {
        let num_bytes_extended = try!(encode_utf8(ch, &mut self.chars_buf[self.len as usize..]));
        self.len += num_bytes_extended as u8;
        Ok(())
    }

    #[inline]
    pub fn push_str<S: AsRef<str>>(&mut self, s: S) {
        self.try_push_str(s).unwrap()
    }

    #[inline]
    pub fn try_push_str<S: AsRef<str>>(&mut self, s: S) -> Result<(), PascalStringAppendError> {
        self._try_push_str(s.as_ref())
    }

    fn _try_push_str(&mut self, s: &str) -> Result<(), PascalStringAppendError> {
        if self.len() + s.len() > PASCAL_STRING_BUF_SIZE {
            return Err(PascalStringAppendError::NoRoom);
        }
        for ch in s.chars() {
            try!(self.try_push(ch))
        }
        Ok(())
    }
}

impl Default for PascalString {
    #[inline]
    fn default() -> Self {
        PascalString {
            len: 0,
            chars_buf: [0u8; PASCAL_STRING_BUF_SIZE]
        }
    }
}

impl Clone for PascalString {
    fn clone(&self) -> Self {
        let mut clone = PascalString::default();
        clone.len = self.len;
        unsafe {
            ptr::copy_nonoverlapping(&self.chars_buf, &mut clone.chars_buf, PASCAL_STRING_BUF_SIZE);
        }
        clone
    }
}

impl Hash for PascalString {
    #[inline]
    fn hash<H: Hasher>(&self, state: &mut H) {
        state.write_u8(self.len);
        state.write(&self.chars_buf);
    }
}

impl Deref for PascalString {
    type Target = PascalStr;

    #[inline]
    fn deref(&self) -> &Self::Target {
        unsafe { mem::transmute(&self.chars_buf[0..(self.len as usize)]) }
    }
}

impl DerefMut for PascalString {
    #[inline]
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe { mem::transmute(&mut self.chars_buf[0..(self.len as usize)]) }
    }
}

#[derive(Debug, Clone, Copy)]
pub enum PascalStringAppendError {
    NoRoom,
    EncodeError(EncodeUtf8Error)
}

impl fmt::Display for PascalStringAppendError {
    fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
        let desc = self.description();
        match *self {
            PascalStringAppendError::NoRoom => fmtr.pad(desc),
            PascalStringAppendError::EncodeError(ref e) => write!(fmtr, "{}: {:?}", desc, e)
        }
    }
}

impl Error for PascalStringAppendError {
    fn description(&self) -> &str {
        match *self {
            PascalStringAppendError::NoRoom => "there is no room for the string to be appended",
            PascalStringAppendError::EncodeError(_) => "there was a problem encoding the character as utf8: {:?}"
        }
    }
}

impl From<EncodeUtf8Error> for PascalStringAppendError {
    fn from(e: EncodeUtf8Error) -> Self {
        PascalStringAppendError::EncodeError(e)
    }
}
<commit_msg>Add doc comments<commit_after>use std::borrow::{Borrow, BorrowMut};
use std::error::Error;
use std::hash::{Hash, Hasher};
use std::ops::{Deref, DerefMut};
use std::{fmt, mem, ptr, str};
use odds::char::{encode_utf8, EncodeUtf8Error};
use ::utf8::PascalStr;
use ::PASCAL_STRING_BUF_SIZE;

/// An owned `PascalString`. This string type stores its data the stack. It is always 256 bytes long, with
/// the first byte storing the length *of the number of bytes used*.
///
/// Note that because this string type is utf8 encoded, the first byte will not store the number of characters
/// in the string.
pub struct PascalString {
    /// The number of bytes used in the string.
    len: u8,
    /// The internal character buffer, encoded in utf8
    chars_buf: [u8; PASCAL_STRING_BUF_SIZE]
}

impl PascalString {
    /// Creates a new, empty `PascalString`.
    #[inline]
    pub fn new() -> Self {
        Default::default()
    }

    /// Creates a new `PascalString` with the contents of `s`.
    ///
    /// # Returns
    ///
    /// If the contents of `s` can be stored in the buffer of the `PascalString`, then it returns
    /// `Ok`. Otherwise, returns `Err`.
    #[inline]
    pub fn from(s: &str) -> Result<Self, ()> {
        unimplemented!()
    }

    /// Push a character onto the end of the string's internal buffer.
    ///
    /// # Panics
    ///
    /// Panics if there is no room to store the `char`.
    #[inline]
    pub fn push(&mut self, ch: char) {
        self.try_push(ch).unwrap()
    }

    /// Attempt to push a `char` onto the end of this string's internal buffer.
    ///
    /// # Returns
    ///
    /// Returns `Ok(())` if the operation succeeded, otherwise an error is returned.
    #[inline]
    pub fn try_push(&mut self, ch: char) -> Result<(), EncodeUtf8Error> {
        let num_bytes_extended = try!(encode_utf8(ch, &mut self.chars_buf[self.len as usize..]));
        self.len += num_bytes_extended as u8;
        Ok(())
    }

    /// Push a string onto the end of this string's internal buffer.
    ///
    /// # Panics
    ///
    /// Panics if there is no room to store the `str`.
    #[inline]
    pub fn push_str<S: AsRef<str>>(&mut self, s: S) {
        self.try_push_str(s).unwrap()
    }

    /// Attempt to push a string onto the end of this string's internal buffer.
    ///
    /// # Returns
    ///
    /// Returns `Ok(())` if the operation succeeded, otherwise an error is returned.
    #[inline]
    pub fn try_push_str<S: AsRef<str>>(&mut self, s: S) -> Result<(), PascalStringAppendError> {
        self._try_push_str(s.as_ref())
    }

    fn _try_push_str(&mut self, s: &str) -> Result<(), PascalStringAppendError> {
        if self.len() + s.len() > PASCAL_STRING_BUF_SIZE {
            return Err(PascalStringAppendError::NoRoom);
        }
        for ch in s.chars() {
            try!(self.try_push(ch))
        }
        Ok(())
    }
}

impl Default for PascalString {
    #[inline]
    fn default() -> Self {
        PascalString {
            len: 0,
            chars_buf: [0u8; PASCAL_STRING_BUF_SIZE]
        }
    }
}

impl Clone for PascalString {
    fn clone(&self) -> Self {
        let mut clone = PascalString::default();
        clone.len = self.len;
        unsafe {
            ptr::copy_nonoverlapping(&self.chars_buf, &mut clone.chars_buf, PASCAL_STRING_BUF_SIZE);
        }
        clone
    }
}

impl Hash for PascalString {
    #[inline]
    fn hash<H: Hasher>(&self, state: &mut H) {
        state.write_u8(self.len);
        state.write(&self.chars_buf);
    }
}

impl Deref for PascalString {
    type Target = PascalStr;

    #[inline]
    fn deref(&self) -> &Self::Target {
        unsafe { mem::transmute(&self.chars_buf[0..(self.len as usize)]) }
    }
}

impl DerefMut for PascalString {
    #[inline]
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe { mem::transmute(&mut self.chars_buf[0..(self.len as usize)]) }
    }
}

#[derive(Debug, Clone, Copy)]
pub enum PascalStringAppendError {
    NoRoom,
    EncodeError(EncodeUtf8Error)
}

impl fmt::Display for PascalStringAppendError {
    fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
        let desc = self.description();
        match *self {
            PascalStringAppendError::NoRoom => fmtr.pad(desc),
            PascalStringAppendError::EncodeError(ref e) => write!(fmtr, "{}: {:?}", desc, e)
        }
    }
}

impl Error for PascalStringAppendError {
    fn description(&self) -> &str {
        match *self {
            PascalStringAppendError::NoRoom => "there is no room for the string to be appended",
            PascalStringAppendError::EncodeError(_) => "there was a problem encoding the character as utf8: {:?}"
        }
    }
}

impl From<EncodeUtf8Error> for PascalStringAppendError {
    fn from(e: EncodeUtf8Error) -> Self {
        PascalStringAppendError::EncodeError(e)
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{self, str};

use util::xeval::EvalContext;
use super::Result;

/// `bytes_to_int_without_context` converts a byte arrays to an i64
/// in best effort, but without context.
/// TODO: handle overflow.
pub fn bytes_to_int_without_context(bytes: &[u8]) -> Result<i64> {
    // trim
    let mut trimed = bytes.iter().skip_while(|&&b| b == b' ' || b == b'\t');
    let mut negative = false;
    let mut r = 0i64;
    if let Some(&c) = trimed.next() {
        if c == b'-' {
            negative = true;
        } else if c >= b'0' && c <= b'9' {
            r = c as i64 - b'0' as i64;
        } else if c != b'+' {
            return Ok(0);
        }

        r = trimed.take_while(|&&c| c >= b'0' && c <= b'9')
            .fold(r, |l, &r| l * 10 + (r - b'0') as i64);
        if negative {
            r = -r;
        }
    }
    Ok(r)
}

/// `bytes_to_int` converts a byte arrays to an i64 in best effort.
/// TODO: handle overflow.
pub fn bytes_to_int(ctx: &EvalContext, bytes: &[u8]) -> Result<i64> {
    let s = try!(str::from_utf8(bytes)).trim();
    let vs = get_valid_int_prefix(s);
    try!(handle_truncate(ctx, s.len() > vs.len()));

    bytes_to_int_without_context(vs.as_bytes())
}

fn bytes_to_f64_without_context(bytes: &[u8]) -> Result<f64> {
    let f = match std::str::from_utf8(bytes) {
        Ok(s) => {
            match s.trim().parse::<f64>() {
                Ok(f) => f,
                Err(e) => {
                    error!("failed to parse float from {}: {}", s, e);
                    0.0
                }
            }
        }
        Err(e) => {
            error!("failed to convert bytes to str: {:?}", e);
            0.0
        }
    };
    Ok(f)
}

/// `bytes_to_f64` converts a byte array to a float64 in best effort.
pub fn bytes_to_f64(ctx: &EvalContext, bytes: &[u8]) -> Result<f64> {
    let s = try!(str::from_utf8(bytes)).trim();
    let vs = get_valid_float_prefix(s);
    try!(handle_truncate(ctx, s.len() > vs.len()));

    bytes_to_f64_without_context(vs.as_bytes())
}

#[inline]
fn handle_truncate(ctx: &EvalContext, is_truncated: bool) -> Result<()> {
    if is_truncated && !(ctx.ignore_truncate || ctx.truncate_as_warning) {
        Err(box_err!("[1265] Data Truncated"))
    } else {
        Ok(())
    }
}

// TODO: port `floatStrToIntStr`.
fn get_valid_int_prefix(s: &str) -> &str {
    get_valid_float_prefix(s)
}

fn get_valid_float_prefix(s: &str) -> &str {
    let mut saw_dot = false;
    let mut saw_digit = false;
    let mut valid_len = 0;
    let mut e_idx = 0;
    for (i, c) in s.chars().enumerate() {
        if c == '+' || c == '-' {
            if i != 0 && i != e_idx + 1 {
                // "1e+1" is valid.
                break;
            }
        } else if c == '.' {
            if saw_dot {
                // "1.1."
                break;
            }
            saw_dot = true;
            if saw_digit {
                // "123." is valid.
                valid_len = i + 1;
            }
        } else if c == 'e' || c == 'E' {
            if !saw_digit {
                // "+.e"
                break;
            }
            if e_idx != 0 {
                // "1e5e"
                break;
            }
            e_idx = i
        } else if c < '0' || c > '9' {
            break;
        } else {
            saw_digit = true;
            valid_len = i + 1;
        }
    }

    if valid_len == 0 { "0" } else { &s[..valid_len] }
}

#[cfg(test)]
mod test {
    use std::f64::EPSILON;
    use chrono::FixedOffset;
    use util::xeval::EvalContext;

    #[test]
    fn test_bytes_to_i64() {
        let tests: Vec<(&'static [u8], i64)> = vec![
            (b"0", 0),
            (b" 23a", 23),
            (b"\t 23a", 23),
            (b"\r23a", 0),
            (b"1", 1),
            (b"2.1", 2),
            (b"23e10", 23),
            (b"ab", 0),
            (b"4a", 4),
            (b"+1024", 1024),
            (b"-231", -231),
            (b"", 0),
        ];

        for (bs, n) in tests {
            let t = super::bytes_to_int_without_context(bs).unwrap();
            if t != n {
                panic!("expect convert {:?} to {}, but got {}", bs, n, t);
            }
        }
    }

    #[test]
    fn test_bytes_to_f64() {
        let tests: Vec<(&'static [u8], f64)> = vec![
            (b"", 0.0),
            (b" 23", 23.0),
            (b"-1", -1.0),
            (b"1.11", 1.11),
            (b"1.11.00", 0.0),
            (b"xx", 0.0),
            (b"0x00", 0.0),
            (b"11.xx", 0.0),
            (b"xx.11", 0.0),
        ];

        for (v, f) in tests {
            let ff = super::bytes_to_f64_without_context(v).unwrap();
            if (ff - f).abs() > EPSILON {
                panic!("{:?} should be decode to {}, but got {}", v, f, ff);
            }
        }
    }

    #[test]
    fn test_handle_truncate() {
        let ctxs = vec![EvalContext {
                            tz: FixedOffset::east(0),
                            ignore_truncate: true,
                            truncate_as_warning: true,
                        },
                        EvalContext {
                            tz: FixedOffset::east(0),
                            ignore_truncate: true,
                            truncate_as_warning: false,
                        },
                        EvalContext {
                            tz: FixedOffset::east(0),
                            ignore_truncate: false,
                            truncate_as_warning: true,
                        },
                        EvalContext {
                            tz: FixedOffset::east(0),
                            ignore_truncate: false,
                            truncate_as_warning: false,
                        }];

        for ctx in &ctxs {
            assert!(super::handle_truncate(ctx, false).is_ok());
        }

        assert!(super::handle_truncate(&ctxs[0], true).is_ok());
        assert!(super::handle_truncate(&ctxs[1], true).is_ok());
        assert!(super::handle_truncate(&ctxs[2], true).is_ok());
        assert!(super::handle_truncate(&ctxs[3], true).is_err());
    }

    #[test]
    fn test_get_valid_float_prefix() {
        let cases = vec![("-100", "-100"),
                         ("1abc", "1"),
                         ("-1-1", "-1"),
                         ("+1+1", "+1"),
                         ("123..34", "123."),
                         ("123.23E-10", "123.23E-10"),
                         ("1.1e1.3", "1.1e1"),
                         ("1.1e-13a", "1.1e-13"),
                         ("1.", "1."),
                         (".1", ".1"),
                         ("", "0"),
                         ("123e+", "123"),
                         ("123.e", "123.")];

        for (i, o) in cases {
            assert_eq!(super::get_valid_float_prefix(i), o);
        }
    }
}
<commit_msg>util/codec: fix get valid float (#1454)<commit_after>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{self, str};

use util::xeval::EvalContext;
use super::Result;

/// `bytes_to_int_without_context` converts a byte arrays to an i64
/// in best effort, but without context.
/// TODO: handle overflow.
pub fn bytes_to_int_without_context(bytes: &[u8]) -> Result<i64> {
    // trim
    let mut trimed = bytes.iter().skip_while(|&&b| b == b' ' || b == b'\t');
    let mut negative = false;
    let mut r = 0i64;
    if let Some(&c) = trimed.next() {
        if c == b'-' {
            negative = true;
        } else if c >= b'0' && c <= b'9' {
            r = c as i64 - b'0' as i64;
        } else if c != b'+' {
            return Ok(0);
        }

        r = trimed.take_while(|&&c| c >= b'0' && c <= b'9')
            .fold(r, |l, &r| l * 10 + (r - b'0') as i64);
        if negative {
            r = -r;
        }
    }
    Ok(r)
}

/// `bytes_to_int` converts a byte arrays to an i64 in best effort.
/// TODO: handle overflow.
pub fn bytes_to_int(ctx: &EvalContext, bytes: &[u8]) -> Result<i64> {
    let s = try!(str::from_utf8(bytes)).trim();
    let vs = get_valid_int_prefix(s);
    try!(handle_truncate(ctx, s.len() > vs.len()));

    bytes_to_int_without_context(vs.as_bytes())
}

fn bytes_to_f64_without_context(bytes: &[u8]) -> Result<f64> {
    let f = match std::str::from_utf8(bytes) {
        Ok(s) => {
            match s.trim().parse::<f64>() {
                Ok(f) => f,
                Err(e) => {
                    error!("failed to parse float from {}: {}", s, e);
                    0.0
                }
            }
        }
        Err(e) => {
            error!("failed to convert bytes to str: {:?}", e);
            0.0
        }
    };
    Ok(f)
}

/// `bytes_to_f64` converts a byte array to a float64 in best effort.
pub fn bytes_to_f64(ctx: &EvalContext, bytes: &[u8]) -> Result<f64> {
    let s = try!(str::from_utf8(bytes)).trim();
    let vs = get_valid_float_prefix(s);
    try!(handle_truncate(ctx, s.len() > vs.len()));

    bytes_to_f64_without_context(vs.as_bytes())
}

#[inline]
fn handle_truncate(ctx: &EvalContext, is_truncated: bool) -> Result<()> {
    if is_truncated && !(ctx.ignore_truncate || ctx.truncate_as_warning) {
        Err(box_err!("[1265] Data Truncated"))
    } else {
        Ok(())
    }
}

// TODO: port `floatStrToIntStr`.
fn get_valid_int_prefix(s: &str) -> &str {
    get_valid_float_prefix(s)
}

fn get_valid_float_prefix(s: &str) -> &str {
    let mut saw_dot = false;
    let mut saw_digit = false;
    let mut valid_len = 0;
    let mut e_idx = 0;
    for (i, c) in s.chars().enumerate() {
        if c == '+' || c == '-' {
            if i != 0 && i != e_idx + 1 {
                // "1e+1" is valid.
                break;
            }
        } else if c == '.' {
            if saw_dot || e_idx > 0 {
                // "1.1." or "1e1.1"
                break;
            }
            saw_dot = true;
            if saw_digit {
                // "123." is valid.
                valid_len = i + 1;
            }
        } else if c == 'e' || c == 'E' {
            if !saw_digit {
                // "+.e"
                break;
            }
            if e_idx != 0 {
                // "1e5e"
                break;
            }
            e_idx = i
        } else if c < '0' || c > '9' {
            break;
        } else {
            saw_digit = true;
            valid_len = i + 1;
        }
    }

    if valid_len == 0 { "0" } else { &s[..valid_len] }
}

#[cfg(test)]
mod test {
    use std::f64::EPSILON;
    use chrono::FixedOffset;
    use util::xeval::EvalContext;

    #[test]
    fn test_bytes_to_i64() {
        let tests: Vec<(&'static [u8], i64)> = vec![
            (b"0", 0),
            (b" 23a", 23),
            (b"\t 23a", 23),
            (b"\r23a", 0),
            (b"1", 1),
            (b"2.1", 2),
            (b"23e10", 23),
            (b"ab", 0),
            (b"4a", 4),
            (b"+1024", 1024),
            (b"-231", -231),
            (b"", 0),
        ];

        for (bs, n) in tests {
            let t = super::bytes_to_int_without_context(bs).unwrap();
            if t != n {
                panic!("expect convert {:?} to {}, but got {}", bs, n, t);
            }
        }
    }

    #[test]
    fn test_bytes_to_f64() {
        let tests: Vec<(&'static [u8], f64)> = vec![
            (b"", 0.0),
            (b" 23", 23.0),
            (b"-1", -1.0),
            (b"1.11", 1.11),
            (b"1.11.00", 0.0),
            (b"xx", 0.0),
            (b"0x00", 0.0),
            (b"11.xx", 0.0),
            (b"xx.11", 0.0),
        ];

        for (v, f) in tests {
            let ff = super::bytes_to_f64_without_context(v).unwrap();
            if (ff - f).abs() > EPSILON {
                panic!("{:?} should be decode to {}, but got {}", v, f, ff);
            }
        }
    }

    #[test]
    fn test_handle_truncate() {
        let ctxs = vec![EvalContext {
                            tz: FixedOffset::east(0),
                            ignore_truncate: true,
                            truncate_as_warning: true,
                        },
                        EvalContext {
                            tz: FixedOffset::east(0),
                            ignore_truncate: true,
                            truncate_as_warning: false,
                        },
                        EvalContext {
                            tz: FixedOffset::east(0),
                            ignore_truncate: false,
                            truncate_as_warning: true,
                        },
                        EvalContext {
                            tz: FixedOffset::east(0),
                            ignore_truncate: false,
                            truncate_as_warning: false,
                        }];

        for ctx in &ctxs {
            assert!(super::handle_truncate(ctx, false).is_ok());
        }

        assert!(super::handle_truncate(&ctxs[0], true).is_ok());
        assert!(super::handle_truncate(&ctxs[1], true).is_ok());
        assert!(super::handle_truncate(&ctxs[2], true).is_ok());
        assert!(super::handle_truncate(&ctxs[3], true).is_err());
    }

    #[test]
    fn test_get_valid_float_prefix() {
        let cases = vec![("-100", "-100"),
                         ("1abc", "1"),
                         ("-1-1", "-1"),
                         ("+1+1", "+1"),
                         ("123..34", "123."),
                         ("123.23E-10", "123.23E-10"),
                         ("1.1e1.3", "1.1e1"),
                         ("11e1.3", "11e1"),
                         ("1.1e-13a", "1.1e-13"),
                         ("1.", "1."),
                         (".1", ".1"),
                         ("", "0"),
                         ("123e+", "123"),
                         ("123.e", "123.")];

        for (i, o) in cases {
            assert_eq!(super::get_valid_float_prefix(i), o);
        }
    }
}
<|endoftext|>