版权声明:本文为灿哥哥http://blog.csdn.net/caoshangpa原创文章,转载请标明出处。 https://blog.csdn.net/caoshangpa/article/details/52966954

一个环形buffer,在尾部追加数据,从头部读取数据,适合用作IO的缓冲区。

详细介绍可参考:https://en.wikipedia.org/wiki/Circular_buffer

一.使用QList和QByteArray

这个方法参考的是Qt源码中的QRingBuffer类,这个类不是Qt API的一部分,所以Qt助手里是查不到的,它的存在只是为了服务其他的源码。

QRingBuffer的源文件在D:\Qt\Qt5.7.0\5.7\Src\qtbase\src\corelib\tools目录中,由qringbuffer_p.h和qringbuffer.cpp实现。

QRingBuffer实现的环形缓冲区大概如下图所示。

Qt实现环形缓冲区的两种方法

qringbuffer.h

  1. #ifndef QRINGBUFFER_P_H

  2. #define QRINGBUFFER_P_H

  3.  
  4. #include <QByteArray>

  5. #include <QList>

  6.  
  7. #ifndef QRINGBUFFER_CHUNKSIZE

  8. #define QRINGBUFFER_CHUNKSIZE 4096

  9. #endif

  10. enum

  11. {

  12. //1G-1字节

  13. MaxAllocSize = (1 << (std::numeric_limits<int>::digits - 1)) - 1

  14. };

  15.  
  16. enum

  17. {

  18. //1G-1-16字节

  19. MaxByteArraySize = MaxAllocSize - sizeof(QtPrivate::remove_pointer<QByteArray::DataPtr>::type)

  20. };

  21.  
  22.  
  23. class QRingBuffer

  24. {

  25. public:

  26. //默认分配QRINGBUFFER_CHUNKSIZE大小的buffer

  27. QRingBuffer(int growth = QRINGBUFFER_CHUNKSIZE) :

  28. head(0), tail(0), tailBuffer(0), basicBlockSize(growth), bufferSize(0) { }

  29. ~QRingBuffer(){}

  30. //获取环形缓冲区指定位置的指针

  31. //length,输出这个指定位置到缓冲区结尾的长度

  32. char *readPointerAtPosition(qint64 pos, qint64 &length);

  33. //申请空间:从尾开始,返回新空间的指针

  34. char *reserve(qint64 bytes);

  35. //申请空间:从头开始,返回新空间的指针

  36. char *reserveFront(qint64 bytes);

  37. //缩短空间

  38. void truncate(qint64 pos)

  39. {

  40. if (pos < bufferSize)

  41. chop(bufferSize - pos);

  42. }

  43. //判断buffers数据是否为空

  44. bool isEmpty()

  45. {

  46. return bufferSize == 0;

  47. }

  48. //从头读取一个字符,并转换为int返回

  49. int getChar()

  50. {

  51. if (isEmpty())

  52. return -1;

  53. char c = *readPointer();

  54. free(1);

  55. return int(uchar(c));

  56. }

  57. //在缓冲区尾部添加字符

  58. void putChar(char c)

  59. {

  60. char *ptr = reserve(1);

  61. *ptr = c;

  62. }

  63. //在缓冲区头部添加字符

  64. void ungetChar(char c)

  65. {

  66. if (head > 0) {

  67. --head;

  68. buffers.first()[head] = c;

  69. ++bufferSize;

  70. } else {

  71. char *ptr = reserveFront(1);

  72. *ptr = c;

  73. }

  74. }

  75. //清空缓冲区

  76. void clear();

  77. //读取maxLength长度数据到data中,如果buffers中的数据少于maxLength,则读取所有数据,

  78. //返回读取数据的长度

  79. qint64 read(char *data, qint64 maxLength);

  80. //读取buffers中的第一个buffer

  81. QByteArray read();

  82. //从指定位置pos拷贝maxLength长度的数据到data中

  83. //返回实际截取的数据长度

  84. qint64 peek(char *data, qint64 maxLength, qint64 pos = 0);

  85. //扩展最后一个buffer

  86. void append(const char *data, qint64 size);

  87. //在最后添加一个新buffer

  88. void append(const QByteArray &qba);

  89. //从头释放lenght长度空间,一般需要配合reserve使用

  90. qint64 skip(qint64 length)

  91. {

  92. qint64 bytesToSkip = qMin(length, bufferSize);

  93.  
  94. free(bytesToSkip);

  95. return bytesToSkip;

  96. }

  97. //从尾释放length长度空间,一般需要配合reserve使用

  98. void chop(qint64 length);

  99. //读取一行,包括该行的结束标志'\n'

  100. qint64 readLine(char *data, qint64 maxLength);

  101.  
  102. bool canReadLine()

  103. {

  104. return indexOf('\n', bufferSize) >= 0;

  105. }

  106. private:

  107. //获取下一个数据块的大小

  108. //如果只剩一个buffer,返回最后一个buffer所含数据的大小;否则返回第一个buffer所含数据的大小。

  109. qint64 nextDataBlockSize()

  110. {

  111. return (tailBuffer == 0 ? tail : buffers.first().size()) - head;

  112. }

  113. //获取缓冲区第一个有效数据的指针

  114. char *readPointer()

  115. {

  116. return bufferSize == 0 ? Q_NULLPTR : (buffers.first().data() + head);

  117. }

  118. qint64 indexOf(char c, qint64 maxLength, qint64 pos = 0);

  119. //释放空间

  120. void free(qint64 bytes);

  121. private:

  122. QList<QByteArray> buffers;

  123. //标识第一个buffer数据起始位置和最后一个buffer数据的结尾位置

  124. int head, tail;

  125. //大小为buffers.size()-1,如果为0,说明只剩一个buffer

  126. int tailBuffer;

  127. //初始分配空间的大小

  128. int basicBlockSize;

  129. //buffers数据总大小

  130. qint64 bufferSize;

  131. };

  132.  
  133. #endif // QRINGBUFFER_P_H

