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

vigna / dsi-bitstream-rs / 29653800427

17 Jul 2026 07:33PM UTC coverage: 58.294% (+0.5%) from 57.839%
29653800427

push

github

vigna
Limit peekable bits to half the buffer instead of peeking twice

Revert the two-refill peek_bits and the associated full-buffer handling in
read_bits/skip_bits/read_unary introduced on the peek_twice branch, restoring
the original single-refill hot paths verbatim. Instead, set PEEK_BITS to
exactly one word (half the two-word bit buffer) so that a peek needs at most
one refill and the bit buffer is never completely full. This keeps all the
other fixes from the branch (copy_to/copy_from, flush retry, into_inner,
counters, dispatch, std::io::Read, word adapters) while avoiding the
per-call branch that the full-buffer handling added to read_bits/peek_bits/
skip_bits on the default (u32) read word.

One word of peekable bits is sufficient for every decoding table; the
generated check_read_table asserts guarantee this at compile time. The read
table-sweep script is clamped so it never generates a table wider than the
read word's PEEK_BITS (e.g. no 9-bit gamma tables on an 8-bit read word).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

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

179 existing lines in 7 files now uncovered.

2228 of 3822 relevant lines covered (58.29%)

2626768.34 hits per line

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

62.06
/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 plus one (extended with zeros beyond end of
33
/// stream).
34
///
35
/// The convenience functions [`from_path`] and [`from_file`] (requiring the
36
/// `std` feature) create a [`BufBitReader`] around a buffered file reader.
37
///
38
/// This implementation is usually faster than
39
/// [`BitReader`](crate::impls::BitReader).
40
///
41
/// The additional type parameter `RP` is used to select the parameters for the
42
/// instantaneous codes, but the casual user should be happy with the default
43
/// value. See [`ReadParams`] for more details.
44
///
45
/// For additional flexibility, when the `std` feature is enabled, this
46
/// structure implements [`std::io::Read`]. Note that because of coherence
47
/// rules it is not possible to implement [`std::io::Read`] for a generic
48
/// [`BitRead`].
49

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

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

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

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

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

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

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

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

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

170
        let new_word: BB<WR> = self.backend.read_word()?.to_be().as_double();
11,621,316✔
171
        self.bits_in_buffer += Self::WORD_BITS;
1,936,886✔
172
        self.buffer |= new_word << (Self::BUFFER_BITS - self.bits_in_buffer);
3,873,772✔
173
        Ok(())
1,936,886✔
174
    }
175
}
176

177
impl<WR: WordRead, RP: ReadParams> BitRead<BE> for BufBitReader<BE, WR, RP>
178
where
179
    WR::Word: DoubleType,
