/*************************************************************************** NetBase.m ------------------- begin : Fri Nov 2 01:19:16 UTC 2001 copyright : (C) 2005 by Andrew Ruder email : aeruder@ksu.edu ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * * published by the Free Software Foundation; either version 2.1 of the * * License or (at your option) any later version. * * * ***************************************************************************/ /** * NetBase reference * * * * * Revision 1 * November 8, 2003 * Andrew Ruder */ #import "NetBase.h" #import #import #import #import #import #import #import #import #include #include NSString *NetException = @"NetException"; NSString *FatalNetException = @"FatalNetException"; NetApplication *netApplication; #ifndef GNUSTEP #include static NSMapTable *desc_to_info = 0; typedef struct { CFSocketRef socket; CFRunLoopSourceRef source; int modes; NSMapTable *watchers; } net_socket_info; static void handle_cf_events(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info); static void remove_info_for_socket(int desc) { net_socket_info *x; x = NSMapGet(desc_to_info, (void *)desc); if (!x) return; CFRunLoopRemoveSource(CFRunLoopGetCurrent(), x->source, kCFRunLoopDefaultMode); CFRelease(x->source); CFRelease(x->socket); NSFreeMapTable(x->watchers); free(x); NSMapRemove(desc_to_info, (void *)desc); return; } static BOOL is_info_for_socket(int desc) { return NSMapGet(desc_to_info, (void *)desc) != 0; } static net_socket_info *info_for_socket(int desc) { CFSocketRef sock; CFRunLoopSourceRef source; net_socket_info *x; x = NSMapGet(desc_to_info, (void *)desc); if (x) return x; sock = CFSocketCreateWithNative( NULL, desc, kCFSocketReadCallBack | kCFSocketWriteCallBack, handle_cf_events, NULL ); CFSocketDisableCallBacks( sock, kCFSocketWriteCallBack | kCFSocketReadCallBack); if (!sock) return NULL; source = CFSocketCreateRunLoopSource( NULL, sock, 1); if (!source) { CFRelease(sock); return NULL; } x = malloc (sizeof(net_socket_info)); x->socket = sock; x->source = source; x->modes = 0; x->watchers = NSCreateMapTable(NSIntMapKeyCallBacks, NSObjectMapValueCallBacks, 100); NSMapInsert(desc_to_info, (void *)desc, (void *)x); CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); return x; } static void handle_cf_events(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info) { int desc; net_socket_info *x; desc = (int)CFSocketGetNative(s); if (!is_info_for_socket(desc)) { return; } x = info_for_socket(desc); if (!x) return; if (callbackType & kCFSocketWriteCallBack) { [(id)NSMapGet(x->watchers, (void *)(1 << ET_WDESC)) receivedEvent: (void *)desc type: ET_WDESC extra: 0 forMode: nil]; } if (callbackType & kCFSocketReadCallBack) { [(id)NSMapGet(x->watchers, (void *)(1 << ET_RDESC)) receivedEvent: (void *)desc type: ET_RDESC extra: 0 forMode: nil]; } } @interface NSRunLoop (RunLoopEventsAdditions) - (void) addEvent: (void *)data type: (RunLoopEventType)type watcher: (id)watcher forMode: (NSString *)mode; - (void) removeEvent: (void *)data type: (RunLoopEventType)type forMode: (NSString *)mode all: (BOOL)removeAll; @end @implementation NSRunLoop (RunLoopEventsAdditions) + (void) initialize { desc_to_info = NSCreateMapTable(NSIntMapKeyCallBacks, NSIntMapValueCallBacks, 100); } - (void) addEvent: (void *)data type: (RunLoopEventType)type watcher: (id)watcher forMode: (NSString *)mode { int desc = (int)data; int add_mode = (int)type; net_socket_info *x; x = info_for_socket(desc); if (x->modes & (1 << add_mode)) return; switch(add_mode) { case ET_RDESC: CFSocketEnableCallBacks( x->socket, kCFSocketReadCallBack ); x->modes |= (1 << add_mode); NSMapInsert(x->watchers, (void *)(1 << add_mode), (void *)watcher); break; case ET_WDESC: CFSocketEnableCallBacks( x->socket, kCFSocketWriteCallBack ); x->modes |= (1 << add_mode); NSMapInsert(x->watchers, (void *)(1 << add_mode), (void *)watcher); break; default: break; } } - (void) removeEvent: (void *)data type: (RunLoopEventType)type forMode: (NSString *)mode all: (BOOL)removeAll { int desc = (int)data; int remove_mode = (int)type; net_socket_info *x; if (!is_info_for_socket(desc)) { return; } x = info_for_socket(desc); switch(remove_mode) { case ET_RDESC: CFSocketDisableCallBacks( x->socket, kCFSocketReadCallBack ); x->modes &= ~(1 << remove_mode); NSMapRemove(x->watchers, (void *)(1 << remove_mode)); break; case ET_WDESC: CFSocketDisableCallBacks( x->socket, kCFSocketWriteCallBack ); x->modes &= ~(1 << remove_mode); NSMapRemove(x->watchers, (void *)(1 << remove_mode)); break; default: break; } if (x->modes == 0) { remove_info_for_socket(desc); } } @end #endif @implementation NetApplication + (int)netclassesMinorVersion { int x; sscanf(PACKAGE_VERSION, "%*d.%d", &x); return x; } + (int)netclassesMajorVersion { int x; sscanf(PACKAGE_VERSION, "%d.%*d", &x); return x; } + (NSString *)netclassesVersion { return [NSString stringWithCString: PACKAGE_VERSION]; } + sharedInstance { return (netApplication) ? (netApplication) : [[NetApplication alloc] init]; } - init { if (!(self = [super init])) return nil; if (netApplication) { [super dealloc]; return nil; } netApplication = RETAIN(self); descTable = NSCreateMapTable(NSIntMapKeyCallBacks, NSNonRetainedObjectMapValueCallBacks, 100); portArray = [NSMutableArray new]; netObjectArray = [NSMutableArray new]; badDescs = [NSMutableArray new]; return self; } - (void)dealloc // How in the world... { RELEASE(portArray); RELEASE(netObjectArray); RELEASE(badDescs); NSFreeMapTable(descTable); netApplication = nil; [super dealloc]; } - (NSDate *)timedOutEvent: (void *)data type: (RunLoopEventType)type forMode: (NSString *)mode { return nil; } - (void)receivedEvent: (void *)data type: (RunLoopEventType)type extra: (void *)extra forMode: (NSString *)mode { id object; object = (id)NSMapGet(descTable, data); if (!object) { [[NSRunLoop currentRunLoop] removeEvent: data type: type forMode: NSDefaultRunLoopMode all: YES]; return; } AUTORELEASE(RETAIN(object)); NS_DURING switch(type) { default: break; case ET_RDESC: if ([object conformsToProtocol: @protocol(NetObject)]) { [object dataReceived: [[object transport] readData: 0]]; } else { [object newConnection]; } break; case ET_WDESC: [[object transport] writeData: nil]; if ([[object transport] isDoneWriting]) { [[NSRunLoop currentRunLoop] removeEvent: data type: ET_WDESC forMode: NSDefaultRunLoopMode all: YES]; } break; case ET_EDESC: [self disconnectObject: self]; break; } NS_HANDLER if (([[localException name] isEqualToString:NetException]) || ([[localException name] isEqualToString:FatalNetException])) { if (type == ET_RDESC) { id data; data = [[localException userInfo] objectForKey: @"Data"]; if (data && ([data length] > 0)) { [object dataReceived: data]; } } [self disconnectObject: object]; } else { [localException raise]; } NS_ENDHANDLER } - connectObject: anObject { void *desc = 0; if ([anObject conformsToProtocol: @protocol(NetPort)]) { desc = (void *)[anObject desc]; [portArray addObject: anObject]; } else if ([anObject conformsToProtocol: @protocol(NetObject)]) { desc = (void *)[[anObject transport] desc]; [netObjectArray addObject: anObject]; } else { [NSException raise: NetException format: @"[NetApplication addObject:] %@ does not follow " @"< NetPort > or < NetObject >", NSStringFromClass([anObject class])]; } NSMapInsert(descTable, desc, anObject); [[NSRunLoop currentRunLoop] addEvent: desc type: ET_EDESC watcher: self forMode: NSDefaultRunLoopMode]; [[NSRunLoop currentRunLoop] addEvent: desc type: ET_RDESC watcher: self forMode: NSDefaultRunLoopMode]; return self; } - disconnectObject: anObject { id whichOne = nil; void *desc = 0; if ([portArray containsObject: anObject]) { whichOne = portArray; desc = (void *)[anObject desc]; } else if ([netObjectArray containsObject: anObject]) { whichOne = netObjectArray; desc = (void *)[[anObject transport] desc]; [[NSRunLoop currentRunLoop] removeEvent: desc type: ET_WDESC forMode: NSDefaultRunLoopMode all: YES]; } else { return self; } [[NSRunLoop currentRunLoop] removeEvent: desc type: ET_RDESC forMode: NSDefaultRunLoopMode all: YES]; [[NSRunLoop currentRunLoop] removeEvent: desc type: ET_EDESC forMode: NSDefaultRunLoopMode all: YES]; NSMapRemove(descTable, desc); RETAIN(anObject); [whichOne removeObject: anObject]; AUTORELEASE(anObject); [anObject connectionLost]; return self; } - closeEverything { CREATE_AUTORELEASE_POOL(apr); while ([netObjectArray count] != 0) { [self disconnectObject: [netObjectArray objectAtIndex: 0]]; } while ([portArray count] != 0) { [self disconnectObject: [portArray objectAtIndex: 0]]; } RELEASE(apr); return self; } - transportNeedsToWrite: (id )aTransport { int desc = [aTransport desc]; if ((id)NSMapGet(descTable, (void *)desc)) { [[NSRunLoop currentRunLoop] addEvent: (void *)desc type: ET_WDESC watcher: self forMode: NSDefaultRunLoopMode]; } return self; } - (NSArray *)netObjectArray { return [NSArray arrayWithArray: netObjectArray]; } - (NSArray *)portArray { return [NSArray arrayWithArray: portArray]; } @end