From ac645046aa5bb08559a9a0236fa0ea8c45fe14ec Mon Sep 17 00:00:00 2001 From: Josh Channings Date: Sun, 20 Jan 2013 21:34:15 +0000 Subject: [PATCH 01/22] Convert to ARC & GCD sockets (working for bound UDP) No other scenarios are working yet; none of the TCP or connect()ed UDP is there. Remove "sent packet" NSLog Only beginReceiving on bind if continuouslyReceivingPackets Mark OSCPacket properties @dynamic We don't want the class @defs to be populated. --- CocoaOSC.xcodeproj/project.pbxproj | 44 +++++++--------- CocoaOSC/NS+OSCAdditions.m | 4 +- CocoaOSC/OSCConnection.h | 16 +++--- CocoaOSC/OSCConnection.m | 85 +++++++++++++++++++----------- CocoaOSC/OSCConnectionDelegate.h | 1 + CocoaOSC/OSCDispatcher.m | 15 ------ CocoaOSC/OSCPacket.m | 22 +------- 7 files changed, 86 insertions(+), 101 deletions(-) diff --git a/CocoaOSC.xcodeproj/project.pbxproj b/CocoaOSC.xcodeproj/project.pbxproj index 63324f9..ecaf4c8 100644 --- a/CocoaOSC.xcodeproj/project.pbxproj +++ b/CocoaOSC.xcodeproj/project.pbxproj @@ -7,6 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + 039D2DC916A9FE9000BDF0CE /* GCDAsyncSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 039D2DC516A9FE9000BDF0CE /* GCDAsyncSocket.h */; }; + 039D2DCA16A9FE9000BDF0CE /* GCDAsyncSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 039D2DC616A9FE9000BDF0CE /* GCDAsyncSocket.m */; }; + 039D2DCB16A9FE9000BDF0CE /* GCDAsyncUdpSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 039D2DC716A9FE9000BDF0CE /* GCDAsyncUdpSocket.h */; }; + 039D2DCC16A9FE9000BDF0CE /* GCDAsyncUdpSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 039D2DC816A9FE9000BDF0CE /* GCDAsyncUdpSocket.m */; }; 0B4C46861460E8B9007B5E06 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B4C46851460E8B9007B5E06 /* Cocoa.framework */; }; 0B4C46901460E8B9007B5E06 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0B4C468E1460E8B9007B5E06 /* InfoPlist.strings */; }; 0B4C46C81460EAAF007B5E06 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B4C46C71460EAAF007B5E06 /* UIKit.framework */; }; @@ -55,15 +59,7 @@ 0B5068E71460E46500BD27ED /* OSCPacket.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B5068DD1460E46500BD27ED /* OSCPacket.h */; }; 0B5068E81460E46500BD27ED /* OSCPacket.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B5068DE1460E46500BD27ED /* OSCPacket.m */; }; 0B50693E1460E4BC00BD27ED /* RegexKitLite.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B50691D1460E4BC00BD27ED /* RegexKitLite.h */; }; - 0B50693F1460E4BC00BD27ED /* RegexKitLite.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B50691F1460E4BC00BD27ED /* RegexKitLite.m */; }; - 0BFFAAD215F42EC50051E00F /* AsyncSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BFFAACE15F42EC50051E00F /* AsyncSocket.h */; }; - 0BFFAAD315F42EC50051E00F /* AsyncSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BFFAACF15F42EC50051E00F /* AsyncSocket.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; - 0BFFAAD415F42EC50051E00F /* AsyncUdpSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BFFAAD015F42EC50051E00F /* AsyncUdpSocket.h */; }; - 0BFFAAD515F42EC50051E00F /* AsyncUdpSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BFFAAD115F42EC50051E00F /* AsyncUdpSocket.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; - 0BFFAAD615F42EEE0051E00F /* AsyncSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BFFAACE15F42EC50051E00F /* AsyncSocket.h */; }; - 0BFFAAD715F42EF20051E00F /* AsyncSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BFFAACF15F42EC50051E00F /* AsyncSocket.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; - 0BFFAAD815F42EF40051E00F /* AsyncUdpSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BFFAAD015F42EC50051E00F /* AsyncUdpSocket.h */; }; - 0BFFAAD915F42EF70051E00F /* AsyncUdpSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BFFAAD115F42EC50051E00F /* AsyncUdpSocket.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; + 0B50693F1460E4BC00BD27ED /* RegexKitLite.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B50691F1460E4BC00BD27ED /* RegexKitLite.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -97,6 +93,10 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 039D2DC516A9FE9000BDF0CE /* GCDAsyncSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GCDAsyncSocket.h; path = CocoaAsyncSocket/GCD/GCDAsyncSocket.h; sourceTree = ""; }; + 039D2DC616A9FE9000BDF0CE /* GCDAsyncSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GCDAsyncSocket.m; path = CocoaAsyncSocket/GCD/GCDAsyncSocket.m; sourceTree = ""; }; + 039D2DC716A9FE9000BDF0CE /* GCDAsyncUdpSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GCDAsyncUdpSocket.h; path = CocoaAsyncSocket/GCD/GCDAsyncUdpSocket.h; sourceTree = ""; }; + 039D2DC816A9FE9000BDF0CE /* GCDAsyncUdpSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GCDAsyncUdpSocket.m; path = CocoaAsyncSocket/GCD/GCDAsyncUdpSocket.m; sourceTree = ""; }; 0B4C46841460E8B9007B5E06 /* CocoaOSC.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CocoaOSC.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 0B4C46851460E8B9007B5E06 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = Library/Frameworks/Cocoa.framework; sourceTree = DEVELOPER_DIR; }; 0B4C46881460E8B9007B5E06 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; @@ -149,10 +149,6 @@ 0B50691D1460E4BC00BD27ED /* RegexKitLite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegexKitLite.h; sourceTree = ""; }; 0B50691E1460E4BC00BD27ED /* RegexKitLite.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = RegexKitLite.html; sourceTree = ""; }; 0B50691F1460E4BC00BD27ED /* RegexKitLite.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RegexKitLite.m; sourceTree = ""; }; - 0BFFAACE15F42EC50051E00F /* AsyncSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AsyncSocket.h; path = CocoaAsyncSocket/RunLoop/AsyncSocket.h; sourceTree = ""; }; - 0BFFAACF15F42EC50051E00F /* AsyncSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AsyncSocket.m; path = CocoaAsyncSocket/RunLoop/AsyncSocket.m; sourceTree = ""; }; - 0BFFAAD015F42EC50051E00F /* AsyncUdpSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AsyncUdpSocket.h; path = CocoaAsyncSocket/RunLoop/AsyncUdpSocket.h; sourceTree = ""; }; - 0BFFAAD115F42EC50051E00F /* AsyncUdpSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AsyncUdpSocket.m; path = CocoaAsyncSocket/RunLoop/AsyncUdpSocket.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -340,10 +336,10 @@ 0BFFAACD15F42EB70051E00F /* CocoaAsyncSocket */ = { isa = PBXGroup; children = ( - 0BFFAACE15F42EC50051E00F /* AsyncSocket.h */, - 0BFFAACF15F42EC50051E00F /* AsyncSocket.m */, - 0BFFAAD015F42EC50051E00F /* AsyncUdpSocket.h */, - 0BFFAAD115F42EC50051E00F /* AsyncUdpSocket.m */, + 039D2DC516A9FE9000BDF0CE /* GCDAsyncSocket.h */, + 039D2DC616A9FE9000BDF0CE /* GCDAsyncSocket.m */, + 039D2DC716A9FE9000BDF0CE /* GCDAsyncUdpSocket.h */, + 039D2DC816A9FE9000BDF0CE /* GCDAsyncUdpSocket.m */, ); name = CocoaAsyncSocket; sourceTree = ""; @@ -361,8 +357,6 @@ 0B4C47131460F4B8007B5E06 /* OSCDispatcher.h in Headers */, 0B4C47151460F4B8007B5E06 /* OSCPacket.h in Headers */, 0B4C471B1460F565007B5E06 /* RegexKitLite.h in Headers */, - 0BFFAAD615F42EEE0051E00F /* AsyncSocket.h in Headers */, - 0BFFAAD815F42EF40051E00F /* AsyncUdpSocket.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -377,8 +371,8 @@ 0B5068E51460E46500BD27ED /* OSCDispatcher.h in Headers */, 0B5068E71460E46500BD27ED /* OSCPacket.h in Headers */, 0B50693E1460E4BC00BD27ED /* RegexKitLite.h in Headers */, - 0BFFAAD215F42EC50051E00F /* AsyncSocket.h in Headers */, - 0BFFAAD415F42EC50051E00F /* AsyncUdpSocket.h in Headers */, + 039D2DC916A9FE9000BDF0CE /* GCDAsyncSocket.h in Headers */, + 039D2DCB16A9FE9000BDF0CE /* GCDAsyncUdpSocket.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -527,8 +521,6 @@ 0B4C47141460F4B8007B5E06 /* OSCDispatcher.m in Sources */, 0B4C47161460F4B8007B5E06 /* OSCPacket.m in Sources */, 0B4C471C1460F565007B5E06 /* RegexKitLite.m in Sources */, - 0BFFAAD715F42EF20051E00F /* AsyncSocket.m in Sources */, - 0BFFAAD915F42EF70051E00F /* AsyncUdpSocket.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -562,8 +554,8 @@ 0B5068E61460E46500BD27ED /* OSCDispatcher.m in Sources */, 0B5068E81460E46500BD27ED /* OSCPacket.m in Sources */, 0B50693F1460E4BC00BD27ED /* RegexKitLite.m in Sources */, - 0BFFAAD315F42EC50051E00F /* AsyncSocket.m in Sources */, - 0BFFAAD515F42EC50051E00F /* AsyncUdpSocket.m in Sources */, + 039D2DCA16A9FE9000BDF0CE /* GCDAsyncSocket.m in Sources */, + 039D2DCC16A9FE9000BDF0CE /* GCDAsyncUdpSocket.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -764,6 +756,7 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + CLANG_ENABLE_OBJC_ARC = YES; COPY_PHASE_STRIP = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; @@ -787,6 +780,7 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + CLANG_ENABLE_OBJC_ARC = YES; COPY_PHASE_STRIP = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; diff --git a/CocoaOSC/NS+OSCAdditions.m b/CocoaOSC/NS+OSCAdditions.m index 14e5bbe..e341332 100644 --- a/CocoaOSC/NS+OSCAdditions.m +++ b/CocoaOSC/NS+OSCAdditions.m @@ -22,8 +22,6 @@ + (NSDate *)ntpReferenceDate [components setDay:1]; NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; date = [[calendar dateFromComponents:components] copy]; - [components release]; - [calendar release]; } return date; } @@ -55,7 +53,7 @@ @implementation NSString (OSCAdditions) - (NSData *)oscStringData { - NSMutableData *data = [[[self dataUsingEncoding:NSASCIIStringEncoding] mutableCopy] autorelease]; + NSMutableData *data = [[self dataUsingEncoding:NSASCIIStringEncoding] mutableCopy]; // Add terminating NULL. [data setLength:[data length]+1]; diff --git a/CocoaOSC/OSCConnection.h b/CocoaOSC/OSCConnection.h index 9c94b53..7ac7b4a 100644 --- a/CocoaOSC/OSCConnection.h +++ b/CocoaOSC/OSCConnection.h @@ -9,9 +9,9 @@ #import #import "OSCConnectionDelegate.h" +#import "GCDAsyncSocket.h" +#import "GCDAsyncUdpSocket.h" -@class AsyncSocket; -@class AsyncUdpSocket; @class OSCPacket; @class OSCDispatcher; @@ -24,14 +24,16 @@ typedef enum { -@interface OSCConnection : NSObject +@interface OSCConnection : NSObject { - id delegate; + __unsafe_unretained id delegate; OSCDispatcher *dispatcher; - AsyncSocket *tcpListenSocket; - AsyncSocket *tcpSocket; - AsyncUdpSocket *udpSocket; + GCDAsyncSocket *tcpListenSocket; + GCDAsyncSocket *tcpSocket; + GCDAsyncUdpSocket *udpSocket; + + dispatch_queue_t socketDelegateQueue; OSCConnectionProtocol protocol; diff --git a/CocoaOSC/OSCConnection.m b/CocoaOSC/OSCConnection.m index f85e12c..cbc39c7 100644 --- a/CocoaOSC/OSCConnection.m +++ b/CocoaOSC/OSCConnection.m @@ -9,8 +9,8 @@ #import "OSCConnection.h" #import "OSCPacket.h" #import "OSCDispatcher.h" -#import "AsyncSocket.h" -#import "AsyncUdpSocket.h" +#import "GCDAsyncSocket.h" +#import "GCDAsyncUdpSocket.h" #define MAX_PACKET_LENGTH 1048576 @@ -49,9 +49,6 @@ - (id)init - (void)dealloc { [self disconnect]; - [dispatcher release]; - [pendingPacketsByTag release]; - [super dealloc]; } @@ -64,16 +61,13 @@ - (void)disconnectAndNotifyDelegate:(BOOL)notify { [tcpListenSocket setDelegate:nil]; [tcpListenSocket disconnect]; - [tcpListenSocket release]; tcpListenSocket = nil; [tcpSocket setDelegate:nil]; [tcpSocket disconnect]; - [tcpSocket release]; tcpSocket = nil; [udpSocket setDelegate:nil]; - [udpSocket release]; udpSocket = nil; [pendingPacketsByTag removeAllObjects]; @@ -87,7 +81,7 @@ - (void)disconnectAndNotifyDelegate:(BOOL)notify - (BOOL)isConnected { - return [self.socket isConnected]; + return ([self.socket isConnected]); } @@ -132,7 +126,7 @@ - (BOOL)connectToHost:(NSString *)host port:(UInt16)port protocol:(OSCConnection if (protocol == OSCConnectionTCP_Int32Header || protocol == OSCConnectionTCP_RFC1055) { - tcpSocket = [[AsyncSocket alloc] initWithDelegate:self]; +// tcpSocket = [[GCDAsyncSocket alloc] initWithDelegate:self]; if (![tcpSocket connectToHost:host onPort:port error:errPtr]) { goto onError; @@ -140,7 +134,7 @@ - (BOOL)connectToHost:(NSString *)host port:(UInt16)port protocol:(OSCConnection } else { - udpSocket = [[AsyncUdpSocket alloc] initWithDelegate:self]; +// udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self]; if (![udpSocket connectToHost:host onPort:port error:errPtr]) { goto onError; @@ -169,7 +163,8 @@ - (BOOL)acceptOnInterface:(NSString *)interface port:(UInt16)port protocol:(OSCC @"Can only accept connections on TCP sockets!"); [self disconnectAndNotifyDelegate:self.connected]; protocol = proto; - tcpListenSocket = [[AsyncSocket alloc] initWithDelegate:self]; + tcpListenSocket = [[GCDAsyncSocket alloc] initWithDelegate:self + delegateQueue:socketDelegateQueue]; return [tcpListenSocket acceptOnInterface:interface port:port error:errPtr]; } @@ -178,11 +173,31 @@ - (BOOL)bindToAddress:(NSString *)localAddr port:(UInt16)port error:(NSError **) { [self disconnectAndNotifyDelegate:self.connected]; protocol = OSCConnectionUDP; - udpSocket = [[AsyncUdpSocket alloc] initWithDelegate:self]; - return [udpSocket bindToAddress:localAddr port:port error:errPtr]; + + dispatch_queue_t delegateQueue = NULL; + if([delegate respondsToSelector:@selector(queue)]) { + delegateQueue = [delegate queue]; + } + + if(delegateQueue == NULL) { + delegateQueue = dispatch_get_current_queue(); + } + + udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self + delegateQueue:delegateQueue]; + BOOL bound = [udpSocket bindToPort:port interface:localAddr error:errPtr]; + + if(!bound) { + return NO; + } + + if(continuouslyReceivePackets) { + return [udpSocket beginReceiving:errPtr]; + } + + return bound; } - - (void)sendPacket:(OSCPacket *)packet { if (!self.connected) @@ -236,7 +251,9 @@ - (void)receivePacket { if (protocol == OSCConnectionUDP) { - [udpSocket receiveWithTimeout:-1 tag:0]; + // TODO: Are we still doing receiveOnce? +// [udpSocket receiveWithTimeout:-1 tag:0]; + } else if (protocol == OSCConnectionTCP_Int32Header) { @@ -274,7 +291,6 @@ - (void)dispatchPacketData:(NSData *)data fromHost:(NSString *)host port:(UInt16 { [delegate oscConnection:self didReceivePacket:packet]; } - [packet release]; } @@ -293,13 +309,13 @@ - (void)notifyDelegateOfSentPacketWithTag:(long)tag #pragma mark TCP Delegate Methods -- (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket +- (void)onSocket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket { [self disconnectAndNotifyDelegate:NO]; - tcpSocket = [newSocket retain]; + tcpSocket = newSocket; } -- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port +- (void)onSocket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port { if ([delegate respondsToSelector:@selector(oscConnectionDidConnect:)]) { @@ -308,13 +324,13 @@ - (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UIn } -- (void)onSocketDidDisconnect:(AsyncSocket *)sock +- (void)onSocketDidDisconnect:(GCDAsyncSocket *)sock { [self disconnectAndNotifyDelegate:YES]; } -- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag +- (void)onSocket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag { if (tag == kPacketHeaderTag) { @@ -345,7 +361,7 @@ - (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)ta } -- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag +- (void)onSocket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag { if (tag != kPacketHeaderTag) { @@ -356,19 +372,26 @@ - (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag #pragma mark UDP Delegate Methods -- (BOOL)onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port +- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data + fromAddress:(NSData *)address +withFilterContext:(id)filterContext { + NSString *host; + UInt16 port; + + [GCDAsyncUdpSocket getHost:&host port:&port fromAddress:address]; + [self dispatchPacketData:data fromHost:host port:port]; - if (self.continuouslyReceivePackets) - { - [self receivePacket]; - } - return YES; +// if (self.continuouslyReceivePackets) +// { +// [self receivePacket]; +// } } -- (void)onUdpSocket:(AsyncUdpSocket *)sock didSendDataWithTag:(long)tag +- (void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)tag { - [self notifyDelegateOfSentPacketWithTag:tag]; + [self notifyDelegateOfSentPacketWithTag:tag]; } + @end diff --git a/CocoaOSC/OSCConnectionDelegate.h b/CocoaOSC/OSCConnectionDelegate.h index 11ed7dd..15bb281 100644 --- a/CocoaOSC/OSCConnectionDelegate.h +++ b/CocoaOSC/OSCConnectionDelegate.h @@ -26,4 +26,5 @@ - (void)oscConnection:(OSCConnection *)connection didReceivePacket:(OSCPacket *)packet fromHost:(NSString *)host port:(UInt16)port; - (void)oscConnection:(OSCConnection *)connection failedToReceivePacketWithError:(NSError *)error; +- (dispatch_queue_t)queue; @end \ No newline at end of file diff --git a/CocoaOSC/OSCDispatcher.m b/CocoaOSC/OSCDispatcher.m index a52d6cc..efaa7e4 100644 --- a/CocoaOSC/OSCDispatcher.m +++ b/CocoaOSC/OSCDispatcher.m @@ -18,11 +18,8 @@ @interface OSCAddressNode : NSObject { - OSCAddressNode *parent; NSString *name; NSMutableDictionary *children; - id target; - SEL action; } @property (nonatomic, assign) OSCAddressNode *parent; @@ -47,19 +44,11 @@ @implementation OSCAddressNode @synthesize parent, name, target, action; -- (void)dealloc -{ - [children release]; - [name release]; - [super dealloc]; -} - - (NSString *)description { NSMutableString *string = [NSMutableString stringWithFormat:@"%@ -> %@ (%@)", self.address, self.target, NSStringFromSelector(self.action)]; NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES]; NSArray *allChildren = [[self.children allValues] sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]]; - [sortDescriptor release]; for (OSCAddressNode *child in allChildren) { [string appendFormat:@"\n%@", child]; @@ -105,7 +94,6 @@ - (void)addMethodAtSubAddress:(NSArray *)address target:(id)t action:(SEL)a child.name = childName; child.parent = self; [self.children setObject:child forKey:childName]; - [child release]; } [child addMethodAtSubAddress:[address cdr] target:t action:a]; } @@ -185,9 +173,6 @@ - (id)init - (void)dealloc { [self cancelQueuedBundles]; - [queuedBundles release]; - [rootNode release]; - [super dealloc]; } diff --git a/CocoaOSC/OSCPacket.m b/CocoaOSC/OSCPacket.m index 1ac0652..78efde8 100644 --- a/CocoaOSC/OSCPacket.m +++ b/CocoaOSC/OSCPacket.m @@ -67,6 +67,7 @@ - (NSString *)description @implementation OSCPacket +@dynamic address, arguments, timetag, childPackets, bundle; // Following accessors overridden by concrete subclasses. @@ -106,25 +107,21 @@ - (id)initWithData:(NSData *)data { if ([data length] == 0) { - [self release]; return nil; } unsigned char firstByte[1]; [data getBytes:firstByte length:1]; if (firstByte[0] == '/') { - [self release]; self = [[OSCMutableMessage alloc] initWithData:data]; } else if (firstByte[0] == '#') { - [self release]; self = [[OSCMutableBundle alloc] initWithData:data]; } else { NSLog(@"Unrecognized first byte for OSC message: %@", data); - [self release]; return nil; } } @@ -133,7 +130,6 @@ - (id)initWithData:(NSData *)data - (id)initWithCoder:(NSCoder *)aDecoder { - [self release]; self = nil; NSData *data = [aDecoder decodeObjectForKey:@"data"]; if (data) { @@ -238,12 +234,6 @@ @implementation OSCMutableMessage @synthesize arguments; @synthesize address; -- (void)dealloc -{ - [arguments release]; - [address release]; - [super dealloc]; -} - (id)init { @@ -435,12 +425,6 @@ @implementation OSCMutableBundle @synthesize childPackets; @synthesize timetag; -- (void)dealloc -{ - [childPackets release]; - [timetag release]; - [super dealloc]; -} - (id)init { @@ -467,7 +451,6 @@ - (id)initWithData:(NSData *)data if (![bundleMarker isEqualToString:@"#bundle"]) { NSLog(@"Malformed bundle marker: %@", bundleMarker); - [self release]; return nil; } @@ -481,7 +464,6 @@ - (id)initWithData:(NSData *)data NSData *subData = [data subdataWithRange:NSMakeRange(index, size)]; OSCPacket *childPacket = [[OSCPacket alloc] initWithData:subData]; [self addChildPacket:childPacket]; - [childPacket release]; index += size; } } @@ -535,7 +517,7 @@ static id parseOSCObject(char typetag, const void *bytes, NSUInteger *ioIndex, N if (buffer[bufferSize-1] == '\0') { NSUInteger strLength = strlen(buffer); - returnValue = [[[NSString alloc] initWithBytesNoCopy:buffer length:strLength encoding:NSASCIIStringEncoding freeWhenDone:YES] autorelease]; + returnValue = [[NSString alloc] initWithBytesNoCopy:buffer length:strLength encoding:NSASCIIStringEncoding freeWhenDone:YES]; *ioIndex += strLength+1; } else From 814b70dd839115a06972405c5f90320ff159a207 Mon Sep 17 00:00:00 2001 From: Josh Channings Date: Mon, 21 Jan 2013 15:20:10 +0000 Subject: [PATCH 02/22] Add lazily-parsed OSCMessage Manually construct OSCMessage.arguments array Still need to make this work with non-float arguments Implement OSCMessage endianness swapping for int32/int64 Add timestamp to OSCMessage --- CocoaOSC/OSCPacket.h | 58 +++++++++++++ CocoaOSC/OSCPacket.m | 202 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 256 insertions(+), 4 deletions(-) diff --git a/CocoaOSC/OSCPacket.h b/CocoaOSC/OSCPacket.h index 4e0e84f..918a25e 100644 --- a/CocoaOSC/OSCPacket.h +++ b/CocoaOSC/OSCPacket.h @@ -8,6 +8,19 @@ #import +typedef enum : char { + OSCValueTypeNone = 0, + + // Standard arguments + OSCValueTypeInteger = 'i', // Int32 + OSCValueTypeFloat = 'f', // float + OSCValueTypeString = 's', // char* + OSCValueTypeBlob = 'b', + + // Nonstandard arguments + OSCValueTypeLong = 'h', // Int64 + OSCValueTypeTimetag = 't' // Int64 +} OSCValueType; /** OSCPacket is a class cluster. The initializer initWithData: returns one of the two subclasses depending on whether the data describes an OSC message or bundle. This is used for receiving and parsing OSC packets. To construct a new packet for sending, using the init initializer of one of the subclasses directly, then call encode to get the network data. @@ -58,6 +71,51 @@ @end +/** Immutable lazily-parsed message + */ +@interface OSCMessage : OSCPacket +{ + /** Preprocessed packet data + + All arguments have been converted to their native endianness after initWithData: + */ + __strong NSMutableData *packetData; +} + +/// System time at which the packet was received (set in initWithData:) +@property (readonly) CFAbsoluteTime timestamp; + +/** Initialize with raw packet buffer + + @param data raw packet buffer + + The data will be converted in-place where byte orders differ from the host CPU + */ +- (id)initWithData:(NSData *)data; + + +- (NSUInteger)countOfArguments; + + + +/** Pointer to data inside of packet buffer + + This method exposes the raw packet data + @param index Must be < countOfArguments + + @warning This method will return NULL for certain types + */ +- (void *)pointerToArgumentAtIndex:(NSUInteger)index; + +/** Length of consumable data + + This method does not take 4-byte alignment into consideration + */ +- (NSUInteger)lengthOfArgumentAtIndex:(NSUInteger)index; +- (OSCValueType)typeOfArgumentAtIndex:(NSUInteger)index; + +@end + @interface OSCMutableBundle : OSCPacket { NSDate *timetag; diff --git a/CocoaOSC/OSCPacket.m b/CocoaOSC/OSCPacket.m index 78efde8..fbe04c5 100644 --- a/CocoaOSC/OSCPacket.m +++ b/CocoaOSC/OSCPacket.m @@ -9,6 +9,8 @@ #import "OSCPacket.h" #import "NS+OSCAdditions.h" +/// Pad length to 4-byte alignment +static NSUInteger padLength(NSUInteger length); static id parseOSCObject(char typetag, const void *bytes, NSUInteger *ioIndex, NSUInteger length); @@ -113,7 +115,7 @@ - (id)initWithData:(NSData *)data [data getBytes:firstByte length:1]; if (firstByte[0] == '/') { - self = [[OSCMutableMessage alloc] initWithData:data]; + self = [[OSCMessage alloc] initWithData:data]; } else if (firstByte[0] == '#') { @@ -225,6 +227,12 @@ - (NSData *)encode return nil; } + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@ (%@) %@>", self.class, self.address, self.arguments]; +} + @end @@ -411,13 +419,195 @@ - (NSData *)encode } -- (NSString *)description + +@end + +@interface OSCMessage () + +- (const char *)_address; +- (const NSUInteger)packetSize; +- (const char *)typetag; + +@end + +@implementation OSCMessage + +@synthesize timestamp; + +- (id)initWithData:(NSData *)data +{ + self = [super init]; + if(!self) return self; + + if(data.length % 4 != 0) { + NSLog(@"Invalid OSC packet received"); + return nil; + } + + packetData = [NSMutableData dataWithData:data]; + timestamp = CFAbsoluteTimeGetCurrent(); + +#if __LITTLE_ENDIAN__ + + // Convert endianness to native for each argument + for(int i = 0 ; i < [self countOfArguments] ; i++) { + void *argumentBytes = [self pointerToArgumentAtIndex:i]; + + OSCValueType type = [self typeOfArgumentAtIndex:i]; + switch (type) { + case OSCValueTypeFloat: { + Float32 hostFloat = CFConvertFloat32SwappedToHost(*(CFSwappedFloat32 *)argumentBytes); + *((Float32 *)argumentBytes) = hostFloat; + } + break; + + case OSCValueTypeInteger: { + int32_t hostInt = CFSwapInt32BigToHost(*((int32_t *)argumentBytes)); + *((int32_t *)argumentBytes) = hostInt; + } + break; + + case OSCValueTypeLong: + case OSCValueTypeTimetag: { + int64_t hostInt = CFSwapInt64BigToHost(*(uint64_t *)argumentBytes); + *((int64_t *)argumentBytes) = hostInt; + } + break; + + default: + break; + } + } + +#endif + + return self; +} + +#pragma mark Parsing + +- (const NSUInteger)packetSize { - return [NSString stringWithFormat:@"", address, arguments]; + return *((NSUInteger *)packetData.bytes); } -@end +- (const char *)_address +{ + return packetData.bytes; +} + +/// Padded address length +- (size_t)addressLength +{ + return padLength(strnlen([self _address], packetData.length)); +} + +- (NSString *)address +{ + size_t length = strnlen([self _address], [self addressLength]); + return [[NSString alloc] initWithBytes:(void *)[self _address] length:length + encoding:NSASCIIStringEncoding]; +} + +/// Padded typetag length +- (size_t)typetagLength +{ + return padLength(strnlen([self typetag], packetData.length - [self addressLength])); +} + +- (const char *)typetag +{ + return (packetData.bytes + [self addressLength]); +} + + +#pragma mark Arguments + +/// Slow copy of all arguments wrapped in NSObjects +- (NSArray *)arguments +{ + int count = [self countOfArguments]; + NSMutableArray *args = [NSMutableArray arrayWithCapacity:count]; + + for(int i = 0 ; i < count ; i++) { + switch([self typeOfArgumentAtIndex:i]) { + case OSCValueTypeFloat: + [args addObject:[NSNumber numberWithFloat: + *((float *)[self pointerToArgumentAtIndex:i])]]; + break; + + case OSCValueTypeNone: + default: + [args addObject:[NSNull null]]; + break; + } + } + + return [NSArray arrayWithArray:args]; +} + +- (NSUInteger)countOfArguments +{ + // TODO: Use strnlen for safety + return strlen([self typetag]) - 1; +} + +- (void *)pointerToArgumentAtIndex:(NSUInteger)index +{ + if(index >= self.countOfArguments) { + return NULL; + } + + void *arg = ((void *)[self typetag] + [self typetagLength]); + int i = 0; + // Iterate until we get to the desired argument + while(i != index) { + arg += [self lengthOfArgumentAtIndex:i]; + i++; + } + + return arg; +} + +- (NSUInteger)lengthOfArgumentAtIndex:(NSUInteger)index +{ + if(index >= self.countOfArguments) { + return 0; + } + + switch([self typeOfArgumentAtIndex:index]) { + case OSCValueTypeInteger: + case OSCValueTypeFloat: + return 4; + + case OSCValueTypeLong: + return 8; + + case OSCValueTypeBlob: + return padLength(*((UInt32 *)[self pointerToArgumentAtIndex:index])) + 4; + + case OSCValueTypeString: + // TODO: Use strnlen for safety + return strlen([self pointerToArgumentAtIndex:index]); + + case OSCValueTypeNone: + default: + return 0; + } +} + +- (OSCValueType)typeOfArgumentAtIndex:(NSUInteger)index +{ + if(index >= self.countOfArguments) { + return OSCValueTypeNone; + } + + return (OSCValueType) self.typetag[index + 1]; // comma is first char +} + + +@end @implementation OSCMutableBundle @@ -501,6 +691,10 @@ - (NSString *)description @end +static NSUInteger padLength(NSUInteger length) +{ + return length - ((length % 4) ?: 4) + 4; +} static id parseOSCObject(char typetag, const void *bytes, NSUInteger *ioIndex, NSUInteger length) { From 5b07b149daf2c1ef2570c02d43a5207981790247 Mon Sep 17 00:00:00 2001 From: Josh Channings Date: Mon, 21 Jan 2013 18:01:08 +0000 Subject: [PATCH 03/22] Make OSCDispatcher optional in OSCConnection Tidy --- CocoaOSC/OSCConnection.h | 5 +++++ CocoaOSC/OSCConnection.m | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CocoaOSC/OSCConnection.h b/CocoaOSC/OSCConnection.h index 7ac7b4a..0ede936 100644 --- a/CocoaOSC/OSCConnection.h +++ b/CocoaOSC/OSCConnection.h @@ -53,6 +53,11 @@ typedef enum { @property (nonatomic, readonly) UInt16 localPort; @property (nonatomic, readonly) OSCConnectionProtocol protocol; +/** + @param dispatcher can be nil + */ +- (id)initWithDispatcher:(OSCDispatcher *)dispatcher; + // Connect and either accept or bind are mutually exclusive. Don't call one after calling the other. - (BOOL)connectToHost:(NSString *)host port:(UInt16)port protocol:(OSCConnectionProtocol)protocol error:(NSError **)errPtr; - (void)disconnect; diff --git a/CocoaOSC/OSCConnection.m b/CocoaOSC/OSCConnection.m index cbc39c7..4683805 100644 --- a/CocoaOSC/OSCConnection.m +++ b/CocoaOSC/OSCConnection.m @@ -37,10 +37,15 @@ @implementation OSCConnection @synthesize protocol, delegate, dispatcher, continuouslyReceivePackets; - (id)init +{ + return [self initWithDispatcher:[[OSCDispatcher alloc] init]]; +} + +- (id)initWithDispatcher:(OSCDispatcher *)_dispatcher { if (self = [super init]) { - dispatcher = [[OSCDispatcher alloc] init]; + dispatcher = _dispatcher; pendingPacketsByTag = [[NSMutableDictionary alloc] init]; } return self; From 9d671d739fde32bae928c64b0382a32e49c0cf66 Mon Sep 17 00:00:00 2001 From: Josh Channings Date: Tue, 22 Jan 2013 17:21:38 +0000 Subject: [PATCH 04/22] Tidy up OSCConnectionDelegate's dispatch_queue_t --- CocoaOSC/OSCConnection.h | 2 -- CocoaOSC/OSCConnection.m | 25 +++++++++++++------------ 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/CocoaOSC/OSCConnection.h b/CocoaOSC/OSCConnection.h index 0ede936..90514c6 100644 --- a/CocoaOSC/OSCConnection.h +++ b/CocoaOSC/OSCConnection.h @@ -33,8 +33,6 @@ typedef enum { GCDAsyncSocket *tcpSocket; GCDAsyncUdpSocket *udpSocket; - dispatch_queue_t socketDelegateQueue; - OSCConnectionProtocol protocol; NSMutableDictionary *pendingPacketsByTag; diff --git a/CocoaOSC/OSCConnection.m b/CocoaOSC/OSCConnection.m index 4683805..39efde6 100644 --- a/CocoaOSC/OSCConnection.m +++ b/CocoaOSC/OSCConnection.m @@ -28,6 +28,7 @@ - (void)dispatchPacketData:(NSData *)data fromHost:(NSString *)host port:(UInt16 - (void)notifyDelegateOfSentPacketWithTag:(long)tag; - (void)disconnectAndNotifyDelegate:(BOOL)notify; @property (nonatomic, readonly) id socket; // TCP or UDP socket or nil. +@property (readonly) dispatch_queue_t delegateQueue; // Queue on which to call delegate methods @end @@ -35,6 +36,7 @@ - (void)disconnectAndNotifyDelegate:(BOOL)notify; @implementation OSCConnection @synthesize protocol, delegate, dispatcher, continuouslyReceivePackets; +@dynamic delegateQueue; - (id)init { @@ -95,6 +97,14 @@ - (id)socket return (tcpSocket ? (id)tcpSocket : (id)udpSocket); } +- (dispatch_queue_t)delegateQueue +{ + if([delegate respondsToSelector:@selector(queue)]) { + return [delegate queue] ?: dispatch_get_main_queue(); + } + + return dispatch_get_main_queue(); +} - (NSString *)connectedHost { @@ -169,7 +179,7 @@ - (BOOL)acceptOnInterface:(NSString *)interface port:(UInt16)port protocol:(OSCC [self disconnectAndNotifyDelegate:self.connected]; protocol = proto; tcpListenSocket = [[GCDAsyncSocket alloc] initWithDelegate:self - delegateQueue:socketDelegateQueue]; + delegateQueue:self.delegateQueue]; return [tcpListenSocket acceptOnInterface:interface port:port error:errPtr]; } @@ -178,18 +188,9 @@ - (BOOL)bindToAddress:(NSString *)localAddr port:(UInt16)port error:(NSError **) { [self disconnectAndNotifyDelegate:self.connected]; protocol = OSCConnectionUDP; - - dispatch_queue_t delegateQueue = NULL; - if([delegate respondsToSelector:@selector(queue)]) { - delegateQueue = [delegate queue]; - } - - if(delegateQueue == NULL) { - delegateQueue = dispatch_get_current_queue(); - } - + udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self - delegateQueue:delegateQueue]; + delegateQueue:self.delegateQueue]; BOOL bound = [udpSocket bindToPort:port interface:localAddr error:errPtr]; if(!bound) { From e65e5cfd8f547bc8a399724a2705e65db5a309bc Mon Sep 17 00:00:00 2001 From: Josh Channings Date: Tue, 22 Jan 2013 17:22:12 +0000 Subject: [PATCH 05/22] Serialise accesses to OSCConnection's pendingPackets on one queue --- CocoaOSC/OSCConnection.h | 3 +++ CocoaOSC/OSCConnection.m | 41 ++++++++++++++++++++++++++++------------ 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/CocoaOSC/OSCConnection.h b/CocoaOSC/OSCConnection.h index 90514c6..275ab74 100644 --- a/CocoaOSC/OSCConnection.h +++ b/CocoaOSC/OSCConnection.h @@ -32,6 +32,9 @@ typedef enum { GCDAsyncSocket *tcpListenSocket; GCDAsyncSocket *tcpSocket; GCDAsyncUdpSocket *udpSocket; + + /// Used to serialize accesses to pendingPacketsByTag + dispatch_queue_t pendingPacketsQueue; OSCConnectionProtocol protocol; diff --git a/CocoaOSC/OSCConnection.m b/CocoaOSC/OSCConnection.m index 39efde6..85a7d4d 100644 --- a/CocoaOSC/OSCConnection.m +++ b/CocoaOSC/OSCConnection.m @@ -49,6 +49,8 @@ - (id)initWithDispatcher:(OSCDispatcher *)_dispatcher { dispatcher = _dispatcher; pendingPacketsByTag = [[NSMutableDictionary alloc] init]; + pendingPacketsQueue = dispatch_queue_create("com.github.cocoaosc.pending-packets", + DISPATCH_QUEUE_SERIAL); } return self; } @@ -76,8 +78,11 @@ - (void)disconnectAndNotifyDelegate:(BOOL)notify [udpSocket setDelegate:nil]; udpSocket = nil; - - [pendingPacketsByTag removeAllObjects]; + + dispatch_async(pendingPacketsQueue, ^{ + [pendingPacketsByTag removeAllObjects]; + }); + if (notify && [delegate respondsToSelector:@selector(oscConnectionDidDisconnect:)]) { @@ -219,7 +224,10 @@ - (void)sendPacket:(OSCPacket *)packet } lastSendTag++; - [pendingPacketsByTag setObject:packet forKey:[NSNumber numberWithLong:lastSendTag]]; + + dispatch_async(pendingPacketsQueue, ^{ + [pendingPacketsByTag setObject:packet forKey:[NSNumber numberWithLong:lastSendTag]]; + }); NSData *packetData = [packet encode]; if (protocol == OSCConnectionUDP) @@ -248,8 +256,11 @@ - (void)sendPacket:(OSCPacket *)packet toHost:(NSString *)host port:(UInt16)port ![udpSocket isConnected], @"-[OSCConnection sendPacket:toHost:port] can only be called on a UDP connection that has been binded."); lastSendTag++; - [pendingPacketsByTag setObject:packet forKey:[NSNumber numberWithLong:lastSendTag]]; - [udpSocket sendData:[packet encode] toHost:host port:port withTimeout:-1 tag:lastSendTag]; + + dispatch_async(pendingPacketsQueue, ^{ + [pendingPacketsByTag setObject:packet forKey:[NSNumber numberWithLong:lastSendTag]]; + [udpSocket sendData:[packet encode] toHost:host port:port withTimeout:-1 tag:lastSendTag]; + }); } @@ -302,13 +313,19 @@ - (void)dispatchPacketData:(NSData *)data fromHost:(NSString *)host port:(UInt16 - (void)notifyDelegateOfSentPacketWithTag:(long)tag { - NSNumber *key = [NSNumber numberWithLong:tag]; - if ([delegate respondsToSelector:@selector(oscConnection:didSendPacket:)]) - { - OSCPacket *packet = [pendingPacketsByTag objectForKey:key]; - [delegate oscConnection:self didSendPacket:packet]; - } - [pendingPacketsByTag removeObjectForKey:key]; + dispatch_async(pendingPacketsQueue, ^{ + NSNumber *key = [NSNumber numberWithLong:tag]; + if ([delegate respondsToSelector:@selector(oscConnection:didSendPacket:)]) + { + OSCPacket *packet = [pendingPacketsByTag objectForKey:key]; + + dispatch_async([self.delegate queue], ^{ + [delegate oscConnection:self didSendPacket:packet]; + }); + } + + [pendingPacketsByTag removeObjectForKey:key]; + }); } From 1c403d21cba8e50c081d2eee2ba8073f820cd131 Mon Sep 17 00:00:00 2001 From: Josh Channings Date: Tue, 22 Jan 2013 17:22:50 +0000 Subject: [PATCH 06/22] Add -[OSCMessage isEqual:] for other OSCMessages --- CocoaOSC/OSCPacket.m | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CocoaOSC/OSCPacket.m b/CocoaOSC/OSCPacket.m index fbe04c5..77c5064 100644 --- a/CocoaOSC/OSCPacket.m +++ b/CocoaOSC/OSCPacket.m @@ -606,6 +606,16 @@ - (OSCValueType)typeOfArgumentAtIndex:(NSUInteger)index return (OSCValueType) self.typetag[index + 1]; // comma is first char } +#pragma mark - + +- (BOOL)isEqual:(id)object +{ + if([object isKindOfClass:[OSCMessage class]]) { + return [packetData isEqualToData:((OSCMessage *)object)->packetData]; + } + + return [super isEqual:object]; +} @end From 7d6a940dfd38ae4af08ba9b70cd845a28076a81d Mon Sep 17 00:00:00 2001 From: Josh Channings Date: Wed, 23 Jan 2013 18:41:57 +0000 Subject: [PATCH 07/22] Fix -[OSCMessage lengthOfArgumentAtIndex:] for strings I wasn't padding the string to 4-byte alignments so all subsequent offsets were wrong. --- CocoaOSC/OSCPacket.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CocoaOSC/OSCPacket.m b/CocoaOSC/OSCPacket.m index 77c5064..b74f110 100644 --- a/CocoaOSC/OSCPacket.m +++ b/CocoaOSC/OSCPacket.m @@ -585,11 +585,12 @@ - (NSUInteger)lengthOfArgumentAtIndex:(NSUInteger)index return 8; case OSCValueTypeBlob: + // FIXME: size is not swapped for little-endian processors return padLength(*((UInt32 *)[self pointerToArgumentAtIndex:index])) + 4; case OSCValueTypeString: // TODO: Use strnlen for safety - return strlen([self pointerToArgumentAtIndex:index]); + return padLength(strlen([self pointerToArgumentAtIndex:index]) + 1); case OSCValueTypeNone: default: From a7cb4e96d5a5882d2081be1887c11e8466d81814 Mon Sep 17 00:00:00 2001 From: Josh Channings Date: Wed, 23 Jan 2013 18:42:29 +0000 Subject: [PATCH 08/22] Implement OSCMessage.arguments for more basic types --- CocoaOSC/OSCPacket.m | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/CocoaOSC/OSCPacket.m b/CocoaOSC/OSCPacket.m index b74f110..6445361 100644 --- a/CocoaOSC/OSCPacket.m +++ b/CocoaOSC/OSCPacket.m @@ -535,7 +535,36 @@ - (NSArray *)arguments [args addObject:[NSNumber numberWithFloat: *((float *)[self pointerToArgumentAtIndex:i])]]; break; + + case OSCValueTypeInteger: + [args addObject:[NSNumber numberWithInt: + *((int32_t *)[self pointerToArgumentAtIndex:i])]]; + break; + + case OSCValueTypeLong: + [args addObject:[NSNumber numberWithLong: + *((int64_t *)[self pointerToArgumentAtIndex:i])]]; + break; + + case OSCValueTypeString: { + char *str = [self pointerToArgumentAtIndex:i]; + int len = strnlen(str, [self lengthOfArgumentAtIndex:i]); + [args addObject:[[NSString alloc] initWithBytes:str length:len + encoding:NSASCIIStringEncoding]]; + } + break; + + case OSCValueTypeBlob: + [args addObject:[NSData dataWithBytesNoCopy:[self pointerToArgumentAtIndex:i] + length:[self lengthOfArgumentAtIndex:i]]]; + break; + + case OSCValueTypeTimetag: + [args addObject:[NSDate dateWithNTPTimestamp: + *((int64_t *)[self pointerToArgumentAtIndex:i])]]; + break; + case OSCValueTypeNone: default: [args addObject:[NSNull null]]; From b36a6175ce14667b2766593274686878e2e438e0 Mon Sep 17 00:00:00 2001 From: Josh Channings Date: Wed, 23 Jan 2013 18:49:09 +0000 Subject: [PATCH 09/22] =?UTF-8?q?Block=20-[OSCConnection=20disconnect?= =?UTF-8?q?=E2=80=A6]=20when=20clearing=20packet=20queue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a race condition when clearing the pending packets asynchronously, as this might be done from within a dealloc—the OSCConnection might be gone by the time the block is dispatched. --- CocoaOSC/OSCConnection.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CocoaOSC/OSCConnection.m b/CocoaOSC/OSCConnection.m index 85a7d4d..ecad6ec 100644 --- a/CocoaOSC/OSCConnection.m +++ b/CocoaOSC/OSCConnection.m @@ -79,7 +79,7 @@ - (void)disconnectAndNotifyDelegate:(BOOL)notify [udpSocket setDelegate:nil]; udpSocket = nil; - dispatch_async(pendingPacketsQueue, ^{ + dispatch_sync(pendingPacketsQueue, ^{ [pendingPacketsByTag removeAllObjects]; }); From 51cbdd00b176d90ae6a73d5d1430a2129cb332c1 Mon Sep 17 00:00:00 2001 From: Josh Channings Date: Wed, 13 Feb 2013 20:44:44 +0000 Subject: [PATCH 10/22] Fix -[OSCMessage addressLength] --- CocoaOSC/OSCPacket.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CocoaOSC/OSCPacket.m b/CocoaOSC/OSCPacket.m index 6445361..bdc2a33 100644 --- a/CocoaOSC/OSCPacket.m +++ b/CocoaOSC/OSCPacket.m @@ -499,7 +499,7 @@ - (const char *)_address /// Padded address length - (size_t)addressLength { - return padLength(strnlen([self _address], packetData.length)); + return padLength(strnlen([self _address], packetData.length) + 1); } - (NSString *)address From 85a8d9b5bb0f5eb41f89a1fa0893189141b5c5ad Mon Sep 17 00:00:00 2001 From: Josh Channings Date: Fri, 15 Feb 2013 14:32:31 +0000 Subject: [PATCH 11/22] Fix release build architecture setting This was set manually to armv6 & armv7, which causes linking to fail on new devices which use armv7s. --- CocoaOSC.xcodeproj/project.pbxproj | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/CocoaOSC.xcodeproj/project.pbxproj b/CocoaOSC.xcodeproj/project.pbxproj index ecaf4c8..262ab78 100644 --- a/CocoaOSC.xcodeproj/project.pbxproj +++ b/CocoaOSC.xcodeproj/project.pbxproj @@ -808,10 +808,7 @@ 0B5068D41460E41500BD27ED /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = ( - armv6, - armv7, - ); + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; DSTROOT = /tmp/CocoaOSC.dst; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "CocoaOSC/CocoaOSC-Prefix.pch"; From 7591ec37324f25609643c7c1c960d5b8bb03ae34 Mon Sep 17 00:00:00 2001 From: Josh Channings Date: Fri, 15 Feb 2013 16:15:10 +0000 Subject: [PATCH 12/22] Use correct GCDAsyncSocket methods in connectToHost: This should allow connectToHost: to work with all protocols. --- CocoaOSC/OSCConnection.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CocoaOSC/OSCConnection.m b/CocoaOSC/OSCConnection.m index ecad6ec..ec742e8 100644 --- a/CocoaOSC/OSCConnection.m +++ b/CocoaOSC/OSCConnection.m @@ -146,7 +146,7 @@ - (BOOL)connectToHost:(NSString *)host port:(UInt16)port protocol:(OSCConnection if (protocol == OSCConnectionTCP_Int32Header || protocol == OSCConnectionTCP_RFC1055) { -// tcpSocket = [[GCDAsyncSocket alloc] initWithDelegate:self]; + tcpSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:self.delegateQueue]; if (![tcpSocket connectToHost:host onPort:port error:errPtr]) { goto onError; @@ -154,7 +154,7 @@ - (BOOL)connectToHost:(NSString *)host port:(UInt16)port protocol:(OSCConnection } else { -// udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self]; + udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:self.delegateQueue]; if (![udpSocket connectToHost:host onPort:port error:errPtr]) { goto onError; From 51ee674d49f3cfa1105510eebc378b4c90600427 Mon Sep 17 00:00:00 2001 From: Josh Channings Date: Fri, 22 Feb 2013 20:47:56 +0000 Subject: [PATCH 13/22] Fix unguarded use of -[OSCConnectionDelegate queue] --- CocoaOSC/OSCConnection.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CocoaOSC/OSCConnection.m b/CocoaOSC/OSCConnection.m index ec742e8..4afe6b9 100644 --- a/CocoaOSC/OSCConnection.m +++ b/CocoaOSC/OSCConnection.m @@ -319,7 +319,7 @@ - (void)notifyDelegateOfSentPacketWithTag:(long)tag { OSCPacket *packet = [pendingPacketsByTag objectForKey:key]; - dispatch_async([self.delegate queue], ^{ + dispatch_async(self.delegateQueue, ^{ [delegate oscConnection:self didSendPacket:packet]; }); } From 6865665adacabdc86a8ce91cca690b5d1c374ed3 Mon Sep 17 00:00:00 2001 From: Josh Channings Date: Tue, 26 Feb 2013 12:59:37 +0000 Subject: [PATCH 14/22] Catch & log truncated bundles before NSData crashes If we get a bundle larger than GCDAsyncUdpSocket's buffer, the socket will truncate it without warning. This can lead to us trying to read past the end of the buffer based on the bundle's metadata. This patch causes the rest of the bundle to be dropped and only the complete packets returned with the OSCMutableBundle. --- CocoaOSC/OSCPacket.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CocoaOSC/OSCPacket.m b/CocoaOSC/OSCPacket.m index bdc2a33..201ce39 100644 --- a/CocoaOSC/OSCPacket.m +++ b/CocoaOSC/OSCPacket.m @@ -691,6 +691,10 @@ - (id)initWithData:(NSData *)data while (index < length) { NSUInteger size = [parseOSCObject('i', bytes, &index, length) unsignedIntegerValue]; + if(index + size > length) { + NSLog(@"Invalid packet size '%ld' in %@", (unsigned long)size, self); + return self; + } NSData *subData = [data subdataWithRange:NSMakeRange(index, size)]; OSCPacket *childPacket = [[OSCPacket alloc] initWithData:subData]; [self addChildPacket:childPacket]; From 1e4f1ffdc209c7c34ec4475e69b999211143e6da Mon Sep 17 00:00:00 2001 From: Josh Channings Date: Tue, 26 Feb 2013 13:02:44 +0000 Subject: [PATCH 15/22] Set udpSocket's maxReceiveIPv4BufferSize to UDP limit The default is set fairly low (~9200b). It might be an idea to expose this configuration so that applications can specify the expected max size of their bundles. 64k is pushing it, and IP fragmentation will already be causing problems of its own. --- CocoaOSC/OSCConnection.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CocoaOSC/OSCConnection.m b/CocoaOSC/OSCConnection.m index 4afe6b9..9cca303 100644 --- a/CocoaOSC/OSCConnection.m +++ b/CocoaOSC/OSCConnection.m @@ -155,6 +155,7 @@ - (BOOL)connectToHost:(NSString *)host port:(UInt16)port protocol:(OSCConnection else { udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:self.delegateQueue]; + udpSocket.maxReceiveIPv4BufferSize = 65535; // Max UDP datagram size if (![udpSocket connectToHost:host onPort:port error:errPtr]) { goto onError; @@ -196,6 +197,7 @@ - (BOOL)bindToAddress:(NSString *)localAddr port:(UInt16)port error:(NSError **) udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:self.delegateQueue]; + udpSocket.maxReceiveIPv4BufferSize = 65535; // Max UDP datagram size BOOL bound = [udpSocket bindToPort:port interface:localAddr error:errPtr]; if(!bound) { From b66cc84e3d42ef1818babc10483f5baa4922c445 Mon Sep 17 00:00:00 2001 From: Josh Channings Date: Thu, 28 Feb 2013 11:54:05 +0000 Subject: [PATCH 16/22] Tell OSCConnection's udpSocket to close on disconnect It's not sufficient to decrement OSCConnection's reference and expect the socket to be deallocated immediately. There could be some retain cycles keeping it alive, and therefore preventing the socket from being unbinded. If the OSCConnection has received disconnect, we can basically guarantee that we want the socket closed. --- CocoaOSC/OSCConnection.m | 1 + 1 file changed, 1 insertion(+) diff --git a/CocoaOSC/OSCConnection.m b/CocoaOSC/OSCConnection.m index 9cca303..d81866d 100644 --- a/CocoaOSC/OSCConnection.m +++ b/CocoaOSC/OSCConnection.m @@ -77,6 +77,7 @@ - (void)disconnectAndNotifyDelegate:(BOOL)notify tcpSocket = nil; [udpSocket setDelegate:nil]; + [udpSocket close]; udpSocket = nil; dispatch_sync(pendingPacketsQueue, ^{ From f8190a401f06a5e160d6f7e077a55e82fe6de25e Mon Sep 17 00:00:00 2001 From: Josh Channings Date: Thu, 28 Feb 2013 18:42:29 +0000 Subject: [PATCH 17/22] Add oscConnection:shouldReceivePacketWithData:fromHost:port: A little bit of flexibility to drop packets before they're parsed. --- CocoaOSC/OSCConnection.m | 6 ++++++ CocoaOSC/OSCConnectionDelegate.h | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/CocoaOSC/OSCConnection.m b/CocoaOSC/OSCConnection.m index d81866d..81d1db6 100644 --- a/CocoaOSC/OSCConnection.m +++ b/CocoaOSC/OSCConnection.m @@ -290,6 +290,12 @@ - (void)receivePacket - (void)dispatchPacketData:(NSData *)data fromHost:(NSString *)host port:(UInt16)port { + if([delegate respondsToSelector:@selector(oscConnection:shouldReceivePacketWithData:fromHost:port:)]) { + if([delegate oscConnection:self shouldReceivePacketWithData:data fromHost:host port:port] == NO) { + return; + } + } + OSCPacket *packet = [[OSCPacket alloc] initWithData:data]; if (!packet) { diff --git a/CocoaOSC/OSCConnectionDelegate.h b/CocoaOSC/OSCConnectionDelegate.h index 15bb281..f3f30ad 100644 --- a/CocoaOSC/OSCConnectionDelegate.h +++ b/CocoaOSC/OSCConnectionDelegate.h @@ -22,6 +22,14 @@ - (void)oscConnection:(OSCConnection *)connection willSendPacket:(OSCPacket *)packet; - (void)oscConnection:(OSCConnection *)connection didSendPacket:(OSCPacket *)packet; +/** Provide an opportunity for delegates to reject a packet before parsing + + Parsing is an expensive operation, and there may be a situation where a packet will + eventually be discarded without being used. Returning NO from this method will + cause the data to be discarded before it's parsed. + */ +- (BOOL)oscConnection:(OSCConnection *)connection shouldReceivePacketWithData:(NSData *)data fromHost:(NSString *)host port:(UInt16)port; + - (void)oscConnection:(OSCConnection *)connection didReceivePacket:(OSCPacket *)packet; - (void)oscConnection:(OSCConnection *)connection didReceivePacket:(OSCPacket *)packet fromHost:(NSString *)host port:(UInt16)port; - (void)oscConnection:(OSCConnection *)connection failedToReceivePacketWithError:(NSError *)error; From 17ada60f59dbc7875bafd8e0943dde5aacf676d3 Mon Sep 17 00:00:00 2001 From: Josh Channings Date: Thu, 14 Mar 2013 17:28:09 +0000 Subject: [PATCH 18/22] Don't assert a bound connection in sendPacket:toHost:port: Log and return instead. This is a stopgap measure since there's no way to check whether the assertion will fail before calling the method. --- CocoaOSC/OSCConnection.m | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CocoaOSC/OSCConnection.m b/CocoaOSC/OSCConnection.m index 81d1db6..cbec9d9 100644 --- a/CocoaOSC/OSCConnection.m +++ b/CocoaOSC/OSCConnection.m @@ -254,10 +254,11 @@ - (void)sendPacket:(OSCPacket *)packet - (void)sendPacket:(OSCPacket *)packet toHost:(NSString *)host port:(UInt16)port { - NSAssert(protocol == OSCConnectionUDP && - udpSocket && - ![udpSocket isConnected], - @"-[OSCConnection sendPacket:toHost:port] can only be called on a UDP connection that has been binded."); + if(!(protocol == OSCConnectionUDP && udpSocket && ![udpSocket isConnected])) { + NSLog(@"-[OSCConnection sendPacket:toHost:port] can only be called on a UDP connection that has been binded."); + return; + } + lastSendTag++; dispatch_async(pendingPacketsQueue, ^{ From c889560ad2765f287f6070412f176a9c9bf8baa5 Mon Sep 17 00:00:00 2001 From: Josh Channings Date: Wed, 1 May 2013 12:04:50 +0100 Subject: [PATCH 19/22] Swap blob argument size to native byte order --- CocoaOSC/OSCPacket.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CocoaOSC/OSCPacket.m b/CocoaOSC/OSCPacket.m index 201ce39..3bb438a 100644 --- a/CocoaOSC/OSCPacket.m +++ b/CocoaOSC/OSCPacket.m @@ -461,6 +461,7 @@ - (id)initWithData:(NSData *)data } break; + case OSCValueTypeBlob: case OSCValueTypeInteger: { int32_t hostInt = CFSwapInt32BigToHost(*((int32_t *)argumentBytes)); *((int32_t *)argumentBytes) = hostInt; @@ -614,7 +615,6 @@ - (NSUInteger)lengthOfArgumentAtIndex:(NSUInteger)index return 8; case OSCValueTypeBlob: - // FIXME: size is not swapped for little-endian processors return padLength(*((UInt32 *)[self pointerToArgumentAtIndex:index])) + 4; case OSCValueTypeString: From 1e2287e7684447659e36f9cfe83c3c3942e90a5b Mon Sep 17 00:00:00 2001 From: Josh Channings Date: Wed, 1 May 2013 12:05:26 +0100 Subject: [PATCH 20/22] Fix double free for blobs in [OSCMessage arguments] --- CocoaOSC/OSCPacket.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CocoaOSC/OSCPacket.m b/CocoaOSC/OSCPacket.m index 3bb438a..90c56c5 100644 --- a/CocoaOSC/OSCPacket.m +++ b/CocoaOSC/OSCPacket.m @@ -558,7 +558,8 @@ - (NSArray *)arguments case OSCValueTypeBlob: [args addObject:[NSData dataWithBytesNoCopy:[self pointerToArgumentAtIndex:i] - length:[self lengthOfArgumentAtIndex:i]]]; + length:[self lengthOfArgumentAtIndex:i] + freeWhenDone:NO]]; break; case OSCValueTypeTimetag: From 7dcd4ffff4a8ef5b0dba45278530d18d058cd252 Mon Sep 17 00:00:00 2001 From: Josh Channings Date: Wed, 7 Aug 2013 13:43:15 +0100 Subject: [PATCH 21/22] Pull in GCDAsyncSocket with dispatch_get_current_queue() fixes --- lib/CocoaAsyncSocket | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CocoaAsyncSocket b/lib/CocoaAsyncSocket index 9616a40..5cf7bac 160000 --- a/lib/CocoaAsyncSocket +++ b/lib/CocoaAsyncSocket @@ -1 +1 @@ -Subproject commit 9616a404dbda03c29dc85ab9acd1c698d04f2745 +Subproject commit 5cf7bac4d0bc18d257e989ea922fd2c4ca9255c4 From 398ca608176b5f346d86e1e90cd9a0d1fd4d0080 Mon Sep 17 00:00:00 2001 From: Josh Channings Date: Tue, 3 Sep 2013 11:14:06 +0100 Subject: [PATCH 22/22] Ignore build output generated by xcodebuild/xctool Running xcodebuild/xctool directly in the directory will output build intermediates directly in the repository. --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 893939c..36f8695 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ .svn *.xcuserdatad *.mode1v3 -*.pbxuser \ No newline at end of file +*.pbxuser +build