|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  */ | 
|  |  | 
|  | package sun.nio.ch; | 
|  |  | 
|  | import java.lang.ref.SoftReference; | 
|  | import java.lang.reflect.*; | 
|  | import java.io.IOException; | 
|  | import java.io.FileDescriptor; | 
|  | import java.nio.ByteBuffer; | 
|  | import java.nio.MappedByteBuffer; | 
|  | import java.nio.channels.*; | 
|  | import java.security.AccessController; | 
|  | import java.security.PrivilegedAction; | 
|  | import java.util.*; | 
|  | import sun.misc.Unsafe; | 
|  | import sun.misc.Cleaner; | 
|  | import sun.security.action.GetPropertyAction; | 
|  |  | 
|  |  | 
|  | public class Util { | 
|  |  | 
|  |     // -- Caches -- | 
|  |  | 
|  |      | 
|  |     private static final int TEMP_BUF_POOL_SIZE = IOUtil.IOV_MAX; | 
|  |  | 
|  |      | 
|  |     private static final long MAX_CACHED_BUFFER_SIZE = getMaxCachedBufferSize(); | 
|  |  | 
|  |      | 
|  |     private static ThreadLocal<BufferCache> bufferCache = | 
|  |         new ThreadLocal<BufferCache>() | 
|  |     { | 
|  |         @Override | 
|  |         protected BufferCache initialValue() { | 
|  |             return new BufferCache(); | 
|  |         } | 
|  |     }; | 
|  |  | 
|  |      | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |      */ | 
|  |     private static long getMaxCachedBufferSize() { | 
|  |         String s = java.security.AccessController.doPrivileged( | 
|  |             new PrivilegedAction<String>() { | 
|  |                 @Override | 
|  |                 public String run() { | 
|  |                     return System.getProperty("jdk.nio.maxCachedBufferSize"); | 
|  |                 } | 
|  |             }); | 
|  |         if (s != null) { | 
|  |             try { | 
|  |                 long m = Long.parseLong(s); | 
|  |                 if (m >= 0) { | 
|  |                     return m; | 
|  |                 } else { | 
|  |                     // if it's negative, ignore the system property | 
|  |                 } | 
|  |             } catch (NumberFormatException e) { | 
|  |                 // if the string is not well formed, ignore the system property | 
|  |             } | 
|  |         } | 
|  |         return Long.MAX_VALUE; | 
|  |     } | 
|  |  | 
|  |      | 
|  |  | 
|  |  | 
|  |      */ | 
|  |     private static boolean isBufferTooLarge(int size) { | 
|  |         return size > MAX_CACHED_BUFFER_SIZE; | 
|  |     } | 
|  |  | 
|  |      | 
|  |  | 
|  |  | 
|  |      */ | 
|  |     private static boolean isBufferTooLarge(ByteBuffer buf) { | 
|  |         return isBufferTooLarge(buf.capacity()); | 
|  |     } | 
|  |  | 
|  |      | 
|  |  | 
|  |      */ | 
|  |     private static class BufferCache { | 
|  |          | 
|  |         private ByteBuffer[] buffers; | 
|  |  | 
|  |          | 
|  |         private int count; | 
|  |  | 
|  |          | 
|  |         private int start; | 
|  |  | 
|  |         private int next(int i) { | 
|  |             return (i + 1) % TEMP_BUF_POOL_SIZE; | 
|  |         } | 
|  |  | 
|  |         BufferCache() { | 
|  |             buffers = new ByteBuffer[TEMP_BUF_POOL_SIZE]; | 
|  |         } | 
|  |  | 
|  |          | 
|  |  | 
|  |  | 
|  |          */ | 
|  |         ByteBuffer get(int size) { | 
|  |              | 
|  |             assert !isBufferTooLarge(size); | 
|  |  | 
|  |             if (count == 0) | 
|  |                 return null;   | 
|  |  | 
|  |             ByteBuffer[] buffers = this.buffers; | 
|  |  | 
|  |              | 
|  |             ByteBuffer buf = buffers[start]; | 
|  |             if (buf.capacity() < size) { | 
|  |                 buf = null; | 
|  |                 int i = start; | 
|  |                 while ((i = next(i)) != start) { | 
|  |                     ByteBuffer bb = buffers[i]; | 
|  |                     if (bb == null) | 
|  |                         break; | 
|  |                     if (bb.capacity() >= size) { | 
|  |                         buf = bb; | 
|  |                         break; | 
|  |                     } | 
|  |                 } | 
|  |                 if (buf == null) | 
|  |                     return null; | 
|  |                  | 
|  |                 buffers[i] = buffers[start]; | 
|  |             } | 
|  |  | 
|  |              | 
|  |             buffers[start] = null; | 
|  |             start = next(start); | 
|  |             count--; | 
|  |  | 
|  |              | 
|  |             buf.rewind(); | 
|  |             buf.limit(size); | 
|  |             return buf; | 
|  |         } | 
|  |  | 
|  |         boolean offerFirst(ByteBuffer buf) { | 
|  |              | 
|  |             assert !isBufferTooLarge(buf); | 
|  |  | 
|  |             if (count >= TEMP_BUF_POOL_SIZE) { | 
|  |                 return false; | 
|  |             } else { | 
|  |                 start = (start + TEMP_BUF_POOL_SIZE - 1) % TEMP_BUF_POOL_SIZE; | 
|  |                 buffers[start] = buf; | 
|  |                 count++; | 
|  |                 return true; | 
|  |             } | 
|  |         } | 
|  |  | 
|  |         boolean offerLast(ByteBuffer buf) { | 
|  |              | 
|  |             assert !isBufferTooLarge(buf); | 
|  |  | 
|  |             if (count >= TEMP_BUF_POOL_SIZE) { | 
|  |                 return false; | 
|  |             } else { | 
|  |                 int next = (start + count) % TEMP_BUF_POOL_SIZE; | 
|  |                 buffers[next] = buf; | 
|  |                 count++; | 
|  |                 return true; | 
|  |             } | 
|  |         } | 
|  |  | 
|  |         boolean isEmpty() { | 
|  |             return count == 0; | 
|  |         } | 
|  |  | 
|  |         ByteBuffer removeFirst() { | 
|  |             assert count > 0; | 
|  |             ByteBuffer buf = buffers[start]; | 
|  |             buffers[start] = null; | 
|  |             start = next(start); | 
|  |             count--; | 
|  |             return buf; | 
|  |         } | 
|  |     } | 
|  |  | 
|  |      | 
|  |  | 
|  |      */ | 
|  |     public static ByteBuffer getTemporaryDirectBuffer(int size) { | 
|  |         // If a buffer of this size is too large for the cache, there | 
|  |         // should not be a buffer in the cache that is at least as | 
|  |         // large. So we'll just create a new one. Also, we don't have | 
|  |         // to remove the buffer from the cache (as this method does | 
|  |          | 
|  |         if (isBufferTooLarge(size)) { | 
|  |             return ByteBuffer.allocateDirect(size); | 
|  |         } | 
|  |  | 
|  |         BufferCache cache = bufferCache.get(); | 
|  |         ByteBuffer buf = cache.get(size); | 
|  |         if (buf != null) { | 
|  |             return buf; | 
|  |         } else { | 
|  |             // No suitable buffer in the cache so we need to allocate a new | 
|  |             // one. To avoid the cache growing then we remove the first | 
|  |              | 
|  |             if (!cache.isEmpty()) { | 
|  |                 buf = cache.removeFirst(); | 
|  |                 free(buf); | 
|  |             } | 
|  |             return ByteBuffer.allocateDirect(size); | 
|  |         } | 
|  |     } | 
|  |  | 
|  |      | 
|  |  | 
|  |      */ | 
|  |     public static void releaseTemporaryDirectBuffer(ByteBuffer buf) { | 
|  |         offerFirstTemporaryDirectBuffer(buf); | 
|  |     } | 
|  |  | 
|  |      | 
|  |  | 
|  |  | 
|  |  | 
|  |      */ | 
|  |     static void offerFirstTemporaryDirectBuffer(ByteBuffer buf) { | 
|  |         // If the buffer is too large for the cache we don't have to | 
|  |          | 
|  |         if (isBufferTooLarge(buf)) { | 
|  |             free(buf); | 
|  |             return; | 
|  |         } | 
|  |  | 
|  |         assert buf != null; | 
|  |         BufferCache cache = bufferCache.get(); | 
|  |         if (!cache.offerFirst(buf)) { | 
|  |              | 
|  |             free(buf); | 
|  |         } | 
|  |     } | 
|  |  | 
|  |      | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |      */ | 
|  |     static void offerLastTemporaryDirectBuffer(ByteBuffer buf) { | 
|  |         // If the buffer is too large for the cache we don't have to | 
|  |          | 
|  |         if (isBufferTooLarge(buf)) { | 
|  |             free(buf); | 
|  |             return; | 
|  |         } | 
|  |  | 
|  |         assert buf != null; | 
|  |         BufferCache cache = bufferCache.get(); | 
|  |         if (!cache.offerLast(buf)) { | 
|  |              | 
|  |             free(buf); | 
|  |         } | 
|  |     } | 
|  |  | 
|  |      | 
|  |  | 
|  |      */ | 
|  |     private static void free(ByteBuffer buf) { | 
|  |         ((DirectBuffer)buf).cleaner().clean(); | 
|  |     } | 
|  |  | 
|  |  | 
|  |     // -- Random stuff -- | 
|  |  | 
|  |     static ByteBuffer[] subsequence(ByteBuffer[] bs, int offset, int length) { | 
|  |         if ((offset == 0) && (length == bs.length)) | 
|  |             return bs; | 
|  |         int n = length; | 
|  |         ByteBuffer[] bs2 = new ByteBuffer[n]; | 
|  |         for (int i = 0; i < n; i++) | 
|  |             bs2[i] = bs[offset + i]; | 
|  |         return bs2; | 
|  |     } | 
|  |  | 
|  |     static <E> Set<E> ungrowableSet(final Set<E> s) { | 
|  |         return new Set<E>() { | 
|  |  | 
|  |                 public int size()                 { return s.size(); } | 
|  |                 public boolean isEmpty()          { return s.isEmpty(); } | 
|  |                 public boolean contains(Object o) { return s.contains(o); } | 
|  |                 public Object[] toArray()         { return s.toArray(); } | 
|  |                 public <T> T[] toArray(T[] a)     { return s.toArray(a); } | 
|  |                 public String toString()          { return s.toString(); } | 
|  |                 public Iterator<E> iterator()     { return s.iterator(); } | 
|  |                 public boolean equals(Object o)   { return s.equals(o); } | 
|  |                 public int hashCode()             { return s.hashCode(); } | 
|  |                 public void clear()               { s.clear(); } | 
|  |                 public boolean remove(Object o)   { return s.remove(o); } | 
|  |  | 
|  |                 public boolean containsAll(Collection<?> coll) { | 
|  |                     return s.containsAll(coll); | 
|  |                 } | 
|  |                 public boolean removeAll(Collection<?> coll) { | 
|  |                     return s.removeAll(coll); | 
|  |                 } | 
|  |                 public boolean retainAll(Collection<?> coll) { | 
|  |                     return s.retainAll(coll); | 
|  |                 } | 
|  |  | 
|  |                 public boolean add(E o){ | 
|  |                     throw new UnsupportedOperationException(); | 
|  |                 } | 
|  |                 public boolean addAll(Collection<? extends E> coll) { | 
|  |                     throw new UnsupportedOperationException(); | 
|  |                 } | 
|  |  | 
|  |         }; | 
|  |     } | 
|  |  | 
|  |  | 
|  |     // -- Unsafe access -- | 
|  |  | 
|  |     private static Unsafe unsafe = Unsafe.getUnsafe(); | 
|  |  | 
|  |     private static byte _get(long a) { | 
|  |         return unsafe.getByte(a); | 
|  |     } | 
|  |  | 
|  |     private static void _put(long a, byte b) { | 
|  |         unsafe.putByte(a, b); | 
|  |     } | 
|  |  | 
|  |     static void erase(ByteBuffer bb) { | 
|  |         unsafe.setMemory(((DirectBuffer)bb).address(), bb.capacity(), (byte)0); | 
|  |     } | 
|  |  | 
|  |     static Unsafe unsafe() { | 
|  |         return unsafe; | 
|  |     } | 
|  |  | 
|  |     private static int pageSize = -1; | 
|  |  | 
|  |     static int pageSize() { | 
|  |         if (pageSize == -1) | 
|  |             pageSize = unsafe().pageSize(); | 
|  |         return pageSize; | 
|  |     } | 
|  |  | 
|  |     private static volatile Constructor<?> directByteBufferConstructor = null; | 
|  |  | 
|  |     private static void initDBBConstructor() { | 
|  |         AccessController.doPrivileged(new PrivilegedAction<Void>() { | 
|  |                 public Void run() { | 
|  |                     try { | 
|  |                         Class<?> cl = Class.forName("java.nio.DirectByteBuffer"); | 
|  |                         Constructor<?> ctor = cl.getDeclaredConstructor( | 
|  |                             new Class<?>[] { int.class, | 
|  |                                              long.class, | 
|  |                                              FileDescriptor.class, | 
|  |                                              Runnable.class }); | 
|  |                         ctor.setAccessible(true); | 
|  |                         directByteBufferConstructor = ctor; | 
|  |                     } catch (ClassNotFoundException   | | 
|  |                              NoSuchMethodException    | | 
|  |                              IllegalArgumentException | | 
|  |                              ClassCastException x) { | 
|  |                         throw new InternalError(x); | 
|  |                     } | 
|  |                     return null; | 
|  |                 }}); | 
|  |     } | 
|  |  | 
|  |     static MappedByteBuffer newMappedByteBuffer(int size, long addr, | 
|  |                                                 FileDescriptor fd, | 
|  |                                                 Runnable unmapper) | 
|  |     { | 
|  |         MappedByteBuffer dbb; | 
|  |         if (directByteBufferConstructor == null) | 
|  |             initDBBConstructor(); | 
|  |         try { | 
|  |             dbb = (MappedByteBuffer)directByteBufferConstructor.newInstance( | 
|  |               new Object[] { new Integer(size), | 
|  |                              new Long(addr), | 
|  |                              fd, | 
|  |                              unmapper }); | 
|  |         } catch (InstantiationException | | 
|  |                  IllegalAccessException | | 
|  |                  InvocationTargetException e) { | 
|  |             throw new InternalError(e); | 
|  |         } | 
|  |         return dbb; | 
|  |     } | 
|  |  | 
|  |     private static volatile Constructor<?> directByteBufferRConstructor = null; | 
|  |  | 
|  |     private static void initDBBRConstructor() { | 
|  |         AccessController.doPrivileged(new PrivilegedAction<Void>() { | 
|  |                 public Void run() { | 
|  |                     try { | 
|  |                         Class<?> cl = Class.forName("java.nio.DirectByteBufferR"); | 
|  |                         Constructor<?> ctor = cl.getDeclaredConstructor( | 
|  |                             new Class<?>[] { int.class, | 
|  |                                              long.class, | 
|  |                                              FileDescriptor.class, | 
|  |                                              Runnable.class }); | 
|  |                         ctor.setAccessible(true); | 
|  |                         directByteBufferRConstructor = ctor; | 
|  |                     } catch (ClassNotFoundException | | 
|  |                              NoSuchMethodException | | 
|  |                              IllegalArgumentException | | 
|  |                              ClassCastException x) { | 
|  |                         throw new InternalError(x); | 
|  |                     } | 
|  |                     return null; | 
|  |                 }}); | 
|  |     } | 
|  |  | 
|  |     static MappedByteBuffer newMappedByteBufferR(int size, long addr, | 
|  |                                                  FileDescriptor fd, | 
|  |                                                  Runnable unmapper) | 
|  |     { | 
|  |         MappedByteBuffer dbb; | 
|  |         if (directByteBufferRConstructor == null) | 
|  |             initDBBRConstructor(); | 
|  |         try { | 
|  |             dbb = (MappedByteBuffer)directByteBufferRConstructor.newInstance( | 
|  |               new Object[] { new Integer(size), | 
|  |                              new Long(addr), | 
|  |                              fd, | 
|  |                              unmapper }); | 
|  |         } catch (InstantiationException | | 
|  |                  IllegalAccessException | | 
|  |                  InvocationTargetException e) { | 
|  |             throw new InternalError(e); | 
|  |         } | 
|  |         return dbb; | 
|  |     } | 
|  |  | 
|  |  | 
|  |     // -- Bug compatibility -- | 
|  |  | 
|  |     private static volatile String bugLevel = null; | 
|  |  | 
|  |     static boolean atBugLevel(String bl) {               | 
|  |         if (bugLevel == null) { | 
|  |             if (!sun.misc.VM.isBooted()) | 
|  |                 return false; | 
|  |             String value = AccessController.doPrivileged( | 
|  |                 new GetPropertyAction("sun.nio.ch.bugLevel")); | 
|  |             bugLevel = (value != null) ? value : ""; | 
|  |         } | 
|  |         return bugLevel.equals(bl); | 
|  |     } | 
|  |  | 
|  | } |