Coverage Report

Created: 2026-06-03 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ruby/prism/buffer.c
Line
Count
Source
1
#include "prism/internal/buffer.h"
2
3
#include "prism/compiler/inline.h"
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
14
/**
15
 * Initialize a pm_buffer_t with the given capacity.
16
 */
17
void
18
0
pm_buffer_init(pm_buffer_t *buffer, size_t capacity) {
19
0
    buffer->length = 0;
20
0
    buffer->capacity = capacity;
21
22
0
    buffer->value = (char *) xmalloc(capacity);
23
0
    if (buffer->value == NULL) abort();
24
0
}
25
26
/**
27
 * Allocate and initialize a new buffer.
28
 */
29
pm_buffer_t *
30
0
pm_buffer_new(void) {
31
0
    pm_buffer_t *buffer = (pm_buffer_t *) xmalloc(sizeof(pm_buffer_t));
32
0
    if (buffer == NULL) abort();
33
34
0
    pm_buffer_init(buffer, 1024);
35
0
    return buffer;
36
0
}
37
38
/**
39
 * Return the value of the buffer.
40
 */
41
char *
42
0
pm_buffer_value(const pm_buffer_t *buffer) {
43
0
    return buffer->value;
44
0
}
45
46
/**
47
 * Return the length of the buffer.
48
 */
49
size_t
50
0
pm_buffer_length(const pm_buffer_t *buffer) {
51
0
    return buffer->length;
52
0
}
53
54
/**
55
 * Append the given amount of space to the buffer.
56
 */
57
static PRISM_INLINE bool
58
0
pm_buffer_append_length(pm_buffer_t *buffer, size_t length) {
59
0
    size_t next_length = buffer->length + length;
60
0
    const size_t original_capacity = buffer->capacity;
61
62
0
    if (next_length > buffer->capacity) {
63
0
        if (buffer->capacity == 0) {
64
0
            buffer->capacity = 1;
65
0
        }
66
67
0
        while (next_length > buffer->capacity) {
68
0
            buffer->capacity *= 2;
69
0
        }
70
71
0
        buffer->value = xrealloc_sized(buffer->value, buffer->capacity, original_capacity);
72
0
        if (buffer->value == NULL) return false;
73
0
    }
74
75
0
    buffer->length = next_length;
76
0
    return true;
77
0
}
78
79
/**
80
 * Append a generic pointer to memory to the buffer.
81
 */
82
static PRISM_INLINE void
83
0
pm_buffer_append(pm_buffer_t *buffer, const void *source, size_t length) {
84
0
    size_t cursor = buffer->length;
85
0
    if (pm_buffer_append_length(buffer, length)) {
86
0
        memcpy(buffer->value + cursor, source, length);
87
0
    }
88
0
}
89
90
/**
91
 * Append the given amount of space as zeroes to the buffer.
92
 */
93
void
94
0
pm_buffer_append_zeroes(pm_buffer_t *buffer, size_t length) {
95
0
    size_t cursor = buffer->length;
96
0
    if (pm_buffer_append_length(buffer, length)) {
97
0
        memset(buffer->value + cursor, 0, length);
98
0
    }
99
0
}
100
101
/**
102
 * Append a formatted string to the buffer.
103
 */
104
void
105
0
pm_buffer_append_format(pm_buffer_t *buffer, const char *format, ...) {
106
0
    va_list arguments;
107
0
    va_start(arguments, format);
108
0
    int result = vsnprintf(NULL, 0, format, arguments);
109
0
    va_end(arguments);
110
111
0
    if (result < 0) return;
112
0
    size_t length = (size_t) (result + 1);
113
114
0
    size_t cursor = buffer->length;
115
0
    if (pm_buffer_append_length(buffer, length)) {
116
0
        va_start(arguments, format);
117
0
        vsnprintf(buffer->value + cursor, length, format, arguments);
118
0
        va_end(arguments);
119
0
        buffer->length--;
120
0
    }
121
0
}
122
123
/**
124
 * Append a string to the buffer.
125
 */
126
void
127
0
pm_buffer_append_string(pm_buffer_t *buffer, const char *value, size_t length) {
128
0
    pm_buffer_append(buffer, value, length);
129
0
}
130
131
/**
132
 * Append a list of bytes to the buffer.
133
 */
134
void
135
0
pm_buffer_append_bytes(pm_buffer_t *buffer, const uint8_t *value, size_t length) {
136
0
    pm_buffer_append(buffer, (const char *) value, length);
137
0
}
138
139
/**
140
 * Append a single byte to the buffer.
141
 */
142
void
143
0
pm_buffer_append_byte(pm_buffer_t *buffer, uint8_t value) {
144
0
    const void *source = &value;
145
0
    pm_buffer_append(buffer, source, sizeof(uint8_t));
146
0
}
147
148
/**
149
 * Append a 32-bit unsigned integer to the buffer as a variable-length integer.
150
 */
