<commit_before>//! The parser functionality

use bindings::*;
use c_helpers::*;
use tree::*;

use std::ffi::{CStr, CString};
use std::fmt;
use std::ptr;
use std::str;

enum XmlParserOption {
  Recover = 1, // Relaxed parsing
  // XML_PARSE_NODEFDTD = 4, // do not default a doctype if not found
  Noerror = 32, // suppress error reports
  Nowarning = 64, // suppress warning reports
                // XML_PARSE_PEDANTIC = 128, // pedantic error reporting
                // XML_PARSE_NOBLANKS = 256, // remove blank nodes
                // XML_PARSE_NONET = 2048, // Forbid network access
                // XML_PARSE_NOIMPLIED = 8192, // Do not add implied Xml/body... elements
                // XML_PARSE_COMPACT = 65536, // compact small text nodes
                // XML_PARSE_IGNORE_ENC = 2097152, // ignore internal document encoding hint
}

enum HtmlParserOption {
  Recover = 1, // Relaxed parsing
  // HTML_PARSE_NODEFDTD = 4, // do not default a doctype if not found
  Noerror = 32, // suppress error reports
  Nowarning = 64, // suppress warning reports
                // HTML_PARSE_PEDANTIC = 128, // pedantic error reporting
                // HTML_PARSE_NOBLANKS = 256, // remove blank nodes
                // HTML_PARSE_NONET = 2048, // Forbid network access
                // HTML_PARSE_NOIMPLIED = 8192, // Do not add implied html/body... elements
                // HTML_PARSE_COMPACT = 65536, // compact small text nodes
                // HTML_PARSE_IGNORE_ENC = 2097152, // ignore internal document encoding hint
}

///Parser Errors
pub enum XmlParseError {
  ///Parsing returned a null pointer as document pointer
  GotNullPointer,
}

impl fmt::Debug for XmlParseError {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match *self {
      XmlParseError::GotNullPointer => write!(f, "Got a Null pointer"),
    }
  }
}

#[derive(PartialEq)]
/// Enum for the parse formats supported by libxml2
pub enum ParseFormat {
  /// Strict parsing for XML
  XML,
  /// Relaxed parsing for HTML
  HTML,
}
/// Parsing API wrapper for libxml2
pub struct Parser {
  /// The `ParseFormat` for this parser
  pub format: ParseFormat,
}
impl Default for Parser {
  /// Create a parser for XML documents
  fn default() -> Self {
    Parser {
      format: ParseFormat::XML,
    }
  }
}
impl Parser {
  /// Create a parser for HTML documents
  pub fn default_html() -> Self {
    Parser {
      format: ParseFormat::HTML,
    }
  }

  ///Parses the XML/HTML file `filename` to generate a new `Document`
  pub fn parse_file(&self, filename: &str) -> Result<Document, XmlParseError> {
    let c_filename = CString::new(filename).unwrap();
    let c_utf8 = CString::new("utf-8").unwrap();
    let options: i32 = XmlParserOption::Recover as i32 + XmlParserOption::Noerror as i32
      + XmlParserOption::Nowarning as i32;
    match self.format {
      ParseFormat::XML => unsafe {
        xmlKeepBlanksDefault(1);
        let docptr = xmlReadFile(c_filename.as_ptr(), c_utf8.as_ptr(), options);
        if docptr.is_null() {
          Err(XmlParseError::GotNullPointer)
        } else {
          Ok(Document::new_ptr(docptr))
        }
      },
      ParseFormat::HTML => {
        // TODO: Allow user-specified options later on
        unsafe {
          let options: i32 = HtmlParserOption::Recover as i32 + HtmlParserOption::Noerror as i32
            + HtmlParserOption::Nowarning as i32;
          xmlKeepBlanksDefault(1);
          let docptr = htmlReadFile(c_filename.as_ptr(), c_utf8.as_ptr(), options);
          if docptr.is_null() {
            Err(XmlParseError::GotNullPointer)
          } else {
            Ok(Document::new_ptr(docptr))
          }
        }
      }
    }
  }

  ///Parses the XML/HTML string `input_string` to generate a new `Document`
  pub fn parse_string(&self, input_string: &str) -> Result<Document, XmlParseError> {
    let c_string = CString::new(input_string).unwrap();
    let c_utf8 = CString::new("utf-8").unwrap();
    let c_url = CString::new("").unwrap();
    match self.format {
      ParseFormat::XML => unsafe {
        let options: i32 = XmlParserOption::Recover as i32 + XmlParserOption::Noerror as i32
          + XmlParserOption::Nowarning as i32;
        let docptr = xmlReadDoc(
          c_string.as_bytes().as_ptr(),
          c_url.as_ptr(),
          c_utf8.as_ptr(),
          options,
        );
        if docptr.is_null() {
          Err(XmlParseError::GotNullPointer)
        } else {
          Ok(Document::new_ptr(docptr))
        }
      },
      ParseFormat::HTML => unsafe {
        let options: i32 = HtmlParserOption::Recover as i32 + HtmlParserOption::Noerror as i32
          + HtmlParserOption::Nowarning as i32;
        let docptr = htmlReadDoc(
          c_string.as_bytes().as_ptr(),
          c_url.as_ptr(),
          c_utf8.as_ptr(),
          options,
        );
        if docptr.is_null() {
          Err(XmlParseError::GotNullPointer)
        } else {
          Ok(Document::new_ptr(docptr))
        }
      },
    }
  }

  /// Checks a string for well-formedness
  /// IMPORTANT: This function is currently implemented in a HACKY way, to ignore invalid errors for HTML5 elements (such as <math>)
  ///            this means you should NEVER USE IT WHILE THREADING, it is CERTAIN TO BREAK
  ///
  /// Help is welcome in implementing it correctly.
  pub fn is_well_formed_html(&self, input_string: &str) -> bool {
    if input_string.is_empty() {
      return false;
    }
    let c_string = CString::new(input_string).unwrap();
    let c_utf8 = CString::new("utf-8").unwrap();
    // disable generic error lines from libxml2
    match self.format {
      ParseFormat::XML => false, // TODO: Add support for XML at some point
      ParseFormat::HTML => unsafe {
        let ctxt = htmlNewParserCtxt();
        setWellFormednessHandler(ctxt);
        let docptr = htmlCtxtReadDoc(
          ctxt,
          c_string.as_bytes().as_ptr(),
          ptr::null_mut(),
          c_utf8.as_ptr(),
          10_596,
        ); // htmlParserOption = 4+32+64+256+2048+8192
        let well_formed_final = if htmlWellFormed(ctxt) {
          // Basic well-formedness passes, let's check if we have an <html> element as root too
          if !docptr.is_null() {
            let node_ptr = xmlDocGetRootElement(docptr);
            let name_ptr = xmlNodeGetName(node_ptr);
            if name_ptr.is_null() {
              false
            }
            //empty string
            else {
              let c_root_name = CStr::from_ptr(name_ptr);
              let root_name = str::from_utf8(c_root_name.to_bytes()).unwrap().to_owned();
              root_name == "html"
            }
          } else {
            false
          }
        } else {
          false
        };

        if !ctxt.is_null() {
          htmlFreeParserCtxt(ctxt);
        }
        if !docptr.is_null() {
          xmlFreeDoc(docptr);
        }
        well_formed_final
      },
    }
  }
}
<commit_msg>reformat parser.rs<commit_after>//! The parser functionality

use bindings::*;
use c_helpers::*;
use tree::*;

use std::ffi::{CStr, CString};
use std::fmt;
use std::ptr;
use std::str;

enum XmlParserOption {
  Recover = 1, // Relaxed parsing
  // XML_PARSE_NODEFDTD = 4, // do not default a doctype if not found
  Noerror = 32, // suppress error reports
  Nowarning = 64, // suppress warning reports
                // XML_PARSE_PEDANTIC = 128, // pedantic error reporting
                // XML_PARSE_NOBLANKS = 256, // remove blank nodes
                // XML_PARSE_NONET = 2048, // Forbid network access
                // XML_PARSE_NOIMPLIED = 8192, // Do not add implied Xml/body... elements
                // XML_PARSE_COMPACT = 65536, // compact small text nodes
                // XML_PARSE_IGNORE_ENC = 2097152, // ignore internal document encoding hint
}

enum HtmlParserOption {
  Recover = 1, // Relaxed parsing
  // HTML_PARSE_NODEFDTD = 4, // do not default a doctype if not found
  Noerror = 32, // suppress error reports
  Nowarning = 64, // suppress warning reports
                // HTML_PARSE_PEDANTIC = 128, // pedantic error reporting
                // HTML_PARSE_NOBLANKS = 256, // remove blank nodes
                // HTML_PARSE_NONET = 2048, // Forbid network access
                // HTML_PARSE_NOIMPLIED = 8192, // Do not add implied html/body... elements
                // HTML_PARSE_COMPACT = 65536, // compact small text nodes
                // HTML_PARSE_IGNORE_ENC = 2097152, // ignore internal document encoding hint
}

///Parser Errors
pub enum XmlParseError {
  ///Parsing returned a null pointer as document pointer
  GotNullPointer,
}

impl fmt::Debug for XmlParseError {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match *self {
      XmlParseError::GotNullPointer => write!(f, "Got a Null pointer"),
    }
  }
}

#[derive(PartialEq)]
/// Enum for the parse formats supported by libxml2
pub enum ParseFormat {
  /// Strict parsing for XML
  XML,
  /// Relaxed parsing for HTML
  HTML,
}
/// Parsing API wrapper for libxml2
pub struct Parser {
  /// The `ParseFormat` for this parser
  pub format: ParseFormat,
}
impl Default for Parser {
  /// Create a parser for XML documents
  fn default() -> Self {
    Parser {
      format: ParseFormat::XML,
    }
  }
}
impl Parser {
  /// Create a parser for HTML documents
  pub fn default_html() -> Self {
    Parser {
      format: ParseFormat::HTML,
    }
  }

  ///Parses the XML/HTML file `filename` to generate a new `Document`
  pub fn parse_file(&self, filename: &str) -> Result<Document, XmlParseError> {
    let c_filename = CString::new(filename).unwrap();
    let c_utf8 = CString::new("utf-8").unwrap();
    let options: i32 = XmlParserOption::Recover as i32
      + XmlParserOption::Noerror as i32
      + XmlParserOption::Nowarning as i32;
    match self.format {
      ParseFormat::XML => unsafe {
        xmlKeepBlanksDefault(1);
        let docptr = xmlReadFile(c_filename.as_ptr(), c_utf8.as_ptr(), options);
        if docptr.is_null() {
          Err(XmlParseError::GotNullPointer)
        } else {
          Ok(Document::new_ptr(docptr))
        }
      },
      ParseFormat::HTML => {
        // TODO: Allow user-specified options later on
        unsafe {
          let options: i32 = HtmlParserOption::Recover as i32
            + HtmlParserOption::Noerror as i32
            + HtmlParserOption::Nowarning as i32;
          xmlKeepBlanksDefault(1);
          let docptr = htmlReadFile(c_filename.as_ptr(), c_utf8.as_ptr(), options);
          if docptr.is_null() {
            Err(XmlParseError::GotNullPointer)
          } else {
            Ok(Document::new_ptr(docptr))
          }
        }
      }
    }
  }

  ///Parses the XML/HTML string `input_string` to generate a new `Document`
  pub fn parse_string(&self, input_string: &str) -> Result<Document, XmlParseError> {
    let c_string = CString::new(input_string).unwrap();
    let c_utf8 = CString::new("utf-8").unwrap();
    let c_url = CString::new("").unwrap();
    match self.format {
      ParseFormat::XML => unsafe {
        let options: i32 = XmlParserOption::Recover as i32
          + XmlParserOption::Noerror as i32
          + XmlParserOption::Nowarning as i32;
        let docptr = xmlReadDoc(
          c_string.as_bytes().as_ptr(),
          c_url.as_ptr(),
          c_utf8.as_ptr(),
          options,
        );
        if docptr.is_null() {
          Err(XmlParseError::GotNullPointer)
        } else {
          Ok(Document::new_ptr(docptr))
        }
      },
      ParseFormat::HTML => unsafe {
        let options: i32 = HtmlParserOption::Recover as i32
          + HtmlParserOption::Noerror as i32
          + HtmlParserOption::Nowarning as i32;
        let docptr = htmlReadDoc(
          c_string.as_bytes().as_ptr(),
          c_url.as_ptr(),
          c_utf8.as_ptr(),
          options,
        );
        if docptr.is_null() {
          Err(XmlParseError::GotNullPointer)
        } else {
          Ok(Document::new_ptr(docptr))
        }
      },
    }
  }

  /// Checks a string for well-formedness
  /// IMPORTANT: This function is currently implemented in a HACKY way, to ignore invalid errors for HTML5 elements (such as <math>)
  ///            this means you should NEVER USE IT WHILE THREADING, it is CERTAIN TO BREAK
  ///
  /// Help is welcome in implementing it correctly.
  pub fn is_well_formed_html(&self, input_string: &str) -> bool {
    if input_string.is_empty() {
      return false;
    }
    let c_string = CString::new(input_string).unwrap();
    let c_utf8 = CString::new("utf-8").unwrap();
    // disable generic error lines from libxml2
    match self.format {
      ParseFormat::XML => false, // TODO: Add support for XML at some point
      ParseFormat::HTML => unsafe {
        let ctxt = htmlNewParserCtxt();
        setWellFormednessHandler(ctxt);
        let docptr = htmlCtxtReadDoc(
          ctxt,
          c_string.as_bytes().as_ptr(),
          ptr::null_mut(),
          c_utf8.as_ptr(),
          10_596,
        ); // htmlParserOption = 4+32+64+256+2048+8192
        let well_formed_final = if htmlWellFormed(ctxt) {
          // Basic well-formedness passes, let's check if we have an <html> element as root too
          if !docptr.is_null() {
            let node_ptr = xmlDocGetRootElement(docptr);
            let name_ptr = xmlNodeGetName(node_ptr);
            if name_ptr.is_null() {
              false
            }
            //empty string
            else {
              let c_root_name = CStr::from_ptr(name_ptr);
              let root_name = str::from_utf8(c_root_name.to_bytes()).unwrap().to_owned();
              root_name == "html"
            }
          } else {
            false
          }
        } else {
          false
        };

        if !ctxt.is_null() {
          htmlFreeParserCtxt(ctxt);
        }
        if !docptr.is_null() {
          xmlFreeDoc(docptr);
        }
        well_formed_final
      },
    }
  }
}
<|endoftext|>


<commit_before>/* diosix hypervisor main entry code
 *
 * (c) Chris Williams, 2019-2020.
 *
 * See LICENSE for usage and copying.
 */

/* let the compiler know we're on our own here in bare-metal world */
#![no_std]
#![no_main]
#![feature(asm)]

#![allow(dead_code)]
#![allow(unused_unsafe)]
#![allow(improper_ctypes)]
#![feature(type_ascription)]

/* provide a framework for unit testing */
#![feature(custom_test_frameworks)]
#![test_runner(crate::run_tests)]
#![reexport_test_harness_main = "hvtests"] /* entry point for tests */

/* plug our custom heap allocator into the Rust language: Box, etc */
#![feature(alloc_error_handler)]
#![feature(box_syntax)]
#[allow(unused_imports)]
#[macro_use]
extern crate alloc;

/* needed to convert raw dtb pointer into a slice */
use core::slice;

/* needed for fast lookup tables of stuff */
extern crate hashbrown;

/* needed for elf parsing */
extern crate xmas_elf;

/* needed for device tree parsing and manipulation */
extern crate devicetree;

/* needed for parsing diosix manifest file-system (DMFS) images bundled with the hypervisor */
extern crate dmfs;

/* pass the feature qemudebug to be able to write direct to the emulator's stdout
via the macro qemuprint::println!() */
#[cfg(feature = "qemudebug")]
extern crate qemuprint;

/* needed for lazyily-allocated static variables, and atomic ops */
#[macro_use]
extern crate lazy_static;
extern crate spin;
use spin::Mutex;

/* this will bring in all the hardware-specific code */
extern crate platform;

/* and now for all our non-hw specific code */
#[macro_use]
mod debug;      /* get us some kind of debug output, typically to a serial port */
mod hardware;   /* parse device trees into hardware objects */
#[macro_use]
mod heap;       /* per-CPU private heap management */
mod panic;      /* implement panic() handlers */
mod irq;        /* handle hw interrupts and sw exceptions, collectively known as IRQs */
#[macro_use]
mod physmem;    /* manage host physical memory */
mod virtmem;    /* manage capsule virtual memory */
mod pcore;      /* manage CPU cores */
mod vcore;      /* virtual CPU core management... */
mod scheduler;  /* ...and scheduling */
mod capsule;    /* manage capsules */
mod loader;     /* parse and load supervisor binaries */
mod message;    /* send messages between physical cores */
mod service;    /* allow capsules to register services */
mod manifest;   /* manage capsules loaded with the hypervisor */

/* list of error codes */
mod error;
use error::Cause;

use pcore::{PhysicalCoreID, BOOT_PCORE_ID};

/* bring in the built-in dmfs image */
use core::intrinsics::transmute;
extern "C"
{
    static _binary_dmfs_img_start: u8;
    static _binary_dmfs_img_size: u8;
}

/* tell Rust to use our HVallocator to allocate and free heap memory.
although we'll keep track of physical memory, we'll let Rust perform essential
tasks, such as dropping objects when it's no longer needed, borrow checking, etc */
#[global_allocator]
static HV_HEAP: heap::HVallocator = heap::HVallocator;

/* set to true to allow physical CPU cores to start running supervisor code */
lazy_static!
{
    static ref INIT_DONE: Mutex<bool> = Mutex::new(false);
}

/* a physical CPU core obtaining this lock when it is false must walk the DMFS, create
capsules required to run at boot time, and set the flag to true. any other core
obtaining it as true must release the lock and move on */
lazy_static!
{
    static ref MANIFEST_UNPACKED: Mutex<bool> = Mutex::new(false);
}

/* set to true if individual cores can sound off their presence and capabilities */
lazy_static!
{
    static ref ROLL_CALL: Mutex<bool> = Mutex::new(false);
}

/* pointer sizes: do not assume this is a 32-bit or 64-bit system. it could be either.
in future, we may support 16- or 128-bit, too. stick to usize as much as possible */

/* hventry
   This is the official entry point of the Rust-level hypervisor.
   Call hvmain, which is where all the real work happens, and catch any errors.
   => cpu_nr = this boot-assigned CPU ID number
      dtb_ptr = pointer to start of device tree blob structure
      dtb_len = 32-bit big-endian length of the device tree blob
   <= return to infinite loop, awaiting interrupts */
#[no_mangle]
pub extern "C" fn hventry(cpu_nr: PhysicalCoreID, dtb_ptr: *const u8, dtb_len: u32)
{
    /* carry out tests if that's what we're here for */
    #[cfg(test)]
    hvtests();

    /* if not performing tests, start the system as normal */
    match hvmain(cpu_nr, dtb_ptr, dtb_len)
    {
        Err(e) =>
        {
            hvalert!("Hypervisor failed to start. Reason: {:?}", e);
            debughousekeeper!(); /* attempt to flush queued debug to output */
        },
        _ => () /* continue waiting for an IRQ to come in */
    }
}

/* hvmain
   This code runs at the hypervisor level, with full physical memory access.
   Its job is to initialize physical CPU cores and other resources so that capsules can be
   created in which supervisors run that manage their own user spaces, in which
   applications run. The hypervisor ensures capsules are kept apart using
   hardware protections.

   Assumes all physical CPU cores enter this function during startup.
   The boot CPU is chosen to initialize the system in pre-SMP mode.
   If we're on a single CPU core then everything should still run OK.
   Assumes hardware and exception interrupts are enabled and handlers
   installed.

   Also assumes all CPU cores are compatible ISA-wise. There is provision
   for marking some cores as more powerful than others for systems with
   a mix of performance and efficiency CPU cores.

   => cpu_nr = arbitrary CPU core ID number assigned by boot code,
               separate from hardware ID number.
               BOOT_PCORE_ID = boot CPU core.
      dtb_ptr = pointer to device tree in memory from bootlaoder
      dtb_len = 32-bit big endian size of the device tree
   <= return to infinite loop, waiting for interrupts
*/
fn hvmain(cpu_nr: PhysicalCoreID, dtb_ptr: *const u8, dtb_len: u32) -> Result<(), Cause>
{
    /* set up each physical processor core with its own private heap pool and any other resources.
    each private pool uses physical memory assigned by the pre-hvmain boot code. init() should be called
    first thing to set up each processor core, including the boot CPU, which then sets up the global
    resources. all non-boot CPUs should wait until global resources are ready. */
    pcore::PhysicalCore::init(cpu_nr);

    /* note that pre-physmem::init(), CPU cores rely on their pre-hventry()-assigned
    heap space. after physmem::init(), CPU cores can extend their heaps using physical memory.
    the hypervisor will become stuck pre-physmem::init() if it goes beyond its assigned heap space. */

    match cpu_nr
    {
        /* delegate to boot CPU the welcome banner and set up global resources.
        note: the platform code should ensure whichever CPU core is assigned
        BOOT_PCORE_ID as its cpu_nr can initialize the hypervisor */
        BOOT_PCORE_ID =>
        {
            /* convert the dtb pointer into a rust byte slice. assumes dtb_len is valid */
            let dtb = unsafe { slice::from_raw_parts(dtb_ptr, u32::from_be(dtb_len) as usize) };

            /* process device tree to create data structures representing system hardware,
            allowing these peripherals to be accessed by subsequent routines. this should
            also initialize any found hardware */
            hardware::parse_and_init(dtb)?;

            /* register all the available physical RAM */
            physmem::init()?;
            describe_system();

            /* allow other cores to continue */
            *(INIT_DONE.lock()) = true;
        },

        /* non-boot cores must wait here for early initialization to complete */
        _ => while *(INIT_DONE.lock()) != true {}
    }

    /* we're now ready to start creating capsules to run from the bundled DMFS image.
    the hypervisor can't make any assumptions about the underlying hardware.
    the device tree for these early capsules is derived from the host's device tree,
    modified to virtualize the peripherals. the virtual CPU cores that will run the
    capsule are based on the physical CPU core that creates it. ensure a suitable
    physical CPU core creates the boot-time capsules. this is more straightforward
    than the hypervisor trying to specify a hypothetical CPU core */
    if pcore::PhysicalCore::smode_supported() == true
    {
        let mut flag = MANIFEST_UNPACKED.lock();

        if *flag == false
        {
            /* process the manifest and mark it as handled */
            process_manifest()?;
            *flag = true;

            /* allow all working cores to join the roll call */
            *(ROLL_CALL.lock()) = true;
        }
    }

    /* once ROLL_CALL is set to true, acknowledge we're alive and well, and report CPU core features */
    while *(ROLL_CALL.lock()) != true {}
    hvdebug!("Physical CPU core {:?} ready to roll", pcore::PhysicalCore::describe());

    /* enable timer on this physical CPU core to start scheduling and running virtual cores */
    scheduler::start()?;

    /* initialization complete. fall through to infinite loop waiting for a timer interrupt
    to come in. when it does fire, this stack will be flattened, a virtual CPU loaded up to run,
    and this boot thread will disappear. thus, the call to start() should be the last thing
    this boot thread does */
    Ok(())
}

fn process_manifest() -> Result<(), Cause>
{
    /* convert the included dmfs image into a byte slice */
    let image = unsafe
    {
        slice::from_raw_parts
        (
            transmute(&_binary_dmfs_img_start),
            transmute(&_binary_dmfs_img_size)
        )
    };

    /* process the contents of the DMFS image, including
    printing to the debug port any messages and creating
    capsules to run system services and supplied guests */
    manifest::unpack(image)?;
    Ok(())
}

/* dump system information to the user */
fn describe_system()
{
    const KILOBYTE: usize = 1024;
    const MEGABYTE: usize = KILOBYTE * KILOBYTE;
    const GIGABYTE: usize = KILOBYTE * MEGABYTE;

    /* say hello via the debug port with some information */
    hvdebug!("Diosix {} :: Debug enabled. {} and {} RAM found",

        /* build version number */
        env!("CARGO_PKG_VERSION"),

        /* report number of CPU cores found */
        match hardware::get_nr_cpu_cores()
        {
            None | Some(0) => format!("no CPU cores"),
            Some(1) => format!("1 CPU core"),
            Some(c) => format!("{} CPU cores", c)
        },

        /* count up total system RAM using GiB / MiB / KiB */
        match hardware::get_phys_ram_total()
        {
            Some(t) => if t >= GIGABYTE
            {
                format!("{} GiB", t / GIGABYTE)
            }
            else if t >= MEGABYTE
            {
                format!("{} MiB", t / MEGABYTE)
            }
            else
            {
                format!("{} KiB", t / KILOBYTE)
            },

            None => format!("no")
    });
}

