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