Ruby 4.1.0dev (2026-03-01 revision 19b636d3ecc8a824437e0d6abd7fe0c24b594ce0)
pm_buffer.c
2
6size_t
7pm_buffer_sizeof(void) {
8 return sizeof(pm_buffer_t);
9}
10
14bool
15pm_buffer_init_capacity(pm_buffer_t *buffer, size_t capacity) {
16 buffer->length = 0;
17 buffer->capacity = capacity;
18
19 buffer->value = (char *) xmalloc(capacity);
20 return buffer->value != NULL;
21}
22
26bool
28 return pm_buffer_init_capacity(buffer, 1024);
29}
30
34char *
36 return buffer->value;
37}
38
42size_t
44 return buffer->length;
45}
46
50static inline bool
51pm_buffer_append_length(pm_buffer_t *buffer, size_t length) {
52 size_t next_length = buffer->length + length;
53 const size_t original_capacity = buffer->capacity;
54
55 if (next_length > buffer->capacity) {
56 if (buffer->capacity == 0) {
57 buffer->capacity = 1;
58 }
59
60 while (next_length > buffer->capacity) {
61 buffer->capacity *= 2;
62 }
63
64 buffer->value = xrealloc_sized(buffer->value, buffer->capacity, original_capacity);
65 if (buffer->value == NULL) return false;
66 }
67
68 buffer->length = next_length;
69 return true;
70}
71
75static inline void
76pm_buffer_append(pm_buffer_t *buffer, const void *source, size_t length) {
77 size_t cursor = buffer->length;
78 if (pm_buffer_append_length(buffer, length)) {
79 memcpy(buffer->value + cursor, source, length);
80 }
81}
82
86void
87pm_buffer_append_zeroes(pm_buffer_t *buffer, size_t length) {
88 size_t cursor = buffer->length;
89 if (pm_buffer_append_length(buffer, length)) {
90 memset(buffer->value + cursor, 0, length);
91 }
92}
93
97void
98pm_buffer_append_format(pm_buffer_t *buffer, const char *format, ...) {
99 va_list arguments;
100 va_start(arguments, format);
101 int result = vsnprintf(NULL, 0, format, arguments);
102 va_end(arguments);
103
104 if (result < 0) return;
105 size_t length = (size_t) (result + 1);
106
107 size_t cursor = buffer->length;
108 if (pm_buffer_append_length(buffer, length)) {
109 va_start(arguments, format);
110 vsnprintf(buffer->value + cursor, length, format, arguments);
111 va_end(arguments);
112 buffer->length--;
113 }
114}
115
119void
120pm_buffer_append_string(pm_buffer_t *buffer, const char *value, size_t length) {
121 pm_buffer_append(buffer, value, length);
122}
123
127void
128pm_buffer_append_bytes(pm_buffer_t *buffer, const uint8_t *value, size_t length) {
129 pm_buffer_append(buffer, (const char *) value, length);
130}
131
135void
136pm_buffer_append_byte(pm_buffer_t *buffer, uint8_t value) {
137 const void *source = &value;
138 pm_buffer_append(buffer, source, sizeof(uint8_t));
139}
140
144void
145pm_buffer_append_varuint(pm_buffer_t *buffer, uint32_t value) {
146 if (value < 128) {
147 pm_buffer_append_byte(buffer, (uint8_t) value);
148 } else {
149 uint32_t n = value;
150 while (n >= 128) {
151 pm_buffer_append_byte(buffer, (uint8_t) (n | 128));
152 n >>= 7;
153 }
154 pm_buffer_append_byte(buffer, (uint8_t) n);
155 }
156}
157
161void
162pm_buffer_append_varsint(pm_buffer_t *buffer, int32_t value) {
163 uint32_t unsigned_int = ((uint32_t)(value) << 1) ^ ((uint32_t)(value >> 31));
164 pm_buffer_append_varuint(buffer, unsigned_int);
165}
166
170void
171pm_buffer_append_double(pm_buffer_t *buffer, double value) {
172 const void *source = &value;
173 pm_buffer_append(buffer, source, sizeof(double));
174}
175
179bool
180pm_buffer_append_unicode_codepoint(pm_buffer_t *buffer, uint32_t value) {
181 if (value <= 0x7F) {
182 pm_buffer_append_byte(buffer, (uint8_t) value); // 0xxxxxxx
183 return true;
184 } else if (value <= 0x7FF) {
185 uint8_t bytes[] = {
186 (uint8_t) (0xC0 | ((value >> 6) & 0x3F)), // 110xxxxx
187 (uint8_t) (0x80 | (value & 0x3F)) // 10xxxxxx
188 };
189
190 pm_buffer_append_bytes(buffer, bytes, 2);
191 return true;
192 } else if (value <= 0xFFFF) {
193 uint8_t bytes[] = {
194 (uint8_t) (0xE0 | ((value >> 12) & 0x3F)), // 1110xxxx
195 (uint8_t) (0x80 | ((value >> 6) & 0x3F)), // 10xxxxxx
196 (uint8_t) (0x80 | (value & 0x3F)) // 10xxxxxx
197 };
198
199 pm_buffer_append_bytes(buffer, bytes, 3);
200 return true;
201 } else if (value <= 0x10FFFF) {
202 uint8_t bytes[] = {
203 (uint8_t) (0xF0 | ((value >> 18) & 0x3F)), // 11110xxx
204 (uint8_t) (0x80 | ((value >> 12) & 0x3F)), // 10xxxxxx
205 (uint8_t) (0x80 | ((value >> 6) & 0x3F)), // 10xxxxxx
206 (uint8_t) (0x80 | (value & 0x3F)) // 10xxxxxx
207 };
208
209 pm_buffer_append_bytes(buffer, bytes, 4);
210 return true;
211 } else {
212 return false;
213 }
214}
215
219void
220pm_buffer_append_source(pm_buffer_t *buffer, const uint8_t *source, size_t length, pm_buffer_escaping_t escaping) {
221 for (size_t index = 0; index < length; index++) {
222 const uint8_t byte = source[index];
223
224 if ((byte <= 0x06) || (byte >= 0x0E && byte <= 0x1F) || (byte >= 0x7F)) {
225 if (escaping == PM_BUFFER_ESCAPING_RUBY) {
226 pm_buffer_append_format(buffer, "\\x%02X", byte);
227 } else {
228 pm_buffer_append_format(buffer, "\\u%04X", byte);
229 }
230 } else {
231 switch (byte) {
232 case '\a':
233 if (escaping == PM_BUFFER_ESCAPING_RUBY) {
234 pm_buffer_append_string(buffer, "\\a", 2);
235 } else {
236 pm_buffer_append_format(buffer, "\\u%04X", byte);
237 }
238 break;
239 case '\b':
240 pm_buffer_append_string(buffer, "\\b", 2);
241 break;
242 case '\t':
243 pm_buffer_append_string(buffer, "\\t", 2);
244 break;
245 case '\n':
246 pm_buffer_append_string(buffer, "\\n", 2);
247 break;
248 case '\v':
249 if (escaping == PM_BUFFER_ESCAPING_RUBY) {
250 pm_buffer_append_string(buffer, "\\v", 2);
251 } else {
252 pm_buffer_append_format(buffer, "\\u%04X", byte);
253 }
254 break;
255 case '\f':
256 pm_buffer_append_string(buffer, "\\f", 2);
257 break;
258 case '\r':
259 pm_buffer_append_string(buffer, "\\r", 2);
260 break;
261 case '"':
262 pm_buffer_append_string(buffer, "\\\"", 2);
263 break;
264 case '#': {
265 if (escaping == PM_BUFFER_ESCAPING_RUBY && index + 1 < length) {
266 const uint8_t next_byte = source[index + 1];
267 if (next_byte == '{' || next_byte == '@' || next_byte == '$') {
268 pm_buffer_append_byte(buffer, '\\');
269 }
270 }
271
272 pm_buffer_append_byte(buffer, '#');
273 break;
274 }
275 case '\\':
276 pm_buffer_append_string(buffer, "\\\\", 2);
277 break;
278 default:
279 pm_buffer_append_byte(buffer, byte);
280 break;
281 }
282 }
283 }
284}
285
289void
290pm_buffer_prepend_string(pm_buffer_t *buffer, const char *value, size_t length) {
291 size_t cursor = buffer->length;
292 if (pm_buffer_append_length(buffer, length)) {
293 memmove(buffer->value + length, buffer->value, cursor);
294 memcpy(buffer->value, value, length);
295 }
296}
297
301void
302pm_buffer_concat(pm_buffer_t *destination, const pm_buffer_t *source) {
303 if (source->length > 0) {
304 pm_buffer_append(destination, source->value, source->length);
305 }
306}
307
312void
313pm_buffer_clear(pm_buffer_t *buffer) {
314 buffer->length = 0;
315}
316
320void
321pm_buffer_rstrip(pm_buffer_t *buffer) {
322 while (buffer->length > 0 && pm_char_is_whitespace((uint8_t) buffer->value[buffer->length - 1])) {
323 buffer->length--;
324 }
325}
326
330size_t
331pm_buffer_index(const pm_buffer_t *buffer, char value) {
332 const char *first = memchr(buffer->value, value, buffer->length);
333 return (first == NULL) ? SIZE_MAX : (size_t) (first - buffer->value);
334}
335
339void
340pm_buffer_insert(pm_buffer_t *buffer, size_t index, const char *value, size_t length) {
341 assert(index <= buffer->length);
342
343 if (index == buffer->length) {
344 pm_buffer_append_string(buffer, value, length);
345 } else {
346 pm_buffer_append_zeroes(buffer, length);
347 memmove(buffer->value + index + length, buffer->value + index, buffer->length - length - index);
348 memcpy(buffer->value + index, value, length);
349 }
350}
351
355void
357 xfree_sized(buffer->value, buffer->capacity);
358}
#define xmalloc
Old name of ruby_xmalloc.
Definition xmalloc.h:53
void pm_buffer_free(pm_buffer_t *buffer)
Free the memory associated with the buffer.
Definition pm_buffer.c:356
bool pm_buffer_init(pm_buffer_t *buffer)
Initialize a pm_buffer_t with its default values.
Definition pm_buffer.c:27
size_t pm_buffer_length(const pm_buffer_t *buffer)
Return the length of the buffer.
Definition pm_buffer.c:43
char * pm_buffer_value(const pm_buffer_t *buffer)
Return the value of the buffer.
Definition pm_buffer.c:35
A wrapper around a contiguous block of allocated memory.
pm_buffer_escaping_t
The different types of escaping that can be performed by the buffer when appending a slice of Ruby so...
Definition pm_buffer.h:160
A pm_buffer_t is a simple memory buffer that stores data in a contiguous block of memory.
Definition pm_buffer.h:22
size_t capacity
The capacity of the buffer in bytes that has been allocated.
Definition pm_buffer.h:27
size_t length
The length of the buffer in bytes.
Definition pm_buffer.h:24
char * value
A pointer to the start of the buffer.
Definition pm_buffer.h:30