ucx/buffer.c

changeset 431
bb7da585debc
parent 324
ce13a778654a
child 440
7c4b9cba09ca
equal deleted inserted replaced
169:fe49cff3c571 431:bb7da585debc
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 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 }

mercurial