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 java.lang.reflect.Array;
25 import java.nio.ByteBuffer;
26 import java.util.ArrayList;
27 
28 /**
29  * Marshal any array {@code T}.
30  *
31  * <p>To marshal any {@code T} to/from a native type, the marshaler for T to/from that native type
32  * also has to exist.</p>
33  *
34  * <p>{@code T} can be either a T2[] where T2 is an object type, or a P[] where P is a
35  * built-in primitive (e.g. int[], float[], etc).</p>
36 
37  * @param <T> the type of the array (e.g. T = int[], or T = Rational[])
38  */
39 public class MarshalQueryableArray<T> implements MarshalQueryable<T> {
40 
41     private static final String TAG = MarshalQueryableArray.class.getSimpleName();
42     private static final boolean DEBUG = false;
43 
44     private class MarshalerArray extends Marshaler<T> {
45         private final Class<T> mClass;
46         private final Marshaler<?> mComponentMarshaler;
47         private final Class<?> mComponentClass;
48 
49         @SuppressWarnings("unchecked")
MarshalerArray(TypeReference<T> typeReference, int nativeType)50         protected MarshalerArray(TypeReference<T> typeReference, int nativeType) {
51             super(MarshalQueryableArray.this, typeReference, nativeType);
52 
53             mClass = (Class<T>)typeReference.getRawType();
54 
55             TypeReference<?> componentToken = typeReference.getComponentType();
56             mComponentMarshaler = MarshalRegistry.getMarshaler(componentToken, mNativeType);
57             mComponentClass = componentToken.getRawType();
58         }
59 
60         @Override
marshal(T value, ByteBuffer buffer)61         public void marshal(T value, ByteBuffer buffer) {
62             int length = Array.getLength(value);
63             for (int i = 0; i < length; ++i) {
64                 marshalArrayElement(mComponentMarshaler, buffer, value, i);
65             }
66         }
67 
68         @Override
unmarshal(ByteBuffer buffer)69         public T unmarshal(ByteBuffer buffer) {
70             Object array;
71 
72             int elementSize = mComponentMarshaler.getNativeSize();
73 
74             if (elementSize != Marshaler.NATIVE_SIZE_DYNAMIC) {
75                 int remaining = buffer.remaining();
76                 int arraySize = remaining / elementSize;
77 
78                 if (remaining % elementSize != 0) {
79                     throw new UnsupportedOperationException("Arrays for " + mTypeReference
80                             + " must be packed tighly into a multiple of " + elementSize
81                             + "; but there are " + (remaining % elementSize) + " left over bytes");
82                 }
83 
84                 if (DEBUG) {
85                     Log.v(TAG, String.format(
86                             "Attempting to unpack array (count = %d, element size = %d, bytes "
87                             + "remaining = %d) for type %s",
88                             arraySize, elementSize, remaining, mClass));
89                 }
90 
91                 array = Array.newInstance(mComponentClass, arraySize);
92                 for (int i = 0; i < arraySize; ++i) {
93                     Object elem = mComponentMarshaler.unmarshal(buffer);
94                     Array.set(array, i, elem);
95                 }
96             } else {
97                 // Dynamic size, use an array list.
98                 ArrayList<Object> arrayList = new ArrayList<Object>();
99 
100                 // Assumes array is packed tightly; no unused bytes allowed
101                 while (buffer.hasRemaining()) {
102                     Object elem = mComponentMarshaler.unmarshal(buffer);
103                     arrayList.add(elem);
104                 }
105 
106                 int arraySize = arrayList.size();
107                 array = copyListToArray(arrayList, Array.newInstance(mComponentClass, arraySize));
108             }
109 
110             if (buffer.remaining() != 0) {
111                 Log.e(TAG, "Trailing bytes (" + buffer.remaining() + ") left over after unpacking "
112                         + mClass);
113             }
114 
115             return mClass.cast(array);
116         }
117 
118         @Override
getNativeSize()119         public int getNativeSize() {
120             return NATIVE_SIZE_DYNAMIC;
121         }
122 
123         @Override
calculateMarshalSize(T value)124         public int calculateMarshalSize(T value) {
125             int elementSize = mComponentMarshaler.getNativeSize();
126             int arrayLength = Array.getLength(value);
127 
128             if (elementSize != Marshaler.NATIVE_SIZE_DYNAMIC) {
129                 // The fast way. Every element size is uniform.
130                 return elementSize * arrayLength;
131             } else {
132                 // The slow way. Accumulate size for each element.
133                 int size = 0;
134                 for (int i = 0; i < arrayLength; ++i) {
135                     size += calculateElementMarshalSize(mComponentMarshaler, value, i);
136                 }
137 
138                 return size;
139             }
140         }
141 
142         /*
143          * Helpers to avoid compiler errors regarding types with wildcards (?)
144          */
145 
146         @SuppressWarnings("unchecked")
marshalArrayElement(Marshaler<TElem> marshaler, ByteBuffer buffer, Object array, int index)147         private <TElem> void marshalArrayElement(Marshaler<TElem> marshaler,
148                 ByteBuffer buffer, Object array, int index) {
149             marshaler.marshal((TElem)Array.get(array, index), buffer);
150         }
151 
152         @SuppressWarnings("unchecked")
copyListToArray(ArrayList<?> arrayList, Object arrayDest)153         private Object copyListToArray(ArrayList<?> arrayList, Object arrayDest) {
154             return arrayList.toArray((T[]) arrayDest);
155         }
156 
157         @SuppressWarnings("unchecked")
calculateElementMarshalSize(Marshaler<TElem> marshaler, Object array, int index)158         private <TElem> int calculateElementMarshalSize(Marshaler<TElem> marshaler,
159                 Object array, int index) {
160             Object elem = Array.get(array, index);
161 
162             return marshaler.calculateMarshalSize((TElem) elem);
163         }
164     }
165 
166     @Override
createMarshaler(TypeReference<T> managedType, int nativeType)167     public Marshaler<T> createMarshaler(TypeReference<T> managedType, int nativeType) {
168         return new MarshalerArray(managedType, nativeType);
169     }
170 
171     @Override
isTypeMappingSupported(TypeReference<T> managedType, int nativeType)172     public boolean isTypeMappingSupported(TypeReference<T> managedType, int nativeType) {
173         // support both ConcreteType[] and GenericType<ConcreteType>[]
174         return managedType.getRawType().isArray();
175 
176         // TODO: Should this recurse deeper and check that there is
177         // a valid marshaler for the ConcreteType as well?
178     }
179 }
180