// A simple (TCP) socket server and a buffered socket connection // (C)2007 Simon Urbanek // Dual-license: GPL v2 and AT&T Proprietary // // this implementation is based on regular unix sockets #import "BufferedConnection.h" @implementation BufferedConnection - (id) initWithConnection: (id ) aConnection bufferSize: (NSUInteger) bufSize { self = [super init]; if (self != nil) { connection = [aConnection retain]; nbs = bufSize; netbuf = (unsigned char*) malloc(nbs); nbp = nbl = 0; } return self; } - (void) dealloc { [connection release]; free(netbuf); [super dealloc]; } - (void) setInputBufferSize: (NSUInteger) size { if (size < nbs) return; /* FIXME: we don't allow reduction of the buffer size */ unsigned char *newbuf = realloc(netbuf, size); if (!newbuf) { // FIXME: we should throw an exception return; } netbuf = newbuf; nbs = size; } - (NSError*) lastError { return [connection lastError]; } - (int) _readNext { int n; if (nbp > 0) { /* consolidate buffer */ if (nbp < nbl) memmove(netbuf, netbuf+nbp, nbl-nbp); nbl -= nbp; nbp = 0; } n = [connection read:netbuf+nbl maxLength:nbs-nbl-1]; /* leave the last byte for termination */ #ifdef TALK_DEBUG NSLog(@"BufferedConnection._readNext: n=%d", n); #endif if (n < 1) return n; nbl += n; return n; } - (char *) _readLineCString { int i; #ifdef TALK_DEBUG NSLog(@"BufferedConnection._readLineCString: npb=%d, nbl=%d", nbp, nbl); #endif if (nbp >= nbl) { int n = [self _readNext]; if (n < 1) return 0; } while (1) { i = nbp; while (i < nbl) { if (netbuf[i] == '\n' || netbuf[i] == '\r') { unsigned char *r = netbuf+nbp; if (i+1 < nbl && ( /* take CRLF or LFCR as one */ (netbuf[i]=='\r' && netbuf[i+1]=='\n') || (netbuf[i]=='\n' && netbuf[i+1]=='\r'))) netbuf[i++]=0; netbuf[i] = 0; nbp = i+1; return (char*) r; } i++; } if (nbp == 0 && nbl >= nbs - 1) { /* used all buffer space */ netbuf[nbl] = 0; /* terminate and return the truncated string */ return (char*) netbuf; } { int n = [self _readNext]; if (n < 1) return 0; } } return 0; } - (NSString *) readLine { char *ln = [self _readLineCString]; return ln?[NSString stringWithUTF8String: ln]:nil; } - (NSInteger) read: (unsigned char *) buf maxLength: (NSUInteger) maxLength; { int length = maxLength; while (length > 0) { if (nbp < nbl) { /* if there is data in the buffer, use as much as possible */ int nxt = (length < nbl-nbp) ? length : (nbl-nbp); memcpy(buf, netbuf+nbp, nxt); length -= nxt; buf += nxt; nbp += nxt; if (!length) break; } /* we land here if more is needed than what the buffer holds */ nbl = nbp = 0; /* we do not need to use the buffer - especially good for large data */ int n = [connection read: buf maxLength: length]; #if TALK_DEBUG NSLog(@"BufferedConnection read:maxLength:%d (length=%d) called read with result %d", maxLength, length, n); #endif if (n > 0) { length -= n; buf += n; } else break; } return maxLength - length; } - (NSInteger) write: (const unsigned char*) buf maxLength: (NSUInteger) len { return [connection write:buf maxLength: len]; } - (BOOL) close { return [connection close]; } - (BOOL) hasBytesAvailable { /* bytes in the buffer? answer right away */ if (nbp < nbl) return YES; /* otherwise ask the connection */ return [connection hasBytesAvailable]; } @end