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