Wed, 22 Jan 2025 20:36:10 +0100
avoid recursion in cxBufferWrite() - fixes #567
src/buffer.c | file | annotate | diff | comparison | revisions |
--- a/src/buffer.c Mon Jan 20 22:50:24 2025 +0100 +++ b/src/buffer.c Wed Jan 22 20:36:10 2025 +0100 @@ -268,17 +268,18 @@ return nitems; } - size_t len; + size_t len, total_flushed = 0; +cx_buffer_write_retry: if (cx_szmul(size, nitems, &len)) { errno = EOVERFLOW; - return 0; + return total_flushed; } if (buffer->pos > SIZE_MAX - len) { errno = EOVERFLOW; - return 0; + return total_flushed; } + size_t required = buffer->pos + len; - bool perform_flush = false; if (required > buffer->capacity) { if (buffer->flags & CX_BUFFER_AUTO_EXTEND) { @@ -286,7 +287,7 @@ perform_flush = true; } else { if (cxBufferMinimumCapacity(buffer, required)) { - return 0; // LCOV_EXCL_LINE + return total_flushed; // LCOV_EXCL_LINE } } } else { @@ -305,7 +306,7 @@ // check here and not above because of possible truncation if (len == 0) { - return 0; + return total_flushed; } // check if we need to copy @@ -313,27 +314,26 @@ // perform the operation if (perform_flush) { - size_t items_flush; + size_t items_flushed; if (buffer->pos == 0) { // if we don't have data in the buffer, but are instructed // to flush, it means that we are supposed to relay the data - items_flush = cx_buffer_flush_helper(buffer, ptr, size, nitems); - if (items_flush == 0) { + items_flushed = cx_buffer_flush_helper(buffer, ptr, size, nitems); + if (items_flushed == 0) { // we needed to relay data, but could not flush anything // i.e. we have to give up to avoid endless trying return 0; } - size_t ritems = nitems - items_flush; - if (ritems > 0) { - const unsigned char *rest = ptr; - rest += items_flush * size; - return items_flush + cxBufferWrite(rest, size, ritems, buffer); - } else { - return items_flush; + nitems -= items_flushed; + total_flushed += items_flushed; + if (nitems > 0) { + ptr = ((unsigned char*)ptr) + items_flushed * size; + goto cx_buffer_write_retry; } + return total_flushed; } else { - items_flush = cx_buffer_flush_impl(buffer, size); - if (items_flush == 0) { + items_flushed = cx_buffer_flush_impl(buffer, size); + if (items_flushed == 0) { // flush target is full, let's try to truncate size_t remaining_space; if (buffer->flags & CX_BUFFER_AUTO_EXTEND) { @@ -347,10 +347,10 @@ } nitems = remaining_space / size; if (nitems == 0) { - return 0; + return total_flushed; } } - return cxBufferWrite(ptr, size, nitems, buffer); + goto cx_buffer_write_retry; } } else { memcpy(buffer->bytes + buffer->pos, ptr, len); @@ -358,9 +358,8 @@ if (buffer->pos > buffer->size) { buffer->size = buffer->pos; } - return nitems; + return total_flushed + nitems; } - } size_t cxBufferAppend(