| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed 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 android.hardware.camera2.marshal.impl; |
| |
| import android.hardware.camera2.marshal.Marshaler; |
| import android.hardware.camera2.marshal.MarshalQueryable; |
| import android.hardware.camera2.marshal.MarshalRegistry; |
| import android.hardware.camera2.utils.TypeReference; |
| import android.util.Log; |
| |
| import static android.hardware.camera2.marshal.MarshalHelpers.isUnwrappedPrimitiveClass; |
| import static android.hardware.camera2.marshal.MarshalHelpers.getPrimitiveTypeClass; |
| |
| import java.lang.reflect.Array; |
| import java.nio.ByteBuffer; |
| import java.nio.DoubleBuffer; |
| import java.nio.FloatBuffer; |
| import java.nio.IntBuffer; |
| import java.nio.LongBuffer; |
| import java.util.ArrayList; |
| |
| /** |
| * Marshal any array {@code T}. |
| * |
| * <p>To marshal any {@code T} to/from a native type, the marshaler for T to/from that native type |
| * also has to exist.</p> |
| * |
| * <p>{@code T} can be either a T2[] where T2 is an object type, or a P[] where P is a |
| * built-in primitive (e.g. int[], float[], etc).</p> |
| |
| * @param <T> the type of the array (e.g. T = int[], or T = Rational[]) |
| */ |
| public class MarshalQueryableArray<T> implements MarshalQueryable<T> { |
| |
| private static final String TAG = MarshalQueryableArray.class.getSimpleName(); |
| private static final boolean DEBUG = false; |
| |
| private static interface PrimitiveArrayFiller { |
| public void fillArray(Object arr, int size, ByteBuffer buffer); |
| static PrimitiveArrayFiller getPrimitiveArrayFiller(Class<?> componentType) { |
| if (componentType == int.class) { |
| return new PrimitiveArrayFiller() { |
| @Override |
| public void fillArray(Object arr, int size, ByteBuffer buffer) { |
| IntBuffer ib = buffer.asIntBuffer().get(int[].class.cast(arr), 0, size); |
| // Update buffer position since the IntBuffer has independent position. |
| buffer.position(buffer.position() + ib.position() * Integer.BYTES); |
| } |
| }; |
| } else if (componentType == float.class) { |
| return new PrimitiveArrayFiller() { |
| @Override |
| public void fillArray(Object arr, int size, ByteBuffer buffer) { |
| FloatBuffer fb = |
| buffer.asFloatBuffer().get(float[].class.cast(arr), 0, size); |
| buffer.position(buffer.position() + fb.position() * Float.BYTES); |
| } |
| }; |
| } else if (componentType == long.class) { |
| return new PrimitiveArrayFiller() { |
| @Override |
| public void fillArray(Object arr, int size, ByteBuffer buffer) { |
| LongBuffer lb = |
| buffer.asLongBuffer().get(long[].class.cast(arr), 0, size); |
| buffer.position(buffer.position() + lb.position() * Long.BYTES); |
| } |
| }; |
| } else if (componentType == double.class) { |
| return new PrimitiveArrayFiller() { |
| @Override |
| public void fillArray(Object arr, int size, ByteBuffer buffer) { |
| DoubleBuffer db = |
| buffer.asDoubleBuffer().get(double[].class.cast(arr), 0, size); |
| buffer.position(buffer.position() + db.position() * Double.BYTES); |
| } |
| }; |
| } else if (componentType == byte.class) { |
| return new PrimitiveArrayFiller() { |
| @Override |
| public void fillArray(Object arr, int size, ByteBuffer buffer) { |
| buffer.get(byte[].class.cast(arr), 0, size); |
| } |
| }; |
| } |
| throw new UnsupportedOperationException("PrimitiveArrayFiller of type " |
| + componentType.getName() + " not supported"); |
| } |
| }; |
| |
| private class MarshalerArray extends Marshaler<T> { |
| private final Class<T> mClass; |
| private final Marshaler<?> mComponentMarshaler; |
| private final Class<?> mComponentClass; |
| |
| @SuppressWarnings("unchecked") |
| protected MarshalerArray(TypeReference<T> typeReference, int nativeType) { |
| super(MarshalQueryableArray.this, typeReference, nativeType); |
| |
| mClass = (Class<T>)typeReference.getRawType(); |
| |
| TypeReference<?> componentToken = typeReference.getComponentType(); |
| mComponentMarshaler = MarshalRegistry.getMarshaler(componentToken, mNativeType); |
| mComponentClass = componentToken.getRawType(); |
| } |
| |
| @Override |
| public void marshal(T value, ByteBuffer buffer) { |
| int length = Array.getLength(value); |
| for (int i = 0; i < length; ++i) { |
| marshalArrayElement(mComponentMarshaler, buffer, value, i); |
| } |
| } |
| |
| @Override |
| public T unmarshal(ByteBuffer buffer) { |
| Object array; |
| |
| int elementSize = mComponentMarshaler.getNativeSize(); |
| |
| if (elementSize != Marshaler.NATIVE_SIZE_DYNAMIC) { |
| int remaining = buffer.remaining(); |
| int arraySize = remaining / elementSize; |
| |
| if (remaining % elementSize != 0) { |
| throw new UnsupportedOperationException("Arrays for " + mTypeReference |
| + " must be packed tighly into a multiple of " + elementSize |
| + "; but there are " + (remaining % elementSize) + " left over bytes"); |
| } |
| |
| if (DEBUG) { |
| Log.v(TAG, String.format( |
| "Attempting to unpack array (count = %d, element size = %d, bytes " |
| + "remaining = %d) for type %s", |
| arraySize, elementSize, remaining, mClass)); |
| } |
| |
| array = Array.newInstance(mComponentClass, arraySize); |
| if (isUnwrappedPrimitiveClass(mComponentClass) && |
| mComponentClass == getPrimitiveTypeClass(mNativeType)) { |
| PrimitiveArrayFiller.getPrimitiveArrayFiller(mComponentClass).fillArray(array, |
| arraySize, buffer); |
| } else { |
| for (int i = 0; i < arraySize; ++i) { |
| Object elem = mComponentMarshaler.unmarshal(buffer); |
| Array.set(array, i, elem); |
| } |
| } |
| } else { |
| // Dynamic size, use an array list. |
| ArrayList<Object> arrayList = new ArrayList<Object>(); |
| |
| // Assumes array is packed tightly; no unused bytes allowed |
| while (buffer.hasRemaining()) { |
| Object elem = mComponentMarshaler.unmarshal(buffer); |
| arrayList.add(elem); |
| } |
| |
| int arraySize = arrayList.size(); |
| array = copyListToArray(arrayList, Array.newInstance(mComponentClass, arraySize)); |
| } |
| |
| if (buffer.remaining() != 0) { |
| Log.e(TAG, "Trailing bytes (" + buffer.remaining() + ") left over after unpacking " |
| + mClass); |
| } |
| |
| return mClass.cast(array); |
| } |
| |
| @Override |
| public int getNativeSize() { |
| return NATIVE_SIZE_DYNAMIC; |
| } |
| |
| @Override |
| public int calculateMarshalSize(T value) { |
| int elementSize = mComponentMarshaler.getNativeSize(); |
| int arrayLength = Array.getLength(value); |
| |
| if (elementSize != Marshaler.NATIVE_SIZE_DYNAMIC) { |
| // The fast way. Every element size is uniform. |
| return elementSize * arrayLength; |
| } else { |
| // The slow way. Accumulate size for each element. |
| int size = 0; |
| for (int i = 0; i < arrayLength; ++i) { |
| size += calculateElementMarshalSize(mComponentMarshaler, value, i); |
| } |
| |
| return size; |
| } |
| } |
| |
| /* |
| * Helpers to avoid compiler errors regarding types with wildcards (?) |
| */ |
| |
| @SuppressWarnings("unchecked") |
| private <TElem> void marshalArrayElement(Marshaler<TElem> marshaler, |
| ByteBuffer buffer, Object array, int index) { |
| marshaler.marshal((TElem)Array.get(array, index), buffer); |
| } |
| |
| @SuppressWarnings("unchecked") |
| private Object copyListToArray(ArrayList<?> arrayList, Object arrayDest) { |
| return arrayList.toArray((T[]) arrayDest); |
| } |
| |
| @SuppressWarnings("unchecked") |
| private <TElem> int calculateElementMarshalSize(Marshaler<TElem> marshaler, |
| Object array, int index) { |
| Object elem = Array.get(array, index); |
| |
| return marshaler.calculateMarshalSize((TElem) elem); |
| } |
| } |
| |
| @Override |
| public Marshaler<T> createMarshaler(TypeReference<T> managedType, int nativeType) { |
| return new MarshalerArray(managedType, nativeType); |
| } |
| |
| @Override |
| public boolean isTypeMappingSupported(TypeReference<T> managedType, int nativeType) { |
| // support both ConcreteType[] and GenericType<ConcreteType>[] |
| return managedType.getRawType().isArray(); |
| |
| // TODO: Should this recurse deeper and check that there is |
| // a valid marshaler for the ConcreteType as well? |
| } |
| } |