<commit_before>#![deny(missing_docs)]
#![feature(core)]

//! A SDL2 window back-end for the Piston game engine.

extern crate sdl2;
extern crate window;
extern crate shader_version;
extern crate input;
extern crate gl;
#[macro_use]
extern crate quack;

// External crates.
use std::mem::transmute;
use window::{
    WindowSettings,
    ShouldClose, Size, PollEvent, SwapBuffers,
    CaptureCursor, DrawSize, Title, ExitOnEsc
};
use input::{ keyboard, Button, MouseButton, Input, Motion };
use shader_version::opengl::OpenGL;
use quack::{ Associative, Set };

/// A widow implemented by SDL2 back-end.
pub struct Sdl2Window {
    /// SDL window handle
    pub window: sdl2::video::Window,
    /// Allow dead code because this keeps track of the OpenGL context.
    /// Will be released on drop.
    #[allow(dead_code)]
    pub context: sdl2::video::GLContext,
    /// SDL context
    pub sdl_context: sdl2::Sdl,
    should_close: bool,
    mouse_relative: Option<(f64, f64)>,
    exit_on_esc: bool,
}

impl Sdl2Window {
    /// Creates a new game window for SDL2.
    pub fn new(opengl: OpenGL, settings: WindowSettings) -> Sdl2Window {
        let sdl_context = sdl2::init(sdl2::INIT_EVERYTHING).unwrap();
        let (major, minor) = opengl.get_major_minor();
        sdl2::video::gl_set_attribute(
            sdl2::video::GLAttr::GLContextMajorVersion,
            major as i32
        );
        sdl2::video::gl_set_attribute(
            sdl2::video::GLAttr::GLContextMinorVersion,
            minor as i32
        );
        sdl2::video::gl_set_attribute(
            sdl2::video::GLAttr::GLContextProfileMask,
            sdl2::video::GLProfile::GLCoreProfile as i32
        );
        if settings.samples != 0 {
            sdl2::video::gl_set_attribute(
                sdl2::video::GLAttr::GLMultiSampleBuffers,
                1
            );
            sdl2::video::gl_set_attribute(
                sdl2::video::GLAttr::GLMultiSampleSamples,
                settings.samples as i32
            );
        }

        let window = sdl2::video::Window::new(
            settings.title.as_slice(),
            sdl2::video::WindowPos::PosCentered,
            sdl2::video::WindowPos::PosCentered,
            settings.size[0] as i32,
            settings.size[1] as i32,
            sdl2::video::OPENGL| sdl2::video::RESIZABLE
        ).unwrap();
        if settings.fullscreen {
            window.set_fullscreen(sdl2::video::FullscreenType::FTTrue);
        }

        // Send text input events.
        sdl2::keyboard::start_text_input();

        let context = window.gl_create_context().unwrap();

        // Load the OpenGL function pointers
        gl::load_with(|s| unsafe {
            transmute(sdl2::video::gl_get_proc_address(s))
        });

        Sdl2Window {
            exit_on_esc: settings.exit_on_esc,
            should_close: false,
            window: window,
            context: context,
            sdl_context: sdl_context,
            mouse_relative: None,
        }
    }

    fn poll_event(&mut self) -> Option<Input> {
        // Even though we create a new EventPump each time we poll an event
        // this should not be a problem since it only contains phantom data
        // and therefore should actually not have any overhead.
        let event = match self.sdl_context.event_pump().poll_event() {
            Some( ev ) => ev,
            None => return None
        };
        match self.mouse_relative {
            Some((x, y)) => {
                self.mouse_relative = None;
                return Some(Input::Move(Motion::MouseRelative(x, y)));
            }
            None => {}
        }
        match event {
            sdl2::event::Event::Quit{..} => {
                self.should_close = true;
            }
            sdl2::event::Event::TextInput { text, .. } => {
                return Some(Input::Text(text));
            }
            sdl2::event::Event::KeyDown { keycode: key, repeat, ..} => {
                // SDL2 repeats the key down event.
                // If the event is the same as last one, ignore it.
                if repeat {
                    return self.poll_event()
                }

                if self.exit_on_esc
                && key == sdl2::keycode::KeyCode::Escape {
                    self.should_close = true;
                } else {
                    return Some(Input::Press(Button::Keyboard(sdl2_map_key(key))));
                }
            }
            sdl2::event::Event::KeyUp { keycode: key, repeat, .. } => {
                if repeat {
                    return self.poll_event()
                }
                return Some(Input::Release(Button::Keyboard(sdl2_map_key(key))));
            }
            sdl2::event::Event::MouseButtonDown { mouse_btn: button, .. } => {
                return Some(Input::Press(Button::Mouse(sdl2_map_mouse(button))));
            }
            sdl2::event::Event::MouseButtonUp { mouse_btn: button, .. } => {
                return Some(Input::Release(Button::Mouse(sdl2_map_mouse(button))));
            }
            sdl2::event::Event::MouseMotion { x, y, xrel: dx, yrel: dy, .. } => {
                // Send relative move movement next time.
                self.mouse_relative = Some((dx as f64, dy as f64));
                return Some(Input::Move(Motion::MouseCursor(x as f64, y as f64)));
            },
            sdl2::event::Event::MouseWheel { x, y, .. } => {
                return Some(Input::Move(Motion::MouseScroll(x as f64, y as f64)));
            }
            sdl2::event::Event::Window { win_event_id: sdl2::event::WindowEventId::Resized, data1: w, data2: h, .. } => {
                return Some(Input::Resize(w as u32, h as u32));
            }
            sdl2::event::Event::Window { win_event_id: sdl2::event::WindowEventId::FocusGained, .. } => {
                return Some(Input::Focus(true));
            }
            sdl2::event::Event::Window { win_event_id: sdl2::event::WindowEventId::FocusLost, .. } => {
                return Some(Input::Focus(false));
            }
            _ => {}
        }
        None
    }
}

impl Drop for Sdl2Window {
    fn drop(&mut self) {
        self.set_mut(CaptureCursor(false));
    }
}

quack! {
    _obj: Sdl2Window[]
    get:
        fn () -> ShouldClose [] { ShouldClose(_obj.should_close) }
        fn () -> Size [] {
            let (w, h) = _obj.window.get_size();
            Size([w as u32, h as u32])
        }
        fn () -> DrawSize [] {
            let (w, h) = _obj.window.get_drawable_size();
            DrawSize([w as u32, h as u32])
        }
        fn () -> Title [] { Title(_obj.window.get_title()) }
        fn () -> ExitOnEsc [] { ExitOnEsc(_obj.exit_on_esc) }
    set:
        fn (val: CaptureCursor) [] {
            sdl2::mouse::set_relative_mouse_mode(val.0)
        }
        fn (val: ShouldClose) [] { _obj.should_close = val.0 }
        fn (val: Title) [] { _obj.window.set_title(val.0.as_slice()).unwrap() }
        fn (val: ExitOnEsc) [] { _obj.exit_on_esc = val.0 }
    action:
        fn (__: SwapBuffers) -> () [] { _obj.window.gl_swap_window() }
        fn (__: PollEvent) -> Option<Input> [] { _obj.poll_event() }
}

impl Associative for (PollEvent, Sdl2Window) {
    type Type = Input;
}

/// Maps a SDL2 key to piston-input key.
pub fn sdl2_map_key(keycode: sdl2::keycode::KeyCode) -> keyboard::Key {
    use std::num::FromPrimitive;
    FromPrimitive::from_u64(keycode as u64).unwrap()
}

/// Maps a SDL2 mouse button to piston-input button.
pub fn sdl2_map_mouse(button: sdl2::mouse::Mouse) -> MouseButton {
    match button {
        sdl2::mouse::Mouse::Left => MouseButton::Left,
        sdl2::mouse::Mouse::Right => MouseButton::Right,
        sdl2::mouse::Mouse::Middle => MouseButton::Middle,
        sdl2::mouse::Mouse::X1 => MouseButton::X1,
        sdl2::mouse::Mouse::X2 => MouseButton::X2,
        sdl2::mouse::Mouse::Unknown(_) => MouseButton::Unknown,
    }
}
<commit_msg>Fix #141 dropped events<commit_after>#![deny(missing_docs)]
#![feature(core)]

//! A SDL2 window back-end for the Piston game engine.

extern crate sdl2;
extern crate window;
extern crate shader_version;
extern crate input;
extern crate gl;
#[macro_use]
extern crate quack;

// External crates.
use std::mem::transmute;
use window::{
    WindowSettings,
    ShouldClose, Size, PollEvent, SwapBuffers,
    CaptureCursor, DrawSize, Title, ExitOnEsc
};
use input::{ keyboard, Button, MouseButton, Input, Motion };
use shader_version::opengl::OpenGL;
use quack::{ Associative, Set };

/// A widow implemented by SDL2 back-end.
pub struct Sdl2Window {
    /// SDL window handle
    pub window: sdl2::video::Window,
    /// Allow dead code because this keeps track of the OpenGL context.
    /// Will be released on drop.
    #[allow(dead_code)]
    pub context: sdl2::video::GLContext,
    /// SDL context
    pub sdl_context: sdl2::Sdl,
    should_close: bool,
    mouse_relative: Option<(f64, f64)>,
    exit_on_esc: bool,
}

impl Sdl2Window {
    /// Creates a new game window for SDL2.
    pub fn new(opengl: OpenGL, settings: WindowSettings) -> Sdl2Window {
        let sdl_context = sdl2::init(sdl2::INIT_EVERYTHING).unwrap();
        let (major, minor) = opengl.get_major_minor();
        sdl2::video::gl_set_attribute(
            sdl2::video::GLAttr::GLContextMajorVersion,
            major as i32
        );
        sdl2::video::gl_set_attribute(
            sdl2::video::GLAttr::GLContextMinorVersion,
            minor as i32
        );
        sdl2::video::gl_set_attribute(
            sdl2::video::GLAttr::GLContextProfileMask,
            sdl2::video::GLProfile::GLCoreProfile as i32
        );
        if settings.samples != 0 {
            sdl2::video::gl_set_attribute(
                sdl2::video::GLAttr::GLMultiSampleBuffers,
                1
            );
            sdl2::video::gl_set_attribute(
                sdl2::video::GLAttr::GLMultiSampleSamples,
                settings.samples as i32
            );
        }

        let window = sdl2::video::Window::new(
            settings.title.as_slice(),
            sdl2::video::WindowPos::PosCentered,
            sdl2::video::WindowPos::PosCentered,
            settings.size[0] as i32,
            settings.size[1] as i32,
            sdl2::video::OPENGL| sdl2::video::RESIZABLE
        ).unwrap();
        if settings.fullscreen {
            window.set_fullscreen(sdl2::video::FullscreenType::FTTrue);
        }

        // Send text input events.
        sdl2::keyboard::start_text_input();

        let context = window.gl_create_context().unwrap();

        // Load the OpenGL function pointers
        gl::load_with(|s| unsafe {
            transmute(sdl2::video::gl_get_proc_address(s))
        });

        Sdl2Window {
            exit_on_esc: settings.exit_on_esc,
            should_close: false,
            window: window,
            context: context,
            sdl_context: sdl_context,
            mouse_relative: None,
        }
    }

    fn poll_event(&mut self) -> Option<Input> {
        // First check for a pending relative mouse move event.
        match self.mouse_relative {
            Some((x, y)) => {
                self.mouse_relative = None;
                return Some(Input::Move(Motion::MouseRelative(x, y)));
            }
            None => {}
        }

        // Even though we create a new EventPump each time we poll an event
        // this should not be a problem since it only contains phantom data
        // and therefore should actually not have any overhead.
        let event = match self.sdl_context.event_pump().poll_event() {
            Some( ev ) => ev,
            None => return None
        };
        match event {
            sdl2::event::Event::Quit{..} => {
                self.should_close = true;
            }
            sdl2::event::Event::TextInput { text, .. } => {
                return Some(Input::Text(text));
            }
            sdl2::event::Event::KeyDown { keycode: key, repeat, ..} => {
                // SDL2 repeats the key down event.
                // If the event is the same as last one, ignore it.
                if repeat {
                    return self.poll_event()
                }

                if self.exit_on_esc
                && key == sdl2::keycode::KeyCode::Escape {
                    self.should_close = true;
                } else {
                    return Some(Input::Press(Button::Keyboard(sdl2_map_key(key))));
                }
            }
            sdl2::event::Event::KeyUp { keycode: key, repeat, .. } => {
                if repeat {
                    return self.poll_event()
                }
                return Some(Input::Release(Button::Keyboard(sdl2_map_key(key))));
            }
            sdl2::event::Event::MouseButtonDown { mouse_btn: button, .. } => {
                return Some(Input::Press(Button::Mouse(sdl2_map_mouse(button))));
            }
            sdl2::event::Event::MouseButtonUp { mouse_btn: button, .. } => {
                return Some(Input::Release(Button::Mouse(sdl2_map_mouse(button))));
            }
            sdl2::event::Event::MouseMotion { x, y, xrel: dx, yrel: dy, .. } => {
                // Send relative move movement next time.
                self.mouse_relative = Some((dx as f64, dy as f64));
                return Some(Input::Move(Motion::MouseCursor(x as f64, y as f64)));
            },
            sdl2::event::Event::MouseWheel { x, y, .. } => {
                return Some(Input::Move(Motion::MouseScroll(x as f64, y as f64)));
            }
            sdl2::event::Event::Window { win_event_id: sdl2::event::WindowEventId::Resized, data1: w, data2: h, .. } => {
                return Some(Input::Resize(w as u32, h as u32));
            }
            sdl2::event::Event::Window { win_event_id: sdl2::event::WindowEventId::FocusGained, .. } => {
                return Some(Input::Focus(true));
            }
            sdl2::event::Event::Window { win_event_id: sdl2::event::WindowEventId::FocusLost, .. } => {
                return Some(Input::Focus(false));
            }
            _ => {}
        }
        None
    }
}

