Skip to content

Commit

Permalink
Fix C++ generator to pass Base64 tests (#442)
Browse files Browse the repository at this point in the history
This patch includes all the changes needed so that we pass the Base64
tests in the SDK.
  • Loading branch information
mristin authored Feb 7, 2024
1 parent 256cc8a commit 3e36069
Show file tree
Hide file tree
Showing 5 changed files with 8 additions and 199 deletions.
4 changes: 4 additions & 0 deletions aas_core_codegen/cpp/stringification/_generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,10 @@ def _generate_base64_decode_implementation() -> List[Stripped]:
> {function_name}(
{I}const std::string& text
) {{
{I}if (text.empty()) {{
{II}return std::vector<std::uint8_t>();
{I}}}
{I}const std::size_t len = text.size();
{I}std::size_t len_wo_pad = len;
Expand Down
114 changes: 0 additions & 114 deletions aas_core_codegen/cpp/wstringification/_generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -457,118 +457,6 @@ def _generate_enum_to_wstring_implementation(
)


def _generate_base64_encode_definition() -> Stripped:
"""Generate the definition of a stringification of bytes as base64 wstring."""
function_name = cpp_naming.function_name(Identifier("base64_encode"))
return Stripped(
f"""\
/**
* Encode the \\p bytes with base64 to a std::wstring.
*
* \\param bytes to be encoded
* \\return base64-encoding of \\p bytes
*/
std::wstring {function_name}(
{I}const std::vector<std::uint8_t>& bytes
);"""
)


def _generate_base64_encode_implementation() -> List[Stripped]:
"""Generate the implementation of a stringification of bytes as base64 wstring."""
function_name = cpp_naming.function_name(Identifier("base64_encode"))

wchar_base64_table = cpp_naming.constant_name(Identifier("wchar_base64_table"))

return [
Stripped(
"""\
// The following encoder has been adapted from Jouni Malinen <j@w1.fi> to work with
// std::wstring. The original source code is available at:
// https://web.mit.edu/freebsd/head/contrib/wpa/src/utils/base64.c"""
),
Stripped(
f"""\
static const wchar_t {wchar_base64_table}[65](
{I}L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
);"""
),
Stripped(
f"""\
std::wstring {function_name}(
{I}const std::vector<std::uint8_t>& bytes
) {{
{I}// See: https://cplusplus.com/reference/vector/vector/data/.
{I}// The data is guaranteed to be a continuous block in memory.
{I}const unsigned char* src(
{II}bytes.data()
{I});
{I}const std::size_t len = bytes.size();
{I}// 3-byte blocks to 4-byte
{I}const std::size_t olen = 4 * ((len + 2) / 3);
{I}// Integer overflow?
{I}if (olen < len) {{
{II}throw std::invalid_argument(
{III}common::Concat(
{IIII}"The calculation of the output length overflowed. "
{IIII}"The length was: ",
{IIII}std::to_string(len),
{IIII}", but the output length was calculated as: ",
{IIII}std::to_string(olen)
{III})
{II});
{I}}}
{I}std::wstring out_wstring;
{I}out_wstring.resize(olen);
{I}wchar_t* out(
{II}static_cast<wchar_t*>(
{III}&out_wstring[0]
{II})
{I});
{I}const unsigned char* end = src + len;
{I}const unsigned char* in = src;
{I}wchar_t* pos = out;
{I}while (end - in >= 3) {{
{II}*pos++ = {wchar_base64_table}[in[0] >> 2];
{II}*pos++ = {wchar_base64_table}[((in[0] & 0x03) << 4) | (in[1] >> 4)];
{II}*pos++ = {wchar_base64_table}[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
{II}*pos++ = {wchar_base64_table}[in[2] & 0x3f];
{II}in += 3;
{I}}}
{I}if (end - in) {{
{II}*pos++ = {wchar_base64_table}[in[0] >> 2];
{II}if (end - in == 1) {{
{III}*pos++ = {wchar_base64_table}[(in[0] & 0x03) << 4];
{III}*pos++ = L'=';
{II}}} else {{
{III}*pos++ = {wchar_base64_table}[
{IIII}((in[0] & 0x03) << 4) | (in[1] >> 4)
{III}];
{III}*pos++ = {wchar_base64_table}[(in[1] & 0x0f) << 2];
{II}}}
{II}*pos++ = L'=';
{I}}}
{I}return out_wstring;
}}"""
),
]


# NOTE (mristin, 2023-07-12):
# The SDK does not use base64-decoding *from* wide strings, so we omit that direction
# here following YAGNI.

# fmt: off
@ensure(
lambda result:
Expand Down Expand Up @@ -622,7 +510,6 @@ def generate_header(

blocks.extend(
[
_generate_base64_encode_definition(),
Stripped(
"""\
} // namespace wstringification
Expand Down Expand Up @@ -682,7 +569,6 @@ def generate_implementation(

blocks.extend(
[
*_generate_base64_encode_implementation(),
cpp_common.generate_namespace_closing(namespace),
cpp_common.WARNING,
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1610,6 +1610,10 @@ common::expected<
> Base64Decode(
const std::string& text
) {
if (text.empty()) {
return std::vector<std::uint8_t>();
}

const std::size_t len = text.size();
std::size_t len_wo_pad = len;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1511,81 +1511,6 @@ std::wstring to_wstring(
}
}

// The following encoder has been adapted from Jouni Malinen <j@w1.fi> to work with
// std::wstring. The original source code is available at:
// https://web.mit.edu/freebsd/head/contrib/wpa/src/utils/base64.c

static const wchar_t kWcharBase64Table[65](
L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
);

std::wstring Base64Encode(
const std::vector<std::uint8_t>& bytes
) {
// See: https://cplusplus.com/reference/vector/vector/data/.
// The data is guaranteed to be a continuous block in memory.
const unsigned char* src(
bytes.data()
);

const std::size_t len = bytes.size();

// 3-byte blocks to 4-byte
const std::size_t olen = 4 * ((len + 2) / 3);

// Integer overflow?
if (olen < len) {
throw std::invalid_argument(
common::Concat(
"The calculation of the output length overflowed. "
"The length was: ",
std::to_string(len),
", but the output length was calculated as: ",
std::to_string(olen)
)
);
}

std::wstring out_wstring;
out_wstring.resize(olen);

wchar_t* out(
static_cast<wchar_t*>(
&out_wstring[0]
)
);

const unsigned char* end = src + len;

const unsigned char* in = src;
wchar_t* pos = out;

while (end - in >= 3) {
*pos++ = kWcharBase64Table[in[0] >> 2];
*pos++ = kWcharBase64Table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
*pos++ = kWcharBase64Table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
*pos++ = kWcharBase64Table[in[2] & 0x3f];
in += 3;
}

if (end - in) {
*pos++ = kWcharBase64Table[in[0] >> 2];

if (end - in == 1) {
*pos++ = kWcharBase64Table[(in[0] & 0x03) << 4];
*pos++ = L'=';
} else {
*pos++ = kWcharBase64Table[
((in[0] & 0x03) << 4) | (in[1] >> 4)
];
*pos++ = kWcharBase64Table[(in[1] & 0x0f) << 2];
}
*pos++ = L'=';
}

return out_wstring;
}

} // namespace wstringification
} // namespace aas_3_0
} // namespace aas_core
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -438,16 +438,6 @@ std::wstring to_wstring(
types::DataTypeIec61360 literal
);

/**
* Encode the \p bytes with base64 to a std::wstring.
*
* \param bytes to be encoded
* \return base64-encoding of \p bytes
*/
std::wstring Base64Encode(
const std::vector<std::uint8_t>& bytes
);

} // namespace wstringification
/**@}*/

Expand Down

0 comments on commit 3e36069

Please sign in to comment.