- ditch the 1 byte leak
[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 pre_alloc)
8 {
9 if (!buf) {
10 buf = emalloc(sizeof(phpstr));
11 }
12
13 buf->size = chunk_size > 0 ? chunk_size : PHPSTR_DEFAULT_SIZE;
14 buf->data = pre_alloc ? emalloc(buf->size) : NULL;
15 buf->free = pre_alloc ? buf->size : 0;
16 buf->used = 0;
17
18 return buf;
19 }
20
21 PHPSTR_API phpstr *phpstr_from_string_ex(phpstr *buf, const char *string, size_t length)
22 {
23 buf = phpstr_init(buf);
24 phpstr_append(buf, string, length);
25 return buf;
26 }
27
28 PHPSTR_API void phpstr_resize_ex(phpstr *buf, size_t len, size_t override_size)
29 {
30 if (buf->free < len) {
31 size_t size = override_size ? override_size : buf->size;
32 while ((size + buf->free) < len) {
33 size *= 2;
34 }
35 if (buf->data) {
36 buf->data = erealloc(buf->data, buf->used + buf->free + size);
37 } else {
38 buf->data = emalloc(size);
39 }
40 buf->free += size;
41 }
42 }
43
44 PHPSTR_API size_t phpstr_append(phpstr *buf, const char *append, size_t append_len)
45 {
46 phpstr_resize(buf, append_len);
47 memcpy(buf->data + buf->used, append, append_len);
48 buf->used += append_len;
49 buf->free -= append_len;
50 return append_len;
51 }
52
53 PHPSTR_API size_t phpstr_appendf(phpstr *buf, const char *format, ...)
54 {
55 va_list argv;
56 char *append;
57 size_t append_len;
58
59 va_start(argv, format);
60 append_len = vspprintf(&append, 0, format, argv);
61 va_end(argv);
62
63 phpstr_append(buf, append, append_len);
64 efree(append);
65
66 return append_len;
67 }
68
69 PHPSTR_API size_t phpstr_insert(phpstr *buf, const char *insert, size_t insert_len, size_t offset)
70 {
71 phpstr_resize(buf, insert_len);
72 memmove(buf->data + offset + insert_len, buf->data + offset, insert_len);
73 memcpy(buf->data + offset, insert, insert_len);
74 buf->used += insert_len;
75 buf->free -= insert_len;
76 return insert_len;
77 }
78
79 PHPSTR_API size_t phpstr_insertf(phpstr *buf, size_t offset, const char *format, ...)
80 {
81 va_list argv;
82 char *insert;
83 size_t insert_len;
84
85 va_start(argv, format);
86 insert_len = vspprintf(&insert, 0, format, argv);
87 va_end(argv);
88
89 phpstr_insert(buf, insert, insert_len, offset);
90 efree(insert);
91
92 return insert_len;
93 }
94
95 PHPSTR_API size_t phpstr_prepend(phpstr *buf, const char *prepend, size_t prepend_len)
96 {
97 phpstr_resize(buf, prepend_len);
98 memmove(buf->data + prepend_len, buf->data, buf->used);
99 memcpy(buf->data, prepend, prepend_len);
100 buf->used += prepend_len;
101 buf->free -= prepend_len;
102 return prepend_len;
103 }
104
105 PHPSTR_API size_t phpstr_prependf(phpstr *buf, const char *format, ...)
106 {
107 va_list argv;
108 char *prepend;
109 size_t prepend_len;
110
111 va_start(argv, format);
112 prepend_len = vspprintf(&prepend, 0, format, argv);
113 va_end(argv);
114
115 phpstr_prepend(buf, prepend, prepend_len);
116 efree(prepend);
117
118 return prepend_len;
119 }
120
121 PHPSTR_API char *phpstr_data(const phpstr *buf, char **into, size_t *len)
122 {
123 char *copy = ecalloc(1, buf->used + 1);
124 memcpy(copy, buf->data, buf->used);
125 if (into) {
126 *into = copy;
127 }
128 if (len) {
129 *len = buf->used;
130 }
131 return copy;
132 }
133
134 PHPSTR_API phpstr *phpstr_dup(const phpstr *buf)
135 {
136 phpstr *dup = phpstr_clone(buf);
137 phpstr_append(dup, buf->data, buf->used);
138 return dup;
139 }
140
141 PHPSTR_API size_t phpstr_cut(phpstr *buf, size_t offset, size_t length)
142 {
143 if (offset >= buf->used) {
144 return 0;
145 }
146 if (offset + length > buf->used) {
147 length = buf->used - offset;
148 }
149 memmove(buf->data + offset, buf->data + offset + length, buf->used - length);
150 buf->used -= length;
151 buf->free += length;
152 return length;
153 }
154
155 PHPSTR_API phpstr *phpstr_sub(const phpstr *buf, size_t offset, size_t length)
156 {
157 if (offset >= buf->used) {
158 return NULL;
159 } else {
160 size_t need = (length + offset) > buf->used ? (buf->used - offset) : (length - offset);
161 phpstr *sub = phpstr_init_ex(NULL, need, 1);
162 phpstr_append(sub, buf->data + offset, need);
163 sub->size = buf->size;
164 return sub;
165 }
166 }
167
168 PHPSTR_API phpstr *phpstr_right(const phpstr *buf, size_t length)
169 {
170 if (length < buf->used) {
171 return phpstr_sub(buf, buf->used - length, length);
172 } else {
173 return phpstr_sub(buf, 0, buf->used);
174 }
175 }
176
177
178 PHPSTR_API phpstr *phpstr_merge_va(phpstr *buf, unsigned argc, va_list argv)
179 {
180 unsigned i = 0;
181 buf = phpstr_init(buf);
182
183 while (argc > i++) {
184 phpstr_free_t f = va_arg(argv, phpstr_free_t);
185 phpstr *current = va_arg(argv, phpstr *);
186 phpstr_append(buf, current->data, current->used);
187 FREE_PHPSTR(f, current);
188 }
189
190 return buf;
191 }
192
193 PHPSTR_API phpstr *phpstr_merge_ex(phpstr *buf, unsigned argc, ...)
194 {
195 va_list argv;
196 phpstr *ret;
197
198 va_start(argv, argc);
199 ret = phpstr_merge_va(buf, argc, argv);
200 va_end(argv);
201 return ret;
202 }
203
204 PHPSTR_API phpstr *phpstr_merge(unsigned argc, ...)
205 {
206 va_list argv;
207 phpstr *ret;
208
209 va_start(argv, argc);
210 ret = phpstr_merge_va(NULL, argc, argv);
211 va_end(argv);
212 return ret;
213 }
214
215 PHPSTR_API void phpstr_fix(phpstr *buf)
216 {
217 phpstr_resize_ex(buf, 1, 1);
218 buf->data[buf->used] = '\0';
219 }
220
221 PHPSTR_API int phpstr_cmp(phpstr *left, phpstr *right)
222 {
223 if (left->used > right->used) {
224 return -1;
225 } else if (right->used > left->used) {
226 return 1;
227 } else {
228 return memcmp(left->data, right->data, left->used);
229 }
230 }
231
232 PHPSTR_API void phpstr_dtor(phpstr *buf)
233 {
234 STR_SET(buf->data, NULL);
235 buf->used = 0;
236 buf->free = 0;
237 }
238
239 PHPSTR_API void phpstr_free(phpstr **buf)
240 {
241 if (*buf) {
242 phpstr_dtor(*buf);
243 efree(*buf);
244 *buf = NULL;
245 }
246 }
247
248 PHPSTR_API size_t phpstr_chunk_buffer(phpstr **s, const char *data, size_t data_len, char **chunk, size_t chunk_size)
249 {
250 phpstr *storage;
251
252 *chunk = NULL;
253
254 if (!*s) {
255 *s = phpstr_init_ex(NULL, chunk_size * 2, chunk_size ? 1 : 0);
256 }
257 storage = *s;
258
259 if (data_len) {
260 phpstr_append(storage, data, data_len);
261 }
262
263 if (!chunk_size) {
264 phpstr_data(storage, chunk, &chunk_size);
265 phpstr_free(s);
266 return chunk_size;
267 }
268
269 if (storage->used >= storage->size/2) {
270 phpstr *avail = phpstr_left(storage, storage->size/2);
271 *chunk = estrndup(PHPSTR_VAL(avail), PHPSTR_LEN(avail));
272 phpstr_free(&avail);
273 phpstr_cut(storage, 0, storage->size/2);
274 return storage->size/2;
275 }
276
277 return 0;
278 }
279
280 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)
281 {
282 char *chunk = NULL;
283 size_t got = 0;
284
285 while (got = phpstr_chunk_buffer(s, data, data_len, &chunk, chunk_len)) {
286 passthru(chunk, got TSRMLS_CC);
287 if (!chunk_len) {
288 /* we already got the last chunk,
289 and freed all resources */
290 break;
291 }
292 data = NULL;
293 data_len = 0;
294 STR_SET(chunk, NULL);
295 }
296 STR_FREE(chunk);
297 }
298
299 /*
300 * Local variables:
301 * tab-width: 4
302 * c-basic-offset: 4
303 * End:
304 * vim600: sw=4 ts=4 fdm=marker
305 * vim<600: sw=4 ts=4
306 */
307