1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 only, as
8 * published by the Free Software Foundation.  Oracle designates this
9 * particular file as subject to the "Classpath" exception as provided
10 * by Oracle in the LICENSE file that accompanied this code.
11 *
12 * This code is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 * version 2 for more details (a copy is included in the LICENSE file that
16 * accompanied this code).
17 *
18 * You should have received a copy of the GNU General Public License version
19 * 2 along with this work; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23 * or visit www.oracle.com if you need additional information or have any
24 * questions.
25 */
26
27package sun.nio.ch;
28
29import java.nio.ByteBuffer;
30import java.security.AccessController;
31import java.util.*;
32import sun.misc.Unsafe;
33import sun.misc.Cleaner;
34import sun.security.action.GetPropertyAction;
35
36
37public class Util {
38
39    // -- Caches --
40
41    // The number of temp buffers in our pool
42    private static final int TEMP_BUF_POOL_SIZE = IOUtil.IOV_MAX;
43
44    // Per-thread cache of temporary direct buffers
45    private static ThreadLocal<BufferCache> bufferCache =
46        new ThreadLocal<BufferCache>()
47    {
48        @Override
49        protected BufferCache initialValue() {
50            return new BufferCache();
51        }
52    };
53
54    /**
55     * A simple cache of direct buffers.
56     */
57    private static class BufferCache {
58        // the array of buffers
59        private ByteBuffer[] buffers;
60
61        // the number of buffers in the cache
62        private int count;
63
64        // the index of the first valid buffer (undefined if count == 0)
65        private int start;
66
67        private int next(int i) {
68            return (i + 1) % TEMP_BUF_POOL_SIZE;
69        }
70
71        BufferCache() {
72            buffers = new ByteBuffer[TEMP_BUF_POOL_SIZE];
73        }
74
75        /**
76         * Removes and returns a buffer from the cache of at least the given
77         * size (or null if no suitable buffer is found).
78         */
79        ByteBuffer get(int size) {
80            if (count == 0)
81                return null;  // cache is empty
82
83            ByteBuffer[] buffers = this.buffers;
84
85            // search for suitable buffer (often the first buffer will do)
86            ByteBuffer buf = buffers[start];
87            if (buf.capacity() < size) {
88                buf = null;
89                int i = start;
90                while ((i = next(i)) != start) {
91                    ByteBuffer bb = buffers[i];
92                    if (bb == null)
93                        break;
94                    if (bb.capacity() >= size) {
95                        buf = bb;
96                        break;
97                    }
98                }
99                if (buf == null)
100                    return null;
101                // move first element to here to avoid re-packing
102                buffers[i] = buffers[start];
103            }
104
105            // remove first element
106            buffers[start] = null;
107            start = next(start);
108            count--;
109
110            // prepare the buffer and return it
111            buf.rewind();
112            buf.limit(size);
113            return buf;
114        }
115
116        boolean offerFirst(ByteBuffer buf) {
117            if (count >= TEMP_BUF_POOL_SIZE) {
118                return false;
119            } else {
120                start = (start + TEMP_BUF_POOL_SIZE - 1) % TEMP_BUF_POOL_SIZE;
121                buffers[start] = buf;
122                count++;
123                return true;
124            }
125        }
126
127        boolean offerLast(ByteBuffer buf) {
128            if (count >= TEMP_BUF_POOL_SIZE) {
129                return false;
130            } else {
131                int next = (start + count) % TEMP_BUF_POOL_SIZE;
132                buffers[next] = buf;
133                count++;
134                return true;
135            }
136        }
137
138        boolean isEmpty() {
139            return count == 0;
140        }
141
142        ByteBuffer removeFirst() {
143            assert count > 0;
144            ByteBuffer buf = buffers[start];
145            buffers[start] = null;
146            start = next(start);
147            count--;
148            return buf;
149        }
150    }
151
152    /**
153     * Returns a temporary buffer of at least the given size
154     */
155    public static ByteBuffer getTemporaryDirectBuffer(int size) {
156        BufferCache cache = bufferCache.get();
157        ByteBuffer buf = cache.get(size);
158        if (buf != null) {
159            return buf;
160        } else {
161            // No suitable buffer in the cache so we need to allocate a new
162            // one. To avoid the cache growing then we remove the first
163            // buffer from the cache and free it.
164            if (!cache.isEmpty()) {
165                buf = cache.removeFirst();
166                free(buf);
167            }
168            return ByteBuffer.allocateDirect(size);
169        }
170    }
171
172    /**
173     * Releases a temporary buffer by returning to the cache or freeing it.
174     */
175    public static void releaseTemporaryDirectBuffer(ByteBuffer buf) {
176        offerFirstTemporaryDirectBuffer(buf);
177    }
178
179    /**
180     * Releases a temporary buffer by returning to the cache or freeing it. If
181     * returning to the cache then insert it at the start so that it is
182     * likely to be returned by a subsequent call to getTemporaryDirectBuffer.
183     */
184    static void offerFirstTemporaryDirectBuffer(ByteBuffer buf) {
185        assert buf != null;
186        BufferCache cache = bufferCache.get();
187        if (!cache.offerFirst(buf)) {
188            // cache is full
189            free(buf);
190        }
191    }
192
193    /**
194     * Releases a temporary buffer by returning to the cache or freeing it. If
195     * returning to the cache then insert it at the end. This makes it
196     * suitable for scatter/gather operations where the buffers are returned to
197     * cache in same order that they were obtained.
198     */
199    static void offerLastTemporaryDirectBuffer(ByteBuffer buf) {
200        assert buf != null;
201        BufferCache cache = bufferCache.get();
202        if (!cache.offerLast(buf)) {
203            // cache is full
204            free(buf);
205        }
206    }
207
208    /**
209     * Frees the memory for the given direct buffer
210     */
211    private static void free(ByteBuffer buf) {
212        Cleaner cleaner = ((DirectBuffer)buf).cleaner();
213        if (cleaner != null) {
214            cleaner.clean();
215        }
216    }
217
218
219    // -- Random stuff --
220
221    static ByteBuffer[] subsequence(ByteBuffer[] bs, int offset, int length) {
222        if ((offset == 0) && (length == bs.length))
223            return bs;
224        int n = length;
225        ByteBuffer[] bs2 = new ByteBuffer[n];
226        for (int i = 0; i < n; i++)
227            bs2[i] = bs[offset + i];
228        return bs2;
229    }
230
231    static <E> Set<E> ungrowableSet(final Set<E> s) {
232        return new Set<E>() {
233
234                public int size()                 { return s.size(); }
235                public boolean isEmpty()          { return s.isEmpty(); }
236                public boolean contains(Object o) { return s.contains(o); }
237                public Object[] toArray()         { return s.toArray(); }
238                public <T> T[] toArray(T[] a)     { return s.toArray(a); }
239                public String toString()          { return s.toString(); }
240                public Iterator<E> iterator()     { return s.iterator(); }
241                public boolean equals(Object o)   { return s.equals(o); }
242                public int hashCode()             { return s.hashCode(); }
243                public void clear()               { s.clear(); }
244                public boolean remove(Object o)   { return s.remove(o); }
245
246                public boolean containsAll(Collection<?> coll) {
247                    return s.containsAll(coll);
248                }
249                public boolean removeAll(Collection<?> coll) {
250                    return s.removeAll(coll);
251                }
252                public boolean retainAll(Collection<?> coll) {
253                    return s.retainAll(coll);
254                }
255
256                public boolean add(E o){
257                    throw new UnsupportedOperationException();
258                }
259                public boolean addAll(Collection<? extends E> coll) {
260                    throw new UnsupportedOperationException();
261                }
262
263        };
264    }
265
266
267    // -- Unsafe access --
268
269    private static Unsafe unsafe = Unsafe.getUnsafe();
270
271    private static byte _get(long a) {
272        return unsafe.getByte(a);
273    }
274
275    private static void _put(long a, byte b) {
276        unsafe.putByte(a, b);
277    }
278
279    static void erase(ByteBuffer bb) {
280        unsafe.setMemory(((DirectBuffer)bb).address(), bb.capacity(), (byte)0);
281    }
282
283    static Unsafe unsafe() {
284        return unsafe;
285    }
286
287    private static int pageSize = -1;
288
289    static int pageSize() {
290        if (pageSize == -1)
291            pageSize = unsafe().pageSize();
292        return pageSize;
293    }
294
295    // -- Bug compatibility --
296    private static volatile String bugLevel = null;
297
298    static boolean atBugLevel(String bl) {              // package-private
299        if (bugLevel == null) {
300            if (!sun.misc.VM.isBooted())
301                return false;
302            String value = AccessController.doPrivileged(
303                new GetPropertyAction("sun.nio.ch.bugLevel"));
304            bugLevel = (value != null) ? value : "";
305        }
306        return bugLevel.equals(bl);
307    }
308
309}
310