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