Skip to content

Commit bfb6d78

Browse files
committed
libfoundation: Add MCStringCopyReversed()
Strings cannot be reversed in-place in the general case. There is no upper limit on the number of codeunits in a grapheme, which means that a temporary buffer of at the same length of the string is required for general correctness. This patch adds a "reversed copy" function which performs a deep copy of the source string, and then constructs the reversed form in the copy by re-reading from the source string via a grapheme break iterator. In practice, the "trivial" flag is not getting set for trivial strings, so all strings that use UTF-16 internal representatino go through the slow grapheme break iterator path.
1 parent 32374f5 commit bfb6d78

File tree

2 files changed

+74
-0
lines changed

2 files changed

+74
-0
lines changed

libfoundation/include/foundation.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2144,6 +2144,11 @@ MC_DLLEXPORT bool MCStringMutableCopySubstringAndRelease(MCStringRef string, MCR
21442144

21452145
/////////
21462146

2147+
// Copy a string, reversing its contents
2148+
MC_DLLEXPORT bool MCStringCopyReversed(MCStringRef string, MCStringRef& r_reversed);
2149+
2150+
/////////
2151+
21472152
// Returns true if the string is mutable
21482153
MC_DLLEXPORT bool MCStringIsMutable(const MCStringRef string);
21492154

libfoundation/src/foundation-string.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1475,6 +1475,75 @@ bool MCStringMutableCopySubstringAndRelease(MCStringRef self, MCRange p_range, M
14751475

14761476
////////////////////////////////////////////////////////////////////////////////
14771477

1478+
MC_DLLEXPORT_DEF
1479+
bool MCStringCopyReversed(MCStringRef self, MCStringRef& r_new_string)
1480+
{
1481+
__MCAssertIsString(self);
1482+
1483+
if (MCStringGetLength(self) < 2)
1484+
return MCStringCopy(self, r_new_string);
1485+
1486+
/* Make a deep copy of the input string. */
1487+
/* TODO[2017-03-29] This could be optimised by _not_ copying the
1488+
* actual string buffer contents during the deep copy, but
1489+
* reversing directly from the input string's internal buffer to
1490+
* the result string's buffer. */
1491+
MCAutoStringRef t_result;
1492+
if (!MCStringMutableCopy(self, &t_result))
1493+
return false;
1494+
if (__MCStringIsIndirect(*t_result))
1495+
if (!__MCStringResolveIndirect(*t_result))
1496+
return false;
1497+
1498+
if (__MCStringIsNative(*t_result))
1499+
{
1500+
/* Native strings have one char_t per grapheme, so they can be
1501+
* reversed in-place. */
1502+
MCInplaceReverse(t_result->native_chars, t_result->char_count);
1503+
}
1504+
else if (__MCStringIsTrivial(*t_result))
1505+
{
1506+
/* Trivial strings have one unichar_t per grapheme, so they
1507+
* can be reversed in-place. */
1508+
MCInplaceReverse(t_result->chars, t_result->char_count);
1509+
}
1510+
else
1511+
{
1512+
/* These strings have some potentially-unbounded number of
1513+
* unichar_t codeunits items per grapheme. In this case, we
1514+
* reverse by iterating over the contents of the original
1515+
* string, copying the graphemes into the new string. */
1516+
MCStringRef t_original = __MCStringIsIndirect(self) ? self->string : self;
1517+
1518+
/* Start of the next grapheme to copy, in the input string */
1519+
uindex_t t_from = 0;
1520+
uindex_t t_length = t_original->char_count;
1521+
1522+
while (t_from < t_length)
1523+
{
1524+
/* Find the end of the current grapheme */
1525+
uindex_t t_grapheme_end =
1526+
MCStringGraphemeBreakIteratorAdvance(t_original, t_from);
1527+
1528+
if (t_grapheme_end == kMCLocaleBreakIteratorDone)
1529+
t_grapheme_end = t_length;
1530+
1531+
MCAssert(t_grapheme_end <= t_length);
1532+
1533+
MCMemoryCopy(t_result->chars + t_length - t_grapheme_end,
1534+
t_original->chars + t_from,
1535+
(t_grapheme_end - t_from) * sizeof(*t_result->chars));
1536+
1537+
t_from = t_grapheme_end;
1538+
}
1539+
}
1540+
1541+
r_new_string = t_result.Take();
1542+
return true;
1543+
}
1544+
1545+
////////////////////////////////////////////////////////////////////////////////
1546+
14781547
MC_DLLEXPORT_DEF
14791548
bool MCStringIsMutable(const MCStringRef self)
14801549
{

0 commit comments

Comments
 (0)