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