blob: 97580d1a57bdf13ae58f5d48c19ab8b4cba4f736 [file] [log] [blame]
/*
* 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 jdk.incubator.foreign.MemoryAddress;
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
*/
@SuppressWarnings("restriction")
public abstract class BaseStateImpl implements BaseState {
public static final String JDK; //must be at least "1.8"
public static final int JDK_MAJOR; //8, 11, 12, etc
public static final int BOOLEAN_SHIFT = 0;
public static final int BYTE_SHIFT = 0;
public static final long SHORT_SHIFT = 1;
public static final long CHAR_SHIFT = 1;
public static final long INT_SHIFT = 2;
public static final long LONG_SHIFT = 3;
public static final long FLOAT_SHIFT = 2;
public static final long DOUBLE_SHIFT = 3;
//class type IDs.
// 0000 0XXX
public static final int READONLY = 1;
public static final int REGION = 1 << 1;
public static final int DUPLICATE = 1 << 2; //for Buffer only
// 000X X000
public static final int HEAP = 0;
public static final int DIRECT = 1 << 3;
public static final int MAP = 1 << 4; //Map is always Direct also
// 00X0 0000
public static final int NATIVE = 0;
public static final int NONNATIVE = 1 << 5;
// 0X00 0000
public static final int MEMORY = 0;
public static final int BUFFER = 1 << 6;
// X000 0000
public static final int BYTEBUF = 1 << 7;
final MemorySegment seg;
final int typeId;
MemoryRequestServer memReqSvr;
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];
}
public BaseStateImpl(final MemorySegment seg, final int typeId) {
this.seg = seg;
this.typeId = typeId;
}
@Override
public void force() { seg.force(); }
@Override
public void load() { seg.load(); }
@Override
public void unload() { seg.unload(); }
@Override
public boolean isLoaded() { return seg.isLoaded(); }
@Override
public ResourceScope scope() { return seg.scope(); }
@SuppressWarnings("resource")
@Override
public boolean isAlive() { return seg.scope().isAlive(); }
/**
* 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
*/
public static void checkBounds(final long reqOff, final long reqLen, final long allocSize) {
if ((reqOff | reqLen | (reqOff + reqLen) | (allocSize - (reqOff + reqLen))) < 0) {
throw new IllegalArgumentException(
"reqOffset: " + reqOff + ", reqLength: " + reqLen
+ ", (reqOff + reqLen): " + (reqOff + reqLen) + ", allocSize: " + allocSize);
}
}
@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 ByteOrder getTypeByteOrder() {
return isNonNativeType() ? NON_NATIVE_BYTE_ORDER : ByteOrder.nativeOrder();
}
@Override
public final boolean isByteOrderCompatible(final ByteOrder byteOrder) {
final ByteOrder typeBO = getTypeByteOrder();
return typeBO == ByteOrder.nativeOrder() && typeBO == byteOrder;
}
@Override
public final boolean equalTo(final long thisOffsetBytes, final Object that,
final long thatOffsetBytes, final long lengthBytes) {
return that instanceof BaseStateImpl
? CompareAndCopy.equals(seg, thisOffsetBytes, ((BaseStateImpl) that).seg, thatOffsetBytes, lengthBytes)
: false;
}
@Override
public final ByteBuffer asByteBufferView(final ByteOrder order) {
final ByteBuffer byteBuf = seg.asByteBuffer().order(order);
return byteBuf;
}
@Override
public ByteBuffer toByteBuffer(final ByteOrder order) {
Objects.requireNonNull(order, "The input ByteOrder must not be null");
return ByteBuffer.wrap(seg.toByteArray());
}
@Override
public MemorySegment toMemorySegment() {
final MemorySegment arrSeg = MemorySegment.ofArray(new byte[(int)seg.byteSize()]);
arrSeg.copyFrom(seg);
return arrSeg;
}
@Override
public final long getCapacity() {
return seg.byteSize();
}
@Override
public MemoryRequestServer getMemoryRequestServer() {
return memReqSvr;
}
@Override
public boolean hasMemoryRequestServer() {
return memReqSvr != null;
}
@Override
public void setMemoryRequestServer(final MemoryRequestServer memReqSvr) {
this.memReqSvr = memReqSvr;
}
int getTypeId() {
return typeId;
}
@Override
public final long xxHash64(final long offsetBytes, final long lengthBytes, final long seed) {
return XxHash64.hash(seg, offsetBytes, lengthBytes, seed);
}
@Override
public final long xxHash64(final long in, final long seed) {
return XxHash64.hash(in, seed);
}
@Override
public final boolean hasByteBuffer() {
return isByteBufferType();
}
@Override
public final boolean isDirect() {
return seg.isNative();
}
@Override
public final boolean isSameResource(final Object that) {
if (this == that) { return true; }
final MemoryAddress myAdd = seg.address();
if (that instanceof BaseStateImpl) {
final MemoryAddress thatAdd = ((BaseStateImpl)that).seg.address();
return (myAdd.equals(thatAdd));
}
return false;
}
//TYPE ID Management
@Override
public final boolean isReadOnly() {
return ((getTypeId() & READONLY) > 0) || seg.isReadOnly();
}
// final static int setReadOnlyType(final int type, final boolean readOnly) { //not used
// return (type & ~READONLY) | (readOnly ? READONLY : 0);
// }
final boolean isRegionType() {
return (getTypeId() & REGION) > 0;
}
final boolean isDuplicateType() {
return (getTypeId() & DUPLICATE) > 0;
}
final boolean isMemoryType() {
return (getTypeId() & BUFFER) == 0;
}
final boolean isBufferType() {
return (getTypeId() & BUFFER) > 0;
}
final boolean isNativeType() {
return (getTypeId() & NONNATIVE) == 0;
}
final boolean isNonNativeType() {
return (getTypeId() & NONNATIVE) > 0;
}
final boolean isHeapType() { //test only
return (getTypeId() >>> 3 & 3) == 0;
}
final boolean isDirectType() { //test only
return (getTypeId() & DIRECT) > 0;
}
final boolean isMapType() { //test only
return (getTypeId() & MAP) > 0;
}
final boolean isByteBufferType() {
return (getTypeId() & BYTEBUF) > 0;
}
//TO STRING
/**
* Decodes the resource type. This is primarily for debugging.
* @param typeId the given typeId
* @return a human readable string.
*/
public static final String typeDecode(final int typeId) {
final StringBuilder sb = new StringBuilder();
final int group1 = typeId & 0x7;
switch (group1) {
case 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();
}
@Override
public final String toHexString(final String comment, final long offsetBytes, final int lengthBytes,
final boolean withData) {
return toHex(this, comment, offsetBytes, lengthBytes, withData);
}
/**
* 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.getTypeByteOrder().toString()).append(LS);
sb.append("Native Byte Order : ").append(ByteOrder.nativeOrder().toString()).append(LS);
sb.append("JDK Runtime Version : ").append(JDK).append(LS);
//Data detail
if (withData) {
sb.append("Data, 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();
}
/**
* 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.
*/
public 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};
}
public static void checkJavaVersion(final String jdkVer, final int p0) {
if ( p0 != 17 ) {
throw new IllegalArgumentException(
"Unsupported JDK Major Version, must be 17; " + jdkVer);
}
}
}