2 * a very simple circular buffer FIFO implementation
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
4 * Copyright (c) 2006 Roman Shaposhnik
6 * This file is part of FFmpeg.
8 * FFmpeg is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with FFmpeg; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
32 // by default the FIFO can be auto-grown to 1MB
33 #define AUTO_GROW_DEFAULT_BYTES (1024 * 1024)
38 size_t elem_size
, nb_elems
;
39 size_t offset_r
, offset_w
;
40 // distinguishes the ambiguous situation offset_r == offset_w
44 size_t auto_grow_limit
;
47 AVFifo
*av_fifo_alloc2(size_t nb_elems
, size_t elem_size
,
57 buffer
= av_realloc_array(NULL
, nb_elems
, elem_size
);
61 f
= av_mallocz(sizeof(*f
));
67 f
->nb_elems
= nb_elems
;
68 f
->elem_size
= elem_size
;
72 f
->auto_grow_limit
= FFMAX(AUTO_GROW_DEFAULT_BYTES
/ elem_size
, 1);
77 void av_fifo_auto_grow_limit(AVFifo
*f
, size_t max_elems
)
79 f
->auto_grow_limit
= max_elems
;
82 size_t av_fifo_elem_size(const AVFifo
*f
)
87 size_t av_fifo_can_read(const AVFifo
*f
)
89 if (f
->offset_w
<= f
->offset_r
&& !f
->is_empty
)
90 return f
->nb_elems
- f
->offset_r
+ f
->offset_w
;
91 return f
->offset_w
- f
->offset_r
;
94 size_t av_fifo_can_write(const AVFifo
*f
)
96 return f
->nb_elems
- av_fifo_can_read(f
);
99 int av_fifo_grow2(AVFifo
*f
, size_t inc
)
103 if (inc
> SIZE_MAX
- f
->nb_elems
)
104 return AVERROR(EINVAL
);
106 tmp
= av_realloc_array(f
->buffer
, f
->nb_elems
+ inc
, f
->elem_size
);
108 return AVERROR(ENOMEM
);
111 // move the data from the beginning of the ring buffer
112 // to the newly allocated space
113 if (f
->offset_w
<= f
->offset_r
&& !f
->is_empty
) {
114 const size_t copy
= FFMIN(inc
, f
->offset_w
);
115 memcpy(tmp
+ f
->nb_elems
* f
->elem_size
, tmp
, copy
* f
->elem_size
);
116 if (copy
< f
->offset_w
) {
117 memmove(tmp
, tmp
+ copy
* f
->elem_size
,
118 (f
->offset_w
- copy
) * f
->elem_size
);
121 f
->offset_w
= copy
== inc
? 0 : f
->nb_elems
+ copy
;
129 static int fifo_check_space(AVFifo
*f
, size_t to_write
)
131 const size_t can_write
= av_fifo_can_write(f
);
132 const size_t need_grow
= to_write
> can_write
? to_write
- can_write
: 0;
138 can_grow
= f
->auto_grow_limit
> f
->nb_elems
?
139 f
->auto_grow_limit
- f
->nb_elems
: 0;
140 if ((f
->flags
& AV_FIFO_FLAG_AUTO_GROW
) && need_grow
<= can_grow
) {
141 // allocate a bit more than necessary, if we can
142 const size_t inc
= (need_grow
< can_grow
/ 2 ) ? need_grow
* 2 : can_grow
;
143 return av_fifo_grow2(f
, inc
);
146 return AVERROR(ENOSPC
);
149 static int fifo_write_common(AVFifo
*f
, const uint8_t *buf
, size_t *nb_elems
,
150 AVFifoCB read_cb
, void *opaque
)
152 size_t to_write
= *nb_elems
;
156 ret
= fifo_check_space(f
, to_write
);
160 offset_w
= f
->offset_w
;
162 while (to_write
> 0) {
163 size_t len
= FFMIN(f
->nb_elems
- offset_w
, to_write
);
164 uint8_t *wptr
= f
->buffer
+ offset_w
* f
->elem_size
;
167 ret
= read_cb(opaque
, wptr
, &len
);
168 if (ret
< 0 || len
== 0)
171 memcpy(wptr
, buf
, len
* f
->elem_size
);
172 buf
+= len
* f
->elem_size
;
175 if (offset_w
>= f
->nb_elems
)
179 f
->offset_w
= offset_w
;
181 if (*nb_elems
!= to_write
)
183 *nb_elems
-= to_write
;
188 int av_fifo_write(AVFifo
*f
, const void *buf
, size_t nb_elems
)
190 return fifo_write_common(f
, buf
, &nb_elems
, NULL
, NULL
);
193 int av_fifo_write_from_cb(AVFifo
*f
, AVFifoCB read_cb
,
194 void *opaque
, size_t *nb_elems
)
196 return fifo_write_common(f
, NULL
, nb_elems
, read_cb
, opaque
);
199 static int fifo_peek_common(const AVFifo
*f
, uint8_t *buf
, size_t *nb_elems
,
200 size_t offset
, AVFifoCB write_cb
, void *opaque
)
202 size_t to_read
= *nb_elems
;
203 size_t offset_r
= f
->offset_r
;
204 size_t can_read
= av_fifo_can_read(f
);
207 if (offset
> can_read
|| to_read
> can_read
- offset
) {
209 return AVERROR(EINVAL
);
212 if (offset_r
>= f
->nb_elems
- offset
)
213 offset_r
-= f
->nb_elems
- offset
;
217 while (to_read
> 0) {
218 size_t len
= FFMIN(f
->nb_elems
- offset_r
, to_read
);
219 uint8_t *rptr
= f
->buffer
+ offset_r
* f
->elem_size
;
222 ret
= write_cb(opaque
, rptr
, &len
);
223 if (ret
< 0 || len
== 0)
226 memcpy(buf
, rptr
, len
* f
->elem_size
);
227 buf
+= len
* f
->elem_size
;
230 if (offset_r
>= f
->nb_elems
)
235 *nb_elems
-= to_read
;
240 int av_fifo_read(AVFifo
*f
, void *buf
, size_t nb_elems
)
242 int ret
= fifo_peek_common(f
, buf
, &nb_elems
, 0, NULL
, NULL
);
243 av_fifo_drain2(f
, nb_elems
);
247 int av_fifo_read_to_cb(AVFifo
*f
, AVFifoCB write_cb
,
248 void *opaque
, size_t *nb_elems
)
250 int ret
= fifo_peek_common(f
, NULL
, nb_elems
, 0, write_cb
, opaque
);
251 av_fifo_drain2(f
, *nb_elems
);
255 int av_fifo_peek(const AVFifo
*f
, void *buf
, size_t nb_elems
, size_t offset
)
257 return fifo_peek_common(f
, buf
, &nb_elems
, offset
, NULL
, NULL
);
260 int av_fifo_peek_to_cb(const AVFifo
*f
, AVFifoCB write_cb
, void *opaque
,
261 size_t *nb_elems
, size_t offset
)
263 return fifo_peek_common(f
, NULL
, nb_elems
, offset
, write_cb
, opaque
);
266 void av_fifo_drain2(AVFifo
*f
, size_t size
)
268 const size_t cur_size
= av_fifo_can_read(f
);
270 av_assert0(cur_size
>= size
);
271 if (cur_size
== size
)
274 if (f
->offset_r
>= f
->nb_elems
- size
)
275 f
->offset_r
-= f
->nb_elems
- size
;
280 void av_fifo_reset2(AVFifo
*f
)
282 f
->offset_r
= f
->offset_w
= 0;
286 void av_fifo_freep2(AVFifo
**f
)
289 av_freep(&(*f
)->buffer
);