12 Commits

Author SHA1 Message Date
cd99ac50c7 Sanitize implicit conversions
Now, clang’s sanitizers do not report any warning when running the test suite with
`-fsanitize=address,undefined,implicit-conversion`.
2025-06-22 16:07:36 +09:00
82a5124f14 Eliminate std::basic_string<uint8_t>
They’ve been removed in LLVM.
e30a148b09
2025-06-22 15:41:08 +09:00
6c0d4fc297 fix release ci install path 2025-06-21 11:03:14 +09:00
c74e19922f create .deb package release ci 2025-06-21 11:03:14 +09:00
5c1a7b3a99 Mention METADATA_BLOCK_PICTURE in the man page 2025-03-12 11:38:38 +09:00
b70e65f0d4 Fix CI 2025-02-15 11:19:16 +09:00
fc7e5e939e Fix typos and formatting in manpage 2025-01-10 22:21:57 +09:00
e8b66a6207 Fix some sanitizer errors of misaligned pointers 2024-11-07 16:40:51 +09:00
ba5c151b5d Add GitHub Action 2024-11-07 16:40:51 +09:00
76afc0efd5 Fix string out-of-bounds access 2024-11-06 15:05:50 +09:00
a54bac8f55 Fix the warning on comparison of size_t and long 2024-11-01 10:30:12 +09:00
3293647e8f Wrap fclose to avoid compiler warnings 2024-11-01 10:20:49 +09:00
12 changed files with 207 additions and 86 deletions

30
.github/workflows/ci.yaml vendored Normal file
View File

@ -0,0 +1,30 @@
name: Continuous Integration
on:
push:
branches: [master]
pull_request:
branches: [master]
workflow_dispatch:
env:
LC_CTYPE: C.UTF-8
CMAKE_COLOR_DIAGNOSTICS: ON
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout git repository
uses: actions/checkout@v4
- name: Install dependencies
run: |
sudo apt install cmake g++ pkg-config libogg-dev ffmpeg libtest-harness-perl libtest-deep-perl liblist-moreutils-perl libtest-utf8-perl
- name: Build
env:
CXX: g++
CXXFLAGS: -D_FORTIFY_SOURCE=3 -D_GLIBCXX_ASSERTIONS -D_GLIBCXX_DEBUG -O2 -flto=auto -g -Wall -Wextra -Werror=format-security -fstack-protector-strong -fstack-clash-protection -fcf-protection -fsanitize=address,undefined
LDFLAGS: -fsanitize=address,undefined
run: |
cmake -B target -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON
cmake --build target
- name: Test
run: |
cmake --build target --target check

83
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,83 @@
name: Release
on:
release:
types: [published]
jobs:
extract-tag:
name: Extract release tag
runs-on: ubuntu-latest
outputs:
tag: ${{ env.RELEASE_TAG }}
version: ${{ env.RELEASE_VERSION }}
steps:
- name: Parse refs
run: |
RELEASE_TAG=${GITHUB_REF#refs/*/}
test -z $RELEASE_TAG && exit 1
RELEASE_VERSION=$(echo "$RELEASE_TAG" | sed 's/^[^0-9]*//')
test -z $RELEASE_VERSION && exit 1
echo "Releasing version: $RELEASE_VERSION; Tag: $RELEASE_TAG"
echo "RELEASE_VERSION=$RELEASE_VERSION" >> $GITHUB_ENV
echo "RELEASE_TAG=$RELEASE_TAG" >> $GITHUB_ENV
build:
name: Build
runs-on: ubuntu-22.04
needs: ["extract-tag"]
steps:
- name: git checkout
uses: actions/checkout@v4
- name: Install dependencies
run: sudo apt update && sudo apt install -y g++ cmake pkg-config libogg-dev libogg0
- name: CMake build
run: |
mkdir build
cmake -DCMAKE_INSTALL_PREFIX=/usr -S .
make
make install DESTDIR=./build
- name: Create control file
run: |
mkdir -p build/DEBIAN
cat << EOF > build/DEBIAN/control
Package: opustags
Version: ${{ needs.extract-tag.outputs.version }}
Architecture: amd64
Maintainer: github.com/fmang
Depends: libogg0 (>= 1.3.4) | libogg (>= 1.3.4)
Priority: optional
Description: Ogg Opus tags editor
EOF
- name: Create package file
run: |
dpkg-deb -v --build ./build
mv build.deb opustags-${{ needs.extract-tag.outputs.version }}-amd64.deb
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: debian-pkg
path: opustags-${{ needs.extract-tag.outputs.version }}-amd64.deb
upload-assets:
name: Upload release assets
needs: ["extract-tag", "build"]
runs-on: ubuntu-latest
steps:
- name: Download all workflow run artifacts
uses: actions/download-artifact@v4
with:
merge-multiple: true
path: .
- name: Upload .deb package file
run: |
gh release upload ${{ needs.extract-tag.outputs.tag }} opustags-${{ needs.extract-tag.outputs.version }}-amd64.deb
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}

