Annotation of libwww/Library/src/HTBufWrt.c, revision 2.27
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.27 ! frystyk 6: ** @(#) $Id: HTBufWrt.c,v 2.26 1999/04/04 00:12:00 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 */
2.20 frystyk 15: #include "wwwsys.h"
2.1 frystyk 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.17 frystyk 49: int status = HT_OK;
2.11 frystyk 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;
2.27 ! frystyk 62: if (me->timer && timer != me->timer)
2.25 frystyk 63: HTDEBUGBREAK("Buffer Writer timer %p not in sync\n" _ timer);
64: HTTRACE(PROT_TRACE, "Buffer...... Timeout flushing %p with timer %p\n" _ me _ timer);
2.11 frystyk 65:
66: /*
67: ** We ignore the return code here which we shouldn't!!!
68: */
2.6 frystyk 69: HTBufferWriter_flush(me);
2.11 frystyk 70:
71: /*
72: ** Delete the timer
73: */
2.24 frystyk 74: HTTimer_delete(me->timer);
2.11 frystyk 75: me->timer = NULL;
2.6 frystyk 76: return HT_OK;
77: }
78:
79: PRIVATE int HTBufferWriter_lazyFlush (HTOutputStream * me)
80: {
81: HTNet * net;
82: int delay;
83:
2.18 frystyk 84: if (me->read <= me->data) {
2.6 frystyk 85: return HT_OK; /* nothing to flush */
2.18 frystyk 86: }
2.6 frystyk 87: /*
88: ** If we are allowed to delay the flush then set a timer with the
89: ** delay descibed by our delay variable. If we can't delay then flush
90: ** right away.
91: */
2.12 frystyk 92: delay = HTHost_findWriteDelay(me->host, me->lastFlushTime, me->read - me->data);
2.6 frystyk 93:
94: /*
95: ** Flush immediately
96: */
2.11 frystyk 97: if (!delay) {
98: int status;
2.25 frystyk 99: HTTRACE(STREAM_TRACE, "Buffer...... Flushing %p\n" _ me);
2.11 frystyk 100: if ((status = HTBufferWriter_flush(me)) && me->timer) {
101: HTTimer_delete(me->timer);
102: me->timer = NULL;
103: }
104: return status;
105: }
2.6 frystyk 106:
107: /*
2.11 frystyk 108: ** Set a timer and tell the host we've done the write if
2.21 frystyk 109: ** we have not already started a timer earlier. If a timer
110: ** does already exist then make sure that it hasn't expired.
111: ** This can be the case if we have a really slow client that
112: ** can't parse the data fast enough.
2.6 frystyk 113: */
2.11 frystyk 114: if (!me->timer) {
115: net = HTHost_getWriteNet(me->host);
2.21 frystyk 116: me->timer = HTTimer_new(NULL, FlushEvent, me, delay, YES, NO);
2.11 frystyk 117: HTHost_unregister(me->host, net, HTEvent_WRITE);
2.25 frystyk 118: HTTRACE(STREAM_TRACE, "Buffer...... Waiting %dms on %p\n" _ delay _ me);
2.21 frystyk 119: } else {
120: if (HTTimer_hasTimerExpired(me->timer)) {
2.25 frystyk 121: HTTRACE(STREAM_TRACE, "Buffer...... Dispatching old timer %p\n" _ me->timer);
2.21 frystyk 122: HTTimer_dispatch(me->timer);
123: me->timer = NULL;
124: } else {
2.25 frystyk 125: HTTRACE(STREAM_TRACE, "Buffer...... Waiting on unexpired timer %p\n" _ me->timer);
2.21 frystyk 126: }
2.11 frystyk 127: }
2.6 frystyk 128: return HT_OK;
129: }
130:
2.1 frystyk 131: PRIVATE int HTBufferWriter_free (HTOutputStream * me)
132: {
2.11 frystyk 133: return HTBufferWriter_lazyFlush(me);
2.1 frystyk 134: }
135:
2.15 frystyk 136: PRIVATE BOOL HTBufferWriter_addBuffer(HTOutputStream * me, int addthis)
137: {
138: if (me) {
2.16 frystyk 139: me->allocated += (addthis - addthis%me->growby + (me->growby*me->expo));
2.17 frystyk 140: me->expo *= 2;
2.25 frystyk 141: HTTRACE(STREAM_TRACE, "Buffer...... Increasing buffer to %d bytes\n" _ me->allocated);
2.15 frystyk 142: if (me->data) {
143: int size = me->read-me->data;
144: if ((me->data = (char *) HT_REALLOC(me->data, me->allocated)) == NULL)
145: HT_OUTOFMEM("HTBufferWriter_addBuffer");
146: me->read = me->data + size;
147: } else {
148: if ((me->data = (char *) HT_CALLOC(1, me->allocated)) == NULL)
149: HT_OUTOFMEM("HTBufferWriter_addBuffer");
150: me->read = me->data;
151: }
152: return YES;
153: }
154: return NO;
155: }
156:
2.1 frystyk 157: PRIVATE int HTBufferWriter_abort (HTOutputStream * me, HTList * e)
158: {
2.25 frystyk 159: HTTRACE(STREAM_TRACE, "Buffer...... ABORTING...\n");
2.8 frystyk 160: if (me->timer) {
161: HTTimer_delete(me->timer);
162: me->timer = NULL;
163: }
2.27 ! frystyk 164: if (me->target) (*me->target->isa->abort)(me->target, e);
2.1 frystyk 165: return HT_ERROR;
166: }
167:
2.6 frystyk 168: PRIVATE int HTBufferWriter_write (HTOutputStream * me, const char * buf, int len)
2.1 frystyk 169: {
170: int status;
2.16 frystyk 171: while (1) {
172: int available = me->data + me->allocated - me->read;
173:
174: /* If we have enough buffer space */
175: if (len <= available) {
176: int size = 0;
177: memcpy(me->read, buf, len);
178: me->read += len;
2.15 frystyk 179:
2.16 frystyk 180: /* If we have accumulated enough data then flush */
181: if ((size = me->read - me->data) > me->growby) {
182: me->lastFlushTime = HTGetTimeInMillis();
183: status = PUTBLOCK(me->data, size);
184: if (status == HT_OK) {
185: me->read = me->data;
186: } else {
187: return (status == HT_WOULD_BLOCK) ? HT_OK : HT_ERROR;
188: }
189: }
190: return HT_OK;
191: } else {
192:
193: /* Fill the existing buffer (if not already) and flush */
194: if (available) {
195: memcpy(me->read, buf, available);
196: buf += available;
197: len -= available;
198: me->read += available;
199: }
200: me->lastFlushTime = HTGetTimeInMillis();
201: status = PUTBLOCK(me->data, me->allocated);
202: if (status == HT_OK) {
203: me->read = me->data;
204: } else if (status == HT_WOULD_BLOCK) {
205: HTBufferWriter_addBuffer(me, len);
206: memcpy(me->read, buf, len);
207: me->read += len;
208: return HT_OK;
209: }
210: }
2.15 frystyk 211: }
2.1 frystyk 212: }
213:
214: /* Character handling
215: ** ------------------
216: */
217: PRIVATE int HTBufferWriter_put_character (HTOutputStream * me, char c)
218: {
219: return HTBufferWriter_write(me, &c, 1);
220: }
221:
222: /* String handling
223: ** ---------------
224: **
225: ** Strings must be smaller than this buffer size.
226: */
227: PRIVATE int HTBufferWriter_put_string (HTOutputStream * me, const char * s)
228: {
229: return HTBufferWriter_write(me, s, (int) strlen(s));
230: }
231: /*
232: ** The difference between the close and the free method is that we don't
233: ** close the connection in the free method - we only call the free method
234: ** of the target stream. That way, we can keep the output stream as long
235: ** as the channel itself.
236: */
237: PRIVATE int HTBufferWriter_close (HTOutputStream * me)
238: {
239: if (me) {
2.27 ! frystyk 240: if (me->timer) {
! 241: HTTimer_delete(me->timer);
! 242: me->timer = NULL;
! 243: }
2.1 frystyk 244: if (me->target) (*me->target->isa->close)(me->target);
245: HT_FREE(me->data);
246: HT_FREE(me);
247: }
248: return HT_OK;
249: }
250:
251: PRIVATE const HTOutputStreamClass HTBufferWriter =
252: {
2.6 frystyk 253: "BufferedSocketWriter",
254: HTBufferWriter_lazyFlush,
2.1 frystyk 255: HTBufferWriter_free,
256: HTBufferWriter_abort,
257: HTBufferWriter_put_character,
258: HTBufferWriter_put_string,
259: HTBufferWriter_write,
260: HTBufferWriter_close
261: };
262:
2.26 frystyk 263: PRIVATE HTOutputStream * buffer_new (HTHost * host, HTChannel * ch,
264: void * param, int bufsize)
2.1 frystyk 265: {
2.6 frystyk 266: if (host && ch) {
2.1 frystyk 267: HTOutputStream * me = HTChannel_output(ch);
2.6 frystyk 268: if (!me) {
269: HTOutputStream * me;
2.16 frystyk 270: int tcpbufsize = 0;
271:
272: #if defined(HAVE_GETSOCKOPT) && defined(SO_SNDBUF)
273: /*
274: ** Get the TCP socket buffer size
275: */
276: {
277: SOCKET sockfd = HTChannel_socket(ch);
278: int size = sizeof(int);
279: int status = getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF,
280: (void *) &tcpbufsize, &size);
281: if (status == -1) {
2.25 frystyk 282: HTTRACE(STREAM_TRACE, "Socket...... Could not get TCP send buffer size for socket %d\n" _ sockfd);
2.16 frystyk 283: } else {
2.25 frystyk 284: HTTRACE(STREAM_TRACE, "Socket...... TCP send buffer size is %d for socket %d\n" _ tcpbufsize _ sockfd);
2.16 frystyk 285: }
286: }
287: #endif
2.17 frystyk 288: if (bufsize <= 0) bufsize = tcpbufsize ? tcpbufsize : OUTPUT_BUFFER_SIZE;
2.6 frystyk 289: if ((me = (HTOutputStream *) HT_CALLOC(1, sizeof(HTOutputStream)))==NULL ||
2.1 frystyk 290: (me->data = (char *) HT_MALLOC(bufsize)) == NULL)
291: HT_OUTOFMEM("HTBufferWriter_new");
292: me->isa = &HTBufferWriter;
293: me->read = me->data;
2.15 frystyk 294: me->allocated = bufsize;
295: me->growby = bufsize;
2.16 frystyk 296: me->expo = 1;
2.6 frystyk 297: me->host = host;
2.15 frystyk 298: return me;
2.26 frystyk 299: }
300: }
301: return NULL;
302: }
303:
304: PUBLIC HTOutputStream * HTBufferWriter_new (HTHost * host,
305: HTChannel * ch,
306: void * param,
307: int bufsize)
308: {
309: HTOutputStream * me = buffer_new(host, ch, param, bufsize);
310: if (me) {
311: me->target = HTWriter_new(host, ch, param, 0);
312: return me;
313: }
314: return NULL;
315: }
316:
317: PUBLIC HTOutputStream * HTBufferConverter_new (HTHost * host,
318: HTChannel * ch,
319: void * param,
320: int bufsize,
321: HTOutputStream * target)
322: {
323: if (target) {
324: HTOutputStream * me = buffer_new(host, ch, param, bufsize);
325: if (me) {
326: me->target = target;
327: return me;
2.6 frystyk 328: }
2.1 frystyk 329: }
330: return NULL;
331: }
Webmaster