                 }
             }
         }
-        #else
+#else
         self.withUnsafeBytes { (bytes) in
             let bins = stride(from: 0, to: self.count, by: 256)
             for bin in bins {

// We provide a compatible method for targets that use Swift 4.x so that we can use the new version
// across all language versions.

internal extension Data {
    func withUnsafeBytes<T>(_ body: (UnsafeRawBufferPointer) throws -> T) rethrows -> T {
        let count = self.count
        return try withUnsafeBytes { (pointer: UnsafePointer<UInt8>) throws -> T in

/// - Throws: Can throw to indicate errors in the data source.
public typealias Provider = (_ position: Int, _ size: Int) throws -> Data

extension Data {
    enum CompressionError: Error {
        case invalidStream
        case corruptedData
    }

    /// Calculate the `CRC32` checksum of the receiver.
    ///
    /// - Parameter checksum: The starting seed.
    /// - Returns: The checksum calculated from the bytes of the receiver and the starting seed.
    public func crc32(checksum: CRC32) -> CRC32 {
        #if canImport(zlib)
        return withUnsafeBytes { bufferPointer in
            let length = UInt32(count)
            return CRC32(zlib.crc32(UInt(checksum), bufferPointer.bindMemory(to: UInt8.self).baseAddress, length))
        }
        #else
        return self.builtInCRC32(checksum: checksum)
        #endif
    }

    /// Compress the output of `provider` and pass it to `consumer`.
    /// - Parameters:
    ///   - size: The uncompressed size of the data to be compressed.
    ///   - bufferSize: The maximum size of the compression buffer.
    ///   - provider: A closure that accepts a position and a chunk size. Returns a `Data` chunk.
    ///   - consumer: A closure that processes the result of the compress operation.
    /// - Returns: The checksum of the processed content.
    public static func compress(size: Int, bufferSize: Int, provider: Provider, consumer: Consumer) throws -> CRC32 {
        #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
        return try self.process(operation: COMPRESSION_STREAM_ENCODE, size: size, bufferSize: bufferSize,
                                provider: provider, consumer: consumer)
        #else
        return try self.encode(size: size, bufferSize: bufferSize, provider: provider, consumer: consumer)
        #endif
    }

    /// Decompress the output of `provider` and pass it to `consumer`.
    /// - Parameters:
    ///   - size: The compressed size of the data to be decompressed.
    ///   - bufferSize: The maximum size of the decompression buffer.
    ///   - skipCRC32: Optional flag to skip calculation of the CRC32 checksum to improve performance.
    ///   - provider: A closure that accepts a position and a chunk size. Returns a `Data` chunk.
    ///   - consumer: A closure that processes the result of the decompress operation.
    /// - Returns: The checksum of the processed content.
    public static func decompress(size: Int, bufferSize: Int, skipCRC32: Bool,
                                  provider: Provider, consumer: Consumer) throws -> CRC32 {
        #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
        return try self.process(operation: COMPRESSION_STREAM_DECODE, size: size, bufferSize: bufferSize,
                                skipCRC32: skipCRC32, provider: provider, consumer: consumer)
        #else
        return try self.decode(bufferSize: bufferSize, skipCRC32: skipCRC32, provider: provider, consumer: consumer)
        #endif
    }
}

// MARK: - Apple Platforms

#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
import Compression

extension Data {

    static func process(operation: compression_stream_operation, size: Int, bufferSize: Int, skipCRC32: Bool = false,
                        provider: Provider, consumer: Consumer) throws -> CRC32 {
        var crc32 = CRC32(0)
        let destPointer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
        defer { destPointer.deallocate() }
        let streamPointer = UnsafeMutablePointer<compression_stream>.allocate(capacity: 1)
        defer { streamPointer.deallocate() }
        var stream = streamPointer.pointee
        var status = compression_stream_init(&stream, operation, COMPRESSION_ZLIB)
        guard status != COMPRESSION_STATUS_ERROR else { throw CompressionError.invalidStream }
        defer { compression_stream_destroy(&stream) }
        stream.src_size = 0
        stream.dst_ptr = destPointer
        stream.dst_size = bufferSize
        var position = 0
        var sourceData: Data?
        repeat {
            if stream.src_size == 0 {
                do {
                    sourceData = try provider(position, Swift.min((size - position), bufferSize))
                    position += stream.prepare(for: sourceData)
                } catch { throw error }
            }
            if let sourceData = sourceData {
                sourceData.withUnsafeBytes { (rawBufferPointer) in
                    if let baseAddress = rawBufferPointer.baseAddress {
                        let pointer = baseAddress.assumingMemoryBound(to: UInt8.self)
                        stream.src_ptr = pointer.advanced(by: sourceData.count - stream.src_size)
                        let flags = sourceData.count < bufferSize ? Int32(COMPRESSION_STREAM_FINALIZE.rawValue) : 0
                        status = compression_stream_process(&stream, flags)
                    }
                }
                if operation == COMPRESSION_STREAM_ENCODE && !skipCRC32 { crc32 = sourceData.crc32(checksum: crc32) }
            }
            switch status {
            case COMPRESSION_STATUS_OK, COMPRESSION_STATUS_END:
                let outputData = Data(bytesNoCopy: destPointer, count: bufferSize - stream.dst_size, deallocator: .none)
                try consumer(outputData)
                if operation == COMPRESSION_STREAM_DECODE && !skipCRC32 { crc32 = outputData.crc32(checksum: crc32) }
                stream.dst_ptr = destPointer
                stream.dst_size = bufferSize
            default: throw CompressionError.corruptedData
            }
        } while status == COMPRESSION_STATUS_OK
        return crc32
    }
}

private extension compression_stream {

    mutating func prepare(for sourceData: Data?) -> Int {
        guard let sourceData = sourceData else { return 0 }

        self.src_size = sourceData.count
        return sourceData.count
    }
}

// MARK: - Linux

#else
import CZlib

extension Data {
    static func encode(size: Int, bufferSize: Int, provider: Provider, consumer: Consumer) throws -> CRC32 {
        var stream = z_stream()
        let streamSize = Int32(MemoryLayout<z_stream>.size)
        var result = deflateInit2_(&stream, Z_DEFAULT_COMPRESSION,
                                   Z_DEFLATED, -MAX_WBITS, 9, Z_DEFAULT_STRATEGY, ZLIB_VERSION, streamSize)
        defer { deflateEnd(&stream) }
        guard result == Z_OK else { throw CompressionError.invalidStream }
        var flush = Z_NO_FLUSH
        var position = 0
        var zipCRC32 = CRC32(0)
        repeat {
            let readSize = Swift.min((size - position), bufferSize)
            var inputChunk = try provider(position, readSize)
            zipCRC32 = inputChunk.crc32(checksum: zipCRC32)
            stream.avail_in = UInt32(inputChunk.count)
            try inputChunk.withUnsafeMutableBytes { (rawBufferPointer) in
                if let baseAddress = rawBufferPointer.baseAddress {
                    let pointer = baseAddress.assumingMemoryBound(to: UInt8.self)
                    stream.next_in = pointer
                    flush = position + bufferSize >= size ? Z_FINISH : Z_NO_FLUSH
                } else if rawBufferPointer.count > 0 {
                    throw CompressionError.corruptedData
                } else {
                    stream.next_in = nil
                    flush = Z_FINISH
                }
                var outputChunk = Data(count: bufferSize)
                repeat {
                    stream.avail_out = UInt32(bufferSize)
                    try outputChunk.withUnsafeMutableBytes { (rawBufferPointer) in
                        guard let baseAddress = rawBufferPointer.baseAddress, rawBufferPointer.count > 0 else {
                            throw CompressionError.corruptedData
                        }
                        let pointer = baseAddress.assumingMemoryBound(to: UInt8.self)
                        stream.next_out = pointer
                        result = deflate(&stream, flush)
                    }
                    guard result >= Z_OK else { throw CompressionError.corruptedData }

                    outputChunk.count = bufferSize - Int(stream.avail_out)
                    try consumer(outputChunk)
                } while stream.avail_out == 0
            }
            position += readSize
        } while flush != Z_FINISH
        return zipCRC32
    }

    static func decode(bufferSize: Int, skipCRC32: Bool, provider: Provider, consumer: Consumer) throws -> CRC32 {
        var stream = z_stream()
        let streamSize = Int32(MemoryLayout<z_stream>.size)
        var result = inflateInit2_(&stream, -MAX_WBITS, ZLIB_VERSION, streamSize)
        defer { inflateEnd(&stream) }
        guard result == Z_OK else { throw CompressionError.invalidStream }
        var unzipCRC32 = CRC32(0)
        var position = 0
        repeat {
            stream.avail_in = UInt32(bufferSize)
            var chunk = try provider(position, bufferSize)
            position += chunk.count
            try chunk.withUnsafeMutableBytes { (rawBufferPointer) in
                if let baseAddress = rawBufferPointer.baseAddress, rawBufferPointer.count > 0 {
                    let pointer = baseAddress.assumingMemoryBound(to: UInt8.self)
                    stream.next_in = pointer
                    repeat {
                        var outputData = Data(count: bufferSize)
                        stream.avail_out = UInt32(bufferSize)
                        try outputData.withUnsafeMutableBytes { (rawBufferPointer) in
                            if let baseAddress = rawBufferPointer.baseAddress, rawBufferPointer.count > 0 {
                                let pointer = baseAddress.assumingMemoryBound(to: UInt8.self)
                                stream.next_out = pointer
                            } else {
                                throw CompressionError.corruptedData
                            }
                            result = inflate(&stream, Z_NO_FLUSH)
                            guard result != Z_NEED_DICT &&
                                result != Z_DATA_ERROR &&
                                result != Z_MEM_ERROR else {
                                    throw CompressionError.corruptedData
                            }
                        }
                        let remainingLength = UInt32(bufferSize) - stream.avail_out
                        outputData.count = Int(remainingLength)
                        try consumer(outputData)
                        if !skipCRC32 { unzipCRC32 = outputData.crc32(checksum: unzipCRC32) }
                    } while stream.avail_out == 0
                }
            }
        } while result != Z_STREAM_END
        return unzipCRC32
    }
}

#endif

/// The lookup table used to calculate `CRC32` checksums when using the built-in
/// CRC32 implementation.
private let crcTable: [CRC32] = [
    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
    0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
    0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
