blob: 077ed8a8088639304d5bfaac008ef2adf919f5ad [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.accumulo.core.data;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
import static org.apache.accumulo.core.util.ByteBufferUtil.toBytes;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.apache.hadoop.io.BytesWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.WritableComparable;
import org.apache.hadoop.io.WritableComparator;
/**
* A byte sequence that is usable as a key or value. Based on
* {@link org.apache.hadoop.io.BytesWritable} only this class is NOT resizable and DOES NOT
* distinguish between the size of the sequence and the current capacity as
* {@link org.apache.hadoop.io.BytesWritable} does. Hence it is comparatively 'immutable'.
*/
public class Value implements WritableComparable<Object> {
private static final byte[] EMPTY = new byte[0];
protected byte[] value;
/**
* Creates a zero-size sequence.
*/
public Value() {
this(EMPTY, false);
}
/**
* Creates a value using the UTF-8 encoding of the CharSequence
*
* @param cs
* may not be null
*
* @since 1.8.0
*/
public Value(CharSequence cs) {
this(cs.toString().getBytes(UTF_8));
}
/**
* Creates a Value using the bytes of the Text. Makes a copy, does not use the byte array from the
* Text.
*
* @param text
* may not be null
*
* @since 1.8.0
*/
public Value(Text text) {
this(text.getBytes(), 0, text.getLength());
}
/**
* Creates a Value using a byte array as the initial value. The given byte array is used directly
* as the backing array, so later changes made to the array reflect into the new Value.
*
* @param bytes
* May not be null
*/
public Value(byte[] bytes) {
this(bytes, false);
}
/**
* Creates a Value using the bytes in a buffer as the initial value. Makes a defensive copy.
*
* @param bytes
* May not be null
*/
public Value(ByteBuffer bytes) {
/* TODO ACCUMULO-2509 right now this uses the entire backing array, which must be accessible. */
this(toBytes(bytes), false);
}
/**
* Creates a Value using a byte array as the initial value.
*
* @param bytes
* may not be null
* @param copy
* false to use the given byte array directly as the backing array, true to force a copy
*/
public Value(byte[] bytes, boolean copy) {
requireNonNull(bytes);
if (copy) {
this.value = new byte[bytes.length];
System.arraycopy(bytes, 0, this.value, 0, bytes.length);
} else {
this.value = bytes;
}
}
/**
* Creates a new Value based on another.
*
* @param ibw
* may not be null.
*/
public Value(final Value ibw) {
this(ibw.get(), 0, ibw.getSize());
}
/**
* Creates a Value based on a range in a byte array. A copy of the bytes is always made.
*
* @param newData
* source of copy, may not be null
* @param offset
* the offset in newData to start with for value bytes
* @param length
* the number of bytes in the value
* @throws IndexOutOfBoundsException
* if offset or length are invalid
*/
public Value(final byte[] newData, final int offset, final int length) {
requireNonNull(newData);
this.value = new byte[length];
System.arraycopy(newData, offset, this.value, 0, length);
}
/**
* Gets the byte data of this value.
*
* @return the underlying byte array directly.
*/
public byte[] get() {
assert (value != null);
return this.value;
}
/**
* Sets the byte data of this value. The given byte array is used directly as the backing array,
* so later changes made to the array reflect into this Value.
*
* @param b
* may not be null
*/
public void set(final byte[] b) {
requireNonNull(b);
this.value = b;
}
/**
* Sets the byte data of this value. The given byte array is copied.
*
* @param b
* may not be null
*/
public void copy(byte[] b) {
requireNonNull(b);
this.value = new byte[b.length];
System.arraycopy(b, 0, this.value, 0, b.length);
}
/**
* Gets the size of this value.
*
* @return size in bytes
*/
public int getSize() {
assert (value != null);
return this.value.length;
}
@Override
public void readFields(final DataInput in) throws IOException {
this.value = new byte[in.readInt()];
in.readFully(this.value, 0, this.value.length);
}
@Override
public void write(final DataOutput out) throws IOException {
out.writeInt(this.value.length);
out.write(this.value, 0, this.value.length);
}
// Below methods copied from BytesWritable
@Override
public int hashCode() {
return WritableComparator.hashBytes(value, this.value.length);
}
/**
* Define the sort order of the BytesWritable.
*
* @param right_obj
* The other bytes writable
* @return Positive if left is bigger than right, 0 if they are equal, and negative if left is
* smaller than right.
*/
@Override
public int compareTo(Object right_obj) {
return compareTo(((Value) right_obj).get());
}
/**
* Compares the bytes in this object to the specified byte array
*
* @return Positive if left is bigger than right, 0 if they are equal, and negative if left is
* smaller than right.
*/
public int compareTo(final byte[] that) {
int diff = this.value.length - that.length;
return (diff != 0) ? diff
: WritableComparator.compareBytes(this.value, 0, this.value.length, that, 0, that.length);
}
@Override
public boolean equals(Object right_obj) {
if (right_obj instanceof Value) {
return compareTo(right_obj) == 0;
}
return false;
}
/**
* Compares the bytes in this object to the specified byte array
*
* @return true if the contents of this Value is equivalent to the supplied byte array
* @since 2.0.0
*/
public boolean contentEquals(byte[] right_obj) {
return compareTo(right_obj) == 0;
}
@Override
public String toString() {
return new String(get(), UTF_8);
}
/**
* A Comparator optimized for Value.
*/
public static class Comparator extends WritableComparator {
private BytesWritable.Comparator comparator = new BytesWritable.Comparator();
/** constructor */
public Comparator() {
super(Value.class);
}
@Override
public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {
return comparator.compare(b1, s1, l1, b2, s2, l2);
}
}
static { // register this comparator
WritableComparator.define(Value.class, new Comparator());
}
}