Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge git://github.com/mruegenberg/objc-utils
  • Loading branch information
Yevhene Shemet committed Mar 22, 2011
2 parents ee6977f + f76ccb5 commit 2a11f52
Show file tree
Hide file tree
Showing 4 changed files with 394 additions and 0 deletions.
18 changes: 18 additions & 0 deletions CoreFoundationAdditions/NSData+CocoaDevUsersAdditions.h
@@ -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 CoreFoundationAdditions/NSData+CocoaDevUsersAdditions.m
@@ -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
30 changes: 30 additions & 0 deletions CoreFoundationAdditions/NSObject+SimpleBindings.h
@@ -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
152 changes: 152 additions & 0 deletions CoreFoundationAdditions/NSObject+SimpleBindings.m
@@ -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

0 comments on commit 2a11f52

Please sign in to comment.