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