1- use std:: collections:: HashMap ;
21use std:: marker:: PhantomData ;
32use std:: sync:: { Arc , RwLock } ;
43
@@ -13,17 +12,80 @@ use crate::settings;
1312pub const EXP_DEGREE : usize = 8 ;
1413
1514// Cache of node's parents.
16- pub type ParentCache = HashMap < usize , Vec < usize > > ;
15+ pub type ParentCache = Vec < Option < Vec < u32 > > > ;
1716
1817// ZigZagGraph will hold two different (but related) `ParentCache`,
1918// the first one for the `forward` direction and the second one
2019// for the `reversed`.
21- pub type TwoWayParentCache = [ ParentCache ; 2 ] ;
20+ #[ derive( Debug , Clone ) ]
21+ pub struct ShareableParentCache {
22+ forward : Arc < RwLock < ParentCache > > ,
23+ reverse : Arc < RwLock < ParentCache > > ,
24+ // Keep the size of the cache outside the lock to be easily accessible.
25+ cache_entries : u32 ,
26+ }
27+
28+ impl ShareableParentCache {
29+ pub fn new ( cache_entries : u32 ) -> Self {
30+ ShareableParentCache {
31+ forward : Arc :: new ( RwLock :: new ( vec ! [ None ; cache_entries as usize ] ) ) ,
32+ reverse : Arc :: new ( RwLock :: new ( vec ! [ None ; cache_entries as usize ] ) ) ,
33+ cache_entries,
34+ }
35+ }
36+
37+ pub fn contains_forward ( & self , node : u32 ) -> bool {
38+ assert ! ( node < self . cache_entries) ;
39+ self . forward . read ( ) . unwrap ( ) [ node as usize ] . is_some ( )
40+ }
41+
42+ pub fn contains_reverse ( & self , node : u32 ) -> bool {
43+ assert ! ( node < self . cache_entries) ;
44+ self . reverse . read ( ) . unwrap ( ) [ node as usize ] . is_some ( )
45+ }
46+
47+ pub fn read_forward < F , T > ( & self , node : u32 , mut cb : F ) -> T
48+ where
49+ F : FnMut ( Option < & Vec < u32 > > ) -> T ,
50+ {
51+ assert ! ( node < self . cache_entries) ;
52+ let lock = self . forward . read ( ) . unwrap ( ) ;
53+ cb ( lock[ node as usize ] . as_ref ( ) )
54+ }
55+
56+ pub fn read_reverse < F , T > ( & self , node : u32 , mut cb : F ) -> T
57+ where
58+ F : FnMut ( Option < & Vec < u32 > > ) -> T ,
59+ {
60+ assert ! ( node < self . cache_entries) ;
61+ let lock = self . reverse . read ( ) . unwrap ( ) ;
62+ cb ( lock[ node as usize ] . as_ref ( ) )
63+ }
64+
65+ pub fn write_forward ( & self , node : u32 , parents : Vec < u32 > ) {
66+ assert ! ( node < self . cache_entries) ;
67+
68+ let mut write_lock = self . forward . write ( ) . unwrap ( ) ;
69+
70+ let old_value = std:: mem:: replace ( & mut write_lock[ node as usize ] , Some ( parents) ) ;
71+
72+ debug_assert_eq ! ( old_value, None ) ;
73+ // We shouldn't be rewriting entries (with most likely the same values),
74+ // this would be a clear indication of a bug.
75+ }
76+
77+ pub fn write_reverse ( & self , node : u32 , parents : Vec < u32 > ) {
78+ assert ! ( node < self . cache_entries) ;
79+
80+ let mut write_lock = self . reverse . write ( ) . unwrap ( ) ;
2281
23- // The cache is hold in an `Arc` to make it available across different
24- // threads. It is accessed through a `RwLock` to distinguish between
25- // read an write operations.
26- pub type ShareableParentCache = Arc < RwLock < TwoWayParentCache > > ;
82+ let old_value = std:: mem:: replace ( & mut write_lock[ node as usize ] , Some ( parents) ) ;
83+
84+ debug_assert_eq ! ( old_value, None ) ;
85+ // We shouldn't be rewriting entries (with most likely the same values),
86+ // this would be a clear indication of a bug.
87+ }
88+ }
2789
2890#[ derive( Debug , Clone ) ]
2991pub struct ZigZagGraph < H , G >
44106 // all entries sequentially when encoding or any random entry once when proving
45107 // or verifying, but there's no locality to take advantage of so keep the logic
46108 // as simple as possible).
47- parents_cache : ShareableParentCache ,
48- // Keep the size of the cache outside the lock to be easily accessible.
49- cache_entries : usize ,
109+ parents_cache : Option < ShareableParentCache > ,
50110 _h : PhantomData < H > ,
51111}
52112
@@ -76,11 +136,12 @@ where
76136 assert_eq ! ( expansion_degree, EXP_DEGREE ) ;
77137 }
78138
79- let cache_entries = if settings:: SETTINGS . lock ( ) . unwrap ( ) . maximize_caching {
139+ let parents_cache = if settings:: SETTINGS . lock ( ) . unwrap ( ) . maximize_caching {
80140 info ! ( "using parents cache of unlimited size" , ) ;
81- nodes
141+ assert ! ( nodes <= std:: u32 :: MAX as usize ) ;
142+ Some ( ShareableParentCache :: new ( nodes as u32 ) )
82143 } else {
83- 0
144+ None
84145 } ;
85146
86147 ZigZagGraph {
@@ -91,11 +152,7 @@ where
91152 expansion_degree,
92153 reversed : false ,
93154 feistel_precomputed : feistel:: precompute ( ( expansion_degree * nodes) as feistel:: Index ) ,
94- parents_cache : Arc :: new ( RwLock :: new ( [
95- HashMap :: with_capacity ( cache_entries) ,
96- HashMap :: with_capacity ( cache_entries) ,
97- ] ) ) ,
98- cache_entries,
155+ parents_cache,
99156 _h : PhantomData ,
100157 }
101158 }
@@ -131,7 +188,9 @@ pub trait ZigZag: ::std::fmt::Debug + Clone + PartialEq + Eq {
131188 fn base_graph ( & self ) -> Self :: BaseGraph ;
132189 fn expansion_degree ( & self ) -> usize ;
133190 fn reversed ( & self ) -> bool ;
134- fn expanded_parents ( & self , node : usize ) -> Vec < usize > ;
191+ fn expanded_parents < F , T > ( & self , node : usize , cb : F ) -> T
192+ where
193+ F : FnMut ( & Vec < u32 > ) -> T ;
135194 fn real_index ( & self , i : usize ) -> usize ;
136195 fn new_zigzag (
137196 nodes : usize ,
@@ -163,21 +222,21 @@ impl<Z: ZigZag> Graph<Z::BaseHasher> for Z {
163222 }
164223
165224 // expanded_parents takes raw_node
166- let expanded_parents = self . expanded_parents ( raw_node) ;
167-
168- for ( ii, value) in expanded_parents. iter ( ) . enumerate ( ) {
169- parents[ ii + self . base_graph ( ) . degree ( ) ] = * value
170- }
225+ self . expanded_parents ( raw_node, |expanded_parents| {
226+ for ( ii, value) in expanded_parents. iter ( ) . enumerate ( ) {
227+ parents[ ii + self . base_graph ( ) . degree ( ) ] = * value as usize
228+ }
171229
172- // Pad so all nodes have correct degree.
173- let current_length = self . base_graph ( ) . degree ( ) + expanded_parents. len ( ) ;
174- for ii in 0 ..( self . degree ( ) - current_length) {
175- if self . reversed ( ) {
176- parents[ ii + current_length] = self . size ( ) - 1
177- } else {
178- parents[ ii + current_length] = 0
230+ // Pad so all nodes have correct degree.
231+ let current_length = self . base_graph ( ) . degree ( ) + expanded_parents. len ( ) ;
232+ for ii in 0 ..( self . degree ( ) - current_length) {
233+ if self . reversed ( ) {
234+ parents[ ii + current_length] = self . size ( ) - 1
235+ } else {
236+ parents[ ii + current_length] = 0
237+ }
179238 }
180- }
239+ } ) ;
181240 assert ! ( parents. len( ) == self . degree( ) ) ;
182241 parents. sort ( ) ;
183242
@@ -266,53 +325,20 @@ where
266325 // back this function in the `reversed` direction).
267326 }
268327
269- // The first cache in `parents_cache` corresponds to the forward direction,
270- // the second one to the reversed.
271- fn get_cache_index ( & self ) -> usize {
272- if self . forward ( ) {
273- 0
274- } else {
275- 1
276- }
277- }
278-
279328 // Read the `node` entry in the parents cache (which may not exist) for
280329 // the current direction set in the graph and return a copy of it (or
281330 // `None` to signal a cache miss).
282- fn read_parents_cache ( & self , node : usize ) -> Option < Vec < usize > > {
283- // If the index exceeds the cache size don't bother checking.
284- if node >= self . cache_entries {
285- return None ;
286- }
287-
288- let read_lock = self . parents_cache . read ( ) . unwrap ( ) ;
289-
290- let parents_cache = & ( * read_lock) [ self . get_cache_index ( ) ] ;
291-
292- if let Some ( parents) = parents_cache. get ( & node) {
293- Some ( parents. clone ( ) )
331+ fn contains_parents_cache ( & self , node : usize ) -> bool {
332+ if let Some ( ref cache) = self . parents_cache {
333+ if self . forward ( ) {
334+ cache. contains_forward ( node as u32 )
335+ } else {
336+ cache. contains_reverse ( node as u32 )
337+ }
294338 } else {
295- None
339+ false
296340 }
297341 }
298-
299- // Save the `parents` of the `node` in its entry of the cache.
300- fn write_parents_cache ( & self , node : usize , parents : Vec < usize > ) {
301- // Don't allow writing more entries than the already allocated space.
302- if node >= self . cache_entries {
303- return ;
304- }
305-
306- let mut write_lock = self . parents_cache . write ( ) . unwrap ( ) ;
307-
308- let parents_cache = & mut ( * write_lock) [ self . get_cache_index ( ) ] ;
309-
310- let old_value = parents_cache. insert ( node, parents) ;
311-
312- debug_assert_eq ! ( old_value, None ) ;
313- // We shouldn't be rewriting entries (with most likely the same values),
314- // this would be a clear indication of a bug.
315- }
316342}
317343
318344impl < ' a , H , G > ZigZag for ZigZagGraph < H , G >
@@ -365,31 +391,48 @@ where
365391 // we would only need to compute the parents in one direction and with
366392 // that fill both caches.
367393 #[ inline]
368- fn expanded_parents ( & self , node : usize ) -> Vec < usize > {
369- if let Some ( parents) = self . read_parents_cache ( node) {
370- return parents;
371- }
372-
373- let parents: Vec < usize > = ( 0 ..self . expansion_degree )
374- . filter_map ( |i| {
375- let other = self . correspondent ( node, i) ;
376- if self . reversed {
377- if other > node {
378- Some ( other)
394+ fn expanded_parents < F , T > ( & self , node : usize , mut cb : F ) -> T
395+ where
396+ F : FnMut ( & Vec < u32 > ) -> T ,
397+ {
398+ if !self . contains_parents_cache ( node) {
399+ let parents: Vec < u32 > = ( 0 ..self . expansion_degree )
400+ . filter_map ( |i| {
401+ let other = self . correspondent ( node, i) ;
402+ if self . reversed {
403+ if other > node {
404+ Some ( other as u32 )
405+ } else {
406+ None
407+ }
408+ } else if other < node {
409+ Some ( other as u32 )
379410 } else {
380411 None
381412 }
382- } else if other < node {
383- Some ( other)
413+ } )
414+ . collect ( ) ;
415+
416+ if let Some ( ref cache) = self . parents_cache {
417+ if self . forward ( ) {
418+ cache. write_forward ( node as u32 , parents) ;
384419 } else {
385- None
420+ cache . write_reverse ( node as u32 , parents ) ;
386421 }
387- } )
388- . collect ( ) ;
389-
390- self . write_parents_cache ( node , parents . clone ( ) ) ;
422+ } else {
423+ return cb ( & parents ) ;
424+ }
425+ }
391426
392- parents
427+ if let Some ( ref cache) = self . parents_cache {
428+ if self . forward ( ) {
429+ cache. read_forward ( node as u32 , |parents| cb ( parents. unwrap ( ) ) )
430+ } else {
431+ cache. read_reverse ( node as u32 , |parents| cb ( parents. unwrap ( ) ) )
432+ }
433+ } else {
434+ unreachable ! ( )
435+ }
393436 }
394437
395438 #[ inline]
@@ -515,40 +558,34 @@ mod tests {
515558 // Check to make sure all (expanded) node-parent relationships also exist in reverse,
516559 // in the original graph's Hashmap.
517560 for p in parents {
518- assert ! ( gcache[ & p ] . contains( & i ) ) ;
561+ assert ! ( gcache[ & ( * p as usize ) ] . contains( & ( i as u32 ) ) ) ;
519562 }
520563 }
521564
522565 // And then do the same check to make sure all (expanded) node-parent relationships from the original
523566 // are present in the zigzag, just reversed.
524567 for i in 0 ..g. size ( ) {
525- let parents = g. expanded_parents ( i) ;
526- for p in parents {
527- assert ! ( gzcache[ & p] . contains( & i) ) ;
528- }
568+ g. expanded_parents ( i, |parents| {
569+ for p in parents. iter ( ) {
570+ assert ! ( gzcache[ & ( * p as usize ) ] . contains( & ( i as u32 ) ) ) ;
571+ }
572+ } ) ;
529573 }
530574 // Having checked both ways, we know the graph and its zigzag counterpart have 'expanded' components
531575 // which are each other's inverses. It's important that this be true.
532576 }
533577
534578 fn get_all_expanded_parents < H : ' static + Hasher > (
535579 zigzag_graph : & ZigZagBucketGraph < H > ,
536- ) -> HashMap < usize , Vec < usize > > {
537- let mut parents_map: HashMap < usize , Vec < usize > > = HashMap :: new ( ) ;
580+ ) -> HashMap < usize , Vec < u32 > > {
581+ let mut parents_map: HashMap < usize , Vec < u32 > > = HashMap :: new ( ) ;
538582 for i in 0 ..zigzag_graph. size ( ) {
539- parents_map. insert ( i, zigzag_graph. expanded_parents ( i) ) ;
583+ parents_map. insert ( i, zigzag_graph. expanded_parents ( i, |p| p . clone ( ) ) ) ;
540584 }
541585
542- assert_eq ! ( get_cache_size( & zigzag_graph) , zigzag_graph. cache_entries) ;
543-
544586 parents_map
545587 }
546588
547- fn get_cache_size < H : ' static + Hasher > ( zigzag_graph : & ZigZagBucketGraph < H > ) -> usize {
548- let parents_cache_lock = zigzag_graph. parents_cache . read ( ) . unwrap ( ) ;
549- ( * parents_cache_lock) [ zigzag_graph. get_cache_index ( ) ] . len ( )
550- }
551-
552589 // Test that 3 (or more) rounds of the Feistel cipher can be used
553590 // as a pseudorandom permutation, that is, each input will be mapped
554591 // to a unique output (and though not test here, since the cipher
0 commit comments