Allow encoding string fields in new event log format.

Return parse results as a StatusOr containing views to values owned by the parser.

Bug: webrtc:11933
Change-Id: Icf26b9cb651d1e9244c764c3ec1fdb66abfc9e08
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/233740
Reviewed-by: Sebastian Jansson <srte@webrtc.org>
Commit-Queue: Björn Terelius <terelius@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#35129}
diff --git a/logging/rtc_event_log/events/rtc_event_field_encoding.cc b/logging/rtc_event_log/events/rtc_event_field_encoding.cc
index 7598587..db407ff 100644
--- a/logging/rtc_event_log/events/rtc_event_field_encoding.cc
+++ b/logging/rtc_event_log/events/rtc_event_field_encoding.cc
@@ -229,6 +229,38 @@
   }
 }
 
+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;
 
diff --git a/logging/rtc_event_log/events/rtc_event_field_encoding.h b/logging/rtc_event_log/events/rtc_event_field_encoding.h
index d70644e..8376a8b 100644
--- a/logging/rtc_event_log/events/rtc_event_field_encoding.h
+++ b/logging/rtc_event_log/events/rtc_event_field_encoding.h
@@ -77,6 +77,9 @@
   void EncodeField(const FieldParameters& params,
                    const ValuesWithPositions& values);
 
+  void EncodeField(const FieldParameters& params,
+                   const std::vector<absl::string_view>& values);
+
   std::string AsString();
 
  private:
diff --git a/logging/rtc_event_log/events/rtc_event_field_encoding_parser.cc b/logging/rtc_event_log/events/rtc_event_field_encoding_parser.cc
index 0eb88dd..ea7ea6e 100644
--- a/logging/rtc_event_log/events/rtc_event_field_encoding_parser.cc
+++ b/logging/rtc_event_log/events/rtc_event_field_encoding_parser.cc
@@ -74,13 +74,10 @@
   return output;
 }
 
-uint64_t EventParser::ReadOptionalValuePositions(std::vector<bool>* positions) {
-  if (!positions) {
-    return CountAndIgnoreOptionalValuePositions();
-  }
-
+uint64_t EventParser::ReadOptionalValuePositions() {
+  RTC_DCHECK(positions_.empty());
   size_t bits_to_read = NumEventsInBatch();
-  RTC_DCHECK(positions->empty());
+  positions_.reserve(bits_to_read);
   if (pending_data_.size() * 8 < bits_to_read) {
     SetError();
     return 0;
@@ -88,7 +85,7 @@
 
   BitstreamReader reader(pending_data_);
   for (size_t i = 0; i < bits_to_read; i++) {
-    positions->push_back(reader.ReadBit());
+    positions_.push_back(reader.ReadBit());
   }
   if (!reader.Ok()) {
     SetError();
@@ -96,30 +93,7 @@
   }
 
   size_t num_existing_values =
-      std::count(positions->begin(), positions->end(), true);
-  pending_data_ = pending_data_.substr((bits_to_read + 7) / 8);
-  return num_existing_values;
-}
-
-uint64_t EventParser::CountAndIgnoreOptionalValuePositions() {
-  size_t bits_to_read = NumEventsInBatch();
-  if (pending_data_.size() * 8 < bits_to_read) {
-    SetError();
-    return 0;
-  }
-
-  BitstreamReader reader(pending_data_);
-  size_t num_existing_values = 0;
-  for (size_t i = 0; i < bits_to_read; i++) {
-    if (reader.ReadBit()) {
-      ++num_existing_values;
-    }
-  }
-  if (!reader.Ok()) {
-    SetError();
-    return 0;
-  }
-
+      std::count(positions_.begin(), positions_.end(), 1);
   pending_data_ = pending_data_.substr((bits_to_read + 7) / 8);
   return num_existing_values;
 }
@@ -144,12 +118,10 @@
 void EventParser::ReadDeltasAndPopulateValues(
     FixedLengthEncodingParametersV3 params,
     uint64_t num_deltas,
-    uint64_t base,
-    std::vector<uint64_t>* values) {
-  RTC_CHECK(values != nullptr);
-  RTC_DCHECK(values->empty());
-  values->reserve(num_deltas + 1);
-  values->push_back(base);
+    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();
@@ -174,7 +146,7 @@
     } else {
       value = (value + delta) & params.value_mask();
     }
-    values->push_back(value);
+    values_.push_back(value);
   }
 
   if (!reader.Ok()) {
@@ -201,11 +173,111 @@
   return RtcEventLogParseStatus::Success();
 }
 
-RtcEventLogParseStatus EventParser::ParseField(const FieldParameters& params,
-                                               std::vector<uint64_t>* values,
-                                               std::vector<bool>* positions) {
-  RTC_CHECK(values != nullptr);
+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);
@@ -224,10 +296,7 @@
   // params.field_id doesn't exist.
   while (!pending_data_.empty()) {
     absl::string_view field_start = pending_data_;
-    values->clear();
-    if (positions) {
-      positions->clear();
-    }
+    ClearTemporaries();
 
     // Read tag for non-positional fields.
     if (params.field_id != FieldParameters::kTimestampField) {
@@ -252,72 +321,15 @@
       return RtcEventLogParseStatus::Success();
     }
 
-    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__);
-      if (positions) {
-        positions->push_back(true);
+    if (field_type == FieldType::kString) {
+      auto status = ParseStringFieldInternal();
+      if (!status.ok()) {
+        return status;
       }
-      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_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,
-                                                            params.value_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(positions);
-        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.
-        if (positions) {
-          positions->assign(NumEventsInBatch(), true);
-        }
-      }
-
-      // 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, values);
-        if (!Ok()) {
-          return RtcEventLogParseStatus::Error("Failed to decode deltas",
-                                               __FILE__, __LINE__);
-        }
+      auto status = ParseNumericFieldInternal(params.value_width, field_type);
+      if (!status.ok()) {
+        return status;
       }
     }
 
@@ -328,11 +340,56 @@
   }
 
   // Field not found because the event ended.