impl Drop for Sdl2Window {
    fn drop(&mut self) {
        self.set_mut(CaptureCursor(false));
    }
}

quack! {
    _obj: Sdl2Window[]
    get:
        fn () -> ShouldClose [] { ShouldClose(_obj.should_close) }
        fn () -> Size [] {
            let (w, h) = _obj.window.get_size();
            Size([w as u32, h as u32])
        }
        fn () -> DrawSize [] {
            let (w, h) = _obj.window.get_drawable_size();
            DrawSize([w as u32, h as u32])
        }
        fn () -> Title [] { Title(_obj.window.get_title()) }
        fn () -> ExitOnEsc [] { ExitOnEsc(_obj.exit_on_esc) }
    set:
        fn (val: CaptureCursor) [] {
            sdl2::mouse::set_relative_mouse_mode(val.0)
        }
        fn (val: ShouldClose) [] { _obj.should_close = val.0 }
        fn (val: Title) [] { _obj.window.set_title(val.0.as_slice()).unwrap() }
        fn (val: ExitOnEsc) [] { _obj.exit_on_esc = val.0 }
    action:
        fn (__: SwapBuffers) -> () [] { _obj.window.gl_swap_window() }
        fn (__: PollEvent) -> Option<Input> [] { _obj.poll_event() }
}

impl Associative for (PollEvent, Sdl2Window) {
    type Type = Input;
}

/// Maps a SDL2 key to piston-input key.
pub fn sdl2_map_key(keycode: sdl2::keycode::KeyCode) -> keyboard::Key {
    use std::num::FromPrimitive;
    FromPrimitive::from_u64(keycode as u64).unwrap()
}

/// Maps a SDL2 mouse button to piston-input button.
pub fn sdl2_map_mouse(button: sdl2::mouse::Mouse) -> MouseButton {
    match button {
        sdl2::mouse::Mouse::Left => MouseButton::Left,
        sdl2::mouse::Mouse::Right => MouseButton::Right,
        sdl2::mouse::Mouse::Middle => MouseButton::Middle,
        sdl2::mouse::Mouse::X1 => MouseButton::X1,
        sdl2::mouse::Mouse::X2 => MouseButton::X2,
        sdl2::mouse::Mouse::Unknown(_) => MouseButton::Unknown,
    }
}
<|endoftext|>


<commit_before>//! Generate YYIDs
//!
//! - GitHub: <https://github.com/asaaki/yyid.rs>
//! - crates.io: <https://crates.io/crates/yyid>
//!
//! ### Example
//!
//! #### Rust
//!
//! ```rust
//! extern crate yyid;
//!
//! use yyid::yyid_string;
//!
//! fn main() {
//!     println!("{}", yyid_string());
//!     // => "02e7f0f6-067e-8c92-b25c-12c9180540a9"
//! }
//! ```
//!
//! #### C
//!
//! ```c
//! #include "path/to/yyid.rs/include/libyyid.h"
//! const char* my_yyid = yyid_c_string();
//! ```
//!
//! ### Other libraries for YYID
//!
//! - Ruby: <https://github.com/janlelis/yyid.rb>
//!
//!   ```ruby
//!   # Code here, since it is only a require and a one-liner:
//!   require "securerandom"
//!   "%08x-%04x-%04x-%04x-%04x%08x" % SecureRandom.random_bytes(16).unpack("NnnnnN")
//!   #=> "37ab3494-7e04-ecf1-b99f-259999a44d16"
//!   ```
//!
//! - JavaScript: <https://github.com/janlelis/yyid.js>
//! - Elixir: <https://github.com/janlelis/yyid.ex>
//! - Go: <https://github.com/janlelis/yyid.go>

// NOTE: Most of this code is currently based on uuid crate
//       (<https://github.com/rust-lang-nursery/uuid>).

#![doc(html_root_url = "http://asaaki.github.io/yyid.rs/yyid/index.html")]

#![cfg_attr(test, deny(warnings))]

extern crate libc;
extern crate rand;

use std::fmt;
use std::hash;
use std::ffi::CString;
use libc::c_char;
use rand::Rng;

pub type YYIDBytes = [u8; 16];

#[derive(Copy, Clone)]
pub struct YYID {
    bytes: YYIDBytes
}

/// Creates a new random YYID as String
///
/// ### Example
/// ```rust
/// extern crate yyid;
///
/// use yyid::yyid_string;
///
/// fn main() {
///     println!("{}", yyid_string());
///     // => "02e7f0f6-067e-8c92-b25c-12c9180540a9"
/// }
/// ```
pub fn yyid_string() -> String {
    YYID::new().to_string()
}

/// Creates a new random YYID as a C-compatible char*
#[no_mangle]
pub extern "C" fn yyid_c_string() -> *const c_char {
    let yyid   = yyid_string();
    let c_yyid = CString::new(yyid).unwrap();
    c_yyid.as_ptr()
}

impl YYID {
    /// Creates a new random YYID
    pub fn new() -> YYID {
        let mut ybytes = [0u8; 16];
        rand::thread_rng().fill_bytes(&mut ybytes);
        YYID{ bytes: ybytes }
    }

    /// Return an array of 16 octets containing the YYID data
    pub fn as_bytes<'a>(&'a self) -> &'a [u8] {
        &self.bytes
    }

    /// Returns a string of hexadecimal digits, separated into groups with a hyphen.
    ///
    /// Example: `02e7f0f6-067e-8c92-b25c-12c9180540a9`
    pub fn to_string(&self) -> String {
        let b = &self.bytes;
        format!("{:02x}{:02x}{:02x}{:02x}-\
                 {:02x}{:02x}-\
                 {:02x}{:02x}-\
                 {:02x}{:02x}-\
                 {:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
                 b[0], b[1], b[2], b[3],
                 b[4], b[5],
                 b[6], b[7],
                 b[8], b[9],
                 b[10], b[11], b[12], b[13], b[14], b[15])
    }

    /// Returns the YYID as a string of 32 hexadecimal digits
    ///
    /// Example: `2ff0b694960e88a4693a66cff98fc56c`
    pub fn to_simple_string(&self) -> String {
        let b = &self.bytes;
        format!("{:02x}{:02x}{:02x}{:02x}\
                 {:02x}{:02x}\
                 {:02x}{:02x}\
                 {:02x}{:02x}\
                 {:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
                 b[0], b[1], b[2], b[3],
                 b[4], b[5],
                 b[6], b[7],
                 b[8], b[9],
                 b[10], b[11], b[12], b[13], b[14], b[15])
    }

    /// Returns the YYID formatted as a full URN string
    ///
    /// This is the same as the hyphenated format, but with the "urn:yyid:" prefix.
    ///
    /// Example: `urn:yyid:05f7d6d3-1727-ce2d-6cf2-3b73ad48ff73`
    pub fn to_urn_string(&self) -> String {
        format!("urn:yyid:{}", self.to_string())
    }
}

/// Convert the YYID to a hexadecimal-based string representation wrapped in `YYID()`
impl fmt::Debug for YYID {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "YYID(\"{}\")", self.to_string())
    }
}

/// Convert the YYID to a hexadecimal-based string representation
impl fmt::Display for YYID {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.to_string())
    }
}

/// Test two YYIDs for equality
///
/// YYIDs are equal only when they are byte-for-byte identical
impl PartialEq for YYID {
    fn eq(&self, other: &YYID) -> bool {
        self.bytes == other.bytes
    }
}

impl Eq for YYID {}

/// Generates a random instance of YYID (V4 conformant)
impl rand::Rand for YYID {
    #[inline]
    fn rand<R: rand::Rng>(rng: &mut R) -> YYID {
        let mut ybytes = [0u8; 16];
        rng.fill_bytes(&mut ybytes);
        YYID{ bytes: ybytes }
    }
}

impl hash::Hash for YYID {
    fn hash<S: hash::Hasher>(&self, state: &mut S) {
        self.bytes.hash(state)
    }
}

#[cfg(test)]
mod tests {
    use super::YYID;
    use super::yyid_string;
    use super::yyid_c_string;
    use std::ffi::CStr;
    use std::str;
    use rand;

    #[test]
    fn test_new() {
        let yyid = YYID::new();
        let ystr = yyid.to_simple_string();

        assert!(ystr.len() == 32);
    }

    #[test]
    fn test_to_string() {
        let yyid = YYID::new();
        let ystr = yyid.to_string();

        assert!(ystr.len() == 36);
        assert!(ystr.chars().all(|c| c.is_digit(16) || c == '-'));
    }

    #[test]
    fn test_exported_yyid_c_string() {
        let yyid_chars = yyid_c_string(); // *const c_char (pointer)
        let c_yyid     = unsafe { CStr::from_ptr(yyid_chars) }; // &CStr
        let yyid       = str::from_utf8(c_yyid.to_bytes()).unwrap();
        //               -(to_bytes)-> &[u8] -(str::from_utf8)-> Result<&str, Utf8Error> -(unwrap)-> &str

        assert!(yyid.len() == 36);
        assert!(yyid.chars().all(|c| c.is_digit(16) || c == '-'));
    }

    #[test]
    fn test_yyid_string() {
        let ystr = yyid_string();

        assert!(ystr.len() == 36);
        assert!(ystr.chars().all(|c| c.is_digit(16) || c == '-'));
    }

    #[test]
    fn test_to_simple_string() {
        let yyid = YYID::new();
        let ystr = yyid.to_simple_string();

        assert!(ystr.len() == 32);
        assert!(ystr.chars().all(|c| c.is_digit(16)));
    }

    #[test]
    fn test_to_urn_string() {
        let yyid = YYID::new();
        let yurn = yyid.to_urn_string();
        let ystr = &yurn[9..];

        assert!(yurn.starts_with("urn:yyid:"));
        assert!(ystr.len() == 36);
        assert!(ystr.chars().all(|c| c.is_digit(16) || c == '-'));
    }

    #[test]
    fn test_to_simple_string_matching() {
        let yyid = YYID::new();

        let yhyphen = yyid.to_string();
        let ysimple = yyid.to_simple_string();

        let ysimplified = yhyphen.chars().filter(|&c| c != '-').collect::<String>();

        assert!(ysimplified == ysimple);
    }

    #[test]
    fn test_compare() {
        let yyid1 = YYID::new();
        let yyid2 = YYID::new();

        assert!(yyid1 == yyid1);
        assert!(yyid2 == yyid2);
        assert!(yyid1 != yyid2);
        assert!(yyid2 != yyid1);
    }

    #[test]
    fn test_as_bytes() {
        let yyid = YYID::new();
        let ybytes = yyid.as_bytes();

        assert!(ybytes.len() == 16);
        assert!(! ybytes.iter().all(|&b| b == 0));
    }

    #[test]
    fn test_operator_eq() {
        let yyid1 = YYID::new();
        let yyid2 = yyid1.clone();
        let yyid3 = YYID::new();

        assert!(yyid1 == yyid1);
        assert!(yyid1 == yyid2);
        assert!(yyid2 == yyid1);

        assert!(yyid1 != yyid3);
        assert!(yyid3 != yyid1);
        assert!(yyid2 != yyid3);
        assert!(yyid3 != yyid2);
    }

    #[test]
    fn test_rand_rand() {
        let mut rng = rand::thread_rng();
        let yyid: YYID = rand::Rand::rand(&mut rng);
        let ybytes = yyid.as_bytes();

        assert!(ybytes.len() == 16);
        assert!(! ybytes.iter().all(|&b| b == 0));
    }

