<commit_before>#![warn(missing_docs, missing_debug_implementations)]
#![forbid(improper_ctypes, unsafe_op_in_unsafe_fn)]

//! EGL utilities
//!
//! This module contains bindings to the `libwayland-egl.so` library.
//!
//! This library is used to interface with the OpenGL stack, and creating
//! EGL surfaces from a wayland surface.
//!
//! See WlEglSurface documentation for details.

use std::os::raw::c_void;

use wayland_backend::client::ObjectId;
use wayland_sys::{client::wl_proxy, egl::*, ffi_dispatch};

/// Checks if the wayland-egl lib is available and can be used
///
/// Trying to create an `WlEglSurface` while this function returns
/// `false` will result in a panic.
pub fn is_available() -> bool {
    is_lib_available()
}

/// EGL surface
///
/// This object is a simple wrapper around a `WlSurface` to add the EGL
/// capabilities. Just use the `ptr` method once this object is created
/// to get the window pointer your OpenGL library is needing to initialize the
/// EGL context (you'll most likely need the display ptr as well, that you can
/// get via the `ptr` method of the `Proxy` trait on the `WlDisplay` object).
#[derive(Debug)]
pub struct WlEglSurface {
    ptr: *mut wl_egl_window,
}

impl WlEglSurface {
    /// Create an EGL surface from a wayland surface
    ///
    /// This method will check that the provided `ObjectId` is still alive and from the
    /// correct interface (`wl_surface`).
    ///
    /// You must always destroy the [`WlEglSurface`] *before* the underling `wl_surface`
    /// protocol object.
    pub fn new(surface: ObjectId, width: i32, height: i32) -> Result<WlEglSurface, Error> {
        if surface.interface().name != "wl_surface" {
            return Err(Error::InvalidId);
        }

        let ptr = surface.as_ptr();
        if ptr.is_null() {
            // ObjectId::as_ptr() returns NULL if the surface is no longer alive
            Err(Error::InvalidId)
        } else {
            // SAFETY: We are sure the pointer is valid and the interface is correct.
            unsafe { WlEglSurface::new_from_raw(ptr, width, height) }
        }
    }

    /// Create an EGL surface from a raw pointer to a wayland surface.
    ///
    /// # Safety
    ///
    /// The provided pointer must be a valid `wl_surface` pointer from `libwayland-client`.
    pub unsafe fn new_from_raw(
        surface: *mut wl_proxy,
        width: i32,
        height: i32,
    ) -> Result<WlEglSurface, Error> {
        if width <= 0 || height <= 0 {
            return Err(Error::InvalidSize);
        }
        let ptr = ffi_dispatch!(WAYLAND_EGL_HANDLE, wl_egl_window_create, surface, width, height);
        if ptr.is_null() {
            panic!("egl window allocation failed");
        }
        Ok(WlEglSurface { ptr })
    }

    /// Fetch current size of the EGL surface
    pub fn get_size(&self) -> (i32, i32) {
        let mut w = 0i32;
        let mut h = 0i32;
        unsafe {
            ffi_dispatch!(
                WAYLAND_EGL_HANDLE,
                wl_egl_window_get_attached_size,
                self.ptr,
                &mut w as *mut i32,
                &mut h as *mut i32
            );
        }
        (w, h)
    }

    /// Resize the EGL surface
    ///
    /// The two first arguments `(width, height)` are the new size of
    /// the surface, the two others `(dx, dy)` represent the displacement
    /// of the top-left corner of the surface. It allows you to control the
    /// direction of the resizing if necessary.
    pub fn resize(&self, width: i32, height: i32, dx: i32, dy: i32) {
        unsafe {
            ffi_dispatch!(WAYLAND_EGL_HANDLE, wl_egl_window_resize, self.ptr, width, height, dx, dy)
        }
    }

    /// Raw pointer to the EGL surface
    ///
    /// You'll need this pointer to initialize the EGL context in your
    /// favourite OpenGL lib.
    pub fn ptr(&self) -> *const c_void {
        self.ptr as *const c_void
    }
}

// SAFETY: We own the pointer to the wl_egl_window and can therefore be transferred to another thread.
unsafe impl Send for WlEglSurface {}
// Note that WlEglSurface is !Sync. This is because the pointer performs no internal synchronization.

impl Drop for WlEglSurface {
    fn drop(&mut self) {
        unsafe {
            ffi_dispatch!(WAYLAND_EGL_HANDLE, wl_egl_window_destroy, self.ptr);
        }
    }
}

/// EGL surface creation error.
#[derive(thiserror::Error, Debug)]
pub enum Error {
    /// Surface width or height are <= 0.
    #[error("surface width or height is <= 0")]
    InvalidSize,
    /// Passed surface object is not a surface.
    #[error("object id is not a surface")]
    InvalidId,
}
<commit_msg>egl: docs review<commit_after>#![warn(missing_docs, missing_debug_implementations)]
#![forbid(improper_ctypes, unsafe_op_in_unsafe_fn)]

//! EGL utilities
//!
//! This module contains bindings to the `libwayland-egl.so` library.
//!
//! This library is used to interface with the OpenGL stack, and creating
//! EGL surfaces from a wayland surface.
//!
//! See [`WlEglSurface`] documentation for details.

use std::os::raw::c_void;

use wayland_backend::client::ObjectId;
use wayland_sys::{client::wl_proxy, egl::*, ffi_dispatch};

/// Checks if the wayland-egl lib is available and can be used
///
/// Trying to create an [`WlEglSurface`] while this function returns
/// `false` will result in a panic.
pub fn is_available() -> bool {
    is_lib_available()
}

/// EGL surface
///
/// This object is a simple wrapper around a `wl_surface` to add the EGL
/// capabilities. Just use the [`ptr()`](WlEglSurface::ptr) method once this object
/// is created to get the window pointer your OpenGL library is needing to initialize
/// the EGL context (you'll most likely need the display ptr as well, that you can
/// get via the [`ObjectId::as_ptr()`] method on of the `wl_display` ID).
#[derive(Debug)]
pub struct WlEglSurface {
    ptr: *mut wl_egl_window,
}

impl WlEglSurface {
    /// Create an EGL surface from a wayland surface
    ///
    /// This method will check that the provided `ObjectId` is still alive and from the
    /// correct interface (`wl_surface`).
    ///
    /// You must always destroy the [`WlEglSurface`] *before* the underling `wl_surface`
    /// protocol object.
    pub fn new(surface: ObjectId, width: i32, height: i32) -> Result<WlEglSurface, Error> {
        if surface.interface().name != "wl_surface" {
            return Err(Error::InvalidId);
        }

        let ptr = surface.as_ptr();
        if ptr.is_null() {
            // ObjectId::as_ptr() returns NULL if the surface is no longer alive
            Err(Error::InvalidId)
        } else {
            // SAFETY: We are sure the pointer is valid and the interface is correct.
            unsafe { WlEglSurface::new_from_raw(ptr, width, height) }
        }
    }

    /// Create an EGL surface from a raw pointer to a wayland surface.
    ///
    /// # Safety
    ///
    /// The provided pointer must be a valid `wl_surface` pointer from `libwayland-client`.
    pub unsafe fn new_from_raw(
        surface: *mut wl_proxy,
        width: i32,
        height: i32,
    ) -> Result<WlEglSurface, Error> {
        if width <= 0 || height <= 0 {
            return Err(Error::InvalidSize);
        }
        let ptr = ffi_dispatch!(WAYLAND_EGL_HANDLE, wl_egl_window_create, surface, width, height);
        if ptr.is_null() {
            panic!("egl window allocation failed");
        }
        Ok(WlEglSurface { ptr })
    }

    /// Fetch current size of the EGL surface
    pub fn get_size(&self) -> (i32, i32) {
        let mut w = 0i32;
        let mut h = 0i32;
        unsafe {
            ffi_dispatch!(
                WAYLAND_EGL_HANDLE,
                wl_egl_window_get_attached_size,
                self.ptr,
                &mut w as *mut i32,
                &mut h as *mut i32
            );
        }
        (w, h)
    }

    /// Resize the EGL surface
    ///
    /// The two first arguments `(width, height)` are the new size of
    /// the surface, the two others `(dx, dy)` represent the displacement
    /// of the top-left corner of the surface. It allows you to control the
    /// direction of the resizing if necessary.
    pub fn resize(&self, width: i32, height: i32, dx: i32, dy: i32) {
        unsafe {
            ffi_dispatch!(WAYLAND_EGL_HANDLE, wl_egl_window_resize, self.ptr, width, height, dx, dy)
        }
    }

    /// Raw pointer to the EGL surface
    ///
    /// You'll need this pointer to initialize the EGL context in your
    /// favourite OpenGL lib.
    pub fn ptr(&self) -> *const c_void {
        self.ptr as *const c_void
    }
}

// SAFETY: We own the pointer to the wl_egl_window and can therefore be transferred to another thread.
unsafe impl Send for WlEglSurface {}
// Note that WlEglSurface is !Sync. This is because the pointer performs no internal synchronization.

impl Drop for WlEglSurface {
    fn drop(&mut self) {
        unsafe {
            ffi_dispatch!(WAYLAND_EGL_HANDLE, wl_egl_window_destroy, self.ptr);
        }
    }
}

/// EGL surface creation error.
#[derive(thiserror::Error, Debug)]
pub enum Error {
    /// Surface width or height are <= 0.
    #[error("surface width or height is <= 0")]
    InvalidSize,
    /// Passed surface object is not a surface.
    #[error("object id is not a surface")]
    InvalidId,
}
<|endoftext|>


<commit_before>use futures::{self, Async, Future};
use futures::future::lazy;
use futures::sync::oneshot;
use futures::task::{self, Unpark};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use ::{scope, ThreadPool, Configuration};

/// Basic test of using futures to data on the stack frame.
#[test]
fn future_test() {
    let data = &[0, 1];

    // Here we call `wait` on a select future, which will block at
    // least one thread. So we need a second thread to ensure no
    // deadlock.
    ThreadPool::new(Configuration::new().set_num_threads(2)).unwrap().install(|| {
        scope(|s| {
            let a = s.spawn_future(futures::future::ok::<_, ()>(&data[0]));
            let b = s.spawn_future(futures::future::ok::<_, ()>(&data[1]));
            let (item1, next) = a.select(b).wait().ok().unwrap();
            let item2 = next.wait().unwrap();
            assert!(*item1 == 0 || *item1 == 1);
            assert!(*item2 == 1 - *item1);
        });
    });
}

/// Test using `map` on a Rayon future. The `map` closure is eecuted
/// for side-effects, and modifies the `data` variable that is owned
/// by enclosing stack frame.
#[test]
fn future_map() {
    let data = &mut [format!("Hello, ")];

    let mut future = None;
    scope(|s| {
        let a = s.spawn_future(lazy(|| Ok::<_, ()>(&mut data[0])));
        future = Some(s.spawn_future(a.map(|v| {
            v.push_str("world!");
        })));
    });

    // future must have executed for the scope to have ended, even
    // though we never invoked `wait` to observe its result
    assert_eq!(data[0], "Hello, world!");
    assert!(future.is_some());
}

/// Test that we can create a future that returns an `&mut` to data,
/// so long as it outlives the scope.
#[test]
fn future_escape_ref() {
    let data = &mut [format!("Hello, ")];

    {
        let mut future = None;
        scope(|s| {
            let data = &mut *data;
            future = Some(s.spawn_future(lazy(move || Ok::<_, ()>(&mut data[0]))));
        });
        let s = future.unwrap().wait().unwrap();
        s.push_str("world!");
    }

    assert_eq!(data[0], "Hello, world!");
}

#[test]
#[should_panic]
fn future_panic_prop() {
    scope(|s| {
        let future = s.spawn_future(lazy(move || Ok::<(), ()>(argh())));
        let _ = future.rayon_wait(); // should panic, not return a value
    });

    fn argh() -> () {
        if true {
            panic!("Hello, world!");
        }
    }
}

/// Test that, even if we have only one thread, invoke `rayon_wait`
/// will not panic.
#[test]
fn future_rayon_wait_1_thread() {
    // run with only 1 worker thread; this would deadlock if we couldn't make progress
    let mut result = None;
    ThreadPool::new(Configuration::new().set_num_threads(1)).unwrap().install(|| {
        scope(|s| {
            use std::sync::mpsc::channel;
            let (tx, rx) = channel();
            let a = s.spawn_future(lazy(move || Ok::<usize, ()>(rx.recv().unwrap())));
            //                          ^^^^ FIXME: why is this needed?
            let b = s.spawn_future(a.map(|v| v + 1));
            let c = s.spawn_future(b.map(|v| v + 1));
            s.spawn(move |_| tx.send(20).unwrap());
            result = Some(c.rayon_wait().unwrap());
        });
    });
    assert_eq!(result, Some(22));
}

/// Test that invoking `wait` on a `RayonFuture` will panic, if it is inside
/// a Rayon worker thread.
#[test]
#[should_panic]
fn future_wait_panics_inside_rayon_thread() {
    scope(|s| {
        let future = s.spawn_future(lazy(move || Ok::<(), ()>(())));
        let _ = future.wait(); // should panic, not return a value
    });
}

/// Test that invoking `wait` on a `RayonFuture` will not panic if we
/// are outside a Rayon worker thread.
#[test]
fn future_wait_works_outside_rayon_threads() {
    let mut future = None;
    scope(|s| {
        future = Some(s.spawn_future(lazy(move || Ok::<(), ()>(()))));
    });
    assert_eq!(Ok(()), future.unwrap().wait());
}

/// Test that invoking `wait` on a `RayonFuture` will not panic if we
/// are outside a Rayon worker thread.
#[test]
#[should_panic(expected = "Hello, world!")]
fn panicy_unpark() {
    scope(|s| {
        let (a_tx, a_rx) = oneshot::channel::<u32>();
        let rf = s.spawn_future(a_rx);

        let mut spawn = task::spawn(rf);
        let unpark = Arc::new(PanicUnpark);
        match spawn.poll_future(unpark.clone()) {
            Ok(Async::NotReady) => {
                // good, we expect not to be ready yet
            }
            r => panic!("spawn poll returned: {:?}", r),
        }

        a_tx.complete(22);
        match spawn.poll_future(unpark) {
            Ok(Async::Ready(v)) => {
                assert_eq!(v, 22);
            }
            r => panic!("spawn poll returned: {:?}", r),
        }
    });
    panic!("scope failed to panic!");

    struct PanicUnpark;

    impl Unpark for PanicUnpark {
        fn unpark(&self) {
            panic!("Hello, world!");
        }
    }
}

#[test]
fn double_unpark() {
    let unpark0 = Arc::new(TrackUnpark { value: AtomicUsize::new(0) });
    let unpark1 = Arc::new(TrackUnpark { value: AtomicUsize::new(0) });
    let mut _tag = None;
    scope(|s| {
        let (a_tx, a_rx) = oneshot::channel::<u32>();
        let rf = s.spawn_future(a_rx);

        let mut spawn = task::spawn(rf);

        // test that we don't panic if people try to install a task many times;
        // even if they are different tasks
        for i in 0..22 {
            let u = if i % 2 == 0 {
                unpark0.clone()
            } else {
                unpark1.clone()
            };
            match spawn.poll_future(u) {
                Ok(Async::NotReady) => {
                    // good, we expect not to be ready yet
                }
                r => panic!("spawn poll returned: {:?}", r),
            }
        }

        a_tx.complete(22);

        // just hold onto `rf` to ensure that nothing is cancelled
        _tag = Some(spawn.into_inner());
    });

    // Since scope is done, our spawned future must have completed. It
    // should have signalled the unpark value we gave it -- but
    // exactly once, even though we called `poll` many times.
    assert_eq!(unpark1.value.load(Ordering::SeqCst), 1);

    // unpark0 was not the last unpark supplied, so it will never be signalled
    assert_eq!(unpark0.value.load(Ordering::SeqCst), 0);

    struct TrackUnpark {
        value: AtomicUsize,
    }

    impl Unpark for TrackUnpark {
        fn unpark(&self) {
            self.value.fetch_add(1, Ordering::SeqCst);
        }
    }
}
<commit_msg>fix `panic_unpark` test not to be racy<commit_after>use futures::{self, Async, Future};
use futures::future::lazy;
use futures::sync::oneshot;
use futures::task::{self, Unpark};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use ::{scope, ThreadPool, Configuration};

/// Basic test of using futures to data on the stack frame.
#[test]
fn future_test() {
    let data = &[0, 1];

    // Here we call `wait` on a select future, which will block at
    // least one thread. So we need a second thread to ensure no
    // deadlock.
    ThreadPool::new(Configuration::new().set_num_threads(2)).unwrap().install(|| {
        scope(|s| {
            let a = s.spawn_future(futures::future::ok::<_, ()>(&data[0]));
            let b = s.spawn_future(futures::future::ok::<_, ()>(&data[1]));
            let (item1, next) = a.select(b).wait().ok().unwrap();
            let item2 = next.wait().unwrap();
            assert!(*item1 == 0 || *item1 == 1);
            assert!(*item2 == 1 - *item1);
        });
    });
}

/// Test using `map` on a Rayon future. The `map` closure is eecuted
/// for side-effects, and modifies the `data` variable that is owned
/// by enclosing stack frame.
#[test]
fn future_map() {
    let data = &mut [format!("Hello, ")];

    let mut future = None;
    scope(|s| {
        let a = s.spawn_future(lazy(|| Ok::<_, ()>(&mut data[0])));
        future = Some(s.spawn_future(a.map(|v| {
            v.push_str("world!");
        })));
    });

    // future must have executed for the scope to have ended, even
    // though we never invoked `wait` to observe its result
    assert_eq!(data[0], "Hello, world!");
    assert!(future.is_some());
}

/// Test that we can create a future that returns an `&mut` to data,
/// so long as it outlives the scope.
#[test]
fn future_escape_ref() {
    let data = &mut [format!("Hello, ")];

    {
        let mut future = None;
        scope(|s| {
            let data = &mut *data;
            future = Some(s.spawn_future(lazy(move || Ok::<_, ()>(&mut data[0]))));
        });
        let s = future.unwrap().wait().unwrap();
        s.push_str("world!");
    }

    assert_eq!(data[0], "Hello, world!");
}

#[test]
#[should_panic]
fn future_panic_prop() {
    scope(|s| {
        let future = s.spawn_future(lazy(move || Ok::<(), ()>(argh())));
        let _ = future.rayon_wait(); // should panic, not return a value
    });

    fn argh() -> () {
        if true {
            panic!("Hello, world!");
        }
    }
}

/// Test that, even if we have only one thread, invoke `rayon_wait`
/// will not panic.
#[test]
fn future_rayon_wait_1_thread() {
    // run with only 1 worker thread; this would deadlock if we couldn't make progress
    let mut result = None;
    ThreadPool::new(Configuration::new().set_num_threads(1)).unwrap().install(|| {
        scope(|s| {
            use std::sync::mpsc::channel;
            let (tx, rx) = channel();
            let a = s.spawn_future(lazy(move || Ok::<usize, ()>(rx.recv().unwrap())));
            //                          ^^^^ FIXME: why is this needed?
            let b = s.spawn_future(a.map(|v| v + 1));
            let c = s.spawn_future(b.map(|v| v + 1));
            s.spawn(move |_| tx.send(20).unwrap());
            result = Some(c.rayon_wait().unwrap());
        });
    });
    assert_eq!(result, Some(22));
}

/// Test that invoking `wait` on a `RayonFuture` will panic, if it is inside
/// a Rayon worker thread.
#[test]
#[should_panic]
fn future_wait_panics_inside_rayon_thread() {
    scope(|s| {
        let future = s.spawn_future(lazy(move || Ok::<(), ()>(())));
        let _ = future.wait(); // should panic, not return a value
    });
}

/// Test that invoking `wait` on a `RayonFuture` will not panic if we
/// are outside a Rayon worker thread.
#[test]
fn future_wait_works_outside_rayon_threads() {
    let mut future = None;
    scope(|s| {
        future = Some(s.spawn_future(lazy(move || Ok::<(), ()>(()))));
    });
    assert_eq!(Ok(()), future.unwrap().wait());
}