/* mandatory error handler for memory allocations */
#[alloc_error_handler]
fn hvalloc_error(attempt: core::alloc::Layout) -> !
{
    let heap = &(*<pcore::PhysicalCore>::this()).heap;
    hvalert!("hvalloc_error: Failed to allocate/free {} bytes. Heap: {:?}", attempt.size(), heap);
    debughousekeeper!();
    loop {} /* it would be nice to be able to not die here :( */
}

/* perform all unit tests required */
#[cfg(test)]
fn run_tests(unit_tests: &[&dyn Fn()])
{
    /* run each test one by one */
    for test in unit_tests
    {
        test();
    }

    /* exit cleanly once tests are complete */
    platform::test::end(Ok(0));
}

#[test_case]
fn test_assertion()
{
    assert_eq!(42, 42);
}<commit_msg>Move manifest code out of main<commit_after>/* diosix hypervisor main entry code
 *
 * (c) Chris Williams, 2019-2021.
 *
 * See LICENSE for usage and copying.
 */

/* let the compiler know we're on our own here in bare-metal world */
#![no_std]
#![no_main]
#![feature(asm)]

#![allow(dead_code)]
#![allow(unused_unsafe)]
#![allow(improper_ctypes)]
#![feature(type_ascription)]

/* provide a framework for unit testing */
#![feature(custom_test_frameworks)]
#![test_runner(crate::run_tests)]
#![reexport_test_harness_main = "hvtests"] /* entry point for tests */

/* plug our custom heap allocator into the Rust language: Box, etc */
#![feature(alloc_error_handler)]
#![feature(box_syntax)]
#[allow(unused_imports)]
#[macro_use]
extern crate alloc;

/* needed to convert raw dtb pointer into a slice */
use core::slice;

/* needed for fast lookup tables of stuff */
extern crate hashbrown;

/* needed for elf parsing */
extern crate xmas_elf;

/* needed for device tree parsing and manipulation */
extern crate devicetree;

/* needed for parsing diosix manifest file-system (DMFS) images bundled with the hypervisor */
extern crate dmfs;

/* pass the feature qemudebug to be able to write direct to the emulator's stdout
via the macro qemuprint::println!() */
#[cfg(feature = "qemudebug")]
extern crate qemuprint;

/* needed for lazyily-allocated static variables, and atomic ops */
#[macro_use]
extern crate lazy_static;
extern crate spin;
use spin::Mutex;

/* this will bring in all the hardware-specific code */
extern crate platform;

/* and now for all our non-hw specific code */
#[macro_use]
mod debug;      /* get us some kind of debug output, typically to a serial port */
mod hardware;   /* parse device trees into hardware objects */
#[macro_use]
mod heap;       /* per-CPU private heap management */
mod panic;      /* implement panic() handlers */
mod irq;        /* handle hw interrupts and sw exceptions, collectively known as IRQs */
#[macro_use]
mod physmem;    /* manage host physical memory */
mod virtmem;    /* manage capsule virtual memory */
mod pcore;      /* manage CPU cores */
mod vcore;      /* virtual CPU core management... */
mod scheduler;  /* ...and scheduling */
mod capsule;    /* manage capsules */
mod loader;     /* parse and load supervisor binaries */
mod message;    /* send messages between physical cores */
mod service;    /* allow capsules to register services */
mod manifest;   /* manage capsules loaded with the hypervisor */

/* list of error codes */
mod error;
use error::Cause;

use pcore::{PhysicalCoreID, BOOT_PCORE_ID};

/* tell Rust to use our HVallocator to allocate and free heap memory.
although we'll keep track of physical memory, we'll let Rust perform essential
tasks, such as dropping objects when it's no longer needed, borrow checking, etc */
#[global_allocator]
static HV_HEAP: heap::HVallocator = heap::HVallocator;

/* set to true to allow physical CPU cores to start running supervisor code */
lazy_static!
{
    static ref INIT_DONE: Mutex<bool> = Mutex::new(false);
}

/* a physical CPU core obtaining this lock when it is false must walk the DMFS, create
capsules required to run at boot time, and set the flag to true. any other core
obtaining it as true must release the lock and move on */
lazy_static!
{
    static ref MANIFEST_UNPACKED: Mutex<bool> = Mutex::new(false);
}

/* set to true if individual cores can sound off their presence and capabilities */
lazy_static!
{
    static ref ROLL_CALL: Mutex<bool> = Mutex::new(false);
}

/* pointer sizes: do not assume this is a 32-bit or 64-bit system. it could be either.
in future, we may support 16- or 128-bit, too. stick to usize as much as possible */

/* hventry
   This is the official entry point of the Rust-level hypervisor.
   Call hvmain, which is where all the real work happens, and catch any errors.
   => cpu_nr = this boot-assigned CPU ID number
      dtb_ptr = pointer to start of device tree blob structure
      dtb_len = 32-bit big-endian length of the device tree blob
   <= return to infinite loop, awaiting interrupts */
#[no_mangle]
pub extern "C" fn hventry(cpu_nr: PhysicalCoreID, dtb_ptr: *const u8, dtb_len: u32)
{
    /* carry out tests if that's what we're here for */
    #[cfg(test)]
    hvtests();

    /* if not performing tests, start the system as normal */
    match hvmain(cpu_nr, dtb_ptr, dtb_len)
    {
        Err(e) =>
        {
            hvalert!("Hypervisor failed to start. Reason: {:?}", e);
            debughousekeeper!(); /* attempt to flush queued debug to output */
        },
        _ => () /* continue waiting for an IRQ to come in */
    }
}

/* hvmain
   This code runs at the hypervisor level, with full physical memory access.
   Its job is to initialize physical CPU cores and other resources so that capsules can be
   created in which supervisors run that manage their own user spaces, in which
   applications run. The hypervisor ensures capsules are kept apart using
   hardware protections.

   Assumes all physical CPU cores enter this function during startup.
   The boot CPU is chosen to initialize the system in pre-SMP mode.
   If we're on a single CPU core then everything should still run OK.
   Assumes hardware and exception interrupts are enabled and handlers
   installed.

   Also assumes all CPU cores are compatible ISA-wise. There is provision
   for marking some cores as more powerful than others for systems with
   a mix of performance and efficiency CPU cores.

   => cpu_nr = arbitrary CPU core ID number assigned by boot code,
               separate from hardware ID number.
               BOOT_PCORE_ID = boot CPU core.
      dtb_ptr = pointer to device tree in memory from bootlaoder
      dtb_len = 32-bit big endian size of the device tree
   <= return to infinite loop, waiting for interrupts
*/
fn hvmain(cpu_nr: PhysicalCoreID, dtb_ptr: *const u8, dtb_len: u32) -> Result<(), Cause>
{
    /* set up each physical processor core with its own private heap pool and any other resources.
    each private pool uses physical memory assigned by the pre-hvmain boot code. init() should be called
    first thing to set up each processor core, including the boot CPU, which then sets up the global
    resources. all non-boot CPUs should wait until global resources are ready. */
    pcore::PhysicalCore::init(cpu_nr);

    /* note that pre-physmem::init(), CPU cores rely on their pre-hventry()-assigned
    heap space. after physmem::init(), CPU cores can extend their heaps using physical memory.
    the hypervisor will become stuck pre-physmem::init() if it goes beyond its assigned heap space. */

    match cpu_nr
    {
        /* delegate to boot CPU the welcome banner and set up global resources.
        note: the platform code should ensure whichever CPU core is assigned
        BOOT_PCORE_ID as its cpu_nr can initialize the hypervisor */
        BOOT_PCORE_ID =>
        {
            /* convert the dtb pointer into a rust byte slice. assumes dtb_len is valid */
            let dtb = unsafe { slice::from_raw_parts(dtb_ptr, u32::from_be(dtb_len) as usize) };

            /* process device tree to create data structures representing system hardware,
            allowing these peripherals to be accessed by subsequent routines. this should
            also initialize any found hardware */
            hardware::parse_and_init(dtb)?;

            /* register all the available physical RAM */
            physmem::init()?;
            describe_system();

            /* allow other cores to continue */
            *(INIT_DONE.lock()) = true;
        },

        /* non-boot cores must wait here for early initialization to complete */
        _ => while *(INIT_DONE.lock()) != true {}
    }

    /* we're now ready to start creating capsules to run from the bundled DMFS image.
    the hypervisor can't make any assumptions about the underlying hardware.
    the device tree for these early capsules is derived from the host's device tree,
    modified to virtualize the peripherals. the virtual CPU cores that will run the
    capsule are based on the physical CPU core that creates it. ensure a suitable
    physical CPU core creates the boot-time capsules. this is more straightforward
    than the hypervisor trying to specify a hypothetical CPU core */
    if pcore::PhysicalCore::smode_supported() == true
    {
        let mut flag = MANIFEST_UNPACKED.lock();

        if *flag == false
        {
            /* process the manifest and mark it as handled */
            process_manifest()?;
            *flag = true;

            /* allow all working cores to join the roll call */
            *(ROLL_CALL.lock()) = true;
        }
    }

    /* once ROLL_CALL is set to true, acknowledge we're alive and well, and report CPU core features */
    while *(ROLL_CALL.lock()) != true {}
    hvdebug!("Physical CPU core {:?} ready to roll", pcore::PhysicalCore::describe());

    /* enable timer on this physical CPU core to start scheduling and running virtual cores */
    scheduler::start()?;

    /* initialization complete. fall through to infinite loop waiting for a timer interrupt
    to come in. when it does fire, this stack will be flattened, a virtual CPU loaded up to run,
    and this boot thread will disappear. thus, the call to start() should be the last thing
    this boot thread does */
    Ok(())
}

fn process_manifest() -> Result<(), Cause>
{
    /* process the contents of the DMFS image, including
    printing to the debug port any messages and creating
    capsules to run system services and supplied guests */
    manifest::unpack_at_boot()?;
    Ok(())
}

/* dump system information to the user */
fn describe_system()
{
    const KILOBYTE: usize = 1024;
    const MEGABYTE: usize = KILOBYTE * KILOBYTE;
    const GIGABYTE: usize = KILOBYTE * MEGABYTE;

    /* say hello via the debug port with some information */
    hvdebug!("Diosix {} :: Debug enabled. {} and {} RAM found",

        /* build version number */
        env!("CARGO_PKG_VERSION"),

        /* report number of CPU cores found */
        match hardware::get_nr_cpu_cores()
        {
            None | Some(0) => format!("no CPU cores"),
            Some(1) => format!("1 CPU core"),
            Some(c) => format!("{} CPU cores", c)
        },

        /* count up total system RAM using GiB / MiB / KiB */
        match hardware::get_phys_ram_total()
        {
            Some(t) => if t >= GIGABYTE
            {
                format!("{} GiB", t / GIGABYTE)
            }
            else if t >= MEGABYTE
            {
                format!("{} MiB", t / MEGABYTE)
            }
            else
            {
                format!("{} KiB", t / KILOBYTE)
            },

            None => format!("no")
    });
}

/* mandatory error handler for memory allocations */
#[alloc_error_handler]
fn hvalloc_error(attempt: core::alloc::Layout) -> !
{
    let heap = &(*<pcore::PhysicalCore>::this()).heap;
    hvalert!("hvalloc_error: Failed to allocate/free {} bytes. Heap: {:?}", attempt.size(), heap);
    debughousekeeper!();
    loop {} /* it would be nice to be able to not die here :( */
}

/* perform all unit tests required */
#[cfg(test)]
fn run_tests(unit_tests: &[&dyn Fn()])
{
    /* run each test one by one */
    for test in unit_tests
    {
        test();
    }

    /* exit cleanly once tests are complete */
    platform::test::end(Ok(0));
}

#[test_case]
fn test_assertion()
{
    assert_eq!(42, 42);
}<|endoftext|>


