| /* |
| * 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. |
| */ |
| |
| #ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FIELD_EXTRACTION_H_ |
| #define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FIELD_EXTRACTION_H_ |
| |
| #include <string> |
| #include <vector> |
| |
| #include "absl/types/optional.h" |
| #include "api/array_view.h" |
| #include "api/rtc_event_log/rtc_event.h" |
| #include "api/units/timestamp.h" |
| #include "logging/rtc_event_log/encoder/rtc_event_log_encoder_common.h" |
| #include "rtc_base/logging.h" |
| |
| namespace webrtc_event_logging { |
| uint8_t UnsignedBitWidth(uint64_t max_magnitude); |
| uint8_t SignedBitWidth(uint64_t max_pos_magnitude, uint64_t max_neg_magnitude); |
| uint64_t MaxUnsignedValueOfBitWidth(uint64_t bit_width); |
| uint64_t UnsignedDelta(uint64_t previous, uint64_t current, uint64_t bit_mask); |
| } // namespace webrtc_event_logging |
| |
| namespace webrtc { |
| template <typename T, std::enable_if_t<std::is_signed<T>::value, bool> = true> |
| uint64_t EncodeAsUnsigned(T value) { |
| return webrtc_event_logging::ToUnsigned(value); |
| } |
| |
| template <typename T, std::enable_if_t<std::is_unsigned<T>::value, bool> = true> |
| uint64_t EncodeAsUnsigned(T value) { |
| return static_cast<uint64_t>(value); |
| } |
| |
| template <typename T, std::enable_if_t<std::is_signed<T>::value, bool> = true> |
| T DecodeFromUnsignedToType(uint64_t value) { |
| T signed_value = 0; |
| bool success = webrtc_event_logging::ToSigned<T>(value, &signed_value); |
| if (!success) { |
| RTC_LOG(LS_ERROR) << "Failed to convert " << value << "to signed type."; |
| // TODO(terelius): Propagate error? |
| } |
| return signed_value; |
| } |
| |
| template <typename T, std::enable_if_t<std::is_unsigned<T>::value, bool> = true> |
| T DecodeFromUnsignedToType(uint64_t value) { |
| // TODO(terelius): Check range? |
| return static_cast<T>(value); |
| } |
| |
| // Given a batch of RtcEvents and a member pointer, extract that |
| // member from each event in the batch. Signed integer members are |
| // encoded as unsigned, and the bitsize increased so the result can |
| // represented as a std::vector<uin64_t>. |
| // This is intended to be used in conjuction with |
| // EventEncoder::EncodeField to encode a batch of events as follows: |
| // auto values = ExtractRtcEventMember(batch, RtcEventFoo::timestamp_ms); |
| // encoder.EncodeField(timestamp_params, values) |
| template <typename T, |
| typename E, |
| std::enable_if_t<std::is_integral<T>::value, bool> = true> |
| std::vector<uint64_t> ExtractRtcEventMember( |
| rtc::ArrayView<const RtcEvent*> batch, |
| const T E::*member) { |
| std::vector<uint64_t> values; |
| values.reserve(batch.size()); |
| for (const RtcEvent* event : batch) { |
| RTC_CHECK_EQ(event->GetType(), E::kType); |
| T value = static_cast<const E*>(event)->*member; |
| values.push_back(EncodeAsUnsigned(value)); |
| } |
| return values; |
| } |
| |
| // Represents a vector<optional<uint64_t>> optional_values |
| // as a bit-vector `position_mask` which identifies the positions |
| // of existing values, and a (potentially shorter) |
| // `vector<uint64_t> values` containing the actual values. |
| // The bit vector is constructed such that position_mask[i] |
| // is true iff optional_values[i] has a value, and `values.size()` |
| // is equal to the number of set bits in `position_mask`. |
| struct ValuesWithPositions { |
| std::vector<bool> position_mask; |
| std::vector<uint64_t> values; |
| }; |
| |
| // Same as above but for optional fields. It returns a struct |
| // containing a vector of positions in addition to the vector of values. |
| // The vector `positions` has the same length as the batch where |
| // `positions[i] == true` iff the batch[i]->member has a value. |
| // The values vector only contains the values that exists, so it |
| // may be shorter than the batch. |
| template <typename T, |
| typename E, |
| std::enable_if_t<std::is_integral<T>::value, bool> = true> |
| ValuesWithPositions ExtractRtcEventMember(rtc::ArrayView<const RtcEvent*> batch, |
| const absl::optional<T> E::*member) { |
| ValuesWithPositions result; |
| result.position_mask.reserve(batch.size()); |
| result.values.reserve(batch.size()); |
| for (const RtcEvent* event : batch) { |
| RTC_CHECK_EQ(event->GetType(), E::kType); |
| absl::optional<T> field = static_cast<const E*>(event)->*member; |
| result.position_mask.push_back(field.has_value()); |
| if (field.has_value()) { |
| result.values.push_back(EncodeAsUnsigned(field.value())); |
| } |
| } |
| return result; |
| } |
| |
| template <typename E> |
| std::vector<absl::string_view> ExtractRtcEventMember( |
| rtc::ArrayView<const RtcEvent*> batch, |
| const std::string E::*member) { |
| std::vector<absl::string_view> values; |
| values.reserve(batch.size()); |
| for (const RtcEvent* event : batch) { |
| RTC_CHECK_EQ(event->GetType(), E::kType); |
| absl::string_view str = static_cast<const E*>(event)->*member; |
| values.push_back(str); |
| } |
| return values; |
| } |
| |
| // Inverse of the ExtractRtcEventMember function used when parsing |
| // a log. Uses a vector of values to populate a specific field in a |
| // vector of structs. |
| template <typename T, |
| typename E, |
| std::enable_if_t<std::is_integral<T>::value, bool> = true> |
| void PopulateRtcEventMember(const rtc::ArrayView<uint64_t> values, |
| T E::*member, |
| rtc::ArrayView<E> output) { |
| size_t batch_size = values.size(); |
| RTC_CHECK_EQ(output.size(), batch_size); |
| for (size_t i = 0; i < batch_size; ++i) { |
| output[i].*member = DecodeFromUnsignedToType<T>(values[i]); |
| } |
| } |
| |
| // Same as above, but for optional fields. |
| template <typename T, |
| typename E, |
| std::enable_if_t<std::is_integral<T>::value, bool> = true> |
| void PopulateRtcEventMember(const rtc::ArrayView<uint8_t> positions, |
| const rtc::ArrayView<uint64_t> values, |
| absl::optional<T> E::*member, |
| rtc::ArrayView<E> output) { |
| size_t batch_size = positions.size(); |
| RTC_CHECK_EQ(output.size(), batch_size); |
| RTC_CHECK_LE(values.size(), batch_size); |
| auto value_it = values.begin(); |
| for (size_t i = 0; i < batch_size; ++i) { |
| if (positions[i]) { |
| RTC_CHECK(value_it != values.end()); |
| output[i].*member = DecodeFromUnsignedToType<T>(value_it); |
| ++value_it; |
| } else { |
| output[i].*member = absl::nullopt; |
| } |
| } |
| RTC_CHECK(value_it == values.end()); |
| } |
| |
| template <typename E> |
| void PopulateRtcEventMember(const rtc::ArrayView<absl::string_view> values, |
| std::string E::*member, |
| rtc::ArrayView<E> output) { |
| size_t batch_size = values.size(); |
| RTC_CHECK_EQ(output.size(), batch_size); |
| for (size_t i = 0; i < batch_size; ++i) { |
| output[i].*member = values[i]; |
| } |
| } |
| |
| template <typename E> |
| void PopulateRtcEventTimestamp(const rtc::ArrayView<uint64_t>& values, |
| Timestamp E::*timestamp, |
| rtc::ArrayView<E> output) { |
| size_t batch_size = values.size(); |
| RTC_CHECK_EQ(batch_size, output.size()); |
| for (size_t i = 0; i < batch_size; ++i) { |
| output[i].*timestamp = |
| Timestamp::Millis(DecodeFromUnsignedToType<int64_t>(values[i])); |
| } |
| } |
| |
| template <typename E> |
| rtc::ArrayView<E> ExtendLoggedBatch(std::vector<E>& output, |
| size_t new_elements) { |
| size_t old_size = output.size(); |
| output.insert(output.end(), old_size + new_elements, E()); |
| rtc::ArrayView<E> output_batch = output; |
| output_batch.subview(old_size); |
| RTC_DCHECK_EQ(output_batch.size(), new_elements); |
| return output_batch; |
| } |
| |
| } // namespace webrtc |
| |
| #endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FIELD_EXTRACTION_H_ |