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 2022/10/27 00:40:06 UTC

[datasketches-memory] 01/01: Added the Java17 classes and tests and they compile and run.

This is an automated email from the ASF dual-hosted git repository.

leerho pushed a commit to branch refactorInterfaces17
in repository https://gitbox.apache.org/repos/asf/datasketches-memory.git

commit 688ebe3f5e14efd950e9621a56ce0dc0e291e68d
Author: Lee Rhodes <le...@users.noreply.github.com>
AuthorDate: Wed Oct 26 17:39:59 2022 -0700

    Added the Java17 classes and tests and they compile and run.
    
    However, they are not refactored like the Java8 classes.
    
    Also note that with the current code as written, the
    jdk.incubator.foreign dependency must be transitive.
---
 src/main/java17/module-info.java                   |   25 +
 .../org/apache/datasketches/memory/BaseBuffer.java |  136 +++
 .../org/apache/datasketches/memory/BaseState.java  |  301 ++++++
 .../org/apache/datasketches/memory/Buffer.java     |  379 +++++++
 .../memory/DefaultMemoryRequestServer.java         |   92 ++
 .../org/apache/datasketches/memory/Memory.java     |  437 ++++++++
 .../datasketches/memory/MemoryRequestServer.java   |   50 +
 .../apache/datasketches/memory/MurmurHash3.java    |  169 +++
 .../apache/datasketches/memory/WritableBuffer.java |  383 +++++++
 .../apache/datasketches/memory/WritableMemory.java |  560 ++++++++++
 .../org/apache/datasketches/memory/XxHash.java     |  177 ++++
 .../memory/internal/BaseBufferImpl.java            |  199 ++++
 .../memory/internal/BaseStateImpl.java             |  475 +++++++++
 .../memory/internal/BaseWritableBufferImpl.java    |  333 ++++++
 .../memory/internal/BaseWritableMemoryImpl.java    |  372 +++++++
 .../memory/internal/CompareAndCopy.java            |   69 ++
 .../memory/internal/MurmurHash3v3.java             |  386 +++++++
 .../memory/internal/NativeWritableBufferImpl.java  |  321 ++++++
 .../memory/internal/NativeWritableMemoryImpl.java  |  225 ++++
 .../internal/NonNativeWritableBufferImpl.java      |  364 +++++++
 .../internal/NonNativeWritableMemoryImpl.java      |  261 +++++
 .../apache/datasketches/memory/internal/Util.java  |  132 +++
 .../datasketches/memory/internal/XxHash64.java     |  311 ++++++
 .../datasketches/memory/internal/package-info.java |   20 +
 .../apache/datasketches/memory/package-info.java   |  103 ++
 .../internal/AllocateDirectMapMemoryTest.java      |  138 +++
 .../memory/internal/AllocateDirectMemoryTest.java  |  113 ++
 .../AllocateDirectWritableMapMemoryTest.java       |  240 +++++
 .../memory/internal/BaseBufferTest.java            |   92 ++
 .../memory/internal/BaseStateTest.java             |  138 +++
 .../datasketches/memory/internal/Buffer2Test.java  |  434 ++++++++
 .../memory/internal/BufferBoundaryCheckTest.java   |  185 ++++
 .../memory/internal/BufferInvariantsTest.java      |  345 +++++++
 .../memory/internal/BufferReadWriteSafetyTest.java |  180 ++++
 .../datasketches/memory/internal/BufferTest.java   |  327 ++++++
 .../memory/internal/CommonBufferTest.java          |  430 ++++++++
 .../memory/internal/CommonMemoryTest.java          |  375 +++++++
 .../memory/internal/CopyMemoryOverlapTest.java     |  187 ++++
 .../memory/internal/CopyMemoryTest.java            |  182 ++++
 .../memory/internal/DruidIssue11544Test.java       |  101 ++
 .../internal/ExampleMemoryRequestServerTest.java   |  138 +++
 .../memory/internal/IgnoredArrayOverflowTest.java  |  110 ++
 .../datasketches/memory/internal/LeafImplTest.java |  259 +++++
 .../memory/internal/MemoryBoundaryCheckTest.java   |  185 ++++
 .../memory/internal/MemoryReadWriteSafetyTest.java |  216 ++++
 .../datasketches/memory/internal/MemoryTest.java   |  461 +++++++++
 .../memory/internal/MemoryWriteToTest.java         |  101 ++
 .../memory/internal/MurmurHash3v3Test.java         |  421 ++++++++
 .../internal/NativeWritableBufferImplTest.java     |  559 ++++++++++
 .../internal/NativeWritableMemoryImplTest.java     |  679 ++++++++++++
 .../internal/NonNativeWritableBufferImplTest.java  |  279 +++++
 .../internal/NonNativeWritableMemoryImplTest.java  |  176 ++++
 .../memory/internal/SpecificLeafTest.java          |  202 ++++
 .../datasketches/memory/internal/UtilTest.java     |  107 ++
 .../memory/internal/WritableDirectCopyTest.java    |  253 +++++
 .../memory/internal/WritableMemoryTest.java        |  184 ++++
 .../memory/internal/XxHash64LoopingTest.java       | 1082 ++++++++++++++++++++
 .../datasketches/memory/internal/XxHash64Test.java |  179 ++++
 .../memory/internal/ZeroCapacityTest.java          |   76 ++
 59 files changed, 15414 insertions(+)

