summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_encoding.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_encoding.cc')
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_encoding.cc299
1 files changed, 299 insertions, 0 deletions
diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_encoding.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_encoding.cc
new file mode 100644
index 0000000000..68188ce856
--- /dev/null
+++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_encoding.cc
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "logging/rtc_event_log/events/rtc_event_field_encoding.h"
+
+#include <algorithm>
+#include <limits>
+#include <memory>
+#include <utility>
+
+#include "logging/rtc_event_log/encoder/bit_writer.h"
+#include "logging/rtc_event_log/encoder/var_int.h"
+#include "logging/rtc_event_log/events/rtc_event_field_extraction.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+using webrtc_event_logging::UnsignedDelta;
+
+namespace {
+
+std::string SerializeLittleEndian(uint64_t value, uint8_t bytes) {
+ RTC_DCHECK_LE(bytes, sizeof(uint64_t));
+ RTC_DCHECK_GE(bytes, 1);
+ if (bytes < sizeof(uint64_t)) {
+ // Note that shifting a 64-bit value by 64 (or more) bits is undefined.
+ RTC_DCHECK_EQ(value >> (8 * bytes), 0);
+ }
+ std::string output(bytes, 0);
+ // Getting a non-const pointer to the representation. See e.g.
+ // https://en.cppreference.com/w/cpp/string/basic_string:
+ // "The elements of a basic_string are stored contiguously,
+ // that is, [...] a pointer to s[0] can be passed to functions
+ // that expect a pointer to the first element of a null-terminated
+ // CharT[] array."
+ uint8_t* p = reinterpret_cast<uint8_t*>(&output[0]);
+#ifdef WEBRTC_ARCH_LITTLE_ENDIAN
+ memcpy(p, &value, bytes);
+#else
+ while (bytes > 0) {
+ *p = static_cast<uint8_t>(value & 0xFF);
+ value >>= 8;
+ ++p;
+ --bytes;
+ }
+#endif // WEBRTC_ARCH_LITTLE_ENDIAN
+ return output;
+}
+
+} // namespace
+
+namespace webrtc {
+
+std::string EncodeOptionalValuePositions(std::vector<bool> positions) {
+ BitWriter writer((positions.size() + 7) / 8);
+ for (bool position : positions) {
+ writer.WriteBits(position ? 1u : 0u, 1);
+ }
+ return writer.GetString();
+}
+
+std::string EncodeSingleValue(uint64_t value, FieldType field_type) {
+ switch (field_type) {
+ case FieldType::kFixed8:
+ return SerializeLittleEndian(value, /*bytes=*/1);
+ case FieldType::kFixed32:
+ return SerializeLittleEndian(value, /*bytes=*/4);
+ case FieldType::kFixed64:
+ return SerializeLittleEndian(value, /*bytes=*/8);
+ case FieldType::kVarInt:
+ return EncodeVarInt(value);
+ case FieldType::kString:
+ RTC_DCHECK_NOTREACHED();
+ return std::string();
+ }
+ RTC_DCHECK_NOTREACHED();
+ return std::string();
+}
+
+absl::optional<FieldType> ConvertFieldType(uint64_t value) {
+ switch (value) {
+ case static_cast<uint64_t>(FieldType::kFixed8):
+ return FieldType::kFixed8;
+ case static_cast<uint64_t>(FieldType::kFixed32):
+ return FieldType::kFixed32;
+ case static_cast<uint64_t>(FieldType::kFixed64):
+ return FieldType::kFixed64;
+ case static_cast<uint64_t>(FieldType::kVarInt):
+ return FieldType::kVarInt;
+ case static_cast<uint64_t>(FieldType::kString):
+ return FieldType::kString;
+ default:
+ return absl::nullopt;
+ }
+}
+
+std::string EncodeDeltasV3(FixedLengthEncodingParametersV3 params,
+ uint64_t base,
+ rtc::ArrayView<const uint64_t> values) {
+ size_t outputbound = (values.size() * params.delta_bit_width() + 7) / 8;
+ BitWriter writer(outputbound);
+
+ uint64_t previous = base;
+ for (uint64_t value : values) {
+ if (params.signed_deltas()) {
+ uint64_t positive_delta =
+ UnsignedDelta(previous, value, params.value_mask());
+ uint64_t negative_delta =
+ UnsignedDelta(value, previous, params.value_mask());
+ uint64_t delta;
+ if (positive_delta <= negative_delta) {
+ delta = positive_delta;
+ } else {
+ // Compute the two's complement representation of a negative
+ // delta, in a field width params_.delta_mask().
+ RTC_DCHECK_GE(params.delta_mask(), negative_delta);
+ RTC_DCHECK_LT(params.delta_mask() - negative_delta,
+ params.delta_mask());
+ delta = params.delta_mask() - negative_delta + 1;
+ RTC_DCHECK_LE(delta, params.delta_mask());
+ }
+ writer.WriteBits(delta, params.delta_bit_width());
+ } else {
+ uint64_t delta = UnsignedDelta(previous, value, params.value_mask());
+ writer.WriteBits(delta, params.delta_bit_width());
+ }
+ previous = value;
+ }
+
+ return writer.GetString();
+}
+
+EventEncoder::EventEncoder(EventParameters params,
+ rtc::ArrayView<const RtcEvent*> batch) {
+ batch_size_ = batch.size();
+ if (!batch.empty()) {
+ // Encode event type.
+ uint32_t batched = batch.size() > 1 ? 1 : 0;
+ event_tag_ = (static_cast<uint32_t>(params.id) << 1) + batched;
+
+ // Event tag and number of encoded bytes will be filled in when the
+ // encoding is finalized in AsString().
+
+ // Encode number of events in batch
+ if (batched) {
+ encoded_fields_.push_back(EncodeVarInt(batch.size()));
+ }
+
+ // Encode timestamp
+ std::vector<uint64_t> timestamps;
+ timestamps.reserve(batch.size());
+ for (const RtcEvent* event : batch) {
+ timestamps.push_back(EncodeAsUnsigned(event->timestamp_ms()));
+ }
+ constexpr FieldParameters timestamp_params{"timestamp_ms",
+ FieldParameters::kTimestampField,
+ FieldType::kVarInt, 64};
+ EncodeField(timestamp_params, timestamps);
+ }
+}
+
+void EventEncoder::EncodeField(const FieldParameters& params,
+ const ValuesWithPositions& values) {
+ return EncodeField(params, values.values, &values.position_mask);
+}
+
+void EventEncoder::EncodeField(const FieldParameters& params,
+ const std::vector<uint64_t>& values,
+ const std::vector<bool>* positions) {
+ if (positions) {
+ RTC_DCHECK_EQ(positions->size(), batch_size_);
+ RTC_DCHECK_LE(values.size(), batch_size_);
+ } else {
+ RTC_DCHECK_EQ(values.size(), batch_size_);
+ }
+
+ if (values.size() == 0) {
+ // If all values for a particular field is empty/nullopt,
+ // then we completely skip the field even if the the batch is non-empty.
+ return;
+ }
+
+ // We know that each event starts with the varint encoded timestamp,
+ // so we omit that field tag (field id + field type). In all other
+ // cases, we write the field tag.
+ if (params.field_id != FieldParameters::kTimestampField) {
+ RTC_DCHECK_LE(params.field_id, std::numeric_limits<uint64_t>::max() >> 3);
+ uint64_t field_tag = params.field_id << 3;
+ field_tag += static_cast<uint64_t>(params.field_type);
+ encoded_fields_.push_back(EncodeVarInt(field_tag));
+ }
+
+ RTC_CHECK_GE(values.size(), 1);
+ if (batch_size_ == 1) {
+ encoded_fields_.push_back(EncodeSingleValue(values[0], params.field_type));
+ return;
+ }
+
+ const bool values_optional = values.size() != batch_size_;
+
+ // Compute delta parameters
+ rtc::ArrayView<const uint64_t> all_values(values);
+ uint64_t base = values[0];
+ rtc::ArrayView<const uint64_t> remaining_values(all_values.subview(1));
+
+ FixedLengthEncodingParametersV3 delta_params =
+ FixedLengthEncodingParametersV3::CalculateParameters(
+ base, remaining_values, params.value_width, values_optional);
+
+ encoded_fields_.push_back(EncodeVarInt(delta_params.DeltaHeaderAsInt()));
+
+ if (values_optional) {
+ RTC_CHECK(positions);
+ encoded_fields_.push_back(EncodeOptionalValuePositions(*positions));
+ }
+ // Base element, encoded as uint8, uint32, uint64 or varint
+ encoded_fields_.push_back(EncodeSingleValue(base, params.field_type));
+
+ // If all (existing) values are equal to the base, then we can skip
+ // writing the all-zero deltas, and instead infer those from the delta
+ // header.
+ if (!delta_params.values_equal()) {
+ encoded_fields_.push_back(
+ EncodeDeltasV3(delta_params, base, remaining_values));
+ }
+}
+
+void EventEncoder::EncodeField(const FieldParameters& params,
+ const std::vector<absl::string_view>& values) {
+ RTC_DCHECK_EQ(values.size(), batch_size_);
+
+ if (values.size() == 0) {
+ // If all values for a particular field is empty/nullopt,
+ // then we completely skip the field even if the the batch is non-empty.
+ return;
+ }
+
+ // Write the field tag.
+ RTC_CHECK_NE(params.field_id, FieldParameters::kTimestampField);
+ RTC_DCHECK_LE(params.field_id, std::numeric_limits<uint64_t>::max() >> 3);
+ RTC_DCHECK_EQ(params.field_type, FieldType::kString);
+ uint64_t field_tag = params.field_id << 3;
+ field_tag += static_cast<uint64_t>(params.field_type);
+ encoded_fields_.push_back(EncodeVarInt(field_tag));
+
+ if (values.size() > 1) {
+ // If multiple values in the batch, write the encoding
+ // parameters. (Values >0 reserved for future use.)
+ uint64_t encoding_params = 0;
+ encoded_fields_.push_back(EncodeVarInt(encoding_params));
+ }
+
+ // Write the strings as (length, data) pairs.
+ for (absl::string_view s : values) {
+ encoded_fields_.push_back(EncodeVarInt(s.size()));
+ encoded_fields_.push_back(std::string(s));
+ }
+}
+
+std::string EventEncoder::AsString() {
+ std::string encoded_event;
+
+ if (batch_size_ == 0) {
+ RTC_DCHECK_EQ(encoded_fields_.size(), 0);
+ return encoded_event;
+ }
+
+ // Compute size of encoded fields.
+ size_t total_fields_size = 0;
+ for (const std::string& s : encoded_fields_) {
+ total_fields_size += s.size();
+ }
+
+ constexpr size_t kExpectedMaxEventTagBytes = 4;
+ constexpr size_t kExpectedMaxSizeEncodingBytes = 4;
+ encoded_event.reserve(kExpectedMaxEventTagBytes +
+ kExpectedMaxSizeEncodingBytes + total_fields_size);
+
+ // Encode event tag (event id and whether batch or single event).
+ encoded_event.append(EncodeVarInt(event_tag_));
+
+ // Encode size of the remaining fields.
+ encoded_event.append(EncodeVarInt(total_fields_size));
+
+ // Append encoded fields.
+ for (const std::string& s : encoded_fields_) {
+ encoded_event.append(s);
+ }
+
+ return encoded_event;
+}
+
+} // namespace webrtc