Coverage Report

Created: 2022-08-24 06:04

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