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