blob: 2b6f3166bc2824f4adf454d2943d302e0d285a81 [file] [log] [blame]
/**
* 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();
}
}