Skip to content

Commit b0619b2

Browse files
committed
Preventing a bug in Apples lsfse compressor that occures for large chunks of data.
1 parent b2c7a80 commit b0619b2

File tree

5 files changed

+284
-76
lines changed

5 files changed

+284
-76
lines changed

DataCompression.podspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = "DataCompression"
3-
s.version = "3.4.0"
3+
s.version = "3.5.0"
44
s.summary = "Swift libcompression wrapper as an extension for the Data type (GZIP, ZLIB, LZFSE, LZMA, LZ4, deflate, RFC-1950, RFC-1951, RFC-1952)"
55
s.authors = { "Markus Wanke" => "mw99@users.noreply.github.com" }
66
s.homepage = "https://github.com/mw99/DataCompression"

README.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
#### Swift version support
2222
| Library Version | Swift Version |
2323
|-----------------|---------------|
24-
| 3.4.0 | 5.0 |
24+
| 3.5.0 | 5.0 |
2525
| 3.1.0 | 4.2 |
2626
| 3.0.0 | 3.0 -> 4.1 |
2727
| 2.0.1 | < 3.0 |
@@ -190,6 +190,9 @@ You only need one file located at `Sources/DataCompression.swift`. Drag and drop
190190
## Change log / Upgrading guide
191191

192192

193+
##### Version `3.4.0` to `3.5.0`
194+
- Fix that prevents a bug in Apples lzfse compressor when working with large chunks of data.
195+
193196
##### Version `3.3.0` to `3.4.0`
194197
- Swift 5 release had further deprecation warnings than in the Swift 5 beta. Fixed.
195198

Sources/DataCompression/DataCompression.swift

+24-10
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public extension Data
5656

5757
/// Please consider the [libcompression documentation](https://developer.apple.com/reference/compression/1665429-data_compression)
5858
/// for further details. Short info:
59-
/// zlib : Aka deflate. Fast with a good compression rate. Proved itself ofer time and is supported everywhere.
59+
/// zlib : Aka deflate. Fast with a good compression rate. Proved itself over time and is supported everywhere.
6060
/// lzfse : Apples custom Lempel-Ziv style compression algorithm. Claims to compress as good as zlib but 2 to 3 times faster.
6161
/// lzma : Horribly slow. Compression as well as decompression. Compresses better than zlib though.
6262
/// lz4 : Fast, but compression rate is very bad. Apples lz4 implementation often to not compress at all.
@@ -433,7 +433,7 @@ public struct Adler32: CustomStringConvertible
433433

434434

435435

436-
extension Data
436+
fileprivate extension Data
437437
{
438438
func withUnsafeBytes<ResultType, ContentType>(_ body: (UnsafePointer<ContentType>) throws -> ResultType) rethrows -> ResultType
439439
{
@@ -470,8 +470,21 @@ fileprivate func perform(_ config: Config, source: UnsafePointer<UInt8>, sourceS
470470
let status = compression_stream_init(&stream, config.operation, config.algorithm)
471471
guard status != COMPRESSION_STATUS_ERROR else { return nil }
472472
defer { compression_stream_destroy(&stream) }
473-
474-
let bufferSize = Swift.max( Swift.min(sourceSize, 64 * 1024), 64)
473+
474+
var result = preload
475+
var flags: Int32 = Int32(COMPRESSION_STREAM_FINALIZE.rawValue)
476+
let blockLimit = 64 * 1024
477+
var bufferSize = Swift.max(sourceSize, 64)
478+
479+
if sourceSize > blockLimit {
480+
bufferSize = blockLimit
481+
if config.algorithm == COMPRESSION_LZFSE && config.operation != COMPRESSION_STREAM_ENCODE {
482+
// This fixes a bug in Apples lzfse decompressor. it will sometimes fail randomly when the input gets
483+
// splitted into multiple chunks and the flag is not 0. Even though it should always work with FINALIZE...
484+
flags = 0
485+
}
486+
}
487+
475488
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
476489
defer { buffer.deallocate() }
477490

@@ -480,20 +493,21 @@ fileprivate func perform(_ config: Config, source: UnsafePointer<UInt8>, sourceS
480493
stream.src_ptr = source
481494
stream.src_size = sourceSize
482495

483-
var res = preload
484-
let flags: Int32 = Int32(COMPRESSION_STREAM_FINALIZE.rawValue)
485-
486496
while true {
487497
switch compression_stream_process(&stream, flags) {
488498
case COMPRESSION_STATUS_OK:
489499
guard stream.dst_size == 0 else { return nil }
490-
res.append(buffer, count: stream.dst_ptr - buffer)
500+
result.append(buffer, count: stream.dst_ptr - buffer)
491501
stream.dst_ptr = buffer
492502
stream.dst_size = bufferSize
503+
504+
if flags == 0 && stream.src_size == 0 { // part of the lzfse bugfix above
505+
flags = Int32(COMPRESSION_STREAM_FINALIZE.rawValue)
506+
}
493507

494508
case COMPRESSION_STATUS_END:
495-
res.append(buffer, count: stream.dst_ptr - buffer)
496-
return res
509+
result.append(buffer, count: stream.dst_ptr - buffer)
510+
return result
497511

498512
default:
499513
return nil

0 commit comments

Comments
 (0)