libjxl

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

dec_bit_reader.h (12140B)


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