151
void
152
0
pm_buffer_append_varuint(pm_buffer_t *buffer, uint32_t value) {
153
0
    if (value < 128) {
154
0
        pm_buffer_append_byte(buffer, (uint8_t) value);
155
0
    } else {
156
0
        uint32_t n = value;
157
0
        while (n >= 128) {
158
0
            pm_buffer_append_byte(buffer, (uint8_t) (n | 128));
159
0
            n >>= 7;
160
0
        }
161
0
        pm_buffer_append_byte(buffer, (uint8_t) n);
162
0
    }
163
0
}
164
165
/**
166
 * Append a 32-bit signed integer to the buffer as a variable-length integer.
167
 */
168
void
169
0
pm_buffer_append_varsint(pm_buffer_t *buffer, int32_t value) {
170
0
    uint32_t unsigned_int = ((uint32_t)(value) << 1) ^ ((uint32_t)(value >> 31));
171
0
    pm_buffer_append_varuint(buffer, unsigned_int);
172
0
}
173
174
/**
175
 * Append a double to the buffer.
176
 */
177
void
178
0
pm_buffer_append_double(pm_buffer_t *buffer, double value) {
179
0
    const void *source = &value;
180
0
    pm_buffer_append(buffer, source, sizeof(double));
181
0
}
182
183
/**
184
 * Append a unicode codepoint to the buffer.
185
 */
186
bool
187
0
pm_buffer_append_unicode_codepoint(pm_buffer_t *buffer, uint32_t value) {
188
0
    if (value <= 0x7F) {
189
0
        pm_buffer_append_byte(buffer, (uint8_t) value); // 0xxxxxxx
190
0
        return true;
191
0
    } else if (value <= 0x7FF) {
192
0
        uint8_t bytes[] = {
193
0
            (uint8_t) (0xC0 | ((value >> 6) & 0x3F)), // 110xxxxx
194
0
            (uint8_t) (0x80 | (value & 0x3F))         // 10xxxxxx
195
0
        };
196
197
0
        pm_buffer_append_bytes(buffer, bytes, 2);
198
0
        return true;
199
0
    } else if (value <= 0xFFFF) {
200
0
        uint8_t bytes[] = {
201
0
            (uint8_t) (0xE0 | ((value >> 12) & 0x3F)), // 1110xxxx
202
0
            (uint8_t) (0x80 | ((value >> 6) & 0x3F)),  // 10xxxxxx
203
0
            (uint8_t) (0x80 | (value & 0x3F))          // 10xxxxxx
204
0
        };
205
206
0
        pm_buffer_append_bytes(buffer, bytes, 3);
207
0
        return true;
208
0
    } else if (value <= 0x10FFFF) {
209
0
        uint8_t bytes[] = {
210
0
            (uint8_t) (0xF0 | ((value >> 18) & 0x3F)), // 11110xxx
211
0
            (uint8_t) (0x80 | ((value >> 12) & 0x3F)), // 10xxxxxx
212
0
            (uint8_t) (0x80 | ((value >> 6) & 0x3F)),  // 10xxxxxx
213
0
            (uint8_t) (0x80 | (value & 0x3F))          // 10xxxxxx
214
0
        };
215
216
0
        pm_buffer_append_bytes(buffer, bytes, 4);
217
0
        return true;
218
0
    } else {
219
0
        return false;
220
0
    }
221
0
}
222
223
/**
224
 * Append a slice of source code to the buffer.
225
 */
