UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 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 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "cx/buffer.h" 30 #include "cx/utils.h" 31 32 #include <stdio.h> 33 #include <string.h> 34 35 int cxBufferInit( 36 CxBuffer *buffer, 37 void *space, 38 size_t capacity, 39 const CxAllocator *allocator, 40 int flags 41 ) { 42 if (allocator == NULL) allocator = cxDefaultAllocator; 43 buffer->allocator = allocator; 44 buffer->flags = flags; 45 if (!space) { 46 buffer->bytes = cxMalloc(allocator, capacity); 47 if (buffer->bytes == NULL) { 48 return 1; 49 } 50 buffer->flags |= CX_BUFFER_FREE_CONTENTS; 51 } else { 52 buffer->bytes = space; 53 } 54 buffer->capacity = capacity; 55 buffer->size = 0; 56 buffer->pos = 0; 57 58 buffer->flush_func = NULL; 59 buffer->flush_target = NULL; 60 buffer->flush_blkmax = 0; 61 buffer->flush_blksize = 4096; 62 buffer->flush_threshold = SIZE_MAX; 63 64 return 0; 65 } 66 67 void cxBufferDestroy(CxBuffer *buffer) { 68 if ((buffer->flags & CX_BUFFER_FREE_CONTENTS) == CX_BUFFER_FREE_CONTENTS) { 69 cxFree(buffer->allocator, buffer->bytes); 70 } 71 } 72 73 CxBuffer *cxBufferCreate( 74 void *space, 75 size_t capacity, 76 const CxAllocator *allocator, 77 int flags 78 ) { 79 CxBuffer *buf = cxMalloc(allocator, sizeof(CxBuffer)); 80 if (buf == NULL) return NULL; 81 if (0 == cxBufferInit(buf, space, capacity, allocator, flags)) { 82 return buf; 83 } else { 84 cxFree(allocator, buf); 85 return NULL; 86 } 87 } 88 89 void cxBufferFree(CxBuffer *buffer) { 90 if ((buffer->flags & CX_BUFFER_FREE_CONTENTS) == CX_BUFFER_FREE_CONTENTS) { 91 cxFree(buffer->allocator, buffer->bytes); 92 } 93 cxFree(buffer->allocator, buffer); 94 } 95 96 int cxBufferSeek( 97 CxBuffer *buffer, 98 off_t offset, 99 int whence 100 ) { 101 size_t npos; 102 switch (whence) { 103 case SEEK_CUR: 104 npos = buffer->pos; 105 break; 106 case SEEK_END: 107 npos = buffer->size; 108 break; 109 case SEEK_SET: 110 npos = 0; 111 break; 112 default: 113 return -1; 114 } 115 116 size_t opos = npos; 117 npos += offset; 118 119 if ((offset > 0 && npos < opos) || (offset < 0 && npos > opos)) { 120 return -1; 121 } 122 123 if (npos >= buffer->size) { 124 return -1; 125 } else { 126 buffer->pos = npos; 127 return 0; 128 } 129 130 } 131 132 void cxBufferClear(CxBuffer *buffer) { 133 memset(buffer->bytes, 0, buffer->size); 134 buffer->size = 0; 135 buffer->pos = 0; 136 } 137 138 void cxBufferReset(CxBuffer *buffer) { 139 buffer->size = 0; 140 buffer->pos = 0; 141 } 142 143 int cxBufferEof(const CxBuffer *buffer) { 144 return buffer->pos >= buffer->size; 145 } 146 147 int cxBufferMinimumCapacity( 148 CxBuffer *buffer, 149 size_t newcap 150 ) { 151 if (newcap <= buffer->capacity) { 152 return 0; 153 } 154 155 if (cxReallocate(buffer->allocator, 156 (void **) &buffer->bytes, newcap) == 0) { 157 buffer->capacity = newcap; 158 return 0; 159 } else { 160 return -1; 161 } 162 } 163 164 /** 165 * Helps flushing data to the flush target of a buffer. 166 * 167 * @param buffer the buffer containing the config 168 * @param space the data to flush 169 * @param size the element size 170 * @param nitems the number of items 171 * @return the number of items flushed 172 */ 173 static size_t cx_buffer_write_flush_helper( 174 CxBuffer *buffer, 175 const unsigned char *space, 176 size_t size, 177 size_t nitems 178 ) { 179 size_t pos = 0; 180 size_t remaining = nitems; 181 size_t max_items = buffer->flush_blksize / size; 182 while (remaining > 0) { 183 size_t items = remaining > max_items ? max_items : remaining; 184 size_t flushed = buffer->flush_func( 185 space + pos, 186 size, items, 187 buffer->flush_target); 188 if (flushed > 0) { 189 pos += (flushed * size); 190 remaining -= flushed; 191 } else { 192 // if no bytes can be flushed out anymore, we give up 193 break; 194 } 195 } 196 return nitems - remaining; 197 } 198 199 size_t cxBufferWrite( 200 const void *ptr, 201 size_t size, 202 size_t nitems, 203 CxBuffer *buffer 204 ) { 205 // optimize for easy case 206 if (size == 1 && (buffer->capacity - buffer->pos) >= nitems) { 207 memcpy(buffer->bytes + buffer->pos, ptr, nitems); 208 buffer->pos += nitems; 209 if (buffer->pos > buffer->size) { 210 buffer->size = buffer->pos; 211 } 212 return nitems; 213 } 214 215 size_t len; 216 size_t nitems_out = nitems; 217 if (cx_szmul(size, nitems, &len)) { 218 return 0; 219 } 220 size_t required = buffer->pos + len; 221 if (buffer->pos > required) { 222 return 0; 223 } 224 225 bool perform_flush = false; 226 if (required > buffer->capacity) { 227 if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND && required) { 228 if (buffer->flush_blkmax > 0 && required > buffer->flush_threshold) { 229 perform_flush = true; 230 } else { 231 if (cxBufferMinimumCapacity(buffer, required)) { 232 return 0; 233 } 234 } 235 } else { 236 if (buffer->flush_blkmax > 0) { 237 perform_flush = true; 238 } else { 239 // truncate data to be written, if we can neither extend nor flush 240 len = buffer->capacity - buffer->pos; 241 if (size > 1) { 242 len -= len % size; 243 } 244 nitems_out = len / size; 245 } 246 } 247 } 248 249 if (len == 0) { 250 return len; 251 } 252 253 if (perform_flush) { 254 size_t flush_max; 255 if (cx_szmul(buffer->flush_blkmax, buffer->flush_blksize, &flush_max)) { 256 return 0; 257 } 258 size_t flush_pos = buffer->flush_func == NULL || buffer->flush_target == NULL 259 ? buffer->pos 260 : cx_buffer_write_flush_helper(buffer, buffer->bytes, 1, buffer->pos); 261 if (flush_pos == buffer->pos) { 262 // entire buffer has been flushed, we can reset 263 buffer->size = buffer->pos = 0; 264 265 size_t items_flush; // how many items can also be directly flushed 266 size_t items_keep; // how many items have to be written to the buffer 267 268 items_flush = flush_max >= required ? nitems : (flush_max - flush_pos) / size; 269 if (items_flush > 0) { 270 items_flush = cx_buffer_write_flush_helper(buffer, ptr, size, items_flush / size); 271 // in case we could not flush everything, keep the rest 272 } 273 items_keep = nitems - items_flush; 274 if (items_keep > 0) { 275 // try again with the remaining stuff 276 const unsigned char *new_ptr = ptr; 277 new_ptr += items_flush * size; 278 // report the directly flushed items as written plus the remaining stuff 279 return items_flush + cxBufferWrite(new_ptr, size, items_keep, buffer); 280 } else { 281 // all items have been flushed - report them as written 282 return nitems; 283 } 284 } else if (flush_pos == 0) { 285 // nothing could be flushed at all, we immediately give up without writing any data 286 return 0; 287 } else { 288 // we were partially successful, we shift left and try again 289 cxBufferShiftLeft(buffer, flush_pos); 290 return cxBufferWrite(ptr, size, nitems, buffer); 291 } 292 } else { 293 memcpy(buffer->bytes + buffer->pos, ptr, len); 294 buffer->pos += len; 295 if (buffer->pos > buffer->size) { 296 buffer->size = buffer->pos; 297 } 298 return nitems_out; 299 } 300 301 } 302 303 int cxBufferPut( 304 CxBuffer *buffer, 305 int c 306 ) { 307 c &= 0xFF; 308 unsigned char const ch = c; 309 if (cxBufferWrite(&ch, 1, 1, buffer) == 1) { 310 return c; 311 } else { 312 return EOF; 313 } 314 } 315 316 size_t cxBufferPutString( 317 CxBuffer *buffer, 318 const char *str 319 ) { 320 return cxBufferWrite(str, 1, strlen(str), buffer); 321 } 322 323 size_t cxBufferRead( 324 void *ptr, 325 size_t size, 326 size_t nitems, 327 CxBuffer *buffer 328 ) { 329 size_t len; 330 if (cx_szmul(size, nitems, &len)) { 331 return 0; 332 } 333 if (buffer->pos + len > buffer->size) { 334 len = buffer->size - buffer->pos; 335 if (size > 1) len -= len % size; 336 } 337 338 if (len <= 0) { 339 return len; 340 } 341 342 memcpy(ptr, buffer->bytes + buffer->pos, len); 343 buffer->pos += len; 344 345 return len / size; 346 } 347 348 int cxBufferGet(CxBuffer *buffer) { 349 if (cxBufferEof(buffer)) { 350 return EOF; 351 } else { 352 int c = buffer->bytes[buffer->pos]; 353 buffer->pos++; 354 return c; 355 } 356 } 357 358 int cxBufferShiftLeft( 359 CxBuffer *buffer, 360 size_t shift 361 ) { 362 if (shift >= buffer->size) { 363 buffer->pos = buffer->size = 0; 364 } else { 365 memmove(buffer->bytes, buffer->bytes + shift, buffer->size - shift); 366 buffer->size -= shift; 367 368 if (buffer->pos >= shift) { 369 buffer->pos -= shift; 370 } else { 371 buffer->pos = 0; 372 } 373 } 374 return 0; 375 } 376 377 int cxBufferShiftRight( 378 CxBuffer *buffer, 379 size_t shift 380 ) { 381 size_t req_capacity = buffer->size + shift; 382 size_t movebytes; 383 384 // auto extend buffer, if required and enabled 385 if (buffer->capacity < req_capacity) { 386 if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND) { 387 if (cxBufferMinimumCapacity(buffer, req_capacity)) { 388 return 1; 389 } 390 movebytes = buffer->size; 391 } else { 392 movebytes = buffer->capacity - shift; 393 } 394 } else { 395 movebytes = buffer->size; 396 } 397 398 memmove(buffer->bytes + shift, buffer->bytes, movebytes); 399 buffer->size = shift + movebytes; 400 401 buffer->pos += shift; 402 if (buffer->pos > buffer->size) { 403 buffer->pos = buffer->size; 404 } 405 406 return 0; 407 } 408 409 int cxBufferShift( 410 CxBuffer *buffer, 411 off_t shift 412 ) { 413 if (shift < 0) { 414 return cxBufferShiftLeft(buffer, (size_t) (-shift)); 415 } else if (shift > 0) { 416 return cxBufferShiftRight(buffer, (size_t) shift); 417 } else { 418 return 0; 419 } 420 } 421