Replace base64 implementation with absl

Benchmarking results show a much more efficient implementation, around 80% faster for decoding 4K inputs.

benchmark_result
name                              old cpu/op   new cpu/op   delta
BM_Base64Encode/64                 156ns ± 1%    66ns ± 2%  -57.30%   (p=0.000 n=9+10)
BM_Base64Encode/512               1.00µs ± 2%  0.34µs ± 2%  -65.82%   (p=0.000 n=9+10)
BM_Base64Encode/4096              7.79µs ± 2%  2.50µs ± 1%  -67.97%   (p=0.000 n=10+9)
BM_Base64Encode/32768             61.8µs ± 2%  19.6µs ± 2%  -68.21%  (p=0.000 n=10+10)
BM_Base64Encode/262144             495µs ± 2%   157µs ± 2%  -68.27%   (p=0.000 n=10+9)
BM_Base64Encode/2097152           4.04ms ± 2%  1.26ms ± 2%  -68.89%    (p=0.000 n=9+9)
BM_Base64Encode/8388608           16.1ms ± 3%   5.0ms ± 2%  -68.75%   (p=0.000 n=10+9)
BM_Base64Decode/64                 331ns ± 2%   137ns ± 2%  -58.45%   (p=0.000 n=9+10)
BM_Base64Decode/512               2.22µs ± 2%  0.88µs ± 2%  -60.35%  (p=0.000 n=10+10)
BM_Base64Decode/4096              17.5µs ± 1%   6.8µs ± 1%  -61.38%    (p=0.000 n=9+9)
BM_Base64Decode/32768              139µs ± 1%    54µs ± 1%  -61.39%    (p=0.000 n=9+8)
BM_Base64Decode/262144            1.11ms ± 3%  0.43ms ± 2%  -61.46%   (p=0.000 n=10+9)
BM_Base64Decode/2097152           8.93ms ± 2%  3.42ms ± 2%  -61.75%   (p=0.000 n=10+9)
BM_Base64Decode/8388608           35.7ms ± 1%  13.7ms ± 2%  -61.63%    (p=0.000 n=9+9)
BM_Base64DecodeForgiving/64        327ns ± 2%    96ns ± 2%  -70.69%   (p=0.000 n=9+10)
BM_Base64DecodeForgiving/512      2.35µs ± 3%  0.49µs ± 2%  -78.93%  (p=0.000 n=10+10)
BM_Base64DecodeForgiving/4096     18.6µs ± 2%   3.7µs ± 2%  -80.21%   (p=0.000 n=8+10)
BM_Base64DecodeForgiving/32768     148µs ± 2%    29µs ± 4%  -80.45%   (p=0.000 n=9+10)
BM_Base64DecodeForgiving/262144   1.18ms ± 2%  0.23ms ± 2%  -80.51%  (p=0.000 n=10+10)
BM_Base64DecodeForgiving/2097152  9.54ms ± 1%  1.84ms ± 1%  -80.69%   (p=0.000 n=8+10)
BM_Base64DecodeForgiving/8388608  37.9ms ± 2%   7.4ms ± 1%  -80.56%  (p=0.000 n=10+10)

