datasketches-cpp
Loading...
Searching...
No Matches
CouponList-internal.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 _COUPONLIST_INTERNAL_HPP_
21#define _COUPONLIST_INTERNAL_HPP_
22
23#include "CouponList.hpp"
24#include "CubicInterpolation.hpp"
25#include "HllUtil.hpp"
26#include "count_zeros.hpp"
27
28#include <algorithm>
29#include <cmath>
30#include <stdexcept>
31
32namespace datasketches {
33
34template<typename A>
35CouponList<A>::CouponList(uint8_t lgConfigK, target_hll_type tgtHllType, hll_mode mode, const A& allocator):
36HllSketchImpl<A>(lgConfigK, tgtHllType, mode, false),
37couponCount_(0),
38oooFlag_(false),
39coupons_(1ULL << (mode == hll_mode::LIST ? hll_constants::LG_INIT_LIST_SIZE : hll_constants::LG_INIT_SET_SIZE), 0, allocator)
40{}
41
42template<typename A>
43CouponList<A>::CouponList(const CouponList& that, const target_hll_type tgtHllType):
44HllSketchImpl<A>(that.lgConfigK_, tgtHllType, that.mode_, false),
45couponCount_(that.couponCount_),
46oooFlag_(that.oooFlag_),
47coupons_(that.coupons_)
48{}
49
50template<typename A>
51std::function<void(HllSketchImpl<A>*)> CouponList<A>::get_deleter() const {
52 return [](HllSketchImpl<A>* ptr) {
53 CouponList<A>* cl = static_cast<CouponList<A>*>(ptr);
54 ClAlloc cla(cl->getAllocator());
55 cl->~CouponList();
56 cla.deallocate(cl, 1);
57 };
58}
59
60template<typename A>
61CouponList<A>* CouponList<A>::copy() const {
62 ClAlloc cla(coupons_.get_allocator());
63 return new (cla.allocate(1)) CouponList<A>(*this);
64}
65
66template<typename A>
67CouponList<A>* CouponList<A>::copyAs(target_hll_type tgtHllType) const {
68 ClAlloc cla(coupons_.get_allocator());
69 return new (cla.allocate(1)) CouponList<A>(*this, tgtHllType);
70}
71
72template<typename A>
73CouponList<A>* CouponList<A>::newList(const void* bytes, size_t len, const A& allocator) {
74 if (len < hll_constants::LIST_INT_ARR_START) {
75 throw std::out_of_range("Input data length insufficient to hold CouponHashSet");
76 }
77
78 const uint8_t* data = static_cast<const uint8_t*>(bytes);
79 if (data[hll_constants::PREAMBLE_INTS_BYTE] != hll_constants::LIST_PREINTS) {
80 throw std::invalid_argument("Incorrect number of preInts in input stream");
81 }
82 if (data[hll_constants::SER_VER_BYTE] != hll_constants::SER_VER) {
83 throw std::invalid_argument("Wrong ser ver in input stream");
84 }
85 if (data[hll_constants::FAMILY_BYTE] != hll_constants::FAMILY_ID) {
86 throw std::invalid_argument("Input stream is not an HLL sketch");
87 }
88
89 hll_mode mode = HllSketchImpl<A>::extractCurMode(data[hll_constants::MODE_BYTE]);
90 if (mode != LIST) {
91 throw std::invalid_argument("Calling list constructor with non-list mode data");
92 }
93
94 target_hll_type tgtHllType = HllSketchImpl<A>::extractTgtHllType(data[hll_constants::MODE_BYTE]);
95
96 const uint8_t lgK = data[hll_constants::LG_K_BYTE];
97 const bool compact = ((data[hll_constants::FLAGS_BYTE] & hll_constants::COMPACT_FLAG_MASK) ? true : false);
98 const bool oooFlag = ((data[hll_constants::FLAGS_BYTE] & hll_constants::OUT_OF_ORDER_FLAG_MASK) ? true : false);
99 const bool emptyFlag = ((data[hll_constants::FLAGS_BYTE] & hll_constants::EMPTY_FLAG_MASK) ? true : false);
100
101 const uint32_t couponCount = data[hll_constants::LIST_COUNT_BYTE];
102 // Reject LIST counts at or above the fixed LIST capacity.
103 const uint32_t listCapacity = 1u << hll_constants::LG_INIT_LIST_SIZE;
104 if (couponCount >= listCapacity) {
105 throw std::invalid_argument("Attempt to deserialize invalid CouponList with couponCount >= capacity. Found couponCount: "
106 + std::to_string(couponCount)
107 + ", capacity: " + std::to_string(listCapacity));
108 }
109 const uint32_t couponsInArray = (compact ? couponCount : (1 << HllUtil<A>::computeLgArrInts(LIST, couponCount, lgK)));
110 const size_t expectedLength = hll_constants::LIST_INT_ARR_START + (couponsInArray * sizeof(uint32_t));
111 if (len < expectedLength) {
112 throw std::out_of_range("Byte array too short for sketch. Expected " + std::to_string(expectedLength)
113 + ", found: " + std::to_string(len));
114 }
115
116 ClAlloc cla(allocator);
117 CouponList<A>* sketch = new (cla.allocate(1)) CouponList<A>(lgK, tgtHllType, mode, allocator);
118 sketch->couponCount_ = couponCount;
119 sketch->putOutOfOrderFlag(oooFlag); // should always be false for LIST
120
121 if (!emptyFlag) {
122 // only need to read valid coupons, unlike in stream case
123 std::memcpy(sketch->coupons_.data(), data + hll_constants::LIST_INT_ARR_START, couponCount * sizeof(uint32_t));
124 }
125
126 return sketch;
127}
128
129template<typename A>
130CouponList<A>* CouponList<A>::newList(std::istream& is, const A& allocator) {
131 uint8_t listHeader[8];
132 read(is, listHeader, 8 * sizeof(uint8_t));
133
134 if (listHeader[hll_constants::PREAMBLE_INTS_BYTE] != hll_constants::LIST_PREINTS) {
135 throw std::invalid_argument("Incorrect number of preInts in input stream");
136 }
137 if (listHeader[hll_constants::SER_VER_BYTE] != hll_constants::SER_VER) {
138 throw std::invalid_argument("Wrong ser ver in input stream");
139 }
140 if (listHeader[hll_constants::FAMILY_BYTE] != hll_constants::FAMILY_ID) {
141 throw std::invalid_argument("Input stream is not an HLL sketch");
142 }
143
144 hll_mode mode = HllSketchImpl<A>::extractCurMode(listHeader[hll_constants::MODE_BYTE]);
145 if (mode != LIST) {
146 throw std::invalid_argument("Calling list constructor with non-list mode data");
147 }
148
149 const target_hll_type tgtHllType = HllSketchImpl<A>::extractTgtHllType(listHeader[hll_constants::MODE_BYTE]);
150
151 const uint8_t lgK = listHeader[hll_constants::LG_K_BYTE];
152 const bool compact = ((listHeader[hll_constants::FLAGS_BYTE] & hll_constants::COMPACT_FLAG_MASK) ? true : false);
153 const bool oooFlag = ((listHeader[hll_constants::FLAGS_BYTE] & hll_constants::OUT_OF_ORDER_FLAG_MASK) ? true : false);
154 const bool emptyFlag = ((listHeader[hll_constants::FLAGS_BYTE] & hll_constants::EMPTY_FLAG_MASK) ? true : false);
155
156 const uint32_t couponCount = listHeader[hll_constants::LIST_COUNT_BYTE];
157 // Reject LIST counts at or above the fixed LIST capacity.
158 const uint32_t listCapacity = 1u << hll_constants::LG_INIT_LIST_SIZE;
159 if (couponCount >= listCapacity) {
160 throw std::invalid_argument("Attempt to deserialize invalid CouponList with couponCount >= capacity. Found couponCount: "
161 + std::to_string(couponCount)
162 + ", capacity: " + std::to_string(listCapacity));
163 }
164
165 ClAlloc cla(allocator);
166 CouponList<A>* sketch = new (cla.allocate(1)) CouponList<A>(lgK, tgtHllType, mode, allocator);
167 using coupon_list_ptr = std::unique_ptr<CouponList<A>, std::function<void(HllSketchImpl<A>*)>>;
168 coupon_list_ptr ptr(sketch, sketch->get_deleter());
169 sketch->couponCount_ = couponCount;
170 sketch->putOutOfOrderFlag(oooFlag); // should always be false for LIST
171
172 if (!emptyFlag) {
173 // For stream processing, need to read entire number written to stream so read
174 // pointer ends up set correctly.
175 // If not compact, still need to read empty items even though in order.
176 const uint32_t numToRead = (compact ? couponCount : static_cast<uint32_t>(sketch->coupons_.size()));
177 read(is, sketch->coupons_.data(), numToRead * sizeof(uint32_t));
178 }
179
180 if (!is.good()) { throw std::runtime_error("error reading from std::istream"); }
181
182 return ptr.release();
183}
184
185template<typename A>
186auto CouponList<A>::serialize(bool compact, unsigned header_size_bytes) const -> vector_bytes {
187 const size_t sketchSizeBytes = (compact ? getCompactSerializationBytes() : getUpdatableSerializationBytes()) + header_size_bytes;
188 vector_bytes byteArr(sketchSizeBytes, 0, getAllocator());
189 uint8_t* bytes = byteArr.data() + header_size_bytes;
190
191 bytes[hll_constants::PREAMBLE_INTS_BYTE] = static_cast<uint8_t>(getPreInts());
192 bytes[hll_constants::SER_VER_BYTE] = static_cast<uint8_t>(hll_constants::SER_VER);
193 bytes[hll_constants::FAMILY_BYTE] = static_cast<uint8_t>(hll_constants::FAMILY_ID);
194 bytes[hll_constants::LG_K_BYTE] = static_cast<uint8_t>(this->lgConfigK_);
195 bytes[hll_constants::LG_ARR_BYTE] = count_trailing_zeros_in_u32(static_cast<uint32_t>(coupons_.size()));
196 bytes[hll_constants::FLAGS_BYTE] = this->makeFlagsByte(compact);
197 bytes[hll_constants::LIST_COUNT_BYTE] = static_cast<uint8_t>(this->mode_ == LIST ? couponCount_ : 0);
198 bytes[hll_constants::MODE_BYTE] = this->makeModeByte();
199
200 if (this->mode_ == SET) {
201 std::memcpy(bytes + hll_constants::HASH_SET_COUNT_INT, &couponCount_, sizeof(couponCount_));
202 }
203
204 // coupons
205 // isCompact() is always false for now
206 const int sw = (isCompact() ? 2 : 0) | (compact ? 1 : 0);
207 switch (sw) {
208 case 0: { // src updatable, dst updatable
209 std::memcpy(bytes + getMemDataStart(), coupons_.data(), coupons_.size() * sizeof(uint32_t));
210 break;
211 }
212 case 1: { // src updatable, dst compact
213 bytes += getMemDataStart(); // reusing pointer for incremental writes
214 for (const uint32_t coupon: *this) {
215 std::memcpy(bytes, &coupon, sizeof(coupon));
216 bytes += sizeof(coupon);
217 }
218 break;
219 }
220
221 default:
222 throw std::runtime_error("Impossible condition when serializing");
223 }
224
225 return byteArr;
226}
227
228template<typename A>
229void CouponList<A>::serialize(std::ostream& os, const bool compact) const {
230 // header
231 const uint8_t preInts = getPreInts();
232 write(os, preInts);
233 const uint8_t serialVersion(hll_constants::SER_VER);
234 write(os, serialVersion);
235 const uint8_t familyId(hll_constants::FAMILY_ID);
236 write(os, familyId);
237 const uint8_t lgKByte = this->lgConfigK_;
238 write(os, lgKByte);
239 const uint8_t lgArrIntsByte = count_trailing_zeros_in_u32(static_cast<uint32_t>(coupons_.size()));
240 write(os, lgArrIntsByte);
241 const uint8_t flagsByte = this->makeFlagsByte(compact);
242 write(os, flagsByte);
243
244 if (this->mode_ == LIST) {
245 const uint8_t listCount = static_cast<uint8_t>(couponCount_);
246 write(os, listCount);
247 } else { // mode == SET
248 const uint8_t unused = 0;
249 write(os, unused);
250 }
251
252 const uint8_t modeByte = this->makeModeByte();
253 write(os, modeByte);
254
255 if (this->mode_ == SET) {
256 // writing as int, already stored as int
257 write(os, couponCount_);
258 }
259
260 // coupons
261 // isCompact() is always false for now
262 const int sw = (isCompact() ? 2 : 0) | (compact ? 1 : 0);
263 switch (sw) {
264 case 0: { // src updatable, dst updatable
265 write(os, coupons_.data(), coupons_.size() * sizeof(uint32_t));
266 break;
267 }
268 case 1: { // src updatable, dst compact
269 for (const uint32_t coupon: *this) {
270 write(os, coupon);
271 }
272 break;
273 }
274
275 default:
276 throw std::runtime_error("Impossible condition when serializing");
277 }
278
279 return;
280}
281
282template<typename A>
283HllSketchImpl<A>* CouponList<A>::couponUpdate(uint32_t coupon) {
284 for (size_t i = 0; i < coupons_.size(); ++i) { // search for empty slot
285 const uint32_t couponAtIdx = coupons_[i];
286 if (couponAtIdx == hll_constants::EMPTY) {
287 coupons_[i] = coupon; // the actual update
288 ++couponCount_;
289 if (couponCount_ == static_cast<uint32_t>(coupons_.size())) { // array full
290 if (this->lgConfigK_ < 8) {
291 return promoteHeapListOrSetToHll(*this);
292 }
293 return promoteHeapListToSet(*this);
294 }
295 return this;
296 }
297 // cell not empty
298 if (couponAtIdx == coupon) {
299 return this; // duplicate
300 }
301 // cell not empty and not a duplicate, continue
302 }
303 throw std::runtime_error("Array invalid: no empties and no duplicates");
304}
305
306template<typename A>
307double CouponList<A>::getCompositeEstimate() const { return getEstimate(); }
308
309template<typename A>
310double CouponList<A>::getEstimate() const {
311 const double est = CubicInterpolation<A>::usingXAndYTables(couponCount_);
312 return fmax(est, couponCount_);
313}
314
315template<typename A>
316double CouponList<A>::getLowerBound(uint8_t numStdDev) const {
317 HllUtil<A>::checkNumStdDev(numStdDev);
318 const double est = CubicInterpolation<A>::usingXAndYTables(couponCount_);
319 const double tmp = est / (1.0 + (numStdDev * hll_constants::COUPON_RSE));
320 return fmax(tmp, couponCount_);
321}
322
323template<typename A>
324double CouponList<A>::getUpperBound(uint8_t numStdDev) const {
325 HllUtil<A>::checkNumStdDev(numStdDev);
326 const double est = CubicInterpolation<A>::usingXAndYTables(couponCount_);
327 const double tmp = est / (1.0 - (numStdDev * hll_constants::COUPON_RSE));
328 return fmax(tmp, couponCount_);
329}
330
331template<typename A>
332bool CouponList<A>::isEmpty() const { return getCouponCount() == 0; }
333
334template<typename A>
335uint32_t CouponList<A>::getUpdatableSerializationBytes() const {
336 return getMemDataStart() + static_cast<uint32_t>(coupons_.size()) * sizeof(uint32_t);
337}
338
339template<typename A>
340uint32_t CouponList<A>::getCouponCount() const {
341 return couponCount_;
342}
343
344template<typename A>
345uint32_t CouponList<A>::getCompactSerializationBytes() const {
346 return getMemDataStart() + (couponCount_ << 2);
347}
348
349template<typename A>
350uint32_t CouponList<A>::getMemDataStart() const {
351 return hll_constants::LIST_INT_ARR_START;
352}
353
354template<typename A>
355uint8_t CouponList<A>::getPreInts() const {
356 return hll_constants::LIST_PREINTS;
357}
358
359template<typename A>
360bool CouponList<A>::isCompact() const { return false; }
361
362template<typename A>
363bool CouponList<A>::isOutOfOrderFlag() const { return oooFlag_; }
364
365template<typename A>
366void CouponList<A>::putOutOfOrderFlag(bool oooFlag) {
367 oooFlag_ = oooFlag;
368}
369
370template<typename A>
371A CouponList<A>::getAllocator() const {
372 return coupons_.get_allocator();
373}
374
375template<typename A>
376HllSketchImpl<A>* CouponList<A>::promoteHeapListToSet(CouponList& list) {
377 return HllSketchImplFactory<A>::promoteListToSet(list);
378}
379
380template<typename A>
381HllSketchImpl<A>* CouponList<A>::promoteHeapListOrSetToHll(CouponList& src) {
382 return HllSketchImplFactory<A>::promoteListOrSetToHll(src);
383}
384
385template<typename A>
386coupon_iterator<A> CouponList<A>::begin(bool all) const {
387 return coupon_iterator<A>(coupons_.data(), coupons_.size(), 0, all);
388}
389
390template<typename A>
391coupon_iterator<A> CouponList<A>::end() const {
392 return coupon_iterator<A>(coupons_.data(), coupons_.size(), coupons_.size(), false);
393}
394
395}
396
397#endif // _COUPONLIST_INTERNAL_HPP_
DataSketches namespace.
Definition binomial_bounds.hpp:38
target_hll_type
Specifies the target type of HLL sketch to be created.
Definition hll.hpp:72