tor-browser

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

padded_bytes.h (6302B)


      1 // Copyright (c) the JPEG XL Project Authors. All rights reserved.
      2 //
      3 // Use of this source code is governed by a BSD-style
      4 // license that can be found in the LICENSE file.
      5 
      6 #ifndef LIB_JXL_BASE_PADDED_BYTES_H_
      7 #define LIB_JXL_BASE_PADDED_BYTES_H_
      8 
      9 // std::vector replacement with padding to reduce bounds checks in WriteBits
     10 
     11 #include <jxl/memory_manager.h>
     12 
     13 #include <algorithm>  // max
     14 #include <cstddef>
     15 #include <cstdint>
     16 #include <cstring>  // memcpy
     17 #include <initializer_list>
     18 #include <utility>  // swap
     19 
     20 #include "lib/jxl/base/status.h"
     21 #include "lib/jxl/memory_manager_internal.h"
     22 
     23 namespace jxl {
     24 
     25 // Provides a subset of the std::vector interface with some differences:
     26 // - allows BitWriter to write 64 bits at a time without bounds checking;
     27 // - ONLY zero-initializes the first byte (required by BitWriter);
     28 // - ensures cache-line alignment.
     29 class PaddedBytes {
     30 public:
     31  // Required for output params.
     32  explicit PaddedBytes(JxlMemoryManager* memory_manager)
     33      : memory_manager_(memory_manager), size_(0), capacity_(0) {}
     34 
     35  static StatusOr<PaddedBytes> WithInitialSpace(
     36      JxlMemoryManager* memory_manager, size_t size) {
     37    PaddedBytes result(memory_manager);
     38    JXL_RETURN_IF_ERROR(result.Init(size));
     39    return result;
     40  }
     41 
     42  // Deleting copy constructor and copy assignment operator to prevent copying
     43  PaddedBytes(const PaddedBytes&) = delete;
     44  PaddedBytes& operator=(const PaddedBytes&) = delete;
     45 
     46  // default is not OK - need to set other.size_ to 0!
     47  PaddedBytes(PaddedBytes&& other) noexcept
     48      : memory_manager_(other.memory_manager_),
     49        size_(other.size_),
     50        capacity_(other.capacity_),
     51        data_(std::move(other.data_)) {
     52    other.size_ = other.capacity_ = 0;
     53  }
     54  PaddedBytes& operator=(PaddedBytes&& other) noexcept {
     55    memory_manager_ = other.memory_manager_;
     56    size_ = other.size_;
     57    capacity_ = other.capacity_;
     58    data_ = std::move(other.data_);
     59 
     60    if (&other != this) {
     61      other.size_ = other.capacity_ = 0;
     62    }
     63    return *this;
     64  }
     65 
     66  JxlMemoryManager* memory_manager() const { return memory_manager_; }
     67 
     68  void swap(PaddedBytes& other) noexcept {
     69    std::swap(memory_manager_, other.memory_manager_);
     70    std::swap(size_, other.size_);
     71    std::swap(capacity_, other.capacity_);
     72    std::swap(data_, other.data_);
     73  }
     74 
     75  // If current capacity is greater than requested, then no-op. Otherwise
     76  // copies existing data to newly allocated "data_".
     77  // The new capacity will be at least 1.5 times the old capacity. This ensures
     78  // that we avoid quadratic behaviour.
     79  Status reserve(size_t capacity) {
     80    if (capacity <= capacity_) return true;
     81 
     82    size_t new_capacity = std::max(capacity, 3 * capacity_ / 2);
     83    new_capacity = std::max<size_t>(64, new_capacity);
     84 
     85    // BitWriter writes up to 7 bytes past the end.
     86    JXL_ASSIGN_OR_RETURN(
     87        AlignedMemory new_data,
     88        AlignedMemory::Create(memory_manager_, new_capacity + 8));
     89 
     90    if (data_.address<void>() == nullptr) {
     91      // First allocation: ensure first byte is initialized (won't be copied).
     92      new_data.address<uint8_t>()[0] = 0;
     93    } else {
     94      // Subsequent resize: copy existing data to new location.
     95      memmove(new_data.address<void>(), data_.address<void>(), size_);
     96      // Ensure that the first new byte is initialized, to allow write_bits to
     97      // safely append to the newly-resized PaddedBytes.
     98      new_data.address<uint8_t>()[size_] = 0;
     99    }
    100 
    101    capacity_ = new_capacity;
    102    data_ = std::move(new_data);
    103    return true;
    104  }
    105 
    106  // NOTE: unlike vector, this does not initialize the new data!
    107  // However, we guarantee that write_bits can safely append after
    108  // the resize, as we zero-initialize the first new byte of data.
    109  // If size < capacity(), does not invalidate the memory.
    110  Status resize(size_t size) {
    111    JXL_RETURN_IF_ERROR(reserve(size));
    112    size_ = size;
    113    return true;
    114  }
    115 
    116  // resize(size) plus explicit initialization of the new data with `value`.
    117  Status resize(size_t size, uint8_t value) {
    118    size_t old_size = size_;
    119    JXL_RETURN_IF_ERROR(resize(size));
    120    if (size_ > old_size) {
    121      memset(data() + old_size, value, size_ - old_size);
    122    }
    123    return true;
    124  }
    125 
    126  // Amortized constant complexity due to exponential growth.
    127  Status push_back(uint8_t x) {
    128    if (size_ == capacity_) {
    129      JXL_RETURN_IF_ERROR(reserve(capacity_ + 1));
    130    }
    131 
    132    data_.address<uint8_t>()[size_++] = x;
    133    return true;
    134  }
    135 
    136  size_t size() const { return size_; }
    137  size_t capacity() const { return capacity_; }
    138 
    139  uint8_t* data() { return data_.address<uint8_t>(); }
    140  const uint8_t* data() const { return data_.address<uint8_t>(); }
    141 
    142  // std::vector operations implemented in terms of the public interface above.
    143 
    144  void clear() {
    145    // Not passing on the Status, because resizing to 0 cannot fail.
    146    static_cast<void>(resize(0));
    147  }
    148  bool empty() const { return size() == 0; }
    149 
    150  Status assign(std::initializer_list<uint8_t> il) {
    151    JXL_RETURN_IF_ERROR(resize(il.size()));
    152    memcpy(data(), il.begin(), il.size());
    153    return true;
    154  }
    155 
    156  uint8_t* begin() { return data(); }
    157  const uint8_t* begin() const { return data(); }
    158  uint8_t* end() { return begin() + size(); }
    159  const uint8_t* end() const { return begin() + size(); }
    160 
    161  uint8_t& operator[](const size_t i) {
    162    BoundsCheck(i);
    163    return data()[i];
    164  }
    165  const uint8_t& operator[](const size_t i) const {
    166    BoundsCheck(i);
    167    return data()[i];
    168  }
    169 
    170  template <typename T>
    171  Status append(const T& other) {
    172    return append(
    173        reinterpret_cast<const uint8_t*>(other.data()),
    174        reinterpret_cast<const uint8_t*>(other.data()) + other.size());
    175  }
    176 
    177  Status append(const uint8_t* begin, const uint8_t* end) {
    178    if (end - begin > 0) {
    179      size_t old_size = size();
    180      JXL_RETURN_IF_ERROR(resize(size() + (end - begin)));
    181      memcpy(data() + old_size, begin, end - begin);
    182    }
    183    return true;
    184  }
    185 
    186 private:
    187  Status Init(size_t size) {
    188    size_ = size;
    189    return reserve(size);
    190  }
    191 
    192  void BoundsCheck(size_t i) const {
    193    // <= is safe due to padding and required by BitWriter.
    194    JXL_DASSERT(i <= size());
    195  }
    196 
    197  JxlMemoryManager* memory_manager_;
    198  size_t size_;
    199  size_t capacity_;
    200  AlignedMemory data_;
    201 };
    202 
    203 }  // namespace jxl
    204 
    205 #endif  // LIB_JXL_BASE_PADDED_BYTES_H_