Compression API on the iPhone
Asked Answered
C

8

31

Is there a compression API available for use on the iPhone? We're building some RESTful web services for our iPhone app to talk to, but we want to compress at least some of the conversations for efficiency.

I don't care what the format (ZIP, LHA, whatever) is, and it does not need to be secure.

Some respondents have pointed out that the server can compress its output, and the iPhone can consume that. The scenario we have is exactly the reverse. We'll be POSTing compressed content to the web service. We're not concerned with compression going the other way.

Cavorelievo answered 23/10, 2008 at 18:57 Comment(1)
H
12

zlib and bzip2 are available. And you can always add others, as long as they'll (generally) compile under OS X.

bzip2 is a better choice for smallest file sizes, but requires much more CPU power to compress and decompress.

Also, since you're talking to a web service, you may not have to do much. NSURLRequest accepts gzip encoding transparently in server responses.

Horme answered 23/10, 2008 at 19:34 Comment(3)
Actually, bzip2 requires more memory, then a little more CPU, which is why I'd go with gzip/deflate (zlib).Discrimination
We're not worried about compression coming from the server. We're worried about compression going TO the server. I've edited my question to reflect this.Cavorelievo
bzip2 seems to be available on the device, but the headers are missing. Apparently some apps got rejected by using these "private" APIs...Retentivity
D
46

If you store the data for the conversations in an NSData object, the folks at the CocoaDev wiki have posted an NSData category that adds gzip and zlib compression / decompression as simple methods. These have worked well for me in my iPhone application.

As the above link has gone dead while the CocoaDev wiki is being moved to a new host, I've reproduced this category in its entirety below.

Interface:

@interface NSData (NSDataExtension)

// Returns range [start, null byte), or (NSNotFound, 0).
- (NSRange) rangeOfNullTerminatedBytesFrom:(int)start;

// Canonical Base32 encoding/decoding.
+ (NSData *) dataWithBase32String:(NSString *)base32;
- (NSString *) base32String;

// COBS is an encoding that eliminates 0x00.
- (NSData *) encodeCOBS;
- (NSData *) decodeCOBS;

// ZLIB
- (NSData *) zlibInflate;
- (NSData *) zlibDeflate;

// GZIP
- (NSData *) gzipInflate;
- (NSData *) gzipDeflate;

//CRC32
- (unsigned int)crc32;

// Hash
- (NSData*) md5Digest;
- (NSString*) md5DigestString;
- (NSData*) sha1Digest;
- (NSString*) sha1DigestString;
- (NSData*) ripemd160Digest;
- (NSString*) ripemd160DigestString;

@end

Implementation:

#import "NSData+CocoaDevUsersAdditions.h"
#include <zlib.h>
#include <openssl/md5.h>
#include <openssl/sha.h>
#include <openssl/ripemd.h>


@implementation NSData (NSDataExtension)

// Returns range [start, null byte), or (NSNotFound, 0).
- (NSRange) rangeOfNullTerminatedBytesFrom:(int)start
{
    const Byte *pdata = [self bytes];
    int len = [self length];
    if (start < len)
    {
        const Byte *end = memchr (pdata + start, 0x00, len - start);
        if (end != NULL) return NSMakeRange (start, end - (pdata + start));
    }
    return NSMakeRange (NSNotFound, 0);
}

