118 return table_.num_entries_ > 1 ? false :
true;
123 return is_empty() ? theta_constants::MAX_THETA : table_.theta_;
128 return table_.num_entries_;
133 return compute_seed_hash(table_.seed_);
138 return table_.lg_nom_size_;
148 update(&value,
sizeof(value));
153 update(&value,
sizeof(value));
158 update(
static_cast<int32_t
>(value));
163 update(
static_cast<int64_t
>(value));
168 update(
static_cast<int16_t
>(value));
173 update(
static_cast<int64_t
>(value));
178 update(
static_cast<int8_t
>(value));
183 update(
static_cast<int64_t
>(value));
188 update(canonical_double(value));
193 update(
static_cast<double>(value));
198 if (value.empty())
return;
199 update(value.c_str(), value.length());
204 const uint64_t hash = table_.hash_and_screen(data, length);
205 if (hash == 0)
return;
206 auto result = table_.find(hash);
207 if (!result.second) {
208 table_.insert(result.first, hash);
224 return iterator(table_.entries_, 1 << table_.lg_cur_size_, 0);
229 return iterator(
nullptr, 0, 1 << table_.lg_cur_size_);
234 return const_iterator(table_.entries_, 1 << table_.lg_cur_size_, 0);
239 return const_iterator(
nullptr, 0, 1 << table_.lg_cur_size_);
246 std::vector<uint64_t, A> entries(table_.allocator_);
247 if (this->is_empty()) {
251 entries.reserve(this->get_num_retained());
252 std::copy(this->begin(), this->end(), std::back_inserter(entries));
253 uint64_t theta = this->get_theta64();
254 const uint32_t nominal_size = 1 << table_.lg_nom_size_;
255 theta = trim_to_nominal<trivial_extract_key>(entries, nominal_size, theta);
256 if (ordered) std::sort(entries.begin(), entries.end());
262 os <<
" lg nominal size : " <<
static_cast<int>(table_.lg_nom_size_) << std::endl;
263 os <<
" lg current size : " <<
static_cast<int>(table_.lg_cur_size_) << std::endl;
264 os <<
" resize factor : " << (1 << table_.rf_) << std::endl;
274 return update_theta_sketch_alloc(this->starting_lg_size(), this->lg_k_, this->rf_, this->p_, this->starting_theta(), this->seed_, this->allocator_);
280template<
typename Other>
288 if (!other.is_empty()) {
289 entries_.reserve(other.get_num_retained());
290 std::copy(other.begin(), other.end(), std::back_inserter(entries_));
291 if (ordered && !other.is_ordered()) std::sort(entries_.begin(), entries_.end());
297 std::vector<uint64_t, A>&& entries):
299is_ordered_(is_ordered || (entries.size() <= 1ULL)),
300seed_hash_(seed_hash),
302entries_(std::move(entries))
307 return entries_.get_allocator();
327 return static_cast<uint32_t
>(entries_.size());
332 return is_empty_ ? 0 : seed_hash_;
337 return iterator(entries_.data(),
static_cast<uint32_t
>(entries_.size()), 0);
342 return iterator(
nullptr, 0,
static_cast<uint32_t
>(entries_.size()));
347 return const_iterator(entries_.data(),
static_cast<uint32_t
>(entries_.size()), 0);
352 return const_iterator(
nullptr, 0,
static_cast<uint32_t
>(entries_.size()));
359uint8_t compact_theta_sketch_alloc<A>::get_preamble_longs(
bool compressed)
const {
361 return this->is_estimation_mode() ? 2 : 1;
363 return this->is_estimation_mode() ? 3 : this->is_empty() || entries_.size() == 1 ? 1 : 2;
373 if (compressed && is_suitable_for_compression()) {
374 return get_compressed_serialized_size_bytes(compute_entry_bits(), get_num_entries_bytes());
376 return sizeof(uint64_t) * get_preamble_longs(
false) +
sizeof(uint64_t) * entries_.size();
382 return whole_bytes_to_hold_bits<uint8_t>(32 - count_leading_zeros_in_u32(
static_cast<uint32_t
>(entries_.size())));
386size_t compact_theta_sketch_alloc<A>::get_compressed_serialized_size_bytes(uint8_t entry_bits, uint8_t num_entries_bytes)
const {
387 const size_t compressed_bits = entry_bits * entries_.size();
388 return sizeof(uint64_t) * get_preamble_longs(
true) + num_entries_bytes + whole_bytes_to_hold_bits(compressed_bits);
393 const uint8_t preamble_longs = get_preamble_longs(
false);
394 write(os, preamble_longs);
395 write(os, UNCOMPRESSED_SERIAL_VERSION);
396 write(os, SKETCH_TYPE);
397 write<uint16_t>(os, 0);
398 const uint8_t flags_byte(
399 (1 << flags::IS_COMPACT) |
400 (1 << flags::IS_READ_ONLY) |
401 (this->is_empty() ? 1 << flags::IS_EMPTY : 0) |
402 (this->is_ordered() ? 1 << flags::IS_ORDERED : 0) |
403 (is_single_item() ? 1 << flags::IS_SINGLE_ITEM : 0)
405 write(os, flags_byte);
406 write(os, get_seed_hash());
407 if (preamble_longs > 1) {
408 write(os,
static_cast<uint32_t
>(entries_.size()));
409 write<uint32_t>(os, 0);
411 if (this->is_estimation_mode()) write(os, this->theta_);
412 if (entries_.size() > 0) write(os, entries_.data(), entries_.size() *
sizeof(uint64_t));
417 const size_t size = get_serialized_size_bytes() + header_size_bytes;
418 vector_bytes bytes(size, 0, entries_.get_allocator());
419 uint8_t* ptr = bytes.data() + header_size_bytes;
420 const uint8_t preamble_longs = get_preamble_longs(
false);
421 *ptr++ = preamble_longs;
422 *ptr++ = UNCOMPRESSED_SERIAL_VERSION;
423 *ptr++ = SKETCH_TYPE;
424 ptr +=
sizeof(uint16_t);
425 const uint8_t flags_byte(
426 (1 << flags::IS_COMPACT) |
427 (1 << flags::IS_READ_ONLY) |
428 (this->is_empty() ? 1 << flags::IS_EMPTY : 0) |
429 (this->is_ordered() ? 1 << flags::IS_ORDERED : 0) |
430 (is_single_item() ? 1 << flags::IS_SINGLE_ITEM : 0)
433 ptr += copy_to_mem(get_seed_hash(), ptr);
434 if (preamble_longs > 1) {
435 ptr += copy_to_mem(
static_cast<uint32_t
>(entries_.size()), ptr);
436 ptr +=
sizeof(uint32_t);
438 if (this->is_estimation_mode()) ptr += copy_to_mem(theta_, ptr);
439 if (entries_.size() > 0) ptr += copy_to_mem(entries_.data(), ptr, entries_.size() *
sizeof(uint64_t));
446 return entries_.size() == 1 && !this->is_estimation_mode();
450bool compact_theta_sketch_alloc<A>::is_suitable_for_compression()
const {
451 if (!this->is_ordered() || entries_.size() == 0 ||
452 (entries_.size() == 1 && !this->is_estimation_mode()))
return false;
458 if (is_suitable_for_compression())
return serialize_version_4(os);
459 return serialize(os);
464 if (is_suitable_for_compression())
return serialize_version_4(header_size_bytes);
465 return serialize(header_size_bytes);
472 uint64_t previous = 0;
474 for (
const uint64_t entry: entries_) {
475 const uint64_t delta = entry - previous;
479 return 64 - count_leading_zeros_in_u64(ored);
483void compact_theta_sketch_alloc<A>::serialize_version_4(std::ostream& os)
const {
484 const uint8_t preamble_longs = get_preamble_longs(
true);
485 const uint8_t entry_bits = compute_entry_bits();
486 const uint8_t num_entries_bytes = get_num_entries_bytes();
488 write(os, preamble_longs);
489 write(os, COMPRESSED_SERIAL_VERSION);
490 write(os, SKETCH_TYPE);
491 write(os, entry_bits);
492 write(os, num_entries_bytes);
493 const uint8_t flags_byte(
494 (1 << flags::IS_COMPACT) |
495 (1 << flags::IS_READ_ONLY) |
496 (1 << flags::IS_ORDERED)
498 write(os, flags_byte);
499 write(os, get_seed_hash());
500 if (this->is_estimation_mode()) write(os, this->theta_);
501 uint32_t num_entries =
static_cast<uint32_t
>(entries_.size());
502 for (
unsigned i = 0; i < num_entries_bytes; ++i) {
503 write<uint8_t>(os, num_entries & 0xff);
507 uint64_t previous = 0;
509 vector_bytes buffer(entry_bits, 0, entries_.get_allocator());
513 for (i = 0; i + 7 < entries_.size(); i += 8) {
514 for (
unsigned j = 0; j < 8; ++j) {
515 deltas[j] = entries_[i + j] - previous;
516 previous = entries_[i + j];
518 pack_bits_block8(deltas, buffer.data(), entry_bits);
519 write(os, buffer.data(), buffer.size());
523 if (i < entries_.size()) {
525 uint8_t* ptr = buffer.data();
526 for (; i < entries_.size(); ++i) {
527 const uint64_t delta = entries_[i] - previous;
528 previous = entries_[i];
529 offset = pack_bits(delta, entry_bits, ptr, offset);
531 if (offset > 0) ++ptr;
532 write(os, buffer.data(), ptr - buffer.data());
537auto compact_theta_sketch_alloc<A>::serialize_version_4(
unsigned header_size_bytes)
const -> vector_bytes {
538 const uint8_t entry_bits = compute_entry_bits();
539 const uint8_t num_entries_bytes = get_num_entries_bytes();
540 const size_t size = get_compressed_serialized_size_bytes(entry_bits, num_entries_bytes) + header_size_bytes;
541 vector_bytes bytes(size, 0, entries_.get_allocator());
542 uint8_t* ptr = bytes.data() + header_size_bytes;
544 *ptr++ = get_preamble_longs(
true);
545 *ptr++ = COMPRESSED_SERIAL_VERSION;
546 *ptr++ = SKETCH_TYPE;
548 *ptr++ = num_entries_bytes;
549 const uint8_t flags_byte(
550 (1 << flags::IS_COMPACT) |
551 (1 << flags::IS_READ_ONLY) |
552 (1 << flags::IS_ORDERED)
555 ptr += copy_to_mem(get_seed_hash(), ptr);
556 if (this->is_estimation_mode()) {
557 ptr += copy_to_mem(theta_, ptr);
559 uint32_t num_entries =
static_cast<uint32_t
>(entries_.size());
560 for (
unsigned i = 0; i < num_entries_bytes; ++i) {
561 *ptr++ = num_entries & 0xff;
565 uint64_t previous = 0;
570 for (i = 0; i + 7 < entries_.size(); i += 8) {
571 for (
unsigned j = 0; j < 8; ++j) {
572 deltas[j] = entries_[i + j] - previous;
573 previous = entries_[i + j];
575 pack_bits_block8(deltas, ptr, entry_bits);
581 for (; i < entries_.size(); ++i) {
582 const uint64_t delta = entries_[i] - previous;
583 previous = entries_[i];
584 offset = pack_bits(delta, entry_bits, ptr, offset);
591 const auto preamble_longs = read<uint8_t>(is);
592 const auto serial_version = read<uint8_t>(is);
593 const auto type = read<uint8_t>(is);
594 checker<true>::check_sketch_type(type, SKETCH_TYPE);
595 switch (serial_version) {
597 return deserialize_v4(preamble_longs, is, seed, allocator);
599 return deserialize_v3(preamble_longs, is, seed, allocator);
601 return deserialize_v1(preamble_longs, is, seed, allocator);
603 return deserialize_v2(preamble_longs, is, seed, allocator);
605 throw std::invalid_argument(
"unexpected sketch serialization version " + std::to_string(serial_version));
610compact_theta_sketch_alloc<A> compact_theta_sketch_alloc<A>::deserialize_v1(
611 uint8_t, std::istream& is, uint64_t seed,
const A& allocator)
613 const auto seed_hash = compute_seed_hash(seed);
616 const auto num_entries = read<uint32_t>(is);
618 const auto theta = read<uint64_t>(is);
619 std::vector<uint64_t, A> entries(num_entries, 0, allocator);
621 if (!is_empty) read(is, entries.data(),
sizeof(uint64_t) * entries.size());
622 if (!is.good())
throw std::runtime_error(
"error reading from std::istream");
623 return compact_theta_sketch_alloc(is_empty,
true, seed_hash, theta, std::move(entries));
627compact_theta_sketch_alloc<A> compact_theta_sketch_alloc<A>::deserialize_v2(
628 uint8_t preamble_longs, std::istream& is, uint64_t seed,
const A& allocator)
632 const uint16_t seed_hash = read<uint16_t>(is);
633 checker<true>::check_seed_hash(seed_hash, compute_seed_hash(seed));
634 if (preamble_longs == 1) {
635 if (!is.good())
throw std::runtime_error(
"error reading from std::istream");
636 std::vector<uint64_t, A> entries(0, 0, allocator);
638 }
else if (preamble_longs == 2) {
639 const uint32_t num_entries = read<uint32_t>(is);
641 std::vector<uint64_t, A> entries(num_entries, 0, allocator);
642 if (num_entries == 0) {
645 read(is, entries.data(), entries.size() *
sizeof(uint64_t));
646 if (!is.good())
throw std::runtime_error(
"error reading from std::istream");
648 }
else if (preamble_longs == 3) {
649 const uint32_t num_entries = read<uint32_t>(is);
651 const auto theta = read<uint64_t>(is);
653 std::vector<uint64_t, A> entries(num_entries, 0, allocator);
655 if (!is.good())
throw std::runtime_error(
"error reading from std::istream");
656 return compact_theta_sketch_alloc(
true,
true, seed_hash, theta, std::move(entries));
658 read(is, entries.data(),
sizeof(uint64_t) * entries.size());
659 if (!is.good())
throw std::runtime_error(
"error reading from std::istream");
660 return compact_theta_sketch_alloc(
false,
true, seed_hash, theta, std::move(entries));
663 throw std::invalid_argument(std::to_string(preamble_longs) +
" longs of premable, but expected 1, 2, or 3");
668compact_theta_sketch_alloc<A> compact_theta_sketch_alloc<A>::deserialize_v3(
669 uint8_t preamble_longs, std::istream& is, uint64_t seed,
const A& allocator)
672 const auto flags_byte = read<uint8_t>(is);
673 const auto seed_hash = read<uint16_t>(is);
674 const bool is_empty = flags_byte & (1 << flags::IS_EMPTY);
675 if (!is_empty) checker<true>::check_seed_hash(seed_hash, compute_seed_hash(seed));
677 uint32_t num_entries = 0;
679 if (preamble_longs == 1) {
682 num_entries = read<uint32_t>(is);
684 if (preamble_longs > 2) theta = read<uint64_t>(is);
687 std::vector<uint64_t, A> entries(num_entries, 0, allocator);
688 if (!is_empty) read(is, entries.data(),
sizeof(uint64_t) * entries.size());
689 const bool is_ordered = flags_byte & (1 << flags::IS_ORDERED);
690 if (!is.good())
throw std::runtime_error(
"error reading from std::istream");
691 return compact_theta_sketch_alloc(is_empty, is_ordered, seed_hash, theta, std::move(entries));
695compact_theta_sketch_alloc<A> compact_theta_sketch_alloc<A>::deserialize_v4(
696 uint8_t preamble_longs, std::istream& is, uint64_t seed,
const A& allocator)
698 const auto entry_bits = read<uint8_t>(is);
699 compact_theta_sketch_parser<true>::check_v4_entry_bits(entry_bits);
700 const auto num_entries_bytes = read<uint8_t>(is);
701 compact_theta_sketch_parser<true>::check_v4_num_entries_bytes(num_entries_bytes);
702 const auto flags_byte = read<uint8_t>(is);
703 const auto seed_hash = read<uint16_t>(is);
704 const bool is_empty = flags_byte & (1 << flags::IS_EMPTY);
705 if (!is_empty) checker<true>::check_seed_hash(seed_hash, compute_seed_hash(seed));
707 if (preamble_longs > 1) theta = read<uint64_t>(is);
708 uint32_t num_entries = 0;
709 for (
unsigned i = 0; i < num_entries_bytes; ++i) {
710 num_entries |= read<uint8_t>(is) << (i << 3);
712 vector_bytes buffer(entry_bits, 0, allocator);
713 std::vector<uint64_t, A> entries(num_entries, 0, allocator);
717 for (i = 0; i + 7 < num_entries; i += 8) {
718 read(is, buffer.data(), buffer.size());
719 unpack_bits_block8(&entries[i], buffer.data(), entry_bits);
722 if (i < num_entries) read(is, buffer.data(), whole_bytes_to_hold_bits((num_entries - i) * entry_bits));
723 if (!is.good())
throw std::runtime_error(
"error reading from std::istream");
724 const uint8_t* ptr = buffer.data();
726 for (; i < num_entries; ++i) {
727 offset = unpack_bits(entries[i], entry_bits, ptr, offset);
730 uint64_t previous = 0;
731 for (i = 0; i < num_entries; ++i) {
732 entries[i] += previous;
733 previous = entries[i];
735 const bool is_ordered = flags_byte & (1 << flags::IS_ORDERED);
736 return compact_theta_sketch_alloc(is_empty, is_ordered, seed_hash, theta, std::move(entries));
741 auto data = compact_theta_sketch_parser<true>::parse(bytes, size, seed,
false);
742 if (data.entry_bits == 64) {
743 const uint64_t* entries =
reinterpret_cast<const uint64_t*
>(data.entries_start_ptr);
744 return compact_theta_sketch_alloc(data.is_empty, data.is_ordered, data.seed_hash, data.theta,
745 std::vector<uint64_t, A>(entries, entries + data.num_entries, allocator));
747 std::vector<uint64_t, A> entries(data.num_entries, 0, allocator);
748 const uint8_t* ptr =
reinterpret_cast<const uint8_t*
>(data.entries_start_ptr);
751 for (i = 0; i + 7 < data.num_entries; i += 8) {
752 unpack_bits_block8(&entries[i], ptr, data.entry_bits);
753 ptr += data.entry_bits;
757 for (; i < data.num_entries; ++i) {
758 offset = unpack_bits(entries[i], data.entry_bits, ptr, offset);
761 uint64_t previous = 0;
762 for (i = 0; i < data.num_entries; ++i) {
763 entries[i] += previous;
764 previous = entries[i];
766 return compact_theta_sketch_alloc(data.is_empty, data.is_ordered, data.seed_hash, data.theta, std::move(entries));
773wrapped_compact_theta_sketch_alloc<A>::wrapped_compact_theta_sketch_alloc(
const data_type& data):
789 return data_.is_empty;
794 return data_.is_ordered;
804 return data_.num_entries;
809 return is_empty() ? 0 : data_.seed_hash;
814 return const_iterator(data_.entries_start_ptr, data_.entry_bits, data_.num_entries, 0);
819 return const_iterator(data_.entries_start_ptr, data_.entry_bits, data_.num_entries, data_.num_entries);
826void wrapped_compact_theta_sketch_alloc<A>::print_items(std::ostringstream& os)
const {
827 os <<
"### Retained entries" << std::endl;
828 for (
const auto hash: *this) {
829 os << hash << std::endl;
831 os <<
"### End retained entries" << std::endl;
835template<
typename Allocator>
836wrapped_compact_theta_sketch_alloc<Allocator>::const_iterator::const_iterator(
837 const void* ptr, uint8_t entry_bits, uint32_t num_entries, uint32_t index):
839entry_bits_(entry_bits),
840num_entries_(num_entries),
843is_block_mode_(num_entries_ >= 8),
846 if (entry_bits == 64) {
847 ptr_ =
reinterpret_cast<const uint64_t*
>(ptr) + index;
848 }
else if (index < num_entries) {
849 if (is_block_mode_) {
857template<
typename Allocator>
858auto wrapped_compact_theta_sketch_alloc<Allocator>::const_iterator::operator++() -> const_iterator& {
859 if (entry_bits_ == 64) {
860 ptr_ =
reinterpret_cast<const uint64_t*
>(ptr_) + 1;
863 if (++index_ < num_entries_) {
864 if (is_block_mode_) {
865 if ((index_ & 7) == 0) {
866 if (num_entries_ - index_ >= 8) {
869 is_block_mode_ =
false;
880template<
typename Allocator>
881void wrapped_compact_theta_sketch_alloc<Allocator>::const_iterator::unpack1() {
882 const uint32_t i = index_ & 7;
883 offset_ = unpack_bits(buffer_[i], entry_bits_,
reinterpret_cast<const uint8_t*&
>(ptr_), offset_);
884 buffer_[i] += previous_;
885 previous_ = buffer_[i];
888template<
typename Allocator>
889void wrapped_compact_theta_sketch_alloc<Allocator>::const_iterator::unpack8() {
890 unpack_bits_block8(buffer_,
reinterpret_cast<const uint8_t*
>(ptr_), entry_bits_);
891 ptr_ =
reinterpret_cast<const uint8_t*
>(ptr_) + entry_bits_;
892 for (
int i = 0; i < 8; ++i) {
893 buffer_[i] += previous_;
894 previous_ = buffer_[i];
898template<
typename Allocator>
899auto wrapped_compact_theta_sketch_alloc<Allocator>::const_iterator::operator++(
int) -> const_iterator {
900 const_iterator tmp(*
this);
905template<
typename Allocator>
906bool wrapped_compact_theta_sketch_alloc<Allocator>::const_iterator::operator!=(
const const_iterator& other)
const {
907 if (entry_bits_ == 64)
return ptr_ != other.ptr_;
908 return index_ != other.index_;
911template<
typename Allocator>
912bool wrapped_compact_theta_sketch_alloc<Allocator>::const_iterator::operator==(
const const_iterator& other)
const {
913 if (entry_bits_ == 64)
return ptr_ == other.ptr_;
914 return index_ == other.index_;
917template<
typename Allocator>
918auto wrapped_compact_theta_sketch_alloc<Allocator>::const_iterator::operator*() const -> reference {
919 if (entry_bits_ == 64)
return *
reinterpret_cast<const uint64_t*
>(ptr_);
920 return buffer_[index_ & 7];
923template<
typename Allocator>
924auto wrapped_compact_theta_sketch_alloc<Allocator>::const_iterator::operator->() const -> pointer {
925 if (entry_bits_ == 64)
return reinterpret_cast<const uint64_t*
>(ptr_);
926 return buffer_ + (index_ & 7);