bc: change bc_read_line() and zbc_vm_stdin() to avoid double buffers
authorDenys Vlasenko <vda.linux@googlemail.com>
Thu, 13 Dec 2018 18:23:45 +0000 (19:23 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Thu, 13 Dec 2018 18:24:42 +0000 (19:24 +0100)
function                                             old     new   delta
bc_read_line                                         129     124      -5
bc_vm_run                                            523     433     -90
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 0/2 up/down: 0/-95)             Total: -95 bytes
   text    data     bss     dec     hex filename
 980445     485    7296  988226   f1442 busybox_old
 980350     485    7296  988131   f13e3 busybox_unstripped

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
miscutils/bc.c

index bc294716150aee2768b2a3afe8ac24c98362813b..7c4dfbb20c06b06d220f1fe43d94b5435bda097e 100644 (file)
@@ -1226,6 +1226,7 @@ static void bc_vec_string(BcVec *v, size_t len, const char *str)
        bc_vec_pushZeroByte(v);
 }
 
+#if ENABLE_FEATURE_BC_SIGNALS && ENABLE_FEATURE_EDITING
 static void bc_vec_concat(BcVec *v, const char *str)
 {
        size_t len, slen;
@@ -1240,6 +1241,7 @@ static void bc_vec_concat(BcVec *v, const char *str)
 
        v->len = len;
 }
+#endif
 
 static void *bc_vec_item(const BcVec *v, size_t idx)
 {
@@ -1326,29 +1328,21 @@ static size_t bc_map_index(const BcVec *v, const void *ptr)
 }
 #endif
 
-static int push_input_byte(BcVec *vec, char c)
+static int bad_input_byte(char c)
 {
        if ((c < ' ' && c != '\t' && c != '\r' && c != '\n') // also allow '\v' '\f'?
         || c > 0x7e
        ) {
-               // Bad chars on this line, ignore entire line
                bc_error_fmt("illegal character 0x%02x", c);
                return 1;
        }
-       bc_vec_pushByte(vec, (char)c);
        return 0;
 }
 
+// Note: it _appends_ data from the stdin to vec.
 static void bc_read_line(BcVec *vec)
 {
-       bool bad_chars;
-
-       do {
-               int c;
-
-               bad_chars = 0;
-               bc_vec_pop_all(vec);
-
+ again:
                fflush_and_check();
 
 #if ENABLE_FEATURE_BC_SIGNALS
@@ -1359,6 +1353,7 @@ static void bc_read_line(BcVec *vec)
                        // GNU dc says "Interrupt!"
                        fputs("\ninterrupted execution\n", stderr);
                }
+
 # if ENABLE_FEATURE_EDITING
                if (G_ttyin) {
                        int n, i;
@@ -1371,15 +1366,20 @@ static void bc_read_line(BcVec *vec)
                        }
                        i = 0;
                        for (;;) {
-                               c = line_buf[i++];
+                               char c = line_buf[i++];
                                if (!c) break;
-                               bad_chars |= push_input_byte(vec, c);
+                               if (bad_input_byte(c)) goto again;
                        }
+                       bc_vec_concat(vec, line_buf);
 #  undef line_buf
                } else
 # endif
 #endif
                {
+                       int c;
+                       bool bad_chars = 0;
+                       size_t len = vec->len;
+
                        IF_FEATURE_BC_SIGNALS(errno = 0;)
                        do {
                                c = fgetc(stdin);
@@ -1399,10 +1399,15 @@ static void bc_read_line(BcVec *vec)
                                        // printf 'print 123' | bc   - fails (syntax error)
                                        break;
                                }
-                               bad_chars |= push_input_byte(vec, c);
+                               bad_chars |= bad_input_byte(c);
+                               bc_vec_pushByte(vec, (char)c);
                        } while (c != '\n');
+                       if (bad_chars) {
+                               // Bad chars on this line, ignore entire line
+                               vec->len = len;
+                               goto again;
+                       }
                }
-       } while (bad_chars);
 
        bc_vec_pushZeroByte(vec);
 }
@@ -5374,12 +5379,12 @@ static BC_STATUS zbc_program_read(void)
 
        f = bc_program_func(BC_PROG_READ);
        bc_vec_pop_all(&f->code);
-       bc_char_vec_init(&buf);
 
        sv_file = G.prog.file;
        G.prog.file = NULL;
        G.in_read = 1;
 
+       bc_char_vec_init(&buf);
        bc_read_line(&buf);
 
        bc_parse_create(&parse, BC_PROG_READ);
@@ -7039,7 +7044,7 @@ err:
 static BC_STATUS zbc_vm_stdin(void)
 {
        BcStatus s;
-       BcVec buf, buffer;
+       BcVec buffer;
        size_t str;
        bool comment;
 
@@ -7047,8 +7052,6 @@ static BC_STATUS zbc_vm_stdin(void)
        bc_lex_file(&G.prs.l);
 
        bc_char_vec_init(&buffer);
-       bc_char_vec_init(&buf);
-       bc_vec_pushZeroByte(&buffer);
 
        // This loop is complex because the vm tries not to send any lines that end
        // with a backslash to the parser. The reason for that is because the parser
@@ -7058,16 +7061,18 @@ static BC_STATUS zbc_vm_stdin(void)
        comment = false;
        str = 0;
        for (;;) {
+               size_t prevlen = buffer.len;
                char *string;
 
-               bc_read_line(&buf);
-               if (buf.len <= 1) // "" buf means EOF
+               bc_read_line(&buffer);
+               // No more input means EOF
+               if (buffer.len <= prevlen + 1) // (we expect +1 for NUL byte)
                        break;
 
-               string = buf.v;
+               string = buffer.v + prevlen;
                while (*string) {
                        char c = *string;
-                       if (string == buf.v || string[-1] != '\\') {
+                       if (string == buffer.v || string[-1] != '\\') {
                                // checking applet type is cheaper than accessing sbgn/send
                                if (IS_BC) // bc: sbgn = send = '"'
                                        str ^= (c == '"');
@@ -7089,17 +7094,20 @@ static BC_STATUS zbc_vm_stdin(void)
                                string++;
                        }
                }
-               bc_vec_concat(&buffer, buf.v);
-               if (str || comment)
+               if (str || comment) {
+                       buffer.len--; // backstep over the trailing NUL byte
                        continue;
+               }
 
                // Check for backslash+newline.
                // we do not check that last char is '\n' -
                // if it is not, then it's EOF, and looping back
                // to bc_read_line() will detect it:
                string -= 2;
-               if (string >= buf.v && *string == '\\')
+               if (string >= buffer.v && *string == '\\') {
+                       buffer.len--;
                        continue;
+               }
 
                s = zbc_vm_process(buffer.v);
                if (s) {
@@ -7121,7 +7129,6 @@ static BC_STATUS zbc_vm_stdin(void)
                s = bc_error("comment end could not be found");
        }
 
-       bc_vec_free(&buf);
        bc_vec_free(&buffer);
        RETURN_STATUS(s);
 }