com.google.cloud.dataflow.sdk.io.range.ByteKey Maven / Gradle / Ivy
Show all versions of google-cloud-dataflow-java-sdk-all Show documentation
/*
* Copyright (C) 2015 Google Inc.
*
* 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.cloud.dataflow.sdk.io.range;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.protobuf.ByteString;
import com.google.protobuf.ByteString.ByteIterator;
import java.io.Serializable;
/**
* A class representing a key consisting of an array of bytes. Arbitrary-length
* {@code byte[]} keys are typical in key-value stores such as Google Cloud Bigtable.
*
* Instances of {@link ByteKey} are immutable.
*
*
{@link ByteKey} implements {@link Comparable Comparable<ByteKey>} by comparing the
* arrays in lexicographic order. The smallest {@link ByteKey} is a zero-length array; the successor
* to a key is the same key with an additional 0 byte appended; and keys have unbounded size.
*
*
Note that the empty {@link ByteKey} compares smaller than all other keys, but some systems
* have the semantic that when an empty {@link ByteKey} is used as an upper bound, it represents
* the largest possible key. In these cases, implementors should use {@link #isEmpty} to test
* whether an upper bound key is empty.
*/
public final class ByteKey implements Comparable, Serializable {
/** An empty key. */
public static final ByteKey EMPTY = ByteKey.of();
/**
* Creates a new {@link ByteKey} backed by the specified {@link ByteString}.
*/
public static ByteKey of(ByteString value) {
return new ByteKey(value);
}
/**
* Creates a new {@link ByteKey} backed by a copy of the specified {@code byte[]}.
*
* Makes a copy of the underlying array.
*/
public static ByteKey copyFrom(byte[] bytes) {
return of(ByteString.copyFrom(bytes));
}
/**
* Creates a new {@link ByteKey} backed by a copy of the specified {@code int[]}. This method is
* primarily used as a convenience to create a {@link ByteKey} in code without casting down to
* signed Java {@link Byte bytes}:
*
*
{@code
* ByteKey key = ByteKey.of(0xde, 0xad, 0xbe, 0xef);
* }
*
* Makes a copy of the input.
*/
public static ByteKey of(int... bytes) {
byte[] ret = new byte[bytes.length];
for (int i = 0; i < bytes.length; ++i) {
ret[i] = (byte) (bytes[i] & 0xff);
}
return ByteKey.copyFrom(ret);
}
/**
* Returns an immutable {@link ByteString} representing this {@link ByteKey}.
*
*
Does not copy.
*/
public ByteString getValue() {
return value;
}
/**
* Returns a newly-allocated {@code byte[]} representing this {@link ByteKey}.
*
*
Copies the underlying {@code byte[]}.
*/
public byte[] getBytes() {
return value.toByteArray();
}
/**
* Returns {@code true} if the {@code byte[]} backing this {@link ByteKey} is of length 0.
*/
public boolean isEmpty() {
return value.isEmpty();
}
/**
* {@link ByteKey} implements {@link Comparable Comparable<ByteKey>} by comparing the
* arrays in lexicographic order. The smallest {@link ByteKey} is a zero-length array; the
* successor to a key is the same key with an additional 0 byte appended; and keys have unbounded
* size.
*/
@Override
public int compareTo(ByteKey other) {
checkNotNull(other, "other");
ByteIterator thisIt = value.iterator();
ByteIterator otherIt = other.value.iterator();
while (thisIt.hasNext() && otherIt.hasNext()) {
// (byte & 0xff) converts [-128,127] bytes to [0,255] ints.
int cmp = (thisIt.nextByte() & 0xff) - (otherIt.nextByte() & 0xff);
if (cmp != 0) {
return cmp;
}
}
// If we get here, the prefix of both arrays is equal up to the shorter array. The array with
// more bytes is larger.
return value.size() - other.value.size();
}
////////////////////////////////////////////////////////////////////////////////////
private final ByteString value;
private ByteKey(ByteString value) {
this.value = value;
}
/** Array used as a helper in {@link #toString}. */
private static final char[] HEX =
new char[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
// Prints the key as a string "[deadbeef]".
@Override
public String toString() {
char[] encoded = new char[2 * value.size() + 2];
encoded[0] = '[';
int cnt = 1;
ByteIterator iterator = value.iterator();
while (iterator.hasNext()) {
byte b = iterator.nextByte();
encoded[cnt] = HEX[(b & 0xF0) >>> 4];
++cnt;
encoded[cnt] = HEX[b & 0xF];
++cnt;
}
encoded[cnt] = ']';
return new String(encoded);
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof ByteKey)) {
return false;
}
ByteKey other = (ByteKey) o;
return (other.value.size() == value.size()) && this.compareTo(other) == 0;
}
@Override
public int hashCode() {
return value.hashCode();
}
}