/// Test that invoking `wait` on a `RayonFuture` will not panic if we
/// are outside a Rayon worker thread.
#[test]
#[should_panic(expected = "Hello, world!")]
fn panicy_unpark() {
    scope(|s| {
        let (a_tx, a_rx) = oneshot::channel::<u32>();
        let rf = s.spawn_future(a_rx);

        // invoke `poll_future` with a `PanicUnpark` instance;
        // this should get installed as a 'waiting task' on the
        // Rayon future `rf`
        let mut spawn = task::spawn(rf);
        let unpark = Arc::new(PanicUnpark);
        match spawn.poll_future(unpark.clone()) {
            Ok(Async::NotReady) => {
                // good, we expect not to be ready yet
            }
            r => panic!("spawn poll returned: {:?}", r),
        }

        // this should trigger the future `a_rx` to be awoken
        // and executing in a Rayon background thread
        a_tx.complete(22);

        // now we wait for `rf` to complete; when it does, it will
        // also signal the `PanicUnpark` to wake up (that is
        // *supposed* to be what triggers us to `poll` again, but
        // we are sidestepping that)
        let v = spawn.into_inner().rayon_wait().unwrap();
        assert_eq!(v, 22);
    });
    panic!("scope failed to panic!");

    struct PanicUnpark;

    impl Unpark for PanicUnpark {
        fn unpark(&self) {
            panic!("Hello, world!");
        }
    }
}

#[test]
fn double_unpark() {
    let unpark0 = Arc::new(TrackUnpark { value: AtomicUsize::new(0) });
    let unpark1 = Arc::new(TrackUnpark { value: AtomicUsize::new(0) });
    let mut _tag = None;
    scope(|s| {
        let (a_tx, a_rx) = oneshot::channel::<u32>();
        let rf = s.spawn_future(a_rx);

        let mut spawn = task::spawn(rf);

        // test that we don't panic if people try to install a task many times;
        // even if they are different tasks
        for i in 0..22 {
            let u = if i % 2 == 0 {
                unpark0.clone()
            } else {
                unpark1.clone()
            };
            match spawn.poll_future(u) {
                Ok(Async::NotReady) => {
                    // good, we expect not to be ready yet
                }
                r => panic!("spawn poll returned: {:?}", r),
            }
        }

        a_tx.complete(22);

        // just hold onto `rf` to ensure that nothing is cancelled
        _tag = Some(spawn.into_inner());
    });

    // Since scope is done, our spawned future must have completed. It
    // should have signalled the unpark value we gave it -- but
    // exactly once, even though we called `poll` many times.
    assert_eq!(unpark1.value.load(Ordering::SeqCst), 1);

    // unpark0 was not the last unpark supplied, so it will never be signalled
    assert_eq!(unpark0.value.load(Ordering::SeqCst), 0);

    struct TrackUnpark {
        value: AtomicUsize,
    }

    impl Unpark for TrackUnpark {
        fn unpark(&self) {
            self.value.fetch_add(1, Ordering::SeqCst);
        }
    }
}
<|endoftext|>


<commit_before>use std::ptr;
use libc::{c_int};
use glib::Error;
use glib::object::{Object, Upcast, Wrapper, Ref};
use glib::types::{StaticType, Type};
use glib::translate::*;
use glib::glib_container::GlibContainer;
use secret_service::SecretService;
use secret_item::SecretItem;
use SecretResult;
use ffi;

pub struct SecretCollection(Ref);

impl SecretCollection {

    /// Lookup which collection is assigned to this alias.
    /// Aliases help determine well known collections, such as 'default'.
    /// Returns the collection, or NULL if none assigned to the alias.
    pub fn for_alias(alias: &str) -> SecretResult<SecretCollection>{
        let mut err = ptr::null_mut();
        let ptr = unsafe{ffi::secret_collection_for_alias_sync(ptr::null_mut(), alias.to_glib_none().0, SECRET_COLLECTION_LOAD_ITEMS, ptr::null_mut(), &mut err)};
        if err.is_null(){
            Ok(unsafe { from_glib_full(ptr) })
        } else {
            Err(Error::wrap(err))
        }
    }

    /// Create a new collection in the secret service.
    /// If you specify an alias and a collection with that alias already exists, then a new collection will not be created. The previous one will be returned instead.
    /// Returns the created Collection.
    pub fn create(label: &str, alias: Option<&str>) -> SecretResult<SecretCollection> {
        let mut err = ptr::null_mut();
        let ptr = unsafe{ffi::secret_collection_create_sync(ptr::null_mut(), label.to_glib_none().0, alias.to_glib_none().0, 0 as c_int, ptr::null_mut(), &mut err)};
        if err.is_null(){
            Ok(unsafe { from_glib_full(ptr) })
        } else {
            Err(Error::wrap(err))
        }
    }

    /// Get the created date and time of the collection.
    /// The return value is the number of seconds since the unix epoch, January 1st 1970.
    pub fn get_created(&self) -> u64 {
        unsafe {ffi::secret_collection_get_created(self.to_glib_none().0)}
    }

    /// Get the modified date and time of the collection.
    /// The return value is the number of seconds since the unix epoch, January 1st 1970.
    pub fn get_modified(&self) -> u64 {
        unsafe {ffi::secret_collection_get_modified(self.to_glib_none().0)}
    }

    /// Get the Secret Service object that this collection was created with.
    pub fn get_service(&self) -> SecretService { //TODO find out if this can return null
        unsafe {
            let ptr = ffi::secret_collection_get_service(self.to_glib_none().0);
            from_glib_none(ptr)
        }
    }

    pub fn are_items_loaded(&self) -> bool {
        let flags = unsafe {ffi::secret_collection_get_flags(self.to_glib_none().0)};
        flags & SECRET_COLLECTION_LOAD_ITEMS != 0
    }

    /// Get the label of this collection.
    pub fn get_label(&self) -> String {
        unsafe{
            let ptr = ffi::secret_collection_get_label(self.to_glib_none().0);
            from_glib_full(ptr)
        }
    }

    /// Get whether the collection is locked or not.
    pub fn get_locked(&self) -> bool {
        let gbool = unsafe{ffi::secret_collection_get_locked(self.to_glib_none().0)};
        from_glib(gbool)
    }

    /// Returns None, if the items have not yet been loaded.
    pub fn get_items(&self) -> Vec<SecretItem> {
        unsafe {
            let glist = ffi::secret_collection_get_items(self.to_glib_none().0);
            Vec::from_glib_full(glist)
        }
    }

    /// Ensure that the SecretCollection proxy has loaded all the items present in the Secret Service.
    pub fn load_items(&self) -> SecretResult<()>{
        unsafe {
            let mut err = ptr::null_mut();
            ffi::secret_collection_load_items_sync(self.to_glib_none().0, ptr::null_mut(), &mut err);
            if err.is_null() {
                Ok(())
            } else {
                Err(Error::wrap(err))
            }
        }
    }

    //TODO set_alias from ss
}

impl StaticType for SecretCollection {
    fn static_type() -> Type{
        unsafe { from_glib(ffi::secret_collection_get_type()) }
    }
}

unsafe impl Upcast<Object> for SecretCollection { }

impl Wrapper for SecretCollection {
    type GlibType = ffi::SecretCollectionFFI;
    unsafe fn wrap(r: Ref) -> Self{
        SecretCollection(r)
    }

    fn as_ref(&self) -> &Ref{
        &self.0
    }

    fn unwrap(self) -> Ref{
        self.0
    }
}

#[allow(dead_code)]
const SECRET_COLLECTION_NONE: i32        = 0;
const SECRET_COLLECTION_LOAD_ITEMS: i32  = 1 << 1;
<commit_msg>Remove useless cast<commit_after>use std::ptr;
use glib::Error;
use glib::object::{Object, Upcast, Wrapper, Ref};
use glib::types::{StaticType, Type};
use glib::translate::*;
use glib::glib_container::GlibContainer;
use secret_service::SecretService;
use secret_item::SecretItem;
use SecretResult;
use ffi;

pub struct SecretCollection(Ref);

impl SecretCollection {

    /// Lookup which collection is assigned to this alias.
    /// Aliases help determine well known collections, such as 'default'.
    /// Returns the collection, or NULL if none assigned to the alias.
    pub fn for_alias(alias: &str) -> SecretResult<SecretCollection>{
        let mut err = ptr::null_mut();
        let ptr = unsafe{ffi::secret_collection_for_alias_sync(ptr::null_mut(), alias.to_glib_none().0, SECRET_COLLECTION_LOAD_ITEMS, ptr::null_mut(), &mut err)};
        if err.is_null(){
            Ok(unsafe { from_glib_full(ptr) })
        } else {
            Err(Error::wrap(err))
        }
    }

    /// Create a new collection in the secret service.
    /// If you specify an alias and a collection with that alias already exists, then a new collection will not be created. The previous one will be returned instead.
    /// Returns the created Collection.
    pub fn create(label: &str, alias: Option<&str>) -> SecretResult<SecretCollection> {
        let mut err = ptr::null_mut();
        let ptr = unsafe{ffi::secret_collection_create_sync(ptr::null_mut(), label.to_glib_none().0, alias.to_glib_none().0, 0, ptr::null_mut(), &mut err)};
        if err.is_null(){
            Ok(unsafe { from_glib_full(ptr) })
        } else {
            Err(Error::wrap(err))
        }
    }

    /// Get the created date and time of the collection.
    /// The return value is the number of seconds since the unix epoch, January 1st 1970.
    pub fn get_created(&self) -> u64 {
        unsafe {ffi::secret_collection_get_created(self.to_glib_none().0)}
    }

    /// Get the modified date and time of the collection.
    /// The return value is the number of seconds since the unix epoch, January 1st 1970.
    pub fn get_modified(&self) -> u64 {
        unsafe {ffi::secret_collection_get_modified(self.to_glib_none().0)}
    }

    /// Get the Secret Service object that this collection was created with.
    pub fn get_service(&self) -> SecretService { //TODO find out if this can return null
        unsafe {
            let ptr = ffi::secret_collection_get_service(self.to_glib_none().0);
            from_glib_none(ptr)
        }
    }

    pub fn are_items_loaded(&self) -> bool {
        let flags = unsafe {ffi::secret_collection_get_flags(self.to_glib_none().0)};
        flags & SECRET_COLLECTION_LOAD_ITEMS != 0
    }

    /// Get the label of this collection.
    pub fn get_label(&self) -> String {
        unsafe{
            let ptr = ffi::secret_collection_get_label(self.to_glib_none().0);
            from_glib_full(ptr)
        }
    }

    /// Get whether the collection is locked or not.
    pub fn get_locked(&self) -> bool {
        let gbool = unsafe{ffi::secret_collection_get_locked(self.to_glib_none().0)};
        from_glib(gbool)
    }

