Coverage Report

Created: 2022-08-24 06:33

/src/libjxl/lib/jxl/enc_bit_writer.cc
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
#include "lib/jxl/enc_bit_writer.h"
7
8
#include <string.h>  // memcpy
9
10
#include "lib/jxl/base/byte_order.h"
11
#include "lib/jxl/dec_bit_reader.h"
12
13
namespace jxl {
14
15
BitWriter::Allotment::Allotment(BitWriter* JXL_RESTRICT writer, size_t max_bits)
16
3.68k
    : max_bits_(max_bits) {
17
3.68k
  if (writer == nullptr) return;
18
3.31k
  prev_bits_written_ = writer->BitsWritten();
19
3.31k
  const size_t prev_bytes = writer->storage_.size();
20
3.31k
  const size_t next_bytes = DivCeil(max_bits, kBitsPerByte);
21
3.31k
  writer->storage_.resize(prev_bytes + next_bytes);
22
3.31k
  parent_ = writer->current_allotment_;
23
3.31k
  writer->current_allotment_ = this;
24
3.31k
}
25
26
3.68k
BitWriter::Allotment::~Allotment() {
27
3.68k
  if (!called_) {
28
    // Not calling is a bug - unused storage will not be reclaimed.
29
0
    JXL_ABORT("Did not call Allotment::ReclaimUnused");
30
0
  }
31
3.68k
}
32
33
1.10k
void BitWriter::Allotment::FinishedHistogram(BitWriter* JXL_RESTRICT writer) {
34
1.10k
  if (writer == nullptr) return;
35
736
  JXL_ASSERT(!called_);              // Call before ReclaimUnused
36
736
  JXL_ASSERT(histogram_bits_ == 0);  // Do not call twice
37
736
  JXL_ASSERT(writer->BitsWritten() >= prev_bits_written_);
38
736
  histogram_bits_ = writer->BitsWritten() - prev_bits_written_;
39
736
}
40
41
void BitWriter::Allotment::PrivateReclaim(BitWriter* JXL_RESTRICT writer,
42
                                          size_t* JXL_RESTRICT used_bits,
43
3.68k
                                          size_t* JXL_RESTRICT unused_bits) {
44
3.68k
  JXL_ASSERT(!called_);  // Do not call twice
45
3.68k
  called_ = true;
46
3.68k
  if (writer == nullptr) return;
47
48
3.31k
  JXL_ASSERT(writer->BitsWritten() >= prev_bits_written_);
49
3.31k
  *used_bits = writer->BitsWritten() - prev_bits_written_;
50
3.31k
  JXL_ASSERT(*used_bits <= max_bits_);
51
3.31k
  *unused_bits = max_bits_ - *used_bits;
52
53
  // Reclaim unused bytes whole bytes from writer's allotment.
54
3.31k
  const size_t unused_bytes = *unused_bits / kBitsPerByte;  // truncate
55
3.31k
  JXL_ASSERT(writer->storage_.size() >= unused_bytes);
56
3.31k
  writer->storage_.resize(writer->storage_.size() - unused_bytes);
57
3.31k
  writer->current_allotment_ = parent_;
58
  // Ensure we don't also charge the parent for these bits.
59
3.31k
  auto parent = parent_;
60
4.32k
  while (parent != nullptr) {
61
1.01k
    parent->prev_bits_written_ += *used_bits;
62
1.01k
    parent = parent->parent_;
63
1.01k
  }
64
3.31k
}
65
66
0
void BitWriter::AppendByteAligned(const Span<const uint8_t>& span) {
67
0
  if (!span.size()) return;
68
0
  storage_.resize(storage_.size() + span.size() + 1);  // extra zero padding
69
70
  // Concatenate by copying bytes because both source and destination are bytes.
71
0
  JXL_ASSERT(BitsWritten() % kBitsPerByte == 0);
72
0
  size_t pos = BitsWritten() / kBitsPerByte;
73
0
  memcpy(storage_.data() + pos, span.data(), span.size());
74
0
  pos += span.size();
75
0
  storage_[pos++] = 0;  // for next Write
76
0
  JXL_ASSERT(pos <= storage_.size());
77
0
  bits_written_ += span.size() * kBitsPerByte;
78
0
}
79
80
0
void BitWriter::AppendByteAligned(const BitWriter& other) {
81
0
  JXL_ASSERT(other.BitsWritten() % kBitsPerByte == 0);
82
0
  JXL_ASSERT(other.BitsWritten() / kBitsPerByte != 0);
83
84
0
  AppendByteAligned(other.GetSpan());
85
0
}
86
87
92
void BitWriter::AppendByteAligned(const std::vector<BitWriter>& others) {
88
  // Total size to add so we can preallocate
89
92
  size_t other_bytes = 0;
90
92
  for (const BitWriter& writer : others) {
91
92
    JXL_ASSERT(writer.BitsWritten() % kBitsPerByte == 0);
92
92
    other_bytes += writer.BitsWritten() / kBitsPerByte;
93
92
  }
94
92
  if (other_bytes == 0) {
95
    // No bytes to append: this happens for example when creating per-group
96
    // storage for groups, but not writing anything in them for e.g. lossless
97
    // images with no alpha. Do nothing.
98
0
    return;
99
0
  }
100
92
  storage_.resize(storage_.size() + other_bytes + 1);  // extra zero padding
101
102
  // Concatenate by copying bytes because both source and destination are bytes.
103
92
  JXL_ASSERT(BitsWritten() % kBitsPerByte == 0);
104
92
  size_t pos = BitsWritten() / kBitsPerByte;
105
92
  for (const BitWriter& writer : others) {
106
92
    const Span<const uint8_t> span = writer.GetSpan();
107
92
    if (!span.empty()) {
108
92
      memcpy(storage_.data() + pos, span.data(), span.size());
109
92
      pos += span.size();
110
92
    }
111
92
  }
112
92
  storage_[pos++] = 0;  // for next Write
113
92
  JXL_ASSERT(pos <= storage_.size());
114
92
  bits_written_ += other_bytes * kBitsPerByte;
115
92
}
116
117
// TODO(lode): avoid code duplication
118
void BitWriter::AppendByteAligned(
119
92
    const std::vector<std::unique_ptr<BitWriter>>& others) {
120
  // Total size to add so we can preallocate
121
92
  size_t other_bytes = 0;
122
92
  for (const auto& writer : others) {
123
0
    JXL_ASSERT(writer->BitsWritten() % kBitsPerByte == 0);
124
0
    other_bytes += writer->BitsWritten() / kBitsPerByte;
125
0
  }
126
92
  if (other_bytes == 0) {
127
    // No bytes to append: this happens for example when creating per-group
128
    // storage for groups, but not writing anything in them for e.g. lossless
129
    // images with no alpha. Do nothing.
130
92
    return;
131
92
  }
132
0
  storage_.resize(storage_.size() + other_bytes + 1);  // extra zero padding
133
134
  // Concatenate by copying bytes because both source and destination are bytes.
135
0
  JXL_ASSERT(BitsWritten() % kBitsPerByte == 0);
136
0
  size_t pos = BitsWritten() / kBitsPerByte;
137
0
  for (const auto& writer : others) {
138
0
    const Span<const uint8_t> span = writer->GetSpan();
139
0
    memcpy(storage_.data() + pos, span.data(), span.size());
140
0
    pos += span.size();
141
0
  }
142
0
  storage_[pos++] = 0;  // for next Write
143
0
  JXL_ASSERT(pos <= storage_.size());
144
0
  bits_written_ += other_bytes * kBitsPerByte;
145
0
}
146
147
// Example: let's assume that 3 bits (Rs below) have been written already:
148
// BYTE+0       BYTE+1       BYTE+2
149
// 0000 0RRR    ???? ????    ???? ????
150
//
151
// Now, we could write up to 5 bits by just shifting them left by 3 bits and
152
// OR'ing to BYTE-0.
153
//
154
// For n > 5 bits, we write the lowest 5 bits as above, then write the next
155
// lowest bits into BYTE+1 starting from its lower bits and so on.
156
22.2k
void BitWriter::Write(size_t n_bits, uint64_t bits) {
157
22.2k
  JXL_DASSERT((bits >> n_bits) == 0);
158
22.2k
  JXL_DASSERT(n_bits <= kMaxBitsPerCall);
159
22.2k
  uint8_t* p = &storage_[bits_written_ / kBitsPerByte];
160
22.2k
  const size_t bits_in_first_byte = bits_written_ % kBitsPerByte;
161
22.2k
  bits <<= bits_in_first_byte;
162
22.2k
#if JXL_BYTE_ORDER_LITTLE
163
22.2k
  uint64_t v = *p;
164
  // Last (partial) or next byte to write must be zero-initialized!
165
  // PaddedBytes initializes the first, and Write/Append maintain this.
166
22.2k
  JXL_DASSERT(v >> bits_in_first_byte == 0);
167
22.2k
  v |= bits;
168
22.2k
  memcpy(p, &v, sizeof(v));  // Write bytes: possibly more than n_bits/8
169
#else
170
  *p++ |= static_cast<uint8_t>(bits & 0xFF);
171
  for (size_t bits_left_to_write = n_bits + bits_in_first_byte;
172
       bits_left_to_write >= 9; bits_left_to_write -= 8) {
173
    bits >>= 8;
174
    *p++ = static_cast<uint8_t>(bits & 0xFF);
175
  }
176
  *p = 0;
177
#endif
178
22.2k
  bits_written_ += n_bits;
179
22.2k
}
180
}  // namespace jxl