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
17package com.android.mediaframeworktest.unit;
18
19import android.util.Log;
20
21import java.lang.reflect.Array;
22import java.nio.ByteBuffer;
23import java.nio.ByteOrder;
24import java.util.Arrays;
25
26public class ByteArrayHelpers {
27
28    private static final String TAG = "ByteArrayHelpers";
29    private static boolean VERBOSE = false;
30
31    /**
32     * Convert an array of byte primitives to a {@code byte[]} using native endian order.
33     *
34     * <p>This function is a pass-through; it's here only to provide overloads for
35     * every single type of primitive arrays.
36     *
37     * @param array array of primitives
38     * @return array
39     */
40    public static byte[] toByteArray(byte[] array) {
41        return array;
42    }
43
44    /**
45     * Convert an array of shorts to a {@code byte[]} using native endian order.
46     *
47     * @param array array of shorts
48     * @return array converted into byte array using native endian order
49     */
50    public static byte[] toByteArray(short[] array) {
51        return toByteArray(array, Short.SIZE);
52    }
53
54    /**
55     * Convert an array of chars to a {@code byte[]} using native endian order.
56     *
57     * @param array array of chars
58     * @return array converted into byte array using native endian order
59     */
60    public static byte[] toByteArray(char[] array) {
61        return toByteArray(array, Character.SIZE);
62    }
63    /**
64     * Convert an array of ints to a {@code byte[]} using native endian order.
65     *
66     * @param array array of ints
67     * @return array converted into byte array using native endian order
68     */
69    public static byte[] toByteArray(int[] array) {
70        return toByteArray(array, Integer.SIZE);
71    }
72    /**
73     * Convert an array of longs to a {@code byte[]} using native endian order.
74     *
75     * @param array array of longs
76     * @return array converted into byte array using native endian order
77     */
78    public static byte[] toByteArray(long[] array) {
79        return toByteArray(array, Long.SIZE);
80    }
81    /**
82     * Convert an array of floats to a {@code byte[]} using native endian order.
83     *
84     * @param array array of floats
85     * @return array converted into byte array using native endian order
86     */
87    public static byte[] toByteArray(float[] array) {
88        return toByteArray(array, Float.SIZE);
89    }
90    /**
91     * Convert an array of doubles to a {@code byte[]} using native endian order.
92     *
93     * @param array array of doubles
94     * @return array converted into byte array using native endian order
95     */
96    public static byte[] toByteArray(double[] array) {
97        return toByteArray(array, Double.SIZE);
98    }
99
100    /**
101     * Convert an array of primitives to a {@code byte[]} using native endian order.
102     *
103     * <p>Arguments other than arrays are not supported. The array component must be primitive,
104     * the wrapper class is not allowed (e.g. {@code int[]} is ok, but {@code Integer[]} is not.</p>
105     *
106     * @param array array of primitives
107     * @return array converted into byte array using native endian order
108     *
109     * @throws IllegalArgumentException if {@code array} was not an array of primitives
110     */
111    public static <T> byte[] toByteArray(T array) {
112        @SuppressWarnings("unchecked")
113        Class<T> klass = (Class<T>) array.getClass();
114
115        if (!klass.isArray()) {
116            throw new IllegalArgumentException("array class must be an array");
117        }
118
119        Class<?> componentClass = klass.getComponentType();
120
121        if (!componentClass.isPrimitive()) {
122            throw new IllegalArgumentException("array's component must be a primitive");
123        }
124
125        int sizeInBits;
126        if (klass == int.class) {
127            sizeInBits = Integer.SIZE;
128        } else if (klass == float.class) {
129            sizeInBits = Float.SIZE;
130        } else if (klass == double.class) {
131            sizeInBits = Double.SIZE;
132        } else if (klass == short.class) {
133            sizeInBits = Short.SIZE;
134        } else if (klass == char.class) {
135            sizeInBits = Character.SIZE;
136        } else if (klass == long.class) {
137            sizeInBits = Long.SIZE;
138        } else if (klass == byte.class) {
139            sizeInBits = Byte.SIZE;
140        } else {
141            throw new AssertionError();
142        }
143
144        return toByteArray(array, sizeInBits);
145    }
146
147    /**
148     * Convert a variadic list of {@code Number}s into a byte array using native endian order.
149     *
150     * <p>Each {@link Number} must be an instance of a primitive wrapper class
151     * (e.g. {@link Integer} is OK, since it wraps {@code int}, but {@code BigInteger} is not.</p>
152     *
153     * @param numbers variadic list of numeric values
154     * @return array converted into byte array using native endian order
155     *
156     * @throws IllegalArgumentException
157     *          if {@code numbers} contained a class that wasn't a primitive wrapper
158     */
159    public static byte[] toByteArray(Number... numbers) {
160        if (numbers.length == 0) {
161            throw new IllegalArgumentException("too few numbers");
162        }
163
164        if (VERBOSE) Log.v(TAG, "toByteArray - input: " + Arrays.toString(numbers));
165
166        // Have a large enough capacity to fit in every number as a double
167        ByteBuffer byteBuffer = ByteBuffer.allocate(numbers.length * (Double.SIZE / Byte.SIZE))
168                .order(ByteOrder.nativeOrder());
169
170        for (int i = 0; i < numbers.length; ++i) {
171            Number value = numbers[i];
172            Class<? extends Number> klass = value.getClass();
173
174            if (VERBOSE) Log.v(TAG, "toByteArray - number " + i + ", class " + klass);
175
176            if (klass == Integer.class) {
177                byteBuffer.putInt((Integer)value);
178            } else if (klass == Float.class) {
179                byteBuffer.putFloat((Float)value);
180            } else if (klass == Double.class) {
181                byteBuffer.putDouble((Double)value);
182            } else if (klass == Short.class) {
183                byteBuffer.putShort((Short)value);
184            } else if (klass == Long.class) {
185                byteBuffer.putLong((Long)value);
186            } else if (klass == Byte.class) {
187                byteBuffer.put((Byte)value);
188            } else {
189                throw new IllegalArgumentException(
190                        "number class invalid; must be wrapper around primitive class");
191            }
192        }
193
194        if (VERBOSE) Log.v(TAG, "toByteArray - end of loop");
195
196        // Each number written is at least 1 byte, so the position should be at least length
197        if (numbers.length != 0 && byteBuffer.position() < numbers.length) {
198            throw new AssertionError(String.format(
199                    "Had %d numbers, but byte buffer position was only %d",
200                    numbers.length, byteBuffer.position()));
201        }
202
203        byteBuffer.flip();
204
205        byte[] bytes = new byte[byteBuffer.limit()];
206        byteBuffer.get(bytes);
207
208        if (VERBOSE) Log.v(TAG, "toByteArray - output: " + Arrays.toString(bytes));
209
210        return bytes;
211    }
212
213    private static <T> byte[] toByteArray(T array, int sizeOfTBits) {
214        @SuppressWarnings("unchecked")
215        Class<T> klass = (Class<T>) array.getClass();
216
217        if (!klass.isArray()) {
218            throw new IllegalArgumentException("array class must be an array");
219        }
220
221        int sizeOfT = sizeOfTBits / Byte.SIZE;
222        int byteLength = Array.getLength(array) * sizeOfT;
223
224        if (klass == byte[].class) {
225            // Always return a copy
226            return Arrays.copyOf((byte[])array, byteLength);
227        }
228
229        ByteBuffer byteBuffer = ByteBuffer.allocate(byteLength).order(ByteOrder.nativeOrder());
230
231        if (klass == int[].class) {
232            byteBuffer.asIntBuffer().put((int[])array);
233        } else if (klass == float[].class) {
234            byteBuffer.asFloatBuffer().put((float[])array);
235        } else if (klass == double[].class) {
236            byteBuffer.asDoubleBuffer().put((double[])array);
237        } else if (klass == short[].class) {
238            byteBuffer.asShortBuffer().put((short[])array);
239        } else if (klass == char[].class) {
240            byteBuffer.asCharBuffer().put((char[])array);
241        } else if (klass == long[].class) {
242            byteBuffer.asLongBuffer().put((long[])array);
243        } else {
244            throw new IllegalArgumentException("array class invalid; must be a primitive array");
245        }
246
247        return byteBuffer.array();
248    }
249
250    private ByteArrayHelpers() {
251        throw new AssertionError();
252    }
253}
254