#import "NSData-HexRepresentation.h"

@implementation NSData (ResKnifeHexRepresentationExtensions)

- (NSString *)hexRepresentation
{
	int currentByte = 0, dataLength = [self length];
	char buffer[dataLength*3 -1], hex1, hex2;
	char *bytes = (char *) [self bytes];
	
	// return empty string if no data
	if(dataLength == 0) return [NSString string];
	
	// calculate bytes
	for(currentByte = 0; currentByte < dataLength; currentByte++)
	{
		hex1 = bytes[currentByte];
		hex2 = bytes[currentByte];
		hex1 >>= 4;
		hex1 &= 0x0F;
		hex2 &= 0x0F;
		hex1 += (hex1 < 10)? 0x30 : 0x37;
		hex2 += (hex2 < 10)? 0x30 : 0x37;
		
		buffer[currentByte*3]    = hex1;
		buffer[currentByte*3 +1] = hex2;
		buffer[currentByte*3 +2] = 0x20;
	}
	
	return [NSString stringWithCString:buffer length:(dataLength*3 -1)];
}

- (NSString *)asciiRepresentation
{
	int currentByte = 0, dataLength = [self length];
	char buffer[dataLength];
	char *bytes = (char *) [self bytes];
	
	// calculate bytes
	for(currentByte = 0; currentByte < dataLength; currentByte++)
	{
		if(bytes[currentByte] >= 0x20 && bytes[currentByte] < 0x7F)
			buffer[currentByte] = bytes[currentByte];
		else buffer[currentByte] = 0x2E;	// full stop								
	}
	
	return [NSString stringWithCString:buffer length:dataLength];
}

- (NSString *)nonLossyAsciiRepresentation
{
	int currentByte = 0, dataLength = [self length];
	char buffer[dataLength];
	char *bytes = (char *) [self bytes];
	
	// calculate bytes
	for(currentByte = 0; currentByte < dataLength; currentByte++)
	{
		if(bytes[currentByte] > 0x20)		// doesn't check for < 0x7F
			buffer[currentByte] = bytes[currentByte];
//		else if(bytes[currentByte] == 0x20)
//			buffer[currentByte] = 0xCA;	// nbsp to stop maligned wraps - doesn't work :(
		else buffer[currentByte] = 0x2E;	// full stop								
	}
	
	return [NSString stringWithCString:buffer length:dataLength];
}

@end

@implementation NSString (ResKnifeHexConversionExtensions)

- (NSData *)dataFromHex
{
	unsigned long actualBytesEncoded = 0;
	unsigned long maxBytesEncoded = floor([self cStringLength] / 2.0);
	const char *bytes = [self cString];
	char *buffer = (char *) malloc(maxBytesEncoded);
	signed char hex1, hex2;
	int i;
	
	for(i = 0; i < maxBytesEncoded * 2;)
	{
		hex1 = bytes[i];
		hex2 = bytes[i+1];
		hex1 -= (hex1 < 'A')? 0x30 : ((hex1 < 'a')? 0x37 : 0x57);   // 0-9 < A-Z < a-z
		hex2 -= (hex2 < 'A')? 0x30 : ((hex2 < 'a')? 0x37 : 0x57);
		if(hex1 & 0xF0 || hex2 & 0xF0) { i++; continue; }			// invalid character found, move forward one byte and try again
		buffer[actualBytesEncoded++] = (hex1 << 4) + hex2;
		i += 2;
	}
	return [NSData dataWithBytesNoCopy:buffer length:actualBytesEncoded freeWhenDone:YES];
}

@end