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 }