// // 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; }