cpbuffer.h 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. #ifndef CPBUFFER_H
  2. #define CPBUFFER_H
  3. #include <string>
  4. #include <cstring>
  5. #include <algorithm>
  6. #include <baseproc-sys.h>
  7. // control protocol buffer, a circular buffer with fixed capacity.
  8. template <unsigned SIZE> class cpbuffer
  9. {
  10. char buf[SIZE];
  11. unsigned cur_idx = 0;
  12. unsigned length = 0; // number of elements in the buffer
  13. public:
  14. static constexpr unsigned get_size()
  15. {
  16. return SIZE;
  17. }
  18. unsigned get_length() noexcept
  19. {
  20. return length;
  21. }
  22. int get_free() noexcept
  23. {
  24. return SIZE - length;
  25. }
  26. char * get_ptr(int index)
  27. {
  28. unsigned pos = cur_idx + index;
  29. if (pos >= SIZE) pos -= SIZE;
  30. return &buf[pos];
  31. }
  32. char * get_buf_base()
  33. {
  34. return buf;
  35. }
  36. unsigned get_contiguous_length(char *ptr)
  37. {
  38. unsigned eidx = cur_idx + length;
  39. if (eidx >= SIZE) eidx -= SIZE;
  40. if (buf + eidx > ptr) {
  41. return (buf + eidx) - ptr;
  42. }
  43. else {
  44. return (buf + SIZE) - ptr;
  45. }
  46. }
  47. // Fill by reading from the given fd, return positive if some was read or -1 on error.
  48. int fill(int fd) noexcept
  49. {
  50. unsigned pos = cur_idx + length;
  51. if (pos >= SIZE) pos -= SIZE;
  52. unsigned max_count = std::min(SIZE - pos, SIZE - length);
  53. ssize_t r = bp_sys::read(fd, buf + pos, max_count);
  54. if (r >= 0) {
  55. length += r;
  56. }
  57. return r;
  58. }
  59. // Fill by reading up to the specified amount of bytes from the given fd,
  60. // Return is the number of bytes read, 0 on end-of-file or -1 on error.
  61. int fill(int fd, unsigned limit) noexcept
  62. {
  63. unsigned pos = cur_idx + length;
  64. if (pos >= SIZE) pos -= SIZE;
  65. unsigned max_count = std::min(SIZE - pos, SIZE - length);
  66. max_count = std::min(max_count, limit);
  67. ssize_t r = bp_sys::read(fd, buf + pos, max_count);
  68. if (r >= 0) {
  69. length += r;
  70. }
  71. return r;
  72. }
  73. // fill by reading from the given fd, until at least the specified number of bytes are in
  74. // the buffer. Return 0 if end-of-file reached before fill complete, or -1 on error.
  75. int fill_to(int fd, unsigned rlength) noexcept
  76. {
  77. while (length < rlength) {
  78. int r = fill(fd);
  79. if (r <= 0) return r;
  80. }
  81. return 1;
  82. }
  83. // Trim the buffer to the specified length (must be less than current length)
  84. void trim_to(unsigned new_length)
  85. {
  86. length = new_length;
  87. }
  88. char operator[](int idx) noexcept
  89. {
  90. unsigned dest_idx = cur_idx + idx;
  91. if (dest_idx >= SIZE) dest_idx -= SIZE;
  92. return buf[dest_idx];
  93. }
  94. // Remove the given number of bytes from the start of the buffer.
  95. void consume(unsigned amount) noexcept
  96. {
  97. cur_idx += amount;
  98. if (cur_idx >= SIZE) cur_idx -= SIZE;
  99. length -= amount;
  100. }
  101. // Extract bytes from the buffer. The bytes remain in the buffer.
  102. void extract(void *dest, unsigned index, unsigned length) noexcept
  103. {
  104. index += cur_idx;
  105. if (index >= SIZE) index -= SIZE;
  106. if (index + length > SIZE) {
  107. // wrap-around copy
  108. unsigned half = SIZE - index;
  109. std::memcpy(dest, buf + index, half);
  110. std::memcpy(reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(dest) + half),
  111. buf, length - half);
  112. }
  113. else {
  114. std::memcpy(dest, buf + index, length);
  115. }
  116. }
  117. // Extract string of given length from given index
  118. // Throws: std::bad_alloc on allocation failure
  119. std::string extract_string(unsigned index, unsigned length)
  120. {
  121. index += cur_idx;
  122. if (index >= SIZE) index -= SIZE;
  123. if (index + length > SIZE) {
  124. std::string r(buf + index, SIZE - index);
  125. r.insert(r.end(), buf, buf + length - (SIZE - index));
  126. return r;
  127. }
  128. else {
  129. return std::string(buf + index, length);
  130. }
  131. }
  132. // Append characters to the buffer. Caller must make certain there
  133. // is enough space to contain the characters first.
  134. void append(const char * s, unsigned len) noexcept
  135. {
  136. unsigned index = cur_idx + length;
  137. if (index >= SIZE) index -= SIZE;
  138. length += len; // (before we destroy len)
  139. unsigned max = SIZE - index;
  140. std::memcpy(buf + index, s, std::min(max, len));
  141. if (len > max) {
  142. // Wrapped around buffer: copy the rest
  143. s += max;
  144. len -= max;
  145. std::memcpy(buf, s, len);
  146. }
  147. }
  148. // reset the index and length.
  149. void reset()
  150. {
  151. cur_idx = 0;
  152. length = 0;
  153. }
  154. };
  155. #endif