]> arthur.barton.de Git - netdata.git/blob - src/web_buffer.c
converted tabs for C, JS, BASH to 4 spaces
[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     long 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     wb->len += print_calculated_number(&wb->buffer[wb->len], value);
217
218     // terminate it
219     buffer_need_bytes(wb, 1);
220     wb->buffer[wb->len] = '\0';
221
222     buffer_overflow_check(wb);
223 }
224
225 // generate a javascript date, the fastest possible way...
226 void buffer_jsdate(BUFFER *wb, int year, int month, int day, int hours, int minutes, int seconds)
227 {
228   //         10        20        30      = 35
229     // 01234567890123456789012345678901234
230     // Date(2014,04,01,03,28,20)
231
232     buffer_need_bytes(wb, 30);
233
234     char *b = &wb->buffer[wb->len], *p;
235   unsigned int *q = (unsigned int *)b;  
236
237   #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
238     *q++ = 0x65746144;  // "Date" backwards.
239   #else
240     *q++ = 0x44617465;  // "Date"
241   #endif
242   p = (char *)q;
243
244   *p++ = '(';
245   *p++ = '0' + year / 1000; year %= 1000;
246   *p++ = '0' + year / 100;  year %= 100;
247   *p++ = '0' + year / 10;
248   *p++ = '0' + year % 10;
249   *p++ = ',';
250   *p   = '0' + month / 10; if (*p != '0') p++;
251   *p++ = '0' + month % 10;
252   *p++ = ',';
253   *p   = '0' + day / 10; if (*p != '0') p++;
254   *p++ = '0' + day % 10;
255   *p++ = ',';
256   *p   = '0' + hours / 10; if (*p != '0') p++;
257   *p++ = '0' + hours % 10;
258   *p++ = ',';
259   *p   = '0' + minutes / 10; if (*p != '0') p++;
260   *p++ = '0' + minutes % 10;
261   *p++ = ',';
262   *p   = '0' + seconds / 10; if (*p != '0') p++;
263   *p++ = '0' + seconds % 10;
264
265   unsigned short *r = (unsigned short *)p;
266
267 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
268     *r++ = 0x0029;  // ")\0" backwards.  
269   #else
270     *r++ = 0x2900;  // ")\0"
271   #endif
272
273     wb->len += (size_t)((char *)r - b - 1);
274
275     // terminate it
276     wb->buffer[wb->len] = '\0';
277     buffer_overflow_check(wb);
278 }
279
280 // generate a date, the fastest possible way...
281 void buffer_date(BUFFER *wb, int year, int month, int day, int hours, int minutes, int seconds)
282 {
283     //         10        20        30      = 35
284     // 01234567890123456789012345678901234
285     // 2014-04-01 03:28:20
286
287     buffer_need_bytes(wb, 36);
288
289     char *b = &wb->buffer[wb->len];
290   char *p = b;
291
292   *p++ = '0' + year / 1000; year %= 1000;
293   *p++ = '0' + year / 100;  year %= 100;
294   *p++ = '0' + year / 10;
295   *p++ = '0' + year % 10;
296   *p++ = '-';
297   *p++ = '0' + month / 10;
298   *p++ = '0' + month % 10;
299   *p++ = '-';
300   *p++ = '0' + day / 10;
301   *p++ = '0' + day % 10;
302   *p++ = ' ';
303   *p++ = '0' + hours / 10;
304   *p++ = '0' + hours % 10;
305   *p++ = ':';
306   *p++ = '0' + minutes / 10;
307   *p++ = '0' + minutes % 10;
308   *p++ = ':';
309   *p++ = '0' + seconds / 10;
310   *p++ = '0' + seconds % 10;
311   *p = '\0';
312
313     wb->len += (size_t)(p - b);
314
315     // terminate it
316     wb->buffer[wb->len] = '\0';
317     buffer_overflow_check(wb);
318 }
319
320 BUFFER *buffer_create(size_t size)
321 {
322     BUFFER *b;
323
324     debug(D_WEB_BUFFER, "Creating new web buffer of size %zu.", size);
325
326     b = callocz(1, sizeof(BUFFER));
327     b->buffer = mallocz(size + sizeof(BUFFER_OVERFLOW_EOF) + 2);
328     b->buffer[0] = '\0';
329     b->size = size;
330     b->contenttype = CT_TEXT_PLAIN;
331     buffer_overflow_init(b);
332     buffer_overflow_check(b);
333
334     return(b);
335 }
336
337 void buffer_free(BUFFER *b)
338 {
339     buffer_overflow_check(b);
340
341     debug(D_WEB_BUFFER, "Freeing web buffer of size %zu.", b->size);
342
343     freez(b->buffer);
344     freez(b);
345 }
346
347 void buffer_increase(BUFFER *b, size_t free_size_required)
348 {
349     buffer_overflow_check(b);
350
351     size_t left = b->size - b->len;
352
353     if(left >= free_size_required) return;
354
355     size_t increase = free_size_required - left;
356     if(increase < WEB_DATA_LENGTH_INCREASE_STEP) increase = WEB_DATA_LENGTH_INCREASE_STEP;
357
358     debug(D_WEB_BUFFER, "Increasing data buffer from size %zu to %zu.", b->size, b->size + increase);
359
360     b->buffer = reallocz(b->buffer, b->size + increase + sizeof(BUFFER_OVERFLOW_EOF) + 2);
361     b->size += increase;
362
363     buffer_overflow_init(b);
364     buffer_overflow_check(b);
365 }