1 /*
   2  *  Copyright (c) 2019, 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 
  27 package jdk.internal.foreign;
  28 
  29 import jdk.incubator.foreign.MemoryAddress;
  30 import jdk.incubator.foreign.MemorySegment;
  31 import jdk.internal.access.JavaNioAccess;
  32 import jdk.internal.access.SharedSecrets;
  33 import jdk.internal.access.foreign.MemorySegmentProxy;
  34 import jdk.internal.misc.Unsafe;
  35 
  36 import java.nio.ByteBuffer;
  37 import java.util.Objects;
  38 import java.util.Random;
  39 
  40 /**
  41  * This class provides an immutable implementation for the {@code MemorySegment} interface. This class contains information
  42  * about the segment's spatial and temporal bounds, as well as the addressing coordinates (base + offset) which allows
  43  * unsafe access; each memory segment implementation is associated with an owner thread which is set at creation time.
  44  * Access to certain sensitive operations on the memory segment will fail with {@code IllegalStateException} if the
  45  * segment is either in an invalid state (e.g. it has already been closed) or if access occurs from a thread other
  46  * than the owner thread. See {@link MemoryScope} for more details on management of temporal bounds.
  47  */
  48 public final class MemorySegmentImpl implements MemorySegment, MemorySegmentProxy {
  49 
  50     private static final Unsafe UNSAFE = Unsafe.getUnsafe();
  51     private static final int BYTE_ARR_BASE = UNSAFE.arrayBaseOffset(byte[].class);
  52 
  53     final long length;
  54     final int mask;
  55     final long min;
  56     final Object base;
  57     final Thread owner;
  58     final MemoryScope scope;
  59 
  60     final static int READ_ONLY = 1;
  61     final static long NONCE = new Random().nextLong();
  62 
  63     public MemorySegmentImpl(long min, Object base, long length, int mask, Thread owner, MemoryScope scope) {
  64         this.length = length;
  65         this.mask = mask;
  66         this.min = min;
  67         this.base = base;
  68         this.owner = owner;
  69         this.scope = scope;
  70     }
  71 
  72     // MemorySegment methods
  73 
  74     @Override
  75     public final MemorySegmentImpl asSlice(long offset, long newSize) {
  76         checkValidState();
  77         checkBounds(offset, newSize);
  78         return new MemorySegmentImpl(min + offset, base, newSize, mask, owner, scope);
  79     }
  80 
  81     @Override
  82     public MemorySegment acquire() {
  83         scope.checkAlive();
  84         return new MemorySegmentImpl(min, base, length, mask, Thread.currentThread(), scope.acquire());
  85     }
  86 
  87     @Override
  88     public final MemoryAddress baseAddress() {
  89         return new MemoryAddressImpl(this, 0);
  90     }
  91 
  92     @Override
  93     public final long byteSize() {
  94         return length;
  95     }
  96 
  97     @Override
  98     public final MemorySegmentImpl asReadOnly() {
  99         checkValidState();
 100         return new MemorySegmentImpl(min, base, length, mask | READ_ONLY, owner, scope);
 101     }
 102 
 103     @Override
 104     public final boolean isAlive() {
 105         return scope.isAlive();
 106     }
 107 
 108     @Override
 109     public final boolean isReadOnly() {
 110         return isSet(READ_ONLY);
 111     }
 112 
 113     @Override
 114     public boolean isAccessible() {
 115         return owner == Thread.currentThread();
 116     }
 117 
 118     @Override
 119     public final void close() {
 120         checkValidState();
 121         scope.close();
 122     }
 123 
 124     @Override
 125     public ByteBuffer asByteBuffer() {
 126         checkValidState();
 127         checkIntSize("ByteBuffer");
 128         JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess();
 129         ByteBuffer _bb;
 130         if (base() != null) {
 131             if (!(base() instanceof byte[])) {
 132                 throw new UnsupportedOperationException("Not an address to an heap-allocated byte array");
 133             }
 134             _bb = nioAccess.newHeapByteBuffer((byte[]) base(), (int)min - BYTE_ARR_BASE, (int) length, this);
 135         } else {
 136             _bb = nioAccess.newDirectByteBuffer(min, (int) length, null, this);
 137         }
 138         if (isReadOnly()) {
 139             //scope is IMMUTABLE - obtain a RO byte buffer
 140             _bb = _bb.asReadOnlyBuffer();
 141         }
 142         return _bb;
 143     }
 144 
 145     @Override
 146     public byte[] toByteArray() {
 147         checkValidState();
 148         checkIntSize("byte[]");
 149         byte[] arr = new byte[(int)length];
 150         MemorySegment arrSegment = MemorySegment.ofArray(arr);
 151         MemoryAddress.copy(this.baseAddress(), arrSegment.baseAddress(), length);
 152         return arr;
 153     }
 154 
 155     // MemorySegmentProxy methods
 156 
 157     @Override
 158     public final void checkValidState() {
 159         if (owner != Thread.currentThread()) {
 160             throw new IllegalStateException("Attempt to access segment outside owning thread");
 161         }
 162         scope.checkAlive();
 163     }
 164 
 165     // Object methods
 166 
 167     @Override
 168     public String toString() {
 169         return "MemorySegment{ id=0x" + Long.toHexString(id()) + " limit: " + byteSize() + " }";
 170     }
 171 
 172     // Helper methods
 173 
 174     void checkRange(long offset, long length, boolean writeAccess) {
 175         checkValidState();
 176         if (isReadOnly() && writeAccess) {
 177             throw new UnsupportedOperationException("Cannot write to read-only memory segment");
 178         }
 179         checkBounds(offset, length);
 180     }
 181 
 182     Object base() {
 183         return base;
 184     }
 185 
 186     private boolean isSet(int mask) {
 187         return (this.mask & mask) != 0;
 188     }
 189 
 190     private void checkIntSize(String typeName) {
 191         if (length > (Integer.MAX_VALUE - 8)) { //conservative check
 192             throw new UnsupportedOperationException(String.format("Segment is too large to wrap as %s. Size: %d", typeName, length));
 193         }
 194     }
 195 
 196     private void checkBounds(long offset, long length) {
 197         if (length < 0 ||
 198                 offset < 0 ||
 199                 offset > this.length - length) { // careful of overflow
 200             throw new IndexOutOfBoundsException(String.format("Out of bound access on segment %s; new offset = %d; new length = %d",
 201                 this, offset, length));
 202         }
 203     }
 204 
 205     private int id() {
 206         //compute a stable and random id for this memory segment
 207         return Math.abs(Objects.hash(base, min, NONCE));
 208     }
 209 
 210 }