From f651d97b703ddcbfba4baef7eb233ebbb5695c4a Mon Sep 17 00:00:00 2001 From: Russ T Fugal Date: Tue, 3 Mar 2026 08:00:58 -0700 Subject: [PATCH] =?UTF-8?q?Remove=20dead=20get=5Ftext=20calls=20from=20nor?= =?UTF-8?q?malize=20loop=20(saves=20O(M=C3=97N))?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `text` parameter was threaded through do_rewrite/normalize_char but never read — iteration is driven by the original charlist, not the rebuilt string. For M replacements on N-character text, this eliminates M × O(N) wasted work from InputText.get_text rebuilding the array. Also alias Kiri.Dictionary.Converter in kiri.convert mix task (Credo). Co-Authored-By: Claude Opus 4.6 --- .../kiri/lib/kiri/input_text/normalize.ex | 31 +++++++------------ packages/kiri/lib/mix/tasks/kiri.convert.ex | 4 ++- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/packages/kiri/lib/kiri/input_text/normalize.ex b/packages/kiri/lib/kiri/input_text/normalize.ex index 3ee85ec..cbf3f3d 100644 --- a/packages/kiri/lib/kiri/input_text/normalize.ex +++ b/packages/kiri/lib/kiri/input_text/normalize.ex @@ -47,10 +47,8 @@ defmodule Kiri.InputText.Normalize do @spec rewrite(InputText.t(), t()) :: InputText.t() def rewrite(builder, rules) do - text = InputText.get_text(builder) - chars = String.to_charlist(text) - text_len = length(chars) - do_rewrite(builder, rules, chars, 0, 0, text, text_len) + chars = builder |> InputText.get_text() |> String.to_charlist() + do_rewrite(builder, rules, chars, 0, 0, length(chars)) end # --- Parsing helpers --- @@ -82,13 +80,13 @@ defmodule Kiri.InputText.Normalize do end # --- Rewrite loop --- - # `text` is threaded through as a parameter and only recomputed after - # actual replacements, avoiding the O(n²) cost of rebuilding the string - # on every character iteration. + # Iteration is driven by the original charlist (`[cp | rest]`), not the + # modified text. `offset` tracks the cumulative length delta so that + # `InputText.replace` receives correct positions. - defp do_rewrite(builder, _rules, [], _i, _offset, _text, _text_len), do: builder + defp do_rewrite(builder, _rules, [], _i, _offset, _text_len), do: builder - defp do_rewrite(builder, rules, [cp | rest] = chars, i, offset, text, text_len) do + defp do_rewrite(builder, rules, [cp | rest] = chars, i, offset, text_len) do max_key_len = Map.get(rules.key_lengths, cp, 0) remaining = text_len - (i + offset) @@ -97,7 +95,6 @@ defmodule Kiri.InputText.Normalize do builder = InputText.replace(builder, i + offset, i + match_len + offset, replacement) rep_len = count_codepoints_fast(replacement) new_text_len = text_len + rep_len - match_len - text = InputText.get_text(builder) do_rewrite( builder, @@ -105,34 +102,31 @@ defmodule Kiri.InputText.Normalize do Enum.drop(rest, match_len - 1), i + match_len, offset + rep_len - match_len, - text, new_text_len ) :not_replaced -> - normalize_char(builder, rules, cp, rest, i, offset, text, text_len) + normalize_char(builder, rules, cp, rest, i, offset, text_len) end end - defp normalize_char(builder, rules, cp, rest, i, offset, text, text_len) do + defp normalize_char(builder, rules, cp, rest, i, offset, text_len) do nfkc = nfkc_replacement(cp, rules.ignore_normalize_set) case nfkc do nil -> - do_rewrite(builder, rules, rest, i + 1, offset, text, text_len) + do_rewrite(builder, rules, rest, i + 1, offset, text_len) <<^cp::utf8>> -> - do_rewrite(builder, rules, rest, i + 1, offset, text, text_len) + do_rewrite(builder, rules, rest, i + 1, offset, text_len) <<_single_cp::utf8>> -> builder = InputText.replace(builder, i + offset, i + 1 + offset, nfkc) - text = InputText.get_text(builder) - do_rewrite(builder, rules, rest, i + 1, offset, text, text_len) + do_rewrite(builder, rules, rest, i + 1, offset, text_len) _ -> rep_len = count_codepoints_fast(nfkc) builder = InputText.replace(builder, i + offset, i + 1 + offset, nfkc) - text = InputText.get_text(builder) do_rewrite( builder, @@ -140,7 +134,6 @@ defmodule Kiri.InputText.Normalize do rest, i + 1, offset + rep_len - 1, - text, text_len + rep_len - 1 ) end diff --git a/packages/kiri/lib/mix/tasks/kiri.convert.ex b/packages/kiri/lib/mix/tasks/kiri.convert.ex index b044f4e..32cba10 100644 --- a/packages/kiri/lib/mix/tasks/kiri.convert.ex +++ b/packages/kiri/lib/mix/tasks/kiri.convert.ex @@ -18,6 +18,8 @@ defmodule Mix.Tasks.Kiri.Convert do use Mix.Task + alias Kiri.Dictionary.Converter + @shortdoc "Convert a Sudachi .dic to .kiri format" @impl Mix.Task @@ -40,7 +42,7 @@ defmodule Mix.Tasks.Kiri.Convert do Mix.shell().info("Converting #{dic_path} -> #{output_dir}") - case Kiri.Dictionary.Converter.convert(dic_path, output_dir, convert_opts) do + case Converter.convert(dic_path, output_dir, convert_opts) do :ok -> Mix.shell().info("Done. Output written to #{output_dir}/") -- 2.54.0