<commit_before>use crate::cached_size::CachedSize;
use crate::well_known_types::Timestamp;
use crate::UnknownFields;
use std::time::{Duration, SystemTime};

impl Timestamp {
    /// Unix epoch value of timestamp.
    pub const UNIX_EPOCH: Timestamp = Timestamp {
        seconds: 0,
        nanos: 0,
        unknown_fields: UnknownFields::INIT,
        cached_size: CachedSize::INIT,
    };
}

/// Convert from [`Timestamp`].
///
/// # Panics
///
/// This function panics if given `SystemTime` is outside of `Timestamp` range.
impl From<SystemTime> for Timestamp {
    fn from(time: SystemTime) -> Self {
        match time.duration_since(SystemTime::UNIX_EPOCH) {
            Ok(since_epoch) => Timestamp {
                seconds: since_epoch.as_secs() as i64,
                nanos: since_epoch.subsec_nanos() as i32,
                ..Default::default()
            },
            Err(e) => {
                let before_epoch = e.duration();
                Timestamp {
                    seconds: -(before_epoch.as_secs() as i64)
                        - (before_epoch.subsec_nanos() != 0) as i64,
                    nanos: (1_000_000_000 - before_epoch.subsec_nanos() as i32) % 1_000_000_000,
                    ..Default::default()
                }
            }
        }
    }
}

/// Convert into [`SystemTime`].
///
/// The conversion could be lossy if `SystemTime` precision is smaller than nanoseconds.
///
/// # Panics
///
/// This function panics:
/// * if given `Timestamp` is outside of `SystemTime` range
/// * if `Timestamp` is malformed
impl Into<SystemTime> for Timestamp {
    fn into(self) -> SystemTime {
        if self.seconds >= 0 {
            let duration =
                Duration::from_secs(self.seconds as u64) + Duration::from_nanos(self.nanos as u64);
            SystemTime::UNIX_EPOCH + duration
        } else {
            let duration =
                Duration::from_secs(-self.seconds as u64) - Duration::from_nanos(self.nanos as u64);
            SystemTime::UNIX_EPOCH - duration
        }
    }
}

#[cfg(test)]
mod test {
    use crate::well_known_types::Timestamp;
    use std::time::Duration;
    use std::time::SystemTime;

    #[test]
    fn from_system_time() {
        fn to_from(timestamp: Timestamp, system_time: SystemTime) {
            assert_eq!(timestamp, Timestamp::from(system_time));
            assert_eq!(system_time, Into::<SystemTime>::into(timestamp));
        }

        to_from(Timestamp::UNIX_EPOCH, SystemTime::UNIX_EPOCH);
        to_from(
            Timestamp {
                seconds: 0,
                nanos: 200_000_000,
                ..Default::default()
            },
            SystemTime::UNIX_EPOCH + Duration::from_millis(200),
        );
        to_from(
            Timestamp {
                seconds: 3,
                nanos: 200_000_000,
                ..Default::default()
            },
            SystemTime::UNIX_EPOCH + Duration::from_millis(3_200),
        );
        to_from(
            Timestamp {
                seconds: -1,
                nanos: 800_000_000,
                ..Default::default()
            },
            SystemTime::UNIX_EPOCH - Duration::from_millis(200),
        );
        to_from(
            Timestamp {
                seconds: -4,
                nanos: 800_000_000,
                ..Default::default()
            },
            SystemTime::UNIX_EPOCH - Duration::from_millis(3_200),
        );
    }
}
<commit_msg>Timestamp::now<commit_after>use crate::cached_size::CachedSize;
use crate::well_known_types::Timestamp;
use crate::UnknownFields;
use std::time::{Duration, SystemTime};

impl Timestamp {
    /// Unix epoch value of timestamp.
    pub const UNIX_EPOCH: Timestamp = Timestamp {
        seconds: 0,
        nanos: 0,
        unknown_fields: UnknownFields::INIT,
        cached_size: CachedSize::INIT,
    };

    /// Return current time as `Timestamp`.
    pub fn now() -> Timestamp {
        Timestamp::from(SystemTime::now())
    }
}

/// Convert from [`Timestamp`].
///
/// # Panics
///
/// This function panics if given `SystemTime` is outside of `Timestamp` range.
impl From<SystemTime> for Timestamp {
    fn from(time: SystemTime) -> Self {
        match time.duration_since(SystemTime::UNIX_EPOCH) {
            Ok(since_epoch) => Timestamp {
                seconds: since_epoch.as_secs() as i64,
                nanos: since_epoch.subsec_nanos() as i32,
                ..Default::default()
            },
            Err(e) => {
                let before_epoch = e.duration();
                Timestamp {
                    seconds: -(before_epoch.as_secs() as i64)
                        - (before_epoch.subsec_nanos() != 0) as i64,
                    nanos: (1_000_000_000 - before_epoch.subsec_nanos() as i32) % 1_000_000_000,
                    ..Default::default()
                }
            }
        }
    }
}

/// Convert into [`SystemTime`].
///
/// The conversion could be lossy if `SystemTime` precision is smaller than nanoseconds.
///
/// # Panics
///
/// This function panics:
/// * if given `Timestamp` is outside of `SystemTime` range
/// * if `Timestamp` is malformed
impl Into<SystemTime> for Timestamp {
    fn into(self) -> SystemTime {
        if self.seconds >= 0 {
            let duration =
                Duration::from_secs(self.seconds as u64) + Duration::from_nanos(self.nanos as u64);
            SystemTime::UNIX_EPOCH + duration
        } else {
            let duration =
                Duration::from_secs(-self.seconds as u64) - Duration::from_nanos(self.nanos as u64);
            SystemTime::UNIX_EPOCH - duration
        }
    }
}

#[cfg(test)]
mod test {
    use crate::well_known_types::Timestamp;
    use std::time::Duration;
    use std::time::SystemTime;

    #[test]
    fn from_system_time() {
        fn to_from(timestamp: Timestamp, system_time: SystemTime) {
            assert_eq!(timestamp, Timestamp::from(system_time));
            assert_eq!(system_time, Into::<SystemTime>::into(timestamp));
        }

        to_from(Timestamp::UNIX_EPOCH, SystemTime::UNIX_EPOCH);
        to_from(
            Timestamp {
                seconds: 0,
                nanos: 200_000_000,
                ..Default::default()
            },
            SystemTime::UNIX_EPOCH + Duration::from_millis(200),
        );
        to_from(
            Timestamp {
                seconds: 3,
                nanos: 200_000_000,
                ..Default::default()
            },
            SystemTime::UNIX_EPOCH + Duration::from_millis(3_200),
        );
        to_from(
            Timestamp {
                seconds: -1,
                nanos: 800_000_000,
                ..Default::default()
            },
            SystemTime::UNIX_EPOCH - Duration::from_millis(200),
        );
        to_from(
            Timestamp {
                seconds: -4,
                nanos: 800_000_000,
                ..Default::default()
            },
            SystemTime::UNIX_EPOCH - Duration::from_millis(3_200),
        );
    }
}
<|endoftext|>


<commit_before>#![no_std]

extern crate alloc;
extern crate lang_items;
extern crate libtock_drivers;

use core::fmt::Write;
use libtock_drivers::console::Console;

#[cfg(not(feature = "with_nfc"))]
mod example {
    use super::Console;
    use super::Write;

    pub fn nfc(console: &mut Console) {
        writeln!(console, "NFC feature flag is missing!").unwrap();
    }
}

#[cfg(feature = "with_nfc")]
mod example {
    use super::Console;
    use super::Write;
    use libtock_core::result::CommandError;
    use libtock_drivers::nfc::NfcTag;
    use libtock_drivers::nfc::RecvOp;
    use libtock_drivers::result::FlexUnwrap;
    use libtock_drivers::result::TockError;
    use libtock_drivers::timer;
    use libtock_drivers::timer::Timer;
    use libtock_drivers::timer::Timestamp;

    /// Helper function to write on console the received packet.
    fn print_rx_buffer(buf: &mut [u8]) {
        let mut console = Console::new();
        write!(console, "RX:").unwrap();
        if let Some((last, bytes)) = buf.split_last() {
            for byte in bytes {
                write!(console, " {:02x?}", byte).unwrap();
            }
            writeln!(console, " {:02x?}", last).unwrap();
        }
        console.flush();
    }

    /// Function to identify the time elapsed for a transmission request.
    fn bench_transmit(console: &mut Console, timer: &Timer, title: &str, mut buf: &mut [u8]) {
        let amount = buf.len();
        let start = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
        match NfcTag::transmit(&mut buf, amount) {
            Ok(_) => (),
            Err(_) => writeln!(Console::new(), " -- tx error!").unwrap(),
        }
        let end = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
        let elapsed = (end - start).ms();
        writeln!(
            console,
            "{}\n{:.2} ms elapsed for {} bytes ({:.2} kbit/s)",
            title,
            elapsed,
            amount,
            (amount as f64) / elapsed * 8.
        )
        .unwrap();
        console.flush();
    }

    fn receive_packet(console: &mut Console, mut buf: &mut [u8; 256]) {
        match NfcTag::receive(&mut buf) {
            Ok(RecvOp {
                recv_amount: amount,
                ..
            }) => {
                if amount > 0 && amount <= buf.len() {
                    print_rx_buffer(&mut buf[..amount]);
                }
            }
            Err(TockError::Command(CommandError {
                return_code: -4, /* EOFF: Not Ready */
                ..
            })) => (),
            Err(TockError::Command(CommandError {
                return_code: value, ..
            })) => writeln!(console, " -- Err({})!", value).unwrap(),
            Err(_) => writeln!(console, " -- RX Err").unwrap(),
        }
    }

    fn transmit_reply(mut console: &mut Console, timer: &Timer, buf: &[u8]) -> bool {
        match buf[0] {
            0xe0 /* RATS */=> {
                let mut answer_to_select = [0x05, 0x78, 0x80, 0xB1, 0x00];
                bench_transmit(&mut console, &timer, "TX: ATS", &mut answer_to_select);
            }
            0xc2 /* DESELECT */ => {
                // Ignore the request
                let mut command_error = [0x6A, 0x81];
                bench_transmit(&mut console, &timer, "TX: DESELECT", &mut command_error);
            }
            0x02 | 0x03 /* APDU Prefix */ => match buf[2] {
                // If the received packet is applet selection command (FIDO 2)
                0xa4 /* SELECT */ => if buf[3] == 0x04 && buf[5] == 0x08 && buf[6] == 0xa0 {
                        // Vesion: "FIDO_2_0"
                        let mut reply = [buf[0], 0x46, 0x49, 0x44, 0x4f, 0x5f, 0x32, 0x5f, 0x30, 0x90, 0x00,];
                        bench_transmit(&mut console, &timer, "TX: Version Str", &mut reply);
                    } else {
                        let mut reply = [buf[0], 0x90, 0x00];
                        bench_transmit(&mut console, &timer, "TX: 0x9000", &mut reply);
                    }
                0xb0 /* READ */ =>  match buf[5] {
                        0x02 => {
                        let mut reply = [buf[0], 0x12, 0x90, 0x00,];
                        bench_transmit(&mut console, &timer, "TX: File Size", &mut reply);
                    }
                    0x12 => {
                        let mut reply = [buf[0], 0xd1, 0x01, 0x0e, 0x55, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x70, 0x65,
                        0x6e, 0x73, 0x6b, 0x2e, 0x64, 0x65, 0x76, 0x90, 0x00,];
                        bench_transmit(&mut console, &timer, "TX: NDEF", &mut reply);
                    }
                    0x0f => {
                        let mut reply = [buf[0], 0x00, 0x0f, 0x20, 0x00, 0x7f, 0x00, 0x7f, 0x04, 0x06, 0xe1, 0x04,
                        0x00, 0x7f, 0x00, 0x00, 0x90, 0x00,];
                        bench_transmit(&mut console, &timer, "TX: CC", &mut reply);
                    }
                    _ => {
                        let mut reply = [buf[0], 0x90, 0x00];
                        bench_transmit(&mut console, &timer, "TX: 0x9000", &mut reply);
                    }
                }
                _ => {
                    let mut reply = [buf[0], 0x90, 0x00];
                    bench_transmit(&mut console, &timer, "TX: 0x9000", &mut reply);
                }
            }
            0x26 | 0x52 | 0x50 /* REQA | WUPA | Halt */ => {
                if NfcTag::disable_emulation() {
                    writeln!(console, " -- TAG DISABLED").unwrap();
                }
                return false;
            }
            _ => (),
        }
        true
    }

    pub fn nfc(mut console: &mut Console) {
        // Setup the timer with a dummy callback (we only care about reading the current time, but the
        // API forces us to set an alarm callback too).
        let mut with_callback = timer::with_callback(|_, _| {});
        let timer = with_callback.init().flex_unwrap();

        writeln!(
            console,
            "Clock frequency: {} Hz",
            timer.clock_frequency().hz()
        )
        .unwrap();

        let mut is_enabled = false;
        let mut state_change_counter = 0;
        loop {
            if is_enabled {
                let mut rx_buf = [0; 256];
                loop {
                    receive_packet(&mut console, &mut rx_buf);
                    // If the reader restarts the communication and we can't
                    // reply to the received packet, then disable the tag.
                    if !transmit_reply(&mut console, &timer, &rx_buf) {
                        is_enabled = false;
                        break;
                    }
                }
            } else {
                NfcTag::enable_emulation();
                // Configure Type 4 tag
                if NfcTag::configure(4) {
                    is_enabled = true;
                }
            }
            state_change_counter += 1;
            if !is_enabled && state_change_counter > 100 {
                break;
            }
        }
    }
}

fn main() {
    let mut console = Console::new();
    writeln!(console, "****************************************").unwrap();
    writeln!(console, "nfct_test application is installed").unwrap();
    example::nfc(&mut console);
    writeln!(console, "****************************************").unwrap();
}
<commit_msg>Updated control flow + cleaned some code<commit_after>#![no_std]

extern crate alloc;
extern crate lang_items;
extern crate libtock_drivers;

use core::fmt::Write;
use libtock_drivers::console::Console;

#[cfg(not(feature = "with_nfc"))]
mod example {
    use super::Console;
    use super::Write;

    pub fn nfc(console: &mut Console) {
        writeln!(console, "NFC feature flag is missing!").unwrap();
    }
}

#[cfg(feature = "with_nfc")]
mod example {
    use super::Console;
    use super::Write;
    use libtock_core::result::CommandError;
    use libtock_drivers::nfc::NfcTag;
    use libtock_drivers::nfc::RecvOp;
    use libtock_drivers::result::FlexUnwrap;
    use libtock_drivers::result::TockError;
    use libtock_drivers::timer;
    use libtock_drivers::timer::Timer;
    use libtock_drivers::timer::Timestamp;

    /// Helper function to write on console the received packet.
    fn print_rx_buffer(buf: &mut [u8]) {
        if let Some((last, bytes)) = buf.split_last() {
            let mut console = Console::new();
            write!(console, "RX:").unwrap();
            for byte in bytes {
                write!(console, " {:02x?}", byte).unwrap();
            }
            writeln!(console, " {:02x?}", last).unwrap();
            console.flush();
        }
    }

    /// Function to identify the time elapsed for a transmission request.
    fn bench_transmit(console: &mut Console, timer: &Timer, title: &str, mut buf: &mut [u8]) {
        let amount = buf.len();
        let start = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
        match NfcTag::transmit(&mut buf, amount) {
            Ok(_) => (),
            Err(_) => writeln!(Console::new(), " -- tx error!").unwrap(),
        }
        let end = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
        let elapsed = (end - start).ms();
        writeln!(
            console,
            "{}\n{:.2} ms elapsed for {} bytes ({:.2} kbit/s)",
            title,
            elapsed,
            amount,
            (amount as f64) / elapsed * 8.
        )
        .unwrap();
        console.flush();
    }

    fn receive_packet(console: &mut Console, mut buf: &mut [u8; 256]) -> bool {
        match NfcTag::receive(&mut buf) {
            Ok(RecvOp {
                recv_amount: amount,
                ..
            }) => {
                if amount <= buf.len() {
                    print_rx_buffer(&mut buf[..amount]);
                }
            }
            Err(TockError::Command(CommandError {
                return_code: -4, /* EOFF: Not Ready */
                ..
            })) => return false,
            // For the example app, just print any other received error without handling.
            Err(TockError::Command(CommandError {
                return_code: value, ..
            })) => writeln!(console, " -- Err({})!", value).unwrap(),
            Err(_) => writeln!(console, " -- RX Err").unwrap(),
        }
        true
    }

    fn transmit_reply(mut console: &mut Console, timer: &Timer, buf: &[u8]) -> bool {
        match buf[0] {
            0xe0 /* RATS */=> {
                let mut answer_to_select = [0x05, 0x78, 0x80, 0xB1, 0x00];
                bench_transmit(&mut console, &timer, "TX: ATS", &mut answer_to_select);
            }
            0xc2 /* DESELECT */ => {
                // Ignore the request
                let mut command_error = [0x6A, 0x81];
                bench_transmit(&mut console, &timer, "TX: DESELECT", &mut command_error);
            }
            0x02 | 0x03 /* APDU Prefix */ => match buf[2] {
                // If the received packet is applet selection command (FIDO 2)
                0xa4 /* SELECT */ => if buf[3] == 0x04 && buf[5] == 0x08 && buf[6] == 0xa0 {
                        // Vesion: "FIDO_2_0"
                        let mut reply = [buf[0], 0x46, 0x49, 0x44, 0x4f, 0x5f, 0x32, 0x5f, 0x30, 0x90, 0x00,];
                        bench_transmit(&mut console, &timer, "TX: Version Str", &mut reply);
                    } else {
                        let mut reply = [buf[0], 0x90, 0x00];
                        bench_transmit(&mut console, &timer, "TX: 0x9000", &mut reply);
                    }
                0xb0 /* READ */ =>  match buf[5] {
                        0x02 => {
                        let mut reply = [buf[0], 0x12, 0x90, 0x00,];
                        bench_transmit(&mut console, &timer, "TX: File Size", &mut reply);
                    }
                    0x12 => {
                        let mut reply = [buf[0], 0xd1, 0x01, 0x0e, 0x55, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x70, 0x65,
                        0x6e, 0x73, 0x6b, 0x2e, 0x64, 0x65, 0x76, 0x90, 0x00,];
                        bench_transmit(&mut console, &timer, "TX: NDEF", &mut reply);
                    }
                    0x0f => {
                        let mut reply = [buf[0], 0x00, 0x0f, 0x20, 0x00, 0x7f, 0x00, 0x7f, 0x04, 0x06, 0xe1, 0x04,
                        0x00, 0x7f, 0x00, 0x00, 0x90, 0x00,];
                        bench_transmit(&mut console, &timer, "TX: CC", &mut reply);
                    }
                    _ => {
                        let mut reply = [buf[0], 0x90, 0x00];
                        bench_transmit(&mut console, &timer, "TX: 0x9000", &mut reply);
                    }
                }
                _ => {
                    let mut reply = [buf[0], 0x90, 0x00];
                    bench_transmit(&mut console, &timer, "TX: 0x9000", &mut reply);
                }
            }
            0x26 | 0x52 | 0x50 /* REQA | WUPA | Halt */ => {
                if NfcTag::disable_emulation() {
                    writeln!(console, " -- TAG DISABLED").unwrap();
                }
                return false;
            }
            _ => (),
        }
        true
    }

    pub fn nfc(mut console: &mut Console) {
        // Setup the timer with a dummy callback (we only care about reading the current time, but the
        // API forces us to set an alarm callback too).
        let mut with_callback = timer::with_callback(|_, _| {});
        let timer = with_callback.init().flex_unwrap();

        writeln!(
            console,
            "Clock frequency: {} Hz",
            timer.clock_frequency().hz()
        )
        .unwrap();

        let mut state_change_counter = 0;
        loop {
            while !NfcTag::enable_emulation() {}
            // Configure Type 4 tag
            while !NfcTag::configure(4) {}
            state_change_counter += 1;
            loop {
                let mut rx_buf = [0; 256];
                // Await a successful receive
                while !receive_packet(&mut console, &mut rx_buf) {}
                // If the reader restarts the communication and we can't
                // reply to the received packet, then disable the tag.
                if !transmit_reply(&mut console, &timer, &rx_buf) {
                    state_change_counter += 1;
                    break;
                }
            }
            if state_change_counter > 100 {
                break;
            }
        }
    }
}

fn main() {
    let mut console = Console::new();
    writeln!(console, "****************************************").unwrap();
    writeln!(console, "nfct_test application is installed").unwrap();
    example::nfc(&mut console);
    writeln!(console, "****************************************").unwrap();
}
<|endoftext|>


<commit_before>
extern crate time;
extern crate serial;
extern crate plugwise;

use std::io::prelude::*;
use plugwise::protocol::*;
use plugwise::stub;
use std::io;
use time::Duration;
use serial::prelude::*;
use std::rc::Rc;
use std::cell::RefCell;

struct PlugwiseInner<'a, I> {
    protocol: Rc<RefCell<Protocol<'a, I>>>
}

struct CircleInner<'a, I> {
    protocol: Rc<RefCell<Protocol<'a, I>>>,
    mac: u64,
    calibration_data: ResCalibration
}

impl<'a, I: Read+Write+'a> PlugwiseInner<'a, I> {
    fn initialize(port: I) -> io::Result<PlugwiseInner<'a, I>> {
        let plugwise = PlugwiseInner {
            protocol: Rc::new(RefCell::new(Protocol::new(port)))
        };

        let result = try!(plugwise.protocol.borrow_mut().initialize());

        if !result.is_online {
            return Err(io::Error::new(io::ErrorKind::Other, "not online"));
        }

        Ok(plugwise)
    }
}

trait Plugwise<'a> {
    fn create_circle(&self, mac: u64) -> io::Result<Box<Circle + 'a>>;
    fn set_snoop(&self, snoop: ProtocolSnoop<'a>);
}

trait Circle {
    fn switch_on(&self) -> io::Result<()>;
    fn switch_off(&self) -> io::Result<()>;
}

impl<'a, I:Read+Write+'a> Plugwise<'a> for PlugwiseInner<'a, I> {
    fn create_circle(&self, mac: u64) -> io::Result<Box<Circle+ 'a>> {
        let calibration_data = try!(self.protocol.borrow_mut().calibrate(mac));
        Ok(Box::new(CircleInner {
            protocol: self.protocol.clone(),
            mac: mac,
            calibration_data: calibration_data
        }))
    }

    fn set_snoop(&self, snoop: ProtocolSnoop<'a>) {
        self.protocol.borrow_mut().set_snoop(snoop);
    }
}

impl<'a, I:Read+Write+'a> Circle for CircleInner<'a, I> {
    fn switch_on(&self) -> io::Result<()> {
        try!(self.protocol.borrow_mut().switch(self.mac, true));
        Ok(())
    }

    fn switch_off(&self) -> io::Result<()> {
        try!(self.protocol.borrow_mut().switch(self.mac, false));
        Ok(())
    }
}

fn plugwise_device<'a>(device: &str) -> io::Result<Box<Plugwise<'a>+ 'a>> {
    let mut port = try!(serial::open(device));
    try!(port.configure(|settings| {
        settings.set_baud_rate(serial::Baud115200);
        settings.set_char_size(serial::Bits8);
        settings.set_parity(serial::ParityNone);
        settings.set_stop_bits(serial::Stop1);
    }));

    port.set_timeout(Duration::milliseconds(1000));
    let plugwise = try!(PlugwiseInner::initialize(port));

    Ok(Box::new(plugwise))
}

fn plugwise_simulator<'a>() -> io::Result<Box<Plugwise<'a>+ 'a>> {
    let port = stub::Stub::new();

    let plugwise = try!(PlugwiseInner::initialize(port));

    Ok(Box::new(plugwise))
}

fn main() {
    let mut debug = io::stdout();

    //let plugwise = plugwise_device("/dev/ttyUSB0").unwrap();
    let plugwise = plugwise_simulator().unwrap();
    plugwise.set_snoop(ProtocolSnoop::Debug(&mut debug));
    let circle = plugwise.create_circle(0x0123456789ABCDEF).unwrap();
    circle.switch_on().unwrap();
    circle.switch_off().unwrap();
}
<commit_msg>Add method to test the switch status<commit_after>
extern crate time;
extern crate serial;
extern crate plugwise;

use std::io::prelude::*;
use plugwise::protocol::*;
use plugwise::stub;
use std::io;
use time::Duration;
use serial::prelude::*;
use std::rc::Rc;
use std::cell::RefCell;

