1 /*
2 * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FIELD_ENCODING_PARSER_H_
12 #define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FIELD_ENCODING_PARSER_H_
13
14 #include <string>
15 #include <vector>
16
17 #include "absl/strings/string_view.h"
18 #include "logging/rtc_event_log/events/rtc_event_field_encoding.h"
19
20 // TODO(terelius): Compared to a generic 'Status' class, this
21 // class allows us additional information about the context
22 // in which the error occurred. This is currently limited to
23 // the source location (file and line), but we plan on adding
24 // information about the event and field name being parsed.
25 // If/when we start using absl::Status in WebRTC, consider
26 // whether payloads would be an appropriate alternative.
27 class RtcEventLogParseStatus {
28 template <typename T>
29 friend class RtcEventLogParseStatusOr;
30
31 public:
Success()32 static RtcEventLogParseStatus Success() { return RtcEventLogParseStatus(); }
Error(absl::string_view error,absl::string_view file,int line)33 static RtcEventLogParseStatus Error(absl::string_view error,
34 absl::string_view file,
35 int line) {
36 return RtcEventLogParseStatus(error, file, line);
37 }
38
ok()39 bool ok() const { return error_.empty(); }
40 ABSL_DEPRECATED("Use ok() instead") explicit operator bool() const {
41 return ok();
42 }
43
message()44 std::string message() const { return error_; }
45
46 private:
RtcEventLogParseStatus()47 RtcEventLogParseStatus() : error_() {}
RtcEventLogParseStatus(absl::string_view error,absl::string_view file,int line)48 RtcEventLogParseStatus(absl::string_view error,
49 absl::string_view file,
50 int line)
51 : error_(std::string(error) + " (" + std::string(file) + ": " +
52 std::to_string(line) + ")") {}
53
54 std::string error_;
55 };
56
57 template <typename T>
58 class RtcEventLogParseStatusOr {
59 public:
RtcEventLogParseStatusOr(RtcEventLogParseStatus status)60 RtcEventLogParseStatusOr(RtcEventLogParseStatus status) // NOLINT
61 : status_(status), value_() {}
RtcEventLogParseStatusOr(const T & value)62 RtcEventLogParseStatusOr(const T& value) // NOLINT
63 : status_(), value_(value) {}
64
ok()65 bool ok() const { return status_.ok(); }
66
message()67 std::string message() const { return status_.message(); }
68
status()69 RtcEventLogParseStatus status() const { return status_; }
70
value()71 const T& value() const {
72 RTC_DCHECK(ok());
73 return value_;
74 }
75
value()76 T& value() {
77 RTC_DCHECK(ok());
78 return value_;
79 }
80
Error(absl::string_view error,absl::string_view file,int line)81 static RtcEventLogParseStatusOr Error(absl::string_view error,
82 absl::string_view file,
83 int line) {
84 return RtcEventLogParseStatusOr(error, file, line);
85 }
86
87 private:
RtcEventLogParseStatusOr()88 RtcEventLogParseStatusOr() : status_() {}
RtcEventLogParseStatusOr(absl::string_view error,absl::string_view file,int line)89 RtcEventLogParseStatusOr(absl::string_view error,
90 absl::string_view file,
91 int line)
92 : status_(error, file, line), value_() {}
93
94 RtcEventLogParseStatus status_;
95 T value_;
96 };
97
98 namespace webrtc {
99
100 class EventParser {
101 public:
102 struct ValueAndPostionView {
103 rtc::ArrayView<uint64_t> values;
104 rtc::ArrayView<uint8_t> positions;
105 };
106
107 EventParser() = default;
108
109 // N.B: This method stores a abls::string_view into the string to be
110 // parsed. The caller is responsible for ensuring that the actual string
111 // remains unmodified and outlives the EventParser.
112 RtcEventLogParseStatus Initialize(absl::string_view s, bool batched);
113
114 // Attempts to parse the field specified by `params`, skipping past
115 // other fields that may occur before it. If 'required_field == true',
116 // then failing to find the field is an error, otherwise the functions
117 // return success, but with an empty view of values.
118 RtcEventLogParseStatusOr<rtc::ArrayView<absl::string_view>> ParseStringField(
119 const FieldParameters& params,
120 bool required_field = true);
121 RtcEventLogParseStatusOr<rtc::ArrayView<uint64_t>> ParseNumericField(
122 const FieldParameters& params,
123 bool required_field = true);
124 RtcEventLogParseStatusOr<ValueAndPostionView> ParseOptionalNumericField(
125 const FieldParameters& params,
126 bool required_field = true);
127
128 // Number of events in a batch.
NumEventsInBatch()129 uint64_t NumEventsInBatch() const { return num_events_; }
130
131 // Bytes remaining in `pending_data_`. Assuming there are no unknown
132 // fields, BytesRemaining() should return 0 when all known fields
133 // in the event have been parsed.
RemainingBytes()134 size_t RemainingBytes() const { return pending_data_.size(); }
135
136 private:
137 uint64_t ReadLittleEndian(uint8_t bytes);
138 uint64_t ReadVarInt();
139 uint64_t ReadSingleValue(FieldType field_type);
140 uint64_t ReadOptionalValuePositions();
141 void ReadDeltasAndPopulateValues(FixedLengthEncodingParametersV3 params,
142 uint64_t num_deltas,
143 uint64_t base);
144 RtcEventLogParseStatus ParseNumericFieldInternal(uint64_t value_bit_width,
145 FieldType field_type);
146 RtcEventLogParseStatus ParseStringFieldInternal();
147
148 // Attempts to parse the field specified by `params`, skipping past
149 // other fields that may occur before it. Returns
150 // RtcEventLogParseStatus::Success() and populates `values_` (and
151 // `positions_`) if the field is found. Returns
152 // RtcEventLogParseStatus::Success() and clears `values_` (and `positions_`)
153 // if the field doesn't exist. Returns a RtcEventLogParseStatus::Error() if
154 // the log is incomplete, malformed or otherwise can't be parsed.
155 RtcEventLogParseStatus ParseField(const FieldParameters& params);
156
SetError()157 void SetError() { error_ = true; }
Ok()158 bool Ok() const { return !error_; }
159
GetValues()160 rtc::ArrayView<uint64_t> GetValues() { return values_; }
GetPositions()161 rtc::ArrayView<uint8_t> GetPositions() { return positions_; }
GetStrings()162 rtc::ArrayView<absl::string_view> GetStrings() { return strings_; }
163
ClearTemporaries()164 void ClearTemporaries() {
165 positions_.clear();
166 values_.clear();
167 strings_.clear();
168 }
169
170 // Tracks whether an error has occurred in one of the helper
171 // functions above.
172 bool error_ = false;
173
174 // Temporary storage for result.
175 std::vector<uint8_t> positions_;
176 std::vector<uint64_t> values_;
177 std::vector<absl::string_view> strings_;
178
179 // String to be consumed.
180 absl::string_view pending_data_;
181 uint64_t num_events_ = 1;
182 uint64_t last_field_id_ = FieldParameters::kTimestampField;
183 };
184
185 // Inverse of the ExtractRtcEventMember function used when parsing
186 // a log. Uses a vector of values to populate a specific field in a
187 // vector of structs.
188 template <typename T,
189 typename E,
190 std::enable_if_t<std::is_integral<T>::value, bool> = true>
191 ABSL_MUST_USE_RESULT RtcEventLogParseStatus
PopulateRtcEventMember(const rtc::ArrayView<uint64_t> values,T E::* member,rtc::ArrayView<E> output)192 PopulateRtcEventMember(const rtc::ArrayView<uint64_t> values,
193 T E::*member,
194 rtc::ArrayView<E> output) {
195 size_t batch_size = values.size();
196 RTC_CHECK_EQ(output.size(), batch_size);
197 for (size_t i = 0; i < batch_size; ++i) {
198 output[i].*member = DecodeFromUnsignedToType<T>(values[i]);
199 }
200 return RtcEventLogParseStatus::Success();
201 }
202
203 // Same as above, but for optional fields.
204 template <typename T,
205 typename E,
206 std::enable_if_t<std::is_integral<T>::value, bool> = true>
207 ABSL_MUST_USE_RESULT RtcEventLogParseStatus
PopulateRtcEventMember(const rtc::ArrayView<uint8_t> positions,const rtc::ArrayView<uint64_t> values,absl::optional<T> E::* member,rtc::ArrayView<E> output)208 PopulateRtcEventMember(const rtc::ArrayView<uint8_t> positions,
209 const rtc::ArrayView<uint64_t> values,
210 absl::optional<T> E::*member,
211 rtc::ArrayView<E> output) {
212 size_t batch_size = positions.size();
213 RTC_CHECK_EQ(output.size(), batch_size);
214 RTC_CHECK_LE(values.size(), batch_size);
215 auto value_it = values.begin();
216 for (size_t i = 0; i < batch_size; ++i) {
217 if (positions[i]) {
218 RTC_CHECK(value_it != values.end());
219 output[i].*member = DecodeFromUnsignedToType<T>(value_it);
220 ++value_it;
221 } else {
222 output[i].*member = absl::nullopt;
223 }
224 }
225 RTC_CHECK(value_it == values.end());
226 return RtcEventLogParseStatus::Success();
227 }
228
229 // Same as above, but for enum fields.
230 template <typename T,
231 typename E,
232 std::enable_if_t<std::is_enum<T>::value, bool> = true>
233 ABSL_MUST_USE_RESULT RtcEventLogParseStatus
PopulateRtcEventMember(const rtc::ArrayView<uint64_t> values,T E::* member,rtc::ArrayView<E> output)234 PopulateRtcEventMember(const rtc::ArrayView<uint64_t> values,
235 T E::*member,
236 rtc::ArrayView<E> output) {
237 size_t batch_size = values.size();
238 RTC_CHECK_EQ(output.size(), batch_size);
239 for (size_t i = 0; i < batch_size; ++i) {
240 auto result = RtcEventLogEnum<T>::Decode(values[i]);
241 if (!result.ok()) {
242 return result.status();
243 }
244 output[i].*member = result.value();
245 }
246 return RtcEventLogParseStatus::Success();
247 }
248
249 // Same as above, but for string fields.
250 template <typename E>
251 ABSL_MUST_USE_RESULT RtcEventLogParseStatus
PopulateRtcEventMember(const rtc::ArrayView<absl::string_view> values,std::string E::* member,rtc::ArrayView<E> output)252 PopulateRtcEventMember(const rtc::ArrayView<absl::string_view> values,
253 std::string E::*member,
254 rtc::ArrayView<E> output) {
255 size_t batch_size = values.size();
256 RTC_CHECK_EQ(output.size(), batch_size);
257 for (size_t i = 0; i < batch_size; ++i) {
258 output[i].*member = values[i];
259 }
260 return RtcEventLogParseStatus::Success();
261 }
262
263 // Same as above, but for Timestamp fields.
264 // N.B. Assumes that the encoded value uses millisecond precision.
265 template <typename E>
266 ABSL_MUST_USE_RESULT RtcEventLogParseStatus
PopulateRtcEventTimestamp(const rtc::ArrayView<uint64_t> & values,Timestamp E::* timestamp,rtc::ArrayView<E> output)267 PopulateRtcEventTimestamp(const rtc::ArrayView<uint64_t>& values,
268 Timestamp E::*timestamp,
269 rtc::ArrayView<E> output) {
270 size_t batch_size = values.size();
271 RTC_CHECK_EQ(batch_size, output.size());
272 for (size_t i = 0; i < batch_size; ++i) {
273 output[i].*timestamp =
274 Timestamp::Millis(DecodeFromUnsignedToType<int64_t>(values[i]));
275 }
276 return RtcEventLogParseStatus::Success();
277 }
278
279 template <typename E>
ExtendLoggedBatch(std::vector<E> & output,size_t new_elements)280 rtc::ArrayView<E> ExtendLoggedBatch(std::vector<E>& output,
281 size_t new_elements) {
282 size_t old_size = output.size();
283 output.insert(output.end(), old_size + new_elements, E());
284 rtc::ArrayView<E> output_batch = output;
285 output_batch.subview(old_size);
286 RTC_DCHECK_EQ(output_batch.size(), new_elements);
287 return output_batch;
288 }
289
290 } // namespace webrtc
291 #endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FIELD_ENCODING_PARSER_H_
292