Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Compression: doesn't decompress LZ4 image

I'm trying to decompress an lz4-compressed png image with the Swift Compression package, but the code exits with a zero size. My code is as follows, and the decompressed file is expected to be 240Kb.

[UPDATE 2] I've stumbled upon this in the Apple's doc:

The frame is documented here so that you can easily wrap another LZ4 encoder/decoder to produce/consume the same data stream if necessary. An LZ4 encoded buffer is a sequence of blocks, each of which begins with a header. There are three possible headers:

A compressed block header consists of the octets 0x62, 0x76, 0x34, and 0x31, followed by the size in bytes of the decoded (plaintext) data represented by the block and the size (in bytes) of the encoded data stored in the block. Both size fields are stored as (possibly unaligned) 32-bit little-endian values. The compressed block header is followed immediately by the actual LZ4-encoded data stream.

An end of stream header consists of the octets 0x62, 0x76, 0x34, and 0x24 and marks the end of the LZ4 frame. No further data may be written or read beyond this header.

So I added the header and footer accordingly and it works. BUT —there's always a 'but'—, how can I know "the size in bytes of the decoded (plaintext) data" prior to uncompress?

[UPDATE 1]

I give one LZ4 file for you to test, and code usage for decompressing it from the main bundle.

LZ4 file compressed with the sw "LZ4 command line interface 64-bits v1.8.0, by Yann Collet" widely available on the Internet

https://drive.google.com/file/d/1eQ204LJs_xsHRwJ_jUcl1Up9NFo8monO/view?usp=sharing

LZ4 decompression usage

    if let url = Bundle.main.url(forResource: "TestImage300.png", withExtension: "lz4") {
        if let compressed = try? Data(contentsOf: url) {
            if let decompressed = compressed.lz4Decompress() {
                if let image = UIImage(data: decompressed) {
                    self.sourceImages.append(image)
                    print("Unittest: Decompressed image as \(image)")
                }
            }
        }
    }

LZ4 Data extension code for decompression:

import Foundation
import Compression

extension Data {

    func lz4Decompress() -> Data? {
        let destinationBufferSize: size_t = 480000
        let destinationBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: destinationBufferSize)

        let sourceBufferSize = count

        let decompressed = withUnsafeBytes { (sourceBuffer: UnsafePointer<UInt8>) -> Data? in

            let size = compression_decode_buffer(
                destinationBuffer, destinationBufferSize,
                sourceBuffer, sourceBufferSize,
                nil,    // scratch buffer automatically handled
                COMPRESSION_LZ4
            )

            if size == 0 {
                print("Error ")
                return nil
            }

            print("Original compressed size: \(sourceBufferSize) | Decompressed size: \(size)")

            return Data(bytesNoCopy: destinationBuffer, count: size, deallocator: .free)
        }
        return decompressed
     }
}
like image 453
Stéphane de Luca Avatar asked Nov 07 '22 13:11

Stéphane de Luca


1 Answers

let intArray: [Int8] =  [-16, 1, 1, 39, 0, 19, 11, -30, 7, 10, 29, 14, 0, 0, 0, 0, 96, 9, 6, 0, 1, 2, 0, 17, 14, 6, 0, 2, 2, 0, 18, 14, 7, 0, 65, 0, 0, 0, -51, 6, 0, 0, 2, 0, 0, 43, 0, 16, 2, 9, 0, -1, 13, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 8, 0, 0, 0, 10, 4, 0, 64, 33, -105, 58, 115, 0, 12, 2, 0, 80, 0, 0, 0, 0, 0]

        let uintArray = intArray.map { UInt8(bitPattern: $0) }

// For Visibility the uintArray unsigned is [240, 1, 1, 39, 0, 19, 11, 226, 7, 10, 29, 14, 0, 0, 0, 0, 96, 9, 6, 0, 1, 2, 0, 17, 14, 6, 0, 2, 2, 0, 18, 14, 7, 0, 65, 0, 0, 0, 205, 6, 0, 0, 2, 0, 0, 43, 0, 16, 2, 9, 0, 255, 13, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 8, 0, 0, 0, 10, 4, 0, 64, 33, 151, 58, 115, 0, 12, 2, 0, 80, 0, 0, 0, 0, 0]

        var encodedData = Data.init(bytes:uintArray)

        let decodedCapacity = 205
        let decodedDestinationBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: decodedCapacity)

        let decodedData = encodedData.withUnsafeBytes {
            (encodedSourceBuffer: UnsafePointer<UInt8>) -> Data? in

            let decodedCharCount = compression_decode_buffer(decodedDestinationBuffer,
                                                             decodedCapacity,
                                                             encodedSourceBuffer,
                                                             encodedData.count,
                                                             nil,
                                                             COMPRESSION_LZ4_RAW)

            if decodedCharCount == 0 {
                fatalError("Decoding failed.")
            }

            print("Before: \(encodedSourceBuffer) | After: \(decodedCharCount)")

            return Data(bytesNoCopy: decodedDestinationBuffer, count: decodedCharCount, deallocator: .free)

        }

You should use COMPRESSION_LZ4_RAW in a case you dont have headers defined in the compression

like image 122
Nicson Avatar answered Dec 05 '22 07:12

Nicson