-  values->clear();
-  if (positions) {
-    positions->clear();
-  }
+  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
diff --git a/logging/rtc_event_log/events/rtc_event_field_encoding_parser.h b/logging/rtc_event_log/events/rtc_event_field_encoding_parser.h
index a1cf2b4..f1af5db 100644
--- a/logging/rtc_event_log/events/rtc_event_field_encoding_parser.h
+++ b/logging/rtc_event_log/events/rtc_event_field_encoding_parser.h
@@ -24,6 +24,9 @@
 // If/when we start using absl::Status in WebRTC, consider
 // whether payloads would be an appropriate alternative.
 class RtcEventLogParseStatus {
+  template <typename T>
+  friend class RtcEventLogParseStatusOr;
+
  public:
   static RtcEventLogParseStatus Success() { return RtcEventLogParseStatus(); }
   static RtcEventLogParseStatus Error(std::string error,
@@ -33,6 +36,7 @@
   }
 
   bool ok() const { return error_.empty(); }
+
   std::string message() const { return error_; }
 
  private:
@@ -43,10 +47,52 @@
   std::string error_;
 };
 
+template <typename T>
+class RtcEventLogParseStatusOr {
+ public:
+  explicit RtcEventLogParseStatusOr(RtcEventLogParseStatus status)
+      : status_(status), value_() {}
+  explicit RtcEventLogParseStatusOr(const T& value)
+      : status_(), value_(value) {}
+
+  bool ok() const { return status_.ok(); }
+
+  std::string message() const { return status_.message(); }
+
+  const T& value() const {
+    RTC_DCHECK(ok());
+    return value_;
+  }
+
+  T& value() {
+    RTC_DCHECK(ok());
+    return value_;
+  }
+
+  static RtcEventLogParseStatusOr Error(std::string error,
+                                        std::string file,
+                                        int line) {
+    return RtcEventLogParseStatusOr(error, file, line);
+  }
+
+ private:
+  RtcEventLogParseStatusOr() : status_() {}
+  RtcEventLogParseStatusOr(std::string error, std::string file, int line)
+      : status_(error, file, line), value_() {}
+
+  RtcEventLogParseStatus status_;
+  T value_;
+};
+
 namespace webrtc {
 
 class EventParser {
  public:
+  struct ValueAndPostionView {
+    rtc::ArrayView<uint64_t> values;
+    rtc::ArrayView<uint8_t> positions;
+  };
+
   EventParser() = default;
 
   // N.B: This method stores a abls::string_view into the string to be
@@ -55,17 +101,18 @@
   RtcEventLogParseStatus Initialize(absl::string_view s, bool batched);
 
   // Attempts to parse the field specified by `params`, skipping past
-  // other fields that may occur before it. Returns
-  // RtcEventLogParseStatus::Success() and populates `values` (and `positions`)
-  // if the field is found. Returns RtcEventLogParseStatus::Success() and clears
-  // `values` (and `positions`) if the field doesn't exist. Returns a
-  // RtcEventLogParseStatus::Error if the log is incomplete, malformed or
-  // otherwise can't be parsed. `values` and `positions` are pure out-parameters
-  // that allow the caller to reuse the same temporary storage for all fields.
-  // Any previous content in the out parameters is cleared.
-  RtcEventLogParseStatus ParseField(const FieldParameters& params,
-                                    std::vector<uint64_t>* values,
-                                    std::vector<bool>* positions = nullptr);
+  // other fields that may occur before it. If 'required_field == true',
+  // then failing to find the field is an error, otherwise the functions
+  // return success, but with an empty view of values.
+  RtcEventLogParseStatusOr<rtc::ArrayView<absl::string_view>> ParseStringField(
+      const FieldParameters& params,
+      bool required_field = true);
+  RtcEventLogParseStatusOr<rtc::ArrayView<uint64_t>> ParseNumericField(
+      const FieldParameters& params,
+      bool required_field = true);
+  RtcEventLogParseStatusOr<ValueAndPostionView> ParseOptionalNumericField(
+      const FieldParameters& params,
+      bool required_field = true);
 
   // Number of events in a batch.
   uint64_t NumEventsInBatch() const { return num_events_; }
@@ -79,23 +126,47 @@
   uint64_t ReadLittleEndian(uint8_t bytes);
   uint64_t ReadVarInt();
   uint64_t ReadSingleValue(FieldType field_type);
-  uint64_t ReadOptionalValuePositions(std::vector<bool>* positions);
-  uint64_t CountAndIgnoreOptionalValuePositions();
+  uint64_t ReadOptionalValuePositions();
   void ReadDeltasAndPopulateValues(FixedLengthEncodingParametersV3 params,
                                    uint64_t num_deltas,
-                                   const uint64_t base,
-                                   std::vector<uint64_t>* values);
+                                   const uint64_t base);
+  RtcEventLogParseStatus ParseNumericFieldInternal(uint64_t value_bit_width,
+                                                   FieldType field_type);
+  RtcEventLogParseStatus ParseStringFieldInternal();
+
+  // Attempts to parse the field specified by `params`, skipping past
+  // other fields that may occur before it. Returns
+  // RtcEventLogParseStatus::Success() and populates `values_` (and
+  // `positions_`) if the field is found. Returns
+  // RtcEventLogParseStatus::Success() and clears `values_` (and `positions_`)
+  // if the field doesn't exist. Returns a RtcEventLogParseStatus::Error() if
+  // the log is incomplete, malformed or otherwise can't be parsed.
+  RtcEventLogParseStatus ParseField(const FieldParameters& params);
 
   void SetError() { error_ = true; }
   bool Ok() const { return !error_; }
 
-  // String to be consumed.
-  absl::string_view pending_data_;
+  rtc::ArrayView<uint64_t> GetValues() { return values_; }
+  rtc::ArrayView<uint8_t> GetPositions() { return positions_; }
+  rtc::ArrayView<absl::string_view> GetStrings() { return strings_; }
+
+  void ClearTemporaries() {
+    positions_.clear();
+    values_.clear();
+    strings_.clear();
+  }
 
   // Tracks whether an error has occurred in one of the helper
   // functions above.
   bool error_ = false;
 
+  // Temporary storage for result.
+  std::vector<uint8_t> positions_;
+  std::vector<uint64_t> values_;
+  std::vector<absl::string_view> strings_;
+
+  // String to be consumed.
+  absl::string_view pending_data_;
   uint64_t num_events_ = 1;
   uint64_t last_field_id_ = FieldParameters::kTimestampField;
 };
