herb 0.8.10-arm-linux-gnu → 0.9.1-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 (212) hide show
  1. checksums.yaml +4 -4
  2. data/Makefile +11 -3
  3. data/README.md +64 -34
  4. data/Rakefile +48 -40
  5. data/config.yml +473 -34
  6. data/ext/herb/error_helpers.c +535 -140
  7. data/ext/herb/error_helpers.h +1 -0
  8. data/ext/herb/extconf.rb +67 -28
  9. data/ext/herb/extension.c +321 -51
  10. data/ext/herb/extension.h +1 -0
  11. data/ext/herb/extension_helpers.c +24 -14
  12. data/ext/herb/extension_helpers.h +2 -2
  13. data/ext/herb/nodes.c +647 -270
  14. data/ext/herb/nodes.h +1 -0
  15. data/herb.gemspec +3 -2
  16. data/lib/herb/3.0/herb.so +0 -0
  17. data/lib/herb/3.1/herb.so +0 -0
  18. data/lib/herb/3.2/herb.so +0 -0
  19. data/lib/herb/3.3/herb.so +0 -0
  20. data/lib/herb/3.4/herb.so +0 -0
  21. data/lib/herb/4.0/herb.so +0 -0
  22. data/lib/herb/ast/helpers.rb +3 -3
  23. data/lib/herb/ast/node.rb +15 -2
  24. data/lib/herb/ast/nodes.rb +1530 -179
  25. data/lib/herb/bootstrap.rb +87 -0
  26. data/lib/herb/cli.rb +341 -31
  27. data/lib/herb/configuration.rb +248 -0
  28. data/lib/herb/defaults.yml +32 -0
  29. data/lib/herb/engine/compiler.rb +78 -11
  30. data/lib/herb/engine/debug_visitor.rb +13 -3
  31. data/lib/herb/engine/error_formatter.rb +13 -9
  32. data/lib/herb/engine/parser_error_overlay.rb +10 -6
  33. data/lib/herb/engine/validator.rb +8 -3
  34. data/lib/herb/engine/validators/nesting_validator.rb +2 -2
  35. data/lib/herb/engine.rb +119 -43
  36. data/lib/herb/errors.rb +808 -88
  37. data/lib/herb/lex_result.rb +1 -0
  38. data/lib/herb/location.rb +7 -3
  39. data/lib/herb/parse_result.rb +12 -2
  40. data/lib/herb/parser_options.rb +62 -0
  41. data/lib/herb/position.rb +1 -0
  42. data/lib/herb/prism_inspect.rb +120 -0
  43. data/lib/herb/project.rb +923 -331
  44. data/lib/herb/range.rb +1 -0
  45. data/lib/herb/token.rb +7 -1
  46. data/lib/herb/version.rb +1 -1
  47. data/lib/herb/visitor.rb +47 -2
  48. data/lib/herb/warnings.rb +6 -1
  49. data/lib/herb.rb +35 -3
  50. data/sig/herb/ast/helpers.rbs +2 -2
  51. data/sig/herb/ast/node.rbs +12 -2
  52. data/sig/herb/ast/nodes.rbs +773 -128
  53. data/sig/herb/bootstrap.rbs +31 -0
  54. data/sig/herb/configuration.rbs +89 -0
  55. data/sig/herb/engine/compiler.rbs +9 -1
  56. data/sig/herb/engine/debug_visitor.rbs +2 -0
  57. data/sig/herb/engine/validator.rbs +5 -1
  58. data/sig/herb/engine.rbs +21 -3
  59. data/sig/herb/errors.rbs +372 -63
  60. data/sig/herb/location.rbs +4 -0
  61. data/sig/herb/parse_result.rbs +4 -2
  62. data/sig/herb/parser_options.rbs +46 -0
  63. data/sig/herb/position.rbs +1 -0
  64. data/sig/herb/prism_inspect.rbs +28 -0
  65. data/sig/herb/range.rbs +1 -0
  66. data/sig/herb/token.rbs +6 -0
  67. data/sig/herb/visitor.rbs +31 -4
  68. data/sig/herb/warnings.rbs +6 -1
  69. data/sig/herb.rbs +14 -0
  70. data/sig/herb_c_extension.rbs +5 -2
  71. data/sig/rubyvm.rbs +5 -0
  72. data/sig/serialized_ast_errors.rbs +82 -6
  73. data/sig/serialized_ast_nodes.rbs +91 -6
  74. data/src/analyze/action_view/attribute_extraction_helpers.c +303 -0
  75. data/src/analyze/action_view/content_tag.c +78 -0
  76. data/src/analyze/action_view/link_to.c +167 -0
  77. data/src/analyze/action_view/registry.c +83 -0
  78. data/src/analyze/action_view/tag.c +70 -0
  79. data/src/analyze/action_view/tag_helper_node_builders.c +305 -0
  80. data/src/analyze/action_view/tag_helpers.c +815 -0
  81. data/src/analyze/action_view/turbo_frame_tag.c +88 -0
  82. data/src/analyze/analyze.c +885 -0
  83. data/src/{analyzed_ruby.c → analyze/analyzed_ruby.c} +13 -11
  84. data/src/analyze/builders.c +343 -0
  85. data/src/analyze/conditional_elements.c +594 -0
  86. data/src/analyze/conditional_open_tags.c +640 -0
  87. data/src/analyze/control_type.c +250 -0
  88. data/src/{analyze_helpers.c → analyze/helpers.c} +48 -23
  89. data/src/analyze/invalid_structures.c +193 -0
  90. data/src/{analyze_missing_end.c → analyze/missing_end.c} +33 -22
  91. data/src/analyze/parse_errors.c +84 -0
  92. data/src/analyze/prism_annotate.c +399 -0
  93. data/src/analyze/render_nodes.c +761 -0
  94. data/src/{analyze_transform.c → analyze/transform.c} +24 -3
  95. data/src/ast_node.c +17 -7
  96. data/src/ast_nodes.c +759 -387
  97. data/src/ast_pretty_print.c +264 -6
  98. data/src/errors.c +1454 -519
  99. data/src/extract.c +145 -49
  100. data/src/herb.c +52 -34
  101. data/src/html_util.c +241 -12
  102. data/src/include/analyze/action_view/attribute_extraction_helpers.h +36 -0
  103. data/src/include/analyze/action_view/tag_helper_handler.h +43 -0
  104. data/src/include/analyze/action_view/tag_helper_node_builders.h +70 -0
  105. data/src/include/analyze/action_view/tag_helpers.h +38 -0
  106. data/src/include/{analyze.h → analyze/analyze.h} +14 -4
  107. data/src/include/{analyzed_ruby.h → analyze/analyzed_ruby.h} +3 -3
  108. data/src/include/analyze/builders.h +27 -0
  109. data/src/include/analyze/conditional_elements.h +9 -0
  110. data/src/include/analyze/conditional_open_tags.h +9 -0
  111. data/src/include/analyze/control_type.h +14 -0
  112. data/src/include/{analyze_helpers.h → analyze/helpers.h} +4 -2
  113. data/src/include/analyze/invalid_structures.h +11 -0
  114. data/src/include/analyze/prism_annotate.h +16 -0
  115. data/src/include/analyze/render_nodes.h +11 -0
  116. data/src/include/ast_node.h +11 -5
  117. data/src/include/ast_nodes.h +154 -38
  118. data/src/include/ast_pretty_print.h +5 -0
  119. data/src/include/element_source.h +3 -8
  120. data/src/include/errors.h +206 -55
  121. data/src/include/extract.h +21 -5
  122. data/src/include/herb.h +18 -6
  123. data/src/include/herb_prism_node.h +13 -0
  124. data/src/include/html_util.h +7 -2
  125. data/src/include/io.h +3 -1
  126. data/src/include/lex_helpers.h +29 -0
  127. data/src/include/lexer.h +1 -1
  128. data/src/include/lexer_peek_helpers.h +87 -13
  129. data/src/include/lexer_struct.h +2 -0
  130. data/src/include/location.h +2 -1
  131. data/src/include/parser.h +28 -2
  132. data/src/include/parser_helpers.h +19 -3
  133. data/src/include/pretty_print.h +10 -5
  134. data/src/include/prism_context.h +45 -0
  135. data/src/include/prism_helpers.h +10 -7
  136. data/src/include/prism_serialized.h +12 -0
  137. data/src/include/token.h +16 -4
  138. data/src/include/token_struct.h +10 -3
  139. data/src/include/utf8.h +2 -1
  140. data/src/include/util/hb_allocator.h +78 -0
  141. data/src/include/util/hb_arena.h +6 -1
  142. data/src/include/util/hb_arena_debug.h +12 -1
  143. data/src/include/util/hb_array.h +7 -3
  144. data/src/include/util/hb_buffer.h +6 -4
  145. data/src/include/util/hb_foreach.h +79 -0
  146. data/src/include/util/hb_narray.h +8 -4
  147. data/src/include/util/hb_string.h +56 -9
  148. data/src/include/util.h +6 -3
  149. data/src/include/version.h +1 -1
  150. data/src/io.c +3 -2
  151. data/src/lexer.c +42 -30
  152. data/src/lexer_peek_helpers.c +12 -74
  153. data/src/location.c +2 -2
  154. data/src/main.c +53 -28
  155. data/src/parser.c +784 -247
  156. data/src/parser_helpers.c +110 -23
  157. data/src/parser_match_tags.c +129 -48
  158. data/src/pretty_print.c +29 -24
  159. data/src/prism_helpers.c +30 -27
  160. data/src/ruby_parser.c +2 -0
  161. data/src/token.c +151 -66
  162. data/src/token_matchers.c +0 -1
  163. data/src/utf8.c +7 -6
  164. data/src/util/hb_allocator.c +341 -0
  165. data/src/util/hb_arena.c +81 -56
  166. data/src/util/hb_arena_debug.c +32 -17
  167. data/src/util/hb_array.c +30 -15
  168. data/src/util/hb_buffer.c +17 -21
  169. data/src/util/hb_narray.c +22 -7
  170. data/src/util/hb_string.c +49 -35
  171. data/src/util.c +21 -11
  172. data/src/visitor.c +67 -0
  173. data/templates/ext/herb/error_helpers.c.erb +24 -11
  174. data/templates/ext/herb/error_helpers.h.erb +1 -0
  175. data/templates/ext/herb/nodes.c.erb +50 -16
  176. data/templates/ext/herb/nodes.h.erb +1 -0
  177. data/templates/java/error_helpers.c.erb +1 -1
  178. data/templates/java/nodes.c.erb +30 -8
  179. data/templates/java/org/herb/ast/Errors.java.erb +24 -1
  180. data/templates/java/org/herb/ast/Nodes.java.erb +80 -21
  181. data/templates/javascript/packages/core/src/errors.ts.erb +16 -3
  182. data/templates/javascript/packages/core/src/node-type-guards.ts.erb +3 -1
  183. data/templates/javascript/packages/core/src/nodes.ts.erb +109 -32
  184. data/templates/javascript/packages/node/extension/error_helpers.cpp.erb +13 -4
  185. data/templates/javascript/packages/node/extension/nodes.cpp.erb +43 -4
  186. data/templates/lib/herb/ast/nodes.rb.erb +95 -32
  187. data/templates/lib/herb/errors.rb.erb +15 -3
  188. data/templates/lib/herb/visitor.rb.erb +2 -2
  189. data/templates/rust/src/ast/nodes.rs.erb +97 -44
  190. data/templates/rust/src/errors.rs.erb +2 -1
  191. data/templates/rust/src/nodes.rs.erb +168 -16
  192. data/templates/rust/src/union_types.rs.erb +60 -0
  193. data/templates/rust/src/visitor.rs.erb +81 -0
  194. data/templates/src/{analyze_missing_end.c.erb → analyze/missing_end.c.erb} +9 -6
  195. data/templates/src/{analyze_transform.c.erb → analyze/transform.c.erb} +2 -2
  196. data/templates/src/ast_nodes.c.erb +34 -26
  197. data/templates/src/ast_pretty_print.c.erb +24 -5
  198. data/templates/src/errors.c.erb +60 -54
  199. data/templates/src/include/ast_nodes.h.erb +6 -2
  200. data/templates/src/include/ast_pretty_print.h.erb +5 -0
  201. data/templates/src/include/errors.h.erb +15 -11
  202. data/templates/src/include/util/hb_foreach.h.erb +20 -0
  203. data/templates/src/parser_match_tags.c.erb +10 -4
  204. data/templates/src/visitor.c.erb +2 -2
  205. data/templates/template.rb +204 -29
  206. data/templates/wasm/error_helpers.cpp.erb +9 -5
  207. data/templates/wasm/nodes.cpp.erb +41 -4
  208. metadata +60 -16
  209. data/src/analyze.c +0 -1608
  210. data/src/element_source.c +0 -12
  211. data/src/include/util/hb_system.h +0 -9
  212. data/src/util/hb_system.c +0 -30
