Encode a Unicode string to IFC STEP directive escapes.
Printable ASCII (32..126) is kept as-is, with ONE exception: the reverse
solidus (\, U+005C) is printable ASCII but goes out as \X\5C, because
a raw `` in the output is what a reader takes for the start of a
directive or of a \\ pair.
8-bit values are encoded as \X\HH.
BMP values are encoded as \X2\HHHH\X0.
Non-BMP values are encoded as \X4\HHHHHHHH\X0.
This is directive encoding only — it does NOT double the apostrophe (',
code point 39 is printable ASCII and passes through unchanged). Its output
is therefore not safe to place directly inside a STEP single-quoted
string literal: an undoubled ' terminates the literal early and produces
a file no conformant reader parses as intended (e.g. a name like
O'Brien). A caller writing into a literal must double ' itself, or use
escapeStepString from @ifc-lite/data, which handles the full
literal-context contract: doubling ' and \, and encoding control
characters and non-ASCII as directives —
per ISO 10303-21 6.3.3.4. The two functions do not produce the same output
for the same input; do not assume they agree. Sweeping U+0000..U+02FF, they
disagree on exactly two of the 95 printable ASCII characters: the
apostrophe (U+0027; doubled there, passed through here) and the reverse
solidus (U+005C; doubled there, \X\5C here). They also disagree on all 32
C0 controls AND on DEL (U+007F) -- \X2\00HH\X0 there, \X\HH here --
and on all
128 of U+0080..U+00FF (\X2\HHHH\X0\ there, \X\HH here). That is 163
disagreements in all. Above U+00FF they agree on all 512.
Kept for round-trip use with decodeIfcString, within the scope the
two functions actually cover: decodeIfcString(encodeIfcString(s)) === s
for every s built from Unicode SCALAR values. That is not the same as
every JS string. A JS string can also hold an unpaired surrogate
(U+D800..U+DFFF), which is not a scalar value; this encoder writes it as a
\X2\ directive, and {@link decodeIfcString} decodes every lone surrogate
to U+FFFD to stay in parity with the Rust decoder's
String::from_utf16_lossy. Those 2048 code units are the only inputs that
do not come back unchanged.
Within that scope the guarantee is independent of apostrophe handling,
because doubling is a literal-context requirement, not an encoding one.
See https://github.com/LTplus-AG/ifc-lite/issues/3445.
Encode a Unicode string to IFC STEP directive escapes.
\, U+005C) is printable ASCII but goes out as\X\5C, because a raw `` in the output is what a reader takes for the start of a directive or of a\\pair.This is directive encoding only — it does NOT double the apostrophe (
', code point 39 is printable ASCII and passes through unchanged). Its output is therefore not safe to place directly inside a STEP single-quoted string literal: an undoubled'terminates the literal early and produces a file no conformant reader parses as intended (e.g. a name likeO'Brien). A caller writing into a literal must double'itself, or useescapeStepStringfrom@ifc-lite/data, which handles the full literal-context contract: doubling'and\, and encoding control characters and non-ASCII as directives — per ISO 10303-21 6.3.3.4. The two functions do not produce the same output for the same input; do not assume they agree. Sweeping U+0000..U+02FF, they disagree on exactly two of the 95 printable ASCII characters: the apostrophe (U+0027; doubled there, passed through here) and the reverse solidus (U+005C; doubled there,\X\5Chere). They also disagree on all 32 C0 controls AND on DEL (U+007F) --\X2\00HH\X0there,\X\HHhere -- and on all 128 of U+0080..U+00FF (\X2\HHHH\X0\there,\X\HHhere). That is 163 disagreements in all. Above U+00FF they agree on all 512.Kept for round-trip use with decodeIfcString, within the scope the two functions actually cover:
decodeIfcString(encodeIfcString(s)) === sfor everysbuilt from Unicode SCALAR values. That is not the same as every JS string. A JS string can also hold an unpaired surrogate (U+D800..U+DFFF), which is not a scalar value; this encoder writes it as a\X2\directive, and {@link decodeIfcString} decodes every lone surrogate to U+FFFD to stay in parity with the Rust decoder'sString::from_utf16_lossy. Those 2048 code units are the only inputs that do not come back unchanged.Within that scope the guarantee is independent of apostrophe handling, because doubling is a literal-context requirement, not an encoding one. See https://github.com/LTplus-AG/ifc-lite/issues/3445.