You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datasketches.apache.org by le...@apache.org on 2021/11/18 19:11:23 UTC
[datasketches-memory] 01/01: This is a mess and has errors, so it will not compile.
This is an automated email from the ASF dual-hosted git repository.
leerho pushed a commit to branch Jdk17_Panama_mess
in repository https://gitbox.apache.org/repos/asf/datasketches-memory.git
commit ad382bccf0ac35dc0beb6bfe7f541f9b24062384
Author: Lee Rhodes <le...@users.noreply.github.com>
AuthorDate: Thu Nov 18 11:10:57 2021 -0800
This is a mess and has errors, so it will not compile.
---
datasketches-memory-java17/pom.xml | 10 +
.../src/main/java/module-info.java | 2 +-
.../org/apache/datasketches/memory/BaseState.java | 265 +++++++++
.../org/apache/datasketches/memory/Memory.java | 490 ++++++++++++++++
.../{internal/Dummy.java => MemoryException.java} | 21 +-
.../datasketches/memory/MemoryRequestServer.java | 50 ++
.../Dummy.java => ReadOnlyException.java} | 17 +-
.../apache/datasketches/memory/WritableMemory.java | 642 +++++++++++++++++++++
.../memory/internal/BaseStateImpl.java | 491 ++++++++++++++++
.../memory/internal/CompareAndCopy.java | 522 +++++++++++++++++
.../datasketches/memory/internal/UnsafeUtil.java | 228 ++++++++
.../apache/datasketches/memory/internal/Util.java | 24 +-
.../{internal/Dummy.java => package-info.java} | 9 +-
.../memory/internal/AllocateDirectMap.java | 6 +-
.../memory/internal/MemoryCleaner.java | 7 +-
.../apache/datasketches/memory/internal/Util.java | 2 +-
.../src/main/java/module-info.java | 2 +-
.../java/org/apache/datasketches/memory/Map.java | 25 +-
.../datasketches/memory/MemoryException.java | 21 +-
.../memory/internal/MemoryCleaner.java | 38 +-
.../apache/datasketches/memory/package-info.java | 9 +-
21 files changed, 2811 insertions(+), 70 deletions(-)
diff --git a/datasketches-memory-java17/pom.xml b/datasketches-memory-java17/pom.xml
index 2a24576..599f84a 100644
--- a/datasketches-memory-java17/pom.xml
+++ b/datasketches-memory-java17/pom.xml
@@ -28,6 +28,15 @@
<version>2.1.0-SNAPSHOT</version>
</parent>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.datasketches</groupId>
+ <artifactId>datasketches-memory-java8</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
<artifactId>datasketches-memory-java17</artifactId>
<name>${project.artifactId}</name>
<packaging>jar</packaging>
@@ -39,4 +48,5 @@
<maven.compiler.target>${java.version}</maven.compiler.target>
</properties>
+
</project>
diff --git a/datasketches-memory-java17/src/main/java/module-info.java b/datasketches-memory-java17/src/main/java/module-info.java
index ed6cf80..c19a017 100644
--- a/datasketches-memory-java17/src/main/java/module-info.java
+++ b/datasketches-memory-java17/src/main/java/module-info.java
@@ -22,5 +22,5 @@ module org.apache.datasketches.memory {
requires java.logging;
requires jdk.unsupported;
requires jdk.incubator.foreign;
- exports org.apache.datasketches.memory.internal;
+ exports org.apache.datasketches.memory;
}
diff --git a/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/BaseState.java b/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/BaseState.java
new file mode 100644
index 0000000..2f0cd96
--- /dev/null
+++ b/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/BaseState.java
@@ -0,0 +1,265 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+package org.apache.datasketches.memory;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import org.apache.datasketches.memory.internal.BaseStateImpl;
+
+/**
+ * Keeps key configuration state for Memory and Buffer plus some common static variables
+ * and check methods.
+ *
+ * @author Lee Rhodes
+ */
+public interface BaseState {
+ /**
+ * The placeholder for the default MemoryRequestServer, if set at all.
+ */
+ static final MemoryRequestServer defaultMemReqSvr = null; //new DefaultMemoryRequestServer();
+
+ //Byte Order Related
+
+ /**
+ * Gets the current Type ByteOrder.
+ * This may be different from the ByteOrder of the backing resource and of the Native Byte Order.
+ * @return the current Type ByteOrder.
+ */
+ ByteOrder getTypeByteOrder();
+
+ /**
+ * Returns true if the Native ByteOrder is the same as the ByteOrder of the
+ * current Buffer or Memory and the same ByteOrder as the given byteOrder.
+ * @param byteOrder the given ByteOrder
+ * @return true if the Native ByteOrder is the same as the ByteOrder of the
+ * current Buffer or Memory and the same ByteOrder as the given byteOrder.
+ */
+ boolean isByteOrderCompatible(ByteOrder byteOrder);
+
+ /**
+ * Returns true if the given object is an instance of this class and has equal data contents.
+ * @param that the given object
+ * @return true if the given Object is an instance of this class and has equal data contents.
+ */
+ @Override
+ boolean equals(Object that);
+
+ /**
+ * Returns true if the given object is an instance of this class and has equal contents to
+ * this object in the given range of bytes. This will also check two distinct ranges within the
+ * same object for equals.
+ * @param thisOffsetBytes the starting offset in bytes for this object.
+ * @param that the given object
+ * @param thatOffsetBytes the starting offset in bytes for the given object
+ * @param lengthBytes the size of the range in bytes
+ * @return true if the given object has equal contents to this object in the given range of
+ * bytes.
+ */
+ boolean equalTo(long thisOffsetBytes, Object that,
+ long thatOffsetBytes, long lengthBytes);
+
+ /**
+ * Gets the backing ByteBuffer if it exists, otherwise returns null.
+ * @return the backing ByteBuffer if it exists, otherwise returns null.
+ */
+ ByteBuffer getByteBuffer();
+
+ /**
+ * Gets the capacity of this object in bytes
+ * @return the capacity of this object in bytes
+ */
+ long getCapacity();
+
+ /**
+ * Gets the cumulative offset in bytes of this object from the backing resource.
+ * This offset may also include other offset components such as the native off-heap
+ * memory address, DirectByteBuffer split offsets, region offsets, and unsafe arrayBaseOffsets.
+ *
+ * @return the cumulative offset in bytes of this object from the backing resource.
+ */
+ long getCumulativeOffset();
+
+ /**
+ * Gets the cumulative offset in bytes of this object from the backing resource including the given
+ * offsetBytes. This offset may also include other offset components such as the native off-heap
+ * memory address, DirectByteBuffer split offsets, region offsets, and unsafe arrayBaseOffsets.
+ *
+ * @param offsetBytes offset to be added to the cumulative offset.
+ * @return the cumulative offset in bytes of this object from the backing resource including the
+ * given offsetBytes.
+ */
+ long getCumulativeOffset(long offsetBytes);
+
+ /**
+ * Returns the offset of address zero of this object relative to the address zero of the
+ * backing resource but not including the size of any Java object header.
+ * @return the offset of address zero of this object relative to the address zero of the
+ * backing resource but not including the size of any Java object header.
+ */
+ long getRegionOffset();
+
+ /**
+ * Returns the offset of address zero of this object relative to the address zero of the
+ * backing resource plus the given offsetBytes but not including the size of any Java object
+ * header.
+ * @param offsetBytes the given offsetBytes
+ * @return the offset of address zero of this object relative to the address zero of the
+ * backing resource plus the given offsetBytes but not including the size of any Java object
+ * header.
+ */
+ long getRegionOffset(long offsetBytes);
+
+ /**
+ * Returns true if this object is backed by an on-heap primitive array
+ * @return true if this object is backed by an on-heap primitive array
+ */
+ boolean hasArray();
+
+ /**
+ * Returns the hashCode of this object.
+ *
+ * <p>The hash code of this object depends upon all of its contents.
+ * Because of this, it is inadvisable to use these objects as keys in hash maps
+ * or similar data structures unless it is known that their contents will not change.</p>
+ *
+ * <p>If it is desirable to use these objects in a hash map depending only on object identity,
+ * than the {@link java.util.IdentityHashMap} can be used.</p>
+ *
+ * @return the hashCode of this object.
+ */
+ @Override
+ int hashCode();
+
+ /**
+ * Returns the 64-bit hash of the sequence of bytes in this object specified by
+ * <i>offsetBytes</i>, <i>lengthBytes</i> and a <i>seed</i>. Note that the sequence of bytes is
+ * always processed in the same order independent of endianness.
+ *
+ * @param offsetBytes the given offset in bytes to the first byte of the byte sequence.
+ * @param lengthBytes the given length in bytes of the byte sequence.
+ * @param seed the given long seed.
+ * @return the 64-bit hash of the sequence of bytes in this object specified by
+ * <i>offsetBytes</i> and <i>lengthBytes</i>.
+ */
+ long xxHash64(long offsetBytes, long lengthBytes, long seed);
+
+ /**
+ * Returns a 64-bit hash from a single long. This method has been optimized for speed when only
+ * a single hash of a long is required.
+ * @param in A long.
+ * @param seed A long valued seed.
+ * @return the hash.
+ */
+ long xxHash64(long in, long seed);
+
+ /**
+ * Returns true if this Memory is backed by a ByteBuffer.
+ * @return true if this Memory is backed by a ByteBuffer.
+ */
+ boolean hasByteBuffer();
+
+ /**
+ * Returns true if the backing resource is direct (off-heap) memory.
+ * This is the case for allocated direct memory, memory mapped files,
+ * @return true if the backing resource is direct (off-heap) memory.
+ */
+ boolean isDirect();
+
+ /**
+ * Returns true if this object or the backing resource is read-only.
+ * @return true if this object or the backing resource is read-only.
+ */
+ boolean isReadOnly();
+
+ /**
+ * Returns true if the backing resource of <i>this</i> is identical with the backing resource
+ * of <i>that</i>. The capacities must be the same. If <i>this</i> is a region,
+ * the region offset must also be the same.
+ * @param that A different non-null object
+ * @return true if the backing resource of <i>this</i> is the same as the backing resource
+ * of <i>that</i>.
+ */
+ boolean isSameResource(Object that);
+
+ /**
+ * Returns true if this object is valid and has not been closed.
+ * This is relevant only for direct (off-heap) memory and Mapped Files.
+ * @return true if this object is valid and has not been closed.
+ */
+ boolean isValid();
+
+ /**
+ * Checks that the specified range of bytes is within bounds of this object, throws
+ * {@link IllegalArgumentException} if it's not: i. e. if offsetBytes < 0, or length < 0,
+ * or offsetBytes + length > {@link #getCapacity()}.
+ * @param offsetBytes the given offset in bytes of this object
+ * @param lengthBytes the given length in bytes of this object
+ */
+ void checkValidAndBounds(long offsetBytes, long lengthBytes);
+
+ //Monitoring
+
+ /**
+ * Gets the current number of active direct memory allocations.
+ * @return the current number of active direct memory allocations.
+ */
+ static long getCurrentDirectMemoryAllocations() {
+ return BaseStateImpl.getCurrentDirectMemoryAllocations();
+ }
+
+ /**
+ * Gets the current size of active direct memory allocated.
+ * @return the current size of active direct memory allocated.
+ */
+ static long getCurrentDirectMemoryAllocated() {
+ return BaseStateImpl.getCurrentDirectMemoryAllocated();
+ }
+
+ /**
+ * Gets the current number of active direct memory map allocations.
+ * @return the current number of active direct memory map allocations.
+ */
+ static long getCurrentDirectMemoryMapAllocations() {
+ return BaseStateImpl.getCurrentDirectMemoryMapAllocations();
+ }
+
+ /**
+ * Gets the current size of active direct memory map allocated.
+ * @return the current size of active direct memory map allocated.
+ */
+ static long getCurrentDirectMemoryMapAllocated() {
+ return BaseStateImpl.getCurrentDirectMemoryMapAllocated();
+ }
+
+ //TO STRING
+
+ /**
+ * Returns a formatted hex string of a range of this object.
+ * Used primarily for testing.
+ * @param header a descriptive header
+ * @param offsetBytes offset bytes relative to this object start
+ * @param lengthBytes number of bytes to convert to a hex string
+ * @return a formatted hex string in a human readable array
+ */
+ String toHexString(String header, long offsetBytes, int lengthBytes);
+
+}
diff --git a/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/Memory.java b/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/Memory.java
new file mode 100644
index 0000000..f25de32
--- /dev/null
+++ b/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/Memory.java
@@ -0,0 +1,490 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+package org.apache.datasketches.memory;
+
+import static org.apache.datasketches.memory.internal.Util.negativeCheck;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.channels.WritableByteChannel;
+import java.util.Objects;
+
+import org.apache.datasketches.memory.internal.BaseWritableMemoryImpl;
+import org.apache.datasketches.memory.internal.Prim;
+import org.apache.datasketches.memory.internal.UnsafeUtil;
+
+/**
+ * Defines the read-only API for offset access to a resource.
+ *
+ * @author Lee Rhodes
+ */
+public interface Memory extends BaseState {
+
+ //BYTE BUFFER
+
+ /**
+ * Accesses the given <i>ByteBuffer</i> for read-only operations. The returned <i>Memory</i> object has
+ * the same byte order, as the given <i>ByteBuffer</i>.
+ * @param byteBuffer the given <i>ByteBuffer</i>. It must be non-null and with capacity ≥ 0.
+ * @return a new <i>Memory</i> for read-only operations on the given <i>ByteBuffer</i>.
+ */
+ static Memory wrap(ByteBuffer byteBuffer) {
+ return wrap(byteBuffer, byteBuffer.order());
+ }
+
+ /**
+ * Accesses the given <i>ByteBuffer</i> for read-only operations. The returned <i>Memory</i> object has
+ * the given byte order, ignoring the byte order of the given <i>ByteBuffer</i> for future reads and writes.
+ * @param byteBuffer the given <i>ByteBuffer</i>. It must be non-null and with capacity ≥ 0.
+ * @param byteOrder the byte order to be used. It must be non-null.
+ * @return a new <i>Memory</i> for read-only operations on the given <i>ByteBuffer</i>.
+ */
+ static Memory wrap(ByteBuffer byteBuffer, ByteOrder byteOrder) {
+ Objects.requireNonNull(byteBuffer, "byteBuffer must not be null");
+ Objects.requireNonNull(byteOrder, "byteOrder must not be null");
+ negativeCheck(byteBuffer.capacity(), "byteBuffer.capacity()");
+ return BaseWritableMemoryImpl.wrapByteBuffer(byteBuffer, true, byteOrder, null);
+ }
+
+ //MAP
+ /**
+ * Maps the entire given file into native-ordered <i>Memory</i> for read operations
+ * Calling this method is equivalent to calling
+ * {@link #map(File, long, long, ByteOrder) map(file, 0, file.length(), ByteOrder.nativeOrder())}.
+ * @param file the given file to map. It must be non-null, length ≥ 0, and readable.
+ * @return <i>MapHandle</i> for managing the mapped memory.
+ * Please read Javadocs for {@link Handle}.
+ */
+ static MapHandle map(File file) {
+ return map(file, 0, file.length(), ByteOrder.nativeOrder());
+ }
+
+ /**
+ * Maps the specified portion of the given file into <i>Memory</i> for read operations.
+ * @param file the given file to map. It must be non-null,readable and length ≥ 0.
+ * @param fileOffsetBytes the position in the given file in bytes. It must not be negative.
+ * @param capacityBytes the size of the mapped memory. It must be ≥ 0.
+ * @param byteOrder the byte order to be used for the mapped memory. It must be non-null.
+ * @return <i>MapHandle</i> for managing the mapped memory.
+ * Please read Javadocs for {@link Handle}.
+ */
+ static MapHandle map(File file, long fileOffsetBytes, long capacityBytes, ByteOrder byteOrder) {
+ Objects.requireNonNull(file, "file must be non-null.");
+ Objects.requireNonNull(byteOrder, "byteOrder must be non-null.");
+ if (!file.canRead()) { throw new IllegalArgumentException("file must be readable."); }
+ negativeCheck(file.length(), "file.length()");
+ negativeCheck(fileOffsetBytes, "fileOffsetBytes");
+ negativeCheck(capacityBytes, "capacityBytes");
+ return (MapHandle) BaseWritableMemoryImpl.wrapMap(file, fileOffsetBytes, capacityBytes, true, byteOrder);
+ }
+
+ //REGIONS
+ /**
+ * A region is a read-only view of this object.
+ * <ul>
+ * <li>Returned object's origin = this object's origin + offsetBytes</li>
+ * <li>Returned object's capacity = capacityBytes</li>
+ * </ul>
+ * @param offsetBytes the starting offset with respect to the origin of this <i>Memory</i>. It must be ≥ 0.
+ * @param capacityBytes the capacity of the region in bytes. It must be ≥ 0.
+ * @return a new <i>Memory</i> representing the defined region based on the given
+ * offsetBytes and capacityBytes.
+ */
+ default Memory region(long offsetBytes, long capacityBytes) {
+ return region(offsetBytes, capacityBytes, ByteOrder.nativeOrder());
+ }
+
+ /**
+ * A region is a read-only view of this object.
+ * <ul>
+ * <li>Returned object's origin = this object's origin + <i>offsetBytes</i></li>
+ * <li>Returned object's capacity = <i>capacityBytes</i></li>
+ * <li>Returned object's byte order = <i>byteOrder</i></li>
+ * </ul>
+ * @param offsetBytes the starting offset with respect to the origin of this Memory. It must be ≥ 0.
+ * @param capacityBytes the capacity of the region in bytes. It must be ≥ 0.
+ * @param byteOrder the given byte order. It must be non-null.
+ * @return a new <i>Memory</i> representing the defined region based on the given
+ * offsetBytes, capacityBytes and byteOrder.
+ */
+ Memory region(long offsetBytes, long capacityBytes, ByteOrder byteOrder);
+
+ //AS BUFFER
+ /**
+ * Returns a new <i>Buffer</i> view of this object.
+ * <ul>
+ * <li>Returned object's origin = this object's origin</li>
+ * <li>Returned object's <i>start</i> = 0</li>
+ * <li>Returned object's <i>position</i> = 0</li>
+ * <li>Returned object's <i>end</i> = this object's capacity</li>
+ * <li>Returned object's <i>capacity</i> = this object's capacity</li>
+ * <li>Returned object's <i>start</i>, <i>position</i> and <i>end</i> are mutable</li>
+ * </ul>
+ * @return a new <i>Buffer</i>
+ */
+ default Buffer asBuffer() {
+ return asBuffer(ByteOrder.nativeOrder());
+ }
+
+ /**
+ * Returns a new <i>Buffer</i> view of this object, with the given
+ * byte order.
+ * <ul>
+ * <li>Returned object's origin = this object's origin</li>
+ * <li>Returned object's <i>start</i> = 0</li>
+ * <li>Returned object's <i>position</i> = 0</li>
+ * <li>Returned object's <i>end</i> = this object's capacity</li>
+ * <li>Returned object's <i>capacity</i> = this object's capacity</li>
+ * <li>Returned object's <i>start</i>, <i>position</i> and <i>end</i> are mutable</li>
+ * </ul>
+ * @param byteOrder the given byte order
+ * @return a new <i>Buffer</i> with the given byteOrder.
+ */
+ Buffer asBuffer(ByteOrder byteOrder);
+
+ //ACCESS PRIMITIVE HEAP ARRAYS for readOnly
+ /**
+ * Wraps the given primitive array for read operations assuming native byte order.
+ * @param array the given primitive array. It must be non-null and with length ≥ 0.
+ * @return a new <i>Memory</i> for read operations
+ */
+ static Memory wrap(byte[] array) {
+ Objects.requireNonNull(array, "array must be non-null");
+ return wrap(array, 0, array.length, ByteOrder.nativeOrder());
+ }
+
+ /**
+ * Wraps the given primitive array for read operations with the given byte order.
+ * @param array the given primitive array.
+ * @param byteOrder the byte order to be used
+ * @return a new <i>Memory</i> for read operations
+ */
+ static Memory wrap(byte[] array, ByteOrder byteOrder) {
+ return wrap(array, 0, array.length, byteOrder);
+ }
+
+ /**
+ * Wraps the given primitive array for read operations with the given byte order.
+ * @param array the given primitive array. It must be non-null and length ≥ 0.
+ * @param offsetBytes the byte offset into the given array
+ * @param lengthBytes the number of bytes to include from the given array, it must be ≥ 0.
+ * @param byteOrder the byte order to be used
+ * @return a new <i>Memory</i> for read operations
+ */
+ static Memory wrap(byte[] array, int offsetBytes, int lengthBytes, ByteOrder byteOrder) {
+ Objects.requireNonNull(array, "array must be non-null");
+ Objects.requireNonNull(byteOrder, "byteOrder must be non-null");
+ negativeCheck(offsetBytes, "offsetBytes");
+ negativeCheck(lengthBytes, "lengthBytes");
+ UnsafeUtil.checkBounds(offsetBytes, lengthBytes, array.length);
+ return BaseWritableMemoryImpl.wrapHeapArray(array, 0, lengthBytes, true, ByteOrder.nativeOrder(), null);
+ }
+
+ /**
+ * Wraps the given primitive array for read operations assuming native byte order.
+ * @param array the given primitive array. It must be non-null and length ≥ 0.
+ * @return a new <i>Memory</i> for read operations
+ */
+ static Memory wrap(boolean[] array) {
+ Objects.requireNonNull(array, "array must be non-null");
+ final long lengthBytes = array.length << Prim.BOOLEAN.shift();
+ return BaseWritableMemoryImpl.wrapHeapArray(array, 0, lengthBytes, true, ByteOrder.nativeOrder(), null);
+ }
+
+ /**
+ * Wraps the given primitive array for read operations assuming native byte order.
+ * @param array the given primitive array. It must be non-null and length ≥ 0.
+ * @return a new <i>Memory</i> for read operations
+ */
+ static Memory wrap(char[] array) {
+ Objects.requireNonNull(array, "array must be non-null");
+ final long lengthBytes = array.length << Prim.CHAR.shift();
+ return BaseWritableMemoryImpl.wrapHeapArray(array, 0L, lengthBytes, true, ByteOrder.nativeOrder(), null);
+ }
+
+ /**
+ * Wraps the given primitive array for read operations assuming native byte order.
+ * @param array the given primitive array. It must be non-null and length ≥ 0.
+ * @return a new <i>Memory</i> for read operations
+ */
+ static Memory wrap(short[] array) {
+ Objects.requireNonNull(array, "arr must be non-null");
+ final long lengthBytes = array.length << Prim.SHORT.shift();
+ return BaseWritableMemoryImpl.wrapHeapArray(array, 0L, lengthBytes, true, ByteOrder.nativeOrder(), null);
+ }
+
+ /**
+ * Wraps the given primitive array for read operations assuming native byte order.
+ * @param array the given primitive array. It must be non-null and length ≥ 0.
+ * @return a new <i>Memory</i> for read operations
+ */
+ static Memory wrap(int[] array) {
+ Objects.requireNonNull(array, "arr must be non-null");
+ final long lengthBytes = array.length << Prim.INT.shift();
+ return BaseWritableMemoryImpl.wrapHeapArray(array, 0L, lengthBytes, true, ByteOrder.nativeOrder(), null);
+ }
+
+ /**
+ * Wraps the given primitive array for read operations assuming native byte order.
+ * @param array the given primitive array. It must be non-null and length ≥ 0.
+ * @return a new <i>Memory</i> for read operations
+ */
+ static Memory wrap(long[] array) {
+ Objects.requireNonNull(array, "arr must be non-null");
+ final long lengthBytes = array.length << Prim.LONG.shift();
+ return BaseWritableMemoryImpl.wrapHeapArray(array, 0L, lengthBytes, true, ByteOrder.nativeOrder(), null);
+ }
+
+ /**
+ * Wraps the given primitive array for read operations assuming native byte order.
+ * @param array the given primitive array. It must be non-null and length ≥ 0.
+ * @return a new <i>Memory</i> for read operations
+ */
+ static Memory wrap(float[] array) {
+ Objects.requireNonNull(array, "arr must be non-null");
+ final long lengthBytes = array.length << Prim.FLOAT.shift();
+ return BaseWritableMemoryImpl.wrapHeapArray(array, 0L, lengthBytes, true, ByteOrder.nativeOrder(), null);
+ }
+
+ /**
+ * Wraps the given primitive array for read operations assuming native byte order.
+ * @param array the given primitive array. It must be non-null and length ≥ 0.
+ * @return a new <i>Memory</i> for read operations
+ */
+ static Memory wrap(double[] array) {
+ Objects.requireNonNull(array, "arr must be non-null");
+ final long lengthBytes = array.length << Prim.DOUBLE.shift();
+ return BaseWritableMemoryImpl.wrapHeapArray(array, 0L, lengthBytes, true, ByteOrder.nativeOrder(), null);
+ }
+
+ //PRIMITIVE getX() and getXArray()
+ /**
+ * Gets the boolean value at the given offset
+ * @param offsetBytes offset bytes relative to this Memory start
+ * @return the boolean at the given offset
+ */
+ boolean getBoolean(long offsetBytes);
+
+ /**
+ * Gets the boolean array at the given offset
+ * @param offsetBytes offset bytes relative to this Memory start
+ * @param dstArray The preallocated destination array.
+ * @param dstOffsetBooleans offset in array units
+ * @param lengthBooleans number of array units to transfer
+ */
+ void getBooleanArray(long offsetBytes, boolean[] dstArray, int dstOffsetBooleans, int lengthBooleans);
+
+ /**
+ * Gets the byte value at the given offset
+ * @param offsetBytes offset bytes relative to this Memory start
+ * @return the byte at the given offset
+ */
+ byte getByte(long offsetBytes);
+
+ /**
+ * Gets the byte array at the given offset
+ * @param offsetBytes offset bytes relative to this Memory start
+ * @param dstArray The preallocated destination array.
+ * @param dstOffsetBytes offset in array units
+ * @param lengthBytes number of array units to transfer
+ */
+ void getByteArray(long offsetBytes, byte[] dstArray, int dstOffsetBytes, int lengthBytes);
+
+ /**
+ * Gets the char value at the given offset
+ * @param offsetBytes offset bytes relative to this Memory start
+ * @return the char at the given offset
+ */
+ char getChar(long offsetBytes);
+
+ /**
+ * Gets the char array at the given offset
+ * @param offsetBytes offset bytes relative to this Memory start
+ * @param dstArray The preallocated destination array.
+ * @param dstOffsetChars offset in array units
+ * @param lengthChars number of array units to transfer
+ */
+ void getCharArray(long offsetBytes, char[] dstArray, int dstOffsetChars, int lengthChars);
+
+ /**
+ * Gets UTF-8 encoded bytes from this Memory, starting at offsetBytes to a length of
+ * utf8LengthBytes, decodes them into characters and appends them to the given Appendable.
+ * This is specifically designed to reduce the production of intermediate objects (garbage),
+ * thus significantly reducing pressure on the JVM Garbage Collector.
+ * @param offsetBytes offset bytes relative to the Memory start
+ * @param utf8LengthBytes the number of encoded UTF-8 bytes to decode. It is assumed that the
+ * caller has the correct number of utf8 bytes required to decode the number of characters
+ * to be appended to dst. Characters outside the ASCII range can require 2, 3 or 4 bytes per
+ * character to decode.
+ * @param dst the destination Appendable to append the decoded characters to.
+ * @return the number of characters decoded
+ * @throws IOException if dst.append() throws IOException
+ * @throws Utf8CodingException in case of malformed or illegal UTF-8 input
+ */
+ int getCharsFromUtf8(long offsetBytes, int utf8LengthBytes, Appendable dst)
+ throws IOException, Utf8CodingException;
+
+ /**
+ * Gets UTF-8 encoded bytes from this Memory, starting at offsetBytes to a length of
+ * utf8LengthBytes, decodes them into characters and appends them to the given StringBuilder.
+ * This method does *not* reset the length of the destination StringBuilder before appending
+ * characters to it.
+ * This is specifically designed to reduce the production of intermediate objects (garbage),
+ * thus significantly reducing pressure on the JVM Garbage Collector.
+ * @param offsetBytes offset bytes relative to the Memory start
+ * @param utf8LengthBytes the number of encoded UTF-8 bytes to decode. It is assumed that the
+ * caller has the correct number of utf8 bytes required to decode the number of characters
+ * to be appended to dst. Characters outside the ASCII range can require 2, 3 or 4 bytes per
+ * character to decode.
+ * @param dst the destination StringBuilder to append decoded characters to.
+ * @return the number of characters decoded.
+ * @throws Utf8CodingException in case of malformed or illegal UTF-8 input
+ */
+ int getCharsFromUtf8(long offsetBytes, int utf8LengthBytes, StringBuilder dst)
+ throws Utf8CodingException;
+
+ /**
+ * Gets the double value at the given offset
+ * @param offsetBytes offset bytes relative to this Memory start
+ * @return the double at the given offset
+ */
+ double getDouble(long offsetBytes);
+
+ /**
+ * Gets the double array at the given offset
+ * @param offsetBytes offset bytes relative to this Memory start
+ * @param dstArray The preallocated destination array.
+ * @param dstOffsetDoubles offset in array units
+ * @param lengthDoubles number of array units to transfer
+ */
+ void getDoubleArray(long offsetBytes, double[] dstArray, int dstOffsetDoubles, int lengthDoubles);
+
+ /**
+ * Gets the float value at the given offset
+ * @param offsetBytes offset bytes relative to this Memory start
+ * @return the float at the given offset
+ */
+ float getFloat(long offsetBytes);
+
+ /**
+ * Gets the float array at the given offset
+ * @param offsetBytes offset bytes relative to this Memory start
+ * @param dstArray The preallocated destination array.
+ * @param dstOffsetFloats offset in array units
+ * @param lengthFloats number of array units to transfer
+ */
+ void getFloatArray(long offsetBytes, float[] dstArray, int dstOffsetFloats, int lengthFloats);
+
+ /**
+ * Gets the int value at the given offset
+ * @param offsetBytes offset bytes relative to this Memory start
+ * @return the int at the given offset
+ */
+ int getInt(long offsetBytes);
+
+ /**
+ * Gets the int array at the given offset
+ * @param offsetBytes offset bytes relative to this Memory start
+ * @param dstArray The preallocated destination array.
+ * @param dstOffsetInts offset in array units
+ * @param lengthInts number of array units to transfer
+ */
+ void getIntArray(long offsetBytes, int[] dstArray, int dstOffsetInts, int lengthInts);
+
+ /**
+ * Gets the long value at the given offset
+ * @param offsetBytes offset bytes relative to this Memory start
+ * @return the long at the given offset
+ */
+ long getLong(long offsetBytes);
+
+ /**
+ * Gets the long array at the given offset
+ * @param offsetBytes offset bytes relative to this Memory start
+ * @param dstArray The preallocated destination array.
+ * @param dstOffsetLongs offset in array units
+ * @param lengthLongs number of array units to transfer
+ */
+ void getLongArray(long offsetBytes, long[] dstArray, int dstOffsetLongs, int lengthLongs);
+
+ /**
+ * Gets the short value at the given offset
+ * @param offsetBytes offset bytes relative to this Memory start
+ * @return the short at the given offset
+ */
+ short getShort(long offsetBytes);
+
+ /**
+ * Gets the short array at the given offset
+ * @param offsetBytes offset bytes relative to this Memory start
+ * @param dstArray The preallocated destination array.
+ * @param dstOffsetShorts offset in array units
+ * @param lengthShorts number of array units to transfer
+ */
+ void getShortArray(long offsetBytes, short[] dstArray, int dstOffsetShorts, int lengthShorts);
+
+ //SPECIAL PRIMITIVE READ METHODS: compareTo, copyTo, writeTo
+ /**
+ * Compares the bytes of this Memory to <i>that</i> Memory.
+ * Returns <i>(this < that) ? (some negative value) : (this > that) ? (some positive value)
+ * : 0;</i>.
+ * If all bytes are equal up to the shorter of the two lengths, the shorter length is considered
+ * to be less than the other.
+ * @param thisOffsetBytes the starting offset for <i>this Memory</i>
+ * @param thisLengthBytes the length of the region to compare from <i>this Memory</i>
+ * @param that the other Memory to compare with
+ * @param thatOffsetBytes the starting offset for <i>that Memory</i>
+ * @param thatLengthBytes the length of the region to compare from <i>that Memory</i>
+ * @return <i>(this < that) ? (some negative value) : (this > that) ? (some positive value)
+ * : 0;</i>
+ */
+ int compareTo(long thisOffsetBytes, long thisLengthBytes, Memory that,
+ long thatOffsetBytes, long thatLengthBytes);
+
+ /**
+ * Copies bytes from a source range of this Memory to a destination range of the given Memory
+ * with the same semantics when copying between overlapping ranges of bytes as method
+ * {@link java.lang.System#arraycopy(Object, int, Object, int, int)} has. However, if the source
+ * and the destination ranges are exactly the same, this method throws {@link
+ * IllegalArgumentException}, because it should never be needed in real-world scenarios and
+ * therefore indicates a bug.
+ * @param srcOffsetBytes the source offset for this Memory
+ * @param destination the destination Memory, which may not be Read-Only.
+ * @param dstOffsetBytes the destination offset
+ * @param lengthBytes the number of bytes to copy
+ */
+ void copyTo(long srcOffsetBytes, WritableMemory destination, long dstOffsetBytes, long lengthBytes);
+
+ /**
+ * Writes bytes from a source range of this Memory to the given {@code WritableByteChannel}.
+ * @param offsetBytes the source offset for this Memory
+ * @param lengthBytes the number of bytes to copy
+ * @param out the destination WritableByteChannel
+ * @throws IOException may occur while writing to the WritableByteChannel
+ */
+ void writeTo(long offsetBytes, long lengthBytes, WritableByteChannel out)
+ throws IOException;
+
+}
+
+
diff --git a/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/Dummy.java b/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/MemoryException.java
similarity index 55%
copy from datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/Dummy.java
copy to datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/MemoryException.java
index aefdcba..6b3663c 100644
--- a/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/Dummy.java
+++ b/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/MemoryException.java
@@ -17,11 +17,26 @@
* under the License.
*/
-package org.apache.datasketches.memory.internal;
+package org.apache.datasketches.memory;
/**
- * Temporary class & placeholder. It may not be needed.
+ * Specific RuntimeExceptions for the datasketches-memory component.
+ *
+ * @author Lee Rhodes
+ *
*/
-public class Dummy {
+public class MemoryException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+ /**
+ * Constructs a new runtime exception with the specified detail message. The cause is not
+ * initialized, and may subsequently be initialized by a call to
+ * Throwable.initCause(java.lang.Throwable).
+ *
+ * @param message the detail message. The detail message is saved for later retrieval by the
+ * Throwable.getMessage() method.
+ */
+ public MemoryException(final String message) {
+ super(message);
+ }
}
diff --git a/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/MemoryRequestServer.java b/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/MemoryRequestServer.java
new file mode 100644
index 0000000..e2ca235
--- /dev/null
+++ b/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/MemoryRequestServer.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.datasketches.memory;
+
+/**
+ * The MemoryRequestServer is a callback interface to provide a means for direct (off-heap), heap and ByteBuffer
+ * backed resources to request more memory.
+ *
+ * @author Lee Rhodes
+ */
+public interface MemoryRequestServer {
+
+ /**
+ * Request new WritableMemory with the given capacity. The current Writable Memory will be used to
+ * determine the byte order of the returned WritableMemory and other checks.
+ * @param currentWritableMemory the current writableMemory of the client. It must be non-null.
+ * @param capacityBytes The capacity being requested. It must be ≥ 0.
+ *
+ * @return new WritableMemory with the given capacity.
+ */
+ WritableMemory request(WritableMemory currentWritableMemory, long capacityBytes);
+
+ /**
+ * Request close the AutoCloseable resource. This only applies to resources allocated using
+ * WritableMemory.allocateDirect(...).
+ * This may be ignored depending on the application implementation.
+ * @param memToClose the relevant WritbleMemory to be considered for closing. It must be non-null.
+ * @param newMemory the newly allocated WritableMemory. It must be non-null.
+ * This is returned from the client to facilitate tracking for the convenience of the resource owner.
+ */
+ void requestClose(final WritableMemory memToClose, WritableMemory newMemory);
+
+}
diff --git a/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/Dummy.java b/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/ReadOnlyException.java
similarity index 65%
copy from datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/Dummy.java
copy to datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/ReadOnlyException.java
index aefdcba..3e14795 100644
--- a/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/Dummy.java
+++ b/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/ReadOnlyException.java
@@ -17,11 +17,22 @@
* under the License.
*/
-package org.apache.datasketches.memory.internal;
+package org.apache.datasketches.memory;
/**
- * Temporary class & placeholder. It may not be needed.
+ * The exception thrown when attempting to write into a read-only Resource.
+ *
+ * @author Praveenkumar Venkatesan
*/
-public class Dummy {
+public class ReadOnlyException extends MemoryException {
+ private static final long serialVersionUID = 1L;
+ /**
+ * Read Only Exception
+ * @param message the error message
+ */
+ public ReadOnlyException(final String message) {
+ super(message);
+ }
}
+
diff --git a/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/WritableMemory.java b/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/WritableMemory.java
new file mode 100644
index 0000000..14023e1
--- /dev/null
+++ b/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/WritableMemory.java
@@ -0,0 +1,642 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.datasketches.memory;
+
+import static org.apache.datasketches.memory.internal.Util.negativeCheck;
+import static org.apache.datasketches.memory.internal.Util.zeroCheck;
+
+import java.io.File;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Objects;
+
+import org.apache.datasketches.memory.internal.BaseWritableMemoryImpl;
+import org.apache.datasketches.memory.internal.Prim;
+import org.apache.datasketches.memory.internal.UnsafeUtil;
+
+/**
+ * Defines the writable API for offset access to a resource.
+ *
+ * @author Lee Rhodes
+ */
+public interface WritableMemory extends Memory {
+
+ //BYTE BUFFER
+ /**
+ * Accesses the given <i>ByteBuffer</i> for write operations. The returned <i>WritableMemory</i> object has
+ * the same byte order, as the given <i>ByteBuffer</i>.
+ * @param byteBuffer the given <i>ByteBuffer</i>. It must be non-null, with capacity ≥ 0, and writable.
+ * @return a new <i>WritableMemory</i> for write operations on the given <i>ByteBuffer</i>.
+ */
+ static WritableMemory writableWrap(ByteBuffer byteBuffer) {
+ return writableWrap(byteBuffer, byteBuffer.order(), defaultMemReqSvr);
+ }
+
+ /**
+ * Accesses the given <i>ByteBuffer</i> for write operations. The returned <i>WritableMemory</i> object has
+ * the given byte order, ignoring the byte order of the given <i>ByteBuffer</i> for future writes and following reads.
+ * However, this does not change the byte order of data already in the <i>ByteBuffer</i>.
+ * @param byteBuffer the given <i>ByteBuffer</i>. It must be non-null, with capacity ≥ 0, and writable.
+ * @param byteOrder the byte order to be used. It must be non-null.
+ * @return a new <i>WritableMemory</i> for write operations on the given <i>ByteBuffer</i>.
+ */
+ static WritableMemory writableWrap(ByteBuffer byteBuffer, ByteOrder byteOrder) {
+ return writableWrap(byteBuffer, byteOrder, defaultMemReqSvr);
+ }
+
+ /**
+ * Accesses the given <i>ByteBuffer</i> for write operations. The returned <i>WritableMemory</i> object has
+ * the given byte order, ignoring the byte order of the given <i>ByteBuffer</i> for future reads and writes.
+ * However, this does not change the byte order of data already in the <i>ByteBuffer</i>.
+ * @param byteBuffer the given <i>ByteBuffer</i>. It must be non-null, with capacity ≥ 0, and writable.
+ * @param byteOrder the byte order to be used. It must be non-null.
+ * @param memReqSvr A user-specified <i>MemoryRequestServer</i>, which may be null.
+ * This is a callback mechanism for a user client to request a larger <i>WritableMemory</i>.
+ * @return a new <i>WritableMemory</i> for write operations on the given <i>ByteBuffer</i>.
+ */
+ static WritableMemory writableWrap(ByteBuffer byteBuffer, ByteOrder byteOrder, MemoryRequestServer memReqSvr) {
+ Objects.requireNonNull(byteBuffer, "byteBuffer must be non-null");
+ Objects.requireNonNull(byteOrder, "byteOrder must be non-null");
+ negativeCheck(byteBuffer.capacity(), "byteBuffer.capacity()");
+ if (byteBuffer.isReadOnly()) { throw new ReadOnlyException("byteBuffer must be writable."); }
+ return BaseWritableMemoryImpl.wrapByteBuffer(byteBuffer, false, byteOrder, memReqSvr);
+ }
+
+ //MAP
+ /**
+ * Maps the entire given file into native-ordered WritableMemory for write operations
+ * Calling this method is equivalent to calling
+ * {@link #writableMap(File, long, long, ByteOrder) writableMap(file, 0, file.length(), ByteOrder.nativeOrder())}.
+ * @param file the given file to map. It must be non-null, with length ≥ 0, and writable.
+ * @return WritableMapHandle for managing the mapped Memory.
+ * Please read Javadocs for {@link Handle}.
+ */
+ static WritableMapHandle writableMap(File file) {
+ return writableMap(file, 0, file.length(), ByteOrder.nativeOrder());
+ }
+
+ /**
+ * Maps the specified portion of the given file into Memory for write operations.
+ *
+ * <p><b>Note:</b> Always qualify this method with the class name, e.g.,
+ * <i>WritableMemory.map(...)</i>.
+ * @param file the given file to map. It must be non-null, writable and length ≥ 0.
+ * @param fileOffsetBytes the position in the given file in bytes. It must not be negative.
+ * @param capacityBytes the size of the mapped Memory. It must be ≥ 0.
+ * @param byteOrder the byte order to be used for the given file. It must be non-null.
+ * @return WritableMapHandle for managing the mapped Memory.
+ * Please read Javadocs for {@link Handle}.
+ */
+ static WritableMapHandle writableMap(File file, long fileOffsetBytes, long capacityBytes, ByteOrder byteOrder) {
+ Objects.requireNonNull(file, "file must be non-null.");
+ Objects.requireNonNull(byteOrder, "byteOrder must be non-null.");
+ if (!file.canWrite()) { throw new ReadOnlyException("file must be writable."); }
+ negativeCheck(file.length(), "file.length()");
+ negativeCheck(fileOffsetBytes, "fileOffsetBytes");
+ negativeCheck(capacityBytes, "capacityBytes");
+ return BaseWritableMemoryImpl.wrapMap(file, fileOffsetBytes, capacityBytes, false, byteOrder);
+ }
+
+ //ALLOCATE DIRECT
+ /**
+ * Allocates and provides access to capacityBytes directly in native (off-heap) memory.
+ * Native byte order is assumed.
+ * The allocated memory will be 8-byte aligned, but may not be page aligned.
+ *
+ * <p><b>NOTE:</b> Native/Direct memory acquired may have garbage in it.
+ * It is the responsibility of the using application to clear this memory, if required,
+ * and to call <i>close()</i> when done.</p>
+ *
+ * @param capacityBytes the size of the desired memory in bytes. It must be > 0.
+ * @return WritableHandle for this off-heap resource.
+ * Please read Javadocs for {@link Handle}.
+ */
+ static WritableHandle allocateDirect(long capacityBytes) {
+ return allocateDirect(capacityBytes, ByteOrder.nativeOrder(), defaultMemReqSvr);
+ }
+
+ /**
+ * Allocates and provides access to capacityBytes directly in native (off-heap) memory.
+ * The allocated memory will be 8-byte aligned, but may not be page aligned.
+ *
+ * <p><b>NOTE:</b> Native/Direct memory acquired may have garbage in it.
+ * It is the responsibility of the using application to clear this memory, if required,
+ * and to call <i>close()</i> when done.</p>
+ *
+ * @param capacityBytes the size of the desired memory in bytes. It must be > 0.
+ * @param byteOrder the given byte order. It must be non-null.
+ * @param memReqSvr A user-specified MemoryRequestServer, which may be null.
+ * This is a callback mechanism for a user client of direct memory to request more memory.
+ * @return WritableHandle for this off-heap resource.
+ * Please read Javadocs for {@link Handle}.
+ */
+ static WritableHandle allocateDirect(long capacityBytes, ByteOrder byteOrder, MemoryRequestServer memReqSvr) {
+ Objects.requireNonNull(byteOrder, "byteOrder must be non-null");
+ zeroCheck(capacityBytes, "capacityBytes");
+ return BaseWritableMemoryImpl.wrapDirect(capacityBytes, byteOrder, memReqSvr);
+ }
+
+ //REGIONS
+ /**
+ * A writable region is a writable view of this object.
+ * This returns a new <i>WritableMemory</i> representing the defined writable region with the
+ * given offsetBytes and capacityBytes.
+ * <ul>
+ * <li>Returned object's origin = this objects' origin + <i>offsetBytes</i></li>
+ * <li>Returned object's capacity = <i>capacityBytes</i></li>
+ * </ul>
+ *
+ * @param offsetBytes the starting offset with respect to this object. It must be ≥ 0.
+ * @param capacityBytes the capacity of the returned object in bytes. It must be > 0.
+ * @return a new <i>WritableMemory</i> representing the defined writable region.
+ */
+ default WritableMemory writableRegion(long offsetBytes, long capacityBytes) {
+ return writableRegion(offsetBytes, capacityBytes, getTypeByteOrder());
+ }
+
+ /**
+ * A writable region is a writable view of this object.
+ * This returns a new <i>WritableMemory</i> representing the defined writable region with the
+ * given offsetBytes, capacityBytes and byte order.
+ * <ul>
+ * <li>Returned object's origin = this objects' origin + <i>offsetBytes</i></li>
+ * <li>Returned object's capacity = <i>capacityBytes</i></li>
+ * <li>Returned object's byte order = <i>byteOrder</i></li>
+ * </ul>
+ *
+ * @param offsetBytes the starting offset with respect to this object. It must be ≥ 0.
+ * @param capacityBytes the capacity of the returned object in bytes. It must be > 0.
+ * @param byteOrder the given byte order. It must be non-null.
+ * @return a new <i>WritableMemory</i> representing the defined writable region.
+ */
+ WritableMemory writableRegion(long offsetBytes, long capacityBytes, ByteOrder byteOrder);
+
+ //AS WRITABLE BUFFER
+ /**
+ * Returns a new <i>WritableBuffer</i> with a writable view of this object.
+ * <ul>
+ * <li>Returned object's origin = this object's origin</li>
+ * <li>Returned object's <i>start</i> = 0</li>
+ * <li>Returned object's <i>position</i> = 0</li>
+ * <li>Returned object's <i>end</i> = this object's capacity</li>
+ * <li>Returned object's <i>capacity</i> = this object's capacity</li>
+ * <li>Returned object's <i>start</i>, <i>position</i> and <i>end</i> are mutable</li>
+ * </ul>
+ * @return a new <i>WritableBuffer</i> with a view of this WritableMemory
+ */
+ default WritableBuffer asWritableBuffer() {
+ return asWritableBuffer(getTypeByteOrder());
+ }
+
+ /**
+ * Returns a new <i>WritableBuffer</i> with a writable view of this object
+ * with the given byte order.
+ * <ul>
+ * <li>Returned object's origin = this object's origin</li>
+ * <li>Returned object's <i>start</i> = 0</li>
+ * <li>Returned object's <i>position</i> = 0</li>
+ * <li>Returned object's <i>end</i> = this object's capacity</li>
+ * <li>Returned object's <i>capacity</i> = this object's capacity</li>
+ * <li>Returned object's <i>start</i>, <i>position</i> and <i>end</i> are mutable</li>
+ * </ul>
+ * @param byteOrder the given byte order
+ * @return a new <i>WritableBuffer</i> with a view of this WritableMemory
+ */
+ WritableBuffer asWritableBuffer(ByteOrder byteOrder);
+
+
+ //ALLOCATE HEAP VIA AUTOMATIC BYTE ARRAY
+ /**
+ * Creates on-heap WritableMemory with the given capacity and the native byte order.
+ * @param capacityBytes the given capacity in bytes. It must be ≥ 0.
+ * @return a new WritableMemory for write operations on a new byte array.
+ */
+ static WritableMemory allocate(int capacityBytes) {
+ return allocate(capacityBytes, ByteOrder.nativeOrder(), defaultMemReqSvr);
+ }
+
+ /**
+ * Creates on-heap WritableMemory with the given capacity and the given byte order.
+ * @param capacityBytes the given capacity in bytes. It must be ≥ 0.
+ * @param byteOrder the given byte order to allocate new Memory object with. It must be non-null.
+ * @return a new WritableMemory for write operations on a new byte array.
+ */
+ static WritableMemory allocate(int capacityBytes, ByteOrder byteOrder) {
+ return allocate(capacityBytes, byteOrder, defaultMemReqSvr);
+ }
+
+ /**
+ * Creates on-heap WritableMemory with the given capacity and the given byte order.
+ * @param capacityBytes the given capacity in bytes. It must be ≥ 0.
+ * @param byteOrder the given byte order to allocate new Memory object with. It must be non-null.
+ * @param memReqSvr A user-specified <i>MemoryRequestServer</i>, which may be null.
+ * This is a callback mechanism for a user client to request a larger <i>WritableMemory</i>.
+ * @return a new WritableMemory for write operations on a new byte array.
+ */
+ static WritableMemory allocate(int capacityBytes, ByteOrder byteOrder, MemoryRequestServer memReqSvr) {
+ final byte[] arr = new byte[capacityBytes];
+ negativeCheck(capacityBytes, "capacityBytes");
+ return writableWrap(arr, 0, capacityBytes, byteOrder, memReqSvr);
+ }
+
+
+ //ACCESS PRIMITIVE HEAP ARRAYS for WRITE
+
+ /**
+ * Wraps the given primitive array for write operations assuming native byte order.
+ *
+ * <p><b>Note:</b> Always qualify this method with the class name, e.g.,
+ * <i>WritableMemory.wrap(...)</i>.
+ * @param array the given primitive array. It must be non-null, with length ≥ 0.
+ * @return a new WritableMemory for write operations on the given primitive array.
+ */
+ static WritableMemory writableWrap(byte[] array) {
+ return writableWrap(array, 0, array.length, ByteOrder.nativeOrder(), defaultMemReqSvr);
+ }
+
+ /**
+ * Wraps the given primitive array for write operations with the given byte order.
+ *
+ * <p><b>Note:</b> Always qualify this method with the class name, e.g.,
+ * <i>WritableMemory.wrap(...)</i>.
+ * @param array the given primitive array. It must be non-null, with length ≥ 0
+ * @param byteOrder the byte order to be used. It must be non-null.
+ * @return a new WritableMemory for write operations on the given primitive array.
+ */
+ static WritableMemory writableWrap(byte[] array, ByteOrder byteOrder) {
+ return writableWrap(array, 0, array.length, byteOrder, defaultMemReqSvr);
+ }
+
+ /**
+ * Wraps the given primitive array for write operations with the given byte order.
+ *
+ * <p><b>Note:</b> Always qualify this method with the class name, e.g.,
+ * <i>WritableMemory.wrap(...)</i>.
+ * @param array the given primitive array. It must be non-null.
+ * @param offsetBytes the byte offset into the given array. It must be ≥ 0.
+ * @param lengthBytes the number of bytes to include from the given array. It must be ≥ 0.
+ * @param byteOrder the byte order to be used. It must be non-null.
+ * @return a new WritableMemory for write operations on the given primitive array.
+ */
+ static WritableMemory writableWrap(byte[] array, int offsetBytes, int lengthBytes, ByteOrder byteOrder) {
+ return writableWrap(array, offsetBytes, lengthBytes, byteOrder, defaultMemReqSvr);
+ }
+
+ /**
+ * Wraps the given primitive array for write operations with the given byte order. If the given
+ * lengthBytes is zero, backing storage, byte order and read-only status of the returned
+ * WritableMemory object are unspecified.
+ *
+ * <p><b>Note:</b> Always qualify this method with the class name, e.g.,
+ * <i>WritableMemory.wrap(...)</i>.
+ * @param array the given primitive array. It must be non-null.
+ * @param offsetBytes the byte offset into the given array. It must be ≥ 0.
+ * @param lengthBytes the number of bytes to include from the given array. It must be ≥ 0.
+ * @param byteOrder the byte order to be used. It must be non-null.
+ * @param memReqSvr A user-specified <i>MemoryRequestServer</i>, which may be null.
+ * This is a callback mechanism for a user client to request a larger <i>WritableMemory</i>.
+ * @return a new WritableMemory for write operations on the given primitive array.
+ */
+ static WritableMemory writableWrap(byte[] array, int offsetBytes, int lengthBytes, ByteOrder byteOrder,
+ MemoryRequestServer memReqSvr) {
+ Objects.requireNonNull(array, "array must be non-null");
+ Objects.requireNonNull(byteOrder, "byteOrder must be non-null");
+ negativeCheck(offsetBytes, "offsetBytes");
+ negativeCheck(lengthBytes, "lengthBytes");
+ UnsafeUtil.checkBounds(offsetBytes, lengthBytes, array.length);
+ return BaseWritableMemoryImpl.wrapHeapArray(array, offsetBytes, lengthBytes, false, byteOrder, memReqSvr);
+ }
+
+ /**
+ * Wraps the given primitive array for write operations assuming native byte order.
+ * @param array the given primitive array. It must be non-null with length ≥ 0.
+ * @return a new WritableMemory for write operations on the given primitive array.
+ */
+ static WritableMemory writableWrap(boolean[] array) {
+ Objects.requireNonNull(array, "array must be non-null");
+ final long lengthBytes = array.length << Prim.BOOLEAN.shift();
+ return BaseWritableMemoryImpl.wrapHeapArray(array, 0, lengthBytes, false, ByteOrder.nativeOrder(), null);
+ }
+
+ /**
+ * Wraps the given primitive array for write operations assuming native byte order.
+ * @param array the given primitive array. It must be non-null with length ≥ 0.
+ * @return a new WritableMemory for write operations on the given primitive array.
+ */
+ static WritableMemory writableWrap(char[] array) {
+ Objects.requireNonNull(array, "array must be non-null");
+ final long lengthBytes = array.length << Prim.CHAR.shift();
+ return BaseWritableMemoryImpl.wrapHeapArray(array, 0L, lengthBytes, false, ByteOrder.nativeOrder(), null);
+ }
+
+ /**
+ * Wraps the given primitive array for write operations assuming native byte order.
+ * @param array the given primitive array. It must be non-null with length ≥ 0.
+ * @return a new WritableMemory for write operations on the given primitive array.
+ */
+ static WritableMemory writableWrap(short[] array) {
+ Objects.requireNonNull(array, "arr must be non-null");
+ final long lengthBytes = array.length << Prim.SHORT.shift();
+ return BaseWritableMemoryImpl.wrapHeapArray(array, 0L, lengthBytes, false, ByteOrder.nativeOrder(), null);
+ }
+
+ /**
+ * Wraps the given primitive array for write operations assuming native byte order.
+ * @param array the given primitive array. It must be non-null with length ≥ 0.
+ * @return a new WritableMemory for write operations on the given primitive array.
+ */
+ static WritableMemory writableWrap(int[] array) {
+ Objects.requireNonNull(array, "arr must be non-null");
+ final long lengthBytes = array.length << Prim.INT.shift();
+ return BaseWritableMemoryImpl.wrapHeapArray(array, 0L, lengthBytes, false, ByteOrder.nativeOrder(), null);
+ }
+
+ /**
+ * Wraps the given primitive array for write operations assuming native byte order.
+ * @param array the given primitive array. It must be non-null with length ≥ 0.
+ * @return a new WritableMemory for write operations on the given primitive array.
+ */
+ static WritableMemory writableWrap(long[] array) {
+ Objects.requireNonNull(array, "arr must be non-null");
+ final long lengthBytes = array.length << Prim.LONG.shift();
+ return BaseWritableMemoryImpl.wrapHeapArray(array, 0L, lengthBytes, false, ByteOrder.nativeOrder(), null);
+ }
+
+ /**
+ * Wraps the given primitive array for write operations assuming native byte order.
+ * @param array the given primitive array. It must be non-null with length ≥ 0.
+ * @return a new WritableMemory for write operations on the given primitive array.
+ */
+ static WritableMemory writableWrap(float[] array) {
+ Objects.requireNonNull(array, "arr must be non-null");
+ final long lengthBytes = array.length << Prim.FLOAT.shift();
+ return BaseWritableMemoryImpl.wrapHeapArray(array, 0L, lengthBytes, false, ByteOrder.nativeOrder(), null);
+ }
+
+ /**
+ * Wraps the given primitive array for write operations assuming native byte order.
+ * @param array the given primitive array. It must be non-null with length ≥ 0.
+ * @return a new WritableMemory for write operations on the given primitive array.
+ */
+ static WritableMemory writableWrap(double[] array) {
+ Objects.requireNonNull(array, "arr must be non-null");
+ final long lengthBytes = array.length << Prim.DOUBLE.shift();
+ return BaseWritableMemoryImpl.wrapHeapArray(array, 0L, lengthBytes, false, ByteOrder.nativeOrder(), null);
+ }
+ //END OF CONSTRUCTOR-TYPE METHODS
+
+ //PRIMITIVE putX() and putXArray()
+ /**
+ * Puts the boolean value at the given offset
+ * @param offsetBytes offset bytes relative to this <i>WritableMemory</i> start
+ * @param value the value to put
+ */
+ void putBoolean(long offsetBytes, boolean value);
+
+ /**
+ * Puts the boolean array at the given offset
+ * @param offsetBytes offset bytes relative to this <i>WritableMemory</i> start
+ * @param srcArray The source array.
+ * @param srcOffsetBooleans offset in array units
+ * @param lengthBooleans number of array units to transfer
+ */
+ void putBooleanArray(long offsetBytes, boolean[] srcArray, int srcOffsetBooleans, int lengthBooleans);
+
+ /**
+ * Puts the byte value at the given offset
+ * @param offsetBytes offset bytes relative to this <i>WritableMemory</i> start
+ * @param value the value to put
+ */
+ void putByte(long offsetBytes, byte value);
+
+ /**
+ * Puts the byte array at the given offset
+ * @param offsetBytes offset bytes relative to this <i>WritableMemory</i> start
+ * @param srcArray The source array.
+ * @param srcOffsetBytes offset in array units
+ * @param lengthBytes number of array units to transfer
+ */
+ void putByteArray(long offsetBytes, byte[] srcArray, int srcOffsetBytes, int lengthBytes);
+
+ /**
+ * Puts the char value at the given offset
+ * @param offsetBytes offset bytes relative to this <i>WritableMemory</i> start
+ * @param value the value to put
+ */
+ void putChar(long offsetBytes, char value);
+
+ /**
+ * Puts the char array at the given offset
+ * @param offsetBytes offset bytes relative to this <i>WritableMemory</i> start
+ * @param srcArray The source array.
+ * @param srcOffsetChars offset in array units
+ * @param lengthChars number of array units to transfer
+ */
+ void putCharArray(long offsetBytes, char[] srcArray, int srcOffsetChars, int lengthChars);
+
+ /**
+ * Encodes characters from the given CharSequence into UTF-8 bytes and puts them into this
+ * <i>WritableMemory</i> begining at the given offsetBytes.
+ * This is specifically designed to reduce the production of intermediate objects (garbage),
+ * thus significantly reducing pressure on the JVM Garbage Collector.
+ * @param offsetBytes offset bytes relative to this <i>WritableMemory</i> start
+ * @param src The source CharSequence to be encoded and put into this WritableMemory. It is
+ * the responsibility of the caller to provide sufficient capacity in this
+ * <i>WritableMemory</i> for the encoded Utf8 bytes. Characters outside the ASCII range can
+ * require 2, 3 or 4 bytes per character to encode.
+ * @return the number of bytes encoded
+ */
+ long putCharsToUtf8(long offsetBytes, CharSequence src);
+
+ /**
+ * Puts the double value at the given offset
+ * @param offsetBytes offset bytes relative to this <i>WritableMemory</i> start
+ * @param value the value to put
+ */
+ void putDouble(long offsetBytes, double value);
+
+ /**
+ * Puts the double array at the given offset
+ * @param offsetBytes offset bytes relative to this <i>WritableMemory</i> start
+ * @param srcArray The source array.
+ * @param srcOffsetDoubles offset in array units
+ * @param lengthDoubles number of array units to transfer
+ */
+ void putDoubleArray(long offsetBytes, double[] srcArray, int srcOffsetDoubles, int lengthDoubles);
+
+ /**
+ * Puts the float value at the given offset
+ * @param offsetBytes offset bytes relative to this <i>WritableMemory</i> start
+ * @param value the value to put
+ */
+ void putFloat(long offsetBytes, float value);
+
+ /**
+ * Puts the float array at the given offset
+ * @param offsetBytes offset bytes relative to this <i>WritableMemory</i> start
+ * @param srcArray The source array.
+ * @param srcOffsetFloats offset in array units
+ * @param lengthFloats number of array units to transfer
+ */
+ void putFloatArray(long offsetBytes, float[] srcArray, int srcOffsetFloats, int lengthFloats);
+
+ /**
+ * Puts the int value at the given offset
+ * @param offsetBytes offset bytes relative to this <i>WritableMemory</i> start
+ * @param value the value to put
+ */
+ void putInt(long offsetBytes, int value);
+
+ /**
+ * Puts the int array at the given offset
+ * @param offsetBytes offset bytes relative to this <i>WritableMemory</i> start
+ * @param srcArray The source array.
+ * @param srcOffsetInts offset in array units
+ * @param lengthInts number of array units to transfer
+ */
+ void putIntArray(long offsetBytes, int[] srcArray, int srcOffsetInts, int lengthInts);
+
+ /**
+ * Puts the long value at the given offset
+ * @param offsetBytes offset bytes relative to this <i>WritableMemory</i> start
+ * @param value the value to put
+ */
+ void putLong(long offsetBytes, long value);
+
+ /**
+ * Puts the long array at the given offset
+ * @param offsetBytes offset bytes relative to this <i>WritableMemory</i> start
+ * @param srcArray The source array.
+ * @param srcOffsetLongs offset in array units
+ * @param lengthLongs number of array units to transfer
+ */
+ void putLongArray(long offsetBytes, long[] srcArray, int srcOffsetLongs, int lengthLongs);
+
+ /**
+ * Puts the short value at the given offset
+ * @param offsetBytes offset bytes relative to this <i>WritableMemory</i> start
+ * @param value the value to put
+ */
+ void putShort(long offsetBytes, short value);
+
+ /**
+ * Puts the short array at the given offset
+ * @param offsetBytes offset bytes relative to this <i>WritableMemory</i> start
+ * @param srcArray The source array.
+ * @param srcOffsetShorts offset in array units
+ * @param lengthShorts number of array units to transfer
+ */
+ void putShortArray(long offsetBytes, short[] srcArray, int srcOffsetShorts, int lengthShorts);
+
+ //Atomic Methods
+ /**
+ * Atomically adds the given value to the long located at offsetBytes.
+ * @param offsetBytes offset bytes relative to this Memory start
+ * @param delta the amount to add
+ * @return the the previous value
+ */
+ long getAndAddLong(long offsetBytes, long delta);
+
+ /**
+ * Atomically sets the current value at the memory location to the given updated value
+ * if and only if the current value {@code ==} the expected value.
+ * @param offsetBytes offset bytes relative to this Memory start
+ * @param expect the expected value
+ * @param update the new value
+ * @return {@code true} if successful. False return indicates that
+ * the current value at the memory location was not equal to the expected value.
+ */
+ boolean compareAndSwapLong(long offsetBytes, long expect, long update);
+
+ /**
+ * Atomically exchanges the given value with the current value located at offsetBytes.
+ * @param offsetBytes offset bytes relative to this Memory start
+ * @param newValue new value
+ * @return the previous value
+ */
+ long getAndSetLong(long offsetBytes, long newValue);
+
+ //OTHER WRITE METHODS
+ /**
+ * Returns the primitive backing array, otherwise null.
+ * @return the primitive backing array, otherwise null.
+ */
+ Object getArray();
+
+ /**
+ * Clears all bytes of this Memory to zero
+ */
+ void clear();
+
+ /**
+ * Clears a portion of this Memory to zero.
+ * @param offsetBytes offset bytes relative to this Memory start
+ * @param lengthBytes the length in bytes
+ */
+ void clear(long offsetBytes, long lengthBytes);
+
+ /**
+ * Clears the bits defined by the bitMask
+ * @param offsetBytes offset bytes relative to this Memory start.
+ * @param bitMask the bits set to one will be cleared
+ */
+ void clearBits(long offsetBytes, byte bitMask);
+
+ /**
+ * Fills all bytes of this Memory region to the given byte value.
+ * @param value the given byte value
+ */
+ void fill(byte value);
+
+ /**
+ * Fills a portion of this Memory region to the given byte value.
+ * @param offsetBytes offset bytes relative to this Memory start
+ * @param lengthBytes the length in bytes
+ * @param value the given byte value
+ */
+ void fill(long offsetBytes, long lengthBytes, byte value);
+
+ /**
+ * Sets the bits defined by the bitMask
+ * @param offsetBytes offset bytes relative to this Memory start
+ * @param bitMask the bits set to one will be set
+ */
+ void setBits(long offsetBytes, byte bitMask);
+
+
+ //OTHER WRITABLE API METHODS
+ /**
+ * WritableMemory enables this for ByteBuffer, Heap and Direct Memory backed resources.
+ * Map backed resources will always return null.
+ * Gets the MemoryRequestServer object, if set, for the above resources to request additional memory.
+ * The user must customize the actions of the MemoryRequestServer by
+ * implementing the MemoryRequestServer interface and set using one of these methods:
+ * <ul><li>{@link WritableMemory#allocateDirect(long, ByteOrder, MemoryRequestServer)}</li>
+ * <li>{@link WritableMemory#allocate(int, ByteOrder, MemoryRequestServer)}</li>
+ * <li>{@link WritableMemory#writableWrap(ByteBuffer, ByteOrder, MemoryRequestServer)}</li>
+ * </ul>
+ * Simple implementation examples include the DefaultMemoryRequestServer in the main tree, as well as
+ * the ExampleMemoryRequestServerTest and the use with ByteBuffer documented in the DruidIssue11544Test
+ * in the test tree.
+ * @return the MemoryRequestServer object or null.
+ */
+ MemoryRequestServer getMemoryRequestServer();
+
+}
diff --git a/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/BaseStateImpl.java b/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/BaseStateImpl.java
new file mode 100644
index 0000000..bd6295e
--- /dev/null
+++ b/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/BaseStateImpl.java
@@ -0,0 +1,491 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.datasketches.memory.internal;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.datasketches.memory.BaseState;
+import org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.ReadOnlyException;
+
+/**
+ * Keeps key configuration state for MemoryImpl and BufferImpl plus some common static variables
+ * and check methods.
+ *
+ * @author Lee Rhodes
+ */
+@SuppressWarnings("restriction")
+public abstract class BaseStateImpl implements BaseState {
+
+ //Monitoring
+ static final AtomicLong currentDirectMemoryAllocations_ = new AtomicLong();
+ static final AtomicLong currentDirectMemoryAllocated_ = new AtomicLong();
+ static final AtomicLong currentDirectMemoryMapAllocations_ = new AtomicLong();
+ static final AtomicLong currentDirectMemoryMapAllocated_ = new AtomicLong();
+
+ //class type IDs. Do not change the bit orders
+ //The first 3 bits are set dynamically
+ // 0000 0XXX
+ static final int READONLY = 1;
+ static final int REGION = 2;
+ static final int DUPLICATE = 4;
+
+ //The following 4 bits are set by the 16 leaf nodes
+ // 000X X000
+ static final int HEAP = 0;
+ static final int DIRECT = 1 << 3;
+ static final int MAP = 2 << 3;
+ static final int BYTEBUF = 3 << 3;
+
+ // 00X0 0000
+ static final int NATIVE = 0;
+ static final int NONNATIVE = 1 << 5;
+
+ // 0X00 0000
+ static final int MEMORY = 0;
+ static final int BUFFER = 1 << 6;
+
+ private final long capacityBytes_;
+
+ /**
+ * This becomes the base offset used by all Unsafe calls. It is cumulative in that in includes
+ * all offsets from regions, user-defined offsets when creating MemoryImpl, and the array object
+ * header offset when creating MemoryImpl from primitive arrays.
+ */
+ private final long cumBaseOffset_;
+
+ /**
+ *
+ * @param unsafeObj The primitive backing array. It may be null. Used by Unsafe calls.
+ * @param nativeBaseOffset The off-heap memory address including DirectByteBuffer split offsets.
+ * @param regionOffset This offset defines address zero of this object (usually a region)
+ * relative to address zero of the backing resource. It is used to compute cumBaseOffset.
+ * This will be loaded from heap ByteBuffers, which have a similar field used for slices.
+ * It is used by region() and writableRegion().
+ * This offset does not include the size of an object array header, if there is one.
+ * @param capacityBytes the capacity of this object. Used by all methods when checking bounds.
+ */
+ BaseStateImpl(final Object unsafeObj, final long nativeBaseOffset, final long regionOffset,
+ final long capacityBytes) {
+ capacityBytes_ = capacityBytes;
+ cumBaseOffset_ = regionOffset + (unsafeObj == null
+ ? nativeBaseOffset
+ : UnsafeUtil.getArrayBaseOffset(unsafeObj.getClass()));
+ }
+
+ //Byte Order Related
+
+ @Override
+ public final ByteOrder getTypeByteOrder() {
+ return isNonNativeType() ? Util.NON_NATIVE_BYTE_ORDER : ByteOrder.nativeOrder();
+ }
+
+ /**
+ * Returns true if the given byteOrder is the same as the native byte order.
+ * @param byteOrder the given byte order
+ * @return true if the given byteOrder is the same as the native byte order.
+ */
+ public static boolean isNativeByteOrder(final ByteOrder byteOrder) {
+ if (byteOrder == null) {
+ throw new IllegalArgumentException("ByteOrder parameter cannot be null.");
+ }
+ return ByteOrder.nativeOrder() == byteOrder;
+ }
+
+ @Override
+ public final boolean isByteOrderCompatible(final ByteOrder byteOrder) {
+ final ByteOrder typeBO = getTypeByteOrder();
+ return typeBO == ByteOrder.nativeOrder() && typeBO == byteOrder;
+ }
+
+ @Override
+ public final boolean equals(final Object that) {
+ if (this == that) { return true; }
+ return that instanceof BaseStateImpl
+ ? CompareAndCopy.equals(this, (BaseStateImpl) that)
+ : false;
+ }
+
+ @Override
+ public final boolean equalTo(final long thisOffsetBytes, final Object that,
+ final long thatOffsetBytes, final long lengthBytes) {
+ return that instanceof BaseStateImpl
+ ? CompareAndCopy.equals(this, thisOffsetBytes, (BaseStateImpl) that, thatOffsetBytes, lengthBytes)
+ : false;
+ }
+
+ //Overridden by ByteBuffer Leafs
+ @Override
+ public ByteBuffer getByteBuffer() {
+ return null;
+ }
+
+ @Override
+ public final long getCapacity() {
+ assertValid();
+ return capacityBytes_;
+ }
+
+ @Override
+ public final long getCumulativeOffset() {
+ assertValid();
+ return cumBaseOffset_;
+ }
+
+ @Override
+ public final long getCumulativeOffset(final long offsetBytes) {
+ assertValid();
+ return cumBaseOffset_ + offsetBytes;
+ }
+
+ //Documented in WritableMemory and WritableBuffer interfaces.
+ //Implemented in the Leaf nodes; Required here by toHex(...).
+ abstract MemoryRequestServer getMemoryRequestServer();
+
+ //Overridden by ByteBuffer, Direct and Map leafs
+ long getNativeBaseOffset() {
+ return 0;
+ }
+
+ @Override
+ public final long getRegionOffset() {
+ final Object unsafeObj = getUnsafeObject();
+ return unsafeObj == null
+ ? cumBaseOffset_ - getNativeBaseOffset()
+ : cumBaseOffset_ - UnsafeUtil.getArrayBaseOffset(unsafeObj.getClass());
+ }
+
+ @Override
+ public final long getRegionOffset(final long offsetBytes) {
+ return getRegionOffset() + offsetBytes;
+ }
+
+ //Overridden by all leafs
+ abstract int getTypeId();
+
+ //Overridden by Heap and ByteBuffer Leafs. Made public as getArray() in WritableMemoryImpl and
+ // WritableBufferImpl
+ Object getUnsafeObject() {
+ return null;
+ }
+
+ @Override
+ public final boolean hasArray() {
+ assertValid();
+ return getUnsafeObject() != null;
+ }
+
+ @Override
+ public final int hashCode() {
+ return (int) xxHash64(0, capacityBytes_, 0); //xxHash64() calls checkValid()
+ }
+
+ @Override
+ public final long xxHash64(final long offsetBytes, final long lengthBytes, final long seed) {
+ checkValid();
+ return XxHash64.hash(getUnsafeObject(), cumBaseOffset_ + offsetBytes, lengthBytes, seed);
+ }
+
+ @Override
+ public final long xxHash64(final long in, final long seed) {
+ return XxHash64.hash(in, seed);
+ }
+
+ @Override
+ public final boolean hasByteBuffer() {
+ assertValid();
+ return getByteBuffer() != null;
+ }
+
+ @Override
+ public final boolean isDirect() {
+ return getUnsafeObject() == null;
+ }
+
+ @Override
+ public final boolean isReadOnly() {
+ assertValid();
+ return isReadOnlyType();
+ }
+
+ @Override
+ public final boolean isSameResource(final Object that) {
+ checkValid();
+ if (that == null) { return false; }
+ final BaseStateImpl that1 = (BaseStateImpl) that;
+ that1.checkValid();
+ if (this == that1) { return true; }
+
+ return cumBaseOffset_ == that1.cumBaseOffset_
+ && capacityBytes_ == that1.capacityBytes_
+ && getUnsafeObject() == that1.getUnsafeObject()
+ && getByteBuffer() == that1.getByteBuffer();
+ }
+
+ //Overridden by Direct and Map leafs
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+
+ //ASSERTS AND CHECKS
+ final void assertValid() {
+ assert isValid() : "MemoryImpl not valid.";
+ }
+
+ void checkValid() {
+ if (!isValid()) {
+ throw new IllegalStateException("MemoryImpl not valid.");
+ }
+ }
+
+ final void assertValidAndBoundsForRead(final long offsetBytes, final long lengthBytes) {
+ assertValid();
+ // capacityBytes_ is intentionally read directly instead of calling getCapacity()
+ // because the later can make JVM to not inline the assert code path (and entirely remove it)
+ // even though it does nothing in production code path.
+ assertBounds(offsetBytes, lengthBytes, capacityBytes_);
+ }
+
+ final void assertValidAndBoundsForWrite(final long offsetBytes, final long lengthBytes) {
+ assertValid();
+ // capacityBytes_ is intentionally read directly instead of calling getCapacity()
+ // because the later can make JVM to not inline the assert code path (and entirely remove it)
+ // even though it does nothing in production code path.
+ assertBounds(offsetBytes, lengthBytes, capacityBytes_);
+ assert !isReadOnly() : "MemoryImpl is read-only.";
+ }
+
+ @Override
+ public final void checkValidAndBounds(final long offsetBytes, final long lengthBytes) {
+ checkValid();
+ //read capacityBytes_ directly to eliminate extra checkValid() call
+ checkBounds(offsetBytes, lengthBytes, capacityBytes_);
+ }
+
+ final void checkValidAndBoundsForWrite(final long offsetBytes, final long lengthBytes) {
+ checkValid();
+ //read capacityBytes_ directly to eliminate extra checkValid() call
+ checkBounds(offsetBytes, lengthBytes, capacityBytes_);
+ if (isReadOnly()) {
+ throw new ReadOnlyException("MemoryImpl is read-only.");
+ }
+ }
+
+ //TYPE ID Management
+ final boolean isReadOnlyType() {
+ return (getTypeId() & READONLY) > 0;
+ }
+
+ final static byte setReadOnlyType(final byte type, final boolean readOnly) {
+ return (byte)((type & ~1) | (readOnly ? READONLY : 0));
+ }
+
+ final boolean isRegionType() {
+ return (getTypeId() & REGION) > 0;
+ }
+
+ final boolean isDuplicateType() {
+ return (getTypeId() & DUPLICATE) > 0;
+ }
+
+ //The following are set by the leaf nodes
+ final boolean isBufferType() {
+ return (getTypeId() & BUFFER) > 0;
+ }
+
+ final boolean isNonNativeType() {
+ return (getTypeId() & NONNATIVE) > 0;
+ }
+
+ final boolean isHeapType() {
+ return (getTypeId() >>> 3 & 3) == 0;
+ }
+
+ final boolean isDirectType() {
+ return (getTypeId() >>> 3 & 3) == 1;
+ }
+
+ final boolean isMapType() {
+ return (getTypeId() >>> 3 & 3) == 2;
+ }
+
+ final boolean isBBType() {
+ return (getTypeId() >>> 3 & 3) == 3;
+ }
+
+
+ //TO STRING
+ /**
+ * Decodes the resource type. This is primarily for debugging.
+ * @param typeId the given typeId
+ * @return a human readable string.
+ */
+ public static final String typeDecode(final int typeId) {
+ final StringBuilder sb = new StringBuilder();
+ final int group1 = typeId & 0x7;
+ switch (group1) {
+ case 1 : sb.append("ReadOnly, "); break;
+ case 2 : sb.append("Region, "); break;
+ case 3 : sb.append("ReadOnly Region, "); break;
+ case 4 : sb.append("Duplicate, "); break;
+ case 5 : sb.append("ReadOnly Duplicate, "); break;
+ case 6 : sb.append("Region Duplicate, "); break;
+ case 7 : sb.append("ReadOnly Region Duplicate, "); break;
+ default: break;
+ }
+ final int group2 = (typeId >>> 3) & 0x3;
+ switch (group2) {
+ case 0 : sb.append("Heap, "); break;
+ case 1 : sb.append("Direct, "); break;
+ case 2 : sb.append("Map, "); break;
+ case 3 : sb.append("ByteBuffer, "); break;
+ default: break;
+ }
+ final int group3 = (typeId >>> 5) & 0x1;
+ switch (group3) {
+ case 0 : sb.append("Native, "); break;
+ case 1 : sb.append("NonNative, "); break;
+ default: break;
+ }
+ final int group4 = (typeId >>> 6) & 0x1;
+ switch (group4) {
+ case 0 : sb.append("Memory"); break;
+ case 1 : sb.append("Buffer"); break;
+ default: break;
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public final String toHexString(final String header, final long offsetBytes,
+ final int lengthBytes) {
+ checkValid();
+ final String klass = this.getClass().getSimpleName();
+ final String s1 = String.format("(..., %d, %d)", offsetBytes, lengthBytes);
+ final long hcode = hashCode() & 0XFFFFFFFFL;
+ final String call = ".toHexString" + s1 + ", hashCode: " + hcode;
+ final StringBuilder sb = new StringBuilder();
+ sb.append("### ").append(klass).append(" SUMMARY ###").append(LS);
+ sb.append("Header Comment : ").append(header).append(LS);
+ sb.append("Call Parameters : ").append(call);
+ return toHex(this, sb.toString(), offsetBytes, lengthBytes);
+ }
+
+ /**
+ * Returns a formatted hex string of an area of this object.
+ * Used primarily for testing.
+ * @param state the BaseStateImpl
+ * @param preamble a descriptive header
+ * @param offsetBytes offset bytes relative to the MemoryImpl start
+ * @param lengthBytes number of bytes to convert to a hex string
+ * @return a formatted hex string in a human readable array
+ */
+ static final String toHex(final BaseStateImpl state, final String preamble, final long offsetBytes,
+ final int lengthBytes) {
+ final long capacity = state.getCapacity();
+ UnsafeUtil.checkBounds(offsetBytes, lengthBytes, capacity);
+ final StringBuilder sb = new StringBuilder();
+ final Object uObj = state.getUnsafeObject();
+ final String uObjStr;
+ final long uObjHeader;
+ if (uObj == null) {
+ uObjStr = "null";
+ uObjHeader = 0;
+ } else {
+ uObjStr = uObj.getClass().getSimpleName() + ", " + (uObj.hashCode() & 0XFFFFFFFFL);
+ uObjHeader = UnsafeUtil.getArrayBaseOffset(uObj.getClass());
+ }
+ final ByteBuffer bb = state.getByteBuffer();
+ final String bbStr = bb == null ? "null"
+ : bb.getClass().getSimpleName() + ", " + (bb.hashCode() & 0XFFFFFFFFL);
+ final MemoryRequestServer memReqSvr = state.getMemoryRequestServer();
+ final String memReqStr = memReqSvr != null
+ ? memReqSvr.getClass().getSimpleName() + ", " + (memReqSvr.hashCode() & 0XFFFFFFFFL)
+ : "null";
+ final long cumBaseOffset = state.getCumulativeOffset();
+ sb.append(preamble).append(LS);
+ sb.append("UnsafeObj, hashCode : ").append(uObjStr).append(LS);
+ sb.append("UnsafeObjHeader : ").append(uObjHeader).append(LS);
+ sb.append("ByteBuf, hashCode : ").append(bbStr).append(LS);
+ sb.append("RegionOffset : ").append(state.getRegionOffset()).append(LS);
+ sb.append("Capacity : ").append(capacity).append(LS);
+ sb.append("CumBaseOffset : ").append(cumBaseOffset).append(LS);
+ sb.append("MemReq, hashCode : ").append(memReqStr).append(LS);
+ sb.append("Valid : ").append(state.isValid()).append(LS);
+ sb.append("Read Only : ").append(state.isReadOnly()).append(LS);
+ sb.append("Type Byte Order : ").append(state.getTypeByteOrder().toString()).append(LS);
+ sb.append("Native Byte Order : ").append(ByteOrder.nativeOrder().toString()).append(LS);
+ sb.append("JDK Runtime Version : ").append(UnsafeUtil.JDK).append(LS);
+ //Data detail
+ sb.append("Data, littleEndian : 0 1 2 3 4 5 6 7");
+
+ for (long i = 0; i < lengthBytes; i++) {
+ final int b = unsafe.getByte(uObj, cumBaseOffset + offsetBytes + i) & 0XFF;
+ if (i % 8 == 0) { //row header
+ sb.append(String.format("%n%20s: ", offsetBytes + i));
+ }
+ sb.append(String.format("%02x ", b));
+ }
+ sb.append(LS);
+
+ return sb.toString();
+ }
+
+ //MONITORING
+
+ /**
+ * Gets the current number of active direct memory allocations.
+ * @return the current number of active direct memory allocations.
+ */
+ public static final long getCurrentDirectMemoryAllocations() {
+ return BaseStateImpl.currentDirectMemoryAllocations_.get();
+ }
+
+ /**
+ * Gets the current size of active direct memory allocated.
+ * @return the current size of active direct memory allocated.
+ */
+ public static final long getCurrentDirectMemoryAllocated() {
+ return BaseStateImpl.currentDirectMemoryAllocated_.get();
+ }
+
+ /**
+ * Gets the current number of active direct memory map allocations.
+ * @return the current number of active direct memory map allocations.
+ */
+ public static final long getCurrentDirectMemoryMapAllocations() {
+ return BaseStateImpl.currentDirectMemoryMapAllocations_.get();
+ }
+
+ /**
+ * Gets the current size of active direct memory map allocated.
+ * @return the current size of active direct memory map allocated.
+ */
+ public static final long getCurrentDirectMemoryMapAllocated() {
+ return BaseStateImpl.currentDirectMemoryMapAllocated_.get();
+ }
+
+ //REACHABILITY FENCE
+ static void reachabilityFence(@SuppressWarnings("unused") final Object obj) { }
+
+}
diff --git a/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/CompareAndCopy.java b/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/CompareAndCopy.java
new file mode 100644
index 0000000..cb5b9b0
--- /dev/null
+++ b/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/CompareAndCopy.java
@@ -0,0 +1,522 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.datasketches.memory.internal;
+
+import static org.apache.datasketches.memory.internal.UnsafeUtil.CHAR_SHIFT;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.DOUBLE_SHIFT;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.FLOAT_SHIFT;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.INT_SHIFT;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.LONG_SHIFT;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.SHORT_SHIFT;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.checkBounds;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.unsafe;
+import static org.apache.datasketches.memory.internal.Util.UNSAFE_COPY_THRESHOLD_BYTES;
+
+/**
+ * @author Lee Rhodes
+ */
+@SuppressWarnings("restriction")
+final class CompareAndCopy {
+
+ private CompareAndCopy() { }
+
+ static int compare(
+ final BaseStateImpl state1, final long offsetBytes1, final long lengthBytes1,
+ final BaseStateImpl state2, final long offsetBytes2, final long lengthBytes2) {
+ state1.checkValid();
+ checkBounds(offsetBytes1, lengthBytes1, state1.getCapacity());
+ state2.checkValid();
+ checkBounds(offsetBytes2, lengthBytes2, state2.getCapacity());
+ final long cumOff1 = state1.getCumulativeOffset(offsetBytes1);
+ final long cumOff2 = state2.getCumulativeOffset(offsetBytes2);
+ final Object arr1 = state1.getUnsafeObject();
+ final Object arr2 = state2.getUnsafeObject();
+ if ((arr1 != arr2) || (cumOff1 != cumOff2)) {
+ final long lenBytes = Math.min(lengthBytes1, lengthBytes2);
+ for (long i = 0; i < lenBytes; i++) {
+ final int byte1 = unsafe.getByte(arr1, cumOff1 + i);
+ final int byte2 = unsafe.getByte(arr2, cumOff2 + i);
+ if (byte1 < byte2) { return -1; }
+ if (byte1 > byte2) { return 1; }
+ }
+ }
+ return Long.compare(lengthBytes1, lengthBytes2);
+ }
+
+ static boolean equals(final BaseStateImpl state1, final BaseStateImpl state2) {
+ final long cap1 = state1.getCapacity();
+ final long cap2 = state2.getCapacity();
+ return (cap1 == cap2) && equals(state1, 0, state2, 0, cap1);
+ }
+
+ //Developer notes: this is subtlely different from (campare == 0) in that this has an early
+ // stop if the arrays and offsets are the same as there is only one length. Also this can take
+ // advantage of chunking with longs, while compare cannot.
+ static boolean equals(
+ final BaseStateImpl state1, final long offsetBytes1,
+ final BaseStateImpl state2, final long offsetBytes2, long lengthBytes) {
+ state1.checkValid();
+ checkBounds(offsetBytes1, lengthBytes, state1.getCapacity());
+ state2.checkValid();
+ checkBounds(offsetBytes2, lengthBytes, state2.getCapacity());
+ long cumOff1 = state1.getCumulativeOffset(offsetBytes1);
+ long cumOff2 = state2.getCumulativeOffset(offsetBytes2);
+ final Object arr1 = state1.getUnsafeObject(); //could be null
+ final Object arr2 = state2.getUnsafeObject(); //could be null
+ if ((arr1 == arr2) && (cumOff1 == cumOff2)) { return true; }
+
+ while (lengthBytes >= Long.BYTES) {
+ final int chunk = (int) Math.min(lengthBytes, UNSAFE_COPY_THRESHOLD_BYTES);
+ // int-counted loop to avoid safepoint polls (otherwise why we chunk by
+ // UNSAFE_COPY_MEMORY_THRESHOLD)
+ int i = 0;
+ for (; i <= (chunk - Long.BYTES); i += Long.BYTES) {
+ final long v1 = unsafe.getLong(arr1, cumOff1 + i);
+ final long v2 = unsafe.getLong(arr2, cumOff2 + i);
+ if (v1 != v2) { return false; }
+ }
+ lengthBytes -= i;
+ cumOff1 += i;
+ cumOff2 += i;
+ }
+ //check the remainder bytes, if any
+ return (lengthBytes == 0) || equalsByBytes(arr1, cumOff1, arr2, cumOff2, (int) lengthBytes);
+ }
+
+ //use only for short runs
+ private static boolean equalsByBytes(final Object arr1, final long cumOff1, final Object arr2,
+ final long cumOff2, final int lenBytes) {
+ // int-counted loop to avoid safepoint polls
+ for (int i = 0; i < lenBytes; i++) {
+ final int v1 = unsafe.getByte(arr1, cumOff1 + i);
+ final int v2 = unsafe.getByte(arr2, cumOff2 + i);
+ if (v1 != v2) { return false; }
+ }
+ return true;
+ }
+
+ static void copy(final BaseStateImpl srcState, final long srcOffsetBytes,
+ final BaseStateImpl dstState, final long dstOffsetBytes, final long lengthBytes) {
+ srcState.checkValid();
+ checkBounds(srcOffsetBytes, lengthBytes, srcState.getCapacity());
+ dstState.checkValid();
+ checkBounds(dstOffsetBytes, lengthBytes, dstState.getCapacity());
+ final long srcAdd = srcState.getCumulativeOffset(srcOffsetBytes);
+ final long dstAdd = dstState.getCumulativeOffset(dstOffsetBytes);
+ copyMemory(srcState.getUnsafeObject(), srcAdd, dstState.getUnsafeObject(), dstAdd,
+ lengthBytes);
+ }
+
+ //Used by all of the get/put array methods in BufferImpl and MemoryImpl classes
+ static final void copyMemoryCheckingDifferentObject(final Object srcUnsafeObj,
+ final long srcAdd, final Object dstUnsafeObj, final long dstAdd, final long lengthBytes) {
+ if (srcUnsafeObj != dstUnsafeObj) {
+ copyNonOverlappingMemoryWithChunking(srcUnsafeObj, srcAdd, dstUnsafeObj, dstAdd,
+ lengthBytes);
+ } else {
+ throw new IllegalArgumentException("Not expecting to copy to/from array which is the "
+ + "underlying object of the memory at the same time");
+ }
+ }
+
+ //only valid and bounds checks have been performed at this point
+ private static void copyMemory(final Object srcUnsafeObj, final long srcAdd,
+ final Object dstUnsafeObj, final long dstAdd, final long lengthBytes) {
+ if (srcUnsafeObj != dstUnsafeObj) {
+ //either srcArray != dstArray OR one of them is off-heap
+ copyNonOverlappingMemoryWithChunking(srcUnsafeObj, srcAdd, dstUnsafeObj, dstAdd,
+ lengthBytes);
+ } else { //either srcArray == dstArray OR both src and dst are off-heap
+ copyMemoryOverlapAddressCheck(srcUnsafeObj, srcAdd, dstUnsafeObj, dstAdd, lengthBytes);
+ }
+ }
+
+ /**
+ * At this point either srcArray == dstArray OR both src and dst are off-heap.
+ * Performs overlapping address check. If addresses do not overlap, proceed to
+ * {@link #copyNonOverlappingMemoryWithChunking(Object, long, Object, long, long)}; otherwise
+ * fall back on <i>Unsafe.copyMemory(...)</i> tolerating potentially long
+ * Time to Safe Point pauses.
+ * If srcAdd == dstAdd an exception will be thrown.
+ * @param srcUnsafeObj The source array object, it may be null.
+ * @param srcAdd The cumulative source offset
+ * @param dstUnsafeObj The destination array object, it may be null
+ * @param dstAdd The cumulative destination offset
+ * @param lengthBytes The length to be copied in bytes
+ */
+ private static void copyMemoryOverlapAddressCheck(final Object srcUnsafeObj, final long srcAdd,
+ final Object dstUnsafeObj, final long dstAdd, final long lengthBytes) {
+ if (((srcAdd + lengthBytes) <= dstAdd) || ((dstAdd + lengthBytes) <= srcAdd)) {
+ copyNonOverlappingMemoryWithChunking(srcUnsafeObj, srcAdd, dstUnsafeObj, dstAdd,
+ lengthBytes);
+ return;
+ }
+ if (srcAdd == dstAdd) {
+ throw new IllegalArgumentException(
+ "Attempt to copy a block of memory exactly in-place, should be a bug");
+ }
+ // If regions do overlap, fall back to unsafe.copyMemory, tolerating potentially long
+ // Time to Safe Point pauses.
+ unsafe.copyMemory(srcUnsafeObj, srcAdd, dstUnsafeObj, dstAdd, lengthBytes);
+ }
+
+ /**
+ * This copies only non-overlapping memory in chunks to avoid safepoint delays.
+ * Java 9 may not require the chunking.
+ * @param srcUnsafeObj The source array object, it may be null.
+ * @param srcAdd The cumulative source offset
+ * @param dstUnsafeObj The destination array object, it may be null
+ * @param dstAdd The cumulative destination offset
+ * @param lengthBytes The length to be copied in bytes
+ * @see #UNSAFE_COPY_THRESHOLD_BYTES
+ */
+ private static void copyNonOverlappingMemoryWithChunking(final Object srcUnsafeObj,
+ long srcAdd, final Object dstUnsafeObj, long dstAdd, long lengthBytes) {
+ while (lengthBytes > 0) {
+ final long chunk = Math.min(lengthBytes, UNSAFE_COPY_THRESHOLD_BYTES);
+ unsafe.copyMemory(srcUnsafeObj, srcAdd, dstUnsafeObj, dstAdd, chunk);
+ lengthBytes -= chunk;
+ srcAdd += chunk;
+ dstAdd += chunk;
+ }
+ }
+
+ static void getNonNativeChars(final Object unsafeObj, long cumOffsetBytes,
+ long copyBytes, final char[] dstArray, int dstOffsetChars,
+ int lengthChars) {
+ checkBounds(dstOffsetChars, lengthChars, dstArray.length);
+ while (copyBytes > UNSAFE_COPY_THRESHOLD_BYTES) {
+ final long chunkBytes = Math.min(copyBytes, UNSAFE_COPY_THRESHOLD_BYTES);
+ final int chunkChars = (int) (chunkBytes >> CHAR_SHIFT);
+ getCharArrayChunk(unsafeObj, cumOffsetBytes, dstArray, dstOffsetChars, chunkChars);
+ cumOffsetBytes += chunkBytes;
+ dstOffsetChars += chunkChars;
+ copyBytes -= chunkBytes;
+ lengthChars -= chunkChars;
+ }
+ getCharArrayChunk(unsafeObj, cumOffsetBytes, dstArray, dstOffsetChars, lengthChars);
+ }
+
+ private static void getCharArrayChunk(final Object unsafeObj, final long cumOffsetBytes,
+ final char[] dstArray, final int dstOffsetChars, final int lengthChars) {
+ // JDK 9 adds native intrinsics for such bulk non-native ordered primitive memory copy.
+ // TODO-JDK9 use them when the library adds support for JDK 9
+ // int-counted loop to avoid safepoint polls
+ for (int i = 0; i < lengthChars; i++) {
+ dstArray[dstOffsetChars + i] = Character.reverseBytes(
+ unsafe.getChar(unsafeObj, cumOffsetBytes + (((long) i) << CHAR_SHIFT)));
+ }
+ }
+
+ static void getNonNativeDoubles(final Object unsafeObj, long cumOffsetBytes,
+ long copyBytes, final double[] dstArray, int dstOffsetDoubles,
+ int lengthDoubles) {
+ checkBounds(dstOffsetDoubles, lengthDoubles, dstArray.length);
+ while (copyBytes > UNSAFE_COPY_THRESHOLD_BYTES) {
+ final long chunkBytes = Math.min(copyBytes, UNSAFE_COPY_THRESHOLD_BYTES);
+ final int chunkDoubles = (int) (chunkBytes >> DOUBLE_SHIFT);
+ getDoubleArrayChunk(unsafeObj, cumOffsetBytes,
+ dstArray, dstOffsetDoubles, chunkDoubles);
+ cumOffsetBytes += chunkBytes;
+ dstOffsetDoubles += chunkDoubles;
+ copyBytes -= chunkBytes;
+ lengthDoubles -= chunkDoubles;
+ }
+ getDoubleArrayChunk(unsafeObj, cumOffsetBytes,
+ dstArray, dstOffsetDoubles, lengthDoubles);
+ }
+
+ private static void getDoubleArrayChunk(final Object unsafeObj, final long cumOffsetBytes,
+ final double[] dstArray, final int dstOffsetDoubles, final int lengthDoubles) {
+ // JDK 9 adds native intrinsics for such bulk non-native ordered primitive memory copy.
+ // TODO-JDK9 use them when the library adds support for JDK 9
+ // int-counted loop to avoid safepoint polls
+ for (int i = 0; i < lengthDoubles; i++) {
+ dstArray[dstOffsetDoubles + i] = Double.longBitsToDouble(Long.reverseBytes(
+ unsafe.getLong(unsafeObj, cumOffsetBytes + (((long) i) << DOUBLE_SHIFT))));
+ }
+ }
+
+ static void getNonNativeFloats(final Object unsafeObj, long cumOffsetBytes,
+ long copyBytes, final float[] dstArray, int dstOffsetFloats,
+ int lengthFloats) {
+ checkBounds(dstOffsetFloats, lengthFloats, dstArray.length);
+ while (copyBytes > UNSAFE_COPY_THRESHOLD_BYTES) {
+ final long chunkBytes = Math.min(copyBytes, UNSAFE_COPY_THRESHOLD_BYTES);
+ final int chunkFloats = (int) (chunkBytes >> FLOAT_SHIFT);
+ getFloatArrayChunk(unsafeObj, cumOffsetBytes, dstArray, dstOffsetFloats, chunkFloats);
+ cumOffsetBytes += chunkBytes;
+ dstOffsetFloats += chunkFloats;
+ copyBytes -= chunkBytes;
+ lengthFloats -= chunkFloats;
+ }
+ getFloatArrayChunk(unsafeObj, cumOffsetBytes, dstArray, dstOffsetFloats, lengthFloats);
+ }
+
+ private static void getFloatArrayChunk(final Object unsafeObj, final long cumOffsetBytes,
+ final float[] dstArray, final int dstOffsetFloats, final int lengthFloats) {
+ // JDK 9 adds native intrinsics for such bulk non-native ordered primitive memory copy.
+ // TODO-JDK9 use them when the library adds support for JDK 9
+ // int-counted loop to avoid safepoint polls
+ for (int i = 0; i < lengthFloats; i++) {
+ dstArray[dstOffsetFloats + i] = Float.intBitsToFloat(Integer.reverseBytes(
+ unsafe.getInt(unsafeObj, cumOffsetBytes + (((long) i) << FLOAT_SHIFT))));
+ }
+ }
+
+ static void getNonNativeInts(final Object unsafeObj, long cumOffsetBytes,
+ long copyBytes, final int[] dstArray, int dstOffsetInts,
+ int lengthInts) {
+ checkBounds(dstOffsetInts, lengthInts, dstArray.length);
+ while (copyBytes > UNSAFE_COPY_THRESHOLD_BYTES) {
+ final long chunkBytes = Math.min(copyBytes, UNSAFE_COPY_THRESHOLD_BYTES);
+ final int chunkInts = (int) (chunkBytes >> INT_SHIFT);
+ getIntArrayChunk(unsafeObj, cumOffsetBytes, dstArray, dstOffsetInts, chunkInts);
+ cumOffsetBytes += chunkBytes;
+ dstOffsetInts += chunkInts;
+ copyBytes -= chunkBytes;
+ lengthInts -= chunkInts;
+ }
+ getIntArrayChunk(unsafeObj, cumOffsetBytes, dstArray, dstOffsetInts, lengthInts);
+ }
+
+ private static void getIntArrayChunk(final Object unsafeObj, final long cumOffsetBytes,
+ final int[] dstArray, final int dstOffsetInts, final int lengthInts) {
+ // JDK 9 adds native intrinsics for such bulk non-native ordered primitive memory copy.
+ // TODO-JDK9 use them when the library adds support for JDK 9
+ // int-counted loop to avoid safepoint polls
+ for (int i = 0; i < lengthInts; i++) {
+ dstArray[dstOffsetInts + i] = Integer.reverseBytes(
+ unsafe.getInt(unsafeObj, cumOffsetBytes + (((long) i) << INT_SHIFT)));
+ }
+ }
+
+ static void getNonNativeLongs(final Object unsafeObj, long cumOffsetBytes,
+ long copyBytes, final long[] dstArray, int dstOffsetLongs,
+ int lengthLongs) {
+ checkBounds(dstOffsetLongs, lengthLongs, dstArray.length);
+ while (copyBytes > UNSAFE_COPY_THRESHOLD_BYTES) {
+ final long chunkBytes = Math.min(copyBytes, UNSAFE_COPY_THRESHOLD_BYTES);
+ final int chunkLongs = (int) (chunkBytes >> LONG_SHIFT);
+ getLongArrayChunk(unsafeObj, cumOffsetBytes, dstArray, dstOffsetLongs, chunkLongs);
+ cumOffsetBytes += chunkBytes;
+ dstOffsetLongs += chunkLongs;
+ copyBytes -= chunkBytes;
+ lengthLongs -= chunkLongs;
+ }
+ getLongArrayChunk(unsafeObj, cumOffsetBytes, dstArray, dstOffsetLongs, lengthLongs);
+ }
+
+ private static void getLongArrayChunk(final Object unsafeObj, final long cumOffsetBytes,
+ final long[] dstArray, final int dstOffsetLongs, final int lengthLongs) {
+ // JDK 9 adds native intrinsics for such bulk non-native ordered primitive memory copy.
+ // TODO-JDK9 use them when the library adds support for JDK 9
+ // int-counted loop to avoid safepoint polls
+ for (int i = 0; i < lengthLongs; i++) {
+ dstArray[dstOffsetLongs + i] = Long.reverseBytes(
+ unsafe.getLong(unsafeObj, cumOffsetBytes + (((long) i) << LONG_SHIFT)));
+ }
+ }
+
+ static void getNonNativeShorts(final Object unsafeObj, long cumOffsetBytes,
+ long copyBytes, final short[] dstArray, int dstOffsetShorts,
+ int lengthShorts) {
+ checkBounds(dstOffsetShorts, lengthShorts, dstArray.length);
+ while (copyBytes > UNSAFE_COPY_THRESHOLD_BYTES) {
+ final long chunkBytes = Math.min(copyBytes, UNSAFE_COPY_THRESHOLD_BYTES);
+ final int chunkShorts = (int) (chunkBytes >> SHORT_SHIFT);
+ getShortArrayChunk(unsafeObj, cumOffsetBytes, dstArray, dstOffsetShorts, chunkShorts);
+ cumOffsetBytes += chunkBytes;
+ dstOffsetShorts += chunkShorts;
+ copyBytes -= chunkBytes;
+ lengthShorts -= chunkShorts;
+ }
+ getShortArrayChunk(unsafeObj, cumOffsetBytes, dstArray, dstOffsetShorts, lengthShorts);
+ }
+
+ private static void getShortArrayChunk(final Object unsafeObj, final long cumOffsetBytes,
+ final short[] dstArray, final int dstOffsetShorts, final int lengthShorts) {
+ // JDK 9 adds native intrinsics for such bulk non-native ordered primitive memory copy.
+ // TODO-JDK9 use them when the library adds support for JDK 9
+ // int-counted loop to avoid safepoint polls
+ for (int i = 0; i < lengthShorts; i++) {
+ dstArray[dstOffsetShorts + i] = Short.reverseBytes(
+ unsafe.getShort(unsafeObj, cumOffsetBytes + (((long) i) << SHORT_SHIFT)));
+ }
+ }
+
+ static void putNonNativeChars(final char[] srcArray, int srcOffsetChars, int lengthChars,
+ long copyBytes, final Object unsafeObj, long cumOffsetBytes) {
+ checkBounds(srcOffsetChars, lengthChars, srcArray.length);
+ while (copyBytes > UNSAFE_COPY_THRESHOLD_BYTES) {
+ final long chunkBytes = Math.min(copyBytes, UNSAFE_COPY_THRESHOLD_BYTES);
+ final int chunkChars = (int) (chunkBytes >> CHAR_SHIFT);
+ putCharArrayChunk(srcArray, srcOffsetChars, chunkChars, unsafeObj, cumOffsetBytes);
+ cumOffsetBytes += chunkBytes;
+ srcOffsetChars += chunkChars;
+ copyBytes -= chunkBytes;
+ lengthChars -= chunkChars;
+ }
+ putCharArrayChunk(srcArray, srcOffsetChars, lengthChars, unsafeObj, cumOffsetBytes);
+ }
+
+ private static void putCharArrayChunk(final char[] srcArray, final int srcOffsetChars,
+ final int lengthChars, final Object unsafeObj, final long cumOffsetBytes) {
+ // JDK 9 adds native intrinsics for such bulk non-native ordered primitive memory copy.
+ // TODO-JDK9 use them when the library adds support for JDK 9
+ // int-counted loop to avoid safepoint polls
+ for (int i = 0; i < lengthChars; i++) {
+ unsafe.putChar(unsafeObj, cumOffsetBytes + (((long) i) << CHAR_SHIFT),
+ Character.reverseBytes(srcArray[srcOffsetChars + i]));
+ }
+ }
+
+ static void putNonNativeDoubles(final double[] srcArray, int srcOffsetDoubles,
+ int lengthDoubles, long copyBytes, final Object unsafeObj, long cumOffsetBytes) {
+ checkBounds(srcOffsetDoubles, lengthDoubles, srcArray.length);
+ while (copyBytes > UNSAFE_COPY_THRESHOLD_BYTES) {
+ final long chunkBytes = Math.min(copyBytes, UNSAFE_COPY_THRESHOLD_BYTES);
+ final int chunkDoubles = (int) (chunkBytes >> DOUBLE_SHIFT);
+ putDoubleArrayChunk(srcArray, srcOffsetDoubles, chunkDoubles,
+ unsafeObj, cumOffsetBytes);
+ cumOffsetBytes += chunkBytes;
+ srcOffsetDoubles += chunkDoubles;
+ copyBytes -= chunkBytes;
+ lengthDoubles -= chunkDoubles;
+ }
+ putDoubleArrayChunk(srcArray, srcOffsetDoubles, lengthDoubles,
+ unsafeObj, cumOffsetBytes);
+ }
+
+ private static void putDoubleArrayChunk(final double[] srcArray, final int srcOffsetDoubles,
+ final int lengthDoubles, final Object unsafeObj, final long cumOffsetBytes) {
+ // JDK 9 adds native intrinsics for such bulk non-native ordered primitive memory copy.
+ // TODO-JDK9 use them when the library adds support for JDK 9
+ // int-counted loop to avoid safepoint polls
+ for (int i = 0; i < lengthDoubles; i++) {
+ unsafe.putLong(unsafeObj, cumOffsetBytes + (((long) i) << DOUBLE_SHIFT),
+ Long.reverseBytes(Double.doubleToRawLongBits(srcArray[srcOffsetDoubles + i])));
+ }
+ }
+
+ static void putNonNativeFloats(final float[] srcArray, int srcOffsetFloats,
+ int lengthFloats, long copyBytes, final Object unsafeObj, long cumOffsetBytes) {
+ checkBounds(srcOffsetFloats, lengthFloats, srcArray.length);
+ while (copyBytes > UNSAFE_COPY_THRESHOLD_BYTES) {
+ final long chunkBytes = Math.min(copyBytes, UNSAFE_COPY_THRESHOLD_BYTES);
+ final int chunkFloats = (int) (chunkBytes >> FLOAT_SHIFT);
+ putFloatArrayChunk(srcArray, srcOffsetFloats, chunkFloats, unsafeObj, cumOffsetBytes);
+ cumOffsetBytes += chunkBytes;
+ srcOffsetFloats += chunkFloats;
+ copyBytes -= chunkBytes;
+ lengthFloats -= chunkFloats;
+ }
+ putFloatArrayChunk(srcArray, srcOffsetFloats, lengthFloats, unsafeObj, cumOffsetBytes);
+ }
+
+ private static void putFloatArrayChunk(final float[] srcArray, final int srcOffsetFloats,
+ final int lengthFloats, final Object unsafeObj, final long cumOffsetBytes) {
+ // JDK 9 adds native intrinsics for such bulk non-native ordered primitive memory copy.
+ // TODO-JDK9 use them when the library adds support for JDK 9
+ // int-counted loop to avoid safepoint polls
+ for (int i = 0; i < lengthFloats; i++) {
+ unsafe.putInt(unsafeObj, cumOffsetBytes + (((long) i) << FLOAT_SHIFT),
+ Integer.reverseBytes(Float.floatToRawIntBits(srcArray[srcOffsetFloats + i])));
+ }
+ }
+
+ static void putNonNativeInts(final int[] srcArray, int srcOffsetInts, int lengthInts,
+ long copyBytes, final Object unsafeObj, long cumOffsetBytes) {
+ checkBounds(srcOffsetInts, lengthInts, srcArray.length);
+ while (copyBytes > UNSAFE_COPY_THRESHOLD_BYTES) {
+ final long chunkBytes = Math.min(copyBytes, UNSAFE_COPY_THRESHOLD_BYTES);
+ final int chunkInts = (int) (chunkBytes >> INT_SHIFT);
+ putIntArrayChunk(srcArray, srcOffsetInts, chunkInts, unsafeObj, cumOffsetBytes);
+ cumOffsetBytes += chunkBytes;
+ srcOffsetInts += chunkInts;
+ copyBytes -= chunkBytes;
+ lengthInts -= chunkInts;
+ }
+ putIntArrayChunk(srcArray, srcOffsetInts, lengthInts, unsafeObj, cumOffsetBytes);
+ }
+
+ private static void putIntArrayChunk(final int[] srcArray, final int srcOffsetInts,
+ final int lengthInts, final Object unsafeObj, final long cumOffsetBytes) {
+ // JDK 9 adds native intrinsics for such bulk non-native ordered primitive memory copy.
+ // TODO-JDK9 use them when the library adds support for JDK 9
+ // int-counted loop to avoid safepoint polls
+ for (int i = 0; i < lengthInts; i++) {
+ unsafe.putInt(unsafeObj, cumOffsetBytes + (((long) i) << INT_SHIFT),
+ Integer.reverseBytes(srcArray[srcOffsetInts + i]));
+ }
+ }
+
+ static void putNonNativeLongs(final long[] srcArray, int srcOffsetLongs, int lengthLongs,
+ long copyBytes, final Object unsafeObj, long cumOffsetBytes) {
+ checkBounds(srcOffsetLongs, lengthLongs, srcArray.length);
+ while (copyBytes > UNSAFE_COPY_THRESHOLD_BYTES) {
+ final long chunkBytes = Math.min(copyBytes, UNSAFE_COPY_THRESHOLD_BYTES);
+ final int chunkLongs = (int) (chunkBytes >> LONG_SHIFT);
+ putLongArrayChunk(srcArray, srcOffsetLongs, chunkLongs, unsafeObj, cumOffsetBytes);
+ cumOffsetBytes += chunkBytes;
+ srcOffsetLongs += chunkLongs;
+ copyBytes -= chunkBytes;
+ lengthLongs -= chunkLongs;
+ }
+ putLongArrayChunk(srcArray, srcOffsetLongs, lengthLongs, unsafeObj, cumOffsetBytes);
+ }
+
+ private static void putLongArrayChunk(final long[] srcArray, final int srcOffsetLongs,
+ final int lengthLongs, final Object unsafeObj, final long cumOffsetBytes) {
+ // JDK 9 adds native intrinsics for such bulk non-native ordered primitive memory copy.
+ // TODO-JDK9 use them when the library adds support for JDK 9
+ // int-counted loop to avoid safepoint polls
+ for (int i = 0; i < lengthLongs; i++) {
+ unsafe.putLong(unsafeObj, cumOffsetBytes + (((long) i) << LONG_SHIFT),
+ Long.reverseBytes(srcArray[srcOffsetLongs + i]));
+ }
+ }
+
+ static void putNonNativeShorts(final short[] srcArray, int srcOffsetShorts,
+ int lengthShorts, long copyBytes, final Object unsafeObj, long cumOffsetBytes) {
+ checkBounds(srcOffsetShorts, lengthShorts, srcArray.length);
+ while (copyBytes > UNSAFE_COPY_THRESHOLD_BYTES) {
+ final long chunkBytes = Math.min(copyBytes, UNSAFE_COPY_THRESHOLD_BYTES);
+ final int chunkShorts = (int) (chunkBytes >> SHORT_SHIFT);
+ putShortArrayChunk(srcArray, srcOffsetShorts, chunkShorts, unsafeObj, cumOffsetBytes);
+ cumOffsetBytes += chunkBytes;
+ srcOffsetShorts += chunkShorts;
+ copyBytes -= chunkBytes;
+ lengthShorts -= chunkShorts;
+ }
+ putShortArrayChunk(srcArray, srcOffsetShorts, lengthShorts, unsafeObj, cumOffsetBytes);
+ }
+
+ private static void putShortArrayChunk(final short[] srcArray, final int srcOffsetShorts,
+ final int lengthShorts, final Object unsafeObj, final long cumOffsetBytes) {
+ // JDK 9 adds native intrinsics for such bulk non-native ordered primitive memory copy.
+ // TODO-JDK9 use them when the library adds support for JDK 9
+ // int-counted loop to avoid safepoint polls
+ for (int i = 0; i < lengthShorts; i++) {
+ unsafe.putShort(unsafeObj, cumOffsetBytes + (((long) i) << SHORT_SHIFT),
+ Short.reverseBytes(srcArray[srcOffsetShorts + i]));
+ }
+ }
+}
diff --git a/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/UnsafeUtil.java b/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/UnsafeUtil.java
new file mode 100644
index 0000000..67c1975
--- /dev/null
+++ b/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/UnsafeUtil.java
@@ -0,0 +1,228 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.datasketches.memory.internal;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+import sun.misc.Unsafe;
+
+/**
+ * Provides access to the sun.misc.Unsafe class and its key static fields.
+ *
+ * @author Lee Rhodes
+ */
+@SuppressWarnings({"restriction", "javadoc"})
+public final class UnsafeUtil {
+ public static final Unsafe unsafe;
+ public static final String JDK; //must be at least "1.8"
+ public static final int JDK_MAJOR; //8, 9, 10, 11, 12, etc
+
+ //not an indicator of whether compressed references are used.
+ public static final int ADDRESS_SIZE;
+ public static final int PAGE_SIZE;
+
+ //For 64-bit JVMs: these offsets vary depending on coop: 16 for JVM <= 32GB; 24 for JVM > 32GB.
+ // Making this constant long-typed, rather than int, to exclude possibility of accidental overflow
+ // in expressions like arrayLength * ARRAY_BYTE_BASE_OFFSET, where arrayLength is int-typed.
+ // The same consideration for constants below: ARRAY_*_INDEX_SCALE, ARRAY_*_INDEX_SHIFT.
+ public static final long ARRAY_BOOLEAN_BASE_OFFSET;
+ public static final long ARRAY_BYTE_BASE_OFFSET;
+ public static final long ARRAY_SHORT_BASE_OFFSET;
+ public static final long ARRAY_CHAR_BASE_OFFSET;
+ public static final long ARRAY_INT_BASE_OFFSET;
+ public static final long ARRAY_LONG_BASE_OFFSET;
+ public static final long ARRAY_FLOAT_BASE_OFFSET;
+ public static final long ARRAY_DOUBLE_BASE_OFFSET;
+ public static final long ARRAY_OBJECT_BASE_OFFSET;
+
+ //@formatter:off
+
+ // Setting those values directly instead of using unsafe.arrayIndexScale(), because it may be
+ // beneficial for runtime execution, those values are backed into generated machine code as
+ // constants. E. g. see https://shipilev.net/jvm-anatomy-park/14-constant-variables/
+ public static final int ARRAY_BOOLEAN_INDEX_SCALE = 1;
+ public static final int ARRAY_BYTE_INDEX_SCALE = 1;
+ public static final long ARRAY_SHORT_INDEX_SCALE = 2;
+ public static final long ARRAY_CHAR_INDEX_SCALE = 2;
+ public static final long ARRAY_INT_INDEX_SCALE = 4;
+ public static final long ARRAY_LONG_INDEX_SCALE = 8;
+ public static final long ARRAY_FLOAT_INDEX_SCALE = 4;
+ public static final long ARRAY_DOUBLE_INDEX_SCALE = 8;
+ public static final long ARRAY_OBJECT_INDEX_SCALE; // varies, 4 or 8 depending on coop
+
+ //Used to convert "type" to bytes: bytes = longs << LONG_SHIFT
+ public static final int BOOLEAN_SHIFT = 0;
+ public static final int BYTE_SHIFT = 0;
+ public static final long SHORT_SHIFT = 1;
+ public static final long CHAR_SHIFT = 1;
+ public static final long INT_SHIFT = 2;
+ public static final long LONG_SHIFT = 3;
+ public static final long FLOAT_SHIFT = 2;
+ public static final long DOUBLE_SHIFT = 3;
+ public static final long OBJECT_SHIFT; // varies, 2 or 3 depending on coop
+
+ public static final String LS = System.getProperty("line.separator");
+
+ //@formatter:on
+
+ static {
+ try {
+ final Constructor<Unsafe> unsafeConstructor = Unsafe.class.getDeclaredConstructor();
+ unsafeConstructor.setAccessible(true);
+ unsafe = unsafeConstructor.newInstance();
+
+ // Alternative, but may not work across different JVMs.
+ // Field field = Unsafe.class.getDeclaredField("theUnsafe");
+ // field.setAccessible(true);
+ // unsafe = (Unsafe) field.get(null);
+
+ } catch (final InstantiationException | IllegalAccessException | IllegalArgumentException
+ | InvocationTargetException | NoSuchMethodException e) {
+ e.printStackTrace();
+ throw new RuntimeException("Unable to acquire Unsafe. " + e);
+ }
+
+ //4 on 32-bit systems. 4 on 64-bit systems < 32GB, otherwise 8.
+ //This alone is not an indicator of compressed ref (coop)
+ ADDRESS_SIZE = unsafe.addressSize();
+ PAGE_SIZE = unsafe.pageSize();
+
+ ARRAY_BOOLEAN_BASE_OFFSET = unsafe.arrayBaseOffset(boolean[].class);
+ ARRAY_BYTE_BASE_OFFSET = unsafe.arrayBaseOffset(byte[].class);
+ ARRAY_SHORT_BASE_OFFSET = unsafe.arrayBaseOffset(short[].class);
+ ARRAY_CHAR_BASE_OFFSET = unsafe.arrayBaseOffset(char[].class);
+ ARRAY_INT_BASE_OFFSET = unsafe.arrayBaseOffset(int[].class);
+ ARRAY_LONG_BASE_OFFSET = unsafe.arrayBaseOffset(long[].class);
+ ARRAY_FLOAT_BASE_OFFSET = unsafe.arrayBaseOffset(float[].class);
+ ARRAY_DOUBLE_BASE_OFFSET = unsafe.arrayBaseOffset(double[].class);
+ ARRAY_OBJECT_BASE_OFFSET = unsafe.arrayBaseOffset(Object[].class);
+
+ ARRAY_OBJECT_INDEX_SCALE = unsafe.arrayIndexScale(Object[].class);
+ OBJECT_SHIFT = ARRAY_OBJECT_INDEX_SCALE == 4 ? 2 : 3;
+
+ final String jdkVer = System.getProperty("java.version");
+ final int[] p = parseJavaVersion(jdkVer);
+ JDK = p[0] + "." + p[1];
+ JDK_MAJOR = (p[0] == 1) ? p[1] : p[0];
+ }
+
+ private UnsafeUtil() {}
+
+ /**
+ * Returns first two number groups of the java version string.
+ * @param jdkVer the java version string from System.getProperty("java.version").
+ * @return first two number groups of the java version string.
+ */
+ public static int[] parseJavaVersion(final String jdkVer) {
+ final int p0, p1;
+ try {
+ String[] parts = jdkVer.trim().split("[^0-9\\.]");//grab only number groups and "."
+ parts = parts[0].split("\\."); //split out the number groups
+ p0 = Integer.parseInt(parts[0]); //the first number group
+ p1 = (parts.length > 1) ? Integer.parseInt(parts[1]) : 0; //2nd number group, or 0
+ } catch (final NumberFormatException | ArrayIndexOutOfBoundsException e) {
+ throw new IllegalArgumentException("Improper Java -version string: " + jdkVer + "\n" + e);
+ }
+ //checkJavaVersion(jdkVer, p0, p1); //TODO Optional to omit this.
+ return new int[] {p0, p1};
+ }
+
+ public static void checkJavaVersion(final String jdkVer, final int p0, final int p1) {
+ if ( (p0 < 1) || ((p0 == 1) && (p1 < 8)) || (p0 > 13) ) {
+ throw new IllegalArgumentException(
+ "Unsupported JDK Major Version, must be one of 1.8, 8, 9, 10, 11, 12, 13: " + jdkVer);
+ }
+ }
+
+ public static long getFieldOffset(final Class<?> c, final String fieldName) {
+ try {
+ return unsafe.objectFieldOffset(c.getDeclaredField(fieldName));
+ } catch (final NoSuchFieldException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ /**
+ * Like {@link Unsafe#arrayBaseOffset(Class)}, but caches return values for common array types.
+ * Useful because calling {@link Unsafe#arrayBaseOffset(Class)} directly incurs more overhead.
+ * @param c The given Class<?>.
+ * @return the base-offset
+ */
+ public static long getArrayBaseOffset(final Class<?> c) {
+ // Ordering here is roughly in order of what we expect to be most popular.
+ if (c == byte[].class) {
+ return ARRAY_BYTE_BASE_OFFSET;
+ } else if (c == int[].class) {
+ return ARRAY_INT_BASE_OFFSET;
+ } else if (c == long[].class) {
+ return ARRAY_LONG_BASE_OFFSET;
+ } else if (c == float[].class) {
+ return ARRAY_FLOAT_BASE_OFFSET;
+ } else if (c == double[].class) {
+ return ARRAY_DOUBLE_BASE_OFFSET;
+ } else if (c == boolean[].class) {
+ return ARRAY_BOOLEAN_BASE_OFFSET;
+ } else if (c == short[].class) {
+ return ARRAY_SHORT_BASE_OFFSET;
+ } else if (c == char[].class) {
+ return ARRAY_CHAR_BASE_OFFSET;
+ } else if (c == Object[].class) {
+ return ARRAY_OBJECT_BASE_OFFSET;
+ } else {
+ return unsafe.arrayBaseOffset(c);
+ }
+ }
+
+ public static long pageCount(final long bytes) {
+ return (int)((bytes + PAGE_SIZE) - 1L) / PAGE_SIZE;
+ }
+
+ /**
+ * Assert the requested offset and length against the allocated size.
+ * The invariants equation is: {@code 0 <= reqOff <= reqLen <= reqOff + reqLen <= allocSize}.
+ * If this equation is violated and assertions are enabled, an {@link AssertionError} will
+ * be thrown.
+ * @param reqOff the requested offset
+ * @param reqLen the requested length
+ * @param allocSize the allocated size.
+ */
+ public static void assertBounds(final long reqOff, final long reqLen, final long allocSize) {
+ assert ((reqOff | reqLen | (reqOff + reqLen) | (allocSize - (reqOff + reqLen))) >= 0) :
+ "reqOffset: " + reqOff + ", reqLength: " + reqLen
+ + ", (reqOff + reqLen): " + (reqOff + reqLen) + ", allocSize: " + allocSize;
+ }
+
+ /**
+ * Check the requested offset and length against the allocated size.
+ * The invariants equation is: {@code 0 <= reqOff <= reqLen <= reqOff + reqLen <= allocSize}.
+ * If this equation is violated an {@link IllegalArgumentException} will be thrown.
+ * @param reqOff the requested offset
+ * @param reqLen the requested length
+ * @param allocSize the allocated size.
+ */
+ public static void checkBounds(final long reqOff, final long reqLen, final long allocSize) {
+ if ((reqOff | reqLen | (reqOff + reqLen) | (allocSize - (reqOff + reqLen))) < 0) {
+ throw new IllegalArgumentException(
+ "reqOffset: " + reqOff + ", reqLength: " + reqLen
+ + ", (reqOff + reqLen): " + (reqOff + reqLen) + ", allocSize: " + allocSize);
+ }
+ }
+}
diff --git a/datasketches-memory-java8/src/main/java/org/apache/datasketches/memory/internal/Util.java b/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/Util.java
similarity index 94%
copy from datasketches-memory-java8/src/main/java/org/apache/datasketches/memory/internal/Util.java
copy to datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/Util.java
index d8768a4..0c02198 100644
--- a/datasketches-memory-java8/src/main/java/org/apache/datasketches/memory/internal/Util.java
+++ b/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/Util.java
@@ -19,6 +19,8 @@
package org.apache.datasketches.memory.internal;
+import static jdk.incubator.foreign.MemoryAccess.getLongAtIndex;
+
import java.io.File;
import java.io.IOException;
import java.net.URI;
@@ -29,7 +31,7 @@ import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Random;
-import org.apache.datasketches.memory.Memory;
+import jdk.incubator.foreign.MemorySegment;
/**
* @author Lee Rhodes
@@ -81,9 +83,9 @@ public final class Util {
* search algorithm. The range must be sorted method) prior to making this call.
* If it is not sorted, the results are undefined. If the range contains
* multiple elements with the specified value, there is no guarantee which one will be found.
- * @param mem the Memory to be searched
- * @param fromLongIndex the index of the first element (inclusive) to be searched
- * @param toLongIndex the index of the last element (exclusive) to be searched
+ * @param seg the MemorySegment to be searched
+ * @param fromLongIndex the index of the first long element (inclusive) to be searched
+ * @param toLongIndex the index of the last long element (exclusive) to be searched
* @param key the value to be searched for
* @return index of the search key, if it is contained in the array within the specified range;
* otherwise, (-(insertion point) - 1). The insertion point is defined as the point at which
@@ -91,15 +93,15 @@ public final class Util {
* than the key, or toIndex if all elements in the range are less than the specified key.
* Note that this guarantees that the return value will be ≥ 0 if and only if the key is found.
*/
- public static long binarySearchLongs(final Memory mem, final long fromLongIndex,
+ public static long binarySearchLongs(final MemorySegment seg, final long fromLongIndex,
final long toLongIndex, final long key) {
- UnsafeUtil.checkBounds(fromLongIndex << 3, (toLongIndex - fromLongIndex) << 3, mem.getCapacity());
+ UnsafeUtil.checkBounds(fromLongIndex << 3, (toLongIndex - fromLongIndex) << 3, seg.byteSize());
long low = fromLongIndex;
long high = toLongIndex - 1L;
while (low <= high) {
final long mid = (low + high) >>> 1;
- final long midVal = mem.getLong(mid << 3);
+ final long midVal = getLongAtIndex(seg, mid);
if (midVal < key) { low = mid + 1; }
else if (midVal > key) { high = mid - 1; }
@@ -224,7 +226,7 @@ public final class Util {
* Fills the given array with random valid Code Points from 0, inclusive, to
* <i>Character.MAX_CODE_POINT</i>, inclusive.
* The surrogate range, which is from <i>Character.MIN_SURROGATE</i>, inclusive, to
- * <i>Character.MAX_SURROGATE</i>, inclusive, is always <u>excluded</u>.
+ * <i>Character.MAX_SURROGATE</i>, inclusive, is always <i>excluded</i>.
* @param cpArr the array to fill
*/
public final void fillCodePointArray(final int[] cpArr) {
@@ -235,7 +237,7 @@ public final class Util {
* Fills the given array with random valid Code Points from <i>startCP</i>, inclusive, to
* <i>endCP</i>, exclusive.
* The surrogate range, which is from <i>Character.MIN_SURROGATE</i>, inclusive, to
- * <i>Character.MAX_SURROGATE</i>, inclusive, is always <u>excluded</u>.
+ * <i>Character.MAX_SURROGATE</i>, inclusive, is always <i>excluded</i>.
* @param cpArr the array to fill
* @param startCP the starting Code Point, included.
* @param endCP the ending Code Point, excluded. This value cannot exceed 0x110000.
@@ -257,7 +259,7 @@ public final class Util {
* Return a single valid random Code Point from 0, inclusive, to
* <i>Character.MAX_CODE_POINT</i>, inclusive.
* The surrogate range, which is from <i>Character.MIN_SURROGATE</i>, inclusive, to
- * <i>Character.MAX_SURROGATE</i>, inclusive, is always <u>excluded</u>.
+ * <i>Character.MAX_SURROGATE</i>, inclusive, is always <i>excluded</i>.
* @return a single valid random CodePoint.
*/
public final int getCodePoint() {
@@ -268,7 +270,7 @@ public final class Util {
* Return a single valid random Code Point from <i>startCP</i>, inclusive, to
* <i>endCP</i>, exclusive.
* The surrogate range, which is from <i>Character.MIN_SURROGATE</i>, inclusive, to
- * <i>Character.MAX_SURROGATE</i>, inclusive, is always <u>excluded</u>.
+ * <i>Character.MAX_SURROGATE</i>, inclusive, is always <i>excluded</i>.
* @param startCP the starting Code Point, included.
* @param endCP the ending Code Point, excluded. This value cannot exceed 0x110000.
* @return a single valid random CodePoint.
diff --git a/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/Dummy.java b/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/package-info.java
similarity index 85%
copy from datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/Dummy.java
copy to datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/package-info.java
index aefdcba..769cee0 100644
--- a/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/Dummy.java
+++ b/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/package-info.java
@@ -17,11 +17,4 @@
* under the License.
*/
-package org.apache.datasketches.memory.internal;
-
-/**
- * Temporary class & placeholder. It may not be needed.
- */
-public class Dummy {
-
-}
+package org.apache.datasketches.memory;
diff --git a/datasketches-memory-java8/src/main/java/org/apache/datasketches/memory/internal/AllocateDirectMap.java b/datasketches-memory-java8/src/main/java/org/apache/datasketches/memory/internal/AllocateDirectMap.java
index 997e7e0..744b3af 100644
--- a/datasketches-memory-java8/src/main/java/org/apache/datasketches/memory/internal/AllocateDirectMap.java
+++ b/datasketches-memory-java8/src/main/java/org/apache/datasketches/memory/internal/AllocateDirectMap.java
@@ -67,13 +67,13 @@ class AllocateDirectMap implements Map {
static final Method MAPPED_BYTE_BUFFER_FORCE0_METHOD;
static {
- try { //The FileChannelImpl methods map0 and unmap0 still exist in 16
+ try { //The FileChannelImpl methods map0 and unmap0 still exist in 17, however...
FILE_CHANNEL_IMPL_MAP0_METHOD = FileChannelImpl.class
- .getDeclaredMethod("map0", int.class, long.class, long.class); //JDK14 add boolean.class
+ .getDeclaredMethod("map0", int.class, long.class, long.class); //JDK14-17 adds boolean.class
FILE_CHANNEL_IMPL_MAP0_METHOD.setAccessible(true);
FILE_CHANNEL_IMPL_UNMAP0_METHOD = FileChannelImpl.class
- .getDeclaredMethod("unmap0", long.class, long.class); //OK through jDK16
+ .getDeclaredMethod("unmap0", long.class, long.class); //OK through jDK17
FILE_CHANNEL_IMPL_UNMAP0_METHOD.setAccessible(true);
diff --git a/datasketches-memory-java8/src/main/java/org/apache/datasketches/memory/internal/MemoryCleaner.java b/datasketches-memory-java8/src/main/java/org/apache/datasketches/memory/internal/MemoryCleaner.java
index 33053eb..88ffc01 100644
--- a/datasketches-memory-java8/src/main/java/org/apache/datasketches/memory/internal/MemoryCleaner.java
+++ b/datasketches-memory-java8/src/main/java/org/apache/datasketches/memory/internal/MemoryCleaner.java
@@ -22,11 +22,8 @@ package org.apache.datasketches.memory.internal;
import sun.misc.Cleaner;
/**
- * Extracts a version-dependent reference to the `sun.misc.Cleaner` into
- * a standalone class. The package name for Cleaner has changed in
- * later versions. The appropriate class will be loaded by the class loader
- * depending on the Java version that is used.
- * For more information, see: https://openjdk.java.net/jeps/238
+ * The package name and API for Cleaner changed from Java 8 to Java 9+.
+ * This extracts the java 8 `sun.misc.Cleaner` into a standalone class.
*/
@SuppressWarnings("restriction")
public class MemoryCleaner {
diff --git a/datasketches-memory-java8/src/main/java/org/apache/datasketches/memory/internal/Util.java b/datasketches-memory-java8/src/main/java/org/apache/datasketches/memory/internal/Util.java
index d8768a4..2ec1d72 100644
--- a/datasketches-memory-java8/src/main/java/org/apache/datasketches/memory/internal/Util.java
+++ b/datasketches-memory-java8/src/main/java/org/apache/datasketches/memory/internal/Util.java
@@ -224,7 +224,7 @@ public final class Util {
* Fills the given array with random valid Code Points from 0, inclusive, to
* <i>Character.MAX_CODE_POINT</i>, inclusive.
* The surrogate range, which is from <i>Character.MIN_SURROGATE</i>, inclusive, to
- * <i>Character.MAX_SURROGATE</i>, inclusive, is always <u>excluded</u>.
+ * <i>Character.MAX_SURROGATE</i>, inclusive, is always <i>excluded</i>.
* @param cpArr the array to fill
*/
public final void fillCodePointArray(final int[] cpArr) {
diff --git a/datasketches-memory-java9/src/main/java/module-info.java b/datasketches-memory-java9/src/main/java/module-info.java
index 990b45d..78c9ce2 100644
--- a/datasketches-memory-java9/src/main/java/module-info.java
+++ b/datasketches-memory-java9/src/main/java/module-info.java
@@ -22,5 +22,5 @@ module org.apache.datasketches.memory {
requires java.logging;
requires jdk.unsupported;
- exports org.apache.datasketches.memory.internal;
+ exports org.apache.datasketches.memory;
}
diff --git a/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/Dummy.java b/datasketches-memory-java9/src/main/java/org/apache/datasketches/memory/Map.java
similarity index 57%
copy from datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/Dummy.java
copy to datasketches-memory-java9/src/main/java/org/apache/datasketches/memory/Map.java
index aefdcba..6b99059 100644
--- a/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/Dummy.java
+++ b/datasketches-memory-java9/src/main/java/org/apache/datasketches/memory/Map.java
@@ -17,11 +17,30 @@
* under the License.
*/
-package org.apache.datasketches.memory.internal;
+package org.apache.datasketches.memory;
/**
- * Temporary class & placeholder. It may not be needed.
+ * Read only interface for a memory mapped file
+ *
+ * @author Roman Leventov
+ * @author Lee Rhodes
+ * @author Praveenkumar Venkatesan
*/
-public class Dummy {
+public interface Map extends AutoCloseable {
+
+ /**
+ * @see <a href="https://docs.oracle.com/javase/8/docs/api/java/nio/MappedByteBuffer.html#load--">
+ * java/nio/MappedByteBuffer.load</a>
+ */
+ void load();
+
+ /**
+ * @return true if loaded
+ *
+ * @see <a href=
+ * "https://docs.oracle.com/javase/8/docs/api/java/nio/MappedByteBuffer.html#isLoaded--"> java
+ * /nio/MappedByteBuffer.isLoaded</a>
+ */
+ boolean isLoaded();
}
diff --git a/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/Dummy.java b/datasketches-memory-java9/src/main/java/org/apache/datasketches/memory/MemoryException.java
similarity index 55%
copy from datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/Dummy.java
copy to datasketches-memory-java9/src/main/java/org/apache/datasketches/memory/MemoryException.java
index aefdcba..6b3663c 100644
--- a/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/Dummy.java
+++ b/datasketches-memory-java9/src/main/java/org/apache/datasketches/memory/MemoryException.java
@@ -17,11 +17,26 @@
* under the License.
*/
-package org.apache.datasketches.memory.internal;
+package org.apache.datasketches.memory;
/**
- * Temporary class & placeholder. It may not be needed.
+ * Specific RuntimeExceptions for the datasketches-memory component.
+ *
+ * @author Lee Rhodes
+ *
*/
-public class Dummy {
+public class MemoryException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+ /**
+ * Constructs a new runtime exception with the specified detail message. The cause is not
+ * initialized, and may subsequently be initialized by a call to
+ * Throwable.initCause(java.lang.Throwable).
+ *
+ * @param message the detail message. The detail message is saved for later retrieval by the
+ * Throwable.getMessage() method.
+ */
+ public MemoryException(final String message) {
+ super(message);
+ }
}
diff --git a/datasketches-memory-java9/src/main/java/org/apache/datasketches/memory/internal/MemoryCleaner.java b/datasketches-memory-java9/src/main/java/org/apache/datasketches/memory/internal/MemoryCleaner.java
index 448f365..ebadf9f 100644
--- a/datasketches-memory-java9/src/main/java/org/apache/datasketches/memory/internal/MemoryCleaner.java
+++ b/datasketches-memory-java9/src/main/java/org/apache/datasketches/memory/internal/MemoryCleaner.java
@@ -19,32 +19,30 @@
package org.apache.datasketches.memory.internal;
-import jdk.internal.ref.Cleaner;
+import java.lang.ref.Cleaner;
/**
- * Extracts a version-dependent reference to the `jdk.internal.ref.Cleaner` into
- * a standalone class. The package name for Cleaner has changed in
- * later versions. The appropriate class will be loaded by the class loader
- * depending on the Java version that is used.
- * For more information, see: https://openjdk.java.net/jeps/238
+ * The package name and API for Cleaner changed from Java 8 to Java 9+.
+ * This extracts the java 9+ `java.lang.ref.Cleaner` into a standalone class.
*/
public class MemoryCleaner {
- private final Cleaner cleaner;
+ private final Cleaner cleaner = Cleaner.create();
+ private final Cleaner.Cleanable cleanable;
- /**
- * Creates a new `jdk.internal.ref.Cleaner`.
- * @param referent the object to be cleaned
- * @param deallocator - the cleanup code to be run when the cleaner is invoked.
- * return MemoryCleaner
- */
- public MemoryCleaner(final Object referent, final Runnable deallocator) {
- cleaner = Cleaner.create(referent, deallocator);
- }
+ /**
+ * Creates a new `java.lang.ref.Cleaner`.
+ * @param referent the object to be cleaned
+ * @param deallocator - the cleanup code to be run when the cleaner is invoked.
+ * return MemoryCleaner
+ */
+ public MemoryCleaner(final Object referent, final Runnable deallocator) {
+ this.cleanable = cleaner.register(referent, deallocator);
+ }
- /**
- * Runs this cleaner, if it has not been run before.
- */
+ /**
+ * Runs this cleaner, if it has not been run before.
+ */
public void clean() {
- cleaner.clean();
+ cleanable.clean();
}
}
diff --git a/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/Dummy.java b/datasketches-memory-java9/src/main/java/org/apache/datasketches/memory/package-info.java
similarity index 85%
rename from datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/Dummy.java
rename to datasketches-memory-java9/src/main/java/org/apache/datasketches/memory/package-info.java
index aefdcba..769cee0 100644
--- a/datasketches-memory-java17/src/main/java/org/apache/datasketches/memory/internal/Dummy.java
+++ b/datasketches-memory-java9/src/main/java/org/apache/datasketches/memory/package-info.java
@@ -17,11 +17,4 @@
* under the License.
*/
-package org.apache.datasketches.memory.internal;
-
-/**
- * Temporary class & placeholder. It may not be needed.
- */
-public class Dummy {
-
-}
+package org.apache.datasketches.memory;
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datasketches.apache.org
For additional commands, e-mail: commits-help@datasketches.apache.org