• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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