Home | History | Annotate | Download | only in media
      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      */
     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      */
     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      */
     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")
     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      */
     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      */
     96     public void remove(long key) {
     97         delete(key);
     98     }
     99 
    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      */
    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      */
    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      */
    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")
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    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")
    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 
    351         private ArrayUtils() { /* cannot be instantiated */
    352         }
    353 
    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 
    362         public static int idealBooleanArraySize(int need) {
    363             return idealByteArraySize(need);
    364         }
    365 
    366         public static int idealShortArraySize(int need) {
    367             return idealByteArraySize(need * 2) / 2;
    368         }
    369 
    370         public static int idealCharArraySize(int need) {
    371             return idealByteArraySize(need * 2) / 2;
    372         }
    373 
    374         public static int idealIntArraySize(int need) {
    375             return idealByteArraySize(need * 4) / 4;
    376         }
    377 
    378         public static int idealFloatArraySize(int need) {
    379             return idealByteArraySize(need * 4) / 4;
    380         }
    381 
    382         public static int idealObjectArraySize(int need) {
    383             return idealByteArraySize(need * 4) / 4;
    384         }
    385 
    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          */
    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")
    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          */
    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 }