[DOC] Tweak for String#+@ (#13285)
[ruby.git] / prism / util / pm_buffer.c
blob2136a7c43eefd5f658b04bc1280fdd4052f73219
1 #include "prism/util/pm_buffer.h"
3 /**
4 * Return the size of the pm_buffer_t struct.
5 */
6 size_t
7 pm_buffer_sizeof(void) {
8 return sizeof(pm_buffer_t);
11 /**
12 * Initialize a pm_buffer_t with the given capacity.
14 bool
15 pm_buffer_init_capacity(pm_buffer_t *buffer, size_t capacity) {
16 buffer->length = 0;
17 buffer->capacity = capacity;
19 buffer->value = (char *) xmalloc(capacity);
20 return buffer->value != NULL;
23 /**
24 * Initialize a pm_buffer_t with its default values.
26 bool
27 pm_buffer_init(pm_buffer_t *buffer) {
28 return pm_buffer_init_capacity(buffer, 1024);
31 /**
32 * Return the value of the buffer.
34 char *
35 pm_buffer_value(const pm_buffer_t *buffer) {
36 return buffer->value;
39 /**
40 * Return the length of the buffer.
42 size_t
43 pm_buffer_length(const pm_buffer_t *buffer) {
44 return buffer->length;
47 /**
48 * Append the given amount of space to the buffer.
50 static inline bool
51 pm_buffer_append_length(pm_buffer_t *buffer, size_t length) {
52 size_t next_length = buffer->length + length;
54 if (next_length > buffer->capacity) {
55 if (buffer->capacity == 0) {
56 buffer->capacity = 1;
59 while (next_length > buffer->capacity) {
60 buffer->capacity *= 2;
63 buffer->value = xrealloc(buffer->value, buffer->capacity);
64 if (buffer->value == NULL) return false;
67 buffer->length = next_length;
68 return true;
71 /**
72 * Append a generic pointer to memory to the buffer.
74 static inline void
75 pm_buffer_append(pm_buffer_t *buffer, const void *source, size_t length) {
76 size_t cursor = buffer->length;
77 if (pm_buffer_append_length(buffer, length)) {
78 memcpy(buffer->value + cursor, source, length);
82 /**
83 * Append the given amount of space as zeroes to the buffer.
85 void
86 pm_buffer_append_zeroes(pm_buffer_t *buffer, size_t length) {
87 size_t cursor = buffer->length;
88 if (pm_buffer_append_length(buffer, length)) {
89 memset(buffer->value + cursor, 0, length);
93 /**
94 * Append a formatted string to the buffer.
96 void
97 pm_buffer_append_format(pm_buffer_t *buffer, const char *format, ...) {
98 va_list arguments;
99 va_start(arguments, format);
100 int result = vsnprintf(NULL, 0, format, arguments);
101 va_end(arguments);
103 if (result < 0) return;
104 size_t length = (size_t) (result + 1);
106 size_t cursor = buffer->length;
107 if (pm_buffer_append_length(buffer, length)) {
108 va_start(arguments, format);
109 vsnprintf(buffer->value + cursor, length, format, arguments);
110 va_end(arguments);
111 buffer->length--;
116 * Append a string to the buffer.
118 void
119 pm_buffer_append_string(pm_buffer_t *buffer, const char *value, size_t length) {
120 pm_buffer_append(buffer, value, length);
124 * Append a list of bytes to the buffer.
126 void
127 pm_buffer_append_bytes(pm_buffer_t *buffer, const uint8_t *value, size_t length) {
128 pm_buffer_append(buffer, (const char *) value, length);
132 * Append a single byte to the buffer.
134 void
135 pm_buffer_append_byte(pm_buffer_t *buffer, uint8_t value) {
136 const void *source = &value;
137 pm_buffer_append(buffer, source, sizeof(uint8_t));
141 * Append a 32-bit unsigned integer to the buffer as a variable-length integer.
143 void
144 pm_buffer_append_varuint(pm_buffer_t *buffer, uint32_t value) {
145 if (value < 128) {
146 pm_buffer_append_byte(buffer, (uint8_t) value);
147 } else {
148 uint32_t n = value;
149 while (n >= 128) {
150 pm_buffer_append_byte(buffer, (uint8_t) (n | 128));
151 n >>= 7;
153 pm_buffer_append_byte(buffer, (uint8_t) n);
158 * Append a 32-bit signed integer to the buffer as a variable-length integer.
160 void
161 pm_buffer_append_varsint(pm_buffer_t *buffer, int32_t value) {
162 uint32_t unsigned_int = ((uint32_t)(value) << 1) ^ ((uint32_t)(value >> 31));
163 pm_buffer_append_varuint(buffer, unsigned_int);
167 * Append a double to the buffer.
169 void
170 pm_buffer_append_double(pm_buffer_t *buffer, double value) {
171 const void *source = &value;
172 pm_buffer_append(buffer, source, sizeof(double));
176 * Append a unicode codepoint to the buffer.
178 bool
179 pm_buffer_append_unicode_codepoint(pm_buffer_t *buffer, uint32_t value) {
180 if (value <= 0x7F) {
181 pm_buffer_append_byte(buffer, (uint8_t) value); // 0xxxxxxx
182 return true;
183 } else if (value <= 0x7FF) {
184 uint8_t bytes[] = {
185 (uint8_t) (0xC0 | ((value >> 6) & 0x3F)), // 110xxxxx
186 (uint8_t) (0x80 | (value & 0x3F)) // 10xxxxxx
189 pm_buffer_append_bytes(buffer, bytes, 2);
190 return true;
191 } else if (value <= 0xFFFF) {
192 uint8_t bytes[] = {
193 (uint8_t) (0xE0 | ((value >> 12) & 0x3F)), // 1110xxxx
194 (uint8_t) (0x80 | ((value >> 6) & 0x3F)), // 10xxxxxx
195 (uint8_t) (0x80 | (value & 0x3F)) // 10xxxxxx
198 pm_buffer_append_bytes(buffer, bytes, 3);
199 return true;
200 } else if (value <= 0x10FFFF) {
201 uint8_t bytes[] = {
202 (uint8_t) (0xF0 | ((value >> 18) & 0x3F)), // 11110xxx
203 (uint8_t) (0x80 | ((value >> 12) & 0x3F)), // 10xxxxxx
204 (uint8_t) (0x80 | ((value >> 6) & 0x3F)), // 10xxxxxx
205 (uint8_t) (0x80 | (value & 0x3F)) // 10xxxxxx
208 pm_buffer_append_bytes(buffer, bytes, 4);
209 return true;
210 } else {
211 return false;
216 * Append a slice of source code to the buffer.
218 void
219 pm_buffer_append_source(pm_buffer_t *buffer, const uint8_t *source, size_t length, pm_buffer_escaping_t escaping) {
220 for (size_t index = 0; index < length; index++) {
221 const uint8_t byte = source[index];
223 if ((byte <= 0x06) || (byte >= 0x0E && byte <= 0x1F) || (byte >= 0x7F)) {
224 if (escaping == PM_BUFFER_ESCAPING_RUBY) {
225 pm_buffer_append_format(buffer, "\\x%02X", byte);
226 } else {
227 pm_buffer_append_format(buffer, "\\u%04X", byte);
229 } else {
230 switch (byte) {
231 case '\a':
232 if (escaping == PM_BUFFER_ESCAPING_RUBY) {
233 pm_buffer_append_string(buffer, "\\a", 2);
234 } else {
235 pm_buffer_append_format(buffer, "\\u%04X", byte);
237 break;
238 case '\b':
239 pm_buffer_append_string(buffer, "\\b", 2);
240 break;
241 case '\t':
242 pm_buffer_append_string(buffer, "\\t", 2);
243 break;
244 case '\n':
245 pm_buffer_append_string(buffer, "\\n", 2);
246 break;
247 case '\v':
248 if (escaping == PM_BUFFER_ESCAPING_RUBY) {
249 pm_buffer_append_string(buffer, "\\v", 2);
250 } else {
251 pm_buffer_append_format(buffer, "\\u%04X", byte);
253 break;
254 case '\f':
255 pm_buffer_append_string(buffer, "\\f", 2);
256 break;
257 case '\r':
258 pm_buffer_append_string(buffer, "\\r", 2);
259 break;
260 case '"':
261 pm_buffer_append_string(buffer, "\\\"", 2);
262 break;
263 case '#': {
264 if (escaping == PM_BUFFER_ESCAPING_RUBY && index + 1 < length) {
265 const uint8_t next_byte = source[index + 1];
266 if (next_byte == '{' || next_byte == '@' || next_byte == '$') {
267 pm_buffer_append_byte(buffer, '\\');
271 pm_buffer_append_byte(buffer, '#');
272 break;
274 case '\\':
275 pm_buffer_append_string(buffer, "\\\\", 2);
276 break;
277 default:
278 pm_buffer_append_byte(buffer, byte);
279 break;
286 * Prepend the given string to the buffer.
288 void
289 pm_buffer_prepend_string(pm_buffer_t *buffer, const char *value, size_t length) {
290 size_t cursor = buffer->length;
291 if (pm_buffer_append_length(buffer, length)) {
292 memmove(buffer->value + length, buffer->value, cursor);
293 memcpy(buffer->value, value, length);
298 * Concatenate one buffer onto another.
300 void
301 pm_buffer_concat(pm_buffer_t *destination, const pm_buffer_t *source) {
302 if (source->length > 0) {
303 pm_buffer_append(destination, source->value, source->length);
308 * Clear the buffer by reducing its size to 0. This does not free the allocated
309 * memory, but it does allow the buffer to be reused.
311 void
312 pm_buffer_clear(pm_buffer_t *buffer) {
313 buffer->length = 0;
317 * Strip the whitespace from the end of the buffer.
319 void
320 pm_buffer_rstrip(pm_buffer_t *buffer) {
321 while (buffer->length > 0 && pm_char_is_whitespace((uint8_t) buffer->value[buffer->length - 1])) {
322 buffer->length--;
327 * Checks if the buffer includes the given value.
329 size_t
330 pm_buffer_index(const pm_buffer_t *buffer, char value) {
331 const char *first = memchr(buffer->value, value, buffer->length);
332 return (first == NULL) ? SIZE_MAX : (size_t) (first - buffer->value);
336 * Insert the given string into the buffer at the given index.
338 void
339 pm_buffer_insert(pm_buffer_t *buffer, size_t index, const char *value, size_t length) {
340 assert(index <= buffer->length);
342 if (index == buffer->length) {
343 pm_buffer_append_string(buffer, value, length);
344 } else {
345 pm_buffer_append_zeroes(buffer, length);
346 memmove(buffer->value + index + length, buffer->value + index, buffer->length - length - index);
347 memcpy(buffer->value + index, value, length);
352 * Free the memory associated with the buffer.
354 void
355 pm_buffer_free(pm_buffer_t *buffer) {
356 xfree(buffer->value);