cmark

My personal build of CMark ✏️

buffer.c (6489B)

  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 <stdint.h>
  8 #include <limits.h>
  9 
 10 #include "config.h"
 11 #include "cmark_ctype.h"
 12 #include "buffer.h"
 13 
 14 /* Used as default value for cmark_strbuf->ptr so that people can always
 15  * assume ptr is non-NULL and zero terminated even for new cmark_strbufs.
 16  */
 17 unsigned char cmark_strbuf__initbuf[1];
 18 
 19 #ifndef MIN
 20 #define MIN(x, y) ((x < y) ? x : y)
 21 #endif
 22 
 23 void cmark_strbuf_init(cmark_mem *mem, cmark_strbuf *buf,
 24                        bufsize_t initial_size) {
 25   buf->mem = mem;
 26   buf->asize = 0;
 27   buf->size = 0;
 28   buf->ptr = cmark_strbuf__initbuf;
 29 
 30   if (initial_size > 0)
 31     cmark_strbuf_grow(buf, initial_size);
 32 }
 33 
 34 static CMARK_INLINE void S_strbuf_grow_by(cmark_strbuf *buf, bufsize_t add) {
 35   cmark_strbuf_grow(buf, buf->size + add);
 36 }
 37 
 38 void cmark_strbuf_grow(cmark_strbuf *buf, bufsize_t target_size) {
 39   assert(target_size > 0);
 40 
 41   if (target_size < buf->asize)
 42     return;
 43 
 44   if (target_size > (bufsize_t)(INT32_MAX / 2)) {
 45     fprintf(stderr,
 46       "[cmark] cmark_strbuf_grow requests buffer with size > %d, aborting\n",
 47          (INT32_MAX / 2));
 48     abort();
 49   }
 50 
 51   /* Oversize the buffer by 50% to guarantee amortized linear time
 52    * complexity on append operations. */
 53   bufsize_t new_size = target_size + target_size / 2;
 54   new_size += 1;
 55   new_size = (new_size + 7) & ~7;
 56 
 57   buf->ptr = (unsigned char *)buf->mem->realloc(buf->asize ? buf->ptr : NULL,
 58                                                 new_size);
 59   buf->asize = new_size;
 60 }
 61 
 62 bufsize_t cmark_strbuf_len(const cmark_strbuf *buf) { return buf->size; }
 63 
 64 void cmark_strbuf_free(cmark_strbuf *buf) {
 65   if (!buf)
 66     return;
 67 
 68   if (buf->ptr != cmark_strbuf__initbuf)
 69     buf->mem->free(buf->ptr);
 70 
 71   cmark_strbuf_init(buf->mem, buf, 0);
 72 }
 73 
 74 void cmark_strbuf_clear(cmark_strbuf *buf) {
 75   buf->size = 0;
 76 
 77   if (buf->asize > 0)
 78     buf->ptr[0] = '\0';
 79 }
 80 
 81 void cmark_strbuf_set(cmark_strbuf *buf, const unsigned char *data,
 82                       bufsize_t len) {
 83   if (len <= 0 || data == NULL) {
 84     cmark_strbuf_clear(buf);
 85   } else {
 86     if (data != buf->ptr) {
 87       if (len >= buf->asize)
 88         cmark_strbuf_grow(buf, len);
 89       memmove(buf->ptr, data, len);
 90     }
 91     buf->size = len;
 92     buf->ptr[buf->size] = '\0';
 93   }
 94 }
 95 
 96 void cmark_strbuf_sets(cmark_strbuf *buf, const char *string) {
 97   cmark_strbuf_set(buf, (const unsigned char *)string,
 98                    string ? strlen(string) : 0);
 99 }
