herb 0.7.2-arm-linux-gnu → 0.7.3-arm-linux-gnu

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/Makefile +2 -0
  3. data/README.md +1 -1
  4. data/Rakefile +46 -1
  5. data/config.yml +714 -0
  6. data/ext/herb/extconf.rb +2 -1
  7. data/ext/herb/nodes.c +1 -1
  8. data/herb.gemspec +3 -0
  9. data/lib/herb/3.0/herb.so +0 -0
  10. data/lib/herb/3.1/herb.so +0 -0
  11. data/lib/herb/3.2/herb.so +0 -0
  12. data/lib/herb/3.3/herb.so +0 -0
  13. data/lib/herb/3.4/herb.so +0 -0
  14. data/lib/herb/version.rb +1 -1
  15. data/src/analyze.c +5 -9
  16. data/src/analyze_helpers.c +17 -6
  17. data/src/include/pretty_print.h +1 -1
  18. data/src/include/version.h +1 -1
  19. data/src/parser.c +1 -0
  20. data/src/pretty_print.c +1 -1
  21. data/templates/ext/herb/error_helpers.c.erb +85 -0
  22. data/templates/ext/herb/error_helpers.h.erb +12 -0
  23. data/templates/ext/herb/nodes.c.erb +90 -0
  24. data/templates/ext/herb/nodes.h.erb +9 -0
  25. data/templates/javascript/packages/core/src/errors.ts.erb +193 -0
  26. data/templates/javascript/packages/core/src/node-type-guards.ts.erb +325 -0
  27. data/templates/javascript/packages/core/src/nodes.ts.erb +414 -0
  28. data/templates/javascript/packages/core/src/visitor.ts.erb +29 -0
  29. data/templates/javascript/packages/node/extension/error_helpers.cpp.erb +113 -0
  30. data/templates/javascript/packages/node/extension/error_helpers.h.erb +17 -0
  31. data/templates/javascript/packages/node/extension/nodes.cpp.erb +111 -0
  32. data/templates/javascript/packages/node/extension/nodes.h.erb +17 -0
  33. data/templates/lib/herb/ast/nodes.rb.erb +117 -0
  34. data/templates/lib/herb/errors.rb.erb +106 -0
  35. data/templates/lib/herb/visitor.rb.erb +28 -0
  36. data/templates/sig/serialized_ast_errors.rbs.erb +10 -0
  37. data/templates/sig/serialized_ast_nodes.rbs.erb +10 -0
  38. data/templates/src/ast_nodes.c.erb +145 -0
  39. data/templates/src/ast_pretty_print.c.erb +97 -0
  40. data/templates/src/errors.c.erb +245 -0
  41. data/templates/src/include/ast_nodes.h.erb +46 -0
  42. data/templates/src/include/ast_pretty_print.h.erb +14 -0
  43. data/templates/src/include/errors.h.erb +58 -0
  44. data/templates/src/visitor.c.erb +47 -0
  45. data/templates/template.rb +406 -0
  46. data/templates/wasm/error_helpers.cpp.erb +93 -0
  47. data/templates/wasm/error_helpers.h.erb +15 -0
  48. data/templates/wasm/nodes.cpp.erb +79 -0
  49. data/templates/wasm/nodes.h.erb +15 -0
  50. data/vendor/prism/Rakefile +75 -0
  51. data/vendor/prism/config.yml +4713 -0
  52. data/vendor/prism/include/prism/ast.h +8190 -0
  53. data/vendor/prism/include/prism/defines.h +260 -0
  54. data/vendor/prism/include/prism/diagnostic.h +455 -0
  55. data/vendor/prism/include/prism/encoding.h +283 -0
  56. data/vendor/prism/include/prism/node.h +129 -0
  57. data/vendor/prism/include/prism/options.h +482 -0
  58. data/vendor/prism/include/prism/pack.h +163 -0
  59. data/vendor/prism/include/prism/parser.h +933 -0
  60. data/vendor/prism/include/prism/prettyprint.h +34 -0
  61. data/vendor/prism/include/prism/regexp.h +43 -0
  62. data/vendor/prism/include/prism/static_literals.h +121 -0
  63. data/vendor/prism/include/prism/util/pm_buffer.h +236 -0
  64. data/vendor/prism/include/prism/util/pm_char.h +204 -0
  65. data/vendor/prism/include/prism/util/pm_constant_pool.h +218 -0
  66. data/vendor/prism/include/prism/util/pm_integer.h +130 -0
  67. data/vendor/prism/include/prism/util/pm_list.h +103 -0
  68. data/vendor/prism/include/prism/util/pm_memchr.h +29 -0
  69. data/vendor/prism/include/prism/util/pm_newline_list.h +113 -0
  70. data/vendor/prism/include/prism/util/pm_string.h +200 -0
  71. data/vendor/prism/include/prism/util/pm_strncasecmp.h +32 -0
  72. data/vendor/prism/include/prism/util/pm_strpbrk.h +46 -0
  73. data/vendor/prism/include/prism/version.h +29 -0
  74. data/vendor/prism/include/prism.h +408 -0
  75. data/vendor/prism/src/diagnostic.c +848 -0
  76. data/vendor/prism/src/encoding.c +5235 -0
  77. data/vendor/prism/src/node.c +8676 -0
  78. data/vendor/prism/src/options.c +328 -0
  79. data/vendor/prism/src/pack.c +509 -0
  80. data/vendor/prism/src/prettyprint.c +8941 -0
  81. data/vendor/prism/src/prism.c +23302 -0
  82. data/vendor/prism/src/regexp.c +790 -0
  83. data/vendor/prism/src/serialize.c +2268 -0
  84. data/vendor/prism/src/static_literals.c +617 -0
  85. data/vendor/prism/src/token_type.c +703 -0
  86. data/vendor/prism/src/util/pm_buffer.c +357 -0
  87. data/vendor/prism/src/util/pm_char.c +318 -0
  88. data/vendor/prism/src/util/pm_constant_pool.c +342 -0
  89. data/vendor/prism/src/util/pm_integer.c +670 -0
  90. data/vendor/prism/src/util/pm_list.c +49 -0
  91. data/vendor/prism/src/util/pm_memchr.c +35 -0
  92. data/vendor/prism/src/util/pm_newline_list.c +125 -0
  93. data/vendor/prism/src/util/pm_string.c +383 -0
  94. data/vendor/prism/src/util/pm_strncasecmp.c +36 -0
  95. data/vendor/prism/src/util/pm_strpbrk.c +206 -0
  96. data/vendor/prism/templates/ext/prism/api_node.c.erb +282 -0
  97. data/vendor/prism/templates/include/prism/ast.h.erb +226 -0
  98. data/vendor/prism/templates/include/prism/diagnostic.h.erb +130 -0
  99. data/vendor/prism/templates/java/org/prism/AbstractNodeVisitor.java.erb +22 -0
  100. data/vendor/prism/templates/java/org/prism/Loader.java.erb +434 -0
  101. data/vendor/prism/templates/java/org/prism/Nodes.java.erb +403 -0
  102. data/vendor/prism/templates/javascript/src/deserialize.js.erb +448 -0
  103. data/vendor/prism/templates/javascript/src/nodes.js.erb +197 -0
  104. data/vendor/prism/templates/javascript/src/visitor.js.erb +78 -0
  105. data/vendor/prism/templates/lib/prism/compiler.rb.erb +43 -0
  106. data/vendor/prism/templates/lib/prism/dispatcher.rb.erb +103 -0
  107. data/vendor/prism/templates/lib/prism/dot_visitor.rb.erb +189 -0
  108. data/vendor/prism/templates/lib/prism/dsl.rb.erb +133 -0
  109. data/vendor/prism/templates/lib/prism/inspect_visitor.rb.erb +131 -0
  110. data/vendor/prism/templates/lib/prism/mutation_compiler.rb.erb +19 -0
  111. data/vendor/prism/templates/lib/prism/node.rb.erb +515 -0
  112. data/vendor/prism/templates/lib/prism/reflection.rb.erb +136 -0
  113. data/vendor/prism/templates/lib/prism/serialize.rb.erb +602 -0
  114. data/vendor/prism/templates/lib/prism/visitor.rb.erb +55 -0
  115. data/vendor/prism/templates/rbi/prism/dsl.rbi.erb +68 -0
  116. data/vendor/prism/templates/rbi/prism/node.rbi.erb +164 -0
  117. data/vendor/prism/templates/rbi/prism/visitor.rbi.erb +18 -0
  118. data/vendor/prism/templates/sig/prism/_private/dot_visitor.rbs.erb +45 -0
  119. data/vendor/prism/templates/sig/prism/dsl.rbs.erb +31 -0
  120. data/vendor/prism/templates/sig/prism/mutation_compiler.rbs.erb +7 -0
  121. data/vendor/prism/templates/sig/prism/node.rbs.erb +132 -0
  122. data/vendor/prism/templates/sig/prism/visitor.rbs.erb +17 -0
  123. data/vendor/prism/templates/sig/prism.rbs.erb +89 -0
  124. data/vendor/prism/templates/src/diagnostic.c.erb +523 -0
  125. data/vendor/prism/templates/src/node.c.erb +333 -0
  126. data/vendor/prism/templates/src/prettyprint.c.erb +166 -0
  127. data/vendor/prism/templates/src/serialize.c.erb +406 -0
  128. data/vendor/prism/templates/src/token_type.c.erb +369 -0
  129. data/vendor/prism/templates/template.rb +689 -0
  130. metadata +112 -2
