blob: 1de0b2ff19a2e10c5c13cd84905ae07bd0fdf5e8 [file]
/*
* 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.kll;
import static java.lang.foreign.ValueLayout.JAVA_BYTE;
import static java.lang.foreign.ValueLayout.JAVA_INT_UNALIGNED;
import static java.lang.foreign.ValueLayout.JAVA_LONG_UNALIGNED;
import static org.apache.datasketches.common.ByteArrayUtil.copyBytes;
import static org.apache.datasketches.kll.KllPreambleUtil.DATA_START_ADR;
import static org.apache.datasketches.kll.KllPreambleUtil.DATA_START_ADR_SINGLE_ITEM;
import static org.apache.datasketches.kll.KllPreambleUtil.getMemorySegmentK;
import static org.apache.datasketches.kll.KllPreambleUtil.getMemorySegmentLevelZeroSortedFlag;
import static org.apache.datasketches.kll.KllPreambleUtil.getMemorySegmentM;
import static org.apache.datasketches.kll.KllPreambleUtil.getMemorySegmentMinK;
import static org.apache.datasketches.kll.KllPreambleUtil.getMemorySegmentN;
import static org.apache.datasketches.kll.KllPreambleUtil.getMemorySegmentNumLevels;
import static org.apache.datasketches.kll.KllPreambleUtil.setMemorySegmentFamilyID;
import static org.apache.datasketches.kll.KllPreambleUtil.setMemorySegmentK;
import static org.apache.datasketches.kll.KllPreambleUtil.setMemorySegmentLevelZeroSortedFlag;
import static org.apache.datasketches.kll.KllPreambleUtil.setMemorySegmentM;
import static org.apache.datasketches.kll.KllPreambleUtil.setMemorySegmentMinK;
import static org.apache.datasketches.kll.KllPreambleUtil.setMemorySegmentN;
import static org.apache.datasketches.kll.KllPreambleUtil.setMemorySegmentNumLevels;
import static org.apache.datasketches.kll.KllPreambleUtil.setMemorySegmentPreInts;
import static org.apache.datasketches.kll.KllPreambleUtil.setMemorySegmentSerVer;
import static org.apache.datasketches.kll.KllSketch.SketchStructure.COMPACT_EMPTY;
import static org.apache.datasketches.kll.KllSketch.SketchStructure.COMPACT_FULL;
import static org.apache.datasketches.kll.KllSketch.SketchStructure.COMPACT_SINGLE;
import static org.apache.datasketches.kll.KllSketch.SketchStructure.UPDATABLE;
import static org.apache.datasketches.kll.KllSketch.SketchType.KLL_LONGS_SKETCH;
import java.lang.foreign.MemorySegment;
import org.apache.datasketches.common.ByteArrayUtil;
import org.apache.datasketches.common.Family;
import org.apache.datasketches.common.MemorySegmentRequest;
import org.apache.datasketches.common.MemorySegmentStatus;
import org.apache.datasketches.common.SketchesArgumentException;
/**
* This class implements an off-heap, updatable KllLongsSketch using MemorySegment.
*
* <p>Please refer to the documentation in the package-info:<br>
* {@link org.apache.datasketches.kll}</p>
*
* @author Lee Rhodes, Kevin Lang
*/
class KllDirectLongsSketch extends KllLongsSketch {
private MemorySegment wseg;
private final MemorySegmentRequest mSegReq;
/**
* Constructs from MemorySegment already initialized with a sketch image and validated.
* @param wseg the current MemorySegment
* @param segVal the MemoryValadate object
* @param mSegmentRequest the MemorySegmentRequest object.
*/
KllDirectLongsSketch(
final MemorySegment wseg,
final KllMemorySegmentValidate segVal,
final MemorySegmentRequest mSegmentRequest) {
super(segVal);
this.wseg = wseg;
mSegReq = mSegmentRequest;
}
/**
* Create a new updatable, direct instance of this sketch backed by a MemorySegment.
* @param k parameter that controls size of the sketch and accuracy of estimates
* @param m parameter that controls the minimum level width in items.
* @param dstSeg the given destination MemorySegment object for use by the sketch
* @param mSegReq the callback for the sketch to request a larger MemorySegment.
* @return a new instance of this sketch
*/
static KllDirectLongsSketch newDirectUpdatableInstance(
final int k,
final int m,
final MemorySegment dstSeg,
final MemorySegmentRequest mSegmentRequest) {
setMemorySegmentPreInts(dstSeg, UPDATABLE.getPreInts());
setMemorySegmentSerVer(dstSeg, UPDATABLE.getSerVer());
setMemorySegmentFamilyID(dstSeg, Family.KLL.getID());
setMemorySegmentK(dstSeg, k);
setMemorySegmentM(dstSeg, m);
setMemorySegmentN(dstSeg, 0);
setMemorySegmentMinK(dstSeg, k);
setMemorySegmentNumLevels(dstSeg, 1);
int offset = DATA_START_ADR;
//new Levels array
MemorySegment.copy(new int[] {k, k}, 0, dstSeg, JAVA_INT_UNALIGNED, offset, 2);
offset += 2 * Integer.BYTES;
//new min/max array
MemorySegment.copy(new long[] {Long.MAX_VALUE, Long.MIN_VALUE}, 0, dstSeg, JAVA_LONG_UNALIGNED, offset, 2);
offset += 2 * ITEM_BYTES;
//new empty items array
MemorySegment.copy(new long[k], 0, dstSeg, JAVA_LONG_UNALIGNED, offset, k);
final KllMemorySegmentValidate segVal = new KllMemorySegmentValidate(dstSeg, KLL_LONGS_SKETCH, null);
final MemorySegment wSeg = dstSeg;
return new KllDirectLongsSketch(wSeg, segVal, mSegmentRequest);
}
//End of Constructors
@Override
String getItemAsString(final int index) {
if (isEmpty()) { return "Null"; }
return Long.toString(getLongItemsArray()[index]);
}
@Override
public int getK() {
return getMemorySegmentK(wseg);
}
//MinMax Methods
@Override
public long getMaxItem() {
if ((sketchStructure == COMPACT_EMPTY) || isEmpty()) { throw new SketchesArgumentException(EMPTY_MSG); }
if (sketchStructure == COMPACT_SINGLE) { return getLongSingleItem(); }
//either compact-full or updatable
final int offset = DATA_START_ADR + getLevelsArrSizeBytes(sketchStructure) + ITEM_BYTES;
return wseg.get(JAVA_LONG_UNALIGNED, offset);
}
@Override
long getMaxItemInternal() {
if ((sketchStructure == COMPACT_EMPTY) || isEmpty()) { return Long.MAX_VALUE; }
if (sketchStructure == COMPACT_SINGLE) { return getLongSingleItem(); }
//either compact-full or updatable
final int offset = DATA_START_ADR + getLevelsArrSizeBytes(sketchStructure) + ITEM_BYTES;
return wseg.get(JAVA_LONG_UNALIGNED, offset);
}
@Override
String getMaxItemAsString() {
final long maxItem = getMaxItemInternal();
return Long.toString(maxItem);
}
@Override
public long getMinItem() {
if ((sketchStructure == COMPACT_EMPTY) || isEmpty()) { throw new SketchesArgumentException(EMPTY_MSG); }
if (sketchStructure == COMPACT_SINGLE) { return getLongSingleItem(); }
//either compact-full or updatable
final int offset = DATA_START_ADR + getLevelsArrSizeBytes(sketchStructure);
return wseg.get(JAVA_LONG_UNALIGNED, offset);
}
@Override
long getMinItemInternal() {
if ((sketchStructure == COMPACT_EMPTY) || isEmpty()) { return Long.MAX_VALUE; }
if (sketchStructure == COMPACT_SINGLE) { return getLongSingleItem(); }
//either compact-full or updatable
final int offset = DATA_START_ADR + getLevelsArrSizeBytes(sketchStructure);
return wseg.get(JAVA_LONG_UNALIGNED, offset);
}
@Override
String getMinItemAsString() {
final long minItem = getMinItemInternal();
return Long.toString(minItem);
}
@Override
void setMaxItem(final long item) {
if (readOnly) { throw new SketchesArgumentException(TGT_IS_READ_ONLY_MSG); }
final int offset = DATA_START_ADR + getLevelsArrSizeBytes(sketchStructure) + ITEM_BYTES;
wseg.set(JAVA_LONG_UNALIGNED, offset, item);
}
@Override
void setMinItem(final long item) {
if (readOnly) { throw new SketchesArgumentException(TGT_IS_READ_ONLY_MSG); }
final int offset = DATA_START_ADR + getLevelsArrSizeBytes(sketchStructure);
wseg.set(JAVA_LONG_UNALIGNED, offset, item);
}
//END MinMax Methods
@Override
public long getN() {
if (sketchStructure == COMPACT_EMPTY) { return 0; }
else if (sketchStructure == COMPACT_SINGLE) { return 1; }
else { return getMemorySegmentN(wseg); }
}
//other restricted
@Override //returns updatable, expanded array including free space at bottom
long[] getLongItemsArray() {
final int k = getK();
if (sketchStructure == COMPACT_EMPTY) { return new long[k]; }
if (sketchStructure == COMPACT_SINGLE) {
final long[] itemsArr = new long[k];
itemsArr[k - 1] = getLongSingleItem();
return itemsArr;
}
final int capacityItems = KllHelper.computeTotalItemCapacity(k, getM(), getNumLevels());
final long[] longItemsArr = new long[capacityItems];
final int offset = DATA_START_ADR + getLevelsArrSizeBytes(sketchStructure) + (2 * ITEM_BYTES);
final int shift = (sketchStructure == COMPACT_FULL) ? levelsArr[0] : 0;
final int numItems = (sketchStructure == COMPACT_FULL) ? getNumRetained() : capacityItems;
MemorySegment.copy(wseg, JAVA_LONG_UNALIGNED, offset, longItemsArr, shift, numItems);
return longItemsArr;
}
@Override //returns compact items array of retained items, no free space.
long[] getLongRetainedItemsArray() {
if (sketchStructure == COMPACT_EMPTY) { return new long[0]; }
if (sketchStructure == COMPACT_SINGLE) { return new long[] { getLongSingleItem() }; }
final int numRetained = getNumRetained();
final long[] longItemsArr = new long[numRetained];
final int offset = DATA_START_ADR + getLevelsArrSizeBytes(sketchStructure) + (2 * ITEM_BYTES)
+ (sketchStructure == COMPACT_FULL ? 0 : levelsArr[0] * ITEM_BYTES);
MemorySegment.copy(wseg, JAVA_LONG_UNALIGNED, offset, longItemsArr, 0, numRetained);
return longItemsArr;
}
@Override
long getLongSingleItem() {
if (!isSingleItem()) { throw new SketchesArgumentException(NOT_SINGLE_ITEM_MSG); }
if (sketchStructure == COMPACT_SINGLE) {
return wseg.get(JAVA_LONG_UNALIGNED, DATA_START_ADR_SINGLE_ITEM);
}
final int offset;
if (sketchStructure == COMPACT_FULL) {
offset = DATA_START_ADR + getLevelsArrSizeBytes(sketchStructure) + (2 * ITEM_BYTES);
} else { //sketchStructure == UPDATABLE
offset = DATA_START_ADR + getLevelsArrSizeBytes(sketchStructure) + (((2 + getK()) - 1) * ITEM_BYTES);
}
return wseg.get(JAVA_LONG_UNALIGNED, offset);
}
@Override
int getM() {
return getMemorySegmentM(wseg);
}
@Override
MemorySegmentRequest getMemorySegmentRequest() {
return mSegReq;
}
@Override
int getMinK() {
if ((sketchStructure == COMPACT_FULL) || (sketchStructure == UPDATABLE)) { return getMemorySegmentMinK(wseg); }
return getK();
}
@Override
byte[] getMinMaxByteArr() {
final byte[] bytesOut = new byte[2 * ITEM_BYTES];
if (sketchStructure == COMPACT_EMPTY) {
ByteArrayUtil.putLongLE(bytesOut, 0, Long.MAX_VALUE);
ByteArrayUtil.putLongLE(bytesOut, ITEM_BYTES, Long.MIN_VALUE);
return bytesOut;
}
final int offset;
if (sketchStructure == COMPACT_SINGLE) {
offset = DATA_START_ADR_SINGLE_ITEM;
MemorySegment.copy(wseg, JAVA_BYTE, offset, bytesOut, 0, ITEM_BYTES);
copyBytes(bytesOut, 0, bytesOut, ITEM_BYTES, ITEM_BYTES);
return bytesOut;
}
//sketchStructure == UPDATABLE OR COMPACT_FULL
offset = DATA_START_ADR + getLevelsArrSizeBytes(sketchStructure);
MemorySegment.copy(wseg, JAVA_BYTE, offset, bytesOut, 0, ITEM_BYTES);
MemorySegment.copy(wseg, JAVA_BYTE, offset + ITEM_BYTES, bytesOut, ITEM_BYTES, ITEM_BYTES);
return bytesOut;
}
@Override
byte[] getRetainedItemsByteArr() {
if (sketchStructure == COMPACT_EMPTY) { return new byte[0]; }
final long[] lngArr = getLongRetainedItemsArray();
final byte[] lngByteArr = new byte[lngArr.length * ITEM_BYTES];
final MemorySegment wseg2 = MemorySegment.ofArray(lngByteArr);
MemorySegment.copy(lngArr, 0, wseg2, JAVA_LONG_UNALIGNED, 0, lngArr.length);
return lngByteArr;
}
@Override
byte[] getTotalItemsByteArr() {
final long[] lngArr = getLongItemsArray();
final byte[] lngByteArr = new byte[lngArr.length * ITEM_BYTES];
final MemorySegment wseg2 = MemorySegment.ofArray(lngByteArr);
MemorySegment.copy(lngArr, 0, wseg2, JAVA_LONG_UNALIGNED, 0, lngArr.length);
return lngByteArr;
}
@Override
MemorySegment getMemorySegment() {
return wseg;
}
@Override
void incN(final int increment) {
if (readOnly) { throw new SketchesArgumentException(TGT_IS_READ_ONLY_MSG); }
setMemorySegmentN(wseg, getMemorySegmentN(wseg) + increment);
}
@Override
void incNumLevels() {
if (readOnly) { throw new SketchesArgumentException(TGT_IS_READ_ONLY_MSG); }
int numLevels = getMemorySegmentNumLevels(wseg);
setMemorySegmentNumLevels(wseg, ++numLevels);
}
@Override
boolean isLevelZeroSorted() {
return getMemorySegmentLevelZeroSortedFlag(wseg);
}
@Override
void setLongItemsArray(final long[] longItems) {
if (readOnly) { throw new SketchesArgumentException(TGT_IS_READ_ONLY_MSG); }
final int offset = DATA_START_ADR + getLevelsArrSizeBytes(sketchStructure) + (2 * ITEM_BYTES);
MemorySegment.copy(longItems, 0, wseg, JAVA_LONG_UNALIGNED, offset, longItems.length);
}
@Override
void setLongItemsArrayAt(final int index, final long item) {
if (readOnly) { throw new SketchesArgumentException(TGT_IS_READ_ONLY_MSG); }
final int offset =
DATA_START_ADR + getLevelsArrSizeBytes(sketchStructure) + ((index + 2) * ITEM_BYTES);
wseg.set(JAVA_LONG_UNALIGNED, offset, item);
}
@Override
void setLongItemsArrayAt(final int index, final long[] items, final int srcOffset, final int length) {
if (readOnly) { throw new SketchesArgumentException(TGT_IS_READ_ONLY_MSG); }
final int offset = DATA_START_ADR + getLevelsArrSizeBytes(sketchStructure) + ((index + 2) * ITEM_BYTES);
MemorySegment.copy(items, srcOffset, wseg, JAVA_LONG_UNALIGNED, offset, length);
}
@Override
void setLevelZeroSorted(final boolean sorted) {
if (readOnly) { throw new SketchesArgumentException(TGT_IS_READ_ONLY_MSG); }
setMemorySegmentLevelZeroSortedFlag(wseg, sorted);
}
@Override
void setMinK(final int minK) {
if (readOnly) { throw new SketchesArgumentException(TGT_IS_READ_ONLY_MSG); }
setMemorySegmentMinK(wseg, minK);
}
@Override
void setN(final long n) {
if (readOnly) { throw new SketchesArgumentException(TGT_IS_READ_ONLY_MSG); }
setMemorySegmentN(wseg, n);
}
@Override
void setNumLevels(final int numLevels) {
if (readOnly) { throw new SketchesArgumentException(TGT_IS_READ_ONLY_MSG); }
setMemorySegmentNumLevels(wseg, numLevels);
}
@Override
public boolean hasMemorySegment() {
return (wseg != null) && wseg.scope().isAlive();
}
@Override
public boolean isOffHeap() {
return hasMemorySegment() && wseg.isNative();
}
@Override
public boolean isSameResource(final MemorySegment that) {
return MemorySegmentStatus.isSameResource(wseg, that);
}
@Override
void setMemorySegment(final MemorySegment wseg) {
this.wseg = wseg;
}
}