diff --git a/Sources/Gzip/Data+Gzip.swift b/Sources/Gzip/Data+Gzip.swift index 53b935c..a3afb22 100644 --- a/Sources/Gzip/Data+Gzip.swift +++ b/Sources/Gzip/Data+Gzip.swift @@ -243,61 +243,71 @@ extension Data { return Data() } - var stream = z_stream() - var status: Int32 - - status = inflateInit2_(&stream, wBits, ZLIB_VERSION, Int32(DataSize.stream)) - - guard status == Z_OK else { - // inflateInit2 returns: - // Z_VERSION_ERROR The zlib library version is incompatible with the version assumed by the caller. - // Z_MEM_ERROR There was not enough memory. - // Z_STREAM_ERROR A parameters are invalid. - - throw GzipError(code: status, msg: stream.msg) - } - var data = Data(capacity: self.count * 2) + var totalIn: uLong = 0 + var totalOut: uLong = 0 + repeat { - if Int(stream.total_out) >= data.count { - data.count += self.count / 2 + var stream = z_stream() + var status: Int32 + + status = inflateInit2_(&stream, wBits, ZLIB_VERSION, Int32(DataSize.stream)) + + guard status == Z_OK else { + // inflateInit2 returns: + // Z_VERSION_ERROR The zlib library version is incompatible with the version assumed by the caller. + // Z_MEM_ERROR There was not enough memory. + // Z_STREAM_ERROR A parameters are invalid. + + throw GzipError(code: status, msg: stream.msg) } - - let inputCount = self.count - let outputCount = data.count - - self.withUnsafeBytes { (inputPointer: UnsafeRawBufferPointer) in - stream.next_in = UnsafeMutablePointer(mutating: inputPointer.bindMemory(to: Bytef.self).baseAddress!).advanced(by: Int(stream.total_in)) - stream.avail_in = uint(inputCount) - uInt(stream.total_in) - - data.withUnsafeMutableBytes { (outputPointer: UnsafeMutableRawBufferPointer) in - stream.next_out = outputPointer.bindMemory(to: Bytef.self).baseAddress!.advanced(by: Int(stream.total_out)) - stream.avail_out = uInt(outputCount) - uInt(stream.total_out) - - status = inflate(&stream, Z_SYNC_FLUSH) - - stream.next_out = nil + + repeat { + if Int(totalOut + stream.total_out) >= data.count { + data.count += self.count / 2 + } + + let inputCount = self.count + let outputCount = data.count + + self.withUnsafeBytes { (inputPointer: UnsafeRawBufferPointer) in + let inputStartPosition = totalIn + stream.total_in + stream.next_in = UnsafeMutablePointer(mutating: inputPointer.bindMemory(to: Bytef.self).baseAddress!).advanced(by: Int(inputStartPosition)) + stream.avail_in = uint(inputCount) - uInt(inputStartPosition) + + data.withUnsafeMutableBytes { (outputPointer: UnsafeMutableRawBufferPointer) in + let outputStartPosition = totalOut + stream.total_out + stream.next_out = outputPointer.bindMemory(to: Bytef.self).baseAddress!.advanced(by: Int(outputStartPosition)) + stream.avail_out = uInt(outputCount) - uInt(outputStartPosition) + + status = inflate(&stream, Z_SYNC_FLUSH) + + stream.next_out = nil + } + + stream.next_in = nil } - stream.next_in = nil + } while (status == Z_OK) + totalIn += stream.total_in + + guard inflateEnd(&stream) == Z_OK, status == Z_STREAM_END else { + // inflate returns: + // Z_DATA_ERROR The input data was corrupted (input stream not conforming to the zlib format or incorrect check value). + // Z_STREAM_ERROR The stream structure was inconsistent (for example if next_in or next_out was NULL). + // Z_MEM_ERROR There was not enough memory. + // Z_BUF_ERROR No progress is possible or there was not enough room in the output buffer when Z_FINISH is used. + throw GzipError(code: status, msg: stream.msg) } - - } while status == Z_OK - - guard inflateEnd(&stream) == Z_OK, status == Z_STREAM_END else { - // inflate returns: - // Z_DATA_ERROR The input data was corrupted (input stream not conforming to the zlib format or incorrect check value). - // Z_STREAM_ERROR The stream structure was inconsistent (for example if next_in or next_out was NULL). - // Z_MEM_ERROR There was not enough memory. - // Z_BUF_ERROR No progress is possible or there was not enough room in the output buffer when Z_FINISH is used. - - throw GzipError(code: status, msg: stream.msg) - } - - data.count = Int(stream.total_out) - + + + totalOut += stream.total_out + } while (totalIn < self.count) + data.count = Int(totalOut) + return data } + } diff --git a/Tests/GzipTests/GzipTests.swift b/Tests/GzipTests/GzipTests.swift index 47c78a8..eac8bdc 100644 --- a/Tests/GzipTests/GzipTests.swift +++ b/Tests/GzipTests/GzipTests.swift @@ -115,6 +115,19 @@ final class GzipTests: XCTestCase { XCTAssertEqual(json?.first, "{") XCTAssertEqual(json?.last, "}") } + + func testDecompressionCompositedCompression() throws { + let firstData = try XCTUnwrap("test".data(using: .utf8)).gzipped() + let secondData = try XCTUnwrap("string".data(using: .utf8)).gzipped() + + let data = firstData + secondData + + let uncompressed = try data.gunzipped() + + XCTAssertTrue(data.isGzipped) + XCTAssertEqual(String(data: uncompressed, encoding: .utf8), "teststring") + } + }