forked from mruegenberg/objc-utils
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Merge git://github.com/mruegenberg/objc-utils
- Loading branch information
Showing
4 changed files
with
394 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// From http://www.cocoadev.com/index.pl?NSDataCategory | ||
|
||
#import <Foundation/Foundation.h> | ||
|
||
|
||
@interface NSData (NSDataExtension) | ||
|
||
/* | ||
Returns range [start, null byte), or (NSNotFound, 0). | ||
*/ | ||
- (NSRange) rangeOfNullTerminatedBytesFrom:(int)start; | ||
|
||
/* | ||
Canonical Base32 encoding/decoding. | ||
*/ | ||
+ (NSData *) dataWithBase32String:(NSString *)base32; | ||
- (NSString *) base32String; | ||
@end |
194 changes: 194 additions & 0 deletions
194
CoreFoundationAdditions/NSData+CocoaDevUsersAdditions.m
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
// From http://www.cocoadev.com/index.pl?NSDataCategory | ||
|
||
#import "NSData+CocoaDevUsersAdditions.h" | ||
#include <zlib.h> | ||
|
||
|
||
@implementation NSData (NSDataExtension) | ||
|
||
// Returns range [start, null byte), or (NSNotFound, 0). | ||
- (NSRange) rangeOfNullTerminatedBytesFrom:(int)start | ||
{ | ||
const Byte *pdata = [self bytes]; | ||
int len = [self length]; | ||
if (start < len) | ||
{ | ||
const Byte *end = memchr (pdata + start, 0x00, len - start); | ||
if (end != NULL) return NSMakeRange (start, end - (pdata + start)); | ||
} | ||
return NSMakeRange (NSNotFound, 0); | ||
} | ||
|
||
+ (NSData *) dataWithBase32String:(NSString *)encoded | ||
{ | ||
/* First valid character that can be indexed in decode lookup table */ | ||
static int charDigitsBase = '2'; | ||
|
||
/* Lookup table used to decode() characters in encoded strings */ | ||
static int charDigits[] = | ||
{ 26,27,28,29,30,31,-1,-1,-1,-1,-1,-1,-1,-1 // 23456789:;<=>? | ||
,-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14 // @ABCDEFGHIJKLMNO | ||
,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1 // PQRSTUVWXYZ[\]^_ | ||
,-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14 // `abcdefghijklmno | ||
,15,16,17,18,19,20,21,22,23,24,25 // pqrstuvwxyz | ||
}; | ||
|
||
if (! [encoded canBeConvertedToEncoding:NSASCIIStringEncoding]) return nil; | ||
const char *chars = [encoded cStringUsingEncoding:NSASCIIStringEncoding]; // avoids using characterAtIndex. | ||
int charsLen = [encoded lengthOfBytesUsingEncoding:NSASCIIStringEncoding]; | ||
|
||
// Note that the code below could detect non canonical Base32 length within the loop. However canonical Base32 length can be tested before entering the loop. | ||
// A canonical Base32 length modulo 8 cannot be: | ||
// 1 (aborts discarding 5 bits at STEP n=0 which produces no byte), | ||
// 3 (aborts discarding 7 bits at STEP n=2 which produces no byte), | ||
// 6 (aborts discarding 6 bits at STEP n=1 which produces no byte). | ||
switch (charsLen & 7) { // test the length of last subblock | ||
case 1: // 5 bits in subblock: 0 useful bits but 5 discarded | ||
case 3: // 15 bits in subblock: 8 useful bits but 7 discarded | ||
case 6: // 30 bits in subblock: 24 useful bits but 6 discarded | ||
return nil; // non-canonical length | ||
} | ||
int charDigitsLen = sizeof(charDigits); | ||
int bytesLen = (charsLen * 5) >> 3; | ||
Byte bytes[bytesLen]; | ||
int bytesOffset = 0, charsOffset = 0; | ||
// Also the code below does test that other discarded bits | ||
// (1 to 4 bits at end) are effectively 0. | ||
while (charsLen > 0) | ||
{ | ||
int digit, lastDigit; | ||
// STEP n = 0: Read the 1st Char in a 8-Chars subblock | ||
// Leave 5 bits, asserting there's another encoding Char | ||
if ((digit = (int)chars[charsOffset] - charDigitsBase) < 0 || digit >= charDigitsLen || (digit = charDigits[digit]) == -1) | ||
return nil; // invalid character | ||
lastDigit = digit << 3; | ||
// STEP n = 5: Read the 2nd Char in a 8-Chars subblock | ||
// Insert 3 bits, leave 2 bits, possibly trailing if no more Char | ||
if ((digit = (int)chars[charsOffset + 1] - charDigitsBase) < 0 || digit >= charDigitsLen || (digit = charDigits[digit]) == -1) | ||
return nil; // invalid character | ||
bytes[bytesOffset] = (Byte)((digit >> 2) | lastDigit); | ||
lastDigit = (digit & 3) << 6; | ||
if (charsLen == 2) { | ||
if (lastDigit != 0) return nil; // non-canonical end | ||
break; // discard the 2 trailing null bits | ||
} | ||
// STEP n = 2: Read the 3rd Char in a 8-Chars subblock | ||
// Leave 7 bits, asserting there's another encoding Char | ||
if ((digit = (int)chars[charsOffset + 2] - charDigitsBase) < 0 || digit >= charDigitsLen || (digit = charDigits[digit]) == -1) | ||
return nil; // invalid character | ||
lastDigit |= (Byte)(digit << 1); | ||
// STEP n = 7: Read the 4th Char in a 8-chars Subblock | ||
// Insert 1 bit, leave 4 bits, possibly trailing if no more Char | ||
if ((digit = (int)chars[charsOffset + 3] - charDigitsBase) < 0 || digit >= charDigitsLen || (digit = charDigits[digit]) == -1) | ||
return nil; // invalid character | ||
bytes[bytesOffset + 1] = (Byte)((digit >> 4) | lastDigit); | ||
lastDigit = (Byte)((digit & 15) << 4); | ||
if (charsLen == 4) { | ||
if (lastDigit != 0) return nil; // non-canonical end | ||
break; // discard the 4 trailing null bits | ||
} | ||
// STEP n = 4: Read the 5th Char in a 8-Chars subblock | ||
// Insert 4 bits, leave 1 bit, possibly trailing if no more Char | ||
if ((digit = (int)chars[charsOffset + 4] - charDigitsBase) < 0 || digit >= charDigitsLen || (digit = charDigits[digit]) == -1) | ||
return nil; // invalid character | ||
bytes[bytesOffset + 2] = (Byte)((digit >> 1) | lastDigit); | ||
lastDigit = (Byte)((digit & 1) << 7); | ||
if (charsLen == 5) { | ||
if (lastDigit != 0) return nil; // non-canonical end | ||
break; // discard the 1 trailing null bit | ||
} | ||
// STEP n = 1: Read the 6th Char in a 8-Chars subblock | ||
// Leave 6 bits, asserting there's another encoding Char | ||
if ((digit = (int)chars[charsOffset + 5] - charDigitsBase) < 0 || digit >= charDigitsLen || (digit = charDigits[digit]) == -1) | ||
return nil; // invalid character | ||
lastDigit |= (Byte)(digit << 2); | ||
// STEP n = 6: Read the 7th Char in a 8-Chars subblock | ||
// Insert 2 bits, leave 3 bits, possibly trailing if no more Char | ||
if ((digit = (int)chars[charsOffset + 6] - charDigitsBase) < 0 || digit >= charDigitsLen || (digit = charDigits[digit]) == -1) | ||
return nil; // invalid character | ||
bytes[bytesOffset + 3] = (Byte)((digit >> 3) | lastDigit); | ||
lastDigit = (Byte)((digit & 7) << 5); | ||
if (charsLen == 7) { | ||
if (lastDigit != 0) return nil; // non-canonical end | ||
break; // discard the 3 trailing null bits | ||
} | ||
// STEP n = 3: Read the 8th Char in a 8-Chars subblock | ||
// Insert 5 bits, leave 0 bit, next encoding Char may not exist | ||
if ((digit = (int)chars[charsOffset + 7] - charDigitsBase) < 0 || digit >= charDigitsLen || (digit = charDigits[digit]) == -1) | ||
return nil; // invalid character | ||
bytes[bytesOffset + 4] = (Byte)(digit | lastDigit); | ||
//// This point is always reached for chars.length multiple of 8 | ||
charsOffset += 8; | ||
bytesOffset += 5; | ||
charsLen -= 8; | ||
} | ||
// On loop exit, discard the n trailing null bits | ||
return [NSData dataWithBytes:bytes length:sizeof(bytes)]; | ||
} | ||
|
||
- (NSString *) base32String | ||
{ | ||
/* Lookup table used to canonically encode() groups of data bits */ | ||
static char canonicalChars[] = | ||
{ 'A','B','C','D','E','F','G','H','I','J','K','L','M' // 00..12 | ||
,'N','O','P','Q','R','S','T','U','V','W','X','Y','Z' // 13..25 | ||
,'2','3','4','5','6','7' // 26..31 | ||
}; | ||
const Byte *bytes = [self bytes]; | ||
int bytesOffset = 0, bytesLen = [self length]; | ||
int charsOffset = 0, charsLen = ((bytesLen << 3) + 4) / 5; | ||
char chars[charsLen + 1]; | ||
while (bytesLen != 0) { | ||
int digit, lastDigit; | ||
// INVARIANTS FOR EACH STEP n in [0..5[; digit in [0..31[; | ||
// The remaining n bits are already aligned on top positions | ||
// of the 5 least bits of digit, the other bits are 0. | ||
////// STEP n = 0: insert new 5 bits, leave 3 bits | ||
digit = bytes[bytesOffset] & 255; | ||
chars[charsOffset] = canonicalChars[digit >> 3]; | ||
lastDigit = (digit & 7) << 2; | ||
if (bytesLen == 1) { // put the last 3 bits | ||
chars[charsOffset + 1] = canonicalChars[lastDigit]; | ||
break; | ||
} | ||
////// STEP n = 3: insert 2 new bits, then 5 bits, leave 1 bit | ||
digit = bytes[bytesOffset + 1] & 255; | ||
chars[charsOffset + 1] = canonicalChars[(digit >> 6) | lastDigit]; | ||
chars[charsOffset + 2] = canonicalChars[(digit >> 1) & 31]; | ||
lastDigit = (digit & 1) << 4; | ||
if (bytesLen == 2) { // put the last 1 bit | ||
chars[charsOffset + 3] = canonicalChars[lastDigit]; | ||
break; | ||
} | ||
////// STEP n = 1: insert 4 new bits, leave 4 bit | ||
digit = bytes[bytesOffset + 2] & 255; | ||
chars[charsOffset + 3] = canonicalChars[(digit >> 4) | lastDigit]; | ||
lastDigit = (digit & 15) << 1; | ||
if (bytesLen == 3) { // put the last 1 bits | ||
chars[charsOffset + 4] = canonicalChars[lastDigit]; | ||
break; | ||
} | ||
////// STEP n = 4: insert 1 new bit, then 5 bits, leave 2 bits | ||
digit = bytes[bytesOffset + 3] & 255; | ||
chars[charsOffset + 4] = canonicalChars[(digit >> 7) | lastDigit]; | ||
chars[charsOffset + 5] = canonicalChars[(digit >> 2) & 31]; | ||
lastDigit = (digit & 3) << 3; | ||
if (bytesLen == 4) { // put the last 2 bits | ||
chars[charsOffset + 6] = canonicalChars[lastDigit]; | ||
break; | ||
} | ||
////// STEP n = 2: insert 3 new bits, then 5 bits, leave 0 bit | ||
digit = bytes[bytesOffset + 4] & 255; | ||
chars[charsOffset + 6] = canonicalChars[(digit >> 5) | lastDigit]; | ||
chars[charsOffset + 7] = canonicalChars[digit & 31]; | ||
//// This point is always reached for bytes.length multiple of 5 | ||
bytesOffset += 5; | ||
charsOffset += 8; | ||
bytesLen -= 5; | ||
} | ||
chars[charsLen] = '\0'; | ||
return [NSString stringWithCString:chars encoding:NSASCIIStringEncoding]; | ||
} | ||
|
||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// | ||
// NSObject+SimpleBindings.h | ||
// Fluxus | ||
// | ||
// Created by Marcel Ruegenberg on 21.03.11. | ||
// Copyright 2011 Dustlab. All rights reserved. | ||
// | ||
|
||
#import <Foundation/Foundation.h> | ||
|
||
/** | ||
A simple bindings-like mechanism. Inspired by Cocoa bindings. | ||
No value transformers are supported. | ||
For more complex needs, use Signals. | ||
*/ | ||
@interface NSObject (SimpleBindings) | ||
|
||
/** | ||
Ensure that the own value for a key is always equal to the value for a keyPath of another object. | ||
*/ | ||
- (void)bind:(NSString *)binding toKeyPath:(NSString *)keyPath ofObject:(id)object; | ||
|
||
/** | ||
Removes the binding from all key paths of object to this object. | ||
*/ | ||
- (void)unbindObject:(NSObject *)object; | ||
|
||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
// | ||
// NSObject+SimpleBindings.m | ||
// Fluxus | ||
// | ||
// Created by Marcel Ruegenberg on 21.03.11. | ||
// Copyright 2011 Dustlab. All rights reserved. | ||
// | ||
|
||
#import "NSObject+SimpleBindings.h" | ||
#import <objc/runtime.h> | ||
|
||
|
||
@interface ObjectBinding : NSObject { | ||
} | ||
|
||
@property (assign) NSObject *obj1; | ||
@property (copy) NSString *keyPath1; | ||
@property (assign) NSObject *obj2; | ||
@property (copy) NSString *keyPath2; | ||
|
||
- (id)initWithKeyPath:(NSString *)keyPath1 ofObj:(NSObject *)obj1 keyPath:(NSString *)keyPath2 ofObj:(NSObject *)obj2; | ||
|
||
- (void)deactivateBinding; | ||
|
||
- (BOOL)containsObject:(NSObject *)object; | ||
|
||
- (NSObject *)otherObject:(NSObject *)obj; | ||
|
||
@end | ||
|
||
@implementation ObjectBinding | ||
@synthesize obj1, keyPath1, obj2, keyPath2; | ||
|
||
- (id)initWithKeyPath:(NSString *)keyPath1_ ofObj:(NSObject *)obj1_ keyPath:(NSString *)keyPath2_ ofObj:(NSObject *)obj2_ { | ||
if((self = [super init])) { | ||
self.obj1 = obj1_; self.keyPath1 = keyPath1_; | ||
self.obj2 = obj2_; self.keyPath2 = keyPath2_; | ||
[self.obj1 addObserver:self forKeyPath:self.keyPath1 options:0 context:NULL]; | ||
[self.obj2 addObserver:self forKeyPath:self.keyPath2 options:0 context:NULL]; | ||
} | ||
return self; | ||
} | ||
|
||
- (void)deactivateBinding { | ||
[self.obj1 removeObserver:self forKeyPath:self.keyPath1]; | ||
[self.obj2 removeObserver:self forKeyPath:self.keyPath2]; | ||
} | ||
|
||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { | ||
NSObject *otherObject = nil; NSString *otherKeyPath = nil; | ||
if(object == self.obj1 && [keyPath isEqualToString:self.keyPath1]) { otherObject = self.obj2; otherKeyPath = self.keyPath2; } | ||
else if(object == self.obj2 && [keyPath isEqualToString:self.keyPath2]) { otherObject = self.obj1; otherKeyPath = self.keyPath1; } | ||
else return; | ||
|
||
id val = [object valueForKeyPath:keyPath]; | ||
|
||
if([object valueForKeyPath:keyPath] != [otherObject valueForKeyPath:otherKeyPath]) { | ||
[otherObject setValue:val forKeyPath:otherKeyPath]; | ||
} | ||
} | ||
|
||
- (BOOL)containsObject:(NSObject *)object { | ||
return self.obj1 == object || self.obj2 == object; | ||
} | ||
|
||
- (NSObject *)otherObject:(NSObject *)obj { | ||
if(obj == self.obj1) return obj1; | ||
else if(obj == self.obj2) return obj2; | ||
else return nil; | ||
} | ||
|
||
- (void)dealloc { | ||
self.obj1 = nil; | ||
self.keyPath1 = nil; | ||
self.obj2 = nil; | ||
self.keyPath2 = nil; | ||
[super dealloc]; | ||
} | ||
|
||
@end | ||
|
||
|
||
@interface NSObject (SimpleBindingsPrivate) | ||
|
||
- (void)bindOtherObject:(ObjectBinding *)binding; | ||
|
||
- (void)unbindOtherObject:(ObjectBinding *)binding; | ||
|
||
@end | ||
|
||
|
||
@implementation NSObject (SimpleBindings) | ||
|
||
static char bindingsKey; | ||
|
||
- (void)bind:(NSString *)binding toKeyPath:(NSString *)keyPath ofObject:(id)object { | ||
// see "Associative references" in the docs for details | ||
NSMutableSet *bindings = objc_getAssociatedObject(self, &bindingsKey); | ||
if(bindings == nil) { | ||
bindings = [NSMutableSet new]; | ||
objc_setAssociatedObject(self, &bindingsKey, bindings, OBJC_ASSOCIATION_RETAIN); | ||
[bindings release]; | ||
} | ||
|
||
ObjectBinding *b = [[ObjectBinding alloc] initWithKeyPath:binding ofObj:self keyPath:keyPath ofObj:object]; | ||
[bindings addObject:b]; | ||
|
||
[object bindOtherObject:b]; | ||
[b release]; | ||
} | ||
|
||
- (void)unbindObject:(NSObject *)object { | ||
NSMutableSet *bindings = objc_getAssociatedObject(self, &bindingsKey); | ||
if(! bindings) return; | ||
|
||
NSMutableSet *bindingsToRemove = [NSMutableSet set]; | ||
for(ObjectBinding *b in bindings) { | ||
if([b containsObject:object]) { | ||
[b deactivateBinding]; | ||
[[b otherObject:self] unbindOtherObject:b]; | ||
[bindingsToRemove addObject:b]; | ||
} | ||
} | ||
[bindings minusSet:bindingsToRemove]; | ||
|
||
} | ||
|
||
@end | ||
|
||
|
||
@implementation NSObject (SimpleBindingsPrivate) | ||
|
||
- (void)bindOtherObject:(ObjectBinding *)binding { | ||
NSMutableSet *bindings = objc_getAssociatedObject(self, &bindingsKey); | ||
if(bindings == nil) { | ||
bindings = [NSMutableSet new]; | ||
objc_setAssociatedObject(self, &bindingsKey, bindings, OBJC_ASSOCIATION_RETAIN); | ||
[bindings release]; | ||
} | ||
|
||
[bindings addObject:binding]; | ||
} | ||
|
||
- (void)unbindOtherObject:(ObjectBinding *)binding { | ||
NSMutableSet *bindings = objc_getAssociatedObject(self, &bindingsKey); | ||
if(! bindings) return; | ||
|
||
[bindings removeObject:binding]; | ||
} | ||
|
||
@end | ||
|