• 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 
17 package com.android.gallery3d.common;
18 
19 
20 // Copied from android.util.LongSparseArray for unbundling
21 
22 /**
23  * SparseArray mapping longs to Objects.  Unlike a normal array of Objects,
24  * there can be gaps in the indices.  It is intended to be more efficient
25  * than using a HashMap to map Longs to Objects.
26  */
27 public class LongSparseArray<E> implements Cloneable {
28     private static final Object DELETED = new Object();
29     private boolean mGarbage = false;
30 
31     private long[] mKeys;
32     private Object[] mValues;
33     private int mSize;
34 
35     /**
36      * Creates a new LongSparseArray containing no mappings.
37      */
LongSparseArray()38     public LongSparseArray() {
39         this(10);
40     }
41 
42     /**
43      * Creates a new LongSparseArray containing no mappings that will not
44      * require any additional memory allocation to store the specified
45      * number of mappings.
46      */
LongSparseArray(int initialCapacity)47     public LongSparseArray(int initialCapacity) {
48         initialCapacity = idealLongArraySize(initialCapacity);
49 
50         mKeys = new long[initialCapacity];
51         mValues = new Object[initialCapacity];
52         mSize = 0;
53     }
54 
55     @Override
56     @SuppressWarnings("unchecked")
clone()57     public LongSparseArray<E> clone() {
58         LongSparseArray<E> clone = null;
59         try {
60             clone = (LongSparseArray<E>) super.clone();
61             clone.mKeys = mKeys.clone();
62             clone.mValues = mValues.clone();
63         } catch (CloneNotSupportedException cnse) {
64             /* ignore */
65         }
66         return clone;
67     }
68 
69     /**
70      * Gets the Object mapped from the specified key, or <code>null</code>
71      * if no such mapping has been made.
72      */
get(long key)73     public E get(long key) {
74         return get(key, null);
75     }
76 
77     /**
78      * Gets the Object mapped from the specified key, or the specified Object
79      * if no such mapping has been made.
80      */
81     @SuppressWarnings("unchecked")
get(long key, E valueIfKeyNotFound)82     public E get(long key, E valueIfKeyNotFound) {
83         int i = binarySearch(mKeys, 0, mSize, key);
84 
85         if (i < 0 || mValues[i] == DELETED) {
86             return valueIfKeyNotFound;
87         } else {
88             return (E) mValues[i];
89         }
90     }
91 
92     /**
93      * Removes the mapping from the specified key, if there was any.
94      */
delete(long key)95     public void delete(long key) {
96         int i = binarySearch(mKeys, 0, mSize, key);
97 
98         if (i >= 0) {
99             if (mValues[i] != DELETED) {
100                 mValues[i] = DELETED;
101                 mGarbage = true;
102             }
103         }
104     }
105 
106     /**
107      * Alias for {@link #delete(long)}.
108      */
remove(long key)109     public void remove(long key) {
110         delete(key);
111     }
112 
113     /**
114      * Removes the mapping at the specified index.
115      */
removeAt(int index)116     public void removeAt(int index) {
117         if (mValues[index] != DELETED) {
118             mValues[index] = DELETED;
119             mGarbage = true;
120         }
121     }
122 
gc()123     private void gc() {
124         // Log.e("SparseArray", "gc start with " + mSize);
125 
126         int n = mSize;
127         int o = 0;
128         long[] keys = mKeys;
129         Object[] values = mValues;
130 
131         for (int i = 0; i < n; i++) {
132             Object val = values[i];
133 
134             if (val != DELETED) {
135                 if (i != o) {
136                     keys[o] = keys[i];
137                     values[o] = val;
138                     values[i] = null;
139                 }
140 
141                 o++;
142             }
143         }
144 
145         mGarbage = false;
146         mSize = o;
147 
148         // Log.e("SparseArray", "gc end with " + mSize);
149     }
150 
151     /**
152      * Adds a mapping from the specified key to the specified value,
153      * replacing the previous mapping from the specified key if there
154      * was one.
155      */
put(long key, E value)156     public void put(long key, E value) {
157         int i = binarySearch(mKeys, 0, mSize, key);
158 
159         if (i >= 0) {
160             mValues[i] = value;
161         } else {
162             i = ~i;
163 
164             if (i < mSize && mValues[i] == DELETED) {
165                 mKeys[i] = key;
166                 mValues[i] = value;
167                 return;
168             }
169 
170             if (mGarbage && mSize >= mKeys.length) {
171                 gc();
172 
173                 // Search again because indices may have changed.
174                 i = ~binarySearch(mKeys, 0, mSize, key);
175             }
176 
177             if (mSize >= mKeys.length) {
178                 int n = idealLongArraySize(mSize + 1);
179 
180                 long[] nkeys = new long[n];
181                 Object[] nvalues = new Object[n];
182 
183                 // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
184                 System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
185                 System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
186 
187                 mKeys = nkeys;
188                 mValues = nvalues;
189             }
190 
191             if (mSize - i != 0) {
192                 // Log.e("SparseArray", "move " + (mSize - i));
193                 System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
194                 System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
195             }
196 
197             mKeys[i] = key;
198             mValues[i] = value;
199             mSize++;
200         }
201     }
202 
203     /**
204      * Returns the number of key-value mappings that this LongSparseArray
205      * currently stores.
206      */
size()207     public int size() {
208         if (mGarbage) {
209             gc();
210         }
211 
212         return mSize;
213     }
214 
215     /**
216      * Given an index in the range <code>0...size()-1</code>, returns
217      * the key from the <code>index</code>th key-value mapping that this
218      * LongSparseArray stores.
219      */
keyAt(int index)220     public long keyAt(int index) {
221         if (mGarbage) {
222             gc();
223         }
224 
225         return mKeys[index];
226     }
227 
228     /**
229      * Given an index in the range <code>0...size()-1</code>, returns
230      * the value from the <code>index</code>th key-value mapping that this
231      * LongSparseArray stores.
232      */
233     @SuppressWarnings("unchecked")
valueAt(int index)234     public E valueAt(int index) {
235         if (mGarbage) {
236             gc();
237         }
238 
239         return (E) mValues[index];
240     }
241 
242     /**
243      * Given an index in the range <code>0...size()-1</code>, sets a new
244      * value for the <code>index</code>th key-value mapping that this
245      * LongSparseArray stores.
246      */
setValueAt(int index, E value)247     public void setValueAt(int index, E value) {
248         if (mGarbage) {
249             gc();
250         }
251 
252         mValues[index] = value;
253     }
254 
255     /**
256      * Returns the index for which {@link #keyAt} would return the
257      * specified key, or a negative number if the specified
258      * key is not mapped.
259      */
indexOfKey(long key)260     public int indexOfKey(long key) {
261         if (mGarbage) {
262             gc();
263         }
264 
265         return binarySearch(mKeys, 0, mSize, key);
266     }
267 
268     /**
269      * Returns an index for which {@link #valueAt} would return the
270      * specified key, or a negative number if no keys map to the
271      * specified value.
272      * Beware that this is a linear search, unlike lookups by key,
273      * and that multiple keys can map to the same value and this will
274      * find only one of them.
275      */
indexOfValue(E value)276     public int indexOfValue(E value) {
277         if (mGarbage) {
278             gc();
279         }
280 
281         for (int i = 0; i < mSize; i++)
282             if (mValues[i] == value)
283                 return i;
284 
285         return -1;
286     }
287 
288     /**
289      * Removes all key-value mappings from this LongSparseArray.
290      */
clear()291     public void clear() {
292         int n = mSize;
293         Object[] values = mValues;
294 
295         for (int i = 0; i < n; i++) {
296             values[i] = null;
297         }
298 
299         mSize = 0;
300         mGarbage = false;
301     }
302 
303     /**
304      * Puts a key/value pair into the array, optimizing for the case where
305      * the key is greater than all existing keys in the array.
306      */
append(long key, E value)307     public void append(long key, E value) {
308         if (mSize != 0 && key <= mKeys[mSize - 1]) {
309             put(key, value);
310             return;
311         }
312 
313         if (mGarbage && mSize >= mKeys.length) {
314             gc();
315         }
316 
317         int pos = mSize;
318         if (pos >= mKeys.length) {
319             int n = idealLongArraySize(pos + 1);
320 
321             long[] nkeys = new long[n];
322             Object[] nvalues = new Object[n];
323 
324             // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
325             System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
326             System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
327 
328             mKeys = nkeys;
329             mValues = nvalues;
330         }
331 
332         mKeys[pos] = key;
333         mValues[pos] = value;
334         mSize = pos + 1;
335     }
336 
binarySearch(long[] a, int start, int len, long key)337     private static int binarySearch(long[] a, int start, int len, long key) {
338         int high = start + len, low = start - 1, guess;
339 
340         while (high - low > 1) {
341             guess = (high + low) / 2;
342 
343             if (a[guess] < key)
344                 low = guess;
345             else
346                 high = guess;
347         }
348 
349         if (high == start + len)
350             return ~(start + len);
351         else if (a[high] == key)
352             return high;
353         else
354             return ~high;
355     }
356 
idealByteArraySize(int need)357     private static int idealByteArraySize(int need) {
358         for (int i = 4; i < 32; i++)
359             if (need <= (1 << i) - 12)
360                 return (1 << i) - 12;
361 
362         return need;
363     }
364 
idealLongArraySize(int need)365     public static int idealLongArraySize(int need) {
366         return idealByteArraySize(need * 8) / 8;
367     }
368 }
369