name                              old time/op          new time/op          delta
BM_Base64Encode/64                 156ns ± 1%            67ns ± 2%  -57.29%   (p=0.000 n=9+10)
BM_Base64Encode/512               1.00µs ± 2%          0.34µs ± 2%  -65.82%   (p=0.000 n=9+10)
BM_Base64Encode/4096              7.83µs ± 1%          2.50µs ± 1%  -68.05%    (p=0.000 n=9+9)
BM_Base64Encode/32768             62.0µs ± 2%          19.7µs ± 2%  -68.21%  (p=0.000 n=10+10)
BM_Base64Encode/262144             496µs ± 2%           157µs ± 2%  -68.27%   (p=0.000 n=10+9)
BM_Base64Encode/2097152           4.05ms ± 2%          1.26ms ± 2%  -68.88%    (p=0.000 n=9+9)
BM_Base64Encode/8388608           16.1ms ± 3%           5.0ms ± 2%  -68.83%   (p=0.000 n=10+8)
BM_Base64Decode/64                 331ns ± 2%           138ns ± 2%  -58.34%    (p=0.000 n=9+9)
BM_Base64Decode/512               2.22µs ± 2%          0.88µs ± 2%  -60.35%  (p=0.000 n=10+10)
BM_Base64Decode/4096              17.6µs ± 1%           6.8µs ± 1%  -61.37%    (p=0.000 n=9+9)
BM_Base64Decode/32768              140µs ± 1%            54µs ± 1%  -61.38%    (p=0.000 n=9+8)
BM_Base64Decode/262144            1.11ms ± 3%          0.43ms ± 2%  -61.45%   (p=0.000 n=10+9)
BM_Base64Decode/2097152           8.95ms ± 2%          3.43ms ± 2%  -61.73%   (p=0.000 n=10+9)
BM_Base64Decode/8388608           35.8ms ± 1%          13.7ms ± 2%  -61.61%    (p=0.000 n=9+9)
BM_Base64DecodeForgiving/64        328ns ± 2%            96ns ± 2%  -70.68%   (p=0.000 n=9+10)
BM_Base64DecodeForgiving/512      2.35µs ± 3%          0.50µs ± 2%  -78.92%  (p=0.000 n=10+10)
BM_Base64DecodeForgiving/4096     18.6µs ± 2%           3.7µs ± 2%  -80.21%   (p=0.000 n=8+10)
BM_Base64DecodeForgiving/32768     149µs ± 2%            29µs ± 4%  -80.45%   (p=0.000 n=9+10)
BM_Base64DecodeForgiving/262144   1.18ms ± 2%          0.23ms ± 2%  -80.51%  (p=0.000 n=10+10)
BM_Base64DecodeForgiving/2097152  9.56ms ± 1%          1.85ms ± 1%  -80.68%   (p=0.000 n=8+10)
BM_Base64DecodeForgiving/8388608  38.0ms ± 2%           7.4ms ± 1%  -80.55%  (p=0.000 n=10+10)

name                              old INSTRUCTIONS/op  new INSTRUCTIONS/op  delta
BM_Base64Encode/64                 2.27k ± 0%           0.87k ± 0%  -61.60%  (p=0.000 n=10+10)
BM_Base64Encode/512                15.4k ± 0%            4.5k ± 0%  -71.19%  (p=0.000 n=10+10)
BM_Base64Encode/4096                121k ± 0%             33k ± 0%  -72.48%  (p=0.000 n=10+10)
BM_Base64Encode/32768               965k ± 0%            263k ± 0%  -72.77%  (p=0.000 n=10+10)
BM_Base64Encode/262144             7.71M ± 0%           2.10M ± 0%  -72.81%   (p=0.000 n=9+10)
BM_Base64Encode/2097152            61.7M ± 0%           16.8M ± 0%  -72.81%   (p=0.000 n=9+10)
BM_Base64Encode/8388608             247M ± 0%             67M ± 0%  -72.81%  (p=0.000 n=10+10)
BM_Base64Decode/64                 4.41k ± 0%           1.91k ± 0%  -56.60%   (p=0.000 n=9+10)
BM_Base64Decode/512                32.4k ± 0%           12.1k ± 0%  -62.84%  (p=0.000 n=10+10)
BM_Base64Decode/4096                257k ± 0%             94k ± 0%  -63.65%    (p=0.001 n=8+9)
BM_Base64Decode/32768              2.05M ± 0%           0.74M ± 0%  -63.81%   (p=0.000 n=9+10)
BM_Base64Decode/262144             16.4M ± 0%            5.9M ± 0%  -63.83%    (p=0.000 n=9+8)
BM_Base64Decode/2097152             131M ± 0%             48M ± 0%  -63.83%  (p=0.000 n=10+10)
BM_Base64Decode/8388608             526M ± 0%            190M ± 0%  -63.83%  (p=0.000 n=10+10)
BM_Base64DecodeForgiving/64        4.45k ± 0%           1.30k ± 0%  -70.73%   (p=0.000 n=9+10)
BM_Base64DecodeForgiving/512       32.7k ± 0%            7.1k ± 0%  -78.15%   (p=0.000 n=9+10)
BM_Base64DecodeForgiving/4096       259k ± 0%             54k ± 0%  -79.01%    (p=0.000 n=9+8)
BM_Base64DecodeForgiving/32768     2.07M ± 0%           0.43M ± 0%  -79.23%    (p=0.000 n=8+8)
BM_Base64DecodeForgiving/262144    16.6M ± 0%            3.4M ± 0%  -79.25%    (p=0.000 n=8+9)
BM_Base64DecodeForgiving/2097152    132M ± 0%             27M ± 0%  -79.26%    (p=0.000 n=8+9)
BM_Base64DecodeForgiving/8388608    530M ± 0%            110M ± 0%  -79.26%    (p=0.001 n=6+8)

