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