Line data Source code
1 : /*
2 : +--------------------------------------------------------------------+
3 : | PECL :: http |
4 : +--------------------------------------------------------------------+
5 : | Redistribution and use in source and binary forms, with or without |
6 : | modification, are permitted provided that the conditions mentioned |
7 : | in the accompanying LICENSE file are met. |
8 : +--------------------------------------------------------------------+
9 : | Copyright (c) 2004-2014, Michael Wallner <mike@php.net> |
10 : +--------------------------------------------------------------------+
11 : */
12 :
13 : #include <php.h>
14 : #include "php_http_buffer.h"
15 :
16 885 : PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_init_ex(php_http_buffer_t *buf, size_t chunk_size, int flags)
17 : {
18 885 : if (!buf) {
19 249 : buf = pemalloc(sizeof(*buf), flags & PHP_HTTP_BUFFER_INIT_PERSISTENT);
20 : }
21 :
22 885 : if (buf) {
23 885 : buf->size = (chunk_size) ? chunk_size : PHP_HTTP_BUFFER_DEFAULT_SIZE;
24 885 : buf->pmem = (flags & PHP_HTTP_BUFFER_INIT_PERSISTENT) ? 1 : 0;
25 885 : buf->data = (flags & PHP_HTTP_BUFFER_INIT_PREALLOC) ? pemalloc(buf->size, buf->pmem) : NULL;
26 885 : buf->free = (flags & PHP_HTTP_BUFFER_INIT_PREALLOC) ? buf->size : 0;
27 885 : buf->used = 0;
28 : }
29 :
30 885 : return buf;
31 : }
32 :
33 97 : PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_from_string_ex(php_http_buffer_t *buf, const char *string, size_t length)
34 : {
35 97 : if ((buf = php_http_buffer_init(buf))) {
36 97 : if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(buf, string, length)) {
37 0 : pefree(buf, buf->pmem);
38 0 : buf = NULL;
39 : }
40 : }
41 97 : return buf;
42 : }
43 :
44 738 : PHP_HTTP_BUFFER_API size_t php_http_buffer_resize_ex(php_http_buffer_t *buf, size_t len, size_t override_size, int allow_error)
45 : {
46 738 : char *ptr = NULL;
47 : #if 0
48 : fprintf(stderr, "RESIZE: len=%lu, size=%lu, used=%lu, free=%lu\n", len, buf->size, buf->used, buf->free);
49 : #endif
50 738 : if (buf->free < len) {
51 683 : size_t size = override_size ? override_size : buf->size;
52 :
53 1559 : while ((size + buf->free) < len) {
54 193 : size <<= 1;
55 : }
56 :
57 683 : if (allow_error) {
58 9 : ptr = perealloc_recoverable(buf->data, buf->used + buf->free + size, buf->pmem);
59 : } else {
60 674 : ptr = perealloc(buf->data, buf->used + buf->free + size, buf->pmem);
61 : }
62 :
63 683 : if (ptr) {
64 683 : buf->data = ptr;
65 : } else {
66 0 : return PHP_HTTP_BUFFER_NOMEM;
67 : }
68 :
69 683 : buf->free += size;
70 683 : return size;
71 : }
72 55 : return 0;
73 : }
74 :
75 185 : PHP_HTTP_BUFFER_API char *php_http_buffer_account(php_http_buffer_t *buf, size_t to_account)
76 : {
77 : /* it's probably already too late but check anyway */
78 185 : if (to_account > buf->free) {
79 0 : return NULL;
80 : }
81 :
82 185 : buf->free -= to_account;
83 185 : buf->used += to_account;
84 :
85 185 : return buf->data + buf->used;
86 : }
87 :
88 118 : PHP_HTTP_BUFFER_API size_t php_http_buffer_shrink(php_http_buffer_t *buf)
89 : {
90 : /* avoid another realloc on fixation */
91 118 : if (buf->free > 1) {
92 101 : char *ptr = perealloc(buf->data, buf->used + 1, buf->pmem);
93 :
94 101 : if (ptr) {
95 101 : buf->data = ptr;
96 : } else {
97 0 : return PHP_HTTP_BUFFER_NOMEM;
98 : }
99 101 : buf->free = 1;
100 : }
101 118 : return buf->used;
102 : }
103 :
104 2770 : PHP_HTTP_BUFFER_API size_t php_http_buffer_append(php_http_buffer_t *buf, const char *append, size_t append_len)
105 : {
106 2770 : if (buf->free < append_len && PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize(buf, append_len)) {
107 0 : return PHP_HTTP_BUFFER_NOMEM;
108 : }
109 2770 : memcpy(buf->data + buf->used, append, append_len);
110 2770 : buf->used += append_len;
111 2770 : buf->free -= append_len;
112 2770 : return append_len;
113 : }
114 :
115 437 : PHP_HTTP_BUFFER_API size_t php_http_buffer_appendf(php_http_buffer_t *buf, const char *format, ...)
116 : {
117 : va_list argv;
118 : char *append;
119 : size_t append_len, alloc;
120 :
121 437 : va_start(argv, format);
122 437 : append_len = vspprintf(&append, 0, format, argv);
123 437 : va_end(argv);
124 :
125 437 : alloc = php_http_buffer_append(buf, append, append_len);
126 437 : efree(append);
127 :
128 437 : if (PHP_HTTP_BUFFER_NOMEM == alloc) {
129 0 : return PHP_HTTP_BUFFER_NOMEM;
130 : }
131 437 : return append_len;
132 : }
133 :
134 111 : PHP_HTTP_BUFFER_API char *php_http_buffer_data(const php_http_buffer_t *buf, char **into, size_t *len)
135 : {
136 111 : char *copy = ecalloc(1, buf->used + 1);
137 111 : memcpy(copy, buf->data, buf->used);
138 111 : if (into) {
139 111 : *into = copy;
140 : }
141 111 : if (len) {
142 111 : *len = buf->used;
143 : }
144 111 : return copy;
145 : }
146 :
147 1945 : PHP_HTTP_BUFFER_API size_t php_http_buffer_cut(php_http_buffer_t *buf, size_t offset, size_t length)
148 : {
149 1945 : if (offset > buf->used) {
150 0 : return 0;
151 : }
152 1945 : if (offset + length > buf->used) {
153 16 : length = buf->used - offset;
154 : }
155 1945 : memmove(buf->data + offset, buf->data + offset + length, buf->used - length - offset);
156 1945 : buf->used -= length;
157 1945 : buf->free += length;
158 1945 : return length;
159 : }
160 :
161 1064 : PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_fix(php_http_buffer_t *buf)
162 : {
163 1064 : if (buf->free < 1 && PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize_ex(buf, 1, 1, 0)) {
164 0 : return NULL;
165 : }
166 1064 : buf->data[buf->used] = '\0';
167 1064 : return buf;
168 : }
169 :
170 331 : PHP_HTTP_BUFFER_API void php_http_buffer_reset(php_http_buffer_t *buf)
171 : {
172 331 : buf->free += buf->used;
173 331 : buf->used = 0;
174 331 : }
175 :
176 691 : PHP_HTTP_BUFFER_API void php_http_buffer_dtor(php_http_buffer_t *buf)
177 : {
178 691 : if (buf->data) {
179 489 : pefree(buf->data, buf->pmem);
180 489 : buf->data = NULL;
181 : }
182 691 : buf->used = 0;
183 691 : buf->free = 0;
184 691 : }
185 :
186 263 : PHP_HTTP_BUFFER_API void php_http_buffer_free(php_http_buffer_t **buf)
187 : {
188 263 : if (*buf) {
189 249 : php_http_buffer_dtor(*buf);
190 249 : pefree(*buf, (*buf)->pmem);
191 249 : *buf = NULL;
192 : }
193 263 : }
194 :
195 53 : PHP_HTTP_BUFFER_API size_t php_http_buffer_chunk_buffer(php_http_buffer_t **s, const char *data, size_t data_len, char **chunk, size_t chunk_size)
196 : {
197 : php_http_buffer_t *storage;
198 :
199 53 : *chunk = NULL;
200 :
201 53 : if (!*s) {
202 0 : *s = php_http_buffer_init_ex(NULL, chunk_size << 1, chunk_size ? PHP_HTTP_BUFFER_INIT_PREALLOC : 0);
203 : }
204 53 : storage = *s;
205 :
206 53 : if (data_len) {
207 36 : php_http_buffer_append(storage, data, data_len);
208 : }
209 :
210 53 : if (!chunk_size) {
211 14 : php_http_buffer_data(storage, chunk, &chunk_size);
212 14 : php_http_buffer_free(s);
213 14 : return chunk_size;
214 : }
215 :
216 39 : if (storage->used >= chunk_size) {
217 7 : *chunk = estrndup(storage->data, chunk_size);
218 7 : php_http_buffer_cut(storage, 0, chunk_size);
219 7 : return chunk_size;
220 : }
221 :
222 32 : return 0;
223 : }
224 :
225 47 : PHP_HTTP_BUFFER_API size_t php_http_buffer_chunked_output(php_http_buffer_t **s, const char *data, size_t data_len, size_t chunk_len, php_http_buffer_pass_func_t passout, void *opaque TSRMLS_DC)
226 : {
227 47 : char *chunk = NULL;
228 47 : size_t passed = 0, got = 0;
229 :
230 100 : while ((got = php_http_buffer_chunk_buffer(s, data, data_len, &chunk, chunk_len))) {
231 20 : if (PHP_HTTP_BUFFER_PASS0 == passout(opaque, chunk, got TSRMLS_CC)) {
232 2 : STR_SET(chunk, NULL);
233 2 : return PHP_HTTP_BUFFER_PASS0;
234 : }
235 18 : ++passed;
236 18 : if (!chunk_len) {
237 : /* we already got the last chunk,
238 : and freed all resources */
239 12 : break;
240 : }
241 6 : data = NULL;
242 6 : data_len = 0;
243 6 : STR_SET(chunk, NULL);
244 : }
245 45 : STR_FREE(chunk);
246 45 : return passed;
247 : }
248 :
249 1 : PHP_HTTP_BUFFER_API ssize_t php_http_buffer_passthru(php_http_buffer_t **s, size_t chunk_size, php_http_buffer_pass_func_t passin, void *passin_arg, php_http_buffer_pass_func_t passon, void *passon_arg TSRMLS_DC)
250 : {
251 1 : size_t passed_on = 0, passed_in = php_http_buffer_chunked_input(s, chunk_size, passin, passin_arg TSRMLS_CC);
252 :
253 1 : if (passed_in == PHP_HTTP_BUFFER_PASS0) {
254 0 : return passed_in;
255 : }
256 1 : if (passed_in || (*s)->used) {
257 1 : passed_on = passon(passon_arg, (*s)->data, (*s)->used TSRMLS_CC);
258 :
259 1 : if (passed_on == PHP_HTTP_BUFFER_PASS0) {
260 0 : return passed_on;
261 : }
262 :
263 1 : if (passed_on) {
264 1 : php_http_buffer_cut(*s, 0, passed_on);
265 : }
266 : }
267 :
268 1 : return passed_on - passed_in;
269 : }
270 :
271 1 : PHP_HTTP_BUFFER_API size_t php_http_buffer_chunked_input(php_http_buffer_t **s, size_t chunk_size, php_http_buffer_pass_func_t passin, void *opaque TSRMLS_DC)
272 : {
273 : php_http_buffer_t *str;
274 : size_t passed;
275 :
276 1 : if (!*s) {
277 1 : *s = php_http_buffer_init_ex(NULL, chunk_size, chunk_size ? PHP_HTTP_BUFFER_INIT_PREALLOC : 0);
278 : }
279 1 : str = *s;
280 :
281 1 : php_http_buffer_resize(str, chunk_size);
282 1 : passed = passin(opaque, str->data + str->used, chunk_size TSRMLS_CC);
283 :
284 1 : if (passed != PHP_HTTP_BUFFER_PASS0) {
285 1 : str->used += passed;
286 1 : str->free -= passed;
287 : }
288 :
289 1 : php_http_buffer_fix(str);
290 :
291 1 : return passed;
292 : }
293 :
294 : #ifdef PHP_HTTP_BUFFER_EXTENDED
295 :
296 : PHP_HTTP_BUFFER_API int php_http_buffer_cmp(php_http_buffer_t *left, php_http_buffer_t *right)
297 : {
298 : if (left->used > right->used) {
299 : return -1;
300 : } else if (right->used > left->used) {
301 : return 1;
302 : } else {
303 : return memcmp(left->data, right->data, left->used);
304 : }
305 : }
306 :
307 : PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_copy(const php_http_buffer_t *from, php_http_buffer_t *to)
308 : {
309 : int free_to = !to;
310 :
311 : to = php_http_buffer_clone(from, to);
312 :
313 : if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(to, from->data, from->used)) {
314 : if (free_to) {
315 : php_http_buffer_free(&to);
316 : } else {
317 : php_http_buffer_dtor(to);
318 : }
319 : }
320 : return to;
321 : }
322 :
323 : PHP_HTTP_BUFFER_API size_t php_http_buffer_insert(php_http_buffer_t *buf, const char *insert, size_t insert_len, size_t offset)
324 : {
325 : if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize(buf, insert_len)) {
326 : return PHP_HTTP_BUFFER_NOMEM;
327 : }
328 : memmove(buf->data + offset + insert_len, buf->data + offset, insert_len);
329 : memcpy(buf->data + offset, insert, insert_len);
330 : buf->used += insert_len;
331 : buf->free -= insert_len;
332 : return insert_len;
333 : }
334 :
335 : PHP_HTTP_BUFFER_API size_t php_http_buffer_insertf(php_http_buffer_t *buf, size_t offset, const char *format, ...)
336 : {
337 : va_list argv;
338 : char *insert;
339 : size_t insert_len, alloc;
340 :
341 : va_start(argv, format);
342 : insert_len = vspprintf(&insert, 0, format, argv);
343 : va_end(argv);
344 :
345 : alloc = php_http_buffer_insert(buf, insert, insert_len, offset);
346 : efree(insert);
347 :
348 : if (PHP_HTTP_BUFFER_NOMEM == alloc) {
349 : return PHP_HTTP_BUFFER_NOMEM;
350 : }
351 : return insert_len;
352 : }
353 :
354 : PHP_HTTP_BUFFER_API size_t php_http_buffer_prepend(php_http_buffer_t *buf, const char *prepend, size_t prepend_len)
355 : {
356 : if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize(buf, prepend_len)) {
357 : return PHP_HTTP_BUFFER_NOMEM;
358 : }
359 : memmove(buf->data + prepend_len, buf->data, buf->used);
360 : memcpy(buf->data, prepend, prepend_len);
361 : buf->used += prepend_len;
362 : buf->free -= prepend_len;
363 : return prepend_len;
364 : }
365 :
366 : PHP_HTTP_BUFFER_API size_t php_http_buffer_prependf(php_http_buffer_t *buf, const char *format, ...)
367 : {
368 : va_list argv;
369 : char *prepend;
370 : size_t prepend_len, alloc;
371 :
372 : va_start(argv, format);
373 : prepend_len = vspprintf(&prepend, 0, format, argv);
374 : va_end(argv);
375 :
376 : alloc = php_http_buffer_prepend(buf, prepend, prepend_len);
377 : efree(prepend);
378 :
379 : if (PHP_HTTP_BUFFER_NOMEM == alloc) {
380 : return PHP_HTTP_BUFFER_NOMEM;
381 : }
382 : return prepend_len;
383 : }
384 :
385 : PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_sub(const php_http_buffer_t *buf, size_t offset, size_t length)
386 : {
387 : if (offset >= buf->used) {
388 : return NULL;
389 : } else {
390 : size_t need = 1 + ((length + offset) > buf->used ? (buf->used - offset) : (length - offset));
391 : php_http_buffer_t *sub = php_http_buffer_init_ex(NULL, need, PHP_HTTP_BUFFER_INIT_PREALLOC | (buf->pmem ? PHP_HTTP_BUFFER_INIT_PERSISTENT:0));
392 : if (sub) {
393 : if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(sub, buf->data + offset, need)) {
394 : php_http_buffer_free(&sub);
395 : } else {
396 : sub->size = buf->size;
397 : }
398 : }
399 : return sub;
400 : }
401 : }
402 :
403 : PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_right(const php_http_buffer_t *buf, size_t length)
404 : {
405 : if (length < buf->used) {
406 : return php_http_buffer_sub(buf, buf->used - length, length);
407 : } else {
408 : return php_http_buffer_sub(buf, 0, buf->used);
409 : }
410 : }
411 :
412 :
413 : PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge_va(php_http_buffer_t *buf, unsigned argc, va_list argv)
414 : {
415 : unsigned i = 0;
416 : buf = php_http_buffer_init(buf);
417 :
418 : if (buf) {
419 : while (argc > i++) {
420 : php_http_buffer_free_t f = va_arg(argv, php_http_buffer_free_t);
421 : php_http_buffer_t *current = va_arg(argv, php_http_buffer_t *);
422 : php_http_buffer_append(buf, current->data, current->used);
423 : FREE_PHP_HTTP_BUFFER(f, current);
424 : }
425 : }
426 :
427 : return buf;
428 : }
429 :
430 : PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge_ex(php_http_buffer_t *buf, unsigned argc, ...)
431 : {
432 : va_list argv;
433 : php_http_buffer_t *ret;
434 :
435 : va_start(argv, argc);
436 : ret = php_http_buffer_merge_va(buf, argc, argv);
437 : va_end(argv);
438 : return ret;
439 : }
440 :
441 : PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge(unsigned argc, ...)
442 : {
443 : va_list argv;
444 : php_http_buffer_t *ret;
445 :
446 : va_start(argv, argc);
447 : ret = php_http_buffer_merge_va(NULL, argc, argv);
448 : va_end(argv);
449 : return ret;
450 : }
451 :
452 : #endif
453 :
454 : /*
455 : * Local variables:
456 : * tab-width: 4
457 : * c-basic-offset: 4
458 : * End:
459 : * vim600: sw=4 ts=4 fdm=marker
460 : * vim<600: sw=4 ts=4
461 : */
462 :
|