debugcon.cpp: Use std::string_view in command parsing and eliminate initial buffer copy

This commit is contained in:
AJR 2022-02-21 08:47:03 -05:00
parent 1f2fb58b87
commit e10f87d9fc
2 changed files with 118 additions and 140 deletions

View File

@ -92,9 +92,9 @@ debugger_console::~debugger_console()
} }
/*------------------------------------------------- //-------------------------------------------------
exit - frees the console system // exit - frees the console system
-------------------------------------------------*/ //-------------------------------------------------
void debugger_console::exit() void debugger_console::exit()
{ {
@ -119,41 +119,39 @@ void debugger_console::exit()
inline bool debugger_console::debug_command::compare::operator()(const debug_command &a, const debug_command &b) const inline bool debugger_console::debug_command::compare::operator()(const debug_command &a, const debug_command &b) const
{ {
return core_stricmp(a.command.c_str(), b.command.c_str()) < 0; return a.command < b.command;
} }
inline bool debugger_console::debug_command::compare::operator()(const char *a, const debug_command &b) const inline bool debugger_console::debug_command::compare::operator()(const char *a, const debug_command &b) const
{ {
return core_stricmp(a, b.command.c_str()) < 0; return strcmp(a, b.command.c_str()) < 0;
} }
inline bool debugger_console::debug_command::compare::operator()(const debug_command &a, const char *b) const inline bool debugger_console::debug_command::compare::operator()(const debug_command &a, const char *b) const
{ {
return core_stricmp(a.command.c_str(), b) < 0; return strcmp(a.command.c_str(), b) < 0;
} }
debugger_console::debug_command::debug_command(const char *_command, u32 _flags, int _minparams, int _maxparams, std::function<void (const std::vector<std::string> &)> &&_handler) debugger_console::debug_command::debug_command(std::string_view _command, u32 _flags, int _minparams, int _maxparams, std::function<void (const std::vector<std::string> &)> &&_handler)
: command(_command), params(nullptr), help(nullptr), handler(std::move(_handler)), flags(_flags), minparams(_minparams), maxparams(_maxparams) : command(_command), params(nullptr), help(nullptr), handler(std::move(_handler)), flags(_flags), minparams(_minparams), maxparams(_maxparams)
{ {
} }
/*------------------------------------------------------------ //------------------------------------------------------------
execute_help_custom - execute the helpcustom command // execute_help_custom - execute the helpcustom command
------------------------------------------------------------*/ //------------------------------------------------------------
void debugger_console::execute_help_custom(const std::vector<std::string> &params) void debugger_console::execute_help_custom(const std::vector<std::string> &params)
{ {
char buf[64];
for (const debug_command &cmd : m_commandlist) for (const debug_command &cmd : m_commandlist)
{ {
if (cmd.flags & CMDFLAG_CUSTOM_HELP) if (cmd.flags & CMDFLAG_CUSTOM_HELP)
{ {
snprintf(buf, 63, "%s help", cmd.command.c_str()); std::string buf = cmd.command + " help";
buf[63] = 0; std::vector<std::string_view> temp_params = { buf };
char *temp_params[1] = { buf }; internal_execute_command(true, temp_params);
internal_execute_command(true, 1, &temp_params[0]);
} }
} }
} }
@ -207,159 +205,145 @@ symbol_table &debugger_console::visible_symtable()
/*------------------------------------------------- //-------------------------------------------------
trim_parameter - executes a // trim_parameter - trim spaces and quotes around
command // a command parameter
-------------------------------------------------*/ //-------------------------------------------------
void debugger_console::trim_parameter(char **paramptr, bool keep_quotes) std::string_view debugger_console::trim_parameter(std::string_view param, bool keep_quotes)
{ {
char *param = *paramptr; std::string_view::size_type len = param.length();
size_t len = strlen(param);
bool repeat; bool repeat;
/* loop until all adornments are gone */ // loop until all adornments are gone
do do
{ {
repeat = false; repeat = false;
/* check for begin/end quotes */ // check for begin/end quotes
if (len >= 2 && param[0] == '"' && param[len - 1] == '"') if (len >= 2 && param[0] == '"' && param[len - 1] == '"')
{ {
if (!keep_quotes) if (!keep_quotes)
{ {
param[len - 1] = 0; param = param.substr(1, len - 2);
param++;
len -= 2; len -= 2;
} }
} }
/* check for start/end braces */ // check for start/end braces
else if (len >= 2 && param[0] == '{' && param[len - 1] == '}') else if (len >= 2 && param[0] == '{' && param[len - 1] == '}')
{ {
param[len - 1] = 0; param = param.substr(1, len - 2);
param++;
len -= 2; len -= 2;
repeat = true; repeat = true;
} }
/* check for leading spaces */ // check for leading spaces
else if (len >= 1 && param[0] == ' ') else if (len >= 1 && param[0] == ' ')
{ {
param++; param.remove_prefix(1);
len--; len--;
repeat = true; repeat = true;
} }
/* check for trailing spaces */ // check for trailing spaces
else if (len >= 1 && param[len - 1] == ' ') else if (len >= 1 && param[len - 1] == ' ')
{ {
param[len - 1] = 0; param.remove_suffix(1);
len--; len--;
repeat = true; repeat = true;
} }
} while (repeat); } while (repeat);
*paramptr = param; return param;
} }
/*------------------------------------------------- //-------------------------------------------------
internal_execute_command - executes a // internal_execute_command - executes a
command // command
-------------------------------------------------*/ //-------------------------------------------------
CMDERR debugger_console::internal_execute_command(bool execute, int params, char **param) CMDERR debugger_console::internal_execute_command(bool execute, std::vector<std::string_view> &params)
{ {
// no params is an error // no params is an error
if (params == 0) if (params.empty())
return CMDERR::none(); return CMDERR::none();
// the first parameter has the command and the real first parameter; separate them // the first parameter has the command and the real first parameter; separate them
char *p, *command; std::string_view command_param = params[0];
for (p = param[0]; *p && isspace(u8(*p)); p++) { } std::string_view::size_type pos = 0;
for (command = p; *p && !isspace(u8(*p)); p++) { } while (pos < command_param.length() && !isspace(u8(command_param[pos])))
if (*p != 0) pos++;
{ const std::string command(strmakelower(command_param.substr(0, pos)));
*p++ = 0; while (pos < command_param.length() && isspace(u8(command_param[pos])))
for ( ; *p && isspace(u8(*p)); p++) { } pos++;
if (*p != 0) if (pos == command_param.length() && params.size() == 1)
param[0] = p; params.clear();
else
params = 0;
}
else else
{ params[0].remove_prefix(pos);
params = 0;
param[0] = nullptr;
}
// search the command list // search the command list
size_t const len = strlen(command); auto const found = m_commandlist.lower_bound(command.c_str());
auto const found = m_commandlist.lower_bound(command);
// error if not found // error if not found
if ((m_commandlist.end() == found) || core_strnicmp(command, found->command.c_str(), len)) if (m_commandlist.end() == found || std::string_view(command) != std::string_view(found->command).substr(0, command.length()))
return CMDERR::unknown_command(0); return CMDERR::unknown_command(0);
if (found->command.length() > len) if (found->command.length() > command.length())
{ {
auto const next = std::next(found); auto const next = std::next(found);
if ((m_commandlist.end() != next) && !core_strnicmp(command, next->command.c_str(), len)) if (m_commandlist.end() != next && std::string_view(command) == std::string_view(next->command).substr(0, command.length()))
return CMDERR::ambiguous_command(0); return CMDERR::ambiguous_command(0);
} }
// NUL-terminate and trim space around all the parameters
for (int i = 1; i < params; i++)
*param[i]++ = 0;
// now go back and trim quotes and braces and any spaces they reveal // now go back and trim quotes and braces and any spaces they reveal
for (int i = 0; i < params; i++) for (std::string_view &param : params)
trim_parameter(&param[i], found->flags & CMDFLAG_KEEP_QUOTES); param = trim_parameter(param, found->flags & CMDFLAG_KEEP_QUOTES);
// see if we have the right number of parameters // see if we have the right number of parameters
if (params < found->minparams) if (params.size() < found->minparams)
return CMDERR::not_enough_params(0); return CMDERR::not_enough_params(0);
if (params > found->maxparams) if (params.size() > found->maxparams)
return CMDERR::too_many_params(0); return CMDERR::too_many_params(0);
// execute the handler // execute the handler
if (execute) if (execute)
{ {
std::vector<std::string> params_vec(param, param + params); std::vector<std::string> params_vec(params.begin(), params.end());
found->handler(params_vec); found->handler(params_vec);
} }
return CMDERR::none(); return CMDERR::none();
} }
/*------------------------------------------------- //-------------------------------------------------
internal_parse_command - parses a command // internal_parse_command - parses a command
and either executes or just validates it // and either executes or just validates it
-------------------------------------------------*/ //-------------------------------------------------
CMDERR debugger_console::internal_parse_command(const std::string &original_command, bool execute) CMDERR debugger_console::internal_parse_command(std::string_view command, bool execute)
{ {
char command[MAX_COMMAND_LENGTH], parens[MAX_COMMAND_LENGTH]; std::string_view::size_type pos = 0;
char *params[MAX_COMMAND_PARAMS] = { nullptr }; std::string_view::size_type len = command.length();
char *command_start;
char *p, c = 0;
/* make a copy of the command */ while (pos < len)
strcpy(command, original_command.c_str());
/* loop over all semicolon-separated stuff */
for (p = command; *p != 0; )
{ {
int paramcount = 0, parendex = 0; std::string parens;
std::vector<std::string_view> params;
bool foundend = false, instring = false, isexpr = false; bool foundend = false, instring = false, isexpr = false;
/* find a semicolon or the end */ // skip leading spaces
for (params[paramcount++] = p; !foundend; p++) while (pos < len && isspace(u8(command[pos])))
pos++;
std::string_view::size_type startpos = pos;
// find a semicolon or the end
for (params.push_back(command.substr(pos)); !foundend && pos < len; pos++)
{ {
c = *p; char c = command[pos];
if (instring) if (instring)
{ {
if (c == '"' && p[-1] != '\\') if (c == '"' && command[pos - 1] != '\\')
instring = false; instring = false;
} }
else else
@ -369,47 +353,42 @@ CMDERR debugger_console::internal_parse_command(const std::string &original_comm
case '"': instring = true; break; case '"': instring = true; break;
case '(': case '(':
case '[': case '[':
case '{': parens[parendex++] = c; break; case '{': parens.push_back(c); break;
case ')': if (parendex == 0 || parens[--parendex] != '(') return CMDERR::unbalanced_parens(p - command); break; case ')': if (parens.empty() || parens.back() != '(') return CMDERR::unbalanced_parens(pos); parens.pop_back(); break;
case ']': if (parendex == 0 || parens[--parendex] != '[') return CMDERR::unbalanced_parens(p - command); break; case ']': if (parens.empty() || parens.back() != '[') return CMDERR::unbalanced_parens(pos); parens.pop_back(); break;
case '}': if (parendex == 0 || parens[--parendex] != '{') return CMDERR::unbalanced_parens(p - command); break; case '}': if (parens.empty() || parens.back() != '{') return CMDERR::unbalanced_parens(pos); parens.pop_back(); break;
case ',': if (parendex == 0) params[paramcount++] = p; break; case ',': if (parens.empty()) { params.back().remove_suffix(len - pos); params.push_back(command.substr(pos + 1)); } break;
case ';': if (parendex == 0) foundend = true; break; case ';': if (parens.empty()) { params.back().remove_suffix(len - pos); foundend = true; } break;
case '-': if (parendex == 0 && paramcount == 1 && p[1] == '-') isexpr = true; *p = c; break; case '-': if (parens.empty() && params.size() == 1 && pos > 0 && command[pos - 1] == '-') isexpr = true; break;
case '+': if (parendex == 0 && paramcount == 1 && p[1] == '+') isexpr = true; *p = c; break; case '+': if (parens.empty() && params.size() == 1 && pos > 0 && command[pos - 1] == '+') isexpr = true; break;
case '=': if (parendex == 0 && paramcount == 1) isexpr = true; *p = c; break; case '=': if (parens.empty() && params.size() == 1) isexpr = true; break;
case 0: foundend = true; break; default: break;
default: *p = c; break;
} }
} }
} }
/* check for unbalanced parentheses or quotes */ // check for unbalanced parentheses or quotes
if (instring) if (instring)
return CMDERR::unbalanced_quotes(p - command); return CMDERR::unbalanced_quotes(pos);
if (parendex != 0) if (!parens.empty())
return CMDERR::unbalanced_parens(p - command); return CMDERR::unbalanced_parens(pos);
/* NULL-terminate if we ended in a semicolon */ // process the command
p--; std::string_view command_or_expr = params[0];
if (c == ';') *p++ = 0;
/* process the command */ // allow for "do" commands
command_start = params[0]; if (command_or_expr.length() > 3 && tolower(u8(command_or_expr[0])) == 'd' && tolower(u8(command_or_expr[1])) == 'o' && isspace(u8(command_or_expr[2])))
/* allow for "do" commands */
if (tolower(u8(command_start[0])) == 'd' && tolower(u8(command_start[1])) == 'o' && isspace(u8(command_start[2])))
{ {
isexpr = true; isexpr = true;
command_start += 3; command_or_expr.remove_prefix(3);
} }
/* if it smells like an assignment expression, treat it as such */ // if it smells like an assignment expression, treat it as such
if (isexpr && paramcount == 1) if (isexpr && params.size() == 1)
{ {
try try
{ {
parsed_expression(visible_symtable(), command_start).execute(); parsed_expression(visible_symtable(), command_or_expr).execute();
} }
catch (expression_error &err) catch (expression_error &err)
{ {
@ -418,29 +397,29 @@ CMDERR debugger_console::internal_parse_command(const std::string &original_comm
} }
else else
{ {
const CMDERR result = internal_execute_command(execute, paramcount, &params[0]); const CMDERR result = internal_execute_command(execute, params);
if (result.error_class() != CMDERR::NONE) if (result.error_class() != CMDERR::NONE)
return CMDERR(result.error_class(), command_start - command); return CMDERR(result.error_class(), startpos);
} }
} }
return CMDERR::none(); return CMDERR::none();
} }
/*------------------------------------------------- //-------------------------------------------------
execute_command - execute a command string // execute_command - execute a command string
-------------------------------------------------*/ //-------------------------------------------------
CMDERR debugger_console::execute_command(const std::string &command, bool echo) CMDERR debugger_console::execute_command(std::string_view command, bool echo)
{ {
/* echo if requested */ // echo if requested
if (echo) if (echo)
printf(">%s\n", command); printf(">%s\n", command);
/* parse and execute */ // parse and execute
const CMDERR result = internal_parse_command(command, true); const CMDERR result = internal_parse_command(command, true);
/* display errors */ // display errors
if (result.error_class() != CMDERR::NONE) if (result.error_class() != CMDERR::NONE)
{ {
if (!echo) if (!echo)
@ -449,7 +428,7 @@ CMDERR debugger_console::execute_command(const std::string &command, bool echo)
printf("%s\n", cmderr_to_string(result)); printf("%s\n", cmderr_to_string(result));
} }
/* update all views */ // update all views
if (echo) if (echo)
{ {
m_machine.debug_view().update_all(); m_machine.debug_view().update_all();
@ -459,11 +438,11 @@ CMDERR debugger_console::execute_command(const std::string &command, bool echo)
} }
/*------------------------------------------------- //-------------------------------------------------
validate_command - validate a command string // validate_command - validate a command string
-------------------------------------------------*/ //-------------------------------------------------
CMDERR debugger_console::validate_command(const char *command) CMDERR debugger_console::validate_command(std::string_view command)
{ {
return internal_parse_command(command, false); return internal_parse_command(command, false);
} }
@ -473,7 +452,7 @@ CMDERR debugger_console::validate_command(const char *command)
register_command - register a command handler register_command - register a command handler
-------------------------------------------------*/ -------------------------------------------------*/
void debugger_console::register_command(const char *command, u32 flags, int minparams, int maxparams, std::function<void (const std::vector<std::string> &)> &&handler) void debugger_console::register_command(std::string_view command, u32 flags, int minparams, int maxparams, std::function<void (const std::vector<std::string> &)> &&handler)
{ {
if (m_machine.phase() != machine_phase::INIT) if (m_machine.phase() != machine_phase::INIT)
throw emu_fatalerror("Can only call debugger_console::register_command() at init time!"); throw emu_fatalerror("Can only call debugger_console::register_command() at init time!");

View File

@ -23,8 +23,7 @@
CONSTANTS CONSTANTS
***************************************************************************/ ***************************************************************************/
#define MAX_COMMAND_LENGTH 4096 constexpr int MAX_COMMAND_PARAMS = 128;
#define MAX_COMMAND_PARAMS 128
// flags for command parsing // flags for command parsing
constexpr u32 CMDFLAG_NONE = 0x0000; constexpr u32 CMDFLAG_NONE = 0x0000;
@ -82,9 +81,9 @@ public:
~debugger_console(); ~debugger_console();
// command handling // command handling
CMDERR execute_command(const std::string &command, bool echo); CMDERR execute_command(std::string_view command, bool echo);
CMDERR validate_command(const char *command); CMDERR validate_command(std::string_view command);
void register_command(const char *command, u32 flags, int minparams, int maxparams, std::function<void (const std::vector<std::string> &)> &&handler); void register_command(std::string_view command, u32 flags, int minparams, int maxparams, std::function<void (const std::vector<std::string> &)> &&handler);
void source_script(const char *file); void source_script(const char *file);
void process_source_file(); void process_source_file();
@ -123,16 +122,16 @@ private:
void execute_help_custom(const std::vector<std::string> &params); void execute_help_custom(const std::vector<std::string> &params);
void execute_condump(const std::vector<std::string>& params); void execute_condump(const std::vector<std::string>& params);
void trim_parameter(char **paramptr, bool keep_quotes); [[nodiscard]] static std::string_view trim_parameter(std::string_view param, bool keep_quotes);
CMDERR internal_execute_command(bool execute, int params, char **param); CMDERR internal_execute_command(bool execute, std::vector<std::string_view> &params);
CMDERR internal_parse_command(const std::string &original_command, bool execute); CMDERR internal_parse_command(std::string_view command, bool execute);
void print_core(std::string_view text); // core text output void print_core(std::string_view text); // core text output
void print_core_wrap(std::string_view text, int wrapcol); // core text output void print_core_wrap(std::string_view text, int wrapcol); // core text output
struct debug_command struct debug_command
{ {
debug_command(const char *_command, u32 _flags, int _minparams, int _maxparams, std::function<void (const std::vector<std::string> &)> &&_handler); debug_command(std::string_view _command, u32 _flags, int _minparams, int _maxparams, std::function<void (const std::vector<std::string> &)> &&_handler);
struct compare struct compare
{ {