/* 
   NSConcreteString.m

   Copyright (C) 1995, 1996 Ovidiu Predescu and Mircea Oancea.
   All rights reserved.

   Author: Mircea Oancea <mircea@jupiter.elcom.pub.ro>

   This file is part of libFoundation.

   Permission to use, copy, modify, and distribute this software and its
   documentation for any purpose and without fee is hereby granted, provided
   that the above copyright notice appear in all copies and that both that
   copyright notice and this permission notice appear in supporting
   documentation.

   We disclaim all warranties with regard to this software, including all
   implied warranties of merchantability and fitness, in no event shall
   we be liable for any special, indirect or consequential damages or any
   damages whatsoever resulting from loss of use, data or profits, whether in
   an action of contract, negligence or other tortious action, arising out of
   or in connection with the use or performance of this software.
*/

#include <config.h>

#include <ctype.h>

#include <Foundation/common.h>
#include <Foundation/NSArray.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/NSData.h>
#include <Foundation/NSCoder.h>
#include <Foundation/NSAutoreleasePool.h>
#include <Foundation/NSException.h>
#include <Foundation/exceptions/StringExceptions.h>
#include <Foundation/exceptions/GeneralExceptions.h>

#include <Foundation/NSCharacterSet.h>
#include <Foundation/NSString.h>
#include <Foundation/NSConcreteString.h>

#include <extensions/objc-runtime.h>

/*
 * Abstract classes for 8 bit strings in the default encoding
 */

@implementation NS8BitString

/* Accessing characters	*/

- (void)getCharacters:(unichar*)buffer range:(NSRange)aRange
{
    if (aRange.location + aRange.length > [self cStringLength])
	THROW([[IndexOutOfRangeException alloc] 
	    initWithFormat:@"range (%d,%d) in string %x of length %d",
	    	aRange.location, aRange.length, self, [self cStringLength]]);
    
    {
	int i = 0;
	char* bytes = [self __compact8BitBytes];

	for (i = 0; i < aRange.length; i++)
	    buffer[i] = bytes[i];
    }
}

/* Dividing strings */

- (NSString*)substringWithRange:(NSRange)aRange
{
    [self subclassResponsibility:_cmd];
    return nil;
}

/* Finding characters and substrings */

- (NSRange)rangeOfCharacterFromSet:(NSCharacterSet*)aSet
  options:(unsigned int)mask range:(NSRange)aRange
{
    // ENCODINGS - this code applies to the system's default encoding
    int i = 0;
    NSRange range = {0, 0};
    IMP imp = [aSet methodForSelector:@selector(characterIsMember:)];
    char* bytes = [self __compact8BitBytes];

    if (aRange.location + aRange.length > [self cStringLength])
	THROW([[IndexOutOfRangeException alloc] 
	    initWithFormat:@"range (%d,%d) in string %x of length %d",
	    	aRange.location, aRange.length, self, [self cStringLength]]);

    if (mask & NSBackwardsSearch) {
	for (i = aRange.length - 1; i >= 0; i--) {
	    unichar c = bytes[i];
	    
	    if ((*imp)(aSet, @selector(characterIsMember:), c) ||
		((mask & NSCaseInsensitiveSearch) && 
		 ((islower(c) &&
		  (*imp)(aSet, @selector(characterIsMember:), toupper(c))) ||
		 (isupper(c) &&
		  (*imp)(aSet, @selector(characterIsMember:), tolower(c))))
		 )) {
		    range.location = aRange.location + i;
		    range.length = 1;
		    return range;
		}
	}
    } 
    else {
	for (i = 0; i < aRange.length; i++) {
	    unichar c = bytes[i];

	    if ((*imp)(aSet, @selector(characterIsMember:), c) ||
		((mask & NSCaseInsensitiveSearch) && 
		 ((islower(c) &&
		  (*imp)(aSet, @selector(characterIsMember:), toupper(c))) ||
		 (isupper(c) &&
		  (*imp)(aSet, @selector(characterIsMember:), tolower(c))))
		 )) {
		    range.location = aRange.location + i;
		    range.length = 1;
		    return range;
		}
	}
    }
    
    return range;
}

