| Index: base/trace_event/v2/append_only_proto_message.cc
|
| diff --git a/base/trace_event/v2/append_only_proto_message.cc b/base/trace_event/v2/append_only_proto_message.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..85a0947717c8b9d819e804b112f6820efd1a48ae
|
| --- /dev/null
|
| +++ b/base/trace_event/v2/append_only_proto_message.cc
|
| @@ -0,0 +1,174 @@
|
| +// Copyright 2016 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "base/trace_event/v2/append_only_proto_message.h"
|
| +
|
| +#include "base/logging.h"
|
| +#include "base/trace_event/v2/proto_utils.h"
|
| +#include "build/build_config.h"
|
| +
|
| +
|
| +// TODO size vs length naming inconsistency.
|
| +// TODO bitmap for mandatory / optional fields
|
| +
|
| +namespace {
|
| +
|
| +enum {
|
| + PROTO_VARINT = 0,
|
| + PROTO_FIXED_64BIT = 1,
|
| + PROTO_LENGTH_LIMITED = 2,
|
| + PROTO_FIXED_32BIT = 5,
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +namespace base {
|
| +namespace trace_event {
|
| +namespace v2 {
|
| +
|
| +// static
|
| +const size_t AppendOnlyProtoMessage::kMaxMessageLength =
|
| + 1ul << (AppendOnlyProtoMessage::kMaxMessageLengthFieldSize * 7);
|
| +
|
| +AppendOnlyProtoMessage::AppendOnlyProtoMessage()
|
| + : buffer_(nullptr),
|
| + size_(0),
|
| + write_size_field_on_finalization_(false),
|
| + size_already_written_(0),
|
| + current_nested_message_(nullptr),
|
| + sealed_(false) {}
|
| +
|
| +AppendOnlyProtoMessage::~AppendOnlyProtoMessage() {}
|
| +
|
| +size_t AppendOnlyProtoMessage::Finalize() {
|
| + if (sealed_)
|
| + return size_;
|
| +
|
| + if (current_nested_message_)
|
| + EndNestedMessage();
|
| +
|
| + if (write_size_field_on_finalization_) {
|
| + // Write the length of the nested message a posteriori, forcing a 32-bit
|
| + // leading-zero varint. This is a slight bending of the proto encoding,
|
| + // essentially size = 7 gets encoded as 0x87,0x80,0x80,0x00 instead of 0x07.
|
| + // The problem here is that we cannot know upfront how big the nested message
|
| + // was. There is precedent for this (TODO link internal bug).
|
| + // TODO optimization: upon beginmessage we could get a promise of the max
|
| + // length of the submessage to avoid wasting 2 bytes if we know upfront that
|
| + // the message is going to be always < 64k.
|
| + DCHECK_LT(size_, kMaxMessageLength);
|
| + uint8_t word[kMaxMessageLengthFieldSize];
|
| + ProtoUtils::EncodeRedundantVarIntUnsigned(size_ - size_already_written_,
|
| + kMaxMessageLengthFieldSize,
|
| + word);
|
| + buffer_->WriteReservedBytes(size_field_, word, kMaxMessageLengthFieldSize);
|
| + }
|
| +
|
| + // No more changes allowed.
|
| + sealed_ = true;
|
| + return size_;
|
| +}
|
| +
|
| +void AppendOnlyProtoMessage::WriteToBuffer(const uint8_t* src, size_t size) {
|
| + buffer_->WriteBytes(src, size);
|
| + size_ += size;
|
| +}
|
| +
|
| +void AppendOnlyProtoMessage::AppendVarIntSigned(uint32_t field_id,
|
| + int64_t value) {
|
| + // TODO ZigZag encoding.
|
| + AppendVarIntUnsigned(field_id, value); // HACK, this is not supporting signed.
|
| +}
|
| +
|
| +void AppendOnlyProtoMessage::AppendVarIntUnsigned(uint32_t field_id,
|
| + uint64_t value) {
|
| + if (current_nested_message_)
|
| + EndNestedMessage();
|
| +
|
| + uint8_t data[32];
|
| + size_t data_size;
|
| + data_size = ProtoUtils::EncodeVarIntUnsigned(ProtoUtils::GetVarIntFieldHeader(field_id), data);
|
| + data_size += ProtoUtils::EncodeVarIntUnsigned(value, &data[data_size]);
|
| + WriteToBuffer(data, data_size);
|
| +}
|
| +
|
| +void AppendOnlyProtoMessage::AppendFloat(uint32_t field_id, float value) {
|
| + if (current_nested_message_)
|
| + EndNestedMessage();
|
| +
|
| + uint8_t data[32];
|
| + size_t data_size;
|
| + data_size = ProtoUtils::EncodeVarIntUnsigned(ProtoUtils::GetFixed32FieldHeader(field_id), data);
|
| +
|
| +#if defined(ARCH_CPU_LITTLE_ENDIAN)
|
| + memcpy(&data[data_size], &value, sizeof(value));
|
| +#else
|
| +#error big-endian encoding not implemented.
|
| +#endif
|
| +
|
| + data_size += sizeof(value);
|
| + WriteToBuffer(data, data_size);
|
| +}
|
| +
|
| +void AppendOnlyProtoMessage::AppendDouble(uint32_t field_id, double value) {
|
| + if (current_nested_message_)
|
| + EndNestedMessage();
|
| +
|
| + uint8_t data[32];
|
| + size_t data_size;
|
| + data_size = ProtoUtils::EncodeVarIntUnsigned(ProtoUtils::GetFixed64FieldHeader(field_id), data);
|
| + memcpy(&data[data_size], &value, sizeof(value));
|
| + data_size += sizeof(value);
|
| + WriteToBuffer(data, data_size);
|
| +}
|
| +
|
| +void AppendOnlyProtoMessage::AppendString(uint32_t field_id, const char* str) {
|
| + if (current_nested_message_)
|
| + EndNestedMessage();
|
| +
|
| + const size_t str_length = strlen(str);
|
| + AppendBytes(field_id, str, str_length);
|
| +}
|
| +
|
| +void AppendOnlyProtoMessage::AppendBytes(uint32_t field_id, const void* value, size_t size) {
|
| + if (current_nested_message_)
|
| + EndNestedMessage();
|
| +
|
| + // Write the proto preamble (field id, type and length of the buffer).
|
| + uint8_t data[32];
|
| + size_t data_size;
|
| + data_size = ProtoUtils::EncodeVarIntUnsigned(ProtoUtils::GetLengthLimitedFieldHeader(field_id), data);
|
| + data_size += ProtoUtils::EncodeVarIntUnsigned(size, &data[data_size]);
|
| + WriteToBuffer(data, data_size);
|
| +
|
| + // Write the actual buffer.
|
| + WriteToBuffer(reinterpret_cast<const uint8_t*>(value), size);
|
| +}
|
| +
|
| +void AppendOnlyProtoMessage::BeginNestedMessage(
|
| + uint32_t field_id,
|
| + AppendOnlyProtoMessage* message) {
|
| + if (current_nested_message_)
|
| + EndNestedMessage();
|
| +
|
| + message->set_buffer_writer(buffer_writer());
|
| + uint8_t data[32];
|
| + size_t data_size;
|
| + data_size = ProtoUtils::EncodeVarIntUnsigned(ProtoUtils::GetLengthLimitedFieldHeader(field_id), data);
|
| + WriteToBuffer(data, data_size);
|
| +
|
| + // 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_write_size_field_on_finalization(buffer_->ReserveBytes(kMaxMessageLengthFieldSize));
|
| + current_nested_message_ = message;
|
| +}
|
| +
|
| +void AppendOnlyProtoMessage::EndNestedMessage() {
|
| + size_ += current_nested_message_->Finalize();
|
| + current_nested_message_ = nullptr;
|
| +}
|
| +
|
| +} // namespace v2
|
| +} // namespace trace_event
|
| +} // namespace base
|
|
|