Path: blob/master/src/hotspot/share/utilities/bitMap.cpp
40949 views
/*1* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*22*/2324#include "precompiled.hpp"25#include "memory/allocation.inline.hpp"26#include "memory/resourceArea.hpp"27#include "runtime/atomic.hpp"28#include "utilities/bitMap.inline.hpp"29#include "utilities/copy.hpp"30#include "utilities/debug.hpp"31#include "utilities/population_count.hpp"3233STATIC_ASSERT(sizeof(BitMap::bm_word_t) == BytesPerWord); // "Implementation assumption."3435typedef BitMap::bm_word_t bm_word_t;36typedef BitMap::idx_t idx_t;3738class ResourceBitMapAllocator : StackObj {39public:40bm_word_t* allocate(idx_t size_in_words) const {41return NEW_RESOURCE_ARRAY(bm_word_t, size_in_words);42}43void free(bm_word_t* map, idx_t size_in_words) const {44// Don't free resource allocated arrays.45}46};4748class CHeapBitMapAllocator : StackObj {49MEMFLAGS _flags;5051public:52CHeapBitMapAllocator(MEMFLAGS flags) : _flags(flags) {}53bm_word_t* allocate(size_t size_in_words) const {54return ArrayAllocator<bm_word_t>::allocate(size_in_words, _flags);55}56void free(bm_word_t* map, idx_t size_in_words) const {57ArrayAllocator<bm_word_t>::free(map, size_in_words);58}59};6061class ArenaBitMapAllocator : StackObj {62Arena* _arena;6364public:65ArenaBitMapAllocator(Arena* arena) : _arena(arena) {}66bm_word_t* allocate(idx_t size_in_words) const {67return (bm_word_t*)_arena->Amalloc(size_in_words * BytesPerWord);68}69void free(bm_word_t* map, idx_t size_in_words) const {70// ArenaBitMaps currently don't free memory.71}72};7374template <class Allocator>75BitMap::bm_word_t* BitMap::reallocate(const Allocator& allocator, bm_word_t* old_map, idx_t old_size_in_bits, idx_t new_size_in_bits, bool clear) {76size_t old_size_in_words = calc_size_in_words(old_size_in_bits);77size_t new_size_in_words = calc_size_in_words(new_size_in_bits);7879bm_word_t* map = NULL;8081if (new_size_in_words > 0) {82map = allocator.allocate(new_size_in_words);8384if (old_map != NULL) {85Copy::disjoint_words((HeapWord*)old_map, (HeapWord*) map,86MIN2(old_size_in_words, new_size_in_words));87}8889if (clear && (new_size_in_bits > old_size_in_bits)) {90// If old_size_in_bits is not word-aligned, then the preceeding91// copy can include some trailing bits in the final copied word92// that also need to be cleared. See clear_range_within_word.93bm_word_t mask = bit_mask(old_size_in_bits) - 1;94map[raw_to_words_align_down(old_size_in_bits)] &= mask;95// Clear the remaining full words.96clear_range_of_words(map, old_size_in_words, new_size_in_words);97}98}99100if (old_map != NULL) {101allocator.free(old_map, old_size_in_words);102}103104return map;105}106107template <class Allocator>108bm_word_t* BitMap::allocate(const Allocator& allocator, idx_t size_in_bits, bool clear) {109// Reuse reallocate to ensure that the new memory is cleared.110return reallocate(allocator, NULL, 0, size_in_bits, clear);111}112113template <class Allocator>114void BitMap::free(const Allocator& allocator, bm_word_t* map, idx_t size_in_bits) {115bm_word_t* ret = reallocate(allocator, map, size_in_bits, 0);116assert(ret == NULL, "Reallocate shouldn't have allocated");117}118119template <class Allocator>120void BitMap::resize(const Allocator& allocator, idx_t new_size_in_bits, bool clear) {121bm_word_t* new_map = reallocate(allocator, map(), size(), new_size_in_bits, clear);122123update(new_map, new_size_in_bits);124}125126template <class Allocator>127void BitMap::initialize(const Allocator& allocator, idx_t size_in_bits, bool clear) {128assert(map() == NULL, "precondition");129assert(size() == 0, "precondition");130131resize(allocator, size_in_bits, clear);132}133134template <class Allocator>135void BitMap::reinitialize(const Allocator& allocator, idx_t new_size_in_bits, bool clear) {136// Remove previous bits - no need to clear137resize(allocator, 0, false /* clear */);138139initialize(allocator, new_size_in_bits, clear);140}141142ResourceBitMap::ResourceBitMap(idx_t size_in_bits, bool clear)143: BitMap(allocate(ResourceBitMapAllocator(), size_in_bits, clear), size_in_bits) {144}145146void ResourceBitMap::resize(idx_t new_size_in_bits) {147BitMap::resize(ResourceBitMapAllocator(), new_size_in_bits, true /* clear */);148}149150void ResourceBitMap::initialize(idx_t size_in_bits) {151BitMap::initialize(ResourceBitMapAllocator(), size_in_bits, true /* clear */);152}153154void ResourceBitMap::reinitialize(idx_t size_in_bits) {155BitMap::reinitialize(ResourceBitMapAllocator(), size_in_bits, true /* clear */);156}157158ArenaBitMap::ArenaBitMap(Arena* arena, idx_t size_in_bits)159: BitMap(allocate(ArenaBitMapAllocator(arena), size_in_bits), size_in_bits) {160}161162CHeapBitMap::CHeapBitMap(idx_t size_in_bits, MEMFLAGS flags, bool clear)163: BitMap(allocate(CHeapBitMapAllocator(flags), size_in_bits, clear), size_in_bits), _flags(flags) {164}165166CHeapBitMap::~CHeapBitMap() {167free(CHeapBitMapAllocator(_flags), map(), size());168}169170void CHeapBitMap::resize(idx_t new_size_in_bits, bool clear) {171BitMap::resize(CHeapBitMapAllocator(_flags), new_size_in_bits, clear);172}173174void CHeapBitMap::initialize(idx_t size_in_bits, bool clear) {175BitMap::initialize(CHeapBitMapAllocator(_flags), size_in_bits, clear);176}177178void CHeapBitMap::reinitialize(idx_t size_in_bits, bool clear) {179BitMap::reinitialize(CHeapBitMapAllocator(_flags), size_in_bits, clear);180}181182#ifdef ASSERT183void BitMap::verify_size(idx_t size_in_bits) {184assert(size_in_bits <= max_size_in_bits(),185"out of bounds: " SIZE_FORMAT, size_in_bits);186}187188void BitMap::verify_index(idx_t bit) const {189assert(bit < _size,190"BitMap index out of bounds: " SIZE_FORMAT " >= " SIZE_FORMAT,191bit, _size);192}193194void BitMap::verify_limit(idx_t bit) const {195assert(bit <= _size,196"BitMap limit out of bounds: " SIZE_FORMAT " > " SIZE_FORMAT,197bit, _size);198}199200void BitMap::verify_range(idx_t beg, idx_t end) const {201assert(beg <= end,202"BitMap range error: " SIZE_FORMAT " > " SIZE_FORMAT, beg, end);203verify_limit(end);204}205#endif // #ifdef ASSERT206207void BitMap::pretouch() {208os::pretouch_memory(word_addr(0), word_addr(size()));209}210211void BitMap::set_range_within_word(idx_t beg, idx_t end) {212// With a valid range (beg <= end), this test ensures that end != 0, as213// required by inverted_bit_mask_for_range. Also avoids an unnecessary write.214if (beg != end) {215bm_word_t mask = inverted_bit_mask_for_range(beg, end);216*word_addr(beg) |= ~mask;217}218}219220void BitMap::clear_range_within_word(idx_t beg, idx_t end) {221// With a valid range (beg <= end), this test ensures that end != 0, as222// required by inverted_bit_mask_for_range. Also avoids an unnecessary write.223if (beg != end) {224bm_word_t mask = inverted_bit_mask_for_range(beg, end);225*word_addr(beg) &= mask;226}227}228229void BitMap::par_put_range_within_word(idx_t beg, idx_t end, bool value) {230assert(value == 0 || value == 1, "0 for clear, 1 for set");231// With a valid range (beg <= end), this test ensures that end != 0, as232// required by inverted_bit_mask_for_range. Also avoids an unnecessary write.233if (beg != end) {234bm_word_t* pw = word_addr(beg);235bm_word_t w = *pw;236bm_word_t mr = inverted_bit_mask_for_range(beg, end);237bm_word_t nw = value ? (w | ~mr) : (w & mr);238while (true) {239bm_word_t res = Atomic::cmpxchg(pw, w, nw);240if (res == w) break;241w = res;242nw = value ? (w | ~mr) : (w & mr);243}244}245}246247void BitMap::set_range(idx_t beg, idx_t end) {248verify_range(beg, end);249250idx_t beg_full_word = to_words_align_up(beg);251idx_t end_full_word = to_words_align_down(end);252253if (beg_full_word < end_full_word) {254// The range includes at least one full word.255set_range_within_word(beg, bit_index(beg_full_word));256set_range_of_words(beg_full_word, end_full_word);257set_range_within_word(bit_index(end_full_word), end);258} else {259// The range spans at most 2 partial words.260idx_t boundary = MIN2(bit_index(beg_full_word), end);261set_range_within_word(beg, boundary);262set_range_within_word(boundary, end);263}264}265266void BitMap::clear_range(idx_t beg, idx_t end) {267verify_range(beg, end);268269idx_t beg_full_word = to_words_align_up(beg);270idx_t end_full_word = to_words_align_down(end);271272if (beg_full_word < end_full_word) {273// The range includes at least one full word.274clear_range_within_word(beg, bit_index(beg_full_word));275clear_range_of_words(beg_full_word, end_full_word);276clear_range_within_word(bit_index(end_full_word), end);277} else {278// The range spans at most 2 partial words.279idx_t boundary = MIN2(bit_index(beg_full_word), end);280clear_range_within_word(beg, boundary);281clear_range_within_word(boundary, end);282}283}284285bool BitMap::is_small_range_of_words(idx_t beg_full_word, idx_t end_full_word) {286// There is little point to call large version on small ranges.287// Need to check carefully, keeping potential idx_t over/underflow in mind,288// because beg_full_word > end_full_word can occur when beg and end are in289// the same word.290// The threshold should be at least one word.291STATIC_ASSERT(small_range_words >= 1);292return beg_full_word + small_range_words >= end_full_word;293}294295void BitMap::set_large_range(idx_t beg, idx_t end) {296verify_range(beg, end);297298idx_t beg_full_word = to_words_align_up(beg);299idx_t end_full_word = to_words_align_down(end);300301if (is_small_range_of_words(beg_full_word, end_full_word)) {302set_range(beg, end);303return;304}305306// The range includes at least one full word.307set_range_within_word(beg, bit_index(beg_full_word));308set_large_range_of_words(beg_full_word, end_full_word);309set_range_within_word(bit_index(end_full_word), end);310}311312void BitMap::clear_large_range(idx_t beg, idx_t end) {313verify_range(beg, end);314315idx_t beg_full_word = to_words_align_up(beg);316idx_t end_full_word = to_words_align_down(end);317318if (is_small_range_of_words(beg_full_word, end_full_word)) {319clear_range(beg, end);320return;321}322323// The range includes at least one full word.324clear_range_within_word(beg, bit_index(beg_full_word));325clear_large_range_of_words(beg_full_word, end_full_word);326clear_range_within_word(bit_index(end_full_word), end);327}328329void BitMap::at_put(idx_t offset, bool value) {330if (value) {331set_bit(offset);332} else {333clear_bit(offset);334}335}336337// Return true to indicate that this thread changed338// the bit, false to indicate that someone else did.339// In either case, the requested bit is in the340// requested state some time during the period that341// this thread is executing this call. More importantly,342// if no other thread is executing an action to343// change the requested bit to a state other than344// the one that this thread is trying to set it to,345// then the the bit is in the expected state346// at exit from this method. However, rather than347// make such a strong assertion here, based on348// assuming such constrained use (which though true349// today, could change in the future to service some350// funky parallel algorithm), we encourage callers351// to do such verification, as and when appropriate.352bool BitMap::par_at_put(idx_t bit, bool value) {353return value ? par_set_bit(bit) : par_clear_bit(bit);354}355356void BitMap::at_put_range(idx_t start_offset, idx_t end_offset, bool value) {357if (value) {358set_range(start_offset, end_offset);359} else {360clear_range(start_offset, end_offset);361}362}363364void BitMap::par_at_put_range(idx_t beg, idx_t end, bool value) {365verify_range(beg, end);366367idx_t beg_full_word = to_words_align_up(beg);368idx_t end_full_word = to_words_align_down(end);369370if (beg_full_word < end_full_word) {371// The range includes at least one full word.372par_put_range_within_word(beg, bit_index(beg_full_word), value);373if (value) {374set_range_of_words(beg_full_word, end_full_word);375} else {376clear_range_of_words(beg_full_word, end_full_word);377}378par_put_range_within_word(bit_index(end_full_word), end, value);379} else {380// The range spans at most 2 partial words.381idx_t boundary = MIN2(bit_index(beg_full_word), end);382par_put_range_within_word(beg, boundary, value);383par_put_range_within_word(boundary, end, value);384}385386}387388void BitMap::at_put_large_range(idx_t beg, idx_t end, bool value) {389if (value) {390set_large_range(beg, end);391} else {392clear_large_range(beg, end);393}394}395396void BitMap::par_at_put_large_range(idx_t beg, idx_t end, bool value) {397verify_range(beg, end);398399idx_t beg_full_word = to_words_align_up(beg);400idx_t end_full_word = to_words_align_down(end);401402if (is_small_range_of_words(beg_full_word, end_full_word)) {403par_at_put_range(beg, end, value);404return;405}406407// The range includes at least one full word.408par_put_range_within_word(beg, bit_index(beg_full_word), value);409if (value) {410set_large_range_of_words(beg_full_word, end_full_word);411} else {412clear_large_range_of_words(beg_full_word, end_full_word);413}414par_put_range_within_word(bit_index(end_full_word), end, value);415}416417inline bm_word_t tail_mask(idx_t tail_bits) {418assert(tail_bits != 0, "precondition"); // Works, but shouldn't be called.419assert(tail_bits < (idx_t)BitsPerWord, "precondition");420return (bm_word_t(1) << tail_bits) - 1;421}422423// Get the low tail_bits of value, which is the last partial word of a map.424inline bm_word_t tail_of_map(bm_word_t value, idx_t tail_bits) {425return value & tail_mask(tail_bits);426}427428// Compute the new last word of a map with a non-aligned length.429// new_value has the new trailing bits of the map in the low tail_bits.430// old_value is the last word of the map, including bits beyond the end.431// Returns old_value with the low tail_bits replaced by the corresponding432// bits in new_value.433inline bm_word_t merge_tail_of_map(bm_word_t new_value,434bm_word_t old_value,435idx_t tail_bits) {436bm_word_t mask = tail_mask(tail_bits);437return (new_value & mask) | (old_value & ~mask);438}439440bool BitMap::contains(const BitMap& other) const {441assert(size() == other.size(), "must have same size");442const bm_word_t* dest_map = map();443const bm_word_t* other_map = other.map();444idx_t limit = to_words_align_down(size());445for (idx_t index = 0; index < limit; ++index) {446// false if other bitmap has bits set which are clear in this bitmap.447if ((~dest_map[index] & other_map[index]) != 0) return false;448}449idx_t rest = bit_in_word(size());450// true unless there is a partial-word tail in which the other451// bitmap has bits set which are clear in this bitmap.452return (rest == 0) || tail_of_map(~dest_map[limit] & other_map[limit], rest) == 0;453}454455bool BitMap::intersects(const BitMap& other) const {456assert(size() == other.size(), "must have same size");457const bm_word_t* dest_map = map();458const bm_word_t* other_map = other.map();459idx_t limit = to_words_align_down(size());460for (idx_t index = 0; index < limit; ++index) {461if ((dest_map[index] & other_map[index]) != 0) return true;462}463idx_t rest = bit_in_word(size());464// false unless there is a partial-word tail with non-empty intersection.465return (rest > 0) && tail_of_map(dest_map[limit] & other_map[limit], rest) != 0;466}467468void BitMap::set_union(const BitMap& other) {469assert(size() == other.size(), "must have same size");470bm_word_t* dest_map = map();471const bm_word_t* other_map = other.map();472idx_t limit = to_words_align_down(size());473for (idx_t index = 0; index < limit; ++index) {474dest_map[index] |= other_map[index];475}476idx_t rest = bit_in_word(size());477if (rest > 0) {478bm_word_t orig = dest_map[limit];479dest_map[limit] = merge_tail_of_map(orig | other_map[limit], orig, rest);480}481}482483void BitMap::set_difference(const BitMap& other) {484assert(size() == other.size(), "must have same size");485bm_word_t* dest_map = map();486const bm_word_t* other_map = other.map();487idx_t limit = to_words_align_down(size());488for (idx_t index = 0; index < limit; ++index) {489dest_map[index] &= ~other_map[index];490}491idx_t rest = bit_in_word(size());492if (rest > 0) {493bm_word_t orig = dest_map[limit];494dest_map[limit] = merge_tail_of_map(orig & ~other_map[limit], orig, rest);495}496}497498void BitMap::set_intersection(const BitMap& other) {499assert(size() == other.size(), "must have same size");500bm_word_t* dest_map = map();501const bm_word_t* other_map = other.map();502idx_t limit = to_words_align_down(size());503for (idx_t index = 0; index < limit; ++index) {504dest_map[index] &= other_map[index];505}506idx_t rest = bit_in_word(size());507if (rest > 0) {508bm_word_t orig = dest_map[limit];509dest_map[limit] = merge_tail_of_map(orig & other_map[limit], orig, rest);510}511}512513bool BitMap::set_union_with_result(const BitMap& other) {514assert(size() == other.size(), "must have same size");515bool changed = false;516bm_word_t* dest_map = map();517const bm_word_t* other_map = other.map();518idx_t limit = to_words_align_down(size());519for (idx_t index = 0; index < limit; ++index) {520bm_word_t orig = dest_map[index];521bm_word_t temp = orig | other_map[index];522changed = changed || (temp != orig);523dest_map[index] = temp;524}525idx_t rest = bit_in_word(size());526if (rest > 0) {527bm_word_t orig = dest_map[limit];528bm_word_t temp = merge_tail_of_map(orig | other_map[limit], orig, rest);529changed = changed || (temp != orig);530dest_map[limit] = temp;531}532return changed;533}534535bool BitMap::set_difference_with_result(const BitMap& other) {536assert(size() == other.size(), "must have same size");537bool changed = false;538bm_word_t* dest_map = map();539const bm_word_t* other_map = other.map();540idx_t limit = to_words_align_down(size());541for (idx_t index = 0; index < limit; ++index) {542bm_word_t orig = dest_map[index];543bm_word_t temp = orig & ~other_map[index];544changed = changed || (temp != orig);545dest_map[index] = temp;546}547idx_t rest = bit_in_word(size());548if (rest > 0) {549bm_word_t orig = dest_map[limit];550bm_word_t temp = merge_tail_of_map(orig & ~other_map[limit], orig, rest);551changed = changed || (temp != orig);552dest_map[limit] = temp;553}554return changed;555}556557bool BitMap::set_intersection_with_result(const BitMap& other) {558assert(size() == other.size(), "must have same size");559bool changed = false;560bm_word_t* dest_map = map();561const bm_word_t* other_map = other.map();562idx_t limit = to_words_align_down(size());563for (idx_t index = 0; index < limit; ++index) {564bm_word_t orig = dest_map[index];565bm_word_t temp = orig & other_map[index];566changed = changed || (temp != orig);567dest_map[index] = temp;568}569idx_t rest = bit_in_word(size());570if (rest > 0) {571bm_word_t orig = dest_map[limit];572bm_word_t temp = merge_tail_of_map(orig & other_map[limit], orig, rest);573changed = changed || (temp != orig);574dest_map[limit] = temp;575}576return changed;577}578579void BitMap::set_from(const BitMap& other) {580assert(size() == other.size(), "must have same size");581bm_word_t* dest_map = map();582const bm_word_t* other_map = other.map();583idx_t copy_words = to_words_align_down(size());584Copy::disjoint_words((HeapWord*)other_map, (HeapWord*)dest_map, copy_words);585idx_t rest = bit_in_word(size());586if (rest > 0) {587dest_map[copy_words] = merge_tail_of_map(other_map[copy_words],588dest_map[copy_words],589rest);590}591}592593bool BitMap::is_same(const BitMap& other) const {594assert(size() == other.size(), "must have same size");595const bm_word_t* dest_map = map();596const bm_word_t* other_map = other.map();597idx_t limit = to_words_align_down(size());598for (idx_t index = 0; index < limit; ++index) {599if (dest_map[index] != other_map[index]) return false;600}601idx_t rest = bit_in_word(size());602return (rest == 0) || (tail_of_map(dest_map[limit] ^ other_map[limit], rest) == 0);603}604605bool BitMap::is_full() const {606const bm_word_t* words = map();607idx_t limit = to_words_align_down(size());608for (idx_t index = 0; index < limit; ++index) {609if (~words[index] != 0) return false;610}611idx_t rest = bit_in_word(size());612return (rest == 0) || (tail_of_map(~words[limit], rest) == 0);613}614615bool BitMap::is_empty() const {616const bm_word_t* words = map();617idx_t limit = to_words_align_down(size());618for (idx_t index = 0; index < limit; ++index) {619if (words[index] != 0) return false;620}621idx_t rest = bit_in_word(size());622return (rest == 0) || (tail_of_map(words[limit], rest) == 0);623}624625void BitMap::clear_large() {626clear_large_range_of_words(0, size_in_words());627}628629BitMap::idx_t BitMap::count_one_bits_in_range_of_words(idx_t beg_full_word, idx_t end_full_word) const {630idx_t sum = 0;631for (idx_t i = beg_full_word; i < end_full_word; i++) {632bm_word_t w = map()[i];633sum += population_count(w);634}635return sum;636}637638BitMap::idx_t BitMap::count_one_bits_within_word(idx_t beg, idx_t end) const {639if (beg != end) {640assert(end > beg, "must be");641bm_word_t mask = ~inverted_bit_mask_for_range(beg, end);642bm_word_t w = *word_addr(beg);643w &= mask;644return population_count(w);645}646return 0;647}648649BitMap::idx_t BitMap::count_one_bits() const {650return count_one_bits(0, size());651}652653// Returns the number of bits set within [beg, end).654BitMap::idx_t BitMap::count_one_bits(idx_t beg, idx_t end) const {655verify_range(beg, end);656657idx_t beg_full_word = to_words_align_up(beg);658idx_t end_full_word = to_words_align_down(end);659660idx_t sum = 0;661662if (beg_full_word < end_full_word) {663// The range includes at least one full word.664sum += count_one_bits_within_word(beg, bit_index(beg_full_word));665sum += count_one_bits_in_range_of_words(beg_full_word, end_full_word);666sum += count_one_bits_within_word(bit_index(end_full_word), end);667} else {668// The range spans at most 2 partial words.669idx_t boundary = MIN2(bit_index(beg_full_word), end);670sum += count_one_bits_within_word(beg, boundary);671sum += count_one_bits_within_word(boundary, end);672}673674assert(sum <= (beg - end), "must be");675676return sum;677678}679680void BitMap::print_on_error(outputStream* st, const char* prefix) const {681st->print_cr("%s[" PTR_FORMAT ", " PTR_FORMAT ")",682prefix, p2i(map()), p2i((char*)map() + (size() >> LogBitsPerByte)));683}684685void BitMap::write_to(bm_word_t* buffer, size_t buffer_size_in_bytes) const {686assert(buffer_size_in_bytes == size_in_bytes(), "must be");687memcpy(buffer, _map, size_in_bytes());688}689690#ifndef PRODUCT691692void BitMap::print_on(outputStream* st) const {693tty->print("Bitmap(" SIZE_FORMAT "):", size());694for (idx_t index = 0; index < size(); index++) {695tty->print("%c", at(index) ? '1' : '0');696}697tty->cr();698}699700#endif701702703