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