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