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 26 package jdk.nashorn.internal.runtime.arrays; 27 28 import java.util.Arrays; 29 import java.util.Map; 30 import java.util.TreeMap; 31 import jdk.nashorn.internal.codegen.types.Type; 32 import jdk.nashorn.internal.runtime.JSType; 33 import jdk.nashorn.internal.runtime.ScriptRuntime; 34 35 /** 36 * Handle arrays where the index is very large. 37 */ 38 class 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<Long, Object>()); 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().longValue() - by; 99 if (newIndex < maxDenseLength) { 100 underlying = underlying.set((int) newIndex, entry.getValue(), false); 101 } else if (newIndex >= 0) { 102 newSparseMap.put(Long.valueOf(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(Long.valueOf(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().longValue() + by; 127 newSparseMap.put(Long.valueOf(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(Long.valueOf(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 double 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 setEmpty(final int index) { 214 underlying.setEmpty(index); 215 return this; 216 } 217 218 @Override 219 public ArrayData setEmpty(final long lo, final long hi) { 220 underlying.setEmpty(lo, hi); 221 return this; 222 } 223 224 @Override 225 public Type getOptimisticType() { 226 return underlying.getOptimisticType(); 227 } 228 229 @Override 230 public int getInt(final int index) { 231 if (index >= 0 && index < maxDenseLength) { 232 return underlying.getInt(index); 233 } 234 return JSType.toInt32(sparseMap.get(indexToKey(index))); 235 } 236 237 @Override 238 public int getIntOptimistic(final int index, final int programPoint) { 239 if (index >= 0 && index < maxDenseLength) { 240 return underlying.getIntOptimistic(index, programPoint); 241 } 242 return JSType.toInt32Optimistic(sparseMap.get(indexToKey(index)), programPoint); 243 } 244 245 @Override 246 public double getDouble(final int index) { 247 if (index >= 0 && index < maxDenseLength) { 248 return underlying.getDouble(index); 249 } 250 return JSType.toNumber(sparseMap.get(indexToKey(index))); 251 } 252 253 @Override 254 public double getDoubleOptimistic(final int index, final int programPoint) { 255 if (index >= 0 && index < maxDenseLength) { 256 return underlying.getDouble(index); 257 } 258 return JSType.toNumberOptimistic(sparseMap.get(indexToKey(index)), programPoint); 259 } 260 261 @Override 262 public Object getObject(final int index) { 263 if (index >= 0 && index < maxDenseLength) { 264 return underlying.getObject(index); 265 } 266 267 final Long key = indexToKey(index); 268 if (sparseMap.containsKey(key)) { 269 return sparseMap.get(key); 270 } 271 272 return ScriptRuntime.UNDEFINED; 273 } 274 275 @Override 276 public boolean has(final int index) { 277 if (index >= 0 && index < maxDenseLength) { 278 return index < underlying.length() && underlying.has(index); 279 } 280 281 return sparseMap.containsKey(indexToKey(index)); 282 } 283 284 @Override 285 public ArrayData delete(final int index) { 286 if (index >= 0 && index < maxDenseLength) { 287 if (index < underlying.length()) { 288 underlying = underlying.delete(index); 289 } 290 } else { 291 sparseMap.remove(indexToKey(index)); 292 } 293 294 return this; 295 } 296 297 @Override 298 public ArrayData delete(final long fromIndex, final long toIndex) { 299 if (fromIndex < maxDenseLength && fromIndex < underlying.length()) { 300 underlying = underlying.delete(fromIndex, Math.min(toIndex, underlying.length() - 1)); 301 } 302 if (toIndex >= maxDenseLength) { 303 sparseMap.subMap(fromIndex, true, toIndex, true).clear(); 304 } 305 return this; 306 } 307 308 private static Long indexToKey(final int index) { 309 return Long.valueOf(ArrayIndex.toLongIndex(index)); 310 } 311 312 @Override 313 public ArrayData convert(final Class<?> type) { 314 underlying = underlying.convert(type); 315 return this; 316 } 317 318 @Override 319 public Object pop() { 320 final long len = length(); 321 final long underlyingLen = underlying.length(); 322 if (len == 0) { 323 return ScriptRuntime.UNDEFINED; 324 } 325 if (len == underlyingLen) { 326 final Object result = underlying.pop(); 327 setLength(underlying.length()); 328 return result; 329 } 330 setLength(len - 1); 331 final Long key = Long.valueOf(len - 1); 332 return sparseMap.containsKey(key) ? sparseMap.remove(key) : ScriptRuntime.UNDEFINED; 333 } 334 335 @Override 336 public ArrayData slice(final long from, final long to) { 337 assert to <= length(); 338 final long start = from < 0 ? (from + length()) : from; 339 final long newLength = to - start; 340 341 final long underlyingLength = underlying.length(); 342 343 if (start >= 0 && to <= maxDenseLength) { 344 if (newLength <= underlyingLength) { 345 return underlying.slice(from, to); 346 } 347 return underlying.slice(from, to).ensure(newLength - 1).delete(underlyingLength, newLength); 348 } 349 350 ArrayData sliced = EMPTY_ARRAY; 351 sliced = sliced.ensure(newLength - 1); 352 for (long i = start; i < to; i = nextIndex(i)) { 353 if (has((int)i)) { 354 sliced = sliced.set((int)(i - start), getObject((int)i), false); 355 } 356 } 357 assert sliced.length() == newLength; 358 return sliced; 359 } 360 361 @Override 362 public long nextIndex(final long index) { 363 if (index < underlying.length() - 1) { 364 return underlying.nextIndex(index); 365 } 366 367 final Long nextKey = sparseMap.higherKey(index); 368 if (nextKey != null) { 369 return nextKey; 370 } 371 372 return length(); 373 } 374 }