View File

@ -1,4 +1,4 @@
.TH opustags 1 "April 2024" "@PROJECT_NAME@ @PROJECT_VERSION@"
.TH opustags 1 "March 2025" "@PROJECT_NAME@ @PROJECT_VERSION@"
.SH NAME
opustags \- Ogg Opus tag editor
.SH SYNOPSIS
@ -43,13 +43,13 @@ to set new tags without being bothered by the old ones.
If you want to replace all the tags, you can use the \fB--set-all\fP option which will cause
\fBopustags\fP to read tags from standard input.
The format is the same as the one used for output: newline-separated \fIFIELD=Value\fP assignment.
All the previously existing tags as deleted.
All the previously existing tags are deleted.
.PP
The Opus format specifications requires that tags are encoded in UTF-8, so that's the only encoding
opustags supports. If your system encoding is different, the tags are automatically converted to and
from your system locale. When you edit an Opus file whose tags contains characters unsupported by
your system encoding, the original UTF-8 values will be preserved for the tags you don't explicitly
modify.
The Opus format specification requires that tags are encoded in UTF-8, so thats the only encoding
\fBopustags\fP supports. If your system encoding is different, the tags are automatically converted
to and from your system locale. When you edit an Opus file whose tags contain characters unsupported
by your system encoding, the original UTF-8 values will be preserved for the tags you dont
explicitly modify.
.SH OPTIONS
.TP
.B \-h, \-\-help
@ -67,7 +67,7 @@ setting \fB--output\fP to the same path as the input file and enabling \fB--over
This option conflicts with \fB--output\fP.
.TP
.B \-y, \-\-overwrite
By default, \fBopustags\fP refuses to overwrite an already-existent file.
By default, \fBopustags\fP refuses to overwrite an already-existing file.
Use \fB-y\fP to allow overwriting.
Note that this option is not needed when the output is a special file like \fI/dev/null\fP.
.TP
@ -79,10 +79,10 @@ In both cases, the field names are case-insensitive, and expected to be ASCII.
.B \-a, \-\-add \fIFIELD=VALUE\fP
Add a tag. Note that multiple tags with the same field name are perfectly acceptable, so you can add
multiple fields with the same name, and previously existing tags will also be preserved.
When the \fB--delete\fP is used with the same \fIFIELD\fP, only the older tags are deleted.
When \fB--delete\fP is used with the same \fIFIELD\fP, only the older tags are deleted.
.TP
.B \-s, \-\-set \fIFIELD=VALUE\fP
This option is provided for convenience. It delete all the fields of the same
This option is provided for convenience. It deletes all the fields of the same
type that may already exist, then adds it with the wanted value.
This is strictly equivalent to \fB--delete\fP \fIFIELD\fP \fB--add\fP
\fIFIELD=VALUE\fP. You can combine it with \fB--add\fP to add tags of the same
@ -93,33 +93,35 @@ added with \fB--add\fP.
Delete all the previously existing tags.
.TP
.B \-S, \-\-set-all
Sets the tags from scratch.
Set the tags from scratch.
All the original tags are deleted and new ones are read from standard input.
Each line must specify a \fIFIELD=VALUE\fP pair and be separated with line feeds.
Empty lines and lines starting with \fI#\fP are ignored.
Multiline tags must have their continuation lines prefixed by a single tab (in other words, every
Multi-line tags must have their continuation lines prefixed by a single tab (in other words, every
\fI\\n\fP must be replaced by \fI\\n\\t\fP).
.TP
.B \-e, \-\-edit
Edit tags interactively by spawning the program specified by the EDITOR
environment variable. The allowed format is the same as \fB--set-all\fP.
environment variable. The allowed format is the same as with \fB--set-all\fP.
If TERM and VISUAL are set, VISUAL takes precedence over EDITOR.
.TP
.B \-\-output-cover \fIFILE\fP
Save the cover art of the input Opus file to the specified location.
Extract the cover art from the \fBMETADATA_BLOCK_PICTURE\fP tag into the specified location.
If the input file does not contain any cover art, this option has no effect.
To allow overwriting the target location, specify \fB--overwrite\fP.
In the case of multiple pictures embedded in the Opus tags, only the first one is saved.
Note that the since the image format is not fixed, you should consider an extension-less file name
and rely on the magic number to deduce the type. opustags does not add or check the target files
extension.
Note that since the image format is not fixed, you should consider an extension-less file name and
rely on the magic number to deduce the type.
\fBopustags\fP does not add or check the target files extension.
You can specify \fB-\fP for standard output, in which case the regular output will be suppressed.
.TP
.B \-\-set-cover \fIFILE\fP
Replace or set the cover art to the specified picture.
Set the cover art by embedding the specified picture into the \fBMETADATA_BLOCK_PICTURE\fP tag,
replacing any existing values.
Specify \fB-\fP to read the picture from standard input.
In theory, an Opus file may contain multiple pictures with different roles, though in practice only
the front cover really matters. opustags can currently only handle one front cover and nothing else.
the front cover really matters.
\fBopustags\fP can currently only handle one front cover and nothing else.
.TP
.B \-\-vendor
Print the vendor string from the OpusTags packet and do nothing else. Standard tags operations are
@ -140,9 +142,10 @@ character set even though your system cannot display it.
When editing tags programmatically with line-based tools like grep or sed, tags containing newlines
are likely to corrupt the result because these tools wont interpret multi-line tags as a whole. To
make automatic processing easier, \fB-z\fP delimits tags by a null byte (ASCII NUL) instead of line
feeds. That same \fB-z\fP flag is also supported by GNU grep or GNU sed and, combined with opustags
-z, would make them process the input tag-by-tag instead of line-by-line, thus supporting multi-line
tags as well. This option also disables the TAB prefix for continuation lines after a line feed.
feeds. That same \fB-z\fP flag is also supported by GNU grep or GNU sed and, combined with
\fBopustags -z\fP, would make them process the input tag-by-tag instead of line-by-line, thus
supporting multi-line tags as well.
This option also disables the tab prefix for continuation lines after a line feed.
.SH EXAMPLES
.PP
List all the tags in file foo.opus:
@ -172,7 +175,7 @@ Use GNU grep to remove all the CHAPTER* tags, with -z to support multi-line tags
.SH CAVEATS
.PP
\fBopustags\fP currently has the following limitations:
.IP \[bu]
.IP \[bu] 2n
Multiplexed streams are not supported.
.IP \[bu]
Control characters inside tags are printed raw rather than being escaped.

View File

@ -27,7 +27,7 @@ std::u8string ot::encode_base64(ot::byte_string_view src)
std::u8string out;
out.resize(olen);
const uint8_t* in = src.data();
const uint8_t* in = reinterpret_cast<const uint8_t*>(src.data());
const uint8_t* end = in + len;
char8_t* pos = out.data();
while (end - in >= 3) {
@ -56,7 +56,7 @@ std::u8string ot::encode_base64(ot::byte_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() == u8'=')
while (!src.empty() && src.back() == u8'=')
src.remove_suffix(1);
size_t olen = src.size() / 4 * 3; // Whole blocks;
@ -68,7 +68,7 @@ ot::byte_string ot::decode_base64(std::u8string_view src)
ot::byte_string out;
out.resize(olen);
uint8_t* pos = out.data();
uint8_t* pos = reinterpret_cast<uint8_t*>(out.data());
unsigned char dtable[256];
memset(dtable, 0x80, 256);
@ -84,11 +84,11 @@ ot::byte_string ot::decode_base64(std::u8string_view src)
block[count++] = tmp;
if (count == 2) {
*pos++ = (block[0] << 2) | (block[1] >> 4);
*pos++ = 0xFF & (block[0] << 2) | (block[1] >> 4);
} else if (count == 3) {
*pos++ = (block[1] << 4) | (block[2] >> 2);
*pos++ = 0xFF & (block[1] << 4) | (block[2] >> 2);
} else if (count == 4) {
*pos++ = (block[2] << 6) | block[3];
*pos++ = 0xFF & (block[2] << 6) | block[3];
count = 0;
}
}

View File

@ -503,7 +503,7 @@ static void process(ot::ogg_reader& reader, ot::ogg_writer* writer, const ot::op
* output stream, we need to renumber all the succeeding pages. If the input stream
* contains gaps, the offset will naively reproduce the gaps: page numbers 0 (1) 2 4 will
* become 0 (1 2) 3 5, where (…) is the OpusTags packet, and not 0 (1 2) 3 4. */
int pageno_offset = 0;
long pageno_offset = 0;
while (reader.next_page()) {
auto serialno = ogg_page_serialno(&reader.page);

View File

@ -28,8 +28,8 @@ bool ot::ogg_reader::next_page()
while ((rc = ogg_sync_pageout(&sync, &page)) != 1) {
if (rc == -1) {
throw status {st::bad_stream,
absolute_page_no == (size_t) -1 ? "Input is not a valid Ogg file."
: "Unsynced data in stream."};
absolute_page_no == -1 ? "Input is not a valid Ogg file."
: "Unsynced data in stream."};
}
if (ogg_sync_check(&sync) != 0)
throw status {st::libogg_error, "ogg_sync_check signalled an error."};

View File

@ -55,7 +55,9 @@ ot::opus_tags ot::parse_tags(const ogg_packet& packet)
// Comment count
if (pos + 4 > size)
throw status {st::cut_comment_count, "Comment count did not fit the comment header"};
uint32_t count = le32toh(*((uint32_t*) (data + pos)));
uint32_t count;
memcpy(&count, data + pos, sizeof(count));
count = le32toh(count);
pos += 4;
// Comments' data
@ -63,7 +65,9 @@ ot::opus_tags ot::parse_tags(const ogg_packet& packet)
if (pos + 4 > size)
throw status {st::cut_comment_length,
"Comment length did not fit the comment header"};
uint32_t comment_length = le32toh(*((uint32_t*) (data + pos)));
uint32_t comment_length;
memcpy(&comment_length, data + pos, sizeof(comment_length));
comment_length = le32toh(comment_length);
if (pos + 4 + comment_length > size)
throw status {st::cut_comment_data,
"Comment string did not fit the comment header"};
@ -73,7 +77,7 @@ ot::opus_tags ot::parse_tags(const ogg_packet& packet)
}
// Extra data
my_tags.extra_data = byte_string(data + pos, size - pos);
my_tags.extra_data = byte_string(reinterpret_cast<const char*>(data + pos), size - pos);
return my_tags;
}
@ -134,12 +138,16 @@ ot::picture::picture(ot::byte_string block)
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*>(&storage[desc_offset]));
uint32_t desc_size;
memcpy(&desc_size, &storage[desc_offset], sizeof(desc_size));
desc_size = be32toh(desc_size);
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*>(&storage[pic_offset]));
uint32_t pic_size;
memcpy(&pic_size, &storage[pic_offset], sizeof(pic_size));
pic_size = be32toh(pic_size);
if (storage.size() != pic_offset + 4 + pic_size)
throw status { st::invalid_size, "invalid picture block size" };
@ -157,7 +165,8 @@ ot::byte_string ot::picture::serialize() const
*reinterpret_cast<uint32_t*>(&bytes[0]) = htobe32(3); // Picture type: front cover.
*reinterpret_cast<uint32_t*>(&bytes[mime_offset]) = htobe32(mime_type.size());
std::copy(mime_type.begin(), mime_type.end(), std::next(bytes.begin(), mime_offset + 4));
*reinterpret_cast<uint32_t*>(&bytes[pic_offset]) = htobe32(picture_data.size());
uint32_t picture_data_size = htobe32(picture_data.size());
memcpy(&bytes[pic_offset], &picture_data_size, sizeof(picture_data_size));
std::copy(picture_data.begin(), picture_data.end(), std::next(bytes.begin(), pic_offset + 4));
return bytes;
}
@ -191,16 +200,16 @@ std::optional<ot::picture> ot::extract_cover(const ot::opus_tags& tags)
static ot::byte_string_view detect_mime_type(ot::byte_string_view data)
{
static std::initializer_list<std::pair<ot::byte_string_view, ot::byte_string_view>> magic_numbers = {
{ "\xff\xd8\xff"_bsv, "image/jpeg"_bsv },
{ "\x89PNG"_bsv, "image/png"_bsv },
{ "GIF8"_bsv, "image/gif"_bsv },
{ "\xff\xd8\xff"sv, "image/jpeg"sv },
{ "\x89PNG"sv, "image/png"sv },
{ "GIF8"sv, "image/gif"sv },
};
for (auto [magic, mime] : magic_numbers) {
if (data.starts_with(magic))
return mime;
}
fputs("warning: Could not identify the MIME type of the picture; defaulting to application/octet-stream.\n", stderr);
return "application/octet-stream"_bsv;
return "application/octet-stream"sv;
}
std::u8string ot::make_cover(ot::byte_string_view picture_data)

