1 /*
  2  * Copyright (c) 2020, 2021, 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 jdk.internal.foreign;
 27 
 28 import jdk.incubator.foreign.*;
 29 import jdk.internal.access.JavaNioAccess;
 30 import jdk.internal.access.SharedSecrets;
 31 import jdk.internal.access.foreign.MemorySegmentProxy;
 32 import jdk.internal.access.foreign.UnmapperProxy;
 33 import jdk.internal.misc.ScopedMemoryAccess;
 34 import jdk.internal.util.ArraysSupport;
 35 import jdk.internal.vm.annotation.ForceInline;
 36 import sun.security.action.GetPropertyAction;
 37 
 38 import java.nio.ByteBuffer;
 39 import java.nio.ByteOrder;
 40 import java.util.*;
 41 import java.util.function.Consumer;
 42 import java.util.function.Function;
 43 import java.util.function.IntFunction;
 44 import java.util.stream.Stream;
 45 import java.util.stream.StreamSupport;
 46 
 47 import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE;
 48 
 49 /**
 50  * This abstract class provides an immutable implementation for the {@code MemorySegment} interface. This class contains information
 51  * about the segment's spatial and temporal bounds; each memory segment implementation is associated with an owner thread which is set at creation time.
 52  * Access to certain sensitive operations on the memory segment will fail with {@code IllegalStateException} if the
 53  * segment is either in an invalid state (e.g. it has already been closed) or if access occurs from a thread other
 54  * than the owner thread. See {@link ResourceScopeImpl} for more details on management of temporal bounds. Subclasses
 55  * are defined for each memory segment kind, see {@link NativeMemorySegmentImpl}, {@link HeapMemorySegmentImpl} and
 56  * {@link MappedMemorySegmentImpl}.
 57  */
 58 public abstract non-sealed class AbstractMemorySegmentImpl extends MemorySegmentProxy implements MemorySegment, SegmentAllocator, Scoped {
 59 
 60     private static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess();
 61 
 62     private static final boolean enableSmallSegments =
 63             Boolean.parseBoolean(GetPropertyAction.privilegedGetProperty("jdk.incubator.foreign.SmallSegments", "true"));
 64 
 65     static final int READ_ONLY = 1;
 66     static final int SMALL = READ_ONLY << 1;
 67     static final long NONCE = new Random().nextLong();
 68 
 69     static final JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess();
 70 
 71     final long length;
 72     final int mask;
 73     final ResourceScopeImpl scope;
 74 
 75     @ForceInline
 76     AbstractMemorySegmentImpl(long length, int mask, ResourceScopeImpl scope) {
 77         this.length = length;
 78         this.mask = mask;
 79         this.scope = scope;
 80     }
 81 
 82     abstract long min();
 83 
 84     abstract Object base();
 85 
 86     abstract AbstractMemorySegmentImpl dup(long offset, long size, int mask, ResourceScopeImpl scope);
 87 
 88     abstract ByteBuffer makeByteBuffer();
 89 
 90     static int defaultAccessModes(long size) {
 91         return (enableSmallSegments && size < Integer.MAX_VALUE) ?
 92                 SMALL : 0;
 93     }
 94 
 95     @Override
 96     public AbstractMemorySegmentImpl asReadOnly() {
 97         return dup(0, length, mask | READ_ONLY, scope);
 98     }
 99 
100     @Override
101     public boolean isReadOnly() {
102         return isSet(READ_ONLY);
103     }
104 
105     @Override
106     public AbstractMemorySegmentImpl asSlice(long offset, long newSize) {
107         checkBounds(offset, newSize);
108         return asSliceNoCheck(offset, newSize);
109     }
110 
111     @Override
112     public AbstractMemorySegmentImpl asSlice(long offset) {
113         checkBounds(offset, 0);
114         return asSliceNoCheck(offset, length - offset);
115     }
116 
117     private AbstractMemorySegmentImpl asSliceNoCheck(long offset, long newSize) {
118         return dup(offset, newSize, mask, scope);
119     }
120 
121     @Override
122     public Spliterator<MemorySegment> spliterator(MemoryLayout elementLayout) {
123         Objects.requireNonNull(elementLayout);
124         if (elementLayout.byteSize() == 0) {
125             throw new IllegalArgumentException("Element layout size cannot be zero");
126         }
127         if (byteSize() % elementLayout.byteSize() != 0) {
128             throw new IllegalArgumentException("Segment size is no a multiple of layout size");
129         }
130         return new SegmentSplitter(elementLayout.byteSize(), byteSize() / elementLayout.byteSize(),
131                 this);
132     }
133 
134     @Override
135     public Stream<MemorySegment> elements(MemoryLayout elementLayout) {
136         return StreamSupport.stream(spliterator(elementLayout), false);
137     }
138 
139     @Override
140     public final MemorySegment fill(byte value){
141         checkAccess(0, length, false);
142         SCOPED_MEMORY_ACCESS.setMemory(scope, base(), min(), length, value);
143         return this;
144     }
145 
146     @Override
147     public MemorySegment allocate(long bytesSize, long bytesAlignment) {
148         return asSlice(0, bytesSize);
149     }
150 
151     @Override
152     public long mismatch(MemorySegment other) {
153         AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl)Objects.requireNonNull(other);
154         final long thisSize = this.byteSize();
155         final long thatSize = that.byteSize();
156         final long length = Math.min(thisSize, thatSize);
157         this.checkAccess(0, length, true);
158         that.checkAccess(0, length, true);
159         if (this == other) {
160             checkValidState();
161             return -1;
162         }
163 
164         long i = 0;
165         if (length > 7) {
166             if (get(JAVA_BYTE, 0) != that.get(JAVA_BYTE, 0)) {
167                 return 0;
168             }
169             i = vectorizedMismatchLargeForBytes(scope, that.scope,
170                     this.base(), this.min(),
171                     that.base(), that.min(),
172                     length);
173             if (i >= 0) {
174                 return i;
175             }
176             long remaining = ~i;
177             assert remaining < 8 : "remaining greater than 7: " + remaining;
178             i = length - remaining;
179         }
180         for (; i < length; i++) {
181             if (get(JAVA_BYTE, i) != that.get(JAVA_BYTE, i)) {
182                 return i;
183             }
184         }
185         return thisSize != thatSize ? length : -1;
186     }
187 
188     /**
189      * Mismatch over long lengths.
190      */
191     private static long vectorizedMismatchLargeForBytes(ResourceScopeImpl aScope, ResourceScopeImpl bScope,
192                                                         Object a, long aOffset,
193                                                         Object b, long bOffset,
194                                                         long length) {
195         long off = 0;
196         long remaining = length;
197         int i, size;
198         boolean lastSubRange = false;
199         while (remaining > 7 && !lastSubRange) {
200             if (remaining > Integer.MAX_VALUE) {
201                 size = Integer.MAX_VALUE;
202             } else {
203                 size = (int) remaining;
204                 lastSubRange = true;
205             }
206             i = SCOPED_MEMORY_ACCESS.vectorizedMismatch(aScope, bScope,
207                     a, aOffset + off,
208                     b, bOffset + off,
209                     size, ArraysSupport.LOG2_ARRAY_BYTE_INDEX_SCALE);
210             if (i >= 0)
211                 return off + i;
212 
213             i = size - ~i;
214             off += i;
215             remaining -= i;
216         }
217         return ~remaining;
218     }
219 
220     @Override
221     public MemoryAddress address() {
222         throw new UnsupportedOperationException("Cannot obtain address of on-heap segment");
223     }
224 
225     @Override
226     public final ByteBuffer asByteBuffer() {
227         checkArraySize("ByteBuffer", 1);
228         ByteBuffer _bb = makeByteBuffer();
229         if (isSet(READ_ONLY)) {
230             //scope is IMMUTABLE - obtain a RO byte buffer
231             _bb = _bb.asReadOnlyBuffer();
232         }
233         return _bb;
234     }
235 
236     @Override
237     public final long byteSize() {
238         return length;
239     }
240 
241     public final boolean isAlive() {
242         return scope.isAlive();
243     }
244 
245     public Thread ownerThread() {
246         return scope.ownerThread();
247     }
248 
249     @Override
250     public boolean isMapped() {
251         return false;
252     }
253 
254     @Override
255     public boolean isNative() {
256         return false;
257     }
258 
259     @Override
260     public final MemorySegment asOverlappingSlice(MemorySegment other) {
261         AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl)Objects.requireNonNull(other);
262         if (base() == that.base()) {  // both either native or heap
263             final long thisStart = this.min();
264             final long thatStart = that.min();
265             final long thisEnd = thisStart + this.byteSize();
266             final long thatEnd = thatStart + that.byteSize();
267 
268             if (thisStart < thatEnd && thisEnd > thatStart) {  //overlap occurs
269                 long offsetToThat = this.segmentOffset(that);
270                 long newOffset = offsetToThat >= 0 ? offsetToThat : 0;
271                 return asSlice(newOffset, Math.min(this.byteSize() - newOffset, that.byteSize() + offsetToThat));
272             }
273         }
274         return null;
275     }
276 
277     @Override
278     public final long segmentOffset(MemorySegment other) {
279         AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl) Objects.requireNonNull(other);
280         if (base() == that.base()) {
281             return that.min() - this.min();
282         }
283         throw new UnsupportedOperationException("Cannot compute offset from native to heap (or vice versa).");
284     }
285 
286     @Override
287     public void load() {
288         throw new UnsupportedOperationException("Not a mapped segment");
289     }
290 
291     @Override
292     public void unload() {
293         throw new UnsupportedOperationException("Not a mapped segment");
294     }
295 
296     @Override
297     public boolean isLoaded() {
298         throw new UnsupportedOperationException("Not a mapped segment");
299     }
300 
301     @Override
302     public void force() {
303         throw new UnsupportedOperationException("Not a mapped segment");
304     }
305 
306     @Override
307     public final byte[] toArray(ValueLayout.OfByte elementLayout) {
308         return toArray(byte[].class, elementLayout, byte[]::new, MemorySegment::ofArray);
309     }
310 
311     @Override
312     public final short[] toArray(ValueLayout.OfShort elementLayout) {
313         return toArray(short[].class, elementLayout, short[]::new, MemorySegment::ofArray);
314     }
315 
316     @Override
317     public final char[] toArray(ValueLayout.OfChar elementLayout) {
318         return toArray(char[].class, elementLayout, char[]::new, MemorySegment::ofArray);
319     }
320 
321     @Override
322     public final int[] toArray(ValueLayout.OfInt elementLayout) {
323         return toArray(int[].class, elementLayout, int[]::new, MemorySegment::ofArray);
324     }
325 
326     @Override
327     public final float[] toArray(ValueLayout.OfFloat elementLayout) {
328         return toArray(float[].class, elementLayout, float[]::new, MemorySegment::ofArray);
329     }
330 
331     @Override
332     public final long[] toArray(ValueLayout.OfLong elementLayout) {
333         return toArray(long[].class, elementLayout, long[]::new, MemorySegment::ofArray);
334     }
335 
336     @Override
337     public final double[] toArray(ValueLayout.OfDouble elementLayout) {
338         return toArray(double[].class, elementLayout, double[]::new, MemorySegment::ofArray);
339     }
340 
341     private <Z> Z toArray(Class<Z> arrayClass, ValueLayout elemLayout, IntFunction<Z> arrayFactory, Function<Z, MemorySegment> segmentFactory) {
342         int size = checkArraySize(arrayClass.getSimpleName(), (int)elemLayout.byteSize());
343         Z arr = arrayFactory.apply(size);
344         MemorySegment arrSegment = segmentFactory.apply(arr);
345         MemorySegment.copy(this, elemLayout, 0, arrSegment, elemLayout.withOrder(ByteOrder.nativeOrder()), 0, size);
346         return arr;
347     }
348 
349     @Override
350     public boolean isSmall() {
351         return isSet(SMALL);
352     }
353 
354     @Override
355     public void checkAccess(long offset, long length, boolean readOnly) {
356         if (!readOnly && isSet(READ_ONLY)) {
357             throw new UnsupportedOperationException("Attempt to write a read-only segment");
358         }
359         checkBounds(offset, length);
360     }
361 
362     void checkValidState() {
363         try {
364             scope.checkValidState();
365         } catch (ScopedMemoryAccess.Scope.ScopedAccessError ex) {
366             throw new IllegalStateException("This segment is already closed");
367         }
368     }
369 
370     @Override
371     public long unsafeGetOffset() {
372         return min();
373     }
374 
375     @Override
376     public Object unsafeGetBase() {
377         return base();
378     }
379 
380     // Helper methods
381 
382     private boolean isSet(int mask) {
383         return (this.mask & mask) != 0;
384     }
385 
386     private int checkArraySize(String typeName, int elemSize) {
387         if (length % elemSize != 0) {
388             throw new IllegalStateException(String.format("Segment size is not a multiple of %d. Size: %d", elemSize, length));
389         }
390         long arraySize = length / elemSize;
391         if (arraySize > (Integer.MAX_VALUE - 8)) { //conservative check
392             throw new IllegalStateException(String.format("Segment is too large to wrap as %s. Size: %d", typeName, length));
393         }
394         return (int)arraySize;
395     }
396 
397     private void checkBounds(long offset, long length) {
398         if (isSmall() &&
399                 offset <= Integer.MAX_VALUE && length <= Integer.MAX_VALUE &&
400                 offset >= Integer.MIN_VALUE && length >= Integer.MIN_VALUE) {
401             checkBoundsSmall((int)offset, (int)length);
402         } else if (this != NativeMemorySegmentImpl.EVERYTHING) { // oob not possible for everything segment
403             if (
404                     length < 0 ||
405                     offset < 0 ||
406                     offset > this.length - length) { // careful of overflow
407                 throw outOfBoundException(offset, length);
408             }
409         }
410     }
411 
412     @Override
413     public ResourceScopeImpl scope() {
414         return scope;
415     }
416 
417     private void checkBoundsSmall(int offset, int length) {
418         if (length < 0 ||
419                 offset < 0 ||
420                 offset > (int)this.length - length) { // careful of overflow
421             throw outOfBoundException(offset, length);
422         }
423     }
424 
425     private IndexOutOfBoundsException outOfBoundException(long offset, long length) {
426         return new IndexOutOfBoundsException(String.format("Out of bound access on segment %s; new offset = %d; new length = %d",
427                         this, offset, length));
428     }
429 
430     protected int id() {
431         //compute a stable and random id for this memory segment
432         return Math.abs(Objects.hash(base(), min(), NONCE));
433     }
434 
435     static class SegmentSplitter implements Spliterator<MemorySegment> {
436         AbstractMemorySegmentImpl segment;
437         long elemCount;
438         final long elementSize;
439         long currentIndex;
440 
441         SegmentSplitter(long elementSize, long elemCount, AbstractMemorySegmentImpl segment) {
442             this.segment = segment;
443             this.elementSize = elementSize;
444             this.elemCount = elemCount;
445         }
446 
447         @Override
448         public SegmentSplitter trySplit() {
449             if (currentIndex == 0 && elemCount > 1) {
450                 AbstractMemorySegmentImpl parent = segment;
451                 long rem = elemCount % 2;
452                 long split = elemCount / 2;
453                 long lobound = split * elementSize;
454                 long hibound = lobound + (rem * elementSize);
455                 elemCount  = split + rem;
456                 segment = parent.asSliceNoCheck(lobound, hibound);
457                 return new SegmentSplitter(elementSize, split, parent.asSliceNoCheck(0, lobound));
458             } else {
459                 return null;
460             }
461         }
462 
463         @Override
464         public boolean tryAdvance(Consumer<? super MemorySegment> action) {
465             Objects.requireNonNull(action);
466             if (currentIndex < elemCount) {
467                 AbstractMemorySegmentImpl acquired = segment;
468                 try {
469                     action.accept(acquired.asSliceNoCheck(currentIndex * elementSize, elementSize));
470                 } finally {
471                     currentIndex++;
472                     if (currentIndex == elemCount) {
473                         segment = null;
474                     }
475                 }
476                 return true;
477             } else {
478                 return false;
479             }
480         }
481 
482         @Override
483         public void forEachRemaining(Consumer<? super MemorySegment> action) {
484             Objects.requireNonNull(action);
485             if (currentIndex < elemCount) {
486                 AbstractMemorySegmentImpl acquired = segment;
487                 try {
488                     if (acquired.isSmall()) {
489                         int index = (int) currentIndex;
490                         int limit = (int) elemCount;
491                         int elemSize = (int) elementSize;
492                         for (; index < limit; index++) {
493                             action.accept(acquired.asSliceNoCheck(index * elemSize, elemSize));
494                         }
495                     } else {
496                         for (long i = currentIndex ; i < elemCount ; i++) {
497                             action.accept(acquired.asSliceNoCheck(i * elementSize, elementSize));
498                         }
499                     }
500                 } finally {
501                     currentIndex = elemCount;
502                     segment = null;
503                 }
504             }
505         }
506 
507         @Override
508         public long estimateSize() {
509             return elemCount;
510         }
511 
512         @Override
513         public int characteristics() {
514             return NONNULL | SUBSIZED | SIZED | IMMUTABLE | ORDERED;
515         }
516     }
517 
518     // Object methods
519 
520     @Override
521     public String toString() {
522         return "MemorySegment{ id=0x" + Long.toHexString(id()) + " limit: " + length + " }";
523     }
524 
525     public static AbstractMemorySegmentImpl ofBuffer(ByteBuffer bb) {
526         Objects.requireNonNull(bb);
527         long bbAddress = nioAccess.getBufferAddress(bb);
528         Object base = nioAccess.getBufferBase(bb);
529         UnmapperProxy unmapper = nioAccess.unmapper(bb);
530 
531         int pos = bb.position();
532         int limit = bb.limit();
533         int size = limit - pos;
534 
535         AbstractMemorySegmentImpl bufferSegment = (AbstractMemorySegmentImpl)nioAccess.bufferSegment(bb);
536         final ResourceScopeImpl bufferScope;
537         int modes;
538         if (bufferSegment != null) {
539             bufferScope = bufferSegment.scope;
540             modes = bufferSegment.mask;
541         } else {
542             bufferScope = ResourceScopeImpl.GLOBAL;
543             modes = defaultAccessModes(size);
544         }
545         if (bb.isReadOnly()) {
546             modes |= READ_ONLY;
547         }
548         if (base != null) {
549             return new HeapMemorySegmentImpl.OfByte(bbAddress + pos, (byte[])base, size, modes);
550         } else if (unmapper == null) {
551             return new NativeMemorySegmentImpl(bbAddress + pos, size, modes, bufferScope);
552         } else {
553             return new MappedMemorySegmentImpl(bbAddress + pos, unmapper, size, modes, bufferScope);
554         }
555     }
556 }