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