bufchain.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  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. /* WINSCP
  16. * Default granule of 512 leads to low performance.
  17. */
  18. #define BUFFER_MIN_GRANULE 512*2*32
  19. struct bufchain_granule {
  20. struct bufchain_granule *next;
  21. char *bufpos, *bufend, *bufmax;
  22. };
  23. static void uninitialised_queue_idempotent_callback(IdempotentCallback *ic)
  24. {
  25. unreachable("bufchain callback used while uninitialised");
  26. }
  27. void bufchain_init(bufchain *ch)
  28. {
  29. ch->head = ch->tail = NULL;
  30. ch->buffersize = 0;
  31. ch->ic = NULL;
  32. ch->queue_idempotent_callback = uninitialised_queue_idempotent_callback;
  33. }
  34. void bufchain_clear(bufchain *ch)
  35. {
  36. struct bufchain_granule *b;
  37. while (ch->head) {
  38. b = ch->head;
  39. ch->head = ch->head->next;
  40. smemclr(b, sizeof(*b));
  41. sfree(b);
  42. }
  43. ch->tail = NULL;
  44. ch->buffersize = 0;
  45. }
  46. size_t bufchain_size(bufchain *ch)
  47. {
  48. return ch->buffersize;
  49. }
  50. void bufchain_set_callback_inner(
  51. bufchain *ch, IdempotentCallback *ic,
  52. void (*queue_idempotent_callback)(IdempotentCallback *ic))
  53. {
  54. ch->queue_idempotent_callback = queue_idempotent_callback;
  55. ch->ic = ic;
  56. }
  57. void bufchain_add(bufchain *ch, const void *data, size_t len)
  58. {
  59. const char *buf = (const char *)data;
  60. if (len == 0) return;
  61. ch->buffersize += len;
  62. while (len > 0) {
  63. if (ch->tail && ch->tail->bufend < ch->tail->bufmax) {
  64. size_t copylen = min(len, ch->tail->bufmax - ch->tail->bufend);
  65. memcpy(ch->tail->bufend, buf, copylen);
  66. buf += copylen;
  67. len -= copylen;
  68. ch->tail->bufend += copylen;
  69. }
  70. if (len > 0) {
  71. size_t grainlen =
  72. max(sizeof(struct bufchain_granule) + len, BUFFER_MIN_GRANULE);
  73. struct bufchain_granule *newbuf;
  74. newbuf = smalloc(grainlen);
  75. newbuf->bufpos = newbuf->bufend =
  76. (char *)newbuf + sizeof(struct bufchain_granule);
  77. newbuf->bufmax = (char *)newbuf + grainlen;
  78. newbuf->next = NULL;
  79. if (ch->tail)
  80. ch->tail->next = newbuf;
  81. else
  82. ch->head = newbuf;
  83. ch->tail = newbuf;
  84. }
  85. }
  86. if (ch->ic)
  87. ch->queue_idempotent_callback(ch->ic);
  88. }
  89. void bufchain_consume(bufchain *ch, size_t len)
  90. {
  91. struct bufchain_granule *tmp;
  92. assert(ch->buffersize >= len);
  93. while (len > 0) {
  94. int remlen = len;
  95. assert(ch->head != NULL);
  96. if (remlen >= ch->head->bufend - ch->head->bufpos) {
  97. remlen = ch->head->bufend - ch->head->bufpos;
  98. tmp = ch->head;
  99. ch->head = tmp->next;
  100. if (!ch->head)
  101. ch->tail = NULL;
  102. smemclr(tmp, sizeof(*tmp));
  103. sfree(tmp);
  104. } else
  105. ch->head->bufpos += remlen;
  106. ch->buffersize -= remlen;
  107. len -= remlen;
  108. }
  109. }
  110. ptrlen bufchain_prefix(bufchain *ch)
  111. {
  112. return make_ptrlen(ch->head->bufpos, ch->head->bufend - ch->head->bufpos);
  113. }
  114. void bufchain_fetch(bufchain *ch, void *data, size_t len)
  115. {
  116. struct bufchain_granule *tmp;
  117. char *data_c = (char *)data;
  118. tmp = ch->head;
  119. assert(ch->buffersize >= len);
  120. while (len > 0) {
  121. int remlen = len;
  122. assert(tmp != NULL);
  123. if (remlen >= tmp->bufend - tmp->bufpos)
  124. remlen = tmp->bufend - tmp->bufpos;
  125. memcpy(data_c, tmp->bufpos, remlen);
  126. tmp = tmp->next;
  127. len -= remlen;
  128. data_c += remlen;
  129. }
  130. }
  131. void bufchain_fetch_consume(bufchain *ch, void *data, size_t len)
  132. {
  133. bufchain_fetch(ch, data, len);
  134. bufchain_consume(ch, len);
  135. }
  136. bool bufchain_try_fetch(bufchain *ch, void *data, size_t len)
  137. {
  138. if (ch->buffersize >= len) {
  139. bufchain_fetch(ch, data, len);
  140. return true;
  141. } else {
  142. return false;
  143. }
  144. }
  145. bool bufchain_try_consume(bufchain *ch, size_t len)
  146. {
  147. if (ch->buffersize >= len) {
  148. bufchain_consume(ch, len);
  149. return true;
  150. } else {
  151. return false;
  152. }
  153. }
  154. bool bufchain_try_fetch_consume(bufchain *ch, void *data, size_t len)
  155. {
  156. if (ch->buffersize >= len) {
  157. bufchain_fetch_consume(ch, data, len);
  158. return true;
  159. } else {
  160. return false;
  161. }
  162. }
  163. size_t bufchain_fetch_consume_up_to(bufchain *ch, void *data, size_t len)
  164. {
  165. if (len > ch->buffersize)
  166. len = ch->buffersize;
  167. if (len)
  168. bufchain_fetch_consume(ch, data, len);
  169. return len;
  170. }