diff options
Diffstat (limited to 'lib/jxl/fields.cc')
-rw-r--r-- | lib/jxl/fields.cc | 318 |
1 files changed, 34 insertions, 284 deletions
diff --git a/lib/jxl/fields.cc b/lib/jxl/fields.cc index e8d6025..47a7563 100644 --- a/lib/jxl/fields.cc +++ b/lib/jxl/fields.cc @@ -9,8 +9,8 @@ #include <algorithm> #include <cmath> +#include <hwy/base.h> -#include "hwy/base.h" #include "lib/jxl/base/bits.h" #include "lib/jxl/base/printf_macros.h" @@ -18,118 +18,7 @@ namespace jxl { namespace { -// A bundle can be in one of three states concerning extensions: not-begun, -// active, ended. Bundles may be nested, so we need a stack of states. -class ExtensionStates { - public: - void Push() { - // Initial state = not-begun. - begun_ <<= 1; - ended_ <<= 1; - } - - // Clears current state; caller must check IsEnded beforehand. - void Pop() { - begun_ >>= 1; - ended_ >>= 1; - } - - // Returns true if state == active || state == ended. - Status IsBegun() const { return (begun_ & 1) != 0; } - // Returns true if state != not-begun && state != active. - Status IsEnded() const { return (ended_ & 1) != 0; } - - void Begin() { - JXL_ASSERT(!IsBegun()); - JXL_ASSERT(!IsEnded()); - begun_ += 1; - } - - void End() { - JXL_ASSERT(IsBegun()); - JXL_ASSERT(!IsEnded()); - ended_ += 1; - } - - private: - // Current state := least-significant bit of begun_ and ended_. - uint64_t begun_ = 0; - uint64_t ended_ = 0; -}; - -// Visitors generate Init/AllDefault/Read/Write logic for all fields. Each -// bundle's VisitFields member function calls visitor->U32 etc. We do not -// overload operator() because a function name is easier to search for. - -class VisitorBase : public Visitor { - public: - explicit VisitorBase() {} - ~VisitorBase() override { JXL_ASSERT(depth_ == 0); } - - // This is the only call site of Fields::VisitFields. - // Ensures EndExtensions was called. - Status Visit(Fields* fields) override { - depth_ += 1; - JXL_ASSERT(depth_ <= Bundle::kMaxExtensions); - extension_states_.Push(); - - const Status ok = fields->VisitFields(this); - - if (ok) { - // If VisitFields called BeginExtensions, must also call - // EndExtensions. - JXL_ASSERT(!extension_states_.IsBegun() || extension_states_.IsEnded()); - } else { - // Failed, undefined state: don't care whether EndExtensions was - // called. - } - - extension_states_.Pop(); - JXL_ASSERT(depth_ != 0); - depth_ -= 1; - - return ok; - } - - // For visitors accepting a const Visitor, need to const-cast so we can call - // the non-const Visitor::VisitFields. NOTE: C is not modified except the - // `all_default` field by CanEncodeVisitor. - Status VisitConst(const Fields& t) { return Visit(const_cast<Fields*>(&t)); } - - // Derived types (overridden by InitVisitor because it is unsafe to read - // from *value there) - - Status Bool(bool default_value, bool* JXL_RESTRICT value) override { - uint32_t bits = *value ? 1 : 0; - JXL_RETURN_IF_ERROR(Bits(1, static_cast<uint32_t>(default_value), &bits)); - JXL_DASSERT(bits <= 1); - *value = bits == 1; - return true; - } - - // Overridden by ReadVisitor and WriteVisitor. - // Called before any conditional visit based on "extensions". - // Overridden by ReadVisitor, CanEncodeVisitor and WriteVisitor. - Status BeginExtensions(uint64_t* JXL_RESTRICT extensions) override { - JXL_RETURN_IF_ERROR(U64(0, extensions)); - - extension_states_.Begin(); - return true; - } - - // Called after all extension fields (if any). Although non-extension - // fields could be visited afterward, we prefer the convention that - // extension fields are always the last to be visited. Overridden by - // ReadVisitor. - Status EndExtensions() override { - extension_states_.End(); - return true; - } - - private: - size_t depth_ = 0; // to check nesting - ExtensionStates extension_states_; -}; +using ::jxl::fields_internal::VisitorBase; struct InitVisitor : public VisitorBase { Status Bits(const size_t /*unused*/, const uint32_t default_value, @@ -336,7 +225,7 @@ class ReadVisitor : public VisitorBase { if (pos_after_ext_size_ == 0) return true; // Not enough bytes as set by BeginExtensions or earlier. Do not return - // this as an JXL_FAILURE or false (which can also propagate to error + // this as a JXL_FAILURE or false (which can also propagate to error // through e.g. JXL_RETURN_IF_ERROR), since this may be used while // silently checking whether there are enough bytes. If this case must be // treated as an error, reader_>Close() will do this, just like is already @@ -377,6 +266,8 @@ class ReadVisitor : public VisitorBase { uint64_t extension_bits_[Bundle::kMaxExtensions] = {0}; uint64_t total_extension_bits_ = 0; size_t pos_after_ext_size_ = 0; // 0 iff extensions == 0. + + friend Status jxl::CheckHasEnoughBits(Visitor*, size_t); }; class MaxBitsVisitor : public VisitorBase { @@ -517,82 +408,24 @@ class CanEncodeVisitor : public VisitorBase { // including the hidden extension sizes. uint64_t pos_after_ext_ = 0; }; - -class WriteVisitor : public VisitorBase { - public: - WriteVisitor(const size_t extension_bits, BitWriter* JXL_RESTRICT writer) - : extension_bits_(extension_bits), writer_(writer) {} - - Status Bits(const size_t bits, const uint32_t /*default_value*/, - uint32_t* JXL_RESTRICT value) override { - ok_ &= BitsCoder::Write(bits, *value, writer_); - return true; - } - Status U32(const U32Enc enc, const uint32_t /*default_value*/, - uint32_t* JXL_RESTRICT value) override { - ok_ &= U32Coder::Write(enc, *value, writer_); - return true; - } - - Status U64(const uint64_t /*default_value*/, - uint64_t* JXL_RESTRICT value) override { - ok_ &= U64Coder::Write(*value, writer_); - return true; - } - - Status F16(const float /*default_value*/, - float* JXL_RESTRICT value) override { - ok_ &= F16Coder::Write(*value, writer_); - return true; - } - - Status BeginExtensions(uint64_t* JXL_RESTRICT extensions) override { - JXL_QUIET_RETURN_IF_ERROR(VisitorBase::BeginExtensions(extensions)); - if (*extensions == 0) { - JXL_ASSERT(extension_bits_ == 0); - return true; - } - // TODO(janwas): extend API to pass in array of extension_bits, one per - // extension. We currently ascribe all bits to the first extension, but - // this is only an encoder limitation. NOTE: extension_bits_ can be zero - // if an extension does not require any additional fields. - ok_ &= U64Coder::Write(extension_bits_, writer_); - // For each nonzero bit except the lowest/first (already written): - for (uint64_t remaining_extensions = *extensions & (*extensions - 1); - remaining_extensions != 0; - remaining_extensions &= remaining_extensions - 1) { - ok_ &= U64Coder::Write(0, writer_); - } - return true; - } - // EndExtensions = default. - - Status OK() const { return ok_; } - - private: - const size_t extension_bits_; - BitWriter* JXL_RESTRICT writer_; - bool ok_ = true; -}; - } // namespace void Bundle::Init(Fields* fields) { InitVisitor visitor; if (!visitor.Visit(fields)) { - JXL_ABORT("Init should never fail"); + JXL_UNREACHABLE("Init should never fail"); } } void Bundle::SetDefault(Fields* fields) { SetDefaultVisitor visitor; if (!visitor.Visit(fields)) { - JXL_ABORT("SetDefault should never fail"); + JXL_UNREACHABLE("SetDefault should never fail"); } } bool Bundle::AllDefault(const Fields& fields) { AllDefaultVisitor visitor; if (!visitor.VisitConst(fields)) { - JXL_ABORT("AllDefault should never fail"); + JXL_UNREACHABLE("AllDefault should never fail"); } return visitor.AllDefault(); } @@ -627,19 +460,23 @@ bool Bundle::CanRead(BitReader* reader, Fields* fields) { // there's an error. Use Read() to determine which error it is. return status.code() != StatusCode::kNotEnoughBytes; } -Status Bundle::Write(const Fields& fields, BitWriter* writer, size_t layer, - AuxOut* aux_out) { - size_t extension_bits, total_bits; - JXL_RETURN_IF_ERROR(CanEncode(fields, &extension_bits, &total_bits)); - - BitWriter::Allotment allotment(writer, total_bits); - WriteVisitor visitor(extension_bits, writer); - JXL_RETURN_IF_ERROR(visitor.VisitConst(fields)); - JXL_RETURN_IF_ERROR(visitor.OK()); - ReclaimAndCharge(writer, &allotment, layer, aux_out); + +size_t BitsCoder::MaxEncodedBits(const size_t bits) { return bits; } + +Status BitsCoder::CanEncode(const size_t bits, const uint32_t value, + size_t* JXL_RESTRICT encoded_bits) { + *encoded_bits = bits; + if (value >= (1ULL << bits)) { + return JXL_FAILURE("Value %u too large for %" PRIu64 " bits", value, + static_cast<uint64_t>(bits)); + } return true; } +uint32_t BitsCoder::Read(const size_t bits, BitReader* JXL_RESTRICT reader) { + return reader->ReadBits(bits); +} + size_t U32Coder::MaxEncodedBits(const U32Enc enc) { size_t extra_bits = 0; for (uint32_t selector = 0; selector < 4; ++selector) { @@ -672,25 +509,6 @@ uint32_t U32Coder::Read(const U32Enc enc, BitReader* JXL_RESTRICT reader) { } } -// Returns false if the value is too large to encode. -Status U32Coder::Write(const U32Enc enc, const uint32_t value, - BitWriter* JXL_RESTRICT writer) { - uint32_t selector; - size_t total_bits; - JXL_RETURN_IF_ERROR(ChooseSelector(enc, value, &selector, &total_bits)); - - writer->Write(2, selector); - - const U32Distr d = enc.GetDistr(selector); - if (!d.IsDirect()) { // Nothing more to write for direct encoding - const uint32_t offset = d.Offset(); - JXL_ASSERT(value >= offset); - writer->Write(total_bits - 2, value - offset); - } - - return true; -} - Status U32Coder::ChooseSelector(const U32Enc enc, const uint32_t value, uint32_t* JXL_RESTRICT selector, size_t* JXL_RESTRICT total_bits) { @@ -761,46 +579,6 @@ uint64_t U64Coder::Read(BitReader* JXL_RESTRICT reader) { return result; } -// Returns false if the value is too large to encode. -Status U64Coder::Write(uint64_t value, BitWriter* JXL_RESTRICT writer) { - if (value == 0) { - // Selector: use 0 bits, value 0 - writer->Write(2, 0); - } else if (value <= 16) { - // Selector: use 4 bits, value 1..16 - writer->Write(2, 1); - writer->Write(4, value - 1); - } else if (value <= 272) { - // Selector: use 8 bits, value 17..272 - writer->Write(2, 2); - writer->Write(8, value - 17); - } else { - // Selector: varint, first a 12-bit group, after that per 8-bit group. - writer->Write(2, 3); - writer->Write(12, value & 4095); - value >>= 12; - int shift = 12; - while (value > 0 && shift < 60) { - // Indicate varint not done - writer->Write(1, 1); - writer->Write(8, value & 255); - value >>= 8; - shift += 8; - } - if (value > 0) { - // This only could happen if shift == N - 4. - writer->Write(1, 1); - writer->Write(4, value & 15); - // Implicitly closed sequence, no extra stop bit is required. - } else { - // Indicate end of varint - writer->Write(1, 0); - } - } - - return true; -} - // Can always encode, but useful because it also returns bit size. Status U64Coder::CanEncode(uint64_t value, size_t* JXL_RESTRICT encoded_bits) { if (value == 0) { @@ -855,46 +633,6 @@ Status F16Coder::Read(BitReader* JXL_RESTRICT reader, return true; } -Status F16Coder::Write(float value, BitWriter* JXL_RESTRICT writer) { - uint32_t bits32; - memcpy(&bits32, &value, sizeof(bits32)); - const uint32_t sign = bits32 >> 31; - const uint32_t biased_exp32 = (bits32 >> 23) & 0xFF; - const uint32_t mantissa32 = bits32 & 0x7FFFFF; - - const int32_t exp = static_cast<int32_t>(biased_exp32) - 127; - if (JXL_UNLIKELY(exp > 15)) { - return JXL_FAILURE("Too big to encode, CanEncode should return false"); - } - - // Tiny or zero => zero. - if (exp < -24) { - writer->Write(16, 0); - return true; - } - - uint32_t biased_exp16, mantissa16; - - // exp = [-24, -15] => subnormal - if (JXL_UNLIKELY(exp < -14)) { - biased_exp16 = 0; - const uint32_t sub_exp = static_cast<uint32_t>(-14 - exp); - JXL_ASSERT(1 <= sub_exp && sub_exp < 11); - mantissa16 = (1 << (10 - sub_exp)) + (mantissa32 >> (13 + sub_exp)); - } else { - // exp = [-14, 15] - biased_exp16 = static_cast<uint32_t>(exp + 15); - JXL_ASSERT(1 <= biased_exp16 && biased_exp16 < 31); - mantissa16 = mantissa32 >> 13; - } - - JXL_ASSERT(mantissa16 < 1024); - const uint32_t bits16 = (sign << 15) | (biased_exp16 << 10) | mantissa16; - JXL_ASSERT(bits16 < 0x10000); - writer->Write(16, bits16); - return true; -} - Status F16Coder::CanEncode(float value, size_t* JXL_RESTRICT encoded_bits) { *encoded_bits = MaxEncodedBits(); if (std::isnan(value) || std::isinf(value)) { @@ -903,4 +641,16 @@ Status F16Coder::CanEncode(float value, size_t* JXL_RESTRICT encoded_bits) { return std::abs(value) <= 65504.0f; } +Status CheckHasEnoughBits(Visitor* visitor, size_t bits) { + if (!visitor->IsReading()) return false; + ReadVisitor* rv = static_cast<ReadVisitor*>(visitor); + size_t have_bits = rv->reader_->TotalBytes() * kBitsPerByte; + size_t want_bits = bits + rv->reader_->TotalBitsConsumed(); + if (have_bits < want_bits) { + return JXL_STATUS(StatusCode::kNotEnoughBytes, + "Not enough bytes for header"); + } + return true; +} + } // namespace jxl |