1/*
2 * Copyright (c) 2022 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 <array>
17#include <algorithm>
18
19#include "base64_utils.h"
20
21namespace OHOS::NetManagerStandard::Base64 {
22static std::string BASE64_CHARS = /* NOLINT */
23    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
24    "abcdefghijklmnopqrstuvwxyz"
25    "0123456789+/";
26
27static constexpr const uint32_t CHAR_ARRAY_LENGTH_THREE = 3;
28static constexpr const uint32_t CHAR_ARRAY_LENGTH_FOUR = 4;
29
30enum 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
45enum 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
59static inline bool IsBase64Char(const char c)
60{
61    return (isalnum(c) || (c == '+') || (c == '/'));
62}
63
64static inline 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[BASE64_ENCODE_INDEX0] & BASE64_ENCODE_MASK1) >> BASE64_ENCODE_OFFSET2),
69        static_cast<uint8_t>(((charArrayThree[BASE64_ENCODE_INDEX0] & BASE64_ENCODE_MASK2) << BASE64_ENCODE_OFFSET4) +
70                             ((charArrayThree[BASE64_ENCODE_INDEX1] & BASE64_ENCODE_MASK5) >> BASE64_ENCODE_OFFSET4)),
71        static_cast<uint8_t>(((charArrayThree[BASE64_ENCODE_INDEX1] & BASE64_ENCODE_MASK3) << BASE64_ENCODE_OFFSET2) +
72                             ((charArrayThree[BASE64_ENCODE_INDEX2] & BASE64_ENCODE_MASK6) >> BASE64_ENCODE_OFFSET6)),
73        static_cast<uint8_t>(charArrayThree[BASE64_ENCODE_INDEX2] & BASE64_ENCODE_MASK4),
74    };
75    for (size_t index = 0; index < CHAR_ARRAY_LENGTH_FOUR; ++index) {
76        charArrayFour[index] = table[index];
77    }
78}
79
80static inline void MakeCharTree(const std::array<uint8_t, CHAR_ARRAY_LENGTH_FOUR> &charArrayFour,
81                                std::array<uint8_t, CHAR_ARRAY_LENGTH_THREE> &charArrayThree)
82{
83    const uint8_t table[CHAR_ARRAY_LENGTH_THREE] = {
84        static_cast<uint8_t>((charArrayFour[BASE64_DECODE_INDEX0] << BASE64_DECODE_OFFSET2) +
85                             ((charArrayFour[BASE64_DECODE_INDEX1] & BASE64_DECODE_MASK1) >> BASE64_DECODE_OFFSET4)),
86        static_cast<uint8_t>(((charArrayFour[BASE64_DECODE_INDEX1] & BASE64_DECODE_MASK2) << BASE64_DECODE_OFFSET4) +
87                             ((charArrayFour[BASE64_DECODE_INDEX2] & BASE64_DECODE_MASK3) >> BASE64_DECODE_OFFSET2)),
88        static_cast<uint8_t>(((charArrayFour[BASE64_DECODE_INDEX2] & BASE64_DECODE_MASK4) << BASE64_DECODE_OFFSET6) +
89                             charArrayFour[BASE64_DECODE_INDEX3]),
90    };
91    for (size_t index = 0; index < CHAR_ARRAY_LENGTH_THREE; ++index) {
92        charArrayThree[index] = table[index];
93    }
94}
95
96std::string Encode(const std::string &source)
97{
98    auto it = source.begin();
99    std::string ret;
100    size_t index = 0;
101    std::array<uint8_t, CHAR_ARRAY_LENGTH_THREE> charArrayThree = {0};
102    std::array<uint8_t, CHAR_ARRAY_LENGTH_FOUR> charArrayFour = {0};
103
104    while (it != source.end()) {
105        charArrayThree[index] = *it;
106        ++index;
107        ++it;
108        if (index != CHAR_ARRAY_LENGTH_THREE) {
109            continue;
110        }
111        MakeCharFour(charArrayThree, charArrayFour);
112        std::for_each(charArrayFour.begin(), charArrayFour.end(), [&ret](uint8_t idx) {
113            ret += BASE64_CHARS[idx];
114        });
115        index = 0;
116    }
117    if (index == 0) {
118        return ret;
119    }
120
121    for (auto i = index; i < CHAR_ARRAY_LENGTH_THREE; ++i) {
122        charArrayThree[i] = 0;
123    }
124    MakeCharFour(charArrayThree, charArrayFour);
125
126    for (size_t i = 0; i < index + 1; ++i) {
127        ret += BASE64_CHARS[charArrayFour[i]];
128    }
129
130    while (index < CHAR_ARRAY_LENGTH_THREE) {
131        ret += '=';
132        ++index;
133    }
134    return ret;
135}
136
137std::string Decode(const std::string &encoded)
138{
139    auto it = encoded.begin();
140    size_t index = 0;
141    std::array<uint8_t, CHAR_ARRAY_LENGTH_THREE> charArrayThree = {0};
142    std::array<uint8_t, CHAR_ARRAY_LENGTH_FOUR> charArrayFour = {0};
143    std::string ret;
144
145    while (it != encoded.end() && IsBase64Char(*it)) {
146        charArrayFour[index] = *it;
147        ++index;
148        ++it;
149        if (index != CHAR_ARRAY_LENGTH_FOUR) {
150            continue;
151        }
152        for (index = 0; index < CHAR_ARRAY_LENGTH_FOUR; ++index) {
153            charArrayFour[index] = BASE64_CHARS.find(static_cast<char>(charArrayFour[index]));
154        }
155        MakeCharTree(charArrayFour, charArrayThree);
156        std::for_each(charArrayThree.begin(), charArrayThree.end(), [&ret](uint8_t idx) {
157            ret += static_cast<char>(idx);
158        });
159        index = 0;
160    }
161    if (index == 0) {
162        return ret;
163    }
164
165    for (auto i = index; i < CHAR_ARRAY_LENGTH_FOUR; ++i) {
166        charArrayFour[i] = 0;
167    }
168    for (unsigned char &i : charArrayFour) {
169        std::string::size_type idx = BASE64_CHARS.find(static_cast<char>(i));
170        if (idx != std::string::npos) {
171            i = static_cast<unsigned char>(idx);
172        }
173    }
174    MakeCharTree(charArrayFour, charArrayThree);
175
176    for (size_t i = 0; i < index - 1; i++) {
177        ret += static_cast<char>(charArrayThree[i]);
178    }
179    return ret;
180}
181} // namespace OHOS::NetManagerStandard::Base64
182