blob: a9b0c083078d51f6e952ff09d92f20be56bc1473 [file] [log] [blame]
/*
* 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_parser.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "logging/rtc_event_log/encoder/var_int.h"
#include "logging/rtc_event_log/events/rtc_event_field_encoding.h"
#include "rtc_base/bitstream_reader.h"
#include "rtc_base/checks.h"
namespace {
absl::optional<webrtc::FieldType> ConvertFieldType(uint64_t value) {
switch (value) {
case static_cast<uint64_t>(webrtc::FieldType::kFixed8):
return webrtc::FieldType::kFixed8;
case static_cast<uint64_t>(webrtc::FieldType::kFixed32):
return webrtc::FieldType::kFixed32;
case static_cast<uint64_t>(webrtc::FieldType::kFixed64):
return webrtc::FieldType::kFixed64;
case static_cast<uint64_t>(webrtc::FieldType::kVarInt):
return webrtc::FieldType::kVarInt;
case static_cast<uint64_t>(webrtc::FieldType::kString):
return webrtc::FieldType::kString;
default:
return absl::nullopt;
}
}
} // namespace
namespace webrtc {
uint64_t EventParser::ReadLittleEndian(uint8_t bytes) {
RTC_DCHECK_LE(bytes, sizeof(uint64_t));
RTC_DCHECK_GE(bytes, 1);
uint64_t value = 0;
if (bytes > pending_data_.length()) {
SetError();
return value;
}
const uint8_t* p = reinterpret_cast<const uint8_t*>(pending_data_.data());
unsigned int shift = 0;
uint8_t remaining = bytes;
while (remaining > 0) {
value += (static_cast<uint64_t>(*p) << shift);
shift += 8;
++p;
--remaining;
}
pending_data_ = pending_data_.substr(bytes);
return value;
}
uint64_t EventParser::ReadVarInt() {
uint64_t output = 0;
bool success;
std::tie(success, pending_data_) = DecodeVarInt(pending_data_, &output);
if (!success) {
SetError();
}
return output;
}
uint64_t EventParser::ReadOptionalValuePositions() {
RTC_DCHECK(positions_.empty());
size_t bits_to_read = NumEventsInBatch();
positions_.reserve(bits_to_read);
if (pending_data_.size() * 8 < bits_to_read) {
SetError();
return 0;
}
BitstreamReader reader(pending_data_);
for (size_t i = 0; i < bits_to_read; i++) {
positions_.push_back(reader.ReadBit());
}
if (!reader.Ok()) {
SetError();
return 0;
}
size_t num_existing_values =
std::count(positions_.begin(), positions_.end(), 1);
pending_data_ = pending_data_.substr((bits_to_read + 7) / 8);
return num_existing_values;
}
uint64_t EventParser::ReadSingleValue(FieldType field_type) {
switch (field_type) {
case FieldType::kFixed8:
return ReadLittleEndian(/*bytes=*/1);
case FieldType::kFixed32:
return ReadLittleEndian(/*bytes=*/4);
case FieldType::kFixed64:
return ReadLittleEndian(/*bytes=*/8);
case FieldType::kVarInt:
return ReadVarInt();
case FieldType::kString:
RTC_DCHECK_NOTREACHED();
SetError();
return 0;
}
RTC_DCHECK_NOTREACHED();
SetError();
return 0;
}
void EventParser::ReadDeltasAndPopulateValues(
FixedLengthEncodingParametersV3 params,
uint64_t num_deltas,
uint64_t base) {
RTC_DCHECK(values_.empty());
values_.reserve(num_deltas + 1);
values_.push_back(base);
if (pending_data_.size() * 8 < num_deltas * params.delta_bit_width()) {
SetError();
return;
}
BitstreamReader reader(pending_data_);
const uint64_t top_bit = static_cast<uint64_t>(1)
<< (params.delta_bit_width() - 1);
uint64_t value = base;
for (uint64_t i = 0; i < num_deltas; ++i) {
uint64_t delta = reader.ReadBits(params.delta_bit_width());
RTC_DCHECK_LE(value, webrtc_event_logging::MaxUnsignedValueOfBitWidth(
params.value_bit_width()));
RTC_DCHECK_LE(delta, webrtc_event_logging::MaxUnsignedValueOfBitWidth(
params.delta_bit_width()));
bool negative_delta = params.signed_deltas() && ((delta & top_bit) != 0);
if (negative_delta) {
uint64_t delta_abs = (~delta & params.delta_mask()) + 1;
value = (value - delta_abs) & params.value_mask();
} else {
value = (value + delta) & params.value_mask();
}
values_.push_back(value);
}
if (!reader.Ok()) {
SetError();
return;
}
pending_data_ =
pending_data_.substr((num_deltas * params.delta_bit_width() + 7) / 8);
}
RtcEventLogParseStatus EventParser::Initialize(absl::string_view s,
bool batched) {
pending_data_ = s;
num_events_ = 1;
if (batched) {
num_events_ = ReadVarInt();
if (!Ok()) {
return RtcEventLogParseStatus::Error(
"Failed to read number of events in batch.", __FILE__, __LINE__);
}
}
return RtcEventLogParseStatus::Success();
}
RtcEventLogParseStatus EventParser::ParseNumericFieldInternal(
uint64_t value_bit_width,
FieldType field_type) {
RTC_DCHECK(values_.empty());
RTC_DCHECK(positions_.empty());
if (num_events_ == 1) {
// Just a single value in the batch.
uint64_t base = ReadSingleValue(field_type);
if (!Ok()) {
return RtcEventLogParseStatus::Error("Failed to read value", __FILE__,
__LINE__);
}
positions_.push_back(true);
values_.push_back(base);
} else {
// Delta compressed batch.
// Read delta header.
uint64_t header_value = ReadVarInt();
if (!Ok())
return RtcEventLogParseStatus::Error("Failed to read delta header",
__FILE__, __LINE__);
// NB: value_bit_width may be incorrect for the field, if this isn't the
// field we are looking for.
absl::optional<FixedLengthEncodingParametersV3> delta_header =
FixedLengthEncodingParametersV3::ParseDeltaHeader(header_value,
value_bit_width);
if (!delta_header.has_value()) {
return RtcEventLogParseStatus::Error("Failed to parse delta header",
__FILE__, __LINE__);
}
uint64_t num_existing_deltas = NumEventsInBatch() - 1;
if (delta_header->values_optional()) {
size_t num_nonempty_values = ReadOptionalValuePositions();
if (!Ok()) {
return RtcEventLogParseStatus::Error(
"Failed to read positions of optional values", __FILE__, __LINE__);
}
if (num_nonempty_values < 1 || NumEventsInBatch() < num_nonempty_values) {
return RtcEventLogParseStatus::Error(
"Expected at least one non_empty value", __FILE__, __LINE__);
}
num_existing_deltas = num_nonempty_values - 1;
} else {
// All elements in the batch have values.
positions_.assign(NumEventsInBatch(), 1u);
}
// Read base.
uint64_t base = ReadSingleValue(field_type);
if (!Ok()) {
return RtcEventLogParseStatus::Error("Failed to read value", __FILE__,
__LINE__);
}
if (delta_header->values_equal()) {
// Duplicate the base value num_existing_deltas times.
values_.assign(num_existing_deltas + 1, base);
} else {
// Read deltas; ceil(num_existing_deltas*delta_width/8) bits
ReadDeltasAndPopulateValues(delta_header.value(), num_existing_deltas,
base);
if (!Ok()) {
return RtcEventLogParseStatus::Error("Failed to decode deltas",
__FILE__, __LINE__);
}
}
}
return RtcEventLogParseStatus::Success();
}
RtcEventLogParseStatus EventParser::ParseStringFieldInternal() {
RTC_DCHECK(strings_.empty());
if (num_events_ > 1) {
// String encoding params reserved for future use.
uint64_t encoding_params = ReadVarInt();
if (!Ok()) {
return RtcEventLogParseStatus::Error("Failed to read string encoding",
__FILE__, __LINE__);
}
if (encoding_params != 0) {
return RtcEventLogParseStatus::Error(
"Unrecognized string encoding parameters", __FILE__, __LINE__);
}
}
strings_.reserve(num_events_);
for (uint64_t i = 0; i < num_events_; ++i) {
// Just a single value in the batch.
uint64_t size = ReadVarInt();
if (!Ok()) {
return RtcEventLogParseStatus::Error("Failed to read string size",
__FILE__, __LINE__);
}
if (size > pending_data_.size()) {
return RtcEventLogParseStatus::Error("String size exceeds remaining data",
__FILE__, __LINE__);
}
strings_.push_back(pending_data_.substr(0, size));
pending_data_ = pending_data_.substr(size);
}
return RtcEventLogParseStatus::Success();
}
RtcEventLogParseStatus EventParser::ParseField(const FieldParameters& params) {
// Verify that the event parses fields in increasing order.
if (params.field_id == FieldParameters::kTimestampField) {
RTC_DCHECK_EQ(last_field_id_, FieldParameters::kTimestampField);
} else {
RTC_DCHECK_GT(params.field_id, last_field_id_);
}
last_field_id_ = params.field_id;
// Initialization for positional fields that don't encode field ID and type.
uint64_t field_id = params.field_id;
FieldType field_type = params.field_type;
// Fields are encoded in increasing field_id order.
// Skip unknown fields with field_id < params.field_id until we either
// find params.field_id or a field with higher id, in which case we know that
// params.field_id doesn't exist.
while (!pending_data_.empty()) {
absl::string_view field_start = pending_data_;
ClearTemporaries();
// Read tag for non-positional fields.
if (params.field_id != FieldParameters::kTimestampField) {
uint64_t field_tag = ReadVarInt();
if (!Ok())
return RtcEventLogParseStatus::Error("Failed to read field tag",
__FILE__, __LINE__);
// Split tag into field ID and field type.
field_id = field_tag >> 3;
absl::optional<FieldType> conversion = ConvertFieldType(field_tag & 7u);
if (!conversion.has_value())
return RtcEventLogParseStatus::Error("Failed to parse field type",
__FILE__, __LINE__);
field_type = conversion.value();
}
if (field_id > params.field_id) {
// We've passed all fields with ids less than or equal to what we are
// looking for. Reset pending_data_ to first field with id higher than
// params.field_id, since we didn't find the field we were looking for.
pending_data_ = field_start;
return RtcEventLogParseStatus::Success();
}
if (field_type == FieldType::kString) {
auto status = ParseStringFieldInternal();
if (!status.ok()) {
return status;
}
} else {
auto status = ParseNumericFieldInternal(params.value_width, field_type);
if (!status.ok()) {
return status;
}
}
if (field_id == params.field_id) {
// The field we're looking for has been found and values populated.
return RtcEventLogParseStatus::Success();
}
}
// Field not found because the event ended.
ClearTemporaries();
return RtcEventLogParseStatus::Success();
}
RtcEventLogParseStatusOr<rtc::ArrayView<absl::string_view>>
EventParser::ParseStringField(const FieldParameters& params,
bool required_field) {
using StatusOr = RtcEventLogParseStatusOr<rtc::ArrayView<absl::string_view>>;
RTC_DCHECK_EQ(params.field_type, FieldType::kString);
auto status = ParseField(params);
if (!status.ok())
return StatusOr(status);
rtc::ArrayView<absl::string_view> strings = GetStrings();
if (required_field && strings.size() != NumEventsInBatch()) {
return StatusOr::Error("Required string field not found", __FILE__,
__LINE__);
}
return StatusOr(strings);
}
RtcEventLogParseStatusOr<rtc::ArrayView<uint64_t>>
EventParser::ParseNumericField(const FieldParameters& params,
bool required_field) {
using StatusOr = RtcEventLogParseStatusOr<rtc::ArrayView<uint64_t>>;
RTC_DCHECK_NE(params.field_type, FieldType::kString);
auto status = ParseField(params);
if (!status.ok())
return StatusOr(status);
rtc::ArrayView<uint64_t> values = GetValues();
if (required_field && values.size() != NumEventsInBatch()) {
return StatusOr::Error("Required numerical field not found", __FILE__,
__LINE__);
}
return StatusOr(values);
}
RtcEventLogParseStatusOr<EventParser::ValueAndPostionView>
EventParser::ParseOptionalNumericField(const FieldParameters& params,
bool required_field) {
using StatusOr = RtcEventLogParseStatusOr<ValueAndPostionView>;
RTC_DCHECK_NE(params.field_type, FieldType::kString);
auto status = ParseField(params);
if (!status.ok())
return StatusOr(status);
ValueAndPostionView view{GetValues(), GetPositions()};
if (required_field && view.positions.size() != NumEventsInBatch()) {
return StatusOr::Error("Required numerical field not found", __FILE__,
__LINE__);
}
return StatusOr(view);
}
} // namespace webrtc