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
uint8_t HFStringEncodingCharacterLength(NSStringEncoding encoding)
BOOL HFStringEncodingIsSupersetOfASCII(NSStringEncoding encoding)
unsigned long long length
Definition: HFTypes.h:4
void HFStopTiming(void)
HFRange is the 64 bit analog of NSRange, containing a 64 bit location and length. ...
Definition: HFTypes.h:2
An object wrapper for the HFRange type.
Definition: HFFunctions.h:455
void HFStartTiming(const char *name)
void HFRegisterViewForWindowAppearanceChanges(NSView *view, SEL notificationSEL, BOOL appToo)
A set of HFRanges. HFRangeSet takes the interpetation that all zero-length ranges are identical...
Definition: HFFunctions.h:482
NSString * HFDescribeByteCount(unsigned long long count)
HFRange HFRange()
long double location
Definition: HFTypes.h:11
long double length
Definition: HFTypes.h:12
unsigned long long location
Definition: HFTypes.h:3
HFFPRange is a struct used for representing floating point ranges, similar to NSRange. It contains two long doubles.
Definition: HFTypes.h:10
void HFDrawShadow(CGContextRef context, NSRect rect, CGFloat size, NSRectEdge rectEdge, BOOL active, NSRect clip)
void HFUnregisterViewForWindowAppearanceChanges(NSView *view, BOOL appToo)