faster procfile parser
Costa Tsaousis (ktsaou) committed
Jan 29, 2017 at 17:16 UTC
2bf7562f92f02426a94fb541569b3853e94d33ad
2 files changed
+136
-117
src/procfile.c
+126
-116
@@ -127,13 +127,6 @@ static inline void pflines_free(pflines *fl) {
127
// ----------------------------------------------------------------------------
128
// The procfile
129
130
-#define PF_CHAR_IS_SEPARATOR ' '
131
-#define PF_CHAR_IS_NEWLINE 'N'
132
-#define PF_CHAR_IS_WORD 'W'
133
-#define PF_CHAR_IS_QUOTE 'Q'
134
-#define PF_CHAR_IS_OPEN 'O'
135
-#define PF_CHAR_IS_CLOSE 'C'
136
-
130
void procfile_close(procfile *ff) {
131
debug(D_PROCFILE, PF_PREFIX ": Closing file '%s'", procfile_filename(ff));
132
@@ -147,113 +140,119 @@ void procfile_close(procfile *ff) {
140
static inline void procfile_parser(procfile *ff) {
141
// debug(D_PROCFILE, PF_PREFIX ": Parsing file '%s'", ff->filename);
142
150
- register char *s = ff->data, *e = &ff->data[ff->len], *t = ff->data;
151
- register char *separators = ff->separators;
152
- char quote = 0;
153
- size_t l = 0, w = 0, opened = 0;
143
+ char *s = ff->data // our current position
144
+ , *e = &ff->data[ff->len] // the terminating null
145
+ , *t = ff->data; // the first character of a quoted or a parenthesized string
146
+
147
+ // the look up array to find our type of character
148
+ PF_CHAR_TYPE *separators = ff->separators;
149
+
150
+ char quote = 0; // the quote character - only when in quoted string
151
+
152
+ size_t
153
+ l = 0 // counts the number of lines we added
154
+ , w = 0 // counts the number of words we added
155
+ , opened = 0; // counts the number of open parenthesis
156
157
ff->lines = pflines_add(ff->lines, w);
158
159
while(likely(s < e)) {
160
// we are not at the end
161
+ PF_CHAR_TYPE ct = separators[(unsigned char)(*s)];
162
160
- switch(separators[(unsigned char)(*s)]) {
161
- case PF_CHAR_IS_OPEN:
162
- if(s == t) {
163
- opened++;
164
- t = ++s;
165
- }
166
- else if(opened) {
167
- opened++;
168
- s++;
169
- }
170
- else
171
- s++;
172
- break;
173
-
174
- case PF_CHAR_IS_CLOSE:
175
- if(opened) {
176
- opened--;
177
-
178
- if(!opened) {
179
- *s = '\0';
180
- ff->words = pfwords_add(ff->words, t);
181
- ff->lines->lines[l].words++;
182
- w++;
183
-
184
- t = ++s;
185
- }
186
- else
187
- s++;
188
- }
189
- else
190
- s++;
191
- break;
163
+ // this is faster than a switch()
164
+ if(likely(ct == PF_CHAR_IS_WORD)) {
165
+ s++;
166
+ }
167
+ else if(likely(ct == PF_CHAR_IS_SEPARATOR)) {
168
+ if(unlikely(quote || opened)) {
169
+ // we are inside a quote
170
+ s++;
171
+ continue;
172
+ }
173
193
- case PF_CHAR_IS_QUOTE:
194
- if(unlikely(!quote && s == t)) {
195
- // quote opened at the beginning
196
- quote = *s;
197
- t = ++s;
198
- }
199
- else if(unlikely(quote && quote == *s)) {
200
- // quote closed
201
- quote = 0;
174
+ if(unlikely(s == t)) {
175
+ // skip all leading white spaces
176
+ t = ++s;
177
+ continue;
178
+ }
179
203
- *s = '\0';
204
- ff->words = pfwords_add(ff->words, t);
205
- ff->lines->lines[l].words++;
206
- w++;
180
+ // end of word
181
+ *s = '\0';
182
208
- t = ++s;
209
- }
210
- else
211
- s++;
212
- break;
183
+ ff->words = pfwords_add(ff->words, t);
184
+ ff->lines->lines[l].words++;
185
+ w++;
186
214
- case PF_CHAR_IS_SEPARATOR:
215
- if(unlikely(quote || opened)) {
216
- // we are inside a quote
217
- s++;
218
- break;
219
- }
187
+ t = ++s;
188
+ }
189
+ else if(likely(ct == PF_CHAR_IS_NEWLINE)) {
190
+ // end of line
191
+ *s = '\0';
192
221
- if(unlikely(s == t)) {
222
- // skip all leading white spaces
223
- t = ++s;
224
- break;
225
- }
193
+ ff->words = pfwords_add(ff->words, t);
194
+ ff->lines->lines[l].words++;
195
+ w++;
196
227
- // end of word
228
- *s = '\0';
197
+ // debug(D_PROCFILE, PF_PREFIX ": ended line %d with %d words", l, ff->lines->lines[l].words);
198
230
- ff->words = pfwords_add(ff->words, t);
231
- ff->lines->lines[l].words++;
232
- w++;
199
+ ff->lines = pflines_add(ff->lines, w);
200
+ l++;
201
202
+ t = ++s;
203
+ }
204
+ else if(likely(ct == PF_CHAR_IS_QUOTE)) {
205
+ if(unlikely(!quote && s == t)) {
206
+ // quote opened at the beginning
207
+ quote = *s;
208
t = ++s;
235
- break;
209
+ }
210
+ else if(unlikely(quote && quote == *s)) {
211
+ // quote closed
212
+ quote = 0;
213
237
- case PF_CHAR_IS_NEWLINE:
238
- // end of line
214
*s = '\0';
240
-
215
ff->words = pfwords_add(ff->words, t);
216
ff->lines->lines[l].words++;
217
w++;
218
245
- // debug(D_PROCFILE, PF_PREFIX ": ended line %d with %d words", l, ff->lines->lines[l].words);
246
-
247
- ff->lines = pflines_add(ff->lines, w);
248
- l++;
249
-
219
t = ++s;
251
- break;
220
+ }
221
+ else
222
+ s++;
223
+ }
224
+ else if(likely(ct == PF_CHAR_IS_OPEN)) {
225
+ if(s == t) {
226
+ opened++;
227
+ t = ++s;
228
+ }
229
+ else if(opened) {
230
+ opened++;
231
+ s++;
232
+ }
233
+ else
234
+ s++;
235
+ }
236
+ else if(likely(ct == PF_CHAR_IS_CLOSE)) {
237
+ if(opened) {
238
+ opened--;
239
+
240
+ if(!opened) {
241
+ *s = '\0';
242
+ ff->words = pfwords_add(ff->words, t);
243
+ ff->lines->lines[l].words++;
244
+ w++;
245
253
- default:
246
+ t = ++s;
247
+ }
248
+ else
249
+ s++;
250
+ }
251
+ else
252
s++;
255
- break;
253
}
254
+ else
255
+ fatal("Internal Error: procfile_readall() does not handle all the cases.");
256
}
257
258
if(likely(s > t && t < e)) {
@@ -317,25 +316,33 @@ procfile *procfile_readall(procfile *ff) {
316
return ff;
317
}
318
320
-static void procfile_set_separators(procfile *ff, const char *separators) {
321
- static char def[256] = { [0 ... 255] = 0 };
319
+static inline void procfile_set_separators(procfile *ff, const char *separators) {
320
+ static PF_CHAR_TYPE def[256];
321
+ static char initilized = 0;
322
323
- if(unlikely(!def[255])) {
323
+ if(unlikely(!initilized)) {
324
// this is thread safe
325
- // we check that the last byte is non-zero
326
- // if it is zero, multiple threads may be executing this at the same time
327
- // setting in def[] the exact same values
328
- int i;
329
- for(i = 0; likely(i < 256) ;i++) {
330
- if(unlikely(i == '\n' || i == '\r')) def[i] = PF_CHAR_IS_NEWLINE;
331
- else if(unlikely(isspace(i) || !isprint(i))) def[i] = PF_CHAR_IS_SEPARATOR;
332
- else def[i] = PF_CHAR_IS_WORD;
325
+ // if initialized is zero, multiple threads may be executing
326
+ // this code at the same time, setting in def[] the exact same values
327
+ int i = 256;
328
+ while(i--) {
329
+ if(unlikely(i == '\n' || i == '\r'))
330
+ def[i] = PF_CHAR_IS_NEWLINE;
331
+
332
+ else if(unlikely(isspace(i) || !isprint(i)))
333
+ def[i] = PF_CHAR_IS_SEPARATOR;
334
+
335
+ else
336
+ def[i] = PF_CHAR_IS_WORD;
337
}
338
+
339
+ initilized = 1;
340
}
341
342
// copy the default
337
- char *ffs = ff->separators, *ffd = def, *ffe = &def[256];
338
- while(likely(ffd != ffe)) *ffs++ = *ffd++;
343
+ PF_CHAR_TYPE *ffs = ff->separators, *ffd = def, *ffe = &def[256];
344
+ while(ffd != ffe)
345
+ *ffs++ = *ffd++;
346
347
// set the separators
348
if(unlikely(!separators))
@@ -343,47 +350,50 @@ static void procfile_set_separators(procfile *ff, const char *separators) {
350
351
ffs = ff->separators;
352
const char *s = separators;
346
- while(likely(*s))
353
+ while(*s)
354
ffs[(int)*s++] = PF_CHAR_IS_SEPARATOR;
355
}
356
357
void procfile_set_quotes(procfile *ff, const char *quotes) {
358
+ PF_CHAR_TYPE *ffs = ff->separators;
359
+
360
// remove all quotes
352
- int i;
353
- for(i = 0; i < 256 ; i++)
354
- if(unlikely(ff->separators[i] == PF_CHAR_IS_QUOTE))
355
- ff->separators[i] = PF_CHAR_IS_WORD;
361
+ int i = 256;
362
+ while(i--)
363
+ if(unlikely(ffs[i] == PF_CHAR_IS_QUOTE))
364
+ ffs[i] = PF_CHAR_IS_WORD;
365
366
// if nothing given, return
367
if(unlikely(!quotes || !*quotes))
368
return;
369
370
// set the quotes
362
- char *ffs = ff->separators;
371
const char *s = quotes;
364
- while(likely(*s))
372
+ while(*s)
373
ffs[(int)*s++] = PF_CHAR_IS_QUOTE;
374
}
375
376
void procfile_set_open_close(procfile *ff, const char *open, const char *close) {
377
+ PF_CHAR_TYPE *ffs = ff->separators;
378
+
379
// remove all open/close
370
- int i;
371
- for(i = 0; i < 256 ; i++)
372
- if(unlikely(ff->separators[i] == PF_CHAR_IS_OPEN || ff->separators[i] == PF_CHAR_IS_CLOSE))
373
- ff->separators[i] = PF_CHAR_IS_WORD;
380
+ int i = 256;
381
+ while(i--)
382
+ if(unlikely(ffs[i] == PF_CHAR_IS_OPEN || ffs[i] == PF_CHAR_IS_CLOSE))
383
+ ffs[i] = PF_CHAR_IS_WORD;
384
385
// if nothing given, return
386
if(unlikely(!open || !*open || !close || !*close))
387
return;
388
389
// set the openings
380
- char *ffs = ff->separators;
390
const char *s = open;
382
- while(likely(*s))
391
+ while(*s)
392
ffs[(int)*s++] = PF_CHAR_IS_OPEN;
393
394
+ // set the closings
395
s = close;
386
- while(likely(*s))
396
+ while(*s)
397
ffs[(int)*s++] = PF_CHAR_IS_CLOSE;
398
}
399
src/procfile.h
+10
-1
@@ -58,6 +58,15 @@ typedef struct {
58
#define PROCFILE_FLAG_DEFAULT 0x00000000
59
#define PROCFILE_FLAG_NO_ERROR_ON_FILE_IO 0x00000001
60
61
+typedef enum procfile_separator {
62
+ PF_CHAR_IS_SEPARATOR,
63
+ PF_CHAR_IS_NEWLINE,
64
+ PF_CHAR_IS_WORD,
65
+ PF_CHAR_IS_QUOTE,
66
+ PF_CHAR_IS_OPEN,
67
+ PF_CHAR_IS_CLOSE
68
+} PF_CHAR_TYPE;
69
+
70
typedef struct {
71
char filename[FILENAME_MAX + 1]; // not populated until profile_filename() is called
72
@@ -67,7 +76,7 @@ typedef struct {
76
size_t size; // the bytes we have allocated for data
77
pflines *lines;
78
pfwords *words;
70
- char separators[256];
79
+ PF_CHAR_TYPE separators[256];
80
char data[]; // allocated buffer to keep file contents
81
} procfile;
82