44
44
* that a certain key is mapped to a particular value <em>or</em> that there are no mapping for
45
45
* the key in the map.
46
46
*
47
- * <p>This map is implemented as a Merkle-Patricia tree. It does not permit null keys and values,
48
- * and requires that keys are 32-byte long.
47
+ * <p>This map is implemented as a Merkle-Patricia tree. It does not permit null keys and values.
48
+ *
49
+ * <p>The Merkle-Patricia tree backing the proof map uses internal 32-byte keys. The tree balance
50
+ * relies on the internal keys being uniformly distributed.
51
+ *
52
+ * <h3><a name="key-hashing">Key hashing in proof maps</a></h3>
53
+ *
54
+ * <p>By default, when creating the proof map using methods
55
+ * {@link #newInstance(String, View, Serializer, Serializer)} and
56
+ * {@link #newInGroupUnsafe(String, byte[], View, Serializer, Serializer)}, the user keys are
57
+ * converted into internal keys through hashing. This allows to use keys of arbitrary size and
58
+ * ensures the balance of the internal tree. It is also possible to create a proof map that will
59
+ * not hash keys with methods {@link #newInstanceNoKeyHashing(String, View, Serializer, Serializer)}
60
+ * and {@link #newInGroupUnsafeNoKeyHashing(String, byte[], View, Serializer, Serializer)}. In this
61
+ * mode, the map will use the user keys as internal tree keys. Such mode of operation is
62
+ * appropriate iff all of the following conditions hold:
63
+ *
64
+ * <ul>
65
+ * <li>All keys are 32-byte long</li>
66
+ * <li>The keys are uniformly distributed</li>
67
+ * <li>The keys come from a trusted source that cannot influence their distribution and affect
68
+ * the tree balance.</li>
69
+ * </ul>
49
70
*
50
71
* <p>The "destructive" methods of the map, i.e., the one that change the map contents,
51
72
* are specified to throw {@link UnsupportedOperationException} if
58
79
* <p>When the view goes out of scope, this map is destroyed. Subsequent use of the closed map
59
80
* is prohibited and will result in {@link IllegalStateException}.
60
81
*
61
- * @param <K> the type of keys in this map. Must be 32-byte long in the serialized form
82
+ * @param <K> the type of keys in this map
62
83
* @param <V> the type of values in this map
63
84
* @see View
64
85
*/
65
86
public final class ProofMapIndexProxy <K , V > extends AbstractIndexProxy implements MapIndex <K , V > {
66
87
67
- private final ProofMapKeyCheckingSerializerDecorator <K > keySerializer ;
88
+ private final Serializer <K > keySerializer ;
68
89
private final CheckingSerializerDecorator <V > valueSerializer ;
69
90
70
91
/**
@@ -74,7 +95,7 @@ public final class ProofMapIndexProxy<K, V> extends AbstractIndexProxy implement
74
95
* [a-zA-Z0-9_]
75
96
* @param view a database view. Must be valid.
76
97
* If a view is read-only, "destructive" operations are not permitted.
77
- * @param keySerializer a serializer of keys, must always produce 32-byte long values
98
+ * @param keySerializer a serializer of keys
78
99
* @param valueSerializer a serializer of values
79
100
* @param <K> the type of keys in the map
80
101
* @param <V> the type of values in the map
@@ -86,9 +107,34 @@ public static <K, V> ProofMapIndexProxy<K, V> newInstance(
86
107
String name , View view , Serializer <K > keySerializer , Serializer <V > valueSerializer ) {
87
108
IndexAddress address = IndexAddress .valueOf (name );
88
109
long viewNativeHandle = view .getViewNativeHandle ();
89
- LongSupplier nativeMapConstructor = () -> nativeCreate (name , viewNativeHandle );
110
+ LongSupplier nativeMapConstructor = () -> nativeCreate (name , viewNativeHandle , true );
111
+
112
+ return getOrCreate (address , view , keySerializer , valueSerializer , nativeMapConstructor , true );
113
+ }
114
+
115
+ /**
116
+ * Creates a <a href="ProofMapIndexProxy.html#key-hashing">ProofMapIndexProxy that uses non-hashed keys</a>.
117
+ * Requires that keys are 32-byte long.
118
+ *
119
+ * @param name a unique alphanumeric non-empty identifier of this map in the underlying storage:
120
+ * [a-zA-Z0-9_]
121
+ * @param view a database view. Must be valid.
122
+ * If a view is read-only, "destructive" operations are not permitted.
123
+ * @param keySerializer a serializer of keys, must always produce 32-byte long values
124
+ * @param valueSerializer a serializer of values
125
+ * @param <K> the type of keys in the map
126
+ * @param <V> the type of values in the map
127
+ * @throws IllegalStateException if the view is not valid
128
+ * @throws IllegalArgumentException if the name is empty
129
+ * @see StandardSerializers
130
+ */
131
+ public static <K , V > ProofMapIndexProxy <K , V > newInstanceNoKeyHashing (
132
+ String name , View view , Serializer <K > keySerializer , Serializer <V > valueSerializer ) {
133
+ IndexAddress address = IndexAddress .valueOf (name );
134
+ long viewNativeHandle = view .getViewNativeHandle ();
135
+ LongSupplier nativeMapConstructor = () -> nativeCreate (name , viewNativeHandle , false );
90
136
91
- return getOrCreate (address , view , keySerializer , valueSerializer , nativeMapConstructor );
137
+ return getOrCreate (address , view , keySerializer , valueSerializer , nativeMapConstructor , false );
92
138
}
93
139
94
140
/**
@@ -100,6 +146,36 @@ public static <K, V> ProofMapIndexProxy<K, V> newInstance(
100
146
* @param groupName a name of the collection group
101
147
* @param mapId an identifier of this collection in the group, see the caveats
102
148
* @param view a database view
149
+ * @param keySerializer a serializer of keys
150
+ * @param valueSerializer a serializer of values
151
+ * @param <K> the type of keys in the map
152
+ * @param <V> the type of values in the map
153
+ * @return a new map proxy
154
+ * @throws IllegalStateException if the view is not valid
155
+ * @throws IllegalArgumentException if the name or index id is empty
156
+ * @see StandardSerializers
157
+ */
158
+ public static <K , V > ProofMapIndexProxy <K , V > newInGroupUnsafe (
159
+ String groupName , byte [] mapId , View view , Serializer <K > keySerializer ,
160
+ Serializer <V > valueSerializer ) {
161
+ IndexAddress address = IndexAddress .valueOf (groupName , mapId );
162
+ long viewNativeHandle = view .getViewNativeHandle ();
163
+ LongSupplier nativeMapConstructor =
164
+ () -> nativeCreateInGroup (groupName , mapId , viewNativeHandle , true );
165
+
166
+ return getOrCreate (address , view , keySerializer , valueSerializer , nativeMapConstructor , true );
167
+ }
168
+
169
+ /**
170
+ * Creates a new <a href="ProofMapIndexProxy.html#key-hashing">proof map that uses non-hashed keys</a>
171
+ * in a <a href="package-summary.html#families">collection group</a> with the given name.
172
+ * Requires that keys are 32-byte long.
173
+ *
174
+ * <p>See a <a href="package-summary.html#families-limitations">caveat</a> on index identifiers.
175
+ *
176
+ * @param groupName a name of the collection group
177
+ * @param mapId an identifier of this collection in the group, see the caveats
178
+ * @param view a database view
103
179
* @param keySerializer a serializer of keys, must always produce 32-byte long values
104
180
* @param valueSerializer a serializer of values
105
181
* @param <K> the type of keys in the map
@@ -109,26 +185,24 @@ public static <K, V> ProofMapIndexProxy<K, V> newInstance(
109
185
* @throws IllegalArgumentException if the name or index id is empty
110
186
* @see StandardSerializers
111
187
*/
112
- public static <K , V > ProofMapIndexProxy <K , V > newInGroupUnsafe (String groupName ,
113
- byte [] mapId ,
114
- View view ,
115
- Serializer <K > keySerializer ,
116
- Serializer <V > valueSerializer ) {
188
+ public static <K , V > ProofMapIndexProxy <K , V > newInGroupUnsafeNoKeyHashing (
189
+ String groupName , byte [] mapId , View view , Serializer <K > keySerializer ,
190
+ Serializer <V > valueSerializer ) {
117
191
IndexAddress address = IndexAddress .valueOf (groupName , mapId );
118
192
long viewNativeHandle = view .getViewNativeHandle ();
119
193
LongSupplier nativeMapConstructor =
120
- () -> nativeCreateInGroup (groupName , mapId , viewNativeHandle );
194
+ () -> nativeCreateInGroup (groupName , mapId , viewNativeHandle , false );
121
195
122
- return getOrCreate (address , view , keySerializer , valueSerializer , nativeMapConstructor );
196
+ return getOrCreate (address , view , keySerializer , valueSerializer , nativeMapConstructor , false );
123
197
}
124
198
125
199
private static <K , V > ProofMapIndexProxy <K , V > getOrCreate (IndexAddress address , View view ,
126
200
Serializer <K > keySerializer , Serializer <V > valueSerializer ,
127
- LongSupplier nativeMapConstructor ) {
201
+ LongSupplier nativeMapConstructor , boolean keyHashing ) {
128
202
return view .findOpenIndex (address )
129
203
.map (ProofMapIndexProxy ::<K , V >checkCachedInstance )
130
204
.orElseGet (() -> newMapIndexProxy (address , view , keySerializer , valueSerializer ,
131
- nativeMapConstructor ));
205
+ nativeMapConstructor , keyHashing ));
132
206
}
133
207
134
208
@ SuppressWarnings ("unchecked" ) // The compiler is correct: the cache is not type-safe: ECR-3387
@@ -139,9 +213,8 @@ private static <K, V> ProofMapIndexProxy<K, V> checkCachedInstance(StorageIndex
139
213
140
214
private static <K , V > ProofMapIndexProxy <K , V > newMapIndexProxy (IndexAddress address , View view ,
141
215
Serializer <K > keySerializer , Serializer <V > valueSerializer ,
142
- LongSupplier nativeMapConstructor ) {
143
- ProofMapKeyCheckingSerializerDecorator <K > ks =
144
- ProofMapKeyCheckingSerializerDecorator .from (keySerializer );
216
+ LongSupplier nativeMapConstructor , boolean keyHashing ) {
217
+ Serializer <K > ks = decorateKeySerializer (keySerializer , keyHashing );
145
218
CheckingSerializerDecorator <V > vs = CheckingSerializerDecorator .from (valueSerializer );
146
219
147
220
NativeHandle mapNativeHandle = createNativeMap (view , nativeMapConstructor );
@@ -151,6 +224,15 @@ private static <K, V> ProofMapIndexProxy<K, V> newMapIndexProxy(IndexAddress add
151
224
return map ;
152
225
}
153
226
227
+ private static <K > Serializer <K > decorateKeySerializer (
228
+ Serializer <K > keySerializer , boolean keyHashing ) {
229
+ if (!keyHashing ) {
230
+ return ProofMapKeyCheckingSerializerDecorator .from (keySerializer );
231
+ } else {
232
+ return CheckingSerializerDecorator .from (keySerializer );
233
+ }
234
+ }
235
+
154
236
private static NativeHandle createNativeMap (View view , LongSupplier nativeMapConstructor ) {
155
237
NativeHandle mapNativeHandle = new NativeHandle (nativeMapConstructor .getAsLong ());
156
238
@@ -160,13 +242,13 @@ private static NativeHandle createNativeMap(View view, LongSupplier nativeMapCon
160
242
return mapNativeHandle ;
161
243
}
162
244
163
- private static native long nativeCreate (String name , long viewNativeHandle );
245
+ private static native long nativeCreate (String name , long viewNativeHandle , boolean keyHashing );
164
246
165
247
private static native long nativeCreateInGroup (String groupName , byte [] mapId ,
166
- long viewNativeHandle );
248
+ long viewNativeHandle , boolean keyHashing );
167
249
168
250
private ProofMapIndexProxy (NativeHandle nativeHandle , IndexAddress address , View view ,
169
- ProofMapKeyCheckingSerializerDecorator <K > keySerializer ,
251
+ Serializer <K > keySerializer ,
170
252
CheckingSerializerDecorator <V > valueSerializer ) {
171
253
super (nativeHandle , address , view );
172
254
this .keySerializer = keySerializer ;
@@ -184,10 +266,11 @@ public boolean containsKey(K key) {
184
266
/**
185
267
* {@inheritDoc}
186
268
*
187
- * @param key a proof map key, must be 32-byte long when serialized
269
+ * @param key a proof map key
188
270
* @param value a storage value to associate with the key
189
271
* @throws IllegalStateException if this map is not valid
190
- * @throws IllegalArgumentException if the size of the key is not 32 bytes
272
+ * @throws IllegalArgumentException if the size of the key is not 32 bytes (in case of a
273
+ * <a href="ProofMapIndexProxy.html#key-hashing">proof map that uses non-hashed keys</a>)
191
274
* @throws UnsupportedOperationException if this map is read-only
192
275
*/
193
276
@ Override
@@ -227,11 +310,11 @@ public V get(K key) {
227
310
* Returns a proof that there are values mapped to the specified keys or that there are no such
228
311
* mappings.
229
312
*
230
- * @param key a proof map key which might be mapped to some value, must be 32-byte long
231
- * @param otherKeys other proof map keys which might be mapped to some values, each must be
232
- * 32-byte long
313
+ * @param key a proof map key which might be mapped to some value
314
+ * @param otherKeys other proof map keys which might be mapped to some values
233
315
* @throws IllegalStateException if this map is not valid
234
- * @throws IllegalArgumentException if the size of any of the keys is not 32 bytes
316
+ * @throws IllegalArgumentException if the size of any of the keys is not 32 bytes (in case of a
317
+ * <a href="ProofMapIndexProxy.html#key-hashing">proof map that uses non-hashed keys</a>)
235
318
*/
236
319
public UncheckedMapProof getProof (K key , K ... otherKeys ) {
237
320
if (otherKeys .length == 0 ) {
@@ -246,10 +329,11 @@ public UncheckedMapProof getProof(K key, K... otherKeys) {
246
329
* Returns a proof that there are values mapped to the specified keys or that there are no such
247
330
* mappings.
248
331
*
249
- * @param keys proof map keys which might be mapped to some values, each must be 32-byte long
332
+ * @param keys proof map keys which might be mapped to some values
250
333
* @throws IllegalStateException if this map is not valid
251
- * @throws IllegalArgumentException if the size of any of the keys is not 32 bytes
252
- * or keys collection is empty
334
+ * @throws IllegalArgumentException if the size of any of the keys is not 32 bytes (in case of a
335
+ * <a href="ProofMapIndexProxy.html#key-hashing">proof map that uses non-hashed keys</a>) or
336
+ * keys collection is empty
253
337
*/
254
338
public UncheckedMapProof getProof (Collection <? extends K > keys ) {
255
339
checkArgument (!keys .isEmpty (), "Keys collection should not be empty" );
0 commit comments