From 88f1cac4fa530fc30fd6ef01970af11c7a959b8e Mon Sep 17 00:00:00 2001 From: cirakg Date: Wed, 21 Oct 2026 03:55:01 +0200 Subject: fix(codes): bound every 2D encoding step under the watchdog; draw large codes from runs M6's step sizes were estimates, only exercised with small codes. With the largest inputs the app accepts, every 2D encoder tripped the watchdog on the Forerunner 255S (and the biggest steps were far over the 768 KB watches' limit), so a large code added from the phone would have stopped the app during settings processing at every start. - QR: resumable segmentation, Reed-Solomon, interleaving, function patterns and final mask; mask scoring on rows and columns packed 16 modules per word (same penalties, checked against the per-module rules). - Reed-Solomon: resumable field tables and generator, encoder shifting with native slice. - Data Matrix, Aztec, PDF417: resumable encodation, stuffing, compaction, placement and build; Data Matrix's grid as a byte array (90 KB -> 35 KB); PDF417 refuses text that can't fit the screen before compacting. - Cache format 2 with 16-bit sizes (wide PDF417 was cached wrong); packing in steps; codes drawn as runs, and from a bitmap painted in steps when a frame can't draw all runs. - The job runner does two steps per tick on 768 KB watches; gen_products --check verifies each product's watchdogCount fits that. - CodeView renews full brightness (AMOLED) or the backlight (MIP) for up to a minute from its single ticker; a tap restarts the minute. Co-Authored-By: Claude Opus 5.5 --- source/util/Jobs.mc | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) (limited to 'source/util') diff --git a/source/util/Jobs.mc b/source/util/Jobs.mc index d7342e3..595562e 100644 --- a/source/util/Jobs.mc +++ b/source/util/Jobs.mc @@ -8,10 +8,17 @@ // that can take long (key derivation, SHA-512 batches, 2D barcode encoding, // large imports) is a Job whose step() does a bounded amount of work. // -// JobRunner drives a job from a timer, one step per tick, and calls back when -// it is done. Tests call Jobs.runToCompletion() instead, synchronously. +// Steps are sized for the Forerunner 255/255S (the only 512 KB targets). +// Every 768 KB target allows twice as much per event +// (tools/gen_products.py --check fails if a product breaks that rule), so +// JobRunner runs two steps per tick there, which halves the time of an +// import or a large QR code. +// +// JobRunner drives a job from a timer and calls back when it is done. +// Tests call Jobs.runToCompletion() instead, synchronously. import Toybox.Lang; +import Toybox.System; import Toybox.Timer; module Jobs { @@ -69,10 +76,25 @@ module Jobs { } function onTick() as Void { - if (_job.step(_budget)) { - cancel(); - _onDone.invoke(); + for (var i = 0; i < stepsPerTick(); i++) { + if (_job.step(_budget)) { + cancel(); + _onDone.invoke(); + return; + } } } } + + // App memory above this means a 240,000-bytecode watchdog. + const LARGE_WATCH_MEMORY = 600000; + var _stepsPerTick as Number = 0; + + // 1 on the 512 KB watches (120,000 bytecodes per event), 2 elsewhere. + function stepsPerTick() as Number { + if (_stepsPerTick == 0) { + _stepsPerTick = System.getSystemStats().totalMemory > LARGE_WATCH_MEMORY ? 2 : 1; + } + return _stepsPerTick; + } } -- cgit v1.2.3