1*6dbdd20aSAndroid Build Coastguard Worker /*
2*6dbdd20aSAndroid Build Coastguard Worker * Copyright (C) 2018 The Android Open Source Project
3*6dbdd20aSAndroid Build Coastguard Worker *
4*6dbdd20aSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*6dbdd20aSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*6dbdd20aSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*6dbdd20aSAndroid Build Coastguard Worker *
8*6dbdd20aSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*6dbdd20aSAndroid Build Coastguard Worker *
10*6dbdd20aSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*6dbdd20aSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*6dbdd20aSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*6dbdd20aSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*6dbdd20aSAndroid Build Coastguard Worker * limitations under the License.
15*6dbdd20aSAndroid Build Coastguard Worker */
16*6dbdd20aSAndroid Build Coastguard Worker
17*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/protozero/proto_decoder.h"
18*6dbdd20aSAndroid Build Coastguard Worker
19*6dbdd20aSAndroid Build Coastguard Worker #include <string.h>
20*6dbdd20aSAndroid Build Coastguard Worker
21*6dbdd20aSAndroid Build Coastguard Worker #include <cinttypes>
22*6dbdd20aSAndroid Build Coastguard Worker #include <limits>
23*6dbdd20aSAndroid Build Coastguard Worker #include <memory>
24*6dbdd20aSAndroid Build Coastguard Worker
25*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/base/compiler.h"
26*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/base/logging.h"
27*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/ext/base/utils.h"
28*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/protozero/proto_utils.h"
29*6dbdd20aSAndroid Build Coastguard Worker
30*6dbdd20aSAndroid Build Coastguard Worker namespace protozero {
31*6dbdd20aSAndroid Build Coastguard Worker
32*6dbdd20aSAndroid Build Coastguard Worker using namespace proto_utils;
33*6dbdd20aSAndroid Build Coastguard Worker
34*6dbdd20aSAndroid Build Coastguard Worker #if !PERFETTO_IS_LITTLE_ENDIAN()
35*6dbdd20aSAndroid Build Coastguard Worker #error Unimplemented for big endian archs.
36*6dbdd20aSAndroid Build Coastguard Worker #endif
37*6dbdd20aSAndroid Build Coastguard Worker
38*6dbdd20aSAndroid Build Coastguard Worker namespace {
39*6dbdd20aSAndroid Build Coastguard Worker
40*6dbdd20aSAndroid Build Coastguard Worker struct ParseFieldResult {
41*6dbdd20aSAndroid Build Coastguard Worker enum ParseResult { kAbort, kSkip, kOk };
42*6dbdd20aSAndroid Build Coastguard Worker ParseResult parse_res;
43*6dbdd20aSAndroid Build Coastguard Worker const uint8_t* next;
44*6dbdd20aSAndroid Build Coastguard Worker Field field;
45*6dbdd20aSAndroid Build Coastguard Worker };
46*6dbdd20aSAndroid Build Coastguard Worker
47*6dbdd20aSAndroid Build Coastguard Worker // Parses one field and returns the field itself and a pointer to the next
48*6dbdd20aSAndroid Build Coastguard Worker // field to parse. If parsing fails, the returned |next| == |buffer|.
ParseOneField(const uint8_t * const buffer,const uint8_t * const end)49*6dbdd20aSAndroid Build Coastguard Worker ParseFieldResult ParseOneField(const uint8_t* const buffer,
50*6dbdd20aSAndroid Build Coastguard Worker const uint8_t* const end) {
51*6dbdd20aSAndroid Build Coastguard Worker ParseFieldResult res{ParseFieldResult::kAbort, buffer, Field{}};
52*6dbdd20aSAndroid Build Coastguard Worker
53*6dbdd20aSAndroid Build Coastguard Worker // The first byte of a proto field is structured as follows:
54*6dbdd20aSAndroid Build Coastguard Worker // The least 3 significant bits determine the field type.
55*6dbdd20aSAndroid Build Coastguard Worker // The most 5 significant bits determine the field id. If MSB == 1, the
56*6dbdd20aSAndroid Build Coastguard Worker // field id continues on the next bytes following the VarInt encoding.
57*6dbdd20aSAndroid Build Coastguard Worker const uint8_t kFieldTypeNumBits = 3;
58*6dbdd20aSAndroid Build Coastguard Worker const uint64_t kFieldTypeMask = (1 << kFieldTypeNumBits) - 1; // 0000 0111;
59*6dbdd20aSAndroid Build Coastguard Worker const uint8_t* pos = buffer;
60*6dbdd20aSAndroid Build Coastguard Worker
61*6dbdd20aSAndroid Build Coastguard Worker // If we've already hit the end, just return an invalid field.
62*6dbdd20aSAndroid Build Coastguard Worker if (PERFETTO_UNLIKELY(pos >= end))
63*6dbdd20aSAndroid Build Coastguard Worker return res;
64*6dbdd20aSAndroid Build Coastguard Worker
65*6dbdd20aSAndroid Build Coastguard Worker uint64_t preamble = 0;
66*6dbdd20aSAndroid Build Coastguard Worker if (PERFETTO_LIKELY(*pos < 0x80)) { // Fastpath for fields with ID < 16.
67*6dbdd20aSAndroid Build Coastguard Worker preamble = *(pos++);
68*6dbdd20aSAndroid Build Coastguard Worker } else {
69*6dbdd20aSAndroid Build Coastguard Worker const uint8_t* next = ParseVarInt(pos, end, &preamble);
70*6dbdd20aSAndroid Build Coastguard Worker if (PERFETTO_UNLIKELY(pos == next))
71*6dbdd20aSAndroid Build Coastguard Worker return res;
72*6dbdd20aSAndroid Build Coastguard Worker pos = next;
73*6dbdd20aSAndroid Build Coastguard Worker }
74*6dbdd20aSAndroid Build Coastguard Worker
75*6dbdd20aSAndroid Build Coastguard Worker uint32_t field_id = static_cast<uint32_t>(preamble >> kFieldTypeNumBits);
76*6dbdd20aSAndroid Build Coastguard Worker if (field_id == 0 || pos >= end)
77*6dbdd20aSAndroid Build Coastguard Worker return res;
78*6dbdd20aSAndroid Build Coastguard Worker
79*6dbdd20aSAndroid Build Coastguard Worker auto field_type = static_cast<uint8_t>(preamble & kFieldTypeMask);
80*6dbdd20aSAndroid Build Coastguard Worker const uint8_t* new_pos = pos;
81*6dbdd20aSAndroid Build Coastguard Worker uint64_t int_value = 0;
82*6dbdd20aSAndroid Build Coastguard Worker uint64_t size = 0;
83*6dbdd20aSAndroid Build Coastguard Worker
84*6dbdd20aSAndroid Build Coastguard Worker switch (field_type) {
85*6dbdd20aSAndroid Build Coastguard Worker case static_cast<uint8_t>(ProtoWireType::kVarInt): {
86*6dbdd20aSAndroid Build Coastguard Worker new_pos = ParseVarInt(pos, end, &int_value);
87*6dbdd20aSAndroid Build Coastguard Worker
88*6dbdd20aSAndroid Build Coastguard Worker // new_pos not being greater than pos means ParseVarInt could not fully
89*6dbdd20aSAndroid Build Coastguard Worker // parse the number. This is because we are out of space in the buffer.
90*6dbdd20aSAndroid Build Coastguard Worker // Set the id to zero and return but don't update the offset so a future
91*6dbdd20aSAndroid Build Coastguard Worker // read can read this field.
92*6dbdd20aSAndroid Build Coastguard Worker if (PERFETTO_UNLIKELY(new_pos == pos))
93*6dbdd20aSAndroid Build Coastguard Worker return res;
94*6dbdd20aSAndroid Build Coastguard Worker
95*6dbdd20aSAndroid Build Coastguard Worker break;
96*6dbdd20aSAndroid Build Coastguard Worker }
97*6dbdd20aSAndroid Build Coastguard Worker
98*6dbdd20aSAndroid Build Coastguard Worker case static_cast<uint8_t>(ProtoWireType::kLengthDelimited): {
99*6dbdd20aSAndroid Build Coastguard Worker uint64_t payload_length;
100*6dbdd20aSAndroid Build Coastguard Worker new_pos = ParseVarInt(pos, end, &payload_length);
101*6dbdd20aSAndroid Build Coastguard Worker if (PERFETTO_UNLIKELY(new_pos == pos))
102*6dbdd20aSAndroid Build Coastguard Worker return res;
103*6dbdd20aSAndroid Build Coastguard Worker
104*6dbdd20aSAndroid Build Coastguard Worker // ParseVarInt guarantees that |new_pos| <= |end| when it succeeds;
105*6dbdd20aSAndroid Build Coastguard Worker if (payload_length > static_cast<uint64_t>(end - new_pos))
106*6dbdd20aSAndroid Build Coastguard Worker return res;
107*6dbdd20aSAndroid Build Coastguard Worker
108*6dbdd20aSAndroid Build Coastguard Worker const uintptr_t payload_start = reinterpret_cast<uintptr_t>(new_pos);
109*6dbdd20aSAndroid Build Coastguard Worker int_value = payload_start;
110*6dbdd20aSAndroid Build Coastguard Worker size = payload_length;
111*6dbdd20aSAndroid Build Coastguard Worker new_pos += payload_length;
112*6dbdd20aSAndroid Build Coastguard Worker break;
113*6dbdd20aSAndroid Build Coastguard Worker }
114*6dbdd20aSAndroid Build Coastguard Worker
115*6dbdd20aSAndroid Build Coastguard Worker case static_cast<uint8_t>(ProtoWireType::kFixed64): {
116*6dbdd20aSAndroid Build Coastguard Worker new_pos = pos + sizeof(uint64_t);
117*6dbdd20aSAndroid Build Coastguard Worker if (PERFETTO_UNLIKELY(new_pos > end))
118*6dbdd20aSAndroid Build Coastguard Worker return res;
119*6dbdd20aSAndroid Build Coastguard Worker memcpy(&int_value, pos, sizeof(uint64_t));
120*6dbdd20aSAndroid Build Coastguard Worker break;
121*6dbdd20aSAndroid Build Coastguard Worker }
122*6dbdd20aSAndroid Build Coastguard Worker
123*6dbdd20aSAndroid Build Coastguard Worker case static_cast<uint8_t>(ProtoWireType::kFixed32): {
124*6dbdd20aSAndroid Build Coastguard Worker new_pos = pos + sizeof(uint32_t);
125*6dbdd20aSAndroid Build Coastguard Worker if (PERFETTO_UNLIKELY(new_pos > end))
126*6dbdd20aSAndroid Build Coastguard Worker return res;
127*6dbdd20aSAndroid Build Coastguard Worker memcpy(&int_value, pos, sizeof(uint32_t));
128*6dbdd20aSAndroid Build Coastguard Worker break;
129*6dbdd20aSAndroid Build Coastguard Worker }
130*6dbdd20aSAndroid Build Coastguard Worker
131*6dbdd20aSAndroid Build Coastguard Worker default:
132*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DLOG("Invalid proto field type: %u", field_type);
133*6dbdd20aSAndroid Build Coastguard Worker return res;
134*6dbdd20aSAndroid Build Coastguard Worker }
135*6dbdd20aSAndroid Build Coastguard Worker
136*6dbdd20aSAndroid Build Coastguard Worker res.next = new_pos;
137*6dbdd20aSAndroid Build Coastguard Worker
138*6dbdd20aSAndroid Build Coastguard Worker if (PERFETTO_UNLIKELY(field_id > Field::kMaxId)) {
139*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DLOG("Skipping field %" PRIu32 " because its id > %" PRIu32,
140*6dbdd20aSAndroid Build Coastguard Worker field_id, Field::kMaxId);
141*6dbdd20aSAndroid Build Coastguard Worker res.parse_res = ParseFieldResult::kSkip;
142*6dbdd20aSAndroid Build Coastguard Worker return res;
143*6dbdd20aSAndroid Build Coastguard Worker }
144*6dbdd20aSAndroid Build Coastguard Worker
145*6dbdd20aSAndroid Build Coastguard Worker if (PERFETTO_UNLIKELY(size > proto_utils::kMaxMessageLength)) {
146*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DLOG("Skipping field %" PRIu32 " because it's too big (%" PRIu64
147*6dbdd20aSAndroid Build Coastguard Worker " KB)",
148*6dbdd20aSAndroid Build Coastguard Worker field_id, size / 1024);
149*6dbdd20aSAndroid Build Coastguard Worker res.parse_res = ParseFieldResult::kSkip;
150*6dbdd20aSAndroid Build Coastguard Worker return res;
151*6dbdd20aSAndroid Build Coastguard Worker }
152*6dbdd20aSAndroid Build Coastguard Worker
153*6dbdd20aSAndroid Build Coastguard Worker res.parse_res = ParseFieldResult::kOk;
154*6dbdd20aSAndroid Build Coastguard Worker res.field.initialize(field_id, field_type, int_value,
155*6dbdd20aSAndroid Build Coastguard Worker static_cast<uint32_t>(size));
156*6dbdd20aSAndroid Build Coastguard Worker return res;
157*6dbdd20aSAndroid Build Coastguard Worker }
158*6dbdd20aSAndroid Build Coastguard Worker
159*6dbdd20aSAndroid Build Coastguard Worker } // namespace
160*6dbdd20aSAndroid Build Coastguard Worker
FindField(uint32_t field_id)161*6dbdd20aSAndroid Build Coastguard Worker Field ProtoDecoder::FindField(uint32_t field_id) {
162*6dbdd20aSAndroid Build Coastguard Worker Field res{};
163*6dbdd20aSAndroid Build Coastguard Worker auto old_position = read_ptr_;
164*6dbdd20aSAndroid Build Coastguard Worker read_ptr_ = begin_;
165*6dbdd20aSAndroid Build Coastguard Worker for (auto f = ReadField(); f.valid(); f = ReadField()) {
166*6dbdd20aSAndroid Build Coastguard Worker if (f.id() == field_id) {
167*6dbdd20aSAndroid Build Coastguard Worker res = f;
168*6dbdd20aSAndroid Build Coastguard Worker break;
169*6dbdd20aSAndroid Build Coastguard Worker }
170*6dbdd20aSAndroid Build Coastguard Worker }
171*6dbdd20aSAndroid Build Coastguard Worker read_ptr_ = old_position;
172*6dbdd20aSAndroid Build Coastguard Worker return res;
173*6dbdd20aSAndroid Build Coastguard Worker }
174*6dbdd20aSAndroid Build Coastguard Worker
ReadField()175*6dbdd20aSAndroid Build Coastguard Worker Field ProtoDecoder::ReadField() {
176*6dbdd20aSAndroid Build Coastguard Worker ParseFieldResult res;
177*6dbdd20aSAndroid Build Coastguard Worker do {
178*6dbdd20aSAndroid Build Coastguard Worker res = ParseOneField(read_ptr_, end_);
179*6dbdd20aSAndroid Build Coastguard Worker read_ptr_ = res.next;
180*6dbdd20aSAndroid Build Coastguard Worker } while (PERFETTO_UNLIKELY(res.parse_res == ParseFieldResult::kSkip));
181*6dbdd20aSAndroid Build Coastguard Worker return res.field;
182*6dbdd20aSAndroid Build Coastguard Worker }
183*6dbdd20aSAndroid Build Coastguard Worker
ParseAllFields()184*6dbdd20aSAndroid Build Coastguard Worker void TypedProtoDecoderBase::ParseAllFields() {
185*6dbdd20aSAndroid Build Coastguard Worker const uint8_t* cur = begin_;
186*6dbdd20aSAndroid Build Coastguard Worker ParseFieldResult res;
187*6dbdd20aSAndroid Build Coastguard Worker for (;;) {
188*6dbdd20aSAndroid Build Coastguard Worker res = ParseOneField(cur, end_);
189*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DCHECK(res.parse_res != ParseFieldResult::kOk || res.next != cur);
190*6dbdd20aSAndroid Build Coastguard Worker cur = res.next;
191*6dbdd20aSAndroid Build Coastguard Worker if (PERFETTO_UNLIKELY(res.parse_res == ParseFieldResult::kSkip))
192*6dbdd20aSAndroid Build Coastguard Worker continue;
193*6dbdd20aSAndroid Build Coastguard Worker if (PERFETTO_UNLIKELY(res.parse_res == ParseFieldResult::kAbort))
194*6dbdd20aSAndroid Build Coastguard Worker break;
195*6dbdd20aSAndroid Build Coastguard Worker
196*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DCHECK(res.parse_res == ParseFieldResult::kOk);
197*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DCHECK(res.field.valid());
198*6dbdd20aSAndroid Build Coastguard Worker auto field_id = res.field.id();
199*6dbdd20aSAndroid Build Coastguard Worker if (PERFETTO_UNLIKELY(field_id >= num_fields_))
200*6dbdd20aSAndroid Build Coastguard Worker continue;
201*6dbdd20aSAndroid Build Coastguard Worker
202*6dbdd20aSAndroid Build Coastguard Worker // There are two reasons why we might want to expand the heap capacity:
203*6dbdd20aSAndroid Build Coastguard Worker // 1. We are writing a non-repeated field, which has an id >
204*6dbdd20aSAndroid Build Coastguard Worker // INITIAL_STACK_CAPACITY. In this case ExpandHeapStorage() ensures to
205*6dbdd20aSAndroid Build Coastguard Worker // allocate at least (num_fields_ + 1) slots.
206*6dbdd20aSAndroid Build Coastguard Worker // 2. We are writing a repeated field but ran out of capacity.
207*6dbdd20aSAndroid Build Coastguard Worker if (PERFETTO_UNLIKELY(field_id >= size_ || size_ >= capacity_))
208*6dbdd20aSAndroid Build Coastguard Worker ExpandHeapStorage();
209*6dbdd20aSAndroid Build Coastguard Worker
210*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DCHECK(field_id < size_);
211*6dbdd20aSAndroid Build Coastguard Worker Field* fld = &fields_[field_id];
212*6dbdd20aSAndroid Build Coastguard Worker if (PERFETTO_LIKELY(!fld->valid())) {
213*6dbdd20aSAndroid Build Coastguard Worker // This is the first time we see this field.
214*6dbdd20aSAndroid Build Coastguard Worker *fld = std::move(res.field);
215*6dbdd20aSAndroid Build Coastguard Worker } else {
216*6dbdd20aSAndroid Build Coastguard Worker // Repeated field case.
217*6dbdd20aSAndroid Build Coastguard Worker // In this case we need to:
218*6dbdd20aSAndroid Build Coastguard Worker // 1. Append the last value of the field to end of the repeated field
219*6dbdd20aSAndroid Build Coastguard Worker // storage.
220*6dbdd20aSAndroid Build Coastguard Worker // 2. Replace the default instance at offset |field_id| with the current
221*6dbdd20aSAndroid Build Coastguard Worker // value. This is because in case of repeated field a call to Get(X) is
222*6dbdd20aSAndroid Build Coastguard Worker // supposed to return the last value of X, not the first one.
223*6dbdd20aSAndroid Build Coastguard Worker // This is so that the RepeatedFieldIterator will iterate in the right
224*6dbdd20aSAndroid Build Coastguard Worker // order, see comments on RepeatedFieldIterator.
225*6dbdd20aSAndroid Build Coastguard Worker if (num_fields_ > size_) {
226*6dbdd20aSAndroid Build Coastguard Worker ExpandHeapStorage();
227*6dbdd20aSAndroid Build Coastguard Worker fld = &fields_[field_id];
228*6dbdd20aSAndroid Build Coastguard Worker }
229*6dbdd20aSAndroid Build Coastguard Worker
230*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DCHECK(size_ < capacity_);
231*6dbdd20aSAndroid Build Coastguard Worker fields_[size_++] = *fld;
232*6dbdd20aSAndroid Build Coastguard Worker *fld = std::move(res.field);
233*6dbdd20aSAndroid Build Coastguard Worker }
234*6dbdd20aSAndroid Build Coastguard Worker }
235*6dbdd20aSAndroid Build Coastguard Worker read_ptr_ = res.next;
236*6dbdd20aSAndroid Build Coastguard Worker }
237*6dbdd20aSAndroid Build Coastguard Worker
ExpandHeapStorage()238*6dbdd20aSAndroid Build Coastguard Worker void TypedProtoDecoderBase::ExpandHeapStorage() {
239*6dbdd20aSAndroid Build Coastguard Worker // When we expand the heap we must ensure that we have at very last capacity
240*6dbdd20aSAndroid Build Coastguard Worker // to deal with all known fields plus at least one repeated field. We go +2048
241*6dbdd20aSAndroid Build Coastguard Worker // here based on observations on a large 4GB android trace. This is to avoid
242*6dbdd20aSAndroid Build Coastguard Worker // trivial re-allocations when dealing with repeated fields of a message that
243*6dbdd20aSAndroid Build Coastguard Worker // has > INITIAL_STACK_CAPACITY fields.
244*6dbdd20aSAndroid Build Coastguard Worker const uint32_t min_capacity = num_fields_ + 2048; // Any num >= +1 will do.
245*6dbdd20aSAndroid Build Coastguard Worker const uint32_t new_capacity = std::max(capacity_ * 2, min_capacity);
246*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_CHECK(new_capacity > size_ && new_capacity > num_fields_);
247*6dbdd20aSAndroid Build Coastguard Worker std::unique_ptr<Field[]> new_storage(new Field[new_capacity]);
248*6dbdd20aSAndroid Build Coastguard Worker
249*6dbdd20aSAndroid Build Coastguard Worker static_assert(std::is_trivially_constructible<Field>::value,
250*6dbdd20aSAndroid Build Coastguard Worker "Field must be trivially constructible");
251*6dbdd20aSAndroid Build Coastguard Worker static_assert(std::is_trivially_copyable<Field>::value,
252*6dbdd20aSAndroid Build Coastguard Worker "Field must be trivially copyable");
253*6dbdd20aSAndroid Build Coastguard Worker
254*6dbdd20aSAndroid Build Coastguard Worker // Zero-initialize the slots for known field IDs slots, as they can be
255*6dbdd20aSAndroid Build Coastguard Worker // randomly accessed. Instead, there is no need to initialize the repeated
256*6dbdd20aSAndroid Build Coastguard Worker // slots, because they are written linearly with no gaps and are always
257*6dbdd20aSAndroid Build Coastguard Worker // initialized before incrementing |size_|.
258*6dbdd20aSAndroid Build Coastguard Worker const uint32_t new_size = std::max(size_, num_fields_);
259*6dbdd20aSAndroid Build Coastguard Worker memset(&new_storage[size_], 0, sizeof(Field) * (new_size - size_));
260*6dbdd20aSAndroid Build Coastguard Worker
261*6dbdd20aSAndroid Build Coastguard Worker memcpy(&new_storage[0], fields_, sizeof(Field) * size_);
262*6dbdd20aSAndroid Build Coastguard Worker
263*6dbdd20aSAndroid Build Coastguard Worker heap_storage_ = std::move(new_storage);
264*6dbdd20aSAndroid Build Coastguard Worker fields_ = &heap_storage_[0];
265*6dbdd20aSAndroid Build Coastguard Worker capacity_ = new_capacity;
266*6dbdd20aSAndroid Build Coastguard Worker size_ = new_size;
267*6dbdd20aSAndroid Build Coastguard Worker }
268*6dbdd20aSAndroid Build Coastguard Worker
269*6dbdd20aSAndroid Build Coastguard Worker } // namespace protozero
270