• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2013, Google LLC
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google LLC nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 package com.android.tools.smali.util;
32 
33 import java.util.Arrays;
34 import java.util.Collections;
35 import java.util.List;
36 
37 /**
38  * SparseArrays map integers to Objects.  Unlike a normal array of Objects,
39  * there can be gaps in the indices.  It is intended to be more efficient
40  * than using a HashMap to map Integers to Objects.
41  */
42 public class SparseArray<E> {
43     private static final Object DELETED = new Object();
44     private boolean mGarbage = false;
45 
46     /**
47      * Creates a new SparseArray containing no mappings.
48      */
SparseArray()49     public SparseArray() {
50         this(10);
51     }
52 
53     /**
54      * Creates a new SparseArray containing no mappings that will not
55      * require any additional memory allocation to store the specified
56      * number of mappings.
57      */
SparseArray(int initialCapacity)58     public SparseArray(int initialCapacity) {
59         mKeys = new int[initialCapacity];
60         mValues = new Object[initialCapacity];
61         mSize = 0;
62     }
63 
64     /**
65      * Gets the Object mapped from the specified key, or <code>null</code>
66      * if no such mapping has been made.
67      */
get(int key)68     public E get(int key) {
69         return get(key, null);
70     }
71 
72     /**
73      * Gets the Object mapped from the specified key, or the specified Object
74      * if no such mapping has been made.
75      */
get(int key, E valueIfKeyNotFound)76     public E get(int key, E valueIfKeyNotFound) {
77         int i = binarySearch(mKeys, 0, mSize, key);
78 
79         if (i < 0 || mValues[i] == DELETED) {
80             return valueIfKeyNotFound;
81         } else {
82             return (E) mValues[i];
83         }
84     }
85 
86     /**
87      * Removes the mapping from the specified key, if there was any.
88      */
delete(int key)89     public void delete(int key) {
90         int i = binarySearch(mKeys, 0, mSize, key);
91 
92         if (i >= 0) {
93             if (mValues[i] != DELETED) {
94                 mValues[i] = DELETED;
95                 mGarbage = true;
96             }
97         }
98     }
99 
100     /**
101      * Alias for {@link #delete(int)}.
102      */
remove(int key)103     public void remove(int key) {
104         delete(key);
105     }
106 
gc()107     private void gc() {
108         // Log.e("SparseArray", "gc start with " + mSize);
109 
110         int n = mSize;
111         int o = 0;
112         int[] keys = mKeys;
113         Object[] values = mValues;
114 
115         for (int i = 0; i < n; i++) {
116             Object val = values[i];
117 
118             if (val != DELETED) {
119                 if (i != o) {
120                     keys[o] = keys[i];
121                     values[o] = val;
122                 }
123 
124                 o++;
125             }
126         }
127 
128         mGarbage = false;
129         mSize = o;
130 
131         // Log.e("SparseArray", "gc end with " + mSize);
132     }
133 
134     /**
135      * Adds a mapping from the specified key to the specified value,
136      * replacing the previous mapping from the specified key if there
137      * was one.
138      */
put(int key, E value)139     public void put(int key, E value) {
140         int i = binarySearch(mKeys, 0, mSize, key);
141 
142         if (i >= 0) {
143             mValues[i] = value;
144         } else {
145             i = ~i;
146 
147             if (i < mSize && mValues[i] == DELETED) {
148                 mKeys[i] = key;
149                 mValues[i] = value;
150                 return;
151             }
152 
153             if (mGarbage && mSize >= mKeys.length) {
154                 gc();
155 
156                 // Search again because indices may have changed.
157                 i = ~binarySearch(mKeys, 0, mSize, key);
158             }
159 
160             if (mSize >= mKeys.length) {
161                 int n = Math.max(mSize + 1, mKeys.length * 2);
162 
163                 int[] nkeys = new int[n];
164                 Object[] nvalues = new Object[n];
165 
166                 // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
167                 System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
168                 System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
169 
170                 mKeys = nkeys;
171                 mValues = nvalues;
172             }
173 
174             if (mSize - i != 0) {
175                 // Log.e("SparseArray", "move " + (mSize - i));
176                 System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
177                 System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
178             }
179 
180             mKeys[i] = key;
181             mValues[i] = value;
182             mSize++;
183         }
184     }
185 
186     /**
187      * Returns the number of key-value mappings that this SparseArray
188      * currently stores.
189      */
size()190     public int size() {
191         if (mGarbage) {
192             gc();
193         }
194 
195         return mSize;
196     }
197 
198     /**
199      * Given an index in the range <code>0...size()-1</code>, returns
200      * the key from the <code>index</code>th key-value mapping that this
201      * SparseArray stores.
202      */
keyAt(int index)203     public int keyAt(int index) {
204         if (mGarbage) {
205             gc();
206         }
207 
208         return mKeys[index];
209     }
210 
211     /**
212      * Given an index in the range <code>0...size()-1</code>, returns
213      * the value from the <code>index</code>th key-value mapping that this
214      * SparseArray stores.
215      */
valueAt(int index)216     public E valueAt(int index) {
217         if (mGarbage) {
218             gc();
219         }
220 
221         return (E) mValues[index];
222     }
223 
224     /**
225      * Given an index in the range <code>0...size()-1</code>, sets a new
226      * value for the <code>index</code>th key-value mapping that this
227      * SparseArray stores.
228      */
setValueAt(int index, E value)229     public void setValueAt(int index, E value) {
230         if (mGarbage) {
231             gc();
232         }
233 
234         mValues[index] = value;
235     }
236 
237     /**
238      * Returns the index for which {@link #keyAt} would return the
239      * specified key, or a negative number if the specified
240      * key is not mapped.
241      */
indexOfKey(int key)242     public int indexOfKey(int key) {
243         if (mGarbage) {
244             gc();
245         }
246 
247         return binarySearch(mKeys, 0, mSize, key);
248     }
249 
250     /**
251      * Returns an index for which {@link #valueAt} would return the
252      * specified key, or a negative number if no keys map to the
253      * specified value.
254      * Beware that this is a linear search, unlike lookups by key,
255      * and that multiple keys can map to the same value and this will
256      * find only one of them.
257      */
indexOfValue(E value)258     public int indexOfValue(E value) {
259         if (mGarbage) {
260             gc();
261         }
262 
263         for (int i = 0; i < mSize; i++)
264             if (mValues[i] == value)
265                 return i;
266 
267         return -1;
268     }
269 
270     /**
271      * Removes all key-value mappings from this SparseArray.
272      */
clear()273     public void clear() {
274         int n = mSize;
275         Object[] values = mValues;
276 
277         for (int i = 0; i < n; i++) {
278             values[i] = null;
279         }
280 
281         mSize = 0;
282         mGarbage = false;
283     }
284 
285     /**
286      * Puts a key/value pair into the array, optimizing for the case where
287      * the key is greater than all existing keys in the array.
288      */
append(int key, E value)289     public void append(int key, E value) {
290         if (mSize != 0 && key <= mKeys[mSize - 1]) {
291             put(key, value);
292             return;
293         }
294 
295         if (mGarbage && mSize >= mKeys.length) {
296             gc();
297         }
298 
299         int pos = mSize;
300         if (pos >= mKeys.length) {
301             int n = Math.max(pos + 1, mKeys.length * 2);
302 
303             int[] nkeys = new int[n];
304             Object[] nvalues = new Object[n];
305 
306             // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
307             System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
308             System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
309 
310             mKeys = nkeys;
311             mValues = nvalues;
312         }
313 
314         mKeys[pos] = key;
315         mValues[pos] = value;
316         mSize = pos + 1;
317     }
318 
319     /**
320      * Increases the size of the underlying storage if needed, to ensure that it can
321      * hold the specified number of items without having to allocate additional memory
322      * @param capacity the number of items
323      */
ensureCapacity(int capacity)324     public void ensureCapacity(int capacity) {
325         if (mGarbage && mSize >= mKeys.length) {
326             gc();
327         }
328 
329         if (mKeys.length < capacity) {
330             int[] nkeys = new int[capacity];
331             Object[] nvalues = new Object[capacity];
332 
333             System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
334             System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
335 
336             mKeys = nkeys;
337             mValues = nvalues;
338         }
339     }
340 
binarySearch(int[] a, int start, int len, int key)341     private static int binarySearch(int[] a, int start, int len, int key) {
342         int high = start + len, low = start - 1, guess;
343 
344         while (high - low > 1) {
345             guess = (high + low) / 2;
346 
347             if (a[guess] < key)
348                 low = guess;
349             else
350                 high = guess;
351         }
352 
353         if (high == start + len)
354             return ~(start + len);
355         else if (a[high] == key)
356             return high;
357         else
358             return ~high;
359     }
360 
361     /**
362      * @return a read-only list of the values in this SparseArray which are in ascending order, based on their
363      * associated key
364      */
getValues()365     public List<E> getValues() {
366         return Collections.unmodifiableList(Arrays.asList((E[])mValues));
367     }
368 
369     private int[] mKeys;
370     private Object[] mValues;
371     private int mSize;
372 }
373