@cryptotaxi247 / netdata-1 / commits / 2bf7562f9

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