1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "db_base64_utils.h"
17 
18 #include <algorithm>
19 #include <array>
20 
21 namespace DistributedDB {
22 static constexpr const std::string_view BASE64_CHARS = /* NOLINT */
23     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
24     "abcdefghijklmnopqrstuvwxyz"
25     "0123456789+/";
26 
27 static constexpr const uint32_t CHAR_ARRAY_LENGTH_THREE = 3;
28 static constexpr const uint32_t CHAR_ARRAY_LENGTH_FOUR = 4;
29 
30 enum class BASE64_ENCODE_CONSTANT : uint8_t {
31     BASE64_ENCODE_MASK1 = 0xfc,
32     BASE64_ENCODE_MASK2 = 0x03,
33     BASE64_ENCODE_MASK3 = 0x0f,
34     BASE64_ENCODE_MASK4 = 0x3f,
35     BASE64_ENCODE_MASK5 = 0xf0,
36     BASE64_ENCODE_MASK6 = 0xc0,
37     BASE64_ENCODE_OFFSET2 = 2,
38     BASE64_ENCODE_OFFSET4 = 4,
39     BASE64_ENCODE_OFFSET6 = 6,
40     BASE64_ENCODE_INDEX0 = 0,
41     BASE64_ENCODE_INDEX1 = 1,
42     BASE64_ENCODE_INDEX2 = 2,
43 };
44 
45 enum class BASE64_DECODE_CONSTANT : uint8_t {
46     BASE64_DECODE_MASK1 = 0x30,
47     BASE64_DECODE_MASK2 = 0xf,
48     BASE64_DECODE_MASK3 = 0x3c,
49     BASE64_DECODE_MASK4 = 0x3,
50     BASE64_DECODE_OFFSET2 = 2,
51     BASE64_DECODE_OFFSET4 = 4,
52     BASE64_DECODE_OFFSET6 = 6,
53     BASE64_DECODE_INDEX0 = 0,
54     BASE64_DECODE_INDEX1 = 1,
55     BASE64_DECODE_INDEX2 = 2,
56     BASE64_DECODE_INDEX3 = 3,
57 };
58 
IsBase64Char(const char c)59 static inline bool IsBase64Char(const char c)
60 {
61     return (isalnum(c) || (c == '+') || (c == '/'));
62 }
63 
MakeCharFour(const std::array<uint8_t,CHAR_ARRAY_LENGTH_THREE> & charArrayThree,std::array<uint8_t,CHAR_ARRAY_LENGTH_FOUR> & charArrayFour)64 static void MakeCharFour(const std::array<uint8_t, CHAR_ARRAY_LENGTH_THREE> &charArrayThree,
65     std::array<uint8_t, CHAR_ARRAY_LENGTH_FOUR> &charArrayFour)
66 {
67     const uint8_t table[CHAR_ARRAY_LENGTH_FOUR] = {
68         static_cast<uint8_t>((charArrayThree[static_cast<uint8_t>(BASE64_ENCODE_CONSTANT::BASE64_ENCODE_INDEX0)] &
69                                 static_cast<uint8_t>(BASE64_ENCODE_CONSTANT::BASE64_ENCODE_MASK1)) >>
70                             static_cast<uint8_t>(BASE64_ENCODE_CONSTANT::BASE64_ENCODE_OFFSET2)),
71         static_cast<uint8_t>(((charArrayThree[static_cast<uint8_t>(BASE64_ENCODE_CONSTANT::BASE64_ENCODE_INDEX0)] &
72                                   static_cast<uint8_t>(BASE64_ENCODE_CONSTANT::BASE64_ENCODE_MASK2))
73                                 << static_cast<uint8_t>(BASE64_ENCODE_CONSTANT::BASE64_ENCODE_OFFSET4)) +
74                                 ((charArrayThree[static_cast<uint8_t>(BASE64_ENCODE_CONSTANT::BASE64_ENCODE_INDEX1)] &
75                                     static_cast<uint8_t>(BASE64_ENCODE_CONSTANT::BASE64_ENCODE_MASK5)) >>
76                                   static_cast<uint8_t>(BASE64_ENCODE_CONSTANT::BASE64_ENCODE_OFFSET4))),
77         static_cast<uint8_t>(((charArrayThree[static_cast<uint8_t>(BASE64_ENCODE_CONSTANT::BASE64_ENCODE_INDEX1)] &
78                                   static_cast<uint8_t>(BASE64_ENCODE_CONSTANT::BASE64_ENCODE_MASK3))
79                                 << static_cast<uint8_t>(BASE64_ENCODE_CONSTANT::BASE64_ENCODE_OFFSET2)) +
80                              ((charArrayThree[static_cast<uint8_t>(BASE64_ENCODE_CONSTANT::BASE64_ENCODE_INDEX2)] &
81                                   static_cast<uint8_t>(BASE64_ENCODE_CONSTANT::BASE64_ENCODE_MASK6)) >>
82                                 static_cast<uint8_t>(BASE64_ENCODE_CONSTANT::BASE64_ENCODE_OFFSET6))),
83         static_cast<uint8_t>(charArrayThree[static_cast<uint8_t>(BASE64_ENCODE_CONSTANT::BASE64_ENCODE_INDEX2)] &
84                             static_cast<uint8_t>(BASE64_ENCODE_CONSTANT::BASE64_ENCODE_MASK4)),
85     };
86     for (size_t index = 0; index < CHAR_ARRAY_LENGTH_FOUR; ++index) {
87         charArrayFour[index] = table[index];
88     }
89 }
90 
MakeCharTree(const std::array<uint8_t,CHAR_ARRAY_LENGTH_FOUR> & charArrayFour,std::array<uint8_t,CHAR_ARRAY_LENGTH_THREE> & charArrayThree)91 static void MakeCharTree(const std::array<uint8_t, CHAR_ARRAY_LENGTH_FOUR> &charArrayFour,
92     std::array<uint8_t, CHAR_ARRAY_LENGTH_THREE> &charArrayThree)
93 {
94     const uint8_t table[CHAR_ARRAY_LENGTH_THREE] = {
95         static_cast<uint8_t>((charArrayFour[static_cast<uint8_t>(BASE64_DECODE_CONSTANT::BASE64_DECODE_INDEX0)]
96                                 << static_cast<uint8_t>(BASE64_DECODE_CONSTANT::BASE64_DECODE_OFFSET2)) +
97                              ((charArrayFour[static_cast<uint8_t>(BASE64_DECODE_CONSTANT::BASE64_DECODE_INDEX1)] &
98                                   static_cast<uint8_t>(BASE64_DECODE_CONSTANT::BASE64_DECODE_MASK1)) >>
99                                 static_cast<uint8_t>(BASE64_DECODE_CONSTANT::BASE64_DECODE_OFFSET4))),
100         static_cast<uint8_t>(((charArrayFour[static_cast<uint8_t>(BASE64_DECODE_CONSTANT::BASE64_DECODE_INDEX1)] &
101                                   static_cast<uint8_t>(BASE64_DECODE_CONSTANT::BASE64_DECODE_MASK2))
102                                   << static_cast<uint8_t>(BASE64_DECODE_CONSTANT::BASE64_DECODE_OFFSET4)) +
103                              ((charArrayFour[static_cast<uint8_t>(BASE64_DECODE_CONSTANT::BASE64_DECODE_INDEX2)] &
104                                   static_cast<uint8_t>(BASE64_DECODE_CONSTANT::BASE64_DECODE_MASK3)) >>
105                                 static_cast<uint8_t>(BASE64_DECODE_CONSTANT::BASE64_DECODE_OFFSET2))),
106         static_cast<uint8_t>(((charArrayFour[static_cast<uint8_t>(BASE64_DECODE_CONSTANT::BASE64_DECODE_INDEX2)] &
107                                   static_cast<uint8_t>(BASE64_DECODE_CONSTANT::BASE64_DECODE_MASK4))
108                                 << static_cast<uint8_t>(BASE64_DECODE_CONSTANT::BASE64_DECODE_OFFSET6)) +
109                              charArrayFour[static_cast<uint8_t>(BASE64_DECODE_CONSTANT::BASE64_DECODE_INDEX3)]),
110     };
111     for (size_t index = 0; index < CHAR_ARRAY_LENGTH_THREE; ++index) {
112         charArrayThree[index] = table[index];
113     }
114 }
115 
Encode(const std::vector<uint8_t> & source)116 std::string DBBase64Utils::Encode(const std::vector<uint8_t> &source)
117 {
118     auto it = source.begin();
119     std::string ret;
120     size_t index = 0;
121     std::array<uint8_t, CHAR_ARRAY_LENGTH_THREE> charArrayThree = { 0 };
122     std::array<uint8_t, CHAR_ARRAY_LENGTH_FOUR> charArrayFour = { 0 };
123 
124     while (it != source.end()) {
125         charArrayThree[index] = *it;
126         ++index;
127         ++it;
128         if (index != CHAR_ARRAY_LENGTH_THREE) {
129             continue;
130         }
131         MakeCharFour(charArrayThree, charArrayFour);
132         std::for_each(charArrayFour.begin(), charArrayFour.end(), [&ret](uint8_t idx) {
133             ret += BASE64_CHARS[idx];
134         });
135         index = 0;
136     }
137     if (index == 0) {
138         return ret;
139     }
140 
141     for (auto i = index; i < CHAR_ARRAY_LENGTH_THREE; ++i) {
142         charArrayThree[i] = 0;
143     }
144     MakeCharFour(charArrayThree, charArrayFour);
145 
146     for (size_t i = 0; i < index + 1; ++i) {
147         ret += BASE64_CHARS[charArrayFour[i]];
148     }
149 
150     while (index < CHAR_ARRAY_LENGTH_THREE) {
151         ret += '=';
152         ++index;
153     }
154     return ret;
155 }
156 
Decode(const std::string & encoded)157 std::vector<uint8_t> DBBase64Utils::Decode(const std::string &encoded)
158 {
159     auto it = encoded.begin();
160     size_t index = 0;
161     std::array<uint8_t, CHAR_ARRAY_LENGTH_THREE> charArrayThree = { 0 };
162     std::array<uint8_t, CHAR_ARRAY_LENGTH_FOUR> charArrayFour = { 0 };
163     std::vector<uint8_t> ret;
164 
165     while (it != encoded.end() && IsBase64Char(*it)) {
166         charArrayFour[index] = *it;
167         ++index;
168         ++it;
169         if (index != CHAR_ARRAY_LENGTH_FOUR) {
170             continue;
171         }
172         for (index = 0; index < CHAR_ARRAY_LENGTH_FOUR; ++index) {
173             charArrayFour[index] = BASE64_CHARS.find(static_cast<char>(charArrayFour[index]));
174         }
175         MakeCharTree(charArrayFour, charArrayThree);
176         std::for_each(charArrayThree.begin(), charArrayThree.end(), [&ret](uint8_t idx) {
177             ret.emplace_back(idx);
178         });
179         index = 0;
180     }
181     if (index == 0) {
182         return ret;
183     }
184 
185     for (auto i = index; i < CHAR_ARRAY_LENGTH_FOUR; ++i) {
186         charArrayFour[i] = 0;
187     }
188     for (unsigned char &i : charArrayFour) {
189         std::string::size_type idx = BASE64_CHARS.find(static_cast<char>(i));
190         if (idx != std::string::npos) {
191             i = static_cast<unsigned char>(idx);
192         }
193     }
194     MakeCharTree(charArrayFour, charArrayThree);
195 
196     for (size_t i = 0; i < index - 1; i++) {
197         ret.emplace_back(charArrayThree[i]);
198     }
199     return ret;
200 }
201 
Encode(const std::string & source)202 std::string DBBase64Utils::Encode(const std::string &source)
203 {
204     return Encode(std::vector<uint8_t>(source.begin(), source.end()));
205 }
206 
DecodeIfNeed(const std::string & source)207 std::string DBBase64Utils::DecodeIfNeed(const std::string &source)
208 {
209     auto decodeRes = Decode(source);
210     if (decodeRes.empty()) {
211         return source;
212     }
213     return std::string(decodeRes.begin(), decodeRes.end());
214 }
215 }