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.