Commit | Line | Data |
---|---|---|
2ba45a60 DM |
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 | ||
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 | ||
43 | AVFifoBuffer *av_fifo_alloc(unsigned int size) | |
44 | { | |
45 | void *buffer = av_malloc(size); | |
46 | return fifo_alloc_common(buffer, size); | |
47 | } | |
48 | ||
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 | ||
55 | void av_fifo_free(AVFifoBuffer *f) | |
56 | { | |
57 | if (f) { | |
58 | av_freep(&f->buffer); | |
59 | av_free(f); | |
60 | } | |
61 | } | |
62 | ||
63 | void av_fifo_freep(AVFifoBuffer **f) | |
64 | { | |
65 | if (f) { | |
66 | av_fifo_free(*f); | |
67 | *f = NULL; | |
68 | } | |
69 | } | |
70 | ||
71 | void av_fifo_reset(AVFifoBuffer *f) | |
72 | { | |
73 | f->wptr = f->rptr = f->buffer; | |
74 | f->wndx = f->rndx = 0; | |
75 | } | |
76 | ||
77 | int av_fifo_size(FF_CONST_AVUTIL53 AVFifoBuffer *f) | |
78 | { | |
79 | return (uint32_t)(f->wndx - f->rndx); | |
80 | } | |
81 | ||
82 | int av_fifo_space(FF_CONST_AVUTIL53 AVFifoBuffer *f) | |
83 | { | |
84 | return f->end - f->buffer - av_fifo_size(f); | |
85 | } | |
86 | ||
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 | ||
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*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) */ | |
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 | if (func(src, wptr, len) <= 0) | |
133 | break; | |
134 | } else { | |
135 | memcpy(wptr, src, len); | |
136 | src = (uint8_t *)src + len; | |
137 | } | |
138 | // Write memory barrier needed for SMP here in theory | |
139 | wptr += len; | |
140 | if (wptr >= f->end) | |
141 | wptr = f->buffer; | |
142 | wndx += len; | |
143 | size -= len; | |
144 | } while (size > 0); | |
145 | f->wndx= wndx; | |
146 | f->wptr= wptr; | |
147 | return total - size; | |
148 | } | |
149 | ||
150 | int av_fifo_generic_read(AVFifoBuffer *f, void *dest, int buf_size, | |
151 | void (*func)(void *, void *, int)) | |
152 | { | |
153 | // Read memory barrier needed for SMP here in theory | |
154 | do { | |
155 | int len = FFMIN(f->end - f->rptr, buf_size); | |
156 | if (func) | |
157 | func(dest, f->rptr, len); | |
158 | else { | |
159 | memcpy(dest, f->rptr, len); | |
160 | dest = (uint8_t *)dest + len; | |
161 | } | |
162 | // memory barrier needed for SMP here in theory | |
163 | av_fifo_drain(f, len); | |
164 | buf_size -= len; | |
165 | } while (buf_size > 0); | |
166 | return 0; | |
167 | } | |
168 | ||
169 | /** Discard data from the FIFO. */ | |
170 | void av_fifo_drain(AVFifoBuffer *f, int size) | |
171 | { | |
172 | av_assert2(av_fifo_size(f) >= size); | |
173 | f->rptr += size; | |
174 | if (f->rptr >= f->end) | |
175 | f->rptr -= f->end - f->buffer; | |
176 | f->rndx += size; | |
177 | } | |
178 | ||
179 | #ifdef TEST | |
180 | ||
181 | int main(void) | |
182 | { | |
183 | /* create a FIFO buffer */ | |
184 | AVFifoBuffer *fifo = av_fifo_alloc(13 * sizeof(int)); | |
185 | int i, j, n; | |
186 | ||
187 | /* fill data */ | |
188 | for (i = 0; av_fifo_space(fifo) >= sizeof(int); i++) | |
189 | av_fifo_generic_write(fifo, &i, sizeof(int), NULL); | |
190 | ||
191 | /* peek at FIFO */ | |
192 | n = av_fifo_size(fifo) / sizeof(int); | |
193 | for (i = -n + 1; i < n; i++) { | |
194 | int *v = (int *)av_fifo_peek2(fifo, i * sizeof(int)); | |
195 | printf("%d: %d\n", i, *v); | |
196 | } | |
197 | printf("\n"); | |
198 | ||
199 | /* read data */ | |
200 | for (i = 0; av_fifo_size(fifo) >= sizeof(int); i++) { | |
201 | av_fifo_generic_read(fifo, &j, sizeof(int), NULL); | |
202 | printf("%d ", j); | |
203 | } | |
204 | printf("\n"); | |
205 | ||
206 | av_fifo_free(fifo); | |
207 | ||
208 | return 0; | |
209 | } | |
210 | ||
211 | #endif |