• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

vigna / dsi-bitstream-rs / 29740807880

20 Jul 2026 11:52AM UTC coverage: 64.573% (+6.3%) from 58.294%
29740807880

push

github

zommiommy
fix(reader): align the peek_bits contract and assertions with PEEK_BITS

The BitRead::peek_bits doc said n may be as large as PeekWord::BITS, and
both BufBitReader implementations debug_assert!-ed that same loose bound,
but PEEK_BITS is one word and peek_bits performs at most one refill: for a
u32-word reader with an empty buffer, a documented peek_bits(64) loads only
32 bits (failing the availability assertion in debug builds and returning
an invalid partial value in release builds). The guarantee is PEEK_BITS,
so the trait doc, the stale BufBitReader doc paragraph (which still
advertised word size plus one), and the entry assertions now all say
PEEK_BITS.

Tests cover both sides of the boundary: peek_bits(PEEK_BITS) from a
completely empty buffer must be served by a single refill for u16/u32/u64
words and both endiannesses, and peek_bits(PEEK_BITS + 1) panics in debug
builds.

2 of 2 new or added lines in 1 file covered. (100.0%)

300 existing lines in 9 files now uncovered.

2570 of 3980 relevant lines covered (64.57%)

2512407.34 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.44
/src/impls/buf_bit_reader.rs
1
/*
2
 * SPDX-FileCopyrightText: 2023 Tommaso Fontana
3
 * SPDX-FileCopyrightText: 2023 Inria
4
 * SPDX-FileCopyrightText: 2023 Sebastiano Vigna
5
 *
6
 * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
7
 */
8

9
use num_primitive::{PrimitiveInteger, PrimitiveNumber};
10

11
use crate::codes::params::{DefaultReadParams, ReadParams};
12
use crate::traits::*;
13
#[cfg(feature = "mem_dbg")]
14
use mem_dbg::{MemDbg, MemSize};
15

16
/// An internal shortcut to the double type of the word of a
17
/// [`WordRead`].
18
type BB<WR> = <<WR as WordRead>::Word as DoubleType>::DoubleType;
19

20
/// An implementation of [`BitRead`] and [`BitSeek`] for a [`WordRead`] and a
21
/// [`WordSeek`].
22
///
23
/// This implementation uses a bit buffer to store bits that are not yet read.
24
/// The buffer is sized as twice the word size of the underlying [`WordRead`].
25
/// Typically, the best choice is to have a buffer that is sized as `usize`,
26
/// which means that the word of the underlying [`WordRead`] should be half of
27
/// that (i.e., `u32` for a 64-bit architecture). However, results will vary
28
/// depending on the CPU.
29
///
30
/// The peek word is equal to the bit buffer. The value returned
31
/// by [`peek_bits`](crate::traits::BitRead::peek_bits) contains at least as
32
/// many bits as the word size (extended with zeros beyond end of stream):
33
/// a peek is served by at most one refill, so only one word of peekable
34
/// bits can be guaranteed.
35
///
36
/// The convenience functions [`from_path`] and [`from_file`] (requiring the
37
/// `std` feature) create a [`BufBitReader`] around a buffered file reader.
38
///
39
/// This implementation is usually faster than
40
/// [`BitReader`](crate::impls::BitReader).
41
///
42
/// The additional type parameter `RP` is used to select the parameters for the
43
/// instantaneous codes, but the casual user should be happy with the default
44
/// value. See [`ReadParams`] for more details.
45
///
46
/// For additional flexibility, when the `std` feature is enabled, this
47
/// structure implements [`std::io::Read`]. Note that because of coherence
48
/// rules it is not possible to implement [`std::io::Read`] for a generic
49
/// [`BitRead`].
50

51
#[derive(Debug)]
52
#[cfg_attr(feature = "mem_dbg", derive(MemDbg, MemSize))]
53
pub struct BufBitReader<E: Endianness, WR: WordRead, RP: ReadParams = DefaultReadParams>
54
where
55
    WR::Word: DoubleType,
56
{
57
    /// The [`WordRead`] used to fill the buffer.
58
    backend: WR,
59
    /// The 2-word bit buffer that is used to read the codes. It is never full,
60
    /// but it may be empty. Only the upper (BE) or lower (LE)
61
    /// `bits_in_buffer` bits are valid; the other bits are always zeroes.
62
    buffer: BB<WR>,
63
    /// Number of valid upper (BE) or lower (LE) bits in the buffer.
64
    /// It is always smaller than `BB::<WR>::BITS`.
65
    bits_in_buffer: usize,
66
    _marker: core::marker::PhantomData<(E, RP)>,
67
}
68

69
/// Creates a new [`BufBitReader`] with [default read
70
/// parameters](`DefaultReadParams`) from a file path using the provided
71
/// endianness and read word.
72
///
73
/// # Examples
74
///
75
/// ```no_run
76
/// use dsi_bitstream::prelude::*;
77
/// let mut reader = buf_bit_reader::from_path::<LE, u32>("data.bin")?;
78
/// # Ok::<(), Box<dyn core::error::Error>>(())
79
/// ```
80
#[cfg(feature = "std")]
81
pub fn from_path<E: Endianness, W: Word + DoubleType>(
1✔
82
    path: impl AsRef<std::path::Path>,
83
) -> std::io::Result<
84
    BufBitReader<E, super::WordAdapter<W, std::io::BufReader<std::fs::File>>, DefaultReadParams>,
85
>
86
where
87
    W::Bytes: Default + AsMut<[u8]>,
88
{
89
    Ok(from_file::<E, W>(std::fs::File::open(path)?))
3✔
90
}
91

92
/// Creates a new [`BufBitReader`] with [default read
93
/// parameters](`DefaultReadParams`) from a file using the provided
94
/// endianness and read word.
95
///
96
/// See also [`from_path`] for a version that takes a path.
97
#[must_use]
98
#[cfg(feature = "std")]
99
pub fn from_file<E: Endianness, W: Word + DoubleType>(
1✔
100
    file: std::fs::File,
101
) -> BufBitReader<E, super::WordAdapter<W, std::io::BufReader<std::fs::File>>, DefaultReadParams>
102
where
103
    W::Bytes: Default + AsMut<[u8]>,
104
{
105
    BufBitReader::new(super::WordAdapter::new(std::io::BufReader::new(file)))
4✔
106
}
107

108
impl<E: Endianness, WR: WordRead + Clone, RP: ReadParams> core::clone::Clone
109
    for BufBitReader<E, WR, RP>
110
where
111
    WR::Word: DoubleType,
112
{
113
    fn clone(&self) -> Self {
×
114
        Self {
115
            backend: self.backend.clone(),
×
116
            buffer: self.buffer,
×
117
            bits_in_buffer: self.bits_in_buffer,
×
118
            _marker: core::marker::PhantomData,
119
        }
120
    }
121
}
122

123
impl<E: Endianness, WR: WordRead, RP: ReadParams> BufBitReader<E, WR, RP>
124
where
125
    WR::Word: DoubleType,
126
{
127
    const WORD_BITS: usize = WR::Word::BITS as usize;
128
    const BUFFER_BITS: usize = BB::<WR>::BITS as usize;
129

130
    /// Creates a new [`BufBitReader`] around a [`WordRead`].
131
    ///
132
    /// # Examples
133
    /// ```
134
    /// use dsi_bitstream::prelude::*;
135
    /// let words: [u32; 2] = [0x0043b59f, 0xccf16077];
136
    /// let word_reader = MemWordReader::new_inf(&words);
137
    /// let mut buf_bit_reader = <BufBitReader<BE, _>>::new(word_reader);
138
    /// ```
139
    #[must_use]
140
    pub const fn new(backend: WR) -> Self {
484,592✔
141
        Self {
142
            backend,
143
            buffer: BB::<WR>::ZERO,
144
            bits_in_buffer: 0,
145
            _marker: core::marker::PhantomData,
146
        }
147
    }
148

149
    /// Consumes this reader and returns the underlying [`WordRead`].
150
    #[must_use]
151
    pub fn into_inner(self) -> WR {
×
152
        self.backend
×
153
    }
154
}
155

