From d728c938b2624a30f453425751beff2b5771caf0 Mon Sep 17 00:00:00 2001 From: David Chisnall Date: Wed, 16 Nov 2011 20:48:50 +0000 Subject: [PATCH] Store tiny strings (up to 8 ASCII characters) inside the pointer. Strings of this length are fairly common, and this makes them very cheap to store / copy / compare (pointer comparisons test for equality). 8 characters seems to be enough for quite a lot of common uses, such as path components and tokens. There are possibly still some things constructing tiny strings and not using these. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@34181 72102866-910b-0410-8b05-ffd578937521 --- Source/GSString.m | 128 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/Source/GSString.m b/Source/GSString.m index ce6b9269d..6858034ef 100644 --- a/Source/GSString.m +++ b/Source/GSString.m @@ -693,6 +693,103 @@ static void GSStrWiden(GSStr s); static void getCString_u(GSStr self, char *buffer, unsigned int maxLength, NSRange aRange, NSRange *leftoverRange); +#if defined(OBJC_SMALL_OBJECT_SHIFT) && (OBJC_SMALL_OBJECT_SHIFT == 3) +#define TINY_STRING_MASK 4 +BOOL useTinyStrings; +/** + * A GSTinyString is used on 64-bit platforms to store up to 8 ASCII (7-bit) + * characters inside a pointer. Note that a mutable version of this class is + * not possible, because modifying the string changes the pointer value. + * The layout of a tiny string is as follows: + struct + { + uintptr_t char0 :7; + uintptr_t char1 :7; + uintptr_t char2 :7; + uintptr_t char3 :7; + uintptr_t char4 :7; + uintptr_t char5 :7; + uintptr_t char6 :7; + uintptr_t char7 :7; + uintptr_t length :5; + uintptr_t tag :3; + }; + */ +#define TINY_STRING_CHAR(s, x) ((s & (0xFE00000000000000 >> (x*7))) >> (57-(x*7))) +#define TINY_STRING_LENGTH_MASK 0x1f +#define TINY_STRING_LENGTH_SHIFT OBJC_SMALL_OBJECT_SHIFT +@interface GSTinyString : NSString @end +@implementation GSTinyString +- (NSUInteger)length +{ + uintptr_t s = (uintptr_t)self; + return (s >> TINY_STRING_LENGTH_SHIFT) & TINY_STRING_LENGTH_MASK; +} +- (unichar)characterAtIndex: (NSUInteger)anIndex +{ + uintptr_t s = (uintptr_t)self; + NSUInteger length = (s >> TINY_STRING_LENGTH_SHIFT) & TINY_STRING_LENGTH_MASK; + if (anIndex >= length) + { + [NSException raise: NSInvalidArgumentException + format: @"-characterAtIndex: index out of range"]; + } + // Implicit NULL terminator on slightly-too-long strings. + if (anIndex == 8) + { + return '\0'; + } + return TINY_STRING_CHAR(s, anIndex); +} ++ (void)load +{ + useTinyStrings = objc_registerSmallObjectClass_np(self, TINY_STRING_MASK); +} ++ (id)alloc +{ + return (id)TINY_STRING_MASK; +} ++ (id)allocWithZone: (NSZone*)aZone +{ + return (id)TINY_STRING_MASK; +} +- (id)copy +{ + return self; +} +- (id)copyWithZone: (NSZone*)aZone +{ + return self; +} +- (id)retain { return self; } +- (id)autorelease { return self; } +- (oneway void)release { } +@end +/** + * Constructs a tiny string. + */ +static id createTinyString(const char *str, int length) +{ + // No tiny string support detected at run time, give up + if (!useTinyStrings) { return nil; } + // String too long to fit in a pointer, give up + if (length > 9) { return nil; } + // String would fit if the last byte was an implicit 0, but it isn't. + if ((length == 9) && str[8] != '\0') { return nil; } + uintptr_t s = TINY_STRING_MASK; + s |= length << TINY_STRING_LENGTH_SHIFT; + for (unsigned int i = 0 ; i