374 lines
12 KiB
C
374 lines
12 KiB
C
//
|
|
// TSICTString.c
|
|
// TSITString
|
|
//
|
|
// Created by Travis Tilley on 9/27/11.
|
|
//
|
|
|
|
#include "TSICTString.h"
|
|
|
|
|
|
const char* const TNetstringTypes = ",#^!~}]Z";
|
|
const char* const OTNetstringTypes = ",#^!~{[Z";
|
|
const UInt8 TNetstringSeparator = ':';
|
|
|
|
TSITStringFormat TSITStringDefaultFormat = kTSITStringFormatTNetstring;
|
|
|
|
static const CFRange BeginningRange = {0,0};
|
|
|
|
static CFTypeID kCFDataTypeID = -1UL;
|
|
static CFTypeID kCFStringTypeID = -1UL;
|
|
static CFTypeID kCFNumberTypeID = -1UL;
|
|
static CFTypeID kCFBooleanTypeID = -1UL;
|
|
static CFTypeID kCFNullTypeID = -1UL;
|
|
static CFTypeID kCFArrayTypeID = -1UL;
|
|
static CFTypeID kCFDictionaryTypeID = -1UL;
|
|
|
|
|
|
__attribute__((constructor)) void Init_TSICTString(void)
|
|
{
|
|
kCFDataTypeID = CFDataGetTypeID();
|
|
kCFStringTypeID = CFStringGetTypeID();
|
|
kCFNumberTypeID = CFNumberGetTypeID();
|
|
kCFBooleanTypeID = CFBooleanGetTypeID();
|
|
kCFNullTypeID = CFNullGetTypeID();
|
|
kCFArrayTypeID = CFArrayGetTypeID();
|
|
kCFDictionaryTypeID = CFDictionaryGetTypeID();
|
|
}
|
|
|
|
|
|
void TSICTStringSetDefaultFormat(TSITStringFormat format)
|
|
{
|
|
if (format == kTSITStringFormatDefault) {
|
|
TSITStringDefaultFormat = kTSITStringFormatTNetstring;
|
|
} else {
|
|
TSITStringDefaultFormat = format;
|
|
}
|
|
}
|
|
|
|
TSITStringFormat TSICTStringGetDefaultFormat(void)
|
|
{
|
|
return TSITStringDefaultFormat;
|
|
}
|
|
|
|
|
|
void TSICTStringDestroy(TStringIRep* rep)
|
|
{
|
|
CFRelease(rep->data);
|
|
free(rep->length);
|
|
free(rep);
|
|
}
|
|
|
|
|
|
static inline TStringIRep* TSICTStringCreateWithDataOfTypeAndFormat(CFDataRef data, TSITStringTag type, TSITStringFormat format)
|
|
{
|
|
if (format == kTSITStringFormatDefault) {
|
|
format = TSICTStringGetDefaultFormat();
|
|
}
|
|
|
|
TStringIRep* rep = calloc(1, sizeof(TStringIRep));
|
|
rep->data = CFDataCreateCopy(kCFAllocatorDefault, data);
|
|
rep->type = type;
|
|
rep->format = format;
|
|
rep->length = calloc(10, sizeof(char));
|
|
|
|
CFIndex len = CFDataGetLength(rep->data);
|
|
if (snprintf(rep->length, 10, "%lu", len)) {
|
|
return rep;
|
|
} else {
|
|
TSICTStringDestroy(rep);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static inline CFDataRef TSICTStringCreateDataFromIntermediateRepresentation(TStringIRep* rep)
|
|
{
|
|
CFIndex len = CFDataGetLength(rep->data);
|
|
CFMutableDataRef buffer = CFDataCreateMutableCopy(kCFAllocatorDefault, (len + 12), rep->data);
|
|
UInt8* bufferBytes = CFDataGetMutableBytePtr(buffer);
|
|
|
|
size_t prefixLength = strlen(rep->length) + 1;
|
|
CFDataReplaceBytes(buffer, BeginningRange, (const UInt8*)rep->length, (CFIndex)prefixLength);
|
|
|
|
if (rep->format == kTSITStringFormatTNetstring) {
|
|
const UInt8 ftag = (UInt8)TNetstringTypes[rep->type];
|
|
CFDataAppendBytes(buffer, &ftag, 1);
|
|
bufferBytes[(prefixLength - 1)] = TNetstringSeparator;
|
|
} else if (rep->format == kTSITStringFormatOTNetstring) {
|
|
const UInt8 ftag = (UInt8)OTNetstringTypes[rep->type];
|
|
bufferBytes[(prefixLength - 1)] = ftag;
|
|
}
|
|
|
|
CFDataRef dataRep = CFDataCreateCopy(kCFAllocatorDefault, buffer);
|
|
CFRelease(buffer);
|
|
|
|
return dataRep;
|
|
}
|
|
|
|
static inline CFStringRef TSICTStringCreateStringFromIntermediateRepresentation(TStringIRep* rep)
|
|
{
|
|
CFDataRef data = TSICTStringCreateDataFromIntermediateRepresentation(rep);
|
|
CFStringRef string = CFStringCreateFromExternalRepresentation(kCFAllocatorDefault, data, kCFStringEncodingUTF8);
|
|
CFRelease(data);
|
|
return string;
|
|
}
|
|
|
|
static inline void TSICTStringAppendObjectToMutableDataWithFormat(CFTypeRef object, CFMutableDataRef buffer, TSITStringFormat format)
|
|
{
|
|
if (object == NULL) {
|
|
object = kCFNull;
|
|
}
|
|
|
|
CFRetain(object);
|
|
|
|
TStringIRep* objRep = TSICTStringCreateWithObjectAndFormat(object, format);
|
|
CFDataRef objData = TSICTStringCreateDataFromIntermediateRepresentation(objRep);
|
|
CFDataAppendBytes(buffer, (CFDataGetBytePtr(objData)), CFDataGetLength(objData));
|
|
CFRelease(objData);
|
|
TSICTStringDestroy(objRep);
|
|
|
|
CFRelease(object);
|
|
}
|
|
|
|
static void ArrayBufferAppendCallback(const void* item, void* context)
|
|
{
|
|
TStringCollectionCallbackContext* cx = (TStringCollectionCallbackContext*)context;
|
|
CFMutableDataRef buffer = cx->buffer;
|
|
TSITStringFormat format = cx->format;
|
|
|
|
TSICTStringAppendObjectToMutableDataWithFormat(item, buffer, format);
|
|
}
|
|
|
|
static void DictionaryBufferAppendCallback(const void* key, const void* value, void* context)
|
|
{
|
|
TStringCollectionCallbackContext* cx = (TStringCollectionCallbackContext*)context;
|
|
CFMutableDataRef buffer = cx->buffer;
|
|
TSITStringFormat format = cx->format;
|
|
|
|
TSICTStringAppendObjectToMutableDataWithFormat(key, buffer, format);
|
|
TSICTStringAppendObjectToMutableDataWithFormat(value, buffer, format);
|
|
}
|
|
|
|
|
|
CFDataRef TSICTStringCreateRenderedData(TStringIRep* rep)
|
|
{
|
|
return TSICTStringCreateDataFromIntermediateRepresentation(rep);
|
|
}
|
|
|
|
CFDataRef TSICTStringCreateRenderedDataFromObjectWithFormat(CFTypeRef object, TSITStringFormat format)
|
|
{
|
|
if (object == NULL) {
|
|
object = kCFNull;
|
|
}
|
|
|
|
CFRetain(object);
|
|
|
|
TStringIRep* rep = TSICTStringCreateWithObjectAndFormat(object, format);
|
|
CFDataRef data = TSICTStringCreateDataFromIntermediateRepresentation(rep);
|
|
|
|
TSICTStringDestroy(rep);
|
|
CFRelease(object);
|
|
|
|
return data;
|
|
}
|
|
|
|
CFStringRef TSICTStringCreateRenderedString(TStringIRep* rep)
|
|
{
|
|
return TSICTStringCreateStringFromIntermediateRepresentation(rep);
|
|
}
|
|
|
|
CFStringRef TSICTStringCreateRenderedStringFromObjectWithFormat(CFTypeRef object, TSITStringFormat format)
|
|
{
|
|
if (object == NULL) {
|
|
object = kCFNull;
|
|
}
|
|
|
|
CFRetain(object);
|
|
|
|
TStringIRep* rep = TSICTStringCreateWithObjectAndFormat(object, format);
|
|
CFStringRef string = TSICTStringCreateStringFromIntermediateRepresentation(rep);
|
|
|
|
TSICTStringDestroy(rep);
|
|
CFRelease(object);
|
|
|
|
return string;
|
|
}
|
|
|
|
|
|
TStringIRep* TSICTStringCreateWithObjectAndFormat(CFTypeRef object, TSITStringFormat format)
|
|
{
|
|
if (object == NULL) {
|
|
return TSICTStringCreateNullWithFormat(format);
|
|
}
|
|
CFRetain(object);
|
|
|
|
CFTypeID cfType = CFGetTypeID(object);
|
|
TStringIRep* rep = NULL;
|
|
|
|
if (cfType == kCFDataTypeID) {
|
|
rep = TSICTStringCreateWithDataOfTypeAndFormat(object, kTSITStringTagString, format);
|
|
} else if (cfType == kCFStringTypeID) {
|
|
rep = TSICTStringCreateWithStringAndFormat(object, format);
|
|
} else if (cfType == kCFNumberTypeID) {
|
|
rep = TSICTStringCreateWithNumberAndFormat(object, format);
|
|
} else if (cfType == kCFBooleanTypeID) {
|
|
if (CFBooleanGetValue(object)) {
|
|
rep = TSICTStringCreateTrueWithFormat(format);
|
|
} else {
|
|
rep = TSICTStringCreateFalseWithFormat(format);
|
|
}
|
|
} else if (cfType == kCFNullTypeID) {
|
|
rep = TSICTStringCreateNullWithFormat(format);
|
|
} else if (cfType == kCFArrayTypeID) {
|
|
rep = TSICTStringCreateWithArrayAndFormat(object, format);
|
|
} else if (cfType == kCFDictionaryTypeID) {
|
|
rep = TSICTStringCreateWithDictionaryAndFormat(object, format);
|
|
} else {
|
|
rep = TSICTStringCreateInvalidWithFormat(format);
|
|
}
|
|
|
|
CFRelease(object);
|
|
return rep;
|
|
}
|
|
|
|
TStringIRep* TSICTStringCreateWithStringAndFormat(CFStringRef string, TSITStringFormat format)
|
|
{
|
|
CFRetain(string);
|
|
CFDataRef data = CFStringCreateExternalRepresentation(kCFAllocatorDefault, string, kCFStringEncodingUTF8, '?');
|
|
TStringIRep* rep = TSICTStringCreateWithDataOfTypeAndFormat(data, kTSITStringTagString, format);
|
|
CFRelease(data);
|
|
CFRelease(string);
|
|
return rep;
|
|
}
|
|
|
|
TStringIRep* TSICTStringCreateWithNumberAndFormat(CFNumberRef number, TSITStringFormat format)
|
|
{
|
|
CFRetain(number);
|
|
TSITStringTag tag = kTSITStringTagNumber;
|
|
CFDataRef data;
|
|
CFNumberType numType = CFNumberGetType(number);
|
|
|
|
switch(numType) {
|
|
case kCFNumberCharType:
|
|
{
|
|
int value;
|
|
if (CFNumberGetValue(number, kCFNumberIntType, &value)) {
|
|
if (value == 0 || value == 1) {
|
|
tag = kTSITStringTagBool;
|
|
} else {
|
|
tag = kTSITStringTagString;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case kCFNumberFloat32Type:
|
|
case kCFNumberFloat64Type:
|
|
case kCFNumberFloatType:
|
|
case kCFNumberDoubleType:
|
|
{
|
|
tag = kTSITStringTagFloat;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (tag == kTSITStringTagBool) {
|
|
bool value;
|
|
CFNumberGetValue(number, kCFNumberIntType, &value);
|
|
if (value) {
|
|
data = CFDataCreate(kCFAllocatorDefault, (UInt8*)"true", 4);
|
|
} else {
|
|
data = CFDataCreate(kCFAllocatorDefault, (UInt8*)"false", 5);
|
|
}
|
|
} else if (tag == kTSITStringTagFloat) {
|
|
char buf[32];
|
|
char *p, *e;
|
|
double value;
|
|
|
|
CFNumberGetValue(number, numType, &value);
|
|
sprintf(buf, "%#.15g", value);
|
|
|
|
e = buf + strlen(buf);
|
|
p = e;
|
|
while (p[-1]=='0' && ('0' <= p[-2] && p[-2] <= '9')) {
|
|
p--;
|
|
}
|
|
memmove(p, e, strlen(e)+1);
|
|
|
|
data = CFDataCreate(kCFAllocatorDefault, (UInt8*)buf, (CFIndex)strlen(buf));
|
|
} else {
|
|
char buf[32];
|
|
SInt64 value;
|
|
CFNumberGetValue(number, numType, &value);
|
|
sprintf(buf, "%lli", value);
|
|
data = CFDataCreate(kCFAllocatorDefault, (UInt8*)buf, (CFIndex)strlen(buf));
|
|
}
|
|
|
|
TStringIRep* rep = TSICTStringCreateWithDataOfTypeAndFormat(data, tag, format);
|
|
CFRelease(data);
|
|
CFRelease(number);
|
|
return rep;
|
|
}
|
|
|
|
TStringIRep* TSICTStringCreateTrueWithFormat(TSITStringFormat format)
|
|
{
|
|
CFDataRef data = CFDataCreate(kCFAllocatorDefault, (UInt8*)"true", 4);
|
|
TStringIRep* rep = TSICTStringCreateWithDataOfTypeAndFormat(data, kTSITStringTagBool, format);
|
|
CFRelease(data);
|
|
return rep;
|
|
}
|
|
|
|
TStringIRep* TSICTStringCreateFalseWithFormat(TSITStringFormat format)
|
|
{
|
|
CFDataRef data = CFDataCreate(kCFAllocatorDefault, (UInt8*)"false", 5);
|
|
TStringIRep* rep = TSICTStringCreateWithDataOfTypeAndFormat(data, kTSITStringTagBool, format);
|
|
CFRelease(data);
|
|
return rep;
|
|
}
|
|
|
|
TStringIRep* TSICTStringCreateNullWithFormat(TSITStringFormat format)
|
|
{
|
|
CFDataRef data = CFDataCreate(kCFAllocatorDefault, NULL, 0);
|
|
TStringIRep* rep = TSICTStringCreateWithDataOfTypeAndFormat(data, kTSITStringTagNull, format);
|
|
CFRelease(data);
|
|
return rep;
|
|
}
|
|
|
|
TStringIRep* TSICTStringCreateInvalidWithFormat(TSITStringFormat format)
|
|
{
|
|
CFDataRef data = CFDataCreate(kCFAllocatorDefault, NULL, 0);
|
|
TStringIRep* rep = TSICTStringCreateWithDataOfTypeAndFormat(data, kTSITStringTagInvalid, format);
|
|
CFRelease(data);
|
|
return rep;
|
|
}
|
|
|
|
TStringIRep* TSICTStringCreateWithArrayAndFormat(CFArrayRef array, TSITStringFormat format)
|
|
{
|
|
CFRetain(array);
|
|
|
|
CFMutableDataRef buffer = CFDataCreateMutable(kCFAllocatorDefault, 0);
|
|
|
|
CFRange all = CFRangeMake(0, CFArrayGetCount(array));
|
|
TStringCollectionCallbackContext cx = {buffer, format};
|
|
CFArrayApplyFunction(array, all, ArrayBufferAppendCallback, &cx);
|
|
|
|
TStringIRep* rep = TSICTStringCreateWithDataOfTypeAndFormat(buffer, kTSITStringTagList, format);
|
|
CFRelease(buffer);
|
|
CFRelease(array);
|
|
return rep;
|
|
}
|
|
|
|
TStringIRep* TSICTStringCreateWithDictionaryAndFormat(CFDictionaryRef dictionary, TSITStringFormat format)
|
|
{
|
|
CFRetain(dictionary);
|
|
|
|
CFMutableDataRef buffer = CFDataCreateMutable(kCFAllocatorDefault, 0);
|
|
|
|
TStringCollectionCallbackContext cx = {buffer, format};
|
|
CFDictionaryApplyFunction(dictionary, DictionaryBufferAppendCallback, &cx);
|
|
|
|
TStringIRep* rep = TSICTStringCreateWithDataOfTypeAndFormat(buffer, kTSITStringTagDict, format);
|
|
CFRelease(buffer);
|
|
CFRelease(dictionary);
|
|
return rep;
|
|
}
|