156
//
157
// Big-endian implementation
158
//
159

160
impl<WR: WordRead, RP: ReadParams> BufBitReader<BE, WR, RP>
161
where
162
    WR::Word: DoubleType,
163
{
164
    /// Ensures that in the buffer there are at least `Self::WORD_BITS` bits to read.
165
    /// This method can be called only if there are at least
166
    /// `Self::WORD_BITS` free bits in the buffer.
167
    #[inline(always)]
168
    fn refill(&mut self) -> Result<(), <WR as WordRead>::Error> {
1,084,022✔
169
        debug_assert!(Self::BUFFER_BITS - self.bits_in_buffer >= Self::WORD_BITS);
2,168,044✔
170

171
        let new_word: BB<WR> = self.backend.read_word()?.to_be().as_double();
6,504,132✔
172
        self.bits_in_buffer += Self::WORD_BITS;
1,084,022✔
173
        self.buffer |= new_word << (Self::BUFFER_BITS - self.bits_in_buffer);
2,168,044✔
174
        Ok(())
1,084,022✔
175
    }
176
    /// Single-word refill for 64-bit words (128-bit buffer), specialized so
177
    /// that the result and the pre-top-up buffer, which both live entirely
178
    /// in the high 64 bits of the buffer, are computed in u64 arithmetic
179
    /// (no cross-half 128-bit shifts).
180
    ///
181
    /// Must be called only when `WORD_BITS == 64`, with `w` the word read
182
    /// for this request and `bits_in_buffer < num_bits <= WORD_BITS`.
183
    #[inline(always)]
184
    fn read_bits_refill_word64(
715,715✔
185
        &mut self,
186
        num_bits: usize,
187
        w: WR::Word,
188
    ) -> Result<u64, <WR as WordRead>::Error> {
189
        debug_assert!(Self::WORD_BITS == 64);
1,431,430✔
190
        let bits = self.bits_in_buffer;
1,431,430✔
191
        // High half of buffer | word placed right below the buffered bits.
192
        // Shifts valid: bits < num_bits <= WORD_BITS = 64, and num_bits >= 1
193
        // because the in-buffer fast path handled num_bits <= bits.
194
        let virt_hi: u64 = (self.buffer >> Self::WORD_BITS).as_to::<u64>() | (w.as_u64() >> bits);
4,294,290✔
195
        let result = virt_hi >> (Self::WORD_BITS - num_bits);
1,431,430✔
196
        // The remaining low WORD_BITS - (num_bits - bits) bits of the word,
197
        // placed at the top of the high half; double shift as
198
        // num_bits - bits may equal WORD_BITS.
199
        let hi = (w << (num_bits - bits - 1)) << 1_u32;
2,147,145✔
200
        let mut buffer = hi.as_double() << Self::WORD_BITS;
1,431,430✔
201
        let mut new_bits = bits + Self::WORD_BITS - num_bits;
1,431,430✔
202
        // Top up with a second word if available: new_bits < WORD_BITS here,
203
        // so there is always room and the buffer stays short of full. The
204
        // atomic optional read never consumes past the end of the stream.
205
        if let Some(w2) = self.backend.read_word_opt() {
2,147,145✔
206
            // Shifts valid: WORD_BITS and new_bits are < BUFFER_BITS.
207
            buffer |= (w2.to_be().as_double() << Self::WORD_BITS) >> new_bits;
1,431,430✔
208
            new_bits += Self::WORD_BITS;
715,715✔
209
        }
210
        self.buffer = buffer;
715,715✔
211
        self.bits_in_buffer = new_bits;
715,715✔
212
        Ok(result)
715,715✔
213
    }
214
}
215

216
impl<WR: WordRead, RP: ReadParams> BitRead<BE> for BufBitReader<BE, WR, RP>
217
where
218
    WR::Word: DoubleType,
