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 019package org.apache.hadoop.io; 020 021import java.io.DataInput; 022import java.io.DataOutput; 023import java.io.IOException; 024import java.lang.reflect.Array; 025import java.util.HashMap; 026import java.util.Map; 027 028import org.apache.hadoop.HadoopIllegalArgumentException; 029import org.apache.hadoop.classification.InterfaceAudience; 030import org.apache.hadoop.classification.InterfaceStability; 031 032/** 033 * This is a wrapper class. It wraps a Writable implementation around 034 * an array of primitives (e.g., int[], long[], etc.), with optimized 035 * wire format, and without creating new objects per element. 036 * 037 * This is a wrapper class only; it does not make a copy of the 038 * underlying array. 039 */ 040@InterfaceAudience.Public 041@InterfaceStability.Stable 042public class ArrayPrimitiveWritable implements Writable { 043 044 //componentType is determined from the component type of the value array 045 //during a "set" operation. It must be primitive. 046 private Class<?> componentType = null; 047 //declaredComponentType need not be declared, but if you do (by using the 048 //ArrayPrimitiveWritable(Class<?>) constructor), it will provide typechecking 049 //for all "set" operations. 050 private Class<?> declaredComponentType = null; 051 private int length; 052 private Object value; //must be an array of <componentType>[length] 053 054 private static final Map<String, Class<?>> PRIMITIVE_NAMES = 055 new HashMap<String, Class<?>>(16); 056 static { 057 PRIMITIVE_NAMES.put(boolean.class.getName(), boolean.class); 058 PRIMITIVE_NAMES.put(byte.class.getName(), byte.class); 059 PRIMITIVE_NAMES.put(char.class.getName(), char.class); 060 PRIMITIVE_NAMES.put(short.class.getName(), short.class); 061 PRIMITIVE_NAMES.put(int.class.getName(), int.class); 062 PRIMITIVE_NAMES.put(long.class.getName(), long.class); 063 PRIMITIVE_NAMES.put(float.class.getName(), float.class); 064 PRIMITIVE_NAMES.put(double.class.getName(), double.class); 065 } 066 067 private static Class<?> getPrimitiveClass(String className) { 068 return PRIMITIVE_NAMES.get(className); 069 } 070 071 private static void checkPrimitive(Class<?> componentType) { 072 if (componentType == null) { 073 throw new HadoopIllegalArgumentException("null component type not allowed"); 074 } 075 if (! PRIMITIVE_NAMES.containsKey(componentType.getName())) { 076 throw new HadoopIllegalArgumentException("input array component type " 077 + componentType.getName() + " is not a candidate primitive type"); 078 } 079 } 080 081 private void checkDeclaredComponentType(Class<?> componentType) { 082 if ((declaredComponentType != null) 083 && (componentType != declaredComponentType)) { 084 throw new HadoopIllegalArgumentException("input array component type " 085 + componentType.getName() + " does not match declared type " 086 + declaredComponentType.getName()); 087 } 088 } 089 090 private static void checkArray(Object value) { 091 if (value == null) { 092 throw new HadoopIllegalArgumentException("null value not allowed"); 093 } 094 if (! value.getClass().isArray()) { 095 throw new HadoopIllegalArgumentException("non-array value of class " 096 + value.getClass() + " not allowed"); 097 } 098 } 099 100 /** 101 * Construct an empty instance, for use during Writable read 102 */ 103 public ArrayPrimitiveWritable() { 104 //empty constructor 105 } 106 107 /** 108 * Construct an instance of known type but no value yet 109 * for use with type-specific wrapper classes 110 */ 111 public ArrayPrimitiveWritable(Class<?> componentType) { 112 checkPrimitive(componentType); 113 this.declaredComponentType = componentType; 114 } 115 116 /** 117 * Wrap an existing array of primitives 118 * @param value - array of primitives 119 */ 120 public ArrayPrimitiveWritable(Object value) { 121 set(value); 122 } 123 124 /** 125 * Get the original array. 126 * Client must cast it back to type componentType[] 127 * (or may use type-specific wrapper classes). 128 * @return - original array as Object 129 */ 130 public Object get() { return value; } 131 132 public Class<?> getComponentType() { return componentType; } 133 134 public Class<?> getDeclaredComponentType() { return declaredComponentType; } 135 136 public boolean isDeclaredComponentType(Class<?> componentType) { 137 return componentType == declaredComponentType; 138 } 139 140 public void set(Object value) { 141 checkArray(value); 142 Class<?> componentType = value.getClass().getComponentType(); 143 checkPrimitive(componentType); 144 checkDeclaredComponentType(componentType); 145 this.componentType = componentType; 146 this.value = value; 147 this.length = Array.getLength(value); 148 } 149 150 /** 151 * Do not use this class. 152 * This is an internal class, purely for ObjectWritable to use as 153 * a label class for transparent conversions of arrays of primitives 154 * during wire protocol reads and writes. 155 */ 156 static class Internal extends ArrayPrimitiveWritable { 157 Internal() { //use for reads 158 super(); 159 } 160 161 Internal(Object value) { //use for writes 162 super(value); 163 } 164 } //end Internal subclass declaration 165 166 /* 167 * @see org.apache.hadoop.io.Writable#write(java.io.DataOutput) 168 */ 169 @Override 170 @SuppressWarnings("deprecation") 171 public void write(DataOutput out) throws IOException { 172 // write componentType 173 UTF8.writeString(out, componentType.getName()); 174 // write length 175 out.writeInt(length); 176 177 // do the inner loop. Walk the decision tree only once. 178 if (componentType == Boolean.TYPE) { // boolean 179 writeBooleanArray(out); 180 } else if (componentType == Character.TYPE) { // char 181 writeCharArray(out); 182 } else if (componentType == Byte.TYPE) { // byte 183 writeByteArray(out); 184 } else if (componentType == Short.TYPE) { // short 185 writeShortArray(out); 186 } else if (componentType == Integer.TYPE) { // int 187 writeIntArray(out); 188 } else if (componentType == Long.TYPE) { // long 189 writeLongArray(out); 190 } else if (componentType == Float.TYPE) { // float 191 writeFloatArray(out); 192 } else if (componentType == Double.TYPE) { // double 193 writeDoubleArray(out); 194 } else { 195 throw new IOException("Component type " + componentType.toString() 196 + " is set as the output type, but no encoding is implemented for this type."); 197 } 198 } 199 200 /* 201 * @see org.apache.hadoop.io.Writable#readFields(java.io.DataInput) 202 */ 203 @Override 204 public void readFields(DataInput in) throws IOException { 205 206 // read and set the component type of the array 207 @SuppressWarnings("deprecation") 208 String className = UTF8.readString(in); 209 Class<?> componentType = getPrimitiveClass(className); 210 if (componentType == null) { 211 throw new IOException("encoded array component type " 212 + className + " is not a candidate primitive type"); 213 } 214 checkDeclaredComponentType(componentType); 215 this.componentType = componentType; 216 217 // read and set the length of the array 218 int length = in.readInt(); 219 if (length < 0) { 220 throw new IOException("encoded array length is negative " + length); 221 } 222 this.length = length; 223 224 // construct and read in the array 225 value = Array.newInstance(componentType, length); 226 227 // do the inner loop. Walk the decision tree only once. 228 if (componentType == Boolean.TYPE) { // boolean 229 readBooleanArray(in); 230 } else if (componentType == Character.TYPE) { // char 231 readCharArray(in); 232 } else if (componentType == Byte.TYPE) { // byte 233 readByteArray(in); 234 } else if (componentType == Short.TYPE) { // short 235 readShortArray(in); 236 } else if (componentType == Integer.TYPE) { // int 237 readIntArray(in); 238 } else if (componentType == Long.TYPE) { // long 239 readLongArray(in); 240 } else if (componentType == Float.TYPE) { // float 241 readFloatArray(in); 242 } else if (componentType == Double.TYPE) { // double 243 readDoubleArray(in); 244 } else { 245 throw new IOException("Encoded type " + className 246 + " converted to valid component type " + componentType.toString() 247 + " but no encoding is implemented for this type."); 248 } 249 } 250 251 //For efficient implementation, there's no way around 252 //the following massive code duplication. 253 254 private void writeBooleanArray(DataOutput out) throws IOException { 255 boolean[] v = (boolean[]) value; 256 for (int i = 0; i < length; i++) 257 out.writeBoolean(v[i]); 258 } 259 260 private void writeCharArray(DataOutput out) throws IOException { 261 char[] v = (char[]) value; 262 for (int i = 0; i < length; i++) 263 out.writeChar(v[i]); 264 } 265 266 private void writeByteArray(DataOutput out) throws IOException { 267 out.write((byte[]) value, 0, length); 268 } 269 270 private void writeShortArray(DataOutput out) throws IOException { 271 short[] v = (short[]) value; 272 for (int i = 0; i < length; i++) 273 out.writeShort(v[i]); 274 } 275 276 private void writeIntArray(DataOutput out) throws IOException { 277 int[] v = (int[]) value; 278 for (int i = 0; i < length; i++) 279 out.writeInt(v[i]); 280 } 281 282 private void writeLongArray(DataOutput out) throws IOException { 283 long[] v = (long[]) value; 284 for (int i = 0; i < length; i++) 285 out.writeLong(v[i]); 286 } 287 288 private void writeFloatArray(DataOutput out) throws IOException { 289 float[] v = (float[]) value; 290 for (int i = 0; i < length; i++) 291 out.writeFloat(v[i]); 292 } 293 294 private void writeDoubleArray(DataOutput out) throws IOException { 295 double[] v = (double[]) value; 296 for (int i = 0; i < length; i++) 297 out.writeDouble(v[i]); 298 } 299 300 private void readBooleanArray(DataInput in) throws IOException { 301 boolean[] v = (boolean[]) value; 302 for (int i = 0; i < length; i++) 303 v[i] = in.readBoolean(); 304 } 305 306 private void readCharArray(DataInput in) throws IOException { 307 char[] v = (char[]) value; 308 for (int i = 0; i < length; i++) 309 v[i] = in.readChar(); 310 } 311 312 private void readByteArray(DataInput in) throws IOException { 313 in.readFully((byte[]) value, 0, length); 314 } 315 316 private void readShortArray(DataInput in) throws IOException { 317 short[] v = (short[]) value; 318 for (int i = 0; i < length; i++) 319 v[i] = in.readShort(); 320 } 321 322 private void readIntArray(DataInput in) throws IOException { 323 int[] v = (int[]) value; 324 for (int i = 0; i < length; i++) 325 v[i] = in.readInt(); 326 } 327 328 private void readLongArray(DataInput in) throws IOException { 329 long[] v = (long[]) value; 330 for (int i = 0; i < length; i++) 331 v[i] = in.readLong(); 332 } 333 334 private void readFloatArray(DataInput in) throws IOException { 335 float[] v = (float[]) value; 336 for (int i = 0; i < length; i++) 337 v[i] = in.readFloat(); 338 } 339 340 private void readDoubleArray(DataInput in) throws IOException { 341 double[] v = (double[]) value; 342 for (int i = 0; i < length; i++) 343 v[i] = in.readDouble(); 344 } 345} 346