24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
26 * POSSIBILITY OF SUCH DAMAGE. |
26 * POSSIBILITY OF SUCH DAMAGE. |
27 */ |
27 */ |
28 |
28 |
29 #include "ucx/buffer.h" |
29 #include "cx/buffer.h" |
30 |
30 #include "cx/utils.h" |
31 #include <stdarg.h> |
31 |
32 #include <stdlib.h> |
32 #include <stdio.h> |
33 #include <string.h> |
33 #include <string.h> |
34 |
34 |
35 UcxBuffer *ucx_buffer_new(void *space, size_t capacity, int flags) { |
35 int cxBufferInit( |
36 UcxBuffer *buffer = (UcxBuffer*) malloc(sizeof(UcxBuffer)); |
36 CxBuffer *buffer, |
37 if (buffer) { |
37 void *space, |
38 buffer->flags = flags; |
38 size_t capacity, |
39 if (!space) { |
39 const CxAllocator *allocator, |
40 buffer->space = (char*)malloc(capacity); |
40 int flags |
41 if (!buffer->space) { |
41 ) { |
42 free(buffer); |
42 if (allocator == NULL) allocator = cxDefaultAllocator; |
43 return NULL; |
43 buffer->allocator = allocator; |
44 } |
44 buffer->flags = flags; |
45 memset(buffer->space, 0, capacity); |
45 if (!space) { |
46 buffer->flags |= UCX_BUFFER_AUTOFREE; |
46 buffer->bytes = cxMalloc(allocator, capacity); |
47 } else { |
47 if (buffer->bytes == NULL) { |
48 buffer->space = (char*)space; |
48 return 1; |
49 } |
49 } |
50 buffer->capacity = capacity; |
50 buffer->flags |= CX_BUFFER_FREE_CONTENTS; |
51 buffer->size = 0; |
51 } else { |
52 |
52 buffer->bytes = space; |
53 buffer->pos = 0; |
53 } |
54 } |
54 buffer->capacity = capacity; |
55 |
55 buffer->size = 0; |
56 return buffer; |
56 buffer->pos = 0; |
57 } |
57 |
58 |
58 buffer->flush_func = NULL; |
59 void ucx_buffer_free(UcxBuffer *buffer) { |
59 buffer->flush_target = NULL; |
60 if ((buffer->flags & UCX_BUFFER_AUTOFREE) == UCX_BUFFER_AUTOFREE) { |
60 buffer->flush_blkmax = 0; |
61 free(buffer->space); |
61 buffer->flush_blksize = 4096; |
62 } |
62 buffer->flush_threshold = SIZE_MAX; |
63 free(buffer); |
63 |
64 } |
64 return 0; |
65 |
65 } |
66 UcxBuffer* ucx_buffer_extract( |
66 |
67 UcxBuffer *src, size_t start, size_t length, int flags) { |
67 void cxBufferDestroy(CxBuffer *buffer) { |
68 if (src->size == 0 || length == 0 || |
68 if ((buffer->flags & CX_BUFFER_FREE_CONTENTS) == CX_BUFFER_FREE_CONTENTS) { |
69 ((size_t)-1) - start < length || start+length > src->capacity) |
69 cxFree(buffer->allocator, buffer->bytes); |
70 { |
70 } |
|
71 } |
|
72 |
|
73 CxBuffer *cxBufferCreate( |
|
74 void *space, |
|
75 size_t capacity, |
|
76 const CxAllocator *allocator, |
|
77 int flags |
|
78 ) { |
|
79 CxBuffer *buf = cxMalloc(allocator, sizeof(CxBuffer)); |
|
80 if (buf == NULL) return NULL; |
|
81 if (0 == cxBufferInit(buf, space, capacity, allocator, flags)) { |
|
82 return buf; |
|
83 } else { |
|
84 cxFree(allocator, buf); |
71 return NULL; |
85 return NULL; |
72 } |
86 } |
73 |
87 } |
74 UcxBuffer *dst = (UcxBuffer*) malloc(sizeof(UcxBuffer)); |
88 |
75 if (dst) { |
89 void cxBufferFree(CxBuffer *buffer) { |
76 dst->space = (char*)malloc(length); |
90 if ((buffer->flags & CX_BUFFER_FREE_CONTENTS) == CX_BUFFER_FREE_CONTENTS) { |
77 if (!dst->space) { |
91 cxFree(buffer->allocator, buffer->bytes); |
78 free(dst); |
92 } |
79 return NULL; |
93 cxFree(buffer->allocator, buffer); |
80 } |
94 } |
81 dst->capacity = length; |
95 |
82 dst->size = length; |
96 int cxBufferSeek( |
83 dst->flags = flags | UCX_BUFFER_AUTOFREE; |
97 CxBuffer *buffer, |
84 dst->pos = 0; |
98 off_t offset, |
85 memcpy(dst->space, src->space+start, length); |
99 int whence |
86 } |
100 ) { |
87 return dst; |
|
88 } |
|
89 |
|
90 int ucx_buffer_seek(UcxBuffer *buffer, off_t offset, int whence) { |
|
91 size_t npos; |
101 size_t npos; |
92 switch (whence) { |
102 switch (whence) { |
93 case SEEK_CUR: |
103 case SEEK_CUR: |
94 npos = buffer->pos; |
104 npos = buffer->pos; |
95 break; |
105 break; |
96 case SEEK_END: |
106 case SEEK_END: |
97 npos = buffer->size; |
107 npos = buffer->size; |
98 break; |
108 break; |
99 case SEEK_SET: |
109 case SEEK_SET: |
100 npos = 0; |
110 npos = 0; |
101 break; |
111 break; |
102 default: |
112 default: |
103 return -1; |
113 return -1; |
104 } |
114 } |
105 |
115 |
106 size_t opos = npos; |
116 size_t opos = npos; |
107 npos += offset; |
117 npos += offset; |
108 |
118 |
109 if ((offset > 0 && npos < opos) || (offset < 0 && npos > opos)) { |
119 if ((offset > 0 && npos < opos) || (offset < 0 && npos > opos)) { |
110 return -1; |
120 return -1; |
111 } |
121 } |
112 |
122 |
113 if (npos >= buffer->size) { |
123 if (npos >= buffer->size) { |
114 return -1; |
124 return -1; |
115 } else { |
125 } else { |
116 buffer->pos = npos; |
126 buffer->pos = npos; |
117 return 0; |
127 return 0; |
118 } |
128 } |
119 |
129 |
120 } |
130 } |
121 |
131 |
122 int ucx_buffer_eof(UcxBuffer *buffer) { |
132 void cxBufferClear(CxBuffer *buffer) { |
|
133 memset(buffer->bytes, 0, buffer->size); |
|
134 buffer->size = 0; |
|
135 buffer->pos = 0; |
|
136 } |
|
137 |
|
138 void cxBufferReset(CxBuffer *buffer) { |
|
139 buffer->size = 0; |
|
140 buffer->pos = 0; |
|
141 } |
|
142 |
|
143 int cxBufferEof(const CxBuffer *buffer) { |
123 return buffer->pos >= buffer->size; |
144 return buffer->pos >= buffer->size; |
124 } |
145 } |
125 |
146 |
126 int ucx_buffer_extend(UcxBuffer *buffer, size_t len) { |
147 int cxBufferMinimumCapacity( |
127 size_t newcap = buffer->capacity; |
148 CxBuffer *buffer, |
128 |
149 size_t newcap |
129 if (buffer->capacity + len < buffer->capacity) { |
150 ) { |
|
151 if (newcap <= buffer->capacity) { |
|
152 return 0; |
|
153 } |
|
154 |
|
155 if (cxReallocate(buffer->allocator, |
|
156 (void **) &buffer->bytes, newcap) == 0) { |
|
157 buffer->capacity = newcap; |
|
158 return 0; |
|
159 } else { |
130 return -1; |
160 return -1; |
131 } |
161 } |
132 |
162 } |
133 while (buffer->capacity + len > newcap) { |
163 |
134 newcap <<= 1; |
164 /** |
135 if (newcap < buffer->capacity) { |
165 * Helps flushing data to the flush target of a buffer. |
136 return -1; |
166 * |
137 } |
167 * @param buffer the buffer containing the config |
138 } |
168 * @param space the data to flush |
139 |
169 * @param size the element size |
140 char *newspace = (char*)realloc(buffer->space, newcap); |
170 * @param nitems the number of items |
141 if (newspace) { |
171 * @return the number of items flushed |
142 memset(newspace+buffer->size, 0, newcap-buffer->size); |
172 */ |
143 buffer->space = newspace; |
173 static size_t cx_buffer_write_flush_helper( |
144 buffer->capacity = newcap; |
174 CxBuffer *buffer, |
145 } else { |
175 const unsigned char *space, |
146 return -1; |
176 size_t size, |
147 } |
177 size_t nitems |
148 |
178 ) { |
149 return 0; |
179 size_t pos = 0; |
150 } |
180 size_t remaining = nitems; |
151 |
181 size_t max_items = buffer->flush_blksize / size; |
152 size_t ucx_buffer_write(const void *ptr, size_t size, size_t nitems, |
182 while (remaining > 0) { |
153 UcxBuffer *buffer) { |
183 size_t items = remaining > max_items ? max_items : remaining; |
|
184 size_t flushed = buffer->flush_func( |
|
185 space + pos, |
|
186 size, items, |
|
187 buffer->flush_target); |
|
188 if (flushed > 0) { |
|
189 pos += (flushed * size); |
|
190 remaining -= flushed; |
|
191 } else { |
|
192 // if no bytes can be flushed out anymore, we give up |
|
193 break; |
|
194 } |
|
195 } |
|
196 return nitems - remaining; |
|
197 } |
|
198 |
|
199 size_t cxBufferWrite( |
|
200 const void *ptr, |
|
201 size_t size, |
|
202 size_t nitems, |
|
203 CxBuffer *buffer |
|
204 ) { |
|
205 // optimize for easy case |
|
206 if (size == 1 && (buffer->capacity - buffer->pos) >= nitems) { |
|
207 memcpy(buffer->bytes + buffer->pos, ptr, nitems); |
|
208 buffer->pos += nitems; |
|
209 if (buffer->pos > buffer->size) { |
|
210 buffer->size = buffer->pos; |
|
211 } |
|
212 return nitems; |
|
213 } |
|
214 |
154 size_t len; |
215 size_t len; |
155 if(ucx_szmul(size, nitems, &len)) { |
216 size_t nitems_out = nitems; |
|
217 if (cx_szmul(size, nitems, &len)) { |
156 return 0; |
218 return 0; |
157 } |
219 } |
158 size_t required = buffer->pos + len; |
220 size_t required = buffer->pos + len; |
159 if (buffer->pos > required) { |
221 if (buffer->pos > required) { |
160 return 0; |
222 return 0; |
161 } |
223 } |
162 |
224 |
|
225 bool perform_flush = false; |
163 if (required > buffer->capacity) { |
226 if (required > buffer->capacity) { |
164 if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) { |
227 if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND && required) { |
165 if (ucx_buffer_extend(buffer, required - buffer->capacity)) { |
228 if (buffer->flush_blkmax > 0 && required > buffer->flush_threshold) { |
166 return 0; |
229 perform_flush = true; |
|
230 } else { |
|
231 if (cxBufferMinimumCapacity(buffer, required)) { |
|
232 return 0; |
|
233 } |
167 } |
234 } |
168 } else { |
235 } else { |
169 len = buffer->capacity - buffer->pos; |
236 if (buffer->flush_blkmax > 0) { |
170 if (size > 1) { |
237 perform_flush = true; |
171 len -= len%size; |
238 } else { |
|
239 // truncate data to be written, if we can neither extend nor flush |
|
240 len = buffer->capacity - buffer->pos; |
|
241 if (size > 1) { |
|
242 len -= len % size; |
|
243 } |
|
244 nitems_out = len / size; |
172 } |
245 } |
173 } |
246 } |
174 } |
247 } |
175 |
248 |
176 if (len == 0) { |
249 if (len == 0) { |
177 return len; |
250 return len; |
178 } |
251 } |
179 |
252 |
180 memcpy(buffer->space + buffer->pos, ptr, len); |
253 if (perform_flush) { |
181 buffer->pos += len; |
254 size_t flush_max; |
182 if(buffer->pos > buffer->size) { |
255 if (cx_szmul(buffer->flush_blkmax, buffer->flush_blksize, &flush_max)) { |
183 buffer->size = buffer->pos; |
256 return 0; |
184 } |
257 } |
185 |
258 size_t flush_pos = buffer->flush_func == NULL || buffer->flush_target == NULL |
186 return len / size; |
259 ? buffer->pos |
187 } |
260 : cx_buffer_write_flush_helper(buffer, buffer->bytes, 1, buffer->pos); |
188 |
261 if (flush_pos == buffer->pos) { |
189 size_t ucx_buffer_read(void *ptr, size_t size, size_t nitems, |
262 // entire buffer has been flushed, we can reset |
190 UcxBuffer *buffer) { |
263 buffer->size = buffer->pos = 0; |
|
264 |
|
265 size_t items_flush; // how many items can also be directly flushed |
|
266 size_t items_keep; // how many items have to be written to the buffer |
|
267 |
|
268 items_flush = flush_max >= required ? nitems : (flush_max - flush_pos) / size; |
|
269 if (items_flush > 0) { |
|
270 items_flush = cx_buffer_write_flush_helper(buffer, ptr, size, items_flush / size); |
|
271 // in case we could not flush everything, keep the rest |
|
272 } |
|
273 items_keep = nitems - items_flush; |
|
274 if (items_keep > 0) { |
|
275 // try again with the remaining stuff |
|
276 const unsigned char *new_ptr = ptr; |
|
277 new_ptr += items_flush * size; |
|
278 // report the directly flushed items as written plus the remaining stuff |
|
279 return items_flush + cxBufferWrite(new_ptr, size, items_keep, buffer); |
|
280 } else { |
|
281 // all items have been flushed - report them as written |
|
282 return nitems; |
|
283 } |
|
284 } else if (flush_pos == 0) { |
|
285 // nothing could be flushed at all, we immediately give up without writing any data |
|
286 return 0; |
|
287 } else { |
|
288 // we were partially successful, we shift left and try again |
|
289 cxBufferShiftLeft(buffer, flush_pos); |
|
290 return cxBufferWrite(ptr, size, nitems, buffer); |
|
291 } |
|
292 } else { |
|
293 memcpy(buffer->bytes + buffer->pos, ptr, len); |
|
294 buffer->pos += len; |
|
295 if (buffer->pos > buffer->size) { |
|
296 buffer->size = buffer->pos; |
|
297 } |
|
298 return nitems_out; |
|
299 } |
|
300 |
|
301 } |
|
302 |
|
303 int cxBufferPut( |
|
304 CxBuffer *buffer, |
|
305 int c |
|
306 ) { |
|
307 c &= 0xFF; |
|
308 unsigned char const ch = c; |
|
309 if (cxBufferWrite(&ch, 1, 1, buffer) == 1) { |
|
310 return c; |
|
311 } else { |
|
312 return EOF; |
|
313 } |
|
314 } |
|
315 |
|
316 size_t cxBufferPutString( |
|
317 CxBuffer *buffer, |
|
318 const char *str |
|
319 ) { |
|
320 return cxBufferWrite(str, 1, strlen(str), buffer); |
|
321 } |
|
322 |
|
323 size_t cxBufferRead( |
|
324 void *ptr, |
|
325 size_t size, |
|
326 size_t nitems, |
|
327 CxBuffer *buffer |
|
328 ) { |
191 size_t len; |
329 size_t len; |
192 if(ucx_szmul(size, nitems, &len)) { |
330 if (cx_szmul(size, nitems, &len)) { |
193 return 0; |
331 return 0; |
194 } |
332 } |
195 if (buffer->pos + len > buffer->size) { |
333 if (buffer->pos + len > buffer->size) { |
196 len = buffer->size - buffer->pos; |
334 len = buffer->size - buffer->pos; |
197 if (size > 1) len -= len%size; |
335 if (size > 1) len -= len % size; |
198 } |
336 } |
199 |
337 |
200 if (len <= 0) { |
338 if (len <= 0) { |
201 return len; |
339 return len; |
202 } |
340 } |
203 |
341 |
204 memcpy(ptr, buffer->space + buffer->pos, len); |
342 memcpy(ptr, buffer->bytes + buffer->pos, len); |
205 buffer->pos += len; |
343 buffer->pos += len; |
206 |
344 |
207 return len / size; |
345 return len / size; |
208 } |
346 } |
209 |
347 |
210 int ucx_buffer_putc(UcxBuffer *buffer, int c) { |
348 int cxBufferGet(CxBuffer *buffer) { |
211 if(buffer->pos >= buffer->capacity) { |
349 if (cxBufferEof(buffer)) { |
212 if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) { |
|
213 if(ucx_buffer_extend(buffer, 1)) { |
|
214 return EOF; |
|
215 } |
|
216 } else { |
|
217 return EOF; |
|
218 } |
|
219 } |
|
220 |
|
221 c &= 0xFF; |
|
222 buffer->space[buffer->pos] = (char) c; |
|
223 buffer->pos++; |
|
224 if(buffer->pos > buffer->size) { |
|
225 buffer->size = buffer->pos; |
|
226 } |
|
227 return c; |
|
228 } |
|
229 |
|
230 int ucx_buffer_getc(UcxBuffer *buffer) { |
|
231 if (ucx_buffer_eof(buffer)) { |
|
232 return EOF; |
350 return EOF; |
233 } else { |
351 } else { |
234 int c = ((unsigned char*)buffer->space)[buffer->pos]; |
352 int c = buffer->bytes[buffer->pos]; |
235 buffer->pos++; |
353 buffer->pos++; |
236 return c; |
354 return c; |
237 } |
355 } |
238 } |
356 } |
239 |
357 |
240 size_t ucx_buffer_puts(UcxBuffer *buffer, const char *str) { |
358 int cxBufferShiftLeft( |
241 return ucx_buffer_write((const void*)str, 1, strlen(str), buffer); |
359 CxBuffer *buffer, |
242 } |
360 size_t shift |
243 |
361 ) { |
244 int ucx_buffer_shift_left(UcxBuffer* buffer, size_t shift) { |
|
245 if (shift >= buffer->size) { |
362 if (shift >= buffer->size) { |
246 buffer->pos = buffer->size = 0; |
363 buffer->pos = buffer->size = 0; |
247 } else { |
364 } else { |
248 memmove(buffer->space, buffer->space + shift, buffer->size - shift); |
365 memmove(buffer->bytes, buffer->bytes + shift, buffer->size - shift); |
249 buffer->size -= shift; |
366 buffer->size -= shift; |
250 |
367 |
251 if (buffer->pos >= shift) { |
368 if (buffer->pos >= shift) { |
252 buffer->pos -= shift; |
369 buffer->pos -= shift; |
253 } else { |
370 } else { |
254 buffer->pos = 0; |
371 buffer->pos = 0; |
255 } |
372 } |
256 } |
373 } |
257 return 0; |
374 return 0; |
258 } |
375 } |
259 |
376 |
260 int ucx_buffer_shift_right(UcxBuffer* buffer, size_t shift) { |
377 int cxBufferShiftRight( |
|
378 CxBuffer *buffer, |
|
379 size_t shift |
|
380 ) { |
261 size_t req_capacity = buffer->size + shift; |
381 size_t req_capacity = buffer->size + shift; |
262 size_t movebytes; |
382 size_t movebytes; |
263 |
383 |
264 // auto extend buffer, if required and enabled |
384 // auto extend buffer, if required and enabled |
265 if (buffer->capacity < req_capacity) { |
385 if (buffer->capacity < req_capacity) { |
266 if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) { |
386 if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND) { |
267 if (ucx_buffer_extend(buffer, req_capacity - buffer->capacity)) { |
387 if (cxBufferMinimumCapacity(buffer, req_capacity)) { |
268 return 1; |
388 return 1; |
269 } |
389 } |
270 movebytes = buffer->size; |
390 movebytes = buffer->size; |
271 } else { |
391 } else { |
272 movebytes = buffer->capacity - shift; |
392 movebytes = buffer->capacity - shift; |
273 } |
393 } |
274 } else { |
394 } else { |
275 movebytes = buffer->size; |
395 movebytes = buffer->size; |
276 } |
396 } |
277 |
397 |
278 memmove(buffer->space + shift, buffer->space, movebytes); |
398 memmove(buffer->bytes + shift, buffer->bytes, movebytes); |
279 buffer->size = shift+movebytes; |
399 buffer->size = shift + movebytes; |
280 |
400 |
281 buffer->pos += shift; |
401 buffer->pos += shift; |
282 if (buffer->pos > buffer->size) { |
402 if (buffer->pos > buffer->size) { |
283 buffer->pos = buffer->size; |
403 buffer->pos = buffer->size; |
284 } |
404 } |
285 |
405 |
286 return 0; |
406 return 0; |
287 } |
407 } |
288 |
408 |
289 int ucx_buffer_shift(UcxBuffer* buffer, off_t shift) { |
409 int cxBufferShift( |
|
410 CxBuffer *buffer, |
|
411 off_t shift |
|
412 ) { |
290 if (shift < 0) { |
413 if (shift < 0) { |
291 return ucx_buffer_shift_left(buffer, (size_t) (-shift)); |
414 return cxBufferShiftLeft(buffer, (size_t) (-shift)); |
292 } else if (shift > 0) { |
415 } else if (shift > 0) { |
293 return ucx_buffer_shift_right(buffer, (size_t) shift); |
416 return cxBufferShiftRight(buffer, (size_t) shift); |
294 } else { |
417 } else { |
295 return 0; |
418 return 0; |
296 } |
419 } |
297 } |
420 } |