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
24typedef 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
33typedef 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
68Buf_BufferQueue
69Buf_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
99void
100Buf_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
144int
145Buf_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
216int
217Buf_QueueWrite (queue, inbuf, size)
218Buf_BufferQueue queue;
219CONST char*     inbuf;
220int             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
290void
291Buf_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
349int
350Buf_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