- (NSRange)rangeOfString:(NSString*)aString
  options:(unsigned int)mask range:(NSRange)aRange
{
    // ENCODINGS - this code applies to the system's default encoding
    NSRange range;
    char* mbytes;
    char* abytes;
    int i, n, a;
    
    if (![aString isKindOfClass:[NS8BitString class]] &&
	![aString isKindOfClass:[NSMutable8BitString class]])
	    return [super rangeOfString:aString options:mask range:aRange];
    
    if (aRange.location + aRange.length > [self cStringLength])
	THROW([[IndexOutOfRangeException alloc] 
	    initWithFormat:@"range (%d,%d) in string %x of length %d",
	    	aRange.location, aRange.length, self, [self cStringLength]]);

    mbytes = [self __compact8BitBytes] + aRange.location;
    abytes = [(id)aString __compact8BitBytes];
    a = [aString cStringLength];
    
    if (!a || aRange.length < a) {
	    return NSMakeRange(0,0);
    }
    
    if (mask & NSAnchoredSearch)  {
	range.location = aRange.location + 
	    ((mask & NSBackwardsSearch) ? aRange.length - a : 0);
	range.length = a;
	
	if ([self compare:aString options:mask range:range] == NSOrderedSame)
	    return range;
	else
	    return NSMakeRange(0,0);
    }
    
    if (mask & NSBackwardsSearch) {	
	if (mask & NSCaseInsensitiveSearch) {
	    // Backward case insensitive
	    char cf = islower(abytes[0]) ? toupper(abytes[0]) : abytes[0];
	    for (n = aRange.length-a; n >= 0; n--) {
		char cm = islower(mbytes[n]) ? toupper(mbytes[n]) : mbytes[n];
		char ca = cf;
		if (cm != ca)
		    continue;
		for (i = 1; i < a; i++) {
		    cm = islower(mbytes[n+i]) ? 
			toupper(mbytes[n+i]) : mbytes[n+i];
		    ca = islower(abytes[i]) ? toupper(abytes[i]) : abytes[i];
		    if (cm != ca)
			break;
		}
		if (i == a) {
		    range.location = aRange.location + n;
		    range.length = a;
		    return range;
		}
	    }
	}
	else {
	    // Backward case sensitive
	    for (n = aRange.length-a; n >= 0; n--) {
		if (mbytes[n] != abytes[0])
		    continue;
		for (i = 1; i < a; i++)
		    if (mbytes[n+i] != abytes[i])
			break;
		if (i == a) {
		    range.location = aRange.location + n;
		    range.length = a;
		    return range;
		}
	    }
	}
    }
    else {
	if (mask & NSCaseInsensitiveSearch) {
	    // Forward case insensitive
	    char cf = islower(abytes[0]) ? toupper(abytes[0]) : abytes[0];
	    for (n = 0; n + a <= aRange.length; n++) {
		char cm = islower(mbytes[n]) ? toupper(mbytes[n]) : mbytes[n];
		char ca = cf;
		
		if (cm != ca)
		    continue;
		for (i = 1; i < a; i++) {
		    cm = islower(mbytes[n+i]) ? 
			toupper(mbytes[n+i]) : mbytes[n+i];
		    ca = islower(abytes[i]) ? toupper(abytes[i]) : abytes[i];
		    if (cm != ca)
			break;
		}
		if (i == a) {
		    range.location = aRange.location + n;
		    range.length = a;
		    return range;
		}
	    }
	}
	else {
	    // Forward case sensitive
	    for (n = 0; n + a <= aRange.length; n++) {
		if (mbytes[n] != abytes[0])
		    continue;
		for (i = 1; i < a; i++)
		    if (mbytes[n+i] != abytes[i])
			break;
		if (i == a) {
		    range.location = aRange.location + n;
		    range.length = a;
		    return range;
		}
	    }
	}
    }
    
    range.location = range.length = 0;
    return range;
}

