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 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
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      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    }