View File

@ -111,21 +111,27 @@ struct status {
std::string message;
};
using byte_string = std::basic_string<uint8_t>;
using byte_string_view = std::basic_string_view<uint8_t>;
/**
* Alias for binary data strings. Concretely the same as regular strings but reflect the intent.
*/
using byte_string = std::string;
using byte_string_view = std::string_view;
/***********************************************************************************************//**
* \defgroup system System
* \{
*/
/** fclose wrapper for std::unique_ptrs deleter. */
void close_file(FILE*);
/**
* Smart auto-closing FILE* handle.
*
* It implictly converts from an already opened FILE*.
*/
struct file : std::unique_ptr<FILE, decltype(&fclose)> {
file(FILE* f = nullptr) : std::unique_ptr<FILE, decltype(&fclose)>(f, &fclose) {}
struct file : std::unique_ptr<FILE, decltype(&close_file)> {
file(FILE* f = nullptr) : std::unique_ptr<FILE, decltype(&close_file)>(f, &close_file) {}
};
/**
@ -260,10 +266,9 @@ struct ogg_reader {
ogg_page page;
/**
* Page number in the physical stream of the last read page, disregarding multiplexed
* streams. The first page number is 0. When no page has been read, its value is
* (size_t) -1.
* streams. The first page number is 0. When no page has been read, its value is -1.
*/
size_t absolute_page_no = -1;
long absolute_page_no = -1;
/**
* The file is our source of binary data. It is not integrated to libogg, so we need to
* handle it ourselves.
@ -581,7 +586,3 @@ 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

@ -19,14 +19,9 @@
#include <sys/wait.h>
#include <unistd.h>
ot::byte_string operator""_bs(const char* data, size_t size)
void ot::close_file(FILE* file)
{
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);
fclose(file);
}
void ot::partial_file::open(const char* destination)
@ -122,9 +117,9 @@ ot::byte_string ot::slurp_binary_file(const char* filename)
byte_string content;
long file_size = get_file_size(f.get());
if (file_size == -1) {
if (file_size < 0) {
// Read the input stream block by block and resize the output byte string as needed.
uint8_t buffer[4096];
char buffer[4096];
while (!feof(f.get())) {
size_t read_len = fread(buffer, 1, sizeof(buffer), f.get());
content.append(buffer, read_len);
@ -135,7 +130,7 @@ ot::byte_string ot::slurp_binary_file(const char* filename)
} else {
// Lucky! We know the file size, so lets slurp it at once.
content.resize(file_size);
if (fread(content.data(), 1, file_size, f.get()) < file_size)
if (fread(content.data(), 1, file_size, f.get()) < size_t(file_size))
throw status { st::standard_error,
"Could not read '"s + filename + "': " + strerror(errno) + "." };
}

View File

@ -3,26 +3,26 @@
static void check_encode_base64()
{
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");
opaque_is(ot::encode_base64(""sv), u8"", "empty");
opaque_is(ot::encode_base64("a"sv), u8"YQ==", "1 character");
opaque_is(ot::encode_base64("aa"sv), u8"YWE=", "2 characters");
opaque_is(ot::encode_base64("aaa"sv), u8"YWFh", "3 characters");
opaque_is(ot::encode_base64("aaaa"sv), u8"YWFhYQ==", "4 characters");
opaque_is(ot::encode_base64("\xFF\xFF\xFE"sv), u8"///+", "RFC alphabet");
opaque_is(ot::encode_base64("\0x"sv), u8"AHg=", "embedded null bytes");
}
static void check_decode_base64()
{
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");
opaque_is(ot::decode_base64(u8""), ""sv, "empty");
opaque_is(ot::decode_base64(u8"YQ=="), "a"sv, "1 character");
opaque_is(ot::decode_base64(u8"YWE="), "aa"sv, "2 characters");
opaque_is(ot::decode_base64(u8"YQ"), "a"sv, "padless 1 character");
opaque_is(ot::decode_base64(u8"YWE"), "aa"sv, "padless 2 characters");
opaque_is(ot::decode_base64(u8"YWFh"), "aaa"sv, "3 characters");
opaque_is(ot::decode_base64(u8"YWFhYQ=="), "aaaa"sv, "4 characters");
opaque_is(ot::decode_base64(u8"///+"), "\xFF\xFF\xFE"sv, "RFC alphabet");
opaque_is(ot::decode_base64(u8"AHg="), "\0x"sv, "embedded null bytes");
try {
ot::decode_base64(u8"Y===");

View File

@ -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"_bsv)
if (tags.extra_data != "\0hello"sv)
throw failure("corrupted extra data");
// recode the packet and ensure it's exactly the same
auto packet = ot::render_tags(tags);
@ -137,7 +137,7 @@ static void recode_padding()
static void extract_cover()
{
ot::byte_string_view picture_data = ""_bsv
ot::byte_string_view picture_data = ""sv
"\x00\x00\x00\x03" // Picture type 3.
"\x00\x00\x00\x09" "image/foo" // MIME type.
"\x00\x00\x00\x00" "" // Description.
@ -152,9 +152,9 @@ 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"_bsv)
if (cover->mime_type != "image/foo"sv)
throw failure("bad extracted MIME type");
if (cover->picture_data != "Picture data"_bsv)
if (cover->picture_data != "Picture data"sv)
throw failure("bad extracted picture data");
ot::byte_string_view truncated_data = picture_data.substr(0, picture_data.size() - 1);
@ -167,7 +167,7 @@ static void extract_cover()
static void make_cover()
{
ot::byte_string_view picture_block = ""_bsv
ot::byte_string_view picture_block = ""sv
"\x00\x00\x00\x03" // Picture type 3.
"\x00\x00\x00\x09" "image/png" // MIME type.
"\x00\x00\x00\x00" "" // Description.
@ -178,7 +178,7 @@ static void make_cover()
"\x00\x00\x00\x11" "\x89PNG Picture data";
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");
opaque_is(ot::make_cover("\x89PNG Picture data"sv), expected, "build the picture tag");
}
int main()

View File

@ -36,7 +36,7 @@ void check_partial_files()
void check_slurp()
{
static const ot::byte_string_view pixel = ""_bsv
static const ot::byte_string_view pixel = ""sv
"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d"
"\x49\x48\x44\x52\x00\x00\x00\x01\x00\x00\x00\x01"
"\x08\x02\x00\x00\x00\x90\x77\x53\xde\x00\x00\x00"