- (NSComparisonResult)compare:(NSString*)aString
  options:(unsigned int)mask range:(NSRange)aRange
{
    // ENCODINGS - this code applies to the system's default encoding
    char* mbytes;
    char* abytes;
    int i, n, a;

    if (![aString isKindOfClass:[NS8BitString class]] &&
	![aString isKindOfClass:[NSMutable8BitString class]])
	    return [super compare:aString options:mask range:aRange];
    
    if (aRange.location + aRange.length > [self cStringLength])
	THROW([[IndexOutOfRangeException alloc] 
	    initWithFormat:@"range (%d,%d) in string %x of length %d",
	    	aRange.location, aRange.length, self, [self cStringLength]]);

    mbytes = [self __compact8BitBytes] + aRange.location;
    abytes = [(id)aString __compact8BitBytes];
    
    a = [aString cStringLength];
    n = MIN(a, aRange.length);
    
    if (mask & NSCaseInsensitiveSearch) {
	for (i = 0; i < n; i++) {
	    char cm = islower(mbytes[i]) ? toupper(mbytes[i]) : mbytes[i];
	    char ca = islower(abytes[i]) ? toupper(abytes[i]) : abytes[i];

	    if (cm < ca)
		return NSOrderedAscending;
	    if (cm > ca)
		return NSOrderedDescending;
	}
    }
    else {
	for (i = 0; i < n; i++) {
	    if (mbytes[i] < abytes[i])
		return NSOrderedAscending;
	    if (mbytes[i] > abytes[i])
		return NSOrderedDescending;
	}
    }
    
    if (aRange.length < a)
	return NSOrderedAscending;
    if (aRange.length > a)
	return NSOrderedDescending;

    return NSOrderedSame;
}

- (unsigned int)hash
{
    char* bytes = [self __compact8BitBytes];
    unsigned hash = 0, hash2;
    int i, n = [self cStringLength];

    for(i=0; i < n; i++) {
        hash <<= 4;
	// UNICODE - must use a for independent of composed characters
        hash += bytes[i];
        if((hash2 = hash & 0xf0000000))
            hash ^= (hash2 >> 24) ^ hash2;
    }
    
    return hash;
}

/* Getting a shared prefix */

- (NSString*)commonPrefixWithString:(NSString*)aString
  options:(unsigned int)mask
{
    // ENCODINGS - this code applies to the system's default encoding
    NSRange range = {0, 0};
    char* mbytes;
    char* abytes;
    int mLen;
    int aLen;
    int i;

    if (![aString isKindOfClass:[NS8BitString class]] &&
	![aString isKindOfClass:[NSMutable8BitString class]])
	    return [super commonPrefixWithString:aString options:mask];

    mLen = [self cStringLength];
    aLen = [aString length];
    mbytes = [self __compact8BitBytes];
    abytes = [self __compact8BitBytes];

    for (i = 0; i < mLen && i < aLen; i++) {
	char c1 = mbytes[i];
	char c2 = abytes[i];
	
	if ((c1 != c2) && ((mask & NSCaseInsensitiveSearch) && (
	    (islower(c1) && (toupper(c1) != c2)) &&
	    (islower(c2) && (toupper(c2) != c1)))))
		break;
    }
    
    range.length = i;
    return [self substringWithRange:range];
}

/* Changing case */

- (NSString*)capitalizedString
{
    // ENCODINGS - this code applies to the system's default encoding
    int i;
    BOOL f = YES;
    int length = [self cStringLength];
    char* bytes = [self __compact8BitBytes];
    char* chars = Malloc(sizeof(unichar)*(length+1));

    for (i = 0; i < length; i++) {
	char c = bytes[i];
	
	if (isspace(c))
	    f = YES;
	
	if (f) {
	    chars[i] = islower(c) ? toupper(c) : c;
	    f = NO;
	}
	else
	    chars[i] = isupper(c) ? tolower(c) : c;
    }
    chars[i] = 0;
    
    return [[[NSOwned8BitString alloc]
	initWithCString:chars length:length copy:NO] autorelease];
}

- (NSString*)lowercaseString
{
    // ENCODINGS - this code applies to the system's default encoding
    int i;
    int length = [self cStringLength];
    char* bytes = [self __compact8BitBytes];
    char* chars = Malloc(sizeof(unichar)*(length+1));

    for (i = 0; i < length; i++) {
	char c = bytes[i];
	chars[i] = isupper(c) ? tolower(c) : c;
    }
    chars[i] = 0;

    return [[[NSOwned8BitString alloc]
	initWithCString:chars length:length copy:NO] autorelease];
}

- (NSString*)uppercaseString
{
    // ENCODINGS - this code applies to the system's default encoding
    int i;
    int length = [self cStringLength];
    char* bytes = [self __compact8BitBytes];
    char* chars = Malloc(sizeof(unichar)*(length+1));

    for (i = 0; i < length; i++) {
	char c = bytes[i];
	chars[i] = islower(c) ? toupper(c) : c;
    }
    
    chars[i] = 0;

    return [[[NSOwned8BitString alloc]
	initWithCString:chars length:length copy:NO] autorelease];
}

