266 buffer->size = buffer->pos; |
266 buffer->size = buffer->pos; |
267 } |
267 } |
268 return nitems; |
268 return nitems; |
269 } |
269 } |
270 |
270 |
271 size_t len; |
271 size_t len, total_flushed = 0; |
|
272 cx_buffer_write_retry: |
272 if (cx_szmul(size, nitems, &len)) { |
273 if (cx_szmul(size, nitems, &len)) { |
273 errno = EOVERFLOW; |
274 errno = EOVERFLOW; |
274 return 0; |
275 return total_flushed; |
275 } |
276 } |
276 if (buffer->pos > SIZE_MAX - len) { |
277 if (buffer->pos > SIZE_MAX - len) { |
277 errno = EOVERFLOW; |
278 errno = EOVERFLOW; |
278 return 0; |
279 return total_flushed; |
279 } |
280 } |
|
281 |
280 size_t required = buffer->pos + len; |
282 size_t required = buffer->pos + len; |
281 |
|
282 bool perform_flush = false; |
283 bool perform_flush = false; |
283 if (required > buffer->capacity) { |
284 if (required > buffer->capacity) { |
284 if (buffer->flags & CX_BUFFER_AUTO_EXTEND) { |
285 if (buffer->flags & CX_BUFFER_AUTO_EXTEND) { |
285 if (buffer->flush != NULL && required > buffer->flush->threshold) { |
286 if (buffer->flush != NULL && required > buffer->flush->threshold) { |
286 perform_flush = true; |
287 perform_flush = true; |
287 } else { |
288 } else { |
288 if (cxBufferMinimumCapacity(buffer, required)) { |
289 if (cxBufferMinimumCapacity(buffer, required)) { |
289 return 0; // LCOV_EXCL_LINE |
290 return total_flushed; // LCOV_EXCL_LINE |
290 } |
291 } |
291 } |
292 } |
292 } else { |
293 } else { |
293 if (buffer->flush != NULL) { |
294 if (buffer->flush != NULL) { |
294 perform_flush = true; |
295 perform_flush = true; |
303 } |
304 } |
304 } |
305 } |
305 |
306 |
306 // check here and not above because of possible truncation |
307 // check here and not above because of possible truncation |
307 if (len == 0) { |
308 if (len == 0) { |
308 return 0; |
309 return total_flushed; |
309 } |
310 } |
310 |
311 |
311 // check if we need to copy |
312 // check if we need to copy |
312 if (buffer_copy_on_write(buffer)) return 0; |
313 if (buffer_copy_on_write(buffer)) return 0; |
313 |
314 |
314 // perform the operation |
315 // perform the operation |
315 if (perform_flush) { |
316 if (perform_flush) { |
316 size_t items_flush; |
317 size_t items_flushed; |
317 if (buffer->pos == 0) { |
318 if (buffer->pos == 0) { |
318 // if we don't have data in the buffer, but are instructed |
319 // if we don't have data in the buffer, but are instructed |
319 // to flush, it means that we are supposed to relay the data |
320 // to flush, it means that we are supposed to relay the data |
320 items_flush = cx_buffer_flush_helper(buffer, ptr, size, nitems); |
321 items_flushed = cx_buffer_flush_helper(buffer, ptr, size, nitems); |
321 if (items_flush == 0) { |
322 if (items_flushed == 0) { |
322 // we needed to relay data, but could not flush anything |
323 // we needed to relay data, but could not flush anything |
323 // i.e. we have to give up to avoid endless trying |
324 // i.e. we have to give up to avoid endless trying |
324 return 0; |
325 return 0; |
325 } |
326 } |
326 size_t ritems = nitems - items_flush; |
327 nitems -= items_flushed; |
327 if (ritems > 0) { |
328 total_flushed += items_flushed; |
328 const unsigned char *rest = ptr; |
329 if (nitems > 0) { |
329 rest += items_flush * size; |
330 ptr = ((unsigned char*)ptr) + items_flushed * size; |
330 return items_flush + cxBufferWrite(rest, size, ritems, buffer); |
331 goto cx_buffer_write_retry; |
331 } else { |
|
332 return items_flush; |
|
333 } |
332 } |
|
333 return total_flushed; |
334 } else { |
334 } else { |
335 items_flush = cx_buffer_flush_impl(buffer, size); |
335 items_flushed = cx_buffer_flush_impl(buffer, size); |
336 if (items_flush == 0) { |
336 if (items_flushed == 0) { |
337 // flush target is full, let's try to truncate |
337 // flush target is full, let's try to truncate |
338 size_t remaining_space; |
338 size_t remaining_space; |
339 if (buffer->flags & CX_BUFFER_AUTO_EXTEND) { |
339 if (buffer->flags & CX_BUFFER_AUTO_EXTEND) { |
340 remaining_space = buffer->flush->threshold > buffer->pos |
340 remaining_space = buffer->flush->threshold > buffer->pos |
341 ? buffer->flush->threshold - buffer->pos |
341 ? buffer->flush->threshold - buffer->pos |
345 ? buffer->capacity - buffer->pos |
345 ? buffer->capacity - buffer->pos |
346 : 0; |
346 : 0; |
347 } |
347 } |
348 nitems = remaining_space / size; |
348 nitems = remaining_space / size; |
349 if (nitems == 0) { |
349 if (nitems == 0) { |
350 return 0; |
350 return total_flushed; |
351 } |
351 } |
352 } |
352 } |
353 return cxBufferWrite(ptr, size, nitems, buffer); |
353 goto cx_buffer_write_retry; |
354 } |
354 } |
355 } else { |
355 } else { |
356 memcpy(buffer->bytes + buffer->pos, ptr, len); |
356 memcpy(buffer->bytes + buffer->pos, ptr, len); |
357 buffer->pos += len; |
357 buffer->pos += len; |
358 if (buffer->pos > buffer->size) { |
358 if (buffer->pos > buffer->size) { |
359 buffer->size = buffer->pos; |
359 buffer->size = buffer->pos; |
360 } |
360 } |
361 return nitems; |
361 return total_flushed + nitems; |
362 } |
362 } |
363 |
|
364 } |
363 } |
365 |
364 |
366 size_t cxBufferAppend( |
365 size_t cxBufferAppend( |
367 const void *ptr, |
366 const void *ptr, |
368 size_t size, |
367 size_t size, |