blob: 0f201bb226446dfa5f708d0024ca634eb607eb0e [file] [log] [blame]
/*
* Copyright (C) 2022 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_PUBLIC_PB_MSG_H_
#define INCLUDE_PERFETTO_PUBLIC_PB_MSG_H_
#include <assert.h>
#include <stdint.h>
#include <string.h>
#include "perfetto/public/abi/stream_writer_abi.h"
#include "perfetto/public/compiler.h"
#include "perfetto/public/pb_utils.h"
#include "perfetto/public/stream_writer.h"
// The number of bytes reserved by this implementation to encode a protobuf type
// 2 field size as var-int. Keep this in sync with kMessageLengthFieldSize in
// proto_utils.h.
#define PROTOZERO_MESSAGE_LENGTH_FIELD_SIZE 4
// Points to the memory used by a `PerfettoPbMsg` for writing.
struct PerfettoPbMsgWriter {
struct PerfettoStreamWriter writer;
};
struct PerfettoPbMsg {
// Pointer to a non-aligned pre-reserved var-int slot of
// PROTOZERO_MESSAGE_LENGTH_FIELD_SIZE bytes. If not NULL,
// protozero_length_buf_finalize() will write the size of proto-encoded
// message in the pointed memory region.
uint8_t* size_field;
// Current size of the buffer.
uint32_t size;
struct PerfettoPbMsgWriter* writer;
struct PerfettoPbMsg* nested;
struct PerfettoPbMsg* parent;
};
static inline void PerfettoPbMsgInit(struct PerfettoPbMsg* msg,
struct PerfettoPbMsgWriter* writer) {
msg->size_field = PERFETTO_NULL;
msg->size = 0;
msg->writer = writer;
msg->nested = PERFETTO_NULL;
msg->parent = PERFETTO_NULL;
}
static inline void PerfettoPbMsgPatch(struct PerfettoPbMsg* msg) {
static_assert(
PROTOZERO_MESSAGE_LENGTH_FIELD_SIZE == PERFETTO_STREAM_WRITER_PATCH_SIZE,
"PROTOZERO_MESSAGE_LENGTH_FIELD_SIZE doesn't match patch size");
msg->size_field =
PerfettoStreamWriterAnnotatePatch(&msg->writer->writer, msg->size_field);
}
static inline void PerfettoPbMsgPatchStack(struct PerfettoPbMsg* msg) {
uint8_t* const cur_range_end = msg->writer->writer.end;
uint8_t* const cur_range_begin = msg->writer->writer.begin;
while (msg && cur_range_begin <= msg->size_field &&
msg->size_field < cur_range_end) {
PerfettoPbMsgPatch(msg);
msg = msg->parent;
}
}
static inline void PerfettoPbMsgAppendBytes(struct PerfettoPbMsg* msg,
const uint8_t* begin,
size_t size) {
if (PERFETTO_UNLIKELY(
size > PerfettoStreamWriterAvailableBytes(&msg->writer->writer))) {
PerfettoPbMsgPatchStack(msg);
}
PerfettoStreamWriterAppendBytes(&msg->writer->writer, begin, size);
msg->size += size;
}
static inline void PerfettoPbMsgAppendByte(struct PerfettoPbMsg* msg,
uint8_t value) {
PerfettoPbMsgAppendBytes(msg, &value, 1);
}
static inline void PerfettoPbMsgAppendVarInt(struct PerfettoPbMsg* msg,
uint64_t value) {
uint8_t* buf_end;
uint8_t buf[PERFETTO_PB_VARINT_MAX_SIZE_64];
buf_end = PerfettoPbWriteVarInt(value, buf);
PerfettoPbMsgAppendBytes(msg, buf,
PERFETTO_STATIC_CAST(size_t, buf_end - buf));
}
static inline void PerfettoPbMsgAppendType0Field(struct PerfettoPbMsg* msg,
int32_t field_id,
uint64_t value) {
uint8_t* buf_end;
uint8_t buf[PERFETTO_PB_VARINT_MAX_SIZE_64 + PERFETTO_PB_VARINT_MAX_SIZE_32];
buf_end = PerfettoPbWriteVarInt(
PerfettoPbMakeTag(field_id, PERFETTO_PB_WIRE_TYPE_VARINT), buf);
buf_end = PerfettoPbWriteVarInt(value, buf_end);
PerfettoPbMsgAppendBytes(msg, buf,
PERFETTO_STATIC_CAST(size_t, buf_end - buf));
}
static inline void PerfettoPbMsgAppendType2Field(struct PerfettoPbMsg* msg,
int32_t field_id,
const uint8_t* data,
size_t size) {
uint8_t* buf_end;
uint8_t buf[PERFETTO_PB_VARINT_MAX_SIZE_64 + PERFETTO_PB_VARINT_MAX_SIZE_32];
buf_end = PerfettoPbWriteVarInt(
PerfettoPbMakeTag(field_id, PERFETTO_PB_WIRE_TYPE_DELIMITED), buf);
buf_end =
PerfettoPbWriteVarInt(PERFETTO_STATIC_CAST(uint64_t, size), buf_end);
PerfettoPbMsgAppendBytes(msg, buf,
PERFETTO_STATIC_CAST(size_t, buf_end - buf));
PerfettoPbMsgAppendBytes(msg, data, size);
}
static inline void PerfettoPbMsgAppendFixed32Field(struct PerfettoPbMsg* msg,
int32_t field_id,
uint32_t value) {
uint8_t* buf_end;
uint8_t buf[PERFETTO_PB_VARINT_MAX_SIZE_32 + 4];
buf_end = PerfettoPbWriteVarInt(
PerfettoPbMakeTag(field_id, PERFETTO_PB_WIRE_TYPE_FIXED32), buf);
buf_end[0] = PERFETTO_STATIC_CAST(uint8_t, value);
buf_end[1] = PERFETTO_STATIC_CAST(uint8_t, value >> 8);
buf_end[2] = PERFETTO_STATIC_CAST(uint8_t, value >> 16);
buf_end[3] = PERFETTO_STATIC_CAST(uint8_t, value >> 24);
buf_end += 4;
PerfettoPbMsgAppendBytes(msg, buf,
PERFETTO_STATIC_CAST(size_t, buf_end - buf));
}
static inline void PerfettoPbMsgAppendFloatField(struct PerfettoPbMsg* msg,
int32_t field_id,
float value) {
uint32_t val;
memcpy(&val, &value, sizeof val);
PerfettoPbMsgAppendFixed32Field(msg, field_id, val);
}
static inline void PerfettoPbMsgAppendFixed64Field(struct PerfettoPbMsg* msg,
int32_t field_id,
uint64_t value) {
uint8_t* buf_end;
uint8_t buf[PERFETTO_PB_VARINT_MAX_SIZE_32 + 8];
buf_end = PerfettoPbWriteVarInt(
PerfettoPbMakeTag(field_id, PERFETTO_PB_WIRE_TYPE_FIXED64), buf);
buf_end[0] = PERFETTO_STATIC_CAST(uint8_t, value);
buf_end[1] = PERFETTO_STATIC_CAST(uint8_t, value >> 8);
buf_end[2] = PERFETTO_STATIC_CAST(uint8_t, value >> 16);
buf_end[3] = PERFETTO_STATIC_CAST(uint8_t, value >> 24);
buf_end[4] = PERFETTO_STATIC_CAST(uint8_t, value >> 32);
buf_end[5] = PERFETTO_STATIC_CAST(uint8_t, value >> 40);
buf_end[6] = PERFETTO_STATIC_CAST(uint8_t, value >> 48);
buf_end[7] = PERFETTO_STATIC_CAST(uint8_t, value >> 56);
buf_end += 8;
PerfettoPbMsgAppendBytes(msg, buf,
PERFETTO_STATIC_CAST(size_t, buf_end - buf));
}
static inline void PerfettoPbMsgAppendDoubleField(struct PerfettoPbMsg* msg,
int32_t field_id,
double value) {
uint64_t val;
memcpy(&val, &value, sizeof val);
PerfettoPbMsgAppendFixed64Field(msg, field_id, val);
}
static inline void PerfettoPbMsgAppendCStrField(struct PerfettoPbMsg* msg,
int32_t field_id,
const char* c_str) {
PerfettoPbMsgAppendType2Field(
msg, field_id, PERFETTO_REINTERPRET_CAST(const uint8_t*, c_str),
strlen(c_str));
}
static inline void PerfettoPbMsgBeginNested(struct PerfettoPbMsg* parent,
struct PerfettoPbMsg* nested,
int32_t field_id) {
PerfettoPbMsgAppendVarInt(
parent, PerfettoPbMakeTag(field_id, PERFETTO_PB_WIRE_TYPE_DELIMITED));
PerfettoPbMsgInit(nested, parent->writer);
if (PERFETTO_UNLIKELY(
PROTOZERO_MESSAGE_LENGTH_FIELD_SIZE >
PerfettoStreamWriterAvailableBytes(&parent->writer->writer))) {
PerfettoPbMsgPatchStack(parent);
}
nested->size_field = PERFETTO_REINTERPRET_CAST(
uint8_t*,
PerfettoStreamWriterReserveBytes(&nested->writer->writer,
PROTOZERO_MESSAGE_LENGTH_FIELD_SIZE));
nested->parent = parent;
parent->size += PROTOZERO_MESSAGE_LENGTH_FIELD_SIZE;
parent->nested = nested;
}
static inline size_t PerfettoPbMsgFinalize(struct PerfettoPbMsg* msg);
static inline void PerfettoPbMsgEndNested(struct PerfettoPbMsg* parent) {
parent->size += PerfettoPbMsgFinalize(parent->nested);
parent->nested = PERFETTO_NULL;
}
static inline size_t PerfettoPbMsgFinalize(struct PerfettoPbMsg* msg) {
if (msg->nested)
PerfettoPbMsgEndNested(msg);
// Write the length of the nested message a posteriori, using a leading-zero
// redundant varint encoding.
if (msg->size_field) {
uint32_t size_to_write;
size_to_write = msg->size;
for (size_t i = 0; i < PROTOZERO_MESSAGE_LENGTH_FIELD_SIZE; i++) {
const uint8_t msb = (i < 3) ? 0x80 : 0;
msg->size_field[i] = (size_to_write & 0xFF) | msb;
size_to_write >>= 7;
}
msg->size_field = PERFETTO_NULL;
}
return msg->size;
}
#endif // INCLUDE_PERFETTO_PUBLIC_PB_MSG_H_