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.DataInput;
022 import java.io.DataOutput;
023 import java.io.IOException;
024 import java.lang.reflect.Array;
025 import java.util.HashMap;
026 import java.util.Map;
027
028 import org.apache.hadoop.HadoopIllegalArgumentException;
029 import org.apache.hadoop.classification.InterfaceAudience;
030 import 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
042 public 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