tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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