@@ -0,0 +1,341 @@
1
+ #include "../include/util/hb_allocator.h"
2
+ #include "../include/util/hb_arena.h"
3
+
4
+ #include <stdio.h>
5
+ #include <stdlib.h>
6
+ #include <string.h>
7
+
8
+ // --- Malloc backend ---
9
+
10
+ static void* malloc_alloc(hb_allocator_T* _self, size_t size) {
11
+ return calloc(1, size);
12
+ }
13
+
14
+ static void malloc_dealloc(hb_allocator_T* _self, void* pointer) {
15
+ free(pointer);
16
+ }
17
+
18
+ static char* malloc_strdup(hb_allocator_T* _self, const char* string) {
19
+ if (!string) { return NULL; }
20
+
21
+ size_t length = strlen(string);
22
+ char* copy = malloc(length + 1);
23
+ if (!copy) { return NULL; }
24
+
25
+ memcpy(copy, string, length + 1);
26
+
27
+ return copy;
28
+ }
29
+
30
+ static char* malloc_strndup(hb_allocator_T* _self, const char* string, size_t length) {
31
+ if (!string) { return NULL; }
32
+
33
+ char* copy = malloc(length + 1);
34
+ if (!copy) { return NULL; }
35
+
36
+ memcpy(copy, string, length);
37
+ copy[length] = '\0';
38
+
39
+ return copy;
40
+ }
41
+
42
+ static void* malloc_realloc(hb_allocator_T* _self, void* pointer, size_t _old_size, size_t new_size) {
43
+ return realloc(pointer, new_size);
44
+ }
45
+
46
+ static void malloc_destroy(hb_allocator_T* _self) {
47
+ }
48
+
49
+ hb_allocator_T hb_allocator_with_malloc(void) {
50
+ return (hb_allocator_T) {
51
+ .alloc = malloc_alloc,
52
+ .realloc = malloc_realloc,
53
+ .dealloc = malloc_dealloc,
54
+ .strdup = malloc_strdup,
55
+ .strndup = malloc_strndup,
56
+ .destroy = malloc_destroy,
57
+ .context = NULL,
58
+ };
59
+ }
60
+
61
+ // --- Arena backend ---
62
+
63
+ static void* arena_alloc(hb_allocator_T* self, size_t size) {
64
+ return hb_arena_alloc((hb_arena_T*) self->context, size);
65
+ }
66
+
67
+ static void* arena_realloc(hb_allocator_T* self, void* pointer, size_t old_size, size_t new_size) {
68
+ if (!pointer) { return arena_alloc(self, new_size); }
69
+
70
+ void* new_pointer = hb_arena_alloc((hb_arena_T*) self->context, new_size);
71
+ if (!new_pointer) { return NULL; }
72
+
73
+ size_t copy_size = old_size < new_size ? old_size : new_size;
74
+ memcpy(new_pointer, pointer, copy_size);
75
+
76
+ return new_pointer;
77
+ }
78
+
79
+ static void arena_dealloc(hb_allocator_T* _self, void* _pointer) {
80
+ }
81
+
82
+ static char* arena_strdup(hb_allocator_T* self, const char* string) {
83
+ if (!string) { return NULL; }
84
+
85
+ size_t length = strlen(string);
86
+ char* copy = hb_arena_alloc((hb_arena_T*) self->context, length + 1);
87
+ if (!copy) { return NULL; }
88
+
89
+ memcpy(copy, string, length + 1);
90
+
91
+ return copy;
92
+ }
93
+
94
+ static char* arena_strndup(hb_allocator_T* self, const char* string, size_t length) {
95
+ if (!string) { return NULL; }
96
+
97
+ char* copy = hb_arena_alloc((hb_arena_T*) self->context, length + 1);
98
+ if (!copy) { return NULL; }
99
+
100
+ memcpy(copy, string, length);
101
+ copy[length] = '\0';
102
+
103
+ return copy;
104
+ }
105
+
106
+ static void arena_destroy(hb_allocator_T* self) {
107
+ hb_arena_T* arena = (hb_arena_T*) self->context;
108
+ hb_arena_free(arena);
109
+ free(arena);
110
+ self->context = NULL;
111
+ }
112
+
113
+ hb_allocator_T hb_allocator_with_arena(hb_arena_T* arena) {
114
+ return (hb_allocator_T) {
115
+ .alloc = arena_alloc,
116
+ .realloc = arena_realloc,
117
+ .dealloc = arena_dealloc,
118
+ .strdup = arena_strdup,
119
+ .strndup = arena_strndup,
120
+ .destroy = arena_destroy,
121
+ .context = arena,
122
+ };
123
+ }
124
+
125
+ // --- Tracking backend ---
126
+
127
+ #define TRACKING_INITIAL_CAPACITY 128
128
+ #define TRACKING_TOMBSTONE ((void*) 1)
129
+
130
+ static size_t tracking_probe(hb_allocator_tracking_stats_T* stats, void* pointer) {
131
+ size_t mask = stats->buckets_capacity - 1;
132
+ size_t index = ((size_t) pointer >> 4) & mask;
133
+
134
+ while (true) {
135
+ void* key = stats->buckets[index].pointer;
136
+ if (key == NULL || key == TRACKING_TOMBSTONE || key == pointer) { return index; }
137
+ index = (index + 1) & mask;
138
+ }
139
+ }
140
+
141
+ static void tracking_grow(hb_allocator_tracking_stats_T* stats) {
142
+ size_t old_capacity = stats->buckets_capacity;
143
+ hb_allocator_tracking_entry_T* old_buckets = stats->buckets;
144
+
145
+ size_t new_capacity = old_capacity ? old_capacity * 2 : TRACKING_INITIAL_CAPACITY;
146
+ stats->buckets = calloc(new_capacity, sizeof(hb_allocator_tracking_entry_T));
147
+ stats->buckets_capacity = new_capacity;
148
+ stats->buckets_used = 0;
149
+
150
+ for (size_t i = 0; i < old_capacity; i++) {
151
+ if (old_buckets[i].pointer != NULL && old_buckets[i].pointer != TRACKING_TOMBSTONE) {
152
+ size_t index = tracking_probe(stats, old_buckets[i].pointer);
153
+ stats->buckets[index] = old_buckets[i];
154
+ stats->buckets_used++;
155
+ }
156
+ }
157
+
158
+ free(old_buckets);
159
+ }
160
+
161
+ static void tracking_record(hb_allocator_tracking_stats_T* stats, void* pointer, size_t size) {
162
+ if (stats->buckets_used * 2 >= stats->buckets_capacity) { tracking_grow(stats); }
163
+
164
+ size_t index = tracking_probe(stats, pointer);
165
+ stats->buckets[index] = (hb_allocator_tracking_entry_T) { .pointer = pointer, .size = size };
166
+ stats->buckets_used++;
167
+ stats->allocation_count++;
168
+ stats->bytes_allocated += size;
169
+ }
170
+
171
+ static void tracking_record_untracked(hb_allocator_tracking_stats_T* stats, void* pointer) {
172
+ stats->untracked_deallocation_count++;
173
+
174
+ if (stats->untracked_pointers_size >= stats->untracked_pointers_capacity) {
175
+ size_t new_capacity = stats->untracked_pointers_capacity ? stats->untracked_pointers_capacity * 2 : 16;
176
+ void** new_pointers = realloc(stats->untracked_pointers, new_capacity * sizeof(void*));
177
+
178
+ if (!new_pointers) { return; }
179
+
180
+ stats->untracked_pointers = new_pointers;
181
+ stats->untracked_pointers_capacity = new_capacity;
182
+ }
183
+
184
+ stats->untracked_pointers[stats->untracked_pointers_size++] = pointer;
185
+ }
186
+
187
+ static void tracking_unrecord(hb_allocator_tracking_stats_T* stats, void* pointer) {
188
+ if (!stats->buckets_capacity) {
189
+ tracking_record_untracked(stats, pointer);
190
+
191
+ return;
192
+ }
193
+
194
+ size_t mask = stats->buckets_capacity - 1;
195
+ size_t index = ((size_t) pointer >> 4) & mask;
196
+
197
+ while (true) {
198
+ void* key = stats->buckets[index].pointer;
199
+
200
+ if (key == NULL) {
201
+ tracking_record_untracked(stats, pointer);
202
+
203
+ return;
204
+ }
205
+
206
+ if (key == pointer) {
207
+ stats->deallocation_count++;
208
+ stats->bytes_deallocated += stats->buckets[index].size;
209
+ stats->buckets[index].pointer = TRACKING_TOMBSTONE;
210
+ stats->buckets[index].size = 0;
211
+
212
+ return;
213
+ }
214
+
215
+ index = (index + 1) & mask;
216
+ }
217
+ }
218
+
219
+ static void* tracking_alloc(hb_allocator_T* self, size_t size) {
220
+ hb_allocator_tracking_stats_T* stats = (hb_allocator_tracking_stats_T*) self->context;
221
+ void* pointer = calloc(1, size);
222
+
223
+ if (pointer) { tracking_record(stats, pointer, size); }
224
+
225
+ return pointer;
226
+ }
227
+
228
+ static void* tracking_realloc(hb_allocator_T* self, void* pointer, size_t _old_size, size_t new_size) {
229
+ hb_allocator_tracking_stats_T* stats = (hb_allocator_tracking_stats_T*) self->context;
230
+
231
+ if (pointer) { tracking_unrecord(stats, pointer); }
232
+
233
+ void* new_pointer = realloc(pointer, new_size);
234
+
235
+ if (new_pointer) { tracking_record(stats, new_pointer, new_size); }
236
+
237
+ return new_pointer;
238
+ }
239
+
240
+ static void tracking_dealloc(hb_allocator_T* self, void* pointer) {
241
+ hb_allocator_tracking_stats_T* stats = (hb_allocator_tracking_stats_T*) self->context;
242
+ tracking_unrecord(stats, pointer);
243
+ free(pointer);
244
+ }
245
+
246
+ static char* tracking_strdup(hb_allocator_T* self, const char* string) {
247
+ if (!string) { return NULL; }
248
+
249
+ hb_allocator_tracking_stats_T* stats = (hb_allocator_tracking_stats_T*) self->context;
250
+ size_t length = strlen(string);
251
+ char* copy = malloc(length + 1);
252
+ if (!copy) { return NULL; }
253
+
254
+ memcpy(copy, string, length + 1);
255
+ tracking_record(stats, copy, length + 1);
256
+
257
+ return copy;
258
+ }
259
+
260
+ static char* tracking_strndup(hb_allocator_T* self, const char* string, size_t length) {
261
+ if (!string) { return NULL; }
262
+
263
+ hb_allocator_tracking_stats_T* stats = (hb_allocator_tracking_stats_T*) self->context;
264
+ char* copy = malloc(length + 1);
265
+ if (!copy) { return NULL; }
266
+
267
+ memcpy(copy, string, length);
268
+ copy[length] = '\0';
269
+ tracking_record(stats, copy, length + 1);
270
+
271
+ return copy;
272
+ }
273
+
274
+ static void tracking_destroy(hb_allocator_T* self) {
275
+ hb_allocator_tracking_stats_T* stats = (hb_allocator_tracking_stats_T*) self->context;
276
+
277
+ free(stats->buckets);
278
+ free(stats->untracked_pointers);
279
+ free(stats);
280
+
281
+ self->context = NULL;
282
+ }
283
+
284
+ hb_allocator_T hb_allocator_with_tracking(void) {
285
+ hb_allocator_tracking_stats_T* stats = calloc(1, sizeof(hb_allocator_tracking_stats_T));
286
+
287
+ return (hb_allocator_T) {
288
+ .alloc = tracking_alloc,
289
+ .realloc = tracking_realloc,
290
+ .dealloc = tracking_dealloc,
291
+ .strdup = tracking_strdup,
292
+ .strndup = tracking_strndup,
293
+ .destroy = tracking_destroy,
294
+ .context = stats,
295
+ };
296
+ }
297
+
298
+ hb_allocator_tracking_stats_T* hb_allocator_tracking_stats(hb_allocator_T* allocator) {
299
+ return (hb_allocator_tracking_stats_T*) allocator->context;
300
+ }
301
+
302
+ // --- High-level API ---
303
+
304
+ bool hb_allocator_init(hb_allocator_T* allocator, hb_allocator_type_T type) {
305
+ return hb_allocator_init_with_size(allocator, type, 0);
306
+ }
307
+
308
+ bool hb_allocator_init_with_size(hb_allocator_T* allocator, hb_allocator_type_T type, size_t initial_size) {
309
+ switch (type) {
310
+ case HB_ALLOCATOR_MALLOC: {
311
+ *allocator = hb_allocator_with_malloc();
312
+ return true;
313
+ }
314
+
315
+ case HB_ALLOCATOR_ARENA: {
316
+ if (initial_size == 0) { initial_size = HB_ALLOCATOR_DEFAULT_ARENA_SIZE; }
317
+
318
+ hb_arena_T* arena = malloc(sizeof(hb_arena_T));
319
+ if (!arena) { return false; }
320
+
321
+ if (!hb_arena_init(arena, initial_size)) {
322
+ free(arena);
323
+ return false;
324
+ }
325
+
326
+ *allocator = hb_allocator_with_arena(arena);
327
+ return true;
328
+ }
329
+
330
+ case HB_ALLOCATOR_TRACKING: {
331
+ *allocator = hb_allocator_with_tracking();
332
+ return true;
333
+ }
334
+
335
+ default: return false;
336
+ }
337
+ }
338
+
339
+ void hb_allocator_destroy(hb_allocator_T* allocator) {
340
+ allocator->destroy(allocator);
341
+ }
data/src/util/hb_arena.c CHANGED
@@ -1,14 +1,38 @@
1
1
  #include "../include/util/hb_arena.h"
