From 6e4e463a96e992516da1bc58d86a6f3a29895bb0 Mon Sep 17 00:00:00 2001 From: Brad Arant Date: Sun, 23 Aug 2026 19:18:17 -0700 Subject: [PATCH] Fix MString assignment operators leaving cursor at its old (stale) offset operator=(MString)/operator=(ZString)/operator=(std::string)/operator=(const char*)/operator=(char) all replaced the buffer's content via setSize()+ memcpy() but never reset cursor back to the start - setSize() preserves the *offset* the cursor was already at (correct for its own use, growing a buffer while keeping a read position stable), so a full content replacement left the cursor sitting at whatever offset it had into the *previous* content, not the new content. operator=(double)/operator=(int) already did `cursor = data;` after the same setSize()+memcpy() pattern, confirming this was the intended behavior everywhere - just missing from the other five overloads. This is invisible to JetCore's interpreter, which never reassigns an already-read-from MString (each tag is a freshly parsed C++ object every time its container is scanned, so a keyword's MString is never both read and then reassigned on the same instance). It surfaced in a prototype JET compiler (JetCore/jetc) built this session, which intentionally reuses one persistent keyword slot across loop iterations instead of reconstructing everything from text every pass - exactly the case this bug needed. A loop reusing a slot for its condition worked for two iterations (matching a decoy pattern: the second assignment happened to land the stale cursor at the exact end of the new, same-length content) and threw "operand is not valid" on the third, since a keyword reassigned inside the loop body left the cursor already at eod() from the read. Verified via jstring_test/mstring_test/zstring_test/parser_test (no regressions), JetCore's tests/posttest.sh and the two-process JSON session persistence test (no regressions), and the jetc prototype's two demo templates - both now produce byte-identical output to jet-2.0's interpreter, including a loop that mutates and rereads the same keyword slot every iteration. Co-Authored-By: Claude Sonnet 5 --- MString.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/MString.cpp b/MString.cpp index 303a590..3f83b2f 100644 --- a/MString.cpp +++ b/MString.cpp @@ -108,6 +108,7 @@ namespace coreutils { setSize(value.getLength()); memcpy(data, value.getCursor(), value.getLength()); length = value.getLength(); + cursor = data; onChange(); return *this; } @@ -116,6 +117,7 @@ namespace coreutils { setSize(value.getLength()); memcpy(data, value.getCursor(), value.getLength()); length = value.getLength(); + cursor = data; onChange(); return *this; } @@ -124,22 +126,25 @@ namespace coreutils { setSize(value.length()); memcpy(data, value.c_str(), value.length()); length = value.length(); + cursor = data; onChange(); return *this; } - + MString &MString::operator=(const char *value) { int len = strlen(value); setSize(len); memcpy(data, value, len); + cursor = data; onChange(); return *this; } - + MString &MString::operator=(char value) { int len = 1; setSize(1); *data = value; + cursor = data; onChange(); return *this; }