diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/rust/bitreader | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/bitreader')
-rw-r--r-- | third_party/rust/bitreader/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | third_party/rust/bitreader/Cargo.toml | 27 | ||||
-rw-r--r-- | third_party/rust/bitreader/LICENSE-APACHE | 202 | ||||
-rw-r--r-- | third_party/rust/bitreader/LICENSE-MIT | 25 | ||||
-rw-r--r-- | third_party/rust/bitreader/README.md | 27 | ||||
-rw-r--r-- | third_party/rust/bitreader/src/lib.rs | 472 | ||||
-rw-r--r-- | third_party/rust/bitreader/src/tests.rs | 316 |
7 files changed, 1070 insertions, 0 deletions
diff --git a/third_party/rust/bitreader/.cargo-checksum.json b/third_party/rust/bitreader/.cargo-checksum.json new file mode 100644 index 0000000000..775d3a9a7e --- /dev/null +++ b/third_party/rust/bitreader/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"40cf98620ffea71407e2eee1026313c8e2576598c46186d8e7ba00279b5543a6","LICENSE-APACHE":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","LICENSE-MIT":"8583712ee2b062ff3d4d6d3e16f19ff0f92bc3a0a4beeec11a81ef00146fbd4f","README.md":"28986de2e8d457e76ae3303d80094697e6ef4ad8da06a4a3178bb1b52bff63d5","src/lib.rs":"6e9c5c337f2503dbd6f505a68bd07386da0421d18f757fab0eec8fd4de29b927","src/tests.rs":"bffb5a76bdc03845a51541b005d23f19a685846cb8311d13777ee71e0b86c56e"},"package":"d84ea71c85d1fe98fe67a9b9988b1695bc24c0b0d3bfb18d4c510f44b4b09941"}
\ No newline at end of file diff --git a/third_party/rust/bitreader/Cargo.toml b/third_party/rust/bitreader/Cargo.toml new file mode 100644 index 0000000000..e2944be4c7 --- /dev/null +++ b/third_party/rust/bitreader/Cargo.toml @@ -0,0 +1,27 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +name = "bitreader" +version = "0.3.6" +authors = ["Ilkka Rauta <ilkka.rauta@gmail.com>"] +description = "BitReader helps reading individual bits from a slice of bytes.\n\nYou can read \"unusual\" numbers of bits from the byte slice, for example 13 bits\nat once. The reader internally keeps track of position within the buffer.\n" +homepage = "https://github.com/irauta/bitreader" +documentation = "https://docs.rs/bitreader" +keywords = ["bit", "bits", "bitstream"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/irauta/bitreader" +[dependencies.cfg-if] +version = "1" + +[features] +default = ["std"] +std = [] diff --git a/third_party/rust/bitreader/LICENSE-APACHE b/third_party/rust/bitreader/LICENSE-APACHE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/third_party/rust/bitreader/LICENSE-APACHE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/third_party/rust/bitreader/LICENSE-MIT b/third_party/rust/bitreader/LICENSE-MIT new file mode 100644 index 0000000000..f8bbc4db18 --- /dev/null +++ b/third_party/rust/bitreader/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2015 Ilkka Rauta + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/third_party/rust/bitreader/README.md b/third_party/rust/bitreader/README.md new file mode 100644 index 0000000000..640071bf23 --- /dev/null +++ b/third_party/rust/bitreader/README.md @@ -0,0 +1,27 @@ +# BitReader + +BitReader is a helper type to extract strings of bits from a slice of bytes. + +[![Published Package](https://img.shields.io/crates/v/bitreader.svg)](https://crates.io/crates/bitreader) +[![Documentation](https://docs.rs/bitreader/badge.svg)](https://docs.rs/bitreader) +[![Build Status](https://travis-ci.org/irauta/bitreader.svg?branch=master)](https://travis-ci.org/irauta/bitreader) + +Here is how you read first a single bit, then three bits and finally four bits from a byte buffer: + + use bitreader::BitReader; + + let slice_of_u8 = &[0b1000_1111]; + let mut reader = BitReader::new(slice_of_u8); + + // You obviously should use try! or some other error handling mechanism here + let a_single_bit = reader.read_u8(1).unwrap(); // 1 + let more_bits = reader.read_u8(3).unwrap(); // 0 + let last_bits_of_byte = reader.read_u8(4).unwrap(); // 0b1111 + +You can naturally read bits from longer buffer of data than just a single byte. + +As you read bits, the internal cursor of BitReader moves on along the stream of bits. Big endian format is assumed when reading the multi-byte values. BitReader supports reading maximum of 64 bits at a time (with read_u64). + +## License + +Licensed under the Apache License, Version 2.0 or the MIT license, at your option. diff --git a/third_party/rust/bitreader/src/lib.rs b/third_party/rust/bitreader/src/lib.rs new file mode 100644 index 0000000000..6c85391761 --- /dev/null +++ b/third_party/rust/bitreader/src/lib.rs @@ -0,0 +1,472 @@ +// Copyright 2015 Ilkka Rauta +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! BitReader is a helper type to extract strings of bits from a slice of bytes. +//! +//! Here is how you read first a single bit, then three bits and finally four bits from a byte +//! buffer: +//! +//! ``` +//! use bitreader::BitReader; +//! +//! let slice_of_u8 = &[0b1000_1111]; +//! let mut reader = BitReader::new(slice_of_u8); +//! +//! // You probably should use try! or some other error handling mechanism in real code if the +//! // length of the input is not known in advance. +//! let a_single_bit = reader.read_u8(1).unwrap(); +//! assert_eq!(a_single_bit, 1); +//! +//! let more_bits = reader.read_u8(3).unwrap(); +//! assert_eq!(more_bits, 0); +//! +//! let last_bits_of_byte = reader.read_u8(4).unwrap(); +//! assert_eq!(last_bits_of_byte, 0b1111); +//! ``` +//! You can naturally read bits from longer buffer of data than just a single byte. +//! +//! As you read bits, the internal cursor of BitReader moves on along the stream of bits. Big +//! endian format is assumed when reading the multi-byte values. BitReader supports reading maximum +//! of 64 bits at a time (with read_u64). Reading signed values directly is not supported at the +//! moment. +//! +//! The reads do not need to be aligned in any particular way. +//! +//! Reading zero bits is a no-op. +//! +//! You can also skip over a number of bits, in which case there is no arbitrary small limits like +//! when reading the values to a variable. However, you can not seek past the end of the slice, +//! either when reading or when skipping bits. +//! +//! Note that the code will likely not work correctly if the slice is longer than 2^61 bytes, but +//! exceeding that should be pretty unlikely. Let's get back to this when people read exabytes of +//! information one bit at a time. +#![no_std] +cfg_if::cfg_if!{ + if #[cfg(feature = "std")] { + extern crate std; + use std::cmp::min; + use std::prelude::v1::*; + use std::fmt; + use std::error::Error; + use std::result; + } else { + use core::result; + use core::fmt; + use core::cmp::min; + } +} + +#[cfg(test)] +mod tests; + +/// BitReader reads data from a byte slice at the granularity of a single bit. +pub struct BitReader<'a> { + bytes: &'a [u8], + /// Position from the start of the slice, counted as bits instead of bytes + position: u64, + relative_offset: u64, + + /// Length this reader is allowed to read from the slice, counted as bits instead of bytes. + length: u64, +} + +impl<'a> BitReader<'a> { + /// Construct a new BitReader from a byte slice. The returned reader lives at most as long as + /// the slice given to is valid. + pub fn new(bytes: &'a [u8]) -> BitReader<'a> { + BitReader { + bytes: bytes, + position: 0, + relative_offset: 0, + length: bytes.len() as u64 * 8, + } + } + + /// Returns a copy of current BitReader, with the difference that its position() returns + /// positions relative to the position of the original BitReader at the construction time. + /// After construction, both readers are otherwise completely independent, except of course + /// for sharing the same source data. + /// + /// ``` + /// use bitreader::BitReader; + /// + /// let bytes = &[0b11110000, 0b00001111]; + /// let mut original = BitReader::new(bytes); + /// assert_eq!(original.read_u8(4).unwrap(), 0b1111); + /// assert_eq!(original.position(), 4); + /// + /// let mut relative = original.relative_reader(); + /// assert_eq!(relative.position(), 0); + /// + /// assert_eq!(original.read_u8(8).unwrap(), 0); + /// assert_eq!(relative.read_u8(8).unwrap(), 0); + /// + /// assert_eq!(original.position(), 12); + /// assert_eq!(relative.position(), 8); + /// ``` + pub fn relative_reader(&self) -> BitReader<'a> { + BitReader { + bytes: self.bytes, + position: self.position, + relative_offset: self.position, + length: self.length - self.position, + } + } + + /// Returns a copy of current BitReader, with the difference that its position() returns + /// positions relative to the position of the original BitReader at the construction time, and + /// will not allow reading more than len bits. After construction, both readers are otherwise + // completely independent, except of course for sharing the same source data. + /// + /// ``` + /// use bitreader::BitReader; + /// use bitreader::BitReaderError; + /// + /// let bytes = &[0b11110000, 0b00001111]; + /// let mut original = BitReader::new(bytes); + /// assert_eq!(original.read_u8(4).unwrap(), 0b1111); + /// assert_eq!(original.position(), 4); + /// + /// let mut relative = original.relative_reader_atmost(8); + /// assert_eq!(relative.position(), 0); + /// + /// assert_eq!(original.read_u8(8).unwrap(), 0); + /// assert_eq!(relative.read_u8(8).unwrap(), 0); + /// + /// assert_eq!(original.position(), 12); + /// assert_eq!(relative.position(), 8); + /// + /// assert_eq!(relative.read_u8(8).unwrap_err(), BitReaderError::NotEnoughData{ + /// position: 8, + /// length: 8, + /// requested: 8 + /// }); + /// ``` + pub fn relative_reader_atmost(&self, len: u64) -> BitReader<'a> { + BitReader { + bytes: self.bytes, + position: self.position, + relative_offset: self.position, + length: min(self.length - self.position, len), + } + } + + /// Read at most 8 bits into a u8. + pub fn read_u8(&mut self, bit_count: u8) -> Result<u8> { + let value = self.read_value(bit_count, 8)?; + Ok((value & 0xff) as u8) + } + + /// Read at most 8 bits into a u8, but without moving the cursor forward. + pub fn peek_u8(&self, bit_count: u8) -> Result<u8> { + self.relative_reader().read_u8(bit_count) + } + + /// Fills the entire `output_bytes` slice. If there aren't enough bits remaining + /// after the internal cursor's current position, the cursor won't be moved forward + /// and the contents of `output_bytes` won't be modified. + pub fn read_u8_slice(&mut self, output_bytes: &mut [u8]) -> Result<()> { + let requested = output_bytes.len() as u64 * 8; + if requested > self.remaining() { + Err(BitReaderError::NotEnoughData { + position: self.position(), + length: self.length, + requested, + }) + } else { + for byte in output_bytes.iter_mut() { + *byte = self.read_u8(8)?; + } + Ok(()) + } + } + + /// Read at most 16 bits into a u16. + pub fn read_u16(&mut self, bit_count: u8) -> Result<u16> { + let value = self.read_value(bit_count, 16)?; + Ok((value & 0xffff) as u16) + } + + /// Read at most 16 bits into a u16, but without moving the cursor forward. + pub fn peek_u16(&self, bit_count: u8) -> Result<u16> { + self.relative_reader().read_u16(bit_count) + } + + /// Read at most 32 bits into a u32. + pub fn read_u32(&mut self, bit_count: u8) -> Result<u32> { + let value = self.read_value(bit_count, 32)?; + Ok((value & 0xffffffff) as u32) + } + + /// Read at most 32 bits into a u32, but without moving the cursor forward. + pub fn peek_u32(&self, bit_count: u8) -> Result<u32> { + self.relative_reader().read_u32(bit_count) + } + + /// Read at most 64 bits into a u64. + pub fn read_u64(&mut self, bit_count: u8) -> Result<u64> { + let value = self.read_value(bit_count, 64)?; + Ok(value) + } + + /// Read at most 64 bits into a u64, but without moving the cursor forward. + pub fn peek_u64(&self, bit_count: u8) -> Result<u64> { + self.relative_reader().read_u64(bit_count) + } + + /// Read at most 8 bits into a i8. + /// Assumes the bits are stored in two's complement format. + pub fn read_i8(&mut self, bit_count: u8) -> Result<i8> { + let value = self.read_signed_value(bit_count, 8)?; + Ok((value & 0xff) as i8) + } + + /// Read at most 16 bits into a i16. + /// Assumes the bits are stored in two's complement format. + pub fn read_i16(&mut self, bit_count: u8) -> Result<i16> { + let value = self.read_signed_value(bit_count, 16)?; + Ok((value & 0xffff) as i16) + } + + /// Read at most 32 bits into a i32. + /// Assumes the bits are stored in two's complement format. + pub fn read_i32(&mut self, bit_count: u8) -> Result<i32> { + let value = self.read_signed_value(bit_count, 32)?; + Ok((value & 0xffffffff) as i32) + } + + /// Read at most 64 bits into a i64. + /// Assumes the bits are stored in two's complement format. + pub fn read_i64(&mut self, bit_count: u8) -> Result<i64> { + let value = self.read_signed_value(bit_count, 64)?; + Ok(value) + } + + /// Read a single bit as a boolean value. + /// Interprets 1 as true and 0 as false. + pub fn read_bool(&mut self) -> Result<bool> { + match self.read_value(1, 1)? { + 0 => Ok(false), + _ => Ok(true), + } + } + + /// Read a single bit as a boolean value, but without moving the cursor forward. + /// Interprets 1 as true and 0 as false. + pub fn peek_bool(&self) -> Result<bool> { + self.relative_reader().read_bool() + } + + /// Skip arbitrary number of bits. However, you can skip at most to the end of the byte slice. + pub fn skip(&mut self, bit_count: u64) -> Result<()> { + let end_position = self.position + bit_count; + if end_position > (self.relative_offset + self.length) { + return Err(BitReaderError::NotEnoughData { + position: self.position(), + length: self.length, + requested: bit_count, + }); + } + self.position = end_position; + Ok(()) + } + + /// Returns the position of the cursor, or how many bits have been read so far. + pub fn position(&self) -> u64 { + self.position - self.relative_offset + } + + /// Returns the number of bits not yet read from the underlying slice. + pub fn remaining(&self) -> u64 { + self.length - self.position + } + + /// Helper to make sure the "bit cursor" is exactly at the beginning of a byte, or at specific + /// multi-byte alignment position. + /// + /// For example `reader.is_aligned(1)` returns true if exactly n bytes, or n * 8 bits, has been + /// read. Similarly, `reader.is_aligned(4)` returns true if exactly n * 32 bits, or n 4-byte + /// sequences has been read. + /// + /// This function can be used to validate the data is being read properly, for example by + /// adding invocations wrapped into `debug_assert!()` to places where it is known the data + /// should be n-byte aligned. + pub fn is_aligned(&self, alignment_bytes: u32) -> bool { + self.position % (alignment_bytes as u64 * 8) == 0 + } + + /// Helper to move the "bit cursor" to exactly the beginning of a byte, or to a specific + /// multi-byte alignment position. + /// + /// That is, `reader.align(n)` moves the cursor to the next position that + /// is a multiple of n * 8 bits, if it's not correctly aligned already. + pub fn align(&mut self, alignment_bytes: u32) -> Result<()> { + let alignment_bits = alignment_bytes as u64 * 8; + let cur_alignment = self.position % alignment_bits; + let bits_to_skip = (alignment_bits - cur_alignment) % alignment_bits; + self.skip(bits_to_skip) + } + + fn read_signed_value(&mut self, bit_count: u8, maximum_count: u8) -> Result<i64> { + if bit_count == 0 { + return Ok(0); + } + let unsigned = self.read_value(bit_count, maximum_count)?; + // Fill the bits above the requested bits with all ones or all zeros, + // depending on the sign bit. + let sign_bit = unsigned >> (bit_count - 1) & 1; + let high_bits = if sign_bit == 1 { -1 } else { 0 }; + Ok(high_bits << bit_count | unsigned as i64) + } + + fn read_value(&mut self, bit_count: u8, maximum_count: u8) -> Result<u64> { + if bit_count == 0 { + return Ok(0); + } + if bit_count > maximum_count { + return Err(BitReaderError::TooManyBitsForType { + position: self.position, + requested: bit_count, + allowed: maximum_count, + }); + } + let start_position = self.position; + let end_position = self.position + bit_count as u64; + if end_position > (self.relative_offset + self.length) { + return Err(BitReaderError::NotEnoughData { + position: self.position(), + length: self.length, + requested: bit_count as u64, + }); + } + + let mut value: u64 = 0; + + for i in start_position..end_position { + let byte_index = (i / 8) as usize; + let byte = self.bytes[byte_index]; + let shift = 7 - (i % 8); + let bit = (byte >> shift) as u64 & 1; + value = (value << 1) | bit; + } + + self.position = end_position; + Ok(value) + } +} + +/// Result type for those BitReader operations that can fail. +pub type Result<T> = result::Result<T, BitReaderError>; + +/// Error enumeration of BitReader errors. +#[derive(Debug,PartialEq,Copy,Clone)] +pub enum BitReaderError { + /// Requested more bits than there are left in the byte slice at the current position. + NotEnoughData { + /// Current posititon in bits relative to the beginning of the reader. + position: u64, + + /// Total readable length in bits of the underlaying slice. + length: u64, + + /// Bits requested to be read. + requested: u64, + }, + /// Requested more bits than the returned variable can hold, for example more than 8 bits when + /// reading into a u8. + TooManyBitsForType { + position: u64, + requested: u8, + allowed: u8, + } +} + +#[cfg(feature = "std")] +impl Error for BitReaderError { + fn description(&self) -> &str { + match *self { + BitReaderError::NotEnoughData {..} => "Requested more bits than the byte slice has left", + BitReaderError::TooManyBitsForType {..} => "Requested more bits than the requested integer type can hold", + } + } +} + +impl fmt::Display for BitReaderError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + //self.description().fmt(fmt) + match *self { + BitReaderError::NotEnoughData { position, length, requested } => write!(fmt, "BitReader: Requested {} bits with only {}/{} bits left (position {})", requested, length - position, length, position), + BitReaderError::TooManyBitsForType { position, requested, allowed } => write!(fmt, "BitReader: Requested {} bits while the type can only hold {} (position {})", requested, allowed, position), + } + } +} + +/// Helper trait to allow reading bits into a variable without explicitly mentioning its type. +/// +/// If you can't or want, for some reason, to use BitReader's read methods (`read_u8` etc.) but +/// want to rely on type inference instead, you can use the ReadInto trait. The trait is +/// implemented for all basic integer types (8/16/32/64 bits, signed/unsigned) +/// and the boolean type. +/// +/// ``` +/// use bitreader::{BitReader,ReadInto}; +/// +/// let slice_of_u8 = &[0b1110_0000]; +/// let mut reader = BitReader::new(slice_of_u8); +/// +/// struct Foo { +/// bar: u8, +/// valid: bool, +/// } +/// +/// // No type mentioned here, instead the type of bits is inferred from the type of Foo::bar, +/// // and consequently the correct "overload" is used. +/// let bits = ReadInto::read(&mut reader, 2).unwrap(); +/// let valid = ReadInto::read(&mut reader, 1).unwrap(); +/// +/// let foo = Foo { bar: bits, valid: valid }; +/// assert_eq!(foo.bar, 3); +/// assert!(foo.valid); +/// ``` +pub trait ReadInto + where Self: Sized +{ + fn read(reader: &mut BitReader, bits: u8) -> Result<Self>; +} + +// There's eight almost identical implementations, let's make this easier. +macro_rules! impl_read_into { + ($T:ty, $method:ident) => ( + impl ReadInto for $T { + fn read(reader: &mut BitReader, bits: u8) -> Result<Self> { + reader.$method(bits) + } + } + ) +} + +impl_read_into!(u8, read_u8); +impl_read_into!(u16, read_u16); +impl_read_into!(u32, read_u32); +impl_read_into!(u64, read_u64); + +impl_read_into!(i8, read_i8); +impl_read_into!(i16, read_i16); +impl_read_into!(i32, read_i32); +impl_read_into!(i64, read_i64); + +// We can't cast to bool, so this requires a separate method. +impl ReadInto for bool { + fn read(reader: &mut BitReader, bits: u8) -> Result<Self> { + match reader.read_u8(bits)? { + 0 => Ok(false), + _ => Ok(true), + } + } +} diff --git a/third_party/rust/bitreader/src/tests.rs b/third_party/rust/bitreader/src/tests.rs new file mode 100644 index 0000000000..f91a23b3f7 --- /dev/null +++ b/third_party/rust/bitreader/src/tests.rs @@ -0,0 +1,316 @@ +// Copyright 2015 Ilkka Rauta +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::*; + +#[test] +fn read_buffer() { + let bytes = &[ + 0b1011_0101, 0b0110_1010, 0b1010_1100, 0b1001_1001, + 0b1001_1001, 0b1001_1001, 0b1001_1001, 0b1110_0111, + ]; + + let mut reader = BitReader::new(bytes); + + assert_eq!(reader.read_u8(1).unwrap(), 0b1); + assert_eq!(reader.peek_u8(3).unwrap(), 0b011); + assert_eq!(reader.read_u8(1).unwrap(), 0b0); + assert_eq!(reader.read_u8(2).unwrap(), 0b11); + + assert!(!reader.is_aligned(1)); + assert!(!reader.is_aligned(2)); + assert!(!reader.is_aligned(4)); + + assert_eq!(reader.position(), 4); + assert_eq!(reader.remaining(), 60); + + assert_eq!(reader.read_u8(4).unwrap(), 0b0101); + + assert!(reader.is_aligned(1)); + assert!(!reader.is_aligned(2)); + assert!(!reader.is_aligned(4)); + + assert_eq!(reader.align(1), Ok(())); // shouldn't do anything if already aligned + + assert_eq!(reader.peek_u64(16).unwrap(), 0b110_1010_1010_1100); + assert_eq!(reader.read_u8(3).unwrap(), 0b11); + assert_eq!(reader.peek_u16(13).unwrap(), 0b1010_1010_1100); + assert_eq!(reader.peek_u32(13).unwrap(), 0b1010_1010_1100); + assert_eq!(reader.peek_u64(13).unwrap(), 0b1010_1010_1100); + assert_eq!(reader.peek_u16(10).unwrap(), 0b01_0101_0101); + assert_eq!(reader.peek_u8(8).unwrap(), 0b0101_0101); + assert_eq!(reader.read_u16(10).unwrap(), 0b01_0101_0101); + assert_eq!(reader.read_u8(3).unwrap(), 0b100); + + assert_eq!(reader.position(), 24); + assert_eq!(reader.remaining(), 40); + + assert!(reader.is_aligned(1)); + + assert_eq!(reader.read_u32(32).unwrap(), 0b1001_1001_1001_1001_1001_1001_1001_1001); + + assert_eq!(reader.peek_bool().unwrap(), true); + assert_eq!(reader.read_u8(4).unwrap(), 0b1110); + assert_eq!(reader.peek_bool().unwrap(), false); + assert_eq!(reader.read_u8(3).unwrap(), 0b011); + assert_eq!(reader.peek_bool().unwrap(), true); + assert_eq!(reader.read_bool().unwrap(), true); + + // Could also be 8 at this point! + assert!(reader.is_aligned(4)); + + // shouldn't do anything if already aligned + assert_eq!(reader.align(1), Ok(())); + assert_eq!(reader.align(2), Ok(())); + assert_eq!(reader.align(4), Ok(())); + assert_eq!(reader.align(8), Ok(())); + + // Start over to test align() + let mut reader = BitReader::new(bytes); + + // shouldn't do anything if already aligned + assert_eq!(reader.align(1), Ok(())); + assert_eq!(reader.align(2), Ok(())); + assert_eq!(reader.align(4), Ok(())); + assert_eq!(reader.align(8), Ok(())); + assert_eq!(reader.position(), 0); + + assert_eq!(reader.read_u8(1).unwrap(), 0b1); + + assert_eq!(reader.align(1), Ok(())); + assert_eq!(reader.position(), 8); + + assert!(reader.is_aligned(1)); + assert!(!reader.is_aligned(2)); + assert!(!reader.is_aligned(4)); + + assert_eq!(reader.align(2), Ok(())); + assert_eq!(reader.position(), 16); + assert!(reader.is_aligned(1)); + assert!(reader.is_aligned(2)); + assert!(!reader.is_aligned(4)); + + assert_eq!(reader.read_u8(7).unwrap(), 0b0101_0110); + assert_eq!(reader.align(4), Ok(())); + assert_eq!(reader.position(), 32); + assert!(reader.is_aligned(1)); + assert!(reader.is_aligned(2)); + assert!(reader.is_aligned(4)); + + let mut reader = BitReader::new(bytes); + assert_eq!(reader.position(), 0); + assert_eq!(reader.skip(1), Ok(())); + assert_eq!(reader.align(4), Ok(())); + assert_eq!(reader.position(), 32); + assert_eq!(reader.skip(7), Ok(())); + assert_eq!(reader.align(1), Ok(())); + assert_eq!(reader.position(), 40); + assert_eq!(reader.align(2), Ok(())); + assert_eq!(reader.position(), 48); + assert_eq!(reader.skip(5), Ok(())); + assert_eq!(reader.align(2), Ok(())); + assert_eq!(reader.position(), 64); + + let mut reader = BitReader::new(bytes); + assert_eq!(reader.skip(1), Ok(())); + assert_eq!(reader.align(3), Ok(())); + assert_eq!(reader.position(), 24); + + assert!(!reader.align(128).is_ok()); +} + +#[test] +fn try_all_sizes() { + let bytes = &[ + 0x4a, 0x1e, 0x39, 0xbb, 0xd0, 0x07, 0xca, 0x9a, + 0xa6, 0xba, 0x25, 0x52, 0x6f, 0x0a, 0x6a, 0xba, + ]; + + let mut reader = BitReader::new(bytes); + assert_eq!(reader.read_u64(64).unwrap(), 0x4a1e39bbd007ca9a); + assert_eq!(reader.read_u64(64).unwrap(), 0xa6ba25526f0a6aba); + + let mut reader = BitReader::new(bytes); + assert_eq!(reader.read_u32(32).unwrap(), 0x4a1e39bb); + assert_eq!(reader.read_u32(32).unwrap(), 0xd007ca9a); + assert_eq!(reader.read_u32(32).unwrap(), 0xa6ba2552); + assert_eq!(reader.read_u32(32).unwrap(), 0x6f0a6aba); + + let mut reader = BitReader::new(bytes); + assert_eq!(reader.read_u16(16).unwrap(), 0x4a1e); + assert_eq!(reader.read_u16(16).unwrap(), 0x39bb); + assert_eq!(reader.read_u16(16).unwrap(), 0xd007); + assert_eq!(reader.read_u16(16).unwrap(), 0xca9a); + assert_eq!(reader.read_u16(16).unwrap(), 0xa6ba); + assert_eq!(reader.read_u16(16).unwrap(), 0x2552); + assert_eq!(reader.read_u16(16).unwrap(), 0x6f0a); + assert_eq!(reader.read_u16(16).unwrap(), 0x6aba); + + let mut reader = BitReader::new(&bytes[..]); + for byte in bytes { + assert_eq!(reader.read_u8(8).unwrap(), *byte); + } +} + +#[test] +fn skipping_and_zero_reads() { + let bytes = &[ + 0b1011_0101, 0b1110_1010, 0b1010_1100, 0b0011_0101, + ]; + + let mut reader = BitReader::new(bytes); + + assert_eq!(reader.read_u8(4).unwrap(), 0b1011); + // Reading zero bits should be a no-op + assert_eq!(reader.read_u8(0).unwrap(), 0b0); + assert_eq!(reader.read_i8(0).unwrap(), 0b0); + assert_eq!(reader.read_u8(4).unwrap(), 0b0101); + reader.skip(3).unwrap(); // 0b111 + assert_eq!(reader.read_u16(10).unwrap(), 0b0101010101); + assert_eq!(reader.read_u8(3).unwrap(), 0b100); + reader.skip(4).unwrap(); // 0b0011 + assert_eq!(reader.read_u32(2).unwrap(), 0b01); + assert_eq!(reader.read_bool().unwrap(), false); + assert_eq!(reader.read_bool().unwrap(), true); +} + +#[test] +fn errors() { + let bytes = &[ + 0b1011_0101, 0b1110_1010, 0b1010_1100, + ]; + + let mut reader = BitReader::new(bytes); + assert_eq!(reader.read_u8(4).unwrap(), 0b1011); + assert_eq!(reader.read_u8(9).unwrap_err(), BitReaderError::TooManyBitsForType { + position: 4, + requested: 9, + allowed: 8 + }); + // If an error happens, it should be possible to resume as if nothing had happened + assert_eq!(reader.read_u8(4).unwrap(), 0b0101); + + let mut reader = BitReader::new(bytes); + assert_eq!(reader.read_u8(4).unwrap(), 0b1011); + // Same with this error + assert_eq!(reader.read_u32(21).unwrap_err(), BitReaderError::NotEnoughData { + position: 4, + length: (bytes.len() * 8) as u64, + requested: 21 + }); + assert_eq!(reader.read_u8(4).unwrap(), 0b0101); +} + +#[test] +fn signed_values() { + let from = -2048; + let to = 2048; + for x in from..to { + let bytes = &[ + (x >> 8) as u8, + x as u8, + ]; + let mut reader = BitReader::new(bytes); + assert_eq!(reader.read_u8(4).unwrap(), if x < 0 { 0b1111 } else { 0 }); + assert_eq!(reader.read_i16(12).unwrap(), x); + } +} + +#[test] +fn boolean_values() { + let bytes: Vec<u8> = (0..16).collect(); + let mut reader = BitReader::new(&bytes); + for v in &bytes { + assert_eq!(reader.read_bool().unwrap(), false); + reader.skip(3).unwrap(); + assert_eq!(reader.read_bool().unwrap(), v & 0x08 == 8); + assert_eq!(reader.read_bool().unwrap(), v & 0x04 == 4); + assert_eq!(reader.read_bool().unwrap(), v & 0x02 == 2); + assert_eq!(reader.read_bool().unwrap(), v & 0x01 == 1); + } +} + +#[test] +fn read_slice() { + let bytes = &[ + 0b1111_0000, 0b0000_1111, 0b1111_0000, + 0b0000_1000, 0b0000_0100, 0b0000_0011, + 0b1111_1100, 0b0000_0011, 0b1101_1000, + ]; + let mut reader = BitReader::new(bytes); + assert_eq!(reader.read_u8(4).unwrap(), 0b1111); + // Just some pattern that's definitely not in the bytes array + let mut output = [0b1010_1101; 3]; + reader.read_u8_slice(&mut output).unwrap(); + assert_eq!(&output, &[0u8, 255u8, 0u8]); + + assert_eq!(reader.read_u8(1).unwrap(), 1); + + reader.read_u8_slice(&mut output[1..2]).unwrap(); + assert_eq!(&output, &[0u8, 0u8, 0u8]); + + assert_eq!(reader.read_u8(1).unwrap(), 1); + + output = [0b1010_1101; 3]; + reader.read_u8_slice(&mut output).unwrap(); + assert_eq!(&output, &[0u8, 255u8, 0u8]); + + reader.read_u8_slice(&mut output[0..1]).unwrap(); + assert_eq!(output[0], 0b1111_0110); + + assert_eq!(reader.read_u8(2).unwrap(), 0); +} + +#[test] +fn read_slice_too_much() { + let bytes = &[ + 0b1111_1111, 0b1111_1111, 0b1111_1111, 0b1111_1111, + ]; + let mut reader = BitReader::new(bytes); + assert_eq!(reader.read_u8(1).unwrap(), 1); + + let mut output = [0u8; 4]; + let should_be_error = reader.read_u8_slice(&mut output); + assert_eq!(should_be_error.unwrap_err(), BitReaderError::NotEnoughData { + position: 1, + length: (bytes.len() * 8) as u64, + requested: (&output.len() * 8) as u64 + }); + assert_eq!(&output, &[0u8; 4]); +} + +#[test] +fn relative_reader() { + let bytes = &[ + 0b0001_0010, 0b0011_0100, + ]; + let mut reader = BitReader::new(bytes); + assert_eq!(reader.read_u8(4).unwrap(), 0b0001); + + let mut relative_reader = reader.relative_reader(); + + assert_eq!(reader.read_u8(4).unwrap(), 0b0010); + assert_eq!(reader.read_u8(4).unwrap(), 0b0011); + assert_eq!(reader.read_u8(4).unwrap(), 0b0100); + + assert_eq!(reader.read_u8(1).unwrap_err(), BitReaderError::NotEnoughData { + position: 16, + length: 16, + requested: 1 + }); + + assert_eq!(relative_reader.read_u8(4).unwrap(), 0b0010); + assert_eq!(relative_reader.read_u8(4).unwrap(), 0b0011); + assert_eq!(relative_reader.read_u8(4).unwrap(), 0b0100); + + assert_eq!(relative_reader.read_u8(1).unwrap_err(), BitReaderError::NotEnoughData { + position: 12, + length: 12, + requested: 1 + }); +} |