ucx/buffer.c

changeset 747
efbd59642577
parent 505
481802342fdf
child 816
839fefbdedc7
equal deleted inserted replaced
746:a569148841ff 747:efbd59642577
1 /* 1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 * 3 *
4 * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. 4 * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met: 7 * modification, are permitted provided that the following conditions are met:
8 * 8 *
9 * 1. Redistributions of source code must retain the above copyright 9 * 1. Redistributions of source code must retain the above copyright
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 CxAllocator const *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 CxAllocator const *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 int cxBufferEof(CxBuffer const *buffer) {
123 return buffer->pos >= buffer->size; 139 return buffer->pos >= buffer->size;
124 } 140 }
125 141
126 int ucx_buffer_extend(UcxBuffer *buffer, size_t len) { 142 int cxBufferMinimumCapacity(
127 size_t newcap = buffer->capacity; 143 CxBuffer *buffer,
128 144 size_t newcap
129 if (buffer->capacity + len < buffer->capacity) { 145 ) {
146 if (newcap <= buffer->capacity) {
147 return 0;
148 }
149
150 if (cxReallocate(buffer->allocator,
151 (void **) &buffer->bytes, newcap) == 0) {
152 buffer->capacity = newcap;
153 return 0;
154 } else {
130 return -1; 155 return -1;
131 } 156 }
132 157 }
133 while (buffer->capacity + len > newcap) { 158
134 newcap <<= 1; 159 /**
135 if (newcap < buffer->capacity) { 160 * Helps flushing data to the flush target of a buffer.
136 return -1; 161 *
137 } 162 * @param buffer the buffer containing the config
138 } 163 * @param space the data to flush
139 164 * @param size the element size
140 char *newspace = (char*)realloc(buffer->space, newcap); 165 * @param nitems the number of items
141 if (newspace) { 166 * @return the number of items flushed
142 memset(newspace+buffer->size, 0, newcap-buffer->size); 167 */
143 buffer->space = newspace; 168 static size_t cx_buffer_write_flush_helper(
144 buffer->capacity = newcap; 169 CxBuffer *buffer,
145 } else { 170 unsigned char const *space,
146 return -1; 171 size_t size,
147 } 172 size_t nitems
148 173 ) {
149 return 0; 174 size_t pos = 0;
150 } 175 size_t remaining = nitems;
151 176 size_t max_items = buffer->flush_blksize / size;
152 size_t ucx_buffer_write(const void *ptr, size_t size, size_t nitems, 177 while (remaining > 0) {
153 UcxBuffer *buffer) { 178 size_t items = remaining > max_items ? max_items : remaining;
179 size_t flushed = buffer->flush_func(
180 space + pos,
181 size, items,
182 buffer->flush_target);
183 if (flushed > 0) {
184 pos += (flushed * size);
185 remaining -= flushed;
186 } else {
187 // if no bytes can be flushed out anymore, we give up
188 break;
189 }
190 }
191 return nitems - remaining;
192 }
193
194 size_t cxBufferWrite(
195 void const *ptr,
196 size_t size,
197 size_t nitems,
198 CxBuffer *buffer
199 ) {
200 // optimize for easy case
201 if (size == 1 && (buffer->capacity - buffer->pos) >= nitems) {
202 memcpy(buffer->bytes + buffer->pos, ptr, nitems);
203 buffer->pos += nitems;
204 if (buffer->pos > buffer->size) {
205 buffer->size = buffer->pos;
206 }
207 return nitems;
208 }
209
154 size_t len; 210 size_t len;
155 if(ucx_szmul(size, nitems, &len)) { 211 size_t nitems_out = nitems;
212 if (cx_szmul(size, nitems, &len)) {
156 return 0; 213 return 0;
157 } 214 }
158 size_t required = buffer->pos + len; 215 size_t required = buffer->pos + len;
159 if (buffer->pos > required) { 216 if (buffer->pos > required) {
160 return 0; 217 return 0;
161 } 218 }
162 219
220 bool perform_flush = false;
163 if (required > buffer->capacity) { 221 if (required > buffer->capacity) {
164 if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) { 222 if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND && required) {
165 if (ucx_buffer_extend(buffer, required - buffer->capacity)) { 223 if (buffer->flush_blkmax > 0 && required > buffer->flush_threshold) {
166 return 0; 224 perform_flush = true;
225 } else {
226 if (cxBufferMinimumCapacity(buffer, required)) {
227 return 0;
228 }
167 } 229 }
168 } else { 230 } else {
169 len = buffer->capacity - buffer->pos; 231 if (buffer->flush_blkmax > 0) {
170 if (size > 1) { 232 perform_flush = true;
171 len -= len%size; 233 } else {
234 // truncate data to be written, if we can neither extend nor flush
235 len = buffer->capacity - buffer->pos;
236 if (size > 1) {
237 len -= len % size;
238 }
239 nitems_out = len / size;
172 } 240 }
173 } 241 }
174 } 242 }
175 243
176 if (len == 0) { 244 if (len == 0) {
177 return len; 245 return len;
178 } 246 }
179 247
180 memcpy(buffer->space + buffer->pos, ptr, len); 248 if (perform_flush) {
181 buffer->pos += len; 249 size_t flush_max;
182 if(buffer->pos > buffer->size) { 250 if (cx_szmul(buffer->flush_blkmax, buffer->flush_blksize, &flush_max)) {
183 buffer->size = buffer->pos; 251 return 0;
184 } 252 }
185 253 size_t flush_pos = buffer->flush_func == NULL || buffer->flush_target == NULL
186 return len / size; 254 ? buffer->pos
187 } 255 : cx_buffer_write_flush_helper(buffer, buffer->bytes, 1, buffer->pos);
188 256 if (flush_pos == buffer->pos) {
189 size_t ucx_buffer_read(void *ptr, size_t size, size_t nitems, 257 // entire buffer has been flushed, we can reset
190 UcxBuffer *buffer) { 258 buffer->size = buffer->pos = 0;
259
260 size_t items_flush; // how many items can also be directly flushed
261 size_t items_keep; // how many items have to be written to the buffer
262
263 items_flush = flush_max >= required ? nitems : (flush_max - flush_pos) / size;
264 if (items_flush > 0) {
265 items_flush = cx_buffer_write_flush_helper(buffer, ptr, size, items_flush / size);
266 // in case we could not flush everything, keep the rest
267 }
268 items_keep = nitems - items_flush;
269 if (items_keep > 0) {
270 // try again with the remaining stuff
271 unsigned char const *new_ptr = ptr;
272 new_ptr += items_flush * size;
273 // report the directly flushed items as written plus the remaining stuff
274 return items_flush + cxBufferWrite(new_ptr, size, items_keep, buffer);
275 } else {
276 // all items have been flushed - report them as written
277 return nitems;
278 }
279 } else if (flush_pos == 0) {
280 // nothing could be flushed at all, we immediately give up without writing any data
281 return 0;
282 } else {
283 // we were partially successful, we shift left and try again
284 cxBufferShiftLeft(buffer, flush_pos);
285 return cxBufferWrite(ptr, size, nitems, buffer);
286 }
287 } else {
288 memcpy(buffer->bytes + buffer->pos, ptr, len);
289 buffer->pos += len;
290 if (buffer->pos > buffer->size) {
291 buffer->size = buffer->pos;
292 }
293 return nitems_out;
294 }
295
296 }
297
298 int cxBufferPut(
299 CxBuffer *buffer,
300 int c
301 ) {
302 c &= 0xFF;
303 unsigned char const ch = c;
304 if (cxBufferWrite(&ch, 1, 1, buffer) == 1) {
305 return c;
306 } else {
307 return EOF;
308 }
309 }
310
311 size_t cxBufferPutString(
312 CxBuffer *buffer,
313 const char *str
314 ) {
315 return cxBufferWrite(str, 1, strlen(str), buffer);
316 }
317
318 size_t cxBufferRead(
319 void *ptr,
320 size_t size,
321 size_t nitems,
322 CxBuffer *buffer
323 ) {
191 size_t len; 324 size_t len;
192 if(ucx_szmul(size, nitems, &len)) { 325 if (cx_szmul(size, nitems, &len)) {
193 return 0; 326 return 0;
194 } 327 }
195 if (buffer->pos + len > buffer->size) { 328 if (buffer->pos + len > buffer->size) {
196 len = buffer->size - buffer->pos; 329 len = buffer->size - buffer->pos;
197 if (size > 1) len -= len%size; 330 if (size > 1) len -= len % size;
198 } 331 }
199 332
200 if (len <= 0) { 333 if (len <= 0) {
201 return len; 334 return len;
202 } 335 }
203 336
204 memcpy(ptr, buffer->space + buffer->pos, len); 337 memcpy(ptr, buffer->bytes + buffer->pos, len);
205 buffer->pos += len; 338 buffer->pos += len;
206 339
207 return len / size; 340 return len / size;
208 } 341 }
209 342
210 int ucx_buffer_putc(UcxBuffer *buffer, int c) { 343 int cxBufferGet(CxBuffer *buffer) {
211 if(buffer->pos >= buffer->capacity) { 344 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; 345 return EOF;
233 } else { 346 } else {
234 int c = ((unsigned char*)buffer->space)[buffer->pos]; 347 int c = buffer->bytes[buffer->pos];
235 buffer->pos++; 348 buffer->pos++;
236 return c; 349 return c;
237 } 350 }
238 } 351 }
239 352
240 size_t ucx_buffer_puts(UcxBuffer *buffer, const char *str) { 353 int cxBufferShiftLeft(
241 return ucx_buffer_write((const void*)str, 1, strlen(str), buffer); 354 CxBuffer *buffer,
242 } 355 size_t shift
243 356 ) {
244 int ucx_buffer_shift_left(UcxBuffer* buffer, size_t shift) {
245 if (shift >= buffer->size) { 357 if (shift >= buffer->size) {
246 buffer->pos = buffer->size = 0; 358 buffer->pos = buffer->size = 0;
247 } else { 359 } else {
248 memmove(buffer->space, buffer->space + shift, buffer->size - shift); 360 memmove(buffer->bytes, buffer->bytes + shift, buffer->size - shift);
249 buffer->size -= shift; 361 buffer->size -= shift;
250 362
251 if (buffer->pos >= shift) { 363 if (buffer->pos >= shift) {
252 buffer->pos -= shift; 364 buffer->pos -= shift;
253 } else { 365 } else {
254 buffer->pos = 0; 366 buffer->pos = 0;
255 } 367 }
256 } 368 }
257 return 0; 369 return 0;
258 } 370 }
259 371
260 int ucx_buffer_shift_right(UcxBuffer* buffer, size_t shift) { 372 int cxBufferShiftRight(
373 CxBuffer *buffer,
374 size_t shift
375 ) {
261 size_t req_capacity = buffer->size + shift; 376 size_t req_capacity = buffer->size + shift;
262 size_t movebytes; 377 size_t movebytes;
263 378
264 // auto extend buffer, if required and enabled 379 // auto extend buffer, if required and enabled
265 if (buffer->capacity < req_capacity) { 380 if (buffer->capacity < req_capacity) {
266 if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) { 381 if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND) {
267 if (ucx_buffer_extend(buffer, req_capacity - buffer->capacity)) { 382 if (cxBufferMinimumCapacity(buffer, req_capacity)) {
268 return 1; 383 return 1;
269 } 384 }
270 movebytes = buffer->size; 385 movebytes = buffer->size;
271 } else { 386 } else {
272 movebytes = buffer->capacity - shift; 387 movebytes = buffer->capacity - shift;
273 } 388 }
274 } else { 389 } else {
275 movebytes = buffer->size; 390 movebytes = buffer->size;
276 } 391 }
277 392
278 memmove(buffer->space + shift, buffer->space, movebytes); 393 memmove(buffer->bytes + shift, buffer->bytes, movebytes);
279 buffer->size = shift+movebytes; 394 buffer->size = shift + movebytes;
280 395
281 buffer->pos += shift; 396 buffer->pos += shift;
282 if (buffer->pos > buffer->size) { 397 if (buffer->pos > buffer->size) {
283 buffer->pos = buffer->size; 398 buffer->pos = buffer->size;
284 } 399 }
285 400
286 return 0; 401 return 0;
287 } 402 }
288 403
289 int ucx_buffer_shift(UcxBuffer* buffer, off_t shift) { 404 int cxBufferShift(
405 CxBuffer *buffer,
406 off_t shift
407 ) {
290 if (shift < 0) { 408 if (shift < 0) {
291 return ucx_buffer_shift_left(buffer, (size_t) (-shift)); 409 return cxBufferShiftLeft(buffer, (size_t) (-shift));
292 } else if (shift > 0) { 410 } else if (shift > 0) {
293 return ucx_buffer_shift_right(buffer, (size_t) shift); 411 return cxBufferShiftRight(buffer, (size_t) shift);
294 } else { 412 } else {
295 return 0; 413 return 0;
296 } 414 }
297 } 415 }

mercurial