1/*
2 * Copyright (C) 2015 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 android.support.v7.content.res;
18
19import java.lang.reflect.Array;
20
21/**
22 * A helper class that aims to provide comparable growth performance to ArrayList, but on primitive
23 * arrays. Common array operations are implemented for efficient use in dynamic containers.
24 *
25 * All methods in this class assume that the length of an array is equivalent to its capacity and
26 * NOT the number of elements in the array. The current size of the array is always passed in as a
27 * parameter.
28 */
29final class GrowingArrayUtils {
30
31    /**
32     * Appends an element to the end of the array, growing the array if there is no more room.
33     * @param array The array to which to append the element. This must NOT be null.
34     * @param currentSize The number of elements in the array. Must be less than or equal to
35     *                    array.length.
36     * @param element The element to append.
37     * @return the array to which the element was appended. This may be different than the given
38     *         array.
39     */
40    public static <T> T[] append(T[] array, int currentSize, T element) {
41        assert currentSize <= array.length;
42
43        if (currentSize + 1 > array.length) {
44            T[] newArray = (T[]) Array.newInstance(array.getClass().getComponentType(),
45                    growSize(currentSize));
46            System.arraycopy(array, 0, newArray, 0, currentSize);
47            array = newArray;
48        }
49        array[currentSize] = element;
50        return array;
51    }
52
53    /**
54     * Primitive int version of {@link #append(Object[], int, Object)}.
55     */
56    public static int[] append(int[] array, int currentSize, int element) {
57        assert currentSize <= array.length;
58
59        if (currentSize + 1 > array.length) {
60            int[] newArray = new int[growSize(currentSize)];
61            System.arraycopy(array, 0, newArray, 0, currentSize);
62            array = newArray;
63        }
64        array[currentSize] = element;
65        return array;
66    }
67
68    /**
69     * Primitive long version of {@link #append(Object[], int, Object)}.
70     */
71    public static long[] append(long[] array, int currentSize, long element) {
72        assert currentSize <= array.length;
73
74        if (currentSize + 1 > array.length) {
75            long[] newArray = new long[growSize(currentSize)];
76            System.arraycopy(array, 0, newArray, 0, currentSize);
77            array = newArray;
78        }
79        array[currentSize] = element;
80        return array;
81    }
82
83    /**
84     * Primitive boolean version of {@link #append(Object[], int, Object)}.
85     */
86    public static boolean[] append(boolean[] array, int currentSize, boolean element) {
87        assert currentSize <= array.length;
88
89        if (currentSize + 1 > array.length) {
90            boolean[] newArray = new boolean[growSize(currentSize)];
91            System.arraycopy(array, 0, newArray, 0, currentSize);
92            array = newArray;
93        }
94        array[currentSize] = element;
95        return array;
96    }
97
98    /**
99     * Inserts an element into the array at the specified index, growing the array if there is no
100     * more room.
101     *
102     * @param array The array to which to append the element. Must NOT be null.
103     * @param currentSize The number of elements in the array. Must be less than or equal to
104     *                    array.length.
105     * @param element The element to insert.
106     * @return the array to which the element was appended. This may be different than the given
107     *         array.
108     */
109    public static <T> T[] insert(T[] array, int currentSize, int index, T element) {
110        assert currentSize <= array.length;
111
112        if (currentSize + 1 <= array.length) {
113            System.arraycopy(array, index, array, index + 1, currentSize - index);
114            array[index] = element;
115            return array;
116        }
117
118        T[] newArray = (T[]) Array.newInstance(array.getClass().getComponentType(),
119                growSize(currentSize));
120        System.arraycopy(array, 0, newArray, 0, index);
121        newArray[index] = element;
122        System.arraycopy(array, index, newArray, index + 1, array.length - index);
123        return newArray;
124    }
125
126    /**
127     * Primitive int version of {@link #insert(Object[], int, int, Object)}.
128     */
129    public static int[] insert(int[] array, int currentSize, int index, int element) {
130        assert currentSize <= array.length;
131
132        if (currentSize + 1 <= array.length) {
133            System.arraycopy(array, index, array, index + 1, currentSize - index);
134            array[index] = element;
135            return array;
136        }
137
138        int[] newArray = new int[growSize(currentSize)];
139        System.arraycopy(array, 0, newArray, 0, index);
140        newArray[index] = element;
141        System.arraycopy(array, index, newArray, index + 1, array.length - index);
142        return newArray;
143    }
144
145    /**
146     * Primitive long version of {@link #insert(Object[], int, int, Object)}.
147     */
148    public static long[] insert(long[] array, int currentSize, int index, long element) {
149        assert currentSize <= array.length;
150
151        if (currentSize + 1 <= array.length) {
152            System.arraycopy(array, index, array, index + 1, currentSize - index);
153            array[index] = element;
154            return array;
155        }
156
157        long[] newArray = new long[growSize(currentSize)];
158        System.arraycopy(array, 0, newArray, 0, index);
159        newArray[index] = element;
160        System.arraycopy(array, index, newArray, index + 1, array.length - index);
161        return newArray;
162    }
163
164    /**
165     * Primitive boolean version of {@link #insert(Object[], int, int, Object)}.
166     */
167    public static boolean[] insert(boolean[] array, int currentSize, int index, boolean element) {
168        assert currentSize <= array.length;
169
170        if (currentSize + 1 <= array.length) {
171            System.arraycopy(array, index, array, index + 1, currentSize - index);
172            array[index] = element;
173            return array;
174        }
175
176        boolean[] newArray = new boolean[growSize(currentSize)];
177        System.arraycopy(array, 0, newArray, 0, index);
178        newArray[index] = element;
179        System.arraycopy(array, index, newArray, index + 1, array.length - index);
180        return newArray;
181    }
182
183    /**
184     * Given the current size of an array, returns an ideal size to which the array should grow.
185     * This is typically double the given size, but should not be relied upon to do so in the
186     * future.
187     */
188    public static int growSize(int currentSize) {
189        return currentSize <= 4 ? 8 : currentSize * 2;
190    }
191
192    // Uninstantiable
193    private GrowingArrayUtils() {}
194}