Coverage Report

Created: 2026-09-01 06:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/block-buffer-0.12.1/src/lib.rs
Line
Count
Source
1
//! Fixed size buffer for block processing of data.
2
//!
3
//! # Examples
4
//! ```
5
//! use block_buffer::{EagerBuffer, array::typenum::U4};
6
//!
7
//! let mut buf = EagerBuffer::<U4>::default();
8
//!
9
//! let mut accum = Vec::new();
10
//! let msg1: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
11
//! let msg2: &[u8] = &[10, 11, 12];
12
//!
13
//! buf.digest_blocks(msg1, |blocks| accum.extend_from_slice(blocks));
14
//! buf.digest_blocks(msg2, |blocks| accum.extend_from_slice(blocks));
15
//!
16
//! assert_eq!(accum.len(), 3);
17
//! assert_eq!(accum[0], [0, 1, 2, 3]);
18
//! assert_eq!(accum[1], [4, 5, 6, 7]);
19
//! assert_eq!(accum[2], [8, 9, 10, 11]);
20
//!
21
//! let padded_block = buf.pad_with_zeros();
22
//! assert_eq!(padded_block, [12, 0, 0, 0]);
23
//! ```
24
//!
25
//! Note that block size used with buffers MUST be bigger than zero and smaller than 256.
26
//! You will get a compilation error with an invalid block size:
27
//!
28
//! ```compile_fail
29
//! use block_buffer::{EagerBuffer, array::typenum::U0};
30
//! let buf = EagerBuffer::<U0>::default();
31
//! ```
32
//! ```compile_fail
33
//! use block_buffer::{EagerBuffer, array::typenum::U256};
34
//! let buf = EagerBuffer::<U256>::default();
35
//! ```
36
#![no_std]
37
#![doc(
38
    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
39
    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
40
)]
41
#![allow(clippy::undocumented_unsafe_blocks)] // TODO(tarcieri): document all unsafe blocks
42
43
pub use hybrid_array as array;
44
45
use array::{Array, ArraySize, typenum::Sum};
46
use core::{fmt, mem::MaybeUninit, ptr, slice};
47
48
#[cfg(feature = "zeroize")]
49
use zeroize::{Zeroize, ZeroizeOnDrop};
50
51
mod read;
52
mod sealed;
53
54
pub use read::ReadBuffer;
55
56
/// Trait implemented for supported block sizes, i.e. for types from `U1` to `U255`.
57
pub trait BlockSizes: ArraySize + sealed::BlockSizes {}
58
59
impl<T: ArraySize + sealed::BlockSizes> BlockSizes for T {}
60
61
/// Trait for buffer kinds.
62
pub trait BufferKind: sealed::Sealed {}
63
64
/// Eager block buffer kind, which guarantees that buffer position
65
/// always lies in the range of `0..BlockSize`.
66
#[derive(Copy, Clone, Debug, Default)]
67
pub struct Eager {}
68
69
/// Lazy block buffer kind, which guarantees that buffer position
70
/// always lies in the range of `0..=BlockSize`.
71
#[derive(Copy, Clone, Debug, Default)]
72
pub struct Lazy {}
73
74
impl BufferKind for Eager {}
75
76
impl BufferKind for Lazy {}
77
78
/// Eager block buffer.
79
pub type EagerBuffer<B> = BlockBuffer<B, Eager>;
80
/// Lazy block buffer.
81
pub type LazyBuffer<B> = BlockBuffer<B, Lazy>;
82
83
/// Block buffer error.
84
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
85
pub struct Error;
86
87
impl fmt::Display for Error {
88
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
89
        f.write_str("Block buffer error")
90
    }
91
}
92
93
/// Buffer for block processing of data.
94
pub struct BlockBuffer<BS: BlockSizes, K: BufferKind> {
95
    buffer: MaybeUninit<Array<u8, BS>>,
96
    pos: K::Pos,
97
}
98
99
impl<BS: BlockSizes, K: BufferKind> Default for BlockBuffer<BS, K> {
100
    #[inline]
101
0
    fn default() -> Self {
102
0
        let mut buffer = MaybeUninit::uninit();
103
0
        let mut pos = Default::default();
104
0
        K::set_pos(&mut buffer, &mut pos, 0);
105
0
        Self { buffer, pos }
106
0
    }
107
}
108
109
impl<BS: BlockSizes, K: BufferKind> Clone for BlockBuffer<BS, K> {
110
    #[inline]
111
    fn clone(&self) -> Self {
112
        // SAFETY: `BlockBuffer` does not implement `Drop` (i.e. it could be a `Copy` type),
113
        // so we can safely clone it using `ptr::read`.
114
        unsafe { ptr::read(self) }
115
    }
116
}
117
118
impl<BS: BlockSizes, K: BufferKind> fmt::Debug for BlockBuffer<BS, K> {
119
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
120
        f.debug_struct(K::NAME)
121
            .field("pos", &self.get_pos())
122
            .field("block_size", &BS::USIZE)
123
            .field("data", &self.get_data())
124
            .finish()
125
    }
