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
26package sun.java2d.marlin;
27
28import java.util.Arrays;
29import static sun.java2d.marlin.MarlinUtils.logInfo;
30
31public final class ArrayCacheConst implements MarlinConst {
32
33    static final int BUCKETS = 8;
34    static final int MIN_ARRAY_SIZE = 4096;
35    // maximum array size
36    static final int MAX_ARRAY_SIZE;
37    // threshold below to grow arrays by 4
38    static final int THRESHOLD_SMALL_ARRAY_SIZE = 4 * 1024 * 1024;
39    // threshold to grow arrays only by (3/2) instead of 2
40    static final int THRESHOLD_ARRAY_SIZE;
41    // threshold to grow arrays only by (5/4) instead of (3/2)
42    static final long THRESHOLD_HUGE_ARRAY_SIZE;
43    static final int[] ARRAY_SIZES = new int[BUCKETS];
44
45    static {
46        // initialize buckets for int/float arrays
47        int arraySize = MIN_ARRAY_SIZE;
48
49        int inc_lg = 2; // x4
50
51        for (int i = 0; i < BUCKETS; i++, arraySize <<= inc_lg) {
52            ARRAY_SIZES[i] = arraySize;
53
54            if (DO_TRACE) {
55                logInfo("arraySize[" + i + "]: " + arraySize);
56            }
57
58            if (arraySize >= THRESHOLD_SMALL_ARRAY_SIZE) {
59                inc_lg = 1; // x2
60            }
61        }
62        MAX_ARRAY_SIZE = arraySize >> inc_lg;
63
64        if (MAX_ARRAY_SIZE <= 0) {
65            throw new IllegalStateException("Invalid max array size !");
66        }
67
68        THRESHOLD_ARRAY_SIZE       =  16  * 1024 * 1024; // >16M
69        THRESHOLD_HUGE_ARRAY_SIZE  =  48L * 1024 * 1024; // >48M
70
71        if (DO_STATS || DO_MONITORS) {
72            logInfo("ArrayCache.BUCKETS        = " + BUCKETS);
73            logInfo("ArrayCache.MIN_ARRAY_SIZE = " + MIN_ARRAY_SIZE);
74            logInfo("ArrayCache.MAX_ARRAY_SIZE = " + MAX_ARRAY_SIZE);
75            logInfo("ArrayCache.ARRAY_SIZES = "
76                    + Arrays.toString(ARRAY_SIZES));
77            logInfo("ArrayCache.THRESHOLD_ARRAY_SIZE = "
78                    + THRESHOLD_ARRAY_SIZE);
79            logInfo("ArrayCache.THRESHOLD_HUGE_ARRAY_SIZE = "
80                    + THRESHOLD_HUGE_ARRAY_SIZE);
81        }
82    }
83
84    private ArrayCacheConst() {
85        // Utility class
86    }
87
88    // small methods used a lot (to be inlined / optimized by hotspot)
89
90    static int getBucket(final int length) {
91        for (int i = 0; i < ARRAY_SIZES.length; i++) {
92            if (length <= ARRAY_SIZES[i]) {
93                return i;
94            }
95        }
96        return -1;
97    }
98
99    /**
100     * Return the new array size (~ x2)
101     * @param curSize current used size
102     * @param needSize needed size
103     * @return new array size
104     */
105    public static int getNewSize(final int curSize, final int needSize) {
106        // check if needSize is negative or integer overflow:
107        if (needSize < 0) {
108            // hard overflow failure - we can't even accommodate
109            // new items without overflowing
110            throw new ArrayIndexOutOfBoundsException(
111                          "array exceeds maximum capacity !");
112        }
113        assert curSize >= 0;
114        final int initial = curSize;
115        int size;
116        if (initial > THRESHOLD_ARRAY_SIZE) {
117            size = initial + (initial >> 1); // x(3/2)
118        } else {
119            size = (initial << 1); // x2
120        }
121        // ensure the new size is >= needed size:
122        if (size < needSize) {
123            // align to 4096 (may overflow):
124            size = ((needSize >> 12) + 1) << 12;
125        }
126        // check integer overflow:
127        if (size < 0) {
128            // resize to maximum capacity:
129            size = Integer.MAX_VALUE;
130        }
131        return size;
132    }
133
134    /**
135     * Return the new array size (~ x2)
136     * @param curSize current used size
137     * @param needSize needed size
138     * @return new array size
139     */
140    public static long getNewLargeSize(final long curSize, final long needSize) {
141        // check if needSize is negative or integer overflow:
142        if ((needSize >> 31L) != 0L) {
143            // hard overflow failure - we can't even accommodate
144            // new items without overflowing
145            throw new ArrayIndexOutOfBoundsException(
146                          "array exceeds maximum capacity !");
147        }
148        assert curSize >= 0L;
149        long size;
150        if (curSize > THRESHOLD_HUGE_ARRAY_SIZE) {
151            size = curSize + (curSize >> 2L); // x(5/4)
152        } else if (curSize > THRESHOLD_ARRAY_SIZE) {
153            size = curSize + (curSize >> 1L); // x(3/2)
154        } else if (curSize > THRESHOLD_SMALL_ARRAY_SIZE) {
155            size = (curSize << 1L); // x2
156        } else {
157            size = (curSize << 2L); // x4
158        }
159        // ensure the new size is >= needed size:
160        if (size < needSize) {
161            // align to 4096:
162            size = ((needSize >> 12L) + 1L) << 12L;
163        }
164        // check integer overflow:
165        if (size > Integer.MAX_VALUE) {
166            // resize to maximum capacity:
167            size = Integer.MAX_VALUE;
168        }
169        return size;
170    }
171
172    static final class CacheStats {
173        final String name;
174        final BucketStats[] bucketStats;
175        int resize = 0;
176        int oversize = 0;
177        long totalInitial = 0L;
178
179        CacheStats(final String name) {
180            this.name = name;
181
182            bucketStats = new BucketStats[BUCKETS];
183            for (int i = 0; i < BUCKETS; i++) {
184                bucketStats[i] = new BucketStats();
185            }
186        }
187
188        void reset() {
189            resize = 0;
190            oversize = 0;
191
192            for (int i = 0; i < BUCKETS; i++) {
193                bucketStats[i].reset();
194            }
195        }
196
197        long dumpStats() {
198            long totalCacheBytes = 0L;
199
200            if (DO_STATS) {
201                for (int i = 0; i < BUCKETS; i++) {
202                    final BucketStats s = bucketStats[i];
203
204                    if (s.maxSize != 0) {
205                        totalCacheBytes += getByteFactor()
206                                           * (s.maxSize * ARRAY_SIZES[i]);
207                    }
208                }
209
210                if (totalInitial != 0L || totalCacheBytes != 0L
211                    || resize != 0 || oversize != 0)
212                {
213                    logInfo(name + ": resize: " + resize
214                            + " - oversize: " + oversize
215                            + " - initial: " + getTotalInitialBytes()
216                            + " bytes (" + totalInitial + " elements)"
217                            + " - cache: " + totalCacheBytes + " bytes"
218                    );
219                }
220
221                if (totalCacheBytes != 0L) {
222                    logInfo(name + ": usage stats:");
223
224                    for (int i = 0; i < BUCKETS; i++) {
225                        final BucketStats s = bucketStats[i];
226
227                        if (s.getOp != 0) {
228                            logInfo("  Bucket[" + ARRAY_SIZES[i] + "]: "
229                                    + "get: " + s.getOp
230                                    + " - put: " + s.returnOp
231                                    + " - create: " + s.createOp
232                                    + " :: max size: " + s.maxSize
233                            );
234                        }
235                    }
236                }
237            }
238            return totalCacheBytes;
239        }
240
241        private int getByteFactor() {
242            int factor = 1;
243            if (name.contains("Int") || name.contains("Float")) {
244                factor = 4;
245            }
246            return factor;
247        }
248
249        long getTotalInitialBytes() {
250            return getByteFactor() * totalInitial;
251        }
252    }
253
254    static final class BucketStats {
255        int getOp = 0;
256        int createOp = 0;
257        int returnOp = 0;
258        int maxSize = 0;
259
260        void reset() {
261            getOp = 0;
262            createOp = 0;
263            returnOp = 0;
264            maxSize = 0;
265        }
266
267        void updateMaxSize(final int size) {
268            if (size > maxSize) {
269                maxSize = size;
270            }
271        }
272    }
273}
274