diff --git a/logging/rtc_event_log/events/rtc_event_field_encoding_unittest.cc b/logging/rtc_event_log/events/rtc_event_field_encoding_unittest.cc
index a993e7b..3ff0114 100644
--- a/logging/rtc_event_log/events/rtc_event_field_encoding_unittest.cc
+++ b/logging/rtc_event_log/events/rtc_event_field_encoding_unittest.cc
@@ -116,6 +116,26 @@
   return tag_size + base_size + delta_header_size + positions_size + delta_size;
 }
 
+size_t ExpectedStringEncodingSize(const FieldParameters& params,
+                                  const std::vector<std::string>& values) {
+  EXPECT_EQ(params.field_type, FieldType::kString);
+  uint64_t numeric_field_type = static_cast<uint64_t>(params.field_type);
+  RTC_DCHECK_LT(numeric_field_type, 1u << 3);
+  size_t tag_size =
+      ExpectedVarIntSize((params.field_id << 3) + numeric_field_type);
+
+  size_t expected_size = tag_size;
+  if (values.size() > 1) {
+    // VarInt encoding header reserved for future use. Currently always 0.
+    expected_size += 1;
+  }
+  for (const auto& s : values) {
+    expected_size += ExpectedVarIntSize(s.size());
+    expected_size += s.size();
+  }
+  return expected_size;
+}
+
 }  // namespace
 
 class RtcTestEvent final : public RtcEvent {
@@ -137,7 +157,8 @@
                uint64_t unsigned64,
                absl::optional<int32_t> optional_signed32,
                absl::optional<int64_t> optional_signed64,
-               uint32_t wrapping21)
+               uint32_t wrapping21,
+               std::string string)
       : b_(b),
         signed32_(signed32),
         unsigned32_(unsigned32),