qringbuffer.cpp

 

 

 
  1. #include "qringbuffer.h"

  2. #include <string.h>

  3.  
  4. char *QRingBuffer::readPointerAtPosition(qint64 pos, qint64 &length)

  5. {

  6. if (pos >= 0)

  7. {

  8. pos += head;

  9. for (int i = 0; i < buffers.size(); ++i)

  10. {

  11. length = (i == tailBuffer ? tail : buffers[i].size());

  12. if (length > pos)

  13. {

  14. length -= pos;

  15. return buffers[i].data() + pos;

  16. }

  17. pos -= length;

  18. }

  19. }

  20.  
  21. length = 0;

  22. return 0;

  23. }

  24.  
  25. void QRingBuffer::free(qint64 bytes)

  26. {

  27. Q_ASSERT(bytes <= bufferSize);

  28.  
  29. while (bytes > 0)

  30. {

  31. const qint64 blockSize = buffers.first().size() - head;

  32. if (tailBuffer == 0 || blockSize > bytes)

  33. {

  34. if (bufferSize <= bytes)

  35. {

  36. if (buffers.first().size() <= basicBlockSize)

  37. {

  38. bufferSize = 0;

  39. head = tail = 0;

  40. } else

  41. {

  42. clear();

  43. }

  44. }

  45. else

  46. {

  47. Q_ASSERT(bytes < MaxByteArraySize);

  48. head += int(bytes);

  49. bufferSize -= bytes;

  50. }

  51. return;

  52. }

  53. bufferSize -= blockSize;

  54. bytes -= blockSize;

  55. buffers.removeFirst();

  56. --tailBuffer;

  57. head = 0;

  58. }

  59. }

  60.  
  61. char *QRingBuffer::reserve(qint64 bytes)

  62. {

  63. if (bytes <= 0 || bytes >= MaxByteArraySize)

  64. return 0;

  65.  
  66. if (buffers.isEmpty())

  67. {

  68. buffers.append(QByteArray());

  69. buffers.first().resize(qMax(basicBlockSize, int(bytes)));

  70. }

  71. else

  72. {

  73. const qint64 newSize = bytes + tail;

  74. //如果超过最后一个buffer所含数据的大小,则最后一个buffer需要从新分配

  75. if (newSize > buffers.last().size())

  76. {

  77. //满足以下条件时,将最后一个buffer的容积缩小到其当前所含数据的大小,

  78. //然后新开辟一个buffer,并将该buffer数据的结尾位置tail设置为0

  79. if (newSize > buffers.last().capacity() && (tail >= basicBlockSize

  80. || newSize >= MaxByteArraySize))

  81. {

  82. buffers.last().resize(tail);

  83. buffers.append(QByteArray());

  84. ++tailBuffer;

  85. tail = 0;

  86. }

  87. //将最后一个buffer进行扩容

  88. buffers.last().resize(qMax(basicBlockSize, tail + int(bytes)));

  89. }

  90. }

  91. char *writePtr = buffers.last().data() + tail;

  92. bufferSize += bytes;

  93. Q_ASSERT(bytes < MaxByteArraySize);

  94. tail += int(bytes);

  95. return writePtr;

  96. }

  97.  
  98. char *QRingBuffer::reserveFront(qint64 bytes)

  99. {

  100. if (bytes <= 0 || bytes >= MaxByteArraySize)

  101. return 0;

  102.  
  103. if (head < bytes)

  104. {

  105. if (buffers.isEmpty())

  106. {

  107. buffers.append(QByteArray());

  108. }

  109. else

  110. {

  111. buffers.first().remove(0, head);

  112. if (tailBuffer == 0)

  113. tail -= head;

  114. }

  115.  
  116. head = qMax(basicBlockSize, int(bytes));

  117. if (bufferSize == 0)

  118. {

  119. tail = head;

  120. }

  121. else

  122. {

  123. buffers.prepend(QByteArray());

  124. ++tailBuffer;

  125. }

  126. buffers.first().resize(head);

  127. }

  128.  
  129. head -= int(bytes);

  130. bufferSize += bytes;

  131. return buffers.first().data() + head;

  132. }

  133.  
  134. void QRingBuffer::chop(qint64 length)

  135. {

  136. Q_ASSERT(length <= bufferSize);

  137.  
  138. while (length > 0)

  139. {

  140. if (tailBuffer == 0 || tail > length)

  141. {

  142. if (bufferSize <= length)

  143. {

  144. if (buffers.first().size() <= basicBlockSize)

  145. {

  146. bufferSize = 0;

  147. head = tail = 0;

  148. }

  149. else

  150. {

  151. clear();

  152. }

  153. }

  154. else

  155. {

  156. Q_ASSERT(length < MaxByteArraySize);

  157. tail -= int(length);

  158. bufferSize -= length;

  159. }

  160. return;

  161. }

  162.  
  163. bufferSize -= tail;

  164. length -= tail;

  165. buffers.removeLast();

  166. --tailBuffer;

  167. tail = buffers.last().size();

  168. }

  169. }

  170.  
  171. void QRingBuffer::clear()

  172. {

  173. if (buffers.isEmpty())

  174. return;

  175.  
  176. buffers.erase(buffers.begin() + 1, buffers.end());

  177. buffers.first().clear();

  178.  
  179. head = tail = 0;

  180. tailBuffer = 0;

  181. bufferSize = 0;

  182. }

  183.  
  184. qint64 QRingBuffer::indexOf(char c, qint64 maxLength, qint64 pos)

  185. {

  186. if (maxLength <= 0 || pos < 0)

  187. return -1;

  188.  
  189. qint64 index = -(pos + head);

  190. for (int i = 0; i < buffers.size(); ++i)

  191. {

  192. qint64 nextBlockIndex = qMin(index + (i == tailBuffer ? tail : buffers[i].size()),

  193. maxLength);

  194.  
  195. if (nextBlockIndex > 0)

  196. {

  197. const char *ptr = buffers[i].data();

  198. if (index < 0)

  199. {

  200. ptr -= index;

  201. index = 0;

  202. }

  203.  
  204. const char *findPtr = reinterpret_cast<const char *>(memchr(ptr, c,

  205. nextBlockIndex - index));

  206. if (findPtr)

  207. return qint64(findPtr - ptr) + index + pos;

  208.  
  209. if (nextBlockIndex == maxLength)

  210. return -1;

  211. }

  212. index = nextBlockIndex;

  213. }

  214. return -1;

  215. }

  216.  
  217. qint64 QRingBuffer::read(char *data, qint64 maxLength)

  218. {

  219. const qint64 bytesToRead = qMin(bufferSize, maxLength);

  220. qint64 readSoFar = 0;

  221. while (readSoFar < bytesToRead)

  222. {

  223. const qint64 bytesToReadFromThisBlock = qMin(bytesToRead - readSoFar,

  224. nextDataBlockSize());

  225. if (data)

  226. memcpy(data + readSoFar, readPointer(), bytesToReadFromThisBlock);

  227. readSoFar += bytesToReadFromThisBlock;

  228. free(bytesToReadFromThisBlock);

  229. }

  230. return readSoFar;

  231. }

  232.  
  233.  
  234. QByteArray QRingBuffer::read()

  235. {

  236. if (bufferSize == 0)

  237. return QByteArray();

  238.  
  239. QByteArray qba(buffers.takeFirst());

  240.  
  241. //避免调整大小时不必要的内存分配,使QByteArray更高效

  242. qba.reserve(0);

  243. if (tailBuffer == 0)

  244. {

  245. qba.resize(tail);

  246. tail = 0;

  247. } else

  248. {

  249. --tailBuffer;

  250. }

  251. qba.remove(0, head);

  252. head = 0;

  253. bufferSize -= qba.size();

  254. return qba;

  255. }

  256.  
  257.  
  258. qint64 QRingBuffer::peek(char *data, qint64 maxLength, qint64 pos)

  259. {

  260. qint64 readSoFar = 0;

  261.  
  262. if (pos >= 0)

  263. {

  264. pos += head;

  265. for (int i = 0; readSoFar < maxLength && i < buffers.size(); ++i)

  266. {

  267. qint64 blockLength = (i == tailBuffer ? tail : buffers[i].size());

  268.  
  269. if (pos < blockLength)

  270. {

  271. blockLength = qMin(blockLength - pos, maxLength - readSoFar);

  272. memcpy(data + readSoFar, buffers[i].data() + pos, blockLength);

  273. readSoFar += blockLength;

  274. pos = 0;

  275. }

  276. else

  277. {

  278. pos -= blockLength;

  279. }

  280. }

  281. }

  282.  
  283. return readSoFar;

  284. }

  285.  
  286. void QRingBuffer::append(const char *data, qint64 size)

  287. {

  288. char *writePointer = reserve(size);

  289. if (size == 1)

  290. *writePointer = *data;

  291. else if (size)

  292. ::memcpy(writePointer, data, size);

  293. }

  294.  
  295. void QRingBuffer::append(const QByteArray &qba)

  296. {

  297. if (tail == 0)

  298. {

  299. if (buffers.isEmpty())

  300. buffers.append(qba);

  301. else

  302. buffers.last() = qba;

  303. }

  304. else

  305. {

  306. buffers.last().resize(tail);

  307. buffers.append(qba);

  308. ++tailBuffer;

  309. }

  310. tail = qba.size();

  311. bufferSize += tail;

  312. }

  313.  
  314. qint64 QRingBuffer::readLine(char *data, qint64 maxLength)

  315. {

  316. if (!data || --maxLength <= 0)

  317. return -1;

  318.  
  319. qint64 i = indexOf('\n', maxLength);

  320. i = read(data, i >= 0 ? (i+1) : maxLength);

  321.  
  322. data[i] = '\0';

  323. return i;

  324. }

