1 /* 2 * Copyright (c) 2015, 2017, 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 ByteArrayCache implements MarlinConst { 51 52 final boolean clean; 53 private final int bucketCapacity; 54 private WeakReference<Bucket[]> refBuckets = null; 55 final CacheStats stats; 56 57 ByteArrayCache(final boolean clean, final int bucketCapacity) { 58 this.clean = clean; 59 this.bucketCapacity = bucketCapacity; 60 this.stats = (DO_STATS) ? 61 new CacheStats(getLogPrefix(clean) + "ByteArrayCache") : null; 62 } 63 64 Bucket getCacheBucket(final int length) { 65 final int bucket = ArrayCacheConst.getBucket(length); 66 return getBuckets()[bucket]; 67 } 68 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 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 byte[] initial; 96 private final boolean clean; 97 private final ByteArrayCache cache; 98 99 Reference(final ByteArrayCache cache, final int initialSize) { 100 this.cache = cache; 101 this.clean = cache.clean; 102 this.initial = createArray(initialSize, clean); 103 if (DO_STATS) { 104 cache.stats.totalInitial += initialSize; 105 } 106 } 107 108 byte[] 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) + "ByteArrayCache: " 117 + "getArray[oversize]: length=\t" + length); 118 } 119 return createArray(length, clean); 120 } 121 122 byte[] widenArray(final byte[] 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 byte[] 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) + "ByteArrayCache: " 145 + "widenArray[" + res.length 146 + "]: usedSize=\t" + usedSize + "\tlength=\t" + length 147 + "\tneeded length=\t" + needSize); 148 } 149 return res; 150 } 151 152 byte[] putArray(final byte[] array) 153 { 154 // dirty array helper: 155 return putArray(array, 0, array.length); 156 } 157 158 byte[] putArray(final byte[] 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, (byte)0); 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 byte[][] arrays; 181 private final BucketStats stats; 182 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 byte[capacity][]; 190 } 191 192 byte[] getArray() { 193 if (DO_STATS) { 194 stats.getOp++; 195 } 196 // use cache: 197 if (tail != 0) { 198 final byte[] array = arrays[--tail]; 199 arrays[tail] = null; 200 return array; 201 } 202 if (DO_STATS) { 203 stats.createOp++; 204 } 205 return createArray(arraySize, clean); 206 } 207 208 void putArray(final byte[] array) 209 { 210 if (DO_CHECKS && (array.length != arraySize)) { 211 logInfo(getLogPrefix(clean) + "ByteArrayCache: " 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) + "ByteArrayCache: " 227 + "array capacity exceeded !"); 228 } 229 } 230 } 231 232 static byte[] createArray(final int length, final boolean clean) { 233 if (clean) { 234 return new byte[length]; 235 } 236 // use JDK9 Unsafe.allocateUninitializedArray(class, length): 237 return (byte[]) OffHeapArray.UNSAFE.allocateUninitializedArray(byte.class, length); 238 } 239 240 static void fill(final byte[] array, final int fromIndex, 241 final int toIndex, final byte value) 242 { 243 // clear array data: 244 Arrays.fill(array, fromIndex, toIndex, value); 245 if (DO_CHECKS) { 246 check(array, fromIndex, toIndex, value); 247 } 248 } 249 250 static void check(final byte[] array, final int fromIndex, 251 final int toIndex, final byte value) 252 { 253 if (DO_CHECKS) { 254 // check zero on full array: 255 for (int i = 0; i < array.length; i++) { 256 if (array[i] != value) { 257 logException("Invalid value at: " + i + " = " + array[i] 258 + " from: " + fromIndex + " to: " + toIndex + "\n" 259 + Arrays.toString(array), new Throwable()); 260 261 // ensure array is correctly filled: 262 Arrays.fill(array, value); 263 264 return; 265 } 266 } 267 } 268 } 269 270 static String getLogPrefix(final boolean clean) { 271 return (clean) ? "Clean" : "Dirty"; 272 } 273 }