1 /*
2  * bufQueue.c --
3  *
4  *	Implementation of a queue out of buffers.
5  *
6  * Copyright (c) 2000 by Andreas Kupries <a.kupries@westend.com>
7  *
8  * See the file "license.terms" for information on usage and redistribution
9  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10  *
11  * RCS: @(#) $Id: bufQueue.c,v 1.2 2002/04/25 06:29:48 andreas_kupries Exp $
12  */
13 
14 #include "buf.h"
15 
16 /*
17  * Internal structures used to hold the buffers in the queue.
18  */
19 
20 /*
21  * Structure of a node in the queue.
22  */
23 
24 typedef struct QNode_ {
25   Buf_Buffer     buf;     /* The buffer managed by the node */
26   struct QNode_* nextPtr; /* Reference to the next node/buffer */
27 } QNode;
28 
29 /*
30  * Structure of the whole queue.
31  */
32 
33 typedef struct Queue_ {
34   QNode*    firstNode;  /* Head of the queue */
35   QNode*    lastNode;   /* Last node/buffer in the queue */
36   int       size;       /* Number of bytes stored in the queue */
37 #if GT81
38   Tcl_Mutex lock;       /* mutex to serialize access to the
39 			 * queue when more than one thread
40 			 * is trying to access it. */
41 #endif
42 } Queue;
43 
44 /*
45  * Declaration of size to use for new buffers when
46  * extending the queue
47  */
48 
49 #define BUF_SIZE (1024)
50 
51 
52 /*
53  *------------------------------------------------------*
54  *
55  *	Buf_NewQueue --
56  *
57  *	Creates a new, empty queue.
58  *
59  *	Sideeffects:
60  *		Allocates and initializes memory.
61  *
62  *	Result:
63  *		A queue token.
64  *
65  *------------------------------------------------------*
66  */
67 
68 Buf_BufferQueue
Buf_NewQueue()69 Buf_NewQueue ()
70 {
71   Queue* q = (Queue*) Tcl_Alloc (sizeof (Queue));
72 
73   q->firstNode = (QNode*) NULL;
74   q->lastNode  = (QNode*) NULL;
75   q->size      = 0;
76 #if GT81
77   q->lock      = (Tcl_Mutex) NULL;
78 #endif
79   return (Buf_BufferQueue) q;
80 }
81 
82 /*
83  *------------------------------------------------------*
84  *
85  *	Buf_FreeQueue --
86  *
87  *	Deletes the specified queue.
88  *
89  *	Sideeffects:
90  *		Deallocates the memory which was
91  *		allocated in Buf_NewQueue.
92  *
93  *	Result:
94  *		None.
95  *
96  *------------------------------------------------------*
97  */
98 
99 void
Buf_FreeQueue(queue)100 Buf_FreeQueue (queue)
101      Buf_BufferQueue queue;
102 {
103   Queue* q = (Queue*) queue;
104   QNode* n = q->firstNode;
105   QNode* tmp;
106 
107 #if GT81
108   Tcl_MutexLock (&q->lock);
109 #endif
110 
111   while (n != (QNode*) NULL) {
112     Buf_DecrRefcount (n->buf);
113     tmp = n->nextPtr;
114     Tcl_Free ((char*) n);
115     n = tmp;
116   }
117 
118 #if GT81
119   Tcl_MutexUnlock   (&q->lock);
120   Tcl_MutexFinalize (&q->lock);
121 #endif
122   Tcl_Free((char*) q);
123   return;
124 }
125 
126 /*
127  *------------------------------------------------------*
128  *
129  *	Buf_QueueRead --
130  *
131  *	Reads information from the queue. The read data
132  *	is deleted from the queue.
133  *
134  *	Sideeffects:
135  *		May deallocate memory. Moves the access
136  *		pointer in the queue buffers.
137  *
138  *	Result:
139  *		Returns the number of bytes actually read.
140  *
141  *------------------------------------------------------*
142  */
143 
144 int
Buf_QueueRead(queue,outbuf,size)145 Buf_QueueRead (queue, outbuf, size)
146      Buf_BufferQueue queue;
147      char*           outbuf;
148      int             size;
149 {
150   Queue* q = (Queue*) queue;
151   QNode* n;
152   int    got, read;
153 
154 #if GT81
155   Tcl_MutexLock (&q->lock);
156 #endif
157 
158   n = q->firstNode;
159 
160   if ((size <= 0) || (n == (QNode*) NULL)) {
161 #if GT81
162     Tcl_MutexUnlock (&q->lock);
163 #endif
164     return 0;
165   }
166 
167   read = 0;
168   while ((size > 0) && (n != (QNode*) NULL)) {
169     got = Buf_Read (n->buf, outbuf, size);
170 
171     if (got > 0) {
172       read   += got;
173       outbuf += got;
174       size   -= got;
175     }
176 
177     if (size > 0) {
178       Buf_DecrRefcount (n->buf);
179       q->firstNode = n->nextPtr;
180       Tcl_Free ((char*) n);
181       n = q->firstNode;
182     }
183   }
184 
185   if (n == (QNode*) NULL) {
186     q->lastNode = (QNode*) NULL;
187   }
188 
189   q->size -= read;
190 
191 #if GT81
192   Tcl_MutexUnlock (&q->lock);
193 #endif
194 
195   return read;
196 }
197 
198 /*
199  *------------------------------------------------------*
200  *
201  *	Buf_QueueWrite --
202  *
203  *	Writes information to the queue. The written data
204  *	is appended at the end of the queue.
205  *
206  *	Sideeffects:
207  *		May allocate memory. Moves the access
208  *		pointer in the queue buffers.
209  *
210  *	Result:
211  *		Returns the number of bytes actually written.
212  *
213  *------------------------------------------------------*
214  */
215 
216 int
Buf_QueueWrite(queue,inbuf,size)217 Buf_QueueWrite (queue, inbuf, size)
218 Buf_BufferQueue queue;
219 CONST char*     inbuf;
220 int             size;
221 {
222   Queue* q = (Queue*) queue;
223   QNode* n;
224   int    done, written;
225 
226   if ((size <= 0)) {
227     return 0;
228   }
229 
230 #if GT81
231   Tcl_MutexLock (&q->lock);
232 #endif
233 
234   n       = q->firstNode;
235   written = 0;
236 
237   while (size > 0) {
238     if (n == (QNode*) NULL) {
239       n = (QNode*) Tcl_Alloc (sizeof (QNode));
240       n->nextPtr = (QNode*) NULL;
241       n->buf     = Buf_CreateFixedBuffer (BUF_SIZE);
242 
243       if (q->lastNode == (QNode*) NULL) {
244 	q->firstNode = n;
245       } else {
246 	q->lastNode->nextPtr = n;
247       }
248 
249       q->lastNode = n;
250     }
251 
252     done = Buf_Write (n->buf, inbuf, size);
253 
254     if (done > 0) {
255       written += done;
256       inbuf   += done;
257       size    -= done;
258     }
259     if (size > 0) {
260       n = (QNode*) NULL;
261     }
262   }
263 
264   q->size += written;
265 
266 #if GT81
267   Tcl_MutexUnlock (&q->lock);
268 #endif
269 
270   return written;
271 }
272 
273 /*
274  *------------------------------------------------------*
275  *
276  *	BufQueue_Append --
277  *
278  *	Appends a range containing the information
279  *	not yet read from the specified buffer to the queue.
280  *
281  *	Sideeffects:
282  *		Creates a range buffer, allocates memory.
283  *
284  *	Result:
285  *		None.
286  *
287  *------------------------------------------------------*
288  */
289 
290 void
Buf_QueueAppend(queue,buf)291 Buf_QueueAppend (queue, buf)
292      Buf_BufferQueue queue;
293      Buf_Buffer      buf;
294 {
295   /* Not the buffer is appended, but a range containing
296    * the rest of the data to read from it.
297    *
298    * Allows external usage of the buffer without affecting
299    * the queue. Writing (s.a.) is no problem, as ranges
300    * always return that nothing was written and thus force
301    * the system to append a new fixed-size buffer behind them.
302    */
303 
304   Queue* q = (Queue*) queue;
305   QNode* n;
306 
307 #if GT81
308   Tcl_MutexLock (&q->lock);
309 #endif
310 
311   buf = Buf_CreateRange (buf, Buf_Size (buf));
312 
313   n = (QNode*) Tcl_Alloc (sizeof (QNode));
314   n->nextPtr = (QNode*) NULL;
315   n->buf     = buf;
316 
317   if (q->lastNode == (QNode*) NULL) {
318     q->firstNode = n;
319   } else {
320     q->lastNode->nextPtr = n;
321   }
322 
323   q->lastNode = n;
324 
325   q->size += Buf_Size (buf);
326 
327 #if GT81
328   Tcl_MutexUnlock (&q->lock);
329 #endif
330   return;
331 }
332 
333 /*
334  *------------------------------------------------------*
335  *
336  *	BufQueue_Size --
337  *
338  *	Returns the current number of bytes stored in the queue.
339  *
340  *	Sideeffects:
341  *		None.
342  *
343  *	Result:
344  *		None.
345  *
346  *------------------------------------------------------*
347  */
348 
349 int
Buf_QueueSize(queue)350 Buf_QueueSize (queue)
351      Buf_BufferQueue queue;
352 {
353   Queue* q = (Queue*) queue;
354   int size;
355 
356 #if GT81
357   Tcl_MutexLock (&q->lock);
358 #endif
359 
360   size = q->size;
361 
362 #if GT81
363   Tcl_MutexUnlock (&q->lock);
364 #endif
365   return size;
366 }
367