Annotation of libwww/Library/src/HTBufWrt.c, revision 2.16

2.1       frystyk     1: /*
                      2: **     BUFFERED TRANSPORT WRITER STREAM
                      3: **
                      4: **     (c) COPYRIGHT MIT 1995.
                      5: **     Please first read the full copyright statement in the file COPYRIGH.
2.16    ! frystyk     6: **     @(#) $Id: HTBufWrt.c,v 2.15 1997/12/04 16:54:47 frystyk Exp $
2.1       frystyk     7: **
                      8: **     A buffered output stream. This stream lets you write characters to a
                      9: **     stream without causing a write every time.  The data is first written
                     10: **     into a buffer. Data is written to the actual stream only when the
                     11: **     buffer is full, or when the stream is flushed.
                     12: */
                     13: 
                     14: /* Library include files */
                     15: #include "sysdep.h"
                     16: #include "WWWUtil.h"
                     17: #include "WWWCore.h"
                     18: #include "HTNetMan.h"
                     19: #include "HTWriter.h"
2.6       frystyk    20: #include "HTTimer.h"
2.1       frystyk    21: #include "HTBufWrt.h"                                   /* Implemented here */
                     22: 
                     23: struct _HTOutputStream {
                     24:     const HTOutputStreamClass *        isa;
                     25:     HTOutputStream *           target;          /* Target for outgoing data */
2.6       frystyk    26:     HTHost *                   host;
2.15      frystyk    27: 
                     28:     int                                allocated;          /* Allocated Buffer size */
                     29:     int                         growby;
2.16    ! frystyk    30:     int                                expo;
2.15      frystyk    31: 
2.1       frystyk    32:     char *                     read;                  /* Position in 'data' */
                     33:     char *                     data;                              /* buffer */
2.6       frystyk    34: 
2.7       frystyk    35:     ms_t                       lastFlushTime;  /* polar coordinates of the moon */
2.6       frystyk    36:     HTTimer *                  timer;
2.1       frystyk    37: };
                     38: 
                     39: #define PUTBLOCK(b,l) (*me->target->isa->put_block)(me->target,(b),(l))
                     40: 
                     41: /* ------------------------------------------------------------------------- */
                     42: 
2.11      frystyk    43: /*
                     44: **  This function is only called from either FlushEvent or HTBufferWriter_lazyFlush
                     45: **  which means that only the host object or timeout can cause a flush
                     46: */
2.1       frystyk    47: PRIVATE int HTBufferWriter_flush (HTOutputStream * me)
                     48: {
2.11      frystyk    49:     int status = HT_ERROR;
                     50:     if (me && me->read > me->data) {
2.16    ! frystyk    51:        me->lastFlushTime = HTGetTimeInMillis();
        !            52:         if ((status = PUTBLOCK(me->data, me->read - me->data))==HT_WOULD_BLOCK)
2.1       frystyk    53:            return HT_WOULD_BLOCK;
                     54:        me->read = me->data;
                     55:     }
                     56:     return status;
                     57: }
                     58: 
2.9       eric       59: PRIVATE int FlushEvent (HTTimer * timer, void * param, HTEventType type)
2.6       frystyk    60: {
                     61:     HTOutputStream * me = (HTOutputStream *) param;
                     62:     if (timer != me->timer) HTDebugBreak();
2.11      frystyk    63:     if (PROT_TRACE) HTTrace("Buffer...... Timeout Flushing...\n");
                     64: 
                     65:     /*
                     66:     **  We ignore the return code here which we shouldn't!!!
                     67:     */
2.6       frystyk    68:     HTBufferWriter_flush(me);
2.11      frystyk    69: 
                     70:     /*
                     71:     **  Delete the timer
                     72:     */
                     73:     me->timer = NULL;
2.6       frystyk    74:     return HT_OK;
                     75: }
                     76: 
                     77: PRIVATE int HTBufferWriter_lazyFlush (HTOutputStream * me)
                     78: {
                     79:     HTNet * net;
                     80:     int delay;
                     81: 
                     82:     if (me->read <= me->data)
                     83:        return HT_OK;                   /* nothing to flush */
                     84:     /*
                     85:     **  If we are allowed to delay the flush then set a timer with the
                     86:     **  delay descibed by our delay variable. If we can't delay then flush 
                     87:     **  right away.
                     88:     */
2.12      frystyk    89:     delay = HTHost_findWriteDelay(me->host, me->lastFlushTime, me->read - me->data);
2.6       frystyk    90: 
                     91:     /*
                     92:     ** Flush immediately
                     93:     */
2.11      frystyk    94:     if (!delay) {
                     95:        int status;
                     96:        if (PROT_TRACE) HTTrace("Buffer...... Flushing\n");
                     97:        if ((status = HTBufferWriter_flush(me)) && me->timer) {
                     98:            HTTimer_delete(me->timer);
                     99:            me->timer = NULL;
                    100:        }
                    101:        return status;
                    102:     }
2.6       frystyk   103: 
                    104:     /*
2.11      frystyk   105:     ** Set a timer and tell the host we've done the write if
                    106:     **  we have not already started a timer earlier.
2.6       frystyk   107:     */
2.11      frystyk   108:     if (!me->timer) {
2.14      frystyk   109:        ms_t exp = HTGetTimeInMillis() + delay;
2.11      frystyk   110:        net = HTHost_getWriteNet(me->host);
2.14      frystyk   111:        me->timer = HTTimer_new(NULL, FlushEvent, me, exp, NO);
2.11      frystyk   112:        HTHost_unregister(me->host, net, HTEvent_WRITE);
                    113:        if (PROT_TRACE) HTTrace("Buffer...... Waiting...\n");
                    114:     }
2.6       frystyk   115:     return HT_OK;
                    116: }
                    117: 