180
{
181
    type Error = <WR as WordRead>::Error;
182
    type PeekWord = BB<WR>;
183
    // We guarantee only half a buffer (one word) of peekable bits, so that a
184
    // peek needs at most one refill and the buffer is never completely full;
185
    // this keeps the read/skip/unary hot paths free of full-buffer handling.
186
    const PEEK_BITS: usize = <WR as WordRead>::Word::BITS as usize;
187

188
    #[inline(always)]
189
    fn peek_bits(&mut self, n_bits: usize) -> Result<Self::PeekWord, Self::Error> {
6,016,662✔
190
        debug_assert!(n_bits > 0);
12,033,324✔
191
        debug_assert!(n_bits <= Self::PeekWord::BITS as usize);
12,033,324✔
192

193
        // A peek can do at most one refill, otherwise we might lose data
194
        if n_bits > self.bits_in_buffer {
6,016,662✔
195
            self.refill()?;
3,873,772✔
196
        }
197

198
        debug_assert!(n_bits <= self.bits_in_buffer);
12,033,324✔
199

200
        // Move the n_bits highest bits of the buffer to the lowest
201
        Ok(self.buffer >> (Self::BUFFER_BITS - n_bits))
6,016,662✔
202
    }
203

204
    #[inline(always)]
205
    fn skip_bits_after_peek(&mut self, n_bits: usize) {
6,010,818✔
206
        self.bits_in_buffer -= n_bits;
6,010,818✔
207
        self.buffer <<= n_bits;
6,010,818✔
208
    }
209

210
    #[inline]
211
    fn read_bits(&mut self, mut num_bits: usize) -> Result<u64, Self::Error> {
110,503,312✔
212
        debug_assert!(num_bits <= 64);
221,006,624✔
213
        debug_assert!(self.bits_in_buffer < Self::BUFFER_BITS);
221,006,624✔
214

215
        // most common path, we just read the buffer
216
        if num_bits <= self.bits_in_buffer {
110,503,312✔
217
            // Valid right shift of BB::<WR>::BITS - num_bits, even when num_bits is zero
218
            let result: u64 = (self.buffer >> (Self::BUFFER_BITS - num_bits - 1) >> 1_u32).as_to();
382,925,772✔
219
            self.bits_in_buffer -= num_bits;
95,731,443✔
220
            self.buffer <<= num_bits;
95,731,443✔
221
            return Ok(result);
95,731,443✔
222
        }
223

224
        let mut result: u64 =
29,543,738✔
225
            (self.buffer >> (Self::BUFFER_BITS - 1 - self.bits_in_buffer) >> 1_u8).as_to();
44,315,607✔
226
        num_bits -= self.bits_in_buffer;
14,771,869✔
227

228
        // Directly read to the result without updating the buffer
229
        while num_bits > Self::WORD_BITS {
22,287,357✔
230
            let new_word: u64 = self.backend.read_word()?.to_be().as_u64();
45,092,928✔
231
            result = (result << Self::WORD_BITS) | new_word;
7,515,488✔
232
            num_bits -= Self::WORD_BITS;
7,515,488✔
233
        }
234

235
        debug_assert!(num_bits > 0);
29,543,738✔
236
        debug_assert!(num_bits <= Self::WORD_BITS);
29,543,738✔
237

238
        // get the final word
239
        let new_word = self.backend.read_word()?.to_be();
59,087,476✔
240
        self.bits_in_buffer = Self::WORD_BITS - num_bits;
14,771,869✔
241
        // compose the remaining bits
242
        let upcast: u64 = new_word.as_u64();
59,087,476✔
243
        let final_bits: u64 = upcast >> self.bits_in_buffer;
44,315,607✔
244
        result = (result << (num_bits - 1) << 1) | final_bits;
14,771,869✔
245
        // and put the rest in the buffer
246
        self.buffer = (new_word.as_double() << (Self::BUFFER_BITS - self.bits_in_buffer - 1)) << 1;
29,543,738✔
247

248
        Ok(result)
14,771,869✔
249
    }
250

251
    #[inline]
252
    fn read_unary(&mut self) -> Result<u64, Self::Error> {
24,115,060✔
253
        debug_assert!(self.bits_in_buffer < Self::BUFFER_BITS);
48,230,120✔
254

255
        // count the zeros from the left
256
        let zeros: usize = self.buffer.leading_zeros() as _;
96,460,240✔
257

258
        // if we encountered a 1 in the bits_in_buffer we can return
259
        if zeros < self.bits_in_buffer {
24,115,060✔
260
            self.buffer = self.buffer << zeros << 1;
11,666,379✔
261
            self.bits_in_buffer -= zeros + 1;
11,666,379✔
262
            return Ok(zeros as u64);
11,666,379✔
263
        }
264

265
        let mut result: u64 = self.bits_in_buffer as _;
37,346,043✔
266

267
        loop {
268
            let new_word = self.backend.read_word()?.to_be();
130,574,928✔
269

270
            if new_word != WR::Word::ZERO {
32,643,732✔
271
                let zeros: usize = new_word.leading_zeros() as _;
49,794,724✔
272
                self.buffer = new_word.as_double() << (Self::WORD_BITS + zeros) << 1;
24,897,362✔
273
                self.bits_in_buffer = Self::WORD_BITS - zeros - 1;
12,448,681✔
274
                return Ok(result + zeros as u64);
12,448,681✔
275
            }
276
            result += Self::WORD_BITS as u64;
20,195,051✔
277
        }
278
    }
279

280
    #[inline]
281
    fn skip_bits(&mut self, mut n_bits: usize) -> Result<(), Self::Error> {
118,440✔
282
        debug_assert!(self.bits_in_buffer < Self::BUFFER_BITS);
236,880✔
283
        // happy case, just shift the buffer
284
        if n_bits <= self.bits_in_buffer {
118,440✔
285
            self.bits_in_buffer -= n_bits;
4,000✔
286
            self.buffer <<= n_bits;
4,000✔
287
            return Ok(());
4,000✔
288
        }
289

290
        n_bits -= self.bits_in_buffer;
114,440✔
291

292
        // skip words as needed
293
        while n_bits > Self::WORD_BITS {
114,440✔
294
            let _ = self.backend.read_word()?;
×
295
            n_bits -= Self::WORD_BITS;
×
296
        }
297

298
        // get the final word
299
        let new_word = self.backend.read_word()?.to_be();
457,760✔
300
        self.bits_in_buffer = Self::WORD_BITS - n_bits;
114,440✔
301

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

304
        Ok(())
114,440✔
305
    }
306

307
    #[cfg(not(feature = "no_copy_impls"))]
308
    fn copy_to<F: Endianness, W: BitWrite<F>>(
309
        &mut self,
310
        bit_write: &mut W,
311
        mut n: u64,
312
    ) -> Result<(), CopyError<Self::Error, W::Error>> {
313
        // Copy from the buffer at most 64 bits at a time, as the buffer
314
        // can hold more than 64 bits, but write_bits accepts at most 64
315
        while n > 0 && self.bits_in_buffer > 0 {
×
316
            let m = Ord::min(Ord::min(n, 64), self.bits_in_buffer as u64) as usize;
×
317
            // The m highest bits of the buffer; m >= 1, so the shift is valid
UNCOV
318
            let value: u64 = (self.buffer >> (Self::BUFFER_BITS - m)).as_to();
×
319
            bit_write
×
UNCOV
320
                .write_bits(value, m)
×
UNCOV
321
                .map_err(CopyError::WriteError)?;
×
322
            // m >= 1, so the two-step shift is valid even when m == BUFFER_BITS
UNCOV
323
            self.buffer = self.buffer << (m - 1) << 1;
×
UNCOV
324
            self.bits_in_buffer -= m;
×
UNCOV
325
            n -= m as u64;
×
326
        }
327

UNCOV
328
        if n == 0 {
×
UNCOV
329
            return Ok(());
×
330
        }
331

332
        // The buffer is empty: copy whole words
UNCOV
333
        while n > Self::WORD_BITS as u64 {
×
UNCOV
334
            bit_write
×
335
                .write_bits(
336
                    self.backend
×
337
                        .read_word()
×
338
                        .map_err(CopyError::ReadError)?
×
339
                        .to_be()
×
340
                        .as_u64(),
×
341
                    Self::WORD_BITS,
×
342
                )
343
                .map_err(CopyError::WriteError)?;
×
344
            n -= Self::WORD_BITS as u64;
×
345
        }
346

347
        debug_assert!(n > 0);
×
348
        // Copy the n highest bits of a final word, and store the remaining
349
        // bits at the top of the buffer, with zeros below
UNCOV
350
        let new_word = self
×
UNCOV
351
            .backend
×
352
            .read_word()
UNCOV
353
            .map_err(CopyError::ReadError)?
×
354
            .to_be();
UNCOV
355
        self.bits_in_buffer = Self::WORD_BITS - n as usize;
×
UNCOV
356
        bit_write
×
UNCOV
357
            .write_bits((new_word >> self.bits_in_buffer).as_u64(), n as usize)
×
UNCOV
358
            .map_err(CopyError::WriteError)?;
×
359
        // n >= 1, so the two-step shift is valid
UNCOV
360
        self.buffer = (new_word.as_double() << (Self::WORD_BITS + n as usize - 1)) << 1;
×
361

UNCOV
362
        Ok(())
×
363
    }
364
}
365

