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