/* Working with C strings */

- (void)getCString:(char*)buffer maxLength:(unsigned int)maxLength
  range:(NSRange)aRange remainingRange:(NSRange*)leftoverRange
{
    char* bytes = [self __compact8BitBytes];
    unsigned int toMove = MIN(maxLength, aRange.length);
    unsigned int cLength = [self cStringLength];
    
    if (aRange.location + aRange.length > cLength)
	THROW([[IndexOutOfRangeException alloc] 
	    initWithFormat:@"range (%d,%d) in string %x of length %d",
	    	aRange.location, aRange.length, self, cLength]);

    if (leftoverRange) {
	leftoverRange->location = aRange.location + toMove;
	leftoverRange->length = cLength - leftoverRange->location;
    }
    memcpy(buffer, bytes+aRange.location, toMove);
}

- (BOOL)writeToFile:(NSString*)path atomically:(BOOL)flag
{
    // UNICODE - remove this
    NSData* data = [NSData dataWithBytes:[self cString]
			   length:[self cStringLength]];
    return writeToFile(path, data, flag);
}

- (Class)classForCoder
{
    return [NS8BitString class];
}

- (void)encodeWithCoder:(NSCoder*)aCoder
{
    const char* bytes = [self __compact8BitBytes];
    int length = [self cStringLength];
    
    [aCoder encodeValueOfObjCType:@encode(int) at:&length];
    [aCoder encodeArrayOfObjCType:@encode(char) count:length at:bytes];
}

- (id)initWithCoder:(NSCoder*)aDecoder
{
    char* bytes;
    int length;

    [aDecoder decodeValueOfObjCType:@encode(int) at:&length];
    bytes = Malloc (length);
    [aDecoder decodeArrayOfObjCType:@encode(char) count:length at:bytes];
    return [[[NSOwned8BitString alloc]
		initWithCString:bytes length:length copy:NO]
		autorelease];
}

static NSString* quoteString (NSString* string)
{
    NSArray* components = [string componentsSeparatedByString:@"\""];
    NSMutableString* result = [NSMutableString stringWithCString:"\""];

    [result appendString:[components componentsJoinedByString:@"\\\""]];
    [result appendString:@"\""];
    return [[result copy] autorelease];
}

- (NSString*)stringRepresentation
{
    const char* cString = [self __compact8BitBytes];
    int i, length = [self cStringLength];

    if(!cString || (cString && *cString == 0))
	return @"\"\"";

    /* Check if the string can be parsed as a STRING token by the property list
       parser. Otherwise we must enclose it in double quotes. */
    if (!isalnum(cString[0]) && cString[0] != '_' && cString[0] != '@'
	&& cString[0] != '#')
	return quoteString (self);

    for(i = 1; i < length; i++)
	if (!isalnum(cString[i]) && cString[i] != '_' && cString[i] != '@'
	    && cString[i] != '#' && cString[i] != '$' && cString[i] != '.')
	    return quoteString (self);

    return self;
}

- (id)copyWithZone:(NSZone*)zone
{
    int length;
    
    if (NSShouldRetainWithZone(self, zone))
	return [self retain];
    
    length = [self cStringLength];
    return [[NSInline8BitString allocForCapacity:length zone:zone]
	initWithCString:[self __compact8BitBytes] length:length];
}

- (id)mutableCopyWithZone:(NSZone*)zone
{
    return [[NSMutableSimple8BitString allocWithZone:zone]
	initWithCString:[self __compact8BitBytes]
	length:[self cStringLength] copy:YES];
}

@end /* NS8BitString */

@implementation NSMutable8BitString

+ (void)initialize
{
    static BOOL initialized = NO;

    if(!initialized) {
	// THREAD
	initialized = YES;
	class_add_behavior(self, [NS8BitString class]);
    }
}

- initWithString:(NSString*)aString
{
    if (![aString isKindOfClass:[NS8BitString class]] &&
	![aString isKindOfClass:[NSMutable8BitString class]])
	    return [super initWithString:(NSString*)aString];
    
    return [self initWithCString:[(id)aString __compact8BitBytes]
	length:[aString cStringLength] copy:YES];
}