2.1       frystyk   118: PRIVATE int HTBufferWriter_free (HTOutputStream * me)
                    119: {
2.11      frystyk   120:     return HTBufferWriter_lazyFlush(me);
2.1       frystyk   121: }
                    122: 
2.15      frystyk   123: PRIVATE BOOL HTBufferWriter_addBuffer(HTOutputStream * me, int addthis)
                    124: {
                    125:     if (me) {
2.16    ! frystyk   126:        me->expo += me->expo;
        !           127:         me->allocated += (addthis - addthis%me->growby + (me->growby*me->expo));
        !           128:        if (PROT_TRACE) HTTrace("Buffer...... Increasing buffer to %d bytes\n", me->allocated);
2.15      frystyk   129:         if (me->data) {
                    130:             int size = me->read-me->data;
                    131:             if ((me->data = (char *) HT_REALLOC(me->data, me->allocated)) == NULL)
                    132:                 HT_OUTOFMEM("HTBufferWriter_addBuffer");
                    133:             me->read = me->data + size;
                    134:         } else {
                    135:             if ((me->data = (char *) HT_CALLOC(1, me->allocated)) == NULL)
                    136:                 HT_OUTOFMEM("HTBufferWriter_addBuffer");
                    137:             me->read = me->data;
                    138:         }
                    139:        return YES;
                    140:     }
                    141:     return NO;
                    142: }
                    143: 
2.1       frystyk   144: PRIVATE int HTBufferWriter_abort (HTOutputStream * me, HTList * e)
                    145: {
2.8       frystyk   146:     if (PROT_TRACE) HTTrace("Buffer...... ABORTING...\n");
2.1       frystyk   147:     if (me->target) (*me->target->isa->abort)(me->target, e);
2.8       frystyk   148:     if (me->timer) {
                    149:        HTTimer_delete(me->timer);
                    150:        me->timer = NULL;
                    151:     }
2.1       frystyk   152:     return HT_ERROR;
                    153: }
                    154: 
