之前去騰訊面試的時候被問到的一道題目:實現一個循環緩衝區(不帶互斥鎖)。仔細一想,其實和循環隊列的思想一模一樣,還是怪自己數據結構沒學好阿(其實我是學通信的,所以最近在惡補)。還是先上代碼
頭文件如下,CircleBuffer.h
#ifndef _CIRCLE_BUFFER_H_
#define _CIRCLE_BUFFER_H_
//basic data type rename
typedef unsigned char uchar8;
typedef char char8;
typedef unsigned int uint32;
typedef int int32;
//definition of CircleBuffer
class CircleBuffer
{
public:
CircleBuffer(int sz);//constructor
~CircleBuffer();//destructor
void ClearBuffer();//clear the buffer to init state, just modify the indicator
bool IsBufferFull();//to judge if the buffer is full
bool IsBufferEmpty();//to judge if the buffer is empty
uint32 BufferLength();//calculate buffer's length
uint32 ReadBuffer(uchar8 *data, uint32 len);//read data from the buffer, return the actual data length
uint32 WriteBuffer(uchar8 *data, uint32 len);//write data into the buffer, return the actual written-in data length
void ShowBuffer();//print single element one by one starting from pread, not from buf[0] for sure
private:
uchar8 *buf;//used to store the data;
uint32 size;//indicates the size of the buffer;
uint32 pwrite;//indicates the start position of a write operation;
uint32 pread;//indicates the start position of a read operation;
inline uint32 NextPos(uint32 cur);//get the next pos of write or read
};
#endif //_CIRCLE_BUFFER_H_
源文件如下,CircleBuffer.cpp
#include <iostream>
#include "CircleBuffer.h"
CircleBuffer::CircleBuffer(int sz)
{
size = sz+1;
buf = new uchar8[sz+1];
if(buf == 0)
{
std::cout << __FUNCTION__ << __LINE__ << ":new failed..." << std::endl;
}
pwrite = 0;
pread = 0;
}
CircleBuffer::~CircleBuffer()
{
if(buf)
{
delete[] buf;
buf = 0;
}
}
void CircleBuffer::ClearBuffer()
{
pwrite = 0;
pread = 0;
}
inline uint32 CircleBuffer::NextPos(uint32 cur)
{
return (cur + 1) % size;
}
bool CircleBuffer::IsBufferFull()
{
if(NextPos(pwrite) == pread)
return true;
return false;
}
bool CircleBuffer::IsBufferEmpty()
{
if(pwrite == pread)
return true;
return false;
}
uint32 CircleBuffer::BufferLength()
{
return (pwrite - pread + size) % size;
}
uint32 CircleBuffer::ReadBuffer(uchar8 *data, uint32 len)
{
uint32 cur = pread;
uint32 i = 0;
while(cur != pwrite && i < len)
{
data[i++] = buf[cur];
cur = NextPos(cur);
}
pread = cur;
return i;
}
uint32 CircleBuffer::WriteBuffer(uchar8 *data, uint32 len)
{
uint32 i = 0;
uint32 cur = pwrite;
while(NextPos(cur) != pread && i < len)
{
buf[cur] = data[i++];
cur = NextPos(cur);
}
if(i < len)
{
std::cout << "buffer full ~!!!, lost " << len-i << " bytes" << std::endl;
}
pwrite = cur;
return i;
}
void CircleBuffer::ShowBuffer()
{
uint32 start = pread;
while(start != pwrite)
{
std::cout << buf[start];
start = NextPos(start);
}
printf("^\n");
}
其核心思想就是循環隊列。對於一個大小爲size的緩衝區,在分配內存時,多分配一個字節。這樣,Buffer寫滿時,寫指針就在讀指針的前一個位置(邏輯上),即判滿條件;而判空條件則是寫指針和讀指針在同一個位置。最後多餘的一個字節不存儲數據,僅僅用來區分buffer是滿還是空。由於是一個循環緩衝區,所以對遍歷指針cur不能用++這樣的操作,我在上面封裝了一個求當前指針位置的下一個位置的函數uint32 NextPos(uint32 cur); 聲明爲內聯函數,以提高效率。
由於測試環境不夠複雜,以上代碼只在我的ubuntu os電腦上作了一個簡單的測試:兩個線程,一個讀,一個寫,寫得更快的時候會出現buffer滿的提示。打印buffer的長度爲構造函數指定的長度,pread和pwrite的位置也正確,調用ShowBuffer挨個打印出的字符順序也都正確(從pread開始)。
小弟經驗不足,僅供參考。並希望各位朋友指出不足之處。