branch off v1 as R_1_7
[m6w6/ext-http] / phpstr / phpstr.c
1
2 /* $Id$ */
3
4 #include "php.h"
5 #include "phpstr.h"
6
7 PHPSTR_API phpstr *phpstr_init_ex(phpstr *buf, size_t chunk_size, int flags)
8 {
9 if (!buf) {
10 buf = pemalloc(sizeof(phpstr), flags & PHPSTR_INIT_PERSISTENT);
11 }
12
13 if (buf) {
14 buf->size = (chunk_size) ? chunk_size : PHPSTR_DEFAULT_SIZE;
15 buf->pmem = (flags & PHPSTR_INIT_PERSISTENT) ? 1 : 0;
16 buf->data = (flags & PHPSTR_INIT_PREALLOC) ? pemalloc(buf->size, buf->pmem) : NULL;
17 buf->free = (flags & PHPSTR_INIT_PREALLOC) ? buf->size : 0;
18 buf->used = 0;
19 }
20
21 return buf;
22 }
23
24 PHPSTR_API phpstr *phpstr_from_string_ex(phpstr *buf, const char *string, size_t length)
25 {
26 if ((buf = phpstr_init(buf))) {
27 if (PHPSTR_NOMEM == phpstr_append(buf, string, length)) {
28 pefree(buf, buf->pmem);
29 buf = NULL;
30 }
31 }
32 return buf;
33 }
34
35 PHPSTR_API size_t phpstr_resize_ex(phpstr *buf, size_t len, size_t override_size, int allow_error)
36 {
37 char *ptr = NULL;
38 #if 0
39 fprintf(stderr, "RESIZE: size=%lu, used=%lu, free=%lu\n", buf->size, buf->used, buf->free);
40 #endif
41 if (buf->free < len) {
42 size_t size = override_size ? override_size : buf->size;
43
44 while ((size + buf->free) < len) {
45 size <<= 1;
46 }
47
48 if (allow_error) {
49 ptr = perealloc_recoverable(buf->data, buf->used + buf->free + size, buf->pmem);
50 } else {
51 ptr = perealloc(buf->data, buf->used + buf->free + size, buf->pmem);
52 }
53
54 if (ptr) {
55 buf->data = ptr;
56 } else {
57 return PHPSTR_NOMEM;
58 }
59
60 buf->free += size;
61 return size;
62 }
63 return 0;
64 }
65
66 PHPSTR_API size_t phpstr_shrink(phpstr *buf)
67 {
68 /* avoid another realloc on fixation */
69 if (buf->free > 1) {
70 char *ptr = perealloc(buf->data, buf->used + 1, buf->pmem);
71
72 if (ptr) {
73 buf->data = ptr;
74 } else {
75 return PHPSTR_NOMEM;
76 }
77 buf->free = 1;
78 }
79 return buf->used;
80 }
81
82 PHPSTR_API size_t phpstr_append(phpstr *buf, const char *append, size_t append_len)
83 {
84 if (PHPSTR_NOMEM == phpstr_resize(buf, append_len)) {
85 return PHPSTR_NOMEM;
86 }
87 memcpy(buf->data + buf->used, append, append_len);
88 buf->used += append_len;
89 buf->free -= append_len;
90 return append_len;
91 }
92
93 PHPSTR_API size_t phpstr_appendf(phpstr *buf, const char *format, ...)
94 {
95 va_list argv;
96 char *append;
97 size_t append_len, alloc;
98
99 va_start(argv, format);
100 append_len = vspprintf(&append, 0, format, argv);
101 va_end(argv);
102
103 alloc = phpstr_append(buf, append, append_len);
104 efree(append);
105
106 if (PHPSTR_NOMEM == alloc) {
107 return PHPSTR_NOMEM;
108 }
109 return append_len;
110 }
111
112 PHPSTR_API size_t phpstr_insert(phpstr *buf, const char *insert, size_t insert_len, size_t offset)
113 {
114 if (PHPSTR_NOMEM == phpstr_resize(buf, insert_len)) {
115 return PHPSTR_NOMEM;
116 }
117 memmove(buf->data + offset + insert_len, buf->data + offset, insert_len);
118 memcpy(buf->data + offset, insert, insert_len);
119 buf->used += insert_len;
120 buf->free -= insert_len;
121 return insert_len;
122 }
123
124 PHPSTR_API size_t phpstr_insertf(phpstr *buf, size_t offset, const char *format, ...)
125 {
126 va_list argv;
127 char *insert;
128 size_t insert_len, alloc;
129
130 va_start(argv, format);
131 insert_len = vspprintf(&insert, 0, format, argv);
132 va_end(argv);
133
134 alloc = phpstr_insert(buf, insert, insert_len, offset);
135 efree(insert);
136
137 if (PHPSTR_NOMEM == alloc) {
138 return PHPSTR_NOMEM;
139 }
140 return insert_len;
141 }
142
143 PHPSTR_API size_t phpstr_prepend(phpstr *buf, const char *prepend, size_t prepend_len)
144 {
145 if (PHPSTR_NOMEM == phpstr_resize(buf, prepend_len)) {
146 return PHPSTR_NOMEM;
147 }
148 memmove(buf->data + prepend_len, buf->data, buf->used);
149 memcpy(buf->data, prepend, prepend_len);
150 buf->used += prepend_len;
151 buf->free -= prepend_len;
152 return prepend_len;
153 }
154
155 PHPSTR_API size_t phpstr_prependf(phpstr *buf, const char *format, ...)
156 {
157 va_list argv;
158 char *prepend;
159 size_t prepend_len, alloc;
160
161 va_start(argv, format);
162 prepend_len = vspprintf(&prepend, 0, format, argv);
163 va_end(argv);
164
165 alloc = phpstr_prepend(buf, prepend, prepend_len);
166 efree(prepend);
167
168 if (PHPSTR_NOMEM == alloc) {
169 return PHPSTR_NOMEM;
170 }
171 return prepend_len;
172 }
173
174 PHPSTR_API char *phpstr_data(const phpstr *buf, char **into, size_t *len)
175 {
176 char *copy = ecalloc(1, buf->used + 1);
177 memcpy(copy, buf->data, buf->used);
178 if (into) {
179 *into = copy;
180 }
181 if (len) {
182 *len = buf->used;
183 }
184 return copy;
185 }
186
187 PHPSTR_API phpstr *phpstr_dup(const phpstr *buf)
188 {
189 phpstr *dup = phpstr_clone(buf);
190 if (PHPSTR_NOMEM == phpstr_append(dup, buf->data, buf->used)) {
191 phpstr_free(&dup);
192 }
193 return dup;
194 }
195
196 PHPSTR_API size_t phpstr_cut(phpstr *buf, size_t offset, size_t length)
197 {
198 if (offset >= buf->used) {
199 return 0;
200 }
201 if (offset + length > buf->used) {
202 length = buf->used - offset;
203 }
204 memmove(buf->data + offset, buf->data + offset + length, buf->used - length);
205 buf->used -= length;
206 buf->free += length;
207 return length;
208 }
209
210 PHPSTR_API phpstr *phpstr_sub(const phpstr *buf, size_t offset, size_t length)
211 {
212 if (offset >= buf->used) {
213 return NULL;
214 } else {
215 size_t need = 1 + ((length + offset) > buf->used ? (buf->used - offset) : (length - offset));
216 phpstr *sub = phpstr_init_ex(NULL, need, PHPSTR_INIT_PREALLOC | (buf->pmem ? PHPSTR_INIT_PERSISTENT:0));
217 if (sub) {
218 if (PHPSTR_NOMEM == phpstr_append(sub, buf->data + offset, need)) {
219 phpstr_free(&sub);
220 } else {
221 sub->size = buf->size;
222 }
223 }
224 return sub;
225 }
226 }
227
228 PHPSTR_API phpstr *phpstr_right(const phpstr *buf, size_t length)
229 {
230 if (length < buf->used) {
231 return phpstr_sub(buf, buf->used - length, length);
232 } else {
233 return phpstr_sub(buf, 0, buf->used);
234 }
235 }
236
237
238 PHPSTR_API phpstr *phpstr_merge_va(phpstr *buf, unsigned argc, va_list argv)
239 {
240 unsigned i = 0;
241 buf = phpstr_init(buf);
242
243 if (buf) {
244 while (argc > i++) {
245 phpstr_free_t f = va_arg(argv, phpstr_free_t);
246 phpstr *current = va_arg(argv, phpstr *);
247 phpstr_append(buf, current->data, current->used);
248 FREE_PHPSTR(f, current);
249 }
250 }
251
252 return buf;
253 }
254
255 PHPSTR_API phpstr *phpstr_merge_ex(phpstr *buf, unsigned argc, ...)
256 {
257 va_list argv;
258 phpstr *ret;
259
260 va_start(argv, argc);
261 ret = phpstr_merge_va(buf, argc, argv);
262 va_end(argv);
263 return ret;
264 }
265
266 PHPSTR_API phpstr *phpstr_merge(unsigned argc, ...)
267 {
268 va_list argv;
269 phpstr *ret;
270
271 va_start(argv, argc);
272 ret = phpstr_merge_va(NULL, argc, argv);
273 va_end(argv);
274 return ret;
275 }
276
277 PHPSTR_API phpstr *phpstr_fix(phpstr *buf)
278 {
279 if (PHPSTR_NOMEM == phpstr_resize_ex(buf, 1, 1, 0)) {
280 return NULL;
281 }
282 buf->data[buf->used] = '\0';
283 return buf;
284 }
285
286 PHPSTR_API int phpstr_cmp(phpstr *left, phpstr *right)
287 {
288 if (left->used > right->used) {
289 return -1;
290 } else if (right->used > left->used) {
291 return 1;
292 } else {
293 return memcmp(left->data, right->data, left->used);
294 }
295 }
296
297 PHPSTR_API void phpstr_reset(phpstr *buf)
298 {
299 buf->free += buf->used;
300 buf->used = 0;
301 }
302
303 PHPSTR_API void phpstr_dtor(phpstr *buf)
304 {
305 if (buf->data) {
306 pefree(buf->data, buf->pmem);
307 buf->data = NULL;
308 }
309 buf->used = 0;
310 buf->free = 0;
311 }
312
313 PHPSTR_API void phpstr_free(phpstr **buf)
314 {
315 if (*buf) {
316 phpstr_dtor(*buf);
317 pefree(*buf, (*buf)->pmem);
318 *buf = NULL;
319 }
320 }
321
322 PHPSTR_API size_t phpstr_chunk_buffer(phpstr **s, const char *data, size_t data_len, char **chunk, size_t chunk_size)
323 {
324 phpstr *storage;
325
326 *chunk = NULL;
327
328 if (!*s) {
329 *s = phpstr_init_ex(NULL, chunk_size << 1, chunk_size ? PHPSTR_INIT_PREALLOC : 0);
330 }
331 storage = *s;
332
333 if (data_len) {
334 phpstr_append(storage, data, data_len);
335 }
336
337 if (!chunk_size) {
338 phpstr_data(storage, chunk, &chunk_size);
339 phpstr_free(s);
340 return chunk_size;
341 }
342
343 if (storage->used >= (chunk_size = storage->size >> 1)) {
344 *chunk = estrndup(storage->data, chunk_size);
345 phpstr_cut(storage, 0, chunk_size);
346 return chunk_size;
347 }
348
349 return 0;
350 }
351
352 PHPSTR_API void phpstr_chunked_output(phpstr **s, const char *data, size_t data_len, size_t chunk_len, phpstr_passthru_func passthru, void *opaque TSRMLS_DC)
353 {
354 char *chunk = NULL;
355 size_t got = 0;
356
357 while ((got = phpstr_chunk_buffer(s, data, data_len, &chunk, chunk_len))) {
358 passthru(opaque, chunk, got TSRMLS_CC);
359 if (!chunk_len) {
360 /* we already got the last chunk,
361 and freed all resources */
362 break;
363 }
364 data = NULL;
365 data_len = 0;
366 STR_SET(chunk, NULL);
367 }
368 STR_FREE(chunk);
369 }
370
371 /*
372 * Local variables:
373 * tab-width: 4
374 * c-basic-offset: 4
375 * End:
376 * vim600: sw=4 ts=4 fdm=marker
377 * vim<600: sw=4 ts=4
378 */
379