366
impl<WR: WordRead + WordSeek<Error = <WR as WordRead>::Error>, RP: ReadParams> BitSeek
367
    for BufBitReader<BE, WR, RP>
368
where
369
    WR::Word: DoubleType,
370
{
371
    type Error = <WR as WordSeek>::Error;
372

373
    #[inline]
374
    fn bit_pos(&mut self) -> Result<u64, Self::Error> {
32,038✔
375
        Ok(self.backend.word_pos()? * Self::WORD_BITS as u64 - self.bits_in_buffer as u64)
128,152✔
376
    }
377

378
    #[inline]
UNCOV
379
    fn set_bit_pos(&mut self, bit_index: u64) -> Result<(), Self::Error> {
×
UNCOV
380
        self.backend
×
UNCOV
381
            .set_word_pos(bit_index / Self::WORD_BITS as u64)?;
×
UNCOV
382
        let bit_offset = (bit_index % Self::WORD_BITS as u64) as usize;
×
UNCOV
383
        self.buffer = BB::<WR>::ZERO;
×
UNCOV
384
        self.bits_in_buffer = 0;
×
UNCOV
385
        if bit_offset != 0 {
×
UNCOV
386
            let new_word: BB<WR> = self.backend.read_word()?.to_be().as_double();
×
UNCOV
387
            self.bits_in_buffer = Self::WORD_BITS - bit_offset;
×
UNCOV
388
            self.buffer = new_word << (Self::BUFFER_BITS - self.bits_in_buffer);
×
389
        }
UNCOV
390
        Ok(())
×
391
    }
392
}
393

394
//
395
// Little-endian implementation
396
//
397

398
impl<WR: WordRead, RP: ReadParams> BufBitReader<LE, WR, RP>
399
where
400
    WR::Word: DoubleType,
401
{
402
    /// Ensures that in the buffer there are at least `Self::WORD_BITS` bits to read.
403
    /// This method can be called only if there are at least
404
    /// `Self::WORD_BITS` free bits in the buffer.
405
    #[inline(always)]
406
    fn refill(&mut self) -> Result<(), <WR as WordRead>::Error> {
1,939,423✔
407
        debug_assert!(Self::BUFFER_BITS - self.bits_in_buffer >= Self::WORD_BITS);
3,878,846✔
408

409
        let new_word: BB<WR> = self.backend.read_word()?.to_le().as_double();
11,636,538✔
410
        self.buffer |= new_word << self.bits_in_buffer;
3,878,846✔
411
        self.bits_in_buffer += Self::WORD_BITS;
1,939,423✔
412
        Ok(())
1,939,423✔
413
    }
414
}
415

