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 Libav.
7 *
8 * Libav 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 * Libav 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 Libav; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22#include "common.h"
23#include "fifo.h"
24
25AVFifoBuffer *av_fifo_alloc(unsigned int size)
26{
27    AVFifoBuffer *f= av_mallocz(sizeof(AVFifoBuffer));
28    if(!f)
29        return NULL;
30    f->buffer = av_malloc(size);
31    f->end = f->buffer + size;
32    av_fifo_reset(f);
33    if (!f->buffer)
34        av_freep(&f);
35    return f;
36}
37
38void av_fifo_free(AVFifoBuffer *f)
39{
40    if(f){
41        av_free(f->buffer);
42        av_free(f);
43    }
44}
45
46void av_fifo_reset(AVFifoBuffer *f)
47{
48    f->wptr = f->rptr = f->buffer;
49    f->wndx = f->rndx = 0;
50}
51
52int av_fifo_size(AVFifoBuffer *f)
53{
54    return (uint32_t)(f->wndx - f->rndx);
55}
56
57int av_fifo_space(AVFifoBuffer *f)
58{
59    return f->end - f->buffer - av_fifo_size(f);
60}
61
62int av_fifo_realloc2(AVFifoBuffer *f, unsigned int new_size) {
63    unsigned int old_size= f->end - f->buffer;
64
65    if(old_size < new_size){
66        int len= av_fifo_size(f);
67        AVFifoBuffer *f2= av_fifo_alloc(new_size);
68
69        if (!f2)
70            return -1;
71        av_fifo_generic_read(f, f2->buffer, len, NULL);
72        f2->wptr += len;
73        f2->wndx += len;
74        av_free(f->buffer);
75        *f= *f2;
76        av_free(f2);
77    }
78    return 0;
79}
80
81// src must NOT be const as it can be a context for func that may need updating (like a pointer or byte counter)
82int av_fifo_generic_write(AVFifoBuffer *f, void *src, int size, int (*func)(void*, void*, int))
83{
84    int total = size;
85    do {
86        int len = FFMIN(f->end - f->wptr, size);
87        if(func) {
88            if(func(src, f->wptr, len) <= 0)
89                break;
90        } else {
91            memcpy(f->wptr, src, len);
92            src = (uint8_t*)src + len;
93        }
94// Write memory barrier needed for SMP here in theory
95        f->wptr += len;
96        if (f->wptr >= f->end)
97            f->wptr = f->buffer;
98        f->wndx += len;
99        size -= len;
100    } while (size > 0);
101    return total - size;
102}
103
104
105int av_fifo_generic_read(AVFifoBuffer *f, void *dest, int buf_size, void (*func)(void*, void*, int))
106{
107// Read memory barrier needed for SMP here in theory
108    do {
109        int len = FFMIN(f->end - f->rptr, buf_size);
110        if(func) func(dest, f->rptr, len);
111        else{
112            memcpy(dest, f->rptr, len);
113            dest = (uint8_t*)dest + len;
114        }
115// memory barrier needed for SMP here in theory
116        av_fifo_drain(f, len);
117        buf_size -= len;
118    } while (buf_size > 0);
119    return 0;
120}
121
122/** Discard data from the FIFO. */
123void av_fifo_drain(AVFifoBuffer *f, int size)
124{
125    f->rptr += size;
126    if (f->rptr >= f->end)
127        f->rptr -= f->end - f->buffer;
128    f->rndx += size;
129}
130
131#ifdef TEST
132
133#undef printf
134
135int main(void)
136{
137    /* create a FIFO buffer */
138    AVFifoBuffer *fifo = av_fifo_alloc(13 * sizeof(int));
139    int i, j, n;
140
141    /* fill data */
142    for (i = 0; av_fifo_space(fifo) >= sizeof(int); i++)
143        av_fifo_generic_write(fifo, &i, sizeof(int), NULL);
144
145    /* peek at FIFO */
146    n = av_fifo_size(fifo)/sizeof(int);
147    for (i = -n+1; i < n; i++) {
148        int *v = (int *)av_fifo_peek2(fifo, i*sizeof(int));
149        printf("%d: %d\n", i, *v);
150    }
151    printf("\n");
152
153    /* read data */
154    for (i = 0; av_fifo_size(fifo) >= sizeof(int); i++) {
155        av_fifo_generic_read(fifo, &j, sizeof(int), NULL);
156        printf("%d ", j);
157    }
158    printf("\n");
159
160    av_fifo_free(fifo);
161
162    return 0;
163}
164
165#endif
166