HexFiend: HFFunctions.h Source File

HexFiend

HFFunctions.h
Go to the documentation of this file.
1 /* Functions and convenience methods for working with HFTypes */
2
3 #import <HexFiend/HFTypes.h>
4 #import <libkern/OSAtomic.h>
5
6 #define HFDEFAULT_FONT (@"Monaco")
7 #define HFDEFAULT_FONTSIZE ((CGFloat)10.)
8
9 #define HFZeroRange (HFRange){0, 0}
10
14 static inline HFRange HFRangeMake(unsigned long long loc, unsigned long long len) {
15 return (HFRange){loc, len};
16 }
17
21 static inline BOOL HFLocationInRange(unsigned long long location, HFRange range) {
22 return location >= range.location && location - range.location < range.length;
23 }
24
28 static inline NSString* HFRangeToString(HFRange range) {
29 return [NSString stringWithFormat:@"{%llu, %llu}", range.location, range.length];
30 }
31
35 static inline NSString* HFFPRangeToString(HFFPRange range) {
36 return [NSString stringWithFormat:@"{%Lf, %Lf}", range.location, range.length];
37 }
38
42 static inline BOOL HFRangeEqualsRange(HFRange a, HFRange b) {
43 return a.location == b.location && a.length == b.length;
44 }
45
49 static inline BOOL HFSumDoesNotOverflow(unsigned long long a, unsigned long long b) {
50 return a + b >= a;
51 }
52
56 static inline BOOL HFProductDoesNotOverflow(unsigned long long a, unsigned long long b) {
57 if (b == 0) return YES;
58 unsigned long long result = a * b;
59 return result / b == a;
60 }
61
65 static inline NSUInteger HFProductInt(NSUInteger a, NSUInteger b) {
66 NSUInteger result = a * b;
67 assert(a == 0 || result / a == b); //detect overflow
68 return result;
69 }
70
74 static inline NSUInteger HFSumInt(NSUInteger a, NSUInteger b) {
75 assert(a + b >= a);
76 return a + b;
77 }
78
82 static inline NSUInteger HFSumIntSaturate(NSUInteger a, NSUInteger b) {
83 NSUInteger result = a + b;
84 return (result < a) ? NSUIntegerMax : result;
85 }
86
90 static inline unsigned long long HFSumULLSaturate(unsigned long long a, unsigned long long b) {
91 unsigned long long result = a + b;
92 return (result < a) ? ULLONG_MAX : result;
93 }
94
98 static inline unsigned long long HFProductULL(unsigned long long a, unsigned long long b) {
99 unsigned long long result = a * b;
100 assert(HFProductDoesNotOverflow(a, b)); //detect overflow
101 return result;
102 }
103
107 static inline unsigned long long HFSum(unsigned long long a, unsigned long long b) {
108 assert(HFSumDoesNotOverflow(a, b));
109 return a + b;
110 }
111
115 static inline unsigned long long HFMaxULL(unsigned long long a, unsigned long long b) {
116 return a < b ? b : a;
117 }
118
122 static inline unsigned long long HFSubtract(unsigned long long a, unsigned long long b) {
123 assert(a >= b);
124 return a - b;
125 }
126
130 static inline unsigned long long HFRoundUpToMultiple(unsigned long long a, unsigned long long b) {
131 // The usual approach of ((a + (b - 1)) / b) * b doesn't handle overflow correctly
132 unsigned long long remainder = a % b;
133 if (remainder == 0) return a;
134 else return HFSum(a, b - remainder);
135 }
136
140 static inline NSUInteger HFRoundUpToMultipleInt(NSUInteger a, NSUInteger b) {
141 // The usual approach of ((a + (b - 1)) / b) * b doesn't handle overflow correctly
142 NSUInteger remainder = a % b;
143 if (remainder == 0) return a;
144 else return (NSUInteger)HFSum(a, b - remainder);
145 }
146
150 static inline NSUInteger HFLeastCommonMultiple(NSUInteger a, NSUInteger b) {
151 assert(a > 0);
152 assert(b > 0);
153
154 /* Compute GCD. It ends up in U. */
155 NSUInteger t, u = a, v = b;
156 while (v > 0) {
157 t = v;
158 v = u % v;
159 u = t;
160 }
161
162 /* Return the product divided by the GCD, in an overflow safe manner */
163 return HFProductInt(a/u, b);
164 }
165
166
170 static inline unsigned long long HFRoundUpToNextMultipleSaturate(unsigned long long a, unsigned long long b) {
171 assert(b > 0);
172 unsigned long long result = a + (b - a % b);
173 if (result < a) result = ULLONG_MAX; //the saturation...on overflow go to the max
174 return result;
175 }
176
178 static inline unsigned long long HFMaxRange(HFRange a) {
179 assert(HFSumDoesNotOverflow(a.location, a.length));
180 return a.location + a.length;
181 }
182
184 static inline BOOL HFRangeIsSubrangeOfRange(HFRange needle, HFRange haystack) {
185 // If needle starts before haystack, or if needle is longer than haystack, it is not a subrange of haystack
186 if (needle.location < haystack.location || needle.length > haystack.length) return NO;
187
188 // Their difference in lengths determines the maximum difference in their start locations. We know that these expressions cannot overflow because of the above checks.
189 return haystack.length - needle.length >= needle.location - haystack.location;
190 }
191
193 static inline void HFRangeSplitAboutSubrange(HFRange range, HFRange subrange, HFRange *outPrefix, HFRange *outSuffix) {
194 // Requires it to be a subrange
195 assert(HFRangeIsSubrangeOfRange(subrange, range));
196 outPrefix->location = range.location;
197 outPrefix->length = HFSubtract(subrange.location, range.location);
198 outSuffix->location = HFMaxRange(subrange);
199 outSuffix->length = HFMaxRange(range) - outSuffix->location;
200 }
201
203 static inline BOOL HFIntersectsRange(HFRange a, HFRange b) {
204 // Ranges are said to intersect if they share at least one value. Therefore, zero length ranges never intersect anything.
205 if (a.length == 0 || b.length == 0) return NO;
206
207 // rearrange (a.location < b.location + b.length && b.location < a.location + a.length) to not overflow
208 // = ! (a.location >= b.location + b.length || b.location >= a.location + a.length)
209 BOOL clause1 = (a.location >= b.location && a.location - b.location >= b.length);
210 BOOL clause2 = (b.location >= a.location && b.location - a.location >= a.length);
211 return ! (clause1 || clause2);
212 }
213
215 static inline BOOL HFFPIntersectsRange(HFFPRange a, HFFPRange b) {
216 // Ranges are said to intersect if they share at least one value. Therefore, zero length ranges never intersect anything.
217 if (a.length == 0 || b.length == 0) return NO;
218
219 if (a.location <= b.location && a.location + a.length >= b.location) return YES;
220 if (b.location <= a.location && b.location + b.length >= a.location) return YES;
221 return NO;
222 }
223
225 static inline HFRange HFUnionRange(HFRange a, HFRange b) {
226 assert(HFIntersectsRange(a, b) || HFMaxRange(a) == b.location || HFMaxRange(b) == a.location);
227 HFRange result;
228 result.location = MIN(a.location, b.location);
229 assert(HFSumDoesNotOverflow(a.location, a.length));
230 assert(HFSumDoesNotOverflow(b.location, b.length));
231 result.length = MAX(a.location + a.length, b.location + b.length) - result.location;
232 return result;
233 }
234
235
237 static inline BOOL HFSumIsLargerThanSum(unsigned long long a, unsigned long long b, unsigned long long c, unsigned long long d) {
238 #if 1
239 // Theory: compare a/2 + b/2 to c/2 + d/2, and if they're equal, compare a%2 + b%2 to c%2 + d%2. We may get into trouble if a and b are both even and c and d are both odd: e.g. a = 2, b = 2, c = 1, d = 3. We would compare 1 + 1 vs 0 + 1, and therefore that 2 + 2 > 1 + 3. To address this, if both remainders are 1, we add this to the sum. We know this cannot overflow because ULLONG_MAX is odd, so (ULLONG_MAX/2) + (ULLONG_MAX/2) + 1 does not overflow.
240 unsigned int rem1 = (unsigned)(a%2 + b%2);
241 unsigned int rem2 = (unsigned)(c%2 + d%2);
242 unsigned long long sum1 = a/2 + b/2 + rem1/2;
243 unsigned long long sum2 = c/2 + d/2 + rem2/2;
244 if (sum1 > sum2) return YES;
245 else if (sum1 < sum2) return NO;
246 else {
247 // sum1 == sum2, so compare the remainders. But we have already added in the remainder / 2, so compare the remainders mod 2.
248 if (rem1%2 > rem2%2) return YES;
249 else return NO;
250 }
251 #else
252 /* Faster version, but not thoroughly tested yet. */
253 unsigned long long xor1 = a^b;
254 unsigned long long xor2 = c^d;
255 unsigned long long avg1 = (a&b)+(xor1/2);
256 unsigned long long avg2 = (c&d)+(xor2/2);
257 unsigned s1l = avg1 > avg2;
258 unsigned eq = (avg1 == avg2);
259 return s1l | ((xor1 & ~xor2) & eq);
260 #endif
261 }
262
264 static inline unsigned long long HFAbsoluteDifference(unsigned long long a, unsigned long long b) {
265 if (a > b) return a - b;
266 else return b - a;
267 }
268
270 static inline BOOL HFRangeExtendsPastRange(HFRange a, HFRange b) {
271 return HFSumIsLargerThanSum(a.location, a.length, b.location, b.length);
272 }
273
275 static inline HFRange HFIntersectionRange(HFRange range1, HFRange range2) {
276 unsigned long long minend = HFRangeExtendsPastRange(range2, range1) ? range1.location + range1.length : range2.location + range2.length;
277 if (range2.location <= range1.location && range1.location - range2.location < range2.length) {
278 return HFRangeMake(range1.location, minend - range1.location);
279 }
280 else if (range1.location <= range2.location && range2.location - range1.location < range1.length) {
281 return HFRangeMake(range2.location, minend - range2.location);
282 }
283 return HFRangeMake(0, 0);
284 }
285
287 static inline CGFloat HFCeil(CGFloat a) {
288 if (sizeof(a) == sizeof(float)) return (CGFloat)ceilf((float)a);
289 else return (CGFloat)ceil((double)a);
290 }
291
293 static inline CGFloat HFFloor(CGFloat a) {
294 if (sizeof(a) == sizeof(float)) return (CGFloat)floorf((float)a);
295 else return (CGFloat)floor((double)a);
296 }
297
299 static inline CGFloat HFRound(CGFloat a) {
300 if (sizeof(a) == sizeof(float)) return (CGFloat)roundf((float)a);
301 else return (CGFloat)round((double)a);
302 }
303
305 static inline CGFloat HFMin(CGFloat a, CGFloat b) {
306 if (sizeof(a) == sizeof(float)) return (CGFloat)fminf((float)a, (float)b);
307 else return (CGFloat)fmin((double)a, (double)b);
308 }
309
311 static inline CGFloat HFMax(CGFloat a, CGFloat b) {
312 if (sizeof(a) == sizeof(float)) return (CGFloat)fmaxf((float)a, (float)b);
313 else return (CGFloat)fmax((double)a, (double)b);
314 }
315
317 static inline BOOL HFFPRangeEqualsRange(HFFPRange a, HFFPRange b) {
318 return a.location == b.location && a.length == b.length;
319 }
320
322 static inline CGFloat HFCopysign(CGFloat a, CGFloat b) {
323 #if CGFLOAT_IS_DOUBLE
324 return copysign(a, b);
325 #else
326 return copysignf(a, b);
327 #endif
328 }
329
331 static inline NSUInteger HFAtomicIncrement(volatile NSUInteger *ptr, BOOL barrier) {
332 return _Generic(ptr,
333 volatile unsigned *: (barrier ? OSAtomicIncrement32Barrier : OSAtomicIncrement32)((volatile int32_t *)ptr),
334 #if ULONG_MAX == UINT32_MAX
335 volatile unsigned long *: (barrier ? OSAtomicIncrement32Barrier : OSAtomicIncrement32)((volatile int32_t *)ptr),
336 #else
337 volatile unsigned long *: (barrier ? OSAtomicIncrement64Barrier : OSAtomicIncrement64)((volatile int64_t *)ptr),
338 #endif
339 volatile unsigned long long *: (barrier ? OSAtomicIncrement64Barrier : OSAtomicIncrement64)((volatile int64_t *)ptr));
340 }
341
343 static inline NSUInteger HFAtomicDecrement(volatile NSUInteger *ptr, BOOL barrier) {
344 return _Generic(ptr,
345 volatile unsigned *: (barrier ? OSAtomicDecrement32Barrier : OSAtomicDecrement32)((volatile int32_t *)ptr),
346 #if ULONG_MAX == UINT32_MAX
347 volatile unsigned long *: (barrier ? OSAtomicDecrement32Barrier : OSAtomicDecrement32)((volatile int32_t *)ptr),
348 #else
349 volatile unsigned long *: (barrier ? OSAtomicDecrement64Barrier : OSAtomicDecrement64)((volatile int64_t *)ptr),
350 #endif
351 volatile unsigned long long *: (barrier ? OSAtomicDecrement64Barrier : OSAtomicDecrement64)((volatile int64_t *)ptr));
352 }
353
355 static inline unsigned long long HFFPToUL(long double val) {
356 assert(val >= 0);
357 assert(val <= ULLONG_MAX);
358 unsigned long long result = (unsigned long long)val;
359 assert((long double)result == val);
360 return result;
361 }
362
364 static inline long double HFULToFP(unsigned long long val) {
365 long double result = (long double)val;
366 assert(HFFPToUL(result) == val);
367 return result;
368 }
369
371 static inline NSString *HFDescribeAffineTransform(CGAffineTransform t) {
372 return [NSString stringWithFormat:@"%f %f 0\n%f %f 0\n%f %f 1", t.a, t.b, t.c, t.d, t.tx, t.ty];
373 }
374
376 static inline NSUInteger HFCountDigitsBase10(unsigned long long val) {
377 const unsigned long long kValues[] = {0ULL, 9ULL, 99ULL, 999ULL, 9999ULL, 99999ULL, 999999ULL, 9999999ULL, 99999999ULL, 999999999ULL, 9999999999ULL, 99999999999ULL, 999999999999ULL, 9999999999999ULL, 99999999999999ULL, 999999999999999ULL, 9999999999999999ULL, 99999999999999999ULL, 999999999999999999ULL, 9999999999999999999ULL};
378 NSUInteger low = 0, high = sizeof kValues / sizeof *kValues;
379 while (high > low) {
380 NSUInteger mid = (low + high)/2; //low + high cannot overflow
381 if (val > kValues[mid]) {
382 low = mid + 1;
383 }
384 else {
385 high = mid;
386 }
387 }
388 return MAX(1u, low);
389 }
390
392 static inline NSUInteger HFCountDigitsBase16(unsigned long long val) {
393 /* __builtin_clzll doesn't like being passed 0 */
394 if (val == 0) return 1;
395
396 /* Compute the log base 2 */
397 NSUInteger leadingZeros = (NSUInteger)__builtin_clzll(val);
398 NSUInteger logBase2 = (CHAR_BIT * sizeof val) - leadingZeros - 1;
399 return 1 + logBase2/4;
400 }
401
403 BOOL HFStringEncodingIsSupersetOfASCII(NSStringEncoding encoding);
404
406 uint8_t HFStringEncodingCharacterLength(NSStringEncoding encoding);
407
409 static inline NSUInteger ll2l(unsigned long long val) { assert(val <= ULONG_MAX); return (unsigned long)val; }
410
412 static inline uintptr_t ll2p(unsigned long long val) { assert(val <= UINTPTR_MAX); return (uintptr_t)val; }
413
415 static inline CGFloat ld2f(long double val) {
416 #if ! NDEBUG
417 if (isfinite(val)) {
418 assert(val <= CGFLOAT_MAX);
419 assert(val >= -CGFLOAT_MAX);
420 if ((val > 0 && val < CGFLOAT_MIN) || (val < 0 && val > -CGFLOAT_MIN)) {
421 NSLog(@"Warning - conversion of long double %Lf to CGFloat will result in the non-normal CGFloat %f", val, (CGFloat)val);
422 }
423 }
424 #endif
425 return (CGFloat)val;
426 }
427
429 static inline unsigned long long HFDivideULLRoundingUp(unsigned long long a, unsigned long long b) {
430 if (a == 0) return 0;
431 else return ((a - 1) / b) + 1;
432 }
433
435 static inline NSUInteger HFDivideULRoundingUp(NSUInteger a, NSUInteger b) {
436 if (a == 0) return 0;
437 else return ((a - 1) / b) + 1;
438 }
439
441 void HFDrawShadow(CGContextRef context, NSRect rect, CGFloat size, NSRectEdge rectEdge, BOOL active, NSRect clip);
442
444 void HFRegisterViewForWindowAppearanceChanges(NSView *view, SEL notificationSEL, BOOL appToo);
445
447 void HFUnregisterViewForWindowAppearanceChanges(NSView *view, BOOL appToo);
448
450 NSString *HFDescribeByteCount(unsigned long long count);
451
455 @interface HFRangeWrapper : NSObject <NSCopying> {
456 @public
457 HFRange range;
458 }
459
461 - (HFRange)HFRange;
462
464 + (HFRangeWrapper *)withRange:(HFRange)range;
465
467 + (NSArray *)withRanges:(const HFRange *)ranges count:(NSUInteger)count;
468
470 + (void)getRanges:(HFRange *)ranges fromArray:(NSArray *)array;
471
473 + (NSArray *)organizeAndMergeRanges:(NSArray *)inputRanges;
474
475 @end
476
485 @interface HFRangeSet : NSObject <NSCopying, NSSecureCoding, NSFastEnumeration> {
486 @private
487 CFMutableArrayRef array;
488 }
489
491 + (HFRangeSet *)withRange:(HFRange)range;
492
494 + (HFRangeSet *)withRanges:(const HFRange *)ranges count:(NSUInteger)count;
495
497 + (HFRangeSet *)withRangeWrappers:(NSArray *)ranges;
498
500 + (HFRangeSet *)withRangeSet:(HFRangeSet *)rangeSet;
501
503 + (HFRangeSet *)complementOfRangeSet:(HFRangeSet *)rangeSet inRange:(HFRange)range;
504
505 - (void)addRange:(HFRange)range;
506 - (void)removeRange:(HFRange)range;
507 - (void)clipToRange:(HFRange)range;
508 - (void)toggleRange:(HFRange)range;
510 - (void)addRangeSet:(HFRangeSet *)rangeSet;
511 - (void)removeRangeSet:(HFRangeSet *)rangeSet;
512 - (void)clipToRangeSet:(HFRangeSet *)rangeSet;
513 - (void)toggleRangeSet:(HFRangeSet *)rangeSet;
516 - (BOOL)isEqualToRangeSet:(HFRangeSet *)rangeSet;
517 - (BOOL)isEmpty;
519 - (BOOL)containsAllRange:(HFRange)range;
520 - (BOOL)overlapsAnyRange:(HFRange)range;
521 - (BOOL)containsAllRangeSet:(HFRangeSet *)rangeSet;
522 - (BOOL)overlapsAnyRangeSet:(HFRangeSet *)rangeSet;
524 - (HFRange)spanningRange;
526 - (void)assertIntegrity;
527
528 @end
529
530 #ifndef NDEBUG
531 void HFStartTiming(const char *name);
532 void HFStopTiming(void);
533 #endif
HFStringEncodingCharacterLength
uint8_t HFStringEncodingCharacterLength(NSStringEncoding encoding)
HFStringEncodingIsSupersetOfASCII
BOOL HFStringEncodingIsSupersetOfASCII(NSStringEncoding encoding)
HFRange::length
unsigned long long length
Definition: HFTypes.h:4
HFStopTiming
void HFStopTiming(void)
HFRange
HFRange is the 64 bit analog of NSRange, containing a 64 bit location and length. ...
Definition: HFTypes.h:2
HFRangeWrapper
An object wrapper for the HFRange type.
Definition: HFFunctions.h:455
HFStartTiming
void HFStartTiming(const char *name)
HFRegisterViewForWindowAppearanceChanges
void HFRegisterViewForWindowAppearanceChanges(NSView *view, SEL notificationSEL, BOOL appToo)
HFRangeSet
A set of HFRanges. HFRangeSet takes the interpetation that all zero-length ranges are identical...
Definition: HFFunctions.h:482
HFDescribeByteCount
NSString * HFDescribeByteCount(unsigned long long count)
-[HFRangeWrapper HFRange]
HFRange HFRange()
HFFPRange::location
long double location
Definition: HFTypes.h:11
HFFPRange::length
long double length
Definition: HFTypes.h:12
HFRange::location
unsigned long long location
Definition: HFTypes.h:3
HFFPRange
HFFPRange is a struct used for representing floating point ranges, similar to NSRange. It contains two long doubles.
Definition: HFTypes.h:10
HFDrawShadow
void HFDrawShadow(CGContextRef context, NSRect rect, CGFloat size, NSRectEdge rectEdge, BOOL active, NSRect clip)
HFUnregisterViewForWindowAppearanceChanges
void HFUnregisterViewForWindowAppearanceChanges(NSView *view, BOOL appToo)