Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Implemented notifying bitfields for the state machine to use.

  • Loading branch information...
commit 97a600794c3d810dcdfcd5fd2b43b93330fb4111 1 parent 1755b9f
Jim Dovey authored
View
19 AQAppStateMachine.xcodeproj/project.pbxproj
@@ -19,6 +19,10 @@
38431B8213A7C63B00178A7E /* AQBitfield.m in Sources */ = {isa = PBXBuildFile; fileRef = 38431B7F13A7C63B00178A7E /* AQBitfield.m */; };
38431B8413A7CCDC00178A7E /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 38431B8313A7CCDC00178A7E /* UIKit.framework */; };
38431B8513A7CCF100178A7E /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 38431B8313A7CCDC00178A7E /* UIKit.framework */; };
+ 3866938A13AA805500268560 /* AQRange.h in Headers */ = {isa = PBXBuildFile; fileRef = 3866938813AA805500268560 /* AQRange.h */; };
+ 3866938B13AA805500268560 /* AQRange.m in Sources */ = {isa = PBXBuildFile; fileRef = 3866938913AA805500268560 /* AQRange.m */; };
+ 3866938E13AA82C400268560 /* AQNotifyingBitfield.h in Headers */ = {isa = PBXBuildFile; fileRef = 3866938C13AA82C400268560 /* AQNotifyingBitfield.h */; };
+ 3866938F13AA82C400268560 /* AQNotifyingBitfield.m in Sources */ = {isa = PBXBuildFile; fileRef = 3866938D13AA82C400268560 /* AQNotifyingBitfield.m */; };
38AC30BC13AA3D5C00AB071C /* AQBitfieldPredicates.h in Headers */ = {isa = PBXBuildFile; fileRef = 38AC30BA13AA3D5C00AB071C /* AQBitfieldPredicates.h */; };
38AC30BD13AA3D5C00AB071C /* AQBitfieldPredicates.m in Sources */ = {isa = PBXBuildFile; fileRef = 38AC30BB13AA3D5C00AB071C /* AQBitfieldPredicates.m */; };
38AC30BE13AA3D5C00AB071C /* AQBitfieldPredicates.m in Sources */ = {isa = PBXBuildFile; fileRef = 38AC30BB13AA3D5C00AB071C /* AQBitfieldPredicates.m */; };
@@ -47,6 +51,10 @@
38431B7E13A7C63B00178A7E /* AQBitfield.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AQBitfield.h; sourceTree = "<group>"; };
38431B7F13A7C63B00178A7E /* AQBitfield.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AQBitfield.m; sourceTree = "<group>"; };
38431B8313A7CCDC00178A7E /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
+ 3866938813AA805500268560 /* AQRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AQRange.h; sourceTree = "<group>"; };
+ 3866938913AA805500268560 /* AQRange.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AQRange.m; sourceTree = "<group>"; };
+ 3866938C13AA82C400268560 /* AQNotifyingBitfield.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AQNotifyingBitfield.h; sourceTree = "<group>"; };
+ 3866938D13AA82C400268560 /* AQNotifyingBitfield.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AQNotifyingBitfield.m; sourceTree = "<group>"; };
38AC30BA13AA3D5C00AB071C /* AQBitfieldPredicates.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AQBitfieldPredicates.h; sourceTree = "<group>"; };
38AC30BB13AA3D5C00AB071C /* AQBitfieldPredicates.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AQBitfieldPredicates.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -111,6 +119,10 @@
38431B7F13A7C63B00178A7E /* AQBitfield.m */,
38AC30BA13AA3D5C00AB071C /* AQBitfieldPredicates.h */,
38AC30BB13AA3D5C00AB071C /* AQBitfieldPredicates.m */,
+ 3866938813AA805500268560 /* AQRange.h */,
+ 3866938913AA805500268560 /* AQRange.m */,
+ 3866938C13AA82C400268560 /* AQNotifyingBitfield.h */,
+ 3866938D13AA82C400268560 /* AQNotifyingBitfield.m */,
38431B5A13A7C26800178A7E /* Supporting Files */,
);
path = AQAppStateMachine;
@@ -152,6 +164,8 @@
files = (
38431B8013A7C63B00178A7E /* AQBitfield.h in Headers */,
38AC30BC13AA3D5C00AB071C /* AQBitfieldPredicates.h in Headers */,
+ 3866938A13AA805500268560 /* AQRange.h in Headers */,
+ 3866938E13AA82C400268560 /* AQNotifyingBitfield.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -199,6 +213,9 @@
/* Begin PBXProject section */
38431B4B13A7C26800178A7E /* Project object */ = {
isa = PBXProject;
+ attributes = {
+ ORGANIZATIONNAME = "Jim Dovey";
+ };
buildConfigurationList = 38431B4E13A7C26800178A7E /* Build configuration list for PBXProject "AQAppStateMachine" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
@@ -252,6 +269,8 @@
files = (
38431B8113A7C63B00178A7E /* AQBitfield.m in Sources */,
38AC30BD13AA3D5C00AB071C /* AQBitfieldPredicates.m in Sources */,
+ 3866938B13AA805500268560 /* AQRange.m in Sources */,
+ 3866938F13AA82C400268560 /* AQNotifyingBitfield.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
19 AQAppStateMachine/AQNotifyingBitfield.h
@@ -0,0 +1,19 @@
+//
+// AQNotifyingBitfield.h
+// AQAppStateMachine
+//
+// Created by Jim Dovey on 11-06-16.
+// Copyright 2011 Jim Dovey. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "AQBitfield.h"
+
+@interface AQNotifyingBitfield : AQBitfield
+
+- (void) notifyModificationOfBitsInRange: (NSRange) range usingBlock: (void (^)(void)) block;
+
+- (void) removeNotifierForBitsInRange: (NSRange) range; // exact range match
+- (void) removeAllNotifiersWithinRange: (NSRange) range; // any wholly-contained ranges
+
+@end
View
173 AQAppStateMachine/AQNotifyingBitfield.m
@@ -0,0 +1,173 @@
+//
+// AQNotifyingBitfield.m
+// AQAppStateMachine
+//
+// Created by Jim Dovey on 11-06-16.
+// Copyright 2011 Jim Dovey. All rights reserved.
+//
+
+#import "AQNotifyingBitfield.h"
+#import "AQRange.h"
+
+@implementation AQNotifyingBitfield
+{
+ NSMutableArray * _order;
+ NSMutableDictionary * _lookup;
+ dispatch_queue_t _syncQ;
+}
+
+- (id) initWithSize: (NSUInteger) numberOfBits
+{
+ self = [super initWithSize: numberOfBits];
+ if ( self == nil )
+ return ( nil );
+
+ _order = [NSMutableArray new];
+ _lookup = [NSMutableDictionary new];
+ _syncQ = dispatch_queue_create("net.alanquatermain.notifyingbitfield.sync", DISPATCH_QUEUE_SERIAL);
+
+ return ( self );
+}
+
+- (void) dealloc
+{
+ if ( _syncQ != NULL )
+ dispatch_release(_syncQ);
+}
+
+- (void) notifyModificationOfBitsInRange: (NSRange) range usingBlock: (void (^)(void)) block
+{
+ dispatch_async(_syncQ, ^{
+ AQRange * rangeObject = [[AQRange alloc] initWithRange: range];
+ NSUInteger existingIdx = [_order indexOfObject: rangeObject];
+ if ( existingIdx == NSNotFound )
+ {
+ [_order addObject: rangeObject];
+ [_order sortUsingSelector: @selector(compare:)];
+ }
+
+ [_lookup setObject: [block copy] forKey: rangeObject];
+ });
+}
+
+- (void) removeNotifierForBitsInRange: (NSRange) range
+{
+ dispatch_async(_syncQ, ^{
+ AQRange * obj = [[AQRange alloc] initWithRange: range];
+ [_order removeObject: obj];
+ [_lookup removeObjectForKey: obj];
+ });
+}
+
+- (void) removeAllNotifiersWithinRange: (NSRange) range
+{
+ dispatch_async(_syncQ, ^{
+ NSMutableIndexSet * indicesToRemove = [NSMutableIndexSet indexSet];
+
+ [_order enumerateObjectsUsingBlock: ^(__strong id obj, NSUInteger idx, BOOL *stop) {
+ NSRange testRange = [obj range];
+ if ( NSEqualRanges(testRange, NSIntersectionRange(range, testRange)) == NO )
+ {
+ if ( testRange.location > NSMaxRange(range) )
+ *stop = YES;
+ return; // not wholly contained in the input range
+ }
+
+ [indicesToRemove addIndex: idx];
+ [_lookup removeObjectForKey: obj];
+ }];
+
+ [_order removeObjectsAtIndexes: indicesToRemove];
+ });
+}
+
+- (void) _scheduleNotificationForRange: (AQRange *) range
+{
+ dispatch_async(dispatch_get_global_queue(0, 0), [_lookup objectForKey: range]);
+}
+
+- (void) flipBitAtIndex: (NSUInteger) index
+{
+ [super flipBitAtIndex: index];
+
+ dispatch_async(_syncQ, ^{
+ [_order enumerateObjectsUsingBlock: ^(__strong id obj, NSUInteger idx, BOOL *stop) {
+ if ( NSLocationInRange(index, [obj range]) )
+ {
+ [self _scheduleNotificationForRange: obj];
+ }
+ else if ( index < [obj range].location )
+ {
+ *stop = YES;
+ }
+ }];
+ });
+}
+
+- (void) flipBitsInRange: (NSRange) range
+{
+ [super flipBitsInRange: range];
+
+ dispatch_async(_syncQ, ^{
+ [_order enumerateObjectsUsingBlock: ^(__strong id obj, NSUInteger idx, BOOL *stop) {
+ if ( NSIntersectionRange(range, [obj range]).location != NSNotFound )
+ {
+ [self _scheduleNotificationForRange: obj];
+ }
+ else if ( NSMaxRange(range) < [obj range].location )
+ {
+ *stop = YES;
+ }
+ }];
+ });
+}
+
+- (void) setBit: (AQBit) bit atIndex: (NSUInteger) index
+{
+ [super setBit: bit atIndex: index];
+
+ dispatch_async(_syncQ, ^{
+ [_order enumerateObjectsUsingBlock: ^(__strong id obj, NSUInteger idx, BOOL *stop) {
+ if ( NSLocationInRange(index, [obj range]) )
+ {
+ [self _scheduleNotificationForRange: obj];
+ }
+ else if ( index < [obj range].location )
+ {
+ *stop = YES;
+ }
+ }];
+ });
+}
+
+- (void) setBitsInRange: (NSRange) range usingBit: (AQBit) bit
+{
+ [super setBitsInRange: range usingBit: bit];
+
+ dispatch_async(_syncQ, ^{
+ [_order enumerateObjectsUsingBlock: ^(__strong id obj, NSUInteger idx, BOOL *stop) {
+ if ( NSIntersectionRange(range, [obj range]).location != NSNotFound )
+ {
+ [self _scheduleNotificationForRange: obj];
+ }
+ else if ( NSMaxRange(range) < [obj range].location )
+ {
+ *stop = YES;
+ }
+ }];
+ });
+}
+
+- (void) setAllBits: (AQBit) bit
+{
+ [super setAllBits: bit];
+
+ // always scheduling for all bits
+ dispatch_async(_syncQ, ^{
+ [_lookup enumerateKeysAndObjectsUsingBlock: ^(__strong id key, __strong id obj, BOOL *stop) {
+ [self _scheduleNotificationForRange: obj];
+ }];
+ });
+}
+
+@end
View
26 AQAppStateMachine/AQRange.h
@@ -0,0 +1,26 @@
+//
+// AQRange.h
+// AQAppStateMachine
+//
+// Created by Jim Dovey on 11-06-16.
+// Copyright 2011 Kobo Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface AQRange : NSObject
+{
+ NSRange _range;
+}
+
+- (id) initWithRange: (NSRange) range; // designated initializer
+
+@property (nonatomic, readonly) NSRange range;
+
+- (BOOL) isEqual: (id) object;
+- (BOOL) isEqualToNSRange: (NSRange) nsRange;
+
+- (NSComparisonResult) compare: (AQRange *) other;
+- (NSComparisonResult) compareToNSRange: (NSRange) nsRange;
+
+@end
View
73 AQAppStateMachine/AQRange.m
@@ -0,0 +1,73 @@
+//
+// AQRange.m
+// AQAppStateMachine
+//
+// Created by Jim Dovey on 11-06-16.
+// Copyright 2011 Kobo Inc. All rights reserved.
+//
+
+#import "AQRange.h"
+
+@implementation AQRange
+
+@synthesize range=_range;
+
+- (id) init
+{
+ return ( [self initWithRange: NSMakeRange(0, 0)] );
+}
+
+- (id) initWithRange: (NSRange) range
+{
+ self = [super init];
+ if ( self == nil )
+ return ( nil );
+
+ _range = range;
+
+ return ( self );
+}
+
+- (NSUInteger) hash
+{
+ return ( _range.location << 16 | _range.length );
+}
+
+- (BOOL) isEqual: (id) object
+{
+ if ( [object isKindOfClass: [self class]] == NO )
+ return ( NO );
+
+ AQRange * other = (AQRange *)object;
+ return ( NSEqualRanges(_range, other->_range) );
+}
+
+- (BOOL) isEqualToNSRange: (NSRange) nsRange
+{
+ return ( NSEqualRanges(_range, nsRange) );
+}
+
+- (NSComparisonResult) compare: (AQRange *) other
+{
+ return ( [self compareToNSRange: other->_range] );
+}
+
+- (NSComparisonResult) compareToNSRange: (NSRange) nsRange
+{
+ if ( NSEqualRanges(_range, nsRange) )
+ return ( NSOrderedSame );
+
+ if ( _range.location < nsRange.location )
+ return ( NSOrderedAscending );
+ else if ( _range.location > nsRange.location )
+ return ( NSOrderedDescending );
+
+ // locations are identical, but lengths are not
+ // in this case, we order by which is *finished* first, i.e. one with the lowest length
+ if ( _range.length > nsRange.length )
+ return ( NSOrderedAscending );
+
+ return ( NSOrderedDescending );
+}
+
+@end
Please sign in to comment.
Something went wrong with that request. Please try again.