1 /*
2 * Copyright (c) 2023, Alliance for Open Media. All rights reserved
3 *
4 * This source code is subject to the terms of the BSD 3-Clause Clear License
5 * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear
6 * License was not distributed with this source code in the LICENSE file, you
7 * can obtain it at www.aomedia.org/license/software-license/bsd-3-c-c. If the
8 * Alliance for Open Media Patent License 1.0 was not distributed with this
9 * source code in the PATENTS file, you can obtain it at
10 * www.aomedia.org/license/patent.
11 */
12 #include "iamf/common/write_bit_buffer.h"
13
14 #include <algorithm>
15 #include <cstdint>
16 #include <fstream>
17 #include <iterator>
18 #include <optional>
19 #include <string>
20 #include <vector>
21
22 #include "absl/log/log.h"
23 #include "absl/status/status.h"
24 #include "absl/strings/str_cat.h"
25 #include "absl/types/span.h"
26 #include "iamf/common/leb_generator.h"
27 #include "iamf/common/utils/bit_buffer_util.h"
28 #include "iamf/common/utils/macros.h"
29 #include "iamf/obu/types.h"
30
31 namespace iamf_tools {
32
33 namespace {
34
MaybeResizeBufferToFitNumBits(int num_bits,int64_t bit_offset,std::vector<uint8_t> & bit_buffer)35 void MaybeResizeBufferToFitNumBits(int num_bits, int64_t bit_offset,
36 std::vector<uint8_t>& bit_buffer) {
37 const int64_t size = static_cast<int64_t>(bit_buffer.size());
38 if (bit_offset + num_bits <= size * 8) {
39 return;
40 }
41
42 const int64_t required_bytes = ((bit_offset + num_bits) / 8) +
43 ((bit_offset + num_bits) % 8 == 0 ? 0 : 1);
44
45 // Adjust the size of the buffer.
46 bit_buffer.resize(required_bytes, 0);
47 }
48
49 // Write one bit to the buffer using an AND or OR mask. All unwritten bits are
50 // unchanged. This function is designed to work even if the buffer has
51 // uninitialized data.
WriteBit(int bit,int64_t & bit_offset,std::vector<uint8_t> & bit_buffer)52 absl::Status WriteBit(int bit, int64_t& bit_offset,
53 std::vector<uint8_t>& bit_buffer) {
54 if (bit_offset < 0) {
55 return absl::InvalidArgumentError("The bit offset should not be negative.");
56 }
57 const int64_t off = bit_offset;
58 const int64_t p = off >> 3;
59 const int q = 7 - static_cast<int>(off & 0x7);
60
61 if (bit == 0) {
62 // AND mask to set the target bit to 0 and leave others unchanged.
63 bit_buffer[p] &= ~(1 << q);
64 } else {
65 // OR mask to set the target bit to 1 and leave others unchanged.
66 bit_buffer[p] |= (1 << q);
67 }
68 bit_offset = off + 1;
69
70 return absl::OkStatus();
71 }
72
73 // A helper function to write out n = `num_bits` bits to the buffer. These
74 // are the lower n bits of uint64_t `data`. n must be <= 64.
InternalWriteUnsigned(int max_bits,uint64_t data,int num_bits,int64_t & bit_offset,std::vector<uint8_t> & bit_buffer)75 absl::Status InternalWriteUnsigned(int max_bits, uint64_t data, int num_bits,
76 int64_t& bit_offset,
77 std::vector<uint8_t>& bit_buffer) {
78 if (num_bits < 0) {
79 return absl::InvalidArgumentError("num_bits cannot be negative.");
80 }
81 if (num_bits == 0) {
82 return absl::OkStatus();
83 }
84 // The `uint64` input limits this function to only write 64 bits at a time.
85 if (max_bits > 64) {
86 return absl::InvalidArgumentError("max_bits cannot be greater than 64.");
87 }
88
89 // Check the calling function's limitation to guard against unexpected
90 // behavior.
91 if (num_bits > max_bits) {
92 return absl::InvalidArgumentError(
93 absl::StrCat("num_bits= ", num_bits,
94 " cannot be greater than max_bits= ", max_bits, "."));
95 }
96
97 // Check if there would be any non-zero bits left after writing. Avoid
98 // shifting by 64 which results in undefined behavior. This is not possible
99 // when writing out 64 bits.
100 if (num_bits != 64 && (data >> num_bits) != 0) {
101 return absl::InvalidArgumentError(
102 absl::StrCat("There is more bits of data in the provided uint64 "
103 "than requested for writing. num_bits= ",
104 num_bits, " data= ", data));
105 }
106
107 // Expand the buffer and pad the input data with zeroes.
108 MaybeResizeBufferToFitNumBits(num_bits, bit_offset, bit_buffer);
109
110 if (bit_offset % 8 == 0 && num_bits % 8 == 0) {
111 // Short-circuit the common case of writing a byte-aligned input to a
112 // byte-aligned output. Copy one byte at a time.
113 for (int byte = (num_bits / 8) - 1; byte >= 0; byte--) {
114 bit_buffer[bit_offset / 8] = (data >> (byte * 8)) & 0xff;
115 bit_offset += 8;
116 }
117 } else {
118 // The input and/or output are not byte-aligned. Write one bit at
119 // a time.
120 for (int bit = num_bits - 1; bit >= 0; bit--) {
121 RETURN_IF_NOT_OK(WriteBit((data >> bit) & 1, bit_offset, bit_buffer));
122 }
123 }
124
125 return absl::OkStatus();
126 }
127
WriteBufferToFile(const std::vector<uint8_t> & buffer,std::fstream & output_file)128 absl::Status WriteBufferToFile(const std::vector<uint8_t>& buffer,
129 std::fstream& output_file) {
130 if (!output_file.is_open()) {
131 return absl::UnknownError("Expected file to be opened.");
132 }
133 output_file.write(reinterpret_cast<const char*>(buffer.data()),
134 buffer.size());
135
136 if (output_file.bad()) {
137 return absl::UnknownError("Writing to file failed.");
138 }
139
140 return absl::OkStatus();
141 }
142
143 } // namespace
144
WriteBitBuffer(int64_t initial_capacity,const LebGenerator & leb_generator)145 WriteBitBuffer::WriteBitBuffer(int64_t initial_capacity,
146 const LebGenerator& leb_generator)
147 : leb_generator_(leb_generator), bit_buffer_(), bit_offset_(0) {
148 bit_buffer_.reserve(initial_capacity);
149 }
150
151 // Writes n = `num_bits` bits to the buffer. These are the lower n bits of
152 // uint32_t `data`. n must be <= 32.
WriteUnsignedLiteral(uint32_t data,int num_bits)153 absl::Status WriteBitBuffer::WriteUnsignedLiteral(uint32_t data, int num_bits) {
154 return InternalWriteUnsigned(32, static_cast<uint64_t>(data), num_bits,
155 bit_offset_, bit_buffer_);
156 }
157
158 // Writes n = `num_bits` bits to the buffer. These are the lower n bits of
159 // uint64_t `data`. n must be <= 64.
WriteUnsignedLiteral64(uint64_t data,int num_bits)160 absl::Status WriteBitBuffer::WriteUnsignedLiteral64(uint64_t data,
161 int num_bits) {
162 return InternalWriteUnsigned(64, data, num_bits, bit_offset_, bit_buffer_);
163 }
164
165 // Writes a standard int8_t in two's complement form to the write buffer. No
166 // special conversion needed as the raw value is in the correct format.
WriteSigned8(int8_t data)167 absl::Status WriteBitBuffer::WriteSigned8(int8_t data) {
168 return WriteUnsignedLiteral((static_cast<uint32_t>(data)) & 0xff, 8);
169 }
170
171 // Writes a standard int16_t in two's complement form to the write buffer. No
172 // special conversion needed as the raw value is in the correct format.
WriteSigned16(int16_t data)173 absl::Status WriteBitBuffer::WriteSigned16(int16_t data) {
174 return WriteUnsignedLiteral(static_cast<uint32_t>(data) & 0xffff, 16);
175 }
176
177 // Writes a null terminated C-style string to the buffer - including the null.
WriteString(const std::string & data)178 absl::Status WriteBitBuffer::WriteString(const std::string& data) {
179 if (data.size() > kIamfMaxStringSize - 1) { // -1 for the NULL terminator.
180 return absl::InvalidArgumentError(
181 absl::StrCat("String length, ", data.size(),
182 ", (including the NULL terminator) is longer than the "
183 "allowed maximum of ",
184 kIamfMaxStringSize));
185 }
186 if (data.empty()) {
187 RETURN_IF_NOT_OK(WriteUnsignedLiteral(static_cast<uint8_t>('\0'), 8));
188 return absl::OkStatus();
189 }
190 for (int i = 0; i < data.size(); ++i) {
191 if (data[i] == '\0') {
192 return absl::InvalidArgumentError(
193 "String contains an internal null terminator");
194 }
195 }
196 for (int i = 0; i < data.size(); ++i) {
197 // Note that some systems have `char` as signed and others unsigned. Write
198 // the same raw byte value regardless.
199 const uint8_t byte = static_cast<uint8_t>(data[i]);
200 RETURN_IF_NOT_OK(WriteUnsignedLiteral(byte, 8));
201 }
202 RETURN_IF_NOT_OK(WriteUnsignedLiteral(static_cast<uint8_t>('\0'), 8));
203 return absl::OkStatus();
204 }
205
WriteUleb128(const DecodedUleb128 data)206 absl::Status WriteBitBuffer::WriteUleb128(const DecodedUleb128 data) {
207 // Transform data to a temporary buffer. Then write it.
208 std::vector<uint8_t> buffer;
209 RETURN_IF_NOT_OK(leb_generator_.Uleb128ToUint8Vector(data, buffer));
210 RETURN_IF_NOT_OK(WriteUint8Span(absl::MakeConstSpan(buffer)));
211 return absl::OkStatus();
212 }
213
WriteIso14496_1Expanded(uint32_t size_of_instance)214 absl::Status WriteBitBuffer::WriteIso14496_1Expanded(
215 uint32_t size_of_instance) {
216 constexpr uint8_t kSizeOfInstanceMask = 0x7f;
217 constexpr uint8_t kNextByteMask = 0x80;
218 std::vector<uint8_t> buffer;
219
220 // Fill the buffer in reverse order. After the loop the most significant bits
221 // will be at the start of the buffer.
222 do {
223 const uint8_t byte =
224 (size_of_instance & kSizeOfInstanceMask) | kNextByteMask;
225 buffer.insert(buffer.begin(), byte);
226
227 size_of_instance >>= 7;
228 } while (size_of_instance > 0);
229 // Ensure the last byte signals the end of the data.
230 buffer.back() &= kSizeOfInstanceMask;
231
232 return WriteUint8Span(absl::MakeConstSpan(buffer));
233 }
234
WriteUint8Span(absl::Span<const uint8_t> data)235 absl::Status WriteBitBuffer::WriteUint8Span(absl::Span<const uint8_t> data) {
236 if (IsByteAligned()) {
237 // In the common case we can just copy all of the data over and update
238 // `bit_offset_`.
239 bit_buffer_.reserve(bit_buffer_.size() + data.size());
240 std::copy(data.begin(), data.end(), std::back_inserter(bit_buffer_));
241 bit_offset_ += 8 * data.size();
242 return absl::OkStatus();
243 }
244
245 // Expand the buffer to fit the data for efficiency when processing large
246 // input.
247 RETURN_IF_NOT_OK(CanWriteBytes(true, data.size(), bit_offset_, bit_buffer_));
248
249 // The buffer is mis-aligned. Copy it over one byte at a time.
250 for (const uint8_t& value : data) {
251 RETURN_IF_NOT_OK(WriteUnsignedLiteral(value, 8));
252 }
253 return absl::OkStatus();
254 }
255
FlushAndWriteToFile(std::optional<std::fstream> & output_file)256 absl::Status WriteBitBuffer::FlushAndWriteToFile(
257 std::optional<std::fstream>& output_file) {
258 if (!IsByteAligned()) {
259 return absl::InvalidArgumentError("Write buffer not byte-aligned");
260 }
261
262 if (output_file.has_value()) {
263 bit_buffer_.resize(bit_offset_ / 8);
264 RETURN_IF_NOT_OK(WriteBufferToFile(bit_buffer_, *output_file));
265 }
266
267 if (bit_offset_ > 0) {
268 LOG_EVERY_POW_2(INFO) << "Flushing " << bit_offset_ / 8 << " bytes";
269 }
270 Reset();
271 return absl::OkStatus();
272 }
273
MaybeFlushIfCloseToCapacity(std::optional<std::fstream> & output_file)274 absl::Status WriteBitBuffer::MaybeFlushIfCloseToCapacity(
275 std::optional<std::fstream>& output_file) {
276 // Query if the buffer is close to capacity without letting it resize.
277 if (CanWriteBytes(/*allow_resizing=*/false, bit_buffer_.capacity() / 2,
278 bit_offset_, bit_buffer_) != absl::OkStatus()) {
279 RETURN_IF_NOT_OK(FlushAndWriteToFile(output_file));
280 }
281
282 return absl::OkStatus();
283 }
284
Reset()285 void WriteBitBuffer::Reset() {
286 bit_offset_ = 0;
287 bit_buffer_.clear();
288 }
289
290 } // namespace iamf_tools
291