/ CFAttributedString.c
CFAttributedString.c
  1  //
  2  //  CFAttributedString.c
  3  //  CoreFoundation
  4  //
  5  //  Copyright (c) 2014 Apportable. All rights reserved.
  6  //
  7  
  8  #include <CoreFoundation/CFAttributedString.h>
  9  #include "CFRuntime.h"
 10  #include "CFInternal.h"
 11  #include "macros.h"
 12  
 13  typedef struct
 14  {
 15      CFRange         _range;
 16      CFDictionaryRef _dictionary;
 17  } __CFRunArrayItem;
 18  
 19  typedef struct __CFAttributedString
 20  {
 21      CFRuntimeBase       _base;
 22      CFStringRef         _string;
 23      __CFRunArrayItem    **_attributes;
 24      CFIndex             _runArrayCount;
 25      Boolean             _isMutable;
 26  } __CFAttributedString;
 27  
 28  static void mergeIntoDictionary(const void* key, const void* value, void* context) {
 29      CFMutableDictionaryRef into = (CFMutableDictionaryRef)context;
 30      CFDictionarySetValue(into, key, value);
 31  }
 32  
 33  static Boolean _CFRunArrayIsEqual(__CFAttributedString *aStr, __CFAttributedString *aStr2)
 34  {
 35      if (aStr->_runArrayCount != aStr2->_runArrayCount)
 36      {
 37          return false;
 38      }
 39      __CFRunArrayItem **inputArray = aStr->_attributes;
 40      __CFRunArrayItem **inputArray2 = aStr2->_attributes;
 41      if (inputArray == inputArray2)
 42      {
 43          return true;
 44      }
 45      if (inputArray == NULL || inputArray2 == NULL)
 46      {
 47          return false;
 48      }
 49      for (CFIndex next, i = 0; i < aStr->_runArrayCount; i = next)
 50      {
 51          __CFRunArrayItem *inp = inputArray[i];
 52          __CFRunArrayItem *inp2 = inputArray2[i];
 53          if (inp)
 54          {
 55              if (inp2 == NULL)
 56              {
 57                  return false;
 58              }
 59              if (inp->_range.location != inp2->_range.location || inp->_range.length != inp2->_range.length)
 60              {
 61                  return false;
 62              }
 63              if (!CFEqual(inp->_dictionary, inp2->_dictionary))
 64              {
 65                  return false;
 66              }
 67              next = i + inp->_range.length;
 68          }
 69          else
 70          {
 71              if (inp2)
 72              {
 73                  return false;
 74              }
 75              next = i + 1;
 76          }
 77      }
 78      return true;
 79  }
 80  
 81  
 82  static __CFRunArrayItem * _CFRunArrayItemInit(CFRange range, CFDictionaryRef dict)
 83  {
 84      __CFRunArrayItem *obj = (__CFRunArrayItem *)malloc(sizeof(__CFRunArrayItem));
 85      obj->_range = range;
 86      obj->_dictionary = CFDictionaryCreateCopy(kCFAllocatorDefault, dict);
 87      return obj;
 88  }
 89  
 90  static void _CFRunArrayItemDestroy(__CFRunArrayItem *ptr)
 91  {
 92      if (ptr->_dictionary)
 93      {
 94          CFRelease(ptr->_dictionary);
 95      }
 96      free(ptr);
 97  }
 98  
 99  static void _CFRunArrayDestroyAttributesOfString(__CFAttributedString *aStr)
