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 PANDA_TOOLING_INSPECTOR_EVALUATION_BASE64_H 17 #define PANDA_TOOLING_INSPECTOR_EVALUATION_BASE64_H 18 19 #include <array> 20 #include <optional> 21 #include <string> 22 23 #include "libpandabase/macros.h" 24 #include "libpandabase/utils/span.h" 25 26 namespace ark::tooling::inspector { 27 class Base64Decoder final { 28 public: 29 Base64Decoder() = delete; 30 DecodedSize(Span<const uint8_t> input)31 static std::optional<size_t> DecodedSize(Span<const uint8_t> input) 32 { 33 if (input.empty() || input.size() % ENCODED_GROUP_BYTES != 0) { 34 return {}; 35 } 36 37 auto sz = input.size() / ENCODED_GROUP_BYTES * DECODED_GROUP_BYTES; 38 auto last = input.end() - 1; 39 for (size_t i = 0; i < MAX_PADDINGS && *last == PADDING_CHAR; ++i, --last) { 40 --sz; 41 } 42 return sz; 43 } 44 Decode(const std::string & encoded,std::string & decoded)45 static bool Decode(const std::string &encoded, std::string &decoded) 46 { 47 ASSERT(!encoded.empty()); 48 49 auto sz = encoded.size(); 50 Span encodingInput(reinterpret_cast<const uint8_t *>(encoded.c_str()), sz); 51 auto bytecodeSize = Base64Decoder::DecodedSize(encodingInput); 52 if (!bytecodeSize) { 53 return false; 54 } 55 decoded.resize(*bytecodeSize); 56 return Base64Decoder::Decode(reinterpret_cast<uint8_t *>(decoded.data()), encodingInput); 57 } 58 Decode(uint8_t * output,Span<const uint8_t> input)59 static bool Decode(uint8_t *output, Span<const uint8_t> input) 60 { 61 ASSERT(output); 62 63 if (input.empty() || input.size() % ENCODED_GROUP_BYTES != 0) { 64 return false; 65 } 66 67 std::array<uint8_t, ENCODED_GROUP_BYTES> decodingBuffer = {0}; 68 size_t baseIdx = 0; 69 auto srcIter = input.begin(); 70 for (auto endIter = input.end(); srcIter != endIter && *srcIter != PADDING_CHAR; ++srcIter) { 71 auto decoded = DecodeChar(*srcIter); 72 if (decoded == INVALID_VALUE) { 73 return false; 74 } 75 decodingBuffer[baseIdx++] = decoded; 76 77 if (baseIdx == ENCODED_GROUP_BYTES) { 78 DecodeSextetsGroup(decodingBuffer, Span(output, DECODED_GROUP_BYTES)); 79 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 80 output += DECODED_GROUP_BYTES; 81 baseIdx = 0; 82 } 83 } 84 85 if (baseIdx != 0) { 86 // Decode the remainder part. 87 std::array<uint8_t, DECODED_GROUP_BYTES> decodedRemainder = {0}; 88 DecodeSextetsGroup(decodingBuffer, Span(decodedRemainder.data(), DECODED_GROUP_BYTES)); 89 for (size_t idx = 0, end = baseIdx - 1; idx < end; ++idx) { 90 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 91 *output++ = decodedRemainder[idx]; 92 } 93 } 94 95 // Only padding symbols could remain. 96 // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) 97 auto remainder = input.end() - srcIter; 98 return (remainder == 0 || (remainder == 1 && *srcIter == PADDING_CHAR) || 99 (remainder == MAX_PADDINGS && *srcIter == PADDING_CHAR && *(srcIter + 1) == PADDING_CHAR)); 100 // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) 101 } 102 103 private: 104 static constexpr size_t DECODED_GROUP_BYTES = 3; 105 static constexpr size_t ENCODED_GROUP_BYTES = 4; 106 107 private: DecodeChar(uint8_t encoded)108 static uint8_t DecodeChar(uint8_t encoded) 109 { 110 if (encoded >= DECODE_TABLE.size()) { 111 return INVALID_VALUE; 112 } 113 return DECODE_TABLE[encoded]; 114 } 115 116 /// @brief Converts 4 sextets into 3 output bytes. DecodeSextetsGroup(const std::array<uint8_t,ENCODED_GROUP_BYTES> & decodingBuffer,Span<uint8_t> output)117 static void DecodeSextetsGroup(const std::array<uint8_t, ENCODED_GROUP_BYTES> &decodingBuffer, Span<uint8_t> output) 118 { 119 // 0b00110000 120 static constexpr uint8_t FOURTH_TO_FIFTH_BITS = 0x30U; 121 // 0b00111100 122 static constexpr uint8_t SECOND_TO_FIFTH_BITS = 0x3CU; 123 static constexpr uint8_t TWO_BITS_SHIFT = 2U; 124 static constexpr uint8_t FOUR_BITS_SHIFT = 4U; 125 static constexpr uint8_t SIX_BITS_SHIFT = 6U; 126 127 ASSERT(output.size() == DECODED_GROUP_BYTES); 128 // NOLINTBEGIN(hicpp-signed-bitwise) 129 output[0UL] = 130 (decodingBuffer[0UL] << TWO_BITS_SHIFT) | ((decodingBuffer[1UL] & FOURTH_TO_FIFTH_BITS) >> FOUR_BITS_SHIFT); 131 output[1UL] = 132 (decodingBuffer[1UL] << FOUR_BITS_SHIFT) | ((decodingBuffer[2UL] & SECOND_TO_FIFTH_BITS) >> TWO_BITS_SHIFT); 133 output[2UL] = (decodingBuffer[2UL] << SIX_BITS_SHIFT) | decodingBuffer[3UL]; 134 // NOLINTEND(hicpp-signed-bitwise) 135 } 136 137 private: 138 static constexpr size_t MAX_PADDINGS = 2U; 139 static constexpr uint8_t PADDING_CHAR = '='; 140 static constexpr uint8_t INVALID_VALUE = 255U; 141 static constexpr std::array<uint8_t, 123> DECODE_TABLE = { 142 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 143 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 144 255, 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 255, 255, 145 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 146 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, 147 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51}; 148 }; 149 } // namespace ark::tooling::inspector 150 151 #endif // PANDA_TOOLING_INSPECTOR_EVALUATION_BASE64_H 152