datasketches-cpp
Loading...
Searching...
No Matches
HllUtil.hpp
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20#ifndef _HLLUTIL_HPP_
21#define _HLLUTIL_HPP_
22
23#include "MurmurHash3.h"
24#include "RelativeErrorTables.hpp"
25#include "count_zeros.hpp"
26#include "common_defs.hpp"
27#include "ceiling_power_of_2.hpp"
28
29#include <cmath>
30#include <stdexcept>
31#include <string>
32
33namespace datasketches {
34
35enum hll_mode { LIST = 0, SET, HLL };
36
37namespace hll_constants {
38
39// preamble stuff
40static const uint8_t SER_VER = 1;
41static const uint8_t FAMILY_ID = 7;
42
43static const uint8_t EMPTY_FLAG_MASK = 4;
44static const uint8_t COMPACT_FLAG_MASK = 8;
45static const uint8_t OUT_OF_ORDER_FLAG_MASK = 16;
46// Bit 32 is RESERVED and must not be written or interpreted.
47// It was formerly FULL_SIZE_FLAG_MASK here, while datasketches-java uses the same bit as
48// REBUILD_CURMIN_NUM_KXQ_MASK for its union gadget. The two meanings collided across
49// implementations, so this side no longer writes or reads it. Do not reuse: bits 64 and 128
50// are free.
51static const uint8_t RESERVED_FLAG_MASK_32 = 32;
52// Deprecated alias for the bit above, retained for source compatibility. Do not use.
53// Added 2026-09-07 (#521), first shipping in the release after 5.2.0. No [[deprecated]]:
54// this library targets C++11, so the alias warns no one.
55static const uint8_t FULL_SIZE_FLAG_MASK = RESERVED_FLAG_MASK_32;
56
57static const uint32_t PREAMBLE_INTS_BYTE = 0;
58static const uint32_t SER_VER_BYTE = 1;
59static const uint32_t FAMILY_BYTE = 2;
60static const uint32_t LG_K_BYTE = 3;
61static const uint32_t LG_ARR_BYTE = 4;
62static const uint32_t FLAGS_BYTE = 5;
63static const uint32_t LIST_COUNT_BYTE = 6;
64static const uint32_t HLL_CUR_MIN_BYTE = 6;
65static const uint32_t MODE_BYTE = 7; // lo2bits = curMode, next 2 bits = tgtHllMode
66
67// Coupon List
68static const uint32_t LIST_INT_ARR_START = 8;
69static const uint8_t LIST_PREINTS = 2;
70// Coupon Hash Set
71static const uint32_t HASH_SET_COUNT_INT = 8;
72static const uint32_t HASH_SET_INT_ARR_START = 12;
73static const uint8_t HASH_SET_PREINTS = 3;
74// HLL
75static const uint8_t HLL_PREINTS = 10;
76static const uint32_t HLL_BYTE_ARR_START = 40;
77static const uint32_t HIP_ACCUM_DOUBLE = 8;
78static const uint32_t KXQ0_DOUBLE = 16;
79static const uint32_t KXQ1_DOUBLE = 24;
80static const uint32_t CUR_MIN_COUNT_INT = 32;
81static const uint32_t AUX_COUNT_INT = 36;
82
83static const uint32_t EMPTY_SKETCH_SIZE_BYTES = 8;
84
85// other HllUtil stuff
86static const uint8_t KEY_BITS_26 = 26;
87static const uint8_t VAL_BITS_6 = 6;
88static const uint32_t KEY_MASK_26 = (1 << KEY_BITS_26) - 1;
89static const uint32_t VAL_MASK_6 = (1 << VAL_BITS_6) - 1;
90static const uint32_t EMPTY = 0;
91static const uint8_t MIN_LOG_K = 4;
92static const uint8_t MAX_LOG_K = 21;
93
94static const double HLL_HIP_RSE_FACTOR = 0.8325546111576977; // sqrt(ln(2))
95static const double HLL_NON_HIP_RSE_FACTOR = 1.0389617614136892; // sqrt((3 * ln(2)) - 1)
96static const double COUPON_RSE_FACTOR = 0.409; // at transition point not the asymptote
97static const double COUPON_RSE = COUPON_RSE_FACTOR / (1 << 13);
98
99static const uint8_t LG_INIT_LIST_SIZE = 3;
100static const uint8_t LG_INIT_SET_SIZE = 5;
101static const uint32_t RESIZE_NUMER = 3;
102static const uint32_t RESIZE_DENOM = 4;
103
104static const uint8_t loNibbleMask = 0x0f;
105static const uint8_t hiNibbleMask = 0xf0;
106static const uint8_t AUX_TOKEN = 0xf;
107
112static const uint8_t LG_AUX_ARR_INTS[] = {
113 0, 2, 2, 2, 2, 2, 2, 3, 3, 3, // 0 - 9
114 4, 4, 5, 5, 6, 7, 8, 9, 10, 11, // 10-19
115 12, 13, 14, 15, 16, 17, 18 // 20-26
116};
117
118} // namespace hll_constants
119
120
121// template provides internal consistency and allows static float values
122// but we don't use the template parameter anywhere
123template<typename A = std::allocator<uint8_t> >
124class HllUtil final {
125public:
126
127 static uint32_t coupon(const uint64_t hash[]);
128 static uint32_t coupon(const HashState& hashState);
129 static void hash(const void* key, size_t keyLen, uint64_t seed, HashState& result);
130 static uint8_t checkLgK(uint8_t lgK);
131 static void checkMemSize(uint64_t minBytes, uint64_t capBytes);
132 static inline void checkNumStdDev(uint8_t numStdDev);
133 static uint32_t pair(uint32_t slotNo, uint8_t value);
134 static uint32_t getLow26(uint32_t coupon);
135 static uint8_t getValue(uint32_t coupon);
136 static uint8_t simpleIntLog2(uint32_t n); // n must be power of 2
137 static uint8_t computeLgArrInts(hll_mode mode, uint32_t count, uint8_t lgConfigK);
138 static double getRelErr(bool upperBound, bool unioned, uint8_t lgConfigK, uint8_t numStdDev);
139};
140
141template<typename A>
142inline uint32_t HllUtil<A>::coupon(const uint64_t hash[]) {
143 uint32_t addr26 = hash[0] & hll_constants::KEY_MASK_26;
144 uint8_t lz = count_leading_zeros_in_u64(hash[1]);
145 uint8_t value = ((lz > 62 ? 62 : lz) + 1);
146 return (value << hll_constants::KEY_BITS_26) | addr26;
147}
148
149template<typename A>
150inline uint32_t HllUtil<A>::coupon(const HashState& hashState) {
151 uint32_t addr26 = (int) (hashState.h1 & hll_constants::KEY_MASK_26);
152 uint8_t lz = count_leading_zeros_in_u64(hashState.h2);
153 uint8_t value = ((lz > 62 ? 62 : lz) + 1);
154 return (value << hll_constants::KEY_BITS_26) | addr26;
155}
156
157template<typename A>
158inline void HllUtil<A>::hash(const void* key, size_t keyLen, uint64_t seed, HashState& result) {
159 MurmurHash3_x64_128(key, keyLen, seed, result);
160}
161
162template<typename A>
163inline uint8_t HllUtil<A>::checkLgK(uint8_t lgK) {
164 if ((lgK >= hll_constants::MIN_LOG_K) && (lgK <= hll_constants::MAX_LOG_K)) {
165 return lgK;
166 } else {
167 throw std::invalid_argument("Invalid value of k: " + std::to_string(lgK));
168 }
169}
170
171template<typename A>
172inline double HllUtil<A>::getRelErr(bool upperBound, bool unioned,
173 uint8_t lgConfigK, uint8_t numStdDev) {
174 checkLgK(lgConfigK);
175 if (lgConfigK > 12) {
176 const double rseFactor = unioned ?
177 hll_constants::HLL_NON_HIP_RSE_FACTOR : hll_constants::HLL_HIP_RSE_FACTOR;
178 const uint32_t configK = 1 << lgConfigK;
179 return (upperBound ? -1 : 1) * (numStdDev * rseFactor) / sqrt(configK);
180 } else {
181 return RelativeErrorTables<A>::getRelErr(upperBound, unioned, lgConfigK, numStdDev);
182 }
183}
184
185template<typename A>
186inline void HllUtil<A>::checkMemSize(uint64_t minBytes, uint64_t capBytes) {
187 if (capBytes < minBytes) {
188 throw std::invalid_argument("Given destination array is not large enough: " + std::to_string(capBytes));
189 }
190}
191
192template<typename A>
193inline void HllUtil<A>::checkNumStdDev(uint8_t numStdDev) {
194 if ((numStdDev < 1) || (numStdDev > 3)) {
195 throw std::invalid_argument("NumStdDev may not be less than 1 or greater than 3.");
196 }
197}
198
199template<typename A>
200inline uint32_t HllUtil<A>::pair(uint32_t slotNo, uint8_t value) {
201 return (value << hll_constants::KEY_BITS_26) | (slotNo & hll_constants::KEY_MASK_26);
202}
203
204template<typename A>
205inline uint32_t HllUtil<A>::getLow26(uint32_t coupon) {
206 return coupon & hll_constants::KEY_MASK_26;
207}
208
209template<typename A>
210inline uint8_t HllUtil<A>::getValue(uint32_t coupon) {
211 return coupon >> hll_constants::KEY_BITS_26;
212}
213
214template<typename A>
215inline uint8_t HllUtil<A>::simpleIntLog2(uint32_t n) {
216 if (n == 0) {
217 throw std::logic_error("cannot take log of 0");
218 }
219 return count_trailing_zeros_in_u32(n);
220}
221
222template<typename A>
223inline uint8_t HllUtil<A>::computeLgArrInts(hll_mode mode, uint32_t count, uint8_t lgConfigK) {
224 // assume value missing and recompute
225 if (mode == LIST) { return hll_constants::LG_INIT_LIST_SIZE; }
226 uint32_t ceilPwr2 = ceiling_power_of_2(count);
227 if ((hll_constants::RESIZE_DENOM * count) > (hll_constants::RESIZE_NUMER * ceilPwr2)) { ceilPwr2 <<= 1;}
228 if (mode == SET) {
229 return std::max(hll_constants::LG_INIT_SET_SIZE, HllUtil<A>::simpleIntLog2(ceilPwr2));
230 }
231 //only used for HLL4
232 return std::max(hll_constants::LG_AUX_ARR_INTS[lgConfigK], HllUtil<A>::simpleIntLog2(ceilPwr2));
233}
234
235}
236
237#endif /* _HLLUTIL_HPP_ */
DataSketches namespace.
Definition binomial_bounds.hpp:38