]> arthur.barton.de Git - netdata.git/blob - src/procfile.c
tomcat plugin: some code optimization
[netdata.git] / src / procfile.c
1 #include "common.h"
2 #include "procfile.h"
3
4 #define PF_PREFIX "PROCFILE"
5
6 #define PFWORDS_INCREASE_STEP 200
7 #define PFLINES_INCREASE_STEP 10
8 #define PROCFILE_INCREMENT_BUFFER 512
9
10 int procfile_adaptive_initial_allocation = 0;
11
12 // if adaptive allocation is set, these store the
13 // max values we have seen so far
14 size_t procfile_max_lines = PFLINES_INCREASE_STEP;
15 size_t procfile_max_words = PFWORDS_INCREASE_STEP;
16 size_t procfile_max_allocation = PROCFILE_INCREMENT_BUFFER;
17
18 // ----------------------------------------------------------------------------
19 // An array of words
20
21 static inline pfwords *pfwords_add(pfwords *fw, char *str) NEVERNULL;
22 static inline pfwords *pfwords_add(pfwords *fw, char *str) {
23     // debug(D_PROCFILE, PF_PREFIX ":   adding word No %d: '%s'", fw->len, str);
24
25     if(unlikely(fw->len == fw->size)) {
26         // debug(D_PROCFILE, PF_PREFIX ":   expanding words");
27
28         fw = reallocz(fw, sizeof(pfwords) + (fw->size + PFWORDS_INCREASE_STEP) * sizeof(char *));
29         fw->size += PFWORDS_INCREASE_STEP;
30     }
31
32     fw->words[fw->len++] = str;
33
34     return fw;
35 }
36
37 static inline pfwords *pfwords_new(void) NEVERNULL;
38 static inline pfwords *pfwords_new(void) {
39     // debug(D_PROCFILE, PF_PREFIX ":   initializing words");
40
41     size_t size = (procfile_adaptive_initial_allocation) ? procfile_max_words : PFWORDS_INCREASE_STEP;
42
43     pfwords *new = mallocz(sizeof(pfwords) + size * sizeof(char *));
44     new->len = 0;
45     new->size = size;
46     return new;
47 }
48
49 static inline void pfwords_reset(pfwords *fw) {
50     // debug(D_PROCFILE, PF_PREFIX ":   reseting words");
51     fw->len = 0;
52 }
53
54 static inline void pfwords_free(pfwords *fw) {
55     // debug(D_PROCFILE, PF_PREFIX ":   freeing words");
56
57     freez(fw);
58 }
59
60
61 // ----------------------------------------------------------------------------
62 // An array of lines
63
64 static inline pflines *pflines_add(pflines *fl, size_t first_word) NEVERNULL;
65 static inline pflines *pflines_add(pflines *fl, size_t first_word) {
66     // debug(D_PROCFILE, PF_PREFIX ":   adding line %d at word %d", fl->len, first_word);
67
68     if(unlikely(fl->len == fl->size)) {
69         // debug(D_PROCFILE, PF_PREFIX ":   expanding lines");
70
71         fl = reallocz(fl, sizeof(pflines) + (fl->size + PFLINES_INCREASE_STEP) * sizeof(ffline));
72         fl->size += PFLINES_INCREASE_STEP;
73     }
74
75     fl->lines[fl->len].words = 0;
76     fl->lines[fl->len++].first = first_word;
77
78     return fl;
79 }
80
81 static inline pflines *pflines_new(void) NEVERNULL;
82 static inline pflines *pflines_new(void) {
83     // debug(D_PROCFILE, PF_PREFIX ":   initializing lines");
84
85     size_t size = (unlikely(procfile_adaptive_initial_allocation)) ? procfile_max_words : PFLINES_INCREASE_STEP;
86
87     pflines *new = mallocz(sizeof(pflines) + size * sizeof(ffline));
88     new->len = 0;
89     new->size = size;
90     return new;
91 }
92
93 static inline void pflines_reset(pflines *fl) {
94     // debug(D_PROCFILE, PF_PREFIX ":   reseting lines");
95
96     fl->len = 0;
97 }
98
99 static inline void pflines_free(pflines *fl) {
100     // debug(D_PROCFILE, PF_PREFIX ":   freeing lines");
101
102     freez(fl);
103 }
104
105
106 // ----------------------------------------------------------------------------
107 // The procfile
108
109 #define PF_CHAR_IS_SEPARATOR    ' '
110 #define PF_CHAR_IS_NEWLINE      'N'
111 #define PF_CHAR_IS_WORD         'W'
112 #define PF_CHAR_IS_QUOTE        'Q'
113 #define PF_CHAR_IS_OPEN         'O'
114 #define PF_CHAR_IS_CLOSE        'C'
115
116 void procfile_close(procfile *ff) {
117     debug(D_PROCFILE, PF_PREFIX ": Closing file '%s'", ff->filename);
118
119     if(likely(ff->lines)) pflines_free(ff->lines);
120     if(likely(ff->words)) pfwords_free(ff->words);
121
122     if(likely(ff->fd != -1)) close(ff->fd);
123     freez(ff);
124 }
125
126 static inline void procfile_parser(procfile *ff) {
127     // debug(D_PROCFILE, PF_PREFIX ": Parsing file '%s'", ff->filename);
128
129     register char *s = ff->data, *e = &ff->data[ff->len], *t = ff->data;
130     register char *separators = ff->separators;
131     char quote = 0;
132     size_t l = 0, w = 0, opened = 0;
133
134     ff->lines = pflines_add(ff->lines, w);
135
136     while(likely(s < e)) {
137         // we are not at the end
138
139         switch(separators[(unsigned char)(*s)]) {
140             case PF_CHAR_IS_OPEN:
141                 if(s == t) {
142                     opened++;
143                     t = ++s;
144                 }
145                 else if(opened) {
146                     opened++;
147                     s++;
148                 }
149                 else
150                     s++;
151                 break;
152
153             case PF_CHAR_IS_CLOSE:
154                 if(opened) {
155                     opened--;
156
157                     if(!opened) {
158                         *s = '\0';
159                         ff->words = pfwords_add(ff->words, t);
160                         ff->lines->lines[l].words++;
161                         w++;
162
163                         t = ++s;
164                     }
165                     else
166                         s++;
167                 }
168                 else
169                     s++;
170                 break;
171
172             case PF_CHAR_IS_QUOTE:
173                 if(unlikely(!quote && s == t)) {
174                     // quote opened at the beginning
175                     quote = *s;
176                     t = ++s;
177                 }
178                 else if(unlikely(quote && quote == *s)) {
179                     // quote closed
180                     quote = 0;
181
182                     *s = '\0';
183                     ff->words = pfwords_add(ff->words, t);
184                     ff->lines->lines[l].words++;
185                     w++;
186
187                     t = ++s;
188                 }
189                 else
190                     s++;
191                 break;
192
193             case PF_CHAR_IS_SEPARATOR:
194                 if(unlikely(quote || opened)) {
195                     // we are inside a quote
196                     s++;
197                     break;
198                 }
199
200                 if(unlikely(s == t)) {
201                     // skip all leading white spaces
202                     t = ++s;
203                     break;
204                 }
205
206                 // end of word
207                 *s = '\0';
208
209                 ff->words = pfwords_add(ff->words, t);
210                 ff->lines->lines[l].words++;
211                 w++;
212
213                 t = ++s;
214                 break;
215
216             case PF_CHAR_IS_NEWLINE:
217                 // end of line
218                 *s = '\0';
219
220                 ff->words = pfwords_add(ff->words, t);
221                 ff->lines->lines[l].words++;
222                 w++;
223
224                 // debug(D_PROCFILE, PF_PREFIX ":   ended line %d with %d words", l, ff->lines->lines[l].words);
225
226                 ff->lines = pflines_add(ff->lines, w);
227                 l++;
228
229                 t = ++s;
230                 break;
231
232             default:
233                 s++;
234                 break;
235         }
236     }
237
238     if(likely(s > t && t < e)) {
239         // the last word
240         if(likely(ff->len < ff->size))
241             *s = '\0';
242         else {
243             // we are going to loose the last byte
244             ff->data[ff->size - 1] = '\0';
245         }
246
247         ff->words = pfwords_add(ff->words, t);
248         ff->lines->lines[l].words++;
249     }
250 }
251
252 procfile *procfile_readall(procfile *ff) {
253     debug(D_PROCFILE, PF_PREFIX ": Reading file '%s'.", ff->filename);
254
255     ssize_t r = 1;
256     ff->len = 0;
257
258     while(likely(r > 0)) {
259         ssize_t s = ff->len;
260         ssize_t x = ff->size - s;
261
262         if(unlikely(!x)) {
263             debug(D_PROCFILE, PF_PREFIX ": Expanding data buffer for file '%s'.", ff->filename);
264
265             ff = reallocz(ff, sizeof(procfile) + ff->size + PROCFILE_INCREMENT_BUFFER);
266             ff->size += PROCFILE_INCREMENT_BUFFER;
267         }
268
269         debug(D_PROCFILE, "Reading file '%s', from position %ld with length %lu", ff->filename, s, ff->size - s);
270         r = read(ff->fd, &ff->data[s], ff->size - s);
271         if(unlikely(r == -1)) {
272             if(unlikely(!(ff->flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO))) error(PF_PREFIX ": Cannot read from file '%s'", ff->filename);
273             procfile_close(ff);
274             return NULL;
275         }
276
277         ff->len += r;
278     }
279
280     debug(D_PROCFILE, "Rewinding file '%s'", ff->filename);
281     if(unlikely(lseek(ff->fd, 0, SEEK_SET) == -1)) {
282         if(unlikely(!(ff->flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO))) error(PF_PREFIX ": Cannot rewind on file '%s'.", ff->filename);
283         procfile_close(ff);
284         return NULL;
285     }
286
287     pflines_reset(ff->lines);
288     pfwords_reset(ff->words);
289     procfile_parser(ff);
290
291     if(unlikely(procfile_adaptive_initial_allocation)) {
292         if(unlikely(ff->len > procfile_max_allocation)) procfile_max_allocation = ff->len;
293         if(unlikely(ff->lines->len > procfile_max_lines)) procfile_max_lines = ff->lines->len;
294         if(unlikely(ff->words->len > procfile_max_words)) procfile_max_words = ff->words->len;
295     }
296
297     debug(D_PROCFILE, "File '%s' updated.", ff->filename);
298     return ff;
299 }
300
301 static void procfile_set_separators(procfile *ff, const char *separators) {
302     static char def[256] = { [0 ... 255] = 0 };
303
304     if(unlikely(!def[255])) {
305         // this is thread safe
306         // we check that the last byte is non-zero
307         // if it is zero, multiple threads may be executing this at the same time
308         // setting in def[] the exact same values
309         int i;
310         for(i = 0; likely(i < 256) ;i++) {
311             if(unlikely(i == '\n' || i == '\r')) def[i] = PF_CHAR_IS_NEWLINE;
312             else if(unlikely(isspace(i) || !isprint(i))) def[i] = PF_CHAR_IS_SEPARATOR;
313             else def[i] = PF_CHAR_IS_WORD;
314         }
315     }
316
317     // copy the default
318     char *ffs = ff->separators, *ffd = def, *ffe = &def[256];
319     while(likely(ffd != ffe)) *ffs++ = *ffd++;
320
321     // set the separators
322     if(unlikely(!separators))
323         separators = " \t=|";
324
325     ffs = ff->separators;
326     const char *s = separators;
327     while(likely(*s))
328         ffs[(int)*s++] = PF_CHAR_IS_SEPARATOR;
329 }
330
331 void procfile_set_quotes(procfile *ff, const char *quotes) {
332     // remove all quotes
333     int i;
334     for(i = 0; i < 256 ; i++)
335         if(unlikely(ff->separators[i] == PF_CHAR_IS_QUOTE))
336             ff->separators[i] = PF_CHAR_IS_WORD;
337
338     // if nothing given, return
339     if(unlikely(!quotes || !*quotes))
340         return;
341
342     // set the quotes
343     char *ffs = ff->separators;
344     const char *s = quotes;
345     while(likely(*s))
346         ffs[(int)*s++] = PF_CHAR_IS_QUOTE;
347 }
348
349 void procfile_set_open_close(procfile *ff, const char *open, const char *close) {
350     // remove all open/close
351     int i;
352     for(i = 0; i < 256 ; i++)
353         if(unlikely(ff->separators[i] == PF_CHAR_IS_OPEN || ff->separators[i] == PF_CHAR_IS_CLOSE))
354             ff->separators[i] = PF_CHAR_IS_WORD;
355
356     // if nothing given, return
357     if(unlikely(!open || !*open || !close || !*close))
358         return;
359
360     // set the openings
361     char *ffs = ff->separators;
362     const char *s = open;
363     while(likely(*s))
364         ffs[(int)*s++] = PF_CHAR_IS_OPEN;
365
366     s = close;
367     while(likely(*s))
368         ffs[(int)*s++] = PF_CHAR_IS_CLOSE;
369 }
370
371 procfile *procfile_open(const char *filename, const char *separators, uint32_t flags) {
372     debug(D_PROCFILE, PF_PREFIX ": Opening file '%s'", filename);
373
374     int fd = open(filename, O_RDONLY, 0666);
375     if(unlikely(fd == -1)) {
376         if(unlikely(!(flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO))) error(PF_PREFIX ": Cannot open file '%s'", filename);
377         return NULL;
378     }
379
380     size_t size = (unlikely(procfile_adaptive_initial_allocation)) ? procfile_max_allocation : PROCFILE_INCREMENT_BUFFER;
381     procfile *ff = mallocz(sizeof(procfile) + size);
382     strncpyz(ff->filename, filename, FILENAME_MAX);
383
384     ff->fd = fd;
385     ff->size = size;
386     ff->len = 0;
387     ff->flags = flags;
388
389     ff->lines = pflines_new();
390     ff->words = pfwords_new();
391
392     procfile_set_separators(ff, separators);
393
394     debug(D_PROCFILE, "File '%s' opened.", filename);
395     return ff;
396 }
397
398 procfile *procfile_reopen(procfile *ff, const char *filename, const char *separators, uint32_t flags) {
399     if(unlikely(!ff)) return procfile_open(filename, separators, flags);
400
401     if(likely(ff->fd != -1)) close(ff->fd);
402
403     ff->fd = open(filename, O_RDONLY, 0666);
404     if(unlikely(ff->fd == -1)) {
405         procfile_close(ff);
406         return NULL;
407     }
408
409     strncpyz(ff->filename, filename, FILENAME_MAX);
410
411     ff->flags = flags;
412
413     // do not do the separators again if NULL is given
414     if(likely(separators)) procfile_set_separators(ff, separators);
415
416     return ff;
417 }
418
419 // ----------------------------------------------------------------------------
420 // example parsing of procfile data
421
422 void procfile_print(procfile *ff) {
423     size_t lines = procfile_lines(ff), l;
424     char *s;
425
426     debug(D_PROCFILE, "File '%s' with %zu lines and %zu words", ff->filename, ff->lines->len, ff->words->len);
427
428     for(l = 0; likely(l < lines) ;l++) {
429         size_t words = procfile_linewords(ff, l);
430
431         debug(D_PROCFILE, " line %zu starts at word %zu and has %zu words", l, ff->lines->lines[l].first, ff->lines->lines[l].words);
432
433         size_t w;
434         for(w = 0; likely(w < words) ;w++) {
435             s = procfile_lineword(ff, l, w);
436             debug(D_PROCFILE, "     [%zu.%zu] '%s'", l, w, s);
437         }
438     }
439 }