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