Created
January 11, 2012 10:20
-
-
Save tmdvs/1594038 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "NSDataAdditions.h" | |
static char encodingTable[64] = { | |
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', | |
'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', | |
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', | |
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' }; | |
@implementation NSData (NSDataAdditions) | |
+ (NSData *) dataWithBase64EncodedString:(NSString *) string { | |
return [[[NSData allocWithZone:nil] initWithBase64EncodedString:string] autorelease]; | |
} | |
- (id) initWithBase64EncodedString:(NSString *) string { | |
NSMutableData *mutableData = nil; | |
if( string ) { | |
unsigned long ixtext = 0; | |
unsigned long lentext = 0; | |
unsigned char ch = 0; | |
unsigned char inbuf[4], outbuf[3]; | |
short i = 0, ixinbuf = 0; | |
BOOL flignore = NO; | |
BOOL flendtext = NO; | |
NSData *base64Data = nil; | |
const unsigned char *base64Bytes = nil; | |
// Convert the string to ASCII data. | |
base64Data = [string dataUsingEncoding:NSASCIIStringEncoding]; | |
base64Bytes = [base64Data bytes]; | |
mutableData = [NSMutableData dataWithCapacity:[base64Data length]]; | |
lentext = [base64Data length]; | |
while( YES ) { | |
if( ixtext >= lentext ) break; | |
ch = base64Bytes[ixtext++]; | |
flignore = NO; | |
if( ( ch >= 'A' ) && ( ch <= 'Z' ) ) ch = ch - 'A'; | |
else if( ( ch >= 'a' ) && ( ch <= 'z' ) ) ch = ch - 'a' + 26; | |
else if( ( ch >= '0' ) && ( ch <= '9' ) ) ch = ch - '0' + 52; | |
else if( ch == '+' ) ch = 62; | |
else if( ch == '=' ) flendtext = YES; | |
else if( ch == '/' ) ch = 63; | |
else flignore = YES; | |
if( ! flignore ) { | |
short ctcharsinbuf = 3; | |
BOOL flbreak = NO; | |
if( flendtext ) { | |
if( ! ixinbuf ) break; | |
if( ( ixinbuf == 1 ) || ( ixinbuf == 2 ) ) ctcharsinbuf = 1; | |
else ctcharsinbuf = 2; | |
ixinbuf = 3; | |
flbreak = YES; | |
} | |
inbuf [ixinbuf++] = ch; | |
if( ixinbuf == 4 ) { | |
ixinbuf = 0; | |
outbuf [0] = ( inbuf[0] << 2 ) | ( ( inbuf[1] & 0x30) >> 4 ); | |
outbuf [1] = ( ( inbuf[1] & 0x0F ) << 4 ) | ( ( inbuf[2] & 0x3C ) >> 2 ); | |
outbuf [2] = ( ( inbuf[2] & 0x03 ) << 6 ) | ( inbuf[3] & 0x3F ); | |
for( i = 0; i < ctcharsinbuf; i++ ) | |
[mutableData appendBytes:&outbuf[i] length:1]; | |
} | |
if( flbreak ) break; | |
} | |
} | |
} | |
self = [self initWithData:mutableData]; | |
return self; | |
} | |
#pragma mark - | |
- (NSString *) base64Encoding { | |
return [self base64EncodingWithLineLength:0]; | |
} | |
- (NSString *) base64EncodingWithLineLength:(NSUInteger) lineLength { | |
const unsigned char *bytes = [self bytes]; | |
NSMutableString *result = [NSMutableString stringWithCapacity:[self length]]; | |
unsigned long ixtext = 0; | |
unsigned long lentext = [self length]; | |
long ctremaining = 0; | |
unsigned char inbuf[3], outbuf[4]; | |
unsigned short i = 0; | |
unsigned short charsonline = 0, ctcopy = 0; | |
unsigned long ix = 0; | |
while( YES ) { | |
ctremaining = lentext - ixtext; | |
if( ctremaining <= 0 ) break; | |
for( i = 0; i < 3; i++ ) { | |
ix = ixtext + i; | |
if( ix < lentext ) inbuf[i] = bytes[ix]; | |
else inbuf [i] = 0; | |
} | |
outbuf [0] = (inbuf [0] & 0xFC) >> 2; | |
outbuf [1] = ((inbuf [0] & 0x03) << 4) | ((inbuf [1] & 0xF0) >> 4); | |
outbuf [2] = ((inbuf [1] & 0x0F) << 2) | ((inbuf [2] & 0xC0) >> 6); | |
outbuf [3] = inbuf [2] & 0x3F; | |
ctcopy = 4; | |
switch( ctremaining ) { | |
case 1: | |
ctcopy = 2; | |
break; | |
case 2: | |
ctcopy = 3; | |
break; | |
} | |
for( i = 0; i < ctcopy; i++ ) | |
[result appendFormat:@"%c", encodingTable[outbuf[i]]]; | |
for( i = ctcopy; i < 4; i++ ) | |
[result appendString:@"="]; | |
ixtext += 3; | |
charsonline += 4; | |
if( lineLength > 0 ) { | |
if( charsonline >= lineLength ) { | |
charsonline = 0; | |
[result appendString:@"\n"]; | |
} | |
} | |
} | |
return [NSString stringWithString:result]; | |
} | |
#pragma mark - | |
- (BOOL) hasPrefix:(NSData *) prefix { | |
NSUInteger length = [prefix length]; | |
if( ! prefix || ! length || [self length] < length ) return NO; | |
return ( memcmp( [self bytes], [prefix bytes], length ) == 0 ); | |
} | |
- (BOOL) hasPrefixBytes:(const void *) prefix length:(NSUInteger) length { | |
if( ! prefix || ! length || [self length] < length ) return NO; | |
return ( memcmp( [self bytes], prefix, length ) == 0 ); | |
} | |
#pragma mark - | |
- (BOOL) hasSuffix:(NSData *) suffix { | |
NSUInteger length = [suffix length]; | |
if( ! suffix || ! length || [self length] < length ) return NO; | |
return ( memcmp( ((const char *)[self bytes] + ([self length] - length)), [suffix bytes], length ) == 0 ); | |
} | |
- (BOOL) hasSuffixBytes:(const void *) suffix length:(NSUInteger) length { | |
if( ! suffix || ! length || [self length] < length ) return NO; | |
return ( memcmp( ((const char *)[self bytes] + ([self length] - length)), suffix, length ) == 0 ); | |
} | |
@end | |
@implementation NSData (base64) | |
+ (NSData *)decodeBase64WithString:(NSString *)strBase64{ | |
const char * objPointer = [strBase64 cStringUsingEncoding:NSASCIIStringEncoding]; | |
int intLength = strlen(objPointer); | |
int intCurrent; | |
int i = 0, j = 0, k; | |
unsigned char * objResult; | |
objResult = calloc(intLength, sizeof(char)); | |
// Run through the whole string, converting as we go | |
while ( ((intCurrent = *objPointer++) != '\0') && (intLength-- > 0) ) { | |
if (intCurrent == '=') { | |
if (*objPointer != '=' && ((i % 4) == 1)) {// || (intLength > 0)) { | |
// the padding character is invalid at this point -- so this entire string is invalid | |
free(objResult); | |
return nil; | |
} | |
continue; | |
} | |
intCurrent = _base64DecodingTable[intCurrent]; | |
if (intCurrent == -1) { | |
// we're at a whitespace -- simply skip over | |
continue; | |
} else if (intCurrent == -2) { | |
// we're at an invalid character | |
free(objResult); | |
return nil; | |
} | |
switch (i % 4) { | |
case 0: | |
objResult[j] = intCurrent << 2; | |
break; | |
case 1: | |
objResult[j++] |= intCurrent >> 4; | |
objResult[j] = (intCurrent & 0x0f) << 4; | |
break; | |
case 2: | |
objResult[j++] |= intCurrent >>2; | |
objResult[j] = (intCurrent & 0x03) << 6; | |
break; | |
case 3: | |
objResult[j++] |= intCurrent; | |
break; | |
} | |
i++; | |
} | |
// mop things up if we ended on a boundary | |
k = j; | |
if (intCurrent == '=') { | |
switch (i % 4) { | |
case 1: | |
// Invalid state | |
free(objResult); | |
return nil; | |
case 2: | |
k++; | |
// flow through | |
case 3: | |
objResult[k] = 0; | |
} | |
} | |
// Cleanup and setup the return NSData | |
NSData * objData = [[[NSData alloc] initWithBytes:objResult length:j] autorelease]; | |
free(objResult); | |
return objData; | |
} | |
- (NSString *) base64String { | |
int length = [self length]; | |
NSData* data = self; | |
unsigned long ixtext, lentext; | |
long ctremaining; | |
unsigned char input[3], output[4]; | |
short i, charsonline = 0, ctcopy; | |
const unsigned char *raw; | |
NSMutableString *result; | |
lentext = [data length]; | |
if (lentext < 1) | |
return @""; | |
result = [NSMutableString stringWithCapacity: lentext]; | |
raw = [data bytes]; | |
ixtext = 0; | |
while (true) { | |
ctremaining = lentext - ixtext; | |
if (ctremaining <= 0) | |
break; | |
for (i = 0; i < 3; i++) { | |
unsigned long ix = ixtext + i; | |
if (ix < lentext) | |
input[i] = raw[ix]; | |
else | |
input[i] = 0; | |
} | |
output[0] = (input[0] & 0xFC) >> 2; | |
output[1] = ((input[0] & 0x03) << 4) | ((input[1] & 0xF0) >> 4); | |
output[2] = ((input[1] & 0x0F) << 2) | ((input[2] & 0xC0) >> 6); | |
output[3] = input[2] & 0x3F; | |
ctcopy = 4; | |
switch (ctremaining) { | |
case 1: | |
ctcopy = 2; | |
break; | |
case 2: | |
ctcopy = 3; | |
break; | |
} | |
for (i = 0; i < ctcopy; i++) | |
[result appendString: [NSString stringWithFormat: @"%c", _base64EncodingTable[output[i]]]]; | |
for (i = ctcopy; i < 4; i++) | |
[result appendString: @"="]; | |
ixtext += 3; | |
charsonline += 4; | |
if ((length > 0) && (charsonline >= length)) | |
charsonline = 0; | |
} | |
return result; | |
} | |
- (NSData *)gzipInflate | |
{ | |
if ([self length] == 0) return self; | |
unsigned full_length = [self length]; | |
unsigned half_length = [self length] / 2; | |
NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length]; | |
BOOL done = NO; | |
int status; | |
z_stream strm; | |
strm.next_in = (Bytef *)[self bytes]; | |
strm.avail_in = [self length]; | |
strm.total_out = 0; | |
strm.zalloc = Z_NULL; | |
strm.zfree = Z_NULL; | |
if (inflateInit2(&strm, (15+32)) != Z_OK) return nil; | |
while (!done) | |
{ | |
// Make sure we have enough room and reset the lengths. | |
if (strm.total_out >= [decompressed length]) | |
[decompressed increaseLengthBy: half_length]; | |
strm.next_out = [decompressed mutableBytes] + strm.total_out; | |
strm.avail_out = [decompressed length] - strm.total_out; | |
// Inflate another chunk. | |
status = inflate (&strm, Z_SYNC_FLUSH); | |
if (status == Z_STREAM_END) done = YES; | |
else if (status != Z_OK) break; | |
} | |
if (inflateEnd (&strm) != Z_OK) return nil; | |
// Set real length. | |
if (done) | |
{ | |
[decompressed setLength: strm.total_out]; | |
return [NSData dataWithData: decompressed]; | |
} | |
else return nil; | |
} | |
- (NSData *)gzipDeflate | |
{ | |
if ([self length] == 0) return self; | |
z_stream strm; | |
strm.zalloc = Z_NULL; | |
strm.zfree = Z_NULL; | |
strm.opaque = Z_NULL; | |
strm.total_out = 0; | |
strm.next_in=(Bytef *)[self bytes]; | |
strm.avail_in = [self length]; | |
// Compresssion Levels: | |
// Z_NO_COMPRESSION | |
// Z_BEST_SPEED | |
// Z_BEST_COMPRESSION | |
// Z_DEFAULT_COMPRESSION | |
if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY) != Z_OK) return nil; | |
NSMutableData *compressed = [NSMutableData dataWithLength:16384]; // 16K chunks for expansion | |
do { | |
if (strm.total_out >= [compressed length]) | |
[compressed increaseLengthBy: 16384]; | |
strm.next_out = [compressed mutableBytes] + strm.total_out; | |
strm.avail_out = [compressed length] - strm.total_out; | |
deflate(&strm, Z_FINISH); | |
} while (strm.avail_out == 0); | |
deflateEnd(&strm); | |
[compressed setLength: strm.total_out]; | |
//[compressed writeToFile:@"/Users/timdavies/Desktop/test.gzip" atomically:YES]; | |
return [NSData dataWithData:compressed]; | |
} | |
- (void) writeToStream:(FILE *)stream { | |
int size = [self length] ; | |
if (size > 0) { | |
char * buffer = (char*)malloc(size); | |
[self getBytes:buffer length:size]; | |
fwrite(buffer, 1, sizeof(buffer), stream); | |
free(buffer); | |
} | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment