
com.google.crypto.tink.internal.PrefixMap Maven / Gradle / Ivy
Show all versions of tink-android Show documentation
// Copyright 2025 Google LLC
//
// 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.google.crypto.tink.internal;
import com.google.crypto.tink.util.Bytes;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.Immutable;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Provides a map from prefix to arbitrary element, allowing to iterate over all elements whose
* prefixes matches a given {@code byte[]}.
*
* To create a {@code PrefixMap}, the user adds pairs {@code (Prefix, Value)}, as in a map. To
* query, the user provides a {@code byte[]} and the map will allow to iterate over all values which
* were added with a prefix of the given {@code byte[]}.
*
*
Currently only supports prefixes of length 5 and 0.
*/
@Immutable
public final class PrefixMap
{
private static final Bytes EMPTY_BYTES = Bytes.copyFrom(new byte[0]);
/** Builder for PrefixMap. */
public static class Builder
{
/**
* Adds a value for a given prefix.
*
*
{@code prefix.size()} has to be 0 or 5.
*/
@CanIgnoreReturnValue
public Builder
put(Bytes prefix, P primitive) throws GeneralSecurityException {
if (prefix.size() != 0 && prefix.size() != 5) {
throw new GeneralSecurityException("PrefixMap only supports 0 and 5 byte prefixes");
}
List
listForThisPrefix;
if (entries.containsKey(prefix)) {
listForThisPrefix = entries.get(prefix);
} else {
listForThisPrefix = new ArrayList
();
entries.put(prefix, listForThisPrefix);
}
listForThisPrefix.add(primitive);
return this;
}
public PrefixMap
build() {
return new PrefixMap
(entries);
}
private final Map> entries = new HashMap<>();
}
private static class ConcatenatedIterator implements Iterator
{
private ConcatenatedIterator(Iterator
it0, Iterator
it1) {
this.it0 = it0;
this.it1 = it1;
}
@Override
public boolean hasNext() {
return it0.hasNext() || it1.hasNext();
}
@Override
public P next() {
if (it0.hasNext()) {
return it0.next();
}
return it1.next();
}
private final Iterator
it0;
private final Iterator
it1;
}
/**
* Provides an iterable which goves over all values which were added with a prefix of the given
* {@code text}.
*
*
The matches with the longest prefixes are returned first. Within a given length, the values
* are returned in the order they were provided in the builder.
*/
public Iterable
getAllWithMatchingPrefix(byte[] text) {
List
zeroByteEntriesOrNull = entries.get(EMPTY_BYTES);
List
fiveByteEntriesOrNull =
text.length >= 5 ? entries.get(Bytes.copyFrom(text, 0, 5)) : null;
if (zeroByteEntriesOrNull == null && fiveByteEntriesOrNull == null) {
return new ArrayList
();
}
if (zeroByteEntriesOrNull == null) {
return fiveByteEntriesOrNull;
}
if (fiveByteEntriesOrNull == null) {
return zeroByteEntriesOrNull;
}
return new Iterable
() {
@Override
public Iterator
iterator() {
return new ConcatenatedIterator
(
fiveByteEntriesOrNull.iterator(), zeroByteEntriesOrNull.iterator());
}
};
}
private PrefixMap(Map> entries) {
this.entries = entries;
}
@SuppressWarnings("Immutable")
private final Map> entries;
}