name                              old CYCLES/op        new CYCLES/op        delta
BM_Base64Encode/64                   502 ± 0%             214 ± 0%  -57.25%   (p=0.000 n=10+8)
BM_Base64Encode/512                3.22k ± 0%           1.10k ± 0%  -65.82%   (p=0.000 n=9+10)
BM_Base64Encode/4096               25.1k ± 0%            8.0k ± 0%  -68.02%   (p=0.000 n=8+10)
BM_Base64Encode/32768               200k ± 0%             63k ± 0%  -68.33%  (p=0.000 n=10+10)
BM_Base64Encode/262144             1.60M ± 0%           0.51M ± 0%  -68.38%    (p=0.000 n=9+9)
BM_Base64Encode/2097152            13.0M ± 0%            4.0M ± 0%  -68.91%  (p=0.000 n=10+10)
BM_Base64Encode/8388608            52.0M ± 0%           16.2M ± 0%  -68.88%  (p=0.000 n=10+10)
BM_Base64Decode/64                 1.07k ± 0%           0.44k ± 0%  -58.38%  (p=0.000 n=10+10)
BM_Base64Decode/512                7.19k ± 0%           2.85k ± 0%  -60.36%   (p=0.000 n=9+10)
BM_Base64Decode/4096               56.5k ± 0%           21.9k ± 0%  -61.30%   (p=0.000 n=8+10)
BM_Base64Decode/32768               450k ± 0%            173k ± 0%  -61.50%   (p=0.000 n=10+9)
BM_Base64Decode/262144             3.60M ± 0%           1.38M ± 0%  -61.58%   (p=0.000 n=9+10)
BM_Base64Decode/2097152            28.8M ± 0%           11.1M ± 0%  -61.60%    (p=0.000 n=9+9)
BM_Base64Decode/8388608             115M ± 0%             44M ± 0%  -61.56%    (p=0.000 n=9+9)
BM_Base64DecodeForgiving/64        1.06k ± 0%           0.31k ± 0%  -70.66%   (p=0.000 n=9+10)
BM_Base64DecodeForgiving/512       7.61k ± 0%           1.60k ± 0%  -79.00%  (p=0.000 n=10+10)
BM_Base64DecodeForgiving/4096      60.1k ± 0%           11.9k ± 0%  -80.14%  (p=0.000 n=10+10)
BM_Base64DecodeForgiving/32768      478k ± 0%             94k ± 0%  -80.42%    (p=0.000 n=9+9)
BM_Base64DecodeForgiving/262144    3.83M ± 0%           0.75M ± 0%  -80.51%  (p=0.000 n=10+10)
BM_Base64DecodeForgiving/2097152   30.6M ± 0%            6.0M ± 0%  -80.54%  (p=0.000 n=10+10)
BM_Base64DecodeForgiving/8388608    123M ± 0%             24M ± 0%  -80.55%   (p=0.000 n=9+10)

