001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.apache.hadoop.io;
020
021import java.io.IOException;
022import java.io.DataInput;
023import java.io.DataOutput;
024
025import org.apache.hadoop.classification.InterfaceAudience;
026import org.apache.hadoop.classification.InterfaceStability;
027
028/** 
029 * A byte sequence that is usable as a key or value.
030 * It is resizable and distinguishes between the size of the sequence and
031 * the current capacity. The hash function is the front of the md5 of the 
032 * buffer. The sort order is the same as memcmp.
033 */
034@InterfaceAudience.Public
035@InterfaceStability.Stable
036public class BytesWritable extends BinaryComparable
037    implements WritableComparable<BinaryComparable> {
038  private static final int LENGTH_BYTES = 4;
039  private static final byte[] EMPTY_BYTES = {};
040
041  private int size;
042  private byte[] bytes;
043  
044  /**
045   * Create a zero-size sequence.
046   */
047  public BytesWritable() {this(EMPTY_BYTES);}
048  
049  /**
050   * Create a BytesWritable using the byte array as the initial value.
051   * @param bytes This array becomes the backing storage for the object.
052   */
053  public BytesWritable(byte[] bytes) {
054    this(bytes, bytes.length);
055  }
056
057  /**
058   * Create a BytesWritable using the byte array as the initial value
059   * and length as the length. Use this constructor if the array is larger
060   * than the value it represents.
061   * @param bytes This array becomes the backing storage for the object.
062   * @param length The number of bytes to use from array.
063   */
064  public BytesWritable(byte[] bytes, int length) {
065    this.bytes = bytes;
066    this.size = length;
067  }
068  
069  /**
070   * Get a copy of the bytes that is exactly the length of the data.
071   * See {@link #getBytes()} for faster access to the underlying array.
072   */
073  public byte[] copyBytes() {
074    byte[] result = new byte[size];
075    System.arraycopy(bytes, 0, result, 0, size);
076    return result;
077  }
078  
079  /**
080   * Get the data backing the BytesWritable. Please use {@link #copyBytes()}
081   * if you need the returned array to be precisely the length of the data.
082   * @return The data is only valid between 0 and getLength() - 1.
083   */
084  @Override
085  public byte[] getBytes() {
086    return bytes;
087  }
088
089  /**
090   * Get the data from the BytesWritable.
091   * @deprecated Use {@link #getBytes()} instead.
092   */
093  @Deprecated
094  public byte[] get() {
095    return getBytes();
096  }
097
098  /**
099   * Get the current size of the buffer.
100   */
101  @Override
102  public int getLength() {
103    return size;
104  }
105
106  /**
107   * Get the current size of the buffer.
108   * @deprecated Use {@link #getLength()} instead.
109   */
110  @Deprecated
111  public int getSize() {
112    return getLength();
113  }
114  
115  /**
116   * Change the size of the buffer. The values in the old range are preserved
117   * and any new values are undefined. The capacity is changed if it is 
118   * necessary.
119   * @param size The new number of bytes
120   */
121  public void setSize(int size) {
122    if (size > getCapacity()) {
123      // Avoid overflowing the int too early by casting to a long.
124      long newSize = Math.min(Integer.MAX_VALUE, (3L * size) / 2L);
125      setCapacity((int) newSize);
126    }
127    this.size = size;
128  }
129  
130  /**
131   * Get the capacity, which is the maximum size that could handled without
132   * resizing the backing storage.
133   * @return The number of bytes
134   */
135  public int getCapacity() {
136    return bytes.length;
137  }
138  
139  /**
140   * Change the capacity of the backing storage.
141   * The data is preserved.
142   * @param new_cap The new capacity in bytes.
143   */
144  public void setCapacity(int new_cap) {
145    if (new_cap != getCapacity()) {
146      byte[] new_data = new byte[new_cap];
147      if (new_cap < size) {
148        size = new_cap;
149      }
150      if (size != 0) {
151        System.arraycopy(bytes, 0, new_data, 0, size);
152      }
153      bytes = new_data;
154    }
155  }
156
157  /**
158   * Set the BytesWritable to the contents of the given newData.
159   * @param newData the value to set this BytesWritable to.
160   */
161  public void set(BytesWritable newData) {
162    set(newData.bytes, 0, newData.size);
163  }
164
165  /**
166   * Set the value to a copy of the given byte range
167   * @param newData the new values to copy in
168   * @param offset the offset in newData to start at
169   * @param length the number of bytes to copy
170   */
171  public void set(byte[] newData, int offset, int length) {
172    setSize(0);
173    setSize(length);
174    System.arraycopy(newData, offset, bytes, 0, size);
175  }
176
177  // inherit javadoc
178  @Override
179  public void readFields(DataInput in) throws IOException {
180    setSize(0); // clear the old data
181    setSize(in.readInt());
182    in.readFully(bytes, 0, size);
183  }
184  
185  // inherit javadoc
186  @Override
187  public void write(DataOutput out) throws IOException {
188    out.writeInt(size);
189    out.write(bytes, 0, size);
190  }
191  
192  @Override
193  public int hashCode() {
194    return super.hashCode();
195  }
196
197  /**
198   * Are the two byte sequences equal?
199   */
200  @Override
201  public boolean equals(Object right_obj) {
202    if (right_obj instanceof BytesWritable)
203      return super.equals(right_obj);
204    return false;
205  }
206
207  /**
208   * Generate the stream of bytes as hex pairs separated by ' '.
209   */
210  @Override
211  public String toString() { 
212    StringBuilder sb = new StringBuilder(3*size);
213    for (int idx = 0; idx < size; idx++) {
214      // if not the first, put a blank separator in
215      if (idx != 0) {
216        sb.append(' ');
217      }
218      String num = Integer.toHexString(0xff & bytes[idx]);
219      // if it is only one digit, add a leading 0.
220      if (num.length() < 2) {
221        sb.append('0');
222      }
223      sb.append(num);
224    }
225    return sb.toString();
226  }
227
228  /** A Comparator optimized for BytesWritable. */ 
229  public static class Comparator extends WritableComparator {
230    public Comparator() {
231      super(BytesWritable.class);
232    }
233    
234    /**
235     * Compare the buffers in serialized form.
236     */
237    @Override
238    public int compare(byte[] b1, int s1, int l1,
239                       byte[] b2, int s2, int l2) {
240      return compareBytes(b1, s1+LENGTH_BYTES, l1-LENGTH_BYTES, 
241                          b2, s2+LENGTH_BYTES, l2-LENGTH_BYTES);
242    }
243  }
244  
245  static {                                        // register this comparator
246    WritableComparator.define(BytesWritable.class, new Comparator());
247  }
248  
249}