aboutsummaryrefslogtreecommitdiff
path: root/src/buffer.c
blob: 2e32720dec4abee85fb1d33a8b66b874cecc662e (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 strbuf->ptr so that people can always
  11. * assume ptr is non-NULL and zero terminated even for new strbufs.
  12. */
  13. unsigned char strbuf__initbuf[1];
  14. unsigned char strbuf__oom[1];
  15. #define ENSURE_SIZE(b, d) \
  16. if ((d) > buf->asize && strbuf_grow(b, (d)) < 0) \
  17. return -1;
  18. void strbuf_init(strbuf *buf, int initial_size)
  19. {
  20. buf->asize = 0;
  21. buf->size = 0;
  22. buf->ptr = strbuf__initbuf;
  23. if (initial_size)
  24. strbuf_grow(buf, initial_size);
  25. }
  26. int strbuf_try_grow(strbuf *buf, int target_size, bool mark_oom)
  27. {
  28. unsigned char *new_ptr;
  29. int new_size;
  30. if (buf->ptr == strbuf__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 = strbuf__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 strbuf_free(strbuf *buf)
  62. {
  63. if (!buf) return;
  64. if (buf->ptr != strbuf__initbuf && buf->ptr != strbuf__oom)
  65. free(buf->ptr);
  66. strbuf_init(buf, 0);
  67. }
  68. void strbuf_clear(strbuf *buf)
  69. {
  70. buf->size = 0;
  71. if (buf->asize > 0)
  72. buf->ptr[0] = '\0';
  73. }
  74. int strbuf_set(strbuf *buf, const unsigned char *data, int len)
  75. {
  76. if (len <= 0 || data == NULL) {
  77. strbuf_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 strbuf_sets(strbuf *buf, const char *string)
  89. {
  90. return strbuf_set(buf,
  91. (const unsigned char *)string,
  92. string ? strlen(string) : 0);
  93. }
  94. int strbuf_putc(strbuf *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 strbuf_put(strbuf *buf, const unsigned char *data, int len)
  102. {
  103. if (len <= 0)
  104. return 0;
  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 strbuf_puts(strbuf *buf, const char *string)
  112. {
  113. return strbuf_put(buf, (const unsigned char *)string, strlen(string));
  114. }
  115. int strbuf_vprintf(strbuf *buf, const char *format, va_list ap)
  116. {
  117. const int expected_size = buf->size + (strlen(format) * 2);
  118. int len;
  119. ENSURE_SIZE(buf, expected_size);
  120. while (1) {
  121. va_list args;
  122. va_copy(args, ap);
  123. len = vsnprintf(
  124. (char *)buf->ptr + buf->size,
  125. buf->asize - buf->size,
  126. format, args
  127. );
  128. if (len < 0) {
  129. free(buf->ptr);
  130. buf->ptr = strbuf__oom;
  131. return -1;
  132. }
  133. if (len + 1 <= buf->asize - buf->size) {
  134. buf->size += len;
  135. break;
  136. }
  137. ENSURE_SIZE(buf, buf->size + len + 1);
  138. }
  139. return 0;
  140. }
  141. int strbuf_printf(strbuf *buf, const char *format, ...)
  142. {
  143. int r;
  144. va_list ap;
  145. va_start(ap, format);
  146. r = strbuf_vprintf(buf, format, ap);
  147. va_end(ap);
  148. return r;
  149. }
  150. void strbuf_copy_cstr(char *data, int datasize, const strbuf *buf)
  151. {
  152. int copylen;
  153. assert(data && datasize && buf);
  154. data[0] = '\0';
  155. if (buf->size == 0 || buf->asize <= 0)
  156. return;
  157. copylen = buf->size;
  158. if (copylen > datasize - 1)
  159. copylen = datasize - 1;
  160. memmove(data, buf->ptr, copylen);
  161. data[copylen] = '\0';
  162. }
  163. void strbuf_swap(strbuf *buf_a, strbuf *buf_b)
  164. {
  165. strbuf t = *buf_a;
  166. *buf_a = *buf_b;
  167. *buf_b = t;
  168. }
  169. unsigned char *strbuf_detach(strbuf *buf)
  170. {
  171. unsigned char *data = buf->ptr;
  172. if (buf->asize == 0 || buf->ptr == strbuf__oom) {
  173. /* return an empty string */
  174. return calloc(1, 1);
  175. }
  176. strbuf_init(buf, 0);
  177. return data;
  178. }
  179. void strbuf_attach(strbuf *buf, unsigned char *ptr, int asize)
  180. {
  181. strbuf_free(buf);
  182. if (ptr) {
  183. buf->ptr = ptr;
  184. buf->size = strlen((char *)ptr);
  185. if (asize)
  186. buf->asize = (asize < buf->size) ? buf->size + 1 : asize;
  187. else /* pass 0 to fall back on strlen + 1 */
  188. buf->asize = buf->size + 1;
  189. } else {
  190. strbuf_grow(buf, asize);
  191. }
  192. }
  193. int strbuf_cmp(const strbuf *a, const strbuf *b)
  194. {
  195. int result = memcmp(a->ptr, b->ptr, MIN(a->size, b->size));
  196. return (result != 0) ? result :
  197. (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
  198. }
  199. int strbuf_strchr(const strbuf *buf, int c, int pos)
  200. {
  201. const unsigned char *p = memchr(buf->ptr + pos, c, buf->size - pos);
  202. if (!p)
  203. return -1;
  204. return (int)(p - (const unsigned char *)buf->ptr);
  205. }
  206. int strbuf_strrchr(const strbuf *buf, int c, int pos)
  207. {
  208. int i;
  209. for (i = pos; i >= 0; i--) {
  210. if (buf->ptr[i] == (unsigned char) c)
  211. return i;
  212. }
  213. return -1;
  214. }
  215. void strbuf_truncate(strbuf *buf, int len)
  216. {
  217. if (len < buf->size) {
  218. buf->size = len;
  219. buf->ptr[buf->size] = '\0';
  220. }
  221. }
  222. void strbuf_drop(strbuf *buf, int n)
  223. {
  224. if (n > 0) {
  225. buf->size = buf->size - n;
  226. if (buf->size)
  227. memmove(buf->ptr, buf->ptr + n, buf->size);
  228. buf->ptr[buf->size] = '\0';
  229. }
  230. }
  231. void strbuf_trim(strbuf *buf)
  232. {
  233. int i = 0;
  234. if (!buf->size)
  235. return;
  236. while (i < buf->size && isspace(buf->ptr[i]))
  237. i++;
  238. strbuf_drop(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. }
  247. // Destructively modify string, collapsing consecutive
  248. // space and newline characters into a single space.
  249. void strbuf_normalize_whitespace(strbuf *s)
  250. {
  251. bool last_char_was_space = false;
  252. int r, w;
  253. for (r = 0, w = 0; r < s->size; ++r) {
  254. switch (s->ptr[r]) {
  255. case ' ':
  256. case '\n':
  257. if (last_char_was_space)
  258. break;
  259. s->ptr[w++] = ' ';
  260. last_char_was_space = true;
  261. break;
  262. default:
  263. s->ptr[w++] = s->ptr[r];
  264. last_char_was_space = false;
  265. }
  266. }
  267. strbuf_truncate(s, w);
  268. }
  269. // Destructively unescape a string: remove backslashes before punctuation chars.
  270. extern void strbuf_unescape(strbuf *buf)
  271. {
  272. int r, w;
  273. for (r = 0, w = 0; r < buf->size; ++r) {
  274. if (buf->ptr[r] == '\\' && ispunct(buf->ptr[r + 1]))
  275. continue;
  276. buf->ptr[w++] = buf->ptr[r];
  277. }
  278. strbuf_truncate(buf, w);
  279. }