canonize static method
Returns keyExpr rewritten into canon form.
A pure transform: no handle, no native resource, no lifecycle. On input that is already canon it returns the same bytes back, unchanged.
The rewrite is canon's, and it is these four rules:
- a run of contiguous
$*collapses to one —hello/foo$*$*/barbecomeshello/foo$*/bar; - contiguous
**chunks collapse to one —hello/**/**becomeshello/**; - a chunk that is exactly
$*becomes*—hello/$*/byebecomeshello/*/bye; **/*reorders to*/**—hello/**/*becomeshello/*/**.
Throws ZenohException only on genuine invalidity — an expression
canon rejects even after the rewrite, such as a//b or the empty
string. It never throws on a merely non-canon expression: rewriting
those is the whole point of the method, and they are exactly the inputs
the strict KeyExpr constructor turns away.
KeyExpr(KeyExpr.canonize(s)) therefore constructs for every s this
returns, and canonizing twice returns the same bytes as canonizing once.
A String-to-String transform — nothing is constructed and nothing is
sent — so non-ASCII input is safe here, and the rewrite operates on
/-delimited chunks and ASCII metacharacters, never splitting a
multi-byte character. Using the result with a session is a different
question: see the non-ASCII warning in this class's documentation.
keyExpr is transformed as bytes, with the encode-boundary caveat
described there.
Implementation
static String canonize(String keyExpr) {
final encoded = utf8.encode(keyExpr);
// 🔴 Canon rewrites this buffer IN PLACE through a `&mut str`. It is the
// marshalling layer's own malloc'd process-heap block -- never
// Dart-managed memory, never a string literal, never read-only -- which
// is the whole of the SEGFAULT guard, and it is non-NULL at length 0.
final buf = _copyToNative(encoded);
// Canon writes the canonized length back here, on success only. It is the
// ONLY truth about the result's extent: the rewrite can preserve the
// length as easily as shorten it, so nothing below reuses `encoded`'s.
final lenCell = calloc<Size>();
try {
lenCell.value = encoded.length;
final rc = bindings.zd_keyexpr_canonize(buf.cast(), lenCell);
if (rc != 0) {
throw ZenohException('Invalid key expression: "$keyExpr"', rc);
}
return buf.cast<Utf8>().toDartString(length: lenCell.value);
} finally {
malloc.free(buf);
calloc.free(lenCell);
}
}