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.util.ArrayList;
30 
31 /**
32  * Marshal any array {@code T}.
33  *
34  * <p>To marshal any {@code T} to/from a native type, the marshaler for T to/from that native type
35  * also has to exist.</p>
36  *
37  * <p>{@code T} can be either a T2[] where T2 is an object type, or a P[] where P is a
38  * built-in primitive (e.g. int[], float[], etc).</p>
39 
40  * @param <T> the type of the array (e.g. T = int[], or T = Rational[])
41  */
42 public class MarshalQueryableArray<T> implements MarshalQueryable<T> {
43 
44     private static final String TAG = MarshalQueryableArray.class.getSimpleName();
45     private static final boolean DEBUG = false;
46 
47     private static interface PrimitiveArrayFiller {
fillPosition(Object arr, int index, ByteBuffer buffer)48         public void fillPosition(Object arr, int index, ByteBuffer buffer);
getPrimitiveArrayFiller(Class<?> componentType)49         static PrimitiveArrayFiller getPrimitiveArrayFiller(Class<?> componentType) {
50             if (componentType == int.class) {
51                 return new PrimitiveArrayFiller() {
52                       @Override
53                       public void fillPosition(Object arr, int index, ByteBuffer buffer) {
54                           int i = buffer.getInt();
55                           Array.setInt(arr, index, i);
56                       }
57                 };
58             } else if (componentType == float.class) {
59                 return new PrimitiveArrayFiller() {
60                       @Override
61                       public void fillPosition(Object arr, int index, ByteBuffer buffer) {
62                           float i = buffer.getFloat();
63                           Array.setFloat(arr, index, i);
64                       }
65                 };
66             } else if (componentType == long.class) {
67                 return new PrimitiveArrayFiller() {
68                       @Override
69                       public void fillPosition(Object arr, int index, ByteBuffer buffer) {
70                           long i = buffer.getLong();
71                           Array.setLong(arr, index, i);
72                       }
73                 };
74             } else if (componentType == double.class) {
75                 return new PrimitiveArrayFiller() {
76                       @Override
77                       public void fillPosition(Object arr, int index, ByteBuffer buffer) {
78                           double i = buffer.getDouble();
79                           Array.setDouble(arr, index, i);
80                       }
81                 };
82             } else if (componentType == byte.class) {
83                 return new PrimitiveArrayFiller() {
84                       @Override
85                       public void fillPosition(Object arr, int index, ByteBuffer buffer) {
86                           byte i = buffer.get();
87                           Array.setByte(arr, index, i);
88                       }
89                 };
90             }
91             throw new UnsupportedOperationException("PrimitiveArrayFiller of type "
92                     + componentType.getName() + " not supported");
93         }
94     };
95 
96     static void unmarshalPrimitiveArray(Object arr, int size, ByteBuffer buffer,
97             PrimitiveArrayFiller filler) {
98         for (int i = 0; i < size; i++) {
99             filler.fillPosition(arr, i, buffer);
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                     unmarshalPrimitiveArray(array, arraySize, buffer,
154                             PrimitiveArrayFiller.getPrimitiveArrayFiller(mComponentClass));
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