Permalink
Browse files

Partway through state machine matching code.

  • Loading branch information...
1 parent cc3ee4d commit 4515f0728a15c607470a4767bad356505cdfba0c Jim Dovey committed Jun 17, 2011
@@ -26,6 +26,9 @@
3866939513AAA0C600268560 /* AQAppStateMachine.h in Headers */ = {isa = PBXBuildFile; fileRef = 3866939313AAA0C600268560 /* AQAppStateMachine.h */; };
3866939613AAA0C600268560 /* AQAppStateMachine.m in Sources */ = {isa = PBXBuildFile; fileRef = 3866939413AAA0C600268560 /* AQAppStateMachine.m */; };
3866939713AAA0C600268560 /* AQAppStateMachine.m in Sources */ = {isa = PBXBuildFile; fileRef = 3866939413AAA0C600268560 /* AQAppStateMachine.m */; };
+ 3866939A13ABC70300268560 /* AQStateMatchingDescriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = 3866939813ABC70200268560 /* AQStateMatchingDescriptor.h */; };
+ 3866939B13ABC70300268560 /* AQStateMatchingDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 3866939913ABC70300268560 /* AQStateMatchingDescriptor.m */; };
+ 3866939C13ABC70300268560 /* AQStateMatchingDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 3866939913ABC70300268560 /* AQStateMatchingDescriptor.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 */; };
@@ -60,6 +63,8 @@
3866938D13AA82C400268560 /* AQNotifyingBitfield.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AQNotifyingBitfield.m; sourceTree = "<group>"; };
3866939313AAA0C600268560 /* AQAppStateMachine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AQAppStateMachine.h; sourceTree = "<group>"; };
3866939413AAA0C600268560 /* AQAppStateMachine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AQAppStateMachine.m; sourceTree = "<group>"; };
+ 3866939813ABC70200268560 /* AQStateMatchingDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AQStateMatchingDescriptor.h; sourceTree = "<group>"; };
+ 3866939913ABC70300268560 /* AQStateMatchingDescriptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AQStateMatchingDescriptor.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 */
@@ -130,6 +135,8 @@
3866938D13AA82C400268560 /* AQNotifyingBitfield.m */,
3866939313AAA0C600268560 /* AQAppStateMachine.h */,
3866939413AAA0C600268560 /* AQAppStateMachine.m */,
+ 3866939813ABC70200268560 /* AQStateMatchingDescriptor.h */,
+ 3866939913ABC70300268560 /* AQStateMatchingDescriptor.m */,
38431B5A13A7C26800178A7E /* Supporting Files */,
);
path = AQAppStateMachine;
@@ -174,6 +181,7 @@
3866938A13AA805500268560 /* AQRange.h in Headers */,
3866938E13AA82C400268560 /* AQNotifyingBitfield.h in Headers */,
3866939513AAA0C600268560 /* AQAppStateMachine.h in Headers */,
+ 3866939A13ABC70300268560 /* AQStateMatchingDescriptor.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -280,6 +288,7 @@
3866938B13AA805500268560 /* AQRange.m in Sources */,
3866938F13AA82C400268560 /* AQNotifyingBitfield.m in Sources */,
3866939613AAA0C600268560 /* AQAppStateMachine.m in Sources */,
+ 3866939B13ABC70300268560 /* AQStateMatchingDescriptor.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -291,6 +300,7 @@
38431B8213A7C63B00178A7E /* AQBitfield.m in Sources */,
38AC30BE13AA3D5C00AB071C /* AQBitfieldPredicates.m in Sources */,
3866939713AAA0C600268560 /* AQAppStateMachine.m in Sources */,
+ 3866939C13ABC70300268560 /* AQStateMatchingDescriptor.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -7,21 +7,26 @@
//
#import <Foundation/Foundation.h>
-#import "AQBitfield.h"
+#import "AQNotifyingBitfield.h"
/*!
This is intended to be a singleton class.
*/
@interface AQAppStateMachine : NSObject
{
- AQBitfield * _stateBits;
+ AQNotifyingBitfield * _stateBits;
+ NSMutableDictionary * _namedRanges;
+ NSMutableArray * _matchDescriptors;
+ NSMutableDictionary * _notifierLookup;
+ dispatch_queue_t _syncQ;
}
/*!
Obtain/create the singleton state machine instance.
*/
+ (AQAppStateMachine *) appStateMachine;
+// core notification API— everything else funnels through these functions
- (void) notifyForChangesToStateBitAtIndex: (NSUInteger) index usingBlock: (void (^)(void)) block;
- (void) notifyForChangesToStateBitsInRange: (NSRange) range usingBlock: (void (^)(void)) block;
- (void) notifyForChangesToStateBitsInRange: (NSRange) range maskedWithInteger: (NSUInteger) mask
@@ -30,3 +35,47 @@
usingBlock: (void (^)(void)) block;
@end
+
+/*!
+ This category defines an interface whereby API clients can interact with the state machine using
+ atomic-sized enumerated value ranges keyed with specific names, rather than knowing the internals
+ of the (potentially quite large) bitfield layout.
+ */
+@interface AQAppStateMachine (NamedStateEnumerations)
+
+// up to 32 bits of enum size
+- (void) addStateMachineValuesFromZeroTo: (NSUInteger) maxValue withName: (NSString *) name;
+
+// up to 32-64 bits of enum size
+- (void) add64BitStateMachineValuesFromZeroTo: (UInt64) maxValue withName: (NSString *) name;
+
+// generic named bit-range creator -- the others all funnel through here
+- (void) addStateMachineValuesUsingBitfieldOfLength: (NSUInteger) length withName: (NSString *) name;
+
+// add notifications for named enumerations
+- (void) notifyChangesToStateMachineValuesWithName: (NSString *) name
+ usingBlock: (void (^)(void)) block;
+- (void) notifyChangesToStateMachineValuesWithName: (NSString *) name
+ matchingMask: (NSUInteger) mask
+ usingBlock: (void (^)(void)) block;
+- (void) notifyChangesToStateMachineValuesWithName: (NSString *) name
+ matching64BitMask: (UInt64) mask
+ usingBlock: (void (^)(void)) block;
+- (void) notifyChangesToStateMachineValuesWithName: (NSString *) name
+ matchingMaskBitfield: (AQBitfield *) mask
+ usingBlock: (void (^)(void)) block;
+
+@end
+
+@interface AQAppStateMachine (MultipleEnumerationNotifications)
+
+// TODO: Figure out a nice API to assign a block to bits in multiple named state enum ranges
+// Probably it'll involve a custom descriptor object
+
+@end
+
+@interface AQAppStateMachine (InteriorThingsICantHelpMyselfFromExposing)
+
+- (NSRange) underlyingBitfieldRangeForName: (NSString *) name;
+
+@end
@@ -7,6 +7,7 @@
//
#import "AQAppStateMachine.h"
+#import "AQRange.h"
#import <dispatch/dispatch.h>
@implementation AQAppStateMachine
@@ -26,32 +27,110 @@ - (id) init
if ( self == nil )
return ( nil );
- // start out with 128 bits
- _stateBits = [[AQBitfield alloc] initWithSize: 128];
+ // start out with 32 bits
+ _stateBits = [[AQNotifyingBitfield alloc] initWithSize: 32];
+ _namedRanges = [NSMutableDictionary new];
+ _matchDescriptors = [NSMutableArray new];
+ _notifierLookup = [NSMutableDictionary new];
+ _syncQ = dispatch_queue_create("net.alanquatermain.state-machine.sync", DISPATCH_QUEUE_SERIAL);
return ( self );
}
+- (void) dealloc
+{
+ if ( _syncQ != NULL )
+ dispatch_release(_syncQ);
+}
+
- (void) notifyForChangesToStateBitAtIndex: (NSUInteger) index usingBlock: (void (^)(void)) block
{
-
+ [self notifyForChangesToStateBitsInRange: NSMakeRange(index, 1) usingBlock: block];
}
- (void) notifyForChangesToStateBitsInRange: (NSRange) range usingBlock: (void (^)(void)) block
{
+ // create match descriptor and store it
+ id desc = nil; // TODO: implement
+ [_matchDescriptors addObject: desc];
+ [_notifierLookup setObject: block forKey: [desc uniqueID]];
+ [_stateBits notifyModificationOfBitsInRange: range usingBlock: ^(NSRange range) {
+ // find and run any stored blocks
+ }];
}
-- (void) notifyForChangesToStateBitsInRange: (NSRange) range maskedWithInteger: (NSUInteger) mask
+- (void) notifyForChangesToStateBitsInRange: (NSRange) range
+ maskedWithInteger: (NSUInteger) mask
usingBlock: (void (^)(void)) block
{
}
-- (void) notifyForChangesToStateBitsInRange: (NSRange) range maskedWithBits: (AQBitfield *) mask
+- (void) notifyForChangesToStateBitsInRange: (NSRange) range
+ maskedWithBits: (AQBitfield *) mask
usingBlock: (void (^)(void)) block
{
}
@end
+
+@implementation AQAppStateMachine (NamedStateEnumerations)
+
+static inline NSUInteger HighestOneBit32(NSUInteger x)
+{
+ x |= x >> 1;
+ x |= x >> 2;
+ x |= x >> 4;
+ x |= x >> 8;
+ x |= x >> 16;
+ return ( x & ~(x >> 1) );
+}
+
+static inline NSUInteger HighestOneBit64(UInt64 x)
+{
+ x |= x >> 1;
+ x |= x >> 2;
+ x |= x >> 4;
+ x |= x >> 8;
+ x |= x >> 16;
+ return ( (NSUInteger)(x & ~(x >> 1)) );
+}
+
+- (void) addStateMachineValuesFromZeroTo: (NSUInteger) maxValue withName: (NSString *) name
+{
+ [self addStateMachineValuesUsingBitfieldOfLength: HighestOneBit32(maxValue) withName: name];
+}
+
+- (void) add64BitStateMachineValuesFromZeroTo: (UInt64) maxValue withName: (NSString *) name
+{
+ [self addStateMachineValuesUsingBitfieldOfLength: HighestOneBit64(maxValue) withName: name];
+}
+
+- (void) addStateMachineValuesUsingBitfieldOfLength: (NSUInteger) length withName: (NSString *) name
+{
+ // round up to byte-size if necessary
+ length = (length + 7) & ~7;
+
+ dispatch_sync(_syncQ, ^{
+ AQRange * range = [[AQRange alloc] initWithRange: NSMakeRange(_stateBits.count, length)];
+ [_namedRanges setObject: range forKey: name];
+ [_stateBits setCount: NSMaxRange(range.range)];
+ });
+}
+
+@end
+
+@implementation AQAppStateMachine (InteriorThingsICantHelpMyselfFromExposing)
+
+- (NSRange) underlyingBitfieldRangeForName: (NSString *) name
+{
+ AQRange * object = [_namedRanges objectForKey: name];
+ if ( object == nil )
+ return ( NSMakeRange(NSNotFound, 0) );
+
+ return ( object.range );
+}
+
+@end
@@ -11,7 +11,7 @@
@implementation AQNotifyingBitfield
{
- NSMutableArray * _order;
+ id _order; // NSMutableArray or NSMutableOrderedSet (if available)
NSMutableDictionary * _lookup;
dispatch_queue_t _syncQ;
}
@@ -22,7 +22,11 @@ - (id) initWithSize: (NSUInteger) numberOfBits
if ( self == nil )
return ( nil );
- _order = [NSMutableArray new];
+ if ( [NSMutableOrderedSet self] )
+ _order = [NSMutableOrderedSet new];
+ else
+ _order = [NSMutableArray new];
+
_lookup = [NSMutableDictionary new];
_syncQ = dispatch_queue_create("net.alanquatermain.notifyingbitfield.sync", DISPATCH_QUEUE_SERIAL);
@@ -0,0 +1,37 @@
+//
+// AQStateMatchingDescriptor.h
+// AQAppStateMachine
+//
+// Created by Jim Dovey on 11-06-17.
+// Copyright 2011 Jim Dovey. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class AQBitfield;
+
+@interface AQStateMatchingDescriptor : NSObject
+{
+ NSString * _uuid;
+ NSIndexSet * _matchingIndices;
+}
+
+// designated initializer
+- (id) initWithRanges: (NSIndexSet *) ranges matchingMasks: (NSArray *) masks;
+
+@property (nonatomic, readonly) NSString * uniqueID;
+
+- (BOOL) matchesRange: (NSRange) range;
+
+@end
+
+@interface AQStateMatchingDescriptor (CreationConvenience)
+
+// single range & mask
+- (id) initWithRange: (NSRange) range matchingMask: (AQBitfield *) mask;
+
+// masks using integral types
+- (id) initWith32BitMask: (NSUInteger) mask forRange: (NSRange) range;
+- (id) initWith64BitMask: (UInt64) mask forRange: (NSRange) range;
+
+@end
Oops, something went wrong.

0 comments on commit 4515f07

Please sign in to comment.