100 
101 void cmark_strbuf_putc(cmark_strbuf *buf, int c) {
102   S_strbuf_grow_by(buf, 1);
103   buf->ptr[buf->size++] = (unsigned char)(c & 0xFF);
104   buf->ptr[buf->size] = '\0';
105 }
106 
107 void cmark_strbuf_put(cmark_strbuf *buf, const unsigned char *data,
108                       bufsize_t len) {
109   if (len <= 0)
110     return;
111 
112   S_strbuf_grow_by(buf, len);
113   memmove(buf->ptr + buf->size, data, len);
114   buf->size += len;
115   buf->ptr[buf->size] = '\0';
116 }
117 
118 void cmark_strbuf_puts(cmark_strbuf *buf, const char *string) {
119   cmark_strbuf_put(buf, (const unsigned char *)string, strlen(string));
120 }
121 
122 void cmark_strbuf_copy_cstr(char *data, bufsize_t datasize,
123                             const cmark_strbuf *buf) {
124   bufsize_t copylen;
125 
126   assert(buf);
127   if (!data || datasize <= 0)
128     return;
129 
130   data[0] = '\0';
131 
132   if (buf->size == 0 || buf->asize <= 0)
133     return;
134 
135   copylen = buf->size;
136   if (copylen > datasize - 1)
137     copylen = datasize - 1;
138   memmove(data, buf->ptr, copylen);
139   data[copylen] = '\0';
140 }
141 
142 void cmark_strbuf_swap(cmark_strbuf *buf_a, cmark_strbuf *buf_b) {
143   cmark_strbuf t = *buf_a;
144   *buf_a = *buf_b;
145   *buf_b = t;
146 }
147 
148 unsigned char *cmark_strbuf_detach(cmark_strbuf *buf) {
149   unsigned char *data = buf->ptr;
150 
151   if (buf->asize == 0) {
152     /* return an empty string */
153     return (unsigned char *)buf->mem->calloc(1, 1);
154   }
155 
156   cmark_strbuf_init(buf->mem, buf, 0);
157   return data;
158 }
159 
160 int cmark_strbuf_cmp(const cmark_strbuf *a, const cmark_strbuf *b) {
161   int result = memcmp(a->ptr, b->ptr, MIN(a->size, b->size));
162   return (result != 0) ? result
163                        : (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
164 }
165 
166 bufsize_t cmark_strbuf_strchr(const cmark_strbuf *buf, int c, bufsize_t pos) {
167   if (pos >= buf->size)
168     return -1;
169   if (pos < 0)
170     pos = 0;
171 
172   const unsigned char *p =
173       (unsigned char *)memchr(buf->ptr + pos, c, buf->size - pos);
174   if (!p)
175     return -1;
176 
177   return (bufsize_t)(p - (const unsigned char *)buf->ptr);
178 }
179 
180 bufsize_t cmark_strbuf_strrchr(const cmark_strbuf *buf, int c, bufsize_t pos) {
181   if (pos < 0 || buf->size == 0)
182     return -1;
183   if (pos >= buf->size)
184     pos = buf->size - 1;
185 
186   bufsize_t i;
187   for (i = pos; i >= 0; i--) {
188     if (buf->ptr[i] == (unsigned char)c)
189       return i;
190   }
191 
192   return -1;
193 }
194 
195 void cmark_strbuf_truncate(cmark_strbuf *buf, bufsize_t len) {
196   if (len < 0)
197     len = 0;
198 
199   if (len < buf->size) {
200     buf->size = len;
201     buf->ptr[buf->size] = '\0';
202   }
203 }
204 
205 void cmark_strbuf_drop(cmark_strbuf *buf, bufsize_t n) {
206   if (n > 0) {
207     if (n > buf->size)
208       n = buf->size;
209     buf->size = buf->size - n;
210     if (buf->size)
211       memmove(buf->ptr, buf->ptr + n, buf->size);
212 
213     buf->ptr[buf->size] = '\0';
214   }
215 }
216 
217 void cmark_strbuf_rtrim(cmark_strbuf *buf) {
218   if (!buf->size)
219     return;
220 
221   while (buf->size > 0) {
222     if (!cmark_isspace(buf->ptr[buf->size - 1]))
223       break;
224 
225     buf->size--;
226   }
227 
228   buf->ptr[buf->size] = '\0';
229 }
230 
231 void cmark_strbuf_trim(cmark_strbuf *buf) {
232   bufsize_t i = 0;
233 
234   if (!buf->size)
235     return;
236 
237   while (i < buf->size && cmark_isspace(buf->ptr[i]))
238     i++;
239 
240   cmark_strbuf_drop(buf, i);
241 
242   cmark_strbuf_rtrim(buf);
243 }
244 
245 // Destructively modify string, collapsing consecutive
246 // space and newline characters into a single space.
247 void cmark_strbuf_normalize_whitespace(cmark_strbuf *s) {
248   bool last_char_was_space = false;
249   bufsize_t r, w;
250 
251   for (r = 0, w = 0; r < s->size; ++r) {
252     if (cmark_isspace(s->ptr[r])) {
253       if (!last_char_was_space) {
254         s->ptr[w++] = ' ';
255         last_char_was_space = true;
256       }
257     } else {
258       s->ptr[w++] = s->ptr[r];
259       last_char_was_space = false;
260     }
261   }
262 
263   cmark_strbuf_truncate(s, w);
264 }
265 
266 // Destructively unescape a string: remove backslashes before punctuation chars.
267 extern void cmark_strbuf_unescape(cmark_strbuf *buf) {
268   bufsize_t r, w;
269 
270   for (r = 0, w = 0; r < buf->size; ++r) {
271     if (buf->ptr[r] == '\\' && cmark_ispunct(buf->ptr[r + 1]))
272       r++;
273 
274     buf->ptr[w++] = buf->ptr[r];
275   }
276 
277   cmark_strbuf_truncate(buf, w);
278 }