1
+ use crate :: serde:: RandomState ;
2
+ use bitvec:: prelude:: * ;
3
+ use bitvec:: vec:: BitVec ;
1
4
/// When deserializing a clvm object, a stack of deserialized child objects
2
5
/// is created, which can be used with back-references. A `ReadCacheLookup` keeps
3
6
/// track of the state of this stack and all child objects under each root
@@ -28,10 +31,10 @@ pub struct ReadCacheLookup {
28
31
/// the tree hashes of the contents on the left and right
29
32
read_stack : Vec < ( Bytes32 , Bytes32 ) > ,
30
33
31
- count : HashMap < Bytes32 , u32 > ,
34
+ count : HashMap < Bytes32 , u32 , RandomState > ,
32
35
33
36
/// a mapping of tree hashes to `(parent, is_right)` tuples
34
- parent_lookup : HashMap < Bytes32 , Vec < ( Bytes32 , u8 ) > > ,
37
+ parent_lookup : HashMap < Bytes32 , Vec < ( Bytes32 , bool ) > , RandomState > ,
35
38
}
36
39
37
40
impl Default for ReadCacheLookup {
@@ -43,10 +46,12 @@ impl Default for ReadCacheLookup {
43
46
impl ReadCacheLookup {
44
47
pub fn new ( ) -> Self {
45
48
let root_hash = hash_blob ( & [ 1 ] ) ;
46
- let read_stack = vec ! [ ] ;
47
- let mut count = HashMap :: default ( ) ;
49
+ let read_stack = Vec :: with_capacity ( 1000 ) ;
50
+ // all keys in count and parent_lookup are tree-hashes. There's no need
51
+ // to hash them again for the hash map
52
+ let mut count = HashMap :: with_hasher ( RandomState :: default ( ) ) ;
48
53
count. insert ( root_hash, 1 ) ;
49
- let parent_lookup = HashMap :: default ( ) ;
54
+ let parent_lookup = HashMap :: with_hasher ( RandomState :: default ( ) ) ;
50
55
Self {
51
56
root_hash,
52
57
read_stack,
@@ -67,13 +72,13 @@ impl ReadCacheLookup {
67
72
* self . count . entry ( id) . or_insert ( 0 ) += 1 ;
68
73
* self . count . entry ( new_root_hash) . or_insert ( 0 ) += 1 ;
69
74
70
- let new_parent_to_old_root = ( new_root_hash, 0 ) ;
75
+ let new_parent_to_old_root = ( new_root_hash, false ) ;
71
76
self . parent_lookup
72
77
. entry ( id)
73
78
. or_default ( )
74
79
. push ( new_parent_to_old_root) ;
75
80
76
- let new_parent_to_id = ( new_root_hash, 1 ) ;
81
+ let new_parent_to_id = ( new_root_hash, true ) ;
77
82
self . parent_lookup
78
83
. entry ( self . root_hash )
79
84
. or_default ( )
@@ -108,31 +113,41 @@ impl ReadCacheLookup {
108
113
self . parent_lookup
109
114
. entry ( left. 0 )
110
115
. or_default ( )
111
- . push ( ( new_root_hash, 0 ) ) ;
116
+ . push ( ( new_root_hash, false ) ) ;
112
117
113
118
self . parent_lookup
114
119
. entry ( right. 0 )
115
120
. or_default ( )
116
- . push ( ( new_root_hash, 1 ) ) ;
121
+ . push ( ( new_root_hash, true ) ) ;
117
122
118
123
self . push ( new_root_hash) ;
119
124
}
120
125
121
126
/// return the list of minimal-length paths to the given hash which will serialize to no larger
122
127
/// than the given size (or an empty list if no such path exists)
123
128
pub fn find_paths ( & self , id : & Bytes32 , serialized_length : u64 ) -> Vec < Vec < u8 > > {
124
- let mut seen_ids = HashSet :: < & Bytes32 > :: default ( ) ;
125
- let mut possible_responses = vec ! [ ] ;
126
- if serialized_length < 3 {
127
- return possible_responses ;
129
+ // this function is not cheap. only keep going if there's potential to
130
+ // save enough bytes
131
+ if serialized_length < 4 {
132
+ return vec ! [ ] ;
128
133
}
129
- assert ! ( serialized_length > 2 ) ;
134
+
135
+ let mut possible_responses = Vec :: with_capacity ( 50 ) ;
136
+
137
+ // all the values we put in this hash set are themselves sha256 hashes.
138
+ // There's no point in hashing the hashes
139
+ let mut seen_ids = HashSet :: < & Bytes32 , RandomState > :: with_capacity_and_hasher (
140
+ 1000 ,
141
+ RandomState :: default ( ) ,
142
+ ) ;
143
+
130
144
let max_bytes_for_path_encoding = serialized_length - 2 ; // 1 byte for 0xfe, 1 min byte for savings
131
145
let max_path_length: usize = ( max_bytes_for_path_encoding. saturating_mul ( 8 ) - 1 )
132
146
. try_into ( )
133
147
. unwrap_or ( usize:: MAX ) ;
134
148
seen_ids. insert ( id) ;
135
- let mut partial_paths = vec ! [ ( * id, vec![ ] ) ] ;
149
+ let mut partial_paths = Vec :: with_capacity ( 500 ) ;
150
+ partial_paths. push ( ( * id, BitVec :: with_capacity ( 100 ) ) ) ;
136
151
137
152
while !partial_paths. is_empty ( ) {
138
153
let mut new_partial_paths = vec ! [ ] ;
@@ -147,11 +162,11 @@ impl ReadCacheLookup {
147
162
for ( parent, direction) in items. iter ( ) {
148
163
if * ( self . count . get ( parent) . unwrap_or ( & 0 ) ) > 0 && !seen_ids. contains ( parent)
149
164
{
150
- let mut new_path = path. clone ( ) ;
151
- new_path. push ( * direction) ;
152
- if new_path. len ( ) > max_path_length {
165
+ if path. len ( ) + 1 > max_path_length {
153
166
return possible_responses;
154
167
}
168
+ let mut new_path = path. clone ( ) ;
169
+ new_path. push ( * direction) ;
155
170
new_partial_paths. push ( ( * parent, new_path) ) ;
156
171
}
157
172
seen_ids. insert ( parent) ;
@@ -185,13 +200,13 @@ impl ReadCacheLookup {
185
200
/// If `A` => `v` then `[A] + [0]` => `v * 2` and `[A] + [1]` => `v * 2 + 1`
186
201
/// Then the integer is turned into the minimal-length array of `u8` representing
187
202
/// that value as an unsigned integer.
188
- fn reversed_path_to_vec_u8 ( path : & [ u8 ] ) -> Vec < u8 > {
203
+ fn reversed_path_to_vec_u8 ( path : & BitSlice ) -> Vec < u8 > {
189
204
let byte_count = ( path. len ( ) + 1 + 7 ) >> 3 ;
190
205
let mut v = vec ! [ 0 ; byte_count] ;
191
206
let mut index = byte_count - 1 ;
192
207
let mut mask: u8 = 1 ;
193
208
for p in path. iter ( ) . rev ( ) {
194
- if * p != 0 {
209
+ if p != false {
195
210
v[ index] |= mask;
196
211
}
197
212
mask = {
@@ -213,30 +228,33 @@ mod tests {
213
228
214
229
#[ test]
215
230
fn test_path_to_vec_u8 ( ) {
216
- assert_eq ! ( reversed_path_to_vec_u8( & [ ] ) , vec!( 0b1 ) ) ;
217
- assert_eq ! ( reversed_path_to_vec_u8( & [ 0 ] ) , vec!( 0b10 ) ) ;
218
- assert_eq ! ( reversed_path_to_vec_u8( & [ 1 ] ) , vec!( 0b11 ) ) ;
219
- assert_eq ! ( reversed_path_to_vec_u8( & [ 0 , 0 ] ) , vec!( 0b100 ) ) ;
220
- assert_eq ! ( reversed_path_to_vec_u8( & [ 0 , 1 ] ) , vec!( 0b101 ) ) ;
221
- assert_eq ! ( reversed_path_to_vec_u8( & [ 1 , 0 ] ) , vec!( 0b110 ) ) ;
222
- assert_eq ! ( reversed_path_to_vec_u8( & [ 1 , 1 ] ) , vec!( 0b111 ) ) ;
223
- assert_eq ! ( reversed_path_to_vec_u8( & [ 1 , 1 , 1 ] ) , vec!( 0b1111 ) ) ;
224
- assert_eq ! ( reversed_path_to_vec_u8( & [ 0 , 1 , 1 , 1 ] ) , vec!( 0b10111 ) ) ;
225
- assert_eq ! ( reversed_path_to_vec_u8( & [ 1 , 0 , 1 , 1 , 1 ] ) , vec!( 0b110111 ) ) ;
231
+ assert_eq ! ( reversed_path_to_vec_u8( bits![ ] ) , vec!( 0b1 ) ) ;
232
+ assert_eq ! ( reversed_path_to_vec_u8( bits![ 0 ] ) , vec!( 0b10 ) ) ;
233
+ assert_eq ! ( reversed_path_to_vec_u8( bits![ 1 ] ) , vec!( 0b11 ) ) ;
234
+ assert_eq ! ( reversed_path_to_vec_u8( bits![ 0 , 0 ] ) , vec!( 0b100 ) ) ;
235
+ assert_eq ! ( reversed_path_to_vec_u8( bits![ 0 , 1 ] ) , vec!( 0b101 ) ) ;
236
+ assert_eq ! ( reversed_path_to_vec_u8( bits![ 1 , 0 ] ) , vec!( 0b110 ) ) ;
237
+ assert_eq ! ( reversed_path_to_vec_u8( bits![ 1 , 1 ] ) , vec!( 0b111 ) ) ;
238
+ assert_eq ! ( reversed_path_to_vec_u8( bits![ 1 , 1 , 1 ] ) , vec!( 0b1111 ) ) ;
239
+ assert_eq ! ( reversed_path_to_vec_u8( bits![ 0 , 1 , 1 , 1 ] ) , vec!( 0b10111 ) ) ;
240
+ assert_eq ! (
241
+ reversed_path_to_vec_u8( bits![ 1 , 0 , 1 , 1 , 1 ] ) ,
242
+ vec!( 0b110111 )
243
+ ) ;
226
244
assert_eq ! (
227
- reversed_path_to_vec_u8( & [ 1 , 1 , 0 , 1 , 1 , 1 ] ) ,
245
+ reversed_path_to_vec_u8( bits! [ 1 , 1 , 0 , 1 , 1 , 1 ] ) ,
228
246
vec!( 0b1110111 )
229
247
) ;
230
248
assert_eq ! (
231
- reversed_path_to_vec_u8( & [ 0 , 1 , 1 , 0 , 1 , 1 , 1 ] ) ,
249
+ reversed_path_to_vec_u8( bits! [ 0 , 1 , 1 , 0 , 1 , 1 , 1 ] ) ,
232
250
vec!( 0b10110111 )
233
251
) ;
234
252
assert_eq ! (
235
- reversed_path_to_vec_u8( & [ 0 , 0 , 1 , 1 , 0 , 1 , 1 , 1 ] ) ,
253
+ reversed_path_to_vec_u8( bits! [ 0 , 0 , 1 , 1 , 0 , 1 , 1 , 1 ] ) ,
236
254
vec!( 0b1 , 0b00110111 )
237
255
) ;
238
256
assert_eq ! (
239
- reversed_path_to_vec_u8( & [ 1 , 0 , 0 , 1 , 1 , 0 , 1 , 1 , 1 ] ) ,
257
+ reversed_path_to_vec_u8( bits! [ 1 , 0 , 0 , 1 , 1 , 0 , 1 , 1 , 1 ] ) ,
240
258
vec!( 0b11 , 0b00110111 )
241
259
) ;
242
260
}
0 commit comments