2
2
  #include "../include/macros.h"
3
- #include "../include/util/hb_system.h"
4
3
 
5
4
  #include <assert.h>
6
5
  #include <stdbool.h>
7
6
  #include <stdint.h>
8
7
  #include <string.h>
9
8
 
10
- #define hb_arena_for_each_page(allocator, page) \
11
- for (hb_arena_page_T* page = (allocator)->head; page != NULL; page = page->next)
9
+ #ifdef HERB_USE_MALLOC
10
+ # include <stdlib.h>
11
+ #else
12
+ # ifdef __linux__
13
+ # define _GNU_SOURCE
14
+ # endif
15
+ # include <sys/mman.h>
16
+ #endif
17
+
18
+ static void* hb_arena_allocate_page(size_t size) {
19
+ #ifdef HERB_USE_MALLOC
20
+ return malloc(size);
21
+ #else
22
+ void* memory = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
23
+ if (memory == MAP_FAILED) { return NULL; }
24
+
25
+ return memory;
26
+ #endif
27
+ }
28
+
29
+ static void hb_arena_free_page(void* pointer, size_t size) {
30
+ #ifdef HERB_USE_MALLOC
31
+ free(pointer);
32
+ #else
33
+ munmap(pointer, size);
34
+ #endif
35
+ }
12
36
 
