SparseArrayData.java revision 1450:68a026de1201
1/*
2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package jdk.nashorn.internal.runtime.arrays;
27
28import java.util.Arrays;
29import java.util.Map;
30import java.util.TreeMap;
31import jdk.nashorn.internal.codegen.types.Type;
32import jdk.nashorn.internal.runtime.JSType;
33import jdk.nashorn.internal.runtime.ScriptRuntime;
34
35/**
36 * Handle arrays where the index is very large.
37 */
38class SparseArrayData extends ArrayData {
39    static final int MAX_DENSE_LENGTH = 1024 * 1024;
40
41    /** Underlying array. */
42    private ArrayData underlying;
43
44    /** Maximum length to be stored in the array. */
45    private final long maxDenseLength;
46
47    /** Sparse elements. */
48    private TreeMap<Long, Object> sparseMap;
49
50    SparseArrayData(final ArrayData underlying, final long length) {
51        this(underlying, length, new TreeMap<>());
52    }
53
54    SparseArrayData(final ArrayData underlying, final long length, final TreeMap<Long, Object> sparseMap) {
55        super(length);
56        assert underlying.length() <= length;
57        this.underlying = underlying;
58        this.maxDenseLength = Math.max(MAX_DENSE_LENGTH, underlying.length());
59        this.sparseMap = sparseMap;
60    }
61
62    @Override
63    public ArrayData copy() {
64        return new SparseArrayData(underlying.copy(), length(), new TreeMap<>(sparseMap));
65    }
66
67    @Override
68    public Object[] asObjectArray() {
69        final int len = (int)Math.min(length(), Integer.MAX_VALUE);
70        final int underlyingLength = (int)Math.min(len, underlying.length());
71        final Object[] objArray = new Object[len];
72
73        for (int i = 0; i < underlyingLength; i++) {
74            objArray[i] = underlying.getObject(i);
75        }
76
77        Arrays.fill(objArray, underlyingLength, len, ScriptRuntime.UNDEFINED);
78
79        for (final Map.Entry<Long, Object> entry : sparseMap.entrySet()) {
80            final long key = entry.getKey();
81            if (key < Integer.MAX_VALUE) {
82                objArray[(int)key] = entry.getValue();
83            } else {
84                break; // ascending key order
85            }
86        }
87
88        return objArray;
89    }
90
91    @Override
92    public void shiftLeft(final int by) {
93        underlying.shiftLeft(by);
94
95        final TreeMap<Long, Object> newSparseMap = new TreeMap<>();
96
97        for (final Map.Entry<Long, Object> entry : sparseMap.entrySet()) {
98            final long newIndex = entry.getKey() - by;
99            if (newIndex < maxDenseLength) {
100                underlying = underlying.set((int) newIndex, entry.getValue(), false);
101            } else if (newIndex >= 0) {
102                newSparseMap.put(newIndex, entry.getValue());
103            }
104        }
105
106        sparseMap = newSparseMap;
107        setLength(Math.max(length() - by, 0));
108    }
109
110    @Override
111    public ArrayData shiftRight(final int by) {
112        final TreeMap<Long, Object> newSparseMap = new TreeMap<>();
113        final long len = underlying.length();
114        if (len + by > maxDenseLength) {
115            for (long i = maxDenseLength - by; i < len; i++) {
116                if (underlying.has((int) i)) {
117                    newSparseMap.put(i + by, underlying.getObject((int) i));
118                }
119            }
120            underlying = underlying.shrink((int) (maxDenseLength - by));
121        }
122
123        underlying.shiftRight(by);
124
125        for (final Map.Entry<Long, Object> entry : sparseMap.entrySet()) {
126            final long newIndex = entry.getKey() + by;
127            newSparseMap.put(newIndex, entry.getValue());
128        }
129
130        sparseMap = newSparseMap;
131        setLength(length() + by);
132
133        return this;
134    }
135
136    @Override
137    public ArrayData ensure(final long safeIndex) {
138        // Usually #ensure only needs to be called if safeIndex is greater or equal current length.
139        // SparseArrayData is an exception as an index smaller than our current length may still
140        // exceed the underlying ArrayData's capacity. Because of this, SparseArrayData invokes
141        // its ensure method internally in various places where other ArrayData subclasses don't,
142        // making it safe for outside uses to only call ensure(safeIndex) if safeIndex >= length.
143        if (safeIndex < maxDenseLength && underlying.length() <= safeIndex) {
144            underlying = underlying.ensure(safeIndex);
145        }
146        if (safeIndex >= length()) {
147            setLength(safeIndex + 1);
148        }
149        return this;
150    }
151
152    @Override
153    public ArrayData shrink(final long newLength) {
154        if (newLength < underlying.length()) {
155            underlying = underlying.shrink(newLength);
156            underlying.setLength(newLength);
157            sparseMap.clear();
158            setLength(newLength);
159        }
160
161        sparseMap.subMap(newLength, Long.MAX_VALUE).clear();
162        setLength(newLength);
163        return this;
164    }
165
166    @Override
167    public ArrayData set(final int index, final Object value, final boolean strict) {
168        if (index >= 0 && index < maxDenseLength) {
169            final long oldLength = underlying.length();
170            ensure(index);
171            underlying = underlying.set(index, value, strict).safeDelete(oldLength, index - 1, strict);
172            setLength(Math.max(underlying.length(), length()));
173        } else {
174            final Long longIndex = indexToKey(index);
175            sparseMap.put(longIndex, value);
176            setLength(Math.max(longIndex + 1, length()));
177        }
178
179        return this;
180    }
181
182    @Override
183    public ArrayData set(final int index, final int value, final boolean strict) {
184        if (index >= 0 && index < maxDenseLength) {
185            final long oldLength = underlying.length();
186            ensure(index);
187            underlying = underlying.set(index, value, strict).safeDelete(oldLength, index - 1, strict);
188            setLength(Math.max(underlying.length(), length()));
189        } else {
190            final Long longIndex = indexToKey(index);
191            sparseMap.put(longIndex, value);
192            setLength(Math.max(longIndex + 1, length()));
193        }
194        return this;
195    }
196
197    @Override
198    public ArrayData set(final int index, final long value, final boolean strict) {
199        if (index >= 0 && index < maxDenseLength) {
200            final long oldLength = underlying.length();
201            ensure(index);
202            underlying = underlying.set(index, value, strict).safeDelete(oldLength, index - 1, strict);
203            setLength(Math.max(underlying.length(), length()));
204        } else {
205            final Long longIndex = indexToKey(index);
206            sparseMap.put(longIndex, value);
207            setLength(Math.max(longIndex + 1, length()));
208        }
209        return this;
210    }
211
212    @Override
213    public ArrayData set(final int index, final double value, final boolean strict) {
214        if (index >= 0 && index < maxDenseLength) {
215            final long oldLength = underlying.length();
216            ensure(index);
217            underlying = underlying.set(index, value, strict).safeDelete(oldLength, index - 1, strict);
218            setLength(Math.max(underlying.length(), length()));
219        } else {
220            final Long longIndex = indexToKey(index);
221            sparseMap.put(longIndex, value);
222            setLength(Math.max(longIndex + 1, length()));
223        }
224        return this;
225    }
226
227    @Override
228    public ArrayData setEmpty(final int index) {
229        underlying.setEmpty(index);
230        return this;
231    }
232
233    @Override
234    public ArrayData setEmpty(final long lo, final long hi) {
235        underlying.setEmpty(lo, hi);
236        return this;
237    }
238
239    @Override
240    public Type getOptimisticType() {
241        return underlying.getOptimisticType();
242    }
243
244    @Override
245    public int getInt(final int index) {
246        if (index >= 0 && index < maxDenseLength) {
247            return underlying.getInt(index);
248        }
249        return JSType.toInt32(sparseMap.get(indexToKey(index)));
250    }
251
252    @Override
253    public int getIntOptimistic(final int index, final int programPoint) {
254        if (index >= 0 && index < maxDenseLength) {
255            return underlying.getIntOptimistic(index, programPoint);
256        }
257        return JSType.toInt32Optimistic(sparseMap.get(indexToKey(index)), programPoint);
258    }
259
260    @Override
261    public long getLong(final int index) {
262        if (index >= 0 && index < maxDenseLength) {
263            return underlying.getLong(index);
264        }
265        return JSType.toLong(sparseMap.get(indexToKey(index)));
266    }
267
268    @Override
269    public long getLongOptimistic(final int index, final int programPoint) {
270        if (index >= 0 && index < maxDenseLength) {
271            return underlying.getLongOptimistic(index, programPoint);
272        }
273        return JSType.toLongOptimistic(sparseMap.get(indexToKey(index)), programPoint);
274    }
275
276    @Override
277    public double getDouble(final int index) {
278        if (index >= 0 && index < maxDenseLength) {
279            return underlying.getDouble(index);
280        }
281        return JSType.toNumber(sparseMap.get(indexToKey(index)));
282    }
283
284    @Override
285    public double getDoubleOptimistic(final int index, final int programPoint) {
286        if (index >= 0 && index < maxDenseLength) {
287            return underlying.getDouble(index);
288        }
289        return JSType.toNumberOptimistic(sparseMap.get(indexToKey(index)), programPoint);
290    }
291
292    @Override
293    public Object getObject(final int index) {
294        if (index >= 0 && index < maxDenseLength) {
295            return underlying.getObject(index);
296        }
297
298        final Long key = indexToKey(index);
299        if (sparseMap.containsKey(key)) {
300            return sparseMap.get(key);
301        }
302
303        return ScriptRuntime.UNDEFINED;
304    }
305
306    @Override
307    public boolean has(final int index) {
308        if (index >= 0 && index < maxDenseLength) {
309            return index < underlying.length() && underlying.has(index);
310        }
311
312        return sparseMap.containsKey(indexToKey(index));
313    }
314
315    @Override
316    public ArrayData delete(final int index) {
317        if (index >= 0 && index < maxDenseLength) {
318            if (index < underlying.length()) {
319                underlying = underlying.delete(index);
320            }
321        } else {
322            sparseMap.remove(indexToKey(index));
323        }
324
325        return this;
326    }
327
328    @Override
329    public ArrayData delete(final long fromIndex, final long toIndex) {
330        if (fromIndex < maxDenseLength && fromIndex < underlying.length()) {
331            underlying = underlying.delete(fromIndex, Math.min(toIndex, underlying.length() - 1));
332        }
333        if (toIndex >= maxDenseLength) {
334            sparseMap.subMap(fromIndex, true, toIndex, true).clear();
335        }
336        return this;
337    }
338
339    private static Long indexToKey(final int index) {
340        return ArrayIndex.toLongIndex(index);
341    }
342
343    @Override
344    public ArrayData convert(final Class<?> type) {
345        underlying = underlying.convert(type);
346        return this;
347    }
348
349    @Override
350    public Object pop() {
351        final long len = length();
352        final long underlyingLen = underlying.length();
353        if (len == 0) {
354            return ScriptRuntime.UNDEFINED;
355        }
356        if (len == underlyingLen) {
357            final Object result = underlying.pop();
358            setLength(underlying.length());
359            return result;
360        }
361        setLength(len - 1);
362        final Long key = len - 1;
363        return sparseMap.containsKey(key) ? sparseMap.remove(key) : ScriptRuntime.UNDEFINED;
364    }
365
366    @Override
367    public ArrayData slice(final long from, final long to) {
368        assert to <= length();
369        final long start = from < 0 ? (from + length()) : from;
370        final long newLength = to - start;
371
372        final long underlyingLength = underlying.length();
373
374        if (start >= 0 && to <= maxDenseLength) {
375            if (newLength <= underlyingLength) {
376                return underlying.slice(from, to);
377            }
378            return underlying.slice(from, to).ensure(newLength - 1).delete(underlyingLength, newLength);
379        }
380
381        ArrayData sliced = EMPTY_ARRAY;
382        sliced = sliced.ensure(newLength - 1);
383        for (long i = start; i < to; i = nextIndex(i)) {
384            if (has((int)i)) {
385                sliced = sliced.set((int)(i - start), getObject((int)i), false);
386            }
387        }
388        assert sliced.length() == newLength;
389        return sliced;
390    }
391
392    @Override
393    public long nextIndex(final long index) {
394        if (index < underlying.length() - 1) {
395            return underlying.nextIndex(index);
396        }
397
398        final Long nextKey = sparseMap.higherKey(index);
399        if (nextKey != null) {
400            return nextKey;
401        }
402
403        return length();
404    }
405}
406