    #[test]
    fn test_iterbytes_impl_for_yyid() {
        use std::collections::HashSet;
        let mut set = HashSet::new();
        let yyid1 = YYID::new();
        let yyid2 = YYID::new();
        set.insert(yyid1.clone());
        assert!(set.contains(&yyid1));
        assert!(!set.contains(&yyid2));
    }
}
<commit_msg>Format code with rustfmt<commit_after>//! Generate YYIDs
//!
//! - GitHub: <https://github.com/asaaki/yyid.rs>
//! - crates.io: <https://crates.io/crates/yyid>
//!
//! ### Example
//!
//! #### Rust
//!
//! ```rust
//! extern crate yyid;
//!
//! use yyid::yyid_string;
//!
//! fn main() {
//!     println!("{}", yyid_string());
//!     // => "02e7f0f6-067e-8c92-b25c-12c9180540a9"
//! }
//! ```
//!
//! #### C
//!
//! ```c
//! #include "path/to/yyid.rs/include/libyyid.h"
//! const char* my_yyid = yyid_c_string();
//! ```
//!
//! ### Other libraries for YYID
//!
//! - Ruby: <https://github.com/janlelis/yyid.rb>
//!
//!   ```ruby
//!   # Code here, since it is only a require and a one-liner:
//!   require "securerandom"
//!   "%08x-%04x-%04x-%04x-%04x%08x" %
//!     SecureRandom.random_bytes(16).unpack("NnnnnN")
//!   #=> "37ab3494-7e04-ecf1-b99f-259999a44d16"
//!   ```
//!
//! - JavaScript: <https://github.com/janlelis/yyid.js>
//! - Elixir: <https://github.com/janlelis/yyid.ex>
//! - Go: <https://github.com/janlelis/yyid.go>

// NOTE: Most of this code is currently based on uuid crate
//       (<https://github.com/rust-lang-nursery/uuid>).

#![doc(html_root_url = "http://asaaki.github.io/yyid.rs/yyid/index.html")]

#![cfg_attr(test, deny(warnings))]

extern crate libc;
extern crate rand;

use std::fmt;
use std::hash;
use std::ffi::CString;
use libc::c_char;
use rand::Rng;

pub type YYIDBytes = [u8; 16];

#[derive(Copy, Clone)]
pub struct YYID {
    bytes: YYIDBytes,
}

/// Creates a new random YYID as String
///
/// ### Example
/// ```rust
/// extern crate yyid;
///
/// use yyid::yyid_string;
///
/// fn main() {
///     println!("{}", yyid_string());
///     // => "02e7f0f6-067e-8c92-b25c-12c9180540a9"
/// }
/// ```
pub fn yyid_string() -> String {
    YYID::new().to_string()
}

/// Creates a new random YYID as a C-compatible char*
#[no_mangle]
pub extern "C" fn yyid_c_string() -> *const c_char {
    let yyid = yyid_string();
    let c_yyid = CString::new(yyid).unwrap();
    c_yyid.as_ptr()
}

impl YYID {
    /// Creates a new random YYID
    pub fn new() -> YYID {
        let mut ybytes = [0u8; 16];
        rand::thread_rng().fill_bytes(&mut ybytes);
        YYID { bytes: ybytes }
    }

    /// Return an array of 16 octets containing the YYID data
    pub fn as_bytes<'a>(&'a self) -> &'a [u8] {
        &self.bytes
    }

    /// Returns a string of hexadecimal digits, separated into groups with a
    /// hyphen.
    ///
    /// Example: `02e7f0f6-067e-8c92-b25c-12c9180540a9`
    pub fn to_string(&self) -> String {
        let b = &self.bytes;
        format!("{:02x}{:02x}{:02x}{:02x}-\
                 {:02x}{:02x}-\
                 {:02x}{:02x}-\
                 {:02x}{:02x}-\
                 {:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
                b[0],
                b[1],
                b[2],
                b[3],
                b[4],
                b[5],
                b[6],
                b[7],
                b[8],
                b[9],
                b[10],
                b[11],
                b[12],
                b[13],
                b[14],
                b[15])
    }

    /// Returns the YYID as a string of 32 hexadecimal digits
    ///
    /// Example: `2ff0b694960e88a4693a66cff98fc56c`
    pub fn to_simple_string(&self) -> String {
        let b = &self.bytes;
        format!("{:02x}{:02x}{:02x}{:02x}\
                 {:02x}{:02x}\
                 {:02x}{:02x}\
                 {:02x}{:02x}\
                 {:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
                b[0],
                b[1],
                b[2],
                b[3],
                b[4],
                b[5],
                b[6],
                b[7],
                b[8],
                b[9],
                b[10],
                b[11],
                b[12],
                b[13],
                b[14],
                b[15])
    }

    /// Returns the YYID formatted as a full URN string
    ///
    /// This is the same as the hyphenated format, but with the "urn:yyid:"
    /// prefix.
    ///
    /// Example: `urn:yyid:05f7d6d3-1727-ce2d-6cf2-3b73ad48ff73`
    pub fn to_urn_string(&self) -> String {
        format!("urn:yyid:{}", self.to_string())
    }
}

/// Convert the YYID to a hexadecimal-based string representation wrapped in
/// `YYID()`
impl fmt::Debug for YYID {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "YYID(\"{}\")", self.to_string())
    }
}

/// Convert the YYID to a hexadecimal-based string representation
impl fmt::Display for YYID {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.to_string())
    }
}

/// Test two YYIDs for equality
///
/// YYIDs are equal only when they are byte-for-byte identical
impl PartialEq for YYID {
    fn eq(&self, other: &YYID) -> bool {
        self.bytes == other.bytes
    }
}

impl Eq for YYID {}

/// Generates a random instance of YYID (V4 conformant)
impl rand::Rand for YYID {
    #[inline]
    fn rand<R: rand::Rng>(rng: &mut R) -> YYID {
        let mut ybytes = [0u8; 16];
        rng.fill_bytes(&mut ybytes);
        YYID { bytes: ybytes }
    }
}

impl hash::Hash for YYID {
    fn hash<S: hash::Hasher>(&self, state: &mut S) {
        self.bytes.hash(state)
    }
}

#[cfg(test)]
mod tests {
    use super::YYID;
    use super::yyid_string;
    use super::yyid_c_string;
    use std::ffi::CStr;
    use std::str;
    use rand;

    #[test]
    fn test_new() {
        let yyid = YYID::new();
        let ystr = yyid.to_simple_string();

        assert!(ystr.len() == 32);
    }

    #[test]
    fn test_to_string() {
        let yyid = YYID::new();
        let ystr = yyid.to_string();

        assert!(ystr.len() == 36);
        assert!(ystr.chars().all(|c| c.is_digit(16) || c == '-'));
    }

    #[test]
    fn test_exported_yyid_c_string() {
        let yyid_cptr = yyid_c_string();
        let yyid_cstr = unsafe { CStr::from_ptr(yyid_cptr) };
        let yyid = str::from_utf8(yyid_cstr.to_bytes()).unwrap();

        assert!(yyid.len() == 36);
        assert!(yyid.chars().all(|c| c.is_digit(16) || c == '-'));
    }

    #[test]
    fn test_yyid_string() {
        let ystr = yyid_string();

        assert!(ystr.len() == 36);
        assert!(ystr.chars().all(|c| c.is_digit(16) || c == '-'));
    }

    #[test]
    fn test_to_simple_string() {
        let yyid = YYID::new();
        let ystr = yyid.to_simple_string();

        assert!(ystr.len() == 32);
        assert!(ystr.chars().all(|c| c.is_digit(16)));
    }

    #[test]
    fn test_to_urn_string() {
        let yyid = YYID::new();
        let yurn = yyid.to_urn_string();
        let ystr = &yurn[9..];

        assert!(yurn.starts_with("urn:yyid:"));
        assert!(ystr.len() == 36);
        assert!(ystr.chars().all(|c| c.is_digit(16) || c == '-'));
    }

    #[test]
    fn test_to_simple_string_matching() {
        let yyid = YYID::new();

        let yhyphen = yyid.to_string();
        let ysimple = yyid.to_simple_string();

        let ysimplified = yhyphen.chars()
            .filter(|&c| c != '-')
            .collect::<String>();

        assert!(ysimplified == ysimple);
    }

    #[test]
    fn test_compare() {
        let yyid1 = YYID::new();
        let yyid2 = YYID::new();

        assert!(yyid1 == yyid1);
        assert!(yyid2 == yyid2);
        assert!(yyid1 != yyid2);
        assert!(yyid2 != yyid1);
    }

    #[test]
    fn test_as_bytes() {
        let yyid = YYID::new();
        let ybytes = yyid.as_bytes();

        assert!(ybytes.len() == 16);
        assert!(!ybytes.iter().all(|&b| b == 0));
    }

    #[test]
    fn test_operator_eq() {
        let yyid1 = YYID::new();
        let yyid2 = yyid1.clone();
        let yyid3 = YYID::new();

        assert!(yyid1 == yyid1);
        assert!(yyid1 == yyid2);
        assert!(yyid2 == yyid1);

        assert!(yyid1 != yyid3);
        assert!(yyid3 != yyid1);
        assert!(yyid2 != yyid3);
        assert!(yyid3 != yyid2);
    }

    #[test]
    fn test_rand_rand() {
        let mut rng = rand::thread_rng();
        let yyid: YYID = rand::Rand::rand(&mut rng);
        let ybytes = yyid.as_bytes();

        assert!(ybytes.len() == 16);
        assert!(!ybytes.iter().all(|&b| b == 0));
    }

    #[test]
    fn test_iterbytes_impl_for_yyid() {
        use std::collections::HashSet;
        let mut set = HashSet::new();
        let yyid1 = YYID::new();
        let yyid2 = YYID::new();
        set.insert(yyid1.clone());
        assert!(set.contains(&yyid1));
        assert!(!set.contains(&yyid2));
    }
}
<|endoftext|>


<commit_before>#![feature(test)]
use std::iter::Iterator;

// Flat-maps an Option<I> where I: Iterator into any underlying Iterator.
pub struct FlatIter<I: Iterator> {
    wrapped: Option<I>,
}

impl<I: Iterator> FlatIter<I> {
    pub fn new(wrapped: Option<I>) -> Self {
        FlatIter { wrapped: wrapped, }
    }
}

impl<I: Iterator> Iterator for FlatIter<I> {
    type Item = <I as Iterator>::Item;

    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
        match self.wrapped {
            Some(ref mut i) => i.next(),
            None => None,
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        match self.wrapped {
            Some(ref i) => i.size_hint(),
            None => (0usize, Some(0usize)),
        }
    }
}

// Main entrypoint for iteration via for comprehensions. Accepts expressions
// like:
//
// (for x in xs; (if a;|let b = c;)*)+ yield r
//
// Expressions are compiled into nested flat_map operations via closures that
// move out of the enclosing environment. See package-level documentation for
// details.
#[macro_export]
macro_rules! iterate {
    // Implementation note: adjacent wildcard matches aren't allowed, so each
    // recursive rule has three cases, wherein the wildcards matches are
    // separated by macro keywords.

    // yield
    (yield $r:expr) => (Some($r).into_iter());
    // chained yield
    (yield $r:expr; for $($rest:tt)+) =>
        (iterate![yield $r].chain(iterate![for $($rest)+]));
    (yield $r:expr; if $($rest:tt)+) =>
        (iterate![yield $r].chain(iterate![if $($rest)+]));
    (yield $r:expr; let $($rest:tt)+) =>
        (iterate![yield $r].chain(iterate![let $($rest)+]));
    (yield $r:expr; yield $($rest:tt)+) =>
        (iterate![yield $r].chain(iterate![yield $($rest)+]));
    // for
    (for $x:ident in $xs:expr; $($rest:tt)*) =>
        ($xs.flat_map(move |$x| { iterate![$($rest)*] }));
    // if if => if
    (if $a:expr; if $b:expr; $(if $c:expr;)* for $($rest:tt)*) =>
        (iterate![if $a && $b $(&& $c)*; for $($rest)*]);
    (if $a:expr; if $b:expr; $(if $c:expr;)* let $($rest:tt)*) =>
        (iterate![if $a && $b $(&& $c)*; let $($rest)*]);
    (if $a:expr; if $b:expr; $(if $c:expr;)* yield $($rest:tt)*) =>
        (iterate![if $a && $b $(&& $c)*; yield $($rest)*]);
    // if
    (if $a:expr; for $($rest:tt)*) =>
        ($crate::FlatIter::new(if $a { Some(iterate![for $($rest)*]) } else { None }));
    (if $a:expr; let $($rest:tt)*) =>
        ($crate::FlatIter::new(if $a { Some(iterate![let $($rest)*]) } else { None }));
    (if $a:expr; yield $($rest:tt)*) =>
        ($crate::FlatIter::new(if $a { Some(iterate![yield $($rest)*]) } else { None }));
    // let
    ($(let $lhs:pat = $rhs:expr;)+ for $($rest:tt)*) =>
        ({ $(let $lhs = $rhs;)+ iterate![for $($rest)*] });
    ($(let $lhs:pat = $rhs:expr;)+ if $($rest:tt)*) =>
        ({ $(let $lhs = $rhs;)+ iterate![if $($rest)*] });
    ($(let $lhs:pat = $rhs:expr;)+ yield $($rest:tt)*) =>
        ({ $(let $lhs = $rhs;)+ iterate![yield $($rest)*] });
}

