blob: 8070cc072d7010aaa6a181645aae20be98c1269a [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.cpc;
import static org.apache.datasketches.Util.zeroPad;
import static org.apache.datasketches.cpc.RuntimeAsserts.rtAssert;
import static org.apache.datasketches.cpc.RuntimeAsserts.rtAssertEquals;
import java.nio.ByteOrder;
import org.apache.datasketches.Family;
import org.apache.datasketches.SketchesArgumentException;
import org.apache.datasketches.SketchesStateException;
import org.apache.datasketches.memory.Memory;
import org.apache.datasketches.memory.WritableMemory;
//@formatter:off
/**
* All formats are illustrated as Big-Endian, LSB on the right.
*
* <pre>
* Format = EMPTY_MERGED/EMPTY_HIP: NoWindow, NoSV, HIP or NoHIP = 00X.
* The first 8 bytes are common for all Formats.
* PI = 2, FIcol = 0
* Long adr ||
* || 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
* 0 ||---SEED HASH-----|-Flags--|-FIcol--|---lgK--|-FamID--|-SerVer-|---PI---|
*
*
* Format = SPARSE_HYBRID_MERGED: {NoWindow, SV, NoHIP} = 2 = 010.
* PI = 4, FIcol = 0
* Long adr ||
* || 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 |
* 1 ||-------------SV Length Ints--------|--------numCoupons = numSV---------|
*
* || 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
* 2 || |----------Start SV bit stream------|
*
*
* Format = SPARSE_HYBRID_HIP: {NoWindow, SV, HIP} = 3 = 011
* PI = 8, FIcol = 0
* Long adr ||
* || 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 |
* 1 ||-------------SV Length Ints--------|--------numCoupons = numSV---------|
*
* || 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
* 2 ||----------------------------------KxP----------------------------------|
*
* || 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 |
* 3 ||-------------------------------HIP Accum-------------------------------|
*
* || 39 | 38 | 37 | 36 | 35 | 34 | 33 | 32 |
* 4 || |----------Start of SV stream-------|
*
*
* Format = PINNED_SLIDING_MERGED_NOSV: {Window, No SV, NoHIP} = 4 = 100
* PI = 4, FIcol = valid
* Long adr ||
* || 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 |
* 1 ||----------Window Length Ints-------|------------numCoupons-------------|
*
* || 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
* 2 || |--------Start of Window stream-----|
*
*
* Format = PINNED_SLIDING_HIP_NOSV: {Window, No SV, HIP} = 5 = 101
* PI = 8, FIcol = valid
* Long adr ||
* || 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 |
* 1 ||----------Window Length Ints-------|------------numCoupons-------------|
*
* || 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
* 2 ||----------------------------------KxP----------------------------------|
*
* || 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 |
* 3 ||-------------------------------HIP Accum-------------------------------|
*
* || 39 | 38 | 37 | 36 | 35 | 34 | 33 | 32 |
* 4 || |--------Start of Window stream-----|
*
*
* Format = PINNED_SLIDING_MERGED: {Window, SV, NoHIP} = 6 = 110
* PI = 6, FIcol = valid
* Long adr ||
* || 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 |
* 1 ||---------------numSV---------------|------------numCoupons-------------|
*
* || 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
* 2 ||----------Window Length Ints-------|-------------SV Length Ints--------|
*
* || XX | XX | XX | XX | 27 | 26 | 25 | 24 |
* 3 ||--------Start of SV stream---------|--------Start of Window stream-----|
*
*
* Format = PINNED_SLIDING_HIP: {Window, SV, HIP} = 7 = 111
* PI = 10, FIcol = valid
* Long adr ||
* || 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 |
* 1 ||---------------numSV---------------|------------numCoupons-------------|
*
* || 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
* 2 ||----------------------------------KxP----------------------------------|
*
* || 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 |
* 3 ||-------------------------------HIP Accum-------------------------------|
*
* || 39 | 38 | 37 | 36 | 35 | 34 | 33 | 32 |
* 4 ||----------Window Length Ints-------|-------------SV Length Ints--------|
*
* || XX | XX | XX | XX | 43 | 42 | 41 | 40 |
* 5 ||--------Start of SV stream---------|--------Start of Window stream-----|
* </pre>
*
* @author Lee Rhodes
* @author Kevin Lang
*/
final class PreambleUtil {
private PreambleUtil() {}
static {
if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) {
throw new SketchesStateException("This sketch will not work on Big Endian CPUs.");
}
}
private static final String LS = System.getProperty("line.separator");
private static final String fmt = "%10d%10x";
/**
* The serialization version for the set of serialization formats defined in this class
*/
static final byte SER_VER = 1;
//Flag bit masks, Byte 5
static final int BIG_ENDIAN_FLAG_MASK = 1; //Reserved.
static final int COMPRESSED_FLAG_MASK = 2;
static final int HIP_FLAG_MASK = 4;
static final int SUP_VAL_FLAG_MASK = 8; //num Suprising Values > 0
static final int WINDOW_FLAG_MASK = 16;//window length > 0
//PREAMBLE SIZE
/**
* This defines the preamble space required by each of the formats in units of 4-byte integers.
*/
private static final byte[] preIntDefs = { 2, 2, 4, 8, 4, 8, 6, 10 };
/**
* Returns the defined size of the preamble in units of integers (4 bytes) given the
* <i>Format</i>.
* @param format the given <i>Format</i>.
* @return the defined size of the preamble in units of integers (4 bytes) given the <i>Format</i>.
*/
static byte getDefinedPreInts(final Format format) {
return preIntDefs[format.ordinal()];
}
//PREAMBLE LO_FIELD DEFINITIONS, OFFSETS, AND GETS
/**
* This defines the seven fields of the first eight bytes of the preamble.
* The ordinal of these values defines the byte offset.
* Do not change the order.
*/
enum LoField { PRE_INTS, SER_VERSION, FAMILY, LG_K, FI_COL, FLAGS, SEED_HASH }
/**
* Returns the defined byte offset from the start of the preamble given a <i>LoField</i>.
* This only applies to the first 8 bytes of the preamble.
* @param loField the given <i>LoField</i>.
* @return the defined byte offset from the start of the preamble given a <i>LoField</i>.
*/
static int getLoFieldOffset(final LoField loField) {
return loField.ordinal();
}
static int getPreInts(final Memory mem) {
return mem.getByte(getLoFieldOffset(LoField.PRE_INTS)) & 0XFF;
}
static int getSerVer(final Memory mem) {
return mem.getByte(getLoFieldOffset(LoField.SER_VERSION)) & 0XFF;
}
static Family getFamily(final Memory mem) {
final int fam = mem.getByte(getLoFieldOffset(LoField.FAMILY)) & 0XFF;
return Family.idToFamily(fam);
}
static int getLgK(final Memory mem) {
return mem.getByte(getLoFieldOffset(LoField.LG_K)) & 0XFF;
}
static int getFiCol(final Memory mem) {
return mem.getByte(getLoFieldOffset(LoField.FI_COL)) & 0XFF;
}
static int getFlags(final Memory mem) {
return mem.getByte(getLoFieldOffset(LoField.FLAGS)) & 0XFF;
}
static short getSeedHash(final Memory mem) {
return mem.getShort(getLoFieldOffset(LoField.SEED_HASH));
}
static int getFormatOrdinal(final Memory mem) {
final int flags = getFlags(mem);
return (flags >>> 2) & 0x7;
}
static Format getFormat(final Memory mem) {
final int ordinal = getFormatOrdinal(mem);
return Format.ordinalToFormat(ordinal);
}
static boolean hasHip(final Memory mem) {
return (getFlags(mem) & HIP_FLAG_MASK) > 0;
}
static final boolean hasSv(final Memory mem) {
return (getFlags(mem) & SUP_VAL_FLAG_MASK) > 0;
}
static final boolean hasWindow(final Memory mem) {
return (getFlags(mem) & WINDOW_FLAG_MASK) > 0;
}
static final boolean isCompressed(final Memory mem) {
return (getFlags(mem) & COMPRESSED_FLAG_MASK) > 0;
}
//PREAMBLE HI_FIELD DEFINITIONS
/**
* This defines the eight additional preamble fields located after the <i>LoField</i>.
* Do not change the order.
*
* <p>Note: NUM_SV has dual meanings: In sparse and hybrid flavors it is equivalent to
* numCoupons so it isn't stored separately. In pinned and sliding flavors is is the
* numSV of the PairTable, which stores only surprising values.</p>
*/
enum HiField { NUM_COUPONS, NUM_SV, KXP, HIP_ACCUM, SV_LENGTH_INTS, W_LENGTH_INTS, SV_STREAM,
W_STREAM }
//PREAMBLE HI_FIELD OFFSETS
/**
* This defines the byte offset for eac of the 8 <i>HiFields</i>
* given the Format ordinal (1st dimension) and the HiField ordinal (2nd dimension).
*/
private static final byte[][] hiFieldOffset = //[Format][HiField]
{ {0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{8, 0, 0, 0, 12, 0, 16, 0},
{8, 0, 16, 24, 12, 0, 32, 0},
{8, 0, 0, 0, 0, 12, 0, 16},
{8, 0, 16, 24, 0, 12, 0, 32},
{8, 12, 0, 0, 16, 20, 24, 24}, //the 2nd 24 is not used.
{8, 12, 16, 24, 32, 36, 40, 40} //the 2nd 40 is not used.
};
/**
* Returns the defined byte offset from the start of the preamble given the <i>HiField</i>
* and the <i>Format</i>.
* Note this can not be used to obtain the stream offsets.
* @param format the desired <i>Format</i>
* @param hiField the desired preamble <i>HiField</i> after the first eight bytes.
* @return the defined byte offset from the start of the preamble for the given <i>HiField</i>
* and the <i>Format</i>.
*/
static long getHiFieldOffset(final Format format, final HiField hiField) {
final int formatIdx = format.ordinal();
final int hiFieldIdx = hiField.ordinal();
final long fieldOffset = hiFieldOffset[formatIdx][hiFieldIdx] & 0xFF; //initially a byte
if (fieldOffset == 0) {
throw new SketchesStateException("Undefined preamble field given the Format: "
+ "Format: " + format.toString() + ", HiField: " + hiField.toString());
}
return fieldOffset;
}
//PREAMBLE HI_FIELD GETS
static int getNumCoupons(final Memory mem) {
final Format format = getFormat(mem);
final HiField hiField = HiField.NUM_COUPONS;
final long offset = getHiFieldOffset(format, hiField);
return mem.getInt(offset);
}
static int getNumSv(final Memory mem) {
final Format format = getFormat(mem);
final HiField hiField = HiField.NUM_SV;
final long offset = getHiFieldOffset(format, hiField);
return mem.getInt(offset);
}
static int getSvLengthInts(final Memory mem) {
final Format format = getFormat(mem);
final HiField hiField = HiField.SV_LENGTH_INTS;
final long offset = getHiFieldOffset(format, hiField);
return mem.getInt(offset);
}
static int getWLengthInts(final Memory mem) {
final Format format = getFormat(mem);
final HiField hiField = HiField.W_LENGTH_INTS;
final long offset = getHiFieldOffset(format, hiField);
return mem.getInt(offset);
}
static double getKxP(final Memory mem) {
final Format format = getFormat(mem);
final HiField hiField = HiField.KXP;
final long offset = getHiFieldOffset(format, hiField);
return mem.getDouble(offset);
}
static double getHipAccum(final Memory mem) {
final Format format = getFormat(mem);
final HiField hiField = HiField.HIP_ACCUM;
final long offset = getHiFieldOffset(format, hiField);
return mem.getDouble(offset);
}
static long getSvStreamOffset(final Memory mem) {
final Format format = getFormat(mem);
final HiField svLenField = HiField.SV_LENGTH_INTS;
if (!hasSv(mem)) {
fieldError(format, svLenField);
} else {
final long svLengthInts = mem.getInt(getHiFieldOffset(format, HiField.SV_LENGTH_INTS)) & 0XFFFF_FFFFL;
if (svLengthInts == 0) {
throw new SketchesStateException("svLengthInts cannot be zero");
}
}
long wLengthInts = 0;
if (hasWindow(mem)) {
wLengthInts = mem.getInt(getHiFieldOffset(format, HiField.W_LENGTH_INTS)) & 0XFFFF_FFFFL;
if (wLengthInts == 0) {
throw new SketchesStateException("wLengthInts cannot be zero");
}
}
return (getPreInts(mem) + wLengthInts) << 2;
}
static long getWStreamOffset(final Memory mem) {
final Format format = getFormat(mem);
final HiField wLenField = HiField.W_LENGTH_INTS;
if (!hasWindow(mem)) { fieldError(format, wLenField); }
final long wLengthInts = mem.getInt(getHiFieldOffset(format, HiField.W_LENGTH_INTS)) & 0XFFFF_FFFFL;
if (wLengthInts == 0) {
throw new SketchesStateException("wLengthInts cannot be zero");
}
return getPreInts(mem) << 2;
}
static int[] getSvStream(final Memory mem) {
final long offset = getSvStreamOffset(mem);
final int svLengthInts = getSvLengthInts(mem);
final int[] svStream = new int[svLengthInts];
mem.getIntArray(offset, svStream, 0, svLengthInts);
return svStream;
}
static int[] getWStream(final Memory mem) {
final long offset = getWStreamOffset(mem);
final int wLength = getWLengthInts(mem);
final int[] wStream = new int[wLength];
mem.getIntArray(offset, wStream, 0, wLength);
return wStream;
}
// PUT INTO MEMORY
static void putEmptyMerged(final WritableMemory wmem,
final int lgK,
final short seedHash) {
final Format format = Format.EMPTY_MERGED;
final byte preInts = getDefinedPreInts(format);
final byte fiCol = (byte) 0;
final byte flags = (byte) ((format.ordinal() << 2) | COMPRESSED_FLAG_MASK);
checkCapacity(wmem.getCapacity(), 8);
putFirst8(wmem, preInts, (byte) lgK, fiCol, flags, seedHash);
}
static void putEmptyHip(final WritableMemory wmem,
final int lgK,
final short seedHash) {
final Format format = Format.EMPTY_HIP;
final byte preInts = getDefinedPreInts(format);
final byte fiCol = (byte) 0;
final byte flags = (byte) ((format.ordinal() << 2) | COMPRESSED_FLAG_MASK);
checkCapacity(wmem.getCapacity(), 8);
putFirst8(wmem, preInts, (byte) lgK, fiCol, flags, seedHash);
}
static void putSparseHybridMerged(final WritableMemory wmem,
final int lgK,
final int numCoupons, //unsigned
final int svLengthInts,
final short seedHash,
final int[] svStream) {
final Format format = Format.SPARSE_HYBRID_MERGED;
final byte preInts = getDefinedPreInts(format);
final byte fiCol = (byte) 0;
final byte flags = (byte) ((format.ordinal() << 2) | COMPRESSED_FLAG_MASK);
checkCapacity(wmem.getCapacity(), 4L * (preInts + svLengthInts));
putFirst8(wmem, preInts, (byte) lgK, fiCol, flags, seedHash);
wmem.putInt(getHiFieldOffset(format, HiField.NUM_COUPONS), numCoupons);
wmem.putInt(getHiFieldOffset(format, HiField.SV_LENGTH_INTS), svLengthInts);
wmem.putIntArray(getSvStreamOffset(wmem), svStream, 0, svLengthInts);
}
static void putSparseHybridHip(final WritableMemory wmem,
final int lgK,
final int numCoupons, //unsigned
final int svLengthInts,
final double kxp,
final double hipAccum,
final short seedHash,
final int[] svStream) {
final Format format = Format.SPARSE_HYBRID_HIP;
final byte preInts = getDefinedPreInts(format);
final byte fiCol = (byte) 0;
final byte flags = (byte) ((format.ordinal() << 2) | COMPRESSED_FLAG_MASK);
checkCapacity(wmem.getCapacity(), 4L * (preInts + svLengthInts));
putFirst8(wmem, preInts, (byte) lgK, fiCol, flags, seedHash);
wmem.putInt(getHiFieldOffset(format, HiField.NUM_COUPONS), numCoupons);
wmem.putInt(getHiFieldOffset(format, HiField.SV_LENGTH_INTS), svLengthInts);
wmem.putDouble(getHiFieldOffset(format, HiField.KXP), kxp);
wmem.putDouble(getHiFieldOffset(format, HiField.HIP_ACCUM), hipAccum);
wmem.putIntArray(getSvStreamOffset(wmem), svStream, 0, svLengthInts);
}
static void putPinnedSlidingMergedNoSv(final WritableMemory wmem,
final int lgK,
final int fiCol,
final int numCoupons, //unsigned
final int wLengthInts,
final short seedHash,
final int[] wStream) {
final Format format = Format.PINNED_SLIDING_MERGED_NOSV;
final byte preInts = getDefinedPreInts(format);
final byte flags = (byte) ((format.ordinal() << 2) | COMPRESSED_FLAG_MASK);
checkCapacity(wmem.getCapacity(), 4L * (preInts + wLengthInts));
putFirst8(wmem, preInts, (byte) lgK, (byte) fiCol, flags, seedHash);
wmem.putInt(getHiFieldOffset(format, HiField.NUM_COUPONS), numCoupons);
wmem.putInt(getHiFieldOffset(format, HiField.W_LENGTH_INTS), wLengthInts);
wmem.putIntArray(getWStreamOffset(wmem), wStream, 0, wLengthInts);
}
static void putPinnedSlidingHipNoSv(final WritableMemory wmem,
final int lgK,
final int fiCol,
final int numCoupons, //unsigned
final int wLengthInts,
final double kxp,
final double hipAccum,
final short seedHash,
final int[] wStream) {
final Format format = Format.PINNED_SLIDING_HIP_NOSV;
final byte preInts = getDefinedPreInts(format);
final byte flags = (byte) ((format.ordinal() << 2) | COMPRESSED_FLAG_MASK);
checkCapacity(wmem.getCapacity(), 4L * (preInts + wLengthInts));
putFirst8(wmem, preInts, (byte) lgK, (byte) fiCol, flags, seedHash);
wmem.putInt(getHiFieldOffset(format, HiField.NUM_COUPONS), numCoupons);
wmem.putInt(getHiFieldOffset(format, HiField.W_LENGTH_INTS), wLengthInts);
wmem.putDouble(getHiFieldOffset(format, HiField.KXP), kxp);
wmem.putDouble(getHiFieldOffset(format, HiField.HIP_ACCUM), hipAccum);
wmem.putIntArray(getWStreamOffset(wmem), wStream, 0, wLengthInts);
}
static void putPinnedSlidingMerged(final WritableMemory wmem,
final int lgK,
final int fiCol,
final int numCoupons, //unsigned
final int numSv,
final int svLengthInts,
final int wLengthInts,
final short seedHash,
final int[] svStream,
final int[] wStream) {
final Format format = Format.PINNED_SLIDING_MERGED;
final byte preInts = getDefinedPreInts(format);
final byte flags = (byte) ((format.ordinal() << 2) | COMPRESSED_FLAG_MASK);
checkCapacity(wmem.getCapacity(), 4L * (preInts + svLengthInts + wLengthInts));
putFirst8(wmem, preInts, (byte) lgK, (byte) fiCol, flags, seedHash);
wmem.putInt(getHiFieldOffset(format, HiField.NUM_COUPONS), numCoupons);
wmem.putInt(getHiFieldOffset(format, HiField.NUM_SV), numSv);
wmem.putInt(getHiFieldOffset(format, HiField.SV_LENGTH_INTS), svLengthInts);
wmem.putInt(getHiFieldOffset(format, HiField.W_LENGTH_INTS), wLengthInts);
wmem.putIntArray(getSvStreamOffset(wmem), svStream, 0, svLengthInts);
wmem.putIntArray(getWStreamOffset(wmem), wStream, 0, wLengthInts);
}
static void putPinnedSlidingHip(final WritableMemory wmem,
final int lgK,
final int fiCol,
final int numCoupons, //unsigned
final int numSv,
final double kxp,
final double hipAccum,
final int svLengthInts,
final int wLengthInts,
final short seedHash,
final int[] svStream,
final int[] wStream) {
final Format format = Format.PINNED_SLIDING_HIP;
final byte preInts = getDefinedPreInts(format);
final byte flags = (byte) ((format.ordinal() << 2) | COMPRESSED_FLAG_MASK);
checkCapacity(wmem.getCapacity(), 4L * (preInts + svLengthInts + wLengthInts));
putFirst8(wmem, preInts, (byte) lgK, (byte) fiCol, flags, seedHash);
wmem.putInt(getHiFieldOffset(format, HiField.NUM_COUPONS), numCoupons);
wmem.putInt(getHiFieldOffset(format, HiField.NUM_SV), numSv);
wmem.putDouble(getHiFieldOffset(format, HiField.KXP), kxp);
wmem.putDouble(getHiFieldOffset(format, HiField.HIP_ACCUM), hipAccum);
wmem.putInt(getHiFieldOffset(format, HiField.SV_LENGTH_INTS), svLengthInts);
wmem.putInt(getHiFieldOffset(format, HiField.W_LENGTH_INTS), wLengthInts);
wmem.putIntArray(getSvStreamOffset(wmem), svStream, 0, svLengthInts);
wmem.putIntArray(getWStreamOffset(wmem), wStream, 0, wLengthInts);
}
private static void putFirst8(final WritableMemory wmem, final byte preInts, final byte lgK,
final byte fiCol, final byte flags, final short seedHash) {
wmem.clear(0L, 4L * preInts);
wmem.putByte(getLoFieldOffset(LoField.PRE_INTS), preInts);
wmem.putByte(getLoFieldOffset(LoField.SER_VERSION), SER_VER);
wmem.putByte(getLoFieldOffset(LoField.FAMILY), (byte) Family.CPC.getID());
wmem.putByte(getLoFieldOffset(LoField.LG_K), lgK);
wmem.putByte(getLoFieldOffset(LoField.FI_COL), fiCol);
wmem.putByte(getLoFieldOffset(LoField.FLAGS), flags);
wmem.putShort(getLoFieldOffset(LoField.SEED_HASH), seedHash);
}
//TO STRING
static String toString(final byte[] byteArr, final boolean detail) {
final Memory mem = Memory.wrap(byteArr);
return toString(mem, detail);
}
static String toString(final Memory mem, final boolean detail) {
final long capBytes = mem.getCapacity();
//Lo Fields Preamble, first 7 fields, first 8 bytes
final int preInts = mem.getByte(getLoFieldOffset(LoField.PRE_INTS)) & 0xFF;
final int serVer = mem.getByte(getLoFieldOffset(LoField.SER_VERSION)) & 0xFF;
final Family family = Family.idToFamily(mem.getByte(getLoFieldOffset(LoField.FAMILY)) & 0xFF);
final int lgK = mem.getByte(getLoFieldOffset(LoField.LG_K)) & 0xFF;
final int fiCol = mem.getByte(getLoFieldOffset(LoField.FI_COL)) & 0xFF;
final int flags = mem.getByte(getLoFieldOffset(LoField.FLAGS)) & 0XFF;
final int seedHash = mem.getShort(getLoFieldOffset(LoField.SEED_HASH)) & 0XFFFF;
final String seedHashStr = Integer.toHexString(seedHash);
//Flags of the Flags byte
final String flagsStr = zeroPad(Integer.toBinaryString(flags), 8) + ", " + (flags);
final boolean bigEndian = (flags & BIG_ENDIAN_FLAG_MASK) > 0;
final boolean compressed = (flags & COMPRESSED_FLAG_MASK) > 0;
final boolean hasHip = (flags & HIP_FLAG_MASK) > 0;
final boolean hasSV = (flags & SUP_VAL_FLAG_MASK) > 0;
final boolean hasWindow = (flags & WINDOW_FLAG_MASK) > 0;
final int formatOrdinal = (flags >>> 2) & 0x7;
final Format format = Format.ordinalToFormat(formatOrdinal);
final String nativeOrderStr = ByteOrder.nativeOrder().toString();
long numCoupons = 0;
long numSv = 0;
long winOffset = 0;
long svLengthInts = 0;
long wLengthInts = 0;
double kxp = 0;
double hipAccum = 0;
long svStreamStart = 0;
long wStreamStart = 0;
long reqBytes = 0;
final StringBuilder sb = new StringBuilder();
sb.append(LS);
sb.append("### CPC SKETCH IMAGE - PREAMBLE:").append(LS);
sb.append("Format : ").append(format.name()).append(LS);
sb.append("Byte 0: Preamble Ints : ").append(preInts).append(LS);
sb.append("Byte 1: SerVer : ").append(serVer).append(LS);
sb.append("Byte 2: Family : ").append(family).append(LS);
sb.append("Byte 3: lgK : ").append(lgK).append(LS);
sb.append("Byte 4: First Interesting Col : ").append(fiCol).append(LS);
sb.append("Byte 5: Flags : ").append(flagsStr).append(LS);
sb.append(" BIG_ENDIAN_STORAGE : ").append(bigEndian).append(LS);
sb.append(" (Native Byte Order) : ").append(nativeOrderStr).append(LS);
sb.append(" Compressed : ").append(compressed).append(LS);
sb.append(" Has HIP : ").append(hasHip).append(LS);
sb.append(" Has Surprising Values : ").append(hasSV).append(LS);
sb.append(" Has Window Values : ").append(hasWindow).append(LS);
sb.append("Byte 6, 7: Seed Hash : ").append(seedHashStr).append(LS);
final Flavor flavor;
switch (format) {
case EMPTY_MERGED :
case EMPTY_HIP : {
flavor = CpcUtil.determineFlavor(lgK, numCoupons);
sb.append("Flavor : ").append(flavor).append(LS);
break;
}
case SPARSE_HYBRID_MERGED : {
numCoupons = mem.getInt(getHiFieldOffset(format, HiField.NUM_COUPONS)) & 0xFFFF_FFFFL;
numSv = numCoupons;
svLengthInts = mem.getInt(getHiFieldOffset(format, HiField.SV_LENGTH_INTS)) & 0xFFFF_FFFFL;
svStreamStart = getSvStreamOffset(mem);
reqBytes = svStreamStart + (svLengthInts << 2);
flavor = CpcUtil.determineFlavor(lgK, numCoupons);
sb.append("Flavor : ").append(flavor).append(LS);
sb.append("Num Coupons : ").append(numCoupons).append(LS);
sb.append("Num SV : ").append(numSv).append(LS);
sb.append("SV Length Ints : ").append(svLengthInts).append(LS);
sb.append("SV Stream Start : ").append(svStreamStart).append(LS);
break;
}
case SPARSE_HYBRID_HIP : {
numCoupons = mem.getInt(getHiFieldOffset(format, HiField.NUM_COUPONS)) & 0xFFFF_FFFFL;
numSv = numCoupons;
svLengthInts = mem.getInt(getHiFieldOffset(format, HiField.SV_LENGTH_INTS)) & 0xFFFF_FFFFL;
svStreamStart = getSvStreamOffset(mem);
kxp = mem.getDouble(getHiFieldOffset(format, HiField.KXP));
hipAccum = mem.getDouble(getHiFieldOffset(format, HiField.HIP_ACCUM));
reqBytes = svStreamStart + (svLengthInts << 2);
flavor = CpcUtil.determineFlavor(lgK, numCoupons);
sb.append("Flavor : ").append(flavor).append(LS);
sb.append("Num Coupons : ").append(numCoupons).append(LS);
sb.append("Num SV : ").append(numSv).append(LS);
sb.append("SV Length Ints : ").append(svLengthInts).append(LS);
sb.append("SV Stream Start : ").append(svStreamStart).append(LS);
sb.append("KxP : ").append(kxp).append(LS);
sb.append("HipAccum : ").append(hipAccum).append(LS);
break;
}
case PINNED_SLIDING_MERGED_NOSV : {
numCoupons = mem.getInt(getHiFieldOffset(format, HiField.NUM_COUPONS)) & 0xFFFF_FFFFL;
winOffset = CpcUtil.determineCorrectOffset(lgK, numCoupons);
wLengthInts = mem.getInt(getHiFieldOffset(format, HiField.W_LENGTH_INTS)) & 0xFFFF_FFFFL;
wStreamStart = getWStreamOffset(mem);
reqBytes = wStreamStart + (wLengthInts << 2);
flavor = CpcUtil.determineFlavor(lgK, numCoupons);
sb.append("Flavor : ").append(flavor).append(LS);
sb.append("Num Coupons : ").append(numCoupons).append(LS);
sb.append("Window Offset : ").append(winOffset).append(LS);
sb.append("Window Length Ints : ").append(wLengthInts).append(LS);
sb.append("Window Stream Start : ").append(wStreamStart).append(LS);
break;
}
case PINNED_SLIDING_HIP_NOSV : {
numCoupons = mem.getInt(getHiFieldOffset(format, HiField.NUM_COUPONS)) & 0xFFFF_FFFFL;
winOffset = CpcUtil.determineCorrectOffset(lgK, numCoupons);
wLengthInts = mem.getInt(getHiFieldOffset(format, HiField.W_LENGTH_INTS)) & 0xFFFF_FFFFL;
wStreamStart = getWStreamOffset(mem);
kxp = mem.getDouble(getHiFieldOffset(format, HiField.KXP));
hipAccum = mem.getDouble(getHiFieldOffset(format, HiField.HIP_ACCUM));
reqBytes = wStreamStart + (wLengthInts << 2);
flavor = CpcUtil.determineFlavor(lgK, numCoupons);
sb.append("Flavor : ").append(flavor).append(LS);
sb.append("Num Coupons : ").append(numCoupons).append(LS);
sb.append("Window Offset : ").append(winOffset).append(LS);
sb.append("Window Length Ints : ").append(wLengthInts).append(LS);
sb.append("Window Stream Start : ").append(wStreamStart).append(LS);
sb.append("KxP : ").append(kxp).append(LS);
sb.append("HipAccum : ").append(hipAccum).append(LS);
break;
}
case PINNED_SLIDING_MERGED : {
numCoupons = mem.getInt(getHiFieldOffset(format, HiField.NUM_COUPONS) & 0xFFFF_FFFFL);
winOffset = CpcUtil.determineCorrectOffset(lgK, numCoupons);
wLengthInts = mem.getInt(getHiFieldOffset(format, HiField.W_LENGTH_INTS)) & 0xFFFF_FFFFL;
numSv = mem.getInt(getHiFieldOffset(format, HiField.NUM_SV)) & 0xFFFF_FFFFL;
svLengthInts = mem.getInt(getHiFieldOffset(format, HiField.SV_LENGTH_INTS)) & 0xFFFF_FFFFL;
wStreamStart = getWStreamOffset(mem);
svStreamStart = getSvStreamOffset(mem);
reqBytes = svStreamStart + (svLengthInts << 2);
flavor = CpcUtil.determineFlavor(lgK, numCoupons);
sb.append("Flavor : ").append(flavor).append(LS);
sb.append("Num Coupons : ").append(numCoupons).append(LS);
sb.append("Num SV : ").append(numSv).append(LS);
sb.append("SV Length Ints : ").append(svLengthInts).append(LS);
sb.append("SV Stream Start : ").append(svStreamStart).append(LS);
sb.append("Window Offset : ").append(winOffset).append(LS);
sb.append("Window Length Ints : ").append(wLengthInts).append(LS);
sb.append("Window Stream Start : ").append(wStreamStart).append(LS);
break;
}
case PINNED_SLIDING_HIP : {
numCoupons = mem.getInt(getHiFieldOffset(format, HiField.NUM_COUPONS) & 0xFFFF_FFFFL);
winOffset = CpcUtil.determineCorrectOffset(lgK, numCoupons);
wLengthInts = mem.getInt(getHiFieldOffset(format, HiField.W_LENGTH_INTS)) & 0xFFFF_FFFFL;
numSv = mem.getInt(getHiFieldOffset(format, HiField.NUM_SV)) & 0xFFFF_FFFFL;
svLengthInts = mem.getInt(getHiFieldOffset(format, HiField.SV_LENGTH_INTS)) & 0xFFFF_FFFFL;
wStreamStart = getWStreamOffset(mem);
svStreamStart = getSvStreamOffset(mem);
kxp = mem.getDouble(getHiFieldOffset(format, HiField.KXP));
hipAccum = mem.getDouble(getHiFieldOffset(format, HiField.HIP_ACCUM));
reqBytes = svStreamStart + (svLengthInts << 2);
flavor = CpcUtil.determineFlavor(lgK, numCoupons);
sb.append("Flavor : ").append(flavor).append(LS);
sb.append("Num Coupons : ").append(numCoupons).append(LS);
sb.append("Num SV : ").append(numSv).append(LS);
sb.append("SV Length Ints : ").append(svLengthInts).append(LS);
sb.append("SV Stream Start : ").append(svStreamStart).append(LS);
sb.append("Window Offset : ").append(winOffset).append(LS);
sb.append("Window Length Ints : ").append(wLengthInts).append(LS);
sb.append("Window Stream Start : ").append(wStreamStart).append(LS);
sb.append("KxP : ").append(kxp).append(LS);
sb.append("HipAccum : ").append(hipAccum).append(LS);
break;
}
}
sb.append("Actual Bytes : ").append(capBytes).append(LS);
sb.append("Required Bytes : ").append(reqBytes).append(LS);
if (detail) {
sb.append(LS).append("### CPC SKETCH IMAGE - DATA").append(LS);
if (wLengthInts > 0) {
sb.append(LS).append("Window Stream:").append(LS);
listData(mem, wStreamStart, wLengthInts, sb);
}
if (svLengthInts > 0) {
sb.append(LS).append("SV Stream:").append(LS);
listData(mem, svStreamStart, svLengthInts, sb);
}
}
sb.append("### END CPC SKETCH IMAGE").append(LS);
return sb.toString();
} //end toString(mem)
private static void listData(final Memory mem, final long offsetBytes, final long lengthInts,
final StringBuilder sb) {
final long memCap = mem.getCapacity();
final long expectedCap = offsetBytes + (4L * lengthInts);
checkCapacity(memCap, expectedCap);
for (int i = 0; i < lengthInts; i++) {
sb.append(String.format(fmt, i, mem.getInt(offsetBytes + (4L * i)))).append(LS);
}
}
static void fieldError(final Format format, final HiField hiField) {
throw new SketchesArgumentException(
"Operation is illegal: Format = " + format.name() + ", HiField = " + hiField);
}
static void checkCapacity(final long memCap, final long expectedCap) {
if (memCap < expectedCap) {
throw new SketchesArgumentException(
"Insufficient Image Bytes = " + memCap + ", Expected = " + expectedCap);
}
}
//basic checks of SerVer, Format, preInts, Family, fiCol, lgK.
static void checkLoPreamble(final Memory mem) {
rtAssertEquals(getSerVer(mem), SER_VER & 0XFF);
final Format fmt = getFormat(mem);
final int preIntsDef = getDefinedPreInts(fmt) & 0XFF;
rtAssertEquals(getPreInts(mem), preIntsDef);
final Family fam = getFamily(mem);
rtAssert(fam == Family.CPC);
final int lgK = getLgK(mem);
rtAssert((lgK >= 4) && (lgK <= 26));
final int fiCol = getFiCol(mem);
rtAssert((fiCol <= 63) && (fiCol >= 0));
}
}
//@formatter:on