bufchain.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. /*
  2. * Generic routines to deal with send buffers: a linked list of
  3. * smallish blocks, with the operations
  4. *
  5. * - add an arbitrary amount of data to the end of the list
  6. * - remove the first N bytes from the list
  7. * - return a (pointer,length) pair giving some initial data in
  8. * the list, suitable for passing to a send or write system
  9. * call
  10. * - retrieve a larger amount of initial data from the list
  11. * - return the current size of the buffer chain in bytes
  12. */
  13. #include "defs.h"
  14. #include "misc.h"
  15. #define BUFFER_MIN_GRANULE 512
  16. struct bufchain_granule {
  17. struct bufchain_granule *next;
  18. char *bufpos, *bufend, *bufmax;
  19. };
  20. static void uninitialised_queue_idempotent_callback(IdempotentCallback *ic)
  21. {
  22. unreachable("bufchain callback used while uninitialised");
  23. }
  24. void bufchain_init(bufchain *ch)
  25. {
  26. ch->head = ch->tail = NULL;
  27. ch->buffersize = 0;
  28. ch->ic = NULL;
  29. ch->queue_idempotent_callback = uninitialised_queue_idempotent_callback;
  30. }
  31. void bufchain_clear(bufchain *ch)
  32. {
  33. struct bufchain_granule *b;
  34. while (ch->head) {
  35. b = ch->head;
  36. ch->head = ch->head->next;
  37. smemclr(b, sizeof(*b));
  38. sfree(b);
  39. }
  40. ch->tail = NULL;
  41. ch->buffersize = 0;
  42. }
  43. size_t bufchain_size(bufchain *ch)
  44. {
  45. return ch->buffersize;
  46. }
  47. void bufchain_set_callback_inner(
  48. bufchain *ch, IdempotentCallback *ic,
  49. void (*queue_idempotent_callback)(IdempotentCallback *ic))
  50. {
  51. ch->queue_idempotent_callback = queue_idempotent_callback;
  52. ch->ic = ic;
  53. }
  54. void bufchain_add(bufchain *ch, const void *data, size_t len)
  55. {
  56. const char *buf = (const char *)data;
  57. if (len == 0) return;
  58. ch->buffersize += len;
  59. while (len > 0) {
  60. if (ch->tail && ch->tail->bufend < ch->tail->bufmax) {
  61. size_t copylen = min(len, ch->tail->bufmax - ch->tail->bufend);
  62. memcpy(ch->tail->bufend, buf, copylen);
  63. buf += copylen;
  64. len -= copylen;
  65. ch->tail->bufend += copylen;
  66. }
  67. if (len > 0) {
  68. size_t grainlen =
  69. max(sizeof(struct bufchain_granule) + len, BUFFER_MIN_GRANULE);
  70. struct bufchain_granule *newbuf;
  71. newbuf = smalloc(grainlen);
  72. newbuf->bufpos = newbuf->bufend =
  73. (char *)newbuf + sizeof(struct bufchain_granule);
  74. newbuf->bufmax = (char *)newbuf + grainlen;
  75. newbuf->next = NULL;
  76. if (ch->tail)
  77. ch->tail->next = newbuf;
  78. else
  79. ch->head = newbuf;
  80. ch->tail = newbuf;
  81. }
  82. }
  83. if (ch->ic)
  84. ch->queue_idempotent_callback(ch->ic);
  85. }
  86. void bufchain_consume(bufchain *ch, size_t len)
  87. {
  88. struct bufchain_granule *tmp;
  89. assert(ch->buffersize >= len);
  90. while (len > 0) {
  91. int remlen = len;
  92. assert(ch->head != NULL);
  93. if (remlen >= ch->head->bufend - ch->head->bufpos) {
  94. remlen = ch->head->bufend - ch->head->bufpos;
  95. tmp = ch->head;
  96. ch->head = tmp->next;
  97. if (!ch->head)
  98. ch->tail = NULL;
  99. smemclr(tmp, sizeof(*tmp));
  100. sfree(tmp);
  101. } else
  102. ch->head->bufpos += remlen;
  103. ch->buffersize -= remlen;
  104. len -= remlen;
  105. }
  106. }
  107. ptrlen bufchain_prefix(bufchain *ch)
  108. {
  109. return make_ptrlen(ch->head->bufpos, ch->head->bufend - ch->head->bufpos);
  110. }
  111. void bufchain_fetch(bufchain *ch, void *data, size_t len)
  112. {
  113. struct bufchain_granule *tmp;
  114. char *data_c = (char *)data;
  115. tmp = ch->head;
  116. assert(ch->buffersize >= len);
  117. while (len > 0) {
  118. int remlen = len;
  119. assert(tmp != NULL);
  120. if (remlen >= tmp->bufend - tmp->bufpos)
  121. remlen = tmp->bufend - tmp->bufpos;
  122. memcpy(data_c, tmp->bufpos, remlen);
  123. tmp = tmp->next;
  124. len -= remlen;
  125. data_c += remlen;
  126. }
  127. }
  128. void bufchain_fetch_consume(bufchain *ch, void *data, size_t len)
  129. {
  130. bufchain_fetch(ch, data, len);
  131. bufchain_consume(ch, len);
  132. }
  133. bool bufchain_try_fetch(bufchain *ch, void *data, size_t len)
  134. {
  135. if (ch->buffersize >= len) {
  136. bufchain_fetch(ch, data, len);
  137. return true;
  138. } else {
  139. return false;
  140. }
  141. }
  142. bool bufchain_try_consume(bufchain *ch, size_t len)
  143. {
  144. if (ch->buffersize >= len) {
  145. bufchain_consume(ch, len);
  146. return true;
  147. } else {
  148. return false;
  149. }
  150. }
  151. bool bufchain_try_fetch_consume(bufchain *ch, void *data, size_t len)
  152. {
  153. if (ch->buffersize >= len) {
  154. bufchain_fetch_consume(ch, data, len);
  155. return true;
  156. } else {
  157. return false;
  158. }
  159. }
  160. size_t bufchain_fetch_consume_up_to(bufchain *ch, void *data, size_t len)
  161. {
  162. if (len > ch->buffersize)
  163. len = ch->buffersize;
  164. if (len)
  165. bufchain_fetch_consume(ch, data, len);
  166. return len;
  167. }