mirror of
https://github.com/mirror/busybox.git
synced 2026-06-30 19:57:32 +00:00
shell/math: reduce stack usage
function old new delta arith_apply 1123 1134 +11 arith_lookup_val 140 145 +5 evaluate_string 1053 1047 -6 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/1 up/down: 16/-6) Total: 10 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
66
shell/math.c
66
shell/math.c
@@ -236,14 +236,19 @@ typedef struct {
|
||||
* then '?' selects one of them based on its left side.
|
||||
*/
|
||||
arith_t second_val;
|
||||
char second_val_present;
|
||||
/* If NULL then it's just a number, else it's a named variable */
|
||||
char *var;
|
||||
#define SECOND_VAL_VALID ((char*)(intptr_t)-1)
|
||||
/* If NULL then it's just a number, if SECOND_VAL_VALID,
|
||||
* it's a result of "expr : expr", else it's a named variable.
|
||||
* (We use SECOND_VAL_VALID instead of a bit flag to keep
|
||||
* var_or_num_t smaller, we allocate a lot of them on stack).
|
||||
*/
|
||||
char *var_name;
|
||||
} var_or_num_t;
|
||||
|
||||
|
||||
typedef struct remembered_name {
|
||||
struct remembered_name *next;
|
||||
const char *var;
|
||||
const char *var_name;
|
||||
} remembered_name;
|
||||
|
||||
|
||||
@@ -253,17 +258,17 @@ evaluate_string(arith_state_t *math_state, const char *expr);
|
||||
static const char*
|
||||
arith_lookup_val(arith_state_t *math_state, var_or_num_t *t)
|
||||
{
|
||||
if (t->var) {
|
||||
const char *p = math_state->lookupvar(t->var);
|
||||
if (t->var_name && t->var_name != SECOND_VAL_VALID) {
|
||||
const char *p = math_state->lookupvar(t->var_name);
|
||||
if (p) {
|
||||
remembered_name *cur;
|
||||
remembered_name cur_save;
|
||||
remembered_name remember;
|
||||
|
||||
/* did we already see this name?
|
||||
* testcase: a=b; b=a; echo $((a))
|
||||
*/
|
||||
for (cur = math_state->list_of_recursed_names; cur; cur = cur->next) {
|
||||
if (strcmp(cur->var, t->var) == 0) {
|
||||
if (strcmp(cur->var_name, t->var_name) == 0) {
|
||||
/* Yes */
|
||||
return "expression recursion loop detected";
|
||||
}
|
||||
@@ -271,9 +276,9 @@ arith_lookup_val(arith_state_t *math_state, var_or_num_t *t)
|
||||
|
||||
/* push current var name */
|
||||
cur = math_state->list_of_recursed_names;
|
||||
cur_save.var = t->var;
|
||||
cur_save.next = cur;
|
||||
math_state->list_of_recursed_names = &cur_save;
|
||||
remember.var_name = t->var_name;
|
||||
remember.next = cur;
|
||||
math_state->list_of_recursed_names = &remember;
|
||||
|
||||
/* recursively evaluate p as expression */
|
||||
t->val = evaluate_string(math_state, p);
|
||||
@@ -286,7 +291,7 @@ arith_lookup_val(arith_state_t *math_state, var_or_num_t *t)
|
||||
/* treat undefined var as 0 */
|
||||
t->val = 0;
|
||||
}
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* "Applying" a token means performing it on the top elements on the integer
|
||||
@@ -303,7 +308,7 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_
|
||||
|
||||
/* There is no operator that can work without arguments */
|
||||
if (NUMPTR == numstack)
|
||||
goto err;
|
||||
goto syntax_err;
|
||||
|
||||
top_of_stack = NUMPTR - 1;
|
||||
|
||||
@@ -326,15 +331,15 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_
|
||||
else if (op != TOK_UPLUS) {
|
||||
/* Binary operators */
|
||||
arith_t right_side_val;
|
||||
char bad_second_val;
|
||||
int bad_second_val;
|
||||
|
||||
/* Binary operators need two arguments */
|
||||
if (top_of_stack == numstack)
|
||||
goto err;
|
||||
goto syntax_err;
|
||||
/* ...and they pop one */
|
||||
NUMPTR = top_of_stack; /* this decrements NUMPTR */
|
||||
|
||||
bad_second_val = top_of_stack->second_val_present;
|
||||
bad_second_val = (top_of_stack->var_name == SECOND_VAL_VALID);
|
||||
if (op == TOK_CONDITIONAL) { /* ? operation */
|
||||
/* Make next if (...) protect against
|
||||
* $((expr1 ? expr2)) - that is, missing ": expr" */
|
||||
@@ -363,8 +368,10 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_
|
||||
/* Protect against $((expr : expr)) */
|
||||
return "malformed ?: operator";
|
||||
}
|
||||
top_of_stack->second_val_present = op;
|
||||
top_of_stack->val = rez;
|
||||
top_of_stack->second_val = right_side_val;
|
||||
top_of_stack->var_name = SECOND_VAL_VALID;
|
||||
return NULL;
|
||||
}
|
||||
else if (op == TOK_BOR || op == TOK_OR_ASSIGN)
|
||||
rez |= right_side_val;
|
||||
@@ -439,13 +446,13 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_
|
||||
if (is_assign_op(op)) {
|
||||
char buf[sizeof(arith_t)*3 + 2];
|
||||
|
||||
if (top_of_stack->var == NULL) {
|
||||
if (!top_of_stack->var_name || top_of_stack->var_name == SECOND_VAL_VALID) {
|
||||
/* Hmm, 1=2 ? */
|
||||
goto err;
|
||||
goto syntax_err;
|
||||
}
|
||||
/* Save to shell variable */
|
||||
sprintf(buf, ARITH_FMT, rez);
|
||||
math_state->setvar(top_of_stack->var, buf);
|
||||
math_state->setvar(top_of_stack->var_name, buf);
|
||||
/* After saving, make previous value for v++ or v-- */
|
||||
if (op == TOK_POST_INC)
|
||||
rez--;
|
||||
@@ -455,9 +462,9 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_
|
||||
|
||||
top_of_stack->val = rez;
|
||||
/* Erase var name, it is just a number now */
|
||||
top_of_stack->var = NULL;
|
||||
top_of_stack->var_name = NULL;
|
||||
return NULL;
|
||||
err:
|
||||
syntax_err:
|
||||
return "arithmetic syntax error";
|
||||
#undef NUMPTR
|
||||
}
|
||||
@@ -660,12 +667,11 @@ evaluate_string(arith_state_t *math_state, const char *expr)
|
||||
if (p != expr) {
|
||||
/* Name */
|
||||
size_t var_name_size = (p - expr) + 1; /* +1 for NUL */
|
||||
numstackptr->var = alloca(var_name_size);
|
||||
safe_strncpy(numstackptr->var, expr, var_name_size);
|
||||
numstackptr->var_name = alloca(var_name_size);
|
||||
safe_strncpy(numstackptr->var_name, expr, var_name_size);
|
||||
//bb_error_msg("var:'%s'", numstackptr->var);
|
||||
expr = p;
|
||||
num:
|
||||
numstackptr->second_val_present = 0;
|
||||
numstackptr++;
|
||||
lasttok = TOK_NUM;
|
||||
continue;
|
||||
@@ -673,7 +679,7 @@ evaluate_string(arith_state_t *math_state, const char *expr)
|
||||
|
||||
if (isdigit(*expr)) {
|
||||
/* Number */
|
||||
numstackptr->var = NULL;
|
||||
numstackptr->var_name = NULL;
|
||||
errno = 0;
|
||||
numstackptr->val = strto_arith_t(expr, (char**) &expr);
|
||||
/* A number can't be followed by another number, or a variable name.
|
||||
@@ -702,7 +708,7 @@ evaluate_string(arith_state_t *math_state, const char *expr)
|
||||
if ((expr[0] == '+' || expr[0] == '-')
|
||||
&& (expr[1] == expr[0])
|
||||
) {
|
||||
if (numstackptr == numstack || !numstackptr[-1].var) { /* not a VAR++ */
|
||||
if (numstackptr == numstack || !numstackptr[-1].var_name) { /* not a VAR++ */
|
||||
char next = skip_whitespace(expr + 2)[0];
|
||||
if (!(isalpha(next) || next == '_')) { /* not a ++VAR */
|
||||
//bb_error_msg("special %c%c", expr[0], expr[0]);
|
||||
@@ -795,14 +801,14 @@ evaluate_string(arith_state_t *math_state, const char *expr)
|
||||
//bb_error_msg("op == TOK_RPAREN");
|
||||
if (prev_op == TOK_LPAREN) {
|
||||
//bb_error_msg("prev_op == TOK_LPAREN");
|
||||
//bb_error_msg(" %p %p numstackptr[-1].var:'%s'", numstack, numstackptr-1, numstackptr[-1].var);
|
||||
if (numstackptr[-1].var) {
|
||||
//bb_error_msg(" %p %p numstackptr[-1].var_name:'%s'", numstack, numstackptr-1, numstackptr[-1].var_name);
|
||||
if (numstackptr[-1].var_name && numstackptr[-1].var_name != SECOND_VAL_VALID) {
|
||||
/* Expression is (var), lookup now */
|
||||
errmsg = arith_lookup_val(math_state, &numstackptr[-1]);
|
||||
if (errmsg)
|
||||
goto err_with_custom_msg;
|
||||
/* Erase var name: (var) is just a number, for example, (var) = 1 is not valid */
|
||||
numstackptr[-1].var = NULL;
|
||||
numstackptr[-1].var_name = NULL;
|
||||
}
|
||||
/* Any operator directly after a
|
||||
* close paren should consider itself binary */
|
||||
|
||||
Reference in New Issue
Block a user