/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.io; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.lang.reflect.Array; import java.util.HashMap; import java.util.Map; import org.apache.hadoop.HadoopIllegalArgumentException; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; /** * This is a wrapper class. It wraps a Writable implementation around * an array of primitives (e.g., int[], long[], etc.), with optimized * wire format, and without creating new objects per element. * * This is a wrapper class only; it does not make a copy of the * underlying array. */ @InterfaceAudience.Public @InterfaceStability.Stable public class ArrayPrimitiveWritable implements Writable { //componentType is determined from the component type of the value array //during a "set" operation. It must be primitive. private Class<?> componentType = null; //declaredComponentType need not be declared, but if you do (by using the //ArrayPrimitiveWritable(Class<?>) constructor), it will provide typechecking //for all "set" operations. private Class<?> declaredComponentType = null; private int length; private Object value; //must be an array of <componentType>[length] private static final Map<String, Class<?>> PRIMITIVE_NAMES = new HashMap<String, Class<?>>(16); static { PRIMITIVE_NAMES.put(boolean.class.getName(), boolean.class); PRIMITIVE_NAMES.put(byte.class.getName(), byte.class); PRIMITIVE_NAMES.put(char.class.getName(), char.class); PRIMITIVE_NAMES.put(short.class.getName(), short.class); PRIMITIVE_NAMES.put(int.class.getName(), int.class); PRIMITIVE_NAMES.put(long.class.getName(), long.class); PRIMITIVE_NAMES.put(float.class.getName(), float.class); PRIMITIVE_NAMES.put(double.class.getName(), double.class); } private static Class<?> getPrimitiveClass(String className) { return PRIMITIVE_NAMES.get(className); } private static void checkPrimitive(Class<?> componentType) { if (componentType == null) { throw new HadoopIllegalArgumentException("null component type not allowed"); } if (! PRIMITIVE_NAMES.containsKey(componentType.getName())) { throw new HadoopIllegalArgumentException("input array component type " + componentType.getName() + " is not a candidate primitive type"); } } private void checkDeclaredComponentType(Class<?> componentType) { if ((declaredComponentType != null) && (componentType != declaredComponentType)) { throw new HadoopIllegalArgumentException("input array component type " + componentType.getName() + " does not match declared type " + declaredComponentType.getName()); } } private static void checkArray(Object value) { if (value == null) { throw new HadoopIllegalArgumentException("null value not allowed"); } if (! value.getClass().isArray()) { throw new HadoopIllegalArgumentException("non-array value of class " + value.getClass() + " not allowed"); } } /** * Construct an empty instance, for use during Writable read */ public ArrayPrimitiveWritable() { //empty constructor } /** * Construct an instance of known type but no value yet * for use with type-specific wrapper classes */ public ArrayPrimitiveWritable(Class<?> componentType) { checkPrimitive(componentType); this.declaredComponentType = componentType; } /** * Wrap an existing array of primitives * @param value - array of primitives */ public ArrayPrimitiveWritable(Object value) { set(value); } /** * Get the original array. * Client must cast it back to type componentType[] * (or may use type-specific wrapper classes). * @return - original array as Object */ public Object get() { return value; } public Class<?> getComponentType() { return componentType; } public Class<?> getDeclaredComponentType() { return declaredComponentType; } public boolean isDeclaredComponentType(Class<?> componentType) { return componentType == declaredComponentType; } public void set(Object value) { checkArray(value); Class<?> componentType = value.getClass().getComponentType(); checkPrimitive(componentType); checkDeclaredComponentType(componentType); this.componentType = componentType; this.value = value; this.length = Array.getLength(value); } /** * Do not use this class. * This is an internal class, purely for ObjectWritable to use as * a label class for transparent conversions of arrays of primitives * during wire protocol reads and writes. */ static class Internal extends ArrayPrimitiveWritable { Internal() { //use for reads super(); } Internal(Object value) { //use for writes super(value); } } //end Internal subclass declaration /* * @see org.apache.hadoop.io.Writable#write(java.io.DataOutput) */ @Override @SuppressWarnings("deprecation") public void write(DataOutput out) throws IOException { // write componentType UTF8.writeString(out, componentType.getName()); // write length out.writeInt(length); // do the inner loop. Walk the decision tree only once. if (componentType == Boolean.TYPE) { // boolean writeBooleanArray(out); } else if (componentType == Character.TYPE) { // char writeCharArray(out); } else if (componentType == Byte.TYPE) { // byte writeByteArray(out); } else if (componentType == Short.TYPE) { // short writeShortArray(out); } else if (componentType == Integer.TYPE) { // int writeIntArray(out); } else if (componentType == Long.TYPE) { // long writeLongArray(out); } else if (componentType == Float.TYPE) { // float writeFloatArray(out); } else if (componentType == Double.TYPE) { // double writeDoubleArray(out); } else { throw new IOException("Component type " + componentType.toString() + " is set as the output type, but no encoding is implemented for this type."); } } /* * @see org.apache.hadoop.io.Writable#readFields(java.io.DataInput) */ @Override public void readFields(DataInput in) throws IOException { // read and set the component type of the array @SuppressWarnings("deprecation") String className = UTF8.readString(in); Class<?> componentType = getPrimitiveClass(className); if (componentType == null) { throw new IOException("encoded array component type " + className + " is not a candidate primitive type"); } checkDeclaredComponentType(componentType); this.componentType = componentType; // read and set the length of the array int length = in.readInt(); if (length < 0) { throw new IOException("encoded array length is negative " + length); } this.length = length; // construct and read in the array value = Array.newInstance(componentType, length); // do the inner loop. Walk the decision tree only once. if (componentType == Boolean.TYPE) { // boolean readBooleanArray(in); } else if (componentType == Character.TYPE) { // char readCharArray(in); } else if (componentType == Byte.TYPE) { // byte readByteArray(in); } else if (componentType == Short.TYPE) { // short readShortArray(in); } else if (componentType == Integer.TYPE) { // int readIntArray(in); } else if (componentType == Long.TYPE) { // long readLongArray(in); } else if (componentType == Float.TYPE) { // float readFloatArray(in); } else if (componentType == Double.TYPE) { // double readDoubleArray(in); } else { throw new IOException("Encoded type " + className + " converted to valid component type " + componentType.toString() + " but no encoding is implemented for this type."); } } //For efficient implementation, there's no way around //the following massive code duplication. private void writeBooleanArray(DataOutput out) throws IOException { boolean[] v = (boolean[]) value; for (int i = 0; i < length; i++) out.writeBoolean(v[i]); } private void writeCharArray(DataOutput out) throws IOException { char[] v = (char[]) value; for (int i = 0; i < length; i++) out.writeChar(v[i]); } private void writeByteArray(DataOutput out) throws IOException { out.write((byte[]) value, 0, length); } private void writeShortArray(DataOutput out) throws IOException { short[] v = (short[]) value; for (int i = 0; i < length; i++) out.writeShort(v[i]); } private void writeIntArray(DataOutput out) throws IOException { int[] v = (int[]) value; for (int i = 0; i < length; i++) out.writeInt(v[i]); } private void writeLongArray(DataOutput out) throws IOException { long[] v = (long[]) value; for (int i = 0; i < length; i++) out.writeLong(v[i]); } private void writeFloatArray(DataOutput out) throws IOException { float[] v = (float[]) value; for (int i = 0; i < length; i++) out.writeFloat(v[i]); } private void writeDoubleArray(DataOutput out) throws IOException { double[] v = (double[]) value; for (int i = 0; i < length; i++) out.writeDouble(v[i]); } private void readBooleanArray(DataInput in) throws IOException { boolean[] v = (boolean[]) value; for (int i = 0; i < length; i++) v[i] = in.readBoolean(); } private void readCharArray(DataInput in) throws IOException { char[] v = (char[]) value; for (int i = 0; i < length; i++) v[i] = in.readChar(); } private void readByteArray(DataInput in) throws IOException { in.readFully((byte[]) value, 0, length); } private void readShortArray(DataInput in) throws IOException { short[] v = (short[]) value; for (int i = 0; i < length; i++) v[i] = in.readShort(); } private void readIntArray(DataInput in) throws IOException { int[] v = (int[]) value; for (int i = 0; i < length; i++) v[i] = in.readInt(); } private void readLongArray(DataInput in) throws IOException { long[] v = (long[]) value; for (int i = 0; i < length; i++) v[i] = in.readLong(); } private void readFloatArray(DataInput in) throws IOException { float[] v = (float[]) value; for (int i = 0; i < length; i++) v[i] = in.readFloat(); } private void readDoubleArray(DataInput in) throws IOException { double[] v = (double[]) value; for (int i = 0; i < length; i++) v[i] = in.readDouble(); } }