416
impl<WR: WordRead, RP: ReadParams> BitRead<LE> for BufBitReader<LE, WR, RP>
417
where
418
    WR::Word: DoubleType,
419
{
420
    type Error = <WR as WordRead>::Error;
421
    type PeekWord = BB<WR>;
422
    // We guarantee only half a buffer (one word) of peekable bits, so that a
423
    // peek needs at most one refill and the buffer is never completely full;
424
    // this keeps the read/skip/unary hot paths free of full-buffer handling.
425
    const PEEK_BITS: usize = <WR as WordRead>::Word::BITS as usize;
426

427
    #[inline(always)]
428
    fn peek_bits(&mut self, n_bits: usize) -> Result<Self::PeekWord, Self::Error> {
6,016,779✔
429
        debug_assert!(n_bits > 0);
12,033,558✔
430
        debug_assert!(n_bits <= Self::PeekWord::BITS as usize);
12,033,558✔
431

432
        // A peek can do at most one refill, otherwise we might lose data
433
        if n_bits > self.bits_in_buffer {
6,016,779✔
434
            self.refill()?;
3,878,846✔
435
        }
436

437
        debug_assert!(n_bits <= self.bits_in_buffer);
12,033,558✔
438

439
        // Keep the n_bits lowest bits of the buffer
440
        let shamt = Self::BUFFER_BITS - n_bits;
12,033,558✔
441
        Ok((self.buffer << shamt) >> shamt)
6,016,779✔
442
    }
443

444
    #[inline(always)]
445
    fn skip_bits_after_peek(&mut self, n_bits: usize) {
6,010,937✔
446
        self.bits_in_buffer -= n_bits;
6,010,937✔
447
        self.buffer >>= n_bits;
6,010,937✔
448
    }
449

450
    #[inline]
451
    fn read_bits(&mut self, mut num_bits: usize) -> Result<u64, Self::Error> {
110,501,127✔
452
        debug_assert!(num_bits <= 64);
221,002,254✔
453
        debug_assert!(self.bits_in_buffer < Self::BUFFER_BITS);
221,002,254✔
454

455
        // most common path, we just read the buffer
456
        if num_bits <= self.bits_in_buffer {
110,501,127✔
457
            let result: u64 = (self.buffer & ((BB::<WR>::ONE << num_bits) - BB::<WR>::ONE)).as_to();
382,902,640✔
458
            self.bits_in_buffer -= num_bits;
95,725,660✔
459
            self.buffer >>= num_bits;
95,725,660✔
460
            return Ok(result);
95,725,660✔
461
        }
462

463
        let mut result: u64 = self.buffer.as_to();
59,101,868✔
464
        let mut bits_in_res = self.bits_in_buffer;
29,550,934✔
465

466
        // Directly read to the result without updating the buffer
467
        while num_bits > Self::WORD_BITS + bits_in_res {
22,277,935✔
468
            let new_word: u64 = self.backend.read_word()?.to_le().as_u64();
45,014,808✔
469
            result |= new_word << bits_in_res;
7,502,468✔
470
            bits_in_res += Self::WORD_BITS;
7,502,468✔
471
        }
472

473
        num_bits -= bits_in_res;
14,775,467✔
474

475
        debug_assert!(num_bits > 0);
29,550,934✔
476
        debug_assert!(num_bits <= Self::WORD_BITS);
29,550,934✔
477

478
        // get the final word
479
        let new_word = self.backend.read_word()?.to_le();
59,101,868✔
480
        self.bits_in_buffer = Self::WORD_BITS - num_bits;
14,775,467✔
481
        // compose the remaining bits
482
        let shamt = 64 - num_bits;
29,550,934✔
483
        let upcast: u64 = new_word.as_u64();
59,101,868✔
484
        let final_bits: u64 = (upcast << shamt) >> shamt;
44,326,401✔
485
        result |= final_bits << bits_in_res;
14,775,467✔
486
        // and put the rest in the buffer
487
        self.buffer = new_word.as_double() >> num_bits;
29,550,934✔
488

489
        Ok(result)
14,775,467✔
490
    }
491

492
    #[inline]
493
    fn read_unary(&mut self) -> Result<u64, Self::Error> {
24,114,077✔
494
        debug_assert!(self.bits_in_buffer < Self::BUFFER_BITS);
48,228,154✔
495

496
        // count the zeros from the right
497
        let zeros: usize = self.buffer.trailing_zeros() as usize;
72,342,231✔
498

499
        // if we encountered a 1 in the bits_in_buffer we can return
500
        if zeros < self.bits_in_buffer {
24,114,077✔
501
            self.buffer = self.buffer >> zeros >> 1;
11,665,331✔
502
            self.bits_in_buffer -= zeros + 1;
11,665,331✔
503
            return Ok(zeros as u64);
11,665,331✔
504
        }
505

506
        let mut result: u64 = self.bits_in_buffer as _;
37,346,238✔
507

508
        loop {
509
            let new_word = self.backend.read_word()?.to_le();
130,462,196✔
510

511
            if new_word != WR::Word::ZERO {
32,615,549✔
512
                let zeros: usize = new_word.trailing_zeros() as _;
49,794,984✔
513
                self.buffer = new_word.as_double() >> zeros >> 1;
24,897,492✔
514
                self.bits_in_buffer = Self::WORD_BITS - zeros - 1;
12,448,746✔
515
                return Ok(result + zeros as u64);
12,448,746✔
516
            }
517
            result += Self::WORD_BITS as u64;
20,166,803✔
518
        }
519
    }
520

521
    #[inline]
522
    fn skip_bits(&mut self, mut n_bits: usize) -> Result<(), Self::Error> {
118,441✔
523
        debug_assert!(self.bits_in_buffer < Self::BUFFER_BITS);
236,882✔
524
        // happy case, just shift the buffer
525
        if n_bits <= self.bits_in_buffer {
118,441✔
526
            self.bits_in_buffer -= n_bits;
4,000✔
527
            self.buffer >>= n_bits;
4,000✔
528
            return Ok(());
4,000✔
529
        }
530

531
        n_bits -= self.bits_in_buffer;
114,441✔
532

533
        // skip words as needed
534
        while n_bits > Self::WORD_BITS {
114,441✔
UNCOV
535
            let _ = self.backend.read_word()?;
×
UNCOV
536
            n_bits -= Self::WORD_BITS;
×
537
        }
538

539
        // get the final word
540
        let new_word = self.backend.read_word()?.to_le();
457,764✔
541
        self.bits_in_buffer = Self::WORD_BITS - n_bits;
114,441✔
542
        self.buffer = new_word.as_double() >> n_bits;
228,882✔
543

544
        Ok(())
114,441✔
545
    }
546

547
    #[cfg(not(feature = "no_copy_impls"))]
548
    fn copy_to<F: Endianness, W: BitWrite<F>>(
549
        &mut self,
550
        bit_write: &mut W,
551
        mut n: u64,
552
    ) -> Result<(), CopyError<Self::Error, W::Error>> {
553
        // Copy from the buffer at most 64 bits at a time, as the buffer
554
        // can hold more than 64 bits, but write_bits accepts at most 64
555
        while n > 0 && self.bits_in_buffer > 0 {
×
556
            let m = Ord::min(Ord::min(n, 64), self.bits_in_buffer as u64) as usize;
×
557
            // The m lowest bits of the buffer; m >= 1, so the mask shift is valid
558
            let value = self.buffer.as_to::<u64>() & (u64::MAX >> (64 - m));
×
559
            bit_write
×
560
                .write_bits(value, m)
×
UNCOV
561
                .map_err(CopyError::WriteError)?;
×
562
            // m >= 1, so the two-step shift is valid even when m == BUFFER_BITS
UNCOV
563
            self.buffer = self.buffer >> (m - 1) >> 1;
×
UNCOV
564
            self.bits_in_buffer -= m;
×
UNCOV
565
            n -= m as u64;
×
566
        }
567

UNCOV
568
        if n == 0 {
×
UNCOV
569
            return Ok(());
×
570
        }
571

572
        // The buffer is empty: copy whole words
UNCOV
573
        while n > Self::WORD_BITS as u64 {
×
UNCOV
574
            bit_write
×
575
                .write_bits(
UNCOV
576
                    self.backend
×
UNCOV
577
                        .read_word()
×
UNCOV
578
                        .map_err(CopyError::ReadError)?
×
UNCOV
579
                        .to_le()
×
UNCOV
580
                        .as_u64(),
×
UNCOV
581
                    Self::WORD_BITS,
×
582
                )
UNCOV
583
                .map_err(CopyError::WriteError)?;
×
UNCOV
584
            n -= Self::WORD_BITS as u64;
×
585
        }
586

UNCOV
587
        debug_assert!(n > 0);
×
588
        // Copy the n lowest bits of a final word, and store the remaining
589
        // bits at the bottom of the buffer, with zeros above
UNCOV
590
        let new_word = self
×
UNCOV
591
            .backend
×
592
            .read_word()
UNCOV
593
            .map_err(CopyError::ReadError)?
×
594
            .to_le();
UNCOV
595
        self.bits_in_buffer = Self::WORD_BITS - n as usize;
×
596
        // n >= 1, so the mask shift is valid
UNCOV
597
        let value = new_word.as_u64() & (u64::MAX >> (64 - n as usize));
×
UNCOV
598
        bit_write
×
UNCOV
599
            .write_bits(value, n as usize)
×
UNCOV
600
            .map_err(CopyError::WriteError)?;
×
UNCOV
601
        self.buffer = new_word.as_double() >> n;
×
UNCOV
602
        Ok(())
×
603
    }
604
}
605

