tor-browser

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

ZoneShim.h (13959B)


      1 // Copyright 2019 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef V8_UTIL_ZONE_H_
      6 #define V8_UTIL_ZONE_H_
      7 
      8 #include <list>
      9 #include <map>
     10 #include <set>
     11 #include <unordered_map>
     12 #include <vector>
     13 
     14 #include "ds/LifoAlloc.h"
     15 #include "ds/Sort.h"
     16 #include "irregexp/util/VectorShim.h"
     17 
     18 namespace v8 {
     19 namespace internal {
     20 
     21 // V8::Zone ~= LifoAlloc
     22 class Zone {
     23 public:
     24  Zone(js::LifoAlloc& alloc) : lifoAlloc_(alloc) {}
     25 
     26  template <typename T, typename... Args>
     27  T* New(Args&&... args) {
     28    js::LifoAlloc::AutoFallibleScope fallible(&lifoAlloc_);
     29    js::AutoEnterOOMUnsafeRegion oomUnsafe;
     30    void* memory = lifoAlloc_.alloc(sizeof(T));
     31    if (!memory) {
     32      oomUnsafe.crash("Irregexp Zone::New");
     33    }
     34    return new (memory) T(std::forward<Args>(args)...);
     35  }
     36 
     37  // Allocates uninitialized memory for 'length' number of T instances.
     38  template <typename T>
     39  T* NewArray(size_t length) {
     40    js::LifoAlloc::AutoFallibleScope fallible(&lifoAlloc_);
     41    js::AutoEnterOOMUnsafeRegion oomUnsafe;
     42    size_t numBytes = length * sizeof(T);
     43    if (MOZ_UNLIKELY(numBytes > INT_MAX)) {
     44      oomUnsafe.crash("Irregexp Zone::New");
     45    }
     46    void* memory = lifoAlloc_.alloc(length * sizeof(T));
     47    if (MOZ_UNLIKELY(!memory)) {
     48      oomUnsafe.crash("Irregexp Zone::New");
     49    }
     50    return static_cast<T*>(memory);
     51  }
     52 
     53  void DeleteAll() { lifoAlloc_.freeAll(); }
     54 
     55  // Returns true if the total memory allocated exceeds a threshold.
     56  static const size_t kExcessLimit = 256 * 1024 * 1024;
     57  bool excess_allocation() const {
     58    return lifoAlloc_.computedSizeOfExcludingThis() > kExcessLimit;
     59  }
     60 
     61  js::LifoAlloc& inner() { return lifoAlloc_; }
     62 
     63 private:
     64  js::LifoAlloc& lifoAlloc_;
     65 };
     66 
     67 // Superclass for classes allocated in a Zone.
     68 // Based on: https://github.com/v8/v8/blob/master/src/zone/zone.h
     69 class ZoneObject {
     70 public:
     71  // new (zone) SomeObject(...) was the old pattern.
     72  // Delete the constructor to avoid using it accidentally.
     73  void* operator new(size_t size, Zone* zone) = delete;
     74 
     75  // Allow non-allocating placement new
     76  void* operator new(size_t size, void* ptr) { return ptr; }
     77 
     78  // Ideally, the delete operator should be private instead of
     79  // public, but unfortunately the compiler sometimes synthesizes
     80  // (unused) destructors for classes derived from ZoneObject, which
     81  // require the operator to be visible. MSVC requires the delete
     82  // operator to be public.
     83 
     84  // ZoneObjects should never be deleted individually; use
     85  // Zone::DeleteAll() to delete all zone objects in one go.
     86  void operator delete(void*, size_t) { MOZ_CRASH("unreachable"); }
     87  void operator delete(void* pointer, Zone* zone) { MOZ_CRASH("unreachable"); }
     88 };
     89 
     90 // ZoneLists are growable lists with constant-time access to the
     91 // elements. The list itself and all its elements are allocated in the
     92 // Zone. ZoneLists cannot be deleted individually; you can delete all
     93 // objects in the Zone by calling Zone::DeleteAll().
     94 // Used throughout irregexp.
     95 // Based on: https://github.com/v8/v8/blob/master/src/zone/zone-list.h
     96 template <typename T>
     97 class ZoneList final : public ZoneObject {
     98 public:
     99  // Construct a new ZoneList with the given capacity; the length is
    100  // always zero. The capacity must be non-negative.
    101  ZoneList(int capacity, Zone* zone) : capacity_(capacity) {
    102    data_ = (capacity_ > 0) ? zone->NewArray<T>(capacity_) : nullptr;
    103  }
    104  // Construct a new ZoneList by copying the elements of the given ZoneList.
    105  ZoneList(const ZoneList<T>& other, Zone* zone)
    106      : ZoneList(other.length(), zone) {
    107    AddAll(other, zone);
    108  }
    109 
    110  // Construct a new ZoneList by copying the elements of the given vector.
    111  ZoneList(const base::Vector<const T>& other, Zone* zone)
    112      : ZoneList(other.length(), zone) {
    113    AddAll(other, zone);
    114  }
    115 
    116  ZoneList(ZoneList<T>&& other) { *this = std::move(other); }
    117 
    118  ZoneList& operator=(ZoneList&& other) {
    119    MOZ_ASSERT(!data_);
    120    data_ = other.data_;
    121    capacity_ = other.capacity_;
    122    length_ = other.length_;
    123    other.Clear();
    124    return *this;
    125  }
    126 
    127  // Returns a reference to the element at index i. This reference is not safe
    128  // to use after operations that can change the list's backing store
    129  // (e.g. Add).
    130  inline T& operator[](int i) const {
    131    MOZ_ASSERT(i >= 0);
    132    MOZ_ASSERT(static_cast<unsigned>(i) < static_cast<unsigned>(length_));
    133    return data_[i];
    134  }
    135  inline T& at(int i) const { return operator[](i); }
    136  inline T& last() const { return at(length_ - 1); }
    137  inline T& first() const { return at(0); }
    138 
    139  using iterator = T*;
    140  inline iterator begin() const { return &data_[0]; }
    141  inline iterator end() const { return &data_[length_]; }
    142 
    143  inline bool is_empty() const { return length_ == 0; }
    144  inline int length() const { return length_; }
    145  inline int capacity() const { return capacity_; }
    146 
    147  base::Vector<T> ToVector() const { return base::Vector<T>(data_, length_); }
    148  base::Vector<T> ToVector(int start, int length) const {
    149    return base::Vector<T>(data_ + start, std::min(length_ - start, length));
    150  }
    151 
    152  base::Vector<const T> ToConstVector() const {
    153    return base::Vector<const T>(data_, length_);
    154  }
    155 
    156  // Adds a copy of the given 'element' to the end of the list,
    157  // expanding the list if necessary.
    158  void Add(const T& element, Zone* zone) {
    159    if (length_ < capacity_) {
    160      data_[length_++] = element;
    161    } else {
    162      ZoneList<T>::ResizeAdd(element, zone);
    163    }
    164  }
    165  // Add all the elements from the argument list to this list.
    166  void AddAll(const ZoneList<T>& other, Zone* zone) {
    167    AddAll(other.ToVector(), zone);
    168  }
    169  // Add all the elements from the vector to this list.
    170  void AddAll(const base::Vector<const T>& other, Zone* zone) {
    171    int result_length = length_ + other.length();
    172    if (capacity_ < result_length) {
    173      Resize(result_length, zone);
    174    }
    175    if (std::is_fundamental<T>()) {
    176      memcpy(data_ + length_, other.begin(), sizeof(*data_) * other.length());
    177    } else {
    178      for (int i = 0; i < other.length(); i++) {
    179        data_[length_ + i] = other.at(i);
    180      }
    181    }
    182    length_ = result_length;
    183  }
    184 
    185  // Overwrites the element at the specific index.
    186  void Set(int index, const T& element) {
    187    MOZ_ASSERT(index >= 0 && index <= length_);
    188    data_[index] = element;
    189  }
    190 
    191  // Removes the i'th element without deleting it even if T is a
    192  // pointer type; moves all elements above i "down". Returns the
    193  // removed element.  This function's complexity is linear in the
    194  // size of the list.
    195  T Remove(int i) {
    196    T element = at(i);
    197    length_--;
    198    while (i < length_) {
    199      data_[i] = data_[i + 1];
    200      i++;
    201    }
    202    return element;
    203  }
    204 
    205  // Removes the last element without deleting it even if T is a
    206  // pointer type. Returns the removed element.
    207  inline T RemoveLast() { return Remove(length_ - 1); }
    208 
    209  // Clears the list, setting the capacity and length to 0.
    210  inline void Clear() {
    211    data_ = nullptr;
    212    capacity_ = 0;
    213    length_ = 0;
    214  }
    215 
    216  // Drops all but the first 'pos' elements from the list.
    217  inline void Rewind(int pos) {
    218    MOZ_ASSERT(0 <= pos && pos <= length_);
    219    length_ = pos;
    220  }
    221 
    222  inline bool Contains(const T& elm) const {
    223    for (int i = 0; i < length_; i++) {
    224      if (data_[i] == elm) return true;
    225    }
    226    return false;
    227  }
    228 
    229  template <typename CompareFunction>
    230  void StableSort(CompareFunction cmp, size_t start, size_t length) {
    231    js::AutoEnterOOMUnsafeRegion oomUnsafe;
    232    T* scratch = static_cast<T*>(js_malloc(length * sizeof(T)));
    233    if (!scratch) {
    234      oomUnsafe.crash("Irregexp stable sort scratch space");
    235    }
    236    auto comparator = [cmp](const T& a, const T& b, bool* lessOrEqual) {
    237      *lessOrEqual = cmp(&a, &b) <= 0;
    238      return true;
    239    };
    240    MOZ_ALWAYS_TRUE(
    241        js::MergeSort(begin() + start, length, scratch, comparator));
    242    js_free(scratch);
    243  }
    244 
    245  void operator delete(void* pointer) { MOZ_CRASH("unreachable"); }
    246  void operator delete(void* pointer, Zone* zone) { MOZ_CRASH("unreachable"); }
    247 
    248 private:
    249  T* data_ = nullptr;
    250  int capacity_ = 0;
    251  int length_ = 0;
    252 
    253  // Increase the capacity of a full list, and add an element.
    254  // List must be full already.
    255  void ResizeAdd(const T& element, Zone* zone) {
    256    MOZ_ASSERT(length_ >= capacity_);
    257    // Grow the list capacity by 100%, but make sure to let it grow
    258    // even when the capacity is zero (possible initial case).
    259    int new_capacity = 1 + 2 * capacity_;
    260    // Since the element reference could be an element of the list, copy
    261    // it out of the old backing storage before resizing.
    262    T temp = element;
    263    Resize(new_capacity, zone);
    264    data_[length_++] = temp;
    265  }
    266 
    267  // Resize the list.
    268  void Resize(int new_capacity, Zone* zone) {
    269    MOZ_ASSERT(length_ <= new_capacity);
    270    static_assert(std::is_trivially_copyable<T>::value);
    271    T* new_data = zone->NewArray<T>(new_capacity);
    272    if (length_ > 0) {
    273      memcpy(new_data, data_, length_ * sizeof(T));
    274    }
    275    data_ = new_data;
    276    capacity_ = new_capacity;
    277  }
    278 
    279  ZoneList& operator=(const ZoneList&) = delete;
    280  ZoneList() = delete;
    281  ZoneList(const ZoneList&) = delete;
    282 };
    283 
    284 // Based on: https://github.com/v8/v8/blob/master/src/zone/zone-allocator.h
    285 template <typename T>
    286 class ZoneAllocator {
    287 public:
    288  using pointer = T*;
    289  using const_pointer = const T*;
    290  using reference = T&;
    291  using const_reference = const T&;
    292  using value_type = T;
    293  using size_type = size_t;
    294  using difference_type = ptrdiff_t;
    295  template <class O>
    296  struct rebind {
    297    using other = ZoneAllocator<O>;
    298  };
    299 
    300  explicit ZoneAllocator(Zone* zone) : zone_(zone) {}
    301  template <typename U>
    302  ZoneAllocator(const ZoneAllocator<U>& other)
    303      : ZoneAllocator<T>(other.zone_) {}
    304  template <typename U>
    305  friend class ZoneAllocator;
    306 
    307  T* allocate(size_t n) { return zone_->NewArray<T>(n); }
    308  void deallocate(T* p, size_t) {}  // noop for zones
    309 
    310  bool operator==(ZoneAllocator const& other) const {
    311    return zone_ == other.zone_;
    312  }
    313  bool operator!=(ZoneAllocator const& other) const {
    314    return zone_ != other.zone_;
    315  }
    316 
    317  using Policy = js::LifoAllocPolicy<js::Fallible>;
    318  Policy policy() const {
    319    return js::LifoAllocPolicy<js::Fallible>(zone_->inner());
    320  }
    321 
    322 private:
    323  Zone* zone_;
    324 };
    325 
    326 // Zone wrappers for std containers:
    327 // Origin:
    328 // https://github.com/v8/v8/blob/5e514a969376dc63517d575b062758efd36cd757/src/zone/zone-containers.h#L25-L169
    329 
    330 // A wrapper subclass for std::vector to make it easy to construct one
    331 // that uses a zone allocator.
    332 // Used throughout irregexp
    333 template <typename T>
    334 class ZoneVector : public std::vector<T, ZoneAllocator<T>> {
    335 public:
    336  ZoneVector(Zone* zone)
    337      : std::vector<T, ZoneAllocator<T>>(ZoneAllocator<T>(zone)) {}
    338 
    339  // Constructs a new vector and fills it with {size} elements, each
    340  // constructed via the default constructor.
    341  ZoneVector(size_t size, Zone* zone)
    342      : std::vector<T, ZoneAllocator<T>>(size, T(), ZoneAllocator<T>(zone)) {}
    343 
    344  // Constructs a new vector and fills it with the contents of the range
    345  // [first, last).
    346  template <class Iter>
    347  ZoneVector(Iter first, Iter last, Zone* zone)
    348      : std::vector<T, ZoneAllocator<T>>(first, last, ZoneAllocator<T>(zone)) {}
    349 };
    350 
    351 // A wrapper subclass for std::list to make it easy to construct one
    352 // that uses a zone allocator.
    353 // Used in regexp-bytecode-peephole.cc
    354 template <typename T>
    355 class ZoneLinkedList : public std::list<T, ZoneAllocator<T>> {
    356 public:
    357  // Constructs an empty list.
    358  explicit ZoneLinkedList(Zone* zone)
    359      : std::list<T, ZoneAllocator<T>>(ZoneAllocator<T>(zone)) {}
    360 };
    361 
    362 // A wrapper subclass for std::set to make it easy to construct one that uses
    363 // a zone allocator.
    364 // Used in regexp-parser.cc
    365 template <typename K, typename Compare = std::less<K>>
    366 class ZoneSet : public std::set<K, Compare, ZoneAllocator<K>> {
    367 public:
    368  // Constructs an empty set.
    369  explicit ZoneSet(Zone* zone)
    370      : std::set<K, Compare, ZoneAllocator<K>>(Compare(),
    371                                               ZoneAllocator<K>(zone)) {}
    372 };
    373 
    374 // A wrapper subclass for std::map to make it easy to construct one that uses
    375 // a zone allocator.
    376 // Used in regexp-bytecode-peephole.cc
    377 template <typename K, typename V, typename Compare = std::less<K>>
    378 class ZoneMap
    379    : public std::map<K, V, Compare, ZoneAllocator<std::pair<const K, V>>> {
    380 public:
    381  // Constructs an empty map.
    382  explicit ZoneMap(Zone* zone)
    383      : std::map<K, V, Compare, ZoneAllocator<std::pair<const K, V>>>(
    384            Compare(), ZoneAllocator<std::pair<const K, V>>(zone)) {}
    385 };
    386 
    387 // A wrapper subclass for std::unordered_map to make it easy to construct one
    388 // that uses a zone allocator.
    389 // Used in regexp-bytecode-peephole.cc
    390 template <typename K, typename V, typename Hash = std::hash<K>,
    391          typename KeyEqual = std::equal_to<K>>
    392 class ZoneUnorderedMap
    393    : public std::unordered_map<K, V, Hash, KeyEqual,
    394                                ZoneAllocator<std::pair<const K, V>>> {
    395 public:
    396  // Constructs an empty map.
    397  explicit ZoneUnorderedMap(Zone* zone, size_t bucket_count = 100)
    398      : std::unordered_map<K, V, Hash, KeyEqual,
    399                           ZoneAllocator<std::pair<const K, V>>>(
    400            bucket_count, Hash(), KeyEqual(),
    401            ZoneAllocator<std::pair<const K, V>>(zone)) {}
    402 };
    403 
    404 // A wrapper subclass for base::SmallVector to make it easy to construct one
    405 // that uses a zone allocator.
    406 template <typename T, size_t kSize>
    407 class SmallZoneVector : public base::SmallVector<T, kSize, ZoneAllocator<T>> {
    408 public:
    409  // Constructs an empty small vector.
    410  explicit SmallZoneVector(Zone* zone)
    411      : base::SmallVector<T, kSize, ZoneAllocator<T>>(ZoneAllocator<T>(zone)) {}
    412 
    413  explicit SmallZoneVector(size_t size, Zone* zone)
    414      : base::SmallVector<T, kSize, ZoneAllocator<T>>(
    415            size, ZoneAllocator<T>(ZoneAllocator<T>(zone))) {}
    416 };
    417 
    418 }  // namespace internal
    419 }  // namespace v8
    420 
    421 #endif  // V8_UTIL_FLAG_H_