/*
* Copyright (c) 2008-2021 Jonathan Schleifer <js@nil.im>
*
* All rights reserved.
*
* This file is part of ObjFW. It may be distributed under the terms of the
* Q Public License 1.0, which can be found in the file LICENSE.QPL included in
* the packaging of this file.
*
* Alternatively, it may be distributed under the terms of the GNU General
* Public License, either version 2 or 3, which can be found in the file
* LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
* file.
*/
#define OF_CONSTANT_STRING_M
#include "config.h"
#include <stdlib.h>
#include <string.h>
#import "OFConstantString.h"
#import "OFUTF8String.h"
#import "OFInitializationFailedException.h"
#import "OFInvalidEncodingException.h"
#import "OFOutOfMemoryException.h"
#if defined(OF_APPLE_RUNTIME) && !defined(__OBJC2__)
# import <objc/runtime.h>
struct {
struct class *isa, *superclass;
const char *name;
long version, info, instanceSize;
struct ivar_list *iVars;
struct method_list **methodList;
struct cache *cache;
struct protocol_list *protocols;
const char *iVarLayout;
struct class_ext *ext;
} _OFConstantStringClassReference;
#endif
@interface OFConstantUTF8String: OFUTF8String
@end
@implementation OFConstantUTF8String
+ (instancetype)alloc
{
OF_UNRECOGNIZED_SELECTOR
}
- (instancetype)retain
{
return self;
}
- (instancetype)autorelease
{
return self;
}
- (unsigned int)retainCount
{
return OF_RETAIN_COUNT_MAX;
}
- (void)release
{
}
- (void)dealloc
{
OF_DEALLOC_UNSUPPORTED
}
@end
@implementation OFConstantString
+ (void)load
{
#if defined(OF_APPLE_RUNTIME) && !defined(__OBJC2__)
/*
* objc_setFutureClass suddenly stopped working as OFConstantString
* became more complex. So the only solution is to make
* _OFConstantStringClassReference the actual class, but there is no
* objc_initializeClassPair in 10.5. However, objc_allocateClassPair
* does not register the new class with the subclass in the ObjC1
* runtime like the ObjC2 runtime does, so this workaround should be
* fine.
*/
Class class;
if ((class = objc_allocateClassPair(self, "OFConstantString_hack",
0)) == NULL)
@throw [OFInitializationFailedException
exceptionWithClass: self];
memcpy(&_OFConstantStringClassReference, class,
sizeof(_OFConstantStringClassReference));
free(class);
objc_registerClassPair((Class)&_OFConstantStringClassReference);
#endif
}
- (void)finishInitialization
{
@synchronized (self) {
struct of_string_utf8_ivars *ivars;
if ([self isMemberOfClass: [OFConstantUTF8String class]])
return;
ivars = of_alloc_zeroed(1, sizeof(*ivars));
ivars->cString = _cString;
ivars->cStringLength = _cStringLength;
switch (of_string_utf8_check(ivars->cString,
ivars->cStringLength, &ivars->length)) {
case 1:
ivars->isUTF8 = true;
break;
case -1:
free(ivars);
@throw [OFInvalidEncodingException exception];
}
_cString = (char *)ivars;
object_setClass(self, [OFConstantUTF8String class]);
}
}
+ (instancetype)alloc
{
OF_UNRECOGNIZED_SELECTOR
}
- (instancetype)retain
{
return self;
}
- (instancetype)autorelease
{
return self;
}
- (unsigned int)retainCount
{
return OF_RETAIN_COUNT_MAX;
}
- (void)release
{
}
- (void)dealloc
{
OF_DEALLOC_UNSUPPORTED
}
/*
* In all following methods, the constant string is converted to an
* OFConstantUTF8String and the message sent again.
*/
/* From protocol OFCopying */
- (id)copy
{
[self finishInitialization];
return [self copy];
}
/* From protocol OFMutableCopying */
- (id)mutableCopy
{
[self finishInitialization];
return [self mutableCopy];
}
/* From protocol OFComparing, but overridden in OFString */
- (OFComparisonResult)compare: (OFString *)string
{
[self finishInitialization];
return [self compare: string];
}
/* From OFObject, but reimplemented in OFString */
- (bool)isEqual: (id)object
{
[self finishInitialization];
return [self isEqual: object];
}
- (unsigned long)hash
{
[self finishInitialization];
return self.hash;
}
- (OFString *)description
{
[self finishInitialization];
return self.description;
}
/* From OFString */
- (const char *)UTF8String
{
[self finishInitialization];
return self.UTF8String;
}
- (size_t)getCString: (char *)cString_
maxLength: (size_t)maxLength
encoding: (OFStringEncoding)encoding
{
[self finishInitialization];
return [self getCString: cString_
maxLength: maxLength
encoding: encoding];
}
- (const char *)cStringWithEncoding: (OFStringEncoding)encoding
{
[self finishInitialization];
return [self cStringWithEncoding: encoding];
}
- (size_t)length
{
[self finishInitialization];
return self.length;
}
- (size_t)UTF8StringLength
{
[self finishInitialization];
return self.UTF8StringLength;
}
- (size_t)cStringLengthWithEncoding: (OFStringEncoding)encoding
{
[self finishInitialization];
return [self cStringLengthWithEncoding: encoding];
}
- (OFComparisonResult)caseInsensitiveCompare: (OFString *)string
{
[self finishInitialization];
return [self caseInsensitiveCompare: string];
}
- (OFUnichar)characterAtIndex: (size_t)idx
{
[self finishInitialization];
return [self characterAtIndex: idx];
}
- (void)getCharacters: (OFUnichar *)buffer inRange: (OFRange)range
{
[self finishInitialization];
[self getCharacters: buffer inRange: range];
}
- (OFRange)rangeOfString: (OFString *)string
{
[self finishInitialization];
return [self rangeOfString: string];
}
- (OFRange)rangeOfString: (OFString *)string
options: (OFStringSearchOptions)options
{
[self finishInitialization];
return [self rangeOfString: string options: options];
}
- (OFRange)rangeOfString: (OFString *)string
options: (OFStringSearchOptions)options
range: (OFRange)range
{
[self finishInitialization];
return [self rangeOfString: string options: options range: range];
}
- (size_t)indexOfCharacterFromSet: (OFCharacterSet *)characterSet
{
[self finishInitialization];
return [self indexOfCharacterFromSet: characterSet];
}
- (size_t)indexOfCharacterFromSet: (OFCharacterSet *)characterSet
options: (OFStringSearchOptions)options
{
[self finishInitialization];
return [self indexOfCharacterFromSet: characterSet options: options];
}
- (size_t)indexOfCharacterFromSet: (OFCharacterSet *)characterSet
options: (OFStringSearchOptions)options
range: (OFRange)range
{
[self finishInitialization];
return [self indexOfCharacterFromSet: characterSet
options: options
range: range];
}
- (bool)containsString: (OFString *)string
{
[self finishInitialization];
return [self containsString: string];
}
- (OFString *)substringFromIndex: (size_t)idx
{
[self finishInitialization];
return [self substringFromIndex: idx];
}
- (OFString *)substringToIndex: (size_t)idx
{
[self finishInitialization];
return [self substringToIndex: idx];
}
- (OFString *)substringWithRange: (OFRange)range
{
[self finishInitialization];
return [self substringWithRange: range];
}
- (OFString *)stringByAppendingString: (OFString *)string
{
[self finishInitialization];
return [self stringByAppendingString: string];
}
- (OFString *)stringByAppendingFormat: (OFConstantString *)format
arguments: (va_list)arguments
{
[self finishInitialization];
return [self stringByAppendingFormat: format arguments: arguments];
}
- (OFString *)stringByAppendingPathComponent: (OFString *)component
{
[self finishInitialization];
return [self stringByAppendingPathComponent: component];
}
- (OFString *)stringByPrependingString: (OFString *)string
{
[self finishInitialization];
return [self stringByPrependingString: string];
}
- (OFString *)stringByReplacingOccurrencesOfString: (OFString *)string
withString: (OFString *)replacement
{
[self finishInitialization];
return [self stringByReplacingOccurrencesOfString: string
withString: replacement];
}
- (OFString *)stringByReplacingOccurrencesOfString: (OFString *)string
withString: (OFString *)replacement
options: (int)options
range: (OFRange)range
{
[self finishInitialization];
return [self stringByReplacingOccurrencesOfString: string
withString: replacement
options: options
range: range];
}
- (OFString *)uppercaseString
{
[self finishInitialization];
return self.uppercaseString;
}
- (OFString *)lowercaseString
{
[self finishInitialization];
return self.lowercaseString;
}
- (OFString *)capitalizedString
{
[self finishInitialization];
return self.capitalizedString;
}
- (OFString *)stringByDeletingLeadingWhitespaces
{
[self finishInitialization];
return self.stringByDeletingLeadingWhitespaces;
}
- (OFString *)stringByDeletingTrailingWhitespaces
{
[self finishInitialization];
return self.stringByDeletingTrailingWhitespaces;
}
- (OFString *)stringByDeletingEnclosingWhitespaces
{
[self finishInitialization];
return self.stringByDeletingEnclosingWhitespaces;
}
- (bool)hasPrefix: (OFString *)prefix
{
[self finishInitialization];
return [self hasPrefix: prefix];
}
- (bool)hasSuffix: (OFString *)suffix
{
[self finishInitialization];
return [self hasSuffix: suffix];
}
- (OFArray *)componentsSeparatedByString: (OFString *)delimiter
{
[self finishInitialization];
return [self componentsSeparatedByString: delimiter];
}
- (OFArray *)componentsSeparatedByString: (OFString *)delimiter
options: (OFStringSeparationOptions)options
{
[self finishInitialization];
return [self componentsSeparatedByString: delimiter options: options];
}
- (OFArray *)
componentsSeparatedByCharactersInSet: (OFCharacterSet *)characterSet
{
[self finishInitialization];
return [self componentsSeparatedByCharactersInSet: characterSet];
}
- (OFArray *)
componentsSeparatedByCharactersInSet: (OFCharacterSet *)characterSet
options: (OFStringSeparationOptions)options
{
[self finishInitialization];
return [self componentsSeparatedByCharactersInSet: characterSet
options: options];
}
- (OFArray *)pathComponents
{
[self finishInitialization];
return self.pathComponents;
}
- (OFString *)lastPathComponent
{
[self finishInitialization];
return self.lastPathComponent;
}
- (OFString *)stringByDeletingLastPathComponent
{
[self finishInitialization];
return self.stringByDeletingLastPathComponent;
}
- (long long)longLongValue
{
[self finishInitialization];
return self.longLongValue;
}
- (long long)longLongValueWithBase: (int)base
{
[self finishInitialization];
return [self longLongValueWithBase: base];
}
- (unsigned long long)unsignedLongLongValue
{
[self finishInitialization];
return self.unsignedLongLongValue;
}
- (unsigned long long)unsignedLongLongValueWithBase: (int)base
{
[self finishInitialization];
return [self unsignedLongLongValueWithBase: base];
}
- (float)floatValue
{
[self finishInitialization];
return self.floatValue;
}
- (double)doubleValue
{
[self finishInitialization];
return self.doubleValue;
}
- (const OFUnichar *)characters
{
[self finishInitialization];
return self.characters;
}
- (const OFChar16 *)UTF16String
{
[self finishInitialization];
return self.UTF16String;
}
- (const OFChar16 *)UTF16StringWithByteOrder: (OFByteOrder)byteOrder
{
[self finishInitialization];
return [self UTF16StringWithByteOrder: byteOrder];
}
- (size_t)UTF16StringLength
{
[self finishInitialization];
return self.UTF16StringLength;
}
- (const OFChar32 *)UTF32String
{
[self finishInitialization];
return self.UTF32String;
}
- (const OFChar32 *)UTF32StringWithByteOrder: (OFByteOrder)byteOrder
{
[self finishInitialization];
return [self UTF32StringWithByteOrder: byteOrder];
}
- (OFData *)dataWithEncoding: (OFStringEncoding)encoding
{
[self finishInitialization];
return [self dataWithEncoding: encoding];
}
#ifdef OF_HAVE_UNICODE_TABLES
- (OFString *)decomposedStringWithCanonicalMapping
{
[self finishInitialization];
return self.decomposedStringWithCanonicalMapping;
}
- (OFString *)decomposedStringWithCompatibilityMapping
{
[self finishInitialization];
return self.decomposedStringWithCompatibilityMapping;
}
#endif
#ifdef OF_WINDOWS
- (OFString *)stringByExpandingWindowsEnvironmentStrings
{
[self finishInitialization];
return self.stringByExpandingWindowsEnvironmentStrings;
}
#endif
#ifdef OF_HAVE_FILES
- (void)writeToFile: (OFString *)path
{
[self finishInitialization];
[self writeToFile: path];
}
- (void)writeToFile: (OFString *)path encoding: (OFStringEncoding)encoding
{
[self finishInitialization];
[self writeToFile: path encoding: encoding];
}
#endif
- (void)writeToURL: (OFURL *)URL
{
[self finishInitialization];
[self writeToURL: URL];
}
- (void)writeToURL: (OFURL *)URL encoding: (OFStringEncoding)encoding
{
[self finishInitialization];
[self writeToURL: URL encoding: encoding];
}
#ifdef OF_HAVE_BLOCKS
- (void)enumerateLinesUsingBlock: (OFStringLineEnumerationBlock)block
{
[self finishInitialization];
[self enumerateLinesUsingBlock: block];
}
#endif
@end