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