Coverage Report

Created: 2025-07-16 07:53

/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 max number of bits required to encode a T.
167
size_t MaxBits(const Fields& fields);
168
169
// Returns whether a header's fields can all be encoded, i.e. they have a
170
// valid representation. If so, "*total_bits" is the exact number of bits
171
// required. Called by Write.
172
Status CanEncode(const Fields& fields, size_t* JXL_RESTRICT extension_bits,
173
                 size_t* JXL_RESTRICT total_bits);
174
175
Status Read(BitReader* reader, Fields* JXL_RESTRICT fields);
176
177
// Returns whether enough bits are available to fully read this bundle using
178
// Read. Also returns true in case of a codestream error (other than not being
179
// large enough): that means enough bits are available to determine there's an
180
// error, use Read to get such error status.
181
// NOTE: this advances the BitReader, a different one pointing back at the
182
// original bit position in the codestream must be created to use Read after
183
// this.
184
bool CanRead(BitReader* reader, Fields* JXL_RESTRICT fields);
185
186
Status Write(const Fields& fields, BitWriter* JXL_RESTRICT writer,
187
             LayerType layer, AuxOut* aux_out);
188
}  // namespace Bundle
189
190
// Different subclasses of Visitor are passed to implementations of Fields
191
// throughout their lifetime. Templates used to be used for this but dynamic
192
// polymorphism produces more compact executables than template reification did.
193
class Visitor {
194
 public:
195
1.70M
  virtual ~Visitor() = default;
196
  virtual Status Visit(Fields* fields) = 0;
197
198
  virtual Status Bool(bool default_value, bool* JXL_RESTRICT value) = 0;
199
  virtual Status U32(U32Enc, uint32_t, uint32_t*) = 0;
200
201
  // Helper to construct U32Enc from U32Distr.
202
  Status U32(const U32Distr d0, const U32Distr d1, const U32Distr d2,
203
             const U32Distr d3, const uint32_t default_value,
204
3.33M
             uint32_t* JXL_RESTRICT value) {
205
3.33M
    return U32(U32Enc(d0, d1, d2, d3), default_value, value);
206
3.33M
  }
207
208
  template <typename EnumT>
209
267k
  Status Enum(const EnumT default_value, EnumT* JXL_RESTRICT value) {
210
267k
    uint32_t u32 = static_cast<uint32_t>(*value);
211
    // 00 -> 0
212
    // 01 -> 1
213
    // 10xxxx -> 2..17
214
    // 11yyyyyy -> 18..81
215
267k
    JXL_RETURN_IF_ERROR(U32(Val(0), Val(1), BitsOffset(4, 2), BitsOffset(6, 18),
216
267k
                            static_cast<uint32_t>(default_value), &u32));
217
267k
    *value = static_cast<EnumT>(u32);
218
267k
    return EnumValid(*value);
219
267k
  }
jxl::Status jxl::Visitor::Enum<jxl::cms::TransferFunction>(jxl::cms::TransferFunction, jxl::cms::TransferFunction*)
Line
Count
Source
209
50.3k
  Status Enum(const EnumT default_value, EnumT* JXL_RESTRICT value) {
210
50.3k
    uint32_t u32 = static_cast<uint32_t>(*value);
211
    // 00 -> 0
212
    // 01 -> 1
213
    // 10xxxx -> 2..17
214
    // 11yyyyyy -> 18..81
215
50.3k
    JXL_RETURN_IF_ERROR(U32(Val(0), Val(1), BitsOffset(4, 2), BitsOffset(6, 18),
216
50.3k
                            static_cast<uint32_t>(default_value), &u32));
217
50.3k
    *value = static_cast<EnumT>(u32);
218
50.3k
    return EnumValid(*value);
219
50.3k
  }
jxl::Status jxl::Visitor::Enum<jxl::cms::ColorSpace>(jxl::cms::ColorSpace, jxl::cms::ColorSpace*)
Line
Count
Source
209
54.9k
  Status Enum(const EnumT default_value, EnumT* JXL_RESTRICT value) {
210
54.9k
    uint32_t u32 = static_cast<uint32_t>(*value);
211
    // 00 -> 0
212
    // 01 -> 1
213
    // 10xxxx -> 2..17
214
    // 11yyyyyy -> 18..81
215
54.9k
    JXL_RETURN_IF_ERROR(U32(Val(0), Val(1), BitsOffset(4, 2), BitsOffset(6, 18),
216
54.9k
                            static_cast<uint32_t>(default_value), &u32));
217
54.9k
    *value = static_cast<EnumT>(u32);
218
54.9k
    return EnumValid(*value);
219
54.9k
  }
jxl::Status jxl::Visitor::Enum<jxl::cms::WhitePoint>(jxl::cms::WhitePoint, jxl::cms::WhitePoint*)
Line
Count
Source
209
50.5k
  Status Enum(const EnumT default_value, EnumT* JXL_RESTRICT value) {
210
50.5k
    uint32_t u32 = static_cast<uint32_t>(*value);
211
    // 00 -> 0
212
    // 01 -> 1
213
    // 10xxxx -> 2..17
214
    // 11yyyyyy -> 18..81
215
50.5k
    JXL_RETURN_IF_ERROR(U32(Val(0), Val(1), BitsOffset(4, 2), BitsOffset(6, 18),
216
50.5k
                            static_cast<uint32_t>(default_value), &u32));
217
50.5k
    *value = static_cast<EnumT>(u32);
218
50.5k
    return EnumValid(*value);
219
50.5k
  }
jxl::Status jxl::Visitor::Enum<jxl::cms::Primaries>(jxl::cms::Primaries, jxl::cms::Primaries*)
Line
Count
Source
209
50.3k
  Status Enum(const EnumT default_value, EnumT* JXL_RESTRICT value) {
210
50.3k
    uint32_t u32 = static_cast<uint32_t>(*value);
211
    // 00 -> 0
212
    // 01 -> 1
213
    // 10xxxx -> 2..17
214
    // 11yyyyyy -> 18..81
215
50.3k
    JXL_RETURN_IF_ERROR(U32(Val(0), Val(1), BitsOffset(4, 2), BitsOffset(6, 18),
216
50.3k
                            static_cast<uint32_t>(default_value), &u32));
217
50.3k
    *value = static_cast<EnumT>(u32);
218
50.3k
    return EnumValid(*value);
219
50.3k
  }
jxl::Status jxl::Visitor::Enum<jxl::cms::RenderingIntent>(jxl::cms::RenderingIntent, jxl::cms::RenderingIntent*)
Line
Count
Source
209
50.4k
  Status Enum(const EnumT default_value, EnumT* JXL_RESTRICT value) {
210
50.4k
    uint32_t u32 = static_cast<uint32_t>(*value);
211
    // 00 -> 0
212
    // 01 -> 1
213
    // 10xxxx -> 2..17
214
    // 11yyyyyy -> 18..81
215
50.4k
    JXL_RETURN_IF_ERROR(U32(Val(0), Val(1), BitsOffset(4, 2), BitsOffset(6, 18),
216
50.4k
                            static_cast<uint32_t>(default_value), &u32));
217
50.4k
    *value = static_cast<EnumT>(u32);
218
50.4k
    return EnumValid(*value);
219
50.4k
  }
jxl::Status jxl::Visitor::Enum<jxl::ExtraChannel>(jxl::ExtraChannel, jxl::ExtraChannel*)
Line
Count
Source
209
10.8k
  Status Enum(const EnumT default_value, EnumT* JXL_RESTRICT value) {
210
10.8k
    uint32_t u32 = static_cast<uint32_t>(*value);
211
    // 00 -> 0
212
    // 01 -> 1
213
    // 10xxxx -> 2..17
214
    // 11yyyyyy -> 18..81
215
10.8k
    JXL_RETURN_IF_ERROR(U32(Val(0), Val(1), BitsOffset(4, 2), BitsOffset(6, 18),
216
10.8k
                            static_cast<uint32_t>(default_value), &u32));
217
10.8k
    *value = static_cast<EnumT>(u32);
218
10.8k
    return EnumValid(*value);
219
10.8k
  }
220
221
  virtual Status Bits(size_t bits, uint32_t default_value,
222
                      uint32_t* JXL_RESTRICT value) = 0;
223
  virtual Status U64(uint64_t default_value, uint64_t* JXL_RESTRICT value) = 0;
224
  virtual Status F16(float default_value, float* JXL_RESTRICT value) = 0;
225
226
  // Returns whether VisitFields should visit some subsequent fields.
227
  // "condition" is typically from prior fields, e.g. flags.
228
  // Overridden by InitVisitor and MaxBitsVisitor.
229
1.52M
  virtual Status Conditional(bool condition) { return condition; }
230
231
  // Overridden by InitVisitor, AllDefaultVisitor and CanEncodeVisitor.
232
  virtual Status AllDefault(const Fields& /*fields*/,
233
182k
                            bool* JXL_RESTRICT all_default) {
234
182k
    JXL_RETURN_IF_ERROR(Bool(true, all_default));
235
182k
    return *all_default;
236
182k
  }
237
238
0
  virtual void SetDefault(Fields* /*fields*/) {
239
    // Do nothing by default, this is overridden by ReadVisitor.
240
0
  }
241
242
  // Returns the result of visiting a nested Bundle.
243
  // Overridden by InitVisitor.
244
343k
  virtual Status VisitNested(Fields* fields) { return Visit(fields); }
245
246
  // Overridden by ReadVisitor. Enables dynamically-sized fields.
247
674k
  virtual bool IsReading() const { return false; }
248
249
  virtual Status BeginExtensions(uint64_t* JXL_RESTRICT extensions) = 0;
250
  virtual Status EndExtensions() = 0;
251
};
252
253
namespace fields_internal {
254
// A bundle can be in one of three states concerning extensions: not-begun,
255
// active, ended. Bundles may be nested, so we need a stack of states.
256
class ExtensionStates {
257
 public:
258
2.04M
  void Push() {
259
    // Initial state = not-begun.
260
2.04M
    begun_ <<= 1;
261
2.04M
    ended_ <<= 1;
262
2.04M
  }
263
264
  // Clears current state; caller must check IsEnded beforehand.
265
2.04M
  void Pop() {
266
2.04M
    begun_ >>= 1;
267
2.04M
    ended_ >>= 1;
268
2.04M
  }
269
270
  // Returns true if state == active || state == ended.
271
0
  Status IsBegun() const { return (begun_ & 1) != 0; }
272
  // Returns true if state != not-begun && state != active.
273
0
  Status IsEnded() const { return (ended_ & 1) != 0; }
274
275
402k
  void Begin() {
276
402k
    JXL_DASSERT(!IsBegun());
277
402k
    JXL_DASSERT(!IsEnded());
278
402k
    begun_ += 1;
279
402k
  }
280
281
402k
  void End() {
282
402k
    JXL_DASSERT(IsBegun());
283
402k
    JXL_DASSERT(!IsEnded());
284
402k
    ended_ += 1;
285
402k
  }
286
287
 private:
288
  // Current state := least-significant bit of begun_ and ended_.
289
  uint64_t begun_ = 0;
290
  uint64_t ended_ = 0;
291
};
292
293
// Visitors generate Init/AllDefault/Read/Write logic for all fields. Each
294
// bundle's VisitFields member function calls visitor->U32 etc. We do not
295
// overload operator() because a function name is easier to search for.
296
297
class VisitorBase : public Visitor {
298
 public:
299
1.70M
  explicit VisitorBase() = default;
300
1.70M
  ~VisitorBase() override { JXL_DASSERT(depth_ == 0); }
301
302
  // This is the only call site of Fields::VisitFields.
303
  // Ensures EndExtensions was called.
304
2.04M
  Status Visit(Fields* fields) override {
305
2.04M
    JXL_ENSURE(depth_ < Bundle::kMaxExtensions);
306
2.04M
    depth_ += 1;
307
2.04M
    extension_states_.Push();
308
309
2.04M
    const Status ok = fields->VisitFields(this);
310
311
2.04M
    if (ok) {
312
      // If VisitFields called BeginExtensions, must also call
313
      // EndExtensions.
314
2.04M
      JXL_DASSERT(!extension_states_.IsBegun() || extension_states_.IsEnded());
315
2.04M
    } else {
316
      // Failed, undefined state: don't care whether EndExtensions was
317
      // called.
318
4.72k
    }
319
320
2.04M
    extension_states_.Pop();
321
2.04M
    JXL_DASSERT(depth_ != 0);
322
2.04M
    depth_ -= 1;
323
324
2.04M
    return ok;
325
2.04M
  }
326
327
  // For visitors accepting a const Visitor, need to const-cast so we can call
328
  // the non-const Visitor::VisitFields. NOTE: C is not modified except the
329
  // `all_default` field by CanEncodeVisitor.
330
4.84k
  Status VisitConst(const Fields& t) { return Visit(const_cast<Fields*>(&t)); }
331
332
  // Derived types (overridden by InitVisitor because it is unsafe to read
333
  // from *value there)
334
335
720k
  Status Bool(bool default_value, bool* JXL_RESTRICT value) override {
336
720k
    uint32_t bits = *value ? 1 : 0;
337
720k
    JXL_RETURN_IF_ERROR(Bits(1, static_cast<uint32_t>(default_value), &bits));
338
719k
    JXL_DASSERT(bits <= 1);
339
719k
    *value = bits == 1;
340
719k
    return true;
341
720k
  }
342
343
  // Overridden by ReadVisitor and WriteVisitor.
344
  // Called before any conditional visit based on "extensions".
345
  // Overridden by ReadVisitor, CanEncodeVisitor and WriteVisitor.
346
402k
  Status BeginExtensions(uint64_t* JXL_RESTRICT extensions) override {
347
402k
    JXL_RETURN_IF_ERROR(U64(0, extensions));
348
349
402k
    extension_states_.Begin();
350
402k
    return true;
351
402k
  }
352
353
  // Called after all extension fields (if any). Although non-extension
354
  // fields could be visited afterward, we prefer the convention that
355
  // extension fields are always the last to be visited. Overridden by
356
  // ReadVisitor.
357
402k
  Status EndExtensions() override {
358
402k
    extension_states_.End();
359
402k
    return true;
360
402k
  }
361
362
 private:
363
  size_t depth_ = 0;  // to check nesting
364
  ExtensionStates extension_states_;
365
};
366
}  // namespace fields_internal
367
368
Status CheckHasEnoughBits(Visitor* visitor, size_t bits);
369
370
}  // namespace jxl
371
372
#endif  // LIB_JXL_FIELDS_H_