<commit_before>//!
//! Raw communication channel to the FUSE kernel driver.
//!

use std::io;
use std::ffi::{CString, CStr, OsStr, AsOsStr};
use std::os::unix::ffi::OsStrExt;
use std::path::{PathBuf, Path};
use libc::{c_char, c_int, c_void, size_t};
use fuse::{fuse_args, fuse_mount_compat25};

// Libc provides iovec based I/O using readv and writev functions
#[allow(dead_code, non_camel_case_types)]
mod libc {
    use libc::{c_char, c_int, c_void, size_t, ssize_t};

    /// Iovec data structure for readv and writev calls.
    #[repr(C)]
    pub struct iovec {
        pub iov_base: *const c_void,
        pub iov_len: size_t,
    }

    extern "system" {
        /// Read data from fd into multiple buffers
        pub fn readv (fd: c_int, iov: *mut iovec, iovcnt: c_int) -> ssize_t;
        /// Write data from multiple buffers to fd
        pub fn writev (fd: c_int, iov: *const iovec, iovcnt: c_int) -> ssize_t;

        pub fn realpath (file_name: *const c_char, resolved_name: *mut c_char) -> *const c_char;

        #[cfg(target_os = "macos")]
        pub fn unmount(dir: *const c_char, flags: c_int) -> c_int;
        #[cfg(not(target_os = "macos"))]
        pub fn umount(dir: *const c_char) -> c_int;
    }

    /// Max length for path names. 4096 should be reasonable safe (OS X uses 1024, Linux uses 4096)
    pub const PATH_MAX: usize = 4096;
}

/// Wrapper around libc's realpath.  Returns the errno value if the real path cannot be obtained.
/// FIXME: Use Rust's realpath method once available in std (see also https://github.com/mozilla/rust/issues/11857)
fn real_path (path: &CStr) -> io::Result<CString> {
    let mut resolved: Vec<c_char> = Vec::with_capacity(libc::PATH_MAX);
    unsafe {
        if libc::realpath(path.as_ptr(), resolved.as_mut_ptr()).is_null() {
            Err(io::Error::last_os_error())
        } else {
            // Using CStr::from_ptr gets the correct string length via strlen()
            let cresolved = CStr::from_ptr(resolved.as_ptr());
            Ok(CString::new(cresolved.to_bytes()).unwrap())
        }
    }
}

/// Helper function to provide options as a fuse_args struct
/// (which contains an argc count and an argv pointer)
fn with_fuse_args<T, F: FnOnce(&fuse_args) -> T> (options: &[&OsStr], f: F) -> T {
    let mut args: Vec<CString> = vec![CString::new("rust-fuse").unwrap()];
    args.extend(options.iter().map(|s| s.to_cstring().unwrap() ));
    let argptrs: Vec<*const i8> = args.iter().map(|s| s.as_ptr()).collect();
    f(&fuse_args { argc: argptrs.len() as i32, argv: argptrs.as_ptr(), allocated: 0 })
}

/// A raw communication channel to the FUSE kernel driver
pub struct Channel {
    mountpoint: PathBuf,
    fd: c_int,
}

impl Channel {
    /// Create a new communication channel to the kernel driver by mounting the
    /// given path. The kernel driver will delegate filesystem operations of
    /// the given path to the channel. If the channel is dropped, the path is
    /// unmounted.
    pub fn new (mountpoint: &Path, options: &[&OsStr]) -> io::Result<Channel> {
        let mnt = try!(mountpoint.as_os_str().to_cstring());
        real_path(&mnt).and_then(|mnt| {
            with_fuse_args(options, |args| {
                let fd = unsafe { fuse_mount_compat25(mnt.as_ptr(), args) };
                if fd < 0 {
                    Err(io::Error::last_os_error())
                } else {
                    let mountpoint = PathBuf::from(<OsStr as OsStrExt>::from_bytes(mnt.as_bytes()));
                    Ok(Channel { mountpoint: mountpoint, fd: fd })
                }
            })
        })
    }

    /// Return path of the mounted filesystem
    pub fn mountpoint (&self) -> &Path {
        &self.mountpoint
    }

    /// Receives data up to the capacity of the given buffer (can block).
    pub fn receive (&self, buffer: &mut Vec<u8>) -> io::Result<()> {
        let rc = unsafe { ::libc::read(self.fd, buffer.as_ptr() as *mut c_void, buffer.capacity() as size_t) };
        if rc < 0 {
            Err(io::Error::last_os_error())
        } else {
            unsafe { buffer.set_len(rc as usize); }
            Ok(())
        }
    }

    /// Returns a sender object for this channel. The sender object can be
    /// used to send to the channel. Multiple sender objects can be used
    /// and they can safely be sent to other threads.
    pub fn sender (&self) -> ChannelSender {
        // Since write/writev syscalls are threadsafe, we can simply create
        // a sender by using the same fd and use it in other threads. Only
        // the channel closes the fd when dropped. If any sender is used after
        // dropping the channel, it'll return an EBADF error.
        ChannelSender { fd: self.fd }
    }
}

impl Drop for Channel {
    fn drop (&mut self) {
        // TODO: send ioctl FUSEDEVIOCSETDAEMONDEAD on OS X before closing the fd
        // Close the communication channel to the kernel driver
        // (closing it before unnmount prevents sync unmount deadlock)
        unsafe { ::libc::close(self.fd); }
        // Unmount this channel's mount point
        let _ = unmount(&self.mountpoint);
    }
}

#[derive(Clone, Copy)]
pub struct ChannelSender {
    fd: c_int,
}

impl ChannelSender {
    /// Send all data in the slice of slice of bytes in a single write (can block).
    pub fn send (&self, buffer: &[&[u8]]) -> io::Result<()> {
        let iovecs: Vec<libc::iovec> = buffer.iter().map(|d| {
            libc::iovec { iov_base: d.as_ptr() as *const c_void, iov_len: d.len() as size_t }
        }).collect();
        let rc = unsafe { libc::writev(self.fd, iovecs.as_ptr(), iovecs.len() as c_int) };
        if rc < 0 {
            Err(io::Error::last_os_error())
        } else {
            Ok(())
        }
    }
}

