| 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 * |
208 const unsigned char *src, |
| 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, |
209 size_t size, |
| 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, space, size, 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, |
| 246 buffer->size = buffer->pos; |
266 buffer->size = buffer->pos; |
| 247 } |
267 } |
| 248 return nitems; |
268 return nitems; |
| 249 } |
269 } |
| 250 |
270 |
| 251 size_t len; |
271 size_t len, total_flushed = 0; |
| 252 size_t nitems_out = nitems; |
272 cx_buffer_write_retry: |
| 253 if (cx_szmul(size, nitems, &len)) { |
273 if (cx_szmul(size, nitems, &len)) { |
| 254 errno = EOVERFLOW; |
274 errno = EOVERFLOW; |
| 255 return 0; |
275 return total_flushed; |
| 256 } |
276 } |
| |
277 if (buffer->pos > SIZE_MAX - len) { |
| |
278 errno = EOVERFLOW; |
| |
279 return total_flushed; |
| |
280 } |
| |
281 |
| 257 size_t required = buffer->pos + len; |
282 size_t required = buffer->pos + len; |
| 258 if (buffer->pos > required) { |
|
| 259 return 0; |
|
| 260 } |
|
| 261 |
|
| 262 bool perform_flush = false; |
283 bool perform_flush = false; |
| 263 if (required > buffer->capacity) { |
284 if (required > buffer->capacity) { |
| 264 if (buffer->flags & CX_BUFFER_AUTO_EXTEND) { |
285 if (buffer->flags & CX_BUFFER_AUTO_EXTEND) { |
| 265 if (buffer->flush_blkmax > 0 && required > buffer->flush_threshold) { |
286 if (buffer->flush != NULL && required > buffer->flush->threshold) { |
| 266 perform_flush = true; |
287 perform_flush = true; |
| 267 } else { |
288 } else { |
| 268 if (cxBufferMinimumCapacity(buffer, required)) { |
289 if (cxBufferMinimumCapacity(buffer, required)) { |
| 269 return 0; // LCOV_EXCL_LINE |
290 return total_flushed; // LCOV_EXCL_LINE |
| 270 } |
291 } |
| 271 } |
292 } |
| 272 } else { |
293 } else { |
| 273 if (buffer->flush_blkmax > 0) { |
294 if (buffer->flush != NULL) { |
| 274 perform_flush = true; |
295 perform_flush = true; |
| 275 } else { |
296 } else { |
| 276 // truncate data to be written, if we can neither extend nor flush |
297 // truncate data, if we can neither extend nor flush |
| 277 len = buffer->capacity - buffer->pos; |
298 len = buffer->capacity - buffer->pos; |
| 278 if (size > 1) { |
299 if (size > 1) { |
| 279 len -= len % size; |
300 len -= len % size; |
| 280 } |
301 } |
| 281 nitems_out = len / size; |
302 nitems = len / size; |
| 282 } |
303 } |
| 283 } |
304 } |
| 284 } |
305 } |
| 285 |
306 |
| |
307 // check here and not above because of possible truncation |
| 286 if (len == 0) { |
308 if (len == 0) { |
| 287 return len; |
309 return total_flushed; |
| 288 } |
310 } |
| 289 |
311 |
| |
312 // check if we need to copy |
| |
313 if (buffer_copy_on_write(buffer)) return 0; |
| |
314 |
| |
315 // perform the operation |
| 290 if (perform_flush) { |
316 if (perform_flush) { |
| 291 size_t flush_max; |
317 size_t items_flushed; |
| 292 if (cx_szmul(buffer->flush_blkmax, buffer->flush_blksize, &flush_max)) { |
318 if (buffer->pos == 0) { |
| 293 errno = EOVERFLOW; |
319 // if we don't have data in the buffer, but are instructed |
| 294 return 0; |
320 // to flush, it means that we are supposed to relay the data |
| 295 } |
321 items_flushed = cx_buffer_flush_helper(buffer, ptr, size, nitems); |
| 296 size_t flush_pos = buffer->flush_func == NULL || buffer->flush_target == NULL |
322 if (items_flushed == 0) { |
| 297 ? buffer->pos |
323 // we needed to relay data, but could not flush anything |
| 298 : cx_buffer_write_flush_helper(buffer, buffer->bytes, 1, buffer->pos); |
324 // i.e. we have to give up to avoid endless trying |
| 299 if (flush_pos == buffer->pos) { |
325 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 } |
326 } |
| 311 items_keep = nitems - items_flush; |
327 nitems -= items_flushed; |
| 312 if (items_keep > 0) { |
328 total_flushed += items_flushed; |
| 313 // try again with the remaining stuff |
329 if (nitems > 0) { |
| 314 const unsigned char *new_ptr = ptr; |
330 ptr = ((unsigned char*)ptr) + items_flushed * size; |
| 315 new_ptr += items_flush * size; |
331 goto cx_buffer_write_retry; |
| 316 // report the directly flushed items as written plus the remaining stuff |
|
| 317 return items_flush + cxBufferWrite(new_ptr, size, items_keep, buffer); |
|
| 318 } else { |
|
| 319 // all items have been flushed - report them as written |
|
| 320 return nitems; |
|
| 321 } |
332 } |
| 322 } else if (flush_pos == 0) { |
333 return total_flushed; |
| 323 // nothing could be flushed at all, we immediately give up without writing any data |
|
| 324 return 0; |
|
| 325 } else { |
334 } else { |
| 326 // we were partially successful, we shift left and try again |
335 items_flushed = cx_buffer_flush_impl(buffer, size); |
| 327 cxBufferShiftLeft(buffer, flush_pos); |
336 if (items_flushed == 0) { |
| 328 return cxBufferWrite(ptr, size, nitems, buffer); |
337 // flush target is full, let's try to truncate |
| 329 } |
338 size_t remaining_space; |
| 330 } else { |
339 if (buffer->flags & CX_BUFFER_AUTO_EXTEND) { |
| 331 if (buffer_copy_on_write(buffer)) return 0; |
340 remaining_space = buffer->flush->threshold > buffer->pos |
| |
341 ? buffer->flush->threshold - buffer->pos |
| |
342 : 0; |
| |
343 } else { |
| |
344 remaining_space = buffer->capacity > buffer->pos |
| |
345 ? buffer->capacity - buffer->pos |
| |
346 : 0; |
| |
347 } |
| |
348 nitems = remaining_space / size; |
| |
349 if (nitems == 0) { |
| |
350 return total_flushed; |
| |
351 } |
| |
352 } |
| |
353 goto cx_buffer_write_retry; |
| |
354 } |
| |
355 } else { |
| 332 memcpy(buffer->bytes + buffer->pos, ptr, len); |
356 memcpy(buffer->bytes + buffer->pos, ptr, len); |
| 333 buffer->pos += len; |
357 buffer->pos += len; |
| 334 if (buffer->pos > buffer->size) { |
358 if (buffer->pos > buffer->size) { |
| 335 buffer->size = buffer->pos; |
359 buffer->size = buffer->pos; |
| 336 } |
360 } |
| 337 return nitems_out; |
361 return total_flushed + nitems; |
| 338 } |
362 } |
| 339 |
|
| 340 } |
363 } |
| 341 |
364 |
| 342 size_t cxBufferAppend( |
365 size_t cxBufferAppend( |
| 343 const void *ptr, |
366 const void *ptr, |
| 344 size_t size, |
367 size_t size, |
| 345 size_t nitems, |
368 size_t nitems, |
| 346 CxBuffer *buffer |
369 CxBuffer *buffer |
| 347 ) { |
370 ) { |
| 348 size_t pos = buffer->pos; |
371 size_t pos = buffer->pos; |
| 349 buffer->pos = buffer->size; |
372 size_t append_pos = buffer->size; |
| |
373 buffer->pos = append_pos; |
| 350 size_t written = cxBufferWrite(ptr, size, nitems, buffer); |
374 size_t written = cxBufferWrite(ptr, size, nitems, buffer); |
| 351 buffer->pos = pos; |
375 // the buffer might have been flushed |
| |
376 // we must compute a possible delta for the position |
| |
377 // expected: pos = append_pos + written |
| |
378 // -> if this is not the case, there is a delta |
| |
379 size_t delta = append_pos + written*size - buffer->pos; |
| |
380 if (delta > pos) { |
| |
381 buffer->pos = 0; |
| |
382 } else { |
| |
383 buffer->pos = pos - delta; |
| |
384 } |
| 352 return written; |
385 return written; |
| 353 } |
386 } |
| 354 |
387 |
| 355 int cxBufferPut( |
388 int cxBufferPut( |
| 356 CxBuffer *buffer, |
389 CxBuffer *buffer, |