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      setCapacity(size * 3 / 2);
124    }
125    this.size = size;
126  }
127  
128  /**
129   * Get the capacity, which is the maximum size that could handled without
130   * resizing the backing storage.
131   * @return The number of bytes
132   */
133  public int getCapacity() {
134    return bytes.length;
135  }
136  
137  /**
138   * Change the capacity of the backing storage.
139   * The data is preserved.
140   * @param new_cap The new capacity in bytes.
141   */
142  public void setCapacity(int new_cap) {
143    if (new_cap != getCapacity()) {
144      byte[] new_data = new byte[new_cap];
145      if (new_cap < size) {
146        size = new_cap;
147      }
148      if (size != 0) {
149        System.arraycopy(bytes, 0, new_data, 0, size);
150      }
151      bytes = new_data;
152    }
153  }
154
155  /**
156   * Set the BytesWritable to the contents of the given newData.
157   * @param newData the value to set this BytesWritable to.
158   */
159  public void set(BytesWritable newData) {
160    set(newData.bytes, 0, newData.size);
161  }
162
163  /**
164   * Set the value to a copy of the given byte range
165   * @param newData the new values to copy in
166   * @param offset the offset in newData to start at
167   * @param length the number of bytes to copy
168   */
169  public void set(byte[] newData, int offset, int length) {
170    setSize(0);
171    setSize(length);
172    System.arraycopy(newData, offset, bytes, 0, size);
173  }
174
175  // inherit javadoc
176  @Override
177  public void readFields(DataInput in) throws IOException {
178    setSize(0); // clear the old data
179    setSize(in.readInt());
180    in.readFully(bytes, 0, size);
181  }
182  
183  // inherit javadoc
184  @Override
185  public void write(DataOutput out) throws IOException {
186    out.writeInt(size);
187    out.write(bytes, 0, size);
188  }
189  
190  @Override
191  public int hashCode() {
192    return super.hashCode();
193  }
194
195  /**
196   * Are the two byte sequences equal?
197   */
198  @Override
199  public boolean equals(Object right_obj) {
200    if (right_obj instanceof BytesWritable)
201      return super.equals(right_obj);
202    return false;
203  }
204
205  /**
206   * Generate the stream of bytes as hex pairs separated by ' '.
207   */
208  @Override
209  public String toString() { 
210    StringBuilder sb = new StringBuilder(3*size);
211    for (int idx = 0; idx < size; idx++) {
212      // if not the first, put a blank separator in
213      if (idx != 0) {
214        sb.append(' ');
215      }
216      String num = Integer.toHexString(0xff & bytes[idx]);
217      // if it is only one digit, add a leading 0.
218      if (num.length() < 2) {
219        sb.append('0');
220      }
221      sb.append(num);
222    }
223    return sb.toString();
224  }
225
226  /** A Comparator optimized for BytesWritable. */ 
227  public static class Comparator extends WritableComparator {
228    public Comparator() {
229      super(BytesWritable.class);
230    }
231    
232    /**
233     * Compare the buffers in serialized form.
234     */
235    @Override
236    public int compare(byte[] b1, int s1, int l1,
237                       byte[] b2, int s2, int l2) {
238      return compareBytes(b1, s1+LENGTH_BYTES, l1-LENGTH_BYTES, 
239                          b2, s2+LENGTH_BYTES, l2-LENGTH_BYTES);
240    }
241  }
242  
243  static {                                        // register this comparator
244    WritableComparator.define(BytesWritable.class, new Comparator());
245  }
246  
247}