]> arthur.barton.de Git - netdata.git/blob - src/web_buffer.c
Merge pull request #838 from ktsaou/master
[netdata.git] / src / web_buffer.c
1 #include "common.h"
2
3 #define BUFFER_OVERFLOW_EOF "EOF"
4
5 static inline void buffer_overflow_init(BUFFER *b)
6 {
7     b->buffer[b->size] = '\0';
8     strcpy(&b->buffer[b->size + 1], BUFFER_OVERFLOW_EOF);
9 }
10
11 #ifdef NETDATA_INTERNAL_CHECKS
12 #define buffer_overflow_check(b) _buffer_overflow_check(b, __FILE__, __FUNCTION__, __LINE__)
13 #else
14 #define buffer_overflow_check(b)
15 #endif
16
17 static inline void _buffer_overflow_check(BUFFER *b, const char *file, const char *function, const unsigned long line)
18 {
19     if(b->len > b->size) {
20         error("BUFFER: length %zu is above size %zu, at line %lu, at function %s() of file '%s'.", b->len, b->size, line, function, file);
21         b->len = b->size;
22     }
23
24     if(b->buffer[b->size] != '\0' || strcmp(&b->buffer[b->size + 1], BUFFER_OVERFLOW_EOF)) {
25         error("BUFFER: detected overflow at line %lu, at function %s() of file '%s'.", line, function, file);
26         buffer_overflow_init(b);
27     }
28 }
29
30
31 void buffer_reset(BUFFER *wb)
32 {
33     buffer_flush(wb);
34
35     wb->contenttype = CT_TEXT_PLAIN;
36     wb->options = 0;
37     wb->date = 0;
38
39     buffer_overflow_check(wb);
40 }
41
42 const char *buffer_tostring(BUFFER *wb)
43 {
44     buffer_need_bytes(wb, 1);
45     wb->buffer[wb->len] = '\0';
46
47     buffer_overflow_check(wb);
48
49     return(wb->buffer);
50 }
51
52 void buffer_char_replace(BUFFER *wb, char from, char to)
53 {
54     char *s = wb->buffer, *end = &wb->buffer[wb->len];
55
56     while(s != end) {
57         if(*s == from) *s = to;
58         s++;
59     }
60
61     buffer_overflow_check(wb);
62 }
63
64 // This trick seems to give an 80% speed increase in 32bit systems
65 // print_calculated_number_llu_r() will just print the digits up to the
66 // point the remaining value fits in 32 bits, and then calls
67 // print_calculated_number_lu_r() to print the rest with 32 bit arithmetic.
68
69 inline char *print_number_lu_r(char *str, unsigned long uvalue) {
70     char *wstr = str;
71
72     // print each digit
73     do *wstr++ = (char)('0' + (uvalue % 10)); while(uvalue /= 10);
74     return wstr;
75 }
76
77 inline char *print_number_llu_r(char *str, unsigned long long uvalue) {
78     char *wstr = str;
79
80     // print each digit
81     do *wstr++ = (char)('0' + (uvalue % 10)); while((uvalue /= 10) && uvalue > (unsigned long long)0xffffffff);
82     if(uvalue) return print_number_lu_r(wstr, uvalue);
83     return wstr;
84 }
85
86 void buffer_print_llu(BUFFER *wb, unsigned long long uvalue)
87 {
88     buffer_need_bytes(wb, 50);
89
90     char *str = &wb->buffer[wb->len];
91     char *wstr = str;
92
93 #ifdef ENVIRONMENT32
94     if(uvalue > (unsigned long long)0xffffffff)
95         wstr = print_number_llu_r(wstr, uvalue);
96     else
97         wstr = print_number_lu_r(wstr, uvalue);
98 #else
99     do *wstr++ = (char)('0' + (uvalue % 10)); while(uvalue /= 10);
100 #endif
101
102     // terminate it
103     *wstr = '\0';
104
105     // reverse it
106     char *begin = str, *end = wstr - 1, aux;
107     while (end > begin) aux = *end, *end-- = *begin, *begin++ = aux;
108
109     // return the buffer length
110     wb->len += wstr - str;
111 }
112
113 void buffer_strcat(BUFFER *wb, const char *txt)
114 {
115     if(unlikely(!txt || !*txt)) return;
116
117     buffer_need_bytes(wb, 1);
118
119     char *s = &wb->buffer[wb->len], *start, *end = &wb->buffer[wb->size];
120     size_t len = wb->len;
121
122     start = s;
123     while(*txt && s != end)
124         *s++ = *txt++;
125
126     len += s - start;
127
128     wb->len = len;
129     buffer_overflow_check(wb);
130
131     if(*txt) {
132         debug(D_WEB_BUFFER, "strcat(): increasing web_buffer at position %zu, size = %zu\n", wb->len, wb->size);
133         len = strlen(txt);
134         buffer_increase(wb, len);
135         buffer_strcat(wb, txt);
136     }
137     else {
138         // terminate the string
139         // without increasing the length
140         buffer_need_bytes(wb, (size_t)1);
141         wb->buffer[wb->len] = '\0';
142     }
143 }
144
145
146 void buffer_snprintf(BUFFER *wb, size_t len, const char *fmt, ...)
147 {
148     if(unlikely(!fmt || !*fmt)) return;
149
150     buffer_need_bytes(wb, len + 1);
151
152     va_list args;
153     va_start(args, fmt);
154     wb->len += vsnprintfz(&wb->buffer[wb->len], len, fmt, args);
155     va_end(args);
156
157     buffer_overflow_check(wb);
158
159     // the buffer is \0 terminated by vsnprintfz
160 }
161
162 void buffer_vsprintf(BUFFER *wb, const char *fmt, va_list args)
163 {
164     if(unlikely(!fmt || !*fmt)) return;
165
166     buffer_need_bytes(wb, 2);
167
168     size_t len = wb->size - wb->len - 1;
169
170     wb->len += vsnprintfz(&wb->buffer[wb->len], len, fmt, args);
171
172     buffer_overflow_check(wb);
173
174     // the buffer is \0 terminated by vsnprintfz
175 }
176
177 void buffer_sprintf(BUFFER *wb, const char *fmt, ...)
178 {
179     if(unlikely(!fmt || !*fmt)) return;
180
181     buffer_need_bytes(wb, 2);
182
183     size_t len = wb->size - wb->len - 1;
184     size_t wrote;
185
186     va_list args;
187     va_start(args, fmt);
188     wrote = (size_t) vsnprintfz(&wb->buffer[wb->len], len, fmt, args);
189     va_end(args);
190
191     if(unlikely(wrote >= len)) {
192         // there is bug in vsnprintf() and it returns
193         // a number higher to len, but it does not
194         // overflow the buffer.
195         // our buffer overflow detector will log it
196         // if it does.
197         buffer_overflow_check(wb);
198
199         debug(D_WEB_BUFFER, "web_buffer_sprintf(): increasing web_buffer at position %zu, size = %zu\n", wb->len, wb->size);
200         buffer_need_bytes(wb, len + WEB_DATA_LENGTH_INCREASE_STEP);
201
202         va_start(args, fmt);
203         buffer_vsprintf(wb, fmt, args);
204         va_end(args);
205     }
206     else
207         wb->len += wrote;
208
209     // the buffer is \0 terminated by vsnprintf
210 }
211
212
213 void buffer_rrd_value(BUFFER *wb, calculated_number value)
214 {
215     buffer_need_bytes(wb, 50);
216
217     if(isnan(value) || isinf(value)) {
218         buffer_strcat(wb, "null");
219         return;
220     }
221     else
222         wb->len += print_calculated_number(&wb->buffer[wb->len], value);
223
224     // terminate it
225     buffer_need_bytes(wb, 1);
226     wb->buffer[wb->len] = '\0';
227
228     buffer_overflow_check(wb);
229 }
230
231 // generate a javascript date, the fastest possible way...
232 void buffer_jsdate(BUFFER *wb, int year, int month, int day, int hours, int minutes, int seconds)
233 {
234   //         10        20        30      = 35
235     // 01234567890123456789012345678901234
236     // Date(2014,04,01,03,28,20)
237
238     buffer_need_bytes(wb, 30);
239
240     char *b = &wb->buffer[wb->len], *p;
241   unsigned int *q = (unsigned int *)b;  
242
243   #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
244     *q++ = 0x65746144;  // "Date" backwards.
245   #else
246     *q++ = 0x44617465;  // "Date"
247   #endif
248   p = (char *)q;
249
250   *p++ = '(';
251   *p++ = '0' + year / 1000; year %= 1000;
252   *p++ = '0' + year / 100;  year %= 100;
253   *p++ = '0' + year / 10;
254   *p++ = '0' + year % 10;
255   *p++ = ',';
256   *p   = '0' + month / 10; if (*p != '0') p++;
257   *p++ = '0' + month % 10;
258   *p++ = ',';
259   *p   = '0' + day / 10; if (*p != '0') p++;
260   *p++ = '0' + day % 10;
261   *p++ = ',';
262   *p   = '0' + hours / 10; if (*p != '0') p++;
263   *p++ = '0' + hours % 10;
264   *p++ = ',';
265   *p   = '0' + minutes / 10; if (*p != '0') p++;
266   *p++ = '0' + minutes % 10;
267   *p++ = ',';
268   *p   = '0' + seconds / 10; if (*p != '0') p++;
269   *p++ = '0' + seconds % 10;
270
271   unsigned short *r = (unsigned short *)p;
272
273 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
274     *r++ = 0x0029;  // ")\0" backwards.  
275   #else
276     *r++ = 0x2900;  // ")\0"
277   #endif
278
279     wb->len += (size_t)((char *)r - b - 1);
280
281     // terminate it
282     wb->buffer[wb->len] = '\0';
283     buffer_overflow_check(wb);
284 }
285
286 // generate a date, the fastest possible way...
287 void buffer_date(BUFFER *wb, int year, int month, int day, int hours, int minutes, int seconds)
288 {
289     //         10        20        30      = 35
290     // 01234567890123456789012345678901234
291     // 2014-04-01 03:28:20
292
293     buffer_need_bytes(wb, 36);
294
295     char *b = &wb->buffer[wb->len];
296   char *p = b;
297
298   *p++ = '0' + year / 1000; year %= 1000;
299   *p++ = '0' + year / 100;  year %= 100;
300   *p++ = '0' + year / 10;
301   *p++ = '0' + year % 10;
302   *p++ = '-';
303   *p++ = '0' + month / 10;
304   *p++ = '0' + month % 10;
305   *p++ = '-';
306   *p++ = '0' + day / 10;
307   *p++ = '0' + day % 10;
308   *p++ = ' ';
309   *p++ = '0' + hours / 10;
310   *p++ = '0' + hours % 10;
311   *p++ = ':';
312   *p++ = '0' + minutes / 10;
313   *p++ = '0' + minutes % 10;
314   *p++ = ':';
315   *p++ = '0' + seconds / 10;
316   *p++ = '0' + seconds % 10;
317   *p = '\0';
318
319     wb->len += (size_t)(p - b);
320
321     // terminate it
322     wb->buffer[wb->len] = '\0';
323     buffer_overflow_check(wb);
324 }
325
326 BUFFER *buffer_create(size_t size)
327 {
328     BUFFER *b;
329
330     debug(D_WEB_BUFFER, "Creating new web buffer of size %zu.", size);
331
332     b = callocz(1, sizeof(BUFFER));
333     b->buffer = mallocz(size + sizeof(BUFFER_OVERFLOW_EOF) + 2);
334     b->buffer[0] = '\0';
335     b->size = size;
336     b->contenttype = CT_TEXT_PLAIN;
337     buffer_overflow_init(b);
338     buffer_overflow_check(b);
339
340     return(b);
341 }
342
343 void buffer_free(BUFFER *b)
344 {
345     buffer_overflow_check(b);
346
347     debug(D_WEB_BUFFER, "Freeing web buffer of size %zu.", b->size);
348
349     freez(b->buffer);
350     freez(b);
351 }
352
353 void buffer_increase(BUFFER *b, size_t free_size_required)
354 {
355     buffer_overflow_check(b);
356
357     size_t left = b->size - b->len;
358
359     if(left >= free_size_required) return;
360
361     size_t increase = free_size_required - left;
362     if(increase < WEB_DATA_LENGTH_INCREASE_STEP) increase = WEB_DATA_LENGTH_INCREASE_STEP;
363
364     debug(D_WEB_BUFFER, "Increasing data buffer from size %zu to %zu.", b->size, b->size + increase);
365
366     b->buffer = reallocz(b->buffer, b->size + increase + sizeof(BUFFER_OVERFLOW_EOF) + 2);
367     b->size += increase;
368
369     buffer_overflow_init(b);
370     buffer_overflow_check(b);
371 }