tag/Handler: pass StringView to OnTag() and OnPair()

Eliminates a number of allocations, because callers don't need to copy
the strings to a newly allocated buffer only to null-terminate them.
And most callers don't need to have a null-terminated string.
This commit is contained in:
Max Kellermann 2019-06-06 12:02:55 +02:00
parent 76eb550011
commit 548aa00111
19 changed files with 108 additions and 62 deletions

View File

@ -21,6 +21,7 @@
#include "tag/Tag.hxx" #include "tag/Tag.hxx"
#include "tag/Settings.hxx" #include "tag/Settings.hxx"
#include "client/Response.hxx" #include "client/Response.hxx"
#include "util/StringView.hxx"
void void
tag_print_types(Response &r) noexcept tag_print_types(Response &r) noexcept
@ -31,6 +32,13 @@ tag_print_types(Response &r) noexcept
r.Format("tagtype: %s\n", tag_item_names[i]); r.Format("tagtype: %s\n", tag_item_names[i]);
} }
void
tag_print(Response &r, TagType type, StringView value) noexcept
{
r.Format("%s: %.*s\n", tag_item_names[type],
int(value.size), value.data);
}
void void
tag_print(Response &r, TagType type, const char *value) noexcept tag_print(Response &r, TagType type, const char *value) noexcept
{ {

View File

@ -25,11 +25,15 @@
enum TagType : uint8_t; enum TagType : uint8_t;
struct Tag; struct Tag;
struct StringView;
class Response; class Response;
void void
tag_print_types(Response &response) noexcept; tag_print_types(Response &response) noexcept;
void
tag_print(Response &response, TagType type, StringView value) noexcept;
void void
tag_print(Response &response, TagType type, const char *value) noexcept; tag_print(Response &response, TagType type, const char *value) noexcept;

View File

@ -27,6 +27,7 @@
#include "client/Client.hxx" #include "client/Client.hxx"
#include "client/Response.hxx" #include "client/Response.hxx"
#include "util/CharUtil.hxx" #include "util/CharUtil.hxx"
#include "util/StringView.hxx"
#include "util/UriUtil.hxx" #include "util/UriUtil.hxx"
#include "tag/Handler.hxx" #include "tag/Handler.hxx"
#include "tag/Generic.hxx" #include "tag/Generic.hxx"
@ -110,13 +111,12 @@ handle_listfiles_local(Response &r, Path path_fs)
gcc_pure gcc_pure
static bool static bool
IsValidName(const char *p) noexcept IsValidName(const StringView s) noexcept
{ {
if (!IsAlphaASCII(*p)) if (s.empty() || !IsAlphaASCII(s.front()))
return false; return false;
while (*++p) { for (const char ch : s) {
const char ch = *p;
if (!IsAlphaASCII(ch) && ch != '_' && ch != '-') if (!IsAlphaASCII(ch) && ch != '_' && ch != '-')
return false; return false;
} }
@ -126,11 +126,9 @@ IsValidName(const char *p) noexcept
gcc_pure gcc_pure
static bool static bool
IsValidValue(const char *p) noexcept IsValidValue(const StringView s) noexcept
{ {
while (*p) { for (const char ch : s) {
const char ch = *p++;
if ((unsigned char)ch < 0x20) if ((unsigned char)ch < 0x20)
return false; return false;
} }
@ -145,9 +143,11 @@ public:
explicit PrintCommentHandler(Response &_response) noexcept explicit PrintCommentHandler(Response &_response) noexcept
:NullTagHandler(WANT_PAIR), response(_response) {} :NullTagHandler(WANT_PAIR), response(_response) {}
void OnPair(const char *key, const char *value) noexcept override { void OnPair(StringView key, StringView value) noexcept override {
if (IsValidName(key) && IsValidValue(value)) if (IsValidName(key) && IsValidValue(value))
response.Format("%s: %s\n", key, value); response.Format("%.*s: %.*s\n",
int(key.size), key.data,
int(value.size), value.data);
} }
}; };

View File

@ -38,6 +38,7 @@
#include "time/ChronoUtil.hxx" #include "time/ChronoUtil.hxx"
#include "util/UriUtil.hxx" #include "util/UriUtil.hxx"
#include "util/StringAPI.hxx" #include "util/StringAPI.hxx"
#include "util/StringView.hxx"
#include "fs/AllocatedPath.hxx" #include "fs/AllocatedPath.hxx"
#include "Stats.hxx" #include "Stats.hxx"
#include "PlaylistFile.hxx" #include "PlaylistFile.hxx"
@ -147,7 +148,7 @@ public:
explicit PrintTagHandler(Response &_response) noexcept explicit PrintTagHandler(Response &_response) noexcept
:NullTagHandler(WANT_TAG), response(_response) {} :NullTagHandler(WANT_TAG), response(_response) {}
void OnTag(TagType type, const char *value) noexcept override { void OnTag(TagType type, StringView value) noexcept override {
if (response.GetClient().tag_mask.Test(type)) if (response.GetClient().tag_mask.Test(type))
tag_print(response, type, value); tag_print(response, type, value);
} }

View File

@ -24,6 +24,7 @@
#include "fs/Path.hxx" #include "fs/Path.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
#include "util/Macros.hxx" #include "util/Macros.hxx"
#include "util/StringView.hxx"
#include "Log.hxx" #include "Log.hxx"
#include <adplug/adplug.h> #include <adplug/adplug.h>
@ -85,7 +86,7 @@ adplug_scan_tag(TagType type, const std::string &value,
TagHandler &handler) noexcept TagHandler &handler) noexcept
{ {
if (!value.empty()) if (!value.empty())
handler.OnTag(type, value.c_str()); handler.OnTag(type, {value.data(), value.size()});
} }
static bool static bool

View File

@ -33,6 +33,7 @@
#include "CheckAudioFormat.hxx" #include "CheckAudioFormat.hxx"
#include "util/bit_reverse.h" #include "util/bit_reverse.h"
#include "util/ByteOrder.hxx" #include "util/ByteOrder.hxx"
#include "util/StringView.hxx"
#include "tag/Handler.hxx" #include "tag/Handler.hxx"
#include "DsdLib.hxx" #include "DsdLib.hxx"
#include "Log.hxx" #include "Log.hxx"
@ -205,15 +206,14 @@ dsdiff_handle_native_tag(DecoderClient *client, InputStream &is,
if (length == 0 || length > MAX_LENGTH) if (length == 0 || length > MAX_LENGTH)
return; return;
char string[MAX_LENGTH + 1]; char string[MAX_LENGTH];
char *label; char *label;
label = string; label = string;
if (!decoder_read_full(client, is, label, (size_t)length)) if (!decoder_read_full(client, is, label, (size_t)length))
return; return;
string[length] = '\0'; handler.OnTag(type, {label, length});
handler.OnTag(type, label);
return; return;
} }

View File

@ -24,6 +24,7 @@
#include "tag/Table.hxx" #include "tag/Table.hxx"
#include "tag/Handler.hxx" #include "tag/Handler.hxx"
#include "tag/Id3MusicBrainz.hxx" #include "tag/Id3MusicBrainz.hxx"
#include "util/StringView.hxx"
extern "C" { extern "C" {
#include <libavutil/dict.h> #include <libavutil/dict.h>

View File

@ -29,6 +29,7 @@
#include "fs/FileSystem.hxx" #include "fs/FileSystem.hxx"
#include "util/ScopeExit.hxx" #include "util/ScopeExit.hxx"
#include "util/StringFormat.hxx" #include "util/StringFormat.hxx"
#include "util/StringView.hxx"
#include "util/UriUtil.hxx" #include "util/UriUtil.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
#include "Log.hxx" #include "Log.hxx"
@ -220,7 +221,7 @@ ScanGmeInfo(const gme_info_t &info, unsigned song_num, int track_count,
handler.OnDuration(SongTime::FromMS(info.play_length)); handler.OnDuration(SongTime::FromMS(info.play_length));
if (track_count > 1) if (track_count > 1)
handler.OnTag(TAG_TRACK, StringFormat<16>("%u", song_num + 1)); handler.OnTag(TAG_TRACK, StringFormat<16>("%u", song_num + 1).c_str());
if (info.song != nullptr) { if (info.song != nullptr) {
if (track_count > 1) { if (track_count > 1) {
@ -229,7 +230,7 @@ ScanGmeInfo(const gme_info_t &info, unsigned song_num, int track_count,
StringFormat<1024>("%s (%u/%d)", StringFormat<1024>("%s (%u/%d)",
info.song, song_num + 1, info.song, song_num + 1,
track_count); track_count);
handler.OnTag(TAG_TITLE, tag_title); handler.OnTag(TAG_TITLE, tag_title.c_str());
} else } else
handler.OnTag(TAG_TITLE, info.song); handler.OnTag(TAG_TITLE, info.song);
} }

View File

@ -24,6 +24,7 @@
#include "fs/Path.hxx" #include "fs/Path.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
#include "util/RuntimeError.hxx" #include "util/RuntimeError.hxx"
#include "util/StringView.hxx"
#include "Log.hxx" #include "Log.hxx"
#include <mikmod.h> #include <mikmod.h>

View File

@ -24,6 +24,7 @@
#include "util/WritableBuffer.hxx" #include "util/WritableBuffer.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
#include "util/RuntimeError.hxx" #include "util/RuntimeError.hxx"
#include "util/StringView.hxx"
#include "Log.hxx" #include "Log.hxx"
#include <libmodplug/modplug.h> #include <libmodplug/modplug.h>

View File

@ -31,6 +31,7 @@
#endif #endif
#include "util/Macros.hxx" #include "util/Macros.hxx"
#include "util/StringFormat.hxx" #include "util/StringFormat.hxx"
#include "util/StringView.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
#include "util/ByteOrder.hxx" #include "util/ByteOrder.hxx"
#include "Log.hxx" #include "Log.hxx"
@ -460,7 +461,7 @@ ScanSidTuneInfo(const SidTuneInfo &info, unsigned track, unsigned n_tracks,
const auto tag_title = const auto tag_title =
StringFormat<1024>("%s (%u/%u)", StringFormat<1024>("%s (%u/%u)",
title, track, n_tracks); title, track, n_tracks);
handler.OnTag(TAG_TITLE, tag_title); handler.OnTag(TAG_TITLE, tag_title.c_str());
} else } else
handler.OnTag(TAG_TITLE, title); handler.OnTag(TAG_TITLE, title);
@ -475,7 +476,7 @@ ScanSidTuneInfo(const SidTuneInfo &info, unsigned track, unsigned n_tracks,
handler.OnTag(TAG_DATE, date); handler.OnTag(TAG_DATE, date);
/* track */ /* track */
handler.OnTag(TAG_TRACK, StringFormat<16>("%u", track)); handler.OnTag(TAG_TRACK, StringFormat<16>("%u", track).c_str());
} }
static bool static bool

View File

@ -24,6 +24,7 @@
#include "tag/Handler.hxx" #include "tag/Handler.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
#include "util/ScopeExit.hxx" #include "util/ScopeExit.hxx"
#include "util/StringView.hxx"
#include "Log.hxx" #include "Log.hxx"
#include <exception> #include <exception>

View File

@ -30,7 +30,7 @@
#include "tag/ReplayGain.hxx" #include "tag/ReplayGain.hxx"
#include "tag/MixRamp.hxx" #include "tag/MixRamp.hxx"
#include "ReplayGainInfo.hxx" #include "ReplayGainInfo.hxx"
#include "util/DivideString.hxx" #include "util/StringView.hxx"
#include <assert.h> #include <assert.h>
@ -98,10 +98,10 @@ flac_scan_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
TagHandler &handler) noexcept TagHandler &handler) noexcept
{ {
if (handler.WantPair()) { if (handler.WantPair()) {
const char *comment = (const char *)entry->entry; const StringView comment((const char *)entry->entry);
const DivideString split(comment, '='); const auto split = StringView(comment).Split('=');
if (split.IsDefined() && !split.empty()) if (!split.first.empty() && !split.second.IsNull())
handler.OnPair(split.GetFirst(), split.GetSecond()); handler.OnPair(split.first, split.second);
} }
for (const struct tag_table *i = xiph_tags; i->name != nullptr; ++i) for (const struct tag_table *i = xiph_tags; i->name != nullptr; ++i)

View File

@ -26,7 +26,7 @@
#include "tag/VorbisComment.hxx" #include "tag/VorbisComment.hxx"
#include "tag/ReplayGain.hxx" #include "tag/ReplayGain.hxx"
#include "ReplayGainInfo.hxx" #include "ReplayGainInfo.hxx"
#include "util/DivideString.hxx" #include "util/StringView.hxx"
bool bool
vorbis_comments_to_replay_gain(ReplayGainInfo &rgi, char **comments) noexcept vorbis_comments_to_replay_gain(ReplayGainInfo &rgi, char **comments) noexcept
@ -69,9 +69,9 @@ static void
vorbis_scan_comment(const char *comment, TagHandler &handler) noexcept vorbis_scan_comment(const char *comment, TagHandler &handler) noexcept
{ {
if (handler.WantPair()) { if (handler.WantPair()) {
const DivideString split(comment, '='); const auto split = StringView(comment).Split('=');
if (split.IsDefined() && !split.empty()) if (!split.first.empty() && !split.second.IsNull())
handler.OnPair(split.GetFirst(), split.GetSecond()); handler.OnPair(split.first, split.second);
} }
for (const struct tag_table *i = xiph_tags; i->name != nullptr; ++i) for (const struct tag_table *i = xiph_tags; i->name != nullptr; ++i)

View File

@ -33,7 +33,7 @@
#include "TagFile.hxx" #include "TagFile.hxx"
#include "fs/Traits.hxx" #include "fs/Traits.hxx"
#include "fs/AllocatedPath.hxx" #include "fs/AllocatedPath.hxx"
#include "util/ASCII.hxx" #include "util/StringView.hxx"
#include <memory> #include <memory>
@ -70,15 +70,14 @@ public:
ExtractCuesheetTagHandler() noexcept:NullTagHandler(WANT_PAIR) {} ExtractCuesheetTagHandler() noexcept:NullTagHandler(WANT_PAIR) {}
void OnPair(const char *key, const char *value) noexcept override; void OnPair(StringView key, StringView value) noexcept override;
}; };
void void
ExtractCuesheetTagHandler::OnPair(const char *name, const char *value) noexcept ExtractCuesheetTagHandler::OnPair(StringView name, StringView value) noexcept
{ {
if (cuesheet.empty() && if (cuesheet.empty() && name.EqualsIgnoreCase("cuesheet"))
StringEqualsCaseASCII(name, "cuesheet")) cuesheet = {value.data, value.size};
cuesheet = value;
} }
static std::unique_ptr<SongEnumerator> static std::unique_ptr<SongEnumerator>

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2003-2018 The Music Player Daemon Project * Copyright 2003-2019 The Music Player Daemon Project
* http://www.musicpd.org * http://www.musicpd.org
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@ -21,9 +21,20 @@
#include "Builder.hxx" #include "Builder.hxx"
#include "AudioFormat.hxx" #include "AudioFormat.hxx"
#include "util/ASCII.hxx" #include "util/ASCII.hxx"
#include "util/StringFormat.hxx" #include "util/CharUtil.hxx"
#include "util/StringView.hxx"
#include <stdlib.h> #include <algorithm>
void
NullTagHandler::OnTag(TagType, StringView) noexcept
{
}
void
NullTagHandler::OnPair(StringView, StringView) noexcept
{
}
void void
NullTagHandler::OnAudioFormat(gcc_unused AudioFormat af) noexcept NullTagHandler::OnAudioFormat(gcc_unused AudioFormat af) noexcept
@ -36,23 +47,35 @@ AddTagHandler::OnDuration(SongTime duration) noexcept
tag.SetDuration(duration); tag.SetDuration(duration);
} }
/**
* Skip leading zeroes and a non-decimal suffix.
*/
static StringView
NormalizeDecimal(StringView s)
{
auto start = std::find_if(s.begin(), s.end(),
[](char ch){ return ch != '0'; });
auto end = std::find_if(start, s.end(),
[](char ch){ return !IsDigitASCII(ch); });
return {start, end};
}
void void
AddTagHandler::OnTag(TagType type, const char *value) noexcept AddTagHandler::OnTag(TagType type, StringView value) noexcept
{ {
if (type == TAG_TRACK || type == TAG_DISC) { if (type == TAG_TRACK || type == TAG_DISC) {
/* filter out this extra data and leading zeroes */ /* filter out this extra data and leading zeroes */
char *end;
unsigned n = strtoul(value, &end, 10); value = NormalizeDecimal(value);
if (value != end) }
tag.AddItem(type, StringFormat<21>("%u", n));
} else
tag.AddItem(type, value); tag.AddItem(type, value);
} }
void void
FullTagHandler::OnPair(const char *name, gcc_unused const char *value) noexcept FullTagHandler::OnPair(StringView name, StringView) noexcept
{ {
if (StringEqualsCaseASCII(name, "cuesheet")) if (name.EqualsIgnoreCase("cuesheet"))
tag.SetHasPlaylist(true); tag.SetHasPlaylist(true);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2003-2018 The Music Player Daemon Project * Copyright 2003-2019 The Music Player Daemon Project
* http://www.musicpd.org * http://www.musicpd.org
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@ -24,6 +24,7 @@
#include "Chrono.hxx" #include "Chrono.hxx"
#include "util/Compiler.h" #include "util/Compiler.h"
struct StringView;
struct AudioFormat; struct AudioFormat;
class TagBuilder; class TagBuilder;
@ -74,13 +75,13 @@ public:
* @param the value of the tag; the pointer will become * @param the value of the tag; the pointer will become
* invalid after returning * invalid after returning
*/ */
virtual void OnTag(TagType type, const char *value) noexcept = 0; virtual void OnTag(TagType type, StringView value) noexcept = 0;
/** /**
* A name-value pair has been read. It is the codec specific * A name-value pair has been read. It is the codec specific
* representation of tags. * representation of tags.
*/ */
virtual void OnPair(const char *key, const char *value) noexcept = 0; virtual void OnPair(StringView key, StringView value) noexcept = 0;
/** /**
* Declare the audio format of a song. * Declare the audio format of a song.
@ -106,10 +107,8 @@ public:
:TagHandler(_want_mask) {} :TagHandler(_want_mask) {}
void OnDuration(gcc_unused SongTime duration) noexcept override {} void OnDuration(gcc_unused SongTime duration) noexcept override {}
void OnTag(gcc_unused TagType type, void OnTag(TagType type, StringView value) noexcept override;
gcc_unused const char *value) noexcept override {} void OnPair(StringView key, StringView value) noexcept override;
void OnPair(gcc_unused const char *key,
gcc_unused const char *value) noexcept override {}
void OnAudioFormat(AudioFormat af) noexcept override; void OnAudioFormat(AudioFormat af) noexcept override;
}; };
@ -130,7 +129,7 @@ public:
:AddTagHandler(0, _builder) {} :AddTagHandler(0, _builder) {}
void OnDuration(SongTime duration) noexcept override; void OnDuration(SongTime duration) noexcept override;
void OnTag(TagType type, const char *value) noexcept override; void OnTag(TagType type, StringView value) noexcept override;
}; };
/** /**
@ -154,7 +153,7 @@ public:
AudioFormat *_audio_format=nullptr) noexcept AudioFormat *_audio_format=nullptr) noexcept
:FullTagHandler(0, _builder, _audio_format) {} :FullTagHandler(0, _builder, _audio_format) {}
void OnPair(const char *key, const char *value) noexcept override; void OnPair(StringView key, StringView value) noexcept override;
void OnAudioFormat(AudioFormat af) noexcept override; void OnAudioFormat(AudioFormat af) noexcept override;
}; };

View File

@ -27,6 +27,7 @@
#include "util/Alloc.hxx" #include "util/Alloc.hxx"
#include "util/ScopeExit.hxx" #include "util/ScopeExit.hxx"
#include "util/StringStrip.hxx" #include "util/StringStrip.hxx"
#include "util/StringView.hxx"
#include "Log.hxx" #include "Log.hxx"
#include <id3tag.h> #include <id3tag.h>

View File

@ -30,6 +30,7 @@
#include "AudioFormat.hxx" #include "AudioFormat.hxx"
#include "util/ScopeExit.hxx" #include "util/ScopeExit.hxx"
#include "util/StringBuffer.hxx" #include "util/StringBuffer.hxx"
#include "util/StringView.hxx"
#include "util/PrintException.hxx" #include "util/PrintException.hxx"
#include <stdexcept> #include <stdexcept>
@ -58,13 +59,16 @@ public:
printf("duration=%f\n", duration.ToDoubleS()); printf("duration=%f\n", duration.ToDoubleS());
} }
void OnTag(TagType type, const char *value) noexcept override { void OnTag(TagType type, StringView value) noexcept override {
printf("[%s]=%s\n", tag_item_names[type], value); printf("[%s]=%.*s\n", tag_item_names[type],
int(value.size), value.data);
empty = false; empty = false;
} }
void OnPair(const char *key, const char *value) noexcept override { void OnPair(StringView key, StringView value) noexcept override {
printf("\"%s\"=%s\n", key, value); printf("\"%.*s\"=%.*s\n",
int(key.size), key.data,
int(value.size), value.data);
} }
void OnAudioFormat(AudioFormat af) noexcept override { void OnAudioFormat(AudioFormat af) noexcept override {