Skip to content

Read an array inside a cell in a binary .sldd: its shape, its parts and its count - #38

Merged
ww-mw merged 1 commit into
mainfrom
fix-cell-array-elements
Sep 18, 2026
Merged

ww-mw merged 1 commit into
mainfrom
fix-cell-array-elements

Conversation

@ww-mw

@ww-mw ww-mw commented Sep 18, 2026

Copy link
Copy Markdown
Member

Three defects with one cause, found by generalizing defect 52 rather than by a report.

parseCellElement is a hand-rolled copy of the class dispatch the entry site (parseEntryValue) and the property/field site (parsePropContent) share, so every idea added to the shared dispatch has to be added to the copy by hand — and the copy lags. Defect 52 was the missing string idea. These three are the missing "a dimensioned element set" idea, which the shared dispatch has in one place (parseArrayOfElements) and the copy had in three half-right ways.

All three are binary-only — the text channel was right for all of them. All three are wrong on screen and lossy in the file, with no edit required: merely saving a dictionary that contained one changed the data. And all three were hidden by their own 1x1 control, which is why they are stated together.

# Value Showed Wrote
53 {[1+2i 3-4i]} {[1 3]} Class="double" Dimension="1*2">1.0 3.0IsComplex and the imaginary parts gone
54 {[struct('a',1) struct('a',2)]} {<1x1 struct>} the structs a level too deep; the next open found neither
55 {[Simulink.Parameter(1) Simulink.Parameter(2)]} {1} the first object only; the rest gone

Causes. 53: the copy asked the shape before the complexity, so a dimensioned element went to the shaped arm, whose numericBody runs each token through parseFloat, which stops at the +. A complex scalar was right the whole time because MATLAB writes one with no Dimension at all, so it fell past the shaped arm and reached the IsComplex check by accident of ordering. 54: a hardcoded [1, 1] where the shared dispatch reads the element's own Dimension. 55: the tail took childElements[0], which is every child when there is one.

Fix — one idea, not three: complexity is asked before shape, the struct arm reads the element's Dimension, and the object tail delegates a dimensioned multi-child element to parseArrayOfElements rather than becoming a fourth copy of the decision. The writer needed no change: once the model carried the right dims, class and count, it already spelled all three of MATLAB's forms.

Measurement. MATLAB spells the three forms three different ways, so a branch keyed on the wrong one would be no fix — test/parity/matlab/probe_cell_arrays.m asks and records:

  • complex: shape and complexity on the same element, parts as text — <Element Class="double" IsComplex="1" Dimension="1*2">1.0+2.0i 3.0-4.0i</Element>
  • struct array: shape on the element that declares the class — <Element Class="struct" Dimension="1*2"> over one child per struct
  • object array: shape on a classless wrapper — <Element Dimension="1*2"> over one classed <Element Class="Simulink.Parameter"> each

It wrote cellarr_{text,binary}.sldd: one dictionary in both flavours, which is what lets the channels be asserted against each other instead of each against its own literal.

Tests. Five in binaryWriteBackGate.test.ts, each of which fails with any one of the three edits reverted (verified by reverting each alone): MATLAB's values read back, the two channels equal over whole subtrees, the three write spellings, every entry byte-for-byte against MATLAB's own chunk, and a reopen of the rebuilt chunk. Full npm run verify green (4879 tests).

Left alone, deliberately: struct-array element rows are labelled 0-based where every other container is 1-based (and the channels disagree about it for an N-D struct array) — visible in these fixtures, unrelated to cells, recorded in DESIGN.md and not asserted either way.

…nd its count

Three defects with one cause, found by generalizing defect 52 rather than by a
report. parseCellElement is a hand-rolled copy of the class dispatch the entry
site and the property/field site share, so every idea added to the shared
dispatch has to be added to the copy by hand -- and the copy lags. 52 was the
missing `string` idea; these are the missing "a dimensioned element set" idea,
which the shared dispatch has in one place (parseArrayOfElements) and the copy
had in three half-right ways.

All three are binary-only, the text channel being right for all of them. All
three are wrong on screen AND lossy in the file, with no edit required -- merely
saving a dictionary that contained one changed the data. And all three were
hidden by their own 1x1 control, which is why they are stated together.

53. A complex ARRAY in a cell lost its imaginary parts. The copy asked the shape
    before the complexity, so a dimensioned element went to the shaped arm, whose
    numericBody runs each token through parseFloat, which stops at the `+`:
    {[1+2i 3-4i]} displayed {[1 3]} and went back out as
    Class="double" Dimension="1*2">1.0 3.0, IsComplex gone too. A complex SCALAR
    was right the whole time because MATLAB writes one with no Dimension at all,
    so it fell past the shaped arm and reached the IsComplex check by accident of
    ordering.

54. A struct ARRAY in a cell was published as <1x1 struct>: a hardcoded [1, 1]
    where the shared dispatch reads the element's own Dimension. The wrong
    summary was the harmless half -- the envelope's dims are what the writer
    spells, so a save emitted the structs a level too deep and the next open
    found neither of them.

55. An object ARRAY in a cell showed only its first object, because the tail took
    childElements[0]. {[Simulink.Parameter(1) Simulink.Parameter(2)]} read as
    though the cell held a scalar, and every object after the first was gone from
    the file on any save.

Fixed as one idea: complexity before shape, the struct arm reads the element's
Dimension, and the object tail delegates a dimensioned multi-child element to
parseArrayOfElements -- the same helper the property path uses -- rather than
becoming a fourth copy of the decision. The writer needed no change; once the
model carried the right dims, class and count, it already spelled all three of
MATLAB's forms.

probe_cell_arrays.m records the three spellings MATLAB uses (they differ from
each other, so a branch keyed on the wrong one would be no fix) and wrote
cellarr_{text,binary}.sldd -- one dictionary in both flavours, which is what
lets the channels be asserted against EACH OTHER instead of each against its own
literal. Five tests in binaryWriteBackGate.test.ts, each failing with any one of
the three edits reverted: MATLAB's values read back, the two channels equal, the
three write spellings, every entry byte-for-byte against MATLAB's own chunk, and
a reopen of the rebuilt chunk.
@ww-mw
ww-mw merged commit eb03d06 into main Sep 18, 2026
1 check passed
@ww-mw
ww-mw deleted the fix-cell-array-elements branch September 18, 2026 19:11
@ww-mw ww-mw mentioned this pull request Sep 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant