Add an Message::AppendScatteredBytes function
This takes an array of buffers which will all be copied into the field
as raw bytes; prior to this AppendBytes() only took a single buffer,
which meant an extra N memcpy's if a heap-allocated message composed
of multiple buffers needed to be copied into a submessage.
R=primiano@google.com
Change-Id: I34e3d8d63316357f190fe8530b3fb0fbcde01536
diff --git a/include/perfetto/protozero/message.h b/include/perfetto/protozero/message.h
index 555e128..cf5499d 100644
--- a/include/perfetto/protozero/message.h
+++ b/include/perfetto/protozero/message.h
@@ -146,6 +146,12 @@
void AppendString(uint32_t field_id, const char* str);
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, using the static storage provided by the parent
// class (see comment in |nested_messages_arena_|). The nested message ends
// either when Finalize() is called or when any other Append* method is called
diff --git a/src/protozero/message.cc b/src/protozero/message.cc
index 843f76d..4559e21 100644
--- a/src/protozero/message.cc
+++ b/src/protozero/message.cc
@@ -86,6 +86,31 @@
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_;
diff --git a/src/protozero/message_unittest.cc b/src/protozero/message_unittest.cc
index 7240f7e..c90acea 100644
--- a/src/protozero/message_unittest.cc
+++ b/src/protozero/message_unittest.cc
@@ -207,6 +207,28 @@
ASSERT_EQ("2803", GetNextSerializedBytes(2));
}
+// Tests using a AppendScatteredBytes to append raw bytes to
+// a message using multiple individual buffers.
+TEST_F(MessageTest, AppendScatteredBytes) {
+ Message* root_msg = NewMessage();
+
+ uint8_t buffer[42];
+ memset(buffer, 0x42, sizeof(buffer));
+
+ ContiguousMemoryRange ranges[] = {{buffer, buffer + sizeof(buffer)},
+ {buffer, buffer + sizeof(buffer)}};
+ root_msg->AppendScatteredBytes(1 /* field_id */, ranges, 2);
+ EXPECT_EQ(86u, root_msg->Finalize());
+ EXPECT_EQ(86u, GetNumSerializedBytes());
+
+ // field_id
+ EXPECT_EQ("0A", GetNextSerializedBytes(1));
+ // field length
+ EXPECT_EQ("54", GetNextSerializedBytes(1));
+ // start of contents
+ EXPECT_EQ("42424242", GetNextSerializedBytes(4));
+}
+
// Checks that the size field of root and nested messages is properly written
// on finalization.
TEST_F(MessageTest, BackfillSizeOnFinalization) {