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 #ifndef API_BASE_UTIL_BASE64_ENCODE_H
17 #define API_BASE_UTIL_BASE64_ENCODE_H
18 
19 #include <base/containers/array_view.h>
20 #include <base/containers/string.h>
21 #include <base/namespace.h>
22 #include <base/util/log.h>
23 
BASE_BEGIN_NAMESPACE()24 BASE_BEGIN_NAMESPACE()
25 namespace Detail {
26 static constexpr const char TO_BASE64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
27 
28 inline void ToBase64(const uint8_t* threeBytes, char* output)
29 {
30     output[0U] = TO_BASE64[threeBytes[0u] >> 2u];
31     output[1U] = TO_BASE64[((threeBytes[0u] << 4u) | (threeBytes[1u] >> 4u)) & 0x3f];
32     output[2U] = TO_BASE64[((threeBytes[1u] << 2u) | (threeBytes[2u] >> 6u)) & 0x3f];
33     output[3U] = TO_BASE64[threeBytes[2u] & 0x3f];
34 }
35 
36 inline void ToBase64(const uint8_t* threeBytes, char* output, signed left)
37 {
38     output[0U] = TO_BASE64[threeBytes[0u] >> 2u];
39     output[1U] = TO_BASE64[((threeBytes[0u] << 4u) | (threeBytes[1u] >> 4u)) & 0x3f];
40 
41     output[2U] = left > 1 ? (TO_BASE64[((threeBytes[1u] << 2u) | (threeBytes[2u] >> 6u)) & 0x3f]) : '=';
42     output[3U] = left > 2 ? TO_BASE64[threeBytes[2u] & 0x3f] : '=';
43 }
44 
45 inline signed EncodeTriplets(char*& dst, const uint8_t*& src, signed left)
46 {
47     for (; left >= 3; left -= 3) {
48         Detail::ToBase64(src, dst);
49         src += 3U;
50         dst += 4U;
51     }
52     return left;
53 }
54 
55 inline void FillTriplet(uint8_t* dst, const uint8_t*& src, signed left)
56 {
57     switch (left) {
58         case 2:
59             *dst++ = *src++;
60         case 1:
61             *dst++ = *src++;
62     }
63 }
64 
65 inline void EncodeTail(char* dst, const uint8_t* src, signed left)
66 {
67     if (left) {
68         uint8_t rest[3] { 0, 0, 0 };
69         FillTriplet(rest, src, left);
70         Detail::ToBase64(rest, dst, left);
71     }
72 }
73 } // namespace Detail
74 
75 /** Base 64 encode data.
76  * @param binaryData Data to encode.
77  * @return Data as a base64 string.
78  */
Base64Encode(array_view<const uint8_t> binaryData)79 inline string Base64Encode(array_view<const uint8_t> binaryData)
80 {
81     // The length of the encoded binary data will be about 4/3 of the encoded string.
82     string encodedString((binaryData.size() + 2U) / 3U * 4U, '=');
83 
84     auto dst = encodedString.data();
85     auto src = binaryData.data();
86     signed left = static_cast<signed>(binaryData.size());
87 
88     // First write the full groups of three bytes
89     left = Detail::EncodeTriplets(dst, src, left);
90 
91     // Add the rest of the bytes that was not divisible by three
92     Detail::EncodeTail(dst, src, left);
93 
94     return encodedString;
95 }
96 BASE_END_NAMESPACE()
97 #endif // API_BASE_UTIL_BASE64_ENCODE_H