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 
27 package sun.nio.ch;
28 
29 import java.nio.ByteBuffer;
30 import java.security.AccessController;
31 import java.util.*;
32 import sun.misc.Unsafe;
33 import sun.misc.Cleaner;
34 import sun.security.action.GetPropertyAction;
35 
36 
37 public 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 
next(int i)67         private int next(int i) {
68             return (i + 1) % TEMP_BUF_POOL_SIZE;
69         }
70 
BufferCache()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          */
get(int size)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 
offerFirst(ByteBuffer buf)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 
offerLast(ByteBuffer buf)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 
isEmpty()138         boolean isEmpty() {
139             return count == 0;
140         }
141 
removeFirst()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      */
getTemporaryDirectBuffer(int size)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      */
releaseTemporaryDirectBuffer(ByteBuffer buf)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      */
offerFirstTemporaryDirectBuffer(ByteBuffer buf)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      */
offerLastTemporaryDirectBuffer(ByteBuffer buf)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      */
free(ByteBuffer buf)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 
subsequence(ByteBuffer[] bs, int offset, int length)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 
ungrowableSet(final Set<E> s)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