606
impl<WR: WordRead + WordSeek<Error = <WR as WordRead>::Error>, RP: ReadParams> BitSeek
607
    for BufBitReader<LE, WR, RP>
608
where
609
    WR::Word: DoubleType,
610
{
611
    type Error = <WR as WordSeek>::Error;
612

613
    #[inline]
614
    fn bit_pos(&mut self) -> Result<u64, Self::Error> {
32,138✔
615
        Ok(self.backend.word_pos()? * Self::WORD_BITS as u64 - self.bits_in_buffer as u64)
128,552✔
616
    }
617

618
    #[inline]
UNCOV
619
    fn set_bit_pos(&mut self, bit_index: u64) -> Result<(), Self::Error> {
×
UNCOV
620
        self.backend
×
UNCOV
621
            .set_word_pos(bit_index / Self::WORD_BITS as u64)?;
×
622

UNCOV
623
        let bit_offset = (bit_index % Self::WORD_BITS as u64) as usize;
×
UNCOV
624
        self.buffer = BB::<WR>::ZERO;
×
UNCOV
625
        self.bits_in_buffer = 0;
×
UNCOV
626
        if bit_offset != 0 {
×
UNCOV
627
            let new_word: BB<WR> = self.backend.read_word()?.to_le().as_double();
×
UNCOV
628
            self.bits_in_buffer = Self::WORD_BITS - bit_offset;
×
UNCOV
629
            self.buffer = new_word >> bit_offset;
×
630
        }
UNCOV
631
        Ok(())
×
632
    }
633
}
634

