Coverage Report

Created: 2026-11-03 08:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/blake3-1.8.4/src/portable.rs
Line
Count
Source
1
use crate::{
2
    counter_high, counter_low, CVBytes, CVWords, IncrementCounter, BLOCK_LEN, IV, MSG_SCHEDULE,
3
    OUT_LEN,
4
};
5
use arrayref::{array_mut_ref, array_ref};
6
7
#[inline(always)]
8
0
fn g(state: &mut [u32; 16], a: usize, b: usize, c: usize, d: usize, x: u32, y: u32) {
9
0
    state[a] = state[a].wrapping_add(state[b]).wrapping_add(x);
10
0
    state[d] = (state[d] ^ state[a]).rotate_right(16);
11
0
    state[c] = state[c].wrapping_add(state[d]);
12
0
    state[b] = (state[b] ^ state[c]).rotate_right(12);
13
0
    state[a] = state[a].wrapping_add(state[b]).wrapping_add(y);
14
0
    state[d] = (state[d] ^ state[a]).rotate_right(8);
15
0
    state[c] = state[c].wrapping_add(state[d]);
16
0
    state[b] = (state[b] ^ state[c]).rotate_right(7);
17
0
}
18
19
#[inline(always)]
20
0
fn round(state: &mut [u32; 16], msg: &[u32; 16], round: usize) {
21
    // Select the message schedule based on the round.
22
0
    let schedule = MSG_SCHEDULE[round];
23
24
    // Mix the columns.
25
0
    g(state, 0, 4, 8, 12, msg[schedule[0]], msg[schedule[1]]);
26
0
    g(state, 1, 5, 9, 13, msg[schedule[2]], msg[schedule[3]]);
27
0
    g(state, 2, 6, 10, 14, msg[schedule[4]], msg[schedule[5]]);
28
0
    g(state, 3, 7, 11, 15, msg[schedule[6]], msg[schedule[7]]);
29
30
    // Mix the diagonals.
31
0
    g(state, 0, 5, 10, 15, msg[schedule[8]], msg[schedule[9]]);
32
0
    g(state, 1, 6, 11, 12, msg[schedule[10]], msg[schedule[11]]);
33
0
    g(state, 2, 7, 8, 13, msg[schedule[12]], msg[schedule[13]]);
34
0
    g(state, 3, 4, 9, 14, msg[schedule[14]], msg[schedule[15]]);
35
0
}
36
37
#[inline(always)]
38
0
fn compress_pre(
39
0
    cv: &CVWords,
40
0
    block: &[u8; BLOCK_LEN],
41
0
    block_len: u8,
42
0
    counter: u64,
43
0
    flags: u8,
44
0
) -> [u32; 16] {
45
0
    let block_words = crate::platform::words_from_le_bytes_64(block);
46
47
0
    let mut state = [
48
0
        cv[0],
49
0
        cv[1],
50
0
        cv[2],
51
0
        cv[3],
52
0
        cv[4],
53
0
        cv[5],
54
0
        cv[6],
55
0
        cv[7],
56
0
        IV[0],
57
0
        IV[1],
58
0
        IV[2],
59
0
        IV[3],
60
0
        counter_low(counter),
61
0
        counter_high(counter),
62
0
        block_len as u32,
63
0
        flags as u32,
64
0
    ];
65
66
0
    round(&mut state, &block_words, 0);
67
0
    round(&mut state, &block_words, 1);
68
0
    round(&mut state, &block_words, 2);
69
0
    round(&mut state, &block_words, 3);
70
0
    round(&mut state, &block_words, 4);
71
0
    round(&mut state, &block_words, 5);
72
0
    round(&mut state, &block_words, 6);
73
74
0
    state
75
0
}
76
77
0
pub fn compress_in_place(
78
0
    cv: &mut CVWords,
79
0
    block: &[u8; BLOCK_LEN],
80
0
    block_len: u8,
81
0
    counter: u64,
82
0
    flags: u8,
83
0
) {
84
0
    let state = compress_pre(cv, block, block_len, counter, flags);
85
86
0
    cv[0] = state[0] ^ state[8];
87
0
    cv[1] = state[1] ^ state[9];
88
0
    cv[2] = state[2] ^ state[10];
89
0
    cv[3] = state[3] ^ state[11];
90
0
    cv[4] = state[4] ^ state[12];
91
0
    cv[5] = state[5] ^ state[13];
92
0
    cv[6] = state[6] ^ state[14];
93
0
    cv[7] = state[7] ^ state[15];
94
0
}
95
96
0
pub fn compress_xof(
97
0
    cv: &CVWords,
98
0
    block: &[u8; BLOCK_LEN],
99
0
    block_len: u8,
100
0
    counter: u64,
101
0
    flags: u8,
102
0
) -> [u8; 64] {
103
0
    let mut state = compress_pre(cv, block, block_len, counter, flags);
104
0
    state[0] ^= state[8];
105
0
    state[1] ^= state[9];
106
0
    state[2] ^= state[10];
107
0
    state[3] ^= state[11];
108
0
    state[4] ^= state[12];
109
0
    state[5] ^= state[13];
110
0
    state[6] ^= state[14];
111
0
    state[7] ^= state[15];
112
0
    state[8] ^= cv[0];
113
0
    state[9] ^= cv[1];
114
0
    state[10] ^= cv[2];
115
0
    state[11] ^= cv[3];
116
0
    state[12] ^= cv[4];
117
0
    state[13] ^= cv[5];
118
0
    state[14] ^= cv[6];
119
0
    state[15] ^= cv[7];
120
0
    crate::platform::le_bytes_from_words_64(&state)
121
0
}
122
123
0
pub fn hash1<const N: usize>(
124
0
    input: &[u8; N],
125
0
    key: &CVWords,
126
0
    counter: u64,
127
0
    flags: u8,
128
0
    flags_start: u8,
129
0
    flags_end: u8,
130
0
    out: &mut CVBytes,
131
0
) {
132
0
    debug_assert_eq!(N % BLOCK_LEN, 0, "uneven blocks");
133
0
    let mut cv = *key;
134
0
    let mut block_flags = flags | flags_start;
135
0
    let mut slice = &input[..];
136
0
    while slice.len() >= BLOCK_LEN {
137
0
        if slice.len() == BLOCK_LEN {
138
0
            block_flags |= flags_end;
139
0
        }
140
0
        compress_in_place(
141
0
            &mut cv,
142
0
            array_ref!(slice, 0, BLOCK_LEN),
143
0
            BLOCK_LEN as u8,
144
0
            counter,
145
0
            block_flags,
146
        );
147
0
        block_flags = flags;
148
0
        slice = &slice[BLOCK_LEN..];
149
    }
150
0
    *out = crate::platform::le_bytes_from_words_32(&cv);
151
0
}
Unexecuted instantiation: blake3::portable::hash1::<1024>
Unexecuted instantiation: blake3::portable::hash1::<64>
152
153
0
pub fn hash_many<const N: usize>(
154
0
    inputs: &[&[u8; N]],
155
0
    key: &CVWords,
156
0
    mut counter: u64,
157
0
    increment_counter: IncrementCounter,
158
0
    flags: u8,
159
0
    flags_start: u8,
160
0
    flags_end: u8,
161
0
    out: &mut [u8],
162
0
) {
163
0
    debug_assert!(out.len() >= inputs.len() * OUT_LEN, "out too short");
164
0
    for (&input, output) in inputs.iter().zip(out.chunks_exact_mut(OUT_LEN)) {
165
0
        hash1(
166
0
            input,
167
0
            key,
168
0
            counter,
169
0
            flags,
170
0
            flags_start,
171
0
            flags_end,
172
0
            array_mut_ref!(output, 0, OUT_LEN),
173
        );
174
0
        if increment_counter.yes() {
175
0
            counter += 1;
176
0
        }
177
    }
178
0
}
Unexecuted instantiation: blake3::portable::hash_many::<1024>
Unexecuted instantiation: blake3::portable::hash_many::<64>
179
180
#[cfg(test)]
181
pub mod test {
182
    use super::*;
183
184
    // This is basically testing the portable implementation against itself,
185
    // but it also checks that compress_in_place and compress_xof are
186
    // consistent. And there are tests against the reference implementation and
187
    // against hardcoded test vectors elsewhere.
188
    #[test]
189
    fn test_compress() {
190
        crate::test::test_compress_fn(compress_in_place, compress_xof);
191
    }
192
193
    // Ditto.
194
    #[test]
195
    fn test_hash_many() {
196
        crate::test::test_hash_many_fn(hash_many, hash_many);
197
    }
198
}