@@ -145,7 +166,8 @@
         unsigned64_(unsigned64),
         optional_signed32_(optional_signed32),
         optional_signed64_(optional_signed64),
-        wrapping21_(wrapping21) {}
+        wrapping21_(wrapping21),
+        string_(string) {}
   ~RtcTestEvent() override = default;
 
   Type GetType() const override { return static_cast<Type>(4711); }
@@ -170,6 +192,8 @@
                                                      FieldType::kVarInt, 64};
   static constexpr FieldParameters wrapping21_params{"wrapping21", 9,
                                                      FieldType::kFixed32, 21};
+  static constexpr FieldParameters string_params{
+      "string", 10, FieldType::kString, /*value_width = */ 0};
 
   static constexpr Type kType = static_cast<RtcEvent::Type>(4711);
 
@@ -181,6 +205,7 @@
   const absl::optional<int32_t> optional_signed32_ = absl::nullopt;
   const absl::optional<int64_t> optional_signed64_ = absl::nullopt;
   const uint32_t wrapping21_ = 0;
+  const std::string string_;
 };
 
 constexpr EventParameters RtcTestEvent::event_params;
@@ -194,6 +219,7 @@
 constexpr FieldParameters RtcTestEvent::optional32_params;
 constexpr FieldParameters RtcTestEvent::optional64_params;
 constexpr FieldParameters RtcTestEvent::wrapping21_params;
+constexpr FieldParameters RtcTestEvent::string_params;
 
 constexpr RtcEvent::Type RtcTestEvent::kType;
 
@@ -209,7 +235,8 @@
       const std::vector<uint64_t>& unsigned64_values,
       const std::vector<absl::optional<int32_t>>& optional32_values,
       const std::vector<absl::optional<int64_t>>& optional64_values,
-      const std::vector<uint32_t>& wrapping21_values) {
+      const std::vector<uint32_t>& wrapping21_values,
+      const std::vector<std::string>& string_values) {
     size_t size = bool_values.size();
     RTC_CHECK_EQ(signed32_values.size(), size);
     RTC_CHECK_EQ(unsigned32_values.size(), size);
@@ -218,12 +245,13 @@
     RTC_CHECK_EQ(optional32_values.size(), size);
     RTC_CHECK_EQ(optional64_values.size(), size);
     RTC_CHECK_EQ(wrapping21_values.size(), size);
+    RTC_CHECK_EQ(string_values.size(), size);
 
     for (size_t i = 0; i < size; i++) {
       batch_.push_back(new RtcTestEvent(
           bool_values[i], signed32_values[i], unsigned32_values[i],
           signed64_values[i], unsigned64_values[i], optional32_values[i],
-          optional64_values[i], wrapping21_values[i]));
+          optional64_values[i], wrapping21_values[i], string_values[i]));
     }
   }
 
