Home | History | Annotate | Download | only in protozero
      1 /*
      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 "perfetto/protozero/message.h"
     18 
     19 #include <type_traits>
     20 
     21 #include "perfetto/base/logging.h"
     22 #include "perfetto/protozero/message_handle.h"
     23 
     24 #if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
     25 // The memcpy() for float and double below needs to be adjusted if we want to
     26 // support big endian CPUs. There doesn't seem to be a compelling need today.
     27 #error Unimplemented for big endian archs.
     28 #endif
     29 
     30 namespace protozero {
     31 
     32 // static
     33 constexpr uint32_t Message::kMaxNestingDepth;
     34 
     35 // Do NOT put any code in the constructor or use default initialization.
     36 // Use the Reset() method below instead. See the header for the reason why.
     37 
     38 // This method is called to initialize both root and nested messages.
     39 void Message::Reset(ScatteredStreamWriter* stream_writer) {
     40 // Older versions of libstdcxx don't have is_trivially_constructible.
     41 #if !defined(__GLIBCXX__) || __GLIBCXX__ >= 20170516
     42   static_assert(std::is_trivially_constructible<Message>::value,
     43                 "Message must be trivially constructible");
     44 #endif
     45 
     46   static_assert(std::is_trivially_destructible<Message>::value,
     47                 "Message must be trivially destructible");
     48 
     49   static_assert(
     50       sizeof(Message::nested_messages_arena_) >=
     51           kMaxNestingDepth *
     52               (sizeof(Message) - sizeof(Message::nested_messages_arena_)),
     53       "Message::nested_messages_arena_ is too small");
     54 
     55   stream_writer_ = stream_writer;
     56   size_ = 0;
     57   size_field_ = nullptr;
     58   size_already_written_ = 0;
     59   nested_message_ = nullptr;
     60   nesting_depth_ = 0;
     61   finalized_ = false;
     62 #if PERFETTO_DCHECK_IS_ON()
     63   handle_ = nullptr;
     64   generation_++;
     65 #endif
     66 }
     67 
     68 void Message::AppendString(uint32_t field_id, const char* str) {
     69   AppendBytes(field_id, str, strlen(str));
     70 }
     71 
     72 void Message::AppendBytes(uint32_t field_id, const void* src, size_t size) {
     73   if (nested_message_)
     74     EndNestedMessage();
     75 
     76   PERFETTO_DCHECK(size < proto_utils::kMaxMessageLength);
     77   // Write the proto preamble (field id, type and length of the field).
     78   uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize];
     79   uint8_t* pos = buffer;
     80   pos = proto_utils::WriteVarInt(proto_utils::MakeTagLengthDelimited(field_id),
     81                                  pos);
     82   pos = proto_utils::WriteVarInt(static_cast<uint32_t>(size), pos);
     83   WriteToStream(buffer, pos);
     84 
     85   const uint8_t* src_u8 = reinterpret_cast<const uint8_t*>(src);
     86   WriteToStream(src_u8, src_u8 + size);
     87 }
     88 
     89 uint32_t Message::Finalize() {
     90   if (finalized_)
     91     return size_;
     92 
     93   if (nested_message_)
     94     EndNestedMessage();
     95 
     96   // Write the length of the nested message a posteriori, using a leading-zero
     97   // redundant varint encoding.
     98   if (size_field_) {
     99     PERFETTO_DCHECK(!finalized_);
    100     PERFETTO_DCHECK(size_ < proto_utils::kMaxMessageLength);
    101     PERFETTO_DCHECK(size_ >= size_already_written_);
    102     proto_utils::WriteRedundantVarInt(size_ - size_already_written_,
    103                                       size_field_);
    104     size_field_ = nullptr;
    105   }
    106 
    107   finalized_ = true;
    108 #if PERFETTO_DCHECK_IS_ON()
    109   if (handle_)
    110     handle_->reset_message();
    111 #endif
    112 
    113   return size_;
    114 }
    115 
    116 void Message::BeginNestedMessageInternal(uint32_t field_id, Message* message) {
    117   if (nested_message_)
    118     EndNestedMessage();
    119 
    120   // Write the proto preamble for the nested message.
    121   uint8_t data[proto_utils::kMaxTagEncodedSize];
    122   uint8_t* data_end = proto_utils::WriteVarInt(
    123       proto_utils::MakeTagLengthDelimited(field_id), data);
    124   WriteToStream(data, data_end);
    125 
    126   message->Reset(stream_writer_);
    127   PERFETTO_CHECK(nesting_depth_ < kMaxNestingDepth);
    128   message->nesting_depth_ = nesting_depth_ + 1;
    129 
    130   // The length of the nested message cannot be known upfront. So right now
    131   // just reserve the bytes to encode the size after the nested message is done.
    132   message->set_size_field(
    133       stream_writer_->ReserveBytes(proto_utils::kMessageLengthFieldSize));
    134   size_ += proto_utils::kMessageLengthFieldSize;
    135   nested_message_ = message;
    136 }
    137 
    138 void Message::EndNestedMessage() {
    139   size_ += nested_message_->Finalize();
    140   nested_message_ = nullptr;
    141 }
    142 
    143 }  // namespace protozero
    144