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 }