#[cfg(test)]
mod tests {
    extern crate test;
    use std::fmt::Display;

    fn check_match<N: PartialEq + Display, I: Iterator<Item=N>, J: Iterator<Item=N>>(mut i: I, mut j: J) {
        let mut n = 0usize;
        loop {
            match (i.next(), j.next()) {
                (None, None) => return,
                (Some(ref x), None) => panic!["Mismatch on item {}: {} vs. None", n, x],
                (None, Some(ref y)) => panic!["Mismatch on item {}: None vs. {}", n, y],
                (Some(ref x), Some(ref y)) => if x != y { panic!["Mismatch on item {}: {} vs. {}", n, x, y] },
            }
            n += 1;
        }
    }

    #[test]
    fn test_basic() {
        check_match(vec![1, 2, 3, 4, 5].into_iter(),
                    iterate![for i in 1..6; yield i]);
    }

    #[test]
    fn test_basic_if() {
        check_match(vec![0, 2, 4, 6, 8].into_iter(),
                    iterate![for i in 0..10;
                             if i % 2 == 0;
                             yield i]);
    }

    #[test]
    fn test_basic_if_let() {
        check_match(vec![1, 3, 5, 7, 9].into_iter(),
                    iterate![for i in 0..10;
                             if i % 2 == 0;
                             let z = i + 1;
                             yield z]);
    }

    // This fails to compile because the closures generated by iterate! may
    // outlive values. (Using values.into_iter() also fails.)
    // fn test_move_fail() {
    //     let values = vec![1, 2, 3, 4];
    //     iterate![for i in 0..10; for v in values.iter(); yield v];
    // }

    // This provides an example of how to refer to an external variable without
    // moving everything out of it.
    #[test]
    fn test_move_succeed() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![1, 2, 3, 4,
                         1, 2, 3, 4,
                         1, 2, 3, 4,
                         1, 2, 3, 4].into_iter(),
                    iterate![for i in 0..4;
                             for v in vs.iter();
                             yield *v]);
    }

    // Taking references like that is still prone to lifetime-checking,
    // however. The below also fails to compile.
    // fn test_move_fail() {
    //     let i = {
    //         let values = vec![1, 2, 3, 4];
    //         let vs = &values;
    //         iterate![for i in 0..4; for v in vs.iter(); yield *v]
    //     };
    //     for x in i {
    //         println!("{}", x)
    //     }
    // }

    #[test]
    fn test_nested_refs() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![1, 2, 3, 4,
                         1, 2, 3, 4,
                         1, 2, 3, 4,
                         1, 2, 3, 4].into_iter(),
                    iterate![for x in vs.iter();
                             for y in vs.iter();
                             yield *y]);
    }

    #[test]
    fn test_nested_refs_inner_if() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![2, 2, 2, 2].into_iter(),
                    iterate![for x in vs.iter();
                             for y in vs.iter();
                             if *y == 2;
                             yield *y]);
    }

    #[test]
    fn test_nested_refs_outer_let() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![5, 6, 7, 8,
                         6, 7, 8, 9,
                         7, 8, 9, 10,
                         8, 9, 10, 11].into_iter(),
                    iterate![for x in vs.iter();
                             let a = *x + 3;
                             for y in vs.iter();
                             yield *y + a]);
    }

    // Variable shadowing is allowed, even when variables differ by type. This
    // is consistent with ordinary "let".
    #[test]
    fn test_nested_refs_let_let_shadowing_1() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![2, 3, 4, 5,
                         2, 3, 4, 5,
                         2, 3, 4, 5,
                         2, 3, 4, 5].into_iter(),
                    iterate![for x in vs.iter();
                             let a = *x + 3;
                             for y in vs.iter();
                             let a = 1;
                             yield *y + a]);
    }

    #[test]
    fn test_nested_refs_let_let_shadowing_2() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![2, 3, 4, 5,
                         2, 3, 4, 5,
                         2, 3, 4, 5,
                         2, 3, 4, 5].into_iter(),
                    iterate![for x in vs.iter();
                             let a = *x + 3;
                             let a = 1;
                             for y in vs.iter();
                             yield *y + a]);
    }

    // The compiler will warn about unused loop variables.
    #[test]
    fn test_nested_refs_for_let_shadowing() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![5, 5, 5, 5,
                         6, 6, 6, 6,
                         7, 7, 7, 7,
                         8, 8, 8, 8].into_iter(),
                    iterate![for x in vs.iter();
                             let a = *x + 3;
                             for y in vs.iter();
                             let y = 1;
                             yield y + a]);
    }

    #[test]
    fn test_nested_refs_for_let_for_shadowing() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        let other_values = vec![5, 6, 7, 8];
        let ys = &other_values;
        check_match(vec![5, 6, 7, 8,
                         5, 6, 7, 8,
                         5, 6, 7, 8,
                         5, 6, 7, 8].into_iter(),
                    iterate![for x in vs.iter();
                             let x = *x + 1;
                             for x in ys.iter();
                             yield *x]);
    }

    #[test]
    fn test_just_multiple_yields() {
        check_match(vec![0, 1, 2, 3, 4, 5].into_iter(),
                    iterate![yield 0;
                             yield 1;
                             yield 2;
                             yield 3;
                             yield 4;
                             yield 5]);
    }

    #[test]
    fn test_multiple_yields_liked_nested_loops() {
        check_match(vec![0, 5, 6, 7, 8, 9,
                         1, 5, 6, 7, 8, 9,
                         2, 5, 6, 7, 8, 9,
                         3, 5, 6, 7, 8, 9,
                         4, 5, 6, 7, 8, 9].into_iter(),
                    iterate![for x in 0..5;
                             yield x;
                             for y in 5..10;
                             yield y]);
    }

    #[test]
    fn test_multiple_yield_with_filter_1() {
      check_match(vec![0, 5, 6, 7, 8, 9,
                       1,
                       2, 5, 6, 7, 8, 9,
                       3,
                       4, 5, 6, 7, 8, 9].into_iter(),
                  iterate![for x in 0..5;
                           yield x;
                           if x % 2 == 0;
                           for y in 5..10;
                           yield y]);
    }

    #[test]
    fn test_multiple_yield_with_filter_2() {
      check_match(vec![0, 5, 6, 7, 8, 9,
                       2, 5, 6, 7, 8, 9,
                       4, 5, 6, 7, 8, 9].into_iter(),
                  iterate![for x in 0..5;
                           if x % 2 == 0;
                           yield x;
                           for y in 5..10;
                           yield y]);
    }
}
<commit_msg>Describe multiple-yield syntax in documentation.<commit_after>#![feature(test)]
use std::iter::Iterator;

// Flat-maps an Option<I> where I: Iterator into any underlying Iterator.
pub struct FlatIter<I: Iterator> {
    wrapped: Option<I>,
}

impl<I: Iterator> FlatIter<I> {
    pub fn new(wrapped: Option<I>) -> Self {
        FlatIter { wrapped: wrapped, }
    }
}

impl<I: Iterator> Iterator for FlatIter<I> {
    type Item = <I as Iterator>::Item;

    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
        match self.wrapped {
            Some(ref mut i) => i.next(),
            None => None,
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        match self.wrapped {
            Some(ref i) => i.size_hint(),
            None => (0usize, Some(0usize)),
        }
    }
}

/// Main entrypoint for iteration via for comprehensions. Produces an iterator
/// over values `yield`ed in a sequence of semicolon-separated expressions.
///
/// Multiple `yield` expressions are permitted. If present, they will produce a
/// chained sequence of iterators. Iterator chaining functions according to the
/// scope of any immediately preceding `for` statement.
///
/// For example, `iterate![yield 0; yield 1; yield 2]` produces the sequence `1,
/// 2, 3`, but `iterate![for x in 0..2; yield x; for y in 2..4; yield y]`
/// produces the sequence `0, 2, 3, 1, 2, 3`, as if you had written:
///
/// ```rust,ignore
/// for x in 0..2 {
///   yield x;
///   for y in 2..4 {
///     yield y;
///   }
/// }
/// ```
///
/// Expressions are:
///
/// * `for x in xs`: introduces a new scope that iterates over `xs`, binding `x`
/// to each of its values.
/// * `if cond`: short-circuits subsequent expressions if `cond` is not `true`.
/// * `let a = b`: introduces a new scope that binds `a` to `b`.
/// * `yield r`: emits the value of the expression `r`.
///
/// Expressions are compiled into nested flat_map operations via closures that
/// move out of the enclosing environment. See the package README for details.
#[macro_export]
macro_rules! iterate {
    // Implementation note: adjacent wildcard matches aren't allowed, so each
    // recursive rule has three cases, wherein the wildcards matches are
    // separated by macro keywords.

    // yield
    (yield $r:expr) => (Some($r).into_iter());
    // chained yield
    (yield $r:expr; for $($rest:tt)+) =>
        (iterate![yield $r].chain(iterate![for $($rest)+]));
    (yield $r:expr; if $($rest:tt)+) =>
        (iterate![yield $r].chain(iterate![if $($rest)+]));
    (yield $r:expr; let $($rest:tt)+) =>
        (iterate![yield $r].chain(iterate![let $($rest)+]));
    (yield $r:expr; yield $($rest:tt)+) =>
        (iterate![yield $r].chain(iterate![yield $($rest)+]));
    // for
    (for $x:ident in $xs:expr; $($rest:tt)*) =>
        ($xs.flat_map(move |$x| { iterate![$($rest)*] }));
    // if if => if
    (if $a:expr; if $b:expr; $(if $c:expr;)* for $($rest:tt)*) =>
        (iterate![if $a && $b $(&& $c)*; for $($rest)*]);
    (if $a:expr; if $b:expr; $(if $c:expr;)* let $($rest:tt)*) =>
        (iterate![if $a && $b $(&& $c)*; let $($rest)*]);
    (if $a:expr; if $b:expr; $(if $c:expr;)* yield $($rest:tt)*) =>
        (iterate![if $a && $b $(&& $c)*; yield $($rest)*]);
    // if
    (if $a:expr; for $($rest:tt)*) =>
        ($crate::FlatIter::new(if $a { Some(iterate![for $($rest)*]) } else { None }));
    (if $a:expr; let $($rest:tt)*) =>
        ($crate::FlatIter::new(if $a { Some(iterate![let $($rest)*]) } else { None }));
    (if $a:expr; yield $($rest:tt)*) =>
        ($crate::FlatIter::new(if $a { Some(iterate![yield $($rest)*]) } else { None }));
    // let
    ($(let $lhs:pat = $rhs:expr;)+ for $($rest:tt)*) =>
        ({ $(let $lhs = $rhs;)+ iterate![for $($rest)*] });
    ($(let $lhs:pat = $rhs:expr;)+ if $($rest:tt)*) =>
        ({ $(let $lhs = $rhs;)+ iterate![if $($rest)*] });
    ($(let $lhs:pat = $rhs:expr;)+ yield $($rest:tt)*) =>
        ({ $(let $lhs = $rhs;)+ iterate![yield $($rest)*] });
}

#[cfg(test)]
mod tests {
    extern crate test;
    use std::fmt::Display;

    fn check_match<N: PartialEq + Display, I: Iterator<Item=N>, J: Iterator<Item=N>>(mut i: I, mut j: J) {
        let mut n = 0usize;
        loop {
            match (i.next(), j.next()) {
                (None, None) => return,
                (Some(ref x), None) => panic!["Mismatch on item {}: {} vs. None", n, x],
                (None, Some(ref y)) => panic!["Mismatch on item {}: None vs. {}", n, y],
                (Some(ref x), Some(ref y)) => if x != y { panic!["Mismatch on item {}: {} vs. {}", n, x, y] },
            }
            n += 1;
        }
    }

    #[test]
    fn test_basic() {
        check_match(vec![1, 2, 3, 4, 5].into_iter(),
                    iterate![for i in 1..6; yield i]);
    }

    #[test]
    fn test_basic_if() {
        check_match(vec![0, 2, 4, 6, 8].into_iter(),
                    iterate![for i in 0..10;
                             if i % 2 == 0;
                             yield i]);
    }

    #[test]
    fn test_basic_if_let() {
        check_match(vec![1, 3, 5, 7, 9].into_iter(),
                    iterate![for i in 0..10;
                             if i % 2 == 0;
                             let z = i + 1;
                             yield z]);
    }

    // This fails to compile because the closures generated by iterate! may
    // outlive values. (Using values.into_iter() also fails.)
    // fn test_move_fail() {
    //     let values = vec![1, 2, 3, 4];
    //     iterate![for i in 0..10; for v in values.iter(); yield v];
    // }

