|
| 1 | +// Copyright 2019-2020 Google LLC |
| 2 | +// |
| 3 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +// you may not use this file except in compliance with the License. |
| 5 | +// You may obtain a copy of the License at |
| 6 | +// |
| 7 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +// |
| 9 | +// Unless required by applicable law or agreed to in writing, software |
| 10 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +// See the License for the specific language governing permissions and |
| 13 | +// limitations under the License. |
| 14 | + |
| 15 | +//! Helps manipulate bit fields in 32-bits words. |
| 16 | +// TODO(ia0): Remove when the module is used. |
| 17 | +#![cfg_attr(not(test), allow(dead_code, unused_macros))] |
| 18 | + |
| 19 | +use crate::{StoreError, StoreResult}; |
| 20 | + |
| 21 | +/// Represents a bit field. |
| 22 | +/// |
| 23 | +/// A bit field is a contiguous sequence of bits in a 32-bits word. |
| 24 | +/// |
| 25 | +/// # Invariant |
| 26 | +/// |
| 27 | +/// - The bit field must fit in a 32-bits word: `pos + len < 32`. |
| 28 | +pub struct Field { |
| 29 | + /// The position of the bit field. |
| 30 | + pub pos: usize, |
| 31 | + |
| 32 | + /// The length of the bit field. |
| 33 | + pub len: usize, |
| 34 | +} |
| 35 | + |
| 36 | +impl Field { |
| 37 | + /// Reads the value of a bit field. |
| 38 | + pub fn get(&self, word: u32) -> usize { |
| 39 | + ((word >> self.pos) & self.mask()) as usize |
| 40 | + } |
| 41 | + |
| 42 | + /// Sets the value of a bit field. |
| 43 | + /// |
| 44 | + /// # Preconditions |
| 45 | + /// |
| 46 | + /// - The value must fit in the bit field: `num_bits(value) < self.len`. |
| 47 | + /// - The value must only change bits from 1 to 0: `self.get(*word) & value == value`. |
| 48 | + pub fn set(&self, word: &mut u32, value: usize) { |
| 49 | + let value = value as u32; |
| 50 | + debug_assert_eq!(value & self.mask(), value); |
| 51 | + let mask = !(self.mask() << self.pos); |
| 52 | + *word &= mask | (value << self.pos); |
| 53 | + debug_assert_eq!(self.get(*word), value as usize); |
| 54 | + } |
| 55 | + |
| 56 | + /// Returns a bit mask the length of the bit field. |
| 57 | + /// |
| 58 | + /// The mask is meant to be applied on a value. It should be shifted to be applied to the bit |
| 59 | + /// field. |
| 60 | + fn mask(&self) -> u32 { |
| 61 | + (1 << self.len) - 1 |
| 62 | + } |
| 63 | +} |
| 64 | + |
| 65 | +/// Represents a constant bit field. |
| 66 | +/// |
| 67 | +/// # Invariant |
| 68 | +/// |
| 69 | +/// - The value must fit in the bit field: `num_bits(value) <= field.len`. |
| 70 | +pub struct ConstField { |
| 71 | + /// The bit field. |
| 72 | + pub field: Field, |
| 73 | + |
| 74 | + /// The constant value. |
| 75 | + pub value: usize, |
| 76 | +} |
| 77 | + |
| 78 | +impl ConstField { |
| 79 | + /// Checks that the bit field has its value. |
| 80 | + pub fn check(&self, word: u32) -> bool { |
| 81 | + self.field.get(word) == self.value |
| 82 | + } |
| 83 | + |
| 84 | + /// Sets the bit field to its value. |
| 85 | + pub fn set(&self, word: &mut u32) { |
| 86 | + self.field.set(word, self.value); |
| 87 | + } |
| 88 | +} |
| 89 | + |
| 90 | +/// Represents a single bit. |
| 91 | +/// |
| 92 | +/// # Invariant |
| 93 | +/// |
| 94 | +/// - The bit must fit in a 32-bits word: `pos < 32`. |
| 95 | +pub struct Bit { |
| 96 | + /// The position of the bit. |
| 97 | + pub pos: usize, |
| 98 | +} |
| 99 | + |
| 100 | +impl Bit { |
| 101 | + /// Returns whether the value of the bit is zero. |
| 102 | + pub fn get(&self, word: u32) -> bool { |
| 103 | + word & (1 << self.pos) == 0 |
| 104 | + } |
| 105 | + |
| 106 | + /// Sets the value of the bit to zero. |
| 107 | + pub fn set(&self, word: &mut u32) { |
| 108 | + *word &= !(1 << self.pos); |
| 109 | + } |
| 110 | +} |
| 111 | + |
| 112 | +/// Represents a checksum. |
| 113 | +/// |
| 114 | +/// A checksum is a bit field counting how many bits are set to zero in the word (except in the |
| 115 | +/// checksum itself) plus some external increment. It essentially behaves like a bit field storing |
| 116 | +/// the external increment. |
| 117 | +pub struct Checksum { |
| 118 | + /// The bit field |
| 119 | + pub field: Field, |
| 120 | +} |
| 121 | + |
| 122 | +impl Checksum { |
| 123 | + /// Reads the external increment from the checksum. |
| 124 | + /// |
| 125 | + /// # Errors |
| 126 | + /// |
| 127 | + /// Returns `InvalidStorage` if the external increment would be negative. |
| 128 | + pub fn get(&self, word: u32) -> StoreResult<usize> { |
| 129 | + let checksum = self.field.get(word); |
| 130 | + let zeros = word.count_zeros() as usize - (self.field.len - checksum.count_ones() as usize); |
| 131 | + checksum |
| 132 | + .checked_sub(zeros) |
| 133 | + .ok_or(StoreError::InvalidStorage) |
| 134 | + } |
| 135 | + |
| 136 | + /// Sets the checksum to the external increment value. |
| 137 | + /// |
| 138 | + /// # Preconditions |
| 139 | + /// |
| 140 | + /// - The bits of the checksum bit field should be set to one: `self.field.get(*word) == |
| 141 | + /// self.field.mask()`. |
| 142 | + /// - The checksum value should fit in the checksum bit field: `num_bits(word.count_zeros() + |
| 143 | + /// value) < self.field.len`. |
| 144 | + pub fn set(&self, word: &mut u32, value: usize) { |
| 145 | + debug_assert_eq!(self.field.get(*word), self.field.mask() as usize); |
| 146 | + self.field.set(word, word.count_zeros() as usize + value); |
| 147 | + } |
| 148 | +} |
| 149 | + |
| 150 | +/// Tracks the number of bits used so far. |
| 151 | +/// |
| 152 | +/// # Features |
| 153 | +/// |
| 154 | +/// Only available for tests. |
| 155 | +#[cfg(any(doc, test))] |
| 156 | +pub struct Length { |
| 157 | + /// The position of the next available bit. |
| 158 | + pub pos: usize, |
| 159 | +} |
| 160 | + |
| 161 | +/// Helps defining contiguous bit fields. |
| 162 | +/// |
| 163 | +/// It takes a sequence of bit field descriptors as argument. A bit field descriptor is one of the |
| 164 | +/// following: |
| 165 | +/// - `$name: Bit,` to define a bit |
| 166 | +/// - `$name: Field <= $max,` to define a bit field of minimum length to store `$max` |
| 167 | +/// - `$name: Checksum <= $max,` to define a checksum of minimum length to store `$max` |
| 168 | +/// - `$name: Length,` to define a length tracker |
| 169 | +/// - `$name: ConstField = [$bits],` to define a constant bit field with value `$bits` (a sequence |
| 170 | +/// of space-separated bits) |
| 171 | +#[cfg_attr(doc, macro_export)] // For `cargo doc` to produce documentation. |
| 172 | +macro_rules! bitfield { |
| 173 | + ($($input: tt)*) => { |
| 174 | + bitfield_impl! { []{ pos: 0 }[$($input)*] } |
| 175 | + }; |
| 176 | +} |
| 177 | + |
| 178 | +macro_rules! bitfield_impl { |
| 179 | + // Main rules: |
| 180 | + // - Input are bit field descriptors |
| 181 | + // - Position is the number of bits used by prior bit fields |
| 182 | + // - Output are the bit field definitions |
| 183 | + ([$($output: tt)*]{ pos: $pos: expr }[$name: ident: Bit, $($input: tt)*]) => { |
| 184 | + bitfield_impl! { |
| 185 | + [$($output)* const $name: Bit = Bit { pos: $pos };] |
| 186 | + { pos: $pos + 1 } |
| 187 | + [$($input)*] |
| 188 | + } |
| 189 | + }; |
| 190 | + ([$($output: tt)*]{ pos: $pos: expr }[$name: ident: Field <= $max: expr, $($input: tt)*]) => { |
| 191 | + bitfield_impl! { |
| 192 | + [$($output)* const $name: Field = Field { pos: $pos, len: num_bits($max) };] |
| 193 | + { pos: $pos + $name.len } |
| 194 | + [$($input)*] |
| 195 | + } |
| 196 | + }; |
| 197 | + ([$($output: tt)*]{ pos: $pos: expr } |
| 198 | + [$name: ident: Checksum <= $max: expr, $($input: tt)*]) => { |
| 199 | + bitfield_impl! { |
| 200 | + [$($output)* const $name: Checksum = Checksum { |
| 201 | + field: Field { pos: $pos, len: num_bits($max) } |
| 202 | + };] |
| 203 | + { pos: $pos + $name.field.len } |
| 204 | + [$($input)*] |
| 205 | + } |
| 206 | + }; |
| 207 | + ([$($output: tt)*]{ pos: $pos: expr } |
| 208 | + [$(#[$meta: meta])* $name: ident: Length, $($input: tt)*]) => { |
| 209 | + bitfield_impl! { |
| 210 | + [$($output)* $(#[$meta])* const $name: Length = Length { pos: $pos };] |
| 211 | + { pos: $pos } |
| 212 | + [$($input)*] |
| 213 | + } |
| 214 | + }; |
| 215 | + ([$($output: tt)*]{ pos: $pos: expr } |
| 216 | + [$name: ident: ConstField = $bits: tt, $($input: tt)*]) => { |
| 217 | + bitfield_impl! { |
| 218 | + Reverse $name []$bits |
| 219 | + [$($output)*]{ pos: $pos }[$($input)*] |
| 220 | + } |
| 221 | + }; |
| 222 | + ([$($output: tt)*]{ pos: $pos: expr }[]) => { $($output)* }; |
| 223 | + |
| 224 | + // Auxiliary rules for constant bit fields: |
| 225 | + // - Input is a sequence of bits |
| 226 | + // - Output is the reversed sequence of bits |
| 227 | + (Reverse $name: ident [$($output_bits: tt)*] [$bit: tt $($input_bits: tt)*] |
| 228 | + [$($output: tt)*]{ pos: $pos: expr }[$($input: tt)*]) => { |
| 229 | + bitfield_impl! { |
| 230 | + Reverse $name [$bit $($output_bits)*][$($input_bits)*] |
| 231 | + [$($output)*]{ pos: $pos }[$($input)*] |
| 232 | + } |
| 233 | + }; |
| 234 | + (Reverse $name: ident $bits: tt [] |
| 235 | + [$($output: tt)*]{ pos: $pos: expr }[$($input: tt)*]) => { |
| 236 | + bitfield_impl! { |
| 237 | + ConstField $name { len: 0, val: 0 }$bits |
| 238 | + [$($output)*]{ pos: $pos }[$($input)*] |
| 239 | + } |
| 240 | + }; |
| 241 | + |
| 242 | + // Auxiliary rules for constant bit fields: |
| 243 | + // - Input is a sequence of bits in reversed order |
| 244 | + // - Output is the constant bit field definition with the sequence of bits as value |
| 245 | + (ConstField $name: ident { len: $len: expr, val: $val: expr }[] |
| 246 | + [$($output: tt)*]{ pos: $pos: expr }[$($input: tt)*]) => { |
| 247 | + bitfield_impl! { |
| 248 | + [$($output)* const $name: ConstField = ConstField { |
| 249 | + field: Field { pos: $pos, len: $len }, |
| 250 | + value: $val, |
| 251 | + };] |
| 252 | + { pos: $pos + $name.field.len } |
| 253 | + [$($input)*] |
| 254 | + } |
| 255 | + }; |
| 256 | + (ConstField $name: ident { len: $len: expr, val: $val: expr }[$bit: tt $($bits: tt)*] |
| 257 | + [$($output: tt)*]{ pos: $pos: expr }[$($input: tt)*]) => { |
| 258 | + bitfield_impl! { |
| 259 | + ConstField $name { len: $len + 1, val: $val * 2 + $bit }[$($bits)*] |
| 260 | + [$($output)*]{ pos: $pos }[$($input)*] |
| 261 | + } |
| 262 | + }; |
| 263 | +} |
| 264 | + |
| 265 | +/// Counts the number of bits equal to zero in a byte slice. |
| 266 | +pub fn count_zeros(slice: &[u8]) -> usize { |
| 267 | + slice.iter().map(|&x| x.count_zeros() as usize).sum() |
| 268 | +} |
| 269 | + |
| 270 | +/// Returns the number of bits necessary to represent a number. |
| 271 | +pub const fn num_bits(x: usize) -> usize { |
| 272 | + 8 * core::mem::size_of::<usize>() - x.leading_zeros() as usize |
| 273 | +} |
| 274 | + |
| 275 | +#[cfg(test)] |
| 276 | +mod tests { |
| 277 | + use super::*; |
| 278 | + |
| 279 | + #[test] |
| 280 | + fn field_ok() { |
| 281 | + let field = Field { pos: 3, len: 5 }; |
| 282 | + assert_eq!(field.get(0x00000000), 0); |
| 283 | + assert_eq!(field.get(0x00000007), 0); |
| 284 | + assert_eq!(field.get(0x00000008), 1); |
| 285 | + assert_eq!(field.get(0x000000f8), 0x1f); |
| 286 | + assert_eq!(field.get(0x0000ff37), 6); |
| 287 | + let mut word = 0xffffffff; |
| 288 | + field.set(&mut word, 3); |
| 289 | + assert_eq!(word, 0xffffff1f); |
| 290 | + } |
| 291 | + |
| 292 | + #[test] |
| 293 | + fn const_field_ok() { |
| 294 | + let field = ConstField { |
| 295 | + field: Field { pos: 3, len: 5 }, |
| 296 | + value: 9, |
| 297 | + }; |
| 298 | + assert!(!field.check(0x00000000)); |
| 299 | + assert!(!field.check(0x0000ffff)); |
| 300 | + assert!(field.check(0x00000048)); |
| 301 | + assert!(field.check(0x0000ff4f)); |
| 302 | + let mut word = 0xffffffff; |
| 303 | + field.set(&mut word); |
| 304 | + assert_eq!(word, 0xffffff4f); |
| 305 | + } |
| 306 | + |
| 307 | + #[test] |
| 308 | + fn bit_ok() { |
| 309 | + let bit = Bit { pos: 3 }; |
| 310 | + assert!(bit.get(0x00000000)); |
| 311 | + assert!(bit.get(0xfffffff7)); |
| 312 | + assert!(!bit.get(0x00000008)); |
| 313 | + assert!(!bit.get(0xffffffff)); |
| 314 | + let mut word = 0xffffffff; |
| 315 | + bit.set(&mut word); |
| 316 | + assert_eq!(word, 0xfffffff7); |
| 317 | + } |
| 318 | + |
| 319 | + #[test] |
| 320 | + fn checksum_ok() { |
| 321 | + let field = Checksum { |
| 322 | + field: Field { pos: 3, len: 5 }, |
| 323 | + }; |
| 324 | + assert_eq!(field.get(0x00000000), Err(StoreError::InvalidStorage)); |
| 325 | + assert_eq!(field.get(0xffffffff), Ok(31)); |
| 326 | + assert_eq!(field.get(0xffffff07), Ok(0)); |
| 327 | + assert_eq!(field.get(0xffffff0f), Ok(1)); |
| 328 | + assert_eq!(field.get(0x00ffff67), Ok(4)); |
| 329 | + assert_eq!(field.get(0x7fffff07), Err(StoreError::InvalidStorage)); |
| 330 | + let mut word = 0x0fffffff; |
| 331 | + field.set(&mut word, 4); |
| 332 | + assert_eq!(word, 0x0fffff47); |
| 333 | + } |
| 334 | + |
| 335 | + #[test] |
| 336 | + fn bitfield_ok() { |
| 337 | + bitfield! { |
| 338 | + FIELD: Field <= 127, |
| 339 | + CONST_FIELD: ConstField = [0 1 0 1], |
| 340 | + BIT: Bit, |
| 341 | + CHECKSUM: Checksum <= 58, |
| 342 | + LENGTH: Length, |
| 343 | + } |
| 344 | + assert_eq!(FIELD.pos, 0); |
| 345 | + assert_eq!(FIELD.len, 7); |
| 346 | + assert_eq!(CONST_FIELD.field.pos, 7); |
| 347 | + assert_eq!(CONST_FIELD.field.len, 4); |
| 348 | + assert_eq!(CONST_FIELD.value, 10); |
| 349 | + assert_eq!(BIT.pos, 11); |
| 350 | + assert_eq!(CHECKSUM.field.pos, 12); |
| 351 | + assert_eq!(CHECKSUM.field.len, 6); |
| 352 | + assert_eq!(LENGTH.pos, 18); |
| 353 | + } |
| 354 | + |
| 355 | + #[test] |
| 356 | + fn count_zeros_ok() { |
| 357 | + assert_eq!(count_zeros(&[0xff, 0xff]), 0); |
| 358 | + assert_eq!(count_zeros(&[0xff, 0xfe]), 1); |
| 359 | + assert_eq!(count_zeros(&[0x7f, 0xff]), 1); |
| 360 | + assert_eq!(count_zeros(&[0x12, 0x48]), 12); |
| 361 | + assert_eq!(count_zeros(&[0x00, 0x00]), 16); |
| 362 | + } |
| 363 | + |
| 364 | + #[test] |
| 365 | + fn num_bits_ok() { |
| 366 | + assert_eq!(num_bits(0), 0); |
| 367 | + assert_eq!(num_bits(1), 1); |
| 368 | + assert_eq!(num_bits(2), 2); |
| 369 | + assert_eq!(num_bits(3), 2); |
| 370 | + assert_eq!(num_bits(4), 3); |
| 371 | + assert_eq!(num_bits(5), 3); |
| 372 | + assert_eq!(num_bits(8), 4); |
| 373 | + assert_eq!(num_bits(9), 4); |
| 374 | + assert_eq!(num_bits(16), 5); |
| 375 | + } |
| 376 | +} |
0 commit comments