100  {
101      __CFRunArrayItem **inputArray = aStr->_attributes;
102      if (inputArray == NULL)
103      {
104          return;
105      }
106      for (CFIndex next, i = 0; i < aStr->_runArrayCount; i = next)
107      {
108          __CFRunArrayItem *inp = inputArray[i];
109          if (inp)
110          {
111              next = i + inp->_range.length;
112              _CFRunArrayItemDestroy(inp);
113          }
114          else
115          {
116              next = i + 1;
117          }
118      }
119      free(aStr->_attributes);
120  }
121  
122  static void _CFRunArrayInsert(__CFAttributedString *attrStr, CFDictionaryRef dict, CFRange range, Boolean clearOther, CFStringRef subtractValue)
123  {
124      if (range.length == 0)
125      {
126          return;
127      }
128  
129      CFIndex rangeLimit = range.location + range.length;
130      if (rangeLimit > attrStr->_runArrayCount)
131      {
132          __CFRunArrayItem **new = (__CFRunArrayItem **)calloc(sizeof(__CFRunArrayItem *), rangeLimit);
133          if (attrStr->_attributes != NULL)
134          {
135              memcpy(new, attrStr->_attributes, sizeof(__CFRunArrayItem *) * attrStr->_runArrayCount);
136              free(attrStr->_attributes);
137          }
138          attrStr->_attributes = new;
139          attrStr->_runArrayCount = rangeLimit;
140      }
141  
142      __CFRunArrayItem **indexArray = attrStr->_attributes;
143      __CFRunArrayItem *ptr = indexArray[range.location];
144  
145      // First entry for this index
146      if (ptr == nil && !subtractValue)
147      {
148          __CFRunArrayItem *obj = _CFRunArrayItemInit(CFRangeMake(range.location, 0), dict);
149          for (CFIndex i = range.location; i < rangeLimit; i++)
150          {
151              if (indexArray[i] == nil)
152              {
153                  indexArray[i] = obj;
154                  obj->_range.length++;
155              }
156              else
157              {
158                  _CFRunArrayInsert(attrStr, dict, CFRangeMake(i, rangeLimit - i), clearOther, subtractValue);
159                  break;
160              }
161          }
162          return;
163      }
164  
165      Boolean split = false;
166      CFIndex ptrRangeLimit = ptr->_range.location + ptr->_range.length;
167  
168      CFMutableDictionaryRef mergeDict = NULL;
169      do {
170          if (clearOther)
171          {
172              mergeDict = (CFMutableDictionaryRef)dict;
173              CFRetain(mergeDict);
174          }
175          else
176          {
177              mergeDict = CFDictionaryCreateMutableCopy(NULL, 0, ptr->_dictionary);
178              if (subtractValue != NULL)
179              {
180                  CFDictionaryRemoveValue(mergeDict, subtractValue);
181              }
182              else
183              {
184                  CFDictionaryApplyFunction(dict, mergeIntoDictionary, mergeDict);
185              }
186          }
187  
188          if (rangeLimit <= ptrRangeLimit && CFEqual(ptr->_dictionary, mergeDict))
189          {
190              // No need to do anymore - the requested insert dictionary is already there
191              break; // CLEANUP
192          }
193  
194          // Check for split at beginning of list
195          if (ptr->_range.location < range.location)
196          {
197              if (CFEqual(ptr->_dictionary, mergeDict))
198              {
199                  // Done with this item but still need to update subsequent run array items
200                  CFIndex restStart = ptrRangeLimit;
201                  CFIndex restLength = rangeLimit - restStart;
202                  _CFRunArrayInsert(attrStr, dict, CFRangeMake(restStart, restLength), clearOther, subtractValue);
203                  break; // CLEANUP
204              }
205              split = true;
206              CFIndex objRangeLimit = ptr->_range.location + ptr->_range.length;
207              ptr->_range.length = range.location - ptr->_range.location;
208              // if *ptr goes beyond range, need to copy element for new range after insert
209              if (objRangeLimit > rangeLimit)
210              {
211                  __CFRunArrayItem *copy = _CFRunArrayItemInit(CFRangeMake(rangeLimit, objRangeLimit - rangeLimit), ptr->_dictionary);
212                  for (CFIndex i = rangeLimit; i < objRangeLimit; i++)
213                  {
214                      indexArray[i] = copy;
215                  }
216              }
217              // fall through - now that preceding (and successive) indices are ready
218          }
219  
220          __CFRunArrayItem *obj;
221          CFIndex newRangeLength = __CFMin(range.length, ptrRangeLimit - range.location);
222          if (range.location > 0 && CFEqual(indexArray[range.location - 1]->_dictionary, mergeDict))
223          {
224              // Can merge with previous
225              obj = indexArray[range.location - 1];
226              obj->_range.length += newRangeLength;
227          }
228          else if (ptrRangeLimit == rangeLimit && 
229              rangeLimit < attrStr->_runArrayCount && 
230              CFEqual(mergeDict, indexArray[rangeLimit]->_dictionary))
231          {
232              // can merge with next
233              indexArray[rangeLimit]->_range.length += rangeLimit - range.location;
234              indexArray[rangeLimit]->_range.location = range.location;
235              for (CFIndex i = range.location; i < rangeLimit; i++)
236              {
237                  indexArray[i] = indexArray[rangeLimit];
238              }
239              break; // CLEANUP
240          }
241          else
242          {
243              obj = _CFRunArrayItemInit(CFRangeMake(range.location, newRangeLength), mergeDict);
244          }
245  
246          for (CFIndex i = range.location; i < range.location + newRangeLength; i++)
247          {
248              indexArray[i] = obj;
249          }
250  
251          if (ptrRangeLimit > rangeLimit)
252          {
253              if (!split)
254              {
255                  ptr->_range.location = rangeLimit;
256                  ptr->_range.length = ptr->_range.length - range.length;
257              }
258              break; // CLEANUP
259          }
260          else
261          {
262              if (!split)
263              {
264                  _CFRunArrayItemDestroy(ptr);
265              }
266              if (ptrRangeLimit < rangeLimit)
267              {
268                  CFIndex restStart = ptrRangeLimit;
269                  CFIndex restLength = rangeLimit - restStart;
270                  _CFRunArrayInsert(attrStr, dict, CFRangeMake(restStart, restLength), clearOther, subtractValue);
271              }
272          }
273      } while(0);
274  
275      // CLEANUP:
276      CFRelease(mergeDict);
277  }
278  
279  static void _CFRunArrayCopy(__CFAttributedString *to, __CFAttributedString *from)
280  {
281      if (from->_attributes == NULL)
282      {
283          return;
284      }
285      _CFRunArrayDestroyAttributesOfString(to);
286      to->_attributes = (__CFRunArrayItem **)calloc(sizeof(__CFRunArrayItem *), from->_runArrayCount);
287      to->_runArrayCount = from->_runArrayCount;
288      __CFRunArrayItem **inputArray = from->_attributes;
289      __CFRunArrayItem **outputArray = to->_attributes;
290      for (CFIndex next, i = 0; i < from->_runArrayCount; i = next)
291      {
292          __CFRunArrayItem *inp = inputArray[i];
293          if (inp)
294          {
295              outputArray[i] = _CFRunArrayItemInit(inp->_range, inp->_dictionary);
296              next = i + inp->_range.length;
297              for (CFIndex j = i; j < next; j++)
298              {
299                  outputArray[j] = outputArray[i];
300              }
301          }
302          else
303          {
304              next = i + 1;
305          }
306      }
307  }
308  
309  static CFDictionaryRef _CFRunArrayObjectAtIndex(__CFAttributedString *aStr, CFIndex loc, CFRange *effectiveRange)
310  {
311      if (loc > aStr->_runArrayCount)
312      {
313          return NULL;
314      }
315      __CFRunArrayItem *ptr = aStr->_attributes[loc];
316      if (effectiveRange)
317      {
318          *effectiveRange = ptr->_range;
319      }
320      return ptr->_dictionary;
321  }
322  
323  static CFTypeID __kCFAttributedStringTypeID = _kCFRuntimeNotATypeID;
324  
325  typedef CFTypeRef (*CF_STRING_CREATE_COPY)(CFAllocatorRef alloc, CFTypeRef theString);
326  
327  static void __CFAttributedStringDeallocate(CFTypeRef cf) {
328      __CFAttributedString *ptr = (__CFAttributedString *)cf;
329      CFRelease(ptr->_string);
330      _CFRunArrayDestroyAttributesOfString(ptr);
331  }
332  
333  static Boolean __CFAttributedStringEqual(CFTypeRef cf1, CFTypeRef cf2) {
334      __CFAttributedString *ptr1 = (__CFAttributedString *)cf1;
335      __CFAttributedString *ptr2 = (__CFAttributedString *)cf2;
336      if (!CFEqual(ptr1->_string, ptr2->_string))
337      {
338          return false;
339      }
340      return _CFRunArrayIsEqual(ptr1, ptr2);
341  }
342  
343  CFHashCode __CFAttributedStringHash(CFTypeRef cf) {
344      __CFAttributedString *ptr = (__CFAttributedString *)cf;
345      return CFHash(ptr->_string);
346  }
347  
348  static CFStringRef __CFAttributedStringCopyDescription(CFTypeRef cf) 
349  {
350      // Update attributes
351      __CFAttributedString *from = (__CFAttributedString *)cf;
352      __CFRunArrayItem **inputArray = from->_attributes;
353      if (inputArray == NULL)
354      {
355          return CFSTR("");
356      }
357      CFMutableStringRef out = CFStringCreateMutable(kCFAllocatorSystemDefault, 0);
358      for (CFIndex next, i = 0; i < from->_runArrayCount; i = next)
359      {
360          __CFRunArrayItem *inp = inputArray[i];
361          if (inp)
362          {
363              next = i + inp->_range.length;
364              CFStringRef substr = CFStringCreateWithSubstring(NULL, from->_string, inp->_range);
365              CFStringAppendFormat(out, NULL, CFSTR("%@ %@ Len %d\n\n"),
366                  substr,
367                  inp->_dictionary,
368                  (int)inp->_range.length);
369              CFRelease(substr);
370          }
371          else
372          {
373              next = i + 1;
374          }
375      }
376      return out;
377  }
378  
379  static const CFRuntimeClass __CFAttributedStringClass = {
380      _kCFRuntimeScannedObject,
381      "CFAttributedString",
382      NULL,      // init
383      (CF_STRING_CREATE_COPY)CFAttributedStringCreateCopy,
384      __CFAttributedStringDeallocate,
385      __CFAttributedStringEqual,
386      __CFAttributedStringHash,
387      NULL,
388      __CFAttributedStringCopyDescription
389  };
390  
391  CF_PRIVATE void __CFAttributedStringInitialize(void) {
392      __kCFAttributedStringTypeID = _CFRuntimeRegisterClass(&__CFAttributedStringClass);
393  }
394  
395  CFTypeID CFAttributedStringGetTypeID(void) {
396      if (__kCFAttributedStringTypeID == _kCFRuntimeNotATypeID) {
397          __CFAttributedStringInitialize();
398      }
399      return __kCFAttributedStringTypeID;
400  }
401  
402  static void _CFAttributedStringCreateAttributes(CFAllocatorRef alloc, __CFAttributedString *attrStr, CFDictionaryRef attributes, CFIndex len)
403  {
404      CFRange range = CFRangeMake(0, len);
405      if (attributes != NULL)
406      {
407          _CFRunArrayInsert(attrStr, attributes, range, false, NULL);
408      }
409      else
410      {
411          CFDictionaryRef insertDict = CFDictionaryCreate(alloc, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
412          _CFRunArrayInsert(attrStr, insertDict, range, false, NULL);
413          CFRelease(insertDict);
414      }
415  }
416  
417  CFAttributedStringRef CFAttributedStringCreate(CFAllocatorRef alloc, CFStringRef str, CFDictionaryRef attributes)
418  {
419      CFIndex size = sizeof(struct __CFAttributedString) - sizeof(CFRuntimeBase);
420      __CFAttributedString *newObj = (struct __CFAttributedString *)_CFRuntimeCreateInstance(alloc, CFAttributedStringGetTypeID(), size, NULL);
421      newObj->_string = CFStringCreateCopy(alloc, str);
422      newObj->_attributes = NULL;
423      newObj->_runArrayCount = 0;
424      newObj->_isMutable = false;
425      _CFAttributedStringCreateAttributes(alloc, newObj, attributes, CFStringGetLength(str));
426      return (CFAttributedStringRef)newObj;
427  }
428  
429  CFAttributedStringRef CFAttributedStringCreateWithSubstring(CFAllocatorRef alloc, CFAttributedStringRef aStr, CFRange range)
430  {
431      CFIndex length = CFAttributedStringGetLength(aStr);
432      if (range.length == 0 || range.location + range.length > length)
433      {
434          return NULL;
435      }
436      CFStringRef s = CFAttributedStringGetString(aStr);
437      CFStringRef substring = CFStringCreateWithSubstring(alloc, s, range);
438      CFMutableAttributedStringRef obj = (CFMutableAttributedStringRef)CFAttributedStringCreate(alloc, substring, NULL);
439      CFRelease(substring);
440  
441      CFIndex delta = range.location;
442      CFIndex newLength;
443  
444      for (CFIndex i = 0; i < range.length; i = i + newLength)
445      {
446          CFRange effRange;
447          CFIndex selfIndex = i + delta;
448          CFDictionaryRef attrs = CFAttributedStringGetAttributes(aStr, selfIndex, &effRange);
449          newLength = effRange.length;
450          if (effRange.location < selfIndex)
451          {
452              newLength -= selfIndex - effRange.location;
453          }
454          newLength = __CFMin(newLength, range.length - i);
455          CFAttributedStringSetAttributes(obj, CFRangeMake(i, newLength), attrs, false);
456      }
457      return (CFAttributedStringRef)obj;
458  }
459  
460  CFAttributedStringRef CFAttributedStringCreateCopy(CFAllocatorRef alloc, CFAttributedStringRef aStr)
461  {
462      if (((CFAttributedStringRef)aStr)->_isMutable)
463      {
464          CFAttributedStringRef retVal = (CFAttributedStringRef)CFAttributedStringCreateMutableCopy(alloc, 0, aStr);
465          ((__CFAttributedString *)retVal)->_isMutable = false;
466          return retVal;
467      }
468      else
469      {
470          return CFRetain(aStr);
471      }
472  }
473  
474  CFStringRef CFAttributedStringGetString(CFAttributedStringRef aStr)
475  {
476      CF_OBJC_FUNCDISPATCHV(__kCFAttributedStringTypeID, CFStringRef, (NSAttributedString *)aStr, string);
477      return ((__CFAttributedString *)aStr)->_string;
478  }
479  
480  CFIndex CFAttributedStringGetLength(CFAttributedStringRef aStr)
481  {
482      CF_OBJC_FUNCDISPATCHV(__kCFAttributedStringTypeID, CFIndex, (NSAttributedString *)aStr, length);
483      return CFStringGetLength(CFAttributedStringGetString(aStr));
484  }
485  
486  CFDictionaryRef CFAttributedStringGetAttributes(CFAttributedStringRef aStr, CFIndex loc, CFRange *effectiveRange)
487  {
488      CF_OBJC_FUNCDISPATCHV(__kCFAttributedStringTypeID, CFDictionaryRef, (NSAttributedString *)aStr, attributesAtIndex:loc effectiveRange:effectiveRange);
489      return _CFRunArrayObjectAtIndex((__CFAttributedString *)aStr, loc, effectiveRange);
490  }
491  
492  CFTypeRef CFAttributedStringGetAttribute(CFAttributedStringRef aStr, CFIndex loc, CFStringRef attrName, CFRange *effectiveRange)
493  {
494      CF_OBJC_FUNCDISPATCHV(__kCFAttributedStringTypeID, id, (NSAttributedString *)aStr, attribute:attrName AtIndex:loc effectiveRange:effectiveRange);
495      CFDictionaryRef dict = CFAttributedStringGetAttributes(aStr, loc, effectiveRange);
496      return CFDictionaryGetValue(dict, attrName);
497  }
498  
499  CFDictionaryRef CFAttributedStringGetAttributesAndLongestEffectiveRange(CFAttributedStringRef aStr, CFIndex loc, CFRange inRange, CFRange *longestEffectiveRange)
500  {
501      CFDictionaryRef retVal = CFAttributedStringGetAttributes(aStr, loc, longestEffectiveRange);
502      if (longestEffectiveRange == NULL)
503      {
504          return retVal;
505      }
506      CFIndex min = longestEffectiveRange->location;  // inclusive end
507      CFIndex max = longestEffectiveRange->location + longestEffectiveRange->length;  // exclusive end
508      CFRange tempRange;
509      CFTypeRef compareVal;
510      CFIndex minLimit = __CFMax(0, inRange.location);
511      while (min > minLimit && 
512          (compareVal = CFAttributedStringGetAttributes(aStr, min - 1, &tempRange)) &&
513          CFEqual(retVal, compareVal))
514      {
515          min = tempRange.location;
516      }
517  
518      CFIndex inRangeLimit = inRange.location + inRange.length;
519      CFIndex maxLimit = __CFMin(CFAttributedStringGetLength(aStr), inRangeLimit);
520      while (max < maxLimit && 
521          (compareVal = CFAttributedStringGetAttributes(aStr, max, &tempRange)) &&
522          CFEqual(retVal, compareVal))
523      {
524          max = tempRange.location + tempRange.length;
525      }
526      CFIndex newLocation = __CFMax(min, inRange.location);
527      CFIndex newLength = __CFMin(max, inRangeLimit) - newLocation;
528      *longestEffectiveRange = CFRangeMake(newLocation, newLength);
529      return retVal;
530  }
531  
532  CFTypeRef CFAttributedStringGetAttributeAndLongestEffectiveRange(CFAttributedStringRef aStr, CFIndex loc, CFStringRef attrName, CFRange inRange, CFRange *longestEffectiveRange)
533  {
534      CFTypeRef retVal = CFAttributedStringGetAttribute(aStr, loc, attrName, longestEffectiveRange);
535      if (longestEffectiveRange == NULL)
536      {
537          return retVal;
538      }
539      CFIndex min = longestEffectiveRange->location;  // inclusive end
540      CFIndex max = longestEffectiveRange->location + longestEffectiveRange->length;  // exclusive end
541      CFRange tempRange;
542      CFTypeRef compareVal;
543      CFIndex minLimit = __CFMax(0, inRange.location);
544      while (min > minLimit && 
545          (compareVal = CFAttributedStringGetAttribute(aStr, min - 1, attrName, &tempRange)) &&
546          CFEqual(retVal, compareVal))
547      {
548          min = tempRange.location;
549      }
550  
551      CFIndex inRangeLimit = inRange.location + inRange.length;
552      CFIndex maxLimit = __CFMin(CFAttributedStringGetLength(aStr), inRangeLimit);
553      while (max < maxLimit && 
554          (compareVal = CFAttributedStringGetAttribute(aStr, max, attrName, &tempRange)) &&
555          CFEqual(retVal, compareVal))
556      {
557          max = tempRange.location + tempRange.length;
558      }
559      CFIndex newLocation = __CFMax(min, inRange.location);
560      CFIndex newLength = __CFMin(max, inRangeLimit) - newLocation;
561      *longestEffectiveRange = CFRangeMake(newLocation, newLength);
562      return retVal;
563  }
564  
565  CFMutableAttributedStringRef CFAttributedStringCreateMutableCopy(CFAllocatorRef alloc, CFIndex maxLength, CFAttributedStringRef aStr)
566  {
567      CFMutableAttributedStringRef retVal = (CFMutableAttributedStringRef)CFAttributedStringCreate(alloc, CFAttributedStringGetString(aStr), NULL);
568      _CFRunArrayCopy((__CFAttributedString *)retVal, (__CFAttributedString *)aStr);
569      ((__CFAttributedString *)retVal)->_isMutable = true;
570      return retVal;
571  }
572  
573  CFMutableAttributedStringRef CFAttributedStringCreateMutable(CFAllocatorRef alloc, CFIndex maxLength)
574  {
575      // iOS also seems to ignore the maxLength parameter - it's passed to CFStringCreateMutable, but at most is used as
576      // a hint - not as an absolute limit as implied by docs.
577      CFMutableAttributedStringRef retVal = (CFMutableAttributedStringRef)CFAttributedStringCreate(alloc, CFSTR(""), NULL);
578      ((__CFAttributedString *)retVal)->_isMutable = true;
579      return retVal;
580  }
581  
582  void CFAttributedStringReplaceString(CFMutableAttributedStringRef aStr, CFRange range, CFStringRef replacement)
583  {
584      CF_OBJC_FUNCDISPATCHV (__kCFAttributedStringTypeID, void, (NSAttributedString *)aStr, replaceCharactersInRange:NSMakeRange(range.location, range.length) withString:replacement);
585      CFIndex adding = CFStringGetLength(replacement);
586      __CFAttributedString *ptr = (struct __CFAttributedString *)aStr;
587      CFIndex oldLength = CFStringGetLength(ptr->_string);
588      CFMutableStringRef str = CFStringCreateMutableCopy(NULL, 0, ptr->_string);
589      CFStringReplace(str, range, replacement);
590      CFRelease(ptr->_string);
591      ptr->_string = str;
592  
593      if (oldLength == 0)
594      {
595          _CFAttributedStringCreateAttributes(NULL, ptr, NULL, adding);
596      }
597      else if (range.location == oldLength)
598      {
599          // Extending old string at end. Attributes copied from last character
600          ptr->_attributes[oldLength - 1]->_range.length += adding;
601          ptr->_attributes = (__CFRunArrayItem **)realloc(ptr->_attributes, sizeof(__CFRunArrayItem *) * (ptr->_runArrayCount + adding));
602          ptr->_runArrayCount += adding;
603          for (CFIndex i = oldLength; i < ptr->_runArrayCount; i++)
604          {
605              ptr->_attributes[i] = ptr->_attributes[oldLength - 1];
606          }
607      }
608      else
609      {
610          // Now fix up attribute ranges. Inserted string takes the attributes of the character at the start
611          CFIndex deleting = range.length;
612          CFIndex delta = adding - deleting;
613  
614          __CFRunArrayItem *startRunPtr = ptr->_attributes[range.location];
615          CFIndex next = startRunPtr->_range.location + startRunPtr->_range.length;
616          CFIndex deleteLimit = 0;
617          CFIndex startRangeDelta = range.location - startRunPtr->_range.location;
618          if (startRunPtr->_range.length - startRangeDelta >= deleting)
619          {
620              if (delta == 0)
621              {
622                  // No adjustments necessary
623                  return;
624              }
625              startRunPtr->_range.length += delta;
626          }
627          else
628          {
629              deleteLimit = range.location + range.length;
630              startRunPtr->_range.length = startRangeDelta + adding;
631          }
632          if (startRunPtr->_range.length == 0)
633          {
634              _CFRunArrayItemDestroy(startRunPtr);
635          }
636          for (CFIndex i = next; i < oldLength; i = next)
637          {
638              __CFRunArrayItem *runPtr = ptr->_attributes[i];
639              next = runPtr->_range.location + runPtr->_range.length;
640              if (next <= deleteLimit)
641              {
642                  _CFRunArrayItemDestroy(runPtr);
643              }
644              else if (i >= deleteLimit)
645              {
646                  runPtr->_range.location += delta;
647              }
648              else
649              {
650                  runPtr->_range.length -= deleteLimit - i;
651                  runPtr->_range.location = range.location + adding;
652              }
653          }
654          // Get attributes array in the right place
655          if (delta > 0)
656          {
657              ptr->_attributes = (__CFRunArrayItem **)realloc(ptr->_attributes, sizeof(__CFRunArrayItem *) * (ptr->_runArrayCount + delta));
658              CFIndex updateStart = range.location;
659              memmove(&ptr->_attributes[updateStart + delta], &ptr->_attributes[updateStart], sizeof(__CFRunArrayItem *) * (ptr->_runArrayCount - updateStart));
660          }
661          else if (delta < 0)
662          {
663              CFIndex updateStart = range.location + adding;
664              CFIndex count = ptr->_runArrayCount - updateStart + delta;
665              if (count > 0)
666              {
667                  memmove(&ptr->_attributes[updateStart], &ptr->_attributes[updateStart - delta], sizeof(__CFRunArrayItem *) * count);
668              }
669          }
670          ptr->_runArrayCount += delta;
671  
672          // Make sure attribute array is correct for length of new string
673          CFIndex endNewAdd = range.location + adding;
674          for (CFIndex i = range.location + 1; i < endNewAdd; i++)
675          {
676              ptr->_attributes[i] = startRunPtr;
677          }
678      }
679  }
680  
681  CFMutableStringRef CFAttributedStringGetMutableString(CFMutableAttributedStringRef aStr)
682  {
683      // iOS only returns a real value for the NS version
684      return nil;
685  }
686  
687  void CFAttributedStringSetAttributes(CFMutableAttributedStringRef aStr, CFRange range, CFDictionaryRef replacement, Boolean clearOtherAttributes)
688  {
689      CF_OBJC_FUNCDISPATCHV(__kCFAttributedStringTypeID, void, (NSMutableAttributedString *)aStr, addAttributes:(NSDictionary *)replacement range:NSMakeRange(range.location, range.length));
690      _CFRunArrayInsert((__CFAttributedString *)aStr, replacement, range, clearOtherAttributes, NULL);
691  }
692  
693  void CFAttributedStringSetAttribute(CFMutableAttributedStringRef aStr, CFRange range, CFStringRef attrName, CFTypeRef value)
694  {
695      CF_OBJC_FUNCDISPATCHV(__kCFAttributedStringTypeID, void, (NSAttributedString *)aStr, setAttribute:attrName value:value range:NSMakeRange(range.location, range.length));
696  
697      CFDictionaryRef dict = CFDictionaryCreate(NULL, (const void **)&attrName, (const void **)&value, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
698      CFAttributedStringSetAttributes((__CFAttributedString *)aStr, range, dict, false);
699      CFRelease(dict);
700  }
701  
702  void CFAttributedStringRemoveAttribute(CFMutableAttributedStringRef aStr, CFRange range, CFStringRef attrName)
703  {
704      CF_OBJC_FUNCDISPATCHV(__kCFAttributedStringTypeID, void, (NSAttributedString *)aStr, removeAttribute:attrName range:NSMakeRange(range.location, range.length));
705      _CFRunArrayInsert(aStr, NULL, range, false, attrName);
706  }
707  
708  void CFAttributedStringReplaceAttributedString(CFMutableAttributedStringRef aStr, CFRange range, CFAttributedStringRef replacement)
709  {
710      CF_OBJC_FUNCDISPATCHV(__kCFAttributedStringTypeID, void, (NSAttributedString *)aStr, replaceCharactersInRange:NSMakeRange(range.location, range.length) withAttributeString:replacement);
711      
712      // Replace string
713      CFStringRef replaceString = CFAttributedStringGetString(replacement);
714      CFAttributedStringReplaceString(aStr, range, replaceString);
715  
716      // Update attributes
717      CFIndex len = CFStringGetLength(replaceString);
718      for (CFIndex next, i = 0; i < len; i = next)
719      {
720          CFRange replacementRange;
721          CFDictionaryRef attrs = CFAttributedStringGetAttributes(replacement, i, &replacementRange);
722          _CFRunArrayInsert(aStr, attrs, CFRangeMake(i + range.location, replacementRange.length), true, NULL);
723          next = i + replacementRange.length;
724      }
725  }
726  
727  void CFAttributedStringBeginEditing(CFMutableAttributedStringRef aStr)
728  {
729      static int printed = 0;
730      if (printed == 0)
731      {
732          DEBUG_LOG("CFAttributedStringBeginEditing and CFAttributedStringEndEditing are currently no-ops");
733          printed = 1;
734      }
735  }
736  
737  void CFAttributedStringEndEditing(CFMutableAttributedStringRef aStr)
738  {
739  }
740  
741  void _CFAttributedStringSetMutable(CFAttributedStringRef aStr, Boolean isMutable)
742  {
743      ((__CFAttributedString *)aStr)->_isMutable = isMutable;
744  }