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 }