From caea76c042097c1624825163fb9831ef68c43eb4 Mon Sep 17 00:00:00 2001 From: Nick Dimiduk Date: Tue, 30 Jul 2013 12:32:42 -0700 Subject: [PATCH] HBASE-9091 Extend ByteRange Extend ByteRange to include a position marker for tracking a consumer's place within a range. Also update and clarify documentation. This class starts to become a mutable alternative to java.nio.HeapByteBuffer. --- .../org/apache/hadoop/hbase/util/ByteRange.java | 449 +++++++++++++++++---- .../apache/hadoop/hbase/util/ByteRangeTool.java | 65 --- .../apache/hadoop/hbase/util/ByteRangeUtils.java | 70 ++++ .../apache/hadoop/hbase/util/TestByteRange.java | 57 ++- .../codec/prefixtree/encode/row/RowNodeWriter.java | 4 +- .../prefixtree/encode/tokenize/TokenizerNode.java | 2 +- .../codec/prefixtree/column/TestColumnBuilder.java | 4 +- .../column/data/TestColumnDataSimple.java | 6 +- 8 files changed, 503 insertions(+), 154 deletions(-) delete mode 100644 hbase-common/src/main/java/org/apache/hadoop/hbase/util/ByteRangeTool.java create mode 100644 hbase-common/src/main/java/org/apache/hadoop/hbase/util/ByteRangeUtils.java diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/util/ByteRange.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/util/ByteRange.java index 6f91861..d419ba3 100644 --- a/hbase-common/src/main/java/org/apache/hadoop/hbase/util/ByteRange.java +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/util/ByteRange.java @@ -18,41 +18,65 @@ package org.apache.hadoop.hbase.util; +import java.nio.ByteBuffer; +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.classification.InterfaceStability; +import com.google.common.base.Preconditions; /** - * Lightweight, reusable class for specifying ranges of byte[]'s. CompareTo and equals methods are - * lexicographic, which is native to HBase. - *

+ * Lightweight, reusable class for specifying ranges of byte[]'s. + *

+ * ByteRange maintains three separate sets of state: an underlying byte[], a viewport + * into that byte[] as a range of bytes, and the consumer's current position in that range. The + * ByteRange is a mutable, reusable object, so the underlying byte[] can be modified after + * instantiation. This is done using the {@link #set(byte[])} and {@link #unset()} methods. Direct + * access to the byte[] is also available via {@link #getBytes()}. The viewport is defined by an + * offset into the byte[] and a length. The range of bytes is 0-indexed, + * and is accessed by index via the {@link #get()} and {@link #put(byte)} methods. ByteRange + * maintains a position into the range so that sequential calls to get or + * put can be made without the caller maintaining their own external index. + * position is considered transient, not fundamental to the definition of the range, + * and does not participate in comparison or copy operations. + *

+ *

+ * The state of a ByteRange is not synchronized and does to support concurrent access. + *

+ *

* This class differs from ByteBuffer: - *

  • On-heap bytes only - *
  • Implements equals, hashCode, and compareTo so that it can be used in standard java - * Collections, similar to String. - *
  • Does not maintain mark/position iterator state inside the class. Doing so leads to many bugs - * in complex applications. - *
  • Allows the addition of simple core methods like this.copyTo(that, offset). - *
  • Can be reused in tight loops like a major compaction which can save significant amounts of - * garbage. - *
  • (Without reuse, we throw off garbage like this thing: - * http://www.youtube.com/watch?v=lkmBH-MjZF4 + *
  • On-heap bytes only
  • + *
  • Raw byte access only; does not encode other primitives.
  • + *
  • Implements {@link #equals(Object)}, {@link #hashCode()}, and {@link #compareTo(ByteRange)} + * so that it can be used in standard java Collections. Comparison operations are lexicographic, + * which is native to HBase.
  • + *
  • Maintains a position marker only.
  • + *
  • Allows the addition of simple core methods like the deep and shallow copy methods.
  • + *
  • Can be reused in tight loops like a major compaction which can save significant amounts of + * garbage. (Without reuse, we throw off garbage like this thing.)
  • *

    - * Mutable, and always evaluates equals, hashCode, and compareTo based on the current contents. + *

    + * Mutable, and always evaluates {@link #equals(Object)}, {@link #hashCode()}, and + * {@link #compareTo(ByteRange)} based on the current contents. *

    - * Can contain convenience methods for comparing, printing, cloning, spawning new arrays, copying to - * other arrays, etc. Please place non-core methods into {@link ByteRangeTool}. + *

    + * Can contain convenience methods for comparing, printing, cloning, spawning new arrays, copying + * to other arrays, etc. Please place non-core methods into {@link ByteRangeUtils}. *

    + *

    * We may consider converting this to an interface and creating separate implementations for a * single byte[], a paged byte[] (growable byte[][]), a ByteBuffer, etc + *

    */ +@InterfaceAudience.Public +@InterfaceStability.Evolving public class ByteRange implements Comparable { private static final int UNSET_HASH_VALUE = -1; - - /********************** fields *****************************/ - - // Do not make these final, as the intention is to reuse objects of this class + // Note to maintainers: Do not make these final, as the intention is to + // reuse objects of this class /** * The array containing the bytes in this range. It will be >= length. @@ -65,6 +89,17 @@ public class ByteRange implements Comparable { private int offset; /** + * The current index into the range. Like ByteBuffer position, it points to the next value that + * will be read/written in the array. It provides the appearance of being 0-indexed, even though + * its value is calculated according to offset. + *

    + * Position is considered transient and does not participate in {@link #equals(Object)} or + * {@link #hashCode()} comparisons. + *

    + */ + private transient int position; + + /** * The number of bytes in the range. Offset + length must be <= bytes.length */ private int length; @@ -73,67 +108,353 @@ public class ByteRange implements Comparable { * Variable for lazy-caching the hashCode of this range. Useful for frequently used ranges, * long-lived ranges, or long ranges. */ - private int hash = UNSET_HASH_VALUE; + private transient int hash = UNSET_HASH_VALUE; + /** + * Create a new ByteRange lacking a backing array and an undefined viewport. + */ + public ByteRange() { + unset(); + } - /********************** construct ***********************/ + /** + * Create a new ByteRange over a new backing array of size capacity. The range's + * offset and length are 0 and capacity, respectively. + * @param capacity the size of the backing array. + */ + public ByteRange(int capacity) { + this(new byte[capacity]); + } - public ByteRange() { - set(new byte[0]);//Could probably get away with a null array if the need arises. + /** + * Create a new ByteRange over a backing array of size capacity. Specify the range's + * offset and length. + * @param capacity the size of the backing array. + * @param offset The offset into bytes considered the beginning of this range. + * @param length The length of this range. Similar to {@link ByteBuffer#limit()}. + */ + public ByteRange(int capacity, int offset, int length) { + this(new byte[capacity], offset, length); } + /** + * Create a new ByteRange over the provided bytes. Similar to + * {@link ByteBuffer#wrap(byte[])}. + * @param bytes The array to wrap. + */ public ByteRange(byte[] bytes) { set(bytes); } + /** + * Create a new ByteRange over the provided bytes. Similar to + * {@link ByteBuffer#wrap(byte[], int, int)}. + * @param bytes The array to wrap. + * @param offset The offset into bytes considered the beginning + * of this range. + * @param length The length of this range. Similar to {@link ByteBuffer#capacity()}. + */ public ByteRange(byte[] bytes, int offset, int length) { set(bytes, offset, length); } + // + // methods for managing the backing array and range viewport + // - /********************** write methods *************************/ + /** + * The underlying byte[]. + */ + public byte[] getBytes() { + return bytes; + } - public ByteRange clear() { + /** + * Nullifies this ByteRange. That is, it becomes a husk, being a range over no byte[] whatsoever. + * @return this + */ + public ByteRange unset() { clearHashCache(); - bytes = null; - offset = 0; - length = 0; + this.bytes = null; + this.offset = 0; + this.position = 0; + this.length = 0; return this; } + /** + * Reuse this ByteRange over a new byte[]. position and offset are set + * to zero and length is set to bytes.length. A null bytes + * IS supported, in which case this method will behave equivalently to {@link #unset()}. + * @param bytes the array to wrap. + * @return this + */ public ByteRange set(byte[] bytes) { + if (null == bytes) return unset(); clearHashCache(); this.bytes = bytes; this.offset = 0; - this.length = ArrayUtils.length(bytes); + this.position = 0; + this.length = bytes.length; return this; } + /** + * Reuse this ByteRange over a new byte[]. A null bytes IS supported, in which case + * this method will behave equivalently to {@link #unset()}, regardless of the values of + * offset and lengthbytes considered the beginning of this range. + * @param length The length of this range. Similar to {@link ByteBuffer#capacity()}. + * @return this. + */ public ByteRange set(byte[] bytes, int offset, int length) { + if (null == bytes) return unset(); + Preconditions.checkPositionIndex(offset + length, bytes.length); clearHashCache(); this.bytes = bytes; this.offset = offset; + this.position = 0; this.length = length; return this; } - public void setLength(int length) { + /** + * The offset, the index into the underlying byte[] at which this range begins. + * @see #getBytes(); + */ + public int getOffset() { + return offset; + } + + /** + * Update the beginning of this range. offset + length may not be greater than + * bytes.length. Resets position to 0. + * @param offset the new start of this range. + * @return this. + */ + public ByteRange setOffset(int offset) { + Preconditions.checkPositionIndex(offset + length, bytes.length); + clearHashCache(); + this.offset = offset; + this.position = 0; + return this; + } + + /** + * The length of the range. + */ + public int getLength() { + return length; + } + + /** + * Update the length of this range. offset + length may not be greater than + * bytes.length. If position is greater than the new + * length, sets position to length. + * @param length The new length of this range. + * @return this. + */ + public ByteRange setLength(int length) { + Preconditions.checkPositionIndex(offset + length, bytes.length); clearHashCache(); this.length = length; + this.position = Math.min(position, length); + return this; + } + + /** + * @return true when this range is of zero length, false otherwise. + */ + public boolean isEmpty() { + return isEmpty(this); + } + + /** + * @return true when range is of zero length, false otherwise. + */ + public static boolean isEmpty(ByteRange range) { + return range == null || range.length == 0; + } + + // + // methods for managing position and retrieving data + // + + /** + * The current position marker. This value is 0-indexed, relative to the beginning + * of the range. + */ + public int getPosition() { + return position; + } + + /** + * Update the position index. May not be greater than length. + * @param position the new position in this range. + */ + public void setPosition(int position) { + this.position = Preconditions.checkPositionIndex(position, length); } + /** + * The number of bytes remaining between position and the end of the range. + */ + public int getRemaining() { + return length - position; + } - /*********** read methods (add non-core methods to ByteRangeUtils) *************/ + /** + * Retrieve the next byte from this range. + */ + public byte get() { + return get(position++); + } /** - * @param index zero-based index - * @return single byte at index + * Retrieve the byte at position index. + * @param index zero-based index into this range. + * @return single byte at index. */ public byte get(int index) { - return bytes[offset + index]; + return bytes[offset + Preconditions.checkPositionIndex(index, length)]; } /** + * Fill dst with bytes from the range, starting from position. This + * range's position is incremented by the length of dst, the number of + * bytes copied. + * @param dst the destination of the copy. + * @return this. + */ + public ByteRange get(byte[] dst) { + return get(dst, 0, dst.length); + } + + /** + * Fill dst with bytes from the range, starting from the current + * position. length bytes are copied into dst, starting at + * offset. This range's position is incremented by the number of bytes + * copied. + * @param dst the destination of the copy. + * @param offset the offset into dst to start the copy. + * @param length the number of bytes to copy into dst. + * @return this. + */ + public ByteRange get(byte[] dst, int offset, int length) { + Preconditions.checkNotNull(dst); + Preconditions.checkPositionIndex(offset, length); + Preconditions.checkPositionIndex(this.position + length, this.length); + System.arraycopy(this.bytes, this.offset + this.position, dst, offset, length); + this.position += length; + return this; + } + + /** + * Fill dst with bytes from the range, starting from index. + * @param index zero-based index into this range. + * @param dst the destination of the copy. + * @return this. + */ + public ByteRange get(int index, byte[] dst) { + return get(index, dst, 0, dst.length); + } + + /** + * Fill dst with bytes from the range, starting from index. + * length bytes are copied into dst, starting at offset. + * @param index zero-based index into this range. + * @param dst the destination of the copy. + * @param offset the offset into dst to start the copy. + * @param length the number of bytes to copy into dst. + * @return this. + */ + public ByteRange get(int index, byte[] dst, int offset, int length) { + Preconditions.checkNotNull(dst); + Preconditions.checkPositionIndex(offset, length); + Preconditions.checkPositionIndex(index + length, this.length); + System.arraycopy(this.bytes, this.offset + index, dst, offset, length); + return this; + } + + /** + * Store val at the next position in this range. + * @param val the new value. + * @return this. + */ + public ByteRange put(byte val) { + return put(position++, val); + } + + /** + * Store val at index. + * @param index the index in the range where val is stored. + * @param val the value to store. + * @return this. + */ + public ByteRange put(int index, byte val) { + bytes[offset + Preconditions.checkPositionIndex(index, length)] = val; + return this; + } + + /** + * Store the content of val in this range, starting at the next position. + * @param val the new value. + * @return this. + */ + public ByteRange put(byte[] val) { + return put(val, 0, val.length); + } + + /** + * Store length bytes from val into this range. Bytes from + * val are copied starting at offset into the range, starting at + * the current position. + * @param val the new value. + * @param offset the offset in val from which to start copying. + * @param length the number of bytes to copy from val. + * @return this. + */ + public ByteRange put(byte[] val, int offset, int length) { + Preconditions.checkNotNull(val); + Preconditions.checkPositionIndex(this.position + length, this.length); + System.arraycopy(val, offset, this.bytes, this.offset + this.position, length); + this.position += length; + return this; + } + + /** + * Store val at index. + * @param index the index in the range where val is stored. + * @param val the value to store. + * @return this. + */ + public ByteRange put(int index, byte[] val) { + return put(index, val, 0, val.length); + } + + /** + * Store length bytes from val into this range, starting at + * index. Bytes from val are copied starting at offset + * into the range. + * @param index position in this range to start the copy. + * @param val the value to store. + * @param offset the offset in val from which to start copying. + * @param length the number of bytes to copy from val. + * @return this. + */ + public ByteRange put(int index, byte[] val, int offset, int length) { + Preconditions.checkNotNull(val); + Preconditions.checkPositionIndex(offset + length, this.length - index); + System.arraycopy(val, offset, this.bytes, this.offset + index, length); + return this; + } + + // + // methods for duplicating the current instance + // + + /** * Instantiate a new byte[] with exact length, which is at least 24 bytes + length. Copy the * contents of this range into it. * @return The newly cloned byte[]. @@ -146,14 +467,12 @@ public class ByteRange implements Comparable { /** * Create a new ByteRange with new backing byte[] and copy the state of this range into the new - * range. Copy the hash over if it is already calculated. + * range. Copy the hash over if it is already calculated. Offset, position, and length are not + * preserved. * @return Deep copy */ public ByteRange deepCopy() { ByteRange clone = new ByteRange(deepCopyToNewArray()); - if (isHashCached()) { - clone.hash = hash; - } return clone; } @@ -180,9 +499,24 @@ public class ByteRange implements Comparable { } /** + * Create a new ByteRange that points at this range's byte[]. Modifying the shallowCopy will + * modify the bytes in this range's array. Pass over the hash code if it is already cached. + * Position is not preserved in the copy. + * @return new ByteRange object referencing this range's byte[]. + */ + public ByteRange shallowCopy() { + ByteRange clone = new ByteRange(bytes, offset, length); + if (isHashCached()) { + clone.hash = hash; + } + return clone; + } + + /** * Create a new ByteRange that points at this range's byte[]. The new range can have different * values for offset and length, but modifying the shallowCopy will modify the bytes in this - * range's array. Pass over the hash code if it is already cached. + * range's array. Pass over the hash code if it is already cached. Position is not preserved in + * the copy. * @param innerOffset First byte of clone will be this.offset + copyOffset. * @param copyLength Number of bytes in the clone. * @return new ByteRange object referencing this range's byte[]. @@ -206,38 +540,9 @@ public class ByteRange implements Comparable { return maxCompares; } - public byte[] getBytes() { - return bytes; - } - - public int getOffset() { - return offset; - } - - public int getLength() { - return length; - } - - public boolean isEmpty(){ - return isEmpty(this); - } - - public boolean notEmpty(){ - return notEmpty(this); - } - - - /******************* static methods ************************/ - - public static boolean isEmpty(ByteRange range){ - return range == null || range.length == 0; - } - - public static boolean notEmpty(ByteRange range){ - return range != null && range.length > 0; - } - - /******************* standard methods *********************/ + // + // methods used for comparison + // @Override public boolean equals(Object thatObject) { diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/util/ByteRangeTool.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/util/ByteRangeTool.java deleted file mode 100644 index dd7cce7..0000000 --- a/hbase-common/src/main/java/org/apache/hadoop/hbase/util/ByteRangeTool.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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.hadoop.hbase.util; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Collection; - -import com.google.common.collect.Lists; - -/** - * Utility methods {@link ByteRange}. - */ -public class ByteRangeTool { - - public static ArrayList copyToNewArrays(Collection ranges) { - if (ranges == null) { - return new ArrayList(0); - } - ArrayList arrays = Lists.newArrayListWithCapacity(ranges.size()); - for (ByteRange range : ranges) { - arrays.add(range.deepCopyToNewArray()); - } - return arrays; - } - - public static ArrayList fromArrays(Collection arrays) { - if (arrays == null) { - return new ArrayList(0); - } - ArrayList ranges = Lists.newArrayListWithCapacity(arrays.size()); - for (byte[] array : arrays) { - ranges.add(new ByteRange(array)); - } - return ranges; - } - - public static void write(OutputStream os, ByteRange byteRange) throws IOException { - os.write(byteRange.getBytes(), byteRange.getOffset(), byteRange.getLength()); - } - - public static void write(OutputStream os, ByteRange byteRange, int byteRangeInnerOffset) - throws IOException { - os.write(byteRange.getBytes(), byteRange.getOffset() + byteRangeInnerOffset, - byteRange.getLength() - byteRangeInnerOffset); - } - -} diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/util/ByteRangeUtils.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/util/ByteRangeUtils.java new file mode 100644 index 0000000..29f5100 --- /dev/null +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/util/ByteRangeUtils.java @@ -0,0 +1,70 @@ +/* + * 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.hadoop.hbase.util; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; + +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.classification.InterfaceStability; + +import com.google.common.collect.Lists; + +/** + * Utility methods {@link ByteRange}. + */ +@InterfaceAudience.Public +@InterfaceStability.Evolving +public class ByteRangeUtils { + + public static ArrayList copyToNewArrays(Collection ranges) { + if (ranges == null) { + return new ArrayList(0); + } + ArrayList arrays = Lists.newArrayListWithCapacity(ranges.size()); + for (ByteRange range : ranges) { + arrays.add(range.deepCopyToNewArray()); + } + return arrays; + } + + public static ArrayList fromArrays(Collection arrays) { + if (arrays == null) { + return new ArrayList(0); + } + ArrayList ranges = Lists.newArrayListWithCapacity(arrays.size()); + for (byte[] array : arrays) { + ranges.add(new ByteRange(array)); + } + return ranges; + } + + public static void write(OutputStream os, ByteRange byteRange) throws IOException { + os.write(byteRange.getBytes(), byteRange.getOffset(), byteRange.getLength()); + } + + public static void write(OutputStream os, ByteRange byteRange, int byteRangeInnerOffset) + throws IOException { + os.write(byteRange.getBytes(), byteRange.getOffset() + byteRangeInnerOffset, + byteRange.getLength() - byteRangeInnerOffset); + } + +} diff --git a/hbase-common/src/test/java/org/apache/hadoop/hbase/util/TestByteRange.java b/hbase-common/src/test/java/org/apache/hadoop/hbase/util/TestByteRange.java index 5b50cb8..109e523 100644 --- a/hbase-common/src/test/java/org/apache/hadoop/hbase/util/TestByteRange.java +++ b/hbase-common/src/test/java/org/apache/hadoop/hbase/util/TestByteRange.java @@ -17,24 +17,22 @@ */ package org.apache.hadoop.hbase.util; -import junit.framework.Assert; -import junit.framework.TestCase; - import org.apache.hadoop.hbase.SmallTests; +import org.junit.Assert; +import org.junit.Test; import org.junit.experimental.categories.Category; @Category(SmallTests.class) -public class TestByteRange extends TestCase { +public class TestByteRange { + @Test public void testEmpty(){ Assert.assertTrue(ByteRange.isEmpty(null)); ByteRange r = new ByteRange(); Assert.assertTrue(ByteRange.isEmpty(r)); - Assert.assertFalse(ByteRange.notEmpty(r)); Assert.assertTrue(r.isEmpty()); - Assert.assertFalse(r.notEmpty()); - Assert.assertNotNull(r.getBytes());//should be empty byte[], but could change this behavior + r.set(new byte[0]); Assert.assertEquals(0, r.getBytes().length); Assert.assertEquals(0, r.getOffset()); Assert.assertEquals(0, r.getLength()); @@ -43,7 +41,8 @@ public class TestByteRange extends TestCase { Assert.assertEquals(0, r.hashCode()); } - public void testBasics(){ + @Test + public void testBasics() { ByteRange r = new ByteRange(new byte[]{1, 3, 2}); Assert.assertFalse(ByteRange.isEmpty(r)); Assert.assertNotNull(r.getBytes());//should be empty byte[], but could change this behavior @@ -71,5 +70,45 @@ public class TestByteRange extends TestCase { Assert.assertTrue(Bytes.equals(new byte[]{1, 3}, r.deepCopyToNewArray())); } -} + @Test + public void testPosition() { + ByteRange r = new ByteRange(5, 1, 3); + + // exercise single-byte put + r.put(Bytes.toBytes("f")[0]) + .put(Bytes.toBytes("o")[0]) + .put(Bytes.toBytes("o")[0]); + Assert.assertEquals(3, r.getPosition()); + Assert.assertArrayEquals( + new byte[] { 0, Bytes.toBytes("f")[0], Bytes.toBytes("o")[0], Bytes.toBytes("o")[0], 0 }, + r.getBytes()); + + // exercise multi-byte put + r.setPosition(0); + r.put(Bytes.toBytes("f")) + .put(Bytes.toBytes("o")) + .put(Bytes.toBytes("o")); + Assert.assertEquals(3, r.getPosition()); + Assert.assertArrayEquals( + new byte[] { 0, Bytes.toBytes("f")[0], Bytes.toBytes("o")[0], Bytes.toBytes("o")[0], 0 }, + r.getBytes()); + // exercise single-byte get + r.setPosition(0); + Assert.assertEquals(Bytes.toBytes("f")[0], r.get()); + Assert.assertEquals(Bytes.toBytes("o")[0], r.get()); + Assert.assertEquals(Bytes.toBytes("o")[0], r.get()); + + r.setPosition(1); + Assert.assertEquals(Bytes.toBytes("o")[0], r.get()); + + // exercise multi-byte get + r.setPosition(0); + byte[] dst = new byte[3]; + r.get(dst); + Assert.assertArrayEquals(Bytes.toBytes("foo"), dst); + + // set position to the end of the range; this should not throw. + r.setPosition(3); + } +} diff --git a/hbase-prefix-tree/src/main/java/org/apache/hadoop/hbase/codec/prefixtree/encode/row/RowNodeWriter.java b/hbase-prefix-tree/src/main/java/org/apache/hadoop/hbase/codec/prefixtree/encode/row/RowNodeWriter.java index 29ebafa..d253e39 100644 --- a/hbase-prefix-tree/src/main/java/org/apache/hadoop/hbase/codec/prefixtree/encode/row/RowNodeWriter.java +++ b/hbase-prefix-tree/src/main/java/org/apache/hadoop/hbase/codec/prefixtree/encode/row/RowNodeWriter.java @@ -28,7 +28,7 @@ import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.hbase.codec.prefixtree.PrefixTreeBlockMeta; import org.apache.hadoop.hbase.codec.prefixtree.encode.PrefixTreeEncoder; import org.apache.hadoop.hbase.codec.prefixtree.encode.tokenize.TokenizerNode; -import org.apache.hadoop.hbase.util.ByteRangeTool; +import org.apache.hadoop.hbase.util.ByteRangeUtils; import org.apache.hadoop.hbase.util.CollectionUtils; import org.apache.hadoop.hbase.util.vint.UFIntTool; import org.apache.hadoop.hbase.util.vint.UVIntTool; @@ -155,7 +155,7 @@ public class RowNodeWriter{ protected void writeRowToken(OutputStream os) throws IOException { UVIntTool.writeBytes(tokenWidth, os); int tokenStartIndex = tokenizerNode.isRoot() ? 0 : 1; - ByteRangeTool.write(os, tokenizerNode.getToken(), tokenStartIndex); + ByteRangeUtils.write(os, tokenizerNode.getToken(), tokenStartIndex); } /** diff --git a/hbase-prefix-tree/src/main/java/org/apache/hadoop/hbase/codec/prefixtree/encode/tokenize/TokenizerNode.java b/hbase-prefix-tree/src/main/java/org/apache/hadoop/hbase/codec/prefixtree/encode/tokenize/TokenizerNode.java index 077b5f5..dd6a3e1 100644 --- a/hbase-prefix-tree/src/main/java/org/apache/hadoop/hbase/codec/prefixtree/encode/tokenize/TokenizerNode.java +++ b/hbase-prefix-tree/src/main/java/org/apache/hadoop/hbase/codec/prefixtree/encode/tokenize/TokenizerNode.java @@ -164,7 +164,7 @@ public class TokenizerNode{ parent = null; nodeDepth = 0; tokenStartOffset = 0; - token.clear(); + token.unset(); numOccurrences = 0; children.clear();// branches & nubs diff --git a/hbase-prefix-tree/src/test/java/org/apache/hadoop/hbase/codec/prefixtree/column/TestColumnBuilder.java b/hbase-prefix-tree/src/test/java/org/apache/hadoop/hbase/codec/prefixtree/column/TestColumnBuilder.java index f1d0456..71b5b1c 100644 --- a/hbase-prefix-tree/src/test/java/org/apache/hadoop/hbase/codec/prefixtree/column/TestColumnBuilder.java +++ b/hbase-prefix-tree/src/test/java/org/apache/hadoop/hbase/codec/prefixtree/column/TestColumnBuilder.java @@ -29,7 +29,7 @@ import org.apache.hadoop.hbase.codec.prefixtree.encode.column.ColumnSectionWrite import org.apache.hadoop.hbase.codec.prefixtree.encode.tokenize.Tokenizer; import org.apache.hadoop.hbase.codec.prefixtree.encode.tokenize.TokenizerNode; import org.apache.hadoop.hbase.util.ByteRange; -import org.apache.hadoop.hbase.util.ByteRangeTool; +import org.apache.hadoop.hbase.util.ByteRangeUtils; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.byterange.impl.ByteRangeTreeSet; import org.junit.Assert; @@ -67,7 +67,7 @@ public class TestColumnBuilder { List inputs = columns.getInputs(); this.columnSorter = new ByteRangeTreeSet(inputs); this.sortedUniqueColumns = columnSorter.compile().getSortedRanges(); - List copies = ByteRangeTool.copyToNewArrays(sortedUniqueColumns); + List copies = ByteRangeUtils.copyToNewArrays(sortedUniqueColumns); Assert.assertTrue(Bytes.isSorted(copies)); this.blockMeta = new PrefixTreeBlockMeta(); this.blockMeta.setNumMetaBytes(0); diff --git a/hbase-prefix-tree/src/test/java/org/apache/hadoop/hbase/codec/prefixtree/column/data/TestColumnDataSimple.java b/hbase-prefix-tree/src/test/java/org/apache/hadoop/hbase/codec/prefixtree/column/data/TestColumnDataSimple.java index 5921116..bdb77ea 100644 --- a/hbase-prefix-tree/src/test/java/org/apache/hadoop/hbase/codec/prefixtree/column/data/TestColumnDataSimple.java +++ b/hbase-prefix-tree/src/test/java/org/apache/hadoop/hbase/codec/prefixtree/column/data/TestColumnDataSimple.java @@ -22,7 +22,7 @@ import java.util.List; import org.apache.hadoop.hbase.codec.prefixtree.column.TestColumnData; import org.apache.hadoop.hbase.util.ByteRange; -import org.apache.hadoop.hbase.util.ByteRangeTool; +import org.apache.hadoop.hbase.util.ByteRangeUtils; import org.apache.hadoop.hbase.util.Bytes; import com.google.common.collect.Lists; @@ -37,7 +37,7 @@ public class TestColumnDataSimple implements TestColumnData { d.add("abc"); d.add("bbc"); d.add("abc"); - return ByteRangeTool.fromArrays(Bytes.getUtf8ByteArrays(d)); + return ByteRangeUtils.fromArrays(Bytes.getUtf8ByteArrays(d)); } @Override @@ -46,7 +46,7 @@ public class TestColumnDataSimple implements TestColumnData { d.add("abc"); d.add("abcde"); d.add("bbc"); - return ByteRangeTool.fromArrays(Bytes.getUtf8ByteArrays(d)); + return ByteRangeUtils.fromArrays(Bytes.getUtf8ByteArrays(d)); } } -- 1.8.3.2