struct PlugwiseInner<'a, I> {
    protocol: Rc<RefCell<Protocol<'a, I>>>
}

struct CircleInner<'a, I> {
    protocol: Rc<RefCell<Protocol<'a, I>>>,
    mac: u64,
    calibration_data: ResCalibration
}

impl<'a, I: Read+Write+'a> PlugwiseInner<'a, I> {
    fn initialize(port: I) -> io::Result<PlugwiseInner<'a, I>> {
        let plugwise = PlugwiseInner {
            protocol: Rc::new(RefCell::new(Protocol::new(port)))
        };

        let result = try!(plugwise.protocol.borrow_mut().initialize());

        if !result.is_online {
            return Err(io::Error::new(io::ErrorKind::Other, "not online"));
        }

        Ok(plugwise)
    }
}

trait Plugwise<'a> {
    fn create_circle(&self, mac: u64) -> io::Result<Box<Circle + 'a>>;
    fn set_snoop(&self, snoop: ProtocolSnoop<'a>);
}

trait Circle {
    fn switch_on(&self) -> io::Result<()>;
    fn switch_off(&self) -> io::Result<()>;
    fn is_switched_on(&self) -> io::Result<bool>;
}

impl<'a, I:Read+Write+'a> Plugwise<'a> for PlugwiseInner<'a, I> {
    fn create_circle(&self, mac: u64) -> io::Result<Box<Circle+ 'a>> {
        let calibration_data = try!(self.protocol.borrow_mut().calibrate(mac));
        Ok(Box::new(CircleInner {
            protocol: self.protocol.clone(),
            mac: mac,
            calibration_data: calibration_data
        }))
    }

    fn set_snoop(&self, snoop: ProtocolSnoop<'a>) {
        self.protocol.borrow_mut().set_snoop(snoop);
    }
}

impl<'a, I:Read+Write+'a> Circle for CircleInner<'a, I> {
    fn switch_on(&self) -> io::Result<()> {
        try!(self.protocol.borrow_mut().switch(self.mac, true));
        Ok(())
    }

    fn switch_off(&self) -> io::Result<()> {
        try!(self.protocol.borrow_mut().switch(self.mac, false));
        Ok(())
    }

    fn is_switched_on(&self) -> io::Result<bool> {
        let info = try!(self.protocol.borrow_mut().get_info(self.mac));
        Ok(info.relay_state)
    }
}

fn plugwise_device<'a>(device: &str) -> io::Result<Box<Plugwise<'a>+ 'a>> {
    let mut port = try!(serial::open(device));
    try!(port.configure(|settings| {
        settings.set_baud_rate(serial::Baud115200);
        settings.set_char_size(serial::Bits8);
        settings.set_parity(serial::ParityNone);
        settings.set_stop_bits(serial::Stop1);
    }));

    port.set_timeout(Duration::milliseconds(1000));
    let plugwise = try!(PlugwiseInner::initialize(port));

    Ok(Box::new(plugwise))
}

fn plugwise_simulator<'a>() -> io::Result<Box<Plugwise<'a>+ 'a>> {
    let port = stub::Stub::new();

    let plugwise = try!(PlugwiseInner::initialize(port));

    Ok(Box::new(plugwise))
}

fn main() {
    let mut debug = io::stdout();

    //let plugwise = plugwise_device("/dev/ttyUSB0").unwrap();
    let plugwise = plugwise_simulator().unwrap();
    plugwise.set_snoop(ProtocolSnoop::Debug(&mut debug));
    let circle = plugwise.create_circle(0x0123456789ABCDEF).unwrap();
    circle.switch_on().unwrap();
    println!("{}", circle.is_switched_on().unwrap());
    circle.switch_off().unwrap();
    println!("{}", circle.is_switched_on().unwrap());
}
<|endoftext|>


<commit_before>use scroll::{Cread, Cwrite, Pread, Pwrite, LE};
use scroll_derive::{IOread, IOwrite, Pread, Pwrite, SizeWith};

use scroll::ctx::SizeWith;

macro_rules! test {
    ( struct $name:ident { $( $field:ident: $t:ty, )* } ) => {
        #[derive(Pread)]
        pub struct $name {
            $( pub $field: $t, )*
        }
    };
}

test! {
    struct Test {
        field: [u16; 40],
    }
}

#[derive(Debug, PartialEq, Pread, Pwrite)]
struct Data {
    id: u32,
    timestamp: f64,
}

#[test]
fn test_data() {
    let bytes = [0xefu8, 0xbe, 0xad, 0xde, 0, 0, 0, 0, 0, 0, 224, 63];
    let data: Data = bytes.pread_with(0, LE).unwrap();
    println!("data: {:?}", &data);
    assert_eq!(data.id, 0xdeadbeefu32);
    assert_eq!(data.timestamp, 0.5f64);
    let mut bytes2 = vec![0; ::std::mem::size_of::<Data>()];
    bytes2.pwrite_with(data, 0, LE).unwrap();
    let data: Data = bytes.pread_with(0, LE).unwrap();
    let data2: Data = bytes2.pread_with(0, LE).unwrap();
    assert_eq!(data, data2);
}

#[derive(Debug, PartialEq, Pread, Pwrite)]
struct Data2 {
    name: [u8; 32],
}

#[test]
fn test_array() {
    let bytes = [0u8; 64];
    let data: Data2 = bytes.pread_with(0, LE).unwrap();
    println!("data: {:?}", &data);
}

#[derive(Debug, PartialEq, Pread, Pwrite, SizeWith)]
struct Data3 {
    name: u32,
}

#[test]
fn test_sizewith() {
    let bytes = [0u8; 64];
    let data: Data3 = bytes.gread_with(&mut 0, LE).unwrap();
    println!("data: {:?}", &data);
}

#[derive(Debug, PartialEq, IOread, IOwrite, SizeWith)]
struct Data4 {
    name: u32,
    j: u16,
    arr: [u8; 2],
}

#[test]
fn test_ioread() {
    let bytes = [0, 1, 2, 3, 0xde, 0xed, 0xbe, 0xaf];
    let data: Data4 = bytes.cread_with(0, LE);
    println!("data: {:?}", &data);
    assert_eq!(data.name, 50462976);
    assert_eq!(data.j, 0xedde);
    assert_eq!(data.arr, [0xbe, 0xaf]);
}

#[test]
fn test_iowrite() {
    let bytes = [0, 1, 2, 3, 0xde, 0xed, 0xbe, 0xaf];
    let data: Data4 = bytes.cread_with(0, LE);
    println!("data: {:?}", &data);
    assert_eq!(data.name, 50462976);
    assert_eq!(data.j, 0xedde);
    assert_eq!(data.arr, [0xbe, 0xaf]);

    let mut bytes_null = [0u8; 8];
    bytes_null.cwrite_with(&data, 0, LE);
    println!("bytes_null: {:?}", &bytes_null);
    println!("bytes     : {:?}", &bytes);
    assert_eq!(bytes_null, bytes);

    let mut bytes_null = [0u8; 8];
    bytes_null.cwrite_with(data, 0, LE);
    println!("bytes_null: {:?}", &bytes_null);
    println!("bytes     : {:?}", &bytes);
    assert_eq!(bytes_null, bytes);
}

#[derive(Debug, PartialEq, Pread, SizeWith)]
#[repr(C)]
struct Data5 {
    name: u32,
    j: u16,
    arr1: [u8; 2],
    arr2: [u16; 2],
}

#[test]
fn test_pread_arrays() {
    let bytes = [0, 1, 2, 3, 0, 0, 0xde, 0xed, 0xad, 0xde, 0xef, 0xbe];
    let data: Data5 = bytes.pread_with(0, LE).unwrap();
    println!("data: {:?}", &data);
    assert_eq!(data.name, 50462976);
    assert_eq!(data.arr1, [0xde, 0xed]);
    assert_eq!(data.arr2, [0xdead, 0xbeef]);
    let offset = &mut 0;
    let data: Data5 = bytes.gread_with(offset, LE).unwrap();
    println!("data: {:?}", &data);
    assert_eq!(data.name, 50462976);
    assert_eq!(data.arr1, [0xde, 0xed]);
    assert_eq!(data.arr2, [0xdead, 0xbeef]);
    assert_eq!(*offset, ::std::mem::size_of::<Data5>());
}

#[derive(Debug, PartialEq, Pread, SizeWith)]
#[repr(C)]
struct Data6 {
    id: u32,
    name: [u8; 5],
}

#[test]
fn test_array_copy() {
    let bytes = [0xde, 0xed, 0xef, 0xbe, 0x68, 0x65, 0x6c, 0x6c, 0x0];
    let data: Data6 = bytes.pread_with(0, LE).unwrap();
    let name: &str = data.name.pread(0).unwrap();
    println!("data: {:?}", &data);
    println!("data.name: {:?}", name);
    assert_eq!(data.id, 0xbeefedde);
    assert_eq!(name, "hell");
}

#[derive(Debug, PartialEq, Eq, Pread, Pwrite, SizeWith)]
struct Data7A {
    pub y: u64,
    pub x: u32,
}

#[derive(Debug, PartialEq, Eq, Pread, Pwrite, SizeWith)]
struct Data7B {
    pub a: Data7A,
}

#[test]
fn test_nested_struct() {
    let b = Data7B {
        a: Data7A { y: 1, x: 2 },
    };
    let size = Data7B::size_with(&LE);
    assert_eq!(size, 12);
    let mut bytes = vec![0; size];
    let written = bytes.pwrite_with(&b, 0, LE).unwrap();
    assert_eq!(written, size);
    let mut read = 0;
    let b2: Data7B = bytes.gread_with(&mut read, LE).unwrap();
    assert_eq!(read, size);
    assert_eq!(b, b2);
}

#[derive(Debug, PartialEq, Eq, Pread, Pwrite, IOread, IOwrite, SizeWith)]
#[repr(C)]
struct Data8<T, Y> {
    ids: [T; 3],
    xyz: Y,
}

#[test]
fn test_generics() {
    let mut bytes = [0xde, 0xed, 0xef, 0x10, 0x10];
    let data: Data8<u8, u16> = bytes.pread_with(0, LE).unwrap();
    assert_eq!(data.ids, [0xde, 0xed, 0xef]);
    assert_eq!(data.xyz, 0x1010);
    let data: Data8<u8, u16> = bytes.cread_with(0, LE);
    assert_eq!(data.ids, [0xde, 0xed, 0xef]);
    assert_eq!(data.xyz, 0x1010);
    let size = Data8::<u8, u16>::size_with(&LE);
    let written = bytes.pwrite_with(&data, 0, LE).unwrap();
    assert_eq!(written, size);
}

#[derive(Debug, PartialEq, Eq, Pread, Pwrite, IOread, IOwrite, SizeWith)]
struct Data9(u8, u16);

#[test]
fn test_newtype() {
    let mut bytes = [0xde, 0x10, 0x10];
    let data: Data9 = bytes.pread_with(0, LE).unwrap();
    assert_eq!(data.0, 0xde);
    assert_eq!(data.1, 0x1010);
    let data: Data9 = bytes.cread_with(0, LE);
    assert_eq!(data.0, 0xde);
    assert_eq!(data.1, 0x1010);
    let size = Data9::size_with(&LE);
    let written = bytes.pwrite_with(&data, 0, LE).unwrap();
    assert_eq!(written, size);
}
<commit_msg>scroll_derive: add PR link and note in macro_rules test<commit_after>use scroll::{Cread, Cwrite, Pread, Pwrite, LE};
use scroll_derive::{IOread, IOwrite, Pread, Pwrite, SizeWith};

use scroll::ctx::SizeWith;

macro_rules! test {
    ( struct $name:ident { $( $field:ident: $t:ty, )* } ) => {
	// check we can exist inside a macro_rules
	// https://github.com/m4b/scroll/pull/75
        #[derive(Pread)]
        pub struct $name {
            $( pub $field: $t, )*
        }
    };
}

test! {
    struct Test {
        field: [u16; 40],
    }
}

#[derive(Debug, PartialEq, Pread, Pwrite)]
struct Data {
    id: u32,
    timestamp: f64,
}