    // This provides an example of how to refer to an external variable without
    // moving everything out of it.
    #[test]
    fn test_move_succeed() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![1, 2, 3, 4,
                         1, 2, 3, 4,
                         1, 2, 3, 4,
                         1, 2, 3, 4].into_iter(),
                    iterate![for i in 0..4;
                             for v in vs.iter();
                             yield *v]);
    }

    // Taking references like that is still prone to lifetime-checking,
    // however. The below also fails to compile.
    // fn test_move_fail() {
    //     let i = {
    //         let values = vec![1, 2, 3, 4];
    //         let vs = &values;
    //         iterate![for i in 0..4; for v in vs.iter(); yield *v]
    //     };
    //     for x in i {
    //         println!("{}", x)
    //     }
    // }

    #[test]
    fn test_nested_refs() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![1, 2, 3, 4,
                         1, 2, 3, 4,
                         1, 2, 3, 4,
                         1, 2, 3, 4].into_iter(),
                    iterate![for x in vs.iter();
                             for y in vs.iter();
                             yield *y]);
    }

    #[test]
    fn test_nested_refs_inner_if() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![2, 2, 2, 2].into_iter(),
                    iterate![for x in vs.iter();
                             for y in vs.iter();
                             if *y == 2;
                             yield *y]);
    }

    #[test]
    fn test_nested_refs_outer_let() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![5, 6, 7, 8,
                         6, 7, 8, 9,
                         7, 8, 9, 10,
                         8, 9, 10, 11].into_iter(),
                    iterate![for x in vs.iter();
                             let a = *x + 3;
                             for y in vs.iter();
                             yield *y + a]);
    }

    // Variable shadowing is allowed, even when variables differ by type. This
    // is consistent with ordinary "let".
    #[test]
    fn test_nested_refs_let_let_shadowing_1() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![2, 3, 4, 5,
                         2, 3, 4, 5,
                         2, 3, 4, 5,
                         2, 3, 4, 5].into_iter(),
                    iterate![for x in vs.iter();
                             let a = *x + 3;
                             for y in vs.iter();
                             let a = 1;
                             yield *y + a]);
    }

    #[test]
    fn test_nested_refs_let_let_shadowing_2() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![2, 3, 4, 5,
                         2, 3, 4, 5,
                         2, 3, 4, 5,
                         2, 3, 4, 5].into_iter(),
                    iterate![for x in vs.iter();
                             let a = *x + 3;
                             let a = 1;
                             for y in vs.iter();
                             yield *y + a]);
    }

    // The compiler will warn about unused loop variables.
    #[test]
    fn test_nested_refs_for_let_shadowing() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        check_match(vec![5, 5, 5, 5,
                         6, 6, 6, 6,
                         7, 7, 7, 7,
                         8, 8, 8, 8].into_iter(),
                    iterate![for x in vs.iter();
                             let a = *x + 3;
                             for y in vs.iter();
                             let y = 1;
                             yield y + a]);
    }

    #[test]
    fn test_nested_refs_for_let_for_shadowing() {
        let values = vec![1, 2, 3, 4];
        let vs = &values;
        let other_values = vec![5, 6, 7, 8];
        let ys = &other_values;
        check_match(vec![5, 6, 7, 8,
                         5, 6, 7, 8,
                         5, 6, 7, 8,
                         5, 6, 7, 8].into_iter(),
                    iterate![for x in vs.iter();
                             let x = *x + 1;
                             for x in ys.iter();
                             yield *x]);
    }

    #[test]
    fn test_just_multiple_yields() {
        check_match(vec![0, 1, 2, 3, 4, 5].into_iter(),
                    iterate![yield 0;
                             yield 1;
                             yield 2;
                             yield 3;
                             yield 4;
                             yield 5]);
    }

    #[test]
    fn test_multiple_yields_liked_nested_loops() {
        check_match(vec![0, 5, 6, 7, 8, 9,
                         1, 5, 6, 7, 8, 9,
                         2, 5, 6, 7, 8, 9,
                         3, 5, 6, 7, 8, 9,
                         4, 5, 6, 7, 8, 9].into_iter(),
                    iterate![for x in 0..5;
                             yield x;
                             for y in 5..10;
                             yield y]);
    }

    #[test]
    fn test_multiple_yield_with_filter_1() {
      check_match(vec![0, 5, 6, 7, 8, 9,
                       1,
                       2, 5, 6, 7, 8, 9,
                       3,
                       4, 5, 6, 7, 8, 9].into_iter(),
                  iterate![for x in 0..5;
                           yield x;
                           if x % 2 == 0;
                           for y in 5..10;
                           yield y]);
    }

    #[test]
    fn test_multiple_yield_with_filter_2() {
      check_match(vec![0, 5, 6, 7, 8, 9,
                       2, 5, 6, 7, 8, 9,
                       4, 5, 6, 7, 8, 9].into_iter(),
                  iterate![for x in 0..5;
                           if x % 2 == 0;
                           yield x;
                           for y in 5..10;
                           yield y]);
    }
}
<|endoftext|>


<commit_before>#![feature(step_by)]

extern crate libc;
pub extern crate nix;

use nix::sys::ptrace;
pub use nix::sys::ptrace::ptrace::*;
use nix::sys::signal;
use std::ptr;
use std::default::Default;
use std::vec::Vec;
use std::mem;

pub type Address = usize;
pub type Word = usize;
pub type IWord = isize;

pub const PTRACE_O_EXITKILL: PtraceOptions        = 1 << 20;
pub const PTRACE_O_SUSPEND_SECCOMP: PtraceOptions = 1 << 21;

#[derive(Clone, Copy, Default, Debug)]
pub struct Registers {
    pub r15: Word,
    pub r14: Word,
    pub r13: Word,
    pub r12: Word,
    pub rbp: Word,
    pub rbx: Word,
    pub r11: Word,
    pub r10: Word,
    pub r9: Word,
    pub r8: Word,
    pub rax: Word,
    pub rcx: Word,
    pub rdx: Word,
    pub rsi: Word,
    pub rdi: Word,
    pub orig_rax: Word,
    pub rip: Word,
    pub cs: Word,
    pub eflags: Word,
    pub rsp: Word,
    pub ss: Word,
    pub fs_base: Word,
    pub gs_base: Word,
    pub ds: Word,
    pub es: Word,
    pub fs: Word,
    pub gs: Word
}

fn ptrace_raw(request: PtraceRequest, pid: libc::pid_t, addr: *mut libc::c_void, data: *mut libc::c_void) -> Result<libc::c_long, i32> {
    match ptrace::ptrace(request, pid, addr, data) {
        Ok(v) => Ok(v),
        Err(e) => match e {
            nix::Error::Sys(errno) => Err(errno as i32),
            nix::Error::InvalidPath => panic!("nix InvalidPath caught"),
        },
    }
}
pub fn setoptions(pid: libc::pid_t, options: PtraceOptions) -> Result<(), i32> {
    match ptrace::ptrace_setoptions(pid, options) {
        Ok(v) => Ok(v),
        Err(e) => match e {
            nix::Error::Sys(errno) => Err(errno as i32),
            nix::Error::InvalidPath => panic!("nix InvalidPath caught"),
        },
    }
}

pub fn getregs(pid: libc::pid_t) -> Result<Registers, i32> {
    let mut buf: Registers = Default::default();
    let buf_mut: *mut Registers = &mut buf;

    match ptrace_raw(PTRACE_GETREGS, pid, ptr::null_mut(), buf_mut as *mut libc::c_void) {
        Ok(_) => Ok(buf),
        Err(e) => Err(e)
    }
}

pub fn setregs(pid: libc::pid_t, regs: &Registers) -> Result<libc::c_long, i32> {
    let buf: *mut libc::c_void = unsafe { mem::transmute(regs) };
    ptrace_raw(PTRACE_SETREGS, pid, ptr::null_mut(), buf)
}

pub fn seize(pid: libc::pid_t) -> Result<libc::c_long, i32> {
    ptrace_raw(PTRACE_SEIZE, pid, ptr::null_mut(), ptr::null_mut())
}

pub fn attach(pid: libc::pid_t) -> Result<libc::c_long, i32> {
    ptrace_raw(PTRACE_ATTACH, pid, ptr::null_mut(), ptr::null_mut())
}

pub fn release(pid: libc::pid_t, signal: Option<signal::Signal>) -> Result<libc::c_long, i32> {
    ptrace_raw(PTRACE_DETACH, pid, ptr::null_mut(), signal.map_or(0, |s| s as u32) as *mut libc::c_void)
}

pub fn geteventmsg(pid: libc::pid_t) -> Result<libc::c_ulong, i32> {
    let mut msg: libc::c_ulong = 0;
    let msg_ptr = &mut msg as *mut libc::c_ulong as *mut libc::c_void;
    ptrace_raw(PTRACE_GETEVENTMSG, pid, ptr::null_mut(), msg_ptr)?;
    Ok(msg)
}

pub fn cont(pid: libc::pid_t, signal: Option<signal::Signal>) -> Result<libc::c_long, i32> {
    ptrace_raw(PTRACE_CONT, pid, ptr::null_mut(), signal.map_or(0, |s| s as u32) as *mut libc::c_void)
}

pub fn cont_syscall(pid: libc::pid_t, signal: Option<signal::Signal>) -> Result<libc::c_long, i32> {
    ptrace_raw(PTRACE_SYSCALL, pid, ptr::null_mut(), signal.map_or(0, |s| s as u32) as *mut libc::c_void)
}

pub fn traceme() -> Result<libc::c_long, i32> {
    ptrace_raw(PTRACE_TRACEME, 0, ptr::null_mut(), ptr::null_mut())
}

#[derive(Clone, Copy, Debug)]
pub struct Syscall {
    pub args: [Word; 6],
    pub call: Word,
    pub pid: libc::pid_t,
    pub return_val: IWord
}

impl Syscall {
    pub fn from_pid(pid: libc::pid_t) -> Result<Syscall, i32> {
        match getregs(pid) {
            Ok(regs) =>
                Ok(Syscall {
                    pid: pid,
                    call: regs.orig_rax,
                    args: [regs.rdi, regs.rsi, regs.rdx, regs.rcx, regs.r8, regs.r9],
                    return_val: regs.rax as IWord
                }),
            Err(e) => Err(e)
        }
    }

    pub fn write(&self) -> Result<libc::c_long, i32> {
        match getregs(self.pid) {
            Ok(mut regs) => {
                regs.rdi = self.args[0];
                regs.rsi = self.args[1];
                regs.rdx = self.args[2];
                regs.rcx = self.args[3];
                regs.r8 = self.args[4];
                regs.r9 = self.args[5];
                regs.orig_rax = self.call;
                regs.rax = self.return_val as Word;
                setregs(self.pid, &regs)
            },
            Err(e) => Err(e)
        }
    }
}

#[derive(Clone, Copy)]
pub struct Reader {
    pub pid: libc::pid_t
}

#[derive(Clone, Copy)]
pub struct Writer {
    pub pid: libc::pid_t
}

impl Writer {
    pub fn new(pid: libc::pid_t) -> Self {
        Writer {
            pid: pid
        }
    }

    pub fn poke_data(&self, address: Address, data: Word) -> Result<Word, i32> {
        match ptrace_raw(PTRACE_POKEDATA, self.pid, address as *mut libc::c_void,
                             data as *mut libc::c_void) {
            Err(e) => Err(e),
            Ok(r) => Ok(r as Word)
        }
    }

    pub fn write_object<T: Sized>(&self, address: Address, data: &T) -> Result<(), usize> {
        let mut buf = Vec::with_capacity(mem::size_of::<T>());
        unsafe {
            let tptr: *const T = data;
            let p: *const u8 = mem::transmute(tptr);
            for i in 0..buf.capacity() {
                buf.push(*p.offset(i as isize));
            }
        }

        Ok(())
    }

    pub fn write_data(&self, address: Address, buf: &Vec<u8>) -> Result<(), i32> {
        // The end of our range
        let max_addr = address + buf.len() as Address;
        // The last word we can completely overwrite
        let align_end = max_addr - (max_addr % mem::size_of::<Word>() as Address);
        for write_addr in (address..align_end).step_by(mem::size_of::<Word>() as Address) {
            let mut d: Word = 0;
            let buf_idx = (write_addr - address) as usize;
            for word_idx in 0..mem::size_of::<Word>() {
                d = set_byte(d, word_idx, buf[buf_idx + word_idx]);
            }
            match self.poke_data(write_addr, d) {
                Ok(_) => {},
                Err(e) => return Err(e)
            }
        }
        // Handle a partial word overwrite
        if max_addr > align_end {
            let buf_start = buf.len() - (max_addr - align_end) as usize;
            let r = Reader::new(self.pid);
            let mut d = match r.peek_data(align_end) {
                Ok(v) => v,
                Err(e) => return Err(e)
            };
            for word_idx in 0..mem::size_of::<Word>() - 2 {
                let buf_idx = buf_start + word_idx;
                d = set_byte(d, word_idx, buf[buf_idx]);
            }
            match self.poke_data(align_end, d) {
                Ok(_) => {},
                Err(e) => return Err(e)
            }
        }
        Ok(())
    }
}

