aboutsummaryrefslogtreecommitdiff
path: root/src/buffer.c
blob: aab1f08a4d0bd4e1f0e187daa176cda679294a1b (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. #define MIN(x,y) ((x<y) ? x : y)
  19. void strbuf_init(strbuf *buf, int initial_size)
  20. {
  21. buf->asize = 0;
  22. buf->size = 0;
  23. buf->ptr = strbuf__initbuf;
  24. if (initial_size)
  25. strbuf_grow(buf, initial_size);
  26. }
  27. int strbuf_try_grow(strbuf *buf, int target_size, bool mark_oom)
  28. {
  29. unsigned char *new_ptr;
  30. int new_size;
  31. if (buf->ptr == strbuf__oom)
  32. return -1;
  33. if (target_size <= buf->asize)
  34. return 0;
  35. if (buf->asize == 0) {
  36. new_size = target_size;
  37. new_ptr = NULL;
  38. } else {
  39. new_size = buf->asize;
  40. new_ptr = buf->ptr;
  41. }
  42. /* grow the buffer size by 1.5, until it's big enough
  43. * to fit our target size */
  44. while (new_size < target_size)
  45. new_size = (new_size << 1) - (new_size >> 1);
  46. /* round allocation up to multiple of 8 */
  47. new_size = (new_size + 7) & ~7;
  48. new_ptr = realloc(new_ptr, new_size);
  49. if (!new_ptr) {
  50. if (mark_oom)
  51. buf->ptr = strbuf__oom;
  52. return -1;
  53. }
  54. buf->asize = new_size;
  55. buf->ptr = new_ptr;
  56. /* truncate the existing buffer size if necessary */
  57. if (buf->size >= buf->asize)
  58. buf->size = buf->asize - 1;
  59. buf->ptr[buf->size] = '\0';
  60. return 0;
  61. }
  62. void strbuf_free(strbuf *buf)
  63. {
  64. if (!buf) return;
  65. if (buf->ptr != strbuf__initbuf && buf->ptr != strbuf__oom)
  66. free(buf->ptr);
  67. strbuf_init(buf, 0);
  68. }
  69. void strbuf_clear(strbuf *buf)
  70. {
  71. buf->size = 0;
  72. if (buf->asize > 0)
  73. buf->ptr[0] = '\0';
  74. }
  75. int strbuf_set(strbuf *buf, const unsigned char *data, int len)
  76. {
  77. if (len <= 0 || data == NULL) {
  78. strbuf_clear(buf);
  79. } else {
  80. if (data != buf->ptr) {
  81. ENSURE_SIZE(buf, len + 1);
  82. memmove(buf->ptr, data, len);
  83. }
  84. buf->size = len;
  85. buf->ptr[buf->size] = '\0';
  86. }
  87. return 0;
  88. }
  89. int strbuf_sets(strbuf *buf, const char *string)
  90. {
  91. return strbuf_set(buf,
  92. (const unsigned char *)string,
  93. string ? strlen(string) : 0);
  94. }
  95. int strbuf_putc(strbuf *buf, int c)
  96. {
  97. ENSURE_SIZE(buf, buf->size + 2);
  98. buf->ptr[buf->size++] = c;
  99. buf->ptr[buf->size] = '\0';
  100. return 0;
  101. }
  102. int strbuf_put(strbuf *buf, const unsigned char *data, int len)
  103. {
  104. if (len <= 0)
  105. return 0;
  106. ENSURE_SIZE(buf, buf->size + len + 1);
  107. memmove(buf->ptr + buf->size, data, len);
  108. buf->size += len;
  109. buf->ptr[buf->size] = '\0';
  110. return 0;
  111. }
  112. int strbuf_puts(strbuf *buf, const char *string)
  113. {
  114. return strbuf_put(buf, (const unsigned char *)string, strlen(string));
  115. }
  116. int strbuf_vprintf(strbuf *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. (char *)buf->ptr + buf->size,
  126. buf->asize - buf->size,
  127. format, args
  128. );
  129. if (len < 0) {
  130. free(buf->ptr);
  131. buf->ptr = strbuf__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 strbuf_printf(strbuf *buf, const char *format, ...)
  143. {
  144. int r;
  145. va_list ap;
  146. va_start(ap, format);
  147. r = strbuf_vprintf(buf, format, ap);
  148. va_end(ap);
  149. return r;
  150. }
  151. void strbuf_copy_cstr(char *data, int datasize, const strbuf *buf)
  152. {
  153. int 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 strbuf_swap(strbuf *buf_a, strbuf *buf_b)
  165. {
  166. strbuf t = *buf_a;
  167. *buf_a = *buf_b;
  168. *buf_b = t;
  169. }
  170. unsigned char *strbuf_detach(strbuf *buf)
  171. {
  172. unsigned char *data = buf->ptr;
  173. if (buf->asize == 0 || buf->ptr == strbuf__oom) {
  174. /* return an empty string */
  175. return calloc(1, 1);
  176. }
  177. strbuf_init(buf, 0);
  178. return data;
  179. }
  180. void strbuf_attach(strbuf *buf, unsigned char *ptr, int asize)
  181. {
  182. strbuf_free(buf);
  183. if (ptr) {
  184. buf->ptr = ptr;
  185. buf->size = strlen((char *)ptr);
  186. if (asize)
  187. buf->asize = (asize < buf->size) ? buf->size + 1 : asize;
  188. else /* pass 0 to fall back on strlen + 1 */
  189. buf->asize = buf->size + 1;
  190. } else {
  191. strbuf_grow(buf, asize);
  192. }
  193. }
  194. int strbuf_cmp(const strbuf *a, const strbuf *b)
  195. {
  196. int result = memcmp(a->ptr, b->ptr, MIN(a->size, b->size));
  197. return (result != 0) ? result :
  198. (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
  199. }
  200. int strbuf_strchr(const strbuf *buf, int c, int pos)
  201. {
  202. const unsigned char *p = memchr(buf->ptr + pos, c, buf->size - pos);
  203. if (!p)
  204. return -1;
  205. return (int)(p - (const unsigned char *)buf->ptr);
  206. }
  207. int strbuf_strrchr(const strbuf *buf, int c, int pos)
  208. {
  209. int i;
  210. for (i = pos; i >= 0; i--) {
  211. if (buf->ptr[i] == (unsigned char) c)
  212. return i;
  213. }
  214. return -1;
  215. }
  216. void strbuf_truncate(strbuf *buf, int len)
  217. {
  218. if (len < buf->size) {
  219. buf->size = len;
  220. buf->ptr[buf->size] = '\0';
  221. }
  222. }
  223. void strbuf_drop(strbuf *buf, int n)
  224. {
  225. if (n > 0) {
  226. buf->size = buf->size - n;
  227. if (buf->size)
  228. memmove(buf->ptr, buf->ptr + n, buf->size);
  229. buf->ptr[buf->size] = '\0';
  230. }
  231. }
  232. void strbuf_trim(strbuf *buf)
  233. {
  234. int i = 0;
  235. if (!buf->size)
  236. return;
  237. while (i < buf->size && isspace(buf->ptr[i]))
  238. i++;
  239. strbuf_drop(buf, i);
  240. /* rtrim */
  241. while (buf->size > 0) {
  242. if (!isspace(buf->ptr[buf->size - 1]))
  243. break;
  244. buf->size--;
  245. }
  246. buf->ptr[buf->size] = '\0';
  247. }
  248. // Destructively modify string, collapsing consecutive
  249. // space and newline characters into a single space.
  250. void strbuf_normalize_whitespace(strbuf *s)
  251. {
  252. bool last_char_was_space = false;
  253. int r, w;
  254. for (r = 0, w = 0; r < s->size; ++r) {
  255. switch (s->ptr[r]) {
  256. case ' ':
  257. case '\n':
  258. if (last_char_was_space)
  259. break;
  260. s->ptr[w++] = ' ';
  261. last_char_was_space = true;
  262. break;
  263. default:
  264. s->ptr[w++] = s->ptr[r];
  265. last_char_was_space = false;
  266. }
  267. }
  268. strbuf_truncate(s, w);
  269. }
  270. // Destructively unescape a string: remove backslashes before punctuation chars.
  271. extern void strbuf_unescape(strbuf *buf)
  272. {
  273. int r, w;
  274. for (r = 0, w = 0; r < buf->size; ++r) {
  275. if (buf->ptr[r] == '\\' && ispunct(buf->ptr[r + 1]))
  276. continue;
  277. buf->ptr[w++] = buf->ptr[r];
  278. }
  279. strbuf_truncate(buf, w);
  280. }