Coverage Report

Created: 2026-03-31 07:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libjxl/lib/jxl/enc_bit_writer.h
Line
Count
Source
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_ENC_BIT_WRITER_H_
7
#define LIB_JXL_ENC_BIT_WRITER_H_
8
9
// BitWriter class: unbuffered writes using unaligned 64-bit stores.
10
11
#include <jxl/memory_manager.h>
12
13
#include <cstddef>
14
#include <cstdint>
15
#include <functional>
16
#include <memory>
17
#include <utility>
18
#include <vector>
19
20
#include "lib/jxl/base/common.h"
21
#include "lib/jxl/base/compiler_specific.h"
22
#include "lib/jxl/base/span.h"
23
#include "lib/jxl/base/status.h"
24
#include "lib/jxl/padded_bytes.h"
25
26
namespace jxl {
27
28
struct AuxOut;
29
enum class LayerType : uint8_t;
30
31
struct BitWriter {
32
  // Upper bound on `n_bits` in each call to Write. We shift a 64-bit word by
33
  // 7 bits (max already valid bits in the last byte) and at least 1 bit is
34
  // needed to zero-initialize the bit-stream ahead (i.e. if 7 bits are valid
35
  // and we write 57 bits, then the next write will access a byte that was not
36
  // yet zero-initialized).
37
  static constexpr size_t kMaxBitsPerCall = 56;
38
39
  explicit BitWriter(JxlMemoryManager* memory_manager)
40
2.01k
      : bits_written_(0), storage_(memory_manager) {}
41
42
  // Disallow copying - may lead to bugs.
43
  BitWriter(const BitWriter&) = delete;
44
  BitWriter& operator=(const BitWriter&) = delete;
45
0
  BitWriter(BitWriter&&) = default;
46
  BitWriter& operator=(BitWriter&&) = default;
47
48
51.0k
  size_t BitsWritten() const { return bits_written_; }
49
50
1.00k
  JxlMemoryManager* memory_manager() const { return storage_.memory_manager(); }
51
52
672
  Span<const uint8_t> GetSpan() const {
53
    // Callers must ensure byte alignment to avoid uninitialized bits.
54
672
    JXL_DASSERT(bits_written_ % kBitsPerByte == 0);
55
672
    return Bytes(storage_.data(), DivCeil(bits_written_, kBitsPerByte));
56
672
  }
57
58
  // Example usage: bytes = std::move(writer).TakeBytes(); Useful for the
59
  // top-level encoder which returns PaddedBytes, not a BitWriter.
60
  // *this must be an rvalue reference and is invalid afterwards.
61
672
  PaddedBytes&& TakeBytes() && {
62
    // Callers must ensure byte alignment to avoid uninitialized bits.
63
672
    JXL_DASSERT(bits_written_ % kBitsPerByte == 0);
64
672
    Status status = storage_.resize(DivCeil(bits_written_, kBitsPerByte));
65
672
    JXL_DASSERT(status);
66
    // Can never fail, because we are resizing to a lower size.
67
672
    (void)status;
68
672
    return std::move(storage_);
69
672
  }
70
71
0
  Status Shrink() { return storage_.shrink_to_fit(); }
72
73
  // Must be byte-aligned before calling.
74
  Status AppendByteAligned(const Span<const uint8_t>& span);
75
76
  // NOTE: no allotment needed, the other BitWriters have already been charged.
77
  Status AppendByteAligned(
78
      const std::vector<std::unique_ptr<BitWriter>>& others);
79
80
  Status AppendUnaligned(const BitWriter& other);
81
82
  // Writes bits into bytes in increasing addresses, and within a byte
83
  // least-significant-bit first.
84
  //
85
  // The function can write up to 56 bits in one go.
86
  void Write(size_t n_bits, uint64_t bits);
87
88
  // This should only rarely be used - e.g. when the current location will be
89
  // referenced via byte offset (TOCs point to groups), or byte-aligned reading
90
  // is required for speed.
91
1.68k
  void ZeroPadToByte() {
92
1.68k
    const size_t remainder_bits =
93
1.68k
        RoundUpBitsToByteMultiple(bits_written_) - bits_written_;
94
1.68k
    if (remainder_bits == 0) return;
95
1.68k
    Write(remainder_bits, 0);
96
1.68k
    JXL_DASSERT(bits_written_ % kBitsPerByte == 0);
97
1.68k
  }
98
99
  Status WithMaxBits(size_t max_bits, LayerType layer,
100
                     AuxOut* JXL_RESTRICT aux_out,
101
                     const std::function<Status()>& function,
102
                     bool finished_histogram = false);
103
104
 private:
105
  class Allotment {
106
   public:
107
    explicit Allotment(size_t max_bits);
108
    ~Allotment();
109
110
    Allotment(const Allotment& other) = delete;
111
    Allotment(Allotment&& other) = delete;
112
    Allotment& operator=(const Allotment&) = delete;
113
    Allotment& operator=(Allotment&&) = delete;
114
115
    // Call after writing a histogram, but before ReclaimUnused.
116
    Status FinishedHistogram(BitWriter* JXL_RESTRICT writer);
117
118
0
    size_t HistogramBits() const {
119
0
      JXL_DASSERT(called_);
120
0
      return histogram_bits_;
121
0
    }
122
123
    Status ReclaimAndCharge(BitWriter* JXL_RESTRICT writer, LayerType layer,
124
                            AuxOut* JXL_RESTRICT aux_out);
125
126
   private:
127
    friend struct BitWriter;
128
129
    // Expands a BitWriter's storage. Must happen before calling Write or
130
    // ZeroPadToByte. Must call ReclaimUnused after writing to reclaim the
131
    // unused storage so that BitWriter memory use remains tightly bounded.
132
    Status Init(BitWriter* JXL_RESTRICT writer);
133
134
    Status PrivateReclaim(BitWriter* JXL_RESTRICT writer,
135
                          size_t* JXL_RESTRICT used_bits,
136
                          size_t* JXL_RESTRICT unused_bits);
137
138
    size_t prev_bits_written_;
139
    const size_t max_bits_;
140
    size_t histogram_bits_ = 0;
141
    bool called_ = false;
142
    Allotment* parent_;
143
  };
144
145
  size_t bits_written_;
146
  PaddedBytes storage_;
147
  Allotment* current_allotment_ = nullptr;
148
};
149
150
}  // namespace jxl
151
152
#endif  // LIB_JXL_ENC_BIT_WRITER_H_