1 /*
2  * a very simple circular buffer FIFO implementation
3  * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
4  * Copyright (c) 2006 Roman Shaposhnik
5  *
6  * This file is part of FFmpeg.
7  *
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.
12  *
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.
17  *
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
21  */
22 
23 #include "avassert.h"
24 #include "common.h"
25 #include "fifo.h"
26 
fifo_alloc_common(void * buffer,size_t size)27 static AVFifoBuffer *fifo_alloc_common(void *buffer, size_t size)
28 {
29     AVFifoBuffer *f;
30     if (!buffer)
31         return NULL;
32     f = av_mallocz(sizeof(AVFifoBuffer));
33     if (!f) {
34         av_free(buffer);
35         return NULL;
36     }
37     f->buffer = buffer;
38     f->end    = f->buffer + size;
39     av_fifo_reset(f);
40     return f;
41 }
42 
av_fifo_alloc(unsigned int size)43 AVFifoBuffer *av_fifo_alloc(unsigned int size)
44 {
45     void *buffer = av_malloc(size);
46     return fifo_alloc_common(buffer, size);
47 }
48 
av_fifo_alloc_array(size_t nmemb,size_t size)49 AVFifoBuffer *av_fifo_alloc_array(size_t nmemb, size_t size)
50 {
51     void *buffer = av_malloc_array(nmemb, size);
52     return fifo_alloc_common(buffer, nmemb * size);
53 }
54 
av_fifo_free(AVFifoBuffer * f)55 void av_fifo_free(AVFifoBuffer *f)
56 {
57     if (f) {
58         av_freep(&f->buffer);
59         av_free(f);
60     }
61 }
62 
av_fifo_freep(AVFifoBuffer ** f)63 void av_fifo_freep(AVFifoBuffer **f)
64 {
65     if (f) {
66         av_fifo_free(*f);
67         *f = NULL;
68     }
69 }
70 
av_fifo_reset(AVFifoBuffer * f)71 void av_fifo_reset(AVFifoBuffer *f)
72 {
73     f->wptr = f->rptr = f->buffer;
74     f->wndx = f->rndx = 0;
75 }
76 
av_fifo_size(const AVFifoBuffer * f)77 int av_fifo_size(const AVFifoBuffer *f)
78 {
79     return (uint32_t)(f->wndx - f->rndx);
80 }
81 
av_fifo_space(const AVFifoBuffer * f)82 int av_fifo_space(const AVFifoBuffer *f)
83 {
84     return f->end - f->buffer - av_fifo_size(f);
85 }
86 
av_fifo_realloc2(AVFifoBuffer * f,unsigned int new_size)87 int av_fifo_realloc2(AVFifoBuffer *f, unsigned int new_size)
88 {
89     unsigned int old_size = f->end - f->buffer;
90 
91     if (old_size < new_size) {
92         int len          = av_fifo_size(f);
93         AVFifoBuffer *f2 = av_fifo_alloc(new_size);
94 
95         if (!f2)
96             return AVERROR(ENOMEM);
97         av_fifo_generic_read(f, f2->buffer, len, NULL);
98         f2->wptr += len;
99         f2->wndx += len;
100         av_free(f->buffer);
101         *f = *f2;
102         av_free(f2);
103     }
104     return 0;
105 }
106 
av_fifo_grow(AVFifoBuffer * f,unsigned int size)107 int av_fifo_grow(AVFifoBuffer *f, unsigned int size)
108 {
109     unsigned int old_size = f->end - f->buffer;
110     if(size + (unsigned)av_fifo_size(f) < size)
111         return AVERROR(EINVAL);
112 
113     size += av_fifo_size(f);
114 
115     if (old_size < size)
116         return av_fifo_realloc2(f, FFMAX(size, 2*old_size));
117     return 0;
118 }
119 
120 /* src must NOT be const as it can be a context for func that may need
121  * updating (like a pointer or byte counter) */
av_fifo_generic_write(AVFifoBuffer * f,void * src,int size,int (* func)(void *,void *,int))122 int av_fifo_generic_write(AVFifoBuffer *f, void *src, int size,
123                           int (*func)(void *, void *, int))
124 {
125     int total = size;
126     uint32_t wndx= f->wndx;
127     uint8_t *wptr= f->wptr;
128 
129     do {
130         int len = FFMIN(f->end - wptr, size);
131         if (func) {
132             len = func(src, wptr, len);
133             if (len <= 0)
134                 break;
135         } else {
136             memcpy(wptr, src, len);
137             src = (uint8_t *)src + len;
138         }
139 // Write memory barrier needed for SMP here in theory
140         wptr += len;
141         if (wptr >= f->end)
142             wptr = f->buffer;
143         wndx    += len;
144         size    -= len;
145     } while (size > 0);
146     f->wndx= wndx;
147     f->wptr= wptr;
148     return total - size;
149 }
150 
av_fifo_generic_peek_at(AVFifoBuffer * f,void * dest,int offset,int buf_size,void (* func)(void *,void *,int))151 int av_fifo_generic_peek_at(AVFifoBuffer *f, void *dest, int offset, int buf_size, void (*func)(void*, void*, int))
152 {
153     uint8_t *rptr = f->rptr;
154 
155     av_assert2(offset >= 0);
156 
157     /*
158      * *ndx are indexes modulo 2^32, they are intended to overflow,
159      * to handle *ndx greater than 4gb.
160      */
161     av_assert2(buf_size + (unsigned)offset <= f->wndx - f->rndx);
162 
163     if (offset >= f->end - rptr)
164         rptr += offset - (f->end - f->buffer);
165     else
166         rptr += offset;
167 
168     while (buf_size > 0) {
169         int len;
170 
171         if (rptr >= f->end)
172             rptr -= f->end - f->buffer;
173 
174         len = FFMIN(f->end - rptr, buf_size);
175         if (func)
176             func(dest, rptr, len);
177         else {
178             memcpy(dest, rptr, len);
179             dest = (uint8_t *)dest + len;
180         }
181 
182         buf_size -= len;
183         rptr     += len;
184     }
185 
186     return 0;
187 }
188 
av_fifo_generic_peek(AVFifoBuffer * f,void * dest,int buf_size,void (* func)(void *,void *,int))189 int av_fifo_generic_peek(AVFifoBuffer *f, void *dest, int buf_size,
190                          void (*func)(void *, void *, int))
191 {
192 // Read memory barrier needed for SMP here in theory
193     uint8_t *rptr = f->rptr;
194 
195     do {
196         int len = FFMIN(f->end - rptr, buf_size);
197         if (func)
198             func(dest, rptr, len);
199         else {
200             memcpy(dest, rptr, len);
201             dest = (uint8_t *)dest + len;
202         }
203 // memory barrier needed for SMP here in theory
204         rptr += len;
205         if (rptr >= f->end)
206             rptr -= f->end - f->buffer;
207         buf_size -= len;
208     } while (buf_size > 0);
209 
210     return 0;
211 }
212 
av_fifo_generic_read(AVFifoBuffer * f,void * dest,int buf_size,void (* func)(void *,void *,int))213 int av_fifo_generic_read(AVFifoBuffer *f, void *dest, int buf_size,
214                          void (*func)(void *, void *, int))
215 {
216 // Read memory barrier needed for SMP here in theory
217     do {
218         int len = FFMIN(f->end - f->rptr, buf_size);
219         if (func)
220             func(dest, f->rptr, len);
221         else {
222             memcpy(dest, f->rptr, len);
223             dest = (uint8_t *)dest + len;
224         }
225 // memory barrier needed for SMP here in theory
226         av_fifo_drain(f, len);
227         buf_size -= len;
228     } while (buf_size > 0);
229     return 0;
230 }
231 
232 /** Discard data from the FIFO. */
av_fifo_drain(AVFifoBuffer * f,int size)233 void av_fifo_drain(AVFifoBuffer *f, int size)
234 {
235     av_assert2(av_fifo_size(f) >= size);
236     f->rptr += size;
237     if (f->rptr >= f->end)
238         f->rptr -= f->end - f->buffer;
239     f->rndx += size;
240 }
241