History

lavu/fifo: Add new AVFifo API based upon the notion of element size

Many AVFifoBuffer users operate on fixed-size elements (e.g. pointers), but the current FIFO API deals exclusively in bytes, requiring extra complexity in all these callers. Add a new AVFifo API creating a FIFO with an element size that may be larger than a byte. All operations on such a FIFO then operate on complete elements. This API does not reuse AVFifoBuffer and its API at all, but instead uses an opaque struct called AVFifo. The AVFifoBuffer API will be deprecated in a future commit once all of its users have been switched to the new API. Not reusing AVFifoBuffer also allowed to use the full range of size_t from the beginning.

Anton Khirnov committed 4 years ago

Commit 7329b221 parent 5939c8d4 files · +415 −1Browse files

Unified · Split

4 files
  1. doc/APIchanges +9 −0
  2. libavutil/fifo.c +226 −0
  3. libavutil/fifo.h +179 −0
  4. libavutil/version.h +1 −1
doc/APIchanges+9−0
@@ -14,6 +14,15 @@ libavutil: 2021-04-27 · 13 unmodified lines
1414
1515 API changes, most recent first:
1616
17+2022-02-07 - xxxxxxxxxx - lavu 57.20.100 - fifo.h
18+ Add a new FIFO API, which allows setting a FIFO element size.
19+ This API operates on these elements rather than on bytes.
20+ Add av_fifo_alloc2(), av_fifo_elem_size(), av_fifo_can_read(),
21+ av_fifo_can_write(), av_fifo_grow2(), av_fifo_drain2(), av_fifo_write(),
22+ av_fifo_write_from_cb(), av_fifo_read(), av_fifo_read_to_cb(),
23+ av_fifo_peek(), av_fifo_peek_to_cb(), av_fifo_drain2(), av_fifo_reset2(),
24+ av_fifo_freep2().
25+
1726 2022-01-26 - af94ab7c7c0 - lavu 57.19.100 - tx.h
1827 Add AV_TX_FLOAT_RDFT, AV_TX_DOUBLE_RDFT and AV_TX_INT32_RDFT.
1928
libavutil/fifo.c+226−0
@@ -26,6 +26,232 @@ · 25 unmodified lines
2626 #include "common.h"
2727 #include "fifo.h"
2828
29+struct AVFifo {
30+ uint8_t *buffer;
31+
32+ size_t elem_size, nb_elems;
33+ size_t offset_r, offset_w;
34+ // distinguishes the ambiguous situation offset_r == offset_w
35+ int is_empty;
36+};
37+
38+AVFifo *av_fifo_alloc2(size_t nb_elems, size_t elem_size,
39+ unsigned int flags)
40+{
41+ AVFifo *f;
42+ void *buffer = NULL;
43+
44+ if (!elem_size)
45+ return NULL;
46+
47+ if (nb_elems) {
48+ buffer = av_realloc_array(NULL, nb_elems, elem_size);
49+ if (!buffer)
50+ return NULL;
51+ }
52+ f = av_mallocz(sizeof(*f));
53+ if (!f) {
54+ av_free(buffer);
55+ return NULL;
56+ }
57+ f->buffer = buffer;
58+ f->nb_elems = nb_elems;
59+ f->elem_size = elem_size;
60+ f->is_empty = 1;
61+
62+ return f;
63+}
64+
65+size_t av_fifo_elem_size(const AVFifo *f)
66+{
67+ return f->elem_size;
68+}
69+
70+size_t av_fifo_can_read(const AVFifo *f)
71+{
72+ if (f->offset_w <= f->offset_r && !f->is_empty)
73+ return f->nb_elems - f->offset_r + f->offset_w;
74+ return f->offset_w - f->offset_r;
75+}
76+
77+size_t av_fifo_can_write(const AVFifo *f)
78+{
79+ return f->nb_elems - av_fifo_can_read(f);
80+}
81+
82+int av_fifo_grow2(AVFifo *f, size_t inc)
83+{
84+ uint8_t *tmp;
85+
86+ if (inc > SIZE_MAX - f->nb_elems)
87+ return AVERROR(EINVAL);
88+
89+ tmp = av_realloc_array(f->buffer, f->nb_elems + inc, f->elem_size);
90+ if (!tmp)
91+ return AVERROR(ENOMEM);
92+ f->buffer = tmp;
93+
94+ // move the data from the beginning of the ring buffer
95+ // to the newly allocated space
96+ if (f->offset_w <= f->offset_r && !f->is_empty) {
97+ const size_t copy = FFMIN(inc, f->offset_w);
98+ memcpy(tmp + f->nb_elems * f->elem_size, tmp, copy * f->elem_size);
99+ if (copy < f->offset_w) {
100+ memmove(tmp, tmp + copy * f->elem_size,
101+ (f->offset_w - copy) * f->elem_size);
102+ f->offset_w -= copy;
103+ } else
104+ f->offset_w = f->nb_elems + copy;
105+ }
106+
107+ f->nb_elems += inc;
108+
109+ return 0;
110+}
111+
112+static int fifo_write_common(AVFifo *f, const uint8_t *buf, size_t *nb_elems,
113+ AVFifoCB read_cb, void *opaque)
114+{
115+ size_t to_write = *nb_elems;
116+ size_t offset_w = f->offset_w;
117+ int ret = 0;
118+
119+ if (to_write > av_fifo_can_write(f))
120+ return AVERROR(ENOSPC);
121+
122+ while (to_write > 0) {
123+ size_t len = FFMIN(f->nb_elems - offset_w, to_write);
124+ uint8_t *wptr = f->buffer + offset_w * f->elem_size;
125+
126+ if (read_cb) {
127+ ret = read_cb(opaque, wptr, &len);
128+ if (ret < 0 || len == 0)
129+ break;
130+ } else {
131+ memcpy(wptr, buf, len * f->elem_size);
132+ buf += len * f->elem_size;
133+ }
134+ offset_w += len;
135+ if (offset_w >= f->nb_elems)
136+ offset_w = 0;
137+ to_write -= len;
138+ }
139+ f->offset_w = offset_w;
140+
141+ if (*nb_elems != to_write)
142+ f->is_empty = 0;
143+ *nb_elems -= to_write;
144+
145+ return ret;
146+}
147+
148+int av_fifo_write(AVFifo *f, const void *buf, size_t nb_elems)
149+{
150+ return fifo_write_common(f, buf, &nb_elems, NULL, NULL);
151+}
152+
153+int av_fifo_write_from_cb(AVFifo *f, AVFifoCB read_cb,
154+ void *opaque, size_t *nb_elems)
155+{
156+ return fifo_write_common(f, NULL, nb_elems, read_cb, opaque);
157+}
158+
159+static int fifo_peek_common(const AVFifo *f, uint8_t *buf, size_t *nb_elems,
160+ size_t offset, AVFifoCB write_cb, void *opaque)
161+{
162+ size_t to_read = *nb_elems;
163+ size_t offset_r = f->offset_r;
164+ size_t can_read = av_fifo_can_read(f);
165+ int ret = 0;
166+
167+ if (offset > can_read || to_read > can_read - offset) {
168+ *nb_elems = 0;
169+ return AVERROR(EINVAL);
170+ }
171+
172+ if (offset_r >= f->nb_elems - offset)
173+ offset_r -= f->nb_elems - offset;
174+ else
175+ offset_r += offset;
176+
177+ while (to_read > 0) {
178+ size_t len = FFMIN(f->nb_elems - offset_r, to_read);
179+ uint8_t *rptr = f->buffer + offset_r * f->elem_size;
180+
181+ if (write_cb) {
182+ ret = write_cb(opaque, rptr, &len);
183+ if (ret < 0 || len == 0)
184+ break;
185+ } else {
186+ memcpy(buf, rptr, len * f->elem_size);
187+ buf += len * f->elem_size;
188+ }
189+ offset_r += len;
190+ if (offset_r >= f->nb_elems)
191+ offset_r = 0;
192+ to_read -= len;
193+ }
194+
195+ *nb_elems -= to_read;
196+
197+ return ret;
198+}
199+
200+int av_fifo_read(AVFifo *f, void *buf, size_t nb_elems)
201+{
202+ int ret = fifo_peek_common(f, buf, &nb_elems, 0, NULL, NULL);
203+ av_fifo_drain2(f, nb_elems);
204+ return ret;
205+}
206+
207+int av_fifo_read_to_cb(AVFifo *f, AVFifoCB write_cb,
208+ void *opaque, size_t *nb_elems)
209+{
210+ int ret = fifo_peek_common(f, NULL, nb_elems, 0, write_cb, opaque);
211+ av_fifo_drain2(f, *nb_elems);
212+ return ret;
213+}
214+
215+int av_fifo_peek(AVFifo *f, void *buf, size_t nb_elems, size_t offset)
216+{
217+ return fifo_peek_common(f, buf, &nb_elems, offset, NULL, NULL);
218+}
219+
220+int av_fifo_peek_to_cb(AVFifo *f, AVFifoCB write_cb, void *opaque,
221+ size_t *nb_elems, size_t offset)
222+{
223+ return fifo_peek_common(f, NULL, nb_elems, offset, write_cb, opaque);
224+}
225+
226+void av_fifo_drain2(AVFifo *f, size_t size)
227+{
228+ const size_t cur_size = av_fifo_can_read(f);
229+
230+ av_assert0(cur_size >= size);
231+ if (cur_size == size)
232+ f->is_empty = 1;
233+
234+ if (f->offset_r >= f->nb_elems - size)
235+ f->offset_r -= f->nb_elems - size;
236+ else
237+ f->offset_r += size;
238+}
239+
240+void av_fifo_reset2(AVFifo *f)
241+{
242+ f->offset_r = f->offset_w = 0;
243+ f->is_empty = 1;
244+}
245+
246+void av_fifo_freep2(AVFifo **f)
247+{
248+ if (*f) {
249+ av_freep(&(*f)->buffer);
250+ av_freep(f);
251+ }
252+}
253+
254+
29255 #define OLD_FIFO_SIZE_MAX (size_t)FFMIN3(INT_MAX, UINT32_MAX, SIZE_MAX)
30256
31257 AVFifoBuffer *av_fifo_alloc_array(size_t nmemb, size_t size)
libavutil/fifo.h+179−0
@@ -28,6 +28,185 @@ · 27 unmodified lines
2828 #include "avutil.h"
2929 #include "attributes.h"
3030
31+typedef struct AVFifo AVFifo;
32+
33+/**
34+ * Callback for writing or reading from a FIFO, passed to (and invoked from) the
35+ * av_fifo_*_cb() functions. It may be invoked multiple times from a single
36+ * av_fifo_*_cb() call and may process less data than the maximum size indicated
37+ * by nb_elems.
38+ *
39+ * @param opaque the opaque pointer provided to the av_fifo_*_cb() function
40+ * @param buf the buffer for reading or writing the data, depending on which
41+ * av_fifo_*_cb function is called
42+ * @param nb_elems On entry contains the maximum number of elements that can be
43+ * read from / written into buf. On success, the callback should
44+ * update it to contain the number of elements actually written.
45+ *
46+ * @return 0 on success, a negative error code on failure (will be returned from
47+ * the invoking av_fifo_*_cb() function)
48+ */
49+typedef int AVFifoCB(void *opaque, void *buf, size_t *nb_elems);
50+
51+/**
52+ * Allocate and initialize an AVFifo with a given element size.
53+ *
54+ * @param elems initial number of elements that can be stored in the FIFO
55+ * @param elem_size Size in bytes of a single element. Further operations on
56+ * the returned FIFO will implicitly use this element size.
57+ * @param flags currently unused, must be 0
58+ *
59+ * @return newly-allocated AVFifo on success, a negative error code on failure
60+ */
61+AVFifo *av_fifo_alloc2(size_t elems, size_t elem_size,
62+ unsigned int flags);
63+
64+/**
65+ * @return Element size for FIFO operations. This element size is set at
66+ * FIFO allocation and remains constant during its lifetime
67+ */
68+size_t av_fifo_elem_size(const AVFifo *f);
69+
70+/**
71+ * @return number of elements available for reading from the given FIFO.
72+ */
73+size_t av_fifo_can_read(const AVFifo *f);
74+
75+/**
76+ * @return number of elements that can be written into the given FIFO.
77+ */
78+size_t av_fifo_can_write(const AVFifo *f);
79+
80+/**
81+ * Enlarge an AVFifo.
82+ *
83+ * On success, the FIFO will be large enough to hold exactly
84+ * inc + av_fifo_can_read() + av_fifo_can_write()
85+ * elements. In case of failure, the old FIFO is kept unchanged.
86+ *
87+ * @param f AVFifo to resize
88+ * @param inc number of elements to allocate for, in addition to the current
89+ * allocated size
90+ * @return a non-negative number on success, a negative error code on failure
91+ */
92+int av_fifo_grow2(AVFifo *f, size_t inc);
93+
94+/**
95+ * Write data into a FIFO.
96+ *
97+ * In case nb_elems > av_fifo_can_write(f), nothing is written and an error
98+ * is returned.
99+ *
100+ * @param f the FIFO buffer
101+ * @param buf Data to be written. nb_elems * av_fifo_elem_size(f) bytes will be
102+ * read from buf on success.
103+ * @param nb_elems number of elements to write into FIFO
104+ *
105+ * @return a non-negative number on success, a negative error code on failure
106+ */
107+int av_fifo_write(AVFifo *f, const void *buf, size_t nb_elems);
108+
109+/**
110+ * Write data from a user-provided callback into a FIFO.
111+ *
112+ * @param f the FIFO buffer
113+ * @param read_cb Callback supplying the data to the FIFO. May be called
114+ * multiple times.
115+ * @param opaque opaque user data to be provided to read_cb
116+ * @param nb_elems Should point to the maximum number of elements that can be
117+ * written. Will be updated to contain the number of elements
118+ * actually written.
119+ *
120+ * @return non-negative number on success, a negative error code on failure
121+ */
122+int av_fifo_write_from_cb(AVFifo *f, AVFifoCB read_cb,
123+ void *opaque, size_t *nb_elems);
124+
125+/**
126+ * Read data from a FIFO.
127+ *
128+ * In case nb_elems > av_fifo_can_read(f), nothing is read and an error
129+ * is returned.
130+ *
131+ * @param f the FIFO buffer
132+ * @param buf Buffer to store the data. nb_elems * av_fifo_elem_size(f) bytes
133+ * will be written into buf on success.
134+ * @param nb_elems number of elements to read from FIFO
135+ *
136+ * @return a non-negative number on success, a negative error code on failure
137+ */
138+int av_fifo_read(AVFifo *f, void *buf, size_t nb_elems);
139+
140+/**
141+ * Feed data from a FIFO into a user-provided callback.
142+ *
143+ * @param f the FIFO buffer
144+ * @param write_cb Callback the data will be supplied to. May be called
145+ * multiple times.
146+ * @param opaque opaque user data to be provided to write_cb
147+ * @param nb_elems Should point to the maximum number of elements that can be
148+ * read. Will be updated to contain the total number of elements
149+ * actually sent to the callback.
150+ *
151+ * @return non-negative number on success, a negative error code on failure
152+ */
153+int av_fifo_read_to_cb(AVFifo *f, AVFifoCB write_cb,
154+ void *opaque, size_t *nb_elems);
155+
156+/**
157+ * Read data from a FIFO without modifying FIFO state.
158+ *
159+ * Returns an error if an attempt is made to peek to nonexistent elements
160+ * (i.e. if offset + nb_elems is larger than av_fifo_can_read(f)).
161+ *
162+ * @param f the FIFO buffer
163+ * @param buf Buffer to store the data. nb_elems * av_fifo_elem_size(f) bytes
164+ * will be written into buf.
165+ * @param nb_elems number of elements to read from FIFO
166+ * @param offset number of initial elements to skip.
167+ *
168+ * @return a non-negative number on success, a negative error code on failure
169+ */
170+int av_fifo_peek(AVFifo *f, void *buf, size_t nb_elems, size_t offset);
171+
172+/**
173+ * Feed data from a FIFO into a user-provided callback.
174+ *
175+ * @param f the FIFO buffer
176+ * @param write_cb Callback the data will be supplied to. May be called
177+ * multiple times.
178+ * @param opaque opaque user data to be provided to write_cb
179+ * @param nb_elems Should point to the maximum number of elements that can be
180+ * read. Will be updated to contain the total number of elements
181+ * actually sent to the callback.
182+ * @param offset number of initial elements to skip; offset + *nb_elems must not
183+ * be larger than av_fifo_can_read(f).
184+ *
185+ * @return a non-negative number on success, a negative error code on failure
186+ */
187+int av_fifo_peek_to_cb(AVFifo *f, AVFifoCB write_cb, void *opaque,
188+ size_t *nb_elems, size_t offset);
189+
190+/**
191+ * Discard the specified amount of data from an AVFifo.
192+ * @param size number of elements to discard, MUST NOT be larger than
193+ * av_fifo_can_read(f)
194+ */
195+void av_fifo_drain2(AVFifo *f, size_t size);
196+
197+/*
198+ * Empty the AVFifo.
199+ * @param f AVFifo to reset
200+ */
201+void av_fifo_reset2(AVFifo *f);
202+
203+/**
204+ * Free an AVFifo and reset pointer to NULL.
205+ * @param f Pointer to an AVFifo to free. *f == NULL is allowed.
206+ */
207+void av_fifo_freep2(AVFifo **f);
208+
209+
31210 typedef struct AVFifoBuffer {
32211 uint8_t *buffer;
33212 uint8_t *rptr, *wptr, *end;
libavutil/version.h+1−1
@@ -79,7 +79,7 @@ · 78 unmodified lines
7979 */
8080
8181 #define LIBAVUTIL_VERSION_MAJOR 57
82−#define LIBAVUTIL_VERSION_MINOR 19
82+#define LIBAVUTIL_VERSION_MINOR 20
8383 #define LIBAVUTIL_VERSION_MICRO 100
8484
8585 #define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t