aboutsummaryrefslogtreecommitdiff
path: root/src/buffer.c
blob: 15a6d05fbcd6a51d7ac14ad31885a400e2ca9533 (plain)
  1. #include <stdarg.h>
  2. #include <string.h>
  3. #include <assert.h>
  4. #include <string.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include "cmark_ctype.h"
  8. #include "buffer.h"
  9. /* Used as default value for cmark_strbuf->ptr so that people can always
  10. * assume ptr is non-NULL and zero terminated even for new cmark_strbufs.
  11. */
  12. unsigned char cmark_strbuf__initbuf[1];
  13. unsigned char cmark_strbuf__oom[1];
  14. #define ENSURE_SIZE(b, d) \
  15. if ((d) > buf->asize && cmark_strbuf_grow(b, (d)) < 0) \
  16. return -1;
  17. #ifndef MIN
  18. #define MIN(x,y) ((x<y) ? x : y)
  19. #endif
  20. void cmark_strbuf_init(cmark_strbuf *buf, int initial_size)
  21. {
  22. buf->asize = 0;
  23. buf->size = 0;
  24. buf->ptr = cmark_strbuf__initbuf;
  25. if (initial_size)
  26. cmark_strbuf_grow(buf, initial_size);
  27. }
  28. int cmark_strbuf_try_grow(cmark_strbuf *buf, int target_size, bool mark_oom)
  29. {
  30. unsigned char *new_ptr;
  31. int new_size;
  32. if (buf->ptr == cmark_strbuf__oom)
  33. return -1;
  34. if (target_size <= buf->asize)
  35. return 0;
  36. if (buf->asize == 0) {
  37. new_size = target_size;
  38. new_ptr = NULL;
  39. } else {
  40. new_size = buf->asize;
  41. new_ptr = buf->ptr;
  42. }
  43. /* grow the buffer size by 1.5, until it's big enough
  44. * to fit our target size */
  45. while (new_size < target_size)
  46. new_size = (new_size << 1) - (new_size >> 1);
  47. /* round allocation up to multiple of 8 */
  48. new_size = (new_size + 7) & ~7;
  49. new_ptr = (unsigned char *)realloc(new_ptr, new_size);
  50. if (!new_ptr) {
  51. if (mark_oom)
  52. buf->ptr = cmark_strbuf__oom;
  53. return -1;
  54. }
  55. buf->asize = new_size;
  56. buf->ptr = new_ptr;
  57. /* truncate the existing buffer size if necessary */
  58. if (buf->size >= buf->asize)
  59. buf->size = buf->asize - 1;
  60. buf->ptr[buf->size] = '\0';
  61. return 0;
  62. }
  63. int cmark_strbuf_grow(cmark_strbuf *buf, int target_size)
  64. {
  65. return cmark_strbuf_try_grow(buf, target_size, true);
  66. }
  67. bool cmark_strbuf_oom(const cmark_strbuf *buf)
  68. {
  69. return (buf->ptr == cmark_strbuf__oom);
  70. }
  71. size_t cmark_strbuf_len(const cmark_strbuf *buf)
  72. {
  73. return buf->size;
  74. }
  75. void cmark_strbuf_free(cmark_strbuf *buf)
  76. {
  77. if (!buf) return;
  78. if (buf->ptr != cmark_strbuf__initbuf && buf->ptr != cmark_strbuf__oom)
  79. free(buf->ptr);
  80. cmark_strbuf_init(buf, 0);
  81. }
  82. void cmark_strbuf_clear(cmark_strbuf *buf)
  83. {
  84. buf->size = 0;
  85. if (buf->asize > 0)
  86. buf->ptr[0] = '\0';
  87. }
  88. int cmark_strbuf_set(cmark_strbuf *buf, const unsigned char *data, int len)
  89. {
  90. if (len <= 0 || data == NULL) {
  91. cmark_strbuf_clear(buf);
  92. } else {
  93. if (data != buf->ptr) {
  94. ENSURE_SIZE(buf, len + 1);
  95. memmove(buf->ptr, data, len);
  96. }
  97. buf->size = len;
  98. buf->ptr[buf->size] = '\0';
  99. }
  100. return 0;
  101. }
  102. int cmark_strbuf_sets(cmark_strbuf *buf, const char *string)
  103. {
  104. return cmark_strbuf_set(buf,
  105. (const unsigned char *)string,
  106. string ? strlen(string) : 0);
  107. }
  108. int cmark_strbuf_putc(cmark_strbuf *buf, int c)
  109. {
  110. ENSURE_SIZE(buf, buf->size + 2);
  111. buf->ptr[buf->size++] = c;
  112. buf->ptr[buf->size] = '\0';
  113. return 0;
  114. }
  115. int cmark_strbuf_put(cmark_strbuf *buf, const unsigned char *data, int len)
  116. {
  117. if (len <= 0)
  118. return 0;
  119. ENSURE_SIZE(buf, buf->size + len + 1);
  120. memmove(buf->ptr + buf->size, data, len);
  121. buf->size += len;
  122. buf->ptr[buf->size] = '\0';
  123. return 0;
  124. }
  125. int cmark_strbuf_puts(cmark_strbuf *buf, const char *string)
  126. {
  127. return cmark_strbuf_put(buf, (const unsigned char *)string, strlen(string));
  128. }
  129. int cmark_strbuf_vprintf(cmark_strbuf *buf, const char *format, va_list ap)
  130. {
  131. const int expected_size = buf->size + (strlen(format) * 2);
  132. int len;
  133. ENSURE_SIZE(buf, expected_size);
  134. while (1) {
  135. va_list args;
  136. va_copy(args, ap);
  137. len = vsnprintf(
  138. (char *)buf->ptr + buf->size,
  139. buf->asize - buf->size,
  140. format, args
  141. );
  142. va_end(args);
  143. if (len < 0) {
  144. free(buf->ptr);
  145. buf->ptr = cmark_strbuf__oom;
  146. return -1;
  147. }
  148. if (len + 1 <= buf->asize - buf->size) {
  149. buf->size += len;
  150. break;
  151. }
  152. ENSURE_SIZE(buf, buf->size + len + 1);
  153. }
  154. return 0;
  155. }
  156. int cmark_strbuf_printf(cmark_strbuf *buf, const char *format, ...)
  157. {
  158. int r;
  159. va_list ap;
  160. va_start(ap, format);
  161. r = cmark_strbuf_vprintf(buf, format, ap);
  162. va_end(ap);
  163. return r;
  164. }
  165. void cmark_strbuf_copy_cstr(char *data, int datasize, const cmark_strbuf *buf)
  166. {
  167. int copylen;
  168. assert(data && datasize && buf);
  169. data[0] = '\0';
  170. if (buf->size == 0 || buf->asize <= 0)
  171. return;
  172. copylen = buf->size;
  173. if (copylen > datasize - 1)
  174. copylen = datasize - 1;
  175. memmove(data, buf->ptr, copylen);
  176. data[copylen] = '\0';
  177. }
  178. void cmark_strbuf_swap(cmark_strbuf *buf_a, cmark_strbuf *buf_b)
  179. {
  180. cmark_strbuf t = *buf_a;
  181. *buf_a = *buf_b;
  182. *buf_b = t;
  183. }
  184. unsigned char *cmark_strbuf_detach(cmark_strbuf *buf)
  185. {
  186. unsigned char *data = buf->ptr;
  187. if (buf->asize == 0 || buf->ptr == cmark_strbuf__oom) {
  188. /* return an empty string */
  189. return (unsigned char *)calloc(1, 1);
  190. }
  191. cmark_strbuf_init(buf, 0);
  192. return data;
  193. }
  194. void cmark_strbuf_attach(cmark_strbuf *buf, unsigned char *ptr, int asize)
  195. {
  196. cmark_strbuf_free(buf);
  197. if (ptr) {
  198. buf->ptr = ptr;
  199. buf->size = strlen((char *)ptr);
  200. if (asize)
  201. buf->asize = (asize < buf->size) ? buf->size + 1 : asize;
  202. else /* pass 0 to fall back on strlen + 1 */
  203. buf->asize = buf->size + 1;
  204. } else {
  205. cmark_strbuf_grow(buf, asize);
  206. }
  207. }
  208. int cmark_strbuf_cmp(const cmark_strbuf *a, const cmark_strbuf *b)
  209. {
  210. int result = memcmp(a->ptr, b->ptr, MIN(a->size, b->size));
  211. return (result != 0) ? result :
  212. (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
  213. }
  214. int cmark_strbuf_strchr(const cmark_strbuf *buf, int c, int pos)
  215. {
  216. const unsigned char *p = (unsigned char *)memchr(buf->ptr + pos, c, buf->size - pos);
  217. if (!p)
  218. return -1;
  219. return (int)(p - (const unsigned char *)buf->ptr);
  220. }
  221. int cmark_strbuf_strrchr(const cmark_strbuf *buf, int c, int pos)
  222. {
  223. int i;
  224. for (i = pos; i >= 0; i--) {
  225. if (buf->ptr[i] == (unsigned char) c)
  226. return i;
  227. }
  228. return -1;
  229. }
  230. void cmark_strbuf_truncate(cmark_strbuf *buf, int len)
  231. {
  232. if (len < buf->size) {
  233. buf->size = len;
  234. buf->ptr[buf->size] = '\0';
  235. }
  236. }
  237. void cmark_strbuf_drop(cmark_strbuf *buf, int n)
  238. {
  239. if (n > 0) {
  240. buf->size = buf->size - n;
  241. if (buf->size)
  242. memmove(buf->ptr, buf->ptr + n, buf->size);
  243. buf->ptr[buf->size] = '\0';
  244. }
  245. }
  246. void cmark_strbuf_rtrim(cmark_strbuf *buf)
  247. {
  248. if (!buf->size)
  249. return;
  250. while (buf->size > 0) {
  251. if (!cmark_isspace(buf->ptr[buf->size - 1]))
  252. break;
  253. buf->size--;
  254. }
  255. buf->ptr[buf->size] = '\0';
  256. }
  257. void cmark_strbuf_trim(cmark_strbuf *buf)
  258. {
  259. int i = 0;
  260. if (!buf->size)
  261. return;
  262. while (i < buf->size && cmark_isspace(buf->ptr[i]))
  263. i++;
  264. cmark_strbuf_drop(buf, i);
  265. cmark_strbuf_rtrim(buf);
  266. }
  267. // Destructively modify string, collapsing consecutive
  268. // space and newline characters into a single space.
  269. void cmark_strbuf_normalize_whitespace(cmark_strbuf *s)
  270. {
  271. bool last_char_was_space = false;
  272. int r, w;
  273. for (r = 0, w = 0; r < s->size; ++r) {
  274. switch (s->ptr[r]) {
  275. case ' ':
  276. case '\n':
  277. if (last_char_was_space)
  278. break;
  279. s->ptr[w++] = ' ';
  280. last_char_was_space = true;
  281. break;
  282. default:
  283. s->ptr[w++] = s->ptr[r];
  284. last_char_was_space = false;
  285. }
  286. }
  287. cmark_strbuf_truncate(s, w);
  288. }
  289. // Destructively unescape a string: remove backslashes before punctuation chars.
  290. extern void cmark_strbuf_unescape(cmark_strbuf *buf)
  291. {
  292. int r, w;
  293. for (r = 0, w = 0; r < buf->size; ++r) {
  294. if (buf->ptr[r] == '\\' && cmark_ispunct(buf->ptr[r + 1]))
  295. continue;
  296. buf->ptr[w++] = buf->ptr[r];
  297. }
  298. cmark_strbuf_truncate(buf, w);
  299. }