• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 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  * NOTE(aws): Adapted into the project from the repository since this is a non-public class.
17  */
18 
19 package com.cooliris.media;
20 
21 import java.lang.reflect.Array;
22 
23 import android.util.Log;
24 
25 /**
26  * SparseArrays map longs to Objects. Unlike a normal array of Objects, there
27  * can be gaps in the indices. It is intended to be more efficient than using a
28  * HashMap to map Longs to Objects.
29  *
30  * @hide
31  */
32 public final class LongSparseArray<E> {
33     private static final Object DELETED = new Object();
34     private boolean mGarbage = false;
35 
36     /**
37      * Creates a new SparseArray containing no mappings.
38      */
LongSparseArray()39     public LongSparseArray() {
40         this(10);
41     }
42 
43     /**
44      * Creates a new SparseArray containing no mappings that will not require
45      * any additional memory allocation to store the specified number of
46      * mappings.
47      */
LongSparseArray(int initialCapacity)48     public LongSparseArray(int initialCapacity) {
49         initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity);
50 
51         mKeys = new long[initialCapacity];
52         mValues = new Object[initialCapacity];
53         mSize = 0;
54     }
55 
56     /**
57      * Gets the Object mapped from the specified key, or <code>null</code> if no
58      * such mapping has been made.
59      */
get(long key)60     public E get(long key) {
61         return get(key, null);
62     }
63 
64     /**
65      * Gets the Object mapped from the specified key, or the specified Object if
66      * no such mapping has been made.
67      */
68     @SuppressWarnings("unchecked")
get(long key, E valueIfKeyNotFound)69     public E get(long key, E valueIfKeyNotFound) {
70         int i = binarySearch(mKeys, 0, mSize, key);
71 
72         if (i < 0 || mValues[i] == DELETED) {
73             return valueIfKeyNotFound;
74         } else {
75             return (E) mValues[i];
76         }
77     }
78 
79     /**
80      * Removes the mapping from the specified key, if there was any.
81      */
delete(long key)82     public void delete(long key) {
83         int i = binarySearch(mKeys, 0, mSize, key);
84 
85         if (i >= 0) {
86             if (mValues[i] != DELETED) {
87                 mValues[i] = DELETED;
88                 mGarbage = true;
89             }
90         }
91     }
92 
93     /**
94      * Alias for {@link #delete(long)}.
95      */
remove(long key)96     public void remove(long key) {
97         delete(key);
98     }
99 
gc()100     private void gc() {
101         // Log.e("SparseArray", "gc start with " + mSize);
102 
103         int n = mSize;
104         int o = 0;
105         long[] keys = mKeys;
106         Object[] values = mValues;
107 
108         for (int i = 0; i < n; i++) {
109             Object val = values[i];
110 
111             if (val != DELETED) {
112                 if (i != o) {
113                     keys[o] = keys[i];
114                     values[o] = val;
115                 }
116 
117                 o++;
118             }
119         }
120 
121         mGarbage = false;
122         mSize = o;
123 
124         // Log.e("SparseArray", "gc end with " + mSize);
125     }
126 
127     /**
128      * Adds a mapping from the specified key to the specified value, replacing
129      * the previous mapping from the specified key if there was one.
130      */
put(long key, E value)131     public void put(long key, E value) {
132         int i = binarySearch(mKeys, 0, mSize, key);
133 
134         if (i >= 0) {
135             mValues[i] = value;
136         } else {
137             i = ~i;
138 
139             if (i < mSize && mValues[i] == DELETED) {
140                 mKeys[i] = key;
141                 mValues[i] = value;
142                 return;
143             }
144 
145             if (mGarbage && mSize >= mKeys.length) {
146                 gc();
147 
148                 // Search again because indices may have changed.
149                 i = ~binarySearch(mKeys, 0, mSize, key);
150             }
151 
152             if (mSize >= mKeys.length) {
153                 int n = ArrayUtils.idealIntArraySize(mSize + 1);
154 
155                 long[] nkeys = new long[n];
156                 Object[] nvalues = new Object[n];
157 
158                 // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
159                 System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
160                 System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
161 
162                 mKeys = nkeys;
163                 mValues = nvalues;
164             }
165 
166             if (mSize - i != 0) {
167                 // Log.e("SparseArray", "move " + (mSize - i));
168                 System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
169                 System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
170             }
171 
172             mKeys[i] = key;
173             mValues[i] = value;
174             mSize++;
175         }
176     }
177 
178     /**
179      * Returns the number of key-value mappings that this SparseArray currently
180      * stores.
181      */
size()182     public int size() {
183         if (mGarbage) {
184             gc();
185         }
186 
187         return mSize;
188     }
189 
190     /**
191      * Given an index in the range <code>0...size()-1</code>, returns the key
192      * from the <code>index</code>th key-value mapping that this SparseArray
193      * stores.
194      */
keyAt(int index)195     public long keyAt(int index) {
196         if (mGarbage) {
197             gc();
198         }
199 
200         return mKeys[index];
201     }
202 
203     /**
204      * Given an index in the range <code>0...size()-1</code>, returns the value
205      * from the <code>index</code>th key-value mapping that this SparseArray
206      * stores.
207      */
208     @SuppressWarnings("unchecked")
valueAt(int index)209     public E valueAt(int index) {
210         if (mGarbage) {
211             gc();
212         }
213 
214         return (E) mValues[index];
215     }
216 
217     /**
218      * Given an index in the range <code>0...size()-1</code>, sets a new value
219      * for the <code>index</code>th key-value mapping that this SparseArray
220      * stores.
221      */
setValueAt(int index, E value)222     public void setValueAt(int index, E value) {
223         if (mGarbage) {
224             gc();
225         }
226 
227         mValues[index] = value;
228     }
229 
230     /**
231      * Returns the index for which {@link #keyAt} would return the specified
232      * key, or a negative number if the specified key is not mapped.
233      */
indexOfKey(long key)234     public int indexOfKey(long key) {
235         if (mGarbage) {
236             gc();
237         }
238 
239         return binarySearch(mKeys, 0, mSize, key);
240     }
241 
242     /**
243      * Returns an index for which {@link #valueAt} would return the specified
244      * key, or a negative number if no keys map to the specified value. Beware
245      * that this is a linear search, unlike lookups by key, and that multiple
246      * keys can map to the same value and this will find only one of them.
247      */
indexOfValue(E value)248     public int indexOfValue(E value) {
249         if (mGarbage) {
250             gc();
251         }
252 
253         for (int i = 0; i < mSize; i++)
254             if (mValues[i] == value)
255                 return i;
256 
257         return -1;
258     }
259 
260     /**
261      * Removes all key-value mappings from this SparseArray.
262      */
clear()263     public void clear() {
264         int n = mSize;
265         Object[] values = mValues;
266 
267         for (int i = 0; i < n; i++) {
268             values[i] = null;
269         }
270 
271         mSize = 0;
272         mGarbage = false;
273     }
274 
275     /**
276      * Puts a key/value pair into the array, optimizing for the case where the
277      * key is greater than all existing keys in the array.
278      */
append(long key, E value)279     public void append(long key, E value) {
280         if (mSize != 0 && key <= mKeys[mSize - 1]) {
281             put(key, value);
282             return;
283         }
284 
285         if (mGarbage && mSize >= mKeys.length) {
286             gc();
287         }
288 
289         int pos = mSize;
290         if (pos >= mKeys.length) {
291             int n = ArrayUtils.idealIntArraySize(pos + 1);
292 
293             long[] nkeys = new long[n];
294             Object[] nvalues = new Object[n];
295 
296             // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
297             System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
298             System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
299 
300             mKeys = nkeys;
301             mValues = nvalues;
302         }
303 
304         mKeys[pos] = key;
305         mValues[pos] = value;
306         mSize = pos + 1;
307     }
308 
binarySearch(long[] a, int start, int len, long key)309     private static int binarySearch(long[] a, int start, int len, long key) {
310         int high = start + len, low = start - 1, guess;
311 
312         while (high - low > 1) {
313             guess = (high + low) / 2;
314 
315             if (a[guess] < key)
316                 low = guess;
317             else
318                 high = guess;
319         }
320 
321         if (high == start + len)
322             return ~(start + len);
323         else if (a[high] == key)
324             return high;
325         else
326             return ~high;
327     }
328 
329     @SuppressWarnings("unused")
checkIntegrity()330     private void checkIntegrity() {
331         for (int i = 1; i < mSize; i++) {
332             if (mKeys[i] <= mKeys[i - 1]) {
333                 for (int j = 0; j < mSize; j++) {
334                     Log.e("FAIL", j + ": " + mKeys[j] + " -> " + mValues[j]);
335                 }
336 
337                 throw new RuntimeException();
338             }
339         }
340     }
341 
342     private long[] mKeys;
343     private Object[] mValues;
344     private int mSize;
345 
346     public static final class ArrayUtils {
347         private static Object[] EMPTY = new Object[0];
348         private static final int CACHE_SIZE = 73;
349         private static Object[] sCache = new Object[CACHE_SIZE];
350 
ArrayUtils()351         private ArrayUtils() { /* cannot be instantiated */
352         }
353 
idealByteArraySize(int need)354         public static int idealByteArraySize(int need) {
355             for (int i = 4; i < 32; i++)
356                 if (need <= (1 << i) - 12)
357                     return (1 << i) - 12;
358 
359             return need;
360         }
361 
idealBooleanArraySize(int need)362         public static int idealBooleanArraySize(int need) {
363             return idealByteArraySize(need);
364         }
365 
idealShortArraySize(int need)366         public static int idealShortArraySize(int need) {
367             return idealByteArraySize(need * 2) / 2;
368         }
369 
idealCharArraySize(int need)370         public static int idealCharArraySize(int need) {
371             return idealByteArraySize(need * 2) / 2;
372         }
373 
idealIntArraySize(int need)374         public static int idealIntArraySize(int need) {
375             return idealByteArraySize(need * 4) / 4;
376         }
377 
idealFloatArraySize(int need)378         public static int idealFloatArraySize(int need) {
379             return idealByteArraySize(need * 4) / 4;
380         }
381 
idealObjectArraySize(int need)382         public static int idealObjectArraySize(int need) {
383             return idealByteArraySize(need * 4) / 4;
384         }
385 
idealLongArraySize(int need)386         public static int idealLongArraySize(int need) {
387             return idealByteArraySize(need * 8) / 8;
388         }
389 
390         /**
391          * Checks if the beginnings of two byte arrays are equal.
392          *
393          * @param array1
394          *            the first byte array
395          * @param array2
396          *            the second byte array
397          * @param length
398          *            the number of bytes to check
399          * @return true if they're equal, false otherwise
400          */
equals(byte[] array1, byte[] array2, int length)401         public static boolean equals(byte[] array1, byte[] array2, int length) {
402             if (array1 == array2) {
403                 return true;
404             }
405             if (array1 == null || array2 == null || array1.length < length || array2.length < length) {
406                 return false;
407             }
408             for (int i = 0; i < length; i++) {
409                 if (array1[i] != array2[i]) {
410                     return false;
411                 }
412             }
413             return true;
414         }
415 
416         /**
417          * Returns an empty array of the specified type. The intent is that it
418          * will return the same empty array every time to avoid reallocation,
419          * although this is not guaranteed.
420          */
421         @SuppressWarnings("unchecked")
emptyArray(Class<T> kind)422         public static <T> T[] emptyArray(Class<T> kind) {
423             if (kind == Object.class) {
424                 return (T[]) EMPTY;
425             }
426 
427             int bucket = ((System.identityHashCode(kind) / 8) & 0x7FFFFFFF) % CACHE_SIZE;
428             Object cache = sCache[bucket];
429 
430             if (cache == null || cache.getClass().getComponentType() != kind) {
431                 cache = Array.newInstance(kind, 0);
432                 sCache[bucket] = cache;
433 
434                 // Log.e("cache", "new empty " + kind.getName() + " at " +
435                 // bucket);
436             }
437 
438             return (T[]) cache;
439         }
440 
441         /**
442          * Checks that value is present as at least one of the elements of the
443          * array.
444          *
445          * @param array
446          *            the array to check in
447          * @param value
448          *            the value to check for
449          * @return true if the value is present in the array
450          */
contains(T[] array, T value)451         public static <T> boolean contains(T[] array, T value) {
452             for (T element : array) {
453                 if (element == null) {
454                     if (value == null)
455                         return true;
456                 } else {
457                     if (value != null && element.equals(value))
458                         return true;
459                 }
460             }
461             return false;
462         }
463     }
464 }