Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -245,11 +245,12 @@ ${OF_POLL_KERNEL_EVENT_OBSERVER_M} \ ${OF_SELECT_KERNEL_EVENT_OBSERVER_M} \ OFTCPSocketSOCKS5Connector.m SRCS_TAGGED_POINTERS = OFTaggedPointerColor.m \ OFTaggedPointerDate.m \ - OFTaggedPointerNumber.m + OFTaggedPointerNumber.m \ + OFTaggedPointerString.m SRCS_WINDOWS += platform/Windows/OFWin32ConsoleStdIOStream.m \ versioninfo.rc OBJS_EXTRA = exceptions/exceptions.a \ encodings/encodings.a \ Index: src/OFString.m ================================================================== --- src/OFString.m +++ src/OFString.m @@ -47,10 +47,11 @@ #import "OFIRI.h" #import "OFIRIHandler.h" #import "OFLocale.h" #import "OFStream.h" #import "OFSystemInfo.h" +#import "OFTaggedPointerString.h" #import "OFUTF8String.h" #import "OFUTF8String+Private.h" #import "OFGetItemAttributesFailedException.h" #import "OFInitializationFailedException.h" @@ -87,10 +88,18 @@ } placeholder; #if defined(HAVE_STRTOF_L) || defined(HAVE_STRTOD_L) || defined(HAVE_USELOCALE) static locale_t cLocale; #endif + +#ifdef OF_OBJFW_RUNTIME +# if UINTPTR_MAX == UINT64_MAX +# define MAX_TAGGED_POINTER_LENGTH 8 +# else +# define MAX_TAGGED_POINTER_LENGTH 4 +# endif +#endif @interface OFString () - (size_t)of_getCString: (char *)cString maxLength: (size_t)maxLength encoding: (OFStringEncoding)encoding @@ -363,10 +372,23 @@ char *copy = (char *)OFAllocMemory(1, length + 1); memcpy(copy, string, length + 1); return copy; } + +#ifdef OF_OBJFW_RUNTIME +static bool +isASCII(const char *string, size_t length) +{ + uint8_t combined = 0; + + for (size_t i = 0; i < length; i++) + combined |= string[i]; + + return !(combined & ~0x7F); +} +#endif @implementation OFPlaceholderString #ifdef __clang__ /* We intentionally don't call into super, so silence the warning. */ # pragma clang diagnostic push @@ -373,20 +395,34 @@ # pragma clang diagnostic ignored "-Wunknown-pragmas" # pragma clang diagnostic ignored "-Wobjc-designated-initializers" #endif - (instancetype)init { - return (id)[[OFUTF8String alloc] init]; + return (id)@""; } - (instancetype)initWithUTF8String: (const char *)UTF8String { + size_t length = strlen(UTF8String); OFUTF8String *string; - size_t length; void *storage; - length = strlen(UTF8String); + if (length == 0) + return (id)@""; + +#ifdef OF_OBJFW_RUNTIME + if (length <= MAX_TAGGED_POINTER_LENGTH && + isASCII(UTF8String, length)) { + id ret = [OFTaggedPointerString + stringWithASCIIString: UTF8String + length: length]; + + if (ret != nil) + return ret; + } +#endif + string = OFAllocObject([OFUTF8String class], length + 1, 1, &storage); return (id)[string of_initWithUTF8String: UTF8String length: length storage: storage]; @@ -395,10 +431,25 @@ - (instancetype)initWithUTF8String: (const char *)UTF8String length: (size_t)UTF8StringLength { OFUTF8String *string; void *storage; + + if (UTF8StringLength == 0) + return (id)@""; + +#ifdef OF_OBJFW_RUNTIME + if (UTF8StringLength <= MAX_TAGGED_POINTER_LENGTH && + isASCII(UTF8String, UTF8StringLength)) { + id ret = [OFTaggedPointerString + stringWithASCIIString: UTF8String + length: UTF8StringLength]; + + if (ret != nil) + return ret; + } +#endif string = OFAllocObject([OFUTF8String class], UTF8StringLength + 1, 1, &storage); return (id)[string of_initWithUTF8String: UTF8String @@ -425,16 +476,31 @@ } - (instancetype)initWithCString: (const char *)cString encoding: (OFStringEncoding)encoding { - if (encoding == OFStringEncodingUTF8) { + if (encoding == OFStringEncodingUTF8 || + encoding == OFStringEncodingASCII) { + size_t length = strlen(cString); OFUTF8String *string; - size_t length; void *storage; - length = strlen(cString); + if (length == 0) + return (id)@""; + +#ifdef OF_OBJFW_RUNTIME + if (length <= MAX_TAGGED_POINTER_LENGTH && + isASCII(cString, length)) { + id ret = [OFTaggedPointerString + stringWithASCIIString: cString + length: length]; + + if (ret != nil) + return ret; + } +#endif + string = OFAllocObject([OFUTF8String class], length + 1, 1, &storage); return (id)[string of_initWithUTF8String: cString length: length @@ -447,13 +513,29 @@ - (instancetype)initWithCString: (const char *)cString encoding: (OFStringEncoding)encoding length: (size_t)cStringLength { - if (encoding == OFStringEncodingUTF8) { + if (encoding == OFStringEncodingUTF8 || + encoding == OFStringEncodingASCII) { OFUTF8String *string; void *storage; + + if (cStringLength == 0) + return (id)@""; + +#ifdef OF_OBJFW_RUNTIME + if (cStringLength <= MAX_TAGGED_POINTER_LENGTH && + isASCII(cString, cStringLength)) { + id ret = [OFTaggedPointerString + stringWithASCIIString: cString + length: cStringLength]; + + if (ret != nil) + return ret; + } +#endif string = OFAllocObject([OFUTF8String class], cStringLength + 1, 1, &storage); return (id)[string of_initWithUTF8String: cString @@ -479,10 +561,38 @@ } - (instancetype)initWithCharacters: (const OFUnichar *)string length: (size_t)length { + if (length == 0) + return (id)@""; + +#ifdef OF_OBJFW_RUNTIME + if (length <= MAX_TAGGED_POINTER_LENGTH) { + char buffer[MAX_TAGGED_POINTER_LENGTH]; + bool isUnicode = false; + + for (size_t i = 0; i < length; i++) { + if (string[i] >= 0x80) { + isUnicode = true; + break; + } + + buffer[i] = (char)string[i]; + } + + if (!isUnicode) { + id ret = [OFTaggedPointerString + stringWithASCIIString: buffer + length: length]; + + if (ret != nil) + return ret; + } + } +#endif + return (id)[[OFUTF8String alloc] initWithCharacters: string length: length]; } - (instancetype)initWithUTF16String: (const OFChar16 *)string ADDED src/OFTaggedPointerString.h Index: src/OFTaggedPointerString.h ================================================================== --- /dev/null +++ src/OFTaggedPointerString.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2008-2024 Jonathan Schleifer + * + * All rights reserved. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3.0 only, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * version 3.0 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3.0 along with this program. If not, see + * . + */ + +#import "OFString.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface OFTaggedPointerString: OFString ++ (OFTaggedPointerString *)stringWithASCIIString: (const char *)ASCIIString + length: (size_t)length; +@end + +OF_ASSUME_NONNULL_END ADDED src/OFTaggedPointerString.m Index: src/OFTaggedPointerString.m ================================================================== --- /dev/null +++ src/OFTaggedPointerString.m @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2008-2024 Jonathan Schleifer + * + * All rights reserved. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3.0 only, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * version 3.0 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3.0 along with this program. If not, see + * . + */ + +#include "config.h" + +#import "OFTaggedPointerString.h" + +#import "OFOutOfRangeException.h" + +static int stringTag; + +static OF_INLINE size_t +lengthForValue(uintptr_t value) +{ + if (value <= 0x7F) + return 1; + if (value <= 0x3FFF) + return 2; + if (value <= 0x1FFFFF) + return 3; + if (value <= 0xFFFFFFF) + return 4; +#if UINTPTR_MAX == UINT64_MAX + if (value <= 0x7FFFFFFFF) + return 5; + if (value <= 0x3FFFFFFFFFF) + return 6; + if (value <= 0x1FFFFFFFFFFFF) + return 7; + if (value <= 0xFFFFFFFFFFFFFF) + return 8; +#endif + + @throw [OFOutOfRangeException exception]; +} + +@implementation OFTaggedPointerString ++ (void)initialize +{ + if (self == [OFTaggedPointerString class]) + stringTag = objc_registerTaggedPointerClass(self); +} + ++ (OFTaggedPointerString *)stringWithASCIIString: (const char *)ASCIIString + length: (size_t)length +{ + uintptr_t value = 0; + + for (size_t i = 0; i < length; i++) + value |= (uintptr_t)ASCIIString[i] << (i * 7); + + return objc_createTaggedPointer(stringTag, value); +} + +- (size_t)length +{ + return lengthForValue(object_getTaggedPointerValue(self)); +} + +- (OFUnichar)characterAtIndex: (size_t)idx +{ + uintptr_t value = object_getTaggedPointerValue(self); + + if (idx >= lengthForValue(value)) + @throw [OFOutOfRangeException exception]; + + return (value >> (idx * 7)) & 0x7F; +} + +- (unsigned long)hash +{ + uintptr_t value = object_getTaggedPointerValue(self); + unsigned long hash; + + OFHashInit(&hash); + + while (value > 0) { + OFHashAddByte(&hash, 0); + OFHashAddByte(&hash, 0); + OFHashAddByte(&hash, value & 0x7F); + value >>= 7; + } + + OFHashFinalize(&hash); + + return hash; +} + +- (size_t)UTF8StringLength +{ + return self.length; +} + +- (size_t)cStringLengthWithEncoding: (OFStringEncoding)encoding +{ + return self.length; +} + +- (void)getCharacters: (OFUnichar *)buffer inRange: (OFRange)range +{ + uintptr_t value = object_getTaggedPointerValue(self); + + if (range.length > SIZE_MAX - range.location || + range.location + range.length > lengthForValue(value)) + @throw [OFOutOfRangeException exception]; + + for (size_t i = 0; i < range.length; i++) + buffer[i] = (value >> ((i + range.location) * 7)) & 0x7F; +} + +OF_SINGLETON_METHODS +@end