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 #include "logging/rtc_event_log/events/rtc_event_field_encoding.h"
12
13 #include <algorithm>
14 #include <limits>
15 #include <memory>
16 #include <utility>
17
18 #include "logging/rtc_event_log/encoder/bit_writer.h"
19 #include "logging/rtc_event_log/encoder/var_int.h"
20 #include "logging/rtc_event_log/events/rtc_event_field_extraction.h"
21 #include "rtc_base/checks.h"
22 #include "rtc_base/logging.h"
23
24 using webrtc_event_logging::UnsignedDelta;
25
26 namespace {
27
SerializeLittleEndian(uint64_t value,uint8_t bytes)28 std::string SerializeLittleEndian(uint64_t value, uint8_t bytes) {
29 RTC_DCHECK_LE(bytes, sizeof(uint64_t));
30 RTC_DCHECK_GE(bytes, 1);
31 if (bytes < sizeof(uint64_t)) {
32 // Note that shifting a 64-bit value by 64 (or more) bits is undefined.
33 RTC_DCHECK_EQ(value >> (8 * bytes), 0);
34 }
35 std::string output(bytes, 0);
36 // Getting a non-const pointer to the representation. See e.g.
37 // https://en.cppreference.com/w/cpp/string/basic_string:
38 // "The elements of a basic_string are stored contiguously,
39 // that is, [...] a pointer to s[0] can be passed to functions
40 // that expect a pointer to the first element of a null-terminated
41 // CharT[] array."
42 uint8_t* p = reinterpret_cast<uint8_t*>(&output[0]);
43 #ifdef WEBRTC_ARCH_LITTLE_ENDIAN
44 memcpy(p, &value, bytes);
45 #else
46 while (bytes > 0) {
47 *p = static_cast<uint8_t>(value & 0xFF);
48 value >>= 8;
49 ++p;
50 --bytes;
51 }
52 #endif // WEBRTC_ARCH_LITTLE_ENDIAN
53 return output;
54 }
55
56 } // namespace
57
58 namespace webrtc {
59
EncodeOptionalValuePositions(std::vector<bool> positions)60 std::string EncodeOptionalValuePositions(std::vector<bool> positions) {
61 BitWriter writer((positions.size() + 7) / 8);
62 for (bool position : positions) {
63 writer.WriteBits(position ? 1u : 0u, 1);
64 }
65 return writer.GetString();
66 }
67
EncodeSingleValue(uint64_t value,FieldType field_type)68 std::string EncodeSingleValue(uint64_t value, FieldType field_type) {
69 switch (field_type) {
70 case FieldType::kFixed8:
71 return SerializeLittleEndian(value, /*bytes=*/1);
72 case FieldType::kFixed32:
73 return SerializeLittleEndian(value, /*bytes=*/4);
74 case FieldType::kFixed64:
75 return SerializeLittleEndian(value, /*bytes=*/8);
76 case FieldType::kVarInt:
77 return EncodeVarInt(value);
78 case FieldType::kString:
79 RTC_DCHECK_NOTREACHED();
80 return std::string();
81 }
82 RTC_DCHECK_NOTREACHED();
83 return std::string();
84 }
85
ConvertFieldType(uint64_t value)86 absl::optional<FieldType> ConvertFieldType(uint64_t value) {
87 switch (value) {
88 case static_cast<uint64_t>(FieldType::kFixed8):
89 return FieldType::kFixed8;
90 case static_cast<uint64_t>(FieldType::kFixed32):
91 return FieldType::kFixed32;
92 case static_cast<uint64_t>(FieldType::kFixed64):
93 return FieldType::kFixed64;
94 case static_cast<uint64_t>(FieldType::kVarInt):
95 return FieldType::kVarInt;
96 case static_cast<uint64_t>(FieldType::kString):
97 return FieldType::kString;
98 default:
99 return absl::nullopt;
100 }
101 }
102
EncodeDeltasV3(FixedLengthEncodingParametersV3 params,uint64_t base,rtc::ArrayView<const uint64_t> values)103 std::string EncodeDeltasV3(FixedLengthEncodingParametersV3 params,
104 uint64_t base,
105 rtc::ArrayView<const uint64_t> values) {
106 size_t outputbound = (values.size() * params.delta_bit_width() + 7) / 8;
107 BitWriter writer(outputbound);
108
109 uint64_t previous = base;
110 for (uint64_t value : values) {
111 if (params.signed_deltas()) {
112 uint64_t positive_delta =
113 UnsignedDelta(previous, value, params.value_mask());
114 uint64_t negative_delta =
115 UnsignedDelta(value, previous, params.value_mask());
116 uint64_t delta;
117 if (positive_delta <= negative_delta) {
118 delta = positive_delta;
119 } else {
120 // Compute the two's complement representation of a negative
121 // delta, in a field width params_.delta_mask().
122 RTC_DCHECK_GE(params.delta_mask(), negative_delta);
123 RTC_DCHECK_LT(params.delta_mask() - negative_delta,
124 params.delta_mask());
125 delta = params.delta_mask() - negative_delta + 1;
126 RTC_DCHECK_LE(delta, params.delta_mask());
127 }
128 writer.WriteBits(delta, params.delta_bit_width());
129 } else {
130 uint64_t delta = UnsignedDelta(previous, value, params.value_mask());
131 writer.WriteBits(delta, params.delta_bit_width());
132 }
133 previous = value;
134 }
135
136 return writer.GetString();
137 }
138
EventEncoder(EventParameters params,rtc::ArrayView<const RtcEvent * > batch)139 EventEncoder::EventEncoder(EventParameters params,
140 rtc::ArrayView<const RtcEvent*> batch) {
141 batch_size_ = batch.size();
142 if (!batch.empty()) {
143 // Encode event type.
144 uint32_t batched = batch.size() > 1 ? 1 : 0;
145 event_tag_ = (static_cast<uint32_t>(params.id) << 1) + batched;
146
147 // Event tag and number of encoded bytes will be filled in when the
148 // encoding is finalized in AsString().
149
150 // Encode number of events in batch
151 if (batched) {
152 encoded_fields_.push_back(EncodeVarInt(batch.size()));
153 }
154
155 // Encode timestamp
156 std::vector<uint64_t> timestamps;
157 timestamps.reserve(batch.size());
158 for (const RtcEvent* event : batch) {
159 timestamps.push_back(EncodeAsUnsigned(event->timestamp_ms()));
160 }
161 constexpr FieldParameters timestamp_params{"timestamp_ms",
162 FieldParameters::kTimestampField,
163 FieldType::kVarInt, 64};
164 EncodeField(timestamp_params, timestamps);
165 }
166 }
167
EncodeField(const FieldParameters & params,const ValuesWithPositions & values)168 void EventEncoder::EncodeField(const FieldParameters& params,
169 const ValuesWithPositions& values) {
170 return EncodeField(params, values.values, &values.position_mask);
171 }
172
EncodeField(const FieldParameters & params,const std::vector<uint64_t> & values,const std::vector<bool> * positions)173 void EventEncoder::EncodeField(const FieldParameters& params,
174 const std::vector<uint64_t>& values,
175 const std::vector<bool>* positions) {
176 if (positions) {
177 RTC_DCHECK_EQ(positions->size(), batch_size_);
178 RTC_DCHECK_LE(values.size(), batch_size_);
179 } else {
180 RTC_DCHECK_EQ(values.size(), batch_size_);
181 }
182
183 if (values.size() == 0) {
184 // If all values for a particular field is empty/nullopt,
185 // then we completely skip the field even if the the batch is non-empty.
186 return;
187 }
188
189 // We know that each event starts with the varint encoded timestamp,
190 // so we omit that field tag (field id + field type). In all other
191 // cases, we write the field tag.
192 if (params.field_id != FieldParameters::kTimestampField) {
193 RTC_DCHECK_LE(params.field_id, std::numeric_limits<uint64_t>::max() >> 3);
194 uint64_t field_tag = params.field_id << 3;
195 field_tag += static_cast<uint64_t>(params.field_type);
196 encoded_fields_.push_back(EncodeVarInt(field_tag));
197 }
198
199 RTC_CHECK_GE(values.size(), 1);
200 if (batch_size_ == 1) {
201 encoded_fields_.push_back(EncodeSingleValue(values[0], params.field_type));
202 return;
203 }
204
205 const bool values_optional = values.size() != batch_size_;
206
207 // Compute delta parameters
208 rtc::ArrayView<const uint64_t> all_values(values);
209 uint64_t base = values[0];
210 rtc::ArrayView<const uint64_t> remaining_values(all_values.subview(1));
211
212 FixedLengthEncodingParametersV3 delta_params =
213 FixedLengthEncodingParametersV3::CalculateParameters(
214 base, remaining_values, params.value_width, values_optional);
215
216 encoded_fields_.push_back(EncodeVarInt(delta_params.DeltaHeaderAsInt()));
217
218 if (values_optional) {
219 RTC_CHECK(positions);
220 encoded_fields_.push_back(EncodeOptionalValuePositions(*positions));
221 }
222 // Base element, encoded as uint8, uint32, uint64 or varint
223 encoded_fields_.push_back(EncodeSingleValue(base, params.field_type));
224
225 // If all (existing) values are equal to the base, then we can skip
226 // writing the all-zero deltas, and instead infer those from the delta
227 // header.
228 if (!delta_params.values_equal()) {
229 encoded_fields_.push_back(
230 EncodeDeltasV3(delta_params, base, remaining_values));
231 }
232 }
233
EncodeField(const FieldParameters & params,const std::vector<absl::string_view> & values)234 void EventEncoder::EncodeField(const FieldParameters& params,
235 const std::vector<absl::string_view>& values) {
236 RTC_DCHECK_EQ(values.size(), batch_size_);
237
238 if (values.size() == 0) {
239 // If all values for a particular field is empty/nullopt,
240 // then we completely skip the field even if the the batch is non-empty.
241 return;
242 }
243
244 // Write the field tag.
245 RTC_CHECK_NE(params.field_id, FieldParameters::kTimestampField);
246 RTC_DCHECK_LE(params.field_id, std::numeric_limits<uint64_t>::max() >> 3);
247 RTC_DCHECK_EQ(params.field_type, FieldType::kString);
248 uint64_t field_tag = params.field_id << 3;
249 field_tag += static_cast<uint64_t>(params.field_type);
250 encoded_fields_.push_back(EncodeVarInt(field_tag));
251
252 if (values.size() > 1) {
253 // If multiple values in the batch, write the encoding
254 // parameters. (Values >0 reserved for future use.)
255 uint64_t encoding_params = 0;
256 encoded_fields_.push_back(EncodeVarInt(encoding_params));
257 }
258
259 // Write the strings as (length, data) pairs.
260 for (absl::string_view s : values) {
261 encoded_fields_.push_back(EncodeVarInt(s.size()));
262 encoded_fields_.push_back(std::string(s));
263 }
264 }
265
AsString()266 std::string EventEncoder::AsString() {
267 std::string encoded_event;
268
269 if (batch_size_ == 0) {
270 RTC_DCHECK_EQ(encoded_fields_.size(), 0);
271 return encoded_event;
272 }
273
274 // Compute size of encoded fields.
275 size_t total_fields_size = 0;
276 for (const std::string& s : encoded_fields_) {
277 total_fields_size += s.size();
278 }
279
280 constexpr size_t kExpectedMaxEventTagBytes = 4;
281 constexpr size_t kExpectedMaxSizeEncodingBytes = 4;
282 encoded_event.reserve(kExpectedMaxEventTagBytes +
283 kExpectedMaxSizeEncodingBytes + total_fields_size);
284
285 // Encode event tag (event id and whether batch or single event).
286 encoded_event.append(EncodeVarInt(event_tag_));
287
288 // Encode size of the remaining fields.
289 encoded_event.append(EncodeVarInt(total_fields_size));
290
291 // Append encoded fields.
292 for (const std::string& s : encoded_fields_) {
293 encoded_event.append(s);
294 }
295
296 return encoded_event;
297 }
298
299 } // namespace webrtc
300