Skip to content

Commit b364717

Browse files
authored
block-buffer: add block_mode_processing methods (#315)
1 parent 215f634 commit b364717

File tree

3 files changed

+164
-15
lines changed

3 files changed

+164
-15
lines changed

block-buffer/src/buffer.rs

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ use block_padding::Padding;
33

44
use crate::{
55
utils::{to_blocks, to_blocks_mut},
6-
Block, DigestBuffer, ParBlock,
6+
Block, DigestBuffer, InvalidLength, ParBlock,
77
};
8+
use core::slice;
89
use generic_array::{typenum::U1, ArrayLength};
910

1011
/// Buffer for block processing of data.
@@ -93,6 +94,65 @@ impl<BlockSize: ArrayLength<u8>> BlockBuffer<BlockSize> {
9394
self.process_data(data, &mut gen_block, set, |f| f(), unreachable);
9495
}
9596

97+
/// Process `data` in blocks and write result to `out_buf`, storing
98+
/// leftovers for future use.
99+
#[inline]
100+
pub fn block_mode_processing<'a>(
101+
&mut self,
102+
mut data: &[u8],
103+
buf: &'a mut [u8],
104+
mut process: impl FnMut(&mut [Block<BlockSize>]),
105+
) -> Result<&'a [u8], InvalidLength> {
106+
let pos = self.get_pos();
107+
let rem = self.remaining();
108+
let mut blocks_processed = 0;
109+
let (_, mut buf_blocks, _) = to_blocks_mut::<BlockSize, U1>(buf);
110+
if pos != 0 {
111+
let n = data.len();
112+
if n < rem {
113+
// double slicing allows to remove panic branches
114+
self.buffer[pos..][..n].copy_from_slice(data);
115+
self.set_pos_unchecked(pos + n);
116+
return Ok(&buf[..0]);
117+
}
118+
if buf_blocks.is_empty() {
119+
return Err(InvalidLength);
120+
}
121+
122+
let (l, r) = buf_blocks.split_at_mut(1);
123+
let buf_block = &mut l[0];
124+
buf_blocks = r;
125+
let (l, r) = data.split_at(rem);
126+
data = r;
127+
128+
buf_block[..pos].copy_from_slice(&self.buffer[..pos]);
129+
buf_block[pos..].copy_from_slice(l);
130+
131+
process(slice::from_mut(buf_block));
132+
blocks_processed += 1;
133+
}
134+
135+
let (data_blocks, leftover) = to_blocks(data);
136+
let buf_blocks = buf_blocks
137+
.get_mut(..data_blocks.len())
138+
.ok_or(InvalidLength)?;
139+
buf_blocks.clone_from_slice(data_blocks);
140+
process(buf_blocks);
141+
blocks_processed += buf_blocks.len();
142+
143+
let n = leftover.len();
144+
self.buffer[..n].copy_from_slice(leftover);
145+
self.set_pos_unchecked(n);
146+
147+
let res = unsafe {
148+
let res_len = BlockSize::USIZE * blocks_processed;
149+
// SAFETY: number of processed blocks never exceeds capacity of `buf`
150+
debug_assert!(buf.len() >= res_len);
151+
buf.get_unchecked(..res_len)
152+
};
153+
Ok(res)
154+
}
155+
96156
/// Compress remaining data after padding it with `delim`, zeros and
97157
/// the `suffix` bytes. If there is not enough unused space, `compress`
98158
/// will be called twice.
@@ -215,7 +275,7 @@ impl<B: ArrayLength<u8>> DigestBuffer<B> for BlockBuffer<B> {
215275
let (left, right) = input.split_at(r);
216276
input = right;
217277
self.buffer[pos..].copy_from_slice(left);
218-
compress(core::slice::from_ref(&self.buffer));
278+
compress(slice::from_ref(&self.buffer));
219279
}
220280

221281
let (blocks, leftover) = to_blocks(input);

block-buffer/src/lazy.rs

Lines changed: 90 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
use crate::{utils::to_blocks, Block, DigestBuffer};
2-
use generic_array::ArrayLength;
1+
use crate::{
2+
utils::{to_blocks, to_blocks_mut},
3+
Block, DigestBuffer, InvalidLength,
4+
};
5+
use core::slice;
6+
use generic_array::{typenum::U1, ArrayLength};
37

48
/// Buffer for lazy block processing of data.
59
#[derive(Clone, Default)]
@@ -9,6 +13,65 @@ pub struct LazyBlockBuffer<BlockSize: ArrayLength<u8>> {
913
}
1014

