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 "fs/Traits.hxx"
#include "util/RecursiveMap.hxx"
#include "util/SplitString.hxx"
#include "util/StringSplit.hxx"
#include "config/Block.hxx"
#include <fmt/core.h>
@ -35,6 +35,17 @@
static constexpr const char *rootid = "0";
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 {
protected:
std::string uri;
@ -97,7 +108,7 @@ public:
private:
void VisitServer(const ContentDirectoryService &server,
std::forward_list<std::string_view> &&vpath,
std::string_view uri,
const DatabaseSelection &selection,
const VisitDirectory& visit_directory,
const VisitSong& visit_song,
@ -117,7 +128,7 @@ private:
const DatabaseSelection &selection) const;
UPnPDirObject Namei(const ContentDirectoryService &server,
std::forward_list<std::string_view> &&vpath) const;
std::string_view uri) const;
/**
* Take server and objid, return metadata.
@ -178,28 +189,19 @@ UpnpDatabase::ReturnSong(const LightSong *_song) const noexcept
const LightSong *
UpnpDatabase::GetSong(std::string_view uri) const
{
auto vpath = SplitString(uri, '/');
if (vpath.empty())
auto [server_name, uri_in_server] = Split(uri, '/');
if (server_name.empty() || uri_in_server.empty())
throw DatabaseError(DatabaseErrorCode::NOT_FOUND,
"No such song");
auto server = discovery->GetServer(vpath.front());
vpath.pop_front();
if (vpath.empty())
throw DatabaseError(DatabaseErrorCode::NOT_FOUND,
"No such song");
auto server = discovery->GetServer(server_name);
UPnPDirObject dirent;
if (vpath.front() != rootid_sv) {
dirent = Namei(server, std::move(vpath));
if (const auto id = AfterRootIdSegment(uri_in_server);
id.data() == nullptr) {
dirent = Namei(server, uri_in_server);
} else {
vpath.pop_front();
if (vpath.empty())
throw DatabaseError(DatabaseErrorCode::NOT_FOUND,
"No such song");
dirent = ReadNode(server, std::string(vpath.front()).c_str());
dirent = ReadNode(server, std::string{id}.c_str());
}
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.
UPnPDirObject
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
return ReadNode(server, rootid);
@ -410,14 +412,16 @@ UpnpDatabase::Namei(const ContentDirectoryService &server,
while (true) {
auto dirbuf = server.readDir(handle, objid.c_str());
const auto [name, rest] = Split(uri, '/');
// Look for the name in the sub-container list
UPnPDirObject *child = dirbuf.FindObject(vpath.front());
UPnPDirObject *child = dirbuf.FindObject(name);
if (child == nullptr)
throw DatabaseError(DatabaseErrorCode::NOT_FOUND,
"No such object");
vpath.pop_front();
if (vpath.empty())
uri = rest;
if (uri.empty())
return std::move(*child);
if (child->type != UPnPDirObject::Type::CONTAINER)
@ -486,7 +490,7 @@ VisitObject(const UPnPDirObject &object, const char *uri,
// really just one path parameter.
void
UpnpDatabase::VisitServer(const ContentDirectoryService &server,
std::forward_list<std::string_view> &&vpath,
std::string_view uri,
const DatabaseSelection &selection,
const VisitDirectory& visit_directory,
const VisitSong& visit_song,
@ -500,19 +504,12 @@ UpnpDatabase::VisitServer(const ContentDirectoryService &server,
/* !Note: this *can't* be handled by Namei further down,
because the path is not valid for traversal. Besides, it's
just faster to access the target node directly */
if (!vpath.empty() && vpath.front() == rootid_sv) {
vpath.pop_front();
if (vpath.empty())
return;
const std::string objid(vpath.front());
vpath.pop_front();
if (!vpath.empty())
throw DatabaseError(DatabaseErrorCode::NOT_FOUND,
"Not found");
if (uri == rootid_sv)
return;
if (const auto id = AfterRootIdSegment(uri); id.data() != nullptr) {
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 ||
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.
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 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. */
const auto contents = server.readDir(handle, tdirent.id.c_str());
for (const auto &dirent : contents.objects) {
const std::string uri = PathTraitsUTF8::Build(base_uri,
dirent.name.c_str());
VisitObject(dirent, uri.c_str(),
const std::string child_uri = PathTraitsUTF8::Build(base_uri,
dirent.name.c_str());
VisitObject(dirent, child_uri.c_str(),
selection,
visit_directory,
visit_song, visit_playlist);
@ -583,8 +580,7 @@ UpnpDatabase::Visit(const DatabaseSelection &selection,
{
DatabaseVisitorHelper helper(CheckSelection(selection), visit_song);
auto vpath = SplitString(selection.uri, '/');
if (vpath.empty()) {
if (selection.uri.empty()) {
for (const auto &server : discovery->GetDirectories()) {
if (visit_directory) {
const LightDirectory d(server.GetFriendlyName().c_str(),
@ -593,7 +589,7 @@ UpnpDatabase::Visit(const DatabaseSelection &selection,
}
if (selection.recursive)
VisitServer(server, std::move(vpath), selection,
VisitServer(server, {}, selection,
visit_directory, visit_song,
visit_playlist);
}
@ -603,10 +599,10 @@ UpnpDatabase::Visit(const DatabaseSelection &selection,
}
// We do have a path: the first element selects the server
auto server = discovery->GetServer(vpath.front());
vpath.pop_front();
const auto [server_name, uri_in_server] = Split(std::string_view{selection.uri}, '/');
auto server = discovery->GetServer(server_name);
VisitServer(server, std::move(vpath), selection,
VisitServer(server, uri_in_server, selection,
visit_directory, visit_song, visit_playlist);
helper.Commit();
}