[git commit] bc: change bc_read_line() and zbc_vm_stdin() to avoid double buffers

Denys Vlasenko vda.linux at googlemail.com
Thu Dec 13 18:24:42 UTC 2018


commit: https://git.busybox.net/busybox/commit/?id=82ea67fbfa98cfd6b8b422db764cb8c97fc3ea7b
branch: https://git.busybox.net/busybox/commit/?id=refs/heads/master

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 at googlemail.com>
---
 miscutils/bc.c | 61 ++++++++++++++++++++++++++++++++--------------------------
 1 file changed, 34 insertions(+), 27 deletions(-)

diff --git a/miscutils/bc.c b/miscutils/bc.c
index bc2947161..7c4dfbb20 100644
--- a/miscutils/bc.c
+++ b/miscutils/bc.c
@@ -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);
 }


More information about the busybox-cvs mailing list