Rewrite -replaceCharactersInRange:withString: to cope with the case where the string argument is the receiver (or some proxy to the receiver etc).

This commit is contained in:
Richard Frith-Macdonald 2020-11-22 08:08:34 -05:00
parent 3bb72398ee
commit 7fcd11d6c9

View file

@ -3592,8 +3592,7 @@ substring_u(GSStr self, NSRange aRange)
return AUTORELEASE((id)o); return AUTORELEASE((id)o);
} }
/* /* Function to examine the given string and see if it is one of our concrete
* Function to examine the given string and see if it is one of our concrete
* string classes. Converts the mutable string (self) from 8-bit to 16-bit * string classes. Converts the mutable string (self) from 8-bit to 16-bit
* representation if necessary in order to contain the data in aString. * representation if necessary in order to contain the data in aString.
* Returns a pointer to aStrings GSStr if aString is a concrete class * Returns a pointer to aStrings GSStr if aString is a concrete class
@ -3608,8 +3607,7 @@ transmute(GSStr self, NSString *aString)
if (self->_flags.wide == 1) if (self->_flags.wide == 1)
{ {
/* /* This is already a unicode string, so we don't need to transmute,
* This is already a unicode string, so we don't need to transmute,
* but we still need to know if the other string is a unicode * but we still need to know if the other string is a unicode
* string whose GSStr we can access directly. * string whose GSStr we can access directly.
*/ */
@ -3622,22 +3620,19 @@ transmute(GSStr self, NSString *aString)
} }
else else
{ {
/* /* This is a string held in the internal 8-bit encoding.
* This is a string held in the internal 8-bit encoding.
*/ */
if (GSObjCIsKindOf(c, GSCStringClass) if (GSObjCIsKindOf(c, GSCStringClass)
|| (c == GSMutableStringClass && other->_flags.wide == 0)) || (c == GSMutableStringClass && other->_flags.wide == 0))
{ {
/* /* The other string is also held in the internal 8-bit encoding,
* The other string is also held in the internal 8-bit encoding,
* so we don't need to transmute, and we can use its GSStr. * so we don't need to transmute, and we can use its GSStr.
*/ */
transmute = NO; transmute = NO;
} }
else if ([aString canBeConvertedToEncoding: internalEncoding] == YES) else if ([aString canBeConvertedToEncoding: internalEncoding] == YES)
{ {
/* /* The other string can be converted to the internal 8-bit encoding,
* The other string can be converted to the internal 8-bit encoding,
* so we don't need to transmute, but we can *not* use its GSStr. * so we don't need to transmute, but we can *not* use its GSStr.
*/ */
transmute = NO; transmute = NO;
@ -3646,8 +3641,7 @@ transmute(GSStr self, NSString *aString)
else if ((c == GSMutableStringClass && other->_flags.wide == 1) else if ((c == GSMutableStringClass && other->_flags.wide == 1)
|| GSObjCIsKindOf(c, GSUnicodeStringClass) == YES) || GSObjCIsKindOf(c, GSUnicodeStringClass) == YES)
{ {
/* /* The other string can not be converted to the internal 8-bit
* The other string can not be converted to the internal 8-bit
* encoding, so we need to transmute, and will then be able to * encoding, so we need to transmute, and will then be able to
* use its GSStr. * use its GSStr.
*/ */
@ -3655,8 +3649,7 @@ transmute(GSStr self, NSString *aString)
} }
else else
{ {
/* /* The other string can not be converted to the internal 8-bit
* The other string can not be converted to the internal 8-bit
* character string, so we need to transmute, but even then we * character string, so we need to transmute, but even then we
* will not be able to use the other strings GSStr because that * will not be able to use the other strings GSStr because that
* string is not a known GSString subclass. * string is not a known GSString subclass.
@ -5336,8 +5329,6 @@ NSAssert(_flags.owned == 1 && _zone != 0, NSInternalInconsistencyException);
- (void) replaceCharactersInRange: (NSRange)aRange - (void) replaceCharactersInRange: (NSRange)aRange
withString: (NSString*)aString withString: (NSString*)aString
{ {
GSStr other = 0;
int offset;
unsigned length = 0; unsigned length = 0;
GS_RANGE_CHECK(aRange, _count); GS_RANGE_CHECK(aRange, _count);
@ -5348,99 +5339,95 @@ NSAssert(_flags.owned == 1 && _zone != 0, NSInternalInconsistencyException);
[NSException raise: NSInvalidArgumentException [NSException raise: NSInvalidArgumentException
format: @"replace characters with non-string"]; format: @"replace characters with non-string"];
} }
else length = [aString length];
{
length = [aString length];
}
} }
offset = length - aRange.length;
/* /* Either we have data to copy into the string (possibly requiring
* We must change into a unicode string (if necessary) *before* * length adjustment first), or we have no data but possibly a gap
* adjusting length and capacity, so that the transmute doesn't * (the range specified) needing to be closed.
* mess up due to any hole in the string etc.
*/ */
if (length > 0) if (length > 0)
{ {
int offset = length - aRange.length;
GSStr other = 0;
/* We must change into a unicode string (if necessary) *before*
* adjusting length and capacity, so that the transmute doesn't
* mess up due to any hole in the string etc.
*/
other = transmute((GSStr)self, aString); other = transmute((GSStr)self, aString);
}
if (offset < 0) if (0 == other || other == self)
{
fillHole((GSStr)self, NSMaxRange(aRange) + offset, -offset);
}
else if (offset > 0)
{
makeHole((GSStr)self, NSMaxRange(aRange), (NSUInteger)offset);
}
if (length > 0)
{
if (_flags.wide == 1)
{ {
if (other == 0) /* Either we couldn't get access to the internal of the string
* to be copied, or we are copying from ourself and need to
* use an intermediate buffer to prevent overwriting.
*/
if (_flags.wide)
{ {
/* GS_BEGINITEMBUF(buf, (length * sizeof(unichar)), unichar);
* Not a cString class - use standard method to get characters.
*/ [aString getCharacters: buf];
[aString getCharacters: &_contents.u[aRange.location]]; if (offset < 0)
{
fillHole((GSStr)self, NSMaxRange(aRange) + offset, -offset);
}
else if (offset > 0)
{
makeHole((GSStr)self, NSMaxRange(aRange), (NSUInteger)offset);
}
memcpy(&_contents.u[aRange.location], buf,
length * sizeof(unichar));
GS_ENDITEMBUF()
} }
else else
{
GS_BEGINITEMBUF(buf, ((length+1) * sizeof(char)), char);
[aString getCString: buf
maxLength: length+1
encoding: internalEncoding];
if (offset < 0)
{
fillHole((GSStr)self, NSMaxRange(aRange) + offset, -offset);
}
else if (offset > 0)
{
makeHole((GSStr)self, NSMaxRange(aRange), (NSUInteger)offset);
}
memcpy(&_contents.c[aRange.location], buf,
length * sizeof(char));
GS_ENDITEMBUF()
}
}
else
{
if (offset < 0)
{
fillHole((GSStr)self, NSMaxRange(aRange) + offset, -offset);
}
else if (offset > 0)
{
makeHole((GSStr)self, NSMaxRange(aRange), (NSUInteger)offset);
}
if (_flags.wide == 1)
{ {
memcpy(&_contents.u[aRange.location], other->_contents.u, memcpy(&_contents.u[aRange.location], other->_contents.u,
length * sizeof(unichar)); length * sizeof(unichar));
} }
}
else
{
if (other == 0)
{
/*
* Since getCString appends a '\0' terminator, we must handle
* that problem in copying data into our buffer. Either by
* saving and restoring the character which would be
* overwritten by the nul, or by getting a character less,
* and fetching the last character separately.
* NB. There is a possibility that aString may be an immutable
* proxy to the receiver, so we must take care to save the
* character before making any changes.
*/
if (aRange.location + length < _count)
{
unsigned char tmp = _contents.c[aRange.location + length];
[aString getCString: (char*)&_contents.c[aRange.location]
maxLength: length+1
encoding: internalEncoding];
_contents.c[aRange.location + length] = tmp;
}
else
{
unsigned int l = length - 1;
unsigned int size = 1;
unichar u = [aString characterAtIndex: l];
unsigned char *dst = &_contents.c[aRange.location + l];
if (l > 0)
{
[aString getCString: (char*)&_contents.c[aRange.location]
maxLength: l+1
encoding: internalEncoding];
}
GSFromUnicode(&dst, &size, &u, 1,
internalEncoding, 0, GSUniStrict);
}
}
else else
{ {
/* memcpy(&_contents.c[aRange.location], other->_contents.c,
* Simply copy cString data from other string into self. length * sizeof(char));
*/
memcpy(&_contents.c[aRange.location], other->_contents.c, length);
} }
} }
_flags.hash = 0; _flags.hash = 0;
} }
else if (aRange.length > 0)
{
fillHole((GSStr)self, aRange.location, aRange.length);
_flags.hash = 0;
}
} }
- (void) setString: (NSString*)aString - (void) setString: (NSString*)aString