Bug: webrtc:42220265
Change-Id: I753d9314f2102efddc7480537b104fee73d11dca
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/384400
Reviewed-by: Tomas Gunnarsson <tommi@webrtc.org>
Commit-Queue: Evan Shrubsole <eshr@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#44314}
diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn
index a6486b6..3ea1dc5 100644
--- a/rtc_base/BUILD.gn
+++ b/rtc_base/BUILD.gn
@@ -1788,9 +1788,9 @@
     "base64.h",
   ]
   deps = [
-    ":checks",
     "../api:array_view",
-    "third_party/base64",
+    "//third_party/abseil-cpp/absl/algorithm:container",
+    "//third_party/abseil-cpp/absl/strings",
     "//third_party/abseil-cpp/absl/strings:string_view",
   ]
 }
diff --git a/rtc_base/base64.cc b/rtc_base/base64.cc
index 03f6a08..eaa4d2b 100644
--- a/rtc_base/base64.cc
+++ b/rtc_base/base64.cc
@@ -10,42 +10,36 @@
 
 #include "rtc_base/base64.h"
 
-#include <cstdint>
 #include <optional>
 #include <string>
-#include <vector>
+#include <utility>
 
+#include "absl/algorithm/container.h"
+#include "absl/strings/ascii.h"
+#include "absl/strings/escaping.h"
 #include "absl/strings/string_view.h"
