mirror of
https://github.com/fmang/opustags.git
synced 2025-01-15 12:43:17 +01:00
Introduce byte strings
This commit is contained in:
parent
92b320f9d9
commit
74e42ee917
@ -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;
|
||||
|
14
src/opus.cc
14
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<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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
32
t/base64.cc
32
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===");
|
||||
|
@ -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);
|
||||
|
7
t/tap.h
7
t/tap.h
@ -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)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user