impl Reader {
    pub fn new(pid: libc::pid_t) -> Reader {
        Reader {
            pid: pid
        }
    }

    pub fn peek_data(&self, address: Address) -> Result<Word, i32> {
        match ptrace_raw(PTRACE_PEEKDATA, self.pid, address as *mut libc::c_void, ptr::null_mut()) {
            Ok(v) => Ok(v as Word),
            Err(e) => Err(e),
        }
    }

    pub fn read_string(&self, address: Address) -> Result<Vec<u8>, i32> {
        let mut end_of_str = false;
        let mut buf: Vec<u8> = Vec::with_capacity(1024);
        let max_addr = address + buf.capacity() as Address;
        let align_end = max_addr - (max_addr % mem::size_of::<Word>() as Address);
        'finish: for read_addr in (address..align_end).step_by(mem::size_of::<Word>() as Address) {
            let d;
            match self.peek_data(read_addr) {
                Ok(v) => d = v,
                Err(e) => return Err(e)
            }
            for word_idx in 0..mem::size_of::<Word>() {
                let chr = get_byte(d, word_idx);
                if chr == 0 {
                    end_of_str = true;
                    break 'finish;
                }
                buf.push(chr);
            }
        }
        if !end_of_str {
            let d;
            match self.peek_data(align_end) {
                Ok(v) => d = v,
                Err(e) => return Err(e)
            }
            for word_idx in 0..mem::size_of::<Word>() {
                let chr = get_byte(d, word_idx);
                if chr == 0 {
                    break;
                }
                buf.push(chr);
            }
        }
        return Ok(buf);
    }
}

fn get_byte(d: Word, byte_idx: usize) -> u8 {
    assert!(byte_idx < mem::size_of::<Word>());
    ((d >> (byte_idx * 8)) & 0xff) as u8
}

fn set_byte(d: Word, byte_idx: usize, value: u8) -> Word {
    assert!(byte_idx < mem::size_of::<Word>());
    let shift = mem::size_of::<u8>() * 8 * byte_idx;
    let mask = 0xff << shift;
    (d & !mask) | (((value as Word) << shift) & mask)
}

#[test]
pub fn test_set_byte() {
    assert_eq!(set_byte(0, 0, 0), 0);
    assert_eq!(set_byte(0xffffffffffff, 0, 0xff), 0xffffffffffff);
    assert_eq!(set_byte(0xffffffffffff, 0, 0),    0xffffffffff00);
    assert_eq!(set_byte(0xffffffffffff, 0, 0xaa), 0xffffffffffaa);
    assert_eq!(set_byte(0xffffffffffff, 1, 0x00), 0xffffffff00ff);
    assert_eq!(set_byte(0xffffffffffff, 4, 0xaa), 0xffaaffffffff);
}

#[test]
pub fn test_get_byte() {
    assert_eq!(get_byte(0, 0), 0);
    assert_eq!(get_byte(0xffffffffffff, 0), 0xff);
    assert_eq!(get_byte(0xffffffffffaa, 0), 0xaa);
    assert_eq!(get_byte(0x0123456789ab, 1), 0x89);
    assert_eq!(get_byte(0x0123456789ab, 4), 0x23);
}
<commit_msg>Add max_length parameter to read_string<commit_after>#![feature(step_by)]

extern crate libc;
pub extern crate nix;

use nix::sys::ptrace;
pub use nix::sys::ptrace::ptrace::*;
use nix::sys::signal;
use std::ptr;
use std::default::Default;
use std::vec::Vec;
use std::mem;

pub type Address = usize;
pub type Word = usize;
pub type IWord = isize;

pub const PTRACE_O_EXITKILL: PtraceOptions        = 1 << 20;
pub const PTRACE_O_SUSPEND_SECCOMP: PtraceOptions = 1 << 21;

#[derive(Clone, Copy, Default, Debug)]
pub struct Registers {
    pub r15: Word,
    pub r14: Word,
    pub r13: Word,
    pub r12: Word,
    pub rbp: Word,
    pub rbx: Word,
    pub r11: Word,
    pub r10: Word,
    pub r9: Word,
    pub r8: Word,
    pub rax: Word,
    pub rcx: Word,
    pub rdx: Word,
    pub rsi: Word,
    pub rdi: Word,
    pub orig_rax: Word,
    pub rip: Word,
    pub cs: Word,
    pub eflags: Word,
    pub rsp: Word,
    pub ss: Word,
    pub fs_base: Word,
    pub gs_base: Word,
    pub ds: Word,
    pub es: Word,
    pub fs: Word,
    pub gs: Word
}

fn ptrace_raw(request: PtraceRequest, pid: libc::pid_t, addr: *mut libc::c_void, data: *mut libc::c_void) -> Result<libc::c_long, i32> {
    match ptrace::ptrace(request, pid, addr, data) {
        Ok(v) => Ok(v),
        Err(e) => match e {
            nix::Error::Sys(errno) => Err(errno as i32),
            nix::Error::InvalidPath => panic!("nix InvalidPath caught"),
        },
    }
}
pub fn setoptions(pid: libc::pid_t, options: PtraceOptions) -> Result<(), i32> {
    match ptrace::ptrace_setoptions(pid, options) {
        Ok(v) => Ok(v),
        Err(e) => match e {
            nix::Error::Sys(errno) => Err(errno as i32),
            nix::Error::InvalidPath => panic!("nix InvalidPath caught"),
        },
    }
}

pub fn getregs(pid: libc::pid_t) -> Result<Registers, i32> {
    let mut buf: Registers = Default::default();
    let buf_mut: *mut Registers = &mut buf;

    match ptrace_raw(PTRACE_GETREGS, pid, ptr::null_mut(), buf_mut as *mut libc::c_void) {
        Ok(_) => Ok(buf),
        Err(e) => Err(e)
    }
}

pub fn setregs(pid: libc::pid_t, regs: &Registers) -> Result<libc::c_long, i32> {
    let buf: *mut libc::c_void = unsafe { mem::transmute(regs) };
    ptrace_raw(PTRACE_SETREGS, pid, ptr::null_mut(), buf)
}

pub fn seize(pid: libc::pid_t) -> Result<libc::c_long, i32> {
    ptrace_raw(PTRACE_SEIZE, pid, ptr::null_mut(), ptr::null_mut())
}

pub fn attach(pid: libc::pid_t) -> Result<libc::c_long, i32> {
    ptrace_raw(PTRACE_ATTACH, pid, ptr::null_mut(), ptr::null_mut())
}

pub fn release(pid: libc::pid_t, signal: Option<signal::Signal>) -> Result<libc::c_long, i32> {
    ptrace_raw(PTRACE_DETACH, pid, ptr::null_mut(), signal.map_or(0, |s| s as u32) as *mut libc::c_void)
}

pub fn geteventmsg(pid: libc::pid_t) -> Result<libc::c_ulong, i32> {
    let mut msg: libc::c_ulong = 0;
    let msg_ptr = &mut msg as *mut libc::c_ulong as *mut libc::c_void;
    ptrace_raw(PTRACE_GETEVENTMSG, pid, ptr::null_mut(), msg_ptr)?;
    Ok(msg)
}

pub fn cont(pid: libc::pid_t, signal: Option<signal::Signal>) -> Result<libc::c_long, i32> {
    ptrace_raw(PTRACE_CONT, pid, ptr::null_mut(), signal.map_or(0, |s| s as u32) as *mut libc::c_void)
}

pub fn cont_syscall(pid: libc::pid_t, signal: Option<signal::Signal>) -> Result<libc::c_long, i32> {
    ptrace_raw(PTRACE_SYSCALL, pid, ptr::null_mut(), signal.map_or(0, |s| s as u32) as *mut libc::c_void)
}

pub fn traceme() -> Result<libc::c_long, i32> {
    ptrace_raw(PTRACE_TRACEME, 0, ptr::null_mut(), ptr::null_mut())
}

#[derive(Clone, Copy, Debug)]
pub struct Syscall {
    pub args: [Word; 6],
    pub call: Word,
    pub pid: libc::pid_t,
    pub return_val: IWord
}

impl Syscall {
    pub fn from_pid(pid: libc::pid_t) -> Result<Syscall, i32> {
        match getregs(pid) {
            Ok(regs) =>
                Ok(Syscall {
                    pid: pid,
                    call: regs.orig_rax,
                    args: [regs.rdi, regs.rsi, regs.rdx, regs.rcx, regs.r8, regs.r9],
                    return_val: regs.rax as IWord
                }),
            Err(e) => Err(e)
        }
    }

    pub fn write(&self) -> Result<libc::c_long, i32> {
        match getregs(self.pid) {
            Ok(mut regs) => {
                regs.rdi = self.args[0];
                regs.rsi = self.args[1];
                regs.rdx = self.args[2];
                regs.rcx = self.args[3];
                regs.r8 = self.args[4];
                regs.r9 = self.args[5];
                regs.orig_rax = self.call;
                regs.rax = self.return_val as Word;
                setregs(self.pid, &regs)
            },
            Err(e) => Err(e)
        }
    }
}

#[derive(Clone, Copy)]
pub struct Reader {
    pub pid: libc::pid_t
}

#[derive(Clone, Copy)]
pub struct Writer {
    pub pid: libc::pid_t
}

impl Writer {
    pub fn new(pid: libc::pid_t) -> Self {
        Writer {
            pid: pid
        }
    }

    pub fn poke_data(&self, address: Address, data: Word) -> Result<Word, i32> {
        match ptrace_raw(PTRACE_POKEDATA, self.pid, address as *mut libc::c_void,
                             data as *mut libc::c_void) {
            Err(e) => Err(e),
            Ok(r) => Ok(r as Word)
        }
    }

    pub fn write_object<T: Sized>(&self, address: Address, data: &T) -> Result<(), usize> {
        let mut buf = Vec::with_capacity(mem::size_of::<T>());
        unsafe {
            let tptr: *const T = data;
            let p: *const u8 = mem::transmute(tptr);
            for i in 0..buf.capacity() {
                buf.push(*p.offset(i as isize));
            }
        }

        Ok(())
    }

    pub fn write_data(&self, address: Address, buf: &Vec<u8>) -> Result<(), i32> {
        // The end of our range
        let max_addr = address + buf.len() as Address;
        // The last word we can completely overwrite
        let align_end = max_addr - (max_addr % mem::size_of::<Word>() as Address);
        for write_addr in (address..align_end).step_by(mem::size_of::<Word>() as Address) {
            let mut d: Word = 0;
            let buf_idx = (write_addr - address) as usize;
            for word_idx in 0..mem::size_of::<Word>() {
                d = set_byte(d, word_idx, buf[buf_idx + word_idx]);
            }
            match self.poke_data(write_addr, d) {
                Ok(_) => {},
                Err(e) => return Err(e)
            }
        }
        // Handle a partial word overwrite
        if max_addr > align_end {
            let buf_start = buf.len() - (max_addr - align_end) as usize;
            let r = Reader::new(self.pid);
            let mut d = match r.peek_data(align_end) {
                Ok(v) => v,
                Err(e) => return Err(e)
            };
            for word_idx in 0..mem::size_of::<Word>() - 2 {
                let buf_idx = buf_start + word_idx;
                d = set_byte(d, word_idx, buf[buf_idx]);
            }
            match self.poke_data(align_end, d) {
                Ok(_) => {},
                Err(e) => return Err(e)
            }
        }
        Ok(())
    }
}

impl Reader {
    pub fn new(pid: libc::pid_t) -> Reader {
        Reader {
            pid: pid
        }
    }

    pub fn peek_data(&self, address: Address) -> Result<Word, i32> {
        match ptrace_raw(PTRACE_PEEKDATA, self.pid, address as *mut libc::c_void, ptr::null_mut()) {
            Ok(v) => Ok(v as Word),
            Err(e) => Err(e),
        }
    }

