From 5c8a211f46d0174028e5d3d0602a1b4c19f16c92 Mon Sep 17 00:00:00 2001 From: SEK1RO Date: Tue, 17 Sep 2024 15:51:42 +0300 Subject: [PATCH] feat(base64): decode --- src/base64.cpp | 61 ++++++++++++++++++++++++++++++++++++-------- test/test-base64.cpp | 20 +++++++++------ 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/src/base64.cpp b/src/base64.cpp index ba10e3e..cdecffe 100644 --- a/src/base64.cpp +++ b/src/base64.cpp @@ -55,10 +55,13 @@ namespace base64 str_size = str_size * 4 + (data.size() % 3 ? 4 : 0); return str_size; } - // uint64_t sizeDecoded(std::string_view str) noexcept - // { - - // } + uint64_t sizeDecoded(std::string_view str) noexcept + { + auto size = std::distance(str.begin(), std::find_if(str.rbegin(), str.rend(), [](char ch) + { return ch != '='; }) + .base()); + return size / 4 * 3 + (size % 4 ? size % 4 - 1 : 0); + } void encode(const uint8_t *data, uint64_t data_size, char *str, uint64_t str_size) { if (str_size < base64::sizeEncoded(std::span(data, data_size))) @@ -100,10 +103,48 @@ namespace base64 base64::encode(data.data(), data.size(), str.data(), str.size()); return str; } - // void decode(const char *str, uint8_t *data, uint64_t data_size) - // { - // } - // std::vector decode(std::string_view str) - // { - // } + void decode(const char *str, uint64_t str_size, uint8_t *data, uint64_t data_size) + { + std::string_view sv(str, str_size); + if (data_size < base64::sizeDecoded(sv)) + { + throw std::logic_error("base64::decode: not enough allocated length"); + } + if (!base64::isValid(sv)) + { + throw std::logic_error("base64::decode: out of digits map"); + } + auto size = std::distance(sv.begin(), std::find_if(sv.rbegin(), sv.rend(), [](char ch) + { return ch != '='; }) + .base()); + if (sv.size() % 4 != 0 || sv.size() - size > 2) + { + throw std::logic_error("base64::decode: incorrect padding"); + } + for (auto i = 0; i < size / 4; i++) + { + data[i * 3] = b64map[(int8_t)str[i * 4]] << 2 | b64map[(int8_t)str[i * 4 + 1]] >> 4; + data[i * 3 + 1] = b64map[(int8_t)str[i * 4 + 1]] << 4 | b64map[(int8_t)str[i * 4 + 2]] >> 2; + data[i * 3 + 2] = b64map[(int8_t)str[i * 4 + 2]] << 6 | b64map[(int8_t)str[i * 4 + 3]]; + } + uint64_t last_idx = size / 4 * 3; + switch (size % 4) + { + case 2: + data[last_idx] = b64map[(int8_t)str[size - 2]] << 2 | b64map[(int8_t)str[size - 1]] >> 4; + break; + case 3: + data[last_idx] = b64map[(int8_t)str[size - 3]] << 2 | b64map[(int8_t)str[size - 2]] >> 4; + data[last_idx + 1] = b64map[(int8_t)str[size - 2]] << 4 | b64map[(int8_t)str[size - 1]] >> 2; + break; + default: + break; + } + } + std::vector decode(std::string_view str) noexcept + { + std::vector data(base64::sizeDecoded(str)); + base64::decode(str.data(), str.size(), data.data(), data.size()); + return data; + } } \ No newline at end of file diff --git a/test/test-base64.cpp b/test/test-base64.cpp index b452011..0587768 100644 --- a/test/test-base64.cpp +++ b/test/test-base64.cpp @@ -25,14 +25,18 @@ TEST(base64, encode_1e7) std::vector data(1e7); encode(data); } -// TEST(base64, decode) -// { -// } -// TEST(base64, decode_1e6) -// { -// std::string str(1e6, '0'); -// decode(str); -// } +TEST(base64, decode) +{ + EXPECT_EQ(hex::encode(decode("")), ""); + EXPECT_EQ(hex::encode(decode("BKUEpQ==")), "04a504a5"); + EXPECT_EQ(hex::encode(decode("BKUEpQA=")), "04a504a500"); + EXPECT_EQ(hex::encode(decode("BKUEpQAA")), "04a504a50000"); +} +TEST(base64, decode_1e7) +{ + std::string str(1e7, '0'); + decode(str); +} int main(int argc, char **argv) {