@stacksjs/zig-dtsx 0.9.12 → 0.9.14

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.
@@ -29,22 +29,17 @@ pub fn extractLeadingComments(s: *Scanner, decl_start: usize) ?[]const []const u
29
29
  const pu: usize = @intCast(p);
30
30
  // Check for block comment ending with */
31
31
  if (p >= 1 and s.source[pu] == ch.CH_SLASH and s.source[pu - 1] == ch.CH_STAR) {
32
- // Find matching /* or /**
33
- var start: isize = p - 2;
34
- while (start >= 1) {
35
- const su: usize = @intCast(start);
36
- if (s.source[su] == ch.CH_SLASH and s.source[su + 1] == ch.CH_STAR) break;
37
- start -= 1;
38
- }
39
- if (start >= 0) {
40
- const su: usize = @intCast(start);
41
- if (s.source[su] == ch.CH_SLASH and s.source[su + 1] == ch.CH_STAR) {
42
- comments.append(s.source[su .. pu + 1]) catch {};
43
- has_block_comment = true;
44
- p = start - 1;
45
- while (p >= 0 and ch.isWhitespace(s.source[@intCast(p)])) p -= 1;
46
- continue;
47
- }
32
+ // Find matching `/*` opener — std.mem.lastIndexOf walks bytes via
33
+ // the optimized scanner path. Was a manual byte-by-byte loop walking
34
+ // backwards, which is O(N) for long block comments.
35
+ const search_end = pu - 1;
36
+ const found = std.mem.lastIndexOf(u8, s.source[0..search_end], "/*");
37
+ if (found) |su| {
38
+ comments.append(s.source[su .. pu + 1]) catch {};
39
+ has_block_comment = true;
40
+ p = @as(isize, @intCast(su)) - 1;
41
+ while (p >= 0 and ch.isWhitespace(s.source[@intCast(p)])) p -= 1;
42
+ continue;
48
43
  }
49
44
  break;
50
45
  }
@@ -78,19 +73,23 @@ pub fn extractLeadingComments(s: *Scanner, decl_start: usize) ?[]const []const u
78
73
  }
79
74
  }
80
75
 
76
+ // Single-line short-circuit: skip reverse + join when there's only one comment.
77
+ if (single_lines.items.len == 1) {
78
+ comments.append(single_lines.items[0]) catch {};
79
+ continue;
80
+ }
81
+
81
82
  // Reverse and join with newlines
82
83
  std.mem.reverse([]const u8, single_lines.items);
83
- var total_len: usize = 0;
84
- for (single_lines.items, 0..) |line, i| {
85
- total_len += line.len;
86
- if (i < single_lines.items.len - 1) total_len += 1;
87
- }
84
+ var total_len: usize = single_lines.items.len - 1; // newlines between
85
+ for (single_lines.items) |line| total_len += line.len;
88
86
  const joined = s.allocator.alloc(u8, total_len) catch break;
89
87
  var offset: usize = 0;
