diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2023-06-13 13:55:13 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2023-06-13 13:55:13 +0200 |
commit | a4f30f3c70550ee6ceea6768864bfb2e3a99a887 (patch) | |
tree | d9499d794557290984bdc785356e7417ddbfd6f4 | |
parent | d417193cf37ca1005830d7e16f5fa7e1d8a44209 (diff) | |
download | busybox-w32-a4f30f3c70550ee6ceea6768864bfb2e3a99a887.tar.gz busybox-w32-a4f30f3c70550ee6ceea6768864bfb2e3a99a887.tar.bz2 busybox-w32-a4f30f3c70550ee6ceea6768864bfb2e3a99a887.zip |
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>
-rw-r--r-- | shell/math.c | 66 |
1 files changed, 36 insertions, 30 deletions
diff --git a/shell/math.c b/shell/math.c index 727c29467..af8e691fe 100644 --- a/shell/math.c +++ b/shell/math.c | |||
@@ -236,14 +236,19 @@ typedef struct { | |||
236 | * then '?' selects one of them based on its left side. | 236 | * then '?' selects one of them based on its left side. |
237 | */ | 237 | */ |
238 | arith_t second_val; | 238 | arith_t second_val; |
239 | char second_val_present; | 239 | #define SECOND_VAL_VALID ((char*)(intptr_t)-1) |
240 | /* If NULL then it's just a number, else it's a named variable */ | 240 | /* If NULL then it's just a number, if SECOND_VAL_VALID, |
241 | char *var; | 241 | * it's a result of "expr : expr", else it's a named variable. |
242 | * (We use SECOND_VAL_VALID instead of a bit flag to keep | ||
243 | * var_or_num_t smaller, we allocate a lot of them on stack). | ||
244 | */ | ||
245 | char *var_name; | ||
242 | } var_or_num_t; | 246 | } var_or_num_t; |
243 | 247 | ||
248 | |||
244 | typedef struct remembered_name { | 249 | typedef struct remembered_name { |
245 | struct remembered_name *next; | 250 | struct remembered_name *next; |
246 | const char *var; | 251 | const char *var_name; |
247 | } remembered_name; | 252 | } remembered_name; |
248 | 253 | ||
249 | 254 | ||
@@ -253,17 +258,17 @@ evaluate_string(arith_state_t *math_state, const char *expr); | |||
253 | static const char* | 258 | static const char* |
254 | arith_lookup_val(arith_state_t *math_state, var_or_num_t *t) | 259 | arith_lookup_val(arith_state_t *math_state, var_or_num_t *t) |
255 | { | 260 | { |
256 | if (t->var) { | 261 | if (t->var_name && t->var_name != SECOND_VAL_VALID) { |
257 | const char *p = math_state->lookupvar(t->var); | 262 | const char *p = math_state->lookupvar(t->var_name); |
258 | if (p) { | 263 | if (p) { |
259 | remembered_name *cur; | 264 | remembered_name *cur; |
260 | remembered_name cur_save; | 265 | remembered_name remember; |
261 | 266 | ||
262 | /* did we already see this name? | 267 | /* did we already see this name? |
263 | * testcase: a=b; b=a; echo $((a)) | 268 | * testcase: a=b; b=a; echo $((a)) |
264 | */ | 269 | */ |
265 | for (cur = math_state->list_of_recursed_names; cur; cur = cur->next) { | 270 | for (cur = math_state->list_of_recursed_names; cur; cur = cur->next) { |
266 | if (strcmp(cur->var, t->var) == 0) { | 271 | if (strcmp(cur->var_name, t->var_name) == 0) { |
267 | /* Yes */ | 272 | /* Yes */ |
268 | return "expression recursion loop detected"; | 273 | return "expression recursion loop detected"; |
269 | } | 274 | } |
@@ -271,9 +276,9 @@ arith_lookup_val(arith_state_t *math_state, var_or_num_t *t) | |||
271 | 276 | ||
272 | /* push current var name */ | 277 | /* push current var name */ |
273 | cur = math_state->list_of_recursed_names; | 278 | cur = math_state->list_of_recursed_names; |
274 | cur_save.var = t->var; | 279 | remember.var_name = t->var_name; |
275 | cur_save.next = cur; | 280 | remember.next = cur; |
276 | math_state->list_of_recursed_names = &cur_save; | 281 | math_state->list_of_recursed_names = &remember; |
277 | 282 | ||
278 | /* recursively evaluate p as expression */ | 283 | /* recursively evaluate p as expression */ |
279 | t->val = evaluate_string(math_state, p); | 284 | t->val = evaluate_string(math_state, p); |
@@ -286,7 +291,7 @@ arith_lookup_val(arith_state_t *math_state, var_or_num_t *t) | |||
286 | /* treat undefined var as 0 */ | 291 | /* treat undefined var as 0 */ |
287 | t->val = 0; | 292 | t->val = 0; |
288 | } | 293 | } |
289 | return 0; | 294 | return NULL; |
290 | } | 295 | } |
291 | 296 | ||
292 | /* "Applying" a token means performing it on the top elements on the integer | 297 | /* "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_ | |||
303 | 308 | ||
304 | /* There is no operator that can work without arguments */ | 309 | /* There is no operator that can work without arguments */ |
305 | if (NUMPTR == numstack) | 310 | if (NUMPTR == numstack) |
306 | goto err; | 311 | goto syntax_err; |
307 | 312 | ||
308 | top_of_stack = NUMPTR - 1; | 313 | top_of_stack = NUMPTR - 1; |
309 | 314 | ||
@@ -326,15 +331,15 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_ | |||
326 | else if (op != TOK_UPLUS) { | 331 | else if (op != TOK_UPLUS) { |
327 | /* Binary operators */ | 332 | /* Binary operators */ |
328 | arith_t right_side_val; | 333 | arith_t right_side_val; |
329 | char bad_second_val; | 334 | int bad_second_val; |
330 | 335 | ||
331 | /* Binary operators need two arguments */ | 336 | /* Binary operators need two arguments */ |
332 | if (top_of_stack == numstack) | 337 | if (top_of_stack == numstack) |
333 | goto err; | 338 | goto syntax_err; |
334 | /* ...and they pop one */ | 339 | /* ...and they pop one */ |
335 | NUMPTR = top_of_stack; /* this decrements NUMPTR */ | 340 | NUMPTR = top_of_stack; /* this decrements NUMPTR */ |
336 | 341 | ||
337 | bad_second_val = top_of_stack->second_val_present; | 342 | bad_second_val = (top_of_stack->var_name == SECOND_VAL_VALID); |
338 | if (op == TOK_CONDITIONAL) { /* ? operation */ | 343 | if (op == TOK_CONDITIONAL) { /* ? operation */ |
339 | /* Make next if (...) protect against | 344 | /* Make next if (...) protect against |
340 | * $((expr1 ? expr2)) - that is, missing ": expr" */ | 345 | * $((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_ | |||
363 | /* Protect against $((expr : expr)) */ | 368 | /* Protect against $((expr : expr)) */ |
364 | return "malformed ?: operator"; | 369 | return "malformed ?: operator"; |
365 | } | 370 | } |
366 | top_of_stack->second_val_present = op; | 371 | top_of_stack->val = rez; |
367 | top_of_stack->second_val = right_side_val; | 372 | top_of_stack->second_val = right_side_val; |
373 | top_of_stack->var_name = SECOND_VAL_VALID; | ||
374 | return NULL; | ||
368 | } | 375 | } |
369 | else if (op == TOK_BOR || op == TOK_OR_ASSIGN) | 376 | else if (op == TOK_BOR || op == TOK_OR_ASSIGN) |
370 | rez |= right_side_val; | 377 | rez |= right_side_val; |
@@ -439,13 +446,13 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_ | |||
439 | if (is_assign_op(op)) { | 446 | if (is_assign_op(op)) { |
440 | char buf[sizeof(arith_t)*3 + 2]; | 447 | char buf[sizeof(arith_t)*3 + 2]; |
441 | 448 | ||
442 | if (top_of_stack->var == NULL) { | 449 | if (!top_of_stack->var_name || top_of_stack->var_name == SECOND_VAL_VALID) { |
443 | /* Hmm, 1=2 ? */ | 450 | /* Hmm, 1=2 ? */ |
444 | goto err; | 451 | goto syntax_err; |
445 | } | 452 | } |
446 | /* Save to shell variable */ | 453 | /* Save to shell variable */ |
447 | sprintf(buf, ARITH_FMT, rez); | 454 | sprintf(buf, ARITH_FMT, rez); |
448 | math_state->setvar(top_of_stack->var, buf); | 455 | math_state->setvar(top_of_stack->var_name, buf); |
449 | /* After saving, make previous value for v++ or v-- */ | 456 | /* After saving, make previous value for v++ or v-- */ |
450 | if (op == TOK_POST_INC) | 457 | if (op == TOK_POST_INC) |
451 | rez--; | 458 | rez--; |
@@ -455,9 +462,9 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_ | |||
455 | 462 | ||
456 | top_of_stack->val = rez; | 463 | top_of_stack->val = rez; |
457 | /* Erase var name, it is just a number now */ | 464 | /* Erase var name, it is just a number now */ |
458 | top_of_stack->var = NULL; | 465 | top_of_stack->var_name = NULL; |
459 | return NULL; | 466 | return NULL; |
460 | err: | 467 | syntax_err: |
461 | return "arithmetic syntax error"; | 468 | return "arithmetic syntax error"; |
462 | #undef NUMPTR | 469 | #undef NUMPTR |
463 | } | 470 | } |
@@ -660,12 +667,11 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
660 | if (p != expr) { | 667 | if (p != expr) { |
661 | /* Name */ | 668 | /* Name */ |
662 | size_t var_name_size = (p - expr) + 1; /* +1 for NUL */ | 669 | size_t var_name_size = (p - expr) + 1; /* +1 for NUL */ |
663 | numstackptr->var = alloca(var_name_size); | 670 | numstackptr->var_name = alloca(var_name_size); |
664 | safe_strncpy(numstackptr->var, expr, var_name_size); | 671 | safe_strncpy(numstackptr->var_name, expr, var_name_size); |
665 | //bb_error_msg("var:'%s'", numstackptr->var); | 672 | //bb_error_msg("var:'%s'", numstackptr->var); |
666 | expr = p; | 673 | expr = p; |
667 | num: | 674 | num: |
668 | numstackptr->second_val_present = 0; | ||
669 | numstackptr++; | 675 | numstackptr++; |
670 | lasttok = TOK_NUM; | 676 | lasttok = TOK_NUM; |
671 | continue; | 677 | continue; |
@@ -673,7 +679,7 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
673 | 679 | ||
674 | if (isdigit(*expr)) { | 680 | if (isdigit(*expr)) { |
675 | /* Number */ | 681 | /* Number */ |
676 | numstackptr->var = NULL; | 682 | numstackptr->var_name = NULL; |
677 | errno = 0; | 683 | errno = 0; |
678 | numstackptr->val = strto_arith_t(expr, (char**) &expr); | 684 | numstackptr->val = strto_arith_t(expr, (char**) &expr); |
679 | /* A number can't be followed by another number, or a variable name. | 685 | /* 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) | |||
702 | if ((expr[0] == '+' || expr[0] == '-') | 708 | if ((expr[0] == '+' || expr[0] == '-') |
703 | && (expr[1] == expr[0]) | 709 | && (expr[1] == expr[0]) |
704 | ) { | 710 | ) { |
705 | if (numstackptr == numstack || !numstackptr[-1].var) { /* not a VAR++ */ | 711 | if (numstackptr == numstack || !numstackptr[-1].var_name) { /* not a VAR++ */ |
706 | char next = skip_whitespace(expr + 2)[0]; | 712 | char next = skip_whitespace(expr + 2)[0]; |
707 | if (!(isalpha(next) || next == '_')) { /* not a ++VAR */ | 713 | if (!(isalpha(next) || next == '_')) { /* not a ++VAR */ |
708 | //bb_error_msg("special %c%c", expr[0], expr[0]); | 714 | //bb_error_msg("special %c%c", expr[0], expr[0]); |
@@ -795,14 +801,14 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
795 | //bb_error_msg("op == TOK_RPAREN"); | 801 | //bb_error_msg("op == TOK_RPAREN"); |
796 | if (prev_op == TOK_LPAREN) { | 802 | if (prev_op == TOK_LPAREN) { |
797 | //bb_error_msg("prev_op == TOK_LPAREN"); | 803 | //bb_error_msg("prev_op == TOK_LPAREN"); |
798 | //bb_error_msg(" %p %p numstackptr[-1].var:'%s'", numstack, numstackptr-1, numstackptr[-1].var); | 804 | //bb_error_msg(" %p %p numstackptr[-1].var_name:'%s'", numstack, numstackptr-1, numstackptr[-1].var_name); |
799 | if (numstackptr[-1].var) { | 805 | if (numstackptr[-1].var_name && numstackptr[-1].var_name != SECOND_VAL_VALID) { |
800 | /* Expression is (var), lookup now */ | 806 | /* Expression is (var), lookup now */ |
801 | errmsg = arith_lookup_val(math_state, &numstackptr[-1]); | 807 | errmsg = arith_lookup_val(math_state, &numstackptr[-1]); |
802 | if (errmsg) | 808 | if (errmsg) |
803 | goto err_with_custom_msg; | 809 | goto err_with_custom_msg; |
804 | /* Erase var name: (var) is just a number, for example, (var) = 1 is not valid */ | 810 | /* Erase var name: (var) is just a number, for example, (var) = 1 is not valid */ |
805 | numstackptr[-1].var = NULL; | 811 | numstackptr[-1].var_name = NULL; |
806 | } | 812 | } |
807 | /* Any operator directly after a | 813 | /* Any operator directly after a |
808 | * close paren should consider itself binary */ | 814 | * close paren should consider itself binary */ |