main.cpp

 

 

 
  1. #include <qringbuffer.h>

  2. #include <QDebug>

  3.  
  4. int main()

  5. {

  6. //测试环形缓冲区的写入和读取+++++++++++++++++++++++++++++++

  7. qDebug()<<QStringLiteral("测试环形缓冲区的写入和读取+++++++++++++++++++++++++++++++");

  8. //方法1

  9. QRingBuffer ringBuffer;

  10. ringBuffer.append("CSDN ",5);

  11. ringBuffer.append("blog ",5);

  12. QByteArray qba("http://blog.csdn.net/caoshangpa");

  13. ringBuffer.append(qba);

  14. QByteArray head=ringBuffer.read();

  15. QByteArray tail=ringBuffer.read();

  16. qDebug()<<head<<tail;

  17. //方法2

  18. ringBuffer.clear();

  19. ringBuffer.append("CSDN ",5);

  20. ringBuffer.append("blog ",5);

  21. ringBuffer.append(qba);

  22. char str[100]={'\0'};

  23. ringBuffer.read(str,100);

  24. qDebug()<<str;

  25. //测试在缓冲区头和尾添加字符+++++++++++++++++++++++++++++++

  26. qDebug()<<QStringLiteral("测试在缓冲区头和尾添加字符+++++++++++++++++++++++++++++++");

  27. ringBuffer.clear();

  28. ringBuffer.append("CSDN ",5);

  29. ringBuffer.append("blog ",5);

  30. ringBuffer.append(qba);

  31. //头

  32. ringBuffer.ungetChar('{');

  33. //尾

  34. ringBuffer.putChar('}');

  35. memset(str,0,100);

  36. ringBuffer.read(str,100);

  37. qDebug()<<str;

  38. //测试读取一行+++++++++++++++++++++++++++++++++++++++++++

  39. qDebug()<<QStringLiteral("测试读取一行+++++++++++++++++++++++++++++++++++++++++++");

  40. ringBuffer.clear();

  41. ringBuffer.append("CSDN ",5);

  42. ringBuffer.append("blog\n",5);

  43. ringBuffer.append(qba);

  44. memset(str,0,100);

  45. if(ringBuffer.canReadLine())

  46. ringBuffer.readLine(str,100);

  47. qDebug()<<str;

  48. //测试拷贝数据+++++++++++++++++++++++++++++++++++++++++++

  49. qDebug()<<QStringLiteral("测试拷贝数据+++++++++++++++++++++++++++++++++++++++++++");

  50. ringBuffer.clear();

  51. ringBuffer.append("CSDN ",5);

  52. ringBuffer.append("blog ",5);

  53. ringBuffer.append(qba);

  54. memset(str,0,100);

  55. ringBuffer.peek(str,10),

  56. qDebug()<<str;

  57. //证明peek只是拷贝数据

  58. memset(str,0,100);

  59. ringBuffer.read(str,100);

  60. qDebug()<<str;

  61. //测试释放指定长度缓冲区+++++++++++++++++++++++++++++++++++++++++++

  62. qDebug()<<QStringLiteral("测试释放指定长度缓冲区+++++++++++++++++++++++++++++++++++++++++++");

  63. ringBuffer.clear();

  64. ringBuffer.append("CSDN ",5);

  65. ringBuffer.append("blog ",5);

  66. ringBuffer.append(qba);

  67. memset(str,0,100);

  68. ringBuffer.skip(10),

  69. ringBuffer.read(str,100);

  70. qDebug()<<str;

  71. //测试申请指定长度缓冲区+++++++++++++++++++++++++++++++++++++++++++

  72. qDebug()<<QStringLiteral("测试申请指定长度缓冲区+++++++++++++++++++++++++++++++++++++++++++");

  73. ringBuffer.clear();

  74. ringBuffer.reserve(100);

  75. qint64 lenToTail=0;

  76. char * pos=ringBuffer.readPointerAtPosition(10,lenToTail);

  77. char * test="CSDN blog http://blog.csdn.net/caoshangpa";

  78. memcpy(pos,test,strlen(test));

  79. ringBuffer.skip(10);

  80. ringBuffer.chop(100-10-strlen(test));

  81. qDebug()<<ringBuffer.read();

  82. qDebug()<<lenToTail;

  83. system("pause");

  84. }

