/src/libjxl/lib/jxl/fields.h
Line | Count | Source (jump to first uncovered line) |
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_FIELDS_H_ |
7 | | #define LIB_JXL_FIELDS_H_ |
8 | | |
9 | | // Forward/backward-compatible 'bundles' with auto-serialized 'fields'. |
10 | | |
11 | | #include <cstddef> |
12 | | #include <cstdint> |
13 | | #include <cstdlib> |
14 | | #include <cstring> |
15 | | |
16 | | #include "lib/jxl/base/compiler_specific.h" |
17 | | #include "lib/jxl/base/status.h" |
18 | | #include "lib/jxl/dec_bit_reader.h" |
19 | | #include "lib/jxl/field_encodings.h" |
20 | | |
21 | | namespace jxl { |
22 | | |
23 | | struct AuxOut; |
24 | | enum class LayerType : uint8_t; |
25 | | struct BitWriter; |
26 | | |
27 | | // Integer coders: BitsCoder (raw), U32Coder (table), U64Coder (varint). |
28 | | |
29 | | // Reads/writes a given (fixed) number of bits <= 32. |
30 | | namespace BitsCoder { |
31 | | size_t MaxEncodedBits(size_t bits); |
32 | | |
33 | | Status CanEncode(size_t bits, uint32_t value, |
34 | | size_t* JXL_RESTRICT encoded_bits); |
35 | | |
36 | | uint32_t Read(size_t bits, BitReader* JXL_RESTRICT reader); |
37 | | |
38 | | // Returns false if the value is too large to encode. |
39 | | Status Write(size_t bits, uint32_t value, BitWriter* JXL_RESTRICT writer); |
40 | | } // namespace BitsCoder |
41 | | |
42 | | // Encodes u32 using a lookup table and/or extra bits, governed by a per-field |
43 | | // encoding `enc` which consists of four distributions `d` chosen via a 2-bit |
44 | | // selector (least significant = 0). Each d may have two modes: |
45 | | // - direct: if d.IsDirect(), the value is d.Direct(); |
46 | | // - offset: the value is derived from d.ExtraBits() extra bits plus d.Offset(); |
47 | | // This encoding is denser than Exp-Golomb or Gamma codes when both small and |
48 | | // large values occur. |
49 | | // |
50 | | // Examples: |
51 | | // Direct: U32Enc(Val(8), Val(16), Val(32), Bits(6)), value 32 => 10b. |
52 | | // Offset: U32Enc(Val(0), BitsOffset(1, 1), BitsOffset(2, 3), BitsOffset(8, 8)) |
53 | | // defines the following prefix code: |
54 | | // 00 -> 0 |
55 | | // 01x -> 1..2 |
56 | | // 10xx -> 3..7 |
57 | | // 11xxxxxxxx -> 8..263 |
58 | | namespace U32Coder { |
59 | | size_t MaxEncodedBits(U32Enc enc); |
60 | | Status CanEncode(U32Enc enc, uint32_t value, size_t* JXL_RESTRICT encoded_bits); |
61 | | uint32_t Read(U32Enc enc, BitReader* JXL_RESTRICT reader); |
62 | | |
63 | | // Returns false if the value is too large to encode. |
64 | | Status Write(U32Enc enc, uint32_t value, BitWriter* JXL_RESTRICT writer); |
65 | | |
66 | | // "private" |
67 | | Status ChooseSelector(U32Enc enc, uint32_t value, |
68 | | uint32_t* JXL_RESTRICT selector, |
69 | | size_t* JXL_RESTRICT total_bits); |
70 | | } // namespace U32Coder |
71 | | |
72 | | // Encodes 64-bit unsigned integers with a fixed distribution, taking 2 bits |
73 | | // to encode 0, 6 bits to encode 1 to 16, 10 bits to encode 17 to 272, 15 bits |
74 | | // to encode up to 4095, and on the order of log2(value) * 1.125 bits for |
75 | | // larger values. |
76 | | namespace U64Coder { |
77 | 0 | constexpr size_t MaxEncodedBits() { return 2 + 12 + 6 * (8 + 1) + (4 + 1); } |
78 | | |
79 | | uint64_t Read(BitReader* JXL_RESTRICT reader); |
80 | | |
81 | | // Returns false if the value is too large to encode. |
82 | | Status Write(uint64_t value, BitWriter* JXL_RESTRICT writer); |
83 | | |
84 | | // Can always encode, but useful because it also returns bit size. |
85 | | Status CanEncode(uint64_t value, size_t* JXL_RESTRICT encoded_bits); |
86 | | } // namespace U64Coder |
87 | | |
88 | | // IEEE 754 half-precision (binary16). Refuses to read/write NaN/Inf. |
89 | | namespace F16Coder { |
90 | 0 | constexpr size_t MaxEncodedBits() { return 16; } |
91 | | |
92 | | // Returns false if the bit representation is NaN or infinity |
93 | | Status Read(BitReader* JXL_RESTRICT reader, float* JXL_RESTRICT value); |
94 | | |
95 | | // Returns false if the value is too large to encode. |
96 | | Status Write(float value, BitWriter* JXL_RESTRICT writer); |
97 | | Status CanEncode(float value, size_t* JXL_RESTRICT encoded_bits); |
98 | | } // namespace F16Coder |
99 | | |
100 | | // A "bundle" is a forward- and backward compatible collection of fields. |
101 | | // They are used for SizeHeader/FrameHeader/GroupHeader. Bundles can be |
102 | | // extended by appending(!) fields. Optional fields may be omitted from the |
103 | | // bitstream by conditionally visiting them. When reading new bitstreams with |
104 | | // old code, we skip unknown fields at the end of the bundle. This requires |
105 | | // storing the amount of extra appended bits, and that fields are visited in |
106 | | // chronological order of being added to the format, because old decoders |
107 | | // cannot skip some future fields and resume reading old fields. Similarly, |
108 | | // new readers query bits in an "extensions" field to skip (groups of) fields |
109 | | // not present in old bitstreams. Note that each bundle must include an |
110 | | // "extensions" field prior to freezing the format, otherwise it cannot be |
111 | | // extended. |
112 | | // |
113 | | // To ensure interoperability, there will be no opaque fields. |
114 | | // |
115 | | // HOWTO: |
116 | | // - basic usage: define a struct with member variables ("fields") and a |
117 | | // VisitFields(v) member function that calls v->U32/Bool etc. for each |
118 | | // field, specifying their default values. The ctor must call |
119 | | // Bundle::Init(this). |
120 | | // |
121 | | // - print a trace of visitors: ensure each bundle has a static Name() member |
122 | | // function, and change Bundle::Print* to return true. |
123 | | // |
124 | | // - optional fields: in VisitFields, add if (v->Conditional(your_condition)) |
125 | | // { v->Bool(default, &field); }. This prevents reading/writing field |
126 | | // if !your_condition, which is typically computed from a prior field. |
127 | | // WARNING: to ensure all fields are initialized, do not add an else branch; |
128 | | // instead add another if (v->Conditional(!your_condition)). |
129 | | // |
130 | | // - repeated fields: for dynamic sizes, use e.g. std::vector and in |
131 | | // VisitFields, if (v->IsReading()) field.resize(size) before accessing field. |
132 | | // For static or bounded sizes, use an array or std::array. In all cases, |
133 | | // simply visit each array element as if it were a normal field. |
134 | | // |
135 | | // - nested bundles: add a bundle as a normal field and in VisitFields call |
136 | | // JXL_RETURN_IF_ERROR(v->VisitNested(&nested)); |
137 | | // |
138 | | // - allow future extensions: define a "uint64_t extensions" field and call |
139 | | // v->BeginExtensions(&extensions) after visiting all non-extension fields, |
140 | | // and `return v->EndExtensions();` after the last extension field. |
141 | | // |
142 | | // - encode an entire bundle in one bit if ALL its fields equal their default |
143 | | // values: add a "mutable bool all_default" field and as the first visitor: |
144 | | // if (v->AllDefault(*this, &all_default)) { |
145 | | // // Overwrite all serialized fields, but not any nonserialized_*. |
146 | | // v->SetDefault(this); |
147 | | // return true; |
148 | | // } |
149 | | // Note: if extensions are present, AllDefault() == false. |
150 | | |
151 | | namespace Bundle { |
152 | | constexpr size_t kMaxExtensions = 64; // bits in u64 |
153 | | |
154 | | // Initializes fields to the default values. It is not recursive to nested |
155 | | // fields, this function is intended to be called in the constructors so |
156 | | // each nested field will already Init itself. |
157 | | void Init(Fields* JXL_RESTRICT fields); |
158 | | |
159 | | // Similar to Init, but recursive to nested fields. |
160 | | void SetDefault(Fields* JXL_RESTRICT fields); |
161 | | |
162 | | // Returns whether ALL fields (including `extensions`, if present) are equal |
163 | | // to their default value. |
164 | | bool AllDefault(const Fields& fields); |
165 | | |
166 | | // Returns whether a header's fields can all be encoded, i.e. they have a |
167 | | // valid representation. If so, "*total_bits" is the exact number of bits |
168 | | // required. Called by Write. |
169 | | Status CanEncode(const Fields& fields, size_t* JXL_RESTRICT extension_bits, |
170 | | size_t* JXL_RESTRICT total_bits); |
171 | | |
172 | | Status Read(BitReader* reader, Fields* JXL_RESTRICT fields); |
173 | | |
174 | | // Returns whether enough bits are available to fully read this bundle using |
175 | | // Read. Also returns true in case of a codestream error (other than not being |
176 | | // large enough): that means enough bits are available to determine there's an |
177 | | // error, use Read to get such error status. |
178 | | // NOTE: this advances the BitReader, a different one pointing back at the |
179 | | // original bit position in the codestream must be created to use Read after |
180 | | // this. |
181 | | bool CanRead(BitReader* reader, Fields* JXL_RESTRICT fields); |
182 | | |
183 | | Status Write(const Fields& fields, BitWriter* JXL_RESTRICT writer, |
184 | | LayerType layer, AuxOut* aux_out); |
185 | | } // namespace Bundle |
186 | | |
187 | | // Different subclasses of Visitor are passed to implementations of Fields |
188 | | // throughout their lifetime. Templates used to be used for this but dynamic |
189 | | // polymorphism produces more compact executables than template reification did. |
190 | | class Visitor { |
191 | | public: |
192 | 3.46M | virtual ~Visitor() = default; |
193 | | virtual Status Visit(Fields* fields) = 0; |
194 | | |
195 | | virtual Status Bool(bool default_value, bool* JXL_RESTRICT value) = 0; |
196 | | virtual Status U32(U32Enc, uint32_t, uint32_t*) = 0; |
197 | | |
198 | | // Helper to construct U32Enc from U32Distr. |
199 | | Status U32(const U32Distr d0, const U32Distr d1, const U32Distr d2, |
200 | | const U32Distr d3, const uint32_t default_value, |
201 | 7.14M | uint32_t* JXL_RESTRICT value) { |
202 | 7.14M | return U32(U32Enc(d0, d1, d2, d3), default_value, value); |
203 | 7.14M | } |
204 | | |
205 | | template <typename EnumT> |
206 | 1.23M | Status Enum(const EnumT default_value, EnumT* JXL_RESTRICT value) { |
207 | 1.23M | uint32_t u32 = static_cast<uint32_t>(*value); |
208 | | // 00 -> 0 |
209 | | // 01 -> 1 |
210 | | // 10xxxx -> 2..17 |
211 | | // 11yyyyyy -> 18..81 |
212 | 1.23M | JXL_RETURN_IF_ERROR(U32(Val(0), Val(1), BitsOffset(4, 2), BitsOffset(6, 18), |
213 | 1.23M | static_cast<uint32_t>(default_value), &u32)); |
214 | 1.23M | *value = static_cast<EnumT>(u32); |
215 | 1.23M | return EnumValid(*value); |
216 | 1.23M | } jxl::Status jxl::Visitor::Enum<jxl::cms::TransferFunction>(jxl::cms::TransferFunction, jxl::cms::TransferFunction*) Line | Count | Source | 206 | 186k | Status Enum(const EnumT default_value, EnumT* JXL_RESTRICT value) { | 207 | 186k | uint32_t u32 = static_cast<uint32_t>(*value); | 208 | | // 00 -> 0 | 209 | | // 01 -> 1 | 210 | | // 10xxxx -> 2..17 | 211 | | // 11yyyyyy -> 18..81 | 212 | 186k | JXL_RETURN_IF_ERROR(U32(Val(0), Val(1), BitsOffset(4, 2), BitsOffset(6, 18), | 213 | 186k | static_cast<uint32_t>(default_value), &u32)); | 214 | 186k | *value = static_cast<EnumT>(u32); | 215 | 186k | return EnumValid(*value); | 216 | 186k | } |
jxl::Status jxl::Visitor::Enum<jxl::cms::ColorSpace>(jxl::cms::ColorSpace, jxl::cms::ColorSpace*) Line | Count | Source | 206 | 190k | Status Enum(const EnumT default_value, EnumT* JXL_RESTRICT value) { | 207 | 190k | uint32_t u32 = static_cast<uint32_t>(*value); | 208 | | // 00 -> 0 | 209 | | // 01 -> 1 | 210 | | // 10xxxx -> 2..17 | 211 | | // 11yyyyyy -> 18..81 | 212 | 190k | JXL_RETURN_IF_ERROR(U32(Val(0), Val(1), BitsOffset(4, 2), BitsOffset(6, 18), | 213 | 190k | static_cast<uint32_t>(default_value), &u32)); | 214 | 190k | *value = static_cast<EnumT>(u32); | 215 | 190k | return EnumValid(*value); | 216 | 190k | } |
jxl::Status jxl::Visitor::Enum<jxl::cms::WhitePoint>(jxl::cms::WhitePoint, jxl::cms::WhitePoint*) Line | Count | Source | 206 | 187k | Status Enum(const EnumT default_value, EnumT* JXL_RESTRICT value) { | 207 | 187k | uint32_t u32 = static_cast<uint32_t>(*value); | 208 | | // 00 -> 0 | 209 | | // 01 -> 1 | 210 | | // 10xxxx -> 2..17 | 211 | | // 11yyyyyy -> 18..81 | 212 | 187k | JXL_RETURN_IF_ERROR(U32(Val(0), Val(1), BitsOffset(4, 2), BitsOffset(6, 18), | 213 | 187k | static_cast<uint32_t>(default_value), &u32)); | 214 | 187k | *value = static_cast<EnumT>(u32); | 215 | 187k | return EnumValid(*value); | 216 | 187k | } |
jxl::Status jxl::Visitor::Enum<jxl::cms::Primaries>(jxl::cms::Primaries, jxl::cms::Primaries*) Line | Count | Source | 206 | 186k | Status Enum(const EnumT default_value, EnumT* JXL_RESTRICT value) { | 207 | 186k | uint32_t u32 = static_cast<uint32_t>(*value); | 208 | | // 00 -> 0 | 209 | | // 01 -> 1 | 210 | | // 10xxxx -> 2..17 | 211 | | // 11yyyyyy -> 18..81 | 212 | 186k | JXL_RETURN_IF_ERROR(U32(Val(0), Val(1), BitsOffset(4, 2), BitsOffset(6, 18), | 213 | 186k | static_cast<uint32_t>(default_value), &u32)); | 214 | 186k | *value = static_cast<EnumT>(u32); | 215 | 186k | return EnumValid(*value); | 216 | 186k | } |
jxl::Status jxl::Visitor::Enum<jxl::cms::RenderingIntent>(jxl::cms::RenderingIntent, jxl::cms::RenderingIntent*) Line | Count | Source | 206 | 187k | Status Enum(const EnumT default_value, EnumT* JXL_RESTRICT value) { | 207 | 187k | uint32_t u32 = static_cast<uint32_t>(*value); | 208 | | // 00 -> 0 | 209 | | // 01 -> 1 | 210 | | // 10xxxx -> 2..17 | 211 | | // 11yyyyyy -> 18..81 | 212 | 187k | JXL_RETURN_IF_ERROR(U32(Val(0), Val(1), BitsOffset(4, 2), BitsOffset(6, 18), | 213 | 187k | static_cast<uint32_t>(default_value), &u32)); | 214 | 187k | *value = static_cast<EnumT>(u32); | 215 | 187k | return EnumValid(*value); | 216 | 187k | } |
jxl::Status jxl::Visitor::Enum<jxl::ExtraChannel>(jxl::ExtraChannel, jxl::ExtraChannel*) Line | Count | Source | 206 | 295k | Status Enum(const EnumT default_value, EnumT* JXL_RESTRICT value) { | 207 | 295k | uint32_t u32 = static_cast<uint32_t>(*value); | 208 | | // 00 -> 0 | 209 | | // 01 -> 1 | 210 | | // 10xxxx -> 2..17 | 211 | | // 11yyyyyy -> 18..81 | 212 | 295k | JXL_RETURN_IF_ERROR(U32(Val(0), Val(1), BitsOffset(4, 2), BitsOffset(6, 18), | 213 | 295k | static_cast<uint32_t>(default_value), &u32)); | 214 | 295k | *value = static_cast<EnumT>(u32); | 215 | 295k | return EnumValid(*value); | 216 | 295k | } |
|
217 | | |
218 | | virtual Status Bits(size_t bits, uint32_t default_value, |
219 | | uint32_t* JXL_RESTRICT value) = 0; |
220 | | virtual Status U64(uint64_t default_value, uint64_t* JXL_RESTRICT value) = 0; |
221 | | virtual Status F16(float default_value, float* JXL_RESTRICT value) = 0; |
222 | | |
223 | | // Returns whether VisitFields should visit some subsequent fields. |
224 | | // "condition" is typically from prior fields, e.g. flags. |
225 | | // Overridden by InitVisitor and MaxBitsVisitor. |
226 | 1.06M | virtual Status Conditional(bool condition) { return condition; } |
227 | | |
228 | | // Overridden by InitVisitor, AllDefaultVisitor and CanEncodeVisitor. |
229 | | virtual Status AllDefault(const Fields& /*fields*/, |
230 | 194k | bool* JXL_RESTRICT all_default) { |
231 | 194k | JXL_RETURN_IF_ERROR(Bool(true, all_default)); |
232 | 194k | return *all_default; |
233 | 194k | } |
234 | | |
235 | 1.77k | virtual void SetDefault(Fields* /*fields*/) { |
236 | | // Do nothing by default, this is overridden by ReadVisitor. |
237 | 1.77k | } |
238 | | |
239 | | // Returns the result of visiting a nested Bundle. |
240 | | // Overridden by InitVisitor. |
241 | 479k | virtual Status VisitNested(Fields* fields) { return Visit(fields); } |
242 | | |
243 | | // Overridden by ReadVisitor. Enables dynamically-sized fields. |
244 | 1.09M | virtual bool IsReading() const { return false; } |
245 | | |
246 | | virtual Status BeginExtensions(uint64_t* JXL_RESTRICT extensions) = 0; |
247 | | virtual Status EndExtensions() = 0; |
248 | | }; |
249 | | |
250 | | namespace fields_internal { |
251 | | // A bundle can be in one of three states concerning extensions: not-begun, |
252 | | // active, ended. Bundles may be nested, so we need a stack of states. |
253 | | class ExtensionStates { |
254 | | public: |
255 | 3.94M | void Push() { |
256 | | // Initial state = not-begun. |
257 | 3.94M | begun_ <<= 1; |
258 | 3.94M | ended_ <<= 1; |
259 | 3.94M | } |
260 | | |
261 | | // Clears current state; caller must check IsEnded beforehand. |
262 | 3.94M | void Pop() { |
263 | 3.94M | begun_ >>= 1; |
264 | 3.94M | ended_ >>= 1; |
265 | 3.94M | } |
266 | | |
267 | | // Returns true if state == active || state == ended. |
268 | 0 | Status IsBegun() const { return (begun_ & 1) != 0; } |
269 | | // Returns true if state != not-begun && state != active. |
270 | 0 | Status IsEnded() const { return (ended_ & 1) != 0; } |
271 | | |
272 | 364k | void Begin() { |
273 | 364k | JXL_DASSERT(!IsBegun()); |
274 | 364k | JXL_DASSERT(!IsEnded()); |
275 | 364k | begun_ += 1; |
276 | 364k | } |
277 | | |
278 | 364k | void End() { |
279 | 364k | JXL_DASSERT(IsBegun()); |
280 | 364k | JXL_DASSERT(!IsEnded()); |
281 | 364k | ended_ += 1; |
282 | 364k | } |
283 | | |
284 | | private: |
285 | | // Current state := least-significant bit of begun_ and ended_. |
286 | | uint64_t begun_ = 0; |
287 | | uint64_t ended_ = 0; |
288 | | }; |
289 | | |
290 | | // Visitors generate Init/AllDefault/Read/Write logic for all fields. Each |
291 | | // bundle's VisitFields member function calls visitor->U32 etc. We do not |
292 | | // overload operator() because a function name is easier to search for. |
293 | | |
294 | | class VisitorBase : public Visitor { |
295 | | public: |
296 | 3.46M | explicit VisitorBase() = default; |
297 | 3.46M | ~VisitorBase() override { JXL_DASSERT(depth_ == 0); } |
298 | | |
299 | | // This is the only call site of Fields::VisitFields. |
300 | | // Ensures EndExtensions was called. |
301 | 3.94M | Status Visit(Fields* fields) override { |
302 | 3.94M | JXL_ENSURE(depth_ < Bundle::kMaxExtensions); |
303 | 3.94M | depth_ += 1; |
304 | 3.94M | extension_states_.Push(); |
305 | | |
306 | 3.94M | const Status ok = fields->VisitFields(this); |
307 | | |
308 | 3.94M | if (ok) { |
309 | | // If VisitFields called BeginExtensions, must also call |
310 | | // EndExtensions. |
311 | 3.93M | JXL_DASSERT(!extension_states_.IsBegun() || extension_states_.IsEnded()); |
312 | 3.93M | } else { |
313 | | // Failed, undefined state: don't care whether EndExtensions was |
314 | | // called. |
315 | 8.85k | } |
316 | | |
317 | 3.94M | extension_states_.Pop(); |
318 | 3.94M | JXL_DASSERT(depth_ != 0); |
319 | 3.94M | depth_ -= 1; |
320 | | |
321 | 3.94M | return ok; |
322 | 3.94M | } |
323 | | |
324 | | // For visitors accepting a const Visitor, need to const-cast so we can call |
325 | | // the non-const Visitor::VisitFields. NOTE: C is not modified except the |
326 | | // `all_default` field by CanEncodeVisitor. |
327 | 68.8k | Status VisitConst(const Fields& t) { return Visit(const_cast<Fields*>(&t)); } |
328 | | |
329 | | // Derived types (overridden by InitVisitor because it is unsafe to read |
330 | | // from *value there) |
331 | | |
332 | 629k | Status Bool(bool default_value, bool* JXL_RESTRICT value) override { |
333 | 629k | uint32_t bits = *value ? 1 : 0; |
334 | 629k | JXL_RETURN_IF_ERROR(Bits(1, static_cast<uint32_t>(default_value), &bits)); |
335 | 628k | JXL_DASSERT(bits <= 1); |
336 | 628k | *value = bits == 1; |
337 | 628k | return true; |
338 | 629k | } |
339 | | |
340 | | // Overridden by ReadVisitor and WriteVisitor. |
341 | | // Called before any conditional visit based on "extensions". |
342 | | // Overridden by ReadVisitor, CanEncodeVisitor and WriteVisitor. |
343 | 364k | Status BeginExtensions(uint64_t* JXL_RESTRICT extensions) override { |
344 | 364k | JXL_RETURN_IF_ERROR(U64(0, extensions)); |
345 | | |
346 | 364k | extension_states_.Begin(); |
347 | 364k | return true; |
348 | 364k | } |
349 | | |
350 | | // Called after all extension fields (if any). Although non-extension |
351 | | // fields could be visited afterward, we prefer the convention that |
352 | | // extension fields are always the last to be visited. Overridden by |
353 | | // ReadVisitor. |
354 | 364k | Status EndExtensions() override { |
355 | 364k | extension_states_.End(); |
356 | 364k | return true; |
357 | 364k | } |
358 | | |
359 | | private: |
360 | | size_t depth_ = 0; // to check nesting |
361 | | ExtensionStates extension_states_; |
362 | | }; |
363 | | } // namespace fields_internal |
364 | | |
365 | | Status CheckHasEnoughBits(Visitor* visitor, size_t bits); |
366 | | |
367 | | } // namespace jxl |
368 | | |
369 | | #endif // LIB_JXL_FIELDS_H_ |