- initWithContentsOfFile:(NSString*)path
{
    int fd = open([path cString], O_RDONLY);
    struct stat fstat_buf;
    char* bytes = NULL;

    if((fd == -1) || (fstat(fd, &fstat_buf) == -1))
	return nil;

    bytes = NSZoneMalloc([self zone], fstat_buf.st_size + 1);
    if (read(fd, bytes, fstat_buf.st_size) != fstat_buf.st_size) {
	Free(bytes);
	return nil;
    }
    close(fd);
    bytes[fstat_buf.st_size]=0;
    [self initWithCString:bytes length:fstat_buf.st_size copy:NO];
    return self;
}

- (id)copyWithZone:(NSZone*)zone
{
    int length = [self cStringLength];
    
    return [[NSInline8BitString allocForCapacity:length zone:zone]
	initWithCString:[self __compact8BitBytes] length:length];
}

- (Class)classForCoder
{
    return [NSMutable8BitString class];
}

- (id)initWithCoder:(NSCoder*)aDecoder
{
    char* bytes;
    int length;

    [aDecoder decodeValueOfObjCType:@encode(int) at:&length];
    bytes = Malloc (length);
    [aDecoder decodeArrayOfObjCType:@encode(char) count:length at:bytes];
    return [[[NSMutableSimple8BitString alloc]
		initWithCString:bytes length:length copy:NO]
		autorelease];
}

@end /* NSMutable8BitString */

/*
 * Null terminated CString containing characters inline
 */
 
@implementation NSInline8BitString

+ allocForCapacity:(unsigned int)capacity zone:(NSZone*)zone
{
    NSInline8BitString* str = NSAllocateObject(self, capacity, zone);
    str->cLength = -1;
    return str;
}

- init
{
    if (cLength != -1)
	THROW([[InvalidUseOfMethodException alloc] initWithFormat:
	    @"cannot send %s to non-mutable instance", sel_get_name(_cmd)]);
    cLength = 0;
    cString[0] = 0;
    return self;
}

- initWithCString:(const char*)byteString length:(unsigned int)length
{
    if (cLength != -1)
	THROW([[InvalidUseOfMethodException alloc] initWithFormat:
	    @"cannot send %s to non-mutable instance", sel_get_name(_cmd)]);
    cLength = length;
    memcpy(cString, byteString, length);
    cString[length] = 0;
    return self;
}

- (void)dealloc
{
    [super dealloc];
}

- (const char*)cString
{
    return cString;
}

- (unsigned int)cStringLength
{
    return (cLength == -1) ? 0 : cLength;
}

- (unsigned int)length
{
    return (cLength == -1) ? 0 : cLength;
}

- (unichar)characterAtIndex:(unsigned int)index
{
    if (cLength == -1 || index >= cLength)
	THROW([[IndexOutOfRangeException alloc] 
	    initWithFormat:@"index %d out of range in string %x of length %d",
	    	index, self, cLength]);
    // ENCODING
    return cString[index];
}

- (NSString*)substringWithRange:(NSRange)aRange
{
    if (aRange.location + aRange.length > cLength)
	THROW([[IndexOutOfRangeException alloc] 
	    initWithFormat:@"range (%d,%d) in string %x of length %d",
	    	aRange.location, aRange.length, self, cLength]);

    if (aRange.length == 0)
	return @"";

    return [[[NSRange8BitString alloc] 
	initWithString:self bytes:cString+aRange.location length:aRange.length]
	autorelease];
}

- (char*)__compact8BitBytes
{
    return cString;
}

@end /* NSInline8BitString */

/*
 * String containing non-owned, zero termintated c-string
 */

@implementation NSNonOwned8BitString

- init
{
    if (cLength || cString)
	THROW([[InvalidUseOfMethodException alloc] initWithFormat:
	    @"cannot send %s to non-mutable instance", sel_get_name(_cmd)]);
    cLength = 0;
    cString = "";
    return self;
}

- initWithCString:(char*)byteString length:(unsigned int)length copy:(BOOL)flag
{
    if (cLength || cString)
	THROW([[InvalidUseOfMethodException alloc] initWithFormat:
	    @"cannot send %s to non-mutable instance", sel_get_name(_cmd)]);
    if (flag)
	THROW([[InvalidUseOfMethodException alloc] initWithFormat:
	    @"cannot send %s with flag set to YES to %@ to instance", 
	    sel_get_name(_cmd), NSStringFromClass([self class])]);
    
    cLength = length;
    cString = byteString;
    return self;
}

