aboutsummaryrefslogtreecommitdiff
path: root/src/buffer.c
blob: 17dc8649bd763b02540b83856a4a60ad7abe7369 (plain)
  1. #include <stdarg.h>
  2. #include <ctype.h>
  3. #include <string.h>
  4. #include <assert.h>
  5. #include <string.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <sys/param.h>
  9. #include "buffer.h"
  10. /* Used as default value for gh_buf->ptr so that people can always
  11. * assume ptr is non-NULL and zero terminated even for new gh_bufs.
  12. */
  13. unsigned char gh_buf__initbuf[1];
  14. unsigned char gh_buf__oom[1];
  15. #define ENSURE_SIZE(b, d) \
  16. if ((d) > buf->asize && gh_buf_grow(b, (d)) < 0)\
  17. return -1;
  18. void gh_buf_init(gh_buf *buf, int initial_size)
  19. {
  20. buf->asize = 0;
  21. buf->size = 0;
  22. buf->ptr = gh_buf__initbuf;
  23. if (initial_size)
  24. gh_buf_grow(buf, initial_size);
  25. }
  26. int gh_buf_try_grow(gh_buf *buf, int target_size, bool mark_oom)
  27. {
  28. unsigned char *new_ptr;
  29. int new_size;
  30. if (buf->ptr == gh_buf__oom)
  31. return -1;
  32. if (target_size <= buf->asize)
  33. return 0;
  34. if (buf->asize == 0) {
  35. new_size = target_size;
  36. new_ptr = NULL;
  37. } else {
  38. new_size = buf->asize;
  39. new_ptr = buf->ptr;
  40. }
  41. /* grow the buffer size by 1.5, until it's big enough
  42. * to fit our target size */
  43. while (new_size < target_size)
  44. new_size = (new_size << 1) - (new_size >> 1);
  45. /* round allocation up to multiple of 8 */
  46. new_size = (new_size + 7) & ~7;
  47. new_ptr = realloc(new_ptr, new_size);
  48. if (!new_ptr) {
  49. if (mark_oom)
  50. buf->ptr = gh_buf__oom;
  51. return -1;
  52. }
  53. buf->asize = new_size;
  54. buf->ptr = new_ptr;
  55. /* truncate the existing buffer size if necessary */
  56. if (buf->size >= buf->asize)
  57. buf->size = buf->asize - 1;
  58. buf->ptr[buf->size] = '\0';
  59. return 0;
  60. }
  61. void gh_buf_free(gh_buf *buf)
  62. {
  63. if (!buf) return;
  64. if (buf->ptr != gh_buf__initbuf && buf->ptr != gh_buf__oom)
  65. free(buf->ptr);
  66. gh_buf_init(buf, 0);
  67. }
  68. void gh_buf_clear(gh_buf *buf)
  69. {
  70. buf->size = 0;
  71. if (buf->asize > 0)
  72. buf->ptr[0] = '\0';
  73. }
  74. int gh_buf_set(gh_buf *buf, const unsigned char *data, int len)
  75. {
  76. if (len == 0 || data == NULL) {
  77. gh_buf_clear(buf);
  78. } else {
  79. if (data != buf->ptr) {
  80. ENSURE_SIZE(buf, len + 1);
  81. memmove(buf->ptr, data, len);
  82. }
  83. buf->size = len;
  84. buf->ptr[buf->size] = '\0';
  85. }
  86. return 0;
  87. }
  88. int gh_buf_sets(gh_buf *buf, const char *string)
  89. {
  90. return gh_buf_set(buf,
  91. (const unsigned char *)string,
  92. string ? strlen(string) : 0);
  93. }
  94. int gh_buf_putc(gh_buf *buf, int c)
  95. {
  96. ENSURE_SIZE(buf, buf->size + 2);
  97. buf->ptr[buf->size++] = c;
  98. buf->ptr[buf->size] = '\0';
  99. return 0;
  100. }
  101. int gh_buf_put(gh_buf *buf, const unsigned char *data, int len)
  102. {
  103. ENSURE_SIZE(buf, buf->size + len + 1);
  104. memmove(buf->ptr + buf->size, data, len);
  105. buf->size += len;
  106. buf->ptr[buf->size] = '\0';
  107. return 0;
  108. }
  109. int gh_buf_puts(gh_buf *buf, const char *string)
  110. {
  111. return gh_buf_put(buf, (const unsigned char *)string, strlen(string));
  112. }
  113. int gh_buf_vprintf(gh_buf *buf, const char *format, va_list ap)
  114. {
  115. const int expected_size = buf->size + (strlen(format) * 2);
  116. int len;
  117. ENSURE_SIZE(buf, expected_size);
  118. while (1) {
  119. va_list args;
  120. va_copy(args, ap);
  121. len = vsnprintf(
  122. (char *)buf->ptr + buf->size,
  123. buf->asize - buf->size,
  124. format, args
  125. );
  126. if (len < 0) {
  127. free(buf->ptr);
  128. buf->ptr = gh_buf__oom;
  129. return -1;
  130. }
  131. if (len + 1 <= buf->asize - buf->size) {
  132. buf->size += len;
  133. break;
  134. }
  135. ENSURE_SIZE(buf, buf->size + len + 1);
  136. }
  137. return 0;
  138. }
  139. int gh_buf_printf(gh_buf *buf, const char *format, ...)
  140. {
  141. int r;
  142. va_list ap;
  143. va_start(ap, format);
  144. r = gh_buf_vprintf(buf, format, ap);
  145. va_end(ap);
  146. return r;
  147. }
  148. void gh_buf_copy_cstr(char *data, int datasize, const gh_buf *buf)
  149. {
  150. int copylen;
  151. assert(data && datasize && buf);
  152. data[0] = '\0';
  153. if (buf->size == 0 || buf->asize <= 0)
  154. return;
  155. copylen = buf->size;
  156. if (copylen > datasize - 1)
  157. copylen = datasize - 1;
  158. memmove(data, buf->ptr, copylen);
  159. data[copylen] = '\0';
  160. }
  161. void gh_buf_swap(gh_buf *buf_a, gh_buf *buf_b)
  162. {
  163. gh_buf t = *buf_a;
  164. *buf_a = *buf_b;
  165. *buf_b = t;
  166. }
  167. unsigned char *gh_buf_detach(gh_buf *buf)
  168. {
  169. unsigned char *data = buf->ptr;
  170. if (buf->asize == 0 || buf->ptr == gh_buf__oom)
  171. return NULL;
  172. gh_buf_init(buf, 0);
  173. return data;
  174. }
  175. void gh_buf_attach(gh_buf *buf, unsigned char *ptr, int asize)
  176. {
  177. gh_buf_free(buf);
  178. if (ptr) {
  179. buf->ptr = ptr;
  180. buf->size = strlen((char *)ptr);
  181. if (asize)
  182. buf->asize = (asize < buf->size) ? buf->size + 1 : asize;
  183. else /* pass 0 to fall back on strlen + 1 */
  184. buf->asize = buf->size + 1;
  185. } else {
  186. gh_buf_grow(buf, asize);
  187. }
  188. }
  189. int gh_buf_cmp(const gh_buf *a, const gh_buf *b)
  190. {
  191. int result = memcmp(a->ptr, b->ptr, MIN(a->size, b->size));
  192. return (result != 0) ? result :
  193. (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
  194. }
  195. int gh_buf_strchr(const gh_buf *buf, int c, int pos)
  196. {
  197. const char *p = memchr(buf->ptr + pos, c, buf->size - pos);
  198. if (!p)
  199. return -1;
  200. return (int)(p - buf->ptr);
  201. }
  202. int gh_buf_strrchr(const gh_buf *buf, int c, int pos)
  203. {
  204. int i;
  205. for (i = pos; i >= 0; i--) {
  206. if (buf->ptr[i] == (unsigned char) c)
  207. return i;
  208. }
  209. return -1;
  210. }
  211. void gh_buf_truncate(gh_buf *buf, size_t len)
  212. {
  213. if (len < buf->size) {
  214. buf->size = len;
  215. buf->ptr[buf->size] = '\0';
  216. }
  217. }
  218. void gh_buf_trim(gh_buf *buf)
  219. {
  220. /* TODO: leading whitespace? */
  221. /*
  222. while (i < buf->size && isspace(buf->ptr[i]))
  223. i++;
  224. gh_buf_truncate(buf, i);
  225. */
  226. /* rtrim */
  227. while (buf->size > 0) {
  228. if (!isspace(buf->ptr[buf->size - 1]))
  229. break;
  230. buf->size--;
  231. }
  232. buf->ptr[buf->size] = '\0';
  233. }