Coverage Report

Created: 2025-12-31 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/util/fifo.c
Line
Count
Source
1
/*
2
 *   This program is free software; you can redistribute it and/or modify
3
 *   it under the terms of the GNU General Public License as published by
4
 *   the Free Software Foundation; either version 2 of the License, or
5
 *   (at your option) any later version.
6
 *
7
 *   This program is distributed in the hope that it will be useful,
8
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 *   GNU General Public License for more details.
11
 *
12
 *   You should have received a copy of the GNU General Public License
13
 *   along with this program; if not, write to the Free Software
14
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15
 */
16
17
/** Non-thread-safe fifo (FIFO) implementation
18
 *
19
 * @file src/lib/util/fifo.c
20
 *
21
 * @copyright 2005,2006 The FreeRADIUS server project
22
 * @copyright 2005 Alan DeKok (aland@freeradius.org)
23
 */
24
RCSID("$Id: 3c967c14f10148ad89679fcadadb49fb8f212808 $")
25
26
#include <string.h>
27
28
#include <freeradius-devel/util/fifo.h>
29
30
struct fr_fifo_s {
31
  unsigned int  num;    //!< How many elements exist in the fifo.
32
  unsigned int  first, last;  //!< Head and tail indexes for the fifo.
33
  unsigned int  max;    //!< How many elements were created in the fifo.
34
  fr_fifo_free_t  free_node;  //!< Function to call to free nodes when the fifo is freed.
35
36
  char const  *type;    //!< Type of elements.
37
38
  void *data[1];
39
};
40
41
/** Free a fifo and optionally, any data still enqueued
42
 *
43
 * @param[in] fi  to free.
44
 * @return 0
45
 */
46
static int _fifo_free(fr_fifo_t *fi)
47
0
{
48
0
  unsigned int i;
49
50
0
  if (fi->free_node) {
51
0
    for (i = 0 ; i < fi->num; i++) {
52
0
      unsigned int element;
53
54
0
      element = i + fi->first;
55
0
      if (element > fi->max) {
56
0
        element -= fi->max;
57
0
      }
58
59
0
      fi->free_node(fi->data[element]);
60
0
      fi->data[element] = NULL;
61
0
    }
62
0
  }
63
64
0
  memset(fi, 0, sizeof(*fi));
65
66
0
  return 0;
67
0
}
68
69
/** Create a fifo queue
70
 *
71
 * The first element enqueued will be the first to be dequeued.
72
 *
73
 * @note The created fifo does not provide any thread synchronisation functionality
74
 *  such as mutexes.  If multiple threads are enqueueing and dequeueing data
75
 *  the callers must synchronise their access.
76
 *
77
 * @param[in] ctx to allocate fifo array in.
78
 * @param[in] type  Talloc type of elements (may be NULL).
79
 * @param[in] max The maximum number of elements allowed.
80
 * @param[in] free_node Function to use to free node data if the fifo is freed.
81
 * @return
82
 *  - A new fifo queue.
83
 *  - NULL on error.
84
 */
85
fr_fifo_t *_fr_fifo_create(TALLOC_CTX *ctx, char const *type, int max, fr_fifo_free_t free_node)
86
0
{
87
0
  fr_fifo_t *fi;
88
89
0
  if ((max < 2) || (max > (1024 * 1024))) return NULL;
90
91
0
  fi = talloc_zero_size(ctx, (sizeof(*fi) + (sizeof(fi->data[0])*max)));
92
0
  if (!fi) return NULL;
93
0
  talloc_set_type(fi, fr_fifo_t);
94
0
  talloc_set_destructor(fi, _fifo_free);
95
96
0
  fi->max = max;
97
0
  fi->type = type;
98
0
  fi->free_node = free_node;
99
100
0
  return fi;
101
0
}
102
103
/** Push data onto the fifo
104
 *
105
 * @param[in] fi  FIFO to push data onto.
106
 * @param[in] data  to push.
107
 * @return
108
 *  - 0 on success.
109
 *  - -1 on error.
110
 */