+ (NSData *) dataWithBase32String:(NSString *)encoded
{
    /* First valid character that can be indexed in decode lookup table */
    static int charDigitsBase = '2';

    /* Lookup table used to decode() characters in encoded strings */
    static int charDigits[] =
    {   26,27,28,29,30,31,-1,-1,-1,-1,-1,-1,-1,-1 //   23456789:;<=>?
        ,-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14 // @ABCDEFGHIJKLMNO
        ,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1 // PQRSTUVWXYZ[\]^_
        ,-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14 // `abcdefghijklmno
        ,15,16,17,18,19,20,21,22,23,24,25                // pqrstuvwxyz
    };

    if (! [encoded canBeConvertedToEncoding:NSASCIIStringEncoding]) return nil;
    const char *chars = [encoded cStringUsingEncoding:NSASCIIStringEncoding]; // avoids using characterAtIndex.
    int charsLen = [encoded lengthOfBytesUsingEncoding:NSASCIIStringEncoding];

    // Note that the code below could detect non canonical Base32 length within the loop. However canonical Base32 length can be tested before entering the loop.
    // A canonical Base32 length modulo 8 cannot be:
    // 1 (aborts discarding 5 bits at STEP n=0 which produces no byte),
    // 3 (aborts discarding 7 bits at STEP n=2 which produces no byte),
    // 6 (aborts discarding 6 bits at STEP n=1 which produces no byte).
    switch (charsLen & 7) { // test the length of last subblock
        case 1: //  5 bits in subblock:  0 useful bits but 5 discarded
        case 3: // 15 bits in subblock:  8 useful bits but 7 discarded
        case 6: // 30 bits in subblock: 24 useful bits but 6 discarded
            return nil; // non-canonical length
    }
    int charDigitsLen = sizeof(charDigits);
    int bytesLen = (charsLen * 5) >> 3;
    Byte bytes[bytesLen];
    int bytesOffset = 0, charsOffset = 0;
    // Also the code below does test that other discarded bits
    // (1 to 4 bits at end) are effectively 0.
    while (charsLen > 0)
    {
        int digit, lastDigit;
        // STEP n = 0: Read the 1st Char in a 8-Chars subblock
        // Leave 5 bits, asserting there's another encoding Char
        if ((digit = (int)chars[charsOffset] - charDigitsBase) < 0 || digit >= charDigitsLen || (digit = charDigits[digit]) == -1)
            return nil; // invalid character
        lastDigit = digit << 3;
        // STEP n = 5: Read the 2nd Char in a 8-Chars subblock
        // Insert 3 bits, leave 2 bits, possibly trailing if no more Char
        if ((digit = (int)chars[charsOffset + 1] - charDigitsBase) < 0 || digit >= charDigitsLen || (digit = charDigits[digit]) == -1)
            return nil; // invalid character
        bytes[bytesOffset] = (Byte)((digit >> 2) | lastDigit);
        lastDigit = (digit & 3) << 6;
        if (charsLen == 2) {
            if (lastDigit != 0) return nil; // non-canonical end
            break; // discard the 2 trailing null bits
        }
        // STEP n = 2: Read the 3rd Char in a 8-Chars subblock
        // Leave 7 bits, asserting there's another encoding Char
        if ((digit = (int)chars[charsOffset + 2] - charDigitsBase) < 0 || digit >= charDigitsLen || (digit = charDigits[digit]) == -1)
            return nil; // invalid character
        lastDigit |= (Byte)(digit << 1);
        // STEP n = 7: Read the 4th Char in a 8-chars Subblock
        // Insert 1 bit, leave 4 bits, possibly trailing if no more Char
        if ((digit = (int)chars[charsOffset + 3] - charDigitsBase) < 0 || digit >= charDigitsLen || (digit = charDigits[digit]) == -1)
            return nil; // invalid character
        bytes[bytesOffset + 1] = (Byte)((digit >> 4) | lastDigit);
        lastDigit = (Byte)((digit & 15) << 4);
        if (charsLen == 4) {
            if (lastDigit != 0) return nil; // non-canonical end
            break; // discard the 4 trailing null bits
        }
        // STEP n = 4: Read the 5th Char in a 8-Chars subblock
        // Insert 4 bits, leave 1 bit, possibly trailing if no more Char
        if ((digit = (int)chars[charsOffset + 4] - charDigitsBase) < 0 || digit >= charDigitsLen || (digit = charDigits[digit]) == -1)
            return nil; // invalid character
        bytes[bytesOffset + 2] = (Byte)((digit >> 1) | lastDigit);
        lastDigit = (Byte)((digit & 1) << 7);
        if (charsLen == 5) {
            if (lastDigit != 0) return nil; // non-canonical end
            break; // discard the 1 trailing null bit
        }
        // STEP n = 1: Read the 6th Char in a 8-Chars subblock
        // Leave 6 bits, asserting there's another encoding Char
        if ((digit = (int)chars[charsOffset + 5] - charDigitsBase) < 0 || digit >= charDigitsLen || (digit = charDigits[digit]) == -1)
            return nil; // invalid character
        lastDigit |= (Byte)(digit << 2);
        // STEP n = 6: Read the 7th Char in a 8-Chars subblock
        // Insert 2 bits, leave 3 bits, possibly trailing if no more Char
        if ((digit = (int)chars[charsOffset + 6] - charDigitsBase) < 0 || digit >= charDigitsLen || (digit = charDigits[digit]) == -1)
            return nil; // invalid character
        bytes[bytesOffset + 3] = (Byte)((digit >> 3) | lastDigit);
        lastDigit = (Byte)((digit & 7) << 5);
        if (charsLen == 7) {
            if (lastDigit != 0) return nil; // non-canonical end
            break; // discard the 3 trailing null bits
        }
        // STEP n = 3: Read the 8th Char in a 8-Chars subblock
        // Insert 5 bits, leave 0 bit, next encoding Char may not exist
        if ((digit = (int)chars[charsOffset + 7] - charDigitsBase) < 0 || digit >= charDigitsLen || (digit = charDigits[digit]) == -1)
            return nil; // invalid character
        bytes[bytesOffset + 4] = (Byte)(digit | lastDigit);
        //// This point is always reached for chars.length multiple of 8
        charsOffset += 8;
        bytesOffset += 5;
        charsLen -= 8;
    }
    // On loop exit, discard the n trailing null bits
    return [NSData dataWithBytes:bytes length:sizeof(bytes)];
}

