Runtime (C): expand ads_fmt_* family + add result mutators#4
Runtime (C): expand ads_fmt_* family + add result mutators#4kevinelliott wants to merge 1 commit into
Conversation
Unblocks ~30+ C escape-hatch stubs surfaced by the Stage 2.5 C bulk port (airframesio/acars-decoder-c#1). Mirrors the TS ResultFormatter shape so cross-language corpus output stays byte-identical. New formatters (24 functions): Time-of-day (seconds since midnight → HH:MM:SS): ads_fmt_eta, _off, _on, _in, _out Calendar: ads_fmt_day, _departure_day, _arrival_day, _month Velocity / atmosphere: ads_fmt_mach, _groundspeed, _airspeed, _temperature, _total_air_temp Fuel: ads_fmt_current_fuel, _remaining_fuel Routing: ads_fmt_alternate_airport, _arrival_runway, _alternate_runway Events: ads_fmt_state_change, _door_event Free-text: ads_fmt_text, _unknown, _unknown_sep, _unknown_arr_sep Diagnostic: ads_fmt_checksum, _checksum_algorithm Generic: ads_fmt_push_item (for plugins that emit custom item shapes) ads_fmt_time_of_day_str (caller-owned malloc'd string helper) New result mutators (6 functions on ads_decode_result_t): ads_result_set_description ads_result_set_remaining ads_result_append_remaining ads_result_get_remaining ads_result_set_decode_level ads_result_clear_items Convention: every ads_fmt_* takes ownership of the ads_value_t * it receives and frees it (matches ads_result_raw_set's transfer semantics). NULL value → no-op, matching the TS guard-before-call pattern. Refactor of existing ads_fmt_unknown_arr to delegate through the new ads_fmt_unknown_arr_sep + ads_result_append_remaining. Verified: runtimes/c builds clean as libads_runtime_c.a. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Too many files changed? Review this PR in Change Stack to see how the pieces fit before you dive in. 📝 WalkthroughWalkthroughThis PR expands the ADS formatter library with comprehensive field-specific formatters (time-of-day, calendar, aircraft telemetry, events, diagnostics) and result-mutation helpers. The header declares all public APIs with documented ownership semantics, while the implementation provides the actual formatting and state-management functions. ChangesADS Formatter API Expansion
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 Infer (1.2.0)runtimes/c/src/result_formatter.cruntimes/c/src/result_formatter.c:4:10: fatal error: 'ads_helpers.h' file not found ... [truncated 728 characters] ... x-x86_64-v1.2.0/lib/infer/facebook-clang-plugins/clang/install/lib/clang/18/include" Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c649d6bab0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| void ads_fmt_checksum_algorithm(ads_decode_result_t *r, const char *value) { | ||
| if (!r || !value) return; | ||
| cJSON_AddStringToObject(r->raw, "checksum_algorithm", value); | ||
| push_item(r, "checksum_algorithm", "CKSUM_ALGO", "Checksum Algorithm", value); |
There was a problem hiding this comment.
Omit the checksum algorithm formatted item
When ARINC 702-style hatches call ads_fmt_checksum_algorithm, this adds a visible formatted item even though the TypeScript reference only sets raw.checksum_algorithm and leaves the formatted push commented out in runtimes/typescript/utils/result_formatter.ts. That makes every checksum-algorithm result diverge from the byte-identical corpus output this runtime is trying to mirror.
Useful? React with 👍 / 👎.
| snprintf(buf, sizeof(buf), "%llx", (long long)n); | ||
| push_item(r, "checksum", "CKSUM", "Checksum", buf); |
There was a problem hiding this comment.
Match TS checksum output formatting
For messages that include a checksum, the TypeScript formatter emits type: 'message_checksum', code: 'CHECKSUM', label: 'Message Checksum', and a zero-padded value like 0x00af; this C formatter emits a bare lowercase hex string with different metadata. Downstream corpus comparisons and consumers of the formatted item shape will see different results for the same decoded message.
Useful? React with 👍 / 👎.
| void ads_fmt_temperature(ads_decode_result_t *r, ads_value_t *v) { push_numeric(r, v, "outside_air_temperature", "outside_air_temperature", "OATEMP", "Outside Air Temperature (C)", "degrees"); } | ||
| void ads_fmt_total_air_temp(ads_decode_result_t *r, ads_value_t *v) { push_numeric(r, v, "total_air_temperature", "total_air_temperature", "TATEMP", "Total Air Temperature (C)", "degrees"); } |
There was a problem hiding this comment.
Parse temperature formatter inputs like TS
The TS temperature/totalAirTemp formatters accept strings such as M05/P03 and convert M/P signs before storing the numeric raw temperature; these C wrappers call push_numeric, so a ported hatch passing the same string value will fail ads_value_as_double and emit 0 degrees. That corrupts ARINC 702 temperature fields rather than matching the reference output.
Useful? React with 👍 / 👎.
| void ads_fmt_mach(ads_decode_result_t *r, ads_value_t *v) { push_numeric(r, v, "mach", "mach", "MACH", "Mach", ""); } | ||
| void ads_fmt_groundspeed(ads_decode_result_t *r, ads_value_t *v) { push_numeric(r, v, "groundspeed", "groundspeed", "GS", "Ground Speed", "knots"); } | ||
| void ads_fmt_airspeed(ads_decode_result_t *r, ads_value_t *v) { push_numeric(r, v, "airspeed", "airspeed", "IAS", "Indicated Airspeed", "knots"); } |
There was a problem hiding this comment.
Preserve TS velocity item metadata
When hatches use these new velocity formatters, their formatted item shapes differ from ResultFormatter: groundspeed should be aircraft_groundspeed/GSPD/Aircraft Groundspeed, airspeed should use ASPD/True Airspeed, and Mach should be labeled Mach Number with a mach suffix. These mismatches break the intended byte-for-byte cross-language formatted output.
Useful? React with 👍 / 👎.
|
|
||
| void ads_fmt_alternate_airport(ads_decode_result_t *r, ads_value_t *v) { push_string(r, v, "alternate_icao", "icao", "ALT_DST", "Alternate Destination"); } | ||
| void ads_fmt_arrival_runway(ads_decode_result_t *r, ads_value_t *v) { push_string(r, v, "arrival_runway", "runway", "ARWY", "Arrival Runway"); } | ||
| void ads_fmt_alternate_runway(ads_decode_result_t *r, ads_value_t *v) { push_string(r, v, "alternate_runway", "runway", "ALT_RWY", "Alternate Runway"); } |
There was a problem hiding this comment.
Use the TS alternate runway code
For decoded alternate runways, the TypeScript formatter emits code ALT_ARWY, but this C helper emits ALT_RWY. Any C escape hatch that was unblocked by this helper will produce a different formatted item code from the reference runtime for the same message.
Useful? React with 👍 / 👎.
| void ads_fmt_in(ads_decode_result_t *r, ads_value_t *v) { | ||
| push_tod(r, v, "in_time", "time", "IN", "Arrival at Gate"); | ||
| } | ||
| void ads_fmt_out(ads_decode_result_t *r, ads_value_t *v) { | ||
| push_tod(r, v, "out_time", "time", "OUT", "Departure from Gate"); |
There was a problem hiding this comment.
For OUT/IN time formatters, the TypeScript reference labels these items Out of Gate Time and In Gate Time; this implementation emits different labels. Ported OOOI hatches that call these new helpers will therefore generate formatted output that does not match the shared corpus even though the raw times are correct.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
This PR expands the C runtime’s result formatting surface by adding many new ads_fmt_* formatter functions and several ads_result_* mutators intended to mirror the TypeScript ResultFormatter API and unblock C escape-hatch ports.
Changes:
- Added new formatter functions for time-of-day, calendar, routing, fuel, velocity/atmosphere, events, diagnostics, and free-text.
- Refactored unknown-array formatting to delegate via
ads_fmt_unknown_arr_sep()andads_result_append_remaining(). - Added result “mutator” helpers for modifying description/remaining/decode level and clearing formatted items.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 11 comments.
| File | Description |
|---|---|
| runtimes/c/src/result_formatter.c | Implements the new formatter family and adds result mutator helpers. |
| runtimes/c/include/ads_helpers.h | Exposes the new formatter and result-mutator APIs via the public helpers header. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| void ads_fmt_day(ads_decode_result_t *r, ads_value_t *v) { push_int_item(r, v, "day", "day", "MSG_DAY", "Day of Month"); } | ||
| void ads_fmt_departure_day(ads_decode_result_t *r, ads_value_t *v) { push_int_item(r, v, "departure_day", "day", "DEP_DAY", "Departure Day"); } | ||
| void ads_fmt_arrival_day(ads_decode_result_t *r, ads_value_t *v) { push_int_item(r, v, "arrival_day", "day", "ARR_DAY", "Arrival Day"); } | ||
| void ads_fmt_month(ads_decode_result_t *r, ads_value_t *v) { push_int_item(r, v, "month", "month", "MSG_MONTH", "Month"); } |
| void ads_fmt_mach(ads_decode_result_t *r, ads_value_t *v) { push_numeric(r, v, "mach", "mach", "MACH", "Mach", ""); } | ||
| void ads_fmt_groundspeed(ads_decode_result_t *r, ads_value_t *v) { push_numeric(r, v, "groundspeed", "groundspeed", "GS", "Ground Speed", "knots"); } | ||
| void ads_fmt_airspeed(ads_decode_result_t *r, ads_value_t *v) { push_numeric(r, v, "airspeed", "airspeed", "IAS", "Indicated Airspeed", "knots"); } | ||
| void ads_fmt_temperature(ads_decode_result_t *r, ads_value_t *v) { push_numeric(r, v, "outside_air_temperature", "outside_air_temperature", "OATEMP", "Outside Air Temperature (C)", "degrees"); } | ||
| void ads_fmt_total_air_temp(ads_decode_result_t *r, ads_value_t *v) { push_numeric(r, v, "total_air_temperature", "total_air_temperature", "TATEMP", "Total Air Temperature (C)", "degrees"); } |
|
|
||
| void ads_fmt_alternate_airport(ads_decode_result_t *r, ads_value_t *v) { push_string(r, v, "alternate_icao", "icao", "ALT_DST", "Alternate Destination"); } | ||
| void ads_fmt_arrival_runway(ads_decode_result_t *r, ads_value_t *v) { push_string(r, v, "arrival_runway", "runway", "ARWY", "Arrival Runway"); } | ||
| void ads_fmt_alternate_runway(ads_decode_result_t *r, ads_value_t *v) { push_string(r, v, "alternate_runway", "runway", "ALT_RWY", "Alternate Runway"); } |
| void ads_fmt_in(ads_decode_result_t *r, ads_value_t *v) { | ||
| push_tod(r, v, "in_time", "time", "IN", "Arrival at Gate"); | ||
| } | ||
| void ads_fmt_out(ads_decode_result_t *r, ads_value_t *v) { | ||
| push_tod(r, v, "out_time", "time", "OUT", "Departure from Gate"); |
| void ads_fmt_state_change(ads_decode_result_t *r, const char *from, const char *to) { | ||
| if (!r || !from || !to) return; | ||
| cJSON *obj = cJSON_CreateObject(); | ||
| cJSON_AddStringToObject(obj, "from", from); | ||
| cJSON_AddStringToObject(obj, "to", to); | ||
| cJSON_AddItemToObject(r->raw, "state_change", obj); | ||
| char buf[128]; | ||
| snprintf(buf, sizeof(buf), "%s -> %s", from, to); | ||
| push_item(r, "state_change", "STATE_CHANGE", "State Change", buf); | ||
| } |
| void ads_fmt_checksum_algorithm(ads_decode_result_t *r, const char *value) { | ||
| if (!r || !value) return; | ||
| cJSON_AddStringToObject(r->raw, "checksum_algorithm", value); | ||
| push_item(r, "checksum_algorithm", "CKSUM_ALGO", "Checksum Algorithm", value); | ||
| } |
| void ads_result_set_remaining(ads_decode_result_t *r, const char *text) { | ||
| if (!r) return; | ||
| free(r->remaining); | ||
| r->remaining = text ? strdup(text) : NULL; | ||
| } | ||
|
|
||
| void ads_result_append_remaining(ads_decode_result_t *r, const char *text, const char *sep) { | ||
| if (!r || !text) return; | ||
| if (!sep) sep = ","; | ||
| if (r->remaining) { | ||
| size_t newlen = strlen(r->remaining) + 1 + strlen(joined) + 1; | ||
| size_t newlen = strlen(r->remaining) + strlen(sep) + strlen(text) + 1; | ||
| char *grown = malloc(newlen); | ||
| snprintf(grown, newlen, "%s,%s", r->remaining, joined); | ||
| if (!grown) return; | ||
| snprintf(grown, newlen, "%s%s%s", r->remaining, sep, text); | ||
| free(r->remaining); | ||
| r->remaining = grown; | ||
| } else { | ||
| r->remaining = strdup(joined); | ||
| r->remaining = strdup(text); | ||
| } |
| void ads_result_set_description(ads_decode_result_t *r, const char *description) { | ||
| if (!r) return; | ||
| free(r->description); | ||
| r->description = description ? strdup(description) : NULL; | ||
| } |
| void ads_result_clear_items(ads_decode_result_t *r) { | ||
| if (!r) return; | ||
| cJSON_Delete(r->items); | ||
| r->items = cJSON_CreateArray(); | ||
| } |
| /* ─── Fuel formatters ────────────────────────────────────────────────────── */ | ||
|
|
||
| void ads_fmt_current_fuel(ads_decode_result_t *r, ads_value_t *v) { push_numeric(r, v, "fuel_on_board", "fuel_on_board", "FOB", "Fuel On Board", ""); } | ||
| void ads_fmt_remaining_fuel(ads_decode_result_t *r, ads_value_t *v) { push_numeric(r, v, "fuel_remaining", "fuel_remaining", "FUEL_REM", "Fuel Remaining", ""); } |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
runtimes/c/src/result_formatter.c (1)
99-114: 💤 Low valueConsider single-pass concatenation to avoid O(n²) strcat overhead.
The repeated
strcatcalls iterate from the start ofjoinedeach time, making the loop O(n²) in total string length. For typical small arrays this is fine, but if large arrays are expected, a pointer-based approach would be more efficient.♻️ Optional O(n) approach
char *joined = malloc(total); if (!joined) return; - joined[0] = '\0'; - for (size_t i = 0; i < count; i++) { - if (i > 0) strcat(joined, sep); - strcat(joined, values[i] ? values[i] : ""); - } + char *p = joined; + for (size_t i = 0; i < count; i++) { + if (i > 0) { memcpy(p, sep, sep_len); p += sep_len; } + const char *s = values[i] ? values[i] : ""; + size_t len = strlen(s); + memcpy(p, s, len); + p += len; + } + *p = '\0';🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@runtimes/c/src/result_formatter.c` around lines 99 - 114, The function ads_fmt_unknown_arr_sep uses repeated strcat causing O(n²) behavior; change ads_fmt_unknown_arr_sep to build the joined buffer in a single pass by allocating the computed total, then maintain a write pointer (e.g., char *p = joined) and for each element memcpy/strcpy the value and insert the separator between elements by advancing p by the copied lengths, finally null-terminate and call ads_result_append_remaining(r, joined, sep); ensure you still handle NULL values (treat as empty string), use sep_len already computed, and free(joined) after the append.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@runtimes/c/include/ads_helpers.h`:
- Around line 63-65: The header comment incorrectly states "Every ads_fmt_*
takes ownership of the ads_value_t pointers", which overstates ownership because
several functions in this file (e.g., ads_fmt_state_change, ads_fmt_door_event,
ads_fmt_text, ads_fmt_unknown*, ads_fmt_checksum_algorithm, ads_fmt_push_item)
do not accept ads_value_t* and thus do not take ownership; update the comment in
runtimes/c/include/ads_helpers.h to explicitly state that only the ads_fmt_*
variants that accept ads_value_t* parameters transfer ownership and free them
(and that callers should pass NULL to no-op), and optionally list or reference
the specific functions that do not take ads_value_t* to avoid confusion.
In `@runtimes/c/src/result_formatter.c`:
- Around line 126-138: The function ads_fmt_time_of_day_str uses a fixed 16-byte
buffer which can overflow/truncate when printing out-of-range int64_t values;
change it to allocate a sufficiently large buffer only when printing the raw
integer: compute the required length for snprintf of the int64_t (or use
snprintf(NULL,0, "%lld", (long long)seconds) + 1), malloc that size, check the
allocation, then call snprintf into that buffer; keep the existing 16-byte
formatted HH:MM:SS branch unchanged and ensure all allocation failures return
NULL.
---
Nitpick comments:
In `@runtimes/c/src/result_formatter.c`:
- Around line 99-114: The function ads_fmt_unknown_arr_sep uses repeated strcat
causing O(n²) behavior; change ads_fmt_unknown_arr_sep to build the joined
buffer in a single pass by allocating the computed total, then maintain a write
pointer (e.g., char *p = joined) and for each element memcpy/strcpy the value
and insert the separator between elements by advancing p by the copied lengths,
finally null-terminate and call ads_result_append_remaining(r, joined, sep);
ensure you still handle NULL values (treat as empty string), use sep_len already
computed, and free(joined) after the append.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e54d0864-607d-4c53-988e-67fd40d331df
📒 Files selected for processing (2)
runtimes/c/include/ads_helpers.hruntimes/c/src/result_formatter.c
| /* Every ads_fmt_* takes ownership of the ads_value_t pointers passed in and | ||
| * frees them (matches ads_result_raw_set's transfer semantics). Pass NULL | ||
| * to no-op (matches the TS pattern of guarding before calling). */ |
There was a problem hiding this comment.
Documentation overstates ownership scope.
The comment says "Every ads_fmt_*" takes ownership of ads_value_t*, but several ads_fmt_* functions declared below don't accept ads_value_t* at all (e.g., ads_fmt_state_change, ads_fmt_door_event, ads_fmt_text, ads_fmt_unknown*, ads_fmt_checksum_algorithm, ads_fmt_push_item). Consider clarifying:
📝 Suggested wording
-/* Every ads_fmt_* takes ownership of the ads_value_t pointers passed in and
+/* Each ads_fmt_* that accepts an ads_value_t* takes ownership of it and
* frees them (matches ads_result_raw_set's transfer semantics). Pass NULL
* to no-op (matches the TS pattern of guarding before calling). */📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /* Every ads_fmt_* takes ownership of the ads_value_t pointers passed in and | |
| * frees them (matches ads_result_raw_set's transfer semantics). Pass NULL | |
| * to no-op (matches the TS pattern of guarding before calling). */ | |
| /* Each ads_fmt_* that accepts an ads_value_t* takes ownership of it and | |
| * frees them (matches ads_result_raw_set's transfer semantics). Pass NULL | |
| * to no-op (matches the TS pattern of guarding before calling). */ |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@runtimes/c/include/ads_helpers.h` around lines 63 - 65, The header comment
incorrectly states "Every ads_fmt_* takes ownership of the ads_value_t
pointers", which overstates ownership because several functions in this file
(e.g., ads_fmt_state_change, ads_fmt_door_event, ads_fmt_text, ads_fmt_unknown*,
ads_fmt_checksum_algorithm, ads_fmt_push_item) do not accept ads_value_t* and
thus do not take ownership; update the comment in
runtimes/c/include/ads_helpers.h to explicitly state that only the ads_fmt_*
variants that accept ads_value_t* parameters transfer ownership and free them
(and that callers should pass NULL to no-op), and optionally list or reference
the specific functions that do not take ads_value_t* to avoid confusion.
| char *ads_fmt_time_of_day_str(int64_t seconds) { | ||
| char *out = malloc(16); | ||
| if (!out) return NULL; | ||
| if (seconds >= 0 && seconds < 86400) { | ||
| int h = (int)(seconds / 3600); | ||
| int m = (int)((seconds % 3600) / 60); | ||
| int s = (int)(seconds % 60); | ||
| snprintf(out, 16, "%02d:%02d:%02d", h, m, s); | ||
| } else { | ||
| snprintf(out, 16, "%lld", (long long)seconds); | ||
| } | ||
| return out; | ||
| } |
There was a problem hiding this comment.
Buffer too small for out-of-range integer values.
When seconds is outside the valid 0–86399 range, snprintf outputs the raw integer. A 64-bit signed integer can require up to 20 characters plus sign and null terminator (e.g., -9223372036854775808). The 16-byte buffer will truncate these values silently.
🔧 Suggested fix
char *ads_fmt_time_of_day_str(int64_t seconds) {
- char *out = malloc(16);
+ char *out = malloc(24); /* Room for longest int64 string + null */
if (!out) return NULL;
if (seconds >= 0 && seconds < 86400) {
int h = (int)(seconds / 3600);
int m = (int)((seconds % 3600) / 60);
int s = (int)(seconds % 60);
- snprintf(out, 16, "%02d:%02d:%02d", h, m, s);
+ snprintf(out, 24, "%02d:%02d:%02d", h, m, s);
} else {
- snprintf(out, 16, "%lld", (long long)seconds);
+ snprintf(out, 24, "%lld", (long long)seconds);
}
return out;
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@runtimes/c/src/result_formatter.c` around lines 126 - 138, The function
ads_fmt_time_of_day_str uses a fixed 16-byte buffer which can overflow/truncate
when printing out-of-range int64_t values; change it to allocate a sufficiently
large buffer only when printing the raw integer: compute the required length for
snprintf of the int64_t (or use snprintf(NULL,0, "%lld", (long long)seconds) +
1), malloc that size, check the allocation, then call snprintf into that buffer;
keep the existing 16-byte formatted HH:MM:SS branch unchanged and ensure all
allocation failures return NULL.
Summary
Adds 24 new formatter functions + 6 result-mutator functions to the C runtime, unblocking ~30+ stubs surfaced by the Stage 2.5 C bulk port (acars-decoder-c#1).
Mirrors the TS
ResultFormattershape exactly so cross-language corpus output stays byte-identical when the C escape hatches are filled in.What landed
New formatters
ads_fmt_eta,_off,_on,_in,_outads_fmt_day,_departure_day,_arrival_day,_monthads_fmt_mach,_groundspeed,_airspeed,_temperature,_total_air_tempads_fmt_current_fuel,_remaining_fuelads_fmt_alternate_airport,_arrival_runway,_alternate_runwayads_fmt_state_change,_door_eventads_fmt_text,_unknown,_unknown_sep,_unknown_arr_sepads_fmt_checksum,_checksum_algorithmads_fmt_push_item(for plugins that emit custom item shapes),ads_fmt_time_of_day_strNew result mutators
For escape hatches that need to override fields the
ads_result_new()already set up (variant-dependent description, custom remaining text, etc.):ads_result_set_descriptionads_result_set_remainingads_result_append_remainingads_result_get_remainingads_result_set_decode_levelads_result_clear_itemsConvention
Every
ads_fmt_*takes ownership of theads_value_t *it receives and frees it (matchesads_result_raw_set's transfer semantics). NULL value → no-op, matching the TS guard-before-call pattern.ads_fmt_unknown_arrrefactored to delegate throughads_fmt_unknown_arr_sep+ads_result_append_remaining.Test plan
runtimes/cbuilds clean aslibads_runtime_c.a(verified with cmake)What this unblocks
The C bulk hatch translation surfaced this exact checklist (see acars-decoder-c#1's commit message for the full list). Once this merges:
Label_10_LDRneedsarrival_runway+alternate_runway+alternate_airport, all now present)🤖 Generated with Claude Code
Summary by CodeRabbit