226
void
227
0
pm_buffer_append_source(pm_buffer_t *buffer, const uint8_t *source, size_t length, pm_buffer_escaping_t escaping) {
228
0
    for (size_t index = 0; index < length; index++) {
229
0
        const uint8_t byte = source[index];
230
231
0
        if ((byte <= 0x06) || (byte >= 0x0E && byte <= 0x1F) || (byte >= 0x7F)) {
232
0
            if (escaping == PM_BUFFER_ESCAPING_RUBY) {
233
0
                pm_buffer_append_format(buffer, "\\x%02X", byte);
234
0
            } else {
235
0
                pm_buffer_append_format(buffer, "\\u%04X", byte);
236
0
            }
237
0
        } else {
238
0
            switch (byte) {
239
0
                case '\a':
240
0
                    if (escaping == PM_BUFFER_ESCAPING_RUBY) {
241
0
                        pm_buffer_append_string(buffer, "\\a", 2);
242
0
                    } else {
243
0
                        pm_buffer_append_format(buffer, "\\u%04X", byte);
244
0
                    }
245
0
                    break;
246
0
                case '\b':
247
0
                    pm_buffer_append_string(buffer, "\\b", 2);
248
0
                    break;
249
0
                case '\t':
250
0
                    pm_buffer_append_string(buffer, "\\t", 2);
251
0
                    break;
252
0
                case '\n':
253
0
                    pm_buffer_append_string(buffer, "\\n", 2);
254
0
                    break;
255
0
                case '\v':
256
0
                    if (escaping == PM_BUFFER_ESCAPING_RUBY) {
257
0
                        pm_buffer_append_string(buffer, "\\v", 2);
258
0
                    } else {
259
0
                        pm_buffer_append_format(buffer, "\\u%04X", byte);
260
0
                    }
261
0
                    break;
262
0
                case '\f':
263
0
                    pm_buffer_append_string(buffer, "\\f", 2);
264
0
                    break;
265
0
                case '\r':
266
0
                    pm_buffer_append_string(buffer, "\\r", 2);
267
0
                    break;
268
0
                case '"':
269
0
                    pm_buffer_append_string(buffer, "\\\"", 2);
270
0
                    break;
271
0
                case '#': {
272
0
                    if (escaping == PM_BUFFER_ESCAPING_RUBY && index + 1 < length) {
273
0
                        const uint8_t next_byte = source[index + 1];
274
0
                        if (next_byte == '{' || next_byte == '@' || next_byte == '$') {
275
0
                            pm_buffer_append_byte(buffer, '\\');
276
0
                        }
277
0
                    }
278
279
0
                    pm_buffer_append_byte(buffer, '#');
280
0
                    break;
281
0
                }
282
0
                case '\\':
283
0
                    pm_buffer_append_string(buffer, "\\\\", 2);
284
0
                    break;
285
0
                default:
286
0
                    pm_buffer_append_byte(buffer, byte);
287
0
                    break;
288
0
            }
289
0
        }
290
0
    }
291
0
}
292
293
/**
294
 * Prepend the given string to the buffer.
295
 */
296
void
297
0
pm_buffer_prepend_string(pm_buffer_t *buffer, const char *value, size_t length) {
298
0
    size_t cursor = buffer->length;
299
0
    if (pm_buffer_append_length(buffer, length)) {
300
0
        memmove(buffer->value + length, buffer->value, cursor);
301
0
        memcpy(buffer->value, value, length);
302
0
    }
303
0
}
304
305
/**
306
 * Concatenate one buffer onto another.
307
 */
308
void
309
0
pm_buffer_concat(pm_buffer_t *destination, const pm_buffer_t *source) {
310
0
    if (source->length > 0) {
311
0
        pm_buffer_append(destination, source->value, source->length);
312
0
    }
313
0
}
314
315
/**
316
 * Clear the buffer by reducing its size to 0. This does not free the allocated
317
 * memory, but it does allow the buffer to be reused.
318
 */
319
void
320
0
pm_buffer_clear(pm_buffer_t *buffer) {
321
0
    buffer->length = 0;
322
0
}
323
324
/**
325
 * Strip the whitespace from the end of the buffer.
326
 */
327
void
328
0
pm_buffer_rstrip(pm_buffer_t *buffer) {
329
0
    while (buffer->length > 0 && pm_char_is_whitespace((uint8_t) buffer->value[buffer->length - 1])) {
330
0
        buffer->length--;
331
0
    }
332
0
}
333
334
/**
335
 * Checks if the buffer includes the given value.
336
 */
337
size_t
338
0
pm_buffer_index(const pm_buffer_t *buffer, char value) {
339
0
    const char *first = memchr(buffer->value, value, buffer->length);
340
0
    return (first == NULL) ? SIZE_MAX : (size_t) (first - buffer->value);
341
0
}
342
343
/**
344
 * Insert the given string into the buffer at the given index.
345
 */
346
void
347
0
pm_buffer_insert(pm_buffer_t *buffer, size_t index, const char *value, size_t length) {
348
0
    assert(index <= buffer->length);
349
350
0
    if (index == buffer->length) {
351
0
        pm_buffer_append_string(buffer, value, length);
352
0
    } else {
353
0
        pm_buffer_append_zeroes(buffer, length);
354
0
        memmove(buffer->value + index + length, buffer->value + index, buffer->length - length - index);
355
0
        memcpy(buffer->value + index, value, length);
356
0
    }
357
0
}
358
359
/**
360
 * Free the memory held by the buffer.
361
 */
362
void
363
0
pm_buffer_cleanup(pm_buffer_t *buffer) {
364
0
    xfree_sized(buffer->value, buffer->capacity);
365
0
}
366
367
/**
368
 * Free both the memory held by the buffer and the buffer itself.
369
 */
370
void
371
0
pm_buffer_free(pm_buffer_t *buffer) {
372
0
    pm_buffer_cleanup(buffer);
373
0
    xfree_sized(buffer, sizeof(pm_buffer_t));
374
0
}