    /// Returns None, if the items have not yet been loaded.
    pub fn get_items(&self) -> Vec<SecretItem> {
        unsafe {
            let glist = ffi::secret_collection_get_items(self.to_glib_none().0);
            Vec::from_glib_full(glist)
        }
    }

    /// Ensure that the SecretCollection proxy has loaded all the items present in the Secret Service.
    pub fn load_items(&self) -> SecretResult<()>{
        unsafe {
            let mut err = ptr::null_mut();
            ffi::secret_collection_load_items_sync(self.to_glib_none().0, ptr::null_mut(), &mut err);
            if err.is_null() {
                Ok(())
            } else {
                Err(Error::wrap(err))
            }
        }
    }

    //TODO set_alias from ss
}

impl StaticType for SecretCollection {
    fn static_type() -> Type{
        unsafe { from_glib(ffi::secret_collection_get_type()) }
    }
}

unsafe impl Upcast<Object> for SecretCollection { }

impl Wrapper for SecretCollection {
    type GlibType = ffi::SecretCollectionFFI;
    unsafe fn wrap(r: Ref) -> Self{
        SecretCollection(r)
    }

    fn as_ref(&self) -> &Ref{
        &self.0
    }

    fn unwrap(self) -> Ref{
        self.0
    }
}

#[allow(dead_code)]
const SECRET_COLLECTION_NONE: i32        = 0;
const SECRET_COLLECTION_LOAD_ITEMS: i32  = 1 << 1;
<|endoftext|>


<commit_before>// Copyright (C) 2020 Jason Ish
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use std::io::BufRead;
use std::sync::Arc;

use crate::eve::eve::EveJson;
use crate::logger::log;
use crate::server::response::Response;
use crate::server::ServerContext;

struct Reader<'a> {
    buf: &'a [u8],
}

impl<'a> Reader<'a> {
    fn next_record(&mut self) -> Result<Option<EveJson>, Box<dyn std::error::Error>> {
        let mut line = String::new();
        let n = self.buf.read_line(&mut line)?;
        if n == 0 {
            Ok(None)
        } else {
            let event: EveJson = serde_json::from_str(&line)?;
            Ok(Some(event))
        }
    }
}

pub async fn handler(
    context: Arc<ServerContext>,
    body: bytes::Bytes,
) -> Result<impl warp::Reply, warp::Rejection> {
    let mut importer = match context.datastore.get_importer() {
        Some(importer) => importer,
        None => {
            return Ok(crate::server::response::Response::Unimplemented);
        }
    };
    let mut errors = Vec::new();

    let mut buf = &body[..];
    let mut count = 0;
    let mut line = String::new();
    loop {
        match buf.read_line(&mut line) {
            Err(err) => {
                errors.push(format!("Failed to read event from request body: {}", err));
                // Failed to read line, can't continue.
                break;
            }
            Ok(n) => {
                if n == 0 {
                    // EOF.
                    break;
                }
                match serde_json::from_str::<EveJson>(&line) {
                    Err(err) => {
                        errors.push(format!(
                            "Failed to decode event from request body ({}): {}",
                            err, line
                        ));
                    }
                    Ok(event) => {
                        count += 1;
                        if let Err(err) = importer.submit(event).await {
                            log::error!("Failed to submit event to importer: {}", err);
                        }
                    }
                }
            }
        }
        line.truncate(0);
    }
    match importer.commit().await {
        Ok(n) => {
            log::debug!("Committed {} events (received {})", n, count);
            return Ok(Response::Ok);
        }
        Err(err) => {
            log::error!("Failed to commit events (received {}): {}", count, err);
            return Ok(Response::InternalError(err.to_string()));
        }
    }
}
<commit_msg>server/submit: return "Count" in response<commit_after>// Copyright (C) 2020 Jason Ish
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use std::io::BufRead;
use std::sync::Arc;

use crate::eve::eve::EveJson;
use crate::logger::log;
use crate::server::response::Response;
use crate::server::ServerContext;

struct Reader<'a> {
    buf: &'a [u8],
}

impl<'a> Reader<'a> {
    fn next_record(&mut self) -> Result<Option<EveJson>, Box<dyn std::error::Error>> {
        let mut line = String::new();
        let n = self.buf.read_line(&mut line)?;
        if n == 0 {
            Ok(None)
        } else {
            let event: EveJson = serde_json::from_str(&line)?;
            Ok(Some(event))
        }
    }
}