219
{
220
    type Error = <WR as WordRead>::Error;
221
    type PeekWord = BB<WR>;
222
    // We guarantee only half a buffer (one word) of peekable bits, so that a
223
    // peek needs at most one refill and the buffer is never completely full;
224
    // this keeps the read/skip/unary hot paths free of full-buffer handling.
225
    const PEEK_BITS: usize = <WR as WordRead>::Word::BITS as usize;
226

227
    #[inline(always)]
228
    fn peek_bits(&mut self, n_bits: usize) -> Result<Self::PeekWord, Self::Error> {
6,016,671✔
229
        debug_assert!(n_bits > 0);
12,033,342✔
230
        debug_assert!(n_bits <= Self::PEEK_BITS);
12,033,342✔
231

232
        // A peek can do at most one refill, otherwise we might lose data
233
        if n_bits > self.bits_in_buffer {
6,016,670✔
234
            self.refill()?;
2,168,044✔
235
        }
236

237
        debug_assert!(n_bits <= self.bits_in_buffer);
12,033,340✔
238

239
        // Move the n_bits highest bits of the buffer to the lowest
240
        Ok(self.buffer >> (Self::BUFFER_BITS - n_bits))
6,016,670✔
241
    }
242

243
    #[inline(always)]
244
    fn skip_bits_after_peek(&mut self, n_bits: usize) {
6,010,822✔
245
        self.bits_in_buffer -= n_bits;
6,010,822✔
246
        self.buffer <<= n_bits;
6,010,822✔
247
    }
248

249
    #[inline]
250
    fn read_bits(&mut self, mut num_bits: usize) -> Result<u64, Self::Error> {
110,504,515✔
251
        debug_assert!(num_bits <= 64);
221,009,030✔
252
        debug_assert!(self.bits_in_buffer < Self::BUFFER_BITS);
221,009,030✔
253

254
        // most common path, we just read the buffer
255
        if num_bits <= self.bits_in_buffer {
110,504,515✔
256
            // Valid right shift of BB::<WR>::BITS - num_bits, even when num_bits is zero
257
            let result: u64 = (self.buffer >> (Self::BUFFER_BITS - num_bits - 1) >> 1_u32).as_to();
401,610,380✔
258
            self.bits_in_buffer -= num_bits;
100,402,595✔
259
            self.buffer <<= num_bits;
100,402,595✔
260
            return Ok(result);
100,402,595✔
261
        }
262
        // Single-word refill path: past the test above, a request of at most
263
        // WORD_BITS bits always consumes exactly one word, which we can
264
        // compose with the buffer without the loop and the branches of the
265
        // general path below.
266
        if num_bits <= Self::WORD_BITS {
10,101,920✔
267
            let bits = self.bits_in_buffer;
10,173,410✔
268
            // The word is required in any case, so no peek is needed.
269
            let w = self.backend.read_word()?.to_be();
20,346,818✔
270
            // For 64-bit words (128-bit buffer), the result and the
271
            // pre-top-up buffer both live entirely in the high 64 bits of
272
            // the buffer, so the specialized helper uses u64 arithmetic,
273
            // avoiding cross-half 128-bit shifts. The branch is resolved at
274
            // compile time; the helper keeps the dead branch's MIR footprint
275
            // to one statement so that inlining decisions for narrow-word
276
            // readers are unaffected.
277
            if Self::WORD_BITS == 64 {
5,086,703✔
278
                return self.read_bits_refill_word64(num_bits, w);
2,862,860✔
279
            }
280
            // Place the word right below the buffered bits; the word fits
281
            // entirely because bits < num_bits <= WORD_BITS, and both
282
            // shifts are valid as WORD_BITS and bits are < BUFFER_BITS.
283
            let placed = (w.as_double() << Self::WORD_BITS) >> bits;
8,741,976✔
284
            let virt = self.buffer | placed;
8,741,976✔
285
            // Valid right shift, even when num_bits is zero
286
            let result: u64 = (virt >> (Self::BUFFER_BITS - num_bits - 1) >> 1_u32).as_to();
17,483,952✔
287
            // The new buffer comes from the placed word alone: bits <
288
            // num_bits, so all buffered bits are consumed and
289
            // `self.buffer << num_bits` would be zero; dropping the `|` from
290
            // this computation shortens the loop-carried dependency chain.
291
            let mut buffer = placed << num_bits;
8,741,976✔
292
            let mut new_bits = bits + Self::WORD_BITS - num_bits;
8,741,976✔
293
            // Top up with a second word if available: new_bits < WORD_BITS
294
            // here, so there is always room and the buffer stays short of
295
            // full. This halves the number of refills, making the in-buffer
296
            // test above more predictable. The atomic optional read never
297
            // consumes past the end of the stream.
298
            if let Some(w2) = self.backend.read_word_opt() {
13,112,946✔
299
                // Shifts valid: WORD_BITS and new_bits are < BUFFER_BITS.
300
                buffer |= (w2.to_be().as_double() << Self::WORD_BITS) >> new_bits;
8,741,958✔
301
                new_bits += Self::WORD_BITS;
4,370,979✔
302
            }
303
            self.buffer = buffer;
4,370,988✔
304
            self.bits_in_buffer = new_bits;
4,370,988✔
305
            return Ok(result);
4,370,988✔
306
        }
307

308
        let mut result: u64 =
10,030,430✔
309
            (self.buffer >> (Self::BUFFER_BITS - 1 - self.bits_in_buffer) >> 1_u8).as_to();
15,045,645✔
310
        num_bits -= self.bits_in_buffer;
5,015,215✔
311

312
        // Directly read to the result without updating the buffer
313
        while num_bits > Self::WORD_BITS {
12,101,119✔
314
            let new_word: u64 = self.backend.read_word()?.to_be().as_u64();
42,515,424✔
315
            result = (result << Self::WORD_BITS) | new_word;
7,085,904✔
316
            num_bits -= Self::WORD_BITS;
7,085,904✔
317
        }
318

319
        debug_assert!(num_bits > 0);
10,030,430✔
320
        debug_assert!(num_bits <= Self::WORD_BITS);
10,030,430✔
321

322
        // get the final word
323
        let new_word = self.backend.read_word()?.to_be();
20,060,860✔
324
        self.bits_in_buffer = Self::WORD_BITS - num_bits;
5,015,215✔
325
        // compose the remaining bits
326
        let upcast: u64 = new_word.as_u64();
20,060,860✔
327
        let final_bits: u64 = upcast >> self.bits_in_buffer;
15,045,645✔
328
        result = (result << (num_bits - 1) << 1) | final_bits;
5,015,215✔
329
        // and put the rest in the buffer
330
        self.buffer = (new_word.as_double() << (Self::BUFFER_BITS - self.bits_in_buffer - 1)) << 1;
10,030,430✔
331

332
        Ok(result)
5,015,215✔
333
    }
334

335
    #[inline]
336
    fn read_unary(&mut self) -> Result<u64, Self::Error> {
24,115,304✔
337
        debug_assert!(self.bits_in_buffer < Self::BUFFER_BITS);
48,230,608✔
338

339
        // count the zeros from the left
340
        let zeros: usize = self.buffer.leading_zeros() as _;
96,461,216✔
341

342
        // if we encountered a 1 in the bits_in_buffer we can return
343
        if zeros < self.bits_in_buffer {
24,115,304✔
344
            // zeros + 1 <= bits_in_buffer < BUFFER_BITS, so both forms are
345
            // always valid. On 128-bit buffers a single merged shift avoids a
346
            // second synthesized wide shift (measured -13% on u64-word
347
            // read_unary); on 64-bit buffers the two-step shift measured
348
            // slightly faster, so each width keeps its best form (const-folded).
349
            if Self::BUFFER_BITS > 64 {
18,391,626✔
350
                self.buffer <<= zeros + 1;
2,330,231✔
351
            } else {
352
                self.buffer = self.buffer << zeros << 1;
13,731,164✔
353
            }
354
            self.bits_in_buffer -= zeros + 1;
16,061,395✔
355
            return Ok(zeros as u64);
16,061,395✔
356
        }
357

358
        let mut result: u64 = self.bits_in_buffer as _;
24,161,727✔
359

360
        loop {
361
            let new_word = self.backend.read_word()?.to_be();
109,358,796✔
362

363
            if new_word != WR::Word::ZERO {
27,339,699✔
364
                let zeros: usize = new_word.leading_zeros() as _;
32,215,636✔
365
                let mut buffer = new_word.as_double() << (Self::WORD_BITS + zeros) << 1;
24,161,727✔
366
                let mut new_bits = Self::WORD_BITS - zeros - 1;
16,107,818✔
367
                // Top up with a second word if available: new_bits <
368
                // WORD_BITS here, so there is always room and the buffer
369
                // stays short of full. The atomic optional read never
370
                // consumes past the end of the stream (see read_bits).
371
                if let Some(w2) = self.backend.read_word_opt() {
24,161,689✔
372
                    // Shifts valid: WORD_BITS and new_bits are < BUFFER_BITS.
373
                    buffer |= (w2.to_be().as_double() << Self::WORD_BITS) >> new_bits;
16,107,780✔
374
                    new_bits += Self::WORD_BITS;
8,053,890✔
375
                }
376
                self.buffer = buffer;
8,053,909✔
377
                self.bits_in_buffer = new_bits;
8,053,909✔
378
                return Ok(result + zeros as u64);
8,053,909✔
379
            }
380
            result += Self::WORD_BITS as u64;
19,285,790✔
381
        }
382
    }
383

384
    #[inline]
385
    fn skip_bits(&mut self, mut n_bits: usize) -> Result<(), Self::Error> {
118,440✔
386
        debug_assert!(self.bits_in_buffer < Self::BUFFER_BITS);
236,880✔
387
        // happy case, just shift the buffer
388
        if n_bits <= self.bits_in_buffer {
118,440✔
389
            self.bits_in_buffer -= n_bits;
4,000✔
390
            self.buffer <<= n_bits;
4,000✔
391
            return Ok(());
4,000✔
392
        }
393

394
        n_bits -= self.bits_in_buffer;
114,440✔
395

396
        // skip words as needed
397
        while n_bits > Self::WORD_BITS {
114,440✔
UNCOV
398
            let _ = self.backend.read_word()?;
×
UNCOV
399
            n_bits -= Self::WORD_BITS;
×
400
        }
401

402
        // get the final word
403
        let new_word = self.backend.read_word()?.to_be();
457,760✔
404
        self.bits_in_buffer = Self::WORD_BITS - n_bits;
114,440✔
405

406
        self.buffer = new_word.as_double() << (Self::BUFFER_BITS - 1 - self.bits_in_buffer) << 1;
343,320✔
407

408
        Ok(())
114,440✔
409
    }
410

411
    #[cfg(not(feature = "no_copy_impls"))]
412
    fn copy_to<F: Endianness, W: BitWrite<F>>(
413
        &mut self,
414
        bit_write: &mut W,
415
        mut n: u64,
416
    ) -> Result<(), CopyError<Self::Error, W::Error>> {
417
        // Copy from the buffer at most 64 bits at a time, as the buffer
418
        // can hold more than 64 bits, but write_bits accepts at most 64
UNCOV
419
        while n > 0 && self.bits_in_buffer > 0 {
×
UNCOV
420
            let m = Ord::min(Ord::min(n, 64), self.bits_in_buffer as u64) as usize;
×
421
            // The m highest bits of the buffer; m >= 1, so the shift is valid
UNCOV
422
            let value: u64 = (self.buffer >> (Self::BUFFER_BITS - m)).as_to();
×
UNCOV
423
            bit_write
×
UNCOV
424
                .write_bits(value, m)
×
UNCOV
425
                .map_err(CopyError::WriteError)?;
×
426
            // m >= 1, so the two-step shift is valid even when m == BUFFER_BITS
UNCOV
427
            self.buffer = self.buffer << (m - 1) << 1;
×
UNCOV
428
            self.bits_in_buffer -= m;
×
UNCOV
429
            n -= m as u64;
×
430
        }
431

UNCOV
432
        if n == 0 {
×
UNCOV
433
            return Ok(());
×
434
        }
435

436
        // The buffer is empty: copy whole words
UNCOV
437
        while n > Self::WORD_BITS as u64 {
×
UNCOV
438
            bit_write
×
439
                .write_bits(
UNCOV
440
                    self.backend
×
UNCOV
441
                        .read_word()
×
UNCOV
442
                        .map_err(CopyError::ReadError)?
×
UNCOV
443
                        .to_be()
×
UNCOV
444
                        .as_u64(),
×
UNCOV
445
                    Self::WORD_BITS,
×
446
                )
UNCOV
447
                .map_err(CopyError::WriteError)?;
×
UNCOV
448
            n -= Self::WORD_BITS as u64;
×
449
        }
450

UNCOV
451
        debug_assert!(n > 0);
×
452
        // Copy the n highest bits of a final word, and store the remaining
453
        // bits at the top of the buffer, with zeros below
UNCOV
454
        let new_word = self
×
UNCOV
455
            .backend
×
456
            .read_word()
UNCOV
457
            .map_err(CopyError::ReadError)?
×
458
            .to_be();
UNCOV
459
        self.bits_in_buffer = Self::WORD_BITS - n as usize;
×
UNCOV
460
        bit_write
×
UNCOV
461
            .write_bits((new_word >> self.bits_in_buffer).as_u64(), n as usize)
×
UNCOV
462
            .map_err(CopyError::WriteError)?;
×
463
        // n >= 1, so the two-step shift is valid
UNCOV
464
        self.buffer = (new_word.as_double() << (Self::WORD_BITS + n as usize - 1)) << 1;
×
465

UNCOV
466
        Ok(())
×
467
    }
468
}
469