-#include "api/array_view.h"
-#include "rtc_base/third_party/base64/base64.h"
 
 namespace webrtc {
 
-std::string Base64Encode(ArrayView<const uint8_t> data) {
-  std::string result;
-  Base64::EncodeFromArray(data.data(), data.size(), &result);
-  return result;
+namespace {
+
+bool IsStrictBase64(absl::string_view data) {
+  // Strict base64 must be a multiple of 4 bytes and have no whitespace.
+  return data.size() % 4 == 0 && absl::c_none_of(data, absl::ascii_isspace);
 }
+}  // namespace
 
-std::optional<std::vector<uint8_t>> Base64Decode(absl::string_view data,
-                                                 Base64DecodeOptions options) {
-  Base64::DecodeFlags flags;
-  switch (options) {
-    case Base64DecodeOptions::kForgiving:
-      flags =
-          Base64::DO_PARSE_WHITE | Base64::DO_PAD_ANY | Base64::DO_TERM_BUFFER;
-      break;
-    case Base64DecodeOptions::kStrict:
-      flags = Base64::DO_STRICT;
-      break;
-  }
-
-  std::vector<uint8_t> result;
-  if (!Base64::DecodeFromArray(data.data(), data.size(), flags, &result,
-                               nullptr)) {
+std::optional<std::string> Base64Decode(absl::string_view data,
+                                        Base64DecodeOptions options) {
+  // absl::Base64Unescape is forgiving. Return nullopt if the input is not
+  // strict.
+  if (options == Base64DecodeOptions::kStrict && !IsStrictBase64(data)) {
     return std::nullopt;
   }
-  return result;
+
+  std::string dest;
+  return absl::Base64Unescape(data, &dest) ? std::make_optional(std::move(dest))
+                                           : std::nullopt;
 }
 
 }  // namespace webrtc
diff --git a/rtc_base/base64.h b/rtc_base/base64.h
index 070e317..24c1d97 100644
--- a/rtc_base/base64.h
+++ b/rtc_base/base64.h
@@ -14,25 +14,31 @@
 #include <cstdint>
 #include <optional>
 #include <string>
-#include <vector>
 
+#include "absl/strings/escaping.h"
 #include "absl/strings/string_view.h"
 #include "api/array_view.h"
 
 namespace webrtc {
 
-std::string Base64Encode(ArrayView<const uint8_t> data);
+inline std::string Base64Encode(ArrayView<const uint8_t> data) {
+  return absl::Base64Escape(absl::string_view(
+      reinterpret_cast<const char*>(data.data()), data.size()));
+}
+
+inline std::string Base64Encode(absl::string_view data) {
+  return absl::Base64Escape(data);
+}
 
 enum class Base64DecodeOptions {
   kStrict,
-
   // Matches https://infra.spec.whatwg.org/#forgiving-base64-decode.
   kForgiving,
 };
 
 // Returns the decoded data if successful, or std::nullopt if the decoding
 // failed.
-std::optional<std::vector<uint8_t>> Base64Decode(
+std::optional<std::string> Base64Decode(
     absl::string_view data,
     Base64DecodeOptions options = Base64DecodeOptions::kStrict);
 
diff --git a/rtc_base/base64_unittest.cc b/rtc_base/base64_unittest.cc
index 32d5a1a..51c90d5 100644
--- a/rtc_base/base64_unittest.cc
+++ b/rtc_base/base64_unittest.cc
@@ -10,33 +10,29 @@
 
 #include "rtc_base/base64.h"
 
-#include <cstdint>
 #include <optional>
 #include <string>
-#include <vector>
 
 #include "absl/strings/string_view.h"
-#include "api/array_view.h"
 #include "test/gmock.h"
 #include "test/gtest.h"
 
 namespace webrtc {
 namespace {
-using ::testing::ElementsAreArray;
+
 using ::testing::Eq;
 using ::testing::Optional;
 using ::testing::SizeIs;
 using ::testing::TestWithParam;
 
 TEST(Base64Test, Encode) {
-  uint8_t data[] = {0x64, 0x65, 0x66};
+  std::string data{0x64, 0x65, 0x66};
   EXPECT_THAT(Base64Encode(data), Eq("ZGVm"));
 }
 
 TEST(Base64Test, EncodeDecode) {
-  uint8_t data[] = {0x01, 0x02, 0x03, 0x04, 0x05};
-  EXPECT_THAT(Base64Decode(Base64Encode(data)),
-              Optional(ElementsAreArray(data)));
+  std::string data{0x01, 0x02, 0x03, 0x04, 0x05};
+  EXPECT_THAT(Base64Decode(Base64Encode(data)), Optional(Eq(data)));
 }
 
 TEST(Base64Test, DecodeCertificate) {
@@ -66,73 +62,79 @@
 struct Base64DecodeTestCase {
   std::string name;
   std::string data;
-  std::optional<std::vector<uint8_t>> result;
+  std::optional<std::string> result;
 };
 
 const Base64DecodeTestCase kBase64DecodeTestCases[] = {
     {"InvalidCharacters", "invalid;;;", std::nullopt},
     {"InvalidLength", "abcde", std::nullopt},
-    {"ValidInput", "abcd", std::vector<uint8_t>{0x69, 0xB7, 0x1D}},
-    {"ValidInputPadding", "abc=", std::vector<uint8_t>{0x69, 0xB7}},
-    {"EmptyInput", "", std::vector<uint8_t>{}},
+    {"ValidInput", "abcd", "i\xB7\x1D"},
+    {"ValidInputPadding", "abc=", "i\xB7"},
+    {"EmptyInput", "", ""},
 };
 
 using Base64DecodeTest = TestWithParam<Base64DecodeTestCase>;
 INSTANTIATE_TEST_SUITE_P(
     Base64DecodeTest,
     Base64DecodeTest,
-    ::testing::ValuesIn<Base64DecodeTestCase>(kBase64DecodeTestCases),
+    testing::ValuesIn<Base64DecodeTestCase>(kBase64DecodeTestCases),
     [](const auto& info) { return info.param.name; });
 
 TEST_P(Base64DecodeTest, TestDecodeStrict) {
   absl::string_view data = GetParam().data;
-  EXPECT_THAT(Base64Decode(data, Base64DecodeOptions::kForgiving),
-              Eq(GetParam().result));
   EXPECT_THAT(Base64Decode(data, Base64DecodeOptions::kStrict),
               Eq(GetParam().result));
 }
 
+TEST_P(Base64DecodeTest, TestDecodeForgiving) {
+  // Test default value is strict.
+  EXPECT_THAT(Base64Decode(GetParam().data), Eq(GetParam().result));
+}
+
 const Base64DecodeTestCase kBase64DecodeForgivingTestCases[] = {
     {
         "ForgivingPadding",
         "abc",
-        std::vector<uint8_t>{0x69, 0xB7},
+        "i\xB7",
     },
     {
         "WhitespaceForgivenTab",
         "ab\tcd",
-        std::vector<uint8_t>{0x69, 0xB7, 0x1D},
+        "i\xB7\x1D",
     },
     {
         "WhitespaceForgivenSpace",
         "a bc d",
-        std::vector<uint8_t>{0x69, 0xB7, 0x1D},
+        "i\xB7\x1D",
     },
     {
         "WhitespaceForgivenNewline",
         "a\nbc\nd",
-        std::vector<uint8_t>{0x69, 0xB7, 0x1D},
+        "i\xB7\x1D",
     },
     {
         "WhitespaceForgivenCarriageReturn",
         "a\r\nbc\rd",
-        std::vector<uint8_t>{0x69, 0xB7, 0x1D},
+        "i\xB7\x1D",
     },
-    {"WhitespaceForgivenLineFeed", "a\fbcd",
-     std::vector<uint8_t>{0x69, 0xB7, 0x1D}},
+    {"WhitespaceForgivenLineFeed", "a\fbcd", "i\xB7\x1D"},
 };
+
 using Base64DecodeForgivingTest = TestWithParam<Base64DecodeTestCase>;
 INSTANTIATE_TEST_SUITE_P(
     Base64DecodeTest,
     Base64DecodeForgivingTest,
-    ::testing::ValuesIn<Base64DecodeTestCase>(kBase64DecodeForgivingTestCases),
+    testing::ValuesIn<Base64DecodeTestCase>(kBase64DecodeForgivingTestCases),
     [](const auto& info) { return info.param.name; });
 
 TEST_P(Base64DecodeForgivingTest, TestDecodeForgiving) {
-  absl::string_view data = GetParam().data;
-  EXPECT_THAT(Base64Decode(data, Base64DecodeOptions::kForgiving),
+  EXPECT_THAT(Base64Decode(GetParam().data, Base64DecodeOptions::kForgiving),
               Eq(GetParam().result));
-  EXPECT_THAT(Base64Decode(data), Eq(std::nullopt));
+}
+
+TEST_P(Base64DecodeForgivingTest, TestDecodeStrictFails) {
+  // Test default value is strict.
+  EXPECT_THAT(Base64Decode(GetParam().data), Eq(std::nullopt));
 }
 
 }  // namespace
diff --git a/test/fuzzers/base64_encode_fuzzer.cc b/test/fuzzers/base64_encode_fuzzer.cc
index 51d329a..ccf81b2 100644
--- a/test/fuzzers/base64_encode_fuzzer.cc
+++ b/test/fuzzers/base64_encode_fuzzer.cc
@@ -11,7 +11,7 @@
 #include <cstdint>
 #include <cstring>
 #include <optional>
-#include <vector>
+#include <string>
 
 #include "api/array_view.h"
 #include "rtc_base/base64.h"
@@ -20,7 +20,7 @@
 namespace webrtc {
 
 void FuzzOneInput(const uint8_t* data, size_t size) {
-  std::optional<std::vector<uint8_t>> decoded_encoded_data =
+  std::optional<std::string> decoded_encoded_data =
       Base64Decode(Base64Encode(rtc::MakeArrayView(data, size)));
   RTC_CHECK(decoded_encoded_data.has_value());
   RTC_CHECK_EQ(std::memcmp(data, decoded_encoded_data->data(), size), 0);