- (NSString *) base32String
{
    /* Lookup table used to canonically encode() groups of data bits */
    static char canonicalChars[] =
    {   'A','B','C','D','E','F','G','H','I','J','K','L','M' // 00..12
        ,'N','O','P','Q','R','S','T','U','V','W','X','Y','Z' // 13..25
        ,'2','3','4','5','6','7'                             // 26..31
    };
    const Byte *bytes = [self bytes];
    int bytesOffset = 0, bytesLen = [self length];
    int charsOffset = 0, charsLen = ((bytesLen << 3) + 4) / 5;
    char chars[charsLen];
    while (bytesLen != 0) {
        int digit, lastDigit;
        // INVARIANTS FOR EACH STEP n in [0..5[; digit in [0..31[;
        // The remaining n bits are already aligned on top positions
        // of the 5 least bits of digit, the other bits are 0.
        ////// STEP n = 0: insert new 5 bits, leave 3 bits
        digit = bytes[bytesOffset] & 255;
        chars[charsOffset] = canonicalChars[digit >> 3];
        lastDigit = (digit & 7) << 2;
        if (bytesLen == 1) { // put the last 3 bits
            chars[charsOffset + 1] = canonicalChars[lastDigit];
            break;
        }
        ////// STEP n = 3: insert 2 new bits, then 5 bits, leave 1 bit
        digit = bytes[bytesOffset + 1] & 255;
        chars[charsOffset + 1] = canonicalChars[(digit >> 6) | lastDigit];
        chars[charsOffset + 2] = canonicalChars[(digit >> 1) & 31];
        lastDigit = (digit & 1) << 4;
        if (bytesLen == 2) { // put the last 1 bit
            chars[charsOffset + 3] = canonicalChars[lastDigit];
            break;
        }
        ////// STEP n = 1: insert 4 new bits, leave 4 bit
        digit = bytes[bytesOffset + 2] & 255;
        chars[charsOffset + 3] = canonicalChars[(digit >> 4) | lastDigit];
        lastDigit = (digit & 15) << 1;
        if (bytesLen == 3) { // put the last 1 bits
            chars[charsOffset + 4] = canonicalChars[lastDigit];
            break;
        }
        ////// STEP n = 4: insert 1 new bit, then 5 bits, leave 2 bits
        digit = bytes[bytesOffset + 3] & 255;
        chars[charsOffset + 4] = canonicalChars[(digit >> 7) | lastDigit];
        chars[charsOffset + 5] = canonicalChars[(digit >> 2) & 31];
        lastDigit = (digit & 3) << 3;
        if (bytesLen == 4) { // put the last 2 bits
            chars[charsOffset + 6] = canonicalChars[lastDigit];
            break;
        }
        ////// STEP n = 2: insert 3 new bits, then 5 bits, leave 0 bit
        digit = bytes[bytesOffset + 4] & 255;
        chars[charsOffset + 6] = canonicalChars[(digit >> 5) | lastDigit];
        chars[charsOffset + 7] = canonicalChars[digit & 31];
        //// This point is always reached for bytes.length multiple of 5
        bytesOffset += 5;
        charsOffset += 8;
        bytesLen -= 5;
    }
    return [NSString stringWithCString:chars length:sizeof(chars)];
}