pub async fn handler(
    context: Arc<ServerContext>,
    body: bytes::Bytes,
) -> Result<impl warp::Reply, warp::Rejection> {
    let mut importer = match context.datastore.get_importer() {
        Some(importer) => importer,
        None => {
            return Ok(crate::server::response::Response::Unimplemented);
        }
    };
    let mut errors = Vec::new();

    let mut buf = &body[..];
    let mut count = 0;
    let mut line = String::new();
    loop {
        match buf.read_line(&mut line) {
            Err(err) => {
                errors.push(format!("Failed to read event from request body: {}", err));
                // Failed to read line, can't continue.
                break;
            }
            Ok(n) => {
                if n == 0 {
                    // EOF.
                    break;
                }
                match serde_json::from_str::<EveJson>(&line) {
                    Err(err) => {
                        errors.push(format!(
                            "Failed to decode event from request body ({}): {}",
                            err, line
                        ));
                    }
                    Ok(event) => {
                        count += 1;
                        if let Err(err) = importer.submit(event).await {
                            log::error!("Failed to submit event to importer: {}", err);
                        }
                    }
                }
            }
        }
        line.truncate(0);
    }

    // I've seen an issue in the Go agent where it sent 0 events, return early if we have
    // nothing to commit.
    if count == 0 {
        // TODO: Log something or return an error to the client.
        let response = json!({
            "Count": 0,
        });
        return Ok(Response::Json(response));
    }

    match importer.commit().await {
        Ok(n) => {
            log::debug!("Committed {} events (received {})", n, count);
            let response = json!({
                // Kept capitolized for compatibility with the Go agent.
                "Count": n,
            });
            return Ok(Response::Json(response));
        }
        Err(err) => {
            log::error!("Failed to commit events (received {}): {}", count, err);
            return Ok(Response::InternalError(err.to_string()));
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use diem_global_constants::VALIDATOR_NETWORK_ADDRESS_KEYS;
use diem_infallible::RwLock;
use diem_secure_storage::{Error as StorageError, KVStorage, Storage};
use diem_types::{
    account_address::AccountAddress,
    network_address::{
        self,
        encrypted::{
            EncNetworkAddress, Key, KeyVersion, TEST_SHARED_VAL_NETADDR_KEY,
            TEST_SHARED_VAL_NETADDR_KEY_VERSION,
        },
        NetworkAddress,
    },
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use thiserror::Error;

#[derive(Debug, Error)]
pub enum Error {
    #[error("Unable to deserialize address for account {0}: {1}")]
    AddressDeserialization(AccountAddress, String),
    #[error("Unable to decrypt address for account {0}: {1}")]
    DecryptionError(AccountAddress, String),
    #[error("Failed (de)serializing validator_network_address_keys")]
    BCSError(#[from] bcs::Error),
    #[error("NetworkAddress parse error {0}")]
    ParseError(#[from] network_address::ParseError),
    #[error("Failed reading validator_network_address_keys from storage")]
    StorageError(#[from] StorageError),
    #[error("The specified version does not exist in validator_network_address_keys: {0}")]
    VersionNotFound(KeyVersion),
}

pub struct Encryptor {
    storage: Storage,
    cached_keys: RwLock<Option<ValidatorKeys>>,
}

impl Encryptor {
    pub fn new(storage: Storage) -> Self {
        Self {
            storage,
            cached_keys: RwLock::new(None),
        }
    }

    /// This generates an Encryptor for use in default / testing scenarios where (proper)
    /// encryption is not necessary.
    pub fn for_testing() -> Self {
        let storage = Storage::InMemoryStorage(diem_secure_storage::InMemoryStorage::new());
        let mut encryptor = Encryptor::new(storage);
        encryptor.initialize_for_testing().unwrap();
        encryptor
    }

    pub fn add_key(&mut self, version: KeyVersion, key: Key) -> Result<(), Error> {
        let mut keys = self.read()?;
        keys.keys.insert(version, StorageKey(key));
        self.write(&keys)
    }

    pub fn set_current_version(&mut self, version: KeyVersion) -> Result<(), Error> {
        let mut keys = self.read()?;
        if keys.keys.get(&version).is_some() {
            keys.current = version;
            self.write(&keys)
        } else {
            Err(Error::VersionNotFound(version))
        }
    }

    pub fn current_version(&self) -> Result<KeyVersion, Error> {
        self.read().map(|keys| keys.current)
    }

    pub fn encrypt(
        &self,
        network_addresses: &[NetworkAddress],
        account: AccountAddress,
        seq_num: u64,
    ) -> Result<Vec<u8>, Error> {
        let keys = self.read()?;
        let key = keys
            .keys
            .get(&keys.current)
            .ok_or(Error::VersionNotFound(keys.current))?;
        let mut enc_addrs = Vec::new();
        for (idx, addr) in network_addresses.iter().cloned().enumerate() {
            enc_addrs.push(addr.encrypt(&key.0, keys.current, &account, seq_num, idx as u32)?);
        }
        bcs::to_bytes(&enc_addrs).map_err(|e| e.into())
    }

    pub fn decrypt(
        &self,
        encrypted_network_addresses: &[u8],
        account: AccountAddress,
    ) -> Result<Vec<NetworkAddress>, Error> {
        let keys = self.read()?;
        let enc_addrs: Vec<EncNetworkAddress> = bcs::from_bytes(&encrypted_network_addresses)
            .map_err(|e| Error::AddressDeserialization(account, e.to_string()))?;
        let mut addrs = Vec::new();
        for (idx, enc_addr) in enc_addrs.iter().enumerate() {
            let key = keys
                .keys
                .get(&enc_addr.key_version())
                .ok_or_else(|| Error::VersionNotFound(enc_addr.key_version()))?;
            let addr = enc_addr
                .clone()
                .decrypt(&key.0, &account, idx as u32)
                .map_err(|e| Error::DecryptionError(account, e.to_string()))?;
            addrs.push(addr);
        }
        Ok(addrs)
    }

    pub fn initialize(&mut self) -> Result<(), Error> {
        self.write(&ValidatorKeys::default())
    }

    pub fn initialize_for_testing(&mut self) -> Result<(), Error> {
        self.initialize()?;
        self.add_key(
            TEST_SHARED_VAL_NETADDR_KEY_VERSION,
            TEST_SHARED_VAL_NETADDR_KEY,
        )?;
        self.set_current_version(TEST_SHARED_VAL_NETADDR_KEY_VERSION)
    }

    fn read(&self) -> Result<ValidatorKeys, Error> {
        let result = self
            .storage
            .get::<ValidatorKeys>(VALIDATOR_NETWORK_ADDRESS_KEYS)
            .map(|v| v.value)
            .map_err(|e| e.into());

        match &result {
            Ok(keys) => {
                *self.cached_keys.write() = Some(keys.clone());
            }
            Err(err) => diem_logger::error!(
                "Unable to read {} from storage: {}",
                VALIDATOR_NETWORK_ADDRESS_KEYS,
                err
            ),
        }

        let keys = self.cached_keys.read();
        keys.as_ref().map_or(result, |v| Ok(v.clone()))
    }

    fn write(&mut self, keys: &ValidatorKeys) -> Result<(), Error> {
        self.storage
            .set(VALIDATOR_NETWORK_ADDRESS_KEYS, keys)
            .map_err(|e| e.into())
    }
}

#[derive(Clone, Debug, Deserialize, Serialize)]
struct StorageKey(
    #[serde(
        serialize_with = "diem_secure_storage::to_base64",
        deserialize_with = "from_base64"
    )]
    Key,
);

pub fn to_base64<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    serializer.serialize_str(&base64::encode(bytes))
}

pub fn from_base64<'de, D>(deserializer: D) -> Result<Key, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let s: String = serde::Deserialize::deserialize(deserializer)?;
    base64::decode(s)
        .map_err(serde::de::Error::custom)
        .and_then(|v| {
            std::convert::TryInto::try_into(v.as_slice()).map_err(serde::de::Error::custom)
        })
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ValidatorKeys {
    keys: HashMap<KeyVersion, StorageKey>,
    current: KeyVersion,
}

impl Default for ValidatorKeys {
    fn default() -> Self {
        ValidatorKeys {
            current: 0,
            keys: HashMap::new(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use diem_secure_storage::InMemoryStorage;
    use rand::{rngs::OsRng, Rng, RngCore, SeedableRng};

    #[test]
    fn e2e() {
        let storage = Storage::InMemoryStorage(InMemoryStorage::new());
        let mut encryptor = Encryptor::new(storage);
        encryptor.initialize().unwrap();

        let mut rng = rand::rngs::StdRng::from_seed(OsRng.gen());
        let mut key = [0; network_address::encrypted::KEY_LEN];
        rng.fill_bytes(&mut key);
        encryptor.add_key(0, key).unwrap();
        encryptor.set_current_version(0).unwrap();
        rng.fill_bytes(&mut key);
        encryptor.add_key(1, key).unwrap();
        encryptor.set_current_version(1).unwrap();
        rng.fill_bytes(&mut key);
        encryptor.add_key(4, key).unwrap();
        encryptor.set_current_version(4).unwrap();

        encryptor.set_current_version(5).unwrap_err();

        let addr = std::str::FromStr::from_str("/ip4/10.0.0.16/tcp/80").unwrap();
        let addrs = vec![addr];
        let account = AccountAddress::random();

        let enc_addrs = encryptor.encrypt(&addrs, account, 5).unwrap();
        let dec_addrs = encryptor.decrypt(&enc_addrs, account).unwrap();
        assert_eq!(addrs, dec_addrs);

        let another_account = AccountAddress::random();
        encryptor.decrypt(&enc_addrs, another_account).unwrap_err();
    }

    #[test]
    fn cache_test() {
        // Prepare some initial data and verify e2e
        let mut encryptor = Encryptor::for_testing();
        let addr = std::str::FromStr::from_str("/ip4/10.0.0.16/tcp/80").unwrap();
        let addrs = vec![addr];
        let account = AccountAddress::random();

        let enc_addrs = encryptor.encrypt(&addrs, account, 0).unwrap();
        let dec_addrs = encryptor.decrypt(&enc_addrs, account).unwrap();
        assert_eq!(addrs, dec_addrs);

        // Reset storage and we should use cache
        encryptor.storage = Storage::from(InMemoryStorage::new());
        let enc_addrs = encryptor.encrypt(&addrs, account, 1).unwrap();
        let dec_addrs = encryptor.decrypt(&enc_addrs, account).unwrap();
        assert_eq!(addrs, dec_addrs);

        // Reset cache and we should get an err
        *encryptor.cached_keys.write() = None;
        encryptor.encrypt(&addrs, account, 1).unwrap_err();
        encryptor.decrypt(&enc_addrs, account).unwrap_err();
    }

    // The only purpose of this test is to generate a baseline for vault
    #[ignore]
    #[test]
    fn initializer() {
        let storage = Storage::VaultStorage(diem_secure_storage::VaultStorage::new(
            "http://127.0.0.1:8200".to_string(),
            "root_token".to_string(),
            Some("network_address_encryption_keys".to_string()),
            None,
            None,
            true,
            None,
            None,
        ));
        let mut encryptor = Encryptor::new(storage);
        encryptor.initialize().unwrap();
        let mut rng = rand::rngs::StdRng::from_seed(OsRng.gen());
        let mut key = [0; network_address::encrypted::KEY_LEN];
        rng.fill_bytes(&mut key);
        encryptor.add_key(0, key).unwrap();
        encryptor.set_current_version(0).unwrap();
    }
}
<commit_msg>[Operational Tool] Add empty encryptor.<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use diem_global_constants::VALIDATOR_NETWORK_ADDRESS_KEYS;
use diem_infallible::RwLock;
use diem_secure_storage::{Error as StorageError, KVStorage, Storage};
use diem_types::{
    account_address::AccountAddress,
    network_address::{
        self,
        encrypted::{
            EncNetworkAddress, Key, KeyVersion, TEST_SHARED_VAL_NETADDR_KEY,
            TEST_SHARED_VAL_NETADDR_KEY_VERSION,
        },
        NetworkAddress,
    },
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use thiserror::Error;

#[derive(Debug, Error)]
pub enum Error {
    #[error("Unable to deserialize address for account {0}: {1}")]
    AddressDeserialization(AccountAddress, String),
    #[error("Unable to decrypt address for account {0}: {1}")]
    DecryptionError(AccountAddress, String),
    #[error("Failed (de)serializing validator_network_address_keys")]
    BCSError(#[from] bcs::Error),
    #[error("NetworkAddress parse error {0}")]
    ParseError(#[from] network_address::ParseError),
    #[error("Failed reading validator_network_address_keys from storage")]
    StorageError(#[from] StorageError),
    #[error("The specified version does not exist in validator_network_address_keys: {0}")]
    VersionNotFound(KeyVersion),
}

pub struct Encryptor {
    storage: Storage,
    cached_keys: RwLock<Option<ValidatorKeys>>,
}

impl Encryptor {
    pub fn new(storage: Storage) -> Self {
        Self {
            storage,
            cached_keys: RwLock::new(None),
        }
    }

    /// This generates an empty encryptor for use in scenarios where encryption is not necessary.
    /// Any encryption operations (e.g., encrypt / decrypt) will return errors.
    pub fn empty() -> Self {
        let storage = Storage::InMemoryStorage(diem_secure_storage::InMemoryStorage::new());
        Encryptor::new(storage)
    }

    /// This generates an encryptor for use in testing scenarios. The encryptor is
    /// initialized with a test network encryption key.
    pub fn for_testing() -> Self {
        let mut encryptor = Self::empty();
        encryptor.initialize_for_testing().unwrap();
        encryptor
    }

    pub fn add_key(&mut self, version: KeyVersion, key: Key) -> Result<(), Error> {
        let mut keys = self.read()?;
        keys.keys.insert(version, StorageKey(key));
        self.write(&keys)
    }

    pub fn set_current_version(&mut self, version: KeyVersion) -> Result<(), Error> {
        let mut keys = self.read()?;
        if keys.keys.get(&version).is_some() {
            keys.current = version;
            self.write(&keys)
        } else {
            Err(Error::VersionNotFound(version))
        }
    }

    pub fn current_version(&self) -> Result<KeyVersion, Error> {
        self.read().map(|keys| keys.current)
    }

    pub fn encrypt(
        &self,
        network_addresses: &[NetworkAddress],
        account: AccountAddress,
        seq_num: u64,
    ) -> Result<Vec<u8>, Error> {
        let keys = self.read()?;
        let key = keys
            .keys
            .get(&keys.current)
            .ok_or(Error::VersionNotFound(keys.current))?;
        let mut enc_addrs = Vec::new();
        for (idx, addr) in network_addresses.iter().cloned().enumerate() {
            enc_addrs.push(addr.encrypt(&key.0, keys.current, &account, seq_num, idx as u32)?);
        }
        bcs::to_bytes(&enc_addrs).map_err(|e| e.into())
    }

    pub fn decrypt(
        &self,
        encrypted_network_addresses: &[u8],
        account: AccountAddress,
    ) -> Result<Vec<NetworkAddress>, Error> {
        let keys = self.read()?;
        let enc_addrs: Vec<EncNetworkAddress> = bcs::from_bytes(&encrypted_network_addresses)
            .map_err(|e| Error::AddressDeserialization(account, e.to_string()))?;
        let mut addrs = Vec::new();
        for (idx, enc_addr) in enc_addrs.iter().enumerate() {
            let key = keys
                .keys
                .get(&enc_addr.key_version())
                .ok_or_else(|| Error::VersionNotFound(enc_addr.key_version()))?;
            let addr = enc_addr
                .clone()
                .decrypt(&key.0, &account, idx as u32)
                .map_err(|e| Error::DecryptionError(account, e.to_string()))?;
            addrs.push(addr);
        }
        Ok(addrs)
    }

    pub fn initialize(&mut self) -> Result<(), Error> {
        self.write(&ValidatorKeys::default())
    }

    pub fn initialize_for_testing(&mut self) -> Result<(), Error> {
        self.initialize()?;
        self.add_key(
            TEST_SHARED_VAL_NETADDR_KEY_VERSION,
            TEST_SHARED_VAL_NETADDR_KEY,
        )?;
        self.set_current_version(TEST_SHARED_VAL_NETADDR_KEY_VERSION)
    }

    fn read(&self) -> Result<ValidatorKeys, Error> {
        let result = self
            .storage
            .get::<ValidatorKeys>(VALIDATOR_NETWORK_ADDRESS_KEYS)
            .map(|v| v.value)
            .map_err(|e| e.into());

        match &result {
            Ok(keys) => {
                *self.cached_keys.write() = Some(keys.clone());
            }
            Err(err) => diem_logger::error!(
                "Unable to read {} from storage: {}",
                VALIDATOR_NETWORK_ADDRESS_KEYS,
                err
            ),
        }

        let keys = self.cached_keys.read();
        keys.as_ref().map_or(result, |v| Ok(v.clone()))
    }

    fn write(&mut self, keys: &ValidatorKeys) -> Result<(), Error> {
        self.storage
            .set(VALIDATOR_NETWORK_ADDRESS_KEYS, keys)
            .map_err(|e| e.into())
    }
}

#[derive(Clone, Debug, Deserialize, Serialize)]
struct StorageKey(
    #[serde(
        serialize_with = "diem_secure_storage::to_base64",
        deserialize_with = "from_base64"
    )]
    Key,
);

pub fn to_base64<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    serializer.serialize_str(&base64::encode(bytes))
}

pub fn from_base64<'de, D>(deserializer: D) -> Result<Key, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let s: String = serde::Deserialize::deserialize(deserializer)?;
    base64::decode(s)
        .map_err(serde::de::Error::custom)
        .and_then(|v| {
            std::convert::TryInto::try_into(v.as_slice()).map_err(serde::de::Error::custom)
        })
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ValidatorKeys {
    keys: HashMap<KeyVersion, StorageKey>,
    current: KeyVersion,
}

impl Default for ValidatorKeys {
    fn default() -> Self {
        ValidatorKeys {
            current: 0,
            keys: HashMap::new(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use diem_secure_storage::InMemoryStorage;
    use rand::{rngs::OsRng, Rng, RngCore, SeedableRng};

    #[test]
    fn e2e() {
        let storage = Storage::InMemoryStorage(InMemoryStorage::new());
        let mut encryptor = Encryptor::new(storage);
        encryptor.initialize().unwrap();

        let mut rng = rand::rngs::StdRng::from_seed(OsRng.gen());
        let mut key = [0; network_address::encrypted::KEY_LEN];
        rng.fill_bytes(&mut key);
        encryptor.add_key(0, key).unwrap();
        encryptor.set_current_version(0).unwrap();
        rng.fill_bytes(&mut key);
        encryptor.add_key(1, key).unwrap();
        encryptor.set_current_version(1).unwrap();
        rng.fill_bytes(&mut key);
        encryptor.add_key(4, key).unwrap();
        encryptor.set_current_version(4).unwrap();

        encryptor.set_current_version(5).unwrap_err();

        let addr = std::str::FromStr::from_str("/ip4/10.0.0.16/tcp/80").unwrap();
        let addrs = vec![addr];
        let account = AccountAddress::random();

        let enc_addrs = encryptor.encrypt(&addrs, account, 5).unwrap();
        let dec_addrs = encryptor.decrypt(&enc_addrs, account).unwrap();
        assert_eq!(addrs, dec_addrs);

        let another_account = AccountAddress::random();
        encryptor.decrypt(&enc_addrs, another_account).unwrap_err();
    }

    #[test]
    fn cache_test() {
        // Prepare some initial data and verify e2e
        let mut encryptor = Encryptor::for_testing();
        let addr = std::str::FromStr::from_str("/ip4/10.0.0.16/tcp/80").unwrap();
        let addrs = vec![addr];
        let account = AccountAddress::random();

        let enc_addrs = encryptor.encrypt(&addrs, account, 0).unwrap();
        let dec_addrs = encryptor.decrypt(&enc_addrs, account).unwrap();
        assert_eq!(addrs, dec_addrs);

        // Reset storage and we should use cache
        encryptor.storage = Storage::from(InMemoryStorage::new());
        let enc_addrs = encryptor.encrypt(&addrs, account, 1).unwrap();
        let dec_addrs = encryptor.decrypt(&enc_addrs, account).unwrap();
        assert_eq!(addrs, dec_addrs);

        // Reset cache and we should get an err
        *encryptor.cached_keys.write() = None;
        encryptor.encrypt(&addrs, account, 1).unwrap_err();
        encryptor.decrypt(&enc_addrs, account).unwrap_err();
    }

    // The only purpose of this test is to generate a baseline for vault
    #[ignore]
    #[test]
    fn initializer() {
        let storage = Storage::VaultStorage(diem_secure_storage::VaultStorage::new(
            "http://127.0.0.1:8200".to_string(),
            "root_token".to_string(),
            Some("network_address_encryption_keys".to_string()),
            None,
            None,
            true,
            None,
            None,
        ));
        let mut encryptor = Encryptor::new(storage);
        encryptor.initialize().unwrap();
        let mut rng = rand::rngs::StdRng::from_seed(OsRng.gen());
        let mut key = [0; network_address::encrypted::KEY_LEN];
        rng.fill_bytes(&mut key);
        encryptor.add_key(0, key).unwrap();
        encryptor.set_current_version(0).unwrap();
    }
}
<|endoftext|>


<commit_before>use std::borrow::Cow::Owned;

use pulldown_cmark as cmark;
use self::cmark::{Parser, Event, Tag, Options, OPTION_ENABLE_TABLES, OPTION_ENABLE_FOOTNOTES};
use slug::slugify;
use syntect::easy::HighlightLines;
use syntect::html::{start_coloured_html_snippet, styles_to_coloured_html, IncludeBackground};

use errors::Result;
use utils::site::resolve_internal_link;
use highlighting::{get_highlighter, THEME_SET};

use table_of_contents::{TempHeader, Header, make_table_of_contents};
use context::RenderContext;

// We might have cases where the slug is already present in our list of anchor
// for example an article could have several titles named Example
// We add a counter after the slug if the slug is already present, which
// means we will have example, example-1, example-2 etc
fn find_anchor(anchors: &[String], name: String, level: u8) -> String {
    if level == 0 && !anchors.contains(&name) {
        return name.to_string();
    }

    let new_anchor = format!("{}-{}", name, level + 1);
    if !anchors.contains(&new_anchor) {
        return new_anchor;
    }

    find_anchor(anchors, name, level + 1)
}

fn is_colocated_asset_link(link: &str) -> bool {
    !link.contains("/")  // http://, ftp://, ../ etc
        && !link.starts_with("mailto:")
}

pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<(String, Vec)> {
    // the rendered html
    let mut html = String::with_capacity(content.len());
    // Set while parsing
    let mut error = None;

    let mut highlighter: Option<HighlightLines> = None;
    // If we get text in header, we need to insert the id and a anchor
    let mut in_header = false;
    // pulldown_cmark can send several text events for a title if there are markdown
    // specific characters like `!` in them. We only want to insert the anchor the first time
    let mut header_created = false;
    let mut anchors: Vec<String> = vec![];

    let mut headers = vec![];
    // Defaults to a 0 level so not a real header
    // It should be an Option ideally but not worth the hassle to update
    let mut temp_header = TempHeader::default();

    let mut opts = Options::empty();
    opts.insert(OPTION_ENABLE_TABLES);
    opts.insert(OPTION_ENABLE_FOOTNOTES);

    {
        let parser = Parser::new_ext(content, opts).map(|event| {
            match event {
                Event::Text(text) => {
                    // Header first
                    if in_header {
                        if header_created {
                            temp_header.push(&text);
                            return Event::Html(Owned(String::new()));
                        }
                        let id = find_anchor(&anchors, slugify(&text), 0);
                        anchors.push(id.clone());
                        // update the header and add it to the list
                        temp_header.permalink = format!("{}#{}", context.current_page_permalink, id);
                        temp_header.id = id;
                        // += as we might have some  or other things already there
                        temp_header.title += &text;
                        header_created = true;
                        return Event::Html(Owned(String::new()));
                    }

                    // if we are in the middle of a code block
                    if let Some(ref mut highlighter) = highlighter {
                        let highlighted = &highlighter.highlight(&text);
                        let html = styles_to_coloured_html(highlighted, IncludeBackground::Yes);
                        return Event::Html(Owned(html));
                    }

                    // Business as usual
                    Event::Text(text)
                },
                Event::Start(Tag::CodeBlock(ref info)) => {
                    if !context.config.highlight_code {
                        return Event::Html(Owned("
".to_string()));
                    }

                    let theme = &THEME_SET.themes[&context.config.highlight_theme];
                    highlighter = Some(get_highlighter(&theme, info));
                    let snippet = start_coloured_html_snippet(theme);
                    Event::Html(Owned(snippet))
                },
                Event::End(Tag::CodeBlock(_)) => {
                    if !context.config.highlight_code {
                        return Event::Html(Owned("
\n".to_string()))
                    }
                    // reset highlight and close the code block
                    highlighter = None;
                    Event::Html(Owned("
".to_string()))
                },
                Event::Start(Tag::Image(src, title)) => {
                    if is_colocated_asset_link(&src) {
                        return Event::Start(
                            Tag::Image(Owned(format!("{}{}", context.current_page_permalink, src)), title)
                        );
                    }

                    Event::Start(Tag::Link(src, title))
                },
                Event::Start(Tag::Link(link, title)) => {
                    // A few situations here:
                    // - it could be a relative link (starting with `./`)
                    // - it could be a link to a co-located asset
                    // - it could be a normal link
                    // - any of those can be in a header or not: if it's in a header we need to append to a string
                    let fixed_link = if link.starts_with("./") {
                        match resolve_internal_link(&link, context.permalinks) {
                            Ok(url) => url,
                            Err(_) => {
                                error = Some(format!("Relative link {} not found.", link).into());
                                return Event::Html(Owned(String::new()));
                            }
                        }
                    } else if is_colocated_asset_link(&link) {
                        format!("{}{}", context.current_page_permalink, link)
                    } else {
                        link.to_string()
                    };

                    if in_header {
                        let html = if title.is_empty() {
                            format!("", fixed_link)
                        } else {
                            format!("", fixed_link, title)
                        };
                        temp_header.push(&html);
                        return Event::Html(Owned(String::new()));
                    }

                    Event::Start(Tag::Link(Owned(fixed_link), title))
                },
                Event::End(Tag::Link(_, _)) => {
                    if in_header {
                        temp_header.push("");
                        return Event::Html(Owned(String::new()));
                    }
                    event
                },
                Event::Start(Tag::Code) => {
                    if in_header {
                        temp_header.push("");
                        return Event::Html(Owned(String::new()));
                    }
                    event
                },
                Event::End(Tag::Code) => {
                    if in_header {
                        temp_header.push("");
                        return Event::Html(Owned(String::new()));
                    }
                    event
                },
                Event::Start(Tag::Header(num)) => {
                    in_header = true;
                    temp_header = TempHeader::new(num);
                    Event::Html(Owned(String::new()))
                },
                Event::End(Tag::Header(_)) => {
                    // End of a header, reset all the things and return the stringified version of the header
                    in_header = false;
                    header_created = false;
                    let val = temp_header.to_string(context.tera, context.insert_anchor);
                    headers.push(temp_header.clone());
                    temp_header = TempHeader::default();
                    Event::Html(Owned(val))
                },
                _ => event,
            }
        });

        cmark::html::push_html(&mut html, parser);
    }

    match error {
        Some(e) => Err(e),
        None => Ok((
            html.replace("

", "").replace("

", "
"),
            make_table_of_contents(&headers)
        )),
    }
}
<commit_msg>Fix image in markdown rendering<commit_after>use std::borrow::Cow::Owned;

use pulldown_cmark as cmark;
use self::cmark::{Parser, Event, Tag, Options, OPTION_ENABLE_TABLES, OPTION_ENABLE_FOOTNOTES};
use slug::slugify;
use syntect::easy::HighlightLines;
use syntect::html::{start_coloured_html_snippet, styles_to_coloured_html, IncludeBackground};

use errors::Result;
use utils::site::resolve_internal_link;
use highlighting::{get_highlighter, THEME_SET};

use table_of_contents::{TempHeader, Header, make_table_of_contents};
use context::RenderContext;

// We might have cases where the slug is already present in our list of anchor
// for example an article could have several titles named Example
// We add a counter after the slug if the slug is already present, which
// means we will have example, example-1, example-2 etc
fn find_anchor(anchors: &[String], name: String, level: u8) -> String {
    if level == 0 && !anchors.contains(&name) {
        return name.to_string();
    }

    let new_anchor = format!("{}-{}", name, level + 1);
    if !anchors.contains(&new_anchor) {
        return new_anchor;
    }

    find_anchor(anchors, name, level + 1)
}

fn is_colocated_asset_link(link: &str) -> bool {
    !link.contains("/")  // http://, ftp://, ../ etc
        && !link.starts_with("mailto:")
}

pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<(String, Vec)> {
    // the rendered html
    let mut html = String::with_capacity(content.len());
    // Set while parsing
    let mut error = None;

    let mut highlighter: Option<HighlightLines> = None;
    // If we get text in header, we need to insert the id and a anchor
    let mut in_header = false;
    // pulldown_cmark can send several text events for a title if there are markdown
    // specific characters like `!` in them. We only want to insert the anchor the first time
    let mut header_created = false;
    let mut anchors: Vec<String> = vec![];

    let mut headers = vec![];
    // Defaults to a 0 level so not a real header
    // It should be an Option ideally but not worth the hassle to update
    let mut temp_header = TempHeader::default();

    let mut opts = Options::empty();
    opts.insert(OPTION_ENABLE_TABLES);
    opts.insert(OPTION_ENABLE_FOOTNOTES);

    {
        let parser = Parser::new_ext(content, opts).map(|event| {
            match event {
                Event::Text(text) => {
                    // Header first
                    if in_header {
                        if header_created {
                            temp_header.push(&text);
                            return Event::Html(Owned(String::new()));
                        }
                        let id = find_anchor(&anchors, slugify(&text), 0);
                        anchors.push(id.clone());
                        // update the header and add it to the list
                        temp_header.permalink = format!("{}#{}", context.current_page_permalink, id);
                        temp_header.id = id;
                        // += as we might have some  or other things already there
                        temp_header.title += &text;
                        header_created = true;
                        return Event::Html(Owned(String::new()));
                    }

                    // if we are in the middle of a code block
                    if let Some(ref mut highlighter) = highlighter {
                        let highlighted = &highlighter.highlight(&text);
                        let html = styles_to_coloured_html(highlighted, IncludeBackground::Yes);
                        return Event::Html(Owned(html));
                    }

                    // Business as usual
                    Event::Text(text)
                },
                Event::Start(Tag::CodeBlock(ref info)) => {
                    if !context.config.highlight_code {
                        return Event::Html(Owned("
".to_string()));
                    }

                    let theme = &THEME_SET.themes[&context.config.highlight_theme];
                    highlighter = Some(get_highlighter(&theme, info));
                    let snippet = start_coloured_html_snippet(theme);
                    Event::Html(Owned(snippet))
                },
                Event::End(Tag::CodeBlock(_)) => {
                    if !context.config.highlight_code {
                        return Event::Html(Owned("
\n".to_string()))
                    }
                    // reset highlight and close the code block
                    highlighter = None;
                    Event::Html(Owned("
".to_string()))
                },
                Event::Start(Tag::Image(src, title)) => {
                    if is_colocated_asset_link(&src) {
                        return Event::Start(
                            Tag::Image(Owned(format!("{}{}", context.current_page_permalink, src)), title)
                        );
                    }

                    Event::Start(Tag::Image(src, title))
                },
                Event::Start(Tag::Link(link, title)) => {
                    // A few situations here:
                    // - it could be a relative link (starting with `./`)
                    // - it could be a link to a co-located asset
                    // - it could be a normal link
                    // - any of those can be in a header or not: if it's in a header we need to append to a string
                    let fixed_link = if link.starts_with("./") {
                        match resolve_internal_link(&link, context.permalinks) {
                            Ok(url) => url,
                            Err(_) => {
                                error = Some(format!("Relative link {} not found.", link).into());
                                return Event::Html(Owned(String::new()));
                            }
                        }
                    } else if is_colocated_asset_link(&link) {
                        format!("{}{}", context.current_page_permalink, link)
                    } else {
                        link.to_string()
                    };

                    if in_header {
                        let html = if title.is_empty() {
                            format!("", fixed_link)
                        } else {
                            format!("", fixed_link, title)
                        };
                        temp_header.push(&html);
                        return Event::Html(Owned(String::new()));
                    }

                    Event::Start(Tag::Link(Owned(fixed_link), title))
                },
                Event::End(Tag::Link(_, _)) => {
                    if in_header {
                        temp_header.push("");
                        return Event::Html(Owned(String::new()));
                    }
                    event
                },
                Event::Start(Tag::Code) => {
                    if in_header {
                        temp_header.push("");
                        return Event::Html(Owned(String::new()));
                    }
                    event
                },
                Event::End(Tag::Code) => {
                    if in_header {
                        temp_header.push("");
                        return Event::Html(Owned(String::new()));
                    }
                    event
                },
                Event::Start(Tag::Header(num)) => {
                    in_header = true;
                    temp_header = TempHeader::new(num);
                    Event::Html(Owned(String::new()))
                },
                Event::End(Tag::Header(_)) => {
                    // End of a header, reset all the things and return the stringified version of the header
                    in_header = false;
                    header_created = false;
                    let val = temp_header.to_string(context.tera, context.insert_anchor);
                    headers.push(temp_header.clone());
                    temp_header = TempHeader::default();
                    Event::Html(Owned(val))
                },
                _ => event,
            }
        });

        cmark::html::push_html(&mut html, parser);
    }

    match error {
        Some(e) => Err(e),
        None => Ok((
            html.replace("

", "").replace("

", "
"),
            make_table_of_contents(&headers)
        )),
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::PerformanceBinding;
use dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceMethods;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, Root};
use dom::bindings::num::Finite;
use dom::bindings::utils::{Reflector, reflect_dom_object};
use dom::performancetiming::PerformanceTiming;
use dom::window::Window;
use time;

pub type DOMHighResTimeStamp = Finite<f64>;

#[dom_struct]
pub struct Performance {
    reflector_: Reflector,
    timing: JS<PerformanceTiming>,
}

impl Performance {
    fn new_inherited(window: &Window,
                     navigation_start: u64,
                     navigation_start_precise: f64) -> Performance {
        Performance {
            reflector_: Reflector::new(),
            timing: JS::from_rooted(&PerformanceTiming::new(window,
                                                            navigation_start,
                                                            navigation_start_precise)),
        }
    }

    pub fn new(window: &Window,
               navigation_start: u64,
               navigation_start_precise: f64) -> Root<Performance> {
        reflect_dom_object(box Performance::new_inherited(window,
                                                          navigation_start,
                                                          navigation_start_precise),
                           GlobalRef::Window(window),
                           PerformanceBinding::Wrap)
    }
}

impl PerformanceMethods for Performance {
    // https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html#performance-timing-attribute
    fn Timing(&self) -> Root<PerformanceTiming> {
        self.timing.root()
    }

    // https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HighResolutionTime/Overview.html#dom-performance-now
    fn Now(&self) -> DOMHighResTimeStamp {
        let navStart = self.timing.root().r().NavigationStartPrecise();
        let now = (time::precise_time_ns() as f64 - navStart) / 1000000 as f64;
        Finite::wrap(now)
    }
}
<commit_msg>Do not root Performance::timing<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::PerformanceBinding;
use dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceMethods;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, Root};
use dom::bindings::num::Finite;
use dom::bindings::utils::{Reflector, reflect_dom_object};
use dom::performancetiming::PerformanceTiming;
use dom::window::Window;
use time;

pub type DOMHighResTimeStamp = Finite<f64>;

#[dom_struct]
pub struct Performance {
    reflector_: Reflector,
    timing: JS<PerformanceTiming>,
}

impl Performance {
    fn new_inherited(window: &Window,
                     navigation_start: u64,
                     navigation_start_precise: f64) -> Performance {
        Performance {
            reflector_: Reflector::new(),
            timing: JS::from_rooted(&PerformanceTiming::new(window,
                                                            navigation_start,
                                                            navigation_start_precise)),
        }
    }

    pub fn new(window: &Window,
               navigation_start: u64,
               navigation_start_precise: f64) -> Root<Performance> {
        reflect_dom_object(box Performance::new_inherited(window,
                                                          navigation_start,
                                                          navigation_start_precise),
                           GlobalRef::Window(window),
                           PerformanceBinding::Wrap)
    }
}

impl PerformanceMethods for Performance {
    // https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html#performance-timing-attribute
    fn Timing(&self) -> Root<PerformanceTiming> {
        self.timing.root()
    }

    // https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HighResolutionTime/Overview.html#dom-performance-now
    fn Now(&self) -> DOMHighResTimeStamp {
        let navStart = self.timing.NavigationStartPrecise();
        let now = (time::precise_time_ns() as f64 - navStart) / 1000000 as f64;
        Finite::wrap(now)
    }
}
<|endoftext|>


<commit_before>//
use std::rc::Rc;
use std::cell::RefCell;

//
use core::buffer::Buffer;
use core::buffer::OpenMode;

//
use core::bufferlog::BufferLog;
use core::bufferlog::BufferOperationType;
use core::bufferlog::BufferOperation;

//
pub type Id = u64;

///
#[derive(Default)]
pub struct DocumentBuilder {
    internal: bool,
    document_name: String,
    file_name: String,
}

///
impl DocumentBuilder {
    ///
    pub fn new() -> Self {
        Self {
            internal: false,
            document_name: String::new(),
            file_name: String::new(),
        }
    }

    ///
    pub fn internal(&self, flag: bool) -> Self {
        Self {
            internal: flag,
            document_name: self.document_name.clone(),
            file_name: self.file_name.clone(),
        }
    }

    ///
    pub fn document_name(&self, name: &str) -> Self {
        let mut s = String::new();
        s.push_str(name);

        Self {
            internal: self.internal,
            document_name: s,
            file_name: self.file_name.clone(),
        }
    }

    ///
    pub fn file_name(&self, name: &str) -> Self {
        let mut s = String::new();
        s.push_str(name);

        Self {
            internal: self.internal,
            document_name: self.document_name.clone(),
            file_name: s,
        }
    }

    ///
    pub fn finalize<'a>(&self) -> Option<Rc<RefCell<Document<'a>>>> {
        let file_name = self.file_name.to_owned();
        let buffer = Buffer::new(file_name, OpenMode::ReadWrite);
        let buffer = match buffer {
            Some(bb) => bb,
            None => return None,
        };

        Some(Rc::new(RefCell::new(Document {
            id: 0,
            name: self.document_name.clone(),
            buffer: buffer,
            buffer_log: BufferLog::new(),
            changed: false,
        })))
    }
}

#[derive(Debug)]
pub struct Document<'a> {
    pub id: Id,
    pub name: String,
    pub buffer: Buffer<'a>,
    pub buffer_log: BufferLog,
    pub changed: bool,
}

impl<'a> Document<'a> {
    pub fn sync_to_disk(&self) -> ::std::io::Result<()> {
        let tmp_file_ext = "unlimited.bk"; // TODO: move to global config
        let tmp_file_name = format!("{}.{}", self.buffer.file_name, tmp_file_ext);
        self.buffer.sync_to_disk(&tmp_file_name)
    }

    /// copy the content of the buffer up to 'nr_bytes' into the data Vec
    /// the read bytes are appended to the data Vec
    /// return XXX on error (use ioresult)
    pub fn read(&self, offset: u64, nr_bytes: usize, data: &mut Vec<u8>) -> usize {
        self.buffer.read(offset, nr_bytes, data)
    }

    /// insert the 'data' Vec content in the buffer up to 'nr_bytes'
    /// return the number of written bytes (TODO: use io::Result)
    pub fn insert(&mut self, offset: u64, nr_bytes: usize, data: &[u8]) -> usize {
        // log insert op
        let mut ins_data = Vec::with_capacity(nr_bytes);
        ins_data.extend(&data[..nr_bytes]);

        self.buffer_log
            .add(offset, BufferOperationType::Insert, ins_data);

        self.buffer.insert(offset, nr_bytes, &data[..nr_bytes])
    }

    /// remove up to 'nr_bytes' from the buffer starting at offset
    /// if removed_data is provided will call self.read(offset, nr_bytes, data)
    /// before remove the bytes
    pub fn remove(
        &mut self,
        offset: u64,
        nr_bytes: usize,
        removed_data: Option<&mut Vec<u8>>,
    ) -> usize {
        let mut rm_data = Vec::with_capacity(nr_bytes);

        let nr_bytes_removed = self.buffer.remove(offset, nr_bytes, Some(&mut rm_data));

        if let Some(v) = removed_data {
            v.extend(rm_data.clone());
        }

        self.buffer_log
            .add(offset, BufferOperationType::Remove, rm_data);

        nr_bytes_removed
    }

    fn apply_log_operation(&mut self, op: &BufferOperation) -> Option<u64> {
        // apply op
        let mark_offset = match op.op {
            BufferOperationType::Insert => {
                self.buffer.insert(op.offset, op.data.len(), &op.data);

                op.offset + op.data.len() as u64
            }
            BufferOperationType::Remove => {
                self.buffer.remove(op.offset, op.data.len(), None);
                op.offset
            }
        };

        Some(mark_offset)
    }

    pub fn undo(&mut self) -> Option<u64> {
        // read current log position
        let pos = self.buffer_log.pos;
        if pos == 0 {
            return None;
        }

        // get inverted operation
        let op = self.buffer_log.data[pos - 1].invert();
        self.buffer_log.pos -= 1;
        self.apply_log_operation(&op)
    }

    pub fn redo(&mut self) -> Option<u64> {
        // read current log position
        let pos = self.buffer_log.pos;
        if pos == self.buffer_log.data.len() {
            return None;
        }

        // replay previous op
        let op = self.buffer_log.data[pos].clone();
        self.buffer_log.pos += 1;
        self.apply_log_operation(&op)
    }
}
<commit_msg>update docs<commit_after>//
use std::rc::Rc;
use std::cell::RefCell;

//
use core::buffer::Buffer;
use core::buffer::OpenMode;

//
use core::bufferlog::BufferLog;
use core::bufferlog::BufferOperationType;
use core::bufferlog::BufferOperation;

//
pub type Id = u64;

///
#[derive(Default)]
pub struct DocumentBuilder {
    internal: bool,
    document_name: String,
    file_name: String,
}

///
impl DocumentBuilder {
    ///
    pub fn new() -> Self {
        Self {
            internal: false,
            document_name: String::new(),
            file_name: String::new(),
        }
    }

    ///
    pub fn internal(&self, flag: bool) -> Self {
        Self {
            internal: flag,
            document_name: self.document_name.clone(),
            file_name: self.file_name.clone(),
        }
    }

    ///
    pub fn document_name(&self, name: &str) -> Self {
        let mut s = String::new();
        s.push_str(name);

        Self {
            internal: self.internal,
            document_name: s,
            file_name: self.file_name.clone(),
        }
    }

    ///
    pub fn file_name(&self, name: &str) -> Self {
        let mut s = String::new();
        s.push_str(name);

        Self {
            internal: self.internal,
            document_name: self.document_name.clone(),
            file_name: s,
        }
    }

    ///
    pub fn finalize<'a>(&self) -> Option<Rc<RefCell<Document<'a>>>> {
        let file_name = self.file_name.to_owned();
        let buffer = Buffer::new(file_name, OpenMode::ReadWrite);
        let buffer = match buffer {
            Some(bb) => bb,
            None => return None,
        };

        Some(Rc::new(RefCell::new(Document {
            id: 0,
            name: self.document_name.clone(),
            buffer: buffer,
            buffer_log: BufferLog::new(),
            changed: false,
        })))
    }
}

#[derive(Debug)]
pub struct Document<'a> {
    pub id: Id,
    pub name: String,
    pub buffer: Buffer<'a>,
    pub buffer_log: BufferLog,
    pub changed: bool,
}

impl<'a> Document<'a> {
    pub fn sync_to_disk(&self) -> ::std::io::Result<()> {
        let tmp_file_ext = "unlimited.bk"; // TODO: move to global config
        let tmp_file_name = format!("{}.{}", self.buffer.file_name, tmp_file_ext);
        self.buffer.sync_to_disk(&tmp_file_name)
    }

    /// copy the content of the buffer up to 'nr_bytes' into the data Vec
    /// the read bytes are appended to the data Vec
    /// return XXX on error (TODO: use ioresult)
    pub fn read(&self, offset: u64, nr_bytes: usize, data: &mut Vec<u8>) -> usize {
        self.buffer.read(offset, nr_bytes, data)
    }

    /// insert the 'data' Vec content in the buffer up to 'nr_bytes'
    /// return the number of written bytes (TODO: use io::Result)
    pub fn insert(&mut self, offset: u64, nr_bytes: usize, data: &[u8]) -> usize {
        // log insert op
        let mut ins_data = Vec::with_capacity(nr_bytes);
        ins_data.extend(&data[..nr_bytes]);

        self.buffer_log
            .add(offset, BufferOperationType::Insert, ins_data);

        self.buffer.insert(offset, nr_bytes, &data[..nr_bytes])
    }

    /// remove up to 'nr_bytes' from the buffer starting at offset
    /// if removed_data is provided will call self.read(offset, nr_bytes, data)
    /// before remove the bytes
    pub fn remove(
        &mut self,
        offset: u64,
        nr_bytes: usize,
        removed_data: Option<&mut Vec<u8>>,
    ) -> usize {
        let mut rm_data = Vec::with_capacity(nr_bytes);

        let nr_bytes_removed = self.buffer.remove(offset, nr_bytes, Some(&mut rm_data));

        if let Some(v) = removed_data {
            v.extend(rm_data.clone());
        }

        self.buffer_log
            .add(offset, BufferOperationType::Remove, rm_data);

        nr_bytes_removed
    }

    fn apply_log_operation(&mut self, op: &BufferOperation) -> Option<u64> {
        // apply op
        let mark_offset = match op.op {
            BufferOperationType::Insert => {
                self.buffer.insert(op.offset, op.data.len(), &op.data);

                op.offset + op.data.len() as u64
            }
            BufferOperationType::Remove => {
                self.buffer.remove(op.offset, op.data.len(), None);
                op.offset
            }
        };

        Some(mark_offset)
    }

    pub fn undo(&mut self) -> Option<u64> {
        // read current log position
        let pos = self.buffer_log.pos;
        if pos == 0 {
            return None;
        }

        // get inverted operation
        let op = self.buffer_log.data[pos - 1].invert();
        self.buffer_log.pos -= 1;
        self.apply_log_operation(&op)
    }

    pub fn redo(&mut self) -> Option<u64> {
        // read current log position
        let pos = self.buffer_log.pos;
        if pos == self.buffer_log.data.len() {
            return None;
        }

        // replay previous op
        let op = self.buffer_log.data[pos].clone();
        self.buffer_log.pos += 1;
        self.apply_log_operation(&op)
    }
}
<|endoftext|>