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

                    If there were none, i.e. it is the first literal, then this part is skipped.
                 - `lp` low bits from current position in output.
                 */
                let litState = ((outWindow.totalPosition & ((1 << lp.toInt()) - 1)) << lc.toInt()) + (prevByte >> (8 - lc)).toInt()
                // If state is greater than 7 we need to do additional decoding with 'matchByte'.
                if state >= 7 {
                    /**
                     Byte in output at position that is the `distance` bytes before current position,
                     where the `distance` is the distance from the latest decoded match.
                     */
                    var matchByte = outWindow.byte(at: rep0 + 1)
                    repeat {
                        let matchBit = ((matchByte >> 7) & 1).toInt()
                        matchByte <<= 1

                    symbol = (symbol << 1) | rangeDecoder.decode(bitWithProb: &literalProbs[litState][symbol])
                }
                let byte = (symbol - 0x100).toUInt8()
                self.put(byte, &out, &outIndex, &uncompressedSize)
                // END.

                // Finally, we need to update `state`.
