写了一个简单的RingBuffer模块。
主要函数包括: egi_ringbuffer_create() (创建RingBuffer), egi_ringbuffer_free()(释放RingBuffer), egi_ringbuffer_write()(写入数据), egi_ringbuffer_read()(读出数据)。 通过一个mutex互斥锁来同步,以实现多个线程同时写和读.。
egi_ringbuffer.h
/*---------------------------------------------------------------------------
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
"egi_ringbuffer.h"
Midas Zhou
midaszhou@yahoo.com
-------------------------------------------------------------------------*/
#ifndef __EGI_RINGBUFFER_H__
#define __EGI_RINGBUFFER_H__
#include <stdlib.h>
typedef struct {
char* buffer; /* Mem. space allocated */
size_t buffsize; /* Size of the ring buffer */
size_t datasize; /* Size of available data buffered */
off_t pw; /* Offset to write position (Write pointer) */
off_t pr; /* Offset to read position (Read pointer) */
pthread_mutex_t ring_mutex;
} EGI_RINGBUFFER;
EGI_RINGBUFFER *egi_ringbuffer_create(size_t buffsize);
void egi_ringbuffer_free(EGI_RINGBUFFER **ringbuf);
size_t egi_ringbuffer_write(EGI_RINGBUFFER *ringbuf, void *src, size_t len);
size_t egi_ringbuffer_read(EGI_RINGBUFFER *ringbuf, void *dest, size_t len);
#endif
egi_ringbuffer.c
/*------------------------------------------------------------------
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
A simple ring buffer.
Journal:
2021-11-15:
1. Create the file and functions:
egi_ringbuffer_create() egi_ringbuffer_free()
egi_ringbuffer_write() egi_ringbuffer_read()
Midas Zhou
midaszhou@yahoo.com
------------------------------------------------------------------*/
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
//#include <egi_debug.h>
#include "egi_ringbuffer.h"
/*-------------------------------------------
Create an EGI ring buffer
@buffsize: Size of the ring buffer.
Return:
Pointer to EGI_RINGBUFFER OK
NULL Fails
-------------------------------------------*/
EGI_RINGBUFFER *egi_ringbuffer_create(size_t buffsize)
{
EGI_RINGBUFFER *ringbuf=calloc(1, sizeof(EGI_RINGBUFFER));
if(ringbuf==NULL) {
//egi_dperr("Fail to calloc ringbuf!");
return NULL;
}
ringbuf->buffer=calloc(1, buffsize);
if(ringbuf->buffer==NULL) {
//egi_dperr("Fail to calloc ringbuf->buffer!");
free(ringbuf);
return NULL;
}
ringbuf->buffsize=buffsize;
if(pthread_mutex_init(&ringbuf->ring_mutex,NULL) != 0)
{
//egi_dperr("Fail init ring_mutex!");
free(ringbuf->buffer);
free(ringbuf);
return NULL;
}
return ringbuf;
}
/*-------------------------------------------
Free an EGI ring buffer.
---------------------------------------------*/
void egi_ringbuffer_free(EGI_RINGBUFFER **ringbuf)
{
if(ringbuf==NULL || *ringbuf==NULL)
return;
/* Hope there is no other user */
if(pthread_mutex_lock(&(*ringbuf)->ring_mutex) !=0 )
//egi_dperr("Fail lock ring_mutex");
#if 1 /* "It shall be safe to destroy an initialized mutex that is unlocked. Attempting to
* destroy a locked mutex results in undefined behavior" --- POSIX man/Linux man 3
*/
if( pthread_mutex_unlock(&(*ringbuf)->ring_mutex) != 0)
//egi_dperr("Fail unlock ring_mutex");
#endif
/* Destroy thread mutex lock */
if(pthread_mutex_destroy(&(*ringbuf)->ring_mutex) !=0 )
//egi_dperr("Fail destroy ring_mutex");
/* Free mem */
free((*ringbuf)->buffer);
free(*ringbuf);
*ringbuf=NULL;
}
/*-------------------------------------------
Wrtie data into a ring buffer.
@ringbuf: Pointer to an ring buffer.
@src: Soure of data.
@len: Length of source data.
Note:
1. Assume pw will NOT surpass pr!
Return:
>0 Actual size of data written
=0 Ringbuffer is full.
OR len==0.
<0 Fails.
-------------------------------------------*/
size_t egi_ringbuffer_write(EGI_RINGBUFFER *ringbuf, void *src, size_t len)
{
int ret=0;
size_t lenx=0;
if(ringbuf==NULL || src==NULL)
return -1;
/* Get mutex lock */
if(pthread_mutex_lock(&ringbuf->ring_mutex) !=0 ){
//egi_dperr("Fail to lock ring_mutex!");
return -1;
}
/* ------ >>> Critical Zone */
/* Check available space in the buffer. */
/* Case_1. Ringbuffer is full! (pw==pr) */
if( ringbuf->datasize == ringbuf->buffsize ) {
/* Check */
if(ringbuf->pw!=ringbuf->pr) {
//egi_dpstd("ERROR! datasize==buffsize while pw!=pr !\n");
ret=-2; goto END_FUNC;
}
else {
ret=0; goto END_FUNC;
}
}
/* Case_2. Ringbuffer has enough space: */
if( ringbuf->datasize + len <= ringbuf->buffsize ) {
/* 2.1 Pw is not necessary to cross the ring END/HEAD */
if( ringbuf->buffsize - ringbuf->pw >= len ) {
memcpy(ringbuf->buffer+ringbuf->pw, src, len);
/* update pw */
ringbuf->pw += len;
if(ringbuf->pw==ringbuf->buffsize)
ringbuf->pw=0;
/* update datasize */
ringbuf->datasize +=len;
ret=len; goto END_FUNC;
}
/* 2.2 pw is to cross the ring END/HEAD */
else {
/* Write to buffer END/HEAD */
lenx = ringbuf->buffsize-ringbuf->pw;
memcpy(ringbuf->buffer+ringbuf->pw, src, lenx);
/* Write remaining from HEAD/END */
memcpy(ringbuf->buffer+0, src+lenx, len-lenx);
/* Update pw */
ringbuf->pw=len-lenx;
/* Update datasize */
ringbuf->datasize +=len;
ret=len; goto END_FUNC;
}
}
/* Case_3. Ringbuffer has NOT enough space: */
else { /* if( ringbuf->datasize + len > ringbuf->buffsize ) */
/* 3.1 Pw is not necessary to cross the ring head/end */
if( ringbuf->pw < ringbuf->pr ) {
/* Write till to pr */
lenx=ringbuf->pr-ringbuf->pw;
memcpy(ringbuf->buffer+ringbuf->pw, src, lenx);
/* Update pw */
ringbuf->pw = ringbuf->pr;
/* update datasize */
ringbuf->datasize +=lenx;
ret=lenx; goto END_FUNC;
}
/* 3.2 pw is to cross the ring head/end */
else { /* pw > pr */
/* Write till to END/HEAD */
lenx=ringbuf->buffsize-ringbuf->pw;
memcpy(ringbuf->buffer+ringbuf->pw, src, lenx);
/* Write remaining from HEAD/END to pr */
memcpy(ringbuf->buffer+0, src+lenx, ringbuf->pr);
/* Update pw */
ringbuf->pw = ringbuf->pr;
/* update datasize */
ringbuf->datasize +=(lenx+ringbuf->pr);
ret=lenx+ringbuf->pr; goto END_FUNC;
}
}
END_FUNC:
/* ------- <<< Critical Zone */
/* put mutex lock */
pthread_mutex_unlock(&ringbuf->ring_mutex);
return ret;
}
/*-------------------------------------------
Read data out from a ring buffer.
@ringbuf: Pointer to an ring buffer.
@dest: Destination of data.
@len: Expected length of data to read.
Note:
1. Assume pw will NOT surpass pr!
Return:
>0 Actual size of data read out.
=0 Ringbuffer is Empty.
OR len==0.
<0 Fails.
-------------------------------------------*/
size_t egi_ringbuffer_read(EGI_RINGBUFFER *ringbuf, void *dest, size_t len)
{
int ret=0;
size_t lenx=0;
if(ringbuf==NULL || dest==NULL)
return -1;
/* Get mutex lock */
if(pthread_mutex_lock(&ringbuf->ring_mutex) !=0 ){
//egi_dperr("Fail to lock ring_mutex!");
return -1;
}
/* ------ >>> Critical Zone */
/* Check available space in the buffer. */
/* Case_1. Ringbuffer has Empty data! (pw==pr) */
if( ringbuf->datasize==0 ) {
/* Check */
if(ringbuf->pw!=ringbuf->pr) {
//egi_dpstd("ERROR! datasize==0 while pw!=pr !\n");
ret=-2; goto END_FUNC;
}
else {
ret=0; goto END_FUNC;
}
}
/* Case_2. Ringbuffer has enough data: */
if( ringbuf->datasize >= len ) {
/* 2.1 Pr is not necessary to cross the ring END/HEAD */
if( ringbuf->buffsize - ringbuf->pr >= len ) {
memcpy(dest, ringbuf->buffer+ringbuf->pr, len);
/* update pr */
ringbuf->pr += len;
if(ringbuf->pr==ringbuf->buffsize)
ringbuf->pr=0;
/* update datasize */
ringbuf->datasize -=len;
ret=len; goto END_FUNC;
}
/* 2.2 pr is to cross the ring END/HEAD */
else {
/* Read till to buffer END/HEAD */
lenx = ringbuf->buffsize-ringbuf->pr;
memcpy(dest, ringbuf->buffer+ringbuf->pr, lenx);
/* Read remaining from HEAD/END */
memcpy(dest+lenx, ringbuf->buffer+0, len-lenx);
/* Update pr */
ringbuf->pr=len-lenx;
/* Update datasize */
ringbuf->datasize -=len;
ret=len; goto END_FUNC;
}
}
/* Case_3. Ringbuffer has NOT enough data: */
else { /* if( ringbuf->datasize<len ) */
/* 3.1 pr is NOT necessary to cross the ring head/end */
if( ringbuf->pr < ringbuf->pw ) {
/* Read till to pw */
lenx=ringbuf->pw-ringbuf->pr;
memcpy(dest, ringbuf->buffer+ringbuf->pr, lenx);
/* Update pr */
ringbuf->pr = ringbuf->pw;
/* update datasize */
ringbuf->datasize -=lenx;
ret=lenx; goto END_FUNC;
}
/* 3.2 pr is to cross the ring head/end */
else { /* pr > pw */
/* Write till to END/HEAD */
lenx=ringbuf->buffsize-ringbuf->pr;
memcpy(dest, ringbuf->buffer+ringbuf->pr, lenx);
/* Write remaining from HEAD/END to pw */
memcpy(dest, ringbuf->buffer+0, ringbuf->pw);
/* Update pr */
ringbuf->pr = ringbuf->pw;
/* update datasize */
ringbuf->datasize -=(lenx+ringbuf->pw);
ret=lenx+ringbuf->pw; goto END_FUNC;
}
}
END_FUNC:
/* ------- <<< Critical Zone */
/* put mutex lock */
pthread_mutex_unlock(&ringbuf->ring_mutex);
return ret;
}
一个简单的测试程序:
test_ringbuff.c
/*------------------------------------------------------------------
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
Midas Zhou
midaszhou@yahoo.com
https://github.com/widora/wegi
------------------------------------------------------------------*/
#include <stdio.h>
#include <unistd.h>
#include "egi_ringbuffer.h"
int main(void)
{
char data[100];
int inBytes=100; /* 每次写入数据量 */
int outBytes=50; /* 每次读出数据量 */
int ret;
EGI_RINGBUFFER *ringbuff=NULL;
ringbuff=egi_ringbuffer_create(4096);
while(1) {
/* Write into the ringbuffer */
if( (ret=egi_ringbuffer_write(ringbuff, data, inBytes)) <inBytes ) {
printf("Ringbuffer overrun... write %d of %d Bytes.\n", ret,inBytes);
/* Adjust speed */
inBytes=50;
outBytes=100;
sleep(1);
}
/* Read out from the ringbuffer */
if( (ret=egi_ringbuffer_read(ringbuff, data, outBytes)) <outBytes ) {
printf("Ringbuffer underrun... read %d of %d Bytes.\n", ret,outBytes);
/* Adjust speed */
inBytes=100;
outBytes=50;
sleep(1);
}
printf("Ringbuff datasize=%zu, pw=%jd, pr=%jd\n", ringbuff->datasize, ringbuff->pw, ringbuff->pr);
}
egi_ringbuffer_free(&ringbuff);
return 0;
}
最后,把它装配到mini流播放器中应用起来.....OK