// // SoulSeekMessage.m // TesteStream // // Created by Marcelo Alves on 14/02/06 (only if it works ;) ). // // TODO : remove the "magic number" +4 (message size) #import "SoulSeekMessage.h" @implementation SoulSeekMessage +(SoulSeekMessage *) message { return [[[self alloc] init] autorelease]; } +(SoulSeekMessage *) messageWithCode:(uint32_t)messageCode order:(NSString*)order params:(NSArray *)params { SoulSeekMessage *m = [[SoulSeekMessage alloc] initWithData:nil andMessageType:messageCode]; int i = 0; NSEnumerator *e = [params objectEnumerator]; id param; while (param = [e nextObject]) { unichar opcode = [order characterAtIndex:i]; switch (opcode) { case 'i' : { [m appendUInt32:[param intValue]]; break; } case 'b' : { [m appendByte:[param shortValue]]; break; } case 's' : { [m appendString:param]; break; } case 'd' : { [m appendData:param]; break; } } i++; } return [m autorelease]; } -(NSData*) data { return data; } -(id) init { return [self initWithData: nil andMessageType: 0]; } -(id) initWithData:(NSData *)theData { return [self initWithData: theData andMessageType: 0]; } -(NSNumber *) type { if ([data length] < 8) return [NSNumber numberWithInt:0]; long oldPos = pos; pos = 4; NSNumber *result = [NSNumber numberWithInt:[self readUInt32]]; pos = oldPos; return result; } -(id) initWithData:(NSData *)theData andMessageType:(uint32_t) messageType { if ((self = [super init])) { data = [[NSMutableData alloc] init]; [self appendUInt32:0]; // the length :) pos = 4; // the first 4 bytes are the length if (messageType != 0) { [self appendUInt32: messageType]; pos += sizeof(uint32_t); } if (theData) [data appendData: theData]; } return self; } -(id) dealloc { [data release]; [super dealloc]; return self; } -(SoulSeekMessage *) appendUInt32: (uint32_t) value { NSAssert(data, @"Data is NULL. Did you forgot to call init?"); uint32_t flippedValue = CFSwapInt32HostToLittle(value); [data appendBytes: &flippedValue length: sizeof(uint32_t)]; return self; } -(SoulSeekMessage *) appendByte: (uint8_t) value { NSAssert(data, @"Data is NULL. Did you forgot to call init?"); [data appendBytes: &value length: sizeof(uint8_t)]; return self; } -(SoulSeekMessage *) appendString: (NSString *) value { NSAssert(data, @"Data is NULL. Did you call init first?"); const uint8_t * charArray = (const uint8_t*)[value UTF8String]; uint32_t stringLength = strlen((const char*)charArray); [self appendUInt32:stringLength]; [data appendBytes: charArray length: stringLength]; return self; } -(void *) bytes { NSAssert(data, @"Data is NULL. Did you call init first?"); // updating the first 4 bytes - Message size NSRange r = NSMakeRange(0, sizeof(uint32_t)); // remember, the message length DOES NOT count in total length uint32_t structSize = CFSwapInt32HostToLittle([data length] - sizeof(uint32_t)); [data replaceBytesInRange: r withBytes: &structSize]; return (void *)[data bytes]; } -(long) length { if (!data) return 0; return [data length]; } -(uint32_t) readUInt32 { if (pos >= [data length]) return 0; NSRange r = NSMakeRange(pos, sizeof(uint32_t)); uint32_t value = 0; [data getBytes: &value range: r]; pos += sizeof(uint32_t); return CFSwapInt32LittleToHost(value); } -(uint8_t) readByte { if (pos >= [data length]) return 0; NSRange r = NSMakeRange(pos, sizeof(uint8_t)); uint8_t value = 0; [data getBytes:&value range:r]; pos += sizeof(uint8_t); return value; } -(NSString *) readString { if (pos >= [data length]) return @""; uint32_t size = [self readUInt32]; if (size + pos > [data length]) { NSLog(@"Weird string, longer than the Message... truncating"); size = [data length] - pos; } NSRange r = NSMakeRange(pos, size); char *c = malloc(size + 1); // just for sure ;) c[size] = 0; [data getBytes:c range:r]; // NSString *result = [[[NSString alloc] initWithUTF8String:c] autorelease]; NSString *result = [[NSString alloc] initWithBytes:c length:size encoding:NSISOLatin1StringEncoding]; pos += size; free(c); return [result autorelease]; } -(void) setPos:(long) newPos { pos = newPos + 4; NSAssert (pos <= [data length], @"Trying set cursor beyond the data..."); } -(long) pos { return pos - 4; } -(SoulSeekMessage *)compress { if (data == NULL) return self; void* dataPtr = [data mutableBytes] + 8; // starting pos to compress. Don't compress the first 8 bytes, as they are the message size and message payload long size = [data length] - 8; long maximumSize = compressBound(size); unsigned long compressedSize = maximumSize; NSMutableData * newData = [[NSMutableData alloc] initWithLength:maximumSize]; void* buffer = [newData mutableBytes]; if (compress(buffer, &compressedSize, dataPtr, size) != Z_OK) { NSLog(@"Compression failed."); } else { // copying the compressed data NSRange range = NSMakeRange(8, compressedSize); [newData setLength: compressedSize]; [data setLength: compressedSize + 8]; [data replaceBytesInRange:range withBytes: buffer]; } [newData release]; return self; } -(SoulSeekMessage *)uncompress { if (data == NULL) return self; void* dataPtr = [data mutableBytes] + 8; // starting pos to compress. Don't compress the first 8 bytes, as they are the message size and message payload unsigned long size = [data length] - 8; unsigned long maximumSize = size * 10; // some people will ask me about the "10" magic number. It's the average compression ratio :) unsigned long uncompressedSize = maximumSize; NSMutableData * newData = [[NSMutableData alloc] initWithLength:maximumSize]; void* buffer = [newData mutableBytes]; if (uncompress(buffer, &uncompressedSize, dataPtr, size) != Z_OK) { NSLog(@"Decompression failed..."); } else { // copying the compressed data NSRange range = NSMakeRange(8, uncompressedSize); [newData setLength: uncompressedSize]; [data setLength: uncompressedSize + 8]; [data replaceBytesInRange:range withBytes: buffer]; } [newData release]; return self; } @end