635
#[cfg(feature = "std")]
636
impl<WR: WordRead, RP: ReadParams> std::io::Read for BufBitReader<LE, WR, RP>
637
where
638
    WR::Word: DoubleType,
639
{
640
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1,014✔
641
        let mut read = 0;
2,028✔
642
        let mut iter = buf.chunks_exact_mut(8);
3,042✔
643

644
        for chunk in &mut iter {
3,222✔
645
            match self.read_bits(64) {
4,416✔
646
                Ok(word) => {
4,416✔
647
                    chunk.copy_from_slice(&word.to_le_bytes());
6,624✔
648
                    read += 8;
2,208✔
649
                }
650
                // If we read some bytes, return them; the error will
651
                // resurface at the next call
UNCOV
652
                Err(_) if read > 0 => return Ok(read),
×
UNCOV
653
                Err(e) => {
×
UNCOV
654
                    return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, e));
×
655
                }
656
            }
657
        }
658

659
        let rem = iter.into_remainder();
3,042✔
660
        if !rem.is_empty() {
1,014✔
661
            match self.read_bits(rem.len() * 8) {
2,856✔
662
                Ok(word) => {
1,904✔
663
                    rem.copy_from_slice(&word.to_le_bytes()[..rem.len()]);
4,760✔
664
                    read += rem.len();
952✔
665
                }
UNCOV
666
                Err(_) if read > 0 => return Ok(read),
×
UNCOV
667
                Err(e) => {
×
UNCOV
668
                    return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, e));
×
669
                }
670
            }
671
        }
672

673
        Ok(read)
1,014✔
674
    }
675
}
676

677
#[cfg(feature = "std")]
678
impl<WR: WordRead, RP: ReadParams> std::io::Read for BufBitReader<BE, WR, RP>
679
where
680
    WR::Word: DoubleType,
681
{
682
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1,014✔
683
        let mut read = 0;
2,028✔
684
        let mut iter = buf.chunks_exact_mut(8);
3,042✔
685

686
        for chunk in &mut iter {
3,222✔
687
            match self.read_bits(64) {
4,416✔
688
                Ok(word) => {
4,416✔
689
                    chunk.copy_from_slice(&word.to_be_bytes());
6,624✔
690
                    read += 8;
2,208✔
691
                }
692
                // If we read some bytes, return them; the error will
693
                // resurface at the next call
UNCOV
694
                Err(_) if read > 0 => return Ok(read),
×
UNCOV
695
                Err(e) => {
×
UNCOV
696
                    return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, e));
×
697
                }
698
            }
699
        }
700

701
        let rem = iter.into_remainder();
3,042✔
702
        if !rem.is_empty() {
1,014✔
703
            match self.read_bits(rem.len() * 8) {
2,856✔
704
                Ok(word) => {
1,904✔
705
                    rem.copy_from_slice(&word.to_be_bytes()[8 - rem.len()..]);
4,760✔
706
                    read += rem.len();
952✔
707
                }
UNCOV
708
                Err(_) if read > 0 => return Ok(read),
×
UNCOV
709
                Err(e) => {
×
UNCOV
710
                    return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, e));
×
711
                }