126
}
127
128
impl<BS: BlockSizes, K: BufferKind> BlockBuffer<BS, K> {
129
    /// Create new buffer from slice.
130
    ///
131
    /// # Panics
132
    /// If slice length is not valid for used buffer kind.
133
    #[inline(always)]
134
    #[must_use]
135
    #[track_caller]
136
    pub fn new(buf: &[u8]) -> Self {
137
        Self::try_new(buf).expect("invalid slice length for buffer kind")
138
    }
139
140
    /// Create new buffer from slice.
141
    ///
142
    /// # Errors
143
    /// If slice length is not valid for used buffer kind.
144
    #[inline(always)]
145
    pub fn try_new(buf: &[u8]) -> Result<Self, Error> {
146
        if !K::invariant(buf.len(), BS::USIZE) {
147
            return Err(Error);
148
        }
149
        let mut res = Self::default();
150
        // SAFETY: we have checked that buffer length satisfies the buffer kind invariant
151
        unsafe {
152
            res.set_data_unchecked(buf);
153
        }
154
        Ok(res)
155
    }
156
157
    /// Digest data in `input` in blocks of size `BlockSize` using
158
    /// the `compress` function, which accepts slice of blocks.
159
    #[inline]
160
0
    pub fn digest_blocks(&mut self, mut input: &[u8], mut compress: impl FnMut(&[Array<u8, BS>])) {
161
0
        let pos = self.get_pos();
162
        // using `self.remaining()` for some reason
163
        // prevents panic elimination
164
0
        let rem = self.size() - pos;
165
0
        let n = input.len();
166
        // Note that checking condition `pos + n < BlockSize` is
167
        // equivalent to checking `n < rem`, where `rem` is equal
168
        // to `BlockSize - pos`. Using the latter allows us to work
169
        // around compiler accounting for possible overflow of
170
        // `pos + n` which results in it inserting unreachable
171
        // panic branches. Using `unreachable_unchecked` in `get_pos`
172
        // we convince compiler that `BlockSize - pos` never underflows.
173
0
        if K::invariant(n, rem) {
174
            // SAFETY: we have checked that length of `input` is smaller than
175
            // number of remaining bytes in `buffer`, so we can safely write data
176
            // into them and update cursor position.
177
0
            unsafe {
178
0
                let buf_ptr = self.buffer.as_mut_ptr().cast::<u8>().add(pos);
179
0
                ptr::copy_nonoverlapping(input.as_ptr(), buf_ptr, input.len());
180
0
                self.set_pos_unchecked(pos + input.len());
181
0
            }
182
0
            return;
183
0
        }
184
0
        if pos != 0 {
185
0
            let (left, right) = input.split_at(rem);
186
0
            input = right;
187
0
188
0
            let g = ResetGuard(self);
189
0
            let buf = &mut g.0.buffer;
190
0
            // SAFETY: length of `left` is equal to number of remaining bytes in `buffer`,
191
0
            // so we can copy data into it and process `buffer` as fully initialized block.
192
0
            // Note that this code can temporarily break the eager buffer invariant,
193
0
            // but we reset the buffer immediately after `compress` using `Drop` impl of
194
0
            // `ResetGuard`, so this code is safe even if `compress` panics.
195
0
            let block = unsafe {
196
0
                let buf_ptr = buf.as_mut_ptr().cast::<u8>().add(pos);
197
0
                ptr::copy_nonoverlapping(left.as_ptr(), buf_ptr, left.len());
198
0
                buf.assume_init_ref()
199
0
            };
200
0
            compress(slice::from_ref(block));
201
0
        }
202
203
0
        let (blocks, leftover) = K::split_blocks(input);
204
0
        if !blocks.is_empty() {
205
0
            compress(blocks);
206
0
        }
207
208
        // SAFETY: `leftover` is always smaller than block size,
209
        // so it satisfies the method's safety requirements for all buffer kinds
210
0
        unsafe {
211
0
            self.set_data_unchecked(leftover);
212
0
        }
213
0
    }
214
215
    /// Reset buffer by setting cursor position to zero.
216
    #[inline(always)]
217
0
    pub fn reset(&mut self) {
218
        // SAFETY: 0 is always valid position
219
0
        unsafe {
220
0
            self.set_pos_unchecked(0);
221
0
        }
222
0
    }
223
224
    /// Pad remaining data with zeros and return resulting block.
225
    #[inline(always)]
226
    pub fn pad_with_zeros(&mut self) -> Array<u8, BS> {
227
        let mut res = Array::<u8, BS>::default();
228
        let data = self.get_data();
229
        res[..data.len()].copy_from_slice(data);
230
        self.reset();
231
        res
232
    }
233
234
    /// Return current cursor position.
235
    #[inline(always)]
236
0
    pub fn get_pos(&self) -> usize {
237
0
        let pos = K::get_pos(&self.buffer, &self.pos);
238
0
        if !K::invariant(pos, BS::USIZE) {
239
0
            debug_assert!(false);
240
            // SAFETY: `pos` never breaks the invariant
241
            unsafe {
242
0
                core::hint::unreachable_unchecked();
243
            }
244
0
        }
245
0
        pos
246
0
    }
247
248
    /// Return slice of data stored inside the buffer.
249
    #[inline(always)]
250
    pub fn get_data(&self) -> &[u8] {
251
        // SAFETY: the `buffer` field is properly initialized up to `self.get_pos()`.
252
        // `get_pos` never returns position bigger than buffer size.
253
        unsafe { slice::from_raw_parts(self.buffer.as_ptr().cast(), self.get_pos()) }
254
    }
255
256
    /// Set buffer content and cursor position.
257
    ///
258
    /// # Panics
259
    /// If `pos` is bigger or equal to block size.
260
    #[inline]
261
    pub fn set(&mut self, buf: Array<u8, BS>, pos: usize) {
262
        assert!(K::invariant(pos, BS::USIZE));
263
        self.buffer = MaybeUninit::new(buf);
264
        // SAFETY: we have asserted that `pos` satisfies the invariant and
265
        // the `buffer` field is fully initialized
266
        unsafe {
267
            self.set_pos_unchecked(pos);
268
        }
269
    }
270
271
    /// Return size of the internal buffer in bytes.
272
    #[inline(always)]
273
0
    pub fn size(&self) -> usize {
274
0
        BS::USIZE
275
0
    }
276
277
    /// Return number of remaining bytes in the internal buffer.
278
    #[inline(always)]
279
    pub fn remaining(&self) -> usize {
280
        self.size() - self.get_pos()
281
    }
282
283
    /// Set buffer position.
284
    ///
285
    /// # Safety
286
    /// Bytes in the range of `0..pos` in the `buffer` field must be properly initialized.
287
    ///
288
    /// `pos` must satisfy invariant of buffer kind, i.e. for eager hashes it must be
289
    /// strictly smaller than block size and for lazy hashes it must be smaller or equal
290
    /// to block size.
291
    #[inline(always)]
292
0
    unsafe fn set_pos_unchecked(&mut self, pos: usize) {
293
0
        debug_assert!(K::invariant(pos, BS::USIZE));
294
0
        K::set_pos(&mut self.buffer, &mut self.pos, pos);
295
0
    }
296
297
    /// Set buffer data.
298
    ///
299
    /// # Safety
300
    /// Length of `buf` must satisfy invariant of buffer kind, i.e. for eager hashes it must be
301
    /// strictly smaller than block size and for lazy hashes it must be smaller or equal
302
    /// to block size.
303
    #[inline(always)]
304
0
    unsafe fn set_data_unchecked(&mut self, buf: &[u8]) {
305
0
        unsafe {
306
0
            self.set_pos_unchecked(buf.len());
307
0
            let dst_ptr: *mut u8 = self.buffer.as_mut_ptr().cast();
308
0
            ptr::copy_nonoverlapping(buf.as_ptr(), dst_ptr, buf.len());
309
0
        }
310
0
    }
311
}
312
313
/// Size of serialized `BlockBuffer` in bytes.
314
pub type SerializedBufferSize<BS, K> = Sum<BS, <K as sealed::Sealed>::Overhead>;
315
/// `BlockBuffer` serialized as a byte array.
316
pub type SerializedBuffer<BS, K> = Array<u8, SerializedBufferSize<BS, K>>;
317
318
impl<BS: BlockSizes, K: BufferKind> BlockBuffer<BS, K>
319
where
320
    BS: core::ops::Add<K::Overhead>,
