1 From: Tony Balinski <ajbj@free.fr>
2 Subject: Perform faster macro concatenation
6 http://sourceforge.net/tracker/index.php?func=detail&aid=971477&group_id=11005&atid=311005
7 [ 971477 ] Perform faster macro concatenation
8 FastConcat.diff 2004-06-11 15:55
10 Previously, concatenation was treated as a binary operator, taking two values
11 from the stack and concatenating them. Where an expression involves more than
12 one concatenation, this involved creating temporary strings, requiring
13 separate allocation, which would only be discarded later, for each application
14 of the binary concatenation. To make matters worse, concatenation would rescan
15 each string to determine its length using strlen().
17 This modification changes the way the concatenation operator is handled.
18 Instead of treating it as a binary operator, it sees it as a list operator,
19 rather like the comma in an argument list. Thus a concatenation expression
20 is treated as a counted sequence of subexpressions. The count is coded into
21 the "bytecode" to tell the concat() function (in interpret.c) how many
22 expression values to pull off the stack. The function then exploits the
23 fact that string DataValue structures now include the strings' lengths to
24 work out the total allocation length required for the whole result. (This
25 is based on the previous implementation of makeArrayKeyFromArgs().)
27 I have also added static functions longAsStr() and lenLongAsStr() to generate
28 a string representation of a number, or just to work out its length. This
29 allows us to avoid calling sprintf() with "%d" all the time. The new
30 AllocStringOfNumber() exploits this, and is used in various places in
31 interpret.c. Note that longAsStr() returns the string value in a static buffer
32 which must be used/copied immediately to avoid a second call overwriting
37 source/interpret.c | 232 ++++++++++++++++++++++++++++++++++++++---------------
38 source/interpret.h | 3
39 source/parse.y | 19 +++-
40 3 files changed, 188 insertions(+), 66 deletions(-)
42 diff --quilt old/source/interpret.c new/source/interpret.c
43 --- old/source/interpret.c
44 +++ new/source/interpret.c
45 @@ -85,6 +85,8 @@ static void restoreContext(RestartData *
47 static int returnValOrNone(int valOnStack);
49 +static int concatenateNwithSep(int nVals, const char *sep, char **result,
51 static void freeSymbolTable(Symbol *symTab);
52 static int errCheck(const char *s);
53 static int execError(const char *s1, const char *s2);
54 @@ -843,6 +845,60 @@ Symbol *PromoteToGlobal(Symbol *sym)
58 +** Convert a long value to its decimal string representation, returned in a
61 +const char *longAsStr(long val)
63 + static const char digits[] = "0123456789";
64 + static char res[TYPE_INT_STR_SIZE(val) + 1];
65 + char *pos = &res[TYPE_INT_STR_SIZE(val)];
67 + /* the string is built backwards, so start by null terminating it */
71 + /* do-while loop will deal with the val == 0 case */
73 + /* we can use the modulo (%) operator here */
74 + *--pos = digits[val % 10];
79 + /* we don't use the modulo (%) operator since its behaviour with
80 + negative numbers is undefined by the C standards */
82 + long val10 = val / 10;
83 + *--pos = digits[(10 * val10) - val];
92 +** Calculate the length of the decimal string representation of a long value.
94 +int lenLongAsStr(long val)
99 + len++; /* for leading '-' */
102 + /* do-while loop will deal with the val == 0 case */
106 + } while (val != 0);
112 ** Allocate memory for a string, and keep track of it, such that it
113 ** can be recovered later using GarbageCollectStrings. (A linked list
114 ** of pointers is maintained by threading through the memory behind
115 @@ -933,6 +989,12 @@ char *AllocStringCpy(const char *s)
116 return AllocStringNCpy(s, s ? strlen(s) : 0);
119 +/* Allocate a string holding the decimal value of the number */
120 +char *AllocStringOfNumber(long val)
122 + return AllocStringCpy(longAsStr(val));
126 * Allocate a new NString buffer, containing a copy of the given string.
127 * The length is set to the length of the string and resulting string is
128 @@ -1172,8 +1234,7 @@ static void freeSymbolTable(Symbol *symT
129 if (dataVal.tag == STRING_TAG) { \
130 __str = dataVal.val.str.rep; \
131 } else if (dataVal.tag == INT_TAG) { \
132 - __str = AllocString(TYPE_INT_STR_SIZE(int)); \
133 - sprintf(__str, "%d", dataVal.val.n); \
134 + __str = AllocStringOfNumber(dataVal.val.n); \
136 return execError("incompatible type in string context: <%s>", \
137 tagToStr(dataVal.tag)); \
138 @@ -1333,9 +1394,8 @@ static int pushArgVal(void)
140 nArgs = FP_GET_ARG_COUNT(FrameP);
141 if (argNum >= nArgs || argNum < 0) {
142 - char argStr[TYPE_INT_STR_SIZE(argNum)];
143 - sprintf(argStr, "%d", argNum + 1);
144 - return execError("referenced undefined argument: $args[%s]", argStr);
145 + return execError("referenced undefined argument: $args[%s]",
146 + longAsStr(argNum + 1));
148 PUSH(FP_GET_ARG_N(FrameP, argNum));
150 @@ -1365,11 +1425,9 @@ static int pushArgArray(void)
151 resultArray->val.arrayPtr = ArrayNew();
153 for (argNum = 0; argNum < nArgs; ++argNum) {
154 - char intStr[TYPE_INT_STR_SIZE(argNum)];
156 - sprintf(intStr, "%d", argNum + 1);
157 argVal = FP_GET_ARG_N(FrameP, argNum);
158 - if (!ArrayInsert(resultArray, AllocStringCpy(intStr), &argVal)) {
159 + if (!ArrayInsert(resultArray, AllocStringOfNumber(argNum + 1),
161 return(execError("array insertion failure", NULL));
164 @@ -1959,26 +2017,105 @@ static int power(void)
168 -** concatenate two top items on the stack
169 -** Before: TheStack-> str2, str1, next, ...
170 -** After: TheStack-> result, next, ...
171 +** A helper routine used in concat(), and makeArrayKeyFromArgs(). Concatenate a
172 +** number of values from the stack and return the result as a character pointer
173 +** in *result and its negated length as the return value, or greater than zero
174 +** on failure. (We do this, because PEEK and POP can cause positive error status
175 +** STAT_ERROR to be returned.) If a divider is specified, add it between each of
176 +** the stack elements. The stack elements are popped from the stack if
177 +** leaveParams is false.
179 +static int concatenateNwithSep(int nVals, const char *sep, char **result,
194 + sepLen = strlen(sep);
196 + /* evaluate total length (upper limit) */
197 + len = sepLen * (nVals - 1);
198 + for (i = nVals; i--;) {
200 + if (value.tag == INT_TAG) {
201 + len += lenLongAsStr(value.val.n);
203 + else if (value.tag == STRING_TAG) {
204 + len += value.val.str.len;
207 + return execError("incompatible type in string context: <%s>",
208 + tagToStr(value.tag));
212 + /* allocate the string */
213 + res = AllocString(len + 1);
215 + /* write everything into the result */
216 + for (i = nVals; i--;) {
218 + if (value.tag == INT_TAG) {
219 + pos += strlen(strcpy(pos, longAsStr(value.val.n)));
221 + else { /* value.tag == STRING_TAG */
222 + strcpy(pos, value.val.str.rep);
223 + pos += value.val.str.len;
231 + /* remove the source expression values */
232 + if (!leaveParams) {
238 + /* now return the results */
240 + return res - pos; /* first position - last position: negated length value */
244 +** concatenate a number of strings and push the result onto the stack
246 +** Before: Prog-> [nExpr], next, ...
247 +** TheStack-> exprValN, ... exprVal1, next, ...
248 +** After: Prog-> nExpr, [next], ...
249 +** TheStack-> concatResult, next, ...
251 static int concat(void)
253 - char *s1, *s2, *out;
258 - DISASM_RT(PC-1, 1);
269 - out = AllocString(len1 + len2 + 1);
270 - strncpy(out, s1, len1);
271 - strcpy(&out[len1], s2);
272 - PUSH_STRING(out, len1 + len2);
273 + DISASM_RT(PC-2, 2);
274 + STACKDUMP(nExpr, 3);
276 + /* remember: concatenateNwithSep() succeeds with a non-positive number */
277 + len = concatenateNwithSep(nExpr, "", &out, False);
281 + PUSH_STRING(out, -len);
285 @@ -2284,45 +2421,12 @@ int ArrayCopy(DataValue *dstArray, DataV
287 static int makeArrayKeyFromArgs(int nArgs, char **keyString, int leaveParams)
290 - int sepLen = strlen(ARRAY_DIM_SEP);
295 - keyLength = sepLen * (nArgs - 1);
296 - for (i = nArgs - 1; i >= 0; --i) {
298 - if (tmpVal.tag == INT_TAG) {
299 - keyLength += TYPE_INT_STR_SIZE(tmpVal.val.n);
301 - else if (tmpVal.tag == STRING_TAG) {
302 - keyLength += tmpVal.val.str.len;
305 - return(execError("can only index array with string or int.", NULL));
308 - *keyString = AllocString(keyLength + 1);
309 - (*keyString)[0] = 0;
310 - for (i = nArgs - 1; i >= 0; --i) {
311 - if (i != nArgs - 1) {
312 - strcat(*keyString, ARRAY_DIM_SEP);
315 - if (tmpVal.tag == INT_TAG) {
316 - sprintf(&((*keyString)[strlen(*keyString)]), "%d", tmpVal.val.n);
318 - else if (tmpVal.tag == STRING_TAG) {
319 - strcat(*keyString, tmpVal.val.str.rep);
322 - return(execError("can only index array with string or int.", NULL));
325 - if (!leaveParams) {
326 - for (i = nArgs - 1; i >= 0; --i) {
329 + /* remember: concatenateNwithSep() succeeds with a non-positive number */
330 + len = concatenateNwithSep(nArgs, ARRAY_DIM_SEP, keyString, leaveParams);
336 @@ -3109,6 +3213,10 @@ static void disasm(Inst *inst, int nInst
337 &inst[i+1] + inst[i+1].value);
340 + else if (j == OP_CONCAT) {
341 + printf("nExpr=%d", inst[i+1].value);
344 else if (j == OP_SUBR_CALL) {
345 printf("%s (%d arg)", inst[i+1].sym->name, inst[i+2].value);
347 diff --quilt old/source/interpret.h new/source/interpret.h
348 --- old/source/interpret.h
349 +++ new/source/interpret.h
350 @@ -172,6 +172,7 @@ void PreemptMacro(void);
351 char *AllocString(int length);
352 char *AllocStringNCpy(const char *s, int length);
353 char *AllocStringCpy(const char *s);
354 +char *AllocStringOfNumber(long val);
355 int AllocNString(NString *string, int length);
356 int AllocNStringNCpy(NString *string, const char *s, int length);
357 int AllocNStringCpy(NString *string, const char *s);
358 @@ -186,5 +187,7 @@ void SetMacroFocusWindow(WindowInfo *win
359 /* function used for implicit conversion from string to number */
360 int StringToNum(const char *string, int *number);
361 int StringToNumEnd(const char *string, const char *end, int *number);
362 +const char *longAsStr(long val);
363 +int lenLongAsStr(long val);
365 #endif /* NEDIT_INTERPRET_H_INCLUDED */
366 diff --quilt old/source/parse.y new/source/parse.y
367 --- old/source/parse.y
368 +++ new/source/parse.y
369 @@ -92,7 +92,7 @@ static int AllowDefine;
371 %token DELETE ARG_LOOKUP
372 %token IF WHILE ELSE FOR BREAK CONTINUE RETURN DEFINE
374 +%type <num> arglist catlist
375 %type <inst> cond comastmts for while else and or arrayexpr
377 %type <define> definesym
378 @@ -361,9 +361,20 @@ arglist: /* nothing */ {
382 -expr: numexpr %prec CONCAT
383 - | expr numexpr %prec CONCAT {
386 +/* string concatenation lists */
387 +catlist: numexpr %prec CONCAT {
390 + | catlist numexpr %prec CONCAT {
397 + ADD_OP(OP_CONCAT); ADD_IMMED($1);
401 initarraylv: SYMBOL {