1 | /* a fifo queue that never fills.
|
---|
2 | * Copyright (C) 2005 Elwood C. Downey ecdowney@clearskyinstitute.com
|
---|
3 | * includes standalone commandline test program, see below.
|
---|
4 |
|
---|
5 | This library is free software; you can redistribute it and/or
|
---|
6 | modify it under the terms of the GNU Lesser General Public
|
---|
7 | License as published by the Free Software Foundation; either
|
---|
8 | version 2.1 of the License, or (at your option) any later version.
|
---|
9 |
|
---|
10 | This library is distributed in the hope that it will be useful,
|
---|
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
13 | Lesser General Public License for more details.
|
---|
14 |
|
---|
15 | You should have received a copy of the GNU Lesser General Public
|
---|
16 | License along with this library; if not, write to the Free Software
|
---|
17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
---|
18 |
|
---|
19 | */
|
---|
20 |
|
---|
21 | /** \file fq.c
|
---|
22 | \brief a fifo queue that never fills.
|
---|
23 |
|
---|
24 | Generic FIFO Queue.
|
---|
25 |
|
---|
26 | an FQ is a FIFO list of pointers to void, each called an "element".
|
---|
27 | elements are added at q[head]. there are (nq) elements in the list. the
|
---|
28 | element to be removed next is q[head-nq]. there are (head-nq) empty slots
|
---|
29 | at the front of the q array. there are (nmem-head) elements available at
|
---|
30 | the end. if the head reaches the end, existing enties are slid to the front
|
---|
31 | of the array and total memory is adjusted up or down as required.
|
---|
32 |
|
---|
33 | example:
|
---|
34 |
|
---|
35 | <-------------------- nmem = 17 --------------------------------->
|
---|
36 | <-- head - nq = 6 ---> <-- nq = 4 --> <---- nmem - head = 7 -->
|
---|
37 | ---------------------------------------------------------------------
|
---|
38 | | | | | | | | x | x | x | x | | | | | | | |
|
---|
39 | ---------------------------------------------------------------------
|
---|
40 | 0 1 2 3 4 5 6 7 8 9 ^
|
---|
41 | head = 10
|
---|
42 |
|
---|
43 | \author Elwood Downey
|
---|
44 | */
|
---|
45 |
|
---|
46 | #include <stdlib.h>
|
---|
47 | #include <string.h>
|
---|
48 |
|
---|
49 | #include "fq.h"
|
---|
50 |
|
---|
51 | struct _FQ {
|
---|
52 | void **q; /* malloced array of (void *) */
|
---|
53 | int nq; /* number of elements on queue */
|
---|
54 | int head; /* index into q[] of next empty spot */
|
---|
55 | int nmem; /* number of total slots in q[] */
|
---|
56 | int grow; /* n elements to grow when out of room*/
|
---|
57 | };
|
---|
58 |
|
---|
59 | /* default memory managers, override with setMemFuncsFQ() */
|
---|
60 | static void *(*mymalloc)(size_t size) = malloc;
|
---|
61 | static void *(*myrealloc)(void *ptr, size_t size) = realloc;
|
---|
62 | static void (*myfree)(void *ptr) = free;
|
---|
63 |
|
---|
64 | static void chkFQ (FQ *q);
|
---|
65 |
|
---|
66 | /* return pointer to a new FQ, or NULL if no more memory.
|
---|
67 | * grow is an efficiency hint of the number of elements to grow when out of
|
---|
68 | * room, nothing terrible happens if it is wrong.
|
---|
69 | */
|
---|
70 | FQ *
|
---|
71 | newFQ (int grow)
|
---|
72 | {
|
---|
73 | FQ *q = (FQ *)(*mymalloc)(sizeof(FQ));
|
---|
74 | memset (q, 0, sizeof(FQ));
|
---|
75 | q->q = (*mymalloc) (1); /* seed for realloc */
|
---|
76 | q->grow = grow > 0 ? grow : 1;
|
---|
77 | return (q);
|
---|
78 | }
|
---|
79 |
|
---|
80 | /* delete a FQ no longer needed */
|
---|
81 | void
|
---|
82 | delFQ (FQ *q)
|
---|
83 | {
|
---|
84 | (*myfree) (q->q); /* guaranteed set in newFQ() */
|
---|
85 | (*myfree) ((void *)q);
|
---|
86 | }
|
---|
87 |
|
---|
88 | /* push an element onto the given FQ */
|
---|
89 | void
|
---|
90 | pushFQ (FQ *q, void *e)
|
---|
91 | {
|
---|
92 | chkFQ (q);
|
---|
93 | q->q[q->head++] = e;
|
---|
94 | q->nq++;
|
---|
95 | }
|
---|
96 |
|
---|
97 | /* pop and return the next element in the given FQ, or NULL if empty */
|
---|
98 | void *
|
---|
99 | popFQ (FQ *q)
|
---|
100 | {
|
---|
101 | return (q->nq > 0 ? q->q[q->head - q->nq--] : NULL);
|
---|
102 | }
|
---|
103 |
|
---|
104 | /* return next element in the given FQ leaving it on the q, or NULL if empty */
|
---|
105 | void *
|
---|
106 | peekFQ (FQ *q)
|
---|
107 | {
|
---|
108 | return (peekiFQ(q,0));
|
---|
109 | }
|
---|
110 |
|
---|
111 | /* return ith element from head of the given FQ.
|
---|
112 | * this can be used for iteration as:
|
---|
113 | * for (i = 0; i < nFQ(q); i++)
|
---|
114 | * void *e = peekiFQ(q,i);
|
---|
115 | */
|
---|
116 | void *
|
---|
117 | peekiFQ (FQ *q, int i)
|
---|
118 | {
|
---|
119 | return (q->nq > 0 ? q->q[q->head - q->nq + i] : NULL);
|
---|
120 | }
|
---|
121 |
|
---|
122 | /* return the number of elements in the given FQ */
|
---|
123 | int
|
---|
124 | nFQ (FQ *q)
|
---|
125 | {
|
---|
126 | return (q->nq);
|
---|
127 | }
|
---|
128 |
|
---|
129 | /* install new version of malloc/realloc/free.
|
---|
130 | * N.B. don't call after first use of any other FQ function
|
---|
131 | */
|
---|
132 | void
|
---|
133 | setMemFuncsFQ (void *(*newmalloc)(size_t size),
|
---|
134 | void *(*newrealloc)(void *ptr, size_t size),
|
---|
135 | void (*newfree)(void *ptr))
|
---|
136 | {
|
---|
137 | mymalloc = newmalloc;
|
---|
138 | myrealloc = newrealloc;
|
---|
139 | myfree = newfree;
|
---|
140 | }
|
---|
141 |
|
---|
142 | /* insure q can hold one more element */
|
---|
143 | static void
|
---|
144 | chkFQ (FQ *q)
|
---|
145 | {
|
---|
146 | int infront;
|
---|
147 |
|
---|
148 | /* done if still room at end */
|
---|
149 | if (q->nmem > q->head)
|
---|
150 | return;
|
---|
151 |
|
---|
152 | /* move list to front */
|
---|
153 | infront = q->head - q->nq;
|
---|
154 | memmove (q->q, &q->q[infront], q->nq * sizeof(void*));
|
---|
155 | q->head -= infront;
|
---|
156 |
|
---|
157 | /* realloc to minimum number of grow-sized chunks required */
|
---|
158 | q->nmem = q->grow*(q->head/q->grow+1);
|
---|
159 | q->q = (*myrealloc) (q->q, q->nmem * sizeof(void*));
|
---|
160 | }
|
---|
161 |
|
---|
162 | #if defined(TEST_FQ)
|
---|
163 |
|
---|
164 | /* to build a stand-alone commandline test program:
|
---|
165 | * cc -DTEST_FQ -o fq fq.c
|
---|
166 | * run ./fq to test push/pop/peek and watch the queue after each operation.
|
---|
167 | * the queue test elements are char, please excuse the ugly casts.
|
---|
168 | */
|
---|
169 |
|
---|
170 | #include <stdio.h>
|
---|
171 |
|
---|
172 | /* draw a simple graphical representation of the given FQ */
|
---|
173 | static void
|
---|
174 | prFQ (FQ *q)
|
---|
175 | {
|
---|
176 | int i;
|
---|
177 |
|
---|
178 | /* print the q, empty slots print as '.' */
|
---|
179 | for (i = 0; i < q->nmem; i++) {
|
---|
180 | if (i >= q->head - q->nq && i < q->head)
|
---|
181 | printf ("%c", (char)(int)q->q[i]);
|
---|
182 | else
|
---|
183 | printf (".");
|
---|
184 | }
|
---|
185 |
|
---|
186 | /* add right-justified stats */
|
---|
187 | printf ("%*s nmem = %2d head = %2d nq = %2d\n", 50-i, "", q->nmem,
|
---|
188 | q->head, q->nq);
|
---|
189 | }
|
---|
190 |
|
---|
191 | int
|
---|
192 | main (int ac, char *av[])
|
---|
193 | {
|
---|
194 | FQ *q = newFQ(8);
|
---|
195 | int c, e = -1;
|
---|
196 | void *p;
|
---|
197 |
|
---|
198 | printf ("Commands:\n");
|
---|
199 | printf (" P = push a letter a-z\n");
|
---|
200 | printf (" p = pop a letter\n");
|
---|
201 | printf (" k = peek into queue\n");
|
---|
202 |
|
---|
203 | while ((c = fgetc(stdin)) != EOF) {
|
---|
204 | switch (c) {
|
---|
205 | case 'P':
|
---|
206 | pushFQ (q, (void*)('a'+(e=(e+1)%26)));
|
---|
207 | prFQ(q);
|
---|
208 | break;
|
---|
209 | case 'p':
|
---|
210 | p = popFQ (q);
|
---|
211 | if (p)
|
---|
212 | printf ("popped %c\n", (char)(int)p);
|
---|
213 | else
|
---|
214 | printf ("popped empty q\n");
|
---|
215 | prFQ(q);
|
---|
216 | break;
|
---|
217 | case 'k':
|
---|
218 | p = peekFQ (q);
|
---|
219 | if (p)
|
---|
220 | printf ("peeked %c\n", (char)(int)p);
|
---|
221 | else
|
---|
222 | printf ("peeked empty q\n");
|
---|
223 | prFQ(q);
|
---|
224 | break;
|
---|
225 | default:
|
---|
226 | break;
|
---|
227 | }
|
---|
228 | }
|
---|
229 |
|
---|
230 | return (0);
|
---|
231 | }
|
---|
232 | #endif /* TEST_FQ */
|
---|
233 |
|
---|