@@ -0,0 +1,49 @@
1
+ #include "prism/util/pm_list.h"
2
+
3
+ /**
4
+ * Returns true if the given list is empty.
5
+ */
6
+ PRISM_EXPORTED_FUNCTION bool
7
+ pm_list_empty_p(pm_list_t *list) {
8
+ return list->head == NULL;
9
+ }
10
+
11
+ /**
12
+ * Returns the size of the list.
13
+ */
14
+ PRISM_EXPORTED_FUNCTION size_t
15
+ pm_list_size(pm_list_t *list) {
16
+ return list->size;
17
+ }
18
+
19
+ /**
20
+ * Append a node to the given list.
21
+ */
22
+ void
23
+ pm_list_append(pm_list_t *list, pm_list_node_t *node) {
24
+ if (list->head == NULL) {
25
+ list->head = node;
26
+ } else {
27
+ list->tail->next = node;
28
+ }
29
+
30
+ list->tail = node;
31
+ list->size++;
32
+ }
33
+
34
+ /**
35
+ * Deallocate the internal state of the given list.
36
+ */
37
+ PRISM_EXPORTED_FUNCTION void
38
+ pm_list_free(pm_list_t *list) {
39
+ pm_list_node_t *node = list->head;
40
+ pm_list_node_t *next;
41
+
42
+ while (node != NULL) {
43
+ next = node->next;
44
+ xfree(node);
45
+ node = next;
46
+ }
47
+
48
+ list->size = 0;
49
+ }
@@ -0,0 +1,35 @@
1
+ #include "prism/util/pm_memchr.h"
2
+
3
+ #define PRISM_MEMCHR_TRAILING_BYTE_MINIMUM 0x40
4
+
5
+ /**
6
+ * We need to roll our own memchr to handle cases where the encoding changes and
7
+ * we need to search for a character in a buffer that could be the trailing byte
8
+ * of a multibyte character.
9
+ */
10
+ void *
11
+ pm_memchr(const void *memory, int character, size_t number, bool encoding_changed, const pm_encoding_t *encoding) {
12
+ if (encoding_changed && encoding->multibyte && character >= PRISM_MEMCHR_TRAILING_BYTE_MINIMUM) {
13
+ const uint8_t *source = (const uint8_t *) memory;
14
+ size_t index = 0;
15
+
16
+ while (index < number) {
17
+ if (source[index] == character) {
18
+ return (void *) (source + index);
19
+ }
20
+
21
+ size_t width = encoding->char_width(source + index, (ptrdiff_t) (number - index));
22
+ if (width == 0) {
23
+ return NULL;
24
+ }
25
+
26
+ index += width;
27
+ }
28
+
29
+ return NULL;
30
+ } else {
31
+ return memchr(memory, character, number);
32
+ }
33
+ }
34
+
35
+ #undef PRISM_MEMCHR_TRAILING_BYTE_MINIMUM
@@ -0,0 +1,125 @@
1
+ #include "prism/util/pm_newline_list.h"
2
+
3
+ /**
4
+ * Initialize a new newline list with the given capacity. Returns true if the
5
+ * allocation of the offsets succeeds, otherwise returns false.
6
+ */
7
+ bool
8
+ pm_newline_list_init(pm_newline_list_t *list, const uint8_t *start, size_t capacity) {
9
+ list->offsets = (size_t *) xcalloc(capacity, sizeof(size_t));
10
+ if (list->offsets == NULL) return false;
11
+
12
+ list->start = start;
13
+
14
+ // This is 1 instead of 0 because we want to include the first line of the
15
+ // file as having offset 0, which is set because of calloc.
16
+ list->size = 1;
17
+ list->capacity = capacity;
18
+
19
+ return true;
20
+ }
21
+
22
+ /**
23
+ * Clear out the newlines that have been appended to the list.
24
+ */
25
+ void
26
+ pm_newline_list_clear(pm_newline_list_t *list) {
27
+ list->size = 1;
28
+ }
29
+
30
+ /**
31
+ * Append a new offset to the newline list. Returns true if the reallocation of
32
+ * the offsets succeeds (if one was necessary), otherwise returns false.
33
+ */
34
+ bool
35
+ pm_newline_list_append(pm_newline_list_t *list, const uint8_t *cursor) {
36
+ if (list->size == list->capacity) {
37
+ size_t *original_offsets = list->offsets;
38
+
39
+ list->capacity = (list->capacity * 3) / 2;
40
+ list->offsets = (size_t *) xcalloc(list->capacity, sizeof(size_t));
41
+ if (list->offsets == NULL) return false;
42
+
43
+ memcpy(list->offsets, original_offsets, list->size * sizeof(size_t));
44
+ xfree(original_offsets);
45
+ }
46
+
47
+ assert(*cursor == '\n');
48
+ assert(cursor >= list->start);
49
+ size_t newline_offset = (size_t) (cursor - list->start + 1);
50
+
51
+ assert(list->size == 0 || newline_offset > list->offsets[list->size - 1]);
52
+ list->offsets[list->size++] = newline_offset;
53
+
54
+ return true;
55
+ }
56
+
57
+ /**
58
+ * Returns the line of the given offset. If the offset is not in the list, the
59
+ * line of the closest offset less than the given offset is returned.
60
+ */
61
+ int32_t
62
+ pm_newline_list_line(const pm_newline_list_t *list, const uint8_t *cursor, int32_t start_line) {
63
+ assert(cursor >= list->start);
64
+ size_t offset = (size_t) (cursor - list->start);
65
+
66
+ size_t left = 0;
67
+ size_t right = list->size - 1;
68
+
69
+ while (left <= right) {
70
+ size_t mid = left + (right - left) / 2;
71
+
72
+ if (list->offsets[mid] == offset) {
73
+ return ((int32_t) mid) + start_line;
74
+ }
75
+
76
+ if (list->offsets[mid] < offset) {
77
+ left = mid + 1;
78
+ } else {
79
+ right = mid - 1;
80
+ }
81
+ }
82
+
83
+ return ((int32_t) left) + start_line - 1;
84
+ }
85
+
86
+ /**
87
+ * Returns the line and column of the given offset. If the offset is not in the
88
+ * list, the line and column of the closest offset less than the given offset
89
+ * are returned.
90
+ */
91
+ pm_line_column_t
92
+ pm_newline_list_line_column(const pm_newline_list_t *list, const uint8_t *cursor, int32_t start_line) {
93
+ assert(cursor >= list->start);
94
+ size_t offset = (size_t) (cursor - list->start);
95
+
96
+ size_t left = 0;
97
+ size_t right = list->size - 1;
98
+
99
+ while (left <= right) {
100
+ size_t mid = left + (right - left) / 2;
101
+
102
+ if (list->offsets[mid] == offset) {
103
+ return ((pm_line_column_t) { ((int32_t) mid) + start_line, 0 });
104
+ }
105
+
106
+ if (list->offsets[mid] < offset) {
107
+ left = mid + 1;
108
+ } else {
109
+ right = mid - 1;
110
+ }
111
+ }
112
+
113
+ return ((pm_line_column_t) {
114
+ .line = ((int32_t) left) + start_line - 1,
115
+ .column = (uint32_t) (offset - list->offsets[left - 1])
116
+ });
117
+ }
118
+
119
+ /**
120
+ * Free the internal memory allocated for the newline list.
121
+ */
122
+ void
123
+ pm_newline_list_free(pm_newline_list_t *list) {
124
+ xfree(list->offsets);
125
+ }
@@ -0,0 +1,383 @@
1
+ #include "prism/util/pm_string.h"
2
+
3
+ /**
4
+ * Returns the size of the pm_string_t struct. This is necessary to allocate the
5
+ * correct amount of memory in the FFI backend.
6
+ */
7
+ PRISM_EXPORTED_FUNCTION size_t
8
+ pm_string_sizeof(void) {
9
+ return sizeof(pm_string_t);
10
+ }
11
+
12
+ /**
13
+ * Initialize a shared string that is based on initial input.
14
+ */
15
+ void
16
+ pm_string_shared_init(pm_string_t *string, const uint8_t *start, const uint8_t *end) {
17
+ assert(start <= end);
18
+
19
+ *string = (pm_string_t) {
20
+ .type = PM_STRING_SHARED,
21
+ .source = start,
22
+ .length = (size_t) (end - start)
23
+ };
24
+ }
25
+
26
+ /**
27
+ * Initialize an owned string that is responsible for freeing allocated memory.
28
+ */
29
+ void
30
+ pm_string_owned_init(pm_string_t *string, uint8_t *source, size_t length) {
31
+ *string = (pm_string_t) {
32
+ .type = PM_STRING_OWNED,
33
+ .source = source,
34
+ .length = length
35
+ };
36
+ }
37
+
38
+ /**
39
+ * Initialize a constant string that doesn't own its memory source.
40
+ */
41
+ void
42
+ pm_string_constant_init(pm_string_t *string, const char *source, size_t length) {
43
+ *string = (pm_string_t) {
44
+ .type = PM_STRING_CONSTANT,
45
+ .source = (const uint8_t *) source,
46
+ .length = length
47
+ };
48
+ }
49
+
50
+ #ifdef _WIN32
51
+ /**
52
+ * Represents a file handle on Windows, where the path will need to be freed
53
+ * when the file is closed.
54
+ */
55
+ typedef struct {
56
+ /** The path to the file, which will become allocated memory. */
57
+ WCHAR *path;
58
+
59
+ /** The handle to the file, which will start as uninitialized memory. */
60
+ HANDLE file;
61
+ } pm_string_file_handle_t;
62
+
63
+ /**
64
+ * Open the file indicated by the filepath parameter for reading on Windows.
65
+ * Perform any kind of normalization that needs to happen on the filepath.
66
+ */
67
+ static pm_string_init_result_t
68
+ pm_string_file_handle_open(pm_string_file_handle_t *handle, const char *filepath) {
69
+ int length = MultiByteToWideChar(CP_UTF8, 0, filepath, -1, NULL, 0);
70
+ if (length == 0) return PM_STRING_INIT_ERROR_GENERIC;
71
+
72
+ handle->path = xmalloc(sizeof(WCHAR) * ((size_t) length));
73
+ if ((handle->path == NULL) || (MultiByteToWideChar(CP_UTF8, 0, filepath, -1, handle->path, length) == 0)) {
74
+ xfree(handle->path);
75
+ return PM_STRING_INIT_ERROR_GENERIC;
76
+ }
77
+
78
+ handle->file = CreateFileW(handle->path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
79
+ if (handle->file == INVALID_HANDLE_VALUE) {
80
+ pm_string_init_result_t result = PM_STRING_INIT_ERROR_GENERIC;
81
+
82
+ if (GetLastError() == ERROR_ACCESS_DENIED) {
83
+ DWORD attributes = GetFileAttributesW(handle->path);
84
+ if ((attributes != INVALID_FILE_ATTRIBUTES) && (attributes & FILE_ATTRIBUTE_DIRECTORY)) {
85
+ result = PM_STRING_INIT_ERROR_DIRECTORY;
86
+ }
87
+ }
88
+
89
+ xfree(handle->path);
90
+ return result;
91
+ }
92
+
93
+ return PM_STRING_INIT_SUCCESS;
94
+ }
95
+
96
+ /**
97
+ * Close the file handle and free the path.
98
+ */
99
+ static void
100
+ pm_string_file_handle_close(pm_string_file_handle_t *handle) {
101
+ xfree(handle->path);
102
+ CloseHandle(handle->file);
103
+ }
104
+ #endif
105
+
106
+ /**
107
+ * Read the file indicated by the filepath parameter into source and load its
108
+ * contents and size into the given `pm_string_t`. The given `pm_string_t`
109
+ * should be freed using `pm_string_free` when it is no longer used.
110
+ *
111
+ * We want to use demand paging as much as possible in order to avoid having to
112
+ * read the entire file into memory (which could be detrimental to performance
113
+ * for large files). This means that if we're on windows we'll use
114
+ * `MapViewOfFile`, on POSIX systems that have access to `mmap` we'll use
115
+ * `mmap`, and on other POSIX systems we'll use `read`.
116
+ */
117
+ PRISM_EXPORTED_FUNCTION pm_string_init_result_t
118
+ pm_string_mapped_init(pm_string_t *string, const char *filepath) {
119
+ #ifdef _WIN32
120
+ // Open the file for reading.
121
+ pm_string_file_handle_t handle;
122
+ pm_string_init_result_t result = pm_string_file_handle_open(&handle, filepath);
123
+ if (result != PM_STRING_INIT_SUCCESS) return result;
124
+
125
+ // Get the file size.
126
+ DWORD file_size = GetFileSize(handle.file, NULL);
127
+ if (file_size == INVALID_FILE_SIZE) {
128
+ pm_string_file_handle_close(&handle);
129
+ return PM_STRING_INIT_ERROR_GENERIC;
130
+ }
131
+
132
+ // If the file is empty, then we don't need to do anything else, we'll set
133
+ // the source to a constant empty string and return.
134
+ if (file_size == 0) {
135
+ pm_string_file_handle_close(&handle);
136
+ const uint8_t source[] = "";
137
+ *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 };
138
+ return PM_STRING_INIT_SUCCESS;
139
+ }
140
+
141
+ // Create a mapping of the file.
142
+ HANDLE mapping = CreateFileMapping(handle.file, NULL, PAGE_READONLY, 0, 0, NULL);
143
+ if (mapping == NULL) {
144
+ pm_string_file_handle_close(&handle);
145
+ return PM_STRING_INIT_ERROR_GENERIC;
146
+ }
147
+
148
+ // Map the file into memory.
149
+ uint8_t *source = (uint8_t *) MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
150
+ CloseHandle(mapping);
151
+ pm_string_file_handle_close(&handle);
152
+
153
+ if (source == NULL) {
154
+ return PM_STRING_INIT_ERROR_GENERIC;
155
+ }
156
+
157
+ *string = (pm_string_t) { .type = PM_STRING_MAPPED, .source = source, .length = (size_t) file_size };
158
+ return PM_STRING_INIT_SUCCESS;
159
+ #elif defined(_POSIX_MAPPED_FILES)
160
+ // Open the file for reading
161
+ int fd = open(filepath, O_RDONLY);
162
+ if (fd == -1) {
163
+ return PM_STRING_INIT_ERROR_GENERIC;
164
+ }
165
+
166
+ // Stat the file to get the file size
167
+ struct stat sb;
168
+ if (fstat(fd, &sb) == -1) {
169
+ close(fd);
170
+ return PM_STRING_INIT_ERROR_GENERIC;
171
+ }
172
+
173
+ // Ensure it is a file and not a directory
174
+ if (S_ISDIR(sb.st_mode)) {
175
+ close(fd);
176
+ return PM_STRING_INIT_ERROR_DIRECTORY;
177
+ }
178
+
179
+ // mmap the file descriptor to virtually get the contents
180
+ size_t size = (size_t) sb.st_size;
181
+ uint8_t *source = NULL;
182
+
183
+ if (size == 0) {
184
+ close(fd);
185
+ const uint8_t source[] = "";
186
+ *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 };
187
+ return PM_STRING_INIT_SUCCESS;
188
+ }
189
+
190
+ source = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
191
+ if (source == MAP_FAILED) {
192
+ close(fd);
193
+ return PM_STRING_INIT_ERROR_GENERIC;
194
+ }
195
+
196
+ close(fd);
197
+ *string = (pm_string_t) { .type = PM_STRING_MAPPED, .source = source, .length = size };
198
+ return PM_STRING_INIT_SUCCESS;
199
+ #else
200
+ return pm_string_file_init(string, filepath);
201
+ #endif
202
+ }
203
+
204
+ /**
205
+ * Read the file indicated by the filepath parameter into source and load its
206
+ * contents and size into the given `pm_string_t`. The given `pm_string_t`
207
+ * should be freed using `pm_string_free` when it is no longer used.
208
+ */
209
+ PRISM_EXPORTED_FUNCTION pm_string_init_result_t
210
+ pm_string_file_init(pm_string_t *string, const char *filepath) {
211
+ #ifdef _WIN32
212
+ // Open the file for reading.
213
+ pm_string_file_handle_t handle;
214
+ pm_string_init_result_t result = pm_string_file_handle_open(&handle, filepath);
215
+ if (result != PM_STRING_INIT_SUCCESS) return result;
216
+
217
+ // Get the file size.
218
+ DWORD file_size = GetFileSize(handle.file, NULL);
219
+ if (file_size == INVALID_FILE_SIZE) {
220
+ pm_string_file_handle_close(&handle);
221
+ return PM_STRING_INIT_ERROR_GENERIC;
222
+ }
223
+
224
+ // If the file is empty, then we don't need to do anything else, we'll set
225
+ // the source to a constant empty string and return.
226
+ if (file_size == 0) {
227
+ pm_string_file_handle_close(&handle);
228
+ const uint8_t source[] = "";
229
+ *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 };
230
+ return PM_STRING_INIT_SUCCESS;
231
+ }
232
+
233
+ // Create a buffer to read the file into.
234
+ uint8_t *source = xmalloc(file_size);
235
+ if (source == NULL) {
236
+ pm_string_file_handle_close(&handle);
237
+ return PM_STRING_INIT_ERROR_GENERIC;
238
+ }
239
+
240
+ // Read the contents of the file
241
+ DWORD bytes_read;
242
+ if (!ReadFile(handle.file, source, file_size, &bytes_read, NULL)) {
243
+ pm_string_file_handle_close(&handle);
244
+ return PM_STRING_INIT_ERROR_GENERIC;
245
+ }
246
+
247
+ // Check the number of bytes read
248
+ if (bytes_read != file_size) {
249
+ xfree(source);
250
+ pm_string_file_handle_close(&handle);
251
+ return PM_STRING_INIT_ERROR_GENERIC;
252
+ }
253
+
254
+ pm_string_file_handle_close(&handle);
255
+ *string = (pm_string_t) { .type = PM_STRING_OWNED, .source = source, .length = (size_t) file_size };
256
+ return PM_STRING_INIT_SUCCESS;
257
+ #elif defined(PRISM_HAS_FILESYSTEM)
258
+ // Open the file for reading
259
+ int fd = open(filepath, O_RDONLY);
260
+ if (fd == -1) {
261
+ return PM_STRING_INIT_ERROR_GENERIC;
262
+ }
263
+
264
+ // Stat the file to get the file size
265
+ struct stat sb;
266
+ if (fstat(fd, &sb) == -1) {
267
+ close(fd);
268
+ return PM_STRING_INIT_ERROR_GENERIC;
269
+ }
270
+
271
+ // Ensure it is a file and not a directory
272
+ if (S_ISDIR(sb.st_mode)) {
273
+ close(fd);
274
+ return PM_STRING_INIT_ERROR_DIRECTORY;
275
+ }
276
+
277
+ // Check the size to see if it's empty
278
+ size_t size = (size_t) sb.st_size;
279
+ if (size == 0) {
280
+ close(fd);
281
+ const uint8_t source[] = "";
282
+ *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 };
283
+ return PM_STRING_INIT_SUCCESS;
284
+ }
285
+
286
+ size_t length = (size_t) size;
287
+ uint8_t *source = xmalloc(length);
288
+ if (source == NULL) {
289
+ close(fd);
290
+ return PM_STRING_INIT_ERROR_GENERIC;
291
+ }
292
+
293
+ long bytes_read = (long) read(fd, source, length);
294
+ close(fd);
295
+
296
+ if (bytes_read == -1) {
297
+ xfree(source);
298
+ return PM_STRING_INIT_ERROR_GENERIC;
299
+ }
300
+
301
+ *string = (pm_string_t) { .type = PM_STRING_OWNED, .source = source, .length = length };
302
+ return PM_STRING_INIT_SUCCESS;
303
+ #else
304
+ (void) string;
305
+ (void) filepath;
306
+ perror("pm_string_file_init is not implemented for this platform");
307
+ return PM_STRING_INIT_ERROR_GENERIC;
308
+ #endif
309
+ }
310
+
311
+ /**
312
+ * Ensure the string is owned. If it is not, then reinitialize it as owned and
313
+ * copy over the previous source.
314
+ */
315
+ void
316
+ pm_string_ensure_owned(pm_string_t *string) {
317
+ if (string->type == PM_STRING_OWNED) return;
318
+
319
+ size_t length = pm_string_length(string);
320
+ const uint8_t *source = pm_string_source(string);
321
+
322
+ uint8_t *memory = xmalloc(length);
323
+ if (!memory) return;
324
+
325
+ pm_string_owned_init(string, memory, length);
326
+ memcpy((void *) string->source, source, length);
327
+ }
328
+
329
+ /**
330
+ * Compare the underlying lengths and bytes of two strings. Returns 0 if the
331
+ * strings are equal, a negative number if the left string is less than the
332
+ * right string, and a positive number if the left string is greater than the
333
+ * right string.
334
+ */
335
+ int
336
+ pm_string_compare(const pm_string_t *left, const pm_string_t *right) {
337
+ size_t left_length = pm_string_length(left);
338
+ size_t right_length = pm_string_length(right);
339
+
340
+ if (left_length < right_length) {
341
+ return -1;
342
+ } else if (left_length > right_length) {
343
+ return 1;
344
+ }
345
+
346
+ return memcmp(pm_string_source(left), pm_string_source(right), left_length);
347
+ }
348
+
349
+ /**
350
+ * Returns the length associated with the string.
351
+ */
352
+ PRISM_EXPORTED_FUNCTION size_t
353
+ pm_string_length(const pm_string_t *string) {
354
+ return string->length;
355
+ }
356
+
357
+ /**
358
+ * Returns the start pointer associated with the string.
359
+ */
360
+ PRISM_EXPORTED_FUNCTION const uint8_t *
361
+ pm_string_source(const pm_string_t *string) {
362
+ return string->source;
363
+ }
364
+
365
+ /**
366
+ * Free the associated memory of the given string.
367
+ */
368
+ PRISM_EXPORTED_FUNCTION void
369
+ pm_string_free(pm_string_t *string) {
370
+ void *memory = (void *) string->source;
371
+
372
+ if (string->type == PM_STRING_OWNED) {
373
+ xfree(memory);
374
+ #ifdef PRISM_HAS_MMAP
375
+ } else if (string->type == PM_STRING_MAPPED && string->length) {
376
+ #if defined(_WIN32)
377
+ UnmapViewOfFile(memory);
378
+ #elif defined(_POSIX_MAPPED_FILES)
379
+ munmap(memory, string->length);
380
+ #endif
381
+ #endif /* PRISM_HAS_MMAP */
382
+ }
383
+ }
@@ -0,0 +1,36 @@
1
+ #include "prism/util/pm_strncasecmp.h"
2
+
3
+ /**
4
+ * A locale-insensitive version of `tolower(3)`
5
+ */
6
+ static inline int
7
+ pm_tolower(int c)
8
+ {
9
+ if ('A' <= c && c <= 'Z') {
10
+ return c | 0x20;
11
+ }
12
+ return c;
13
+ }
14
+
15
+ /**
16
+ * Compare two strings, ignoring case, up to the given length. Returns 0 if the
17
+ * strings are equal, a negative number if string1 is less than string2, or a
18
+ * positive number if string1 is greater than string2.
19
+ *
20
+ * Note that this is effectively our own implementation of strncasecmp, but it's
21
+ * not available on all of the platforms we want to support so we're rolling it
22
+ * here.
23
+ */
24
+ int
25
+ pm_strncasecmp(const uint8_t *string1, const uint8_t *string2, size_t length) {
26
+ size_t offset = 0;
27
+ int difference = 0;
28
+
29
+ while (offset < length && string1[offset] != '\0') {
30
+ if (string2[offset] == '\0') return string1[offset];
31
+ if ((difference = pm_tolower(string1[offset]) - pm_tolower(string2[offset])) != 0) return difference;
32
+ offset++;
33
+ }
34
+
35
+ return difference;
36
+ }