#define FinishBlock(X) \
(*code_ptr = (X), \
code_ptr = dst++, \
code = 0x01)

- (NSData *) encodeCOBS
{
    if ([self length] == 0) return self;

    NSMutableData *encoded = [NSMutableData dataWithLength:([self length] + [self length] / 254 + 1)];
    unsigned char *dst = [encoded mutableBytes];
    const unsigned char *ptr = [self bytes];
    unsigned long length = [self length];
    const unsigned char *end = ptr + length;
    unsigned char *code_ptr = dst++;
    unsigned char code = 0x01;
    while (ptr < end)
    {
        if (*ptr == 0) FinishBlock(code);
        else
        {
            *dst++ = *ptr;
            code++;
            if (code == 0xFF) FinishBlock(code);
        }
        ptr++;
    }
    FinishBlock(code);

    [encoded setLength:((Byte *)dst - (Byte *)[encoded mutableBytes])];
    return [NSData dataWithData:encoded];
}

- (NSData *) decodeCOBS
{
    if ([self length] == 0) return self;

    const Byte *ptr = [self bytes];
    unsigned length = [self length];
    NSMutableData *decoded = [NSMutableData dataWithLength:length];
    Byte *dst = [decoded mutableBytes];
    Byte *basedst = dst;

    const unsigned char *end = ptr + length;
    while (ptr < end)
    {
        int i, code = *ptr++;
        for (i=1; i<code; i++) *dst++ = *ptr++;
        if (code < 0xFF) *dst++ = 0;
    }

    [decoded setLength:(dst - basedst)];
    return [NSData dataWithData:decoded];
}

- (NSData *)zlibInflate
{
    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 (inflateInit (&strm) != 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 *)zlibDeflate
{
    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 (deflateInit(&strm, Z_DEFAULT_COMPRESSION) != Z_OK) return nil;

    NSMutableData *compressed = [NSMutableData dataWithLength:16384];  // 16K chuncks 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];
    return [NSData dataWithData: compressed];
}

- (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];
    return [NSData dataWithData:compressed];
}

// --------------------------------CRC32-------------------------------
static const unsigned long crc32table[] =
{
    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
    0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
    0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
    0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
    0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
    0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
    0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
    0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
    0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
    0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
    0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
    0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
    0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
    0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
    0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
    0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
    0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
    0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
    0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
    0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
    0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
    0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};

