Bitmap.h (7172B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef ds_Bitmap_h 8 #define ds_Bitmap_h 9 10 #include "mozilla/Array.h" 11 #include "mozilla/Assertions.h" 12 #include "mozilla/Attributes.h" 13 14 #include <algorithm> 15 #include <stddef.h> 16 #include <stdint.h> 17 18 #include "js/AllocPolicy.h" 19 #include "js/HashTable.h" 20 #include "js/HeapAPI.h" 21 #include "js/Vector.h" 22 23 // This file provides two classes for representing bitmaps. 24 // 25 // DenseBitmap is an array of words of bits, with size linear in the maximum 26 // bit which has been set on it. 27 // 28 // SparseBitmap provides a reasonably simple, reasonably efficient (in time and 29 // space) implementation of a sparse bitmap. The basic representation is a hash 30 // table whose entries are fixed length malloc'ed blocks of bits. 31 32 namespace js { 33 34 class DenseBitmap { 35 using Data = Vector<uintptr_t, 0, SystemAllocPolicy>; 36 37 Data data; 38 39 public: 40 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) { 41 return data.sizeOfExcludingThis(mallocSizeOf); 42 } 43 44 bool ensureSpace(size_t numWords) { 45 MOZ_ASSERT(data.empty()); 46 return data.appendN(0, numWords); 47 } 48 49 size_t count() const { return numWords() * JS_BITS_PER_WORD; } 50 51 size_t numWords() const { return data.length(); } 52 uintptr_t word(size_t i) const { return data[i]; } 53 uintptr_t& word(size_t i) { return data[i]; } 54 55 bool getBit(size_t bit) const { 56 return word(bit / JS_BITS_PER_WORD) & 57 (uintptr_t(1) << (bit % JS_BITS_PER_WORD)); 58 } 59 60 template <typename T> 61 typename std::enable_if_t<std::is_convertible_v<T, uintptr_t>, void> 62 copyBitsFrom(size_t wordStart, size_t numWords, T* source) { 63 MOZ_ASSERT(wordStart + numWords <= data.length()); 64 for (size_t i = 0; i < numWords; i++) { 65 data[wordStart + i] = source[i]; 66 } 67 } 68 69 template <typename T> 70 typename std::enable_if_t<std::is_convertible_v<T, uintptr_t>, void> 71 bitwiseOrRangeInto(size_t wordStart, size_t numWords, T* target) const { 72 if (wordStart >= data.length()) { 73 return; 74 } 75 76 // Does not support copying partial blocks. 77 MOZ_ASSERT(wordStart + numWords <= data.length()); 78 79 for (size_t i = 0; i < numWords; i++) { 80 target[i] |= data[wordStart + i]; 81 } 82 } 83 84 template <typename F> 85 void forEachWord(size_t wordStart, size_t numWords, F&& func) { 86 MOZ_ASSERT(wordStart + numWords <= data.length()); 87 for (size_t i = 0; i < numWords; i++) { 88 func(data[wordStart + i]); 89 } 90 } 91 }; 92 93 class SparseBitmap { 94 // The number of words of bits to use for each block mainly affects the 95 // memory usage of the bitmap. To minimize overhead, bitmaps which are 96 // expected to be fairly dense should have a large block size, and bitmaps 97 // which are expected to be very sparse should have a small block size. 98 static const size_t WordsInBlock = 4096 / sizeof(uintptr_t); 99 100 using BitBlock = mozilla::Array<uintptr_t, WordsInBlock>; 101 using Data = 102 HashMap<size_t, BitBlock*, DefaultHasher<size_t>, SystemAllocPolicy>; 103 104 Data data; 105 106 MOZ_ALWAYS_INLINE static size_t blockStartWord(size_t word) { 107 return word & ~(WordsInBlock - 1); 108 } 109 110 MOZ_ALWAYS_INLINE static uintptr_t bitMask(size_t bit) { 111 return uintptr_t(1) << (bit % JS_BITS_PER_WORD); 112 } 113 114 // Return the number of words in a BitBlock starting at |blockWord| which 115 // are in |other|. 116 static size_t wordIntersectCount(size_t blockWord, const DenseBitmap& other) { 117 long count = other.numWords() - blockWord; 118 return static_cast<size_t>(std::clamp(count, 0l, (long)WordsInBlock)); 119 } 120 121 BitBlock* createBlock(Data::AddPtr p, size_t blockId); 122 123 MOZ_ALWAYS_INLINE BitBlock* getBlock(size_t blockId) const { 124 Data::Ptr p = data.lookup(blockId); 125 return p ? p->value() : nullptr; 126 } 127 128 MOZ_ALWAYS_INLINE BitBlock* readonlyThreadsafeGetBlock(size_t blockId) const { 129 Data::Ptr p = data.readonlyThreadsafeLookup(blockId); 130 return p ? p->value() : nullptr; 131 } 132 133 MOZ_ALWAYS_INLINE BitBlock* getOrCreateBlock(size_t blockId) { 134 Data::AddPtr p = data.lookupForAdd(blockId); 135 if (p) { 136 return p->value(); 137 } 138 return createBlock(p, blockId); 139 } 140 141 public: 142 ~SparseBitmap(); 143 144 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf); 145 146 [[nodiscard]] MOZ_ALWAYS_INLINE bool setBit(size_t bit) { 147 size_t word = bit / JS_BITS_PER_WORD; 148 size_t blockWord = blockStartWord(word); 149 BitBlock* block = getOrCreateBlock(blockWord / WordsInBlock); 150 if (!block) { 151 return false; 152 } 153 (*block)[word - blockWord] |= bitMask(bit); 154 return true; 155 } 156 157 void atomicSetExistingBit(size_t bit) { 158 size_t word = bit / JS_BITS_PER_WORD; 159 size_t blockWord = blockStartWord(word); 160 BitBlock* block = readonlyThreadsafeGetBlock(blockWord / WordsInBlock); 161 MOZ_ASSERT(block); 162 uintptr_t* ptr = &(*block)[word - blockWord]; 163 __atomic_fetch_or(ptr, bitMask(bit), __ATOMIC_RELAXED); 164 } 165 166 bool getBit(size_t bit) const; 167 bool readonlyThreadsafeGetBit(size_t bit) const; 168 169 void bitwiseAndWith(const DenseBitmap& other); 170 [[nodiscard]] bool bitwiseOrWith(const SparseBitmap& other); 171 void bitwiseOrInto(DenseBitmap& other) const; 172 173 // Currently, the following APIs only supports a range of words that is in a 174 // single bit block. 175 176 template <typename T> 177 typename std::enable_if_t<std::is_convertible_v<T, uintptr_t>, void> 178 bitwiseAndRangeWith(size_t wordStart, size_t numWords, T* source) { 179 size_t blockWord = blockStartWord(wordStart); 180 181 // We only support using a single bit block in this API. 182 MOZ_ASSERT(numWords && 183 (blockWord == blockStartWord(wordStart + numWords - 1))); 184 185 BitBlock* block = getBlock(blockWord / WordsInBlock); 186 if (block) { 187 for (size_t i = 0; i < numWords; i++) { 188 (*block)[wordStart - blockWord + i] &= source[i]; 189 } 190 } 191 } 192 193 template <typename T> 194 typename std::enable_if_t<std::is_convertible_v<T, uintptr_t>, void> 195 bitwiseOrRangeInto(size_t wordStart, size_t numWords, T* target) const { 196 size_t blockWord = blockStartWord(wordStart); 197 198 // We only support using a single bit block in this API. 199 MOZ_ASSERT(numWords && 200 (blockWord == blockStartWord(wordStart + numWords - 1))); 201 202 BitBlock* block = getBlock(blockWord / WordsInBlock); 203 if (block) { 204 for (size_t i = 0; i < numWords; i++) { 205 target[i] |= (*block)[wordStart - blockWord + i]; 206 } 207 } 208 } 209 210 template <typename F> 211 void forEachWord(size_t wordStart, size_t numWords, F&& func) { 212 size_t blockWord = blockStartWord(wordStart); 213 214 // We only support using a single bit block in this API. 215 MOZ_ASSERT(numWords && 216 (blockWord == blockStartWord(wordStart + numWords - 1))); 217 218 BitBlock* block = getBlock(blockWord / WordsInBlock); 219 if (block) { 220 for (size_t i = 0; i < numWords; i++) { 221 func((*block)[wordStart - blockWord + i]); 222 } 223 } 224 } 225 }; 226 227 } // namespace js 228 229 #endif // ds_Bitmap_h