circlebuffer.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. #include "circlebuffer.h"
  2. #include <assert.h>
  3. #include <stdlib.h>
  4. #include <stdio.h>
  5. #include <string.h> /* memory copy */
  6. #include "duck_mem.h"
  7. /* this is just a debugging trick so that we can "see" the free space */
  8. void* circleread_memcpy(void* dst, void* src, int64_t count);
  9. void* circleread_memcpy(void* dst, void* src, int64_t count)
  10. {
  11. return duck_memcpy64(dst, src, count);
  12. }
  13. void CircleReport(const CircleBuffer_t* cb, const char* title)
  14. {
  15. printf("-----(%s)------\n", title);
  16. printf("max Size cb = %ld\n", cb->bufSize);
  17. printf("fills at = %ld\n", cb->bufSize * cb->percent / 100 );
  18. printf("Current amount = %ld, level = %ld\n", cb->count, cb->count * 100 / cb->bufSize);
  19. }
  20. int ForwardBuffer(CircleBuffer_t* cb, int64_t len)
  21. {
  22. if (len >= (int64_t)cb->count)
  23. return -1;
  24. if ( (cb->head + len) < cb->bufSize )
  25. cb->head += (int)len;
  26. else
  27. cb->head = (int)len - (cb->bufSize - cb->head);
  28. cb->count -= (int)len;
  29. return 0;
  30. }
  31. int RewindBuffer(CircleBuffer_t* cb, int64_t len)
  32. {
  33. if (len >= (int64_t)(cb->bufSize - cb->count) )
  34. return -1; /* not enough history in buffer ! */
  35. if (cb->head <= (size_t)len)
  36. {
  37. if (cb->wrapped == 0)
  38. return -1;
  39. cb->head = cb->bufSize - ((int)len - cb->head);
  40. cb->count += (int)len;
  41. return 0;
  42. }
  43. else
  44. {
  45. cb->head -= (int)len;
  46. cb->count += (int)len;
  47. }
  48. return 0;
  49. }
  50. void destroyCircleBuffer(CircleBuffer_t* cb)
  51. {
  52. assert(cb);
  53. if (cb->buffer)
  54. free(cb->buffer);
  55. if (cb->maxChunk)
  56. free(cb->maxChunk);
  57. }
  58. int resetCircleBuffer(CircleBuffer_t* cb)
  59. {
  60. cb->count = 0;
  61. cb->bytesConsumed = 0;
  62. cb->wrapped = 0;
  63. cb->starvedBytes = 0;
  64. cb->starvedRequests = 0;
  65. return 0;
  66. }
  67. int initCircleBuffer(
  68. CircleBuffer_t* cb,
  69. size_t countRecords,
  70. int percent,
  71. size_t maxChunk,
  72. FuncLock_t lock,
  73. FuncLock_t unlock
  74. )
  75. {
  76. assert(cb);
  77. cb->buffer = (unsigned char * ) calloc(1, countRecords);
  78. cb->maxChunk = (unsigned char *) calloc(1, maxChunk);
  79. cb->maxChunkLen = maxChunk;
  80. if (cb->buffer)
  81. {
  82. cb->head = cb->count = 0;
  83. cb->balance = 0;
  84. cb->bufSize = countRecords;
  85. cb->bytesConsumed = 0;
  86. cb->muted = false;
  87. cb->percent = percent;
  88. cb->wrapped = 0;
  89. cb->lock = lock;
  90. cb->unlock = unlock;
  91. return 0;
  92. }
  93. else
  94. {
  95. return -1; /* error */
  96. }
  97. }
  98. /* return zero if plenty of room and success */
  99. /*-------------------------------------------*/
  100. /* free space nested in the middle of the buffer is consider endSpace */
  101. /* and when free space nested in middle, startSpace is considered to be zero */
  102. /*---------------------------------------------------------------------------*/
  103. int addToCircleBuffer(CircleBuffer_t* cb, void* data, size_t requestSpace)
  104. {
  105. int64_t freeSpace; /* count total free space in buffer */
  106. int64_t head = cb->head; /* offset start of valid data */
  107. int64_t tail = (cb->head + cb->count) % cb->bufSize; /* offest first free byte after valid data */
  108. int64_t endSpace;
  109. freeSpace = cb->bufSize - cb->count;
  110. /* if not enough room to do the add */
  111. /*----------------------------------*/
  112. if (requestSpace > freeSpace)
  113. {
  114. assert(0);
  115. return CB_FULL;
  116. }
  117. endSpace = cb->bufSize - tail;
  118. if (tail >= head && requestSpace > endSpace) /* additional data write will wrap */
  119. {
  120. duck_memcpy64(&cb->buffer[tail], data, endSpace);
  121. duck_memcpy64(
  122. cb->buffer,
  123. (unsigned char *)data+endSpace,
  124. requestSpace - endSpace);
  125. }
  126. else /* existing data wrapped around from end of buffer through beginning of buffer. */
  127. {
  128. memcpy(&cb->buffer[tail], data, requestSpace);
  129. }
  130. cb->count += requestSpace;
  131. cb->balance += 1;
  132. return 0; /* -1 will mean error,m zero is OK */
  133. }
  134. /* get info need so we can write direct as in memcpy into the circle buffer */
  135. /*--------------------------------------------------------------------------*/
  136. void FreeWrapless(const CircleBuffer_t* cb, void* handle, int64_t* sizeWrapless)
  137. {
  138. int64_t tail = (cb->head + cb->count) % cb->bufSize;
  139. if ((cb->head + cb->count) < cb->bufSize)
  140. {
  141. *((void **) handle) = &cb->buffer[tail];
  142. *sizeWrapless = (cb->bufSize -(cb->head + cb->count));
  143. }
  144. else
  145. {
  146. *((void **) handle) = &cb->buffer[tail];
  147. *sizeWrapless = (cb->bufSize - cb->count);
  148. }
  149. }
  150. /* Please clone this sucker from readFromCircleBuffer */
  151. int accessCircleBuffer(CircleBuffer_t* cb, void* handle1, size_t requestSize)
  152. {
  153. int64_t head = cb->head;
  154. int64_t tail = (cb->head + cb->count) % cb->bufSize;
  155. void** handle = (void **) handle1;
  156. void* dest = *handle;
  157. if (requestSize <= 0)
  158. {
  159. return requestSize;
  160. }
  161. if (cb->count < requestSize)
  162. {
  163. return -1;
  164. }
  165. if (tail > head) /* the data does not wrap ! */
  166. {
  167. *handle = &cb->buffer[head];
  168. }
  169. else /* the current data does wrap */
  170. {
  171. /* but our read does not wrap */
  172. if (head + requestSize < cb->bufSize)
  173. {
  174. *handle = &cb->buffer[head];
  175. }
  176. else if (head + requestSize == cb->bufSize)
  177. {
  178. *handle = &cb->buffer[head];
  179. }
  180. else /* our read will wrap ! */
  181. {
  182. int64_t temp = cb->bufSize - head;
  183. dest = cb->maxChunk;
  184. assert(cb->maxChunkLen >= requestSize);
  185. circleread_memcpy(
  186. dest,
  187. &cb->buffer[head],
  188. temp);
  189. circleread_memcpy(
  190. ((unsigned char *) dest) + temp,
  191. cb->buffer,
  192. requestSize - temp);
  193. *handle = dest;
  194. }
  195. }
  196. cb->head = (cb->head + requestSize) % cb->bufSize;
  197. cb->count -= requestSize;
  198. cb->bytesConsumed += requestSize;
  199. cb->balance -= 1;
  200. return requestSize; /* records (16 bit or maybe other in future) */
  201. }
  202. /* return count read , or -1 if not enough data */
  203. /*----------------------------------------------*/
  204. int readFromCircleBuffer(CircleBuffer_t* cb, void* dest, size_t requestSize)
  205. {
  206. int64_t head = cb->head;
  207. int64_t tail = (cb->head + cb->count) % cb->bufSize;
  208. if (cb->count < requestSize)
  209. {
  210. requestSize = cb->count; /* Give them what we have */
  211. }
  212. if (requestSize <= 0)
  213. {
  214. return (int)requestSize;
  215. }
  216. if (tail > head) /* the data does not wrap ! */
  217. {
  218. circleread_memcpy(dest, &cb->buffer[head], requestSize);
  219. }
  220. else /* the current data does wrap */
  221. {
  222. /* but our read does not wrap */
  223. if (head + requestSize < cb->bufSize)
  224. {
  225. circleread_memcpy(dest, &cb->buffer[head], requestSize);
  226. }
  227. else if (head + requestSize == cb->bufSize)
  228. {
  229. circleread_memcpy(dest, &cb->buffer[head], requestSize);
  230. memset(&cb->buffer[head], 0, (size_t)requestSize); /* optional, debug */
  231. }
  232. else /* our read will wrap ! */
  233. {
  234. int64_t temp = cb->bufSize - head;
  235. circleread_memcpy(
  236. dest,
  237. &cb->buffer[head],
  238. temp);
  239. circleread_memcpy(
  240. ((unsigned char *) dest) + temp,
  241. cb->buffer,
  242. requestSize - temp);
  243. }
  244. }
  245. cb->head = (cb->head + requestSize) % cb->bufSize;
  246. cb->count -= requestSize;
  247. cb->bytesConsumed += requestSize;
  248. cb->balance -= 1;
  249. return (int)requestSize; /* records (16 bit or maybe other in future) */
  250. }
  251. void testCircleBuffer()
  252. {
  253. CircleBuffer_t temp;
  254. size_t bufSize = 256;
  255. const size_t maxInput = 256*3;
  256. size_t count = 0;
  257. size_t t;
  258. int i;
  259. const int maxRandom = 32;
  260. size_t chunkOut = 30;
  261. CircleRecord_t data[256*3];
  262. initCircleBuffer(&temp, bufSize, 75, 256, 0, 0);
  263. /* who cares ... take the default seed value. */
  264. while (count < maxInput || temp.count > chunkOut)
  265. {
  266. t = rand();
  267. /* for whatever reason this seems to be a 16 bit random number */
  268. t = t / ( 2 << (16 - 7) ) ;
  269. for(i = 0; i < (int)t; i++)
  270. {
  271. data[i] = (unsigned char ) ( count + i );
  272. }
  273. if (
  274. ((temp.bufSize - temp.count) >= t*sizeof(short)+1) &&
  275. (count < (maxInput - maxRandom))
  276. ) /* add 1 to keep buffer being completely filled */
  277. {
  278. int64_t tail = (temp.head + temp.count) % temp.bufSize;
  279. addToCircleBuffer(&temp, data, t);
  280. printf("Add to buffer count = %ld. head = %ld, tail = %ld\n", t, temp.head, tail);
  281. count += t;
  282. }
  283. else /* not enough space in buffer, try to empty some out */
  284. {
  285. int r;
  286. r = readFromCircleBuffer(&temp, data, chunkOut);
  287. if (r >= 0)
  288. {
  289. int64_t tail = (temp.head + temp.count) % temp.bufSize;
  290. for(i = 0; i < r; i++)
  291. printf("%ld ", data[i]);
  292. printf("\nRead from buffer. head = %ld, tail = %ld\n", temp.head, tail);
  293. }
  294. }
  295. } /* while we have not accumulated a large eough test ... */
  296. }
  297. int CirclePercent(CircleBuffer_t* cb)
  298. {
  299. return (int)(cb->count * 100 / cb->bufSize);
  300. }
  301. int CircleAtLevel(CircleBuffer_t* cb)
  302. {
  303. return (int)(cb->count * 100 / cb->bufSize) >= cb->percent;
  304. }
  305. int CircleOverLevel(CircleBuffer_t* cb)
  306. {
  307. return (int)(cb->count * 100 / cb->bufSize) > cb->percent;
  308. }