- (unsigned int)crc32
{
    unsigned int    crcval;
    unsigned int    x, y;
    const void      *bytes;
    unsigned int    max;

    bytes = [self bytes];
    max = [self length];
    crcval = 0xffffffff;
    for (x = 0, y = max; x < y; x++) {
        crcval = ((crcval >> 8) & 0x00ffffff) ^ crc32table[(crcval ^ (*((unsigned char *)bytes + x))) & 0xff];
    }

    return crcval ^ 0xffffffff;
}

// Hash function, by [[DamienBob]]

#define HEComputeDigest(method)                     \
method##_CTX ctx;                               \
unsigned char digest[method##_DIGEST_LENGTH];       \
method##_Init(&ctx);                            \
method##_Update(&ctx, [self bytes], [self length]);     \
method##_Final(digest, &ctx);

#define HEComputeDigestNSData(method)               \
HEComputeDigest(method)                     \
return [NSData dataWithBytes:digest length:method##_DIGEST_LENGTH];

#define HEComputeDigestNSString(method)             \
static char __HEHexDigits[] = "0123456789abcdef";       \
unsigned char digestString[2*method##_DIGEST_LENGTH];\
unsigned int i;                                 \
HEComputeDigest(method)                     \
for(i=0; i<method##_DIGEST_LENGTH; i++) {               \
    digestString[2*i]   = __HEHexDigits[digest[i] >> 4];    \
    digestString[2*i+1] = __HEHexDigits[digest[i] & 0x0f];\
}                                           \
return [NSString stringWithCString:(char *)digestString length:2*method##_DIGEST_LENGTH];

#define SHA1_CTX                SHA_CTX
#define SHA1_DIGEST_LENGTH      SHA_DIGEST_LENGTH

- (NSData*) md5Digest
{
    HEComputeDigestNSData(MD5);
}

- (NSString*) md5DigestString
{
    HEComputeDigestNSString(MD5);
}

- (NSData*) sha1Digest
{
    HEComputeDigestNSData(SHA1);
}

- (NSString*) sha1DigestString
{
    HEComputeDigestNSString(SHA1);
}

- (NSData*) ripemd160Digest
{
    HEComputeDigestNSData(RIPEMD160);
}

- (NSString*) ripemd160DigestString
{
    HEComputeDigestNSString(RIPEMD160);
}

@end
Dissentient answered 24/10, 2008 at 15:50 Comment(12)
@Brad Larson NSData category link is not working (404 not found). Please update...Panacea
@Pranav - Thanks for letting me know. I've added the full source code from the Google cache of the site into the body of my answer.Dissentient
@BradLarson Thanks 4 Update. I'm using this category to compress a recorded video (Medium Quality) on iPad but I don't see any reduction in the file size of the video after using this category. It remains the same, is there some thing I'hv missed out on?Panacea
@Pranav - H.264 video is already heavily compressed. Gzip compression isn't going to do much for that. Gzip works best for uncompressed sources like text, and may even make files larger if used on already compressed files.Dissentient
The gzipInflate method in the code above has a bug. It doesn't handle Z_BUF_ERROR appropriately. See: #17821164Dismuke
@Dismuke - Good catch. If that fixes the issue, I can update the above with it. This was based on code from the CocoaDev wiki, and had worked for all the gzip test cases I threw at it. I may not have used as large a test base as I could, though.Dissentient
web.archive.org/web/20100102154917/http://cocoadev.com/…Yellowknife
@BradLarson Does this support Decompressing of Folder?Panacea
Well i know this is old, but i'm losing my mind with this. where do i find the libraries you reference? openssl/md5.h, etc. that will work with this project?Stanley
@deweydb - zlib is a standard library that you just need to include. OpenSSL isn't really needed for the Zip part of this, just the hashes at the end. You can remove all the code for hashing at the bottom of this to get rid of the OpenSSL dependency.Dissentient
can you please provide swift version?Cookgeneral
Thank you so much for this Brad! In case someone wants to include this in a Pod, then you'll notice it has some compiler warnings and a deprecation warning (NSString stringWithCString). I fixed these up (no other changes), here: github.com/xaphod/xaphodObjCUtils/blob/master/Pod/Classes/…Dumortierite
H
12

zlib and bzip2 are available. And you can always add others, as long as they'll (generally) compile under OS X.

bzip2 is a better choice for smallest file sizes, but requires much more CPU power to compress and decompress.

Also, since you're talking to a web service, you may not have to do much. NSURLRequest accepts gzip encoding transparently in server responses.

Horme answered 23/10, 2008 at 19:34 Comment(3)
Actually, bzip2 requires more memory, then a little more CPU, which is why I'd go with gzip/deflate (zlib).Discrimination
We're not worried about compression coming from the server. We're worried about compression going TO the server. I've edited my question to reflect this.Cavorelievo
bzip2 seems to be available on the device, but the headers are missing. Apparently some apps got rejected by using these "private" APIs...Retentivity
O
8

Apple's built in libcompression is now available for iOS 9. A short example of compression_encode_buffer is shown below for compressing NSData.

@import Compression;

NSData *theData = [NSData dataWithContentsOfFile:[<some file> path]];
size_t theDataSize = [theData length];
const uint8_t *buf = (const uint8_t *)[theData bytes];
uint8_t *destBuf = malloc(sizeof(uint8_t) * theDataSize);
size_t compressedSize = compression_encode_buffer(destBuf, theDataSize, buf, theDataSize, NULL, COMPRESSION_LZFSE);
self.<NSData item> = [NSData dataWithBytes:destBuf length:compressedSize];

NSLog(@"originalsize:%zu compressed:%zu", theDataSize, compressedSize);
free(destBuf);

A number of different algorithms are available:

  • LZMA
  • LZ4
  • ZLIB
  • LZFSE

Block compression or stream compression are both supported.

See https://developer.apple.com/library/mac/documentation/Performance/Reference/Compression/

Oly answered 11/8, 2016 at 6:2 Comment(1)
Is it possible to set a password for compression with this method?Terebinthine
A
4

If you have only compressed data and know uncompressed size you can use:

#import "zlib.h"


int datal = [zipedData length];
Bytef *buffer[uncompressedSize];
Bytef *dataa[datal];

[zipedData getBytes:dataa];

Long *ld;

uLong sl = datal;
*ld = uncompressedSize;
if(uncompress(buffer, ld, dataa, sl) == Z_OK)
{
NSData *uncompressedData = [NSData dataWithBytes:buffer length:uncompressedSize];
NSString *txtFile = [[NSString alloc] initWithData:uncompressedData encoding:NSUTF8StringEncoding];
}
Audiovisual answered 8/6, 2010 at 11:14 Comment(0)
A
1

I believe zlib is available on the phone.

Acumen answered 23/10, 2008 at 19:5 Comment(0)
H
1

trust me the best choice is to use ZipArchive see: How to decompress an AES-256 encrypted Zip file?

ready for help if needed.

Houseman answered 3/11, 2010 at 7:4 Comment(0)
B
1

Objective-Zip is another option. See these excellent instructions.

Note I had to convert the source code to use ARC by using the XCode->Edit->Refactor->Convert to Objective C ARC.

Beatty answered 6/12, 2011 at 5:55 Comment(0)
A
0

NSURL says it supports the gzip encoding so you shouldn't have to do anything more than have your RESTful web service return gzip encoded content when appropriate. All the decoding will be done under the covers.

Aseity answered 23/10, 2008 at 20:32 Comment(2)
@Gregory Hinley: NSMutableURLRequest *req; [req setHTTPMethod:@"POST"];Gastongastralgia
@JeremyW.Sherman it does not handle the POST at all. I verified this working on a project. NSURLRequest only takes care of it on download. github.com/AFNetworking/AFNetworking/wiki/… github.com/mattt/GodzippaDeonnadeonne

© 2022 - 2024 — McMap. All rights reserved.