/// Unmount an arbitrary mount point
pub fn unmount (mountpoint: &Path) -> io::Result<()> {
    // fuse_unmount_compat22 unfortunately doesn't return a status. Additionally,
    // it attempts to call realpath, which in turn calls into the filesystem. So
    // if the filesystem returns an error, the unmount does not take place, with
    // no indication of the error available to the caller. So we call unmount
    // directly, which is what osxfuse does anyway, since we already converted
    // to the real path when we first mounted.
    #[cfg(target_os = "macos")] #[inline]
    fn libc_umount (mnt: &CStr) -> c_int { unsafe { libc::unmount(mnt.as_ptr(), 0) } }
    #[cfg(not(target_os = "macos"))] #[inline]
    fn libc_umount (mnt: &CStr) -> c_int { unsafe { libc::umount(mnt.as_ptr()) } }

    let mnt = try!(mountpoint.as_os_str().to_cstring());
    let rc = libc_umount(&mnt);
    if rc < 0 {
        Err(io::Error::last_os_error())
    } else {
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::with_fuse_args;
    use std::ffi::{CStr, OsStr};

    #[test]
    fn fuse_args () {
        with_fuse_args(&[OsStr::new("foo"), OsStr::new("bar")], |args| {
            assert_eq!(args.argc, 3);
            assert_eq!(unsafe { CStr::from_ptr(*args.argv.offset(0)).to_bytes() }, b"rust-fuse");
            assert_eq!(unsafe { CStr::from_ptr(*args.argv.offset(1)).to_bytes() }, b"foo");
            assert_eq!(unsafe { CStr::from_ptr(*args.argv.offset(2)).to_bytes() }, b"bar");
        });
    }
}
<commit_msg>convert the Option from to_cstring() into an io::Result<commit_after>//!
//! Raw communication channel to the FUSE kernel driver.
//!

use std::io;
use std::ffi::{CString, CStr, OsStr, AsOsStr};
use std::os::unix::ffi::OsStrExt;
use std::path::{PathBuf, Path};
use libc::{c_char, c_int, c_void, size_t};
use fuse::{fuse_args, fuse_mount_compat25};

// Libc provides iovec based I/O using readv and writev functions
#[allow(dead_code, non_camel_case_types)]
mod libc {
    use libc::{c_char, c_int, c_void, size_t, ssize_t};

    /// Iovec data structure for readv and writev calls.
    #[repr(C)]
    pub struct iovec {
        pub iov_base: *const c_void,
        pub iov_len: size_t,
    }

    extern "system" {
        /// Read data from fd into multiple buffers
        pub fn readv (fd: c_int, iov: *mut iovec, iovcnt: c_int) -> ssize_t;
        /// Write data from multiple buffers to fd
        pub fn writev (fd: c_int, iov: *const iovec, iovcnt: c_int) -> ssize_t;

        pub fn realpath (file_name: *const c_char, resolved_name: *mut c_char) -> *const c_char;

        #[cfg(target_os = "macos")]
        pub fn unmount(dir: *const c_char, flags: c_int) -> c_int;
        #[cfg(not(target_os = "macos"))]
        pub fn umount(dir: *const c_char) -> c_int;
    }

    /// Max length for path names. 4096 should be reasonable safe (OS X uses 1024, Linux uses 4096)
    pub const PATH_MAX: usize = 4096;
}

/// Wrapper around libc's realpath.  Returns the errno value if the real path cannot be obtained.
/// FIXME: Use Rust's realpath method once available in std (see also https://github.com/mozilla/rust/issues/11857)
fn real_path (path: &CStr) -> io::Result<CString> {
    let mut resolved: Vec<c_char> = Vec::with_capacity(libc::PATH_MAX);
    unsafe {
        if libc::realpath(path.as_ptr(), resolved.as_mut_ptr()).is_null() {
            Err(io::Error::last_os_error())
        } else {
            // Using CStr::from_ptr gets the correct string length via strlen()
            let cresolved = CStr::from_ptr(resolved.as_ptr());
            Ok(CString::new(cresolved.to_bytes()).unwrap())
        }
    }
}

/// Helper function to provide options as a fuse_args struct
/// (which contains an argc count and an argv pointer)
fn with_fuse_args<T, F: FnOnce(&fuse_args) -> T> (options: &[&OsStr], f: F) -> T {
    let mut args: Vec<CString> = vec![CString::new("rust-fuse").unwrap()];
    args.extend(options.iter().map(|s| s.to_cstring().unwrap() ));
    let argptrs: Vec<*const i8> = args.iter().map(|s| s.as_ptr()).collect();
    f(&fuse_args { argc: argptrs.len() as i32, argv: argptrs.as_ptr(), allocated: 0 })
}

/// A raw communication channel to the FUSE kernel driver
pub struct Channel {
    mountpoint: PathBuf,
    fd: c_int,
}

impl Channel {
    /// Create a new communication channel to the kernel driver by mounting the
    /// given path. The kernel driver will delegate filesystem operations of
    /// the given path to the channel. If the channel is dropped, the path is
    /// unmounted.
    pub fn new (mountpoint: &Path, options: &[&OsStr]) -> io::Result<Channel> {
        let mnt = try!(mountpoint.as_os_str().to_cstring().ok_or(
                io::Error::new(io::ErrorKind::InvalidInput, "invalid path")));
        real_path(&mnt).and_then(|mnt| {
            with_fuse_args(options, |args| {
                let fd = unsafe { fuse_mount_compat25(mnt.as_ptr(), args) };
                if fd < 0 {
                    Err(io::Error::last_os_error())
                } else {
                    let mountpoint = PathBuf::from(<OsStr as OsStrExt>::from_bytes(mnt.as_bytes()));
                    Ok(Channel { mountpoint: mountpoint, fd: fd })
                }
            })
        })
    }

    /// Return path of the mounted filesystem
    pub fn mountpoint (&self) -> &Path {
        &self.mountpoint
    }

    /// Receives data up to the capacity of the given buffer (can block).
    pub fn receive (&self, buffer: &mut Vec<u8>) -> io::Result<()> {
        let rc = unsafe { ::libc::read(self.fd, buffer.as_ptr() as *mut c_void, buffer.capacity() as size_t) };
        if rc < 0 {
            Err(io::Error::last_os_error())
        } else {
            unsafe { buffer.set_len(rc as usize); }
            Ok(())
        }
    }

    /// Returns a sender object for this channel. The sender object can be
    /// used to send to the channel. Multiple sender objects can be used
    /// and they can safely be sent to other threads.
    pub fn sender (&self) -> ChannelSender {
        // Since write/writev syscalls are threadsafe, we can simply create
        // a sender by using the same fd and use it in other threads. Only
        // the channel closes the fd when dropped. If any sender is used after
        // dropping the channel, it'll return an EBADF error.
        ChannelSender { fd: self.fd }
    }
}

impl Drop for Channel {
    fn drop (&mut self) {
        // TODO: send ioctl FUSEDEVIOCSETDAEMONDEAD on OS X before closing the fd
        // Close the communication channel to the kernel driver
        // (closing it before unnmount prevents sync unmount deadlock)
        unsafe { ::libc::close(self.fd); }
        // Unmount this channel's mount point
        let _ = unmount(&self.mountpoint);
    }
}

#[derive(Clone, Copy)]
pub struct ChannelSender {
    fd: c_int,
}

impl ChannelSender {
    /// Send all data in the slice of slice of bytes in a single write (can block).
    pub fn send (&self, buffer: &[&[u8]]) -> io::Result<()> {
        let iovecs: Vec<libc::iovec> = buffer.iter().map(|d| {
            libc::iovec { iov_base: d.as_ptr() as *const c_void, iov_len: d.len() as size_t }
        }).collect();
        let rc = unsafe { libc::writev(self.fd, iovecs.as_ptr(), iovecs.len() as c_int) };
        if rc < 0 {
            Err(io::Error::last_os_error())
        } else {
            Ok(())
        }
    }
}

/// Unmount an arbitrary mount point
pub fn unmount (mountpoint: &Path) -> io::Result<()> {
    // fuse_unmount_compat22 unfortunately doesn't return a status. Additionally,
    // it attempts to call realpath, which in turn calls into the filesystem. So
    // if the filesystem returns an error, the unmount does not take place, with
    // no indication of the error available to the caller. So we call unmount
    // directly, which is what osxfuse does anyway, since we already converted
    // to the real path when we first mounted.
    #[cfg(target_os = "macos")] #[inline]
    fn libc_umount (mnt: &CStr) -> c_int { unsafe { libc::unmount(mnt.as_ptr(), 0) } }
    #[cfg(not(target_os = "macos"))] #[inline]
    fn libc_umount (mnt: &CStr) -> c_int { unsafe { libc::umount(mnt.as_ptr()) } }

    let mnt = try!(mountpoint.as_os_str().to_cstring().ok_or(
            io::Error::new(io::ErrorKind::InvalidInput, "invalid path")));
    let rc = libc_umount(&mnt);
    if rc < 0 {
        Err(io::Error::last_os_error())
    } else {
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::with_fuse_args;
    use std::ffi::{CStr, OsStr};

    #[test]
    fn fuse_args () {
        with_fuse_args(&[OsStr::new("foo"), OsStr::new("bar")], |args| {
            assert_eq!(args.argc, 3);
            assert_eq!(unsafe { CStr::from_ptr(*args.argv.offset(0)).to_bytes() }, b"rust-fuse");
            assert_eq!(unsafe { CStr::from_ptr(*args.argv.offset(1)).to_bytes() }, b"foo");
            assert_eq!(unsafe { CStr::from_ptr(*args.argv.offset(2)).to_bytes() }, b"bar");
        });
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! Utilities for working with the chunked format.
//!
//! The chunked file format is extremely simple serialization procedure where
//! each chunk of raw bytes is prepended with the size of the chunk (as 64-bit
//! unsigned big-endian integer).
//!
//! Its primary application is streaming encoding and decoding of blobs of data
//! in formats that do not support or are inefficient for these purposes (such
//! as serialized Protocol Buffer messages).

use std::io::Cursor;

use byteorder::BigEndian;

/// Encodes a given iterator over binary blobs into the chunked format.
///
/// This is a streaming encoder and performs the encoding in a lazy way. It
/// should compose well with other streaming encoders (e.g. these offered by
/// the [`flate2`] crate).
///
/// [`flate2`]: https://crates.io/crates/flate2
///
/// # Examples
///
/// ```no_run
/// use std::fs::File;
///
/// let data = [b"foo", b"bar", b"baz"];
///
/// let mut stream = rrg::chunked::encode(data.iter().map(|blob| &blob[..]));
/// let mut file = File::create("output.chunked").unwrap();
/// std::io::copy(&mut stream, &mut file).unwrap();
/// ```
pub fn encode<I, M>(iter: I) -> Encode<I>
where
    I: Iterator<Item = M>,
    M: prost::Message,
{
    Encode {
        iter: iter,
        cur: Cursor::new(vec!()),
    }
}

/// Decodes a buffer in the chunked format into binary blobs.
///
/// This is a streaming decoder and performs the decoding in a lazy way. It
/// should compose well with other streaming decoders (e.g. these offered by the
/// [`flate2`] crate).
///
/// [`flate2`]: https://crates.io/crates/flate2
///
/// # Examples
///
/// ```no_run
/// use std::fs::File;
///
/// let file = File::open("input.chunked").unwrap();
/// for (idx, blob) in rrg::chunked::decode(file).enumerate() {
///     println!("blob #{}: {:?}", idx, blob.unwrap());
/// }
/// ```
pub fn decode<R, M>(buf: R) -> Decode<R, M>
where
    R: std::io::Read,
    M: prost::Message,
{
    Decode {
        reader: buf,
        buf: vec!(),
        marker: std::marker::PhantomData,
    }
}

/// Streaming encoder for the chunked format.
///
/// It implements the `Read` trait, lazily polling the underlying chunk iterator
/// as more bytes is needed.
///
/// Instances of this type can be constructed using the [`encode`] function.
///
/// [`encode`]: fn.encode.html
pub struct Encode<I> {
    iter: I,
    cur: Cursor<Vec<u8>>,
}

impl<I, M> Encode<I>
where
    I: Iterator<Item = M>,
    M: prost::Message,
{
    /// Checks whether all the data from the underlying cursor has been read.
    fn is_empty(&self) -> bool {
        self.cur.position() == self.cur.get_ref().len() as u64
    }

    /// Pulls another blob of data from the underlying iterator.
    fn pull(&mut self) -> std::io::Result<()> {
        use byteorder::WriteBytesExt as _;

        let msg = match self.iter.next() {
            Some(msg) => msg,
            None => return Ok(()),
        };

        self.cur.get_mut().clear();
        self.cur.set_position(0);

        self.cur.write_u64::<BigEndian>(msg.encoded_len() as u64)?;
        msg.encode(&mut self.cur.get_mut())?;
        self.cur.set_position(0);

        Ok(())
    }
}

impl<I, M> std::io::Read for Encode<I>
where
    I: Iterator<Item = M>,
    M: prost::Message,
{
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        if self.is_empty() {
            self.pull()?;
        }

        self.cur.read(buf)
    }
}

/// Streaming decoder for the chunked format.
///
/// It implements the `Iterator` trait yielding chunks of decoded blobs, lazily
/// decoding data from the underlying buffer.
///
/// Instances of this type can be constructed using the [`decode`] function.
///
/// [`decode`]: fn.decode.html
pub struct Decode<R, M> {
    reader: R,
    buf: Vec<u8>,
    marker: std::marker::PhantomData<M>,
}

impl<R: std::io::Read, M> Decode<R, M> {

    /// Reads a size tag from the underlying buffer.
    ///
    /// It will return `None` if the is no more data in the buffer.
    fn read_len(&mut self) -> std::io::Result<Option<usize>> {
        use byteorder::ReadBytesExt as _;

        let mut buf = [0; 8];
        match self.reader.read(&mut buf[..])? {
            8 => (),
            0 => return Ok(None),
            _ => return Err(SizeTagError.into()),
        }

        let len = (&buf[..]).read_u64::<BigEndian>()? as usize;
        Ok(Some(len))
    }
}

impl<R, M> Iterator for Decode<R, M>
where
    R: std::io::Read,
    M: prost::Message + Default,
{
    type Item = std::io::Result<M>;

    fn next(&mut self) -> Option<std::io::Result<M>> {
        let len = match self.read_len() {
            Ok(Some(len)) => len,
            Ok(None) => return None,
            Err(error) => return Some(Err(error)),
        };

        self.buf.resize(len, u8::default());
        match self.reader.read_exact(&mut self.buf[..]) {
            Ok(()) => (),
            Err(error) => return Some(Err(error)),
        }

        let msg = match M::decode(&self.buf[..]) {
            Ok(msg) => msg,
            Err(error) => return Some(Err(error.into())),
        };

        Some(Ok(msg))
    }
}

/// An error type for errors encountered when reading the size tag.
#[derive(Debug)]
struct SizeTagError;

impl std::fmt::Display for SizeTagError {

    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(fmt, "incorrect length of the size tag")
    }
}

impl std::error::Error for SizeTagError {
}

impl From<SizeTagError> for std::io::Error {

    fn from(error: SizeTagError) -> std::io::Error {
        std::io::Error::new(std::io::ErrorKind::InvalidData, error)
    }
}

#[cfg(test)]
pub mod tests {

    use super::*;

    #[test]
    pub fn test_encode_empty_iter() {
        use std::io::Read as _;

        let mut stream = encode(std::iter::empty::<()>());

        let mut output = vec!();
        stream.read_to_end(&mut output).unwrap();

        assert!(output.is_empty());
    }

    #[test]
    pub fn test_decode_empty_buf() {
        let buf: &[u8] = b"";
        let mut iter = decode::<_, ()>(buf);

        assert!(iter.next().is_none());
    }

    #[test]
    pub fn test_decode_incorrect_size_tag() {
        let buf: &[u8] = b"\x12\x34\x56";
        let mut iter = decode::<_, ()>(buf);

        let error = iter.next().unwrap().unwrap_err();
        assert_eq!(error.kind(), std::io::ErrorKind::InvalidData);
    }

    #[test]
    pub fn test_decode_short_input() {
        let buf: &[u8] = b"\x00\x00\x00\x00\x00\x00\x00\x42foo";
        let mut iter = decode::<_, ()>(buf);

        let error = iter.next().unwrap().unwrap_err();
        assert_eq!(error.kind(), std::io::ErrorKind::UnexpectedEof);
    }

    #[test]
    pub fn test_encode_and_decode_single_message() {
        let mut iter = decode(encode(vec!(String::from("foo")).into_iter()))
            .map(Result::unwrap);

        assert_eq!(iter.next(), Some(String::from("foo")));
        assert_eq!(iter.next(), None);
    }

    #[test]
    pub fn test_encode_and_decode_single_unit_message() {
        let mut iter = decode(encode(vec!(()).into_iter()))
            .map(Result::unwrap);

        assert_eq!(iter.next(), Some(()));
        assert_eq!(iter.next(), None);
    }

    #[test]
    pub fn test_encode_and_decode_multiple_messages() {
        let msgs = vec! {
            b"foo".to_vec(),
            b"bar".to_vec(),
            b"baz".to_vec(),
        };

        let mut iter = decode(encode(msgs.into_iter()))
            .map(Result::unwrap);

        assert_eq!(iter.next(), Some(b"foo".to_vec()));
        assert_eq!(iter.next(), Some(b"bar".to_vec()));
        assert_eq!(iter.next(), Some(b"baz".to_vec()));
        assert_eq!(iter.next(), None);
    }

    #[test]
    pub fn test_encode_and_decode_multiple_unit_messages() {
        let msgs = vec!((), (), ());

        let mut iter = decode(encode(msgs.into_iter()))
            .map(Result::unwrap);

        assert_eq!(iter.next(), Some(()));
        assert_eq!(iter.next(), Some(()));
        assert_eq!(iter.next(), Some(()));
        assert_eq!(iter.next(), None);
    }
}
<commit_msg>Fix a chunked decoder bug with partial size tags.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! Utilities for working with the chunked format.
//!
//! The chunked file format is extremely simple serialization procedure where
//! each chunk of raw bytes is prepended with the size of the chunk (as 64-bit
//! unsigned big-endian integer).
//!
//! Its primary application is streaming encoding and decoding of blobs of data
//! in formats that do not support or are inefficient for these purposes (such
//! as serialized Protocol Buffer messages).

use std::io::Cursor;

use byteorder::BigEndian;

/// Encodes a given iterator over binary blobs into the chunked format.
///
/// This is a streaming encoder and performs the encoding in a lazy way. It
/// should compose well with other streaming encoders (e.g. these offered by
/// the [`flate2`] crate).
///
/// [`flate2`]: https://crates.io/crates/flate2
///
/// # Examples
///
/// ```no_run
/// use std::fs::File;
///
/// let data = [b"foo", b"bar", b"baz"];
///
/// let mut stream = rrg::chunked::encode(data.iter().map(|blob| &blob[..]));
/// let mut file = File::create("output.chunked").unwrap();
/// std::io::copy(&mut stream, &mut file).unwrap();
/// ```
pub fn encode<I, M>(iter: I) -> Encode<I>
where
    I: Iterator<Item = M>,
    M: prost::Message,
{
    Encode {
        iter: iter,
        cur: Cursor::new(vec!()),
    }
}

/// Decodes a buffer in the chunked format into binary blobs.
///
/// This is a streaming decoder and performs the decoding in a lazy way. It
/// should compose well with other streaming decoders (e.g. these offered by the
/// [`flate2`] crate).
///
/// [`flate2`]: https://crates.io/crates/flate2
///
/// # Examples
///
/// ```no_run
/// use std::fs::File;
///
/// let file = File::open("input.chunked").unwrap();
/// for (idx, blob) in rrg::chunked::decode(file).enumerate() {
///     println!("blob #{}: {:?}", idx, blob.unwrap());
/// }
/// ```
pub fn decode<R, M>(buf: R) -> Decode<R, M>
where
    R: std::io::Read,
    M: prost::Message,
{
    Decode {
        reader: buf,
        buf: vec!(),
        marker: std::marker::PhantomData,
    }
}

/// Streaming encoder for the chunked format.
///
/// It implements the `Read` trait, lazily polling the underlying chunk iterator
/// as more bytes is needed.
///
/// Instances of this type can be constructed using the [`encode`] function.
///
/// [`encode`]: fn.encode.html
pub struct Encode<I> {
    iter: I,
    cur: Cursor<Vec<u8>>,
}

impl<I, M> Encode<I>
where
    I: Iterator<Item = M>,
    M: prost::Message,
{
    /// Checks whether all the data from the underlying cursor has been read.
    fn is_empty(&self) -> bool {
        self.cur.position() == self.cur.get_ref().len() as u64
    }

    /// Pulls another blob of data from the underlying iterator.
    fn pull(&mut self) -> std::io::Result<()> {
        use byteorder::WriteBytesExt as _;

        let msg = match self.iter.next() {
            Some(msg) => msg,
            None => return Ok(()),
        };

        self.cur.get_mut().clear();
        self.cur.set_position(0);

        self.cur.write_u64::<BigEndian>(msg.encoded_len() as u64)?;
        msg.encode(&mut self.cur.get_mut())?;
        self.cur.set_position(0);

        Ok(())
    }
}

impl<I, M> std::io::Read for Encode<I>
where
    I: Iterator<Item = M>,
    M: prost::Message,
{
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        if self.is_empty() {
            self.pull()?;
        }

        self.cur.read(buf)
    }
}

/// Streaming decoder for the chunked format.
///
/// It implements the `Iterator` trait yielding chunks of decoded blobs, lazily
/// decoding data from the underlying buffer.
///
/// Instances of this type can be constructed using the [`decode`] function.
///
/// [`decode`]: fn.decode.html
pub struct Decode<R, M> {
    reader: R,
    buf: Vec<u8>,
    marker: std::marker::PhantomData<M>,
}

impl<R: std::io::Read, M> Decode<R, M> {

    /// Reads a size tag from the underlying buffer.
    ///
    /// It will return `None` if the is no more data in the buffer.
    fn read_len(&mut self) -> std::io::Result<Option<usize>> {
        use byteorder::ReadBytesExt as _;

        // `read` might not always read all 8 bytes. On the other hand, we also
        // cannot use just `read_exact` because the stream might have ended
        // already. Hence, we combine the two. First we attempt to read some
        // bytes with `read`: it should either return 0 (indicating end of the
        // stream), 8 (indicating that we have filled the whole buffer fully)
        // or something in between. In the last case, we use `read_exact to get
        // the remaining bytes (which should be non-zero now).
        let mut buf = [0; 8];
        match self.reader.read(&mut buf[..])? {
            8 => (),
            0 => return Ok(None),
            len => self.reader.read_exact(&mut buf[len..])?,
        }

        let len = (&buf[..]).read_u64::<BigEndian>()? as usize;
        Ok(Some(len))
    }
}

impl<R, M> Iterator for Decode<R, M>
where
    R: std::io::Read,
    M: prost::Message + Default,
{
    type Item = std::io::Result<M>;

    fn next(&mut self) -> Option<std::io::Result<M>> {
        let len = match self.read_len() {
            Ok(Some(len)) => len,
            Ok(None) => return None,
            Err(error) => return Some(Err(error)),
        };

        self.buf.resize(len, u8::default());
        match self.reader.read_exact(&mut self.buf[..]) {
            Ok(()) => (),
            Err(error) => return Some(Err(error)),
        }

        let msg = match M::decode(&self.buf[..]) {
            Ok(msg) => msg,
            Err(error) => return Some(Err(error.into())),
        };

        Some(Ok(msg))
    }
}

#[cfg(test)]
pub mod tests {

    use super::*;

    #[test]
    pub fn test_encode_empty_iter() {
        use std::io::Read as _;

        let mut stream = encode(std::iter::empty::<()>());

        let mut output = vec!();
        stream.read_to_end(&mut output).unwrap();

        assert!(output.is_empty());
    }

    #[test]
    pub fn test_decode_empty_buf() {
        let buf: &[u8] = b"";
        let mut iter = decode::<_, ()>(buf);

        assert!(iter.next().is_none());
    }

    #[test]
    pub fn test_decode_incorrect_size_tag() {
        let buf: &[u8] = b"\x12\x34\x56";
        let mut iter = decode::<_, ()>(buf);

        let error = iter.next().unwrap().unwrap_err();
        assert_eq!(error.kind(), std::io::ErrorKind::UnexpectedEof);
    }

    #[test]
    pub fn test_decode_zero_size_tag() {
        let buf: &[u8] = b"\x00\x00\x00\x00\x00\x00\x00\x00";
        let mut iter = decode::<_, ()>(buf);

        assert!(iter.next().is_none());
    }

    #[test]
    pub fn test_decode_partial_size_tag() {

        // A simple reader that yields a 0-valued size tag byte by byte.
        struct Reader(u8);

        impl std::io::Read for Reader {

            fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
                if self.0 > 8 {
                    Ok(0)
                } else {
                    buf[0] = 0;
                    self.0 += 1;
                    Ok(1)
                }
            }
        }

        let mut iter = decode::<_, ()>(Reader(0));
        assert!(iter.next().is_none());
    }

    #[test]
    pub fn test_decode_short_input() {
        let buf: &[u8] = b"\x00\x00\x00\x00\x00\x00\x00\x42foo";
        let mut iter = decode::<_, ()>(buf);

        let error = iter.next().unwrap().unwrap_err();
        assert_eq!(error.kind(), std::io::ErrorKind::UnexpectedEof);
    }

    #[test]
    pub fn test_encode_and_decode_single_message() {
        let mut iter = decode(encode(vec!(String::from("foo")).into_iter()))
            .map(Result::unwrap);

        assert_eq!(iter.next(), Some(String::from("foo")));
        assert_eq!(iter.next(), None);
    }

    #[test]
    pub fn test_encode_and_decode_single_unit_message() {
        let mut iter = decode(encode(vec!(()).into_iter()))
            .map(Result::unwrap);

        assert_eq!(iter.next(), Some(()));
        assert_eq!(iter.next(), None);
    }

    #[test]
    pub fn test_encode_and_decode_multiple_messages() {
        let msgs = vec! {
            b"foo".to_vec(),
            b"bar".to_vec(),
            b"baz".to_vec(),
        };

        let mut iter = decode(encode(msgs.into_iter()))
            .map(Result::unwrap);

        assert_eq!(iter.next(), Some(b"foo".to_vec()));
        assert_eq!(iter.next(), Some(b"bar".to_vec()));
        assert_eq!(iter.next(), Some(b"baz".to_vec()));
        assert_eq!(iter.next(), None);
    }

    #[test]
    pub fn test_encode_and_decode_multiple_unit_messages() {
        let msgs = vec!((), (), ());

        let mut iter = decode(encode(msgs.into_iter()))
            .map(Result::unwrap);

        assert_eq!(iter.next(), Some(()));
        assert_eq!(iter.next(), Some(()));
        assert_eq!(iter.next(), Some(()));
        assert_eq!(iter.next(), None);
    }
}
<|endoftext|>