- (const char*)cString
{
    return cString;
}

- (unsigned int)cStringLength
{
    return cLength;
}

- (unsigned int)length
{
    return cLength;
}

- (unichar)characterAtIndex:(unsigned int)index
{
    if (cLength == -1 || index >= cLength)
	THROW([[IndexOutOfRangeException alloc] 
	    initWithFormat:@"index %d out of range in string %x of length %d",
	    	index, self, cLength]);
    // ENCODING
    return cString[index];
}

- (NSString*)substringWithRange:(NSRange)aRange
{
    if (aRange.location + aRange.length > cLength)
	THROW([[IndexOutOfRangeException alloc] 
	    initWithFormat:@"range (%d,%d) in string %x of length %d",
	    	aRange.location, aRange.length, self, cLength]);

    if (aRange.length == 0)
	return @"";

    return [[[NSRange8BitString alloc] 
	initWithString:self bytes:cString+aRange.location length:aRange.length]
	autorelease];
}

- (char*)__compact8BitBytes
{
    return cString;
}

@end /* NSNonOwned8BitString */

@implementation NSOwned8BitString

- init
{
    if (cLength || cString)
	THROW([[InvalidUseOfMethodException alloc] initWithFormat:
	    @"cannot send %s to non-mutable instance", sel_get_name(_cmd)]);

    cLength = 0;
    cString = NSZoneMalloc([self zone], sizeof(char));
    cString[0] = 0;
    return self;
}

- initWithCString:(char*)byteString length:(unsigned int)length copy:(BOOL)flag
{
    if (cLength || cString)
	THROW([[InvalidUseOfMethodException alloc] initWithFormat:
	    @"cannot send %s to non-mutable instance", sel_get_name(_cmd)]);

    cLength = length;
    if (flag) {
	cString = NSZoneMalloc([self zone], sizeof(char)*(length+1));
	memcpy(cString, byteString, length);
	cString[cLength] = 0;
    }
    else
	cString = byteString;
    return self;
}

- (void)dealloc
{
    Free(cString);
    [super dealloc];
}

@end /* NSOwned8BitString */

@implementation NXConstantString

- (oneway void)release
{
}

- retain
{
    return self;
}

- (unsigned)retainCount
{
    return 1;
}

- autorelease
{
    return self;
}

- (void)dealloc
{
    [self shouldNotImplement:_cmd];
}

@end /* NXConstantString */

@implementation NSNonOwnedOpen8BitString

- (const char*)cString
{
    char* str = NSZoneMalloc([self zone], sizeof(char)*(cLength + 1));
    
    memcpy(str, cString, cLength);
    str[cLength] = 0;
    [NSAutoreleasedPointer autoreleasePointer:str];
    return str;
}

@end /* NSNonOwnedOpen8BitString */

@implementation NSOwnedOpen8BitString

- initWithCString:(char*)byteString length:(unsigned int)length copy:(BOOL)flag
{
    if (cLength || cString)
	THROW([[InvalidUseOfMethodException alloc] initWithFormat:
	    @"cannot send %s to non-mutable instance", sel_get_name(_cmd)]);

    cLength = length;
    if (flag) {
	cString = NSZoneMalloc([self zone], sizeof(char)*(length));
	memcpy(cString, byteString, length);
    }
    else
	cString = byteString;
    return self;
}

- (const char*)cString
{
    char* str = Malloc(sizeof(char)*(cLength + 1));

    memcpy(str, cString, cLength);
    str[cLength] = 0;
    [NSAutoreleasedPointer autoreleasePointer:str];
    return str;
}

@end /* NSOwnedOpen8BitString */

@implementation NSRange8BitString

- initWithString:(NSString*)aParent 
  bytes:(char*)bytes length:(unsigned int)length
{
    if (cLength || cString)
	THROW([[InvalidUseOfMethodException alloc] initWithFormat:
	    @"cannot send %s to non-mutable instance", sel_get_name(_cmd)]);

    cString = bytes;
    cLength = length;
    parent = [aParent retain];
    return self;
}

- (void)dealloc
{
    [parent release];
    [super dealloc];
}

