| 158 | /// the `compress` function, which accepts slice of blocks. |
| 159 | #[inline] |
| 160 | pub fn digest_blocks(&mut self, mut input: &[u8], mut compress: impl FnMut(&[Array<u8, BS>])) { |
| 161 | let pos = self.get_pos(); |
| 162 | // using `self.remaining()` for some reason |
| 163 | // prevents panic elimination |
| 164 | let rem = self.size() - pos; |
| 165 | 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 | 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 | unsafe { |
| 178 | let buf_ptr = self.buffer.as_mut_ptr().cast::<u8>().add(pos); |
| 179 | ptr::copy_nonoverlapping(input.as_ptr(), buf_ptr, input.len()); |
| 180 | self.set_pos_unchecked(pos + input.len()); |
| 181 | } |
| 182 | return; |
| 183 | } |
| 184 | if pos != 0 { |
| 185 | let (left, right) = input.split_at(rem); |
| 186 | input = right; |
| 187 | |
| 188 | let g = ResetGuard(self); |
| 189 | let buf = &mut g.0.buffer; |
| 190 | // SAFETY: length of `left` is equal to number of remaining bytes in `buffer`, |
| 191 | // so we can copy data into it and process `buffer` as fully initialized block. |
| 192 | // Note that this code can temporarily break the eager buffer invariant, |
| 193 | // but we reset the buffer immediately after `compress` using `Drop` impl of |
| 194 | // `ResetGuard`, so this code is safe even if `compress` panics. |
| 195 | let block = unsafe { |
| 196 | let buf_ptr = buf.as_mut_ptr().cast::<u8>().add(pos); |
| 197 | ptr::copy_nonoverlapping(left.as_ptr(), buf_ptr, left.len()); |
| 198 | buf.assume_init_ref() |
| 199 | }; |
| 200 | compress(slice::from_ref(block)); |
| 201 | } |
| 202 | |
| 203 | let (blocks, leftover) = K::split_blocks(input); |
| 204 | if !blocks.is_empty() { |
| 205 | compress(blocks); |
| 206 | } |
| 207 | |
| 208 | // SAFETY: `leftover` is always smaller than block size, |
| 209 | // so it satisfies the method's safety requirements for all buffer kinds |
| 210 | unsafe { |
| 211 | self.set_data_unchecked(leftover); |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | /// Reset buffer by setting cursor position to zero. |
| 216 | #[inline(always)] |