/***************************************************************************
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