470
impl<WR: WordRead + WordSeek<Error = <WR as WordRead>::Error>, RP: ReadParams> BitSeek
471
    for BufBitReader<BE, WR, RP>
472
where
473
    WR::Word: DoubleType,
474
{
475
    type Error = <WR as WordSeek>::Error;
476

477
    #[inline]
478
    fn bit_pos(&mut self) -> Result<u64, Self::Error> {
32,038✔
479
        Ok(self.backend.word_pos()? * Self::WORD_BITS as u64 - self.bits_in_buffer as u64)
128,152✔
480
    }
481

482
    #[inline]
UNCOV
483
    fn set_bit_pos(&mut self, bit_index: u64) -> Result<(), Self::Error> {
×
UNCOV
484
        self.backend
×
UNCOV
485
            .set_word_pos(bit_index / Self::WORD_BITS as u64)?;
×
UNCOV
486
        let bit_offset = (bit_index % Self::WORD_BITS as u64) as usize;
×
UNCOV
487
        self.buffer = BB::<WR>::ZERO;
×
UNCOV
488
        self.bits_in_buffer = 0;
×
UNCOV
489
        if bit_offset != 0 {
×
UNCOV
490
            let new_word: BB<WR> = self.backend.read_word()?.to_be().as_double();
×
UNCOV
491
            self.bits_in_buffer = Self::WORD_BITS - bit_offset;
×
UNCOV
492
            self.buffer = new_word << (Self::BUFFER_BITS - self.bits_in_buffer);
×
493
        }
UNCOV
494
        Ok(())
×
495
    }
496
}
497

498
//
499
// Little-endian implementation
500
//
501

502
impl<WR: WordRead, RP: ReadParams> BufBitReader<LE, WR, RP>
503
where
504
    WR::Word: DoubleType,
505
{
506
    /// Ensures that in the buffer there are at least `Self::WORD_BITS` bits to read.
507
    /// This method can be called only if there are at least
508
    /// `Self::WORD_BITS` free bits in the buffer.
509
    #[inline(always)]
510
    fn refill(&mut self) -> Result<(), <WR as WordRead>::Error> {
1,086,354✔
511
        debug_assert!(Self::BUFFER_BITS - self.bits_in_buffer >= Self::WORD_BITS);
2,172,708✔
512

513
        let new_word: BB<WR> = self.backend.read_word()?.to_le().as_double();
6,518,124✔
514
        self.buffer |= new_word << self.bits_in_buffer;
2,172,708✔
515
        self.bits_in_buffer += Self::WORD_BITS;
1,086,354✔
516
        Ok(())
1,086,354✔
517
    }
518
}
519

520
impl<WR: WordRead, RP: ReadParams> BitRead<LE> for BufBitReader<LE, WR, RP>
521
where
522
    WR::Word: DoubleType,
523
{
524
    type Error = <WR as WordRead>::Error;
525
    type PeekWord = BB<WR>;
526
    // We guarantee only half a buffer (one word) of peekable bits, so that a
527
    // peek needs at most one refill and the buffer is never completely full;
528
    // this keeps the read/skip/unary hot paths free of full-buffer handling.
529
    const PEEK_BITS: usize = <WR as WordRead>::Word::BITS as usize;
530

531
    #[inline(always)]
532
    fn peek_bits(&mut self, n_bits: usize) -> Result<Self::PeekWord, Self::Error> {
6,016,811✔
533
        debug_assert!(n_bits > 0);
12,033,622✔
534
        debug_assert!(n_bits <= Self::PEEK_BITS);
12,033,622✔
535

536
        // A peek can do at most one refill, otherwise we might lose data
537
        if n_bits > self.bits_in_buffer {
6,016,810✔
538
            self.refill()?;
2,172,708✔
539
        }
540

541
        debug_assert!(n_bits <= self.bits_in_buffer);
12,033,620✔
542

543
        // Keep the n_bits lowest bits of the buffer
544
        let shamt = Self::BUFFER_BITS - n_bits;
12,033,620✔
545
        Ok((self.buffer << shamt) >> shamt)
6,016,810✔
546
    }
547

548
    #[inline(always)]
549
    fn skip_bits_after_peek(&mut self, n_bits: usize) {
6,010,960✔
550
        self.bits_in_buffer -= n_bits;
6,010,960✔
551
        self.buffer >>= n_bits;
6,010,960✔
552
    }
553

554
    #[inline]
555
    fn read_bits(&mut self, mut num_bits: usize) -> Result<u64, Self::Error> {
110,502,323✔
556
        debug_assert!(num_bits <= 64);
221,004,646✔
557
        debug_assert!(self.bits_in_buffer < Self::BUFFER_BITS);
221,004,646✔
558

559
        // most common path, we just read the buffer
560
        if num_bits <= self.bits_in_buffer {
110,502,323✔
561
            let result: u64 = (self.buffer & ((BB::<WR>::ONE << num_bits) - BB::<WR>::ONE)).as_to();
401,593,504✔
562
            self.bits_in_buffer -= num_bits;
100,398,376✔
563
            self.buffer >>= num_bits;
100,398,376✔
564
            return Ok(result);
100,398,376✔
565
        }
566

567
        // Single-word refill path: see the big-endian implementation for the
568
        // invariants.
569
        if num_bits <= Self::WORD_BITS {
10,103,947✔
570
            let bits = self.bits_in_buffer;
10,186,772✔
571
            // The word is required in any case, so no peek is needed.
572
            let w = self.backend.read_word()?.to_le();
20,373,542✔
573
            // Shift valid: bits < num_bits <= WORD_BITS < BUFFER_BITS,
574
            // and the word fits entirely above the buffered bits.
575
            let virt = self.buffer | (w.as_double() << bits);
20,373,536✔
576
            // Extract the low num_bits (num_bits <= WORD_BITS < BUFFER_BITS)
577
            let result: u64 = (virt & ((BB::<WR>::ONE << num_bits) - BB::<WR>::ONE)).as_to();
20,373,536✔
578
            // The new buffer comes from the word alone: bits < num_bits, so
579
            // all buffered bits are consumed and `buffer >> num_bits` would
580
            // be zero. Keeping `virt` off this computation shortens the
581
            // loop-carried dependency chain. Shift valid: 1 <= num_bits -
582
            // bits <= WORD_BITS < BUFFER_BITS.
583
            let mut buffer = w.as_double() >> (num_bits - bits);
20,373,536✔
584
            let mut new_bits = bits + Self::WORD_BITS - num_bits;
10,186,768✔
585
            // Top up with a second word if available: new_bits < WORD_BITS
586
            // here, so there is always room and the buffer stays short of
587
            // full. This halves the number of refills, making the in-buffer
588
            // test above more predictable. The atomic optional read never
589
            // consumes past the end of the stream.
590
            if let Some(w2) = self.backend.read_word_opt() {
15,280,100✔
591
                // Shift valid: new_bits < WORD_BITS <= BUFFER_BITS - WORD_BITS.
592
                buffer |= w2.to_le().as_double() << new_bits;
15,280,074✔
593
                new_bits += Self::WORD_BITS;
5,093,358✔
594
            }
595
            self.buffer = buffer;
5,093,384✔
596
            self.bits_in_buffer = new_bits;
5,093,384✔
597
            return Ok(result);
5,093,384✔
598
        }
599

600
        let mut result: u64 = self.buffer.as_to();
20,042,244✔
601
        let mut bits_in_res = self.bits_in_buffer;
10,021,122✔
602

603
        // Directly read to the result without updating the buffer
604
        while num_bits > Self::WORD_BITS + bits_in_res {
12,083,775✔
605
            let new_word: u64 = self.backend.read_word()?.to_le().as_u64();
42,439,284✔
606
            result |= new_word << bits_in_res;
7,073,214✔
607
            bits_in_res += Self::WORD_BITS;
7,073,214✔
608
        }
609

610
        num_bits -= bits_in_res;
5,010,561✔
611

612
        debug_assert!(num_bits > 0);
10,021,122✔
613
        debug_assert!(num_bits <= Self::WORD_BITS);
10,021,122✔
614

615
        // get the final word
616
        let new_word = self.backend.read_word()?.to_le();
20,042,244✔
617
        self.bits_in_buffer = Self::WORD_BITS - num_bits;
5,010,561✔
618
        // compose the remaining bits
619
        let shamt = 64 - num_bits;
10,021,122✔
620
        let upcast: u64 = new_word.as_u64();
20,042,244✔
621
        let final_bits: u64 = (upcast << shamt) >> shamt;
15,031,683✔
622
        result |= final_bits << bits_in_res;
5,010,561✔
623
        // and put the rest in the buffer
624
        self.buffer = new_word.as_double() >> num_bits;
10,021,122✔
625

626
        Ok(result)
5,010,561✔
627
    }
628

629
    #[inline]
630
    fn read_unary(&mut self) -> Result<u64, Self::Error> {
24,114,319✔
631
        debug_assert!(self.bits_in_buffer < Self::BUFFER_BITS);
48,228,638✔
632

633
        // count the zeros from the right
634
        let zeros: usize = self.buffer.trailing_zeros() as usize;
72,342,957✔
635

636
        // if we encountered a 1 in the bits_in_buffer we can return
637
        if zeros < self.bits_in_buffer {
24,114,319✔
638
            // See the big-endian implementation for the shift-form choice.
639
            if Self::BUFFER_BITS > 64 {
18,392,416✔
640
                self.buffer >>= zeros + 1;
2,330,770✔
641
            } else {
642
                self.buffer = self.buffer >> zeros >> 1;
13,730,876✔
643
            }
644
            self.bits_in_buffer -= zeros + 1;
16,061,646✔
645
            return Ok(zeros as u64);
16,061,646✔
646
        }
647

648
        let mut result: u64 = self.bits_in_buffer as _;
24,158,019✔
649

650
        loop {
651
            let new_word = self.backend.read_word()?.to_le();
109,240,552✔
652

653
            if new_word != WR::Word::ZERO {
27,310,138✔
654
                let zeros: usize = new_word.trailing_zeros() as _;
32,210,692✔
655
                let mut buffer = new_word.as_double() >> zeros >> 1;
24,158,019✔
656
                let mut new_bits = Self::WORD_BITS - zeros - 1;
16,105,346✔
657
                // Top up with a second word if available: new_bits <
658
                // WORD_BITS here, so there is always room and the buffer
659
                // stays short of full. The atomic optional read never
660
                // consumes past the end of the stream (see read_bits).
661
                if let Some(w2) = self.backend.read_word_opt() {
24,157,943✔
662
                    // Shift valid: new_bits < WORD_BITS <= BUFFER_BITS - WORD_BITS.
663
                    buffer |= w2.to_le().as_double() << new_bits;
24,157,905✔
664
                    new_bits += Self::WORD_BITS;
8,052,635✔
665
                }
666
                self.buffer = buffer;
8,052,673✔
667
                self.bits_in_buffer = new_bits;
8,052,673✔
668
                return Ok(result + zeros as u64);
8,052,673✔
669
            }
670
            result += Self::WORD_BITS as u64;
19,257,465✔
671
        }
672
    }
673

674
    #[inline]
675
    fn skip_bits(&mut self, mut n_bits: usize) -> Result<(), Self::Error> {
118,442✔
676
        debug_assert!(self.bits_in_buffer < Self::BUFFER_BITS);
236,884✔
677
        // happy case, just shift the buffer
678
        if n_bits <= self.bits_in_buffer {
118,442✔
679
            self.bits_in_buffer -= n_bits;
4,001✔
680
            self.buffer >>= n_bits;
4,001✔
681
            return Ok(());
4,001✔
682
        }
683

684
        n_bits -= self.bits_in_buffer;
114,441✔
685

686
        // skip words as needed
687
        while n_bits > Self::WORD_BITS {
114,441✔
UNCOV
688
            let _ = self.backend.read_word()?;
×
UNCOV
689
            n_bits -= Self::WORD_BITS;
×
690
        }
691

692
        // get the final word
693
        let new_word = self.backend.read_word()?.to_le();
457,763✔
694
        self.bits_in_buffer = Self::WORD_BITS - n_bits;
114,440✔
695
        self.buffer = new_word.as_double() >> n_bits;
228,880✔
696

697
        Ok(())
114,440✔
698
    }
699

700
    #[cfg(not(feature = "no_copy_impls"))]
701
    fn copy_to<F: Endianness, W: BitWrite<F>>(
702
        &mut self,
703
        bit_write: &mut W,
704
        mut n: u64,
705
    ) -> Result<(), CopyError<Self::Error, W::Error>> {
706
        // Copy from the buffer at most 64 bits at a time, as the buffer
707
        // can hold more than 64 bits, but write_bits accepts at most 64
UNCOV
708
        while n > 0 && self.bits_in_buffer > 0 {
×
709
            let m = Ord::min(Ord::min(n, 64), self.bits_in_buffer as u64) as usize;
×
710
            // The m lowest bits of the buffer; m >= 1, so the mask shift is valid
711
            let value = self.buffer.as_to::<u64>() & (u64::MAX >> (64 - m));
×
UNCOV
712
            bit_write
×
UNCOV
713
                .write_bits(value, m)
×
UNCOV
714
                .map_err(CopyError::WriteError)?;
×
715
            // m >= 1, so the two-step shift is valid even when m == BUFFER_BITS
UNCOV
716
            self.buffer = self.buffer >> (m - 1) >> 1;
×
UNCOV
717
            self.bits_in_buffer -= m;
×
UNCOV
718
            n -= m as u64;
×
719
        }
720

UNCOV
721
        if n == 0 {
×
UNCOV
722
            return Ok(());
×
723
        }
724

725
        // The buffer is empty: copy whole words
UNCOV
726
        while n > Self::WORD_BITS as u64 {
×
UNCOV
727
            bit_write
×
728
                .write_bits(
UNCOV
729
                    self.backend
×
UNCOV
730
                        .read_word()
×
UNCOV
731
                        .map_err(CopyError::ReadError)?
×
UNCOV
732
                        .to_le()
×
UNCOV
733
                        .as_u64(),
×
UNCOV
734
                    Self::WORD_BITS,
×
735
                )
UNCOV
736
                .map_err(CopyError::WriteError)?;
×
UNCOV
737
            n -= Self::WORD_BITS as u64;
×
738
        }
739

UNCOV
740
        debug_assert!(n > 0);
×
741
        // Copy the n lowest bits of a final word, and store the remaining
742
        // bits at the bottom of the buffer, with zeros above
UNCOV
743
        let new_word = self
×
UNCOV
744
            .backend
×
745
            .read_word()
UNCOV
746
            .map_err(CopyError::ReadError)?
×
747
            .to_le();
UNCOV
748
        self.bits_in_buffer = Self::WORD_BITS - n as usize;
×
749
        // n >= 1, so the mask shift is valid
UNCOV
750
        let value = new_word.as_u64() & (u64::MAX >> (64 - n as usize));
×
UNCOV
751
        bit_write
×
UNCOV
752
            .write_bits(value, n as usize)
×
UNCOV
753
            .map_err(CopyError::WriteError)?;
×
UNCOV
754
        self.buffer = new_word.as_double() >> n;
×
UNCOV
755
        Ok(())
×
756
    }
757
}
758

