#ifndef _HLL4ARRAY_INTERNAL_HPP_
#define _HLL4ARRAY_INTERNAL_HPP_
#include "Hll4Array.hpp"
#include <cstring>
#include <memory>
#include <stdexcept>
#include <string>
namespace datasketches {
template<typename A>
Hll4Array<A>::Hll4Array(uint8_t lgConfigK, bool startFullSize, const A& allocator):
HllArray<A>(lgConfigK, target_hll_type::HLL_4, startFullSize, allocator),
auxHashMap_(nullptr)
{
const uint32_t numBytes = this->hll4ArrBytes(lgConfigK);
this->hllByteArr_.resize(numBytes, 0);
}
template<typename A>
Hll4Array<A>::Hll4Array(const Hll4Array<A>& that) :
HllArray<A>(that)
{
if (that.auxHashMap_ != nullptr) {
auxHashMap_ = that.auxHashMap_->copy();
} else {
auxHashMap_ = nullptr;
}
}
template<typename A>
Hll4Array<A>::~Hll4Array() {
if (auxHashMap_ != nullptr) {
AuxHashMap<A>::make_deleter()(auxHashMap_);
}
}
template<typename A>
std::function<void(HllSketchImpl<A>*)> Hll4Array<A>::get_deleter() const {
return [](HllSketchImpl<A>* ptr) {
Hll4Array<A>* hll = static_cast<Hll4Array<A>*>(ptr);
using Hll4Alloc = typename std::allocator_traits<A>::template rebind_alloc<Hll4Array<A>>;
Hll4Alloc hll4Alloc(hll->getAllocator());
hll->~Hll4Array();
hll4Alloc.deallocate(hll, 1);
};
}
template<typename A>
Hll4Array<A>* Hll4Array<A>::copy() const {
using Hll4Alloc = typename std::allocator_traits<A>::template rebind_alloc<Hll4Array<A>>;
Hll4Alloc hll4Alloc(this->getAllocator());
return new (hll4Alloc.allocate(1)) Hll4Array<A>(*this);
}
template<typename A>
uint32_t Hll4Array<A>::getUpdatableSerializationBytes() const {
AuxHashMap<A>* auxHashMap = getAuxHashMap();
uint32_t auxBytes;
if (auxHashMap == nullptr) {
auxBytes = 4 << hll_constants::LG_AUX_ARR_INTS[this->lgConfigK_];
} else {
auxBytes = 4 << auxHashMap->getLgAuxArrInts();
}
return hll_constants::HLL_BYTE_ARR_START + getHllByteArrBytes() + auxBytes;
}
template<typename A>
uint32_t Hll4Array<A>::getHllByteArrBytes() const {
return this->hll4ArrBytes(this->lgConfigK_);
}
template<typename A>
AuxHashMap<A>* Hll4Array<A>::getAuxHashMap() const {
return auxHashMap_;
}
template<typename A>
void Hll4Array<A>::putAuxHashMap(AuxHashMap<A>* auxHashMap) {
this->auxHashMap_ = auxHashMap;
}
template<typename A>
uint8_t Hll4Array<A>::getSlot(uint32_t slotNo) const {
const uint8_t byte = this->hllByteArr_[slotNo >> 1];
if ((slotNo & 1) > 0) { return byte >> 4;
}
return byte & hll_constants::loNibbleMask;
}
template<typename A>
uint8_t Hll4Array<A>::get_value(uint32_t index) const {
const uint8_t value = getSlot(index);
if (value != hll_constants::AUX_TOKEN) return value + this->curMin_;
return auxHashMap_->mustFindValueFor(index);
}
template<typename A>
HllSketchImpl<A>* Hll4Array<A>::couponUpdate(uint32_t coupon) {
internalCouponUpdate(coupon);
return this;
}
template<typename A>
void Hll4Array<A>::internalCouponUpdate(uint32_t coupon) {
const uint8_t newValue = HllUtil<A>::getValue(coupon);
if (newValue <= this->curMin_) {
return; }
const uint32_t configKmask = (1 << this->lgConfigK_) - 1;
const uint32_t slotNo = HllUtil<A>::getLow26(coupon) & configKmask;
internalHll4Update(slotNo, newValue);
}
template<typename A>
void Hll4Array<A>::putSlot(uint32_t slotNo, uint8_t newValue) {
const uint32_t byteno = slotNo >> 1;
const uint8_t oldValue = this->hllByteArr_[byteno];
if ((slotNo & 1) == 0) { this->hllByteArr_[byteno]
= ((oldValue & hll_constants::hiNibbleMask) | (newValue & hll_constants::loNibbleMask));
} else { this->hllByteArr_[byteno]
= ((oldValue & hll_constants::loNibbleMask) | ((newValue << 4) & hll_constants::hiNibbleMask));
}
}
template<typename A>
void Hll4Array<A>::internalHll4Update(uint32_t slotNo, uint8_t newVal) {
const uint8_t rawStoredOldValue = getSlot(slotNo); const uint8_t lbOnOldValue = rawStoredOldValue + this->curMin_;
if (newVal > lbOnOldValue) { const uint8_t actualOldValue = (rawStoredOldValue < hll_constants::AUX_TOKEN)
? (lbOnOldValue) : (auxHashMap_->mustFindValueFor(slotNo));
if (newVal > actualOldValue) { this->hipAndKxQIncrementalUpdate(actualOldValue, newVal);
const uint8_t shiftedNewValue = newVal - this->curMin_;
if (rawStoredOldValue == hll_constants::AUX_TOKEN) {
if (shiftedNewValue >= hll_constants::AUX_TOKEN) { auxHashMap_->mustReplace(slotNo, newVal);
}
else { }
} else { if (shiftedNewValue >= hll_constants::AUX_TOKEN) { putSlot(slotNo, hll_constants::AUX_TOKEN);
if (auxHashMap_ == nullptr) {
auxHashMap_ = AuxHashMap<A>::newAuxHashMap(hll_constants::LG_AUX_ARR_INTS[this->lgConfigK_],
this->lgConfigK_, this->getAllocator());
}
auxHashMap_->mustAdd(slotNo, newVal);
}
else { putSlot(slotNo, shiftedNewValue);
}
}
if (actualOldValue == this->curMin_) { this->decNumAtCurMin();
while (this->numAtCurMin_ == 0) {
shiftToBiggerCurMin(); }
}
} } }
template<typename A>
void Hll4Array<A>::shiftToBiggerCurMin() {
const uint8_t newCurMin = this->curMin_ + 1;
const uint32_t configK = 1 << this->lgConfigK_;
const uint32_t configKmask = configK - 1;
uint32_t numAtNewCurMin = 0;
uint32_t numAuxTokens = 0;
for (uint32_t i = 0; i < configK; i++) { uint8_t oldStoredValue = getSlot(i);
if (oldStoredValue == 0) {
throw std::runtime_error("Array slots cannot be 0 at this point.");
}
if (oldStoredValue < hll_constants::AUX_TOKEN) {
putSlot(i, --oldStoredValue);
if (oldStoredValue == 0) { numAtNewCurMin++; }
} else { numAuxTokens++;
if (auxHashMap_ == nullptr) {
throw std::logic_error("auxHashMap cannot be null at this point");
}
}
}
AuxHashMap<A>* newAuxMap = nullptr;
if (auxHashMap_ != nullptr) {
uint32_t slotNum;
uint8_t oldActualVal;
uint8_t newShiftedVal;
for (const auto coupon: *auxHashMap_) {
slotNum = HllUtil<A>::getLow26(coupon) & configKmask;
oldActualVal = HllUtil<A>::getValue(coupon);
newShiftedVal = oldActualVal - newCurMin;
if (newShiftedVal < 0) {
throw std::logic_error("oldActualVal < newCurMin when incrementing curMin");
}
if (getSlot(slotNum) != hll_constants::AUX_TOKEN) {
throw std::logic_error("getSlot(slotNum) != AUX_TOKEN for item in auxiliary hash map");
}
if (newShiftedVal < hll_constants::AUX_TOKEN) { if (newShiftedVal != 14) {
throw std::logic_error("newShiftedVal != 14 for item in old auxHashMap despite curMin increment");
}
putSlot(slotNum, newShiftedVal);
numAuxTokens--;
} else { if (newAuxMap == nullptr) {
newAuxMap = AuxHashMap<A>::newAuxHashMap(hll_constants::LG_AUX_ARR_INTS[this->lgConfigK_],
this->lgConfigK_, this->getAllocator());
}
newAuxMap->mustAdd(slotNum, oldActualVal);
}
} } else { if (numAuxTokens != 0) {
throw std::logic_error("No auxiliary hash map, but numAuxTokens != 0");
}
}
if (newAuxMap != nullptr) {
if (newAuxMap->getAuxCount() != numAuxTokens) {
throw std::runtime_error("Inconsistent counts: auxCount: " + std::to_string(newAuxMap->getAuxCount())
+ ", HLL tokesn: " + std::to_string(numAuxTokens));
}
}
if (auxHashMap_ != nullptr) {
AuxHashMap<A>::make_deleter()(auxHashMap_);
}
auxHashMap_ = newAuxMap;
this->curMin_ = newCurMin;
this->numAtCurMin_ = numAtNewCurMin;
}
template<typename A>
typename HllArray<A>::const_iterator Hll4Array<A>::begin(bool all) const {
return typename HllArray<A>::const_iterator(this->hllByteArr_.data(), 1 << this->lgConfigK_, 0, this->tgtHllType_,
auxHashMap_, this->curMin_, all);
}
template<typename A>
typename HllArray<A>::const_iterator Hll4Array<A>::end() const {
return typename HllArray<A>::const_iterator(this->hllByteArr_.data(), 1 << this->lgConfigK_, 1 << this->lgConfigK_,
this->tgtHllType_, auxHashMap_, this->curMin_, false);
}
template<typename A>
void Hll4Array<A>::mergeHll(const HllArray<A>& src) {
for (const auto coupon: src) {
internalCouponUpdate(coupon);
}
}
}
#endif