mt-lang 0.3.8 → 0.3.10
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.
- checksums.yaml +4 -4
- data/lib/milk_tea/base.rb +1 -2
- data/lib/milk_tea/bindings/bindgen/ast_parser.rb +0 -2
- data/lib/milk_tea/bindings/bindgen/declaration.rb +0 -2
- data/lib/milk_tea/bindings/bindgen/emitter.rb +0 -2
- data/lib/milk_tea/bindings/bindgen/overrides.rb +0 -2
- data/lib/milk_tea/bindings/bindgen/type_mapper.rb +51 -53
- data/lib/milk_tea/bindings/imported_bindings/generator.rb +7 -9
- data/lib/milk_tea/bindings/imported_bindings/method_source.rb +0 -2
- data/lib/milk_tea/bindings/imported_bindings/naming.rb +6 -8
- data/lib/milk_tea/bindings/imported_bindings.rb +0 -2
- data/lib/milk_tea/bindings/opengl_registry.rb +3 -7
- data/lib/milk_tea/bindings/steamworks.rb +12 -16
- data/lib/milk_tea/bindings/upstream_sources.rb +10 -14
- data/lib/milk_tea/bindings/vendored_box2d.rb +10 -12
- data/lib/milk_tea/bindings/vendored_cjson.rb +6 -8
- data/lib/milk_tea/bindings/vendored_flecs.rb +10 -12
- data/lib/milk_tea/bindings/vendored_glfw.rb +11 -13
- data/lib/milk_tea/bindings/vendored_libuv.rb +11 -13
- data/lib/milk_tea/bindings/vendored_pcre2.rb +11 -13
- data/lib/milk_tea/bindings/vendored_raylib.rb +6 -10
- data/lib/milk_tea/bindings/vendored_sdl3.rb +11 -13
- data/lib/milk_tea/bindings/vendored_steamworks.rb +1 -5
- data/lib/milk_tea/core/ast.rb +103 -45
- data/lib/milk_tea/core/async_runtime_installer.rb +0 -2
- data/lib/milk_tea/core/bindings/attribute_binding.rb +1 -3
- data/lib/milk_tea/core/c_backend/aggregate_utils.rb +147 -84
- data/lib/milk_tea/core/c_backend/control_flow_emission.rb +394 -396
- data/lib/milk_tea/core/c_backend/expressions.rb +489 -476
- data/lib/milk_tea/core/c_backend/feature_detection.rb +446 -437
- data/lib/milk_tea/core/c_backend/format_helpers.rb +0 -2
- data/lib/milk_tea/core/c_backend/reachability.rb +339 -341
- data/lib/milk_tea/core/c_backend/reinterpret.rb +231 -217
- data/lib/milk_tea/core/c_backend/runtime_helpers.rb +531 -526
- data/lib/milk_tea/core/c_backend/statements.rb +528 -530
- data/lib/milk_tea/core/c_backend/type_collectors.rb +1344 -1346
- data/lib/milk_tea/core/c_backend/type_declaration.rb +215 -213
- data/lib/milk_tea/core/c_backend/type_system.rb +347 -349
- data/lib/milk_tea/core/c_backend.rb +17 -9
- data/lib/milk_tea/core/compatibility_helpers.rb +0 -2
- data/lib/milk_tea/core/compile_time/const_eval.rb +7 -12
- data/lib/milk_tea/core/compile_time.rb +32 -38
- data/lib/milk_tea/core/control_flow/builder.rb +3 -5
- data/lib/milk_tea/core/control_flow/constant_propagation.rb +14 -17
- data/lib/milk_tea/core/control_flow/definite_assignment.rb +0 -1
- data/lib/milk_tea/core/ir.rb +10 -10
- data/lib/milk_tea/core/lexer/character_classes.rb +5 -7
- data/lib/milk_tea/core/lexer/format_strings.rb +7 -9
- data/lib/milk_tea/core/lexer/heredocs.rb +22 -22
- data/lib/milk_tea/core/lexer/indentation.rb +0 -2
- data/lib/milk_tea/core/lexer/numbers.rb +0 -2
- data/lib/milk_tea/core/lexer/recovery.rb +0 -2
- data/lib/milk_tea/core/lexer/strings.rb +0 -2
- data/lib/milk_tea/core/lexer/symbols.rb +0 -2
- data/lib/milk_tea/core/lexer/trivia.rb +0 -2
- data/lib/milk_tea/core/lexer.rb +0 -2
- data/lib/milk_tea/core/lowering/async/analysis.rb +0 -2
- data/lib/milk_tea/core/lowering/async/lowering.rb +1264 -1265
- data/lib/milk_tea/core/lowering/async/normalization.rb +0 -2
- data/lib/milk_tea/core/lowering/async.rb +627 -630
- data/lib/milk_tea/core/lowering/block.rb +413 -416
- data/lib/milk_tea/core/lowering/calls.rb +1381 -1384
- data/lib/milk_tea/core/lowering/declarations.rb +180 -183
- data/lib/milk_tea/core/lowering/dyn.rb +48 -50
- data/lib/milk_tea/core/lowering/events.rb +983 -985
- data/lib/milk_tea/core/lowering/expressions.rb +1513 -1518
- data/lib/milk_tea/core/lowering/foreign_cstr.rb +181 -184
- data/lib/milk_tea/core/lowering/functions.rb +185 -188
- data/lib/milk_tea/core/lowering/loops.rb +1028 -1031
- data/lib/milk_tea/core/lowering/proc.rb +363 -366
- data/lib/milk_tea/core/lowering/resolve.rb +2133 -2135
- data/lib/milk_tea/core/lowering/scans.rb +179 -181
- data/lib/milk_tea/core/lowering/str_buffer.rb +102 -105
- data/lib/milk_tea/core/lowering/utils.rb +1040 -1044
- data/lib/milk_tea/core/lowering.rb +5 -7
- data/lib/milk_tea/core/module_binder.rb +0 -2
- data/lib/milk_tea/core/module_loader/errors.rb +4 -2
- data/lib/milk_tea/core/module_loader.rb +113 -20
- data/lib/milk_tea/core/module_path_resolver.rb +0 -2
- data/lib/milk_tea/core/module_roots.rb +6 -8
- data/lib/milk_tea/core/parser/attributes.rb +0 -2
- data/lib/milk_tea/core/parser/blocks.rb +0 -2
- data/lib/milk_tea/core/parser/declarations.rb +0 -2
- data/lib/milk_tea/core/parser/expressions.rb +31 -14
- data/lib/milk_tea/core/parser/recovery.rb +0 -2
- data/lib/milk_tea/core/parser/statements.rb +0 -2
- data/lib/milk_tea/core/parser/types.rb +8 -10
- data/lib/milk_tea/core/parser.rb +1 -3
- data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +31 -33
- data/lib/milk_tea/core/pretty_printer/base_formatter.rb +0 -2
- data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +0 -2
- data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +0 -2
- data/lib/milk_tea/core/semantic_analyzer/attributes.rb +14 -16
- data/lib/milk_tea/core/semantic_analyzer/calls.rb +0 -2
- data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1 -14
- data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +18 -21
- data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +0 -3
- data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +12 -15
- data/lib/milk_tea/core/semantic_analyzer/generics.rb +57 -59
- data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +0 -2
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +5 -9
- data/lib/milk_tea/core/semantic_analyzer/nullability.rb +0 -3
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +35 -40
- data/lib/milk_tea/core/semantic_analyzer/top_level.rb +10 -12
- data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +0 -6
- data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +102 -105
- data/lib/milk_tea/core/semantic_analyzer.rb +17 -8
- data/lib/milk_tea/core/types/layout.rb +21 -22
- data/lib/milk_tea/core/types/predicates.rb +0 -2
- data/lib/milk_tea/core/types/registry.rb +16 -18
- data/lib/milk_tea/core/types/visitor.rb +0 -4
- data/lib/milk_tea/dap/backends/lldb_dap.rb +0 -2
- data/lib/milk_tea/dap/protocol.rb +1 -1
- data/lib/milk_tea/dap/server/breakpoints.rb +0 -2
- data/lib/milk_tea/dap/server/debug_map.rb +0 -2
- data/lib/milk_tea/dap/server/handlers.rb +0 -2
- data/lib/milk_tea/dap/server/launch.rb +0 -2
- data/lib/milk_tea/dap/server/lldb_backend.rb +8 -10
- data/lib/milk_tea/dap/server/pause_diagnostics.rb +14 -16
- data/lib/milk_tea/dap/server/utilities.rb +0 -2
- data/lib/milk_tea/dap/server/wire.rb +0 -2
- data/lib/milk_tea/dap/server.rb +0 -2
- data/lib/milk_tea/lsp/diagnostics.rb +16 -16
- data/lib/milk_tea/lsp/server/call_hierarchy.rb +11 -12
- data/lib/milk_tea/lsp/server/code_actions.rb +355 -357
- data/lib/milk_tea/lsp/server/code_lens.rb +23 -25
- data/lib/milk_tea/lsp/server/completion.rb +873 -875
- data/lib/milk_tea/lsp/server/configuration.rb +129 -131
- data/lib/milk_tea/lsp/server/debug_info.rb +0 -2
- data/lib/milk_tea/lsp/server/definition.rb +564 -565
- data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +191 -193
- data/lib/milk_tea/lsp/server/execute_command.rb +0 -2
- data/lib/milk_tea/lsp/server/folding_range.rb +0 -4
- data/lib/milk_tea/lsp/server/formatting.rb +478 -480
- data/lib/milk_tea/lsp/server/hover.rb +1282 -1284
- data/lib/milk_tea/lsp/server/inlay_hints.rb +179 -181
- data/lib/milk_tea/lsp/server/lifecycle.rb +192 -194
- data/lib/milk_tea/lsp/server/linked_editing_range.rb +0 -2
- data/lib/milk_tea/lsp/server/on_type_formatting.rb +0 -2
- data/lib/milk_tea/lsp/server/progress.rb +0 -2
- data/lib/milk_tea/lsp/server/references.rb +316 -318
- data/lib/milk_tea/lsp/server/rename.rb +445 -447
- data/lib/milk_tea/lsp/server/selection_range.rb +0 -4
- data/lib/milk_tea/lsp/server/semantic_tokens.rb +1318 -1320
- data/lib/milk_tea/lsp/server/signature_help.rb +146 -148
- data/lib/milk_tea/lsp/server/text_documents.rb +99 -101
- data/lib/milk_tea/lsp/server/type_hierarchy.rb +2 -4
- data/lib/milk_tea/lsp/server/utilities.rb +397 -399
- data/lib/milk_tea/lsp/server.rb +0 -3
- data/lib/milk_tea/lsp/workspace/analysis.rb +40 -42
- data/lib/milk_tea/lsp/workspace/dependency_graph.rb +4 -10
- data/lib/milk_tea/lsp/workspace/utilities.rb +19 -19
- data/lib/milk_tea/packages/atomic_write.rb +4 -5
- data/lib/milk_tea/packages/dependency_solver.rb +15 -15
- data/lib/milk_tea/packages/lock.rb +4 -4
- data/lib/milk_tea/packages/manager_cli.rb +1 -1
- data/lib/milk_tea/packages/manifest.rb +3 -3
- data/lib/milk_tea/packages/registry_store.rb +4 -6
- data/lib/milk_tea/packages/services.rb +16 -16
- data/lib/milk_tea/packages/source_cache.rb +4 -6
- data/lib/milk_tea/packages/source_resolver.rb +44 -44
- data/lib/milk_tea/packages/version.rb +8 -8
- data/lib/milk_tea/tooling/build.rb +3 -7
- data/lib/milk_tea/tooling/build_cache.rb +0 -2
- data/lib/milk_tea/tooling/debug_info_formatter.rb +31 -33
- data/lib/milk_tea/tooling/debug_map.rb +8 -10
- data/lib/milk_tea/tooling/docs_app.rb +0 -2
- data/lib/milk_tea/tooling/linter/doc_tags.rb +25 -27
- data/lib/milk_tea/tooling/linter/fix_engine.rb +87 -24
- data/lib/milk_tea/tooling/linter/flow_rules.rb +44 -46
- data/lib/milk_tea/tooling/linter/imports_platform.rb +14 -16
- data/lib/milk_tea/tooling/linter/release_rules.rb +3 -5
- data/lib/milk_tea/tooling/linter/reserved_names.rb +23 -25
- data/lib/milk_tea/tooling/linter/rules.rb +87 -89
- data/lib/milk_tea/tooling/linter/source_helpers.rb +40 -42
- data/lib/milk_tea/tooling/linter/trailing_comma.rb +17 -19
- data/lib/milk_tea/tooling/linter/visitors.rb +162 -50
- data/lib/milk_tea/tooling/linter.rb +0 -2
- data/lib/milk_tea/tooling/project_scaffold.rb +0 -2
- data/lib/milk_tea/tooling/run.rb +0 -2
- data/lib/milk_tea/tooling/toolchain_cli.rb +0 -2
- data/std/cli.mt +13 -32
- data/std/color.mt +28 -28
- data/std/encoding.mt +4 -4
- data/std/graph.mt +1 -2
- data/std/hash.mt +14 -14
- data/std/http/server.mt +3 -3
- data/std/input.mt +1 -1
- data/std/json.mt +1 -1
- data/std/linear_algebra.mt +6 -6
- data/std/net/mux.mt +1 -1
- data/std/noise.mt +23 -23
- data/std/parse.mt +2 -2
- data/std/process.mt +3 -4
- data/std/sparse_set.mt +1 -1
- data/std/steering.mt +5 -5
- data/std/str.mt +1 -1
- data/std/tween.mt +4 -4
- data/std/utility.mt +3 -3
- metadata +2 -2
|
@@ -3,536 +3,541 @@
|
|
|
3
3
|
module MilkTea
|
|
4
4
|
class CBackend
|
|
5
5
|
module RuntimeHelpers
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
end
|
|
6
|
+
def emit_fatal_helper
|
|
7
|
+
lines = []
|
|
8
|
+
|
|
9
|
+
if uses_mt_fatal_helper?
|
|
10
|
+
lines.concat([
|
|
11
|
+
"static _Noreturn void mt_fatal(const char* message) {",
|
|
12
|
+
"#{INDENT}fputs(message, stderr);",
|
|
13
|
+
"#{INDENT}fputc('\\n', stderr);",
|
|
14
|
+
"#{INDENT}abort();",
|
|
15
|
+
"}",
|
|
16
|
+
])
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
if uses_mt_fatal_str_helper?
|
|
20
|
+
lines << "" unless lines.empty?
|
|
21
|
+
lines.concat([
|
|
22
|
+
"static _Noreturn void mt_fatal_str(mt_str message) {",
|
|
23
|
+
"#{INDENT}fwrite(message.data, 1, message.len, stderr);",
|
|
24
|
+
"#{INDENT}fputc('\\n', stderr);",
|
|
25
|
+
"#{INDENT}abort();",
|
|
26
|
+
"}",
|
|
27
|
+
])
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
lines
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def emit_str_equality_helper
|
|
34
|
+
[
|
|
35
|
+
"static bool mt_str_equal(mt_str left, mt_str right) {",
|
|
36
|
+
"#{INDENT}if (left.len != right.len) return false;",
|
|
37
|
+
"#{INDENT}for (uintptr_t index = 0; index < left.len; index++) {",
|
|
38
|
+
"#{INDENT * 2}if (left.data[index] != right.data[index]) return false;",
|
|
39
|
+
"#{INDENT}}",
|
|
40
|
+
"#{INDENT}return true;",
|
|
41
|
+
"}",
|
|
42
|
+
]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def emit_variant_equality_helpers
|
|
46
|
+
emitted_aggregate_variants.flat_map { |variant_decl| emit_variant_equality_helper(variant_decl) }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def emit_variant_equality_helper(variant_decl)
|
|
50
|
+
outer_c = variant_decl.linkage_name
|
|
51
|
+
lines = ["static bool mt_variant_eq_#{outer_c}(struct #{outer_c} left, struct #{outer_c} right) {"]
|
|
52
|
+
lines << "#{INDENT}if (left.kind != right.kind) return false;"
|
|
53
|
+
lines << "#{INDENT}switch (left.kind) {"
|
|
54
|
+
|
|
55
|
+
variant_decl.arms.each do |arm|
|
|
56
|
+
lines << "#{INDENT * 2}case #{outer_c}_kind_#{arm.name}:"
|
|
57
|
+
if arm.fields.empty?
|
|
58
|
+
lines << "#{INDENT * 3}return true;"
|
|
59
|
+
else
|
|
60
|
+
arm.fields.each do |field|
|
|
61
|
+
field_type = field.type
|
|
62
|
+
left_expr = "left.data.#{sanitize_c_identifier(arm.name)}.#{sanitize_c_identifier(field.name)}"
|
|
63
|
+
right_expr = "right.data.#{sanitize_c_identifier(arm.name)}.#{sanitize_c_identifier(field.name)}"
|
|
64
|
+
if field_type.is_a?(Types::StringView)
|
|
65
|
+
lines << "#{INDENT * 3}if (!mt_str_equal(#{left_expr}, #{right_expr})) return false;"
|
|
66
|
+
elsif field_type.is_a?(Types::Variant)
|
|
67
|
+
lines << "#{INDENT * 3}if (!mt_variant_eq_#{named_type_c_name(field_type)}(#{left_expr}, #{right_expr})) return false;"
|
|
68
|
+
elsif field_type.is_a?(Types::Nullable)
|
|
69
|
+
if c_backend_pointer_like_type?(field_type.base)
|
|
70
|
+
lines << "#{INDENT * 3}if (#{left_expr} != #{right_expr}) return false;"
|
|
71
|
+
else
|
|
72
|
+
lines << "#{INDENT * 3}if (#{left_expr}.has_value != #{right_expr}.has_value) return false;"
|
|
73
|
+
lines << "#{INDENT * 3}if (#{left_expr}.has_value && #{left_expr}.value != #{right_expr}.value) return false;"
|
|
75
74
|
end
|
|
76
|
-
|
|
75
|
+
elsif field_type.is_a?(Types::Primitive) || field_type.is_a?(Types::EnumBase)
|
|
76
|
+
lines << "#{INDENT * 3}if (#{left_expr} != #{right_expr}) return false;"
|
|
77
|
+
else
|
|
78
|
+
lines << "#{INDENT * 3}if (#{left_expr} != #{right_expr}) return false;"
|
|
77
79
|
end
|
|
78
80
|
end
|
|
79
|
-
|
|
80
|
-
lines << "#{INDENT * 2}default: return true;"
|
|
81
|
-
lines << "#{INDENT}}"
|
|
82
|
-
lines << "}"
|
|
83
|
-
lines
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def emit_async_memory_helpers
|
|
87
|
-
[
|
|
88
|
-
"#define MT_ASYNC_HEADER_SIZE (sizeof(uint64_t) + sizeof(uintptr_t))",
|
|
89
|
-
"#define MT_ASYNC_MAGIC UINT64_C(0x4D5441464D454D00)",
|
|
90
|
-
"",
|
|
91
|
-
"static void* mt_async_alloc(uintptr_t size) {",
|
|
92
|
-
"#{INDENT}char* raw = (char*) calloc(1, (size_t)(MT_ASYNC_HEADER_SIZE + size));",
|
|
93
|
-
"#{INDENT}if (raw == NULL) {",
|
|
94
|
-
"#{INDENT * 2}abort();",
|
|
95
|
-
"#{INDENT}}",
|
|
96
|
-
"#{INDENT}*(uint64_t*)raw = MT_ASYNC_MAGIC;",
|
|
97
|
-
"#{INDENT}*(uintptr_t*)(raw + sizeof(uint64_t)) = 1;",
|
|
98
|
-
"#{INDENT}return raw + MT_ASYNC_HEADER_SIZE;",
|
|
99
|
-
"}",
|
|
100
|
-
"",
|
|
101
|
-
"static void mt_async_retain(void* frame) {",
|
|
102
|
-
"#{INDENT}char* raw = (char*)frame - MT_ASYNC_HEADER_SIZE;",
|
|
103
|
-
"#{INDENT}if (*(uint64_t*)raw != MT_ASYNC_MAGIC) return;",
|
|
104
|
-
"#{INDENT}uintptr_t* ref = (uintptr_t*)(raw + sizeof(uint64_t));",
|
|
105
|
-
"#{INDENT}(*ref)++;",
|
|
106
|
-
"}",
|
|
107
|
-
"",
|
|
108
|
-
"static void mt_async_free(void* frame) {",
|
|
109
|
-
"#{INDENT}char* raw = (char*)frame - MT_ASYNC_HEADER_SIZE;",
|
|
110
|
-
"#{INDENT}if (*(uint64_t*)raw != MT_ASYNC_MAGIC) return;",
|
|
111
|
-
"#{INDENT}uintptr_t* ref = (uintptr_t*)(raw + sizeof(uint64_t));",
|
|
112
|
-
"#{INDENT}if (--(*ref) == 0) {",
|
|
113
|
-
"#{INDENT * 2}free(raw);",
|
|
114
|
-
"#{INDENT}}",
|
|
115
|
-
"}",
|
|
116
|
-
]
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def emit_parallel_for_helper
|
|
120
|
-
[
|
|
121
|
-
"typedef struct {",
|
|
122
|
-
"#{INDENT}void (*work)(void* data, int64_t start, int64_t end);",
|
|
123
|
-
"#{INDENT}void* data;",
|
|
124
|
-
"#{INDENT}int64_t start;",
|
|
125
|
-
"#{INDENT}int64_t end;",
|
|
126
|
-
"} mt_pfor_chunk;",
|
|
127
|
-
"",
|
|
128
|
-
"static void mt_pfor_runner(void* arg) {",
|
|
129
|
-
"#{INDENT}mt_pfor_chunk* chunk = (mt_pfor_chunk*)arg;",
|
|
130
|
-
"#{INDENT}chunk->work(chunk->data, chunk->start, chunk->end);",
|
|
131
|
-
"}",
|
|
132
|
-
"",
|
|
133
|
-
"static void mt_parallel_for(void (*work)(void* data, int64_t start, int64_t end), void* data, int64_t count) {",
|
|
134
|
-
"#{INDENT}if (count <= 0) return;",
|
|
135
|
-
"#{INDENT}uv_cpu_info_t* cpu_info;",
|
|
136
|
-
"#{INDENT}int ncpu = 1;",
|
|
137
|
-
"#{INDENT}if (uv_cpu_info(&cpu_info, &ncpu) == 0) {",
|
|
138
|
-
"#{INDENT * 2}uv_free_cpu_info(cpu_info, ncpu);",
|
|
139
|
-
"#{INDENT}}",
|
|
140
|
-
"#{INDENT}if (ncpu < 1) ncpu = 1;",
|
|
141
|
-
"#{INDENT}if (ncpu > 64) ncpu = 64;",
|
|
142
|
-
"#{INDENT}if (count < (int64_t)ncpu) ncpu = (int)count;",
|
|
143
|
-
"#{INDENT}int64_t chunk_size = (count + ncpu - 1) / ncpu;",
|
|
144
|
-
"#{INDENT}mt_pfor_chunk chunks[64];",
|
|
145
|
-
"#{INDENT}uv_thread_t threads[64];",
|
|
146
|
-
"#{INDENT}int nworkers = 0;",
|
|
147
|
-
"#{INDENT}for (int t = 1; t < ncpu; t++) {",
|
|
148
|
-
"#{INDENT * 2}int64_t s = t * chunk_size;",
|
|
149
|
-
"#{INDENT * 2}int64_t e = s + chunk_size;",
|
|
150
|
-
"#{INDENT * 2}if (e > count) e = count;",
|
|
151
|
-
"#{INDENT * 2}if (s >= count) break;",
|
|
152
|
-
"#{INDENT * 2}chunks[nworkers].work = work;",
|
|
153
|
-
"#{INDENT * 2}chunks[nworkers].data = data;",
|
|
154
|
-
"#{INDENT * 2}chunks[nworkers].start = s;",
|
|
155
|
-
"#{INDENT * 2}chunks[nworkers].end = e;",
|
|
156
|
-
"#{INDENT * 2}uv_thread_create(&threads[nworkers], mt_pfor_runner, &chunks[nworkers]);",
|
|
157
|
-
"#{INDENT * 2}nworkers++;",
|
|
158
|
-
"#{INDENT}}",
|
|
159
|
-
"#{INDENT}int64_t first_end = chunk_size < count ? chunk_size : count;",
|
|
160
|
-
"#{INDENT}work(data, 0, first_end);",
|
|
161
|
-
"#{INDENT}for (int t = 0; t < nworkers; t++) {",
|
|
162
|
-
"#{INDENT * 2}uv_thread_join(&threads[t]);",
|
|
163
|
-
"#{INDENT}}",
|
|
164
|
-
"}",
|
|
165
|
-
]
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
def emit_spawn_all_helper
|
|
169
|
-
[
|
|
170
|
-
"typedef struct {",
|
|
171
|
-
"#{INDENT}void (*work)(void* data);",
|
|
172
|
-
"#{INDENT}void* data;",
|
|
173
|
-
"} mt_spawn_item;",
|
|
174
|
-
"",
|
|
175
|
-
"static void mt_spawn_item_runner(void* arg) {",
|
|
176
|
-
"#{INDENT}mt_spawn_item* item = (mt_spawn_item*)arg;",
|
|
177
|
-
"#{INDENT}item->work(item->data);",
|
|
178
|
-
"}",
|
|
179
|
-
"",
|
|
180
|
-
"static void mt_spawn_all(mt_spawn_item* items, int count) {",
|
|
181
|
-
"#{INDENT}if (count <= 0) return;",
|
|
182
|
-
"#{INDENT}uv_thread_t threads[64];",
|
|
183
|
-
"#{INDENT}int nworkers = 0;",
|
|
184
|
-
"#{INDENT}for (int t = 1; t < count && nworkers < 63; t++) {",
|
|
185
|
-
"#{INDENT * 2}uv_thread_create(&threads[nworkers], mt_spawn_item_runner, &items[t]);",
|
|
186
|
-
"#{INDENT * 2}nworkers++;",
|
|
187
|
-
"#{INDENT}}",
|
|
188
|
-
"#{INDENT}items[0].work(items[0].data);",
|
|
189
|
-
"#{INDENT}for (int t = 0; t < nworkers; t++) {",
|
|
190
|
-
"#{INDENT * 2}uv_thread_join(&threads[t]);",
|
|
191
|
-
"#{INDENT}}",
|
|
192
|
-
"}",
|
|
193
|
-
]
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
def emit_detach_helpers
|
|
197
|
-
[
|
|
198
|
-
"typedef struct {",
|
|
199
|
-
"#{INDENT}uv_thread_t thread;",
|
|
200
|
-
"} mt_detach_handle;",
|
|
201
|
-
"",
|
|
202
|
-
"static void* mt_detach_run(void (*work)(void*), void* cap) {",
|
|
203
|
-
"#{INDENT}mt_detach_handle* h = (mt_detach_handle*)malloc(sizeof(mt_detach_handle));",
|
|
204
|
-
"#{INDENT}uv_thread_create(&h->thread, work, cap);",
|
|
205
|
-
"#{INDENT}return h;",
|
|
206
|
-
"}",
|
|
207
|
-
"",
|
|
208
|
-
"static void mt_detach_join(void* handle) {",
|
|
209
|
-
"#{INDENT}if (!handle) return;",
|
|
210
|
-
"#{INDENT}mt_detach_handle* h = (mt_detach_handle*)handle;",
|
|
211
|
-
"#{INDENT}uv_thread_join(&h->thread);",
|
|
212
|
-
"#{INDENT}free(h);",
|
|
213
|
-
"}",
|
|
214
|
-
]
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
def emit_foreign_temp_cstr_helpers
|
|
218
|
-
lines = []
|
|
219
|
-
|
|
220
|
-
if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_foreign_str_to_cstr_temp]) }
|
|
221
|
-
lines.concat([
|
|
222
|
-
"static const char* mt_foreign_str_to_cstr_temp(mt_str value) {",
|
|
223
|
-
"#{INDENT}char* data = (char*)malloc(value.len + 1);",
|
|
224
|
-
"#{INDENT}uintptr_t index = 0;",
|
|
225
|
-
"#{INDENT}if (data == NULL) mt_fatal(\"foreign str temporary allocation failed\");",
|
|
226
|
-
"#{INDENT}while (index < value.len) {",
|
|
227
|
-
"#{INDENT * 2}data[index] = value.data[index];",
|
|
228
|
-
"#{INDENT * 2}index++;",
|
|
229
|
-
"#{INDENT}}",
|
|
230
|
-
"#{INDENT}data[value.len] = '\\0';",
|
|
231
|
-
"#{INDENT}return data;",
|
|
232
|
-
"}",
|
|
233
|
-
])
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_free_foreign_cstr_temp]) }
|
|
237
|
-
lines << "" unless lines.empty?
|
|
238
|
-
lines.concat([
|
|
239
|
-
"static void mt_free_foreign_cstr_temp(const char* value) {",
|
|
240
|
-
"#{INDENT}free((void*)value);",
|
|
241
|
-
"}",
|
|
242
|
-
])
|
|
243
|
-
end
|
|
244
|
-
|
|
245
|
-
if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_foreign_strs_to_cstrs_temp]) }
|
|
246
|
-
lines << "" unless lines.empty?
|
|
247
|
-
lines.concat([
|
|
248
|
-
"static void mt_foreign_strs_to_cstrs_temp(mt_span_str values, char*** items_out, char** data_out, uintptr_t* len_out) {",
|
|
249
|
-
"#{INDENT}uintptr_t total_bytes = 0;",
|
|
250
|
-
"#{INDENT}uintptr_t index = 0;",
|
|
251
|
-
"#{INDENT}uintptr_t offset = 0;",
|
|
252
|
-
"#{INDENT}char** items = NULL;",
|
|
253
|
-
"#{INDENT}char* data = NULL;",
|
|
254
|
-
"#{INDENT}while (index < values.len) {",
|
|
255
|
-
"#{INDENT * 2}total_bytes += values.data[index].len + 1;",
|
|
256
|
-
"#{INDENT * 2}index++;",
|
|
257
|
-
"#{INDENT}}",
|
|
258
|
-
"#{INDENT}if (values.len > 0) {",
|
|
259
|
-
"#{INDENT * 2}items = (char**)malloc(values.len * sizeof(char*));",
|
|
260
|
-
"#{INDENT * 2}if (items == NULL) mt_fatal(\"foreign string-list temporary allocation failed\");",
|
|
261
|
-
"#{INDENT}}",
|
|
262
|
-
"#{INDENT}if (total_bytes > 0) {",
|
|
263
|
-
"#{INDENT * 2}data = (char*)malloc(total_bytes);",
|
|
264
|
-
"#{INDENT * 2}if (data == NULL) {",
|
|
265
|
-
"#{INDENT * 3}free(items);",
|
|
266
|
-
"#{INDENT * 3}mt_fatal(\"foreign string-list temporary allocation failed\");",
|
|
267
|
-
"#{INDENT * 2}}",
|
|
268
|
-
"#{INDENT}}",
|
|
269
|
-
"#{INDENT}index = 0;",
|
|
270
|
-
"#{INDENT}while (index < values.len) {",
|
|
271
|
-
"#{INDENT * 2}mt_str value = values.data[index];",
|
|
272
|
-
"#{INDENT * 2}uintptr_t byte_index = 0;",
|
|
273
|
-
"#{INDENT * 2}items[index] = data + offset;",
|
|
274
|
-
"#{INDENT * 2}while (byte_index < value.len) {",
|
|
275
|
-
"#{INDENT * 3}data[offset + byte_index] = value.data[byte_index];",
|
|
276
|
-
"#{INDENT * 3}byte_index++;",
|
|
277
|
-
"#{INDENT * 2}}",
|
|
278
|
-
"#{INDENT * 2}data[offset + value.len] = '\\0';",
|
|
279
|
-
"#{INDENT * 2}offset += value.len + 1;",
|
|
280
|
-
"#{INDENT * 2}index++;",
|
|
281
|
-
"#{INDENT}}",
|
|
282
|
-
"#{INDENT}*items_out = items;",
|
|
283
|
-
"#{INDENT}*data_out = data;",
|
|
284
|
-
"#{INDENT}*len_out = values.len;",
|
|
285
|
-
"}",
|
|
286
|
-
])
|
|
287
|
-
end
|
|
288
|
-
|
|
289
|
-
if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_free_foreign_cstrs_temp]) }
|
|
290
|
-
lines << "" unless lines.empty?
|
|
291
|
-
lines.concat([
|
|
292
|
-
"static void mt_free_foreign_cstrs_temp(char** items, char* data) {",
|
|
293
|
-
"#{INDENT}free(items);",
|
|
294
|
-
"#{INDENT}free(data);",
|
|
295
|
-
"}",
|
|
296
|
-
])
|
|
297
|
-
end
|
|
298
|
-
|
|
299
|
-
lines
|
|
300
|
-
end
|
|
301
|
-
|
|
302
|
-
def emit_entrypoint_argv_helpers
|
|
303
|
-
lines = []
|
|
304
|
-
|
|
305
|
-
if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_entry_argv_to_span_str]) }
|
|
306
|
-
lines.concat([
|
|
307
|
-
"static mt_span_str mt_entry_argv_to_span_str(int32_t argc, char** argv, mt_str** items_out) {",
|
|
308
|
-
"#{INDENT}uintptr_t len = argc > 1 ? (uintptr_t)(argc - 1) : 0;",
|
|
309
|
-
"#{INDENT}mt_str* items = NULL;",
|
|
310
|
-
"#{INDENT}uintptr_t index = 0;",
|
|
311
|
-
"#{INDENT}if (len > 0) {",
|
|
312
|
-
"#{INDENT * 2}items = (mt_str*)malloc(len * sizeof(mt_str));",
|
|
313
|
-
"#{INDENT * 2}if (items == NULL) abort();",
|
|
314
|
-
"#{INDENT}}",
|
|
315
|
-
"#{INDENT}while (index < len) {",
|
|
316
|
-
"#{INDENT * 2}char* value = argv[index + 1];",
|
|
317
|
-
"#{INDENT * 2}items[index] = (mt_str){ .data = value, .len = (uintptr_t)strlen(value) };",
|
|
318
|
-
"#{INDENT * 2}index++;",
|
|
319
|
-
"#{INDENT}}",
|
|
320
|
-
"#{INDENT}*items_out = items;",
|
|
321
|
-
"#{INDENT}return (mt_span_str){ .data = items, .len = len };",
|
|
322
|
-
"}",
|
|
323
|
-
])
|
|
324
|
-
end
|
|
325
|
-
|
|
326
|
-
if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_free_entry_argv_strs]) }
|
|
327
|
-
lines << "" unless lines.empty?
|
|
328
|
-
lines.concat([
|
|
329
|
-
"static void mt_free_entry_argv_strs(mt_str* items) {",
|
|
330
|
-
"#{INDENT}free(items);",
|
|
331
|
-
"}",
|
|
332
|
-
])
|
|
333
|
-
end
|
|
334
|
-
|
|
335
|
-
lines
|
|
336
|
-
end
|
|
337
|
-
|
|
338
|
-
def emit_text_buffer_helpers
|
|
339
|
-
[
|
|
340
|
-
"static bool mt_is_utf8_continuation_byte(unsigned char byte) {",
|
|
341
|
-
"#{INDENT}return (byte & 0xC0u) == 0x80u;",
|
|
342
|
-
"}",
|
|
343
|
-
"",
|
|
344
|
-
"static bool mt_is_valid_utf8(const char* data, uintptr_t len) {",
|
|
345
|
-
"#{INDENT}uintptr_t index = 0;",
|
|
346
|
-
"#{INDENT}while (index < len) {",
|
|
347
|
-
"#{INDENT * 2}unsigned char lead = (unsigned char) data[index];",
|
|
348
|
-
"#{INDENT * 2}if (lead < 0x80u) {",
|
|
349
|
-
"#{INDENT * 3}index++;",
|
|
350
|
-
"#{INDENT * 3}continue;",
|
|
351
|
-
"#{INDENT * 2}}",
|
|
352
|
-
"#{INDENT * 2}if (lead < 0xC2u) return false;",
|
|
353
|
-
"#{INDENT * 2}if (lead < 0xE0u) {",
|
|
354
|
-
"#{INDENT * 3}if (index + 1 >= len) return false;",
|
|
355
|
-
"#{INDENT * 3}unsigned char byte1 = (unsigned char) data[index + 1];",
|
|
356
|
-
"#{INDENT * 3}if (!mt_is_utf8_continuation_byte(byte1)) return false;",
|
|
357
|
-
"#{INDENT * 3}index += 2;",
|
|
358
|
-
"#{INDENT * 3}continue;",
|
|
359
|
-
"#{INDENT * 2}}",
|
|
360
|
-
"#{INDENT * 2}if (lead < 0xF0u) {",
|
|
361
|
-
"#{INDENT * 3}if (index + 2 >= len) return false;",
|
|
362
|
-
"#{INDENT * 3}unsigned char byte1 = (unsigned char) data[index + 1];",
|
|
363
|
-
"#{INDENT * 3}unsigned char byte2 = (unsigned char) data[index + 2];",
|
|
364
|
-
"#{INDENT * 3}if (lead == 0xE0u) {",
|
|
365
|
-
"#{INDENT * 4}if (byte1 < 0xA0u || byte1 > 0xBFu) return false;",
|
|
366
|
-
"#{INDENT * 3}} else if (lead == 0xEDu) {",
|
|
367
|
-
"#{INDENT * 4}if (byte1 < 0x80u || byte1 > 0x9Fu) return false;",
|
|
368
|
-
"#{INDENT * 3}} else if (!mt_is_utf8_continuation_byte(byte1)) {",
|
|
369
|
-
"#{INDENT * 4}return false;",
|
|
370
|
-
"#{INDENT * 3}}",
|
|
371
|
-
"#{INDENT * 3}if (!mt_is_utf8_continuation_byte(byte2)) return false;",
|
|
372
|
-
"#{INDENT * 3}index += 3;",
|
|
373
|
-
"#{INDENT * 3}continue;",
|
|
374
|
-
"#{INDENT * 2}}",
|
|
375
|
-
"#{INDENT * 2}if (lead < 0xF5u) {",
|
|
376
|
-
"#{INDENT * 3}if (index + 3 >= len) return false;",
|
|
377
|
-
"#{INDENT * 3}unsigned char byte1 = (unsigned char) data[index + 1];",
|
|
378
|
-
"#{INDENT * 3}unsigned char byte2 = (unsigned char) data[index + 2];",
|
|
379
|
-
"#{INDENT * 3}unsigned char byte3 = (unsigned char) data[index + 3];",
|
|
380
|
-
"#{INDENT * 3}if (lead == 0xF0u) {",
|
|
381
|
-
"#{INDENT * 4}if (byte1 < 0x90u || byte1 > 0xBFu) return false;",
|
|
382
|
-
"#{INDENT * 3}} else if (lead == 0xF4u) {",
|
|
383
|
-
"#{INDENT * 4}if (byte1 < 0x80u || byte1 > 0x8Fu) return false;",
|
|
384
|
-
"#{INDENT * 3}} else if (!mt_is_utf8_continuation_byte(byte1)) {",
|
|
385
|
-
"#{INDENT * 4}return false;",
|
|
386
|
-
"#{INDENT * 3}}",
|
|
387
|
-
"#{INDENT * 3}if (!mt_is_utf8_continuation_byte(byte2) || !mt_is_utf8_continuation_byte(byte3)) return false;",
|
|
388
|
-
"#{INDENT * 3}index += 4;",
|
|
389
|
-
"#{INDENT * 3}continue;",
|
|
390
|
-
"#{INDENT * 2}}",
|
|
391
|
-
"#{INDENT * 2}return false;",
|
|
392
|
-
"#{INDENT}}",
|
|
393
|
-
"#{INDENT}return true;",
|
|
394
|
-
"}",
|
|
395
|
-
]
|
|
396
|
-
end
|
|
397
|
-
|
|
398
|
-
def emit_str_buffer_helpers
|
|
399
|
-
lines = []
|
|
400
|
-
|
|
401
|
-
if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_len mt_str_buffer_as_cstr mt_str_buffer_append]) }
|
|
402
|
-
lines.concat([
|
|
403
|
-
"static uintptr_t mt_str_buffer_len(char* data, uintptr_t cap, uintptr_t* len, bool* dirty) {",
|
|
404
|
-
"#{INDENT}if (*dirty) {",
|
|
405
|
-
"#{INDENT * 2}uintptr_t current = 0;",
|
|
406
|
-
"#{INDENT * 2}while (current < cap + 1 && data[current] != '\\0') {",
|
|
407
|
-
"#{INDENT * 3}current++;",
|
|
408
|
-
"#{INDENT * 2}}",
|
|
409
|
-
"#{INDENT * 2}if (current > cap) mt_fatal(\"str_buffer text requires a trailing NUL within capacity\");",
|
|
410
|
-
"#{INDENT * 2}if (!mt_is_valid_utf8(data, current)) mt_fatal(\"str_buffer text must be valid UTF-8\");",
|
|
411
|
-
"#{INDENT * 2}*len = current;",
|
|
412
|
-
"#{INDENT * 2}*dirty = false;",
|
|
413
|
-
"#{INDENT}}",
|
|
414
|
-
"#{INDENT}return *len;",
|
|
415
|
-
"}",
|
|
416
|
-
])
|
|
417
|
-
end
|
|
418
|
-
|
|
419
|
-
if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_as_cstr]) }
|
|
420
|
-
lines << "" unless lines.empty?
|
|
421
|
-
lines.concat([
|
|
422
|
-
"static const char* mt_str_buffer_as_cstr(char* data, uintptr_t cap, uintptr_t* len, bool* dirty) {",
|
|
423
|
-
"#{INDENT}(void)mt_str_buffer_len(data, cap, len, dirty);",
|
|
424
|
-
"#{INDENT}return data;",
|
|
425
|
-
"}",
|
|
426
|
-
])
|
|
427
|
-
end
|
|
428
|
-
|
|
429
|
-
if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_clear]) }
|
|
430
|
-
lines << "" unless lines.empty?
|
|
431
|
-
lines.concat([
|
|
432
|
-
"static void mt_str_buffer_clear(char* data, uintptr_t cap, uintptr_t* len, bool* dirty) {",
|
|
433
|
-
"#{INDENT}memset(data, 0, cap + 1);",
|
|
434
|
-
"#{INDENT}*len = 0;",
|
|
435
|
-
"#{INDENT}*dirty = false;",
|
|
436
|
-
"}",
|
|
437
|
-
])
|
|
438
|
-
end
|
|
439
|
-
|
|
440
|
-
if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_assign]) }
|
|
441
|
-
lines << "" unless lines.empty?
|
|
442
|
-
lines.concat([
|
|
443
|
-
"static void mt_str_buffer_assign(mt_str value, char* data, uintptr_t cap, uintptr_t* len, bool* dirty) {",
|
|
444
|
-
"#{INDENT}if (value.len > cap) mt_fatal(\"str_buffer.assign exceeds capacity\");",
|
|
445
|
-
"#{INDENT}memcpy(data, value.data, value.len);",
|
|
446
|
-
"#{INDENT}data[value.len] = '\\0';",
|
|
447
|
-
"#{INDENT}if (value.len < cap + 1) memset(data + value.len + 1, 0, cap - value.len);",
|
|
448
|
-
"#{INDENT}*len = value.len;",
|
|
449
|
-
"#{INDENT}*dirty = false;",
|
|
450
|
-
"}",
|
|
451
|
-
])
|
|
452
|
-
end
|
|
453
|
-
|
|
454
|
-
if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_append]) }
|
|
455
|
-
lines << "" unless lines.empty?
|
|
456
|
-
lines.concat([
|
|
457
|
-
"static void mt_str_buffer_append(mt_str value, char* data, uintptr_t cap, uintptr_t* len, bool* dirty) {",
|
|
458
|
-
"#{INDENT}uintptr_t current = mt_str_buffer_len(data, cap, len, dirty);",
|
|
459
|
-
"#{INDENT}if (value.len > cap - current) mt_fatal(\"str_buffer.append exceeds capacity\");",
|
|
460
|
-
"#{INDENT}memcpy(data + current, value.data, value.len);",
|
|
461
|
-
"#{INDENT}current += value.len;",
|
|
462
|
-
"#{INDENT}data[current] = '\\0';",
|
|
463
|
-
"#{INDENT}*len = current;",
|
|
464
|
-
"#{INDENT}*dirty = false;",
|
|
465
|
-
"}",
|
|
466
|
-
])
|
|
467
|
-
end
|
|
468
|
-
|
|
469
|
-
if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_prepare_write]) }
|
|
470
|
-
lines << "" unless lines.empty?
|
|
471
|
-
lines.concat([
|
|
472
|
-
"static char* mt_str_buffer_prepare_write(char* data, uintptr_t cap, bool* dirty) {",
|
|
473
|
-
"#{INDENT}data[cap] = '\\0';",
|
|
474
|
-
"#{INDENT}*dirty = true;",
|
|
475
|
-
"#{INDENT}return data;",
|
|
476
|
-
"}",
|
|
477
|
-
])
|
|
478
|
-
end
|
|
479
|
-
|
|
480
|
-
lines
|
|
481
|
-
end
|
|
482
|
-
|
|
483
|
-
def emit_checked_array_index_helper(type)
|
|
484
|
-
helper_name = checked_array_index_helper_name(type)
|
|
485
|
-
params = [c_declaration(type, '(*array)'), c_declaration(Types::Registry.primitive('ptr_uint'), 'index')].join(', ')
|
|
486
|
-
[
|
|
487
|
-
"static inline #{c_function_declaration(pointer_to(array_element_type(type)), helper_name, params)} {",
|
|
488
|
-
"#{INDENT}if (index >= #{array_length(type)}) mt_fatal(\"array index out of bounds\");",
|
|
489
|
-
"#{INDENT}return &(*array)[index];",
|
|
490
|
-
"}",
|
|
491
|
-
]
|
|
492
|
-
end
|
|
493
|
-
|
|
494
|
-
def emit_checked_span_index_helper(type)
|
|
495
|
-
helper_name = checked_span_index_helper_name(type)
|
|
496
|
-
params = [c_declaration(type, 'span'), c_declaration(Types::Registry.primitive('ptr_uint'), 'index')].join(', ')
|
|
497
|
-
[
|
|
498
|
-
"static inline #{c_function_declaration(pointer_to(type.element_type), helper_name, params)} {",
|
|
499
|
-
"#{INDENT}if (index >= span.len) mt_fatal(\"span index out of bounds\");",
|
|
500
|
-
"#{INDENT}return &span.data[index];",
|
|
501
|
-
"}",
|
|
502
|
-
]
|
|
503
|
-
end
|
|
504
|
-
|
|
505
|
-
def emit_nullable_array_index_helper(type)
|
|
506
|
-
helper_name = nullable_array_index_helper_name(type)
|
|
507
|
-
params = [c_declaration(type, '(*array)'), c_declaration(Types::Registry.primitive('ptr_uint'), 'index')].join(', ')
|
|
508
|
-
[
|
|
509
|
-
"static inline #{c_function_declaration(pointer_to(array_element_type(type)), helper_name, params)} {",
|
|
510
|
-
"#{INDENT}if (index >= #{array_length(type)}) return NULL;",
|
|
511
|
-
"#{INDENT}return &(*array)[index];",
|
|
512
|
-
"}",
|
|
513
|
-
]
|
|
514
|
-
end
|
|
515
|
-
|
|
516
|
-
def emit_nullable_span_index_helper(type)
|
|
517
|
-
helper_name = nullable_span_index_helper_name(type)
|
|
518
|
-
params = [c_declaration(type, 'span'), c_declaration(Types::Registry.primitive('ptr_uint'), 'index')].join(', ')
|
|
519
|
-
[
|
|
520
|
-
"static inline #{c_function_declaration(pointer_to(type.element_type), helper_name, params)} {",
|
|
521
|
-
"#{INDENT}if (index >= span.len) return NULL;",
|
|
522
|
-
"#{INDENT}return &span.data[index];",
|
|
523
|
-
"}",
|
|
524
|
-
]
|
|
525
|
-
end
|
|
526
|
-
|
|
527
|
-
def emit_str_literal_constants(literals)
|
|
528
|
-
literals.each_with_index.map do |value, i|
|
|
529
|
-
"static const mt_str #{str_literal_name(i)} = { .data = #{value.inspect}, .len = #{value.bytesize} };"
|
|
530
|
-
end
|
|
531
|
-
end
|
|
532
|
-
|
|
533
|
-
def str_literal_name(index)
|
|
534
|
-
"mt_str_lit_#{index}"
|
|
81
|
+
lines << "#{INDENT * 3}return true;"
|
|
535
82
|
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
lines << "#{INDENT * 2}default: return true;"
|
|
86
|
+
lines << "#{INDENT}}"
|
|
87
|
+
lines << "}"
|
|
88
|
+
lines
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def emit_async_memory_helpers
|
|
92
|
+
[
|
|
93
|
+
"#define MT_ASYNC_HEADER_SIZE (sizeof(uint64_t) + sizeof(uintptr_t))",
|
|
94
|
+
"#define MT_ASYNC_MAGIC UINT64_C(0x4D5441464D454D00)",
|
|
95
|
+
"",
|
|
96
|
+
"static void* mt_async_alloc(uintptr_t size) {",
|
|
97
|
+
"#{INDENT}char* raw = (char*) calloc(1, (size_t)(MT_ASYNC_HEADER_SIZE + size));",
|
|
98
|
+
"#{INDENT}if (raw == NULL) {",
|
|
99
|
+
"#{INDENT * 2}abort();",
|
|
100
|
+
"#{INDENT}}",
|
|
101
|
+
"#{INDENT}*(uint64_t*)raw = MT_ASYNC_MAGIC;",
|
|
102
|
+
"#{INDENT}*(uintptr_t*)(raw + sizeof(uint64_t)) = 1;",
|
|
103
|
+
"#{INDENT}return raw + MT_ASYNC_HEADER_SIZE;",
|
|
104
|
+
"}",
|
|
105
|
+
"",
|
|
106
|
+
"static void mt_async_retain(void* frame) {",
|
|
107
|
+
"#{INDENT}char* raw = (char*)frame - MT_ASYNC_HEADER_SIZE;",
|
|
108
|
+
"#{INDENT}if (*(uint64_t*)raw != MT_ASYNC_MAGIC) return;",
|
|
109
|
+
"#{INDENT}uintptr_t* ref = (uintptr_t*)(raw + sizeof(uint64_t));",
|
|
110
|
+
"#{INDENT}(*ref)++;",
|
|
111
|
+
"}",
|
|
112
|
+
"",
|
|
113
|
+
"static void mt_async_free(void* frame) {",
|
|
114
|
+
"#{INDENT}char* raw = (char*)frame - MT_ASYNC_HEADER_SIZE;",
|
|
115
|
+
"#{INDENT}if (*(uint64_t*)raw != MT_ASYNC_MAGIC) return;",
|
|
116
|
+
"#{INDENT}uintptr_t* ref = (uintptr_t*)(raw + sizeof(uint64_t));",
|
|
117
|
+
"#{INDENT}if (--(*ref) == 0) {",
|
|
118
|
+
"#{INDENT * 2}free(raw);",
|
|
119
|
+
"#{INDENT}}",
|
|
120
|
+
"}",
|
|
121
|
+
]
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def emit_parallel_for_helper
|
|
125
|
+
[
|
|
126
|
+
"typedef struct {",
|
|
127
|
+
"#{INDENT}void (*work)(void* data, int64_t start, int64_t end);",
|
|
128
|
+
"#{INDENT}void* data;",
|
|
129
|
+
"#{INDENT}int64_t start;",
|
|
130
|
+
"#{INDENT}int64_t end;",
|
|
131
|
+
"} mt_pfor_chunk;",
|
|
132
|
+
"",
|
|
133
|
+
"static void mt_pfor_runner(void* arg) {",
|
|
134
|
+
"#{INDENT}mt_pfor_chunk* chunk = (mt_pfor_chunk*)arg;",
|
|
135
|
+
"#{INDENT}chunk->work(chunk->data, chunk->start, chunk->end);",
|
|
136
|
+
"}",
|
|
137
|
+
"",
|
|
138
|
+
"static void mt_parallel_for(void (*work)(void* data, int64_t start, int64_t end), void* data, int64_t count) {",
|
|
139
|
+
"#{INDENT}if (count <= 0) return;",
|
|
140
|
+
"#{INDENT}uv_cpu_info_t* cpu_info;",
|
|
141
|
+
"#{INDENT}int ncpu = 1;",
|
|
142
|
+
"#{INDENT}if (uv_cpu_info(&cpu_info, &ncpu) == 0) {",
|
|
143
|
+
"#{INDENT * 2}uv_free_cpu_info(cpu_info, ncpu);",
|
|
144
|
+
"#{INDENT}}",
|
|
145
|
+
"#{INDENT}if (ncpu < 1) ncpu = 1;",
|
|
146
|
+
"#{INDENT}if (ncpu > 64) ncpu = 64;",
|
|
147
|
+
"#{INDENT}if (count < (int64_t)ncpu) ncpu = (int)count;",
|
|
148
|
+
"#{INDENT}int64_t chunk_size = (count + ncpu - 1) / ncpu;",
|
|
149
|
+
"#{INDENT}mt_pfor_chunk chunks[64];",
|
|
150
|
+
"#{INDENT}uv_thread_t threads[64];",
|
|
151
|
+
"#{INDENT}int nworkers = 0;",
|
|
152
|
+
"#{INDENT}for (int t = 1; t < ncpu; t++) {",
|
|
153
|
+
"#{INDENT * 2}int64_t s = t * chunk_size;",
|
|
154
|
+
"#{INDENT * 2}int64_t e = s + chunk_size;",
|
|
155
|
+
"#{INDENT * 2}if (e > count) e = count;",
|
|
156
|
+
"#{INDENT * 2}if (s >= count) break;",
|
|
157
|
+
"#{INDENT * 2}chunks[nworkers].work = work;",
|
|
158
|
+
"#{INDENT * 2}chunks[nworkers].data = data;",
|
|
159
|
+
"#{INDENT * 2}chunks[nworkers].start = s;",
|
|
160
|
+
"#{INDENT * 2}chunks[nworkers].end = e;",
|
|
161
|
+
"#{INDENT * 2}uv_thread_create(&threads[nworkers], mt_pfor_runner, &chunks[nworkers]);",
|
|
162
|
+
"#{INDENT * 2}nworkers++;",
|
|
163
|
+
"#{INDENT}}",
|
|
164
|
+
"#{INDENT}int64_t first_end = chunk_size < count ? chunk_size : count;",
|
|
165
|
+
"#{INDENT}work(data, 0, first_end);",
|
|
166
|
+
"#{INDENT}for (int t = 0; t < nworkers; t++) {",
|
|
167
|
+
"#{INDENT * 2}uv_thread_join(&threads[t]);",
|
|
168
|
+
"#{INDENT}}",
|
|
169
|
+
"}",
|
|
170
|
+
]
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def emit_spawn_all_helper
|
|
174
|
+
[
|
|
175
|
+
"typedef struct {",
|
|
176
|
+
"#{INDENT}void (*work)(void* data);",
|
|
177
|
+
"#{INDENT}void* data;",
|
|
178
|
+
"} mt_spawn_item;",
|
|
179
|
+
"",
|
|
180
|
+
"static void mt_spawn_item_runner(void* arg) {",
|
|
181
|
+
"#{INDENT}mt_spawn_item* item = (mt_spawn_item*)arg;",
|
|
182
|
+
"#{INDENT}item->work(item->data);",
|
|
183
|
+
"}",
|
|
184
|
+
"",
|
|
185
|
+
"static void mt_spawn_all(mt_spawn_item* items, int count) {",
|
|
186
|
+
"#{INDENT}if (count <= 0) return;",
|
|
187
|
+
"#{INDENT}uv_thread_t threads[64];",
|
|
188
|
+
"#{INDENT}int nworkers = 0;",
|
|
189
|
+
"#{INDENT}for (int t = 1; t < count && nworkers < 63; t++) {",
|
|
190
|
+
"#{INDENT * 2}uv_thread_create(&threads[nworkers], mt_spawn_item_runner, &items[t]);",
|
|
191
|
+
"#{INDENT * 2}nworkers++;",
|
|
192
|
+
"#{INDENT}}",
|
|
193
|
+
"#{INDENT}items[0].work(items[0].data);",
|
|
194
|
+
"#{INDENT}for (int t = 0; t < nworkers; t++) {",
|
|
195
|
+
"#{INDENT * 2}uv_thread_join(&threads[t]);",
|
|
196
|
+
"#{INDENT}}",
|
|
197
|
+
"}",
|
|
198
|
+
]
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def emit_detach_helpers
|
|
202
|
+
[
|
|
203
|
+
"typedef struct {",
|
|
204
|
+
"#{INDENT}uv_thread_t thread;",
|
|
205
|
+
"} mt_detach_handle;",
|
|
206
|
+
"",
|
|
207
|
+
"static void* mt_detach_run(void (*work)(void*), void* cap) {",
|
|
208
|
+
"#{INDENT}mt_detach_handle* h = (mt_detach_handle*)malloc(sizeof(mt_detach_handle));",
|
|
209
|
+
"#{INDENT}uv_thread_create(&h->thread, work, cap);",
|
|
210
|
+
"#{INDENT}return h;",
|
|
211
|
+
"}",
|
|
212
|
+
"",
|
|
213
|
+
"static void mt_detach_join(void* handle) {",
|
|
214
|
+
"#{INDENT}if (!handle) return;",
|
|
215
|
+
"#{INDENT}mt_detach_handle* h = (mt_detach_handle*)handle;",
|
|
216
|
+
"#{INDENT}uv_thread_join(&h->thread);",
|
|
217
|
+
"#{INDENT}free(h);",
|
|
218
|
+
"}",
|
|
219
|
+
]
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def emit_foreign_temp_cstr_helpers
|
|
223
|
+
lines = []
|
|
224
|
+
|
|
225
|
+
if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_foreign_str_to_cstr_temp]) }
|
|
226
|
+
lines.concat([
|
|
227
|
+
"static const char* mt_foreign_str_to_cstr_temp(mt_str value) {",
|
|
228
|
+
"#{INDENT}char* data = (char*)malloc(value.len + 1);",
|
|
229
|
+
"#{INDENT}uintptr_t index = 0;",
|
|
230
|
+
"#{INDENT}if (data == NULL) mt_fatal(\"foreign str temporary allocation failed\");",
|
|
231
|
+
"#{INDENT}while (index < value.len) {",
|
|
232
|
+
"#{INDENT * 2}data[index] = value.data[index];",
|
|
233
|
+
"#{INDENT * 2}index++;",
|
|
234
|
+
"#{INDENT}}",
|
|
235
|
+
"#{INDENT}data[value.len] = '\\0';",
|
|
236
|
+
"#{INDENT}return data;",
|
|
237
|
+
"}",
|
|
238
|
+
])
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_free_foreign_cstr_temp]) }
|
|
242
|
+
lines << "" unless lines.empty?
|
|
243
|
+
lines.concat([
|
|
244
|
+
"static void mt_free_foreign_cstr_temp(const char* value) {",
|
|
245
|
+
"#{INDENT}free((void*)value);",
|
|
246
|
+
"}",
|
|
247
|
+
])
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_foreign_strs_to_cstrs_temp]) }
|
|
251
|
+
lines << "" unless lines.empty?
|
|
252
|
+
lines.concat([
|
|
253
|
+
"static void mt_foreign_strs_to_cstrs_temp(mt_span_str values, char*** items_out, char** data_out, uintptr_t* len_out) {",
|
|
254
|
+
"#{INDENT}uintptr_t total_bytes = 0;",
|
|
255
|
+
"#{INDENT}uintptr_t index = 0;",
|
|
256
|
+
"#{INDENT}uintptr_t offset = 0;",
|
|
257
|
+
"#{INDENT}char** items = NULL;",
|
|
258
|
+
"#{INDENT}char* data = NULL;",
|
|
259
|
+
"#{INDENT}while (index < values.len) {",
|
|
260
|
+
"#{INDENT * 2}total_bytes += values.data[index].len + 1;",
|
|
261
|
+
"#{INDENT * 2}index++;",
|
|
262
|
+
"#{INDENT}}",
|
|
263
|
+
"#{INDENT}if (values.len > 0) {",
|
|
264
|
+
"#{INDENT * 2}items = (char**)malloc(values.len * sizeof(char*));",
|
|
265
|
+
"#{INDENT * 2}if (items == NULL) mt_fatal(\"foreign string-list temporary allocation failed\");",
|
|
266
|
+
"#{INDENT}}",
|
|
267
|
+
"#{INDENT}if (total_bytes > 0) {",
|
|
268
|
+
"#{INDENT * 2}data = (char*)malloc(total_bytes);",
|
|
269
|
+
"#{INDENT * 2}if (data == NULL) {",
|
|
270
|
+
"#{INDENT * 3}free(items);",
|
|
271
|
+
"#{INDENT * 3}mt_fatal(\"foreign string-list temporary allocation failed\");",
|
|
272
|
+
"#{INDENT * 2}}",
|
|
273
|
+
"#{INDENT}}",
|
|
274
|
+
"#{INDENT}index = 0;",
|
|
275
|
+
"#{INDENT}while (index < values.len) {",
|
|
276
|
+
"#{INDENT * 2}mt_str value = values.data[index];",
|
|
277
|
+
"#{INDENT * 2}uintptr_t byte_index = 0;",
|
|
278
|
+
"#{INDENT * 2}items[index] = data + offset;",
|
|
279
|
+
"#{INDENT * 2}while (byte_index < value.len) {",
|
|
280
|
+
"#{INDENT * 3}data[offset + byte_index] = value.data[byte_index];",
|
|
281
|
+
"#{INDENT * 3}byte_index++;",
|
|
282
|
+
"#{INDENT * 2}}",
|
|
283
|
+
"#{INDENT * 2}data[offset + value.len] = '\\0';",
|
|
284
|
+
"#{INDENT * 2}offset += value.len + 1;",
|
|
285
|
+
"#{INDENT * 2}index++;",
|
|
286
|
+
"#{INDENT}}",
|
|
287
|
+
"#{INDENT}*items_out = items;",
|
|
288
|
+
"#{INDENT}*data_out = data;",
|
|
289
|
+
"#{INDENT}*len_out = values.len;",
|
|
290
|
+
"}",
|
|
291
|
+
])
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_free_foreign_cstrs_temp]) }
|
|
295
|
+
lines << "" unless lines.empty?
|
|
296
|
+
lines.concat([
|
|
297
|
+
"static void mt_free_foreign_cstrs_temp(char** items, char* data) {",
|
|
298
|
+
"#{INDENT}free(items);",
|
|
299
|
+
"#{INDENT}free(data);",
|
|
300
|
+
"}",
|
|
301
|
+
])
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
lines
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def emit_entrypoint_argv_helpers
|
|
308
|
+
lines = []
|
|
309
|
+
|
|
310
|
+
if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_entry_argv_to_span_str]) }
|
|
311
|
+
lines.concat([
|
|
312
|
+
"static mt_span_str mt_entry_argv_to_span_str(int32_t argc, char** argv, mt_str** items_out) {",
|
|
313
|
+
"#{INDENT}uintptr_t len = argc > 0 ? (uintptr_t)argc : 0;",
|
|
314
|
+
"#{INDENT}mt_str* items = NULL;",
|
|
315
|
+
"#{INDENT}uintptr_t index = 0;",
|
|
316
|
+
"#{INDENT}if (len > 0) {",
|
|
317
|
+
"#{INDENT * 2}items = (mt_str*)malloc(len * sizeof(mt_str));",
|
|
318
|
+
"#{INDENT * 2}if (items == NULL) abort();",
|
|
319
|
+
"#{INDENT}}",
|
|
320
|
+
"#{INDENT}while (index < len) {",
|
|
321
|
+
"#{INDENT * 2}char* value = argv[index];",
|
|
322
|
+
"#{INDENT * 2}items[index] = (mt_str){ .data = value, .len = (uintptr_t)strlen(value) };",
|
|
323
|
+
"#{INDENT * 2}index++;",
|
|
324
|
+
"#{INDENT}}",
|
|
325
|
+
"#{INDENT}*items_out = items;",
|
|
326
|
+
"#{INDENT}return (mt_span_str){ .data = items, .len = len };",
|
|
327
|
+
"}",
|
|
328
|
+
])
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_free_entry_argv_strs]) }
|
|
332
|
+
lines << "" unless lines.empty?
|
|
333
|
+
lines.concat([
|
|
334
|
+
"static void mt_free_entry_argv_strs(mt_str* items) {",
|
|
335
|
+
"#{INDENT}free(items);",
|
|
336
|
+
"}",
|
|
337
|
+
])
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
lines
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def emit_text_buffer_helpers
|
|
344
|
+
[
|
|
345
|
+
"static bool mt_is_utf8_continuation_byte(unsigned char byte) {",
|
|
346
|
+
"#{INDENT}return (byte & 0xC0u) == 0x80u;",
|
|
347
|
+
"}",
|
|
348
|
+
"",
|
|
349
|
+
"static bool mt_is_valid_utf8(const char* data, uintptr_t len) {",
|
|
350
|
+
"#{INDENT}uintptr_t index = 0;",
|
|
351
|
+
"#{INDENT}while (index < len) {",
|
|
352
|
+
"#{INDENT * 2}unsigned char lead = (unsigned char) data[index];",
|
|
353
|
+
"#{INDENT * 2}if (lead < 0x80u) {",
|
|
354
|
+
"#{INDENT * 3}index++;",
|
|
355
|
+
"#{INDENT * 3}continue;",
|
|
356
|
+
"#{INDENT * 2}}",
|
|
357
|
+
"#{INDENT * 2}if (lead < 0xC2u) return false;",
|
|
358
|
+
"#{INDENT * 2}if (lead < 0xE0u) {",
|
|
359
|
+
"#{INDENT * 3}if (index + 1 >= len) return false;",
|
|
360
|
+
"#{INDENT * 3}unsigned char byte1 = (unsigned char) data[index + 1];",
|
|
361
|
+
"#{INDENT * 3}if (!mt_is_utf8_continuation_byte(byte1)) return false;",
|
|
362
|
+
"#{INDENT * 3}index += 2;",
|
|
363
|
+
"#{INDENT * 3}continue;",
|
|
364
|
+
"#{INDENT * 2}}",
|
|
365
|
+
"#{INDENT * 2}if (lead < 0xF0u) {",
|
|
366
|
+
"#{INDENT * 3}if (index + 2 >= len) return false;",
|
|
367
|
+
"#{INDENT * 3}unsigned char byte1 = (unsigned char) data[index + 1];",
|
|
368
|
+
"#{INDENT * 3}unsigned char byte2 = (unsigned char) data[index + 2];",
|
|
369
|
+
"#{INDENT * 3}if (lead == 0xE0u) {",
|
|
370
|
+
"#{INDENT * 4}if (byte1 < 0xA0u || byte1 > 0xBFu) return false;",
|
|
371
|
+
"#{INDENT * 3}} else if (lead == 0xEDu) {",
|
|
372
|
+
"#{INDENT * 4}if (byte1 < 0x80u || byte1 > 0x9Fu) return false;",
|
|
373
|
+
"#{INDENT * 3}} else if (!mt_is_utf8_continuation_byte(byte1)) {",
|
|
374
|
+
"#{INDENT * 4}return false;",
|
|
375
|
+
"#{INDENT * 3}}",
|
|
376
|
+
"#{INDENT * 3}if (!mt_is_utf8_continuation_byte(byte2)) return false;",
|
|
377
|
+
"#{INDENT * 3}index += 3;",
|
|
378
|
+
"#{INDENT * 3}continue;",
|
|
379
|
+
"#{INDENT * 2}}",
|
|
380
|
+
"#{INDENT * 2}if (lead < 0xF5u) {",
|
|
381
|
+
"#{INDENT * 3}if (index + 3 >= len) return false;",
|
|
382
|
+
"#{INDENT * 3}unsigned char byte1 = (unsigned char) data[index + 1];",
|
|
383
|
+
"#{INDENT * 3}unsigned char byte2 = (unsigned char) data[index + 2];",
|
|
384
|
+
"#{INDENT * 3}unsigned char byte3 = (unsigned char) data[index + 3];",
|
|
385
|
+
"#{INDENT * 3}if (lead == 0xF0u) {",
|
|
386
|
+
"#{INDENT * 4}if (byte1 < 0x90u || byte1 > 0xBFu) return false;",
|
|
387
|
+
"#{INDENT * 3}} else if (lead == 0xF4u) {",
|
|
388
|
+
"#{INDENT * 4}if (byte1 < 0x80u || byte1 > 0x8Fu) return false;",
|
|
389
|
+
"#{INDENT * 3}} else if (!mt_is_utf8_continuation_byte(byte1)) {",
|
|
390
|
+
"#{INDENT * 4}return false;",
|
|
391
|
+
"#{INDENT * 3}}",
|
|
392
|
+
"#{INDENT * 3}if (!mt_is_utf8_continuation_byte(byte2) || !mt_is_utf8_continuation_byte(byte3)) return false;",
|
|
393
|
+
"#{INDENT * 3}index += 4;",
|
|
394
|
+
"#{INDENT * 3}continue;",
|
|
395
|
+
"#{INDENT * 2}}",
|
|
396
|
+
"#{INDENT * 2}return false;",
|
|
397
|
+
"#{INDENT}}",
|
|
398
|
+
"#{INDENT}return true;",
|
|
399
|
+
"}",
|
|
400
|
+
]
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
def emit_str_buffer_helpers
|
|
404
|
+
lines = []
|
|
405
|
+
|
|
406
|
+
if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_len mt_str_buffer_as_cstr mt_str_buffer_append]) }
|
|
407
|
+
lines.concat([
|
|
408
|
+
"static uintptr_t mt_str_buffer_len(char* data, uintptr_t cap, uintptr_t* len, bool* dirty) {",
|
|
409
|
+
"#{INDENT}if (*dirty) {",
|
|
410
|
+
"#{INDENT * 2}uintptr_t current = 0;",
|
|
411
|
+
"#{INDENT * 2}while (current < cap + 1 && data[current] != '\\0') {",
|
|
412
|
+
"#{INDENT * 3}current++;",
|
|
413
|
+
"#{INDENT * 2}}",
|
|
414
|
+
"#{INDENT * 2}if (current > cap) mt_fatal(\"str_buffer text requires a trailing NUL within capacity\");",
|
|
415
|
+
"#{INDENT * 2}if (!mt_is_valid_utf8(data, current)) mt_fatal(\"str_buffer text must be valid UTF-8\");",
|
|
416
|
+
"#{INDENT * 2}*len = current;",
|
|
417
|
+
"#{INDENT * 2}*dirty = false;",
|
|
418
|
+
"#{INDENT}}",
|
|
419
|
+
"#{INDENT}return *len;",
|
|
420
|
+
"}",
|
|
421
|
+
])
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_as_cstr]) }
|
|
425
|
+
lines << "" unless lines.empty?
|
|
426
|
+
lines.concat([
|
|
427
|
+
"static const char* mt_str_buffer_as_cstr(char* data, uintptr_t cap, uintptr_t* len, bool* dirty) {",
|
|
428
|
+
"#{INDENT}(void)mt_str_buffer_len(data, cap, len, dirty);",
|
|
429
|
+
"#{INDENT}return data;",
|
|
430
|
+
"}",
|
|
431
|
+
])
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_clear]) }
|
|
435
|
+
lines << "" unless lines.empty?
|
|
436
|
+
lines.concat([
|
|
437
|
+
"static void mt_str_buffer_clear(char* data, uintptr_t cap, uintptr_t* len, bool* dirty) {",
|
|
438
|
+
"#{INDENT}memset(data, 0, cap + 1);",
|
|
439
|
+
"#{INDENT}*len = 0;",
|
|
440
|
+
"#{INDENT}*dirty = false;",
|
|
441
|
+
"}",
|
|
442
|
+
])
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_assign]) }
|
|
446
|
+
lines << "" unless lines.empty?
|
|
447
|
+
lines.concat([
|
|
448
|
+
"static void mt_str_buffer_assign(mt_str value, char* data, uintptr_t cap, uintptr_t* len, bool* dirty) {",
|
|
449
|
+
"#{INDENT}if (value.len > cap) mt_fatal(\"str_buffer.assign exceeds capacity\");",
|
|
450
|
+
"#{INDENT}memcpy(data, value.data, value.len);",
|
|
451
|
+
"#{INDENT}data[value.len] = '\\0';",
|
|
452
|
+
"#{INDENT}if (value.len < cap + 1) memset(data + value.len + 1, 0, cap - value.len);",
|
|
453
|
+
"#{INDENT}*len = value.len;",
|
|
454
|
+
"#{INDENT}*dirty = false;",
|
|
455
|
+
"}",
|
|
456
|
+
])
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_append]) }
|
|
460
|
+
lines << "" unless lines.empty?
|
|
461
|
+
lines.concat([
|
|
462
|
+
"static void mt_str_buffer_append(mt_str value, char* data, uintptr_t cap, uintptr_t* len, bool* dirty) {",
|
|
463
|
+
"#{INDENT}uintptr_t current = mt_str_buffer_len(data, cap, len, dirty);",
|
|
464
|
+
"#{INDENT}if (value.len > cap - current) mt_fatal(\"str_buffer.append exceeds capacity\");",
|
|
465
|
+
"#{INDENT}memcpy(data + current, value.data, value.len);",
|
|
466
|
+
"#{INDENT}current += value.len;",
|
|
467
|
+
"#{INDENT}data[current] = '\\0';",
|
|
468
|
+
"#{INDENT}*len = current;",
|
|
469
|
+
"#{INDENT}*dirty = false;",
|
|
470
|
+
"}",
|
|
471
|
+
])
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_prepare_write]) }
|
|
475
|
+
lines << "" unless lines.empty?
|
|
476
|
+
lines.concat([
|
|
477
|
+
"static char* mt_str_buffer_prepare_write(char* data, uintptr_t cap, bool* dirty) {",
|
|
478
|
+
"#{INDENT}data[cap] = '\\0';",
|
|
479
|
+
"#{INDENT}*dirty = true;",
|
|
480
|
+
"#{INDENT}return data;",
|
|
481
|
+
"}",
|
|
482
|
+
])
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
lines
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
def emit_checked_array_index_helper(type)
|
|
489
|
+
helper_name = checked_array_index_helper_name(type)
|
|
490
|
+
params = [c_declaration(type, '(*array)'), c_declaration(Types::Registry.primitive('ptr_uint'), 'index')].join(', ')
|
|
491
|
+
[
|
|
492
|
+
"static inline #{c_function_declaration(pointer_to(array_element_type(type)), helper_name, params)} {",
|
|
493
|
+
"#{INDENT}if (index >= #{array_length(type)}) mt_fatal(\"array index out of bounds\");",
|
|
494
|
+
"#{INDENT}return &(*array)[index];",
|
|
495
|
+
"}",
|
|
496
|
+
]
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
def emit_checked_span_index_helper(type)
|
|
500
|
+
helper_name = checked_span_index_helper_name(type)
|
|
501
|
+
params = [c_declaration(type, 'span'), c_declaration(Types::Registry.primitive('ptr_uint'), 'index')].join(', ')
|
|
502
|
+
[
|
|
503
|
+
"static inline #{c_function_declaration(pointer_to(type.element_type), helper_name, params)} {",
|
|
504
|
+
"#{INDENT}if (index >= span.len) mt_fatal(\"span index out of bounds\");",
|
|
505
|
+
"#{INDENT}return &span.data[index];",
|
|
506
|
+
"}",
|
|
507
|
+
]
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
def emit_nullable_array_index_helper(type)
|
|
511
|
+
helper_name = nullable_array_index_helper_name(type)
|
|
512
|
+
params = [c_declaration(type, '(*array)'), c_declaration(Types::Registry.primitive('ptr_uint'), 'index')].join(', ')
|
|
513
|
+
[
|
|
514
|
+
"static inline #{c_function_declaration(pointer_to(array_element_type(type)), helper_name, params)} {",
|
|
515
|
+
"#{INDENT}if (index >= #{array_length(type)}) return NULL;",
|
|
516
|
+
"#{INDENT}return &(*array)[index];",
|
|
517
|
+
"}",
|
|
518
|
+
]
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
def emit_nullable_span_index_helper(type)
|
|
522
|
+
helper_name = nullable_span_index_helper_name(type)
|
|
523
|
+
params = [c_declaration(type, 'span'), c_declaration(Types::Registry.primitive('ptr_uint'), 'index')].join(', ')
|
|
524
|
+
[
|
|
525
|
+
"static inline #{c_function_declaration(pointer_to(type.element_type), helper_name, params)} {",
|
|
526
|
+
"#{INDENT}if (index >= span.len) return NULL;",
|
|
527
|
+
"#{INDENT}return &span.data[index];",
|
|
528
|
+
"}",
|
|
529
|
+
]
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
def emit_str_literal_constants(literals)
|
|
533
|
+
literals.each_with_index.map do |value, i|
|
|
534
|
+
"static const mt_str #{str_literal_name(i)} = { .data = #{value.inspect}, .len = #{value.bytesize} };"
|
|
535
|
+
end
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
def str_literal_name(index)
|
|
539
|
+
"mt_str_lit_#{index}"
|
|
540
|
+
end
|
|
536
541
|
end
|
|
537
542
|
end
|
|
538
543
|
end
|