1115
impl<BlockSize: ArrayLength<u8>> LazyBlockBuffer<BlockSize> {
16+
/// Process `data` in blocks and write result to `out_buf`, storing
17+
/// leftovers for future use.
18+
#[inline]
19+
pub fn block_mode_processing<'a>(
20+
&mut self,
21+
mut data: &[u8],
22+
buf: &'a mut [u8],
23+
mut process: impl FnMut(&mut [Block<BlockSize>]),
24+
) -> Result<&'a [u8], InvalidLength> {
25+
let pos = self.get_pos();
26+
let rem = self.remaining();
27+
let mut blocks_processed = 0;
28+
let (_, mut buf_blocks, _) = to_blocks_mut::<BlockSize, U1>(buf);
29+
if pos != 0 {
30+
let n = data.len();
31+
if n <= rem {
32+
// double slicing allows to remove panic branches
33+
self.buffer[pos..][..n].copy_from_slice(data);
34+
self.set_pos_unchecked(pos + n);
35+
return Ok(&buf[..0]);
36+
}
37+
if buf_blocks.is_empty() {
38+
return Err(InvalidLength);
39+
}
40+
41+
let (l, r) = buf_blocks.split_at_mut(1);
42+
let buf_block = &mut l[0];
43+
buf_blocks = r;
44+
let (l, r) = data.split_at(rem);
45+
data = r;
46+
47+
buf_block[..pos].copy_from_slice(&self.buffer[..pos]);
48+
buf_block[pos..].copy_from_slice(l);
49+
50+
process(slice::from_mut(buf_block));
51+
blocks_processed += 1;
52+
}
53+
54+
let (data_blocks, leftover) = to_blocks_lazy(data);
55+
let buf_blocks = buf_blocks
56+
.get_mut(..data_blocks.len())
57+
.ok_or(InvalidLength)?;
58+
buf_blocks.clone_from_slice(data_blocks);
59+
process(buf_blocks);
60+
blocks_processed += buf_blocks.len();
61+
62+
let n = leftover.len();
63+
self.buffer[..n].copy_from_slice(leftover);
64+
self.set_pos_unchecked(n);
65+
66+
let res = unsafe {
67+
let res_len = BlockSize::USIZE * blocks_processed;
68+
// SAFETY: number of processed blocks never exceeds capacity of `buf`
69+
debug_assert!(buf.len() >= res_len);
70+
buf.get_unchecked(..res_len)
71+
};
72+
Ok(res)
73+
}
74+
1275
/// Pad remaining data with zeros and call `compress` with resulting block.
1376
pub fn pad_zeros(&mut self) -> &mut Block<BlockSize> {
1477
let pos = self.get_pos();
@@ -17,6 +80,15 @@ impl<BlockSize: ArrayLength<u8>> LazyBlockBuffer<BlockSize> {
1780
&mut self.buffer
1881
}
1982

83+
/// Return block if buffer is full, otherwise returns `None`.
84+
#[inline]
85+
pub fn get_full_block(&mut self) -> Option<&mut Block<BlockSize>> {
86+
match self.remaining() {
87+
0 => Some(&mut self.buffer),
88+
_ => None,
89+
}
90+
}
91+
2092
/// Return size of the internall buffer in bytes.
2193
#[inline]
2294
pub fn size(&self) -> usize {
@@ -82,17 +154,7 @@ impl<B: ArrayLength<u8>> DigestBuffer<B> for LazyBlockBuffer<B> {
82154
compress(core::slice::from_ref(&self.buffer));
83155
}
84156

85-
let (mut blocks, mut leftover) = to_blocks(input);
86-
if leftover.is_empty() {
87-
debug_assert!(!blocks.is_empty());
88-
let m = blocks.len() - 1;
89-
// SAFETY: at this stage `input` always contains at least one byte,
90-
// so either `leftover` is not empty or we have at least one block
91-
unsafe {
92-
leftover = blocks.get_unchecked(m);
93-
blocks = blocks.get_unchecked(..m);
94-
}
95-
}
157+
let (blocks, leftover) = to_blocks_lazy(input);
96158
compress(blocks);
97159

98160
let n = leftover.len();
@@ -105,3 +167,18 @@ impl<B: ArrayLength<u8>> DigestBuffer<B> for LazyBlockBuffer<B> {
105167
self.pos = 0;
106168
}
107169
}
170+
171+
fn to_blocks_lazy<N: ArrayLength<u8>>(data: &[u8]) -> (&[Block<N>], &[u8]) {
172+
let (mut blocks, mut leftover) = to_blocks(data);
173+
if leftover.is_empty() {
174+
debug_assert!(!blocks.is_empty());
175+
let m = blocks.len() - 1;
176+
// SAFETY: at this stage `input` always contains at least one byte,
177+
// so either `leftover` is not empty or we have at least one block
178+
unsafe {
179+
leftover = blocks.get_unchecked(m);
180+
blocks = blocks.get_unchecked(..m);
181+
}
182+
}
183+
(blocks, leftover)
184+
}

block-buffer/src/lib.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
pub use block_padding;
1212
pub use generic_array;
1313

14+
use core::fmt;
1415
use generic_array::{ArrayLength, GenericArray};
1516

1617
mod buffer;
@@ -34,3 +35,14 @@ pub trait DigestBuffer<BlockSize: ArrayLength<u8>>: Default {
3435
/// Reset buffer by setting cursor position to zero.
3536
fn reset(&mut self);
3637
}
38+
39+
/// Error type used by the [`BlockBuffer::block_mode_processing`] and
40+
/// [`LazyBlockBuffer::block_mode_processing`] methods.
41+
#[derive(Copy, Clone, Debug)]
42+
pub struct InvalidLength;
43+
44+
impl fmt::Display for InvalidLength {
45+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
46+
f.write_str("Invalid Length")
47+
}
48+
}

0 commit comments

Comments
 (0)