1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.hardware.camera2.marshal.impl; 17 18 import android.hardware.camera2.marshal.Marshaler; 19 import android.hardware.camera2.marshal.MarshalQueryable; 20 import android.hardware.camera2.marshal.MarshalRegistry; 21 import android.hardware.camera2.utils.TypeReference; 22 import android.util.Log; 23 24 import static android.hardware.camera2.marshal.MarshalHelpers.isUnwrappedPrimitiveClass; 25 import static android.hardware.camera2.marshal.MarshalHelpers.getPrimitiveTypeClass; 26 27 import java.lang.reflect.Array; 28 import java.nio.ByteBuffer; 29 import java.nio.DoubleBuffer; 30 import java.nio.FloatBuffer; 31 import java.nio.IntBuffer; 32 import java.nio.LongBuffer; 33 import java.util.ArrayList; 34 35 /** 36 * Marshal any array {@code T}. 37 * 38 * <p>To marshal any {@code T} to/from a native type, the marshaler for T to/from that native type 39 * also has to exist.</p> 40 * 41 * <p>{@code T} can be either a T2[] where T2 is an object type, or a P[] where P is a 42 * built-in primitive (e.g. int[], float[], etc).</p> 43 44 * @param <T> the type of the array (e.g. T = int[], or T = Rational[]) 45 */ 46 public class MarshalQueryableArray<T> implements MarshalQueryable<T> { 47 48 private static final String TAG = MarshalQueryableArray.class.getSimpleName(); 49 private static final boolean DEBUG = false; 50 51 private static interface PrimitiveArrayFiller { fillArray(Object arr, int size, ByteBuffer buffer)52 public void fillArray(Object arr, int size, ByteBuffer buffer); getPrimitiveArrayFiller(Class<?> componentType)53 static PrimitiveArrayFiller getPrimitiveArrayFiller(Class<?> componentType) { 54 if (componentType == int.class) { 55 return new PrimitiveArrayFiller() { 56 @Override 57 public void fillArray(Object arr, int size, ByteBuffer buffer) { 58 IntBuffer ib = buffer.asIntBuffer().get(int[].class.cast(arr), 0, size); 59 // Update buffer position since the IntBuffer has independent position. 60 buffer.position(buffer.position() + ib.position() * Integer.BYTES); 61 } 62 }; 63 } else if (componentType == float.class) { 64 return new PrimitiveArrayFiller() { 65 @Override 66 public void fillArray(Object arr, int size, ByteBuffer buffer) { 67 FloatBuffer fb = 68 buffer.asFloatBuffer().get(float[].class.cast(arr), 0, size); 69 buffer.position(buffer.position() + fb.position() * Float.BYTES); 70 } 71 }; 72 } else if (componentType == long.class) { 73 return new PrimitiveArrayFiller() { 74 @Override 75 public void fillArray(Object arr, int size, ByteBuffer buffer) { 76 LongBuffer lb = 77 buffer.asLongBuffer().get(long[].class.cast(arr), 0, size); 78 buffer.position(buffer.position() + lb.position() * Long.BYTES); 79 } 80 }; 81 } else if (componentType == double.class) { 82 return new PrimitiveArrayFiller() { 83 @Override 84 public void fillArray(Object arr, int size, ByteBuffer buffer) { 85 DoubleBuffer db = 86 buffer.asDoubleBuffer().get(double[].class.cast(arr), 0, size); 87 buffer.position(buffer.position() + db.position() * Double.BYTES); 88 } 89 }; 90 } else if (componentType == byte.class) { 91 return new PrimitiveArrayFiller() { 92 @Override 93 public void fillArray(Object arr, int size, ByteBuffer buffer) { 94 buffer.get(byte[].class.cast(arr), 0, size); 95 } 96 }; 97 } 98 throw new UnsupportedOperationException("PrimitiveArrayFiller of type " 99 + componentType.getName() + " not supported"); 100 } 101 }; 102 103 private class MarshalerArray extends Marshaler<T> { 104 private final Class<T> mClass; 105 private final Marshaler<?> mComponentMarshaler; 106 private final Class<?> mComponentClass; 107 108 @SuppressWarnings("unchecked") 109 protected MarshalerArray(TypeReference<T> typeReference, int nativeType) { 110 super(MarshalQueryableArray.this, typeReference, nativeType); 111 112 mClass = (Class<T>)typeReference.getRawType(); 113 114 TypeReference<?> componentToken = typeReference.getComponentType(); 115 mComponentMarshaler = MarshalRegistry.getMarshaler(componentToken, mNativeType); 116 mComponentClass = componentToken.getRawType(); 117 } 118 119 @Override 120 public void marshal(T value, ByteBuffer buffer) { 121 int length = Array.getLength(value); 122 for (int i = 0; i < length; ++i) { 123 marshalArrayElement(mComponentMarshaler, buffer, value, i); 124 } 125 } 126 127 @Override 128 public T unmarshal(ByteBuffer buffer) { 129 Object array; 130 131 int elementSize = mComponentMarshaler.getNativeSize(); 132 133 if (elementSize != Marshaler.NATIVE_SIZE_DYNAMIC) { 134 int remaining = buffer.remaining(); 135 int arraySize = remaining / elementSize; 136 137 if (remaining % elementSize != 0) { 138 throw new UnsupportedOperationException("Arrays for " + mTypeReference 139 + " must be packed tighly into a multiple of " + elementSize 140 + "; but there are " + (remaining % elementSize) + " left over bytes"); 141 } 142 143 if (DEBUG) { 144 Log.v(TAG, String.format( 145 "Attempting to unpack array (count = %d, element size = %d, bytes " 146 + "remaining = %d) for type %s", 147 arraySize, elementSize, remaining, mClass)); 148 } 149 150 array = Array.newInstance(mComponentClass, arraySize); 151 if (isUnwrappedPrimitiveClass(mComponentClass) && 152 mComponentClass == getPrimitiveTypeClass(mNativeType)) { 153 PrimitiveArrayFiller.getPrimitiveArrayFiller(mComponentClass).fillArray(array, 154 arraySize, buffer); 155 } else { 156 for (int i = 0; i < arraySize; ++i) { 157 Object elem = mComponentMarshaler.unmarshal(buffer); 158 Array.set(array, i, elem); 159 } 160 } 161 } else { 162 // Dynamic size, use an array list. 163 ArrayList<Object> arrayList = new ArrayList<Object>(); 164 165 // Assumes array is packed tightly; no unused bytes allowed 166 while (buffer.hasRemaining()) { 167 Object elem = mComponentMarshaler.unmarshal(buffer); 168 arrayList.add(elem); 169 } 170 171 int arraySize = arrayList.size(); 172 array = copyListToArray(arrayList, Array.newInstance(mComponentClass, arraySize)); 173 } 174 175 if (buffer.remaining() != 0) { 176 Log.e(TAG, "Trailing bytes (" + buffer.remaining() + ") left over after unpacking " 177 + mClass); 178 } 179 180 return mClass.cast(array); 181 } 182 183 @Override 184 public int getNativeSize() { 185 return NATIVE_SIZE_DYNAMIC; 186 } 187 188 @Override 189 public int calculateMarshalSize(T value) { 190 int elementSize = mComponentMarshaler.getNativeSize(); 191 int arrayLength = Array.getLength(value); 192 193 if (elementSize != Marshaler.NATIVE_SIZE_DYNAMIC) { 194 // The fast way. Every element size is uniform. 195 return elementSize * arrayLength; 196 } else { 197 // The slow way. Accumulate size for each element. 198 int size = 0; 199 for (int i = 0; i < arrayLength; ++i) { 200 size += calculateElementMarshalSize(mComponentMarshaler, value, i); 201 } 202 203 return size; 204 } 205 } 206 207 /* 208 * Helpers to avoid compiler errors regarding types with wildcards (?) 209 */ 210 211 @SuppressWarnings("unchecked") 212 private <TElem> void marshalArrayElement(Marshaler<TElem> marshaler, 213 ByteBuffer buffer, Object array, int index) { 214 marshaler.marshal((TElem)Array.get(array, index), buffer); 215 } 216 217 @SuppressWarnings("unchecked") 218 private Object copyListToArray(ArrayList<?> arrayList, Object arrayDest) { 219 return arrayList.toArray((T[]) arrayDest); 220 } 221 222 @SuppressWarnings("unchecked") 223 private <TElem> int calculateElementMarshalSize(Marshaler<TElem> marshaler, 224 Object array, int index) { 225 Object elem = Array.get(array, index); 226 227 return marshaler.calculateMarshalSize((TElem) elem); 228 } 229 } 230 231 @Override 232 public Marshaler<T> createMarshaler(TypeReference<T> managedType, int nativeType) { 233 return new MarshalerArray(managedType, nativeType); 234 } 235 236 @Override 237 public boolean isTypeMappingSupported(TypeReference<T> managedType, int nativeType) { 238 // support both ConcreteType[] and GenericType<ConcreteType>[] 239 return managedType.getRawType().isArray(); 240 241 // TODO: Should this recurse deeper and check that there is 242 // a valid marshaler for the ConcreteType as well? 243 } 244 } 245