1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  * Copyright (c) 2000, 2016, 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.security.PrivilegedAction;
32 import java.util.*;
33 import sun.misc.Unsafe;
34 import sun.misc.Cleaner;
35 import sun.security.action.GetPropertyAction;
36 
37 
38 public class Util {
39 
40     // -- Caches --
41 
42     // The number of temp buffers in our pool
43     private static final int TEMP_BUF_POOL_SIZE = IOUtil.IOV_MAX;
44 
45     // The max size allowed for a cached temp buffer, in bytes
46     private static final long MAX_CACHED_BUFFER_SIZE = getMaxCachedBufferSize();
47 
48     // Per-thread cache of temporary direct buffers
49     private static ThreadLocal<BufferCache> bufferCache =
50         new ThreadLocal<BufferCache>()
51     {
52         @Override
53         protected BufferCache initialValue() {
54             return new BufferCache();
55         }
56     };
57 
58     /**
59      * Returns the max size allowed for a cached temp buffers, in
60      * bytes. It defaults to Long.MAX_VALUE. It can be set with the
61      * jdk.nio.maxCachedBufferSize property. Even though
62      * ByteBuffer.capacity() returns an int, we're using a long here
63      * for potential future-proofing.
64      */
getMaxCachedBufferSize()65     private static long getMaxCachedBufferSize() {
66         String s = java.security.AccessController.doPrivileged(
67             new PrivilegedAction<String>() {
68                 @Override
69                 public String run() {
70                     return System.getProperty("jdk.nio.maxCachedBufferSize");
71                 }
72             });
73         if (s != null) {
74             try {
75                 long m = Long.parseLong(s);
76                 if (m >= 0) {
77                     return m;
78                 } else {
79                     // if it's negative, ignore the system property
80                 }
81             } catch (NumberFormatException e) {
82                 // if the string is not well formed, ignore the system property
83             }
84         }
85         return Long.MAX_VALUE;
86     }
87 
88     /**
89      * Returns true if a buffer of this size is too large to be
90      * added to the buffer cache, false otherwise.
91      */
isBufferTooLarge(int size)92     private static boolean isBufferTooLarge(int size) {
93         return size > MAX_CACHED_BUFFER_SIZE;
94     }
95 
96     /**
97      * Returns true if the buffer is too large to be added to the
98      * buffer cache, false otherwise.
99      */
isBufferTooLarge(ByteBuffer buf)100     private static boolean isBufferTooLarge(ByteBuffer buf) {
101         return isBufferTooLarge(buf.capacity());
102     }
103 
104     /**
105      * A simple cache of direct buffers.
106      */
107     private static class BufferCache {
108         // the array of buffers
109         private ByteBuffer[] buffers;
110 
111         // the number of buffers in the cache
112         private int count;
113 
114         // the index of the first valid buffer (undefined if count == 0)
115         private int start;
116 
next(int i)117         private int next(int i) {
118             return (i + 1) % TEMP_BUF_POOL_SIZE;
119         }
120 
BufferCache()121         BufferCache() {
122             buffers = new ByteBuffer[TEMP_BUF_POOL_SIZE];
123         }
124 
125         /**
126          * Removes and returns a buffer from the cache of at least the given
127          * size (or null if no suitable buffer is found).
128          */
get(int size)129         ByteBuffer get(int size) {
130             // Don't call this if the buffer would be too large.
131             assert !isBufferTooLarge(size);
132 
133             if (count == 0)
134                 return null;  // cache is empty
135 
136             ByteBuffer[] buffers = this.buffers;
137 
138             // search for suitable buffer (often the first buffer will do)
139             ByteBuffer buf = buffers[start];
140             if (buf.capacity() < size) {
141                 buf = null;
142                 int i = start;
143                 while ((i = next(i)) != start) {
144                     ByteBuffer bb = buffers[i];
145                     if (bb == null)
146                         break;
147                     if (bb.capacity() >= size) {
148                         buf = bb;
149                         break;
150                     }
151                 }
152                 if (buf == null)
153                     return null;
154                 // move first element to here to avoid re-packing
155                 buffers[i] = buffers[start];
156             }
157 
158             // remove first element
159             buffers[start] = null;
160             start = next(start);
161             count--;
162 
163             // prepare the buffer and return it
164             buf.rewind();
165             buf.limit(size);
166             return buf;
167         }
168 
offerFirst(ByteBuffer buf)169         boolean offerFirst(ByteBuffer buf) {
170             // Don't call this if the buffer is too large.
171             assert !isBufferTooLarge(buf);
172 
173             if (count >= TEMP_BUF_POOL_SIZE) {
174                 return false;
175             } else {
176                 start = (start + TEMP_BUF_POOL_SIZE - 1) % TEMP_BUF_POOL_SIZE;
177                 buffers[start] = buf;
178                 count++;
179                 return true;
180             }
181         }
182 
offerLast(ByteBuffer buf)183         boolean offerLast(ByteBuffer buf) {
184             // Don't call this if the buffer is too large.
185             assert !isBufferTooLarge(buf);
186 
187             if (count >= TEMP_BUF_POOL_SIZE) {
188                 return false;
189             } else {
190                 int next = (start + count) % TEMP_BUF_POOL_SIZE;
191                 buffers[next] = buf;
192                 count++;
193                 return true;
194             }
195         }
196 
isEmpty()197         boolean isEmpty() {
198             return count == 0;
199         }
200 
removeFirst()201         ByteBuffer removeFirst() {
202             assert count > 0;
203             ByteBuffer buf = buffers[start];
204             buffers[start] = null;
205             start = next(start);
206             count--;
207             return buf;
208         }
209     }
210 
211     /**
212      * Returns a temporary buffer of at least the given size
213      */
getTemporaryDirectBuffer(int size)214     public static ByteBuffer getTemporaryDirectBuffer(int size) {
215         // If a buffer of this size is too large for the cache, there
216         // should not be a buffer in the cache that is at least as
217         // large. So we'll just create a new one. Also, we don't have
218         // to remove the buffer from the cache (as this method does
219         // below) given that we won't put the new buffer in the cache.
220         if (isBufferTooLarge(size)) {
221             return ByteBuffer.allocateDirect(size);
222         }
223 
224         BufferCache cache = bufferCache.get();
225         ByteBuffer buf = cache.get(size);
226         if (buf != null) {
227             return buf;
228         } else {
229             // No suitable buffer in the cache so we need to allocate a new
230             // one. To avoid the cache growing then we remove the first
231             // buffer from the cache and free it.
232             if (!cache.isEmpty()) {
233                 buf = cache.removeFirst();
234                 free(buf);
235             }
236             return ByteBuffer.allocateDirect(size);
237         }
238     }
239 
240     /**
241      * Releases a temporary buffer by returning to the cache or freeing it.
242      */
releaseTemporaryDirectBuffer(ByteBuffer buf)243     public static void releaseTemporaryDirectBuffer(ByteBuffer buf) {
244         offerFirstTemporaryDirectBuffer(buf);
245     }
246 
247     /**
248      * Releases a temporary buffer by returning to the cache or freeing it. If
249      * returning to the cache then insert it at the start so that it is
250      * likely to be returned by a subsequent call to getTemporaryDirectBuffer.
251      */
offerFirstTemporaryDirectBuffer(ByteBuffer buf)252     static void offerFirstTemporaryDirectBuffer(ByteBuffer buf) {
253         // If the buffer is too large for the cache we don't have to
254         // check the cache. We'll just free it.
255         if (isBufferTooLarge(buf)) {
256             free(buf);
257             return;
258         }
259 
260         assert buf != null;
261         BufferCache cache = bufferCache.get();
262         if (!cache.offerFirst(buf)) {
263             // cache is full
264             free(buf);
265         }
266     }
267 
268     /**
269      * Releases a temporary buffer by returning to the cache or freeing it. If
270      * returning to the cache then insert it at the end. This makes it
271      * suitable for scatter/gather operations where the buffers are returned to
272      * cache in same order that they were obtained.
273      */
offerLastTemporaryDirectBuffer(ByteBuffer buf)274     static void offerLastTemporaryDirectBuffer(ByteBuffer buf) {
275         // If the buffer is too large for the cache we don't have to
276         // check the cache. We'll just free it.
277         if (isBufferTooLarge(buf)) {
278             free(buf);
279             return;
280         }
281 
282         assert buf != null;
283         BufferCache cache = bufferCache.get();
284         if (!cache.offerLast(buf)) {
285             // cache is full
286             free(buf);
287         }
288     }
289 
290     /**
291      * Frees the memory for the given direct buffer
292      */
free(ByteBuffer buf)293     private static void free(ByteBuffer buf) {
294         // Android-changed: Add null check for cleaner. http://b/26040655
295         // ((DirectBuffer)buf).cleaner().clean();
296         Cleaner cleaner = ((DirectBuffer)buf).cleaner();
297         if (cleaner != null) {
298             cleaner.clean();
299         }
300     }
301 
302 
303     // -- Random stuff --
304 
subsequence(ByteBuffer[] bs, int offset, int length)305     static ByteBuffer[] subsequence(ByteBuffer[] bs, int offset, int length) {
306         if ((offset == 0) && (length == bs.length))
307             return bs;
308         int n = length;
309         ByteBuffer[] bs2 = new ByteBuffer[n];
310         for (int i = 0; i < n; i++)
311             bs2[i] = bs[offset + i];
312         return bs2;
313     }
314 
ungrowableSet(final Set<E> s)315     static <E> Set<E> ungrowableSet(final Set<E> s) {
316         return new Set<E>() {
317 
318                 public int size()                 { return s.size(); }
319                 public boolean isEmpty()          { return s.isEmpty(); }
320                 public boolean contains(Object o) { return s.contains(o); }
321                 public Object[] toArray()         { return s.toArray(); }
322                 public <T> T[] toArray(T[] a)     { return s.toArray(a); }
323                 public String toString()          { return s.toString(); }
324                 public Iterator<E> iterator()     { return s.iterator(); }
325                 public boolean equals(Object o)   { return s.equals(o); }
326                 public int hashCode()             { return s.hashCode(); }
327                 public void clear()               { s.clear(); }
328                 public boolean remove(Object o)   { return s.remove(o); }
329 
330                 public boolean containsAll(Collection<?> coll) {
331                     return s.containsAll(coll);
332                 }
333                 public boolean removeAll(Collection<?> coll) {
334                     return s.removeAll(coll);
335                 }
336                 public boolean retainAll(Collection<?> coll) {
337                     return s.retainAll(coll);
338                 }
339 
340                 public boolean add(E o){
341                     throw new UnsupportedOperationException();
342                 }
343                 public boolean addAll(Collection<? extends E> coll) {
344                     throw new UnsupportedOperationException();
345                 }
346 
347         };
348     }
349 
350 
351     // -- Unsafe access --
352 
353     private static Unsafe unsafe = Unsafe.getUnsafe();
354 
355     private static byte _get(long a) {
356         return unsafe.getByte(a);
357     }
358 
359     private static void _put(long a, byte b) {
360         unsafe.putByte(a, b);
361     }
362 
363     static void erase(ByteBuffer bb) {
364         unsafe.setMemory(((DirectBuffer)bb).address(), bb.capacity(), (byte)0);
365     }
366 
367     static Unsafe unsafe() {
368         return unsafe;
369     }
370 
371     // BEGIN Android-removed: DirectByteBuffer is @hide public on Android so reflection unneeded.
372     /*
373     private static int pageSize = -1;
374 
375     static int pageSize() {
376         if (pageSize == -1)
377             pageSize = unsafe().pageSize();
378         return pageSize;
379     }
380 
381     private static volatile Constructor<?> directByteBufferConstructor = null;
382 
383     private static void initDBBConstructor() {
384         AccessController.doPrivileged(new PrivilegedAction<Void>() {
385                 public Void run() {
386                     try {
387                         Class<?> cl = Class.forName("java.nio.DirectByteBuffer");
388                         Constructor<?> ctor = cl.getDeclaredConstructor(
389                             new Class<?>[] { int.class,
390                                              long.class,
391                                              FileDescriptor.class,
392                                              Runnable.class });
393                         ctor.setAccessible(true);
394                         directByteBufferConstructor = ctor;
395                     } catch (ClassNotFoundException   |
396                              NoSuchMethodException    |
397                              IllegalArgumentException |
398                              ClassCastException x) {
399                         throw new InternalError(x);
400                     }
401                     return null;
402                 }});
403     }
404 
405     static MappedByteBuffer newMappedByteBuffer(int size, long addr,
406                                                 FileDescriptor fd,
407                                                 Runnable unmapper)
408     {
409         MappedByteBuffer dbb;
410         if (directByteBufferConstructor == null)
411             initDBBConstructor();
412         try {
413             dbb = (MappedByteBuffer)directByteBufferConstructor.newInstance(
414               new Object[] { new Integer(size),
415                              new Long(addr),
416                              fd,
417                              unmapper });
418         } catch (InstantiationException |
419                  IllegalAccessException |
420                  InvocationTargetException e) {
421             throw new InternalError(e);
422         }
423         return dbb;
424     }
425 
426     private static volatile Constructor<?> directByteBufferRConstructor = null;
427 
428     private static void initDBBRConstructor() {
429         AccessController.doPrivileged(new PrivilegedAction<Void>() {
430                 public Void run() {
431                     try {
432                         Class<?> cl = Class.forName("java.nio.DirectByteBufferR");
433                         Constructor<?> ctor = cl.getDeclaredConstructor(
434                             new Class<?>[] { int.class,
435                                              long.class,
436                                              FileDescriptor.class,
437                                              Runnable.class });
438                         ctor.setAccessible(true);
439                         directByteBufferRConstructor = ctor;
440                     } catch (ClassNotFoundException |
441                              NoSuchMethodException |
442                              IllegalArgumentException |
443                              ClassCastException x) {
444                         throw new InternalError(x);
445                     }
446                     return null;
447                 }});
448     }
449 
450     static MappedByteBuffer newMappedByteBufferR(int size, long addr,
451                                                  FileDescriptor fd,
452                                                  Runnable unmapper)
453     {
454         MappedByteBuffer dbb;
455         if (directByteBufferRConstructor == null)
456             initDBBRConstructor();
457         try {
458             dbb = (MappedByteBuffer)directByteBufferRConstructor.newInstance(
459               new Object[] { new Integer(size),
460                              new Long(addr),
461                              fd,
462                              unmapper });
463         } catch (InstantiationException |
464                  IllegalAccessException |
465                  InvocationTargetException e) {
466             throw new InternalError(e);
467         }
468         return dbb;
469     }
470     */
471     // END Android-removed: DirectByteBuffer is @hide public on Android so reflection unneeded.
472 
473     // -- Bug compatibility --
474 
475     private static volatile String bugLevel = null;
476 
477     static boolean atBugLevel(String bl) {              // package-private
478         if (bugLevel == null) {
479             if (!sun.misc.VM.isBooted())
480                 return false;
481             String value = AccessController.doPrivileged(
482                 new GetPropertyAction("sun.nio.ch.bugLevel"));
483             bugLevel = (value != null) ? value : "";
484         }
485         return bugLevel.equals(bl);
486     }
487 
488 }
489