Skip to main content

inspect_format/
bitfields.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! # Inspect bitfields
6//!
7//! This module contains the bitfield definitions of the [`Inspect VMO format`][inspect-vmo].
8//!
9//! [inspect-vmo]: https://fuchsia.dev/fuchsia-src/reference/diagnostics/inspect/vmo-format
10
11use crate::block::{Block, BlockKind};
12use crate::container::{ReadBytes, WriteBytes};
13use std::ops::{Deref, DerefMut};
14
15macro_rules! bitfield_fields {
16    ($offset_fn:ident,) => {};
17
18    ($offset_fn:ident,
19     $(#[$attr:meta])* $type:ty,
20     $name:ident: $msb:expr, $lsb:expr;
21     $($rest:tt)*
22    ) => {
23        paste::item! {
24            $(#[$attr])*
25            #[inline]
26            #[allow(clippy::identity_op)]
27            pub fn $name<T: Deref<Target=Q>, Q: ReadBytes, K: BlockKind>(b: &Block<T, K>) -> $type {
28                if let Some(value) = b.container.get_value::<u64>(b.$offset_fn()) {
29                    static MASK : u64 = (1 << ($msb - $lsb + 1)) - 1;
30                    // This cast is fine. We only deal with u8, u16, u32, u64 here.
31                    return ((value >> ($lsb % 64)) & MASK ) as $type;
32                }
33                0
34            }
35
36            $(#[$attr])*
37            #[inline]
38            #[allow(clippy::identity_op)]
39            pub fn [<set_ $name>]<T: Deref<Target=Q> + DerefMut<Target=Q>, Q: WriteBytes + ReadBytes, K: BlockKind>(
40                   b: &mut Block<T, K>, value: $type) {
41                let offset = b.$offset_fn();
42                if let Some(num) = b.container.get_value::<u64>(offset) {
43                    static MASK : u64 = (1u64 << ($msb - $lsb + 1)) - 1;
44                    let new_val = (num & !MASK.checked_shl($lsb).unwrap_or(0)) |
45                               (value as u64).checked_shl($lsb).unwrap_or(0);
46                    let _ = b.container.set_value::<u64>(offset, new_val);
47                }
48            }
49        }
50        bitfield_fields!{$offset_fn, $($rest)*}
51    };
52}
53
54macro_rules! block_bitfield {
55    ($(#[$attr:meta])* struct $name:ident, $offset_fn:ident; $($rest:tt)*) => {
56        $(#[$attr])*
57        pub struct $name;
58
59        impl $name {
60            bitfield_fields!{$offset_fn, $($rest)*}
61
62            /// Get the raw 64 bits of the header section of the block.
63            pub fn value<T: Deref<Target=Q>, Q: ReadBytes, K: BlockKind>(b: &Block<T, K>) -> u64 {
64                b.container.get_value::<u64>(b.$offset_fn()).unwrap_or(0)
65            }
66
67            /// Set the raw 64 bits of the header section of the block.
68            #[inline]
69            pub fn set_value<T: Deref<Target=Q> + DerefMut<Target=Q>, Q: WriteBytes + ReadBytes, K: BlockKind>(
70                b: &mut Block<T, K>, value: u64
71            ) {
72                let offset = b.$offset_fn();
73                let _ = b.container.set_value::<u64>(offset, value);
74            }
75        }
76    };
77}
78
79block_bitfield! {
80    /// Bitfields for writing and reading segments of the header and payload of
81    /// inspect VMO blocks.
82    /// Represents the header structure of an inspect VMO Block. Not to confuse with
83    /// the `HEADER` block.
84    struct HeaderFields, header_offset;
85
86    /// The size of a block given as a bit shift from the minimum size.
87    /// `size_in_bytes = 16 << order`. Separates blocks into classes by their (power of two) size.
88    u8, order: 3, 0;
89
90    /// The type of the block. Determines how the rest of the bytes are interpreted.
91    /// - 0: Free
92    /// - 1: Reserved
93    /// - 2: Header
94    /// - 3: Node
95    /// - 4: Int value
96    /// - 5: Uint value
97    /// - 6: Double value
98    /// - 7: Buffer value
99    /// - 8: Extent
100    /// - 9: Name
101    /// - 10: Tombstone
102    /// - 11: Array value
103    /// - 12: Link value
104    /// - 13: Bool value
105    /// - 14: String Reference
106    u8, block_type: 15, 8;
107
108    /// Only for a `HEADER` block. The version number. Currently 1.
109    u32, header_version: 31, 16;
110
111    /// Only for a `HEADER` block. The magic number "INSP".
112    u32, header_magic: 63, 32;
113
114    /// Only for `*_VALUE` blocks. The index of the `NAME` block of associated with this value.
115    u32, value_name_index: 63, 40;
116
117    /// Only for `*_VALUE` blocks. The index of the parent of this value.
118    u32, value_parent_index: 39, 16;
119
120    // Only for RESERVED blocks
121    u64, reserved_empty: 63, 16;
122
123    // Only for TOMBSTONE blocks
124    u64, tombstone_empty: 63, 16;
125
126    /// Only for `FREE` blocks. The index of the next free block.
127    u8, free_reserved: 7, 4;
128    u32, free_next_index: 39, 16;
129    u32, free_empty: 63, 40;
130
131    /// Only for `NAME` blocks. The length of the string.
132    u16, name_length: 27, 16;
133
134    /// Only for `EXTENT` or `STRING_REFERENCE` blocks.
135    /// The index of the next `EXTENT` block.
136    u32, extent_next_index: 39, 16;
137
138    /// Only for `STRING_REFERENCE` blocks.
139    /// The number of active references to the string, including itself.
140    u32, string_reference_count: 63, 40;
141}
142
143block_bitfield! {
144    /// Represents the payload of inspect VMO Blocks (except for `EXTENT` and `NAME`).
145    struct PayloadFields, payload_offset;
146
147    /// Only for `BUFFER` or `STRING_REFERENCE` blocks. The total size of the buffer.
148    u32, property_total_length:  31, 0;
149
150    /// Only for `BUFFER` blocks. The index of the first `EXTENT` block of this buffer.
151    u32, property_extent_index: 59, 32;
152
153    /// Only for `BUFFER` blocks. The buffer flags of this block indicating its display format.
154    /// 0: utf-8 string
155    /// 1: binary array
156    u8, property_flags: 63, 60;
157
158    /// Only for `ARRAY_VALUE` blocks. The type of each entry in the array (int, uint, double).
159    /// 0: Int
160    /// 1: Uint
161    /// 2: Double
162    u8, array_entry_type: 3, 0;
163
164    /// Only for `ARRAY_VALUE` blocks. The display format of the block (default, linear histogram,
165    /// exponential histogram)
166    /// 0: Regular array
167    /// 1: Linear histogram
168    /// 2: Exponential histogram
169    u8, array_flags: 7, 4;
170
171    /// Only for `ARRAY_VALUE` blocks. The nmber of entries in the array.
172    u8, array_slots_count: 15, 8;
173
174    /// Only for `LINK_VALUE` blocks. Index of the content of this link (as a `NAME` node)
175    u32, content_index: 19, 0;
176
177    /// Only for `LINK_VALUE`. Instructs readers whether to use child or inline disposition.
178    /// 0: child
179    /// 1: inline
180    u8, disposition_flags: 63, 60;
181}
182
183#[cfg(test)]
184mod tests {
185    use super::*;
186    use crate::{BlockIndex, Buffer, Header};
187
188    #[fuchsia::test]
189    fn test_header() {
190        let mut container = [0u8; 16];
191        let mut block = Block::<_, Header>::new(&mut container, BlockIndex::EMPTY);
192        let magic = 0x494e5350;
193        HeaderFields::set_order(&mut block, 13);
194        HeaderFields::set_block_type(&mut block, 3);
195        HeaderFields::set_header_version(&mut block, 1);
196        HeaderFields::set_header_magic(&mut block, magic);
197        assert_eq!(HeaderFields::order(&block), 13);
198        assert_eq!(HeaderFields::header_version(&block), 1);
199        assert_eq!(HeaderFields::header_magic(&block), magic);
200        assert_eq!(HeaderFields::value(&block), 0x494e53500001030d);
201    }
202
203    #[fuchsia::test]
204    fn test_payload() {
205        let mut container = [0u8; 16];
206        let mut block = Block::<_, Buffer>::new(&mut container, BlockIndex::EMPTY);
207        PayloadFields::set_property_total_length(&mut block, 0xab);
208        PayloadFields::set_property_extent_index(&mut block, 0x1234);
209        PayloadFields::set_property_flags(&mut block, 3);
210        assert_eq!(PayloadFields::property_total_length(&block), 0xab);
211        assert_eq!(PayloadFields::property_extent_index(&block), 0x1234);
212        assert_eq!(PayloadFields::property_flags(&block), 3);
213        assert_eq!(PayloadFields::value(&block), 0x30001234000000ab);
214    }
215}