/*
* Copyright (C) 2017 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 "perfetto/protozero/message.h"
#include <type_traits>
#include "perfetto/base/logging.h"
#include "perfetto/protozero/message_handle.h"
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
// The memcpy() for float and double below needs to be adjusted if we want to
// support big endian CPUs. There doesn't seem to be a compelling need today.
#error Unimplemented for big endian archs.
#endif
namespace protozero {
// static
constexpr uint32_t Message::kMaxNestingDepth;
// Do NOT put any code in the constructor or use default initialization.
// Use the Reset() method below instead. See the header for the reason why.
// This method is called to initialize both root and nested messages.
void Message::Reset(ScatteredStreamWriter* stream_writer) {
// Older versions of libstdcxx don't have is_trivially_constructible.
#if !defined(__GLIBCXX__) || __GLIBCXX__ >= 20170516
static_assert(std::is_trivially_constructible<Message>::value,
"Message must be trivially constructible");
#endif
static_assert(std::is_trivially_destructible<Message>::value,
"Message must be trivially destructible");
static_assert(
sizeof(Message::nested_messages_arena_) >=
kMaxNestingDepth *
(sizeof(Message) - sizeof(Message::nested_messages_arena_)),
"Message::nested_messages_arena_ is too small");
stream_writer_ = stream_writer;
size_ = 0;
size_field_ = nullptr;
size_already_written_ = 0;
nested_message_ = nullptr;
nesting_depth_ = 0;
finalized_ = false;
#if PERFETTO_DCHECK_IS_ON()
handle_ = nullptr;
generation_++;
#endif
}
void Message::AppendString(uint32_t field_id, const char* str) {
AppendBytes(field_id, str, strlen(str));
}
void Message::AppendBytes(uint32_t field_id, const void* src, size_t size) {
if (nested_message_)
EndNestedMessage();
PERFETTO_DCHECK(size < proto_utils::kMaxMessageLength);
// Write the proto preamble (field id, type and length of the field).
uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize];
uint8_t* pos = buffer;
pos = proto_utils::WriteVarInt(proto_utils::MakeTagLengthDelimited(field_id),
pos);
pos = proto_utils::WriteVarInt(static_cast<uint32_t>(size), pos);
WriteToStream(buffer, pos);
const uint8_t* src_u8 = reinterpret_cast<const uint8_t*>(src);
WriteToStream(src_u8, src_u8 + size);
}
size_t Message::AppendScatteredBytes(uint32_t field_id,
ContiguousMemoryRange* ranges,
size_t num_ranges) {
size_t size = 0;
for (size_t i = 0; i < num_ranges; ++i) {
size += ranges[i].size();
}
PERFETTO_DCHECK(size < proto_utils::kMaxMessageLength);
uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize];
uint8_t* pos = buffer;
pos = proto_utils::WriteVarInt(proto_utils::MakeTagLengthDelimited(field_id),
pos);
pos = proto_utils::WriteVarInt(static_cast<uint32_t>(size), pos);
WriteToStream(buffer, pos);
for (size_t i = 0; i < num_ranges; ++i) {
auto& range = ranges[i];
WriteToStream(range.begin, range.end);
}
return size;
}
uint32_t Message::Finalize() {
if (finalized_)
return size_;
if (nested_message_)
EndNestedMessage();
// Write the length of the nested message a posteriori, using a leading-zero
// redundant varint encoding.
if (size_field_) {
PERFETTO_DCHECK(!finalized_);
PERFETTO_DCHECK(size_ < proto_utils::kMaxMessageLength);
PERFETTO_DCHECK(size_ >= size_already_written_);
proto_utils::WriteRedundantVarInt(size_ - size_already_written_,
size_field_);
size_field_ = nullptr;
}
finalized_ = true;
#if PERFETTO_DCHECK_IS_ON()
if (handle_)
handle_->reset_message();
#endif
return size_;
}
void Message::BeginNestedMessageInternal(uint32_t field_id, Message* message) {
if (nested_message_)
EndNestedMessage();
// Write the proto preamble for the nested message.
uint8_t data[proto_utils::kMaxTagEncodedSize];
uint8_t* data_end = proto_utils::WriteVarInt(
proto_utils::MakeTagLengthDelimited(field_id), data);
WriteToStream(data, data_end);
message->Reset(stream_writer_);
PERFETTO_CHECK(nesting_depth_ < kMaxNestingDepth);
message->nesting_depth_ = nesting_depth_ + 1;
// The length of the nested message cannot be known upfront. So right now
// just reserve the bytes to encode the size after the nested message is done.
message->set_size_field(
stream_writer_->ReserveBytes(proto_utils::kMessageLengthFieldSize));
size_ += proto_utils::kMessageLengthFieldSize;
nested_message_ = message;
}
void Message::EndNestedMessage() {
size_ += nested_message_->Finalize();
nested_message_ = nullptr;
}
} // namespace protozero