    pub fn read_string(&self, address: Address, max_length: usize) -> Result<Vec<u8>, i32> {
        let mut end_of_str = false;
        let mut buf: Vec<u8> = Vec::with_capacity(max_length);
        let max_addr = address + buf.capacity() as Address;
        let align_end = max_addr - (max_addr % mem::size_of::<Word>() as Address);
        'finish: for read_addr in (address..align_end).step_by(mem::size_of::<Word>() as Address) {
            let d;
            match self.peek_data(read_addr) {
                Ok(v) => d = v,
                Err(e) => return Err(e)
            }
            for word_idx in 0..mem::size_of::<Word>() {
                let chr = get_byte(d, word_idx);
                if chr == 0 {
                    end_of_str = true;
                    break 'finish;
                }
                buf.push(chr);
            }
        }
        if !end_of_str {
            let d;
            match self.peek_data(align_end) {
                Ok(v) => d = v,
                Err(e) => return Err(e)
            }
            for word_idx in 0..mem::size_of::<Word>() {
                let chr = get_byte(d, word_idx);
                if chr == 0 {
                    break;
                }
                buf.push(chr);
            }
        }
        return Ok(buf);
    }
}

fn get_byte(d: Word, byte_idx: usize) -> u8 {
    assert!(byte_idx < mem::size_of::<Word>());
    ((d >> (byte_idx * 8)) & 0xff) as u8
}

fn set_byte(d: Word, byte_idx: usize, value: u8) -> Word {
    assert!(byte_idx < mem::size_of::<Word>());
    let shift = mem::size_of::<u8>() * 8 * byte_idx;
    let mask = 0xff << shift;
    (d & !mask) | (((value as Word) << shift) & mask)
}

#[test]
pub fn test_set_byte() {
    assert_eq!(set_byte(0, 0, 0), 0);
    assert_eq!(set_byte(0xffffffffffff, 0, 0xff), 0xffffffffffff);
    assert_eq!(set_byte(0xffffffffffff, 0, 0),    0xffffffffff00);
    assert_eq!(set_byte(0xffffffffffff, 0, 0xaa), 0xffffffffffaa);
    assert_eq!(set_byte(0xffffffffffff, 1, 0x00), 0xffffffff00ff);
    assert_eq!(set_byte(0xffffffffffff, 4, 0xaa), 0xffaaffffffff);
}

#[test]
pub fn test_get_byte() {
    assert_eq!(get_byte(0, 0), 0);
    assert_eq!(get_byte(0xffffffffffff, 0), 0xff);
    assert_eq!(get_byte(0xffffffffffaa, 0), 0xaa);
    assert_eq!(get_byte(0x0123456789ab, 1), 0x89);
    assert_eq!(get_byte(0x0123456789ab, 4), 0x23);
}
<|endoftext|>


<commit_before>#![deny(missing_docs)]
#![feature(io,test)]

//! jed creates Json iterators over instances of io.Read

extern crate rustc_serialize;
extern crate test;

use std::io::{ Chars, Read };
use std::iter::Iterator;
use rustc_serialize::json::{ Json, Builder };

/// An iterator over the Json elements of an io::Read stream
pub struct Iter<R> {
  inner: R
}

impl<R: Read> Iter<R> {
  /// Create a new Iter instance
  pub fn new(inner: R) -> Iter<R> {
    Iter { inner: inner }
  }
}

impl<R: Read> Iterator for Iter<R> {
  type Item = Json;

  fn next(&mut self) -> Option<Json> {

    fn chomp<R: Read>(mut chars: Chars<R>, buf: &mut String) -> Option<Json> {
      match chars.next() {
        Some(Ok(c)) => {
          buf.push(c);
          match c {
            '}' | ']' => {
              let cbuf = buf.clone();
              match Builder::new(cbuf.chars()).build() {
                Ok(j) => Some(j),
                _ => chomp(chars, buf)
              }
            }, _ =>
              chomp(chars, buf)
          }
        }, _ => None
      }
    }
    let ref mut inner = self.inner;
    chomp(inner.chars(), &mut String::new())
  }
}

#[cfg(test)]
mod tests {
  use super::Iter;
  use test::Bencher;
  use std::io::{ empty, BufReader };

  #[test]
  fn test_not_json_iter() {
    let reader = BufReader::new("bogus".as_bytes());
    assert_eq!(Iter::new(reader).count(), 0);
  }

  #[test]
  fn test_empty_iter() {
    assert_eq!(Iter::new(empty()).count(), 0);
  }

  #[test]
  fn test_ary_iter() {
    let reader = BufReader::new("[][]".as_bytes());
    assert_eq!(Iter::new(reader).count(), 2)
  }

  #[test]
  fn test_obj_iter() {
    let reader = BufReader::new("{}{}".as_bytes());
    assert_eq!(Iter::new(reader).count(), 2)
  }

  #[bench]
  fn bench_iter(b: &mut Bencher) {
    b.iter(|| Iter::new(BufReader::new("{'foo':'bar'}{'foo':'baz'}".as_bytes())).count())
  }
}
<commit_msg>fix annotations for test<commit_after>#![deny(missing_docs)]
#![cfg_attr(all(test, feature = "nightly"), feature(test))]

//! jed creates Json iterators over instances of io.Read

extern crate rustc_serialize;

use std::io::{ Chars, Read };
use std::iter::Iterator;
use rustc_serialize::json::{ Json, Builder };

/// An iterator over the Json elements of an io::Read stream
pub struct Iter<R> {
  inner: R
}

impl<R: Read> Iter<R> {
  /// Create a new Iter instance
  pub fn new(inner: R) -> Iter<R> {
    Iter { inner: inner }
  }
}

impl<R: Read> Iterator for Iter<R> {
  type Item = Json;

  fn next(&mut self) -> Option<Json> {

    fn chomp<R: Read>(mut chars: Chars<R>, buf: &mut String) -> Option<Json> {
      match chars.next() {
        Some(Ok(c)) => {
          buf.push(c);
          match c {
            '}' | ']' => {
              let cbuf = buf.clone();
              match Builder::new(cbuf.chars()).build() {
                Ok(j) => Some(j),
                _ => chomp(chars, buf)
              }
            }, _ =>
              chomp(chars, buf)
          }
        }, _ => None
      }
    }
    let ref mut inner = self.inner;
    chomp(inner.chars(), &mut String::new())
  }
}

#[cfg(test)]
mod tests {
  use super::Iter;
  use test::Bencher;
  use std::io::{ empty, BufReader };

  #[test]
  fn test_not_json_iter() {
    let reader = BufReader::new("bogus".as_bytes());
    assert_eq!(Iter::new(reader).count(), 0);
  }

  #[test]
  fn test_empty_iter() {
    assert_eq!(Iter::new(empty()).count(), 0);
  }

  #[test]
  fn test_ary_iter() {
    let reader = BufReader::new("[][]".as_bytes());
    assert_eq!(Iter::new(reader).count(), 2)
  }

  #[test]
  fn test_obj_iter() {
    let reader = BufReader::new("{}{}".as_bytes());
    assert_eq!(Iter::new(reader).count(), 2)
  }

  #[bench]
  fn bench_iter(b: &mut Bencher) {
    b.iter(|| Iter::new(BufReader::new("{'foo':'bar'}{'foo':'baz'}".as_bytes())).count())
  }
}
<|endoftext|>


<commit_before>//! Setup and control loop devices.
//!
//! Provides rust interface with similar functionality to the Linux utility `losetup`.
//!
//! # Examples
//!
//! ```rust
//! use loopdev::LoopControl;
//! let lc = LoopControl::open().unwrap();
//! let ld = lc.next_free().unwrap();
//!
//! println!("{}", ld.path().unwrap().display());
//!
//! ld.attach_file("test.img").unwrap();
//! // ...
//! ld.detach().unwrap();
//! ```

extern crate libc;

use std::fs::File;
use std::fs::OpenOptions;

use libc::{c_int, ioctl};
use std::default::Default;
use std::io;
use std::os::unix::prelude::*;
use std::path::{Path, PathBuf};

// TODO support missing operations
const LOOP_SET_FD: u16 = 0x4C00;
const LOOP_CLR_FD: u16 = 0x4C01;
const LOOP_SET_STATUS64: u16 = 0x4C04;
//const LOOP_GET_STATUS64: u16 = 0x4C05;
const LOOP_SET_CAPACITY: u16 = 0x4C07;
//const LOOP_SET_DIRECT_IO: u16 = 0x4C08;
//const LOOP_SET_BLOCK_SIZE: u16 = 0x4C09;

//const LOOP_CTL_ADD: u16 = 0x4C80;
//const LOOP_CTL_REMOVE: u16 = 0x4C81;
const LOOP_CTL_GET_FREE: u16 = 0x4C82;

const LOOP_CONTROL: &str = "/dev/loop-control";
const LOOP_PREFIX: &str = "/dev/loop";

/// Interface to the loop control device: `/dev/loop-control`.
#[derive(Debug)]
pub struct LoopControl {
    dev_file: File,
}

impl LoopControl {
    /// Opens the loop control device.
    pub fn open() -> io::Result<Self> {
        Ok(Self {
            dev_file: OpenOptions::new()
                .read(true)
                .write(true)
                .open(LOOP_CONTROL)?,
        })
    }

    /// Finds and opens the next available loop device.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use loopdev::LoopControl;
    /// let lc = LoopControl::open().unwrap();
    /// let ld = lc.next_free().unwrap();
    /// println!("{}", ld.path().unwrap().display());
    /// ```
    pub fn next_free(&self) -> io::Result<LoopDevice> {
        let dev_num = ioctl_to_error(unsafe {
            ioctl(self.dev_file.as_raw_fd() as c_int, LOOP_CTL_GET_FREE.into())
        })?;
        LoopDevice::open(&format!("{}{}", LOOP_PREFIX, dev_num))
    }
}

/// Interface to a loop device ie `/dev/loop0`.
#[derive(Debug)]
pub struct LoopDevice {
    device: File,
}

impl AsRawFd for LoopDevice {
    fn as_raw_fd(&self) -> RawFd {
        self.device.as_raw_fd()
    }
}

impl LoopDevice {
    /// Opens a loop device.
    pub fn open<P: AsRef<Path>>(dev: P) -> io::Result<Self> {
        // TODO create dev if it does not exist and begins with LOOP_PREFIX
        Ok(Self {
            device: OpenOptions::new().read(true).write(true).open(dev)?,
        })
    }

    /// Attach the loop device to a file starting at offset into the file.
    #[deprecated(
        since = "0.2.0",
        note = "use `attach_file` or `attach_with_offset` instead"
    )]
    pub fn attach<P: AsRef<Path>>(&self, backing_file: P, offset: u64) -> io::Result<()> {
        self.attach_with_sizelimit(backing_file, offset, 0)
    }

    /// Attach the loop device to a file that maps to the whole file.
    ///
    /// # Examples
    ///
    /// Attach the device to a file.
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let ld = LoopDevice::open("/dev/loop4").unwrap();
    /// ld.attach_file("test.img").unwrap();
    /// # ld.detach().unwrap();
    /// ```
    pub fn attach_file<P: AsRef<Path>>(&self, backing_file: P) -> io::Result<()> {
        self.attach_with_sizelimit(backing_file, 0, 0)
    }

    /// Attach the loop device to a file starting at offset into the file.
    ///
    /// # Examples
    ///
    /// Attach the device to the start of a file.
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let ld = LoopDevice::open("/dev/loop5").unwrap();
    /// ld.attach_with_offset("test.img", 0).unwrap();
    /// # ld.detach().unwrap();
    /// ```
    pub fn attach_with_offset<P: AsRef<Path>>(
        &self,
        backing_file: P,
        offset: u64,
    ) -> io::Result<()> {
        self.attach_with_sizelimit(backing_file, offset, 0)
    }

    /// Attach the loop device to a file starting at offset into the file and a the given sizelimit.
    ///
    /// # Examples
    ///
    /// Attach the device to the start of a file with a maximum size of 1024 bytes.
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let ld = LoopDevice::open("/dev/loop6").unwrap();
    /// ld.attach_with_sizelimit("test.img", 0, 1024).unwrap();
    /// # ld.detach().unwrap();
    /// ```
    pub fn attach_with_sizelimit<P: AsRef<Path>>(
        &self,
        backing_file: P,
        offset: u64,
        sizelimit: u64,
    ) -> io::Result<()> {
        // Set offset for backing_file
        let mut info = loop_info64::default();
        info.lo_offset = offset;
        info.lo_sizelimit = sizelimit;

        Self::attach_with_loop_info(self, backing_file, info)
    }

    /// Attach the loop device with automatic partition scanning.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let ld = LoopDevice::open("/dev/loop5").unwrap();
    /// ld.attach_with_partscan("test.img").unwrap();
    /// # ld.detach().unwrap();
    /// ```
    pub fn attach_with_partscan<P: AsRef<Path>>(&self, backing_file: P) -> io::Result<()> {
        // Set lo_flags to LO_FLAGS_PARTSCAN value
        let mut info = loop_info64::default();
        info.lo_flags = 8;

        Self::attach_with_loop_info(self, backing_file, info)
    }

    /// Attach the loop device to a file with loop_info.
    fn attach_with_loop_info<P: AsRef<Path>>(
        &self,
        backing_file: P,
        loop_info: loop_info64,
    ) -> io::Result<()> {
        let bf = OpenOptions::new()
            .read(true)
            .write(true)
            .open(backing_file)?;

        // Attach the file
        ioctl_to_error(unsafe {
            ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_SET_FD.into(),
                bf.as_raw_fd() as c_int,
            )
        })?;

        if let Err(err) = ioctl_to_error(unsafe {
            ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_SET_STATUS64.into(),
                &loop_info,
            )
        }) {
            // Ignore the error to preserve the original error
            let _ = self.detach();
            return Err(err);
        }
        Ok(())
    }

    /// Get the path of the loop device.
    #[deprecated(since = "0.2.0", note = "use `path` instead")]
    pub fn get_path(&self) -> Option<PathBuf> {
        self.path()
    }

    /// Get the path of the loop device.
    pub fn path(&self) -> Option<PathBuf> {
        let mut p = PathBuf::from("/proc/self/fd");
        p.push(self.device.as_raw_fd().to_string());
        std::fs::read_link(&p).ok()
    }

    /// Detach a loop device from its backing file.
    ///
    /// Note that the device won't fully detach until a short delay after the underling device file
    /// gets closed. This happens when LoopDev goes out of scope so you should ensure the LoopDev
    /// lives for a short a time as possible.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let ld = LoopDevice::open("/dev/loop7").unwrap();
    /// # ld.attach_file("test.img").unwrap();
    /// ld.detach().unwrap();
    /// ```
    pub fn detach(&self) -> io::Result<()> {
        ioctl_to_error(unsafe { ioctl(self.device.as_raw_fd() as c_int, LOOP_CLR_FD.into(), 0) })?;
        Ok(())
    }

    /// Resize a live loop device. If the size of the backing file changes this can be called to
    /// inform the loop driver about the new size.
    pub fn set_capacity(&self) -> io::Result<()> {
        ioctl_to_error(unsafe {
            ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_SET_CAPACITY.into(),
                0,
            )
        })?;
        Ok(())
    }
}

