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