759
impl<WR: WordRead + WordSeek<Error = <WR as WordRead>::Error>, RP: ReadParams> BitSeek
760
    for BufBitReader<LE, WR, RP>
761
where
762
    WR::Word: DoubleType,
763
{
764
    type Error = <WR as WordSeek>::Error;
765

766
    #[inline]
767
    fn bit_pos(&mut self) -> Result<u64, Self::Error> {
32,138✔
768
        Ok(self.backend.word_pos()? * Self::WORD_BITS as u64 - self.bits_in_buffer as u64)
128,552✔
769
    }
770

771
    #[inline]
UNCOV
772
    fn set_bit_pos(&mut self, bit_index: u64) -> Result<(), Self::Error> {
×
UNCOV
773
        self.backend
×
UNCOV
774
            .set_word_pos(bit_index / Self::WORD_BITS as u64)?;
×
775

UNCOV
776
        let bit_offset = (bit_index % Self::WORD_BITS as u64) as usize;
×
UNCOV
777
        self.buffer = BB::<WR>::ZERO;
×
UNCOV
778
        self.bits_in_buffer = 0;
×
UNCOV
779
        if bit_offset != 0 {
×
UNCOV
780
            let new_word: BB<WR> = self.backend.read_word()?.to_le().as_double();
×
UNCOV
781
            self.bits_in_buffer = Self::WORD_BITS - bit_offset;
×
UNCOV
782
            self.buffer = new_word >> bit_offset;
×
783
        }
UNCOV
784
        Ok(())
×
785
    }
786
}
787

788
#[cfg(feature = "std")]
789
impl<WR: WordRead, RP: ReadParams> std::io::Read for BufBitReader<LE, WR, RP>
790
where
791
    WR::Word: DoubleType,
792
{
793
    /// Note that this implementation transfers data in 8-byte chunks, and a
794
    /// [`WordRead`] backend error is not atomic with respect to the chunk:
795
    /// near the end of the stream a partial chunk may be consumed and then
796
    /// discarded, so up to 7 trailing bytes can be unreachable through this
797
    /// interface when the destination buffer length is a multiple of 8.
798
    /// Moreover, the backend error type cannot distinguish end of stream
799
    /// from a backend failure, so reading past the last available byte fails
800
    /// with [`std::io::ErrorKind::UnexpectedEof`] instead of returning
801
    /// `Ok(0)`.
802
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1,014✔
803
        let mut read = 0;
2,028✔
804
        let mut iter = buf.chunks_exact_mut(8);
3,042✔
805

806
        for chunk in &mut iter {
3,222✔
807
            match self.read_bits(64) {
4,416✔
808
                Ok(word) => {
4,416✔
809
                    chunk.copy_from_slice(&word.to_le_bytes());
6,624✔
810
                    read += 8;
2,208✔
811
                }
812
                // If we read some bytes, return them; the error will
813
                // resurface at the next call
UNCOV
814
                Err(_) if read > 0 => return Ok(read),
×
UNCOV
815
                Err(e) => {
×
UNCOV
816
                    return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, e));
×
817
                }
818
            }
819
        }
820

821
        let rem = iter.into_remainder();
3,042✔
822
        if !rem.is_empty() {
1,014✔
823
            match self.read_bits(rem.len() * 8) {
2,856✔
824
                Ok(word) => {
1,904✔
825
                    rem.copy_from_slice(&word.to_le_bytes()[..rem.len()]);
4,760✔
826
                    read += rem.len();
952✔
827
                }
UNCOV
828
                Err(_) if read > 0 => return Ok(read),
×
UNCOV
829
                Err(e) => {
×
UNCOV
830
                    return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, e));
×
831
                }
832
            }
833
        }
834

835
        Ok(read)
1,014✔
836
    }
837
}
838

839
#[cfg(feature = "std")]
840
impl<WR: WordRead, RP: ReadParams> std::io::Read for BufBitReader<BE, WR, RP>
841
where
842
    WR::Word: DoubleType,
843
{
844
    /// Note that this implementation transfers data in 8-byte chunks, and a
845
    /// [`WordRead`] backend error is not atomic with respect to the chunk:
846
    /// near the end of the stream a partial chunk may be consumed and then
847
    /// discarded, so up to 7 trailing bytes can be unreachable through this
848
    /// interface when the destination buffer length is a multiple of 8.
849
    /// Moreover, the backend error type cannot distinguish end of stream
850
    /// from a backend failure, so reading past the last available byte fails
851
    /// with [`std::io::ErrorKind::UnexpectedEof`] instead of returning
852
    /// `Ok(0)`.
853
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1,014✔
854
        let mut read = 0;
2,028✔
855
        let mut iter = buf.chunks_exact_mut(8);
3,042✔
856

857
        for chunk in &mut iter {
3,222✔
858
            match self.read_bits(64) {
4,416✔
859
                Ok(word) => {
4,416✔
860
                    chunk.copy_from_slice(&word.to_be_bytes());
6,624✔
861
                    read += 8;
2,208✔
862
                }
863
                // If we read some bytes, return them; the error will
864
                // resurface at the next call
UNCOV
865
                Err(_) if read > 0 => return Ok(read),
×
UNCOV
866
                Err(e) => {
×
UNCOV
867
                    return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, e));
×
868
                }
869
            }
870
        }
871

872
        let rem = iter.into_remainder();
3,042✔
873
        if !rem.is_empty() {
1,014✔
874
            match self.read_bits(rem.len() * 8) {
2,856✔
875
                Ok(word) => {
1,904✔
876
                    rem.copy_from_slice(&word.to_be_bytes()[8 - rem.len()..]);
4,760✔
877
                    read += rem.len();
952✔
878
                }
UNCOV
879
                Err(_) if read > 0 => return Ok(read),
×
UNCOV
880
                Err(e) => {
×
UNCOV
881
                    return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, e));
×
882
                }
883
            }
884
        }
885

886
        Ok(read)
1,014✔
887
    }
888
}
889

890
#[cfg(test)]
891
#[cfg(feature = "std")]
892
mod tests {
893
    use super::*;
894
    use crate::prelude::{MemWordReader, MemWordWriterVec};
895
    use core::error::Error;
896
    use std::io::Read;
897
    /// On a strict (finite) backend, the two-word top-up must consume a word
898
    /// only when one is available, and the read-and-consume must be atomic:
899
    /// every bit of the stream is read back exactly once, the reader works
900
    /// through the exact end of the data, and only then errors.
901
    #[test]
902
    fn test_topup_at_end_of_stream() {
903
        macro_rules! check {
904
            ($E:ty, $to:ident) => {{
905
                let words: [u32; 3] = [0xA1B2_C3D4_u32.$to(), 0x1596_37D8_u32.$to(), !0];
906
                let mut r = BufBitReader::<$E, _>::new(MemWordReader::new(&words));
907
                let mut all: Vec<u64> = Vec::new();
908
                // 20 + 40 + 20 + 16 = 96 bits = exactly three words; the
909
                // first and third reads cross a word boundary and top up.
910
                for n in [20, 40, 20, 16] {
911
                    all.push(r.read_bits(n).unwrap());
912
                }
913
                // The stream is exhausted: no word was consumed early, none
914
                // was lost, and the next read fails.
915
                assert!(r.read_bits(1).is_err());
916
                // The same bits read in one 64-bit and one 32-bit piece must
917
                // reassemble identically.
918
                let mut r2 = BufBitReader::<$E, _>::new(MemWordReader::new(&words));
919
                let a = r2.read_bits(64).unwrap();
920
                let b = r2.read_bits(32).unwrap();
921
                assert!(r2.read_bits(1).is_err());
922
                if TypeId::of::<$E>() == TypeId::of::<BE>() {
923
                    assert_eq!(all[0], a >> 44);
924
                    assert_eq!(all[1], (a >> 4) & ((1 << 40) - 1));
925
                    assert_eq!(all[2], ((a & 0xF) << 16) | (b >> 16));
926
                    assert_eq!(all[3], b & 0xFFFF);
927
                } else {
928
                    assert_eq!(all[0], a & ((1 << 20) - 1));
929
                    assert_eq!(all[1], (a >> 20) & ((1 << 40) - 1));
930
                    assert_eq!(all[2], (a >> 60) | ((b & 0xFFFF) << 4));
931
                    assert_eq!(all[3], b >> 16);
932
                }
933
            }};
934
        }
935
        use core::any::TypeId;
936
        check!(BE, to_be);
937
        check!(LE, to_le);
938
    }
939

940
    /// The maximum documented peek (`PEEK_BITS` = one word) must be served
941
    /// by a single refill from a completely empty buffer.
942
    #[test]
943
    fn test_peek_max_from_empty_buffer() {
944
        macro_rules! check {
945
            ($W:ty) => {{
946
                const BITS: usize = <$W>::BITS as usize;
947
                let words: [$W; 4] = [!0, 0, 0, 0];
948
                let mut r = BufBitReader::<BE, _>::new(MemWordReader::new(&words));
949
                assert_eq!(
950
                    <BufBitReader<BE, MemWordReader<$W, &[$W; 4]>> as BitRead<BE>>::PEEK_BITS,
951
                    BITS
952
                );
953
                assert_eq!(
954
                    r.peek_bits(BITS).unwrap().as_to::<u64>(),
955
                    (!0 as $W).as_to::<u64>()
956
                );
957
                let mut r = BufBitReader::<LE, _>::new(MemWordReader::new(&words));
958
                assert_eq!(
959
                    r.peek_bits(BITS).unwrap().as_to::<u64>(),
960
                    (!0 as $W).as_to::<u64>()
961
                );
962
            }};
963
        }
964
        check!(u16);
965
        check!(u32);
966
        check!(u64);
967
    }
968

969
    /// Peeking more than `PEEK_BITS` bits violates the documented contract;
970
    /// in debug builds the tightened assertion catches it.
971
    #[test]
972
    #[cfg(debug_assertions)]
973
    #[should_panic]
974
    fn test_peek_beyond_peek_bits_panics_be() {
975
        let words: [u32; 4] = [0; 4];
976
        let mut r = BufBitReader::<BE, _>::new(MemWordReader::new(&words));
977
        let _ = r.peek_bits(33);
978
    }
979

980
    /// See [`test_peek_beyond_peek_bits_panics_be`].
981
    #[test]
982
    #[cfg(debug_assertions)]
983
    #[should_panic]
984
    fn test_peek_beyond_peek_bits_panics_le() {
985
        let words: [u32; 4] = [0; 4];
986
        let mut r = BufBitReader::<LE, _>::new(MemWordReader::new(&words));
987
        let _ = r.peek_bits(33);
988
    }
989

990
    #[test]
991
    fn test_read() -> std::io::Result<()> {
992
        let data = [
993
            0x90, 0x2d, 0xd0, 0x26, 0xdf, 0x89, 0xbb, 0x7e, 0x3a, 0xd6, 0xc6, 0x96, 0x73, 0xe9,
994
            0x9d, 0xc9, 0x2a, 0x77, 0x82, 0xa9, 0xe6, 0x4b, 0x53, 0xcc, 0x83, 0x80, 0x4a, 0xf3,
995
            0xcd, 0xe3, 0x50, 0x4e, 0x45, 0x4a, 0x3a, 0x42, 0x00, 0x4b, 0x4d, 0xbe, 0x4c, 0x88,
996
            0x24, 0xf2, 0x4b, 0x6b, 0xbd, 0x79, 0xeb, 0x74, 0xbc, 0xe8, 0x7d, 0xff, 0x4b, 0x3d,
997
            0xa7, 0xd6, 0x0d, 0xef, 0x9c, 0x5b, 0xb3, 0xec, 0x94, 0x97, 0xcc, 0x8b, 0x41, 0xe1,
998
            0x9c, 0xcc, 0x1a, 0x03, 0x58, 0xc4, 0xfb, 0xd0, 0xc0, 0x10, 0xe2, 0xa0, 0xc9, 0xac,
999
            0xa7, 0xbb, 0x50, 0xf6, 0x5c, 0x87, 0x68, 0x0f, 0x42, 0x93, 0x3f, 0x2e, 0x28, 0x28,
1000
            0x76, 0x83, 0x9b, 0xeb, 0x12, 0xe0, 0x4f, 0xc5, 0xb0, 0x8d, 0x14, 0xda, 0x3b, 0xdf,
1001
            0xd3, 0x4b, 0x80, 0xd1, 0xfc, 0x87, 0x85, 0xae, 0x54, 0xc7, 0x45, 0xc9, 0x38, 0x43,
1002
            0xa7, 0x9f, 0xdd, 0xa9, 0x71, 0xa7, 0x52, 0x36, 0x82, 0xff, 0x49, 0x55, 0xdb, 0x84,
1003
            0xc2, 0x95, 0xad, 0x45, 0x80, 0xc6, 0x02, 0x80, 0xf8, 0xfc, 0x86, 0x79, 0xae, 0xb9,
1004
            0x57, 0xe7, 0x3b, 0x33, 0x64, 0xa8,
1005
        ];
1006
        let data_u32 = unsafe { data.align_to::<u32>().1 };
1007

1008
        for i in 0..data.len() {
1009
            let mut reader = BufBitReader::<LE, _>::new(MemWordReader::new_inf(&data_u32));
1010
            let mut buffer = vec![0; i];
1011
            assert_eq!(reader.read(&mut buffer)?, i);
1012
            assert_eq!(&buffer, &data[..i]);
1013

1014
            let mut reader = BufBitReader::<BE, _>::new(MemWordReader::new_inf(&data_u32));
1015
            let mut buffer = vec![0; i];
1016
            assert_eq!(reader.read(&mut buffer)?, i);
1017
            assert_eq!(&buffer, &data[..i]);
1018
        }
1019
        Ok(())
1020
    }
1021

1022
    #[test]
1023
    fn test_copy_to_then_decode() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
1024
        use crate::prelude::BufBitWriter;
1025
        // Regression test: the big-endian copy_to used to leave garbage in
1026
        // the low bits of the buffer, corrupting reads after a refill
1027
        // caused by a peek
1028
        let data: Vec<u32> = vec![u32::from_be(0x0000_FFFF), 0, 0, 0, 0, 0, 0, 0];
1029
        let mut r = BufBitReader::<BE, _>::new(MemWordReader::new(&data));
1030
        assert_eq!(r.read_bits(16)?, 0);
1031
        let mut sink: Vec<u64> = vec![];
1032
        let mut w = BufBitWriter::<BE, _>::new(MemWordWriterVec::new(&mut sink));
1033
        r.copy_to(&mut w, 10)?;
1034
        assert_eq!(r.read_bits(2)?, 0b11);
1035
        let _ = r.peek_bits(32)?;
1036
        assert_eq!(r.read_bits(30)?, 0b1111 << 26);
1037
        let _ = r.peek_bits(32)?;
1038
        assert_eq!(r.read_bits(33)?, 0);
1039
        Ok(())
1040
    }
