libjxl

FORK: libjxl patches used on blog
git clone https://git.neptards.moe/blog/libjxl.git
Log | Files | Refs | Submodules | README | LICENSE

padded_bytes.h (6338B)


      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 <stddef.h>
     12 #include <stdint.h>
     13 #include <string.h>  // memcpy
     14 
     15 #include <algorithm>  // max
     16 #include <initializer_list>
     17 #include <utility>  // swap
     18 
     19 #include "lib/jxl/base/compiler_specific.h"
     20 #include "lib/jxl/base/status.h"
     21 #include "lib/jxl/cache_aligned.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   PaddedBytes() : size_(0), capacity_(0) {}
     33 
     34   explicit PaddedBytes(size_t size) : size_(size), capacity_(0) {
     35     reserve(size);
     36   }
     37 
     38   PaddedBytes(size_t size, uint8_t value) : size_(size), capacity_(0) {
     39     reserve(size);
     40     if (size_ != 0) {
     41       memset(data(), value, size);
     42     }
     43   }
     44 
     45   PaddedBytes(const PaddedBytes& other) : size_(other.size_), capacity_(0) {
     46     reserve(size_);
     47     if (data() != nullptr) memcpy(data(), other.data(), size_);
     48   }
     49   PaddedBytes& operator=(const PaddedBytes& other) {
     50     // Self-assignment is safe.
     51     resize(other.size());
     52     if (data() != nullptr) memmove(data(), other.data(), size_);
     53     return *this;
     54   }
     55 
     56   // default is not OK - need to set other.size_ to 0!
     57   PaddedBytes(PaddedBytes&& other) noexcept
     58       : size_(other.size_),
     59         capacity_(other.capacity_),
     60         data_(std::move(other.data_)) {
     61     other.size_ = other.capacity_ = 0;
     62   }
     63   PaddedBytes& operator=(PaddedBytes&& other) noexcept {
     64     size_ = other.size_;
     65     capacity_ = other.capacity_;
     66     data_ = std::move(other.data_);
     67 
     68     if (&other != this) {
     69       other.size_ = other.capacity_ = 0;
     70     }
     71     return *this;
     72   }
     73 
     74   void swap(PaddedBytes& other) noexcept {
     75     std::swap(size_, other.size_);
     76     std::swap(capacity_, other.capacity_);
     77     std::swap(data_, other.data_);
     78   }
     79 
     80   // If current capacity is greater than requested, then no-op. Otherwise
     81   // copies existing data to newly allocated "data_". If allocation fails,
     82   // data() == nullptr and size_ = capacity_ = 0.
     83   // The new capacity will be at least 1.5 times the old capacity. This ensures
     84   // that we avoid quadratic behaviour.
     85   void reserve(size_t capacity) {
     86     if (capacity <= capacity_) return;
     87 
     88     size_t new_capacity = std::max(capacity, 3 * capacity_ / 2);
     89     new_capacity = std::max<size_t>(64, new_capacity);
     90 
     91     // BitWriter writes up to 7 bytes past the end.
     92     CacheAlignedUniquePtr new_data = AllocateArray(new_capacity + 8);
     93     if (new_data == nullptr) {
     94       // Allocation failed, discard all data to ensure this is noticed.
     95       size_ = capacity_ = 0;
     96       return;
     97     }
     98 
     99     if (data_ == nullptr) {
    100       // First allocation: ensure first byte is initialized (won't be copied).
    101       new_data[0] = 0;
    102     } else {
    103       // Subsequent resize: copy existing data to new location.
    104       memcpy(new_data.get(), data_.get(), size_);
    105       // Ensure that the first new byte is initialized, to allow write_bits to
    106       // safely append to the newly-resized PaddedBytes.
    107       new_data[size_] = 0;
    108     }
    109 
    110     capacity_ = new_capacity;
    111     std::swap(new_data, data_);
    112   }
    113 
    114   // NOTE: unlike vector, this does not initialize the new data!
    115   // However, we guarantee that write_bits can safely append after
    116   // the resize, as we zero-initialize the first new byte of data.
    117   // If size < capacity(), does not invalidate the memory.
    118   void resize(size_t size) {
    119     reserve(size);
    120     size_ = (data() == nullptr) ? 0 : size;
    121   }
    122 
    123   // resize(size) plus explicit initialization of the new data with `value`.
    124   void resize(size_t size, uint8_t value) {
    125     size_t old_size = size_;
    126     resize(size);
    127     if (size_ > old_size) {
    128       memset(data() + old_size, value, size_ - old_size);
    129     }
    130   }
    131 
    132   // Amortized constant complexity due to exponential growth.
    133   void push_back(uint8_t x) {
    134     if (size_ == capacity_) {
    135       reserve(capacity_ + 1);
    136       if (data() == nullptr) return;
    137     }
    138 
    139     data_[size_++] = x;
    140   }
    141 
    142   size_t size() const { return size_; }
    143   size_t capacity() const { return capacity_; }
    144 
    145   uint8_t* data() { return data_.get(); }
    146   const uint8_t* data() const { return data_.get(); }
    147 
    148   // std::vector operations implemented in terms of the public interface above.
    149 
    150   void clear() { resize(0); }
    151   bool empty() const { return size() == 0; }
    152 
    153   void assign(std::initializer_list<uint8_t> il) {
    154     resize(il.size());
    155     memcpy(data(), il.begin(), il.size());
    156   }
    157 
    158   uint8_t* begin() { return data(); }
    159   const uint8_t* begin() const { return data(); }
    160   uint8_t* end() { return begin() + size(); }
    161   const uint8_t* end() const { return begin() + size(); }
    162 
    163   uint8_t& operator[](const size_t i) {
    164     BoundsCheck(i);
    165     return data()[i];
    166   }
    167   const uint8_t& operator[](const size_t i) const {
    168     BoundsCheck(i);
    169     return data()[i];
    170   }
    171 
    172   uint8_t& back() {
    173     JXL_ASSERT(size() != 0);
    174     return data()[size() - 1];
    175   }
    176   const uint8_t& back() const {
    177     JXL_ASSERT(size() != 0);
    178     return data()[size() - 1];
    179   }
    180 
    181   template <typename T>
    182   void append(const T& other) {
    183     append(reinterpret_cast<const uint8_t*>(other.data()),
    184            reinterpret_cast<const uint8_t*>(other.data()) + other.size());
    185   }
    186 
    187   void append(const uint8_t* begin, const uint8_t* end) {
    188     if (end - begin > 0) {
    189       size_t old_size = size();
    190       resize(size() + (end - begin));
    191       memcpy(data() + old_size, begin, end - begin);
    192     }
    193   }
    194 
    195  private:
    196   void BoundsCheck(size_t i) const {
    197     // <= is safe due to padding and required by BitWriter.
    198     JXL_ASSERT(i <= size());
    199   }
    200 
    201   size_t size_;
    202   size_t capacity_;
    203   CacheAlignedUniquePtr data_;
    204 };
    205 
    206 template <typename T>
    207 static inline void Append(const T& s, PaddedBytes* out,
    208                           size_t* JXL_RESTRICT byte_pos) {
    209   memcpy(out->data() + *byte_pos, s.data(), s.size());
    210   *byte_pos += s.size();
    211   JXL_CHECK(*byte_pos <= out->size());
    212 }
    213 
    214 }  // namespace jxl
    215 
    216 #endif  // LIB_JXL_BASE_PADDED_BYTES_H_