Ruby 4.1.0dev (2026-10-26 revision 29264f878a52d2c36c75bfca6d04adaefeacbb30)
buffer.c
1#include "prism/internal/buffer.h"
2
4
5#include "prism/internal/char.h"
6#include "prism/internal/allocator.h"
7
8#include <assert.h>
9#include <stdarg.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13
17void
18pm_buffer_init(pm_buffer_t *buffer, size_t capacity) {
19 buffer->length = 0;
20 buffer->capacity = capacity;
21
22 buffer->value = (char *) xmalloc(capacity);
23 if (buffer->value == NULL) abort();
24}
25
30pm_buffer_new(void) {
31 pm_buffer_t *buffer = (pm_buffer_t *) xmalloc(sizeof(pm_buffer_t));
32 if (buffer == NULL) abort();
33
34 pm_buffer_init(buffer, 1024);
35 return buffer;
36}
37
41char *
42pm_buffer_value(const pm_buffer_t *buffer) {
43 return buffer->value;
44}
45
49size_t
50pm_buffer_length(const pm_buffer_t *buffer) {
51 return buffer->length;
52}
53
61static PRISM_INLINE bool
62pm_buffer_append_length(pm_buffer_t *buffer, size_t length) {
63 if (length == 0) return false;
64
65 size_t next_length = buffer->length + length;
66 const size_t original_capacity = buffer->capacity;
67
68 if (next_length > buffer->capacity) {
69 if (buffer->capacity == 0) {
70 buffer->capacity = 1;
71 }
72
73 while (next_length > buffer->capacity) {
74 buffer->capacity *= 2;
75 }
76
77 buffer->value = xrealloc_sized(buffer->value, buffer->capacity, original_capacity);
78 if (buffer->value == NULL) return false;
79 }
80
81 buffer->length = next_length;
82 return true;
83}
84
88static PRISM_INLINE void
89pm_buffer_append(pm_buffer_t *buffer, const void *source, size_t length) {
90 size_t cursor = buffer->length;
91 if (pm_buffer_append_length(buffer, length)) {
92 memcpy(buffer->value + cursor, source, length);
93 }
94}
95
99void
100pm_buffer_append_zeroes(pm_buffer_t *buffer, size_t length) {
101 size_t cursor = buffer->length;
102 if (pm_buffer_append_length(buffer, length)) {
103 memset(buffer->value + cursor, 0, length);
104 }
105}
106
110void
111pm_buffer_append_format(pm_buffer_t *buffer, const char *format, ...) {
112 va_list arguments;
113 va_start(arguments, format);
114 int result = vsnprintf(NULL, 0, format, arguments);
115 va_end(arguments);
116
117 if (result < 0) return;
118 size_t length = (size_t) (result + 1);
119
120 size_t cursor = buffer->length;
121 if (pm_buffer_append_length(buffer, length)) {
122 va_start(arguments, format);
123 vsnprintf(buffer->value + cursor, length, format, arguments);
124 va_end(arguments);
125 buffer->length--;
126 }
127}
128
132void
133pm_buffer_append_string(pm_buffer_t *buffer, const char *value, size_t length) {
134 pm_buffer_append(buffer, value, length);
135}
136
140void
141pm_buffer_append_bytes(pm_buffer_t *buffer, const uint8_t *value, size_t length) {
142 pm_buffer_append(buffer, (const char *) value, length);
143}
144
148void
149pm_buffer_append_byte(pm_buffer_t *buffer, uint8_t value) {
150 const void *source = &value;
151 pm_buffer_append(buffer, source, sizeof(uint8_t));
152}
153
157void
158pm_buffer_append_varuint(pm_buffer_t *buffer, uint32_t value) {
159 if (value < 128) {
160 pm_buffer_append_byte(buffer, (uint8_t) value);
161 } else {
162 uint32_t n = value;
163 while (n >= 128) {
164 pm_buffer_append_byte(buffer, (uint8_t) (n | 128));
165 n >>= 7;
166 }
167 pm_buffer_append_byte(buffer, (uint8_t) n);
168 }
169}
170
174void
175pm_buffer_append_varsint(pm_buffer_t *buffer, int32_t value) {
176 uint32_t unsigned_int = ((uint32_t)(value) << 1) ^ ((uint32_t)(value >> 31));
177 pm_buffer_append_varuint(buffer, unsigned_int);
178}
179
183void
184pm_buffer_append_double(pm_buffer_t *buffer, double value) {
185 const void *source = &value;
186 pm_buffer_append(buffer, source, sizeof(double));
187}
188
192void
193pm_buffer_append_uint64(pm_buffer_t *buffer, uint64_t value) {
194 const void *source = &value;
195 pm_buffer_append(buffer, source, sizeof(uint64_t));
196}
197
201bool
202pm_buffer_append_unicode_codepoint(pm_buffer_t *buffer, uint32_t value) {
203 if (value <= 0x7F) {
204 pm_buffer_append_byte(buffer, (uint8_t) value); // 0xxxxxxx
205 return true;
206 } else if (value <= 0x7FF) {
207 uint8_t bytes[] = {
208 (uint8_t) (0xC0 | ((value >> 6) & 0x3F)), // 110xxxxx
209 (uint8_t) (0x80 | (value & 0x3F)) // 10xxxxxx
210 };
211
212 pm_buffer_append_bytes(buffer, bytes, 2);
213 return true;
214 } else if (value <= 0xFFFF) {
215 uint8_t bytes[] = {
216 (uint8_t) (0xE0 | ((value >> 12) & 0x3F)), // 1110xxxx
217 (uint8_t) (0x80 | ((value >> 6) & 0x3F)), // 10xxxxxx
218 (uint8_t) (0x80 | (value & 0x3F)) // 10xxxxxx
219 };
220
221 pm_buffer_append_bytes(buffer, bytes, 3);
222 return true;
223 } else if (value <= 0x10FFFF) {
224 uint8_t bytes[] = {
225 (uint8_t) (0xF0 | ((value >> 18) & 0x3F)), // 11110xxx
226 (uint8_t) (0x80 | ((value >> 12) & 0x3F)), // 10xxxxxx
227 (uint8_t) (0x80 | ((value >> 6) & 0x3F)), // 10xxxxxx
228 (uint8_t) (0x80 | (value & 0x3F)) // 10xxxxxx
229 };
230
231 pm_buffer_append_bytes(buffer, bytes, 4);
232 return true;
233 } else {
234 return false;
235 }
236}
237
241void
242pm_buffer_append_source(pm_buffer_t *buffer, const uint8_t *source, size_t length, pm_buffer_escaping_t escaping) {
243 for (size_t index = 0; index < length; index++) {
244 const uint8_t byte = source[index];
245
246 if ((byte <= 0x06) || (byte >= 0x0E && byte <= 0x1F) || (byte >= 0x7F)) {
247 if (escaping == PM_BUFFER_ESCAPING_RUBY) {
248 pm_buffer_append_format(buffer, "\\x%02X", byte);
249 } else {
250 pm_buffer_append_format(buffer, "\\u%04X", byte);
251 }
252 } else {
253 switch (byte) {
254 case '\a':
255 if (escaping == PM_BUFFER_ESCAPING_RUBY) {
256 pm_buffer_append_string(buffer, "\\a", 2);
257 } else {
258 pm_buffer_append_format(buffer, "\\u%04X", byte);
259 }
260 break;
261 case '\b':
262 pm_buffer_append_string(buffer, "\\b", 2);
263 break;
264 case '\t':
265 pm_buffer_append_string(buffer, "\\t", 2);
266 break;
267 case '\n':
268 pm_buffer_append_string(buffer, "\\n", 2);
269 break;
270 case '\v':
271 if (escaping == PM_BUFFER_ESCAPING_RUBY) {
272 pm_buffer_append_string(buffer, "\\v", 2);
273 } else {
274 pm_buffer_append_format(buffer, "\\u%04X", byte);
275 }
276 break;
277 case '\f':
278 pm_buffer_append_string(buffer, "\\f", 2);
279 break;
280 case '\r':
281 pm_buffer_append_string(buffer, "\\r", 2);
282 break;
283 case '"':
284 pm_buffer_append_string(buffer, "\\\"", 2);
285 break;
286 case '#': {
287 if (escaping == PM_BUFFER_ESCAPING_RUBY && index + 1 < length) {
288 const uint8_t next_byte = source[index + 1];
289 if (next_byte == '{' || next_byte == '@' || next_byte == '$') {
290 pm_buffer_append_byte(buffer, '\\');
291 }
292 }
293
294 pm_buffer_append_byte(buffer, '#');
295 break;
296 }
297 case '\\':
298 pm_buffer_append_string(buffer, "\\\\", 2);
299 break;
300 default:
301 pm_buffer_append_byte(buffer, byte);
302 break;
303 }
304 }
305 }
306}
307
311void
312pm_buffer_prepend_string(pm_buffer_t *buffer, const char *value, size_t length) {
313 size_t cursor = buffer->length;
314 if (pm_buffer_append_length(buffer, length)) {
315 memmove(buffer->value + length, buffer->value, cursor);
316 memcpy(buffer->value, value, length);
317 }
318}
319
323void
324pm_buffer_concat(pm_buffer_t *destination, const pm_buffer_t *source) {
325 if (source->length > 0) {
326 pm_buffer_append(destination, source->value, source->length);
327 }
328}
329
334void
335pm_buffer_clear(pm_buffer_t *buffer) {
336 buffer->length = 0;
337}
338
342void
343pm_buffer_rstrip(pm_buffer_t *buffer) {
344 while (buffer->length > 0 && pm_char_is_whitespace((uint8_t) buffer->value[buffer->length - 1])) {
345 buffer->length--;
346 }
347}
348
352size_t
353pm_buffer_index(const pm_buffer_t *buffer, char value) {
354 const char *first = memchr(buffer->value, value, buffer->length);
355 return (first == NULL) ? SIZE_MAX : (size_t) (first - buffer->value);
356}
357
361void
362pm_buffer_insert(pm_buffer_t *buffer, size_t index, const char *value, size_t length) {
363 assert(index <= buffer->length);
364
365 if (index == buffer->length) {
366 pm_buffer_append_string(buffer, value, length);
367 } else {
368 pm_buffer_append_zeroes(buffer, length);
369 memmove(buffer->value + index + length, buffer->value + index, buffer->length - length - index);
370 memcpy(buffer->value + index, value, length);
371 }
372}
373
377void
378pm_buffer_cleanup(pm_buffer_t *buffer) {
379 xfree_sized(buffer->value, buffer->capacity);
380}
381
385void
386pm_buffer_free(pm_buffer_t *buffer) {
387 pm_buffer_cleanup(buffer);
388 xfree_sized(buffer, sizeof(pm_buffer_t));
389}
#define xmalloc
Old name of ruby_xmalloc.
Definition xmalloc.h:53
#define PRISM_INLINE
Old Visual Studio versions do not support the inline keyword, so we need to define it to be __inline.
Definition inline.h:12
Defines old _.