- (NSString*)substringWithRange:(NSRange)aRange
{
    if (aRange.location + aRange.length > cLength)
	THROW([[IndexOutOfRangeException alloc] 
	    initWithFormat:@"range (%d,%d) in string %x of length %d",
	    	aRange.location, aRange.length, self, cLength]);

    if (aRange.length == 0)
	return @"";

    return [[[NSRange8BitString alloc] 
	    initWithString:parent
	    bytes:cString+aRange.location 
	    length:aRange.length]
	autorelease];
}

@end /* NSRange8BitString */

@implementation NSMutableSimple8BitString

- init
{
    cLength = 0;
    return self;
}

- initWithCapacity:(unsigned int)capacity
{
    cLength = 0;
    cCapacity = capacity;
    Free(cString);
    cString = NSZoneMalloc([self zone], sizeof(char)*capacity);
    return self;
}

- initWithCString:(char*)byteString length:(unsigned int)length copy:(BOOL)flag
{
    if (flag) {
	if (cCapacity < length) {
	    Free(cString);
	    cString = NSZoneMalloc([self zone], sizeof(char)*length);
	    cCapacity = length;
	}
	cLength = length;
	memcpy(cString, byteString, length);
    }
    else {
	Free(cString);
	cString = byteString;
	cLength = cCapacity = length;
    }
    return self;
}

- (void)dealloc
{
    Free(cString);
    [super dealloc];
}

- (const char*)cString
{
    char* str = Malloc(sizeof(char)*(cLength + 1));

    memcpy(str, cString, cLength);
    str[cLength] = 0;
    [NSAutoreleasedPointer autoreleasePointer:str];
    return str;
}

- (unsigned int)cStringLength
{
    return cLength;
}

- (unsigned int)length
{
    return cLength;
}

- (unichar)characterAtIndex:(unsigned int)index
{
    if (index >= cLength)
	THROW([[IndexOutOfRangeException alloc] 
	    initWithFormat:@"index %d out of range in string %x of length %d",
	    	index, self, cLength]);
    // ENCODING
    return cString[index];
}

- (NSString*)substringWithRange:(NSRange)aRange
{
    if (aRange.location + aRange.length > cLength)
	THROW([[IndexOutOfRangeException alloc] 
	    initWithFormat:@"range (%d,%d) in string %x of length %d",
	    	aRange.location, aRange.length, self, cLength]);

    if (aRange.length == 0)
	return @"";

    return [[[NSInline8BitString allocForCapacity:aRange.length zone:NULL] 
	initWithCString:cString+aRange.location length:aRange.length]
	autorelease];
}

- (char*)__compact8BitBytes
{
    return cString;
}

- (void)replaceCharactersInRange:(NSRange)aRange
  withString:(NSString*)aString
{
    int strLength, i, iFrom, iTo, count;
    
    /* check range */
    if (aRange.location + aRange.length > cLength)
	THROW([[IndexOutOfRangeException alloc] 
	    initWithFormat:@"range (%d,%d) in string %x of length %d",
	    	aRange.location, aRange.length, self, cLength]);
    
    strLength = [aString cStringLength];
    /* if range is smaller than new string enough room */
    if (aRange.length >= strLength) {
	iFrom = aRange.location + aRange.length;
	iTo = aRange.location + strLength;
	count = cLength - iFrom;
	if (iFrom != iTo)
	    for (i = 0; i < count; i++)
		cString[iTo + i] = cString[iFrom + i];
	cLength = cLength - aRange.length + strLength;
    }
    /* if range greater than new string */
    else {
	/* is there enough free space */
	if ((cCapacity - cLength) < (strLength - aRange.length)) {
	    /* reallocation grow strategy */
	    cCapacity += MAX(cCapacity, 
		(strLength - aRange.length) - (cCapacity - cLength));
	    cString = NSZoneRealloc(
		cString ? NSZoneFromPointer(cString) : [self zone],
		cString, sizeof(char)*cCapacity);
	}
	/* we have enough size */
	count = cLength - aRange.location - aRange.length;
	iFrom = cLength - 1;
	iTo = cLength + strLength - aRange.length - 1;
	cLength += strLength - aRange.length;
	for (i = 0; i < count; i++)
	    cString[iTo - i] = cString[iFrom - i];
    }
    
    /* move string in its position */
    if (strLength) {
	NSRange range = {0, strLength};
	[aString getCString:cString + aRange.location 
	    maxLength:strLength
	    range:range
	    remainingRange:NULL];
    }
}

@end /* NSMutableSimple8BitString */
