Introduce byte strings

This commit is contained in:
Frédéric Mangano 2023-02-28 16:40:27 +09:00
parent 92b320f9d9
commit 74e42ee917
7 changed files with 65 additions and 43 deletions

View File

@ -13,10 +13,10 @@
#include <cstring>
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<const unsigned char*>(src.data());
const unsigned char* end = in + len;
unsigned char* pos = reinterpret_cast<unsigned char*>(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<unsigned char*>(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;

View File

@ -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<const uint8_t*>(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<const uint32_t*>(bytes + mime_offset));
uint32_t mime_size = be32toh(*reinterpret_cast<const uint32_t*>(&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<const uint32_t*>(bytes + desc_offset));
uint32_t desc_size = be32toh(*reinterpret_cast<const uint32_t*>(&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<const uint32_t*>(bytes + pic_offset));
uint32_t pic_size = be32toh(*reinterpret_cast<const uint32_t*>(&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<const char*>(bytes + mime_offset + 4), mime_size);
picture_data = std::string_view(reinterpret_cast<const char*>(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);
}
/**

View File

@ -111,6 +111,9 @@ struct status {
std::string message;
};
using byte_string = std::basic_string<uint8_t>;
using byte_string_view = std::basic_string_view<uint8_t>;
/***********************************************************************************************//**
* \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);

View File

@ -18,6 +18,16 @@
#include <sys/wait.h>
#include <unistd.h>
ot::byte_string operator""_bs(const char* data, size_t size)
{
return ot::byte_string(reinterpret_cast<const uint8_t*>(data), size);
}
ot::byte_string_view operator""_bsv(const char* data, size_t size)
{
return ot::byte_string_view(reinterpret_cast<const uint8_t*>(data), size);
}
void ot::partial_file::open(const char* destination)
{
final_name = destination;

View File

@ -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===");

View File

@ -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<ot::picture> 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);

View File

@ -51,6 +51,13 @@ void is(const T& got, const U& expected, const char* name)
}
}
template <typename T, typename U>
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)
{