Prepare for new encoding of RTC event log numeric fields.

Bug: webrtc:11933
Change-Id: I32e59059ea6166b2fc089d9d19d3ab3829c2190e
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/228942
Commit-Queue: Björn Terelius <terelius@webrtc.org>
Reviewed-by: Sebastian Jansson <srte@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#35071}
diff --git a/logging/BUILD.gn b/logging/BUILD.gn
index 200a377..68f6092 100644
--- a/logging/BUILD.gn
+++ b/logging/BUILD.gn
@@ -32,6 +32,27 @@
   deps = [ "../api/rtc_event_log" ]
 }
 
+rtc_library("rtc_event_field") {
+  sources = [
+    "rtc_event_log/events/rtc_event_field_extraction.cc",
+    "rtc_event_log/events/rtc_event_field_extraction.h",
+  ]
+
+  deps = [
+    ":rtc_event_number_encodings",
+    "../api:array_view",
+    "../api/rtc_event_log",
+    "../rtc_base:checks",
+    "../rtc_base:logging",
+    "../rtc_base:rtc_base_approved",
+  ]
+  absl_deps = [
+    "//third_party/abseil-cpp/absl/memory",
+    "//third_party/abseil-cpp/absl/strings",
+    "//third_party/abseil-cpp/absl/types:optional",
+  ]
+}
+
 rtc_library("rtc_stream_config") {
   sources = [
     "rtc_event_log/rtc_stream_config.cc",
@@ -190,14 +211,10 @@
   absl_deps = [ "//third_party/abseil-cpp/absl/memory" ]
 }
 
-# TODO(eladalon): Break down into (1) encoder and (2) decoder; we don't need
-# the decoder code in the WebRTC library, only in unit tests and tools.
-rtc_library("rtc_event_log_impl_encoder") {
+rtc_library("rtc_event_number_encodings") {
   sources = [
-    "rtc_event_log/encoder/blob_encoding.cc",
-    "rtc_event_log/encoder/blob_encoding.h",
-    "rtc_event_log/encoder/delta_encoding.cc",
-    "rtc_event_log/encoder/delta_encoding.h",
+    "rtc_event_log/encoder/bit_writer.cc",
+    "rtc_event_log/encoder/bit_writer.h",
     "rtc_event_log/encoder/rtc_event_log_encoder_common.cc",
     "rtc_event_log/encoder/rtc_event_log_encoder_common.h",
     "rtc_event_log/encoder/var_int.cc",
@@ -207,6 +224,33 @@
   defines = []
 
   deps = [
+    "../rtc_base:bitstream_reader",
+    "../rtc_base:checks",
+    "../rtc_base:ignore_wundef",
+    "../rtc_base:macromagic",
+    "../rtc_base:rtc_base_approved",
+  ]
+  absl_deps = [
+    "//third_party/abseil-cpp/absl/memory",
+    "//third_party/abseil-cpp/absl/strings",
+    "//third_party/abseil-cpp/absl/types:optional",
+  ]
+}
+
+# TODO(eladalon): Break down into (1) encoder and (2) decoder; we don't need
+# the decoder code in the WebRTC library, only in unit tests and tools.
+rtc_library("rtc_event_log_impl_encoder") {
+  sources = [
+    "rtc_event_log/encoder/blob_encoding.cc",
+    "rtc_event_log/encoder/blob_encoding.h",
+    "rtc_event_log/encoder/delta_encoding.cc",
+    "rtc_event_log/encoder/delta_encoding.h",
+  ]
+
+  defines = []
+
+  deps = [
+    ":rtc_event_number_encodings",
     "../api:rtp_headers",
     "../api:rtp_parameters",
     "../api/transport:network_control",
@@ -330,6 +374,7 @@
       ":rtc_event_log2_proto",
       ":rtc_event_log_impl_encoder",
       ":rtc_event_log_proto",
+      ":rtc_event_number_encodings",
       ":rtc_event_pacing",
       ":rtc_event_rtp_rtcp",
       ":rtc_event_video",
@@ -369,6 +414,7 @@
         "rtc_event_log/encoder/delta_encoding_unittest.cc",
         "rtc_event_log/encoder/rtc_event_log_encoder_common_unittest.cc",
         "rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc",
+        "rtc_event_log/events/rtc_event_field_extraction_unittest.cc",
         "rtc_event_log/rtc_event_log_unittest.cc",
         "rtc_event_log/rtc_event_log_unittest_helper.cc",
         "rtc_event_log/rtc_event_log_unittest_helper.h",
@@ -378,12 +424,14 @@
         ":ice_log",
         ":rtc_event_audio",
         ":rtc_event_bwe",
+        ":rtc_event_field",
         ":rtc_event_frame_events",
         ":rtc_event_generic_packet_events",
         ":rtc_event_log2_proto",
         ":rtc_event_log_impl_encoder",
         ":rtc_event_log_parser",
         ":rtc_event_log_proto",
+        ":rtc_event_number_encodings",
         ":rtc_event_pacing",
         ":rtc_event_rtp_rtcp",
         ":rtc_event_video",
diff --git a/logging/rtc_event_log/encoder/bit_writer.cc b/logging/rtc_event_log/encoder/bit_writer.cc
new file mode 100644
index 0000000..e8748d3
--- /dev/null
+++ b/logging/rtc_event_log/encoder/bit_writer.cc
@@ -0,0 +1,49 @@
+/*
+ *  Copyright (c) 2020 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/encoder/bit_writer.h"
+
+namespace webrtc {
+
+namespace {
+size_t BitsToBytes(size_t bits) {
+  return (bits / 8) + (bits % 8 > 0 ? 1 : 0);
+}
+}  // namespace
+
+void BitWriter::WriteBits(uint64_t val, size_t bit_count) {
+  RTC_DCHECK(valid_);
+  const bool success = bit_writer_.WriteBits(val, bit_count);
+  RTC_DCHECK(success);
+  written_bits_ += bit_count;
+}
+
+void BitWriter::WriteBits(absl::string_view input) {
+  RTC_DCHECK(valid_);
+  for (char c : input) {
+    WriteBits(static_cast<unsigned char>(c), CHAR_BIT);
+  }
+}
+
+// Returns everything that was written so far.
+// Nothing more may be written after this is called.
+std::string BitWriter::GetString() {
+  RTC_DCHECK(valid_);
+  valid_ = false;
+
+  buffer_.resize(BitsToBytes(written_bits_));
+  written_bits_ = 0;
+
+  std::string result;
+  std::swap(buffer_, result);
+  return result;
+}
+
+}  // namespace webrtc
diff --git a/logging/rtc_event_log/encoder/bit_writer.h b/logging/rtc_event_log/encoder/bit_writer.h
new file mode 100644
index 0000000..85340c3
--- /dev/null
+++ b/logging/rtc_event_log/encoder/bit_writer.h
@@ -0,0 +1,61 @@
+/*
+ *  Copyright (c) 2020 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_ENCODER_BIT_WRITER_H_
+#define LOGGING_RTC_EVENT_LOG_ENCODER_BIT_WRITER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+#include <utility>
+
+#include "absl/strings/string_view.h"
+#include "rtc_base/bit_buffer.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/constructor_magic.h"
+
+namespace webrtc {
+
+// Wrap BitBufferWriter and extend its functionality by (1) keeping track of
+// the number of bits written and (2) owning its buffer.
+class BitWriter final {
+ public:
+  explicit BitWriter(size_t byte_count)
+      : buffer_(byte_count, '\0'),
+        bit_writer_(reinterpret_cast<uint8_t*>(&buffer_[0]), buffer_.size()),
+        written_bits_(0),
+        valid_(true) {
+    RTC_DCHECK_GT(byte_count, 0);
+  }
+
+  void WriteBits(uint64_t val, size_t bit_count);
+
+  void WriteBits(absl::string_view input);
+
+  // Returns everything that was written so far.
+  // Nothing more may be written after this is called.
+  std::string GetString();
+
+ private:
+  std::string buffer_;
+  rtc::BitBufferWriter bit_writer_;
+  // Note: Counting bits instead of bytes wraps around earlier than it has to,
+  // which means the maximum length is lower than it could be. We don't expect
+  // to go anywhere near the limit, though, so this is good enough.
+  size_t written_bits_;
+  bool valid_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(BitWriter);
+};
+
+}  // namespace webrtc
+
+#endif  // LOGGING_RTC_EVENT_LOG_ENCODER_BIT_WRITER_H_
diff --git a/logging/rtc_event_log/encoder/delta_encoding.cc b/logging/rtc_event_log/encoder/delta_encoding.cc
index 437392f..a96d3a7 100644
--- a/logging/rtc_event_log/encoder/delta_encoding.cc
+++ b/logging/rtc_event_log/encoder/delta_encoding.cc
@@ -16,6 +16,7 @@
 #include <utility>
 
 #include "absl/memory/memory.h"
+#include "logging/rtc_event_log/encoder/bit_writer.h"
 #include "logging/rtc_event_log/encoder/var_int.h"
 #include "rtc_base/bit_buffer.h"
 #include "rtc_base/bitstream_reader.h"
@@ -107,58 +108,6 @@
 constexpr bool kDefaultValuesOptional = false;
 constexpr uint64_t kDefaultValueWidthBits = 64;
 
-// Wrap BitBufferWriter and extend its functionality by (1) keeping track of
-// the number of bits written and (2) owning its buffer.
-class BitWriter final {
- public:
-  explicit BitWriter(size_t byte_count)
-      : buffer_(byte_count, '\0'),
-        bit_writer_(reinterpret_cast<uint8_t*>(&buffer_[0]), buffer_.size()),
-        written_bits_(0),
-        valid_(true) {
-    RTC_DCHECK_GT(byte_count, 0);
-  }
-
-  void WriteBits(uint64_t val, size_t bit_count) {
-    RTC_DCHECK(valid_);
-    const bool success = bit_writer_.WriteBits(val, bit_count);
-    RTC_DCHECK(success);
-    written_bits_ += bit_count;
-  }
-
-  void WriteBits(const std::string& input) {
-    RTC_DCHECK(valid_);
-    for (std::string::value_type c : input) {
-      WriteBits(c, 8 * sizeof(std::string::value_type));
-    }
-  }
-
-  // Returns everything that was written so far.
-  // Nothing more may be written after this is called.
-  std::string GetString() {
-    RTC_DCHECK(valid_);
-    valid_ = false;
-
-    buffer_.resize(BitsToBytes(written_bits_));
-    written_bits_ = 0;
-
-    std::string result;
-    std::swap(buffer_, result);
-    return result;
-  }
-
- private:
-  std::string buffer_;
-  rtc::BitBufferWriter bit_writer_;
-  // Note: Counting bits instead of bytes wraps around earlier than it has to,
-  // which means the maximum length is lower than it could be. We don't expect
-  // to go anywhere near the limit, though, so this is good enough.
-  size_t written_bits_;
-  bool valid_;
-
-  RTC_DISALLOW_COPY_AND_ASSIGN(BitWriter);
-};
-
 // Parameters for fixed-size delta-encoding/decoding.
 // These are tailored for the sequence which will be encoded (e.g. widths).
 class FixedLengthEncodingParameters final {
diff --git a/logging/rtc_event_log/events/rtc_event_field_extraction.cc b/logging/rtc_event_log/events/rtc_event_field_extraction.cc
new file mode 100644
index 0000000..99f0b36
--- /dev/null
+++ b/logging/rtc_event_log/events/rtc_event_field_extraction.cc
@@ -0,0 +1,60 @@
+/*
+ *  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_extraction.h"
+
+#include <algorithm>
+#include <limits>
+
+#include "rtc_base/checks.h"
+
+namespace webrtc_event_logging {
+
+// The bitwidth required to encode values in the range
+// [0, `max_pos_magnitude`] using an unsigned representation.
+uint8_t UnsignedBitWidth(uint64_t max_magnitude) {
+  uint8_t required_bits = 1;
+  while (max_magnitude >>= 1) {
+    ++required_bits;
+  }
+  return required_bits;
+}
+
+// The bitwidth required to encode signed values in the range
+// [-`max_neg_magnitude`, `max_pos_magnitude`] using a signed
+// 2-complement representation.
+uint8_t SignedBitWidth(uint64_t max_pos_magnitude, uint64_t max_neg_magnitude) {
+  const uint8_t bitwidth_positive =
+      max_pos_magnitude > 0 ? UnsignedBitWidth(max_pos_magnitude) : 0;
+  const uint8_t bitwidth_negative =
+      (max_neg_magnitude > 1) ? UnsignedBitWidth(max_neg_magnitude - 1) : 0;
+  return 1 + std::max(bitwidth_positive, bitwidth_negative);
+}
+
+// Return the maximum integer of a given bit width.
+uint64_t MaxUnsignedValueOfBitWidth(uint64_t bit_width) {
+  RTC_DCHECK_GE(bit_width, 1);
+  RTC_DCHECK_LE(bit_width, 64);
+  return (bit_width == 64) ? std::numeric_limits<uint64_t>::max()
+                           : ((static_cast<uint64_t>(1) << bit_width) - 1);
+}
+
+// Computes the delta between `previous` and `current`, under the assumption
+// that `bit_mask` is the largest value before wrap-around occurs. The bitmask
+// must be of the form 2^x-1. (We use the wrap-around to more efficiently
+// compress counters that wrap around at different bit widths than the
+// backing C++ data type.)
+uint64_t UnsignedDelta(uint64_t previous, uint64_t current, uint64_t bit_mask) {
+  RTC_DCHECK_LE(previous, bit_mask);
+  RTC_DCHECK_LE(current, bit_mask);
+  return (current - previous) & bit_mask;
+}
+
+}  // namespace webrtc_event_logging
diff --git a/logging/rtc_event_log/events/rtc_event_field_extraction.h b/logging/rtc_event_log/events/rtc_event_field_extraction.h
new file mode 100644
index 0000000..a0e8ff0
--- /dev/null
+++ b/logging/rtc_event_log/events/rtc_event_field_extraction.h
@@ -0,0 +1,161 @@
+/*
+ *  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 "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;
+}
+
+// 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,
+                            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 std::vector<bool>& positions,
+                            const std::vector<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());
+}
+
+}  // namespace webrtc
+
+#endif  // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FIELD_EXTRACTION_H_
diff --git a/logging/rtc_event_log/events/rtc_event_field_extraction_unittest.cc b/logging/rtc_event_log/events/rtc_event_field_extraction_unittest.cc
new file mode 100644
index 0000000..f9fb993
--- /dev/null
+++ b/logging/rtc_event_log/events/rtc_event_field_extraction_unittest.cc
@@ -0,0 +1,97 @@
+/*   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_extraction.h"
+
+#include "rtc_base/random.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+TEST(UnsignedBitWidthTest, SmallValues) {
+  EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(0), 1u);
+  EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(1), 1u);
+  EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(2), 2u);
+  EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(3), 2u);
+  EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(4), 3u);
+  EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(5), 3u);
+  EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(6), 3u);
+  EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(7), 3u);
+}
+
+TEST(UnsignedBitWidthTest, PowersOfTwo) {
+  EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(0), 1u);
+
+  for (unsigned i = 0; i < 64; i++) {
+    uint64_t x = 1;
+    x = x << i;
+    EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(x), i + 1);
+  }
+}
+
+TEST(UnsignedBitWidthTest, PowersOfTwoMinusOne) {
+  for (unsigned i = 1; i < 64; i++) {
+    uint64_t x = 1;
+    x = (x << i) - 1;
+    EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(x), i);
+  }
+
+  uint64_t x = ~static_cast<uint64_t>(0);
+  EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(x), 64u);
+}
+
+TEST(UnsignedBitWidthTest, RandomInputs) {
+  Random rand(12345);
+
+  for (unsigned i = 0; i < 64; i++) {
+    uint64_t x = 1;
+    x = x << i;
+    uint64_t high = rand.Rand<uint32_t>();
+    uint64_t low = rand.Rand<uint32_t>();
+    x += ((high << 32) + low) % x;
+    EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(x), i + 1);
+  }
+}
+
+TEST(SignedBitWidthTest, SignedBitWidth) {
+  EXPECT_EQ(webrtc_event_logging::SignedBitWidth(0, 1), 1u);
+  EXPECT_EQ(webrtc_event_logging::SignedBitWidth(1, 0), 2u);
+  EXPECT_EQ(webrtc_event_logging::SignedBitWidth(1, 2), 2u);
+  EXPECT_EQ(webrtc_event_logging::SignedBitWidth(1, 128), 8u);
+  EXPECT_EQ(webrtc_event_logging::SignedBitWidth(127, 1), 8u);
+  EXPECT_EQ(webrtc_event_logging::SignedBitWidth(127, 128), 8u);
+  EXPECT_EQ(webrtc_event_logging::SignedBitWidth(1, 129), 9u);
+  EXPECT_EQ(webrtc_event_logging::SignedBitWidth(128, 1), 9u);
+}
+
+TEST(MaxUnsignedValueOfBitWidthTest, MaxUnsignedValueOfBitWidth) {
+  EXPECT_EQ(webrtc_event_logging::MaxUnsignedValueOfBitWidth(1), 0x01u);
+  EXPECT_EQ(webrtc_event_logging::MaxUnsignedValueOfBitWidth(6), 0x3Fu);
+  EXPECT_EQ(webrtc_event_logging::MaxUnsignedValueOfBitWidth(8), 0xFFu);
+  EXPECT_EQ(webrtc_event_logging::MaxUnsignedValueOfBitWidth(32), 0xFFFFFFFFu);
+}
+
+TEST(EncodeAsUnsignedTest, NegativeValues) {
+  // Negative values are converted as if cast to unsigned type of
+  // the same bitsize using 2-complement representation.
+  int16_t x = -1;
+  EXPECT_EQ(EncodeAsUnsigned(x), static_cast<uint64_t>(0xFFFF));
+  int64_t y = -1;
+  EXPECT_EQ(EncodeAsUnsigned(y), static_cast<uint64_t>(0xFFFFFFFFFFFFFFFFull));
+}
+
+TEST(EncodeAsUnsignedTest, PositiveValues) {
+  // Postive values are unchanged.
+  int16_t x = 42;
+  EXPECT_EQ(EncodeAsUnsigned(x), static_cast<uint64_t>(42));
+  int64_t y = 42;
+  EXPECT_EQ(EncodeAsUnsigned(y), static_cast<uint64_t>(42));
+}
+
+}  // namespace webrtc