#[test]
fn test_data() {
    let bytes = [0xefu8, 0xbe, 0xad, 0xde, 0, 0, 0, 0, 0, 0, 224, 63];
    let data: Data = bytes.pread_with(0, LE).unwrap();
    println!("data: {:?}", &data);
    assert_eq!(data.id, 0xdeadbeefu32);
    assert_eq!(data.timestamp, 0.5f64);
    let mut bytes2 = vec![0; ::std::mem::size_of::<Data>()];
    bytes2.pwrite_with(data, 0, LE).unwrap();
    let data: Data = bytes.pread_with(0, LE).unwrap();
    let data2: Data = bytes2.pread_with(0, LE).unwrap();
    assert_eq!(data, data2);
}

#[derive(Debug, PartialEq, Pread, Pwrite)]
struct Data2 {
    name: [u8; 32],
}

#[test]
fn test_array() {
    let bytes = [0u8; 64];
    let data: Data2 = bytes.pread_with(0, LE).unwrap();
    println!("data: {:?}", &data);
}

#[derive(Debug, PartialEq, Pread, Pwrite, SizeWith)]
struct Data3 {
    name: u32,
}

#[test]
fn test_sizewith() {
    let bytes = [0u8; 64];
    let data: Data3 = bytes.gread_with(&mut 0, LE).unwrap();
    println!("data: {:?}", &data);
}

#[derive(Debug, PartialEq, IOread, IOwrite, SizeWith)]
struct Data4 {
    name: u32,
    j: u16,
    arr: [u8; 2],
}

#[test]
fn test_ioread() {
    let bytes = [0, 1, 2, 3, 0xde, 0xed, 0xbe, 0xaf];
    let data: Data4 = bytes.cread_with(0, LE);
    println!("data: {:?}", &data);
    assert_eq!(data.name, 50462976);
    assert_eq!(data.j, 0xedde);
    assert_eq!(data.arr, [0xbe, 0xaf]);
}

#[test]
fn test_iowrite() {
    let bytes = [0, 1, 2, 3, 0xde, 0xed, 0xbe, 0xaf];
    let data: Data4 = bytes.cread_with(0, LE);
    println!("data: {:?}", &data);
    assert_eq!(data.name, 50462976);
    assert_eq!(data.j, 0xedde);
    assert_eq!(data.arr, [0xbe, 0xaf]);

    let mut bytes_null = [0u8; 8];
    bytes_null.cwrite_with(&data, 0, LE);
    println!("bytes_null: {:?}", &bytes_null);
    println!("bytes     : {:?}", &bytes);
    assert_eq!(bytes_null, bytes);

    let mut bytes_null = [0u8; 8];
    bytes_null.cwrite_with(data, 0, LE);
    println!("bytes_null: {:?}", &bytes_null);
    println!("bytes     : {:?}", &bytes);
    assert_eq!(bytes_null, bytes);
}

#[derive(Debug, PartialEq, Pread, SizeWith)]
#[repr(C)]
struct Data5 {
    name: u32,
    j: u16,
    arr1: [u8; 2],
    arr2: [u16; 2],
}

#[test]
fn test_pread_arrays() {
    let bytes = [0, 1, 2, 3, 0, 0, 0xde, 0xed, 0xad, 0xde, 0xef, 0xbe];
    let data: Data5 = bytes.pread_with(0, LE).unwrap();
    println!("data: {:?}", &data);
    assert_eq!(data.name, 50462976);
    assert_eq!(data.arr1, [0xde, 0xed]);
    assert_eq!(data.arr2, [0xdead, 0xbeef]);
    let offset = &mut 0;
    let data: Data5 = bytes.gread_with(offset, LE).unwrap();
    println!("data: {:?}", &data);
    assert_eq!(data.name, 50462976);
    assert_eq!(data.arr1, [0xde, 0xed]);
    assert_eq!(data.arr2, [0xdead, 0xbeef]);
    assert_eq!(*offset, ::std::mem::size_of::<Data5>());
}

#[derive(Debug, PartialEq, Pread, SizeWith)]
#[repr(C)]
struct Data6 {
    id: u32,
    name: [u8; 5],
}

#[test]
fn test_array_copy() {
    let bytes = [0xde, 0xed, 0xef, 0xbe, 0x68, 0x65, 0x6c, 0x6c, 0x0];
    let data: Data6 = bytes.pread_with(0, LE).unwrap();
    let name: &str = data.name.pread(0).unwrap();
    println!("data: {:?}", &data);
    println!("data.name: {:?}", name);
    assert_eq!(data.id, 0xbeefedde);
    assert_eq!(name, "hell");
}

#[derive(Debug, PartialEq, Eq, Pread, Pwrite, SizeWith)]
struct Data7A {
    pub y: u64,
    pub x: u32,
}

#[derive(Debug, PartialEq, Eq, Pread, Pwrite, SizeWith)]
struct Data7B {
    pub a: Data7A,
}

#[test]
fn test_nested_struct() {
    let b = Data7B {
        a: Data7A { y: 1, x: 2 },
    };
    let size = Data7B::size_with(&LE);
    assert_eq!(size, 12);
    let mut bytes = vec![0; size];
    let written = bytes.pwrite_with(&b, 0, LE).unwrap();
    assert_eq!(written, size);
    let mut read = 0;
    let b2: Data7B = bytes.gread_with(&mut read, LE).unwrap();
    assert_eq!(read, size);
    assert_eq!(b, b2);
}

#[derive(Debug, PartialEq, Eq, Pread, Pwrite, IOread, IOwrite, SizeWith)]
#[repr(C)]
struct Data8<T, Y> {
    ids: [T; 3],
    xyz: Y,
}

#[test]
fn test_generics() {
    let mut bytes = [0xde, 0xed, 0xef, 0x10, 0x10];
    let data: Data8<u8, u16> = bytes.pread_with(0, LE).unwrap();
    assert_eq!(data.ids, [0xde, 0xed, 0xef]);
    assert_eq!(data.xyz, 0x1010);
    let data: Data8<u8, u16> = bytes.cread_with(0, LE);
    assert_eq!(data.ids, [0xde, 0xed, 0xef]);
    assert_eq!(data.xyz, 0x1010);
    let size = Data8::<u8, u16>::size_with(&LE);
    let written = bytes.pwrite_with(&data, 0, LE).unwrap();
    assert_eq!(written, size);
}

#[derive(Debug, PartialEq, Eq, Pread, Pwrite, IOread, IOwrite, SizeWith)]
struct Data9(u8, u16);

#[test]
fn test_newtype() {
    let mut bytes = [0xde, 0x10, 0x10];
    let data: Data9 = bytes.pread_with(0, LE).unwrap();
    assert_eq!(data.0, 0xde);
    assert_eq!(data.1, 0x1010);
    let data: Data9 = bytes.cread_with(0, LE);
    assert_eq!(data.0, 0xde);
    assert_eq!(data.1, 0x1010);
    let size = Data9::size_with(&LE);
    let written = bytes.pwrite_with(&data, 0, LE).unwrap();
    assert_eq!(written, size);
}
<|endoftext|>


<commit_before>extern crate core_foundation;
extern crate cgl;
extern crate gleam;

use view::DrawableGeometry;

use cocoa::appkit;
use cocoa::appkit::*;
use cocoa::foundation::*;
use cocoa::base::*;
use objc::declare::ClassDecl;
use objc::runtime::{Class, Object, Sel};
use self::cgl::{CGLEnable, kCGLCECrashOnRemovedFunctions};
use self::core_foundation::base::TCFType;
use self::core_foundation::string::CFString;
use self::core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName};
use std::os::raw::c_void;
use std::str::FromStr;
use view::{ElementState, MouseButton, ViewEvent, TouchPhase, MouseScrollDelta};
use super::utils;

pub fn register() {
    let superclass = Class::get("NSView").unwrap();
    let mut class = ClassDecl::new("NSServoView", superclass).unwrap();

    class.add_ivar::<*mut c_void>("event_queue");

    extern fn store_nsevent(this: &Object, _sel: Sel, nsevent: id) {
        let event = {
            unsafe {
                let event_type = nsevent.eventType();
                match event_type {
                    NSScrollWheel => {
                        // Stolen from winit
                        use self::MouseScrollDelta::{LineDelta, PixelDelta};
                        let nswindow: id = nsevent.window();
                        let hidpi_factor: CGFloat = msg_send![nswindow, backingScaleFactor];
                        let hidpi_factor = hidpi_factor as f32;
                        let delta = if nsevent.hasPreciseScrollingDeltas() == YES {
                            PixelDelta(hidpi_factor * nsevent.scrollingDeltaX() as f32,
                            hidpi_factor * nsevent.scrollingDeltaY() as f32)
                        } else {
                            LineDelta(hidpi_factor * nsevent.scrollingDeltaX() as f32,
                            hidpi_factor * nsevent.scrollingDeltaY() as f32)
                        };
                        let phase = match nsevent.phase() {
                            appkit::NSEventPhaseMayBegin | appkit::NSEventPhaseBegan => TouchPhase::Started,
                            appkit::NSEventPhaseEnded => TouchPhase::Ended,
                            _ => TouchPhase::Moved,
                        };
                        Some(ViewEvent::MouseWheel(delta, phase))
                    },
                    NSMouseMoved => {
                        let nswindow = nsevent.window();
                        let window_point = nsevent.locationInWindow();
                        let view_point: NSPoint = msg_send![this, convertPoint:window_point fromView:nil];
                        let frame: NSRect = msg_send![this, frame];
                        let hidpi_factor: CGFloat = msg_send![nswindow, backingScaleFactor];
                        let hidpi_factor = hidpi_factor as f32;
                        let x = (hidpi_factor * view_point.x as f32) as i32;
                        let y = (hidpi_factor * (frame.size.height - view_point.y) as f32) as i32;
                        Some(ViewEvent::MouseMoved(x, y))
                    }

                    NSLeftMouseDown => { Some(ViewEvent::MouseInput(ElementState::Pressed, MouseButton::Left)) },
                    NSLeftMouseUp => { Some(ViewEvent::MouseInput(ElementState::Released, MouseButton::Left)) },
                    NSRightMouseDown => { Some(ViewEvent::MouseInput(ElementState::Pressed, MouseButton::Right)) },
                    NSRightMouseUp => { Some(ViewEvent::MouseInput(ElementState::Released, MouseButton::Right)) },
                    NSOtherMouseDown => { Some(ViewEvent::MouseInput(ElementState::Pressed, MouseButton::Middle)) },
                    NSOtherMouseUp => { Some(ViewEvent::MouseInput(ElementState::Released, MouseButton::Middle)) },

                    _ => None
                }
            }

        };

        if event.is_some() {
            utils::get_event_queue(this).push(event);
        }
    }

    extern fn awake_from_nib(this: &mut Object, _sel: Sel) {
        // FIXME: is that the best way to create a raw pointer?
        let event_queue: Vec<ViewEvent> = Vec::new();
        let event_queue_ptr = Box::into_raw(Box::new(event_queue));
        unsafe {
            this.set_ivar("event_queue", event_queue_ptr as *mut c_void);
        }
    }

    extern fn accept_first_responder(_this: &Object, _sel: Sel) -> BOOL {
        YES
    }

    extern fn did_resize(this: &Object, _sel: Sel) {
        utils::get_event_queue(this).push(ViewEvent::GeometryDidChange);
    }

    unsafe {
        class.add_method(sel!(scrollWheel:), store_nsevent as extern fn(&Object, Sel, id));
        class.add_method(sel!(mouseDown:), store_nsevent as extern fn(&Object, Sel, id));
        class.add_method(sel!(mouseUp:), store_nsevent as extern fn(&Object, Sel, id));
        class.add_method(sel!(mouseMoved:), store_nsevent as extern fn(&Object, Sel, id));

        class.add_method(sel!(acceptsFirstResponder), accept_first_responder as extern fn(&Object, Sel) -> BOOL);

        class.add_method(sel!(viewDidEndLiveResize), did_resize as extern fn(&Object, Sel));

        class.add_method(sel!(awakeFromNib), awake_from_nib as extern fn(&mut Object, Sel));
    }

    class.register();
}

pub struct View {
    nsview: id,
    context: id,
}

impl View {

    pub fn new(nsview: id) -> View {
        let context: id = View::init_gl(nsview);
        View {
            nsview: nsview,
            context: context
        }
    }

    pub fn swap_buffers(&self) {
        unsafe {
            msg_send![self.context, flushBuffer];
        }
    }

    pub fn update_drawable(&self) {
        unsafe {
            msg_send![self.context, update];
        }
    }

    pub fn get_geometry(&self) -> DrawableGeometry {
        unsafe {
            let nswindow: id = msg_send![self.nsview, window];
            let frame: NSRect = msg_send![self.nsview, frame];
            let hidpi_factor: CGFloat = msg_send![nswindow, backingScaleFactor];
            let visible_rect: NSRect = msg_send![nswindow, contentLayoutRect];

            // FIXME: coordinates are flipped
            let bottom = visible_rect.origin.y - frame.origin.y;
            let top = frame.size.height - bottom - visible_rect.size.height;
            let left = 0;
            let right = 0;

            DrawableGeometry {
                view_size: (frame.size.width as u32, frame.size.height as u32),
                margins: (top as u32, right as u32, bottom as u32, left as u32),
                position: (0, 0),
                hidpi_factor: hidpi_factor as f32,
            }
        }
    }

    pub fn get_events(&self) -> Vec<ViewEvent> {
        let nsobject = unsafe { &*self.nsview};
        utils::get_event_queue(nsobject).drain(..).collect()
    }

    pub fn enter_fullscreen(&self) {
        unsafe {
            msg_send![self.nsview, enterFullScreenMode:nil withOptions:nil];
        }
    }

    pub fn exit_fullscreen(&self) {
        unsafe {
            msg_send![self.nsview, exitFullScreenModeWithOptions:nil];
        }
    }

    fn init_gl(nsview: id) -> id {
        let ctx = unsafe {
            nsview.setWantsBestResolutionOpenGLSurface_(YES);
            let attributes = vec![NSOpenGLPFADoubleBuffer as u32,
                NSOpenGLPFAClosestPolicy as u32,
                NSOpenGLPFAColorSize as u32,
                32,
                NSOpenGLPFAAlphaSize as u32,
                8,
                NSOpenGLPFADepthSize as u32,
                24,
                NSOpenGLPFAStencilSize as u32,
                8,
                NSOpenGLPFAOpenGLProfile as u32,
                NSOpenGLProfileVersion3_2Core as u32,
                0];

            let pixelformat = NSOpenGLPixelFormat::alloc(nil).initWithAttributes_(&attributes);
            let ctx: id = NSOpenGLContext::alloc(nil).initWithFormat_shareContext_(pixelformat, nil);
            msg_send![ctx, setView: nsview];
            let value = 1;
            ctx.setValues_forParameter_(&value, NSOpenGLContextParameter::NSOpenGLCPSwapInterval);
            CGLEnable(ctx.CGLContextObj() as *mut _, kCGLCECrashOnRemovedFunctions);
            ctx
        };

        unsafe {
            msg_send![ctx, update];
            msg_send![ctx, makeCurrentContext];
        };

        gleam::gl::load_with(|addr| {
            let symbol_name: CFString = FromStr::from_str(addr).unwrap();
            let framework_name: CFString = FromStr::from_str("com.apple.opengl").unwrap();
            let framework = unsafe {
                CFBundleGetBundleWithIdentifier(framework_name.as_concrete_TypeRef())
            };
            let symbol = unsafe {
                CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef())
            };
            symbol as *const c_void
        });

        gleam::gl::clear_color(1.0, 1.0, 1.0, 1.0);
        gleam::gl::clear(gleam::gl::COLOR_BUFFER_BIT);
        gleam::gl::finish();

        ctx
    }
}

<commit_msg>fix scrolling regression<commit_after>extern crate core_foundation;
extern crate cgl;
extern crate gleam;

use view::DrawableGeometry;

use cocoa::appkit;
use cocoa::appkit::*;
use cocoa::foundation::*;
use cocoa::base::*;
use objc::declare::ClassDecl;
use objc::runtime::{Class, Object, Sel};
use self::cgl::{CGLEnable, kCGLCECrashOnRemovedFunctions};
use self::core_foundation::base::TCFType;
use self::core_foundation::string::CFString;
use self::core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName};
use std::os::raw::c_void;
use std::str::FromStr;
use view::{ElementState, MouseButton, ViewEvent, TouchPhase, MouseScrollDelta};
use super::utils;

pub fn register() {
    let superclass = Class::get("NSView").unwrap();
    let mut class = ClassDecl::new("NSServoView", superclass).unwrap();

    class.add_ivar::<*mut c_void>("event_queue");

    extern fn store_nsevent(this: &Object, _sel: Sel, nsevent: id) {
        let event = {
            unsafe {
                let event_type = nsevent.eventType();
                match event_type {
                    NSScrollWheel => {
                        // Stolen from winit
                        use self::MouseScrollDelta::{LineDelta, PixelDelta};
                        let nswindow: id = nsevent.window();
                        let hidpi_factor: CGFloat = msg_send![nswindow, backingScaleFactor];
                        let hidpi_factor = hidpi_factor as f32;
                        let delta = if nsevent.hasPreciseScrollingDeltas() == YES {
                            PixelDelta(hidpi_factor * nsevent.scrollingDeltaX() as f32,
                            hidpi_factor * nsevent.scrollingDeltaY() as f32)
                        } else {
                            LineDelta(hidpi_factor * nsevent.scrollingDeltaX() as f32,
                            hidpi_factor * nsevent.scrollingDeltaY() as f32)
                        };
                        let phase = match nsevent.phase() {
                            appkit::NSEventPhaseMayBegin | appkit::NSEventPhaseBegan => TouchPhase::Started,
                            appkit::NSEventPhaseEnded => TouchPhase::Ended,
                            _ => TouchPhase::Moved,
                        };
                        Some(ViewEvent::MouseWheel(delta, phase))
                    },
                    NSMouseMoved => {
                        let nswindow = nsevent.window();
                        let window_point = nsevent.locationInWindow();
                        let view_point: NSPoint = msg_send![this, convertPoint:window_point fromView:nil];
                        let frame: NSRect = msg_send![this, frame];
                        let hidpi_factor: CGFloat = msg_send![nswindow, backingScaleFactor];
                        let hidpi_factor = hidpi_factor as f32;
                        let x = (hidpi_factor * view_point.x as f32) as i32;
                        let y = (hidpi_factor * (frame.size.height - view_point.y) as f32) as i32;
                        Some(ViewEvent::MouseMoved(x, y))
                    }

                    NSLeftMouseDown => { Some(ViewEvent::MouseInput(ElementState::Pressed, MouseButton::Left)) },
                    NSLeftMouseUp => { Some(ViewEvent::MouseInput(ElementState::Released, MouseButton::Left)) },
                    NSRightMouseDown => { Some(ViewEvent::MouseInput(ElementState::Pressed, MouseButton::Right)) },
                    NSRightMouseUp => { Some(ViewEvent::MouseInput(ElementState::Released, MouseButton::Right)) },
                    NSOtherMouseDown => { Some(ViewEvent::MouseInput(ElementState::Pressed, MouseButton::Middle)) },
                    NSOtherMouseUp => { Some(ViewEvent::MouseInput(ElementState::Released, MouseButton::Middle)) },

                    _ => None
                }
            }

        };

        if let Some(event) = event {
            utils::get_event_queue(this).push(event);
        }
    }

    extern fn awake_from_nib(this: &mut Object, _sel: Sel) {
        // FIXME: is that the best way to create a raw pointer?
        let event_queue: Vec<ViewEvent> = Vec::new();
        let event_queue_ptr = Box::into_raw(Box::new(event_queue));
        unsafe {
            this.set_ivar("event_queue", event_queue_ptr as *mut c_void);
        }
    }

    extern fn accept_first_responder(_this: &Object, _sel: Sel) -> BOOL {
        YES
    }

    extern fn did_resize(this: &Object, _sel: Sel) {
        utils::get_event_queue(this).push(ViewEvent::GeometryDidChange);
    }

    unsafe {
        class.add_method(sel!(scrollWheel:), store_nsevent as extern fn(&Object, Sel, id));
        class.add_method(sel!(mouseDown:), store_nsevent as extern fn(&Object, Sel, id));
        class.add_method(sel!(mouseUp:), store_nsevent as extern fn(&Object, Sel, id));
        class.add_method(sel!(mouseMoved:), store_nsevent as extern fn(&Object, Sel, id));

        class.add_method(sel!(acceptsFirstResponder), accept_first_responder as extern fn(&Object, Sel) -> BOOL);

        class.add_method(sel!(viewDidEndLiveResize), did_resize as extern fn(&Object, Sel));

        class.add_method(sel!(awakeFromNib), awake_from_nib as extern fn(&mut Object, Sel));
    }

    class.register();
}

pub struct View {
    nsview: id,
    context: id,
}

impl View {

    pub fn new(nsview: id) -> View {
        let context: id = View::init_gl(nsview);
        View {
            nsview: nsview,
            context: context
        }
    }

    pub fn swap_buffers(&self) {
        unsafe {
            msg_send![self.context, flushBuffer];
        }
    }

    pub fn update_drawable(&self) {
        unsafe {
            msg_send![self.context, update];
        }
    }

    pub fn get_geometry(&self) -> DrawableGeometry {
        unsafe {
            let nswindow: id = msg_send![self.nsview, window];
            let frame: NSRect = msg_send![self.nsview, frame];
            let hidpi_factor: CGFloat = msg_send![nswindow, backingScaleFactor];
            let visible_rect: NSRect = msg_send![nswindow, contentLayoutRect];

            // FIXME: coordinates are flipped
            let bottom = visible_rect.origin.y - frame.origin.y;
            let top = frame.size.height - bottom - visible_rect.size.height;
            let left = 0;
            let right = 0;

            DrawableGeometry {
                view_size: (frame.size.width as u32, frame.size.height as u32),
                margins: (top as u32, right as u32, bottom as u32, left as u32),
                position: (0, 0),
                hidpi_factor: hidpi_factor as f32,
            }
        }
    }

    pub fn get_events(&self) -> Vec<ViewEvent> {
        let nsobject = unsafe { &*self.nsview};
        utils::get_event_queue(nsobject).drain(..).collect()
    }

    pub fn enter_fullscreen(&self) {
        unsafe {
            msg_send![self.nsview, enterFullScreenMode:nil withOptions:nil];
        }
    }

    pub fn exit_fullscreen(&self) {
        unsafe {
            msg_send![self.nsview, exitFullScreenModeWithOptions:nil];
        }
    }

    fn init_gl(nsview: id) -> id {
        let ctx = unsafe {
            nsview.setWantsBestResolutionOpenGLSurface_(YES);
            let attributes = vec![NSOpenGLPFADoubleBuffer as u32,
                NSOpenGLPFAClosestPolicy as u32,
                NSOpenGLPFAColorSize as u32,
                32,
                NSOpenGLPFAAlphaSize as u32,
                8,
                NSOpenGLPFADepthSize as u32,
                24,
                NSOpenGLPFAStencilSize as u32,
                8,
                NSOpenGLPFAOpenGLProfile as u32,
                NSOpenGLProfileVersion3_2Core as u32,
                0];

            let pixelformat = NSOpenGLPixelFormat::alloc(nil).initWithAttributes_(&attributes);
            let ctx: id = NSOpenGLContext::alloc(nil).initWithFormat_shareContext_(pixelformat, nil);
            msg_send![ctx, setView: nsview];
            let value = 1;
            ctx.setValues_forParameter_(&value, NSOpenGLContextParameter::NSOpenGLCPSwapInterval);
            CGLEnable(ctx.CGLContextObj() as *mut _, kCGLCECrashOnRemovedFunctions);
            ctx
        };

        unsafe {
            msg_send![ctx, update];
            msg_send![ctx, makeCurrentContext];
        };

        gleam::gl::load_with(|addr| {
            let symbol_name: CFString = FromStr::from_str(addr).unwrap();
            let framework_name: CFString = FromStr::from_str("com.apple.opengl").unwrap();
            let framework = unsafe {
                CFBundleGetBundleWithIdentifier(framework_name.as_concrete_TypeRef())
            };
            let symbol = unsafe {
                CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef())
            };
            symbol as *const c_void
        });

        gleam::gl::clear_color(1.0, 1.0, 1.0, 1.0);
        gleam::gl::clear(gleam::gl::COLOR_BUFFER_BIT);
        gleam::gl::finish();

        ctx
    }
}

<|endoftext|>