aboutsummaryrefslogtreecommitdiff
path: root/src/buffer.c
blob: b81e7faf09e1112d28368e06a400a34add2aabef (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. char *new_ptr;
  29. size_t new_size;
  30. if (buf->ptr == gh_buf__oom || buf->asize < 0)
  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->asize > 0 && 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. if (buf->asize < 0) {
  74. buf->ptr = gh_buf__initbuf;
  75. buf->asize = 0;
  76. }
  77. }
  78. int gh_buf_set(gh_buf *buf, const char *data, int len)
  79. {
  80. if (len == 0 || data == NULL) {
  81. gh_buf_clear(buf);
  82. } else {
  83. if (data != buf->ptr) {
  84. ENSURE_SIZE(buf, len + 1);
  85. memmove(buf->ptr, data, len);
  86. }
  87. buf->size = len;
  88. buf->ptr[buf->size] = '\0';
  89. }
  90. return 0;
  91. }
  92. int gh_buf_sets(gh_buf *buf, const char *string)
  93. {
  94. return gh_buf_set(buf, string, string ? strlen(string) : 0);
  95. }
  96. int gh_buf_putc(gh_buf *buf, char c)
  97. {
  98. ENSURE_SIZE(buf, buf->size + 2);
  99. buf->ptr[buf->size++] = c;
  100. buf->ptr[buf->size] = '\0';
  101. return 0;
  102. }
  103. int gh_buf_put(gh_buf *buf, const char *data, int len)
  104. {
  105. ENSURE_SIZE(buf, buf->size + len + 1);
  106. memmove(buf->ptr + buf->size, data, len);
  107. buf->size += len;
  108. buf->ptr[buf->size] = '\0';
  109. return 0;
  110. }
  111. int gh_buf_puts(gh_buf *buf, const char *string)
  112. {
  113. assert(string);
  114. return gh_buf_put(buf, string, strlen(string));
  115. }
  116. int gh_buf_vprintf(gh_buf *buf, const char *format, va_list ap)
  117. {
  118. const int expected_size = buf->size + (strlen(format) * 2);
  119. int len;
  120. ENSURE_SIZE(buf, expected_size);
  121. while (1) {
  122. va_list args;
  123. va_copy(args, ap);
  124. len = vsnprintf(
  125. buf->ptr + buf->size,
  126. buf->asize - buf->size,
  127. format, args
  128. );
  129. if (len < 0) {
  130. free(buf->ptr);
  131. buf->ptr = gh_buf__oom;
  132. return -1;
  133. }
  134. if (len + 1 <= buf->asize - buf->size) {
  135. buf->size += len;
  136. break;
  137. }
  138. ENSURE_SIZE(buf, buf->size + len + 1);
  139. }
  140. return 0;
  141. }
  142. int gh_buf_printf(gh_buf *buf, const char *format, ...)
  143. {
  144. int r;
  145. va_list ap;
  146. va_start(ap, format);
  147. r = gh_buf_vprintf(buf, format, ap);
  148. va_end(ap);
  149. return r;
  150. }
  151. void gh_buf_copy_cstr(char *data, size_t datasize, const gh_buf *buf)
  152. {
  153. size_t copylen;
  154. assert(data && datasize && buf);
  155. data[0] = '\0';
  156. if (buf->size == 0 || buf->asize <= 0)
  157. return;
  158. copylen = buf->size;
  159. if (copylen > datasize - 1)
  160. copylen = datasize - 1;
  161. memmove(data, buf->ptr, copylen);
  162. data[copylen] = '\0';
  163. }
  164. void gh_buf_swap(gh_buf *buf_a, gh_buf *buf_b)
  165. {
  166. gh_buf t = *buf_a;
  167. *buf_a = *buf_b;
  168. *buf_b = t;
  169. }
  170. char *gh_buf_detach(gh_buf *buf)
  171. {
  172. char *data = buf->ptr;
  173. if (buf->asize == 0 || buf->ptr == gh_buf__oom)
  174. return NULL;
  175. gh_buf_init(buf, 0);
  176. return data;
  177. }
  178. void gh_buf_attach(gh_buf *buf, char *ptr, int asize)
  179. {
  180. gh_buf_free(buf);
  181. if (ptr) {
  182. buf->ptr = ptr;
  183. buf->size = strlen(ptr);
  184. if (asize)
  185. buf->asize = (asize < buf->size) ? buf->size + 1 : asize;
  186. else /* pass 0 to fall back on strlen + 1 */
  187. buf->asize = buf->size + 1;
  188. } else {
  189. gh_buf_grow(buf, asize);
  190. }
  191. }
  192. int gh_buf_cmp(const gh_buf *a, const gh_buf *b)
  193. {
  194. int result = memcmp(a->ptr, b->ptr, MIN(a->size, b->size));
  195. return (result != 0) ? result :
  196. (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
  197. }
  198. int gh_buf_strchr(const gh_buf *buf, int c, int pos)
  199. {
  200. const char *p = memchr(buf->ptr + pos, c, buf->size - pos);
  201. if (!p)
  202. return -1;
  203. return (int)(p - p->ptr);
  204. }
  205. int gh_buf_strrchr(const gh_buf *buf, int c, int pos)
  206. {
  207. int i;
  208. for (i = pos; i >= 0; i--) {
  209. if (buf->ptr[i] == (unsigned char) c)
  210. return i;
  211. }
  212. return -1;
  213. }
  214. void gh_buf_truncate(gh_buf *buf, size_t len)
  215. {
  216. assert(buf->asize >= 0);
  217. if (len < buf->size) {
  218. buf->size = len;
  219. buf->ptr[buf->size] = '\0';
  220. }
  221. }
  222. void gh_buf_ltruncate(gh_buf *buf, size_t len)
  223. {
  224. assert(buf->asize >= 0);
  225. if (len && len < buf->size) {
  226. memmove(buf->ptr, buf->ptr + len, buf->size - len);
  227. buf->size -= len;
  228. buf->ptr[buf->size] = '\0';
  229. }
  230. }
  231. void gh_buf_trim(gh_buf *buf)
  232. {
  233. size_t i = 0;
  234. assert(buf->asize >= 0);
  235. /* ltrim */
  236. while (i < buf->size && isspace(buf->ptr[i]))
  237. i++;
  238. gh_buf_truncate(buf, i);
  239. /* rtrim */
  240. while (buf->size > 0) {
  241. if (!isspace(buf->ptr[buf->size - 1]))
  242. break;
  243. buf->size--;
  244. }
  245. buf->ptr[buf->size] = '\0';
  246. }