@@ -252,15 +280,33 @@
   }
 
   void ParseAndVerifyTimestamps() {
-    std::vector<uint64_t> values;
-    auto status = parser_.ParseField(RtcTestEvent::timestamp_params, &values);
-    ASSERT_TRUE(status.ok()) << status.message().c_str();
-    ASSERT_EQ(values.size(), batch_.size());
+    auto result = parser_.ParseNumericField(RtcTestEvent::timestamp_params);
+    ASSERT_TRUE(result.ok()) << result.message().c_str();
+    ASSERT_EQ(result.value().size(), batch_.size());
     for (size_t i = 0; i < batch_.size(); i++) {
-      EXPECT_EQ(values[i], static_cast<uint64_t>(batch_[i]->timestamp_ms()));
+      EXPECT_EQ(result.value()[i],
+                static_cast<uint64_t>(batch_[i]->timestamp_ms()));
     }
   }
 
+  void ParseAndVerifyStringField(
+      const FieldParameters& params,
+      const std::vector<std::string>& expected_values,
+      size_t expected_skipped_bytes = 0) {
+    size_t expected_size = ExpectedStringEncodingSize(params, expected_values) +
+                           expected_skipped_bytes;
+    size_t size_before = parser_.RemainingBytes();
+    auto result = parser_.ParseStringField(params);
+    ASSERT_TRUE(result.ok()) << result.message().c_str();
+    ASSERT_EQ(result.value().size(), expected_values.size());
+    for (size_t i = 0; i < expected_values.size(); i++) {
+      EXPECT_EQ(result.value()[i], expected_values[i]);
+    }
+    size_t size_after = parser_.RemainingBytes();
+    EXPECT_EQ(size_before - size_after, expected_size)
+        << " for field " << params.name;
+  }
+
   template <typename T>
   void ParseAndVerifyField(const FieldParameters& params,
                            const std::vector<T>& expected_values,
@@ -269,13 +315,13 @@
     size_t expected_size =
         ExpectedEncodingSize(params, expected_values, expected_bits_per_delta) +
         expected_skipped_bytes;
-    std::vector<uint64_t> values;
     size_t size_before = parser_.RemainingBytes();
-    auto status = parser_.ParseField(params, &values);
-    ASSERT_TRUE(status.ok()) << status.message().c_str();
-    ASSERT_EQ(values.size(), expected_values.size());
+    auto result = parser_.ParseNumericField(params);
+    ASSERT_TRUE(result.ok()) << result.message().c_str();
+    ASSERT_EQ(result.value().size(), expected_values.size());
     for (size_t i = 0; i < expected_values.size(); i++) {
-      EXPECT_EQ(DecodeFromUnsignedToType<T>(values[i]), expected_values[i]);
+      EXPECT_EQ(DecodeFromUnsignedToType<T>(result.value()[i]),
+                expected_values[i]);
     }
     size_t size_after = parser_.RemainingBytes();
     EXPECT_EQ(size_before - size_after, expected_size)
@@ -291,15 +337,13 @@
     size_t expected_size =
         ExpectedEncodingSize(params, expected_values, expected_bits_per_delta) +
         expected_skipped_bytes;
-    std::vector<bool> positions;
-    positions.reserve(expected_values.size());
-    std::vector<uint64_t> values;
-    values.reserve(expected_values.size());
     size_t size_before = parser_.RemainingBytes();
-    auto status = parser_.ParseField(params, &values, &positions);
-    ASSERT_TRUE(status.ok()) << status.message().c_str();
-    auto value_it = values.begin();
+    auto result = parser_.ParseOptionalNumericField(params);
+    ASSERT_TRUE(result.ok()) << result.message().c_str();
+    rtc::ArrayView<uint64_t> values = result.value().values;
+    rtc::ArrayView<uint8_t> positions = result.value().positions;
     ASSERT_EQ(positions.size(), expected_values.size());
+    auto value_it = values.begin();
     for (size_t i = 0; i < expected_values.size(); i++) {
       if (positions[i]) {
         ASSERT_NE(value_it, values.end());
@@ -317,17 +361,17 @@
   }
 
   void ParseAndVerifyMissingField(const FieldParameters& params) {
-    std::vector<uint64_t> values{4711};
-    auto status = parser_.ParseField(params, &values);
-    ASSERT_TRUE(status.ok()) << status.message().c_str();
-    EXPECT_EQ(values.size(), 0u);
+    auto result = parser_.ParseNumericField(params, /*required_field=*/false);
+    ASSERT_TRUE(result.ok()) << result.message().c_str();
+    EXPECT_EQ(result.value().size(), 0u);
   }
 
   void ParseAndVerifyMissingOptionalField(const FieldParameters& params) {
-    std::vector<bool> positions{true, false};
-    std::vector<uint64_t> values{4711};
-    auto status = parser_.ParseField(params, &values, &positions);
-    ASSERT_TRUE(status.ok()) << status.message().c_str();
+    auto result =
+        parser_.ParseOptionalNumericField(params, /*required_field=*/false);
+    ASSERT_TRUE(result.ok()) << result.message().c_str();
+    rtc::ArrayView<uint64_t> values = result.value().values;
+    rtc::ArrayView<uint8_t> positions = result.value().positions;
     EXPECT_EQ(positions.size(), 0u);
     EXPECT_EQ(values.size(), 0u);
   }
@@ -359,10 +403,11 @@
   std::vector<absl::optional<int32_t>> optional32_values = {kInt32Min};
   std::vector<absl::optional<int64_t>> optional64_values = {kInt64Max};
   std::vector<uint32_t> wrapping21_values = {(1 << 21) - 1};
+  std::vector<std::string> string_values = {"foo"};
 
   CreateFullEvents(bool_values, signed32_values, unsigned32_values,
                    signed64_values, unsigned64_values, optional32_values,
-                   optional64_values, wrapping21_values);
+                   optional64_values, wrapping21_values, string_values);
 
   EventEncoder encoder(RtcTestEvent::event_params, batch_);
   encoder.EncodeField(RtcTestEvent::bool_params,
@@ -386,6 +431,8 @@
   encoder.EncodeField(
       RtcTestEvent::wrapping21_params,
       ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_));
+  encoder.EncodeField(RtcTestEvent::string_params,
+                      ExtractRtcEventMember(batch_, &RtcTestEvent::string_));
   std::string s = encoder.AsString();
 
   // Optional debug printing
@@ -409,6 +456,8 @@
                               optional64_values, /*no deltas*/ 0);
   ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values,
                       /*no deltas*/ 0);