#[repr(C)]
struct loop_info64 {
    pub lo_device: u64,
    pub lo_inode: u64,
    pub lo_rdevice: u64,
    pub lo_offset: u64,
    pub lo_sizelimit: u64,
    pub lo_number: u32,
    pub lo_encrypt_type: u32,
    pub lo_encrypt_key_size: u32,
    pub lo_flags: u32,
    pub lo_file_name: [u8; 64],
    pub lo_crypt_name: [u8; 64],
    pub lo_encrypt_key: [u8; 32],
    pub lo_init: [u64; 2],
}

impl Default for loop_info64 {
    fn default() -> Self {
        Self {
            lo_device: 0,
            lo_inode: 0,
            lo_rdevice: 0,
            lo_offset: 0,
            lo_sizelimit: 0,
            lo_number: 0,
            lo_encrypt_type: 0,
            lo_encrypt_key_size: 0,
            lo_flags: 0,
            lo_file_name: [0; 64],
            lo_crypt_name: [0; 64],
            lo_encrypt_key: [0; 32],
            lo_init: [0; 2],
        }
    }
}

fn ioctl_to_error(ret: i32) -> io::Result<i32> {
    if ret < 0 {
        Err(io::Error::last_os_error())
    } else {
        Ok(ret)
    }
}
<commit_msg>Renames the loop_info64 struct<commit_after>//! Setup and control loop devices.
//!
//! Provides rust interface with similar functionality to the Linux utility `losetup`.
//!
//! # Examples
//!
//! ```rust
//! use loopdev::LoopControl;
//! let lc = LoopControl::open().unwrap();
//! let ld = lc.next_free().unwrap();
//!
//! println!("{}", ld.path().unwrap().display());
//!
//! ld.attach_file("test.img").unwrap();
//! // ...
//! ld.detach().unwrap();
//! ```

extern crate libc;

use std::fs::File;
use std::fs::OpenOptions;

use libc::{c_int, ioctl};
use std::default::Default;
use std::io;
use std::os::unix::prelude::*;
use std::path::{Path, PathBuf};

// TODO support missing operations
const LOOP_SET_FD: u16 = 0x4C00;
const LOOP_CLR_FD: u16 = 0x4C01;
const LOOP_SET_STATUS64: u16 = 0x4C04;
//const LOOP_GET_STATUS64: u16 = 0x4C05;
const LOOP_SET_CAPACITY: u16 = 0x4C07;
//const LOOP_SET_DIRECT_IO: u16 = 0x4C08;
//const LOOP_SET_BLOCK_SIZE: u16 = 0x4C09;

//const LOOP_CTL_ADD: u16 = 0x4C80;
//const LOOP_CTL_REMOVE: u16 = 0x4C81;
const LOOP_CTL_GET_FREE: u16 = 0x4C82;

const LOOP_CONTROL: &str = "/dev/loop-control";
const LOOP_PREFIX: &str = "/dev/loop";

/// Interface to the loop control device: `/dev/loop-control`.
#[derive(Debug)]
pub struct LoopControl {
    dev_file: File,
}

impl LoopControl {
    /// Opens the loop control device.
    pub fn open() -> io::Result<Self> {
        Ok(Self {
            dev_file: OpenOptions::new()
                .read(true)
                .write(true)
                .open(LOOP_CONTROL)?,
        })
    }

    /// Finds and opens the next available loop device.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use loopdev::LoopControl;
    /// let lc = LoopControl::open().unwrap();
    /// let ld = lc.next_free().unwrap();
    /// println!("{}", ld.path().unwrap().display());
    /// ```
    pub fn next_free(&self) -> io::Result<LoopDevice> {
        let dev_num = ioctl_to_error(unsafe {
            ioctl(self.dev_file.as_raw_fd() as c_int, LOOP_CTL_GET_FREE.into())
        })?;
        LoopDevice::open(&format!("{}{}", LOOP_PREFIX, dev_num))
    }
}

/// Interface to a loop device ie `/dev/loop0`.
#[derive(Debug)]
pub struct LoopDevice {
    device: File,
}

impl AsRawFd for LoopDevice {
    fn as_raw_fd(&self) -> RawFd {
        self.device.as_raw_fd()
    }
}

impl LoopDevice {
    /// Opens a loop device.
    pub fn open<P: AsRef<Path>>(dev: P) -> io::Result<Self> {
        // TODO create dev if it does not exist and begins with LOOP_PREFIX
        Ok(Self {
            device: OpenOptions::new().read(true).write(true).open(dev)?,
        })
    }

    /// Attach the loop device to a file starting at offset into the file.
    #[deprecated(
        since = "0.2.0",
        note = "use `attach_file` or `attach_with_offset` instead"
    )]
    pub fn attach<P: AsRef<Path>>(&self, backing_file: P, offset: u64) -> io::Result<()> {
        self.attach_with_sizelimit(backing_file, offset, 0)
    }

    /// Attach the loop device to a file that maps to the whole file.
    ///
    /// # Examples
    ///
    /// Attach the device to a file.
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let ld = LoopDevice::open("/dev/loop4").unwrap();
    /// ld.attach_file("test.img").unwrap();
    /// # ld.detach().unwrap();
    /// ```
    pub fn attach_file<P: AsRef<Path>>(&self, backing_file: P) -> io::Result<()> {
        self.attach_with_sizelimit(backing_file, 0, 0)
    }

    /// Attach the loop device to a file starting at offset into the file.
    ///
    /// # Examples
    ///
    /// Attach the device to the start of a file.
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let ld = LoopDevice::open("/dev/loop5").unwrap();
    /// ld.attach_with_offset("test.img", 0).unwrap();
    /// # ld.detach().unwrap();
    /// ```
    pub fn attach_with_offset<P: AsRef<Path>>(
        &self,
        backing_file: P,
        offset: u64,
    ) -> io::Result<()> {
        self.attach_with_sizelimit(backing_file, offset, 0)
    }

    /// Attach the loop device to a file starting at offset into the file and a the given sizelimit.
    ///
    /// # Examples
    ///
    /// Attach the device to the start of a file with a maximum size of 1024 bytes.
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let ld = LoopDevice::open("/dev/loop6").unwrap();
    /// ld.attach_with_sizelimit("test.img", 0, 1024).unwrap();
    /// # ld.detach().unwrap();
    /// ```
    pub fn attach_with_sizelimit<P: AsRef<Path>>(
        &self,
        backing_file: P,
        offset: u64,
        sizelimit: u64,
    ) -> io::Result<()> {
        // Set offset for backing_file
        let mut info = LoopInfo64::default();
        info.lo_offset = offset;
        info.lo_sizelimit = sizelimit;

        Self::attach_with_loop_info(self, backing_file, info)
    }

    /// Attach the loop device with automatic partition scanning.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let ld = LoopDevice::open("/dev/loop5").unwrap();
    /// ld.attach_with_partscan("test.img").unwrap();
    /// # ld.detach().unwrap();
    /// ```
    pub fn attach_with_partscan<P: AsRef<Path>>(&self, backing_file: P) -> io::Result<()> {
        // Set lo_flags to LO_FLAGS_PARTSCAN value
        let mut info = LoopInfo64::default();
        info.lo_flags = 8;

        Self::attach_with_loop_info(self, backing_file, info)
    }

    /// Attach the loop device to a file with loop_info.
    fn attach_with_loop_info<P: AsRef<Path>>(
        &self,
        backing_file: P,
        loop_info: LoopInfo64,
    ) -> io::Result<()> {
        let bf = OpenOptions::new()
            .read(true)
            .write(true)
            .open(backing_file)?;

        // Attach the file
        ioctl_to_error(unsafe {
            ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_SET_FD.into(),
                bf.as_raw_fd() as c_int,
            )
        })?;

        if let Err(err) = ioctl_to_error(unsafe {
            ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_SET_STATUS64.into(),
                &loop_info,
            )
        }) {
            // Ignore the error to preserve the original error
            let _ = self.detach();
            return Err(err);
        }
        Ok(())
    }

    /// Get the path of the loop device.
    #[deprecated(since = "0.2.0", note = "use `path` instead")]
    pub fn get_path(&self) -> Option<PathBuf> {
        self.path()
    }

    /// Get the path of the loop device.
    pub fn path(&self) -> Option<PathBuf> {
        let mut p = PathBuf::from("/proc/self/fd");
        p.push(self.device.as_raw_fd().to_string());
        std::fs::read_link(&p).ok()
    }

    /// Detach a loop device from its backing file.
    ///
    /// Note that the device won't fully detach until a short delay after the underling device file
    /// gets closed. This happens when LoopDev goes out of scope so you should ensure the LoopDev
    /// lives for a short a time as possible.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let ld = LoopDevice::open("/dev/loop7").unwrap();
    /// # ld.attach_file("test.img").unwrap();
    /// ld.detach().unwrap();
    /// ```
    pub fn detach(&self) -> io::Result<()> {
        ioctl_to_error(unsafe { ioctl(self.device.as_raw_fd() as c_int, LOOP_CLR_FD.into(), 0) })?;
        Ok(())
    }

    /// Resize a live loop device. If the size of the backing file changes this can be called to
    /// inform the loop driver about the new size.
    pub fn set_capacity(&self) -> io::Result<()> {
        ioctl_to_error(unsafe {
            ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_SET_CAPACITY.into(),
                0,
            )
        })?;
        Ok(())
    }
}

// https://man7.org/linux/man-pages/man4/loop.4.html
#[repr(C)]
struct LoopInfo64 {
    pub lo_device: u64,           // ioctl r/o
    pub lo_inode: u64,            // ioctl r/o
    pub lo_rdevice: u64,          // ioctl r/o
    pub lo_offset: u64,           //
    pub lo_sizelimit: u64,        // bytes, 0 == max available
    pub lo_number: u32,           // ioctl r/o
    pub lo_encrypt_type: u32,     //
    pub lo_encrypt_key_size: u32, // ioctl w/o
    pub lo_flags: u32,            // ioctl r/w (r/o before Linux 2.6.25)
    pub lo_file_name: [u8; 64],   //
    pub lo_crypt_name: [u8; 64],  //
    pub lo_encrypt_key: [u8; 32], // ioctl w/o
    pub lo_init: [u64; 2],        //
}

impl Default for LoopInfo64 {
    fn default() -> Self {
        Self {
            lo_device: 0,
            lo_inode: 0,
            lo_rdevice: 0,
            lo_offset: 0,
            lo_sizelimit: 0,
            lo_number: 0,
            lo_encrypt_type: 0,
            lo_encrypt_key_size: 0,
            lo_flags: 0,
            lo_file_name: [0; 64],
            lo_crypt_name: [0; 64],
            lo_encrypt_key: [0; 32],
            lo_init: [0; 2],
        }
    }
}

fn ioctl_to_error(ret: i32) -> io::Result<i32> {
    if ret < 0 {
        Err(io::Error::last_os_error())
    } else {
        Ok(ret)
    }
}
<|endoftext|>