/*
* Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
*
* 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
* <https://www.gnu.org/licenses/>.
*/
#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