PageRenderTime 135ms CodeModel.GetById 26ms app.highlight 99ms RepoModel.GetById 1ms app.codeStats 0ms

/src/pdsh/cbuf.c

https://code.google.com/
C | 1814 lines | 1338 code | 226 blank | 250 comment | 377 complexity | fa029cf4f962d7f512a04d537f042e85 MD5 | raw file
   1/*****************************************************************************
   2 *  $Id$
   3 *****************************************************************************
   4 *  Copyright (C) 2002-2003 The Regents of the University of California.
   5 *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
   6 *  Written by Chris Dunlap <cdunlap@llnl.gov>.
   7 *
   8 *  This file is from LSD-Tools, the LLNL Software Development Toolbox.
   9 *
  10 *  LSD-Tools is free software; you can redistribute it and/or modify it under
  11 *  the terms of the GNU General Public License as published by the Free
  12 *  Software Foundation; either version 2 of the License, or (at your option)
  13 *  any later version.
  14 *
  15 *  LSD-Tools is distributed in the hope that it will be useful, but WITHOUT
  16 *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  17 *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  18 *  more details.
  19 *
  20 *  You should have received a copy of the GNU General Public License along
  21 *  with LSD-Tools; if not, write to the Free Software Foundation, Inc.,
  22 *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
  23 *****************************************************************************
  24 *  Refer to "cbuf.h" for documentation on public functions.
  25 *****************************************************************************/
  26
  27
  28#ifdef HAVE_CONFIG_H
  29#  include "config.h"
  30#endif /* HAVE_CONFIG_H */
  31
  32#ifdef WITH_PTHREADS
  33#  include <pthread.h>
  34#endif /* WITH_PTHREADS */
  35
  36#include <assert.h>
  37#include <errno.h>
  38#include <stdlib.h>
  39#include <string.h>
  40#include <unistd.h>
  41#include "cbuf.h"
  42
  43
  44/*********************
  45 *  lsd_fatal_error  *
  46 *********************/
  47
  48#ifdef WITH_LSD_FATAL_ERROR_FUNC
  49#  undef lsd_fatal_error
  50   extern void lsd_fatal_error(char *file, int line, char *mesg);
  51#else /* !WITH_LSD_FATAL_ERROR_FUNC */
  52#  ifndef lsd_fatal_error
  53#    include <errno.h>
  54#    include <stdio.h>
  55#    include <string.h>
  56#    define lsd_fatal_error(file, line, mesg)                                 \
  57       do {                                                                   \
  58           fprintf(stderr, "ERROR: [%s:%d] %s: %s\n",                         \
  59                   file, line, mesg, strerror(errno));                        \
  60       } while (0)
  61#  endif /* !lsd_fatal_error */
  62#endif /* !WITH_LSD_FATAL_ERROR_FUNC */
  63
  64
  65/*********************
  66 *  lsd_nomem_error  *
  67 *********************/
  68
  69#ifdef WITH_LSD_NOMEM_ERROR_FUNC
  70#  undef lsd_nomem_error
  71   extern void * lsd_nomem_error(char *file, int line, char *mesg);
  72#else /* !WITH_LSD_NOMEM_ERROR_FUNC */
  73#  ifndef lsd_nomem_error
  74#    define lsd_nomem_error(file, line, mesg) (NULL)
  75#  endif /* !lsd_nomem_error */
  76#endif /* !WITH_LSD_NOMEM_ERROR_FUNC */
  77
  78
  79/***************
  80 *  Constants  *
  81 ***************/
  82
  83#define CBUF_CHUNK      1000
  84#define CBUF_MAGIC      0xDEADBEEF
  85#define CBUF_MAGIC_LEN  (sizeof(unsigned long))
  86
  87
  88/****************
  89 *  Data Types  *
  90 ****************/
  91
  92struct cbuf {
  93
  94#ifndef NDEBUG
  95    unsigned long       magic;          /* cookie for asserting validity     */
  96#endif /* !NDEBUG */
  97
  98#ifdef WITH_PTHREADS
  99    pthread_mutex_t     mutex;          /* mutex to protect access to cbuf   */
 100#endif /* WITH_PTHREADS */
 101
 102    int                 alloc;          /* num bytes malloc'd/realloc'd      */
 103    int                 minsize;        /* min bytes of data to allocate     */
 104    int                 maxsize;        /* max bytes of data to allocate     */
 105    int                 size;           /* num bytes of data allocated       */
 106    int                 used;           /* num bytes of unread data          */
 107    cbuf_overwrite_t    overwrite;      /* overwrite option behavior         */
 108    int                 got_wrap;       /* true if data has wrapped          */
 109    int                 i_in;           /* index to where data is written in */
 110    int                 i_out;          /* index to where data is read out   */
 111    int                 i_rep;          /* index to where data is replayable */
 112    unsigned char      *data;           /* ptr to circular buffer of data    */
 113};
 114
 115typedef int (*cbuf_iof) (void *cbuf_data, void *arg, int len);
 116
 117
 118/****************
 119 *  Prototypes  *
 120 ****************/
 121
 122static int cbuf_find_replay_line (cbuf_t cb, int chars, int *nlines, int *nl);
 123static int cbuf_find_unread_line (cbuf_t cb, int chars, int *nlines);
 124
 125static int cbuf_get_fd (void *dstbuf, int *psrcfd, int len);
 126static int cbuf_get_mem (void *dstbuf, unsigned char **psrcbuf, int len);
 127static int cbuf_put_fd (void *srcbuf, int *pdstfd, int len);
 128static int cbuf_put_mem (void *srcbuf, unsigned char **pdstbuf, int len);
 129
 130static int cbuf_copier (cbuf_t src, cbuf_t dst, int len, int *ndropped);
 131static int cbuf_dropper (cbuf_t cb, int len);
 132static int cbuf_reader (cbuf_t src, int len, cbuf_iof putf, void *dst);
 133static int cbuf_replayer (cbuf_t src, int len, cbuf_iof putf, void *dst);
 134static int cbuf_writer (cbuf_t dst, int len, cbuf_iof getf, void *src,
 135       int *ndropped);
 136
 137static int cbuf_grow (cbuf_t cb, int n);
 138static int cbuf_shrink (cbuf_t cb);
 139
 140#ifndef NDEBUG
 141static int cbuf_is_valid (cbuf_t cb);
 142#endif /* !NDEBUG */
 143
 144
 145/************
 146 *  Macros  *
 147 ************/
 148
 149#ifndef MAX
 150#  define MAX(x,y) (((x) >= (y)) ? (x) : (y))
 151#endif /* !MAX */
 152
 153#ifndef MIN
 154#  define MIN(x,y) (((x) <= (y)) ? (x) : (y))
 155#endif /* !MIN */
 156
 157#ifdef WITH_PTHREADS
 158
 159#  define cbuf_mutex_init(cb)                                                 \
 160     do {                                                                     \
 161         int e = pthread_mutex_init(&cb->mutex, NULL);                        \
 162         if (e) {                                                             \
 163             errno = e;                                                       \
 164             lsd_fatal_error(__FILE__, __LINE__, "cbuf mutex init");          \
 165             abort();                                                         \
 166         }                                                                    \
 167     } while (0)
 168
 169#  define cbuf_mutex_lock(cb)                                                 \
 170     do {                                                                     \
 171         int e = pthread_mutex_lock(&cb->mutex);                              \
 172         if (e) {                                                             \
 173             errno = e;                                                       \
 174             lsd_fatal_error(__FILE__, __LINE__, "cbuf mutex lock");          \
 175             abort();                                                         \
 176         }                                                                    \
 177     } while (0)
 178
 179#  define cbuf_mutex_unlock(cb)                                               \
 180     do {                                                                     \
 181         int e = pthread_mutex_unlock(&cb->mutex);                            \
 182         if (e) {                                                             \
 183             errno = e;                                                       \
 184             lsd_fatal_error(__FILE__, __LINE__, "cbuf mutex unlock");        \
 185             abort();                                                         \
 186         }                                                                    \
 187     } while (0)
 188
 189#  define cbuf_mutex_destroy(cb)                                              \
 190     do {                                                                     \
 191         int e = pthread_mutex_destroy(&cb->mutex);                           \
 192         if (e) {                                                             \
 193             errno = e;                                                       \
 194             lsd_fatal_error(__FILE__, __LINE__, "cbuf mutex destroy");       \
 195             abort();                                                         \
 196         }                                                                    \
 197     } while (0)
 198
 199#  ifndef NDEBUG
 200     static int cbuf_mutex_is_locked (cbuf_t cb);
 201#  endif /* !NDEBUG */
 202
 203#else /* !WITH_PTHREADS */
 204
 205#  define cbuf_mutex_init(cb)
 206#  define cbuf_mutex_lock(cb)
 207#  define cbuf_mutex_unlock(cb)
 208#  define cbuf_mutex_destroy(cb)
 209#  define cbuf_mutex_is_locked(cb) (1)
 210
 211#endif /* !WITH_PTHREADS */
 212
 213
 214/***************
 215 *  Functions  *
 216 ***************/
 217
 218cbuf_t
 219cbuf_create (int minsize, int maxsize)
 220{
 221    cbuf_t cb;
 222
 223    if (minsize <= 0) {
 224        errno = EINVAL;
 225        return(NULL);
 226    }
 227    if (!(cb = malloc(sizeof(struct cbuf)))) {
 228        errno = ENOMEM;
 229        return(lsd_nomem_error(__FILE__, __LINE__, "cbuf struct"));
 230    }
 231    /*  Circular buffer is empty when (i_in == i_out),
 232     *    so reserve 1 byte for this sentinel.
 233     */
 234    cb->alloc = minsize + 1;
 235#ifndef NDEBUG
 236    /*  Reserve space for the magic cookies used to protect the
 237     *    cbuf data[] array from underflow and overflow.
 238     */
 239    cb->alloc += 2 * CBUF_MAGIC_LEN;
 240#endif /* !NDEBUG */
 241
 242    if (!(cb->data = malloc(cb->alloc))) {
 243        free(cb);
 244        errno = ENOMEM;
 245        return(lsd_nomem_error(__FILE__, __LINE__, "cbuf data"));
 246    }
 247    cbuf_mutex_init(cb);
 248    cb->minsize = minsize;
 249    cb->maxsize = (maxsize > minsize) ? maxsize : minsize;
 250    cb->size = minsize;
 251    cb->used = 0;
 252    cb->overwrite = CBUF_WRAP_MANY;
 253    cb->got_wrap = 0;
 254    cb->i_in = cb->i_out = cb->i_rep = 0;
 255
 256#ifndef NDEBUG
 257    /*  C is for cookie, that's good enough for me, yeah!
 258     *  The magic cookies are only defined during DEBUG code.
 259     *  The first "magic" cookie is at the top of the structure.
 260     *  Magic cookies are also placed at the top & bottom of the
 261     *  cbuf data[] array to catch buffer underflow & overflow errors.
 262     */
 263    cb->data += CBUF_MAGIC_LEN;         /* jump forward past underflow magic */
 264    cb->magic = CBUF_MAGIC;
 265    /*
 266     *  Must use memcpy since overflow cookie may not be word-aligned.
 267     */
 268    memcpy(cb->data - CBUF_MAGIC_LEN, (void *) &cb->magic, CBUF_MAGIC_LEN);
 269    memcpy(cb->data + cb->size + 1, (void *) &cb->magic, CBUF_MAGIC_LEN);
 270
 271    cbuf_mutex_lock(cb);
 272    assert(cbuf_is_valid(cb));
 273    cbuf_mutex_unlock(cb);
 274#endif /* !NDEBUG */
 275
 276    return(cb);
 277}
 278
 279
 280void
 281cbuf_destroy (cbuf_t cb)
 282{
 283    assert(cb != NULL);
 284    cbuf_mutex_lock(cb);
 285    assert(cbuf_is_valid(cb));
 286
 287#ifndef NDEBUG
 288    /*  The moon sometimes looks like a C, but you can't eat that.
 289     *  Munch the magic cookies before freeing memory.
 290     */
 291    cb->magic = ~CBUF_MAGIC;            /* the anti-cookie! */
 292    memcpy(cb->data - CBUF_MAGIC_LEN, (void *) &cb->magic, CBUF_MAGIC_LEN);
 293    memcpy(cb->data + cb->size + 1, (void *) &cb->magic, CBUF_MAGIC_LEN);
 294    cb->data -= CBUF_MAGIC_LEN;         /* jump back to what malloc returned */
 295#endif /* !NDEBUG */
 296
 297    free(cb->data);
 298    cbuf_mutex_unlock(cb);
 299    cbuf_mutex_destroy(cb);
 300    free(cb);
 301    return;
 302}
 303
 304
 305void
 306cbuf_flush (cbuf_t cb)
 307{
 308    assert(cb != NULL);
 309    cbuf_mutex_lock(cb);
 310    assert(cbuf_is_valid(cb));
 311    /*
 312     *  FIXME: Shrink buffer back to minimum size.
 313     */
 314    cb->used = 0;
 315    cb->got_wrap = 0;
 316    cb->i_in = cb->i_out = cb->i_rep = 0;
 317    assert(cbuf_is_valid(cb));
 318    cbuf_mutex_unlock(cb);
 319    return;
 320}
 321
 322
 323int
 324cbuf_size (cbuf_t cb)
 325{
 326    int size;
 327
 328    assert(cb != NULL);
 329    cbuf_mutex_lock(cb);
 330    assert(cbuf_is_valid(cb));
 331    size = cb->size;
 332    cbuf_mutex_unlock(cb);
 333    return(size);
 334}
 335
 336
 337int
 338cbuf_free (cbuf_t cb)
 339{
 340    int nfree;
 341
 342    assert(cb != NULL);
 343    cbuf_mutex_lock(cb);
 344    assert(cbuf_is_valid(cb));
 345    nfree = cb->size - cb->used;
 346    cbuf_mutex_unlock(cb);
 347    return(nfree);
 348}
 349
 350
 351int
 352cbuf_used (cbuf_t cb)
 353{
 354    int used;
 355
 356    assert(cb != NULL);
 357    cbuf_mutex_lock(cb);
 358    assert(cbuf_is_valid(cb));
 359    used = cb->used;
 360    cbuf_mutex_unlock(cb);
 361    return(used);
 362}
 363
 364
 365int
 366cbuf_lines_used (cbuf_t cb)
 367{
 368    int lines = -1;
 369
 370    assert(cb != NULL);
 371    cbuf_mutex_lock(cb);
 372    assert(cbuf_is_valid(cb));
 373    cbuf_find_unread_line(cb, cb->size, &lines);
 374    cbuf_mutex_unlock(cb);
 375    return(lines);
 376}
 377
 378
 379int
 380cbuf_reused (cbuf_t cb)
 381{
 382    int reused;
 383
 384    assert(cb != NULL);
 385    cbuf_mutex_lock(cb);
 386    assert(cbuf_is_valid(cb));
 387    reused = (cb->i_out - cb->i_rep + (cb->size + 1)) % (cb->size + 1);
 388    cbuf_mutex_unlock(cb);
 389    return(reused);
 390}
 391
 392
 393int
 394cbuf_lines_reused (cbuf_t cb)
 395{
 396    int lines = -1;
 397
 398    assert(cb != NULL);
 399    cbuf_mutex_lock(cb);
 400    assert(cbuf_is_valid(cb));
 401    cbuf_find_replay_line(cb, cb->size, &lines, NULL);
 402    cbuf_mutex_unlock(cb);
 403    return(lines);
 404}
 405
 406
 407int
 408cbuf_is_empty (cbuf_t cb)
 409{
 410    int used;
 411
 412    assert(cb != NULL);
 413    cbuf_mutex_lock(cb);
 414    assert(cbuf_is_valid(cb));
 415    used = cb->used;
 416    cbuf_mutex_unlock(cb);
 417    return(used == 0);
 418}
 419
 420
 421int
 422cbuf_opt_get (cbuf_t cb, cbuf_opt_t name, int *value)
 423{
 424    int rc = 0;
 425
 426    assert(cb != NULL);
 427
 428    if (value == NULL) {
 429        errno = EINVAL;
 430        return(-1);
 431    }
 432    cbuf_mutex_lock(cb);
 433    assert(cbuf_is_valid(cb));
 434    if (name == CBUF_OPT_OVERWRITE) {
 435        *value = cb->overwrite;
 436    }
 437    else {
 438        errno = EINVAL;
 439        rc = -1;
 440    }
 441    cbuf_mutex_unlock(cb);
 442    return(rc);
 443}
 444
 445
 446int
 447cbuf_opt_set (cbuf_t cb, cbuf_opt_t name, int value)
 448{
 449    int rc = 0;
 450
 451    assert(cb != NULL);
 452
 453    cbuf_mutex_lock(cb);
 454    assert(cbuf_is_valid(cb));
 455    if (name == CBUF_OPT_OVERWRITE) {
 456        if  (  (value == CBUF_NO_DROP)
 457            || (value == CBUF_WRAP_ONCE)
 458            || (value == CBUF_WRAP_MANY) ) {
 459            cb->overwrite = value;
 460        }
 461        else {
 462            errno = EINVAL;
 463            rc = -1;
 464        }
 465    }
 466    else {
 467        errno = EINVAL;
 468        rc = -1;
 469    }
 470    assert(cbuf_is_valid(cb));
 471    cbuf_mutex_unlock(cb);
 472    return(rc);
 473}
 474
 475
 476int
 477cbuf_drop (cbuf_t src, int len)
 478{
 479    assert(src != NULL);
 480
 481    if (len < -1) {
 482        errno = EINVAL;
 483        return(-1);
 484    }
 485    if (len == 0) {
 486        return(0);
 487    }
 488    cbuf_mutex_lock(src);
 489    assert(cbuf_is_valid(src));
 490
 491    if (len == -1) {
 492        len = src->used;
 493    }
 494    else {
 495        len = MIN(len, src->used);
 496    }
 497    if (len > 0) {
 498        cbuf_dropper(src, len);
 499    }
 500    assert(cbuf_is_valid(src));
 501    cbuf_mutex_unlock(src);
 502    return(len);
 503}
 504
 505
 506int
 507cbuf_peek (cbuf_t src, void *dstbuf, int len)
 508{
 509    int n;
 510
 511    assert(src != NULL);
 512
 513    if ((dstbuf == NULL) || (len < 0)) {
 514        errno = EINVAL;
 515        return(-1);
 516    }
 517    if (len == 0) {
 518        return(0);
 519    }
 520    cbuf_mutex_lock(src);
 521    assert(cbuf_is_valid(src));
 522    n = cbuf_reader(src, len, (cbuf_iof) cbuf_put_mem, &dstbuf);
 523    assert(cbuf_is_valid(src));
 524    cbuf_mutex_unlock(src);
 525    return(n);
 526}
 527
 528
 529int
 530cbuf_read (cbuf_t src, void *dstbuf, int len)
 531{
 532    int n;
 533
 534    assert(src != NULL);
 535
 536    if ((dstbuf == NULL) || (len < 0)) {
 537        errno = EINVAL;
 538        return(-1);
 539    }
 540    if (len == 0) {
 541        return(0);
 542    }
 543    cbuf_mutex_lock(src);
 544    assert(cbuf_is_valid(src));
 545    n = cbuf_reader(src, len, (cbuf_iof) cbuf_put_mem, &dstbuf);
 546    if (n > 0) {
 547        cbuf_dropper(src, n);
 548    }
 549    assert(cbuf_is_valid(src));
 550    cbuf_mutex_unlock(src);
 551    return(n);
 552}
 553
 554
 555int
 556cbuf_replay (cbuf_t src, void *dstbuf, int len)
 557{
 558    int n;
 559
 560    assert(src != NULL);
 561
 562    if ((dstbuf == NULL) || (len < 0)) {
 563        errno = EINVAL;
 564        return(-1);
 565    }
 566    if (len == 0) {
 567        return(0);
 568    }
 569    cbuf_mutex_lock(src);
 570    assert(cbuf_is_valid(src));
 571    n = cbuf_replayer(src, len, (cbuf_iof) cbuf_put_mem, &dstbuf);
 572    assert(cbuf_is_valid(src));
 573    cbuf_mutex_unlock(src);
 574    return(n);
 575}
 576
 577
 578int
 579cbuf_rewind (cbuf_t src, int len)
 580{
 581    int reused;
 582
 583    assert(src != NULL);
 584
 585    if (len < -1) {
 586        errno = EINVAL;
 587        return(-1);
 588    }
 589    if (len == 0) {
 590        return(0);
 591    }
 592    cbuf_mutex_lock(src);
 593    assert(cbuf_is_valid(src));
 594
 595    reused = (src->i_out - src->i_rep + (src->size + 1)) % (src->size + 1);
 596    if (len == -1) {
 597        len = reused;
 598    }
 599    else {
 600        len = MIN(len, reused);
 601    }
 602    if (len > 0) {
 603        src->used += len;
 604        src->i_out = (src->i_out - len + (src->size + 1)) % (src->size + 1);
 605    }
 606    assert(cbuf_is_valid(src));
 607    cbuf_mutex_unlock(src);
 608    return(len);
 609}
 610
 611
 612int
 613cbuf_write (cbuf_t dst, void *srcbuf, int len, int *ndropped)
 614{
 615    int n;
 616
 617    assert(dst != NULL);
 618
 619    if (ndropped) {
 620        *ndropped = 0;
 621    }
 622    if ((srcbuf == NULL) || (len < 0)) {
 623        errno = EINVAL;
 624        return(-1);
 625    }
 626    if (len == 0) {
 627        return(0);
 628    }
 629    cbuf_mutex_lock(dst);
 630    assert(cbuf_is_valid(dst));
 631    n = cbuf_writer(dst, len, (cbuf_iof) cbuf_get_mem, &srcbuf, ndropped);
 632    assert(cbuf_is_valid(dst));
 633    cbuf_mutex_unlock(dst);
 634    return(n);
 635}
 636
 637
 638int
 639cbuf_drop_line (cbuf_t src, int len, int lines)
 640{
 641    int n;
 642
 643    assert(src != NULL);
 644
 645    if ((len < 0) || (lines < -1)) {
 646        errno = EINVAL;
 647        return(-1);
 648    }
 649    if (lines == 0) {
 650        return(0);
 651    }
 652    cbuf_mutex_lock(src);
 653    assert(cbuf_is_valid(src));
 654
 655    n = cbuf_find_unread_line(src, len, &lines);
 656    if (n > 0) {
 657        cbuf_dropper(src, n);
 658    }
 659    assert(cbuf_is_valid(src));
 660    cbuf_mutex_unlock(src);
 661    return(n);
 662}
 663
 664
 665int
 666cbuf_peek_line (cbuf_t src, char *dstbuf, int len, int lines)
 667{
 668    int n, m, l;
 669    char *pdst;
 670
 671    assert(src != NULL);
 672
 673    if ((dstbuf == NULL) || (len < 0) || (lines < -1)) {
 674        errno = EINVAL;
 675        return(-1);
 676    }
 677    if (lines == 0) {
 678        return(0);
 679    }
 680    cbuf_mutex_lock(src);
 681    assert(cbuf_is_valid(src));
 682    n = cbuf_find_unread_line(src, len - 1, &lines);
 683    if (n > 0) {
 684        if (len > 0) {
 685            m = MIN(n, len - 1);
 686            if (m > 0) {
 687                pdst = dstbuf;
 688                l = cbuf_reader(src, m, (cbuf_iof) cbuf_put_mem, &pdst);
 689                assert(l == m);
 690            }
 691            assert(m < len);
 692            dstbuf[m] = '\0';
 693        }
 694    }
 695    assert(cbuf_is_valid(src));
 696    cbuf_mutex_unlock(src);
 697    return(n);
 698}
 699
 700
 701int
 702cbuf_read_line (cbuf_t src, char *dstbuf, int len, int lines)
 703{
 704    int n, m, l;
 705    char *pdst;
 706
 707    assert(src != NULL);
 708
 709    if ((dstbuf == NULL) || (len < 0) || (lines < -1)) {
 710        errno = EINVAL;
 711        return(-1);
 712    }
 713    if (lines == 0) {
 714        return(0);
 715    }
 716    cbuf_mutex_lock(src);
 717    assert(cbuf_is_valid(src));
 718    n = cbuf_find_unread_line(src, len - 1, &lines);
 719    if (n > 0) {
 720        if (len > 0) {
 721            m = MIN(n, len - 1);
 722            if (m > 0) {
 723                pdst = dstbuf;
 724                l = cbuf_reader(src, m, (cbuf_iof) cbuf_put_mem, &pdst);
 725                assert(l == m);
 726            }
 727            assert(m < len);
 728            dstbuf[m] = '\0';
 729        }
 730        cbuf_dropper(src, n);
 731    }
 732    assert(cbuf_is_valid(src));
 733    cbuf_mutex_unlock(src);
 734    return(n);
 735}
 736
 737
 738int
 739cbuf_replay_line (cbuf_t src, char *dstbuf, int len, int lines)
 740{
 741    int n, m, l;
 742    int nl;
 743    char *pdst;
 744
 745    assert(src != NULL);
 746
 747    if ((dstbuf == NULL) || (len < 0) || (lines < -1)) {
 748        errno = EINVAL;
 749        return(-1);
 750    }
 751    if (lines == 0) {
 752        return(0);
 753    }
 754    cbuf_mutex_lock(src);
 755    assert(cbuf_is_valid(src));
 756    n = cbuf_find_replay_line(src, len - 1, &lines, &nl);
 757    if (n > 0) {
 758        if (len > 0) {
 759            assert((nl == 0) || (nl == 1));
 760            m = MIN(n, len - 1 - nl);
 761            m = MAX(m, 0);
 762            if (m > 0) {
 763                pdst = dstbuf;
 764                l = cbuf_replayer(src, m, (cbuf_iof) cbuf_put_mem, &pdst);
 765                assert(l == m);
 766            }
 767            /*  Append newline if needed and space allows.
 768             */
 769            if ((nl) && (len > 1)) {
 770                dstbuf[m++] = '\n';
 771            }
 772            assert(m < len);
 773            dstbuf[m] = '\0';
 774            n += nl;
 775        }
 776    }
 777    assert(cbuf_is_valid(src));
 778    cbuf_mutex_unlock(src);
 779    return(n);
 780}
 781
 782
 783int
 784cbuf_rewind_line (cbuf_t src, int len, int lines)
 785{
 786    int n;
 787
 788    assert(src != NULL);
 789
 790    if ((len < 0) || (lines < -1)) {
 791        errno = EINVAL;
 792        return(-1);
 793    }
 794    if (lines == 0) {
 795        return(0);
 796    }
 797    cbuf_mutex_lock(src);
 798    assert(cbuf_is_valid(src));
 799
 800    n = cbuf_find_replay_line(src, len, &lines, NULL);
 801    if (n > 0) {
 802        src->used += n;
 803        src->i_out = (src->i_out - n + (src->size + 1)) % (src->size + 1);
 804    }
 805    assert(cbuf_is_valid(src));
 806    cbuf_mutex_unlock(src);
 807    return(n);
 808}
 809
 810
 811int
 812cbuf_write_line (cbuf_t dst, char *srcbuf, int *ndropped)
 813{
 814    int len;
 815    int nfree, ncopy, n;
 816    int ndrop = 0, d;
 817    char *psrc = srcbuf;
 818    char *newline = "\n";
 819
 820    assert(dst != NULL);
 821
 822    if (ndropped) {
 823        *ndropped = 0;
 824    }
 825    if (srcbuf == NULL) {
 826        errno = EINVAL;
 827        return(-1);
 828    }
 829    /*  Compute number of bytes to effectively copy to dst cbuf.
 830     *  Reserve space for the trailing newline if needed.
 831     */
 832    len = ncopy = strlen(srcbuf);
 833    if ((len == 0) || (srcbuf[len - 1] != '\n')) {
 834        len++;
 835    }
 836    cbuf_mutex_lock(dst);
 837    assert(cbuf_is_valid(dst));
 838    /*
 839     *  Attempt to grow dst cbuf if necessary.
 840     */
 841    nfree = dst->size - dst->used;
 842    if ((len > nfree) && (dst->size < dst->maxsize)) {
 843        nfree += cbuf_grow(dst, len - nfree);
 844    }
 845    /*  Determine if src will fit (or be made to fit) in dst cbuf.
 846     */
 847    if (dst->overwrite == CBUF_NO_DROP) {
 848        if (len > dst->size - dst->used) {
 849            errno = ENOSPC;
 850            len = -1;                   /* cannot return while mutex locked */
 851        }
 852    }
 853    else if (dst->overwrite == CBUF_WRAP_ONCE) {
 854        if (len > dst->size) {
 855            errno = ENOSPC;
 856            len = -1;                   /* cannot return while mutex locked */
 857        }
 858    }
 859    if (len > 0) {
 860        /*
 861         *  Discard data that won't fit in dst cbuf.
 862         */
 863        if (len > dst->size) {
 864            ndrop += len - dst->size;
 865            ncopy -= ndrop;
 866            psrc += ndrop;
 867        }
 868        /*  Copy data from src string to dst cbuf.
 869         */
 870        if (ncopy > 0) {
 871            n = cbuf_writer(dst, ncopy, (cbuf_iof) cbuf_get_mem, &psrc, &d);
 872            assert(n == ncopy);
 873            ndrop += d;
 874        }
 875        /*  Append newline if needed.
 876         */
 877        if (srcbuf[len - 1] != '\n') {
 878            n = cbuf_writer(dst, 1, (cbuf_iof) cbuf_get_mem, &newline, &d);
 879            assert(n == 1);
 880            ndrop += d;
 881        }
 882    }
 883    assert(cbuf_is_valid(dst));
 884    cbuf_mutex_unlock(dst);
 885    if (ndropped) {
 886        *ndropped = ndrop;
 887    }
 888    return(len);
 889}
 890
 891
 892int
 893cbuf_peek_to_fd (cbuf_t src, int dstfd, int len)
 894{
 895    int n = 0;
 896
 897    assert(src != NULL);
 898
 899    if ((dstfd < 0) || (len < -1)) {
 900        errno = EINVAL;
 901        return(-1);
 902    }
 903    cbuf_mutex_lock(src);
 904    assert(cbuf_is_valid(src));
 905    if (len == -1) {
 906        len = src->used;
 907    }
 908    if (len > 0) {
 909        n = cbuf_reader(src, len, (cbuf_iof) cbuf_put_fd, &dstfd);
 910    }
 911    assert(cbuf_is_valid(src));
 912    cbuf_mutex_unlock(src);
 913    return(n);
 914}
 915
 916
 917int
 918cbuf_read_to_fd (cbuf_t src, int dstfd, int len)
 919{
 920    int n = 0;
 921
 922    assert(src != NULL);
 923
 924    if ((dstfd < 0) || (len < -1)) {
 925        errno = EINVAL;
 926        return(-1);
 927    }
 928    cbuf_mutex_lock(src);
 929    assert(cbuf_is_valid(src));
 930    if (len == -1) {
 931        len = src->used;
 932    }
 933    if (len > 0) {
 934        n = cbuf_reader(src, len, (cbuf_iof) cbuf_put_fd, &dstfd);
 935        if (n > 0) {
 936            cbuf_dropper(src, n);
 937        }
 938    }
 939    assert(cbuf_is_valid(src));
 940    cbuf_mutex_unlock(src);
 941    return(n);
 942}
 943
 944
 945int
 946cbuf_replay_to_fd (cbuf_t src, int dstfd, int len)
 947{
 948    int n = 0;
 949
 950    assert(src != NULL);
 951
 952    if ((dstfd < 0) || (len < -1)) {
 953        errno = EINVAL;
 954        return(-1);
 955    }
 956    cbuf_mutex_lock(src);
 957    assert(cbuf_is_valid(src));
 958    if (len == -1) {
 959        len = src->size - src->used;
 960    }
 961    if (len > 0) {
 962        n = cbuf_replayer(src, len, (cbuf_iof) cbuf_put_fd, &dstfd);
 963    }
 964    assert(cbuf_is_valid(src));
 965    cbuf_mutex_unlock(src);
 966    return(n);
 967}
 968
 969
 970int
 971cbuf_write_from_fd (cbuf_t dst, int srcfd, int len, int *ndropped)
 972{
 973    int n = 0;
 974
 975    assert(dst != NULL);
 976
 977    if (ndropped) {
 978        *ndropped = 0;
 979    }
 980    if ((srcfd < 0) || (len < -1)) {
 981        errno = EINVAL;
 982        return(-1);
 983    }
 984    cbuf_mutex_lock(dst);
 985    assert(cbuf_is_valid(dst));
 986    if (len == -1) {
 987        /*
 988         *  Try to use all of the free buffer space available for writing.
 989         *    If it is all in use, try to grab another chunk and limit the
 990         *    amount of data being overwritten.
 991         */
 992        len = dst->size - dst->used;
 993        if (len == 0) {
 994            len = MIN(dst->size, CBUF_CHUNK);
 995        }
 996    }
 997    if (len > 0) {
 998        n = cbuf_writer(dst, len, (cbuf_iof) cbuf_get_fd, &srcfd, ndropped);
 999    }
1000    assert(cbuf_is_valid(dst));
1001    cbuf_mutex_unlock(dst);
1002    return(n);
1003}
1004
1005
1006int
1007cbuf_copy (cbuf_t src, cbuf_t dst, int len, int *ndropped)
1008{
1009    int n = 0;
1010
1011    assert(src != NULL);
1012    assert(dst != NULL);
1013
1014    if (ndropped) {
1015        *ndropped = 0;
1016    }
1017    if (src == dst) {
1018        errno = EINVAL;
1019        return(-1);
1020    }
1021    if (len < -1) {
1022        errno = EINVAL;
1023        return(-1);
1024    }
1025    if (len == 0) {
1026        return(0);
1027    }
1028    /*  Lock cbufs in order of lowest memory address to prevent deadlock.
1029     */
1030    if (src < dst) {
1031        cbuf_mutex_lock(src);
1032        cbuf_mutex_lock(dst);
1033    }
1034    else {
1035        cbuf_mutex_lock(dst);
1036        cbuf_mutex_lock(src);
1037    }
1038    assert(cbuf_is_valid(src));
1039    assert(cbuf_is_valid(dst));
1040
1041    if (len == -1) {
1042        len = src->used;
1043    }
1044    if (len > 0) {
1045        n = cbuf_copier(src, dst, len, ndropped);
1046    }
1047    assert(cbuf_is_valid(src));
1048    assert(cbuf_is_valid(dst));
1049    cbuf_mutex_unlock(src);
1050    cbuf_mutex_unlock(dst);
1051    return(n);
1052}
1053
1054
1055int
1056cbuf_move (cbuf_t src, cbuf_t dst, int len, int *ndropped)
1057{
1058    int n = 0;
1059
1060    assert(src != NULL);
1061    assert(dst != NULL);
1062
1063    if (ndropped) {
1064        *ndropped = 0;
1065    }
1066    if (src == dst) {
1067        errno = EINVAL;
1068        return(-1);
1069    }
1070    if (len < -1) {
1071        errno = EINVAL;
1072        return(-1);
1073    }
1074    if (len == 0) {
1075        return(0);
1076    }
1077    /*  Lock cbufs in order of lowest memory address to prevent deadlock.
1078     */
1079    if (src < dst) {
1080        cbuf_mutex_lock(src);
1081        cbuf_mutex_lock(dst);
1082    }
1083    else {
1084        cbuf_mutex_lock(dst);
1085        cbuf_mutex_lock(src);
1086    }
1087    assert(cbuf_is_valid(src));
1088    assert(cbuf_is_valid(dst));
1089
1090    if (len == -1) {
1091        len = src->used;
1092    }
1093    if (len > 0) {
1094        n = cbuf_copier(src, dst, len, ndropped);
1095        if (n > 0) {
1096            cbuf_dropper(src, n);
1097        }
1098    }
1099    assert(cbuf_is_valid(src));
1100    assert(cbuf_is_valid(dst));
1101    cbuf_mutex_unlock(src);
1102    cbuf_mutex_unlock(dst);
1103    return(n);
1104}
1105
1106
1107static int
1108cbuf_find_replay_line (cbuf_t cb, int chars, int *nlines, int *nl)
1109{
1110/*  Finds the specified number of lines from the replay region of the buffer.
1111 *  If ([nlines] > 0), returns the number of bytes comprising the line count,
1112 *    or 0 if this number of lines is not available (ie, all or none).
1113 *  If ([nlines] == -1), returns the number of bytes comprising the maximum
1114 *    line count bounded by the number of characters specified by [chars].
1115 *  Only complete lines (ie, those terminated by a newline) are counted,
1116 *    with once exception: the most recent line of replay data is treated
1117 *    as a complete line regardless of the presence of a terminating newline.
1118 *  Sets the value-result parameter [nlines] to the number of lines found.
1119 *  Sets [nl] to '1' if a newline is required to terminate the replay data.
1120 */
1121    int i, n, m, l;
1122    int lines;
1123
1124    assert(cb != NULL);
1125    assert(nlines != NULL);
1126    assert(*nlines >= -1);
1127    assert(cbuf_mutex_is_locked(cb));
1128
1129    n = m = l = 0;
1130    lines = *nlines;
1131    *nlines = 0;
1132
1133    if (nl) {
1134        *nl = 0;                        /* init in case of early return */
1135    }
1136    if ((lines == 0) || ((lines <= -1) && (chars <= 0))) {
1137        return(0);
1138    }
1139    if (cb->i_out == cb->i_rep) {
1140        return(0);                      /* no replay data available */
1141    }
1142    if (lines > 0) {
1143        chars = -1;                     /* chars parm not used if lines > 0 */
1144    }
1145    else {
1146        ++chars;                        /* incr to allow for preceding '\n' */
1147    }
1148    /*  Since the most recent line of replay data is considered implicitly
1149     *    terminated, decrement the char count to account for the newline
1150     *    if one is not present, or increment the line count if one is.
1151     *  Note: cb->data[(O - 1 + (S+1)) % (S+1)] is the last replayable char.
1152     */
1153    if (cb->data[(cb->i_out + cb->size) % (cb->size + 1)] != '\n') {
1154        if (nl) {
1155            *nl = 1;
1156        }
1157        --chars;
1158    }
1159    else {
1160        if (lines > 0) {
1161            ++lines;
1162        }
1163        --l;
1164    }
1165    i = cb->i_out;
1166    while (i != cb->i_rep) {
1167        i = (i + cb->size) % (cb->size + 1); /* (i - 1 + (S+1)) % (S+1) */
1168        ++n;
1169        if (chars > 0) {
1170            --chars;
1171        }
1172        /*  Complete lines are identified by a preceding newline.
1173         */
1174        if (cb->data[i] == '\n') {
1175            if (lines > 0) {
1176                --lines;
1177            }
1178            m = n - 1;                  /* do not include preceding '\n' */
1179            ++l;
1180        }
1181        if ((chars == 0) || (lines == 0)) {
1182            break;
1183        }
1184    }
1185    /*  But the first line written in does not need a preceding newline.
1186     */
1187    if ((!cb->got_wrap) && ((chars > 0) || (lines > 0))) {
1188        if (lines > 0) {
1189            --lines;
1190        }
1191        m = n;
1192        ++l;
1193    }
1194    if (lines > 0) {
1195        return(0);                      /* all or none, and not enough found */
1196    }
1197    *nlines = l;
1198    return(m);
1199}
1200
1201
1202static int
1203cbuf_find_unread_line (cbuf_t cb, int chars, int *nlines)
1204{
1205/*  Finds the specified number of lines from the unread region of the buffer.
1206 *  If ([nlines] > 0), returns the number of bytes comprising the line count,
1207 *    or 0 if this number of lines is not available (ie, all or none).
1208 *  If ([nlines] == -1), returns the number of bytes comprising the maximum
1209 *    line count bounded by the number of characters specified by [chars].
1210 *  Only complete lines (ie, those terminated by a newline) are counted.
1211 *  Sets the value-result parameter [nlines] to the number of lines found.
1212 */
1213    int i, n, m, l;
1214    int lines;
1215
1216    assert(cb != NULL);
1217    assert(nlines != NULL);
1218    assert(*nlines >= -1);
1219    assert(cbuf_mutex_is_locked(cb));
1220
1221    n = m = l = 0;
1222    lines = *nlines;
1223    *nlines = 0;
1224
1225    if ((lines == 0) || ((lines <= -1) && (chars <= 0))) {
1226        return(0);
1227    }
1228    if (cb->used == 0) {
1229        return(0);                      /* no unread data available */
1230    }
1231    if (lines > 0) {
1232        chars = -1;                     /* chars parm not used if lines > 0 */
1233    }
1234    i = cb->i_out;
1235    while (i != cb->i_in) {
1236        ++n;
1237        if (chars > 0) {
1238            --chars;
1239        }
1240        if (cb->data[i] == '\n') {
1241            if (lines > 0) {
1242                --lines;
1243            }
1244            m = n;
1245            ++l;
1246        }
1247        if ((chars == 0) || (lines == 0)) {
1248            break;
1249        }
1250        i = (i + 1) % (cb->size + 1);
1251    }
1252    if (lines > 0) {
1253        return(0);                      /* all or none, and not enough found */
1254    }
1255    *nlines = l;
1256    return(m);
1257}
1258
1259
1260static int
1261cbuf_get_fd (void *dstbuf, int *psrcfd, int len)
1262{
1263/*  Copies data from the file referenced by the file descriptor
1264 *    pointed at by [psrcfd] into cbuf's [dstbuf].
1265 *  Returns the number of bytes read from the fd, 0 on EOF, or -1 on error.
1266 */
1267    int n;
1268
1269    assert(dstbuf != NULL);
1270    assert(psrcfd != NULL);
1271    assert(*psrcfd >= 0);
1272    assert(len > 0);
1273
1274    do {
1275        n = read(*psrcfd, dstbuf, len);
1276    } while ((n < 0) && (errno == EINTR));
1277    return(n);
1278}
1279
1280
1281static int
1282cbuf_get_mem (void *dstbuf, unsigned char **psrcbuf, int len)
1283{
1284/*  Copies data from the buffer pointed at by [psrcbuf] into cbuf's [dstbuf].
1285 *  Returns the number of bytes copied.
1286 */
1287    assert(dstbuf != NULL);
1288    assert(psrcbuf != NULL);
1289    assert(*psrcbuf != NULL);
1290    assert(len > 0);
1291
1292    memcpy(dstbuf, *psrcbuf, len);
1293    *psrcbuf += len;
1294    return(len);
1295}
1296
1297
1298static int
1299cbuf_put_fd (void *srcbuf, int *pdstfd, int len)
1300{
1301/*  Copies data from cbuf's [srcbuf] into the file referenced
1302 *    by the file descriptor pointed at by [pdstfd].
1303 *  Returns the number of bytes written to the fd, or -1 on error.
1304 */
1305    int n;
1306
1307    assert(srcbuf != NULL);
1308    assert(pdstfd != NULL);
1309    assert(*pdstfd >= 0);
1310    assert(len > 0);
1311
1312    do {
1313        n = write(*pdstfd, srcbuf, len);
1314    } while ((n < 0) && (errno == EINTR));
1315    return(n);
1316}
1317
1318
1319static int
1320cbuf_put_mem (void *srcbuf, unsigned char **pdstbuf, int len)
1321{
1322/*  Copies data from cbuf's [srcbuf] into the buffer pointed at by [pdstbuf].
1323 *  Returns the number of bytes copied.
1324 */
1325    assert(srcbuf != NULL);
1326    assert(pdstbuf != NULL);
1327    assert(*pdstbuf != NULL);
1328    assert(len > 0);
1329
1330    memcpy(*pdstbuf, srcbuf, len);
1331    *pdstbuf += len;
1332    return(len);
1333}
1334
1335
1336static int
1337cbuf_copier (cbuf_t src, cbuf_t dst, int len, int *ndropped)
1338{
1339/*  Copies up to [len] bytes from the [src] cbuf into the [dst] cbuf.
1340 *  Returns the number of bytes copied, or -1 on error (with errno set).
1341 *  Sets [ndropped] (if not NULL) to the number of [dst] bytes overwritten.
1342 */
1343    int ncopy, nfree, nleft, nrepl, n;
1344    int i_src, i_dst;
1345
1346    assert(src != NULL);
1347    assert(dst != NULL);
1348    assert(len > 0);
1349    assert(cbuf_mutex_is_locked(src));
1350    assert(cbuf_mutex_is_locked(dst));
1351
1352    /*  Bound len by the number of bytes available.
1353     */
1354    len = MIN(len, src->used);
1355    if (len == 0) {
1356        return(0);
1357    }
1358    /*  Attempt to grow dst cbuf if necessary.
1359     */
1360    nfree = dst->size - dst->used;
1361    if ((len > nfree) && (dst->size < dst->maxsize)) {
1362        nfree += cbuf_grow(dst, len - nfree);
1363    }
1364    /*  Compute number of bytes to effectively copy to dst cbuf.
1365     */
1366    if (dst->overwrite == CBUF_NO_DROP) {
1367        len = MIN(len, dst->size - dst->used);
1368        if (len == 0) {
1369            errno = ENOSPC;
1370            return(-1);
1371        }
1372    }
1373    else if (dst->overwrite == CBUF_WRAP_ONCE) {
1374        len = MIN(len, dst->size);
1375    }
1376    /*  Compute number of bytes that will be overwritten in dst cbuf.
1377     */
1378    if (ndropped) {
1379        *ndropped = MAX(0, len - dst->size + dst->used);
1380    }
1381    /*  Compute number of bytes to physically copy to dst cbuf.  This prevents
1382     *    copying data that will overwritten if the cbuf wraps multiple times.
1383     */
1384    ncopy = len;
1385    i_src = src->i_out;
1386    i_dst = dst->i_in;
1387    if (ncopy > dst->size) {
1388        n = ncopy - dst->size;
1389        i_src = (i_src + n) % (src->size + 1);
1390        ncopy -= n;
1391    }
1392    /*  Copy data from src cbuf to dst cbuf.
1393     */
1394    nleft = ncopy;
1395    while (nleft > 0) {
1396        n = MIN(((src->size + 1) - i_src), ((dst->size + 1) - i_dst));
1397        n = MIN(n, nleft);
1398        memcpy(&dst->data[i_dst], &src->data[i_src], n);
1399        i_src = (i_src + n) % (src->size + 1);
1400        i_dst = (i_dst + n) % (dst->size + 1);
1401        nleft -= n;
1402    }
1403    /*  Update dst cbuf metadata.
1404     */
1405    if (ncopy > 0) {
1406        nrepl = (dst->i_out - dst->i_rep + (dst->size + 1)) % (dst->size + 1);
1407        dst->used = MIN(dst->used + ncopy, dst->size);
1408        assert(i_dst == (dst->i_in + ncopy) % (dst->size + 1));
1409        dst->i_in = i_dst;
1410        if (ncopy > nfree - nrepl) {
1411            dst->got_wrap = 1;
1412            dst->i_rep = (dst->i_in + 1) % (dst->size + 1);
1413        }
1414        if (ncopy > nfree) {
1415            dst->i_out = dst->i_rep;
1416        }
1417    }
1418    return(len);
1419}
1420
1421
1422static int
1423cbuf_dropper (cbuf_t cb, int len)
1424{
1425/*  Discards exactly [len] bytes of unread data from [cb].
1426 *  Returns the number of bytes dropped.
1427 */
1428    assert(cb != NULL);
1429    assert(len > 0);
1430    assert(len <= cb->used);
1431    assert(cbuf_mutex_is_locked(cb));
1432
1433    cb->used -= len;
1434    cb->i_out = (cb->i_out + len) % (cb->size + 1);
1435
1436    /*  Attempt to shrink cbuf if possible.
1437     */
1438    if ((cb->size - cb->used > CBUF_CHUNK) && (cb->size > cb->minsize)) {
1439        cbuf_shrink(cb);
1440    }
1441    /*  Don't call me clumsy, don't call me a fool.
1442     *  When things fall down on me, I'm following the rule.
1443     */
1444    return(len);
1445}
1446
1447
1448static int
1449cbuf_reader (cbuf_t src, int len, cbuf_iof putf, void *dst)
1450{
1451/*  Reads up to [len] bytes from [src] into the object pointed at by [dst].
1452 *    The I/O function [putf] specifies how data is written into [dst].
1453 *  Returns the number of bytes read, or -1 on error (with errno set).
1454 *  Note that [dst] is a value-result parameter and will be "moved forward"
1455 *    by the number of bytes written into it.
1456 */
1457    int nleft, n, m;
1458    int i_src;
1459
1460    assert(src != NULL);
1461    assert(len > 0);
1462    assert(putf != NULL);
1463    assert(dst != NULL);
1464    assert(cbuf_mutex_is_locked(src));
1465
1466    /*  Bound len by the number of bytes available.
1467     */
1468    len = MIN(len, src->used);
1469    if (len == 0) {
1470        return(0);
1471    }
1472    /*  Copy data from src cbuf to dst obj.  Do the cbuf hokey-pokey and
1473     *    wrap-around the buffer at most once.  Break out if putf() returns
1474     *    either an ERR or a short count.
1475     */
1476    i_src = src->i_out;
1477    nleft = len;
1478    m = 0;
1479    while (nleft > 0) {
1480        n = MIN(nleft, (src->size + 1) - i_src);
1481        m = putf(&src->data[i_src], dst, n);
1482        if (m > 0) {
1483            nleft -= m;
1484            i_src = (i_src + m) % (src->size + 1);
1485        }
1486        if (n != m) {
1487            break;                      /* got ERR or "short" putf() */
1488        }
1489    }
1490    /*  Compute number of bytes written to dst obj.
1491     */
1492    n = len - nleft;
1493    assert((n >= 0) && (n <= len));
1494    /*
1495     *  If no data has been written, return the ERR reported by putf().
1496     */
1497    if (n == 0) {
1498        return(m);
1499    }
1500    return(n);
1501}
1502
1503
1504static int
1505cbuf_replayer (cbuf_t src, int len, cbuf_iof putf, void *dst)
1506{
1507/*  Replays up to [len] bytes from [src] into the object pointed at by [dst].
1508 *    The I/O function [putf] specifies how data is written into [dst].
1509 *  Returns the number of bytes replayed, or -1 on error (with errno set).
1510 *  Note that [dst] is a value-result parameter and will be "moved forward"
1511 *    by the number of bytes written into it.
1512 */
1513    int nleft, n, m;
1514    int i_src;
1515
1516    assert(src != NULL);
1517    assert(len > 0);
1518    assert(putf != NULL);
1519    assert(dst != NULL);
1520    assert(cbuf_mutex_is_locked(src));
1521
1522    /*  Bound len by the number of bytes available.
1523     */
1524    n = (src->i_out - src->i_rep + (src->size + 1)) % (src->size + 1);
1525    len = MIN(len, n);
1526    if (len == 0) {
1527        return(0);
1528    }
1529    /*  Copy data from src cbuf to dst obj.  Do the cbuf hokey-pokey and
1530     *    wrap-around the buffer at most once.  Break out if putf() returns
1531     *    either an ERR or a short count.
1532     */
1533    i_src = (src->i_out - len + (src->size + 1)) % (src->size + 1);
1534    nleft = len;
1535    m = 0;
1536    while (nleft > 0) {
1537        n = MIN(nleft, (src->size + 1) - i_src);
1538        m = putf(&src->data[i_src], dst, n);
1539        if (m > 0) {
1540            nleft -= m;
1541            i_src = (i_src + m) % (src->size + 1);
1542        }
1543        if (n != m) {
1544            break;                      /* got ERR or "short" putf() */
1545        }
1546    }
1547    /*  Compute number of bytes written to dst obj.
1548     */
1549    n = len - nleft;
1550    assert((n >= 0) && (n <= len));
1551    /*
1552     *  If no data has been written, return the ERR reported by putf().
1553     */
1554    if (n == 0) {
1555        return(m);
1556    }
1557    return(n);
1558}
1559
1560
1561static int
1562cbuf_writer (cbuf_t dst, int len, cbuf_iof getf, void *src, int *ndropped)
1563{
1564/*  Writes up to [len] bytes from the object pointed at by [src] into [dst].
1565 *    The I/O function [getf] specifies how data is read from [src].
1566 *  Returns the number of bytes written, or -1 on error (with errno set).
1567 *  Sets [ndropped] (if not NULL) to the number of [dst] bytes overwritten.
1568 *  Note that [src] is a value-result parameter and will be "moved forward"
1569 *    by the number of bytes read from it.
1570 */
1571    int nfree, nleft, nrepl, n, m;
1572    int i_dst;
1573
1574    assert(dst != NULL);
1575    assert(len > 0);
1576    assert(getf != NULL);
1577    assert(src != NULL);
1578    assert(cbuf_mutex_is_locked(dst));
1579
1580    /*  Attempt to grow dst cbuf if necessary.
1581     */
1582    nfree = dst->size - dst->used;
1583    if ((len > nfree) && (dst->size < dst->maxsize)) {
1584        nfree += cbuf_grow(dst, len - nfree);
1585    }
1586    /*  Compute number of bytes to write to dst cbuf.
1587     */
1588    if (dst->overwrite == CBUF_NO_DROP) {
1589        len = MIN(len, dst->size - dst->used);
1590        if (len == 0) {
1591            errno = ENOSPC;
1592            return(-1);
1593        }
1594    }
1595    else if (dst->overwrite == CBUF_WRAP_ONCE) {
1596        len = MIN(len, dst->size);
1597    }
1598    /*  Copy data from src obj to dst cbuf.  Do the cbuf hokey-pokey and
1599     *    wrap-around the buffer as needed.  Break out if getf() returns
1600     *    either an EOF/ERR or a short count.
1601     */
1602    i_dst = dst->i_in;
1603    nleft = len;
1604    m = 0;
1605    while (nleft > 0) {
1606        n = MIN(nleft, (dst->size + 1) - i_dst);
1607        m = getf(&dst->data[i_dst], src, n);
1608        if (m > 0) {
1609            nleft -= m;
1610            i_dst = (i_dst + m) % (dst->size + 1);
1611        }
1612        if (n != m) {
1613            break;                      /* got EOF/ERR or "short" getf() */
1614        }
1615    }
1616    /*  Compute number of bytes written to dst cbuf.
1617     */
1618    n = len - nleft;
1619    assert((n >= 0) && (n <= len));
1620    /*
1621     *  If no data has been written, return the EOF/ERR reported by getf().
1622     */
1623    if (n == 0) {
1624        return(m);
1625    }
1626    /*  Update dst cbuf metadata.
1627     */
1628    if (n > 0) {
1629        nrepl = (dst->i_out - dst->i_rep + (dst->size + 1)) % (dst->size + 1);
1630        dst->used = MIN(dst->used + n, dst->size);
1631        assert(i_dst == (dst->i_in + n) % (dst->size + 1));
1632        dst->i_in = i_dst;
1633        if (n > nfree - nrepl) {
1634            dst->got_wrap = 1;
1635            dst->i_rep = (dst->i_in + 1) % (dst->size + 1);
1636        }
1637        if (n > nfree) {
1638            dst->i_out = dst->i_rep;
1639        }
1640    }
1641    if (ndropped) {
1642        *ndropped = MAX(0, n - nfree);
1643    }
1644    return(n);
1645}
1646
1647
1648static int
1649cbuf_grow (cbuf_t cb, int n)
1650{
1651/*  Attempts to grow the circular buffer [cb] by at least [n] bytes.
1652 *  Returns the number of bytes by which the buffer has grown (which may be
1653 *    less-than, equal-to, or greater-than the number of bytes requested).
1654 */
1655    unsigned char *data;
1656    int size_old, size_meta;
1657    int m;
1658
1659    assert(cb != NULL);
1660    assert(n > 0);
1661    assert(cbuf_mutex_is_locked(cb));
1662
1663    if (cb->size == cb->maxsize) {
1664        return(0);
1665    }
1666    size_old = cb->size;
1667    size_meta = cb->alloc - cb->size;   /* size of sentinel & magic cookies */
1668    assert(size_meta > 0);
1669
1670    /*  Attempt to grow data buffer by multiples of the chunk-size.
1671     */
1672    m = cb->alloc + n;
1673    m = m + (CBUF_CHUNK - (m % CBUF_CHUNK));
1674    m = MIN(m, (cb->maxsize + size_meta));
1675    assert(m > cb->alloc);
1676
1677    data = cb->data;
1678#ifndef NDEBUG
1679    data -= CBUF_MAGIC_LEN;             /* jump back to what malloc returned */
1680#endif /* !NDEBUG */
1681
1682    if (!(data = realloc(data, m))) {
1683        /*
1684         *  XXX: Set flag or somesuch to prevent regrowing when out of memory?
1685         */
1686        return(0);                      /* unable to grow data buffer */
1687    }
1688    cb->data = data;
1689    cb->alloc = m;
1690    cb->size = m - size_meta;
1691
1692#ifndef NDEBUG
1693    /*  A round cookie with one bite out of it looks like a C.
1694     *  The underflow cookie will have been copied by realloc() if needed.
1695     *    But the overflow cookie must be rebaked.
1696     *  Must use memcpy since overflow cookie may not be word-aligned.
1697     */
1698    cb->data += CBUF_MAGIC_LEN;         /* jump forward past underflow magic */
1699    memcpy(cb->data + cb->size + 1, (void *) &cb->magic, CBUF_MAGIC_LEN);
1700#endif /* !NDEBUG */
1701
1702    /*  The memory containing replay and unread data must be contiguous modulo
1703     *    the buffer size.  Additional memory must be inserted between where
1704     *    new data is written in (i_in) and where replay data starts (i_rep).
1705     *  If replay data wraps-around the old buffer, move it to the new end
1706     *    of the buffer so it wraps-around in the same manner.
1707     */
1708    if (cb->i_rep > cb->i_in) {
1709        n = (size_old + 1) - cb->i_rep;
1710        m = (cb->size + 1) - n;
1711        memmove(cb->data + m, cb->data + cb->i_rep, n);
1712
1713        if (cb->i_out >= cb->i_rep) {
1714            cb->i_out += m - cb->i_rep;
1715        }
1716        cb->i_rep = m;
1717    }
1718    assert(cbuf_is_valid(cb));
1719    return(cb->size - size_old);
1720}
1721
1722
1723static int
1724cbuf_shrink (cbuf_t cb)
1725{
1726/*  XXX: DOCUMENT ME.
1727 */
1728    assert(cb != NULL);
1729    assert(cbuf_mutex_is_locked(cb));
1730    assert(cbuf_is_valid(cb));
1731
1732    if (cb->size == cb->minsize) {
1733        return(0);
1734    }
1735    if (cb->size - cb->used <= CBUF_CHUNK) {
1736        return(0);
1737    }
1738    /*  FIXME: NOT IMPLEMENTED.
1739     */
1740    assert(cbuf_is_valid(cb));
1741    return(0);
1742}
1743
1744
1745#ifndef NDEBUG
1746#ifdef WITH_PTHREADS
1747static int
1748cbuf_mutex_is_locked (cbuf_t cb)
1749{
1750/*  Returns true if the mutex is locked; o/w, returns false.
1751 */
1752    int rc;
1753
1754    assert(cb != NULL);
1755    rc = pthread_mutex_trylock(&cb->mutex);
1756    return(rc == EBUSY ? 1 : 0);
1757}
1758#endif /* WITH_PTHREADS */
1759#endif /* !NDEBUG */
1760
1761
1762#ifndef NDEBUG
1763static int
1764cbuf_is_valid (cbuf_t cb)
1765{
1766/*  Validates the data structure.  All invariants should be tested here.
1767 *  Returns true if everything is valid; o/w, aborts due to assertion failure.
1768 */
1769    int nfree;
1770
1771    assert(cb != NULL);
1772    assert(cbuf_mutex_is_locked(cb));
1773    assert(cb->data != NULL);
1774    assert(cb->magic == CBUF_MAGIC);
1775    /*
1776     *  Must use memcmp since overflow cookie may not be word-aligned.
1777     */
1778    assert(memcmp(cb->data - CBUF_MAGIC_LEN,
1779        (void *) &cb->magic, CBUF_MAGIC_LEN) == 0);
1780    assert(memcmp(cb->data + cb->size + 1,
1781        (void *) &cb->magic, CBUF_MAGIC_LEN) == 0);
1782
1783    assert(cb->alloc > 0);
1784    assert(cb->alloc > cb->size);
1785    assert(cb->size > 0);
1786    assert(cb->size >= cb->minsize);
1787    assert(cb->size <= cb->maxsize);
1788    assert(cb->minsize > 0);
1789    assert(cb->maxsize > 0);
1790    assert(cb->used >= 0);
1791    assert(cb->used <= cb->size);
1792    assert(cb->overwrite == CBUF_NO_DROP
1793        || cb->overwrite == CBUF_WRAP_ONCE
1794        || cb->overwrite == CBUF_WRAP_MANY);
1795    assert(cb->got_wrap || !cb->i_rep); /* i_rep = 0 if data has not wrapped */
1796    assert(cb->i_in >= 0);
1797    assert(cb->i_in <= cb->size);
1798    assert(cb->i_out >= 0);
1799    assert(cb->i_out <= cb->size);
1800    assert(cb->i_rep >= 0);
1801    assert(cb->i_rep <= cb->size);
1802
1803    if (cb->i_in >= cb->i_out) {
1804        assert((cb->i_rep > cb->i_in) || (cb->i_rep <= cb->i_out));
1805    }
1806    else /* if (cb->in < cb->i_out) */ {
1807        assert((cb->i_rep > cb->i_in) && (cb->i_rep <= cb->i_out));
1808    }
1809    nfree = (cb->i_out - cb->i_in - 1 + (cb->size + 1)) % (cb->size + 1);
1810    assert(cb->size - cb->used == nfree);
1811
1812    return(1);
1813}
1814#endif /* !NDEBUG */