| /* |
| * Copyright (C) 2026 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "src/trace_processor/util/flatbuffer_writer.h" |
| |
| #include <algorithm> |
| #include <cstdint> |
| #include <cstring> |
| #include <limits> |
| #include <string_view> |
| #include <vector> |
| |
| #include "perfetto/base/compiler.h" |
| #include "perfetto/base/logging.h" |
| |
| namespace perfetto::trace_processor::util { |
| |
| FlatBufferWriter::FlatBufferWriter(uint32_t initial_capacity) |
| : buf_(initial_capacity, 0), head_(initial_capacity) {} |
| |
| void FlatBufferWriter::GrowIfNeeded(uint32_t bytes) { |
| if (head_ >= bytes) { |
| return; |
| } |
| uint32_t old_size = static_cast<uint32_t>(buf_.size()); |
| uint32_t new_size = std::max(old_size * 2, old_size + bytes); |
| buf_.resize(new_size, 0); |
| // Shift existing data to the end of the new buffer. |
| uint32_t data_len = old_size - head_; |
| uint32_t new_head = new_size - data_len; |
| memmove(buf_.data() + new_head, buf_.data() + head_, data_len); |
| memset(buf_.data() + head_, 0, new_head - head_); |
| head_ = new_head; |
| } |
| |
| void FlatBufferWriter::Align(uint32_t alignment) { |
| min_align_ = std::max(min_align_, alignment); |
| uint32_t pad = (alignment - (size() % alignment)) % alignment; |
| GrowIfNeeded(pad); |
| for (uint32_t i = 0; i < pad; i++) { |
| buf_[--head_] = 0; |
| } |
| } |
| |
| void FlatBufferWriter::PrependBytes(const void* src, uint32_t len) { |
| if (len == 0) { |
| return; |
| } |
| GrowIfNeeded(len); |
| head_ -= len; |
| memcpy(&buf_[head_], src, len); |
| } |
| |
| // --------------------------------------------------------------------------- |
| // Strings |
| // --------------------------------------------------------------------------- |
| |
| FlatBufferWriter::Offset FlatBufferWriter::WriteString(std::string_view s) { |
| // Final buffer layout: [u32 len][chars...][null][pad]. The padding must be |
| // sized so that the LENGTH PREFIX (not the string tail) ends up 4-byte |
| // aligned once the terminator and characters have been prepended. |
| auto n = static_cast<uint32_t>(s.size()); |
| uint32_t pad = (4 - ((size() + n + 1) % 4)) % 4; |
| GrowIfNeeded(pad); |
| for (uint32_t i = 0; i < pad; i++) { |
| buf_[--head_] = 0; |
| } |
| Prepend<uint8_t>(0); |
| PrependBytes(s.data(), n); |
| Prepend<uint32_t>(n); |
| return Offset{size()}; |
| } |
| |
| // --------------------------------------------------------------------------- |
| // Vectors |
| // --------------------------------------------------------------------------- |
| |
| FlatBufferWriter::Offset FlatBufferWriter::WriteVecOffsets( |
| const Offset* offsets, |
| uint32_t count) { |
| // Elements are written back-to-front (last element first). |
| for (int32_t i = static_cast<int32_t>(count) - 1; i >= 0; i--) { |
| Align(sizeof(uint32_t)); |
| // Relative offset: from the slot position to the target. |
| uint32_t stored = size() + sizeof(uint32_t) - offsets[i].o; |
| Prepend(stored); |
| } |
| Prepend<uint32_t>(count); |
| return Offset{size()}; |
| } |
| |
| FlatBufferWriter::Offset FlatBufferWriter::WriteVecStruct(const void* data, |
| uint32_t elem_size, |
| uint32_t count, |
| uint32_t elem_align) { |
| Align(elem_align); |
| PrependBytes(data, elem_size * count); |
| Prepend<uint32_t>(count); |
| return Offset{size()}; |
| } |
| |
| // --------------------------------------------------------------------------- |
| // Tables |
| // --------------------------------------------------------------------------- |
| |
| void FlatBufferWriter::StartTable() { |
| table_body_start_ = size(); |
| fields_.clear(); |
| } |
| |
| void FlatBufferWriter::FieldBool(uint32_t index, bool val) { |
| Align(sizeof(uint8_t)); |
| Prepend<uint8_t>(val ? 1 : 0); |
| fields_.push_back({index, size()}); |
| } |
| |
| void FlatBufferWriter::FieldU8(uint32_t index, uint8_t val) { |
| Align(sizeof(uint8_t)); |
| Prepend(val); |
| fields_.push_back({index, size()}); |
| } |
| |
| void FlatBufferWriter::FieldI16(uint32_t index, int16_t val) { |
| Align(sizeof(int16_t)); |
| Prepend(val); |
| fields_.push_back({index, size()}); |
| } |
| |
| void FlatBufferWriter::FieldI32(uint32_t index, int32_t val) { |
| Align(sizeof(int32_t)); |
| Prepend(val); |
| fields_.push_back({index, size()}); |
| } |
| |
| void FlatBufferWriter::FieldI64(uint32_t index, int64_t val) { |
| Align(sizeof(int64_t)); |
| Prepend(val); |
| fields_.push_back({index, size()}); |
| } |
| |
| void FlatBufferWriter::FieldOffset(uint32_t index, Offset off) { |
| Align(sizeof(uint32_t)); |
| // Relative offset from the slot position to the target. |
| uint32_t stored = size() + sizeof(uint32_t) - off.o; |
| Prepend(stored); |
| fields_.push_back({index, size()}); |
| } |
| |
| FlatBufferWriter::Offset FlatBufferWriter::EndTable() { |
| // Prepend the soffset placeholder (patched below once the vtable position |
| // is known). Its position must be tracked as an offset from the end of the |
| // buffer: prepending the vtable below may grow the buffer, which shifts |
| // all data and invalidates absolute indices. |
| Align(sizeof(int32_t)); |
| Prepend<int32_t>(0); |
| uint32_t table_off = size(); |
| |
| uint32_t max_index = 0; |
| for (const auto& f : fields_) { |
| max_index = std::max(max_index, f.index); |
| } |
| uint32_t num_slots = fields_.empty() ? 0 : max_index + 1; |
| uint32_t vtable_size = 4 + num_slots * 2; |
| uint32_t table_size = table_off - table_body_start_; |
| |
| // Every vtable entry below is bounded by table_size, so this single check |
| // covers all of them as well as the table_size entry itself. |
| if (PERFETTO_UNLIKELY(table_size > std::numeric_limits<uint16_t>::max())) { |
| PERFETTO_ELOG( |
| "FlatBufferWriter: table size %u overflows the 16-bit vtable " |
| "encoding; the resulting buffer will be corrupt", |
| table_size); |
| } |
| |
| // Each vtable entry is the byte offset from the table start (soffset |
| // position) to the field data. 0 means "not present". |
| std::vector<uint16_t> vt_entries(num_slots, 0); |
| for (const auto& f : fields_) { |
| vt_entries[f.index] = static_cast<uint16_t>(table_off - f.off_from_end); |
| } |
| |
| // Prepend the vtable back-to-front: entries (last first), table_size, |
| // vtable_size. |
| Align(sizeof(uint16_t)); |
| for (int32_t i = static_cast<int32_t>(num_slots) - 1; i >= 0; i--) { |
| Prepend(vt_entries[static_cast<uint32_t>(i)]); |
| } |
| Prepend(static_cast<uint16_t>(table_size)); |
| Prepend(static_cast<uint16_t>(vtable_size)); |
| uint32_t vtable_off = size(); |
| |
| // Patch soffset: signed offset from the table to its vtable, i.e. the |
| // reader computes vtable = table - soffset. |
| auto soff = static_cast<int32_t>(vtable_off - table_off); |
| memcpy(&buf_[static_cast<uint32_t>(buf_.size()) - table_off], &soff, |
| sizeof(int32_t)); |
| |
| return Offset{table_off}; |
| } |
| |
| // --------------------------------------------------------------------------- |
| // Finalize |
| // --------------------------------------------------------------------------- |
| |
| void FlatBufferWriter::Finish(Offset root) { |
| // Pad so that the finished buffer's size is a multiple of the largest |
| // alignment used. All element positions are aligned relative to the |
| // buffer end; an aligned total size makes them aligned relative to the |
| // buffer start as well. |
| uint32_t total = size() + sizeof(uint32_t); |
| uint32_t pad = (min_align_ - (total % min_align_)) % min_align_; |
| GrowIfNeeded(pad); |
| for (uint32_t i = 0; i < pad; i++) { |
| buf_[--head_] = 0; |
| } |
| uint32_t stored = size() + sizeof(uint32_t) - root.o; |
| Prepend(stored); |
| } |
| |
| std::vector<uint8_t> FlatBufferWriter::Release() { |
| std::vector<uint8_t> result(buf_.begin() + head_, buf_.end()); |
| buf_.clear(); |
| head_ = 0; |
| return result; |
| } |
| |
| } // namespace perfetto::trace_processor::util |