712
            }
713
        }
714

715
        Ok(read)
1,014✔
716
    }
717
}
718

719
#[cfg(test)]
720
#[cfg(feature = "std")]
721
mod tests {
722
    use super::*;
723
    use crate::prelude::{MemWordReader, MemWordWriterVec};
724
    use core::error::Error;
725
    use std::io::Read;
726

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

745
        for i in 0..data.len() {
746
            let mut reader = BufBitReader::<LE, _>::new(MemWordReader::new_inf(&data_u32));
747
            let mut buffer = vec![0; i];
748
            assert_eq!(reader.read(&mut buffer)?, i);
749
            assert_eq!(&buffer, &data[..i]);
750

751
            let mut reader = BufBitReader::<BE, _>::new(MemWordReader::new_inf(&data_u32));
752
            let mut buffer = vec![0; i];
753
            assert_eq!(reader.read(&mut buffer)?, i);
754
            assert_eq!(&buffer, &data[..i]);
755
        }
756
        Ok(())
757
    }
758

759
    #[test]
760
    fn test_copy_to_then_decode() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
761
        use crate::prelude::BufBitWriter;
762
        // Regression test: the big-endian copy_to used to leave garbage in
763
        // the low bits of the buffer, corrupting reads after a refill
764
        // caused by a peek
765
        let data: Vec<u32> = vec![u32::from_be(0x0000_FFFF), 0, 0, 0, 0, 0, 0, 0];
766
        let mut r = BufBitReader::<BE, _>::new(MemWordReader::new(&data));
767
        assert_eq!(r.read_bits(16)?, 0);
768
        let mut sink: Vec<u64> = vec![];
769
        let mut w = BufBitWriter::<BE, _>::new(MemWordWriterVec::new(&mut sink));
770
        r.copy_to(&mut w, 10)?;
771
        assert_eq!(r.read_bits(2)?, 0b11);
772
        let _ = r.peek_bits(32)?;
773
        assert_eq!(r.read_bits(30)?, 0b1111 << 26);
774
        let _ = r.peek_bits(32)?;
775
        assert_eq!(r.read_bits(33)?, 0);
776
        Ok(())
777
    }
778

779
    #[test]
780
    fn test_copy_to_large_buffer() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
781
        use crate::prelude::BufBitWriter;
782
        // Regression test: copy_to used to pass more than 64 bits to a
783
        // single write_bits call when the buffer held more than 64 bits
784
        let data: Vec<u64> = vec![
785
            u64::from_be(0x0123_4567_89AB_CDEF),
786
            u64::from_be(0xFEDC_BA98_7654_3210),
787
            u64::from_be(0xAAAA_5555_AAAA_5555),
788
            0,
789
            0,
790
        ];
791
        let mut r = BufBitReader::<BE, _>::new(MemWordReader::new(&data));
792
        let _ = r.read_bits(10)?;
793
        // A peek of a full word grows the buffer past 64 bits (54 + 64 = 118)
794
        let _ = r.peek_bits(64)?; // now the buffer holds more than 64 bits
795
        let mut sink: Vec<u64> = vec![];
796
        {
797
            let mut w = BufBitWriter::<BE, _>::new(MemWordWriterVec::new(&mut sink));
798
            r.copy_to(&mut w, 100)?;
799
            w.flush()?;
800
        }
801
        let mut r2 = BufBitReader::<BE, _>::new(MemWordReader::new(&data));
802
        let _ = r2.read_bits(10)?;
803
        let hi = r2.read_bits(50)?;
804
        let lo = r2.read_bits(50)?;
805
        let mut check = BufBitReader::<BE, _>::new(MemWordReader::new(&sink));
806
        assert_eq!(check.read_bits(50)?, hi);
807
        assert_eq!(check.read_bits(50)?, lo);
808
        Ok(())
809
    }
810

