Add optimisation for the use of GSImmutableString so that it can make use of the layout of the underlying mutable string that the GSImmutableString is a proxy for.

This commit is contained in:
Richard Frith-Macdonald 2020-11-22 09:55:39 -05:00
parent 7fcd11d6c9
commit 0dbd863dde
2 changed files with 56 additions and 1 deletions

View file

@ -34,10 +34,14 @@
/* This private class is used for the -immutableProxy method in the category /* This private class is used for the -immutableProxy method in the category
* on NSMutableString. * on NSMutableString.
* It is needed for [NSAttributedString-string] and [NSTextStorage-string] * It is needed for [NSAttributedString-string] and [NSTextStorage-string]
*
* NB. The _parent instance variable is private but must *not* be changed
* because it is actually used by GSMutableString code to permit some
* optimisation (see GSString.m).
*/ */
@interface GSImmutableString : NSString @interface GSImmutableString : NSString
{ {
NSString *_parent; NSString *_parent; // Do not change this ivar declaration
} }
- (id) initWithString: (NSString*)parent; - (id) initWithString: (NSString*)parent;
@end @end

View file

@ -641,6 +641,7 @@ static Class GSUnicodeStringClass = 0;
static Class GSUnicodeBufferStringClass = 0; static Class GSUnicodeBufferStringClass = 0;
static Class GSUnicodeSubStringClass = 0; static Class GSUnicodeSubStringClass = 0;
static Class GSUInlineStringClass = 0; static Class GSUInlineStringClass = 0;
static Class GSImmutableStringClass = 0;
static Class GSMutableStringClass = 0; static Class GSMutableStringClass = 0;
static Class NSConstantStringClass = 0; static Class NSConstantStringClass = 0;
@ -651,6 +652,7 @@ static SEL equalSel;
static BOOL (*equalImp)(id, SEL, id); static BOOL (*equalImp)(id, SEL, id);
static SEL hashSel; static SEL hashSel;
static NSUInteger (*hashImp)(id, SEL); static NSUInteger (*hashImp)(id, SEL);
static Ivar immutableIvar;
/* /*
* The setup() function is called when any concrete string class is * The setup() function is called when any concrete string class is
@ -697,6 +699,14 @@ setup(BOOL rerun)
GSMutableStringClass = [GSMutableString class]; GSMutableStringClass = [GSMutableString class];
NSConstantStringClass = [NXConstantString class]; NSConstantStringClass = [NXConstantString class];
/* This comes from the base additions library. The instance variable
* pointing to the original mutable string is not public, but we want
* to use it efficiently.
*/
GSImmutableStringClass = NSClassFromString(@"GSImmutableString");
immutableIvar
= class_getInstanceVariable(GSImmutableStringClass, "_parent");
/* /*
* Cache some selectors and method implementations for * Cache some selectors and method implementations for
* cases where we want to use the implementation * cases where we want to use the implementation
@ -3605,6 +3615,16 @@ transmute(GSStr self, NSString *aString)
BOOL transmute = YES; BOOL transmute = YES;
Class c = object_getClass(aString); // NB aString must not be nil Class c = object_getClass(aString); // NB aString must not be nil
/* If the string is an immutable proxy to a mutable string, we want to
* access the original (which we won't modify) for better performance.
*/
if (c == GSImmutableStringClass)
{
aString = object_getIvar(aString, immutableIvar);
other = (GSStr)aString;
c = object_getClass(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,
@ -5357,6 +5377,37 @@ NSAssert(_flags.owned == 1 && _zone != 0, NSInternalInconsistencyException);
*/ */
other = transmute((GSStr)self, aString); other = transmute((GSStr)self, aString);
if (other == self)
{
if (aRange.length == _count)
{
// NSLog(@"replace all characters with self ... nothing to do");
return;
}
else if (aRange.length == 0
&& (aRange.location == _count || aRange.location == 0))
{
/* We are appending self at the end of self, or we are
* prepending self at the start of the self.
* Either way in effect we double the string, so we can
* do an efficient size extension and copy.
*/
makeHole((GSStr)self, length, length);
if (_flags.wide)
{
memcpy(&_contents.u[length], _contents.u,
length * sizeof(unichar));
}
else
{
memcpy(&_contents.c[length], _contents.c,
length * sizeof(char));
}
_flags.hash = 0;
return;
}
// NSLog(@"replace characters in range with self");
}
if (0 == other || other == self) if (0 == other || other == self)
{ {
/* Either we couldn't get access to the internal of the string /* Either we couldn't get access to the internal of the string