buffer.c 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. /*
  2. * Squeezelite - lightweight headless squeezebox emulator
  3. *
  4. * (c) Adrian Smith 2012-2015, triode1@btinternet.com
  5. * Ralph Irving 2015-2017, ralph_irving@hotmail.com
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation, either version 3 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. *
  20. */
  21. // fifo bufffers
  22. #ifndef _GNU_SOURCE
  23. #define _GNU_SOURCE
  24. #endif
  25. #include "squeezelite.h"
  26. // _* called with muxtex locked
  27. inline unsigned _buf_used(struct buffer *buf) {
  28. return buf->writep >= buf->readp ? buf->writep - buf->readp : buf->size - (buf->readp - buf->writep);
  29. }
  30. unsigned _buf_space(struct buffer *buf) {
  31. return buf->size - _buf_used(buf) - 1; // reduce by one as full same as empty otherwise
  32. }
  33. unsigned _buf_cont_read(struct buffer *buf) {
  34. return buf->writep >= buf->readp ? buf->writep - buf->readp : buf->wrap - buf->readp;
  35. }
  36. unsigned _buf_cont_write(struct buffer *buf) {
  37. return buf->writep >= buf->readp ? buf->wrap - buf->writep : buf->readp - buf->writep;
  38. }
  39. void _buf_inc_readp(struct buffer *buf, unsigned by) {
  40. buf->readp += by;
  41. if (buf->readp >= buf->wrap) {
  42. buf->readp -= buf->size;
  43. }
  44. }
  45. void _buf_inc_writep(struct buffer *buf, unsigned by) {
  46. buf->writep += by;
  47. if (buf->writep >= buf->wrap) {
  48. buf->writep -= buf->size;
  49. }
  50. }
  51. void buf_flush(struct buffer *buf) {
  52. mutex_lock(buf->mutex);
  53. buf->readp = buf->buf;
  54. buf->writep = buf->buf;
  55. mutex_unlock(buf->mutex);
  56. }
  57. void _buf_flush(struct buffer *buf) {
  58. buf->readp = buf->buf;
  59. buf->writep = buf->buf;
  60. }
  61. // adjust buffer to multiple of mod bytes so reading in multiple always wraps on frame boundary
  62. void buf_adjust(struct buffer *buf, size_t mod) {
  63. mutex_lock(buf->mutex);
  64. buf->base_size = ((size_t)(buf->size / mod)) * mod;
  65. buf->readp = buf->writep = buf->buf;
  66. buf->wrap = buf->buf + buf->base_size;
  67. buf->size = buf->base_size;
  68. mutex_unlock(buf->mutex);
  69. }
  70. // called with mutex locked to resize, does not retain contents, reverts to original size if fails
  71. void _buf_resize(struct buffer *buf, size_t size) {
  72. if (size == buf->size) return;
  73. free(buf->buf);
  74. buf->buf = malloc(size);
  75. if (!buf->buf) {
  76. size = buf->size;
  77. buf->buf = malloc(size);
  78. if (!buf->buf) size = 0;
  79. }
  80. buf->writep = buf->readp = buf->buf;
  81. buf->wrap = buf->buf + size;
  82. buf->true_size = buf->base_size = buf->size = size;
  83. }
  84. size_t _buf_limit(struct buffer *buf, size_t limit) {
  85. if (limit) {
  86. buf->size = limit;
  87. buf->readp = buf->writep = buf->buf;
  88. } else {
  89. buf->size = buf->base_size;
  90. }
  91. buf->wrap = buf->buf + buf->size;
  92. return buf->base_size - buf->size;
  93. }
  94. void _buf_unwrap(struct buffer *buf, size_t cont) {
  95. ssize_t len, by = cont - (buf->wrap - buf->readp);
  96. size_t size;
  97. u8_t *scratch;
  98. // do nothing if we have enough space
  99. if (by <= 0 || cont >= buf->size) return;
  100. // buffer already unwrapped, just move it up
  101. if (buf->writep >= buf->readp) {
  102. memmove(buf->readp - by, buf->readp, buf->writep - buf->readp);
  103. buf->readp -= by;
  104. buf->writep -= by;
  105. return;
  106. }
  107. // how much is overlapping
  108. size = by - (buf->readp - buf->writep);
  109. len = buf->writep - buf->buf;
  110. // buffer is wrapped and enough free space to move data up directly
  111. if (size <= 0) {
  112. memmove(buf->readp - by, buf->readp, buf->wrap - buf->readp);
  113. buf->readp -= by;
  114. memcpy(buf->wrap - by, buf->buf, min(len, by));
  115. if (len > by) {
  116. memmove(buf->buf, buf->buf + by, len - by);
  117. buf->writep -= by;
  118. } else {
  119. buf->writep += buf->size - by;
  120. }
  121. return;
  122. }
  123. scratch = malloc(size);
  124. // buffer is wrapped but not enough free room => use scratch zone
  125. if (scratch) {
  126. memcpy(scratch, buf->writep - size, size);
  127. memmove(buf->readp - by, buf->readp, buf->wrap - buf->readp);
  128. buf->readp -= by;
  129. memcpy(buf->wrap - by, buf->buf, by);
  130. memmove(buf->buf, buf->buf + by, len - by - size);
  131. buf->writep -= by;
  132. memcpy(buf->writep - size, scratch, size);
  133. free(scratch);
  134. } else {
  135. _buf_unwrap(buf, cont / 2);
  136. _buf_unwrap(buf, cont - cont / 2);
  137. }
  138. }
  139. void buf_init(struct buffer *buf, size_t size) {
  140. buf->buf = malloc(size);
  141. buf->readp = buf->buf;
  142. buf->writep = buf->buf;
  143. buf->wrap = buf->buf + size;
  144. buf->true_size = buf->base_size = buf->size = size;
  145. mutex_create_p(buf->mutex);
  146. }
  147. void buf_destroy(struct buffer *buf) {
  148. if (buf->buf) {
  149. free(buf->buf);
  150. buf->buf = NULL;
  151. buf->size = buf->base_size = buf->true_size = 0;
  152. mutex_destroy(buf->mutex);
  153. }
  154. }