2.6       frystyk   155: PRIVATE int HTBufferWriter_write (HTOutputStream * me, const char * buf, int len)
2.1       frystyk   156: {
2.6       frystyk   157:     HTNet * net = HTHost_getWriteNet(me->host);
2.1       frystyk   158:     int status;
2.16    ! frystyk   159:     while (1) {
        !           160:        int available = me->data + me->allocated - me->read;
        !           161: 
        !           162:        /* If we have enough buffer space */
        !           163:        if (len <= available) {
        !           164:            int size = 0;
        !           165:            memcpy(me->read, buf, len);
        !           166:            me->read += len;
2.15      frystyk   167:         
2.16    ! frystyk   168:            /* If we have accumulated enough data then flush */
        !           169:            if ((size = me->read - me->data) > me->growby) {
        !           170:                me->lastFlushTime = HTGetTimeInMillis();
        !           171:                status = PUTBLOCK(me->data, size);
        !           172:                if (status == HT_OK) {
        !           173:                    HTNet_addBytesWritten(net, size);
        !           174:                    me->read = me->data;
        !           175:                } else {
        !           176:                    return (status == HT_WOULD_BLOCK) ? HT_OK : HT_ERROR;
        !           177:                }
        !           178:            }
        !           179:            return HT_OK;
        !           180:        } else {
        !           181: 
        !           182:            /* Fill the existing buffer (if not already) and flush */
        !           183:            if (available) {
        !           184:                memcpy(me->read, buf, available);
        !           185:                buf += available;
        !           186:                len -= available;
        !           187:                me->read += available;
        !           188:            }
        !           189:            me->lastFlushTime = HTGetTimeInMillis();
        !           190:            status = PUTBLOCK(me->data, me->allocated);
        !           191:            if (status == HT_OK) {
        !           192:                HTNet_addBytesWritten(net, me->allocated);
        !           193:                me->read = me->data;
        !           194:            } else if (status == HT_WOULD_BLOCK) {
        !           195:                HTBufferWriter_addBuffer(me, len);
        !           196:                memcpy(me->read, buf, len);
        !           197:                me->read += len;
        !           198:                return HT_OK;
        !           199:            }
        !           200:        }
2.15      frystyk   201:     }
2.1       frystyk   202: }
                    203: 
                    204: /*     Character handling
                    205: **     ------------------
                    206: */
                    207: PRIVATE int HTBufferWriter_put_character (HTOutputStream * me, char c)
                    208: {
                    209:     return HTBufferWriter_write(me, &c, 1);
                    210: }
                    211: 
                    212: /*     String handling
                    213: **     ---------------
                    214: **
                    215: **     Strings must be smaller than this buffer size.
                    216: */
                    217: PRIVATE int HTBufferWriter_put_string (HTOutputStream * me, const char * s)
                    218: {
                    219:     return HTBufferWriter_write(me, s, (int) strlen(s));
                    220: }
                    221: /*
                    222: **     The difference between the close and the free method is that we don't
                    223: **     close the connection in the free method - we only call the free method
                    224: **     of the target stream. That way, we can keep the output stream as long 
                    225: **     as the channel itself.
                    226: */
                    227: PRIVATE int HTBufferWriter_close (HTOutputStream * me)
                    228: {
                    229:     if (me) {
                    230:        if (me->target) (*me->target->isa->close)(me->target);
                    231:        HT_FREE(me->data);
                    232:        HT_FREE(me);
                    233:     }
                    234:     return HT_OK;
                    235: }
                    236: 
                    237: PRIVATE const HTOutputStreamClass HTBufferWriter =
                    238: {              
2.6       frystyk   239:     "BufferedSocketWriter",
                    240:     HTBufferWriter_lazyFlush,
2.1       frystyk   241:     HTBufferWriter_free,
                    242:     HTBufferWriter_abort,
                    243:     HTBufferWriter_put_character,
                    244:     HTBufferWriter_put_string,
                    245:     HTBufferWriter_write,
                    246:     HTBufferWriter_close
                    247: }; 
                    248: 
2.6       frystyk   249: PUBLIC HTOutputStream * HTBufferWriter_new (HTHost * host, HTChannel * ch,
2.1       frystyk   250:                                            void * param, int bufsize)
                    251: {
2.6       frystyk   252:     if (host && ch) {
2.1       frystyk   253:        HTOutputStream * me = HTChannel_output(ch);
2.6       frystyk   254:        if (!me) {
                    255:            HTOutputStream * me;
2.16    ! frystyk   256:            int tcpbufsize = 0;
        !           257: 
        !           258: #if defined(HAVE_GETSOCKOPT) && defined(SO_SNDBUF)
        !           259:            /*
        !           260:            ** Get the TCP socket buffer size
        !           261:            */
        !           262:            {
        !           263:                SOCKET sockfd = HTChannel_socket(ch);
        !           264:                int size = sizeof(int);
        !           265:                int status = getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF,
        !           266:                                        (void *) &tcpbufsize, &size);
        !           267:                if (status == -1) {
        !           268:                    if (PROT_TRACE)
        !           269:                        HTTrace("Socket...... Could not get TCP send buffer size for socket %d\n", sockfd);
        !           270:                } else {
        !           271:                    if (PROT_TRACE)                 
        !           272:                        HTTrace("Socket...... TCP send buffer size is %d for socket %d\n", tcpbufsize, sockfd);
        !           273:                }               
        !           274:            }
        !           275: #endif
        !           276:            if (bufsize <= 0) bufsize = tcpbufsize ? tcpbufsize>>1 : OUTPUT_BUFFER_SIZE;
        !           277:            if (PROT_TRACE) HTTrace("Socket...... We use buffer size %d\n", bufsize);
2.6       frystyk   278:            if ((me = (HTOutputStream *) HT_CALLOC(1, sizeof(HTOutputStream)))==NULL ||
2.1       frystyk   279:                (me->data = (char *) HT_MALLOC(bufsize)) == NULL)
                    280:                HT_OUTOFMEM("HTBufferWriter_new");
                    281:            me->isa = &HTBufferWriter;
                    282:            me->read = me->data;
2.15      frystyk   283:            me->allocated = bufsize;
                    284:             me->growby = bufsize;
2.16    ! frystyk   285:            me->expo = 1;
2.6       frystyk   286:            me->target = HTWriter_new(host, ch, param, 0);
                    287:            me->host = host;
2.15      frystyk   288:            return me;
2.6       frystyk   289:        }
2.1       frystyk   290:     }
                    291:     return NULL;
                    292: }

Webmaster