#pragma once namespace saw { namespace encode { struct Base64 {}; } template<> class data { public: using Schema = schema::String; private: std::string data_; public: data() = default; data(std::string data__): data_{std::move(data__)} {} uint64_t size() const { return data_.size(); } char& at(uint64_t i){ return data_.at(i); } const char& at(uint64_t i) const { return data_.at(i); } std::string stl_string() const { return data_; } bool operator==(const data& rhs) const { return data_ == rhs.data_; } }; namespace impl { constexpr std::string_view base64_char_map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; } template<> class codec { public: using Schema = schema::String; private: bool is_base64(char a) const { for(auto iter : impl::base64_char_map){ if(iter == a){ return true; } } return false; } error_or find_base64_char_pos(char a) const { for(uint8_t i = 0; i < impl::base64_char_map.size(); ++i){ if(impl::base64_char_map.at(i) == a){ return i; } } return make_error("Didn't find base64 char"); } public: template error_or encode(const data& from, data& to){ std::string b64_str; try { uint64_t unpadded_len = (from.size() * 4u + 2u).get() / 3u; uint64_t padded_len = (unpadded_len + 3u) & ~3u; b64_str.resize(padded_len); }catch(const std::exception&){ return make_error(); } uint64_t j{0u}, k{0u}; std::array s{}; for(data i = 0u; i < from.size(); ++i){ s[j] = from.at(i.get()); ++j; if(j==3){ b64_str.at(k) = impl::base64_char_map.at((s[0u] & 0xFC) >> 2); b64_str.at(k+1u) = impl::base64_char_map.at(((s[0u] & 0x03) << 4) | ((s[1] & 0xF0) >> 4)); b64_str.at(k+2u) = impl::base64_char_map.at(((s[1u] & 0x0F) << 2) | ((s[2] & 0xC0) >> 6)); b64_str.at(k+3u) = impl::base64_char_map.at((s[2u] & 0x3F)); j = 0u; k+=4u; } } if(j > 0){ if( j == 1u ){ s[1u] = 0u; } b64_str.at(k+0u) = impl::base64_char_map.at((s[0u] & 0xFC) >> 2); b64_str.at(k+1u) = impl::base64_char_map.at(((s[0u] & 0x03)<<4) + ((s[1u]&0xF0)>>4)); if(j == 2u){ b64_str.at(k+2u) = impl::base64_char_map.at( ((s[1u]&0x0F)<<2) ); }else { b64_str.at(k+2u) = '='; } b64_str.at(k+3u) = '='; } to = {std::move(b64_str)}; return void_t{}; } template error_or decode(data& from, data& to){ uint64_t b64_len = from.size(); if((b64_len % 4) != 0){ return make_error("Base64 String is not padded"); } uint64_t j{0u}; std::array s{}; std::array t{}; std::string to_str; for(uint64_t i = 0u; i < b64_len && from.at(i) != '='; ++i){ auto eo_pos = this->find_base64_char_pos(from.at(i)); if(eo_pos.is_error()){ return std::move(eo_pos.get_error()); } t[j] = eo_pos.get_value(); ++j; if(j == 4u){ s[0u] = (t[0u] << 2) + ((t[1] & 0x30) >> 4); s[1u] = ((t[1u] & 0x0F) << 4) + ((t[2u] & 0x3C) >> 2); s[2u] = ((t[2u] & 0x03) << 6) + t[3u]; to_str += s[0u]; to_str += s[1u]; to_str += s[2u]; j = 0u; } } if(j > 0u){ for(uint64_t i = j; i < t.size(); ++i){ t[i] = 0u; } s[0u] = (t[0u] << 2) + ((t[1] & 0x30) >> 4); s[1u] = ((t[1u] & 0x0F) << 4) + ((t[2u] & 0x3c) >> 2); s[2u] = ((t[2u] & 0x03) << 6) + t[3u]; for(uint64_t i = 0u; (i+1u) < j; ++i){ to_str += s[i]; } } to = {std::move(to_str)}; return make_void(); } }; }