File: proto_zero_message.h

package info (click to toggle)
chromium-browser 57.0.2987.98-1~deb8u1
  • links: PTS, VCS
  • area: main
  • in suites: jessie
  • size: 2,637,852 kB
  • ctags: 2,544,394
  • sloc: cpp: 12,815,961; ansic: 3,676,222; python: 1,147,112; asm: 526,608; java: 523,212; xml: 286,794; perl: 92,654; sh: 86,408; objc: 73,271; makefile: 27,698; cs: 18,487; yacc: 13,031; tcl: 12,957; pascal: 4,875; ml: 4,716; lex: 3,904; sql: 3,862; ruby: 1,982; lisp: 1,508; php: 1,368; exp: 404; awk: 325; csh: 117; jsp: 39; sed: 37
file content (213 lines) | stat: -rw-r--r-- 8,374 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_TRACING_CORE_PROTO_ZERO_MESSAGE_H_
#define COMPONENTS_TRACING_CORE_PROTO_ZERO_MESSAGE_H_

#include <stdint.h>

#include <type_traits>

#include "base/compiler_specific.h"
#include "base/gtest_prod_util.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/template_util.h"
#include "build/build_config.h"
#include "components/tracing/core/proto_utils.h"
#include "components/tracing/core/scattered_stream_writer.h"
#include "components/tracing/tracing_export.h"

namespace tracing {
namespace v2 {

class ProtoZeroMessageHandleBase;

// Base class extended by the proto C++ stubs generated by the ProtoZero
// compiler (see //components/tracing/tools/). This class provides the minimal
// runtime required to support append-only operations and is desiged for
// performance. None of the methods require any dynamic memory allocation.
class TRACING_EXPORT ProtoZeroMessage {
 public:
  ProtoZeroMessage();

  // Clears up the state, allowing the message to be reused as a fresh one.
  void Reset(ScatteredStreamWriter*);

  // 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.
  size_t Finalize();

  // Optional. If is_valid() == true, the corresponding memory region (its
  // length == proto::kMessageLengthFieldSize) is backfilled with the size of
  // this message (minus |size_already_written| below) when the message is
  // finalized. This is the mechanism used by messages to backfill their
  // corresponding size field in the parent message.
  ContiguousMemoryRange size_field() const { return size_field_; }
  void set_size_field(const ContiguousMemoryRange& reserved_range) {
    size_field_ = reserved_range;
  }

  // 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(size_t size) { size_already_written_ += size; }

#if DCHECK_IS_ON()
  // Only for ProtoZeroMessageHandleBase.
  void set_handle(ProtoZeroMessageHandleBase* handle) { handle_ = handle; }
#endif

 protected:
  // 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::kMaxSimpleFieldEncodedSize];
    uint8_t* pos = buffer;

    pos = proto::WriteVarInt(proto::MakeTagVarInt(field_id), pos);
    // WriteVarInt encodes signed values in two's complement form.
    pos = proto::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::ZigZagEncode(value));
  }

  // Proto types: bool, enum (small).
  // Faster version of AppendVarInt for tiny numbers.
  void AppendTinyVarInt(uint32_t field_id, int32_t value) {
    DCHECK(0 <= value && value < 0x80);
    if (nested_message_)
      EndNestedMessage();

    uint8_t buffer[proto::kMaxSimpleFieldEncodedSize];
    uint8_t* pos = buffer;
    // MakeTagVarInt gets super optimized here for constexpr.
    pos = proto::WriteVarInt(proto::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::kMaxSimpleFieldEncodedSize];
    uint8_t* pos = buffer;

    pos = proto::WriteVarInt(proto::MakeTagFixed<T>(field_id), pos);
    memcpy(pos, &value, sizeof(T));
    pos += sizeof(T);
    // TODO(kraynov): Optimize memcpy performance, see http://crbug.com/624311 .
    WriteToStream(buffer, pos);
  }

  void AppendString(uint32_t field_id, const char* str);
  void AppendBytes(uint32_t field_id, const void* value, size_t size);

  // 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
  // in the parent class.
  // The template argument T is supposed to be a stub class auto generated from
  // a .proto, hence a subclass of ProtoZeroMessage.
  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<ProtoZeroMessage, T>::value,
                  "T must be a subclass of ProtoZeroMessage");
    static_assert(sizeof(T) == sizeof(ProtoZeroMessage),
                  "ProtoZeroMessage subclasses cannot introduce extra state.");
    T* message = reinterpret_cast<T*>(nested_messages_arena_);
    BeginNestedMessageInternal(field_id, message);
    return message;
  }

 private:
  friend class ProtoZeroMessageTest;
  FRIEND_TEST_ALL_PREFIXES(ProtoZeroMessageTest, BasicTypesNoNesting);
  FRIEND_TEST_ALL_PREFIXES(ProtoZeroMessageTest, BackfillSizeOnFinalization);
  FRIEND_TEST_ALL_PREFIXES(ProtoZeroMessageTest, NestedMessagesSimple);
  FRIEND_TEST_ALL_PREFIXES(ProtoZeroMessageTest, StressTest);
  FRIEND_TEST_ALL_PREFIXES(ProtoZeroMessageTest, MessageHandle);

  enum : uint32_t { kMaxNestingDepth = 8 };

  void BeginNestedMessageInternal(uint32_t field_id, ProtoZeroMessage*);

  // Called by Finalize and Append* methods.
  void EndNestedMessage();

  void WriteToStream(const uint8_t* src_begin, const uint8_t* src_end) {
#if DCHECK_IS_ON()
    DCHECK(!sealed_);
#endif
    DCHECK(src_begin < src_end);
    const size_t size = static_cast<size_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 the corresponding .cc file.

  // The stream writer interface used for the serialization.
  ScatteredStreamWriter* stream_writer_;

  // Keeps track of the size of the current message.
  size_t size_;

  ContiguousMemoryRange size_field_;
  size_t size_already_written_;

  // Used to detect attemps to create messages with a nesting level >
  // kMaxNestingDepth. |nesting_depth_| == 0 for root (non-nested) messages.
  uint32_t nesting_depth_;

#if DCHECK_IS_ON()
  // 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 sealed_;

  ProtoZeroMessageHandleBase* handle_;
#endif

  // 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.
  ProtoZeroMessage* nested_message_;

  // The root message owns the storage for all its nested messages, up to a max
  // of kMaxNestingDepth levels (see the .cc file). Note that the boundaries of
  // the arena are meaningful only for the root message. The static_assert in
  // the .cc file guarantees that the sizeof(nested_messages_arena_) is enough
  // to contain up to kMaxNestingDepth messages.
  ALIGNAS(sizeof(void*)) uint8_t nested_messages_arena_[512];

  // DO NOT add any fields below |nested_messages_arena_|. The memory layout of
  // nested messages would overflow the storage allocated by the root message.

  DISALLOW_COPY_AND_ASSIGN(ProtoZeroMessage);
};

}  // namespace v2
}  // namespace tracing

#endif  // COMPONENTS_TRACING_CORE_PROTO_ZERO_MESSAGE_H_