xref: /aosp_15_r20/external/webrtc/logging/rtc_event_log/events/rtc_event_field_encoding.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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