13
37
  static inline size_t hb_arena_align_size(size_t size, size_t alignment) {
14
38
  assert(size <= SIZE_MAX - (alignment - 1));
@@ -22,7 +46,7 @@ static inline bool hb_arena_page_has_capacity(hb_arena_page_T* page, size_t requ
22
46
  return page->position + required_size <= page->capacity;
23
47
  }
24
48
 
25
- static inline void* hb_arena_page_alloc_from(hb_arena_page_T* page, size_t size) {
49
+ static inline void* hb_arena_page_alloc(hb_arena_page_T* page, size_t size) {
26
50
  assert(size > 0);
27
51
  assert(page->position + size <= page->capacity);
28
52
 
@@ -32,28 +56,18 @@ static inline void* hb_arena_page_alloc_from(hb_arena_page_T* page, size_t size)
32
56
  return result;
33
57
  }
34
58
 
35
- static inline void hb_arena_page_reset(hb_arena_page_T* page) {
36
- page->position = 0;
37
- }
38
-
39
- static inline void hb_arena_reset_pages_after(hb_arena_page_T* start_page) {
40
- for (hb_arena_page_T* page = start_page; page != NULL; page = page->next) {
41
- hb_arena_page_reset(page);
42
- }
43
- }
44
-
45
- static bool hb_arena_append_page(hb_arena_T* allocator, size_t minimum_size) {
46
- assert(minimum_size > 0);
47
-
48
- size_t page_size = MAX(allocator->default_page_size, minimum_size);
59
+ static size_t hb_arena_page_free(hb_arena_page_T* starting_page);
49
60
 
61
+ static bool hb_arena_append_page(hb_arena_T* allocator, size_t page_size) {
50
62
  assert(page_size <= SIZE_MAX - sizeof(hb_arena_page_T));
51
- size_t total_size = page_size + sizeof(hb_arena_page_T);
63
+ size_t page_size_with_meta_data = page_size + sizeof(hb_arena_page_T);
52
64
 
53
- hb_arena_page_T* page = hb_system_allocate_memory(total_size);
65
+ hb_arena_page_T* page = hb_arena_allocate_page(page_size_with_meta_data);
54
66
  if (page == NULL) { return false; }
55
67
 
56
- *page = (hb_arena_page_T) { .next = NULL, .capacity = page_size, .position = 0 };
68
+ page->next = NULL;
69
+ page->capacity = page_size;
70
+ page->position = 0;
57
71
 
58
72
  if (allocator->head == NULL) {
59
73
  allocator->head = page;
@@ -72,15 +86,15 @@ static bool hb_arena_append_page(hb_arena_T* allocator, size_t minimum_size) {
72
86
  return true;
73
87
  }
74
88
 
75
- bool hb_arena_init(hb_arena_T* allocator, size_t initial_size) {
76
- assert(initial_size > 0);
89
+ bool hb_arena_init(hb_arena_T* allocator, size_t default_page_size) {
90
+ assert(default_page_size > 0);
77
91
 
78
92
  allocator->head = NULL;
79
93
  allocator->tail = NULL;
80
- allocator->default_page_size = initial_size;
94
+ allocator->default_page_size = default_page_size;
81
95
  allocator->allocation_count = 0;
82
96
 
83
- return hb_arena_append_page(allocator, initial_size);
97
+ return hb_arena_append_page(allocator, default_page_size);
84
98
  }
85
99
 
86
100
  void* hb_arena_alloc(hb_arena_T* allocator, size_t size) {
@@ -92,27 +106,28 @@ void* hb_arena_alloc(hb_arena_T* allocator, size_t size) {
92
106
  allocator->allocation_count++;
93
107
 
94
108
  if (hb_arena_page_has_capacity(allocator->tail, required_size)) {
95
- return hb_arena_page_alloc_from(allocator->tail, required_size);
109
+ return hb_arena_page_alloc(allocator->tail, required_size);
96
110
  }
97
111
 
98
112
  for (hb_arena_page_T* page = allocator->tail->next; page != NULL; page = page->next) {
99
113
  if (hb_arena_page_has_capacity(page, required_size)) {
100
114
  allocator->tail = page;
101
- return hb_arena_page_alloc_from(allocator->tail, required_size);
115
+ return hb_arena_page_alloc(allocator->tail, required_size);
102
116
  }
103
117
  }
104
118
 
105
- bool allocated = hb_arena_append_page(allocator, required_size);
119
+ size_t page_size = MAX(allocator->default_page_size, required_size);
120
+ bool allocated = hb_arena_append_page(allocator, page_size);
106
121
 
107
122
  if (!allocated) { return NULL; }
108
123
 
109
- return hb_arena_page_alloc_from(allocator->tail, required_size);
124
+ return hb_arena_page_alloc(allocator->tail, required_size);
110
125
  }
111
126
 
112
127
  size_t hb_arena_position(hb_arena_T* allocator) {
113
128
  size_t total = 0;
114
129
 
115
- hb_arena_for_each_page(allocator, page) {
130
+ hb_arena_for_each_page(allocator) {
116
131
  total += page->position;
117
132
  }
118
133
 
@@ -122,7 +137,7 @@ size_t hb_arena_position(hb_arena_T* allocator) {
122
137
  size_t hb_arena_capacity(hb_arena_T* allocator) {
123
138
  size_t total = 0;
124
139
 
125
- hb_arena_for_each_page(allocator, page) {
140
+ hb_arena_for_each_page(allocator) {
126
141
  total += page->capacity;
127
142
  }
128
143
 
@@ -130,48 +145,58 @@ size_t hb_arena_capacity(hb_arena_T* allocator) {
130
145
  }
131
146
 
132
147
  void hb_arena_reset(hb_arena_T* allocator) {
133
- hb_arena_for_each_page(allocator, page) {
134
- hb_arena_page_reset(page);
135
- }
136
-
137
- allocator->tail = allocator->head;
148
+ hb_arena_reset_to(allocator, 0);
138
149
  allocator->allocation_count = 0;
139
150
  }
140
151
 
141
152
  void hb_arena_reset_to(hb_arena_T* allocator, size_t target_position) {
142
- if (target_position == 0) {
143
- hb_arena_reset(allocator);
153
+ hb_arena_page_T* current_page = allocator->head;
154
+ size_t current_position = 0;
155
+
156
+ while (current_page != NULL) {
157
+ current_position += current_page->position;
144
158
 
145
- return;
159
+ if (current_position >= target_position) {
160
+ current_page->position -= current_position - target_position;
161
+ break;
162
+ }
163
+
164
+ current_page = current_page->next;
146
165
  }
147
166
 
148
- size_t accumulated = 0;
167
+ if (current_page == NULL) { return; }
149
168
 
150
- hb_arena_for_each_page(allocator, page) {
151
- if (accumulated + page->capacity >= target_position) {
152
- page->position = target_position - accumulated;
153
- allocator->tail = page;
169
+ allocator->tail = current_page;
154
170
 
155
- hb_arena_reset_pages_after(page->next);
171
+ if (current_page->next != NULL) {
172
+ size_t freed_size = hb_arena_page_free(current_page->next);
173
+ current_page->next = NULL;
156
174
 
157
- return;
158
- }
175
+ hb_arena_append_page(allocator, freed_size);
176
+ }
177
+ }
178
+
179
+ static size_t hb_arena_page_free(hb_arena_page_T* starting_page) {
180
+ size_t freed_capacity = 0;
159
181
 
160
- accumulated += page->capacity;
161
- page->position = page->capacity;
182
+ for (hb_arena_page_T* current_page = starting_page; current_page != NULL;) {
183
+ hb_arena_page_T* next_page = current_page->next;
184
+
185
+ freed_capacity += current_page->capacity;
186
+
187
+ size_t total_size = sizeof(hb_arena_page_T) + current_page->capacity;
188
+ hb_arena_free_page(current_page, total_size);
189
+
190
+ current_page = next_page;
162
191
  }
192
+
193
+ return freed_capacity;
163
194
  }
164
195
 
165
196
  void hb_arena_free(hb_arena_T* allocator) {
166
197
  if (allocator->head == NULL) { return; }
167
198
 
168
- for (hb_arena_page_T* current = allocator->head; current != NULL;) {
169
- hb_arena_page_T* next = current->next;
170
- size_t total_size = sizeof(hb_arena_page_T) + current->capacity;
171
- hb_system_free_memory(current, total_size);
172
-
173
- current = next;
174
- }
199
+ hb_arena_page_free(allocator->head);
175
200
 
176
201
  allocator->head = NULL;
177
202
  allocator->tail = NULL;
@@ -3,6 +3,7 @@
3
3
  #include <string.h>
4
4
 
5
5
  #include "../include/util/hb_arena.h"
6
+ #include "../include/util/hb_arena_debug.h"
6
7
 
7
8
  #define ANSI_COLOR_GREEN "\033[32m"
8
9
  #define ANSI_COLOR_YELLOW "\033[33m"
@@ -117,6 +118,29 @@ static void print_box_line_with_bullet_and_color(const char* color, const char*
117
118
  printf("║%s%*s║\n", line, BOX_WIDTH - visual_length, "");
118
119
  }
119
120
 
121
+ hb_arena_stats_T hb_arena_get_stats(const hb_arena_T* arena) {
122
+ hb_arena_stats_T stats = { 0 };
123
+
124
+ if (arena->head == NULL) { return stats; }
125
+
126
+ stats.default_page_size = arena->default_page_size;
127
+ stats.allocations = arena->allocation_count;
128
+
129
+ hb_arena_for_each_page_const(arena) {
130
+ stats.pages++;
131
+ stats.total_capacity += page->capacity;
132
+ stats.total_used += page->position;
133
+
134
+ if (page != arena->tail && page->position < page->capacity) {
135
+ stats.fragmentation += (page->capacity - page->position);
136
+ }
137
+ }
138
+
139
+ stats.total_available = stats.total_capacity - stats.total_used;
140
+
141
+ return stats;
142
+ }
143
+
120
144
  void hb_arena_print_stats(const hb_arena_T* allocator) {
121
145
  if (allocator->head == NULL) {
122
146
  print_box_top();
@@ -128,22 +152,13 @@ void hb_arena_print_stats(const hb_arena_T* allocator) {
128
152
  return;
129
153
  }
130
154
 
131
- size_t num_pages = 0;
132
- size_t total_capacity = 0;
133
- size_t total_used = 0;
134
- size_t fragmentation = 0;
155
+ hb_arena_stats_T stats = hb_arena_get_stats(allocator);
135
156
 
136
- for (const hb_arena_page_T* page = allocator->head; page != NULL; page = page->next) {
137
- num_pages++;
138
- total_capacity += page->capacity;
139
- total_used += page->position;
140
-
141
- if (page != allocator->tail && page->position < page->capacity) {
142
- fragmentation += (page->capacity - page->position);
143
- }
144
- }
157
+ size_t total_capacity = stats.total_capacity;
158
+ size_t total_used = stats.total_used;
159
+ size_t total_available = stats.total_available;
160
+ size_t fragmentation = stats.fragmentation;
145
161
 
146
- size_t total_available = total_capacity - total_used;
147
162
  double usage_percentage = (double) total_used / (double) total_capacity * 100.0;
148
163
  double fragmentation_percentage = (double) fragmentation / (double) total_capacity * 100.0;
149
164
  const char* overall_color = get_usage_color(usage_percentage);
@@ -160,7 +175,7 @@ void hb_arena_print_stats(const hb_arena_T* allocator) {
160
175
  print_box_separator();
161
176
  print_box_line(" Statistics:");
162
177
 
163
- print_box_line_with_bullet(" • Pages: %zu", num_pages);
178
+ print_box_line_with_bullet(" • Pages: %zu", stats.pages);
164
179
  print_box_line_with_bullet(" • Default Page Size: %s", default_size_string);
165
180
  print_box_line_with_bullet(" • Total Capacity: %s", capacity_string);
166
181
  print_box_line_with_bullet_and_color(
@@ -178,7 +193,7 @@ void hb_arena_print_stats(const hb_arena_T* allocator) {
178
193
  usage_percentage,
179
194
  ANSI_COLOR_RESET
180
195
  );
181
- print_box_line_with_bullet(" • Allocations: %zu", allocator->allocation_count);
196
+ print_box_line_with_bullet(" • Allocations: %zu", stats.allocations);
182
197
  print_box_line_with_bullet(" • Fragmentation: %s", fragmentation_string);
183
198
 
184
199
  if (fragmentation > 0) { print_box_line(" (%.1f%% skipped in non-tail pages)", fragmentation_percentage); }
@@ -187,7 +202,7 @@ void hb_arena_print_stats(const hb_arena_T* allocator) {
187
202
 
188
203
  size_t page_number = 0;
189
204
 
190
- for (const hb_arena_page_T* page = allocator->head; page != NULL; page = page->next) {
205
+ hb_arena_for_each_page_const(allocator) {
191
206
  double page_usage = (double) page->position / (double) page->capacity * 100.0;
192
207
  const char* page_color = get_usage_color(page_usage);
193
208