base: add base64.cc
Add a dedicated base64 translation unit for both encoding
and decoding. Will be used by follow-up CLs.
Also refactor existing code currently using base64
from string_utils.cc.
Bug: 205274609
Change-Id: I8ce97dd09fc1707c9989c0f148bd972b805d4b49
diff --git a/Android.bp b/Android.bp
index f5484c8..ea02f7e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -6842,6 +6842,7 @@
name: "perfetto_src_base_base",
srcs: [
"src/base/android_utils.cc",
+ "src/base/base64.cc",
"src/base/crash_keys.cc",
"src/base/ctrl_c_handler.cc",
"src/base/event_fd.cc",
@@ -6888,6 +6889,7 @@
filegroup {
name: "perfetto_src_base_unittests",
srcs: [
+ "src/base/base64_unittest.cc",
"src/base/circular_queue_unittest.cc",
"src/base/flat_set_unittest.cc",
"src/base/getopt_compat_unittest.cc",
diff --git a/BUILD b/BUILD
index 6422ad6..c69e93d 100644
--- a/BUILD
+++ b/BUILD
@@ -335,6 +335,7 @@
name = "include_perfetto_ext_base_base",
srcs = [
"include/perfetto/ext/base/android_utils.h",
+ "include/perfetto/ext/base/base64.h",
"include/perfetto/ext/base/circular_queue.h",
"include/perfetto/ext/base/container_annotations.h",
"include/perfetto/ext/base/crash_keys.h",
@@ -640,6 +641,7 @@
name = "src_base_base",
srcs = [
"src/base/android_utils.cc",
+ "src/base/base64.cc",
"src/base/crash_keys.cc",
"src/base/ctrl_c_handler.cc",
"src/base/event_fd.cc",
diff --git a/include/perfetto/ext/base/BUILD.gn b/include/perfetto/ext/base/BUILD.gn
index f3b9113..34e09de 100644
--- a/include/perfetto/ext/base/BUILD.gn
+++ b/include/perfetto/ext/base/BUILD.gn
@@ -17,6 +17,7 @@
source_set("base") {
sources = [
"android_utils.h",
+ "base64.h",
"circular_queue.h",
"container_annotations.h",
"crash_keys.h",
diff --git a/include/perfetto/ext/base/base64.h b/include/perfetto/ext/base/base64.h
new file mode 100644
index 0000000..84e739c
--- /dev/null
+++ b/include/perfetto/ext/base/base64.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_EXT_BASE_BASE64_H_
+#define INCLUDE_PERFETTO_EXT_BASE_BASE64_H_
+
+#include <string>
+
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/string_view.h"
+
+namespace perfetto {
+namespace base {
+
+// Returns the length of the destination string (included '=' padding).
+// Does NOT include the size of the string null terminator.
+inline size_t Base64EncSize(size_t src_size) {
+ return (src_size + 2) / 3 * 4;
+}
+
+// Returns the upper bound on the length of the destination buffer.
+// The actual decoded length might be <= the number returned here.
+inline size_t Base64DecSize(size_t src_size) {
+ return (src_size + 3) / 4 * 3;
+}
+
+// Does NOT null-terminate |dst|.
+ssize_t Base64Encode(const void* src,
+ size_t src_size,
+ char* dst,
+ size_t dst_size);
+
+std::string Base64Encode(const void* src, size_t src_size);
+
+inline std::string Base64Encode(StringView sv) {
+ return Base64Encode(sv.data(), sv.size());
+}
+
+// Returns -1 in case of failure.
+ssize_t Base64Decode(const char* src,
+ size_t src_size,
+ uint8_t* dst,
+ size_t dst_size);
+
+Optional<std::string> Base64Decode(const char* src, size_t src_size);
+
+inline Optional<std::string> Base64Decode(StringView sv) {
+ return Base64Decode(sv.data(), sv.size());
+}
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_BASE64_H_
diff --git a/include/perfetto/ext/base/string_utils.h b/include/perfetto/ext/base/string_utils.h
index 2508aec..58444a8 100644
--- a/include/perfetto/ext/base/string_utils.h
+++ b/include/perfetto/ext/base/string_utils.h
@@ -125,7 +125,6 @@
const std::string& to_replace,
const std::string& replacement);
std::string TrimLeading(const std::string& str);
-std::string Base64Encode(const void* raw, size_t size);
// A BSD-style strlcpy without the return value.
// Copies at most |dst_size|-1 characters. Unlike strncpy, it always \0
diff --git a/src/base/BUILD.gn b/src/base/BUILD.gn
index 156c98b..f6d86b0 100644
--- a/src/base/BUILD.gn
+++ b/src/base/BUILD.gn
@@ -32,6 +32,7 @@
]
sources = [
"android_utils.cc",
+ "base64.cc",
"crash_keys.cc",
"ctrl_c_handler.cc",
"event_fd.cc",
@@ -159,6 +160,7 @@
]
sources = [
+ "base64_unittest.cc",
"circular_queue_unittest.cc",
"flat_set_unittest.cc",
"getopt_compat_unittest.cc",
diff --git a/src/base/base64.cc b/src/base/base64.cc
new file mode 100644
index 0000000..437ad67
--- /dev/null
+++ b/src/base/base64.cc
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/ext/base/base64.h"
+
+namespace perfetto {
+namespace base {
+
+namespace {
+
+constexpr char kPadding = '=';
+
+constexpr char kEncTable[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static_assert(sizeof(kEncTable) == (1u << 6) + sizeof('\0'), "Bad table size");
+
+// Maps an ASCII character to its 6-bit value. It only contains translations
+// from '+' to 'z'. Supports the standard (+/) and URL-safe (-_) alphabets.
+constexpr uint8_t kX = 0xff; // Value used for invalid characters
+constexpr uint8_t kDecTable[] = {
+ 62, kX, 62, kX, 63, 52, 53, 54, 55, 56, // 00 - 09
+ 57, 58, 59, 60, 61, kX, kX, kX, 0, kX, // 10 - 19
+ kX, kX, 0, 1, 2, 3, 4, 5, 6, 7, // 20 - 29
+ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, // 30 - 39
+ 18, 19, 20, 21, 22, 23, 24, 25, kX, kX, // 40 - 49
+ kX, kX, 63, kX, 26, 27, 28, 29, 30, 31, // 50 - 59
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, // 60 - 69
+ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // 70 - 79
+};
+constexpr char kMinDecChar = '+';
+constexpr char kMaxDecChar = 'z';
+static_assert(kMaxDecChar - kMinDecChar <= sizeof(kDecTable), "Bad table size");
+
+inline uint8_t DecodeChar(char c) {
+ if (c < kMinDecChar || c > kMaxDecChar)
+ return kX;
+ return kDecTable[c - kMinDecChar];
+}
+
+} // namespace
+
+ssize_t Base64Encode(const void* src,
+ size_t src_size,
+ char* dst,
+ size_t dst_size) {
+ const size_t padded_dst_size = Base64EncSize(src_size);
+ if (dst_size < padded_dst_size)
+ return -1; // Not enough space in output.
+
+ const uint8_t* rd = static_cast<const uint8_t*>(src);
+ const uint8_t* const end = rd + src_size;
+ size_t wr_size = 0;
+ while (rd < end) {
+ uint8_t s[3]{};
+ s[0] = *(rd++);
+ dst[wr_size++] = kEncTable[s[0] >> 2];
+
+ uint8_t carry0 = static_cast<uint8_t>((s[0] & 0x03) << 4);
+ if (PERFETTO_LIKELY(rd < end)) {
+ s[1] = *(rd++);
+ dst[wr_size++] = kEncTable[carry0 | (s[1] >> 4)];
+ } else {
+ dst[wr_size++] = kEncTable[carry0];
+ dst[wr_size++] = kPadding;
+ dst[wr_size++] = kPadding;
+ break;
+ }
+
+ uint8_t carry1 = static_cast<uint8_t>((s[1] & 0x0f) << 2);
+ if (PERFETTO_LIKELY(rd < end)) {
+ s[2] = *(rd++);
+ dst[wr_size++] = kEncTable[carry1 | (s[2] >> 6)];
+ } else {
+ dst[wr_size++] = kEncTable[carry1];
+ dst[wr_size++] = kPadding;
+ break;
+ }
+
+ dst[wr_size++] = kEncTable[s[2] & 0x3f];
+ }
+ PERFETTO_DCHECK(wr_size == padded_dst_size);
+ return static_cast<ssize_t>(padded_dst_size);
+}
+
+std::string Base64Encode(const void* src, size_t src_size) {
+ std::string dst;
+ dst.resize(Base64EncSize(src_size));
+ auto res = Base64Encode(src, src_size, &dst[0], dst.size());
+ PERFETTO_CHECK(res == static_cast<ssize_t>(dst.size()));
+ return dst;
+}
+
+ssize_t Base64Decode(const char* src,
+ size_t src_size,
+ uint8_t* dst,
+ size_t dst_size) {
+ const size_t min_dst_size = Base64DecSize(src_size);
+ if (dst_size < min_dst_size)
+ return -1;
+
+ const char* rd = src;
+ const char* const end = src + src_size;
+ size_t wr_size = 0;
+
+ char s[4]{};
+ while (rd < end) {
+ uint8_t d[4];
+ for (uint32_t j = 0; j < 4; j++) {
+ // Padding is only feasible for the last 2 chars of each group of 4.
+ s[j] = rd < end ? *(rd++) : (j < 2 ? '\0' : kPadding);
+ d[j] = DecodeChar(s[j]);
+ if (d[j] == kX)
+ return -1; // Invalid input char.
+ }
+ dst[wr_size] = static_cast<uint8_t>((d[0] << 2) | (d[1] >> 4));
+ dst[wr_size + 1] = static_cast<uint8_t>((d[1] << 4) | (d[2] >> 2));
+ dst[wr_size + 2] = static_cast<uint8_t>((d[2] << 6) | (d[3]));
+ wr_size += 3;
+ }
+
+ PERFETTO_CHECK(wr_size <= dst_size);
+ wr_size -= (s[3] == kPadding ? 1 : 0) + (s[2] == kPadding ? 1 : 0);
+ return static_cast<ssize_t>(wr_size);
+}
+
+Optional<std::string> Base64Decode(const char* src, size_t src_size) {
+ std::string dst;
+ dst.resize(Base64DecSize(src_size));
+ auto res = Base64Decode(src, src_size, reinterpret_cast<uint8_t*>(&dst[0]),
+ dst.size());
+ if (res < 0)
+ return nullopt; // Decoding error.
+
+ PERFETTO_CHECK(res <= static_cast<ssize_t>(dst.size()));
+ dst.resize(static_cast<size_t>(res));
+ return make_optional(dst);
+}
+
+} // namespace base
+} // namespace perfetto
diff --git a/src/base/base64_unittest.cc b/src/base/base64_unittest.cc
new file mode 100644
index 0000000..fb131d9
--- /dev/null
+++ b/src/base/base64_unittest.cc
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/ext/base/base64.h"
+
+#include "perfetto/ext/base/string_view.h"
+#include "perfetto/ext/base/utils.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace base {
+namespace {
+
+struct TestPattern {
+ size_t decoded_len;
+ const char* decoded;
+ const char* encoded;
+};
+
+TestPattern kPatterns[] = {
+
+ // Basic bit patterns;
+ // values obtained with "echo -n '...' | uuencode -m test"
+
+ {1, "\000", "AA=="},
+ {1, "\001", "AQ=="},
+ {1, "\002", "Ag=="},
+ {1, "\004", "BA=="},
+ {1, "\010", "CA=="},
+ {1, "\020", "EA=="},
+ {1, "\040", "IA=="},
+ {1, "\100", "QA=="},
+ {1, "\200", "gA=="},
+
+ {1, "\377", "/w=="},
+ {1, "\376", "/g=="},
+ {1, "\375", "/Q=="},
+ {1, "\373", "+w=="},
+ {1, "\367", "9w=="},
+ {1, "\357", "7w=="},
+ {1, "\337", "3w=="},
+ {1, "\277", "vw=="},
+ {1, "\177", "fw=="},
+ {2, "\000\000", "AAA="},
+ {2, "\000\001", "AAE="},
+ {2, "\000\002", "AAI="},
+ {2, "\000\004", "AAQ="},
+ {2, "\000\010", "AAg="},
+ {2, "\000\020", "ABA="},
+ {2, "\000\040", "ACA="},
+ {2, "\000\100", "AEA="},
+ {2, "\000\200", "AIA="},
+ {2, "\001\000", "AQA="},
+ {2, "\002\000", "AgA="},
+ {2, "\004\000", "BAA="},
+ {2, "\010\000", "CAA="},
+ {2, "\020\000", "EAA="},
+ {2, "\040\000", "IAA="},
+ {2, "\100\000", "QAA="},
+ {2, "\200\000", "gAA="},
+
+ {2, "\377\377", "//8="},
+ {2, "\377\376", "//4="},
+ {2, "\377\375", "//0="},
+ {2, "\377\373", "//s="},
+ {2, "\377\367", "//c="},
+ {2, "\377\357", "/+8="},
+ {2, "\377\337", "/98="},
+ {2, "\377\277", "/78="},
+ {2, "\377\177", "/38="},
+ {2, "\376\377", "/v8="},
+ {2, "\375\377", "/f8="},
+ {2, "\373\377", "+/8="},
+ {2, "\367\377", "9/8="},
+ {2, "\357\377", "7/8="},
+ {2, "\337\377", "3/8="},
+ {2, "\277\377", "v/8="},
+ {2, "\177\377", "f/8="},
+
+ {3, "\000\000\000", "AAAA"},
+ {3, "\000\000\001", "AAAB"},
+ {3, "\000\000\002", "AAAC"},
+ {3, "\000\000\004", "AAAE"},
+ {3, "\000\000\010", "AAAI"},
+ {3, "\000\000\020", "AAAQ"},
+ {3, "\000\000\040", "AAAg"},
+ {3, "\000\000\100", "AABA"},
+ {3, "\000\000\200", "AACA"},
+ {3, "\000\001\000", "AAEA"},
+ {3, "\000\002\000", "AAIA"},
+ {3, "\000\004\000", "AAQA"},
+ {3, "\000\010\000", "AAgA"},
+ {3, "\000\020\000", "ABAA"},
+ {3, "\000\040\000", "ACAA"},
+ {3, "\000\100\000", "AEAA"},
+ {3, "\000\200\000", "AIAA"},
+ {3, "\001\000\000", "AQAA"},
+ {3, "\002\000\000", "AgAA"},
+ {3, "\004\000\000", "BAAA"},
+ {3, "\010\000\000", "CAAA"},
+ {3, "\020\000\000", "EAAA"},
+ {3, "\040\000\000", "IAAA"},
+ {3, "\100\000\000", "QAAA"},
+ {3, "\200\000\000", "gAAA"},
+
+ {3, "\377\377\377", "////"},
+ {3, "\377\377\376", "///+"},
+ {3, "\377\377\375", "///9"},
+ {3, "\377\377\373", "///7"},
+ {3, "\377\377\367", "///3"},
+ {3, "\377\377\357", "///v"},
+ {3, "\377\377\337", "///f"},
+ {3, "\377\377\277", "//+/"},
+ {3, "\377\377\177", "//9/"},
+ {3, "\377\376\377", "//7/"},
+ {3, "\377\375\377", "//3/"},
+ {3, "\377\373\377", "//v/"},
+ {3, "\377\367\377", "//f/"},
+ {3, "\377\357\377", "/+//"},
+ {3, "\377\337\377", "/9//"},
+ {3, "\377\277\377", "/7//"},
+ {3, "\377\177\377", "/3//"},
+ {3, "\376\377\377", "/v//"},
+ {3, "\375\377\377", "/f//"},
+ {3, "\373\377\377", "+///"},
+ {3, "\367\377\377", "9///"},
+ {3, "\357\377\377", "7///"},
+ {3, "\337\377\377", "3///"},
+ {3, "\277\377\377", "v///"},
+ {3, "\177\377\377", "f///"},
+
+ // Random numbers: values obtained with
+ //
+ // #! /bin/bash
+ // dd bs=$1 count=1 if=/dev/random of=/tmp/bar.random
+ // od -N $1 -t o1 /tmp/bar.random
+ // uuencode -m test < /tmp/bar.random
+ //
+ // where $1 is the number of bytes (2, 3)
+
+ {2, "\243\361", "o/E="},
+ {2, "\024\167", "FHc="},
+ {2, "\313\252", "y6o="},
+ {2, "\046\041", "JiE="},
+ {2, "\145\236", "ZZ4="},
+ {2, "\254\325", "rNU="},
+ {2, "\061\330", "Mdg="},
+ {2, "\245\032", "pRo="},
+ {2, "\006\000", "BgA="},
+ {2, "\375\131", "/Vk="},
+ {2, "\303\210", "w4g="},
+ {2, "\040\037", "IB8="},
+ {2, "\261\372", "sfo="},
+ {2, "\335\014", "3Qw="},
+ {2, "\233\217", "m48="},
+ {2, "\373\056", "+y4="},
+ {2, "\247\232", "p5o="},
+ {2, "\107\053", "Rys="},
+ {2, "\204\077", "hD8="},
+ {2, "\276\211", "vok="},
+ {2, "\313\110", "y0g="},
+ {2, "\363\376", "8/4="},
+ {2, "\251\234", "qZw="},
+ {2, "\103\262", "Q7I="},
+ {2, "\142\312", "Yso="},
+ {2, "\067\211", "N4k="},
+ {2, "\220\001", "kAE="},
+ {2, "\152\240", "aqA="},
+ {2, "\367\061", "9zE="},
+ {2, "\133\255", "W60="},
+ {2, "\176\035", "fh0="},
+ {2, "\032\231", "Gpk="},
+
+ {3, "\013\007\144", "Cwdk"},
+ {3, "\030\112\106", "GEpG"},
+ {3, "\047\325\046", "J9Um"},
+ {3, "\310\160\022", "yHAS"},
+ {3, "\131\100\237", "WUCf"},
+ {3, "\064\342\134", "NOJc"},
+ {3, "\010\177\004", "CH8E"},
+ {3, "\345\147\205", "5WeF"},
+ {3, "\300\343\360", "wOPw"},
+ {3, "\061\240\201", "MaCB"},
+ {3, "\225\333\044", "ldsk"},
+ {3, "\215\137\352", "jV/q"},
+ {3, "\371\147\160", "+Wdw"},
+ {3, "\030\320\051", "GNAp"},
+ {3, "\044\174\241", "JHyh"},
+ {3, "\260\127\037", "sFcf"},
+ {3, "\111\045\033", "SSUb"},
+ {3, "\202\114\107", "gkxH"},
+ {3, "\057\371\042", "L/ki"},
+ {3, "\223\247\244", "k6ek"},
+ {3, "\047\216\144", "J45k"},
+ {3, "\203\070\327", "gzjX"},
+ {3, "\247\140\072", "p2A6"},
+ {3, "\124\115\116", "VE1O"},
+ {3, "\157\162\050", "b3Io"},
+ {3, "\357\223\004", "75ME"},
+ {3, "\052\117\156", "Kk9u"},
+ {3, "\347\154\000", "52wA"},
+ {3, "\303\012\142", "wwpi"},
+ {3, "\060\035\362", "MB3y"},
+ {3, "\130\226\361", "WJbx"},
+ {3, "\173\013\071", "ews5"},
+ {3, "\336\004\027", "3gQX"},
+ {3, "\357\366\234", "7/ac"},
+ {3, "\353\304\111", "68RJ"},
+ {3, "\024\264\131", "FLRZ"},
+ {3, "\075\114\251", "PUyp"},
+ {3, "\315\031\225", "zRmV"},
+ {3, "\154\201\276", "bIG+"},
+ {3, "\200\066\072", "gDY6"},
+ {3, "\142\350\267", "Yui3"},
+ {3, "\033\000\166", "GwB2"},
+ {3, "\210\055\077", "iC0/"},
+ {3, "\341\037\124", "4R9U"},
+ {3, "\161\103\152", "cUNq"},
+ {3, "\270\142\131", "uGJZ"},
+ {3, "\337\076\074", "3z48"},
+ {3, "\375\106\362", "/Uby"},
+ {3, "\227\301\127", "l8FX"},
+ {3, "\340\002\234", "4AKc"},
+ {3, "\121\064\033", "UTQb"},
+ {3, "\157\134\143", "b1xj"},
+ {3, "\247\055\327", "py3X"},
+ {3, "\340\142\005", "4GIF"},
+ {3, "\060\260\143", "MLBj"},
+ {3, "\075\203\170", "PYN4"},
+ {3, "\143\160\016", "Y3AO"},
+ {3, "\313\013\063", "ywsz"},
+ {3, "\174\236\135", "fJ5d"},
+ {3, "\103\047\026", "QycW"},
+ {3, "\365\005\343", "9QXj"},
+ {3, "\271\160\223", "uXCT"},
+ {3, "\362\255\172", "8q16"},
+ {3, "\113\012\015", "SwoN"},
+
+ // various lengths, generated by this python script:
+ //
+ // from string import lowercase as lc
+ // for i in range(27):
+ // print '{ %2d, "%s",%s "%s" },' % (i, lc[:i], ' ' * (26-i),
+ // lc[:i].encode('base64').strip())
+
+ {0, "abcdefghijklmnopqrstuvwxyz", ""},
+ {1, "abcdefghijklmnopqrstuvwxyz", "YQ=="},
+ {2, "abcdefghijklmnopqrstuvwxyz", "YWI="},
+ {3, "abcdefghijklmnopqrstuvwxyz", "YWJj"},
+ {4, "abcdefghijklmnopqrstuvwxyz", "YWJjZA=="},
+ {5, "abcdefghijklmnopqrstuvwxyz", "YWJjZGU="},
+ {6, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVm"},
+ {7, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZw=="},
+ {8, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2g="},
+ {9, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hp"},
+ {10, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpag=="},
+ {11, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpams="},
+ {12, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamts"},
+ {13, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbQ=="},
+ {14, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW4="},
+ {15, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5v"},
+ {16, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcA=="},
+ {17, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHE="},
+ {18, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFy"},
+ {19, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFycw=="},
+ {20, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3Q="},
+ {21, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1"},
+ {22, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dg=="},
+ {23, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnc="},
+ {24, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4"},
+ {25, "abcdefghijklmnopqrstuvwxy", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eQ=="},
+ {26, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo="},
+};
+
+TEST(Base64Test, Encode) {
+ EXPECT_EQ(Base64Encode(""), "");
+ EXPECT_EQ(Base64Encode("f"), "Zg==");
+ EXPECT_EQ(Base64Encode("fo"), "Zm8=");
+ EXPECT_EQ(Base64Encode("foo"), "Zm9v");
+ EXPECT_EQ(Base64Encode("foob"), "Zm9vYg==");
+ EXPECT_EQ(Base64Encode("fooba"), "Zm9vYmE=");
+ EXPECT_EQ(Base64Encode("foobar"), "Zm9vYmFy");
+ EXPECT_EQ(Base64Encode("\xff"), "/w==");
+ EXPECT_EQ(Base64Encode("\xff\xfe"), "//4=");
+ EXPECT_EQ(Base64Encode("\xff\xfe\xfd"), "//79");
+ EXPECT_EQ(Base64Encode("\xff\xfe\xfd\xfc"), "//79/A==");
+
+ for (size_t i = 0; i < ArraySize(kPatterns); ++i) {
+ const auto& p = kPatterns[i];
+ std::string res = Base64Encode(StringView(p.decoded, p.decoded_len));
+ EXPECT_EQ(p.encoded, res);
+ }
+
+ // Error cases
+ char buf[4];
+ EXPECT_EQ(0, Base64Encode("", 0, buf, 0));
+ EXPECT_EQ(0, Base64Encode("", 0, buf, 1));
+ EXPECT_EQ(-1, Base64Encode("a", 1, buf, 0));
+ EXPECT_EQ(-1, Base64Encode("abc", 3, buf, 0));
+ EXPECT_EQ(-1, Base64Encode("abc", 3, buf, 1));
+ EXPECT_EQ(-1, Base64Encode("abc", 3, buf, 3));
+ EXPECT_EQ(4, Base64Encode("abc", 3, buf, 4));
+}
+
+TEST(Base64Test, Decode) {
+ EXPECT_EQ(Base64Decode(""), "");
+ EXPECT_EQ(Base64Decode("Zg=="), "f");
+ EXPECT_EQ(Base64Decode("Zg="), "f");
+ EXPECT_EQ(Base64Decode("Zg"), "f");
+ EXPECT_EQ(Base64Decode("Zm8="), "fo");
+ EXPECT_EQ(Base64Decode("Zm8"), "fo");
+ EXPECT_EQ(Base64Decode("Zm9v"), "foo");
+ EXPECT_EQ(Base64Decode("Zm9vYg=="), "foob");
+ EXPECT_EQ(Base64Decode("Zm9vYg="), "foob");
+ EXPECT_EQ(Base64Decode("Zm9vYg"), "foob");
+ EXPECT_EQ(Base64Decode("Zm9vYmE="), "fooba");
+ EXPECT_EQ(Base64Decode("Zm9vYmE"), "fooba");
+ EXPECT_EQ(Base64Decode("Zm9vYmFy"), "foobar");
+ EXPECT_EQ(Base64Decode("/w=="), "\xff");
+ EXPECT_EQ(Base64Decode("/w="), "\xff");
+ EXPECT_EQ(Base64Decode("/w"), "\xff");
+ EXPECT_EQ(Base64Decode("//4="), "\xff\xfe");
+ EXPECT_EQ(Base64Decode("//4"), "\xff\xfe");
+ EXPECT_EQ(Base64Decode("//79"), "\xff\xfe\xfd");
+ EXPECT_EQ(Base64Decode("//79/A=="), "\xff\xfe\xfd\xfc");
+ EXPECT_EQ(Base64Decode("//79/A="), "\xff\xfe\xfd\xfc");
+ EXPECT_EQ(Base64Decode("//79/A"), "\xff\xfe\xfd\xfc");
+
+ for (size_t i = 0; i < ArraySize(kPatterns); ++i) {
+ const auto& p = kPatterns[i];
+ Optional<std::string> dec = Base64Decode(StringView(p.encoded));
+ EXPECT_TRUE(dec.has_value());
+ EXPECT_EQ(dec.value(), StringView(p.decoded, p.decoded_len).ToStdString());
+ }
+
+ // Error cases:
+ EXPECT_EQ(Base64Decode("Z"), nullopt);
+ EXPECT_EQ(Base64Decode("Zm9vY"), nullopt);
+
+ uint8_t buf[4];
+ EXPECT_EQ(Base64Decode("", 0, buf, 2), 0); // Valid, 0 len.
+ EXPECT_EQ(Base64Decode("Z", 1, buf, 1), -1); // Invalid input.
+ EXPECT_EQ(Base64Decode("Zg==", 4, buf, 1), -1); // Not enough dst space.
+}
+
+} // namespace
+} // namespace base
+} // namespace perfetto
diff --git a/src/base/string_utils.cc b/src/base/string_utils.cc
index af663b8..5efca77 100644
--- a/src/base/string_utils.cc
+++ b/src/base/string_utils.cc
@@ -33,11 +33,6 @@
namespace perfetto {
namespace base {
-namespace {
-constexpr char kBase64Table[] =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "abcdefghijklmnopqrstuvwxyz0123456789+/";
-}
// Locale-independant as possible version of strtod.
double StrToD(const char* nptr, char** endptr) {
@@ -215,44 +210,6 @@
return idx == std::string::npos ? str : str.substr(idx);
}
-std::string Base64Encode(const void* raw, size_t size) {
- // The following three cases are based on the tables in the example
- // section in https://en.wikipedia.org/wiki/Base64. We process three
- // input bytes at a time, emitting 4 output bytes at a time.
- const uint8_t* ptr = static_cast<const uint8_t*>(raw);
- size_t ii = 0;
-
- std::string out;
- out.reserve((size + 2) * 4 / 3);
-
- // While possible, process three input bytes.
- for (; ii + 3 <= size; ii += 3) {
- uint32_t twentyfour_bits =
- (uint32_t(ptr[ii]) << 16) | (uint32_t(ptr[ii + 1]) << 8) | ptr[ii + 2];
- out.push_back(kBase64Table[(twentyfour_bits >> 18)]);
- out.push_back(kBase64Table[(twentyfour_bits >> 12) & 0x3f]);
- out.push_back(kBase64Table[(twentyfour_bits >> 6) & 0x3f]);
- out.push_back(kBase64Table[twentyfour_bits & 0x3f]);
- }
- if (ii + 2 <= size) { // Process two input bytes.
- uint32_t twentyfour_bits =
- (uint32_t(ptr[ii]) << 16) | (uint32_t(ptr[ii + 1]) << 8);
- out.push_back(kBase64Table[(twentyfour_bits >> 18)]);
- out.push_back(kBase64Table[(twentyfour_bits >> 12) & 0x3f]);
- out.push_back(kBase64Table[(twentyfour_bits >> 6) & 0x3f]);
- out.push_back('='); // Emit padding.
- return out;
- }
- if (ii + 1 <= size) { // Process a single input byte.
- uint32_t twentyfour_bits = (uint32_t(ptr[ii]) << 16);
- out.push_back(kBase64Table[(twentyfour_bits >> 18)]);
- out.push_back(kBase64Table[(twentyfour_bits >> 12) & 0x3f]);
- out.push_back('='); // Emit padding.
- out.push_back('='); // Emit padding.
- }
- return out;
-}
-
size_t SprintfTrunc(char* dst, size_t dst_size, const char* fmt, ...) {
if (PERFETTO_UNLIKELY(dst_size) == 0)
return 0;
diff --git a/src/base/string_utils_unittest.cc b/src/base/string_utils_unittest.cc
index 0787802..39803ff 100644
--- a/src/base/string_utils_unittest.cc
+++ b/src/base/string_utils_unittest.cc
@@ -303,29 +303,6 @@
EXPECT_EQ(TrimLeading(" aaaaa "), "aaaaa ");
}
-TEST(StringUtilsTest, Base64Encode) {
- auto base64_encode = [](const std::string& str) {
- return Base64Encode(str.c_str(), str.size());
- };
-
- EXPECT_EQ(base64_encode(""), "");
- EXPECT_EQ(base64_encode("f"), "Zg==");
- EXPECT_EQ(base64_encode("fo"), "Zm8=");
- EXPECT_EQ(base64_encode("foo"), "Zm9v");
- EXPECT_EQ(base64_encode("foob"), "Zm9vYg==");
- EXPECT_EQ(base64_encode("fooba"), "Zm9vYmE=");
- EXPECT_EQ(base64_encode("foobar"), "Zm9vYmFy");
-
- EXPECT_EQ(Base64Encode("foo\0bar", 7), "Zm9vAGJhcg==");
-
- std::vector<uint8_t> buffer = {0x04, 0x53, 0x42, 0x35,
- 0x32, 0xFF, 0x00, 0xFE};
- EXPECT_EQ(Base64Encode(buffer.data(), buffer.size()), "BFNCNTL/AP4=");
-
- buffer = {0xfb, 0xf0, 0x3e, 0x07, 0xfc};
- EXPECT_EQ(Base64Encode(buffer.data(), buffer.size()), "+/A+B/w=");
-}
-
TEST(StringUtilsTest, StringCopy) {
// Nothing should be written when |dst_size| = 0.
{
diff --git a/src/trace_processor/importers/proto/metadata_module.cc b/src/trace_processor/importers/proto/metadata_module.cc
index b811320..cfb644b 100644
--- a/src/trace_processor/importers/proto/metadata_module.cc
+++ b/src/trace_processor/importers/proto/metadata_module.cc
@@ -16,7 +16,7 @@
#include "src/trace_processor/importers/proto/metadata_module.h"
-#include "perfetto/ext/base/string_utils.h"
+#include "perfetto/ext/base/base64.h"
#include "src/trace_processor/importers/common/slice_tracker.h"
#include "src/trace_processor/importers/common/track_tracker.h"
#include "src/trace_processor/importers/proto/metadata_tracker.h"