1use crate::{
4 core::{compress, H0},
5 lanes::Lanes,
6};
7
8pub(crate) const MAX_LANES: usize = 16;
12
13pub(crate) const MAX_WIDTH: usize = 32;
18
19pub(crate) const BLOCK: usize = 64;
20
21#[derive(Clone, Copy)]
26pub struct Message<'a> {
27 pub prefix: &'a [u8],
28 pub body: &'a [u8],
29 pub tail: &'a [u8],
31}
32
33impl<'a> Message<'a> {
34 #[inline]
35 pub fn new(body: &'a [u8]) -> Self {
36 Message {
37 prefix: &[],
38 body,
39 tail: &[],
40 }
41 }
42
43 #[inline]
44 pub fn prefixed(prefix: &'a [u8], body: &'a [u8]) -> Self {
45 Message {
46 prefix,
47 body,
48 tail: &[],
49 }
50 }
51
52 #[inline]
54 pub fn pair(prefix: &'a [u8], left: &'a [u8], right: &'a [u8]) -> Self {
55 Message {
56 prefix,
57 body: left,
58 tail: right,
59 }
60 }
61
62 #[inline]
63 pub(crate) fn len(&self) -> usize {
64 self.prefix.len() + self.body.len() + self.tail.len()
65 }
66
67 #[inline]
69 pub(crate) fn blocks(&self) -> usize {
70 (self.len() + 1 + 8).div_ceil(BLOCK)
71 }
72
73 #[inline]
78 pub(crate) fn block_is_interior(&self, k: usize) -> bool {
79 let start = k * BLOCK;
80 start >= self.prefix.len() && start + BLOCK <= self.prefix.len() + self.body.len()
82 }
83
84 #[inline]
88 pub(crate) fn interior_block(&self, k: usize) -> &'a [u8] {
89 let off = k * BLOCK - self.prefix.len();
90 &self.body[off..off + BLOCK]
91 }
92
93 #[inline]
95 pub(crate) fn fill_block(&self, k: usize, out: &mut [u8; BLOCK]) {
96 let start = k * BLOCK;
97 let len = self.len();
98 let plen = self.prefix.len();
99
100 out.fill(0);
101
102 if start < plen {
103 let n = (plen - start).min(BLOCK);
104 out[..n].copy_from_slice(&self.prefix[start..start + n]);
105 }
106 let bend = plen + self.body.len();
108 let from = start.max(plen);
109 let to = (start + BLOCK).min(bend);
110 if from < to {
111 let off = from - start;
112 let n = to - from;
113 let sfrom = from - plen;
114 out[off..off + n].copy_from_slice(&self.body[sfrom..sfrom + n]);
115 }
116 if !self.tail.is_empty() {
117 let tend = bend + self.tail.len();
118 let from = start.max(bend);
119 let to = (start + BLOCK).min(tend);
120 if from < to {
121 let off = from - start;
122 let n = to - from;
123 let sfrom = from - bend;
124 out[off..off + n].copy_from_slice(&self.tail[sfrom..sfrom + n]);
125 }
126 }
127 if start <= len && len < start + BLOCK {
129 out[len - start] = 0x80;
130 }
131 if start + BLOCK == self.blocks() * BLOCK {
133 let bits = (len as u64).wrapping_mul(8);
134 out[BLOCK - 8..].copy_from_slice(&bits.to_be_bytes());
135 }
136 }
137}
138
139pub(crate) struct Shape {
153 pub(crate) same: bool,
154 pub(crate) plen: usize,
155 pub(crate) same_prefix: bool,
156 pub(crate) k_lo: usize,
157 pub(crate) k_hi: usize,
158}
159
160impl Shape {
161 #[inline(always)]
163 pub(crate) fn of(msgs: &[Message<'_>]) -> Shape {
164 let p0 = msgs[0].prefix;
165 let plen = p0.len();
166 let blen = msgs[0].body.len();
167 let len = msgs[0].len();
168 let mut same = true;
169 let mut same_prefix = true;
170 for m in msgs {
171 same &= m.prefix.len() == plen && m.body.len() == blen && m.len() == len;
172 same_prefix &= m.prefix == p0;
173 }
174 Shape {
175 same,
176 plen,
177 same_prefix,
178 k_lo: plen.div_ceil(BLOCK),
179 k_hi: (plen + blen) / BLOCK,
180 }
181 }
182}
183
184#[inline]
186pub(crate) fn stage_prefix_block(
187 msgs: &[Message<'_>],
188 shape: &Shape,
189 staging: &mut [[u8; BLOCK]],
190) -> bool {
191 let plen = shape.plen;
192 if !shape.same_prefix || !(1..BLOCK).contains(&plen) {
193 return false;
194 }
195 let bn = BLOCK - plen;
196
197 if !msgs.iter().all(|m| m.body.len() >= bn) {
198 return false;
199 }
200 let mut tmpl = [0u8; BLOCK];
201 tmpl[..plen].copy_from_slice(&msgs[0].prefix[..plen]);
202 for (s, m) in staging.iter_mut().zip(msgs) {
203 *s = tmpl;
204 s[plen..].copy_from_slice(&m.body[..bn]);
205 }
206 true
207}
208
209#[inline]
210pub(crate) fn write_digest<const W: usize>(state: &[[u32; W]; 8], lane: usize, out: &mut [u8; 32]) {
211 for (i, chunk) in out.chunks_exact_mut(4).enumerate() {
212 chunk.copy_from_slice(&state[i][lane].to_be_bytes());
213 }
214}
215
216#[inline(always)]
227pub(crate) fn hash_lanes<L: Lanes, const W: usize>(msgs: &[Message<'_>], out: &mut [[u8; 32]]) {
228 const { assert!(L::N <= MAX_LANES) };
230 const { assert!(L::N == W) };
231 assert!(msgs.len() <= L::N);
232 assert_eq!(msgs.len(), out.len());
233 let n = msgs.len();
234 if n == 0 {
235 return;
236 }
237
238 let mut state = H0.map(L::splat);
239
240 let mut nblocks = [0usize; W];
242 let mut max_blocks = 0usize;
243 for (lane, m) in msgs.iter().enumerate() {
244 let b = m.blocks();
245 nblocks[lane] = b;
246 max_blocks = max_blocks.max(b);
247 }
248 let uniform = nblocks[..n].iter().all(|&b| b == max_blocks);
249
250 let mut blocks = [[0u8; BLOCK]; W];
251 let mut unpacked = [[0u32; W]; 8];
252
253 let shape = Shape::of(msgs);
254 let mut bases: [*const u8; W] = [std::ptr::null(); W];
255 for (b, m) in bases.iter_mut().zip(msgs) {
256 *b = m.body.as_ptr();
257 }
258 let mut staged = [usize::MAX; W];
259 let mut kk = [0usize; W];
260 let mut interior = [false; W];
261
262 let mut srcs: [*const u8; W] = [std::ptr::null(); W];
263 let staged0 = stage_prefix_block(msgs, &shape, &mut blocks);
264 for k in 0..max_blocks {
265 if shape.same {
267 if k >= shape.k_lo && k < shape.k_hi {
268 let off = k * BLOCK - shape.plen;
269 for (s, base) in srcs.iter_mut().zip(bases.iter()).take(n) {
270 *s = unsafe { base.add(off) };
272 }
273 } else {
274 if !(k == 0 && staged0) {
275 for (lane, m) in msgs.iter().enumerate() {
276 m.fill_block(k, &mut blocks[lane]);
277 }
278 }
279 for (lane, b) in blocks.iter().enumerate().take(n) {
280 srcs[lane] = b.as_ptr();
281 }
282 }
283 } else {
284 if k == 0 && staged0 {
286 kk[..n].fill(0);
287 interior[..n].fill(false);
288 staged[..n].fill(0);
289 } else {
290 for (lane, m) in msgs.iter().enumerate() {
291 let idx = k.min(nblocks[lane] - 1);
292 kk[lane] = idx;
293 let is_interior = m.block_is_interior(idx);
294 interior[lane] = is_interior;
295 if !is_interior && staged[lane] != idx {
296 m.fill_block(idx, &mut blocks[lane]);
297 staged[lane] = idx;
298 }
299 }
300 }
301 for (lane, m) in msgs.iter().enumerate() {
303 srcs[lane] = if interior[lane] {
304 m.interior_block(kk[lane]).as_ptr()
305 } else {
306 blocks[lane].as_ptr()
307 };
308 }
309 }
310 let w = unsafe { L::transpose(&srcs, n) };
313 compress::<L>(&mut state, w);
314
315 if !uniform {
317 let mut any = false;
318 for lane in 0..n {
319 if nblocks[lane] != k + 1 {
320 continue;
321 }
322 if !any {
323 for (i, s) in state.iter().enumerate() {
324 s.store(&mut unpacked[i][..L::N]);
325 }
326 any = true;
327 }
328 write_digest(&unpacked, lane, &mut out[lane]);
329 }
330 }
331 }
332
333 if uniform {
334 for (i, s) in state.iter().enumerate() {
335 s.store(&mut unpacked[i][..L::N]);
336 }
337 for (lane, o) in out.iter_mut().enumerate().take(n) {
338 write_digest(&unpacked, lane, o);
339 }
340 }
341}
342
343pub(crate) unsafe fn drive_pairs(
349 width: usize,
350 group: GroupFn,
351 prefix: &[u8],
352 left: &[&[u8]],
353 right: &[&[u8]],
354 out: &mut [[u8; 32]],
355) {
356 if width == 1 {
360 drive_pairs_staged::<1>(width, group, prefix, left, right, out)
361 } else if width <= MAX_LANES {
362 drive_pairs_staged::<MAX_LANES>(width, group, prefix, left, right, out)
363 } else {
364 drive_pairs_staged::<MAX_WIDTH>(width, group, prefix, left, right, out)
365 }
366}
367
368unsafe fn drive_pairs_staged<const W: usize>(
369 width: usize,
370 group: GroupFn,
371 prefix: &[u8],
372 left: &[&[u8]],
373 right: &[&[u8]],
374 out: &mut [[u8; 32]],
375) {
376 debug_assert!(width <= W);
377 assert_eq!(left.len(), right.len(), "left and right must pair up");
378 assert_eq!(
379 left.len(),
380 out.len(),
381 "output slice must have one digest per pair"
382 );
383 let mut staging = [Message::new(&[]); W];
384 for ((l, r), o) in left
385 .chunks(width)
386 .zip(right.chunks(width))
387 .zip(out.chunks_mut(width))
388 {
389 for ((slot, a), b) in staging.iter_mut().zip(l).zip(r) {
390 *slot = Message::pair(prefix, a, b);
391 }
392 group(&staging[..l.len()], o);
393 }
394}
395
396pub(crate) type GroupFn = unsafe fn(&[Message<'_>], &mut [[u8; 32]]);
398
399pub(crate) unsafe fn drive(
406 width: usize,
407 group: GroupFn,
408 msgs: &[Message<'_>],
409 out: &mut [[u8; 32]],
410) {
411 assert_eq!(
412 msgs.len(),
413 out.len(),
414 "output slice must have one digest per message"
415 );
416 for (m, o) in msgs.chunks(width).zip(out.chunks_mut(width)) {
417 group(m, o);
418 }
419}
420
421pub(crate) unsafe fn drive_slices(
431 width: usize,
432 group: GroupFn,
433 prefix: &[u8],
434 bodies: &[&[u8]],
435 out: &mut [[u8; 32]],
436) {
437 if width == 1 {
439 drive_slices_staged::<1>(width, group, prefix, bodies, out)
440 } else if width <= MAX_LANES {
441 drive_slices_staged::<MAX_LANES>(width, group, prefix, bodies, out)
442 } else {
443 drive_slices_staged::<MAX_WIDTH>(width, group, prefix, bodies, out)
444 }
445}
446
447unsafe fn drive_slices_staged<const W: usize>(
448 width: usize,
449 group: GroupFn,
450 prefix: &[u8],
451 bodies: &[&[u8]],
452 out: &mut [[u8; 32]],
453) {
454 debug_assert!(width <= W);
455 assert_eq!(
456 bodies.len(),
457 out.len(),
458 "output slice must have one digest per message"
459 );
460 let mut staging = [Message::new(&[]); W];
461 for (chunk, o) in bodies.chunks(width).zip(out.chunks_mut(width)) {
462 for (slot, body) in staging.iter_mut().zip(chunk) {
463 *slot = Message::prefixed(prefix, body);
464 }
465 group(&staging[..chunk.len()], o);
466 }
467}