811
    macro_rules! test_buf_bit_reader {
812
        ($f: ident, $word:ty) => {
813
            #[test]
814
            fn $f() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
815
                #[allow(unused_imports)]
816
                use crate::{
817
                    codes::{GammaRead, GammaWrite},
818
                    prelude::{
819
                        BufBitWriter, DeltaRead, DeltaWrite, MemWordReader, len_delta, len_gamma,
820
                    },
821
                };
822
                use rand::{RngExt, SeedableRng, rngs::SmallRng};
823

824
                let mut buffer_be: Vec<$word> = vec![];
825
                let mut buffer_le: Vec<$word> = vec![];
826
                let mut big = BufBitWriter::<BE, _>::new(MemWordWriterVec::new(&mut buffer_be));
827
                let mut little = BufBitWriter::<LE, _>::new(MemWordWriterVec::new(&mut buffer_le));
828

829
                let mut r = SmallRng::seed_from_u64(0);
830
                const ITER: usize = 1_000_000;
831

832
                for _ in 0..ITER {
833
                    let value = r.random_range(0..128);
834
                    assert_eq!(big.write_gamma(value)?, len_gamma(value));
835
                    let value = r.random_range(0..128);
836
                    assert_eq!(little.write_gamma(value)?, len_gamma(value));
837
                    let value = r.random_range(0..128);
838
                    assert_eq!(big.write_gamma(value)?, len_gamma(value));
839
                    let value = r.random_range(0..128);
840
                    assert_eq!(little.write_gamma(value)?, len_gamma(value));
841
                    let value = r.random_range(0..128);
842
                    assert_eq!(big.write_delta(value)?, len_delta(value));
843
                    let value = r.random_range(0..128);
844
                    assert_eq!(little.write_delta(value)?, len_delta(value));
845
                    let value = r.random_range(0..128);
846
                    assert_eq!(big.write_delta(value)?, len_delta(value));
847
                    let value = r.random_range(0..128);
848
                    assert_eq!(little.write_delta(value)?, len_delta(value));
849
                    let n_bits = r.random_range(0..=64);
850
                    if n_bits == 0 {
851
                        big.write_bits(0, 0)?;
852
                    } else {
853
                        big.write_bits(1, n_bits)?;
854
                    }
855
                    let n_bits = r.random_range(0..=64);
856
                    if n_bits == 0 {
857
                        little.write_bits(0, 0)?;
858
                    } else {
859
                        little.write_bits(1, n_bits)?;
860
                    }
861
                    let value = r.random_range(0..128);
862
                    assert_eq!(big.write_unary(value)?, value as usize + 1);
863
                    let value = r.random_range(0..128);
864
                    assert_eq!(little.write_unary(value)?, value as usize + 1);
865
                }
866

867
                drop(big);
868
                drop(little);
869

870
                type ReadWord = $word;
871

872
                #[allow(clippy::size_of_in_element_count)] // false positive
873
                let be_trans: &[ReadWord] = unsafe {
874
                    core::slice::from_raw_parts(
875
                        buffer_be.as_ptr() as *const ReadWord,
876
                        buffer_be.len()
877
                            * (core::mem::size_of::<$word>() / core::mem::size_of::<ReadWord>()),
878
                    )
879
                };
880

881
                #[allow(clippy::size_of_in_element_count)] // false positive
882
                let le_trans: &[ReadWord] = unsafe {
883
                    core::slice::from_raw_parts(
884
                        buffer_le.as_ptr() as *const ReadWord,
885
                        buffer_le.len()
886
                            * (core::mem::size_of::<$word>() / core::mem::size_of::<ReadWord>()),
887
                    )
888
                };
889

890
                let mut big_buff = BufBitReader::<BE, _>::new(MemWordReader::new_inf(be_trans));
891
                let mut little_buff = BufBitReader::<LE, _>::new(MemWordReader::new_inf(le_trans));
892

893
                let mut r = SmallRng::seed_from_u64(0);
894

895
                for _ in 0..ITER {
896
                    assert_eq!(big_buff.read_gamma()?, r.random_range(0..128));
897
                    assert_eq!(little_buff.read_gamma()?, r.random_range(0..128));
898
                    assert_eq!(big_buff.read_gamma()?, r.random_range(0..128));
899
                    assert_eq!(little_buff.read_gamma()?, r.random_range(0..128));
900
                    assert_eq!(big_buff.read_delta()?, r.random_range(0..128));
901
                    assert_eq!(little_buff.read_delta()?, r.random_range(0..128));
902
                    assert_eq!(big_buff.read_delta()?, r.random_range(0..128));
903
                    assert_eq!(little_buff.read_delta()?, r.random_range(0..128));
904
                    let n_bits = r.random_range(0..=64);
905
                    if n_bits == 0 {
906
                        assert_eq!(big_buff.read_bits(0)?, 0);
907
                    } else {
908
                        assert_eq!(big_buff.read_bits(n_bits)?, 1);
909
                    }
910
                    let n_bits = r.random_range(0..=64);
911
                    if n_bits == 0 {
912
                        assert_eq!(little_buff.read_bits(0)?, 0);
913
                    } else {
914
                        assert_eq!(little_buff.read_bits(n_bits)?, 1);
915
                    }
916

917
                    assert_eq!(big_buff.read_unary()?, r.random_range(0..128));
918
                    assert_eq!(little_buff.read_unary()?, r.random_range(0..128));
919
                }
920

921
                Ok(())
922
            }
923
        };
924
    }
925

926
    test_buf_bit_reader!(test_u64, u64);
927
    test_buf_bit_reader!(test_u32, u32);
928

929
    test_buf_bit_reader!(test_u16, u16);
930
}
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