db/upnp: eliminate the std::forward_list, use IterableSplitString()

This commit is contained in:
Max Kellermann 2024-01-04 16:09:03 +01:00
parent cc41e95806
commit 95842e7984
1 changed files with 42 additions and 46 deletions

View File

@ -21,7 +21,7 @@
#include "tag/Table.hxx" #include "tag/Table.hxx"
#include "fs/Traits.hxx" #include "fs/Traits.hxx"
#include "util/RecursiveMap.hxx" #include "util/RecursiveMap.hxx"
#include "util/SplitString.hxx" #include "util/StringSplit.hxx"
#include "config/Block.hxx" #include "config/Block.hxx"
#include <fmt/core.h> #include <fmt/core.h>
@ -35,6 +35,17 @@
static constexpr const char *rootid = "0"; static constexpr const char *rootid = "0";
static constexpr std::string_view rootid_sv{rootid}; static constexpr std::string_view rootid_sv{rootid};
static std::string_view
AfterRootIdSegment(std::string_view &uri) noexcept
{
if (uri.size() >= rootid_sv.size() + 2 &&
uri.starts_with(rootid_sv) &&
uri[rootid_sv.size()] == '/')
return uri.substr(rootid_sv.size() + 1);
else
return {};
}
class UpnpSongData { class UpnpSongData {
protected: protected:
std::string uri; std::string uri;
@ -97,7 +108,7 @@ public:
private: private:
void VisitServer(const ContentDirectoryService &server, void VisitServer(const ContentDirectoryService &server,
std::forward_list<std::string_view> &&vpath, std::string_view uri,
const DatabaseSelection &selection, const DatabaseSelection &selection,
const VisitDirectory& visit_directory, const VisitDirectory& visit_directory,
const VisitSong& visit_song, const VisitSong& visit_song,
@ -117,7 +128,7 @@ private:
const DatabaseSelection &selection) const; const DatabaseSelection &selection) const;
UPnPDirObject Namei(const ContentDirectoryService &server, UPnPDirObject Namei(const ContentDirectoryService &server,
std::forward_list<std::string_view> &&vpath) const; std::string_view uri) const;
/** /**
* Take server and objid, return metadata. * Take server and objid, return metadata.
@ -178,28 +189,19 @@ UpnpDatabase::ReturnSong(const LightSong *_song) const noexcept
const LightSong * const LightSong *
UpnpDatabase::GetSong(std::string_view uri) const UpnpDatabase::GetSong(std::string_view uri) const
{ {
auto vpath = SplitString(uri, '/'); auto [server_name, uri_in_server] = Split(uri, '/');
if (vpath.empty()) if (server_name.empty() || uri_in_server.empty())
throw DatabaseError(DatabaseErrorCode::NOT_FOUND, throw DatabaseError(DatabaseErrorCode::NOT_FOUND,
"No such song"); "No such song");
auto server = discovery->GetServer(vpath.front()); auto server = discovery->GetServer(server_name);
vpath.pop_front();
if (vpath.empty())
throw DatabaseError(DatabaseErrorCode::NOT_FOUND,
"No such song");
UPnPDirObject dirent; UPnPDirObject dirent;
if (vpath.front() != rootid_sv) { if (const auto id = AfterRootIdSegment(uri_in_server);
dirent = Namei(server, std::move(vpath)); id.data() == nullptr) {
dirent = Namei(server, uri_in_server);
} else { } else {
vpath.pop_front(); dirent = ReadNode(server, std::string{id}.c_str());
if (vpath.empty())
throw DatabaseError(DatabaseErrorCode::NOT_FOUND,
"No such song");
dirent = ReadNode(server, std::string(vpath.front()).c_str());
} }
return new UpnpSong(std::move(dirent), uri); return new UpnpSong(std::move(dirent), uri);
@ -398,9 +400,9 @@ UpnpDatabase::BuildPath(const ContentDirectoryService &server,
// Take server and internal title pathname and return objid and metadata. // Take server and internal title pathname and return objid and metadata.
UPnPDirObject UPnPDirObject
UpnpDatabase::Namei(const ContentDirectoryService &server, UpnpDatabase::Namei(const ContentDirectoryService &server,
std::forward_list<std::string_view> &&vpath) const std::string_view uri) const
{ {
if (vpath.empty()) if (uri.empty())
// looking for root info // looking for root info
return ReadNode(server, rootid); return ReadNode(server, rootid);
@ -410,14 +412,16 @@ UpnpDatabase::Namei(const ContentDirectoryService &server,
while (true) { while (true) {
auto dirbuf = server.readDir(handle, objid.c_str()); auto dirbuf = server.readDir(handle, objid.c_str());
const auto [name, rest] = Split(uri, '/');
// Look for the name in the sub-container list // Look for the name in the sub-container list
UPnPDirObject *child = dirbuf.FindObject(vpath.front()); UPnPDirObject *child = dirbuf.FindObject(name);
if (child == nullptr) if (child == nullptr)
throw DatabaseError(DatabaseErrorCode::NOT_FOUND, throw DatabaseError(DatabaseErrorCode::NOT_FOUND,
"No such object"); "No such object");
vpath.pop_front(); uri = rest;
if (vpath.empty()) if (uri.empty())
return std::move(*child); return std::move(*child);
if (child->type != UPnPDirObject::Type::CONTAINER) if (child->type != UPnPDirObject::Type::CONTAINER)
@ -486,7 +490,7 @@ VisitObject(const UPnPDirObject &object, const char *uri,
// really just one path parameter. // really just one path parameter.
void void
UpnpDatabase::VisitServer(const ContentDirectoryService &server, UpnpDatabase::VisitServer(const ContentDirectoryService &server,
std::forward_list<std::string_view> &&vpath, std::string_view uri,
const DatabaseSelection &selection, const DatabaseSelection &selection,
const VisitDirectory& visit_directory, const VisitDirectory& visit_directory,
const VisitSong& visit_song, const VisitSong& visit_song,
@ -500,19 +504,12 @@ UpnpDatabase::VisitServer(const ContentDirectoryService &server,
/* !Note: this *can't* be handled by Namei further down, /* !Note: this *can't* be handled by Namei further down,
because the path is not valid for traversal. Besides, it's because the path is not valid for traversal. Besides, it's
just faster to access the target node directly */ just faster to access the target node directly */
if (!vpath.empty() && vpath.front() == rootid_sv) { if (uri == rootid_sv)
vpath.pop_front(); return;
if (vpath.empty())
return;
const std::string objid(vpath.front());
vpath.pop_front();
if (!vpath.empty())
throw DatabaseError(DatabaseErrorCode::NOT_FOUND,
"Not found");
if (const auto id = AfterRootIdSegment(uri); id.data() != nullptr) {
if (visit_song) { if (visit_song) {
auto dirent = ReadNode(server, objid.c_str()); auto dirent = ReadNode(server, std::string{id}.c_str());
if (dirent.type != UPnPDirObject::Type::ITEM || if (dirent.type != UPnPDirObject::Type::ITEM ||
dirent.item_class != UPnPDirObject::ItemClass::MUSIC) dirent.item_class != UPnPDirObject::ItemClass::MUSIC)
@ -529,7 +526,7 @@ UpnpDatabase::VisitServer(const ContentDirectoryService &server,
} }
// Translate the target path into an object id and the associated metadata. // Translate the target path into an object id and the associated metadata.
const auto tdirent = Namei(server, std::move(vpath)); const auto tdirent = Namei(server, uri);
/* If recursive is set, this is a search... No use sending it /* If recursive is set, this is a search... No use sending it
if the filter is empty. In this case, we implement limited if the filter is empty. In this case, we implement limited
@ -556,9 +553,9 @@ UpnpDatabase::VisitServer(const ContentDirectoryService &server,
data to the client when we're done anyway. */ data to the client when we're done anyway. */
const auto contents = server.readDir(handle, tdirent.id.c_str()); const auto contents = server.readDir(handle, tdirent.id.c_str());
for (const auto &dirent : contents.objects) { for (const auto &dirent : contents.objects) {
const std::string uri = PathTraitsUTF8::Build(base_uri, const std::string child_uri = PathTraitsUTF8::Build(base_uri,
dirent.name.c_str()); dirent.name.c_str());
VisitObject(dirent, uri.c_str(), VisitObject(dirent, child_uri.c_str(),
selection, selection,
visit_directory, visit_directory,
visit_song, visit_playlist); visit_song, visit_playlist);
@ -583,8 +580,7 @@ UpnpDatabase::Visit(const DatabaseSelection &selection,
{ {
DatabaseVisitorHelper helper(CheckSelection(selection), visit_song); DatabaseVisitorHelper helper(CheckSelection(selection), visit_song);
auto vpath = SplitString(selection.uri, '/'); if (selection.uri.empty()) {
if (vpath.empty()) {
for (const auto &server : discovery->GetDirectories()) { for (const auto &server : discovery->GetDirectories()) {
if (visit_directory) { if (visit_directory) {
const LightDirectory d(server.GetFriendlyName().c_str(), const LightDirectory d(server.GetFriendlyName().c_str(),
@ -593,7 +589,7 @@ UpnpDatabase::Visit(const DatabaseSelection &selection,
} }
if (selection.recursive) if (selection.recursive)
VisitServer(server, std::move(vpath), selection, VisitServer(server, {}, selection,
visit_directory, visit_song, visit_directory, visit_song,
visit_playlist); visit_playlist);
} }
@ -603,10 +599,10 @@ UpnpDatabase::Visit(const DatabaseSelection &selection,
} }
// We do have a path: the first element selects the server // We do have a path: the first element selects the server
auto server = discovery->GetServer(vpath.front()); const auto [server_name, uri_in_server] = Split(std::string_view{selection.uri}, '/');
vpath.pop_front(); auto server = discovery->GetServer(server_name);
VisitServer(server, std::move(vpath), selection, VisitServer(server, uri_in_server, selection,
visit_directory, visit_song, visit_playlist); visit_directory, visit_song, visit_playlist);
helper.Commit(); helper.Commit();
} }