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

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

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

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

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

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

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

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

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

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

    #[test]
    fn compact_unicode_utf8() {
        assert_eq!(
            "[\"a\\\\B\\\"\"]",
            compact("[\"\\u0061\\u005C\\u0042\\u0022\"]")
        );
        assert_eq!("[\"\u{120}\"]", compact("[\"\\u0120\"]"));
        assert_eq!("[\"\u{FFF}\"]", compact("[\"\\u0FFF\"]"));
        assert_eq!("[\"\u{1820}\"]", compact("[\"\\u1820\"]"));
        assert_eq!("[\"\u{FFFF}\"]", compact("[\"\\uFFFF\"]"));
        assert_eq!("[\"\u{20820}\"]", compact("[\"\\uD842\\uDC20\"]"));
        assert_eq!("[\"\u{10FFFF}\"]", compact("[\"\\uDBFF\\uDFFF\"]"));
    }
}
<commit_msg>Return a Result<(),()> rather than a bool<commit_after>/* Copyright 2016 Mark Haines
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
use readhex::*;

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

pub fn compact_vector(input: &[u8], output: &mut Vec<u8>) -> Result<(),()> {
    if compact_vector_(input, output) {
        Ok(())
    } else {
        Err(())
    }
}

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

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

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

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

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

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

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

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

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


<commit_before>use libc;
use std;

#[repr(C)]
pub struct ContentRef {
  opaque: *mut libc::c_void
}

pub trait Content {
  fn as_content(&self) -> *mut libc::c_void;

  fn get_preferred_size(&mut self) -> (bool, f32, f32) {
    unsafe {
      let mut width:f32 = std::intrinsics::init();
      let mut height:f32 = std::intrinsics::init();
      let foreign_result = clutter_content_get_preferred_size(self.as_content(), &mut width, &mut height);
      return (foreign_result != 0, width, height);
    }
  }

  fn invalidate(&mut self) {
    unsafe {
      clutter_content_invalidate(self.as_content());
    }
  }
}

impl Content for ContentRef {
  fn as_content(&self) -> *mut libc::c_void {
    return self.opaque;
  }
}

extern {
  fn clutter_content_get_preferred_size(self_value: *mut libc::c_void, width: *mut f32, height: *mut f32) -> i32;
  fn clutter_content_invalidate(self_value: *mut libc::c_void);
}

<commit_msg>Document content mod<commit_after>#![stable]

use libc;
use std;

/// Opaque struct which holds a reference to the underlying Clutter object.
#[repr(C)]
pub struct ContentRef {
  opaque: *mut libc::c_void
}

/// Delegate for painting the content of an actor
///
/// Content is an interface to implement types responsible for painting the
/// content of an Actor.
///
/// Multiple actors can use the same Content instance, in order to share the
/// resources associated with painting the same content.
///
/// _Since 1.10_
pub trait Content {
  /// Returns a pointer to the the underlying C object.
  ///
  /// Generally only used internally.
  fn as_content(&self) -> *mut libc::c_void;

  /// Retrieves the natural size of the content, if any.
  ///
  /// The natural size of a Content is defined as the size the content would
  /// have regardless of the allocation of the actor that is painting it, for
  /// instance the size of an image data.
  ///
  /// _Since 1.10_
  fn get_preferred_size(&mut self) -> (bool, f32, f32) {
    unsafe {
      let mut width:f32 = std::intrinsics::init();
      let mut height:f32 = std::intrinsics::init();
      let foreign_result = clutter_content_get_preferred_size(self.as_content(), &mut width, &mut height);
      return (foreign_result != 0, width, height);
    }
  }

  /// Invalidates a Content.
  ///
  /// This function should be called by Content implementations when they change
  /// the way the content should be painted regardless of the actor state.
  ///
  /// _Since 1.10_
  fn invalidate(&mut self) {
    unsafe {
      clutter_content_invalidate(self.as_content());
    }
  }
}

impl Content for ContentRef {
  fn as_content(&self) -> *mut libc::c_void {
    return self.opaque;
  }
}

extern {
  fn clutter_content_get_preferred_size(self_value: *mut libc::c_void, width: *mut f32, height: *mut f32) -> i32;
  fn clutter_content_invalidate(self_value: *mut libc::c_void);
}
<|endoftext|>


<commit_before>use std::fmt;
/// We re-export winit so it's easy for people to use the same version as we are
/// without having to mess around figuring it out.
pub use winit;

use crate::audio;
use crate::conf;
use crate::error::GameResult;
use crate::event::winit_event;
use crate::filesystem::Filesystem;
use crate::graphics::{self, Point2};
use crate::input::{gamepad, keyboard, mouse};
use crate::timer;

/// A `Context` is an object that holds on to global resources.
/// It basically tracks hardware state such as the screen, audio
/// system, timers, and so on.  Generally this type is **not** thread-
/// safe and only one `Context` can exist at a time.  Trying to create
/// another one will fail.
///
/// Most functions that interact with the hardware, for instance
/// drawing things, playing sounds, or loading resources (which then
/// need to be transformed into a format the hardware likes) will need
/// to access the `Context`.
///
/// All fields in this struct are basically undocumented features, only
/// here to make it easier to debug,
/// or to let advanced users hook into the guts of ggez and make it
/// do things it normally can't.
/// Most users shouldn't touch these things directly, since
/// implementation details may change without warning.  The
/// public and stable API is `ggez`'s module-level functions and types.
pub struct Context {
    /// Filesystem state
    pub filesystem: Filesystem,
    /// Graphics state
    pub(crate) gfx_context: crate::graphics::context::GraphicsContext,
    /// Timer state
    pub timer_context: timer::TimeContext,
    /// Audio context
    pub audio_context: Box<dyn audio::AudioContext>,
    /// Keyboard context
    pub keyboard_context: keyboard::KeyboardContext,
    /// Mouse context
    pub mouse_context: mouse::MouseContext,
    /// Gamepad context
    pub gamepad_context: Box<dyn gamepad::GamepadContext>,

    /// The Conf object the Context was created with.
    /// It's here just so that we can see the original settings,
    /// updating it will have no effect.
    pub conf: conf::Conf,
    /// Controls whether or not the event loop should be running.
    /// Set this with `ggez::quit()`.
    pub continuing: bool,

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

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

impl Context {
    /// Tries to create a new Context using settings from the given [`Conf`](../conf/struct.Conf.html) object.
    /// Usually called by [`ContextBuilder::build()`](struct.ContextBuilder.html#method.build).
    fn from_conf(conf: conf::Conf, mut fs: Filesystem) -> GameResult<(Context, winit::EventsLoop)> {
        let debug_id = DebugId::new();
        let audio_context: Box<dyn audio::AudioContext> = if conf.modules.audio {
            Box::new(audio::RodioAudioContext::new()?)
        } else {
            Box::new(audio::NullAudioContext::default())
        };
        let events_loop = winit::EventsLoop::new();
        let timer_context = timer::TimeContext::new();
        let backend_spec = graphics::GlBackendSpec::from(conf.backend);
        let graphics_context = graphics::context::GraphicsContext::new(
            &mut fs,
            &events_loop,
            &conf.window_setup,
            conf.window_mode,
            backend_spec,
            debug_id,
        )?;
        let mouse_context = mouse::MouseContext::new();
        let keyboard_context = keyboard::KeyboardContext::new();
        let gamepad_context: Box<dyn gamepad::GamepadContext> = if conf.modules.gamepad {
            Box::new(gamepad::GilrsGamepadContext::new()?)
        } else {
            Box::new(gamepad::NullGamepadContext::default())
        };

        let ctx = Context {
            conf,
            filesystem: fs,
            gfx_context: graphics_context,
            continuing: true,
            timer_context,
            audio_context,
            keyboard_context,
            gamepad_context,
            mouse_context,

            debug_id,
        };

        Ok((ctx, events_loop))
    }

    /// Feeds an `Event` into the `Context` so it can update any internal
    /// state it needs to, such as detecting window resizes.  If you are
    /// rolling your own event loop, you should call this on the events
    /// you receive before processing them yourself.
    ///
    /// TODO: Resolve hidpi mongling in #391.
    /// This also returns a new version of the `Event` that has been modified
    /// for ggez's optional overriding of hidpi.  For full discussion see
    /// <https://github.com/tomaka/winit/issues/591#issuecomment-403096230>.
    pub fn process_event(&mut self, event: winit::Event) -> winit::Event {
        match event.clone() {
            winit_event::Event::WindowEvent { event, .. } => match event {
                winit_event::WindowEvent::Resized(_) => {
                    self.gfx_context.resize_viewport();
                }
                winit_event::WindowEvent::CursorMoved {
                    position: logical_position,
                    ..
                } => {
                    self.mouse_context.set_last_position(Point2::new(
                        logical_position.x as f32,
                        logical_position.y as f32,
                    ));
                }
                winit_event::WindowEvent::MouseInput { button, state, .. } => {
                    let pressed = match state {
                        winit_event::ElementState::Pressed => true,
                        winit_event::ElementState::Released => false,
                    };
                    self.mouse_context.set_button(button, pressed);
                }
                winit_event::WindowEvent::KeyboardInput {
                    input:
                        winit::KeyboardInput {
                            state,
                            virtual_keycode: Some(keycode),
                            modifiers,
                            ..
                        },
                    ..
                } => {
                    let pressed = match state {
                        winit_event::ElementState::Pressed => true,
                        winit_event::ElementState::Released => false,
                    };
                    self.keyboard_context
                        .set_modifiers(keyboard::KeyMods::from(modifiers));
                    self.keyboard_context.set_key(keycode, pressed);
                }
                winit_event::WindowEvent::HiDpiFactorChanged(_) => {
                    // Nope.
                }
                _ => (),
            },
            winit_event::Event::DeviceEvent { event, .. } => match event {
                winit_event::DeviceEvent::MouseMotion { delta: (x, y) } => {
                    self.mouse_context.set_last_delta(Point2::new(
                        x as f32,
                        y as f32,
                    ));
                }
                _ => (),
            },

            _ => (),
        };
        event
    }
}

use std::path;

/// A builder object for creating a [`Context`](struct.Context.html).
#[derive(Debug, Clone, PartialEq)]
pub struct ContextBuilder {
    pub(crate) game_id: &'static str,
    pub(crate) author: &'static str,
    pub(crate) conf: conf::Conf,
    pub(crate) paths: Vec<path::PathBuf>,
    pub(crate) load_conf_file: bool,
}

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

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

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

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

    /// Sets the modules configuration.
    pub fn modules(mut self, modules: conf::ModuleConf) -> Self {
        self.conf.modules = modules;
        self
    }

    /// Sets all the config options, overriding any previous
    /// ones from [`window_setup()`](#method.window_setup),
    /// [`window_mode()`](#method.window_mode), and
    /// [`backend()`](#method.backend).
    pub fn conf(mut self, conf: conf::Conf) -> Self {
        self.conf = conf;
        self
    }

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

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

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

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

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

        Context::from_conf(config, fs)
    }
}

/// Terminates the [`ggez::run()`](fn.run.html) loop by setting
/// [`Context.continuing`](struct.Context.html#structfield.continuing)
/// to `false`.
pub fn quit(ctx: &mut Context) {
    ctx.continuing = false;
}

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

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

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

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

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

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

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

    pub fn assert(&self, _ctx: &Context) {
        // Do nothing.
    }
}
<commit_msg>Add window.resize() call.<commit_after>use std::fmt;
/// We re-export winit so it's easy for people to use the same version as we are
/// without having to mess around figuring it out.
pub use winit;

use crate::audio;
use crate::conf;
use crate::error::GameResult;
use crate::event::winit_event;
use crate::filesystem::Filesystem;
use crate::graphics::{self, Point2};
use crate::input::{gamepad, keyboard, mouse};
use crate::timer;

/// A `Context` is an object that holds on to global resources.
/// It basically tracks hardware state such as the screen, audio
/// system, timers, and so on.  Generally this type is **not** thread-
/// safe and only one `Context` can exist at a time.  Trying to create
/// another one will fail.
///
/// Most functions that interact with the hardware, for instance
/// drawing things, playing sounds, or loading resources (which then
/// need to be transformed into a format the hardware likes) will need
/// to access the `Context`.
///
/// All fields in this struct are basically undocumented features, only
/// here to make it easier to debug,
/// or to let advanced users hook into the guts of ggez and make it
/// do things it normally can't.
/// Most users shouldn't touch these things directly, since
/// implementation details may change without warning.  The
/// public and stable API is `ggez`'s module-level functions and types.
pub struct Context {
    /// Filesystem state
    pub filesystem: Filesystem,
    /// Graphics state
    pub(crate) gfx_context: crate::graphics::context::GraphicsContext,
    /// Timer state
    pub timer_context: timer::TimeContext,
    /// Audio context
    pub audio_context: Box<dyn audio::AudioContext>,
    /// Keyboard context
    pub keyboard_context: keyboard::KeyboardContext,
    /// Mouse context
    pub mouse_context: mouse::MouseContext,
    /// Gamepad context
    pub gamepad_context: Box<dyn gamepad::GamepadContext>,

    /// The Conf object the Context was created with.
    /// It's here just so that we can see the original settings,
    /// updating it will have no effect.
    pub conf: conf::Conf,
    /// Controls whether or not the event loop should be running.
    /// Set this with `ggez::quit()`.
    pub continuing: bool,

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

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

impl Context {
    /// Tries to create a new Context using settings from the given [`Conf`](../conf/struct.Conf.html) object.
    /// Usually called by [`ContextBuilder::build()`](struct.ContextBuilder.html#method.build).
    fn from_conf(conf: conf::Conf, mut fs: Filesystem) -> GameResult<(Context, winit::EventsLoop)> {
        let debug_id = DebugId::new();
        let audio_context: Box<dyn audio::AudioContext> = if conf.modules.audio {
            Box::new(audio::RodioAudioContext::new()?)
        } else {
            Box::new(audio::NullAudioContext::default())
        };
        let events_loop = winit::EventsLoop::new();
        let timer_context = timer::TimeContext::new();
        let backend_spec = graphics::GlBackendSpec::from(conf.backend);
        let graphics_context = graphics::context::GraphicsContext::new(
            &mut fs,
            &events_loop,
            &conf.window_setup,
            conf.window_mode,
            backend_spec,
            debug_id,
        )?;
        let mouse_context = mouse::MouseContext::new();
        let keyboard_context = keyboard::KeyboardContext::new();
        let gamepad_context: Box<dyn gamepad::GamepadContext> = if conf.modules.gamepad {
            Box::new(gamepad::GilrsGamepadContext::new()?)
        } else {
            Box::new(gamepad::NullGamepadContext::default())
        };

        let ctx = Context {
            conf,
            filesystem: fs,
            gfx_context: graphics_context,
            continuing: true,
            timer_context,
            audio_context,
            keyboard_context,
            gamepad_context,
            mouse_context,

            debug_id,
        };

        Ok((ctx, events_loop))
    }

    /// Feeds an `Event` into the `Context` so it can update any internal
    /// state it needs to, such as detecting window resizes.  If you are
    /// rolling your own event loop, you should call this on the events
    /// you receive before processing them yourself.
    ///
    /// TODO: Resolve hidpi mongling in #391.
    /// This also returns a new version of the `Event` that has been modified
    /// for ggez's optional overriding of hidpi.  For full discussion see
    /// <https://github.com/tomaka/winit/issues/591#issuecomment-403096230>.
    pub fn process_event(&mut self, event: winit::Event) -> winit::Event {
        match event.clone() {
            winit_event::Event::WindowEvent { event, .. } => match event {
                winit_event::WindowEvent::Resized(logical_size) => {
                    let hidpi_factor = self.gfx_context.window.get_hidpi_factor();
                    let physical_size = logical_size.to_physical(hidpi_factor as f64);
                    self.gfx_context.window.resize(physical_size);
                    self.gfx_context.resize_viewport();
                }
                winit_event::WindowEvent::CursorMoved {
                    position: logical_position,
                    ..
                } => {
                    self.mouse_context.set_last_position(Point2::new(
                        logical_position.x as f32,
                        logical_position.y as f32,
                    ));
                }
                winit_event::WindowEvent::MouseInput { button, state, .. } => {
                    let pressed = match state {
                        winit_event::ElementState::Pressed => true,
                        winit_event::ElementState::Released => false,
                    };
                    self.mouse_context.set_button(button, pressed);
                }
                winit_event::WindowEvent::KeyboardInput {
                    input:
                        winit::KeyboardInput {
                            state,
                            virtual_keycode: Some(keycode),
                            modifiers,
                            ..
                        },
                    ..
                } => {
                    let pressed = match state {
                        winit_event::ElementState::Pressed => true,
                        winit_event::ElementState::Released => false,
                    };
                    self.keyboard_context
                        .set_modifiers(keyboard::KeyMods::from(modifiers));
                    self.keyboard_context.set_key(keycode, pressed);
                }
                winit_event::WindowEvent::HiDpiFactorChanged(_) => {
                    // Nope.
                }
                _ => (),
            },
            winit_event::Event::DeviceEvent { event, .. } => match event {
                winit_event::DeviceEvent::MouseMotion { delta: (x, y) } => {
                    self.mouse_context.set_last_delta(Point2::new(
                        x as f32,
                        y as f32,
                    ));
                }
                _ => (),
            },

            _ => (),
        };
        event
    }
}

use std::path;

/// A builder object for creating a [`Context`](struct.Context.html).
#[derive(Debug, Clone, PartialEq)]
pub struct ContextBuilder {
    pub(crate) game_id: &'static str,
    pub(crate) author: &'static str,
    pub(crate) conf: conf::Conf,
    pub(crate) paths: Vec<path::PathBuf>,
    pub(crate) load_conf_file: bool,
}

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

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

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

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

    /// Sets the modules configuration.
    pub fn modules(mut self, modules: conf::ModuleConf) -> Self {
        self.conf.modules = modules;
        self
    }

    /// Sets all the config options, overriding any previous
    /// ones from [`window_setup()`](#method.window_setup),
    /// [`window_mode()`](#method.window_mode), and
    /// [`backend()`](#method.backend).
    pub fn conf(mut self, conf: conf::Conf) -> Self {
        self.conf = conf;
        self
    }

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

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

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

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

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

        Context::from_conf(config, fs)
    }
}

/// Terminates the [`ggez::run()`](fn.run.html) loop by setting
/// [`Context.continuing`](struct.Context.html#structfield.continuing)
/// to `false`.
pub fn quit(ctx: &mut Context) {
    ctx.continuing = false;
}

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

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

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

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

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

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

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

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


<commit_before>use std::collections::{HashMap, VecDeque};

use serde::Serialize;
use serde_json::value::{to_value, Map, Value as Json};

use crate::block::{BlockContext, BlockParamHolder};
use crate::error::RenderError;
use crate::grammar::Rule;
use crate::json::path::*;
use crate::json::value::ScopedJson;

pub type Object = HashMap<String, Json>;

/// The context wrap data you render on your templates.
///
#[derive(Debug, Clone)]
pub struct Context {
    data: Json,
}

pub enum ResolvedPath {
    // FIXME: change to borrowed when possible
    // full path
    AbsolutePath(Vec<String>),
    // relative path and path root
    RelativePath(Vec<String>),
    // relative path against block param value
    BlockParamValue(Vec<String>, Json),
}

impl ResolvedPath {
    fn full_path(self) -> Option<Vec<String>> {
        match self {
            ResolvedPath::AbsolutePath(p) => Some(p),
            _ => None,
        }
    }
}

fn parse_json_visitor<'reg: 'rc, 'rc>(
    relative_path: &[PathSeg],
    block_contexts: &VecDeque<BlockContext<'reg, 'rc>>,
    always_for_absolute_path: bool,
) -> Result<ResolvedPath, RenderError> {
    let mut path_context_depth: i64 = -1;
    let mut with_block_param = None;
    let mut from_root = false;

    // peek relative_path for block param, @root and  "../../"
    for path_seg in relative_path {
        match path_seg {
            PathSeg::Named(the_path) => {
                if let Some(holder) = get_in_block_params(&block_contexts, the_path) {
                    with_block_param = Some(holder);
                }
                break;
            }
            PathSeg::Ruled(the_rule) => match the_rule {
                Rule::path_root => {
                    from_root = true;
                    break;
                }
                Rule::path_up => path_context_depth += 1,
                _ => break,
            },
        }
    }

    let mut path_stack = Vec::with_capacity(relative_path.len() + 5);
    match with_block_param {
        Some(BlockParamHolder::Value(ref value)) => {
            merge_json_path(&mut path_stack, &relative_path[1..]);
            Ok(ResolvedPath::BlockParamValue(path_stack, value.clone()))
        }
        Some(BlockParamHolder::Path(ref paths)) => {
            // TODO: if this path equals base_path, skip this step and make it a ResolvedPath::RelativePath
            path_stack.extend_from_slice(paths);
            merge_json_path(&mut path_stack, &relative_path[1..]);

            Ok(ResolvedPath::AbsolutePath(path_stack))
        }
        None => {
            if path_context_depth >= 0 {
                if let Some(ref context_base_path) = block_contexts
                    .get(path_context_depth as usize)
                    .map(|blk| blk.local_path_root())
                {
                    path_stack.extend_from_slice(context_base_path);
                } else {
                    // TODO: is this correct behaviour?
                    if let Some(ref base_path) = block_contexts.front().map(|blk| blk.base_path()) {
                        path_stack.extend_from_slice(base_path);
                    }
                }
                merge_json_path(&mut path_stack, relative_path);
                Ok(ResolvedPath::AbsolutePath(path_stack))
            } else if from_root {
                merge_json_path(&mut path_stack, relative_path);
                Ok(ResolvedPath::AbsolutePath(path_stack))
            } else {
                if always_for_absolute_path {
                    if let Some(ref base_path) = block_contexts.front().map(|blk| blk.base_path()) {
                        path_stack.extend_from_slice(base_path);
                    }
                    merge_json_path(&mut path_stack, relative_path);
                    Ok(ResolvedPath::AbsolutePath(path_stack))
                } else {
                    merge_json_path(&mut path_stack, relative_path);
                    Ok(ResolvedPath::RelativePath(path_stack))
                }
            }
        }
    }
}

fn get_data<'a>(d: Option<&'a Json>, p: &str) -> Result<Option<&'a Json>, RenderError> {
    if p == "this" {
        return Ok(d);
    }

    let result = match d {
        Some(&Json::Array(ref l)) => p
            .parse::<usize>()
            .map_err(RenderError::with)
            .map(|idx_u| l.get(idx_u))?,
        Some(&Json::Object(ref m)) => m.get(p),
        Some(_) => None,
        None => None,
    };
    Ok(result)
}

fn get_in_block_params<'a>(
    block_contexts: &'a VecDeque<BlockContext>,
    p: &str,
) -> Option<&'a BlockParamHolder> {
    for bc in block_contexts {
        let v = bc.get_block_param(p);
        if v.is_some() {
            return v;
        }
    }

    None
}

pub(crate) fn merge_json(base: &Json, addition: &HashMap<&&str, &Json>) -> Json {
    let mut base_map = match base {
        Json::Object(ref m) => m.clone(),
        _ => Map::new(),
    };

    for (k, v) in addition.iter() {
        base_map.insert((*(*k)).to_string(), (*v).clone());
    }

    Json::Object(base_map)
}

impl Context {
    /// Create a context with null data
    pub fn null() -> Context {
        Context { data: Json::Null }
    }

    /// Create a context with given data
    pub fn wraps<T: Serialize>(e: T) -> Result<Context, RenderError> {
        to_value(e)
            .map_err(RenderError::from)
            .map(|d| Context { data: d })
    }

    /// Navigate the context with base path and relative path
    /// Typically you will set base path to `RenderContext.get_path()`
    /// and set relative path to helper argument or so.
    ///
    /// If you want to navigate from top level, set the base path to `"."`
    pub(crate) fn navigate<'reg, 'rc>(
        &'rc self,
        relative_path: &[PathSeg],
        block_contexts: &VecDeque<BlockContext<'reg, 'rc>>,
    ) -> Result<ScopedJson<'reg, 'rc>, RenderError> {
        // always use absolute at the moment until we get base_value lifetime issue fixed
        let resolved_visitor = parse_json_visitor(&relative_path, block_contexts, true)?;

        match resolved_visitor {
            ResolvedPath::AbsolutePath(paths) => {
                let mut ptr = Some(self.data());
                for p in paths.iter() {
                    ptr = get_data(ptr, p)?;
                }

                let path_root = if !relative_path.is_empty() {
                    parse_json_visitor(&relative_path[..1], block_contexts, true)?.full_path()
                } else {
                    None
                };

                Ok(ptr
                    .map(|v| ScopedJson::Context(v, paths, path_root))
                    .unwrap_or_else(|| ScopedJson::Missing))
            }
            ResolvedPath::RelativePath(_paths) => {
                // relative path is disabled for now
                unreachable!()
                // let mut ptr = block_contexts.front().and_then(|blk| blk.base_value());
                // for p in paths.iter() {
                //     ptr = get_data(ptr, p)?;
                // }

                // let path_root = if !relative_path.is_empty() {
                //     parse_json_visitor(&relative_path[..1], block_contexts, true)?.full_path()
                // } else {
                //     None
                // };

                // Ok(ptr
                //     .map(|v| ScopedJson::Context(v, paths, path_root))
                //     .unwrap_or_else(|| ScopedJson::Missing))
            }
            ResolvedPath::BlockParamValue(paths, value) => {
                let mut ptr = Some(&value);
                for p in paths.iter() {
                    ptr = get_data(ptr, p)?;
                }
                Ok(ptr
                    .map(|v| ScopedJson::Derived(v.clone()))
                    .unwrap_or_else(|| ScopedJson::Missing))
            }
        }
    }

    pub fn data(&self) -> &Json {
        &self.data
    }

    pub fn data_mut(&mut self) -> &mut Json {
        &mut self.data
    }
}

fn join(segs: &VecDeque<&str>, sep: &str) -> String {
    let mut out = String::new();
    let mut iter = segs.iter();
    if let Some(fst) = iter.next() {
        out.push_str(fst);
        for elt in iter {
            out.push_str(sep);
            out.push_str(elt);
        }
    }
    out
}

#[cfg(test)]
mod test {
    use crate::context::{self, BlockParams, Context};
    use crate::error::RenderError;
    use crate::json::path::PathSeg;
    use crate::json::value::{self, ScopedJson};
    use serde_json::value::Map;
    use std::collections::{HashMap, VecDeque};

    fn navigate_from_root<'reg, 'rc>(
        ctx: &'rc Context,
        path: &str,
    ) -> Result<ScopedJson<'reg, 'rc>, RenderError> {
        let relative_path = PathSeg::parse(path).unwrap();
        ctx.navigate(
            &Vec::new(),
            &VecDeque::new(),
            &relative_path,
            &VecDeque::new(),
        )
    }

    #[derive(Serialize)]
    struct Address {
        city: String,
        country: String,
    }

    #[derive(Serialize)]
    struct Person {
        name: String,
        age: i16,
        addr: Address,
        titles: Vec<String>,
    }

    #[test]
    fn test_render() {
        let v = "hello";
        let ctx = Context::wraps(&v.to_string()).unwrap();
        assert_eq!(
            navigate_from_root(&ctx, "this").unwrap().render(),
            v.to_string()
        );
    }

    #[test]
    fn test_navigation() {
        let addr = Address {
            city: "Beijing".to_string(),
            country: "China".to_string(),
        };

        let person = Person {
            name: "Ning Sun".to_string(),
            age: 27,
            addr,
            titles: vec!["programmer".to_string(), "cartographer".to_string()],
        };

        let ctx = Context::wraps(&person).unwrap();
        assert_eq!(
            navigate_from_root(&ctx, "./addr/country").unwrap().render(),
            "China".to_string()
        );
        assert_eq!(
            navigate_from_root(&ctx, "addr.[country]").unwrap().render(),
            "China".to_string()
        );

        let v = true;
        let ctx2 = Context::wraps(&v).unwrap();
        assert_eq!(
            navigate_from_root(&ctx2, "this").unwrap().render(),
            "true".to_string()
        );

        assert_eq!(
            navigate_from_root(&ctx, "titles.[0]").unwrap().render(),
            "programmer".to_string()
        );

        assert_eq!(
            navigate_from_root(&ctx, "age").unwrap().render(),
            "27".to_string()
        );
    }

    #[test]
    fn test_this() {
        let mut map_with_this = Map::new();
        map_with_this.insert("this".to_string(), value::to_json("hello"));
        map_with_this.insert("age".to_string(), value::to_json(5usize));
        let ctx1 = Context::wraps(&map_with_this).unwrap();

        let mut map_without_this = Map::new();
        map_without_this.insert("age".to_string(), value::to_json(4usize));
        let ctx2 = Context::wraps(&map_without_this).unwrap();

        assert_eq!(
            navigate_from_root(&ctx1, "this").unwrap().render(),
            "[object]".to_owned()
        );
        assert_eq!(
            navigate_from_root(&ctx2, "age").unwrap().render(),
            "4".to_owned()
        );
    }

    #[test]
    fn test_merge_json() {
        let map = json!({ "age": 4 });
        let s = "hello".to_owned();
        let mut hash = HashMap::new();
        let v = value::to_json("h1");
        hash.insert(&"tag", &v);

        let ctx_a1 = Context::wraps(&context::merge_json(&map, &hash)).unwrap();
        assert_eq!(
            navigate_from_root(&ctx_a1, "age").unwrap().render(),
            "4".to_owned()
        );
        assert_eq!(
            navigate_from_root(&ctx_a1, "tag").unwrap().render(),
            "h1".to_owned()
        );

        let ctx_a2 = Context::wraps(&context::merge_json(&value::to_json(s), &hash)).unwrap();
        assert_eq!(
            navigate_from_root(&ctx_a2, "this").unwrap().render(),
            "[object]".to_owned()
        );
        assert_eq!(
            navigate_from_root(&ctx_a2, "tag").unwrap().render(),
            "h1".to_owned()
        );
    }

    #[test]
    fn test_key_name_with_this() {
        let m = btreemap! {
            "this_name".to_string() => "the_value".to_string()
        };
        let ctx = Context::wraps(&m).unwrap();
        assert_eq!(
            navigate_from_root(&ctx, "this_name").unwrap().render(),
            "the_value".to_string()
        );
    }

    use serde::ser::Error as SerdeError;
    use serde::{Serialize, Serializer};

    struct UnserializableType {}

    impl Serialize for UnserializableType {
        fn serialize<S>(&self, _: S) -> Result<S::Ok, S::Error>
        where
            S: Serializer,
        {
            Err(SerdeError::custom("test"))
        }
    }

    #[test]
    fn test_serialize_error() {
        let d = UnserializableType {};
        assert!(Context::wraps(&d).is_err());
    }

    #[test]
    fn test_root() {
        let m = json!({
            "a" : {
                "b" : {
                    "c" : {
                        "d" : 1
                    }
                }
            },
            "b": 2
        });
        let ctx = Context::wraps(&m).unwrap();
        assert_eq!(
            ctx.navigate(
                &["a".to_owned(), "b".to_owned()],
                &VecDeque::new(),
                &PathSeg::parse("@root/b").unwrap(),
                &VecDeque::new()
            )
            .unwrap()
            .render(),
            "2".to_string()
        );
    }

    #[test]
    fn test_block_params() {
        let m = json!([{
            "a": [1, 2]
        }, {
            "b": [2, 3]
        }]);

        let ctx = Context::wraps(&m).unwrap();
        let mut block_param = BlockParams::new();
        block_param
            .add_path("z", ["0".to_owned(), "a".to_owned()].to_vec())
            .unwrap();
        block_param.add_value("t", json!("good")).unwrap();

        let mut block_params = VecDeque::new();
        block_params.push_front(block_param);

        assert_eq!(
            ctx.navigate(
                &Vec::new(),
                &VecDeque::new(),
                &PathSeg::parse("z.[1]").unwrap(),
                &block_params
            )
            .unwrap()
            .render(),
            "2".to_string()
        );
        assert_eq!(
            ctx.navigate(
                &Vec::new(),
                &VecDeque::new(),
                &PathSeg::parse("t").unwrap(),
                &block_params
            )
            .unwrap()
            .render(),
            "good".to_string()
        );
    }
}
<commit_msg>(fix) clippy warnings<commit_after>use std::collections::{HashMap, VecDeque};

use serde::Serialize;
use serde_json::value::{to_value, Map, Value as Json};

use crate::block::{BlockContext, BlockParamHolder};
use crate::error::RenderError;
use crate::grammar::Rule;
use crate::json::path::*;
use crate::json::value::ScopedJson;

pub type Object = HashMap<String, Json>;

/// The context wrap data you render on your templates.
///
#[derive(Debug, Clone)]
pub struct Context {
    data: Json,
}

pub enum ResolvedPath {
    // FIXME: change to borrowed when possible
    // full path
    AbsolutePath(Vec<String>),
    // relative path and path root
    RelativePath(Vec<String>),
    // relative path against block param value
    BlockParamValue(Vec<String>, Json),
}

impl ResolvedPath {
    fn full_path(self) -> Option<Vec<String>> {
        match self {
            ResolvedPath::AbsolutePath(p) => Some(p),
            _ => None,
        }
    }
}

fn parse_json_visitor<'reg: 'rc, 'rc>(
    relative_path: &[PathSeg],
    block_contexts: &VecDeque<BlockContext<'reg, 'rc>>,
    always_for_absolute_path: bool,
) -> Result<ResolvedPath, RenderError> {
    let mut path_context_depth: i64 = -1;
    let mut with_block_param = None;
    let mut from_root = false;

    // peek relative_path for block param, @root and  "../../"
    for path_seg in relative_path {
        match path_seg {
            PathSeg::Named(the_path) => {
                if let Some(holder) = get_in_block_params(&block_contexts, the_path) {
                    with_block_param = Some(holder);
                }
                break;
            }
            PathSeg::Ruled(the_rule) => match the_rule {
                Rule::path_root => {
                    from_root = true;
                    break;
                }
                Rule::path_up => path_context_depth += 1,
                _ => break,
            },
        }
    }

    let mut path_stack = Vec::with_capacity(relative_path.len() + 5);
    match with_block_param {
        Some(BlockParamHolder::Value(ref value)) => {
            merge_json_path(&mut path_stack, &relative_path[1..]);
            Ok(ResolvedPath::BlockParamValue(path_stack, value.clone()))
        }
        Some(BlockParamHolder::Path(ref paths)) => {
            // TODO: if this path equals base_path, skip this step and make it a ResolvedPath::RelativePath
            path_stack.extend_from_slice(paths);
            merge_json_path(&mut path_stack, &relative_path[1..]);

            Ok(ResolvedPath::AbsolutePath(path_stack))
        }
        None => {
            if path_context_depth >= 0 {
                if let Some(ref context_base_path) = block_contexts
                    .get(path_context_depth as usize)
                    .map(|blk| blk.local_path_root())
                {
                    path_stack.extend_from_slice(context_base_path);
                } else {
                    // TODO: is this correct behaviour?
                    if let Some(ref base_path) = block_contexts.front().map(|blk| blk.base_path()) {
                        path_stack.extend_from_slice(base_path);
                    }
                }
                merge_json_path(&mut path_stack, relative_path);
                Ok(ResolvedPath::AbsolutePath(path_stack))
            } else if from_root {
                merge_json_path(&mut path_stack, relative_path);
                Ok(ResolvedPath::AbsolutePath(path_stack))
            } else if always_for_absolute_path {
                if let Some(ref base_path) = block_contexts.front().map(|blk| blk.base_path()) {
                    path_stack.extend_from_slice(base_path);
                }
                merge_json_path(&mut path_stack, relative_path);
                Ok(ResolvedPath::AbsolutePath(path_stack))
            } else {
                merge_json_path(&mut path_stack, relative_path);
                Ok(ResolvedPath::RelativePath(path_stack))
            }
        }
    }
}

fn get_data<'a>(d: Option<&'a Json>, p: &str) -> Result<Option<&'a Json>, RenderError> {
    if p == "this" {
        return Ok(d);
    }

    let result = match d {
        Some(&Json::Array(ref l)) => p
            .parse::<usize>()
            .map_err(RenderError::with)
            .map(|idx_u| l.get(idx_u))?,
        Some(&Json::Object(ref m)) => m.get(p),
        Some(_) => None,
        None => None,
    };
    Ok(result)
}

fn get_in_block_params<'a>(
    block_contexts: &'a VecDeque<BlockContext>,
    p: &str,
) -> Option<&'a BlockParamHolder> {
    for bc in block_contexts {
        let v = bc.get_block_param(p);
        if v.is_some() {
            return v;
        }
    }

    None
}

pub(crate) fn merge_json(base: &Json, addition: &HashMap<&&str, &Json>) -> Json {
    let mut base_map = match base {
        Json::Object(ref m) => m.clone(),
        _ => Map::new(),
    };

    for (k, v) in addition.iter() {
        base_map.insert((*(*k)).to_string(), (*v).clone());
    }

    Json::Object(base_map)
}

impl Context {
    /// Create a context with null data
    pub fn null() -> Context {
        Context { data: Json::Null }
    }

    /// Create a context with given data
    pub fn wraps<T: Serialize>(e: T) -> Result<Context, RenderError> {
        to_value(e)
            .map_err(RenderError::from)
            .map(|d| Context { data: d })
    }

    /// Navigate the context with base path and relative path
    /// Typically you will set base path to `RenderContext.get_path()`
    /// and set relative path to helper argument or so.
    ///
    /// If you want to navigate from top level, set the base path to `"."`
    pub(crate) fn navigate<'reg, 'rc>(
        &'rc self,
        relative_path: &[PathSeg],
        block_contexts: &VecDeque<BlockContext<'reg, 'rc>>,
    ) -> Result<ScopedJson<'reg, 'rc>, RenderError> {
        // always use absolute at the moment until we get base_value lifetime issue fixed
        let resolved_visitor = parse_json_visitor(&relative_path, block_contexts, true)?;

        match resolved_visitor {
            ResolvedPath::AbsolutePath(paths) => {
                let mut ptr = Some(self.data());
                for p in paths.iter() {
                    ptr = get_data(ptr, p)?;
                }

                let path_root = if !relative_path.is_empty() {
                    parse_json_visitor(&relative_path[..1], block_contexts, true)?.full_path()
                } else {
                    None
                };

                Ok(ptr
                    .map(|v| ScopedJson::Context(v, paths, path_root))
                    .unwrap_or_else(|| ScopedJson::Missing))
            }
            ResolvedPath::RelativePath(_paths) => {
                // relative path is disabled for now
                unreachable!()
                // let mut ptr = block_contexts.front().and_then(|blk| blk.base_value());
                // for p in paths.iter() {
                //     ptr = get_data(ptr, p)?;
                // }

                // let path_root = if !relative_path.is_empty() {
                //     parse_json_visitor(&relative_path[..1], block_contexts, true)?.full_path()
                // } else {
                //     None
                // };

                // Ok(ptr
                //     .map(|v| ScopedJson::Context(v, paths, path_root))
                //     .unwrap_or_else(|| ScopedJson::Missing))
            }
            ResolvedPath::BlockParamValue(paths, value) => {
                let mut ptr = Some(&value);
                for p in paths.iter() {
                    ptr = get_data(ptr, p)?;
                }
                Ok(ptr
                    .map(|v| ScopedJson::Derived(v.clone()))
                    .unwrap_or_else(|| ScopedJson::Missing))
            }
        }
    }

    pub fn data(&self) -> &Json {
        &self.data
    }

    pub fn data_mut(&mut self) -> &mut Json {
        &mut self.data
    }
}

fn join(segs: &VecDeque<&str>, sep: &str) -> String {
    let mut out = String::new();
    let mut iter = segs.iter();
    if let Some(fst) = iter.next() {
        out.push_str(fst);
        for elt in iter {
            out.push_str(sep);
            out.push_str(elt);
        }
    }
    out
}

#[cfg(test)]
mod test {
    use crate::context::{self, BlockParams, Context};
    use crate::error::RenderError;
    use crate::json::path::PathSeg;
    use crate::json::value::{self, ScopedJson};
    use serde_json::value::Map;
    use std::collections::{HashMap, VecDeque};

    fn navigate_from_root<'reg, 'rc>(
        ctx: &'rc Context,
        path: &str,
    ) -> Result<ScopedJson<'reg, 'rc>, RenderError> {
        let relative_path = PathSeg::parse(path).unwrap();
        ctx.navigate(
            &Vec::new(),
            &VecDeque::new(),
            &relative_path,
            &VecDeque::new(),
        )
    }

    #[derive(Serialize)]
    struct Address {
        city: String,
        country: String,
    }

    #[derive(Serialize)]
    struct Person {
        name: String,
        age: i16,
        addr: Address,
        titles: Vec<String>,
    }

    #[test]
    fn test_render() {
        let v = "hello";
        let ctx = Context::wraps(&v.to_string()).unwrap();
        assert_eq!(
            navigate_from_root(&ctx, "this").unwrap().render(),
            v.to_string()
        );
    }

    #[test]
    fn test_navigation() {
        let addr = Address {
            city: "Beijing".to_string(),
            country: "China".to_string(),
        };

        let person = Person {
            name: "Ning Sun".to_string(),
            age: 27,
            addr,
            titles: vec!["programmer".to_string(), "cartographer".to_string()],
        };

        let ctx = Context::wraps(&person).unwrap();
        assert_eq!(
            navigate_from_root(&ctx, "./addr/country").unwrap().render(),
            "China".to_string()
        );
        assert_eq!(
            navigate_from_root(&ctx, "addr.[country]").unwrap().render(),
            "China".to_string()
        );

        let v = true;
        let ctx2 = Context::wraps(&v).unwrap();
        assert_eq!(
            navigate_from_root(&ctx2, "this").unwrap().render(),
            "true".to_string()
        );

        assert_eq!(
            navigate_from_root(&ctx, "titles.[0]").unwrap().render(),
            "programmer".to_string()
        );

        assert_eq!(
            navigate_from_root(&ctx, "age").unwrap().render(),
            "27".to_string()
        );
    }

    #[test]
    fn test_this() {
        let mut map_with_this = Map::new();
        map_with_this.insert("this".to_string(), value::to_json("hello"));
        map_with_this.insert("age".to_string(), value::to_json(5usize));
        let ctx1 = Context::wraps(&map_with_this).unwrap();

        let mut map_without_this = Map::new();
        map_without_this.insert("age".to_string(), value::to_json(4usize));
        let ctx2 = Context::wraps(&map_without_this).unwrap();

        assert_eq!(
            navigate_from_root(&ctx1, "this").unwrap().render(),
            "[object]".to_owned()
        );
        assert_eq!(
            navigate_from_root(&ctx2, "age").unwrap().render(),
            "4".to_owned()
        );
    }

    #[test]
    fn test_merge_json() {
        let map = json!({ "age": 4 });
        let s = "hello".to_owned();
        let mut hash = HashMap::new();
        let v = value::to_json("h1");
        hash.insert(&"tag", &v);

        let ctx_a1 = Context::wraps(&context::merge_json(&map, &hash)).unwrap();
        assert_eq!(
            navigate_from_root(&ctx_a1, "age").unwrap().render(),
            "4".to_owned()
        );
        assert_eq!(
            navigate_from_root(&ctx_a1, "tag").unwrap().render(),
            "h1".to_owned()
        );

        let ctx_a2 = Context::wraps(&context::merge_json(&value::to_json(s), &hash)).unwrap();
        assert_eq!(
            navigate_from_root(&ctx_a2, "this").unwrap().render(),
            "[object]".to_owned()
        );
        assert_eq!(
            navigate_from_root(&ctx_a2, "tag").unwrap().render(),
            "h1".to_owned()
        );
    }

    #[test]
    fn test_key_name_with_this() {
        let m = btreemap! {
            "this_name".to_string() => "the_value".to_string()
        };
        let ctx = Context::wraps(&m).unwrap();
        assert_eq!(
            navigate_from_root(&ctx, "this_name").unwrap().render(),
            "the_value".to_string()
        );
    }

    use serde::ser::Error as SerdeError;
    use serde::{Serialize, Serializer};

    struct UnserializableType {}

    impl Serialize for UnserializableType {
        fn serialize<S>(&self, _: S) -> Result<S::Ok, S::Error>
        where
            S: Serializer,
        {
            Err(SerdeError::custom("test"))
        }
    }

    #[test]
    fn test_serialize_error() {
        let d = UnserializableType {};
        assert!(Context::wraps(&d).is_err());
    }

    #[test]
    fn test_root() {
        let m = json!({
            "a" : {
                "b" : {
                    "c" : {
                        "d" : 1
                    }
                }
            },
            "b": 2
        });
        let ctx = Context::wraps(&m).unwrap();
        assert_eq!(
            ctx.navigate(
                &["a".to_owned(), "b".to_owned()],
                &VecDeque::new(),
                &PathSeg::parse("@root/b").unwrap(),
                &VecDeque::new()
            )
            .unwrap()
            .render(),
            "2".to_string()
        );
    }

    #[test]
    fn test_block_params() {
        let m = json!([{
            "a": [1, 2]
        }, {
            "b": [2, 3]
        }]);

        let ctx = Context::wraps(&m).unwrap();
        let mut block_param = BlockParams::new();
        block_param
            .add_path("z", ["0".to_owned(), "a".to_owned()].to_vec())
            .unwrap();
        block_param.add_value("t", json!("good")).unwrap();

        let mut block_params = VecDeque::new();
        block_params.push_front(block_param);

        assert_eq!(
            ctx.navigate(
                &Vec::new(),
                &VecDeque::new(),
                &PathSeg::parse("z.[1]").unwrap(),
                &block_params
            )
            .unwrap()
            .render(),
            "2".to_string()
        );
        assert_eq!(
            ctx.navigate(
                &Vec::new(),
                &VecDeque::new(),
                &PathSeg::parse("t").unwrap(),
                &block_params
            )
            .unwrap()
            .render(),
            "good".to_string()
        );
    }
}
<|endoftext|>


<commit_before>//! Traits for converting to and from Perl scalars.

use std::fmt::Display;
use raw;
use SV;

/// Fast unsafe conversion from raw SV pointer.
pub trait FromSV {
    /// Perform the conversion.
    unsafe fn from_sv(pthx: raw::Interpreter, raw: *mut raw::SV) -> Self;
}

/// Construct new `SV` from `self`.
pub trait IntoSV {
    /// Perform the conversion.
    fn into_sv(self, pthx: raw::Interpreter) -> SV;
}

/// Attempt unsafe conversion from a raw SV pointer.
pub trait TryFromSV: Sized {
    /// The type returned in the event of a conversion error.
    type Error: Display;
    /// Perform the conversion.
    unsafe fn try_from_sv(pthx: raw::Interpreter, raw: *mut raw::SV) -> Result<Self, Self::Error>;
}

impl<T> TryFromSV for T where T: FromSV {
    type Error = &'static str;
    unsafe fn try_from_sv(pthx: raw::Interpreter, raw: *mut raw::SV) -> Result<T, Self::Error>
    {
        Ok(T::from_sv(pthx, raw))
    }
}
<commit_msg>Implement IntoSV for Option<T: IntoSV><commit_after>//! Traits for converting to and from Perl scalars.

use std::fmt::Display;
use raw;
use SV;

/// Fast unsafe conversion from raw SV pointer.
pub trait FromSV {
    /// Perform the conversion.
    unsafe fn from_sv(pthx: raw::Interpreter, raw: *mut raw::SV) -> Self;
}

/// Construct new `SV` from `self`.
pub trait IntoSV {
    /// Perform the conversion.
    fn into_sv(self, pthx: raw::Interpreter) -> SV;
}

impl<T> IntoSV for Option<T> where T: IntoSV {
    fn into_sv(self, pthx: raw::Interpreter) -> SV {
        match self {
            Some(inner) => inner.into_sv(pthx),
            None => unsafe { SV::from_raw_owned(pthx, pthx.sv_undef()) },
        }
    }
}

/// Attempt unsafe conversion from a raw SV pointer.
pub trait TryFromSV: Sized {
    /// The type returned in the event of a conversion error.
    type Error: Display;
    /// Perform the conversion.
    unsafe fn try_from_sv(pthx: raw::Interpreter, raw: *mut raw::SV) -> Result<Self, Self::Error>;
}

impl<T> TryFromSV for T where T: FromSV {
    type Error = &'static str;
    unsafe fn try_from_sv(pthx: raw::Interpreter, raw: *mut raw::SV) -> Result<T, Self::Error>
    {
        Ok(T::from_sv(pthx, raw))
    }
}
<|endoftext|>


<commit_before>use std::num::Int;

use cpu::exec::fetch_exec;
use mmu::Memory;

pub mod exec;

static INTERRUPT_TABLE: [u16; 5] = [
    0x0040,   // V-Blank
    0x0048,   // LCD STAT
    0x0050,   // Timer
    0x0058,   // Serial
    0x0060,   // Joypad
];

#[derive(Copy)]
#[repr(u8)]
pub enum Interrupt {
    VBlank = 0b00000001,
    Stat   = 0b00000010,
    Timer  = 0b00000100,
    Serial = 0b00001000,
    Joypad = 0b00010000,
}

#[derive(PartialEq, Copy)]
pub enum State {
    Running,
    Stopped,
    Halted,
}

/// The main processor for GB/C. The processor is similar to the Z80 processor, with a few
/// differences (see: http://www.devrs.com/gb/files/gbspec.txt)
///
/// # Registers
///
/// * a,b,c,d,e,f,h,l : main register set (8-bit)
/// * sp : Stack pointer (16-bit)
/// * pc : Program counter (16-bit)
/// * ime : Interrupt flags register (8-bit)
///
/// In addition, the main register set can be combined into 16-bit registers: af, bc, de, hl
#[allow(missing_copy_implementations)]
pub struct Cpu {
    pub crashed: bool,

    pub a: u8,
    pub b: u8,
    pub c: u8,
    pub d: u8,
    pub e: u8,
    pub f: u8,
    pub h: u8,
    pub l: u8,

    pub ime: u8,

    pub sp: u16,
    pub pc: u16,

    pub state: State,
    pub clock: u8,
}

impl Cpu {
    pub fn new() -> Cpu {
        Cpu {
            crashed: true,

            a: 0, b: 0, c: 0, d: 0, e: 0, f: 0, h: 0, l: 0,

            ime: 0,

            sp: 0,
            pc: 0,

            state: State::Running,
            clock: 0,
        }
    }

    pub fn start_up(&mut self) {
        // TODO(major): Add support for SGB and CGB
        self.af().set(0x01B0);
        self.bc().set(0x0013);
        self.de().set(0x00D8);
        self.hl().set(0x014D);
        self.sp = 0xFFFE;
        self.pc = 0x0100;

        self.crashed = false;
    }

    /// Steps the CPU returning the number of elapsed cycles
    pub fn step(&mut self, mem: &mut Memory) -> u8 {
        if self.crashed {
            // If the CPU has crashed, then we can't do anything
            return 0xFF;
        }

        let mut elapsed_cycles = 0;
        if self.state == State::Running {
            elapsed_cycles += fetch_exec(self, mem);
        }
        else {
            elapsed_cycles += 1;
        }

        if self.ime != 0 || self.state == State::Halted {
            elapsed_cycles += self.handle_interrupts(mem);
        }

        // TODO: add support for CGB double speed mode
        self.clock += elapsed_cycles * 4;
        elapsed_cycles * 4
    }

    /// Handles interrupts
    /// Returns the amount of elapsed cycles (if any) as a result of processing interrupts.
    fn handle_interrupts(&mut self, mem: &mut Memory) -> u8 {
        let interrupts = mem.if_reg & mem.ie_reg;
        if interrupts == 0 {
            // No interrupts to handle
            return 0;
        }

        // Get the highest priority interrupt
        let i = interrupts.trailing_zeros();

        // Clear the interrupt bit if the CPU is not halted
        if self.state != State::Halted {
            mem.if_reg &= !(1 << i);
        }

        self.disable_interrupts(mem);
        self.call(mem, INTERRUPT_TABLE[i as usize]);
        self.state = State::Running;

        1
    }

    /// Enables CPU interrupts
    pub fn enable_interrupts(&mut self, _mem: &mut Memory) {
        self.ime = 1;
    }

    /// Disables CPU interrupts
    pub fn disable_interrupts(&mut self, _mem: &mut Memory) {
        self.ime = 0;
    }

    /// Jumps to a location
    pub fn jump(&mut self, addr: u16) {
        self.pc = addr;
    }

    pub fn push16(&mut self, mem: &mut Memory, val: u16) {
        self.sp = self.sp.wrapping_sub(2);
        mem.sw(self.sp, val);
    }

    pub fn pop16(&mut self, mem: &mut Memory) -> u16 {
        self.sp = self.sp.wrapping_add(2);
        mem.lw(self.sp.wrapping_sub(2))
    }

    pub fn call(&mut self, mem: &mut Memory, addr: u16) {
        self.sp = self.sp.wrapping_sub(2);
        mem.sw(self.sp, self.pc);
        self.pc = addr;
    }

    pub fn ret(&mut self, mem: &mut Memory) {
        self.pc = mem.lw(self.sp);
        self.sp = self.sp.wrapping_add(2);
    }

    /// Halt the cpu
    pub fn halt(&mut self) {
        self.state = State::Halted;
    }

    /// Stop the cpu
    pub fn stop(&mut self) {
        self.state = State::Stopped;
    }

    /// Increments the program counter, returning the old value
    pub fn bump(&mut self) -> u16 {
        self.pc = self.sp.wrapping_add(1);
        self.pc.wrapping_sub(1)
    }

    /// Handle instructions corresponding to invalid opcodes
    fn invalid_inst(&mut self, _opcode: u8) -> u8 {
        self.crashed = true;
        0
    }
}

// The 8-bit registers in the main register set can be joint to form 16-bit registers.
macro_rules! join_regs { (($r1: ident, $r2: ident) as $name: ident) => (
    impl Cpu {
        pub fn $name(&mut self) -> JointReg {
            JointReg {
                high: &mut self.$r1,
                low: &mut self.$r2,
            }
        }
    }
) }

join_regs!((b, c) as bc);
join_regs!((d, e) as de);
join_regs!((h, l) as hl);
join_regs!((a, f) as af);

pub struct JointReg<'a> {
    high: &'a mut u8,
    low: &'a mut u8,
}

impl<'a> JointReg<'a> {
    #[inline(always)]
    pub fn set(&mut self, value: u16) {
        *self.high = (value >> 8) as u8;
        *self.low = value as u8;
    }

    #[inline(always)]
    pub fn get(&self) -> u16 {
        ((*self.high as u16) << 8) + *self.low as u16
    }
}
<commit_msg>Fix bug caused by recent changes.<commit_after>use std::num::Int;

use cpu::exec::fetch_exec;
use mmu::Memory;

pub mod exec;

static INTERRUPT_TABLE: [u16; 5] = [
    0x0040,   // V-Blank
    0x0048,   // LCD STAT
    0x0050,   // Timer
    0x0058,   // Serial
    0x0060,   // Joypad
];

#[derive(Copy)]
#[repr(u8)]
pub enum Interrupt {
    VBlank = 0b00000001,
    Stat   = 0b00000010,
    Timer  = 0b00000100,
    Serial = 0b00001000,
    Joypad = 0b00010000,
}

#[derive(PartialEq, Copy)]
pub enum State {
    Running,
    Stopped,
    Halted,
}

/// The main processor for GB/C. The processor is similar to the Z80 processor, with a few
/// differences (see: http://www.devrs.com/gb/files/gbspec.txt)
///
/// # Registers
///
/// * a,b,c,d,e,f,h,l : main register set (8-bit)
/// * sp : Stack pointer (16-bit)
/// * pc : Program counter (16-bit)
/// * ime : Interrupt flags register (8-bit)
///
/// In addition, the main register set can be combined into 16-bit registers: af, bc, de, hl
#[allow(missing_copy_implementations)]
pub struct Cpu {
    pub crashed: bool,

    pub a: u8,
    pub b: u8,
    pub c: u8,
    pub d: u8,
    pub e: u8,
    pub f: u8,
    pub h: u8,
    pub l: u8,

    pub ime: u8,

    pub sp: u16,
    pub pc: u16,

    pub state: State,
    pub clock: u8,
}

impl Cpu {
    pub fn new() -> Cpu {
        Cpu {
            crashed: true,

            a: 0, b: 0, c: 0, d: 0, e: 0, f: 0, h: 0, l: 0,

            ime: 0,

            sp: 0,
            pc: 0,

            state: State::Running,
            clock: 0,
        }
    }

    pub fn start_up(&mut self) {
        // TODO(major): Add support for SGB and CGB
        self.af().set(0x01B0);
        self.bc().set(0x0013);
        self.de().set(0x00D8);
        self.hl().set(0x014D);
        self.sp = 0xFFFE;
        self.pc = 0x0100;

        self.crashed = false;
    }

    /// Steps the CPU returning the number of elapsed cycles
    pub fn step(&mut self, mem: &mut Memory) -> u8 {
        if self.crashed {
            // If the CPU has crashed, then we can't do anything
            return 0xFF;
        }

        let mut elapsed_cycles = 0;
        if self.state == State::Running {
            elapsed_cycles += fetch_exec(self, mem);
        }
        else {
            elapsed_cycles += 1;
        }

        if self.ime != 0 || self.state == State::Halted {
            elapsed_cycles += self.handle_interrupts(mem);
        }

        // TODO: add support for CGB double speed mode
        self.clock += elapsed_cycles * 4;
        elapsed_cycles * 4
    }

    /// Handles interrupts
    /// Returns the amount of elapsed cycles (if any) as a result of processing interrupts.
    fn handle_interrupts(&mut self, mem: &mut Memory) -> u8 {
        let interrupts = mem.if_reg & mem.ie_reg;
        if interrupts == 0 {
            // No interrupts to handle
            return 0;
        }

        // Get the highest priority interrupt
        let i = interrupts.trailing_zeros();

        // Clear the interrupt bit if the CPU is not halted
        if self.state != State::Halted {
            mem.if_reg &= !(1 << i);
        }

        self.disable_interrupts(mem);
        self.call(mem, INTERRUPT_TABLE[i as usize]);
        self.state = State::Running;

        1
    }

    /// Enables CPU interrupts
    pub fn enable_interrupts(&mut self, _mem: &mut Memory) {
        self.ime = 1;
    }

    /// Disables CPU interrupts
    pub fn disable_interrupts(&mut self, _mem: &mut Memory) {
        self.ime = 0;
    }

    /// Jumps to a location
    pub fn jump(&mut self, addr: u16) {
        self.pc = addr;
    }

    pub fn push16(&mut self, mem: &mut Memory, val: u16) {
        self.sp = self.sp.wrapping_sub(2);
        mem.sw(self.sp, val);
    }

    pub fn pop16(&mut self, mem: &mut Memory) -> u16 {
        self.sp = self.sp.wrapping_add(2);
        mem.lw(self.sp.wrapping_sub(2))
    }

    pub fn call(&mut self, mem: &mut Memory, addr: u16) {
        self.sp = self.sp.wrapping_sub(2);
        mem.sw(self.sp, self.pc);
        self.pc = addr;
    }

    pub fn ret(&mut self, mem: &mut Memory) {
        self.pc = mem.lw(self.sp);
        self.sp = self.sp.wrapping_add(2);
    }

    /// Halt the cpu
    pub fn halt(&mut self) {
        self.state = State::Halted;
    }

    /// Stop the cpu
    pub fn stop(&mut self) {
        self.state = State::Stopped;
    }

    /// Increments the program counter, returning the old value
    pub fn bump(&mut self) -> u16 {
        self.pc = self.pc.wrapping_add(1);
        self.pc.wrapping_sub(1)
    }

    /// Handle instructions corresponding to invalid opcodes
    fn invalid_inst(&mut self, _opcode: u8) -> u8 {
        self.crashed = true;
        0
    }
}

// The 8-bit registers in the main register set can be joint to form 16-bit registers.
macro_rules! join_regs { (($r1: ident, $r2: ident) as $name: ident) => (
    impl Cpu {
        pub fn $name(&mut self) -> JointReg {
            JointReg {
                high: &mut self.$r1,
                low: &mut self.$r2,
            }
        }
    }
) }

join_regs!((b, c) as bc);
join_regs!((d, e) as de);
join_regs!((h, l) as hl);
join_regs!((a, f) as af);

pub struct JointReg<'a> {
    high: &'a mut u8,
    low: &'a mut u8,
}

impl<'a> JointReg<'a> {
    #[inline(always)]
    pub fn set(&mut self, value: u16) {
        *self.high = (value >> 8) as u8;
        *self.low = value as u8;
    }

    #[inline(always)]
    pub fn get(&self) -> u16 {
        ((*self.high as u16) << 8) + *self.low as u16
    }
}
<|endoftext|>


<commit_before>// Zinc, the bare metal stack for rust.
// Copyright 2014 Vladimir "farcaller" Pouzanov <farcaller@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! The final object that gets linked into the binary. Provides trampilines
//! for stack management, scheduler, ISRs and other symbols with global
//! visibility.

#![crate_type="staticlib"]
#![no_std]

extern crate core;
extern crate zinc;
extern crate app;

#[no_split_stack]
#[no_mangle]
#[start]
pub extern fn main() {
  app::main();
}

#[no_split_stack]
#[no_mangle]
#[cfg(not(cfg_multitasking))]
pub extern fn __morestack() {
  unsafe { core::intrinsics::abort() };
}

#[no_split_stack]
#[no_mangle]
#[cfg(cfg_multitasking)]
pub extern fn __morestack() {
  zinc::os::task::morestack();
}

#[no_split_stack]
#[no_mangle]
#[cfg(cfg_multitasking)]
pub unsafe fn task_scheduler() {
  zinc::os::task::task_scheduler();
}
<commit_msg>Removed #[start] from app.’s, as it’s never used.<commit_after>// Zinc, the bare metal stack for rust.
// Copyright 2014 Vladimir "farcaller" Pouzanov <farcaller@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! The final object that gets linked into the binary. Provides trampilines
//! for stack management, scheduler, ISRs and other symbols with global
//! visibility.

#![crate_type="staticlib"]
#![no_std]

extern crate core;
extern crate zinc;
extern crate app;

#[no_split_stack]
#[no_mangle]
pub extern fn main() {
  app::main();
}

#[no_split_stack]
#[no_mangle]
#[cfg(not(cfg_multitasking))]
pub extern fn __morestack() {
  unsafe { core::intrinsics::abort() };
}

#[no_split_stack]
#[no_mangle]
#[cfg(cfg_multitasking)]
pub extern fn __morestack() {
  zinc::os::task::morestack();
}

#[no_split_stack]
#[no_mangle]
#[cfg(cfg_multitasking)]
pub unsafe fn task_scheduler() {
  zinc::os::task::task_scheduler();
}
<|endoftext|>


<commit_before>pub mod config;
pub mod downloader;
pub mod converter;
pub mod db;

use lib::downloader::{Downloader};

use mysql::error::MyError;
use std::env::current_exe;
use std::process::{Command,Output};
use std::error::Error;
use std::{self,io,str};
use std::fs::remove_file;
use CONFIG;

use std::ascii::AsciiExt;

use std::fs::{rename};

#[derive(Debug)]
pub enum DownloadError{
    DownloadError(String),
    FFMPEGError(String),
    DMCAError,
    NotAvailable,
    QualityNotAvailable,
    ExtractorError,
    InternalError(String),
    DBError(String),
}

impl From<MyError> for DownloadError {
    fn from(err: MyError) -> DownloadError {
        DownloadError::DBError(err.description().into())
    }
}

impl From<io::Error> for DownloadError {
    fn from(err: io::Error) -> DownloadError {
        DownloadError::InternalError(err.description().into())
    }
}

///Return whether the quality is a split container or not: video only
///as specified in the docs
pub fn is_split_container(quality: &i16) -> bool {
    if CONFIG.extensions.mp3.contains(quality) {
        false
    } else if CONFIG.extensions.aac.contains(quality) {
        false
    } else if CONFIG.extensions.m4a.contains(quality) {
        false
    } else {
        true
    }
}

///Format file name for
pub fn format_file_name<'a>(name: &str, download: &'a Downloader, qid: &i64) -> String {
    println!("Fileextension: {:?}", get_file_ext(download));
    format!("{}-{}.{}",url_encode(name), qid, get_file_ext(download))
}

///Returns the file extension
pub fn get_file_ext<'a>(download: &Downloader) -> &'a str {
    if download.is_audio() {
        if CONFIG.extensions.aac.contains(&download.ddb.quality) {
            "aac"
        }else if CONFIG.extensions.mp3.contains(&download.ddb.quality) {
            "mp3"
        }else{
            "unknown"
        }
    }else{
        if CONFIG.extensions.mp4.contains(&download.ddb.quality) {
            "mp4"
        } else if CONFIG.extensions.flv.contains(&download.ddb.quality) {
            "flv"
        } else {
            "unknown"
        }
    }
}

///Move file to location
pub fn move_file(original: &str, destination: &str) -> Result<(),DownloadError> {
    match rename(original, destination) { // no try possible..
        Err(v) => Err(v.into()),
        Ok(_) => Ok(()),
    }
}

///Return an sanitized String (url encode still required)
pub fn url_encode(input: &str) -> String {
    // iterator over input, apply function to each element(function
    input.chars().map(|char| {
        match char {
            '\'' | '"' | '\\' => '_',
            '&' => '-',
            c if c.is_ascii() => c,
            _ => '_'
        }
    }).collect()
    // match for each char, then do collect: loop through the iterator, collect all elements
    // into container FromIterator
}

///Removed file name invalid chars
pub fn file_encode(input: &str) -> String {
    input.chars().map(|char| {
        match char {
            '\\' | '/' | '\0' => '_',
            c => c,
        }
    }).collect()
}

///Format save location for file, zip dependent
///audio files get an 'a' as suffix
pub fn format_file_path(qid: &i64, folder: Option<String>, audio: bool) -> String {
    let suffix = if audio {
        "a"
    }else {
        ""
    };
    match folder {
        Some(v) => format!("{}/{}/{}{}", &CONFIG.general.save_dir, v, qid,suffix),
        None => format!("{}/{}{}", &CONFIG.general.save_dir, qid,suffix),
    }
}

///Format save path, dependent on zip option.
pub fn format_save_path<'a>(folder: Option<String>, name: &str, download: &'a Downloader, qid: &i64) -> String {
    let clean_name = &file_encode(&name);
    match folder {
        Some(v) => format!("{}/{}/{}", &CONFIG.general.save_dir, v, format_file_name(clean_name,download,qid)),
        None => format!("{}/{}", &CONFIG.general.download_dir, format_file_name(clean_name,download,qid)),
    }
}

///Zip folder to file
pub fn zip_folder(folder: &str, zip_name: &str) -> Result<(), DownloadError> {
    let io = try!(create_zip_cmd(folder,zip_name));
    println!("zip stdout: {}\nzip stderr: {}", str::from_utf8(&io.stdout).unwrap(),str::from_utf8(&io.stderr).unwrap());
    if str::from_utf8(&io.stderr).unwrap().contains("error") {
        return Err(DownloadError::DownloadError(format!("error: {:?}",&io.stdout)))
    }
    Ok(())
}

fn create_zip_cmd(folder: &str, zip_file: &str) -> Result<Output, DownloadError> {
    match Command::new("tar").arg("-zcf").arg(zip_file).arg("-C").arg(folder).arg(".").output() {
                     Err(e) => Err(DownloadError::InternalError(format!("failed to zip: {}", e))),
                     Ok(v) => Ok(v),
    }
}

///Delete all files in the list
pub fn delete_files(files: Vec<String>) -> Result<(), DownloadError>{
    for file in files.iter() {
        println!("deleting {}",file);
        try!(remove_file(file));
    }
    Ok(())
}

pub fn get_executable_folder() -> std::path::PathBuf {
	let mut folder = current_exe().unwrap_or(
		{error!("no exec path in get_executable_folder!"); panic!();}
	);
	folder.pop();
	folder
}
<commit_msg>fixed get_exectuable_folder & added l_expect usage<commit_after>pub mod config;
pub mod downloader;
pub mod converter;
pub mod db;

use lib::downloader::{Downloader};

use mysql::error::MyError;
use std::env::current_exe;
use std::process::{Command,Output};
use std::error::Error;
use std::{self,io,str};
use std::fs::remove_file;
use CONFIG;

use std::ascii::AsciiExt;

use std::fs::{rename};

#[derive(Debug)]
pub enum DownloadError{
    DownloadError(String),
    FFMPEGError(String),
    DMCAError,
    NotAvailable,
    QualityNotAvailable,
    ExtractorError,
    InternalError(String),
    DBError(String),
}

impl From<MyError> for DownloadError {
    fn from(err: MyError) -> DownloadError {
        DownloadError::DBError(err.description().into())
    }
}

impl From<io::Error> for DownloadError {
    fn from(err: io::Error) -> DownloadError {
        DownloadError::InternalError(err.description().into())
    }
}

///Return whether the quality is a split container or not: video only
///as specified in the docs
pub fn is_split_container(quality: &i16) -> bool {
    if CONFIG.extensions.mp3.contains(quality) {
        false
    } else if CONFIG.extensions.aac.contains(quality) {
        false
    } else if CONFIG.extensions.m4a.contains(quality) {
        false
    } else {
        true
    }
}

///Format file name for
pub fn format_file_name<'a>(name: &str, download: &'a Downloader, qid: &i64) -> String {
    println!("Fileextension: {:?}", get_file_ext(download));
    format!("{}-{}.{}",url_encode(name), qid, get_file_ext(download))
}

///Returns the file extension
pub fn get_file_ext<'a>(download: &Downloader) -> &'a str {
    if download.is_audio() {
        if CONFIG.extensions.aac.contains(&download.ddb.quality) {
            "aac"
        }else if CONFIG.extensions.mp3.contains(&download.ddb.quality) {
            "mp3"
        }else{
            "unknown"
        }
    }else{
        if CONFIG.extensions.mp4.contains(&download.ddb.quality) {
            "mp4"
        } else if CONFIG.extensions.flv.contains(&download.ddb.quality) {
            "flv"
        } else {
            "unknown"
        }
    }
}

///Move file to location
pub fn move_file(original: &str, destination: &str) -> Result<(),DownloadError> {
    match rename(original, destination) { // no try possible..
        Err(v) => Err(v.into()),
        Ok(_) => Ok(()),
    }
}

///Return an sanitized String (url encode still required)
pub fn url_encode(input: &str) -> String {
    // iterator over input, apply function to each element(function
    input.chars().map(|char| {
        match char {
            '\'' | '"' | '\\' => '_',
            '&' => '-',
            c if c.is_ascii() => c,
            _ => '_'
        }
    }).collect()
    // match for each char, then do collect: loop through the iterator, collect all elements
    // into container FromIterator
}

///Removed file name invalid chars
pub fn file_encode(input: &str) -> String {
    input.chars().map(|char| {
        match char {
            '\\' | '/' | '\0' => '_',
            c => c,
        }
    }).collect()
}

///Format save location for file, zip dependent
///audio files get an 'a' as suffix
pub fn format_file_path(qid: &i64, folder: Option<String>, audio: bool) -> String {
    let suffix = if audio {
        "a"
    }else {
        ""
    };
    match folder {
        Some(v) => format!("{}/{}/{}{}", &CONFIG.general.save_dir, v, qid,suffix),
        None => format!("{}/{}{}", &CONFIG.general.save_dir, qid,suffix),
    }
}

///Format save path, dependent on zip option.
pub fn format_save_path<'a>(folder: Option<String>, name: &str, download: &'a Downloader, qid: &i64) -> String {
    let clean_name = &file_encode(&name);
    match folder {
        Some(v) => format!("{}/{}/{}", &CONFIG.general.save_dir, v, format_file_name(clean_name,download,qid)),
        None => format!("{}/{}", &CONFIG.general.download_dir, format_file_name(clean_name,download,qid)),
    }
}

///Zip folder to file
pub fn zip_folder(folder: &str, zip_name: &str) -> Result<(), DownloadError> {
    let io = try!(create_zip_cmd(folder,zip_name));
    println!("zip stdout: {}\nzip stderr: {}", str::from_utf8(&io.stdout).unwrap(),str::from_utf8(&io.stderr).unwrap());
    if str::from_utf8(&io.stderr).unwrap().contains("error") {
        return Err(DownloadError::DownloadError(format!("error: {:?}",&io.stdout)))
    }
    Ok(())
}

fn create_zip_cmd(folder: &str, zip_file: &str) -> Result<Output, DownloadError> {
    match Command::new("tar").arg("-zcf").arg(zip_file).arg("-C").arg(folder).arg(".").output() {
                     Err(e) => Err(DownloadError::InternalError(format!("failed to zip: {}", e))),
                     Ok(v) => Ok(v),
    }
}

///Delete all files in the list
pub fn delete_files(files: Vec<String>) -> Result<(), DownloadError>{
    for file in files.iter() {
        println!("deleting {}",file);
        try!(remove_file(file));
    }
    Ok(())
}

pub fn get_executable_folder() -> Result<std::path::PathBuf, io::Error> {
	let mut folder = try!(current_exe());
	folder.pop();
	Ok(folder)
}
<|endoftext|>