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