<commit_before>/* Copyright 2016 Mark Haines
 *
 * 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,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
use readhex::*;

const HEX : [u8 ; 16] = [
    b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9',
    b'A', b'B', b'C', b'D', b'E', b'F'
];

pub fn compact_vector(input: &[u8], output: &mut Vec<u8>) -> bool {
    let mut iter = input.iter();

    loop {
        let input_char = match iter.next() {
            None => return true,
            Some(value) => *value,
        };
        if input_char <= b' ' { // Whitespace '\n', '\r', '\t', ' '
            continue;
        }
        output.push(input_char);
        if input_char == b'\"' { // Double Quote '\"'
            loop {
                let input_char = match iter.next() {
                    None => return false,
                    Some(value) => *value,
                };
                if input_char == b'\\' { // Back Slash '\\'
                    let input_char = match iter.next() {
                        None => return false,
                        Some(value) => *value,
                    };
                    if input_char == b'u' { // Unicode escape "u"
                        let h0 = match iter.next() {
                            None => return false,
                            Some(value) => *value
                        };
                        let h1 = match iter.next() {
                            None => return false,
                            Some(value) => *value
                        };
                        let h2 = match iter.next() {
                            None => return false,
                            Some(value) => *value
                        };
                        let h3 = match iter.next() {
                            None => return false,
                            Some(value) => *value
                        };
                        let escaped = read_hexdigits(h0, h1, h2, h3);
                        if escaped < 0x20 {
                            output.push(b'\\');
                            match escaped {
                                0x08 => output.push(b'b'),
                                0x09 => output.push(b't'),
                                0x0A => output.push(b'n'),
                                0x0C => output.push(b'f'),
                                0x0D => output.push(b'r'),
                                _ => {
                                    output.push(b'u');
                                    output.push(b'0');
                                    output.push(b'0');
                                    output.push(b'0' + (escaped as u8 >> 4));
                                    output.push(HEX[(escaped & 0xF) as usize]);
                                },
                            }
                        } else if escaped < 0x80 {
                            if escaped as u8 == b'\"' || escaped as u8 == b'\\' {
                                output.push(b'\\');
                            }
                            output.push(escaped as u8);
                        } else if escaped < 0x800 {
                            output.push((escaped >> 6) as u8 | 0xC0);
                            output.push((escaped as u8 & 0x3F) | 0x80);
                        } else {
                            if escaped < 0xD800 || escaped >= 0xE000 {
                                output.push((escaped >> 12) as u8 | 0xE0);
                                output.push(((escaped >> 6) & 0x3F) as u8 | 0x80);
                                output.push((escaped as u8 & 0x3F) | 0x80);
                            } else {
                                // surrogate pair
                                match iter.next() {
                                    None => return false,
                                    _ => {;}
                                }
                                match iter.next() {
                                    None => return false,
                                    _ => {;}
                                }
                                let h0 = match iter.next() {
                                    None => return false,
                                    Some(value) => *value
                                };
                                let h1 = match iter.next() {
                                    None => return false,
                                    Some(value) => *value
                                };
                                let h2 = match iter.next() {
                                    None => return false,
                                    Some(value) => *value
                                };
                                let h3 = match iter.next() {
                                    None => return false,
                                    Some(value) => *value
                                };
                                let surrogate = read_hexdigits(h0, h1, h2, h3);
                                let codepoint = 0x10000 + (
                                    ((escaped & 0x3FF) << 10) | (surrogate & 0x3FF)
                                );
                                output.push((codepoint >> 18) as u8 | 0xF0);
                                output.push(((codepoint >> 12) & 0x3F) as u8 | 0x80);
                                output.push(((codepoint >> 6) & 0x3F) as u8 | 0x80);
                                output.push((codepoint as u8 & 0x3F) | 0x80);
                            }
                        }

                    } else if input_char == b'/' { // Forward Slash '/'
                        output.push(input_char);
                    } else {
                        output.push(b'\\');
                        output.push(input_char);
                    }
                } else {
                    output.push(input_char);
                }
                if input_char == b'\"' {
                    break;
                }
            }
        }
    }
}

pub fn compact(input_json: &str) -> String {
    let mut output : Vec<u8> = Vec::with_capacity(input_json.as_bytes().len());
    if compact_vector(input_json.as_bytes(), &mut output) {
        return String::from_utf8(output).unwrap();
    } else {
        return "FAIL".to_string();
    }
}

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

    #[test]
    fn compact_json_object() {
        assert_eq!("{}", compact("{ }"));
    }

    #[test]
    fn compact_json_string() {
        assert_eq!(
            r#"{"key":"\"hello / world\""}"#,
            compact(r#" { "key" : "\"hello \/ world\"" } "#)
        );
    }

    #[test]
    fn compact_unicode_escapes() {
        assert_eq!(
            r#"["\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007"]"#,
            compact(r#"["\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007"]"#)
        );
        assert_eq!(
            r#"["\b\t\n\u000B\f\r\u000E\u000F"]"#,
            compact(r#"["\u0008\u0009\u000A\u000B\u000C\u000D\u000E\u000F"]"#)
        );
        assert_eq!(
            r#"["\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017"]"#,
            compact(r#"["\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017"]"#)
        );
        assert_eq!(
            r#"["\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F"]"#,
            compact(r#"["\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F"]"#)
        );
    }

    #[test]
    fn compact_unicode_utf8() {
        assert_eq!(
            "[\"a\\\\B\\\"\"]",
            compact("[\"\\u0061\\u005C\\u0042\\u0022\"]")
        );
        assert_eq!("[\"\u{120}\"]", compact("[\"\\u0120\"]"));
        assert_eq!("[\"\u{FFF}\"]", compact("[\"\\u0FFF\"]"));
        assert_eq!("[\"\u{1820}\"]", compact("[\"\\u1820\"]"));
        assert_eq!("[\"\u{FFFF}\"]", compact("[\"\\uFFFF\"]"));
        assert_eq!("[\"\u{20820}\"]", compact("[\"\\uD842\\uDC20\"]"));
        assert_eq!("[\"\u{10FFFF}\"]", compact("[\"\\uDBFF\\uDFFF\"]"));
    }
}
<commit_msg>Use a byte string literal rather than an array<commit_after>/* Copyright 2016 Mark Haines
 *
 * 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,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
use readhex::*;

const HEX : [u8 ; 16] = *b"0123456789ABCDEF";

pub fn compact_vector(input: &[u8], output: &mut Vec<u8>) -> bool {
    let mut iter = input.iter();

    loop {
        let input_char = match iter.next() {
            None => return true,
            Some(value) => *value,
        };
        if input_char <= b' ' { // Whitespace '\n', '\r', '\t', ' '
            continue;
        }
        output.push(input_char);
        if input_char == b'\"' { // Double Quote '\"'
            loop {
                let input_char = match iter.next() {
                    None => return false,
                    Some(value) => *value,
                };
                if input_char == b'\\' { // Back Slash '\\'
                    let input_char = match iter.next() {
                        None => return false,
                        Some(value) => *value,
                    };
                    if input_char == b'u' { // Unicode escape "u"
                        let h0 = match iter.next() {
                            None => return false,
                            Some(value) => *value
                        };
                        let h1 = match iter.next() {
                            None => return false,
                            Some(value) => *value
                        };
                        let h2 = match iter.next() {
                            None => return false,
                            Some(value) => *value
                        };
                        let h3 = match iter.next() {
                            None => return false,
                            Some(value) => *value
                        };
                        let escaped = read_hexdigits(h0, h1, h2, h3);
                        if escaped < 0x20 {
                            output.push(b'\\');
                            match escaped {
                                0x08 => output.push(b'b'),
                                0x09 => output.push(b't'),
                                0x0A => output.push(b'n'),
                                0x0C => output.push(b'f'),
                                0x0D => output.push(b'r'),
                                _ => {
                                    output.push(b'u');
                                    output.push(b'0');
                                    output.push(b'0');
                                    output.push(b'0' + (escaped as u8 >> 4));
                                    output.push(HEX[(escaped & 0xF) as usize]);
                                },
                            }
                        } else if escaped < 0x80 {
                            if escaped as u8 == b'\"' || escaped as u8 == b'\\' {
                                output.push(b'\\');
                            }
                            output.push(escaped as u8);
                        } else if escaped < 0x800 {
                            output.push((escaped >> 6) as u8 | 0xC0);
                            output.push((escaped as u8 & 0x3F) | 0x80);
                        } else {
                            if escaped < 0xD800 || escaped >= 0xE000 {
                                output.push((escaped >> 12) as u8 | 0xE0);
                                output.push(((escaped >> 6) & 0x3F) as u8 | 0x80);
                                output.push((escaped as u8 & 0x3F) | 0x80);
                            } else {
                                // surrogate pair
                                match iter.next() {
                                    None => return false,
                                    _ => {;}
                                }
                                match iter.next() {
                                    None => return false,
                                    _ => {;}
                                }
                                let h0 = match iter.next() {
                                    None => return false,
                                    Some(value) => *value
                                };
                                let h1 = match iter.next() {
                                    None => return false,
                                    Some(value) => *value
                                };
                                let h2 = match iter.next() {
                                    None => return false,
                                    Some(value) => *value
                                };
                                let h3 = match iter.next() {
                                    None => return false,
                                    Some(value) => *value
                                };
                                let surrogate = read_hexdigits(h0, h1, h2, h3);
                                let codepoint = 0x10000 + (
                                    ((escaped & 0x3FF) << 10) | (surrogate & 0x3FF)
                                );
                                output.push((codepoint >> 18) as u8 | 0xF0);
                                output.push(((codepoint >> 12) & 0x3F) as u8 | 0x80);
                                output.push(((codepoint >> 6) & 0x3F) as u8 | 0x80);
                                output.push((codepoint as u8 & 0x3F) | 0x80);
                            }
                        }

                    } else if input_char == b'/' { // Forward Slash '/'
                        output.push(input_char);
                    } else {
                        output.push(b'\\');
                        output.push(input_char);
                    }
                } else {
                    output.push(input_char);
                }
                if input_char == b'\"' {
                    break;
                }
            }
        }
    }
}

pub fn compact(input_json: &str) -> String {
    let mut output : Vec<u8> = Vec::with_capacity(input_json.as_bytes().len());
    if compact_vector(input_json.as_bytes(), &mut output) {
        return String::from_utf8(output).unwrap();
    } else {
        return "FAIL".to_string();
    }
}

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

    #[test]
    fn compact_json_object() {
        assert_eq!("{}", compact("{ }"));
    }

    #[test]
    fn compact_json_string() {
        assert_eq!(
            r#"{"key":"\"hello / world\""}"#,
            compact(r#" { "key" : "\"hello \/ world\"" } "#)
        );
    }

    #[test]
    fn compact_unicode_escapes() {
        assert_eq!(
            r#"["\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007"]"#,
            compact(r#"["\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007"]"#)
        );
        assert_eq!(
            r#"["\b\t\n\u000B\f\r\u000E\u000F"]"#,
            compact(r#"["\u0008\u0009\u000A\u000B\u000C\u000D\u000E\u000F"]"#)
        );
        assert_eq!(
            r#"["\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017"]"#,
            compact(r#"["\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017"]"#)
        );
        assert_eq!(
            r#"["\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F"]"#,
            compact(r#"["\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F"]"#)
        );
    }

    #[test]
    fn compact_unicode_utf8() {
        assert_eq!(
            "[\"a\\\\B\\\"\"]",
            compact("[\"\\u0061\\u005C\\u0042\\u0022\"]")
        );
        assert_eq!("[\"\u{120}\"]", compact("[\"\\u0120\"]"));
        assert_eq!("[\"\u{FFF}\"]", compact("[\"\\u0FFF\"]"));
        assert_eq!("[\"\u{1820}\"]", compact("[\"\\u1820\"]"));
        assert_eq!("[\"\u{FFFF}\"]", compact("[\"\\uFFFF\"]"));
        assert_eq!("[\"\u{20820}\"]", compact("[\"\\uD842\\uDC20\"]"));
        assert_eq!("[\"\u{10FFFF}\"]", compact("[\"\\uDBFF\\uDFFF\"]"));
    }
}
<|endoftext|>


<commit_before>use image::{self, GenericImage};
use sdl2::pixels;
use sdl2::surface;
use sdl2::{self, Sdl};

use std::fmt;
use std::io::Read;

use GameError;
use GameResult;
use audio;
use conf;
use event;
use filesystem::Filesystem;
use graphics;
use input;
use mouse;
use timer;

/// A `Context` is an object that holds on to global resources.
/// It basically tracks hardware state such as the screen, audio
/// system, timers, and so on.  Generally this type is **not** thread-
/// safe and only one `Context` can exist at a time.  Trying to create
/// another one will fail.
///
/// Most functions that interact with the hardware, for instance
/// drawing things, playing sounds, or loading resources (which then
/// need to be transformed into a format the hardware likes) will need
/// to access the `Context`.
pub struct Context {
    /// The Conf object the Context was created with
    pub conf: conf::Conf,
    /// SDL context
    pub sdl_context: Sdl,
    /// Filesystem state
    pub filesystem: Filesystem,
    /// Graphics state
    pub(crate) gfx_context: graphics::GraphicsContext,
    /// Event context
    pub event_context: sdl2::EventSubsystem,
    /// Timer state
    pub timer_context: timer::TimeContext,
    /// Audio context
    pub audio_context: audio::AudioContext,
    /// Gamepad context
    pub gamepad_context: input::GamepadContext,
    /// Mouse context
    pub mouse_context: mouse::MouseContext,
    /// Default font
    pub default_font: graphics::Font,

    /// Context-specific unique ID.
    /// Compiles to nothing in release mode, and so
    /// vanishes; meanwhile we get dead-code warnings.
    #[allow(dead_code)]
    debug_id: DebugId,
}

impl fmt::Debug for Context {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<Context: {:p}>", self)
    }
}

/// Sets the window icon from the Conf `window_icon` field.
/// An empty string in the conf's `window_icon`
/// means to do nothing.
fn set_window_icon(context: &mut Context) -> GameResult<()> {
    // This clone is a little annoying, but, borrowing is inconvenient.
    let icon = &context.conf.window_setup.icon.clone();
    if !icon.is_empty() {
        let mut f = context.filesystem.open(icon)?;
        let mut buf = Vec::new();
        f.read_to_end(&mut buf)?;
        let image = image::load_from_memory(&buf)?;
        let image_data = &mut image.to_rgba();
        // The "pitch" parameter here is not the count
        // between pixels, but the count between rows.
        // For some retarded reason.
        // Also SDL seems to have strange ideas of what
        // "RGBA" means.
        let surface = surface::Surface::from_data(
            image_data,
            image.width(),
            image.height(),
            image.width() * 4,
            pixels::PixelFormatEnum::ABGR8888,
        )?;
        let window = graphics::get_window_mut(context);
        window.set_icon(surface);
    };
    Ok(())
}

impl Context {
    /// Tries to create a new Context using settings from the given config file.
    /// Usually called by `Context::load_from_conf()`.
    fn from_conf(conf: conf::Conf, fs: Filesystem, sdl_context: Sdl) -> GameResult<Context> {
        let debug_id = DebugId::new();
        let video = sdl_context.video()?;
        let audio_context = audio::AudioContext::new()?;
        let event_context = sdl_context.event()?;
        let timer_context = timer::TimeContext::new();
        let font = graphics::Font::default_font()?;
        let backend_spec = graphics::GlBackendSpec::from(conf.backend);
        let graphics_context = graphics::GraphicsContext::new(
            &video,
            &conf.window_setup,
            conf.window_mode,
            backend_spec,
            debug_id,
        )?;
        let gamepad_context = input::GamepadContext::new(&sdl_context)?;
        let mouse_context = mouse::MouseContext::new();

        let mut ctx = Context {
            conf,
            sdl_context,
            filesystem: fs,
            gfx_context: graphics_context,
            event_context,
            timer_context,
            audio_context,
            gamepad_context,
            mouse_context,

            default_font: font,
            debug_id,
        };

        set_window_icon(&mut ctx)?;

        Ok(ctx)
    }

    /// Tries to create a new Context by loading a config
    /// file from its default path, using the given `Conf`
    /// object as a default if none is found.
    ///
    /// The `game_id` and `author` are game-specific strings that
    /// are used to locate the default storage locations for the
    /// platform it looks in, as documented in the `filesystem`
    /// module.  You can also always debug-print the
    /// `Context::filesystem` field to see what paths it is
    /// searching.
    pub fn load_from_conf(
        game_id: &'static str,
        author: &'static str,
        default_config: conf::Conf,
    ) -> GameResult<Context> {
        let sdl_context = sdl2::init()?;
        let mut fs = Filesystem::new(game_id, author)?;

        let config = match fs.read_config() {
            Ok(config) => {
                info!("Loading conf.toml");
                config
            }
            Err(e) => {
                info!("Could not load conf.toml, using default: {:?}", e);
                default_config
            }
        };

        Context::from_conf(config, fs, sdl_context)
    }

    /// Prints out information on the resources subsystem.
    pub fn print_resource_stats(&mut self) {
        // TODO: consider using logging macros.
        // ACTUALLY TODO: Deprecate this in favor of filesystem.log_all()
        self.filesystem.print_all();
    }

    /// Triggers a Quit event.
    pub fn quit(&mut self) -> GameResult<()> {
        let now_dur = timer::get_time_since_start(self);
        let now = timer::duration_to_f64(now_dur);
        let e = sdl2::event::Event::Quit {
            timestamp: now as u32,
        };
        // println!("Pushing event {:?}", e);
        self.event_context.push_event(e).map_err(GameError::from)
    }

    /// Feeds an `Event` into the `Context` so it can update any internal
    /// state it needs to, such as detecting window resizes.  If you are
    /// rolling your own event loop, you should call this on the events
    /// you receive before processing them yourself.
    pub fn process_event(&mut self, event: &event::Event) {
        match *event {
            event::Event::MouseMotion { x, y, .. } => {
                // Keeping the mouse state info in the Context is a bit of a hack, see issue #283.
                // Seems the best workaround though.
                use graphics::Point2;
                self.mouse_context
                    .set_last_position(Point2::new(x as f32, y as f32));
            }
            event::Event::Window {
                win_event: sdl2::event::WindowEvent::Resized(_, _),
                ..
            } => {
                self.gfx_context.resize_viewport();
            }
            _ => {}
        }
    }
}

use std::path;

/// A builder object for creating a context.
///
/// Can do everything the `Context::load_from_conf()` method does, plus you can
/// also specify new paths to add to the resource path list at build time instead
/// of using `filesystem::mount()`.
#[derive(Debug)]
pub struct ContextBuilder {
    game_id: &'static str,
    author: &'static str,
    conf: conf::Conf,
    paths: Vec<path::PathBuf>,
    load_conf_file: bool,
}

impl ContextBuilder {
    /// Create a new ContextBuilder
    pub fn new(game_id: &'static str, author: &'static str) -> Self {
        Self {
            game_id,
            author,
            conf: conf::Conf::default(),
            paths: vec![],
            load_conf_file: true,
        }
    }

    /// Sets the window setup settings
    pub fn window_setup(mut self, setup: conf::WindowSetup) -> Self {
        self.conf.window_setup = setup;
        self
    }

    /// Sets the window mode settings
    pub fn window_mode(mut self, mode: conf::WindowMode) -> Self {
        self.conf.window_mode = mode;
        self
    }

    /// Sets the graphics backend
    pub fn backend(mut self, backend: conf::Backend) -> Self {
        self.conf.backend = backend;
        self
    }

    /// Add a new read-only filesystem path to the places to search
    /// for resources.
    pub fn add_resource_path<T>(mut self, path: T) -> Self
    where
        T: Into<path::PathBuf>,
    {
        self.paths.push(path.into());
        self
    }

    /// Specifies whether or not to load the `conf.toml` file if it
    /// exists and use its settings to override the provided values.
    /// Defaults to `true` which is usually what you want, but being
    /// able to fiddle with it is sometimes useful for debugging.
    pub fn with_conf_file(mut self, load_conf_file: bool) -> Self {
        self.load_conf_file = load_conf_file;
        self
    }

    /// Build the Context.
    pub fn build(self) -> GameResult<Context> {
        let sdl_context = sdl2::init()?;
        let mut fs = Filesystem::new(self.game_id, self.author)?;

        let config = if self.load_conf_file {
            fs.read_config().unwrap_or(self.conf)
        } else {
            self.conf
        };

        for path in &self.paths {
            fs.mount(path, true);
        }

        Context::from_conf(config, fs, sdl_context)
    }
}

#[cfg(debug_assertions)]
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
#[cfg(debug_assertions)]
static DEBUG_ID_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT;

/// This is a type that contains a unique ID for each Context and
/// is contained in each thing created from the Context which contains
/// data that becomes invalid when the Context goes away (ie, texture
/// handles).  When compiling without assertions (ie in release mode) it
/// is replaced with a zero-size type, compiles down to nothing,
/// and should disappear entirely with a puff of optimization logic.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg(debug_assertions)]
pub(crate) struct DebugId(u32);
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg(not(debug_assertions))]
pub(crate) struct DebugId;

#[cfg(debug_assertions)]
impl DebugId {
    pub fn new() -> Self {
        let id = DEBUG_ID_COUNTER.fetch_add(1, Ordering::SeqCst) as u32;
        // fetch_add() wraps on overflow so we check for overflow explicitly.
        // JUST IN CASE YOU TRY TO CREATE 2^32 CONTEXTS IN ONE PROGRAM!
        assert!(DEBUG_ID_COUNTER.load(Ordering::SeqCst) as u32 > id);
        DebugId(id)
    }

    pub fn get(ctx: &Context) -> Self {
        DebugId(ctx.debug_id.0)
    }

    pub fn assert(&self, ctx: &Context) {
        if *self != ctx.debug_id {
            panic!("Tried to use a resource with a Context that did not create it; this should never happen!");
        }
    }
}

#[cfg(not(debug_assertions))]
impl DebugId {
    pub fn new() -> Self {
        DebugId
    }

    pub fn get(_ctx: &Context) -> Self {
        DebugId
    }

    pub fn assert(&self, _ctx: &Context) {
        // Do nothing.
    }
}
<commit_msg>Rewrote `set_window_icon()`; removed `sdl2` imports from `context.rs`.<commit_after>use image::{self, GenericImage};
use winit;

use std::fmt;
use std::io::Read;

use GameError;
use GameResult;
use audio;
use conf;
use event;
use filesystem::Filesystem;
use graphics;
use input;
use mouse;
use timer;

/// A `Context` is an object that holds on to global resources.
/// It basically tracks hardware state such as the screen, audio
/// system, timers, and so on.  Generally this type is **not** thread-
/// safe and only one `Context` can exist at a time.  Trying to create
/// another one will fail.
///
/// Most functions that interact with the hardware, for instance
/// drawing things, playing sounds, or loading resources (which then
/// need to be transformed into a format the hardware likes) will need
/// to access the `Context`.
pub struct Context {
    /// The Conf object the Context was created with
    pub conf: conf::Conf,
    /// SDL context
    pub sdl_context: Sdl,
    /// Filesystem state
    pub filesystem: Filesystem,
    /// Graphics state
    pub(crate) gfx_context: graphics::GraphicsContext,
    /// Event context
    pub event_context: sdl2::EventSubsystem,
    /// Timer state
    pub timer_context: timer::TimeContext,
    /// Audio context
    pub audio_context: audio::AudioContext,
    /// Gamepad context
    pub gamepad_context: input::GamepadContext,
    /// Mouse context
    pub mouse_context: mouse::MouseContext,
    /// Default font
    pub default_font: graphics::Font,

    /// Context-specific unique ID.
    /// Compiles to nothing in release mode, and so
    /// vanishes; meanwhile we get dead-code warnings.
    #[allow(dead_code)]
    debug_id: DebugId,
}

impl fmt::Debug for Context {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<Context: {:p}>", self)
    }
}

/// Sets the window icon from the Conf `window_icon` field.
/// An empty string in the conf's `window_icon`
/// means to do nothing.
fn set_window_icon(context: &mut Context) -> GameResult<()> {
    // This clone is a little annoying, but, borrowing is inconvenient.
    let icon = &context.conf.window_setup.icon.clone();
    if !icon.is_empty() {
        let icon = winit::Icon::from_path(icon)?;
        let window = graphics::get_window(context);
        window.set_window_icon(Some(icon));
    };
    Ok(())
}

impl Context {
    /// Tries to create a new Context using settings from the given config file.
    /// Usually called by `Context::load_from_conf()`.
    fn from_conf(conf: conf::Conf, fs: Filesystem, sdl_context: Sdl) -> GameResult<Context> {
        let debug_id = DebugId::new();
        let video = sdl_context.video()?;
        let audio_context = audio::AudioContext::new()?;
        let event_context = sdl_context.event()?;
        let timer_context = timer::TimeContext::new();
        let font = graphics::Font::default_font()?;
        let backend_spec = graphics::GlBackendSpec::from(conf.backend);
        let graphics_context = graphics::GraphicsContext::new(
            &video,
            &conf.window_setup,
            conf.window_mode,
            backend_spec,
            debug_id,
        )?;
        let gamepad_context = input::GamepadContext::new(&sdl_context)?;
        let mouse_context = mouse::MouseContext::new();

        let mut ctx = Context {
            conf,
            sdl_context,
            filesystem: fs,
            gfx_context: graphics_context,
            event_context,
            timer_context,
            audio_context,
            gamepad_context,
            mouse_context,

            default_font: font,
            debug_id,
        };

        set_window_icon(&mut ctx)?;

        Ok(ctx)
    }

    /// Tries to create a new Context by loading a config
    /// file from its default path, using the given `Conf`
    /// object as a default if none is found.
    ///
    /// The `game_id` and `author` are game-specific strings that
    /// are used to locate the default storage locations for the
    /// platform it looks in, as documented in the `filesystem`
    /// module.  You can also always debug-print the
    /// `Context::filesystem` field to see what paths it is
    /// searching.
    pub fn load_from_conf(
        game_id: &'static str,
        author: &'static str,
        default_config: conf::Conf,
    ) -> GameResult<Context> {
        let sdl_context = sdl2::init()?;
        let mut fs = Filesystem::new(game_id, author)?;

        let config = match fs.read_config() {
            Ok(config) => {
                info!("Loading conf.toml");
                config
            }
            Err(e) => {
                info!("Could not load conf.toml, using default: {:?}", e);
                default_config
            }
        };

        Context::from_conf(config, fs, sdl_context)
    }

    /// Prints out information on the resources subsystem.
    pub fn print_resource_stats(&mut self) {
        // TODO: consider using logging macros.
        // ACTUALLY TODO: Deprecate this in favor of filesystem.log_all()
        self.filesystem.print_all();
    }

    /// Triggers a Quit event.
    pub fn quit(&mut self) -> GameResult<()> {
        let now_dur = timer::get_time_since_start(self);
        let now = timer::duration_to_f64(now_dur);
        let e = sdl2::event::Event::Quit {
            timestamp: now as u32,
        };
        // println!("Pushing event {:?}", e);
        self.event_context.push_event(e).map_err(GameError::from)
    }

    /// Feeds an `Event` into the `Context` so it can update any internal
    /// state it needs to, such as detecting window resizes.  If you are
    /// rolling your own event loop, you should call this on the events
    /// you receive before processing them yourself.
    pub fn process_event(&mut self, event: &event::Event) {
        match *event {
            event::Event::MouseMotion { x, y, .. } => {
                // Keeping the mouse state info in the Context is a bit of a hack, see issue #283.
                // Seems the best workaround though.
                use graphics::Point2;
                self.mouse_context
                    .set_last_position(Point2::new(x as f32, y as f32));
            }
            event::Event::Window {
                win_event: sdl2::event::WindowEvent::Resized(_, _),
                ..
            } => {
                self.gfx_context.resize_viewport();
            }
            _ => {}
        }
    }
}

use std::path;

/// A builder object for creating a context.
///
/// Can do everything the `Context::load_from_conf()` method does, plus you can
/// also specify new paths to add to the resource path list at build time instead
/// of using `filesystem::mount()`.
#[derive(Debug)]
pub struct ContextBuilder {
    game_id: &'static str,
    author: &'static str,
    conf: conf::Conf,
    paths: Vec<path::PathBuf>,
    load_conf_file: bool,
}

impl ContextBuilder {
    /// Create a new ContextBuilder
    pub fn new(game_id: &'static str, author: &'static str) -> Self {
        Self {
            game_id,
            author,
            conf: conf::Conf::default(),
            paths: vec![],
            load_conf_file: true,
        }
    }

    /// Sets the window setup settings
    pub fn window_setup(mut self, setup: conf::WindowSetup) -> Self {
        self.conf.window_setup = setup;
        self
    }

    /// Sets the window mode settings
    pub fn window_mode(mut self, mode: conf::WindowMode) -> Self {
        self.conf.window_mode = mode;
        self
    }

    /// Sets the graphics backend
    pub fn backend(mut self, backend: conf::Backend) -> Self {
        self.conf.backend = backend;
        self
    }

    /// Add a new read-only filesystem path to the places to search
    /// for resources.
    pub fn add_resource_path<T>(mut self, path: T) -> Self
    where
        T: Into<path::PathBuf>,
    {
        self.paths.push(path.into());
        self
    }

    /// Specifies whether or not to load the `conf.toml` file if it
    /// exists and use its settings to override the provided values.
    /// Defaults to `true` which is usually what you want, but being
    /// able to fiddle with it is sometimes useful for debugging.
    pub fn with_conf_file(mut self, load_conf_file: bool) -> Self {
        self.load_conf_file = load_conf_file;
        self
    }

    /// Build the Context.
    pub fn build(self) -> GameResult<Context> {
        let sdl_context = sdl2::init()?;
        let mut fs = Filesystem::new(self.game_id, self.author)?;

        let config = if self.load_conf_file {
            fs.read_config().unwrap_or(self.conf)
        } else {
            self.conf
        };

        for path in &self.paths {
            fs.mount(path, true);
        }

        Context::from_conf(config, fs, sdl_context)
    }
}

#[cfg(debug_assertions)]
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
#[cfg(debug_assertions)]
static DEBUG_ID_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT;

/// This is a type that contains a unique ID for each Context and
/// is contained in each thing created from the Context which contains
/// data that becomes invalid when the Context goes away (ie, texture
/// handles).  When compiling without assertions (ie in release mode) it
/// is replaced with a zero-size type, compiles down to nothing,
/// and should disappear entirely with a puff of optimization logic.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg(debug_assertions)]
pub(crate) struct DebugId(u32);
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg(not(debug_assertions))]
pub(crate) struct DebugId;

#[cfg(debug_assertions)]
impl DebugId {
    pub fn new() -> Self {
        let id = DEBUG_ID_COUNTER.fetch_add(1, Ordering::SeqCst) as u32;
        // fetch_add() wraps on overflow so we check for overflow explicitly.
        // JUST IN CASE YOU TRY TO CREATE 2^32 CONTEXTS IN ONE PROGRAM!
        assert!(DEBUG_ID_COUNTER.load(Ordering::SeqCst) as u32 > id);
        DebugId(id)
    }

    pub fn get(ctx: &Context) -> Self {
        DebugId(ctx.debug_id.0)
    }

    pub fn assert(&self, ctx: &Context) {
        if *self != ctx.debug_id {
            panic!("Tried to use a resource with a Context that did not create it; this should never happen!");
        }
    }
}

#[cfg(not(debug_assertions))]
impl DebugId {
    pub fn new() -> Self {
        DebugId
    }

    pub fn get(_ctx: &Context) -> Self {
        DebugId
    }

    pub fn assert(&self, _ctx: &Context) {
        // Do nothing.
    }
}
<|endoftext|>


<commit_before>use crate::errors::OscError;
use crate::types::{OscArray, OscBundle, OscColor, OscMessage, OscMidiMessage, OscPacket, OscTime, OscType};
use crate::alloc::{string::{String, ToString}, vec::Vec};

use nom::Offset;
use nom::sequence::terminated;
use nom::bytes::complete::{take, take_till};
use nom::combinator::{map, map_parser};
use nom::multi::many0;
use nom::number::complete::{be_f32, be_f64, be_i32, be_i64, be_u32};
use nom::{IResult,combinator::map_res,sequence::tuple,Err};

/// Common MTU size for ethernet
pub const MTU: usize = 1536;

/// Takes a bytes slice representing a UDP packet and returns the OSC packet as well as a slice of
/// any bytes remaining after the OSC packet.
pub fn decode_udp<'a>(msg: &'a [u8]) -> Result<(&'a [u8], OscPacket), OscError> {
    match decode_packet(msg, msg) {
        Ok((remainder, osc_packet)) => Ok((remainder, osc_packet)),
        Err(e) => match e {
            Err::Incomplete(_) => Err(OscError::BadPacket("Incomplete data")),
            Err::Error(e) => Err(e),
            Err::Failure(e) => Err(e)
        }
    }
}

/// Takes a bytes slice from a TCP stream (or any stream-based protocol) and returns the first OSC
/// packet as well as a slice of the bytes remaining after the packet.
pub fn decode_tcp<'a>(msg: &'a [u8]) -> Result<(&'a [u8], Option<OscPacket>), OscError> {
    let (input, osc_packet_length) = match be_u32(msg) {
        Ok((i, o)) => (i, o),
        Err(e) => match e {
            Err::Incomplete(_) => return Err(OscError::BadPacket("Incomplete data")),
            Err::Error(e) => return Err(e),
            Err::Failure(e) => return Err(e)
        }
    };

    if osc_packet_length as usize > msg.len() {
        return Ok((msg, None));
    }

    match decode_packet(input, msg).map(|(remainder, osc_packet)| {
        (remainder, Some(osc_packet))
    }) {
        Ok((remainder, osc_packet)) => Ok((remainder, osc_packet)),
        Err(e) => match e {
            Err::Incomplete(_) => Err(OscError::BadPacket("Incomplete data")),
            Err::Error(e) => Err(e),
            Err::Failure(e) => Err(e)
        }
    }
}

/// Takes a bytes slice from a TCP stream (or any stream-based protocol) and returns a vec of all
/// OSC packets in the slice as well as a slice of the bytes remaining after the last packet.
pub fn decode_tcp_vec<'a>(msg: &'a [u8]) -> Result<(&'a [u8], Vec<OscPacket>), OscError> {
    let mut input = msg;
    let mut osc_packets = vec![];

    while let (remainder, Some(osc_packet)) = decode_tcp(input)? {
        input = remainder;
        osc_packets.push(osc_packet);

        if remainder.len() == 0 {
            break;
        }
    };

    Ok((input, osc_packets))
}

fn decode_packet<'a>(
    input: &'a [u8],
    original_input: &'a[u8],
) -> IResult<&'a [u8], OscPacket, OscError> {
    if input.is_empty() {
        return Err(nom::Err::Error(OscError::BadPacket("Empty packet.")));
    }

    let (input, addr) = read_osc_string(input, original_input)?;

    match addr.chars().next() {
        Some('/') => {
            decode_message(addr, input, original_input)
        }
        Some('#') if &addr == "#bundle" => {
            decode_bundle(input, original_input)
        }
        _ => Err(nom::Err::Error(OscError::BadPacket(
            "Invalid message address or bundle tag"
        ))),
    }
}

fn decode_message<'a>(
    addr: String,
    input: &'a [u8],
    original_input: &'a[u8],
) -> IResult<&'a [u8], OscPacket, OscError> {
    let (input, type_tags) = read_osc_string(input, original_input)?;

    if type_tags.len() > 1 {
        let (input, args) = read_osc_args(
            input,
            original_input,
            type_tags,
        )?;
        Ok((input, OscPacket::Message(OscMessage { addr, args })))
    } else {
        Ok((input, OscPacket::Message(OscMessage { addr, args: vec![] })))
    }
}

fn decode_bundle<'a>(
    input: &'a [u8],
    original_input: &'a[u8],
) -> IResult<&'a [u8], OscPacket, OscError> {
    let (input, (timetag, content)) = tuple((
        read_time_tag,
        many0(|input| read_bundle_element(input, original_input)),
    ))(input)?;

    Ok((
        input,
        OscPacket::Bundle(OscBundle {
            timetag,
            content,
        }),
    ))
}

fn read_bundle_element<'a>(input: &'a [u8], original_input: &'a[u8]) -> IResult<&'a [u8], OscPacket, OscError> {
    let (input, elem_size) = be_u32(input)?;

    let result = map_parser(
        |input| {
            take(elem_size)(input).map_err(|_: nom::Err<OscError>| {
                nom::Err::Error(OscError::BadBundle(
                    "Bundle shorter than expected!".to_string(),
                ))
            })
        },
        |input| decode_packet(input, original_input),
    )(input);
    drop(elem_size);
    result
}

fn read_osc_string<'a>(input: &'a [u8], original_input: &'a[u8]) -> IResult<&'a [u8], String, OscError> {
    map_res(
        terminated(
            take_till(|c| c == 0u8),
            pad_to_32_bit_boundary(original_input),
        ),
        |str_buf: &'a[u8]| {
            String::from_utf8(str_buf.into())
                .map_err(OscError::StringError)
                .map(|s| s.trim_matches(0u8 as char).to_string())
        },
    )(input)
}

fn read_osc_args<'a>(
    mut input: &'a[u8],
    original_input: &'a[u8],
    raw_type_tags: String,
) ->  IResult<&'a [u8], Vec<OscType>, OscError> {
    let type_tags: Vec<char> = raw_type_tags.chars().skip(1).collect();

    let mut args: Vec<OscType> = Vec::with_capacity(type_tags.len());
    let mut stack: Vec<Vec<OscType>> = Vec::new();
    for tag in type_tags {
        if tag == '[' {
            // array start: save current frame and start a new frame
            // for the array's content
            stack.push(args);
            args = Vec::new();
        } else if tag == ']' {
            // found the end of the current array:
            // create array object from current frame and step one level up
            let array = OscType::Array(OscArray { content: args });
            match stack.pop() {
                Some(stashed) => args = stashed,
                None => return Err(nom::Err::Error(OscError::BadMessage(
                    "Encountered ] outside array"
                ))),
            }
            args.push(array);
        } else {
            let input_and_arg = read_osc_arg(input, original_input, tag)?;
            input = input_and_arg.0;
            args.push(input_and_arg.1);
        }
    }
    Ok((input, args))
}

fn read_osc_arg<'a>(input: &'a[u8], original_input: &'a[u8], tag: char) -> IResult<&'a[u8], OscType, OscError> {
    match tag {
        'f' => map(be_f32, OscType::Float)(input),
        'd' => map(be_f64, OscType::Double)(input),
        'i' => map(be_i32, OscType::Int)(input),
        'h' => map(be_i64, OscType::Long)(input),
        's' => {
            read_osc_string(input, original_input)
                .map(|(remainder, string)| (remainder, OscType::String(string)))
        }
        't' => read_time_tag(input).map(|(remainder, time)| {
            (remainder, OscType::Time(time))
        }),
        'b' => read_blob(input, original_input),
        'r' => read_osc_color(input),
        'T' => Ok((input, true.into())),
        'F' => Ok((input, false.into())),
        'N' => Ok((input, OscType::Nil)),
        'I' => Ok((input, OscType::Inf)),
        'c' => read_char(input),
        'm' => read_midi_message(input),
        _ => Err(nom::Err::Error(OscError::BadArg(format!(
            "Type tag \"{}\" is not implemented!",
            tag
        )))),
    }
}

fn read_char(input: &[u8]) -> IResult<&[u8], OscType, OscError> {
    map_res(
        be_u32,
        |b| {
            let opt_char = char::from_u32(b);
            match opt_char {
                Some(c) => Ok(OscType::Char(c)),
                None => Err(OscError::BadArg(
                    "Argument is not a char!".to_string(),
                )),
            }
        },
    )(input)
}

fn read_blob<'a>(input: &'a[u8], original_input: &'a[u8]) -> IResult<&'a[u8], OscType, OscError> {
    let (input, size) = be_u32(input)?;

    map(
        terminated(
            take(size),
            pad_to_32_bit_boundary(original_input),
        ),
        |blob| OscType::Blob(blob.into()),
    )(input)
}

fn read_time_tag<'a>(input: &'a[u8]) -> IResult<&'a[u8], OscTime, OscError> {
    map(
        tuple((be_u32, be_u32)),
        |(seconds, fractional)| OscTime {
            seconds,
            fractional,
        },
    )(input)
}

fn read_midi_message<'a>(input: &'a[u8]) -> IResult<&'a[u8], OscType, OscError> {
    map(take(4usize), |buf: &[u8]| {
        OscType::Midi(OscMidiMessage {
            port: buf[0],
            status: buf[1],
            data1: buf[2],
            data2: buf[3],
        })
    })(input)
}

fn read_osc_color<'a>(input: &'a[u8]) -> IResult<&'a[u8], OscType, OscError> {
    map(take(4usize), |buf: &[u8]| {
        OscType::Color(OscColor {
            red: buf[0],
            green: buf[1],
            blue: buf[2],
            alpha: buf[3],
        })
    })(input)
}

fn pad_to_32_bit_boundary<'a>(
    original_input: &'a[u8]
) -> impl Fn(&'a[u8]) -> IResult<&'a[u8], (), OscError> {
    move |input| {
        let offset = 4 - original_input.offset(input) % 4;
        let (input, _) = take(offset)(input)?;
        Ok((input, ()))
    }
}
<commit_msg>Combine match cases<commit_after>use crate::errors::OscError;
use crate::types::{OscArray, OscBundle, OscColor, OscMessage, OscMidiMessage, OscPacket, OscTime, OscType};
use crate::alloc::{string::{String, ToString}, vec::Vec};

use nom::Offset;
use nom::sequence::terminated;
use nom::bytes::complete::{take, take_till};
use nom::combinator::{map, map_parser};
use nom::multi::many0;
use nom::number::complete::{be_f32, be_f64, be_i32, be_i64, be_u32};
use nom::{IResult,combinator::map_res,sequence::tuple,Err};

/// Common MTU size for ethernet
pub const MTU: usize = 1536;

/// Takes a bytes slice representing a UDP packet and returns the OSC packet as well as a slice of
/// any bytes remaining after the OSC packet.
pub fn decode_udp<'a>(msg: &'a [u8]) -> Result<(&'a [u8], OscPacket), OscError> {
    match decode_packet(msg, msg) {
        Ok((remainder, osc_packet)) => Ok((remainder, osc_packet)),
        Err(e) => match e {
            Err::Incomplete(_) => Err(OscError::BadPacket("Incomplete data")),
            Err::Error(e) | Err::Failure(e) => Err(e),
        }
    }
}

/// Takes a bytes slice from a TCP stream (or any stream-based protocol) and returns the first OSC
/// packet as well as a slice of the bytes remaining after the packet.
pub fn decode_tcp<'a>(msg: &'a [u8]) -> Result<(&'a [u8], Option<OscPacket>), OscError> {
    let (input, osc_packet_length) = match be_u32(msg) {
        Ok((i, o)) => (i, o),
        Err(e) => match e {
            Err::Incomplete(_) => return Err(OscError::BadPacket("Incomplete data")),
            Err::Error(e) | Err::Failure(e) => return Err(e),
        }
    };

    if osc_packet_length as usize > msg.len() {
        return Ok((msg, None));
    }

    match decode_packet(input, msg).map(|(remainder, osc_packet)| {
        (remainder, Some(osc_packet))
    }) {
        Ok((remainder, osc_packet)) => Ok((remainder, osc_packet)),
        Err(e) => match e {
            Err::Incomplete(_) => Err(OscError::BadPacket("Incomplete data")),
            Err::Error(e) | Err::Failure(e) => Err(e),
        }
    }
}

/// Takes a bytes slice from a TCP stream (or any stream-based protocol) and returns a vec of all
/// OSC packets in the slice as well as a slice of the bytes remaining after the last packet.
pub fn decode_tcp_vec<'a>(msg: &'a [u8]) -> Result<(&'a [u8], Vec<OscPacket>), OscError> {
    let mut input = msg;
    let mut osc_packets = vec![];

    while let (remainder, Some(osc_packet)) = decode_tcp(input)? {
        input = remainder;
        osc_packets.push(osc_packet);

        if remainder.len() == 0 {
            break;
        }
    };

    Ok((input, osc_packets))
}

fn decode_packet<'a>(
    input: &'a [u8],
    original_input: &'a[u8],
) -> IResult<&'a [u8], OscPacket, OscError> {
    if input.is_empty() {
        return Err(nom::Err::Error(OscError::BadPacket("Empty packet.")));
    }

    let (input, addr) = read_osc_string(input, original_input)?;

    match addr.chars().next() {
        Some('/') => {
            decode_message(addr, input, original_input)
        }
        Some('#') if &addr == "#bundle" => {
            decode_bundle(input, original_input)
        }
        _ => Err(nom::Err::Error(OscError::BadPacket(
            "Invalid message address or bundle tag"
        ))),
    }
}

fn decode_message<'a>(
    addr: String,
    input: &'a [u8],
    original_input: &'a[u8],
) -> IResult<&'a [u8], OscPacket, OscError> {
    let (input, type_tags) = read_osc_string(input, original_input)?;

    if type_tags.len() > 1 {
        let (input, args) = read_osc_args(
            input,
            original_input,
            type_tags,
        )?;
        Ok((input, OscPacket::Message(OscMessage { addr, args })))
    } else {
        Ok((input, OscPacket::Message(OscMessage { addr, args: vec![] })))
    }
}

fn decode_bundle<'a>(
    input: &'a [u8],
    original_input: &'a[u8],
) -> IResult<&'a [u8], OscPacket, OscError> {
    let (input, (timetag, content)) = tuple((
        read_time_tag,
        many0(|input| read_bundle_element(input, original_input)),
    ))(input)?;

    Ok((
        input,
        OscPacket::Bundle(OscBundle {
            timetag,
            content,
        }),
    ))
}

fn read_bundle_element<'a>(input: &'a [u8], original_input: &'a[u8]) -> IResult<&'a [u8], OscPacket, OscError> {
    let (input, elem_size) = be_u32(input)?;

    let result = map_parser(
        |input| {
            take(elem_size)(input).map_err(|_: nom::Err<OscError>| {
                nom::Err::Error(OscError::BadBundle(
                    "Bundle shorter than expected!".to_string(),
                ))
            })
        },
        |input| decode_packet(input, original_input),
    )(input);
    drop(elem_size);
    result
}

fn read_osc_string<'a>(input: &'a [u8], original_input: &'a[u8]) -> IResult<&'a [u8], String, OscError> {
    map_res(
        terminated(
            take_till(|c| c == 0u8),
            pad_to_32_bit_boundary(original_input),
        ),
        |str_buf: &'a[u8]| {
            String::from_utf8(str_buf.into())
                .map_err(OscError::StringError)
                .map(|s| s.trim_matches(0u8 as char).to_string())
        },
    )(input)
}

fn read_osc_args<'a>(
    mut input: &'a[u8],
    original_input: &'a[u8],
    raw_type_tags: String,
) ->  IResult<&'a [u8], Vec<OscType>, OscError> {
    let type_tags: Vec<char> = raw_type_tags.chars().skip(1).collect();

    let mut args: Vec<OscType> = Vec::with_capacity(type_tags.len());
    let mut stack: Vec<Vec<OscType>> = Vec::new();
    for tag in type_tags {
        if tag == '[' {
            // array start: save current frame and start a new frame
            // for the array's content
            stack.push(args);
            args = Vec::new();
        } else if tag == ']' {
            // found the end of the current array:
            // create array object from current frame and step one level up
            let array = OscType::Array(OscArray { content: args });
            match stack.pop() {
                Some(stashed) => args = stashed,
                None => return Err(nom::Err::Error(OscError::BadMessage(
                    "Encountered ] outside array"
                ))),
            }
            args.push(array);
        } else {
            let input_and_arg = read_osc_arg(input, original_input, tag)?;
            input = input_and_arg.0;
            args.push(input_and_arg.1);
        }
    }
    Ok((input, args))
}

fn read_osc_arg<'a>(input: &'a[u8], original_input: &'a[u8], tag: char) -> IResult<&'a[u8], OscType, OscError> {
    match tag {
        'f' => map(be_f32, OscType::Float)(input),
        'd' => map(be_f64, OscType::Double)(input),
        'i' => map(be_i32, OscType::Int)(input),
        'h' => map(be_i64, OscType::Long)(input),
        's' => {
            read_osc_string(input, original_input)
                .map(|(remainder, string)| (remainder, OscType::String(string)))
        }
        't' => read_time_tag(input).map(|(remainder, time)| {
            (remainder, OscType::Time(time))
        }),
        'b' => read_blob(input, original_input),
        'r' => read_osc_color(input),
        'T' => Ok((input, true.into())),
        'F' => Ok((input, false.into())),
        'N' => Ok((input, OscType::Nil)),
        'I' => Ok((input, OscType::Inf)),
        'c' => read_char(input),
        'm' => read_midi_message(input),
        _ => Err(nom::Err::Error(OscError::BadArg(format!(
            "Type tag \"{}\" is not implemented!",
            tag
        )))),
    }
}

fn read_char(input: &[u8]) -> IResult<&[u8], OscType, OscError> {
    map_res(
        be_u32,
        |b| {
            let opt_char = char::from_u32(b);
            match opt_char {
                Some(c) => Ok(OscType::Char(c)),
                None => Err(OscError::BadArg(
                    "Argument is not a char!".to_string(),
                )),
            }
        },
    )(input)
}

fn read_blob<'a>(input: &'a[u8], original_input: &'a[u8]) -> IResult<&'a[u8], OscType, OscError> {
    let (input, size) = be_u32(input)?;

    map(
        terminated(
            take(size),
            pad_to_32_bit_boundary(original_input),
        ),
        |blob| OscType::Blob(blob.into()),
    )(input)
}

fn read_time_tag<'a>(input: &'a[u8]) -> IResult<&'a[u8], OscTime, OscError> {
    map(
        tuple((be_u32, be_u32)),
        |(seconds, fractional)| OscTime {
            seconds,
            fractional,
        },
    )(input)
}

fn read_midi_message<'a>(input: &'a[u8]) -> IResult<&'a[u8], OscType, OscError> {
    map(take(4usize), |buf: &[u8]| {
        OscType::Midi(OscMidiMessage {
            port: buf[0],
            status: buf[1],
            data1: buf[2],
            data2: buf[3],
        })
    })(input)
}

fn read_osc_color<'a>(input: &'a[u8]) -> IResult<&'a[u8], OscType, OscError> {
    map(take(4usize), |buf: &[u8]| {
        OscType::Color(OscColor {
            red: buf[0],
            green: buf[1],
            blue: buf[2],
            alpha: buf[3],
        })
    })(input)
}

fn pad_to_32_bit_boundary<'a>(
    original_input: &'a[u8]
) -> impl Fn(&'a[u8]) -> IResult<&'a[u8], (), OscError> {
    move |input| {
        let offset = 4 - original_input.offset(input) % 4;
        let (input, _) = take(offset)(input)?;
        Ok((input, ()))
    }
}
<|endoftext|>