blob: e59273d6eceec74db11f482c4534ab8d0bd68bee [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.quantiles;
import static org.apache.datasketches.quantiles.PreambleUtil.COMPACT_FLAG_MASK;
import static org.apache.datasketches.quantiles.PreambleUtil.EMPTY_FLAG_MASK;
import static org.apache.datasketches.quantiles.PreambleUtil.ORDERED_FLAG_MASK;
import static org.apache.datasketches.quantiles.PreambleUtil.READ_ONLY_FLAG_MASK;
import static org.apache.datasketches.quantiles.PreambleUtil.insertFamilyID;
import static org.apache.datasketches.quantiles.PreambleUtil.insertFlags;
import static org.apache.datasketches.quantiles.PreambleUtil.insertK;
import static org.apache.datasketches.quantiles.PreambleUtil.insertMaxDouble;
import static org.apache.datasketches.quantiles.PreambleUtil.insertMinDouble;
import static org.apache.datasketches.quantiles.PreambleUtil.insertN;
import static org.apache.datasketches.quantiles.PreambleUtil.insertPreLongs;
import static org.apache.datasketches.quantiles.PreambleUtil.insertSerVer;
import java.util.Arrays;
import org.apache.datasketches.Family;
import org.apache.datasketches.memory.WritableMemory;
/**
* The doubles to byte array algorithms.
*
* @author Lee Rhodes
* @author Jon Malkin
*/
final class DoublesByteArrayImpl {
private DoublesByteArrayImpl() {}
static byte[] toByteArray(final DoublesSketch sketch, final boolean ordered,
final boolean compact) {
final boolean empty = sketch.isEmpty();
//create the flags byte
final int flags = (empty ? EMPTY_FLAG_MASK : 0)
| (ordered ? ORDERED_FLAG_MASK : 0)
| (compact ? (COMPACT_FLAG_MASK | READ_ONLY_FLAG_MASK) : 0);
if (empty && !sketch.isDirect()) { //empty & on-heap
final byte[] outByteArr = new byte[Long.BYTES];
final WritableMemory memOut = WritableMemory.writableWrap(outByteArr);
final int preLongs = 1;
insertPre0(memOut, preLongs, flags, sketch.getK());
return outByteArr;
}
//not empty || direct; flags passed for convenience
return convertToByteArray(sketch, flags, ordered, compact);
}
/**
* Returns a byte array, including preamble, min, max and data extracted from the sketch.
* @param sketch the given DoublesSketch
* @param flags the Flags field
* @param ordered true if the desired form of the resulting array has the base buffer sorted.
* @param compact true if the desired form of the resulting array is in compact form.
* @return a byte array, including preamble, min, max and data extracted from the Combined Buffer.
*/
private static byte[] convertToByteArray(final DoublesSketch sketch, final int flags,
final boolean ordered, final boolean compact) {
final int preLongs = 2;
final int extra = 2; // extra space for min and max values
final int prePlusExtraBytes = (preLongs + extra) << 3;
final int k = sketch.getK();
final long n = sketch.getN();
// If not-compact, have accessor always report full levels. Then use level size to determine
// whether to copy data out.
final DoublesSketchAccessor dsa = DoublesSketchAccessor.wrap(sketch, !compact);
final int outBytes = (compact ? sketch.getCompactStorageBytes() : sketch.getUpdatableStorageBytes());
final byte[] outByteArr = new byte[outBytes];
final WritableMemory memOut = WritableMemory.writableWrap(outByteArr);
//insert preamble-0, N, min, max
insertPre0(memOut, preLongs, flags, k);
if (sketch.isEmpty()) { return outByteArr; }
insertN(memOut, n);
insertMinDouble(memOut, sketch.getMinValue());
insertMaxDouble(memOut, sketch.getMaxValue());
long memOffsetBytes = prePlusExtraBytes;
// might need to sort base buffer but don't want to change input sketch
final int bbCnt = Util.computeBaseBufferItems(k, n);
if (bbCnt > 0) { //Base buffer items only
final double[] bbItemsArr = dsa.getArray(0, bbCnt);
if (ordered) { Arrays.sort(bbItemsArr); }
memOut.putDoubleArray(memOffsetBytes, bbItemsArr, 0, bbCnt);
}
// If n < 2k, totalLevels == 0 so ok to overshoot the offset update
memOffsetBytes += (compact ? bbCnt : 2 * k) << 3;
// If serializing from a compact sketch to a non-compact form, we may end up copying data for a
// higher level one or more times into an unused level. A bit wasteful, but not incorrect.
final int totalLevels = Util.computeTotalLevels(sketch.getBitPattern());
for (int lvl = 0; lvl < totalLevels; ++lvl) {
dsa.setLevel(lvl);
if (dsa.numItems() > 0) {
assert dsa.numItems() == k;
memOut.putDoubleArray(memOffsetBytes, dsa.getArray(0, k), 0, k);
memOffsetBytes += (k << 3);
}
}
return outByteArr;
}
private static void insertPre0(final WritableMemory wmem,
final int preLongs, final int flags, final int k) {
insertPreLongs(wmem, preLongs);
insertSerVer(wmem, DoublesSketch.DOUBLES_SER_VER);
insertFamilyID(wmem, Family.QUANTILES.getID());
insertFlags(wmem, flags);
insertK(wmem, k);
}
}