1 /*
   2  * Copyright (c) 2016, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package jdk.internal.foreign;
  24 
  25 import jdk.internal.misc.Unsafe;
  26 import jdk.internal.vm.annotation.ForceInline;
  27 
  28 import java.nio.Buffer;
  29 import java.nio.ByteBuffer;
  30 
  31 public class MemoryBoundInfo {
  32 
  33     public static final MemoryBoundInfo EVERYTHING = new MemoryBoundInfo(null, 0, Long.MAX_VALUE) {
  34         @Override
  35         void checkRange(long offset, long length) {
  36             checkOverflow(offset, length);
  37         }
  38     };
  39 
  40     public static final MemoryBoundInfo NOTHING = ofNative(0, 0);
  41 
  42     public static final Unsafe UNSAFE = Unsafe.getUnsafe();
  43 
  44     public static final long BYTE_BUFFER_BASE;
  45     public static final long BUFFER_ADDRESS;
  46 
  47     static {
  48         try {
  49             BYTE_BUFFER_BASE = UNSAFE.objectFieldOffset(ByteBuffer.class.getDeclaredField("hb"));
  50             BUFFER_ADDRESS = UNSAFE.objectFieldOffset(Buffer.class.getDeclaredField("address"));
  51         }
  52         catch (Exception e) {
  53             throw new InternalError(e);
  54         }
  55     }
  56 
  57     public final Object base;
  58     public final long min;
  59     final long length;
  60 
  61     private MemoryBoundInfo(Object base, long min, long length) {
  62         if(length < 0) {
  63             throw new IllegalArgumentException("length must be positive");
  64         }
  65         if(base != null && min < 0) {
  66             throw new IllegalArgumentException("min must be positive if base is used");
  67         }
  68         checkOverflow(min, length);
  69         this.base = base;
  70         this.min = min;
  71         this.length = length;
  72     }
  73 
  74     public static MemoryBoundInfo ofNative(long min, long length) {
  75         return new MemoryBoundInfo(null, min, length);
  76     }
  77 
  78     public static MemoryBoundInfo ofHeap(Object base, long min, long length) {
  79         checkOverflow(min, length);
  80         return new MemoryBoundInfo(base, min, length);
  81     }
  82 
  83     private static void checkOverflow(long min, long length) {
  84         // we never access at `length`
  85         addUnsignedExact(min, length == 0 ? 0 : length - 1);
  86     }
  87 
  88     public static MemoryBoundInfo ofByteBuffer(ByteBuffer bb) {
  89         // For a direct ByteBuffer base == null and address is absolute
  90         Object base = getBufferBase(bb);
  91         long address = getBufferAddress(bb);
  92 
  93         int pos = bb.position();
  94         int limit = bb.limit();
  95         return new MemoryBoundInfo(base, address + pos, limit - pos) {
  96             // Keep a reference to the buffer so it is kept alive while the
  97             // region is alive
  98             final Object ref = bb;
  99 
 100             // @@@ For heap ByteBuffer the addr() will throw an exception
 101             //     need to adapt a pointer and memory region be more cognizant
 102             //     of the double addressing mode
 103             //     the direct address for a heap buffer needs to behave
 104             //     differently see JNI GetPrimitiveArrayCritical for clues on
 105             //     behaviour.
 106 
 107             // @@@ Same trick can be performed to create a pointer to a
 108             //     primitive array
 109             @Override
 110             MemoryBoundInfo limit(long offset, long newLength) {
 111                 throw new UnsupportedOperationException(); // bb ref would be lost otherwise
 112             }
 113         };
 114     }
 115 
 116     void checkRange(long offset, long length) {
 117         // FIXME check for negative length?
 118         if (offset < 0 || offset > this.length - length) { // careful of overflow
 119             throw new IllegalStateException("offset: " + offset + ", region length: " + this.length);
 120         }
 121     }
 122 
 123     @ForceInline
 124     MemoryBoundInfo limit(long offset, long newLength) {
 125         if (newLength > length || newLength < 0) {
 126             throw new IllegalArgumentException();
 127         }
 128         return new MemoryBoundInfo(base, min + offset, newLength);
 129     }
 130 
 131      static long addUnsignedExact(long a, long b) {
 132         long result = a + b;
 133         if(Long.compareUnsigned(result, a) < 0) {
 134             throw new ArithmeticException(
 135                 "Unsigned overflow: "
 136                     + Long.toUnsignedString(a) + " + "
 137                     + Long.toUnsignedString(b));
 138         }
 139 
 140         return result;
 141     }
 142 
 143     @Override
 144     public String toString() {
 145         return base != null ?
 146                 "HeapRegion{base=" + base + ", length=" + length + "}" :
 147                 "NativeRegion{min=" + min + ", length=" + length + "}";
 148     }
 149 
 150     static Object getBufferBase(ByteBuffer bb) {
 151         return UNSAFE.getReference(bb, BYTE_BUFFER_BASE);
 152     }
 153 
 154     static long getBufferAddress(ByteBuffer bb) {
 155         return UNSAFE.getLong(bb, BUFFER_ADDRESS);
 156     }
 157 }