1041

1042
    #[test]
1043
    fn test_copy_to_large_buffer() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
1044
        use crate::prelude::BufBitWriter;
1045
        // Regression test: copy_to used to pass more than 64 bits to a
1046
        // single write_bits call when the buffer held more than 64 bits
1047
        let data: Vec<u64> = vec![
1048
            u64::from_be(0x0123_4567_89AB_CDEF),
1049
            u64::from_be(0xFEDC_BA98_7654_3210),
1050
            u64::from_be(0xAAAA_5555_AAAA_5555),
1051
            0,
1052
            0,
1053
        ];
1054
        let mut r = BufBitReader::<BE, _>::new(MemWordReader::new(&data));
1055
        let _ = r.read_bits(10)?;
1056
        // A peek of a full word grows the buffer past 64 bits (54 + 64 = 118)
1057
        let _ = r.peek_bits(64)?; // now the buffer holds more than 64 bits
1058
        let mut sink: Vec<u64> = vec![];
1059
        {
1060
            let mut w = BufBitWriter::<BE, _>::new(MemWordWriterVec::new(&mut sink));
1061
            r.copy_to(&mut w, 100)?;
1062
            w.flush()?;
1063
        }
1064
        let mut r2 = BufBitReader::<BE, _>::new(MemWordReader::new(&data));
1065
        let _ = r2.read_bits(10)?;
1066
        let hi = r2.read_bits(50)?;
1067
        let lo = r2.read_bits(50)?;
1068
        let mut check = BufBitReader::<BE, _>::new(MemWordReader::new(&sink));
1069
        assert_eq!(check.read_bits(50)?, hi);
1070
        assert_eq!(check.read_bits(50)?, lo);
1071
        Ok(())
1072
    }
1073

1074
    macro_rules! test_buf_bit_reader {
1075
        ($f: ident, $word:ty) => {
1076
            #[test]
1077
            fn $f() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
1078
                #[allow(unused_imports)]
1079
                use crate::{
1080
                    codes::{GammaRead, GammaWrite},
1081
                    prelude::{
1082
                        BufBitWriter, DeltaRead, DeltaWrite, MemWordReader, len_delta, len_gamma,
1083
                    },
1084
                };
1085
                use rand::{RngExt, SeedableRng, rngs::SmallRng};
1086

1087
                let mut buffer_be: Vec<$word> = vec![];
1088
                let mut buffer_le: Vec<$word> = vec![];
1089
                let mut big = BufBitWriter::<BE, _>::new(MemWordWriterVec::new(&mut buffer_be));
1090
                let mut little = BufBitWriter::<LE, _>::new(MemWordWriterVec::new(&mut buffer_le));
1091

1092
                let mut r = SmallRng::seed_from_u64(0);
1093
                const ITER: usize = 1_000_000;
1094

1095
                for _ in 0..ITER {
1096
                    let value = r.random_range(0..128);
1097
                    assert_eq!(big.write_gamma(value)?, len_gamma(value));
1098
                    let value = r.random_range(0..128);
1099
                    assert_eq!(little.write_gamma(value)?, len_gamma(value));
1100
                    let value = r.random_range(0..128);
1101
                    assert_eq!(big.write_gamma(value)?, len_gamma(value));
1102
                    let value = r.random_range(0..128);
1103
                    assert_eq!(little.write_gamma(value)?, len_gamma(value));
1104
                    let value = r.random_range(0..128);
1105
                    assert_eq!(big.write_delta(value)?, len_delta(value));
1106
                    let value = r.random_range(0..128);
1107
                    assert_eq!(little.write_delta(value)?, len_delta(value));
1108
                    let value = r.random_range(0..128);
1109
                    assert_eq!(big.write_delta(value)?, len_delta(value));
1110
                    let value = r.random_range(0..128);
1111
                    assert_eq!(little.write_delta(value)?, len_delta(value));
1112
                    let n_bits = r.random_range(0..=64);
1113
                    if n_bits == 0 {
1114
                        big.write_bits(0, 0)?;
1115
                    } else {
1116
                        big.write_bits(1, n_bits)?;
1117
                    }
1118
                    let n_bits = r.random_range(0..=64);
1119
                    if n_bits == 0 {
1120
                        little.write_bits(0, 0)?;
1121
                    } else {
1122
                        little.write_bits(1, n_bits)?;
1123
                    }
1124
                    let value = r.random_range(0..128);
1125
                    assert_eq!(big.write_unary(value)?, value as usize + 1);
1126
                    let value = r.random_range(0..128);
1127
                    assert_eq!(little.write_unary(value)?, value as usize + 1);
1128
                }
1129

1130
                drop(big);
1131
                drop(little);
1132

1133
                type ReadWord = $word;
1134

1135
                #[allow(clippy::size_of_in_element_count)] // false positive
1136
                let be_trans: &[ReadWord] = unsafe {
1137
                    core::slice::from_raw_parts(
1138
                        buffer_be.as_ptr() as *const ReadWord,
1139
                        buffer_be.len()
1140
                            * (core::mem::size_of::<$word>() / core::mem::size_of::<ReadWord>()),
1141
                    )
1142
                };
1143

1144
                #[allow(clippy::size_of_in_element_count)] // false positive
1145
                let le_trans: &[ReadWord] = unsafe {
1146
                    core::slice::from_raw_parts(
1147
                        buffer_le.as_ptr() as *const ReadWord,
1148
                        buffer_le.len()
1149
                            * (core::mem::size_of::<$word>() / core::mem::size_of::<ReadWord>()),
1150
                    )
1151
                };
1152

1153
                let mut big_buff = BufBitReader::<BE, _>::new(MemWordReader::new_inf(be_trans));
1154
                let mut little_buff = BufBitReader::<LE, _>::new(MemWordReader::new_inf(le_trans));
1155

1156
                let mut r = SmallRng::seed_from_u64(0);
1157

1158
                for _ in 0..ITER {
1159
                    assert_eq!(big_buff.read_gamma()?, r.random_range(0..128));
1160
                    assert_eq!(little_buff.read_gamma()?, r.random_range(0..128));
1161
                    assert_eq!(big_buff.read_gamma()?, r.random_range(0..128));
1162
                    assert_eq!(little_buff.read_gamma()?, r.random_range(0..128));
1163
                    assert_eq!(big_buff.read_delta()?, r.random_range(0..128));
1164
                    assert_eq!(little_buff.read_delta()?, r.random_range(0..128));
1165
                    assert_eq!(big_buff.read_delta()?, r.random_range(0..128));
1166
                    assert_eq!(little_buff.read_delta()?, r.random_range(0..128));
1167
                    let n_bits = r.random_range(0..=64);
1168
                    if n_bits == 0 {
1169
                        assert_eq!(big_buff.read_bits(0)?, 0);
1170
                    } else {
1171
                        assert_eq!(big_buff.read_bits(n_bits)?, 1);
1172
                    }
1173
                    let n_bits = r.random_range(0..=64);
1174
                    if n_bits == 0 {
1175
                        assert_eq!(little_buff.read_bits(0)?, 0);
1176
                    } else {
1177
                        assert_eq!(little_buff.read_bits(n_bits)?, 1);
1178
                    }
1179

1180
                    assert_eq!(big_buff.read_unary()?, r.random_range(0..128));
1181
                    assert_eq!(little_buff.read_unary()?, r.random_range(0..128));
1182
                }
1183

1184
                Ok(())
1185
            }
1186
        };
1187
    }
1188

1189
    test_buf_bit_reader!(test_u64, u64);
1190
    test_buf_bit_reader!(test_u32, u32);
1191

1192
    test_buf_bit_reader!(test_u16, u16);
1193
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc