一个简单的RingBuffer

11 篇文章 0 订阅

  写了一个简单的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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Midas-Zhou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值