     }
 }
 
-class InflateZlib
+struct GZipHeader {
+    struct Extra {
+        let SI: (CChar, CChar)
+        let data: UnsafeBufferPointer<UInt8>
+    }
+    let mtime: NSDate?
+    let name: String?
+    let comment: String?
+    let extra: [Extra]
+
+    init(_ header: gz_header) {
+        if header.time != 0 {
+            mtime = NSDate(timeIntervalSince1970: NSTimeInterval(header.time))
+        }
+        if header.name != nil {
+            name = String.fromCString(UnsafePointer<CChar>(header.name))
+        }
+        if header.comment != nil {
+            comment = String.fromCString(UnsafePointer<CChar>(header.comment))
+        }
+        let N = Int(header.extra_len)
+        var X = [Extra]()
+        for var i = 4; i < N; () {
+            let LEN = Int(header.extra[i - 2]) + 256 * Int(header.extra[i - 1])
+            if i + LEN <= N {
+                let subField = Extra(
+                    SI: (CChar(header.extra[i - 4]), CChar(header.extra[i - 3])),
+                    data: UnsafeBufferPointer(start: header.extra.advancedBy(i), count: LEN)
+                )
+                X.append(subField)
+            }
+            i += 4 + LEN
+        }
+        extra = X
+    }
+}
+
+class Inflate
 {
-    private let raw: InflateBack
+    enum Header {
+        case None
+        case zlib
+        case gzip(GZipHeader)
+
+        static func make(header: gz_header) -> Header
+        {
+            if header.done ==  1 { return .gzip(GZipHeader(header)) }
+            if header.done == -1 { return .zlib }
+            return .None
+        }
+    }
+    let scratch = UnsafeMutablePointer<Bytef>.alloc(3 * 64 * 1024)
+    var stream = z_stream(next_in: nil, avail_in: 0, total_in: 0, next_out: nil, avail_out: 0, total_out: 0, msg: nil, state: nil, zalloc: nil, zfree: nil, opaque: nil, data_type: 0, adler: 0, reserved: 0)
 
     init()
     {
-        self.raw = InflateBack()
+        inflateInit2_(&stream, MAX_WBITS + 32, ZLIB_VERSION, Int32(sizeof(z_stream)))
     }
     deinit
     {
-        raw.dealloc()
-    }
-
-    func inflateTo(       sink: (UnsafeBufferPointer<UInt8>) -> Int32,
-                   From source: () -> UnsafeBufferPointer<UInt8>) -> (Int, Int)
-    {
-        // check zlib header
-        // see http://tools.ietf.org/html/rfc1950#page-4
-        let first  = source()
-        if first.count < 2 { return (0, 0) }
-
-        if (Int(first[0]) * 256 + Int(first[1])) % 31 != 0 { return (Int(Z_DATA_ERROR), 0) }
-
-        let CM     = first[0] & 0x0F
-        if CM != UInt8(Z_DEFLATED) { return (Int(Z_DATA_ERROR), 0) }
-
-        let CINFO  = first[0] >> 4
-        if CINFO > 7 { return (Int(Z_DATA_ERROR), 0) }
-
-        let FDICT  = first[1] & 0x20;
-        if FDICT != 0  { return (Int(Z_NEED_DICT), 0) }
-
-        return (0, 0) //raw.inflateTo(sink, From: source);
-    }
-}
+        inflateEnd(&stream)
+        scratch.dealloc(3 * 64 * 1024)
+    }
+
+    func inflateTo(           sink: (UnsafeBufferPointer<UInt8>) -> Int32,
+                   Header delegate: (Header) -> Int32,
+                       From source: () -> UnsafeBufferPointer<UInt8>) -> (Int, Int)
+    {
+        let extra   = UnsafeMutableBufferPointer<Bytef>(start: scratch.advancedBy(0*64*1024), count: 64*1024)
+        let name    = UnsafeMutableBufferPointer<Bytef>(start: scratch.advancedBy(1*64*1024), count: 64*1024)
+        let comment = UnsafeMutableBufferPointer<Bytef>(start: scratch.advancedBy(2*64*1024), count: 64*1024)
+        var hdr = gz_header(
+            text: 0,
+            time: 0,
+            xflags: 0,
+            os: 0,
+            extra: extra.baseAddress, extra_len: 0, extra_max: uInt(extra.count),
+            name: name.baseAddress, name_max: uInt(name.count - 1),
+            comment: comment.baseAddress, comm_max: uInt(comment.count - 1),
+            hcrc: 0, done: 0)
+
+        inflateReset(&stream)
+        inflateGetHeader(&stream, &hdr)
+        stream.next_out = scratch
+        stream.avail_out = 0
+        stream.avail_in = 0
+
+        for ( ; ; ()) {
+            if stream.avail_in == 0 {
+                let input = source()
+                let bytes = uInt(input.count)
+                if bytes == 0 { return (Int(Z_OK), Int(stream.total_in)) }
+
+                stream.next_in = UnsafeMutablePointer<Bytef>(input.baseAddress)
+                stream.avail_in = bytes
+            }
+            let zrc = inflate(&stream, Z_BLOCK)
+            switch zrc {
+            case Z_OK:
+                /* need more input */
+                continue;
+            case Z_BUF_ERROR:
+                if stream.total_out == 0 {
+                    name[name.count - 1] = 0
+                    comment[comment.count - 1] = 0
+                    let rc = delegate(Header.make(hdr))
+                    if rc != 0 { return (Int(rc), Int(stream.total_in)) }
+                }
+                else {
+                    let bytes = 64*1024 - stream.avail_out
+                    let data = UnsafePointer<UInt8>(scratch);
+                    let rc = sink(UnsafeBufferPointer<UInt8>(start: data, count: Int(bytes)))
+                    if rc != 0 { return (Int(rc), Int(stream.total_in)) }
+                }
+                stream.next_out = scratch
+                stream.avail_out = 64*1024
+            default:
+                return (Int(zrc), Int(stream.total_in))
+            }
+        }
+    }
+}

