aboutsummaryrefslogtreecommitdiff
path: root/src/buffer.c
blob: cdf8ca0b5f4f18066391caccccc2248a7e5db7a2 (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 NULL;
  174. strbuf_init(buf, 0);
  175. return data;
  176. }
  177. void strbuf_attach(strbuf *buf, unsigned char *ptr, int asize)
  178. {
  179. strbuf_free(buf);
  180. if (ptr) {
  181. buf->ptr = ptr;
  182. buf->size = strlen((char *)ptr);
  183. if (asize)
  184. buf->asize = (asize < buf->size) ? buf->size + 1 : asize;
  185. else /* pass 0 to fall back on strlen + 1 */
  186. buf->asize = buf->size + 1;
  187. } else {
  188. strbuf_grow(buf, asize);
  189. }
  190. }
  191. int strbuf_cmp(const strbuf *a, const strbuf *b)
  192. {
  193. int result = memcmp(a->ptr, b->ptr, MIN(a->size, b->size));
  194. return (result != 0) ? result :
  195. (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
  196. }
  197. int strbuf_strchr(const strbuf *buf, int c, int pos)
  198. {
  199. const unsigned char *p = memchr(buf->ptr + pos, c, buf->size - pos);
  200. if (!p)
  201. return -1;
  202. return (int)(p - (const unsigned char *)buf->ptr);
  203. }
  204. int strbuf_strrchr(const strbuf *buf, int c, int pos)
  205. {
  206. int i;
  207. for (i = pos; i >= 0; i--) {
  208. if (buf->ptr[i] == (unsigned char) c)
  209. return i;
  210. }
  211. return -1;
  212. }
  213. void strbuf_truncate(strbuf *buf, int len)
  214. {
  215. if (len < buf->size) {
  216. buf->size = len;
  217. buf->ptr[buf->size] = '\0';
  218. }
  219. }
  220. void strbuf_drop(strbuf *buf, int n)
  221. {
  222. if (n > 0) {
  223. buf->size = buf->size - n;
  224. if (buf->size)
  225. memmove(buf->ptr, buf->ptr + n, buf->size);
  226. buf->ptr[buf->size] = '\0';
  227. }
  228. }
  229. void strbuf_trim(strbuf *buf)
  230. {
  231. int i = 0;
  232. if (!buf->size)
  233. return;
  234. while (i < buf->size && isspace(buf->ptr[i]))
  235. i++;
  236. strbuf_drop(buf, i);
  237. /* rtrim */
  238. while (buf->size > 0) {
  239. if (!isspace(buf->ptr[buf->size - 1]))
  240. break;
  241. buf->size--;
  242. }
  243. buf->ptr[buf->size] = '\0';
  244. }
  245. // Destructively modify string, collapsing consecutive
  246. // space and newline characters into a single space.
  247. void strbuf_normalize_whitespace(strbuf *s)
  248. {
  249. bool last_char_was_space = false;
  250. int r, w;
  251. for (r = 0, w = 0; r < s->size; ++r) {
  252. switch (s->ptr[r]) {
  253. case ' ':
  254. case '\n':
  255. if (last_char_was_space)
  256. break;
  257. s->ptr[w++] = ' ';
  258. last_char_was_space = true;
  259. break;
  260. default:
  261. s->ptr[w++] = s->ptr[r];
  262. last_char_was_space = false;
  263. }
  264. }
  265. strbuf_truncate(s, w);
  266. }
  267. // Destructively unescape a string: remove backslashes before punctuation chars.
  268. extern void strbuf_unescape(strbuf *buf)
  269. {
  270. int r, w;
  271. for (r = 0, w = 0; r < buf->size; ++r) {
  272. if (buf->ptr[r] == '\\' && ispunct(buf->ptr[r + 1]))
  273. continue;
  274. buf->ptr[w++] = buf->ptr[r];
  275. }
  276. strbuf_truncate(buf, w);
  277. }