321
    Sum<BS, K::Overhead>: ArraySize,
322
{
323
    /// Serialize buffer into a byte array.
324
    #[allow(clippy::missing_panics_doc)]
325
    pub fn serialize(&self) -> SerializedBuffer<BS, K> {
326
        let mut buf = SerializedBuffer::<BS, K>::default();
327
        let data = self.get_data();
328
        let (pos, block) = buf.split_at_mut(1);
329
        pos[0] = u8::try_from(data.len()).expect("buffer size is smaller than 256");
330
        block[..data.len()].copy_from_slice(data);
331
        buf
332
    }
333
334
    /// Deserialize buffer from a byte array.
335
    ///
336
    /// # Errors
337
    /// If `buf` does not represent a valid serialization of `BlockBuffer`.
338
    pub fn deserialize(buf: &SerializedBuffer<BS, K>) -> Result<Self, Error> {
339
        let (pos, block) = buf.split_at(1);
340
        let pos = usize::from(pos[0]);
341
342
        if !<K as sealed::Sealed>::invariant(pos, BS::USIZE) {
343
            return Err(Error);
344
        }
345
346
        let (data, tail) = block.split_at(pos);
347
348
        if tail.iter().any(|&b| b != 0) {
349
            return Err(Error);
350
        }
351
352
        let mut res = Self::default();
353
        unsafe { res.set_data_unchecked(data) };
354
        Ok(res)
355
    }
356
}
357
358
impl<BS: BlockSizes> BlockBuffer<BS, Eager> {
359
    /// Compress remaining data after padding it with `delim`, zeros and
360
    /// the `suffix` bytes. If there is not enough unused space, `compress`
361
    /// will be called twice.
362
    ///
363
    /// # Panics
364
    /// If suffix length is bigger than block size.
365
    #[inline(always)]
366
0
    pub fn digest_pad(
367
0
        &mut self,
368
0
        delim: u8,
369
0
        suffix: &[u8],
370
0
        mut compress: impl FnMut(&Array<u8, BS>),
371
0
    ) {
372
0
        let pos = self.get_pos();
373
0
        let size = self.size();
374
        // Number of bytes remaining in the buffer after `delim` is written to it
375
        // This never underflows since for eager buffers `size` is always greater than `pos`.
376
0
        let pad_len = size - pos - 1;
377
378
0
        let suffix_dst_pos = size
379
0
            .checked_sub(suffix.len())
380
0
            .expect("suffix must be smaller than buffer block size");
381
382
0
        let g = ResetGuard(self);
383
        // SAFETY: we fully initialize the buffer. Note that we may temporarily break
384
        // the buffer invariant, but we restore it using `ResetGuard`,
385
        // which works even if `compress` panics.
386
0
        let buf = unsafe {
387
0
            let p: *mut u8 = g.0.buffer.as_mut_ptr().cast::<u8>().add(pos);
388
0
            ptr::write(p, delim);
389
0
            ptr::write_bytes(p.add(1), 0, pad_len);
390
0
            g.0.buffer.assume_init_mut()
391
        };
392
393
0
        if pad_len < suffix.len() {
394
0
            compress(buf);
395
0
            buf.fill(0);
396
0
        }
397
398
0
        buf[suffix_dst_pos..].copy_from_slice(suffix);
399
0
        compress(buf);
400
0
    }
401
402
    /// Pad message with 0x80, zeros and 64-bit message length using
403
    /// big-endian byte order.
404
    #[inline]
405
0
    pub fn len64_padding_be(&mut self, data_len: u64, compress: impl FnMut(&Array<u8, BS>)) {
406
0
        self.digest_pad(0x80, &data_len.to_be_bytes(), compress);
407
0
    }
408
409
    /// Pad message with 0x80, zeros and 64-bit message length using
410
    /// little-endian byte order.
411
    #[inline]
412
    pub fn len64_padding_le(&mut self, data_len: u64, compress: impl FnMut(&Array<u8, BS>)) {
413
        self.digest_pad(0x80, &data_len.to_le_bytes(), compress);
414
    }
415
416
    /// Pad message with 0x80, zeros and 128-bit message length using
417
    /// big-endian byte order.
418
    #[inline]
419
    pub fn len128_padding_be(&mut self, data_len: u128, compress: impl FnMut(&Array<u8, BS>)) {
420
        self.digest_pad(0x80, &data_len.to_be_bytes(), compress);
421
    }
422
}
423
424
#[cfg(feature = "zeroize")]
425
impl<BS: BlockSizes, K: BufferKind> Zeroize for BlockBuffer<BS, K> {
426
    #[inline]
427
    fn zeroize(&mut self) {
428
        self.buffer.zeroize();
429
        self.pos.zeroize();
430
    }
431
}
432
433
impl<BS: BlockSizes, K: BufferKind> Drop for BlockBuffer<BS, K> {
434
    #[inline]
435
0
    fn drop(&mut self) {
436
        #[cfg(feature = "zeroize")]
437
        self.zeroize();
438
0
    }
439
}
440
441
#[cfg(feature = "zeroize")]
442
impl<BS: BlockSizes, K: BufferKind> ZeroizeOnDrop for BlockBuffer<BS, K> {}
443
444
/// Resets the referenced buffer on drop.
445
struct ResetGuard<'a, BS: BlockSizes, K: BufferKind>(&'a mut BlockBuffer<BS, K>);
446
447
impl<BS: BlockSizes, K: BufferKind> Drop for ResetGuard<'_, BS, K> {
448
0
    fn drop(&mut self) {
449
0
        self.0.reset();
450
0
    }
451
}