//

import Swift

private let MAX_WBYTES = Int(1 << MAX_WBITS)


    let window: UnsafeMutablePointer<UInt8>

    // inflate's state object
    var stream = z_stream(next_in: nil, avail_in: 0, total_in: 0, next_out: nil, avail_out: 0, total_out: 0, msg: nil, state: nil, zalloc: nil, zfree: nil, opaque: nil, data_type: 0, adler: 0, reserved: 0)

    init()
    {
        window = UnsafeMutablePointer<UInt8>.alloc(MAX_WBYTES)
        inflateBackInitialize(&stream, MAX_WBITS, window);
    }

    mutating func dealloc() -> ()
    {
        inflateBackEnd(&stream)
        window.dealloc(MAX_WBYTES)
    }

    mutating func inflateTo(       sink: (UnsafeBufferPointer<UInt8>) -> Int32,
                   From source: () -> UnsafeBufferPointer<UInt8>) -> (Int, Int)
    {
        stream.total_in = 0
        let rc = inflateBackHelper(&stream,
            { let data = source(); $0[0] = data.baseAddress; return UInt32(data.count) },
            { sink(UnsafeBufferPointer(start: $1, count: Int($0))) }
        )
        return (Int(rc), Int(stream.total_in))
    }
    
    mutating func inflateTo(       sink: (UnsafeBufferPointer<UInt8>) -> Int32,
                   From source:  UnsafeBufferPointer<UInt8>) -> (Int, Int)
    {
        stream.total_in = 0
        let rc = inflateBackHelper(&stream,
            { $0[0] = source.baseAddress; return UInt32(source.count) },
            { sink(UnsafeBufferPointer(start: $1, count: Int($0))) }
        )
        return (Int(rc), Int(stream.total_in))
    }
}

class InflateRaw
{
    private var raw: InflateBack

    init()
    {
