diff --git a/garnet/public/rust/fuchsia-inspect/src/vmo/block.rs b/garnet/public/rust/fuchsia-inspect/src/vmo/block.rs index fb8707a25cecb72f3d5b31daab0f67737d0138f1..af71b805bd4c91a2fd104723bbc5f3c654d90e2f 100644 --- a/garnet/public/rust/fuchsia-inspect/src/vmo/block.rs +++ b/garnet/public/rust/fuchsia-inspect/src/vmo/block.rs @@ -86,12 +86,14 @@ impl<T: ReadableBlockContainer> Block<T> { } /// Returns the magic number in a HEADER block. + #[cfg(test)] pub fn header_magic(&self) -> Result<u32, Error> { self.check_type(BlockType::Header)?; Ok(self.read_header().header_magic()) } /// Returns the version of a HEADER block. + #[cfg(test)] pub fn header_version(&self) -> Result<u32, Error> { self.check_type(BlockType::Header)?; Ok(self.read_header().header_version()) @@ -122,6 +124,7 @@ impl<T: ReadableBlockContainer> Block<T> { } /// Get the total length of the PROPERTY block. + #[cfg(test)] pub fn property_total_length(&self) -> Result<u32, Error> { self.check_type(BlockType::PropertyValue)?; Ok(self.read_payload().property_total_length()) @@ -140,6 +143,7 @@ impl<T: ReadableBlockContainer> Block<T> { } /// Returns the payload bytes value of an EXTENT block. + #[cfg(test)] pub fn extent_contents(&self) -> Result<Vec<u8>, Error> { self.check_type(BlockType::Extent)?; let length = utils::payload_size_for_order(self.order()); @@ -173,12 +177,14 @@ impl<T: ReadableBlockContainer> Block<T> { } /// Get the length of the name of a NAME block + #[cfg(test)] pub fn name_length(&self) -> Result<usize, Error> { self.check_type(BlockType::Name)?; Ok(self.read_header().name_length().to_usize().unwrap()) } /// Returns the contents of a NAME block. + #[cfg(test)] pub fn name_contents(&self) -> Result<String, Error> { self.check_type(BlockType::Name)?; let length = self.name_length()?; diff --git a/garnet/public/rust/fuchsia-inspect/src/vmo/block_type.rs b/garnet/public/rust/fuchsia-inspect/src/vmo/block_type.rs index f930bce23c8344af056ef65a6fea0d789a7d7428..873c0a0a3dc6b8cda77099de7de394f76b87b52b 100644 --- a/garnet/public/rust/fuchsia-inspect/src/vmo/block_type.rs +++ b/garnet/public/rust/fuchsia-inspect/src/vmo/block_type.rs @@ -75,6 +75,7 @@ impl BlockType { } } + #[cfg(test)] pub fn all() -> [BlockType; 11] { [ BlockType::Free, diff --git a/garnet/public/rust/fuchsia-inspect/src/vmo/heap.rs b/garnet/public/rust/fuchsia-inspect/src/vmo/heap.rs index 2a2ba2113bb2105d896fd0225801d27253eed060..edaa7af2585fb02a0178b52bb5a96f4d0a90bc10 100644 --- a/garnet/public/rust/fuchsia-inspect/src/vmo/heap.rs +++ b/garnet/public/rust/fuchsia-inspect/src/vmo/heap.rs @@ -93,7 +93,7 @@ impl Heap { } /// The bytes in this heap. - #[allow(dead_code)] // Used in testing. + #[cfg(test)] pub(in crate::vmo) fn bytes(&self) -> Vec<u8> { let mut result = vec![0u8; self.current_size_bytes]; self.mapping.read(&mut result[..]); diff --git a/garnet/public/rust/fuchsia-inspect/src/vmo/inspector.rs b/garnet/public/rust/fuchsia-inspect/src/vmo/inspector.rs deleted file mode 100644 index 8e7f26a1baffd8136e1d2797438cdbbb980ab41c..0000000000000000000000000000000000000000 --- a/garnet/public/rust/fuchsia-inspect/src/vmo/inspector.rs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2019 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use failure::{format_err, Error}; -use mapped_vmo::Mapping; -use parking_lot::Mutex; -use std::rc::Rc; -use std::sync::Arc; - -use crate::vmo::constants; -use crate::vmo::heap::Heap; -use crate::vmo::state::State; -use crate::vmo::types::Node; - -/// Root of the Inspect API -pub struct Inspector { - /// The root node. - root_node: Node, -} - -impl Inspector { - /// Create a new Inspect VMO object with the given maximum size. - pub fn new(max_size: usize, name: &str) -> Result<Self, Error> { - let (mapping, _) = Mapping::allocate(max_size) - .map_err(|e| format_err!("failed to allocate vmo zx status={}", e))?; - let heap = Heap::new(Rc::new(mapping))?; - let state = State::create(heap)?; - let root_node = - Node::allocate(Arc::new(Mutex::new(state)), name, constants::ROOT_PARENT_INDEX)?; - Ok(Inspector { root_node }) - } - - /// Create the root of the VMO object with the given |name|. - pub fn root(&self) -> &Node { - &self.root_node - } -} diff --git a/garnet/public/rust/fuchsia-inspect/src/vmo/mod.rs b/garnet/public/rust/fuchsia-inspect/src/vmo/mod.rs index eb069321a849f5a953570311f6d6506bcb5de96a..21f06b05efe027b8c02e7cd84799e080999219ed 100644 --- a/garnet/public/rust/fuchsia-inspect/src/vmo/mod.rs +++ b/garnet/public/rust/fuchsia-inspect/src/vmo/mod.rs @@ -2,13 +2,405 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +use failure::{format_err, Error}; +use mapped_vmo::Mapping; +use num_traits::ToPrimitive; +use parking_lot::Mutex; +use paste; +use std::marker::PhantomData; +use std::rc::Rc; +use std::sync::Arc; + +use crate::vmo::heap::Heap; +use crate::vmo::state::State; + mod bitfields; mod block; mod block_type; mod constants; mod heap; -pub mod inspector; -pub mod reader; +mod reader; mod state; -pub mod types; mod utils; + +/// Root of the Inspect API +pub struct Inspector { + /// The root node. + root_node: Node, +} + +/// Allowed formats for a Metric data type (uint, int, double) +pub trait MetricFormat {} +impl MetricFormat for u64 {} +impl MetricFormat for i64 {} +impl MetricFormat for f64 {} + +/// Provides functions required to implement formats for a property. +pub trait PropertyFormat { + fn bytes(&self) -> &[u8]; + fn flag(&self) -> u8; + fn length_in_bytes(&self) -> u32; +} + +/// Inspect API Node data type. +pub struct Node { + /// Index of the block in the VMO. + block_index: u32, + + /// Reference to the VMO heap. + state: Arc<Mutex<State>>, +} + +/// Inspect API Metric data type. +pub struct Metric<T: MetricFormat> { + /// Index of the block in the VMO. + block_index: u32, + + /// Reference to the VMO heap. + state: Arc<Mutex<State>>, + phantom: PhantomData<T>, +} + +/// Inspect API Property data type. +pub struct Property<T: PropertyFormat> { + /// Index of the block in the VMO. + block_index: u32, + + /// Reference to the VMO heap. + state: Arc<Mutex<State>>, + phantom: PhantomData<T>, +} + +/// Root API for inspect. Used to create the VMO and get the root node. +impl Inspector { + /// Create a new Inspect VMO object with the given maximum size. + pub fn new(max_size: usize, name: &str) -> Result<Self, Error> { + let (mapping, _) = Mapping::allocate(max_size) + .map_err(|e| format_err!("failed to allocate vmo zx status={}", e))?; + let heap = Heap::new(Rc::new(mapping))?; + let state = State::create(heap)?; + let root_node = + Node::allocate(Arc::new(Mutex::new(state)), name, constants::ROOT_PARENT_INDEX)?; + Ok(Inspector { root_node }) + } + + /// Create the root of the VMO object with the given |name|. + pub fn root(&self) -> &Node { + &self.root_node + } +} + +/// Implementation of property for a byte vector. +impl PropertyFormat for &[u8] { + fn bytes(&self) -> &[u8] { + &self + } + + fn flag(&self) -> u8 { + constants::PROPERTY_FLAG_BYTE_VECTOR + } + + fn length_in_bytes(&self) -> u32 { + self.len().to_u32().unwrap() + } +} + +/// Implementation of property for a string. +impl PropertyFormat for &str { + fn bytes(&self) -> &[u8] { + self.as_bytes() + } + + fn flag(&self) -> u8 { + constants::PROPERTY_FLAG_STRING + } + + fn length_in_bytes(&self) -> u32 { + self.bytes().len().to_u32().unwrap() + } +} + +/// Implementation of property for a string. +impl PropertyFormat for String { + fn bytes(&self) -> &[u8] { + self.as_bytes() + } + + fn flag(&self) -> u8 { + constants::PROPERTY_FLAG_STRING + } + + fn length_in_bytes(&self) -> u32 { + self.bytes().len().to_u32().unwrap() + } +} + +/// Utility for generating functions to create a metric. +/// `name`: identifier for the name (example: double) +/// `type`: the type of the metric (example: f64) +macro_rules! create_metric_fn { + ($name:ident, $type:ident) => { + paste::item! { + pub fn [<create_ $name _metric>](&self, name: &str, value: $type) + -> Result<Metric<$type>, Error> { + let block = self.state.lock().[<create_ $name _metric>]( + name, value, self.block_index)?; + Ok(Metric::<$type> {state: self.state.clone(), block_index: block.index(), phantom: PhantomData}) + } + } + }; +} + +impl Node { + /// Allocate a new NODE object. Called through Inspector. + pub(in crate::vmo) fn allocate( + state: Arc<Mutex<State>>, + name: &str, + parent_index: u32, + ) -> Result<Self, Error> { + let block = state.lock().create_node(name, parent_index)?; + Ok(Node { state: state.clone(), block_index: block.index() }) + } + + /// Add a child to this node. + pub fn create_child(&self, name: &str) -> Result<Node, Error> { + let block = self.state.lock().create_node(name, self.block_index)?; + Ok(Node { state: self.state.clone(), block_index: block.index() }) + } + + /// Add a metric to this node: create_int_metric, create_double_metric, + /// create_uint_metric. + create_metric_fn!(int, i64); + create_metric_fn!(uint, u64); + create_metric_fn!(double, f64); + + /// Add a property to this node. + pub fn create_property<T: PropertyFormat>( + &self, + name: &str, + value: T, + ) -> Result<Property<T>, Error> { + let block = self.state.lock().create_property(name, value, self.block_index)?; + Ok(Property::<T> { + state: self.state.clone(), + block_index: block.index(), + phantom: PhantomData, + }) + } +} + +impl Drop for Node { + fn drop(&mut self) { + self.state + .lock() + .free_value(self.block_index) + .expect(&format!("Failed to free node index={}", self.block_index)); + } +} + +/// Utility for generating metric functions (example: set, add, subtract) +/// `fn_name`: the name of the function to generate (example: set) +/// `type`: the type of the argument of the function to generate (example: f64) +/// `name`: the readble name of the type of the function (example: double) +macro_rules! metric_fn { + ($fn_name:ident, $type:ident, $name:ident) => { + paste::item! { + pub fn $fn_name(&self, value: $type) -> Result<(), Error> { + self.state.lock().[<$fn_name _ $name _metric>](self.block_index, value) + } + } + }; +} + +/// Utility for generating a metric datatype impl +/// `name`: the readble name of the type of the function (example: double) +/// `type`: the type of the argument of the function to generate (example: f64) +macro_rules! metric_impl { + ($name:ident, $type:ident) => { + impl Metric<$type> { + metric_fn!(set, $type, $name); + metric_fn!(add, $type, $name); + metric_fn!(subtract, $type, $name); + } + }; +} + +metric_impl!(int, i64); +metric_impl!(uint, u64); +metric_impl!(double, f64); + +impl<T: MetricFormat> Drop for Metric<T> { + fn drop(&mut self) { + self.state + .lock() + .free_value(self.block_index) + .expect(&format!("Failed to free metric index={}", self.block_index)); + } +} + +impl<T: PropertyFormat> Property<T> { + /// Set a property value. + pub fn set(&self, value: T) -> Result<(), Error> { + self.state.lock().set_property(self.block_index, value) + } +} + +impl<T: PropertyFormat> Drop for Property<T> { + fn drop(&mut self) { + self.state + .lock() + .free_property(self.block_index) + .expect(&format!("Failed to free property index={}", self.block_index)); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::vmo::block_type::BlockType; + use crate::vmo::constants; + use crate::vmo::heap::Heap; + use mapped_vmo::Mapping; + use std::rc::Rc; + + #[test] + fn node() { + let mapping = Rc::new(Mapping::allocate(4096).unwrap().0); + let state = get_state(mapping.clone()); + let node = Node::allocate(state, "root", constants::HEADER_INDEX).unwrap(); + let node_block = node.state.lock().heap.get_block(node.block_index).unwrap(); + assert_eq!(node_block.block_type(), BlockType::NodeValue); + assert_eq!(node_block.child_count().unwrap(), 0); + { + let child = node.create_child("child").unwrap(); + let child_block = node.state.lock().heap.get_block(child.block_index).unwrap(); + assert_eq!(child_block.block_type(), BlockType::NodeValue); + assert_eq!(child_block.child_count().unwrap(), 0); + assert_eq!(node_block.child_count().unwrap(), 1); + } + assert_eq!(node_block.child_count().unwrap(), 0); + } + + #[test] + fn double_metric() { + let mapping = Rc::new(Mapping::allocate(4096).unwrap().0); + let state = get_state(mapping.clone()); + let node = Node::allocate(state, "root", constants::HEADER_INDEX).unwrap(); + let node_block = node.state.lock().heap.get_block(node.block_index).unwrap(); + { + let metric = node.create_double_metric("metric", 1.0).unwrap(); + let metric_block = node.state.lock().heap.get_block(metric.block_index).unwrap(); + assert_eq!(metric_block.block_type(), BlockType::DoubleValue); + assert_eq!(metric_block.double_value().unwrap(), 1.0); + assert_eq!(node_block.child_count().unwrap(), 1); + + assert!(metric.set(2.0).is_ok()); + assert_eq!(metric_block.double_value().unwrap(), 2.0); + + assert!(metric.subtract(5.5).is_ok()); + assert_eq!(metric_block.double_value().unwrap(), -3.5); + + assert!(metric.add(8.1).is_ok()); + assert_eq!(metric_block.double_value().unwrap(), 4.6); + } + assert_eq!(node_block.child_count().unwrap(), 0); + } + + #[test] + fn int_metric() { + let mapping = Rc::new(Mapping::allocate(4096).unwrap().0); + let state = get_state(mapping.clone()); + let node = Node::allocate(state, "root", constants::HEADER_INDEX).unwrap(); + let node_block = node.state.lock().heap.get_block(node.block_index).unwrap(); + { + let metric = node.create_int_metric("metric", 1).unwrap(); + let metric_block = node.state.lock().heap.get_block(metric.block_index).unwrap(); + assert_eq!(metric_block.block_type(), BlockType::IntValue); + assert_eq!(metric_block.int_value().unwrap(), 1); + assert_eq!(node_block.child_count().unwrap(), 1); + + assert!(metric.set(2).is_ok()); + assert_eq!(metric_block.int_value().unwrap(), 2); + + assert!(metric.subtract(5).is_ok()); + assert_eq!(metric_block.int_value().unwrap(), -3); + + assert!(metric.add(8).is_ok()); + assert_eq!(metric_block.int_value().unwrap(), 5); + } + assert_eq!(node_block.child_count().unwrap(), 0); + } + + #[test] + fn uint_metric() { + let mapping = Rc::new(Mapping::allocate(4096).unwrap().0); + let state = get_state(mapping.clone()); + let node = Node::allocate(state, "root", constants::HEADER_INDEX).unwrap(); + let node_block = node.state.lock().heap.get_block(node.block_index).unwrap(); + { + let metric = node.create_uint_metric("metric", 1).unwrap(); + let metric_block = node.state.lock().heap.get_block(metric.block_index).unwrap(); + assert_eq!(metric_block.block_type(), BlockType::UintValue); + assert_eq!(metric_block.uint_value().unwrap(), 1); + assert_eq!(node_block.child_count().unwrap(), 1); + + assert!(metric.set(5).is_ok()); + assert_eq!(metric_block.uint_value().unwrap(), 5); + + assert!(metric.subtract(3).is_ok()); + assert_eq!(metric_block.uint_value().unwrap(), 2); + + assert!(metric.add(8).is_ok()); + assert_eq!(metric_block.uint_value().unwrap(), 10); + } + assert_eq!(node_block.child_count().unwrap(), 0); + } + + #[test] + fn string_property() { + let mapping = Rc::new(Mapping::allocate(4096).unwrap().0); + let state = get_state(mapping.clone()); + let node = Node::allocate(state, "root", constants::HEADER_INDEX).unwrap(); + let node_block = node.state.lock().heap.get_block(node.block_index).unwrap(); + { + let property = node.create_property("property", "test").unwrap(); + let property_block = node.state.lock().heap.get_block(property.block_index).unwrap(); + assert_eq!(property_block.block_type(), BlockType::PropertyValue); + assert_eq!(property_block.property_total_length().unwrap(), 4); + assert_eq!(property_block.property_flags().unwrap(), constants::PROPERTY_FLAG_STRING); + assert_eq!(node_block.child_count().unwrap(), 1); + + assert!(property.set("test-set").is_ok()); + assert_eq!(property_block.property_total_length().unwrap(), 8); + } + assert_eq!(node_block.child_count().unwrap(), 0); + } + + #[test] + fn bytevector_property() { + let mapping = Rc::new(Mapping::allocate(4096).unwrap().0); + let state = get_state(mapping.clone()); + let node = Node::allocate(state, "root", constants::HEADER_INDEX).unwrap(); + let node_block = node.state.lock().heap.get_block(node.block_index).unwrap(); + { + let property = node.create_property("property", "test".as_bytes()).unwrap(); + let property_block = node.state.lock().heap.get_block(property.block_index).unwrap(); + assert_eq!(property_block.block_type(), BlockType::PropertyValue); + assert_eq!(property_block.property_total_length().unwrap(), 4); + assert_eq!( + property_block.property_flags().unwrap(), + constants::PROPERTY_FLAG_BYTE_VECTOR + ); + assert_eq!(node_block.child_count().unwrap(), 1); + + assert!(property.set("test-set".as_bytes()).is_ok()); + assert_eq!(property_block.property_total_length().unwrap(), 8); + } + assert_eq!(node_block.child_count().unwrap(), 0); + } + + fn get_state(mapping: Rc<Mapping>) -> Arc<Mutex<State>> { + let heap = Heap::new(mapping).unwrap(); + Arc::new(Mutex::new(State::create(heap).unwrap())) + } +} diff --git a/garnet/public/rust/fuchsia-inspect/src/vmo/reader.rs b/garnet/public/rust/fuchsia-inspect/src/vmo/reader.rs index efdebd89d6d5e79d6769037de3609a64402a4984..4c680e32e98804a091083faefc250d30bf2e0feb 100644 --- a/garnet/public/rust/fuchsia-inspect/src/vmo/reader.rs +++ b/garnet/public/rust/fuchsia-inspect/src/vmo/reader.rs @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#![cfg(test)] + use crate::vmo::block::Block; use crate::vmo::utils; diff --git a/garnet/public/rust/fuchsia-inspect/src/vmo/state.rs b/garnet/public/rust/fuchsia-inspect/src/vmo/state.rs index 7b813aca85c1a4a8c4f623ce401a5fba16efd3d9..6aa1317110f8efa12cc118f797bf588c7d9826e2 100644 --- a/garnet/public/rust/fuchsia-inspect/src/vmo/state.rs +++ b/garnet/public/rust/fuchsia-inspect/src/vmo/state.rs @@ -12,6 +12,7 @@ use crate::vmo::block_type::BlockType; use crate::vmo::constants; use crate::vmo::heap::Heap; use crate::vmo::utils; +use crate::vmo::PropertyFormat; /// Wraps a heap and implements the Inspect VMO API on top of it at a low level. pub struct State { @@ -19,58 +20,6 @@ pub struct State { header: Block<Rc<Mapping>>, } -/// Provides functions required to implement formats for a property. -pub trait PropertyFormat { - fn bytes(&self) -> &[u8]; - fn flag(&self) -> u8; - fn length_in_bytes(&self) -> u32; -} - -/// Implementation of property for a byte vector. -impl PropertyFormat for &[u8] { - fn bytes(&self) -> &[u8] { - &self - } - - fn flag(&self) -> u8 { - constants::PROPERTY_FLAG_BYTE_VECTOR - } - - fn length_in_bytes(&self) -> u32 { - self.len().to_u32().unwrap() - } -} - -/// Implementation of property for a string. -impl PropertyFormat for &str { - fn bytes(&self) -> &[u8] { - self.as_bytes() - } - - fn flag(&self) -> u8 { - constants::PROPERTY_FLAG_STRING - } - - fn length_in_bytes(&self) -> u32 { - self.bytes().len().to_u32().unwrap() - } -} - -/// Implementation of property for a string. -impl PropertyFormat for String { - fn bytes(&self) -> &[u8] { - self.as_bytes() - } - - fn flag(&self) -> u8 { - constants::PROPERTY_FLAG_STRING - } - - fn length_in_bytes(&self) -> u32 { - self.bytes().len().to_u32().unwrap() - } -} - /// Locks the VMO Header blcok, executes the given codeblock and unblocks it. macro_rules! with_header_lock { ($self:ident, $code:block) => {{ diff --git a/garnet/public/rust/fuchsia-inspect/src/vmo/types.rs b/garnet/public/rust/fuchsia-inspect/src/vmo/types.rs deleted file mode 100644 index 8e5cfcc40d2dfddcfc1771fad385f6c6088fdbd5..0000000000000000000000000000000000000000 --- a/garnet/public/rust/fuchsia-inspect/src/vmo/types.rs +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright 2019 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use failure::Error; -use parking_lot::Mutex; -use paste; -use std::marker::PhantomData; -use std::sync::Arc; - -use crate::vmo::state::{PropertyFormat, State}; - -/// Inspect API Node data type. -pub struct Node { - /// Index of the block in the VMO. - block_index: u32, - - /// Reference to the VMO heap. - state: Arc<Mutex<State>>, -} - -/// Inspect API Metric data type. -pub struct Metric<T: MetricFormat> { - /// Index of the block in the VMO. - block_index: u32, - - /// Reference to the VMO heap. - state: Arc<Mutex<State>>, - phantom: PhantomData<T>, -} - -/// Inspect API Property data type. -pub struct Property<T: PropertyFormat> { - /// Index of the block in the VMO. - block_index: u32, - - /// Reference to the VMO heap. - state: Arc<Mutex<State>>, - phantom: PhantomData<T>, -} - -/// Allowed formats for a Metric data type (uint, int, double) -pub trait MetricFormat {} -impl MetricFormat for u64 {} -impl MetricFormat for i64 {} -impl MetricFormat for f64 {} - -/// Utility for generating functions to create a metric. -/// `name`: identifier for the name (example: double) -/// `type`: the type of the metric (example: f64) -macro_rules! create_metric_fn { - ($name:ident, $type:ident) => { - paste::item! { - pub fn [<create_ $name _metric>](&self, name: &str, value: $type) - -> Result<Metric<$type>, Error> { - let block = self.state.lock().[<create_ $name _metric>]( - name, value, self.block_index)?; - Ok(Metric::<$type> {state: self.state.clone(), block_index: block.index(), phantom: PhantomData}) - } - } - }; -} - -impl Node { - /// Allocate a new NODE object. Called through Inspector. - pub(in crate::vmo) fn allocate( - state: Arc<Mutex<State>>, - name: &str, - parent_index: u32, - ) -> Result<Self, Error> { - let block = state.lock().create_node(name, parent_index)?; - Ok(Node { state: state.clone(), block_index: block.index() }) - } - - /// Add a child to this node. - pub fn create_child(&self, name: &str) -> Result<Node, Error> { - let block = self.state.lock().create_node(name, self.block_index)?; - Ok(Node { state: self.state.clone(), block_index: block.index() }) - } - - /// Add a metric to this node: create_int_metric, create_double_metric, - /// create_uint_metric. - create_metric_fn!(int, i64); - create_metric_fn!(uint, u64); - create_metric_fn!(double, f64); - - /// Add a property to this node. - pub fn create_property<T: PropertyFormat>( - &self, - name: &str, - value: T, - ) -> Result<Property<T>, Error> { - let block = self.state.lock().create_property(name, value, self.block_index)?; - Ok(Property::<T> { - state: self.state.clone(), - block_index: block.index(), - phantom: PhantomData, - }) - } -} - -impl Drop for Node { - fn drop(&mut self) { - self.state - .lock() - .free_value(self.block_index) - .expect(&format!("Failed to free node index={}", self.block_index)); - } -} - -/// Utility for generating metric functions (example: set, add, subtract) -/// `fn_name`: the name of the function to generate (example: set) -/// `type`: the type of the argument of the function to generate (example: f64) -/// `name`: the readble name of the type of the function (example: double) -macro_rules! metric_fn { - ($fn_name:ident, $type:ident, $name:ident) => { - paste::item! { - pub fn $fn_name(&self, value: $type) -> Result<(), Error> { - self.state.lock().[<$fn_name _ $name _metric>](self.block_index, value) - } - } - }; -} - -/// Utility for generating a metric datatype impl -/// `name`: the readble name of the type of the function (example: double) -/// `type`: the type of the argument of the function to generate (example: f64) -macro_rules! metric_impl { - ($name:ident, $type:ident) => { - impl Metric<$type> { - metric_fn!(set, $type, $name); - metric_fn!(add, $type, $name); - metric_fn!(subtract, $type, $name); - } - }; -} - -metric_impl!(int, i64); -metric_impl!(uint, u64); -metric_impl!(double, f64); - -impl<T: MetricFormat> Drop for Metric<T> { - fn drop(&mut self) { - self.state - .lock() - .free_value(self.block_index) - .expect(&format!("Failed to free metric index={}", self.block_index)); - } -} - -impl<T: PropertyFormat> Property<T> { - /// Set a property value. - pub fn set(&self, value: T) -> Result<(), Error> { - self.state.lock().set_property(self.block_index, value) - } -} - -impl<T: PropertyFormat> Drop for Property<T> { - fn drop(&mut self) { - self.state - .lock() - .free_property(self.block_index) - .expect(&format!("Failed to free property index={}", self.block_index)); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::vmo::block_type::BlockType; - use crate::vmo::constants; - use crate::vmo::heap::Heap; - use mapped_vmo::Mapping; - use std::rc::Rc; - - #[test] - fn node() { - let mapping = Rc::new(Mapping::allocate(4096).unwrap().0); - let state = get_state(mapping.clone()); - let node = Node::allocate(state, "root", constants::HEADER_INDEX).unwrap(); - let node_block = node.state.lock().heap.get_block(node.block_index).unwrap(); - assert_eq!(node_block.block_type(), BlockType::NodeValue); - assert_eq!(node_block.child_count().unwrap(), 0); - { - let child = node.create_child("child").unwrap(); - let child_block = node.state.lock().heap.get_block(child.block_index).unwrap(); - assert_eq!(child_block.block_type(), BlockType::NodeValue); - assert_eq!(child_block.child_count().unwrap(), 0); - assert_eq!(node_block.child_count().unwrap(), 1); - } - assert_eq!(node_block.child_count().unwrap(), 0); - } - - #[test] - fn double_metric() { - let mapping = Rc::new(Mapping::allocate(4096).unwrap().0); - let state = get_state(mapping.clone()); - let node = Node::allocate(state, "root", constants::HEADER_INDEX).unwrap(); - let node_block = node.state.lock().heap.get_block(node.block_index).unwrap(); - { - let metric = node.create_double_metric("metric", 1.0).unwrap(); - let metric_block = node.state.lock().heap.get_block(metric.block_index).unwrap(); - assert_eq!(metric_block.block_type(), BlockType::DoubleValue); - assert_eq!(metric_block.double_value().unwrap(), 1.0); - assert_eq!(node_block.child_count().unwrap(), 1); - - assert!(metric.set(2.0).is_ok()); - assert_eq!(metric_block.double_value().unwrap(), 2.0); - - assert!(metric.subtract(5.5).is_ok()); - assert_eq!(metric_block.double_value().unwrap(), -3.5); - - assert!(metric.add(8.1).is_ok()); - assert_eq!(metric_block.double_value().unwrap(), 4.6); - } - assert_eq!(node_block.child_count().unwrap(), 0); - } - - #[test] - fn int_metric() { - let mapping = Rc::new(Mapping::allocate(4096).unwrap().0); - let state = get_state(mapping.clone()); - let node = Node::allocate(state, "root", constants::HEADER_INDEX).unwrap(); - let node_block = node.state.lock().heap.get_block(node.block_index).unwrap(); - { - let metric = node.create_int_metric("metric", 1).unwrap(); - let metric_block = node.state.lock().heap.get_block(metric.block_index).unwrap(); - assert_eq!(metric_block.block_type(), BlockType::IntValue); - assert_eq!(metric_block.int_value().unwrap(), 1); - assert_eq!(node_block.child_count().unwrap(), 1); - - assert!(metric.set(2).is_ok()); - assert_eq!(metric_block.int_value().unwrap(), 2); - - assert!(metric.subtract(5).is_ok()); - assert_eq!(metric_block.int_value().unwrap(), -3); - - assert!(metric.add(8).is_ok()); - assert_eq!(metric_block.int_value().unwrap(), 5); - } - assert_eq!(node_block.child_count().unwrap(), 0); - } - - #[test] - fn uint_metric() { - let mapping = Rc::new(Mapping::allocate(4096).unwrap().0); - let state = get_state(mapping.clone()); - let node = Node::allocate(state, "root", constants::HEADER_INDEX).unwrap(); - let node_block = node.state.lock().heap.get_block(node.block_index).unwrap(); - { - let metric = node.create_uint_metric("metric", 1).unwrap(); - let metric_block = node.state.lock().heap.get_block(metric.block_index).unwrap(); - assert_eq!(metric_block.block_type(), BlockType::UintValue); - assert_eq!(metric_block.uint_value().unwrap(), 1); - assert_eq!(node_block.child_count().unwrap(), 1); - - assert!(metric.set(5).is_ok()); - assert_eq!(metric_block.uint_value().unwrap(), 5); - - assert!(metric.subtract(3).is_ok()); - assert_eq!(metric_block.uint_value().unwrap(), 2); - - assert!(metric.add(8).is_ok()); - assert_eq!(metric_block.uint_value().unwrap(), 10); - } - assert_eq!(node_block.child_count().unwrap(), 0); - } - - #[test] - fn string_property() { - let mapping = Rc::new(Mapping::allocate(4096).unwrap().0); - let state = get_state(mapping.clone()); - let node = Node::allocate(state, "root", constants::HEADER_INDEX).unwrap(); - let node_block = node.state.lock().heap.get_block(node.block_index).unwrap(); - { - let property = node.create_property("property", "test").unwrap(); - let property_block = node.state.lock().heap.get_block(property.block_index).unwrap(); - assert_eq!(property_block.block_type(), BlockType::PropertyValue); - assert_eq!(property_block.property_total_length().unwrap(), 4); - assert_eq!(property_block.property_flags().unwrap(), constants::PROPERTY_FLAG_STRING); - assert_eq!(node_block.child_count().unwrap(), 1); - - assert!(property.set("test-set").is_ok()); - assert_eq!(property_block.property_total_length().unwrap(), 8); - } - assert_eq!(node_block.child_count().unwrap(), 0); - } - - #[test] - fn bytevector_property() { - let mapping = Rc::new(Mapping::allocate(4096).unwrap().0); - let state = get_state(mapping.clone()); - let node = Node::allocate(state, "root", constants::HEADER_INDEX).unwrap(); - let node_block = node.state.lock().heap.get_block(node.block_index).unwrap(); - { - let property = node.create_property("property", "test".as_bytes()).unwrap(); - let property_block = node.state.lock().heap.get_block(property.block_index).unwrap(); - assert_eq!(property_block.block_type(), BlockType::PropertyValue); - assert_eq!(property_block.property_total_length().unwrap(), 4); - assert_eq!( - property_block.property_flags().unwrap(), - constants::PROPERTY_FLAG_BYTE_VECTOR - ); - assert_eq!(node_block.child_count().unwrap(), 1); - - assert!(property.set("test-set".as_bytes()).is_ok()); - assert_eq!(property_block.property_total_length().unwrap(), 8); - } - assert_eq!(node_block.child_count().unwrap(), 0); - } - - fn get_state(mapping: Rc<Mapping>) -> Arc<Mutex<State>> { - let heap = Heap::new(mapping).unwrap(); - Arc::new(Mutex::new(State::create(heap).unwrap())) - } -}