blob: 3e053f42b9aacfb3048a61cf042605d8f5b4ccb7 [file] [log] [blame]
Primiano Tucci2a29ac72017-10-24 17:47:19 +01001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "protozero/protozero_message.h"
18
19#include <type_traits>
20
Primiano Tuccid7d1be02017-10-30 17:41:34 +000021#include "base/logging.h"
Primiano Tucci2a29ac72017-10-24 17:47:19 +010022
23#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
24// The memcpy() for float and double below needs to be adjusted if we want to
25// support big endian CPUs. There doesn't seem to be a compelling need today.
26#error Unimplemented for big endian archs.
27#endif
28
29namespace protozero {
30
31// static
32constexpr uint32_t ProtoZeroMessage::kMaxNestingDepth;
33
34// Do NOT put any code in the constructor or use default initialization.
35// Use the Reset() method below instead. See the header for the reason why.
36
37// This method is called to initialize both root and nested messages.
38void ProtoZeroMessage::Reset(ScatteredStreamWriter* stream_writer) {
39// Older versions of libstdcxx don't have is_trivially_constructible.
40#if !defined(__GLIBCXX__) || __GLIBCXX__ >= 20170516
41 static_assert(std::is_trivially_constructible<ProtoZeroMessage>::value,
42 "ProtoZeroMessage must be trivially constructible");
43#endif
44
45 static_assert(std::is_trivially_destructible<ProtoZeroMessage>::value,
46 "ProtoZeroMessage must be trivially destructible");
47
48 static_assert(
49 sizeof(ProtoZeroMessage::nested_messages_arena_) >=
50 kMaxNestingDepth * (sizeof(ProtoZeroMessage) -
51 sizeof(ProtoZeroMessage::nested_messages_arena_)),
52 "ProtoZeroMessage::nested_messages_arena_ is too small");
53
54 stream_writer_ = stream_writer;
55 size_ = 0;
56 size_field_.reset();
Primiano Tucci2a29ac72017-10-24 17:47:19 +010057 nested_message_ = nullptr;
58 nesting_depth_ = 0;
59#if PROTOZERO_ENABLE_HANDLE_DEBUGGING()
60 sealed_ = false;
61 handle_ = nullptr;
62#endif
63}
64
65void ProtoZeroMessage::AppendString(uint32_t field_id, const char* str) {
66 AppendBytes(field_id, str, strlen(str));
67}
68
69void ProtoZeroMessage::AppendBytes(uint32_t field_id,
70 const void* src,
71 size_t size) {
72 if (nested_message_)
73 EndNestedMessage();
74
Primiano Tuccid7d1be02017-10-30 17:41:34 +000075 PERFETTO_DCHECK(size < proto_utils::kMaxMessageLength);
Primiano Tucci2a29ac72017-10-24 17:47:19 +010076 // Write the proto preamble (field id, type and length of the field).
77 uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize];
78 uint8_t* pos = buffer;
79 pos = proto_utils::WriteVarInt(proto_utils::MakeTagLengthDelimited(field_id),
80 pos);
81 pos = proto_utils::WriteVarInt(static_cast<uint32_t>(size), pos);
82 WriteToStream(buffer, pos);
83
84 const uint8_t* src_u8 = reinterpret_cast<const uint8_t*>(src);
85 WriteToStream(src_u8, src_u8 + size);
86}
87
88size_t ProtoZeroMessage::Finalize() {
89 if (nested_message_)
90 EndNestedMessage();
91
92 // Write the length of the nested message a posteriori, using a leading-zero
93 // redundant varint encoding.
94 if (size_field_.is_valid()) {
95#if PROTOZERO_ENABLE_HANDLE_DEBUGGING()
Primiano Tuccid7d1be02017-10-30 17:41:34 +000096 PERFETTO_DCHECK(!sealed_);
Primiano Tucci2a29ac72017-10-24 17:47:19 +010097#endif
Primiano Tuccid7d1be02017-10-30 17:41:34 +000098 PERFETTO_DCHECK(size_ < proto_utils::kMaxMessageLength);
99 PERFETTO_DCHECK(proto_utils::kMessageLengthFieldSize == size_field_.size());
Primiano Tucci25aff4e2017-11-23 12:18:52 +0000100 proto_utils::WriteRedundantVarInt(static_cast<uint32_t>(size_),
101 size_field_.begin);
Primiano Tucci2a29ac72017-10-24 17:47:19 +0100102 size_field_.reset();
103 }
104
105#if PROTOZERO_ENABLE_HANDLE_DEBUGGING()
106 sealed_ = true;
107 if (handle_)
108 handle_->reset_message();
109#endif
110
111 return size_;
112}
113
114void ProtoZeroMessage::BeginNestedMessageInternal(uint32_t field_id,
115 ProtoZeroMessage* message) {
116 if (nested_message_)
117 EndNestedMessage();
118
119 // Write the proto preamble for the nested message.
120 uint8_t data[proto_utils::kMaxTagEncodedSize];
121 uint8_t* data_end = proto_utils::WriteVarInt(
122 proto_utils::MakeTagLengthDelimited(field_id), data);
123 WriteToStream(data, data_end);
124
125 message->Reset(stream_writer_);
Primiano Tuccid7d1be02017-10-30 17:41:34 +0000126 PERFETTO_CHECK(nesting_depth_ < kMaxNestingDepth);
Primiano Tucci2a29ac72017-10-24 17:47:19 +0100127 message->nesting_depth_ = nesting_depth_ + 1;
128
129 // The length of the nested message cannot be known upfront. So right now
130 // just reserve the bytes to encode the size after the nested message is done.
131 message->set_size_field(
132 stream_writer_->ReserveBytes(proto_utils::kMessageLengthFieldSize));
133 size_ += proto_utils::kMessageLengthFieldSize;
134 nested_message_ = message;
135}
136
137void ProtoZeroMessage::EndNestedMessage() {
138 size_ += nested_message_->Finalize();
139 nested_message_ = nullptr;
140}
141
142} // namespace protozero