git.y1.nz

SameBoy

Accurate GB/GBC emulator
download: https://git.y1.nz/archives/sameboy.tar.gz
README | Files | Log | Refs | LICENSE

HexFiend/HFFunctions.m

      1 #import <HexFiend/HFFunctions.h>
      2 #import <HexFiend/HFController.h>
      3 
      4 #import "HFFunctions_Private.h"
      5 
      6 #ifndef NDEBUG
      7 //#define USE_CHUD 1
      8 #endif
      9 
     10 #ifndef USE_CHUD
     11 #define USE_CHUD 0
     12 #endif
     13 
     14 #if USE_CHUD
     15 #import <CHUD/CHUD.h>
     16 #endif
     17 
     18 NSImage *HFImageNamed(NSString *name) {
     19     HFASSERT(name != NULL);
     20     NSImage *image = [NSImage imageNamed:name];
     21     if (image == NULL) {
     22         NSString *imagePath = [[NSBundle bundleForClass:[HFController class]] pathForResource:name ofType:@"tiff"];
     23         if (! imagePath) {
     24             NSLog(@"Unable to find image named %@.tiff", name);
     25         }
     26         else {
     27             image = [[NSImage alloc] initByReferencingFile:imagePath];
     28             if (image == nil || ! [image isValid]) {
     29                 NSLog(@"Couldn't load image at path %@", imagePath);
     30                 [image release];
     31                 image = nil;
     32             }
     33             else {
     34                 [image setName:name];
     35             }
     36         }
     37     }
     38     return image;
     39 }
     40 
     41 @implementation HFRangeWrapper
     42 
     43 - (HFRange)HFRange { return range; }
     44 
     45 + (HFRangeWrapper *)withRange:(HFRange)range {
     46     HFRangeWrapper *result = [[self alloc] init];
     47     result->range = range;
     48     return [result autorelease];
     49 }
     50 
     51 + (NSArray *)withRanges:(const HFRange *)ranges count:(NSUInteger)count {
     52     HFASSERT(count == 0 || ranges != NULL);
     53     NSUInteger i;
     54     NSArray *result;
     55     NEW_ARRAY(HFRangeWrapper *, wrappers, count);
     56     for (i=0; i < count; i++) wrappers[i] = [self withRange:ranges[i]];
     57     result = [NSArray arrayWithObjects:wrappers count:count];
     58     FREE_ARRAY(wrappers);
     59     return result;
     60 }
     61 
     62 - (BOOL)isEqual:(id)obj {
     63     if (! [obj isKindOfClass:[HFRangeWrapper class]]) return NO;
     64     else return HFRangeEqualsRange(range, [obj HFRange]);
     65 }
     66 
     67 - (NSUInteger)hash {
     68     return (NSUInteger)(range.location + (range.length << 16));
     69 }
     70 
     71 - (id)copyWithZone:(NSZone *)zone {
     72     USE(zone);
     73     return [self retain];
     74 }
     75 
     76 - (NSString *)description {
     77     return HFRangeToString(range);
     78 }
     79 
     80 static int hfrange_compare(const void *ap, const void *bp) {
     81     const HFRange *a = ap;
     82     const HFRange *b = bp;
     83     if (a->location < b->location) return -1;
     84     else if (a->location > b->location) return 1;
     85     else if (a->length < b->length) return -1;
     86     else if (a->length > b->length) return 1;
     87     else return 0;
     88 }
     89 
     90 + (NSArray *)organizeAndMergeRanges:(NSArray *)inputRanges {
     91     HFASSERT(inputRanges != NULL);
     92     NSUInteger leading = 0, trailing = 0, length = [inputRanges count];
     93     if (length == 0) return @[];
     94     else if (length == 1) return [NSArray arrayWithArray:inputRanges];
     95     
     96     NEW_ARRAY(HFRange, ranges, length);
     97     [self getRanges:ranges fromArray:inputRanges];
     98     qsort(ranges, length, sizeof ranges[0], hfrange_compare);
     99     leading = 0;
    100     while (leading < length) {
    101         leading++;
    102         if (leading < length) {
    103             HFRange leadRange = ranges[leading], trailRange = ranges[trailing];
    104             if (HFIntersectsRange(leadRange, trailRange) || HFMaxRange(leadRange) == trailRange.location || HFMaxRange(trailRange) == leadRange.location) {
    105                 ranges[trailing] = HFUnionRange(leadRange, trailRange);
    106             }
    107             else {
    108                 trailing++;
    109                 ranges[trailing] = ranges[leading];
    110             }
    111         }
    112     }
    113     NSArray *result = [HFRangeWrapper withRanges:ranges count:trailing + 1];
    114     FREE_ARRAY(ranges);
    115     return result;
    116 }
    117 
    118 + (void)getRanges:(HFRange *)ranges fromArray:(NSArray *)array {
    119     HFASSERT(ranges != NULL || [array count] == 0);
    120     if (ranges) {
    121         FOREACH(HFRangeWrapper*, wrapper, array) *ranges++ = [wrapper HFRange];
    122     }
    123 }
    124 
    125 @end
    126 
    127 @implementation HFRangeSet
    128 // HFRangeSet is implemented as a CFMutableArray of uintptr_t "fenceposts". The array
    129 // is even in length, sorted, duplicate free, and considered to include the ranges
    130 // [array[0], array[1]), [array[2], array[3]), ..., [array[2n], array[2n+1])
    131 
    132 CFComparisonResult uintptrComparator(const void *val1, const void *val2, void *context) {
    133     (void)context;
    134     uintptr_t a = (uintptr_t)val1;
    135     uintptr_t b = (uintptr_t)val2;
    136     if(a < b) return kCFCompareLessThan;
    137     if(a > b) return kCFCompareGreaterThan;
    138     return kCFCompareEqualTo;
    139 }
    140 
    141 static void HFRangeSetAddRange(CFMutableArrayRef array, uintptr_t a, uintptr_t b) {
    142     CFIndex count = CFArrayGetCount(array);
    143     assert(a < b); assert(count % 2 == 0);
    144     CFIndex idxa = CFArrayBSearchValues(array, CFRangeMake(0, count), (void*)a, uintptrComparator, NULL);
    145     CFIndex idxb = CFArrayBSearchValues(array, CFRangeMake(0, count), (void*)b, uintptrComparator, NULL);
    146 
    147     const void *x[2] = { (void*)a, (void*)b };
    148     if(idxa >= count) {
    149         CFArrayReplaceValues(array, CFRangeMake(count, 0), x, 2);
    150         return;
    151     }
    152     if(idxb == 0) {
    153         CFArrayReplaceValues(array, CFRangeMake(0, 0), x, 2);
    154         return;
    155     }
    156 
    157     // Clear fenceposts strictly between 'a' and 'b', and then possibly
    158     // add 'a' or 'b' as fenceposts.
    159     CFIndex cutloc = (uintptr_t)CFArrayGetValueAtIndex(array, idxa) == a ? idxa+1 : idxa;
    160     CFIndex cutlen = idxb - cutloc;
    161     
    162     bool inca = cutloc % 2 == 0; // Include 'a' if it would begin an included range
    163     bool incb = (count - cutlen + inca) % 2 == 1; // The set must be even, which tells us about 'b'.
    164     
    165     CFArrayReplaceValues(array, CFRangeMake(cutloc, cutlen), x+inca, inca+incb);
    166     assert(CFArrayGetCount(array) % 2 == 0);
    167 }
    168 
    169 static void HFRangeSetRemoveRange(CFMutableArrayRef array, uintptr_t a, uintptr_t b) {
    170     CFIndex count = CFArrayGetCount(array);
    171     assert(a < b); assert(count % 2 == 0);
    172     CFIndex idxa = CFArrayBSearchValues(array, CFRangeMake(0, count), (void*)a, uintptrComparator, NULL);
    173     CFIndex idxb = CFArrayBSearchValues(array, CFRangeMake(0, count), (void*)b, uintptrComparator, NULL);
    174     if(idxa >= count || idxb == 0) return;
    175 
    176     // Remove fenceposts strictly between 'a' and 'b', and then possibly
    177     // add 'a' or 'b' as fenceposts.
    178     CFIndex cutloc = (uintptr_t)CFArrayGetValueAtIndex(array, idxa) == a ? idxa+1 : idxa;
    179     CFIndex cutlen = idxb - cutloc;
    180     
    181     bool inca = cutloc % 2 == 1; // Include 'a' if it would end an included range
    182     bool incb = (count - cutlen + inca) % 2 == 1; // The set must be even, which tells us about 'b'.
    183     
    184     const void *x[2] = { (void*)a, (void*)b };
    185     CFArrayReplaceValues(array, CFRangeMake(cutloc, cutlen), x+inca, inca+incb);
    186     assert(CFArrayGetCount(array) % 2 == 0);
    187 }
    188 
    189 static void HFRangeSetToggleRange(CFMutableArrayRef array, uintptr_t a, uintptr_t b) {
    190     CFIndex count = CFArrayGetCount(array);
    191     assert(a < b); assert(count % 2 == 0);
    192     
    193     // In the fencepost representation, simply toggling the existence of
    194     // fenceposts 'a' and 'b' achieves symmetric difference.
    195     
    196     CFIndex idxa = CFArrayBSearchValues(array, CFRangeMake(0, count), (void*)a, uintptrComparator, NULL);
    197     if((uintptr_t)CFArrayGetValueAtIndex(array, idxa) == a) {
    198         CFArrayRemoveValueAtIndex(array, idxa);
    199     } else {
    200         CFArrayInsertValueAtIndex(array, idxa, (void*)a);
    201     }
    202 
    203     CFIndex idxb = CFArrayBSearchValues(array, CFRangeMake(0, count), (void*)b, uintptrComparator, NULL);
    204     if((uintptr_t)CFArrayGetValueAtIndex(array, idxb) == b) {
    205         CFArrayRemoveValueAtIndex(array, idxb);
    206     } else {
    207         CFArrayInsertValueAtIndex(array, idxb, (void*)b);
    208     }
    209     
    210     assert(CFArrayGetCount(array) % 2 == 0);
    211 }
    212 
    213 static BOOL HFRangeSetContainsAllRange(CFMutableArrayRef array, uintptr_t a, uintptr_t b) {
    214     CFIndex count = CFArrayGetCount(array);
    215     assert(a < b); assert(count % 2 == 0);
    216     CFIndex idxa = CFArrayBSearchValues(array, CFRangeMake(0, count), (void*)a, uintptrComparator, NULL);
    217     CFIndex idxb = CFArrayBSearchValues(array, CFRangeMake(0, count), (void*)b, uintptrComparator, NULL);
    218     if(idxa >= count || idxb == 0) return NO;
    219     
    220     // Optimization: if the indexes are far enough apart, then obviouly there's a gap.
    221     if(idxb - idxa >= 2) return NO;
    222 
    223     // The first fencepost >= 'b' must end an include range, a must be in the same range.
    224     return idxb%2 == 1 && idxa == ((uintptr_t)CFArrayGetValueAtIndex(array, idxa) == a ? idxb-1 : idxb);
    225 }
    226 
    227 static BOOL HFRangeSetOverlapsAnyRange(CFMutableArrayRef array, uintptr_t a, uintptr_t b) {
    228     CFIndex count = CFArrayGetCount(array);
    229     assert(a < b); assert(count % 2 == 0);
    230     CFIndex idxa = CFArrayBSearchValues(array, CFRangeMake(0, count), (void*)a, uintptrComparator, NULL);
    231     CFIndex idxb = CFArrayBSearchValues(array, CFRangeMake(0, count), (void*)b, uintptrComparator, NULL);
    232     if(idxa >= count || idxb == 0) return NO;
    233     
    234     // Optimization: if the indexes are far enough apart, then obviouly there's overlap.
    235     if(idxb - idxa >= 2) return YES;
    236     
    237     if((uintptr_t)CFArrayGetValueAtIndex(array, idxa) == a) {
    238         // 'a' is an included fencepost, or instead 'b' makes it past an included fencepost.
    239         return idxa % 2 == 0 || b > (uintptr_t)CFArrayGetValueAtIndex(array, idxa+1);
    240     } else {
    241         // 'a' lies in an included range, or instead 'b' makes it past an included fencepost.
    242         return  idxa % 2 == 1 || b > (uintptr_t)CFArrayGetValueAtIndex(array, idxa);
    243     }
    244 }
    245 
    246 - (instancetype)init {
    247     if(!(self = [super init])) return nil;
    248     array = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
    249     return self;
    250 }
    251 
    252 - (void)dealloc {
    253     CFRelease(array);
    254     [super dealloc];
    255 }
    256 
    257 + (HFRangeSet *)withRange:(HFRange)range {
    258     HFRangeSet *newSet = [[[HFRangeSet alloc] init] autorelease];
    259     if(range.length > 0) {
    260         CFArrayAppendValue(newSet->array, (void*)ll2p(range.location));
    261         CFArrayAppendValue(newSet->array, (void*)ll2p(HFMaxRange(range)));
    262     }
    263     return newSet;
    264 }
    265 
    266 + (HFRangeSet *)withRanges:(const HFRange *)ranges count:(NSUInteger)count {
    267     // FIXME: Stub. Don't rely on the thing we're replacing!
    268     return [HFRangeSet withRangeWrappers:[HFRangeWrapper withRanges:ranges count:count]];
    269 }
    270 
    271 + (HFRangeSet *)withRangeWrappers:(NSArray *)ranges {
    272     HFRangeSet *newSet = [[[HFRangeSet alloc] init] autorelease];
    273     FOREACH(HFRangeWrapper *, wrapper, [HFRangeWrapper organizeAndMergeRanges:ranges]) {
    274         if(wrapper->range.length > 0) {
    275             CFArrayAppendValue(newSet->array, (void*)ll2p(wrapper->range.location));
    276             CFArrayAppendValue(newSet->array, (void*)ll2p(HFMaxRange(wrapper->range)));
    277         }
    278     }
    279     return newSet;
    280 }
    281 
    282 + (HFRangeSet *)withRangeSet:(HFRangeSet *)rangeSet {
    283     return [[rangeSet copy] autorelease];
    284 }
    285 
    286 + (HFRangeSet *)complementOfRangeSet:(HFRangeSet *)rangeSet inRange:(HFRange)range {
    287     if(range.length <= 0) {
    288         // Complement in empty is... empty!
    289         return [HFRangeSet withRange:HFZeroRange];
    290     }
    291     uintptr_t a = ll2p(range.location);
    292     uintptr_t b = ll2p(HFMaxRange(range));
    293     CFIndex count = CFArrayGetCount(rangeSet->array);
    294     CFIndex idxa = CFArrayBSearchValues(rangeSet->array, CFRangeMake(0, count), (void*)a, uintptrComparator, NULL);
    295     CFIndex idxb = CFArrayBSearchValues(rangeSet->array, CFRangeMake(0, count), (void*)b, uintptrComparator, NULL);
    296     if(idxa >= count || idxb == 0)
    297         return [HFRangeSet withRange:range];
    298     
    299     // Alright, the trivial responses are past. We'll need to build a new set.
    300     // Given the fencepost representation of sets, we can efficiently produce an
    301     // inverted set by just copying the fenceposts between 'a' and 'b', and then
    302     // maybe including 'a' and 'b'.
    303     
    304     HFRangeSet *newSet = [[[HFRangeSet alloc] init] autorelease];
    305 
    306     // newSet must contain all the fenceposts strictly between 'a' and 'b'
    307     CFIndex copyloc = (uintptr_t)CFArrayGetValueAtIndex(rangeSet->array, idxa) == a ? idxa+1 : idxa;
    308     CFIndex copylen = idxb - copyloc;
    309     
    310     // Include 'a' if it's needed to invert the parity of the copy.
    311     if(copyloc % 2 == 0) CFArrayAppendValue(newSet->array, &a);
    312     
    313     CFArrayAppendArray(newSet->array, rangeSet->array, CFRangeMake(copyloc, copylen));
    314     
    315     // Include 'b' if it's needed to close off the set.
    316     if(CFArrayGetCount(newSet->array) % 2 == 1)
    317         CFArrayAppendValue(newSet->array, &b);
    318 
    319     assert(CFArrayGetCount(newSet->array) % 2 == 0);
    320     return newSet;
    321 }
    322 
    323 
    324 - (void)addRange:(HFRange)range {
    325     if(range.length == 0) return;
    326     HFRangeSetAddRange(array, ll2p(range.location), ll2p(HFMaxRange(range)));
    327 }
    328 - (void)removeRange:(HFRange)range {
    329     if(range.length == 0) return;
    330     HFRangeSetRemoveRange(array, ll2p(range.location), ll2p(HFMaxRange(range)));
    331 }
    332 - (void)toggleRange:(HFRange)range {
    333     if(range.length == 0) return;
    334     HFRangeSetToggleRange(array, ll2p(range.location), ll2p(HFMaxRange(range)));
    335 }
    336 
    337 - (void)clipToRange:(HFRange)range {
    338     if(range.length <= 0) {
    339         CFArrayRemoveAllValues(array);
    340         return;
    341     }
    342     uintptr_t a = ll2p(range.location);
    343     uintptr_t b = ll2p(HFMaxRange(range));
    344     CFIndex count = CFArrayGetCount(array);
    345     CFIndex idxa = CFArrayBSearchValues(array, CFRangeMake(0, count), (void*)a, uintptrComparator, NULL);
    346     CFIndex idxb = CFArrayBSearchValues(array, CFRangeMake(0, count), (void*)b, uintptrComparator, NULL);
    347     if(idxa >= count || idxb == 0) {
    348         CFArrayRemoveAllValues(array);
    349         return;
    350     }
    351     
    352     // Keep only fenceposts strictly between 'a' and 'b', and then possibly
    353     // add 'a' or 'b' as fenceposts.
    354     CFIndex keeploc = (uintptr_t)CFArrayGetValueAtIndex(array, idxa) == a ? idxa+1 : idxa;
    355     CFIndex keeplen = idxb - keeploc;
    356     
    357     // Include 'a' if it's needed to keep the parity straight.
    358     if(keeploc % 2 == 1) {
    359         keeploc--; keeplen++;
    360         CFArraySetValueAtIndex(array, keeploc, (void*)a);
    361     }
    362     
    363     if(keeploc > 0)
    364         CFArrayReplaceValues(array, CFRangeMake(0, keeploc), NULL, 0);
    365     if(keeploc+keeplen < count)
    366         CFArrayReplaceValues(array, CFRangeMake(0, keeplen), NULL, 0);
    367     
    368     // Include 'b' if it's needed to keep the length even.
    369     if(keeplen % 2 == 1) {
    370         CFArrayAppendValue(array, (void*)b);
    371     }
    372     
    373     assert(CFArrayGetCount(array) % 2 == 0);
    374 }
    375 
    376 
    377 - (void)addRangeSet:(HFRangeSet *)rangeSet {
    378     CFArrayRef a = rangeSet->array;
    379     CFIndex c = CFArrayGetCount(a);
    380     for(CFIndex i2 = 0; i2 < c; i2 += 2) {
    381         HFRangeSetAddRange(array, (uintptr_t)CFArrayGetValueAtIndex(a, i2), (uintptr_t)CFArrayGetValueAtIndex(a, i2+1));
    382     }
    383 }
    384 - (void)removeRangeSet:(HFRangeSet *)rangeSet {
    385     CFArrayRef a = rangeSet->array;
    386     CFIndex c = CFArrayGetCount(a);
    387     for(CFIndex i2 = 0; i2 < c; i2 += 2) {
    388         HFRangeSetRemoveRange(array, (uintptr_t)CFArrayGetValueAtIndex(a, i2), (uintptr_t)CFArrayGetValueAtIndex(a, i2+1));
    389     }
    390 }
    391 - (void)toggleRangeSet:(HFRangeSet *)rangeSet {
    392     CFArrayRef a = rangeSet->array;
    393     CFIndex c = CFArrayGetCount(a);
    394     for(CFIndex i2 = 0; i2 < c; i2 += 2) {
    395         HFRangeSetToggleRange(array, (uintptr_t)CFArrayGetValueAtIndex(a, i2), (uintptr_t)CFArrayGetValueAtIndex(a, i2+1));
    396     }
    397 }
    398 
    399 - (void)clipToRangeSet:(HFRangeSet *)rangeSet {
    400     HFRange span = [rangeSet spanningRange];
    401     [self clipToRange:span];
    402     [self removeRangeSet:[HFRangeSet complementOfRangeSet:rangeSet inRange:span]];
    403 }
    404 
    405 - (BOOL)isEqualToRangeSet:(HFRangeSet *)rangeSet {
    406     // Because our arrays are fully normalized, this just checks for array equality.
    407     CFArrayRef a = rangeSet->array;
    408     CFIndex c = CFArrayGetCount(a);
    409     if(c != CFArrayGetCount(array))
    410         return NO;
    411     
    412     // Optimization: For long arrays, check the last few first,
    413     // since appending to ranges is probably a common usage pattern.
    414     const CFIndex opt_end = 10;
    415     if(c > 2*opt_end) {
    416         for(CFIndex i = c - 2*opt_end; i < c; i++) {
    417             if(CFArrayGetValueAtIndex(a, i) != CFArrayGetValueAtIndex(array, i))
    418                 return NO;
    419         }
    420         c -= 2*opt_end;
    421     }
    422     
    423     for(CFIndex i = 0; i < c; i++) {
    424         if(CFArrayGetValueAtIndex(a, i) != CFArrayGetValueAtIndex(array, i))
    425             return NO;
    426     }
    427     
    428     return YES;
    429 }
    430 
    431 - (BOOL)isEmpty {
    432     return CFArrayGetCount(array) == 0;
    433 }
    434 
    435 - (BOOL)containsAllRange:(HFRange)range {
    436     if(range.length == 0) return YES;
    437     return HFRangeSetContainsAllRange(array, ll2p(range.location), ll2p(HFMaxRange(range)));
    438 }
    439 
    440 - (BOOL)overlapsAnyRange:(HFRange)range {
    441     if(range.length == 0) return NO;
    442     return HFRangeSetOverlapsAnyRange(array, ll2p(range.location), ll2p(HFMaxRange(range)));
    443 }
    444 
    445 - (BOOL)containsAllRangeSet:(HFRangeSet *)rangeSet {
    446     CFArrayRef a = rangeSet->array;
    447     CFIndex c = CFArrayGetCount(a);
    448     
    449     // Optimization: check if containment is possible.
    450     if(!HFRangeIsSubrangeOfRange([rangeSet spanningRange], [self spanningRange])) {
    451         return NO;
    452     }
    453     
    454     for(CFIndex i2 = 0; i2 < c; i2 += 2) {
    455         uintptr_t x = (uintptr_t)CFArrayGetValueAtIndex(a, i2);
    456         uintptr_t y = (uintptr_t)CFArrayGetValueAtIndex(a, i2+1);
    457         if(!HFRangeSetContainsAllRange(array, x, y)) return NO;
    458     }
    459     return YES;
    460 }
    461 
    462 - (BOOL)overlapsAnyRangeSet:(HFRangeSet *)rangeSet {
    463     CFArrayRef a = rangeSet->array;
    464     CFIndex c = CFArrayGetCount(a);
    465 
    466     // Optimization: check if overlap is possible.
    467     if(!HFIntersectsRange([rangeSet spanningRange], [self spanningRange])) {
    468         return NO;
    469     }
    470 
    471     for(CFIndex i2 = 0; i2 < c; i2 += 2) {
    472         uintptr_t x = (uintptr_t)CFArrayGetValueAtIndex(a, i2);
    473         uintptr_t y = (uintptr_t)CFArrayGetValueAtIndex(a, i2+1);
    474         if(!HFRangeSetOverlapsAnyRange(array, x, y)) return YES;
    475     }
    476     return NO;
    477 }
    478 
    479 
    480 - (HFRange)spanningRange {
    481     CFIndex count = CFArrayGetCount(array);
    482     if(count == 0) return HFZeroRange;
    483     
    484     uintptr_t a = (uintptr_t)CFArrayGetValueAtIndex(array, 0);
    485     uintptr_t b = (uintptr_t)CFArrayGetValueAtIndex(array, count-2) + (uintptr_t)CFArrayGetValueAtIndex(array, count-1);
    486     
    487     return HFRangeMake(a, b-a);
    488 }
    489 
    490 - (void)assertIntegrity {
    491     CFIndex count = CFArrayGetCount(array);
    492     HFASSERT(count % 2 == 0);
    493     if(count == 0) return;
    494     
    495     uintptr_t prev = (uintptr_t)CFArrayGetValueAtIndex(array, 0);
    496     for(CFIndex i = 1; i < count; i++) {
    497         uintptr_t val = (uintptr_t)CFArrayGetValueAtIndex(array, i);
    498         HFASSERT(val > prev);
    499         prev = val;
    500     }
    501 }
    502 
    503 - (BOOL)isEqual:(id)object {
    504     if(![object isKindOfClass:[HFRangeSet class]])
    505         return false;
    506     return [self isEqualToRangeSet:object];
    507 }
    508 
    509 - (NSUInteger)hash {
    510     CFIndex count = CFArrayGetCount(array);
    511     NSUInteger x = 0;
    512     for(CFIndex i2 = 0; i2 < count; i2 += 2) {
    513         uintptr_t a = (uintptr_t)CFArrayGetValueAtIndex(array, i2);
    514         uintptr_t b = (uintptr_t)CFArrayGetValueAtIndex(array, i2+1);
    515 #if 6364136223846793005 < NSUIntegerMax
    516         x = (6364136223846793005 * (uint64_t)x + a);
    517 #else
    518         x = (NSUInteger)(1103515245 * (uint64_t)x + a);
    519 #endif
    520         x ^= (NSUInteger)b;
    521     }
    522     return x;
    523 }
    524 
    525 - (id)copyWithZone:(NSZone *)zone {
    526     HFRangeSet *newSet = [[HFRangeSet allocWithZone:zone] init];
    527     CFRelease(newSet->array);
    528     newSet->array = (CFMutableArrayRef)[[NSMutableArray allocWithZone:zone] initWithArray:(NSArray*)array copyItems:NO];
    529     return newSet;
    530 }
    531 
    532 - (void)encodeWithCoder:(NSCoder *)aCoder {
    533     NSUInteger count = CFArrayGetCount(array);
    534     NEW_ARRAY(uint64_t, values, count);
    535     
    536     // Fill array with 64-bit, little endian bytes.
    537     if(sizeof(const void *) == sizeof(uint64_t)) {
    538         // Hooray, we can just use CFArrayGetValues
    539         CFArrayGetValues(array, CFRangeMake(0, count), (const void **)&values);
    540 #if __LITTLE_ENDIAN__
    541 #else
    542         // Boo, we have to swap everything.
    543         for(NSUInteger i = 0; i < count; i++) {
    544             values[i] = CFSwapInt64HostToLittle(values[i]);
    545         }
    546 #endif
    547     } else {
    548         // Boo, we have to iterate through the array.
    549         NSUInteger i = 0;
    550         FOREACH(id, val, (NSArray*)array) {
    551             values[i++] = CFSwapInt64HostToLittle((uint64_t)(const void *)val);
    552         }
    553     }
    554     [aCoder encodeBytes:values length:count * sizeof(*values)];
    555 }
    556 
    557 - (instancetype)initWithCoder:(NSCoder *)aDecoder {
    558     if(!(self = [super init])) return nil;
    559     
    560     NSUInteger count;
    561     uint64_t *values = [aDecoder decodeBytesWithReturnedLength:&count];
    562     array = CFArrayCreateMutable(kCFAllocatorDefault, count+1, NULL);
    563     
    564     for(NSUInteger i = 0; i < count; i++) {
    565         uint64_t x = CFSwapInt64LittleToHost(values[i]);
    566         if(x > UINTPTR_MAX)
    567             goto fail;
    568         CFArrayAppendValue(array, (const void *)(uintptr_t)x);
    569     }
    570     if(CFArrayGetCount(array)%2 != 0)
    571         goto fail;
    572     return self;
    573     
    574 fail:
    575     CFRelease(array);
    576     [super release];
    577     return nil;
    578 }
    579 
    580 + (BOOL)supportsSecureCoding {
    581     return YES;
    582 }
    583 
    584 - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len {
    585     NSUInteger base = state->state;
    586     NSUInteger length = CFArrayGetCount(array)/2;
    587     NSUInteger i = 0;
    588     
    589     while(i < len && base + i < length) {
    590         uintptr_t a = (uintptr_t)CFArrayGetValueAtIndex(array, 2*i);
    591         uintptr_t b = (uintptr_t)CFArrayGetValueAtIndex(array, 2*i+1);
    592         stackbuf[i] = [HFRangeWrapper withRange:HFRangeMake(a, b-a)];
    593     }
    594     
    595     state->state = base + i;
    596     state->itemsPtr = stackbuf;
    597     state->mutationsPtr = &state->extra[0]; // Use simple mutation checking.
    598     state->extra[0] = length;
    599     
    600     return i;
    601 }
    602 
    603 @end
    604 
    605 
    606 BOOL HFStringEncodingIsSupersetOfASCII(NSStringEncoding encoding) {
    607     switch (CFStringConvertNSStringEncodingToEncoding(encoding)) {
    608 	case kCFStringEncodingMacRoman: return YES;
    609 	case kCFStringEncodingWindowsLatin1: return YES;
    610 	case kCFStringEncodingISOLatin1: return YES;
    611 	case kCFStringEncodingNextStepLatin: return YES;
    612 	case kCFStringEncodingASCII: return YES;
    613 	case kCFStringEncodingUnicode: return NO;
    614 	case kCFStringEncodingUTF8: return YES;
    615 	case kCFStringEncodingNonLossyASCII: return NO;
    616 //	case kCFStringEncodingUTF16: return NO;
    617 	case kCFStringEncodingUTF16BE: return NO;
    618 	case kCFStringEncodingUTF16LE: return NO;
    619 	case kCFStringEncodingUTF32: return NO;
    620 	case kCFStringEncodingUTF32BE: return NO;
    621 	case kCFStringEncodingUTF32LE: return NO;
    622 	case kCFStringEncodingMacJapanese: return NO;
    623 	case kCFStringEncodingMacChineseTrad: return YES;
    624 	case kCFStringEncodingMacKorean: return YES;
    625 	case kCFStringEncodingMacArabic: return NO;
    626 	case kCFStringEncodingMacHebrew: return NO;
    627 	case kCFStringEncodingMacGreek: return YES;
    628 	case kCFStringEncodingMacCyrillic: return YES;
    629 	case kCFStringEncodingMacDevanagari: return YES;
    630 	case kCFStringEncodingMacGurmukhi: return YES;
    631 	case kCFStringEncodingMacGujarati: return YES;
    632 	case kCFStringEncodingMacOriya: return YES;
    633 	case kCFStringEncodingMacBengali: return YES;
    634 	case kCFStringEncodingMacTamil: return YES;
    635 	case kCFStringEncodingMacTelugu: return YES;
    636 	case kCFStringEncodingMacKannada: return YES;
    637 	case kCFStringEncodingMacMalayalam: return YES;
    638 	case kCFStringEncodingMacSinhalese: return YES;
    639 	case kCFStringEncodingMacBurmese: return YES;
    640 	case kCFStringEncodingMacKhmer: return YES;
    641 	case kCFStringEncodingMacThai: return YES;
    642 	case kCFStringEncodingMacLaotian: return YES;
    643 	case kCFStringEncodingMacGeorgian: return YES;
    644 	case kCFStringEncodingMacArmenian: return YES;
    645 	case kCFStringEncodingMacChineseSimp: return YES;
    646 	case kCFStringEncodingMacTibetan: return YES;
    647 	case kCFStringEncodingMacMongolian: return YES;
    648 	case kCFStringEncodingMacEthiopic: return YES;
    649 	case kCFStringEncodingMacCentralEurRoman: return YES;
    650 	case kCFStringEncodingMacVietnamese: return YES;
    651 	case kCFStringEncodingMacExtArabic: return YES;
    652 	case kCFStringEncodingMacSymbol: return NO;
    653 	case kCFStringEncodingMacDingbats: return NO;
    654 	case kCFStringEncodingMacTurkish: return YES;
    655 	case kCFStringEncodingMacCroatian: return YES;
    656 	case kCFStringEncodingMacIcelandic: return YES;
    657 	case kCFStringEncodingMacRomanian: return YES;
    658 	case kCFStringEncodingMacCeltic: return YES;
    659 	case kCFStringEncodingMacGaelic: return YES;
    660 	case kCFStringEncodingMacFarsi: return YES;
    661 	case kCFStringEncodingMacUkrainian: return NO;
    662 	case kCFStringEncodingMacInuit: return YES;
    663 	case kCFStringEncodingMacVT100: return YES;
    664 	case kCFStringEncodingMacHFS: return YES;
    665 	case kCFStringEncodingISOLatin2: return YES;
    666 	case kCFStringEncodingISOLatin3: return YES;
    667 	case kCFStringEncodingISOLatin4: return YES;
    668 	case kCFStringEncodingISOLatinCyrillic: return YES;
    669 	case kCFStringEncodingISOLatinArabic: return NO;
    670 	case kCFStringEncodingISOLatinGreek: return YES;
    671 	case kCFStringEncodingISOLatinHebrew: return YES;
    672 	case kCFStringEncodingISOLatin5: return YES;
    673 	case kCFStringEncodingISOLatin6: return YES;
    674 	case kCFStringEncodingISOLatinThai: return YES;
    675 	case kCFStringEncodingISOLatin7: return YES;
    676 	case kCFStringEncodingISOLatin8: return YES;
    677 	case kCFStringEncodingISOLatin9: return YES;
    678 	case kCFStringEncodingISOLatin10: return YES;
    679 	case kCFStringEncodingDOSLatinUS: return YES;
    680 	case kCFStringEncodingDOSGreek: return YES;
    681 	case kCFStringEncodingDOSBalticRim: return YES;
    682 	case kCFStringEncodingDOSLatin1: return YES;
    683 	case kCFStringEncodingDOSGreek1: return YES;
    684 	case kCFStringEncodingDOSLatin2: return YES;
    685 	case kCFStringEncodingDOSCyrillic: return YES;
    686 	case kCFStringEncodingDOSTurkish: return YES;
    687 	case kCFStringEncodingDOSPortuguese: return YES;
    688 	case kCFStringEncodingDOSIcelandic: return YES;
    689 	case kCFStringEncodingDOSHebrew: return YES;
    690 	case kCFStringEncodingDOSCanadianFrench: return YES;
    691 	case kCFStringEncodingDOSArabic: return YES;
    692 	case kCFStringEncodingDOSNordic: return YES;
    693 	case kCFStringEncodingDOSRussian: return YES;
    694 	case kCFStringEncodingDOSGreek2: return YES;
    695 	case kCFStringEncodingDOSThai: return YES;
    696 	case kCFStringEncodingDOSJapanese: return YES;
    697 	case kCFStringEncodingDOSChineseSimplif: return YES;
    698 	case kCFStringEncodingDOSKorean: return YES;
    699 	case kCFStringEncodingDOSChineseTrad: return YES;
    700 	case kCFStringEncodingWindowsLatin2: return YES;
    701 	case kCFStringEncodingWindowsCyrillic: return YES;
    702 	case kCFStringEncodingWindowsGreek: return YES;
    703 	case kCFStringEncodingWindowsLatin5: return YES;
    704 	case kCFStringEncodingWindowsHebrew: return YES;
    705 	case kCFStringEncodingWindowsArabic: return YES;
    706 	case kCFStringEncodingWindowsBalticRim: return YES;
    707 	case kCFStringEncodingWindowsVietnamese: return YES;
    708 	case kCFStringEncodingWindowsKoreanJohab: return YES;
    709 	case kCFStringEncodingANSEL: return NO;
    710 	case kCFStringEncodingJIS_X0201_76: return NO;
    711 	case kCFStringEncodingJIS_X0208_83: return NO;
    712 	case kCFStringEncodingJIS_X0208_90: return NO;
    713 	case kCFStringEncodingJIS_X0212_90: return NO;
    714 	case kCFStringEncodingJIS_C6226_78: return NO;
    715 	case 0x0628/*kCFStringEncodingShiftJIS_X0213*/: return NO;
    716 	case kCFStringEncodingShiftJIS_X0213_MenKuTen: return NO;
    717 	case kCFStringEncodingGB_2312_80: return NO;
    718 	case kCFStringEncodingGBK_95: return NO;
    719 	case kCFStringEncodingGB_18030_2000: return NO;
    720 	case kCFStringEncodingKSC_5601_87: return NO;
    721 	case kCFStringEncodingKSC_5601_92_Johab: return NO;
    722 	case kCFStringEncodingCNS_11643_92_P1: return NO;
    723 	case kCFStringEncodingCNS_11643_92_P2: return NO;
    724 	case kCFStringEncodingCNS_11643_92_P3: return NO;
    725 	case kCFStringEncodingISO_2022_JP: return NO;
    726 	case kCFStringEncodingISO_2022_JP_2: return NO;
    727 	case kCFStringEncodingISO_2022_JP_1: return NO;
    728 	case kCFStringEncodingISO_2022_JP_3: return NO;
    729 	case kCFStringEncodingISO_2022_CN: return NO;
    730 	case kCFStringEncodingISO_2022_CN_EXT: return NO;
    731 	case kCFStringEncodingISO_2022_KR: return NO;
    732 	case kCFStringEncodingEUC_JP: return YES;
    733 	case kCFStringEncodingEUC_CN: return YES;
    734 	case kCFStringEncodingEUC_TW: return YES;
    735 	case kCFStringEncodingEUC_KR: return YES;
    736 	case kCFStringEncodingShiftJIS: return NO;
    737 	case kCFStringEncodingKOI8_R: return YES;
    738 	case kCFStringEncodingBig5: return YES;
    739 	case kCFStringEncodingMacRomanLatin1: return YES;
    740 	case kCFStringEncodingHZ_GB_2312: return NO;
    741 	case kCFStringEncodingBig5_HKSCS_1999: return YES;
    742 	case kCFStringEncodingVISCII: return YES; // though not quite
    743 	case kCFStringEncodingKOI8_U: return YES;
    744 	case kCFStringEncodingBig5_E: return YES;
    745 	case kCFStringEncodingNextStepJapanese: return YES;
    746 	case kCFStringEncodingEBCDIC_US: return NO;
    747 	case kCFStringEncodingEBCDIC_CP037: return NO;
    748         default:
    749             NSLog(@"Unknown string encoding %lu in %s", (unsigned long)encoding, __FUNCTION__);
    750             return NO;
    751     }
    752 }
    753 
    754 uint8_t HFStringEncodingCharacterLength(NSStringEncoding encoding) {
    755     switch (CFStringConvertNSStringEncodingToEncoding(encoding)) {
    756 	case kCFStringEncodingMacRoman: return 1;
    757 	case kCFStringEncodingWindowsLatin1: return 1;
    758 	case kCFStringEncodingISOLatin1: return 1;
    759 	case kCFStringEncodingNextStepLatin: return 1;
    760 	case kCFStringEncodingASCII: return 1;
    761 	case kCFStringEncodingUnicode: return 2;
    762 	case kCFStringEncodingUTF8: return 1;
    763 	case kCFStringEncodingNonLossyASCII: return 1;
    764             //	case kCFStringEncodingUTF16: return 2;
    765 	case kCFStringEncodingUTF16BE: return 2;
    766 	case kCFStringEncodingUTF16LE: return 2;
    767 	case kCFStringEncodingUTF32: return 4;
    768 	case kCFStringEncodingUTF32BE: return 4;
    769 	case kCFStringEncodingUTF32LE: return 4;
    770 	case kCFStringEncodingMacJapanese: return 1;
    771 	case kCFStringEncodingMacChineseTrad: return 1; // ??
    772 	case kCFStringEncodingMacKorean: return 1;
    773 	case kCFStringEncodingMacArabic: return 1;
    774 	case kCFStringEncodingMacHebrew: return 1;
    775 	case kCFStringEncodingMacGreek: return 1;
    776 	case kCFStringEncodingMacCyrillic: return 1;
    777 	case kCFStringEncodingMacDevanagari: return 1;
    778 	case kCFStringEncodingMacGurmukhi: return 1;
    779 	case kCFStringEncodingMacGujarati: return 1;
    780 	case kCFStringEncodingMacOriya: return 1;
    781 	case kCFStringEncodingMacBengali: return 1;
    782 	case kCFStringEncodingMacTamil: return 1;
    783 	case kCFStringEncodingMacTelugu: return 1;
    784 	case kCFStringEncodingMacKannada: return 1;
    785 	case kCFStringEncodingMacMalayalam: return 1;
    786 	case kCFStringEncodingMacSinhalese: return 1;
    787 	case kCFStringEncodingMacBurmese: return 1;
    788 	case kCFStringEncodingMacKhmer: return 1;
    789 	case kCFStringEncodingMacThai: return 1;
    790 	case kCFStringEncodingMacLaotian: return 1;
    791 	case kCFStringEncodingMacGeorgian: return 1;
    792 	case kCFStringEncodingMacArmenian: return 1;
    793 	case kCFStringEncodingMacChineseSimp: return 1;
    794 	case kCFStringEncodingMacTibetan: return 1;
    795 	case kCFStringEncodingMacMongolian: return 1;
    796 	case kCFStringEncodingMacEthiopic: return 1;
    797 	case kCFStringEncodingMacCentralEurRoman: return 1;
    798 	case kCFStringEncodingMacVietnamese: return 1;
    799 	case kCFStringEncodingMacExtArabic: return 1;
    800 	case kCFStringEncodingMacSymbol: return 1;
    801 	case kCFStringEncodingMacDingbats: return 1;
    802 	case kCFStringEncodingMacTurkish: return 1;
    803 	case kCFStringEncodingMacCroatian: return 1;
    804 	case kCFStringEncodingMacIcelandic: return 1;
    805 	case kCFStringEncodingMacRomanian: return 1;
    806 	case kCFStringEncodingMacCeltic: return 1;
    807 	case kCFStringEncodingMacGaelic: return 1;
    808 	case kCFStringEncodingMacFarsi: return 1;
    809 	case kCFStringEncodingMacUkrainian: return 1;
    810 	case kCFStringEncodingMacInuit: return 1;
    811 	case kCFStringEncodingMacVT100: return 1;
    812 	case kCFStringEncodingMacHFS: return 1;
    813 	case kCFStringEncodingISOLatin2: return 1;
    814 	case kCFStringEncodingISOLatin3: return 1;
    815 	case kCFStringEncodingISOLatin4: return 1;
    816 	case kCFStringEncodingISOLatinCyrillic: return 1;
    817 	case kCFStringEncodingISOLatinArabic: return 1;
    818 	case kCFStringEncodingISOLatinGreek: return 1;
    819 	case kCFStringEncodingISOLatinHebrew: return 1;
    820 	case kCFStringEncodingISOLatin5: return 1;
    821 	case kCFStringEncodingISOLatin6: return 1;
    822 	case kCFStringEncodingISOLatinThai: return 1;
    823 	case kCFStringEncodingISOLatin7: return 1;
    824 	case kCFStringEncodingISOLatin8: return 1;
    825 	case kCFStringEncodingISOLatin9: return 1;
    826 	case kCFStringEncodingISOLatin10: return 1;
    827 	case kCFStringEncodingDOSLatinUS: return 1;
    828 	case kCFStringEncodingDOSGreek: return 1;
    829 	case kCFStringEncodingDOSBalticRim: return 1;
    830 	case kCFStringEncodingDOSLatin1: return 1;
    831 	case kCFStringEncodingDOSGreek1: return 1;
    832 	case kCFStringEncodingDOSLatin2: return 1;
    833 	case kCFStringEncodingDOSCyrillic: return 1;
    834 	case kCFStringEncodingDOSTurkish: return 1;
    835 	case kCFStringEncodingDOSPortuguese: return 1;
    836 	case kCFStringEncodingDOSIcelandic: return 1;
    837 	case kCFStringEncodingDOSHebrew: return 1;
    838 	case kCFStringEncodingDOSCanadianFrench: return 1;
    839 	case kCFStringEncodingDOSArabic: return 1;
    840 	case kCFStringEncodingDOSNordic: return 1;
    841 	case kCFStringEncodingDOSRussian: return 1;
    842 	case kCFStringEncodingDOSGreek2: return 1;
    843 	case kCFStringEncodingDOSThai: return 1;
    844 	case kCFStringEncodingDOSJapanese: return 1;
    845 	case kCFStringEncodingDOSChineseSimplif: return 1;
    846 	case kCFStringEncodingDOSKorean: return 1;
    847 	case kCFStringEncodingDOSChineseTrad: return 1;
    848 	case kCFStringEncodingWindowsLatin2: return 1;
    849 	case kCFStringEncodingWindowsCyrillic: return 1;
    850 	case kCFStringEncodingWindowsGreek: return 1;
    851 	case kCFStringEncodingWindowsLatin5: return 1;
    852 	case kCFStringEncodingWindowsHebrew: return 1;
    853 	case kCFStringEncodingWindowsArabic: return 1;
    854 	case kCFStringEncodingWindowsBalticRim: return 1;
    855 	case kCFStringEncodingWindowsVietnamese: return 1;
    856 	case kCFStringEncodingWindowsKoreanJohab: return 1;
    857 	case kCFStringEncodingANSEL: return 1;
    858 	case kCFStringEncodingJIS_X0201_76: return 1;
    859 	case kCFStringEncodingJIS_X0208_83: return 1;
    860 	case kCFStringEncodingJIS_X0208_90: return 1;
    861 	case kCFStringEncodingJIS_X0212_90: return 1;
    862 	case kCFStringEncodingJIS_C6226_78: return 1;
    863 	case 0x0628/*kCFStringEncodingShiftJIS_X0213*/: return 1;
    864 	case kCFStringEncodingShiftJIS_X0213_MenKuTen: return 1;
    865 	case kCFStringEncodingGB_2312_80: return 1;
    866 	case kCFStringEncodingGBK_95: return 1;
    867 	case kCFStringEncodingGB_18030_2000: return 1;
    868 	case kCFStringEncodingKSC_5601_87: return 1;
    869 	case kCFStringEncodingKSC_5601_92_Johab: return 1;
    870 	case kCFStringEncodingCNS_11643_92_P1: return 1;
    871 	case kCFStringEncodingCNS_11643_92_P2: return 1;
    872 	case kCFStringEncodingCNS_11643_92_P3: return 1;
    873 	case kCFStringEncodingISO_2022_JP: return 1;
    874 	case kCFStringEncodingISO_2022_JP_2: return 1;
    875 	case kCFStringEncodingISO_2022_JP_1: return 1;
    876 	case kCFStringEncodingISO_2022_JP_3: return 1;
    877 	case kCFStringEncodingISO_2022_CN: return 1;
    878 	case kCFStringEncodingISO_2022_CN_EXT: return 1;
    879 	case kCFStringEncodingISO_2022_KR: return 1;
    880 	case kCFStringEncodingEUC_JP: return 1;
    881 	case kCFStringEncodingEUC_CN: return 1;
    882 	case kCFStringEncodingEUC_TW: return 1;
    883 	case kCFStringEncodingEUC_KR: return 1;
    884 	case kCFStringEncodingShiftJIS: return 1;
    885 	case kCFStringEncodingKOI8_R: return 1;
    886 	case kCFStringEncodingBig5: return 2; //yay, a 2
    887 	case kCFStringEncodingMacRomanLatin1: return 1;
    888 	case kCFStringEncodingHZ_GB_2312: return 2;
    889 	case kCFStringEncodingBig5_HKSCS_1999: return 1;
    890 	case kCFStringEncodingVISCII: return 1;
    891 	case kCFStringEncodingKOI8_U: return 1;
    892 	case kCFStringEncodingBig5_E: return 2;
    893 	case kCFStringEncodingNextStepJapanese: return YES; // ??
    894 	case kCFStringEncodingEBCDIC_US: return 1; //lol
    895 	case kCFStringEncodingEBCDIC_CP037: return 1;
    896 	case kCFStringEncodingUTF7: return 1;
    897 	case kCFStringEncodingUTF7_IMAP : return 1;
    898         default:
    899             NSLog(@"Unknown string encoding %lx in %s", (long)encoding, __FUNCTION__);
    900             return 1;
    901     }    
    902 }
    903 
    904 /* Converts a hexadecimal digit into a corresponding 4 bit unsigned int; returns -1 on failure.  The ... is a gcc extension. */
    905 static NSInteger char2hex(unichar c) {
    906     switch (c) {
    907         case '0' ... '9': return c - '0';
    908         case 'a' ... 'f': return c - 'a' + 10;
    909         case 'A' ... 'F': return c - 'A' + 10;
    910         default: return -1;
    911     }
    912 }
    913 
    914 static unsigned char hex2char(NSUInteger c) {
    915     HFASSERT(c < 16);
    916     return "0123456789ABCDEF"[c];
    917 }
    918 
    919 NSData *HFDataFromHexString(NSString *string, BOOL* isMissingLastNybble) {
    920     REQUIRE_NOT_NULL(string);
    921     NSUInteger stringIndex=0, resultIndex=0, max=[string length];
    922     NSMutableData* result = [NSMutableData dataWithLength:(max + 1)/2];
    923     unsigned char* bytes = [result mutableBytes];
    924     
    925     NSUInteger numNybbles = 0;
    926     unsigned char byteValue = 0;
    927     
    928     for (stringIndex = 0; stringIndex < max; stringIndex++) {
    929         NSInteger val = char2hex([string characterAtIndex:stringIndex]);
    930         if (val < 0) continue;
    931         numNybbles++;
    932         byteValue = byteValue * 16 + (unsigned char)val;
    933         if (! (numNybbles % 2)) {
    934             bytes[resultIndex++] = byteValue;
    935             byteValue = 0;
    936         }
    937     }
    938     
    939     if (isMissingLastNybble) *isMissingLastNybble = (numNybbles % 2);
    940     
    941     //final nibble
    942     if (numNybbles % 2) {
    943         bytes[resultIndex++] = byteValue;
    944     }
    945     
    946     [result setLength:resultIndex];
    947     return result;    
    948 }
    949 
    950 NSString *HFHexStringFromData(NSData *data) {
    951     REQUIRE_NOT_NULL(data);
    952     NSUInteger dataLength = [data length];
    953     NSUInteger stringLength = HFProductInt(dataLength, 2);
    954     const unsigned char *bytes = [data bytes];
    955     unsigned char *charBuffer = check_malloc(stringLength);
    956     NSUInteger charIndex = 0, byteIndex;
    957     for (byteIndex = 0; byteIndex < dataLength; byteIndex++) {
    958         unsigned char byte = bytes[byteIndex];
    959         charBuffer[charIndex++] = hex2char(byte >> 4);
    960         charBuffer[charIndex++] = hex2char(byte & 0xF);
    961     }
    962     return [[[NSString alloc] initWithBytesNoCopy:charBuffer length:stringLength encoding:NSASCIIStringEncoding freeWhenDone:YES] autorelease];
    963 }
    964 
    965 void HFSetFDShouldCache(int fd, BOOL shouldCache) {
    966     int result = fcntl(fd, F_NOCACHE, !shouldCache);
    967     if (result == -1) {
    968         int err = errno;
    969         NSLog(@"fcntl(%d, F_NOCACHE, %d) returned error %d: %s", fd, !shouldCache, err, strerror(err));
    970     }
    971 }
    972 
    973 NSString *HFDescribeByteCount(unsigned long long count) {
    974     return HFDescribeByteCountWithPrefixAndSuffix(NULL, count, NULL);
    975 }
    976 
    977 /* A big_num represents a number in some base.  Here it is value = big * base + little. */
    978 typedef struct big_num {
    979     unsigned int big; 
    980     unsigned long long little;
    981 } big_num;
    982 
    983 static inline big_num divide_bignum_by_2(big_num a, unsigned long long base) {
    984     //value = a.big * base + a.little;
    985     big_num result;
    986     result.big = a.big / 2;
    987     unsigned int shiftedRemainder = (unsigned int)(a.little & 1);
    988     result.little = a.little / 2;
    989     if (a.big & 1) {
    990         //need to add base/2 to result.little.  We know that won't overflow because result.little is already a.little / 2
    991         result.little += base / 2;
    992         
    993         // If we shift off a bit for base/2, and we also shifted off a bit for a.little/2, then we have a carry bit we need to add
    994         if ((base & 1) && shiftedRemainder) {
    995             /* Is there a chance that adding 1 will overflow?  We know base is odd (base & 1), so consider an example of base = 9.  Then the largest that result.little could be is (9 - 1)/2 + base/2 = 8.  We could add 1 and get back to base, but we can never exceed base, so we cannot overflow an unsigned long long. */
    996             result.little += 1;
    997             HFASSERT(result.little <= base);
    998             if (result.little == base) {
    999                 result.big++;
   1000                 result.little = 0;
   1001             }
   1002         }
   1003     }
   1004     HFASSERT(result.little < base);
   1005     return result;
   1006 }
   1007 
   1008 static inline big_num add_big_nums(big_num a, big_num b, unsigned long long base) {
   1009     /* Perform the addition result += left.  The addition is:
   1010       result.big = a.big + b.big + (a.little + b.little) / base
   1011       result.little = (a.little + b.little) % base
   1012       
   1013       a.little + b.little may overflow, so we have to take some care in how we calculate them.
   1014       Since both a.little and b.little are less than base, we know that if we overflow, we can subtract base from it to underflow and still get the same remainder.
   1015     */
   1016     unsigned long long remainder = a.little + b.little;
   1017     unsigned int dividend = 0;
   1018     // remainder < a.little detects overflow, and remainder >= base detects the case where we did not overflow but are larger than base
   1019     if (remainder < a.little || remainder >= base) {
   1020         remainder -= base;
   1021         dividend++;
   1022     }
   1023     HFASSERT(remainder < base);
   1024     
   1025     big_num result = {a.big + b.big + dividend, remainder};
   1026     return result;
   1027 }
   1028 
   1029 
   1030 /* Returns the first digit after the decimal point for a / b, rounded off, without overflow.  This may return 10, indicating that the digit is 0 and we should carry. */
   1031 static unsigned int computeRemainderPrincipalDigit(unsigned long long a, unsigned long long base) {
   1032     struct big_num result = {0, 0}, left = {(unsigned)(a / base), a % base}, right = {(unsigned)(100 / base), 100 % base};
   1033     while (right.big > 0 || right.little > 0) {
   1034         /* Determine the least significant bit of right, which is right.big * base + right.little */
   1035         unsigned int bigTermParity = (base & 1) && (right.big & 1);
   1036         unsigned int littleTermParity = (unsigned)(right.little & 1);
   1037         if (bigTermParity != littleTermParity) result = add_big_nums(result, left, base);
   1038 
   1039         right = divide_bignum_by_2(right, base);
   1040         left = add_big_nums(left, left, base);
   1041     }
   1042 
   1043     //result.big now contains 100 * a / base
   1044     unsigned int principalTwoDigits = (unsigned int)(result.big % 100);
   1045     unsigned int principalDigit = (principalTwoDigits / 10) + ((principalTwoDigits % 10) >= 5);
   1046     return principalDigit;
   1047 }
   1048 
   1049 NSString *HFDescribeByteCountWithPrefixAndSuffix(const char *stringPrefix, unsigned long long count, const char *stringSuffix) {
   1050     if (! stringPrefix) stringPrefix = "";
   1051     if (! stringSuffix) stringSuffix = "";
   1052 
   1053     if (count == 0) return [NSString stringWithFormat:@"%s0 bytes%s", stringPrefix, stringSuffix];
   1054                             
   1055     const struct {
   1056         unsigned long long size;
   1057         const char *suffix;
   1058     } suffixes[] = {
   1059         {1ULL<<0,   "byte"},
   1060         {1ULL<<10,  "byte"},
   1061         {1ULL<<20,  "kilobyte"},
   1062         {1ULL<<30,  "megabyte"},
   1063         {1ULL<<40,  "gigabyte"},
   1064         {1ULL<<50,  "terabyte"},
   1065         {1ULL<<60,  "petabyte"},
   1066         {ULLONG_MAX, "exabyte"}
   1067     };
   1068     const unsigned numSuffixes = sizeof suffixes / sizeof *suffixes;
   1069     //HFASSERT((sizeof sizes / sizeof *sizes) == (sizeof suffixes / sizeof *suffixes));
   1070     unsigned i;
   1071     unsigned long long base;
   1072     for (i=0; i < numSuffixes; i++) {
   1073         if (count < suffixes[i].size || suffixes[i].size == ULLONG_MAX) break;
   1074     }
   1075     
   1076     if (i >= numSuffixes) return [NSString stringWithFormat:@"%san unbelievable number of bytes%s", stringPrefix, stringSuffix];
   1077     base = suffixes[i-1].size;
   1078     
   1079     unsigned long long dividend = count / base;
   1080     unsigned int remainderPrincipalDigit = computeRemainderPrincipalDigit(count % base, base);
   1081     HFASSERT(remainderPrincipalDigit <= 10);
   1082     if (remainderPrincipalDigit == 10) {
   1083         /* Carry */
   1084         dividend++;
   1085         remainderPrincipalDigit = 0;
   1086     }
   1087         
   1088     BOOL needsPlural = (dividend != 1 || remainderPrincipalDigit > 0);
   1089     
   1090     char remainderBuff[64];
   1091     if (remainderPrincipalDigit > 0) snprintf(remainderBuff, sizeof remainderBuff, ".%u", remainderPrincipalDigit);
   1092     else remainderBuff[0] = 0;
   1093     
   1094     char* resultPointer = NULL;
   1095     int numChars = asprintf(&resultPointer, "%s%llu%s %s%s%s", stringPrefix, dividend, remainderBuff, suffixes[i].suffix, needsPlural ? "s" : "", stringSuffix);
   1096     if (numChars < 0) return NULL;
   1097     return [[[NSString alloc] initWithBytesNoCopy:resultPointer length:numChars encoding:NSASCIIStringEncoding freeWhenDone:YES] autorelease];
   1098 }
   1099 
   1100 static CGFloat interpolateShadow(CGFloat val) {
   1101     //A value of 1 means we are at the rightmost, and should return our max value.  By adjusting the scale, we control how quickly the shadow drops off.
   1102     CGFloat scale = 1.4;
   1103     return (CGFloat)(expm1(val * scale) / expm1(scale));
   1104 }
   1105 
   1106 void HFDrawShadow(CGContextRef ctx, NSRect rect, CGFloat shadowSize, NSRectEdge rectEdge, BOOL drawActive, NSRect clip) {
   1107     NSRect remainingRect, unused;
   1108     NSDivideRect(rect, &remainingRect, &unused, shadowSize, rectEdge);
   1109     
   1110     CGFloat maxAlpha = (drawActive ? .25 : .10);
   1111 
   1112     for (CGFloat i=0; i < shadowSize; i++) {
   1113         NSRect shadowLine;
   1114         NSDivideRect(remainingRect, &shadowLine, &remainingRect, 1, rectEdge);
   1115         
   1116         NSRect clippedLine = NSIntersectionRect(shadowLine, clip);
   1117         if (! NSIsEmptyRect(clippedLine)) {   
   1118             CGFloat gray = 0.;
   1119             CGFloat alpha = maxAlpha * interpolateShadow((shadowSize - i) / shadowSize);
   1120             CGContextSetGrayFillColor(ctx, gray, alpha);
   1121             CGContextFillRect(ctx, NSRectToCGRect(clippedLine));
   1122         }
   1123     }
   1124 
   1125 }
   1126 
   1127 void HFRegisterViewForWindowAppearanceChanges(NSView *self, SEL notificationSEL, BOOL appToo) {
   1128     NSWindow *window = [self window];
   1129     NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
   1130     if (window) {
   1131         [center addObserver:self selector:notificationSEL name:NSWindowDidBecomeKeyNotification object:window];
   1132         [center addObserver:self selector:notificationSEL name:NSWindowDidResignKeyNotification object:window];
   1133     }
   1134     if (appToo) {
   1135         [center addObserver:self selector:notificationSEL name:NSApplicationDidBecomeActiveNotification object:nil];
   1136         [center addObserver:self selector:notificationSEL name:NSApplicationDidResignActiveNotification object:nil];        
   1137     }
   1138 }
   1139 
   1140 void HFUnregisterViewForWindowAppearanceChanges(NSView *self, BOOL appToo) {
   1141     NSWindow *window = [self window];
   1142     NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
   1143     if (window) {
   1144         [center removeObserver:self name:NSWindowDidBecomeKeyNotification object:window];
   1145         [center removeObserver:self name:NSWindowDidResignKeyNotification object:window];        
   1146     }
   1147     if (appToo) {
   1148         [center removeObserver:self name:NSApplicationDidBecomeActiveNotification object:nil];
   1149         [center removeObserver:self name:NSApplicationDidResignActiveNotification object:nil];
   1150     }    
   1151 }
   1152 
   1153 #if USE_CHUD
   1154 void HFStartTiming(const char *name) {
   1155     static BOOL inited;
   1156     if (! inited) {
   1157         inited = YES;
   1158         chudInitialize();
   1159         chudSetErrorLogFile(stderr);
   1160         chudAcquireRemoteAccess();
   1161     }
   1162     chudStartRemotePerfMonitor(name);
   1163     
   1164 }
   1165 
   1166 void HFStopTiming(void) {
   1167     chudStopRemotePerfMonitor();
   1168 }
   1169 #else
   1170 void HFStartTiming(const char *name) { USE(name); }
   1171 void HFStopTiming(void) { }
   1172 #endif

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.