1 /* 2 * Copyright (c) 2015, 2018, 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 sun.java2d.marlin; 27 28 import static sun.java2d.marlin.ArrayCacheConst.ARRAY_SIZES; 29 import static sun.java2d.marlin.ArrayCacheConst.BUCKETS; 30 import static sun.java2d.marlin.ArrayCacheConst.MAX_ARRAY_SIZE; 31 import static sun.java2d.marlin.MarlinUtils.logInfo; 32 import static sun.java2d.marlin.MarlinUtils.logException; 33 34 import java.lang.ref.WeakReference; 35 import java.util.Arrays; 36 37 import sun.java2d.marlin.ArrayCacheConst.BucketStats; 38 import sun.java2d.marlin.ArrayCacheConst.CacheStats; 39 40 /* 41 * Note that the [BYTE/INT/FLOAT/DOUBLE]ArrayCache files are nearly identical except 42 * for a few type and name differences. Typically, the [BYTE]ArrayCache.java file 43 * is edited manually and then [INT/FLOAT/DOUBLE]ArrayCache.java 44 * files are generated with the following command lines: 45 */ 46 // % sed -e 's/(b\yte)[ ]*//g' -e 's/b\yte/int/g' -e 's/B\yte/Int/g' < B\yteArrayCache.java > IntArrayCache.java 47 // % sed -e 's/(b\yte)[ ]*0/0.0f/g' -e 's/(b\yte)[ ]*/(float) /g' -e 's/b\yte/float/g' -e 's/B\yte/Float/g' < B\yteArrayCache.java > FloatArrayCache.java 48 // % sed -e 's/(b\yte)[ ]*0/0.0d/g' -e 's/(b\yte)[ ]*/(double) /g' -e 's/b\yte/double/g' -e 's/B\yte/Double/g' < B\yteArrayCache.java > DoubleArrayCache.java 49 50 final class DoubleArrayCache implements MarlinConst { 51 52 final boolean clean; 53 private final int bucketCapacity; 54 private WeakReference<Bucket[]> refBuckets = null; 55 final CacheStats stats; 56 DoubleArrayCache(final boolean clean, final int bucketCapacity)57 DoubleArrayCache(final boolean clean, final int bucketCapacity) { 58 this.clean = clean; 59 this.bucketCapacity = bucketCapacity; 60 this.stats = (DO_STATS) ? 61 new CacheStats(getLogPrefix(clean) + "DoubleArrayCache") : null; 62 } 63 getCacheBucket(final int length)64 Bucket getCacheBucket(final int length) { 65 final int bucket = ArrayCacheConst.getBucket(length); 66 return getBuckets()[bucket]; 67 } 68 getBuckets()69 private Bucket[] getBuckets() { 70 // resolve reference: 71 Bucket[] buckets = (refBuckets != null) ? refBuckets.get() : null; 72 73 // create a new buckets ? 74 if (buckets == null) { 75 buckets = new Bucket[BUCKETS]; 76 77 for (int i = 0; i < BUCKETS; i++) { 78 buckets[i] = new Bucket(clean, ARRAY_SIZES[i], bucketCapacity, 79 (DO_STATS) ? stats.bucketStats[i] : null); 80 } 81 82 // update weak reference: 83 refBuckets = new WeakReference<Bucket[]>(buckets); 84 } 85 return buckets; 86 } 87 createRef(final int initialSize)88 Reference createRef(final int initialSize) { 89 return new Reference(this, initialSize); 90 } 91 92 static final class Reference { 93 94 // initial array reference (direct access) 95 final double[] initial; 96 private final boolean clean; 97 private final DoubleArrayCache cache; 98 Reference(final DoubleArrayCache cache, final int initialSize)99 Reference(final DoubleArrayCache cache, final int initialSize) { 100 this.cache = cache; 101 this.clean = cache.clean; 102 this.initial = createArray(initialSize); 103 if (DO_STATS) { 104 cache.stats.totalInitial += initialSize; 105 } 106 } 107 getArray(final int length)108 double[] getArray(final int length) { 109 if (length <= MAX_ARRAY_SIZE) { 110 return cache.getCacheBucket(length).getArray(); 111 } 112 if (DO_STATS) { 113 cache.stats.oversize++; 114 } 115 if (DO_LOG_OVERSIZE) { 116 logInfo(getLogPrefix(clean) + "DoubleArrayCache: " 117 + "getArray[oversize]: length=\t" + length); 118 } 119 return createArray(length); 120 } 121 widenArray(final double[] array, final int usedSize, final int needSize)122 double[] widenArray(final double[] array, final int usedSize, 123 final int needSize) 124 { 125 final int length = array.length; 126 if (DO_CHECKS && length >= needSize) { 127 return array; 128 } 129 if (DO_STATS) { 130 cache.stats.resize++; 131 } 132 133 // maybe change bucket: 134 // ensure getNewSize() > newSize: 135 final double[] res = getArray(ArrayCacheConst.getNewSize(usedSize, needSize)); 136 137 // use wrapper to ensure proper copy: 138 System.arraycopy(array, 0, res, 0, usedSize); // copy only used elements 139 140 // maybe return current array: 141 putArray(array, 0, usedSize); // ensure array is cleared 142 143 if (DO_LOG_WIDEN_ARRAY) { 144 logInfo(getLogPrefix(clean) + "DoubleArrayCache: " 145 + "widenArray[" + res.length 146 + "]: usedSize=\t" + usedSize + "\tlength=\t" + length 147 + "\tneeded length=\t" + needSize); 148 } 149 return res; 150 } 151 putArray(final double[] array)152 double[] putArray(final double[] array) 153 { 154 // dirty array helper: 155 return putArray(array, 0, array.length); 156 } 157 putArray(final double[] array, final int fromIndex, final int toIndex)158 double[] putArray(final double[] array, final int fromIndex, 159 final int toIndex) 160 { 161 if (array.length <= MAX_ARRAY_SIZE) { 162 if ((clean || DO_CLEAN_DIRTY) && (toIndex != 0)) { 163 // clean-up array of dirty part[fromIndex; toIndex[ 164 fill(array, fromIndex, toIndex, 0.0d); 165 } 166 // ensure to never store initial arrays in cache: 167 if (array != initial) { 168 cache.getCacheBucket(array.length).putArray(array); 169 } 170 } 171 return initial; 172 } 173 } 174 175 static final class Bucket { 176 177 private int tail = 0; 178 private final int arraySize; 179 private final boolean clean; 180 private final double[][] arrays; 181 private final BucketStats stats; 182 Bucket(final boolean clean, final int arraySize, final int capacity, final BucketStats stats)183 Bucket(final boolean clean, final int arraySize, 184 final int capacity, final BucketStats stats) 185 { 186 this.arraySize = arraySize; 187 this.clean = clean; 188 this.stats = stats; 189 this.arrays = new double[capacity][]; 190 } 191 getArray()192 double[] getArray() { 193 if (DO_STATS) { 194 stats.getOp++; 195 } 196 // use cache: 197 if (tail != 0) { 198 final double[] array = arrays[--tail]; 199 arrays[tail] = null; 200 return array; 201 } 202 if (DO_STATS) { 203 stats.createOp++; 204 } 205 return createArray(arraySize); 206 } 207 putArray(final double[] array)208 void putArray(final double[] array) 209 { 210 if (DO_CHECKS && (array.length != arraySize)) { 211 logInfo(getLogPrefix(clean) + "DoubleArrayCache: " 212 + "bad length = " + array.length); 213 return; 214 } 215 if (DO_STATS) { 216 stats.returnOp++; 217 } 218 // fill cache: 219 if (arrays.length > tail) { 220 arrays[tail++] = array; 221 222 if (DO_STATS) { 223 stats.updateMaxSize(tail); 224 } 225 } else if (DO_CHECKS) { 226 logInfo(getLogPrefix(clean) + "DoubleArrayCache: " 227 + "array capacity exceeded !"); 228 } 229 } 230 } 231 createArray(final int length)232 static double[] createArray(final int length) { 233 return new double[length]; 234 } 235 fill(final double[] array, final int fromIndex, final int toIndex, final double value)236 static void fill(final double[] array, final int fromIndex, 237 final int toIndex, final double value) 238 { 239 // clear array data: 240 Arrays.fill(array, fromIndex, toIndex, value); 241 if (DO_CHECKS) { 242 check(array, fromIndex, toIndex, value); 243 } 244 } 245 check(final double[] array, final int fromIndex, final int toIndex, final double value)246 static void check(final double[] array, final int fromIndex, 247 final int toIndex, final double value) 248 { 249 if (DO_CHECKS) { 250 // check zero on full array: 251 for (int i = 0; i < array.length; i++) { 252 if (array[i] != value) { 253 logException("Invalid value at: " + i + " = " + array[i] 254 + " from: " + fromIndex + " to: " + toIndex + "\n" 255 + Arrays.toString(array), new Throwable()); 256 257 // ensure array is correctly filled: 258 Arrays.fill(array, value); 259 260 return; 261 } 262 } 263 } 264 } 265 getLogPrefix(final boolean clean)266 static String getLogPrefix(final boolean clean) { 267 return (clean) ? "Clean" : "Dirty"; 268 } 269 } 270