测试结果

 

Qt实现环形缓冲区的两种方法

二.使用QSemaphore

用定时器将当前时间存储到QStringList对象中,然后通过现场去QStringList对象中取出并打印,通过两个QSemaphore对象进行同步,形成环形缓冲区。

thread.h

 

 
  1. #ifndef THREAD_H

  2. #define THREAD_H

  3.  
  4. #include <QThread>

  5. #include <QSemaphore>

  6. #include <QStringList>

  7.  
  8. class Thread : public QThread

  9. {

  10. Q_OBJECT

  11. public:

  12. Thread();

  13. ~Thread();

  14.  
  15. protected:

  16. void run();

  17.  
  18. private slots:

  19. void slotTimeout();

  20.  
  21. };

  22.  
  23. #endif // THREAD_H

thread.cpp

 

 

 
  1. #include "thread.h"

  2. #include <QDebug>

  3. #include <QTimer>

  4. #include <QTime>

  5. //1024是这个list的缓冲区大小,当然可以随意设定;80的意思是初始空闲的缓冲区大小是1024

  6. QSemaphore freeBytes(1024);

  7. //初始被使用的缓冲区大小是0

  8. QSemaphore usedBytes(0);

  9. QStringList timeList;

  10. Thread::Thread()

  11. {

  12. QTimer *timer=new QTimer(this);

  13. connect(timer,SIGNAL(timeout()),this,SLOT(slotTimeout()));

  14. timer->start(50);

  15. }

  16.  
  17. Thread::~Thread()

  18. {

  19.  
  20. }

  21.  
  22. void Thread::run()

  23. {

  24. //获取并打印list中的时间

  25. while(true)

  26. {

  27. usedBytes.acquire();

  28. qDebug()<<timeList.takeFirst();

  29. freeBytes.release();

  30. }

  31. }

  32. //通过定时器给list存入当前时间

  33. void Thread::slotTimeout()

  34. {

  35. freeBytes.acquire();//先申请一个位置,相当于空闲的缓冲区变为1023

  36. timeList.append(QTime::currentTime().toString("now : hh:mm:ss:zzz"));

  37. usedBytes.release();//给使用的大小+1

  38. }

main.cpp

 

 

 
  1. #include <QCoreApplication>

  2. #include "thread.h"

  3. int main(int argc, char *argv[])

  4. {

  5. QCoreApplication a(argc, argv);

  6. Thread *thread=new Thread;

  7. thread->start();

  8. return a.exec();

  9. }

测试结果

 

Qt实现环形缓冲区的两种方法


Qt实现环形缓冲区的两种方法

论坛相关讨论:

http://www.qtcn.org/bbs/read-htm-tid-58314-page-1.html

相关文章:

  • 2021-05-05
  • 2021-08-22
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
猜你喜欢
  • 2021-07-08
  • 2021-06-03
  • 2022-12-23
  • 2021-09-18
相关资源
相似解决方案