tor-browser

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

dec_bit_reader.h (11484B)


      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_DEC_BIT_READER_H_
      7 #define LIB_JXL_DEC_BIT_READER_H_
      8 
      9 // Bounds-checked bit reader; 64-bit buffer with support for deferred refills
     10 // and switching to reading byte-aligned words.
     11 
     12 #include <cstddef>
     13 #include <cstdint>
     14 #include <cstring>  // memcpy
     15 
     16 #ifdef __BMI2__
     17 #include <immintrin.h>
     18 #endif
     19 
     20 #include "lib/jxl/base/byte_order.h"
     21 #include "lib/jxl/base/common.h"
     22 #include "lib/jxl/base/compiler_specific.h"
     23 #include "lib/jxl/base/status.h"
     24 
     25 namespace jxl {
     26 
     27 // Reads bits previously written to memory by BitWriter. Uses unaligned 8-byte
     28 // little-endian loads.
     29 class BitReader {
     30 public:
     31  static constexpr size_t kMaxBitsPerCall = 56;
     32 
     33  // Constructs an invalid BitReader, to be overwritten before usage.
     34  BitReader()
     35      : buf_(0),
     36        bits_in_buf_(0),
     37        next_byte_{nullptr},
     38        end_minus_8_{nullptr},
     39        first_byte_(nullptr) {}
     40  BitReader(const BitReader&) = delete;
     41 
     42  // bytes need not be aligned nor padded!
     43  template <class ArrayLike>
     44  explicit BitReader(const ArrayLike& bytes)
     45      : buf_(0),
     46        bits_in_buf_(0),
     47        next_byte_(bytes.data()),
     48        // Assumes first_byte_ >= 8.
     49        end_minus_8_(bytes.data() - 8 + bytes.size()),
     50        first_byte_(bytes.data()) {
     51    Refill();
     52  }
     53  ~BitReader() {
     54    // Close() must be called before destroying an initialized bit reader.
     55    // Invalid bit readers will have a nullptr in first_byte_.
     56    JXL_DASSERT(close_called_ || !first_byte_);
     57  }
     58 
     59  // Move operator needs to invalidate the other BitReader such that it is
     60  // irrelevant if we call Close() on it or not.
     61  BitReader& operator=(BitReader&& other) noexcept {
     62    // Ensure the current instance was already closed, before we overwrite it
     63    // with other.
     64    JXL_DASSERT(close_called_ || !first_byte_);
     65 
     66    JXL_DASSERT(!other.close_called_);
     67    buf_ = other.buf_;
     68    bits_in_buf_ = other.bits_in_buf_;
     69    next_byte_ = other.next_byte_;
     70    end_minus_8_ = other.end_minus_8_;
     71    first_byte_ = other.first_byte_;
     72    overread_bytes_ = other.overread_bytes_;
     73    close_called_ = other.close_called_;
     74 
     75    other.first_byte_ = nullptr;
     76    other.next_byte_ = nullptr;
     77    return *this;
     78  }
     79  BitReader& operator=(const BitReader& other) = delete;
     80 
     81  // For time-critical reads, refills can be shared by multiple reads.
     82  // Based on variant 4 (plus bounds-checking), see
     83  // fgiesen.wordpress.com/2018/02/20/reading-bits-in-far-too-many-ways-part-2/
     84  JXL_INLINE void Refill() {
     85    if (JXL_UNLIKELY(next_byte_ > end_minus_8_)) {
     86      BoundsCheckedRefill();
     87    } else {
     88      // It's safe to load 64 bits; insert valid (possibly nonzero) bits above
     89      // bits_in_buf_. The shift requires bits_in_buf_ < 64.
     90      buf_ |= LoadLE64(next_byte_) << bits_in_buf_;
     91 
     92      // Advance by bytes fully absorbed into the buffer.
     93      next_byte_ += (63 - bits_in_buf_) >> 3;
     94 
     95      // We absorbed a multiple of 8 bits, so the lower 3 bits of bits_in_buf_
     96      // must remain unchanged, otherwise the next refill's shifted bits will
     97      // not align with buf_. Set the three upper bits so the result >= 56.
     98      bits_in_buf_ |= 56;
     99      JXL_DASSERT(56 <= bits_in_buf_ && bits_in_buf_ < 64);
    100    }
    101  }
    102 
    103  // Returns the bits that would be returned by Read without calling Advance().
    104  // It is legal to PEEK at more bits than present in the bitstream (required
    105  // by Huffman), and those bits will be zero.
    106  template <size_t N>
    107  JXL_INLINE uint64_t PeekFixedBits() const {
    108    static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call.");
    109    JXL_DASSERT(!close_called_);
    110    return buf_ & ((1ULL << N) - 1);
    111  }
    112 
    113  JXL_INLINE uint64_t PeekBits(size_t nbits) const {
    114    JXL_DASSERT(nbits <= kMaxBitsPerCall);
    115    JXL_DASSERT(!close_called_);
    116 
    117    // Slightly faster but requires BMI2. It is infeasible to make the many
    118    // callers reside between begin/end_target, especially because only the
    119    // callers in dec_ans are time-critical. Therefore only enabled if the
    120    // entire binary is compiled for (and thus requires) BMI2.
    121 #if defined(__BMI2__) && defined(__x86_64__)
    122    return _bzhi_u64(buf_, nbits);
    123 #else
    124    const uint64_t mask = (1ULL << nbits) - 1;
    125    return buf_ & mask;
    126 #endif
    127  }
    128 
    129  // Removes bits from the buffer. Need not match the previous Peek size, but
    130  // the buffer must contain at least num_bits (this prevents consuming more
    131  // than the total number of bits).
    132  JXL_INLINE void Consume(size_t num_bits) {
    133    JXL_DASSERT(!close_called_);
    134    JXL_DASSERT(bits_in_buf_ >= num_bits);
    135    if (JXL_CRASH_ON_ERROR) {
    136      // When JXL_CRASH_ON_ERROR is defined, it is a fatal error to read more
    137      // bits than available in the stream. A non-zero overread_bytes_ implies
    138      // that next_byte_ is already at the end of the stream, so we don't need
    139      // to check that.
    140      JXL_DASSERT(bits_in_buf_ >= num_bits + overread_bytes_ * kBitsPerByte);
    141    }
    142    bits_in_buf_ -= num_bits;
    143    buf_ >>= num_bits;
    144  }
    145 
    146  JXL_INLINE uint64_t ReadBits(size_t nbits) {
    147    JXL_DASSERT(!close_called_);
    148    Refill();
    149    const uint64_t bits = PeekBits(nbits);
    150    Consume(nbits);
    151    return bits;
    152  }
    153 
    154  template <size_t N>
    155  JXL_INLINE uint64_t ReadFixedBits() {
    156    JXL_DASSERT(!close_called_);
    157    Refill();
    158    const uint64_t bits = PeekFixedBits<N>();
    159    Consume(N);
    160    return bits;
    161  }
    162 
    163  // Equivalent to calling ReadFixedBits(1) `skip` times, but much faster.
    164  // `skip` is typically large.
    165  void SkipBits(size_t skip) {
    166    JXL_DASSERT(!close_called_);
    167    // Buffer is large enough - don't zero buf_ below.
    168    if (JXL_UNLIKELY(skip <= bits_in_buf_)) {
    169      Consume(skip);
    170      return;
    171    }
    172 
    173    // First deduct what we can satisfy from the buffer
    174    skip -= bits_in_buf_;
    175    bits_in_buf_ = 0;
    176    // Not enough to call Advance - that may leave some bits in the buffer
    177    // which were previously ABOVE bits_in_buf.
    178    buf_ = 0;
    179 
    180    // Skip whole bytes
    181    const size_t whole_bytes = skip / kBitsPerByte;
    182    skip %= kBitsPerByte;
    183    if (JXL_UNLIKELY(whole_bytes >
    184                     static_cast<size_t>(end_minus_8_ + 8 - next_byte_))) {
    185      // This is already an overflow condition (skipping past the end of the bit
    186      // stream). However if we increase next_byte_ too much we risk overflowing
    187      // that value and potentially making it valid again (next_byte_ < end).
    188      // This will set next_byte_ to the end of the stream and still consume
    189      // some bits in overread_bytes_, however the TotalBitsConsumed() will be
    190      // incorrect (still larger than the TotalBytes()).
    191      next_byte_ = end_minus_8_ + 8;
    192      skip += kBitsPerByte;
    193    } else {
    194      next_byte_ += whole_bytes;
    195    }
    196 
    197    Refill();
    198    Consume(skip);
    199  }
    200 
    201  size_t TotalBitsConsumed() const {
    202    const size_t bytes_read = static_cast<size_t>(next_byte_ - first_byte_);
    203    return (bytes_read + overread_bytes_) * kBitsPerByte - bits_in_buf_;
    204  }
    205 
    206  Status JumpToByteBoundary() {
    207    const size_t remainder = TotalBitsConsumed() % kBitsPerByte;
    208    if (remainder == 0) return true;
    209    if (JXL_UNLIKELY(ReadBits(kBitsPerByte - remainder) != 0)) {
    210      return JXL_FAILURE("Non-zero padding bits");
    211    }
    212    return true;
    213  }
    214 
    215  // For interoperability with other bitreaders (for resuming at
    216  // non-byte-aligned positions).
    217  const uint8_t* FirstByte() const { return first_byte_; }
    218  size_t TotalBytes() const {
    219    return static_cast<size_t>(end_minus_8_ + 8 - first_byte_);
    220  }
    221 
    222  // Returns whether all the bits read so far have been within the input bounds.
    223  // When reading past the EOF, the Read*() and Consume() functions return zeros
    224  // but flag a failure when calling Close() without checking this function.
    225  Status AllReadsWithinBounds() {
    226    // Mark up to which point the user checked the out of bounds condition. If
    227    // the user handles the condition at higher level (e.g. fetch more bytes
    228    // from network, return a custom JXL_FAILURE, ...), Close() should not
    229    // output a debug error (which would break tests with JXL_CRASH_ON_ERROR
    230    // even when legitimately handling the situation at higher level). This is
    231    // used by Bundle::CanRead.
    232    checked_out_of_bounds_bits_ = TotalBitsConsumed();
    233    if (TotalBitsConsumed() > TotalBytes() * kBitsPerByte) {
    234      return false;
    235    }
    236    return true;
    237  }
    238 
    239  // Close the bit reader and return whether all the previous reads were
    240  // successful. Close must be called once.
    241  Status Close() {
    242    JXL_DASSERT(!close_called_);
    243    close_called_ = true;
    244    if (!first_byte_) return true;
    245    if (TotalBitsConsumed() > checked_out_of_bounds_bits_ &&
    246        TotalBitsConsumed() > TotalBytes() * kBitsPerByte) {
    247      return JXL_FAILURE("Read more bits than available in the bit_reader");
    248    }
    249    return true;
    250  }
    251 
    252 private:
    253  // Separate function avoids inlining this relatively cold code into callers.
    254  JXL_NOINLINE void BoundsCheckedRefill() {
    255    const uint8_t* end = end_minus_8_ + 8;
    256 
    257    // Read whole bytes until we have [56, 64) bits (same as LoadLE64)
    258    for (; bits_in_buf_ < 64 - kBitsPerByte; bits_in_buf_ += kBitsPerByte) {
    259      if (next_byte_ >= end) break;
    260      buf_ |= static_cast<uint64_t>(*next_byte_++) << bits_in_buf_;
    261    }
    262    JXL_DASSERT(bits_in_buf_ < 64);
    263 
    264    // Add extra bytes as 0 at the end of the stream in the bit_buffer_. If
    265    // these bits are read, Close() will return a failure.
    266    size_t extra_bytes = (63 - bits_in_buf_) / kBitsPerByte;
    267    overread_bytes_ += extra_bytes;
    268    bits_in_buf_ += extra_bytes * kBitsPerByte;
    269 
    270    JXL_DASSERT(bits_in_buf_ < 64);
    271    JXL_DASSERT(bits_in_buf_ >= 56);
    272  }
    273 
    274  JXL_NOINLINE uint32_t BoundsCheckedReadByteAlignedWord() {
    275    if (next_byte_ + 1 < end_minus_8_ + 8) {
    276      uint32_t ret = LoadLE16(next_byte_);
    277      next_byte_ += 2;
    278      return ret;
    279    }
    280    overread_bytes_ += 2;
    281    return 0;
    282  }
    283 
    284  uint64_t buf_;
    285  size_t bits_in_buf_;  // [0, 64)
    286  const uint8_t* JXL_RESTRICT next_byte_;
    287  const uint8_t* end_minus_8_;  // for refill bounds check
    288  const uint8_t* first_byte_;   // for GetSpan
    289 
    290  // Number of bytes past the end that were loaded into the buf_. These bytes
    291  // are not read from memory, but instead assumed 0. It is an error (likely due
    292  // to an invalid stream) to Consume() more bits than specified in the range
    293  // passed to the constructor.
    294  uint64_t overread_bytes_{0};
    295  bool close_called_{false};
    296 
    297  uint64_t checked_out_of_bounds_bits_{0};
    298 };
    299 
    300 // Closes a BitReader when the BitReaderScopedCloser goes out of scope. When
    301 // closing the bit reader, if the status result was failure it sets this failure
    302 // to the passed variable pointer. Typical usage.
    303 //
    304 // Status ret = true;
    305 // {
    306 //   BitReader reader(...);
    307 //   BitReaderScopedCloser reader_closer(&reader, &ret);
    308 //
    309 //   // ... code that can return errors here ...
    310 // }
    311 // // ... more code that doesn't use the BitReader.
    312 // return ret;
    313 
    314 class BitReaderScopedCloser {
    315 public:
    316  BitReaderScopedCloser(BitReader& reader, Status& status)
    317      : reader_(&reader), status_(&status) {}
    318  ~BitReaderScopedCloser() {
    319    if (reader_ != nullptr) {
    320      Status close_ret = reader_->Close();
    321      if (!close_ret) *status_ = close_ret;
    322    }
    323  }
    324  BitReaderScopedCloser(const BitReaderScopedCloser&) = delete;
    325 
    326 private:
    327  BitReader* reader_;
    328  Status* status_;
    329 };
    330 
    331 }  // namespace jxl
    332 
    333 #endif  // LIB_JXL_DEC_BIT_READER_H_