1/*
2 * Copyright (C) 2017 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.text;
18
19import android.annotation.IntRange;
20import android.annotation.NonNull;
21
22import com.android.internal.util.ArrayUtils;
23
24import libcore.util.EmptyArray;
25
26/**
27 * Implements a growing array of int primitives.
28 *
29 * These arrays are NOT thread safe.
30 *
31 * @hide
32 */
33public final class AutoGrowArray {
34    private static final int MIN_CAPACITY_INCREMENT = 12;
35    private static final int MAX_CAPACITY_TO_BE_KEPT = 10000;
36
37    /**
38     * Returns next capacity size.
39     *
40     * The returned capacity is larger than requested capacity.
41     */
42    private static int computeNewCapacity(int currentSize, int requested) {
43        final int targetCapacity = currentSize + (currentSize < (MIN_CAPACITY_INCREMENT / 2)
44                ?  MIN_CAPACITY_INCREMENT : currentSize >> 1);
45        return targetCapacity > requested ? targetCapacity : requested;
46    }
47
48    /**
49     * An auto growing byte array.
50     */
51    public static class ByteArray {
52
53        private @NonNull byte[] mValues;
54        private @IntRange(from = 0) int mSize;
55
56        /**
57         * Creates an empty ByteArray with the default initial capacity.
58         */
59        public ByteArray() {
60            this(10);
61        }
62
63        /**
64         * Creates an empty ByteArray with the specified initial capacity.
65         */
66        public ByteArray(@IntRange(from = 0) int initialCapacity) {
67            if (initialCapacity == 0) {
68                mValues = EmptyArray.BYTE;
69            } else {
70                mValues = ArrayUtils.newUnpaddedByteArray(initialCapacity);
71            }
72            mSize = 0;
73        }
74
75        /**
76         * Changes the size of this ByteArray. If this ByteArray is shrinked, the backing array
77         * capacity is unchanged.
78         */
79        public void resize(@IntRange(from = 0) int newSize) {
80            if (newSize > mValues.length) {
81                ensureCapacity(newSize - mSize);
82            }
83            mSize = newSize;
84        }
85
86        /**
87         * Appends the specified value to the end of this array.
88         */
89        public void append(byte value) {
90            ensureCapacity(1);
91            mValues[mSize++] = value;
92        }
93
94        /**
95         * Ensures capacity to append at least <code>count</code> values.
96         */
97        private void ensureCapacity(@IntRange int count) {
98            final int requestedSize = mSize + count;
99            if (requestedSize >= mValues.length) {
100                final int newCapacity = computeNewCapacity(mSize, requestedSize);
101                final byte[] newValues = ArrayUtils.newUnpaddedByteArray(newCapacity);
102                System.arraycopy(mValues, 0, newValues, 0, mSize);
103                mValues = newValues;
104            }
105        }
106
107        /**
108         * Removes all values from this array.
109         */
110        public void clear() {
111            mSize = 0;
112        }
113
114        /**
115         * Removes all values from this array and release the internal array object if it is too
116         * large.
117         */
118        public void clearWithReleasingLargeArray() {
119            clear();
120            if (mValues.length > MAX_CAPACITY_TO_BE_KEPT) {
121                mValues = EmptyArray.BYTE;
122            }
123        }
124
125        /**
126         * Returns the value at the specified position in this array.
127         */
128        public byte get(@IntRange(from = 0) int index) {
129            return mValues[index];
130        }
131
132        /**
133         * Sets the value at the specified position in this array.
134         */
135        public void set(@IntRange(from = 0) int index, byte value) {
136            mValues[index] = value;
137        }
138
139        /**
140         * Returns the number of values in this array.
141         */
142        public @IntRange(from = 0) int size() {
143            return mSize;
144        }
145
146        /**
147         * Returns internal raw array.
148         *
149         * Note that this array may have larger size than you requested.
150         * Use size() instead for getting the actual array size.
151         */
152        public @NonNull byte[] getRawArray() {
153            return mValues;
154        }
155    }
156
157    /**
158     * An auto growing int array.
159     */
160    public static class IntArray {
161
162        private @NonNull int[] mValues;
163        private @IntRange(from = 0) int mSize;
164
165        /**
166         * Creates an empty IntArray with the default initial capacity.
167         */
168        public IntArray() {
169            this(10);
170        }
171
172        /**
173         * Creates an empty IntArray with the specified initial capacity.
174         */
175        public IntArray(@IntRange(from = 0) int initialCapacity) {
176            if (initialCapacity == 0) {
177                mValues = EmptyArray.INT;
178            } else {
179                mValues = ArrayUtils.newUnpaddedIntArray(initialCapacity);
180            }
181            mSize = 0;
182        }
183
184        /**
185         * Changes the size of this IntArray. If this IntArray is shrinked, the backing array
186         * capacity is unchanged.
187         */
188        public void resize(@IntRange(from = 0) int newSize) {
189            if (newSize > mValues.length) {
190                ensureCapacity(newSize - mSize);
191            }
192            mSize = newSize;
193        }
194
195        /**
196         * Appends the specified value to the end of this array.
197         */
198        public void append(int value) {
199            ensureCapacity(1);
200            mValues[mSize++] = value;
201        }
202
203        /**
204         * Ensures capacity to append at least <code>count</code> values.
205         */
206        private void ensureCapacity(@IntRange(from = 0) int count) {
207            final int requestedSize = mSize + count;
208            if (requestedSize >= mValues.length) {
209                final int newCapacity = computeNewCapacity(mSize, requestedSize);
210                final int[] newValues = ArrayUtils.newUnpaddedIntArray(newCapacity);
211                System.arraycopy(mValues, 0, newValues, 0, mSize);
212                mValues = newValues;
213            }
214        }
215
216        /**
217         * Removes all values from this array.
218         */
219        public void clear() {
220            mSize = 0;
221        }
222
223        /**
224         * Removes all values from this array and release the internal array object if it is too
225         * large.
226         */
227        public void clearWithReleasingLargeArray() {
228            clear();
229            if (mValues.length > MAX_CAPACITY_TO_BE_KEPT) {
230                mValues = EmptyArray.INT;
231            }
232        }
233
234        /**
235         * Returns the value at the specified position in this array.
236         */
237        public int get(@IntRange(from = 0) int index) {
238            return mValues[index];
239        }
240
241        /**
242         * Sets the value at the specified position in this array.
243         */
244        public void set(@IntRange(from = 0) int index, int value) {
245            mValues[index] = value;
246        }
247
248        /**
249         * Returns the number of values in this array.
250         */
251        public @IntRange(from = 0) int size() {
252            return mSize;
253        }
254
255        /**
256         * Returns internal raw array.
257         *
258         * Note that this array may have larger size than you requested.
259         * Use size() instead for getting the actual array size.
260         */
261        public @NonNull int[] getRawArray() {
262            return mValues;
263        }
264    }
265
266    /**
267     * An auto growing float array.
268     */
269    public static class FloatArray {
270
271        private @NonNull float[] mValues;
272        private @IntRange(from = 0) int mSize;
273
274        /**
275         * Creates an empty FloatArray with the default initial capacity.
276         */
277        public FloatArray() {
278            this(10);
279        }
280
281        /**
282         * Creates an empty FloatArray with the specified initial capacity.
283         */
284        public FloatArray(@IntRange(from = 0) int initialCapacity) {
285            if (initialCapacity == 0) {
286                mValues = EmptyArray.FLOAT;
287            } else {
288                mValues = ArrayUtils.newUnpaddedFloatArray(initialCapacity);
289            }
290            mSize = 0;
291        }
292
293        /**
294         * Changes the size of this FloatArray. If this FloatArray is shrinked, the backing array
295         * capacity is unchanged.
296         */
297        public void resize(@IntRange(from = 0) int newSize) {
298            if (newSize > mValues.length) {
299                ensureCapacity(newSize - mSize);
300            }
301            mSize = newSize;
302        }
303
304        /**
305         * Appends the specified value to the end of this array.
306         */
307        public void append(float value) {
308            ensureCapacity(1);
309            mValues[mSize++] = value;
310        }
311
312        /**
313         * Ensures capacity to append at least <code>count</code> values.
314         */
315        private void ensureCapacity(int count) {
316            final int requestedSize = mSize + count;
317            if (requestedSize >= mValues.length) {
318                final int newCapacity = computeNewCapacity(mSize, requestedSize);
319                final float[] newValues = ArrayUtils.newUnpaddedFloatArray(newCapacity);
320                System.arraycopy(mValues, 0, newValues, 0, mSize);
321                mValues = newValues;
322            }
323        }
324
325        /**
326         * Removes all values from this array.
327         */
328        public void clear() {
329            mSize = 0;
330        }
331
332        /**
333         * Removes all values from this array and release the internal array object if it is too
334         * large.
335         */
336        public void clearWithReleasingLargeArray() {
337            clear();
338            if (mValues.length > MAX_CAPACITY_TO_BE_KEPT) {
339                mValues = EmptyArray.FLOAT;
340            }
341        }
342
343        /**
344         * Returns the value at the specified position in this array.
345         */
346        public float get(@IntRange(from = 0) int index) {
347            return mValues[index];
348        }
349
350        /**
351         * Sets the value at the specified position in this array.
352         */
353        public void set(@IntRange(from = 0) int index, float value) {
354            mValues[index] = value;
355        }
356
357        /**
358         * Returns the number of values in this array.
359         */
360        public @IntRange(from = 0) int size() {
361            return mSize;
362        }
363
364        /**
365         * Returns internal raw array.
366         *
367         * Note that this array may have larger size than you requested.
368         * Use size() instead for getting the actual array size.
369         */
370        public @NonNull float[] getRawArray() {
371            return mValues;
372        }
373    }
374}
375