| 564 | |
| 565 | template <class OutputBuffer> |
| 566 | inline OutputBuffer decode_into(std::string_view base64Text) { |
| 567 | typedef typename OutputBuffer::value_type output_value_type; |
| 568 | static_assert(std::is_same_v<output_value_type, char> || |
| 569 | std::is_same_v<output_value_type, signed char> || |
| 570 | std::is_same_v<output_value_type, unsigned char> || |
| 571 | std::is_same_v<output_value_type, std::byte>); |
| 572 | if (base64Text.empty()) { |
| 573 | return OutputBuffer(); |
| 574 | } |
| 575 | |
| 576 | if ((base64Text.size() & 3) != 0) { |
| 577 | throw std::runtime_error{ |
| 578 | "Invalid base64 encoded data - Size not divisible by 4"}; |
| 579 | } |
| 580 | |
| 581 | const size_t numPadding = |
| 582 | std::count(base64Text.rbegin(), base64Text.rbegin() + 4, '='); |
| 583 | if (numPadding > 2) { |
| 584 | throw std::runtime_error{ |
| 585 | "Invalid base64 encoded data - Found more than 2 padding signs"}; |
| 586 | } |
| 587 | |
| 588 | const size_t decodedsize = (base64Text.size() * 3 >> 2) - numPadding; |
| 589 | OutputBuffer decoded(decodedsize, '.'); |
| 590 | |
| 591 | const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&base64Text[0]); |
| 592 | char* currDecoding = reinterpret_cast<char*>(&decoded[0]); |
| 593 | |
| 594 | for (size_t i = (base64Text.size() >> 2) - (numPadding != 0); i; --i) { |
| 595 | const uint8_t t1 = *bytes++; |
| 596 | const uint8_t t2 = *bytes++; |
| 597 | const uint8_t t3 = *bytes++; |
| 598 | const uint8_t t4 = *bytes++; |
| 599 | |
| 600 | const uint32_t d1 = detail::decode_table_0[t1]; |
| 601 | const uint32_t d2 = detail::decode_table_1[t2]; |
| 602 | const uint32_t d3 = detail::decode_table_2[t3]; |
| 603 | const uint32_t d4 = detail::decode_table_3[t4]; |
| 604 | |
| 605 | const uint32_t temp = d1 | d2 | d3 | d4; |
| 606 | |
| 607 | if (temp >= detail::bad_char) { |
| 608 | throw std::runtime_error{ |
| 609 | "Invalid base64 encoded data - Invalid character"}; |
| 610 | } |
| 611 | |
| 612 | // Use bit_cast instead of union and type punning to avoid |
| 613 | // undefined behaviour risk: |
| 614 | // https://en.wikipedia.org/wiki/Type_punning#Use_of_union |
| 615 | const std::array<char, 4> tempBytes = |
| 616 | detail::bit_cast<std::array<char, 4>, uint32_t>(temp); |
| 617 | |
| 618 | *currDecoding++ = tempBytes[detail::decidx0]; |
| 619 | *currDecoding++ = tempBytes[detail::decidx1]; |
| 620 | *currDecoding++ = tempBytes[detail::decidx2]; |
| 621 | } |
| 622 | |
| 623 | switch (numPadding) { |