From 1d13c258e41559f911ead69e7f0a9e047e63bd35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Mangano?= Date: Fri, 3 Mar 2023 12:07:31 +0900 Subject: [PATCH] Use std::u8string where appropriate --- src/base64.cc | 14 ++++---- src/cli.cc | 93 +++++++++++++++++++++++++++----------------------- src/opus.cc | 24 ++++++------- src/opustags.h | 28 ++++++--------- t/base64.cc | 36 +++++++++---------- t/cli.cc | 34 +++++++++--------- t/opus.cc | 16 ++++----- 7 files changed, 123 insertions(+), 122 deletions(-) diff --git a/src/base64.cc b/src/base64.cc index 3bd7be4..9118f41 100644 --- a/src/base64.cc +++ b/src/base64.cc @@ -13,10 +13,10 @@ #include -static const char base64_table[65] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char8_t base64_table[65] = + u8"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -std::string ot::encode_base64(ot::byte_string_view src) +std::u8string 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. @@ -24,12 +24,12 @@ std::string ot::encode_base64(ot::byte_string_view src) if (olen < len) throw std::overflow_error("failed to encode excessively long base64 block"); - std::string out; + std::u8string out; out.resize(olen); const uint8_t* in = src.data(); const uint8_t* end = in + len; - char* pos = out.data(); + char8_t* pos = out.data(); while (end - in >= 3) { *pos++ = base64_table[in[0] >> 2]; *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; @@ -53,10 +53,10 @@ std::string ot::encode_base64(ot::byte_string_view src) return out; } -ot::byte_string ot::decode_base64(std::string_view src) +ot::byte_string ot::decode_base64(std::u8string_view src) { // Remove the padding and rely on the string length instead. - while (src.back() == '=') + while (src.back() == u8'=') src.remove_suffix(1); size_t olen = src.size() / 4 * 3; // Whole blocks; diff --git a/src/cli.cc b/src/cli.cc index 1f3bb4a..ae890c4 100644 --- a/src/cli.cc +++ b/src/cli.cc @@ -65,6 +65,8 @@ ot::options ot::parse_options(int argc, char** argv, FILE* comments_input) options opt; const char* equal; ot::status rc; + std::list local_to_add; // opt.to_add before UTF-8 conversion. + std::list local_to_delete; // opt.to_delete before UTF-8 conversion. bool set_all = false; std::optional set_cover; opt = {}; @@ -90,7 +92,7 @@ ot::options ot::parse_options(int argc, char** argv, FILE* comments_input) opt.overwrite = true; break; case 'd': - opt.to_delete.emplace_back(optarg); + local_to_delete.emplace_back(optarg); break; case 'a': case 's': @@ -98,8 +100,8 @@ ot::options ot::parse_options(int argc, char** argv, FILE* comments_input) if (equal == nullptr) throw status {st::bad_arguments, "Comment does not contain an equal sign: "s + optarg + "."}; if (c == 's') - opt.to_delete.emplace_back(optarg, equal - optarg); - opt.to_add.emplace_back(optarg); + local_to_delete.emplace_back(optarg, equal - optarg); + local_to_add.emplace_back(optarg); break; case 'S': opt.delete_all = true; @@ -151,14 +153,22 @@ ot::options ot::parse_options(int argc, char** argv, FILE* comments_input) throw status { st::bad_arguments, "Cannot use standard input more than once." }; // Convert arguments to UTF-8. - if (!opt.raw) { - for (std::list* args : { &opt.to_add, &opt.to_delete }) { - try { - for (std::string& arg : *args) - arg = to_utf8(arg); - } catch (const ot::status& rc) { - throw status {st::bad_arguments, "Could not encode argument into UTF-8: " + rc.message}; - } + if (opt.raw) { + // Cast the user data without any encoding conversion. + auto cast_to_utf8 = [](std::string_view in) + { return std::u8string(reinterpret_cast(in.data()), in.size()); }; + std::transform(local_to_add.begin(), local_to_add.end(), + std::back_inserter(opt.to_add), cast_to_utf8); + std::transform(local_to_delete.begin(), local_to_delete.end(), + std::back_inserter(opt.to_delete), cast_to_utf8); + } else { + try { + std::transform(local_to_add.begin(), local_to_add.end(), + std::back_inserter(opt.to_add), encode_utf8); + std::transform(local_to_delete.begin(), local_to_delete.end(), + std::back_inserter(opt.to_delete), encode_utf8); + } catch (const ot::status& rc) { + throw status {st::bad_arguments, "Could not encode argument into UTF-8: " + rc.message}; } } @@ -188,13 +198,13 @@ ot::options ot::parse_options(int argc, char** argv, FILE* comments_input) if (set_cover) { byte_string picture_data = ot::slurp_binary_file(set_cover->c_str()); - opt.to_delete.push_back("METADATA_BLOCK_PICTURE"); + opt.to_delete.push_back(u8"METADATA_BLOCK_PICTURE"s); opt.to_add.push_back(ot::make_cover(picture_data)); } if (set_all) { // Read comments from stdin and prepend them to opt.to_add. - std::list comments = read_comments(comments_input, opt.raw); + std::list comments = read_comments(comments_input, opt.raw); opt.to_add.splice(opt.to_add.begin(), std::move(comments)); } return opt; @@ -202,21 +212,21 @@ ot::options ot::parse_options(int argc, char** argv, FILE* comments_input) /** Format a UTF-8 string by adding tabulations (\t) after line feeds (\n) to mark continuation for * multiline values. */ -static std::string format_value(const std::string& source) +static std::u8string format_value(const std::u8string& source) { - auto newline_count = std::count(source.begin(), source.end(), '\n'); + auto newline_count = std::count(source.begin(), source.end(), u8'\n'); // General case: the value fits on a single line. Use std::string’s copy constructor for the // most efficient copy we could hope for. if (newline_count == 0) return source; - std::string formatted; + std::u8string formatted; formatted.reserve(source.size() + newline_count); for (auto c : source) { formatted.push_back(c); if (c == '\n') - formatted.push_back('\t'); + formatted.push_back(u8'\t'); } return formatted; } @@ -227,11 +237,10 @@ static std::string format_value(const std::string& source) * To disambiguate between a newline embedded in a comment and a newline representing the start of * the next tag, continuation lines always have a single TAB (^I) character added to the beginning. */ -void ot::print_comments(const std::list& comments, FILE* output, bool raw) +void ot::print_comments(const std::list& comments, FILE* output, bool raw) { - std::string local; bool has_control = false; - for (const std::string& source_comment : comments) { + for (const std::u8string& source_comment : comments) { if (!has_control) { // Don’t bother analyzing comments if the flag is already up. for (unsigned char c : source_comment) { if (c < 0x20 && c != '\n') { @@ -241,46 +250,43 @@ void ot::print_comments(const std::list& comments, FILE* output, bo } } - std::string utf8_comment = format_value(source_comment); - const std::string* comment; + std::u8string utf8_comment = format_value(source_comment); // Convert the comment from UTF-8 to the system encoding if relevant. if (raw) { - comment = &utf8_comment; + fwrite(utf8_comment.data(), 1, utf8_comment.size(), output); } else { try { - local = from_utf8(utf8_comment); - comment = &local; + std::string local = decode_utf8(utf8_comment); + fwrite(local.data(), 1, local.size(), output); } catch (ot::status& rc) { rc.message += " See --raw."; throw; } } - - fwrite(comment->data(), 1, comment->size(), output); putc('\n', output); } if (has_control) fputs("warning: Some tags contain control characters.\n", stderr); } -std::list ot::read_comments(FILE* input, bool raw) +std::list ot::read_comments(FILE* input, bool raw) { - std::list comments; + std::list comments; comments.clear(); char* source_line = nullptr; size_t buflen = 0; ssize_t nread; - std::string* previous_comment = nullptr; + std::u8string* previous_comment = nullptr; while ((nread = getline(&source_line, &buflen, input)) != -1) { if (nread > 0 && source_line[nread - 1] == '\n') --nread; // Chomp. - std::string line; + std::u8string line; if (raw) { - line = std::string(source_line, nread); + line = std::u8string(reinterpret_cast(source_line), nread); } else { try { - line = to_utf8(std::string_view(source_line, nread)); + line = encode_utf8(std::string_view(source_line, nread)); } catch (const ot::status& rc) { free(source_line); throw ot::status {ot::st::badly_encoded, "UTF-8 conversion error: " + rc.message}; @@ -290,10 +296,10 @@ std::list ot::read_comments(FILE* input, bool raw) if (line.empty()) { // Ignore empty lines. previous_comment = nullptr; - } else if (line[0] == '#') { + } else if (line[0] == u8'#') { // Ignore comments. previous_comment = nullptr; - } else if (line[0] == '\t') { + } else if (line[0] == u8'\t') { // Continuation line: append the current line to the previous tag. if (previous_comment == nullptr) { ot::status rc = {ot::st::error, "Unexpected continuation line: " + std::string(source_line, nread)}; @@ -303,7 +309,7 @@ std::list ot::read_comments(FILE* input, bool raw) line[0] = '\n'; previous_comment->append(line); } - } else if (line.find('=') == std::string::npos) { + } else if (line.find(u8'=') == decltype(line)::npos) { ot::status rc = {ot::st::error, "Malformed tag: " + std::string(source_line, nread)}; free(source_line); throw rc; @@ -315,19 +321,20 @@ std::list ot::read_comments(FILE* input, bool raw) return comments; } -void ot::delete_comments(std::list& comments, const std::string& selector) +void ot::delete_comments(std::list& comments, const std::u8string& selector) { auto name = selector.data(); - auto equal = selector.find('='); - auto value = (equal == std::string::npos ? nullptr : name + equal + 1); + auto equal = selector.find(u8'='); + auto value = (equal == std::u8string::npos ? nullptr : name + equal + 1); auto name_len = value ? equal : selector.size(); auto value_len = value ? selector.size() - equal - 1 : 0; auto it = comments.begin(), end = comments.end(); while (it != end) { auto current = it++; + /** \todo Avoid using strncasecmp because it assumes the system locale is UTF-8. */ bool name_match = current->size() > name_len + 1 && (*current)[name_len] == '=' && - strncasecmp(current->data(), name, name_len) == 0; + strncasecmp((const char*) current->data(), (const char*) name, name_len) == 0; if (!name_match) continue; bool value_match = value == nullptr || @@ -343,11 +350,11 @@ static void edit_tags(ot::opus_tags& tags, const ot::options& opt) { if (opt.delete_all) { tags.comments.clear(); - } else for (const std::string& name : opt.to_delete) { - ot::delete_comments(tags.comments, name.c_str()); + } else for (const std::u8string& name : opt.to_delete) { + ot::delete_comments(tags.comments, name); } - for (const std::string& comment : opt.to_add) + for (const std::u8string& comment : opt.to_add) tags.comments.emplace_back(comment); } diff --git a/src/opus.cc b/src/opus.cc index 1c02535..6a6ce68 100644 --- a/src/opus.cc +++ b/src/opus.cc @@ -30,14 +30,14 @@ ot::opus_tags ot::parse_tags(const ogg_packet& packet) if (packet.bytes < 0) throw status {st::int_overflow, "Overflowing comment header length"}; size_t size = static_cast(packet.bytes); - const char* data = reinterpret_cast(packet.packet); + const uint8_t* data = reinterpret_cast(packet.packet); size_t pos = 0; opus_tags my_tags; // Magic number if (8 > size) throw status {st::cut_magic_number, "Comment header too short for the magic number"}; - if (memcmp(data, "OpusTags", 8) != 0) + if (memcmp(data, u8"OpusTags", 8) != 0) throw status {st::bad_magic_number, "Comment header did not start with OpusTags"}; // Vendor @@ -48,7 +48,7 @@ ot::opus_tags ot::parse_tags(const ogg_packet& packet) size_t vendor_length = le32toh(*((uint32_t*) (data + pos))); if (pos + 4 + vendor_length > size) throw status {st::cut_vendor_data, "Vendor string did not fit the comment header"}; - my_tags.vendor = std::string(data + pos + 4, vendor_length); + my_tags.vendor = std::u8string(reinterpret_cast(&data[pos + 4]), vendor_length); pos += 4 + my_tags.vendor.size(); // Comment count @@ -66,13 +66,13 @@ ot::opus_tags ot::parse_tags(const ogg_packet& packet) if (pos + 4 + comment_length > size) throw status {st::cut_comment_data, "Comment string did not fit the comment header"}; - const char *comment_value = data + pos + 4; + auto comment_value = reinterpret_cast(&data[pos + 4]); my_tags.comments.emplace_back(comment_value, comment_length); pos += 4 + comment_length; } // Extra data - my_tags.extra_data = std::string(data + pos, size - pos); + my_tags.extra_data = byte_string(data + pos, size - pos); return my_tags; } @@ -80,7 +80,7 @@ ot::opus_tags ot::parse_tags(const ogg_packet& packet) ot::dynamic_ogg_packet ot::render_tags(const opus_tags& tags) { size_t size = 8 + 4 + tags.vendor.size() + 4; - for (const std::string& comment : tags.comments) + for (const std::u8string& comment : tags.comments) size += 4 + comment.size(); size += tags.extra_data.size(); @@ -100,7 +100,7 @@ ot::dynamic_ogg_packet ot::render_tags(const opus_tags& tags) n = htole32(tags.comments.size()); memcpy(data, &n, 4); data += 4; - for (const std::string& comment : tags.comments) { + for (const std::u8string& comment : tags.comments) { n = htole32(comment.size()); memcpy(data, &n, 4); memcpy(data+4, comment.data(), comment.size()); @@ -166,8 +166,8 @@ ot::byte_string ot::picture::serialize() const */ std::optional ot::extract_cover(const ot::opus_tags& tags) { - static const std::string_view prefix = "METADATA_BLOCK_PICTURE="sv; - auto is_cover = [](const std::string& tag) { return tag.starts_with(prefix); }; + static const std::u8string_view prefix = u8"METADATA_BLOCK_PICTURE="sv; + auto is_cover = [](const std::u8string& tag) { return tag.starts_with(prefix); }; auto cover_tag = std::find_if(tags.comments.begin(), tags.comments.end(), is_cover); if (cover_tag == tags.comments.end()) return {}; // No cover art. @@ -177,7 +177,7 @@ std::optional ot::extract_cover(const ot::opus_tags& tags) fputs("warning: Found multiple covers; only the first will be extracted." " Please report your use case if you need a finer selection.\n", stderr); - std::string_view cover_value = *cover_tag; + std::u8string_view cover_value = *cover_tag; cover_value.remove_prefix(prefix.size()); return picture(decode_base64(cover_value)); } @@ -202,10 +202,10 @@ static ot::byte_string_view detect_mime_type(ot::byte_string_view data) return "application/octet-stream"_bsv; } -std::string ot::make_cover(ot::byte_string_view picture_data) +std::u8string ot::make_cover(ot::byte_string_view picture_data) { picture pic; pic.mime_type = detect_mime_type(picture_data); pic.picture_data = picture_data; - return "METADATA_BLOCK_PICTURE=" + encode_base64(pic.serialize()); + return u8"METADATA_BLOCK_PICTURE=" + encode_base64(pic.serialize()); } diff --git a/src/opustags.h b/src/opustags.h index cfb80d8..a97479e 100644 --- a/src/opustags.h +++ b/src/opustags.h @@ -186,8 +186,8 @@ void run_editor(std::string_view editor, std::string_view path); */ timespec get_file_timestamp(const char* path); -std::string encode_base64(byte_string_view src); -byte_string decode_base64(std::string_view src); +std::u8string encode_base64(byte_string_view src); +byte_string decode_base64(std::u8string_view src); /** \} */ @@ -361,7 +361,7 @@ struct opus_tags { * OpusTags packets begin with a vendor string, meant to identify the implementation of the * encoder. It is expected to be an arbitrary UTF-8 string. */ - std::string vendor; + std::u8string vendor; /** * Comments are strings in the NAME=Value format. A comment may also be called a field, or a * tag. @@ -370,7 +370,7 @@ struct opus_tags { * can be any valid UTF-8 string. The specification is not too clear for Opus, but let's * assume it's the same. */ - std::list comments; + std::list comments; /** * According to RFC 7845: * > Immediately following the user comment list, the comment header MAY contain @@ -382,7 +382,7 @@ struct opus_tags { * In the future, we could add options to manipulate this data: view it, edit it, truncate * it if it's marked as padding, truncate it unconditionally. */ - std::string extra_data; + byte_string extra_data; }; /** @@ -428,7 +428,7 @@ std::optional extract_cover(const opus_tags& tags); * Return a METADATA_BLOCK_PICTURE tag defining the front cover art to the given picture data (JPEG, * PNG). The MIME type is deduced from the magic number. */ -std::string make_cover(byte_string_view picture_data); +std::u8string make_cover(byte_string_view picture_data); /** \} */ @@ -493,11 +493,9 @@ struct options { * #to_add takes precedence over #to_delete, so if the same comment appears in both lists, * the one in #to_delete applies only to the previously existing tags. * - * The strings are stored in UTF-8. - * * Option: --delete, --set */ - std::list to_delete; + std::list to_delete; /** * Delete all the existing comments. * @@ -508,11 +506,9 @@ struct options { * List of comments to add, in the current system encoding. For exemple `TITLE=a b c`. They * must be valid. * - * The strings are stored in UTF-8. - * * Options: --add, --set, --set-all */ - std::list to_add; + std::list to_add; /** * If set, the input file’s cover art is exported to the specified file. - for stdout. Does * not overwrite the file if it already exists unless -y is specified. Does nothing if the @@ -544,21 +540,19 @@ options parse_options(int argc, char** argv, FILE* comments); * * The output generated is meant to be parseable by #ot::read_comments. */ -void print_comments(const std::list& comments, FILE* output, bool raw); +void print_comments(const std::list& comments, FILE* output, bool raw); /** * Parse the comments outputted by #ot::print_comments. Unless raw is true, the comments are * converted from the system encoding to UTF-8, and returned as UTF-8. */ -std::list read_comments(FILE* input, bool raw); +std::list read_comments(FILE* input, bool raw); /** * Remove all comments matching the specified selector, which may either be a field name or a * NAME=VALUE pair. The field name is case-insensitive. - * - * The strings are all UTF-8. */ -void delete_comments(std::list& comments, const std::string& selector); +void delete_comments(std::list& comments, const std::u8string& selector); /** * Main entry point to the opustags program, and pretty much the same as calling opustags from the diff --git a/t/base64.cc b/t/base64.cc index a775d46..d29d525 100644 --- a/t/base64.cc +++ b/t/base64.cc @@ -3,35 +3,35 @@ static void check_encode_base64() { - 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"); + opaque_is(ot::encode_base64(""_bsv), u8"", "empty"); + opaque_is(ot::encode_base64("a"_bsv), u8"YQ==", "1 character"); + opaque_is(ot::encode_base64("aa"_bsv), u8"YWE=", "2 characters"); + opaque_is(ot::encode_base64("aaa"_bsv), u8"YWFh", "3 characters"); + opaque_is(ot::encode_base64("aaaa"_bsv), u8"YWFhYQ==", "4 characters"); + opaque_is(ot::encode_base64("\xFF\xFF\xFE"_bsv), u8"///+", "RFC alphabet"); + opaque_is(ot::encode_base64("\0x"_bsv), u8"AHg=", "embedded null bytes"); } static void check_decode_base64() { - 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"); + opaque_is(ot::decode_base64(u8""), ""_bsv, "empty"); + opaque_is(ot::decode_base64(u8"YQ=="), "a"_bsv, "1 character"); + opaque_is(ot::decode_base64(u8"YWE="), "aa"_bsv, "2 characters"); + opaque_is(ot::decode_base64(u8"YQ"), "a"_bsv, "padless 1 character"); + opaque_is(ot::decode_base64(u8"YWE"), "aa"_bsv, "padless 2 characters"); + opaque_is(ot::decode_base64(u8"YWFh"), "aaa"_bsv, "3 characters"); + opaque_is(ot::decode_base64(u8"YWFhYQ=="), "aaaa"_bsv, "4 characters"); + opaque_is(ot::decode_base64(u8"///+"), "\xFF\xFF\xFE"_bsv, "RFC alphabet"); + opaque_is(ot::decode_base64(u8"AHg="), "\0x"_bsv, "embedded null bytes"); try { - ot::decode_base64("Y==="); + ot::decode_base64(u8"Y==="); throw failure("accepted a bad block size"); } catch (const ot::status& e) { } try { - ot::decode_base64("\xFF bad message!"); + ot::decode_base64(u8"\xFF bad message!"); throw failure("accepted an invalid character"); } catch (const ot::status& e) { } diff --git a/t/cli.cc b/t/cli.cc index bb847ad..d8e1577 100644 --- a/t/cli.cc +++ b/t/cli.cc @@ -3,7 +3,7 @@ #include -static ot::status read_comments(FILE* input, std::list& comments, bool raw) +static ot::status read_comments(FILE* input, std::list& comments, bool raw) { try { comments = ot::read_comments(input, raw); @@ -15,7 +15,7 @@ static ot::status read_comments(FILE* input, std::list& comments, b void check_read_comments() { - std::list comments; + std::list comments; ot::status rc; { std::string txt = "TITLE=a b c\n\nARTIST=X\nArtist=Y\n"s; @@ -23,7 +23,7 @@ void check_read_comments() rc = read_comments(input.get(), comments, false); if (rc != ot::st::ok) throw failure("could not read comments"); - auto&& expected = {"TITLE=a b c", "ARTIST=X", "Artist=Y"}; + auto&& expected = {u8"TITLE=a b c", u8"ARTIST=X", u8"Artist=Y"}; if (!std::equal(comments.begin(), comments.end(), expected.begin(), expected.end())) throw failure("parsed user comments did not match expectations"); } @@ -40,7 +40,7 @@ void check_read_comments() rc = read_comments(input.get(), comments, true); if (rc != ot::st::ok) throw failure("could not read comments"); - if (comments.front() != "RAW=\xFF\xFF") + if (comments.front() != (char8_t*) "RAW=\xFF\xFF") throw failure("parsed user comments did not match expectations"); } { @@ -49,7 +49,7 @@ void check_read_comments() rc = read_comments(input.get(), comments, true); if (rc != ot::st::ok) throw failure("could not read comments"); - if (comments.front() != "MULTILINE=First\nSecond") + if (comments.front() != u8"MULTILINE=First\nSecond") throw failure("parsed user comments did not match expectations"); } { @@ -109,14 +109,14 @@ void check_good_arguments() opt = parse({"opustags", "x", "--output", "y", "-D", "-s", "X=Y Z", "-d", "a=b"}); if (opt.paths_in.size() != 1 || opt.paths_in.front() != "x" || !opt.path_out || opt.path_out != "y" || !opt.delete_all || opt.overwrite || opt.to_delete.size() != 2 || - opt.to_delete.front() != "X" || *std::next(opt.to_delete.begin()) != "a=b" || - opt.to_add != std::list{"X=Y Z"}) + opt.to_delete.front() != u8"X" || *std::next(opt.to_delete.begin()) != u8"a=b" || + opt.to_add != std::list{ u8"X=Y Z" }) throw failure("unexpected option parsing result for case #1"); opt = parse({"opustags", "-S", "x", "-S", "-a", "x=y z", "-i"}); if (opt.paths_in.size() != 1 || opt.paths_in.front() != "x" || opt.path_out || !opt.overwrite || opt.to_delete.size() != 0 || - opt.to_add != std::list{"N=1", "x=y z"}) + opt.to_add != std::list{ u8"N=1", u8"x=y z" }) throw failure("unexpected option parsing result for case #2"); opt = parse({"opustags", "-i", "x", "y", "z"}); @@ -130,7 +130,7 @@ void check_good_arguments() throw failure("unexpected option parsing result for case #4"); opt = parse({"opustags", "-a", "X=\xFF", "--raw", "x"}); - if (!opt.raw || opt.to_add.front() != "X=\xFF") + if (!opt.raw || opt.to_add.front() != u8"X=\xFF") throw failure("--raw did not disable transcoding"); } @@ -198,23 +198,23 @@ void check_bad_arguments() static void check_delete_comments() { - using C = std::list; - C original = {"TITLE=X", "Title=Y", "Title=Z", "ARTIST=A", "artIst=B"}; + using C = std::list; + C original = {u8"TITLE=X", u8"Title=Y", u8"Title=Z", u8"ARTIST=A", u8"artIst=B"}; C edited = original; - ot::delete_comments(edited, "derp"); + ot::delete_comments(edited, u8"derp"); if (!std::equal(edited.begin(), edited.end(), original.begin(), original.end())) throw failure("should not have deleted anything"); - ot::delete_comments(edited, "Title"); - C expected = {"ARTIST=A", "artIst=B"}; + ot::delete_comments(edited, u8"Title"); + C expected = {u8"ARTIST=A", u8"artIst=B"}; if (!std::equal(edited.begin(), edited.end(), expected.begin(), expected.end())) throw failure("did not delete all titles correctly"); edited = original; - ot::delete_comments(edited, "titlE=Y"); - ot::delete_comments(edited, "Title=z"); - expected = {"TITLE=X", "Title=Z", "ARTIST=A", "artIst=B"}; + ot::delete_comments(edited, u8"titlE=Y"); + ot::delete_comments(edited, u8"Title=z"); + expected = {u8"TITLE=X", u8"Title=Z", u8"ARTIST=A", u8"artIst=B"}; if (!std::equal(edited.begin(), edited.end(), expected.begin(), expected.end())) throw failure("did not delete a specific title correctly"); } diff --git a/t/opus.cc b/t/opus.cc index 8188530..cc4c9c7 100644 --- a/t/opus.cc +++ b/t/opus.cc @@ -16,15 +16,15 @@ static void parse_standard() op.bytes = sizeof(standard_OpusTags) - 1; op.packet = (unsigned char*) standard_OpusTags; ot::opus_tags tags = ot::parse_tags(op); - if (tags.vendor != "opustags test packet") + if (tags.vendor != u8"opustags test packet") throw failure("bad vendor string"); if (tags.comments.size() != 2) throw failure("bad number of comments"); auto it = tags.comments.begin(); - if (*it != "TITLE=Foo") + if (*it != u8"TITLE=Foo") throw failure("bad title"); ++it; - if (*it != "ARTIST=Bar") + if (*it != u8"ARTIST=Bar") throw failure("bad artist"); if (tags.extra_data.size() != 0) throw failure("found mysterious padding data"); @@ -123,7 +123,7 @@ static void recode_padding() op.packet = (unsigned char*) padded_OpusTags.data(); ot::opus_tags tags = ot::parse_tags(op); - if (tags.extra_data != "\0hello"s) + if (tags.extra_data != "\0hello"_bsv) throw failure("corrupted extra data"); // recode the packet and ensure it's exactly the same auto packet = ot::render_tags(tags); @@ -148,7 +148,7 @@ static void extract_cover() "\x00\x00\x00\x0C" "Picture data"; ot::opus_tags tags; - tags.comments = { "METADATA_BLOCK_PICTURE=" + ot::encode_base64(picture_data) }; + tags.comments = { u8"METADATA_BLOCK_PICTURE=" + ot::encode_base64(picture_data) }; std::optional cover = ot::extract_cover(tags); if (!cover) throw failure("could not extract the cover"); @@ -158,7 +158,7 @@ static void extract_cover() throw failure("bad extracted picture data"); ot::byte_string_view truncated_data = picture_data.substr(0, picture_data.size() - 1); - tags.comments = { "METADATA_BLOCK_PICTURE=" + ot::encode_base64(truncated_data) }; + tags.comments = { u8"METADATA_BLOCK_PICTURE=" + ot::encode_base64(truncated_data) }; try { ot::extract_cover(tags); throw failure("accepted a bad picture block"); @@ -177,8 +177,8 @@ static void make_cover() "\x00\x00\x00\x00" // Palette size. "\x00\x00\x00\x11" "\x89PNG Picture data"; - std::string expected = "METADATA_BLOCK_PICTURE=" + ot::encode_base64(picture_block); - is(ot::make_cover("\x89PNG Picture data"_bsv), expected, "build the picture tag"); + std::u8string expected = u8"METADATA_BLOCK_PICTURE=" + ot::encode_base64(picture_block); + opaque_is(ot::make_cover("\x89PNG Picture data"_bsv), expected, "build the picture tag"); } int main()