aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2023-06-13 13:55:13 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2023-06-13 13:55:13 +0200
commita4f30f3c70550ee6ceea6768864bfb2e3a99a887 (patch)
treed9499d794557290984bdc785356e7417ddbfd6f4
parentd417193cf37ca1005830d7e16f5fa7e1d8a44209 (diff)
downloadbusybox-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.c66
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
244typedef struct remembered_name { 249typedef 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);
253static const char* 258static const char*
254arith_lookup_val(arith_state_t *math_state, var_or_num_t *t) 259arith_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 */