All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.apple.foundationdb.subspace.Subspace Maven / Gradle / Ivy

/*
 * Subspace.java
 *
 * This source file is part of the FoundationDB open source project
 *
 * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
 *
 * 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.apple.foundationdb.subspace;

import static com.apple.foundationdb.tuple.ByteArrayUtil.join;
import static com.apple.foundationdb.tuple.ByteArrayUtil.printable;

import java.util.Arrays;

import com.apple.foundationdb.Range;
import com.apple.foundationdb.tuple.ByteArrayUtil;
import com.apple.foundationdb.tuple.Tuple;
import com.apple.foundationdb.tuple.Versionstamp;

/**
 * {@code Subspace} provide a convenient way to use {@link Tuple}s to define namespaces for
 * different categories of data. The namespace is specified by a prefix {@link Tuple}
 * which is prepended to all {@link Tuple}s packed by the {@code Subspace}. When unpacking a key
 * with the {@code Subspace}, the prefix {@link Tuple} will be removed from the result.
 *
 * 

* For general guidance on subspace usage, see the discussion in * Developer Guide. *

* *

* As a best practice, API clients should use at least one subspace for application data. *

*/ public class Subspace { private static final Tuple EMPTY_TUPLE = Tuple.from(); private static final byte[] EMPTY_BYTES = new byte[0]; private final byte[] rawPrefix; /** * Constructor for a subspace formed with an empty prefix {@link Tuple}. */ public Subspace() { this(EMPTY_TUPLE, EMPTY_BYTES); } /** * Constructor for a subspace formed with the specified prefix {@link Tuple}. * Note that the {@link Tuple} {@code prefix} should not contain any incomplete * {@link Versionstamp}s as any of its entries. * * @param prefix a {@link Tuple} used to form the subspace * @throws IllegalArgumentException if {@code prefix} contains any incomplete {@link Versionstamp}s */ public Subspace(Tuple prefix) { this(prefix, EMPTY_BYTES); } /** * Constructor for a subspace formed with the specified byte string, which will * be prepended to all packed keys. * * @param rawPrefix a byte array used as the prefix for all packed keys */ public Subspace(byte[] rawPrefix) { this(EMPTY_TUPLE, rawPrefix); } /** * Constructor for a subspace formed with both a prefix {@link Tuple} and a * prefix byte string. The prefix {@code Tuple} will be prepended to all * {@code Tuples} packed by the {@code Subspace}, and the byte string prefix * will be prepended to the packed result. Note that the {@link Tuple} {@code prefix} * should not contain any incomplete {@link Versionstamp}s as any of its entries. * * @param prefix a {@code Tuple} used to form the subspace * @param rawPrefix a byte array used as the prefix for all packed keys * @throws IllegalArgumentException if {@code prefix} contains any incomplete {@link Versionstamp}s */ public Subspace(Tuple prefix, byte[] rawPrefix) { this.rawPrefix = join(rawPrefix, prefix.pack()); } /** * Returns true if this {@code Subspace} is equal to {@code rhs}. * Two {@code Subspace}s are equal if they have the same prefix. * * @param rhs the object to check for equality * @return {@code true} if this {@code Subspace} and {@code rhs} have equal prefixes */ @Override public boolean equals(Object rhs) { if(this == rhs) { return true; } if(rhs == null || getClass() != rhs.getClass()) { return false; } Subspace other = (Subspace)rhs; return Arrays.equals(rawPrefix, other.rawPrefix); } /** * Create a human-readable string representation of this subspace. This is * really only useful for debugging purposes, but it includes information * on what raw prefix the subspace is using. * @return a printable representation of the subspace */ @Override public String toString() { return "Subspace(rawPrefix=" + printable(rawPrefix) + ")"; } /** * Returns a hash-table compatible hash of this subspace. This is based off * of the hash of the underlying byte-array prefix. * @return a hash of this subspace */ @Override public int hashCode() { return Arrays.hashCode(rawPrefix); } /** * Gets a new subspace which is equivalent to this subspace with its prefix {@link Tuple} extended by * the specified {@code Object}. The object will be inserted into a {@link Tuple} and passed to {@link #get(Tuple)}. * * @param obj an {@code Object} compatible with {@code Tuple}s * @return a new subspace formed by joining this {@code Subspace}'s prefix to {@code obj} */ public Subspace get(Object obj) { return get(Tuple.from(obj)); } /** * Gets a new subspace which is equivalent to this subspace with its prefix {@link Tuple} extended by * the specified {@link Tuple}. * * @param tuple the {@link Tuple} used to form the new {@code Subspace} * @return a new subspace formed by joining this {@code Subspace}'s prefix to {@code tuple} */ public Subspace get(Tuple tuple) { return subspace(tuple); } /** * Gets the key encoding the prefix used for this {@code Subspace}. This is equivalent to * {@link #pack}ing the empty {@link Tuple}. * * @return the key encoding the prefix used for this {@code Subspace} */ public byte[] getKey() { return pack(); } /** * Gets the key encoding the prefix used for this {@code Subspace}. * * @return the key encoding the prefix used for this {@code Subspace} */ public byte[] pack() { return Arrays.copyOf(rawPrefix, rawPrefix.length); } /** * Gets the key encoding the specified {@code Object} in this {@code Subspace}. {@code obj} is * inserted into a {@link Tuple} and packed with {@link #pack(Tuple)}. * * @param obj an {@code Object} to be packed that is compatible with {@link Tuple}s * @return the key encoding the tuple derived from {@code obj} */ public byte[] pack(Object obj) { return pack(Tuple.from(obj)); } /** * Gets the key encoding the specified tuple in this {@code Subspace}. For example, if you have a {@code Subspace} * with prefix {@link Tuple} {@code ("users")} and you use it to pack the {@link Tuple} {@code ("Smith")}, * the result is the same as if you packed the {@link Tuple} {@code ("users", "Smith")}. * * @param tuple the {@code Tuple} to be packed * @return the key encoding the specified tuple in this {@code Subspace} */ public byte[] pack(Tuple tuple) { return tuple.pack(rawPrefix); } /** * Gets the key encoding the specified tuple in this {@code Subspace} for use with * {@link com.apple.foundationdb.MutationType#SET_VERSIONSTAMPED_KEY MutationType.SET_VERSIONSTAMPED_KEY}. * There must be exactly one incomplete {@link Versionstamp} included in the given {@link Tuple}. It will * create a key that is within this {@code Subspace} that can be provided as the {@code key} argument to * {@link com.apple.foundationdb.Transaction#mutate(com.apple.foundationdb.MutationType, byte[], byte[]) Transaction.mutate()} * with the {@link com.apple.foundationdb.MutationType#SET_VERSIONSTAMPED_KEY SET_VERSIONSTAMPED_KEY} * mutation. This will throw an {@link IllegalArgumentException} if the {@link Tuple} does not * contain an incomplete {@link Versionstamp} or if it contains multiple. * * @param tuple the {@code Tuple} to be packed * @return the key encoding the specified tuple in this {@code Subspace} * @throws IllegalArgumentException if {@code tuple} does not contain exactly one incomplete {@link Versionstamp} */ public byte[] packWithVersionstamp(Tuple tuple) { return tuple.packWithVersionstamp(rawPrefix); } /** * Gets the {@link Tuple} encoded by the given key, with this {@code Subspace}'s prefix {@link Tuple} and * {@code raw prefix} removed. * * @param key The key being decoded * @return the {@link Tuple} encoded by {@code key} with the prefix removed */ public Tuple unpack(byte[] key) { if(!contains(key)) throw new IllegalArgumentException("Cannot unpack key that is not contained in subspace."); return Tuple.fromBytes(key, rawPrefix.length, key.length - rawPrefix.length); } /** * Gets a {@link Range} respresenting all keys strictly in the {@code Subspace}. * * @return the {@link Range} of keyspace corresponding to this {@code Subspace} */ public Range range() { return range(EMPTY_TUPLE); } /** * Gets a {@link Range} representing all keys in the {@code Subspace} strictly starting with * the specified {@link Tuple}. * * @param tuple the {@code Tuple} whose sub-keys we are searching for * @return the {@link Range} of keyspace corresponding to {@code tuple} */ public Range range(Tuple tuple) { return tuple.range(rawPrefix); } /** * Tests whether the specified key starts with this {@code Subspace}'s prefix, indicating that * the {@code Subspace} logically contains key. * * @param key the key to be tested * @return {@code true} if {@code key} starts with {@code Subspace.key()} */ public boolean contains(byte[] key) { return ByteArrayUtil.startsWith(key, rawPrefix); } /** * Gets a new subspace which is equivalent to this subspace with its prefix {@link Tuple} extended by * the specified {@link Tuple}. * * @param tuple the {@link Tuple} used to form the new {@code Subspace} * @return a new subspace formed by joining this {@code Subspace}'s prefix to {@code tuple} */ public Subspace subspace(Tuple tuple) { return new Subspace(tuple, rawPrefix); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy