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