src/ucx/buffer.c

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

mercurial