-
Notifications
You must be signed in to change notification settings - Fork 30
Add no key hashing option to ProofMapIndexProxy [ECR-3779] #1222
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
e4a3631
137e992
4048d4c
6cc288a
20e7ac8
8edf1bf
2d22068
48109ca
3e215d1
3a8fe9f
95e96ad
5b3e619
82bbba8
0b00849
3c21a42
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -44,8 +44,15 @@ | |
| * that a certain key is mapped to a particular value <em>or</em> that there are no mapping for | ||
| * the key in the map. | ||
| * | ||
| * <p>This map is implemented as a Merkle-Patricia tree. It does not permit null keys and values, | ||
| * and requires that keys are 32-byte long. | ||
| * <p>This map is implemented as a Merkle-Patricia tree. It does not permit null keys and values. | ||
| * | ||
| * <p>This map can be instantiated in two ways - either with keys hashing with methods | ||
| * {@link #newInstance(String, View, Serializer, Serializer)} and | ||
| * {@link #newInGroupUnsafe(String, byte[], View, Serializer, Serializer)} or with no key hashing | ||
| * with methods {@link #newInstanceNoKeyHashing(String, View, Serializer, Serializer)} and | ||
| * {@link #newInGroupUnsafeNoKeyHashing(String, byte[], View, Serializer, Serializer)}. In case of | ||
| * no key hashing keys are required to be 32-byte long. Note that the former option is considered | ||
|
||
| * a default one. | ||
| * | ||
| * <p>The "destructive" methods of the map, i.e., the one that change the map contents, | ||
| * are specified to throw {@link UnsupportedOperationException} if | ||
|
|
@@ -58,7 +65,7 @@ | |
| * <p>When the view goes out of scope, this map is destroyed. Subsequent use of the closed map | ||
| * is prohibited and will result in {@link IllegalStateException}. | ||
| * | ||
| * @param <K> the type of keys in this map. Must be 32-byte long in the serialized form | ||
| * @param <K> the type of keys in this map | ||
| * @param <V> the type of values in this map | ||
| * @see View | ||
| */ | ||
|
|
@@ -74,7 +81,7 @@ public final class ProofMapIndexProxy<K, V> extends AbstractIndexProxy implement | |
| * [a-zA-Z0-9_] | ||
| * @param view a database view. Must be valid. | ||
| * If a view is read-only, "destructive" operations are not permitted. | ||
| * @param keySerializer a serializer of keys, must always produce 32-byte long values | ||
| * @param keySerializer a serializer of keys | ||
| * @param valueSerializer a serializer of values | ||
| * @param <K> the type of keys in the map | ||
| * @param <V> the type of values in the map | ||
|
|
@@ -86,9 +93,34 @@ public static <K, V> ProofMapIndexProxy<K, V> newInstance( | |
| String name, View view, Serializer<K> keySerializer, Serializer<V> valueSerializer) { | ||
| IndexAddress address = IndexAddress.valueOf(name); | ||
| long viewNativeHandle = view.getViewNativeHandle(); | ||
| LongSupplier nativeMapConstructor = () -> nativeCreate(name, viewNativeHandle); | ||
| LongSupplier nativeMapConstructor = () -> nativeCreate(name, viewNativeHandle, true); | ||
|
|
||
| return getOrCreate(address, view, keySerializer, valueSerializer, nativeMapConstructor, true); | ||
| } | ||
|
|
||
| /** | ||
| * Creates a ProofMapIndexProxy with no key hashing. Requires that keys are 32-byte long. | ||
|
||
| * | ||
| * @param name a unique alphanumeric non-empty identifier of this map in the underlying storage: | ||
| * [a-zA-Z0-9_] | ||
| * @param view a database view. Must be valid. | ||
| * If a view is read-only, "destructive" operations are not permitted. | ||
| * @param keySerializer a serializer of keys, must always produce 32-byte long values | ||
| * @param valueSerializer a serializer of values | ||
| * @param <K> the type of keys in the map | ||
| * @param <V> the type of values in the map | ||
| * @throws IllegalStateException if the view is not valid | ||
| * @throws IllegalArgumentException if the name is empty | ||
| * @see StandardSerializers | ||
| * @see ProofMapIndexProxy | ||
|
||
| */ | ||
| public static <K, V> ProofMapIndexProxy<K, V> newInstanceNoKeyHashing( | ||
| String name, View view, Serializer<K> keySerializer, Serializer<V> valueSerializer) { | ||
| IndexAddress address = IndexAddress.valueOf(name); | ||
| long viewNativeHandle = view.getViewNativeHandle(); | ||
| LongSupplier nativeMapConstructor = () -> nativeCreate(name, viewNativeHandle, false); | ||
|
|
||
| return getOrCreate(address, view, keySerializer, valueSerializer, nativeMapConstructor); | ||
| return getOrCreate(address, view, keySerializer, valueSerializer, nativeMapConstructor, false); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -100,6 +132,35 @@ public static <K, V> ProofMapIndexProxy<K, V> newInstance( | |
| * @param groupName a name of the collection group | ||
| * @param mapId an identifier of this collection in the group, see the caveats | ||
| * @param view a database view | ||
| * @param keySerializer a serializer of keys | ||
| * @param valueSerializer a serializer of values | ||
| * @param <K> the type of keys in the map | ||
| * @param <V> the type of values in the map | ||
| * @return a new map proxy | ||
| * @throws IllegalStateException if the view is not valid | ||
| * @throws IllegalArgumentException if the name or index id is empty | ||
| * @see StandardSerializers | ||
| */ | ||
| public static <K, V> ProofMapIndexProxy<K, V> newInGroupUnsafe( | ||
| String groupName, byte[] mapId, View view, Serializer<K> keySerializer, | ||
| Serializer<V> valueSerializer) { | ||
| IndexAddress address = IndexAddress.valueOf(groupName, mapId); | ||
| long viewNativeHandle = view.getViewNativeHandle(); | ||
| LongSupplier nativeMapConstructor = | ||
| () -> nativeCreateInGroup(groupName, mapId, viewNativeHandle, true); | ||
|
|
||
| return getOrCreate(address, view, keySerializer, valueSerializer, nativeMapConstructor, true); | ||
| } | ||
|
|
||
| /** | ||
| * Creates a new proof map in a <a href="package-summary.html#families">collection group</a> | ||
| * with the given name. Requires that keys are 32-byte long. | ||
| * | ||
| * <p>See a <a href="package-summary.html#families-limitations">caveat</a> on index identifiers. | ||
| * | ||
| * @param groupName a name of the collection group | ||
| * @param mapId an identifier of this collection in the group, see the caveats | ||
| * @param view a database view | ||
| * @param keySerializer a serializer of keys, must always produce 32-byte long values | ||
| * @param valueSerializer a serializer of values | ||
| * @param <K> the type of keys in the map | ||
|
|
@@ -108,27 +169,26 @@ public static <K, V> ProofMapIndexProxy<K, V> newInstance( | |
| * @throws IllegalStateException if the view is not valid | ||
| * @throws IllegalArgumentException if the name or index id is empty | ||
| * @see StandardSerializers | ||
| * @see ProofMapIndexProxy | ||
| */ | ||
| public static <K, V> ProofMapIndexProxy<K, V> newInGroupUnsafe(String groupName, | ||
| byte[] mapId, | ||
| View view, | ||
| Serializer<K> keySerializer, | ||
| Serializer<V> valueSerializer) { | ||
| public static <K, V> ProofMapIndexProxy<K, V> newInGroupUnsafeNoKeyHashing( | ||
| String groupName, byte[] mapId, View view, Serializer<K> keySerializer, | ||
| Serializer<V> valueSerializer) { | ||
| IndexAddress address = IndexAddress.valueOf(groupName, mapId); | ||
| long viewNativeHandle = view.getViewNativeHandle(); | ||
| LongSupplier nativeMapConstructor = | ||
| () -> nativeCreateInGroup(groupName, mapId, viewNativeHandle); | ||
| () -> nativeCreateInGroup(groupName, mapId, viewNativeHandle, false); | ||
|
|
||
| return getOrCreate(address, view, keySerializer, valueSerializer, nativeMapConstructor); | ||
| return getOrCreate(address, view, keySerializer, valueSerializer, nativeMapConstructor, false); | ||
| } | ||
|
|
||
| private static <K, V> ProofMapIndexProxy<K, V> getOrCreate(IndexAddress address, View view, | ||
| Serializer<K> keySerializer, Serializer<V> valueSerializer, | ||
| LongSupplier nativeMapConstructor) { | ||
| LongSupplier nativeMapConstructor, boolean keyHashing) { | ||
| return view.findOpenIndex(address) | ||
| .map(ProofMapIndexProxy::<K, V>checkCachedInstance) | ||
| .orElseGet(() -> newMapIndexProxy(address, view, keySerializer, valueSerializer, | ||
| nativeMapConstructor)); | ||
| nativeMapConstructor, keyHashing)); | ||
| } | ||
|
|
||
| @SuppressWarnings("unchecked") // The compiler is correct: the cache is not type-safe: ECR-3387 | ||
|
|
@@ -139,9 +199,8 @@ private static <K, V> ProofMapIndexProxy<K, V> checkCachedInstance(StorageIndex | |
|
|
||
| private static <K, V> ProofMapIndexProxy<K, V> newMapIndexProxy(IndexAddress address, View view, | ||
| Serializer<K> keySerializer, Serializer<V> valueSerializer, | ||
| LongSupplier nativeMapConstructor) { | ||
| ProofMapKeyCheckingSerializerDecorator<K> ks = | ||
| ProofMapKeyCheckingSerializerDecorator.from(keySerializer); | ||
| LongSupplier nativeMapConstructor, boolean keyHashing) { | ||
| ProofMapKeyCheckingSerializerDecorator<K> ks = decorateKeySerializer(keySerializer, keyHashing); | ||
| CheckingSerializerDecorator<V> vs = CheckingSerializerDecorator.from(valueSerializer); | ||
|
|
||
| NativeHandle mapNativeHandle = createNativeMap(view, nativeMapConstructor); | ||
|
|
@@ -151,6 +210,17 @@ private static <K, V> ProofMapIndexProxy<K, V> newMapIndexProxy(IndexAddress add | |
| return map; | ||
| } | ||
|
|
||
| private static <K> ProofMapKeyCheckingSerializerDecorator<K> decorateKeySerializer( | ||
| Serializer<K> keySerializer, boolean keyHashing) { | ||
| if (!keyHashing) { | ||
| ProofMapKeySizeCheckingSerializerDecorator<K> sizeCheckingSerializerDecorator = | ||
| ProofMapKeySizeCheckingSerializerDecorator.from(keySerializer); | ||
| return ProofMapKeyCheckingSerializerDecorator.from(sizeCheckingSerializerDecorator); | ||
| } else { | ||
| return ProofMapKeyCheckingSerializerDecorator.from(keySerializer); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this needed at all if it can return just keySerializer, with no extra decorator types? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To check for not null values? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is one for that already. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. com.exonum.binding.common.serialization.CheckingSerializerDecorator |
||
| } | ||
| } | ||
|
|
||
| private static NativeHandle createNativeMap(View view, LongSupplier nativeMapConstructor) { | ||
| NativeHandle mapNativeHandle = new NativeHandle(nativeMapConstructor.getAsLong()); | ||
|
|
||
|
|
@@ -160,10 +230,10 @@ private static NativeHandle createNativeMap(View view, LongSupplier nativeMapCon | |
| return mapNativeHandle; | ||
| } | ||
|
|
||
| private static native long nativeCreate(String name, long viewNativeHandle); | ||
| private static native long nativeCreate(String name, long viewNativeHandle, boolean keyHashing); | ||
|
|
||
| private static native long nativeCreateInGroup(String groupName, byte[] mapId, | ||
| long viewNativeHandle); | ||
| long viewNativeHandle, boolean keyHashing); | ||
|
|
||
| private ProofMapIndexProxy(NativeHandle nativeHandle, IndexAddress address, View view, | ||
| ProofMapKeyCheckingSerializerDecorator<K> keySerializer, | ||
|
|
@@ -184,10 +254,11 @@ public boolean containsKey(K key) { | |
| /** | ||
| * {@inheritDoc} | ||
| * | ||
| * @param key a proof map key, must be 32-byte long when serialized | ||
| * @param key a proof map key | ||
| * @param value a storage value to associate with the key | ||
| * @throws IllegalStateException if this map is not valid | ||
| * @throws IllegalArgumentException if the size of the key is not 32 bytes | ||
| * @throws IllegalArgumentException if the size of the key is not 32 bytes (in case of a | ||
| * no key hashing proof map) | ||
|
||
| * @throws UnsupportedOperationException if this map is read-only | ||
| */ | ||
| @Override | ||
|
|
@@ -227,11 +298,11 @@ public V get(K key) { | |
| * Returns a proof that there are values mapped to the specified keys or that there are no such | ||
| * mappings. | ||
| * | ||
| * @param key a proof map key which might be mapped to some value, must be 32-byte long | ||
| * @param otherKeys other proof map keys which might be mapped to some values, each must be | ||
| * 32-byte long | ||
| * @param key a proof map key which might be mapped to some value | ||
| * @param otherKeys other proof map keys which might be mapped to some values | ||
| * @throws IllegalStateException if this map is not valid | ||
| * @throws IllegalArgumentException if the size of any of the keys is not 32 bytes | ||
| * @throws IllegalArgumentException if the size of any of the keys is not 32 bytes (in case of a | ||
| * no key hashing proof map) | ||
| */ | ||
| public UncheckedMapProof getProof(K key, K... otherKeys) { | ||
| if (otherKeys.length == 0) { | ||
|
|
@@ -246,10 +317,10 @@ public UncheckedMapProof getProof(K key, K... otherKeys) { | |
| * Returns a proof that there are values mapped to the specified keys or that there are no such | ||
| * mappings. | ||
| * | ||
| * @param keys proof map keys which might be mapped to some values, each must be 32-byte long | ||
| * @param keys proof map keys which might be mapped to some values | ||
| * @throws IllegalStateException if this map is not valid | ||
| * @throws IllegalArgumentException if the size of any of the keys is not 32 bytes | ||
| * or keys collection is empty | ||
| * @throws IllegalArgumentException if the size of any of the keys is not 32 bytes (in case of a | ||
| * no key hashing proof map) or keys collection is empty | ||
| */ | ||
| public UncheckedMapProof getProof(Collection<? extends K> keys) { | ||
| checkArgument(!keys.isEmpty(), "Keys collection should not be empty"); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,15 +16,12 @@ | |
|
|
||
| package com.exonum.binding.core.storage.indices; | ||
|
|
||
| import static com.exonum.binding.core.storage.indices.StoragePreconditions.checkProofKey; | ||
| import static com.google.common.base.Preconditions.checkNotNull; | ||
|
|
||
| import com.exonum.binding.common.serialization.Serializer; | ||
|
|
||
| /** | ||
| * A serializer decorator that checks proof map keys for correctness. | ||
| * | ||
| * @see StoragePreconditions#checkProofKey(byte[]) | ||
| * A serializer decorator that checks proof map keys are not null. | ||
|
||
| */ | ||
| final class ProofMapKeyCheckingSerializerDecorator<T> implements Serializer<T> { | ||
|
|
||
|
|
@@ -49,12 +46,12 @@ private ProofMapKeyCheckingSerializerDecorator(Serializer<T> delegate) { | |
| @Override | ||
| public byte[] toBytes(T proofKey) { | ||
| byte[] dbValue = delegate.toBytes(proofKey); | ||
| return checkProofKey(dbValue); | ||
| return checkNotNull(dbValue); | ||
| } | ||
|
|
||
| @Override | ||
| public T fromBytes(byte[] serializedProofKey) { | ||
| checkProofKey(serializedProofKey); | ||
| checkNotNull(serializedProofKey); | ||
| return delegate.fromBytes(serializedProofKey); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| /* | ||
| * Copyright 2019 The Exonum Team | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package com.exonum.binding.core.storage.indices; | ||
|
|
||
| import static com.exonum.binding.core.storage.indices.StoragePreconditions.checkProofKey; | ||
| import static com.google.common.base.Preconditions.checkNotNull; | ||
|
|
||
| import com.exonum.binding.common.serialization.Serializer; | ||
|
|
||
| /** | ||
| * A serializer decorator that checks that proof map keys are 32-byte long. | ||
| * | ||
| * @see StoragePreconditions#checkProofKey(byte[]) | ||
| */ | ||
| final class ProofMapKeySizeCheckingSerializerDecorator<T> implements Serializer<T> { | ||
|
|
||
| private final Serializer<T> delegate; | ||
|
|
||
| /** | ||
| * Creates a proof map key checking serializer decorator. Will not decorate itself. | ||
| * | ||
| * @param serializer a serializer to decorate | ||
| */ | ||
| public static <T> ProofMapKeySizeCheckingSerializerDecorator<T> from(Serializer<T> serializer) { | ||
| if (serializer instanceof ProofMapKeySizeCheckingSerializerDecorator) { | ||
| return (ProofMapKeySizeCheckingSerializerDecorator<T>) serializer; | ||
| } | ||
| return new ProofMapKeySizeCheckingSerializerDecorator<>(serializer); | ||
| } | ||
|
|
||
| private ProofMapKeySizeCheckingSerializerDecorator(Serializer<T> delegate) { | ||
| this.delegate = checkNotNull(delegate); | ||
| } | ||
|
|
||
| @Override | ||
| public byte[] toBytes(T proofKey) { | ||
| byte[] dbValue = delegate.toBytes(proofKey); | ||
| return checkProofKey(dbValue); | ||
| } | ||
|
|
||
| @Override | ||
| public T fromBytes(byte[] serializedProofKey) { | ||
| checkProofKey(serializedProofKey); | ||
| return delegate.fromBytes(serializedProofKey); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with..with...
how about : ...either with keys hashing using methods...