+  ParseAndVerifyStringField(RtcTestEvent::string_params, string_values);
+  EXPECT_EQ(parser_.RemainingBytes(), 0u);
 }
 
 TEST_F(RtcEventFieldTest, EqualElements) {
@@ -426,10 +475,11 @@
       kInt64Max, kInt64Max, kInt64Max, kInt64Max};
   std::vector<uint32_t> wrapping21_values = {(1 << 21) - 1, (1 << 21) - 1,
                                              (1 << 21) - 1, (1 << 21) - 1};
+  std::vector<std::string> string_values = {"foo", "foo", "foo", "foo"};
 
   CreateFullEvents(bool_values, signed32_values, unsigned32_values,
                    signed64_values, unsigned64_values, optional32_values,
-                   optional64_values, wrapping21_values);
+                   optional64_values, wrapping21_values, string_values);
   EventEncoder encoder(RtcTestEvent::event_params, batch_);
   encoder.EncodeField(RtcTestEvent::bool_params,
                       ExtractRtcEventMember(batch_, &RtcTestEvent::b_));
@@ -452,6 +502,8 @@
   encoder.EncodeField(
       RtcTestEvent::wrapping21_params,
       ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_));
+  encoder.EncodeField(RtcTestEvent::string_params,
+                      ExtractRtcEventMember(batch_, &RtcTestEvent::string_));
   std::string s = encoder.AsString();
 
   // Optional debug printing
@@ -475,6 +527,8 @@
                               optional64_values, /*no deltas*/ 0);
   ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values,
                       /*no deltas*/ 0);
+  ParseAndVerifyStringField(RtcTestEvent::string_params, string_values);
+  EXPECT_EQ(parser_.RemainingBytes(), 0u);
 }
 
 TEST_F(RtcEventFieldTest, Increasing) {
@@ -490,10 +544,12 @@
       kInt64Max - 1, kInt64Max, kInt64Min, kInt64Min + 1};
   std::vector<uint32_t> wrapping21_values = {(1 << 21) - 2, (1 << 21) - 1, 0,
                                              1};
