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 2026/05/03 22:42:24 UTC
(datasketches-memory) branch update_3.1.X created (now 52683ef3)
This is an automated email from the ASF dual-hosted git repository.
leerho pushed a change to branch update_3.1.X
in repository https://gitbox.apache.org/repos/asf/datasketches-memory.git
at 52683ef3 Temporary check in for update_3.1.X
This branch includes the following new commits:
new 52683ef3 Temporary check in for update_3.1.X
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datasketches.apache.org
For additional commands, e-mail: commits-help@datasketches.apache.org
(datasketches-memory) 01/01: Temporary check in for update_3.1.X
Posted by le...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
leerho pushed a commit to branch update_3.1.X
in repository https://gitbox.apache.org/repos/asf/datasketches-memory.git
commit 52683ef38640db971b1211f75e56f65b98ac32f9
Author: Lee Rhodes <le...@gmail.com>
AuthorDate: Sun May 3 15:42:07 2026 -0700
Temporary check in for update_3.1.X
---
.github/workflows/auto-jdk-os-matrix.yml | 12 +-
.../memory/internal/AllocateDirect.java | 115 +--
.../memory/internal/BaseWritableMemoryImpl.java | 4 +-
.../memory/internal/ZeroCapacityTest.java | 2 +-
datasketches-memory-java21/pom.xml | 209 ++++
.../org/apache/datasketches/memory/Buffer.java | 407 ++++++++
.../memory/BufferPositionInvariantsException.java | 28 +-
.../memory/DefaultMemoryRequestServer.java | 92 ++
.../org/apache/datasketches/memory/Memory.java | 452 ++++++++
.../datasketches/memory/MemoryBoundsException.java | 27 +-
.../datasketches/memory/MemoryException.java | 31 +-
.../datasketches/memory/MemoryRequestServer.java | 63 ++
.../apache/datasketches/memory/MurmurHash3v2.java | 384 +++++++
.../org/apache/datasketches/memory/Positional.java | 110 ++
.../datasketches/memory/ReadOnlyException.java | 25 +-
.../org/apache/datasketches/memory/Resource.java | 384 +++++++
.../apache/datasketches/memory/WritableBuffer.java | 429 ++++++++
.../apache/datasketches/memory/WritableMemory.java | 587 +++++++++++
.../org/apache/datasketches/memory/XxHash.java | 202 ++++
.../memory/internal/AccessByteBuffer.java | 96 ++
.../memory/internal/AllocateDirect.java | 0
.../internal/BBNonNativeWritableBufferImpl.java | 124 +++
.../internal/BBNonNativeWritableMemoryImpl.java | 109 ++
.../memory/internal/BBWritableBufferImpl.java | 124 +++
.../memory/internal/BBWritableMemoryImpl.java | 109 ++
.../memory/internal/BaseWritableBufferImpl.java | 391 +++++++
.../memory/internal/BaseWritableMemoryImpl.java | 40 +-
.../memory/internal/CompareAndCopy.java | 521 ++++++++++
.../DirectNonNativeWritableBufferImpl.java | 124 +++
.../DirectNonNativeWritableMemoryImpl.java | 109 ++
.../memory/internal/DirectWritableBufferImpl.java | 124 +++
.../memory/internal/DirectWritableMemoryImpl.java | 109 ++
.../internal/HeapNonNativeWritableBufferImpl.java | 112 ++
.../internal/HeapNonNativeWritableMemoryImpl.java | 97 ++
.../memory/internal/HeapWritableBufferImpl.java | 112 ++
.../memory/internal/HeapWritableMemoryImpl.java | 97 ++
.../apache/datasketches/memory/internal/Ints.java | 22 +-
.../memory/internal/MemoryCleaner.java | 51 +
.../memory/internal/NativeWritableBufferImpl.java | 352 +++++++
.../memory/internal/NativeWritableMemoryImpl.java | 285 ++++++
.../internal/NonNativeWritableBufferImpl.java | 295 ++++++
.../internal/NonNativeWritableMemoryImpl.java | 217 ++++
.../memory/internal/PositionalImpl.java | 145 +++
.../apache/datasketches/memory/internal/Prim.java | 89 ++
.../datasketches/memory/internal/ResourceImpl.java | 558 ++++++++++
.../datasketches/memory/internal/StepBoolean.java | 72 ++
.../datasketches/memory/internal/UnsafeUtil.java | 166 +++
.../apache/datasketches/memory/internal/Util.java | 427 ++++++++
.../memory/internal/VirtualMachineMemory.java | 0
.../datasketches/memory/internal/XxHash64.java | 320 ++++++
.../datasketches/memory/internal/package-info.java | 13 -
.../apache/datasketches/memory/package-info.java | 15 +-
.../src/main/javadoc/overview.html | 34 +
.../memory/internal/AllocateDirectMemoryTest.java | 102 ++
.../datasketches/memory/internal/Buffer2Test.java | 440 ++++++++
.../memory/internal/BufferBoundaryCheckTest.java | 110 ++
.../memory/internal/BufferInvariantsTest.java | 338 ++++++
.../memory/internal/BufferReadWriteSafetyTest.java | 170 +++
.../datasketches/memory/internal/BufferTest.java | 320 ++++++
.../memory/internal/CommonBufferTest.java | 420 ++++++++
.../memory/internal/CommonMemoryTest.java | 365 +++++++
.../memory/internal/CopyMemoryOverlapTest.java | 180 ++++
.../memory/internal/CopyMemoryTest.java | 178 ++++
.../memory/internal/DruidIssue11544Test.java | 105 ++
.../internal/ExampleMemoryRequestServerTest.java | 118 +++
.../memory/internal/IgnoredArrayOverflowTest.java | 101 ++
.../datasketches/memory/internal/LeafImplTest.java | 249 +++++
.../memory/internal/MemoryBoundaryCheckTest.java | 110 ++
.../memory/internal/MemoryCleanerTest.java | 62 ++
.../memory/internal/MemoryCloseExceptionTest.java | 12 +-
.../memory/internal/MemoryReadWriteSafetyTest.java | 179 ++++
.../datasketches/memory/internal/MemoryTest.java | 453 ++++++++
.../memory/internal/MemoryWriteToTest.java | 94 ++
.../internal/NativeWritableBufferImplTest.java | 567 ++++++++++
.../internal/NativeWritableMemoryImplTest.java | 675 ++++++++++++
.../internal/NonNativeWritableBufferImplTest.java | 260 +++++
.../internal/NonNativeWritableMemoryImplTest.java | 183 ++++
.../memory/internal/PositionalTest.java | 86 ++
.../datasketches/memory/internal/ResourceTest.java | 196 ++++
.../memory/internal/SpecificLeafTest.java | 151 +++
.../memory/internal/UnsafeUtilTest.java | 91 ++
.../datasketches/memory/internal/UtilTest.java | 207 ++++
.../memory/internal/WritableDirectCopyTest.java | 234 +++++
.../memory/internal/WritableMemoryTest.java | 198 ++++
.../memory/internal/XxHash64LoopingTest.java | 1082 ++++++++++++++++++++
.../datasketches/memory/internal/XxHash64Test.java | 186 ++++
.../memory/internal/ZeroCapacityTest.java | 0
.../src/test/resources/GettysburgAddress.txt | 7 +
datasketches-memory-report/pom.xml | 5 +
pom.xml | 1 +
90 files changed, 17058 insertions(+), 199 deletions(-)
diff --git a/.github/workflows/auto-jdk-os-matrix.yml b/.github/workflows/auto-jdk-os-matrix.yml
index eea189cd..9193f91c 100644
--- a/.github/workflows/auto-jdk-os-matrix.yml
+++ b/.github/workflows/auto-jdk-os-matrix.yml
@@ -1,12 +1,12 @@
name: Auto JDK & OS Matrix Test & Install
on:
- push:
- paths-ignore: [ '**/*.html', '**/*.md', '**/*.txt', '**/LICENSE', '**/NOTICE' ]
- branches: [ 'main', '[0-9]+.[0-9]+.[Xx]', 'v[0-9]+.[0-9]+.[Xx]']
- pull_request:
- paths-ignore: [ '**/*.html', '**/*.md', '**/*.txt', '**/LICENSE', '**/NOTICE' ]
- branches: [ 'main', '[0-9]+.[0-9]+.[Xx]', 'v[0-9]+.[0-9]+.[Xx]']
+# push:
+# paths-ignore: [ '**/*.html', '**/*.md', '**/*.txt', '**/LICENSE', '**/NOTICE' ]
+# branches: [ 'main', '[0-9]+.[0-9]+.[Xx]', 'v[0-9]+.[0-9]+.[Xx]']
+# pull_request:
+# paths-ignore: [ '**/*.html', '**/*.md', '**/*.txt', '**/LICENSE', '**/NOTICE' ]
+# branches: [ 'main', '[0-9]+.[0-9]+.[Xx]', 'v[0-9]+.[0-9]+.[Xx]']
workflow_dispatch:
concurrency:
diff --git a/datasketches-memory-java11/src/main/java/org/apache/datasketches/memory/internal/AllocateDirect.java b/datasketches-memory-java11/src/main/java/org/apache/datasketches/memory/internal/AllocateDirect.java
index 6db389c8..b93aae08 100644
--- a/datasketches-memory-java11/src/main/java/org/apache/datasketches/memory/internal/AllocateDirect.java
+++ b/datasketches-memory-java11/src/main/java/org/apache/datasketches/memory/internal/AllocateDirect.java
@@ -21,82 +21,68 @@ package org.apache.datasketches.memory.internal;
import static org.apache.datasketches.memory.internal.UnsafeUtil.unsafe;
-import java.util.logging.Logger;
+import java.lang.ref.Cleaner;
/**
- * Provides access to direct (native) memory.
+ * Provides access to off-heap memory.
*
- * @author Roman Leventov
* @author Lee Rhodes
*/
-//@SuppressWarnings("restriction")
-final class AllocateDirect {
- static final Logger LOG = Logger.getLogger(AllocateDirect.class.getCanonicalName());
-
+public class AllocateDirect {
+ private static final Cleaner CLEANER = Cleaner.create();
+ private final long rawAddress; //used for freeMemory
+ private final long alignedAddress; //data start address
private final Deallocator deallocator;
- private final long nativeBaseOffset;
- private final MemoryCleaner cleaner;
-
+ private final Cleaner.Cleanable cleanable;
+
/**
- * Base Constructor for allocate native memory.
- *
- * <p>Allocates and provides access to capacityBytes directly in native (off-heap) memory
- * leveraging the MemoryImpl interface.
- * The allocated memory will be 8-byte aligned, but may not be page aligned.
- * @param capacityBytes the the requested capacity of off-heap memory. Cannot be zero.
+ * Allocates off-heap memory with a default alignment of 8 bytes.
+ * @param capacityBytes must be greater than zero.
*/
AllocateDirect(final long capacityBytes) {
- final boolean pageAligned = VirtualMachineMemory.getIsPageAligned();
- final long pageSize = getPageSize();
- final long allocationSize = capacityBytes + (pageAligned ? pageSize : 0);
- final long nativeAddress;
+ this(capacityBytes, Long.BYTES);
+ }
+
+ /**
+ * Allocates off-heap memory with a specified alignment.
+ * @param capacityBytes must be greater than or equal 0.
+ * @param alignment the desired alignment. It must be a power of 2; e.g., 2, 4 or 8; and greater than 1.
+ */
+ AllocateDirect(final long capacityBytes, int alignment) {
+ if (capacityBytes < 0) { throw new IllegalArgumentException("capacityBytes must be >= 0: " + capacityBytes); }
+ long mask = alignment - 1L;
+ if (checkAlignment(alignment)) {
+ throw new IllegalArgumentException("alignment must be a positive power of 2 and greater than one: " + alignment); }
try {
- nativeAddress = unsafe.allocateMemory(allocationSize);
+ this.rawAddress = unsafe.allocateMemory(capacityBytes + mask);
} catch (final OutOfMemoryError err) {
throw new RuntimeException(err);
- }
- if (pageAligned && ((nativeAddress % pageSize) != 0)) {
- //Round up to page boundary
- nativeBaseOffset = (nativeAddress & ~(pageSize - 1L)) + pageSize;
- } else {
- nativeBaseOffset = nativeAddress;
- }
- deallocator = new Deallocator(nativeAddress);
- cleaner = new MemoryCleaner(this, deallocator);
+ }
+ this.alignedAddress = rawAddress & ~mask;
+ this.deallocator = new Deallocator(rawAddress);
+ this.cleanable = CLEANER.register(this, deallocator);
}
-
- public void close() {
- try {
- if (deallocator.deallocate(false)) {
- // This Cleaner.clean() call effectively just removes the Cleaner from the internal linked
- // list of all cleaners. It will delegate to Deallocator.deallocate() which will be a no-op
- // because the valid state is already changed.
- cleaner.clean();
- }
- } finally {
- ResourceImpl.reachabilityFence(this);
- }
- }
-
- long getNativeBaseOffset() {
- return nativeBaseOffset;
+
+ public long getAddress() {
+ return alignedAddress;
}
- public static int getPageSize() {
- return unsafe.pageSize();
+ public void close() {
+ cleanable.clean();
}
public StepBoolean getValid() {
return deallocator.getValid();
}
+ //Must be static and NOT hold a reference to the outer AllocateDirect class
private static final class Deallocator implements Runnable {
//This is the only place the actual native address is kept for use by unsafe.freeMemory();
- private final long nativeAddress;
+ private final long addressToFree;
private final StepBoolean valid = new StepBoolean(true); //only place for this
- Deallocator(final long nativeAddress) {
- this.nativeAddress = nativeAddress;
+ Deallocator(final long addressToFree) {
+ this.addressToFree = addressToFree;
}
StepBoolean getValid() {
@@ -104,21 +90,20 @@ final class AllocateDirect {
}
@Override
- public void run() throws IllegalStateException {
- deallocate(true);
- }
-
- boolean deallocate(final boolean calledFromCleaner) throws IllegalStateException {
- if (valid.change()) {
- if (calledFromCleaner) {
- // Warn about non-deterministic resource cleanup.
- LOG.warning("A direct resource was not closed explicitly.");
- }
- unsafe.freeMemory(nativeAddress);
- return true;
+ public void run() {
+ if (valid.change() && addressToFree != 0) {
+ unsafe.freeMemory(addressToFree);
}
- return false;
}
}
-
+
+ /**
+ * Returns true if given <i>n</i> is greater than one and a positive power of 2.
+ *
+ * @param n The input argument.
+ * @return true if argument is greater than one and a positive power of 2.
+ */
+ private static final boolean checkAlignment(final int n) {
+ return (n >= 1) && ((n & -n) == n);
+ }
}
diff --git a/datasketches-memory-java11/src/main/java/org/apache/datasketches/memory/internal/BaseWritableMemoryImpl.java b/datasketches-memory-java11/src/main/java/org/apache/datasketches/memory/internal/BaseWritableMemoryImpl.java
index 2bd6971d..81c2464e 100644
--- a/datasketches-memory-java11/src/main/java/org/apache/datasketches/memory/internal/BaseWritableMemoryImpl.java
+++ b/datasketches-memory-java11/src/main/java/org/apache/datasketches/memory/internal/BaseWritableMemoryImpl.java
@@ -157,9 +157,9 @@ public abstract class BaseWritableMemoryImpl extends ResourceImpl implements Wri
final ByteOrder byteOrder, final MemoryRequestServer memReqSvr) {
Util.negativeCheck(capacityBytes, "capacityBytes");
Objects.requireNonNull(byteOrder, "byteOrder must be non-null.");
- final AllocateDirect direct = new AllocateDirect(capacityBytes);
+ final AllocateDirect direct = new AllocateDirect(capacityBytes, 3);
final int typeId = 0; //direct is never read-only on construction
- final long nativeBaseOffset = direct.getNativeBaseOffset();
+ final long nativeBaseOffset = direct.getAddress();
final long cumOffsetBytes = nativeBaseOffset;
final BaseWritableMemoryImpl wmem = Util.isNativeByteOrder(byteOrder)
? new DirectWritableMemoryImpl(
diff --git a/datasketches-memory-java11/src/test/java/org/apache/datasketches/memory/internal/ZeroCapacityTest.java b/datasketches-memory-java11/src/test/java/org/apache/datasketches/memory/internal/ZeroCapacityTest.java
index fa500dbc..22b15fdf 100644
--- a/datasketches-memory-java11/src/test/java/org/apache/datasketches/memory/internal/ZeroCapacityTest.java
+++ b/datasketches-memory-java11/src/test/java/org/apache/datasketches/memory/internal/ZeroCapacityTest.java
@@ -28,7 +28,7 @@ import org.apache.datasketches.memory.WritableMemory;
import org.testng.annotations.Test;
/**
- * Although allocating zero bytes may be a bug, it is tolerated in Java.
+ * Although one might think that allocating zero bytes is a bug, it is tolerated in Java.
*
* @author Lee Rhodes
*/
diff --git a/datasketches-memory-java21/pom.xml b/datasketches-memory-java21/pom.xml
new file mode 100644
index 00000000..3a0c799d
--- /dev/null
+++ b/datasketches-memory-java21/pom.xml
@@ -0,0 +1,209 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+ https://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.datasketches</groupId>
+ <artifactId>datasketches-memory</artifactId>
+ <version>3.0.2</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>datasketches-memory-java21</artifactId>
+ <name>${project.artifactId}</name>
+ <packaging>jar</packaging>
+
+ <properties>
+ <!-- Test -->
+ <testng.version>7.12.0</testng.version>
+ <java.version>21</java.version>
+
+ <jdk-toolchain.version>${java.version}</jdk-toolchain.version>
+ <maven.compiler.source>${java.version}</maven.compiler.source>
+ <maven.compiler.target>${java.version}</maven.compiler.target>
+ <maven.javadoc.skip>false</maven.javadoc.skip>
+ <maven.compiler.showWarnings>true</maven.compiler.showWarnings>
+ <!-- jdk.internal.misc is used to call public Unsafe & public VM -->
+ <arg.jdk.internal.misc>--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED</arg.jdk.internal.misc>
+ <!-- jdk.internal.ref is used to call public Cleaner -->
+ <arg.jdk.internal.ref>--add-exports=java.base/jdk.internal.ref=ALL-UNNAMED</arg.jdk.internal.ref>
+ <!-- sun.nio.ch.exports is used to open FileChannelImpl for DirectBuffer-->
+ <arg.sun.nio.ch.exports>--add-exports=java.base/sun.nio.ch=ALL-UNNAMED</arg.sun.nio.ch.exports>
+ <!-- jdk.internal.access is used to access SharedSecrets and JavaNioAccess -->
+ <arg.jdk.internal.access>--add-exports=java.base/jdk.internal.access=ALL-UNNAMED</arg.jdk.internal.access>
+ <!-- java.nio is used to open MappedByteBuffer load0, isloaded0, & force0 for reflection -->
+ <arg.java.nio>--add-opens=java.base/java.nio=ALL-UNNAMED</arg.java.nio>
+ <!-- sun.nio.ch.opens is used to open FileChannelImpl for map0 & unmap0 for reflection-->
+ <arg.sun.nio.ch.opens>--add-opens=java.base/sun.nio.ch=ALL-UNNAMED</arg.sun.nio.ch.opens>
+
+
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.testng</groupId>
+ <artifactId>testng</artifactId>
+ <version>${testng.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <!-- Used for xxHash testing -->
+ <groupId>net.openhft</groupId>
+ <artifactId>zero-allocation-hashing</artifactId>
+ <version>${zero-allocation-hashing.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <version>${org-slf4j.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <fork>true</fork> <!-- required if Maven is using a different JVM -->
+ <compilerArgs>
+ <arg>${arg.jdk.internal.misc}</arg>
+ <arg>${arg.jdk.internal.ref}</arg>
+ <arg>${arg.sun.nio.ch.exports}</arg>
+ <arg>${arg.jdk.internal.access}</arg>
+ <arg>${arg.java.nio}</arg>
+ <arg>${arg.sun.nio.ch.opens}</arg>
+ </compilerArgs>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <argLine>
+ @{argLine}
+ @{jvm.args}
+ ${arg.java.nio}
+ ${arg.sun.nio.ch.exports}
+ ${arg.sun.nio.ch.opens}
+ ${arg.jdk.internal.misc}
+ ${arg.jdk.internal.ref}
+ ${arg.jdk.internal.access}
+ -Dorg.slf4j.simpleLogger.log.org.testng=warn
+ </argLine>
+ <trimStackTrace>false</trimStackTrace>
+ <useFile>true</useFile>
+ <useManifestOnlyJar>false</useManifestOnlyJar>
+ <redirectTestOutputToFile>true</redirectTestOutputToFile>
+ <reportsDirectory>${project.build.directory}/test-output/${maven.build.timestamp}</reportsDirectory>
+ <perCoreThreadCount>false</perCoreThreadCount>
+ <properties>
+ <property>
+ <name>verbose</name>
+ <value>0</value>
+ </property>
+ </properties>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-toolchains-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>toolchain</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <toolchains>
+ <jdk>
+ <version>${java.version}</version> <!-- override -->
+ </jdk>
+ </toolchains>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+
+ <profile>
+ <id>assemble-mr-jar</id>
+ <activation>
+ <activeByDefault>false</activeByDefault>
+ </activation>
+ <properties> <!-- project.root defined in /.mvn/maven.config -->
+ <assembly.script.path>${project.root}/tools/scripts/package-multi-release-jar.sh</assembly.script.path>
+ <assembly.java.home>${env.JAVA_HOME}</assembly.java.home>
+ <assembly.git.tag>${project.version}</assembly.git.tag>
+ <assembly.base.dir>${project.root}</assembly.base.dir>
+ </properties>
+ <build>
+ <defaultGoal>exec:exec</defaultGoal>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <!-- The trailing comment on the line below ignores an eclipse warning. The version is required for dependency updates. -->
+ <version>${exec-maven-plugin.version}</version><!--$NO-MVN-MAN-VER$--> <!-- ?? -->
+ <executions>
+ <execution>
+ <id>package-mr-jar</id>
+ <goals>
+ <goal>exec</goal>
+ </goals>
+ <phase>package</phase>
+ <configuration>
+ <executable>bash</executable>
+ <commandlineArgs>${assembly.script.path} ${assembly.java.home} ${assembly.git.tag} ${assembly.base.dir}</commandlineArgs>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
+</project>
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/Buffer.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/Buffer.java
new file mode 100644
index 00000000..3c194307
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/Buffer.java
@@ -0,0 +1,407 @@
+/*
+ * 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.BaseWritableBufferImpl;
+
+/**
+ * Defines the read-only API for relative positional access to a resource.
+ *
+ * @author Lee Rhodes
+ */
+public interface Buffer extends Positional {
+
+ //BYTE BUFFER
+ /**
+ * Provides a view of the given <i>ByteBuffer</i> for read-only operations.
+ * The view is of the entire ByteBuffer independent of <i>position</i> and <i>limit</i>.
+ * However, the returned <i>Buffer</i> will have <i>position</i> and <i>end</i> set to the
+ * ByteBuffer's <i>position</i> and <i>limit</i>, respectively.
+ * The returned Buffer will use the <i>ByteOrder</i> of the <i>ByteBuffer</i>.
+ * @param byteBuffer the given ByteBuffer, must not be null.
+ * @return a new <i>Buffer</i> for read-only operations on the given ByteBuffer.
+ */
+ static Buffer wrap(final ByteBuffer byteBuffer) {
+ return wrap(byteBuffer, byteBuffer.order());
+ }
+
+ /**
+ * Provides a view of the given <i>ByteBuffer</i> for read-only operations.
+ * The view is of the entire ByteBuffer independent of <i>position</i> and <i>limit</i>.
+ * However, the returned <i>Buffer</i> will have <i>position</i> and <i>end</i> set to the
+ * ByteBuffer's <i>position</i> and <i>limit</i>, respectively.
+ * The returned Buffer will use the given <i>ByteOrder</i>,
+ * independent of the ByteOrder of the given ByteBuffer.
+ * This does not affect the ByteOrder of data already in the ByteBuffer.
+ * @param byteBuffer the given ByteBuffer. It must be non-null
+ * @param byteOrder the ByteOrder to be used. It must be non-null.
+ * @return a new <i>Buffer</i> for read-only operations on the given ByteBuffer.
+ */
+ static Buffer wrap(
+ final ByteBuffer byteBuffer,
+ final ByteOrder byteOrder) {
+ return BaseWritableBufferImpl.wrapByteBuffer(byteBuffer, true, byteOrder, null);
+ }
+
+ //DUPLICATES
+ /**
+ * Returns a read-only duplicate view of this Buffer with the same byte order,
+ * but independent values of
+ * <i>start</i>, <i>position</i> and <i>end</i>.
+ * <ul>
+ * <li>Returned object's origin = this object's origin</li>
+ * <li>Returned object's <i>start</i> = this object's <i>start</i></li>
+ * <li>Returned object's <i>position</i> = this object's <i>position</i></li>
+ * <li>Returned object's <i>end</i> = this object's <i>end</i></li>
+ * <li>Returned object's <i>capacity</i> = this object' <i>capacityBytes</i></li>
+ * <li>Returned object's <i>start</i>, <i>position</i> and <i>end</i> are mutable and
+ * independent of this object's <i>start</i>, <i>position</i> and <i>end</i></li>
+ * </ul>
+ * @return a read-only duplicate view of this Buffer with the same but independent values of
+ * <i>start</i>, <i>position</i> and <i>end</i>.
+ */
+ default Buffer duplicate() {
+ return duplicate(getTypeByteOrder());
+ }
+
+ /**
+ * Returns a read-only duplicate view of this Buffer with the same but independent values of
+ * <i>start</i>, <i>position</i> and <i>end</i>, but with the specified byteOrder.
+ * <ul>
+ * <li>Returned object's origin = this object's origin</li>
+ * <li>Returned object's <i>start</i> = this object's <i>start</i></li>
+ * <li>Returned object's <i>position</i> = this object's <i>position</i></li>
+ * <li>Returned object's <i>end</i> = this object's <i>end</i></li>
+ * <li>Returned object's <i>capacity</i> = this object' <i>capacityBytes</i></li>
+ * <li>Returned object's <i>start</i>, <i>position</i> and <i>end</i> are mutable and
+ * independent of this object's <i>start</i>, <i>position</i> and <i>end</i></li>
+ * </ul>
+ * @param byteOrder the given <i>ByteOrder</i>.
+ * @return a read-only duplicate view of this Buffer with the same but independent values of
+ * <i>start</i>, <i>position</i> and <i>end</i>.
+ */
+ Buffer duplicate(ByteOrder byteOrder);
+
+ //NO MAP use Memory
+ //NO ALLOCATE OFF-HEAP, makes no sense
+
+ //REGIONS
+ /**
+ * A region is a read-only view of this object from <i>position</i> to <i>end</i>
+ * and with the same byte order.
+ * <ul>
+ * <li>Returned object's origin = this object's <i>position</i></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 (<i>end</i> - <i>position</i>)</li>
+ * <li>Returned object's <i>capacity</i> = this object's (<i>end</i> - <i>position</i>)</li>
+ * <li>Returned object's <i>start</i>, <i>position</i> and <i>end</i> are mutable and
+ * independent of this object's <i>start</i>, <i>position</i> and <i>end</i></li>
+ * </ul>
+ * @return a new <i>Buffer</i> representing the defined region based on the current
+ * <i>position</i> and <i>end</i>.
+ */
+ default Buffer region() {
+ return region(getPosition(), getEnd() - getPosition(), getTypeByteOrder());
+ }
+
+ /**
+ * A region is a read-only view of this object from offsetBytes and with a length of
+ * capacityBytes and with the given byte order.
+ * <ul>
+ * <li>Returned object's origin = this objects' origin + <i>offsetBytes</i></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> = <i>capacityBytes</i></li>
+ * <li>Returned object's <i>capacity</i> = <i>capacityBytes</i></li>
+ * <li>Returned object's <i>start</i>, <i>position</i> and <i>end</i> are mutable and
+ * independent of this object's <i>start</i>, <i>position</i> and <i>end</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 <i>WritableBuffer</i>
+ * @param capacityBytes the <i>capacity</i> of the returned region in bytes
+ * @param byteOrder the given byte order
+ * @return a new <i>Buffer</i> representing the defined writable region
+ * based on the current <i>position</i>, <i>end</i> and byteOrder.
+ */
+ Buffer region(
+ long offsetBytes,
+ long capacityBytes,
+ ByteOrder byteOrder);
+
+ //AS MEMORY
+ /**
+ * Convert this Buffer to a Memory. The current <i>start</i>, <i>position</i> and <i>end</i>
+ * are ignored.
+ * @return Memory
+ */
+ default Memory asMemory() {
+ return asMemory(getTypeByteOrder());
+ }
+
+ /**
+ * Convert this Buffer to a Memory with the given byte order.
+ * The current <i>start</i>, <i>position</i> and <i>end</i> are ignored.
+ * @param byteOrder the given byte order.
+ * @return Memory
+ */
+ Memory asMemory(ByteOrder byteOrder);
+
+ //NO ALLOCATE HEAP BYTE ARRAYS, makes no sense. Use WritableMemory
+ //NO WRAP - ACCESS PRIMITIVE HEAP ARRAYS for readOnly.
+ // Because of the ambiguity of positional API with the multibyte primitives. Use Memory instead.
+ //END OF CONSTRUCTOR-TYPE METHODS
+
+ //PRIMITIVE getX() and getXArray()
+
+ /**
+ * Gets the boolean value at the current position.
+ * Increments the position by <i>Byte.BYTES</i>.
+ * @return the boolean at the current position
+ */
+ boolean getBoolean();
+
+ /**
+ * Gets the boolean value at the given offset.
+ * This does not change the position.
+ * @param offsetBytes offset bytes relative to this Memory start
+ * @return the boolean at the given offset
+ */
+ boolean getBoolean(long offsetBytes);
+
+ /**
+ * Gets the byte value at the current position.
+ * Increments the position by <i>Byte.BYTES</i>.
+ * @return the byte at the current position
+ */
+ byte getByte();
+
+ /**
+ * Gets the byte value at the given offset.
+ * This does not change the position.
+ * @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 current position.
+ * Increments the position by <i>Byte.BYTES * (lengthBytes - dstOffsetBytes)</i>.
+ * @param dstArray The preallocated destination array.
+ * @param dstOffsetBytes offset in array units
+ * @param lengthBytes number of array units to transfer
+ */
+ void getByteArray(
+ byte[] dstArray,
+ int dstOffsetBytes,
+ int lengthBytes);
+
+ /**
+ * Gets the char value at the current position.
+ * Increments the position by <i>Character.BYTES</i>.
+ * @return the char at the current position
+ */
+ char getChar();
+
+ /**
+ * Gets the char value at the given offset.
+ * This does not change the position.
+ * @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 current position.
+ * Increments the position by <i>Character.BYTES * (lengthChars - dstOffsetChars)</i>.
+ * @param dstArray The preallocated destination array.
+ * @param dstOffsetChars offset in array units
+ * @param lengthChars number of array units to transfer
+ */
+ void getCharArray(
+ char[] dstArray,
+ int dstOffsetChars,
+ int lengthChars);
+
+ /**
+ * Gets the double value at the current position.
+ * Increments the position by <i>Double.BYTES</i>.
+ * @return the double at the current position
+ */
+ double getDouble();
+
+ /**
+ * Gets the double value at the given offset.
+ * This does not change the position.
+ * @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 current position.
+ * Increments the position by <i>Double.BYTES * (lengthDoubles - dstOffsetDoubles)</i>.
+ * @param dstArray The preallocated destination array.
+ * @param dstOffsetDoubles offset in array units
+ * @param lengthDoubles number of array units to transfer
+ */
+ void getDoubleArray(
+ double[] dstArray,
+ int dstOffsetDoubles,
+ int lengthDoubles);
+
+ /**
+ * Gets the float value at the current position.
+ * Increments the position by <i>Float.BYTES</i>.
+ * @return the float at the current position
+ */
+ float getFloat();
+
+ /**
+ * Gets the float value at the given offset.
+ * This does not change the position.
+ * @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 current position.
+ * Increments the position by <i>Float.BYTES * (lengthFloats - dstOffsetFloats)</i>.
+ * @param dstArray The preallocated destination array.
+ * @param dstOffsetFloats offset in array units
+ * @param lengthFloats number of array units to transfer
+ */
+ void getFloatArray(
+ float[] dstArray,
+ int dstOffsetFloats,
+ int lengthFloats);
+
+ /**
+ * Gets the int value at the current position.
+ * Increments the position by <i>Integer.BYTES</i>.
+ * @return the int at the current position
+ */
+ int getInt();
+
+ /**
+ * Gets the int value at the given offset.
+ * This does not change the position.
+ * @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 current position.
+ * Increments the position by <i>Integer.BYTES * (lengthInts - dstOffsetInts)</i>.
+ * @param dstArray The preallocated destination array.
+ * @param dstOffsetInts offset in array units
+ * @param lengthInts number of array units to transfer
+ */
+ void getIntArray(
+ int[] dstArray,
+ int dstOffsetInts,
+ int lengthInts);
+
+ /**
+ * Gets the long value at the current position.
+ * Increments the position by <i>Long.BYTES</i>.
+ * @return the long at the current position
+ */
+ long getLong();
+
+ /**
+ * Gets the long value at the given offset.
+ * This does not change the position.
+ * @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 current position.
+ * Increments the position by <i>Long.BYTES * (lengthLongs - dstOffsetLongs)</i>.
+ * @param dstArray The preallocated destination array.
+ * @param dstOffsetLongs offset in array units
+ * @param lengthLongs number of array units to transfer
+ */
+ void getLongArray(
+ long[] dstArray,
+ int dstOffsetLongs,
+ int lengthLongs);
+
+ /**
+ * Gets the short value at the current position.
+ * Increments the position by <i>Short.BYTES</i>.
+ * @return the short at the current position
+ */
+ short getShort();
+
+ /**
+ * Gets the short value at the given offset.
+ * This does not change the position.
+ * @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 current position.
+ * Increments the position by <i>Short.BYTES * (lengthShorts - dstOffsetShorts)</i>.
+ * @param dstArray The preallocated destination array.
+ * @param dstOffsetShorts offset in array units
+ * @param lengthShorts number of array units to transfer
+ */
+ void getShortArray(
+ short[] dstArray,
+ int dstOffsetShorts,
+ int lengthShorts);
+
+ //SPECIAL PRIMITIVE READ METHODS: compareTo.
+ // No copyTo, No writeTo. Use Memory for those.
+
+ /**
+ * Compares the bytes of this Buffer to <i>that</i> Buffer.
+ * This uses absolute offsets not the start, position and end.
+ * 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 Buffer</i>
+ * @param thisLengthBytes the length of the region to compare from <i>this Buffer</i>
+ * @param that the other Buffer to compare with
+ * @param thatOffsetBytes the starting offset for <i>that Buffer</i>
+ * @param thatLengthBytes the length of the region to compare from <i>that Buffer</i>
+ * @return <i>(this < that) ? (some negative value) : (this > that) ? (some positive value)
+ * : 0;</i>
+ */
+ int compareTo(
+ long thisOffsetBytes,
+ long thisLengthBytes,
+ Buffer that,
+ long thatOffsetBytes,
+ long thatLengthBytes);
+
+}
diff --git a/datasketches-memory-java11/src/test/java/org/apache/datasketches/memory/internal/VirtualMachineMemoryTest.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/BufferPositionInvariantsException.java
similarity index 60%
copy from datasketches-memory-java11/src/test/java/org/apache/datasketches/memory/internal/VirtualMachineMemoryTest.java
copy to datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/BufferPositionInvariantsException.java
index 2793b30b..7a072f1f 100644
--- a/datasketches-memory-java11/src/test/java/org/apache/datasketches/memory/internal/VirtualMachineMemoryTest.java
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/BufferPositionInvariantsException.java
@@ -17,17 +17,23 @@
* under the License.
*/
-package org.apache.datasketches.memory.internal;
+package org.apache.datasketches.memory;
-import org.testng.annotations.Test;
-
-@SuppressWarnings({"unused"})
-public class VirtualMachineMemoryTest {
+/**
+ * Position operation violation.
+ */
+public class BufferPositionInvariantsException extends MemoryException {
+ private static final long serialVersionUID = 1L;
- @Test
- public void inertPageAlignment() {
- boolean result = VirtualMachineMemory.getIsPageAligned();
- //System.out.println("VM page alignment:" + result);
- assert (true); //no exception was thrown
- }
+ /**
+ * The associated position operation violated one of the positional invariants.
+ *
+ * <p>The invariants equation is {@code 0 <= start <= position <= end <= capacity}.</p>
+ *
+ * @param details of the violation.
+ */
+ public BufferPositionInvariantsException(final String details) {
+ super(details);
+ }
}
+
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/DefaultMemoryRequestServer.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/DefaultMemoryRequestServer.java
new file mode 100644
index 00000000..8d405a8f
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/DefaultMemoryRequestServer.java
@@ -0,0 +1,92 @@
+/*
+ * 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.ByteOrder;
+
+/**
+ * This example MemoryRequestServer is simple but demonstrates one of many ways to
+ * manage continuous requests for larger memory.
+ * This capability is only available for writable, non-file-memory-mapping resources.
+ *
+ * @author Lee Rhodes
+ */
+public final class DefaultMemoryRequestServer implements MemoryRequestServer {
+ private final boolean offHeap; //create the new memory off-heap; otherwise, on-heap
+ private final boolean copyOldToNew; //copy data from old memory to new memory.
+
+ /**
+ * Default constructor.
+ * Create new memory on-heap and do not copy old contents to new memory.
+ */
+ public DefaultMemoryRequestServer() {
+ this(false, false);
+ }
+
+ /**
+ * Constructor with parameters
+ * @param offHeap if true, the returned new memory will be off heap
+ * @param copyOldToNew if true, the data from the current memory will be copied to the new memory,
+ * starting at address 0, and through the currentMemory capacity.
+ */
+ public DefaultMemoryRequestServer(
+ final boolean offHeap,
+ final boolean copyOldToNew) {
+ this.offHeap = offHeap;
+ this.copyOldToNew = copyOldToNew;
+ }
+
+ @Override
+ public WritableMemory request(
+ final WritableMemory currentWmem,
+ final long newCapacityBytes) {
+ final ByteOrder order = currentWmem.getTypeByteOrder();
+ final long currentBytes = currentWmem.getCapacity();
+ final WritableMemory newWmem;
+
+ if (newCapacityBytes <= currentBytes) {
+ throw new IllegalArgumentException("newCapacityBytes must be > currentBytes");
+ }
+
+ if (offHeap) {
+ newWmem = WritableMemory.allocateDirect(newCapacityBytes, order, this);
+ }
+ else { //On-heap
+ if (newCapacityBytes > Integer.MAX_VALUE) {
+ throw new IllegalArgumentException("Requested capacity exceeds Integer.MAX_VALUE.");
+ }
+ newWmem = WritableMemory.allocate((int)newCapacityBytes, order, this);
+ }
+
+ if (copyOldToNew) {
+ currentWmem.copyTo(0, newWmem, 0, currentBytes);
+ }
+
+ return newWmem;
+ }
+
+ @Override
+ public void requestClose(
+ final WritableMemory memToClose,
+ final WritableMemory newMemory) {
+ if (memToClose.isCloseable()) { memToClose.close(); }
+ }
+
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/Memory.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/Memory.java
new file mode 100644
index 00000000..366837ad
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/Memory.java
@@ -0,0 +1,452 @@
+/*
+ * 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.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;
+
+/**
+ * Defines the read-only API for offset access to a resource.
+ *
+ * @author Lee Rhodes
+ */
+public interface Memory extends Resource {
+
+ //BYTE BUFFER
+
+ /**
+ * Provides a view of the given <i>ByteBuffer</i> for read-only operations.
+ * The view is of the entire ByteBuffer independent of <i>position</i> and <i>limit</i>.
+ * The returned <i>Memory</i> will use the <i>ByteOrder</i> of the <i>ByteBuffer</i>.
+ * @param byteBuffer the given <i>ByteBuffer</i>. It must be non-null.
+ * @return a new <i>Memory</i> for read-only operations on the given <i>ByteBuffer</i>.
+ */
+ static Memory wrap(final ByteBuffer byteBuffer) {
+ return wrap(byteBuffer, byteBuffer.order());
+ }
+
+ /**
+ * Provides a view of the given <i>ByteBuffer</i> for read-only operations.
+ * The view is of the entire ByteBuffer independent of <i>position</i> and <i>limit</i>.
+ * The returned <i>Memory</i> will use the given <i>ByteOrder</i>,
+ * independent of the ByteOrder of the given ByteBuffer.
+ * This does not affect the ByteOrder of data already in the ByteBuffer.
+ * @param byteBuffer the given <i>ByteBuffer</i>. It must be non-null.
+ * @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(
+ final ByteBuffer byteBuffer,
+ final ByteOrder byteOrder) {
+ return BaseWritableMemoryImpl.wrapByteBuffer(byteBuffer, true, byteOrder, null);
+ }
+
+ //Duplicates make no sense here
+
+ //No Mapping in Java 21
+
+ //NO ALLOCATE OFF-HEAP, makes no sense
+
+ //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(
+ final long offsetBytes,
+ final long capacityBytes) {
+ return region(offsetBytes, capacityBytes, getTypeByteOrder());
+ }
+
+ /**
+ * 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(getTypeByteOrder());
+ }
+
+ /**
+ * 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. It must be non-null.
+ * @return a new <i>Buffer</i> with the given byteOrder.
+ */
+ Buffer asBuffer(ByteOrder byteOrder);
+
+ //NO ALLOCATE HEAP BYTE ARRAYS, makes no sense
+
+ //WRAP - 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.
+ * @return a new <i>Memory</i> for read operations
+ */
+ static Memory wrap(final 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. It must be non-null.
+ * @param byteOrder the byte order to be used. It must be non-null.
+ * @return a new <i>Memory</i> for read operations
+ */
+ static Memory wrap(
+ final byte[] array,
+ final 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.
+ * @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 <i>Memory</i> for read operations
+ */
+ static Memory wrap(
+ final byte[] array,
+ final int offsetBytes,
+ final int lengthBytes,
+ final ByteOrder byteOrder) {
+ return BaseWritableMemoryImpl.wrapHeapArray(array, offsetBytes, lengthBytes, true, byteOrder, null);
+ }
+
+ /**
+ * Wraps the given primitive array for read operations assuming native byte order.
+ * @param array the given primitive array. It must be non-null.
+ * @return a new <i>Memory</i> for read operations
+ */
+ static Memory wrap(final 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.
+ * @return a new <i>Memory</i> for read operations
+ */
+ static Memory wrap(final short[] array) {
+ Objects.requireNonNull(array, "array 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.
+ * @return a new <i>Memory</i> for read operations
+ */
+ static Memory wrap(final int[] array) {
+ Objects.requireNonNull(array, "array 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.
+ * @return a new <i>Memory</i> for read operations
+ */
+ static Memory wrap(final long[] array) {
+ Objects.requireNonNull(array, "array 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.
+ * @return a new <i>Memory</i> for read operations
+ */
+ static Memory wrap(final float[] array) {
+ Objects.requireNonNull(array, "array 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.
+ * @return a new <i>Memory</i> for read operations
+ */
+ static Memory wrap(final double[] array) {
+ Objects.requireNonNull(array, "array must be non-null");
+ final long lengthBytes = array.length << Prim.DOUBLE.shift();
+ return BaseWritableMemoryImpl.wrapHeapArray(array, 0L, lengthBytes, true, ByteOrder.nativeOrder(), null);
+ }
+ //END OF CONSTRUCTOR-TYPE METHODS
+
+ //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 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 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-java11/src/test/java/org/apache/datasketches/memory/internal/VirtualMachineMemoryTest.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/MemoryBoundsException.java
similarity index 64%
copy from datasketches-memory-java11/src/test/java/org/apache/datasketches/memory/internal/VirtualMachineMemoryTest.java
copy to datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/MemoryBoundsException.java
index 2793b30b..501012d1 100644
--- a/datasketches-memory-java11/src/test/java/org/apache/datasketches/memory/internal/VirtualMachineMemoryTest.java
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/MemoryBoundsException.java
@@ -17,17 +17,24 @@
* under the License.
*/
-package org.apache.datasketches.memory.internal;
+package org.apache.datasketches.memory;
-import org.testng.annotations.Test;
+/**
+ * Specific RuntimeException for bounds violations.
+ *
+ * @author Lee Rhodes
+ */
+public class MemoryBoundsException extends MemoryException {
+ private static final long serialVersionUID = 1L;
-@SuppressWarnings({"unused"})
-public class VirtualMachineMemoryTest {
+ /**
+ * The associated operation violated access bounds with required details.
+ *
+ * @param details of the violation.
+ */
+ public MemoryBoundsException(final String details) {
+ super(details);
+ }
- @Test
- public void inertPageAlignment() {
- boolean result = VirtualMachineMemory.getIsPageAligned();
- //System.out.println("VM page alignment:" + result);
- assert (true); //no exception was thrown
- }
}
+
diff --git a/datasketches-memory-java11/src/test/java/org/apache/datasketches/memory/internal/VirtualMachineMemoryTest.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/MemoryException.java
similarity index 54%
copy from datasketches-memory-java11/src/test/java/org/apache/datasketches/memory/internal/VirtualMachineMemoryTest.java
copy to datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/MemoryException.java
index 2793b30b..6b3663ca 100644
--- a/datasketches-memory-java11/src/test/java/org/apache/datasketches/memory/internal/VirtualMachineMemoryTest.java
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/MemoryException.java
@@ -17,17 +17,26 @@
* under the License.
*/
-package org.apache.datasketches.memory.internal;
+package org.apache.datasketches.memory;
-import org.testng.annotations.Test;
-
-@SuppressWarnings({"unused"})
-public class VirtualMachineMemoryTest {
+/**
+ * Specific RuntimeExceptions for the datasketches-memory component.
+ *
+ * @author Lee Rhodes
+ *
+ */
+public class MemoryException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
- @Test
- public void inertPageAlignment() {
- boolean result = VirtualMachineMemory.getIsPageAligned();
- //System.out.println("VM page alignment:" + result);
- assert (true); //no exception was thrown
- }
+ /**
+ * 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-java21/src/main/java/org/apache/datasketches/memory/MemoryRequestServer.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/MemoryRequestServer.java
new file mode 100644
index 00000000..9f4b2c48
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/MemoryRequestServer.java
@@ -0,0 +1,63 @@
+/*
+ * 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 off-heap, heap and ByteBuffer
+ * backed resources to request more memory.
+ *
+ * @author Lee Rhodes
+ */
+public interface MemoryRequestServer {
+
+ /**
+ * Request new WritableMemory with the given newCapacityBytes. The current Writable Memory will be used to
+ * determine the byte order of the returned WritableMemory and other properties.
+ * @param currentWritableMemory the current writableMemory of the client. It must be non-null.
+ * @param newCapacityBytes The capacity being requested. It must be > the capacity of the currentWritableMemory.
+ *
+ * @return new WritableMemory with the requested capacity.
+ */
+ WritableMemory request(
+ WritableMemory currentWritableMemory,
+ long newCapacityBytes);
+
+ /**
+ * Request to close the resource, if applicable.
+ *
+ * @param memToClose the relevant WritableMemory to be considered for closing. It must be non-null.
+ */
+ default void requestClose(final WritableMemory memToClose) {
+ requestClose(memToClose, null);
+ }
+
+ /**
+ * Request to close the resource, if applicable.
+ *
+ * @param memToClose the relevant WritbleMemory to be considered for closing. It must be non-null.
+ * @param newMemory the newly allocated WritableMemory.
+ * The newMemory reference is returned from the client for the convenience of the system that
+ * owns the responsibility of memory allocation. It may be null.
+ */
+ void requestClose(
+ final WritableMemory memToClose,
+ WritableMemory newMemory);
+
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/MurmurHash3v2.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/MurmurHash3v2.java
new file mode 100644
index 00000000..916494f8
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/MurmurHash3v2.java
@@ -0,0 +1,384 @@
+/*
+ * 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 java.nio.charset.StandardCharsets.UTF_8;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.unsafe;
+
+/**
+ * The MurmurHash3 is a fast, non-cryptographic, 128-bit hash function that has
+ * excellent avalanche and 2-way bit independence properties.
+ *
+ * <p>Austin Appleby's C++
+ * <a href="https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp">
+ * MurmurHash3_x64_128(...), final revision 150</a>,
+ * which is in the Public Domain, was the inspiration for this implementation in Java.</p>
+ *
+ * <p>This implementation of the MurmurHash3 allows hashing of a block of on-heap Memory defined by an offset
+ * and length. The calling API also allows the user to supply the small output array of two longs,
+ * so that the entire hash function is static and free of object allocations.</p>
+ *
+ * <p>This implementation produces exactly the same hash result as the
+ * MurmurHash3 function in datasketches-java given compatible inputs.</p>
+ *
+ * @author Lee Rhodes
+ */
+public final class MurmurHash3v2 {
+ private static final long C1 = 0x87c37b91114253d5L;
+ private static final long C2 = 0x4cf5ad432745937fL;
+
+ //Provided for backward compatibility
+
+ /**
+ * Returns a 128-bit hash of the input.
+ * Provided for compatibility with older version of MurmurHash3,
+ * but empty or null input now throws IllegalArgumentException.
+ * @param in long array
+ * @param seed A long valued seed.
+ * @return the hash
+ */
+ public static long[] hash(
+ final long[] in,
+ final long seed) {
+ if ((in == null) || (in.length == 0)) {
+ throw new IllegalArgumentException("Input in is empty or null.");
+ }
+ return hash(Memory.wrap(in), 0L, in.length << 3, seed, new long[2]);
+ }
+
+ /**
+ * Returns a 128-bit hash of the input.
+ * Provided for compatibility with older version of MurmurHash3,
+ * but empty or null input now throws IllegalArgumentException.
+ * @param in int array
+ * @param seed A long valued seed.
+ * @return the hash
+ */
+ public static long[] hash(
+ final int[] in,
+ final long seed) {
+ if ((in == null) || (in.length == 0)) {
+ throw new IllegalArgumentException("Input in is empty or null.");
+ }
+ return hash(Memory.wrap(in), 0L, in.length << 2, seed, new long[2]);
+ }
+
+ /**
+ * Returns a 128-bit hash of the input.
+ * Provided for compatibility with older version of MurmurHash3,
+ * but empty or null input now throws IllegalArgumentException.
+ * @param in char array
+ * @param seed A long valued seed.
+ * @return the hash
+ */
+ public static long[] hash(
+ final char[] in,
+ final long seed) {
+ if ((in == null) || (in.length == 0)) {
+ throw new IllegalArgumentException("Input in is empty or null.");
+ }
+ return hash(Memory.wrap(in), 0L, in.length << 1, seed, new long[2]);
+ }
+
+ /**
+ * Returns a 128-bit hash of the input.
+ * Provided for compatibility with older version of MurmurHash3,
+ * but empty or null input now throws IllegalArgumentException.
+ * @param in byte array
+ * @param seed A long valued seed.
+ * @return the hash
+ */
+ public static long[] hash(
+ final byte[] in,
+ final long seed) {
+ if ((in == null) || (in.length == 0)) {
+ throw new IllegalArgumentException("Input in is empty or null.");
+ }
+ return hash(Memory.wrap(in), 0L, in.length, seed, new long[2]);
+ }
+
+ //Single primitive inputs
+
+ /**
+ * Returns a 128-bit hash of the input.
+ * Note the entropy of the resulting hash cannot be more than 64 bits.
+ * @param in a long
+ * @param seed A long valued seed.
+ * @param hashOut A long array of size 2
+ * @return the hash
+ */
+ public static long[] hash(
+ final long in,
+ final long seed,
+ final long[] hashOut) {
+ final long h1 = seed ^ mixK1(in);
+ final long h2 = seed;
+ return finalMix128(h1, h2, 8, hashOut);
+ }
+
+ /**
+ * Returns a 128-bit hash of the input.
+ * Note the entropy of the resulting hash cannot be more than 64 bits.
+ * @param in a double
+ * @param seed A long valued seed.
+ * @param hashOut A long array of size 2
+ * @return the hash
+ */
+ public static long[] hash(
+ final double in,
+ final long seed,
+ final long[] hashOut) {
+ final double d = (in == 0.0) ? 0.0 : in; // canonicalize -0.0, 0.0
+ final long k1 = Double.doubleToLongBits(d); // canonicalize all NaN forms
+ final long h1 = seed ^ mixK1(k1);
+ final long h2 = seed;
+ return finalMix128(h1, h2, 8, hashOut);
+ }
+
+ /**
+ * Returns a 128-bit hash of the input.
+ * An empty or null input throws IllegalArgumentException.
+ * @param in a String
+ * @param seed A long valued seed.
+ * @param hashOut A long array of size 2
+ * @return the hash
+ */
+ public static long[] hash(
+ final String in,
+ final long seed,
+ final long[] hashOut) {
+ if ((in == null) || (in.length() == 0)) {
+ throw new IllegalArgumentException("Input in is empty or null.");
+ }
+ final byte[] byteArr = in.getBytes(UTF_8);
+ return hash(Memory.wrap(byteArr), 0L, byteArr.length, seed, hashOut);
+ }
+
+ //The main API call
+
+ /**
+ * Returns a 128-bit hash of the input as a long array of size 2.
+ *
+ * @param mem The input on-heap Memory. Must be non-null and non-empty,
+ * otherwise throws IllegalArgumentException.
+ * @param offsetBytes the starting point within Memory.
+ * @param lengthBytes the total number of bytes to be hashed.
+ * @param seed A long valued seed.
+ * @param hashOut the size 2 long array for the resulting 128-bit hash
+ * @return the hash.
+ */
+ //@SuppressWarnings("restriction")
+ public static long[] hash(
+ final Memory mem,
+ final long offsetBytes,
+ final long lengthBytes,
+ final long seed,
+ final long[] hashOut) {
+ if ((mem == null) || (mem.getCapacity() == 0L)) {
+ throw new IllegalArgumentException("Input mem is empty or null.");
+ }
+ final Object uObj = ((WritableMemory) mem).getArray();
+ if (uObj == null) {
+ throw new IllegalArgumentException("The backing resource of input mem is not on-heap.");
+ }
+ long cumOff = mem.getCumulativeOffset() + offsetBytes;
+
+ long h1 = seed;
+ long h2 = seed;
+ long rem = lengthBytes;
+
+ // Process the 128-bit blocks (the body) into the hash
+ while (rem >= 16L) {
+ final long k1 = unsafe.getLong(uObj, cumOff); //0, 16, 32, ...
+ final long k2 = unsafe.getLong(uObj, cumOff + 8); //8, 24, 40, ...
+ cumOff += 16L;
+ rem -= 16L;
+
+ h1 ^= mixK1(k1);
+ h1 = Long.rotateLeft(h1, 27);
+ h1 += h2;
+ h1 = (h1 * 5) + 0x52dce729L;
+
+ h2 ^= mixK2(k2);
+ h2 = Long.rotateLeft(h2, 31);
+ h2 += h1;
+ h2 = (h2 * 5) + 0x38495ab5L;
+ }
+
+ // Get the tail (if any): 1 to 15 bytes
+ if (rem > 0L) {
+ long k1 = 0;
+ long k2 = 0;
+ switch ((int) rem) {
+ case 15: {
+ k2 ^= (unsafe.getByte(uObj, cumOff + 14) & 0xFFL) << 48;
+ }
+ //$FALL-THROUGH$
+ case 14: {
+ k2 ^= (unsafe.getShort(uObj, cumOff + 12) & 0xFFFFL) << 32;
+ k2 ^= (unsafe.getInt(uObj, cumOff + 8) & 0xFFFFFFFFL);
+ k1 = unsafe.getLong(uObj, cumOff);
+ break;
+ }
+
+ case 13: {
+ k2 ^= (unsafe.getByte(uObj, cumOff + 12) & 0xFFL) << 32;
+ }
+ //$FALL-THROUGH$
+ case 12: {
+ k2 ^= (unsafe.getInt(uObj, cumOff + 8) & 0xFFFFFFFFL);
+ k1 = unsafe.getLong(uObj, cumOff);
+ break;
+ }
+
+ case 11: {
+ k2 ^= (unsafe.getByte(uObj, cumOff + 10) & 0xFFL) << 16;
+ }
+ //$FALL-THROUGH$
+ case 10: {
+ k2 ^= (unsafe.getShort(uObj, cumOff + 8) & 0xFFFFL);
+ k1 = unsafe.getLong(uObj, cumOff);
+ break;
+ }
+
+ case 9: {
+ k2 ^= (unsafe.getByte(uObj, cumOff + 8) & 0xFFL);
+ }
+ //$FALL-THROUGH$
+ case 8: {
+ k1 = unsafe.getLong(uObj, cumOff);
+ break;
+ }
+
+ case 7: {
+ k1 ^= (unsafe.getByte(uObj, cumOff + 6) & 0xFFL) << 48;
+ }
+ //$FALL-THROUGH$
+ case 6: {
+ k1 ^= (unsafe.getShort(uObj, cumOff + 4) & 0xFFFFL) << 32;
+ k1 ^= (unsafe.getInt(uObj, cumOff) & 0xFFFFFFFFL);
+ break;
+ }
+
+ case 5: {
+ k1 ^= (unsafe.getByte(uObj, cumOff + 4) & 0xFFL) << 32;
+ }
+ //$FALL-THROUGH$
+ case 4: {
+ k1 ^= (unsafe.getInt(uObj, cumOff) & 0xFFFFFFFFL);
+ break;
+ }
+
+ case 3: {
+ k1 ^= (unsafe.getByte(uObj, cumOff + 2) & 0xFFL) << 16;
+ }
+ //$FALL-THROUGH$
+ case 2: {
+ k1 ^= (unsafe.getShort(uObj, cumOff) & 0xFFFFL);
+ break;
+ }
+
+ case 1: {
+ k1 ^= (unsafe.getByte(uObj, cumOff) & 0xFFL);
+ break;
+ }
+ default: break; //can't happen
+ }
+
+ h1 ^= mixK1(k1);
+ h2 ^= mixK2(k2);
+ }
+ return finalMix128(h1, h2, lengthBytes, hashOut);
+ }
+
+ //--Helper methods----------------------------------------------------
+
+ /**
+ * Self mix of k1
+ *
+ * @param k1 input argument
+ * @return mix
+ */
+ private static long mixK1(long k1) {
+ k1 *= C1;
+ k1 = Long.rotateLeft(k1, 31);
+ k1 *= C2;
+ return k1;
+ }
+
+ /**
+ * Self mix of k2
+ *
+ * @param k2 input argument
+ * @return mix
+ */
+ private static long mixK2(long k2) {
+ k2 *= C2;
+ k2 = Long.rotateLeft(k2, 33);
+ k2 *= C1;
+ return k2;
+ }
+
+ /**
+ * Final self mix of h*.
+ *
+ * @param h input to final mix
+ * @return mix
+ */
+ private static long finalMix64(long h) {
+ h ^= h >>> 33;
+ h *= 0xff51afd7ed558ccdL;
+ h ^= h >>> 33;
+ h *= 0xc4ceb9fe1a85ec53L;
+ h ^= h >>> 33;
+ return h;
+ }
+
+ /**
+ * Finalization: Add the length into the hash and mix
+ * @param h1 intermediate hash
+ * @param h2 intermediate hash
+ * @param lengthBytes the length in bytes
+ * @param hashOut the output array of 2 longs
+ * @return hashOut
+ */
+ private static long[] finalMix128(
+ long h1,
+ long h2,
+ final long lengthBytes,
+ final long[] hashOut) {
+ h1 ^= lengthBytes;
+ h2 ^= lengthBytes;
+
+ h1 += h2;
+ h2 += h1;
+
+ h1 = finalMix64(h1);
+ h2 = finalMix64(h2);
+
+ h1 += h2;
+ h2 += h1;
+
+ hashOut[0] = h1;
+ hashOut[1] = h2;
+ return hashOut;
+ }
+
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/Positional.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/Positional.java
new file mode 100644
index 00000000..8e9195f0
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/Positional.java
@@ -0,0 +1,110 @@
+/*
+ * 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;
+
+/**
+ * Defines the relative positional API.
+ * This is different from and simpler than Java ByteBuffer positional API.
+ * <ul><li>All based on longs instead of ints.</li>
+ * <li>Eliminated "mark". Rarely used and confusing with its silent side effects.</li>
+ * <li>The invariants are {@code 0 <= start <= position <= end <= capacity}.</li>
+ * <li>It always starts up as (0, 0, capacity, capacity).</li>
+ * <li>You set (start, position, end) in one call with
+ * {@link #setStartPositionEnd(long, long, long)}</li>
+ * <li>Position can be set directly or indirectly when using the positional get/put methods.
+ * <li>Added incrementPosition(long), which is much easier when you know the increment.</li>
+ * <li>This approach eliminated a number of methods and checks, and has no unseen side effects,
+ * e.g., <i>mark</i> being invalidated.</li>
+ * <li>Clearer method naming (IMHO).</li>
+ * </ul>
+ * @author Lee Rhodes
+ */
+public interface Positional extends Resource {
+
+ /**
+ * Increments the current position by the given increment.
+ * Checks that the resource is alive and that the positional invariants are not violated
+ * @param increment the given increment
+ * @return this Positional
+ * @throws BufferPositionInvariantsException if positional invariants have been violated.
+ */
+ Positional incrementPosition(long increment);
+
+ /**
+ * Gets the end position
+ * @return the end position
+ */
+ long getEnd();
+
+ /**
+ * Gets the current position
+ * @return the current position
+ */
+ long getPosition();
+
+ /**
+ * Gets start position
+ * @return start position
+ */
+ long getStart();
+
+ /**
+ * The number of elements remaining between the current position and the end position
+ * @return {@code (end - position)}
+ */
+ long getRemaining();
+
+ /**
+ * Returns true if there are elements remaining between the current position and the end position
+ * @return {@code (end - position) > 0}
+ */
+ boolean hasRemaining();
+
+ /**
+ * Resets the current position to the start position,
+ * This does not modify any data.
+ * @return this Positional
+ */
+ Positional resetPosition();
+
+ /**
+ * Sets the current position.
+ * Checks that the positional invariants are not violated.
+ * @param position the given current position.
+ * @return this Positional
+ * @throws BufferPositionInvariantsException if positional invariants have been violated.
+ */
+ Positional setPosition(long position);
+
+ /**
+ * Sets start position, current position, and end position.
+ * Checks that the positional invariants are not violated.
+ * @param start the start position in the buffer
+ * @param position the current position between the start and end
+ * @param end the end position in the buffer
+ * @return this Positional
+ * @throws BufferPositionInvariantsException if positional invariants have been violated.
+ */
+ Positional setStartPositionEnd(
+ long start,
+ long position,
+ long end);
+
+}
diff --git a/datasketches-memory-java11/src/test/java/org/apache/datasketches/memory/internal/VirtualMachineMemoryTest.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/ReadOnlyException.java
similarity index 65%
copy from datasketches-memory-java11/src/test/java/org/apache/datasketches/memory/internal/VirtualMachineMemoryTest.java
copy to datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/ReadOnlyException.java
index 2793b30b..3e147952 100644
--- a/datasketches-memory-java11/src/test/java/org/apache/datasketches/memory/internal/VirtualMachineMemoryTest.java
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/ReadOnlyException.java
@@ -17,17 +17,22 @@
* under the License.
*/
-package org.apache.datasketches.memory.internal;
+package org.apache.datasketches.memory;
-import org.testng.annotations.Test;
-
-@SuppressWarnings({"unused"})
-public class VirtualMachineMemoryTest {
+/**
+ * The exception thrown when attempting to write into a read-only Resource.
+ *
+ * @author Praveenkumar Venkatesan
+ */
+public class ReadOnlyException extends MemoryException {
+ private static final long serialVersionUID = 1L;
- @Test
- public void inertPageAlignment() {
- boolean result = VirtualMachineMemory.getIsPageAligned();
- //System.out.println("VM page alignment:" + result);
- assert (true); //no exception was thrown
+ /**
+ * Read Only Exception
+ * @param message the error message
+ */
+ public ReadOnlyException(final String message) {
+ super(message);
}
}
+
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/Resource.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/Resource.java
new file mode 100644
index 00000000..8b001287
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/Resource.java
@@ -0,0 +1,384 @@
+/*
+ * 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;
+
+/**
+ * The base class for Memory and Buffer plus some common static variables and check methods.
+ *
+ * @author Lee Rhodes
+ */
+public interface Resource extends AutoCloseable {
+
+ //MemoryRequestServer logic
+
+ /**
+ * The default MemoryRequestServer is used if not specified by the user.
+ */
+ static MemoryRequestServer defaultMemReqSvr = new DefaultMemoryRequestServer();
+
+ /**
+ * Gets the MemoryRequestServer object, if set, for the below resources to request additional memory.
+ *
+ * <p>WritableMemory enables this for ByteBuffer, Heap and Off-heap Memory backed resources.</p>
+ *
+ * <p>WritableBuffer enables this for ByteBuffer backed resources. However, the object returned is in the form of
+ * a WritableMemory. To convert to WritableBuffer use asWritableBuffer(). To enable for Heap and Off-heap Buffer
+ * resources, use the WritableMemory to configure and then call asWritableBuffer().</p>
+ *
+ * <p>Map backed resources will always return null.</p>
+ *
+ * <p>The user must customize the actions of the MemoryRequestServer by
+ * implementing the MemoryRequestServer interface.</p>
+ *
+ * <p>For WritableMemory, to enable at runtime set your custom MemoryRequestServer using one of these methods:</p>
+ * <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>
+ *
+ * <p>ForWritableBuffer, to enable at runtime set your custom MemoryRequestServer using the following method:</p>
+ * <ul>
+ * <li>{@link WritableBuffer#writableWrap(ByteBuffer, ByteOrder, MemoryRequestServer)}</li>
+ * </ul>
+ *
+ * <p>Simple implementation examples include the DefaultMemoryRequestServer in the main source tree, as well as
+ * the ExampleMemoryRequestServerTest and the use with ByteBuffer documented in the DruidIssue11544Test
+ * in the test source tree.</p>
+ *
+ * @return the MemoryRequestServer object or null.
+ */
+ MemoryRequestServer getMemoryRequestServer();
+
+ /**
+ * Returns true if the MemoryRequestServer has been configured by the user.
+ * @return true if the MemoryRequestServer has been configured by the user..
+ */
+ boolean hasMemoryRequestServer();
+
+ /**
+ * Sets the Default MemoryRequestServer
+ * @param memReqSvr the given MemoryRequestServer.
+ */
+ void setMemoryRequestServer(MemoryRequestServer memReqSvr);
+
+ //***
+
+ /**
+ * Closes this resource if this can be closed via <em>AutoCloseable</em>.
+ * If this operation completes without exceptions, this resource will be marked as <em>not alive</em>,
+ * and subsequent operations on this resource will fail with {@link IllegalStateException}.
+ *
+ * <p>This operation is not idempotent; that is, closing an already closed resource <em>always</em>
+ * results in an exception being thrown. This reflects a deliberate design choice: resource state transitions
+ * should be manifest in the client code; a failure in any of these transitions reveals a bug in the underlying
+ * application logic.</p>
+ *
+ * @throws IllegalStateException if this Resource is not <em>alive</em>.
+ * @throws IllegalStateException if this method is not accessed from the owning thread.
+ * @throws UnsupportedOperationException if this resource is not {@link AutoCloseable}.
+ */
+ @Override
+ void close();
+
+ /**
+ * Return true if this resource is closeable.
+ * @return true if this resource is closeable.
+ */
+ boolean isCloseable();
+
+ /**
+ * Returns true if the given object (<em>that</em>) is an instance of this class and has contents equal to
+ * this object.
+ * @param that the given Resource object
+ * @return true if the given object has equal contents to this object.
+ * @see #equalTo(long, Resource, long, long)
+ */
+ default boolean equalTo(final Resource that) {
+ if (that == null || this.getCapacity() != that.getCapacity()) { return false; }
+ return equalTo(0, that, 0, that.getCapacity());
+ }
+
+ /**
+ * Returns true if the given Resource 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 Resource
+ * @param thatOffsetBytes the starting offset in bytes for the given Resource object
+ * @param lengthBytes the size of the range in bytes
+ * @return true if the given Resource object has equal contents to this object in the given range of bytes.
+ * @throws IllegalStateException if either resource is not <em>alive</em>.
+ * @throws MemoryBoundsException if there is a bounds violation.
+ */
+ boolean equalTo(
+ long thisOffsetBytes,
+ Resource that,
+ long thatOffsetBytes,
+ long lengthBytes);
+
+ /**
+ * Forces any changes made to the contents of this memory-mapped Resource to be written to the storage
+ * device described by the configured file descriptor.
+ *
+ * <p>If the file descriptor associated with this memory-mapped Resource resides on a local storage device then when
+ * this method returns, it is guaranteed that all changes made to this mapped Resource since it was created, or since
+ * this method was last invoked, will have been written to that device.</p>
+ *
+ * <p>If the file descriptor associated with this memory-mapped Resource does not reside on a local device then no
+ * such guarantee is made.</p>
+ *
+ * <p>If this memory-mapped Resource was not mapped in read/write mode
+ * ({@link java.nio.channels.FileChannel.MapMode#READ_WRITE}) then invoking this method may have no effect.
+ * In particular, this method has no effect for files mapped in read-only or private
+ * mapping modes. This method may or may not have an effect for implementation-specific mapping modes.</p>
+ *
+ * @throws IllegalStateException if this Resource is not <em>alive</em>.
+ * @throws IllegalStateException if this method is not accessed from the owning thread.
+ * @throws UnsupportedOperationException if this Resource is not memory-mapped, e.g. if {@code isMapped() == false}.
+ * @throws ReadOnlyException if this Resource is read-only.
+ * @throws RuntimeException if there is some other error writing the contents of this
+ * memory-mapped Resource to the associated storage device.
+ */
+ void force();
+
+ /**
+ * 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.
+ */
+ default long getCumulativeOffset() {
+ return getCumulativeOffset(0);
+ }
+
+ /**
+ * 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 base address of the
+ * backing resource. This does not include the object header for heap arrays nor the initial
+ * offset of a memory-mapped file.
+ * @return the offset of address zero of this object relative to the base address of the
+ * backing resource.
+ */
+ long getRelativeOffset();
+
+ /**
+ * Gets the current ByteOrder.
+ * This may be different from the ByteOrder of the backing resource and {@link ByteOrder#nativeOrder()}
+ * @return the current ByteOrder.
+ */
+ ByteOrder getTypeByteOrder();
+
+ /**
+ * 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 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);
+
+ /**
+ * If true, the backing resource is off-heap memory.
+ * This is the case for allocated off-heap memory, memory-mapped files,
+ * or from a wrapped ByteBuffer that was allocated off-heap.
+ * If false, the backing resource is the Java heap.
+ * @return true if the backing resource is off-heap memory.
+ */
+ boolean isDirect();
+
+ /**
+ * Returns true if this instance is a duplicate of a Buffer instance.
+ * @return true if this instance is a duplicate of a Buffer instance.
+ */
+ boolean isDuplicate();
+
+ /**
+ * Returns true if this object is backed by an on-heap primitive array or an on-heap ByteBuffer.
+ * @return true if this object is backed by an on-heap primitive array or an on-heap ByteBuffer.
+ */
+ boolean isHeap();
+
+ /**
+ * Tells whether or not the contents of this memory-mapped Resource is resident in physical memory.
+ *
+ * <p>A return value of {@code true} implies that it is highly likely that all of the data in this memory-mapped
+ * Resource is resident in physical memory and may therefore be accessed without incurring any virtual-memory page
+ * faults or I/O operations.</p>
+ *
+ * <p>A return value of {@code false} does not necessarily imply that all of the data in this memory-mapped Resource
+ * is not resident in physical memory.</p>
+ *
+ * <p>The returned value is a hint, rather than a guarantee, because the underlying operating system may have paged
+ * out some of this Resource's data by the time that an invocation of this method returns.</p>
+ *
+ * @return true if it is likely that all of the data in this memory-mapped Resource is resident in physical memory
+ *
+ * @throws IllegalStateException if this Resource is not <em>alive</em>.
+ * @throws IllegalStateException if this method is not accessed from the owning thread.
+ * @throws UnsupportedOperationException if this Resource is not memory-mapped, e.g. if {@code isMapped() == false}.
+ */
+ boolean isLoaded();
+
+ /**
+ * If true, this is a <i>Memory</i> or <i>WritableMemory</i> instance, which provides the Memory API.
+ * The Memory API is the principal API for this Memory Component.
+ * It provides a rich variety of direct manipulations of four types of resources:
+ * On-heap memory, off-heap memory, memory-mapped files, and ByteBuffers.
+ * If false, this is a <i>Buffer</i> or <i>WritableBuffer</i> instance, which provides the Buffer API.
+ *
+ * <p>The Buffer API is largely parallel to the Memory API except that it adds a positional API
+ * similar to that in <i>ByteBuffer</i>. The positional API is a convenience when iterating over structured
+ * arrays, or buffering input or output streams (thus the name).</p>
+ *
+ * @return true if this is a <i>Memory</i> or <i>WritableMemory</i> instance, which provides the Memory API,
+ * otherwise this is a <i>Buffer</i> or <i>WritableBuffer</i> instance, which provides the Buffer API.
+ */
+ boolean isMemory();
+
+ /**
+ * Returns true if the backing resource is a memory-mapped file.
+ * @return true if the backing resource is a memory-mapped file.
+ */
+ boolean isMapped();
+
+ /**
+ * If true, all put and get operations will assume the non-native ByteOrder.
+ * Otherwise, all put and get operations will assume the native ByteOrder.
+ * @return true, if all put and get operations will assume the non-native ByteOrder.
+ */
+ boolean isNonNativeOrder();
+
+ /**
+ * 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 this instance is a region view of another Memory or Buffer
+ * @return true if this instance is a region view of another Memory or Buffer
+ */
+ boolean isRegionView();
+
+ /**
+ * 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 Resource
+ * @return true if the backing resource of <i>this</i> is the same as the backing resource
+ * of <i>that</i>.
+ */
+ boolean isSameResource(Resource that);
+
+ /**
+ * Returns true if this object is alive and has not been closed.
+ * This is relevant only for off-heap memory and memory-mapped Files.
+ * @return true if this object is alive and has not been closed.
+ */
+ boolean isAlive();
+
+ /**
+ * Loads the contents of this memory-mapped Resource into physical memory.
+ *
+ * <p>This method makes a best effort to ensure that, when it returns, this contents of the memory-mapped Resource is
+ * resident in physical memory. Invoking this method may cause some number of page faults and
+ * I/O operations to occur.</p>
+ *
+ * @throws IllegalStateException if this Resource is not <em>alive</em>.
+ * @throws IllegalStateException if this method is not accessed from the owning thread.
+ * @throws UnsupportedOperationException if this Resource is not memory-mapped, e.g. if {@code isMapped() == false}.
+ */
+ void load();
+
+ /**
+ * Returns a description of this object with an optional formatted hex string of the data
+ * for the specified a range. 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
+ * @param withData include output listing of byte data in the given range
+ * @return a formatted hex string in a human readable array
+ */
+ String toString(
+ String header,
+ long offsetBytes,
+ int lengthBytes,
+ boolean withData);
+
+ /**
+ * Returns a brief description of this object.
+ * @return a brief description of this object.
+ */
+ @Override
+ String toString();
+
+ /**
+ * 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);
+
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/WritableBuffer.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/WritableBuffer.java
new file mode 100644
index 00000000..d015c8e3
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/WritableBuffer.java
@@ -0,0 +1,429 @@
+/*
+ * 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.BaseWritableBufferImpl;
+
+/**
+ * Defines the writable API for relative positional access to a resource
+ *
+ * @author Lee Rhodes
+ */
+public interface WritableBuffer extends Buffer {
+
+ //BYTE BUFFER
+ /**
+ * Provides a view of the given <i>ByteBuffer</i> for write operations.
+ * The view is of the entire ByteBuffer independent of <i>position</i> and <i>limit</i>.
+ * However, the returned <i>Buffer</i> will have <i>position</i> and <i>end</i> set to the
+ * ByteBuffer's <i>position</i> and <i>limit</i>, respectively.
+ * The returned WritableBuffer will use the <i>ByteOrder</i> of the <i>ByteBuffer</i>.
+ * @param byteBuffer the given ByteBuffer. It must be non-null and writable.
+ * @return a new <i>WritableBuffer</i> for write operations on the given <i>ByteBuffer</i>.
+ */
+ static WritableBuffer writableWrap(final ByteBuffer byteBuffer) {
+ return writableWrap(byteBuffer, byteBuffer.order(), defaultMemReqSvr);
+ }
+
+ /**
+ * Provides a view of the given <i>ByteBuffer</i> for write operations.
+ * The view is of the entire ByteBuffer independent of <i>position</i> and <i>limit</i>.
+ * However, the returned <i>Buffer</i> will have <i>position</i> and <i>end</i> set to the
+ * ByteBuffer's <i>position</i> and <i>limit</i>, respectively.
+ * The returned WritableBuffer will use the given <i>ByteOrder</i>,
+ * independent of the ByteOrder of the given ByteBuffer.
+ * @param byteBuffer the given ByteBuffer. It must be non-null 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 more memory.
+ * @return a new <i>WritableBuffer</i> for write operations on the given <i>ByteBuffer</i>.
+ * @throws IllegalArgumentException if ByteBuffer is not writable
+ */
+ static WritableBuffer writableWrap(
+ final ByteBuffer byteBuffer,
+ final ByteOrder byteOrder,
+ final MemoryRequestServer memReqSvr) {
+ if (byteBuffer.isReadOnly()) {
+ throw new ReadOnlyException("Cannot create a WritableBuffer from a ReadOnly ByteBuffer.");
+ }
+ return BaseWritableBufferImpl.wrapByteBuffer(byteBuffer, false, byteOrder, memReqSvr);
+ }
+
+ // NO MAP use WritableMemory
+ // NO ALLOCATE OFF-HEAP use WritableMemory
+
+ //DUPLICATES
+ /**
+ * Returns a duplicate writable view of this Buffer with the same but independent values of
+ * <i>start</i>, <i>position</i> and <i>end</i>.
+ * <ul>
+ * <li>Returned object's origin = this object's origin</li>
+ * <li>Returned object's <i>start</i> = this object's <i>start</i></li>
+ * <li>Returned object's <i>position</i> = this object's <i>position</i></li>
+ * <li>Returned object's <i>end</i> = this object's <i>end</i></li>
+ * <li>Returned object's <i>capacity</i> = this object' <i>capacityBytes</i></li>
+ * <li>Returned object's <i>start</i>, <i>position</i> and <i>end</i> are mutable and
+ * independent of this object's <i>start</i>, <i>position</i> and <i>end</i></li>
+ * </ul>
+ * @return a duplicate writable view of this Buffer with the same but independent values of
+ * <i>start</i>, <i>position</i> and <i>end</i>.
+ */
+ WritableBuffer writableDuplicate();
+
+ /**
+ * Returns a duplicate writable view of this Buffer with the same but independent values of
+ * <i>start</i>, <i>position</i> and <i>end</i>, but with the specified byteOrder.
+ * <ul>
+ * <li>Returned object's origin = this object's origin</li>
+ * <li>Returned object's <i>start</i> = this object's <i>start</i></li>
+ * <li>Returned object's <i>position</i> = this object's <i>position</i></li>
+ * <li>Returned object's <i>end</i> = this object's <i>end</i></li>
+ * <li>Returned object's <i>capacity</i> = this object' <i>capacityBytes</i></li>
+ * <li>Returned object's <i>start</i>, <i>position</i> and <i>end</i> are mutable and
+ * independent of this object's <i>start</i>, <i>position</i> and <i>end</i></li>
+ * </ul>
+ * @param byteOrder the given <i>ByteOrder</i>. It must be non-null.
+ * @return a duplicate writable view of this Buffer with the same but independent values of
+ * <i>start</i>, <i>position</i> and <i>end</i>.
+ */
+ WritableBuffer writableDuplicate(ByteOrder byteOrder);
+
+ //REGIONS
+ /**
+ * A writable region is a writable view of this object.
+ * <ul>
+ * <li>Returned object's origin = this object's <i>position</i></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 (<i>end</i> - <i>position</i>)</li>
+ * <li>Returned object's <i>capacity</i> = this object's (<i>end</i> - <i>position</i>)</li>
+ * <li>Returned object's <i>start</i>, <i>position</i> and <i>end</i> are mutable and
+ * independent of this object's <i>start</i>, <i>position</i> and <i>end</i></li>
+ * </ul>
+ * @return a new <i>WritableBuffer</i> representing the defined writable region.
+ */
+ WritableBuffer writableRegion();
+
+ /**
+ * A writable region is a writable view of this object.
+ * <ul>
+ * <li>Returned object's origin = this objects' origin + <i>offsetBytes</i></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> = <i>capacityBytes</i></li>
+ * <li>Returned object's <i>capacity</i> = <i>capacityBytes</i></li>
+ * <li>Returned object's <i>start</i>, <i>position</i> and <i>end</i> are mutable and
+ * independent of this object's <i>start</i>, <i>position</i> and <i>end</i></li>
+ * <li>Returned object's byte order = <i>byteOrder</i></li>
+ * </ul>
+ *
+ * <p><b>Note: </b><i>asWritableMemory()</i> and <i>asMemory()</i>
+ * will return the originating <i>Memory</i> byte order.</p>
+ * @param offsetBytes the starting offset with respect to the origin of this <i>WritableBuffer</i>
+ * @param capacityBytes the <i>capacity</i> of the returned region in bytes
+ * @param byteOrder the given byte order. It must be non-null.
+ * @return a new <i>WritableBuffer</i> representing the defined writable region
+ * with the given offsetBytes, capacityBytes and byte order.
+ */
+ WritableBuffer writableRegion(
+ long offsetBytes,
+ long capacityBytes,
+ ByteOrder byteOrder);
+
+ //AS WRITABLE MEMORY
+ /**
+ * Convert this WritableBuffer to a WritableMemory.
+ * If this object's capacity is zero, the returned object is effectively immutable and
+ * the backing storage and byte order are unspecified.
+ * @return WritableMemory
+ */
+ default WritableMemory asWritableMemory() {
+ return asWritableMemory(ByteOrder.nativeOrder());
+ }
+
+ /**
+ * Convert this WritableBuffer to a WritableMemory with the given byte order.
+ * If this object's capacity is zero, the returned object is effectively immutable and
+ * the backing storage and byte order are unspecified.
+ * @param byteOrder the byte order to be used. It must be non-null.
+ * @return WritableMemory
+ */
+ WritableMemory asWritableMemory(ByteOrder byteOrder);
+
+ //NO ALLOCATE HEAP BYTE ARRAY use WritableMemory
+ //NO WRITABLE WRAP -- ACCESS PRIMITIVE HEAP ARRAYS for WRITE
+ //NO ACCESS PRIMITIVE HEAP ARRAYS for WRITE
+ //END OF CONSTRUCTOR-TYPE METHODS
+
+ //PRIMITIVE putX() and putXArray()
+
+ /**
+ * Puts the boolean value at the current position.
+ * Increments the position by <i>Byte.BYTES</i>.
+ * @param value the value to put
+ */
+ void putBoolean(boolean value);
+
+ /**
+ * Puts the boolean value at the given offset.
+ * This does not change the position.
+ * @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 byte value at the current position.
+ * Increments the position by <i>Byte.BYTES</i>.
+ * @param value the value to put
+ */
+ void putByte(byte value);
+
+ /**
+ * Puts the byte value at the given offset.
+ * This does not change the position.
+ * @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 current position.
+ * Increments the position by <i>Byte.BYTES * (lengthBytes - srcOffsetBytes)</i>.
+ * @param srcArray The source array.
+ * @param srcOffsetBytes offset in array units
+ * @param lengthBytes number of array units to transfer
+ */
+ void putByteArray(
+ byte[] srcArray,
+ int srcOffsetBytes,
+ int lengthBytes);
+
+ /**
+ * Puts the char value at the current position.
+ * Increments the position by <i>Character.BYTES</i>.
+ * @param value the value to put
+ */
+ void putChar(char value);
+
+ /**
+ * Puts the char value at the given offset.
+ * This does not change the position.
+ * @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 current position.
+ * Increments the position by <i>Character.BYTES * (lengthChars - srcOffsetChars)</i>.
+ * @param srcArray The source array.
+ * @param srcOffsetChars offset in array units
+ * @param lengthChars number of array units to transfer
+ */
+ void putCharArray(
+ char[] srcArray,
+ int srcOffsetChars,
+ int lengthChars);
+
+ /**
+ * Puts the double value at the current position.
+ * Increments the position by <i>Double.BYTES</i>.
+ * @param value the value to put
+ */
+ void putDouble(double value);
+
+ /**
+ * Puts the double value at the given offset.
+ * This does not change the position.
+ * @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 current position.
+ * Increments the position by <i>Double.BYTES * (lengthDoubles - srcOffsetDoubles)</i>.
+ * @param srcArray The source array.
+ * @param srcOffsetDoubles offset in array units
+ * @param lengthDoubles number of array units to transfer
+ */
+ void putDoubleArray(
+ double[] srcArray,
+ int srcOffsetDoubles,
+ int lengthDoubles);
+
+ /**
+ * Puts the float value at the current position.
+ * Increments the position by <i>Float.BYTES</i>.
+ * @param value the value to put
+ */
+ void putFloat(float value);
+
+ /**
+ * Puts the float value at the given offset.
+ * This does not change the position.
+ * @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 current position.
+ * Increments the position by <i>Float.BYTES * (lengthFloats - srcOffsetFloats)</i>.
+ * @param srcArray The source array.
+ * @param srcOffsetFloats offset in array units
+ * @param lengthFloats number of array units to transfer
+ */
+ void putFloatArray(
+ float[] srcArray,
+ int srcOffsetFloats,
+ int lengthFloats);
+
+ /**
+ * Puts the int value at the current position.
+ * Increments the position by <i>Integer.BYTES</i>.
+ * @param value the value to put
+ */
+ void putInt(int value);
+
+ /**
+ * Puts the int value at the given offset.
+ * This does not change the position.
+ * @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 current position.
+ * Increments the position by <i>Integer.BYTES * (lengthInts - srcOffsetInts)</i>.
+ * @param srcArray The source array.
+ * @param srcOffsetInts offset in array units
+ * @param lengthInts number of array units to transfer
+ */
+ void putIntArray(
+ int[] srcArray,
+ int srcOffsetInts,
+ int lengthInts);
+
+ /**
+ * Puts the long value at the current position.
+ * Increments the position by <i>Long.BYTES</i>.
+ * @param value the value to put
+ */
+ void putLong(long value);
+
+ /**
+ * Puts the long value at the given offset.
+ * This does not change the position.
+ * @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 current position.
+ * Increments the position by <i>Long.BYTES * (lengthLongs - srcOffsetLongs)</i>.
+ * @param srcArray The source array.
+ * @param srcOffsetLongs offset in array units
+ * @param lengthLongs number of array units to transfer
+ */
+ void putLongArray(
+ long[] srcArray,
+ int srcOffsetLongs,
+ int lengthLongs);
+
+ /**
+ * Puts the short value at the current position.
+ * Increments the position by <i>Short.BYTES</i>.
+ * @param value the value to put
+ */
+ void putShort(short value);
+
+ /**
+ * Puts the short value at the given offset.
+ * This does not change the position.
+ * @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 current position.
+ * Increments the position by <i>Short.BYTES * (lengthShorts - srcOffsetShorts)</i>.
+ * @param srcArray The source array.
+ * @param srcOffsetShorts offset in array units
+ * @param lengthShorts number of array units to transfer
+ */
+ void putShortArray(
+ short[] srcArray,
+ int srcOffsetShorts,
+ int lengthShorts);
+
+ // NO ATOMIC METHODS
+
+ //OTHER WRITE METHODS
+ /**
+ * Returns the primitive backing array, otherwise null.
+ * @return the primitive backing array, otherwise null.
+ */
+ Object getArray();
+
+ /**
+ * Clears all bytes of this Buffer from position to end to zero. The position will be set to end.
+ */
+ void clear();
+
+ //NO clearBits(...)
+
+ /**
+ * Fills this Buffer from position to end with the given byte value.
+ * The position will be set to <i>end</i>.
+ * @param value the given byte value
+ */
+ void fill(byte value);
+
+ //NO fill(offsetBytes, lengthBytes, value)
+
+ //NO setBits(...)
+
+ //NO OTHER WRITABLE API METHODS
+
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/WritableMemory.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/WritableMemory.java
new file mode 100644
index 00000000..1d9662bb
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/WritableMemory.java
@@ -0,0 +1,587 @@
+/*
+ * 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.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Objects;
+
+import org.apache.datasketches.memory.internal.BaseWritableMemoryImpl;
+import org.apache.datasketches.memory.internal.Prim;
+
+/**
+ * Defines the writable API for offset access to a resource.
+ *
+ * @author Lee Rhodes
+ */
+public interface WritableMemory extends Memory {
+
+ //BYTE BUFFER
+ /**
+ * Provides a view of the given <i>ByteBuffer</i> for write operations.
+ * The view is of the entire ByteBuffer independent of <i>position</i> and <i>limit</i>.
+ * The returned <i>WritableMemory</i> will assume the <i>ByteOrder</i> of the given <i>ByteBuffer</i>.
+ * @param byteBuffer the given <i>ByteBuffer</i>. It must be non-null and writable.
+ * @return a new <i>WritableMemory</i> for write operations on the given <i>ByteBuffer</i>.
+ */
+ static WritableMemory writableWrap(final ByteBuffer byteBuffer) {
+ return writableWrap(byteBuffer, byteBuffer.order(), null);
+ }
+
+ /**
+ * Provides a view of the given <i>ByteBuffer</i> for write operations.
+ * The view is of the entire ByteBuffer independent of <i>position</i> and <i>limit</i>.
+ * The returned <i>WritableMemory</i> will use the given <i>ByteOrder</i>,
+ * independent of the ByteOrder of the given ByteBuffer.
+ * This does not affect the ByteOrder of data already in the ByteBuffer.
+ * @param byteBuffer the given <i>ByteBuffer</i>. It must be non-null 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>.
+ * @throws IllegalArgumentException if ByteBuffer is not writable.
+ */
+ static WritableMemory writableWrap(
+ final ByteBuffer byteBuffer,
+ final ByteOrder byteOrder,
+ final MemoryRequestServer memReqSvr) {
+ if (byteBuffer.isReadOnly()) { throw new ReadOnlyException("byteBuffer must be writable."); }
+ return BaseWritableMemoryImpl.wrapByteBuffer(byteBuffer, false, byteOrder, memReqSvr);
+ }
+
+ //Duplicates make no sense here
+
+ //No Mapping in Java 21
+
+ //ALLOCATE OFF-HEAP
+
+ /**
+ * Allocates and provides access to capacityBytes directly in off-heap memory.
+ * Native byte order is assumed.
+ * The allocated memory will be 8-byte aligned.
+ *
+ * <p><b>NOTE:</b> Off-heap 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.
+ * @return WritableMemory for this off-heap resource.
+ */
+ static WritableMemory allocateDirect(final long capacityBytes) {
+ return allocateDirect(capacityBytes, ByteOrder.nativeOrder(), null);
+ }
+
+ /**
+ * Allocates and provides access to capacityBytes directly in off-heap memory.
+ * The allocated memory will be 8-byte aligned.
+ *
+ * <p><b>NOTE:</b> Off-heap 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.
+ * @param byteOrder the given <i>ByteOrder</i>. 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 off-heap memory to request more memory.
+ * @return a WritableMemory for this off-heap resource.
+ */
+ static WritableMemory allocateDirect(
+ final long capacityBytes,
+ final ByteOrder byteOrder,
+ final MemoryRequestServer memReqSvr) {
+ 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(
+ final long offsetBytes,
+ final 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.
+ * @param capacityBytes the capacity of the returned object in bytes.
+ * @param byteOrder the given <i>ByteOrder</i>. 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 <i>ByteOrder</i>. It must be non-null.
+ * @return a new <i>WritableBuffer</i> with a view of this WritableMemory
+ */
+ WritableBuffer asWritableBuffer(ByteOrder byteOrder);
+
+ //ALLOCATE HEAP BYTE ARRAYS
+
+ /**
+ * Creates on-heap WritableMemory with the given capacity and the native byte order.
+ * @param capacityBytes the given capacity in bytes.
+ * @return a new WritableMemory for write operations on a new byte array.
+ */
+ static WritableMemory allocate(final int capacityBytes) {
+ return allocate(capacityBytes, ByteOrder.nativeOrder(), null);
+ }
+
+ /**
+ * Creates on-heap WritableMemory with the given capacity and the given byte order.
+ * @param capacityBytes the given capacity in bytes.
+ * @param byteOrder the given <i>ByteOrder</i>. It must be non-null.
+ * @return a new WritableMemory for write operations on a new byte array.
+ */
+ static WritableMemory allocate(
+ final int capacityBytes,
+ final ByteOrder byteOrder) {
+ return allocate(capacityBytes, byteOrder, null);
+ }
+
+ /**
+ * Creates on-heap WritableMemory with the given capacity and the given byte order.
+ * @param capacityBytes the given capacity in bytes.
+ * @param byteOrder the given <i>ByteOrder</i>. 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(
+ final int capacityBytes,
+ final ByteOrder byteOrder,
+ final MemoryRequestServer memReqSvr) {
+ final byte[] arr = new byte[capacityBytes];
+ negativeCheck(capacityBytes, "capacityBytes");
+ return writableWrap(arr, 0, capacityBytes, byteOrder, memReqSvr);
+ }
+
+ //WRITABLE WRAP - 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.
+ * @return a new WritableMemory for write operations on the given primitive array.
+ */
+ static WritableMemory writableWrap(final byte[] array) {
+ return writableWrap(array, 0, array.length, ByteOrder.nativeOrder(), null);
+ }
+
+ /**
+ * 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 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(
+ final byte[] array,
+ final ByteOrder byteOrder) {
+ return writableWrap(array, 0, array.length, byteOrder, null);
+ }
+
+ /**
+ * 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.
+ * @param lengthBytes the number of bytes to include from the given array.
+ * @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(
+ final byte[] array,
+ final int offsetBytes,
+ final int lengthBytes,
+ final ByteOrder byteOrder) {
+ return writableWrap(array, offsetBytes, lengthBytes, byteOrder, null);
+ }
+
+ /**
+ * 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(
+ final byte[] array,
+ final int offsetBytes,
+ final int lengthBytes,
+ final ByteOrder byteOrder,
+ final MemoryRequestServer memReqSvr) {
+ 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.
+ * @return a new WritableMemory for write operations on the given primitive array.
+ */
+ static WritableMemory writableWrap(final 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.
+ * @return a new WritableMemory for write operations on the given primitive array.
+ */
+ static WritableMemory writableWrap(final short[] array) {
+ Objects.requireNonNull(array, "array 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.
+ * @return a new WritableMemory for write operations on the given primitive array.
+ */
+ static WritableMemory writableWrap(final int[] array) {
+ Objects.requireNonNull(array, "array 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.
+ * @return a new WritableMemory for write operations on the given primitive array.
+ */
+ static WritableMemory writableWrap(final long[] array) {
+ Objects.requireNonNull(array, "array 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.
+ * @return a new WritableMemory for write operations on the given primitive array.
+ */
+ static WritableMemory writableWrap(final float[] array) {
+ Objects.requireNonNull(array, "array 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.
+ * @return a new WritableMemory for write operations on the given primitive array.
+ */
+ static WritableMemory writableWrap(final double[] array) {
+ Objects.requireNonNull(array, "array 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 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);
+
+ /**
+ * 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);
+
+ //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);
+
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/XxHash.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/XxHash.java
new file mode 100644
index 00000000..281f3409
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/XxHash.java
@@ -0,0 +1,202 @@
+/*
+ * 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.XxHash64.hash;
+import static org.apache.datasketches.memory.internal.XxHash64.hashBytes;
+import static org.apache.datasketches.memory.internal.XxHash64.hashChars;
+import static org.apache.datasketches.memory.internal.XxHash64.hashDoubles;
+import static org.apache.datasketches.memory.internal.XxHash64.hashFloats;
+import static org.apache.datasketches.memory.internal.XxHash64.hashInts;
+import static org.apache.datasketches.memory.internal.XxHash64.hashLongs;
+import static org.apache.datasketches.memory.internal.XxHash64.hashShorts;
+
+/**
+ * The XxHash is a fast, non-cryptographic, 64-bit hash function that has
+ * excellent avalanche and 2-way bit independence properties.
+ * This java version adapted the C++ version and the OpenHFT/Zero-Allocation-Hashing implementation
+ * referenced below as inspiration.
+ *
+ * <p>The C++ source repository:
+ * <a href="https://github.com/Cyan4973/xxHash">
+ * https://github.com/Cyan4973/xxHash</a>. It has a BSD 2-Clause License:
+ * <a href="http://www.opensource.org/licenses/bsd-license.php">
+ * http://www.opensource.org/licenses/bsd-license.php</a>. See LICENSE.
+ *
+ * <p>Portions of this code were adapted from
+ * <a href="https://github.com/OpenHFT/Zero-Allocation-Hashing/blob/master/src/main/java/net/openhft/hashing/XxHash.java">
+ * OpenHFT/Zero-Allocation-Hashing</a>, which has an Apache 2 license as does this site. See LICENSE.
+ *
+ * @author Lee Rhodes
+ */
+public final class XxHash {
+
+ private XxHash() { /* singleton */ }
+
+ /**
+ * Hash the given arr starting at the given offset and continuing for the given length using the
+ * given seed.
+ * @param arr the given array
+ * @param offsetBytes starting at this offset
+ * @param lengthBytes continuing for this length
+ * @param seed the given seed
+ * @return the hash
+ */
+ public static long hashByteArr(
+ final byte[] arr,
+ final long offsetBytes,
+ final long lengthBytes,
+ final long seed) {
+ return hashBytes(arr, offsetBytes, lengthBytes, seed);
+ }
+
+ /**
+ * Hash the given arr starting at the given offset and continuing for the given length using the
+ * given seed.
+ * @param arr the given array
+ * @param offsetShorts starting at this offset
+ * @param lengthShorts continuing for this length
+ * @param seed the given seed
+ * @return the hash
+ */
+ public static long hashShortArr(
+ final short[] arr,
+ final long offsetShorts,
+ final long lengthShorts,
+ final long seed) {
+ return hashShorts(arr, offsetShorts, lengthShorts, seed);
+ }
+
+ /**
+ * Hash the given arr starting at the given offset and continuing for the given length using the
+ * given seed.
+ * @param arr the given array
+ * @param offsetChars starting at this offset
+ * @param lengthChars continuing for this length
+ * @param seed the given seed
+ * @return the hash
+ */
+ public static long hashCharArr(
+ final char[] arr,
+ final long offsetChars,
+ final long lengthChars,
+ final long seed) {
+ return hashChars(arr, offsetChars, lengthChars, seed);
+ }
+
+ /**
+ * Hash the given arr starting at the given offset and continuing for the given length using the
+ * given seed.
+ * @param arr the given array
+ * @param offsetInts starting at this offset
+ * @param lengthInts continuing for this length
+ * @param seed the given seed
+ * @return the hash
+ */
+ public static long hashIntArr(
+ final int[] arr,
+ final long offsetInts,
+ final long lengthInts,
+ final long seed) {
+ return hashInts(arr, offsetInts, lengthInts, seed);
+ }
+
+ /**
+ * Hash the given arr starting at the given offset and continuing for the given length using the
+ * given seed.
+ * @param arr the given array
+ * @param offsetLongs starting at this offset
+ * @param lengthLongs continuing for this length
+ * @param seed the given seed
+ * @return the hash
+ */
+ public static long hashLongArr(
+ final long[] arr,
+ final long offsetLongs,
+ final long lengthLongs,
+ final long seed) {
+ return hashLongs(arr, offsetLongs, lengthLongs, 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.
+ */
+ public static long hashLong(
+ final long in,
+ final long seed) {
+ return hash(in, seed);
+ }
+
+ /**
+ * Hash the given arr starting at the given offset and continuing for the given length using the
+ * given seed.
+ * @param arr the given array
+ * @param offsetFloats starting at this offset
+ * @param lengthFloats continuing for this length
+ * @param seed the given seed
+ * @return the hash
+ */
+ public static long hashFloatArr(
+ final float[] arr,
+ final long offsetFloats,
+ final long lengthFloats,
+ final long seed) {
+ return hashFloats(arr, offsetFloats, lengthFloats, seed);
+ }
+
+ /**
+ * Hash the given arr starting at the given offset and continuing for the given length using the
+ * given seed.
+ * @param arr the given array
+ * @param offsetDoubles starting at this offset
+ * @param lengthDoubles continuing for this length
+ * @param seed the given seed
+ * @return the hash
+ */
+ public static long hashDoubleArr(
+ final double[] arr,
+ final long offsetDoubles,
+ final long lengthDoubles,
+ final long seed) {
+ return hashDoubles(arr, offsetDoubles, lengthDoubles, seed);
+ }
+
+ /**
+ * Hash the given arr starting at the given offset and continuing for the given length using the
+ * given seed.
+ * @param str the given string
+ * @param offsetChars starting at this offset
+ * @param lengthChars continuing for this length
+ * @param seed the given seed
+ * @return the hash
+ */
+ public static long hashString(
+ final String str,
+ final long offsetChars,
+ final long lengthChars,
+ final long seed) {
+ return org.apache.datasketches.memory.internal.XxHash64.hashString(str, offsetChars, lengthChars, seed);
+ }
+
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/AccessByteBuffer.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/AccessByteBuffer.java
new file mode 100644
index 00000000..f0a05901
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/AccessByteBuffer.java
@@ -0,0 +1,96 @@
+/*
+ * 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.unsafe;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Acquires access to a ByteBuffer.
+ *
+ * @author Lee Rhodes
+ * @author Praveenkumar Venkatesan
+ * @author Roman Leventov
+ */
+//@SuppressWarnings("restriction")
+final class AccessByteBuffer {
+
+ static final ByteBuffer ZERO_READ_ONLY_DIRECT_BYTE_BUFFER =
+ ByteBuffer.allocateDirect(0).asReadOnlyBuffer();
+
+ private static final long NIO_BUFFER_ADDRESS_FIELD_OFFSET =
+ UnsafeUtil.getFieldOffset(java.nio.Buffer.class, "address");
+ private static final long NIO_BUFFER_CAPACITY_FIELD_OFFSET =
+ UnsafeUtil.getFieldOffset(java.nio.Buffer.class, "capacity");
+ private static final long BYTE_BUFFER_HB_FIELD_OFFSET =
+ UnsafeUtil.getFieldOffset(java.nio.ByteBuffer.class, "hb");
+ private static final long BYTE_BUFFER_OFFSET_FIELD_OFFSET =
+ UnsafeUtil.getFieldOffset(java.nio.ByteBuffer.class, "offset");
+
+ final long nativeBaseOffset;
+ final long initialCumOffset;
+ final long capacityBytes;
+ final long offsetBytes;
+ final Object unsafeObj;
+ final boolean resourceReadOnly;
+ final ByteOrder byteOrder; //not used externally, here for reference.
+
+ /**
+ * The given ByteBuffer may be either readOnly or writable
+ * @param byteBuf the given ByteBuffer
+ */
+ AccessByteBuffer(final ByteBuffer byteBuf) {
+ capacityBytes = byteBuf.capacity();
+ resourceReadOnly = byteBuf.isReadOnly();
+ byteOrder = byteBuf.order();
+ final boolean direct = byteBuf.isDirect();
+ if (direct) {
+ nativeBaseOffset = ((sun.nio.ch.DirectBuffer) byteBuf).address();
+ unsafeObj = null;
+ offsetBytes = 0L; //address() is already adjusted for direct slices, so offset = 0
+ initialCumOffset = nativeBaseOffset;
+ } else {
+ nativeBaseOffset = 0L;
+ // ByteBuffer.arrayOffset() and ByteBuffer.array() throw ReadOnlyBufferException if
+ // ByteBuffer is read-only, so this uses reflection for both writable and read-only cases.
+ // OffsetBytes includes the slice() offset for heap. The original array is still there because
+ // this is a view.
+ offsetBytes = unsafe.getInt(byteBuf, BYTE_BUFFER_OFFSET_FIELD_OFFSET);
+ unsafeObj = unsafe.getObject(byteBuf, BYTE_BUFFER_HB_FIELD_OFFSET);
+ initialCumOffset = UnsafeUtil.getArrayBaseOffset(unsafeObj.getClass()) + offsetBytes;
+ }
+ }
+
+ /**
+ * This method is adapted from
+ * https://github.com/odnoklassniki/one-nio/blob/master/src/one/nio/mem/DirectMemory.java
+ * : wrap(...). See LICENSE.
+ */
+ static ByteBuffer getDummyReadOnlyDirectByteBuffer(final long address, final int capacity) {
+ final ByteBuffer byteBuf = ZERO_READ_ONLY_DIRECT_BYTE_BUFFER.duplicate();
+ unsafe.putLong(byteBuf, NIO_BUFFER_ADDRESS_FIELD_OFFSET, address);
+ unsafe.putInt(byteBuf, NIO_BUFFER_CAPACITY_FIELD_OFFSET, capacity);
+ byteBuf.limit(capacity);
+ return byteBuf;
+ }
+
+}
diff --git a/datasketches-memory-java11/src/main/java/org/apache/datasketches/memory/internal/AllocateDirect.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/AllocateDirect.java
similarity index 100%
copy from datasketches-memory-java11/src/main/java/org/apache/datasketches/memory/internal/AllocateDirect.java
copy to datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/AllocateDirect.java
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/BBNonNativeWritableBufferImpl.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/BBNonNativeWritableBufferImpl.java
new file mode 100644
index 00000000..8d0c9d45
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/BBNonNativeWritableBufferImpl.java
@@ -0,0 +1,124 @@
+/*
+ * 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 org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableBuffer;
+
+/**
+ * Implementation of {@link WritableBuffer} for ByteBuffer, non-native byte order.
+ *
+ * @author Roman Leventov
+ * @author Lee Rhodes
+ */
+final class BBNonNativeWritableBufferImpl extends NonNativeWritableBufferImpl {
+ private final ByteBuffer byteBuf; //holds a reference to a ByteBuffer until we are done with it.
+ private final Object unsafeObj;
+ private final long nativeBaseOffset; //raw off-heap address of allocation base if ByteBuffer direct, else 0
+
+ BBNonNativeWritableBufferImpl(
+ final Object unsafeObj,
+ final long nativeBaseOffset,
+ final long offsetBytes,
+ final long capacityBytes,
+ final int typeId,
+ final long cumOffsetBytes,
+ final MemoryRequestServer memReqSvr,
+ final ByteBuffer byteBuf) {
+ super(capacityBytes);
+ this.unsafeObj = unsafeObj;
+ this.nativeBaseOffset = nativeBaseOffset;
+ this.offsetBytes = offsetBytes; //in ResourceImpl
+ this.capacityBytes = capacityBytes; //in ResourceImpl
+ this.typeId = removeNnBuf(typeId) | BYTEBUF | BUFFER | NONNATIVE_BO; //in ResourceImpl
+ this.cumOffsetBytes = cumOffsetBytes; //in ResourceImpl
+ this.memReqSvr = memReqSvr; //in ResourceImpl
+ this.byteBuf = byteBuf;
+ if ((this.owner != null) && (this.owner != Thread.currentThread())) {
+ throw new IllegalStateException(THREAD_EXCEPTION_TEXT);
+ }
+ this.owner = Thread.currentThread(); //in ResourceImpl
+ }
+
+ @Override
+ BaseWritableBufferImpl toWritableRegion(
+ final long regionOffsetBytes,
+ final long capacityBytes,
+ final boolean readOnly,
+ final ByteOrder byteOrder) {
+ final long newOffsetBytes = offsetBytes + regionOffsetBytes;
+ final long newCumOffsetBytes = cumOffsetBytes + regionOffsetBytes;
+ int typeIdOut = removeNnBuf(typeId) | BUFFER | REGION | (readOnly ? READONLY : 0);
+
+ if (Util.isNativeByteOrder(byteOrder)) {
+ typeIdOut |= NATIVE_BO;
+ return new BBWritableBufferImpl(
+ unsafeObj, nativeBaseOffset, newOffsetBytes, capacityBytes, typeIdOut, newCumOffsetBytes, memReqSvr, byteBuf);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new BBNonNativeWritableBufferImpl(
+ unsafeObj, nativeBaseOffset, newOffsetBytes, capacityBytes, typeIdOut, newCumOffsetBytes, memReqSvr, byteBuf);
+ }
+ }
+
+ @Override
+ BaseWritableMemoryImpl toWritableMemory(final boolean readOnly, final ByteOrder byteOrder) {
+ int typeIdOut = removeNnBuf(typeId) | MEMORY | (readOnly ? READONLY : 0);
+
+ if (byteOrder == ByteOrder.nativeOrder()) {
+ typeIdOut |= NATIVE_BO;
+ return new BBWritableMemoryImpl(
+ unsafeObj, nativeBaseOffset, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr, byteBuf);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new BBNonNativeWritableMemoryImpl(
+ unsafeObj, nativeBaseOffset, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr, byteBuf);
+ }
+ }
+
+ @Override
+ BaseWritableBufferImpl toDuplicate(final boolean readOnly, final ByteOrder byteOrder) {
+ int typeIdOut = removeNnBuf(typeId) | BUFFER | DUPLICATE | (readOnly ? READONLY : 0);
+
+ if (byteOrder == ByteOrder.nativeOrder()) {
+ typeIdOut |= NATIVE_BO;
+ return new BBWritableBufferImpl(
+ unsafeObj, nativeBaseOffset, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr, byteBuf);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new BBNonNativeWritableBufferImpl(
+ unsafeObj, nativeBaseOffset, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr, byteBuf);
+ }
+ }
+
+ @Override
+ public ByteBuffer getByteBuffer() {
+ return byteBuf;
+ }
+
+ @Override
+ Object getUnsafeObject() {
+ return unsafeObj;
+ }
+
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/BBNonNativeWritableMemoryImpl.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/BBNonNativeWritableMemoryImpl.java
new file mode 100644
index 00000000..17f80b8f
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/BBNonNativeWritableMemoryImpl.java
@@ -0,0 +1,109 @@
+/*
+ * 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 org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableMemory;
+
+/**
+ * Implementation of {@link WritableMemory} for ByteBuffer, non-native byte order.
+ *
+ * @author Roman Leventov
+ * @author Lee Rhodes
+ */
+final class BBNonNativeWritableMemoryImpl extends NonNativeWritableMemoryImpl {
+ private final ByteBuffer byteBuf; //holds a reference to a ByteBuffer until we are done with it.
+ private final Object unsafeObj;
+ private final long nativeBaseOffset; //raw off-heap address of allocation base if ByteBuffer direct, else 0
+
+ BBNonNativeWritableMemoryImpl(
+ final Object unsafeObj,
+ final long nativeBaseOffset,
+ final long offsetBytes,
+ final long capacityBytes,
+ final int typeId,
+ final long cumOffsetBytes,
+ final MemoryRequestServer memReqSvr,
+ final ByteBuffer byteBuf) {
+ super();
+ this.unsafeObj = unsafeObj;
+ this.nativeBaseOffset = nativeBaseOffset;
+ this.offsetBytes = offsetBytes;
+ this.capacityBytes = capacityBytes;
+ this.typeId = removeNnBuf(typeId) | BYTEBUF | MEMORY | NONNATIVE_BO;
+ this.cumOffsetBytes = cumOffsetBytes;
+ this.memReqSvr = memReqSvr; //in ResourceImpl
+ this.byteBuf = byteBuf;
+ if ((this.owner != null) && (this.owner != Thread.currentThread())) {
+ throw new IllegalStateException(THREAD_EXCEPTION_TEXT);
+ }
+ this.owner = Thread.currentThread();
+ }
+
+ @Override
+ BaseWritableMemoryImpl toWritableRegion(
+ final long regionOffsetBytes,
+ final long capacityBytes,
+ final boolean readOnly,
+ final ByteOrder byteOrder) {
+ final long newOffsetBytes = offsetBytes + regionOffsetBytes;
+ final long newCumOffsetBytes = cumOffsetBytes + regionOffsetBytes;
+ int typeIdOut = removeNnBuf(typeId) | MEMORY | REGION | (readOnly ? READONLY : 0);
+
+ if (Util.isNativeByteOrder(byteOrder)) {
+ typeIdOut |= NATIVE_BO;
+ return new BBWritableMemoryImpl(
+ unsafeObj, nativeBaseOffset, newOffsetBytes, capacityBytes, typeIdOut, newCumOffsetBytes, memReqSvr, byteBuf);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new BBNonNativeWritableMemoryImpl(
+ unsafeObj, nativeBaseOffset, newOffsetBytes, capacityBytes, typeIdOut, newCumOffsetBytes, memReqSvr, byteBuf);
+ }
+ }
+
+ @Override
+ BaseWritableBufferImpl toWritableBuffer(final boolean readOnly, final ByteOrder byteOrder) {
+ int typeIdOut = removeNnBuf(typeId) | BUFFER | (readOnly ? READONLY : 0);
+
+ if (byteOrder == ByteOrder.nativeOrder()) {
+ typeIdOut |= NATIVE_BO;
+ return new BBWritableBufferImpl(
+ unsafeObj, nativeBaseOffset, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr, byteBuf);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new BBNonNativeWritableBufferImpl(
+ unsafeObj, nativeBaseOffset, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr, byteBuf);
+ }
+ }
+
+ @Override
+ public ByteBuffer getByteBuffer() {
+ return byteBuf;
+ }
+
+ @Override
+ Object getUnsafeObject() {
+ return unsafeObj;
+ }
+
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/BBWritableBufferImpl.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/BBWritableBufferImpl.java
new file mode 100644
index 00000000..1cd0316f
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/BBWritableBufferImpl.java
@@ -0,0 +1,124 @@
+/*
+ * 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 org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableBuffer;
+
+/**
+ * Implementation of {@link WritableBuffer} for ByteBuffer, native byte order.
+ *
+ * @author Roman Leventov
+ * @author Lee Rhodes
+ */
+final class BBWritableBufferImpl extends NativeWritableBufferImpl {
+ private final ByteBuffer byteBuf; //holds a reference to a ByteBuffer until we are done with it.
+ private final Object unsafeObj;
+ private final long nativeBaseOffset; //raw off-heap address of allocation base if ByteBuffer direct, else 0
+
+ BBWritableBufferImpl(
+ final Object unsafeObj,
+ final long nativeBaseOffset,
+ final long offsetBytes,
+ final long capacityBytes,
+ final int typeId,
+ final long cumOffsetBytes,
+ final MemoryRequestServer memReqSvr,
+ final ByteBuffer byteBuf) {
+ super(capacityBytes);
+ this.unsafeObj = unsafeObj;
+ this.nativeBaseOffset = nativeBaseOffset;
+ this.offsetBytes = offsetBytes; //in ResourceImpl
+ this.capacityBytes = capacityBytes; //in ResourceImpl
+ this.typeId = removeNnBuf(typeId) | BYTEBUF | BUFFER | NATIVE_BO; //in ResourceImpl
+ this.cumOffsetBytes = cumOffsetBytes; //in ResourceImpl
+ this.memReqSvr = memReqSvr; //in ResourceImpl
+ this.byteBuf = byteBuf;
+ if ((this.owner != null) && (this.owner != Thread.currentThread())) {
+ throw new IllegalStateException(THREAD_EXCEPTION_TEXT);
+ }
+ this.owner = Thread.currentThread(); //in ResourceImpl
+ }
+
+ @Override
+ BaseWritableBufferImpl toWritableRegion(
+ final long regionOffsetBytes,
+ final long capacityBytes,
+ final boolean readOnly,
+ final ByteOrder byteOrder) {
+ final long newOffsetBytes = offsetBytes + regionOffsetBytes;
+ final long newCumOffsetBytes = cumOffsetBytes + regionOffsetBytes;
+ int typeIdOut = removeNnBuf(typeId) | BUFFER | REGION | (readOnly ? READONLY : 0);
+
+ if (Util.isNativeByteOrder(byteOrder)) {
+ typeIdOut |= NATIVE_BO;
+ return new BBWritableBufferImpl(
+ unsafeObj, nativeBaseOffset, newOffsetBytes, capacityBytes, typeIdOut, newCumOffsetBytes, memReqSvr, byteBuf);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new BBNonNativeWritableBufferImpl(
+ unsafeObj, nativeBaseOffset, newOffsetBytes, capacityBytes, typeIdOut, newCumOffsetBytes, memReqSvr, byteBuf);
+ }
+ }
+
+ @Override
+ BaseWritableMemoryImpl toWritableMemory(final boolean readOnly, final ByteOrder byteOrder) {
+ int typeIdOut = removeNnBuf(typeId) | MEMORY | (readOnly ? READONLY : 0);
+
+ if (byteOrder == ByteOrder.nativeOrder()) {
+ typeIdOut |= NATIVE_BO;
+ return new BBWritableMemoryImpl(
+ unsafeObj, nativeBaseOffset, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr, byteBuf);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new BBNonNativeWritableMemoryImpl(
+ unsafeObj, nativeBaseOffset, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr, byteBuf);
+ }
+ }
+
+ @Override
+ BaseWritableBufferImpl toDuplicate(final boolean readOnly, final ByteOrder byteOrder) {
+ int typeIdOut = removeNnBuf(typeId) | BUFFER | DUPLICATE | (readOnly ? READONLY : 0);
+
+ if (byteOrder == ByteOrder.nativeOrder()) {
+ typeIdOut |= NATIVE_BO;
+ return new BBWritableBufferImpl(
+ unsafeObj, nativeBaseOffset, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr, byteBuf);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new BBNonNativeWritableBufferImpl(
+ unsafeObj, nativeBaseOffset, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr, byteBuf);
+ }
+ }
+
+ @Override
+ public ByteBuffer getByteBuffer() {
+ return byteBuf;
+ }
+
+ @Override
+ Object getUnsafeObject() {
+ return unsafeObj;
+ }
+
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/BBWritableMemoryImpl.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/BBWritableMemoryImpl.java
new file mode 100644
index 00000000..81580a3f
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/BBWritableMemoryImpl.java
@@ -0,0 +1,109 @@
+/*
+ * 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 org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableMemory;
+
+/**
+ * Implementation of {@link WritableMemory} for ByteBuffer, native byte order.
+ *
+ * @author Roman Leventov
+ * @author Lee Rhodes
+ */
+final class BBWritableMemoryImpl extends NativeWritableMemoryImpl {
+ private final ByteBuffer byteBuf; //holds a reference to a ByteBuffer until we are done with it.
+ private final Object unsafeObj;
+ private final long nativeBaseOffset; //raw off-heap address of allocation base if ByteBuffer direct, else 0
+
+ BBWritableMemoryImpl(
+ final Object unsafeObj,
+ final long nativeBaseOffset,
+ final long offsetBytes,
+ final long capacityBytes,
+ final int typeId,
+ final long cumOffsetBytes,
+ final MemoryRequestServer memReqSvr,
+ final ByteBuffer byteBuf) {
+ super();
+ this.unsafeObj = unsafeObj;
+ this.nativeBaseOffset = nativeBaseOffset;
+ this.offsetBytes = offsetBytes;
+ this.capacityBytes = capacityBytes;
+ this.typeId = removeNnBuf(typeId) | BYTEBUF | MEMORY | NATIVE_BO;
+ this.cumOffsetBytes = cumOffsetBytes;
+ this.memReqSvr = memReqSvr; //in ResourceImpl
+ this.byteBuf = byteBuf;
+ if ((this.owner != null) && (this.owner != Thread.currentThread())) {
+ throw new IllegalStateException(THREAD_EXCEPTION_TEXT);
+ }
+ this.owner = Thread.currentThread();
+ }
+
+ @Override
+ BaseWritableMemoryImpl toWritableRegion(
+ final long regionOffsetBytes,
+ final long capacityBytes,
+ final boolean readOnly,
+ final ByteOrder byteOrder) {
+ final long newOffsetBytes = offsetBytes + regionOffsetBytes;
+ final long newCumOffsetBytes = cumOffsetBytes + regionOffsetBytes;
+ int typeIdOut = removeNnBuf(typeId) | MEMORY | REGION | (readOnly ? READONLY : 0);
+
+ if (Util.isNativeByteOrder(byteOrder)) {
+ typeIdOut |= NATIVE_BO;
+ return new BBWritableMemoryImpl(
+ unsafeObj, nativeBaseOffset, newOffsetBytes, capacityBytes, typeIdOut, newCumOffsetBytes, memReqSvr, byteBuf);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new BBNonNativeWritableMemoryImpl(
+ unsafeObj, nativeBaseOffset, newOffsetBytes, capacityBytes, typeIdOut, newCumOffsetBytes, memReqSvr, byteBuf);
+ }
+ }
+
+ @Override
+ BaseWritableBufferImpl toWritableBuffer(final boolean readOnly, final ByteOrder byteOrder) {
+ int typeIdOut = removeNnBuf(typeId) | BUFFER | (readOnly ? READONLY : 0);
+
+ if (byteOrder == ByteOrder.nativeOrder()) {
+ typeIdOut |= NATIVE_BO;
+ return new BBWritableBufferImpl(
+ unsafeObj, nativeBaseOffset, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr, byteBuf);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new BBNonNativeWritableBufferImpl(
+ unsafeObj, nativeBaseOffset, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr, byteBuf);
+ }
+ }
+
+ @Override
+ public ByteBuffer getByteBuffer() {
+ return byteBuf;
+ }
+
+ @Override
+ Object getUnsafeObject() {
+ return unsafeObj;
+ }
+
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/BaseWritableBufferImpl.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/BaseWritableBufferImpl.java
new file mode 100644
index 00000000..106f004b
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/BaseWritableBufferImpl.java
@@ -0,0 +1,391 @@
+/*
+ * 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.ARRAY_BOOLEAN_INDEX_SCALE;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_BYTE_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_BYTE_INDEX_SCALE;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_CHAR_INDEX_SCALE;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_INT_INDEX_SCALE;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_LONG_INDEX_SCALE;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_SHORT_INDEX_SCALE;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.unsafe;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Objects;
+
+import org.apache.datasketches.memory.Buffer;
+import org.apache.datasketches.memory.Memory;
+import org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.ReadOnlyException;
+import org.apache.datasketches.memory.WritableBuffer;
+import org.apache.datasketches.memory.WritableMemory;
+
+/**
+ * Common base of native-ordered and non-native-ordered {@link WritableBuffer} implementations.
+ * Contains methods which are agnostic to the byte order.
+ */
+//@SuppressWarnings("restriction")
+public abstract class BaseWritableBufferImpl extends PositionalImpl implements WritableBuffer {
+
+ //Pass-through constructor
+ BaseWritableBufferImpl(final long capacityBytes) { super(capacityBytes); }
+
+ /**
+ * The static constructor that chooses the correct ByteBuffer leaf node based on the byte order.
+ * @param byteBuffer the ByteBuffer being wrapped
+ * @param localReadOnly the requested read-only state
+ * @param byteOrder the requested byteOrder
+ * @param memReqSvr the requested MemoryRequestServer, which may be null.
+ * @return this class constructed via the leaf node.
+ */
+ public static WritableBuffer wrapByteBuffer(
+ final ByteBuffer byteBuffer, final boolean localReadOnly, final ByteOrder byteOrder,
+ final MemoryRequestServer memReqSvr) {
+ Objects.requireNonNull(byteBuffer, "byteBuffer must not be null");
+ Objects.requireNonNull(byteOrder, "byteOrder must not be null");
+ final AccessByteBuffer abb = new AccessByteBuffer(byteBuffer);
+ final int typeId = (abb.resourceReadOnly || localReadOnly) ? READONLY : 0;
+ final long cumOffsetBytes = abb.initialCumOffset;
+ final BaseWritableBufferImpl bwbi = Util.isNativeByteOrder(byteOrder)
+ ? new BBWritableBufferImpl(abb.unsafeObj, abb.nativeBaseOffset,
+ abb.offsetBytes, abb.capacityBytes, typeId, cumOffsetBytes, memReqSvr, byteBuffer)
+ : new BBNonNativeWritableBufferImpl(abb.unsafeObj, abb.nativeBaseOffset,
+ abb.offsetBytes, abb.capacityBytes, typeId, cumOffsetBytes, memReqSvr, byteBuffer);
+ bwbi.setStartPositionEnd(0, byteBuffer.position(), byteBuffer.limit());
+ return bwbi;
+ }
+
+ //REGIONS
+ @Override
+ public Buffer region() {
+ return writableRegionImpl(getPosition(), getEnd() - getPosition(), true, getTypeByteOrder());
+ }
+
+ @Override
+ public Buffer region(final long offsetBytes, final long capacityBytes, final ByteOrder byteOrder) {
+ final WritableBuffer buf = writableRegionImpl(offsetBytes, capacityBytes, true, byteOrder);
+ buf.setStartPositionEnd(0, 0, capacityBytes);
+ return buf;
+ }
+
+ @Override
+ public WritableBuffer writableRegion() {
+ return writableRegionImpl(getPosition(), getEnd() - getPosition(), false, getTypeByteOrder());
+ }
+
+ @Override
+ public WritableBuffer writableRegion(final long offsetBytes, final long capacityBytes, final ByteOrder byteOrder) {
+ final WritableBuffer wbuf = writableRegionImpl(offsetBytes, capacityBytes, false, byteOrder);
+ wbuf.setStartPositionEnd(0, 0, capacityBytes);
+ return wbuf;
+ }
+
+ WritableBuffer writableRegionImpl(final long offsetBytes, final long capacityBytes,
+ final boolean localReadOnly, final ByteOrder byteOrder) {
+ if (isReadOnly() && !localReadOnly) {
+ throw new ReadOnlyException("Writable region of a read-only Buffer is not allowed.");
+ }
+ checkValidAndBounds(offsetBytes, capacityBytes);
+ final boolean readOnly = isReadOnly() || localReadOnly;
+ final WritableBuffer wbuf = toWritableRegion(offsetBytes, capacityBytes, readOnly, byteOrder);
+ wbuf.setStartPositionEnd(0, 0, capacityBytes);
+ return wbuf;
+ }
+
+ abstract WritableBuffer toWritableRegion(
+ long offsetBytes, long capcityBytes, boolean readOnly, ByteOrder byteOrder);
+
+ //DUPLICATES
+ @Override
+ public Buffer duplicate() {
+ return writableDuplicateImpl(true, getTypeByteOrder());
+ }
+
+ @Override
+ public Buffer duplicate(final ByteOrder byteOrder) {
+ return writableDuplicateImpl(true, byteOrder);
+ }
+
+ @Override
+ public WritableBuffer writableDuplicate() {
+ return writableDuplicateImpl(false, getTypeByteOrder());
+ }
+
+ @Override
+ public WritableBuffer writableDuplicate(final ByteOrder byteOrder) {
+ return writableDuplicateImpl(false, byteOrder);
+ }
+
+ WritableBuffer writableDuplicateImpl(final boolean localReadOnly, final ByteOrder byteOrder) {
+ if (isReadOnly() && !localReadOnly) {
+ throw new ReadOnlyException("Writable duplicate of a read-only Buffer is not allowed.");
+ }
+ final boolean finalReadOnly = isReadOnly() || localReadOnly;
+ final WritableBuffer wbuf = toDuplicate(finalReadOnly, byteOrder);
+ wbuf.setStartPositionEnd(getStart(), getPosition(), getEnd());
+ return wbuf;
+ }
+
+ abstract BaseWritableBufferImpl toDuplicate(boolean readOnly, ByteOrder byteOrder);
+
+ //AS MEMORY
+ @Override
+ public Memory asMemory(final ByteOrder byteOrder) {
+ return asWritableMemory(true, byteOrder);
+ }
+
+ @Override
+ public WritableMemory asWritableMemory(final ByteOrder byteOrder) {
+ return asWritableMemory(false, byteOrder);
+ }
+
+ WritableMemory asWritableMemory(final boolean localReadOnly, final ByteOrder byteOrder) {
+ Objects.requireNonNull(byteOrder, "byteOrder must be non-null");
+ if (isReadOnly() && !localReadOnly) {
+ throw new ReadOnlyException(
+ "Converting a read-only Buffer to a writable Memory is not allowed.");
+ }
+ final boolean finalReadOnly = isReadOnly() || localReadOnly;
+ final WritableMemory wmem = toWritableMemory(finalReadOnly, byteOrder);
+ return wmem;
+ }
+
+ abstract WritableMemory toWritableMemory(boolean readOnly, ByteOrder byteOrder);
+
+ //PRIMITIVE getX() and getXArray()
+ @Override
+ public final boolean getBoolean() {
+ final long pos = getPosition();
+ incrementAndCheckPositionForRead(pos, ARRAY_BOOLEAN_INDEX_SCALE);
+ return unsafe.getBoolean(getUnsafeObject(), getCumulativeOffset(pos));
+ }
+
+ @Override
+ public final boolean getBoolean(final long offsetBytes) {
+ checkValidAndBounds(offsetBytes, ARRAY_BOOLEAN_INDEX_SCALE);
+ return unsafe.getBoolean(getUnsafeObject(), getCumulativeOffset(offsetBytes));
+ }
+
+ @Override
+ public final byte getByte() {
+ final long pos = getPosition();
+ incrementAndCheckPositionForRead(pos, ARRAY_BYTE_INDEX_SCALE);
+ return unsafe.getByte(getUnsafeObject(), getCumulativeOffset(pos));
+ }
+
+ @Override
+ public final byte getByte(final long offsetBytes) {
+ checkValidAndBounds(offsetBytes, ARRAY_BYTE_INDEX_SCALE);
+ return unsafe.getByte(getUnsafeObject(), getCumulativeOffset(offsetBytes));
+ }
+
+ @Override
+ public final void getByteArray(final byte[] dstArray, final int dstOffsetBytes,
+ final int lengthBytes) {
+ final long pos = getPosition();
+ final long copyBytes = lengthBytes;
+ incrementAndCheckPositionForRead(pos, copyBytes);
+ ResourceImpl.checkBounds(dstOffsetBytes, lengthBytes, dstArray.length);
+ CompareAndCopy.copyMemoryCheckingDifferentObject(
+ getUnsafeObject(),
+ getCumulativeOffset(pos),
+ dstArray,
+ ARRAY_BYTE_BASE_OFFSET + dstOffsetBytes,
+ copyBytes);
+ }
+
+ //PRIMITIVE getX() Native Endian (used by both endians)
+ final char getNativeOrderedChar() {
+ final long pos = getPosition();
+ incrementAndCheckPositionForRead(pos, ARRAY_CHAR_INDEX_SCALE);
+ return unsafe.getChar(getUnsafeObject(), getCumulativeOffset(pos));
+ }
+
+ final char getNativeOrderedChar(final long offsetBytes) {
+ checkValidAndBounds(offsetBytes, ARRAY_CHAR_INDEX_SCALE);
+ return unsafe.getChar(getUnsafeObject(), getCumulativeOffset(offsetBytes));
+ }
+
+ final int getNativeOrderedInt() {
+ final long pos = getPosition();
+ incrementAndCheckPositionForRead(pos, ARRAY_INT_INDEX_SCALE);
+ return unsafe.getInt(getUnsafeObject(), getCumulativeOffset(pos));
+ }
+
+ final int getNativeOrderedInt(final long offsetBytes) {
+ checkValidAndBounds(offsetBytes, ARRAY_INT_INDEX_SCALE);
+ return unsafe.getInt(getUnsafeObject(), getCumulativeOffset(offsetBytes));
+ }
+
+ final long getNativeOrderedLong() {
+ final long pos = getPosition();
+ incrementAndCheckPositionForRead(pos, ARRAY_LONG_INDEX_SCALE);
+ return unsafe.getLong(getUnsafeObject(), getCumulativeOffset(pos));
+ }
+
+ final long getNativeOrderedLong(final long offsetBytes) {
+ checkValidAndBounds(offsetBytes, ARRAY_LONG_INDEX_SCALE);
+ return unsafe.getLong(getUnsafeObject(), getCumulativeOffset(offsetBytes));
+ }
+
+ final short getNativeOrderedShort() {
+ final long pos = getPosition();
+ incrementAndCheckPositionForRead(pos, ARRAY_SHORT_INDEX_SCALE);
+ return unsafe.getShort(getUnsafeObject(), getCumulativeOffset(pos));
+ }
+
+ final short getNativeOrderedShort(final long offsetBytes) {
+ checkValidAndBounds(offsetBytes, ARRAY_SHORT_INDEX_SCALE);
+ return unsafe.getShort(getUnsafeObject(), getCumulativeOffset(offsetBytes));
+ }
+
+ //OTHER PRIMITIVE READ METHODS: copyTo, compareTo
+ @Override
+ public final int compareTo(final long thisOffsetBytes, final long thisLengthBytes,
+ final Buffer thatBuf, final long thatOffsetBytes, final long thatLengthBytes) {
+ return CompareAndCopy.compare((ResourceImpl)this, thisOffsetBytes, thisLengthBytes,
+ (ResourceImpl)thatBuf, thatOffsetBytes, thatLengthBytes);
+ }
+
+ /*
+ * Developer notes: There is no copyTo for Buffers because of the ambiguity of what to do with
+ * the positional values. Switch to MemoryImpl view to do copyTo.
+ */
+
+ //PRIMITIVE putX() and putXArray() implementations
+ @Override
+ public final void putBoolean(final boolean value) {
+ final long pos = getPosition();
+ incrementAndCheckPositionForWrite(pos, ARRAY_BOOLEAN_INDEX_SCALE);
+ unsafe.putBoolean(getUnsafeObject(), getCumulativeOffset(pos), value);
+ }
+
+ @Override
+ public final void putBoolean(final long offsetBytes, final boolean value) {
+ checkValidAndBoundsForWrite(offsetBytes, ARRAY_BOOLEAN_INDEX_SCALE);
+ unsafe.putBoolean(getUnsafeObject(), getCumulativeOffset(offsetBytes), value);
+ }
+
+ @Override
+ public final void putByte(final byte value) {
+ final long pos = getPosition();
+ incrementAndCheckPositionForWrite(pos, ARRAY_BYTE_INDEX_SCALE);
+ unsafe.putByte(getUnsafeObject(), getCumulativeOffset(pos), value);
+ }
+
+ @Override
+ public final void putByte(final long offsetBytes, final byte value) {
+ checkValidAndBoundsForWrite(offsetBytes, ARRAY_BYTE_INDEX_SCALE);
+ unsafe.putByte(getUnsafeObject(), getCumulativeOffset(offsetBytes), value);
+ }
+
+ @Override
+ public final void putByteArray(final byte[] srcArray, final int srcOffsetBytes,
+ final int lengthBytes) {
+ final long pos = getPosition();
+ final long copyBytes = lengthBytes;
+ incrementAndCheckPositionForWrite(pos, copyBytes);
+ ResourceImpl.checkBounds(srcOffsetBytes, lengthBytes, srcArray.length);
+ CompareAndCopy.copyMemoryCheckingDifferentObject(
+ srcArray,
+ ARRAY_BYTE_BASE_OFFSET + srcOffsetBytes,
+ getUnsafeObject(),
+ getCumulativeOffset(pos),
+ copyBytes);
+ }
+
+ //PRIMITIVE putX() Native Endian (used by both endians)
+ final void putNativeOrderedChar(final char value) {
+ final long pos = getPosition();
+ incrementAndCheckPositionForWrite(pos, ARRAY_CHAR_INDEX_SCALE);
+ unsafe.putChar(getUnsafeObject(), getCumulativeOffset(pos), value);
+ }
+
+ final void putNativeOrderedChar(final long offsetBytes, final char value) {
+ checkValidAndBoundsForWrite(offsetBytes, ARRAY_CHAR_INDEX_SCALE);
+ unsafe.putChar(getUnsafeObject(), getCumulativeOffset(offsetBytes), value);
+ }
+
+ final void putNativeOrderedInt(final int value) {
+ final long pos = getPosition();
+ incrementAndCheckPositionForWrite(pos, ARRAY_INT_INDEX_SCALE);
+ unsafe.putInt(getUnsafeObject(), getCumulativeOffset(pos), value);
+ }
+
+ final void putNativeOrderedInt(final long offsetBytes, final int value) {
+ checkValidAndBoundsForWrite(offsetBytes, ARRAY_INT_INDEX_SCALE);
+ unsafe.putInt(getUnsafeObject(), getCumulativeOffset(offsetBytes), value);
+ }
+
+ final void putNativeOrderedLong(final long value) {
+ final long pos = getPosition();
+ incrementAndCheckPositionForWrite(pos, ARRAY_LONG_INDEX_SCALE);
+ unsafe.putLong(getUnsafeObject(), getCumulativeOffset(pos), value);
+ }
+
+ final void putNativeOrderedLong(final long offsetBytes, final long value) {
+ checkValidAndBoundsForWrite(offsetBytes, ARRAY_LONG_INDEX_SCALE);
+ unsafe.putLong(getUnsafeObject(), getCumulativeOffset(offsetBytes), value);
+ }
+
+ final void putNativeOrderedShort(final short value) {
+ final long pos = getPosition();
+ incrementAndCheckPositionForWrite(pos, ARRAY_SHORT_INDEX_SCALE);
+ unsafe.putShort(getUnsafeObject(), getCumulativeOffset(pos), value);
+ }
+
+ final void putNativeOrderedShort(final long offsetBytes, final short value) {
+ checkValidAndBoundsForWrite(offsetBytes, ARRAY_SHORT_INDEX_SCALE);
+ unsafe.putShort(getUnsafeObject(), getCumulativeOffset(offsetBytes), value);
+ }
+
+ //OTHER WRITE METHODS
+
+ /**
+ * Returns the primitive backing array, otherwise null.
+ * @return the primitive backing array, otherwise null.
+ */
+ @Override
+ public final Object getArray() {
+ checkValid();
+ return getUnsafeObject();
+ }
+
+ @Override
+ public final void clear() {
+ fill((byte)0);
+ }
+
+ @Override
+ public final void fill(final byte value) {
+ checkNotReadOnly();
+ long pos = getPosition();
+ long len = getEnd() - pos;
+ checkInvariants(getStart(), pos + len, getEnd(), getCapacity());
+ while (len > 0) {
+ final long chunk = Math.min(len, Util.UNSAFE_COPY_THRESHOLD_BYTES);
+ unsafe.setMemory(getUnsafeObject(), getCumulativeOffset(pos), chunk, value);
+ pos += chunk;
+ len -= chunk;
+ }
+ }
+}
diff --git a/datasketches-memory-java11/src/main/java/org/apache/datasketches/memory/internal/BaseWritableMemoryImpl.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/BaseWritableMemoryImpl.java
similarity index 91%
copy from datasketches-memory-java11/src/main/java/org/apache/datasketches/memory/internal/BaseWritableMemoryImpl.java
copy to datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/BaseWritableMemoryImpl.java
index 2bd6971d..5f174fca 100644
--- a/datasketches-memory-java11/src/main/java/org/apache/datasketches/memory/internal/BaseWritableMemoryImpl.java
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/BaseWritableMemoryImpl.java
@@ -29,7 +29,6 @@ import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_SHORT_IND
import static org.apache.datasketches.memory.internal.UnsafeUtil.unsafe;
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;
@@ -111,41 +110,6 @@ public abstract class BaseWritableMemoryImpl extends ResourceImpl implements Wri
abb.offsetBytes, abb.capacityBytes, typeId, cumOffsetBytes, memReqSvr, byteBuffer);
}
- /**
- * The static constructor that chooses the correct Map leaf node based on the byte order.
- * @param file the file being wrapped. It must be non-null.
- * @param fileOffsetBytes the file offset bytes. It must be ≥ 0.
- * @param capacityBytes the requested capacity of the memory mapped region. It must be ≥ 0.
- * @param localReadOnly the requested read-only state
- * @param byteOrder the requested byte-order. It must be non-null.
- * @return this class constructed via the leaf node.
- */
- public static WritableMemory wrapMap(final File file, final long fileOffsetBytes,
- final long capacityBytes, final boolean localReadOnly, final ByteOrder byteOrder) {
- Objects.requireNonNull(file, "File must be non-null.");
- Util.negativeCheck(fileOffsetBytes, "fileOffsetBytes");
- Util.negativeCheck(capacityBytes, "capacityBytes");
- Objects.requireNonNull(byteOrder, "ByteOrder must be non-null.");
- final AllocateDirectWritableMap dirWMap =
- new AllocateDirectWritableMap(file, fileOffsetBytes, capacityBytes, localReadOnly);
- final int typeId = (dirWMap.resourceReadOnly || localReadOnly) ? READONLY : 0;
- final long cumOffsetBytes = dirWMap.nativeBaseOffset;
- final BaseWritableMemoryImpl wmem = Util.isNativeByteOrder(byteOrder)
- ? new MapWritableMemoryImpl(
- dirWMap,
- 0L,
- capacityBytes,
- typeId,
- cumOffsetBytes)
- : new MapNonNativeWritableMemoryImpl(
- dirWMap,
- 0L,
- capacityBytes,
- typeId,
- cumOffsetBytes);
- return wmem;
- }
-
/**
* The static constructor that chooses the correct Direct leaf node based on the byte order.
* @param capacityBytes the requested capacity for the Direct (off-heap) memory. It must be ≥ 0.
@@ -299,8 +263,8 @@ public abstract class BaseWritableMemoryImpl extends ResourceImpl implements Wri
}
@Override
- public final void writeTo(final long offsetBytes, final long lengthBytes,
- final WritableByteChannel out) throws IOException {
+ public final void writeTo(final long offsetBytes, final long lengthBytes, final WritableByteChannel out)
+ throws IOException {
checkValidAndBounds(offsetBytes, lengthBytes);
if (getUnsafeObject() instanceof byte[]) {
writeByteArrayTo((byte[]) getUnsafeObject(), offsetBytes, lengthBytes, out);
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/CompareAndCopy.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/CompareAndCopy.java
new file mode 100644
index 00000000..a3ef00f5
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/CompareAndCopy.java
@@ -0,0 +1,521 @@
+/*
+ * 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.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 ResourceImpl state1, final long offsetBytes1, final long lengthBytes1,
+ final ResourceImpl state2, final long offsetBytes2, final long lengthBytes2) {
+ state1.checkValid();
+ ResourceImpl.checkBounds(offsetBytes1, lengthBytes1, state1.getCapacity());
+ state2.checkValid();
+ ResourceImpl.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 ResourceImpl state1, final ResourceImpl state2) {
+ final long cap1 = state1.getCapacity();
+ final long cap2 = state2.getCapacity();
+ return (cap1 == cap2) && equals(state1, 0, state2, 0, cap1);
+ }
+
+ //Developer notes: this is subtlety different from (compare == 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 ResourceImpl state1, final long offsetBytes1,
+ final ResourceImpl state2, final long offsetBytes2, long lengthBytes) {
+ state1.checkValid();
+ ResourceImpl.checkBounds(offsetBytes1, lengthBytes, state1.getCapacity());
+ state2.checkValid();
+ ResourceImpl.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 ResourceImpl srcState, final long srcOffsetBytes,
+ final ResourceImpl dstState, final long dstOffsetBytes, final long lengthBytes) {
+ srcState.checkValid();
+ ResourceImpl.checkBounds(srcOffsetBytes, lengthBytes, srcState.getCapacity());
+ dstState.checkValid();
+ ResourceImpl.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) {
+ ResourceImpl.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) {
+ ResourceImpl.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) {
+ ResourceImpl.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) {
+ ResourceImpl.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) {
+ ResourceImpl.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) {
+ ResourceImpl.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) {
+ ResourceImpl.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) {
+ ResourceImpl.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) {
+ ResourceImpl.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) {
+ ResourceImpl.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) {
+ ResourceImpl.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) {
+ ResourceImpl.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-java21/src/main/java/org/apache/datasketches/memory/internal/DirectNonNativeWritableBufferImpl.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/DirectNonNativeWritableBufferImpl.java
new file mode 100644
index 00000000..09b6bfae
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/DirectNonNativeWritableBufferImpl.java
@@ -0,0 +1,124 @@
+/*
+ * 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.ByteOrder;
+
+import org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableBuffer;
+
+/**
+ * Implementation of {@link WritableBuffer} for direct memory, non-native byte order.
+ *
+ * @author Roman Leventov
+ * @author Lee Rhodes
+ */
+final class DirectNonNativeWritableBufferImpl extends NonNativeWritableBufferImpl {
+ private final AllocateDirect direct;
+
+ DirectNonNativeWritableBufferImpl(
+ final AllocateDirect direct,
+ final long offsetBytes,
+ final long capacityBytes,
+ final int typeId,
+ final long cumOffsetBytes,
+ final MemoryRequestServer memReqSvr) {
+ super(capacityBytes);
+ this.direct = direct;
+ this.offsetBytes = offsetBytes;
+ this.capacityBytes = capacityBytes;
+ this.typeId = removeNnBuf(typeId) | DIRECT | BUFFER | NONNATIVE_BO; //initially cannot be ReadOnly
+ this.cumOffsetBytes = cumOffsetBytes;
+ this.memReqSvr = memReqSvr; //in ResourceImpl
+ if ((this.owner != null) && (this.owner != Thread.currentThread())) {
+ throw new IllegalStateException(THREAD_EXCEPTION_TEXT);
+ }
+ this.owner = Thread.currentThread();
+ }
+
+ @Override
+ BaseWritableBufferImpl toWritableRegion(
+ final long regionOffsetBytes,
+ final long capacityBytes,
+ final boolean readOnly,
+ final ByteOrder byteOrder) {
+ final long newOffsetBytes = offsetBytes + regionOffsetBytes;
+ final long newCumOffsetBytes = cumOffsetBytes + regionOffsetBytes;
+ int typeIdOut = removeNnBuf(typeId) | BUFFER | REGION | (readOnly ? READONLY : 0);
+
+ if (Util.isNativeByteOrder(byteOrder)) {
+ typeIdOut |= NATIVE_BO;
+ return new DirectWritableBufferImpl(
+ direct, newOffsetBytes, capacityBytes, typeIdOut, newCumOffsetBytes, memReqSvr);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new DirectNonNativeWritableBufferImpl(
+ direct, newOffsetBytes, capacityBytes, typeIdOut, newCumOffsetBytes, memReqSvr);
+ }
+ }
+
+ @Override
+ BaseWritableMemoryImpl toWritableMemory(final boolean readOnly, final ByteOrder byteOrder) {
+ int typeIdOut = removeNnBuf(typeId) | MEMORY | (readOnly ? READONLY : 0);
+
+ if (byteOrder == ByteOrder.nativeOrder()) {
+ typeIdOut |= NATIVE_BO;
+ return new DirectWritableMemoryImpl(
+ direct, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new DirectNonNativeWritableMemoryImpl(
+ direct, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr);
+ }
+ }
+
+ @Override
+ BaseWritableBufferImpl toDuplicate(final boolean readOnly, final ByteOrder byteOrder) {
+ int typeIdOut = removeNnBuf(typeId) | BUFFER | DUPLICATE | (readOnly ? READONLY : 0);
+
+ if (byteOrder == ByteOrder.nativeOrder()) {
+ typeIdOut |= NATIVE_BO;
+ return new DirectWritableBufferImpl(
+ direct, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new DirectNonNativeWritableBufferImpl(
+ direct, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr);
+ }
+ }
+
+ @Override
+ public void close() {
+ checkValid();
+ checkThread(owner);
+ direct.close();
+ }
+
+ @Override
+ Object getUnsafeObject() {
+ return null;
+ }
+
+ @Override
+ public boolean isAlive() {
+ return direct.getValid().get();
+ }
+
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/DirectNonNativeWritableMemoryImpl.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/DirectNonNativeWritableMemoryImpl.java
new file mode 100644
index 00000000..d98f9948
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/DirectNonNativeWritableMemoryImpl.java
@@ -0,0 +1,109 @@
+/*
+ * 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.ByteOrder;
+
+import org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableMemory;
+
+/**
+ * Implementation of {@link WritableMemory} for direct memory, non-native byte order.
+ *
+ * @author Roman Leventov
+ * @author Lee Rhodes
+ */
+final class DirectNonNativeWritableMemoryImpl extends NonNativeWritableMemoryImpl {
+ private final AllocateDirect direct;
+
+ DirectNonNativeWritableMemoryImpl(
+ final AllocateDirect direct,
+ final long offsetBytes,
+ final long capacityBytes,
+ final int typeId,
+ final long cumOffsetBytes,
+ final MemoryRequestServer memReqSvr) {
+ super();
+ this.direct = direct;
+ this.offsetBytes = offsetBytes;
+ this.capacityBytes = capacityBytes;
+ this.typeId = removeNnBuf(typeId) | DIRECT | MEMORY | NONNATIVE_BO; //initially cannot be ReadOnly
+ this.cumOffsetBytes = cumOffsetBytes;
+ this.memReqSvr = memReqSvr; //in ResourceImpl
+ if ((this.owner != null) && (this.owner != Thread.currentThread())) {
+ throw new IllegalStateException(THREAD_EXCEPTION_TEXT);
+ }
+ this.owner = Thread.currentThread();
+ }
+
+ @Override
+ BaseWritableMemoryImpl toWritableRegion(
+ final long regionOffsetBytes,
+ final long capacityBytes,
+ final boolean readOnly,
+ final ByteOrder byteOrder) {
+ final long newOffsetBytes = offsetBytes + regionOffsetBytes;
+ final long newCumOffsetBytes = cumOffsetBytes + regionOffsetBytes;
+ int typeIdOut = removeNnBuf(typeId) | MEMORY | REGION | (readOnly ? READONLY : 0);
+
+ if (Util.isNativeByteOrder(byteOrder)) {
+ typeIdOut |= NATIVE_BO;
+ return new DirectWritableMemoryImpl(
+ direct, newOffsetBytes, capacityBytes, typeIdOut, newCumOffsetBytes, memReqSvr);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new DirectNonNativeWritableMemoryImpl(
+ direct, newOffsetBytes, capacityBytes, typeIdOut, newCumOffsetBytes, memReqSvr);
+ }
+ }
+
+ @Override
+ BaseWritableBufferImpl toWritableBuffer(final boolean readOnly, final ByteOrder byteOrder) {
+ int typeIdOut = removeNnBuf(typeId) | BUFFER | (readOnly ? READONLY : 0);
+
+ if (byteOrder == ByteOrder.nativeOrder()) {
+ typeIdOut |= NATIVE_BO;
+ return new DirectWritableBufferImpl(
+ direct, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new DirectNonNativeWritableBufferImpl(
+ direct, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr);
+ }
+ }
+
+ @Override
+ public void close() {
+ checkValid();
+ checkThread(owner);
+ direct.close();
+ }
+
+ @Override
+ Object getUnsafeObject() {
+ return null;
+ }
+
+ @Override
+ public boolean isAlive() {
+ return direct.getValid().get();
+ }
+
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/DirectWritableBufferImpl.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/DirectWritableBufferImpl.java
new file mode 100644
index 00000000..8d99b163
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/DirectWritableBufferImpl.java
@@ -0,0 +1,124 @@
+/*
+ * 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.ByteOrder;
+
+import org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableBuffer;
+
+/**
+ * Implementation of {@link WritableBuffer} for direct memory, native byte order.
+ *
+ * @author Roman Leventov
+ * @author Lee Rhodes
+ */
+final class DirectWritableBufferImpl extends NativeWritableBufferImpl {
+ private final AllocateDirect direct;
+
+ DirectWritableBufferImpl(
+ final AllocateDirect direct,
+ final long offsetBytes,
+ final long capacityBytes,
+ final int typeId,
+ final long cumOffsetBytes,
+ final MemoryRequestServer memReqSvr) {
+ super(capacityBytes);
+ this.direct = direct;
+ this.offsetBytes = offsetBytes;
+ this.capacityBytes = capacityBytes;
+ this.typeId = removeNnBuf(typeId) | DIRECT | BUFFER | NATIVE_BO; //initially cannot be ReadOnly
+ this.cumOffsetBytes = cumOffsetBytes;
+ this.memReqSvr = memReqSvr; //in ResourceImpl
+ if ((this.owner != null) && (this.owner != Thread.currentThread())) {
+ throw new IllegalStateException(THREAD_EXCEPTION_TEXT);
+ }
+ this.owner = Thread.currentThread();
+ }
+
+ @Override
+ BaseWritableBufferImpl toWritableRegion(
+ final long regionOffsetBytes,
+ final long capacityBytes,
+ final boolean readOnly,
+ final ByteOrder byteOrder) {
+ final long newOffsetBytes = offsetBytes + regionOffsetBytes;
+ final long newCumOffsetBytes = cumOffsetBytes + regionOffsetBytes;
+ int typeIdOut = removeNnBuf(typeId) | BUFFER | REGION | (readOnly ? READONLY : 0);
+
+ if (Util.isNativeByteOrder(byteOrder)) {
+ typeIdOut |= NATIVE_BO;
+ return new DirectWritableBufferImpl(
+ direct, newOffsetBytes, capacityBytes, typeIdOut, newCumOffsetBytes, memReqSvr);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new DirectNonNativeWritableBufferImpl(
+ direct, newOffsetBytes, capacityBytes, typeIdOut, newCumOffsetBytes, memReqSvr);
+ }
+ }
+
+ @Override
+ BaseWritableMemoryImpl toWritableMemory(final boolean readOnly, final ByteOrder byteOrder) {
+ int typeIdOut = removeNnBuf(typeId) | MEMORY | (readOnly ? READONLY : 0);
+
+ if (byteOrder == ByteOrder.nativeOrder()) {
+ typeIdOut |= NATIVE_BO;
+ return new DirectWritableMemoryImpl(
+ direct, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new DirectNonNativeWritableMemoryImpl(
+ direct, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr);
+ }
+ }
+
+ @Override
+ BaseWritableBufferImpl toDuplicate(final boolean readOnly, final ByteOrder byteOrder) {
+ int typeIdOut = removeNnBuf(typeId) | BUFFER | DUPLICATE | (readOnly ? READONLY : 0);
+
+ if (byteOrder == ByteOrder.nativeOrder()) {
+ typeIdOut |= NATIVE_BO;
+ return new DirectWritableBufferImpl(
+ direct, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new DirectNonNativeWritableBufferImpl(
+ direct, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr);
+ }
+ }
+
+ @Override
+ public void close() {
+ checkValid();
+ checkThread(owner);
+ direct.close();
+ }
+
+ @Override
+ Object getUnsafeObject() {
+ return null;
+ }
+
+ @Override
+ public boolean isAlive() {
+ return direct.getValid().get();
+ }
+
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/DirectWritableMemoryImpl.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/DirectWritableMemoryImpl.java
new file mode 100644
index 00000000..88706612
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/DirectWritableMemoryImpl.java
@@ -0,0 +1,109 @@
+/*
+ * 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.ByteOrder;
+
+import org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableMemory;
+
+/**
+ * Implementation of {@link WritableMemory} for direct memory, native byte order.
+ *
+ * @author Roman Leventov
+ * @author Lee Rhodes
+ */
+final class DirectWritableMemoryImpl extends NativeWritableMemoryImpl {
+ private final AllocateDirect direct;
+
+ DirectWritableMemoryImpl(
+ final AllocateDirect direct,
+ final long offsetBytes,
+ final long capacityBytes,
+ final int typeId,
+ final long cumOffsetBytes,
+ final MemoryRequestServer memReqSvr) {
+ super();
+ this.direct = direct;
+ this.offsetBytes = offsetBytes;
+ this.capacityBytes = capacityBytes;
+ this.typeId = removeNnBuf(typeId) | DIRECT | MEMORY | NATIVE_BO; //initially cannot be ReadOnly
+ this.cumOffsetBytes = cumOffsetBytes;
+ this.memReqSvr = memReqSvr; //in ResourceImpl
+ if ((this.owner != null) && (this.owner != Thread.currentThread())) {
+ throw new IllegalStateException(THREAD_EXCEPTION_TEXT);
+ }
+ this.owner = Thread.currentThread();
+ }
+
+ @Override
+ BaseWritableMemoryImpl toWritableRegion(
+ final long regionOffsetBytes,
+ final long capacityBytes,
+ final boolean readOnly,
+ final ByteOrder byteOrder) {
+ final long newOffsetBytes = offsetBytes + regionOffsetBytes;
+ final long newCumOffsetBytes = cumOffsetBytes + regionOffsetBytes;
+ int typeIdOut = removeNnBuf(typeId) | MEMORY | REGION | (readOnly ? READONLY : 0);
+
+ if (Util.isNativeByteOrder(byteOrder)) {
+ typeIdOut |= NATIVE_BO;
+ return new DirectWritableMemoryImpl(
+ direct, newOffsetBytes, capacityBytes, typeIdOut, newCumOffsetBytes, memReqSvr);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new DirectNonNativeWritableMemoryImpl(
+ direct, newOffsetBytes, capacityBytes, typeIdOut, newCumOffsetBytes, memReqSvr);
+ }
+ }
+
+ @Override
+ BaseWritableBufferImpl toWritableBuffer(final boolean readOnly, final ByteOrder byteOrder) {
+ int typeIdOut = removeNnBuf(typeId) | BUFFER | (readOnly ? READONLY : 0);
+
+ if (byteOrder == ByteOrder.nativeOrder()) {
+ typeIdOut |= NATIVE_BO;
+ return new DirectWritableBufferImpl(
+ direct, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new DirectNonNativeWritableBufferImpl(
+ direct, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr);
+ }
+ }
+
+ @Override
+ public void close() {
+ checkValid();
+ checkThread(owner);
+ direct.close();
+ }
+
+ @Override
+ Object getUnsafeObject() {
+ return null;
+ }
+
+ @Override
+ public boolean isAlive() {
+ return direct.getValid().get();
+ }
+
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/HeapNonNativeWritableBufferImpl.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/HeapNonNativeWritableBufferImpl.java
new file mode 100644
index 00000000..95ca2a17
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/HeapNonNativeWritableBufferImpl.java
@@ -0,0 +1,112 @@
+/*
+ * 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.ByteOrder;
+
+import org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableBuffer;
+
+/**
+ * Implementation of {@link WritableBuffer} for heap-based, non-native byte order.
+ *
+ * @author Roman Leventov
+ * @author Lee Rhodes
+ */
+final class HeapNonNativeWritableBufferImpl extends NonNativeWritableBufferImpl {
+ private final Object unsafeObj;
+
+ HeapNonNativeWritableBufferImpl(
+ final Object unsafeObj,
+ final long offsetBytes,
+ final long capacityBytes,
+ final int typeId,
+ final long cumOffsetBytes,
+ final MemoryRequestServer memReqSvr) {
+ super(capacityBytes);
+ this.unsafeObj = unsafeObj;
+ this.offsetBytes = offsetBytes;
+ this.capacityBytes = capacityBytes;
+ this.typeId = removeNnBuf(typeId) | HEAP | BUFFER | NONNATIVE_BO;
+ this.cumOffsetBytes = cumOffsetBytes;
+ this.memReqSvr = memReqSvr; //in ResourceImpl
+ if ((this.owner != null) && (this.owner != Thread.currentThread())) {
+ throw new IllegalStateException(THREAD_EXCEPTION_TEXT);
+ }
+ this.owner = Thread.currentThread();
+ }
+
+ @Override
+ BaseWritableBufferImpl toWritableRegion(
+ final long regionOffsetBytes,
+ final long capacityBytes,
+ final boolean readOnly,
+ final ByteOrder byteOrder) {
+ final long newOffsetBytes = offsetBytes + regionOffsetBytes;
+ final long newCumOffsetBytes = cumOffsetBytes + regionOffsetBytes;
+ int typeIdOut = removeNnBuf(typeId) | BUFFER | REGION | (readOnly ? READONLY : 0);
+
+ if (Util.isNativeByteOrder(byteOrder)) {
+ typeIdOut |= NATIVE_BO;
+ return new HeapWritableBufferImpl(
+ unsafeObj, newOffsetBytes, capacityBytes, typeIdOut, newCumOffsetBytes, memReqSvr);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new HeapNonNativeWritableBufferImpl(
+ unsafeObj, newOffsetBytes, capacityBytes, typeIdOut, newCumOffsetBytes, memReqSvr);
+ }
+ }
+
+ @Override
+ BaseWritableMemoryImpl toWritableMemory(final boolean readOnly, final ByteOrder byteOrder) {
+ int typeIdOut = removeNnBuf(typeId) | MEMORY | (readOnly ? READONLY : 0);
+
+ if (byteOrder == ByteOrder.nativeOrder()) {
+ typeIdOut |= NATIVE_BO;
+ return new HeapWritableMemoryImpl(
+ unsafeObj, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new HeapNonNativeWritableMemoryImpl(
+ unsafeObj, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr);
+ }
+ }
+
+ @Override
+ BaseWritableBufferImpl toDuplicate(final boolean readOnly, final ByteOrder byteOrder) {
+ int typeIdOut = removeNnBuf(typeId) | BUFFER | DUPLICATE | (readOnly ? READONLY : 0);
+
+ if (byteOrder == ByteOrder.nativeOrder()) {
+ typeIdOut |= NATIVE_BO;
+ return new HeapWritableBufferImpl(
+ unsafeObj, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new HeapNonNativeWritableBufferImpl(
+ unsafeObj, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr);
+ }
+ }
+
+ @Override
+ Object getUnsafeObject() {
+ return unsafeObj;
+ }
+
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/HeapNonNativeWritableMemoryImpl.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/HeapNonNativeWritableMemoryImpl.java
new file mode 100644
index 00000000..d26d32e9
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/HeapNonNativeWritableMemoryImpl.java
@@ -0,0 +1,97 @@
+/*
+ * 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.ByteOrder;
+
+import org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableMemory;
+
+/**
+ * Implementation of {@link WritableMemory} for heap-based, non-native byte order.
+ *
+ * @author Roman Leventov
+ * @author Lee Rhodes
+ */
+final class HeapNonNativeWritableMemoryImpl extends NonNativeWritableMemoryImpl {
+ private final Object unsafeObj;
+
+ HeapNonNativeWritableMemoryImpl(
+ final Object unsafeObj,
+ final long offsetBytes,
+ final long capacityBytes,
+ final int typeId,
+ final long cumOffsetBytes,
+ final MemoryRequestServer memReqSvr) {
+ super();
+ this.unsafeObj = unsafeObj;
+ this.offsetBytes = offsetBytes;
+ this.capacityBytes = capacityBytes;
+ this.typeId = removeNnBuf(typeId) | HEAP | MEMORY | NONNATIVE_BO;
+ this.cumOffsetBytes = cumOffsetBytes;
+ this.memReqSvr = memReqSvr; //in ResourceImpl
+ if ((this.owner != null) && (this.owner != Thread.currentThread())) {
+ throw new IllegalStateException(THREAD_EXCEPTION_TEXT);
+ }
+ this.owner = Thread.currentThread();
+ }
+
+ @Override
+ BaseWritableMemoryImpl toWritableRegion(
+ final long regionOffsetBytes,
+ final long capacityBytes,
+ final boolean readOnly,
+ final ByteOrder byteOrder) {
+ final long newOffsetBytes = offsetBytes + regionOffsetBytes;
+ final long newCumOffsetBytes = cumOffsetBytes + regionOffsetBytes;
+ int typeIdOut = removeNnBuf(typeId) | MEMORY | REGION | (readOnly ? READONLY : 0);
+
+ if (Util.isNativeByteOrder(byteOrder)) {
+ typeIdOut |= NATIVE_BO;
+ return new HeapWritableMemoryImpl(
+ unsafeObj, newOffsetBytes, capacityBytes, typeIdOut, newCumOffsetBytes, memReqSvr);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new HeapNonNativeWritableMemoryImpl(
+ unsafeObj, newOffsetBytes, capacityBytes, typeIdOut, newCumOffsetBytes, memReqSvr);
+ }
+ }
+
+ @Override
+ BaseWritableBufferImpl toWritableBuffer(final boolean readOnly, final ByteOrder byteOrder) {
+ int typeIdOut = removeNnBuf(typeId) | BUFFER | (readOnly ? READONLY : 0);
+
+ if (byteOrder == ByteOrder.nativeOrder()) {
+ typeIdOut |= NATIVE_BO;
+ return new HeapWritableBufferImpl(
+ unsafeObj, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new HeapNonNativeWritableBufferImpl(
+ unsafeObj, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr);
+ }
+ }
+
+ @Override
+ Object getUnsafeObject() {
+ return unsafeObj;
+ }
+
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/HeapWritableBufferImpl.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/HeapWritableBufferImpl.java
new file mode 100644
index 00000000..ef0d7644
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/HeapWritableBufferImpl.java
@@ -0,0 +1,112 @@
+/*
+ * 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.ByteOrder;
+
+import org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableBuffer;
+
+/**
+ * Implementation of {@link WritableBuffer} for heap-based, native byte order.
+ *
+ * @author Roman Leventov
+ * @author Lee Rhodes
+ */
+final class HeapWritableBufferImpl extends NativeWritableBufferImpl {
+ private final Object unsafeObj;
+
+ HeapWritableBufferImpl(
+ final Object unsafeObj,
+ final long offsetBytes,
+ final long capacityBytes,
+ final int typeId,
+ final long cumOffsetBytes,
+ final MemoryRequestServer memReqSvr) {
+ super(capacityBytes);
+ this.unsafeObj = unsafeObj;
+ this.offsetBytes = offsetBytes;
+ this.capacityBytes = capacityBytes;
+ this.typeId = removeNnBuf(typeId) | HEAP | BUFFER | NATIVE_BO;
+ this.cumOffsetBytes = cumOffsetBytes;
+ this.memReqSvr = memReqSvr; //in ResourceImpl
+ if ((this.owner != null) && (this.owner != Thread.currentThread())) {
+ throw new IllegalStateException(THREAD_EXCEPTION_TEXT);
+ }
+ this.owner = Thread.currentThread();
+ }
+
+ @Override
+ BaseWritableBufferImpl toWritableRegion(
+ final long regionOffsetBytes,
+ final long capacityBytes,
+ final boolean readOnly,
+ final ByteOrder byteOrder) {
+ final long newOffsetBytes = offsetBytes + regionOffsetBytes;
+ final long newCumOffsetBytes = cumOffsetBytes + regionOffsetBytes;
+ int typeIdOut = removeNnBuf(typeId) | BUFFER | REGION | (readOnly ? READONLY : 0);
+
+ if (Util.isNativeByteOrder(byteOrder)) {
+ typeIdOut |= NATIVE_BO;
+ return new HeapWritableBufferImpl(
+ unsafeObj, newOffsetBytes, capacityBytes, typeIdOut, newCumOffsetBytes, memReqSvr);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new HeapNonNativeWritableBufferImpl(
+ unsafeObj, newOffsetBytes, capacityBytes, typeIdOut, newCumOffsetBytes, memReqSvr);
+ }
+ }
+
+ @Override
+ BaseWritableMemoryImpl toWritableMemory(final boolean readOnly, final ByteOrder byteOrder) {
+ int typeIdOut = removeNnBuf(typeId) | MEMORY | (readOnly ? READONLY : 0);
+
+ if (byteOrder == ByteOrder.nativeOrder()) {
+ typeIdOut |= NATIVE_BO;
+ return new HeapWritableMemoryImpl(
+ unsafeObj, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new HeapNonNativeWritableMemoryImpl(
+ unsafeObj, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr);
+ }
+ }
+
+ @Override
+ BaseWritableBufferImpl toDuplicate(final boolean readOnly, final ByteOrder byteOrder) {
+ int typeIdOut = removeNnBuf(typeId) | BUFFER | DUPLICATE | (readOnly ? READONLY : 0);
+
+ if (byteOrder == ByteOrder.nativeOrder()) {
+ typeIdOut |= NATIVE_BO;
+ return new HeapWritableBufferImpl(
+ unsafeObj, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new HeapNonNativeWritableBufferImpl(
+ unsafeObj, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr);
+ }
+ }
+
+ @Override
+ Object getUnsafeObject() {
+ return unsafeObj;
+ }
+
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/HeapWritableMemoryImpl.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/HeapWritableMemoryImpl.java
new file mode 100644
index 00000000..85ba4607
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/HeapWritableMemoryImpl.java
@@ -0,0 +1,97 @@
+/*
+ * 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.ByteOrder;
+
+import org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableMemory;
+
+/**
+ * Implementation of {@link WritableMemory} for heap-based, native byte order.
+ *
+ * @author Roman Leventov
+ * @author Lee Rhodes
+ */
+final class HeapWritableMemoryImpl extends NativeWritableMemoryImpl {
+ private final Object unsafeObj;
+
+ HeapWritableMemoryImpl(
+ final Object unsafeObj,
+ final long offsetBytes,
+ final long capacityBytes,
+ final int typeId,
+ final long cumOffsetBytes,
+ final MemoryRequestServer memReqSvr) {
+ super();
+ this.unsafeObj = unsafeObj;
+ this.offsetBytes = offsetBytes;
+ this.capacityBytes = capacityBytes;
+ this.typeId = removeNnBuf(typeId) | HEAP | MEMORY | NATIVE_BO;
+ this.cumOffsetBytes = cumOffsetBytes;
+ this.memReqSvr = memReqSvr; //in ResourceImpl
+ if ((this.owner != null) && (this.owner != Thread.currentThread())) {
+ throw new IllegalStateException(THREAD_EXCEPTION_TEXT);
+ }
+ this.owner = Thread.currentThread();
+ }
+
+ @Override
+ BaseWritableMemoryImpl toWritableRegion(
+ final long regionOffsetBytes,
+ final long capacityBytes,
+ final boolean readOnly,
+ final ByteOrder byteOrder) {
+ final long newOffsetBytes = offsetBytes + regionOffsetBytes;
+ final long newCumOffsetBytes = cumOffsetBytes + regionOffsetBytes;
+ int typeIdOut = removeNnBuf(typeId) | MEMORY | REGION | (readOnly ? READONLY : 0);
+
+ if (Util.isNativeByteOrder(byteOrder)) {
+ typeIdOut |= NATIVE_BO;
+ return new HeapWritableMemoryImpl(
+ unsafeObj, newOffsetBytes, capacityBytes, typeIdOut, newCumOffsetBytes, memReqSvr);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new HeapNonNativeWritableMemoryImpl(
+ unsafeObj, newOffsetBytes, capacityBytes, typeIdOut, newCumOffsetBytes, memReqSvr);
+ }
+ }
+
+ @Override
+ BaseWritableBufferImpl toWritableBuffer(final boolean readOnly, final ByteOrder byteOrder) {
+ int typeIdOut = removeNnBuf(typeId) | BUFFER | (readOnly ? READONLY : 0);
+
+ if (byteOrder == ByteOrder.nativeOrder()) {
+ typeIdOut |= NATIVE_BO;
+ return new HeapWritableBufferImpl(
+ unsafeObj, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr);
+ } else {
+ typeIdOut |= NONNATIVE_BO;
+ return new HeapNonNativeWritableBufferImpl(
+ unsafeObj, offsetBytes, capacityBytes, typeIdOut, cumOffsetBytes, memReqSvr);
+ }
+ }
+
+ @Override
+ Object getUnsafeObject() {
+ return unsafeObj;
+ }
+
+}
diff --git a/datasketches-memory-java11/src/test/java/org/apache/datasketches/memory/internal/VirtualMachineMemoryTest.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/Ints.java
similarity index 64%
copy from datasketches-memory-java11/src/test/java/org/apache/datasketches/memory/internal/VirtualMachineMemoryTest.java
copy to datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/Ints.java
index 2793b30b..72a447d6 100644
--- a/datasketches-memory-java11/src/test/java/org/apache/datasketches/memory/internal/VirtualMachineMemoryTest.java
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/Ints.java
@@ -19,15 +19,21 @@
package org.apache.datasketches.memory.internal;
-import org.testng.annotations.Test;
+/** Equivalent of Guava's Ints. */
+public final class Ints {
-@SuppressWarnings({"unused"})
-public class VirtualMachineMemoryTest {
+ private Ints() {}
- @Test
- public void inertPageAlignment() {
- boolean result = VirtualMachineMemory.getIsPageAligned();
- //System.out.println("VM page alignment:" + result);
- assert (true); //no exception was thrown
+ /**
+ * Checks if a cast of a long to an int is within the range of an int
+ * @param v the given long
+ * @return returns the cast int, or throws an exception that the long was out-of-range of an int.
+ */
+ public static int checkedCast(final long v) {
+ final int result = (int) v;
+ if (result != v) {
+ throw new IllegalArgumentException("Out of range: " + v);
}
+ return result;
+ }
}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/MemoryCleaner.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/MemoryCleaner.java
new file mode 100644
index 00000000..fd21f119
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/MemoryCleaner.java
@@ -0,0 +1,51 @@
+/*
+ * 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 jdk.internal.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
+ */
+//@SuppressWarnings("restriction")
+public class MemoryCleaner {
+ private final Cleaner cleaner;
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Runs this cleaner, if it has not been run before.
+ */
+ public void clean() {
+ cleaner.clean();
+ }
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/NativeWritableBufferImpl.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/NativeWritableBufferImpl.java
new file mode 100644
index 00000000..d5be892a
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/NativeWritableBufferImpl.java
@@ -0,0 +1,352 @@
+/*
+ * 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.ARRAY_CHAR_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_DOUBLE_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_DOUBLE_INDEX_SCALE;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_FLOAT_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_FLOAT_INDEX_SCALE;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_INT_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_LONG_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_SHORT_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.unsafe;
+
+import org.apache.datasketches.memory.WritableBuffer;
+
+/**
+ * Implementation of {@link WritableBuffer} for native endian byte order.
+ * @author Roman Leventov
+ * @author Lee Rhodes
+ */
+//@SuppressWarnings("restriction")
+abstract class NativeWritableBufferImpl extends BaseWritableBufferImpl {
+
+ //Pass-through constructor
+ NativeWritableBufferImpl(final long capacityBytes) { super(capacityBytes); }
+
+ //PRIMITIVE getX() and getXArray()
+ @Override
+ public char getChar() {
+ return getNativeOrderedChar();
+ }
+
+ @Override
+ public char getChar(final long offsetBytes) {
+ return getNativeOrderedChar(offsetBytes);
+ }
+
+ @Override
+ public void getCharArray(final char[] dstArray, final int dstOffsetChars, final int lengthChars) {
+ final long pos = getPosition();
+ final long copyBytes = ((long) lengthChars) << CHAR_SHIFT;
+ incrementAndCheckPositionForRead(pos, copyBytes);
+ ResourceImpl.checkBounds(dstOffsetChars, lengthChars, dstArray.length);
+ CompareAndCopy.copyMemoryCheckingDifferentObject(
+ getUnsafeObject(),
+ getCumulativeOffset(pos),
+ dstArray,
+ ARRAY_CHAR_BASE_OFFSET + (((long) dstOffsetChars) << CHAR_SHIFT),
+ copyBytes);
+ }
+
+ @Override
+ public double getDouble() {
+ final long pos = getPosition();
+ incrementAndCheckPositionForRead(pos, ARRAY_DOUBLE_INDEX_SCALE);
+ return unsafe.getDouble(getUnsafeObject(), getCumulativeOffset(pos));
+ }
+
+ @Override
+ public double getDouble(final long offsetBytes) {
+ checkValidAndBounds(offsetBytes, ARRAY_DOUBLE_INDEX_SCALE);
+ return unsafe.getDouble(getUnsafeObject(), getCumulativeOffset(offsetBytes));
+ }
+
+ @Override
+ public void getDoubleArray(final double[] dstArray, final int dstOffsetDoubles,
+ final int lengthDoubles) {
+ final long pos = getPosition();
+ final long copyBytes = ((long) lengthDoubles) << DOUBLE_SHIFT;
+ incrementAndCheckPositionForRead(pos, copyBytes);
+ ResourceImpl.checkBounds(dstOffsetDoubles, lengthDoubles, dstArray.length);
+ CompareAndCopy.copyMemoryCheckingDifferentObject(
+ getUnsafeObject(),
+ getCumulativeOffset(pos),
+ dstArray,
+ ARRAY_DOUBLE_BASE_OFFSET + (((long) dstOffsetDoubles) << DOUBLE_SHIFT),
+ copyBytes);
+ }
+
+ @Override
+ public float getFloat() {
+ final long pos = getPosition();
+ incrementAndCheckPositionForRead(pos, ARRAY_FLOAT_INDEX_SCALE);
+ return unsafe.getFloat(getUnsafeObject(), getCumulativeOffset(pos));
+ }
+
+ @Override
+ public float getFloat(final long offsetBytes) {
+ checkValidAndBounds(offsetBytes, ARRAY_FLOAT_INDEX_SCALE);
+ return unsafe.getFloat(getUnsafeObject(), getCumulativeOffset(offsetBytes));
+ }
+
+ @Override
+ public void getFloatArray(final float[] dstArray, final int dstOffsetFloats,
+ final int lengthFloats) {
+ final long pos = getPosition();
+ final long copyBytes = ((long) lengthFloats) << FLOAT_SHIFT;
+ incrementAndCheckPositionForRead(pos, copyBytes);
+ ResourceImpl.checkBounds(dstOffsetFloats, lengthFloats, dstArray.length);
+ CompareAndCopy.copyMemoryCheckingDifferentObject(
+ getUnsafeObject(),
+ getCumulativeOffset(pos),
+ dstArray,
+ ARRAY_FLOAT_BASE_OFFSET + (((long) dstOffsetFloats) << FLOAT_SHIFT),
+ copyBytes);
+ }
+
+ @Override
+ public int getInt() {
+ return getNativeOrderedInt();
+ }
+
+ @Override
+ public int getInt(final long offsetBytes) {
+ return getNativeOrderedInt(offsetBytes);
+ }
+
+ @Override
+ public void getIntArray(final int[] dstArray, final int dstOffsetInts, final int lengthInts) {
+ final long pos = getPosition();
+ final long copyBytes = ((long) lengthInts) << INT_SHIFT;
+ incrementAndCheckPositionForRead(pos, copyBytes);
+ ResourceImpl.checkBounds(dstOffsetInts, lengthInts, dstArray.length);
+ CompareAndCopy.copyMemoryCheckingDifferentObject(
+ getUnsafeObject(),
+ getCumulativeOffset(pos),
+ dstArray,
+ ARRAY_INT_BASE_OFFSET + (((long) dstOffsetInts) << INT_SHIFT),
+ copyBytes);
+ }
+
+ @Override
+ public long getLong() {
+ return getNativeOrderedLong();
+ }
+
+ @Override
+ public long getLong(final long offsetBytes) {
+ return getNativeOrderedLong(offsetBytes);
+ }
+
+ @Override
+ public void getLongArray(final long[] dstArray, final int dstOffsetLongs, final int lengthLongs) {
+ final long pos = getPosition();
+ final long copyBytes = ((long) lengthLongs) << LONG_SHIFT;
+ incrementAndCheckPositionForRead(pos, copyBytes);
+ ResourceImpl.checkBounds(dstOffsetLongs, lengthLongs, dstArray.length);
+ CompareAndCopy.copyMemoryCheckingDifferentObject(
+ getUnsafeObject(),
+ getCumulativeOffset(pos),
+ dstArray,
+ ARRAY_LONG_BASE_OFFSET + (((long) dstOffsetLongs) << LONG_SHIFT),
+ copyBytes);
+ }
+
+ @Override
+ public short getShort() {
+ return getNativeOrderedShort();
+ }
+
+ @Override
+ public short getShort(final long offsetBytes) {
+ return getNativeOrderedShort(offsetBytes);
+ }
+
+ @Override
+ public void getShortArray(final short[] dstArray, final int dstOffsetShorts,
+ final int lengthShorts) {
+ final long pos = getPosition();
+ final long copyBytes = ((long) lengthShorts) << SHORT_SHIFT;
+ incrementAndCheckPositionForRead(pos, copyBytes);
+ ResourceImpl.checkBounds(dstOffsetShorts, lengthShorts, dstArray.length);
+ CompareAndCopy.copyMemoryCheckingDifferentObject(
+ getUnsafeObject(),
+ getCumulativeOffset(pos),
+ dstArray,
+ ARRAY_SHORT_BASE_OFFSET + (((long) dstOffsetShorts) << SHORT_SHIFT),
+ copyBytes);
+ }
+
+ //PRIMITIVE putX() and putXArray()
+ @Override
+ public void putChar(final char value) {
+ putNativeOrderedChar(value);
+ }
+
+ @Override
+ public void putChar(final long offsetBytes, final char value) {
+ putNativeOrderedChar(offsetBytes, value);
+ }
+
+ @Override
+ public void putCharArray(final char[] srcArray, final int srcOffsetChars, final int lengthChars) {
+ final long pos = getPosition();
+ final long copyBytes = ((long) lengthChars) << CHAR_SHIFT;
+ incrementAndCheckPositionForWrite(pos, copyBytes);
+ ResourceImpl.checkBounds(srcOffsetChars, lengthChars, srcArray.length);
+ CompareAndCopy.copyMemoryCheckingDifferentObject(
+ srcArray,
+ ARRAY_CHAR_BASE_OFFSET + (((long) srcOffsetChars) << CHAR_SHIFT),
+ getUnsafeObject(),
+ getCumulativeOffset(pos),
+ copyBytes);
+ }
+
+ @Override
+ public void putDouble(final double value) {
+ final long pos = getPosition();
+ incrementAndCheckPositionForWrite(pos, ARRAY_DOUBLE_INDEX_SCALE);
+ unsafe.putDouble(getUnsafeObject(), getCumulativeOffset(pos), value);
+ }
+
+ @Override
+ public void putDouble(final long offsetBytes, final double value) {
+ checkValidAndBoundsForWrite(offsetBytes, ARRAY_DOUBLE_INDEX_SCALE);
+ unsafe.putDouble(getUnsafeObject(), getCumulativeOffset(offsetBytes), value);
+ }
+
+ @Override
+ public void putDoubleArray(final double[] srcArray, final int srcOffsetDoubles,
+ final int lengthDoubles) {
+ final long pos = getPosition();
+ final long copyBytes = ((long) lengthDoubles) << DOUBLE_SHIFT;
+ incrementAndCheckPositionForWrite(pos, copyBytes);
+ ResourceImpl.checkBounds(srcOffsetDoubles, lengthDoubles, srcArray.length);
+ CompareAndCopy.copyMemoryCheckingDifferentObject(
+ srcArray,
+ ARRAY_DOUBLE_BASE_OFFSET + (((long) srcOffsetDoubles) << DOUBLE_SHIFT),
+ getUnsafeObject(),
+ getCumulativeOffset(pos),
+ copyBytes);
+ }
+
+ @Override
+ public void putFloat(final float value) {
+ final long pos = getPosition();
+ incrementAndCheckPositionForWrite(pos, ARRAY_FLOAT_INDEX_SCALE);
+ unsafe.putFloat(getUnsafeObject(), getCumulativeOffset(pos), value);
+ }
+
+ @Override
+ public void putFloat(final long offsetBytes, final float value) {
+ checkValidAndBoundsForWrite(offsetBytes, ARRAY_FLOAT_INDEX_SCALE);
+ unsafe.putFloat(getUnsafeObject(), getCumulativeOffset(offsetBytes), value);
+ }
+
+ @Override
+ public void putFloatArray(final float[] srcArray, final int srcOffsetFloats,
+ final int lengthFloats) {
+ final long pos = getPosition();
+ final long copyBytes = ((long) lengthFloats) << FLOAT_SHIFT;
+ incrementAndCheckPositionForWrite(pos, copyBytes);
+ ResourceImpl.checkBounds(srcOffsetFloats, lengthFloats, srcArray.length);
+ CompareAndCopy.copyMemoryCheckingDifferentObject(
+ srcArray,
+ ARRAY_FLOAT_BASE_OFFSET + (((long) srcOffsetFloats) << FLOAT_SHIFT),
+ getUnsafeObject(),
+ getCumulativeOffset(pos),
+ copyBytes);
+ }
+
+ @Override
+ public void putInt(final int value) {
+ putNativeOrderedInt(value);
+ }
+
+ @Override
+ public void putInt(final long offsetBytes, final int value) {
+ putNativeOrderedInt(offsetBytes, value);
+ }
+
+ @Override
+ public void putIntArray(final int[] srcArray, final int srcOffsetInts, final int lengthInts) {
+ final long pos = getPosition();
+ final long copyBytes = ((long) lengthInts) << INT_SHIFT;
+ incrementAndCheckPositionForWrite(pos, copyBytes);
+ ResourceImpl.checkBounds(srcOffsetInts, lengthInts, srcArray.length);
+ CompareAndCopy.copyMemoryCheckingDifferentObject(
+ srcArray,
+ ARRAY_INT_BASE_OFFSET + (((long) srcOffsetInts) << INT_SHIFT),
+ getUnsafeObject(),
+ getCumulativeOffset(pos),
+ copyBytes);
+ }
+
+ @Override
+ public void putLong(final long value) {
+ putNativeOrderedLong(value);
+ }
+
+ @Override
+ public void putLong(final long offsetBytes, final long value) {
+ putNativeOrderedLong(offsetBytes, value);
+ }
+
+ @Override
+ public void putLongArray(final long[] srcArray, final int srcOffsetLongs, final int lengthLongs) {
+ final long pos = getPosition();
+ final long copyBytes = ((long) lengthLongs) << LONG_SHIFT;
+ incrementAndCheckPositionForWrite(pos, copyBytes);
+ ResourceImpl.checkBounds(srcOffsetLongs, lengthLongs, srcArray.length);
+ CompareAndCopy.copyMemoryCheckingDifferentObject(
+ srcArray,
+ ARRAY_LONG_BASE_OFFSET + (((long) srcOffsetLongs) << LONG_SHIFT),
+ getUnsafeObject(),
+ getCumulativeOffset(pos),
+ copyBytes);
+ }
+
+ @Override
+ public void putShort(final short value) {
+ putNativeOrderedShort(value);
+ }
+
+ @Override
+ public void putShort(final long offsetBytes, final short value) {
+ putNativeOrderedShort(offsetBytes, value);
+ }
+
+ @Override
+ public void putShortArray(final short[] srcArray, final int srcOffsetShorts,
+ final int lengthShorts) {
+ final long pos = getPosition();
+ final long copyBytes = ((long) lengthShorts) << SHORT_SHIFT;
+ incrementAndCheckPositionForWrite(pos, copyBytes);
+ ResourceImpl.checkBounds(srcOffsetShorts, lengthShorts, srcArray.length);
+ CompareAndCopy.copyMemoryCheckingDifferentObject(
+ srcArray,
+ ARRAY_SHORT_BASE_OFFSET + (((long) srcOffsetShorts) << SHORT_SHIFT),
+ getUnsafeObject(),
+ getCumulativeOffset(pos),
+ copyBytes);
+ }
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/NativeWritableMemoryImpl.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/NativeWritableMemoryImpl.java
new file mode 100644
index 00000000..198fbe06
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/NativeWritableMemoryImpl.java
@@ -0,0 +1,285 @@
+/*
+ * 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.ARRAY_CHAR_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_DOUBLE_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_DOUBLE_INDEX_SCALE;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_FLOAT_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_FLOAT_INDEX_SCALE;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_INT_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_LONG_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_SHORT_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.unsafe;
+
+import org.apache.datasketches.memory.WritableMemory;
+
+/**
+ * Implementation of {@link WritableMemory} for native endian byte order.
+ * @author Roman Leventov
+ * @author Lee Rhodes
+ */
+//@SuppressWarnings("restriction")
+abstract class NativeWritableMemoryImpl extends BaseWritableMemoryImpl {
+
+ //Pass-through constructor
+ NativeWritableMemoryImpl() { }
+
+ ///PRIMITIVE getX() and getXArray()
+ @Override
+ public char getChar(final long offsetBytes) {
+ return getNativeOrderedChar(offsetBytes);
+ }
+
+ @Override
+ public void getCharArray(final long offsetBytes, final char[] dstArray, final int dstOffsetChars,
+ final int lengthChars) {
+ final long copyBytes = ((long) lengthChars) << CHAR_SHIFT;
+ checkValidAndBounds(offsetBytes, copyBytes);
+ ResourceImpl.checkBounds(dstOffsetChars, lengthChars, dstArray.length);
+ CompareAndCopy.copyMemoryCheckingDifferentObject(
+ getUnsafeObject(),
+ getCumulativeOffset(offsetBytes),
+ dstArray,
+ ARRAY_CHAR_BASE_OFFSET + (((long) dstOffsetChars) << CHAR_SHIFT),
+ copyBytes);
+ }
+
+ @Override
+ public double getDouble(final long offsetBytes) {
+ checkValidAndBounds(offsetBytes, ARRAY_DOUBLE_INDEX_SCALE);
+ return unsafe.getDouble(getUnsafeObject(), getCumulativeOffset(offsetBytes));
+ }
+
+ @Override
+ public void getDoubleArray(final long offsetBytes, final double[] dstArray,
+ final int dstOffsetDoubles, final int lengthDoubles) {
+ final long copyBytes = ((long) lengthDoubles) << DOUBLE_SHIFT;
+ checkValidAndBounds(offsetBytes, copyBytes);
+ ResourceImpl.checkBounds(dstOffsetDoubles, lengthDoubles, dstArray.length);
+ CompareAndCopy.copyMemoryCheckingDifferentObject(
+ getUnsafeObject(),
+ getCumulativeOffset(offsetBytes),
+ dstArray,
+ ARRAY_DOUBLE_BASE_OFFSET + (((long) dstOffsetDoubles) << DOUBLE_SHIFT),
+ copyBytes);
+ }
+
+ @Override
+ public float getFloat(final long offsetBytes) {
+ checkValidAndBounds(offsetBytes, ARRAY_FLOAT_INDEX_SCALE);
+ return unsafe.getFloat(getUnsafeObject(), getCumulativeOffset(offsetBytes));
+ }
+
+ @Override
+ public void getFloatArray(final long offsetBytes, final float[] dstArray,
+ final int dstOffsetFloats, final int lengthFloats) {
+ final long copyBytes = ((long) lengthFloats) << FLOAT_SHIFT;
+ checkValidAndBounds(offsetBytes, copyBytes);
+ ResourceImpl.checkBounds(dstOffsetFloats, lengthFloats, dstArray.length);
+ CompareAndCopy.copyMemoryCheckingDifferentObject(
+ getUnsafeObject(),
+ getCumulativeOffset(offsetBytes),
+ dstArray,
+ ARRAY_FLOAT_BASE_OFFSET + (((long) dstOffsetFloats) << FLOAT_SHIFT),
+ copyBytes);
+ }
+
+ @Override
+ public int getInt(final long offsetBytes) {
+ return getNativeOrderedInt(offsetBytes);
+ }
+
+ @Override
+ public void getIntArray(final long offsetBytes, final int[] dstArray, final int dstOffsetInts,
+ final int lengthInts) {
+ final long copyBytes = ((long) lengthInts) << INT_SHIFT;
+ checkValidAndBounds(offsetBytes, copyBytes);
+ ResourceImpl.checkBounds(dstOffsetInts, lengthInts, dstArray.length);
+ CompareAndCopy.copyMemoryCheckingDifferentObject(
+ getUnsafeObject(),
+ getCumulativeOffset(offsetBytes),
+ dstArray,
+ ARRAY_INT_BASE_OFFSET + (((long) dstOffsetInts) << INT_SHIFT),
+ copyBytes);
+ }
+
+ @Override
+ public long getLong(final long offsetBytes) {
+ return getNativeOrderedLong(offsetBytes);
+ }
+
+ @Override
+ public void getLongArray(final long offsetBytes, final long[] dstArray,
+ final int dstOffsetLongs, final int lengthLongs) {
+ final long copyBytes = ((long) lengthLongs) << LONG_SHIFT;
+ checkValidAndBounds(offsetBytes, copyBytes);
+ ResourceImpl.checkBounds(dstOffsetLongs, lengthLongs, dstArray.length);
+ CompareAndCopy.copyMemoryCheckingDifferentObject(
+ getUnsafeObject(),
+ getCumulativeOffset(offsetBytes),
+ dstArray,
+ ARRAY_LONG_BASE_OFFSET + (((long) dstOffsetLongs) << LONG_SHIFT),
+ copyBytes);
+ }
+
+ @Override
+ public short getShort(final long offsetBytes) {
+ return getNativeOrderedShort(offsetBytes);
+ }
+
+ @Override
+ public void getShortArray(final long offsetBytes, final short[] dstArray,
+ final int dstOffsetShorts, final int lengthShorts) {
+ final long copyBytes = ((long) lengthShorts) << SHORT_SHIFT;
+ checkValidAndBounds(offsetBytes, copyBytes);
+ ResourceImpl.checkBounds(dstOffsetShorts, lengthShorts, dstArray.length);
+ CompareAndCopy.copyMemoryCheckingDifferentObject(
+ getUnsafeObject(),
+ getCumulativeOffset(offsetBytes),
+ dstArray,
+ ARRAY_SHORT_BASE_OFFSET + (((long) dstOffsetShorts) << SHORT_SHIFT),
+ copyBytes);
+ }
+
+ //PRIMITIVE putX() and putXArray() implementations
+ @Override
+ public void putChar(final long offsetBytes, final char value) {
+ putNativeOrderedChar(offsetBytes, value);
+ }
+
+ @Override
+ public void putCharArray(final long offsetBytes, final char[] srcArray,
+ final int srcOffsetChars, final int lengthChars) {
+ final long copyBytes = ((long) lengthChars) << CHAR_SHIFT;
+ checkValidAndBoundsForWrite(offsetBytes, copyBytes);
+ ResourceImpl.checkBounds(srcOffsetChars, lengthChars, srcArray.length);
+ CompareAndCopy.copyMemoryCheckingDifferentObject(
+ srcArray,
+ ARRAY_CHAR_BASE_OFFSET + (((long) srcOffsetChars) << CHAR_SHIFT),
+ getUnsafeObject(),
+ getCumulativeOffset(offsetBytes),
+ copyBytes
+ );
+ }
+
+ @Override
+ public void putDouble(final long offsetBytes, final double value) {
+ checkValidAndBoundsForWrite(offsetBytes, ARRAY_DOUBLE_INDEX_SCALE);
+ unsafe.putDouble(getUnsafeObject(), getCumulativeOffset(offsetBytes), value);
+ }
+
+ @Override
+ public void putDoubleArray(final long offsetBytes, final double[] srcArray,
+ final int srcOffsetDoubles, final int lengthDoubles) {
+ final long copyBytes = ((long) lengthDoubles) << DOUBLE_SHIFT;
+ checkValidAndBoundsForWrite(offsetBytes, copyBytes);
+ ResourceImpl.checkBounds(srcOffsetDoubles, lengthDoubles, srcArray.length);
+ CompareAndCopy.copyMemoryCheckingDifferentObject(
+ srcArray,
+ ARRAY_DOUBLE_BASE_OFFSET + (((long) srcOffsetDoubles) << DOUBLE_SHIFT),
+ getUnsafeObject(),
+ getCumulativeOffset(offsetBytes),
+ copyBytes
+ );
+ }
+
+ @Override
+ public void putFloat(final long offsetBytes, final float value) {
+ checkValidAndBoundsForWrite(offsetBytes, ARRAY_FLOAT_INDEX_SCALE);
+ unsafe.putFloat(getUnsafeObject(), getCumulativeOffset(offsetBytes), value);
+ }
+
+ @Override
+ public void putFloatArray(final long offsetBytes, final float[] srcArray,
+ final int srcOffsetFloats, final int lengthFloats) {
+ final long copyBytes = ((long) lengthFloats) << FLOAT_SHIFT;
+ checkValidAndBoundsForWrite(offsetBytes, copyBytes);
+ ResourceImpl.checkBounds(srcOffsetFloats, lengthFloats, srcArray.length);
+ CompareAndCopy.copyMemoryCheckingDifferentObject(
+ srcArray,
+ ARRAY_FLOAT_BASE_OFFSET + (((long) srcOffsetFloats) << FLOAT_SHIFT),
+ getUnsafeObject(),
+ getCumulativeOffset(offsetBytes),
+ copyBytes
+ );
+ }
+
+ @Override
+ public void putInt(final long offsetBytes, final int value) {
+ putNativeOrderedInt(offsetBytes, value);
+ }
+
+ @Override
+ public void putIntArray(final long offsetBytes, final int[] srcArray, final int srcOffsetInts,
+ final int lengthInts) {
+ final long copyBytes = ((long) lengthInts) << INT_SHIFT;
+ checkValidAndBoundsForWrite(offsetBytes, copyBytes);
+ ResourceImpl.checkBounds(srcOffsetInts, lengthInts, srcArray.length);
+ CompareAndCopy.copyMemoryCheckingDifferentObject(
+ srcArray,
+ ARRAY_INT_BASE_OFFSET + (((long) srcOffsetInts) << INT_SHIFT),
+ getUnsafeObject(),
+ getCumulativeOffset(offsetBytes),
+ copyBytes
+ );
+ }
+
+ @Override
+ public void putLong(final long offsetBytes, final long value) {
+ putNativeOrderedLong(offsetBytes, value);
+ }
+
+ @Override
+ public void putLongArray(final long offsetBytes, final long[] srcArray, final int srcOffsetLongs,
+ final int lengthLongs) {
+ final long copyBytes = ((long) lengthLongs) << LONG_SHIFT;
+ checkValidAndBoundsForWrite(offsetBytes, copyBytes);
+ ResourceImpl.checkBounds(srcOffsetLongs, lengthLongs, srcArray.length);
+ CompareAndCopy.copyMemoryCheckingDifferentObject(
+ srcArray,
+ ARRAY_LONG_BASE_OFFSET + (((long) srcOffsetLongs) << LONG_SHIFT),
+ getUnsafeObject(),
+ getCumulativeOffset(offsetBytes),
+ copyBytes
+ );
+ }
+
+ @Override
+ public void putShort(final long offsetBytes, final short value) {
+ putNativeOrderedShort(offsetBytes, value);
+ }
+
+ @Override
+ public void putShortArray(final long offsetBytes, final short[] srcArray,
+ final int srcOffsetShorts, final int lengthShorts) {
+ final long copyBytes = ((long) lengthShorts) << SHORT_SHIFT;
+ checkValidAndBoundsForWrite(offsetBytes, copyBytes);
+ ResourceImpl.checkBounds(srcOffsetShorts, lengthShorts, srcArray.length);
+ CompareAndCopy.copyMemoryCheckingDifferentObject(
+ srcArray,
+ ARRAY_SHORT_BASE_OFFSET + (((long) srcOffsetShorts) << SHORT_SHIFT),
+ getUnsafeObject(),
+ getCumulativeOffset(offsetBytes),
+ copyBytes
+ );
+ }
+
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/NonNativeWritableBufferImpl.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/NonNativeWritableBufferImpl.java
new file mode 100644
index 00000000..f16c57c3
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/NonNativeWritableBufferImpl.java
@@ -0,0 +1,295 @@
+/*
+ * 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.ARRAY_DOUBLE_INDEX_SCALE;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_FLOAT_INDEX_SCALE;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.unsafe;
+
+import org.apache.datasketches.memory.WritableBuffer;
+
+/**
+ * Implementation of {@link WritableBuffer} for non-native endian byte order.
+ * @author Roman Leventov
+ * @author Lee Rhodes
+ */
+//@SuppressWarnings("restriction")
+abstract class NonNativeWritableBufferImpl extends BaseWritableBufferImpl {
+
+ //Pass-through constructor
+ NonNativeWritableBufferImpl(final long capacityBytes) { super(capacityBytes); }
+
+ //PRIMITIVE getX() and getXArray()
+ @Override
+ public char getChar() {
+ return Character.reverseBytes(getNativeOrderedChar());
+ }
+
+ @Override
+ public char getChar(final long offsetBytes) {
+ return Character.reverseBytes(getNativeOrderedChar(offsetBytes));
+ }
+
+ @Override
+ public void getCharArray(final char[] dstArray, final int dstOffsetChars, final int lengthChars) {
+ final long pos = getPosition();
+ final long copyBytes = ((long) lengthChars) << CHAR_SHIFT;
+ incrementAndCheckPositionForRead(pos, copyBytes);
+ CompareAndCopy.getNonNativeChars(getUnsafeObject(), getCumulativeOffset(pos), copyBytes,
+ dstArray, dstOffsetChars, lengthChars);
+ }
+
+ @Override
+ public double getDouble() {
+ final long pos = getPosition();
+ incrementAndCheckPositionForRead(pos, ARRAY_DOUBLE_INDEX_SCALE);
+ return Double.longBitsToDouble(
+ Long.reverseBytes(unsafe.getLong(getUnsafeObject(), getCumulativeOffset(pos))));
+ }
+
+ @Override
+ public double getDouble(final long offsetBytes) {
+ checkValidAndBounds(offsetBytes, ARRAY_DOUBLE_INDEX_SCALE);
+ return Double.longBitsToDouble(
+ Long.reverseBytes(unsafe.getLong(getUnsafeObject(), getCumulativeOffset(offsetBytes))));
+ }
+
+ @Override
+ public void getDoubleArray(final double[] dstArray, final int dstOffsetDoubles,
+ final int lengthDoubles) {
+ final long pos = getPosition();
+ final long copyBytes = ((long) lengthDoubles) << DOUBLE_SHIFT;
+ incrementAndCheckPositionForRead(pos, copyBytes);
+ CompareAndCopy.getNonNativeDoubles(getUnsafeObject(), getCumulativeOffset(pos), copyBytes,
+ dstArray, dstOffsetDoubles, lengthDoubles);
+ }
+
+ @Override
+ public float getFloat() {
+ final long pos = getPosition();
+ incrementAndCheckPositionForRead(pos, ARRAY_FLOAT_INDEX_SCALE);
+ return Float.intBitsToFloat(
+ Integer.reverseBytes(unsafe.getInt(getUnsafeObject(), getCumulativeOffset(pos))));
+ }
+
+ @Override
+ public float getFloat(final long offsetBytes) {
+ checkValidAndBounds(offsetBytes, ARRAY_FLOAT_INDEX_SCALE);
+ return Float.intBitsToFloat(
+ Integer.reverseBytes(unsafe.getInt(getUnsafeObject(), getCumulativeOffset(offsetBytes))));
+ }
+
+ @Override
+ public void getFloatArray(final float[] dstArray, final int dstOffsetFloats,
+ final int lengthFloats) {
+ final long pos = getPosition();
+ final long copyBytes = ((long) lengthFloats) << FLOAT_SHIFT;
+ incrementAndCheckPositionForRead(pos, copyBytes);
+ CompareAndCopy.getNonNativeFloats(getUnsafeObject(), getCumulativeOffset(pos), copyBytes,
+ dstArray, dstOffsetFloats, lengthFloats);
+ }
+
+ @Override
+ public int getInt() {
+ return Integer.reverseBytes(getNativeOrderedInt());
+ }
+
+ @Override
+ public int getInt(final long offsetBytes) {
+ return Integer.reverseBytes(getNativeOrderedInt(offsetBytes));
+ }
+
+ @Override
+ public void getIntArray(final int[] dstArray, final int dstOffsetInts, final int lengthInts) {
+ final long pos = getPosition();
+ final long copyBytes = ((long) lengthInts) << INT_SHIFT;
+ incrementAndCheckPositionForRead(pos, copyBytes);
+ CompareAndCopy.getNonNativeInts(getUnsafeObject(), getCumulativeOffset(pos), copyBytes,
+ dstArray, dstOffsetInts, lengthInts);
+ }
+
+ @Override
+ public long getLong() {
+ return Long.reverseBytes(getNativeOrderedLong());
+ }
+
+ @Override
+ public long getLong(final long offsetBytes) {
+ return Long.reverseBytes(getNativeOrderedLong(offsetBytes));
+ }
+
+ @Override
+ public void getLongArray(final long[] dstArray, final int dstOffsetLongs, final int lengthLongs) {
+ final long pos = getPosition();
+ final long copyBytes = ((long) lengthLongs) << LONG_SHIFT;
+ incrementAndCheckPositionForRead(pos, copyBytes);
+ CompareAndCopy.getNonNativeLongs(getUnsafeObject(), getCumulativeOffset(pos), copyBytes,
+ dstArray, dstOffsetLongs, lengthLongs);
+ }
+
+ @Override
+ public short getShort() {
+ return Short.reverseBytes(getNativeOrderedShort());
+ }
+
+ @Override
+ public short getShort(final long offsetBytes) {
+ return Short.reverseBytes(getNativeOrderedShort(offsetBytes));
+ }
+
+ @Override
+ public void getShortArray(final short[] dstArray, final int dstOffsetShorts,
+ final int lengthShorts) {
+ final long pos = getPosition();
+ final long copyBytes = ((long) lengthShorts) << SHORT_SHIFT;
+ incrementAndCheckPositionForRead(pos, copyBytes);
+ CompareAndCopy.getNonNativeShorts(getUnsafeObject(), getCumulativeOffset(pos), copyBytes,
+ dstArray, dstOffsetShorts, lengthShorts);
+ }
+
+ //PRIMITIVE putX() and putXArray()
+ @Override
+ public void putChar(final char value) {
+ putNativeOrderedChar(Character.reverseBytes(value));
+ }
+
+ @Override
+ public void putChar(final long offsetBytes, final char value) {
+ putNativeOrderedChar(offsetBytes, Character.reverseBytes(value));
+ }
+
+ @Override
+ public void putCharArray(final char[] srcArray, final int srcOffsetChars, final int lengthChars) {
+ final long pos = getPosition();
+ final long copyBytes = ((long) lengthChars) << CHAR_SHIFT;
+ incrementAndCheckPositionForWrite(pos, copyBytes);
+ CompareAndCopy.putNonNativeChars(srcArray, srcOffsetChars, lengthChars, copyBytes,
+ getUnsafeObject(), getCumulativeOffset(pos));
+ }
+
+ @Override
+ public void putDouble(final double value) {
+ final long pos = getPosition();
+ incrementAndCheckPositionForWrite(pos, ARRAY_DOUBLE_INDEX_SCALE);
+ unsafe.putLong(getUnsafeObject(), getCumulativeOffset(pos),
+ Long.reverseBytes(Double.doubleToRawLongBits(value)));
+ }
+
+ @Override
+ public void putDouble(final long offsetBytes, final double value) {
+ checkValidAndBoundsForWrite(offsetBytes, ARRAY_DOUBLE_INDEX_SCALE);
+ unsafe.putLong(getUnsafeObject(), getCumulativeOffset(offsetBytes),
+ Long.reverseBytes(Double.doubleToRawLongBits(value)));
+ }
+
+ @Override
+ public void putDoubleArray(final double[] srcArray, final int srcOffsetDoubles,
+ final int lengthDoubles) {
+ final long pos = getPosition();
+ final long copyBytes = ((long) lengthDoubles) << DOUBLE_SHIFT;
+ incrementAndCheckPositionForWrite(pos, copyBytes);
+ CompareAndCopy.putNonNativeDoubles(srcArray, srcOffsetDoubles, lengthDoubles, copyBytes,
+ getUnsafeObject(), getCumulativeOffset(pos));
+ }
+
+ @Override
+ public void putFloat(final float value) {
+ final long pos = getPosition();
+ incrementAndCheckPositionForWrite(pos, ARRAY_FLOAT_INDEX_SCALE);
+ unsafe.putInt(getUnsafeObject(), getCumulativeOffset(pos),
+ Integer.reverseBytes(Float.floatToRawIntBits(value)));
+ }
+
+ @Override
+ public void putFloat(final long offsetBytes, final float value) {
+ checkValidAndBoundsForWrite(offsetBytes, ARRAY_FLOAT_INDEX_SCALE);
+ unsafe.putInt(getUnsafeObject(), getCumulativeOffset(offsetBytes),
+ Integer.reverseBytes(Float.floatToRawIntBits(value)));
+ }
+
+ @Override
+ public void putFloatArray(final float[] srcArray, final int srcOffsetFloats,
+ final int lengthFloats) {
+ final long pos = getPosition();
+ final long copyBytes = ((long) lengthFloats) << FLOAT_SHIFT;
+ incrementAndCheckPositionForWrite(pos, copyBytes);
+ CompareAndCopy.putNonNativeFloats(srcArray, srcOffsetFloats, lengthFloats, copyBytes,
+ getUnsafeObject(), getCumulativeOffset(pos));
+ }
+
+ @Override
+ public void putInt(final int value) {
+ putNativeOrderedInt(Integer.reverseBytes(value));
+ }
+
+ @Override
+ public void putInt(final long offsetBytes, final int value) {
+ putNativeOrderedInt(offsetBytes, Integer.reverseBytes(value));
+ }
+
+ @Override
+ public void putIntArray(final int[] srcArray, final int srcOffsetInts, final int lengthInts) {
+ final long pos = getPosition();
+ final long copyBytes = ((long) lengthInts) << INT_SHIFT;
+ incrementAndCheckPositionForWrite(pos, copyBytes);
+ CompareAndCopy.putNonNativeInts(srcArray, srcOffsetInts, lengthInts, copyBytes,
+ getUnsafeObject(), getCumulativeOffset(pos));
+ }
+
+ @Override
+ public void putLong(final long value) {
+ putNativeOrderedLong(Long.reverseBytes(value));
+ }
+
+ @Override
+ public void putLong(final long offsetBytes, final long value) {
+ putNativeOrderedLong(offsetBytes, Long.reverseBytes(value));
+ }
+
+ @Override
+ public void putLongArray(final long[] srcArray, final int srcOffsetLongs, final int lengthLongs) {
+ final long pos = getPosition();
+ final long copyBytes = ((long) lengthLongs) << LONG_SHIFT;
+ incrementAndCheckPositionForWrite(pos, copyBytes);
+ CompareAndCopy.putNonNativeLongs(srcArray, srcOffsetLongs, lengthLongs, copyBytes,
+ getUnsafeObject(), getCumulativeOffset(pos));
+ }
+
+ @Override
+ public void putShort(final short value) {
+ putNativeOrderedShort(Short.reverseBytes(value));
+ }
+
+ @Override
+ public void putShort(final long offsetBytes, final short value) {
+ putNativeOrderedShort(offsetBytes, Short.reverseBytes(value));
+ }
+
+ @Override
+ public void putShortArray(final short[] srcArray, final int srcOffsetShorts,
+ final int lengthShorts) {
+ final long pos = getPosition();
+ final long copyBytes = ((long) lengthShorts) << SHORT_SHIFT;
+ incrementAndCheckPositionForWrite(pos, copyBytes);
+ CompareAndCopy.putNonNativeShorts(srcArray, srcOffsetShorts, lengthShorts, copyBytes,
+ getUnsafeObject(), getCumulativeOffset(pos));
+ }
+
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/NonNativeWritableMemoryImpl.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/NonNativeWritableMemoryImpl.java
new file mode 100644
index 00000000..bc94ecff
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/NonNativeWritableMemoryImpl.java
@@ -0,0 +1,217 @@
+/*
+ * 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.ARRAY_DOUBLE_INDEX_SCALE;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_FLOAT_INDEX_SCALE;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.unsafe;
+
+import org.apache.datasketches.memory.WritableMemory;
+
+/**
+ * Implementation of {@link WritableMemory} for non-native endian byte order.
+ * @author Roman Leventov
+ * @author Lee Rhodes
+ */
+//@SuppressWarnings("restriction")
+abstract class NonNativeWritableMemoryImpl extends BaseWritableMemoryImpl {
+
+ //Pass-through constructor
+ NonNativeWritableMemoryImpl() { }
+
+ ///PRIMITIVE getX() and getXArray()
+ @Override
+ public char getChar(final long offsetBytes) {
+ return Character.reverseBytes(getNativeOrderedChar(offsetBytes));
+ }
+
+ @Override
+ public void getCharArray(final long offsetBytes, final char[] dstArray, final int dstOffsetChars,
+ final int lengthChars) {
+ final long copyBytes = ((long) lengthChars) << CHAR_SHIFT;
+ checkValidAndBounds(offsetBytes, copyBytes);
+ CompareAndCopy.getNonNativeChars(getUnsafeObject(), getCumulativeOffset(offsetBytes),
+ copyBytes, dstArray, dstOffsetChars, lengthChars);
+ }
+
+ @Override
+ public double getDouble(final long offsetBytes) {
+ checkValidAndBounds(offsetBytes, ARRAY_DOUBLE_INDEX_SCALE);
+ return Double.longBitsToDouble(
+ Long.reverseBytes(unsafe.getLong(getUnsafeObject(), getCumulativeOffset(offsetBytes))));
+ }
+
+ @Override
+ public void getDoubleArray(final long offsetBytes, final double[] dstArray,
+ final int dstOffsetDoubles, final int lengthDoubles) {
+ final long copyBytes = ((long) lengthDoubles) << DOUBLE_SHIFT;
+ checkValidAndBounds(offsetBytes, copyBytes);
+ CompareAndCopy.getNonNativeDoubles(getUnsafeObject(), getCumulativeOffset(offsetBytes),
+ copyBytes, dstArray, dstOffsetDoubles, lengthDoubles);
+ }
+
+ @Override
+ public float getFloat(final long offsetBytes) {
+ checkValidAndBounds(offsetBytes, ARRAY_FLOAT_INDEX_SCALE);
+ return Float.intBitsToFloat(
+ Integer.reverseBytes(unsafe.getInt(getUnsafeObject(), getCumulativeOffset(offsetBytes))));
+ }
+
+ @Override
+ public void getFloatArray(final long offsetBytes, final float[] dstArray,
+ final int dstOffsetFloats, final int lengthFloats) {
+ final long copyBytes = ((long) lengthFloats) << FLOAT_SHIFT;
+ checkValidAndBounds(offsetBytes, copyBytes);
+ CompareAndCopy.getNonNativeFloats(getUnsafeObject(), getCumulativeOffset(offsetBytes),
+ copyBytes, dstArray, dstOffsetFloats, lengthFloats);
+ }
+
+ @Override
+ public int getInt(final long offsetBytes) {
+ return Integer.reverseBytes(getNativeOrderedInt(offsetBytes));
+ }
+
+ @Override
+ public void getIntArray(final long offsetBytes, final int[] dstArray, final int dstOffsetInts,
+ final int lengthInts) {
+ final long copyBytes = ((long) lengthInts) << INT_SHIFT;
+ checkValidAndBounds(offsetBytes, copyBytes);
+ CompareAndCopy.getNonNativeInts(getUnsafeObject(), getCumulativeOffset(offsetBytes), copyBytes,
+ dstArray, dstOffsetInts, lengthInts);
+ }
+
+ @Override
+ public long getLong(final long offsetBytes) {
+ return Long.reverseBytes(getNativeOrderedLong(offsetBytes));
+ }
+
+ @Override
+ public void getLongArray(final long offsetBytes, final long[] dstArray,
+ final int dstOffsetLongs, final int lengthLongs) {
+ final long copyBytes = ((long) lengthLongs) << LONG_SHIFT;
+ checkValidAndBounds(offsetBytes, copyBytes);
+ CompareAndCopy.getNonNativeLongs(getUnsafeObject(), getCumulativeOffset(offsetBytes), copyBytes,
+ dstArray, dstOffsetLongs, lengthLongs);
+ }
+
+ @Override
+ public short getShort(final long offsetBytes) {
+ return Short.reverseBytes(getNativeOrderedShort(offsetBytes));
+ }
+
+ @Override
+ public void getShortArray(final long offsetBytes, final short[] dstArray,
+ final int dstOffsetShorts, final int lengthShorts) {
+ final long copyBytes = ((long) lengthShorts) << SHORT_SHIFT;
+ checkValidAndBounds(offsetBytes, copyBytes);
+ CompareAndCopy.getNonNativeShorts(getUnsafeObject(), getCumulativeOffset(offsetBytes),
+ copyBytes, dstArray, dstOffsetShorts, lengthShorts);
+ }
+
+ //PRIMITIVE putX() and putXArray() implementations
+ @Override
+ public void putChar(final long offsetBytes, final char value) {
+ putNativeOrderedChar(offsetBytes, Character.reverseBytes(value));
+ }
+
+ @Override
+ public void putCharArray(final long offsetBytes, final char[] srcArray, final int srcOffsetChars,
+ final int lengthChars) {
+ final long copyBytes = ((long) lengthChars) << CHAR_SHIFT;
+ checkValidAndBoundsForWrite(offsetBytes, copyBytes);
+ CompareAndCopy.putNonNativeChars(srcArray, srcOffsetChars, lengthChars, copyBytes,
+ getUnsafeObject(), getCumulativeOffset(offsetBytes));
+ }
+
+ @Override
+ public void putDouble(final long offsetBytes, final double value) {
+ checkValidAndBoundsForWrite(offsetBytes, ARRAY_DOUBLE_INDEX_SCALE);
+ unsafe.putLong(getUnsafeObject(), getCumulativeOffset(offsetBytes),
+ Long.reverseBytes(Double.doubleToRawLongBits(value)));
+ }
+
+ @Override
+ public void putDoubleArray(final long offsetBytes, final double[] srcArray,
+ final int srcOffsetDoubles, final int lengthDoubles) {
+ final long copyBytes = ((long) lengthDoubles) << DOUBLE_SHIFT;
+ checkValidAndBoundsForWrite(offsetBytes, copyBytes);
+ CompareAndCopy.putNonNativeDoubles(srcArray, srcOffsetDoubles, lengthDoubles, copyBytes,
+ getUnsafeObject(), getCumulativeOffset(offsetBytes));
+ }
+
+ @Override
+ public void putFloat(final long offsetBytes, final float value) {
+ checkValidAndBoundsForWrite(offsetBytes, ARRAY_FLOAT_INDEX_SCALE);
+ unsafe.putInt(getUnsafeObject(), getCumulativeOffset(offsetBytes),
+ Integer.reverseBytes(Float.floatToRawIntBits(value)));
+ }
+
+ @Override
+ public void putFloatArray(final long offsetBytes, final float[] srcArray,
+ final int srcOffsetFloats, final int lengthFloats) {
+ final long copyBytes = ((long) lengthFloats) << FLOAT_SHIFT;
+ checkValidAndBoundsForWrite(offsetBytes, copyBytes);
+ CompareAndCopy.putNonNativeFloats(srcArray, srcOffsetFloats, lengthFloats, copyBytes,
+ getUnsafeObject(), getCumulativeOffset(offsetBytes));
+ }
+
+ @Override
+ public void putInt(final long offsetBytes, final int value) {
+ putNativeOrderedInt(offsetBytes, Integer.reverseBytes(value));
+ }
+
+ @Override
+ public void putIntArray(final long offsetBytes, final int[] srcArray, final int srcOffsetInts,
+ final int lengthInts) {
+ final long copyBytes = ((long) lengthInts) << INT_SHIFT;
+ checkValidAndBoundsForWrite(offsetBytes, copyBytes);
+ CompareAndCopy.putNonNativeInts(srcArray, srcOffsetInts, lengthInts, copyBytes,
+ getUnsafeObject(), getCumulativeOffset(offsetBytes));
+ }
+
+ @Override
+ public void putLong(final long offsetBytes, final long value) {
+ putNativeOrderedLong(offsetBytes, Long.reverseBytes(value));
+ }
+
+ @Override
+ public void putLongArray(final long offsetBytes, final long[] srcArray, final int srcOffsetLongs,
+ final int lengthLongs) {
+ final long copyBytes = ((long) lengthLongs) << LONG_SHIFT;
+ checkValidAndBoundsForWrite(offsetBytes, copyBytes);
+ CompareAndCopy.putNonNativeLongs(srcArray, srcOffsetLongs, lengthLongs, copyBytes,
+ getUnsafeObject(), getCumulativeOffset(offsetBytes));
+ }
+
+ @Override
+ public void putShort(final long offsetBytes, final short value) {
+ putNativeOrderedShort(offsetBytes, Short.reverseBytes(value));
+ }
+
+ @Override
+ public void putShortArray(final long offsetBytes, final short[] srcArray,
+ final int srcOffsetShorts, final int lengthShorts) {
+ final long copyBytes = ((long) lengthShorts) << SHORT_SHIFT;
+ checkValidAndBoundsForWrite(offsetBytes, copyBytes);
+ CompareAndCopy.putNonNativeShorts(srcArray, srcOffsetShorts, lengthShorts, copyBytes,
+ getUnsafeObject(), getCumulativeOffset(offsetBytes));
+ }
+
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/PositionalImpl.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/PositionalImpl.java
new file mode 100644
index 00000000..a18cd268
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/PositionalImpl.java
@@ -0,0 +1,145 @@
+/*
+ * 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 org.apache.datasketches.memory.Positional;
+import org.apache.datasketches.memory.BufferPositionInvariantsException;
+
+/**
+ * A new positional API. This is different from and simpler than Java Buffer positional approach.
+ * <ul><li>All based on longs instead of ints.</li>
+ * <li>Eliminated "mark". Rarely used and confusing with its silent side effects.</li>
+ * <li>The invariants are {@code 0 <= start <= position <= end <= capacity}.</li>
+ * <li>It always starts up as (0, 0, capacity, capacity).</li>
+ * <li>You set (start, position, end) in one call with
+ * {@link #setStartPositionEnd(long, long, long)}</li>
+ * <li>Position can be set directly or indirectly when using the positional get/put methods.
+ * <li>Added incrementPosition(long), which is much easier when you know the increment.</li>
+ * <li>This approach eliminated a number of methods and checks, and has no unseen side effects,
+ * e.g., mark being invalidated.</li>
+ * <li>Clearer method naming (IMHO).</li>
+ * </ul>
+ *
+ * @author Lee Rhodes
+ */
+public abstract class PositionalImpl extends ResourceImpl implements Positional {
+ private long capacity;
+ private long start = 0;
+ private long pos = 0;
+ private long end;
+
+ //Pass-through constructor
+ PositionalImpl(final long capacityBytes) {
+ super();
+ capacity = end = capacityBytes;
+ }
+
+ @Override
+ public final PositionalImpl incrementPosition(final long increment) {
+ incrementAndCheckPositionForRead(pos, increment);
+ return this;
+ }
+
+ @Override
+ public final long getEnd() {
+ return end;
+ }
+
+ @Override
+ public final long getPosition() {
+ return pos;
+ }
+
+ @Override
+ public final long getStart() {
+ return start;
+ }
+
+ @Override
+ public final long getRemaining() {
+ return end - pos;
+ }
+
+ @Override
+ public final boolean hasRemaining() {
+ return (end - pos) > 0;
+ }
+
+ @Override
+ public final PositionalImpl resetPosition() {
+ pos = start;
+ return this;
+ }
+
+ @Override
+ public final PositionalImpl setPosition(final long position) {
+ return setStartPositionEnd(start, position, end);
+ }
+
+ @Override
+ public final PositionalImpl setStartPositionEnd(final long start, final long position, final long end) {
+ checkValid();
+ checkInvariants(start, position, end, capacity);
+ this.start = start;
+ this.end = end;
+ pos = position;
+ return this;
+ }
+
+ //RESTRICTED
+
+ //used for buffer arrays and apply at runtime
+ final void incrementAndCheckPositionForRead(final long position, final long increment) {
+ checkValid();
+ final long newPos = position + increment;
+ checkInvariants(start, newPos, end, capacity);
+ pos = newPos;
+ }
+
+ //used for buffer arrays and apply at runtime
+ final void incrementAndCheckPositionForWrite(final long position, final long increment) {
+ checkNotReadOnly();
+ incrementAndCheckPositionForRead(position, increment);
+ }
+
+ /**
+ * The invariants equation is: {@code 0 <= start <= position <= end <= capacity}.
+ * If this equation is violated a <i>BufferPositionInvariantsException</i> will be thrown.
+ * @param start the lowest start position
+ * @param pos the current position
+ * @param end the highest position
+ * @param cap the capacity of the backing buffer.
+ */
+ static final void checkInvariants(final long start, final long pos, final long end, final long cap) {
+ if ((start | pos | end | cap | (pos - start) | (end - pos) | (cap - end) ) < 0L) {
+ throw new BufferPositionInvariantsException(
+ "Violation of Invariants: "
+ + "start: " + start
+ + " <= pos: " + pos
+ + " <= end: " + end
+ + " <= cap: " + cap
+ + "; (pos - start): " + (pos - start)
+ + ", (end - pos): " + (end - pos)
+ + ", (cap - end): " + (cap - end)
+ );
+ }
+ }
+
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/Prim.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/Prim.java
new file mode 100644
index 00000000..0caffb31
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/Prim.java
@@ -0,0 +1,89 @@
+/*
+ * 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.ARRAY_BOOLEAN_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_BOOLEAN_INDEX_SCALE;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_BYTE_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_BYTE_INDEX_SCALE;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_CHAR_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_CHAR_INDEX_SCALE;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_DOUBLE_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_DOUBLE_INDEX_SCALE;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_FLOAT_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_FLOAT_INDEX_SCALE;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_INT_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_INT_INDEX_SCALE;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_LONG_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_LONG_INDEX_SCALE;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_OBJECT_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_OBJECT_INDEX_SCALE;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_SHORT_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_SHORT_INDEX_SCALE;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.BOOLEAN_SHIFT;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.BYTE_SHIFT;
+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.OBJECT_SHIFT;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.SHORT_SHIFT;
+
+/**
+ * Creates easy to access association between the major Unsafe constants.
+ *
+ * @author Lee Rhodes
+ */
+//@SuppressWarnings("javadoc")
+public enum Prim {
+ BOOLEAN(ARRAY_BOOLEAN_BASE_OFFSET, ARRAY_BOOLEAN_INDEX_SCALE, BOOLEAN_SHIFT),
+ BYTE(ARRAY_BYTE_BASE_OFFSET, ARRAY_BYTE_INDEX_SCALE, BYTE_SHIFT),
+ CHAR(ARRAY_CHAR_BASE_OFFSET, ARRAY_CHAR_INDEX_SCALE, CHAR_SHIFT),
+ SHORT(ARRAY_SHORT_BASE_OFFSET, ARRAY_SHORT_INDEX_SCALE, SHORT_SHIFT),
+ INT(ARRAY_INT_BASE_OFFSET, ARRAY_INT_INDEX_SCALE, INT_SHIFT),
+ LONG(ARRAY_LONG_BASE_OFFSET, ARRAY_LONG_INDEX_SCALE, LONG_SHIFT),
+ FLOAT(ARRAY_FLOAT_BASE_OFFSET, ARRAY_FLOAT_INDEX_SCALE, FLOAT_SHIFT),
+ DOUBLE(ARRAY_DOUBLE_BASE_OFFSET, ARRAY_DOUBLE_INDEX_SCALE, DOUBLE_SHIFT),
+ OBJECT(ARRAY_OBJECT_BASE_OFFSET, ARRAY_OBJECT_INDEX_SCALE, OBJECT_SHIFT);
+
+ private final long arrBaseOff_;
+ private final long arrIdxScale_;
+ private final long sizeShift_;
+
+ private Prim(final long arrBaseOff, final long arrIdxScale, final long sizeShift) {
+ this.arrBaseOff_ = arrBaseOff;
+ this.arrIdxScale_ = arrIdxScale;
+ this.sizeShift_ = sizeShift;
+ }
+
+ public long off() {
+ return arrBaseOff_;
+ }
+
+ public long scale() {
+ return arrIdxScale_;
+ }
+
+ public long shift() {
+ return sizeShift_;
+ }
+
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/ResourceImpl.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/ResourceImpl.java
new file mode 100644
index 00000000..7f18658e
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/ResourceImpl.java
@@ -0,0 +1,558 @@
+/*
+ * 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.unsafe;
+import static org.apache.datasketches.memory.internal.Util.characterPad;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import org.apache.datasketches.memory.MemoryBoundsException;
+import org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.ReadOnlyException;
+import org.apache.datasketches.memory.Resource;
+
+/**
+ * Implements the root Resource methods plus some common static variables and check methods.
+ *
+ * @author Lee Rhodes
+ */
+//@SuppressWarnings("restriction")
+public abstract class ResourceImpl implements Resource {
+ static final String JDK;
+ static final int JDK_MAJOR; //8, 11, 17, etc
+
+ //Used to convert "type" to bytes: bytes = longs << LONG_SHIFT
+ static final int BOOLEAN_SHIFT = 0;
+ static final int BYTE_SHIFT = 0;
+ static final long SHORT_SHIFT = 1;
+ static final long CHAR_SHIFT = 1;
+ static final long INT_SHIFT = 2;
+ static final long LONG_SHIFT = 3;
+ static final long FLOAT_SHIFT = 2;
+ static final long DOUBLE_SHIFT = 3;
+
+ //class type IDs. Do not change the bit orders
+ //The lowest 3 bits are set dynamically
+ // 0000 0XXX Group 1
+ static final int WRITABLE = 0; //bit 0 = 0
+ static final int READONLY = 1; //bit 0
+ static final int REGION = 2; //bit 1
+ static final int DUPLICATE = 4; //bit 2, for Buffer only
+
+ // 000X X000 Group 2
+ static final int HEAP = 0; //bits 3,4 = 0
+ static final int DIRECT = 8; //bit 3
+ static final int MAP = 16; //bit 4, Map is effectively Direct
+
+ // 00X0 0000 Group 3 ByteOrder
+ static final int NATIVE_BO = 0; //bit 5 = 0
+ static final int NONNATIVE_BO = 32;//bit 5
+
+ // 0X00 0000 Group 4
+ static final int MEMORY = 0; //bit 6 = 0
+ static final int BUFFER = 64; //bit 6
+
+ // X000 0000 Group 5
+ static final int BYTEBUF = 128; //bit 7
+
+ /**
+ * The java line separator character as a String.
+ */
+ public static final String LS = System.getProperty("line.separator");
+
+ static final String NOT_MAPPED_FILE_RESOURCE = "This is not a memory-mapped file resource";
+ static final String THREAD_EXCEPTION_TEXT = "Attempted access outside owning thread";
+
+ //private static boolean JAVA_VERSION_WARNING_PRINTED = false;
+
+ static {
+ 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];
+ }
+
+ //set by the leaf nodes
+ long capacityBytes;
+ long cumOffsetBytes;
+ long offsetBytes;
+ int typeId;
+ Thread owner = null;
+
+ /**
+ * The root of the Memory inheritance hierarchy
+ */
+ ResourceImpl() { }
+
+ //MemoryRequestServer logic
+
+ /**
+ * User specified MemoryRequestServer. Set here and by leaf nodes.
+ */
+ MemoryRequestServer memReqSvr = null;
+
+ @Override
+ public MemoryRequestServer getMemoryRequestServer() {
+ return memReqSvr;
+ }
+
+ @Override
+ public boolean hasMemoryRequestServer() {
+ return memReqSvr != null;
+ }
+
+ @Override
+ public void setMemoryRequestServer(final MemoryRequestServer memReqSvr) { this.memReqSvr = memReqSvr; }
+
+ //***
+
+ /**
+ * 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 MemoryBoundsException} will be thrown.
+ * @param reqOff the requested offset
+ * @param reqLen the requested length
+ * @param allocSize the allocated size.
+ * @throws MemoryBoundsException if the given arguments constitute a violation
+ * of the invariants equation expressed above.
+ */
+ public static void checkBounds(final long reqOff, final long reqLen, final long allocSize) {
+ if ((reqOff | reqLen | (reqOff + reqLen) | (allocSize - (reqOff + reqLen))) < 0) {
+ throw new MemoryBoundsException(
+ "reqOffset: " + reqOff + ", reqLength: " + reqLen
+ + ", (reqOff + reqLen): " + (reqOff + reqLen) + ", allocSize: " + allocSize);
+ }
+ }
+
+ /**
+ * Checks the runtime Java Version string. Note that Java 17 and 21 is allowed only because some clients do not use the
+ * WritableMemory.allocateDirect(..) and related functions, which will not work with Java versions >= 14.
+ * The on-heap functions may work with 17 and 21, nonetheless, versions > Java 11 are not officially supported.
+ * Caveat emptor.
+ * @param jdkVer the <i>System.getProperty("java.version")</i> string of the form "p0.p1.X"
+ * @param p0 The first number group
+ * @param p1 The second number group
+ */
+ static void checkJavaVersion(final String jdkVer, final int p0, final int p1 ) {
+ final boolean ok = ((p0 == 1) && (p1 == 8)) || (p0 == 8) || (p0 == 11) || (p0 == 17 || (p0 == 21) || (p0 == 25));
+ if (!ok) { throw new IllegalArgumentException(
+ "Unsupported JDK Major Version. It must be one of 1.8, 8, 11, 17, 21, 25: " + jdkVer);
+ }
+// if (p0 > 11 && !JAVA_VERSION_WARNING_PRINTED) {
+// System.err.println(
+// "Warning: Java versions > Java 11 can only operate in restricted mode where no off-heap operations are allowed!");
+// JAVA_VERSION_WARNING_PRINTED = true;
+// }
+ }
+
+ void checkNotReadOnly() {
+ if (isReadOnly()) {
+ throw new ReadOnlyException("Cannot write to a read-only Resource.");
+ }
+ }
+
+ /**
+ * This checks that the current thread is the same as the given owner thread.
+ * @Throws IllegalStateException if it is not.
+ * @param owner the given owner thread.
+ */
+ static final void checkThread(final Thread owner) {
+ if (owner != Thread.currentThread()) {
+ throw new IllegalStateException(THREAD_EXCEPTION_TEXT);
+ }
+ }
+
+ /**
+ * @throws IllegalStateException if this Resource is AutoCloseable, and already closed, i.e., not <em>alive</em>.
+ */
+ void checkValid() {
+ if (!isAlive()) {
+ throw new IllegalStateException("this Resource is AutoCloseable, and already closed, i.e., not <em>alive</em>.");
+ }
+ }
+
+ /**
+ * Checks that this resource is still valid and throws a MemoryInvalidException if it is not.
+ * Checks that the specified range of bytes is within bounds of this resource, throws
+ * {@link MemoryBoundsException} 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
+ * @throws IllegalStateException if this resource is AutoCloseable and is no longer valid, i.e.,
+ * it has already been closed.
+ * @throws MemoryBoundsException if this resource violates the memory bounds of this resource.
+ */
+ public final void checkValidAndBounds(final long offsetBytes, final long lengthBytes) {
+ checkValid();
+ checkBounds(offsetBytes, lengthBytes, getCapacity());
+ }
+
+ /**
+ * Checks that this resource is still valid and throws a MemoryInvalidException if it is not.
+ * Checks that the specified range of bytes is within bounds of this resource, throws
+ * {@link MemoryBoundsException} if it's not: i. e. if offsetBytes < 0, or length < 0,
+ * or offsetBytes + length > {@link #getCapacity()}.
+ * Checks that this operation is a read-only operation and throws a ReadOnlyException if not.
+ * @param offsetBytes the given offset in bytes of this object
+ * @param lengthBytes the given length in bytes of this object
+ * @Throws MemoryInvalidException if this resource is AutoCloseable and is no longer valid, i.e.,
+ * it has already been closed.
+ * @Throws MemoryBoundsException if this resource violates the memory bounds of this resource.
+ * @Throws ReadOnlyException if the associated operation is not a Read-only operation.
+ */
+ final void checkValidAndBoundsForWrite(final long offsetBytes, final long lengthBytes) {
+ checkValid();
+ checkBounds(offsetBytes, lengthBytes, getCapacity());
+ if (isReadOnly()) {
+ throw new ReadOnlyException("Memory is read-only.");
+ }
+ }
+
+ @Override
+ public void close() {
+ /* Overridden by the leaf sub-classes that need AutoCloseable. */
+ }
+
+ @Override
+ public final boolean equalTo(final long thisOffsetBytes, final Resource that,
+ final long thatOffsetBytes, final long lengthBytes) {
+ if (that == null) { return false; }
+ return CompareAndCopy.equals(this, thisOffsetBytes, (ResourceImpl) that, thatOffsetBytes, lengthBytes);
+ }
+
+ @Override
+ public void force() { //overridden by Map Leaves
+ throw new UnsupportedOperationException(NOT_MAPPED_FILE_RESOURCE);
+ }
+
+ //Overridden by ByteBuffer Leaves. Used internally and for tests.
+ ByteBuffer getByteBuffer() {
+ return null;
+ }
+
+ @Override
+ public final ByteOrder getTypeByteOrder() {
+ return isNativeOrder(getTypeId()) ? Util.NATIVE_BYTE_ORDER : Util.NON_NATIVE_BYTE_ORDER;
+ }
+
+ @Override
+ public long getCapacity() {
+ checkValid();
+ return capacityBytes;
+ }
+
+ @Override
+ public long getCumulativeOffset(final long addOffsetBytes) {
+ return cumOffsetBytes + addOffsetBytes;
+ }
+
+ @Override
+ public long getRelativeOffset() {
+ return offsetBytes;
+ }
+
+ //Overridden by all leaves
+ int getTypeId() {
+ return typeId;
+ }
+
+ //Overridden by Heap and ByteBuffer leaves. Made public as getArray() in BaseWritableMemoryImpl and BaseWritableBufferImpl
+ Object getUnsafeObject() {
+ return null;
+ }
+
+ @Override
+ public boolean hasByteBuffer() {
+ return (getTypeId() & BYTEBUF) > 0;
+ }
+
+ @Override
+ public final boolean isByteOrderCompatible(final ByteOrder byteOrder) {
+ final ByteOrder typeBO = getTypeByteOrder();
+ return typeBO == ByteOrder.nativeOrder() && typeBO == byteOrder;
+ }
+
+ static final boolean isBuffer(final int typeId) {
+ return (typeId & BUFFER) > 0;
+ }
+
+ @Override
+ public boolean isCloseable() {
+ return (getTypeId() & (MAP | DIRECT)) > 0 && isAlive();
+ }
+
+ @Override
+ public final boolean isDirect() {
+ return getUnsafeObject() == null;
+ }
+
+ @Override
+ public boolean isDuplicate() {
+ return (getTypeId() & DUPLICATE) > 0;
+ }
+
+ @Override
+ public final boolean isHeap() {
+ checkValid();
+ return getUnsafeObject() != null;
+ }
+
+ @Override
+ public boolean isLoaded() { //overridden by Map Leaves
+ throw new IllegalStateException(NOT_MAPPED_FILE_RESOURCE);
+ }
+
+ @Override
+ public boolean isMapped() {
+ return (getTypeId() & MAP) > 0;
+ }
+
+ @Override
+ public boolean isMemory() {
+ return (getTypeId() & BUFFER) == 0;
+ }
+
+ static final boolean isNativeOrder(final int typeId) { //not used
+ return (typeId & NONNATIVE_BO) == 0;
+ }
+
+ @Override
+ public boolean isNonNativeOrder() {
+ return (getTypeId() & NONNATIVE_BO) > 0;
+ }
+
+ @Override
+ public final boolean isReadOnly() {
+ checkValid();
+ return (getTypeId() & READONLY) > 0;
+ }
+
+ @Override
+ public boolean isRegionView() {
+ return (getTypeId() & REGION) > 0;
+ }
+
+ @Override
+ public boolean isSameResource(final Resource that) {
+ checkValid();
+ if (that == null) { return false; }
+ final ResourceImpl that1 = (ResourceImpl) that;
+ that1.checkValid();
+ if (this == that1) { return true; }
+ return getCumulativeOffset(0) == that1.getCumulativeOffset(0)
+ && getCapacity() == that1.getCapacity()
+ && getUnsafeObject() == that1.getUnsafeObject()
+ && getByteBuffer() == that1.getByteBuffer();
+ }
+
+ //Overridden by Direct and Map leaves
+ @Override
+ public boolean isAlive() {
+ return true;
+ }
+
+ @Override
+ public void load() { //overridden by Map leaves
+ throw new IllegalStateException(NOT_MAPPED_FILE_RESOURCE);
+ }
+
+ private static String pad(final String s, final int fieldLen) {
+ return characterPad(s, fieldLen, ' ' , true);
+ }
+
+ /**
+ * 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.
+ * @throws IllegalArgumentException for an improper Java version string.
+ */
+ 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 + LS + e);
+ }
+ checkJavaVersion(jdkVer, p0, p1);
+ return new int[] {p0, p1};
+ }
+
+ //REACHABILITY FENCE
+ static void reachabilityFence(final Object obj) { }
+
+ final static int removeNnBuf(final int typeId) { return typeId & ~NONNATIVE_BO & ~BUFFER; }
+
+ final static int setReadOnlyBit(final int typeId, final boolean readOnly) {
+ return readOnly ? typeId | READONLY : typeId & ~READONLY;
+ }
+
+ /**
+ * Returns a formatted hex string of an area of this object.
+ * Used primarily for testing.
+ * @param state the ResourceImpl
+ * @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 ResourceImpl state, final String preamble, final long offsetBytes, final int lengthBytes,
+ final boolean withData) {
+ final long capacity = state.getCapacity();
+ ResourceImpl.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(0);
+ 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.getRelativeOffset()).append(LS);
+ if (ResourceImpl.isBuffer(state.typeId)) {
+ sb.append("Start : ").append(((PositionalImpl)state).getStart()).append(LS);
+ sb.append("Position : ").append(((PositionalImpl)state).getPosition()).append(LS);
+ sb.append("End : ").append(((PositionalImpl)state).getEnd()).append(LS);
+ }
+ sb.append("Capacity : ").append(capacity).append(LS);
+ sb.append("CumBaseOffset : ").append(cumBaseOffset).append(LS);
+ sb.append("MemReqSvr, hashCode : ").append(memReqStr).append(LS);
+ sb.append("is Alive : ").append(state.isAlive()).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(JDK).append(LS);
+ //Data detail
+ if (withData) {
+ sb.append("Data, bytes : 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);
+ }
+ sb.append("### END SUMMARY");
+ return sb.toString();
+ }
+
+ @Override
+ public final String toString(final String header, final long offsetBytes, final int lengthBytes,
+ final boolean withData) {
+ 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("Type Info : ").append(typeDecode(typeId)).append(LS + LS);
+ sb.append("Header Comment : ").append(header).append(LS);
+ sb.append("Call Parameters : ").append(call);
+ return toHex(this, sb.toString(), offsetBytes, lengthBytes, withData);
+ }
+
+ @Override
+ public final String toString() {
+ return toString("", 0, (int)this.getCapacity(), false);
+ }
+
+ /**
+ * Decodes the resource type. This is primarily for debugging.
+ * @param typeId the given typeId
+ * @return a human readable string.
+ */
+ static final String typeDecode(final int typeId) {
+ final StringBuilder sb = new StringBuilder();
+ final int group1 = typeId & 0x7;
+ switch (group1) { // 0000 0XXX
+ case 0 : sb.append(pad("Writable + ",32)); break;
+ case 1 : sb.append(pad("ReadOnly + ",32)); break;
+ case 2 : sb.append(pad("Writable + Region + ",32)); break;
+ case 3 : sb.append(pad("ReadOnly + Region + ",32)); break;
+ case 4 : sb.append(pad("Writable + Duplicate + ",32)); break;
+ case 5 : sb.append(pad("ReadOnly + Duplicate + ",32)); break;
+ case 6 : sb.append(pad("Writable + Region + Duplicate + ",32)); break;
+ case 7 : sb.append(pad("ReadOnly + Region + Duplicate + ",32)); break;
+ default: break;
+ }
+ final int group2 = (typeId >>> 3) & 0x3;
+ switch (group2) { // 000X X000
+ case 0 : sb.append(pad("Heap + ",15)); break;
+ case 1 : sb.append(pad("Direct + ",15)); break;
+ case 2 : sb.append(pad("Map + Direct + ",15)); break;
+ case 3 : sb.append(pad("Map + Direct + ",15)); break;
+ default: break;
+ }
+ final int group3 = (typeId >>> 5) & 0x1;
+ switch (group3) { // 00X0 0000
+ case 0 : sb.append(pad("NativeOrder + ",17)); break;
+ case 1 : sb.append(pad("NonNativeOrder + ",17)); break;
+ default: break;
+ }
+ final int group4 = (typeId >>> 6) & 0x1;
+ switch (group4) { // 0X00 0000
+ case 0 : sb.append(pad("Memory + ",9)); break;
+ case 1 : sb.append(pad("Buffer + ",9)); break;
+ default: break;
+ }
+ final int group5 = (typeId >>> 7) & 0x1;
+ switch (group5) { // X000 0000
+ case 0 : sb.append(pad("",10)); break;
+ case 1 : sb.append(pad("ByteBuffer",10)); break;
+ default: break;
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public final long xxHash64(final long offsetBytes, final long lengthBytes, final long seed) {
+ checkValid();
+ return XxHash64.hash(getUnsafeObject(), getCumulativeOffset(0) + offsetBytes, lengthBytes, seed);
+ }
+
+ @Override
+ public final long xxHash64(final long in, final long seed) {
+ return XxHash64.hash(in, seed);
+ }
+
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/StepBoolean.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/StepBoolean.java
new file mode 100644
index 00000000..45f81c35
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/StepBoolean.java
@@ -0,0 +1,72 @@
+/*
+ * 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.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+
+/**
+ * This is a step boolean function that can change its state only once.
+ *
+ * @author Lee Rhodes
+ */
+public final class StepBoolean {
+ private static final int FALSE = 0;
+ private static final int TRUE = 1;
+ private static final AtomicIntegerFieldUpdater<StepBoolean> STATE_FIELD_UPDATER =
+ AtomicIntegerFieldUpdater.newUpdater(StepBoolean.class, "state");
+
+ private final int initialState;
+ private volatile int state;
+
+ /**
+ * Defines the initial state
+ * @param initialState the given initial state
+ */
+ public StepBoolean(final boolean initialState) {
+ this.initialState = initialState ? TRUE : FALSE;
+ state = this.initialState;
+ }
+
+ /**
+ * Gets the current state.
+ * @return the current state.
+ */
+ public boolean get() {
+ return state == TRUE;
+ }
+
+ /**
+ * This changes the state of this step boolean function if it has not yet changed.
+ * @return true if this call led to the change of the state; false if the state has already been
+ * changed
+ */
+ public boolean change() {
+ final int notInitialState = initialState == TRUE ? FALSE : TRUE;
+ return STATE_FIELD_UPDATER.compareAndSet(this, initialState, notInitialState);
+ }
+
+ /**
+ * Return true if the state has changed from the initial state
+ * @return true if the state has changed from the initial state
+ */
+ public boolean hasChanged() {
+ return state != initialState;
+ }
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/UnsafeUtil.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/UnsafeUtil.java
new file mode 100644
index 00000000..515d9f6f
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/UnsafeUtil.java
@@ -0,0 +1,166 @@
+/*
+ * 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;
+
+ //not an indicator of whether compressed references are used.
+ public static final int ADDRESS_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();
+
+ 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;
+ }
+
+ private UnsafeUtil() {}
+
+ /**
+ * Gets the <i>unsafe.objectFieldOffset(..)</i> for declared fields of a class.
+ * @param c the class
+ * @param fieldName the declared field name
+ * @return the field offset in bytes.
+ */
+ @SuppressWarnings("deprecation")
+ 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 + ": " + fieldName);
+ }
+ }
+
+ /**
+ * 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);
+ }
+ }
+
+}
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/Util.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/Util.java
new file mode 100644
index 00000000..7c4f947a
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/Util.java
@@ -0,0 +1,427 @@
+/*
+ * 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 java.util.Arrays.fill;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.ByteOrder;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.Objects;
+import java.util.Random;
+
+import org.apache.datasketches.memory.Memory;
+
+/**
+ * @author Lee Rhodes
+ */
+public final class Util {
+
+ private Util() { }
+
+ /**
+ * Java line separator that is platform independent.
+ */
+ public static final String LS = System.getProperty("line.separator");
+
+ /**
+ * The static final for <i>ByteOrder.nativeOrder()</i>.
+ */
+ public static final ByteOrder NATIVE_BYTE_ORDER = ByteOrder.nativeOrder();
+
+ /**
+ * The static final for NON <i>ByteOrder.nativeOrder()</i>.
+ */
+ public static final ByteOrder NON_NATIVE_BYTE_ORDER =
+ (NATIVE_BYTE_ORDER == ByteOrder.LITTLE_ENDIAN) ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;
+
+ /**
+ * Returns the opposite byte order from the given one.
+ * @param order the given byte order
+ * @return the opposite byte order from the given one
+ */
+ public static ByteOrder otherByteOrder(final ByteOrder order) {
+ return (order == NATIVE_BYTE_ORDER) ? NON_NATIVE_BYTE_ORDER : NATIVE_BYTE_ORDER;
+ }
+
+ /**
+ * Don't use sun.misc.Unsafe#copyMemory to copy blocks of memory larger than this
+ * threshold, because internally it doesn't have safepoint polls, that may cause long
+ * "Time To Safe Point" pauses in the application. This has been fixed in JDK 9 (see
+ * https://bugs.openjdk.java.net/browse/JDK-8149596 and
+ * https://bugs.openjdk.java.net/browse/JDK-8141491), but not in JDK 8, so the Memory library
+ * should keep having this boilerplate as long as it supports Java 8.
+ *
+ * <p>A reference to this can be found in java.nio.Bits.</p>
+ */
+ public static final int UNSAFE_COPY_THRESHOLD_BYTES = 1024 * 1024;
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Searches a range of the specified array of longs for the specified value using the binary
+ * 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 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
+ * the key would be inserted into the array: the index of the first element in the range greater
+ * 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,
+ final long toLongIndex, final long key) {
+ ResourceImpl.checkBounds(fromLongIndex << 3, (toLongIndex - fromLongIndex) << 3, mem.getCapacity());
+ long low = fromLongIndex;
+ long high = toLongIndex - 1L;
+
+ while (low <= high) {
+ final long mid = (low + high) >>> 1;
+ final long midVal = mem.getLong(mid << 3);
+
+ if (midVal < key) { low = mid + 1; }
+ else if (midVal > key) { high = mid - 1; }
+ else { return mid; } // key found
+ }
+ return -(low + 1); // key not found.
+ }
+
+ /**
+ * Prepend the given string with zeros. If the given string is equal or greater than the given
+ * field length, it will be returned without modification.
+ * @param s the given string
+ * @param fieldLength desired total field length including the given string
+ * @return the given string prepended with zeros.
+ */
+ public static final String zeroPad(final String s, final int fieldLength) {
+ return characterPad(s, fieldLength, '0', false);
+ }
+
+ /**
+ * Prepend or postpend the given string with the given character to fill the given field length.
+ * If the given string is equal or greater than the given field length, it will be returned
+ * without modification.
+ * @param s the given string
+ * @param fieldLength the desired field length
+ * @param padChar the desired pad character
+ * @param postpend if true append the pacCharacters to the end of the string.
+ * @return prepended or postpended given string with the given character to fill the given field length.
+ */
+ public static final String characterPad(final String s, final int fieldLength, final char padChar, final boolean postpend) {
+ final int sLen = s.length();
+ if (sLen < fieldLength) {
+ final char[] cArr = new char[fieldLength - sLen];
+ fill(cArr, padChar);
+ final String addstr = String.valueOf(cArr);
+ return (postpend) ? s.concat(addstr) : addstr.concat(s);
+ }
+ return s;
+ }
+
+ /**
+ * Return true if all the masked bits of value are zero
+ * @param value the value to be tested
+ * @param bitMask defines the bits of interest
+ * @return true if all the masked bits of value are zero
+ */
+ public static final boolean isAllBitsClear(final long value, final long bitMask) {
+ return (~value & bitMask) == bitMask;
+ }
+
+ /**
+ * Return true if all the masked bits of value are one
+ * @param value the value to be tested
+ * @param bitMask defines the bits of interest
+ * @return true if all the masked bits of value are one
+ */
+ public static final boolean isAllBitsSet(final long value, final long bitMask) {
+ return (value & bitMask) == bitMask;
+ }
+
+ /**
+ * Return true if any the masked bits of value are zero
+ * @param value the value to be tested
+ * @param bitMask defines the bits of interest
+ * @return true if any the masked bits of value are zero
+ */
+ public static final boolean isAnyBitsClear(final long value, final long bitMask) {
+ return (~value & bitMask) != 0;
+ }
+
+ /**
+ * Return true if any the masked bits of value are one
+ * @param value the value to be tested
+ * @param bitMask defines the bits of interest
+ * @return true if any the masked bits of value are one
+ */
+ public static final boolean isAnyBitsSet(final long value, final long bitMask) {
+ return (value & bitMask) != 0;
+ }
+
+ /**
+ * Creates random valid Character Code Points (as integers). By definition, valid CodePoints
+ * are integers in the range 0 to Character.MAX_CODE_POINT, and exclude the surrogate values.
+ * This is used in unit testing and characterization testing of the UTF8 class. Because the
+ * characterization tools are in a separate package, this must remain public.
+ *
+ * @author Lee Rhodes
+ */
+ public static class RandomCodePoints {
+ private Random rand; //
+ private static final int ALL_CP = Character.MAX_CODE_POINT + 1;
+ private static final int MIN_SUR = Character.MIN_SURROGATE;
+ private static final int MAX_SUR = Character.MAX_SURROGATE;
+
+ /**
+ * @param deterministic if true, configure java.util.Random with a fixed seed.
+ */
+ public RandomCodePoints(final boolean deterministic) {
+ rand = deterministic ? new Random(0) : new Random();
+ }
+
+ /**
+ * 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>.
+ * @param cpArr the array to fill
+ */
+ public final void fillCodePointArray(final int[] cpArr) {
+ fillCodePointArray(cpArr, 0, ALL_CP);
+ }
+
+ /**
+ * 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>.
+ * @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.
+ */
+ public final void fillCodePointArray(final int[] cpArr, final int startCP, final int endCP) {
+ final int arrLen = cpArr.length;
+ final int numCP = Math.min(endCP, 0X110000) - Math.min(0, startCP);
+ int idx = 0;
+ while (idx < arrLen) {
+ final int cp = startCP + rand.nextInt(numCP);
+ if ((cp >= MIN_SUR) && (cp <= MAX_SUR)) {
+ continue;
+ }
+ cpArr[idx++] = cp;
+ }
+ }
+
+ /**
+ * 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>.
+ * @return a single valid random CodePoint.
+ */
+ public final int getCodePoint() {
+ return getCodePoint(0, ALL_CP);
+ }
+
+ /**
+ * 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>.
+ * @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.
+ */
+ public final int getCodePoint(final int startCP, final int endCP) {
+ final int numCP = Math.min(endCP, 0X110000) - Math.min(0, startCP);
+ while (true) {
+ final int cp = startCP + rand.nextInt(numCP);
+ if ((cp < MIN_SUR) || (cp > MAX_SUR)) {
+ return cp;
+ }
+ }
+ }
+ } //End class RandomCodePoints
+
+ /**
+ * Checks if given value is ≤ zero.
+ * @param value the given value
+ * @param arg a meaningful name of the actual argument.
+ * @throws IllegalArgumentException if value is ≤ 0.
+ */
+ public static final void zeroCheck(final long value, final String arg) {
+ if (value <= 0) {
+ throw new IllegalArgumentException("The argument '" + arg + "' may not be negative or zero.");
+ }
+ }
+
+ /**
+ * Checks if given value is < zero.
+ * @param value the given value
+ * @param arg a meaningful name of the actual argument.
+ * @throws IllegalArgumentException if value is < 0.
+ */
+ public static final void negativeCheck(final long value, final String arg) {
+ if (value < 0) {
+ throw new IllegalArgumentException("The argument '" + arg + "' may not be negative.");
+ }
+ }
+
+ /**
+ * Checks if given value is null.
+ * @param obj the given object
+ * @param arg a meaningful name of the actual argument.
+ * @throws IllegalArgumentException if value is null.
+ */
+ public static final void nullCheck(final Object obj, final String arg) {
+ if (obj == null) {
+ throw new IllegalArgumentException("The argument '" + arg + "' may not be null.");
+ }
+ }
+
+ //Resources NOTE: these 3 methods are duplicated in Java/ datasketches/Util
+
+ /**
+ * Gets the Path of the given resource file's shortName.
+ *
+ * <p>Note that the ClassLoader.getResource(shortName) returns a URL,
+ * which can have special characters, e.g., "%20" for spaces. This method
+ * obtains the URL, converts it to a URI, then does a uri.getPath(), which
+ * decodes any special characters in the URI path. This is required to make
+ * obtaining resources operating-system independent.</p>
+ *
+ * @param shortFileName the last name in the pathname's name sequence.
+ * @return the absolute path of the given resource file's shortName.
+ */
+ public static String getResourcePath(final String shortFileName) {
+ Objects.requireNonNull(shortFileName, "input parameter " + shortFileName + " cannot be null.");
+ try {
+ final URL url = Util.class.getClassLoader().getResource(shortFileName);
+ Objects.requireNonNull(url, "resource " + shortFileName + " could not be acquired.");
+ final URI uri = url.toURI();
+ //decodes any special characters
+ final String pathStr = uri.isAbsolute() ? Paths.get(uri).toAbsolutePath().toString() : uri.getPath();
+ return pathStr;
+ } catch (final URISyntaxException e) {
+ throw new IllegalArgumentException("Cannot find resource: " + shortFileName + LS + e);
+ }
+ }
+
+// /**
+// * Gets the file defined by the given resource file's shortFileName.
+// * @param shortFileName the last name in the pathname's name sequence.
+// * @return the file defined by the given resource file's shortFileName.
+// */
+// public static File getResourceFile(final String shortFileName) {
+// return new File(getResourcePath(shortFileName));
+// }
+
+ /**
+ * Returns a byte array of the contents of the file defined by the given resource file's
+ * shortFileName.
+ * @param shortFileName the last name in the pathname's name sequence.
+ * @return a byte array of the contents of the file defined by the given resource file's
+ * shortFileName.
+ */
+ public static byte[] getResourceBytes(final String shortFileName) {
+ try {
+ return Files.readAllBytes(Paths.get(getResourcePath(shortFileName)));
+ } catch (final IOException e) {
+ throw new IllegalArgumentException("Cannot read resource: " + shortFileName + LS + e);
+ }
+ }
+
+ public static void ensureReadOnly(Path path) {
+ if (!Files.exists(path)) {
+ throw new IllegalArgumentException("File not found.");
+ }
+
+ // Works on Windows and POSIX)
+ path.toFile().setReadOnly();
+ }
+
+ /**
+ * Windows and JAR friendly get Resource File
+ * @param resourceName the simple file name. No special characters or slashes.
+ * @return a File System File
+ */
+ public static File getResourceFile(String resourceName) {
+ Objects.requireNonNull(resourceName, "Given resourceName must not be null");
+ if (resourceName.isEmpty()) { throw new IllegalArgumentException("Given resourceName must not be empty"); }
+ // Normalize name: ClassLoaders MUST use forward slashes even on Windows
+ String normalizedName = resourceName.replace('\\', '/');
+ if (normalizedName.startsWith("/")) { normalizedName = normalizedName.substring(1); }
+
+ ClassLoader loader = Util.class.getClassLoader();
+ URL url = loader.getResource(normalizedName);
+ if (url == null) { throw new IllegalArgumentException("Resource not found: " + normalizedName); }
+
+ // If it's a real file, return it directly
+ if ("file".equals(url.getProtocol())) {
+ try {
+ URI uri = url.toURI();
+ return new File(uri); }
+ catch (URISyntaxException e) { return new File(url.getPath()); }
+ }
+
+ // If it's in a JAR, we must extract it for Memory.map() to work
+ // We use a prefix that won't collide with Windows reserved names
+ File tempFile;
+ try { tempFile = File.createTempFile("datasketches-", ".bin"); }
+ catch (IOException e1) { throw new IllegalArgumentException(e1); }
+ tempFile.deleteOnExit();
+
+ try (InputStream in = loader.getResourceAsStream(normalizedName)) {
+ if (in == null) throw new IllegalArgumentException("Could not open stream for " + normalizedName);
+
+ // Use REPLACE_EXISTING to avoid "File Already Exists" errors on Windows retries
+ Files.copy(in, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
+ } catch (IOException e) { throw new IllegalArgumentException(e); }
+
+ // Final Windows Fix: Ensure the file is actually writable if you need to setReadOnly later
+ //tempFile.setWritable(true);
+
+ return tempFile;
+ }
+
+
+}
diff --git a/datasketches-memory-java11/src/main/java/org/apache/datasketches/memory/internal/VirtualMachineMemory.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/VirtualMachineMemory.java
similarity index 100%
rename from datasketches-memory-java11/src/main/java/org/apache/datasketches/memory/internal/VirtualMachineMemory.java
rename to datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/VirtualMachineMemory.java
diff --git a/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/XxHash64.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/XxHash64.java
new file mode 100644
index 00000000..2071b116
--- /dev/null
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/XxHash64.java
@@ -0,0 +1,320 @@
+/*
+ * 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.ARRAY_BYTE_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_CHAR_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_DOUBLE_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_FLOAT_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_INT_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_LONG_BASE_OFFSET;
+import static org.apache.datasketches.memory.internal.UnsafeUtil.ARRAY_SHORT_BASE_OFFSET;
+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.unsafe;
+
+/**
+ * The XxHash is a fast, non-cryptographic, 64-bit hash function that has
+ * excellent avalanche and 2-way bit independence properties.
+ * This java version adapted the C++ version and the OpenHFT/Zero-Allocation-Hashing implementation
+ * referenced below as inspiration.
+ *
+ * <p>The C++ source repository:
+ * <a href="https://github.com/Cyan4973/xxHash">
+ * https://github.com/Cyan4973/xxHash</a>. It has a BSD 2-Clause License:
+ * <a href="http://www.opensource.org/licenses/bsd-license.php">
+ * http://www.opensource.org/licenses/bsd-license.php</a>. See LICENSE.
+ *
+ * <p>Portions of this code were adapted from
+ * <a href="https://github.com/OpenHFT/Zero-Allocation-Hashing/blob/master/src/main/java/net/openhft/hashing/XxHash.java">
+ * OpenHFT/Zero-Allocation-Hashing</a>, which has an Apache 2 license as does this site. See LICENSE.
+ *
+ * @author Lee Rhodes
+ */
+//@SuppressWarnings("restriction")
+public class XxHash64 {
+ // Unsigned, 64-bit primes
+ private static final long P1 = -7046029288634856825L;
+ private static final long P2 = -4417276706812531889L;
+ private static final long P3 = 1609587929392839161L;
+ private static final long P4 = -8796714831421723037L;
+ private static final long P5 = 2870177450012600261L;
+
+ /**
+ * Returns the 64-bit hash of the sequence of bytes in the unsafeObject specified by
+ * <i>cumOffsetBytes</i>, <i>lengthBytes</i> and a <i>seed</i>.
+ *
+ * @param unsafeObj A reference to the object parameter required by unsafe. It may be null.
+ * @param cumOffsetBytes cumulative offset in bytes of this object from the backing resource
+ * including any user 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 lengthBytes the length in bytes of the sequence to be hashed
+ * @param seed a given seed
+ * @return the 64-bit hash of the sequence of bytes in the unsafeObject specified by
+ * <i>cumOffsetBytes</i>, <i>lengthBytes</i> and a <i>seed</i>.
+ */
+ static long hash(final Object unsafeObj, long cumOffsetBytes, final long lengthBytes,
+ final long seed) {
+ long hash;
+ long remaining = lengthBytes;
+
+ if (remaining >= 32) {
+ long v1 = seed + P1 + P2;
+ long v2 = seed + P2;
+ long v3 = seed;
+ long v4 = seed - P1;
+
+ do {
+ v1 += unsafe.getLong(unsafeObj, cumOffsetBytes) * P2;
+ v1 = Long.rotateLeft(v1, 31);
+ v1 *= P1;
+
+ v2 += unsafe.getLong(unsafeObj, cumOffsetBytes + 8L) * P2;
+ v2 = Long.rotateLeft(v2, 31);
+ v2 *= P1;
+
+ v3 += unsafe.getLong(unsafeObj, cumOffsetBytes + 16L) * P2;
+ v3 = Long.rotateLeft(v3, 31);
+ v3 *= P1;
+
+ v4 += unsafe.getLong(unsafeObj, cumOffsetBytes + 24L) * P2;
+ v4 = Long.rotateLeft(v4, 31);
+ v4 *= P1;
+
+ cumOffsetBytes += 32;
+ remaining -= 32;
+ } while (remaining >= 32);
+
+ hash = Long.rotateLeft(v1, 1)
+ + Long.rotateLeft(v2, 7)
+ + Long.rotateLeft(v3, 12)
+ + Long.rotateLeft(v4, 18);
+
+ v1 *= P2;
+ v1 = Long.rotateLeft(v1, 31);
+ v1 *= P1;
+ hash ^= v1;
+ hash = (hash * P1) + P4;
+
+ v2 *= P2;
+ v2 = Long.rotateLeft(v2, 31);
+ v2 *= P1;
+ hash ^= v2;
+ hash = (hash * P1) + P4;
+
+ v3 *= P2;
+ v3 = Long.rotateLeft(v3, 31);
+ v3 *= P1;
+ hash ^= v3;
+ hash = (hash * P1) + P4;
+
+ v4 *= P2;
+ v4 = Long.rotateLeft(v4, 31);
+ v4 *= P1;
+ hash ^= v4;
+ hash = (hash * P1) + P4;
+ } //end remaining >= 32
+ else {
+ hash = seed + P5;
+ }
+
+ hash += lengthBytes;
+
+ while (remaining >= 8) {
+ long k1 = unsafe.getLong(unsafeObj, cumOffsetBytes);
+ k1 *= P2;
+ k1 = Long.rotateLeft(k1, 31);
+ k1 *= P1;
+ hash ^= k1;
+ hash = (Long.rotateLeft(hash, 27) * P1) + P4;
+ cumOffsetBytes += 8;
+ remaining -= 8;
+ }
+
+ if (remaining >= 4) { //treat as unsigned ints
+ hash ^= (unsafe.getInt(unsafeObj, cumOffsetBytes) & 0XFFFF_FFFFL) * P1;
+ hash = (Long.rotateLeft(hash, 23) * P2) + P3;
+ cumOffsetBytes += 4;
+ remaining -= 4;
+ }
+
+ while (remaining != 0) { //treat as unsigned bytes
+ hash ^= (unsafe.getByte(unsafeObj, cumOffsetBytes) & 0XFFL) * P5;
+ hash = Long.rotateLeft(hash, 11) * P1;
+ --remaining;
+ ++cumOffsetBytes;
+ }
+
+ return finalize(hash);
+ }
+
+ /**
+ * 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.
+ */
+ public static long hash(final long in, final long seed) {
+ long hash = seed + P5;
+ hash += 8;
+ long k1 = in;
+ k1 *= P2;
+ k1 = Long.rotateLeft(k1, 31);
+ k1 *= P1;
+ hash ^= k1;
+ hash = (Long.rotateLeft(hash, 27) * P1) + P4;
+ return finalize(hash);
+ }
+
+ private static long finalize(long hash) {
+ hash ^= hash >>> 33;
+ hash *= P2;
+ hash ^= hash >>> 29;
+ hash *= P3;
+ hash ^= hash >>> 32;
+ return hash;
+ }
+
+ /**
+ * Hash the given arr starting at the given offset and continuing for the given length using the
+ * given seed.
+ * @param arr the given array
+ * @param offsetBytes starting at this offset
+ * @param lengthBytes continuing for this length
+ * @param seed the given seed
+ * @return the hash
+ */
+ public static long hashBytes(final byte[] arr, final long offsetBytes,
+ final long lengthBytes, final long seed) {
+ return hash(arr, ARRAY_BYTE_BASE_OFFSET + offsetBytes, lengthBytes, seed);
+ }
+
+ /**
+ * Hash the given arr starting at the given offset and continuing for the given length using the
+ * given seed.
+ * @param arr the given array
+ * @param offsetShorts starting at this offset
+ * @param lengthShorts continuing for this length
+ * @param seed the given seed
+ * @return the hash
+ */
+ public static long hashShorts(final short[] arr, final long offsetShorts,
+ final long lengthShorts, final long seed) {
+ return hash(arr, ARRAY_SHORT_BASE_OFFSET + (offsetShorts << SHORT_SHIFT),
+ lengthShorts << SHORT_SHIFT, seed);
+ }
+
+ /**
+ * Hash the given arr starting at the given offset and continuing for the given length using the
+ * given seed.
+ * @param arr the given array
+ * @param offsetChars starting at this offset
+ * @param lengthChars continuing for this length
+ * @param seed the given seed
+ * @return the hash
+ */
+ public static long hashChars(final char[] arr, final long offsetChars,
+ final long lengthChars, final long seed) {
+ return hash(arr, ARRAY_CHAR_BASE_OFFSET + (offsetChars << CHAR_SHIFT),
+ lengthChars << CHAR_SHIFT, seed);
+ }
+
+ /**
+ * Hash the given arr starting at the given offset and continuing for the given length using the
+ * given seed.
+ * @param arr the given array
+ * @param offsetInts starting at this offset
+ * @param lengthInts continuing for this length
+ * @param seed the given seed
+ * @return the hash
+ */
+ public static long hashInts(final int[] arr, final long offsetInts,
+ final long lengthInts, final long seed) {
+ return hash(arr, ARRAY_INT_BASE_OFFSET + (offsetInts << INT_SHIFT),
+ lengthInts << INT_SHIFT, seed);
+ }
+
+ /**
+ * Hash the given arr starting at the given offset and continuing for the given length using the
+ * given seed.
+ * @param arr the given array
+ * @param offsetLongs starting at this offset
+ * @param lengthLongs continuing for this length
+ * @param seed the given seed
+ * @return the hash
+ */
+ public static long hashLongs(final long[] arr, final long offsetLongs,
+ final long lengthLongs, final long seed) {
+ return hash(arr, ARRAY_LONG_BASE_OFFSET + (offsetLongs << LONG_SHIFT),
+ lengthLongs << LONG_SHIFT, seed);
+ }
+
+ /**
+ * Hash the given arr starting at the given offset and continuing for the given length using the
+ * given seed.
+ * @param arr the given array
+ * @param offsetFloats starting at this offset
+ * @param lengthFloats continuing for this length
+ * @param seed the given seed
+ * @return the hash
+ */
+ public static long hashFloats(final float[] arr, final long offsetFloats,
+ final long lengthFloats, final long seed) {
+ return hash(arr, ARRAY_FLOAT_BASE_OFFSET + (offsetFloats << FLOAT_SHIFT),
+ lengthFloats << FLOAT_SHIFT, seed);
+ }
+
+ /**
+ * Hash the given arr starting at the given offset and continuing for the given length using the
+ * given seed.
+ * @param arr the given array
+ * @param offsetDoubles starting at this offset
+ * @param lengthDoubles continuing for this length
+ * @param seed the given seed
+ * @return the hash
+ */
+ public static long hashDoubles(final double[] arr, final long offsetDoubles,
+ final long lengthDoubles, final long seed) {
+ return hash(arr, ARRAY_DOUBLE_BASE_OFFSET + (offsetDoubles << DOUBLE_SHIFT),
+ lengthDoubles << DOUBLE_SHIFT, seed);
+ }
+
+ /**
+ * Hash the given arr starting at the given offset and continuing for the given length using the
+ * given seed.
+ * @param str the given string
+ * @param offsetChars starting at this offset
+ * @param lengthChars continuing for this length
+ * @param seed the given seed
+ * @return the hash
+ */
+ public static long hashString(final String str, final long offsetChars,
+ final long lengthChars, final long seed) {
+ return hashChars(str.toCharArray(), offsetChars, lengthChars, seed);
+ }
+
+}
+
diff --git a/datasketches-memory-java11/src/test/java/org/apache/datasketches/memory/internal/VirtualMachineMemoryTest.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/package-info.java
similarity index 71%
copy from datasketches-memory-java11/src/test/java/org/apache/datasketches/memory/internal/VirtualMachineMemoryTest.java
copy to datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/package-info.java
index 2793b30b..2a3bb7a2 100644
--- a/datasketches-memory-java11/src/test/java/org/apache/datasketches/memory/internal/VirtualMachineMemoryTest.java
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/internal/package-info.java
@@ -18,16 +18,3 @@
*/
package org.apache.datasketches.memory.internal;
-
-import org.testng.annotations.Test;
-
-@SuppressWarnings({"unused"})
-public class VirtualMachineMemoryTest {
-
- @Test
- public void inertPageAlignment() {
- boolean result = VirtualMachineMemory.getIsPageAligned();
- //System.out.println("VM page alignment:" + result);
- assert (true); //no exception was thrown
- }
-}
diff --git a/datasketches-memory-java11/src/test/java/org/apache/datasketches/memory/internal/VirtualMachineMemoryTest.java b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/package-info.java
similarity index 67%
copy from datasketches-memory-java11/src/test/java/org/apache/datasketches/memory/internal/VirtualMachineMemoryTest.java
copy to datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/package-info.java
index 2793b30b..769cee00 100644
--- a/datasketches-memory-java11/src/test/java/org/apache/datasketches/memory/internal/VirtualMachineMemoryTest.java
+++ b/datasketches-memory-java21/src/main/java/org/apache/datasketches/memory/package-info.java
@@ -17,17 +17,4 @@
* under the License.
*/
-package org.apache.datasketches.memory.internal;
-
-import org.testng.annotations.Test;
-
-@SuppressWarnings({"unused"})
-public class VirtualMachineMemoryTest {
-
- @Test
- public void inertPageAlignment() {
- boolean result = VirtualMachineMemory.getIsPageAligned();
- //System.out.println("VM page alignment:" + result);
- assert (true); //no exception was thrown
- }
-}
+package org.apache.datasketches.memory;
diff --git a/datasketches-memory-java21/src/main/javadoc/overview.html b/datasketches-memory-java21/src/main/javadoc/overview.html
new file mode 100644
index 00000000..86ef2bb5
--- /dev/null
+++ b/datasketches-memory-java21/src/main/javadoc/overview.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+ 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.
+-->
+
+<html>
+<head>
+</head>
+<body>
+<h2>Memory</h2>
+<h3>Overview</h3>
+
+<p><b><a href="org/apache/datasketches/memory/package-summary.html">Memory Package Overview</a></b></p>
+
+<p>Note: If the requirements or promises of any method's contract are not fulfilled (that is, if there is a bug in either the method or its caller), then an unchecked exception will be thrown. The precise type of such an unchecked exception does not form part of any method's contract.</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/datasketches-memory-java21/src/test/java/org/apache/datasketches/memory/internal/AllocateDirectMemoryTest.java b/datasketches-memory-java21/src/test/java/org/apache/datasketches/memory/internal/AllocateDirectMemoryTest.java
new file mode 100644
index 00000000..a18ce3bb
--- /dev/null
+++ b/datasketches-memory-java21/src/test/java/org/apache/datasketches/memory/internal/AllocateDirectMemoryTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.Resource;
+import org.apache.datasketches.memory.WritableMemory;
+import org.testng.annotations.Test;
+
+public class AllocateDirectMemoryTest {
+
+ @Test(expectedExceptions = IllegalStateException.class)
+ public void simpleAllocateDirect() {
+ int longs = 32;
+ try (WritableMemory wMem = WritableMemory.allocateDirect(longs << 3)) {
+ for (int i = 0; i < longs; i++) {
+ wMem.putLong(i << 3, i);
+ assertEquals(wMem.getLong(i << 3), i);
+ }
+ //inside the TWR block the memory should be valid
+ ((ResourceImpl)wMem).checkValid();
+ wMem.close(); //explicit close
+ } //normal TWR close will not throw if already closed
+ }
+
+ @Test
+ public void checkDefaultMemoryRequestServer() {
+ int longs1 = 32;
+ int bytes1 = longs1 << 3;
+ WritableMemory origWmem = WritableMemory.allocateDirect(bytes1);
+ for (int i = 0; i < longs1; i++) { //puts data in origWmem
+ origWmem.putLong(i << 3, i);
+ assertEquals(origWmem.getLong(i << 3), i);
+ }
+ println(origWmem.toString("Test", 0, 32 * 8, true));
+
+ int longs2 = 64;
+ int bytes2 = longs2 << 3;
+ origWmem.setMemoryRequestServer(Resource.defaultMemReqSvr);
+ MemoryRequestServer myMemReqSvr = origWmem.getMemoryRequestServer();
+
+ WritableMemory newWmem = myMemReqSvr.request(origWmem, bytes2);
+ assertTrue(newWmem.isHeap()); //on heap by default
+ for (int i = 0; i < longs2; i++) {
+ newWmem.putLong(i << 3, i);
+ assertEquals(newWmem.getLong(i << 3), i);
+ }
+ if (origWmem.isCloseable()) {
+ myMemReqSvr.requestClose(origWmem, newWmem);
+ }
+ }
+
+ @Test
+ public void checkNonNativeDirect() {
+ try (WritableMemory wmem = WritableMemory.allocateDirect(128, Util.NON_NATIVE_BYTE_ORDER, null)) {
+ wmem.putChar(0, (char) 1);
+ assertEquals(wmem.getByte(1), (byte) 1);
+ }
+ }
+
+ @Test
+ public void checkExplicitClose() {
+ final long cap = 128;
+ WritableMemory wMem = WritableMemory.allocateDirect(cap);
+ assertTrue(wMem.isAlive());
+ wMem.close();
+ assertFalse(wMem.isAlive());
+ }
+
+ @Test
+ public void printlnTest() {
+ println("PRINTING: " + this.getClass().getName());
+ }
+
+ /**
+ * @param s value to print
+ */
+ static void println(String s) {
+ //System.out.println(s); //disable here
+ }
+}
diff --git a/datasketches-memory-java21/src/test/java/org/apache/datasketches/memory/internal/Buffer2Test.java b/datasketches-memory-java21/src/test/java/org/apache/datasketches/memory/internal/Buffer2Test.java
new file mode 100644
index 00000000..a926f165
--- /dev/null
+++ b/datasketches-memory-java21/src/test/java/org/apache/datasketches/memory/internal/Buffer2Test.java
@@ -0,0 +1,440 @@
+/*
+ * 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.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import org.apache.datasketches.memory.Buffer;
+import org.apache.datasketches.memory.DefaultMemoryRequestServer;
+import org.apache.datasketches.memory.Memory;
+import org.apache.datasketches.memory.ReadOnlyException;
+import org.apache.datasketches.memory.WritableBuffer;
+import org.apache.datasketches.memory.WritableMemory;
+import org.testng.annotations.Test;
+
+public class Buffer2Test {
+
+ @Test
+ public void testWrapHeapByteBuf() {
+ ByteBuffer bb = ByteBuffer.allocate(64).order(ByteOrder.nativeOrder());
+
+ Byte b = 0;
+ while (bb.hasRemaining()) {
+ bb.put(b);
+ b++;
+ }
+ bb.position(0);
+
+ Buffer buffer = Buffer.wrap(bb.asReadOnlyBuffer().order(ByteOrder.nativeOrder()));
+ while (buffer.hasRemaining()) {
+ byte a1 = bb.get();
+ byte b1 = buffer.getByte();
+ assertEquals(a1, b1);
+ }
+
+ assertEquals(true, buffer.isHeap());
+ assertEquals(true, buffer.hasByteBuffer());
+ }
+
+ @Test
+ public void testWrapDirectBB() {
+ ByteBuffer bb = ByteBuffer.allocateDirect(64).order(ByteOrder.nativeOrder());
+
+ Byte b = 0;
+ while (bb.hasRemaining()) {
+ bb.put(b);
+ b++;
+ }
+ bb.position(0);
+
+ Buffer buffer = Buffer.wrap(bb);
+ while (buffer.hasRemaining()) {
+ assertEquals(bb.get(), buffer.getByte());
+ }
+
+ assertEquals(false, buffer.isHeap());
+ assertEquals(true, buffer.hasByteBuffer());
+ }
+
+ @Test
+ public void testWrapByteArray() {
+ byte[] byteArray = new byte[64];
+
+ for (byte i = 0; i < 64; i++) {
+ byteArray[i] = i;
+ }
+
+ Buffer buffer = Memory.wrap(byteArray).asBuffer();
+ int i = 0;
+ while (buffer.hasRemaining()) {
+ assertEquals(byteArray[i++], buffer.getByte());
+ }
+
+ buffer.setPosition(0);
+ byte[] copyByteArray = new byte[64];
+ buffer.getByteArray(copyByteArray, 0, 64);
+ assertEquals(byteArray, copyByteArray);
+
+ assertEquals(true, buffer.isHeap());
+ assertEquals(false, buffer.hasByteBuffer());
+ }
+
+ @Test
+ public void testWrapCharArray() {
+ char[] charArray = new char[64];
+
+ for (char i = 0; i < 64; i++) {
+ charArray[i] = i;
+ }
+
+ Buffer buffer = Memory.wrap(charArray).asBuffer();
+ int i = 0;
+ while (buffer.hasRemaining()) {
+ assertEquals(charArray[i++], buffer.getChar());
+ }
+
+ buffer.setPosition(0);
+ char[] copyCharArray = new char[64];
+ buffer.getCharArray(copyCharArray, 0, 64);
+ assertEquals(charArray, copyCharArray);
+ }
+
+ @Test
+ public void testWrapShortArray() {
+ short[] shortArray = new short[64];
+
+ for (short i = 0; i < 64; i++) {
+ shortArray[i] = i;
+ }
+
+ Buffer buffer = Memory.wrap(shortArray).asBuffer();
+ int i = 0;
+ while (buffer.hasRemaining()) {
+ assertEquals(shortArray[i++], buffer.getShort());
+ }
+
+ buffer.setPosition(0);
+ short[] copyShortArray = new short[64];
+ buffer.getShortArray(copyShortArray, 0, 64);
+ assertEquals(shortArray, copyShortArray);
+ }
+
+ @Test
+ public void testWrapIntArray() {
+ int[] intArray = new int[64];
+
+ for (int i = 0; i < 64; i++) {
+ intArray[i] = i;
+ }
+
+ Buffer buffer = Memory.wrap(intArray).asBuffer();
+ int i = 0;
+ while (buffer.hasRemaining()) {
+ assertEquals(intArray[i++], buffer.getInt());
+ }
+
+ buffer.setPosition(0);
+ int[] copyIntArray = new int[64];
+ buffer.getIntArray(copyIntArray, 0, 64);
+ assertEquals(intArray, copyIntArray);
+ }
+
+ @Test
+ public void testWrapLongArray() {
+ long[] longArray = new long[64];
+
+ for (int i = 0; i < 64; i++) {
+ longArray[i] = i;
+ }
+
+ Buffer buffer = Memory.wrap(longArray).asBuffer();
+ int i = 0;
+ while (buffer.hasRemaining()) {
+ assertEquals(longArray[i++], buffer.getLong());
+ }
+
+ buffer.setPosition(0);
+ long[] copyLongArray = new long[64];
+ buffer.getLongArray(copyLongArray, 0, 64);
+ assertEquals(longArray, copyLongArray);
+ }
+
+ @Test
+ public void testWrapFloatArray() {
+ float[] floatArray = new float[64];
+
+ for (int i = 0; i < 64; i++) {
+ floatArray[i] = i;
+ }
+
+ Buffer buffer = Memory.wrap(floatArray).asBuffer();
+ int i = 0;
+ while (buffer.hasRemaining()) {
+ assertEquals(floatArray[i++], buffer.getFloat());
+ }
+
+ buffer.setPosition(0);
+ float[] copyFloatArray = new float[64];
+ buffer.getFloatArray(copyFloatArray, 0, 64);
+ assertEquals(floatArray, copyFloatArray);
+ }
+
+ @Test
+ public void testWrapDoubleArray() {
+ double[] doubleArray = new double[64];
+
+ for (int i = 0; i < 64; i++) {
+ doubleArray[i] = i;
+ }
+
+ Buffer buffer = Memory.wrap(doubleArray).asBuffer();
+ int i = 0;
+ while (buffer.hasRemaining()) {
+ assertEquals(doubleArray[i++], buffer.getDouble());
+ }
+
+ buffer.setPosition(0);
+ double[] copyDoubleArray = new double[64];
+ buffer.getDoubleArray(copyDoubleArray, 0, 64);
+ assertEquals(doubleArray, copyDoubleArray);
+ }
+
+ @Test
+ public void testByteBufferPositionPreservation() {
+ ByteBuffer bb = ByteBuffer.allocate(64).order(ByteOrder.nativeOrder());
+
+ Byte b = 0;
+ while (bb.hasRemaining()) {
+ bb.put(b);
+ b++;
+ }
+ bb.position(10);
+
+ Buffer buffer = Buffer.wrap(bb);
+ while (buffer.hasRemaining()) {
+ assertEquals(bb.get(), buffer.getByte());
+ }
+ }
+
+ @Test
+ public void testGetAndHasRemaining() {
+ ByteBuffer bb = ByteBuffer.allocate(64).order(ByteOrder.nativeOrder());
+
+ Byte b = 0;
+ while (bb.hasRemaining()) {
+ bb.put(b);
+ b++;
+ }
+ bb.position(10);
+
+ Buffer buffer = Buffer.wrap(bb);
+ assertEquals(bb.hasRemaining(), buffer.hasRemaining());
+ assertEquals(bb.remaining(), buffer.getRemaining());
+ }
+
+ @Test
+ public void testGetSetIncResetPosition() {
+ ByteBuffer bb = ByteBuffer.allocate(64).order(ByteOrder.nativeOrder());
+
+ Byte b = 0;
+ while (bb.hasRemaining()) {
+ bb.put(b);
+ b++;
+ }
+ bb.position(10);
+
+ Buffer buffer = Buffer.wrap(bb);
+ assertEquals(bb.position(), buffer.getPosition());
+ assertEquals(30, buffer.setPosition(30).getPosition());
+ assertEquals(40, buffer.incrementPosition(10).getPosition());
+ assertEquals(0, buffer.resetPosition().getPosition());
+ }
+
+ @Test
+ public void testByteBufferSlice() {
+ ByteBuffer bb = ByteBuffer.allocate(64).order(ByteOrder.nativeOrder());
+
+ Byte b = 0;
+ while (bb.hasRemaining()) {
+ bb.put(b);
+ b++;
+ }
+ bb.position(10);
+
+ Buffer buffer = Buffer.wrap(bb.slice().order(ByteOrder.nativeOrder()));
+ while (buffer.hasRemaining()) {
+ assertEquals(bb.get(), buffer.getByte());
+ }
+
+ assertEquals(bb.position(), buffer.getPosition() + 10);
+ assertEquals(30, buffer.setPosition(30).getPosition());
+ assertEquals(40, buffer.incrementPosition(10).getPosition());
+ assertEquals(0, buffer.resetPosition().getPosition());
+ }
+
+ @Test
+ public void testDuplicateAndRegion() {
+ ByteBuffer bb = ByteBuffer.allocate(64).order(ByteOrder.nativeOrder());
+
+ Byte b = 0;
+ while (bb.hasRemaining()) {
+ bb.put(b);
+ b++;
+ }
+ bb.position(10);
+
+ Buffer buffer = Buffer.wrap(bb.slice().order(ByteOrder.nativeOrder())); //slice size = 54
+ buffer.setPosition(30);//remaining = 24
+ Buffer dupBuffer = buffer.duplicate(); //all 54
+ Buffer regionBuffer = buffer.region(); //24
+
+ assertEquals(dupBuffer.getStart(), buffer.getStart());
+ assertEquals(regionBuffer.getStart(), buffer.getStart());
+ assertEquals(dupBuffer.getEnd(), buffer.getEnd());
+ assertEquals(regionBuffer.getEnd(), buffer.getRemaining());
+ assertEquals(dupBuffer.getPosition(), buffer.getPosition());
+ assertEquals(regionBuffer.getPosition(), 0);
+ assertEquals(dupBuffer.getCapacity(), buffer.getCapacity());
+ assertEquals(regionBuffer.getCapacity(), buffer.getCapacity() - 30);
+ }
... 8036 lines suppressed ...
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datasketches.apache.org
For additional commands, e-mail: commits-help@datasketches.apache.org