diff --git a/src/base64.cc b/src/base64.cc index 12f24df..3bd7be4 100644 --- a/src/base64.cc +++ b/src/base64.cc @@ -13,10 +13,10 @@ #include -static const unsigned char base64_table[65] = +static const char base64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -std::string ot::encode_base64(std::string_view src) +std::string ot::encode_base64(ot::byte_string_view src) { size_t len = src.size(); size_t num_blocks = (len + 2) / 3; // Count of 3-byte blocks, rounded up. @@ -27,9 +27,9 @@ std::string ot::encode_base64(std::string_view src) std::string out; out.resize(olen); - const unsigned char* in = reinterpret_cast(src.data()); - const unsigned char* end = in + len; - unsigned char* pos = reinterpret_cast(out.data()); + const uint8_t* in = src.data(); + const uint8_t* end = in + len; + char* pos = out.data(); while (end - in >= 3) { *pos++ = base64_table[in[0] >> 2]; *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; @@ -53,7 +53,7 @@ std::string ot::encode_base64(std::string_view src) return out; } -std::string ot::decode_base64(std::string_view src) +ot::byte_string ot::decode_base64(std::string_view src) { // Remove the padding and rely on the string length instead. while (src.back() == '=') @@ -66,14 +66,14 @@ std::string ot::decode_base64(std::string_view src) case 3: olen += 2; break; } - std::string out; + ot::byte_string out; out.resize(olen); - unsigned char* pos = reinterpret_cast(out.data()); + uint8_t* pos = out.data(); unsigned char dtable[256]; memset(dtable, 0x80, 256); for (size_t i = 0; i < sizeof(base64_table) - 1; ++i) - dtable[base64_table[i]] = (unsigned char) i; + dtable[(size_t) base64_table[i]] = (unsigned char) i; unsigned char block[4]; size_t count = 0; diff --git a/src/opus.cc b/src/opus.cc index df36bad..5466440 100644 --- a/src/opus.cc +++ b/src/opus.cc @@ -122,31 +122,29 @@ ot::dynamic_ogg_packet ot::render_tags(const opus_tags& tags) * * Integers are all big endian. */ -ot::picture::picture(std::string block) +ot::picture::picture(ot::byte_string block) : storage(std::move(block)) { - auto bytes = reinterpret_cast(storage.data()); - size_t mime_offset = 4; if (storage.size() < mime_offset + 4) throw status { st::invalid_size, "missing MIME type in picture block" }; - uint32_t mime_size = be32toh(*reinterpret_cast(bytes + mime_offset)); + uint32_t mime_size = be32toh(*reinterpret_cast(&storage[mime_offset])); size_t desc_offset = mime_offset + 4 + mime_size; if (storage.size() < desc_offset + 4) throw status { st::invalid_size, "missing description in picture block" }; - uint32_t desc_size = be32toh(*reinterpret_cast(bytes + desc_offset)); + uint32_t desc_size = be32toh(*reinterpret_cast(&storage[desc_offset])); size_t pic_offset = desc_offset + 4 + desc_size + 16; if (storage.size() < pic_offset + 4) throw status { st::invalid_size, "missing picture data in picture block" }; - uint32_t pic_size = be32toh(*reinterpret_cast(bytes + pic_offset)); + uint32_t pic_size = be32toh(*reinterpret_cast(&storage[pic_offset])); if (storage.size() != pic_offset + 4 + pic_size) throw status { st::invalid_size, "invalid picture block size" }; - mime_type = std::string_view(reinterpret_cast(bytes + mime_offset + 4), mime_size); - picture_data = std::string_view(reinterpret_cast(bytes + pic_offset + 4), pic_size); + mime_type = byte_string_view(&storage[mime_offset + 4], mime_size); + picture_data = byte_string_view(&storage[pic_offset + 4], pic_size); } /** diff --git a/src/opustags.h b/src/opustags.h index d422b2d..6e27773 100644 --- a/src/opustags.h +++ b/src/opustags.h @@ -111,6 +111,9 @@ struct status { std::string message; }; +using byte_string = std::basic_string; +using byte_string_view = std::basic_string_view; + /***********************************************************************************************//** * \defgroup system System * \{ @@ -192,8 +195,8 @@ void run_editor(std::string_view editor, std::string_view path); */ timespec get_file_timestamp(const char* path); -std::string encode_base64(std::string_view src); -std::string decode_base64(std::string_view src); +std::string encode_base64(byte_string_view src); +byte_string decode_base64(std::string_view src); /** \} */ @@ -410,12 +413,12 @@ dynamic_ogg_packet render_tags(const opus_tags& tags); */ struct picture { /** Extract the picture information from serialized binary data.*/ - picture(std::string block); - std::string_view mime_type; - std::string_view picture_data; + picture(byte_string block); + byte_string_view mime_type; + byte_string_view picture_data; /** To avoid needless copies of the picture data, move the original data block there. The * string_view attributes will refer to it. */ - std::string storage; + byte_string storage; }; /** Extract the first picture embedded in the tags, regardless of its type. */ @@ -560,3 +563,7 @@ void run(const options& opt); /** \} */ } + +/** Handy literal suffix for building byte strings. */ +ot::byte_string operator""_bs(const char* data, size_t size); +ot::byte_string_view operator""_bsv(const char* data, size_t size); diff --git a/src/system.cc b/src/system.cc index 7682ffe..20677df 100644 --- a/src/system.cc +++ b/src/system.cc @@ -18,6 +18,16 @@ #include #include +ot::byte_string operator""_bs(const char* data, size_t size) +{ + return ot::byte_string(reinterpret_cast(data), size); +} + +ot::byte_string_view operator""_bsv(const char* data, size_t size) +{ + return ot::byte_string_view(reinterpret_cast(data), size); +} + void ot::partial_file::open(const char* destination) { final_name = destination; diff --git a/t/base64.cc b/t/base64.cc index 7b0c686..a775d46 100644 --- a/t/base64.cc +++ b/t/base64.cc @@ -3,26 +3,26 @@ static void check_encode_base64() { - is(ot::encode_base64(""), "", "empty"); - is(ot::encode_base64("a"), "YQ==", "1 character"); - is(ot::encode_base64("aa"), "YWE=", "2 characters"); - is(ot::encode_base64("aaa"), "YWFh", "3 characters"); - is(ot::encode_base64("aaaa"), "YWFhYQ==", "4 characters"); - is(ot::encode_base64("\xFF\xFF\xFE"), "///+", "RFC alphabet"); - is(ot::encode_base64("\0x"sv), "AHg=", "embedded null bytes"); + is(ot::encode_base64(""_bsv), "", "empty"); + is(ot::encode_base64("a"_bsv), "YQ==", "1 character"); + is(ot::encode_base64("aa"_bsv), "YWE=", "2 characters"); + is(ot::encode_base64("aaa"_bsv), "YWFh", "3 characters"); + is(ot::encode_base64("aaaa"_bsv), "YWFhYQ==", "4 characters"); + is(ot::encode_base64("\xFF\xFF\xFE"_bsv), "///+", "RFC alphabet"); + is(ot::encode_base64("\0x"_bsv), "AHg=", "embedded null bytes"); } static void check_decode_base64() { - is(ot::decode_base64(""), "", "empty"); - is(ot::decode_base64("YQ=="), "a", "1 character"); - is(ot::decode_base64("YWE="), "aa", "2 characters"); - is(ot::decode_base64("YQ"), "a", "padless 1 character"); - is(ot::decode_base64("YWE"), "aa", "padless 2 characters"); - is(ot::decode_base64("YWFh"), "aaa", "3 characters"); - is(ot::decode_base64("YWFhYQ=="), "aaaa", "4 characters"); - is(ot::decode_base64("///+"), "\xFF\xFF\xFE", "RFC alphabet"); - is(ot::decode_base64("AHg="), "\0x"sv, "embedded null bytes"); + opaque_is(ot::decode_base64(""), ""_bsv, "empty"); + opaque_is(ot::decode_base64("YQ=="), "a"_bsv, "1 character"); + opaque_is(ot::decode_base64("YWE="), "aa"_bsv, "2 characters"); + opaque_is(ot::decode_base64("YQ"), "a"_bsv, "padless 1 character"); + opaque_is(ot::decode_base64("YWE"), "aa"_bsv, "padless 2 characters"); + opaque_is(ot::decode_base64("YWFh"), "aaa"_bsv, "3 characters"); + opaque_is(ot::decode_base64("YWFhYQ=="), "aaaa"_bsv, "4 characters"); + opaque_is(ot::decode_base64("///+"), "\xFF\xFF\xFE"_bsv, "RFC alphabet"); + opaque_is(ot::decode_base64("AHg="), "\0x"_bsv, "embedded null bytes"); try { ot::decode_base64("Y==="); diff --git a/t/opus.cc b/t/opus.cc index fdeed6b..b39926d 100644 --- a/t/opus.cc +++ b/t/opus.cc @@ -137,7 +137,7 @@ static void recode_padding() static void extract_cover() { - std::string_view picture_data = ""sv + ot::byte_string_view picture_data = ""_bsv "\x00\x00\x00\x03" // Picture type 3. "\x00\x00\x00\x09" "image/foo" // MIME type. "\x00\x00\x00\x00" "" // Description. @@ -152,12 +152,12 @@ static void extract_cover() std::optional cover = ot::extract_cover(tags); if (!cover) throw failure("could not extract the cover"); - if (cover->mime_type != "image/foo") + if (cover->mime_type != "image/foo"_bsv) throw failure("bad extracted MIME type"); - if (cover->picture_data != "Picture data") + if (cover->picture_data != "Picture data"_bsv) throw failure("bad extracted picture data"); - std::string_view truncated_data = picture_data.substr(0, picture_data.size() - 1); + ot::byte_string_view truncated_data = picture_data.substr(0, picture_data.size() - 1); tags.comments = { "METADATA_BLOCK_PICTURE=" + ot::encode_base64(truncated_data) }; try { ot::extract_cover(tags); diff --git a/t/tap.h b/t/tap.h index 5c415bf..f3c64ef 100644 --- a/t/tap.h +++ b/t/tap.h @@ -51,6 +51,13 @@ void is(const T& got, const U& expected, const char* name) } } +template +void opaque_is(const T& got, const U& expected, const char* name) +{ + if (got != expected) + throw failure(name); +} + template <> void is(const ot::status& got, const ot::st& expected, const char* name) {