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    
019    package org.apache.hadoop.io;
020    
021    import java.io.IOException;
022    import java.io.DataInput;
023    import java.io.DataOutput;
024    
025    import org.apache.hadoop.classification.InterfaceAudience;
026    import 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
036    public 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    }