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