textconvert is O(n²) in the number of translations — one-line fix
Environment: TouchGFX 4.26.0, Windows, Ruby 3.0 (bundled MinGW env). Text database: texts.xml is 13.7 MB - 20 languages x 8,255 text entries = 165,100 translations. "remap": "yes" in application.config, binary translations and binary fonts both enabled for one of the passes.
Symptom: make texts took ~181s cold on a developer machine, ~2min on our build server. Adding timing markers around each phase of Generator#run put 146s of it inside StringCollector#run.
Cause: the fold that collects the translations, in framework/tools/textconvert/lib/string_collector.rb:
all_strings = Array.new(languages.count, Array.new)
text_entries.each do |text_entry|
text_entry.translations.each_with_index do |translation, lang_index|
all_strings[remap_global ? 0 : lang_index] += [translation]
end
end`a += [x]` allocates a new array and copies the whole accumulated contents on every iteration, so this is O(n^2) in the number of translations. With global remapping every language folds into slot 0, so n is the *combined* translation count across all languages - 165,100 here. Measured: 105s of a 120s run spent in this loop alone.
Fix - append in place:
all_strings = Array.new(languages.count) { Array.new }
text_entries.each do |text_entry|
text_entry.translations.each_with_index do |translation, lang_index|
all_strings[remap_global ? 0 : lang_index] << translation
end
endNote the constructor change is required, not cosmetic: Array.new(n, Array.new) puts *the same* array object into all n slots. That latent bug is invisible today only because `+=` rebinds the slot instead of mutating the shared array - switching to `<<` without also changing the constructor would fold all languages together regardless of remap_global.
Result: Cold make texts 181s -> 48s locally; our build server's asset-generation step went from ~2min to 53s.
Verification: all 209 generated files under generated/texts and generated/fonts (Language*.cpp, Texts.cpp, TypedTextDatabase.*, Font_*.cpp, Table_*.cpp, Kerning_*.cpp, UnicodeList*.txt, headers) are byte-identical before and after, with remap: yes and binary translations/fonts unchanged. The fix only changes how the list is built, not its contents or its order.
Since the cost is quadratic in translation count, it's invisible on small databases and only bites larger multi-language ones - ours is 13.7 MB / 20 languages.
Would be great to see this in a future release so we don't have to re-apply it on every upgrade.
