View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.util;
20  
21  import org.apache.hadoop.classification.InterfaceAudience;
22  import org.apache.hadoop.classification.InterfaceStability;
23  
24  /**
25   * A basic {@link ByteRange} implementation.
26   */
27  @InterfaceAudience.Public
28  @InterfaceStability.Evolving
29  public class SimpleByteRange implements ByteRange {
30  
31    private static final int UNSET_HASH_VALUE = -1;
32  
33    // Note to maintainers: Do not make these final, as the intention is to
34    // reuse objects of this class
35  
36    /**
37     * The array containing the bytes in this range. It will be >= length.
38     */
39    protected byte[] bytes;
40  
41    /**
42     * The index of the first byte in this range. {@code ByteRange.get(0)} will
43     * return bytes[offset].
44     */
45    protected int offset;
46  
47    /**
48     * The number of bytes in the range.  Offset + length must be <= bytes.length
49     */
50    protected int length;
51  
52    /**
53     * Variable for lazy-caching the hashCode of this range. Useful for
54     * frequently used ranges, long-lived ranges, or long ranges.
55     */
56    private int hash = UNSET_HASH_VALUE;
57  
58    /**
59     * Create a new {@code ByteRange} lacking a backing array and with an
60     * undefined viewport.
61     */
62    public SimpleByteRange() {
63      unset();
64    }
65  
66    /**
67     * Create a new {@code ByteRange} over a new backing array of size
68     * {@code capacity}. The range's offset and length are 0 and {@code capacity},
69     * respectively.
70     * @param capacity the size of the backing array.
71     */
72    public SimpleByteRange(int capacity) {
73      this(new byte[capacity]);
74    }
75  
76    /**
77     * Create a new {@code ByteRange} over the provided {@code bytes}.
78     * @param bytes The array to wrap.
79     */
80    public SimpleByteRange(byte[] bytes) {
81      set(bytes);
82    }
83  
84    /**
85     * Create a new {@code ByteRange} over the provided {@code bytes}.
86     * @param bytes The array to wrap.
87     * @param offset The offset into {@code bytes} considered the beginning
88     *          of this range.
89     * @param length The length of this range.
90     */
91    public SimpleByteRange(byte[] bytes, int offset, int length) {
92      set(bytes, offset, length);
93    }
94  
95    //
96    // methods for managing the backing array and range viewport
97    //
98  
99    @Override
100   public byte[] getBytes() {
101     return bytes;
102   }
103 
104   @Override
105   public ByteRange unset() {
106     clearHashCache();
107     this.bytes = null;
108     this.offset = 0;
109     this.length = 0;
110     return this;
111   }
112 
113   @Override
114   public ByteRange set(int capacity) {
115     return set(new byte[capacity]);
116   }
117 
118   @Override
119   public ByteRange set(byte[] bytes) {
120     if (null == bytes) return unset();
121     clearHashCache();
122     this.bytes = bytes;
123     this.offset = 0;
124     this.length = bytes.length;
125     return this;
126   }
127 
128   @Override
129   public ByteRange set(byte[] bytes, int offset, int length) {
130     if (null == bytes) return unset();
131     clearHashCache();
132     this.bytes = bytes;
133     this.offset = offset;
134     this.length = length;
135     return this;
136   }
137 
138   @Override
139   public int getOffset() {
140     return offset;
141   }
142 
143   @Override
144   public ByteRange setOffset(int offset) {
145     clearHashCache();
146     this.offset = offset;
147     return this;
148   }
149 
150   @Override
151   public int getLength() {
152     return length;
153   }
154 
155   @Override
156   public ByteRange setLength(int length) {
157     clearHashCache();
158     this.length = length;
159     return this;
160   }
161 
162   @Override
163   public boolean isEmpty() {
164     return isEmpty(this);
165   }
166 
167   /**
168    * @return true when {@code range} is of zero length, false otherwise.
169    */
170   public static boolean isEmpty(ByteRange range) {
171     return range == null || range.getLength() == 0;
172   }
173 
174   //
175   // methods for retrieving data
176   //
177 
178   @Override
179   public byte get(int index) {
180     return bytes[offset + index];
181   }
182 
183   @Override
184   public ByteRange get(int index, byte[] dst) {
185     if (0 == dst.length) return this;
186     return get(index, dst, 0, dst.length);
187   }
188 
189   @Override
190   public ByteRange get(int index, byte[] dst, int offset, int length) {
191     if (0 == length) return this;
192     System.arraycopy(this.bytes, this.offset + index, dst, offset, length);
193     return this;
194   }
195 
196   @Override
197   public ByteRange put(int index, byte val) {
198     bytes[offset + index] = val;
199     return this;
200   }
201 
202   @Override
203   public ByteRange put(int index, byte[] val) {
204     if (0 == val.length) return this;
205     return put(index, val, 0, val.length);
206   }
207 
208   @Override
209   public ByteRange put(int index, byte[] val, int offset, int length) {
210     if (0 == length) return this;
211     System.arraycopy(val, offset, this.bytes, this.offset + index, length);
212     return this;
213   }
214 
215   //
216   // methods for duplicating the current instance
217   //
218 
219   @Override
220   public byte[] deepCopyToNewArray() {
221     byte[] result = new byte[length];
222     System.arraycopy(bytes, offset, result, 0, length);
223     return result;
224   }
225 
226   @Override
227   public ByteRange deepCopy() {
228     SimpleByteRange clone = new SimpleByteRange(deepCopyToNewArray());
229     if (isHashCached()) {
230       clone.hash = hash;
231     }
232     return clone;
233   }
234 
235   @Override
236   public void deepCopyTo(byte[] destination, int destinationOffset) {
237     System.arraycopy(bytes, offset, destination, destinationOffset, length);
238   }
239 
240   @Override
241   public void deepCopySubRangeTo(int innerOffset, int copyLength, byte[] destination,
242       int destinationOffset) {
243     System.arraycopy(bytes, offset + innerOffset, destination, destinationOffset, copyLength);
244   }
245 
246   @Override
247   public ByteRange shallowCopy() {
248     SimpleByteRange clone = new SimpleByteRange(bytes, offset, length);
249     if (isHashCached()) {
250       clone.hash = hash;
251     }
252     return clone;
253   }
254 
255   @Override
256   public ByteRange shallowCopySubRange(int innerOffset, int copyLength) {
257     SimpleByteRange clone = new SimpleByteRange(bytes, offset + innerOffset, copyLength);
258     if (isHashCached()) {
259       clone.hash = hash;
260     }
261     return clone;
262   }
263 
264   //
265   // methods used for comparison
266   //
267 
268   @Override
269   public boolean equals(Object thatObject) {
270     if (thatObject == null){
271       return false;
272     }
273     if (this == thatObject) {
274       return true;
275     }
276     if (hashCode() != thatObject.hashCode()) {
277       return false;
278     }
279     if (!(thatObject instanceof SimpleByteRange)) {
280       return false;
281     }
282     SimpleByteRange that = (SimpleByteRange) thatObject;
283     return Bytes.equals(bytes, offset, length, that.bytes, that.offset, that.length);
284   }
285 
286   @Override
287   public int hashCode() {
288     if (isHashCached()) {// hash is already calculated and cached
289       return hash;
290     }
291     if (this.isEmpty()) {// return 0 for empty ByteRange
292       hash = 0;
293       return hash;
294     }
295     int off = offset;
296     hash = 0;
297     for (int i = 0; i < length; i++) {
298       hash = 31 * hash + bytes[off++];
299     }
300     return hash;
301   }
302 
303   private boolean isHashCached() {
304     return hash != UNSET_HASH_VALUE;
305   }
306 
307   protected void clearHashCache() {
308     hash = UNSET_HASH_VALUE;
309   }
310 
311   /**
312    * Bitwise comparison of each byte in the array.  Unsigned comparison, not
313    * paying attention to java's signed bytes.
314    */
315   @Override
316   public int compareTo(ByteRange other) {
317     return Bytes.compareTo(bytes, offset, length, other.getBytes(), other.getOffset(),
318       other.getLength());
319   }
320 
321   @Override
322   public String toString() {
323     return Bytes.toStringBinary(bytes, offset, length);
324   }
325 }