88
+ const last_idx = single_lines.items.len - 1;
90
89
  for (single_lines.items, 0..) |line, i| {
91
90
  @memcpy(joined[offset .. offset + line.len], line);
92
91
  offset += line.len;
93
- if (i < single_lines.items.len - 1) {
92
+ if (i < last_idx) {
94
93
  joined[offset] = '\n';
95
94
  offset += 1;
96
95
  }
@@ -151,7 +150,8 @@ pub fn extractImport(s: *Scanner, start: usize) Declaration {
151
150
  }
152
151
  }
153
152
 
154
- // Extract source module
153
+ // Extract source module — use indexOfChar (single-byte SIMD scan) for the
154
+ // closing quote instead of indexOf with a 1-char needle.
155
155
  var module_src: []const u8 = "";
156
156
  {
157
157
  const from_idx = ch.indexOf(text, "from ", 0);
@@ -161,9 +161,7 @@ pub fn extractImport(s: *Scanner, start: usize) Declaration {
161
161
  if (mi < text.len) {
162
162
  const q = text[mi];
163
163
  if (q == ch.CH_SQUOTE or q == ch.CH_DQUOTE) {
164
- const q_str: []const u8 = if (q == ch.CH_SQUOTE) "'" else "\"";
165
- const end_idx = ch.indexOf(text, q_str, mi + 1);
166
- if (end_idx) |ei| {
164
+ if (ch.indexOfChar(text, q, mi + 1)) |ei| {
167
165
  module_src = text[mi + 1 .. ei];
168
166
  }
169
167
  }
@@ -173,9 +171,7 @@ pub fn extractImport(s: *Scanner, start: usize) Declaration {
173
171
  while (mi < text.len and text[mi] != ch.CH_SQUOTE and text[mi] != ch.CH_DQUOTE) mi += 1;
174
172
  if (mi < text.len) {
175
173
  const q = text[mi];
176
- const q_str: []const u8 = if (q == ch.CH_SQUOTE) "'" else "\"";
177
- const end_idx = ch.indexOf(text, q_str, mi + 1);
178
- if (end_idx) |ei| {
174
+ if (ch.indexOfChar(text, q, mi + 1)) |ei| {
179
175
  module_src = text[mi + 1 .. ei];
180
176
  }
181
177
  }
@@ -240,9 +236,9 @@ pub fn extractImport(s: *Scanner, start: usize) Declaration {
240
236
  }
241
237
  }
242
238
 
243
- // Named imports
239
+ // Named imports — splitScalar is faster than splitSequence for a 1-byte separator.
244
240
  const named_part = import_part[bs + 1 .. be];
245
- var iter = std.mem.splitSequence(u8, named_part, ",");
241
+ var iter = std.mem.splitScalar(u8, named_part, ',');
246
242
  while (iter.next()) |raw_item| {
247
243
  const trimmed = ch.sliceTrimmed(raw_item, 0, raw_item.len);
248
244
  if (trimmed.len == 0) continue;
@@ -266,14 +262,15 @@ pub fn extractImport(s: *Scanner, start: usize) Declaration {
266
262
  }
267
263
  }
268
264
 
265
+ // toOwnedSlice trims unused capacity — important for non-arena callers.
269
266
  parsed_import = .{
270
267
  .default_name = default_name,
271
- .named_items = named_items.items,
268
+ .named_items = named_items.toOwnedSlice() catch named_items.items,
272
269
  .source = module_src,
273
270
  .is_type_only = is_type_only,
274
271
  .is_namespace = is_namespace,
275
272
  .namespace_name = namespace_name,
276
- .resolved_items = resolved_items.items,
273
+ .resolved_items = resolved_items.toOwnedSlice() catch resolved_items.items,
277
274
  };
278
275
  }
279
276
 
@@ -302,8 +299,9 @@ pub fn extractGenerics(s: *Scanner) []const u8 {
302
299
  const start = s.pos;
303
300
  _ = s.findMatchingClose(ch.CH_LANGLE, ch.CH_RANGLE);
304
301
  const raw = s.source[start..s.pos];
305
- // Normalize multi-line generics to single line
306
- if (ch.indexOf(raw, "\n", 0) != null) {
302
+ // Normalize multi-line generics to single line. indexOfChar is the
303
+ // single-byte SIMD path; the previous code used the multi-byte indexOf.
304
+ if (ch.indexOfChar(raw, '\n', 0) != null) {
307
305
  // Direct alloc: output ≤ input length (whitespace collapsed)
308
306
  const buf = s.allocator.alloc(u8, raw.len) catch return raw;
309
307
  var pos: usize = 0;
@@ -395,15 +393,18 @@ fn endsWithWord(text: []const u8, word: []const u8) bool {
395
393
 
396
394
  /// Check if string is a numeric literal
397
395
  pub fn isNumericLiteral(v: []const u8) bool {
396
+ if (v.len == 0) return false;
398
397
  var i: usize = 0;
399
- if (i < v.len and v[i] == '-') i += 1;
400
- if (i >= v.len) return false;
401
- if (v[i] < '0' or v[i] > '9') return false;
402
- while (i < v.len and v[i] >= '0' and v[i] <= '9') i += 1;
398
+ if (v[0] == '-') {
399
+ i = 1;
400
+ if (i >= v.len) return false;
401
+ }
402
+ if (!ch.isDigit(v[i])) return false;
403
+ while (i < v.len and ch.isDigit(v[i])) i += 1;
403
404
  if (i < v.len and v[i] == '.') {
404
405
  i += 1;
405
- if (i >= v.len or v[i] < '0' or v[i] > '9') return false;
406
- while (i < v.len and v[i] >= '0' and v[i] <= '9') i += 1;
406
+ if (i >= v.len or !ch.isDigit(v[i])) return false;
407
+ while (i < v.len and ch.isDigit(v[i])) i += 1;
407
408
  }
408
409
  return i == v.len;
409
410
  }
@@ -411,27 +412,40 @@ pub fn isNumericLiteral(v: []const u8) bool {
411
412
  /// Infer type from a default value expression (simple cases)
412
413
  pub fn inferTypeFromDefault(value: []const u8) []const u8 {
413
414
  const v = std.mem.trim(u8, value, " \t\r\n");
414
- if (std.mem.eql(u8, v, "true") or std.mem.eql(u8, v, "false")) return "boolean";
415
- if (isNumericLiteral(v)) return "number";
416
- if (v.len >= 2 and ((v[0] == '\'' and v[v.len - 1] == '\'') or (v[0] == '"' and v[v.len - 1] == '"'))) return "string";
417
- if (v.len > 0 and v[0] == '[') return "unknown[]";
418
- if (v.len > 0 and v[0] == '{') return "Record<string, unknown>";
419
- return "unknown";
415
+ if (v.len == 0) return "unknown";
416
+ // Dispatch on first byte to avoid running multiple checks for cases that
417
+ // can't possibly match. e.g. a value starting with `[` is never `true`.
418
+ return switch (v[0]) {
419
+ 't' => if (std.mem.eql(u8, v, "true")) "boolean" else "unknown",
420
+ 'f' => if (std.mem.eql(u8, v, "false")) "boolean" else "unknown",
421
+ '[' => "unknown[]",
422
+ '{' => "Record<string, unknown>",
423
+ '\'', '"' => if (v.len >= 2 and v[v.len - 1] == v[0]) "string" else "unknown",
424
+ '-', '0'...'9' => if (isNumericLiteral(v)) "number" else "unknown",
425
+ else => "unknown",
426
+ };
420
427
  }
421
428
 
422
429
  /// Infer literal type from initializer value (for const-like / static readonly)
423
430
  pub fn inferLiteralType(value: []const u8) []const u8 {
424
431
  const v = std.mem.trim(u8, value, " \t\r\n");
425
- if (std.mem.eql(u8, v, "true") or std.mem.eql(u8, v, "false")) return v;
426
- if (isNumericLiteral(v)) return v;
427
- if (v.len >= 2 and ((v[0] == '\'' and v[v.len - 1] == '\'') or (v[0] == '"' and v[v.len - 1] == '"'))) return v;
428
- return "unknown";
432
+ if (v.len == 0) return "unknown";
433
+ return switch (v[0]) {
434
+ 't' => if (std.mem.eql(u8, v, "true")) v else "unknown",
435
+ 'f' => if (std.mem.eql(u8, v, "false")) v else "unknown",
436
+ '\'', '"' => if (v.len >= 2 and v[v.len - 1] == v[0]) v else "unknown",
437
+ '-', '0'...'9' => if (isNumericLiteral(v)) v else "unknown",
438
+ else => "unknown",
439
+ };
429
440
  }
430
441
 
431
442
  /// Extract type from `as Type` assertion in initializer
432
443
  pub fn extractAssertion(init_text: []const u8) ?[]const u8 {
433
444
  if (ch.endsWith(init_text, "as const")) return null;
434
- // Only find " as " at depth 0 (not inside nested brackets/braces/parens)
445
+ // Cheap pre-check: if ` as ` doesn't appear anywhere, skip the entire walk.
446
+ if (ch.indexOf(init_text, " as ", 0) == null) return null;
447
+
448
+ // Only find " as " at depth 0 (not inside nested brackets/braces/parens).
435
449
  var last_as: ?usize = null;
436
450
  var depth: isize = 0;
437
451
  var in_str: u8 = 0;
@@ -453,7 +467,11 @@ pub fn extractAssertion(init_text: []const u8) ?[]const u8 {
453
467
  depth += 1;
454
468
  } else if (c == ')' or c == ']' or c == '}' or c == '>') {
455
469
  depth -= 1;
456
- } else if (depth == 0 and i + 4 <= init_text.len and std.mem.eql(u8, init_text[i .. i + 4], " as ")) {
470
+ } else if (depth == 0 and c == ' ' and i + 4 <= init_text.len and
471
+ init_text[i + 1] == 'a' and init_text[i + 2] == 's' and init_text[i + 3] == ' ')
472
+ {
473
+ // Inline 4-byte check is faster than std.mem.eql for a fixed-length
474
+ // needle, and gates the comparison on the leading space first.
457
475
  last_as = i;
458
476
  }
459
477
  i += 1;
@@ -502,7 +520,16 @@ pub fn buildDtsParams(s: *Scanner, raw_params: []const u8) []const u8 {
502
520
  }
503
521
  }
504
522
  if (can_passthrough and colons >= commas + 1) {
505
- // Check for parameter modifiers
523
+ // Check for parameter modifiers. First-byte fast-fail: param
524
+ // modifiers start with one of `p`, `r`, or `o`. If `inner` doesn't
525
+ // even contain any of those bytes, skip the per-modifier loop
526
+ // entirely — saves ~5 full string scans on every parameter list
527
+ // that lacks modifiers (the common case).
528
+ const has_p = ch.indexOfChar(inner, 'p', 0) != null;
529
+ const has_r = ch.indexOfChar(inner, 'r', 0) != null;
530
+ const has_o = ch.indexOfChar(inner, 'o', 0) != null;
531
+ if (!has_p and !has_r and !has_o) return raw_params;
532
+
506
533
  var has_modifier = false;
507
534
  for (types.PARAM_MODIFIERS) |mod| {
508
535
  if (ch.indexOf(inner, mod, 0)) |mod_idx| {
@@ -602,10 +629,15 @@ pub fn buildSingleDtsParam(s: *Scanner, raw: []const u8) []const u8 {
602
629
  p = std.mem.trim(u8, p[di..], " \t\r\n");
603
630
  }
604
631
 
605
- // Strip parameter modifiers
632
+ // Strip parameter modifiers — fast-fail by first byte. The 5 PARAM_MODIFIERS
633
+ // ("public", "protected", "private", "readonly", "override") all start with
634
+ // 'p', 'r', or 'o', so we can reject most bytes in O(1) without iterating
635
+ // the full list.
606
636
  var stripped = true;
607
- while (stripped) {
637
+ while (stripped and p.len > 0) {
608
638
  stripped = false;
639
+ const c0 = p[0];
640
+ if (c0 != 'p' and c0 != 'r' and c0 != 'o') break;
609
641
  for (types.PARAM_MODIFIERS) |mod| {
610
642
  if (p.len > mod.len and ch.startsWith(p, mod) and !ch.isIdentChar(p[mod.len])) {
611
643
  p = std.mem.trim(u8, p[mod.len..], " \t\r\n");
@@ -695,14 +727,17 @@ pub fn buildSingleDtsParam(s: *Scanner, raw: []const u8) []const u8 {
695
727
  }
696
728
  const opt_marker: []const u8 = if (is_optional and !is_rest) "?" else "";
697
729
 
698
- // Build result
699
- var result = std.array_list.Managed(u8).init(s.allocator);
700
- if (is_rest) result.appendSlice("...") catch {};
701
- result.appendSlice(name) catch {};
702
- result.appendSlice(opt_marker) catch {};
703
- result.appendSlice(": ") catch {};
704
- result.appendSlice(param_type) catch {};
705
- return result.toOwnedSlice() catch "unknown: unknown";
730
+ // Build result — direct alloc (no ArrayList overhead)
731
+ const rest_prefix: []const u8 = if (is_rest) "..." else "";
732
+ const total = rest_prefix.len + name.len + opt_marker.len + 2 + param_type.len;
733
+ const result_buf = s.allocator.alloc(u8, total) catch return "unknown: unknown";
734
+ var rp: usize = 0;
735
+ if (is_rest) { @memcpy(result_buf[rp..][0..3], "..."); rp += 3; }
736
+ @memcpy(result_buf[rp..][0..name.len], name); rp += name.len;
737
+ @memcpy(result_buf[rp..][0..opt_marker.len], opt_marker); rp += opt_marker.len;
738
+ @memcpy(result_buf[rp..][0..2], ": "); rp += 2;
739
+ @memcpy(result_buf[rp..][0..param_type.len], param_type); rp += param_type.len;
740
+ return result_buf[0..rp];
706
741
  }
707
742
 
708
743
  /// Clean a destructured pattern by stripping default values and rest operators.
@@ -710,6 +745,14 @@ pub fn buildSingleDtsParam(s: *Scanner, raw: []const u8) []const u8 {
710
745
  /// Also handles multiline patterns like:
711
746
  /// "{\n name,\n headers = { ... },\n}" → "{\n name,\n headers,\n}"
712
747
  fn cleanDestructuredPattern(alloc: std.mem.Allocator, pattern: []const u8) []const u8 {
748
+ // Fast path: if there's nothing to clean (no defaults, no rest operators,
749
+ // no newlines), the pattern is already in DTS-friendly form. The previous
750
+ // code always allocated and walked the entire pattern.
751
+ const has_eq = ch.indexOfChar(pattern, '=', 0) != null;
752
+ const has_dots = ch.indexOf(pattern, "...", 0) != null;
753
+ const has_nl = ch.indexOfChar(pattern, '\n', 0) != null;
754
+ if (!has_eq and !has_dots and !has_nl) return pattern;
755
+
713
756
  var result = std.array_list.Managed(u8).init(alloc);
714
757
  result.ensureTotalCapacity(pattern.len) catch {};
715
758
  var i: usize = 0;
@@ -791,8 +834,9 @@ fn cleanDestructuredPattern(alloc: std.mem.Allocator, pattern: []const u8) []con
791
834
 
792
835
  const cleaned = result.toOwnedSlice() catch return pattern;
793
836
 
794
- // If the pattern contains newlines, try to collapse to single line if short enough
795
- if (ch.indexOf(cleaned, "\n", 0) != null) {
837
+ // If the pattern contains newlines, try to collapse to single line if short enough.
838
+ // indexOfChar is the SIMD-optimized single-byte search.
839
+ if (ch.indexOfChar(cleaned, '\n', 0) != null) {
796
840
  // Collapse newlines and extra whitespace to single spaces
797
841
  var collapsed = std.array_list.Managed(u8).init(alloc);
798
842
  collapsed.ensureTotalCapacity(cleaned.len) catch {};
@@ -888,23 +932,30 @@ pub fn extractFunction(s: *Scanner, decl_start: usize, is_exported: bool, is_asy
888
932
  const dts_params = buildDtsParams(s, raw_params);
889
933
  const func_name = if (name.len > 0) name else "default";
890
934
 
891
- // Build DTS text
892
- var text = std.array_list.Managed(u8).init(s.allocator);
893
- text.ensureTotalCapacity(128) catch {};
894
- if (is_exported) text.appendSlice("export ") catch {};
895
- text.appendSlice("declare function ") catch {};
896
- text.appendSlice(func_name) catch {};
897
- text.appendSlice(generics) catch {};
898
- text.appendSlice(dts_params) catch {};
899
- text.appendSlice(": ") catch {};
900
- text.appendSlice(return_type) catch {};
901
- text.append(';') catch {};
902
-
903
- const dts_text = text.toOwnedSlice() catch "";
935
+ // Build DTS text — single alloc + memcpy is cheaper than ArrayList init +
936
+ // multiple appendSlice + toOwnedSlice for the fixed-shape function header.
937
+ const export_prefix: []const u8 = if (is_exported) "export " else "";
938
+ const declare_kw = "declare function ";
939
+ const colon_sep = ": ";
940
+ const total = export_prefix.len + declare_kw.len + func_name.len + generics.len +
941
+ dts_params.len + colon_sep.len + return_type.len + 1; // +1 for ';'
942
+ const dts_text = blk: {
943
+ const buf = s.allocator.alloc(u8, total) catch break :blk @as([]const u8, "");
944
+ var tp: usize = 0;
945
+ @memcpy(buf[tp..][0..export_prefix.len], export_prefix); tp += export_prefix.len;
946
+ @memcpy(buf[tp..][0..declare_kw.len], declare_kw); tp += declare_kw.len;
947
+ @memcpy(buf[tp..][0..func_name.len], func_name); tp += func_name.len;
948
+ @memcpy(buf[tp..][0..generics.len], generics); tp += generics.len;
949
+ @memcpy(buf[tp..][0..dts_params.len], dts_params); tp += dts_params.len;
950
+ @memcpy(buf[tp..][0..colon_sep.len], colon_sep); tp += colon_sep.len;
951
+ @memcpy(buf[tp..][0..return_type.len], return_type); tp += return_type.len;
952
+ buf[tp] = ';';
953
+ break :blk @as([]const u8, buf);
954
+ };
904
955
  const comments = extractLeadingComments(s, decl_start);
905
956
 
906
957
  if (has_body) {
907
- s.func_body_indices.put(s.declarations.items.len, {}) catch {};
958
+ s.putFuncBodyIndex(s.declarations.items.len);
908
959
  }
909
960
 
910
961
  return .{
@@ -987,6 +1038,55 @@ pub fn extractVariable(s: *Scanner, decl_start: usize, kind: []const u8, is_expo
987
1038
  const init_start = s.pos;
988
1039
  var depth: isize = 0;
989
1040
  while (s.pos < s.len) {
1041
+ // SIMD fast-skip: bulk-skip bytes that can't be structural
1042
+ if (depth > 0) {
1043
+ while (s.pos + 16 <= s.len) {
1044
+ const chunk: @Vector(16, u8) = s.source[s.pos..][0..16].*;
1045
+ const interesting = (chunk == @as(@Vector(16, u8), @splat(ch.CH_LPAREN))) |
1046
+ (chunk == @as(@Vector(16, u8), @splat(ch.CH_RPAREN))) |
1047
+ (chunk == @as(@Vector(16, u8), @splat(ch.CH_LBRACE))) |
1048
+ (chunk == @as(@Vector(16, u8), @splat(ch.CH_RBRACE))) |
1049
+ (chunk == @as(@Vector(16, u8), @splat(ch.CH_LBRACKET))) |
1050
+ (chunk == @as(@Vector(16, u8), @splat(ch.CH_RBRACKET))) |
1051
+ (chunk == @as(@Vector(16, u8), @splat(ch.CH_LANGLE))) |
1052
+ (chunk == @as(@Vector(16, u8), @splat(ch.CH_RANGLE))) |
1053
+ (chunk == @as(@Vector(16, u8), @splat(ch.CH_SQUOTE))) |
1054
+ (chunk == @as(@Vector(16, u8), @splat(ch.CH_DQUOTE))) |
1055
+ (chunk == @as(@Vector(16, u8), @splat(ch.CH_BACKTICK))) |
1056
+ (chunk == @as(@Vector(16, u8), @splat(ch.CH_SLASH)));
1057
+ if (!@reduce(.Or, interesting)) {
1058
+ s.pos += 16;
1059
+ } else {
1060
+ break;
1061
+ }
1062
+ }
1063
+ } else {
1064
+ while (s.pos + 16 <= s.len) {
1065
+ const chunk: @Vector(16, u8) = s.source[s.pos..][0..16].*;
1066
+ const interesting = (chunk == @as(@Vector(16, u8), @splat(ch.CH_LPAREN))) |
1067
+ (chunk == @as(@Vector(16, u8), @splat(ch.CH_RPAREN))) |
1068
+ (chunk == @as(@Vector(16, u8), @splat(ch.CH_LBRACE))) |
1069
+ (chunk == @as(@Vector(16, u8), @splat(ch.CH_RBRACE))) |
1070
+ (chunk == @as(@Vector(16, u8), @splat(ch.CH_LBRACKET))) |
1071
+ (chunk == @as(@Vector(16, u8), @splat(ch.CH_RBRACKET))) |
1072
+ (chunk == @as(@Vector(16, u8), @splat(ch.CH_LANGLE))) |
1073
+ (chunk == @as(@Vector(16, u8), @splat(ch.CH_RANGLE))) |
1074
+ (chunk == @as(@Vector(16, u8), @splat(ch.CH_SQUOTE))) |
1075
+ (chunk == @as(@Vector(16, u8), @splat(ch.CH_DQUOTE))) |
1076
+ (chunk == @as(@Vector(16, u8), @splat(ch.CH_BACKTICK))) |
1077
+ (chunk == @as(@Vector(16, u8), @splat(ch.CH_SLASH))) |
1078
+ (chunk == @as(@Vector(16, u8), @splat(ch.CH_SEMI))) |
1079
+ (chunk == @as(@Vector(16, u8), @splat(ch.CH_COMMA))) |
1080
+ (chunk == @as(@Vector(16, u8), @splat(ch.CH_LF))) |
1081
+ (chunk == @as(@Vector(16, u8), @splat(ch.CH_CR)));
1082
+ if (!@reduce(.Or, interesting)) {
1083
+ s.pos += 16;
1084
+ } else {
1085
+ break;
1086
+ }
1087
+ }
1088
+ }
1089
+ if (s.pos >= s.len) break;
990
1090
  if (s.skipNonCode()) continue;
991
1091
  const ic = s.source[s.pos];
992
1092
  if (ic == ch.CH_LPAREN or ic == ch.CH_LBRACE or ic == ch.CH_LBRACKET or ic == ch.CH_LANGLE) {
@@ -1028,17 +1128,23 @@ pub fn extractVariable(s: *Scanner, decl_start: usize, kind: []const u8, is_expo
1028
1128
  const comments = extractLeadingComments(s, decl_start);
1029
1129
  const final_type = if (type_annotation.len > 0) type_annotation else "unknown";
1030
1130
 
1031
- // Build DTS text
1032
- var text = std.array_list.Managed(u8).init(s.allocator);
1033
- text.ensureTotalCapacity(128) catch {};
1034
- if (is_exported) text.appendSlice("export ") catch {};
1035
- text.appendSlice("declare ") catch {};
1036
- text.appendSlice(kind) catch {};
1037
- text.append(' ') catch {};
1038
- text.appendSlice(name) catch {};
1039
- text.appendSlice(": ") catch {};
1040
- text.appendSlice(final_type) catch {};
1041
- text.append(';') catch {};
1131
+ // Build DTS text — direct alloc (no ArrayList overhead).
1132
+ const export_prefix: []const u8 = if (is_exported) "export " else "";
1133
+ const declare_kw = "declare ";
1134
+ const total = export_prefix.len + declare_kw.len + kind.len + 1 + name.len + 2 + final_type.len + 1;
1135
+ const text_buf = blk: {
1136
+ const buf = s.allocator.alloc(u8, total) catch break :blk @as([]const u8, "");
1137
+ var tp: usize = 0;
1138
+ @memcpy(buf[tp..][0..export_prefix.len], export_prefix); tp += export_prefix.len;
1139
+ @memcpy(buf[tp..][0..declare_kw.len], declare_kw); tp += declare_kw.len;
1140
+ @memcpy(buf[tp..][0..kind.len], kind); tp += kind.len;
1141
+ buf[tp] = ' '; tp += 1;
1142
+ @memcpy(buf[tp..][0..name.len], name); tp += name.len;
1143
+ @memcpy(buf[tp..][0..2], ": "); tp += 2;
1144
+ @memcpy(buf[tp..][0..final_type.len], final_type); tp += final_type.len;
1145
+ buf[tp] = ';';
1146
+ break :blk @as([]const u8, buf);
1147
+ };
1042
1148
 
1043
1149
  // Store the variable kind in modifiers
1044
1150
  const mods = s.allocator.alloc([]const u8, 1) catch null;
@@ -1049,7 +1155,7 @@ pub fn extractVariable(s: *Scanner, decl_start: usize, kind: []const u8, is_expo
1049
1155
  results.append(.{
1050
1156
  .kind = .variable_decl,
1051
1157
  .name = name,
1052
- .text = text.toOwnedSlice() catch "",
1158
+ .text = text_buf,
1053
1159
  .is_exported = is_exported,
1054
1160
  .modifiers = mods,
1055
1161
  .type_annotation = type_annotation,
@@ -1102,26 +1208,33 @@ pub fn extractInterface(s: *Scanner, decl_start: usize, is_exported: bool) Decla
1102
1208
  const raw_body = if (s.pos < s.len and s.source[s.pos] == ch.CH_LBRACE) s.extractBraceBlock() else "{}";
1103
1209
  const body = cleanBraceBlock(s, raw_body);
1104
1210
 
1105
- // Build DTS text
1106
- var text = std.array_list.Managed(u8).init(s.allocator);
1107
- text.ensureTotalCapacity(128) catch {};
1108
- if (is_exported) text.appendSlice("export ") catch {};
1109
- text.appendSlice("declare interface ") catch {};
1110
- text.appendSlice(name) catch {};
1111
- text.appendSlice(generics) catch {};
1112
- if (extends_clause.len > 0) {
1113
- text.appendSlice(" extends ") catch {};
1114
- text.appendSlice(extends_clause) catch {};
1115
- }
1116
- text.append(' ') catch {};
1117
- text.appendSlice(body) catch {};
1211
+ // Build DTS text — direct alloc + memcpy for better cache behavior on the
1212
+ // many-interfaces hot path.
1213
+ const export_prefix: []const u8 = if (is_exported) "export " else "";
1214
+ const declare_kw = "declare interface ";
1215
+ const extends_kw: []const u8 = if (extends_clause.len > 0) " extends " else "";
1216
+ const total = export_prefix.len + declare_kw.len + name.len + generics.len +
1217
+ extends_kw.len + extends_clause.len + 1 + body.len;
1218
+ const text = blk: {
1219
+ const buf = s.allocator.alloc(u8, total) catch break :blk @as([]const u8, "");
1220
+ var tp: usize = 0;
1221
+ @memcpy(buf[tp..][0..export_prefix.len], export_prefix); tp += export_prefix.len;
1222
+ @memcpy(buf[tp..][0..declare_kw.len], declare_kw); tp += declare_kw.len;
1223
+ @memcpy(buf[tp..][0..name.len], name); tp += name.len;
1224
+ @memcpy(buf[tp..][0..generics.len], generics); tp += generics.len;
1225
+ @memcpy(buf[tp..][0..extends_kw.len], extends_kw); tp += extends_kw.len;
1226
+ @memcpy(buf[tp..][0..extends_clause.len], extends_clause); tp += extends_clause.len;
1227
+ buf[tp] = ' '; tp += 1;
1228
+ @memcpy(buf[tp..][0..body.len], body); tp += body.len;
1229
+ break :blk @as([]const u8, buf);
1230
+ };
1118
1231
 
1119
1232
  const comments = extractLeadingComments(s, decl_start);
1120
1233
 
1121
1234
  return .{
1122
1235
  .kind = .interface_decl,
1123
1236
  .name = name,
1124
- .text = text.toOwnedSlice() catch "",
1237
+ .text = text,
1125
1238
  .is_exported = is_exported,
1126
1239
  .extends_clause = extends_clause,
1127
1240
  .generics = generics,
@@ -1167,21 +1280,29 @@ pub fn extractTypeAlias(s: *Scanner, decl_start: usize, is_exported: bool) Decla
1167
1280
  const type_body = s.sliceTrimmed(type_start, s.pos);
1168
1281
  if (s.pos < s.len and s.source[s.pos] == ch.CH_SEMI) s.pos += 1;
1169
1282
 
1170
- var text = std.array_list.Managed(u8).init(s.allocator);
1171
- text.ensureTotalCapacity(128) catch {};
1172
- if (is_exported) text.appendSlice("export ") catch {};
1173
- text.appendSlice("type ") catch {};
1174
- text.appendSlice(name) catch {};
1175
- text.appendSlice(generics) catch {};
1176
- text.appendSlice(" = ") catch {};
1177
- text.appendSlice(type_body) catch {};
1283
+ // Direct alloc — fixed-shape "[export ]type name<G> = body".
1284
+ const export_prefix: []const u8 = if (is_exported) "export " else "";
1285
+ const type_kw = "type ";
1286
+ const eq_sep = " = ";
1287
+ const total = export_prefix.len + type_kw.len + name.len + generics.len + eq_sep.len + type_body.len;
1288
+ const text = blk: {
1289
+ const buf = s.allocator.alloc(u8, total) catch break :blk @as([]const u8, "");
1290
+ var tp: usize = 0;
1291
+ @memcpy(buf[tp..][0..export_prefix.len], export_prefix); tp += export_prefix.len;
1292
+ @memcpy(buf[tp..][0..type_kw.len], type_kw); tp += type_kw.len;
1293
+ @memcpy(buf[tp..][0..name.len], name); tp += name.len;
1294
+ @memcpy(buf[tp..][0..generics.len], generics); tp += generics.len;
1295
+ @memcpy(buf[tp..][0..eq_sep.len], eq_sep); tp += eq_sep.len;
1296
+ @memcpy(buf[tp..][0..type_body.len], type_body); tp += type_body.len;
1297
+ break :blk @as([]const u8, buf);
1298
+ };
1178
1299
 
1179
1300
  const comments = extractLeadingComments(s, decl_start);
1180
1301
 
1181
1302
  return .{
1182
1303
  .kind = .type_decl,
1183
1304
  .name = name,
1184
- .text = text.toOwnedSlice() catch "",
1305
+ .text = text,
1185
1306
  .is_exported = is_exported,
1186
1307
  .generics = generics,
1187
1308
  .leading_comments = comments,
@@ -1289,32 +1410,40 @@ pub fn extractClass(s: *Scanner, decl_start: usize, is_exported: bool, is_abstra
1289
1410
  s.skipWhitespaceAndComments();
1290
1411
  const class_body = buildClassBodyDts(s);
1291
1412
 
1292
- // Build DTS text
1293
- var text = std.array_list.Managed(u8).init(s.allocator);
1294
- text.ensureTotalCapacity(128) catch {};
1295
- if (is_exported) text.appendSlice("export ") catch {};
1296
- text.appendSlice("declare ") catch {};
1297
- if (is_abstract) text.appendSlice("abstract ") catch {};
1298
- text.appendSlice("class ") catch {};
1299
- text.appendSlice(name) catch {};
1300
- text.appendSlice(generics) catch {};
1301
- if (extends_clause.len > 0) {
1302
- text.appendSlice(" extends ") catch {};
1303
- text.appendSlice(extends_clause) catch {};
1304
- }
1305
- if (implements_text.len > 0) {
1306
- text.appendSlice(" implements ") catch {};
1307
- text.appendSlice(implements_text) catch {};
1308
- }
1309
- text.append(' ') catch {};
1310
- text.appendSlice(class_body) catch {};
1413
+ // Build DTS text — direct alloc + memcpy.
1414
+ const export_prefix: []const u8 = if (is_exported) "export " else "";
1415
+ const declare_kw = "declare ";
1416
+ const abstract_kw: []const u8 = if (is_abstract) "abstract " else "";
1417
+ const class_kw = "class ";
1418
+ const extends_sep: []const u8 = if (extends_clause.len > 0) " extends " else "";
1419
+ const implements_sep: []const u8 = if (implements_text.len > 0) " implements " else "";
1420
+ const total = export_prefix.len + declare_kw.len + abstract_kw.len + class_kw.len +
1421
+ name.len + generics.len + extends_sep.len + extends_clause.len +
1422
+ implements_sep.len + implements_text.len + 1 + class_body.len;
1423
+ const text = blk: {
1424
+ const buf = s.allocator.alloc(u8, total) catch break :blk @as([]const u8, "");
1425
+ var tp: usize = 0;
1426
+ @memcpy(buf[tp..][0..export_prefix.len], export_prefix); tp += export_prefix.len;
1427
+ @memcpy(buf[tp..][0..declare_kw.len], declare_kw); tp += declare_kw.len;
1428
+ @memcpy(buf[tp..][0..abstract_kw.len], abstract_kw); tp += abstract_kw.len;
1429
+ @memcpy(buf[tp..][0..class_kw.len], class_kw); tp += class_kw.len;
1430
+ @memcpy(buf[tp..][0..name.len], name); tp += name.len;
1431
+ @memcpy(buf[tp..][0..generics.len], generics); tp += generics.len;
1432
+ @memcpy(buf[tp..][0..extends_sep.len], extends_sep); tp += extends_sep.len;
1433
+ @memcpy(buf[tp..][0..extends_clause.len], extends_clause); tp += extends_clause.len;
1434
+ @memcpy(buf[tp..][0..implements_sep.len], implements_sep); tp += implements_sep.len;
1435
+ @memcpy(buf[tp..][0..implements_text.len], implements_text); tp += implements_text.len;
1436
+ buf[tp] = ' '; tp += 1;
1437
+ @memcpy(buf[tp..][0..class_body.len], class_body); tp += class_body.len;
1438
+ break :blk @as([]const u8, buf);
1439
+ };
1311
1440
 
1312
1441
  const comments = extractLeadingComments(s, decl_start);
1313
1442
 
1314
1443
  return .{
1315
1444
  .kind = .class_decl,
1316
1445
  .name = name,
1317
- .text = text.toOwnedSlice() catch "",
1446
+ .text = text,
1318
1447
  .is_exported = is_exported,
1319
1448
  .extends_clause = extends_clause,
1320
1449
  .generics = generics,
@@ -1362,33 +1491,42 @@ fn buildClassBodyDts(s: *Scanner) []const u8 {
1362
1491
 
1363
1492
  while (true) {
1364
1493
  s.skipWhitespaceAndComments();
1365
- if (s.matchWord("private")) {
1366
- is_private = true;
1367
- s.pos += 7;
1368
- } else if (s.matchWord("protected")) {
1369
- is_protected = true;
1370
- s.pos += 9;
1371
- } else if (s.matchWord("public")) {
1372
- s.pos += 6;
1373
- } else if (s.matchWord("static")) {
1374
- is_static = true;
1375
- s.pos += 6;
1376
- } else if (s.matchWord("abstract")) {
1377
- is_abstract = true;
1378
- s.pos += 8;
1379
- } else if (s.matchWord("readonly")) {
1380
- is_readonly = true;
1381
- s.pos += 8;
1382
- } else if (s.matchWord("override")) {
1383
- s.pos += 8;
1384
- } else if (s.matchWord("accessor")) {
1385
- s.pos += 8;
1386
- } else if (s.matchWord("async")) {
1387
- is_async = true;
1388
- s.pos += 5;
1389
- } else if (s.matchWord("declare")) {
1390
- s.pos += 7;
1391
- } else break;
1494
+ if (s.pos >= s.len) break;
1495
+ // First-byte dispatch — most class members have at most one or two
1496
+ // modifiers. The previous code ran up to 10 matchWord calls per
1497
+ // member iteration; this lets us reject most bytes immediately.
1498
+ const mc = s.source[s.pos];
1499
+ switch (mc) {
1500
+ 'p' => {
1501
+ if (s.matchWord("private")) { is_private = true; s.pos += 7; }
1502
+ else if (s.matchWord("protected")) { is_protected = true; s.pos += 9; }
1503
+ else if (s.matchWord("public")) { s.pos += 6; }
1504
+ else break;
1505
+ },
1506
+ 's' => {
1507
+ if (s.matchWord("static")) { is_static = true; s.pos += 6; }
1508
+ else break;
1509
+ },
1510
+ 'a' => {
1511
+ if (s.matchWord("abstract")) { is_abstract = true; s.pos += 8; }
1512
+ else if (s.matchWord("accessor")) { s.pos += 8; }
1513
+ else if (s.matchWord("async")) { is_async = true; s.pos += 5; }
1514
+ else break;
1515
+ },
1516
+ 'r' => {
1517
+ if (s.matchWord("readonly")) { is_readonly = true; s.pos += 8; }
1518
+ else break;
1519
+ },
1520
+ 'o' => {
1521
+ if (s.matchWord("override")) { s.pos += 8; }
1522
+ else break;
1523
+ },
1524
+ 'd' => {
1525
+ if (s.matchWord("declare")) { s.pos += 7; }
1526
+ else break;
1527
+ },
1528
+ else => break,
1529
+ }
1392
1530
  }
1393
1531
 
1394
1532
  s.skipWhitespaceAndComments();
@@ -1402,14 +1540,24 @@ fn buildClassBodyDts(s: *Scanner) []const u8 {
1402
1540
  continue;
1403
1541
  }
1404
1542
 
1405
- // Build modifier prefix
1406
- var mod_prefix = std.array_list.Managed(u8).init(s.allocator);
1407
- mod_prefix.appendSlice(" ") catch {};
1408
- if (is_protected) mod_prefix.appendSlice("protected ") catch {};
1409
- if (is_static) mod_prefix.appendSlice("static ") catch {};
1410
- if (is_abstract) mod_prefix.appendSlice("abstract ") catch {};
1411
- if (is_readonly) mod_prefix.appendSlice("readonly ") catch {};
1412
- const prefix = mod_prefix.toOwnedSlice() catch " ";
1543
+ // Build modifier prefix — direct alloc + memcpy beats ArrayList for the
1544
+ // ≤4 known fixed-length tokens. Saves an ArrayList init + toOwnedSlice
1545
+ // shrink per class member.
1546
+ const proto_len: usize = 2 +
1547
+ @as(usize, if (is_protected) "protected ".len else 0) +
1548
+ @as(usize, if (is_static) "static ".len else 0) +
1549
+ @as(usize, if (is_abstract) "abstract ".len else 0) +
1550
+ @as(usize, if (is_readonly) "readonly ".len else 0);
1551
+ const prefix = blk: {
1552
+ const pbuf = s.allocator.alloc(u8, proto_len) catch break :blk @as([]const u8, " ");
1553
+ pbuf[0] = ' '; pbuf[1] = ' ';
1554
+ var pp: usize = 2;
1555
+ if (is_protected) { @memcpy(pbuf[pp..][0.."protected ".len], "protected "); pp += "protected ".len; }
1556
+ if (is_static) { @memcpy(pbuf[pp..][0.."static ".len], "static "); pp += "static ".len; }
1557
+ if (is_abstract) { @memcpy(pbuf[pp..][0.."abstract ".len], "abstract "); pp += "abstract ".len; }
1558
+ if (is_readonly) { @memcpy(pbuf[pp..][0.."readonly ".len], "readonly "); pp += "readonly ".len; }
1559
+ break :blk @as([]const u8, pbuf);
1560
+ };
1413
1561
 
1414
1562
  // Detect member type
1415
1563
  if (s.matchWord("constructor")) {
@@ -1429,11 +1577,14 @@ fn buildClassBodyDts(s: *Scanner) []const u8 {
1429
1577
  }
1430
1578
 
1431
1579
  const dts_params = buildDtsParams(s, raw_params);
1432
- var member = std.array_list.Managed(u8).init(s.allocator);
1433
- member.appendSlice(" constructor") catch {};
1434
- member.appendSlice(dts_params) catch {};
1435
- member.append(';') catch {};
1436
- members.append(member.toOwnedSlice() catch "") catch {};
1580
+ // Direct alloc — fixed-shape " constructor<params>;".
1581
+ const ctor_prefix = " constructor";
1582
+ const ctor_total = ctor_prefix.len + dts_params.len + 1;
1583
+ const ctor_buf = s.allocator.alloc(u8, ctor_total) catch break;
1584
+ @memcpy(ctor_buf[0..ctor_prefix.len], ctor_prefix);
1585
+ @memcpy(ctor_buf[ctor_prefix.len..][0..dts_params.len], dts_params);
1586
+ ctor_buf[ctor_total - 1] = ';';
1587
+ members.append(ctor_buf) catch {};
1437
1588
  } else if (s.matchWord("get") and isAccessorFollowed(s)) {
1438
1589
  s.pos += 3;
1439
1590
  s.skipWhitespaceAndComments();
@@ -1453,14 +1604,19 @@ fn buildClassBodyDts(s: *Scanner) []const u8 {
1453
1604
  } else if (s.pos < s.len and s.source[s.pos] == ch.CH_SEMI) {
1454
1605
  s.pos += 1;
1455
1606
  }
1456
- var member = std.array_list.Managed(u8).init(s.allocator);
1457
- member.appendSlice(prefix) catch {};
1458
- member.appendSlice("get ") catch {};
1459
- member.appendSlice(member_name) catch {};
1460
- member.appendSlice("(): ") catch {};
1461
- member.appendSlice(ret_type) catch {};
1462
- member.append(';') catch {};
1463
- members.append(member.toOwnedSlice() catch "") catch {};
1607
+ // Direct alloc — fixed shape "<prefix>get <name>(): <ret>;".
1608
+ const get_kw = "get ";
1609
+ const get_sep = "(): ";
1610
+ const get_total = prefix.len + get_kw.len + member_name.len + get_sep.len + ret_type.len + 1;
1611
+ const get_buf = s.allocator.alloc(u8, get_total) catch break;
1612
+ var gp: usize = 0;
1613
+ @memcpy(get_buf[gp..][0..prefix.len], prefix); gp += prefix.len;
1614
+ @memcpy(get_buf[gp..][0..get_kw.len], get_kw); gp += get_kw.len;
1615
+ @memcpy(get_buf[gp..][0..member_name.len], member_name); gp += member_name.len;
1616
+ @memcpy(get_buf[gp..][0..get_sep.len], get_sep); gp += get_sep.len;
1617
+ @memcpy(get_buf[gp..][0..ret_type.len], ret_type); gp += ret_type.len;
1618
+ get_buf[gp] = ';';
1619
+ members.append(get_buf) catch {};
1464
1620
  } else if (s.matchWord("set") and isAccessorFollowed(s)) {
1465
1621
  s.pos += 3;
1466
1622
  s.skipWhitespaceAndComments();
@@ -1478,13 +1634,17 @@ fn buildClassBodyDts(s: *Scanner) []const u8 {
1478
1634
  s.pos += 1;
1479
1635
  }
1480
1636
  const dts_params = buildDtsParams(s, raw_params);
1481
- var member = std.array_list.Managed(u8).init(s.allocator);
1482
- member.appendSlice(prefix) catch {};
1483
- member.appendSlice("set ") catch {};
1484
- member.appendSlice(member_name) catch {};
1485
- member.appendSlice(dts_params) catch {};
1486
- member.append(';') catch {};
1487
- members.append(member.toOwnedSlice() catch "") catch {};
1637
+ // Direct alloc — fixed shape "<prefix>set <name><params>;".
1638
+ const set_kw = "set ";
1639
+ const set_total = prefix.len + set_kw.len + member_name.len + dts_params.len + 1;
1640
+ const set_buf = s.allocator.alloc(u8, set_total) catch break;
1641
+ var sp: usize = 0;
1642
+ @memcpy(set_buf[sp..][0..prefix.len], prefix); sp += prefix.len;
1643
+ @memcpy(set_buf[sp..][0..set_kw.len], set_kw); sp += set_kw.len;
1644
+ @memcpy(set_buf[sp..][0..member_name.len], member_name); sp += member_name.len;
1645
+ @memcpy(set_buf[sp..][0..dts_params.len], dts_params); sp += dts_params.len;
1646
+ set_buf[sp] = ';';
1647
+ members.append(set_buf) catch {};
1488
1648
  } else {
1489
1649
  // Regular method or property
1490
1650
  const is_generator = s.source[s.pos] == ch.CH_STAR;
@@ -1503,22 +1663,31 @@ fn buildClassBodyDts(s: *Scanner) []const u8 {
1503
1663
 
1504
1664
  if (members.items.len == 0) return "{}";
1505
1665
 
1506
- // Join members — pre-calculate total length
1507
- var total_len: usize = 4; // "{\n" + "\n}"
1508
- for (members.items) |m| total_len += m.len + 1;
1509
- var result = std.array_list.Managed(u8).init(s.allocator);
1510
- result.ensureTotalCapacity(total_len) catch {};
1511
- result.appendSlice("{\n") catch {};
1512
- for (members.items, 0..) |m, i| {
1513
- result.appendSlice(m) catch {};
1514
- if (i < members.items.len - 1) result.append('\n') catch {};
1666
+ // Join members — direct alloc + memcpy. Total = "{\n" + members joined by
1667
+ // '\n' + "\n}". Previously used ArrayList with per-member appendSlice + a
1668
+ // boundary conditional inside the loop.
1669
+ var total_len: usize = 4; // "{\n" (2) + "\n}" (2)
1670
+ for (members.items) |m| total_len += m.len;
1671
+ total_len += members.items.len - 1; // newlines between
1672
+ const buf = s.allocator.alloc(u8, total_len) catch return "{}";
1673
+ buf[0] = '{'; buf[1] = '\n';
1674
+ var rp: usize = 2;
1675
+ @memcpy(buf[rp..][0..members.items[0].len], members.items[0]);
1676
+ rp += members.items[0].len;
1677
+ for (members.items[1..]) |m| {
1678
+ buf[rp] = '\n'; rp += 1;
1679
+ @memcpy(buf[rp..][0..m.len], m);
1680
+ rp += m.len;
1515
1681
  }
1516
- result.appendSlice("\n}") catch {};
1517
- return result.toOwnedSlice() catch "{}";
1682
+ buf[rp] = '\n'; rp += 1;
1683
+ buf[rp] = '}'; rp += 1;
1684
+ return buf[0..rp];
1518
1685
  }
1519
1686
 
1520
1687
  fn isAccessorFollowed(s: *const Scanner) bool {
1521
- const next_after = s.peekAfterWord(if (s.matchWord("get")) "get" else "set");
1688
+ // Both "get" and "set" are 3 chars — the caller already verified one matched.
1689
+ // Inspect the byte at +3 directly instead of calling matchWord again.
1690
+ const next_after = s.peekAfterWord("get"); // length is what matters; "get".len == "set".len
1522
1691
  return next_after != 0 and (ch.isIdentStart(next_after) or next_after == ch.CH_LBRACKET or next_after == ch.CH_HASH);
1523
1692
  }
1524
1693
 
@@ -1584,17 +1753,23 @@ fn handleMethodOrPropertyAfterName(s: *Scanner, member_name: []const u8, mod_pre
1584
1753
  const opt_mark: []const u8 = if (is_optional) "?" else "";
1585
1754
  const gen_text: []const u8 = if (is_generator) "*" else "";
1586
1755
 
1587
- var member = std.array_list.Managed(u8).init(s.allocator);
1588
- member.appendSlice(mod_prefix) catch {};
1589
- member.appendSlice(gen_text) catch {};
1590
- member.appendSlice(member_name) catch {};
1591
- member.appendSlice(opt_mark) catch {};
1592
- member.appendSlice(generics) catch {};
1593
- member.appendSlice(dts_params) catch {};
1594
- member.appendSlice(": ") catch {};
1595
- member.appendSlice(ret_type) catch {};
1596
- member.append(';') catch {};
1597
- members.append(member.toOwnedSlice() catch "") catch {};
1756
+ // Direct alloc — class methods are emitted in tight loops, so the
1757
+ // ArrayList overhead added up to a non-trivial fraction of class
1758
+ // emission time on member-heavy classes.
1759
+ const meth_total = mod_prefix.len + gen_text.len + member_name.len + opt_mark.len +
1760
+ generics.len + dts_params.len + 2 + ret_type.len + 1;
1761
+ const meth_buf = s.allocator.alloc(u8, meth_total) catch return;
1762
+ var mp: usize = 0;
1763
+ @memcpy(meth_buf[mp..][0..mod_prefix.len], mod_prefix); mp += mod_prefix.len;
1764
+ @memcpy(meth_buf[mp..][0..gen_text.len], gen_text); mp += gen_text.len;
1765
+ @memcpy(meth_buf[mp..][0..member_name.len], member_name); mp += member_name.len;
1766
+ @memcpy(meth_buf[mp..][0..opt_mark.len], opt_mark); mp += opt_mark.len;
1767
+ @memcpy(meth_buf[mp..][0..generics.len], generics); mp += generics.len;
1768
+ @memcpy(meth_buf[mp..][0..dts_params.len], dts_params); mp += dts_params.len;
1769
+ @memcpy(meth_buf[mp..][0..2], ": "); mp += 2;
1770
+ @memcpy(meth_buf[mp..][0..ret_type.len], ret_type); mp += ret_type.len;
1771
+ meth_buf[mp] = ';';
1772
+ members.append(meth_buf) catch {};
1598
1773
  } else if (next_ch == ch.CH_COLON or next_ch == ch.CH_EQUAL or next_ch == ch.CH_SEMI or next_ch == ch.CH_RBRACE or next_ch == ch.CH_LF or next_ch == ch.CH_CR) {
1599
1774
  // Property
1600
1775
  var prop_type: []const u8 = "";
@@ -1661,14 +1836,17 @@ fn handleMethodOrPropertyAfterName(s: *Scanner, member_name: []const u8, mod_pre
1661
1836
  }
1662
1837
 
1663
1838
  const opt_mark: []const u8 = if (is_optional) "?" else "";
1664
- var member = std.array_list.Managed(u8).init(s.allocator);
1665
- member.appendSlice(mod_prefix) catch {};
1666
- member.appendSlice(member_name) catch {};
1667
- member.appendSlice(opt_mark) catch {};
1668
- member.appendSlice(": ") catch {};
1669
- member.appendSlice(prop_type) catch {};
1670
- member.append(';') catch {};
1671
- members.append(member.toOwnedSlice() catch "") catch {};
1839
+ // Direct alloc — fixed shape "<prefix><name>[?]: <type>;".
1840
+ const prop_total = mod_prefix.len + member_name.len + opt_mark.len + 2 + prop_type.len + 1;
1841
+ const prop_buf = s.allocator.alloc(u8, prop_total) catch return;
1842
+ var pp: usize = 0;
1843
+ @memcpy(prop_buf[pp..][0..mod_prefix.len], mod_prefix); pp += mod_prefix.len;
1844
+ @memcpy(prop_buf[pp..][0..member_name.len], member_name); pp += member_name.len;
1845
+ @memcpy(prop_buf[pp..][0..opt_mark.len], opt_mark); pp += opt_mark.len;
1846
+ @memcpy(prop_buf[pp..][0..2], ": "); pp += 2;
1847
+ @memcpy(prop_buf[pp..][0..prop_type.len], prop_type); pp += prop_type.len;
1848
+ prop_buf[pp] = ';';
1849
+ members.append(prop_buf) catch {};
1672
1850
  } else {
1673
1851
  s.skipClassMember();
1674
1852
  }
@@ -1717,27 +1895,31 @@ fn extractParamProperties(s: *Scanner, raw_params: []const u8, members: *std.arr
1717
1895
  params.append(std.mem.trim(u8, inner[start..], " \t\r\n")) catch {};
1718
1896
 
1719
1897
  for (params.items) |param| {
1720
- const has_public = ch.startsWith(param, "public ") or ch.startsWith(param, "public\t");
1721
- const has_protected = ch.startsWith(param, "protected ") or ch.startsWith(param, "protected\t");
1722
- const has_private = ch.startsWith(param, "private ") or ch.startsWith(param, "private\t");
1723
- const has_readonly = ch.contains(param, "readonly ");
1898
+ if (param.len == 0) continue;
1899
+ // First-byte fast-fail only constructor params starting with 'p' or
1900
+ // 'r' can carry an access modifier, so reject everything else without
1901
+ // running the four startsWith / contains scans.
1902
+ const first = param[0];
1903
+ if (first != 'p' and first != 'r') continue;
1904
+
1905
+ const has_public = first == 'p' and (ch.startsWith(param, "public ") or ch.startsWith(param, "public\t"));
1906
+ const has_protected = first == 'p' and (ch.startsWith(param, "protected ") or ch.startsWith(param, "protected\t"));
1907
+ const has_private = first == 'p' and (ch.startsWith(param, "private ") or ch.startsWith(param, "private\t"));
1908
+ const has_readonly = (first == 'r' and (ch.startsWith(param, "readonly ") or ch.startsWith(param, "readonly\t"))) or ch.contains(param, " readonly ");
1724
1909
 
1725
1910
  if (!has_public and !has_protected and !has_private and !has_readonly) continue;
1726
1911
  if (has_private) continue;
1727
1912
 
1728
1913
  var p = param;
1729
- var mods = std.array_list.Managed([]const u8).init(s.allocator);
1730
1914
  if (has_public) {
1731
1915
  var si: usize = 6;
1732
1916
  while (si < p.len and ch.isWhitespace(p[si])) si += 1;
1733
1917
  p = p[si..];
1734
- mods.append("public") catch {};
1735
1918
  }
1736
1919
  if (has_protected) {
1737
1920
  var si: usize = 9;
1738
1921
  while (si < p.len and ch.isWhitespace(p[si])) si += 1;
1739
1922
  p = p[si..];
1740
- mods.append("protected") catch {};
1741
1923
  }
1742
1924
  if (has_readonly) {
1743
1925
  if (ch.indexOf(p, "readonly ", 0)) |ri| {
@@ -1751,24 +1933,45 @@ fn extractParamProperties(s: *Scanner, raw_params: []const u8, members: *std.arr
1751
1933
  @memcpy(new_p[before.len..], after);
1752
1934
  p = new_p;
1753
1935
  }
1754
- mods.append("readonly") catch {};
1755
- }
1756
-
1757
- // Build mod text
1758
- var mod_text = std.array_list.Managed(u8).init(s.allocator);
1759
- for (mods.items, 0..) |m, i| {
1760
- mod_text.appendSlice(m) catch {};
1761
- if (i < mods.items.len - 1) mod_text.append(' ') catch {};
1762
1936
  }
1763
- if (mods.items.len > 0) mod_text.append(' ') catch {};
1764
1937
 
1938
+ // Compute total mod_text length up front — at most 3 modifier tokens
1939
+ // are possible (public/protected, readonly) plus separators. Direct
1940
+ // alloc avoids the ArrayList init + per-modifier append + toOwnedSlice
1941
+ // cost the previous code paid for every parameter property.
1765
1942
  const dts_param = buildSingleDtsParam(s, p);
1766
- var member = std.array_list.Managed(u8).init(s.allocator);
1767
- member.appendSlice(" ") catch {};
1768
- member.appendSlice(mod_text.toOwnedSlice() catch "") catch {};
1769
- member.appendSlice(dts_param) catch {};
1770
- member.append(';') catch {};
1771
- members.append(member.toOwnedSlice() catch "") catch {};
1943
+ const pub_token: []const u8 = if (has_public) "public" else "";
1944
+ const prot_token: []const u8 = if (has_protected) "protected" else "";
1945
+ const ro_token: []const u8 = if (has_readonly) "readonly" else "";
1946
+ const num_mods: usize = @as(usize, if (has_public) 1 else 0) +
1947
+ @as(usize, if (has_protected) 1 else 0) +
1948
+ @as(usize, if (has_readonly) 1 else 0);
1949
+ const mod_text_len = pub_token.len + prot_token.len + ro_token.len +
1950
+ (if (num_mods > 0) num_mods else 0); // separators + trailing space when any modifier present
1951
+
1952
+ const m_total = 2 + mod_text_len + dts_param.len + 1;
1953
+ const mbuf = s.allocator.alloc(u8, m_total) catch continue;
1954
+ var mp: usize = 0;
1955
+ mbuf[mp] = ' '; mp += 1;
1956
+ mbuf[mp] = ' '; mp += 1;
1957
+ var first_mod = true;
1958
+ if (has_public) {
1959
+ @memcpy(mbuf[mp..][0..pub_token.len], pub_token); mp += pub_token.len;
1960
+ first_mod = false;
1961
+ }
1962
+ if (has_protected) {
1963
+ if (!first_mod) { mbuf[mp] = ' '; mp += 1; }
1964
+ @memcpy(mbuf[mp..][0..prot_token.len], prot_token); mp += prot_token.len;
1965
+ first_mod = false;
1966
+ }
1967
+ if (has_readonly) {
1968
+ if (!first_mod) { mbuf[mp] = ' '; mp += 1; }
1969
+ @memcpy(mbuf[mp..][0..ro_token.len], ro_token); mp += ro_token.len;
1970
+ }
1971
+ if (num_mods > 0) { mbuf[mp] = ' '; mp += 1; }
1972
+ @memcpy(mbuf[mp..][0..dts_param.len], dts_param); mp += dts_param.len;
1973
+ mbuf[mp] = ';'; mp += 1;
1974
+ members.append(mbuf[0..mp]) catch {};
1772
1975
  }
1773
1976
  }
1774
1977
 
@@ -1814,15 +2017,22 @@ pub fn extractModule(s: *Scanner, decl_start: usize, is_exported: bool, keyword:
1814
2017
  else
1815
2018
  "{}";
1816
2019
 
1817
- var text = std.array_list.Managed(u8).init(s.allocator);
1818
- text.ensureTotalCapacity(128) catch {};
1819
- if (is_exported) text.appendSlice("export ") catch {};
1820
- text.appendSlice("declare ") catch {};
1821
- text.appendSlice(keyword) catch {};
1822
- text.append(' ') catch {};
1823
- text.appendSlice(name) catch {};
1824
- text.append(' ') catch {};
1825
- text.appendSlice(body) catch {};
2020
+ // Direct alloc — fixed-shape "[export ]declare <keyword> <name> <body>".
2021
+ const export_prefix: []const u8 = if (is_exported) "export " else "";
2022
+ const declare_kw = "declare ";
2023
+ const total = export_prefix.len + declare_kw.len + keyword.len + 1 + name.len + 1 + body.len;
2024
+ const text = blk: {
2025
+ const buf = s.allocator.alloc(u8, total) catch break :blk @as([]const u8, "");
2026
+ var tp: usize = 0;
2027
+ @memcpy(buf[tp..][0..export_prefix.len], export_prefix); tp += export_prefix.len;
2028
+ @memcpy(buf[tp..][0..declare_kw.len], declare_kw); tp += declare_kw.len;
2029
+ @memcpy(buf[tp..][0..keyword.len], keyword); tp += keyword.len;
2030
+ buf[tp] = ' '; tp += 1;
2031
+ @memcpy(buf[tp..][0..name.len], name); tp += name.len;
2032
+ buf[tp] = ' '; tp += 1;
2033
+ @memcpy(buf[tp..][0..body.len], body); tp += body.len;
2034
+ break :blk @as([]const u8, buf);
2035
+ };
1826
2036
 
1827
2037
  const comments = extractLeadingComments(s, decl_start);
1828
2038
  const is_ambient = name.len > 0 and (name[0] == '\'' or name[0] == '"');
@@ -1830,7 +2040,7 @@ pub fn extractModule(s: *Scanner, decl_start: usize, is_exported: bool, keyword:
1830
2040
  return .{
1831
2041
  .kind = .module_decl,
1832
2042
  .name = name,
1833
- .text = text.toOwnedSlice() catch "",
2043
+ .text = text,
1834
2044
  .is_exported = is_exported,
1835
2045
  .source_module = if (is_ambient and name.len > 2) name[1 .. name.len - 1] else "",
1836
2046
  .leading_comments = comments,
@@ -1859,22 +2069,28 @@ pub fn buildNamespaceBodyDts(s: *Scanner, indent: []const u8) []const u8 {
1859
2069
  continue;
1860
2070
  }
1861
2071
 
2072
+ // First-byte gate for "export"/"declare" — avoids running matchWord on
2073
+ // every byte that can't possibly start either keyword.
1862
2074
  var has_export = false;
1863
- if (s.matchWord("export")) {
2075
+ if (s.pos < s.len and s.source[s.pos] == 'e' and s.matchWord("export")) {
1864
2076
  has_export = true;
1865
2077
  s.pos += 6;
1866
2078
  s.skipWhitespaceAndComments();
1867
2079
  }
1868
- if (s.matchWord("declare")) {
2080
+ if (s.pos < s.len and s.source[s.pos] == 'd' and s.matchWord("declare")) {
1869
2081
  s.pos += 7;
1870
2082
  s.skipWhitespaceAndComments();
1871
2083
  }
1872
2084
 
1873
2085
  const prefix: []const u8 = if (has_export) "export " else "";
1874
2086
 
1875
- if (s.matchWord("function") or (s.matchWord("async") and s.peekAfterKeyword("async", "function"))) {
2087
+ // First-byte gate: only check `function`/`async function` when the next
2088
+ // byte could start either. Saves matchWord on every other dispatch path.
2089
+ const dispatch_byte: u8 = if (s.pos < s.len) s.source[s.pos] else 0;
2090
+ if ((dispatch_byte == 'f' and s.matchWord("function")) or
2091
+ (dispatch_byte == 'a' and s.matchWord("async") and s.peekAfterKeyword("async", "function"))) {
1876
2092
  var is_async = false;
1877
- if (s.matchWord("async")) {
2093
+ if (dispatch_byte == 'a' and s.matchWord("async")) {
1878
2094
  is_async = true;
1879
2095
  s.pos += 5;
1880
2096
  s.skipWhitespaceAndComments();
@@ -1901,37 +2117,53 @@ pub fn buildNamespaceBodyDts(s: *Scanner, indent: []const u8) []const u8 {
1901
2117
  s.pos += 1;
1902
2118
  }
1903
2119
  const dts_params = buildDtsParams(s, raw_params);
1904
- var line = std.array_list.Managed(u8).init(s.allocator);
1905
- line.appendSlice(indent) catch {};
1906
- line.appendSlice(prefix) catch {};
1907
- line.appendSlice("function ") catch {};
1908
- line.appendSlice(fname) catch {};
1909
- line.appendSlice(generics) catch {};
1910
- line.appendSlice(dts_params) catch {};
1911
- line.appendSlice(": ") catch {};
1912
- line.appendSlice(ret_type) catch {};
1913
- line.append(';') catch {};
1914
- lines.append(line.toOwnedSlice() catch "") catch {};
2120
+ // Direct alloc — fixed shape "<indent><prefix>function <name><generics><params>: <ret>;".
2121
+ const fn_kw = "function ";
2122
+ const colon_sep = ": ";
2123
+ const fn_total = indent.len + prefix.len + fn_kw.len + fname.len +
2124
+ generics.len + dts_params.len + colon_sep.len + ret_type.len + 1;
2125
+ const fn_buf = s.allocator.alloc(u8, fn_total) catch continue;
2126
+ var fp: usize = 0;
2127
+ @memcpy(fn_buf[fp..][0..indent.len], indent); fp += indent.len;
2128
+ @memcpy(fn_buf[fp..][0..prefix.len], prefix); fp += prefix.len;
2129
+ @memcpy(fn_buf[fp..][0..fn_kw.len], fn_kw); fp += fn_kw.len;
2130
+ @memcpy(fn_buf[fp..][0..fname.len], fname); fp += fname.len;
2131
+ @memcpy(fn_buf[fp..][0..generics.len], generics); fp += generics.len;
2132
+ @memcpy(fn_buf[fp..][0..dts_params.len], dts_params); fp += dts_params.len;
2133
+ @memcpy(fn_buf[fp..][0..colon_sep.len], colon_sep); fp += colon_sep.len;
2134
+ @memcpy(fn_buf[fp..][0..ret_type.len], ret_type); fp += ret_type.len;
2135
+ fn_buf[fp] = ';';
2136
+ lines.append(fn_buf) catch {};
1915
2137
  } else if (s.matchWord("const") or s.matchWord("let") or s.matchWord("var")) {
1916
- const kw: []const u8 = if (s.matchWord("const")) "const" else if (s.matchWord("let")) "let" else "var";
2138
+ // First-char dispatch previously matchWord ran twice more here.
2139
+ const kw: []const u8 = switch (s.source[s.pos]) {
2140
+ 'c' => "const",
2141
+ 'l' => "let",
2142
+ else => "var",
2143
+ };
1917
2144
  s.pos += kw.len;
1918
2145
  s.skipWhitespaceAndComments();
1919
2146
 
1920
2147
  // Check for const enum
1921
- if (std.mem.eql(u8, kw, "const") and s.matchWord("enum")) {
2148
+ // kw is one of "const", "let", "var" — check by first byte instead of mem.eql.
2149
+ if (kw[0] == 'c' and s.matchWord("enum")) {
1922
2150
  s.pos += 4;
1923
2151
  s.skipWhitespaceAndComments();
1924
2152
  const ce_name = s.readIdent();
1925
2153
  s.skipWhitespaceAndComments();
1926
2154
  const ce_body = if (s.pos < s.len and s.source[s.pos] == ch.CH_LBRACE) s.extractBraceBlock() else "{}";
1927
- var line = std.array_list.Managed(u8).init(s.allocator);
1928
- line.appendSlice(indent) catch {};
1929
- line.appendSlice(prefix) catch {};
1930
- line.appendSlice("const enum ") catch {};
1931
- line.appendSlice(ce_name) catch {};
1932
- line.append(' ') catch {};
1933
- line.appendSlice(ce_body) catch {};
1934
- lines.append(line.toOwnedSlice() catch "") catch {};
2155
+ // Direct alloc — fixed shape "<indent><prefix>const enum <name> <body>".
2156
+ const ce_kw = "const enum ";
2157
+ const ce_total = indent.len + prefix.len + ce_kw.len + ce_name.len + 1 + ce_body.len;
2158
+ const ce_buf = s.allocator.alloc(u8, ce_total) catch continue;
2159
+ var cp: usize = 0;
2160
+ @memcpy(ce_buf[cp..][0..indent.len], indent); cp += indent.len;
2161
+ @memcpy(ce_buf[cp..][0..prefix.len], prefix); cp += prefix.len;
2162
+ @memcpy(ce_buf[cp..][0..ce_kw.len], ce_kw); cp += ce_kw.len;
2163
+ @memcpy(ce_buf[cp..][0..ce_name.len], ce_name); cp += ce_name.len;
2164
+ ce_buf[cp] = ' '; cp += 1;
2165
+ @memcpy(ce_buf[cp..][0..ce_body.len], ce_body); cp += ce_body.len;
2166
+ lines.append(ce_buf) catch {};
1935
2167
  continue;
1936
2168
  }
1937
2169
 
@@ -1989,7 +2221,9 @@ pub fn buildNamespaceBodyDts(s: *Scanner, indent: []const u8) []const u8 {
1989
2221
  const as_type = extractAssertion(init_text);
1990
2222
  if (as_type) |t| {
1991
2223
  vtype = t;
1992
- } else if (std.mem.eql(u8, kw, "const")) {
2224
+ } else if (kw[0] == 'c') {
2225
+ // kw is "const" — first-byte check is sufficient since it's
2226
+ // one of "const" / "let" / "var".
1993
2227
  vtype = inferLiteralType(init_text);
1994
2228
  } else {
1995
2229
  vtype = inferTypeFromDefault(init_text);
@@ -1997,16 +2231,19 @@ pub fn buildNamespaceBodyDts(s: *Scanner, indent: []const u8) []const u8 {
1997
2231
  }
1998
2232
  if (vtype.len == 0) vtype = "unknown";
1999
2233
 
2000
- var line = std.array_list.Managed(u8).init(s.allocator);
2001
- line.appendSlice(indent) catch {};
2002
- line.appendSlice(prefix) catch {};
2003
- line.appendSlice(kw) catch {};
2004
- line.append(' ') catch {};
2005
- line.appendSlice(vname) catch {};
2006
- line.appendSlice(": ") catch {};
2007
- line.appendSlice(vtype) catch {};
2008
- line.append(';') catch {};
2009
- lines.append(line.toOwnedSlice() catch "") catch {};
2234
+ // Direct alloc — fixed shape "<indent><prefix><kw> <name>: <type>;".
2235
+ const var_total = indent.len + prefix.len + kw.len + 1 + vname.len + 2 + vtype.len + 1;
2236
+ const var_buf = s.allocator.alloc(u8, var_total) catch continue;
2237
+ var vp: usize = 0;
2238
+ @memcpy(var_buf[vp..][0..indent.len], indent); vp += indent.len;
2239
+ @memcpy(var_buf[vp..][0..prefix.len], prefix); vp += prefix.len;
2240
+ @memcpy(var_buf[vp..][0..kw.len], kw); vp += kw.len;
2241
+ var_buf[vp] = ' '; vp += 1;
2242
+ @memcpy(var_buf[vp..][0..vname.len], vname); vp += vname.len;
2243
+ @memcpy(var_buf[vp..][0..2], ": "); vp += 2;
2244
+ @memcpy(var_buf[vp..][0..vtype.len], vtype); vp += vtype.len;
2245
+ var_buf[vp] = ';';
2246
+ lines.append(var_buf) catch {};
2010
2247
  } else if (s.matchWord("interface")) {
2011
2248
  s.pos += 9;
2012
2249
  s.skipWhitespaceAndComments();
@@ -2024,16 +2261,21 @@ pub fn buildNamespaceBodyDts(s: *Scanner, indent: []const u8) []const u8 {
2024
2261
  ext = s.source[ext_start..s.pos];
2025
2262
  }
2026
2263
  const body = cleanBraceBlock(s, s.extractBraceBlock());
2027
- var line = std.array_list.Managed(u8).init(s.allocator);
2028
- line.appendSlice(indent) catch {};
2029
- line.appendSlice(prefix) catch {};
2030
- line.appendSlice("interface ") catch {};
2031
- line.appendSlice(iname) catch {};
2032
- line.appendSlice(generics) catch {};
2033
- line.appendSlice(ext) catch {};
2034
- line.append(' ') catch {};
2035
- line.appendSlice(body) catch {};
2036
- lines.append(line.toOwnedSlice() catch "") catch {};
2264
+ // Direct alloc — fixed shape "<indent><prefix>interface <name><gen><ext> <body>".
2265
+ const if_kw = "interface ";
2266
+ const if_total = indent.len + prefix.len + if_kw.len + iname.len +
2267
+ generics.len + ext.len + 1 + body.len;
2268
+ const if_buf = s.allocator.alloc(u8, if_total) catch continue;
2269
+ var ifp: usize = 0;
2270
+ @memcpy(if_buf[ifp..][0..indent.len], indent); ifp += indent.len;
2271
+ @memcpy(if_buf[ifp..][0..prefix.len], prefix); ifp += prefix.len;
2272
+ @memcpy(if_buf[ifp..][0..if_kw.len], if_kw); ifp += if_kw.len;
2273
+ @memcpy(if_buf[ifp..][0..iname.len], iname); ifp += iname.len;
2274
+ @memcpy(if_buf[ifp..][0..generics.len], generics); ifp += generics.len;
2275
+ @memcpy(if_buf[ifp..][0..ext.len], ext); ifp += ext.len;
2276
+ if_buf[ifp] = ' '; ifp += 1;
2277
+ @memcpy(if_buf[ifp..][0..body.len], body); ifp += body.len;
2278
+ lines.append(if_buf) catch {};
2037
2279
  } else if (s.matchWord("type")) {
2038
2280
  s.pos += 4;
2039
2281
  s.skipWhitespaceAndComments();
@@ -2061,15 +2303,21 @@ pub fn buildNamespaceBodyDts(s: *Scanner, indent: []const u8) []const u8 {
2061
2303
  }
2062
2304
  const type_body = s.sliceTrimmed(ts, s.pos);
2063
2305
  if (s.pos < s.len and s.source[s.pos] == ch.CH_SEMI) s.pos += 1;
2064
- var line = std.array_list.Managed(u8).init(s.allocator);
2065
- line.appendSlice(indent) catch {};
2066
- line.appendSlice(prefix) catch {};
2067
- line.appendSlice("type ") catch {};
2068
- line.appendSlice(tname) catch {};
2069
- line.appendSlice(generics) catch {};
2070
- line.appendSlice(" = ") catch {};
2071
- line.appendSlice(type_body) catch {};
2072
- lines.append(line.toOwnedSlice() catch "") catch {};
2306
+ // Direct alloc — fixed shape "<indent><prefix>type <name><gen> = <body>".
2307
+ const ty_kw = "type ";
2308
+ const eq_sep = " = ";
2309
+ const ty_total = indent.len + prefix.len + ty_kw.len + tname.len +
2310
+ generics.len + eq_sep.len + type_body.len;
2311
+ const ty_buf = s.allocator.alloc(u8, ty_total) catch continue;
2312
+ var typ: usize = 0;
2313
+ @memcpy(ty_buf[typ..][0..indent.len], indent); typ += indent.len;
2314
+ @memcpy(ty_buf[typ..][0..prefix.len], prefix); typ += prefix.len;
2315
+ @memcpy(ty_buf[typ..][0..ty_kw.len], ty_kw); typ += ty_kw.len;
2316
+ @memcpy(ty_buf[typ..][0..tname.len], tname); typ += tname.len;
2317
+ @memcpy(ty_buf[typ..][0..generics.len], generics); typ += generics.len;
2318
+ @memcpy(ty_buf[typ..][0..eq_sep.len], eq_sep); typ += eq_sep.len;
2319
+ @memcpy(ty_buf[typ..][0..type_body.len], type_body); typ += type_body.len;
2320
+ lines.append(ty_buf) catch {};
2073
2321
  }
2074
2322
  } else if (s.matchWord("enum")) {
2075
2323
  // Handle enum inside namespace
@@ -2078,32 +2326,39 @@ pub fn buildNamespaceBodyDts(s: *Scanner, indent: []const u8) []const u8 {
2078
2326
  const ename = s.readIdent();
2079
2327
  s.skipWhitespaceAndComments();
2080
2328
  const ebody = if (s.pos < s.len and s.source[s.pos] == ch.CH_LBRACE) s.extractBraceBlock() else "{}";
2081
- var line = std.array_list.Managed(u8).init(s.allocator);
2082
- line.appendSlice(indent) catch {};
2083
- line.appendSlice(prefix) catch {};
2084
- line.appendSlice("enum ") catch {};
2085
- line.appendSlice(ename) catch {};
2086
- line.append(' ') catch {};
2087
- line.appendSlice(ebody) catch {};
2088
- lines.append(line.toOwnedSlice() catch "") catch {};
2329
+ // Direct alloc — fixed shape "<indent><prefix>enum <name> <body>".
2330
+ const en_kw = "enum ";
2331
+ const en_total = indent.len + prefix.len + en_kw.len + ename.len + 1 + ebody.len;
2332
+ const en_buf = s.allocator.alloc(u8, en_total) catch continue;
2333
+ var ep: usize = 0;
2334
+ @memcpy(en_buf[ep..][0..indent.len], indent); ep += indent.len;
2335
+ @memcpy(en_buf[ep..][0..prefix.len], prefix); ep += prefix.len;
2336
+ @memcpy(en_buf[ep..][0..en_kw.len], en_kw); ep += en_kw.len;
2337
+ @memcpy(en_buf[ep..][0..ename.len], ename); ep += ename.len;
2338
+ en_buf[ep] = ' '; ep += 1;
2339
+ @memcpy(en_buf[ep..][0..ebody.len], ebody); ep += ebody.len;
2340
+ lines.append(en_buf) catch {};
2089
2341
  } else if (s.matchWord("namespace") or s.matchWord("module")) {
2090
- // Handle nested namespace/module
2091
- const ns_kw: []const u8 = if (s.matchWord("namespace")) "namespace" else "module";
2342
+ // First-char dispatch — previously matchWord ran twice more.
2343
+ const ns_kw: []const u8 = if (s.source[s.pos] == 'n') "namespace" else "module";
2092
2344
  s.pos += ns_kw.len;
2093
2345
  s.skipWhitespaceAndComments();
2094
2346
  const ns_name = s.readIdent();
2095
2347
  s.skipWhitespaceAndComments();
2096
2348
  // Use same indent level for nested namespace body (matches TS behavior)
2097
2349
  const ns_body = if (s.pos < s.len and s.source[s.pos] == ch.CH_LBRACE) buildNamespaceBodyDts(s, indent) else "{}";
2098
- var line = std.array_list.Managed(u8).init(s.allocator);
2099
- line.appendSlice(indent) catch {};
2100
- line.appendSlice(prefix) catch {};
2101
- line.appendSlice(ns_kw) catch {};
2102
- line.append(' ') catch {};
2103
- line.appendSlice(ns_name) catch {};
2104
- line.append(' ') catch {};
2105
- line.appendSlice(ns_body) catch {};
2106
- lines.append(line.toOwnedSlice() catch "") catch {};
2350
+ // Direct alloc — fixed shape "<indent><prefix><ns_kw> <name> <body>".
2351
+ const ns_total = indent.len + prefix.len + ns_kw.len + 1 + ns_name.len + 1 + ns_body.len;
2352
+ const ns_buf = s.allocator.alloc(u8, ns_total) catch continue;
2353
+ var nsp: usize = 0;
2354
+ @memcpy(ns_buf[nsp..][0..indent.len], indent); nsp += indent.len;
2355
+ @memcpy(ns_buf[nsp..][0..prefix.len], prefix); nsp += prefix.len;
2356
+ @memcpy(ns_buf[nsp..][0..ns_kw.len], ns_kw); nsp += ns_kw.len;
2357
+ ns_buf[nsp] = ' '; nsp += 1;
2358
+ @memcpy(ns_buf[nsp..][0..ns_name.len], ns_name); nsp += ns_name.len;
2359
+ ns_buf[nsp] = ' '; nsp += 1;
2360
+ @memcpy(ns_buf[nsp..][0..ns_body.len], ns_body); nsp += ns_body.len;
2361
+ lines.append(ns_buf) catch {};
2107
2362
  } else if (s.matchWord("class") or s.matchWord("abstract")) {
2108
2363
  // Handle class inside namespace
2109
2364
  var is_abs = false;
@@ -2119,31 +2374,46 @@ pub fn buildNamespaceBodyDts(s: *Scanner, indent: []const u8) []const u8 {
2119
2374
  s.skipWhitespaceAndComments();
2120
2375
  const cgen = extractGenerics(s);
2121
2376
  s.skipWhitespaceAndComments();
2122
- // Skip extends/implements
2123
- while (s.matchWord("extends") or s.matchWord("implements")) {
2124
- const kw_len: usize = if (s.matchWord("extends")) 7 else 10;
2377
+ // Skip extends/implements — first-byte dispatch saves redundant
2378
+ // matchWord calls (the previous code ran matchWord up to 4× per
2379
+ // outer iteration just to determine the keyword length).
2380
+ while (true) {
2381
+ if (s.pos >= s.len) break;
2382
+ const c0 = s.source[s.pos];
2383
+ var kw_len: usize = 0;
2384
+ if (c0 == 'e' and s.matchWord("extends")) kw_len = 7
2385
+ else if (c0 == 'i' and s.matchWord("implements")) kw_len = 10
2386
+ else break;
2125
2387
  s.pos += kw_len;
2126
2388
  s.skipWhitespaceAndComments();
2127
2389
  var depth: isize = 0;
2128
2390
  while (s.pos < s.len) {
2129
2391
  if (s.skipNonCode()) continue;
2130
2392
  const tc = s.source[s.pos];
2131
- if (tc == ch.CH_LANGLE) depth += 1 else if (tc == ch.CH_RANGLE and !s.isArrowGT()) depth -= 1 else if (depth == 0 and (tc == ch.CH_LBRACE or s.matchWord("implements"))) break;
2393
+ if (tc == ch.CH_LANGLE) depth += 1
2394
+ else if (tc == ch.CH_RANGLE and !s.isArrowGT()) depth -= 1
2395
+ else if (depth == 0 and (tc == ch.CH_LBRACE or (tc == 'i' and s.matchWord("implements")))) break;
2132
2396
  s.pos += 1;
2133
2397
  }
2134
2398
  }
2135
2399
  s.skipWhitespaceAndComments();
2136
2400
  const cbody = if (s.pos < s.len and s.source[s.pos] == ch.CH_LBRACE) buildClassBodyDts(s) else "{}";
2137
- var line = std.array_list.Managed(u8).init(s.allocator);
2138
- line.appendSlice(indent) catch {};
2139
- line.appendSlice(prefix) catch {};
2140
- if (is_abs) line.appendSlice("abstract ") catch {};
2141
- line.appendSlice("class ") catch {};
2142
- line.appendSlice(cname) catch {};
2143
- line.appendSlice(cgen) catch {};
2144
- line.append(' ') catch {};
2145
- line.appendSlice(cbody) catch {};
2146
- lines.append(line.toOwnedSlice() catch "") catch {};
2401
+ // Direct alloc — "<indent><prefix>[abstract ]class <name><gen> <body>".
2402
+ const abs_kw: []const u8 = if (is_abs) "abstract " else "";
2403
+ const cl_kw = "class ";
2404
+ const cl_total = indent.len + prefix.len + abs_kw.len + cl_kw.len +
2405
+ cname.len + cgen.len + 1 + cbody.len;
2406
+ const cl_buf = s.allocator.alloc(u8, cl_total) catch continue;
2407
+ var cp: usize = 0;
2408
+ @memcpy(cl_buf[cp..][0..indent.len], indent); cp += indent.len;
2409
+ @memcpy(cl_buf[cp..][0..prefix.len], prefix); cp += prefix.len;
2410
+ @memcpy(cl_buf[cp..][0..abs_kw.len], abs_kw); cp += abs_kw.len;
2411
+ @memcpy(cl_buf[cp..][0..cl_kw.len], cl_kw); cp += cl_kw.len;
2412
+ @memcpy(cl_buf[cp..][0..cname.len], cname); cp += cname.len;
2413
+ @memcpy(cl_buf[cp..][0..cgen.len], cgen); cp += cgen.len;
2414
+ cl_buf[cp] = ' '; cp += 1;
2415
+ @memcpy(cl_buf[cp..][0..cbody.len], cbody); cp += cbody.len;
2416
+ lines.append(cl_buf) catch {};
2147
2417
  } else {
2148
2418
  s.skipToStatementEnd();
2149
2419
  }
@@ -2155,12 +2425,16 @@ pub fn buildNamespaceBodyDts(s: *Scanner, indent: []const u8) []const u8 {
2155
2425
  var def_text = s.sliceTrimmed(def_start, s.pos);
2156
2426
  if (def_text.len > 0 and def_text[def_text.len - 1] == ';') def_text = def_text[0 .. def_text.len - 1];
2157
2427
  if (def_text.len > 0) {
2158
- var line = std.array_list.Managed(u8).init(s.allocator);
2159
- line.appendSlice(indent) catch {};
2160
- line.appendSlice("export default ") catch {};
2161
- line.appendSlice(def_text) catch {};
2162
- line.append(';') catch {};
2163
- lines.append(line.toOwnedSlice() catch "") catch {};
2428
+ // Direct alloc — fixed shape "<indent>export default <text>;".
2429
+ const def_kw = "export default ";
2430
+ const def_total = indent.len + def_kw.len + def_text.len + 1;
2431
+ const def_buf = s.allocator.alloc(u8, def_total) catch continue;
2432
+ var dp: usize = 0;
2433
+ @memcpy(def_buf[dp..][0..indent.len], indent); dp += indent.len;
2434
+ @memcpy(def_buf[dp..][0..def_kw.len], def_kw); dp += def_kw.len;
2435
+ @memcpy(def_buf[dp..][0..def_text.len], def_text); dp += def_text.len;
2436
+ def_buf[dp] = ';';
2437
+ lines.append(def_buf) catch {};
2164
2438
  }
2165
2439
  } else {
2166
2440
  s.skipToStatementEnd();
@@ -2168,14 +2442,23 @@ pub fn buildNamespaceBodyDts(s: *Scanner, indent: []const u8) []const u8 {
2168
2442
  }
2169
2443
 
2170
2444
  if (lines.items.len == 0) return "{}";
2171
- var result = std.array_list.Managed(u8).init(s.allocator);
2172
- result.appendSlice("{\n") catch {};
2173
- for (lines.items, 0..) |line, i| {
2174
- result.appendSlice(line) catch {};
2175
- if (i < lines.items.len - 1) result.append('\n') catch {};
2445
+ // Direct alloc — pre-compute total and emit in one pass.
2446
+ var total_len: usize = 4; // "{\n" + "\n}"
2447
+ for (lines.items) |line| total_len += line.len;
2448
+ total_len += lines.items.len - 1; // newlines between
2449
+ const buf = s.allocator.alloc(u8, total_len) catch return "{}";
2450
+ buf[0] = '{'; buf[1] = '\n';
2451
+ var rp: usize = 2;
2452
+ @memcpy(buf[rp..][0..lines.items[0].len], lines.items[0]);
2453
+ rp += lines.items[0].len;
2454
+ for (lines.items[1..]) |line| {
2455
+ buf[rp] = '\n'; rp += 1;
2456
+ @memcpy(buf[rp..][0..line.len], line);
2457
+ rp += line.len;
2176
2458
  }
2177
- result.appendSlice("\n}") catch {};
2178
- return result.toOwnedSlice() catch "{}";
2459
+ buf[rp] = '\n'; rp += 1;
2460
+ buf[rp] = '}'; rp += 1;
2461
+ return buf[0..rp];
2179
2462
  }
2180
2463
 
2181
2464
  // ========================================================================
@@ -2184,6 +2467,17 @@ pub fn buildNamespaceBodyDts(s: *Scanner, indent: []const u8) []const u8 {
2184
2467
 
2185
2468
  /// Strip trailing inline comments from a line (respecting strings)
2186
2469
  fn stripTrailingInlineComment(line: []const u8) []const u8 {
2470
+ // Fast path: most lines have no `/` at all — `indexOfChar` SIMD-scans for it
2471
+ // in a single pass. If absent, we know there's no inline comment and can
2472
+ // skip the string-state walking entirely.
2473
+ const slash_idx = ch.indexOfChar(line, '/', 0);
2474
+ if (slash_idx == null) {
2475
+ // Trim trailing whitespace.
2476
+ var end = line.len;
2477
+ while (end > 0 and (line[end - 1] == ' ' or line[end - 1] == '\t' or line[end - 1] == '\r')) end -= 1;
2478
+ return line[0..end];
2479
+ }
2480
+
2187
2481
  var in_string: u8 = 0;
2188
2482
  var i: usize = 0;
2189
2483
  while (i < line.len) {
@@ -2241,8 +2535,18 @@ pub fn cleanBraceBlock(s: *Scanner, raw: []const u8) []const u8 {
2241
2535
  if (!needs_reindent) return raw;
2242
2536
  }
2243
2537
 
2244
- // Check if there are any comment markers
2245
- const has_comments = ch.contains(raw, "//") or ch.contains(raw, "/*");
2538
+ // Check if there are any comment markers — single scan for '/' and only do
2539
+ // the multi-byte verification if found.
2540
+ const has_comments = blk: {
2541
+ var i: usize = 0;
2542
+ while (ch.indexOfChar(raw, '/', i)) |slash_idx| {
2543
+ if (slash_idx + 1 < raw.len and (raw[slash_idx + 1] == '/' or raw[slash_idx + 1] == '*')) {
2544
+ break :blk true;
2545
+ }
2546
+ i = slash_idx + 1;
2547
+ }
2548
+ break :blk false;
2549
+ };
2246
2550
 
2247
2551
  // Split raw text by newlines and process line by line
2248
2552
  const est_lines = @max(raw.len / 30, 4);
@@ -2294,7 +2598,8 @@ pub fn cleanBraceBlock(s: *Scanner, raw: []const u8) []const u8 {
2294
2598
  // Compute indent
2295
2599
  var iw: usize = 0;
2296
2600
  while (iw < cleaned_line.len and (cleaned_line[iw] == ' ' or cleaned_line[iw] == '\t')) iw += 1;
2297
- if (!std.mem.eql(u8, ct, "{") and !std.mem.eql(u8, ct, "}")) {
2601
+ // Single-byte literal check `std.mem.eql` is overkill for a 1-char comparison.
2602
+ if (!(ct.len == 1 and (ct[0] == '{' or ct[0] == '}'))) {
2298
2603
  if (iw < min_indent) min_indent = iw;
2299
2604
  }
2300
2605
  indent_cache.append(iw) catch {};
@@ -2314,7 +2619,8 @@ pub fn cleanBraceBlock(s: *Scanner, raw: []const u8) []const u8 {
2314
2619
 
2315
2620
  var iw: usize = 0;
2316
2621
  while (iw < cleaned_line.len and (cleaned_line[iw] == ' ' or cleaned_line[iw] == '\t')) iw += 1;
2317
- if (!std.mem.eql(u8, ct, "{") and !std.mem.eql(u8, ct, "}")) {
2622
+ // Single-byte literal check `std.mem.eql` is overkill for a 1-char comparison.
2623
+ if (!(ct.len == 1 and (ct[0] == '{' or ct[0] == '}'))) {
2318
2624
  if (iw < min_indent) min_indent = iw;
2319
2625
  }
2320
2626
  indent_cache.append(iw) catch {};
@@ -2350,9 +2656,9 @@ pub fn cleanBraceBlock(s: *Scanner, raw: []const u8) []const u8 {
2350
2656
  }
2351
2657
 
2352
2658
  const ct = ch.sliceTrimmed(line, 0, line.len);
2353
- if (std.mem.eql(u8, ct, "{")) {
2354
- @memcpy(buf[pos..][0..ct.len], ct);
2355
- pos += ct.len;
2659
+ if (ct.len == 1 and ct[0] == '{') {
2660
+ buf[pos] = '{';
2661
+ pos += 1;
2356
2662
  continue;
2357
2663
  }
2358
2664
 
@@ -2381,78 +2687,92 @@ pub fn cleanBraceBlock(s: *Scanner, raw: []const u8) []const u8 {
2381
2687
 
2382
2688
  /// Handle `declare ...` after `declare` keyword
2383
2689
  pub fn handleDeclare(s: *Scanner, stmt_start: usize, is_exported: bool) void {
2384
- if (s.matchWord("function")) {
2690
+ // First-byte dispatch — most declare keywords are uniquely identified by
2691
+ // their first byte, so we avoid running the full matchWord scan against
2692
+ // unrelated keywords (each matchWord costs a length+boundary check).
2693
+ if (s.pos >= s.len) return;
2694
+ const c0 = s.source[s.pos];
2695
+ if (c0 == 'f' and s.matchWord("function")) {
2385
2696
  const decl = extractFunction(s, stmt_start, is_exported, false, false);
2386
2697
  if (decl) |d| s.declarations.append(d) catch {};
2387
- } else if (s.matchWord("async")) {
2388
- s.pos += 5;
2389
- s.skipWhitespaceAndComments();
2390
- if (s.matchWord("function")) {
2391
- const decl = extractFunction(s, stmt_start, is_exported, true, false);
2392
- if (decl) |d| s.declarations.append(d) catch {};
2698
+ } else if (c0 == 'a') {
2699
+ if (s.matchWord("async")) {
2700
+ s.pos += 5;
2701
+ s.skipWhitespaceAndComments();
2702
+ if (s.matchWord("function")) {
2703
+ const decl = extractFunction(s, stmt_start, is_exported, true, false);
2704
+ if (decl) |d| s.declarations.append(d) catch {};
2705
+ }
2706
+ } else if (s.matchWord("abstract")) {
2707
+ s.pos += 8;
2708
+ s.skipWhitespaceAndComments();
2709
+ if (s.matchWord("class")) {
2710
+ const decl = extractClass(s, stmt_start, is_exported, true);
2711
+ s.declarations.append(decl) catch {};
2712
+ }
2393
2713
  }
2394
- } else if (s.matchWord("class")) {
2395
- const decl = extractClass(s, stmt_start, is_exported, false);
2396
- s.declarations.append(decl) catch {};
2397
- } else if (s.matchWord("abstract")) {
2398
- s.pos += 8;
2399
- s.skipWhitespaceAndComments();
2714
+ } else if (c0 == 'c') {
2400
2715
  if (s.matchWord("class")) {
2401
- const decl = extractClass(s, stmt_start, is_exported, true);
2716
+ const decl = extractClass(s, stmt_start, is_exported, false);
2402
2717
  s.declarations.append(decl) catch {};
2718
+ } else if (s.matchWord("const")) {
2719
+ const saved_pos = s.pos;
2720
+ s.pos += 5;
2721
+ s.skipWhitespaceAndComments();
2722
+ if (s.matchWord("enum")) {
2723
+ s.pos = saved_pos + 5;
2724
+ s.skipWhitespaceAndComments();
2725
+ const decl = extractEnum(s, stmt_start, is_exported, true);
2726
+ s.declarations.append(decl) catch {};
2727
+ } else if (is_exported) {
2728
+ s.pos = saved_pos;
2729
+ const decls = extractVariable(s, stmt_start, "const", true);
2730
+ for (decls) |d| s.declarations.append(d) catch {};
2731
+ } else {
2732
+ s.skipToStatementEnd();
2733
+ }
2403
2734
  }
2404
- } else if (s.matchWord("interface")) {
2735
+ } else if (c0 == 'i' and s.matchWord("interface")) {
2405
2736
  const decl = extractInterface(s, stmt_start, is_exported);
2406
2737
  s.declarations.append(decl) catch {};
2407
- } else if (s.matchWord("type")) {
2738
+ } else if (c0 == 't' and s.matchWord("type")) {
2408
2739
  const decl = extractTypeAlias(s, stmt_start, is_exported);
2409
2740
  s.declarations.append(decl) catch {};
2410
- } else if (s.matchWord("enum")) {
2741
+ } else if (c0 == 'e' and s.matchWord("enum")) {
2411
2742
  const decl = extractEnum(s, stmt_start, is_exported, false);
2412
2743
  s.declarations.append(decl) catch {};
2413
- } else if (s.matchWord("const")) {
2414
- const saved_pos = s.pos;
2415
- s.pos += 5;
2416
- s.skipWhitespaceAndComments();
2417
- if (s.matchWord("enum")) {
2418
- s.pos = saved_pos + 5;
2419
- s.skipWhitespaceAndComments();
2420
- const decl = extractEnum(s, stmt_start, is_exported, true);
2421
- s.declarations.append(decl) catch {};
2422
- } else if (is_exported) {
2423
- s.pos = saved_pos;
2424
- const decls = extractVariable(s, stmt_start, "const", true);
2425
- for (decls) |d| s.declarations.append(d) catch {};
2426
- } else {
2427
- s.skipToStatementEnd();
2428
- }
2429
- } else if (s.matchWord("let") or s.matchWord("var")) {
2744
+ } else if ((c0 == 'l' and s.matchWord("let")) or (c0 == 'v' and s.matchWord("var"))) {
2430
2745
  if (is_exported) {
2431
- const kind: []const u8 = if (s.matchWord("let")) "let" else "var";
2746
+ // First-char dispatch saves a redundant matchWord call.
2747
+ const kind: []const u8 = if (c0 == 'l') "let" else "var";
2432
2748
  const decls = extractVariable(s, stmt_start, kind, true);
2433
2749
  for (decls) |d| s.declarations.append(d) catch {};
2434
2750
  } else {
2435
2751
  s.skipToStatementEnd();
2436
2752
  }
2437
- } else if (s.matchWord("module")) {
2753
+ } else if (c0 == 'm' and s.matchWord("module")) {
2438
2754
  const decl = extractModule(s, stmt_start, is_exported, "module");
2439
2755
  s.declarations.append(decl) catch {};
2440
- } else if (s.matchWord("namespace")) {
2756
+ } else if (c0 == 'n' and s.matchWord("namespace")) {
2441
2757
  const decl = extractModule(s, stmt_start, is_exported, "namespace");
2442
2758
  s.declarations.append(decl) catch {};
2443
- } else if (s.matchWord("global")) {
2759
+ } else if (c0 == 'g' and s.matchWord("global")) {
2444
2760
  s.pos += 6;
2445
2761
  s.skipWhitespaceAndComments();
2446
2762
  const body = if (s.pos < s.len and s.source[s.pos] == ch.CH_LBRACE) buildNamespaceBodyDts(s, " ") else "{}";
2447
- var text = std.array_list.Managed(u8).init(s.allocator);
2448
- text.ensureTotalCapacity(128) catch {};
2449
- text.appendSlice("declare global ") catch {};
2450
- text.appendSlice(body) catch {};
2763
+ // Direct alloc — fixed prefix + body slice.
2764
+ const prefix = "declare global ";
2765
+ const text_buf = blk: {
2766
+ const buf = s.allocator.alloc(u8, prefix.len + body.len) catch break :blk @as([]const u8, "");
2767
+ @memcpy(buf[0..prefix.len], prefix);
2768
+ @memcpy(buf[prefix.len..][0..body.len], body);
2769
+ break :blk @as([]const u8, buf);
2770
+ };
2451
2771
  const comments = extractLeadingComments(s, stmt_start);
2452
2772
  s.declarations.append(.{
2453
2773
  .kind = .module_decl,
2454
2774
  .name = "global",
2455
- .text = text.toOwnedSlice() catch "",
2775
+ .text = text_buf,
2456
2776
  .is_exported = false,
2457
2777
  .leading_comments = comments,
2458
2778
  .start = stmt_start,