 private let MAX_WBYTES = Int(1 << MAX_WBITS)
 
 //// TODO: enum for inflate error codes
+
+private struct ZStream : DebugPrintable
+{
+    var stream: z_stream
+
+    init()
+    {
+        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);
+    }
+    var debugDescription: String
+    {
+        let msg = stream.msg != nil ? String.fromCString(stream.msg) : "";
+        return "avail_in: \(stream.avail_in); avail_out: \(stream.avail_out); total_in: \(stream.total_in); total_out: \(stream.total_out); data_type: \(stream.data_type); checksum: \(stream.adler); msg: '\(msg)'"
+    }
+    mutating func InflateInit(wbits: Int32) -> Int32
+    {
+        return inflateInit2_(&stream, wbits, ZLIB_VERSION, Int32(sizeof(z_stream)))
+    }
+    mutating func Inflate(flush: Int32) -> Int32
+    {
+        return inflate(&stream, flush)
+    }
+    mutating func getHeader(head: gz_headerp) -> Int32
+    {
+        return inflateGetHeader(&stream, head)
+    }
+    mutating func InflateEnd() -> Int32
+    {
+        return inflateEnd(&stream)
+    }
+    mutating func InflateReset() -> Int32
+    {
+        return inflateReset(&stream)
+    }
+    var totalIn: Int {
+        get {
+            return Int(stream.total_in)
+        }
+        set (y) {
+            stream.total_in = uLong(y)
+        }
+    }
+    var totalOut: Int {
+        get {
+            return Int(stream.total_out)
+        }
+        set (y) {
+            stream.total_out = uLong(y)
+        }
+    }
+    var hasInput: Bool {
+        return stream.avail_in != 0
+    }
+    var input: UnsafeBufferPointer<UInt8> {
+        get {
+            return UnsafeBufferPointer(start: UnsafePointer<UInt8>(stream.next_in), count: Int(stream.avail_in))
+        }
+        set(newValue) {
+            stream.next_in = UnsafeMutablePointer<Bytef>(newValue.baseAddress)
+            stream.avail_in = uInt(newValue.count)
+        }
+    }
+    var availOut: Int { return Int(stream.avail_out) }
+    mutating func setOutput(buffer: UnsafeMutablePointer<UInt8>, size: Int)
+    {
+        stream.next_out = UnsafeMutablePointer<Bytef>(buffer)
+        stream.avail_out = uInt(size)
+    }
+    mutating func noOutput()
+    {
+        let bogus = UnsafePointer<Void>(bitPattern: 0xDEADBEEF)
+        setOutput(UnsafeMutablePointer<UInt8>(bogus), size: 0)
+    }
+    mutating func noInput()
+    {
+        stream.next_in = UnsafeMutablePointer<Bytef>(nil)
+        stream.avail_in = 0
+    }
+}
 
 // callback based inflate
 private struct InflateBack

                   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))
            }
        }
    }
}

            return .None
        }
    }
    private let scratch = UnsafeMutablePointer<Bytef>.alloc(3 * 64 * 1024)
    private var stream = ZStream()

    init()
    {
        stream.InflateInit(MAX_WBITS + 32)
    }
    deinit
    {
        stream.InflateEnd()
        scratch.dealloc(3 * 64 * 1024)
    }

