| |
| /* |
| * 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 "logging/rtc_event_log/events/rtc_event_log_parse_status.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 |