command/*: use std::span instead of ConstBuffer

This commit is contained in:
Max Kellermann 2022-07-04 18:38:57 +02:00
parent baff5e5594
commit 9b427b3171
12 changed files with 118 additions and 78 deletions

View File

@ -337,17 +337,17 @@ command_check_request(const struct command *cmd, Response &r,
if (min < 0)
return true;
if (min == max && unsigned(max) != args.size) {
if (min == max && unsigned(max) != args.size()) {
r.FmtError(ACK_ERROR_ARG,
FMT_STRING("wrong number of arguments for \"{}\""),
cmd->cmd);
return false;
} else if (args.size < unsigned(min)) {
} else if (args.size() < unsigned(min)) {
r.FmtError(ACK_ERROR_ARG,
FMT_STRING("too few arguments for \"{}\""),
cmd->cmd);
return false;
} else if (max >= 0 && args.size > unsigned(max)) {
} else if (max >= 0 && args.size() > unsigned(max)) {
r.FmtError(ACK_ERROR_ARG,
FMT_STRING("too many arguments for \"{}\""),
cmd->cmd);
@ -403,13 +403,13 @@ command_process(Client &client, unsigned num, char *line) noexcept
}
char *argv[COMMAND_ARGV_MAX];
Request args(argv, 0);
try {
/* now parse the arguments (quoted or unquoted) */
std::size_t n_args = 0;
while (true) {
if (args.size == COMMAND_ARGV_MAX) {
if (n_args == COMMAND_ARGV_MAX) {
r.Error(ACK_ERROR_ARG, "Too many arguments");
return CommandResult::ERROR;
}
@ -418,9 +418,11 @@ command_process(Client &client, unsigned num, char *line) noexcept
if (a == nullptr)
break;
argv[args.size++] = a;
argv[n_args++] = a;
}
Request args{argv, n_args};
/* look up and invoke the command handler */
const struct command *cmd =

View File

@ -30,7 +30,6 @@
#include "client/Client.hxx"
#include "client/Response.hxx"
#include "tag/ParseName.hxx"
#include "util/ConstBuffer.hxx"
#include "util/Exception.hxx"
#include "util/StringAPI.hxx"
#include "util/ASCII.hxx"
@ -75,8 +74,8 @@ ParseSortTag(const char *s)
static unsigned
ParseQueuePosition(Request &args, unsigned queue_length)
{
if (args.size >= 2 && StringIsEqual(args[args.size - 2], "position")) {
unsigned position = args.ParseUnsigned(args.size - 1,
if (args.size() >= 2 && StringIsEqual(args[args.size() - 2], "position")) {
unsigned position = args.ParseUnsigned(args.size() - 1,
queue_length);
args.pop_back();
args.pop_back();
@ -90,7 +89,7 @@ ParseQueuePosition(Request &args, unsigned queue_length)
static unsigned
ParseInsertPosition(Request &args, const playlist &playlist)
{
if (args.size >= 2 && StringIsEqual(args[args.size - 2], "position")) {
if (args.size() >= 2 && StringIsEqual(args[args.size() - 2], "position")) {
unsigned position = ParseInsertPosition(args.back(), playlist);
args.pop_back();
args.pop_back();
@ -110,8 +109,8 @@ static DatabaseSelection
ParseDatabaseSelection(Request args, bool fold_case, SongFilter &filter)
{
RangeArg window = RangeArg::All();
if (args.size >= 2 && StringIsEqual(args[args.size - 2], "window")) {
window = args.ParseRange(args.size - 1);
if (args.size() >= 2 && StringIsEqual(args[args.size() - 2], "window")) {
window = args.ParseRange(args.size() - 1);
args.pop_back();
args.pop_back();
@ -119,7 +118,7 @@ ParseDatabaseSelection(Request args, bool fold_case, SongFilter &filter)
TagType sort = TAG_NUM_OF_ITEM_TYPES;
bool descending = false;
if (args.size >= 2 && StringIsEqual(args[args.size - 2], "sort")) {
if (args.size() >= 2 && StringIsEqual(args[args.size() - 2], "sort")) {
const char *s = args.back();
if (*s == '-') {
descending = true;
@ -236,8 +235,8 @@ CommandResult
handle_count(Client &client, Request args, Response &r)
{
TagType group = TAG_NUM_OF_ITEM_TYPES;
if (args.size >= 2 && StringIsEqual(args[args.size - 2], "group")) {
const char *s = args[args.size - 1];
if (args.size() >= 2 && StringIsEqual(args[args.size() - 2], "group")) {
const char *s = args[args.size() - 1];
group = tag_name_parse_i(s);
if (group == TAG_NUM_OF_ITEM_TYPES) {
r.FmtError(ACK_ERROR_ARG,
@ -317,7 +316,7 @@ handle_list(Client &client, Request args, Response &r)
std::unique_ptr<SongFilter> filter;
std::vector<TagType> tag_types;
if (args.size == 1 &&
if (args.size() == 1 &&
/* parantheses are the syntax for filter expressions: no
compatibility mode */
args.front()[0] != '(') {
@ -333,9 +332,9 @@ handle_list(Client &client, Request args, Response &r)
args.shift());
}
while (args.size >= 2 &&
StringIsEqual(args[args.size - 2], "group")) {
const char *s = args[args.size - 1];
while (args.size() >= 2 &&
StringIsEqual(args[args.size() - 2], "group")) {
const char *s = args[args.size() - 1];
const auto group = tag_name_parse_i(s);
if (group == TAG_NUM_OF_ITEM_TYPES) {
r.FmtError(ACK_ERROR_ARG,

View File

@ -138,7 +138,7 @@ public:
CommandResult
handle_read_comments(Client &client, Request args, Response &r)
{
assert(args.size == 1);
assert(args.size() == 1);
const char *const uri = args.front();
@ -288,7 +288,7 @@ read_db_art(Client &client, Response &r, const char *uri, const uint64_t offset)
CommandResult
handle_album_art(Client &client, Request args, Response &r)
{
assert(args.size == 2);
assert(args.size() == 2);
const char *uri = args.front();
size_t offset = args.ParseUnsigned(1);
@ -368,7 +368,7 @@ public:
CommandResult
handle_read_picture(Client &client, Request args, Response &r)
{
assert(args.size == 2);
assert(args.size() == 2);
const char *const uri = args.front();
const size_t offset = args.ParseUnsigned(1);

View File

@ -22,7 +22,6 @@
#include "client/Client.hxx"
#include "client/List.hxx"
#include "client/Response.hxx"
#include "util/ConstBuffer.hxx"
#include "Partition.hxx"
#include <fmt/format.h>
@ -34,7 +33,7 @@
CommandResult
handle_subscribe(Client &client, Request args, Response &r)
{
assert(args.size == 1);
assert(args.size() == 1);
const char *const channel_name = args[0];
switch (client.Subscribe(channel_name)) {
@ -62,7 +61,7 @@ handle_subscribe(Client &client, Request args, Response &r)
CommandResult
handle_unsubscribe(Client &client, Request args, Response &r)
{
assert(args.size == 1);
assert(args.size() == 1);
const char *const channel_name = args[0];
if (client.Unsubscribe(channel_name))
@ -109,7 +108,7 @@ handle_read_messages(Client &client,
CommandResult
handle_send_message(Client &client, Request args, Response &r)
{
assert(args.size == 2);
assert(args.size() == 2);
const char *const channel_name = args[0];
const char *const message_text = args[1];

View File

@ -277,7 +277,7 @@ handle_update(Client &client, Request args, Response &r, bool discard)
#ifdef ENABLE_DATABASE
const char *path = "";
assert(args.size <= 1);
assert(args.size() <= 1);
if (!args.empty()) {
path = args.front();

View File

@ -30,7 +30,7 @@
CommandResult
handle_enableoutput(Client &client, Request args, Response &r)
{
assert(args.size == 1);
assert(args.size() == 1);
unsigned device = args.ParseUnsigned(0);
if (!audio_output_enable_index(client.GetPartition().outputs, device)) {
@ -44,7 +44,7 @@ handle_enableoutput(Client &client, Request args, Response &r)
CommandResult
handle_disableoutput(Client &client, Request args, Response &r)
{
assert(args.size == 1);
assert(args.size() == 1);
unsigned device = args.ParseUnsigned(0);
if (!audio_output_disable_index(client.GetPartition().outputs, device)) {
@ -58,7 +58,7 @@ handle_disableoutput(Client &client, Request args, Response &r)
CommandResult
handle_toggleoutput(Client &client, Request args, Response &r)
{
assert(args.size == 1);
assert(args.size() == 1);
unsigned device = args.ParseUnsigned(0);
if (!audio_output_toggle_index(client.GetPartition().outputs, device)) {
@ -90,7 +90,7 @@ IsValidAttributeName(const char *s) noexcept
CommandResult
handle_outputset(Client &client, Request request, Response &response)
{
assert(request.size == 3);
assert(request.size() == 3);
const unsigned i = request.ParseUnsigned(0);
auto &partition = client.GetPartition();

View File

@ -41,7 +41,6 @@
#include "Mapper.hxx"
#include "fs/AllocatedPath.hxx"
#include "time/ChronoUtil.hxx"
#include "util/ConstBuffer.hxx"
#include "util/UriExtract.hxx"
#include "LocateUri.hxx"
@ -88,7 +87,7 @@ handle_load(Client &client, Request args, [[maybe_unused]] Response &r)
auto &playlist = client.GetPlaylist();
const unsigned old_size = playlist.GetLength();
const unsigned position = args.size > 2
const unsigned position = args.size() > 2
? ParseInsertPosition(args[2], partition.playlist)
: old_size;
@ -257,7 +256,7 @@ handle_playlistadd(Client &client, Request args, [[maybe_unused]] Response &r)
const char *const playlist = args[0];
const char *const uri = args[1];
if (args.size >= 3)
if (args.size() >= 3)
return handle_playlistadd_position(client, playlist, uri,
args.ParseUnsigned(2), r);

View File

@ -37,7 +37,6 @@
#include "Partition.hxx"
#include "Instance.hxx"
#include "BulkEdit.hxx"
#include "util/ConstBuffer.hxx"
#include "util/Exception.hxx"
#include "util/StringAPI.hxx"
#include "util/NumberParser.hxx"
@ -82,7 +81,7 @@ handle_add(Client &client, Request args, [[maybe_unused]] Response &r)
uri = "";
const auto old_size = partition.playlist.GetLength();
const unsigned position = args.size > 1
const unsigned position = args.size() > 1
? ParseInsertPosition(args[1], partition.playlist)
: old_size;
@ -137,7 +136,7 @@ handle_addid(Client &client, Request args, Response &r)
const auto queue_length = partition.playlist.queue.GetLength();
if (args.size > 1)
if (args.size() > 1)
to = ParseInsertPosition(args[1], partition.playlist);
const SongLoader loader(client);
@ -308,8 +307,8 @@ handle_playlist_match(Client &client, Request args, Response &r,
bool fold_case)
{
RangeArg window = RangeArg::All();
if (args.size >= 2 && StringIsEqual(args[args.size - 2], "window")) {
window = args.ParseRange(args.size - 1);
if (args.size() >= 2 && StringIsEqual(args[args.size() - 2], "window")) {
window = args.ParseRange(args.size() - 1);
args.pop_back();
args.pop_back();
@ -317,7 +316,7 @@ handle_playlist_match(Client &client, Request args, Response &r,
TagType sort = TAG_NUM_OF_ITEM_TYPES;
bool descending = false;
if (args.size >= 2 && StringIsEqual(args[args.size - 2], "sort")) {
if (args.size() >= 2 && StringIsEqual(args[args.size() - 2], "sort")) {
const char *s = args.back();
if (*s == '-') {
descending = true;

View File

@ -23,80 +23,124 @@
#include "protocol/ArgParser.hxx"
#include "protocol/RangeArg.hxx"
#include "Chrono.hxx"
#include "util/ConstBuffer.hxx"
#include <cassert>
#include <span>
#include <utility>
class Response;
class Request : public ConstBuffer<const char *> {
typedef ConstBuffer<const char *> Base;
class Request {
std::span<const char *const> args;
public:
constexpr Request(const char *const*argv, size_type n)
:Base(argv, n) {}
explicit constexpr Request(const char *const*argv, std::size_t n)
:args(argv, n) {}
constexpr bool empty() const noexcept {
return args.empty();
}
constexpr std::size_t size() const noexcept {
return args.size();
}
constexpr const char *front() const noexcept {
return args.front();
}
constexpr const char *back() const noexcept {
return args.back();
}
constexpr const char *shift() noexcept {
const char *value = args.front();
args = args.subspan(1);
return value;
}
constexpr const char *pop_back() noexcept {
const char *value = args.back();
args = args.first(args.size() - 1);
return value;
}
constexpr const char *operator[](std::size_t i) const noexcept {
return args[i];
}
constexpr auto begin() const noexcept {
return args.begin();
}
constexpr auto end() const noexcept {
return args.end();
}
constexpr operator std::span<const char *const>() const noexcept {
return args;
}
constexpr const char *GetOptional(unsigned idx,
const char *default_value=nullptr) const {
return idx < size
? data[idx]
return idx < size()
? args[idx]
: default_value;
}
int ParseInt(unsigned idx) const {
assert(idx < size);
return ParseCommandArgInt(data[idx]);
assert(idx < size());
return ParseCommandArgInt(args[idx]);
}
int ParseInt(unsigned idx, int min_value, int max_value) const {
assert(idx < size);
return ParseCommandArgInt(data[idx], min_value, max_value);
assert(idx < size());
return ParseCommandArgInt(args[idx], min_value, max_value);
}
unsigned ParseUnsigned(unsigned idx) const {
assert(idx < size);
return ParseCommandArgUnsigned(data[idx]);
assert(idx < size());
return ParseCommandArgUnsigned(args[idx]);
}
unsigned ParseUnsigned(unsigned idx, unsigned max_value) const {
assert(idx < size);
return ParseCommandArgUnsigned(data[idx], max_value);
assert(idx < size());
return ParseCommandArgUnsigned(args[idx], max_value);
}
bool ParseBool(unsigned idx) const {
assert(idx < size);
return ParseCommandArgBool(data[idx]);
assert(idx < size());
return ParseCommandArgBool(args[idx]);
}
RangeArg ParseRange(unsigned idx) const {
assert(idx < size);
return ParseCommandArgRange(data[idx]);
assert(idx < size());
return ParseCommandArgRange(args[idx]);
}
float ParseFloat(unsigned idx) const {
assert(idx < size);
return ParseCommandArgFloat(data[idx]);
assert(idx < size());
return ParseCommandArgFloat(args[idx]);
}
SongTime ParseSongTime(unsigned idx) const {
assert(idx < size);
return ParseCommandArgSongTime(data[idx]);
assert(idx < size());
return ParseCommandArgSongTime(args[idx]);
}
SignedSongTime ParseSignedSongTime(unsigned idx) const {
assert(idx < size);
return ParseCommandArgSignedSongTime(data[idx]);
assert(idx < size());
return ParseCommandArgSignedSongTime(args[idx]);
}
int ParseOptional(unsigned idx, int default_value) const {
return idx < size
return idx < size()
? ParseInt(idx)
: default_value;
}
RangeArg ParseOptional(unsigned idx, RangeArg default_value) const {
return idx < size
return idx < size()
? ParseRange(idx)
: default_value;
}

View File

@ -59,7 +59,7 @@ handle_sticker_song(Response &r, Partition &partition,
const char *const cmd = args.front();
/* get song song_id key */
if (args.size == 4 && StringIsEqual(cmd, "get")) {
if (args.size() == 4 && StringIsEqual(cmd, "get")) {
const LightSong *song = db.GetSong(args[2]);
assert(song != nullptr);
AtScopeExit(&db, song) { db.ReturnSong(song); };
@ -75,7 +75,7 @@ handle_sticker_song(Response &r, Partition &partition,
return CommandResult::OK;
/* list song song_id */
} else if (args.size == 3 && StringIsEqual(cmd, "list")) {
} else if (args.size() == 3 && StringIsEqual(cmd, "list")) {
const LightSong *song = db.GetSong(args[2]);
assert(song != nullptr);
AtScopeExit(&db, song) { db.ReturnSong(song); };
@ -85,7 +85,7 @@ handle_sticker_song(Response &r, Partition &partition,
return CommandResult::OK;
/* set song song_id id key */
} else if (args.size == 5 && StringIsEqual(cmd, "set")) {
} else if (args.size() == 5 && StringIsEqual(cmd, "set")) {
const LightSong *song = db.GetSong(args[2]);
assert(song != nullptr);
AtScopeExit(&db, song) { db.ReturnSong(song); };
@ -94,13 +94,13 @@ handle_sticker_song(Response &r, Partition &partition,
args[3], args[4]);
return CommandResult::OK;
/* delete song song_id [key] */
} else if ((args.size == 3 || args.size == 4) &&
} else if ((args.size() == 3 || args.size() == 4) &&
StringIsEqual(cmd, "delete")) {
const LightSong *song = db.GetSong(args[2]);
assert(song != nullptr);
AtScopeExit(&db, song) { db.ReturnSong(song); };
bool ret = args.size == 3
bool ret = args.size() == 3
? sticker_song_delete(sticker_database, *song)
: sticker_song_delete_value(sticker_database, *song,
args[3]);
@ -111,7 +111,7 @@ handle_sticker_song(Response &r, Partition &partition,
return CommandResult::OK;
/* find song dir key */
} else if ((args.size == 4 || args.size == 6) &&
} else if ((args.size() == 4 || args.size() == 6) &&
StringIsEqual(cmd, "find")) {
/* "sticker find song a/directory name" */
@ -120,7 +120,7 @@ handle_sticker_song(Response &r, Partition &partition,
StickerOperator op = StickerOperator::EXISTS;
const char *value = nullptr;
if (args.size == 6) {
if (args.size() == 6) {
/* match the value */
const char *op_s = args[4];
@ -157,7 +157,7 @@ handle_sticker_song(Response &r, Partition &partition,
CommandResult
handle_sticker(Client &client, Request args, Response &r)
{
assert(args.size >= 3);
assert(args.size() >= 3);
auto &instance = client.GetInstance();
if (!instance.HasStickerDatabase()) {

View File

@ -22,7 +22,6 @@
#include "Request.hxx"
#include "time/ChronoUtil.hxx"
#include "util/UriUtil.hxx"
#include "util/ConstBuffer.hxx"
#include "fs/Traits.hxx"
#include "client/Client.hxx"
#include "client/Response.hxx"

View File

@ -23,7 +23,6 @@
#include "client/Response.hxx"
#include "tag/ParseName.hxx"
#include "queue/Playlist.hxx"
#include "util/ConstBuffer.hxx"
#include <fmt/format.h>
@ -52,7 +51,7 @@ handle_cleartagid(Client &client, Request args, Response &r)
unsigned song_id = args.ParseUnsigned(0);
TagType tag_type = TAG_NUM_OF_ITEM_TYPES;
if (args.size >= 2) {
if (args.size() >= 2) {
const char *const tag_name = args[1];
tag_type = tag_name_parse_i(tag_name);
if (tag_type == TAG_NUM_OF_ITEM_TYPES) {