├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE.txt ├── README.md ├── assets ├── analysis.R ├── bench-bit-reads.png └── bitter-benchmark-data.csv ├── compare ├── Cargo.toml └── benches │ └── bench_bits.rs ├── examples └── sample.rs ├── fuzz ├── .gitignore ├── Cargo.toml └── fuzz_targets │ └── fuzz_simple.rs ├── release.toml ├── src └── lib.rs └── tests ├── properties.rs └── regressions.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - master 7 | schedule: 8 | - cron: '00 01 * * *' 9 | 10 | jobs: 11 | test: 12 | name: test 13 | env: 14 | # For some builds, we use cross to test on 32-bit and big-endian 15 | # systems. 16 | CARGO: cargo 17 | # When CARGO is set to CROSS, TARGET is set to `--target matrix.target`. 18 | TARGET: 19 | runs-on: ${{ matrix.os }} 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | build: 24 | - pinned 25 | - stable 26 | - stable-32 27 | - big-endian 28 | - beta 29 | - nightly 30 | - macos 31 | - win-msvc 32 | - win-gnu 33 | include: 34 | - build: pinned 35 | os: ubuntu-latest 36 | rust: 1.51.0 37 | - build: stable 38 | os: ubuntu-latest 39 | rust: stable 40 | - build: stable-32 41 | os: ubuntu-latest 42 | rust: stable 43 | target: i686-unknown-linux-gnu 44 | - build: big-endian 45 | os: ubuntu-latest 46 | rust: stable 47 | target: powerpc64-unknown-linux-gnu 48 | - build: beta 49 | os: ubuntu-latest 50 | rust: beta 51 | - build: nightly 52 | os: ubuntu-latest 53 | rust: nightly 54 | - build: macos 55 | os: macos-latest 56 | rust: stable 57 | - build: win-msvc 58 | os: windows-latest 59 | rust: stable 60 | - build: win-gnu 61 | os: windows-latest 62 | rust: stable-x86_64-gnu 63 | steps: 64 | - name: Checkout repository 65 | uses: actions/checkout@v4 66 | 67 | - name: Install Rust 68 | uses: dtolnay/rust-toolchain@master 69 | with: 70 | toolchain: ${{ matrix.rust }} 71 | 72 | - name: Use Cross 73 | if: matrix.target != '' 74 | run: | 75 | cargo install cross 76 | echo "CARGO=cross" >> $GITHUB_ENV 77 | echo "TARGET=--target ${{ matrix.target }}" >> $GITHUB_ENV 78 | 79 | - name: Build 80 | run: ${{ env.CARGO }} build --verbose $TARGET 81 | 82 | - name: Build docs 83 | run: ${{ env.CARGO }} doc --verbose $TARGET 84 | 85 | - name: No Std Tests 86 | if: matrix.build != 'pinned' 87 | run: ${{ env.CARGO }} test --no-default-features --verbose $TARGET 88 | 89 | - name: Tests 90 | if: matrix.build != 'pinned' 91 | run: ${{ env.CARGO }} test --verbose $TARGET 92 | 93 | - name: No panic tests 94 | if: matrix.build != 'pinned' 95 | run: ${{ env.CARGO }} build --release --verbose $TARGET --example sample 96 | 97 | - name: Compile benchmarks 98 | if: matrix.build == 'stable' 99 | run: cargo bench --verbose --no-run $TARGET 100 | 101 | - name: Run miri 102 | if: matrix.build == 'nightly' 103 | run: | 104 | rustup toolchain install nightly --component miri 105 | cargo miri setup 106 | cargo miri test 107 | 108 | - name: Compile fuzz 109 | if: matrix.build == 'nightly' 110 | run: | 111 | cargo install cargo-fuzz 112 | cargo fuzz build fuzz_simple 113 | 114 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | callgrind.* 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.7.1 - 2024-12-19 2 | 3 | - Expose the number of unbuffered bytes remaining via `BitReader::unbuffered_bytes_remaining` 4 | - Restore `read_bytes` optimization for unaligned big endian readers for 10x throughput improvement 5 | 6 | ## 0.7.0 - 2024-05-14 7 | 8 | - Revert `read_bytes` optimization for unaligned big endian readers due to correctness issues 9 | - Add support to `read_bits` for reading any number of bits up to 64 bits 10 | - Add `BitReader::read_u64`, `BitReader::read_i64`, and `BitReader::read_f64` 11 | - Add support to `read_bits`, `peek`, and `consume` of zero bits 12 | - Add `BitReader::lookahead_bits` that returns the number of bits in the lookahead buffer 13 | - Allow `peek` and `consume` to accept arguments up to `BitReader::lookahead_bits` 14 | - Remove the return value from `BitReader::refill_lookahead`, which can now be found in `BitReader::lookahead_bits` 15 | - MSRV increased to 1.51 16 | - Most `unsafe` code has been converted to their safe equivalents without a loss in performance 17 | 18 | ### Migration 19 | 20 | The return value of `BitReader::refill_lookahead` has been removed and can be replaced with: 21 | 22 | ```rust 23 | let data: [u8; 1] = [0xab]; 24 | let mut bits = LittleEndianReader::new(&data); 25 | 26 | // BEFORE: 27 | // let left = bits.refill_lookahead(); 28 | 29 | // AFTER: 30 | bits.refill_lookahead(); 31 | let left = bits.lookahead_bits(); 32 | ``` 33 | 34 | ## 0.6.2 - 2024-01-14 35 | 36 | - `read_bytes` performance impovements: 35-40% for small reads and up to 22x for large byte unaligned reads. 37 | - Bitter constructors and `sign_extend` have been annotated with `#[must_use]` 38 | 39 | ## 0.6.1 - 2022-10-24 40 | 41 | - Performance improvement for `read_signed_bits` with branchless sign extension 42 | - Expose `bitter:sign_extend` for those who want sign extension with manual mode 43 | - Clearer error message for `peek(0)` 44 | 45 | ## 0.6.0 - 2022-08-31 46 | 47 | See PR #21 for more details, but this is a large and breaking change in the name more performance! The short of it: 48 | 49 | - Bit reads now capped at 56 bits 50 | - Most unchecked method have been removed 51 | - Users now can manually control when to trigger lookahead buffer refills to best take advantage of patterns in their data 52 | - MSRV bumped to 1.48.0 53 | 54 | ## 0.5.1 - 2021-07-11 55 | 56 | - Mark remaining non-generic functions as inline 57 | 58 | ## 0.5.0 - 2021-01-23 59 | 60 | This is a big and breaking release but hopefully at the end of the changelog you'll be in agreement that the improvements are very much worth it. I'm confident that the current API will hold up much better and I'm committed to releasing 1.0 if no unforseen flaws are encountered. 61 | 62 | ### BigEndian and NativeEndian APIs 63 | 64 | - `BigGet` has been renamed to `LittleEndianReader` to better reflect its name in light of: 65 | - Introduction of `BigEndianReader` and `NativeEndianReader` alias 66 | - A trait, `BitReader` that is the new home for consuming bits with `read_u8` 67 | 68 | Instead of writing: 69 | 70 | ```rust 71 | use bitter::BitGet; 72 | let mut bitter = BitGet::new(&[0xff, 0x04]); 73 | assert_eq!(bitter.read_bit(), Some(true)); 74 | assert_eq!(bitter.read_u8(), Some(0x7f)); 75 | ``` 76 | 77 | One will now write: 78 | 79 | ```rust 80 | use bitter::{BitReader, LittleEndianReader}; 81 | let mut bitter = LittleEndianReader::new(&[0xff, 0x04]); 82 | assert_eq!(bitter.read_bit(), Some(true)); 83 | assert_eq!(bitter.read_u8(), Some(0x7f)); 84 | ``` 85 | 86 | ### Support Bit Reads up to 64 bits 87 | 88 | Instead of 89 | 90 | ```rust 91 | assert_eq!(bitter.read_u32_bits(7), Some(0x02)); 92 | ``` 93 | 94 | The `read_u32_bits` family has been replaced with: 95 | 96 | ```rust 97 | assert_eq!(bitter.read_bits(7), Some(0x02)); 98 | ``` 99 | 100 | The new API: 101 | 102 | - can process process up to 64 bits instead of 32 bits 103 | - comes at no performance cost for 64bit systems 104 | 105 | ### Support Two's Complement for Arbitrary Bit Widths 106 | 107 | The following functions have been removed: 108 | 109 | ``` 110 | read_i32_bits 111 | read_i32_bits_unchecked 112 | ``` 113 | 114 | The use cases for these functions had been extremely limited. 115 | 116 | It is more intuitive that when the user requests 3 bits that are 117 | signed that these three bits are treated as 3 bits of two's complement, 118 | meaning that the top bit would make the result negative. To give an 119 | example, if the next three bits are encountered: 120 | 121 | ``` 122 | 111 123 | ``` 124 | 125 | Then the result should be -1. Arbitrarily wide two's complement support 126 | had not been possible until today. This functionality is now exposed in 127 | two new functions: 128 | 129 | ``` 130 | read_signed_bits 131 | read_signed_bits_unchecked 132 | ``` 133 | 134 | ### Rework `read_bytes` to Accept Mutable Buffer 135 | 136 | By moving `read_bytes` to accept a mutable buffer: 137 | 138 | - Remove all allocations (hidden or otherwise) from the API 139 | - Increase performance in both aligned and unaligned scenarios 140 | 141 | Instead of 142 | 143 | ```rust 144 | use bitter::BitGet; 145 | let mut bitter = BitGet::new(&[0b1010_1010, 0b0101_0101]); 146 | assert_eq!( 147 | bitter.read_bytes(2).map(|x| x.into_owned()), 148 | Some(vec![0b1010_1010, 0b0101_0101]) 149 | ) 150 | ``` 151 | 152 | write: 153 | 154 | ```rust 155 | use bitter::{BitReader, LittleEndianReader}; 156 | let mut buf = [0u8; 2]; 157 | let mut bitter = LittleEndianReader::new(&[0b1010_1010, 0b0101_0101]); 158 | assert!(bitter.read_bytes(&mut buf)); 159 | assert_eq!(&buf, &[0b1010_1010, 0b0101_0101]); 160 | ``` 161 | 162 | The new API should be more reminiscent of the Read trait's read method, 163 | which also takes a mutable buffer, but instead of returning a result of 164 | bytes read we know that either we'll fill the entire buffer or there 165 | isn't enough data. 166 | 167 | ### Other Improvements 168 | 169 | - Enable `no_std` builds 170 | - Add `read_f64` and `read_f64_unchecked` 171 | - Bump edition to 2018 172 | 173 | ## 0.4.1 - 2020-11-21 174 | 175 | Maintenance release that trims down the number of files in crate so that the download size is smaller 176 | 177 | ## 0.4.0 - 2019-12-17 178 | 179 | Fix has_bits_remaining at max value to avoid overflow 180 | 181 | The read_bits_max function is now split into two halves: 182 | 183 | - `read_bits_max` which takes one fewer argument 184 | - `read_bits_max_computed` which takes the same arguments but the bits argument should now be one less. 185 | 186 | The first justification is that 187 | 188 | ``` 189 | bits.read_bits_max(100); 190 | ``` 191 | 192 | reads better than 193 | 194 | ``` 195 | bits.read_bits_max(7, 100); 196 | ``` 197 | 198 | It's less prone to error. If one were to use the new API with the 199 | incorrect bit width, a debug assertion will fail. Using the single 200 | argument, `read_bits_max`, one can't call it incorrectly. 201 | 202 | If one already has the bit width of the max value alread calculated then 203 | use the `read_bits_max_computed` function. Keep in mind it has to 204 | satisfy: 205 | 206 | ``` 207 | max(bit_width(max), 1) == bits + 1); 208 | ``` 209 | 210 | The new APIs and assertions fix a potential underflow when 0 is the max 211 | to value read. 212 | 213 | ## 0.3.2 - 2019-05-23 214 | 215 | * A 10%-50% performance improvement to unchecked API (and checked APIs but to a lesser extent) 216 | 217 | ## 0.3.1 - 2019-01-30 218 | 219 | * In 0.3.0 a performance optimization was used that satisfied valgrind's memcheck but failed LLVM's AddressSanitizer with a heap-buffer-overflow despite the overflowed bytes never being acted upon. This optimization has been removed to increase bitter's friendliness. 220 | * Fix bug in `read_bytes` 221 | * Fix bug in `has_bits_remaining` 222 | 223 | ## 0.3.0 - 2019-01-23 224 | 225 | - Breaking change: `bits_remaining` would exhibit overflow when the data was long enough that counting the number of bits (`data.len() * 8`) would overflow. The API has been changed to return `Option`. Prefer `has_bits_remaining` instead 226 | - Version 0.2.0 did not live up to performance expectations, so it's implementation has been rolled back. Instead of reverting back to byteorder, which will verify that every 8 byte cache read has enough data to be fulfilled, byteorder's internal implementation can be extracted so that one can always read into the 8 byte cache. While this does load undefined memory into the cache, the undefined bytes are never referenced, which appeases valgrind. The upside is that this simplifies implementation (ie: makes it easier to audit) and improved performance 15-25% over bitter v1. 227 | - Consequently, unchecked APIs now will exhibit undefined behavior if abused instead of panicking. 228 | - Rust 1.26 now required 229 | 230 | ## 0.2.0 - 2019-01-20 231 | 232 | - Remove byteorder dependency (so bitter is dependency free) 233 | - Requires Rust 1.32 or later 234 | 235 | ## 0.1.0 - 2018-01-25 236 | 237 | * Initial release 238 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bitter" 3 | version = "0.7.1" 4 | authors = ["Nick Babcock "] 5 | description = "Swiftly extracts unaligned bit-level data from a byte slice" 6 | repository = "https://github.com/nickbabcock/bitter" 7 | readme = "README.md" 8 | keywords = ["bitstream", "bits", "binary"] 9 | categories = ["parsing", "no-std"] 10 | license = "MIT" 11 | include = ["src/**/*.rs", "benches"] 12 | edition = "2018" 13 | 14 | [features] 15 | default = ["std"] 16 | std = [] 17 | 18 | [dev-dependencies] 19 | quickcheck = "1" 20 | quickcheck_macros = "1" 21 | no-panic = "0.1" 22 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy of 2 | this software and associated documentation files (the "Software"), to deal in 3 | the Software without restriction, including without limitation the rights to 4 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 5 | of the Software, and to permit persons to whom the Software is furnished to do 6 | so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all 9 | copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 17 | SOFTWARE. 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bitter 2 | 3 | ***Reading bits until the bitter end*** 4 | 5 | ![ci](https://github.com/nickbabcock/bitter/workflows/ci/badge.svg) [![](https://docs.rs/bitter/badge.svg)](https://docs.rs/bitter) [![Version](https://img.shields.io/crates/v/bitter.svg?style=flat-square)](https://crates.io/crates/bitter) 6 | 7 | Bitter reads bits in a desired endian format platform agnostically. Performance is the distinguishing feature compared to other bit readers. See [benchmarks](https://github.com/nickbabcock/bitter#comparison-to-other-libraries) for more information. 8 | 9 | ## Features 10 | 11 | - ✔ support for little endian, big endian, and native endian formats 12 | - ✔ request an arbitrary amount of bits (up to 64 bits) and bytes 13 | - ✔ ergonomic requests for common data types (eg: `u8` ... `u64`, `f64`, etc) 14 | - ✔ fastest bit reader at multi-GiB/s throughput 15 | - ✔ no allocations, no dependencies, and [no panics](https://github.com/dtolnay/no-panic) 16 | - ✔ `no_std` compatible 17 | 18 | ## Quick start with Auto mode 19 | 20 | The example below gives a good demonstration of the main API surface area to decode a 16 bit data model. It strikes a good balance between ergonomics and speed. 21 | 22 | ```rust 23 | use bitter::{BitReader, LittleEndianReader}; 24 | let mut bits = LittleEndianReader::new(&[0xff, 0x04]); 25 | assert_eq!(bits.bytes_remaining(), 2); 26 | assert_eq!(bits.read_bit(), Some(true)); 27 | assert_eq!(bits.bytes_remaining(), 1); 28 | assert_eq!(bits.read_u8(), Some(0x7f)); 29 | assert!(bits.has_bits_remaining(7)); 30 | assert_eq!(bits.read_bits(7), Some(0x02)); 31 | ``` 32 | 33 | The `read_` prefixed functions are colloquially known as "Auto mode", as one does not need to manage the underlying bits in the lookahead buffer. 34 | 35 | ## Manual mode 36 | 37 | One can unlock additional performance by amortizing internal state logic management when exploiting patterns in the encoded data. Our example from before can be rewritten to take advantage of our domain knowledge that we'll be decoding 16 bits. 38 | 39 | ```rust 40 | use bitter::{BitReader, LittleEndianReader}; 41 | let mut bits = LittleEndianReader::new(&[0xff, 0x04]); 42 | 43 | // ... snip code that may have read some bits 44 | 45 | // We first check that there's enough total bits 46 | if !bits.has_bits_remaining(16) { 47 | panic!("not enough bits remaining"); 48 | } 49 | 50 | // Despite there being enough data, the lookahead buffer may not be sufficient 51 | if bits.lookahead_bits() < 16 { 52 | bits.refill_lookahead(); 53 | assert!(bits.lookahead_bits() >= 16) 54 | } 55 | 56 | // We use a combination of peek and consume instead of read_* 57 | assert_eq!(bits.peek(1), 1); 58 | bits.consume(1); 59 | 60 | // Notice how the return type is not an `Option` 61 | assert_eq!(bits.peek(8), 0x7f); 62 | bits.consume(8); 63 | 64 | // We can switch back to auto mode any time 65 | assert!(bits.has_bits_remaining(7)); 66 | assert_eq!(bits.read_bits(7), Some(0x02)); 67 | ``` 68 | 69 | The refill, peek, and consume combination are the building blocks for Manual Mode, and allows fine grain management for hot loops. The surface area of Manual Mode APIs is purposely compact to keep things simple. The Auto mode API is larger as that API should be the first choice. 70 | 71 | A major difference between Manual mode and Auto mode is that one can't peek at more than what's in the lookahead buffer. Since the lookahead buffer will vary between `MAX_READ_BITS` and 63 bits, one will need to write logic to stitch together peeks above `MAX_READ_BITS` in the endian of their choice. 72 | 73 | Manual mode can be intimidating, but it's one of if not the fastest way to decode a bit stream, as it's based on variant 4 from [Fabian Giesen's excellent series on reading bits](https://fgiesen.wordpress.com/2018/02/20/reading-bits-in-far-too-many-ways-part-2/). Others have employed this underlying technique to [significantly speed up DEFLATE](https://dougallj.wordpress.com/2022/08/20/faster-zlib-deflate-decompression-on-the-apple-m1-and-x86/). 74 | 75 | An example of how one can write a Manual mode solution for reading 60 bits: 76 | 77 | ```rust 78 | use bitter::{BitReader, LittleEndianReader}; 79 | 80 | let data: [u8; 8] = [0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89]; 81 | let mut bits = LittleEndianReader::new(&data); 82 | 83 | // ... snip ... maybe some bits are read here. 84 | 85 | let expected = 0x0967_4523_01EF_CDAB; 86 | let bits_to_read = 60u32; 87 | 88 | bits.refill_lookahead(); 89 | let lo_len = bits.lookahead_bits(); 90 | let lo = bits.peek(lo_len); 91 | bits.consume(lo_len); 92 | 93 | let left = bits_to_read - lo_len; 94 | bits.refill_lookahead(); 95 | let hi_len = bits.lookahead_bits().min(left); 96 | let hi = bits.peek(hi_len); 97 | bits.consume(hi_len); 98 | 99 | assert_eq!(expected, (hi << lo_len) + lo); 100 | ``` 101 | 102 | The above is not an endorsement of the best way to simulate larger reads in Manual mode. For instance, it may be better to drain the lookahead first, or use `MAX_READ_BITS` to calculate `lo` instead of querying `lookahead_bits`. Always profile for your environment. 103 | 104 | ## Unchecked mode 105 | 106 | There's one final trick that bitter exposes that dials performance to 11 at the cost of safety and increased assumptions. Welcome to the unchecked refill API (referred to as "unchecked"), which can only be called when there are at least 8 bytes left in the buffer. Anything less than that can cause invalid memory access. The upside is that this API unlocks the holy grail of branchless bit reading. 107 | 108 | Always guard unchecked access at a higher level: 109 | 110 | ```rust 111 | use bitter::{BitReader, LittleEndianReader, MAX_READ_BITS}; 112 | 113 | let mut bits = LittleEndianReader::new(&[0u8; 100]); 114 | let objects_to_read = 10; 115 | let object_bits = 56; 116 | let desired_bits = objects_to_read * object_bits; 117 | let bytes_needed = (desired_bits as f64 / 8.0).ceil(); 118 | 119 | if bits.unbuffered_bytes_remaining() >= bytes_needed as usize { 120 | for _ in 0..objects_to_read { 121 | unsafe { bits.refill_lookahead_unchecked() }; 122 | let _field1 = bits.peek(2); 123 | bits.consume(2); 124 | 125 | let _field2 = bits.peek(18); 126 | bits.consume(18); 127 | 128 | let _field3 = bits.peek(18); 129 | bits.consume(18); 130 | 131 | let _field4 = bits.peek(18); 132 | bits.consume(18); 133 | } 134 | } else if bits.has_bits_remaining(desired_bits) { 135 | // So have enough bits to read all the objects just not 136 | // enough bits to call the unchecked lookahead API everytime. 137 | assert!(false); 138 | } else { 139 | // Not enough data. 140 | assert!(false); 141 | } 142 | ``` 143 | 144 | All three modes: auto, manual, and unchecked can be mixed and matched as desired. 145 | 146 | ### `no_std` crates 147 | 148 | This crate has a feature, `std`, that is enabled by default. To use this crate 149 | in a `no_std` context, add the following to your `Cargo.toml`: 150 | 151 | ```toml 152 | [dependencies] 153 | bitter = { version = "x", default-features = false } 154 | ``` 155 | 156 | ## Comparison to other libraries 157 | 158 | Bitter is hardly the first Rust library for handling bits. 159 | [nom](https://crates.io/crates/nom), 160 | [bitvec](https://github.com/bitvecto-rs/bitvec), 161 | [bitstream_io](https://crates.io/crates/bitstream-io), and 162 | [bitreader](https://crates.io/crates/bitreader) are all crates that deal with bit reading. 163 | The reason why someone would choose bitter is speed. 164 | 165 | ![bench-bit-reads.png](assets/bench-bit-reads.png) 166 | 167 | Takeaways: 168 | 169 | * Bitter is the fastest Rust bit reading library even using the safest Auto API 170 | * Chaining small bit reads with the manual or unchecked API allows Bitter to issue multiple read commands _per nanosecond_ 171 | * At large read sizes, differences between the bitter APIs and the next fastest (bitbuffer) start to diminish. 172 | 173 | ## Benchmarking 174 | 175 | Benchmarks are ran with the following command: 176 | 177 | ```bash 178 | (cd compare && cargo clean && cargo bench) 179 | find ./compare/target -path "*bit-reading*" -wholename "*/new/raw.csv" -print0 \ 180 | | xargs -0 xsv cat rows > assets/bitter-benchmark-data.csv 181 | ``` 182 | 183 | And can be analyzed with the R script found in the assets directory. Keep in mind, benchmarks will vary by machine 184 | -------------------------------------------------------------------------------- /assets/analysis.R: -------------------------------------------------------------------------------- 1 | library(scales) 2 | library(tidyverse) 3 | library(readr) 4 | library(ggnewscale) 5 | 6 | is_bitter <- Vectorize(function(fn) { 7 | switch(fn, 8 | "bitter-auto" = TRUE, 9 | "bitter-manual" = TRUE, 10 | "bitter-unchecked" = TRUE, 11 | FALSE) 12 | }) 13 | 14 | get_line_type <- Vectorize(function(fn) { 15 | switch(fn, 16 | "bitter-auto" = "bitter", 17 | "bitter-manual" = "bitter", 18 | "bitter-unchecked" = "bitter", 19 | "other") 20 | }) 21 | 22 | df <- read_csv("./bitter-benchmark-data.csv") 23 | 24 | df <- mutate(df, 25 | fn = `function`, 26 | bitter = is_bitter(fn), 27 | line = get_line_type(fn), 28 | latency = (iteration_count * 1000) / sample_measured_value, 29 | ) 30 | 31 | functionNames <- df %>% select(fn) %>% distinct() %>% pull() %>% sort(decreasing = TRUE) 32 | pal <- brewer.pal(length(functionNames), "Set1") 33 | names(pal) <- functionNames 34 | 35 | dfBitter <- df %>% filter(bitter == TRUE) 36 | dfOther <- df %>% filter(bitter == FALSE) 37 | 38 | ggplot(mapping = aes(value, latency)) + 39 | stat_summary(data = df, mapping=aes(value, latency, color = fn), fun = mean, geom="point", size = 1.5) + 40 | scale_color_manual("Points", values=pal, guide=FALSE) + 41 | ggnewscale::new_scale_color() + 42 | stat_summary(data = dfOther, mapping=aes(linetype = line, color = fn), fun = mean, geom="line", size = 1.2) + 43 | scale_color_manual("Other Crates", values=pal, guide=guide_legend(order = 2)) + 44 | scale_linetype(guide = FALSE) + 45 | ggnewscale::new_scale_color() + 46 | stat_summary(data = dfBitter, mapping=aes(linetype = line, color = fn), fun = mean, geom="line", size = 1.2) + 47 | scale_color_manual("Bitter", values=pal, guide=guide_legend(order = 1)) + 48 | scale_y_continuous(breaks = pretty_breaks(10)) + 49 | scale_x_continuous(breaks = c(seq(1, 64, 4), 64)) + 50 | labs(title = "Rust Bit Readers Performance Comparison", 51 | subtitle = "Performance measured in reads per nanosecond (higher is better)", 52 | caption = "Bitter implementations marked with solid lines", 53 | col = "Bit reader", 54 | y = "Reads per ns", 55 | x = "Read size (bits)") 56 | ggsave('bench-bit-reads.png', width = 9, height = 5, dpi = 100) 57 | -------------------------------------------------------------------------------- /assets/bench-bit-reads.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nickbabcock/bitter/d9d726d1b43d084709137cd65f7239ddd88f9170/assets/bench-bit-reads.png -------------------------------------------------------------------------------- /compare/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "compare" 3 | version = "0.0.1" 4 | authors = ["Nick Babcock "] 5 | license = "MIT" 6 | edition = "2018" 7 | publish = false 8 | 9 | [workspace] 10 | members = ["."] 11 | 12 | [dev-dependencies] 13 | criterion = { version = "0.5", features = ["csv_output"] } 14 | bitbuffer = "0.10.9" 15 | bitter = {path = ".." } 16 | bitreader = "0.3.8" 17 | bitstream-io = "2.2.0" 18 | bitvec = "1" 19 | llvm-bitcursor = "0.0.3" 20 | 21 | [profile.release] 22 | lto = true 23 | codegen-units = 1 24 | 25 | [[bench]] 26 | name = "bench_bits" 27 | harness = false 28 | -------------------------------------------------------------------------------- /compare/benches/bench_bits.rs: -------------------------------------------------------------------------------- 1 | use bitreader::BitReader as BR; 2 | use bitstream_io::{BitRead, BitReader as bio_br, LittleEndian}; 3 | use bitter; 4 | use bitter::{BitReader, LittleEndianReader}; 5 | use bitvec::{field::BitField, order::Lsb0, view::BitView}; 6 | use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; 7 | use std::io::Cursor; 8 | 9 | static DATA: [u8; 0x10_000] = [0; 0x10_000]; 10 | 11 | const ITER: u64 = 1000; 12 | 13 | // It may seem like cheating to pass in a constant used for loop unrolling, but 14 | // the whole point of manual and unchecked mode is that one can write code that 15 | // can exploit data patterns and this was a good compromise. This could have 16 | // been cranked up to 11 and have the amount of bits be a constant propagated 17 | // into here, but that seemed unfair. 18 | fn bit_reading(mut bitter: T, bits: u32) { 19 | let iterations = ITER / N as u64; 20 | let mut result = 0; 21 | for _ in 0..iterations { 22 | if UNCHECKED { 23 | unsafe { bitter.refill_lookahead_unchecked() } 24 | } else { 25 | bitter.refill_lookahead() 26 | }; 27 | 28 | for _ in 0..N { 29 | result |= bitter.peek(bits); 30 | bitter.consume(bits); 31 | } 32 | } 33 | assert_eq!(result, 0); 34 | } 35 | 36 | fn bitting(c: &mut Criterion) { 37 | let parameters: Vec = (1..65).collect(); 38 | 39 | let mut group = c.benchmark_group("bit-reading"); 40 | for i in parameters { 41 | group.throughput(Throughput::Bytes((i as u64 * ITER) / 8)); 42 | 43 | group.bench_with_input(BenchmarkId::new("bitter-auto", i), &i, |b, param| { 44 | b.iter(|| { 45 | let mut bitter = LittleEndianReader::new(&DATA[..]); 46 | let mut result = bitter.read_bits(1).unwrap(); 47 | for _ in 0..ITER { 48 | result |= bitter.read_bits(*param).unwrap(); 49 | } 50 | assert_eq!(result, 0); 51 | }) 52 | }); 53 | 54 | group.bench_with_input(BenchmarkId::new("bitter-manual", i), &i, |b, param| { 55 | b.iter(|| { 56 | let mut bitter = LittleEndianReader::new(&DATA[..]); 57 | bitter.refill_lookahead(); 58 | bitter.consume(1); 59 | match *param { 60 | 1..=3 => bit_reading::<16, false, _>(bitter, *param), 61 | 4..=7 => bit_reading::<8, false, _>(bitter, *param), 62 | 8..=14 => bit_reading::<4, false, _>(bitter, *param), 63 | 15..=28 => bit_reading::<2, false, _>(bitter, *param), 64 | 29..=56 => bit_reading::<1, false, _>(bitter, *param), 65 | x => { 66 | let mut result = 0; 67 | for _ in 0..ITER { 68 | bitter.refill_lookahead(); 69 | let lo = bitter.peek(bitter::MAX_READ_BITS); 70 | bitter.consume(bitter::MAX_READ_BITS); 71 | 72 | let hi_bits = x - bitter::MAX_READ_BITS; 73 | bitter.refill_lookahead(); 74 | 75 | let hi = bitter.peek(hi_bits); 76 | bitter.consume(hi_bits); 77 | 78 | let read = (hi << bitter::MAX_READ_BITS) + lo; 79 | result |= read; 80 | } 81 | assert_eq!(result, 0); 82 | } 83 | } 84 | }) 85 | }); 86 | 87 | group.bench_with_input(BenchmarkId::new("bitter-unchecked", i), &i, |b, param| { 88 | b.iter(|| { 89 | let mut bitter = LittleEndianReader::new(&DATA[..]); 90 | unsafe { bitter.refill_lookahead_unchecked() }; 91 | bitter.consume(1); 92 | match *param { 93 | 1..=3 => bit_reading::<16, true, _>(bitter, *param), 94 | 4..=7 => bit_reading::<8, true, _>(bitter, *param), 95 | 8..=14 => bit_reading::<4, true, _>(bitter, *param), 96 | 15..=28 => bit_reading::<2, true, _>(bitter, *param), 97 | 29..=56 => bit_reading::<1, true, _>(bitter, *param), 98 | x => { 99 | let mut result = 0; 100 | for _ in 0..ITER { 101 | unsafe { bitter.refill_lookahead_unchecked() } 102 | 103 | let lo = bitter.peek(bitter::MAX_READ_BITS); 104 | bitter.consume(bitter::MAX_READ_BITS); 105 | 106 | let hi_bits = x - bitter::MAX_READ_BITS; 107 | unsafe { bitter.refill_lookahead_unchecked() } 108 | 109 | let hi = bitter.peek(hi_bits); 110 | bitter.consume(hi_bits); 111 | 112 | let read = (hi << bitter::MAX_READ_BITS) + lo; 113 | result |= read; 114 | } 115 | assert_eq!(result, 0); 116 | } 117 | } 118 | }) 119 | }); 120 | 121 | group.bench_with_input(BenchmarkId::new("bitreader", i), &i, |b, param| { 122 | b.iter(|| { 123 | let mut bitter = BR::new(&DATA); 124 | let mut result = bitter.read_u64(1).unwrap(); 125 | for _ in 0..ITER { 126 | result |= bitter.read_u64(*param as u8).unwrap(); 127 | } 128 | assert_eq!(result, 0); 129 | }) 130 | }); 131 | 132 | group.bench_with_input(BenchmarkId::new("bitstream-io", i), &i, |b, param| { 133 | b.iter(|| { 134 | let mut cursor = Cursor::new(&DATA[..]); 135 | { 136 | let mut bits = bio_br::endian(&mut cursor, LittleEndian); 137 | let mut result = bits.read::(1).unwrap(); 138 | for _ in 0..ITER { 139 | result |= bits.read::(*param as u32).unwrap(); 140 | } 141 | assert_eq!(result, 0); 142 | } 143 | }); 144 | }); 145 | 146 | group.bench_with_input(BenchmarkId::new("bitvec", i), &i, |b, param| { 147 | b.iter(|| { 148 | let mut bits = DATA.view_bits::(); 149 | bits = &bits[1..]; 150 | let mut result = 0; 151 | for _ in 0..ITER { 152 | let (curr, next) = bits.split_at(*param as usize); 153 | result |= curr.load_le::(); 154 | bits = next; 155 | } 156 | assert_eq!(result, 0); 157 | }) 158 | }); 159 | 160 | group.bench_with_input(BenchmarkId::new("bitbuffer", i), &i, |b, param| { 161 | b.iter(|| { 162 | let buffer = bitbuffer::BitReadBuffer::new(&DATA[..], bitbuffer::LittleEndian); 163 | let mut stream = bitbuffer::BitReadStream::new(buffer); 164 | stream.skip_bits(1).unwrap(); 165 | let mut result = 0; 166 | for _ in 0..ITER { 167 | result |= stream.read_int::(*param as usize).unwrap(); 168 | } 169 | assert_eq!(result, 0); 170 | }) 171 | }); 172 | 173 | group.bench_with_input(BenchmarkId::new("bitcursor", i), &i, |b, param| { 174 | b.iter(|| { 175 | let mut cursor = llvm_bitcursor::BitCursor::new(&DATA[..]); 176 | let mut result = cursor.read(1).unwrap(); 177 | 178 | // bitcursor does not support 64 bit reads 179 | if *param == 64 { 180 | for _ in 0..ITER { 181 | result |= cursor.read(63).unwrap(); 182 | result |= cursor.read(1).unwrap() << 63; 183 | } 184 | } else { 185 | for _ in 0..ITER { 186 | result |= cursor.read(*param as usize).unwrap(); 187 | } 188 | } 189 | 190 | assert_eq!(result, 0); 191 | }) 192 | }); 193 | } 194 | 195 | group.finish(); 196 | } 197 | 198 | fn real_world1(c: &mut Criterion) { 199 | // Parse a rocket league Quaternion, which is bit reads of 2 + 18 + 18 + 18 = 56; 200 | let mut group = c.benchmark_group("real-world-1"); 201 | 202 | group.throughput(Throughput::Bytes((56 as u64 * ITER) / 8)); 203 | 204 | group.bench_function("bitter-auto", |b| { 205 | b.iter(|| { 206 | let mut bits = LittleEndianReader::new(&DATA); 207 | let mut result = 0; 208 | for _ in 0..ITER { 209 | let a = bits.read_bits(2).unwrap(); 210 | let b = bits.read_bits(18).unwrap(); 211 | let c = bits.read_bits(18).unwrap(); 212 | let d = bits.read_bits(18).unwrap(); 213 | result |= a + b + c + d; 214 | } 215 | assert_eq!(result, 0); 216 | }) 217 | }); 218 | 219 | group.bench_function("bitter-manual", |b| { 220 | b.iter(|| { 221 | let mut bits = LittleEndianReader::new(&DATA); 222 | let mut result = 0; 223 | for _ in 0..ITER { 224 | bits.refill_lookahead(); 225 | assert!(bits.lookahead_bits() >= bitter::MAX_READ_BITS); 226 | 227 | let a = bits.peek(2); 228 | bits.consume(2); 229 | 230 | let b = bits.peek(18); 231 | bits.consume(18); 232 | 233 | let c = bits.peek(18); 234 | bits.consume(18); 235 | 236 | let d = bits.peek(18); 237 | bits.consume(18); 238 | 239 | result |= a + b + c + d; 240 | } 241 | assert_eq!(result, 0); 242 | }) 243 | }); 244 | 245 | group.bench_function("bitter-unchecked", |b| { 246 | b.iter(|| { 247 | let mut bits = LittleEndianReader::new(&DATA); 248 | let mut result = 0; 249 | for _ in 0..ITER { 250 | unsafe { bits.refill_lookahead_unchecked() }; 251 | 252 | let a = bits.peek(2); 253 | bits.consume(2); 254 | 255 | let b = bits.peek(18); 256 | bits.consume(18); 257 | 258 | let c = bits.peek(18); 259 | bits.consume(18); 260 | 261 | let d = bits.peek(18); 262 | bits.consume(18); 263 | 264 | result |= a + b + c + d; 265 | } 266 | assert_eq!(result, 0); 267 | }) 268 | }); 269 | 270 | group.finish(); 271 | } 272 | 273 | fn real_world2(c: &mut Criterion) { 274 | // Parse a rocket league Vector3i (simiplified) 275 | let mut group = c.benchmark_group("real-world-2"); 276 | 277 | group.throughput(Throughput::Bytes((62 as u64 * ITER) / 8)); 278 | 279 | group.bench_function("bitter-auto", |b| { 280 | b.iter(|| { 281 | let mut bits = LittleEndianReader::new(&DATA); 282 | let mut result = 0; 283 | for _ in 0..ITER { 284 | let a = bits.read_bits(4).unwrap(); 285 | let b = bits.read_bits(1).unwrap(); 286 | let c = bits.read_bits(19).unwrap(); 287 | let d = bits.read_bits(19).unwrap(); 288 | let e = bits.read_bits(19).unwrap(); 289 | result |= a + b + c + d + e; 290 | } 291 | assert_eq!(result, 0); 292 | }) 293 | }); 294 | 295 | group.bench_function("bitter-manual", |b| { 296 | b.iter(|| { 297 | let mut bits = LittleEndianReader::new(&DATA); 298 | let mut result = 0; 299 | for _ in 0..ITER { 300 | bits.refill_lookahead(); 301 | assert!(bits.lookahead_bits() >= 24); 302 | let a = bits.peek(4); 303 | bits.consume(4); 304 | 305 | let b = bits.peek(1); 306 | bits.consume(1); 307 | 308 | let c = bits.peek(19); 309 | bits.consume(19); 310 | 311 | bits.refill_lookahead(); 312 | assert!(bits.lookahead_bits() > 36); 313 | let d = bits.peek(19); 314 | bits.consume(19); 315 | 316 | let e = bits.peek(19); 317 | bits.consume(19); 318 | result |= a + b + c + d + e; 319 | } 320 | assert_eq!(result, 0); 321 | }) 322 | }); 323 | 324 | group.bench_function("bitter-unchecked", |b| { 325 | b.iter(|| { 326 | let mut bits = LittleEndianReader::new(&DATA); 327 | let mut result = 0; 328 | for _ in 0..ITER { 329 | unsafe { bits.refill_lookahead_unchecked() }; 330 | 331 | let a = bits.peek(4); 332 | bits.consume(4); 333 | 334 | let b = bits.peek(1); 335 | bits.consume(1); 336 | 337 | let c = bits.peek(19); 338 | bits.consume(19); 339 | 340 | unsafe { bits.refill_lookahead_unchecked() }; 341 | 342 | let d = bits.peek(19); 343 | bits.consume(19); 344 | 345 | let e = bits.peek(19); 346 | bits.consume(19); 347 | result |= a + b + c + d + e; 348 | } 349 | assert_eq!(result, 0); 350 | }) 351 | }); 352 | 353 | group.finish(); 354 | } 355 | 356 | fn read_bytes(c: &mut Criterion) { 357 | let mut group = c.benchmark_group("read_bytes"); 358 | 359 | for i in &[4, 8, 16, 80, 240, 960] { 360 | let iterations = (DATA.len() / *i) - 1; 361 | group.throughput(Throughput::Bytes((iterations * *i) as u64)); 362 | 363 | group.bench_with_input(BenchmarkId::new("aligned", i), &i, |b, param| { 364 | let mut buf = vec![0u8; **param]; 365 | b.iter(|| { 366 | let mut bitter = LittleEndianReader::new(&DATA[..]); 367 | let mut result = true; 368 | for _ in 0..iterations { 369 | result &= bitter.read_bytes(&mut buf); 370 | } 371 | assert!(result); 372 | }) 373 | }); 374 | 375 | group.bench_with_input(BenchmarkId::new("unaligned", i), &i, |b, param| { 376 | let mut buf = vec![0u8; **param]; 377 | b.iter(|| { 378 | let mut bitter = LittleEndianReader::new(&DATA[..]); 379 | bitter.read_bit(); 380 | let mut result = true; 381 | for _ in 0..iterations { 382 | result &= bitter.read_bytes(&mut buf); 383 | } 384 | assert!(result); 385 | }) 386 | }); 387 | } 388 | 389 | group.finish(); 390 | } 391 | 392 | fn signed(c: &mut Criterion) { 393 | let mut group = c.benchmark_group("signed_reads"); 394 | 395 | let bits_to_read = 33; 396 | group.throughput(Throughput::Bytes((bits_to_read as u64 * ITER) / 8)); 397 | 398 | group.bench_function("auto", |b| { 399 | b.iter(|| { 400 | let mut bits = LittleEndianReader::new(&DATA[..]); 401 | let mut result = 0; 402 | for _ in 0..ITER { 403 | result |= bits.read_signed_bits(bits_to_read).unwrap(); 404 | } 405 | assert_eq!(result, 0); 406 | }) 407 | }); 408 | 409 | group.bench_function("manual", |b| { 410 | b.iter(|| { 411 | let mut bits = LittleEndianReader::new(&DATA[..]); 412 | let mut result = 0; 413 | for _ in 0..ITER { 414 | let _len = bits.refill_lookahead(); 415 | let val = bits.peek(bits_to_read); 416 | bits.consume(bits_to_read); 417 | result |= bitter::sign_extend(val, bits_to_read); 418 | } 419 | assert_eq!(result, 0); 420 | }) 421 | }); 422 | 423 | group.finish(); 424 | } 425 | 426 | criterion_group!( 427 | benches, 428 | bitting, 429 | real_world1, 430 | real_world2, 431 | read_bytes, 432 | signed 433 | ); 434 | 435 | criterion_main!(benches); 436 | -------------------------------------------------------------------------------- /examples/sample.rs: -------------------------------------------------------------------------------- 1 | use bitter::BitReader; 2 | use std::io::Read; 3 | 4 | // Using debug_assertions as a poor man's way to omit no_panic compilation on 5 | // unoptimized builds. 6 | #[cfg_attr(not(debug_assertions), no_panic::no_panic)] 7 | #[inline(never)] 8 | fn read_data(data: &[u8]) -> Option { 9 | let mut reader = bitter::LittleEndianReader::new(data); 10 | let mut result = reader.read_signed_bits(27)?; 11 | result += i64::from(reader.read_i8()?); 12 | 13 | reader.refill_lookahead(); 14 | unsafe { reader.refill_lookahead_unchecked() } 15 | result += reader.peek(0) as i64; 16 | reader.consume(0); 17 | 18 | let mut buf = [0u8; 10]; 19 | if !reader.read_bytes(&mut buf) { 20 | return None; 21 | } 22 | 23 | Some(result + i64::from(buf[0])) 24 | } 25 | 26 | fn main() { 27 | let stdin = std::io::stdin(); 28 | let mut data = Vec::new(); 29 | stdin.lock().read_to_end(&mut data).unwrap(); 30 | println!("{:?}", read_data(&data)); 31 | } 32 | -------------------------------------------------------------------------------- /fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | target 3 | corpus 4 | artifacts 5 | -------------------------------------------------------------------------------- /fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | 2 | [package] 3 | name = "bitter-fuzz" 4 | version = "0.0.1" 5 | authors = ["Automatically generated"] 6 | publish = false 7 | edition = "2018" 8 | 9 | [package.metadata] 10 | cargo-fuzz = true 11 | 12 | [dependencies] 13 | bitstream-io = "1.5.0" 14 | libfuzzer-sys = "0.4" 15 | 16 | [dependencies.bitter] 17 | path = ".." 18 | 19 | # Prevent this from interfering with workspaces 20 | [workspace] 21 | members = ["."] 22 | 23 | [[bin]] 24 | name = "fuzz_simple" 25 | path = "fuzz_targets/fuzz_simple.rs" 26 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/fuzz_simple.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use bitter::{BigEndianReader, BitReader, LittleEndianReader}; 3 | use libfuzzer_sys::fuzz_target; 4 | 5 | fuzz_target!(|data: &[u8]| { 6 | let size = LittleEndianReader::new(data).read_u32().unwrap_or(0); 7 | 8 | let mut bits = LittleEndianReader::new(data); 9 | 10 | loop { 11 | if bits.read_bits(17).is_none() { 12 | assert!(!bits.has_bits_remaining(17)); 13 | break; 14 | } 15 | } 16 | 17 | let mut lebits = LittleEndianReader::new(data); 18 | let mut bebits = BigEndianReader::new(data); 19 | for &i in data { 20 | assert_eq!(lebits.read_u8().unwrap(), i); 21 | assert_eq!(bebits.read_u8().unwrap(), i); 22 | } 23 | 24 | assert_eq!(lebits.read_u8(), None); 25 | assert_eq!(bebits.read_u8(), None); 26 | assert!(lebits.is_empty()); 27 | assert!(bebits.is_empty()); 28 | 29 | let mut bitter = LittleEndianReader::new(data); 30 | let mut i = 0; 31 | while bitter.lookahead_bits() != 0 { 32 | let read = (i % 56 + 1) % bitter.lookahead_bits() + 1; 33 | i += 1; 34 | bitter.peek(read); 35 | bitter.consume(read); 36 | if bitter.lookahead_bits() == 0 { 37 | bitter.refill_lookahead(); 38 | } 39 | } 40 | 41 | use bitstream_io::BitRead; 42 | 43 | let bits = (size % bitter::MAX_READ_BITS) + 1; 44 | let mut io = bitstream_io::BitReader::endian(data, bitstream_io::LittleEndian); 45 | let mut bitter = LittleEndianReader::new(data); 46 | 47 | while bitter.has_bits_remaining(bits as usize) { 48 | assert_eq!(io.read(bits).ok(), bitter.read_bits(bits)) 49 | } 50 | 51 | let mut io = bitstream_io::BitReader::endian(data, bitstream_io::BigEndian); 52 | let mut bitter = BigEndianReader::new(data); 53 | 54 | while bitter.has_bits_remaining(bits as usize) { 55 | assert_eq!(io.read(bits).ok(), bitter.read_bits(bits)) 56 | } 57 | 58 | let mut io = bitstream_io::BitReader::endian(data, bitstream_io::BigEndian); 59 | let mut bitter = BigEndianReader::new(data); 60 | assert_eq!(io.read(bits).ok(), bitter.read_bits(bits)); 61 | let mut out1 = vec![0; 12]; 62 | let mut out2 = vec![0; 12]; 63 | let result1 = io.read_bytes(&mut out1); 64 | let result2 = bitter.read_bytes(&mut out2); 65 | assert_eq!(result1.is_ok(), result2); 66 | if result2 { 67 | assert_eq!(out1, out2); 68 | } 69 | }); 70 | -------------------------------------------------------------------------------- /release.toml: -------------------------------------------------------------------------------- 1 | tag-message = "Release {{version}}" 2 | pre-release-commit-message = "Release {{version}}" 3 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | ***Reading bits until the bitter end*** 4 | 5 | Bitter reads bits in a desired endian format platform agnostically. Performance is the distinguishing feature compared to other bit readers. See [benchmarks](https://github.com/nickbabcock/bitter#comparison-to-other-libraries) for more information. 6 | 7 | ## Features 8 | 9 | - ✔ support for little endian, big endian, and native endian formats 10 | - ✔ request an arbitrary amount of bits (up to 64 bits) and bytes 11 | - ✔ ergonomic requests for common data types (eg: `u8` ... `u64`, `f64`, etc) 12 | - ✔ fastest bit reader at multi-GiB/s throughput 13 | - ✔ no allocations, no dependencies, and [no panics](https://github.com/dtolnay/no-panic) 14 | - ✔ `no_std` compatible 15 | 16 | ## Quick start with Auto mode 17 | 18 | The example below gives a good demonstration of the main API surface area to decode a 16 bit data model. It strikes a good balance between ergonomics and speed. 19 | 20 | ```rust 21 | use bitter::{BitReader, LittleEndianReader}; 22 | let mut bits = LittleEndianReader::new(&[0xff, 0x04]); 23 | assert_eq!(bits.bytes_remaining(), 2); 24 | assert_eq!(bits.read_bit(), Some(true)); 25 | assert_eq!(bits.bytes_remaining(), 1); 26 | assert_eq!(bits.read_u8(), Some(0x7f)); 27 | assert!(bits.has_bits_remaining(7)); 28 | assert_eq!(bits.read_bits(7), Some(0x02)); 29 | ``` 30 | 31 | The `read_` prefixed functions are colloquially known as "Auto mode", as one does not need to manage the underlying bits in the lookahead buffer. 32 | 33 | ## Manual mode 34 | 35 | One can unlock additional performance by amortizing internal state logic management when exploiting patterns in the encoded data. Our example from before can be rewritten to take advantage of our domain knowledge that we'll be decoding 16 bits. 36 | 37 | ```rust 38 | use bitter::{BitReader, LittleEndianReader}; 39 | let mut bits = LittleEndianReader::new(&[0xff, 0x04]); 40 | 41 | // ... snip code that may have read some bits 42 | 43 | // We first check that there's enough total bits 44 | if !bits.has_bits_remaining(16) { 45 | panic!("not enough bits remaining"); 46 | } 47 | 48 | // Despite there being enough data, the lookahead buffer may not be sufficient 49 | if bits.lookahead_bits() < 16 { 50 | bits.refill_lookahead(); 51 | assert!(bits.lookahead_bits() >= 16) 52 | } 53 | 54 | // We use a combination of peek and consume instead of read_* 55 | assert_eq!(bits.peek(1), 1); 56 | bits.consume(1); 57 | 58 | // Notice how the return type is not an `Option` 59 | assert_eq!(bits.peek(8), 0x7f); 60 | bits.consume(8); 61 | 62 | // We can switch back to auto mode any time 63 | assert!(bits.has_bits_remaining(7)); 64 | assert_eq!(bits.read_bits(7), Some(0x02)); 65 | ``` 66 | 67 | The refill, peek, and consume combination are the building blocks for Manual Mode, and allows fine grain management for hot loops. The surface area of Manual Mode APIs is purposely compact to keep things simple. The Auto mode API is larger as that API should be the first choice. 68 | 69 | A major difference between Manual mode and Auto mode is that one can't peek at more than what's in the lookahead buffer. Since the lookahead buffer will vary between `MAX_READ_BITS` and 63 bits, one will need to write logic to stitch together peeks above `MAX_READ_BITS` in the endian of their choice. 70 | 71 | Manual mode can be intimidating, but it's one of if not the fastest way to decode a bit stream, as it's based on variant 4 from [Fabian Giesen's excellent series on reading bits](https://fgiesen.wordpress.com/2018/02/20/reading-bits-in-far-too-many-ways-part-2/). Others have employed this underlying technique to [significantly speed up DEFLATE](https://dougallj.wordpress.com/2022/08/20/faster-zlib-deflate-decompression-on-the-apple-m1-and-x86/). 72 | 73 | An example of how one can write a Manual mode solution for reading 60 bits: 74 | 75 | ```rust 76 | use bitter::{BitReader, LittleEndianReader}; 77 | 78 | let data: [u8; 8] = [0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89]; 79 | let mut bits = LittleEndianReader::new(&data); 80 | 81 | // ... snip ... maybe some bits are read here. 82 | 83 | let expected = 0x0967_4523_01EF_CDAB; 84 | let bits_to_read = 60u32; 85 | 86 | bits.refill_lookahead(); 87 | let lo_len = bits.lookahead_bits(); 88 | let lo = bits.peek(lo_len); 89 | bits.consume(lo_len); 90 | 91 | let left = bits_to_read - lo_len; 92 | bits.refill_lookahead(); 93 | let hi_len = bits.lookahead_bits().min(left); 94 | let hi = bits.peek(hi_len); 95 | bits.consume(hi_len); 96 | 97 | assert_eq!(expected, (hi << lo_len) + lo); 98 | ``` 99 | 100 | The above is not an endorsement of the best way to simulate larger reads in Manual mode. For instance, it may be better to drain the lookahead first, or use `MAX_READ_BITS` to calculate `lo` instead of querying `lookahead_bits`. Always profile for your environment. 101 | 102 | ## Unchecked mode 103 | 104 | There's one final trick that bitter exposes that dials performance to 11 at the cost of safety and increased assumptions. Welcome to the unchecked refill API (referred to as "unchecked"), which can only be called when there are at least 8 bytes left in the buffer. Anything less than that can cause invalid memory access. The upside is that this API unlocks the holy grail of branchless bit reading. 105 | 106 | Always guard unchecked access at a higher level: 107 | 108 | ```rust 109 | use bitter::{BitReader, LittleEndianReader, MAX_READ_BITS}; 110 | 111 | let mut bits = LittleEndianReader::new(&[0u8; 100]); 112 | let objects_to_read = 10; 113 | let object_bits = 56; 114 | let desired_bits = objects_to_read * object_bits; 115 | let bytes_needed = (desired_bits as f64 / 8.0).ceil(); 116 | 117 | if bits.unbuffered_bytes_remaining() >= bytes_needed as usize { 118 | for _ in 0..objects_to_read { 119 | unsafe { bits.refill_lookahead_unchecked() }; 120 | let _field1 = bits.peek(2); 121 | bits.consume(2); 122 | 123 | let _field2 = bits.peek(18); 124 | bits.consume(18); 125 | 126 | let _field3 = bits.peek(18); 127 | bits.consume(18); 128 | 129 | let _field4 = bits.peek(18); 130 | bits.consume(18); 131 | } 132 | } else if bits.has_bits_remaining(desired_bits) { 133 | // So have enough bits to read all the objects just not 134 | // enough bits to call the unchecked lookahead API everytime. 135 | assert!(false); 136 | } else { 137 | // Not enough data. 138 | assert!(false); 139 | } 140 | ``` 141 | 142 | All three modes: auto, manual, and unchecked can be mixed and matched as desired. 143 | 144 | ### `no_std` crates 145 | 146 | This crate has a feature, `std`, that is enabled by default. To use this crate 147 | in a `no_std` context, add the following to your `Cargo.toml`: 148 | 149 | ```toml 150 | [dependencies] 151 | bits = { version = "x", default-features = false } 152 | ``` 153 | 154 | */ 155 | 156 | #![warn(missing_docs)] 157 | #![cfg_attr(all(not(feature = "std"), not(test)), no_std)] 158 | 159 | /// Read bits in a given endian order 160 | pub trait BitReader { 161 | /// Consume a bit and return if the bit was enabled 162 | /// 163 | /// ```rust 164 | /// use bitter::{BitReader, BigEndianReader}; 165 | /// let mut bits = BigEndianReader::new(&[0b1001_0011]); 166 | /// assert_eq!(bits.read_bit(), Some(true)); 167 | /// assert_eq!(bits.read_bit(), Some(false)); 168 | /// ``` 169 | fn read_bit(&mut self) -> Option; 170 | 171 | /// Consume 8 bits and return the deserialized byte 172 | /// 173 | /// ```rust 174 | /// use bitter::{BitReader, BigEndianReader}; 175 | /// let mut bits = BigEndianReader::new(&[0b1001_0011]); 176 | /// assert_eq!(bits.read_u8(), Some(0b1001_0011)); 177 | /// ``` 178 | fn read_u8(&mut self) -> Option; 179 | 180 | /// Consume 8 bits and return the deserialized byte 181 | /// 182 | /// ```rust 183 | /// use bitter::{BitReader, BigEndianReader}; 184 | /// let mut bits = BigEndianReader::new(&[0b1001_0011]); 185 | /// assert_eq!(bits.read_i8(), Some(-109)); 186 | /// ``` 187 | fn read_i8(&mut self) -> Option; 188 | 189 | /// Consume 16 bits and return the deserialized short 190 | /// 191 | /// ```rust 192 | /// use bitter::{BitReader, LittleEndianReader}; 193 | /// let mut bits = LittleEndianReader::new(&[0b1001_0011, 0b1111_1111]); 194 | /// assert_eq!(bits.read_u16(), Some(0xff93)); 195 | /// ``` 196 | fn read_u16(&mut self) -> Option; 197 | 198 | /// Consume 16 bits and return the deserialized short 199 | /// 200 | /// ```rust 201 | /// use bitter::{BitReader, LittleEndianReader}; 202 | /// let data = (-500i16).to_le_bytes(); 203 | /// let mut bits = LittleEndianReader::new(&data); 204 | /// assert_eq!(bits.read_i16(), Some(-500)); 205 | /// ``` 206 | fn read_i16(&mut self) -> Option; 207 | 208 | /// Consume 32 bits and return the deserialized int 209 | /// 210 | /// ```rust 211 | /// use bitter::{BitReader, LittleEndianReader}; 212 | /// let data = (22000u32).to_le_bytes(); 213 | /// let mut bits = LittleEndianReader::new(&data); 214 | /// assert_eq!(bits.read_u32(), Some(22000u32)); 215 | /// ``` 216 | fn read_u32(&mut self) -> Option; 217 | 218 | /// Consume 32 bits and return the deserialized int 219 | /// 220 | /// ```rust 221 | /// use bitter::{BitReader, BigEndianReader}; 222 | /// let data = (-22000i32).to_be_bytes(); 223 | /// let mut bits = BigEndianReader::new(&data); 224 | /// assert_eq!(bits.read_i32(), Some(-22000i32)); 225 | /// ``` 226 | fn read_i32(&mut self) -> Option; 227 | 228 | /// Consume 32 bits and return the deserialized floating point 229 | /// 230 | /// ```rust 231 | /// use bitter::{BitReader, BigEndianReader}; 232 | /// let data = 12.5f32.to_be_bytes(); 233 | /// let mut bits = BigEndianReader::new(&data); 234 | /// assert_eq!(bits.read_f32(), Some(12.5f32)); 235 | /// ``` 236 | fn read_f32(&mut self) -> Option; 237 | 238 | /// Consume 64 bits and return the deserialized int 239 | /// 240 | /// ```rust 241 | /// use bitter::{BitReader, LittleEndianReader}; 242 | /// let data = (22000u64).to_le_bytes(); 243 | /// let mut bits = LittleEndianReader::new(&data); 244 | /// assert_eq!(bits.read_u64(), Some(22000u64)); 245 | /// ``` 246 | fn read_u64(&mut self) -> Option; 247 | 248 | /// Consume 64 bits and return the deserialized int 249 | /// 250 | /// ```rust 251 | /// use bitter::{BitReader, BigEndianReader}; 252 | /// let data = (-22000i64).to_be_bytes(); 253 | /// let mut bits = BigEndianReader::new(&data); 254 | /// assert_eq!(bits.read_i64(), Some(-22000i64)); 255 | /// ``` 256 | fn read_i64(&mut self) -> Option; 257 | 258 | /// Consume 64 bits and return the deserialized floating point 259 | /// 260 | /// ```rust 261 | /// use bitter::{BitReader, BigEndianReader}; 262 | /// let data = 12.5f64.to_be_bytes(); 263 | /// let mut bits = BigEndianReader::new(&data); 264 | /// assert_eq!(bits.read_f64(), Some(12.5f64)); 265 | /// ``` 266 | fn read_f64(&mut self) -> Option; 267 | 268 | /// Reads an arbitrary number of bits in the range of [0, 64] and returns 269 | /// the unsigned result 270 | /// 271 | /// ```rust 272 | /// use bitter::{BitReader, BigEndianReader}; 273 | /// let mut bits = BigEndianReader::new(&[0xff, 0x00, 0xab, 0xcd]); 274 | /// assert_eq!(bits.read_bits(32), Some(0xff00abcd)); 275 | /// ``` 276 | fn read_bits(&mut self, bits: u32) -> Option; 277 | 278 | /// Reads an arbitrary number of bits in the range of [0, 64] and returns 279 | /// the signed result. 280 | /// 281 | /// If the most significant bit is enabled, the result will be negative. 282 | /// This can be somewhat counterintuitive so see the examples 283 | /// 284 | /// ```rust 285 | /// use bitter::{BitReader, BigEndianReader}; 286 | /// let mut bits = BigEndianReader::new(&[0xfa, 0x93]); 287 | /// assert_eq!(bits.read_signed_bits(4), Some(-1)); 288 | /// assert_eq!(bits.read_signed_bits(4), Some(-6)); 289 | /// assert_eq!(bits.read_signed_bits(4), Some(-7)); 290 | /// assert_eq!(bits.read_signed_bits(4), Some(3)); 291 | /// ``` 292 | /// 293 | /// To think of it another way, reading the number of bits equivalent to a 294 | /// builtin type (i8, i16, etc), will always equal its associated ergonomic 295 | /// equivalent when casted. 296 | /// 297 | /// ```rust 298 | /// use bitter::{BitReader, BigEndianReader}; 299 | /// let mut bits = BigEndianReader::new(&[0xff]); 300 | /// let mut bitter2 = BigEndianReader::new(&[0xff]); 301 | /// assert_eq!( 302 | /// bits.read_signed_bits(8).map(|x| x as i8), 303 | /// bitter2.read_i8() 304 | /// ); 305 | /// ``` 306 | fn read_signed_bits(&mut self, bits: u32) -> Option; 307 | 308 | /// Returns how many complete bytes are left in the bitstream. 309 | /// 310 | /// ```rust 311 | /// # use bitter::{LittleEndianReader, BitReader}; 312 | /// let mut bits = LittleEndianReader::new(&[0xff]); 313 | /// assert_eq!(bits.bytes_remaining(), 1); 314 | /// assert!(bits.read_bit().is_some()); 315 | /// assert_eq!(bits.bytes_remaining(), 0); 316 | /// assert!(bits.read_bits(7).is_some()); 317 | /// assert_eq!(bits.bytes_remaining(), 0); 318 | /// ``` 319 | fn bytes_remaining(&self) -> usize; 320 | 321 | /// Returns how many bytes are still left in the passed in buffer. 322 | /// 323 | /// How many bytes remain in the original buffer is typically an 324 | /// implementation detail, and one should prefer 325 | /// [`BitReader::bytes_remaining`], which includes bytes in the lookahead 326 | /// buffer. 327 | /// 328 | /// However, the bitter unchecked API, 329 | /// [`BitReader::refill_lookahead_unchecked`], requires this same 330 | /// calculation to avoid undefined behavior. 331 | /// 332 | /// **Anecdotally** the use of this function can assist the compiler in 333 | /// eliminating branches that would appear in 334 | /// [`BitReader::refill_lookahead`] without needing to introduce unsafe 335 | /// blocks. Your mileage may vary. 336 | /// 337 | /// ```rust 338 | /// # use bitter::{LittleEndianReader, BitReader}; 339 | /// let mut bits = LittleEndianReader::new(&[0u8; 100]); 340 | /// if bits.unbuffered_bytes_remaining() >= 16 { 341 | /// // The compiler can eliminate the end of buffer checks 342 | /// // in both of these refills without needing to drop to unsafe 343 | /// bits.refill_lookahead(); 344 | /// // ... do some reading ... 345 | /// bits.refill_lookahead(); 346 | /// } 347 | /// ``` 348 | fn unbuffered_bytes_remaining(&self) -> usize; 349 | 350 | /// Returns the exact number of bits remaining in the bitstream if the 351 | /// number of bits can fit within a `usize`. For large byte slices, 352 | /// calculating the number of bits can cause an overflow, hence an `Option` 353 | /// is returned. See [`has_bits_remaining`](BitReader::has_bits_remaining) 354 | /// for a more robust, performant, and ergonomic alternative. 355 | /// 356 | /// ```rust 357 | /// # use bitter::{LittleEndianReader, BitReader}; 358 | /// let mut bits = LittleEndianReader::new(&[0xff]); 359 | /// assert_eq!(bits.bits_remaining(), Some(8)); 360 | /// assert!(bits.read_bit().is_some()); 361 | /// assert_eq!(bits.bits_remaining(), Some(7)); 362 | /// assert!(bits.read_bits(7).is_some()); 363 | /// assert_eq!(bits.bits_remaining(), Some(0)); 364 | /// ``` 365 | fn bits_remaining(&self) -> Option; 366 | 367 | /// Returns true if at least `bits` number of bits are left in the stream. A 368 | /// more robust, performant, and ergonomic way than 369 | /// [`bits_remaining`](BitReader::bits_remaining). 370 | /// 371 | /// ```rust 372 | /// # use bitter::{LittleEndianReader, BitReader}; 373 | /// let mut bits = LittleEndianReader::new(&[0xff]); 374 | /// assert!(bits.has_bits_remaining(7)); 375 | /// assert!(bits.has_bits_remaining(8)); 376 | /// assert!(!bits.has_bits_remaining(9)); 377 | /// 378 | /// assert!(bits.read_bit().is_some()); 379 | /// assert!(bits.has_bits_remaining(7)); 380 | /// assert!(!bits.has_bits_remaining(8)); 381 | /// 382 | /// assert!(bits.read_bits(7).is_some()); 383 | /// assert!(!bits.has_bits_remaining(7)); 384 | /// assert!(bits.has_bits_remaining(0)); 385 | /// ``` 386 | fn has_bits_remaining(&self, bits: usize) -> bool; 387 | 388 | /// Read the number of bytes needed to fill the provided buffer. Returns whether 389 | /// the read was successful and the buffer has been filled. If there aren't enough 390 | /// bytes remaining, then the bit stream and buffer remains unchanged 391 | /// 392 | /// ```rust 393 | /// # use bitter::{LittleEndianReader, BitReader}; 394 | /// let mut bits = LittleEndianReader::new(&[0b1010_1010, 0b0101_0101]); 395 | /// let mut buf = [0; 1]; 396 | /// assert_eq!(bits.read_bit(), Some(false)); 397 | /// assert!(bits.read_bytes(&mut buf)); 398 | /// assert_eq!(&buf, &[0b1101_0101]); 399 | /// assert!(!bits.read_bytes(&mut buf)); 400 | /// assert_eq!(bits.read_bits(1), Some(0)); 401 | /// ``` 402 | fn read_bytes(&mut self, buf: &mut [u8]) -> bool; 403 | 404 | /// Returns if the bitstream has no bits left 405 | /// 406 | /// ```rust 407 | /// # use bitter::{LittleEndianReader, BitReader}; 408 | /// let mut bits = LittleEndianReader::new(&[0b1010_1010, 0b0101_0101]); 409 | /// assert_eq!(bits.is_empty(), false); 410 | /// assert_eq!(bits.read_u16(), Some(0b0101_0101_1010_1010)); 411 | /// assert_eq!(bits.is_empty(), true); 412 | /// ``` 413 | fn is_empty(&self) -> bool; 414 | 415 | /// Peeks at the given number of bits in the lookahead buffer 416 | /// 417 | /// It is invalid to peek at more than what was returned in the 418 | /// last [refill](BitReader::refill_lookahead) 419 | /// 420 | /// A core tenent of Manual Mode: refill / peek / consume 421 | fn peek(&self, count: u32) -> u64; 422 | 423 | /// Consumes the number of bits from the lookahead buffer 424 | /// 425 | /// A core tenent of Manual Mode: refill / peek / consume 426 | fn consume(&mut self, count: u32); 427 | 428 | /// Refills the lookahead buffer. 429 | /// 430 | /// A core tenent of Manual Mode: refill / peek / consume 431 | /// 432 | /// Refills the lookahead buffer anywhere between [[`MAX_READ_BITS`], 64] as 433 | /// long as the end of the stream has not been reached. See how many bits 434 | /// are in the buffer with [`BitReader::lookahead_bits`]. 435 | /// 436 | /// If [`BitReader::lookahead_bits`] is already in the specified range, 437 | /// additional refills will have no effect. 438 | fn refill_lookahead(&mut self); 439 | 440 | /// Returns the number of bits in the lookahead buffer 441 | /// 442 | /// These are the number of bits available to be consumed before a refill is 443 | /// necessary. 444 | /// 445 | /// Guaranteed to be between [[`MAX_READ_BITS`], 64] when at least 8 bytes 446 | /// of data remains unread. 447 | fn lookahead_bits(&self) -> u32; 448 | 449 | /// Refills the lookahead buffer without bounds checking 450 | /// 451 | /// After calling, the lookahead buffer is guaranteed to have between 452 | /// [[`MAX_READ_BITS`], 64] bits available to read. 453 | /// 454 | /// # Safety 455 | /// 456 | /// This function assumes that there are at least 8 bytes left unbuffered 457 | /// for an unaligned read. It is undefined behavior if there is less than 8 458 | /// bytes remaining 459 | /// 460 | /// Guard all usages with [`BitReader::unbuffered_bytes_remaining`] 461 | /// 462 | /// ```rust 463 | /// # use bitter::{LittleEndianReader, BitReader}; 464 | /// let mut bits = LittleEndianReader::new(&[0u8; 100]); 465 | /// let objects_to_read = 7; 466 | /// let object_bits = 39; 467 | /// let desired_bits = objects_to_read * object_bits; 468 | /// let bytes_needed = (desired_bits as f64 / 8.0).ceil(); 469 | /// if bits.unbuffered_bytes_remaining() >= bytes_needed as usize { 470 | /// for _ in 0..objects_to_read { 471 | /// unsafe { bits.refill_lookahead_unchecked() }; 472 | /// let _field1 = bits.peek(10); 473 | /// bits.consume(10); 474 | /// 475 | /// let _field2 = bits.peek(29); 476 | /// bits.consume(29); 477 | /// } 478 | /// } 479 | /// ``` 480 | unsafe fn refill_lookahead_unchecked(&mut self); 481 | 482 | /// Returns true if the reader is not partway through a byte 483 | /// 484 | /// ```rust 485 | /// # use bitter::{LittleEndianReader, BitReader}; 486 | /// let mut bits = LittleEndianReader::new(&[0b1010_1010, 0b0101_0101]); 487 | /// let mut buf = [0; 1]; 488 | /// assert!(bits.byte_aligned()); 489 | /// assert_eq!(bits.read_bit(), Some(false)); 490 | /// assert!(!bits.byte_aligned()); 491 | /// ``` 492 | fn byte_aligned(&self) -> bool; 493 | } 494 | 495 | const BYTE_WIDTH: usize = core::mem::size_of::(); 496 | const BIT_WIDTH: usize = BYTE_WIDTH * 8; 497 | 498 | /// The minimum number of bits available after a lookahead refill. 499 | /// 500 | /// This minimum is only guaranteed when the end of data has not been reached. 501 | /// It provides the lower bound to the maximum number of bits that can be 502 | /// consumed without a refill. 503 | /// 504 | /// The actual number of bits available in the lookahead can be found at 505 | /// [`BitReader::lookahead_bits`]. 506 | /// 507 | /// Use static assertions to ensure that your data model fits within expected 508 | /// number of refills 509 | /// 510 | /// ```rust 511 | /// const _: () = assert!( 512 | /// bitter::MAX_READ_BITS >= 56, 513 | /// "unexpected lookahead buffer size" 514 | /// ); 515 | /// ``` 516 | /// 517 | /// If your data model doesn't fit, either use [`BitReader::lookahead_bits`] to 518 | /// dynamically adjust to what's available for a given refill or refill at given 519 | /// intervals regardless of what's available. 520 | pub const MAX_READ_BITS: u32 = 56; 521 | 522 | struct BitterState<'a, const LE: bool> { 523 | /// Pointer of next byte for lookahead 524 | data: &'a [u8], 525 | 526 | /// Current lookahead buffer contents 527 | bit_buf: u64, 528 | 529 | /// Number of bits in buffer 530 | bit_count: u32, 531 | } 532 | 533 | impl<'a, const LE: bool> BitterState<'a, LE> { 534 | #[inline] 535 | #[must_use] 536 | pub fn new(data: &'a [u8]) -> Self { 537 | Self { 538 | data, 539 | bit_buf: 0, 540 | bit_count: 0, 541 | } 542 | } 543 | 544 | #[inline] 545 | fn which(input: u64) -> u64 { 546 | if LE { 547 | input.to_le() 548 | } else { 549 | input.to_be() 550 | } 551 | } 552 | 553 | #[inline] 554 | fn read(&self) -> u64 { 555 | debug_assert!(self.unbuffered_bytes() >= 8); 556 | let mut result = [0u8; 8]; 557 | result.copy_from_slice(&self.data[..8]); 558 | Self::which(u64::from_ne_bytes(result)) 559 | } 560 | 561 | #[inline] 562 | fn read_eof(&self) -> u64 { 563 | debug_assert!(self.unbuffered_bytes() < 8); 564 | let mut result = [0u8; 8]; 565 | let len = self.unbuffered_bytes(); 566 | result[..len].copy_from_slice(self.data); 567 | let result = u64::from_ne_bytes(result); 568 | Self::which(result) 569 | } 570 | 571 | #[inline] 572 | fn peek_(&self, count: u32) -> u64 { 573 | if LE { 574 | self.bit_buf & ((1 << count) - 1) 575 | } else if count != 0 { 576 | self.bit_buf >> (BIT_WIDTH - count as usize) 577 | } else { 578 | 0 579 | } 580 | } 581 | #[inline] 582 | fn consume_(&mut self, count: u32) { 583 | if LE { 584 | self.bit_buf >>= count; 585 | } else { 586 | self.bit_buf <<= count; 587 | } 588 | self.bit_count -= count; 589 | } 590 | 591 | #[inline] 592 | fn refill_shift(&mut self, raw: u64) -> usize { 593 | self.bit_buf |= if LE { 594 | raw << self.bit_count 595 | } else { 596 | raw >> self.bit_count 597 | }; 598 | 7 - ((self.bit_count as usize >> 3) & 7) 599 | } 600 | 601 | #[inline] 602 | fn refill(&mut self) { 603 | let raw = self.read(); 604 | let shift = self.refill_shift(raw); 605 | self.data = &self.data[shift..]; 606 | self.bit_count |= MAX_READ_BITS; 607 | } 608 | 609 | #[inline] 610 | fn refill_eof(&mut self) { 611 | let raw = self.read_eof(); 612 | let shift = self.refill_shift(raw); 613 | let consumed = self.unbuffered_bytes().min(shift); 614 | self.data = &self.data[consumed..]; 615 | self.bit_count += (consumed * 8) as u32; 616 | } 617 | 618 | // End of buffer checking is a fascinating topic, see: 619 | // https://fgiesen.wordpress.com/2016/01/02/end-of-buffer-checks-in-decompressors/ 620 | // 621 | // We use the so declared "boring" method of using a marker to know when 622 | // we can't blindly unalign read 64 bits, and duplicate some behavior. 623 | #[inline] 624 | fn has_data_for_unaligned_loads(&self) -> bool { 625 | self.data.len() >= core::mem::size_of::() 626 | } 627 | 628 | #[inline] 629 | fn unbuffered_bytes(&self) -> usize { 630 | self.data.len() 631 | } 632 | 633 | #[inline] 634 | fn bytes_remaining(&self) -> usize { 635 | self.unbuffered_bytes() + (self.bit_count >> 3) as usize 636 | } 637 | 638 | #[inline] 639 | fn bits_remaining(&self) -> Option { 640 | self.unbuffered_bytes() 641 | .checked_mul(8) 642 | .map(|x| x + self.bit_count as usize) 643 | } 644 | 645 | #[inline] 646 | fn has_bits_remaining(&self, bits: usize) -> bool { 647 | let bytes = self.unbuffered_bytes(); 648 | bytes >= bits || (bytes * 8 + (self.bit_count) as usize) >= bits 649 | } 650 | } 651 | 652 | macro_rules! gen_read { 653 | ($name:ident, $t:ty) => { 654 | #[inline] 655 | fn $name(&mut self) -> Option<$t> { 656 | let bits = (core::mem::size_of::<$t>() * 8); 657 | self.read_bits(bits as u32).map(|x| x as $t) 658 | } 659 | }; 660 | } 661 | 662 | impl BitReader for BitterState<'_, LE> { 663 | gen_read!(read_u8, u8); 664 | gen_read!(read_i8, i8); 665 | gen_read!(read_u16, u16); 666 | gen_read!(read_i16, i16); 667 | gen_read!(read_u32, u32); 668 | gen_read!(read_i32, i32); 669 | gen_read!(read_u64, u64); 670 | gen_read!(read_i64, i64); 671 | 672 | fn read_bit(&mut self) -> Option { 673 | self.read_bits(1).map(|x| x == 1) 674 | } 675 | 676 | #[inline] 677 | fn read_f32(&mut self) -> Option { 678 | self.read_u32().map(f32::from_bits) 679 | } 680 | 681 | #[inline] 682 | fn read_f64(&mut self) -> Option { 683 | self.read_u64().map(f64::from_bits) 684 | } 685 | 686 | #[inline] 687 | fn read_bits(&mut self, bits: u32) -> Option { 688 | debug_assert!( 689 | bits <= BIT_WIDTH as u32, 690 | "read request exceeded limit of {} bits, received {}", 691 | BIT_WIDTH, 692 | bits 693 | ); 694 | 695 | if self.has_data_for_unaligned_loads() { 696 | if bits > self.bit_count { 697 | self.refill(); 698 | } 699 | 700 | if bits <= MAX_READ_BITS { 701 | let result = self.peek(bits); 702 | self.consume(bits); 703 | Some(result) 704 | } else { 705 | let lo = self.peek(MAX_READ_BITS); 706 | self.consume(MAX_READ_BITS); 707 | 708 | self.refill_lookahead(); 709 | let hi_len = bits - MAX_READ_BITS; 710 | let hi = self.peek(hi_len); 711 | self.consume(hi_len); 712 | if LE { 713 | Some((hi << MAX_READ_BITS) + lo) 714 | } else { 715 | Some((lo << hi_len) + hi) 716 | } 717 | } 718 | } else if self.has_bits_remaining(bits as usize) { 719 | self.refill_eof(); 720 | if bits <= MAX_READ_BITS { 721 | let result = self.peek(bits); 722 | self.consume(bits); 723 | Some(result) 724 | } else { 725 | let lo = self.peek(MAX_READ_BITS); 726 | self.consume(MAX_READ_BITS); 727 | 728 | self.refill_eof(); 729 | let hi_len = bits - MAX_READ_BITS; 730 | let hi = self.peek(hi_len); 731 | self.consume(hi_len); 732 | if LE { 733 | Some((hi << MAX_READ_BITS) + lo) 734 | } else { 735 | Some((lo << hi_len) + hi) 736 | } 737 | } 738 | } else { 739 | None 740 | } 741 | } 742 | 743 | #[inline] 744 | fn read_signed_bits(&mut self, bits: u32) -> Option { 745 | self.read_bits(bits).map(|x| sign_extend(x, bits)) 746 | } 747 | 748 | #[inline] 749 | fn bytes_remaining(&self) -> usize { 750 | self.bytes_remaining() 751 | } 752 | 753 | #[inline] 754 | fn unbuffered_bytes_remaining(&self) -> usize { 755 | self.unbuffered_bytes() 756 | } 757 | 758 | #[inline] 759 | fn bits_remaining(&self) -> Option { 760 | self.bits_remaining() 761 | } 762 | 763 | #[inline] 764 | fn has_bits_remaining(&self, bits: usize) -> bool { 765 | self.has_bits_remaining(bits) 766 | } 767 | 768 | #[inline] 769 | fn read_bytes(&mut self, buf: &mut [u8]) -> bool { 770 | let lookahead_bytes = (self.bit_count >> 3) as usize; 771 | 772 | // Before we get to fast-path copying we need to consume as much of 773 | // the lookahead buffer as we can. 774 | let lookahead_consumption = lookahead_bytes.min(buf.len()); 775 | let (buf_lookahead, buf) = buf.split_at_mut(lookahead_consumption); 776 | 777 | let (head, tail) = if buf.len() <= self.data.len() { 778 | self.data.split_at(buf.len()) 779 | } else { 780 | return false; 781 | }; 782 | 783 | for dst in buf_lookahead.iter_mut() { 784 | *dst = self.peek(8) as u8; 785 | self.consume(8); 786 | } 787 | 788 | if self.byte_aligned() { 789 | // Return if the lookahead buffer was big enough to fill everything 790 | if buf.is_empty() { 791 | return true; 792 | } 793 | 794 | buf.copy_from_slice(head); 795 | 796 | // Since we just consumed the entire lookahead (which isn't normally 797 | // possible), we reset internal state. 798 | self.data = tail; 799 | self.bit_buf = 0; 800 | self.refill_lookahead(); 801 | } else if let Some((first, buf)) = buf.split_first_mut() { 802 | // Consume the rest of the lookahead 803 | let lookahead_remainder = self.bit_count; 804 | let lookahead_tail = self.peek(lookahead_remainder) as u8; 805 | self.consume(lookahead_remainder); 806 | 807 | // lookahead now empty, but adjust overlapping data byte 808 | *first = if LE { 809 | (head[0] << lookahead_remainder) + lookahead_tail 810 | } else { 811 | (head[0] >> lookahead_remainder) + (lookahead_tail << (8 - lookahead_remainder)) 812 | }; 813 | 814 | // Then attempt to process multiple 16 bytes at once 815 | let chunk_size = 16; 816 | let buf_chunks = buf.len() / chunk_size; 817 | let chunk_bytes = buf_chunks * chunk_size; 818 | 819 | let (buf_body, buf) = buf.split_at_mut(chunk_bytes); 820 | if LE { 821 | read_n_bytes(lookahead_remainder, head, buf_body); 822 | } else { 823 | read_n_bytes_be(lookahead_remainder, head, buf_body); 824 | } 825 | 826 | // Process trailing bytes that don't fit into chunk 827 | // 828 | // SAFETY: we know this is safe as chunk_bytes is <= than head.len() 829 | // and head was split off from data (but the compiler isn't smart 830 | // enough to deduce this and we want to avoid introducing a panic). 831 | self.data = unsafe { self.data.get_unchecked(chunk_bytes..) }; 832 | self.bit_buf = 0; 833 | self.refill_lookahead(); 834 | self.consume(8 - lookahead_remainder); 835 | self.refill_lookahead(); 836 | 837 | for dst in buf.iter_mut() { 838 | if self.lookahead_bits() < 8 { 839 | self.refill_lookahead(); 840 | debug_assert!(self.lookahead_bits() >= 8); 841 | } 842 | 843 | *dst = self.peek(8) as u8; 844 | self.consume(8); 845 | } 846 | } 847 | 848 | true 849 | } 850 | 851 | #[inline] 852 | fn peek(&self, count: u32) -> u64 { 853 | debug_assert!( 854 | count <= self.bit_count, 855 | "not enough bits in lookahead buffer to fulfill peek ({} vs {})", 856 | count, 857 | self.bit_count 858 | ); 859 | self.peek_(count) 860 | } 861 | 862 | #[inline] 863 | fn consume(&mut self, count: u32) { 864 | debug_assert!( 865 | count <= self.bit_count, 866 | "not enough bits in lookahead buffer to fulfill consume ({} vs {})", 867 | count, 868 | self.bit_count 869 | ); 870 | self.consume_(count); 871 | } 872 | 873 | #[inline] 874 | fn lookahead_bits(&self) -> u32 { 875 | self.bit_count 876 | } 877 | 878 | #[inline] 879 | fn refill_lookahead(&mut self) { 880 | if self.has_data_for_unaligned_loads() { 881 | self.refill(); 882 | } else { 883 | self.refill_eof(); 884 | } 885 | } 886 | 887 | #[inline] 888 | unsafe fn refill_lookahead_unchecked(&mut self) { 889 | debug_assert!(self.unbuffered_bytes() >= 8); 890 | let result = self.data.as_ptr().cast::().read_unaligned(); 891 | let shift = self.refill_shift(Self::which(result)); 892 | self.data = self.data.get_unchecked(shift..); 893 | self.bit_count |= MAX_READ_BITS; 894 | } 895 | 896 | #[inline] 897 | fn is_empty(&self) -> bool { 898 | !self.has_bits_remaining(1) 899 | } 900 | 901 | #[inline] 902 | fn byte_aligned(&self) -> bool { 903 | self.bit_count % 8 == 0 904 | } 905 | } 906 | 907 | /// Reads bits in the little-endian format 908 | /// 909 | /// ```rust 910 | /// use bitter::{BitReader, LittleEndianReader}; 911 | /// let mut lebits = LittleEndianReader::new(&[0b0000_0001]); 912 | /// assert_eq!(lebits.read_bit(), Some(true)); 913 | /// ``` 914 | pub struct LittleEndianReader<'a>(BitterState<'a, true>); 915 | 916 | impl<'a> LittleEndianReader<'a> { 917 | /// Create a little endian reader from the given byte slice. 918 | #[inline] 919 | #[must_use] 920 | pub fn new(data: &'a [u8]) -> Self { 921 | Self(BitterState::new(data)) 922 | } 923 | } 924 | 925 | impl BitReader for LittleEndianReader<'_> { 926 | #[inline] 927 | fn read_bit(&mut self) -> Option { 928 | self.0.read_bit() 929 | } 930 | 931 | #[inline] 932 | fn read_u8(&mut self) -> Option { 933 | self.0.read_u8() 934 | } 935 | 936 | #[inline] 937 | fn read_i8(&mut self) -> Option { 938 | self.0.read_i8() 939 | } 940 | 941 | #[inline] 942 | fn read_u16(&mut self) -> Option { 943 | self.0.read_u16() 944 | } 945 | 946 | #[inline] 947 | fn read_i16(&mut self) -> Option { 948 | self.0.read_i16() 949 | } 950 | 951 | #[inline] 952 | fn read_u32(&mut self) -> Option { 953 | self.0.read_u32() 954 | } 955 | 956 | #[inline] 957 | fn read_i32(&mut self) -> Option { 958 | self.0.read_i32() 959 | } 960 | 961 | #[inline] 962 | fn read_f32(&mut self) -> Option { 963 | self.0.read_f32() 964 | } 965 | 966 | #[inline] 967 | fn read_u64(&mut self) -> Option { 968 | self.0.read_u64() 969 | } 970 | 971 | #[inline] 972 | fn read_i64(&mut self) -> Option { 973 | self.0.read_i64() 974 | } 975 | 976 | #[inline] 977 | fn read_f64(&mut self) -> Option { 978 | self.0.read_f64() 979 | } 980 | 981 | #[inline] 982 | fn read_bits(&mut self, bits: u32) -> Option { 983 | self.0.read_bits(bits) 984 | } 985 | 986 | #[inline] 987 | fn read_signed_bits(&mut self, bits: u32) -> Option { 988 | self.0.read_signed_bits(bits) 989 | } 990 | 991 | #[inline] 992 | fn bytes_remaining(&self) -> usize { 993 | self.0.bytes_remaining() 994 | } 995 | 996 | #[inline] 997 | fn unbuffered_bytes_remaining(&self) -> usize { 998 | self.0.unbuffered_bytes() 999 | } 1000 | 1001 | #[inline] 1002 | fn bits_remaining(&self) -> Option { 1003 | self.0.bits_remaining() 1004 | } 1005 | 1006 | #[inline] 1007 | fn has_bits_remaining(&self, bits: usize) -> bool { 1008 | self.0.has_bits_remaining(bits) 1009 | } 1010 | 1011 | #[inline] 1012 | fn read_bytes(&mut self, buf: &mut [u8]) -> bool { 1013 | self.0.read_bytes(buf) 1014 | } 1015 | 1016 | #[inline] 1017 | fn is_empty(&self) -> bool { 1018 | self.0.is_empty() 1019 | } 1020 | 1021 | #[inline] 1022 | fn peek(&self, count: u32) -> u64 { 1023 | self.0.peek(count) 1024 | } 1025 | 1026 | #[inline] 1027 | fn consume(&mut self, count: u32) { 1028 | self.0.consume(count); 1029 | } 1030 | 1031 | #[inline] 1032 | fn lookahead_bits(&self) -> u32 { 1033 | self.0.lookahead_bits() 1034 | } 1035 | 1036 | #[inline] 1037 | fn refill_lookahead(&mut self) { 1038 | self.0.refill_lookahead(); 1039 | } 1040 | 1041 | #[inline] 1042 | unsafe fn refill_lookahead_unchecked(&mut self) { 1043 | self.0.refill_lookahead_unchecked(); 1044 | } 1045 | 1046 | #[inline] 1047 | fn byte_aligned(&self) -> bool { 1048 | self.0.byte_aligned() 1049 | } 1050 | } 1051 | 1052 | /// Reads bits in the big-endian format 1053 | /// 1054 | /// ```rust 1055 | /// use bitter::{BitReader, BigEndianReader}; 1056 | /// let mut bebits = BigEndianReader::new(&[0b1000_0000]); 1057 | /// assert_eq!(bebits.read_bit(), Some(true)); 1058 | /// ``` 1059 | pub struct BigEndianReader<'a>(BitterState<'a, false>); 1060 | 1061 | impl<'a> BigEndianReader<'a> { 1062 | /// Create a big endian reader from the given byte slice. 1063 | #[inline] 1064 | #[must_use] 1065 | pub fn new(data: &'a [u8]) -> Self { 1066 | Self(BitterState::new(data)) 1067 | } 1068 | } 1069 | 1070 | impl BitReader for BigEndianReader<'_> { 1071 | #[inline] 1072 | fn read_bit(&mut self) -> Option { 1073 | self.0.read_bit() 1074 | } 1075 | 1076 | #[inline] 1077 | fn read_u8(&mut self) -> Option { 1078 | self.0.read_u8() 1079 | } 1080 | 1081 | #[inline] 1082 | fn read_i8(&mut self) -> Option { 1083 | self.0.read_i8() 1084 | } 1085 | 1086 | #[inline] 1087 | fn read_u16(&mut self) -> Option { 1088 | self.0.read_u16() 1089 | } 1090 | 1091 | #[inline] 1092 | fn read_i16(&mut self) -> Option { 1093 | self.0.read_i16() 1094 | } 1095 | 1096 | #[inline] 1097 | fn read_u32(&mut self) -> Option { 1098 | self.0.read_u32() 1099 | } 1100 | 1101 | #[inline] 1102 | fn read_i32(&mut self) -> Option { 1103 | self.0.read_i32() 1104 | } 1105 | 1106 | #[inline] 1107 | fn read_f32(&mut self) -> Option { 1108 | self.0.read_f32() 1109 | } 1110 | 1111 | #[inline] 1112 | fn read_u64(&mut self) -> Option { 1113 | self.0.read_u64() 1114 | } 1115 | 1116 | #[inline] 1117 | fn read_i64(&mut self) -> Option { 1118 | self.0.read_i64() 1119 | } 1120 | 1121 | #[inline] 1122 | fn read_f64(&mut self) -> Option { 1123 | self.0.read_f64() 1124 | } 1125 | 1126 | #[inline] 1127 | fn read_bits(&mut self, bits: u32) -> Option { 1128 | self.0.read_bits(bits) 1129 | } 1130 | 1131 | #[inline] 1132 | fn read_signed_bits(&mut self, bits: u32) -> Option { 1133 | self.0.read_signed_bits(bits) 1134 | } 1135 | 1136 | #[inline] 1137 | fn bytes_remaining(&self) -> usize { 1138 | self.0.bytes_remaining() 1139 | } 1140 | 1141 | #[inline] 1142 | fn unbuffered_bytes_remaining(&self) -> usize { 1143 | self.0.unbuffered_bytes() 1144 | } 1145 | 1146 | #[inline] 1147 | fn bits_remaining(&self) -> Option { 1148 | self.0.bits_remaining() 1149 | } 1150 | 1151 | #[inline] 1152 | fn has_bits_remaining(&self, bits: usize) -> bool { 1153 | self.0.has_bits_remaining(bits) 1154 | } 1155 | 1156 | #[inline] 1157 | fn read_bytes(&mut self, buf: &mut [u8]) -> bool { 1158 | self.0.read_bytes(buf) 1159 | } 1160 | 1161 | #[inline] 1162 | fn is_empty(&self) -> bool { 1163 | self.0.is_empty() 1164 | } 1165 | 1166 | #[inline] 1167 | fn peek(&self, count: u32) -> u64 { 1168 | self.0.peek(count) 1169 | } 1170 | 1171 | #[inline] 1172 | fn consume(&mut self, count: u32) { 1173 | self.0.consume(count); 1174 | } 1175 | 1176 | #[inline] 1177 | fn lookahead_bits(&self) -> u32 { 1178 | self.0.lookahead_bits() 1179 | } 1180 | 1181 | #[inline] 1182 | fn refill_lookahead(&mut self) { 1183 | self.0.refill_lookahead(); 1184 | } 1185 | 1186 | #[inline] 1187 | unsafe fn refill_lookahead_unchecked(&mut self) { 1188 | self.0.refill_lookahead_unchecked(); 1189 | } 1190 | 1191 | #[inline] 1192 | fn byte_aligned(&self) -> bool { 1193 | self.0.byte_aligned() 1194 | } 1195 | } 1196 | 1197 | /// Read bits in system native-endian format 1198 | #[cfg(target_endian = "little")] 1199 | pub type NativeEndianReader<'a> = LittleEndianReader<'a>; 1200 | 1201 | /// Read bits in system native-endian format 1202 | #[cfg(target_endian = "big")] 1203 | pub type NativeEndianReader<'a> = BigEndianReader<'a>; 1204 | 1205 | /// Arbitrary sign extension for manual mode API. 1206 | /// 1207 | /// See [`BitReader::read_signed_bits`] for more information 1208 | /// 1209 | /// It is assumed the input value has zeros for bits above the given position. 1210 | /// 1211 | /// ```rust 1212 | /// use bitter::{BitReader, LittleEndianReader}; 1213 | /// let mut bits = LittleEndianReader::new(&[0x9c]); 1214 | /// bits.refill_lookahead(); 1215 | /// let bits_to_read = 4; 1216 | /// let value = bits.peek(bits_to_read); 1217 | /// assert_eq!(value, 12); 1218 | /// assert_eq!(bitter::sign_extend(value, bits_to_read), -4); 1219 | /// bits.consume(bits_to_read); 1220 | /// ``` 1221 | #[inline] 1222 | #[must_use] 1223 | pub fn sign_extend(val: u64, bits: u32) -> i64 { 1224 | // Branchless sign extension from bit twiddling hacks: 1225 | // https://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend 1226 | // 1227 | // The 3 operation approach with division turned out to be significantly slower, 1228 | // and so was not used. 1229 | debug_assert!(val.leading_zeros() as usize >= (BIT_WIDTH - bits as usize)); 1230 | let m = 1i64.wrapping_shl(bits.wrapping_sub(1)); 1231 | #[allow(clippy::cast_possible_wrap)] 1232 | let val = val as i64; 1233 | (val ^ m) - m 1234 | } 1235 | 1236 | #[inline] 1237 | fn read_n_bytes(rem: u32, input: &[u8], out: &mut [u8]) { 1238 | let mask = (1 << rem) - 1; 1239 | let shift = 8 - rem; 1240 | 1241 | for (i, o) in input.windows(2).zip(out.iter_mut()) { 1242 | let left_part = (i[0] >> shift) & mask; 1243 | let right_part = i[1] << rem; 1244 | *o = left_part + right_part; 1245 | } 1246 | } 1247 | 1248 | #[inline] 1249 | fn read_n_bytes_be(rem: u32, input: &[u8], out: &mut [u8]) { 1250 | let mask = (1 << rem) - 1; 1251 | 1252 | for (i, o) in input.windows(2).zip(out.iter_mut()) { 1253 | let left_part = (i[0] & mask) << (8 - rem); 1254 | let right_part = i[1] >> rem; 1255 | *o = left_part | right_part; 1256 | } 1257 | } 1258 | 1259 | #[cfg(test)] 1260 | mod tests { 1261 | use super::*; 1262 | 1263 | #[test] 1264 | fn test_u64_reads_le() { 1265 | let mut bits = LittleEndianReader::new(&[ 1266 | 0xff, 0xfe, 0xfa, 0xf7, 0xf5, 0xf0, 0xb1, 0xb2, 0x01, 0xff, 0xfe, 0xfa, 0xf7, 0xf5, 1267 | 0xf0, 0xb1, 0xb3, 1268 | ]); 1269 | 1270 | let mut out = [0u8; 8]; 1271 | assert!(bits.read_bytes(&mut out)); 1272 | assert_eq!(u64::from_le_bytes(out), 0xb2b1_f0f5_f7fa_feff); 1273 | assert_eq!(bits.read_bits(8), Some(0x01)); 1274 | assert!(bits.read_bytes(&mut out)); 1275 | assert_eq!(u64::from_le_bytes(out), 0xb3b1_f0f5_f7fa_feff); 1276 | } 1277 | 1278 | #[test] 1279 | fn test_has_remaining_bits() { 1280 | let mut bits = LittleEndianReader::new(&[0xff, 0xff]); 1281 | assert!(bits.has_bits_remaining(7)); 1282 | assert!(bits.has_bits_remaining(8)); 1283 | assert!(bits.has_bits_remaining(9)); 1284 | 1285 | assert!(bits.read_bits(9).is_some()); 1286 | assert!(bits.has_bits_remaining(7)); 1287 | assert!(!bits.has_bits_remaining(8)); 1288 | assert!(!bits.has_bits_remaining(9)); 1289 | 1290 | assert!(bits.read_bits(7).is_some()); 1291 | assert!(!bits.has_bits_remaining(7)); 1292 | assert!(bits.has_bits_remaining(0)); 1293 | } 1294 | 1295 | #[test] 1296 | fn test_zero_bit_reads() { 1297 | let mut bits = LittleEndianReader::new(&[0xff]); 1298 | assert_eq!(bits.peek(0), 0); 1299 | bits.consume(0); 1300 | assert_eq!(bits.read_bits(0), Some(0)); 1301 | 1302 | assert_eq!(bits.read_u8(), Some(0xff)); 1303 | assert_eq!(bits.read_bits(0), Some(0)); 1304 | assert_eq!(bits.peek(0), 0); 1305 | bits.consume(0); 1306 | assert_eq!(bits.peek(0), 0); 1307 | bits.consume(0); 1308 | assert_eq!(bits.read_bits(0), Some(0)); 1309 | } 1310 | 1311 | #[test] 1312 | fn test_whole_bytes() { 1313 | let mut bits = LittleEndianReader::new(&[ 1314 | 0xff, 0xdd, 0xee, 0xff, 0xdd, 0xee, 0xaa, 0xbb, 0xcc, 0xdd, 0xff, 0xdd, 0xee, 0xff, 1315 | 0xdd, 1316 | ]); 1317 | assert_eq!(bits.read_u8(), Some(0xff)); 1318 | assert_eq!(bits.read_u16(), Some(u16::from_le_bytes([0xdd, 0xee]))); 1319 | let mut out = [0u8; 8]; 1320 | assert!(bits.read_bytes(&mut out)); 1321 | assert_eq!( 1322 | u64::from_le_bytes(out), 1323 | u64::from_le_bytes([0xff, 0xdd, 0xee, 0xaa, 0xbb, 0xcc, 0xdd, 0xff]) 1324 | ); 1325 | assert_eq!( 1326 | bits.read_u32(), 1327 | Some(u32::from_le_bytes([0xdd, 0xee, 0xff, 0xdd])) 1328 | ); 1329 | } 1330 | 1331 | #[test] 1332 | fn test_whole_bytes_shift() { 1333 | let mut bits = LittleEndianReader::new(&[ 1334 | 0xdf, 0xed, 0xfe, 0xdf, 0xed, 0xae, 0xba, 0xcb, 0xdc, 0xfd, 0xdf, 0xed, 0xfe, 0xdf, 1335 | 0x0d, 1336 | ]); 1337 | 1338 | assert_eq!(bits.read_bits(4), Some(0x0f)); 1339 | assert_eq!(bits.read_u16(), Some(u16::from_le_bytes([0xdd, 0xee]))); 1340 | let mut out = [0u8; 8]; 1341 | assert!(bits.read_bytes(&mut out)); 1342 | assert_eq!( 1343 | u64::from_le_bytes(out), 1344 | u64::from_le_bytes([0xff, 0xdd, 0xee, 0xaa, 0xbb, 0xcc, 0xdd, 0xff]) 1345 | ); 1346 | assert_eq!( 1347 | bits.read_u32(), 1348 | Some(u32::from_le_bytes([0xdd, 0xee, 0xff, 0xdd])) 1349 | ); 1350 | } 1351 | 1352 | #[test] 1353 | fn test_whole_bytes_large() { 1354 | let mut data = vec![0u8; 76]; 1355 | data[0] = 0b0000_0101; 1356 | data[6] = 0b0000_1000; 1357 | data[7] = 0b0000_0010; 1358 | data[8] = 0b0000_0011; 1359 | data[28] = 0b0000_0000; 1360 | data[29] = 0b1111_1111; 1361 | data[74] = 0b1000_0000; 1362 | data[75] = 0b1011_1111; 1363 | 1364 | let mut bitter = LittleEndianReader::new(&data); 1365 | assert_eq!(bitter.read_bit(), Some(true)); 1366 | 1367 | let mut buf = [0u8; 75]; 1368 | assert!(bitter.read_bytes(&mut buf)); 1369 | 1370 | // prefix 1371 | assert_eq!(buf[0], 0b0000_0010); 1372 | assert_eq!(buf[6], 0b0000_0100); 1373 | 1374 | // body 1375 | assert_eq!(buf[7], 0b1000_0001); 1376 | assert_eq!(buf[8], 0b0000_0001); 1377 | assert_eq!(buf[28], 0b1000_0000); 1378 | 1379 | // suffix 1380 | assert_eq!(buf[74], 0b1100_0000); 1381 | assert_eq!(bitter.read_bits(7), Some(0b0101_1111)); 1382 | assert_eq!(bitter.read_bit(), None); 1383 | } 1384 | 1385 | #[test] 1386 | fn test_has_remaining_bits2() { 1387 | let mut bits = LittleEndianReader::new(&[0xff, 0xff, 0xff, 0xff]); 1388 | assert!(bits.read_bits(31).is_some()); 1389 | assert!(!bits.has_bits_remaining(2)); 1390 | assert!(bits.has_bits_remaining(1)); 1391 | } 1392 | 1393 | #[test] 1394 | fn test_signed_bits_fast() { 1395 | let mut bits = LittleEndianReader::new(&[ 1396 | 0x9c, 0x73, 0xce, 0x39, 0xe7, 0x9c, 0x73, 0xce, 0x39, 0xe7, 0x9c, 0x73, 0xce, 0x39, 1397 | 0xe7, 1398 | ]); 1399 | 1400 | for _ in 0..10 { 1401 | assert_eq!(bits.read_signed_bits(5), Some(-4)); 1402 | } 1403 | } 1404 | 1405 | #[test] 1406 | fn test_signed_bits2_fast() { 1407 | let mut bits = 1408 | LittleEndianReader::new(&[0xff, 0xdd, 0xee, 0xff, 0xdd, 0xee, 0xaa, 0xbb, 0xcc, 0xdd]); 1409 | assert_eq!(bits.read_signed_bits(10), Some(0x1ff)); 1410 | assert_eq!(bits.read_signed_bits(10), Some(-73)); 1411 | assert_eq!(bits.read_signed_bits(10), Some(-2)); 1412 | assert_eq!(bits.read_signed_bits(10), Some(-137)); 1413 | assert_eq!(bits.read_signed_bits(8), Some(-18)); 1414 | assert_eq!(bits.read_signed_bits(8), Some(-86)); 1415 | assert_eq!(bits.read_signed_bits(8), Some(-69)); 1416 | assert_eq!(bits.read_signed_bits(8), Some(-52)); 1417 | assert_eq!(bits.read_signed_bits(8), Some(-35)); 1418 | assert_eq!(bits.read_bits(1), None); 1419 | } 1420 | 1421 | #[test] 1422 | fn test_u8_reads() { 1423 | let mut bits = LittleEndianReader::new(&[0xff, 0xfe, 0xfa, 0xf7, 0xf5, 0xf0, 0xb1, 0xb2]); 1424 | assert_eq!(bits.read_u8(), Some(0xff)); 1425 | assert_eq!(bits.read_u8(), Some(0xfe)); 1426 | assert_eq!(bits.read_u8(), Some(0xfa)); 1427 | assert_eq!(bits.read_u8(), Some(0xf7)); 1428 | assert_eq!(bits.read_u8(), Some(0xf5)); 1429 | assert_eq!(bits.read_u8(), Some(0xf0)); 1430 | assert_eq!(bits.read_u8(), Some(0xb1)); 1431 | assert_eq!(bits.read_u8(), Some(0xb2)); 1432 | assert_eq!(bits.read_u8(), None); 1433 | } 1434 | 1435 | #[test] 1436 | fn test_u32_bit_read() { 1437 | let mut bits = LittleEndianReader::new(&[0xff, 0x00, 0xab, 0xcd]); 1438 | assert_eq!(bits.read_bits(32), Some(0xcdab00ff)); 1439 | } 1440 | 1441 | #[test] 1442 | fn test_u32_reads() { 1443 | let mut bits = LittleEndianReader::new(&[ 1444 | 0xff, 1445 | 0x00, 1446 | 0xab, 1447 | 0xcd, 1448 | 0b1111_1110, 1449 | 0b0000_0001, 1450 | 0b0101_0110, 1451 | 0b1001_1011, 1452 | 0b0101_0101, 1453 | ]); 1454 | assert_eq!(bits.read_u32(), Some(0xcdab00ff)); 1455 | assert_eq!(bits.read_bit(), Some(false)); 1456 | assert_eq!(bits.read_u32(), Some(0xcdab00ff)); 1457 | assert_eq!(bits.read_bit(), Some(false)); 1458 | assert_eq!(bits.read_u32(), None); 1459 | } 1460 | 1461 | #[test] 1462 | fn test_f32_reads() { 1463 | let mut bits = LittleEndianReader::new(&[ 1464 | 0b0111_1011, 1465 | 0b0001_0100, 1466 | 0b1010_1110, 1467 | 0b0011_1101, 1468 | 0b1111_0110, 1469 | 0b0010_1000, 1470 | 0b0101_1100, 1471 | 0b0111_1011, 1472 | 0b0000_0010, 1473 | ]); 1474 | assert_eq!(bits.read_f32(), Some(0.085)); 1475 | assert_eq!(bits.read_bit(), Some(false)); 1476 | assert_eq!(bits.read_f32(), Some(0.085)); 1477 | } 1478 | 1479 | #[test] 1480 | fn test_f64_reads() { 1481 | let mut bits = LittleEndianReader::new(&[0u8; 8]); 1482 | assert_eq!(bits.read_f64(), Some(0.0)); 1483 | } 1484 | 1485 | #[test] 1486 | fn test_u32_bits() { 1487 | let mut bits = LittleEndianReader::new(&[0xff, 0xdd, 0xee, 0xff, 0xdd, 0xee]); 1488 | assert_eq!(bits.read_bits(10), Some(0x1ff)); 1489 | assert_eq!(bits.read_bits(10), Some(0x3b7)); 1490 | assert_eq!(bits.read_bits(10), Some(0x3fe)); 1491 | assert_eq!(bits.read_bits(10), Some(0x377)); 1492 | assert_eq!(bits.read_bits(10), None); 1493 | } 1494 | 1495 | #[test] 1496 | fn test_u32_bits2() { 1497 | let mut bits = LittleEndianReader::new(&[ 1498 | 0x9c, 0x73, 0xce, 0x39, 0xe7, 0x9c, 0x73, 0xce, 0x39, 0xe7, 0x9c, 0x73, 0xce, 0x39, 1499 | 0xe7, 1500 | ]); 1501 | for _ in 0..10 { 1502 | assert_eq!(bits.read_bits(5), Some(28)); 1503 | } 1504 | } 1505 | 1506 | #[test] 1507 | fn test_signed_bits2() { 1508 | let mut bits = LittleEndianReader::new(&[ 1509 | 0x9c, 0x73, 0xce, 0x39, 0xe7, 0x9c, 0x73, 0xce, 0x39, 0xe7, 0x9c, 0x73, 0xce, 0x39, 1510 | 0xe7, 1511 | ]); 1512 | 1513 | for _ in 0..10 { 1514 | assert_eq!(bits.read_signed_bits(5), Some(-4)); 1515 | } 1516 | } 1517 | 1518 | #[test] 1519 | fn test_approx_bytes_and_empty() { 1520 | let mut bits = LittleEndianReader::new(&[0xff, 0x04]); 1521 | assert!(!bits.is_empty()); 1522 | assert_eq!(bits.bytes_remaining(), 2); 1523 | assert_eq!(bits.unbuffered_bytes_remaining(), 2); 1524 | assert!(bits.read_bit().is_some()); 1525 | assert!(!bits.is_empty()); 1526 | assert_eq!(bits.bytes_remaining(), 1); 1527 | assert_eq!(bits.unbuffered_bytes_remaining(), 0); 1528 | assert!(bits.read_bits(6).is_some()); 1529 | assert!(!bits.is_empty()); 1530 | assert_eq!(bits.bytes_remaining(), 1); 1531 | assert!(bits.read_bit().is_some()); 1532 | assert!(!bits.is_empty()); 1533 | assert_eq!(bits.bytes_remaining(), 1); 1534 | assert!(bits.read_bit().is_some()); 1535 | assert!(!bits.is_empty()); 1536 | assert_eq!(bits.bytes_remaining(), 0); 1537 | assert!(bits.read_bits(7).is_some()); 1538 | assert!(bits.is_empty()); 1539 | assert_eq!(bits.bytes_remaining(), 0); 1540 | } 1541 | 1542 | #[test] 1543 | fn has_bits_remaining_max() { 1544 | let data = vec![]; 1545 | let bits = LittleEndianReader::new(data.as_slice()); 1546 | assert!(!bits.has_bits_remaining(usize::MAX)); 1547 | } 1548 | 1549 | #[test] 1550 | fn i16_test() { 1551 | let data = [0b1111_1111, 0b1111_1111]; 1552 | let mut bits = LittleEndianReader::new(&data[..]); 1553 | 1554 | assert_eq!(bits.read_i16(), Some(i16::from_le_bytes(data))); 1555 | } 1556 | 1557 | #[test] 1558 | fn i16_min_test() { 1559 | let data = [0b0000_0000, 0b1000_0000]; 1560 | let mut bits = LittleEndianReader::new(&data[..]); 1561 | 1562 | assert_eq!(bits.read_i16(), Some(i16::MIN)); 1563 | } 1564 | 1565 | #[test] 1566 | fn i16_max_test() { 1567 | let data = [0b1111_1111, 0b0111_1111]; 1568 | let mut bits = LittleEndianReader::new(&data[..]); 1569 | 1570 | assert_eq!(bits.read_i16(), Some(i16::MAX)); 1571 | } 1572 | 1573 | #[test] 1574 | fn read_bits_52() { 1575 | let data = [0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88]; 1576 | let mut bits = LittleEndianReader::new(&data); 1577 | assert_eq!(bits.read_bits(4), Some(0xf)); 1578 | assert_eq!(bits.read_bits(52), Some(0x99aabbccddeef)); 1579 | } 1580 | 1581 | #[test] 1582 | fn read_bits_56() { 1583 | let data: [u8; 8] = [0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89]; 1584 | let mut bits = LittleEndianReader::new(&data); 1585 | assert_eq!(bits.read_bits(56), Some(0x67452301efcdab)); 1586 | assert_eq!(bits.read_bits(4), Some(0x09)); 1587 | } 1588 | 1589 | #[test] 1590 | fn read_bits_64() { 1591 | let mut bits = LittleEndianReader::new(&[ 1592 | 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 1593 | 0x67, 0x89, 1594 | ]); 1595 | assert_eq!(bits.read_bits(64), Some(0x8967452301efcdab)); 1596 | assert_eq!(bits.read_bits(64), Some(0x8967452301efcdab)); 1597 | } 1598 | 1599 | #[test] 1600 | fn regression1() { 1601 | let data = vec![0b0000_0010, 0b0011_1111, 0b1011_1100]; 1602 | let mut bits = LittleEndianReader::new(data.as_slice()); 1603 | 1604 | assert_eq!(bits.read_bits(3), Some(2)); 1605 | assert_eq!(bits.read_u8(), Some(224)); 1606 | assert_eq!(bits.read_bit(), Some(true)); 1607 | assert_eq!(bits.read_bits(13), None); 1608 | } 1609 | 1610 | #[test] 1611 | fn test_bytes_remaining() { 1612 | let mut bits = LittleEndianReader::new(&[0xff, 0x04]); 1613 | assert_eq!(bits.bytes_remaining(), 2); 1614 | assert_eq!(bits.unbuffered_bytes_remaining(), 2); 1615 | assert_eq!(bits.read_bit(), Some(true)); 1616 | assert_eq!(bits.unbuffered_bytes_remaining(), 0); 1617 | assert_eq!(bits.bytes_remaining(), 1); 1618 | assert_eq!(bits.read_u8(), Some(0x7f)); 1619 | assert!(bits.has_bits_remaining(7)); 1620 | assert_eq!(bits.read_bits(7), Some(0x02)); 1621 | } 1622 | } 1623 | 1624 | #[cfg(test)] 1625 | mod be_tests { 1626 | use super::{BigEndianReader, BitReader}; 1627 | 1628 | #[test] 1629 | fn test_be_bit_bits_reads() { 1630 | let mut bits = BigEndianReader::new(&[0b1010_1010, 0b0101_0101]); 1631 | assert!(bits.has_bits_remaining(16)); 1632 | assert_eq!(bits.bits_remaining(), Some(16)); 1633 | assert_eq!(bits.read_bits(1), Some(1)); 1634 | assert!(!bits.has_bits_remaining(16)); 1635 | assert_eq!(bits.bits_remaining(), Some(15)); 1636 | assert_eq!(bits.read_bits(1), Some(0)); 1637 | assert_eq!(bits.read_bits(1), Some(1)); 1638 | assert_eq!(bits.read_bits(1), Some(0)); 1639 | assert_eq!(bits.read_bits(1), Some(1)); 1640 | assert_eq!(bits.read_bits(1), Some(0)); 1641 | assert_eq!(bits.read_bits(1), Some(1)); 1642 | assert_eq!(bits.read_bits(1), Some(0)); 1643 | assert_eq!(bits.read_bits(1), Some(0)); 1644 | assert_eq!(bits.read_bits(1), Some(1)); 1645 | assert_eq!(bits.read_bits(1), Some(0)); 1646 | assert_eq!(bits.read_bits(1), Some(1)); 1647 | assert_eq!(bits.read_bits(1), Some(0)); 1648 | assert_eq!(bits.read_bits(1), Some(1)); 1649 | assert_eq!(bits.read_bits(1), Some(0)); 1650 | assert_eq!(bits.read_bits(1), Some(1)); 1651 | 1652 | assert_eq!(bits.read_bits(1), None); 1653 | } 1654 | 1655 | #[test] 1656 | fn test_zero_bit_reads() { 1657 | let mut bits = BigEndianReader::new(&[0xff]); 1658 | assert_eq!(bits.peek(0), 0); 1659 | bits.consume(0); 1660 | assert_eq!(bits.read_bits(0), Some(0)); 1661 | 1662 | assert_eq!(bits.read_u8(), Some(0xff)); 1663 | assert_eq!(bits.read_bits(0), Some(0)); 1664 | assert_eq!(bits.peek(0), 0); 1665 | bits.consume(0); 1666 | assert_eq!(bits.peek(0), 0); 1667 | bits.consume(0); 1668 | assert_eq!(bits.read_bits(0), Some(0)); 1669 | } 1670 | 1671 | #[test] 1672 | fn test_whole_bytes() { 1673 | let mut bits = BigEndianReader::new(&[ 1674 | 0xff, 0xdd, 0xee, 0xff, 0xdd, 0xee, 0xaa, 0xbb, 0xcc, 0xdd, 0xff, 0xdd, 0xee, 0xff, 1675 | 0xdd, 1676 | ]); 1677 | assert_eq!(bits.read_u8(), Some(0xff)); 1678 | assert_eq!(bits.read_u16(), Some(u16::from_be_bytes([0xdd, 0xee]))); 1679 | let mut out = [0u8; 8]; 1680 | assert!(bits.read_bytes(&mut out)); 1681 | assert_eq!( 1682 | u64::from_be_bytes(out), 1683 | u64::from_be_bytes([0xff, 0xdd, 0xee, 0xaa, 0xbb, 0xcc, 0xdd, 0xff]) 1684 | ); 1685 | assert_eq!( 1686 | bits.read_u32(), 1687 | Some(u32::from_be_bytes([0xdd, 0xee, 0xff, 0xdd])) 1688 | ); 1689 | } 1690 | 1691 | #[test] 1692 | fn test_whole_bytes_shift() { 1693 | let mut bits = BigEndianReader::new(&[ 1694 | 0xdf, 0xed, 0xfe, 0xdf, 0xed, 0xae, 0xba, 0xcb, 0xdc, 0xfd, 0xdf, 0xed, 0xfe, 0xdf, 1695 | 0x0d, 1696 | ]); 1697 | 1698 | assert_eq!(bits.read_bits(4), Some(0x0d)); 1699 | assert_eq!(bits.read_u16(), Some(u16::from_be_bytes([0xfe, 0xdf]))); 1700 | let mut out = [0u8; 8]; 1701 | assert!(bits.read_bytes(&mut out)); 1702 | assert_eq!( 1703 | u64::from_be_bytes(out), 1704 | u64::from_be_bytes([0xed, 0xfe, 0xda, 0xeb, 0xac, 0xbd, 0xcf, 0xdd]) 1705 | ); 1706 | 1707 | assert_eq!( 1708 | bits.read_u32(), 1709 | Some(u32::from_be_bytes([0xfe, 0xdf, 0xed, 0xf0])) 1710 | ); 1711 | } 1712 | 1713 | #[test] 1714 | fn test_whole_bytes_trailing() { 1715 | let mut bits = 1716 | BigEndianReader::new(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); 1717 | 1718 | assert!(bits.read_bytes(&mut [0])); 1719 | assert_eq!(bits.read_bits(0), Some(0)); 1720 | } 1721 | 1722 | #[test] 1723 | fn test_whole_bytes_large() { 1724 | let mut data = vec![0u8; 76]; 1725 | data[0] = 0b0000_0101; 1726 | data[6] = 0b0000_1000; 1727 | data[7] = 0b0000_0010; 1728 | data[8] = 0b0000_0011; 1729 | data[28] = 0b0000_0000; 1730 | data[29] = 0b1111_1111; 1731 | data[74] = 0b1000_0000; 1732 | data[75] = 0b1011_1111; 1733 | 1734 | let mut bitter = BigEndianReader::new(&data); 1735 | assert_eq!(bitter.read_bit(), Some(false)); 1736 | 1737 | let mut buf = [0u8; 75]; 1738 | assert!(bitter.read_bytes(&mut buf)); 1739 | 1740 | // prefix 1741 | assert_eq!(buf[0], 0b0000_1010); 1742 | assert_eq!(buf[6], 0b0001_0000); 1743 | 1744 | // body 1745 | assert_eq!(buf[7], 0b0000_0100); 1746 | assert_eq!(buf[8], 0b0000_0110); 1747 | assert_eq!(buf[28], 0b0000_0001); 1748 | 1749 | // suffix 1750 | assert_eq!(buf[74], 0b0000_0001); 1751 | assert_eq!(bitter.read_bits(7), Some(0b0011_1111)); 1752 | assert_eq!(bitter.read_bit(), None); 1753 | } 1754 | 1755 | #[test] 1756 | fn test_u32_bits() { 1757 | let mut bits = 1758 | BigEndianReader::new(&[0xff, 0xdd, 0xee, 0xff, 0xdd, 0xee, 0xaa, 0xbb, 0xcc, 0xdd]); 1759 | assert_eq!(bits.read_bits(10), Some(0b11_1111_1111)); 1760 | assert_eq!(bits.read_bits(10), Some(0b01_1101_1110)); 1761 | assert_eq!(bits.read_bits(10), Some(0b11_1011_1111)); 1762 | assert_eq!(bits.read_bits(10), Some(0b11_1101_1101)); 1763 | assert_eq!(bits.read_bits(8), Some(0xee)); 1764 | assert_eq!(bits.read_bits(8), Some(0xaa)); 1765 | assert_eq!(bits.read_bits(8), Some(0xbb)); 1766 | assert_eq!(bits.read_bits(8), Some(0xcc)); 1767 | assert_eq!(bits.read_bits(8), Some(0xdd)); 1768 | assert_eq!(bits.read_bit(), None); 1769 | } 1770 | 1771 | #[test] 1772 | fn test_u32_bits2() { 1773 | let mut bits = BigEndianReader::new(&[ 1774 | 0b1110_0111, 1775 | 0b0011_1001, 1776 | 0b1100_1110, 1777 | 0b0111_0011, 1778 | 0b1001_1100, 1779 | 0b1110_0111, 1780 | 0b0011_1001, 1781 | 0b1100_1110, 1782 | 0b0111_0011, 1783 | 0b1001_1100, 1784 | ]); 1785 | 1786 | for _ in 0..16 { 1787 | assert_eq!(bits.read_bits(5), Some(28)); 1788 | } 1789 | } 1790 | 1791 | #[test] 1792 | fn test_u32_bit_read() { 1793 | let mut bits = BigEndianReader::new(&[0xff, 0x00, 0xab, 0xcd]); 1794 | assert_eq!(bits.read_bits(32), Some(0xff00abcd)); 1795 | } 1796 | 1797 | #[test] 1798 | fn read_bits_52() { 1799 | let data = [0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88]; 1800 | let mut bits = BigEndianReader::new(&data); 1801 | assert_eq!(bits.read_bits(4), Some(0xf)); 1802 | assert_eq!(bits.read_bits(52), Some(0xfeeddccbbaa99)); 1803 | } 1804 | 1805 | #[test] 1806 | fn read_bits_64() { 1807 | let mut bits = BigEndianReader::new(&[ 1808 | 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 1809 | 0x67, 0x89, 1810 | ]); 1811 | assert_eq!(bits.read_bits(64), Some(0xabcdef0123456789)); 1812 | assert_eq!(bits.read_bits(64), Some(0xabcdef0123456789)); 1813 | } 1814 | 1815 | #[test] 1816 | fn test_signed_bits() { 1817 | let mut bits = BigEndianReader::new(&[0xe7, 0x39, 0xce, 0x73, 0x9C, 0xE7, 0x39, 0xC0]); 1818 | 1819 | for _ in 0..12 { 1820 | assert_eq!(bits.read_signed_bits(5), Some(-4)); 1821 | } 1822 | } 1823 | } 1824 | -------------------------------------------------------------------------------- /tests/properties.rs: -------------------------------------------------------------------------------- 1 | use bitter::{BigEndianReader, BitReader, LittleEndianReader}; 2 | use quickcheck::TestResult; 3 | use quickcheck_macros::quickcheck; 4 | 5 | fn _test_bit_reads_auto(mut bitter: T, data: &[u8]) { 6 | assert!(bitter.has_bits_remaining(data.len() * 8)); 7 | assert!(!bitter.has_bits_remaining(data.len() * 8 + 1)); 8 | assert_eq!(bitter.read_bits(1), Some(0)); 9 | assert_eq!(bitter.read_bits(1), Some(1)); 10 | assert_eq!(bitter.read_bits(1), Some(0)); 11 | assert_eq!(bitter.read_bits(1), Some(1)); 12 | assert_eq!(bitter.read_bits(1), Some(0)); 13 | assert_eq!(bitter.read_bits(1), Some(1)); 14 | assert_eq!(bitter.read_bits(1), Some(0)); 15 | assert_eq!(bitter.read_bits(1), Some(1)); 16 | 17 | assert!(bitter.has_bits_remaining(8)); 18 | assert_eq!(bitter.read_bits(1), Some(1)); 19 | assert_eq!(bitter.read_bits(1), Some(0)); 20 | assert_eq!(bitter.read_bits(1), Some(1)); 21 | assert_eq!(bitter.read_bits(1), Some(0)); 22 | assert_eq!(bitter.read_bits(1), Some(1)); 23 | assert_eq!(bitter.read_bits(1), Some(0)); 24 | assert_eq!(bitter.read_bits(1), Some(1)); 25 | assert_eq!(bitter.read_bits(1), Some(0)); 26 | 27 | if data.len() == 2 { 28 | assert_eq!(bitter.read_bits(1), None); 29 | } 30 | 31 | assert!(bitter.has_bits_remaining((data.len() - 2) * 8)); 32 | assert!(!bitter.has_bits_remaining((data.len() - 2) * 8 + 1)); 33 | } 34 | 35 | #[quickcheck] 36 | fn test_bit_reads_le_auto(mut data: Vec) { 37 | data.insert(0, 0b0101_0101); 38 | data.insert(0, 0b1010_1010); 39 | 40 | let bitter = LittleEndianReader::new(&data); 41 | _test_bit_reads_auto(bitter, &data); 42 | } 43 | 44 | #[quickcheck] 45 | fn test_bit_reads_le_manual(mut data: Vec) -> TestResult { 46 | if data.len() < 2 { 47 | return TestResult::discard(); 48 | } 49 | 50 | data[..2].copy_from_slice(&[0b1010_1010, 0b0101_0101]); 51 | 52 | let mut bitter = LittleEndianReader::new(&data); 53 | 54 | bitter.refill_lookahead(); 55 | assert!(bitter.lookahead_bits() >= 16); 56 | assert!(bitter.has_bits_remaining(data.len() * 8)); 57 | assert!(!bitter.has_bits_remaining(data.len() * 8 + 1)); 58 | assert_eq!(bitter.peek(1), 0); 59 | bitter.consume(1); 60 | assert_eq!(bitter.peek(1), 1); 61 | bitter.consume(1); 62 | assert_eq!(bitter.peek(1), 0); 63 | bitter.consume(1); 64 | assert_eq!(bitter.peek(1), 1); 65 | bitter.consume(1); 66 | assert_eq!(bitter.peek(1), 0); 67 | bitter.consume(1); 68 | assert_eq!(bitter.peek(1), 1); 69 | bitter.consume(1); 70 | assert_eq!(bitter.peek(1), 0); 71 | bitter.consume(1); 72 | assert_eq!(bitter.peek(1), 1); 73 | bitter.consume(1); 74 | 75 | assert!(bitter.has_bits_remaining(8)); 76 | assert_eq!(bitter.peek(1), 1); 77 | bitter.consume(1); 78 | assert_eq!(bitter.peek(1), 0); 79 | bitter.consume(1); 80 | assert_eq!(bitter.peek(1), 1); 81 | bitter.consume(1); 82 | assert_eq!(bitter.peek(1), 0); 83 | bitter.consume(1); 84 | assert_eq!(bitter.peek(1), 1); 85 | bitter.consume(1); 86 | assert_eq!(bitter.peek(1), 0); 87 | bitter.consume(1); 88 | assert_eq!(bitter.peek(1), 1); 89 | bitter.consume(1); 90 | assert_eq!(bitter.peek(1), 0); 91 | bitter.consume(1); 92 | 93 | assert!(bitter.has_bits_remaining((data.len() - 2) * 8)); 94 | assert!(!bitter.has_bits_remaining((data.len() - 2) * 8 + 1)); 95 | bitter.refill_lookahead(); 96 | assert!(bitter.lookahead_bits() as usize <= ((data.len() - 2) * 8),); 97 | 98 | TestResult::passed() 99 | } 100 | 101 | #[quickcheck] 102 | fn test_has_remaining_bits_bit_by_bit(data: Vec) { 103 | let mut bitter = LittleEndianReader::new(&data); 104 | for _ in 0..200 { 105 | assert_eq!(bitter.has_bits_remaining(1), bitter.read_bits(1).is_some()); 106 | } 107 | } 108 | 109 | #[quickcheck] 110 | fn test_16_bit_reads(mut data: Vec) -> TestResult { 111 | if data.len() < 2 { 112 | return TestResult::discard(); 113 | } 114 | 115 | data[..2].copy_from_slice(&[0b1010_1010, 0b0101_0101]); 116 | let mut bitter = LittleEndianReader::new(&data); 117 | assert_eq!(bitter.read_bits(16), Some(0b0101_0101_1010_1010)); 118 | TestResult::passed() 119 | } 120 | 121 | #[quickcheck] 122 | fn test_read_bytes(mut data: Vec) { 123 | let mut buf = [0u8; 2]; 124 | if data.len() < 2 { 125 | let mut bitter = LittleEndianReader::new(&data); 126 | assert!(bitter.read_bytes(&mut buf[..data.len()])); 127 | assert_eq!(&buf[..data.len()], &data); 128 | return; 129 | } 130 | 131 | data[..2].copy_from_slice(&[0b1010_1010, 0b0101_0101]); 132 | 133 | let mut bitter = LittleEndianReader::new(&data); 134 | assert!(bitter.read_bytes(&mut buf)); 135 | assert_eq!(&buf, &[0b1010_1010, 0b0101_0101]); 136 | 137 | let mut bitter = LittleEndianReader::new(&data); 138 | assert_eq!(bitter.read_bits(1), Some(0)); 139 | if data.len() == 2 { 140 | assert!(!bitter.read_bytes(&mut buf)); 141 | } 142 | assert!(bitter.read_bytes(&mut buf[..1])); 143 | assert_eq!(&buf[..1], &[0b1101_0101]); 144 | 145 | let mut remainder = vec![0u8; bitter.bytes_remaining()]; 146 | assert!(bitter.read_bytes(&mut remainder)); 147 | } 148 | 149 | #[quickcheck] 150 | fn test_read_bytes_large(mut data: Vec, buf_len: u8) { 151 | let buf_len = buf_len.saturating_add(75); 152 | let mut buf = vec![0u8; usize::from(buf_len)]; 153 | if data.len() < buf.len() + 1 { 154 | let mut bitter = LittleEndianReader::new(&data); 155 | assert!(bitter.read_bytes(&mut buf[..data.len()])); 156 | assert_eq!(&buf[..data.len()], &data); 157 | return; 158 | } 159 | 160 | data[0] = 0b0000_0101; 161 | data[1] &= 0b1111_1110; // turn off low bit for bit read 162 | data[6] = 0b0000_1000; 163 | data[7] = 0b0000_0010; 164 | data[8] = 0b0000_0011; 165 | data[9] &= 0b1111_1110; // turn off low bit for bit read 166 | data[28] = 0b0000_0000; 167 | data[29] = 0b1111_1111; 168 | data[74] = 0b1000_0000; 169 | data[75] = 0b1011_1111; 170 | 171 | let mut bitter = LittleEndianReader::new(&data); 172 | assert_eq!(bitter.read_u8(), Some(0b0000_0101)); 173 | assert!(bitter.read_bytes(&mut buf)); 174 | assert_eq!(buf[5], 0b0000_1000); 175 | assert_eq!(buf[6], 0b0000_0010); 176 | assert_eq!(buf[7], 0b0000_0011); 177 | assert_eq!(buf[27], 0b0000_0000); 178 | assert_eq!(buf[28], 0b1111_1111); 179 | assert_eq!(buf[73], 0b1000_0000); 180 | assert_eq!(buf[74], 0b1011_1111); 181 | 182 | let mut bitter = LittleEndianReader::new(&data); 183 | assert_eq!(bitter.read_bit(), Some(true)); 184 | assert!(bitter.read_bytes(&mut buf)); 185 | assert_eq!(buf[0], 0b0000_0010); 186 | assert_eq!(buf[6], 0b0000_0100); 187 | assert_eq!(buf[7], 0b1000_0001); 188 | assert_eq!(buf[8], 0b0000_0001); 189 | assert_eq!(buf[28], 0b1000_0000); 190 | assert_eq!(buf[74], 0b1100_0000); 191 | } 192 | 193 | fn _test_read_bytes_equiv(mut bitter1: T, mut bitter2: T, buf_len: u8, shift: u8) { 194 | let mut buf1 = vec![0u8; usize::from(buf_len)]; 195 | let mut buf2 = vec![0u8; usize::from(buf_len)]; 196 | 197 | let shift = shift as u32 % 65; 198 | assert_eq!(bitter1.read_bits(shift), bitter2.read_bits(shift)); 199 | 200 | if !bitter1.has_bits_remaining(buf1.len() * 8) { 201 | assert!(!bitter1.read_bytes(&mut buf1)); 202 | return; 203 | } 204 | 205 | for x in buf1.iter_mut() { 206 | *x = bitter1.read_u8().unwrap(); 207 | } 208 | 209 | assert!(bitter2.read_bytes(&mut buf2)); 210 | assert_eq!(buf1.as_slice(), buf2.as_slice()); 211 | assert_eq!(bitter1.read_bits(shift), bitter2.read_bits(shift)); 212 | } 213 | 214 | #[quickcheck] 215 | fn test_read_bytes_equiv_le(data: Vec, buf_len: u8, shift: u8) { 216 | let bitter1 = LittleEndianReader::new(&data); 217 | let bitter2 = LittleEndianReader::new(&data); 218 | 219 | _test_read_bytes_equiv(bitter1, bitter2, buf_len, shift); 220 | } 221 | 222 | #[quickcheck] 223 | fn test_read_bytes_equiv_be(data: Vec, buf_len: u8, shift: u8) { 224 | let bitter1 = BigEndianReader::new(&data); 225 | let bitter2 = BigEndianReader::new(&data); 226 | 227 | _test_read_bytes_equiv(bitter1, bitter2, buf_len, shift); 228 | } 229 | 230 | #[quickcheck] 231 | fn read_bytes_eq(k1: u8, data: Vec) -> bool { 232 | let mut bits = LittleEndianReader::new(data.as_slice()); 233 | let mut buf = vec![0u8; usize::from(k1)]; 234 | if !bits.read_bytes(&mut buf) { 235 | k1 > data.len() as u8 236 | } else { 237 | buf.as_slice() == &data[..usize::from(k1)] 238 | } 239 | } 240 | 241 | #[quickcheck] 242 | fn test_bit_reads(data: Vec) { 243 | let mut lebits = LittleEndianReader::new(data.as_slice()); 244 | let mut bebits = BigEndianReader::new(data.as_slice()); 245 | 246 | while !lebits.is_empty() { 247 | assert!(lebits.read_bit().is_some()); 248 | } 249 | 250 | while !bebits.is_empty() { 251 | assert!(bebits.read_bit().is_some()); 252 | } 253 | } 254 | 255 | fn _test_bit_reads2(mut bitter: T, bits: u32) { 256 | let chunk = bits % 64 + 1; 257 | while bitter.has_bits_remaining(chunk as usize) { 258 | let mut chunk_remaining = chunk; 259 | while chunk_remaining > 0 { 260 | if bitter.lookahead_bits() == 0 { 261 | bitter.refill_lookahead(); 262 | assert!(bitter.lookahead_bits() > 0); 263 | } 264 | 265 | let to_read = bitter.lookahead_bits().min(chunk_remaining); 266 | let res = bitter.peek(to_read); 267 | bitter.consume(to_read); 268 | let _ = bitter::sign_extend(res, to_read); 269 | chunk_remaining -= to_read; 270 | } 271 | } 272 | } 273 | 274 | #[quickcheck] 275 | fn test_bit_reads2(bits: u32, data: Vec) { 276 | let lebits = LittleEndianReader::new(data.as_slice()); 277 | _test_bit_reads2(lebits, bits); 278 | 279 | let bebits = BigEndianReader::new(data.as_slice()); 280 | _test_bit_reads2(bebits, bits); 281 | } 282 | 283 | #[quickcheck] 284 | fn has_bits_remaining_bit_reads(data: Vec) -> bool { 285 | let mut bits = LittleEndianReader::new(data.as_slice()); 286 | (1..128).all(|_x| bits.has_bits_remaining(1) == bits.read_bit().is_some()) 287 | } 288 | 289 | #[quickcheck] 290 | fn has_bits_remaining_be_bit_reads(data: Vec) -> bool { 291 | let mut bits = BigEndianReader::new(data.as_slice()); 292 | (1..128).all(|_x| bits.has_bits_remaining(1) == bits.read_bit().is_some()) 293 | } 294 | 295 | #[quickcheck] 296 | fn has_bits_remaining(data: Vec) -> bool { 297 | let mut bits = LittleEndianReader::new(data.as_slice()); 298 | (1..32).all(|x| bits.has_bits_remaining(x) == bits.read_bits(x as u32).is_some()) 299 | && bits.has_bits_remaining(8) == bits.read_u8().is_some() 300 | && bits.has_bits_remaining(16) == bits.read_u16().is_some() 301 | && bits.has_bits_remaining(32) == bits.read_u32().is_some() 302 | } 303 | 304 | #[quickcheck] 305 | fn read_byte_eq(data: Vec) -> bool { 306 | let mut lebits = LittleEndianReader::new(data.as_slice()); 307 | let mut bebits = BigEndianReader::new(data.as_slice()); 308 | 309 | for _ in &data { 310 | if lebits.read_u8() != bebits.read_u8() { 311 | return false; 312 | } 313 | } 314 | 315 | lebits.is_empty() && bebits.is_empty() 316 | } 317 | 318 | #[quickcheck] 319 | fn read_byte_unchecked_eq(data: Vec) -> bool { 320 | let mut lebits = LittleEndianReader::new(data.as_slice()); 321 | let mut bebits = BigEndianReader::new(data.as_slice()); 322 | 323 | for _ in &data { 324 | assert_eq!(lebits.read_u8(), bebits.read_u8()); 325 | } 326 | 327 | lebits.is_empty() && bebits.is_empty() 328 | } 329 | 330 | #[quickcheck] 331 | fn read_byte_alternate(data: Vec) -> bool { 332 | let mut lebits = LittleEndianReader::new(data.as_slice()); 333 | let mut bebits = BigEndianReader::new(data.as_slice()); 334 | 335 | for (i, x) in data.iter().enumerate() { 336 | if i % 3 == 0 { 337 | let val = lebits.read_u8().unwrap(); 338 | if val != *x || val != bebits.read_u8().unwrap() { 339 | return false; 340 | } 341 | } else { 342 | lebits.refill_lookahead(); 343 | let le_peek = lebits.peek(8) as u8; 344 | lebits.consume(8); 345 | 346 | bebits.refill_lookahead(); 347 | let be_peek = bebits.peek(8) as u8; 348 | bebits.consume(8); 349 | if le_peek != *x || le_peek != be_peek { 350 | return false; 351 | } 352 | } 353 | } 354 | 355 | lebits.is_empty() && bebits.is_empty() 356 | } 357 | 358 | #[quickcheck] 359 | fn has_bits_remaining_bit_reads_ends(reads: u8, data: Vec) -> bool { 360 | fn test_fn(bits: &mut B, reads: u8) -> bool { 361 | let mut result = true; 362 | if bits.has_bits_remaining(usize::from(reads)) { 363 | for _ in 0..reads { 364 | result &= bits.read_bit().is_some(); 365 | } 366 | } 367 | 368 | result 369 | } 370 | 371 | let mut lebits = LittleEndianReader::new(data.as_slice()); 372 | let mut bebits = LittleEndianReader::new(data.as_slice()); 373 | 374 | test_fn(&mut lebits, reads) && test_fn(&mut bebits, reads) 375 | } 376 | 377 | #[quickcheck] 378 | fn read_le_signed_bits(a: i8, b: i16, c: i32) -> bool { 379 | let mut data = Vec::new(); 380 | data.extend_from_slice(&(a.to_le_bytes())); 381 | data.extend_from_slice(&(b.to_le_bytes())); 382 | data.extend_from_slice(&(c.to_le_bytes())); 383 | let mut lebits = LittleEndianReader::new(data.as_slice()); 384 | 385 | lebits.read_signed_bits(8).map(|x| x as i8) == Some(a) 386 | && lebits.read_signed_bits(16).map(|x| x as i16) == Some(b) 387 | && lebits.read_signed_bits(32).map(|x| x as i32) == Some(c) 388 | } 389 | 390 | #[quickcheck] 391 | fn read_le_signed_bits2(a: i8, b: i16, c: i32) -> bool { 392 | let mut data = Vec::new(); 393 | data.extend_from_slice(&(a.to_le_bytes())); 394 | data.extend_from_slice(&(b.to_le_bytes())); 395 | data.extend_from_slice(&(c.to_le_bytes())); 396 | let mut lebits = LittleEndianReader::new(data.as_slice()); 397 | let mut lebits2 = LittleEndianReader::new(data.as_slice()); 398 | 399 | lebits.read_signed_bits(8).map(|x| x as i8) == lebits2.read_i8() 400 | && lebits.read_signed_bits(16).map(|x| x as i16) == lebits2.read_i16() 401 | && lebits.read_signed_bits(32).map(|x| x as i32) == lebits2.read_i32() 402 | } 403 | 404 | #[quickcheck] 405 | fn read_be_signed_bits(a: i8, b: i16, c: i32) -> bool { 406 | let mut data = Vec::new(); 407 | data.extend_from_slice(&(a.to_be_bytes())); 408 | data.extend_from_slice(&(b.to_be_bytes())); 409 | data.extend_from_slice(&(c.to_be_bytes())); 410 | let mut bebits = BigEndianReader::new(data.as_slice()); 411 | 412 | bebits.read_signed_bits(8).map(|x| x as i8) == Some(a) 413 | && bebits.read_signed_bits(16).map(|x| x as i16) == Some(b) 414 | && bebits.read_signed_bits(32).map(|x| x as i32) == Some(c) 415 | } 416 | 417 | #[quickcheck] 418 | fn read_be_signed_bits2(a: i8, b: i16, c: i32) -> bool { 419 | let mut data = Vec::new(); 420 | data.extend_from_slice(&(a.to_be_bytes())); 421 | data.extend_from_slice(&(b.to_be_bytes())); 422 | data.extend_from_slice(&(c.to_be_bytes())); 423 | let mut bebits = BigEndianReader::new(data.as_slice()); 424 | let mut bebits2 = BigEndianReader::new(data.as_slice()); 425 | 426 | bebits.read_signed_bits(8).map(|x| x as i8) == bebits2.read_i8() 427 | && bebits.read_signed_bits(16).map(|x| x as i16) == bebits2.read_i16() 428 | && bebits.read_signed_bits(32).map(|x| x as i32) == bebits2.read_i32() 429 | } 430 | 431 | #[quickcheck] 432 | fn read_ergonomics(i8: i8, u8: u8, i16: i16, u16: u16, i32: i32, u32: u32, i64: i64, u64: u64) { 433 | let ff32 = f32::from_bits(u32); 434 | let ff64 = f64::from_bits(u64); 435 | 436 | let mut data = Vec::new(); 437 | data.extend_from_slice(&i8.to_le_bytes()); 438 | data.extend_from_slice(&u8.to_le_bytes()); 439 | data.extend_from_slice(&i16.to_le_bytes()); 440 | data.extend_from_slice(&u16.to_le_bytes()); 441 | data.extend_from_slice(&i32.to_le_bytes()); 442 | data.extend_from_slice(&u32.to_le_bytes()); 443 | data.extend_from_slice(&i64.to_le_bytes()); 444 | data.extend_from_slice(&u64.to_le_bytes()); 445 | data.extend_from_slice(&ff32.to_le_bytes()); 446 | data.extend_from_slice(&ff64.to_le_bytes()); 447 | 448 | let mut data2 = Vec::new(); 449 | data2.extend_from_slice(&i8.to_be_bytes()); 450 | data2.extend_from_slice(&u8.to_be_bytes()); 451 | data2.extend_from_slice(&i16.to_be_bytes()); 452 | data2.extend_from_slice(&u16.to_be_bytes()); 453 | data2.extend_from_slice(&i32.to_be_bytes()); 454 | data2.extend_from_slice(&u32.to_be_bytes()); 455 | data2.extend_from_slice(&i64.to_be_bytes()); 456 | data2.extend_from_slice(&u64.to_be_bytes()); 457 | data2.extend_from_slice(&ff32.to_be_bytes()); 458 | data2.extend_from_slice(&ff64.to_be_bytes()); 459 | 460 | let mut lebits = LittleEndianReader::new(&data); 461 | 462 | assert_eq!(lebits.read_i8(), Some(i8)); 463 | assert_eq!(lebits.read_u8(), Some(u8)); 464 | assert_eq!(lebits.read_i16(), Some(i16)); 465 | assert_eq!(lebits.read_u16(), Some(u16)); 466 | assert_eq!(lebits.read_i32(), Some(i32)); 467 | assert_eq!(lebits.read_u32(), Some(u32)); 468 | assert_eq!(lebits.read_i64(), Some(i64)); 469 | assert_eq!(lebits.read_u64(), Some(u64)); 470 | 471 | assert!( 472 | (ff32.is_nan() && lebits.read_f32().unwrap().is_nan()) || lebits.read_f32() == Some(ff32) 473 | ); 474 | 475 | assert!( 476 | (ff64.is_nan() && lebits.read_f64().unwrap().is_nan()) || lebits.read_f64() == Some(ff64) 477 | ); 478 | 479 | let mut bebits = BigEndianReader::new(&data2); 480 | assert_eq!(bebits.read_i8(), Some(i8)); 481 | assert_eq!(bebits.read_u8(), Some(u8)); 482 | assert_eq!(bebits.read_i16(), Some(i16)); 483 | assert_eq!(bebits.read_u16(), Some(u16)); 484 | assert_eq!(bebits.read_i32(), Some(i32)); 485 | assert_eq!(bebits.read_u32(), Some(u32)); 486 | assert_eq!(bebits.read_i64(), Some(i64)); 487 | assert_eq!(bebits.read_u64(), Some(u64)); 488 | 489 | assert!( 490 | (ff32.is_nan() && bebits.read_f32().unwrap().is_nan()) || bebits.read_f32() == Some(ff32) 491 | ); 492 | 493 | assert!( 494 | (ff64.is_nan() && bebits.read_f64().unwrap().is_nan()) || bebits.read_f64() == Some(ff64) 495 | ); 496 | } 497 | -------------------------------------------------------------------------------- /tests/regressions.rs: -------------------------------------------------------------------------------- 1 | use bitter::{BigEndianReader, BitReader, LittleEndianReader}; 2 | 3 | #[test] 4 | fn test_f32_endian() { 5 | let bits = 0x41480000u32; 6 | let le_data = bits.to_le_bytes(); 7 | let be_data = bits.to_be_bytes(); 8 | 9 | let mut lebits = LittleEndianReader::new(&le_data); 10 | let mut bebits = BigEndianReader::new(&be_data); 11 | 12 | assert_eq!(lebits.read_f32(), Some(12.5f32)); 13 | assert_eq!(bebits.read_f32(), Some(12.5f32)); 14 | } 15 | 16 | #[test] 17 | fn read_byte_eq() { 18 | let data = vec![0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1]; 19 | let mut lebits = LittleEndianReader::new(data.as_slice()); 20 | let mut bebits = BigEndianReader::new(data.as_slice()); 21 | 22 | assert_eq!(lebits.read_u8(), bebits.read_u8()); 23 | assert_eq!(lebits.read_u8(), bebits.read_u8()); 24 | assert_eq!(lebits.read_u8(), bebits.read_u8()); 25 | assert_eq!(lebits.read_u8(), bebits.read_u8()); 26 | assert_eq!(lebits.read_u8(), bebits.read_u8()); 27 | assert_eq!(lebits.read_u8(), bebits.read_u8()); 28 | assert_eq!(lebits.read_u8(), bebits.read_u8()); 29 | assert_eq!(lebits.read_u8(), bebits.read_u8()); 30 | assert_eq!(lebits.read_u8(), bebits.read_u8()); 31 | assert_eq!(lebits.read_u8(), bebits.read_u8()); 32 | assert_eq!(lebits.read_u8(), bebits.read_u8()); 33 | assert_eq!(lebits.read_u8(), bebits.read_u8()); 34 | assert_eq!(lebits.read_u8(), bebits.read_u8()); 35 | } 36 | 37 | #[test] 38 | fn read_byte_eq2() { 39 | let data = vec![1, 0, 0, 0, 0, 0, 0, 0, 0]; 40 | let mut lebits = LittleEndianReader::new(data.as_slice()); 41 | let mut bebits = BigEndianReader::new(data.as_slice()); 42 | 43 | assert_eq!(lebits.read_u8(), bebits.read_u8()); 44 | assert_eq!(lebits.read_u8(), bebits.read_u8()); 45 | assert_eq!(lebits.read_u8(), bebits.read_u8()); 46 | assert_eq!(lebits.read_u8(), bebits.read_u8()); 47 | assert_eq!(lebits.read_u8(), bebits.read_u8()); 48 | assert_eq!(lebits.read_u8(), bebits.read_u8()); 49 | assert_eq!(lebits.read_u8(), bebits.read_u8()); 50 | assert_eq!(lebits.read_u8(), bebits.read_u8()); 51 | assert_eq!(lebits.read_u8(), bebits.read_u8()); 52 | assert_eq!(lebits.read_u8(), bebits.read_u8()); 53 | } 54 | 55 | #[test] 56 | fn read_byte_be() { 57 | let data = vec![1, 0, 0, 0, 0, 0, 0, 0, 0]; 58 | let mut bits = BigEndianReader::new(data.as_slice()); 59 | 60 | assert_eq!(bits.read_u8(), Some(1)); 61 | assert_eq!(bits.read_u8(), Some(0)); 62 | assert_eq!(bits.read_u8(), Some(0)); 63 | assert_eq!(bits.read_u8(), Some(0)); 64 | assert_eq!(bits.read_u8(), Some(0)); 65 | assert_eq!(bits.read_u8(), Some(0)); 66 | assert_eq!(bits.read_u8(), Some(0)); 67 | assert_eq!(bits.read_u8(), Some(0)); 68 | assert_eq!(bits.read_u8(), Some(0)); 69 | 70 | assert!(bits.is_empty()); 71 | } 72 | 73 | #[test] 74 | fn remaining_bits_le() { 75 | let data: &[u8] = &[0, 0, 0, 0, 0, 0, 0, 0, 0]; 76 | let mut lebits = LittleEndianReader::new(data); 77 | assert!(lebits.has_bits_remaining(65)); 78 | for _ in 0..65 { 79 | assert!(!lebits.read_bit().unwrap()); 80 | } 81 | } 82 | 83 | #[test] 84 | fn has_bits_remaining_bit_reads_test_case() { 85 | let data = &[0, 0, 0, 0, 0, 0, 0, 0]; 86 | let mut bits = LittleEndianReader::new(data); 87 | assert!((1..128).all(|_x| bits.has_bits_remaining(1) == bits.read_bit().is_some())) 88 | } 89 | --------------------------------------------------------------------------------