blob: 069bc34db1892ae59f48435c77ed6876c068f992 [file] [log] [blame]
/*
* 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.
*/
#ifndef INCLUDE_PERFETTO_PROTOZERO_MESSAGE_H_
#define INCLUDE_PERFETTO_PROTOZERO_MESSAGE_H_
#include <assert.h>
#include <stdint.h>
#include <string.h>
#include <string>
#include <type_traits>
#include "perfetto/base/export.h"
#include "perfetto/base/logging.h"
#include "perfetto/protozero/contiguous_memory_range.h"
#include "perfetto/protozero/proto_utils.h"
#include "perfetto/protozero/scattered_stream_writer.h"
namespace perfetto {
namespace shm_fuzz {
class FakeProducer;
} // namespace shm_fuzz
} // namespace perfetto
namespace protozero {
class MessageArena;
class MessageHandleBase;
// Base class extended by the proto C++ stubs generated by the ProtoZero
// compiler. This class provides the minimal runtime required to support
// append-only operations and is designed for performance. None of the methods
// require any dynamic memory allocation, unless more than 16 nested messages
// are created via BeginNestedMessage() calls.
class PERFETTO_EXPORT_COMPONENT Message {
public:
friend class MessageHandleBase;
// The ctor is deliberately a no-op to avoid forwarding args from all
// subclasses. The real initialization is performed by Reset().
// Nested messages are allocated via placement new by MessageArena and
// implictly destroyed when the RootMessage's arena goes away. This is
// fine as long as all the fields are PODs, which is checked by the
// static_assert()s in the Reset() method.
Message() = default;
// Clears up the state, allowing the message to be reused as a fresh one.
void Reset(ScatteredStreamWriter*, MessageArena*);
// Commits all the changes to the buffer (backfills the size field of this and
// all nested messages) and seals the message. Returns the size of the message
// (and all nested sub-messages), without taking into account any chunking.
// Finalize is idempotent and can be called several times w/o side effects.
uint32_t Finalize();
// Optional. If is_valid() == true, the corresponding memory region (its
// length == proto_utils::kMessageLengthFieldSize) is backfilled with the size
// of this message (minus |size_already_written| below). This is the mechanism
// used by messages to backfill their corresponding size field in the parent
// message.
uint8_t* size_field() const { return size_field_; }
void set_size_field(uint8_t* size_field) { size_field_ = size_field; }
// This is to deal with case of backfilling the size of a root (non-nested)
// message which is split into multiple chunks. Upon finalization only the
// partial size that lies in the last chunk has to be backfilled.
void inc_size_already_written(uint32_t sz) { size_already_written_ += sz; }
Message* nested_message() { return nested_message_; }
bool is_finalized() const { return finalized_; }
#if PERFETTO_DCHECK_IS_ON()
void set_handle(MessageHandleBase* handle) { handle_ = handle; }
#endif
// Proto types: uint64, uint32, int64, int32, bool, enum.
template <typename T>
void AppendVarInt(uint32_t field_id, T value) {
if (nested_message_)
EndNestedMessage();
uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize];
uint8_t* pos = buffer;
pos = proto_utils::WriteVarInt(proto_utils::MakeTagVarInt(field_id), pos);
// WriteVarInt encodes signed values in two's complement form.
pos = proto_utils::WriteVarInt(value, pos);
WriteToStream(buffer, pos);
}
// Proto types: sint64, sint32.
template <typename T>
void AppendSignedVarInt(uint32_t field_id, T value) {
AppendVarInt(field_id, proto_utils::ZigZagEncode(value));
}
// Proto types: bool, enum (small).
// Faster version of AppendVarInt for tiny numbers.
void AppendTinyVarInt(uint32_t field_id, int32_t value) {
PERFETTO_DCHECK(0 <= value && value < 0x80);
if (nested_message_)
EndNestedMessage();
uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize];
uint8_t* pos = buffer;
// MakeTagVarInt gets super optimized here for constexpr.
pos = proto_utils::WriteVarInt(proto_utils::MakeTagVarInt(field_id), pos);
*pos++ = static_cast<uint8_t>(value);
WriteToStream(buffer, pos);
}
// Proto types: fixed64, sfixed64, fixed32, sfixed32, double, float.
template <typename T>
void AppendFixed(uint32_t field_id, T value) {
if (nested_message_)
EndNestedMessage();
uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize];
uint8_t* pos = buffer;
pos = proto_utils::WriteVarInt(proto_utils::MakeTagFixed<T>(field_id), pos);
memcpy(pos, &value, sizeof(T));
pos += sizeof(T);
// TODO: Optimize memcpy performance, see http://crbug.com/624311 .
WriteToStream(buffer, pos);
}
void AppendString(uint32_t field_id, const char* str);
void AppendString(uint32_t field_id, const std::string& str) {
AppendBytes(field_id, str.data(), str.size());
}
void AppendBytes(uint32_t field_id, const void* value, size_t size);
// Append raw bytes for a field, using the supplied |ranges| to
// copy from |num_ranges| individual buffers.
size_t AppendScatteredBytes(uint32_t field_id,
ContiguousMemoryRange* ranges,
size_t num_ranges);
// Begins a nested message. The returned object is owned by the MessageArena
// of the root message. The nested message ends either when Finalize() is
// called or when any other Append* method is called in the parent class.
// The template argument T is supposed to be a stub class auto generated from
// a .proto, hence a subclass of Message.
template <class T>
T* BeginNestedMessage(uint32_t field_id) {
// This is to prevent subclasses (which should be autogenerated, though), to
// introduce extra state fields (which wouldn't be initialized by Reset()).
static_assert(std::is_base_of<Message, T>::value,
"T must be a subclass of Message");
static_assert(sizeof(T) == sizeof(Message),
"Message subclasses cannot introduce extra state.");
return static_cast<T*>(BeginNestedMessageInternal(field_id));
}
// Gives read-only access to the underlying stream_writer. This is used only
// by few internals to query the state of the underlying buffer. It is almost
// always a bad idea to poke at the stream_writer() internals.
const ScatteredStreamWriter* stream_writer() const { return stream_writer_; }
// Appends some raw bytes to the message. The use-case for this is preserving
// unknown fields in the decode -> re-encode path of xxx.gen.cc classes
// generated by the cppgen_plugin.cc.
// The caller needs to guarantee that the appended data is properly
// proto-encoded and each field has a proto preamble.
void AppendRawProtoBytes(const void* data, size_t size) {
const uint8_t* src = reinterpret_cast<const uint8_t*>(data);
WriteToStream(src, src + size);
}
private:
Message(const Message&) = delete;
Message& operator=(const Message&) = delete;
Message* BeginNestedMessageInternal(uint32_t field_id);
// Called by Finalize and Append* methods.
void EndNestedMessage();
void WriteToStream(const uint8_t* src_begin, const uint8_t* src_end) {
PERFETTO_DCHECK(!finalized_);
PERFETTO_DCHECK(src_begin <= src_end);
const uint32_t size = static_cast<uint32_t>(src_end - src_begin);
stream_writer_->WriteBytes(src_begin, size);
size_ += size;
}
// Only POD fields are allowed. This class's dtor is never called.
// See the comment on the static_assert in the corresponding .cc file.
// The stream writer interface used for the serialization.
ScatteredStreamWriter* stream_writer_;
// The storage used to allocate nested Message objects.
// This is owned by RootMessage<T>.
MessageArena* arena_;
// Pointer to the last child message created through BeginNestedMessage(), if
// any, nullptr otherwise. There is no need to keep track of more than one
// message per nesting level as the proto-zero API contract mandates that
// nested fields can be filled only in a stacked fashion. In other words,
// nested messages are finalized and sealed when any other field is set in the
// parent message (or the parent message itself is finalized) and cannot be
// accessed anymore afterwards.
Message* nested_message_;
// [optional] Pointer to a non-aligned pre-reserved var-int slot of
// kMessageLengthFieldSize bytes. When set, the Finalize() method will write
// the size of proto-encoded message in the pointed memory region.
uint8_t* size_field_;
// Keeps track of the size of the current message.
uint32_t size_;
// See comment for inc_size_already_written().
uint32_t size_already_written_;
// When true, no more changes to the message are allowed. This is to DCHECK
// attempts of writing to a message which has been Finalize()-d.
bool finalized_;
#if PERFETTO_DCHECK_IS_ON()
// Current generation of message. Incremented on Reset.
// Used to detect stale handles.
uint32_t generation_;
MessageHandleBase* handle_;
#endif
};
} // namespace protozero
#endif // INCLUDE_PERFETTO_PROTOZERO_MESSAGE_H_