risc0_zkp/
taps.rs

1// Copyright 2025 RISC Zero, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use core::{
16    cmp::Ordering,
17    fmt::{self, Debug},
18};
19
20/// This class is an implementation detail and carefully built to be efficient
21/// on RISC-V for use in recursion.
22#[derive(Debug)]
23pub struct TapData {
24    // The offset in register group (reg #)
25    pub offset: u16,
26    // How many cycles back this tap is
27    pub back: u16,
28    // Which register group this tap is a part of
29    pub group: usize,
30    // Which combo this register is part of
31    pub combo: u8,
32    // How far to skip to next register
33    pub skip: u8,
34}
35
36impl PartialEq for TapData {
37    fn eq(&self, other: &Self) -> bool {
38        self.offset == other.offset && self.back == other.back && self.group == other.group
39    }
40}
41
42impl PartialOrd for TapData {
43    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
44        match self.group.partial_cmp(&other.group) {
45            Some(Ordering::Equal) => {}
46            ord => return ord,
47        }
48        match self.offset.partial_cmp(&other.offset) {
49            Some(Ordering::Equal) => {}
50            ord => return ord,
51        }
52        self.back.partial_cmp(&other.back)
53    }
54}
55
56#[derive(Debug)]
57pub struct TapSet<'a> {
58    pub taps: &'a [TapData],
59    pub combo_taps: &'a [u16],
60    pub combo_begin: &'a [u16],
61    pub group_begin: &'a [usize],
62    pub combos_count: usize,
63    pub reg_count: usize,
64    pub tot_combo_backs: usize,
65    pub group_names: &'a [&'a str],
66}
67
68impl TapSet<'_> {
69    pub fn num_groups(&self) -> usize {
70        self.group_names.len()
71    }
72
73    pub fn tap_size(&self) -> usize {
74        self.group_begin[self.num_groups()]
75    }
76
77    pub fn taps(&self) -> TapIter<'_> {
78        TapIter {
79            data: self.taps,
80            cursor: 0,
81            end: self.group_begin[self.num_groups()],
82        }
83    }
84
85    pub fn regs(&self) -> RegisterIter<'_> {
86        RegisterIter {
87            data: self.taps,
88            cursor: 0,
89            end: self.group_begin[self.num_groups()],
90        }
91    }
92
93    pub fn group_taps(&self, group_id: usize) -> TapIter<'_> {
94        TapIter {
95            data: self.taps,
96            cursor: self.group_begin[group_id],
97            end: self.group_begin[group_id + 1],
98        }
99    }
100
101    pub fn group_regs(&self, group_id: usize) -> RegisterIter<'_> {
102        RegisterIter {
103            data: self.taps,
104            cursor: self.group_begin[group_id],
105            end: self.group_begin[group_id + 1],
106        }
107    }
108
109    pub fn group_size(&self, group_id: usize) -> usize {
110        let idx = self.group_begin[group_id + 1] - 1;
111        let last = self.taps[idx].offset as usize;
112        last + 1
113    }
114
115    pub fn group_name(&self, group_id: usize) -> &str {
116        self.group_names[group_id]
117    }
118
119    pub fn combos_size(&self) -> usize {
120        self.combos_count
121    }
122
123    pub fn reg_count(&self) -> usize {
124        self.reg_count
125    }
126
127    pub fn combos(&self) -> ComboIter<'_> {
128        ComboIter {
129            data: ComboData {
130                taps: self.combo_taps,
131                offsets: self.combo_begin,
132            },
133            id: 0,
134            end: self.combos_count,
135        }
136    }
137
138    pub fn get_combo(&self, id: usize) -> ComboRef<'_> {
139        ComboRef {
140            data: ComboData {
141                taps: self.combo_taps,
142                offsets: self.combo_begin,
143            },
144            id,
145        }
146    }
147}
148
149pub struct RegisterRef<'a> {
150    data: &'a [TapData],
151    cursor: usize,
152}
153
154impl RegisterRef<'_> {
155    pub fn group(&self) -> usize {
156        self.data[self.cursor].group
157    }
158
159    pub fn offset(&self) -> usize {
160        self.data[self.cursor].offset as usize
161    }
162
163    pub fn combo_id(&self) -> usize {
164        self.data[self.cursor].combo as usize
165    }
166
167    /// Number of taps in the register
168    pub fn size(&self) -> usize {
169        self.data[self.cursor].skip as usize
170    }
171
172    /// Get a specific 'back' value
173    pub fn back(&self, i: usize) -> usize {
174        self.data[self.cursor + i].back as usize
175    }
176}
177
178impl Debug for RegisterRef<'_> {
179    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
180        f.debug_struct("RegisterRef")
181            .field("group", &self.group())
182            .field("offset", &self.offset())
183            .field("combo_id", &self.combo_id())
184            .field("size", &self.size())
185            .finish()
186    }
187}
188
189impl<'a> IntoIterator for RegisterRef<'a> {
190    type Item = TapRef<'a>;
191    type IntoIter = TapIter<'a>;
192
193    fn into_iter(self) -> Self::IntoIter {
194        TapIter {
195            data: self.data,
196            cursor: self.cursor,
197            end: self.cursor + self.size(),
198        }
199    }
200}
201
202pub struct RegisterIter<'a> {
203    data: &'a [TapData],
204    cursor: usize,
205    end: usize,
206}
207
208impl<'a> Iterator for RegisterIter<'a> {
209    type Item = RegisterRef<'a>;
210
211    fn next(&mut self) -> Option<Self::Item> {
212        let cursor = self.cursor;
213        if cursor >= self.data.len() {
214            return None;
215        }
216        let next = cursor + self.data[cursor].skip as usize;
217        if next > self.end {
218            return None;
219        }
220        self.cursor = next;
221        Some(RegisterRef {
222            data: self.data,
223            cursor,
224        })
225    }
226}
227
228pub struct TapRef<'a> {
229    data: &'a TapData,
230}
231
232impl TapRef<'_> {
233    pub fn group(&self) -> usize {
234        self.data.group
235    }
236
237    pub fn offset(&self) -> usize {
238        self.data.offset as usize
239    }
240
241    pub fn back(&self) -> usize {
242        self.data.back as usize
243    }
244
245    pub fn combo_id(&self) -> usize {
246        self.data.combo as usize
247    }
248}
249
250impl Debug for TapRef<'_> {
251    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
252        f.debug_struct("TapRef")
253            .field("group", &self.group())
254            .field("offset", &self.offset())
255            .field("back", &self.back())
256            .field("combo_id", &self.combo_id())
257            .finish()
258    }
259}
260
261pub struct TapIter<'a> {
262    data: &'a [TapData],
263    cursor: usize,
264    end: usize,
265}
266
267impl<'a> Iterator for TapIter<'a> {
268    type Item = TapRef<'a>;
269
270    fn next(&mut self) -> Option<Self::Item> {
271        let cursor = self.cursor;
272        let next = self.cursor + 1;
273        if next > self.end {
274            return None;
275        }
276        self.cursor = next;
277        Some(TapRef {
278            data: &self.data[cursor],
279        })
280    }
281}
282
283/// Combo data holds the tap set for each 'combo'.
284///
285/// Basically, combo N consists of taps in the range [offsets\[n\],
286/// offsets\[n+1\]). Again this is an implementation detail, and the format is
287/// designed to put the actual arrays into static data.
288#[derive(Clone)]
289pub struct ComboData<'a> {
290    taps: &'a [u16],
291    offsets: &'a [u16],
292}
293
294pub struct ComboRef<'a> {
295    data: ComboData<'a>,
296    id: usize,
297}
298
299impl ComboRef<'_> {
300    pub fn id(&self) -> usize {
301        self.id
302    }
303
304    pub fn size(&self) -> usize {
305        self.next_offset() - self.self_offset()
306    }
307
308    pub fn slice(&self) -> &[u16] {
309        &self.data.taps[self.self_offset()..self.next_offset()]
310    }
311
312    fn self_offset(&self) -> usize {
313        self.data.offsets[self.id] as usize
314    }
315
316    fn next_offset(&self) -> usize {
317        self.data.offsets[self.id + 1] as usize
318    }
319}
320
321pub struct ComboIter<'a> {
322    data: ComboData<'a>,
323    id: usize,
324    end: usize,
325}
326
327impl<'a> Iterator for ComboIter<'a> {
328    type Item = ComboRef<'a>;
329
330    fn next(&mut self) -> Option<Self::Item> {
331        let id = self.id;
332        let next = self.id + 1;
333        if next > self.end {
334            return None;
335        }
336        self.id = next;
337        Some(ComboRef {
338            data: self.data.clone(),
339            id,
340        })
341    }
342}