src/buffer.c

changeset 1110
a0e9be7ed131
parent 1065
6eb7b54975ee
equal deleted inserted replaced
1109:89ec23988b88 1110:a0e9be7ed131
69 } 69 }
70 buffer->capacity = capacity; 70 buffer->capacity = capacity;
71 buffer->size = 0; 71 buffer->size = 0;
72 buffer->pos = 0; 72 buffer->pos = 0;
73 73
74 buffer->flush_func = NULL; 74 buffer->flush = NULL;
75 buffer->flush_target = NULL; 75
76 buffer->flush_blkmax = 0; 76 return 0;
77 buffer->flush_blksize = 4096; 77 }
78 buffer->flush_threshold = SIZE_MAX; 78
79 79 int cxBufferEnableFlushing(
80 CxBuffer *buffer,
81 CxBufferFlushConfig config
82 ) {
83 buffer->flush = malloc(sizeof(CxBufferFlushConfig));
84 if (buffer->flush == NULL) return -1; // LCOV_EXCL_LINE
85 memcpy(buffer->flush, &config, sizeof(CxBufferFlushConfig));
80 return 0; 86 return 0;
81 } 87 }
82 88
83 void cxBufferDestroy(CxBuffer *buffer) { 89 void cxBufferDestroy(CxBuffer *buffer) {
84 if (buffer->flags & CX_BUFFER_FREE_CONTENTS) { 90 if (buffer->flags & CX_BUFFER_FREE_CONTENTS) {
85 cxFree(buffer->allocator, buffer->bytes); 91 cxFree(buffer->allocator, buffer->bytes);
86 } 92 }
93 free(buffer->flush);
87 memset(buffer, 0, sizeof(CxBuffer)); 94 memset(buffer, 0, sizeof(CxBuffer));
88 } 95 }
89 96
90 CxBuffer *cxBufferCreate( 97 CxBuffer *cxBufferCreate(
91 void *space, 98 void *space,
194 } else { 201 } else {
195 return -1; // LCOV_EXCL_LINE 202 return -1; // LCOV_EXCL_LINE
196 } 203 }
197 } 204 }
198 205
199 /** 206 static size_t cx_buffer_flush_helper(
200 * Helps flushing data to the flush target of a buffer. 207 const CxBuffer *buffer,
201 *
202 * @param buffer the buffer containing the config
203 * @param space the data to flush
204 * @param size the element size
205 * @param nitems the number of items
206 * @return the number of items flushed
207 */
208 static size_t cx_buffer_write_flush_helper(
209 CxBuffer *buffer,
210 const unsigned char *space,
211 size_t size, 208 size_t size,
209 const unsigned char *src,
212 size_t nitems 210 size_t nitems
213 ) { 211 ) {
214 size_t pos = 0; 212 // flush data from an arbitrary source
215 size_t remaining = nitems; 213 // does not need to be the buffer's contents
216 size_t max_items = buffer->flush_blksize / size; 214 size_t max_items = buffer->flush->blksize / size;
217 while (remaining > 0) { 215 size_t fblocks = 0;
218 size_t items = remaining > max_items ? max_items : remaining; 216 size_t flushed_total = 0;
219 size_t flushed = buffer->flush_func( 217 while (nitems > 0 && fblocks < buffer->flush->blkmax) {
220 space + pos, 218 fblocks++;
221 size, items, 219 size_t items = nitems > max_items ? max_items : nitems;
222 buffer->flush_target); 220 size_t flushed = buffer->flush->wfunc(
221 src, size, items, buffer->flush->target);
223 if (flushed > 0) { 222 if (flushed > 0) {
224 pos += (flushed * size); 223 flushed_total += flushed;
225 remaining -= flushed; 224 src += flushed * size;
225 nitems -= flushed;
226 } else { 226 } else {
227 // if no bytes can be flushed out anymore, we give up 227 // if no bytes can be flushed out anymore, we give up
228 break; 228 break;
229 } 229 }
230 } 230 }
231 return nitems - remaining; 231 return flushed_total;
232 }
233
234 static size_t cx_buffer_flush_impl(CxBuffer *buffer, size_t size) {
235 // flush the current contents of the buffer
236 unsigned char *space = buffer->bytes;
237 size_t remaining = buffer->pos / size;
238 size_t flushed_total = cx_buffer_flush_helper(
239 buffer, size, space, remaining);
240
241 // shift the buffer left after flushing
242 // IMPORTANT: up to this point, copy on write must have been
243 // performed already, because we can't do error handling here
244 cxBufferShiftLeft(buffer, flushed_total*size);
245
246 return flushed_total;
247 }
248
249 size_t cxBufferFlush(CxBuffer *buffer) {
250 if (buffer_copy_on_write(buffer)) return 0;
251 return cx_buffer_flush_impl(buffer, 1);
232 } 252 }
233 253
234 size_t cxBufferWrite( 254 size_t cxBufferWrite(
235 const void *ptr, 255 const void *ptr,
236 size_t size, 256 size_t size,
247 } 267 }
248 return nitems; 268 return nitems;
249 } 269 }
250 270
251 size_t len; 271 size_t len;
252 size_t nitems_out = nitems;
253 if (cx_szmul(size, nitems, &len)) { 272 if (cx_szmul(size, nitems, &len)) {
254 errno = EOVERFLOW; 273 errno = EOVERFLOW;
255 return 0; 274 return 0;
256 } 275 }
276 if (buffer->pos > SIZE_MAX - len) {
277 errno = EOVERFLOW;
278 return 0;
279 }
257 size_t required = buffer->pos + len; 280 size_t required = buffer->pos + len;
258 if (buffer->pos > required) {
259 return 0;
260 }
261 281
262 bool perform_flush = false; 282 bool perform_flush = false;
263 if (required > buffer->capacity) { 283 if (required > buffer->capacity) {
264 if (buffer->flags & CX_BUFFER_AUTO_EXTEND) { 284 if (buffer->flags & CX_BUFFER_AUTO_EXTEND) {
265 if (buffer->flush_blkmax > 0 && required > buffer->flush_threshold) { 285 if (buffer->flush != NULL && required > buffer->flush->threshold) {
266 perform_flush = true; 286 perform_flush = true;
267 } else { 287 } else {
268 if (cxBufferMinimumCapacity(buffer, required)) { 288 if (cxBufferMinimumCapacity(buffer, required)) {
269 return 0; // LCOV_EXCL_LINE 289 return 0; // LCOV_EXCL_LINE
270 } 290 }
271 } 291 }
272 } else { 292 } else {
273 if (buffer->flush_blkmax > 0) { 293 if (buffer->flush != NULL) {
274 perform_flush = true; 294 perform_flush = true;
275 } else { 295 } else {
276 // truncate data to be written, if we can neither extend nor flush 296 // truncate data, if we can neither extend nor flush
277 len = buffer->capacity - buffer->pos; 297 len = buffer->capacity - buffer->pos;
278 if (size > 1) { 298 if (size > 1) {
279 len -= len % size; 299 len -= len % size;
280 } 300 }
281 nitems_out = len / size; 301 nitems = len / size;
282 } 302 }
283 } 303 }
284 } 304 }
285 305
306 // check here and not above because of possible truncation
286 if (len == 0) { 307 if (len == 0) {
287 return len; 308 return 0;
288 } 309 }
289 310
311 // check if we need to copy
312 if (buffer_copy_on_write(buffer)) return 0;
313
314 // perform the operation
290 if (perform_flush) { 315 if (perform_flush) {
291 size_t flush_max; 316 size_t items_flush;
292 if (cx_szmul(buffer->flush_blkmax, buffer->flush_blksize, &flush_max)) { 317 if (buffer->pos == 0) {
293 errno = EOVERFLOW; 318 // if we don't have data in the buffer, but are instructed
294 return 0; 319 // to flush, it means that we are supposed to relay the data
295 } 320 items_flush = cx_buffer_flush_helper(buffer, size, ptr, nitems);
296 size_t flush_pos = buffer->flush_func == NULL || buffer->flush_target == NULL 321 if (items_flush == 0) {
297 ? buffer->pos 322 // we needed to flush, but could not flush anything
298 : cx_buffer_write_flush_helper(buffer, buffer->bytes, 1, buffer->pos); 323 // give up and avoid endless trying
299 if (flush_pos == buffer->pos) { 324 return 0;
300 // entire buffer has been flushed, we can reset
301 buffer->size = buffer->pos = 0;
302
303 size_t items_flush; // how many items can also be directly flushed
304 size_t items_keep; // how many items have to be written to the buffer
305
306 items_flush = flush_max >= required ? nitems : (flush_max - flush_pos) / size;
307 if (items_flush > 0) {
308 items_flush = cx_buffer_write_flush_helper(buffer, ptr, size, items_flush / size);
309 // in case we could not flush everything, keep the rest
310 } 325 }
311 items_keep = nitems - items_flush; 326 size_t ritems = nitems - items_flush;
312 if (items_keep > 0) { 327 const unsigned char *rest = ptr;
313 // try again with the remaining stuff 328 rest += items_flush * size;
314 const unsigned char *new_ptr = ptr; 329 return items_flush + cxBufferWrite(rest, size, ritems, buffer);
315 new_ptr += items_flush * size; 330 } else {
316 // report the directly flushed items as written plus the remaining stuff 331 items_flush = cx_buffer_flush_impl(buffer, size);
317 return items_flush + cxBufferWrite(new_ptr, size, items_keep, buffer); 332 if (items_flush == 0) {
318 } else { 333 return 0;
319 // all items have been flushed - report them as written
320 return nitems;
321 } 334 }
322 } else if (flush_pos == 0) {
323 // nothing could be flushed at all, we immediately give up without writing any data
324 return 0;
325 } else {
326 // we were partially successful, we shift left and try again
327 cxBufferShiftLeft(buffer, flush_pos);
328 return cxBufferWrite(ptr, size, nitems, buffer); 335 return cxBufferWrite(ptr, size, nitems, buffer);
329 } 336 }
330 } else { 337 } else {
331 if (buffer_copy_on_write(buffer)) return 0;
332 memcpy(buffer->bytes + buffer->pos, ptr, len); 338 memcpy(buffer->bytes + buffer->pos, ptr, len);
333 buffer->pos += len; 339 buffer->pos += len;
334 if (buffer->pos > buffer->size) { 340 if (buffer->pos > buffer->size) {
335 buffer->size = buffer->pos; 341 buffer->size = buffer->pos;
336 } 342 }
337 return nitems_out; 343 return nitems;
338 } 344 }
339 345
340 } 346 }
341 347
342 size_t cxBufferAppend( 348 size_t cxBufferAppend(

mercurial