2017/04/29

Compressing and Decompressing NSData

Since iOS9 Compression.framework which we can use for (de)compressing certain chunks of data.

Note: This framework (and below code) is not for directly zipping/unzipping files. The zip format is for compresssing various files and/or directories which require more metadata. The structure of a PKZip file.

Below code simply shows how to compress a particular chunk of NSData.

import Compression

extension Data {
    func compressedData(algorithm: compression_algorithm) -> Data? {
        return performEncoding(compress: true, algorithm: algorithm)
    }

    func decompressedData(algorithm: compression_algorithm) -> Data? {
        return performEncoding(compress: false, algorithm: algorithm)
    }

    private var unsafePointerUInt8: UnsafePointer<UInt8> {
        // based on http://stackoverflow.com/questions/25554986/
        let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: count)
        let stream = OutputStream(toBuffer: buffer, capacity: count)
        stream.open()
        withUnsafeBytes({ (p: UnsafePointer<UInt8>) -> Void in
            stream.write(p, maxLength: self.count)
        })
        stream.close()
        return UnsafePointer<UInt8>(buffer)
    }
    
    private func performEncoding(compress: Bool, algorithm: compression_algorithm) -> Data? {
        // based on https://gist.github.com/daltheman/4716ec10d6d0f71aba56
        let sourceBuffer = unsafePointerUInt8
        let sourceBufferSize = data.count
        
        let destinationBuffer = UnsafeMutablePointer.allocate(capacity: sourceBufferSize)
        let destinationBufferSize: Int = sourceBufferSize
        
        let status: Int
        if compress {
            status = compression_encode_buffer(
                destinationBuffer, destinationBufferSize,
                sourceBuffer, sourceBufferSize,
                nil, algorithm)
        } else {
            status = compression_decode_buffer(
                destinationBuffer, destinationBufferSize,
                sourceBuffer, sourceBufferSize,
                nil, algorithm)
        }
        
        if status == 0 {
            print("Error with status: \(status)")
            return nil
        }
        print("Original size: \(sourceBufferSize) | Compressed size: \(status)")
        return Data(bytesNoCopy: destinationBuffer, count: status, deallocator: .free)
    }
}

0 comments :