diff --git a/src/main/java17/module-info.java b/src/main/java17/module-info.java
new file mode 100644
index 0000000..125759f
--- /dev/null
+++ b/src/main/java17/module-info.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+module org.apache.datasketches.memory {
+  requires java.base;
+  requires java.logging;
+  requires jdk.unsupported;
+  requires transitive jdk.incubator.foreign;
+  exports org.apache.datasketches.memory;
+}
\ No newline at end of file
diff --git a/src/main/java17/org/apache/datasketches/memory/BaseBuffer.java b/src/main/java17/org/apache/datasketches/memory/BaseBuffer.java
new file mode 100644
index 0000000..9cd824e
--- /dev/null
+++ b/src/main/java17/org/apache/datasketches/memory/BaseBuffer.java
@@ -0,0 +1,136 @@
+/*
+ * 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., mark being invalidated.</li>
+ * <li>Clearer method naming (IMHO).</li>
+ * </ul>
+ * @author Lee Rhodes
+ */
+public interface BaseBuffer extends BaseState {
+
+  /**
+   * Increments the current position by the given increment.
+   * Asserts that the resource is valid and that the positional invariants are not violated,
+   * otherwise, if asserts are enabled throws an {@link AssertionError}.
+   * @param increment the given increment
+   * @return BaseBuffer
+   */
+  BaseBuffer incrementPosition(long increment);
+
+  /**
+   * Increments the current position by the given increment.
+   * Checks that the resource is valid and that the positional invariants are not violated,
+   * otherwise throws an {@link IllegalArgumentException}.
+   * @param increment the given increment
+   * @return BaseBuffer
+   */
+  BaseBuffer incrementAndCheckPosition(final 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 BaseBuffer
+   */
+  BaseBuffer resetPosition();
+
+  /**
+   * Sets the current position.
+   * Asserts that the positional invariants are not violated,
+   * otherwise, if asserts are enabled throws an {@link AssertionError}.
+   * @param position the given current position.
+   * @return BaseBuffer
+   */
+  BaseBuffer setPosition(long position);
+
+  /**
+   * Sets the current position.
+   * Checks that the positional invariants are not violated,
+   * otherwise, throws an {@link IllegalArgumentException}.
+   * @param position the given current position.
+   * @return BaseBuffer
+   */
+  BaseBuffer setAndCheckPosition(long position);
+
+  /**
+   * Sets start position, current position, and end position.
+   * Asserts that the positional invariants are not violated,
+   * otherwise, if asserts are enabled throws an {@link AssertionError}.
+   * @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 BaseBuffer
+   */
+  BaseBuffer setStartPositionEnd(long start, long position, long end);
+
+  /**
+   * Sets start position, current position, and end position.
+   * Checks that the positional invariants are not violated,
+   * otherwise, throws an {@link IllegalArgumentException}.
+   * @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 BaseBuffer
+   */
+  BaseBuffer setAndCheckStartPositionEnd(long start, long position, long end);
+
+}
diff --git a/src/main/java17/org/apache/datasketches/memory/BaseState.java b/src/main/java17/org/apache/datasketches/memory/BaseState.java
new file mode 100644
index 0000000..0eca005
--- /dev/null
+++ b/src/main/java17/org/apache/datasketches/memory/BaseState.java
@@ -0,0 +1,301 @@
+/*
+ * 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 jdk.incubator.foreign.MemorySegment;
+import jdk.incubator.foreign.ResourceScope;
+
+/**
+ * Keeps key configuration state for Memory and Buffer plus some common static variables
+ * and check methods.
+ *
+ * @author Lee Rhodes
+ */
+public interface BaseState {
+
+  /**
+   * The java line separator character as a String.
+   */
+  static final String LS = System.getProperty("line.separator");
+
+  static final ByteOrder NATIVE_BYTE_ORDER = ByteOrder.nativeOrder();
+  static final ByteOrder NON_NATIVE_BYTE_ORDER =
+      (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;
+
+  /**
+   * Currently used only for test, hold for possible future use
+   */
+  static final MemoryRequestServer defaultMemReqSvr = new DefaultMemoryRequestServer();
+
+  /**
+   * Returns a ByteBuffer view of this Memory object with the given ByteOrder.
+   * Some of the properties of the returned buffer are linked to the properties of this Memory object.
+   * For instance, if this Memory object is immutable (i.e., read-only, see isReadOnly()),
+   * then the resulting buffer is read-only (see Buffer.isReadOnly().
+   * Additionally, if this is a native memory segment, the resulting buffer is direct
+   * (see ByteBuffer.isDirect()). The endianness of the returned buffer will be set to
+   * the given ByteOrder.
+   * @param order the given ByteOrder.
+   * @return a ByteBuffer view of this Memory object with the given ByteOrder.
+   * @throws UnsupportedOperationException - if this segment cannot be mapped onto a ByteBuffer instance,
+   * e.g. because it models an heap-based segment that is not based on a byte[]),
+   * or if its size is greater than Integer.MAX_VALUE.
+   */
+  ByteBuffer asByteBufferView(ByteOrder order);
+
+  /**
+   * For off-heap segments, this closes the controlling ResourceScope. If the segment is
+   * not off-heap, this does nothing.
+   */
+  void close();
+
+  /**
+   * Returns true if the given object is an instance of this class and has equal contents to
+   * this object.
+   * @param that the given BaseState object
+   * @return true if the given object has equal contents to this object.
+   */
+  boolean equalTo(BaseState that);
+
+  /**
+   * Returns true if the given object is an instance of this class and has equal contents to
+   * this object in the given range of bytes. This will also check two distinct ranges within the
+   * same object for equals.
+   * @param thisOffsetBytes the starting offset in bytes for this object.
+   * @param that the given BaseState object
+   * @param thatOffsetBytes the starting offset in bytes for the given object
+   * @param lengthBytes the size of the range in bytes
+   * @return true if the given object has equal contents to this object in the given range of
+   * bytes.
+   */
+  boolean equalTo(long thisOffsetBytes, BaseState that,
+      long thatOffsetBytes, long lengthBytes);
+
+  /**
+   * Forces any changes made to the contents of this mapped segment to be written to the storage device described
+   * by the mapped segment's file descriptor. Please refer to
+   * <a href="https://docs.oracle.com/en/java/javase/17/docs/api/jdk.incubator.foreign/jdk/incubator/foreign/MemorySegment.html#force()">force()</a>
+   */
+  void force();
+
+  /**
+   * Gets the capacity of this object in bytes
+   * @return the capacity of this object in bytes
+   */
+  long getCapacity();
+
+  /**
+   * Returns the configured MemoryRequestSever or null, if it has not been configured.
+   * @return the configured MemoryRequestSever or null, if it has not been configured.
+   */
+  MemoryRequestServer getMemoryRequestServer();
+
+  /**
+   * Gets the current Type ByteOrder.
+   * This may be different from the ByteOrder of the backing resource and of the Native Byte Order.
+   * @return the current Type ByteOrder.
+   */
+  ByteOrder getByteOrder();
+
+  /**
+   * 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 MemoryRequestServer has been configured.
+   * @return true if the MemoryRequestServer has been configured.
+   */
+  boolean hasMemoryRequestServer();
+
+  /**
+   * Is the underlying resource scope alive?
+   * @return true, if the underlying resource scope is alive.
+   */
+  boolean isAlive();
+
+  /**
+   * Returns true if this instance is a Buffer or WritableBuffer instance.
+   * @return true if this instance is a Buffer or WritableBuffer instance.
+   */
+  boolean isBuffer();
+
+  /**
+   * Returns true if the Native ByteOrder is the same as the ByteOrder of the
+   * current Buffer or Memory and the same ByteOrder as the given byteOrder.
+   * @param byteOrder the given ByteOrder
+   * @return true if the Native ByteOrder is the same as the ByteOrder of the
+   * current Buffer or Memory and the same ByteOrder as the given byteOrder.
+   */
+  boolean isByteOrderCompatible(ByteOrder byteOrder);
+
+  /**
+   * Returns true if the backing resource is direct (off-heap) memory.
+   * This can be true for allocated direct memory, memory mapped files,
+   * or from a wrapped ByteBuffer that was allocated direct.
+   * @return true if the backing resource is direct (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 the backing resource is on the Java heap.
+   * This can be true for wrapped heap primitive arrays
+   * or from a wrapped ByteBuffer that was allocated on the Java heap.
+   * @return true if the backing resource is on the Java heap.
+   */
+  boolean isHeap();
+
+  /**
+   * Tells whether or not the contents of this mapped segment is resident in physical memory. Please refer to
+   * <a href="https://docs.oracle.com/en/java/javase/17/docs/api/jdk.incubator.foreign/jdk/incubator/foreign/MemorySegment.html#isLoaded()">isLoaded()</a>
+   * @return true if it is likely that the contents of this segment is resident in physical memory.
+   */
+  boolean isLoaded();
+
+  /**
+   * Returns true if this instance is of a memory mapped file.
+   * @return true if this instance is of a memory mapped file.
+   */
+  boolean isMapped();
+
+  /**
+   * Returns true if this instance is of a Memory or WritableMemory instance
+   * @return true if this instance is of a Memory or WritableMemory instance
+   */
+  boolean isMemory();
+
+  /**
+   * 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 isRegion();
+
+  /**
+   * Loads the contents of this mapped segment into physical memory. Please refer to
+   * <a href="https://docs.oracle.com/en/java/javase/17/docs/api/jdk.incubator.foreign/jdk/incubator/foreign/MemorySegment.html#load()">load()</a>
+   */
+  void load();
+
+  /**
+   * Returns a positive number if <i>this</i> overlaps <i>that</i> and <i>this</i> base address is &le; <i>that</i>
+   * base address.
+   * Returns a negative number if <i>this</i> overlaps <i>that</i> and <i>this</i> base address is &gt; <i>that</i>
+   * base address.
+   * Returns a zero if there is no overlap or if one or both objects are null, not active or on heap.
+   * @param that the other BaseState object
+   * @return a long value representing the ordering and size of overlap between <i>this</i> and <i>that</i>.
+   */
+  long nativeOverlap(BaseState that);
+
+  /**
+   * See <a href="https://docs.oracle.com/en/java/javase/17/docs/api/jdk.incubator.foreign/jdk/incubator/foreign/MemorySegment.html#mismatch(jdk.incubator.foreign.MemorySegment)>mismatch</a>
+   * @param that the other BaseState
+   * @return the relative offset, in bytes, of the first mismatch between this and the given other BaseState object,
+   * otherwise -1 if no mismatch
+   */
+  long mismatch(BaseState that);
+
+  /**
+   * Returns the resource scope associated with this memory segment.
+   * @return the resource scope associated with this memory segment.
+   */
+  ResourceScope scope();
+
+  //  /**
+  //   * Sets the default MemoryRequestServer to be used in case of capacity overflow of off-heap
+  //   * (Direct or Native) allocated Memory or of on-heap allocated Memory.
+  //   * @param memReqSvr the given default MemoryRequestServer
+  //   */
+  //  void setMemoryRequestServer(MemoryRequestServer memReqSvr);
+
+  /**
+   * Returns a new ByteBuffer with a copy of the data from this Memory object.
+   * This new ByteBuffer will be writable, on heap, and with the endianness specified
+   * by the given ByteOrder.
+   * @param order the given ByteOrder.
+   * @return a new ByteBuffer with a copy of the data from this Memory object.
+   */
+  ByteBuffer toByteBuffer(ByteOrder order);
+
+  /**
+   * 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 comment a description
+   * @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 description and hex output in a human readable format.
+   */
+  String toHexString(String comment, long offsetBytes, int lengthBytes, boolean withData);
+
+  /**
+   * Returns a copy of the underlying MemorySegment.
+   * The size is limited to <i>Integer.MAX_VALUE</i>.
+   * @return a copy of the underlying MemorySegment
+   */
+  MemorySegment toMemorySegment();
+
+  /**
+   * Unloads the contents of this mapped segment from physical memory. Please refer to
+   * <a href="https://docs.oracle.com/en/java/javase/17/docs/api/jdk.incubator.foreign/jdk/incubator/foreign/MemorySegment.html#unload()">unload()</a>
+   */
+  void unload();
+
+  /**
+   * 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);
+
+  //TO STRING
+
+  /**
+   * 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);
+
+}
diff --git a/src/main/java17/org/apache/datasketches/memory/Buffer.java b/src/main/java17/org/apache/datasketches/memory/Buffer.java
new file mode 100644
index 0000000..465eb9a
--- /dev/null
+++ b/src/main/java17/org/apache/datasketches/memory/Buffer.java
@@ -0,0 +1,379 @@
+/*
+ * 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 BaseBuffer {
+
+  //BYTE BUFFER
+  /**
+   * Provides a view of the given <i>ByteBuffer</i> for read-only operations.
+   * The view is of the entire ByteBuffer independent of position and limit.
+   * However, the returned <i>WritableBuffer</i> will have a position and end set to the
+   * ByteBuffer's position and limit, respectively.
+   * The returned WritableBuffer will use the native <i>ByteOrder</i>,
+   * ignoring the ByteOrder of the given ByteBuffer.
+   * This does not affect the ByteOrder of data already in the ByteBuffer.
+   * @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(ByteBuffer byteBuffer) {
+    return wrap(byteBuffer, ByteOrder.nativeOrder());
+  }
+
+  /**
+   * Provides a view of the given <i>ByteBuffer</i> for read-only operations.
+   * The view is of the entire ByteBuffer independent of position and limit.
+   * However, the returned <i>WritableBuffer</i> will have a position and end set to the
+   * ByteBuffer's position and limit, respectively.
+   * The returned WritableBuffer will use the native <i>ByteOrder</i>,
+   * ignoring the ByteOrder of the given ByteBuffer.
+   * This does not affect the ByteOrder of data already in the ByteBuffer.
+   * @param byteBuffer the given ByteBuffer, must not be 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(ByteBuffer byteBuffer, 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(getByteOrder());
+  }
+
+  /**
+   * 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 DIRECT, 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(), getByteOrder());
+  }
+
+  /**
+   * 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(getByteOrder());
+  }
+
+  /**
+   * 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 that.
+
+  /**
+   * Compares the bytes of this Buffer to <i>that</i> Buffer.
+   * This uses absolute offsets not the start, position and end.
+   * Returns <i>(this &lt; that) ? (some negative value) : (this &gt; 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 &lt; that) ? (some negative value) : (this &gt; that) ? (some positive value)
+   * : 0;</i>
+   */
+  int compareTo(long thisOffsetBytes, long thisLengthBytes, Buffer that,
+          long thatOffsetBytes, long thatLengthBytes);
+
+}
diff --git a/src/main/java17/org/apache/datasketches/memory/DefaultMemoryRequestServer.java b/src/main/java17/org/apache/datasketches/memory/DefaultMemoryRequestServer.java
new file mode 100644
index 0000000..f8f828c
--- /dev/null
+++ b/src/main/java17/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;
+
+/**
+ * This is a simple implementation of the MemoryRequestServer that creates space on the Java heap
+ * for the requesting application. This capability is only available for direct, off-heap
+ * allocated memory.
+ *
+ * <p>Using this default implementation could be something like the following:
+ *
+ * <blockquote><pre>
+ * class OffHeap {
+ *   WritableMemory mem;
+ *   MemoryRequestServer memReqSvr = null;
+ *
+ *   void add(Object something) {
+ *
+ *     if (outOfSpace) { // determine if out-of-space
+ *       long spaceNeeded = ...
+ *
+ *       //Acquire the MemoryRequestServer from the direct Memory the first time.
+ *       //Once acquired, this can be reused if more memory is needed later.
+ *       //This is required for the default implementation because it returns memory on heap
+ *       // and on-heap memory does not carry a reference to the MemoryRequestServer.
+ *       memReqSvr = (memReqSvr == null) ? mem.getMemoryRequestServer() : memReqSvr;
+ *
+ *       //Request bigger memory
+ *       WritableMemory newMem = memReqSvr.request(mem, spaceNeeded);
+ *
+ *       //Copy your data from the current memory to the new one and resize
+ *       moveAndResize(mem, newMem);
+ *
+ *       //You are done with the old memory, so request close.
+ *       //Note that it is up to the owner of the WritableHandle whether or not to
+ *       // actually close the resource.
+ *       memReqSvr.requestClose(mem, newMem);
+ *
+ *       mem = newMem; //update your reference to memory
+ *     }
+ *
+ *     //continue with the add process
+ *   }
+ * }
+ * </pre></blockquote>
+ *
+ *
+ * @author Lee Rhodes
+ */
+public final class DefaultMemoryRequestServer implements MemoryRequestServer {
+
+  /**
+   * {@inheritDoc}
+   *
+   * <p>By default this allocates new memory requests on the Java heap.
+   */
+  @Override
+  public WritableMemory request(final WritableMemory currentWritableMemory, final long capacityBytes) {
+    final WritableMemory wmem = WritableMemory.allocate((int)capacityBytes, currentWritableMemory.getByteOrder());
+    return wmem;
+  }
+
+  /**
+   * {@inheritDoc}
+   *
+   * <p>This method does nothing in this default implementation because it is application specific.
+   * This method must be overridden to explicitly close if desired.
+   * Otherwise, the AutoCloseable will eventually close the resource.
+   */
+  @Override
+  public void requestClose(final WritableMemory memToRelease, final WritableMemory newMemory) {
+    //do nothing
+  }
+
+}
diff --git a/src/main/java17/org/apache/datasketches/memory/Memory.java b/src/main/java17/org/apache/datasketches/memory/Memory.java
new file mode 100644
index 0000000..a4eeb2e
--- /dev/null
+++ b/src/main/java17/org/apache/datasketches/memory/Memory.java
@@ -0,0 +1,437 @@
+/*
+ * 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.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import org.apache.datasketches.memory.internal.BaseWritableMemoryImpl;
+
+import jdk.incubator.foreign.MemorySegment;
+import jdk.incubator.foreign.ResourceScope;
+
+/**
+ * Defines the read-only API for offset access to a resource.
+ *
+ * @author Lee Rhodes
+ */
+public interface Memory extends BaseState {
+
+  //BYTE BUFFER
+  /**
+   * Provides a view of the given <i>ByteBuffer</i> for read-only operations.
+   * The view is of the entire ByteBuffer independent of position and limit.
+   * The returned <i>WritableMemory</i> will use the native <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.
+   * @return a new <i>Memory</i> for read-only operations on the given <i>ByteBuffer</i>.
+   */
+  static Memory wrap(ByteBuffer byteBuffer) {
+    return wrap(byteBuffer, ByteOrder.nativeOrder());
+  }
+
+  /**
+   * Provides a view of the given <i>ByteBuffer</i> for read-only operations.
+   * The view is of the entire ByteBuffer independent of position and limit.
+   * The returned <i>WritableMemory</i> will use the native <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(ByteBuffer byteBuffer, ByteOrder byteOrder) {
+    return BaseWritableMemoryImpl.wrapByteBuffer(byteBuffer, true, byteOrder, null);
+  }
+
+  //Duplicates make no sense here
+
+  //MAP
+  /**
+   * Maps the given file into <i>Memory</i> for read operations
+   * Calling this method is equivalent to calling
+   * {@link #map(File, long, long, ResourceScope, ByteOrder)
+   * map(file, 0, file.length(), scope, ByteOrder.nativeOrder())}.
+   * @param file the given file to map. It must be non-null with a non-negative length and readable.
+   * @param scope the given ResourceScope. It must be non-null.
+   * @return mapped Memory.
+   * @throws IllegalArgumentException -- if file is not readable.
+   * @throws IllegalStateException - if scope has been already closed, or if access occurs from a thread other
+   * than the thread owning scope.
+   * @throws IOException - if the specified path does not point to an existing file, or if some other I/O error occurs.
+   * @throws SecurityException - If a security manager is installed and it denies an unspecified permission
+   * required by the implementation.
+   */
+  static Memory map(File file, ResourceScope scope)
+      throws IllegalArgumentException, IllegalStateException, IOException, SecurityException {
+    return map(file, 0, file.length(), scope, ByteOrder.nativeOrder());
+  }
+
+  /**
+   * Maps the specified portion of the given file into <i>Memory</i> for read operations.
+   * @param file the given file to map. It must be non-null,readable and length &ge; 0.
+   * @param fileOffsetBytes the position in the given file in bytes. It must not be negative.
+   * @param capacityBytes the size of the mapped memory. It must not be negative..
+   * @param scope the given ResourceScope. It must be non-null.
+   * @param byteOrder the byte order to be used.  It must be non-null.
+   * @return mapped Memory
+   * @throws IllegalArgumentException -- if file is not readable.
+   * @throws IllegalStateException - if scope has been already closed, or if access occurs from a thread other
+   * than the thread owning scope.
+   * @throws IOException - if the specified path does not point to an existing file, or if some other I/O error occurs.
+   * @throws SecurityException - If a security manager is installed and it denies an unspecified permission
+   * required by the implementation.
+   */
+  static Memory map(File file, long fileOffsetBytes, long capacityBytes, ResourceScope scope, ByteOrder byteOrder)
+      throws IllegalArgumentException, IllegalStateException, IOException, SecurityException {
+    return BaseWritableMemoryImpl.wrapMap(file, fileOffsetBytes, capacityBytes, scope, true, byteOrder);
+  }
+
+  //NO ALLOCATE DIRECT, 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 &ge; 0.
+   * @param capacityBytes the capacity of the region in bytes. It must be &ge; 0.
+   * @return a new <i>Memory</i> representing the defined region based on the given
+   * offsetBytes and capacityBytes.
+   */
+  default Memory region(long offsetBytes, long capacityBytes) {
+    return region(offsetBytes, capacityBytes, getByteOrder());
+  }
+
+  /**
+   * 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 &ge; 0.
+   * @param capacityBytes the capacity of the region in bytes. It must be &ge; 0.
+   * @param byteOrder the byte order to be used.  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(getByteOrder());
+  }
+
+  /**
+   * 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 byte order to be used.  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(byte[] array) {
+    return wrap(array, 0, array.length, ByteOrder.nativeOrder());
+  }
+
+  /**
+   * Wraps the given primitive array for read operations with the given byte order.
+   * @param array the given primitive array.
+   * @param byteOrder the byte order to be used
+   * @return a new <i>Memory</i> for read operations
+   */
+  static Memory wrap(byte[] array, ByteOrder byteOrder) {
+    return wrap(array, 0, array.length, byteOrder);
+  }
+
+  /**
+   * Wraps the given primitive array for read operations with the given byte order.
+   * @param array the given primitive array. It must be non-null.
+   * @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 <i>Memory</i> for read operations
+   */
+  static Memory wrap(byte[] array, int offsetBytes, int lengthBytes, ByteOrder byteOrder) {
+    final MemorySegment slice = MemorySegment.ofArray(array).asSlice(offsetBytes, lengthBytes).asReadOnly();
+    return BaseWritableMemoryImpl.wrapSegmentAsArray(slice, 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(char[] array) {
+    final MemorySegment seg = MemorySegment.ofArray(array).asReadOnly();
+    return BaseWritableMemoryImpl.wrapSegmentAsArray(seg, 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(short[] array) {
+    final MemorySegment seg = MemorySegment.ofArray(array).asReadOnly();
+    return BaseWritableMemoryImpl.wrapSegmentAsArray(seg, 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(int[] array) {
+    final MemorySegment seg = MemorySegment.ofArray(array).asReadOnly();
+    return BaseWritableMemoryImpl.wrapSegmentAsArray(seg, 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(long[] array) {
+    final MemorySegment seg = MemorySegment.ofArray(array).asReadOnly();
+    return BaseWritableMemoryImpl.wrapSegmentAsArray(seg, 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(float[] array) {
+    final MemorySegment seg = MemorySegment.ofArray(array).asReadOnly();
+    return BaseWritableMemoryImpl.wrapSegmentAsArray(seg, 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(double[] array) {
+    final MemorySegment seg = MemorySegment.ofArray(array).asReadOnly();
+    return BaseWritableMemoryImpl.wrapSegmentAsArray(seg, 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 READ METHODS: compareTo, copyTo, writeTo
+
+  /**
+   * Compares the bytes of this Memory to <i>that</i> Memory.
+   * Returns <i>(this &lt; that) ? (some negative value) : (this &gt; 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 &lt; that) ? (some negative value) : (this &gt; 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 ByteArrayOutputStream
+   * @throws IOException may occur while writing to the WritableByteChannel
+   */
+  void writeToByteStream(long offsetBytes, int lengthBytes, ByteArrayOutputStream out)
+      throws IOException;
+
+}
diff --git a/src/main/java17/org/apache/datasketches/memory/MemoryRequestServer.java b/src/main/java17/org/apache/datasketches/memory/MemoryRequestServer.java
new file mode 100644
index 0000000..6bef2c6
--- /dev/null
+++ b/src/main/java17/org/apache/datasketches/memory/MemoryRequestServer.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.datasketches.memory;
+
+/**
+ * The MemoryRequestServer is a callback interface to provide a means for direct (off-heap), heap and ByteBuffer
+ * backed resources to request more memory.
+ *
+ * @author Lee Rhodes
+ */
+public interface MemoryRequestServer {
+
+  /**
+   * Request new WritableMemory with the given capacity. The current Writable Memory will be used to
+   * determine the byte order of the returned WritableMemory and other checks.
+   * @param currentWritableMemory the current writableMemory of the client. It must be non-null.
+   * @param newCapacityBytes The capacity being requested. It must be &ge; 0.
+   *
+   * @return new WritableMemory with the given capacity.
+   */
+  WritableMemory request(WritableMemory currentWritableMemory, long newCapacityBytes);
+
+  /**
+   * Request close the AutoCloseable resource. This only applies to resources allocated using
+   * WritableMemory.allocateDirect(...).
+   * This may be ignored depending on the application implementation.
+   * @param memToClose the relevant WritbleMemory to be considered for closing. It must be non-null.
+   * @param newMemory the newly allocated WritableMemory. It must be non-null.
+   * This is returned from the client to facilitate tracking for the convenience of the resource owner.
+   */
+  void requestClose(final WritableMemory memToClose, WritableMemory newMemory);
+
+}
diff --git a/src/main/java17/org/apache/datasketches/memory/MurmurHash3.java b/src/main/java17/org/apache/datasketches/memory/MurmurHash3.java
new file mode 100644
index 0000000..7fd992c
--- /dev/null
+++ b/src/main/java17/org/apache/datasketches/memory/MurmurHash3.java
@@ -0,0 +1,169 @@
+/*
+ * 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 org.apache.datasketches.memory.internal.MurmurHash3v3;
+
+import jdk.incubator.foreign.MemorySegment;
+
+/**
+ * <p>The MurmurHash3 is a fast, non-cryptographic, 128-bit hash function that has
+ * excellent avalanche and 2-way bit independence properties.</p>
+ *
+ * <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>
+ *
+ * <p>This version 3 of the implementation leverages the jdk.incubator.foreign package of JDK-17 in place of
+ * the Unsafe class.
+ *
+ * @author Lee Rhodes
+ */
+public final class MurmurHash3 {
+
+  //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) {
+    return MurmurHash3v3.hash(in, seed);
+  }
+
+  /**
+   * 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) {
+    return MurmurHash3v3.hash(in, seed);
+  }
+
+  /**
+   * 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) {
+    return MurmurHash3v3.hash(in, seed);
+  }
+
+  /**
+   * 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) {
+    return MurmurHash3v3.hash(in, seed);
+  }
+
+  //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) {
+    return MurmurHash3v3.hash(in, seed, 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) {
+    return MurmurHash3v3.hash(in, seed, 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) {
+    return MurmurHash3v3.hash(in, seed, hashOut);
+  }
+
+  //The main API calls
+
+  /**
+   * 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.
+   * @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.
+   */
+  public static long[] hash(final Memory mem, final long offsetBytes, final long lengthBytes,
+      final long seed, final long[] hashOut) {
+    return MurmurHash3v3.hash(mem, offsetBytes, lengthBytes, seed, hashOut);
+  }
+
+  /**
+   * Returns a 128-bit hash of the input as a long array of size 2.
+   *
+   * @param seg The input MemorySegment. Must be non-null and non-empty.
+   * @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.
+   */
+  public static long[] hash(final MemorySegment seg, final long offsetBytes, final long lengthBytes,
+      final long seed, final long[] hashOut) {
+    return MurmurHash3v3.hash(seg, offsetBytes, lengthBytes, seed, hashOut);
+  }
+
+}
diff --git a/src/main/java17/org/apache/datasketches/memory/WritableBuffer.java b/src/main/java17/org/apache/datasketches/memory/WritableBuffer.java
new file mode 100644
index 0000000..11f2a03
--- /dev/null
+++ b/src/main/java17/org/apache/datasketches/memory/WritableBuffer.java
@@ -0,0 +1,383 @@
+/*
+ * 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 position and limit.
+   * However, the returned <i>WritableBuffer</i> will have a position and end set to the
+   * ByteBuffer's position and limit, respectively.
+   * The returned WritableBuffer will use the native <i>ByteOrder</i>,
+   * ignoring 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 and writable.
+   * @return a new <i>WritableBuffer</i> for write operations on the given <i>ByteBuffer</i>.
+   */
+  static WritableBuffer writableWrap(ByteBuffer byteBuffer) {
+    return writableWrap(byteBuffer, ByteOrder.nativeOrder(), null);
+  }
+
+  /**
+   * Provides a view of the given <i>ByteBuffer</i> for write operations.
+   * The view is of the entire ByteBuffer independent of position and limit.
+   * However, the returned <i>WritableBuffer</i> will have a position and end set to the
+   * ByteBuffer's position and limit, respectively.
+   * The returned WritableBuffer will use the native <i>ByteOrder</i>,
+   * ignoring 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 and writable.
+   * @param byteOrder the byte order to be used.  It must be non-null.
+   * @param memReqSvr A user-specified MemoryRequestServer, which may be null.
+   * @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(ByteBuffer byteBuffer, ByteOrder byteOrder, MemoryRequestServer memReqSvr) {
+    return BaseWritableBufferImpl.wrapByteBuffer(byteBuffer, false, byteOrder, memReqSvr);
+  }
+
+  //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 byte order to be used.  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);
+
+  // NO MAP use WritableMemory
+  // NO ALLOCATE DIRECT use WritableMemory
+
+  //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.
+   */
+  default WritableBuffer writableRegion() {
+    return writableRegion(getPosition(), getEnd() - getPosition(), getByteOrder());
+  }
+
+  /**
+   * 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 byte order to be used.  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
+
+  /**
+   * 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/src/main/java17/org/apache/datasketches/memory/WritableMemory.java b/src/main/java17/org/apache/datasketches/memory/WritableMemory.java
new file mode 100644
index 0000000..4eaedf1
--- /dev/null
+++ b/src/main/java17/org/apache/datasketches/memory/WritableMemory.java
@@ -0,0 +1,560 @@
+/*
+ * 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.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import org.apache.datasketches.memory.internal.BaseWritableMemoryImpl;
+
+import jdk.incubator.foreign.MemorySegment;
+import jdk.incubator.foreign.ResourceScope;
+
+/**
+ * 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 position and limit.
+   * The returned <i>WritableMemory</i> will use the native <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.
+   * @return a new <i>WritableMemory</i> for write operations on the given <i>ByteBuffer</i>.
+   */
+  static WritableMemory writableWrap(ByteBuffer byteBuffer) {
+    return writableWrap(byteBuffer, ByteOrder.nativeOrder(), null);
+  }
+
+  /**
+   * Provides a view of the given <i>ByteBuffer</i> for write operations.
+   * The view is of the entire ByteBuffer independent of position and limit.
+   * The returned <i>WritableMemory</i> will use the native <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 MemoryRequestServer, which may be null.
+   * @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(ByteBuffer byteBuffer, ByteOrder byteOrder, MemoryRequestServer memReqSvr) {
+    return BaseWritableMemoryImpl.wrapByteBuffer(byteBuffer, false, byteOrder, memReqSvr);
+  }
+
+  //Duplicates make no sense here
+
+  //MAP
+  /**
+   * Maps the entire given file into native-ordered WritableMemory for write operations
+   * Calling this method is equivalent to calling
+   * {@link #writableMap(File, long, long, ResourceScope, ByteOrder)
+   *   writableMap(file, 0, file.length(), scope, ByteOrder.nativeOrder())}.
+   * @param file the given file to map. It must be non-null with a non-negative length and writable.
+   * @param scope the give Resource Scope. It must be non-null.
+   * @return mapped WritableMemory
+   * @throws IllegalArgumentException -- if file is not readable.
+   * @throws IllegalArgumentException -- if file is not writable.
+   * @throws IllegalStateException - if scope has been already closed, or if access occurs from a thread other
+   * than the thread owning scope.
+   * @throws IOException - if the specified path does not point to an existing file, or if some other I/O error occurs.
+   * @throws SecurityException - If a security manager is installed and it denies an unspecified permission
+   * required by the implementation.
+   */
+  static WritableMemory writableMap(File file, ResourceScope scope)
+      throws IllegalArgumentException, IllegalStateException, IOException, SecurityException {
+    return writableMap(file, 0, file.length(), scope, ByteOrder.nativeOrder());
+  }
+
+  /**
+   * Maps the specified portion of the given file into Memory for write operations.
+   * @param file the given file to map. It must be non-null with a non-negative length and writable.
+   * @param fileOffsetBytes the position in the given file in bytes. It must not be negative.
+   * @param capacityBytes the size of the mapped Memory. It must be &ge; 0.
+   * @param scope the give Resource Scope. It must be non-null.
+   * @param byteOrder the byte order to be used.  It must be non-null.
+   * @return mapped WritableMemory.
+   * @throws IllegalArgumentException -- if file is not readable or writable.
+   * @throws IllegalArgumentException -- if file is not writable.
+   * @throws IllegalStateException - if scope has been already closed, or if access occurs from a thread other
+   * than the thread owning scope.
+   * @throws IOException - if the specified path does not point to an existing file, or if some other I/O error occurs.
+   * @throws SecurityException - If a security manager is installed and it denies an unspecified permission
+   * required by the implementation.
+   */
+  static WritableMemory writableMap(File file, long fileOffsetBytes, long capacityBytes, ResourceScope scope,
+      ByteOrder byteOrder) throws IllegalArgumentException, IllegalStateException, IOException, SecurityException {
+    return BaseWritableMemoryImpl.wrapMap(file, fileOffsetBytes, capacityBytes, scope, false, byteOrder);
+  }
+
+  //ALLOCATE DIRECT
+
+  /**
+   * Allocates and provides access to capacityBytes directly in native (off-heap) memory.
+   * Native byte order is assumed.
+   * The allocated memory will be 8-byte aligned.
+   *
+   * <p><b>NOTICE:</b> It is the responsibility of the using application to call <i>close()</i> when done.</p>
+   *
+   * @param capacityBytes the size of the desired memory in bytes.
+   * @param scope the given ResourceScope. It must be non-null.
+   * @param memReqSvr A user-specified MemoryRequestServer, which may be null.
+   * @return WritableMemory for this off-heap, native resource.
+   */
+  static WritableMemory allocateDirect(long capacityBytes, ResourceScope scope, MemoryRequestServer memReqSvr) {
+    return allocateDirect(capacityBytes, 8, scope, ByteOrder.nativeOrder(), memReqSvr);
+  }
+
+  /**
+   * Allocates and provides access to capacityBytes directly in native (off-heap) memory.
+   * The allocated memory will be aligned to the given <i>alignmentBytes</i>.
+   *
+   * <p><b>NOTICE:</b> It is the responsibility of the using application to
+   * call <i>close()</i> when done.</p>
+   *
+   * @param capacityBytes the size of the desired memory in bytes.
+   * @param alignmentBytes requested segment alignment. Typically 1, 2, 4 or 8.
+   * @param scope the given ResourceScope. It must be non-null.
+   * @param byteOrder the byte order to be used.  It must be non-null.
+   * @param memReqSvr A user-specified MemoryRequestServer, which may be null.
+   * This is a callback mechanism for a user client of direct memory to request more memory.
+   * @return WritableMemory
+   */
+  static WritableMemory allocateDirect(
+      long capacityBytes,
+      long alignmentBytes,
+      ResourceScope scope,
+      ByteOrder byteOrder,
+      MemoryRequestServer memReqSvr) {
+    return BaseWritableMemoryImpl.wrapDirect(capacityBytes, alignmentBytes, scope, 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.
+   * @param capacityBytes the capacity of the returned object in bytes
+   * @return a new <i>WritableMemory</i> representing the defined writable region.
+   */
+  default WritableMemory writableRegion(long offsetBytes, long capacityBytes) {
+    return writableRegion(offsetBytes, capacityBytes, getByteOrder());
+  }
+
+  /**
+   * 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 byte order to be used.  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(getByteOrder());
+  }
+
+  /**
+   * 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 byte order to be used.  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.
+   * This will utilize the MemoryRequestServer if it has been configured.
+   * @param capacityBytes the given capacity in bytes.
+   * @return a new WritableMemory for write operations on a new byte array.
+   */
+  static WritableMemory allocate(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 byte order to be used.  It must be non-null.
+   * @return a new WritableMemory for write operations on a new byte array.
+   */
+  static WritableMemory allocate(int capacityBytes, ByteOrder byteOrder) {
+    return allocate(capacityBytes, byteOrder, null);
+  }
+
+  /**
+   * Creates on-heap WritableMemory with the given capacity and the given byte order.
+   * @param capacityBytes the given capacity in bytes.
+   * @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 a new byte array.
+   */
+  static WritableMemory allocate(int capacityBytes, ByteOrder byteOrder, MemoryRequestServer memReqSvr) {
+    final byte[] arr = new byte[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(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(byte[] array, 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(byte[] array, int offsetBytes, int lengthBytes, 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.
+   * @param array the given primitive array. It must be non-null.
+   * @param offsetBytes the byte offset into the given array. It must be &ge; 0.
+   * @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.
+   * @param memReqSvr A user-specified <i>MemoryRequestServer</i>, which may be null.
+   * This is a callback mechanism for a user client to request a larger <i>WritableMemory</i>.
+   * @return a new WritableMemory for write operations on the given primitive array.
+   */
+  static WritableMemory writableWrap(byte[] array, int offsetBytes, int lengthBytes, ByteOrder byteOrder,
+      MemoryRequestServer memReqSvr) {
+    final MemorySegment slice = MemorySegment.ofArray(array).asSlice(offsetBytes, lengthBytes);
+    return BaseWritableMemoryImpl.wrapSegmentAsArray(slice, 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(char[] array) {
+    final MemorySegment seg = MemorySegment.ofArray(array);
+    return BaseWritableMemoryImpl.wrapSegmentAsArray(seg, 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(short[] array) {
+    final MemorySegment seg = MemorySegment.ofArray(array);
+    return BaseWritableMemoryImpl.wrapSegmentAsArray(seg, 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(int[] array) {
+    final MemorySegment seg = MemorySegment.ofArray(array);
+    return BaseWritableMemoryImpl.wrapSegmentAsArray(seg, 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(long[] array) {
+    final MemorySegment seg = MemorySegment.ofArray(array);
+    return BaseWritableMemoryImpl.wrapSegmentAsArray(seg, 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(float[] array) {
+    final MemorySegment seg = MemorySegment.ofArray(array);
+    return BaseWritableMemoryImpl.wrapSegmentAsArray(seg, 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(double[] array) {
+    final MemorySegment seg = MemorySegment.ofArray(array);
+    return BaseWritableMemoryImpl.wrapSegmentAsArray(seg, 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
+
+  /**
+   * Clears all bytes of this Memory to zero
+   */
+  void clear();
+
+  /**
+   * Clears a portion of this Memory to zero.
+   * @param offsetBytes offset bytes relative to this Memory start
+   * @param lengthBytes the length in bytes
+   */
+  void clear(long offsetBytes, long lengthBytes);
+
+  /**
+   * Clears the bits defined by the bitMask
+   * @param offsetBytes offset bytes relative to this Memory start.
+   * @param bitMask the bits set to one will be cleared
+   */
+  void clearBits(long offsetBytes, byte bitMask);
+
+  /**
+   * Fills all bytes of this Memory region to the given byte value.
+   * @param value the given byte value
+   */
+  void fill(byte value);
+
+  /**
+   * Fills a portion of this Memory region to the given byte value.
+   * @param offsetBytes offset bytes relative to this Memory start
+   * @param lengthBytes the length in bytes
+   * @param value the given byte value
+   */
+  void fill(long offsetBytes, long lengthBytes, byte value);
+
+  /**
+   * Sets the bits defined by the bitMask
+   * @param offsetBytes offset bytes relative to this Memory start
+   * @param bitMask the bits set to one will be set
+   */
+  void setBits(long offsetBytes, byte bitMask);
+
+
+  //OTHER WRITABLE API METHODS
+  /**
+   * WritableMemory enables the MemoryRequestServer Direct (Native) Memory backed resources.
+   * Other backed resources will always return null.
+   * Gets the MemoryRequestServer implementation, if set, to request additional memory.
+   * The user must customize the actions of the MemoryRequestServer by
+   * implementing the MemoryRequestServer interface and set using this method:
+   * {@link WritableMemory#allocateDirect(long, long, ResourceScope, ByteOrder, MemoryRequestServer)}.
+   * Simple implementation examples include the DefaultMemoryRequestServer in the main tree, as well as
+   * the ExampleMemoryRequestServerTest and the use with ByteBuffer documented in the DruidIssue11544Test
+   * in the test tree.
+   * @return the MemoryRequestServer object or null.
+   */
+  @Override
+  MemoryRequestServer getMemoryRequestServer();
+
+}
diff --git a/src/main/java17/org/apache/datasketches/memory/XxHash.java b/src/main/java17/org/apache/datasketches/memory/XxHash.java
new file mode 100644
index 0000000..11cc388
--- /dev/null
+++ b/src/main/java17/org/apache/datasketches/memory/XxHash.java
@@ -0,0 +1,177 @@
+/*
+ * 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 int offsetBytes,
+      final int 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 int offsetShorts,
+      final int 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 int offsetChars,
+      final int 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 int offsetInts,
+      final int 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 int offsetLongs,
+      final int 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 int offsetFloats,
+      final int 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 int offsetDoubles,
+      final int 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 int offsetChars,
+      final int lengthChars, final long seed) {
+    return org.apache.datasketches.memory.internal.XxHash64.hashString(str, offsetChars, lengthChars, seed);
+  }
+
+}
+
diff --git a/src/main/java17/org/apache/datasketches/memory/internal/BaseBufferImpl.java b/src/main/java17/org/apache/datasketches/memory/internal/BaseBufferImpl.java
new file mode 100644
index 0000000..5d50d7c
--- /dev/null
+++ b/src/main/java17/org/apache/datasketches/memory/internal/BaseBufferImpl.java
@@ -0,0 +1,199 @@
+/*
+ * 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.BaseBuffer;
+import org.apache.datasketches.memory.MemoryRequestServer;
+
+import jdk.incubator.foreign.MemorySegment;
+
+/**
+ * This implements the positional API.
+ * This is different from and simpler than Java BufferImpl 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
+ */
+abstract class BaseBufferImpl extends BaseStateImpl implements BaseBuffer {
+  private long capacity;
+  private long start = 0;
+  private long pos = 0;
+  private long end;
+
+  //Pass-through ctor
+  BaseBufferImpl(
+      final MemorySegment seg,
+      final int typeId,
+      final MemoryRequestServer memReqSvr) {
+    super(seg, typeId, memReqSvr);
+    capacity = end = seg.byteSize();
+  }
+
+  @Override
+  public final BaseBufferImpl incrementPosition(final long increment) {
+    incrementAndAssertPositionForRead(pos, increment);
+    return this;
+  }
+
+  @Override
+  public final BaseBufferImpl incrementAndCheckPosition(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 BaseBufferImpl resetPosition() {
+    pos = start;
+    return this;
+  }
+
+  @Override
+  public final BaseBufferImpl setPosition(final long position) {
+    assertInvariants(start, position, end, capacity);
+    pos = position;
+    return this;
+  }
+
+  @Override
+  public final BaseBufferImpl setAndCheckPosition(final long position) {
+    checkInvariants(start, position, end, capacity);
+    pos = position;
+    return this;
+  }
+
+  @Override
+  public final BaseBufferImpl setStartPositionEnd(final long start, final long position,
+      final long end) {
+    assertInvariants(start, position, end, capacity);
+    this.start = start;
+    this.end = end;
+    pos = position;
+    return this;
+  }
+
+  @Override
+  public final BaseBufferImpl setAndCheckStartPositionEnd(final long start, final long position,
+      final long end) {
+    checkInvariants(start, position, end, capacity);
+    this.start = start;
+    this.end = end;
+    pos = position;
+    return this;
+  }
+
+  //RESTRICTED
+  //Position checks are only used for Buffers
+  //asserts are used for primitives, not used at runtime
+  final void incrementAndAssertPositionForRead(final long position, final long increment) {
+    final long newPos = position + increment;
+    assertInvariants(start, newPos, end, capacity);
+    pos = newPos;
+  }
+
+  //checks are used for arrays and apply at runtime
+  final void incrementAndCheckPositionForRead(final long position, final long increment) {
+    final long newPos = position + increment;
+    checkInvariants(start, newPos, end, capacity);
+    pos = newPos;
+  }
+
+  /**
+   * The invariants equation is: {@code 0 <= start <= position <= end <= capacity}.
+   * If this equation is violated and assertions are enabled,
+   * an <i>AssertionError</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 assertInvariants(final long start, final long pos, final long end,
+      final long cap) {
+    assert (start | pos | end | cap | (pos - start) | (end - pos) | (cap - end) ) >= 0L
+        : "Violation of Invariants: "
+        + "start: " + start
+        + " <= pos: " + pos
+        + " <= end: " + end
+        + " <= cap: " + cap
+        + "; (pos - start): " + (pos - start)
+        + ", (end - pos): " + (end - pos)
+        + ", (cap - end): " + (cap - end);
+  }
+
+  /**
+   * The invariants equation is: {@code 0 <= start <= position <= end <= capacity}.
+   * If this equation is violated an <i>IllegalArgumentException</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 IllegalArgumentException(
+          "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/src/main/java17/org/apache/datasketches/memory/internal/BaseStateImpl.java b/src/main/java17/org/apache/datasketches/memory/internal/BaseStateImpl.java
new file mode 100644
index 0000000..891f283
--- /dev/null
+++ b/src/main/java17/org/apache/datasketches/memory/internal/BaseStateImpl.java
@@ -0,0 +1,475 @@
+/*
+ * 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 jdk.incubator.foreign.MemoryAccess.getByteAtOffset;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Objects;
+
+import org.apache.datasketches.memory.BaseState;
+import org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableBuffer;
+import org.apache.datasketches.memory.WritableMemory;
+
+import jdk.incubator.foreign.MemorySegment;
+import jdk.incubator.foreign.ResourceScope;
+
+/**
+ * Keeps key configuration state for MemoryImpl and BufferImpl plus some common static variables
+ * and check methods.
+ *
+ * @author Lee Rhodes
+ */
+abstract class BaseStateImpl implements BaseState {
+  static final String JDK; //must be at least "1.8"
+  static final int JDK_MAJOR; //8, 11, 12, etc
+
+  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.
+  // 0000 0XXX
+  static final int READONLY  = 1;
+  static final int REGION    = 1 << 1;
+  static final int DUPLICATE = 1 << 2; //for Buffer only
+
+  // 000X X000
+  static final int HEAP   = 0;
+  static final int DIRECT = 1 << 3;
+  static final int MAP    = 1 << 4; //Map is always Direct also
+
+  // 00X0 0000
+  static final int NATIVE    = 0;
+  static final int NONNATIVE = 1 << 5;
+
+  // 0X00 0000
+  static final int MEMORY = 0;
+   static final int BUFFER = 1 << 6;
+
+  // X000 0000
+  static final int BYTEBUF = 1 << 7;
+
+  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];
+  }
+
+  final MemorySegment seg;
+  final int typeId;
+
+  MemoryRequestServer memReqSvr;
+
+  BaseStateImpl(final MemorySegment seg, final int typeId, final MemoryRequestServer memReqSvr) {
+    this.seg = seg;
+    this.typeId = typeId;
+    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 IllegalArgumentException} will be thrown.
+   * @param reqOff the requested offset
+   * @param reqLen the requested length
+   * @param allocSize the allocated size.
+   * @throws IllegalArgumentException for exceeding address bounds
+   */
+  static void checkBounds(final long reqOff, final long reqLen, final long allocSize) {
+    if ((reqOff | reqLen | (reqOff + reqLen) | (allocSize - (reqOff + reqLen))) < 0) {
+      throw new IllegalArgumentException(
+          "reqOffset: " + reqOff + ", reqLength: " + reqLen
+              + ", (reqOff + reqLen): " + (reqOff + reqLen) + ", allocSize: " + allocSize);
+    }
+  }
+
+  static void checkJavaVersion(final String jdkVer, final int p0) {
+    if ( p0 != 17 ) {
+      throw new IllegalArgumentException(
+          "Unsupported JDK Major Version, must be 17; " + jdkVer);
+    }
+  }
+
+  /**
+   * 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("\\.");//grab only number groups and "."
+      parts = parts[0].split("\\."); //split out the number groups
+      p0 = Integer.parseInt(parts[0]); //the first number group
+      p1 = (parts.length > 1) ? Integer.parseInt(parts[1]) : 0; //2nd number group, or 0
+    } catch (final NumberFormatException | ArrayIndexOutOfBoundsException  e) {
+      throw new IllegalArgumentException("Improper Java -version string: " + jdkVer + "\n" + e);
+    }
+    checkJavaVersion(jdkVer, p0);
+    return new int[] {p0, p1};
+  }
+
+  /**
+   * 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) {
+      case 0 : sb.append("Writable:\t"); break;
+      case 1 : sb.append("ReadOnly:\t"); break;
+      case 2 : sb.append("Writable:\tRegion:\t"); break;
+      case 3 : sb.append("ReadOnly:\tRegion:\t"); break;
+      case 4 : sb.append("Writable:\tDuplicate:\t"); break;
+      case 5 : sb.append("ReadOnly:\tDuplicate:\t"); break;
+      case 6 : sb.append("Writable:\tRegion:\tDuplicate:\t"); break;
+      case 7 : sb.append("ReadOnly:\tRegion:\tDuplicate:\t"); break;
+      default: break;
+    }
+    final int group2 = (typeId >>> 3) & 0x3;
+    switch (group2) {
+      case 0 : sb.append("Heap:\t"); break;
+      case 1 : sb.append("Direct:\t"); break;
+      case 2 : sb.append("Map:\t"); break;
+      case 3 : sb.append("Direct:\tMap:\t"); break;
+      default: break;
+    }
+    if ((typeId & BYTEBUF) > 0) { sb.append("ByteBuffer:\t"); }
+
+    final int group3 = (typeId >>> 5) & 0x1;
+    switch (group3) {
+      case 0 : sb.append("NativeOrder:\t"); break;
+      case 1 : sb.append("NonNativeOrder:\t"); break;
+      default: break;
+    }
+    final int group4 = (typeId >>> 6) & 0x1;
+    switch (group4) {
+      case 0 : sb.append("Memory"); break;
+      case 1 : sb.append("Buffer"); break;
+      default: break;
+    }
+    return sb.toString();
+  }
+
+  static final WritableBuffer selectBuffer(
+      final MemorySegment segment,
+      final int type,
+      final MemoryRequestServer memReqSvr,
+      final boolean byteBufferType,
+      final boolean mapType,
+      final boolean nativeBOType) {
+    final MemoryRequestServer memReqSvr2 = (byteBufferType || mapType) ? null : memReqSvr;
+    final WritableBuffer wbuf;
+    if (nativeBOType) {
+      wbuf = new NativeWritableBufferImpl(segment, type, memReqSvr2);
+    } else { //non-native BO
+      wbuf = new NonNativeWritableBufferImpl(segment, type, memReqSvr2);
+    }
+    return wbuf;
+  }
+
+  static final WritableMemory selectMemory(
+      final MemorySegment segment,
+      final int type,
+      final MemoryRequestServer memReqSvr,
+      final boolean byteBufferType,
+      final boolean mapType,
+      final boolean nativeBOType) {
+    final MemoryRequestServer memReqSvr2 = (byteBufferType || mapType) ? null : memReqSvr;
+    final WritableMemory wmem;
+    if (nativeBOType) {
+      wmem = new NativeWritableMemoryImpl(segment, type, memReqSvr2);
+    } else { //non-native BO
+      wmem = new NonNativeWritableMemoryImpl(segment, type, memReqSvr2);
+    }
+    return wmem;
+  }
+
+  /**
+   * Returns a formatted hex string of an area of this object.
+   * Used primarily for testing.
+   * @param state the BaseStateImpl
+   * @param comment optional unique description
+   * @param offsetBytes offset bytes relative to the MemoryImpl start
+   * @param lengthBytes number of bytes to convert to a hex string
+   * @return a formatted hex string in a human readable array
+   */
+  static final String toHex(final BaseStateImpl state, final String comment, final long offsetBytes,
+      final int lengthBytes, final boolean withData) {
+    final MemorySegment seg = state.seg;
+    final long capacity = seg.byteSize();
+    checkBounds(offsetBytes, lengthBytes, capacity);
+    final StringBuilder sb = new StringBuilder();
+    final String theComment = (comment != null) ? comment : "";
+    final String addHCStr = "" + Integer.toHexString(seg.address().hashCode());
+    final MemoryRequestServer memReqSvr = state.getMemoryRequestServer();
+    final String memReqStr = memReqSvr != null
+        ? memReqSvr.getClass().getSimpleName() + ", " + Integer.toHexString(memReqSvr.hashCode())
+        : "null";
+
+    sb.append(LS + "### DataSketches Memory Component SUMMARY ###").append(LS);
+    sb.append("Optional Comment       : ").append(theComment).append(LS);
+    sb.append("TypeId String          : ").append(typeDecode(state.typeId)).append(LS);
+    sb.append("OffsetBytes            : ").append(offsetBytes).append(LS);
+    sb.append("LengthBytes            : ").append(lengthBytes).append(LS);
+    sb.append("Capacity               : ").append(capacity).append(LS);
+    sb.append("MemoryAddress hashCode : ").append(addHCStr).append(LS);
+    sb.append("MemReqSvr, hashCode    : ").append(memReqStr).append(LS);
+    sb.append("Read Only              : ").append(state.isReadOnly()).append(LS);
+    sb.append("Type Byte Order        : ").append(state.getByteOrder().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, LittleEndian     :  0  1  2  3  4  5  6  7");
+      for (long i = 0; i < lengthBytes; i++) {
+        final int b = getByteAtOffset(seg, offsetBytes + i) & 0XFF;
+        if (i % 8 == 0) { //row header
+          sb.append(String.format("%n%23s: ", offsetBytes + i));
+        }
+        sb.append(String.format("%02x ", b));
+      }
+    }
+    sb.append(LS + "### END SUMMARY ###");
+    sb.append(LS);
+
+    return sb.toString();
+  }
+
+  @Override
+  public final ByteBuffer asByteBufferView(final ByteOrder order) {
+    final ByteBuffer byteBuf = seg.asByteBuffer().order(order);
+    return byteBuf;
+  }
+
+  @SuppressWarnings("resource")
+  @Override
+  public void close() {
+    if (seg != null && seg.scope().isAlive() && !seg.scope().isImplicit()) {
+      if (seg.isNative() || seg.isMapped()) {
+        seg.scope().close();
+      }
+    }
+  }
+
+  @Override
+  public final boolean equalTo(final BaseState that) {
+    Objects.requireNonNull(that);
+    return equalTo(0, that, 0, that.getCapacity());
+  }
+
+  @Override
+  public final boolean equalTo(final long thisOffsetBytes, final BaseState that,
+      final long thatOffsetBytes, final long lengthBytes) {
+    Objects.requireNonNull(that);
+   return CompareAndCopy.equals(seg, thisOffsetBytes, ((BaseStateImpl) that).seg, thatOffsetBytes, lengthBytes);
+  }
+
+  @Override
+  public void force() { seg.force(); }
+
+  @Override
+  public final long getCapacity() {
+    return seg.byteSize();
+  }
+
+  @Override
+  public MemoryRequestServer getMemoryRequestServer() {
+    return memReqSvr;
+  }
+
+  @Override
+  public final ByteOrder getByteOrder() {
+    return (typeId & NONNATIVE) > 0 ? NON_NATIVE_BYTE_ORDER : ByteOrder.nativeOrder();
+  }
+
+  @Override
+  public final boolean hasByteBuffer() {
+    return (typeId & BYTEBUF) > 0;
+  }
+
+  @Override
+  public boolean hasMemoryRequestServer() {
+    return memReqSvr != null;
+  }
+
+  @SuppressWarnings("resource")
+  @Override
+  public boolean isAlive() { return seg.scope().isAlive(); }
+
+  @Override
+  public final boolean isByteOrderCompatible(final ByteOrder byteOrder) {
+    final ByteOrder typeBO = getByteOrder();
+    return typeBO == ByteOrder.nativeOrder() && typeBO == byteOrder;
+  }
+
+  @Override
+  public final boolean isBuffer() {
+    return (typeId & BUFFER) > 0;
+  }
+
+  @Override
+  public final boolean isDirect() {
+    assert seg.isNative() == (typeId & DIRECT) > 0;
+    return seg.isNative();
+  }
+
+  @Override
+  public final boolean isDuplicate() {
+    return (typeId & DUPLICATE) > 0;
+  }
+
+  @Override
+  public final boolean isHeap() {
+    return !isDirect() && !isMapped();
+  }
+
+  @Override
+  public boolean isLoaded() { return seg.isLoaded(); }
+
+  @Override
+  public boolean isMapped() {
+    assert seg.isMapped() == (typeId & MAP) > 0;
+    return seg.isMapped();
+  }
+
+  @Override
+  public final boolean isMemory() {
+    return (typeId & BUFFER) == 0;
+  }
+
+  @Override
+  public final boolean isReadOnly() {
+    assert seg.isReadOnly() == (typeId & READONLY) > 0;
+    return seg.isReadOnly();
+  }
+
+  @Override
+  public final boolean isRegion() {
+    return (typeId & REGION) > 0;
+  }
+
+  @Override
+  public void load() { seg.load(); }
+
+  @Override
+  public long mismatch(final BaseState that) {
+    Objects.requireNonNull(that);
+    if (!that.isAlive()) { throw new IllegalArgumentException("Given argument is not alive."); }
+    BaseStateImpl thatBSI = (BaseStateImpl) that;
+    return seg.mismatch(thatBSI.seg);
+  }
+
+  @Override
+  public final long nativeOverlap(final BaseState that) {
+    if (that == null) { return 0; }
+    if (!that.isAlive()) { return 0; }
+    BaseStateImpl thatBSI = (BaseStateImpl) that;
+    if (this == thatBSI) { return seg.byteSize(); }
+    return nativeOverlap(seg, thatBSI.seg);
+  }
+
+  static final long nativeOverlap(final MemorySegment segA, final MemorySegment segB) { //used in test
+    if (!segA.isNative() || !segB.isNative()) { return 0; } //both segments must be native
+    //Assume that memory addresses increase from left to right.
+    //Identify the left and right edges of two regions, A and B in memory.
+    final long bytesA = segA.byteSize();
+    final long bytesB = segB.byteSize();
+    final long lA = segA.address().toRawLongValue(); //left A
+    final long lB = segB.address().toRawLongValue(); //left B
+    final long rA = lA + bytesA; //right A
+    final long rB = lB + bytesB; //right B
+    if ((rA <= lB) || (rB <= lA)) { return 0; } //Eliminate the totally disjoint case:
+
+    final long result = (bytesA == bytesB) //Two major cases: equal and not equal in size
+        ? nativeOverlapEqualSizes(lA, rA, lB, rB)
+        : nativeOverlapNotEqualSizes(lA, rA, lB, rB);
+
+    return (lB < lA) ? -result : result; //if lB is lower in memory than lA, we return a negative result
+  }
+
+  private static final long nativeOverlapEqualSizes(final long lA, final long rA, final long lB, final long rB) {
+    if (lA == lB) { return rA - lA; } //Exact overlap, return either size
+    return (lA < lB)
+        ? rA - lB  //Partial overlap on right portion of A
+        : rB - lA; //else partial overlap on left portion of A
+  }
+
+  private static final long nativeOverlapNotEqualSizes(final long lA, final long rA, final long lB, final long rB) {
+    return (rB - lB < rA - lA) //whichever is larger we assign to parameters 1 and 2
+        ? biggerSmaller(lA, rA, lB, rB)  //A bigger than B
+        : biggerSmaller(lB, rB, lA, rA); //B bigger than A, reverse parameters
+  }
+
+  private static final long biggerSmaller(long lLarge, long rLarge, long lSmall, long rSmall) {
+    if ((rSmall <= rLarge) && (lLarge <= lSmall)) { return rSmall - lSmall; } //Small is totally within Large
+    return (rLarge < rSmall)
+        ? rLarge - lSmall  //Partial overlap on right portion of Large
+        : rSmall - lLarge; //Partial overlap on left portion of Large
+  }
+
+  @Override
+  public ResourceScope scope() { return seg.scope(); }
+
+  @Override
+  public ByteBuffer toByteBuffer(final ByteOrder order) {
+    Objects.requireNonNull(order, "The input ByteOrder must not be null");
+    return ByteBuffer.wrap(seg.toByteArray());
+  }
+
+  @Override
+  public final String toHexString(final String comment, final long offsetBytes, final int lengthBytes,
+      final boolean withData) {
+    return toHex(this, comment, offsetBytes, lengthBytes, withData);
+  }
+
+  @Override
+  public MemorySegment toMemorySegment() {
+    final MemorySegment arrSeg = MemorySegment.ofArray(new byte[(int)seg.byteSize()]);
+    arrSeg.copyFrom(seg);
+    return arrSeg;
+  }
+
+  @Override
+  public void unload() { seg.unload(); }
+
+  @Override
+  public final long xxHash64(final long in, final long seed) {
+    return XxHash64.hash(in, seed);
+  }
+
+  @Override
+  public final long xxHash64(final long offsetBytes, final long lengthBytes, final long seed) {
+    return XxHash64.hash(seg, offsetBytes, lengthBytes, seed);
+  }
+
+}
diff --git a/src/main/java17/org/apache/datasketches/memory/internal/BaseWritableBufferImpl.java b/src/main/java17/org/apache/datasketches/memory/internal/BaseWritableBufferImpl.java
new file mode 100644
index 0000000..a92e75c
--- /dev/null
+++ b/src/main/java17/org/apache/datasketches/memory/internal/BaseWritableBufferImpl.java
@@ -0,0 +1,333 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.datasketches.memory.internal;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Objects;
+
+import org.apache.datasketches.memory.Buffer;
+import org.apache.datasketches.memory.Memory;
+import org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableBuffer;
+import org.apache.datasketches.memory.WritableMemory;
+
+import jdk.incubator.foreign.MemoryAccess;
+import jdk.incubator.foreign.MemorySegment;
+
+/*
+ * Developer notes: The heavier methods, such as put/get arrays, duplicate, region, clear, fill,
+ * compareTo, etc., use hard checks (check*() and incrementAndCheck*() methods), which execute at
+ * runtime and throw exceptions if violated. The cost of the runtime checks are minor compared to
+ * the rest of the work these methods are doing.
+ *
+ * <p>The light weight methods, such as put/get primitives, use asserts (assert*() and
+ * incrementAndAssert*() methods), which only execute when asserts are enabled and JIT will remove
+ * them entirely from production runtime code. The offset versions of the light weight methods will
+ * simplify to a single unsafe call, which is further simplified by JIT to an intrinsic that is
+ * often a single CPU instruction.
+ */
+
+/**
+ * Common base of native-ordered and non-native-ordered {@link WritableBuffer} implementations.
+ * Contains methods which are agnostic to the byte order.
+ */
+public abstract class BaseWritableBufferImpl extends BaseBufferImpl implements WritableBuffer {
+
+  //Pass-through constructor
+  BaseWritableBufferImpl(
+      final MemorySegment seg,
+      final int typeId,
+      final MemoryRequestServer memReqSvr) {
+    super(seg, typeId, memReqSvr);
+  }
+
+  //NO WRAP HEAP ARRAY RESOURCE
+
+  //BYTE BUFFER RESOURCE
+
+  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 ByteBuffer byteBuf;
+    if (localReadOnly) {
+      if (byteBuffer.isReadOnly()) {
+        byteBuf = byteBuffer.duplicate();
+      } else { //bb writable
+        byteBuf = byteBuffer.asReadOnlyBuffer();
+      }
+    } else { //want writable
+      if (byteBuffer.isReadOnly()) {
+        throw new IllegalArgumentException("ByteBuffer must be writable.");
+      }
+      byteBuf = byteBuffer.duplicate();
+    }
+    byteBuf.clear(); //resets position to zero and limit to capacity. Does not clear data.
+    final MemorySegment seg = MemorySegment.ofByteBuffer(byteBuf); //from 0 to capacity
+    int type = BUFFER | BYTEBUF
+        | (localReadOnly ? READONLY : 0)
+        | (seg.isNative() ? DIRECT : 0)
+        | (seg.isMapped() ? MAP : 0);
+    final WritableBuffer wbuf;
+    if (byteOrder == NON_NATIVE_BYTE_ORDER) {
+      type |= NONNATIVE;
+      wbuf = new NonNativeWritableBufferImpl(seg, type, memReqSvr);
+    } else {
+      wbuf = new NativeWritableBufferImpl(seg, type, memReqSvr);
+    }
+    wbuf.setStartPositionEnd(0, byteBuffer.position(), byteBuffer.limit());
+    return wbuf;
+  }
+
+  //NO MAP RESOURCE
+  //NO DIRECT RESOURCE
+
+  //REGIONS DERIVED
+
+  @Override
+  public Buffer region(final long offsetBytes, final long capacityBytes, final ByteOrder byteOrder) {
+    return regionImpl(offsetBytes, capacityBytes, true, byteOrder);
+  }
+
+  @Override
+  public WritableBuffer writableRegion(final long offsetBytes, final long capacityBytes, final ByteOrder byteOrder) {
+    if (this.isReadOnly()) {
+      throw new IllegalArgumentException("Cannot create a writable region from a read-only Memory.");
+    }
+    return regionImpl(offsetBytes, capacityBytes, false, byteOrder);
+  }
+
+  private WritableBuffer regionImpl(
+      final long offsetBytes,
+      final long capacityBytes,
+      final boolean localReadOnly,
+      final ByteOrder byteOrder) {
+    if (!this.isAlive()) { throw new IllegalStateException("This Buffer is not alive."); }
+    Objects.requireNonNull(byteOrder, "byteOrder must be non-null.");
+    final boolean readOnly = isReadOnly() || localReadOnly;
+    final MemorySegment slice = (readOnly && !seg.isReadOnly())
+        ? seg.asSlice(offsetBytes, capacityBytes).asReadOnly()
+        : seg.asSlice(offsetBytes, capacityBytes);
+    final boolean duplicateType = isDuplicate();
+    final boolean mapType = seg.isMapped();
+    final boolean directType = seg.isNative();
+    final boolean nativeBOType = byteOrder == ByteOrder.nativeOrder();
+    final boolean byteBufferType = hasByteBuffer();
+    final int type = BUFFER | REGION
+        | (readOnly ? READONLY : 0)
+        | (duplicateType ? DUPLICATE : 0)
+        | (mapType ? MAP : 0)
+        | (directType ? DIRECT : 0)
+        | (nativeBOType ? NATIVE : NONNATIVE)
+        | (byteBufferType ? BYTEBUF : 0);
+
+    final WritableBuffer wbuf = selectBuffer(slice, type, memReqSvr, byteBufferType, mapType, nativeBOType);
+    wbuf.setStartPositionEnd(0, 0, wbuf.getCapacity());
+    return wbuf;
+  }
+
+  //DUPLICATES, DERIVED
+  @Override
+  public Buffer duplicate() {
+    return duplicateImpl(true, getByteOrder());
+  }
+
+  @Override
+  public Buffer duplicate(final ByteOrder byteOrder) {
+    return duplicateImpl(true, byteOrder);
+  }
+
+  @Override
+  public WritableBuffer writableDuplicate() {
+    if (isReadOnly()) {
+      throw new IllegalArgumentException("Cannot create a writable duplicate from a read-only Buffer.");
+    }
+    return duplicateImpl(false, getByteOrder());
+  }
+
+  @Override
+  public WritableBuffer writableDuplicate(final ByteOrder byteOrder) {
+    if (isReadOnly()) {
+      throw new IllegalArgumentException("Cannot create a writable duplicate from a read-only Buffer.");
+    }
+    return duplicateImpl(false, byteOrder);
+  }
+
+  private WritableBuffer duplicateImpl(final boolean localReadOnly, final ByteOrder byteOrder) {
+    if (!this.isAlive()) { throw new IllegalStateException("This Memory is not alive."); }
+    final boolean readOnly = isReadOnly() || localReadOnly;
+    final MemorySegment seg2 = (readOnly && !seg.isReadOnly()) ? seg.asReadOnly() : seg;
+    final boolean regionType = isRegion();
+    final boolean mapType = seg.isMapped();
+    final boolean directType = seg.isNative();
+    final boolean nativeBOType = byteOrder == ByteOrder.nativeOrder();
+    final boolean byteBufferType = hasByteBuffer();
+    final int type = BUFFER | DUPLICATE
+        | (readOnly ? READONLY : 0)
+        | (regionType ? REGION : 0)
+        | (mapType ? MAP : 0)
+        | (directType ? DIRECT : 0)
+        | (nativeBOType ? NATIVE : NONNATIVE)
+        | (byteBufferType ? BYTEBUF : 0);
+
+    final WritableBuffer wbuf = selectBuffer(seg2, type, memReqSvr, byteBufferType, mapType, nativeBOType);
+    wbuf.setStartPositionEnd(getStart(), getPosition(), getEnd());
+    return wbuf;
+  }
+
+  //AS MEMORY DERIVED
+
+  @Override
+  public Memory asMemory(final ByteOrder byteOrder) {
+    return asWritableMemoryImpl(true, byteOrder);
+  }
+
+  @Override
+  public WritableMemory asWritableMemory(final ByteOrder byteOrder) {
+    if (isReadOnly()) {
+      throw new IllegalArgumentException(
+          "Cannot create a writable Memory from a read-only Buffer.");
+    }
+    return asWritableMemoryImpl(false, byteOrder);
+  }
+
+  private WritableMemory asWritableMemoryImpl(final boolean localReadOnly, final ByteOrder byteOrder) {
+    if (!this.isAlive()) { throw new IllegalStateException("This Buffer is not alive."); }
+    Objects.requireNonNull(byteOrder, "byteOrder must be non-null");
+    final boolean readOnly = isReadOnly() || localReadOnly;
+    final MemorySegment seg2 = (readOnly && !seg.isReadOnly()) ? seg.asReadOnly() : seg;
+    final boolean regionType = isRegion();
+    final boolean duplicateType = isDuplicate();
+    final boolean mapType = seg.isMapped();
+    final boolean directType = seg.isNative();
+    final boolean nativeBOType = byteOrder == ByteOrder.nativeOrder();
+    final boolean byteBufferType = hasByteBuffer();
+    final int type = MEMORY
+        | (readOnly ? READONLY : 0)
+        | (regionType ? REGION : 0)
+        | (duplicateType ? DUPLICATE : 0)
+        | (mapType ? MAP : 0)
+        | (directType ? DIRECT : 0)
+        | (nativeBOType ? NATIVE : NONNATIVE)
+        | (byteBufferType ? BYTEBUF : 0);
+
+    final WritableMemory wmem = selectMemory(seg2, type, memReqSvr, byteBufferType, mapType, nativeBOType);
+    return wmem;
+  }
+
+  //PRIMITIVE getX() and getXArray()
+
+  @Override
+  public final boolean getBoolean() {
+    return getByte() != 0;
+  }
+
+  @Override
+  public final boolean getBoolean(final long offsetBytes) {
+    return getByte(offsetBytes) != 0;
+  }
+
+  @Override
+  public final byte getByte() {
+    final long pos = getPosition();
+    final byte aByte = MemoryAccess.getByteAtOffset(seg, pos);
+    setPosition(pos + Byte.BYTES);
+    return aByte;
+  }
+
+  @Override
+  public final byte getByte(final long offsetBytes) {
+    return MemoryAccess.getByteAtOffset(seg, offsetBytes);
+  }
+
+  @Override
+  public final void getByteArray(final byte[] dstArray, final int dstOffsetBytes,
+      final int lengthBytes) {
+    final MemorySegment dstSlice = MemorySegment.ofArray(dstArray).asSlice(dstOffsetBytes, lengthBytes);
+    final long pos = getPosition();
+    final MemorySegment srcSlice = seg.asSlice(pos, lengthBytes);
+    dstSlice.copyFrom(srcSlice);
+    setPosition(pos + lengthBytes);
+  }
+
+  //OTHER PRIMITIVE READ METHODS: e.g., copyTo, compareTo. No writeTo
+
+  @Override
+  public final int compareTo(final long thisOffsetBytes, final long thisLengthBytes,
+      final Buffer that, final long thatOffsetBytes, final long thatLengthBytes) {
+    return CompareAndCopy.compare(seg, thisOffsetBytes, thisLengthBytes,
+        ((BaseStateImpl)that).seg, thatOffsetBytes, thatLengthBytes);
+  }
+
+  /*
+   * Developer notes: There is no copyTo for Buffers because of the ambiguity of what to do with
+   * the positional values. Switch to asMemory view to do copyTo.
+   */
+
+  //PRIMITIVE putX() and putXArray() implementations
+
+  @Override
+  public final void putBoolean(final boolean value) {
+    putByte(value ? (byte)1 : 0);
+  }
+
+  @Override
+  public final void putBoolean(final long offsetBytes, final boolean value) {
+    putByte(offsetBytes, value ? (byte)1 : 0);
+  }
+
+  @Override
+  public final void putByte(final byte value) {
+    final long pos = getPosition();
+    MemoryAccess.setByteAtOffset(seg, pos, value);
+    setPosition(pos + Byte.BYTES);
+  }
+
+  @Override
+  public final void putByte(final long offsetBytes, final byte value) {
+    MemoryAccess.setByteAtOffset(seg, offsetBytes, value);
+  }
+
+  @Override
+  public final void putByteArray(final byte[] srcArray, final int srcOffsetBytes,
+      final int lengthBytes) {
+    final MemorySegment srcSlice = MemorySegment.ofArray(srcArray).asSlice(srcOffsetBytes, lengthBytes);
+    final long pos = getPosition();
+    final MemorySegment dstSlice = seg.asSlice(pos, lengthBytes);
+    dstSlice.copyFrom(srcSlice);
+    setPosition(pos + lengthBytes);
+  }
+
+  //OTHER
+
+  @Override
+  public final void clear() {
+    seg.fill((byte)0);
+  }
+
+  @Override
+  public final void fill(final byte value) {
+    seg.fill(value);
+  }
+}
diff --git a/src/main/java17/org/apache/datasketches/memory/internal/BaseWritableMemoryImpl.java b/src/main/java17/org/apache/datasketches/memory/internal/BaseWritableMemoryImpl.java
new file mode 100644
index 0000000..c458d80
--- /dev/null
+++ b/src/main/java17/org/apache/datasketches/memory/internal/BaseWritableMemoryImpl.java
@@ -0,0 +1,372 @@
+/*
+ * 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.nio.channels.FileChannel.MapMode.READ_ONLY;
+import static java.nio.channels.FileChannel.MapMode.READ_WRITE;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.channels.FileChannel;
+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.WritableBuffer;
+import org.apache.datasketches.memory.WritableMemory;
+
+import jdk.incubator.foreign.MemoryAccess;
+import jdk.incubator.foreign.MemorySegment;
+import jdk.incubator.foreign.ResourceScope;
+
+/*
+ * Developer notes: The heavier methods, such as put/get arrays, duplicate, region, clear, fill,
+ * compareTo, etc., use hard checks (checkValid*() and checkBounds()), which execute at runtime and
+ * throw exceptions if violated. The cost of the runtime checks are minor compared to the rest of
+ * the work these methods are doing.
+ *
+ * <p>The light weight methods, such as put/get primitives, use asserts (assertValid*()), which only
+ * execute when asserts are enabled and JIT will remove them entirely from production runtime code.
+ * The light weight methods will simplify to a single unsafe call, which is further simplified by
+ * JIT to an intrinsic that is often a single CPU instruction.
+ */
+
+/**
+ * Common base of native-ordered and non-native-ordered {@link WritableMemory} implementations.
+ * Contains methods which are agnostic to the byte order.
+ */
+public abstract class BaseWritableMemoryImpl extends BaseStateImpl implements WritableMemory {
+
+  //Pass-through constructor
+  BaseWritableMemoryImpl(
+      final MemorySegment seg,
+      final int typeId,
+      final MemoryRequestServer memReqSvr) {
+    super(seg, typeId, memReqSvr);
+  }
+
+  //WRAP HEAP ARRAY RESOURCE
+
+  public static WritableMemory wrapSegmentAsArray(
+      final MemorySegment seg,
+      final ByteOrder byteOrder,
+      final MemoryRequestServer memReqSvr) {
+    Objects.requireNonNull(byteOrder, "byteOrder must be non-null");
+    int type = MEMORY
+        | (seg.isReadOnly() ? READONLY : 0);
+    if (byteOrder == NON_NATIVE_BYTE_ORDER) {
+      type |= NONNATIVE;
+      return new NonNativeWritableMemoryImpl(seg, type, memReqSvr);
+    }
+    return new NativeWritableMemoryImpl(seg, type, memReqSvr);
+  }
+
+  //BYTE BUFFER RESOURCE
+
+  public static WritableMemory 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 ByteBuffer byteBuf;
+    if (localReadOnly) {
+      if (byteBuffer.isReadOnly()) {
+        byteBuf = byteBuffer.duplicate();
+      } else { //bb writable
+        byteBuf = byteBuffer.asReadOnlyBuffer();
+      }
+    } else { //want writable
+      if (byteBuffer.isReadOnly()) {
+        throw new IllegalArgumentException("ByteBuffer must be writable.");
+      }
+      byteBuf = byteBuffer.duplicate();
+    }
+    byteBuf.clear(); //resets position to zero and limit to capacity. Does not clear data.
+    final MemorySegment seg = MemorySegment.ofByteBuffer(byteBuf); //from 0 to capacity
+    int type = MEMORY | BYTEBUF
+        | (localReadOnly ? READONLY : 0)
+        | (seg.isNative() ? DIRECT : 0)
+        | (seg.isMapped() ? MAP : 0);
+    final WritableMemory wmem;
+    if (byteOrder == NON_NATIVE_BYTE_ORDER) {
+      type |= NONNATIVE;
+      wmem = new NonNativeWritableMemoryImpl(seg, type, memReqSvr);
+    } else {
+      wmem = new NativeWritableMemoryImpl(seg, type, memReqSvr);
+    }
+    return wmem;
+  }
+
+  //MAP FILE RESOURCE
+
+  @SuppressWarnings("resource")
+  public static WritableMemory wrapMap(
+      final File file,
+      final long fileOffsetBytes,
+      final long capacityBytes,
+      final ResourceScope scope,
+      final boolean localReadOnly,
+      final ByteOrder byteOrder)
+          throws IllegalArgumentException, IllegalStateException, IOException, SecurityException {
+    Objects.requireNonNull(file, "File must be non-null.");
+    Objects.requireNonNull(byteOrder, "ByteOrder must be non-null.");
+    Objects.requireNonNull(scope, "ResourceScope must be non-null.");
+    if (!file.canRead()) { throw new IllegalArgumentException("File must be readable."); }
+    if (!localReadOnly && !file.canWrite()) { throw new IllegalArgumentException("File must be writable."); }
+    final FileChannel.MapMode mapMode = (localReadOnly) ? READ_ONLY : READ_WRITE;
+
+    final MemorySegment seg = MemorySegment.mapFile(file.toPath(), fileOffsetBytes, capacityBytes, mapMode, scope);
+    final boolean nativeBOType = byteOrder == ByteOrder.nativeOrder();
+    final int type = MEMORY | MAP | DIRECT
+        | (localReadOnly ? READONLY : 0)
+        | (nativeBOType ? NATIVE : NONNATIVE);
+    return nativeBOType
+        ? new NativeWritableMemoryImpl(seg, type, null)
+        : new NonNativeWritableMemoryImpl(seg, type, null);
+  }
+
+  //DIRECT RESOURCE
+
+  /**
+   * 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 &ge; 0.
+   * @param alignmentBytes requested segment alignment. Typically 1, 2, 4 or 8.
+   * @param scope ResourceScope for the backing MemorySegment.
+   * Typically <i>ResourceScope.newConfinedScope()</i>.
+   * @param byteOrder the byte order to be used.  It must be non-null.
+   * @param memReqSvr A user-specified MemoryRequestServer, which may be null.
+   * This is a callback mechanism for a user client of direct memory to request more memory.
+   * @return WritableMemory
+   */
+  @SuppressWarnings("resource")
+  public static WritableMemory wrapDirect(
+      final long capacityBytes,
+      final long alignmentBytes,
+      final ResourceScope scope,
+      final ByteOrder byteOrder,
+      final MemoryRequestServer memReqSvr) {
+    Objects.requireNonNull(scope, "ResourceScope must be non-null");
+    Objects.requireNonNull(byteOrder, "ByteOrder must be non-null");
+    final MemorySegment seg = MemorySegment.allocateNative(
+        capacityBytes,
+        alignmentBytes,
+        scope);
+    final boolean nativeBOType = byteOrder == ByteOrder.nativeOrder();
+    final int type = MEMORY | DIRECT
+        | (nativeBOType ? NATIVE : NONNATIVE);
+    return nativeBOType
+        ? new NativeWritableMemoryImpl(seg, type, memReqSvr)
+        : new NonNativeWritableMemoryImpl(seg, type, memReqSvr);
+  }
+
+  //REGION DERIVED
+
+  @Override
+  public Memory region(final long offsetBytes, final long capacityBytes, final ByteOrder byteOrder) {
+    return regionImpl(offsetBytes, capacityBytes, true, byteOrder);
+  }
+
+  @Override
+  public WritableMemory writableRegion(final long offsetBytes, final long capacityBytes, final ByteOrder byteOrder) {
+    if (this.isReadOnly()) {
+      throw new IllegalArgumentException("Cannot create a writable region from a read-only Memory.");
+    }
+    return regionImpl(offsetBytes, capacityBytes, false, byteOrder);
+  }
+
+  private WritableMemory regionImpl(
+      final long offsetBytes,
+      final long capacityBytes,
+      final boolean localReadOnly,
+      final ByteOrder byteOrder) {
+    if (!this.isAlive()) { throw new IllegalStateException("This Memory is not alive."); }
+    Objects.requireNonNull(byteOrder, "byteOrder must be non-null.");
+    final boolean readOnly = isReadOnly() || localReadOnly;
+    final MemorySegment slice = (readOnly && !seg.isReadOnly())
+        ? seg.asSlice(offsetBytes, capacityBytes).asReadOnly()
+        : seg.asSlice(offsetBytes, capacityBytes);
+    final boolean duplicateType = isDuplicate();
+    final boolean mapType = seg.isMapped();
+    final boolean directType = seg.isNative();
+    final boolean nativeBOType = byteOrder == ByteOrder.nativeOrder();
+    final boolean byteBufferType = hasByteBuffer();
+    final int type = MEMORY | REGION
+        | (readOnly ? READONLY : 0)
+        | (duplicateType ? DUPLICATE : 0)
+        | (mapType ? MAP : 0)
+        | (directType ? DIRECT : 0)
+        | (nativeBOType ? NATIVE : NONNATIVE)
+        | (byteBufferType ? BYTEBUF : 0);
+
+    final WritableMemory wmem = selectMemory(slice, type, memReqSvr, byteBufferType, mapType, nativeBOType);
+    return wmem;
+  }
+
+  //AS BUFFER DERIVED
+
+  @Override
+  public Buffer asBuffer(final ByteOrder byteOrder) {
+    return asWritableBufferImpl(true, byteOrder);
+  }
+
+  @Override
+  public WritableBuffer asWritableBuffer(final ByteOrder byteOrder) {
+    if (isReadOnly()) {
+      throw new IllegalArgumentException(
+          "Cannot create a writable buffer from a read-only Memory.");
+    }
+    return asWritableBufferImpl(false, byteOrder);
+  }
+
+  private WritableBuffer asWritableBufferImpl(final boolean localReadOnly, final ByteOrder byteOrder) {
+    if (!this.isAlive()) { throw new IllegalStateException("This Memory is not alive."); }
+    Objects.requireNonNull(byteOrder, "byteOrder must be non-null");
+    final boolean readOnly = isReadOnly() || localReadOnly;
+    final MemorySegment seg2 = (readOnly && !seg.isReadOnly()) ? seg.asReadOnly() : seg;
+    final boolean regionType = isRegion();
+    final boolean duplicateType = isDuplicate();
+    final boolean mapType = seg.isMapped();
+    final boolean directType = seg.isNative();
+    final boolean nativeBOType = byteOrder == ByteOrder.nativeOrder();
+    final boolean byteBufferType = hasByteBuffer();
+    final int type = BUFFER
+        | (readOnly ? READONLY : 0)
+        | (regionType ? REGION : 0)
+        | (duplicateType ? DUPLICATE : 0)
+        | (directType ? DIRECT : 0)
+        | (mapType ? MAP : 0)
+        | (nativeBOType ? NATIVE : NONNATIVE)
+        | (byteBufferType ? BYTEBUF : 0);
+
+    final WritableBuffer wbuf = selectBuffer(seg2, type, memReqSvr, byteBufferType, mapType, nativeBOType);
+    wbuf.setStartPositionEnd(0, 0, wbuf.getCapacity());
+    return wbuf;
+  }
+
+  //PRIMITIVE getX() and getXArray()
+
+  @Override
+  public final boolean getBoolean(final long offsetBytes) {
+    return getByte(offsetBytes) != 0;
+  }
+
+  @Override
+  public final byte getByte(final long offsetBytes) {
+    return MemoryAccess.getByteAtOffset(seg, offsetBytes);
+  }
+
+  @Override
+  public final void getByteArray(final long offsetBytes, final byte[] dstArray,
+      final int dstOffsetBytes, final int lengthBytes) {
+    checkBounds(dstOffsetBytes, lengthBytes, dstArray.length);
+    final MemorySegment srcSlice = seg.asSlice(offsetBytes, lengthBytes);
+    final MemorySegment dstSlice = MemorySegment.ofArray(dstArray).asSlice(dstOffsetBytes, lengthBytes);
+    dstSlice.copyFrom(srcSlice);
+  }
+
+  //OTHER PRIMITIVE READ METHODS: compareTo, copyTo, writeTo
+
+  @Override
+  public final int compareTo(final long thisOffsetBytes, final long thisLengthBytes,
+      final Memory that, final long thatOffsetBytes, final long thatLengthBytes) {
+    return CompareAndCopy.compare(seg, thisOffsetBytes, thisLengthBytes,
+        ((BaseStateImpl)that).seg, thatOffsetBytes, thatLengthBytes);
+  }
+
+  @Override
+  public final void copyTo(final long srcOffsetBytes,
+      final WritableMemory destination, final long dstOffsetBytes, final long lengthBytes) {
+    CompareAndCopy.copy(seg, srcOffsetBytes,
+        ((BaseStateImpl)destination).seg, dstOffsetBytes, lengthBytes);
+  }
+
+  @Override
+  public final void writeToByteStream(final long offsetBytes, final int lengthBytes,
+      final ByteArrayOutputStream out) throws IOException {
+    checkBounds(offsetBytes, lengthBytes, seg.byteSize());
+    final byte[] bArr = new byte[lengthBytes];
+    getByteArray(offsetBytes,bArr, 0, lengthBytes); //fundamental limitation of MemorySegment
+    out.writeBytes(bArr);
+  }
+
+  //  //PRIMITIVE putX() and putXArray() implementations
+
+  @Override
+  public final void putBoolean(final long offsetBytes, final boolean value) {
+    putByte(offsetBytes, value ? (byte)1 : 0);
+  }
+
+
+  @Override
+  public final void putByte(final long offsetBytes, final byte value) {
+    MemoryAccess.setByteAtOffset(seg, offsetBytes, value);
+  }
+
+  @Override
+  public final void putByteArray(final long offsetBytes, final byte[] srcArray,
+      final int srcOffsetBytes, final int lengthBytes) {
+    final MemorySegment srcSlice = MemorySegment.ofArray(srcArray).asSlice(srcOffsetBytes, lengthBytes);
+    final MemorySegment dstSlice = seg.asSlice(offsetBytes, lengthBytes);
+    dstSlice.copyFrom(srcSlice);
+  }
+
+  // OTHER WRITE METHODS
+
+  @Override
+  public final void clear() {
+    seg.fill((byte)0);
+  }
+
+  @Override
+  public final void clear(final long offsetBytes, final long lengthBytes) {
+    final MemorySegment slice = seg.asSlice(offsetBytes, lengthBytes);
+    slice.fill((byte)0);
+  }
+
+  @Override
+  public final void clearBits(final long offsetBytes, final byte bitMask) {
+    final byte b = MemoryAccess.getByteAtOffset(seg, offsetBytes);
+    MemoryAccess.setByteAtOffset(seg, offsetBytes, (byte)(b & ~bitMask));
+  }
+
+  @Override
+  public final void fill(final byte value) {
+    seg.fill(value);
+  }
+
+  @Override
+  public final void fill(final long offsetBytes, final long lengthBytes, final byte value) {
+    final MemorySegment slice = seg.asSlice(offsetBytes, lengthBytes);
+    slice.fill(value);
+  }
+
+  @Override
+  public final void setBits(final long offsetBytes, final byte bitMask) {
+    final byte b = MemoryAccess.getByteAtOffset(seg, offsetBytes);
+    MemoryAccess.setByteAtOffset(seg, offsetBytes, (byte)(b | bitMask));
+  }
+
+}
diff --git a/src/main/java17/org/apache/datasketches/memory/internal/CompareAndCopy.java b/src/main/java17/org/apache/datasketches/memory/internal/CompareAndCopy.java
new file mode 100644
index 0000000..d7f4e1a
--- /dev/null
+++ b/src/main/java17/org/apache/datasketches/memory/internal/CompareAndCopy.java
@@ -0,0 +1,69 @@
+/*
+ * 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 jdk.incubator.foreign.MemoryAccess.getByteAtOffset;
+
+import jdk.incubator.foreign.MemorySegment;
+
+/**
+ * @author Lee Rhodes
+ */
+final class CompareAndCopy {
+
+  private CompareAndCopy() { }
+
+  static int compare(
+      final MemorySegment seg1, final long offsetBytes1, final long lengthBytes1,
+      final MemorySegment seg2, final long offsetBytes2, final long lengthBytes2) {
+    final MemorySegment slice1 = seg1.asSlice(offsetBytes1, lengthBytes1);
+    final MemorySegment slice2 = seg2.asSlice(offsetBytes2, lengthBytes2);
+    final long mm = slice1.mismatch(slice2);
+    if (mm == -1) { return 0; }
+    if ((lengthBytes1 > mm) && (lengthBytes2 > mm)) {
+      return Integer.compare(
+          getByteAtOffset(slice1, mm) & 0XFF, getByteAtOffset(slice2, mm) & 0XFF);
+    }
+    if (lengthBytes1 == mm) { return -1; }
+    return +1;
+  }
+
+  static boolean equals(final MemorySegment seg1, final MemorySegment seg2) {
+    final long cap1 = seg1.byteSize();
+    final long cap2 = seg2.byteSize();
+    return (cap1 == cap2) && equals(seg1, 0, seg2, 0, cap1);
+  }
+
+  static boolean equals(
+      final MemorySegment seg1, final long offsetBytes1,
+      final MemorySegment seg2, final long offsetBytes2, final long lengthBytes) {
+    final MemorySegment slice1 = seg1.asSlice(offsetBytes1, lengthBytes);
+    final MemorySegment slice2 = seg2.asSlice(offsetBytes2, lengthBytes);
+    return slice1.mismatch(slice2) == -1;
+  }
+
+  static void copy(final MemorySegment srcSegment, final long srcOffsetBytes,
+      final MemorySegment dstSegment, final long dstOffsetBytes, final long lengthBytes) {
+    final MemorySegment srcSlice = srcSegment.asSlice(srcOffsetBytes, lengthBytes);
+    final MemorySegment dstSlice = dstSegment.asSlice(dstOffsetBytes, lengthBytes);
+    dstSlice.copyFrom(srcSlice);
+  }
+
+}
diff --git a/src/main/java17/org/apache/datasketches/memory/internal/MurmurHash3v3.java b/src/main/java17/org/apache/datasketches/memory/internal/MurmurHash3v3.java
new file mode 100644
index 0000000..c873120
--- /dev/null
+++ b/src/main/java17/org/apache/datasketches/memory/internal/MurmurHash3v3.java
@@ -0,0 +1,386 @@
+/*
+ * 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.nio.charset.StandardCharsets.UTF_8;
+
+import java.util.Objects;
+
+import org.apache.datasketches.memory.Memory;
+
+import jdk.incubator.foreign.MemoryAccess;
+import jdk.incubator.foreign.MemorySegment;
+
+/**
+ * <p>The MurmurHash3 is a fast, non-cryptographic, 128-bit hash function that has
+ * excellent avalanche and 2-way bit independence properties.</p>
+ *
+ * <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>
+ *
+ * <p>This version 3 of the implementation leverages the jdk.incubator.foreign package of JDK-17 in place of
+ * the Unsafe class.
+ *
+ * @author Lee Rhodes
+ */
+public final class MurmurHash3v3 {
+  private static final long C1 = 0x87c37b91114253d5L;
+  private static final long C2 = 0x4cf5ad432745937fL;
+
+  /**
+   * 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
+   * @throws IllegalArgumentException if input is empty or null
+   */
+  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(MemorySegment.ofArray(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
+   * @throws IllegalArgumentException if input is empty or null
+   */
+  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(MemorySegment.ofArray(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
+   * @throws IllegalArgumentException if input is empty or null
+   */
+  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(MemorySegment.ofArray(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
+   * @throws IllegalArgumentException if input is empty or null
+   */
+  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(MemorySegment.ofArray(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
+   * @throws IllegalArgumentException if input is empty or null
+   */
+  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(MemorySegment.ofArray(byteArr), 0L, byteArr.length, seed, hashOut);
+  }
+
+  //The main API calls
+
+  /**
+   * Returns a 128-bit hash of the input as a long array of size 2.
+   *
+   * @param mem The input 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.
+   */
+  public static long[] hash(final Memory mem, final long offsetBytes, final long lengthBytes,
+      final long seed, final long[] hashOut) {
+    Objects.requireNonNull(mem, "Input Memory is null");
+    final MemorySegment seg = ((BaseStateImpl)mem).seg;
+    return hash(seg, offsetBytes, lengthBytes, seed, hashOut);
+  }
+
+  /**
+   * Returns a 128-bit hash of the input as a long array of size 2.
+   *
+   * @param seg The input MemorySegment. 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.
+   * @throws IllegalArgumentException if input MemorySegment is empty
+   */
+  public static long[] hash(final MemorySegment seg, final long offsetBytes, final long lengthBytes,
+      final long seed, final long[] hashOut) {
+    Objects.requireNonNull(seg, "Input MemorySegment is null");
+    if (seg.byteSize() == 0L) { throw new IllegalArgumentException("Input MemorySegment is empty."); }
+
+    long cumOff = 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 = MemoryAccess.getLongAtOffset(seg, cumOff);     //0, 16, 32, ...
+      final long k2 = MemoryAccess.getLongAtOffset(seg, 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 ^= (MemoryAccess.getByteAtOffset(seg, cumOff + 14) & 0xFFL) << 48;
+        }
+        //$FALL-THROUGH$
+        case 14: {
+          k2 ^= (MemoryAccess.getShortAtOffset(seg, cumOff + 12) & 0xFFFFL) << 32;
+          k2 ^= (MemoryAccess.getIntAtOffset(seg, cumOff + 8) & 0xFFFFFFFFL);
+          k1 = MemoryAccess.getLongAtOffset(seg, cumOff);
+          break;
+        }
+
+        case 13: {
+          k2 ^= (MemoryAccess.getByteAtOffset(seg, cumOff + 12) & 0xFFL) << 32;
+        }
+        //$FALL-THROUGH$
+        case 12: {
+          k2 ^= (MemoryAccess.getIntAtOffset(seg, cumOff + 8) & 0xFFFFFFFFL);
+          k1 = MemoryAccess.getLongAtOffset(seg, cumOff);
+          break;
+        }
+
+        case 11: {
+          k2 ^= (MemoryAccess.getByteAtOffset(seg, cumOff + 10) & 0xFFL) << 16;
+        }
+        //$FALL-THROUGH$
+        case 10: {
+          k2 ^= (MemoryAccess.getShortAtOffset(seg, cumOff +  8) & 0xFFFFL);
+          k1 = MemoryAccess.getLongAtOffset(seg, cumOff);
+          break;
+        }
+
+        case  9: {
+          k2 ^= (MemoryAccess.getByteAtOffset(seg, cumOff +  8) & 0xFFL);
+        }
+        //$FALL-THROUGH$
+        case  8: {
+          k1 = MemoryAccess.getLongAtOffset(seg, cumOff);
+          break;
+        }
+
+        case  7: {
+          k1 ^= (MemoryAccess.getByteAtOffset(seg, cumOff +  6) & 0xFFL) << 48;
+        }
+        //$FALL-THROUGH$
+        case  6: {
+          k1 ^= (MemoryAccess.getShortAtOffset(seg, cumOff +  4) & 0xFFFFL) << 32;
+          k1 ^= (MemoryAccess.getIntAtOffset(seg, cumOff) & 0xFFFFFFFFL);
+          break;
+        }
+
+        case  5: {
+          k1 ^= (MemoryAccess.getByteAtOffset(seg, cumOff +  4) & 0xFFL) << 32;
+        }
+        //$FALL-THROUGH$
+        case  4: {
+          k1 ^= (MemoryAccess.getIntAtOffset(seg, cumOff) & 0xFFFFFFFFL);
+          break;
+        }
+
+        case  3: {
+          k1 ^= (MemoryAccess.getByteAtOffset(seg, cumOff +  2) & 0xFFL) << 16;
+        }
+        //$FALL-THROUGH$
+        case  2: {
+          k1 ^= (MemoryAccess.getShortAtOffset(seg, cumOff) & 0xFFFFL);
+          break;
+        }
+
+        case  1: {
+          k1 ^= (MemoryAccess.getByteAtOffset(seg, 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/src/main/java17/org/apache/datasketches/memory/internal/NativeWritableBufferImpl.java b/src/main/java17/org/apache/datasketches/memory/internal/NativeWritableBufferImpl.java
new file mode 100644
index 0000000..c622766
--- /dev/null
+++ b/src/main/java17/org/apache/datasketches/memory/internal/NativeWritableBufferImpl.java
@@ -0,0 +1,321 @@
+/*
+ * 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.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableBuffer;
+
+import jdk.incubator.foreign.MemoryAccess;
+import jdk.incubator.foreign.MemorySegment;
+
+/*
+ * Developer notes: The heavier methods, such as put/get arrays, duplicate, region, clear, fill,
+ * compareTo, etc., use hard checks (check*() and incrementAndCheck*() methods), which execute at
+ * runtime and throw exceptions if violated. The cost of the runtime checks are minor compared to
+ * the rest of the work these methods are doing.
+ *
+ * <p>The light weight methods, such as put/get primitives, use asserts (assert*() and
+ * incrementAndAssert*() methods), which only execute when asserts are enabled and JIT will remove
+ * them entirely from production runtime code. The offset versions of the light weight methods will
+ * simplify to a single unsafe call, which is further simplified by JIT to an intrinsic that is
+ * often a single CPU instruction.
+ */
+
+/**
+ * Implementation of {@link WritableBuffer} for native endian byte order.
+ * @author Roman Leventov
+ * @author Lee Rhodes
+ */
+final class NativeWritableBufferImpl extends BaseWritableBufferImpl {
+
+  NativeWritableBufferImpl(
+      final MemorySegment seg,
+      final int typeId,
+      final MemoryRequestServer memReqSvr) {
+    super(seg, typeId, memReqSvr);
+  }
+
+  //PRIMITIVE getX() and getXArray()
+  @Override
+  public char getChar() {
+    final long pos = getPosition();
+    setPosition(pos + Character.BYTES);
+    return MemoryAccess.getCharAtOffset(seg, pos);
+  }
+
+  @Override
+  public char getChar(final long offsetBytes) {
+    return MemoryAccess.getCharAtOffset(seg, 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;
+    final MemorySegment srcSlice = seg.asSlice(pos, copyBytes);
+    final MemorySegment dstSlice = MemorySegment.ofArray(dstArray).asSlice(dstOffsetChars << CHAR_SHIFT, copyBytes);
+    dstSlice.copyFrom(srcSlice);
+    setPosition(pos + copyBytes);
+  }
+
+  @Override
+  public double getDouble() {
+    final long pos = getPosition();
+    setPosition(pos + Double.BYTES);
+    return MemoryAccess.getDoubleAtOffset(seg, pos);
+  }
+
+  @Override
+  public double getDouble(final long offsetBytes) {
+    return MemoryAccess.getDoubleAtOffset(seg, 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;
+    final MemorySegment srcSlice = seg.asSlice(pos, copyBytes);
+    final MemorySegment dstSlice = MemorySegment.ofArray(dstArray).asSlice(dstOffsetDoubles << DOUBLE_SHIFT, copyBytes);
+    dstSlice.copyFrom(srcSlice);
+    setPosition(pos + copyBytes);
+  }
+
+  @Override
+  public float getFloat() {
+    final long pos = getPosition();
+    setPosition(pos + Float.BYTES);
+    return MemoryAccess.getFloatAtOffset(seg, pos);
+  }
+
+  @Override
+  public float getFloat(final long offsetBytes) {
+    return MemoryAccess.getFloatAtOffset(seg, 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;
+    final MemorySegment srcSlice = seg.asSlice(pos, copyBytes);
+    final MemorySegment dstSlice = MemorySegment.ofArray(dstArray).asSlice(dstOffsetFloats << FLOAT_SHIFT, copyBytes);
+    dstSlice.copyFrom(srcSlice);
+    setPosition(pos + copyBytes);
+  }
+
+  @Override
+  public int getInt() {
+    final long pos = getPosition();
+    setPosition(pos + Integer.BYTES);
+    return MemoryAccess.getIntAtOffset(seg, pos);
+  }
+
+  @Override
+  public int getInt(final long offsetBytes) {
+    return MemoryAccess.getIntAtOffset(seg, 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;
+    final MemorySegment srcSlice = seg.asSlice(pos, copyBytes);
+    final MemorySegment dstSlice = MemorySegment.ofArray(dstArray).asSlice(dstOffsetInts << INT_SHIFT, copyBytes);
+    dstSlice.copyFrom(srcSlice);
+    setPosition(pos + copyBytes);
+  }
+
+  @Override
+  public long getLong() {
+    final long pos = getPosition();
+    setPosition(pos + Long.BYTES);
+    return MemoryAccess.getLongAtOffset(seg, pos);
+  }
+
+  @Override
+  public long getLong(final long offsetBytes) {
+    return MemoryAccess.getLongAtOffset(seg, 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;
+    final MemorySegment srcSlice = seg.asSlice(pos, copyBytes);
+    final MemorySegment dstSlice = MemorySegment.ofArray(dstArray).asSlice(dstOffsetLongs << LONG_SHIFT, copyBytes);
+    dstSlice.copyFrom(srcSlice);
+    setPosition(pos + copyBytes);
+  }
+
+  @Override
+  public short getShort() {
+    final long pos = getPosition();
+    setPosition(pos + Short.BYTES);
+    return MemoryAccess.getShortAtOffset(seg, pos);
+  }
+
+  @Override
+  public short getShort(final long offsetBytes) {
+    return MemoryAccess.getShortAtOffset(seg, 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;
+    final MemorySegment srcSlice = seg.asSlice(pos, copyBytes);
+    final MemorySegment dstSlice = MemorySegment.ofArray(dstArray).asSlice(dstOffsetShorts << SHORT_SHIFT, copyBytes);
+    dstSlice.copyFrom(srcSlice);
+    setPosition(pos + copyBytes);
+  }
+
+  //PRIMITIVE putX() and putXArray()
+  @Override
+  public void putChar(final char value) {
+    final long pos = getPosition();
+    setPosition(pos + Character.BYTES);
+    MemoryAccess.setCharAtOffset(seg, pos, value);
+  }
+
+  @Override
+  public void putChar(final long offsetBytes, final char value) {
+    MemoryAccess.setCharAtOffset(seg, 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;
+    final MemorySegment srcSlice = MemorySegment.ofArray(srcArray).asSlice(srcOffsetChars << CHAR_SHIFT, copyBytes);
+    final MemorySegment dstSlice = seg.asSlice(pos, copyBytes);
+    dstSlice.copyFrom(srcSlice);
+    setPosition(pos + copyBytes);
+  }
+
+  @Override
+  public void putDouble(final double value) {
+    final long pos = getPosition();
+    setPosition(pos + Double.BYTES);
+    MemoryAccess.setDoubleAtOffset(seg, pos, value);
+  }
+
+  @Override
+  public void putDouble(final long offsetBytes, final double value) {
+    MemoryAccess.setDoubleAtOffset(seg, 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;
+    final MemorySegment srcSlice = MemorySegment.ofArray(srcArray).asSlice(srcOffsetDoubles << DOUBLE_SHIFT, copyBytes);
+    final MemorySegment dstSlice = seg.asSlice(pos, copyBytes);
+    dstSlice.copyFrom(srcSlice);
+    setPosition(pos + copyBytes);
+  }
+
+  @Override
+  public void putFloat(final float value) {
+    final long pos = getPosition();
+    setPosition(pos + Float.BYTES);
+    MemoryAccess.setFloatAtOffset(seg, pos, value);
+  }
+
+  @Override
+  public void putFloat(final long offsetBytes, final float value) {
+    MemoryAccess.setFloatAtOffset(seg, 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;
+    final MemorySegment srcSlice = MemorySegment.ofArray(srcArray).asSlice(srcOffsetFloats << FLOAT_SHIFT, copyBytes);
+    final MemorySegment dstSlice = seg.asSlice(pos, copyBytes);
+    dstSlice.copyFrom(srcSlice);
+    setPosition(pos + copyBytes);
+  }
+
+  @Override
+  public void putInt(final int value) {
+    final long pos = getPosition();
+    setPosition(pos + Integer.BYTES);
+    MemoryAccess.setIntAtOffset(seg, pos, value);
+  }
+
+  @Override
+  public void putInt(final long offsetBytes, final int value) {
+    MemoryAccess.setIntAtOffset(seg, 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;
+    final MemorySegment srcSlice = MemorySegment.ofArray(srcArray).asSlice(srcOffsetInts << INT_SHIFT, copyBytes);
+    final MemorySegment dstSlice = seg.asSlice(pos, copyBytes);
+    dstSlice.copyFrom(srcSlice);
+    setPosition(pos + copyBytes);
+  }
+
+  @Override
+  public void putLong(final long value) {
+    final long pos = getPosition();
+    setPosition(pos + Long.BYTES);
+    MemoryAccess.setLongAtOffset(seg, pos, value);
+  }
+
+  @Override
+  public void putLong(final long offsetBytes, final long value) {
+    MemoryAccess.setLongAtOffset(seg, 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;
+    final MemorySegment srcSlice = MemorySegment.ofArray(srcArray).asSlice(srcOffsetLongs << LONG_SHIFT, copyBytes);
+    final MemorySegment dstSlice = seg.asSlice(pos, copyBytes);
+    dstSlice.copyFrom(srcSlice);
+    setPosition(pos + copyBytes);
+  }
+
+  @Override
+  public void putShort(final short value) {
+    final long pos = getPosition();
+    setPosition(pos + Short.BYTES);
+    MemoryAccess.setShortAtOffset(seg, pos, value);
+  }
+
+  @Override
+  public void putShort(final long offsetBytes, final short value) {
+    MemoryAccess.setShortAtOffset(seg, 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;
+    final MemorySegment srcSlice = MemorySegment.ofArray(srcArray).asSlice(srcOffsetShorts << SHORT_SHIFT, copyBytes);
+    final MemorySegment dstSlice = seg.asSlice(pos, copyBytes);
+    dstSlice.copyFrom(srcSlice);
+    setPosition(pos + copyBytes);
+  }
+}
diff --git a/src/main/java17/org/apache/datasketches/memory/internal/NativeWritableMemoryImpl.java b/src/main/java17/org/apache/datasketches/memory/internal/NativeWritableMemoryImpl.java
new file mode 100644
index 0000000..41cb9b6
--- /dev/null
+++ b/src/main/java17/org/apache/datasketches/memory/internal/NativeWritableMemoryImpl.java
@@ -0,0 +1,225 @@
+/*
+ * 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.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableMemory;
+
+import jdk.incubator.foreign.MemoryAccess;
+import jdk.incubator.foreign.MemorySegment;
+
+/*
+ * Developer notes: The heavier methods, such as put/get arrays, duplicate, region, clear, fill,
+ * compareTo, etc., use hard checks (checkValid*() and checkBounds()), which execute at runtime and
+ * throw exceptions if violated. The cost of the runtime checks are minor compared to the rest of
+ * the work these methods are doing.
+ *
+ * <p>The light weight methods, such as put/get primitives, use asserts (assertValid*()), which only
+ * execute when asserts are enabled and JIT will remove them entirely from production runtime code.
+ * The light weight methods will simplify to a single unsafe call, which is further simplified by
+ * JIT to an intrinsic that is often a single CPU instruction.
+ */
+
+/**
+ * Implementation of {@link WritableMemory} for native endian byte order.
+ * @author Roman Leventov
+ * @author Lee Rhodes
+ */
+final class NativeWritableMemoryImpl extends BaseWritableMemoryImpl {
+
+  //Pass-through ctor
+  NativeWritableMemoryImpl(
+      final MemorySegment seg,
+      final int typeId,
+      final MemoryRequestServer memReqSvr) {
+    super(seg, typeId, memReqSvr);
+  }
+
+  ///PRIMITIVE getX() and getXArray()
+  @Override
+  public char getChar(final long offsetBytes) {
+    return MemoryAccess.getCharAtOffset(seg, offsetBytes);
+  }
+
+  @Override
+  public void getCharArray(final long offsetBytes, final char[] dstArray, final int dstOffsetChars,
+      final int lengthChars) {
+    final long copyBytes = ((long) lengthChars) << CHAR_SHIFT;
+    final MemorySegment srcSlice = seg.asSlice(offsetBytes, copyBytes);
+    final MemorySegment dstSlice = MemorySegment.ofArray(dstArray).asSlice(dstOffsetChars << CHAR_SHIFT, copyBytes);
+    dstSlice.copyFrom(srcSlice);
+  }
+
+  @Override
+  public double getDouble(final long offsetBytes) {
+    return MemoryAccess.getDoubleAtOffset(seg, offsetBytes);
+  }
+
+  @Override
+  public void getDoubleArray(final long offsetBytes, final double[] dstArray,
+      final int dstOffsetDoubles, final int lengthDoubles) {
+    final long copyBytes = ((long) lengthDoubles) << DOUBLE_SHIFT;
+    final MemorySegment srcSlice = seg.asSlice(offsetBytes, copyBytes);
+    final MemorySegment dstSlice = MemorySegment.ofArray(dstArray).asSlice(dstOffsetDoubles << DOUBLE_SHIFT, copyBytes);
+    dstSlice.copyFrom(srcSlice);
+  }
+
+  @Override
+  public float getFloat(final long offsetBytes) {
+    return MemoryAccess.getFloatAtOffset(seg, offsetBytes);
+  }
+
+  @Override
+  public void getFloatArray(final long offsetBytes, final float[] dstArray,
+      final int dstOffsetFloats, final int lengthFloats) {
+    final long copyBytes = ((long) lengthFloats) << FLOAT_SHIFT;
+    final MemorySegment srcSlice = seg.asSlice(offsetBytes, copyBytes);
+    final MemorySegment dstSlice = MemorySegment.ofArray(dstArray).asSlice(dstOffsetFloats << FLOAT_SHIFT, copyBytes);
+    dstSlice.copyFrom(srcSlice);
+  }
+
+  @Override
+  public int getInt(final long offsetBytes) {
+    return MemoryAccess.getIntAtOffset(seg, offsetBytes);
+  }
+
+  @Override
+  public void getIntArray(final long offsetBytes, final int[] dstArray, final int dstOffsetInts,
+      final int lengthInts) {
+    final long copyBytes = ((long) lengthInts) << INT_SHIFT;
+    final MemorySegment srcSlice = seg.asSlice(offsetBytes, copyBytes);
+    final MemorySegment dstSlice = MemorySegment.ofArray(dstArray).asSlice(dstOffsetInts << INT_SHIFT, copyBytes);
+    dstSlice.copyFrom(srcSlice);
+  }
+
+  @Override
+  public long getLong(final long offsetBytes) {
+    return MemoryAccess.getLongAtOffset(seg, offsetBytes);
+  }
+
+  @Override
+  public void getLongArray(final long offsetBytes, final long[] dstArray,
+      final int dstOffsetLongs, final int lengthLongs) {
+    final long copyBytes = ((long) lengthLongs) << LONG_SHIFT;
+    final MemorySegment srcSlice = seg.asSlice(offsetBytes, copyBytes);
+    final MemorySegment dstSlice = MemorySegment.ofArray(dstArray).asSlice(dstOffsetLongs << LONG_SHIFT, copyBytes);
+    dstSlice.copyFrom(srcSlice);
+  }
+
+  @Override
+  public short getShort(final long offsetBytes) {
+    return MemoryAccess.getShortAtOffset(seg, offsetBytes);
+  }
+
+  @Override
+  public void getShortArray(final long offsetBytes, final short[] dstArray,
+      final int dstOffsetShorts, final int lengthShorts) {
+    final long copyBytes = ((long) lengthShorts) << SHORT_SHIFT;
+    final MemorySegment srcSlice = seg.asSlice(offsetBytes, copyBytes);
+    final MemorySegment dstSlice = MemorySegment.ofArray(dstArray).asSlice(dstOffsetShorts << SHORT_SHIFT, copyBytes);
+    dstSlice.copyFrom(srcSlice);
+  }
+
+  //PRIMITIVE putX() and putXArray() implementations
+  @Override
+  public void putChar(final long offsetBytes, final char value) {
+    MemoryAccess.setCharAtOffset(seg, 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;
+    final MemorySegment srcSlice = MemorySegment.ofArray(srcArray).asSlice(srcOffsetChars << CHAR_SHIFT, copyBytes);
+    final MemorySegment dstSlice = seg.asSlice(offsetBytes, copyBytes);
+    dstSlice.copyFrom(srcSlice);
+  }
+
+  @Override
+  public void putDouble(final long offsetBytes, final double value) {
+    MemoryAccess.setDoubleAtOffset(seg, 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;
+    final MemorySegment srcSlice = MemorySegment.ofArray(srcArray).asSlice(srcOffsetDoubles << DOUBLE_SHIFT, copyBytes);
+    final MemorySegment dstSlice = seg.asSlice(offsetBytes, copyBytes);
+    dstSlice.copyFrom(srcSlice);
+  }
+
+  @Override
+  public void putFloat(final long offsetBytes, final float value) {
+    MemoryAccess.setFloatAtOffset(seg, 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;
+    final MemorySegment srcSlice = MemorySegment.ofArray(srcArray).asSlice(srcOffsetFloats << FLOAT_SHIFT, copyBytes);
+    final MemorySegment dstSlice = seg.asSlice(offsetBytes, copyBytes);
+    dstSlice.copyFrom(srcSlice);
+  }
+
+  @Override
+  public void putInt(final long offsetBytes, final int value) {
+    MemoryAccess.setIntAtOffset(seg, 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;
+    final MemorySegment srcSlice = MemorySegment.ofArray(srcArray).asSlice(srcOffsetInts << INT_SHIFT, copyBytes);
+    final MemorySegment dstSlice = seg.asSlice(offsetBytes, copyBytes);
+    dstSlice.copyFrom(srcSlice);
+  }
+
+  @Override
+  public void putLong(final long offsetBytes, final long value) {
+    MemoryAccess.setLongAtOffset(seg, 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;
+    final MemorySegment srcSlice = MemorySegment.ofArray(srcArray).asSlice(srcOffsetLongs << LONG_SHIFT, copyBytes);
+    final MemorySegment dstSlice = seg.asSlice(offsetBytes, copyBytes);
+    dstSlice.copyFrom(srcSlice);
+  }
+
+  @Override
+  public void putShort(final long offsetBytes, final short value) {
+    MemoryAccess.setShortAtOffset(seg, 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;
+    final MemorySegment srcSlice = MemorySegment.ofArray(srcArray).asSlice(srcOffsetShorts << SHORT_SHIFT, copyBytes);
+    final MemorySegment dstSlice = seg.asSlice(offsetBytes, copyBytes);
+    dstSlice.copyFrom(srcSlice);
+  }
+
+}
diff --git a/src/main/java17/org/apache/datasketches/memory/internal/NonNativeWritableBufferImpl.java b/src/main/java17/org/apache/datasketches/memory/internal/NonNativeWritableBufferImpl.java
new file mode 100644
index 0000000..a27878e
--- /dev/null
+++ b/src/main/java17/org/apache/datasketches/memory/internal/NonNativeWritableBufferImpl.java
@@ -0,0 +1,364 @@
+/*
+ * 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.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableBuffer;
+
+import jdk.incubator.foreign.MemoryAccess;
+import jdk.incubator.foreign.MemorySegment;
+
+/*
+ * Developer notes: The heavier methods, such as put/get arrays, duplicate, region, clear, fill,
+ * compareTo, etc., use hard checks (check*() and incrementAndCheck*() methods), which execute at
+ * runtime and throw exceptions if violated. The cost of the runtime checks are minor compared to
+ * the rest of the work these methods are doing.
+ *
+ * <p>The light weight methods, such as put/get primitives, use asserts (assert*() and
+ * incrementAndAssert*() methods), which only execute when asserts are enabled and JIT will remove
+ * them entirely from production runtime code. The offset versions of the light weight methods will
+ * simplify to a single unsafe call, which is further simplified by JIT to an intrinsic that is
+ * often a single CPU instruction.
+ */
+
+/**
+ * Implementation of {@link WritableBuffer} for non-native endian byte order.
+ * @author Roman Leventov
+ * @author Lee Rhodes
+ */
+final class NonNativeWritableBufferImpl extends BaseWritableBufferImpl {
+
+  //Pass-through ctor
+  NonNativeWritableBufferImpl(
+      final MemorySegment seg,
+      final int typeId,
+      final MemoryRequestServer memReqSvr) {
+    super(seg, typeId, memReqSvr);
+  }
+
+  //PRIMITIVE getX() and getXArray()
+  @Override
+  public char getChar() {
+    final long pos = getPosition();
+    setPosition(pos + Character.BYTES);
+    return MemoryAccess.getCharAtOffset(seg, pos, NON_NATIVE_BYTE_ORDER);
+  }
+
+  @Override
+  public char getChar(final long offsetBytes) {
+    return MemoryAccess.getCharAtOffset(seg, offsetBytes, NON_NATIVE_BYTE_ORDER);
+  }
+
+  @Override
+  public void getCharArray(final char[] dstArray, final int dstOffsetChars, final int lengthChars) {
+    final long pos = getPosition();
+    final long copyBytes = ((long) lengthChars) << CHAR_SHIFT;
+    final MemorySegment srcSlice = seg.asSlice(pos, copyBytes);
+    final MemorySegment dstSlice = MemorySegment.ofArray(dstArray).asSlice(dstOffsetChars << CHAR_SHIFT, copyBytes);
+    for (int index = 0; index < lengthChars; index++) {
+      final char aChar = MemoryAccess.getCharAtIndex(srcSlice, index,  NON_NATIVE_BYTE_ORDER);
+      MemoryAccess.setCharAtIndex(dstSlice, index, NATIVE_BYTE_ORDER, aChar);
+    }
+    setPosition(pos + copyBytes);
+  }
+
+  @Override
+  public double getDouble() {
+    final long pos = getPosition();
+    setPosition(pos + Double.BYTES);
+    return MemoryAccess.getDoubleAtOffset(seg, pos, NON_NATIVE_BYTE_ORDER);
+  }
+
+  @Override
+  public double getDouble(final long offsetBytes) {
+    return MemoryAccess.getDoubleAtOffset(seg, offsetBytes, NON_NATIVE_BYTE_ORDER);
+  }
+
+  @Override
+  public void getDoubleArray(final double[] dstArray, final int dstOffsetDoubles,
+      final int lengthDoubles) {
+    final long pos = getPosition();
+    final long copyBytes = ((long) lengthDoubles) << DOUBLE_SHIFT;
+    final MemorySegment srcSlice = seg.asSlice(pos, copyBytes);
+    final MemorySegment dstSlice = MemorySegment.ofArray(dstArray).asSlice(dstOffsetDoubles << DOUBLE_SHIFT, copyBytes);
+    for (int index = 0; index < lengthDoubles; index++) {
+      final double dbl = MemoryAccess.getDoubleAtIndex(srcSlice, index,  NON_NATIVE_BYTE_ORDER);
+      MemoryAccess.setDoubleAtIndex(dstSlice, index, NATIVE_BYTE_ORDER, dbl);
+    }
+    setPosition(pos + copyBytes);
+  }
+
+  @Override
+  public float getFloat() {
+    final long pos = getPosition();
+    setPosition(pos + Float.BYTES);
+    return MemoryAccess.getFloatAtOffset(seg, pos, NON_NATIVE_BYTE_ORDER);
+  }
+
+  @Override
+  public float getFloat(final long offsetBytes) {
+    return MemoryAccess.getFloatAtOffset(seg, offsetBytes, NON_NATIVE_BYTE_ORDER);
+  }
+
+  @Override
+  public void getFloatArray(final float[] dstArray, final int dstOffsetFloats,
+      final int lengthFloats) {
+    final long pos = getPosition();
+    final long copyBytes = ((long) lengthFloats) << FLOAT_SHIFT;
+    final MemorySegment srcSlice = seg.asSlice(pos, copyBytes);
+    final MemorySegment dstSlice = MemorySegment.ofArray(dstArray).asSlice(dstOffsetFloats << FLOAT_SHIFT, copyBytes);
+    for (int index = 0; index < lengthFloats; index++) {
+      final float flt = MemoryAccess.getFloatAtIndex(srcSlice, index,  NON_NATIVE_BYTE_ORDER);
+      MemoryAccess.setFloatAtIndex(dstSlice, index, NATIVE_BYTE_ORDER, flt);
+    }
+    setPosition(pos + copyBytes);
+  }
+
+  @Override
+  public int getInt() {
+    final long pos = getPosition();
+    setPosition(pos + Integer.BYTES);
+    return MemoryAccess.getIntAtOffset(seg, pos, NON_NATIVE_BYTE_ORDER);
+  }
+
+  @Override
+  public int getInt(final long offsetBytes) {
+    return MemoryAccess.getIntAtOffset(seg, offsetBytes, NON_NATIVE_BYTE_ORDER);
+  }
+
+  @Override
+  public void getIntArray(final int[] dstArray, final int dstOffsetInts, final int lengthInts) {
+    final long pos = getPosition();
+    final long copyBytes = ((long) lengthInts) << INT_SHIFT;
+    final MemorySegment srcSlice = seg.asSlice(pos, copyBytes);
+    final MemorySegment dstSlice = MemorySegment.ofArray(dstArray).asSlice(dstOffsetInts << INT_SHIFT, copyBytes);
+    for (int index = 0; index < lengthInts; index++) {
+      final int anInt = MemoryAccess.getIntAtIndex(srcSlice, index,  NON_NATIVE_BYTE_ORDER);
+      MemoryAccess.setIntAtIndex(dstSlice, index, NATIVE_BYTE_ORDER, anInt);
+    }
+    setPosition(pos + copyBytes);
+  }
+
+  @Override
+  public long getLong() {
+    final long pos = getPosition();
+    setPosition(pos + Long.BYTES);
+    return MemoryAccess.getLongAtOffset(seg, pos, NON_NATIVE_BYTE_ORDER);
+  }
+
+  @Override
+  public long getLong(final long offsetBytes) {
+    return MemoryAccess.getLongAtOffset(seg, offsetBytes, NON_NATIVE_BYTE_ORDER);
+  }
+
+  @Override
+  public void getLongArray(final long[] dstArray, final int dstOffsetLongs, final int lengthLongs) {
+    final long pos = getPosition();
+    final long copyBytes = ((long) lengthLongs) << LONG_SHIFT;
+    final MemorySegment srcSlice = seg.asSlice(pos, copyBytes);
+    final MemorySegment dstSlice = MemorySegment.ofArray(dstArray).asSlice(dstOffsetLongs << LONG_SHIFT, copyBytes);
+    for (int index = 0; index < lengthLongs; index++) {
+      final long aLong = MemoryAccess.getLongAtIndex(srcSlice, index,  NON_NATIVE_BYTE_ORDER);
+      MemoryAccess.setLongAtIndex(dstSlice, index, NATIVE_BYTE_ORDER, aLong);
+    }
+    setPosition(pos + copyBytes);
+  }
+
+  @Override
+  public short getShort() {
+    final long pos = getPosition();
+    setPosition(pos + Short.BYTES);
+    return MemoryAccess.getShortAtOffset(seg, pos, NON_NATIVE_BYTE_ORDER);
+  }
+
+  @Override
+  public short getShort(final long offsetBytes) {
+    return MemoryAccess.getShortAtOffset(seg, offsetBytes, NON_NATIVE_BYTE_ORDER);
+  }
+
+  @Override
+  public void getShortArray(final short[] dstArray, final int dstOffsetShorts,
+      final int lengthShorts) {
+    final long pos = getPosition();
+    final long copyBytes = ((long) lengthShorts) << SHORT_SHIFT;
+    final MemorySegment srcSlice = seg.asSlice(pos, copyBytes);
+    final MemorySegment dstSlice = MemorySegment.ofArray(dstArray).asSlice(dstOffsetShorts << SHORT_SHIFT, copyBytes);
+    for (int index = 0; index < lengthShorts; index++) {
+      final short aShort = MemoryAccess.getShortAtIndex(srcSlice, index,  NON_NATIVE_BYTE_ORDER);
+      MemoryAccess.setShortAtIndex(dstSlice, index, NATIVE_BYTE_ORDER, aShort);
+    }
+    setPosition(pos + copyBytes);
+  }
+
+  //PRIMITIVE putX() and putXArray()
+  @Override
+  public void putChar(final char value) {
+    final long pos = getPosition();
+    MemoryAccess.setCharAtOffset(seg, pos, NON_NATIVE_BYTE_ORDER, value);
+    setPosition(pos + Character.BYTES);
+  }
+
+  @Override
+  public void putChar(final long offsetBytes, final char value) {
+    MemoryAccess.setCharAtOffset(seg, offsetBytes, NON_NATIVE_BYTE_ORDER, 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;
+    final MemorySegment srcSlice = MemorySegment.ofArray(srcArray).asSlice(srcOffsetChars << CHAR_SHIFT, copyBytes);
+    final MemorySegment dstSlice = seg.asSlice(pos, copyBytes);
+    for (int index = 0; index < lengthChars; index++) {
+      final char aChar = MemoryAccess.getCharAtIndex(srcSlice, index,  NATIVE_BYTE_ORDER);
+      MemoryAccess.setCharAtIndex(dstSlice, index, NON_NATIVE_BYTE_ORDER, aChar);
+    }
+    setPosition(pos + copyBytes);
+  }
+
+  @Override
+  public void putDouble(final double value) {
+    final long pos = getPosition();
+    MemoryAccess.setDoubleAtOffset(seg, pos, NON_NATIVE_BYTE_ORDER, value);
+    setPosition(pos + Double.BYTES);
+  }
+
+  @Override
+  public void putDouble(final long offsetBytes, final double value) {
+    MemoryAccess.setDoubleAtOffset(seg, offsetBytes, NON_NATIVE_BYTE_ORDER, 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;
+    final MemorySegment srcSlice = MemorySegment.ofArray(srcArray).asSlice(srcOffsetDoubles << DOUBLE_SHIFT, copyBytes);
+    final MemorySegment dstSlice = seg.asSlice(pos, copyBytes);
+    for (int index = 0; index < lengthDoubles; index++) {
+      final double dbl = MemoryAccess.getDoubleAtIndex(srcSlice, index,  NATIVE_BYTE_ORDER);
+      MemoryAccess.setDoubleAtIndex(dstSlice, index, NON_NATIVE_BYTE_ORDER, dbl);
+    }
+    setPosition(pos + copyBytes);
+  }
+
+  @Override
+  public void putFloat(final float value) {
+    final long pos = getPosition();
+    MemoryAccess.setFloatAtOffset(seg, pos, NON_NATIVE_BYTE_ORDER, value);
+    setPosition(pos + Float.BYTES);
+  }
+
+  @Override
+  public void putFloat(final long offsetBytes, final float value) {
+    MemoryAccess.setFloatAtOffset(seg, offsetBytes, NON_NATIVE_BYTE_ORDER, 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;
+    final MemorySegment srcSlice = MemorySegment.ofArray(srcArray).asSlice(srcOffsetFloats << FLOAT_SHIFT, copyBytes);
+    final MemorySegment dstSlice = seg.asSlice(pos, copyBytes);
+    for (int index = 0; index < lengthFloats; index++) {
+      final float flt = MemoryAccess.getFloatAtIndex(srcSlice, index,  NATIVE_BYTE_ORDER);
+      MemoryAccess.setFloatAtIndex(dstSlice, index, NON_NATIVE_BYTE_ORDER, flt);
+    }
+    setPosition(pos + copyBytes);
+  }
+
+  @Override
+  public void putInt(final int value) {
+    final long pos = getPosition();
+    MemoryAccess.setIntAtOffset(seg, pos, NON_NATIVE_BYTE_ORDER, value);
+    setPosition(pos + Integer.BYTES);
+  }
+
+  @Override
+  public void putInt(final long offsetBytes, final int value) {
+    MemoryAccess.setIntAtOffset(seg, offsetBytes, NON_NATIVE_BYTE_ORDER, 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;
+    final  MemorySegment srcSlice = MemorySegment.ofArray(srcArray).asSlice(srcOffsetInts << INT_SHIFT, copyBytes);
+    final MemorySegment dstSlice = seg.asSlice(pos, copyBytes);
+    for (int index = 0; index < lengthInts; index++) {
+      final int anInt = MemoryAccess.getIntAtIndex(srcSlice, index,  NATIVE_BYTE_ORDER);
+      MemoryAccess.setIntAtIndex(dstSlice, index, NON_NATIVE_BYTE_ORDER, anInt);
+    }
+    setPosition(pos + copyBytes);
+  }
+
+  @Override
+  public void putLong(final long value) {
+    final long pos = getPosition();
+    MemoryAccess.setLongAtOffset(seg, pos, NON_NATIVE_BYTE_ORDER, value);
+    setPosition(pos + Long.BYTES);
+  }
+
+  @Override
+  public void putLong(final long offsetBytes, final long value) {
+    MemoryAccess.setLongAtOffset(seg, offsetBytes, NON_NATIVE_BYTE_ORDER, 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;
+    final MemorySegment srcSlice = MemorySegment.ofArray(srcArray).asSlice(srcOffsetLongs << LONG_SHIFT, copyBytes);
+    final MemorySegment dstSlice = seg.asSlice(pos, copyBytes);
+    for (int index = 0; index < lengthLongs; index++) {
+      final long aLong = MemoryAccess.getLongAtIndex(srcSlice, index,  NATIVE_BYTE_ORDER);
+      MemoryAccess.setLongAtIndex(dstSlice, index, NON_NATIVE_BYTE_ORDER, aLong);
+    }
+    setPosition(pos + copyBytes);
+  }
+
+  @Override
+  public void putShort(final short value) {
+    final long pos = getPosition();
+    MemoryAccess.setShortAtOffset(seg, pos, NON_NATIVE_BYTE_ORDER, value);
+    setPosition(pos + Short.BYTES);
+  }
+
+  @Override
+  public void putShort(final long offsetBytes, final short value) {
+    MemoryAccess.setShortAtOffset(seg, offsetBytes, NON_NATIVE_BYTE_ORDER, 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;
+    final MemorySegment srcSlice = MemorySegment.ofArray(srcArray).asSlice(srcOffsetShorts << SHORT_SHIFT, copyBytes);
+    final MemorySegment dstSlice = seg.asSlice(pos, copyBytes);
+    for (int index = 0; index < lengthShorts; index++) {
+      final short aShort = MemoryAccess.getShortAtIndex(srcSlice, index,  NATIVE_BYTE_ORDER);
+      MemoryAccess.setShortAtIndex(dstSlice, index, NON_NATIVE_BYTE_ORDER, aShort);
+    }
+    setPosition(pos + copyBytes);
+  }
+
+}
diff --git a/src/main/java17/org/apache/datasketches/memory/internal/NonNativeWritableMemoryImpl.java b/src/main/java17/org/apache/datasketches/memory/internal/NonNativeWritableMemoryImpl.java
new file mode 100644
index 0000000..c699e8e
--- /dev/null
+++ b/src/main/java17/org/apache/datasketches/memory/internal/NonNativeWritableMemoryImpl.java
@@ -0,0 +1,261 @@
+/*
+ * 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.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableMemory;
+
+import jdk.incubator.foreign.MemoryAccess;
+import jdk.incubator.foreign.MemorySegment;
+
+/*
+ * Developer notes: The heavier methods, such as put/get arrays, duplicate, region, clear, fill,
+ * compareTo, etc., use hard checks (checkValid*() and checkBounds()), which execute at runtime and
+ * throw exceptions if violated. The cost of the runtime checks are minor compared to the rest of
+ * the work these methods are doing.
+ *
+ * <p>The light weight methods, such as put/get primitives, use asserts (assertValid*()), which only
+ * execute when asserts are enabled and JIT will remove them entirely from production runtime code.
+ * The light weight methods will simplify to a single unsafe call, which is further simplified by
+ * JIT to an intrinsic that is often a single CPU instruction.
+ */
+
+/**
+ * Implementation of {@link WritableMemory} for non-native endian byte order.
+ * @author Roman Leventov
+ * @author Lee Rhodes
+ */
+final class NonNativeWritableMemoryImpl extends BaseWritableMemoryImpl {
+
+  //Pass-through ctor
+  NonNativeWritableMemoryImpl(
+      final MemorySegment seg,
+      final int typeId,
+      final MemoryRequestServer memReqSvr) {
+    super(seg, typeId, memReqSvr);
+  }
+
+  ///PRIMITIVE getX() and getXArray()
+  @Override
+  public char getChar(final long offsetBytes) {
+    return MemoryAccess.getCharAtOffset(seg, offsetBytes, NON_NATIVE_BYTE_ORDER);
+  }
+
+  @Override
+  public void getCharArray(final long offsetBytes, final char[] dstArray,
+      final int dstOffsetChars, final int lengthChars) {
+    final long copyBytes = ((long) lengthChars) << CHAR_SHIFT;
+    final MemorySegment srcSlice = seg.asSlice(offsetBytes, copyBytes);
+    final MemorySegment dstSlice = MemorySegment.ofArray(dstArray).asSlice(dstOffsetChars << CHAR_SHIFT, copyBytes);
+    for (int index = 0; index < lengthChars; index++) {
+      final char aChar = MemoryAccess.getCharAtIndex(srcSlice, index,  NON_NATIVE_BYTE_ORDER);
+      MemoryAccess.setCharAtIndex(dstSlice, index, NATIVE_BYTE_ORDER, aChar);
+    }
+  }
+
+  @Override
+  public double getDouble(final long offsetBytes) {
+    return MemoryAccess.getDoubleAtOffset(seg, offsetBytes, NON_NATIVE_BYTE_ORDER);
+  }
+
+  @Override
+  public void getDoubleArray(final long offsetBytes, final double[] dstArray,
+      final int dstOffsetDoubles, final int lengthDoubles) {
+    final long copyBytes = ((long) lengthDoubles) << DOUBLE_SHIFT;
+    final MemorySegment srcSlice = seg.asSlice(offsetBytes, copyBytes);
+    final MemorySegment dstSlice = MemorySegment.ofArray(dstArray).asSlice(dstOffsetDoubles << DOUBLE_SHIFT, copyBytes);
+    for (int index = 0; index < lengthDoubles; index++) {
+      final double dbl = MemoryAccess.getDoubleAtIndex(srcSlice, index,  NON_NATIVE_BYTE_ORDER);
+      MemoryAccess.setDoubleAtIndex(dstSlice, index, NATIVE_BYTE_ORDER, dbl);
+    }
+  }
+
+  @Override
+  public float getFloat(final long offsetBytes) {
+    return MemoryAccess.getFloatAtOffset(seg, offsetBytes, NON_NATIVE_BYTE_ORDER);
+  }
+
+  @Override
+  public void getFloatArray(final long offsetBytes, final float[] dstArray,
+      final int dstOffsetFloats, final int lengthFloats) {
+    final long copyBytes = ((long) lengthFloats) << FLOAT_SHIFT;
+    final MemorySegment srcSlice = seg.asSlice(offsetBytes, copyBytes);
+    final MemorySegment dstSlice = MemorySegment.ofArray(dstArray).asSlice(dstOffsetFloats << FLOAT_SHIFT, copyBytes);
+    for (int index = 0; index < lengthFloats; index++) {
+      final float flt = MemoryAccess.getFloatAtIndex(srcSlice, index,  NON_NATIVE_BYTE_ORDER);
+      MemoryAccess.setFloatAtIndex(dstSlice, index, NATIVE_BYTE_ORDER, flt);
+    }
+  }
+
+  @Override
+  public int getInt(final long offsetBytes) {
+    return MemoryAccess.getIntAtOffset(seg, offsetBytes, NON_NATIVE_BYTE_ORDER);
+  }
+
+  @Override
+  public void getIntArray(final long offsetBytes, final int[] dstArray, final int dstOffsetInts,
+      final int lengthInts) {
+    final long copyBytes = ((long) lengthInts) << INT_SHIFT;
+    final MemorySegment srcSlice = seg.asSlice(offsetBytes, copyBytes);
+    final MemorySegment dstSlice = MemorySegment.ofArray(dstArray).asSlice(dstOffsetInts << INT_SHIFT, copyBytes);
+    for (int index = 0; index < lengthInts; index++) {
+      final int anInt = MemoryAccess.getIntAtIndex(srcSlice, index,  NON_NATIVE_BYTE_ORDER);
+      MemoryAccess.setIntAtIndex(dstSlice, index, NATIVE_BYTE_ORDER, anInt);
+    }
+  }
+
+  @Override
+  public long getLong(final long offsetBytes) {
+    return MemoryAccess.getLongAtOffset(seg, offsetBytes, NON_NATIVE_BYTE_ORDER);
+  }
+
+  @Override
+  public void getLongArray(final long offsetBytes, final long[] dstArray,
+      final int dstOffsetLongs, final int lengthLongs) {
+    final long copyBytes = ((long) lengthLongs) << LONG_SHIFT;
+    final MemorySegment srcSlice = seg.asSlice(offsetBytes, copyBytes);
+    final MemorySegment dstSlice = MemorySegment.ofArray(dstArray).asSlice(dstOffsetLongs << LONG_SHIFT, copyBytes);
+    for (int index = 0; index < lengthLongs; index++) {
+      final long aLong = MemoryAccess.getLongAtIndex(srcSlice, index,  NON_NATIVE_BYTE_ORDER);
+      MemoryAccess.setLongAtIndex(dstSlice, index, NATIVE_BYTE_ORDER, aLong);
+    }
+  }
+
+  @Override
+  public short getShort(final long offsetBytes) {
+    return MemoryAccess.getShortAtOffset(seg, offsetBytes, NON_NATIVE_BYTE_ORDER);
+  }
+
+  @Override
+  public void getShortArray(final long offsetBytes, final short[] dstArray,
+      final int dstOffsetShorts, final int lengthShorts) {
+    final long copyBytes = ((long) lengthShorts) << SHORT_SHIFT;
+    final MemorySegment srcSlice = seg.asSlice(offsetBytes, copyBytes);
+    final MemorySegment dstSlice = MemorySegment.ofArray(dstArray).asSlice(dstOffsetShorts << SHORT_SHIFT, copyBytes);
+    for (int index = 0; index < lengthShorts; index++) {
+      final short aShort = MemoryAccess.getShortAtIndex(srcSlice, index,  NON_NATIVE_BYTE_ORDER);
+      MemoryAccess.setShortAtIndex(dstSlice, index, NATIVE_BYTE_ORDER, aShort);
+    }
+  }
+
+  //PRIMITIVE putX() and putXArray() implementations
+  @Override
+  public void putChar(final long offsetBytes, final char value) {
+    MemoryAccess.setCharAtOffset(seg, offsetBytes, NON_NATIVE_BYTE_ORDER, value);
+  }
+
+  @Override
+  public void putCharArray(final long offsetBytes, final char[] srcArray, final int srcOffsetChars,
+      final int lengthChars) {
+    final long copyBytes = ((long) lengthChars) << CHAR_SHIFT;
+    final MemorySegment srcSlice = MemorySegment.ofArray(srcArray).asSlice(srcOffsetChars << CHAR_SHIFT, copyBytes);
+    final MemorySegment dstSlice = seg.asSlice(offsetBytes, copyBytes);
+    for (int index = 0; index < lengthChars; index++) {
+      final char aChar = MemoryAccess.getCharAtIndex(srcSlice, index,  NATIVE_BYTE_ORDER);
+      MemoryAccess.setCharAtIndex(dstSlice, index, NON_NATIVE_BYTE_ORDER, aChar);
+    }
+  }
+
+  @Override
+  public void putDouble(final long offsetBytes, final double value) {
+    MemoryAccess.setDoubleAtOffset(seg, offsetBytes, NON_NATIVE_BYTE_ORDER, value);
+  }
+
+  @Override
+  public void putDoubleArray(final long offsetBytes, final double[] srcArray,
+      final int srcOffsetDoubles, final int lengthDoubles) {
+    final long copyBytes = ((long) lengthDoubles) << DOUBLE_SHIFT;
+    final MemorySegment srcSlice = MemorySegment.ofArray(srcArray).asSlice(srcOffsetDoubles << DOUBLE_SHIFT, copyBytes);
+    final MemorySegment dstSlice = seg.asSlice(offsetBytes, copyBytes);
+    for (int index = 0; index < lengthDoubles; index++) {
+      final double dbl = MemoryAccess.getDoubleAtIndex(srcSlice, index,  NATIVE_BYTE_ORDER);
+      MemoryAccess.setDoubleAtIndex(dstSlice, index, NON_NATIVE_BYTE_ORDER, dbl);
+    }
+  }
+
+  @Override
+  public void putFloat(final long offsetBytes, final float value) {
+    MemoryAccess.setFloatAtOffset(seg, offsetBytes, NON_NATIVE_BYTE_ORDER, value);
+  }
+
+  @Override
+  public void putFloatArray(final long offsetBytes, final float[] srcArray,
+      final int srcOffsetFloats, final int lengthFloats) {
+    final long copyBytes = ((long) lengthFloats) << FLOAT_SHIFT;
+    final MemorySegment srcSlice = MemorySegment.ofArray(srcArray).asSlice(srcOffsetFloats << FLOAT_SHIFT, copyBytes);
+    final MemorySegment dstSlice = seg.asSlice(offsetBytes, copyBytes);
+    for (int index = 0; index < lengthFloats; index++) {
+      final float flt = MemoryAccess.getFloatAtIndex(srcSlice, index,  NATIVE_BYTE_ORDER);
+      MemoryAccess.setFloatAtIndex(dstSlice, index, NON_NATIVE_BYTE_ORDER, flt);
+    }
+  }
+
+  @Override
+  public void putInt(final long offsetBytes, final int value) {
+    MemoryAccess.setIntAtOffset(seg, offsetBytes, NON_NATIVE_BYTE_ORDER, value);
+  }
+
+  @Override
+  public void putIntArray(final long offsetBytes, final int[] srcArray, final int srcOffsetInts,
+      final int lengthInts) {
+    final long copyBytes = ((long) lengthInts) << INT_SHIFT;
+    final MemorySegment srcSlice = MemorySegment.ofArray(srcArray).asSlice(srcOffsetInts << INT_SHIFT, copyBytes);
+    final MemorySegment dstSlice = seg.asSlice(offsetBytes, copyBytes);
+    for (int index = 0; index < lengthInts; index++) {
+      final int anInt = MemoryAccess.getIntAtIndex(srcSlice, index,  NATIVE_BYTE_ORDER);
+      MemoryAccess.setIntAtIndex(dstSlice, index, NON_NATIVE_BYTE_ORDER, anInt);
+    }
+  }
+
+  @Override
+  public void putLong(final long offsetBytes, final long value) {
+    MemoryAccess.setLongAtOffset(seg, offsetBytes, NON_NATIVE_BYTE_ORDER, value);
+  }
+
+  @Override
+  public void putLongArray(final long offsetBytes, final long[] srcArray, final int srcOffsetLongs,
+      final int lengthLongs) {
+    final long copyBytes = ((long) lengthLongs) << LONG_SHIFT;
+    final MemorySegment srcSlice = MemorySegment.ofArray(srcArray).asSlice(srcOffsetLongs << LONG_SHIFT, copyBytes);
+    final MemorySegment dstSlice = seg.asSlice(offsetBytes, copyBytes);
+    for (int index = 0; index < lengthLongs; index++) {
+      final long aLong = MemoryAccess.getLongAtIndex(srcSlice, index,  NATIVE_BYTE_ORDER);
+      MemoryAccess.setLongAtIndex(dstSlice, index, NON_NATIVE_BYTE_ORDER, aLong);
+    }
+  }
+
+  @Override
+  public void putShort(final long offsetBytes, final short value) {
+    MemoryAccess.setShortAtOffset(seg, offsetBytes, NON_NATIVE_BYTE_ORDER, value);
+  }
+
+  @Override
+  public void putShortArray(final long offsetBytes, final short[] srcArray,
+      final int srcOffsetShorts, final int lengthShorts) {
+    final long copyBytes = ((long) lengthShorts) << SHORT_SHIFT;
+    final MemorySegment srcSlice = MemorySegment.ofArray(srcArray).asSlice(srcOffsetShorts << SHORT_SHIFT, copyBytes);
+    final MemorySegment dstSlice = seg.asSlice(offsetBytes, copyBytes);
+    for (int index = 0; index < lengthShorts; index++) {
+      final short aShort = MemoryAccess.getShortAtIndex(srcSlice, index,  NATIVE_BYTE_ORDER);
+      MemoryAccess.setShortAtIndex(dstSlice, index, NON_NATIVE_BYTE_ORDER, aShort);
+    }
+  }
+
+}
diff --git a/src/main/java17/org/apache/datasketches/memory/internal/Util.java b/src/main/java17/org/apache/datasketches/memory/internal/Util.java
new file mode 100644
index 0000000..07c0a7a
--- /dev/null
+++ b/src/main/java17/org/apache/datasketches/memory/internal/Util.java
@@ -0,0 +1,132 @@
+/*
+ * 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.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Objects;
+
+/**
+ * @author Lee Rhodes
+ */
+@SuppressWarnings("javadoc")
+final class Util {
+  static final String LS = System.getProperty("line.separator");
+
+  private Util() { }
+
+  /**
+   * 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
+   */
+  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
+   */
+  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
+   */
+  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
+   */
+  static final boolean isAnyBitsSet(final long value, final long bitMask) {
+    return (value & bitMask) != 0;
+  }
+
+  //Resources mention: these 3 methods are duplicated in Java/ datasketches/Util
+
+  /**
+   * Gets the absolute 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.
+   * @throws IllegalArgumentException if resource cannot be found
+   */
+  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 path = uri.isAbsolute() ? Paths.get(uri).toAbsolutePath().toString() : uri.getPath();
+      return path;
+    } 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.
+   */
+  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.
+   * @throws IllegalArgumentException if resource cannot be read.
+   */
+  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);
+    }
+  }
+
+}
diff --git a/src/main/java17/org/apache/datasketches/memory/internal/XxHash64.java b/src/main/java17/org/apache/datasketches/memory/internal/XxHash64.java
new file mode 100644
index 0000000..994cb02
--- /dev/null
+++ b/src/main/java17/org/apache/datasketches/memory/internal/XxHash64.java
@@ -0,0 +1,311 @@
+/*
+ * 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 jdk.incubator.foreign.MemoryAccess.getByteAtOffset;
+import static jdk.incubator.foreign.MemoryAccess.getIntAtOffset;
+import static jdk.incubator.foreign.MemoryAccess.getLongAtOffset;
+import static org.apache.datasketches.memory.internal.BaseStateImpl.CHAR_SHIFT;
+import static org.apache.datasketches.memory.internal.BaseStateImpl.DOUBLE_SHIFT;
+import static org.apache.datasketches.memory.internal.BaseStateImpl.FLOAT_SHIFT;
+import static org.apache.datasketches.memory.internal.BaseStateImpl.INT_SHIFT;
+import static org.apache.datasketches.memory.internal.BaseStateImpl.LONG_SHIFT;
+import static org.apache.datasketches.memory.internal.BaseStateImpl.SHORT_SHIFT;
+
+import jdk.incubator.foreign.MemorySegment;
+
+/**
+ * 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 from 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 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 given MemorySegment
+   *
+   * @param seg A reference to the relevant MemorySegment.
+   * @param offsetBytes offset in bytes in the given segment.
+   * @param lengthBytes the length in bytes to be hashed
+   * @param seed a given seed
+   * @return the 64-bit hash of the sequence of bytes.
+   */
+  static long hash(final MemorySegment seg, long offsetBytes, 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 += getLongAtOffset(seg, offsetBytes) * P2;
+        v1 = Long.rotateLeft(v1, 31);
+        v1 *= P1;
+
+        v2 += getLongAtOffset(seg, offsetBytes + 8L) * P2;
+        v2 = Long.rotateLeft(v2, 31);
+        v2 *= P1;
+
+        v3 += getLongAtOffset(seg, offsetBytes + 16L) * P2;
+        v3 = Long.rotateLeft(v3, 31);
+        v3 *= P1;
+
+        v4 += getLongAtOffset(seg, offsetBytes + 24L) * P2;
+        v4 = Long.rotateLeft(v4, 31);
+        v4 *= P1;
+
+        offsetBytes += 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 = getLongAtOffset(seg, offsetBytes);
+      k1 *= P2;
+      k1 = Long.rotateLeft(k1, 31);
+      k1 *= P1;
+      hash ^= k1;
+      hash = (Long.rotateLeft(hash, 27) * P1) + P4;
+      offsetBytes += 8;
+      remaining -= 8;
+    }
+
+    if (remaining >= 4) { //treat as unsigned ints
+      hash ^= (getIntAtOffset(seg, offsetBytes) & 0XFFFF_FFFFL) * P1;
+      hash = (Long.rotateLeft(hash, 23) * P2) + P3;
+      offsetBytes += 4;
+      remaining -= 4;
+    }
+
+    while (remaining != 0) { //treat as unsigned bytes
+      hash ^= (getByteAtOffset(seg, offsetBytes) & 0XFFL) * P5;
+      hash = Long.rotateLeft(hash, 11) * P1;
+      --remaining;
+      ++offsetBytes;
+    }
+
+    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 int offsetBytes,
+      final int lengthBytes, final long seed) {
+    final MemorySegment seg = MemorySegment.ofArray(arr);
+    return hash(seg, 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 int offsetShorts,
+      final int lengthShorts, final long seed) {
+    final MemorySegment seg = MemorySegment.ofArray(arr);
+    return hash(seg, (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 int offsetChars,
+      final int lengthChars, final long seed) {
+    final MemorySegment seg = MemorySegment.ofArray(arr);
+    return hash(seg, 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 int offsetInts,
+      final int lengthInts, final long seed) {
+    final MemorySegment seg = MemorySegment.ofArray(arr);
+    return hash(seg, 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 int offsetLongs,
+      final int lengthLongs, final long seed) {
+    final MemorySegment seg = MemorySegment.ofArray(arr);
+    return hash(seg, 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 int offsetFloats,
+      final int lengthFloats, final long seed) {
+    final MemorySegment seg = MemorySegment.ofArray(arr);
+    return hash(seg, 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 int offsetDoubles,
+      final int lengthDoubles, final long seed) {
+    final MemorySegment seg = MemorySegment.ofArray(arr);
+    return hash(seg, 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 int offsetChars,
+      final int lengthChars, final long seed) {
+    final MemorySegment seg = MemorySegment.ofArray(str.toCharArray());
+    return hash(seg, offsetChars << CHAR_SHIFT, lengthChars << CHAR_SHIFT, seed);
+  }
+
+}
diff --git a/src/main/java17/org/apache/datasketches/memory/internal/package-info.java b/src/main/java17/org/apache/datasketches/memory/internal/package-info.java
new file mode 100644
index 0000000..2a3bb7a
--- /dev/null
+++ b/src/main/java17/org/apache/datasketches/memory/internal/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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;
diff --git a/src/main/java17/org/apache/datasketches/memory/package-info.java b/src/main/java17/org/apache/datasketches/memory/package-info.java
new file mode 100644
index 0000000..87b0a0f
--- /dev/null
+++ b/src/main/java17/org/apache/datasketches/memory/package-info.java
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+/**
+ * <p>This package provides high performance primitive and primitive array access to direct (native),
+ * off-heap memory and memory-mapped file resources, and consistent views into
+ * {@link java.nio.ByteBuffer}, and on-heap primitive arrays. It can be used as a more
+ * comprehensive and flexible replacement for {@link java.nio.ByteBuffer}.
+ * </p>
+ *
+ * <p>In addition, this package provides:</p>
+ *
+ * <ul><li>Two different access APIs: read-only {@link org.apache.datasketches.memory.Memory} and
+ * {@link org.apache.datasketches.memory.WritableMemory} for absolute offset access,
+ * and read-only {@link org.apache.datasketches.memory.Buffer} and
+ * {@link org.apache.datasketches.memory.WritableBuffer}
+ * for relative positional access (similar to ByteBuffer).</li>
+ *
+ * <li>Clean separation of Read-only API from Writable API, which makes writable versus read-only
+ * resources detectable at compile time.</li>
+ *
+ * <li>The conversion from Writable to read-only is just a cast, so no unnecessary objects are
+ * created. For example:
+ * <blockquote><pre>
+ *     WritableMemory wMem = ...
+ *     Memory mem = wMem;
+ * </pre></blockquote>
+ * </li>
+ *
+ * <li> {@link java.lang.AutoCloseable} for the external resources that require it,
+ * which enables compile-time checks for non-closed resources.</li>
+ *
+ * <li>Immediate invalidation of all downstream references of an AutoCloseable
+ * resource when that resource is closed, either manually or by the JVM.
+ * This virtually eliminates the possibility of accidentally writing into the memory space
+ * previously owned by a closed resource.</li>
+ *
+ * <li>Improved performance over the prior Memory implementation.</li>
+ *
+ * <li>No external dependencies, which makes it simple to install in virtually any Java environment.
+ * </li>
+ * </ul>
+ *
+ * <p>More specifically, this package provides access to four different types of resources using
+ * two different access APIs. These resources are contiguous blobs of bytes that provide at least
+ * byte-level read and write access. The four resources are:</p>
+ *
+ * <ul><li>Direct (a.k.a. Native) off-heap memory allocated by the user.</li>
+ * <li>Memory-mapped files, both writable and read-only.</li>
+ * <li>{@code ByteBuffers}, both heap-based and direct, writable and read-only.</li>
+ * <li>Heap-based primitive arrays, which can be accessed as writable or read-only.</li>
+ * </ul>
+ *
+ * <p>The two different access APIs are:</p>
+ * <ul><li><i>Memory, WritableMemory</i>: Absolute offset addressing into a resource.</li>
+ * <li><i>Buffer, WritableBuffer</i>: Position relative addressing into a resource.</li>
+ * </ul>
+ *
+ * <p>In addition, all combinations of access APIs and backing resources can be accessed via
+ * multibyte primitive methods (e.g.
+ * <i>getLong(...), getLongArray(...), putLong(...), putLongArray(...)</i>) as either
+ * {@link java.nio.ByteOrder#BIG_ENDIAN} or {@link java.nio.ByteOrder#LITTLE_ENDIAN}.</p>
+ *
+ * <p>The resources don't know or care about the access APIs, and the access
+ * APIs don't really know or care what resource they are accessing.</p>
+ *
+ * <p>An access API is joined with a resource with a static factory method.
+ *
+ *<p>Moving back and forth between <i>Memory</i> and <i>Buffer</i>:</p>
+ *<blockquote><pre>
+ *    Memory mem = ...
+ *    Buffer buf = mem.asBuffer();
+ *    ...
+ *    Memory mem2 = buf.asMemory();
+ *    ...
+ * </pre></blockquote>
+ *
+ * <p>Hierarchical memory regions can be easily created:</p>
+ * <blockquote><pre>
+ *     WritableMemory wMem = ...
+ *     WritableMemory wReg = wMem.writableRegion(offset, length); //OR
+ *     Memory reg = wMem.region(offset, length);
+ * </pre></blockquote>
+ *
+ * @author Lee Rhodes
+ */
+package org.apache.datasketches.memory;
diff --git a/src/test/java17/org/apache/datasketches/memory/internal/AllocateDirectMapMemoryTest.java b/src/test/java17/org/apache/datasketches/memory/internal/AllocateDirectMapMemoryTest.java
new file mode 100644
index 0000000..161eb1b
--- /dev/null
+++ b/src/test/java17/org/apache/datasketches/memory/internal/AllocateDirectMapMemoryTest.java
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+
+/*
+ * Note: Lincoln's Gettysburg Address is in the public domain. See LICENSE.
+ */
+
+package org.apache.datasketches.memory.internal;
+
+import static org.apache.datasketches.memory.internal.Util.getResourceFile;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.io.File;
+import java.nio.ByteOrder;
+
+import org.apache.datasketches.memory.Memory;
+import org.testng.annotations.Test;
+
+import jdk.incubator.foreign.ResourceScope;
+
+public class AllocateDirectMapMemoryTest {
+  private static final String LS = System.getProperty("line.separator");
+
+  @Test
+  public void simpleMap() throws Exception {
+    File file = getResourceFile("GettysburgAddress.txt");
+    file.setReadOnly();
+    Memory mem = null;
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      mem = Memory.map(file, scope);
+    }
+    assertFalse(mem.isAlive());
+  }
+
+  @Test
+  public void testIllegalArguments() throws Exception {
+    File file = getResourceFile("GettysburgAddress.txt");
+    Memory mem = null;
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      mem = Memory.map(file, -1, Integer.MAX_VALUE, scope, ByteOrder.nativeOrder());
+      fail("Failed: test IllegalArgumentException: Position was negative.");
+      mem.getCapacity();
+    }
+    catch (IllegalArgumentException e) {
+      //ok
+    }
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      mem = Memory.map(file, 0, -1, scope, ByteOrder.nativeOrder());
+      fail("Failed: testIllegalArgumentException: Size was negative.");
+    } catch (IllegalArgumentException e) {
+      //ok
+    }
+  }
+
+  @Test
+  public void testMapAndMultipleClose() throws Exception {
+    File file = getResourceFile("GettysburgAddress.txt");
+    long memCapacity = file.length();
+    Memory mem = null;
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      mem = Memory.map(file, 0, memCapacity, scope, ByteOrder.nativeOrder());
+      assertEquals(memCapacity, mem.getCapacity());
+      //mem.close(); //a close inside the TWR block will throw excption when the TWR block ends
+    }
+    mem.close(); //multiple closes outside the TWR block are OK, but unnecessary
+    mem.close();
+  }
+
+  @Test
+  public void testLoad() throws Exception {
+    File file = getResourceFile("GettysburgAddress.txt");
+    long memCapacity = file.length();
+    Memory mem = null;
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      mem = Memory.map(file, 0, memCapacity, scope, ByteOrder.nativeOrder());
+      mem.load();
+      assertTrue(mem.isLoaded());
+    }
+  }
+
+  @SuppressWarnings("resource")
+  @Test
+  public void testHandleHandoff() throws Exception {
+    File file = getResourceFile("GettysburgAddress.txt");
+    long memCapacity = file.length();
+    ResourceScope scope = ResourceScope.newConfinedScope();
+    Memory mem = Memory.map(file, 0, memCapacity, scope, ByteOrder.nativeOrder());
+    ResourceScope.Handle handle = scope.acquire();
+    try {
+      mem.load();
+      assertTrue(mem.isLoaded());
+    } finally {
+      mem.scope().release(handle);
+    }
+    assertTrue(mem.isAlive());
+    mem.close(); //handle must be released before close
+    assertFalse(mem.isAlive());
+  }
+
+  @Test
+  public void printlnTest() {
+    println("PRINTING: "+this.getClass().getName());
+  }
+
+  static void println(final Object o) {
+    if (o == null) { print(LS); }
+    else { print(o.toString() + LS); }
+  }
+
+  /**
+   * @param o value to print
+   */
+  static void print(final Object o) {
+    if (o != null) {
+      //System.out.print(o.toString()); //disable here
+    }
+  }
+
+}
diff --git a/src/test/java17/org/apache/datasketches/memory/internal/AllocateDirectMemoryTest.java b/src/test/java17/org/apache/datasketches/memory/internal/AllocateDirectMemoryTest.java
new file mode 100644
index 0000000..0497a66
--- /dev/null
+++ b/src/test/java17/org/apache/datasketches/memory/internal/AllocateDirectMemoryTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.BaseState;
+import org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableMemory;
+import org.testng.annotations.Test;
+
+import jdk.incubator.foreign.ResourceScope;
+
+public class AllocateDirectMemoryTest {
+  private static final MemoryRequestServer memReqSvr = BaseState.defaultMemReqSvr;
+
+  @SuppressWarnings("resource")
+  @Test
+  public void simpleAllocateDirect() {
+    int longs = 32;
+    WritableMemory wMem = null;
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      wMem = WritableMemory.allocateDirect(longs << 3, scope, memReqSvr);
+      for (int i = 0; i<longs; i++) {
+        wMem.putLong(i << 3, i);
+        assertEquals(wMem.getLong(i << 3), i);
+      }
+      //inside the TWR block the memory scope will be alive
+      assertTrue(wMem.isAlive());
+    }
+    //The TWR block has exited, so the memory should be invalid
+    assertFalse(wMem.isAlive());
+  }
+
+  @Test
+  public void checkDefaultMemoryRequestServer() {
+    int longs1 = 32;
+    int bytes1 = longs1 << 3;
+    WritableMemory wmem = null;
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      wmem = WritableMemory.allocateDirect(bytes1, scope, memReqSvr);
+
+      for (int i = 0; i < longs1; i++) { //puts data in origWmem
+        wmem.putLong(i << 3, i);
+        assertEquals(wmem.getLong(i << 3), i);
+      }
+      println(wmem.toHexString("Test", 0, 32 * 8, true));
+
+      int longs2 = 64;
+      int bytes2 = longs2 << 3;
+      WritableMemory newWmem = memReqSvr.request(wmem, bytes2); //on the heap
+      assertFalse(newWmem.isDirect()); //on heap by default
+      for (int i = 0; i < longs2; i++) {
+          newWmem.putLong(i << 3, i);
+          assertEquals(newWmem.getLong(i << 3), i);
+      }
+      memReqSvr.requestClose(wmem, newWmem); //The default MRS doesn't close.
+    } // So we let the TWR close it here
+  }
+
+  @SuppressWarnings("resource")
+  @Test
+  public void checkNonNativeDirect() {
+    WritableMemory wmem = null;
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      wmem = WritableMemory.allocateDirect( 128, 8, scope,
+          BaseState.NON_NATIVE_BYTE_ORDER, memReqSvr);
+      wmem.putChar(0, (char) 1);
+      assertEquals(wmem.getByte(1), (byte) 1);
+    }
+  }
+
+  @SuppressWarnings("resource")
+  @Test
+  public void checkExplicitCloseNoTWR() {
+    final long cap = 128;
+    WritableMemory wmem = null;
+    ResourceScope scope = ResourceScope.newConfinedScope();
+    wmem = WritableMemory.allocateDirect(cap, scope, memReqSvr);
+    wmem.close(); //explicit close
+  }
+
+  @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/src/test/java17/org/apache/datasketches/memory/internal/AllocateDirectWritableMapMemoryTest.java b/src/test/java17/org/apache/datasketches/memory/internal/AllocateDirectWritableMapMemoryTest.java
new file mode 100644
index 0000000..1f33854
--- /dev/null
+++ b/src/test/java17/org/apache/datasketches/memory/internal/AllocateDirectWritableMapMemoryTest.java
@@ -0,0 +1,240 @@
+/*
+ * 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.
+ */
+
+/*
+ * Note: Lincoln's Gettysburg Address is in the public domain. See LICENSE.
+ */
+
+package org.apache.datasketches.memory.internal;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.apache.datasketches.memory.internal.Util.getResourceFile;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteOrder;
+import java.nio.file.InvalidPathException;
+
+import org.apache.datasketches.memory.BaseState;
+import org.apache.datasketches.memory.Memory;
+import org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableMemory;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import jdk.incubator.foreign.ResourceScope;
+
+public class AllocateDirectWritableMapMemoryTest {
+  private static final String LS = System.getProperty("line.separator");
+  private final MemoryRequestServer memReqSvr = BaseState.defaultMemReqSvr;
+
+  @BeforeClass
+  public void setReadOnly() throws IOException {
+    UtilTest.setGettysburgAddressFileToReadOnly();
+  }
+
+  @Test
+  public void simpleMap()
+      throws IllegalArgumentException, InvalidPathException, IllegalStateException, UnsupportedOperationException,
+      IOException, SecurityException {
+    File file = getResourceFile("GettysburgAddress.txt");
+    Memory mem = null;
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      mem = Memory.map(file,scope);
+      byte[] byteArr = new byte[(int)mem.getCapacity()];
+      mem.getByteArray(0, byteArr, 0, byteArr.length);
+      String text = new String(byteArr, UTF_8);
+      println(text);
+      assertTrue(mem.isReadOnly());
+    }
+  }
+
+  @Test
+  public void copyOffHeapToMemoryMappedFile()
+      throws IllegalArgumentException, InvalidPathException, IllegalStateException, UnsupportedOperationException,
+      IOException, SecurityException {
+    long numBytes = 1L << 10; //small for unit tests.  Make it larger than 2GB if you like.
+    long numLongs = numBytes >>> 3;
+
+    File file = new File("TestFile.bin"); //create a dummy file
+    if (file.exists()) {
+      java.nio.file.Files.delete(file.toPath());
+    }
+    assertTrue(file.createNewFile());
+    assertTrue (file.setWritable(true, false)); //writable=true, ownerOnly=false
+    assertTrue (file.isFile());
+    file.deleteOnExit();  //comment out if you want to examine the file.
+
+    WritableMemory dstMem = null;
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) { //this scope manages two Memory objects
+      dstMem = WritableMemory.writableMap(file, 0, numBytes, scope, ByteOrder.nativeOrder());
+
+      WritableMemory srcMem
+        = WritableMemory.allocateDirect(numBytes, 8, scope, ByteOrder.nativeOrder(), memReqSvr);
+
+      //load source with consecutive longs
+      for (long i = 0; i < numLongs; i++) {
+        srcMem.putLong(i << 3, i);
+      }
+      //off-heap to off-heap copy
+      srcMem.copyTo(0, dstMem, 0, srcMem.getCapacity());
+      dstMem.force(); //push any remaining to the file
+      //check end value
+      assertEquals(dstMem.getLong(numLongs - 1L << 3), numLongs - 1L);
+    } //both map and direct closed here
+  }
+
+  @Test
+  public void checkNonNativeFile()
+      throws IllegalArgumentException, InvalidPathException, IllegalStateException, UnsupportedOperationException,
+      IOException, SecurityException {
+    File file = new File("TestFile2.bin");
+    if (file.exists()) {
+      java.nio.file.Files.delete(file.toPath());
+    }
+    assertTrue(file.createNewFile());
+    assertTrue(file.setWritable(true, false)); //writable=true, ownerOnly=false
+    assertTrue(file.isFile());
+    file.deleteOnExit();  //comment out if you want to examine the file.
+
+    final long bytes = 8;
+    WritableMemory wmem = null;
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      wmem = WritableMemory.writableMap(file, 0L, bytes, scope, BaseState.NON_NATIVE_BYTE_ORDER);
+      wmem.putChar(0, (char) 1);
+      assertEquals(wmem.getByte(1), (byte) 1);
+    }
+  }
+
+  @SuppressWarnings("resource")
+  @Test
+  public void testMapExceptionNoTWR()
+      throws IllegalArgumentException, InvalidPathException, IllegalStateException, UnsupportedOperationException,
+      IOException, SecurityException {
+    File dummy = createFile("dummy.txt", ""); //zero length
+    ResourceScope scope = ResourceScope.newConfinedScope();
+    Memory.map(dummy, 0, dummy.length(), scope, ByteOrder.nativeOrder());
+    scope.close();
+  }
+
+  @Test(expectedExceptions = IllegalArgumentException.class)
+  public void simpleMap2()
+      throws IllegalArgumentException, InvalidPathException, IllegalStateException, UnsupportedOperationException,
+      IOException, SecurityException {
+    File file = getResourceFile("GettysburgAddress.txt");
+    assertTrue(file.canRead());
+    assertFalse(file.canWrite());
+    WritableMemory wmem = null;
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      wmem = WritableMemory.writableMap(file, scope); //throws ReadOnlyException
+      wmem.getCapacity();
+    }
+  }
+
+  @Test(expectedExceptions = IllegalArgumentException.class)
+  public void checkReadException()
+      throws IllegalArgumentException, InvalidPathException, IllegalStateException, UnsupportedOperationException,
+      IOException, SecurityException {
+    File file = getResourceFile("GettysburgAddress.txt");
+    WritableMemory wmem = null;
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      wmem = WritableMemory.writableMap(file, 0, 1 << 20, scope, ByteOrder.nativeOrder());
+      //throws ReadOnlyException
+      wmem.getCapacity();
+    }
+  }
+
+  @Test
+  public void testForce()
+      throws IllegalArgumentException, InvalidPathException, IllegalStateException, UnsupportedOperationException,
+      IOException, SecurityException {
+    String origStr = "Corectng spellng mistks";
+    File origFile = createFile("force_original.txt", origStr); //23
+    assertTrue(origFile.setWritable(true, false));
+    long origBytes = origFile.length();
+    String correctStr = "Correcting spelling mistakes"; //28
+    byte[] correctByteArr = correctStr.getBytes(UTF_8);
+    long correctBytesLen = correctByteArr.length;
+
+    Memory mem = null;
+    WritableMemory wmem = null;
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      mem = Memory.map(origFile, 0, origBytes, scope, ByteOrder.nativeOrder());
+      mem.load();
+      assertTrue(mem.isLoaded());
+      //confirm orig string
+      byte[] buf = new byte[(int)origBytes];
+      mem.getByteArray(0, buf, 0, (int)origBytes);
+      String bufStr = new String(buf, UTF_8);
+      assertEquals(bufStr, origStr);
+
+      wmem = WritableMemory.writableMap(origFile, 0, correctBytesLen, scope, ByteOrder.nativeOrder());
+      wmem.load();
+      assertTrue(wmem.isLoaded());
+      // over write content
+      wmem.putByteArray(0, correctByteArr, 0, (int)correctBytesLen);
+      wmem.force();
+      //confirm correct string
+      byte[] buf2 = new byte[(int)correctBytesLen];
+      wmem.getByteArray(0, buf2, 0, (int)correctBytesLen);
+      String bufStr2 = new String(buf2, UTF_8);
+      assertEquals(bufStr2, correctStr);
+    }
+  }
+
+  private static File createFile(String fileName, String text) throws FileNotFoundException {
+    File file = new File(fileName);
+    file.deleteOnExit();
+    PrintWriter writer;
+    try {
+      writer = new PrintWriter(file, UTF_8.name());
+      writer.print(text);
+      writer.close();
+    } catch (UnsupportedEncodingException e) {
+      e.printStackTrace();
+    }
+    return file;
+  }
+
+  @Test
+  public void printlnTest() {
+    println("PRINTING: "+this.getClass().getName());
+  }
+
+  static void println(final Object o) {
+    if (o == null) { print(LS); }
+    else { print(o.toString() + LS); }
+  }
+
+  /**
+   * @param o value to print
+   */
+  static void print(final Object o) {
+    if (o != null) {
+      //System.out.print(o.toString()); //disable here
+    }
+  }
+
+}
diff --git a/src/test/java17/org/apache/datasketches/memory/internal/BaseBufferTest.java b/src/test/java17/org/apache/datasketches/memory/internal/BaseBufferTest.java
new file mode 100644
index 0000000..73cd01f
--- /dev/null
+++ b/src/test/java17/org/apache/datasketches/memory/internal/BaseBufferTest.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.internal;
+
+import static org.testng.Assert.fail;
+
+import org.apache.datasketches.memory.BaseState;
+import org.apache.datasketches.memory.Buffer;
+import org.apache.datasketches.memory.Memory;
+import org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableMemory;
+import org.testng.annotations.Test;
+
+import jdk.incubator.foreign.ResourceScope;
+
+/**
+ * @author Lee Rhodes
+ */
+public class BaseBufferTest {
+  private static final MemoryRequestServer memReqSvr = BaseState.defaultMemReqSvr;
+
+  @Test
+  public void checkLimits() {
+    Buffer buf = Memory.wrap(new byte[100]).asBuffer();
+    buf.setStartPositionEnd(40, 45, 50);
+    buf.setStartPositionEnd(0, 0, 100);
+    try {
+      buf.setStartPositionEnd(0, 0, 101);
+      fail();
+    } catch (AssertionError e) {
+      //ok
+    }
+  }
+
+  @Test
+  public void checkLimitsAndCheck() {
+    Buffer buf = Memory.wrap(new byte[100]).asBuffer();
+    buf.setAndCheckStartPositionEnd(40, 45, 50);
+    buf.setAndCheckStartPositionEnd(0, 0, 100);
+    try {
+      buf.setAndCheckStartPositionEnd(0, 0, 101);
+      fail();
+    } catch (IllegalArgumentException e) {
+      //ok
+    }
+    buf.setAndCheckPosition(100);
+    try {
+      buf.setAndCheckPosition(101);
+      fail();
+    } catch (IllegalArgumentException e) {
+      //ok
+    }
+    buf.setPosition(99);
+    buf.incrementAndCheckPosition(1L);
+    try {
+      buf.incrementAndCheckPosition(1L);
+      fail();
+    } catch (IllegalArgumentException e) {
+      //ok
+    }
+  }
+
+  @Test
+  public void checkCheckNotAliveAfterTWR() {
+    WritableMemory wmem;
+    Buffer buf;
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      wmem = WritableMemory.allocateDirect(100, scope, memReqSvr);
+      buf = wmem.asBuffer();
+    }
+    try {
+      buf.asMemory(); //not alive
+    } catch (IllegalStateException e) { }
+  }
+}
diff --git a/src/test/java17/org/apache/datasketches/memory/internal/BaseStateTest.java b/src/test/java17/org/apache/datasketches/memory/internal/BaseStateTest.java
new file mode 100644
index 0000000..d4c0bbf
--- /dev/null
+++ b/src/test/java17/org/apache/datasketches/memory/internal/BaseStateTest.java
@@ -0,0 +1,138 @@
+/*
+ * 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 java.nio.ByteOrder;
+
+import org.apache.datasketches.memory.Buffer;
+import org.apache.datasketches.memory.Memory;
+import org.apache.datasketches.memory.WritableMemory;
+import org.testng.annotations.Test;
+
+import jdk.incubator.foreign.MemorySegment;
+import jdk.incubator.foreign.ResourceScope;
+
+public class BaseStateTest {
+
+  @Test
+  public void checkNativeOverlap() {
+    MemorySegment par = MemorySegment.allocateNative(100, ResourceScope.newImplicitScope());
+    //Equal sizes
+    assertEquals(BaseStateImpl.nativeOverlap(getSeg(par,  0, 20), getSeg(par, 40, 60)),   0);
+    assertEquals(BaseStateImpl.nativeOverlap(getSeg(par,  0, 20), getSeg(par, 20, 40)),   0);
+    assertEquals(BaseStateImpl.nativeOverlap(getSeg(par,  0, 20), getSeg(par,  0, 20)),  20);
+    assertEquals(BaseStateImpl.nativeOverlap(getSeg(par,  0, 20), getSeg(par, 10, 30)),  10);
+    assertEquals(BaseStateImpl.nativeOverlap(getSeg(par, 10, 30), getSeg(par,  0, 20)), -10);
+    assertEquals(BaseStateImpl.nativeOverlap(getSeg(par, 20, 40), getSeg(par,  0, 20)),   0);
+    assertEquals(BaseStateImpl.nativeOverlap(getSeg(par,  0,  0), getSeg(par,  0,  0)),   0);
+    //Unequal Sizes A > B
+    assertEquals(BaseStateImpl.nativeOverlap(getSeg(par,  0, 40), getSeg(par, 60, 80)),   0);
+    assertEquals(BaseStateImpl.nativeOverlap(getSeg(par,  0, 40), getSeg(par, 40, 60)),   0);
+    assertEquals(BaseStateImpl.nativeOverlap(getSeg(par,  0, 40), getSeg(par, 30, 50)),  10);
+    assertEquals(BaseStateImpl.nativeOverlap(getSeg(par,  0, 40), getSeg(par, 20, 40)),  20);
+    assertEquals(BaseStateImpl.nativeOverlap(getSeg(par,  0, 40), getSeg(par, 10, 30)),  20);
+    assertEquals(BaseStateImpl.nativeOverlap(getSeg(par,  0, 40), getSeg(par,  0, 20)),  20);
+    assertEquals(BaseStateImpl.nativeOverlap(getSeg(par, 10, 50), getSeg(par,  0, 20)), -10);
+    assertEquals(BaseStateImpl.nativeOverlap(getSeg(par, 20, 60), getSeg(par,  0, 20)),   0);
+    assertEquals(BaseStateImpl.nativeOverlap(getSeg(par, 40, 80), getSeg(par,  0, 20)),   0);
+    assertEquals(BaseStateImpl.nativeOverlap(getSeg(par, 40, 80), getSeg(par,  0,  0)),   0);
+
+    //Unequal Sizes B > A
+    assertEquals(BaseStateImpl.nativeOverlap(getSeg(par, 60, 80), getSeg(par,  0, 40)),   0);
+    assertEquals(BaseStateImpl.nativeOverlap(getSeg(par, 40, 60), getSeg(par,  0, 40)),   0);
+    assertEquals(BaseStateImpl.nativeOverlap(getSeg(par, 30, 50), getSeg(par,  0, 40)), -10);
+    assertEquals(BaseStateImpl.nativeOverlap(getSeg(par, 20, 40), getSeg(par,  0, 40)), -20);
+    assertEquals(BaseStateImpl.nativeOverlap(getSeg(par, 10, 30), getSeg(par,  0, 40)), -20);
+    assertEquals(BaseStateImpl.nativeOverlap(getSeg(par,  0, 20), getSeg(par,  0, 40)),  20);
+    assertEquals(BaseStateImpl.nativeOverlap( getSeg(par, 0, 20), getSeg(par, 10, 50)),  10);
+    assertEquals(BaseStateImpl.nativeOverlap(getSeg(par,  0, 20), getSeg(par, 20, 60)),   0);
+    assertEquals(BaseStateImpl.nativeOverlap(getSeg(par,  0, 20), getSeg(par, 40, 80)),   0);
+    assertEquals(BaseStateImpl.nativeOverlap(getSeg(par,  0,  0), getSeg(par, 40, 80)),   0);
+  }
+
+  private static MemorySegment getSeg(MemorySegment parent, long left, long right) {
+    return parent.asSlice(left, right - left);
+  }
+
+  @Test
+  public void checkNotEqualTo() {
+    byte[] arr1 = new byte[8];
+    Memory mem = Memory.wrap(arr1);
+    byte[] arr2 = new byte[8];
+    arr2[7] = 1;
+    Memory mem2 = Memory.wrap(arr2);
+    assertFalse(mem.equalTo(0, mem2, 0, 8));
+  }
+
+  @Test
+  public void checkIsByteOrderCompatible() {
+    WritableMemory wmem = WritableMemory.allocate(8);
+    assertTrue(wmem.isByteOrderCompatible(ByteOrder.nativeOrder()));
+  }
+
+  @Test
+  public void checkXxHash64() {
+    WritableMemory mem = WritableMemory.allocate(8);
+    long out = mem.xxHash64(mem.getLong(0), 1L);
+    assertTrue(out != 0);
+  }
+
+  @Test
+  public void checkTypeDecode() {
+    for (int i = 0; i < 256; i++) {
+      String str = BaseStateImpl.typeDecode(i);
+      println(i + "\t" + str);
+    }
+  }
+
+  @Test
+  public void checkToHexString() {
+    WritableMemory mem = WritableMemory.writableWrap(new byte[16]);
+    println(mem.toHexString("baseMem", 0, 16, true));
+    for (int i = 0; i < 16; i++) { mem.putByte(i, (byte)i); }
+    Buffer buf = mem.asBuffer();
+    println(buf.toHexString("buffer", 0, 16, true));
+  }
+
+  @Test
+  public void checkToMemorySegment() {
+    WritableMemory mem = WritableMemory.allocate(8);
+    mem.toMemorySegment();
+    mem.asByteBufferView(ByteOrder.nativeOrder());
+  }
+
+  /********************/
+  @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/src/test/java17/org/apache/datasketches/memory/internal/Buffer2Test.java b/src/test/java17/org/apache/datasketches/memory/internal/Buffer2Test.java
new file mode 100644
index 0000000..d1eb6cb
--- /dev/null
+++ b/src/test/java17/org/apache/datasketches/memory/internal/Buffer2Test.java
@@ -0,0 +1,434 @@
+/*
+ * 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.BaseState;
+import org.apache.datasketches.memory.Buffer;
+import org.apache.datasketches.memory.Memory;
+import org.apache.datasketches.memory.WritableBuffer;
+import org.apache.datasketches.memory.WritableMemory;
+import org.testng.annotations.Test;
+
+import jdk.incubator.foreign.ResourceScope;
+
+public class Buffer2Test {
+
+  @Test
+  public void testWrapByteBuf() {
+    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()) {
+      assertEquals(bb.get(), buffer.getByte());
+    }
+    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(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(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 = 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);
+  }
+
+  @Test
+  public void checkRORegions() {
+    int n = 16;
+    int n2 = n / 2;
+    long[] arr = new long[n];
+    for (int i = 0; i < n; i++) { arr[i] = i; }
+    Memory mem = Memory.wrap(arr);
+    Buffer buf = mem.asBuffer();
+    Buffer reg = buf.region(n2 * 8, n2 * 8, buf.getByteOrder()); //top half
+    for (int i = 0; i < n2; i++) {
+      long v = reg.getLong(i * 8);
+      long e = i + n2;
+      assertEquals(v, e);
+    }
+  }
+
+  @Test
+  public void testAsMemory() {
+    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);
+    Memory memory = buffer.asMemory();
+
+    assertEquals(buffer.getCapacity(), memory.getCapacity());
+
+    while(buffer.hasRemaining()){
+      assertEquals(memory.getByte(buffer.getPosition()), buffer.getByte());
+    }
+  }
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testROByteBuffer() {
+    byte[] arr = new byte[64];
+    ByteBuffer roBB = ByteBuffer.wrap(arr).asReadOnlyBuffer();
+    Buffer buf = Buffer.wrap(roBB);
+    WritableBuffer wbuf = (WritableBuffer) buf;
+    wbuf.putByte(0, (byte) 1);
+  }
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testROByteBuffer2() {
+    byte[] arr = new byte[64];
+    ByteBuffer roBB = ByteBuffer.wrap(arr).asReadOnlyBuffer();
+    Buffer buf = Buffer.wrap(roBB);
+    WritableBuffer wbuf = (WritableBuffer) buf;
+    wbuf.putByteArray(arr, 0, 64);
+  }
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testIllegalFill() {
+    byte[] arr = new byte[64];
+    ByteBuffer roBB = ByteBuffer.wrap(arr).asReadOnlyBuffer();
+    Buffer buf = Buffer.wrap(roBB);
+    WritableBuffer wbuf = (WritableBuffer) buf;
+    wbuf.fill((byte)0);
+  }
+
+  @Test
+  public void checkWritableWrap() {
+    ByteBuffer bb = ByteBuffer.allocate(16);
+    WritableBuffer buf = WritableBuffer.writableWrap(bb, ByteOrder.nativeOrder(), BaseState.defaultMemReqSvr);
+    assertNotNull(buf);
+  }
+
+  @Test
+  public void testWritableDuplicate() {
+    WritableMemory wmem = WritableMemory.writableWrap(new byte[1]);
+    WritableBuffer wbuf = wmem.asWritableBuffer();
+    WritableBuffer wbuf2 = wbuf.writableDuplicate();
+    assertEquals(wbuf2.getCapacity(), 1);
+    Buffer buf = wmem.asBuffer();
+    assertEquals(buf.getCapacity(), 1);
+  }
+
+  @Test
+  public void checkIndependence() {
+    int cap = 64;
+    ResourceScope scope = ResourceScope.newImplicitScope();
+    WritableMemory wmem = WritableMemory.allocateDirect(cap, scope, null);
+    WritableBuffer wbuf1 = wmem.asWritableBuffer();
+    WritableBuffer wbuf2 = wmem.asWritableBuffer();
+    assertFalse(wbuf1 == wbuf2);
+    assertTrue(wbuf1.nativeOverlap(wbuf2) == cap);
+
+    WritableMemory reg1 = wmem.writableRegion(0, cap);
+    WritableMemory reg2 = wmem.writableRegion(0, cap);
+    assertFalse(reg1 == reg2);
+    assertTrue(reg1.nativeOverlap(reg2) == cap);
+
+
+    WritableBuffer wbuf3 = wbuf1.writableRegion();
+    WritableBuffer wbuf4 = wbuf1.writableRegion();
+    assertFalse(wbuf3 == wbuf4);
+    assertTrue(wbuf3.nativeOverlap(wbuf4) == cap);
+  }
+
+  @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/src/test/java17/org/apache/datasketches/memory/internal/BufferBoundaryCheckTest.java b/src/test/java17/org/apache/datasketches/memory/internal/BufferBoundaryCheckTest.java
new file mode 100644
index 0000000..d45f5f8
--- /dev/null
+++ b/src/test/java17/org/apache/datasketches/memory/internal/BufferBoundaryCheckTest.java
@@ -0,0 +1,185 @@
+/*
+ * 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.fail;
+
+import org.apache.datasketches.memory.WritableMemory;
+import org.testng.annotations.Test;
+
+public class BufferBoundaryCheckTest {
+
+  private final WritableMemory writableMemory = WritableMemory.allocate(8);
+
+  @Test
+  public void testGetByte() {
+    writableMemory.getByte(7);
+    try {
+      writableMemory.getByte(8);
+      fail("Expected IndexOutOfBoundsException");
+    } catch (final IndexOutOfBoundsException expected) {
+      // ignore
+    }
+  }
+
+  @Test
+  public void testPutByte() {
+    writableMemory.putByte(7, (byte) 1);
+    try {
+      writableMemory.putByte(8, (byte) 1);
+      fail("Expected IndexOutOfBoundsException");
+    } catch (final IndexOutOfBoundsException expected) {
+      // ignore
+    }
+  }
+
+  @Test
+  public void testGetChar() {
+    writableMemory.getChar(6);
+    try {
+      writableMemory.getChar(7);
+      fail("Expected IndexOutOfBoundsException");
+    } catch (final IndexOutOfBoundsException expected) {
+      // ignore
+    }
+  }
+
+  @Test
+  public void testPutChar() {
+    writableMemory.putChar(6, 'a');
+    try {
+      writableMemory.putChar(7, 'a');
+      fail("Expected IndexOutOfBoundsException");
+    } catch (final IndexOutOfBoundsException expected) {
+      // ignore
+    }
+  }
+
+  @Test
+  public void testGetShort() {
+    writableMemory.getShort(6);
+    try {
+      writableMemory.getShort(7);
+      fail("Expected IndexOutOfBoundsException");
+    } catch (final IndexOutOfBoundsException expected) {
+      // ignore
+    }
+  }
+
+  @Test
+  public void testPutShort() {
+    writableMemory.putShort(6, (short) 1);
+    try {
+      writableMemory.putShort(7, (short) 1);
+      fail("Expected IndexOutOfBoundsException");
+    } catch (final IndexOutOfBoundsException expected) {
+      // ignore
+    }
+  }
+
+  @Test
+  public void testGetInt() {
+    writableMemory.getInt(4);
+    try {
+      writableMemory.getInt(5);
+      fail("Expected IndexOutOfBoundsException");
+    } catch (final IndexOutOfBoundsException expected) {
+      // ignore
+    }
+  }
+
+  @Test
+  public void testPutInt() {
+    writableMemory.putInt(4, 1);
+    try {
+      writableMemory.putInt(5, 1);
+      fail("Expected IndexOutOfBoundsException");
+    } catch (final IndexOutOfBoundsException expected) {
+      // ignore
+    }
+  }
+
+  @Test
+  public void testGetFloat() {
+    writableMemory.getFloat(4);
+    try {
+      writableMemory.getFloat(5);
+      fail("Expected IndexOutOfBoundsException");
+    } catch (final IndexOutOfBoundsException expected) {
+      // ignore
+    }
+  }
+
+  @Test
+  public void testPutFloat() {
+    writableMemory.putFloat(4, 1f);
+    try {
+      writableMemory.putFloat(5, 1f);
+      fail("Expected IndexOutOfBoundsException");
+    } catch (final IndexOutOfBoundsException expected) {
+      // ignore
+    }
+  }
+
+  @Test
+  public void testGetLong() {
+    writableMemory.getLong(0);
+    try {
+      writableMemory.getLong(1);
+      fail("Expected IndexOutOfBoundsException");
+    } catch (final IndexOutOfBoundsException expected) {
+      // ignore
+    }
+  }
+
+  @Test
+  public void testPutLong() {
+    writableMemory.putLong(0, 1L);
+    try {
+      writableMemory.putLong(1, 1L);
+      fail("Expected IndexOutOfBoundsException");
+    } catch (final IndexOutOfBoundsException expected) {
+      // ignore
+    }
+  }
+
+  @Test
+  public void testGetDouble() {
+    writableMemory.getDouble(0);
+    try {
+      writableMemory.getDouble(1);
+      fail("Expected IndexOutOfBoundsException");
+    } catch (final IndexOutOfBoundsException expected) {
+      // ignore
+    }
+  }
+
+  @Test
+  public void testPutDouble() {
+    writableMemory.putDouble(0, 1d);
+    try {
+      writableMemory.putDouble(1, 1d);
+      fail("Expected IndexOutOfBoundsException");
+    } catch (final IndexOutOfBoundsException expected) {
+      // ignore
+    }
+  }
+
+}
diff --git a/src/test/java17/org/apache/datasketches/memory/internal/BufferInvariantsTest.java b/src/test/java17/org/apache/datasketches/memory/internal/BufferInvariantsTest.java
new file mode 100644
index 0000000..4b233be
--- /dev/null
+++ b/src/test/java17/org/apache/datasketches/memory/internal/BufferInvariantsTest.java
@@ -0,0 +1,345 @@
+/*
+ * 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.fail;
+
+import java.nio.ByteBuffer;
+
+import org.apache.datasketches.memory.BaseState;
+import org.apache.datasketches.memory.Buffer;
+import org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableBuffer;
+import org.apache.datasketches.memory.WritableMemory;
+import org.testng.annotations.Test;
+
+import jdk.incubator.foreign.ResourceScope;
+
+/**
+ * @author Lee Rhodes
+ */
+public class BufferInvariantsTest {
+  private static final MemoryRequestServer memReqSvr = BaseState.defaultMemReqSvr;
+
+  @Test
+  public void testRegion() {
+    ByteBuffer byteBuffer = ByteBuffer.allocate(10);
+    byteBuffer.limit(7);
+    Buffer buff = Buffer.wrap(byteBuffer); //assuming buff has cap of 8
+    assertEquals(buff.getCapacity(), 10); //wrong should be 8
+    buff.getByte(); //pos moves to 1
+    Buffer copyBuff = buff.region(); //pos: 0, start: 0, end: 6: cap: 7
+    assertEquals(copyBuff.getEnd(), 6);
+    assertEquals(copyBuff.getCapacity(), 6);
+    assertEquals(copyBuff.getStart(), 0);
+    assertEquals(copyBuff.getPosition(), 0);
+
+    buff.setStartPositionEnd(1, 1, 5);
+    buff.getByte();
+    Buffer copyBuff2 = buff.region();
+    assertEquals(copyBuff2.getEnd(), 3);
+    assertEquals(copyBuff2.getCapacity(), 3);
+    assertEquals(copyBuff2.getStart(), 0);
+    assertEquals(copyBuff2.getPosition(), 0);
+  }
+
+  @Test
+  public void testBB() {
+    int n = 25;
+    ByteBuffer bb = ByteBuffer.allocate(n);
+    for (byte i = 0; i < n; i++) { bb.put(i, i); }
+    assertEquals(bb.position(), 0);
+    assertEquals(bb.limit(), n);
+    assertEquals(bb.get(0), 0);
+//    print("Orig : ");
+//    printbb(bb);
+
+    bb.limit(20);
+    bb.position(5);
+    assertEquals(bb.remaining(), 15);
+//    print("Set  : ");
+//    printbb(bb);
+
+    ByteBuffer dup = bb.duplicate();
+    assertEquals(dup.position(), 5);
+    assertEquals(dup.limit(), 20);
+    assertEquals(dup.capacity(), 25);
+//    print("Dup  : ");
+//    printbb(dup);
+
+    ByteBuffer sl = bb.slice();
+    assertEquals(sl.position(), 0);
+    assertEquals(sl.limit(), 15);
+    assertEquals(sl.capacity(), 15);
+//    print("Slice: ");
+//    printbb(sl);
+  }
+
+  @Test
+  public void testBuf() {
+    int n = 25;
+    WritableBuffer buf = WritableMemory.allocate(n).asWritableBuffer();
+    for (byte i = 0; i < n; i++) { buf.putByte(i); }
+    buf.setPosition(0);
+    assertEquals(buf.getPosition(), 0);
+    assertEquals(buf.getEnd(), 25);
+    assertEquals(buf.getCapacity(), 25);
+//    print("Orig  : ");
+//    printbuf(buf);
+
+    buf.setStartPositionEnd(0, 5, 20);
+    assertEquals(buf.getRemaining(), 15);
+    assertEquals(buf.getCapacity(), 25);
+    assertEquals(buf.getByte(), 5);
+    buf.setPosition(5);
+//    print("Set   : ");
+//    printbuf(buf);
+
+    Buffer dup = buf.duplicate();
+    assertEquals(dup.getRemaining(), 15);
+    assertEquals(dup.getCapacity(), 25);
+    assertEquals(dup.getByte(), 5);
+    dup.setPosition(5);
+//    print("Dup   : ");
+//    printbuf(dup);
+
+
+    Buffer reg = buf.region();
+    assertEquals(reg.getPosition(), 0);
+    assertEquals(reg.getEnd(), 15);
+    assertEquals(reg.getRemaining(), 15);
+    assertEquals(reg.getCapacity(), 15);
+    assertEquals(reg.getByte(), 5);
+    reg.setPosition(0);
+//    print("Region: ");
+//    printbuf(reg);
+  }
+
+  @Test
+  public void testBufWrap() {
+    int n = 25;
+    ByteBuffer bb = ByteBuffer.allocate(n);
+    for (byte i = 0; i < n; i++) { bb.put(i, i); }
+
+    bb.position(5);
+    bb.limit(20);
+
+    Buffer buf = Buffer.wrap(bb);
+    assertEquals(buf.getPosition(), 5);
+    assertEquals(buf.getEnd(), 20);
+    assertEquals(buf.getRemaining(), 15);
+    assertEquals(buf.getCapacity(), 25);
+    assertEquals(buf.getByte(), 5);
+    buf.setPosition(5);
+//    print("Buf.wrap: ");
+//    printbuf(buf);
+
+    Buffer reg = buf.region();
+    assertEquals(reg.getPosition(), 0);
+    assertEquals(reg.getEnd(), 15);
+    assertEquals(reg.getRemaining(), 15);
+    assertEquals(reg.getCapacity(), 15);
+    assertEquals(reg.getByte(), 5);
+    reg.setPosition(0);
+//    print("Buf.region: ");
+//    printbuf(reg);
+  }
+
+  @Test
+  public void checkLimitsDirect() throws Exception {
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      WritableMemory wmem = WritableMemory.allocateDirect(100, scope, memReqSvr);
+      Buffer buf = wmem.asBuffer();
+      buf.setStartPositionEnd(40, 45, 50);
+      buf.setStartPositionEnd(0, 0, 100);
+      try {
+        buf.setStartPositionEnd(0, 0, 101);
+        fail();
+      } catch (AssertionError e) {
+        //
+      }
+    }
+  }
+
+  @Test
+  public void testRegionDirect() {
+    ByteBuffer byteBuffer = ByteBuffer.allocate(10);
+    byteBuffer.limit(7);
+    Buffer buff = Buffer.wrap(byteBuffer); //assuming buff has cap of 8
+    assertEquals(buff.getCapacity(), 10); //wrong should be 8
+    buff.getByte(); //pos moves to 1
+    Buffer copyBuff = buff.region(); //pos: 0, start: 0, end: 6: cap: 7
+    assertEquals(copyBuff.getEnd(), 6);
+    assertEquals(copyBuff.getCapacity(), 6);
+    assertEquals(copyBuff.getStart(), 0);
+    assertEquals(copyBuff.getPosition(), 0);
+
+    buff.setStartPositionEnd(1, 1, 5);
+    buff.getByte();
+    Buffer copyBuff2 = buff.region();
+    assertEquals(copyBuff2.getEnd(), 3);
+    assertEquals(copyBuff2.getCapacity(), 3);
+    assertEquals(copyBuff2.getStart(), 0);
+    assertEquals(copyBuff2.getPosition(), 0);
+  }
+
+  @Test
+  public void testBBDirect() {
+    int n = 25;
+    ByteBuffer bb = ByteBuffer.allocateDirect(n);
+    for (byte i = 0; i < n; i++) { bb.put(i, i); }
+    assertEquals(bb.position(), 0);
+    assertEquals(bb.limit(), n);
+    assertEquals(bb.get(0), 0);
+//    print("Orig : ");
+//    printbb(bb);
+
+    bb.limit(20);
+    bb.position(5);
+    assertEquals(bb.remaining(), 15);
+//    print("Set  : ");
+//    printbb(bb);
+
+    ByteBuffer dup = bb.duplicate();
+    assertEquals(dup.position(), 5);
+    assertEquals(dup.limit(), 20);
+    assertEquals(dup.capacity(), 25);
+//    print("Dup  : ");
+//    printbb(dup);
+
+    ByteBuffer sl = bb.slice();
+    assertEquals(sl.position(), 0);
+    assertEquals(sl.limit(), 15);
+    assertEquals(sl.capacity(), 15);
+//    print("Slice: ");
+//    printbb(sl);
+  }
+
+  @Test
+  public void testBufDirect() throws Exception {
+    int n = 25;
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+    WritableMemory wmem = WritableMemory.allocateDirect(n, scope, memReqSvr);
+    WritableBuffer buf = wmem.asWritableBuffer();
+    for (byte i = 0; i < n; i++) { buf.putByte(i); }
+    buf.setPosition(0);
+    assertEquals(buf.getPosition(), 0);
+    assertEquals(buf.getEnd(), 25);
+    assertEquals(buf.getCapacity(), 25);
+//    print("Orig  : ");
+//    printbuf(buf);
+
+    buf.setStartPositionEnd(0, 5, 20);
+    assertEquals(buf.getRemaining(), 15);
+    assertEquals(buf.getCapacity(), 25);
+    assertEquals(buf.getByte(), 5);
+    buf.setPosition(5);
+//    print("Set   : ");
+//    printbuf(buf);
+
+    Buffer dup = buf.duplicate();
+    assertEquals(dup.getRemaining(), 15);
+    assertEquals(dup.getCapacity(), 25);
+    assertEquals(dup.getByte(), 5);
+    dup.setPosition(5);
+//    print("Dup   : ");
+//    printbuf(dup);
+
+
+    Buffer reg = buf.region();
+    assertEquals(reg.getPosition(), 0);
+    assertEquals(reg.getEnd(), 15);
+    assertEquals(reg.getRemaining(), 15);
+    assertEquals(reg.getCapacity(), 15);
+    assertEquals(reg.getByte(), 5);
+    reg.setPosition(0);
+//    print("Region: ");
+//    printbuf(reg);
+    }
+  }
+
+  @Test
+  public void testBufWrapDirect() {
+    int n = 25;
+    ByteBuffer bb = ByteBuffer.allocateDirect(n);
+    for (byte i = 0; i < n; i++) { bb.put(i, i); }
+
+    bb.position(5);
+    bb.limit(20);
+
+    Buffer buf = Buffer.wrap(bb);
+    assertEquals(buf.getPosition(), 5);
+    assertEquals(buf.getEnd(), 20);
+    assertEquals(buf.getRemaining(), 15);
+    assertEquals(buf.getCapacity(), 25);
+    assertEquals(buf.getByte(), 5);
+    buf.setPosition(5);
+//    print("Buf.wrap: ");
+//    printbuf(buf);
+
+    Buffer reg = buf.region();
+    assertEquals(reg.getPosition(), 0);
+    assertEquals(reg.getEnd(), 15);
+    assertEquals(reg.getRemaining(), 15);
+    assertEquals(reg.getCapacity(), 15);
+    assertEquals(reg.getByte(), 5);
+    reg.setPosition(0);
+//    print("Buf.region: ");
+//    printbuf(reg);
+  }
+
+
+  static void printbb(ByteBuffer bb) {
+    println("pos: " + bb.position() + ", lim: " + bb.limit() + ", cap: " + bb.capacity());
+    int rem = bb.remaining();
+    int pos = bb.position();
+    int i;
+    for (i = 0; i < (rem-1); i++) {
+      print(bb.get(i+ pos) + ", ");
+    }
+    println(bb.get(i + pos) + "\n");
+  }
+
+  static void printbuf(Buffer buf) {
+    println("pos: " + buf.getPosition() + ", end: " + buf.getEnd() + ", cap: " + buf.getCapacity());
+    long rem = buf.getRemaining();
+    long pos = buf.getPosition();
+    int i;
+    for (i = 0; i < (rem-1); i++) {
+      print(buf.getByte(i+ pos) + ", ");
+    }
+    println(buf.getByte(i + pos) + "\n");
+  }
+
+  /**
+   * @param s value to print
+   */
+  static void println(String s) {
+    //System.out.println(s); //disable here
+  }
+
+  /**
+   * @param s value to print
+   */
+  static void print(String s) {
+    //System.out.print(s); //disable here
+  }
+}
diff --git a/src/test/java17/org/apache/datasketches/memory/internal/BufferReadWriteSafetyTest.java b/src/test/java17/org/apache/datasketches/memory/internal/BufferReadWriteSafetyTest.java
new file mode 100644
index 0000000..4e375d6
--- /dev/null
+++ b/src/test/java17/org/apache/datasketches/memory/internal/BufferReadWriteSafetyTest.java
@@ -0,0 +1,180 @@
+/*
+ * 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 org.apache.datasketches.memory.Buffer;
+import org.apache.datasketches.memory.WritableBuffer;
+import org.apache.datasketches.memory.WritableMemory;
+import org.testng.annotations.Test;
+
+public class BufferReadWriteSafetyTest {
+
+  // Test various operations with read-only Buffer
+
+  private final WritableBuffer buf = (WritableBuffer) Buffer.wrap(ByteBuffer.allocate(8));
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testPutByte() {
+    buf.setPosition(0);
+    buf.putByte(0, (byte) 1);
+  }
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testPutBytePositional() {
+    buf.setPosition(0);
+    buf.putByte((byte) 1);
+  }
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testPutShort() {
+    buf.setPosition(0);
+    buf.putShort(0, (short) 1);
+  }
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testPutShortPositional() {
+    buf.setPosition(0);
+    buf.putShort((short) 1);
+  }
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testPutChar() {
+    buf.setPosition(0);
+    buf.putChar(0, (char) 1);
+  }
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testPutCharPositional() {
+    buf.setPosition(0);
+    buf.putChar((char) 1);
+  }
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testPutInt() {
+    buf.setPosition(0);
+    buf.putInt(0, 1);
+  }
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testPutIntPositional() {
+    buf.setPosition(0);
+    buf.putInt(1);
+  }
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testPutLong() {
+    buf.setPosition(0);
+    buf.putLong(0, 1);
+  }
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testPutLongPositional() {
+    buf.setPosition(0);
+    buf.putLong(1);
+  }
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testPutFloat() {
+    buf.setPosition(0);
+    buf.putFloat(0, 1);
+  }
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testPutFloatPositional() {
+    buf.setPosition(0);
+    buf.putFloat(1);
+  }
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testPutDouble() {
+    buf.setPosition(0);
+    buf.putDouble(0, 1);
+  }
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testPutDoublePositional() {
+    buf.setPosition(0);
+    buf.putDouble(1);
+  }
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testPutByteArray() {
+    buf.setPosition(0);
+    buf.putByteArray(new byte[] {1}, 0, 1);
+  }
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testPutShortArray() {
+    buf.setPosition(0);
+    buf.putShortArray(new short[] {1}, 0, 1);
+  }
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testPutCharArray() {
+    buf.setPosition(0);
+    buf.putCharArray(new char[] {1}, 0, 1);
+  }
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testPutIntArray() {
+    buf.setPosition(0);
+    buf.putIntArray(new int[] {1}, 0, 1);
+  }
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testPutLongArray() {
+    buf.setPosition(0);
+    buf.putLongArray(new long[] {1}, 0, 1);
+  }
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testPutFloatArray() {
+    buf.setPosition(0);
+    buf.putFloatArray(new float[] {1}, 0, 1);
+  }
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testDoubleByteArray() {
+    buf.setPosition(0);
+    buf.putDoubleArray(new double[] {1}, 0, 1);
+  }
+
+  // Now, test that various ways to obtain a read-only buffer produce a read-only buffer indeed
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testWritableMemoryAsBuffer() {
+    WritableBuffer buf1 = (WritableBuffer) WritableMemory.allocate(8).asBuffer();
+    buf1.putInt(1);
+  }
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testWritableBufferRegion() {
+    WritableBuffer buf1 = (WritableBuffer) WritableMemory.allocate(8).asWritableBuffer().region();
+    buf1.putInt(1);
+  }
+
+  @Test(expectedExceptions = UnsupportedOperationException.class)
+  public void testWritableBufferDuplicate() {
+    WritableBuffer buf1 = (WritableBuffer) WritableMemory.allocate(8).asWritableBuffer().duplicate();
+    buf1.putInt(1);
+  }
+}
diff --git a/src/test/java17/org/apache/datasketches/memory/internal/BufferTest.java b/src/test/java17/org/apache/datasketches/memory/internal/BufferTest.java
new file mode 100644
index 0000000..c9e58f0
--- /dev/null
+++ b/src/test/java17/org/apache/datasketches/memory/internal/BufferTest.java
@@ -0,0 +1,327 @@
+/*
+ * 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 java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.List;
+
+import org.apache.datasketches.memory.BaseState;
+import org.apache.datasketches.memory.Buffer;
+import org.apache.datasketches.memory.Memory;
+import org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableBuffer;
+import org.apache.datasketches.memory.WritableMemory;
+import org.testng.annotations.Test;
+import org.testng.collections.Lists;
+
+import jdk.incubator.foreign.ResourceScope;
+
+public class BufferTest {
+  private final MemoryRequestServer memReqSvr = BaseState.defaultMemReqSvr;
+  @Test
+  public void checkDirectRoundTrip() throws Exception {
+    int n = 1024; //longs
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+    WritableMemory wmem = WritableMemory.allocateDirect(n * 8, scope, memReqSvr);
+      WritableBuffer wbuf = wmem.asWritableBuffer();
+      for (int i = 0; i < n; i++) {
+        wbuf.putLong(i);
+      }
+      wbuf.resetPosition();
+      for (int i = 0; i < n; i++) {
+        long v = wbuf.getLong();
+        assertEquals(v, i);
+      }
+    }
+  }
+
+  @Test
+  public void checkAutoHeapRoundTrip() {
+    int n = 1024; //longs
+    WritableBuffer wbuf = WritableMemory.allocate(n * 8).asWritableBuffer();
+    for (int i = 0; i < n; i++) {
+      wbuf.putLong(i);
+    }
+    wbuf.resetPosition();
+    for (int i = 0; i < n; i++) {
+      long v = wbuf.getLong();
+      assertEquals(v, i);
+    }
+  }
+
+  @Test
+  public void checkArrayWrap() {
+    int n = 1024; //longs
+    byte[] arr = new byte[n * 8];
+    WritableBuffer wbuf = WritableMemory.writableWrap(arr).asWritableBuffer();
+    for (int i = 0; i < n; i++) {
+      wbuf.putLong(i);
+    }
+    wbuf.resetPosition();
+    for (int i = 0; i < n; i++) {
+      long v = wbuf.getLong();
+      assertEquals(v, i);
+    }
+    Buffer buf = Memory.wrap(arr).asBuffer();
+    buf.resetPosition();
+    for (int i = 0; i < n; i++) {
+      long v = buf.getLong();
+      assertEquals(v, i);
+    }
+    // Check Zero length array wraps
+    Memory mem = Memory.wrap(new byte[0]);
+    Buffer buffZeroLengthArrayWrap = mem.asBuffer();
+    assertEquals(buffZeroLengthArrayWrap.getCapacity(), 0);
+    // check 0 length array wraps
+    List<Buffer> buffersToCheck = Lists.newArrayList();
+    buffersToCheck.add(WritableMemory.allocate(0).asBuffer());
+    buffersToCheck.add(WritableBuffer.writableWrap(ByteBuffer.allocate(0)));
+    buffersToCheck.add(Buffer.wrap(ByteBuffer.allocate(0)));
+    buffersToCheck.add(Memory.wrap(new byte[0]).asBuffer());
+    buffersToCheck.add(Memory.wrap(new char[0]).asBuffer());
+    buffersToCheck.add(Memory.wrap(new short[0]).asBuffer());
+    buffersToCheck.add(Memory.wrap(new int[0]).asBuffer());
+    buffersToCheck.add(Memory.wrap(new long[0]).asBuffer());
+    buffersToCheck.add(Memory.wrap(new float[0]).asBuffer());
+    buffersToCheck.add(Memory.wrap(new double[0]).asBuffer());
+    //Check the buffer lengths
+    for (Buffer buffer : buffersToCheck) {
+      assertEquals(buffer.getCapacity(), 0);
+    }
+  }
+
+  @Test
+  public void simpleBBTest() {
+    int n = 1024; //longs
+    byte[] arr = new byte[n * 8];
+    ByteBuffer bb = ByteBuffer.wrap(arr);
+    bb.order(ByteOrder.nativeOrder());
+
+    WritableBuffer wbuf = WritableBuffer.writableWrap(bb);
+    for (int i = 0; i < n; i++) { //write to wbuf
+      wbuf.putLong(i);
+    }
+    wbuf.resetPosition();
+    for (int i = 0; i < n; i++) { //read from wbuf
+      long v = wbuf.getLong();
+      assertEquals(v, i);
+    }
+    for (int i = 0; i < n; i++) { //read from BB
+      long v = bb.getLong();
+      assertEquals(v, i);
+    }
+  }
+
+  @Test
+  public void checkByteBufHeap() {
+    int n = 1024; //longs
+    byte[] arr = new byte[n * 8];
+    ByteBuffer bb = ByteBuffer.wrap(arr);
+    bb.order(ByteOrder.nativeOrder());
+
+    WritableBuffer wbuf = WritableBuffer.writableWrap(bb);
+    for (int i = 0; i < n; i++) { //write to wbuf
+      wbuf.putLong(i);
+    }
+    wbuf.resetPosition();
+    for (int i = 0; i < n; i++) { //read from wbuf
+      long v = wbuf.getLong();
+      assertEquals(v, i);
+    }
+    for (int i = 0; i < n; i++) { //read from BB
+      long v = bb.getLong(i * 8);
+      assertEquals(v, i);
+    }
+    Buffer buf1 = Memory.wrap(arr).asBuffer();
+    for (int i = 0; i < n; i++) { //read from wrapped arr
+      long v = buf1.getLong();
+      assertEquals(v, i);
+    }
+    //convert to wbuf to RO
+    Buffer buf = wbuf;
+    buf.resetPosition();
+    for (int i = 0; i < n; i++) {
+      long v = buf.getLong();
+      assertEquals(v, i);
+    }
+  }
+
+  @Test
+  public void checkByteBufDirect() {
+    int n = 1024; //longs
+    ByteBuffer bb = ByteBuffer.allocateDirect(n * 8);
+    bb.order(ByteOrder.nativeOrder());
+
+    WritableBuffer wbuf = WritableBuffer.writableWrap(bb);
+    for (int i = 0; i < n; i++) { //write to wmem
+      wbuf.putLong(i);
+    }
+    wbuf.resetPosition();
+    for (int i = 0; i < n; i++) { //read from wmem
+      long v = wbuf.getLong();
+      assertEquals(v, i);
+    }
+    for (int i = 0; i < n; i++) { //read from BB
+      long v = bb.getLong(i * 8);
+      assertEquals(v, i);
+    }
+    Buffer buf1 = Buffer.wrap(bb);
+    for (int i = 0; i < n; i++) { //read from wrapped bb RO
+      long v = buf1.getLong();
+      assertEquals(v, i);
+    }
+    //convert to RO
+    Buffer buf = wbuf;
+    buf.resetPosition();
+    for (int i = 0; i < n; i++) {
+      long v = buf.getLong();
+      assertEquals(v, i);
+    }
+  }
+
+  @Test
+  public void checkByteBufBigEndianOrder() {
+    int n = 1024; //longs
+    ByteBuffer bb = ByteBuffer.allocate(n * 8);
+    bb.order(BaseState.NON_NATIVE_BYTE_ORDER);
+    Buffer buf = Buffer.wrap(bb);
+    assertEquals(buf.getByteOrder(), ByteOrder.nativeOrder());
+  }
+
+  @Test
+  public void checkReadOnlyHeapByteBuffer() {
+    ByteBuffer bb = ByteBuffer.allocate(128);
+    bb.order(ByteOrder.nativeOrder());
+    for (int i = 0; i < 128; i++) { bb.put(i, (byte)i); }
+
+    bb.position(64);
+    ByteBuffer slice = bb.slice().asReadOnlyBuffer();
+    slice.order(ByteOrder.nativeOrder());
+
+    Buffer buf = Buffer.wrap(slice);
+    for (int i = 0; i < 64; i++) {
+      assertEquals(buf.getByte(), 64 + i);
+    }
+    buf.toHexString("slice", 0, slice.capacity(), true);
+    //println(s);
+  }
+
+  @Test
+  public void checkPutGetArraysHeap() {
+    int n = 1024; //longs
+    long[] arr = new long[n];
+    for (int i = 0; i < n; i++) { arr[i] = i; }
+
+    WritableBuffer wbuf = WritableMemory.allocate(n * 8).asWritableBuffer();
+    wbuf.putLongArray(arr, 0, n);
+    long[] arr2 = new long[n];
+    wbuf.resetPosition();
+    wbuf.getLongArray(arr2, 0, n);
+    for (int i = 0; i < n; i++) {
+      assertEquals(arr2[i], i);
+    }
+  }
+
+  @Test
+  public void checkRORegions() {
+    int n = 16;
+    int n2 = n / 2;
+    long[] arr = new long[n];
+    for (int i = 0; i < n; i++) { arr[i] = i; }
+
+    Buffer buf = Memory.wrap(arr).asBuffer();
+    buf.setPosition(n2 * 8);
+    Buffer reg = buf.region();
+    for (int i = 0; i < n2; i++) {
+      long v = reg.getLong();
+      assertEquals(v, i + n2);
+      //println("" + v);
+    }
+  }
+
+  @Test
+  public void checkWRegions() {
+    int n = 16;
+    int n2 = n / 2;
+    long[] arr = new long[n];
+    for (int i = 0; i < n; i++) { arr[i] = i; }
+    WritableBuffer wbuf = WritableMemory.writableWrap(arr).asWritableBuffer();
+    for (int i = 0; i < n; i++) {
+      assertEquals(wbuf.getLong(), i); //write all
+      //println("" + wmem.getLong(i * 8));
+    }
+    //println("");
+    wbuf.setPosition(n2 * 8);
+    WritableBuffer reg = wbuf.writableRegion();
+    for (int i = 0; i < n2; i++) { reg.putLong(i); } //rewrite top half
+    wbuf.resetPosition();
+    for (int i = 0; i < n; i++) {
+      assertEquals(wbuf.getLong(), i % 8);
+      //println("" + wmem.getLong(i * 8));
+    }
+  }
+
+  @SuppressWarnings("resource")
+  @Test(expectedExceptions = IllegalStateException.class)
+  public void checkParentUseAfterFree() throws Exception {
+    int bytes = 64 * 8;
+    WritableMemory wmem = WritableMemory.allocateDirect(bytes, ResourceScope.newConfinedScope(), memReqSvr);
+    WritableBuffer wbuf = wmem.asWritableBuffer();
+    wbuf.close();
+    //with -ea assert: Memory not valid.
+    //with -da sometimes segfaults, sometimes passes!
+    wbuf.getLong();
+  }
+
+  @SuppressWarnings("resource")
+  @Test(expectedExceptions = IllegalStateException.class)
+  public void checkRegionUseAfterFree() throws Exception {
+    int bytes = 64;
+    WritableMemory wmem = WritableMemory.allocateDirect(bytes, ResourceScope.newConfinedScope(), memReqSvr);
+    Buffer region = wmem.asBuffer().region();
+    region.close();
+    //with -ea assert: Memory not valid.
+    //with -da sometimes segfaults, sometimes passes!
+    region.getByte();
+  }
+
+  @Test(expectedExceptions = AssertionError.class)
+  public void checkBaseBufferInvariants() {
+    WritableBuffer wbuf = WritableMemory.allocate(64).asWritableBuffer();
+    wbuf.setStartPositionEnd(1, 0, 2); //out of order
+  }
+
+
+  @Test
+  public void printlnTest() {
+    println("PRINTING: "+this.getClass().getName());
+  }
+
+  /**
+   * @param s String to print
+   */
+  static void println(final String s) {
+    //System.out.println(s);
+  }
+
+}
diff --git a/src/test/java17/org/apache/datasketches/memory/internal/CommonBufferTest.java b/src/test/java17/org/apache/datasketches/memory/internal/CommonBufferTest.java
new file mode 100644
index 0000000..8d3b519
--- /dev/null
+++ b/src/test/java17/org/apache/datasketches/memory/internal/CommonBufferTest.java
@@ -0,0 +1,430 @@
+/*
+ * 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 org.apache.datasketches.memory.BaseState;
+import org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableBuffer;
+import org.apache.datasketches.memory.WritableMemory;
+import org.testng.annotations.Test;
+
+import jdk.incubator.foreign.ResourceScope;
+
+public class CommonBufferTest {
+  private final MemoryRequestServer memReqSvr = BaseState.defaultMemReqSvr;
+  @Test
+  public void checkSetGet() throws Exception {
+    int memCapacity = 60; //must be at least 60
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      WritableMemory mem = WritableMemory.allocateDirect(memCapacity, scope, memReqSvr);
+      WritableBuffer buf = mem.asWritableBuffer();
+      assertEquals(buf.getCapacity(), memCapacity);
+      setGetTests(buf);
+      setGetTests2(buf);
+    }
+  }
+
+  public static void setGetTests(WritableBuffer buf) {
+    buf.putBoolean(true);
+    buf.putBoolean(false);
+    buf.putByte((byte) -1);
+    buf.putByte((byte) 0);
+    buf.putChar('A');
+    buf.putChar('Z');
+    buf.putShort(Short.MAX_VALUE);
+    buf.putShort(Short.MIN_VALUE);
+    buf.putInt(Integer.MAX_VALUE);
+    buf.putInt(Integer.MIN_VALUE);
+    buf.putFloat(Float.MAX_VALUE);
+    buf.putFloat(Float.MIN_VALUE);
+    buf.putLong(Long.MAX_VALUE);
+    buf.putLong(Long.MIN_VALUE);
+    buf.putDouble(Double.MAX_VALUE);
+    buf.putDouble(Double.MIN_VALUE);
+
+    buf.resetPosition();
+
+    assertEquals(buf.getBoolean(buf.getPosition()), true);
+    assertEquals(buf.getBoolean(), true);
+    assertEquals(buf.getBoolean(buf.getPosition()), false);
+    assertEquals(buf.getBoolean(), false);
+    assertEquals(buf.getByte(buf.getPosition()), (byte) -1);
+    assertEquals(buf.getByte(), (byte) -1);
+    assertEquals(buf.getByte(buf.getPosition()), (byte)0);
+    assertEquals(buf.getByte(), (byte)0);
+    assertEquals(buf.getChar(buf.getPosition()), 'A');
+    assertEquals(buf.getChar(), 'A');
+    assertEquals(buf.getChar(buf.getPosition()), 'Z');
+    assertEquals(buf.getChar(), 'Z');
+    assertEquals(buf.getShort(buf.getPosition()), Short.MAX_VALUE);
+    assertEquals(buf.getShort(), Short.MAX_VALUE);
+    assertEquals(buf.getShort(buf.getPosition()), Short.MIN_VALUE);
+    assertEquals(buf.getShort(), Short.MIN_VALUE);
+    assertEquals(buf.getInt(buf.getPosition()), Integer.MAX_VALUE);
+    assertEquals(buf.getInt(), Integer.MAX_VALUE);
+    assertEquals(buf.getInt(buf.getPosition()), Integer.MIN_VALUE);
+    assertEquals(buf.getInt(), Integer.MIN_VALUE);
+    assertEquals(buf.getFloat(buf.getPosition()), Float.MAX_VALUE);
+    assertEquals(buf.getFloat(), Float.MAX_VALUE);
+    assertEquals(buf.getFloat(buf.getPosition()), Float.MIN_VALUE);
+    assertEquals(buf.getFloat(), Float.MIN_VALUE);
+    assertEquals(buf.getLong(buf.getPosition()), Long.MAX_VALUE);
+    assertEquals(buf.getLong(), Long.MAX_VALUE);
+    assertEquals(buf.getLong(buf.getPosition()), Long.MIN_VALUE);
+    assertEquals(buf.getLong(), Long.MIN_VALUE);
+    assertEquals(buf.getDouble(buf.getPosition()), Double.MAX_VALUE);
+    assertEquals(buf.getDouble(), Double.MAX_VALUE);
+    assertEquals(buf.getDouble(buf.getPosition()), Double.MIN_VALUE);
+    assertEquals(buf.getDouble(), Double.MIN_VALUE);
+  }
+
+  public static void setGetTests2(WritableBuffer buf) {
+    buf.putBoolean(0, true);
+    buf.putBoolean(1, false);
+    buf.putByte(2, (byte) -1);
+    buf.putByte(3, (byte) 0);
+    buf.putChar(4,'A');
+    buf.putChar(6,'Z');
+    buf.putShort(8, Short.MAX_VALUE);
+    buf.putShort(10, Short.MIN_VALUE);
+    buf.putInt(12, Integer.MAX_VALUE);
+    buf.putInt(16, Integer.MIN_VALUE);
+    buf.putFloat(20, Float.MAX_VALUE);
+    buf.putFloat(24, Float.MIN_VALUE);
+    buf.putLong(28, Long.MAX_VALUE);
+    buf.putLong(36, Long.MIN_VALUE);
+    buf.putDouble(44, Double.MAX_VALUE);
+    buf.putDouble(52, Double.MIN_VALUE);
+
+    assertEquals(buf.getBoolean(0), true);
+    assertEquals(buf.getBoolean(1), false);
+    assertEquals(buf.getByte(2), (byte) -1);
+    assertEquals(buf.getByte(3), (byte)0);
+    assertEquals(buf.getChar(4), 'A');
+    assertEquals(buf.getChar(6), 'Z');
+    assertEquals(buf.getShort(8), Short.MAX_VALUE);
+    assertEquals(buf.getShort(10), Short.MIN_VALUE);
+    assertEquals(buf.getInt(12), Integer.MAX_VALUE);
+    assertEquals(buf.getInt(16), Integer.MIN_VALUE);
+    assertEquals(buf.getFloat(20), Float.MAX_VALUE);
+    assertEquals(buf.getFloat(24), Float.MIN_VALUE);
+    assertEquals(buf.getLong(28), Long.MAX_VALUE);
+    assertEquals(buf.getLong(36), Long.MIN_VALUE);
+    assertEquals(buf.getDouble(44), Double.MAX_VALUE);
+    assertEquals(buf.getDouble(52), Double.MIN_VALUE);
+  }
+
+  @Test
+  public void checkSetGetArrays() throws Exception {
+    int memCapacity = 32;
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      WritableMemory mem = WritableMemory.allocateDirect(memCapacity, scope, memReqSvr);
+      WritableBuffer buf = mem.asWritableBuffer();
+      assertEquals(buf.getCapacity(), memCapacity);
+      setGetArraysTests(buf);
+    }
+  }
+
+  public static void setGetArraysTests(WritableBuffer buf) {
+    int words = 4;
+
+    byte[] srcArray2 = { 1, -2, 3, -4 };
+    byte[] dstArray2 = new byte[4];
+    buf.resetPosition();
+    buf.putByteArray(srcArray2, 0, words);
+    buf.resetPosition();
+    buf.getByteArray(dstArray2, 0, words);
+    for (int i=0; i<words; i++) {
+      assertEquals(dstArray2[i], srcArray2[i]);
+    }
+
+    char[] srcArray3 = { 'A', 'B', 'C', 'D' };
+    char[] dstArray3 = new char[words];
+    buf.resetPosition();
+    buf.putCharArray(srcArray3, 0, words);
+    buf.resetPosition();
+    buf.getCharArray(dstArray3, 0, words);
+    for (int i=0; i<words; i++) {
+      assertEquals(dstArray3[i], srcArray3[i]);
+    }
+
+    double[] srcArray4 = { 1.0, -2.0, 3.0, -4.0 };
+    double[] dstArray4 = new double[words];
+    buf.resetPosition();
+    buf.putDoubleArray(srcArray4, 0, words);
+    buf.resetPosition();
+    buf.getDoubleArray(dstArray4, 0, words);
+    for (int i=0; i<words; i++) {
+      assertEquals(dstArray4[i], srcArray4[i], 0.0);
+    }
+
+    float[] srcArray5 = { 1.0F, -2.0F, 3.0F, -4.0F };
+    float[] dstArray5 = new float[words];
+    buf.resetPosition();
+    buf.putFloatArray(srcArray5, 0, words);
+    buf.resetPosition();
+    buf.getFloatArray(dstArray5, 0, words);
+    for (int i=0; i<words; i++) {
+      assertEquals(dstArray5[i], srcArray5[i], 0.0);
+    }
+
+    int[] srcArray6 = { 1, -2, 3, -4 };
+    int[] dstArray6 = new int[words];
+    buf.resetPosition();
+    buf.putIntArray(srcArray6, 0, words);
+    buf.resetPosition();
+    buf.getIntArray(dstArray6, 0, words);
+    for (int i=0; i<words; i++) {
+      assertEquals(dstArray6[i], srcArray6[i]);
+    }
+
+    long[] srcArray7 = { 1, -2, 3, -4 };
+    long[] dstArray7 = new long[words];
+    buf.resetPosition();
+    buf.putLongArray(srcArray7, 0, words);
+    buf.resetPosition();
+    buf.getLongArray(dstArray7, 0, words);
+    for (int i=0; i<words; i++) {
+      assertEquals(dstArray7[i], srcArray7[i]);
+    }
+
+    short[] srcArray8 = { 1, -2, 3, -4 };
+    short[] dstArray8 = new short[words];
+    buf.resetPosition();
+    buf.putShortArray(srcArray8, 0, words);
+    buf.resetPosition();
+    buf.getShortArray(dstArray8, 0, words);
+    for (int i=0; i<words; i++) {
+      assertEquals(dstArray8[i], srcArray8[i]);
+    }
+  }
+
+  @Test
+  public void checkSetGetPartialArraysWithOffset() throws Exception {
+    int memCapacity = 32;
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      WritableMemory mem = WritableMemory.allocateDirect(memCapacity, scope, memReqSvr);
+      WritableBuffer buf = mem.asWritableBuffer();
+      assertEquals(buf.getCapacity(), memCapacity);
+      setGetPartialArraysWithOffsetTests(buf);
+    }
+  }
+
+  public static void setGetPartialArraysWithOffsetTests(WritableBuffer buf) {
+    int items= 4;
+
+    byte[] srcArray2 = { 1, -2, 3, -4 };
+    byte[] dstArray2 = new byte[items];
+    buf.resetPosition();
+    buf.putByteArray(srcArray2, 2, items/2);
+    buf.resetPosition();
+    buf.getByteArray(dstArray2, 2, items/2);
+    for (int i=2; i<items; i++) {
+      assertEquals(dstArray2[i], srcArray2[i]);
+    }
+
+    char[] srcArray3 = { 'A', 'B', 'C', 'D' };
+    char[] dstArray3 = new char[items];
+    buf.resetPosition();
+    buf.putCharArray(srcArray3, 2, items/2);
+    buf.resetPosition();
+    buf.getCharArray(dstArray3, 2, items/2);
+    for (int i=2; i<items; i++) {
+      assertEquals(dstArray3[i], srcArray3[i]);
+    }
+
+    double[] srcArray4 = { 1.0, -2.0, 3.0, -4.0 };
+    double[] dstArray4 = new double[items];
+    buf.resetPosition();
+    buf.putDoubleArray(srcArray4, 2, items/2);
+    buf.resetPosition();
+    buf.getDoubleArray(dstArray4, 2, items/2);
+    for (int i=2; i<items; i++) {
+      assertEquals(dstArray4[i], srcArray4[i], 0.0);
+    }
+
+    float[] srcArray5 = { 1.0F, -2.0F, 3.0F, -4.0F };
+    float[] dstArray5 = new float[items];
+    buf.resetPosition();
+    buf.putFloatArray(srcArray5, 2, items/2);
+    buf.resetPosition();
+    buf.getFloatArray(dstArray5, 2, items/2);
+    for (int i=2; i<items; i++) {
+      assertEquals(dstArray5[i], srcArray5[i], 0.0);
+    }
+
+    int[] srcArray6 = { 1, -2, 3, -4 };
+    int[] dstArray6 = new int[items];
+    buf.resetPosition();
+    buf.putIntArray(srcArray6, 2, items/2);
+    buf.resetPosition();
+    buf.getIntArray(dstArray6, 2, items/2);
+    for (int i=2; i<items; i++) {
+      assertEquals(dstArray6[i], srcArray6[i]);
+    }
+
+    long[] srcArray7 = { 1, -2, 3, -4 };
+    long[] dstArray7 = new long[items];
+    buf.resetPosition();
+    buf.putLongArray(srcArray7, 2, items/2);
+    buf.resetPosition();
+    buf.getLongArray(dstArray7, 2, items/2);
+    for (int i=2; i<items; i++) {
+      assertEquals(dstArray7[i], srcArray7[i]);
+    }
+
+    short[] srcArray8 = { 1, -2, 3, -4 };
+    short[] dstArray8 = new short[items];
+    buf.resetPosition();
+    buf.putShortArray(srcArray8, 2, items/2);
+    buf.resetPosition();
+    buf.getShortArray(dstArray8, 2, items/2);
+    for (int i=2; i<items; i++) {
+      assertEquals(dstArray8[i], srcArray8[i]);
+    }
+  }
+
+  @Test
+  public void checkSetClearMemoryRegions() throws Exception {
+    int memCapacity = 64; //must be 64
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      WritableMemory mem = WritableMemory.allocateDirect(memCapacity, scope, memReqSvr);
+      WritableBuffer buf = mem.asWritableBuffer();
+      assertEquals(buf.getCapacity(), memCapacity);
+
+      setClearMemoryRegionsTests(buf); //requires println enabled to visually check
+      buf.resetPosition();
+      for (int i = 0; i < memCapacity; i++) {
+        assertEquals(mem.getByte(i), 0);
+      }
+    }
+  }
+
+  //enable println statements to visually check
+  public static void setClearMemoryRegionsTests(WritableBuffer buf) {
+    int accessCapacity = (int)buf.getCapacity();
+
+  //define regions
+    int reg1Start = 0;
+    int reg1Len = 28;
+    int reg2Start = 28;
+    int reg2Len = 32;
+
+    //set region 1
+    byte b1 = 5;
+    buf.setStartPositionEnd(reg1Start, reg1Start, reg1Len);
+    buf.fill(b1);
+    buf.resetPosition();
+    for (int i=reg1Start; i<(reg1Len+reg1Start); i++) {
+      assertEquals(buf.getByte(), b1);
+    }
+    //println(buf.toHexString("Region1 to 5", reg1Start, reg1Len));
+
+    //set region 2
+    byte b2 = 7;
+    buf.setStartPositionEnd(reg2Start, reg2Start, reg2Start + reg2Len);
+    buf.fill(b2);
+    //println(mem.toHexString("Fill", 0, (int)mem.getCapacity()));
+    buf.resetPosition();
+    for (int i=reg2Start; i<(reg2Start+reg2Len); i++) {
+      assertEquals(buf.getByte(), b2);
+    }
+    //println(buf.toHexString("Region2 to 7", reg2Start, reg2Len));
+
+    //clear region 1
+    byte zeroByte = 0;
+    buf.setStartPositionEnd(reg1Start, reg1Start, reg2Len);
+    buf.resetPosition();
+    buf.clear();
+    buf.resetPosition();
+    for (int i=reg1Start; i<(reg1Start+reg1Len); i++) {
+      assertEquals(buf.getByte(), zeroByte);
+    }
+    //println(buf.toHexString("Region1 cleared", reg1Start, reg1Len));
+
+    //clear region 2
+    buf.setStartPositionEnd(reg2Start, reg2Start, reg2Start + reg2Len);
+    buf.resetPosition();
+    buf.clear();
+    buf.resetPosition();
+    for (int i=reg2Start; i<(reg2Len+reg2Start); i++) {
+      assertEquals(buf.getByte(), zeroByte);
+    }
+    //println(buf.toHexString("Region2 cleared", reg2Start, reg2Len));
+
+    //set all to ones
+    buf.setStartPositionEnd(reg1Start, reg1Start, accessCapacity);
+    byte b4 = 127;
+    buf.resetPosition();
+    buf.fill(b4);
+    buf.resetPosition();
+    for (int i=0; i<accessCapacity; i++) {
+      assertEquals(buf.getByte(), b4);
+    }
+    //println(buf.toHexString("Region1 + Region2 all ones", 0, accessCapacity));
+
+    //clear all
+    buf.resetPosition();
+    buf.clear();
+    buf.resetPosition();
+    for (int i=0; i<accessCapacity; i++) {
+      assertEquals(buf.getByte(), zeroByte);
+    }
+    //println(buf.toHexString("Region1 + Region2 cleared", 0, accessCapacity));
+  }
+
+  @Test
+  public void checkToHexStringAllMem() throws Exception {
+    int memCapacity = 48; //must be 48
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      WritableMemory mem = WritableMemory.allocateDirect(memCapacity, scope, memReqSvr);
+      WritableBuffer buf = mem.asWritableBuffer();
+      assertEquals(buf.getCapacity(), memCapacity);
+      toHexStringAllMemTests(buf); //requires println enabled to visually check
+    }
+  }
+
+  //enable println to visually check
+  public static void toHexStringAllMemTests(WritableBuffer buf) {
+    int memCapacity = (int)buf.getCapacity();
+
+    for (int i=0; i<memCapacity; i++) {
+      buf.putByte((byte)i);
+    }
+
+    //println(buf.toHexString("Check toHexString(0, 48) to integers", 0, memCapacity));
+    //println(buf.toHexString("Check toHexString(8, 40)", 8, 40));
+  }
+
+  @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/src/test/java17/org/apache/datasketches/memory/internal/CommonMemoryTest.java b/src/test/java17/org/apache/datasketches/memory/internal/CommonMemoryTest.java
new file mode 100644
index 0000000..9245a59
--- /dev/null
+++ b/src/test/java17/org/apache/datasketches/memory/internal/CommonMemoryTest.java
@@ -0,0 +1,375 @@
+/*
+ * 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.Util.isAllBitsClear;
+import static org.apache.datasketches.memory.internal.Util.isAllBitsSet;
+import static org.apache.datasketches.memory.internal.Util.isAnyBitsClear;
+import static org.apache.datasketches.memory.internal.Util.isAnyBitsSet;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import org.apache.datasketches.memory.BaseState;
+import org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableMemory;
+import org.testng.annotations.Test;
+
+import jdk.incubator.foreign.ResourceScope;
+
+public class CommonMemoryTest {
+  private final MemoryRequestServer memReqSvr = BaseState.defaultMemReqSvr;
+  @Test
+  public void checkSetGet() throws Exception {
+    int memCapacity = 16; //must be at least 8
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      WritableMemory mem = WritableMemory.allocateDirect(memCapacity, scope, memReqSvr);
+      assertEquals(mem.getCapacity(), memCapacity);
+      setGetTests(mem);
+    }
+  }
+
+  public static void setGetTests(WritableMemory mem) {
+    mem.putBoolean(0, true);
+    assertTrue(mem.getBoolean(0));
+    mem.putBoolean(0, false);
+    assertFalse(mem.getBoolean(0));
+
+    mem.putByte(0, (byte) -1);
+    assertEquals(mem.getByte(0), (byte) -1);
+    mem.putByte(0, (byte) 0);
+    assertEquals(mem.getByte(0), (byte) 0);
+
+    mem.putChar(0, 'A');
+    assertEquals(mem.getChar(0), 'A');
+    mem.putChar(0, 'Z');
+    assertEquals(mem.getChar(0), 'Z');
+
+    mem.putShort(0, Short.MAX_VALUE);
+    assertEquals(mem.getShort(0), Short.MAX_VALUE);
+    mem.putShort(0, Short.MIN_VALUE);
+    assertEquals(mem.getShort(0), Short.MIN_VALUE);
+
+    mem.putInt(0, Integer.MAX_VALUE);
+    assertEquals(mem.getInt(0), Integer.MAX_VALUE);
+    mem.putInt(0, Integer.MIN_VALUE);
+    assertEquals(mem.getInt(0), Integer.MIN_VALUE);
+
+    mem.putFloat(0, Float.MAX_VALUE);
+    assertEquals(mem.getFloat(0), Float.MAX_VALUE);
+    mem.putFloat(0, Float.MIN_VALUE);
+    assertEquals(mem.getFloat(0), Float.MIN_VALUE);
+
+    mem.putLong(0, Long.MAX_VALUE);
+    assertEquals(mem.getLong(0), Long.MAX_VALUE);
+    mem.putLong(0, Long.MIN_VALUE);
+    assertEquals(mem.getLong(0), Long.MIN_VALUE);
+
+    mem.putDouble(0, Double.MAX_VALUE);
+    assertEquals(mem.getDouble(0), Double.MAX_VALUE);
+    mem.putDouble(0, Double.MIN_VALUE);
+    assertEquals(mem.getDouble(0), Double.MIN_VALUE);
+  }
+
+  @Test
+  public void checkSetGetArrays() throws Exception {
+    int memCapacity = 32;
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      WritableMemory mem = WritableMemory.allocateDirect(memCapacity, scope, memReqSvr);
+      assertEquals(memCapacity, mem.getCapacity());
+      setGetArraysTests(mem);
+    }
+  }
+
+  public static void setGetArraysTests(WritableMemory mem) {
+    int words = 4;
+
+    byte[] srcArray2 = { 1, -2, 3, -4 };
+    byte[] dstArray2 = new byte[4];
+    mem.putByteArray(0, srcArray2, 0, words);
+    mem.getByteArray(0, dstArray2, 0, words);
+    for (int i = 0; i < words; i++) {
+      assertEquals(dstArray2[i], srcArray2[i]);
+    }
+
+    char[] srcArray3 = { 'A', 'B', 'C', 'D' };
+    char[] dstArray3 = new char[words];
+    mem.putCharArray(0, srcArray3, 0, words);
+    mem.getCharArray(0, dstArray3, 0, words);
+    for (int i = 0; i < words; i++) {
+      assertEquals(dstArray3[i], srcArray3[i]);
+    }
+
+    double[] srcArray4 = { 1.0, -2.0, 3.0, -4.0 };
+    double[] dstArray4 = new double[words];
+    mem.putDoubleArray(0, srcArray4, 0, words);
+    mem.getDoubleArray(0, dstArray4, 0, words);
+    for (int i = 0; i < words; i++) {
+      assertEquals(dstArray4[i], srcArray4[i], 0.0);
+    }
+
+    float[] srcArray5 = { (float)1.0, (float)-2.0, (float)3.0, (float)-4.0 };
+    float[] dstArray5 = new float[words];
+    mem.putFloatArray(0, srcArray5, 0, words);
+    mem.getFloatArray(0, dstArray5, 0, words);
+    for (int i = 0; i < words; i++) {
+      assertEquals(dstArray5[i], srcArray5[i], 0.0);
+    }
+
+    int[] srcArray6 = { 1, -2, 3, -4 };
+    int[] dstArray6 = new int[words];
+    mem.putIntArray(0, srcArray6, 0, words);
+    mem.getIntArray(0, dstArray6, 0, words);
+    for (int i = 0; i < words; i++) {
+      assertEquals(dstArray6[i], srcArray6[i]);
+    }
+
+    long[] srcArray7 = { 1, -2, 3, -4 };
+    long[] dstArray7 = new long[words];
+    mem.putLongArray(0, srcArray7, 0, words);
+    mem.getLongArray(0, dstArray7, 0, words);
+    for (int i = 0; i < words; i++) {
+      assertEquals(dstArray7[i], srcArray7[i]);
+    }
+
+    short[] srcArray8 = { 1, -2, 3, -4 };
+    short[] dstArray8 = new short[words];
+    mem.putShortArray(0, srcArray8, 0, words);
+    mem.getShortArray(0, dstArray8, 0, words);
+    for (int i = 0; i < words; i++) {
+      assertEquals(dstArray8[i], srcArray8[i]);
+    }
+  }
+
+  @Test
+  public void checkSetGetPartialArraysWithOffset() throws Exception {
+    int memCapacity = 32;
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      WritableMemory mem = WritableMemory.allocateDirect(memCapacity, scope, memReqSvr);
+      assertEquals(memCapacity, mem.getCapacity());
+      setGetPartialArraysWithOffsetTests(mem);
+    }
+  }
+
+  public static void setGetPartialArraysWithOffsetTests(WritableMemory mem) {
+    int items= 4;
+
+    byte[] srcArray2 = { 1, -2, 3, -4 };
+    byte[] dstArray2 = new byte[items];
+    mem.putByteArray(0, srcArray2, 2, items/2);
+    mem.getByteArray(0, dstArray2, 2, items/2);
+    for (int i = 2; i < items; i++) {
+      assertEquals(dstArray2[i], srcArray2[i]);
+    }
+
+    char[] srcArray3 = { 'A', 'B', 'C', 'D' };
+    char[] dstArray3 = new char[items];
+    mem.putCharArray(0, srcArray3, 2, items/2);
+    mem.getCharArray(0, dstArray3, 2, items/2);
+    for (int i = 2; i < items; i++) {
+      assertEquals(dstArray3[i], srcArray3[i]);
+    }
+
+    double[] srcArray4 = { 1.0, -2.0, 3.0, -4.0 };
+    double[] dstArray4 = new double[items];
+    mem.putDoubleArray(0, srcArray4, 2, items/2);
+    mem.getDoubleArray(0, dstArray4, 2, items/2);
+    for (int i = 2; i < items; i++) {
+      assertEquals(dstArray4[i], srcArray4[i], 0.0);
+    }
+
+    float[] srcArray5 = { (float)1.0, (float)-2.0, (float)3.0, (float)-4.0 };
+    float[] dstArray5 = new float[items];
+    mem.putFloatArray(0, srcArray5, 2, items/2);
+    mem.getFloatArray(0, dstArray5, 2, items/2);
+    for (int i = 2; i < items; i++) {
+      assertEquals(dstArray5[i], srcArray5[i], 0.0);
+    }
+
+    int[] srcArray6 = { 1, -2, 3, -4 };
+    int[] dstArray6 = new int[items];
+    mem.putIntArray(0, srcArray6, 2, items/2);
+    mem.getIntArray(0, dstArray6, 2, items/2);
+    for (int i = 2; i < items; i++) {
+      assertEquals(dstArray6[i], srcArray6[i]);
+    }
+
+    long[] srcArray7 = { 1, -2, 3, -4 };
+    long[] dstArray7 = new long[items];
+    mem.putLongArray(0, srcArray7, 2, items/2);
+    mem.getLongArray(0, dstArray7, 2, items/2);
+    for (int i = 2; i < items; i++) {
+      assertEquals(dstArray7[i], srcArray7[i]);
+    }
+
+    short[] srcArray8 = { 1, -2, 3, -4 };
+    short[] dstArray8 = new short[items];
+    mem.putShortArray(0, srcArray8, 2, items/2);
+    mem.getShortArray(0, dstArray8, 2, items/2);
+    for (int i = 2; i < items; i++) {
+      assertEquals(dstArray8[i], srcArray8[i]);
+    }
+  }
+
+  @Test
+  public void checkSetClearIsBits() throws Exception {
+    int memCapacity = 8;
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      WritableMemory mem = WritableMemory.allocateDirect(memCapacity, scope, memReqSvr);
+      assertEquals(memCapacity, mem.getCapacity());
+      mem.clear();
+      setClearIsBitsTests(mem);
+    }
+  }
+
+  public static void setClearIsBitsTests(WritableMemory mem) {
+  //single bits
+    for (int i = 0; i < 8; i++) {
+      long bitMask = (1 << i);
+      long v = mem.getByte(0) & 0XFFL;
+      assertTrue(isAnyBitsClear(v, bitMask));
+      mem.setBits(0, (byte) bitMask);
+      v = mem.getByte(0) & 0XFFL;
+      assertTrue(isAnyBitsSet(v, bitMask));
+      mem.clearBits(0, (byte) bitMask);
+      v = mem.getByte(0) & 0XFFL;
+      assertTrue(isAnyBitsClear(v, bitMask));
+    }
+
+    //multiple bits
+    for (int i = 0; i < 7; i++) {
+      long bitMask1 = (1 << i);
+      long bitMask2 = (3 << i);
+      long v = mem.getByte(0) & 0XFFL;
+      assertTrue(isAnyBitsClear(v, bitMask1));
+      assertTrue(isAnyBitsClear(v, bitMask2));
+      mem.setBits(0, (byte) bitMask1); //set one bit
+      v = mem.getByte(0) & 0XFFL;
+      assertTrue(isAnyBitsSet(v, bitMask2));
+      assertTrue(isAnyBitsClear(v, bitMask2));
+      assertFalse(isAllBitsSet(v, bitMask2));
+      assertFalse(isAllBitsClear(v, bitMask2));
+    }
+  }
+
+  @Test
+  public void checkSetClearMemoryRegions() throws Exception {
+    int memCapacity = 64; //must be 64
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      WritableMemory mem = WritableMemory.allocateDirect(memCapacity, scope, memReqSvr);
+
+      setClearMemoryRegionsTests(mem); //requires println enabled to visually check
+      for (int i = 0; i < memCapacity; i++) {
+        assertEquals(mem.getByte(i), 0);
+      }
+    }
+  }
+
+  //enable println stmts to visually check
+  public static void setClearMemoryRegionsTests(WritableMemory mem) {
+    int accessCapacity = (int)mem.getCapacity();
+
+  //define regions
+    int reg1Start = 0;
+    int reg1Len = 28;
+    int reg2Start = 28;
+    int reg2Len = 32;
+
+    //set region 1
+    byte b1 = 5;
+    mem.fill(reg1Start, reg1Len, b1);
+    for (int i = reg1Start; i < (reg1Len+reg1Start); i++) {
+      assertEquals(mem.getByte(i), b1);
+    }
+    //println(mem.toHexString("Region1 to 5", reg1Start, reg1Len));
+
+    //set region 2
+    byte b2 = 7;
+    mem.fill(reg2Start, reg2Len, b2);
+    //println(mem.toHexString("Fill", 0, (int)mem.getCapacity()));
+    for (int i = reg2Start; i < (reg2Len+reg2Start); i++) {
+      assertEquals(mem.getByte(i), b2);
+    }
+    //println(mem.toHexString("Region2 to 7", reg2Start, reg2Len));
+
+    //clear region 1
+    byte zeroByte = 0;
+    mem.clear(reg1Start, reg1Len);
+    for (int i = reg1Start; i < (reg1Len+reg1Start); i++) {
+      assertEquals(mem.getByte(i), zeroByte);
+    }
+    //println(mem.toHexString("Region1 cleared", reg1Start, reg1Len));
+
+    //clear region 2
+    mem.clear(reg2Start, reg2Len);
+    for (int i = reg2Start; i < (reg2Len+reg2Start); i++) {
+      assertEquals(mem.getByte(i), zeroByte);
+    }
+    //println(mem.toHexString("Region2 cleared", reg2Start, reg2Len));
+
+    //set all to ones
+    byte b4 = 127;
+    mem.fill(b4);
+    for (int i=0; i<accessCapacity; i++) {
+      assertEquals(mem.getByte(i), b4);
+    }
+    //println(mem.toHexString("Region1 + Region2 all ones", 0, accessCapacity));
+
+    //clear all
+    mem.clear();
+    for (int i = 0; i < accessCapacity; i++) {
+      assertEquals(mem.getByte(i), zeroByte);
+    }
+    //println(mem.toHexString("Region1 + Region2 cleared", 0, accessCapacity));
+  }
+
+  @Test
+  public void checkToHexStringAllMem() throws Exception {
+    int memCapacity = 48; //must be 48
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      WritableMemory mem = WritableMemory.allocateDirect(memCapacity, scope, memReqSvr);
+      toHexStringAllMemTests(mem); //requires println enabled to visually check
+    }
+  }
+
+  //enable println to visually check
+  public static void toHexStringAllMemTests(WritableMemory mem) {
+    int memCapacity = (int)mem.getCapacity();
+
+    for (int i = 0; i < memCapacity; i++) {
+      mem.putByte(i, (byte)i);
+    }
+
+    //println(mem.toHexString("Check toHexString(0, 48) to integers", 0, memCapacity));
+    //println(mem.toHexString("Check toHexString(8, 40)", 8, 40));
+  }
+
+  @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/src/test/java17/org/apache/datasketches/memory/internal/CopyMemoryOverlapTest.java b/src/test/java17/org/apache/datasketches/memory/internal/CopyMemoryOverlapTest.java
new file mode 100644
index 0000000..8a4bdc0
--- /dev/null
+++ b/src/test/java17/org/apache/datasketches/memory/internal/CopyMemoryOverlapTest.java
@@ -0,0 +1,187 @@
+/*
+ * 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 org.apache.datasketches.memory.BaseState;
+import org.apache.datasketches.memory.Memory;
+import org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableMemory;
+import org.testng.annotations.Test;
+
+import jdk.incubator.foreign.ResourceScope;
+
+/**
+ * @author Lee Rhodes
+ */
+public class CopyMemoryOverlapTest {
+  private static final MemoryRequestServer memReqSvr = BaseState.defaultMemReqSvr;
+
+  @Test
+  public void checkOverlapUsingMemory() throws Exception {
+    long copyLongs = 1 << 20;
+    double overlap = 0.5;
+    long start_mS = System.currentTimeMillis();
+
+    copyUsingDirectMemory(copyLongs, overlap, true);
+    long end1_mS = System.currentTimeMillis();
+
+    copyUsingDirectMemory(copyLongs, overlap, false);
+    long end2_mS = System.currentTimeMillis();
+
+    println("CopyUp Time Sec: " + ((end1_mS - start_mS)/1000.0));
+    println("CopyDn Time Sec: " + ((end2_mS - end1_mS)/1000.0));
+  }
+
+  @Test
+  public void checkOverlapUsingRegions() throws Exception {
+    long copyLongs = 1 << 20;
+    double overlap = 0.5;
+    long start_mS = System.currentTimeMillis();
+
+    copyUsingDirectRegions(copyLongs, overlap, true);
+    long end1_mS = System.currentTimeMillis();
+
+    copyUsingDirectRegions(copyLongs, overlap, false);
+    long end2_mS = System.currentTimeMillis();
+
+    println("CopyUp Time Sec: " + ((end1_mS - start_mS)/1000.0));
+    println("CopyDn Time Sec: " + ((end2_mS - end1_mS)/1000.0));
+  }
+
+  private static final void copyUsingDirectMemory(long copyLongs, double overlap, boolean copyUp) throws Exception {
+    println("Copy Using Direct Memory");
+    long overlapLongs = (long) (overlap * copyLongs);
+    long backingLongs = (2 * copyLongs) - overlapLongs;
+
+    long fromOffsetLongs;
+    long toOffsetLongs;
+    //long deltaLongs;
+
+    if (copyUp) {
+      fromOffsetLongs = 0;
+      toOffsetLongs = copyLongs - overlapLongs;
+      //deltaLongs = toOffsetLongs - fromOffsetLongs;
+    } else {
+      fromOffsetLongs = copyLongs - overlapLongs;
+      toOffsetLongs = 0;
+      //deltaLongs = toOffsetLongs - fromOffsetLongs;
+    }
+
+    long backingBytes = backingLongs << 3;
+    long copyBytes = copyLongs << 3;
+    long fromOffsetBytes = fromOffsetLongs << 3;
+    long toOffsetBytes = toOffsetLongs << 3;
+    //long deltaBytes = deltaLongs << 3;
+    println("Copy longs   : " + copyLongs    + "\t bytes: " + copyBytes);
+    println("Overlap      : " + (overlap * 100.0) + "%");
+    println("CopyUp       : " + copyUp);
+    println("Backing longs: " + backingLongs + "\t bytes: " + backingBytes);
+
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      WritableMemory backingMem = WritableMemory.allocateDirect(backingBytes, scope, memReqSvr);
+      fill(backingMem); //fill mem with 0 thru copyLongs -1
+      //listMem(backingMem, "Original");
+      backingMem.copyTo(fromOffsetBytes, backingMem, toOffsetBytes, copyBytes);
+      //listMem(backingMem, "After");
+      checkMemLongs(backingMem, fromOffsetLongs, toOffsetLongs, copyLongs);
+    }
+    println("");
+  }
+
+  private static final void copyUsingDirectRegions(long copyLongs, double overlap, boolean copyUp) throws Exception {
+    println("Copy Using Direct Memory");
+    long overlapLongs = (long) (overlap * copyLongs);
+    long backingLongs = (2 * copyLongs) - overlapLongs;
+
+    long fromOffsetLongs;
+    long toOffsetLongs;
+    //long deltaLongs;
+
+    if (copyUp) {
+      fromOffsetLongs = 0;
+      toOffsetLongs = copyLongs - overlapLongs;
+      //deltaLongs = toOffsetLongs - fromOffsetLongs;
+    } else {
+      fromOffsetLongs = copyLongs - overlapLongs;
+      toOffsetLongs = 0;
+      //deltaLongs = toOffsetLongs - fromOffsetLongs;
+    }
+
+    long backingBytes = backingLongs << 3;
+    long copyBytes = copyLongs << 3;
+    long fromOffsetBytes = fromOffsetLongs << 3;
+    long toOffsetBytes = toOffsetLongs << 3;
+    //long deltaBytes = deltaLongs << 3;
+    println("Copy longs   : " + copyLongs    + "\t bytes: " + copyBytes);
+    println("Overlap      : " + (overlap * 100.0) + "%");
+    println("CopyUp       : " + copyUp);
+    println("Backing longs: " + backingLongs + "\t bytes: " + backingBytes);
+
+    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+      WritableMemory backingMem = WritableMemory.allocateDirect(backingBytes, scope, memReqSvr);
+      fill(backingMem); //fill mem with 0 thru copyLongs -1
+      //listMem(backingMem, "Original");
+      WritableMemory reg1 = backingMem.writableRegion(fromOffsetBytes, copyBytes);
+      WritableMemory reg2 = backingMem.writableRegion(toOffsetBytes, copyBytes);
+
+      reg1.copyTo(0, reg2, 0, copyBytes);
+      //listMem(backingMem, "After");
+      checkMemLongs(reg2, fromOffsetLongs, 0, copyLongs);
+    }
+    println("");
+  }
+
+  private static final void fill(WritableMemory wmem) {
+    long longs = wmem.getCapacity() >>> 3;
+    for (long i = 0; i < longs; i++) { wmem.putLong(i << 3, i); } //fill with 0 .. (longs - 1)
+    //checkMemLongs(wmem, 0L, 0L, longs);
+  }
+
+  private static final void checkMemLongs(Memory mem, long fromOffsetLongs, long toOffsetLongs, long copyLongs) {
+    for (long i = 0; i < copyLongs; i++) {
+      long memVal = mem.getLong((toOffsetLongs + i) << 3);
+      assertEquals(memVal, fromOffsetLongs + i);
+    }
+  }
+
+  @SuppressWarnings("unused")
+  private static final void listMem(Memory mem, String comment) {
+    println(comment);
+    println("Idx\tValue");
+    long longs = mem.getCapacity() >>> 3;
+    for (long i = 0; i < longs; i++) {
+      println(i + "\t" + mem.getLong(i << 3));
+    }
+  }
+
+  @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/src/test/java17/org/apache/datasketches/memory/internal/CopyMemoryTest.java b/src/test/java17/org/apache/datasketches/memory/internal/CopyMemoryTest.java
new file mode 100644
index 0000000..f0af8c4
--- /dev/null
+++ b/src/test/java17/org/apache/datasketches/memory/internal/CopyMemoryTest.java
@@ -0,0 +1,182 @@
+/*
+ * 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 java.util.concurrent.ThreadLocalRandom;
+
+import org.apache.datasketches.memory.BaseState;
+import org.apache.datasketches.memory.Memory;
+import org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableMemory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import jdk.incubator.foreign.ResourceScope;
+
+public class CopyMemoryTest {
+  private static final MemoryRequestServer memReqSvr = BaseState.defaultMemReqSvr;
+
+  @Test
+  public void heapWSource() {
+    int k1 = 1 << 20; //longs
+    int k2 = 2 * k1;
+    WritableMemory srcMem = genMem(k1, false); //!empty
+    //println(srcMem.toHexString("src: ", 0, k1 << 3));
+    WritableMemory dstMem = genMem(k2, true);
+    srcMem.copyTo(0, dstMem, k1 << 3, k1 << 3);
+    //println(dstMem.toHexString("dst: ", 0, k2 << 3));
+    check(dstMem, k1, k1, 1);
+  }
+
+  @Test
+  public void heapROSource() {
+    int k1 = 1 << 20; //longs
+    int k2 = 2 * k1;
+    Memory srcMem = genMem(k1, false); //!empty
+    WritableMemory dstMem = genMem(k2, true);
+    srcMem.copyTo(0, dstMem, k1 << 3, k1 << 3);
+    check(dstMem, k1, k1, 1);
+  }
+
+  @Test
+  public void directWSource() throws Exception {
+    int k1 = 1 << 20; //longs
+    int k2 = 2 * k1;
+    WritableMemory srcMem = genWmem(k1, false);
+    WritableMemory dstMem = genMem(k2, true);
+    srcMem.copyTo(0, dstMem, k1 << 3, k1 << 3);
+    check(dstMem, k1, k1, 1);
+    srcMem.close();
+  }
+
+  @Test
+  public void directROSource() throws Exception {
+    int k1 = 1 << 20; //longs
+    int k2 = 2 * k1;
+    Memory srcMem = genWmem(k1, false);
+    WritableMemory dstMem = genMem(k2, true);
+    srcMem.copyTo(0, dstMem, k1 << 3, k1 << 3);
+    check(dstMem, k1, k1, 1);
+    srcMem.close();
+  }
+
+  @Test
+  public void heapWSrcRegion() {
+    int k1 = 1 << 20; //longs
+    //gen baseMem of k1 longs w data
+    WritableMemory baseMem = genMem(k1, false); //!empty
+    //gen src region of k1/2 longs, off= k1/2
+    WritableMemory srcReg = baseMem.writableRegion((k1/2) << 3, (k1/2) << 3);
+    WritableMemory dstMem = genMem(2 * k1, true); //empty
+    srcReg.copyTo(0, dstMem, k1 << 3, (k1/2) << 3);
+    //println(dstMem.toHexString("dstMem: ", k1 << 3, (k1/2) << 3));
+    check(dstMem, k1, k1/2, (k1/2) + 1);
+  }
+
+  @Test
+  public void heapROSrcRegion() {
+    int k1 = 1 << 20; //longs
+    //gen baseMem of k1 longs w data
+    WritableMemory baseMem = genMem(k1, false); //!empty
+    //gen src region of k1/2 longs, off= k1/2
+    Memory srcReg = baseMem.region((k1/2) << 3, (k1/2) << 3);
+    WritableMemory dstMem = genMem(2 * k1, true); //empty
+    srcReg.copyTo(0, dstMem, k1 << 3, (k1/2) << 3);
+    check(dstMem, k1, k1/2, (k1/2) + 1);
+  }
+
+  @Test
+  public void directROSrcRegion() throws Exception {
+    int k1 = 1 << 20; //longs
+    //gen baseMem of k1 longs w data, direct
+    Memory baseMem = genWmem(k1, false);
+    //gen src region of k1/2 longs, off= k1/2
+    Memory srcReg = baseMem.region((k1/2) << 3, (k1/2) << 3);
+
+    WritableMemory dstMem = genMem(2 * k1, true); //empty
+    srcReg.copyTo(0, dstMem, k1 << 3, (k1/2) << 3);
+    check(dstMem, k1, k1/2, (k1/2) + 1);
+    baseMem.close();
+  }
+
+  @Test
+  public void testOverlappingCopyLeftToRight() {
+    byte[] bytes = new byte[(((1 << 20) * 5) / 2) + 1];
+    ThreadLocalRandom.current().nextBytes(bytes);
+    byte[] referenceBytes = bytes.clone();
+    Memory referenceMem = Memory.wrap(referenceBytes);
+    WritableMemory mem = WritableMemory.writableWrap(bytes);
+    long copyLen = (1 << 20) * 2;
+    mem.copyTo(0, mem, (1 << 20) / 2, copyLen);
+    Assert.assertEquals(0, mem.compareTo((1 << 20) / 2, copyLen, referenceMem, 0, copyLen));
+  }
+
+  @Test
+  public void testOverlappingCopyRightToLeft() {
+    byte[] bytes = new byte[(((1 << 20) * 5) / 2) + 1];
+    ThreadLocalRandom.current().nextBytes(bytes);
+    byte[] referenceBytes = bytes.clone();
+    Memory referenceMem = Memory.wrap(referenceBytes);
+    WritableMemory mem = WritableMemory.writableWrap(bytes);
+    long copyLen = (1 << 20) * 2;
+    mem.copyTo((1 << 20) / 2, mem, 0, copyLen);
+    Assert.assertEquals(0, mem.compareTo(0, copyLen, referenceMem, (1 << 20) / 2, copyLen));
+  }
+
+  private static void check(Memory mem, int offsetLongs, int lengthLongs, int startValue) {
+    int offBytes = offsetLongs << 3;
+    for (long i = 0; i < lengthLongs; i++) {
+      assertEquals(mem.getLong(offBytes + (i << 3)), i + startValue);
+    }
+  }
+
+  @SuppressWarnings("resource")
+  private static WritableMemory genWmem(int longs, boolean empty) {
+    WritableMemory wmem = WritableMemory.allocateDirect(longs << 3, ResourceScope.newConfinedScope(), memReqSvr);
+    if (empty) {
+      wmem.clear();
+    } else {
+      for (int i = 0; i < longs; i++) { wmem.putLong(i << 3, i + 1); }
+    }
+    return wmem;
+  }
+
+  private static WritableMemory genMem(int longs, boolean empty) {
+    WritableMemory mem = WritableMemory.allocate(longs << 3);
+    if (!empty) {
+      for (int i = 0; i < longs; i++) { mem.putLong(i << 3, i + 1); }
+    }
+    return mem;
+  }
+
+  @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/src/test/java17/org/apache/datasketches/memory/internal/DruidIssue11544Test.java b/src/test/java17/org/apache/datasketches/memory/internal/DruidIssue11544Test.java
new file mode 100644
index 0000000..a9fce8a
--- /dev/null
+++ b/src/test/java17/org/apache/datasketches/memory/internal/DruidIssue11544Test.java
@@ -0,0 +1,101 @@
+/*
+ * 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 java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import org.apache.datasketches.memory.BaseState;
+import org.apache.datasketches.memory.MemoryRequestServer;
+import org.apache.datasketches.memory.WritableMemory;
+import org.testng.annotations.Test;
+
+/**
+ * The original design provided the MemoryRequestServer callback only for Memory segments allocated via
+ * <i>WritableMemory.allocateDirect(...)</i> calls.  Memory segments allocated via
+ * <i>WritableMemory.wrap(ByteBuffer)</i> did not have this capability.  This was a major oversight since
+ * all off-heap memory in Druid is allocated using ByteBuffers!  It is unusual that no one has
+ * uncovered this until August 2021.  Nonetheless, the fix involves instrumenting all the paths involved
+ * in providing this callback mechanism for wrapped ByteBuffers.
+ *
... 5841 lines suppressed ...


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datasketches.apache.org
For additional commands, e-mail: commits-help@datasketches.apache.org