                 }
 
                 if uncompressedSize == 0 { throw LZMAError.ExceededUncompressedSize }
-                if rep0 >= dictionarySize || !outWindow.check(distance: rep0) { throw LZMAError.NotEnoughToRepeat }
+                if rep0 >= dictionarySize || !self.check(distance: rep0) { throw LZMAError.NotEnoughToRepeat }
             }
             // Converting from zero-based length of the match to the real one.
             len += LZMAConstants.matchMinLen
             if uncompressedSize > -1 && uncompressedSize < len { throw LZMAError.RepeatWillExceed }
-            outWindow.copyMatch(at: rep0 + 1, length: len, &out, &outIndex, &uncompressedSize)
+            self.copyMatch(at: rep0 + 1, length: len, &out, &outIndex, &uncompressedSize)
         }
 
         return out
     }
 
+    // MARK: LZMAOutWindow
+    private var byteBuffer: [UInt8]
+    private var position: Int = 0
+    private var size: Int
+    private var isFull: Bool = false
+
+    private(set) var totalPosition: Int = 0
+
+    var isEmpty: Bool {
+        return self.position == 0 && !self.isFull
+    }
+
+    func put(_ byte: UInt8) {
+        self.totalPosition += 1
+        self.byteBuffer[position] = byte
+        self.position += 1
+        if self.position == self.size {
+            self.position = 0
+            self.isFull = true
+        }
+    }
+
+    func put(_ byte: UInt8, _ out: inout [UInt8], _ outIndex: inout Int,
+             _ uncompressedSize: inout Int) {
+        self.put(byte)
+
+        if uncompressedSize > 0 {
+            out[outIndex] = byte
+            outIndex += 1
+        } else {
+            out.append(byte)
+        }
+        uncompressedSize -= 1
+    }
+
+    func byte(at distance: Int) -> UInt8 {
+        return self.byteBuffer[distance <= self.position ? self.position - distance :
+            self.size - distance + self.position]
+    }
+
+    func copyMatch(at distance: Int, length: Int, _ out: inout [UInt8], _ outIndex: inout Int,
+                   _ uncompressedSize: inout Int) {
+        for _ in 0..<length {
+            self.put(self.byte(at: distance), &out, &outIndex, &uncompressedSize)
+        }
+    }
+
+    func check(distance: Int) -> Bool {
+        return distance <= self.position || self.isFull
+    }
+
 }


    func resetDictionary(_ dictSize: Int) {
        self.dictionarySize = dictSize
        self.outWindow = LZMAOutWindow(dictSize: dictSize)
    }

    private func resetState() {


        self.rangeDecoder = LZMARangeDecoder()

        self.byteBuffer = Array(repeating: 0, count: dictionarySize)
        self.size = dictionarySize // TODO: remove self.size

        self.literalProbs = Array(repeating: Array(repeating: LZMAConstants.probInitValue,
                                                   count: 0x300),
