blob: 3dfdf4fa1df4b6248115bb03a274732df75dd1a7 [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.theta;
import static java.lang.Math.min;
import static java.lang.foreign.ValueLayout.JAVA_BYTE;
import static java.lang.foreign.ValueLayout.JAVA_FLOAT_UNALIGNED;
import static java.lang.foreign.ValueLayout.JAVA_INT_UNALIGNED;
import static java.lang.foreign.ValueLayout.JAVA_LONG_UNALIGNED;
import static java.lang.foreign.ValueLayout.JAVA_SHORT_UNALIGNED;
import static org.apache.datasketches.common.Util.clearBits;
import static org.apache.datasketches.common.Util.floorPowerOf2;
import static org.apache.datasketches.common.Util.setBits;
import static org.apache.datasketches.theta.PreambleUtil.EMPTY_FLAG_MASK;
import static org.apache.datasketches.theta.PreambleUtil.FAMILY_BYTE;
import static org.apache.datasketches.theta.PreambleUtil.FLAGS_BYTE;
import static org.apache.datasketches.theta.PreambleUtil.LG_ARR_LONGS_BYTE;
import static org.apache.datasketches.theta.PreambleUtil.LG_NOM_LONGS_BYTE;
import static org.apache.datasketches.theta.PreambleUtil.PREAMBLE_LONGS_BYTE;
import static org.apache.datasketches.theta.PreambleUtil.P_FLOAT;
import static org.apache.datasketches.theta.PreambleUtil.RETAINED_ENTRIES_INT;
import static org.apache.datasketches.theta.PreambleUtil.SEED_HASH_SHORT;
import static org.apache.datasketches.theta.PreambleUtil.SER_VER;
import static org.apache.datasketches.theta.PreambleUtil.SER_VER_BYTE;
import static org.apache.datasketches.theta.PreambleUtil.THETA_LONG;
import static org.apache.datasketches.theta.PreambleUtil.clearEmpty;
import static org.apache.datasketches.theta.PreambleUtil.extractCurCount;
import static org.apache.datasketches.theta.PreambleUtil.extractFamilyID;
import static org.apache.datasketches.theta.PreambleUtil.extractFlags;
import static org.apache.datasketches.theta.PreambleUtil.extractLgArrLongs;
import static org.apache.datasketches.theta.PreambleUtil.extractSerVer;
import static org.apache.datasketches.theta.PreambleUtil.extractThetaLong;
import static org.apache.datasketches.theta.PreambleUtil.insertCurCount;
import static org.apache.datasketches.theta.PreambleUtil.insertFamilyID;
import static org.apache.datasketches.theta.PreambleUtil.insertLgArrLongs;
import static org.apache.datasketches.theta.PreambleUtil.insertP;
import static org.apache.datasketches.theta.PreambleUtil.insertPreLongs;
import static org.apache.datasketches.theta.PreambleUtil.insertSerVer;
import static org.apache.datasketches.theta.PreambleUtil.insertThetaLong;
import static org.apache.datasketches.theta.PreambleUtil.setEmpty;
import static org.apache.datasketches.thetacommon.HashOperations.continueCondition;
import static org.apache.datasketches.thetacommon.HashOperations.hashInsertOnly;
import static org.apache.datasketches.thetacommon.HashOperations.hashInsertOnlyMemorySegment;
import static org.apache.datasketches.thetacommon.HashOperations.hashSearch;
import static org.apache.datasketches.thetacommon.HashOperations.minLgHashTableSize;
import java.lang.foreign.MemorySegment;
import java.util.Arrays;
import org.apache.datasketches.common.Family;
import org.apache.datasketches.common.MemorySegmentStatus;
import org.apache.datasketches.common.SketchesArgumentException;
import org.apache.datasketches.common.SketchesReadOnlyException;
import org.apache.datasketches.common.SketchesStateException;
import org.apache.datasketches.common.Util;
import org.apache.datasketches.thetacommon.ThetaUtil;
/**
* The intersection operation for ThetaSketches.
*
* <p>This implementation uses data either on-heap or off-heap in a given MemorySegment
* that is owned and managed by the caller.
* The off-heap MemorySegment, which if managed properly, will greatly reduce the need for
* the JVM to perform garbage collection.</p>
*
* @author Lee Rhodes
* @author Kevin Lang
*/
final class ThetaIntersectionImpl extends ThetaIntersection {
private final short seedHash_;
private final boolean readOnly_; //True if this sketch is to be treated as read only
private final MemorySegment wseg_;
private final int maxLgArrLongs_; //only used with MemorySegment, not serialized
//Note: intersection operation does not use lgNomLongs or k, per se.
private int lgArrLongs_; //current size of hash table
private int curCount_; //curCount of HT, if < 0 means Universal Set (US) is true
private long thetaLong_;
private boolean empty_; //A virgin intersection represents the Universal Set, so empty is FALSE!
private long[] hashTable_; //retained entries of the intersection, on-heap only.
/**
* Constructor: Sets the class finals and computes, sets and checks the seedHash.
* @param wseg Can be either a Source(e.g. wrap) or Destination (new offHeap) MemorySegment.
* @param seed Used to validate incoming sketch arguments.
* @param dstSegFlag The given MemorySegment is a Destination (new offHeap) MemorySegment.
* @param readOnly True if MemorySegment is to be treated as read only.
*/
private ThetaIntersectionImpl(final MemorySegment wseg, final long seed, final boolean dstSegFlag,
final boolean readOnly) {
readOnly_ = readOnly;
if (wseg != null) {
wseg_ = wseg;
if (dstSegFlag) { //DstSeg: compute & store seedHash, no seedHash checking
ThetaIntersectionImpl.checkMinSizeMemorySegment(wseg);
maxLgArrLongs_ = !readOnly ? ThetaIntersectionImpl.getMaxLgArrLongs(wseg) : 0; //Only Off Heap
seedHash_ = Util.computeSeedHash(seed);
wseg_.set(JAVA_SHORT_UNALIGNED, SEED_HASH_SHORT, seedHash_);
} else { //SrcSeg:gets and stores the seedHash, checks seg_seedHash against the seed
seedHash_ = wseg_.get(JAVA_SHORT_UNALIGNED, SEED_HASH_SHORT);
Util.checkSeedHashes(seedHash_, Util.computeSeedHash(seed)); //check for seed hash conflict
maxLgArrLongs_ = 0;
}
} else { //compute & store seedHash
wseg_ = null;
maxLgArrLongs_ = 0;
seedHash_ = Util.computeSeedHash(seed);
}
}
/**
* Factory: Construct a new ThetaIntersection target on the java heap.
* Called by ThetaSetOperationBuilder, test.
*
* @param seed <a href="{@docRoot}/resources/dictionary.html#seed">See Seed</a>
* @return a new IntersectionImpl on the Java heap
*/
static ThetaIntersectionImpl initNewHeapInstance(final long seed) {
final boolean dstSegFlag = false;
final boolean readOnly = false;
final ThetaIntersectionImpl impl = new ThetaIntersectionImpl(null, seed, dstSegFlag, readOnly);
impl.hardReset();
return impl;
}
/**
* Factory: Construct a new ThetaIntersection target direct to the given destination MemorySegment.
* Called by ThetaSetOperationBuilder, test.
*
* @param seed <a href="{@docRoot}/resources/dictionary.html#seed">See Seed</a>
* @param dstSeg destination MemorySegment
* @return a new IntersectionImpl that may be off-heap
*/
static ThetaIntersectionImpl initNewDirectInstance(final long seed, final MemorySegment dstSeg) {
//Load Preamble
//Pre0
dstSeg.asSlice(0, CONST_PREAMBLE_LONGS << 3).fill((byte)0);
insertPreLongs(dstSeg, CONST_PREAMBLE_LONGS); //RF not used = 0
insertSerVer(dstSeg, SER_VER);
insertFamilyID(dstSeg, Family.INTERSECTION.getID());
//lgNomLongs not used by this intersection operation
//lgArrLongs set by hardReset
//flags are already 0: reserved = readOnly = compact = ordered = empty = false;
//seedHash loaded and checked in IntersectionImpl constructor
//Pre1
//CurCount set by hardReset
insertP(dstSeg, (float) 1.0); //not used by intersection
//Pre2
//thetaLong set by hardReset
//Initialize
final boolean dstSegFlag = true;
final boolean readOnly = false;
final ThetaIntersectionImpl impl = new ThetaIntersectionImpl(dstSeg, seed, dstSegFlag, readOnly);
impl.hardReset();
return impl;
}
/**
* Factory: Heapify an intersection target from a MemorySegment image containing data.
* @param srcSeg The source MemorySegment object.
* @param seed <a href="{@docRoot}/resources/dictionary.html#seed">See seed</a>
* @return a IntersectionImpl instance on the Java heap
*/
static ThetaIntersectionImpl heapifyInstance(final MemorySegment srcSeg, final long seed) {
final boolean dstSegFlag = false;
final boolean readOnly = false;
ThetaIntersectionImpl.segChecks(srcSeg);
final ThetaIntersectionImpl impl = new ThetaIntersectionImpl(null, seed, dstSegFlag, readOnly);
//Initialize
impl.lgArrLongs_ = extractLgArrLongs(srcSeg);
impl.curCount_ = extractCurCount(srcSeg);
impl.thetaLong_ = extractThetaLong(srcSeg);
impl.empty_ = (extractFlags(srcSeg) & EMPTY_FLAG_MASK) > 0;
if (!impl.empty_ && (impl.curCount_ > 0)) {
impl.hashTable_ = new long[1 << impl.lgArrLongs_];
MemorySegment.copy(srcSeg, JAVA_LONG_UNALIGNED, CONST_PREAMBLE_LONGS << 3, impl.hashTable_, 0, 1 << impl.lgArrLongs_);
}
return impl;
}
/**
* Factory: Wrap a ThetaIntersection target around the given source MemorySegment containing intersection data.
* If the given source MemorySegment is read-only, the returned object will also be read-only.
* @param srcSeg The source MemorySegment image.
* @param seed <a href="{@docRoot}/resources/dictionary.html#seed">See seed</a>
* @param readOnly True if MemorySegment is to be treated as read only
* @return a IntersectionImpl that wraps a source MemorySegment that contains a ThetaIntersection image
*/
static ThetaIntersectionImpl wrapInstance(
final MemorySegment srcSeg,
final long seed,
final boolean readOnly) {
final boolean dstSegFlag = false;
ThetaIntersectionImpl.segChecks(srcSeg);
final ThetaIntersectionImpl impl = new ThetaIntersectionImpl(srcSeg, seed, dstSegFlag, readOnly);
impl.lgArrLongs_ = extractLgArrLongs(srcSeg);
impl.curCount_ = extractCurCount(srcSeg);
impl.thetaLong_ = extractThetaLong(srcSeg);
impl.empty_ = (extractFlags(srcSeg) & EMPTY_FLAG_MASK) > 0;
return impl;
}
@Override
public CompactThetaSketch intersect(final ThetaSketch a, final ThetaSketch b, final boolean dstOrdered, final MemorySegment dstSeg) {
if ((wseg_ != null) && readOnly_) { throw new SketchesReadOnlyException(); }
hardReset();
intersect(a);
intersect(b);
final CompactThetaSketch csk = getResult(dstOrdered, dstSeg);
hardReset();
return csk;
}
@Override
public void intersect(final ThetaSketch sketchIn) {
if (sketchIn == null) {
throw new SketchesArgumentException("The input argument must not be null.");
}
if ((wseg_ != null) && readOnly_) { throw new SketchesReadOnlyException(); }
if (empty_ || sketchIn.isEmpty()) { //empty rule
//Because of the def of null above and the Empty Rule (which is OR), empty_ must be true.
//Whatever the current internal state, we make our local empty.
resetToEmpty();
return;
}
Util.checkSeedHashes(seedHash_, sketchIn.getSeedHash());
//Set minTheta
thetaLong_ = min(thetaLong_, sketchIn.getThetaLong()); //Theta rule
empty_ = false;
if (wseg_ != null) {
insertThetaLong(wseg_, thetaLong_);
clearEmpty(wseg_); //false
}
// The truth table for the following state machine. MinTheta is set above.
// Incoming sketch is not null and not empty, but could have 0 count and Theta < 1.0
// Case curCount sketchInEntries | Actions
// 1 <0 0 | First intersect, set curCount = 0; HT = null; minTh; exit
// 2 0 0 | set curCount = 0; HT = null; minTh; exit
// 3 >0 0 | set curCount = 0; HT = null; minTh; exit
// 4 | Not used
// 5 <0 >0 | First intersect, clone SketchIn; exit
// 6 0 >0 | set curCount = 0; HT = null; minTh; exit
// 7 >0 >0 | Perform full intersect
final int sketchInEntries = sketchIn.getRetainedEntries(true);
//states 1,2,3,6
if ((curCount_ == 0) || (sketchInEntries == 0)) {
curCount_ = 0;
if (wseg_ != null) { insertCurCount(wseg_, 0); }
hashTable_ = null; //No need for a HT. Don't bother clearing seg if valid
} //end of states 1,2,3,6
// state 5
else if ((curCount_ < 0) && (sketchInEntries > 0)) {
curCount_ = sketchIn.getRetainedEntries(true);
final int requiredLgArrLongs = minLgHashTableSize(curCount_, ThetaUtil.REBUILD_THRESHOLD);
final int priorLgArrLongs = lgArrLongs_; //prior only used in error message
lgArrLongs_ = requiredLgArrLongs;
if (wseg_ != null) { //Off heap, check if current dstSeg is large enough
insertCurCount(wseg_, curCount_);
insertLgArrLongs(wseg_, lgArrLongs_);
if (requiredLgArrLongs <= maxLgArrLongs_) {
wseg_.asSlice(CONST_PREAMBLE_LONGS << 3, 8 << lgArrLongs_).fill((byte)0);
}
else { //not enough space in dstSeg
final int requiredBytes = (8 << requiredLgArrLongs) + 24;
final int givenBytes = (8 << priorLgArrLongs) + 24;
throw new SketchesArgumentException(
"Insufficient internal MemorySegment space: " + requiredBytes + " > " + givenBytes);
}
}
else { //On the heap, allocate a HT
hashTable_ = new long[1 << lgArrLongs_];
}
moveDataToTgt(sketchIn);
} //end of state 5
//state 7
else if ((curCount_ > 0) && (sketchInEntries > 0)) {
//Sets resulting hashTable, curCount and adjusts lgArrLongs
performIntersect(sketchIn);
} //end of state 7
else {
assert false : "Should not happen";
}
}
@Override
MemorySegment getMemorySegment() { return wseg_; }
@Override
public CompactThetaSketch getResult(final boolean dstOrdered, final MemorySegment dstSeg) {
if (curCount_ < 0) {
throw new SketchesStateException(
"Calling getResult() with no intervening intersections would represent the infinite set, "
+ "which is not a legal result.");
}
long[] compactCache;
final boolean srcOrdered, srcCompact;
if (curCount_ == 0) {
compactCache = new long[0];
srcCompact = true;
srcOrdered = false; //hashTable, even though empty
return CompactOperations.componentsToCompact(
thetaLong_, curCount_, seedHash_, empty_, srcCompact, srcOrdered, dstOrdered,
dstSeg, compactCache);
}
//else curCount > 0
final long[] hashTable;
if (wseg_ != null) {
final int htLen = 1 << lgArrLongs_;
hashTable = new long[htLen];
MemorySegment.copy(wseg_, JAVA_LONG_UNALIGNED, CONST_PREAMBLE_LONGS << 3, hashTable, 0, htLen);
} else {
hashTable = hashTable_;
}
compactCache = ThetaIntersectionImpl.compactCachePart(hashTable, lgArrLongs_, curCount_, thetaLong_, dstOrdered);
srcCompact = true;
srcOrdered = dstOrdered;
return CompactOperations.componentsToCompact(
thetaLong_, curCount_, seedHash_, empty_, srcCompact, srcOrdered, dstOrdered,
dstSeg, compactCache);
}
@Override
public boolean hasMemorySegment() {
return (wseg_ != null) && wseg_.scope().isAlive();
}
@Override
public boolean hasResult() {
return hasMemorySegment() ? wseg_.get(JAVA_INT_UNALIGNED, RETAINED_ENTRIES_INT) >= 0 : curCount_ >= 0;
}
@Override
public boolean isOffHeap() {
return hasMemorySegment() && wseg_.isNative();
}
@Override
public boolean isSameResource(final MemorySegment that) {
return hasMemorySegment() && MemorySegmentStatus.isSameResource(wseg_, that);
}
@Override
public void reset() {
hardReset();
}
@Override
public byte[] toByteArray() {
final int preBytes = CONST_PREAMBLE_LONGS << 3;
final int dataBytes = curCount_ > 0 ? 8 << lgArrLongs_ : 0;
final byte[] byteArrOut = new byte[preBytes + dataBytes];
if (wseg_ != null) {
MemorySegment.copy(wseg_, JAVA_BYTE, 0, byteArrOut, 0, preBytes + dataBytes);
}
else {
final MemorySegment segOut = MemorySegment.ofArray(byteArrOut);
//preamble
segOut.set(JAVA_BYTE, PREAMBLE_LONGS_BYTE, (byte) CONST_PREAMBLE_LONGS); //RF not used = 0
segOut.set(JAVA_BYTE, SER_VER_BYTE, (byte) SER_VER);
segOut.set(JAVA_BYTE, FAMILY_BYTE, (byte) Family.INTERSECTION.getID());
segOut.set(JAVA_BYTE, LG_NOM_LONGS_BYTE, (byte) 0); //not used
segOut.set(JAVA_BYTE, LG_ARR_LONGS_BYTE, (byte) lgArrLongs_);
if (empty_) { setBits(segOut, FLAGS_BYTE, (byte) EMPTY_FLAG_MASK); }
else { clearBits(segOut, FLAGS_BYTE, (byte) EMPTY_FLAG_MASK); }
segOut.set(JAVA_SHORT_UNALIGNED, SEED_HASH_SHORT, seedHash_);
segOut.set(JAVA_INT_UNALIGNED, RETAINED_ENTRIES_INT, curCount_);
segOut.set(JAVA_FLOAT_UNALIGNED, P_FLOAT, (float) 1.0);
segOut.set(JAVA_LONG_UNALIGNED, THETA_LONG, thetaLong_);
//data
if (curCount_ > 0) {
MemorySegment.copy(hashTable_, 0, segOut, JAVA_LONG_UNALIGNED, preBytes, 1 << lgArrLongs_);
}
}
return byteArrOut;
}
//restricted
/**
* Gets the number of retained entries from this operation. If negative, it is interpreted
* as the infinite <i>Universal Set</i>.
*/
@Override
int getRetainedEntries() {
return curCount_;
}
@Override
boolean isEmpty() {
return empty_;
}
@Override
long[] getCache() {
if (wseg_ == null) {
return hashTable_ != null ? hashTable_ : new long[0];
}
//offHeap
final int arrLongs = 1 << lgArrLongs_;
final long[] outArr = new long[arrLongs];
MemorySegment.copy(wseg_, JAVA_LONG_UNALIGNED, CONST_PREAMBLE_LONGS << 3, outArr, 0, arrLongs);
return outArr;
}
@Override
short getSeedHash() {
return seedHash_;
}
@Override
long getThetaLong() {
return thetaLong_;
}
private void performIntersect(final ThetaSketch sketchIn) {
// curCount and input data are nonzero, match against HT
assert (curCount_ > 0) && !empty_;
final long[] hashTable;
if (wseg_ != null) {
final int htLen = 1 << lgArrLongs_;
hashTable = new long[htLen];
MemorySegment.copy(wseg_, JAVA_LONG_UNALIGNED, CONST_PREAMBLE_LONGS << 3, hashTable, 0, htLen);
} else {
hashTable = hashTable_;
}
//allocate space for matching
final long[] matchSet = new long[ min(curCount_, sketchIn.getRetainedEntries(true)) ];
int matchSetCount = 0;
final boolean isOrdered = sketchIn.isOrdered();
final HashIterator it = sketchIn.iterator();
while (it.next()) {
final long hashIn = it.get();
if (hashIn < thetaLong_) {
final int foundIdx = hashSearch(hashTable, lgArrLongs_, hashIn);
if (foundIdx != -1) {
matchSet[matchSetCount++] = hashIn;
}
} else if (isOrdered) { break; } // early stop
}
//reduce effective array size to minimum
curCount_ = matchSetCount;
lgArrLongs_ = minLgHashTableSize(matchSetCount, ThetaUtil.REBUILD_THRESHOLD);
if (wseg_ != null) {
insertCurCount(wseg_, matchSetCount);
insertLgArrLongs(wseg_, lgArrLongs_);
wseg_.asSlice(CONST_PREAMBLE_LONGS << 3, 8 << lgArrLongs_).fill((byte)0); //clear for rebuild
} else {
Arrays.fill(hashTable_, 0, 1 << lgArrLongs_, 0L); //clear for rebuild
}
if (curCount_ > 0) {
moveDataToTgt(matchSet, matchSetCount); //move matchSet to target
} else if (thetaLong_ == Long.MAX_VALUE) {
empty_ = true;
}
}
private void moveDataToTgt(final long[] arr, final int count) {
final int arrLongsIn = arr.length;
int tmpCnt = 0;
if (wseg_ != null) { //Off Heap puts directly into mem
final int preBytes = CONST_PREAMBLE_LONGS << 3;
final int lgArrLongs = lgArrLongs_;
final long thetaLong = thetaLong_;
for (int i = 0; i < arrLongsIn; i++ ) {
final long hashIn = arr[i];
if (continueCondition(thetaLong, hashIn)) { continue; }
hashInsertOnlyMemorySegment(wseg_, lgArrLongs, hashIn, preBytes);
tmpCnt++;
}
} else { //On Heap. Assumes HT exists and is large enough
for (int i = 0; i < arrLongsIn; i++ ) {
final long hashIn = arr[i];
if (continueCondition(thetaLong_, hashIn)) { continue; }
hashInsertOnly(hashTable_, lgArrLongs_, hashIn);
tmpCnt++;
}
}
assert tmpCnt == count : "ThetaIntersection Count Check: got: " + tmpCnt + ", expected: " + count;
}
private void moveDataToTgt(final ThetaSketch sketch) {
final int count = sketch.getRetainedEntries();
int tmpCnt = 0;
if (wseg_ != null) { //Off Heap puts directly into mem
final int preBytes = CONST_PREAMBLE_LONGS << 3;
final int lgArrLongs = lgArrLongs_;
final long thetaLong = thetaLong_;
final HashIterator it = sketch.iterator();
while (it.next()) {
final long hash = it.get();
if (continueCondition(thetaLong, hash)) { continue; }
hashInsertOnlyMemorySegment(wseg_, lgArrLongs, hash, preBytes);
tmpCnt++;
}
} else { //On Heap. Assumes HT exists and is large enough
final HashIterator it = sketch.iterator();
while (it.next()) {
final long hash = it.get();
if (continueCondition(thetaLong_, hash)) { continue; }
hashInsertOnly(hashTable_, lgArrLongs_, hash);
tmpCnt++;
}
}
assert tmpCnt == count : "ThetaIntersection Count Check: got: " + tmpCnt + ", expected: " + count;
}
private void hardReset() {
resetCommon();
if (wseg_ != null) {
insertCurCount(wseg_, -1); //Universal Set
clearEmpty(wseg_); //false
}
curCount_ = -1; //Universal Set
empty_ = false;
}
private void resetToEmpty() {
resetCommon();
if (wseg_ != null) {
insertCurCount(wseg_, 0);
setEmpty(wseg_); //true
}
curCount_ = 0;
empty_ = true;
}
private void resetCommon() {
if (wseg_ != null) {
if (readOnly_) { throw new SketchesReadOnlyException(); }
wseg_.asSlice(CONST_PREAMBLE_LONGS << 3, 8 << ThetaUtil.MIN_LG_ARR_LONGS).fill((byte)0);
insertLgArrLongs(wseg_, ThetaUtil.MIN_LG_ARR_LONGS);
insertThetaLong(wseg_, Long.MAX_VALUE);
}
lgArrLongs_ = ThetaUtil.MIN_LG_ARR_LONGS;
thetaLong_ = Long.MAX_VALUE;
hashTable_ = null;
}
/**
* Compact first 2^lgArrLongs of given array
* @param srcCache anything
* @param lgArrLongs The correct
* <a href="{@docRoot}/resources/dictionary.html#lgArrLongs">lgArrLongs</a>.
* @param curCount must be correct
* @param thetaLong The correct
* <a href="{@docRoot}/resources/dictionary.html#thetaLong">thetaLong</a>.
* @param dstOrdered true if output array must be sorted
* @return the compacted array
*/ //used in Test
static final long[] compactCachePart(final long[] srcCache, final int lgArrLongs,
final int curCount, final long thetaLong, final boolean dstOrdered) {
if (curCount == 0) {
return new long[0];
}
final long[] cacheOut = new long[curCount];
final int len = 1 << lgArrLongs;
int j = 0;
for (int i = 0; i < len; i++) {
final long v = srcCache[i];
if (v <= 0L || v >= thetaLong ) { continue; }
cacheOut[j++] = v;
}
assert curCount == j;
if (dstOrdered) {
Arrays.sort(cacheOut);
}
return cacheOut;
}
private static void checkMinSizeMemorySegment(final MemorySegment seg) {
final int minBytes = (CONST_PREAMBLE_LONGS << 3) + (8 << ThetaUtil.MIN_LG_ARR_LONGS);//280
final long cap = seg.byteSize();
if (cap < minBytes) {
throw new SketchesArgumentException(
"MemorySegment must be at least " + minBytes + " bytes. Actual capacity: " + cap);
}
}
/**
* Returns the maximum lgArrLongs given the capacity of the MemorySegment.
* @param dstSeg the given MemorySegment
* @return the maximum lgArrLongs given the capacity of the MemorySegment
*/
private static int getMaxLgArrLongs(final MemorySegment dstSeg) {
final int preBytes = CONST_PREAMBLE_LONGS << 3;
final long cap = dstSeg.byteSize();
return Integer.numberOfTrailingZeros(floorPowerOf2((int)(cap - preBytes)) >>> 3);
}
private static void segChecks(final MemorySegment srcSeg) {
//Get Preamble
//Note: This intersection operation does not use lgNomLongs (or k), per se.
//seedHash loaded and checked in private constructor
final int preLongs = ThetaSketch.getPreambleLongs(srcSeg);
final int serVer = extractSerVer(srcSeg);
final int famID = extractFamilyID(srcSeg);
final boolean empty = (extractFlags(srcSeg) & EMPTY_FLAG_MASK) > 0;
final int curCount = extractCurCount(srcSeg);
//Checks
if (preLongs != CONST_PREAMBLE_LONGS) {
throw new SketchesArgumentException(
"MemorySegment PreambleLongs must equal " + CONST_PREAMBLE_LONGS + ": " + preLongs);
}
if (serVer != SER_VER) {
throw new SketchesArgumentException("Serialization Version must equal " + SER_VER);
}
Family.INTERSECTION.checkFamilyID(famID);
if (empty) {
if (curCount != 0) {
throw new SketchesArgumentException(
"srcSeg empty state inconsistent with curCount: " + empty + "," + curCount);
}
//empty = true AND curCount_ = 0: OK
} //else empty = false, curCount could be anything
}
}