xref: /aosp_15_r20/external/perfetto/src/base/base64.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "perfetto/ext/base/base64.h"
18 
19 namespace perfetto {
20 namespace base {
21 
22 namespace {
23 
24 constexpr char kPadding = '=';
25 
26 constexpr char kEncTable[] =
27     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
28 static_assert(sizeof(kEncTable) == (1u << 6) + sizeof('\0'), "Bad table size");
29 
30 // Maps an ASCII character to its 6-bit value. It only contains translations
31 // from '+' to 'z'. Supports the standard (+/) and URL-safe (-_) alphabets.
32 constexpr uint8_t kX = 0xff;  // Value used for invalid characters
33 constexpr uint8_t kDecTable[] = {
34     62, kX, 62, kX, 63, 52, 53, 54, 55, 56,  // 00 - 09
35     57, 58, 59, 60, 61, kX, kX, kX, 0,  kX,  // 10 - 19
36     kX, kX, 0,  1,  2,  3,  4,  5,  6,  7,   // 20 - 29
37     8,  9,  10, 11, 12, 13, 14, 15, 16, 17,  // 30 - 39
38     18, 19, 20, 21, 22, 23, 24, 25, kX, kX,  // 40 - 49
39     kX, kX, 63, kX, 26, 27, 28, 29, 30, 31,  // 50 - 59
40     32, 33, 34, 35, 36, 37, 38, 39, 40, 41,  // 60 - 69
41     42, 43, 44, 45, 46, 47, 48, 49, 50, 51,  // 70 - 79
42 };
43 constexpr char kMinDecChar = '+';
44 constexpr char kMaxDecChar = 'z';
45 static_assert(kMaxDecChar - kMinDecChar <= sizeof(kDecTable), "Bad table size");
46 
DecodeChar(char c)47 inline uint8_t DecodeChar(char c) {
48   if (c < kMinDecChar || c > kMaxDecChar)
49     return kX;
50   return kDecTable[c - kMinDecChar];
51 }
52 
53 }  // namespace
54 
Base64Encode(const void * src,size_t src_size,char * dst,size_t dst_size)55 ssize_t Base64Encode(const void* src,
56                      size_t src_size,
57                      char* dst,
58                      size_t dst_size) {
59   const size_t padded_dst_size = Base64EncSize(src_size);
60   if (dst_size < padded_dst_size)
61     return -1;  // Not enough space in output.
62 
63   const uint8_t* rd = static_cast<const uint8_t*>(src);
64   const uint8_t* const end = rd + src_size;
65   size_t wr_size = 0;
66   while (rd < end) {
67     uint8_t s[3]{};
68     s[0] = *(rd++);
69     dst[wr_size++] = kEncTable[s[0] >> 2];
70 
71     uint8_t carry0 = static_cast<uint8_t>((s[0] & 0x03) << 4);
72     if (PERFETTO_LIKELY(rd < end)) {
73       s[1] = *(rd++);
74       dst[wr_size++] = kEncTable[carry0 | (s[1] >> 4)];
75     } else {
76       dst[wr_size++] = kEncTable[carry0];
77       dst[wr_size++] = kPadding;
78       dst[wr_size++] = kPadding;
79       break;
80     }
81 
82     uint8_t carry1 = static_cast<uint8_t>((s[1] & 0x0f) << 2);
83     if (PERFETTO_LIKELY(rd < end)) {
84       s[2] = *(rd++);
85       dst[wr_size++] = kEncTable[carry1 | (s[2] >> 6)];
86     } else {
87       dst[wr_size++] = kEncTable[carry1];
88       dst[wr_size++] = kPadding;
89       break;
90     }
91 
92     dst[wr_size++] = kEncTable[s[2] & 0x3f];
93   }
94   PERFETTO_DCHECK(wr_size == padded_dst_size);
95   return static_cast<ssize_t>(padded_dst_size);
96 }
97 
Base64Encode(const void * src,size_t src_size)98 std::string Base64Encode(const void* src, size_t src_size) {
99   std::string dst;
100   dst.resize(Base64EncSize(src_size));
101   auto res = Base64Encode(src, src_size, &dst[0], dst.size());
102   PERFETTO_CHECK(res == static_cast<ssize_t>(dst.size()));
103   return dst;
104 }
105 
Base64Decode(const char * src,size_t src_size,uint8_t * dst,size_t dst_size)106 ssize_t Base64Decode(const char* src,
107                      size_t src_size,
108                      uint8_t* dst,
109                      size_t dst_size) {
110   const size_t min_dst_size = Base64DecSize(src_size);
111   if (dst_size < min_dst_size)
112     return -1;
113 
114   const char* rd = src;
115   const char* const end = src + src_size;
116   size_t wr_size = 0;
117 
118   char s[4]{};
119   while (rd < end) {
120     uint8_t d[4];
121     for (uint32_t j = 0; j < 4; j++) {
122       // Padding is only feasible for the last 2 chars of each group of 4.
123       s[j] = rd < end ? *(rd++) : (j < 2 ? '\0' : kPadding);
124       d[j] = DecodeChar(s[j]);
125       if (d[j] == kX)
126         return -1;  // Invalid input char.
127     }
128     dst[wr_size] = static_cast<uint8_t>((d[0] << 2) | (d[1] >> 4));
129     dst[wr_size + 1] = static_cast<uint8_t>((d[1] << 4) | (d[2] >> 2));
130     dst[wr_size + 2] = static_cast<uint8_t>((d[2] << 6) | (d[3]));
131     wr_size += 3;
132   }
133 
134   PERFETTO_CHECK(wr_size <= dst_size);
135   wr_size -= (s[3] == kPadding ? 1 : 0) + (s[2] == kPadding ? 1 : 0);
136   return static_cast<ssize_t>(wr_size);
137 }
138 
Base64Decode(const char * src,size_t src_size)139 std::optional<std::string> Base64Decode(const char* src, size_t src_size) {
140   std::string dst;
141   dst.resize(Base64DecSize(src_size));
142   auto res = Base64Decode(src, src_size, reinterpret_cast<uint8_t*>(&dst[0]),
143                           dst.size());
144   if (res < 0)
145     return std::nullopt;  // Decoding error.
146 
147   PERFETTO_CHECK(res <= static_cast<ssize_t>(dst.size()));
148   dst.resize(static_cast<size_t>(res));
149   return std::make_optional(dst);
150 }
151 
152 }  // namespace base
153 }  // namespace perfetto
154