summaryrefslogtreecommitdiff
path: root/lib/jxl/fields.cc
diff options
context:
space:
mode:
Diffstat (limited to 'lib/jxl/fields.cc')
-rw-r--r--lib/jxl/fields.cc318
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