Skip to content

Commit bd91ba3

Browse files
committed
[[ Bug 22011 ]] Fix memory leak when using LCB's char iterator
This patch fixes a memory leak which occurs when using LCB's repeat for each char control structure when the loop terminates early, e.g. due to using exit repeat or return within the loop. The reason the leak occurs is that, previously, the underlying iterator required a memory allocation at the start, which required deallocation at the end. However, LCB cannot support such iterator requirements correctly at the moment. To fix the issue, the char iterator implementation has been changed to use the underlying grapheme iterator directly - this does not require any memory allocation to hold state as it works by looking at adjacent codepoints, and thus can be implemented in the same fashion as the other iterators (i.e. by storing the current codeunit offset in the iterator state pointer).
1 parent b5e0e6b commit bd91ba3

File tree

3 files changed

+33
-18
lines changed

3 files changed

+33
-18
lines changed

docs/notes/bugfix-22011.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Fix memory leak when using repeat for each in LCB in early termination cases

libscript/src/module-char.cpp

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -246,28 +246,29 @@ extern "C" MC_DLLEXPORT_DEF void MCCharExecDeleteLastCharOf(MCStringRef& x_targe
246246
// end repeat
247247
// Will result in tChar containing the value it had at the point of end repeat.
248248
extern "C" MC_DLLEXPORT_DEF bool MCCharRepeatForEachChar(void*& x_iterator, MCStringRef& r_iterand, MCStringRef p_string)
249-
{
250-
MCTextChunkIterator *t_iterator;
251-
bool t_first;
252-
t_first = false;
249+
{
250+
uindex_t t_offset;
251+
t_offset = (uindex_t)(uintptr_t)x_iterator;
253252

254-
if ((uintptr_t)x_iterator == 0)
255-
{
256-
t_first = true;
257-
t_iterator = MCChunkCreateTextChunkIterator(p_string, nil, kMCChunkTypeCharacter, nil, nil, kMCStringOptionCompareExact);
258-
}
259-
else
260-
t_iterator = (MCTextChunkIterator *)x_iterator;
253+
uindex_t t_length =
254+
MCStringGetLength(p_string);
255+
if (t_offset == t_length)
256+
return false;
257+
258+
uindex_t t_next =
259+
MCStringGraphemeBreakIteratorAdvance(p_string,
260+
t_offset);
261+
if (t_next == (uindex_t)-1)
262+
t_next = t_length;
261263

262-
if (t_iterator -> Next())
263-
t_iterator -> CopyString(r_iterand);
264-
else
264+
if (!MCStringCopySubstring(p_string,
265+
MCRangeMake(t_offset, t_next - t_offset),
266+
r_iterand))
265267
{
266-
delete t_iterator;
267268
return false;
268269
}
269270

270-
x_iterator = (void *)(t_iterator);
271+
x_iterator = (void *)(uintptr_t)t_next;
271272

272273
return true;
273274
}

tests/lcb/stdlib/char.lcb

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -397,13 +397,26 @@ public handler TestRepeatChar()
397397
variable tCount
398398

399399
put 0 into tCount
400-
401400
repeat for each char tIter in "xyz"
402401
add 1 to tCount
403402
test "repeatchar (iter)" when tIter is char tCount of "xyz"
404403
end repeat
405-
406404
test "repeatchar (count)" when tCount is 3
405+
406+
put 0 into tCount
407+
repeat for each char tIter in "a\u{1d11e}c"
408+
add 1 to tCount
409+
test "repeatchar SMP (iter)" when tIter is char tCount of "a\u{1d11e}c"
410+
end repeat
411+
test "repeatchar SMP (count)" when tCount is 3
412+
413+
put 0 into tCount
414+
repeat for each char tIter in "av\u{fe0e}c"
415+
add 1 to tCount
416+
test "repeatchar VAR (iter)" when tIter is char tCount of "av\u{fe0e}c"
417+
end repeat
418+
test "repeatchar VAR (count)" when tCount is 3
419+
407420
end handler
408421

409422
handler TestReverse_Inplace(in pDesc as String, \

0 commit comments

Comments
 (0)