+  std::vector<std::string> string_values = {
+      "", "a", "bc", "def"};  // No special compression of strings.
 
   CreateFullEvents(bool_values, signed32_values, unsigned32_values,
                    signed64_values, unsigned64_values, optional32_values,
-                   optional64_values, wrapping21_values);
+                   optional64_values, wrapping21_values, string_values);
 
   EventEncoder encoder(RtcTestEvent::event_params, batch_);
   encoder.EncodeField(RtcTestEvent::bool_params,
@@ -517,6 +573,8 @@
   encoder.EncodeField(
       RtcTestEvent::wrapping21_params,
       ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_));
+  encoder.EncodeField(RtcTestEvent::string_params,
+                      ExtractRtcEventMember(batch_, &RtcTestEvent::string_));
   std::string s = encoder.AsString();
 
   // Optional debug printing
@@ -540,6 +598,8 @@
                               optional64_values, /*delta bits*/ 1);
   ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values,
                       /*delta bits*/ 1);
+  ParseAndVerifyStringField(RtcTestEvent::string_params, string_values);
+  EXPECT_EQ(parser_.RemainingBytes(), 0u);
 }
 
 TEST_F(RtcEventFieldTest, Decreasing) {
@@ -555,10 +615,12 @@
       kInt64Min + 1, kInt64Min, kInt64Max, kInt64Max - 1};
   std::vector<uint32_t> wrapping21_values = {1, 0, (1 << 21) - 1,
                                              (1 << 21) - 2};
+  std::vector<std::string> string_values = {
+      "def", "bc", "a", ""};  // No special compression of strings.
 
   CreateFullEvents(bool_values, signed32_values, unsigned32_values,
                    signed64_values, unsigned64_values, optional32_values,
-                   optional64_values, wrapping21_values);
+                   optional64_values, wrapping21_values, string_values);
 
   EventEncoder encoder(RtcTestEvent::event_params, batch_);
   encoder.EncodeField(RtcTestEvent::bool_params,
@@ -582,6 +644,8 @@
   encoder.EncodeField(
       RtcTestEvent::wrapping21_params,
       ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_));
+  encoder.EncodeField(RtcTestEvent::string_params,
+                      ExtractRtcEventMember(batch_, &RtcTestEvent::string_));
   std::string s = encoder.AsString();
 
   // Optional debug printing
@@ -605,6 +669,8 @@
                               optional64_values, /*delta bits*/ 1);
   ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values,
                       /*delta bits*/ 1);
+  ParseAndVerifyStringField(RtcTestEvent::string_params, string_values);
+  EXPECT_EQ(parser_.RemainingBytes(), 0u);
 }
 
 TEST_F(RtcEventFieldTest, SkipsDeprecatedFields) {
@@ -620,6 +686,7 @@
   std::vector<absl::optional<int64_t>> optional64_values = {kInt64Min / 2,
                                                             kInt64Max / 2};
   std::vector<uint32_t> wrapping21_values = {0, 1 << 20};
+  std::vector<std::string> string_values = {"foo", "bar"};
 
   size_t signed32_encoding_size =
       /*tag*/ 1 + /* varint base*/ 5 + /* delta_header*/ 1 + /*deltas*/ 4;
@@ -630,7 +697,7 @@
 
   CreateFullEvents(bool_values, signed32_values, unsigned32_values,
                    signed64_values, unsigned64_values, optional32_values,
-                   optional64_values, wrapping21_values);
+                   optional64_values, wrapping21_values, string_values);
 
   EventEncoder encoder(RtcTestEvent::event_params, batch_);
   encoder.EncodeField(RtcTestEvent::bool_params,
@@ -654,6 +721,8 @@
   encoder.EncodeField(
       RtcTestEvent::wrapping21_params,
       ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_));
+  encoder.EncodeField(RtcTestEvent::string_params,
+                      ExtractRtcEventMember(batch_, &RtcTestEvent::string_));
   std::string s = encoder.AsString();
 
   // Optional debug printing
@@ -666,7 +735,8 @@
   // Skips parsing the `signed32_values`. The following unsigned fields should
   // still be found.
   ParseAndVerifyField(RtcTestEvent::unsigned32_params, unsigned32_values,
-                      /*delta_bits=*/31, signed32_encoding_size);
+                      /*delta_bits=*/31,
+                      /*expected_skipped_bytes=*/signed32_encoding_size);
   // Skips parsing the `signed64_values`. The following unsigned fields should
   // still be found.
   ParseAndVerifyField(RtcTestEvent::unsigned64_params, unsigned64_values,
@@ -678,6 +748,8 @@
                               /*delta_bits=*/63, optional32_encoding_size);
   ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values,
                       /*delta_bits=*/20);
+  ParseAndVerifyStringField(RtcTestEvent::string_params, string_values);
+  EXPECT_EQ(parser_.RemainingBytes(), 0u);
 }
 
 TEST_F(RtcEventFieldTest, SkipsMissingFields) {
@@ -693,10 +765,11 @@
   std::vector<absl::optional<int64_t>> optional64_values = {kInt64Min / 2,
                                                             kInt64Max / 2};
   std::vector<uint32_t> wrapping21_values = {0, 1 << 20};
+  std::vector<std::string> string_values = {"foo", "foo"};
 
   CreateFullEvents(bool_values, signed32_values, unsigned32_values,
                    signed64_values, unsigned64_values, optional32_values,
-                   optional64_values, wrapping21_values);
+                   optional64_values, wrapping21_values, string_values);
 
   EventEncoder encoder(RtcTestEvent::event_params, batch_);
   // Skip encoding the `bool_values`.
@@ -713,6 +786,8 @@
   encoder.EncodeField(
       RtcTestEvent::wrapping21_params,
       ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_));
+  encoder.EncodeField(RtcTestEvent::string_params,
+                      ExtractRtcEventMember(batch_, &RtcTestEvent::string_));
   std::string s = encoder.AsString();
 
   // Optional debug printing
@@ -732,6 +807,8 @@
   ParseAndVerifyMissingOptionalField(RtcTestEvent::optional64_params);
   ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values,
                       /*delta_bits=*/20);
+  ParseAndVerifyStringField(RtcTestEvent::string_params, string_values);
+  EXPECT_EQ(parser_.RemainingBytes(), 0u);
 }
 
 TEST_F(RtcEventFieldTest, OptionalFields) {
@@ -744,7 +821,7 @@
   for (size_t i = 0; i < optional32_values.size(); i++) {
     batch_.push_back(new RtcTestEvent(0, 0, 0, 0, 0, optional32_values[i],
                                       optional64_values[i],
-                                      wrapping21_values[i]));
+                                      wrapping21_values[i], ""));
   }
 
   EventEncoder encoder(RtcTestEvent::event_params, batch_);
@@ -770,6 +847,7 @@
                               optional64_values, /*delta bits*/ 1);
   ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values,
                       /*delta bits*/ 2);
+  EXPECT_EQ(parser_.RemainingBytes(), 0u);
 }
 
 TEST_F(RtcEventFieldTest, AllNulloptTreatedAsMissing) {
@@ -781,7 +859,7 @@
 
   for (size_t i = 0; i < optional32_values.size(); i++) {
     batch_.push_back(new RtcTestEvent(0, 0, 0, 0, 0, optional32_values[i],
-                                      optional64_values[i], 0));
+                                      optional64_values[i], 0, ""));
   }
 
   EventEncoder encoder(RtcTestEvent::event_params, batch_);
@@ -801,6 +879,7 @@
   ParseAndVerifyMissingOptionalField(RtcTestEvent::optional32_params);
   ParseAndVerifyOptionalField(RtcTestEvent::optional64_params,
                               optional64_values, /*delta_bits=*/1);
+  EXPECT_EQ(parser_.RemainingBytes(), 0u);
 }
 
 }  // namespace webrtc
diff --git a/logging/rtc_event_log/events/rtc_event_field_extraction.h b/logging/rtc_event_log/events/rtc_event_field_extraction.h
index a0e8ff0..8cd020f 100644
--- a/logging/rtc_event_log/events/rtc_event_field_extraction.h
+++ b/logging/rtc_event_log/events/rtc_event_field_extraction.h
@@ -116,13 +116,27 @@
   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 std::vector<uint64_t>& values,
+void PopulateRtcEventMember(const rtc::ArrayView<uint64_t> values,
                             T E::*member,
                             rtc::ArrayView<E> output) {
   size_t batch_size = values.size();
@@ -136,8 +150,8 @@
 template <typename T,
           typename E,
           std::enable_if_t<std::is_integral<T>::value, bool> = true>
-void PopulateRtcEventMember(const std::vector<bool>& positions,
-                            const std::vector<uint64_t>& values,
+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();
@@ -156,6 +170,17 @@
   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];
+  }
+}
+
 }  // namespace webrtc
 
 #endif  // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FIELD_EXTRACTION_H_