111
int fr_fifo_push(fr_fifo_t *fi, void *data)
112
0
{
113
0
  if (!fi || !data) return -1;
114
115
0
  if (fi->num >= fi->max) return -1;
116
117
0
#ifndef TALLOC_GET_TYPE_ABORT_NOOP
118
0
  if (fi->type) _talloc_get_type_abort(data, fi->type, __location__);
119
0
#endif
120
121
0
  fi->data[fi->last++] = data;
122
0
  if (fi->last >= fi->max) fi->last = 0;
123
0
  fi->num++;
124
125
0
  return 0;
126
0
}
127
128
/** Pop data off of the fifo
129
 *
130
 * @param[in] fi  FIFO to pop data from.
131
 * @return
132
 *  - The data popped.
133
 *  - NULL if the queue is empty.
134
 */
135
void *fr_fifo_pop(fr_fifo_t *fi)
136
0
{
137
0
  void *data;
138
139
0
  if (!fi || (fi->num == 0)) return NULL;
140
141
0
  data = fi->data[fi->first++];
142
143
0
  if (fi->first >= fi->max) {
144
0
    fi->first = 0;
145
0
  }
146
0
  fi->num--;
147
148
0
  return data;
149
0
}
150
151
/** Examine the next element that would be popped
152
 *
153
 * @param[in] fi  FIFO to peek at.
154
 * @return
155
 *  - The data at the head of the queue
156
 *  - NULL if the queue is empty.
157
 */
158
void *fr_fifo_peek(fr_fifo_t *fi)
159
0
{
160
0
  if (!fi || (fi->num == 0)) return NULL;
161
162
0
  return fi->data[fi->first];
163
0
}
164
165
/** Return the number of elements in the fifo queue
166
 *
167
 * @param[in] fi  FIFO to count elements in.
168
 * @return the number of elements
169
 */
170
unsigned int fr_fifo_num_elements(fr_fifo_t *fi)
171
0
{
172
0
  if (!fi) return 0;
173
174
0
  return fi->num;
175
0
}
176
177
#ifdef TESTING
178
179
/*
180
 *  cc -DTESTING -I .. fifo.c -o fifo
181
 *
182
 *  ./fifo
183
 */
184
185
#define MAX 1024
186
int main(int argc, char **argv)
187
{
188
  int i, j, array[MAX];
189
  fr_fifo_t *fi;
190
191
  fi = fr_fifo_create(NULL, MAX, NULL);
192
  if (!fi) fr_exit(1);
193
194
  for (j = 0; j < 5; j++) {
195
#define SPLIT (MAX/3)
196
#define COUNT ((j * SPLIT) + i)
197
    for (i = 0; i < SPLIT; i++) {
198
      array[COUNT % MAX] = COUNT;
199
200
      if (fr_fifo_push(fi, &array[COUNT % MAX]) < 0) {
201
        fprintf(stderr, "%d %d\tfailed pushing %d\n",
202
          j, i, COUNT);
203
        fr_exit(2);
204
      }
205
206
      if (fr_fifo_num_elements(fi) != (i + 1)) {
207
        fprintf(stderr, "%d %d\tgot size %d expected %d\n",
208
          j, i, i + 1, fr_fifo_num_elements(fi));
209
        fr_exit(1);
210
      }
211
    }
212
213
    if (fr_fifo_num_elements(fi) != SPLIT) {
214
      fprintf(stderr, "HALF %d %d\n",
215
        fr_fifo_num_elements(fi), SPLIT);
216
      fr_exit(1);
217
    }
218
219
    for (i = 0; i < SPLIT; i++) {
220
      int *p;
221
222
      p = fr_fifo_pop(fi);
223
      if (!p) {
224
        fprintf(stderr, "No pop at %d\n", i);
225
        fr_exit(3);
226
      }
227
228
      if (*p != COUNT) {
229
        fprintf(stderr, "%d %d\tgot %d expected %d\n",
230
          j, i, *p, COUNT);
231
        fr_exit(4);
232
      }
233
234
      if (fr_fifo_num_elements(fi) != SPLIT - (i + 1)) {
235
        fprintf(stderr, "%d %d\tgot size %d expected %d\n",
236
          j, i, SPLIT - (i + 1), fr_fifo_num_elements(fi));
237
        fr_exit(1);
238
      }
239
    }
240
241
    if (fr_fifo_num_elements(fi) != 0) {
242
      fprintf(stderr, "ZERO %d %d\n",
243
        fr_fifo_num_elements(fi), 0);
244
      fr_exit(1);
245
    }
246
  }
247
248
  talloc_free(fi);
249
250
  fr_exit(0);
251
}
252
#endif