commonMain.korlibs.compression.lzma.SevenZip.kt Maven / Gradle / Ivy
The newest version!
package korlibs.compression.lzma
import kotlin.math.*
@ExperimentalStdlibApi
object SevenZip {
interface ICodeProgress {
fun setProgress(inSize: Long, outSize: Long)
}
class BitTreeEncoder(private var numBitLevels: Int) {
private val models: ShortArray = ShortArray(1 shl numBitLevels)
fun init() {
RangeDecoder.initBitModels(models)
}
fun encode(rangeEncoder: RangeEncoder, symbol: Int) {
var m = 1
var bitIndex = numBitLevels
while (bitIndex != 0) {
bitIndex--
val bit = symbol.ushr(bitIndex) and 1
rangeEncoder.encode(models, m, bit)
m = m shl 1 or bit
}
}
fun reverseEncode(rangeEncoder: RangeEncoder, symbol: Int) {
var symbol = symbol
var m = 1
for (i in 0 until numBitLevels) {
val bit = symbol and 1
rangeEncoder.encode(models, m, bit)
m = m shl 1 or bit
symbol = symbol shr 1
}
}
fun getPrice(symbol: Int): Int {
var price = 0
var m = 1
var bitIndex = numBitLevels
while (bitIndex != 0) {
bitIndex--
val bit = symbol.ushr(bitIndex) and 1
price += RangeEncoder.getPrice(
models[m].toInt(),
bit
)
m = (m shl 1) + bit
}
return price
}
fun reverseGetPrice(symbol: Int): Int {
var symbol = symbol
var price = 0
var m = 1
for (i in numBitLevels downTo 1) {
val bit = symbol and 1
symbol = symbol ushr 1
price += RangeEncoder.getPrice(
models[m].toInt(),
bit
)
m = m shl 1 or bit
}
return price
}
companion object {
fun reverseGetPrice(
Models: ShortArray, startIndex: Int,
NumBitLevels: Int, symbol: Int
): Int {
var symbol = symbol
var price = 0
var m = 1
for (i in NumBitLevels downTo 1) {
val bit = symbol and 1
symbol = symbol ushr 1
price += RangeEncoder.getPrice(
Models[startIndex + m].toInt(),
bit
)
m = m shl 1 or bit
}
return price
}
fun reverseEncode(
Models: ShortArray, startIndex: Int,
rangeEncoder: RangeEncoder, NumBitLevels: Int, symbol: Int
) {
var symbol = symbol
var m = 1
for (i in 0 until NumBitLevels) {
val bit = symbol and 1
rangeEncoder.encode(Models, startIndex + m, bit)
m = m shl 1 or bit
symbol = symbol shr 1
}
}
}
}
class BitTreeDecoder(private var numBitLevels: Int) {
private val models: ShortArray = ShortArray(1 shl numBitLevels)
fun init() {
RangeDecoder.initBitModels(models)
}
fun decode(rangeDecoder: RangeDecoder): Int {
var m = 1
for (bitIndex in numBitLevels downTo 1)
m = (m shl 1) + rangeDecoder.decodeBit(models, m)
return m - (1 shl numBitLevels)
}
fun reverseDecode(rangeDecoder: RangeDecoder): Int {
var m = 1
var symbol = 0
for (bitIndex in 0 until numBitLevels) {
val bit = rangeDecoder.decodeBit(models, m)
m = m shl 1
m += bit
symbol = symbol or (bit shl bitIndex)
}
return symbol
}
companion object {
fun reverseDecode(
Models: ShortArray, startIndex: Int,
rangeDecoder: RangeDecoder, NumBitLevels: Int
): Int {
var m = 1
var symbol = 0
for (bitIndex in 0 until NumBitLevels) {
val bit = rangeDecoder.decodeBit(Models, startIndex + m)
m = m shl 1
m += bit
symbol = symbol or (bit shl bitIndex)
}
return symbol
}
}
}
class RangeDecoder {
internal var range: Int = 0
internal var code: Int = 0
internal var stream: LzmaInputStream? = null
fun setStream(stream: LzmaInputStream) {
this.stream = stream
}
fun releaseStream() {
stream = null
}
fun init() {
code = 0
range = -1
for (i in 0..4)
code = code shl 8 or stream!!.read()
}
fun decodeDirectBits(numTotalBits: Int): Int {
var result = 0
for (i in numTotalBits downTo 1) {
range = range ushr 1
val t = (code - range).ushr(31)
code -= range and t - 1
result = result shl 1 or 1 - t
if (range and kTopMask == 0) {
code = code shl 8 or stream!!.read()
range = range shl 8
}
}
return result
}
fun decodeBit(probs: ShortArray, index: Int): Int {
val prob = probs[index].toInt()
val newBound = range.ushr(kNumBitModelTotalBits) * prob
if (code xor -0x80000000 < newBound xor -0x80000000) {
range = newBound
probs[index] = (prob + (kBitModelTotal - prob).ushr(
kNumMoveBits
)).toShort()
if (range and kTopMask == 0) {
code = code shl 8 or stream!!.read()
range = range shl 8
}
return 0
} else {
range -= newBound
code -= newBound
probs[index] = (prob - prob.ushr(kNumMoveBits)).toShort()
if (range and kTopMask == 0) {
code = code shl 8 or stream!!.read()
range = range shl 8
}
return 1
}
}
companion object {
internal const val kTopMask = ((1 shl 24) - 1).inv()
internal const val kNumBitModelTotalBits = 11
internal const val kBitModelTotal = 1 shl kNumBitModelTotalBits
internal const val kNumMoveBits = 5
fun initBitModels(probs: ShortArray) {
for (i in probs.indices)
probs[i] = kBitModelTotal.ushr(1).toShort()
}
}
}
class RangeEncoder {
internal var stream: LzmaOutputStream? = null
private var low: Long = 0
internal var range: Int = 0
private var _cacheSize: Int = 0
private var _cache: Int = 0
private var _position: Long = 0
fun setStream(stream: LzmaOutputStream) {
this.stream = stream
}
fun releaseStream() {
stream = null
}
fun init() {
_position = 0
low = 0
range = -1
_cacheSize = 1
_cache = 0
}
fun flushData() {
for (i in 0..4)
shiftLow()
}
fun flushStream() {
stream!!.flush()
}
private fun shiftLow() {
val lowHi = low.ushr(32).toInt()
if (lowHi != 0 || low < 0xFF000000L) {
_position += _cacheSize.toLong()
var temp = _cache
do {
stream!!.write8(temp + lowHi)
temp = 0xFF
} while (--_cacheSize != 0)
_cache = low.toInt().ushr(24)
}
_cacheSize++
low = low and 0xFFFFFF shl 8
}
fun encodeDirectBits(v: Int, numTotalBits: Int) {
for (i in numTotalBits - 1 downTo 0) {
range = range ushr 1
if (v.ushr(i) and 1 == 1)
low += range.toLong()
if (range and kTopMask == 0) {
range = range shl 8
shiftLow()
}
}
}
fun getProcessedSizeAdd(): Long {
return _cacheSize.toLong() + _position + 4
}
fun encode(probs: ShortArray, index: Int, symbol: Int) {
val prob = probs[index].toInt()
val newBound = range.ushr(kNumBitModelTotalBits) * prob
if (symbol == 0) {
range = newBound
probs[index] = (prob + (kBitModelTotal - prob).ushr(
kNumMoveBits
)).toShort()
} else {
low += newBound and 0xFFFFFFFFL.toInt()
range -= newBound
probs[index] = (prob - prob.ushr(kNumMoveBits)).toShort()
}
if (range and kTopMask == 0) {
range = range shl 8
shiftLow()
}
}
companion object {
internal const val kTopMask = ((1 shl 24) - 1).inv()
internal const val kNumBitModelTotalBits = 11
internal const val kBitModelTotal = 1 shl kNumBitModelTotalBits
internal const val kNumMoveBits = 5
private const val kNumMoveReducingBits = 2
const val kNumBitPriceShiftBits = 6
fun initBitModels(probs: ShortArray) {
for (i in probs.indices) probs[i] = kBitModelTotal.ushr(1).toShort()
}
private val probPrices = IntArray(kBitModelTotal ushr kNumMoveReducingBits)
init {
val kNumBits = kNumBitModelTotalBits - kNumMoveReducingBits
for (i in kNumBits - 1 downTo 0) {
val start = 1 shl kNumBits - i - 1
val end = 1 shl kNumBits - i
for (j in start until end)
probPrices[j] = (i shl kNumBitPriceShiftBits) +
(end - j shl kNumBitPriceShiftBits).ushr(kNumBits - i - 1)
}
}
fun getPrice(Prob: Int, symbol: Int): Int = probPrices[(Prob - symbol xor -symbol and kBitModelTotal - 1).ushr(kNumMoveReducingBits)]
fun getPrice0(Prob: Int): Int = probPrices[Prob ushr kNumMoveReducingBits]
fun getPrice1(Prob: Int): Int = probPrices[(kBitModelTotal - Prob).ushr(kNumMoveReducingBits)]
}
}
object LzmaBase {
const val kNumRepDistances = 4
const val kNumStates = 12
const val kNumPosSlotBits = 6
const val kDicLogSizeMin = 0
// public static final int kDicLogSizeMax = 28;
// public static final int kDistTableSizeMax = kDicLogSizeMax * 2;
const val kNumLenToPosStatesBits = 2 // it's for speed optimization
const val kNumLenToPosStates = 1 shl kNumLenToPosStatesBits
const val kMatchMinLen = 2
const val kNumAlignBits = 4
const val kAlignTableSize = 1 shl kNumAlignBits
const val kAlignMask = kAlignTableSize - 1
const val kStartPosModelIndex = 4
const val kEndPosModelIndex = 14
//const val kNumPosModels = kEndPosModelIndex - kStartPosModelIndex
const val kNumFullDistances = 1 shl kEndPosModelIndex / 2
const val kNumLitPosStatesBitsEncodingMax = 4
const val kNumLitContextBitsMax = 8
const val kNumPosStatesBitsMax = 4
const val kNumPosStatesMax = 1 shl kNumPosStatesBitsMax
const val kNumPosStatesBitsEncodingMax = 4
const val kNumPosStatesEncodingMax = 1 shl kNumPosStatesBitsEncodingMax
const val kNumLowLenBits = 3
const val kNumMidLenBits = 3
const val kNumHighLenBits = 8
const val kNumLowLenSymbols = 1 shl kNumLowLenBits
const val kNumMidLenSymbols = 1 shl kNumMidLenBits
const val kNumLenSymbols = kNumLowLenSymbols + kNumMidLenSymbols + (1 shl kNumHighLenBits)
const val kMatchMaxLen = kMatchMinLen + kNumLenSymbols - 1
fun stateInit(): Int = 0
fun stateUpdateChar(index: Int): Int = when {
index < 4 -> 0
index < 10 -> index - 3
else -> index - 6
}
fun stateUpdateMatch(index: Int): Int = if (index < 7) 7 else 10
fun stateUpdateRep(index: Int): Int = if (index < 7) 8 else 11
fun stateUpdateShortRep(index: Int): Int = if (index < 7) 9 else 11
fun stateIsCharState(index: Int): Boolean = index < 7
fun getLenToPosState(len: Int): Int {
var len = len
len -= kMatchMinLen
return if (len < kNumLenToPosStates) len else kNumLenToPosStates - 1
}
}
class LzmaDecoder {
private var m_OutWindow = LzOutWindow()
private var m_RangeDecoder = RangeDecoder()
private var m_IsMatchDecoders = ShortArray(LzmaBase.kNumStates shl LzmaBase.kNumPosStatesBitsMax)
private var m_IsRepDecoders = ShortArray(LzmaBase.kNumStates)
private var m_IsRepG0Decoders = ShortArray(LzmaBase.kNumStates)
private var m_IsRepG1Decoders = ShortArray(LzmaBase.kNumStates)
private var m_IsRepG2Decoders = ShortArray(LzmaBase.kNumStates)
private var m_IsRep0LongDecoders = ShortArray(LzmaBase.kNumStates shl LzmaBase.kNumPosStatesBitsMax)
private var m_PosSlotDecoder = arrayOfNulls(LzmaBase.kNumLenToPosStates)
private var m_PosDecoders = ShortArray(LzmaBase.kNumFullDistances - LzmaBase.kEndPosModelIndex)
private var m_PosAlignDecoder = BitTreeDecoder(LzmaBase.kNumAlignBits)
private var m_LenDecoder = LenDecoder()
private var m_RepLenDecoder = LenDecoder()
private var m_LiteralDecoder = LiteralDecoder()
private var m_DictionarySize = -1
private var m_DictionarySizeCheck = -1
private var m_PosStateMask: Int = 0
internal inner class LenDecoder {
private var m_Choice = ShortArray(2)
private var m_LowCoder = arrayOfNulls(LzmaBase.kNumPosStatesMax)
private var m_MidCoder = arrayOfNulls(LzmaBase.kNumPosStatesMax)
private var m_HighCoder = BitTreeDecoder(LzmaBase.kNumHighLenBits)
private var m_NumPosStates = 0
fun create(numPosStates: Int) {
while (m_NumPosStates < numPosStates) {
m_LowCoder[m_NumPosStates] = BitTreeDecoder(LzmaBase.kNumLowLenBits)
m_MidCoder[m_NumPosStates] = BitTreeDecoder(LzmaBase.kNumMidLenBits)
m_NumPosStates++
}
}
fun init() {
RangeDecoder.initBitModels(m_Choice)
for (posState in 0 until m_NumPosStates) {
m_LowCoder[posState]!!.init()
m_MidCoder[posState]!!.init()
}
m_HighCoder.init()
}
fun decode(rangeDecoder: RangeDecoder, posState: Int): Int {
if (rangeDecoder.decodeBit(m_Choice, 0) == 0) return m_LowCoder[posState]!!.decode(rangeDecoder)
var symbol = LzmaBase.kNumLowLenSymbols
symbol += if (rangeDecoder.decodeBit(m_Choice, 1) == 0) m_MidCoder[posState]!!.decode(rangeDecoder) else LzmaBase.kNumMidLenSymbols + m_HighCoder.decode(rangeDecoder)
return symbol
}
}
internal inner class LiteralDecoder {
private var m_Coders: Array? = null
private var m_NumPrevBits: Int = 0
private var m_NumPosBits: Int = 0
private var m_PosMask: Int = 0
internal inner class Decoder2 {
private var m_Decoders = ShortArray(0x300)
fun init() {
RangeDecoder.initBitModels(
m_Decoders
)
}
fun decodeNormal(rangeDecoder: RangeDecoder): Byte {
var symbol = 1
do { symbol = symbol shl 1 or rangeDecoder.decodeBit(m_Decoders, symbol) } while (symbol < 0x100)
return symbol.toByte()
}
fun decodeWithMatchByte(rangeDecoder: RangeDecoder, matchByte: Byte): Byte {
var matchByte = matchByte
var symbol = 1
do {
val matchBit = (matchByte shr 7) and 1
matchByte = ((matchByte shl 1).toByte())
val bit = rangeDecoder.decodeBit(m_Decoders, (1 + matchBit shl 8) + symbol)
symbol = symbol shl 1 or bit
if (matchBit != bit) {
while (symbol < 0x100) symbol = symbol shl 1 or rangeDecoder.decodeBit(m_Decoders, symbol)
break
}
} while (symbol < 0x100)
return symbol.toByte()
}
}
fun create(numPosBits: Int, numPrevBits: Int) {
if (m_Coders != null && m_NumPrevBits == numPrevBits && m_NumPosBits == numPosBits) return
m_NumPosBits = numPosBits
m_PosMask = (1 shl numPosBits) - 1
m_NumPrevBits = numPrevBits
val numStates = 1 shl m_NumPrevBits + m_NumPosBits
m_Coders = Array(numStates) { Decoder2() }
}
fun init() {
val numStates = 1 shl m_NumPrevBits + m_NumPosBits
for (i in 0 until numStates)
m_Coders!![i].init()
}
fun getDecoder(pos: Int, prevByte: Byte): Decoder2 = m_Coders!![(pos and m_PosMask shl m_NumPrevBits) + (prevByte and 0xFF).ushr(8 - m_NumPrevBits)]
}
init {
for (i in 0 until LzmaBase.kNumLenToPosStates) m_PosSlotDecoder[i] = BitTreeDecoder(LzmaBase.kNumPosSlotBits)
}
private fun setDictionarySize(dictionarySize: Int): Boolean {
if (dictionarySize < 0) return false
if (m_DictionarySize != dictionarySize) {
m_DictionarySize = dictionarySize
m_DictionarySizeCheck = max(m_DictionarySize, 1)
m_OutWindow.create(max(m_DictionarySizeCheck, 1 shl 12))
}
return true
}
private fun setLcLpPb(lc: Int, lp: Int, pb: Int): Boolean {
if (lc > LzmaBase.kNumLitContextBitsMax || lp > 4 || pb > LzmaBase.kNumPosStatesBitsMax) return false
m_LiteralDecoder.create(lp, lc)
val numPosStates = 1 shl pb
m_LenDecoder.create(numPosStates)
m_RepLenDecoder.create(numPosStates)
m_PosStateMask = numPosStates - 1
return true
}
internal fun init() {
m_OutWindow.init(false)
RangeDecoder.initBitModels(m_IsMatchDecoders)
RangeDecoder.initBitModels(m_IsRep0LongDecoders)
RangeDecoder.initBitModels(m_IsRepDecoders)
RangeDecoder.initBitModels(m_IsRepG0Decoders)
RangeDecoder.initBitModels(m_IsRepG1Decoders)
RangeDecoder.initBitModels(m_IsRepG2Decoders)
RangeDecoder.initBitModels(m_PosDecoders)
m_LiteralDecoder.init()
for (i in 0 until LzmaBase.kNumLenToPosStates) m_PosSlotDecoder[i]!!.init()
m_LenDecoder.init()
m_RepLenDecoder.init()
m_PosAlignDecoder.init()
m_RangeDecoder.init()
}
fun code(inStream: LzmaInputStream, outStream: LzmaOutputStream, outSize: Long): Boolean {
m_RangeDecoder.setStream(inStream)
m_OutWindow.setStream(outStream)
init()
var state = LzmaBase.stateInit()
var rep0 = 0
var rep1 = 0
var rep2 = 0
var rep3 = 0
var nowPos64: Long = 0
var prevByte: Byte = 0
while (outSize < 0 || nowPos64 < outSize) {
val posState = nowPos64.toInt() and m_PosStateMask
if (m_RangeDecoder.decodeBit(m_IsMatchDecoders, (state shl LzmaBase.kNumPosStatesBitsMax) + posState) == 0) {
val decoder2 = m_LiteralDecoder.getDecoder(nowPos64.toInt(), prevByte)
prevByte = if (!LzmaBase.stateIsCharState(state))
decoder2.decodeWithMatchByte(m_RangeDecoder, m_OutWindow.getByte(rep0))
else
decoder2.decodeNormal(m_RangeDecoder)
m_OutWindow.putByte(prevByte)
state = LzmaBase.stateUpdateChar(state)
nowPos64++
} else {
var len: Int
if (m_RangeDecoder.decodeBit(m_IsRepDecoders, state) == 1) {
len = 0
if (m_RangeDecoder.decodeBit(m_IsRepG0Decoders, state) == 0) {
if (m_RangeDecoder.decodeBit(
m_IsRep0LongDecoders,
(state shl LzmaBase.kNumPosStatesBitsMax) + posState
) == 0
) {
state = LzmaBase.stateUpdateShortRep(
state
)
len = 1
}
} else {
val distance: Int
if (m_RangeDecoder.decodeBit(m_IsRepG1Decoders, state) == 0)
distance = rep1
else {
if (m_RangeDecoder.decodeBit(m_IsRepG2Decoders, state) == 0)
distance = rep2
else {
distance = rep3
rep3 = rep2
}
rep2 = rep1
}
rep1 = rep0
rep0 = distance
}
if (len == 0) {
len = m_RepLenDecoder.decode(m_RangeDecoder, posState) +
LzmaBase.kMatchMinLen
state = LzmaBase.stateUpdateRep(state)
}
} else {
rep3 = rep2
rep2 = rep1
rep1 = rep0
len = LzmaBase.kMatchMinLen + m_LenDecoder.decode(m_RangeDecoder, posState)
state = LzmaBase.stateUpdateMatch(state)
val posSlot = m_PosSlotDecoder[LzmaBase.getLenToPosState(
len
)]!!.decode(m_RangeDecoder)
if (posSlot >= LzmaBase.kStartPosModelIndex) {
val numDirectBits = (posSlot shr 1) - 1
rep0 = 2 or (posSlot and 1) shl numDirectBits
if (posSlot < LzmaBase.kEndPosModelIndex)
rep0 += BitTreeDecoder.reverseDecode(
m_PosDecoders,
rep0 - posSlot - 1, m_RangeDecoder, numDirectBits
)
else {
rep0 += m_RangeDecoder.decodeDirectBits(
numDirectBits - LzmaBase.kNumAlignBits
) shl LzmaBase.kNumAlignBits
rep0 += m_PosAlignDecoder.reverseDecode(m_RangeDecoder)
if (rep0 < 0) {
if (rep0 == -1)
break
return false
}
}
} else
rep0 = posSlot
}
if (rep0 >= nowPos64 || rep0 >= m_DictionarySizeCheck) {
// m_OutWindow.Flush();
return false
}
m_OutWindow.copyBlock(rep0, len)
nowPos64 += len.toLong()
prevByte = m_OutWindow.getByte(0)
}
}
m_OutWindow.flush()
m_OutWindow.releaseStream()
m_RangeDecoder.releaseStream()
return true
}
fun setDecoderProperties(properties: ByteArray): Boolean {
if (properties.size < 5) return false
val `val` = properties[0] and 0xFF
val lc = `val` % 9
val remainder = `val` / 9
val lp = remainder % 5
val pb = remainder / 5
var dictionarySize = 0
for (i in 0..3) dictionarySize += properties[1 + i].toInt() and 0xFF shl i * 8
return if (!setLcLpPb(lc, lp, pb)) false else setDictionarySize(dictionarySize)
}
}
class LzmaEncoder {
private var _state = LzmaBase.stateInit()
private var _previousByte: Byte = 0
private var _repDistances = IntArray(LzmaBase.kNumRepDistances)
private var _optimum = Array(kNumOpts) { Optimal() }
private var _matchFinder: LzBinTree? = null
private var _rangeEncoder = RangeEncoder()
private var _isMatch = ShortArray(LzmaBase.kNumStates shl LzmaBase.kNumPosStatesBitsMax)
private var _isRep = ShortArray(LzmaBase.kNumStates)
private var _isRepG0 = ShortArray(LzmaBase.kNumStates)
private var _isRepG1 = ShortArray(LzmaBase.kNumStates)
private var _isRepG2 = ShortArray(LzmaBase.kNumStates)
private var _isRep0Long = ShortArray(LzmaBase.kNumStates shl LzmaBase.kNumPosStatesBitsMax)
private var _posSlotEncoder = Array(LzmaBase.kNumLenToPosStates) { BitTreeEncoder(LzmaBase.kNumPosSlotBits) } // kNumPosSlotBits
private var _posEncoders = ShortArray(LzmaBase.kNumFullDistances - LzmaBase.kEndPosModelIndex)
private var _posAlignEncoder = BitTreeEncoder(LzmaBase.kNumAlignBits)
private var _lenEncoder = LenPriceTableEncoder()
private var _repMatchLenEncoder = LenPriceTableEncoder()
private var _literalEncoder = LiteralEncoder()
private var _matchDistances = IntArray(LzmaBase.kMatchMaxLen * 2 + 2)
private var _numFastBytes = kNumFastBytesDefault
private var _longestMatchLength: Int = 0
private var _numDistancePairs: Int = 0
private var _additionalOffset: Int = 0
private var _optimumEndIndex: Int = 0
private var _optimumCurrentIndex: Int = 0
private var _longestMatchWasFound: Boolean = false
private var _posSlotPrices = IntArray(1 shl LzmaBase.kNumPosSlotBits + LzmaBase.kNumLenToPosStatesBits)
private var _distancesPrices = IntArray(LzmaBase.kNumFullDistances shl LzmaBase.kNumLenToPosStatesBits)
private var _alignPrices = IntArray(LzmaBase.kAlignTableSize)
private var _alignPriceCount: Int = 0
private var _distTableSize = kDefaultDictionaryLogSize * 2
private var _posStateBits = 2
private var _posStateMask = 4 - 1
private var _numLiteralPosStateBits = 0
private var _numLiteralContextBits = 3
private var _dictionarySize = 1 shl kDefaultDictionaryLogSize
private var _dictionarySizePrev = -1
private var _numFastBytesPrev = -1
private var nowPos64: Long = 0
private var _finished: Boolean = false
private var _inStream: LzmaInputStream? = null
private var _matchFinderType = EMatchFinderTypeBT4
private var _writeEndMark = false
private var _needReleaseMFStream = false
private var reps = IntArray(LzmaBase.kNumRepDistances)
private var repLens = IntArray(LzmaBase.kNumRepDistances)
private var backRes: Int = 0
private var processedInSize = LongArray(1)
private var processedOutSize = LongArray(1)
private var finished = BooleanArray(1)
private var properties = ByteArray(kPropSize)
private var tempPrices = IntArray(LzmaBase.kNumFullDistances)
private var _matchPriceCount: Int = 0
private fun baseInit() {
_state = LzmaBase.stateInit()
_previousByte = 0
for (i in 0 until LzmaBase.kNumRepDistances) _repDistances[i] = 0
}
internal inner class LiteralEncoder {
private var m_Coders: Array? = null
private var m_NumPrevBits: Int = 0
private var m_NumPosBits: Int = 0
private var m_PosMask: Int = 0
internal inner class Encoder2 {
private var m_Encoders = ShortArray(0x300)
fun init() {
RangeEncoder.initBitModels(m_Encoders)
}
fun encode(rangeEncoder: RangeEncoder, symbol: Byte) {
var context = 1
for (i in 7 downTo 0) {
val bit = (symbol shr i) and 1
rangeEncoder.encode(m_Encoders, context, bit)
context = context shl 1 or bit
}
}
fun encodeMatched(rangeEncoder: RangeEncoder, matchByte: Byte, symbol: Byte) {
var context = 1
var same = true
for (i in 7 downTo 0) {
val bit = symbol shr i and 1
var state = context
if (same) {
val matchBit = matchByte shr i and 1
state += 1 + matchBit shl 8
same = matchBit == bit
}
rangeEncoder.encode(m_Encoders, state, bit)
context = context shl 1 or bit
}
}
fun getPrice(matchMode: Boolean, matchByte: Byte, symbol: Byte): Int {
var price = 0
var context = 1
var i = 7
if (matchMode) {
while (i >= 0) {
val matchBit = matchByte shr i and 1
val bit = symbol shr i and 1
price += RangeEncoder.getPrice(
m_Encoders[(1 + matchBit shl 8) + context].toInt(),
bit
)
context = context shl 1 or bit
if (matchBit != bit) {
i--
break
}
i--
}
}
while (i >= 0) {
val bit = symbol shr i and 1
price += RangeEncoder.getPrice(
m_Encoders[context].toInt(),
bit
)
context = context shl 1 or bit
i--
}
return price
}
}
fun create(numPosBits: Int, numPrevBits: Int) {
if (m_Coders != null && m_NumPrevBits == numPrevBits && m_NumPosBits == numPosBits)
return
m_NumPosBits = numPosBits
m_PosMask = (1 shl numPosBits) - 1
m_NumPrevBits = numPrevBits
val numStates = 1 shl m_NumPrevBits + m_NumPosBits
m_Coders = Array(numStates) { Encoder2() }
}
fun init() {
val numStates = 1 shl m_NumPrevBits + m_NumPosBits
for (i in 0 until numStates)
m_Coders!![i].init()
}
fun getSubCoder(pos: Int, prevByte: Byte): Encoder2 =
m_Coders!![(pos and m_PosMask shl m_NumPrevBits) + (prevByte and 0xFF).ushr(8 - m_NumPrevBits)]
}
internal open inner class LenEncoder {
private var _choice = ShortArray(2)
private var _lowCoder = Array(LzmaBase.kNumPosStatesEncodingMax) { BitTreeEncoder(LzmaBase.kNumLowLenBits) }
private var _midCoder = Array(LzmaBase.kNumPosStatesEncodingMax) { BitTreeEncoder(LzmaBase.kNumMidLenBits) }
private var _highCoder = BitTreeEncoder(LzmaBase.kNumHighLenBits)
fun init(numPosStates: Int) {
RangeEncoder.initBitModels(_choice)
for (posState in 0 until numPosStates) {
_lowCoder[posState].init()
_midCoder[posState].init()
}
_highCoder.init()
}
open fun encode(rangeEncoder: RangeEncoder, symbol: Int, posState: Int) {
var sym = symbol
if (sym < LzmaBase.kNumLowLenSymbols) {
rangeEncoder.encode(_choice, 0, 0)
_lowCoder[posState].encode(rangeEncoder, sym)
} else {
sym -= LzmaBase.kNumLowLenSymbols
rangeEncoder.encode(_choice, 0, 1)
if (sym < LzmaBase.kNumMidLenSymbols) {
rangeEncoder.encode(_choice, 1, 0)
_midCoder[posState].encode(rangeEncoder, sym)
} else {
rangeEncoder.encode(_choice, 1, 1)
_highCoder.encode(rangeEncoder, sym - LzmaBase.kNumMidLenSymbols)
}
}
}
fun setPrices(posState: Int, numSymbols: Int, prices: IntArray, st: Int) {
val a0 = RangeEncoder.getPrice0(_choice[0].toInt())
val a1 = RangeEncoder.getPrice1(_choice[0].toInt())
val b0 = a1 + RangeEncoder.getPrice0(_choice[1].toInt())
val b1 = a1 + RangeEncoder.getPrice1(_choice[1].toInt())
var i = 0
while (i < LzmaBase.kNumLowLenSymbols) {
if (i >= numSymbols) return
prices[st + i] = a0 + _lowCoder[posState].getPrice(i)
i++
}
while (i < LzmaBase.kNumLowLenSymbols + LzmaBase.kNumMidLenSymbols) {
if (i >= numSymbols) return
prices[st + i] = b0 + _midCoder[posState].getPrice(i - LzmaBase.kNumLowLenSymbols)
i++
}
while (i < numSymbols) {
prices[st + i] = b1 + _highCoder.getPrice(i - LzmaBase.kNumLowLenSymbols - LzmaBase.kNumMidLenSymbols)
i++
}
}
}
internal inner class LenPriceTableEncoder : LenEncoder() {
private var _prices = IntArray(LzmaBase.kNumLenSymbols shl LzmaBase.kNumPosStatesBitsEncodingMax)
private var _tableSize: Int = 0
private var _counters = IntArray(LzmaBase.kNumPosStatesEncodingMax)
fun setTableSize(tableSize: Int) {
_tableSize = tableSize
}
fun getPrice(symbol: Int, posState: Int): Int = _prices[posState * LzmaBase.kNumLenSymbols + symbol]
private fun updateTable(posState: Int) {
setPrices(posState, _tableSize, _prices, posState * LzmaBase.kNumLenSymbols)
_counters[posState] = _tableSize
}
fun updateTables(numPosStates: Int) { for (posState in 0 until numPosStates) updateTable(posState) }
override fun encode(rangeEncoder: RangeEncoder, symbol: Int, posState: Int) {
super.encode(rangeEncoder, symbol, posState)
if (--_counters[posState] == 0)
updateTable(posState)
}
}
internal inner class Optimal {
var state: Int = 0
var prev1IsChar: Boolean = false
var prev2: Boolean = false
var posPrev2: Int = 0
var backPrev2: Int = 0
var price: Int = 0
var posPrev: Int = 0
var backPrev: Int = 0
var backs0: Int = 0
var backs1: Int = 0
var backs2: Int = 0
var backs3: Int = 0
fun makeAsChar() {
backPrev = -1
prev1IsChar = false
}
fun makeAsShortRep() {
backPrev = 0
prev1IsChar = false
}
fun isShortRep(): Boolean = backPrev == 0
}
internal fun create() {
if (_matchFinder == null) {
val bt = LzBinTree()
var numHashBytes = 4
if (_matchFinderType == EMatchFinderTypeBT2)
numHashBytes = 2
bt.setType(numHashBytes)
_matchFinder = bt
}
_literalEncoder.create(_numLiteralPosStateBits, _numLiteralContextBits)
if (_dictionarySize == _dictionarySizePrev && _numFastBytesPrev == _numFastBytes)
return
_matchFinder!!.create(
_dictionarySize,
kNumOpts, _numFastBytes, LzmaBase.kMatchMaxLen + 1
)
_dictionarySizePrev = _dictionarySize
_numFastBytesPrev = _numFastBytes
}
//internal fun setWriteEndMarkerMode(writeEndMarker: Boolean) { _writeEndMark = writeEndMarker }
internal fun init() {
baseInit()
_rangeEncoder.init()
RangeEncoder.initBitModels(_isMatch)
RangeEncoder.initBitModels(_isRep0Long)
RangeEncoder.initBitModels(_isRep)
RangeEncoder.initBitModels(_isRepG0)
RangeEncoder.initBitModels(_isRepG1)
RangeEncoder.initBitModels(_isRepG2)
RangeEncoder.initBitModels(_posEncoders)
_literalEncoder.init()
for (i in 0 until LzmaBase.kNumLenToPosStates) {
_posSlotEncoder[i].init()
}
_lenEncoder.init(1 shl _posStateBits)
_repMatchLenEncoder.init(1 shl _posStateBits)
_posAlignEncoder.init()
_longestMatchWasFound = false
_optimumEndIndex = 0
_optimumCurrentIndex = 0
_additionalOffset = 0
}
private fun readMatchDistances(): Int {
var lenRes = 0
_numDistancePairs = _matchFinder!!.getMatches(_matchDistances)
if (_numDistancePairs > 0) {
lenRes = _matchDistances[_numDistancePairs - 2]
if (lenRes == _numFastBytes)
lenRes += _matchFinder!!.getMatchLen(
lenRes - 1, _matchDistances[_numDistancePairs - 1],
LzmaBase.kMatchMaxLen - lenRes
)
}
_additionalOffset++
return lenRes
}
private fun movePos(num: Int) {
if (num > 0) {
_matchFinder!!.skip(num)
_additionalOffset += num
}
}
private fun getRepLen1Price(state: Int, posState: Int): Int =
RangeEncoder.getPrice0(_isRepG0[state].toInt()) + RangeEncoder.getPrice0(
_isRep0Long[(state shl LzmaBase.kNumPosStatesBitsMax) + posState].toInt()
)
private fun getPureRepPrice(repIndex: Int, state: Int, posState: Int): Int {
var price: Int
if (repIndex == 0) {
price = RangeEncoder.getPrice0(_isRepG0[state].toInt())
price += RangeEncoder.getPrice1(
_isRep0Long[(state shl LzmaBase.kNumPosStatesBitsMax) + posState].toInt()
)
} else {
price = RangeEncoder.getPrice1(_isRepG0[state].toInt())
if (repIndex == 1) {
price += RangeEncoder.getPrice0(_isRepG1[state].toInt())
} else {
price += RangeEncoder.getPrice1(_isRepG1[state].toInt())
price += RangeEncoder.getPrice(_isRepG2[state].toInt(), repIndex - 2)
}
}
return price
}
private fun getRepPrice(repIndex: Int, len: Int, state: Int, posState: Int): Int {
val price = _repMatchLenEncoder.getPrice(len - LzmaBase.kMatchMinLen, posState)
return price + getPureRepPrice(repIndex, state, posState)
}
private fun getPosLenPrice(pos: Int, len: Int, posState: Int): Int {
val price: Int
val lenToPosState = LzmaBase.getLenToPosState(len)
price = if (pos < LzmaBase.kNumFullDistances)
_distancesPrices[lenToPosState * LzmaBase.kNumFullDistances + pos]
else
_posSlotPrices[(lenToPosState shl LzmaBase.kNumPosSlotBits) + getPosSlot2(pos)] + _alignPrices[pos and LzmaBase.kAlignMask]
return price + _lenEncoder.getPrice(len - LzmaBase.kMatchMinLen, posState)
}
private fun backward(cur: Int): Int {
var cc = cur
_optimumEndIndex = cc
var posMem = _optimum[cc].posPrev
var backMem = _optimum[cc].backPrev
do {
if (_optimum[cc].prev1IsChar) {
_optimum[posMem].makeAsChar()
_optimum[posMem].posPrev = posMem - 1
if (_optimum[cc].prev2) {
_optimum[posMem - 1].prev1IsChar = false
_optimum[posMem - 1].posPrev = _optimum[cc].posPrev2
_optimum[posMem - 1].backPrev = _optimum[cc].backPrev2
}
}
val posPrev = posMem
val backCur = backMem
backMem = _optimum[posPrev].backPrev
posMem = _optimum[posPrev].posPrev
_optimum[posPrev].backPrev = backCur
_optimum[posPrev].posPrev = cc
cc = posPrev
} while (cc > 0)
backRes = _optimum[0].backPrev
_optimumCurrentIndex = _optimum[0].posPrev
return _optimumCurrentIndex
}
private fun getOptimum(position: Int): Int {
var ppos = position
if (_optimumEndIndex != _optimumCurrentIndex) {
val lenRes = _optimum[_optimumCurrentIndex].posPrev - _optimumCurrentIndex
backRes = _optimum[_optimumCurrentIndex].backPrev
_optimumCurrentIndex = _optimum[_optimumCurrentIndex].posPrev
return lenRes
}
_optimumEndIndex = 0
_optimumCurrentIndex = _optimumEndIndex
val lenMain: Int
var numDistancePairs: Int
if (!_longestMatchWasFound) {
lenMain = readMatchDistances()
} else {
lenMain = _longestMatchLength
_longestMatchWasFound = false
}
numDistancePairs = _numDistancePairs
var numAvailableBytes = _matchFinder!!.getNumAvailableBytes() + 1
if (numAvailableBytes < 2) {
backRes = -1
return 1
}
if (numAvailableBytes > LzmaBase.kMatchMaxLen) {
@Suppress("UNUSED_VALUE")
numAvailableBytes = LzmaBase.kMatchMaxLen
}
var repMaxIndex = 0
var i = 0
while (i < LzmaBase.kNumRepDistances) {
reps[i] = _repDistances[i]
repLens[i] = _matchFinder!!.getMatchLen(
0 - 1, reps[i],
LzmaBase.kMatchMaxLen
)
if (repLens[i] > repLens[repMaxIndex])
repMaxIndex = i
i++
}
if (repLens[repMaxIndex] >= _numFastBytes) {
backRes = repMaxIndex
val lenRes = repLens[repMaxIndex]
movePos(lenRes - 1)
return lenRes
}
if (lenMain >= _numFastBytes) {
backRes = _matchDistances[numDistancePairs - 1] +
LzmaBase.kNumRepDistances
movePos(lenMain - 1)
return lenMain
}
var currentByte = _matchFinder!!.getIndexByte(0 - 1)
var matchByte = _matchFinder!!.getIndexByte(0 - _repDistances[0] - 1 - 1)
if (lenMain < 2 && currentByte != matchByte && repLens[repMaxIndex] < 2) {
backRes = -1
return 1
}
_optimum[0].state = _state
var posState = ppos and _posStateMask
_optimum[1].price = RangeEncoder.getPrice0(
_isMatch[(_state shl LzmaBase.kNumPosStatesBitsMax) + posState].toInt()
) +
_literalEncoder.getSubCoder(ppos, _previousByte).getPrice(
!LzmaBase.stateIsCharState(_state),
matchByte,
currentByte
)
_optimum[1].makeAsChar()
var matchPrice =
RangeEncoder.getPrice1(_isMatch[(_state shl LzmaBase.kNumPosStatesBitsMax) + posState].toInt())
var repMatchPrice =
matchPrice + RangeEncoder.getPrice1(
_isRep[_state].toInt()
)
if (matchByte == currentByte) {
val shortRepPrice = repMatchPrice + getRepLen1Price(_state, posState)
if (shortRepPrice < _optimum[1].price) {
_optimum[1].price = shortRepPrice
_optimum[1].makeAsShortRep()
}
}
var lenEnd = if (lenMain >= repLens[repMaxIndex]) lenMain else repLens[repMaxIndex]
if (lenEnd < 2) {
backRes = _optimum[1].backPrev
return 1
}
_optimum[1].posPrev = 0
_optimum[0].backs0 = reps[0]
_optimum[0].backs1 = reps[1]
_optimum[0].backs2 = reps[2]
_optimum[0].backs3 = reps[3]
var len = lenEnd
do
_optimum[len--].price =
kIfinityPrice
while (len >= 2)
i = 0
while (i < LzmaBase.kNumRepDistances) {
var repLen = repLens[i]
if (repLen < 2) {
i++
continue
}
val price = repMatchPrice + getPureRepPrice(i, _state, posState)
do {
val curAndLenPrice = price + _repMatchLenEncoder.getPrice(repLen - 2, posState)
val optimum = _optimum[repLen]
if (curAndLenPrice < optimum.price) {
optimum.price = curAndLenPrice
optimum.posPrev = 0
optimum.backPrev = i
optimum.prev1IsChar = false
}
} while (--repLen >= 2)
i++
}
var normalMatchPrice =
matchPrice + RangeEncoder.getPrice0(
_isRep[_state].toInt()
)
len = if (repLens[0] >= 2) repLens[0] + 1 else 2
if (len <= lenMain) {
var offs = 0
while (len > _matchDistances[offs])
offs += 2
while (true) {
val distance = _matchDistances[offs + 1]
val curAndLenPrice = normalMatchPrice + getPosLenPrice(distance, len, posState)
val optimum = _optimum[len]
if (curAndLenPrice < optimum.price) {
optimum.price = curAndLenPrice
optimum.posPrev = 0
optimum.backPrev = distance +
LzmaBase.kNumRepDistances
optimum.prev1IsChar = false
}
if (len == _matchDistances[offs]) {
offs += 2
if (offs == numDistancePairs)
break
}
len++
}
}
var cur = 0
while (true) {
cur++
if (cur == lenEnd)
return backward(cur)
var newLen = readMatchDistances()
numDistancePairs = _numDistancePairs
if (newLen >= _numFastBytes) {
_longestMatchLength = newLen
_longestMatchWasFound = true
return backward(cur)
}
ppos++
var posPrev = _optimum[cur].posPrev
var state: Int
if (_optimum[cur].prev1IsChar) {
posPrev--
if (_optimum[cur].prev2) {
state = _optimum[_optimum[cur].posPrev2].state
state = if (_optimum[cur].backPrev2 < LzmaBase.kNumRepDistances)
LzmaBase.stateUpdateRep(state)
else
LzmaBase.stateUpdateMatch(state)
} else
state = _optimum[posPrev].state
state = LzmaBase.stateUpdateChar(state)
} else
state = _optimum[posPrev].state
if (posPrev == cur - 1) {
state = if (_optimum[cur].isShortRep())
LzmaBase.stateUpdateShortRep(state)
else
LzmaBase.stateUpdateChar(state)
} else {
val pos: Int
if (_optimum[cur].prev1IsChar && _optimum[cur].prev2) {
posPrev = _optimum[cur].posPrev2
pos = _optimum[cur].backPrev2
state = LzmaBase.stateUpdateRep(state)
} else {
pos = _optimum[cur].backPrev
state = if (pos < LzmaBase.kNumRepDistances)
LzmaBase.stateUpdateRep(state)
else
LzmaBase.stateUpdateMatch(state)
}
val opt = _optimum[posPrev]
if (pos < LzmaBase.kNumRepDistances) {
when (pos) {
0 -> {
reps[0] = opt.backs0
reps[1] = opt.backs1
reps[2] = opt.backs2
reps[3] = opt.backs3
}
1 -> {
reps[0] = opt.backs1
reps[1] = opt.backs0
reps[2] = opt.backs2
reps[3] = opt.backs3
}
2 -> {
reps[0] = opt.backs2
reps[1] = opt.backs0
reps[2] = opt.backs1
reps[3] = opt.backs3
}
else -> {
reps[0] = opt.backs3
reps[1] = opt.backs0
reps[2] = opt.backs1
reps[3] = opt.backs2
}
}
} else {
reps[0] = pos - LzmaBase.kNumRepDistances
reps[1] = opt.backs0
reps[2] = opt.backs1
reps[3] = opt.backs2
}
}
_optimum[cur].state = state
_optimum[cur].backs0 = reps[0]
_optimum[cur].backs1 = reps[1]
_optimum[cur].backs2 = reps[2]
_optimum[cur].backs3 = reps[3]
val curPrice = _optimum[cur].price
currentByte = _matchFinder!!.getIndexByte(0 - 1)
matchByte = _matchFinder!!.getIndexByte(0 - reps[0] - 1 - 1)
posState = ppos and _posStateMask
val curAnd1Price = curPrice +
RangeEncoder.getPrice0(
_isMatch[(state shl LzmaBase.kNumPosStatesBitsMax) + posState].toInt()
) +
_literalEncoder.getSubCoder(
ppos,
_matchFinder!!.getIndexByte(0 - 2)
).getPrice(!LzmaBase.stateIsCharState(state), matchByte, currentByte)
val nextOptimum = _optimum[cur + 1]
var nextIsChar = false
if (curAnd1Price < nextOptimum.price) {
nextOptimum.price = curAnd1Price
nextOptimum.posPrev = cur
nextOptimum.makeAsChar()
nextIsChar = true
}
matchPrice = curPrice +
RangeEncoder.getPrice1(
_isMatch[(state shl LzmaBase.kNumPosStatesBitsMax) + posState].toInt()
)
repMatchPrice = matchPrice +
RangeEncoder.getPrice1(
_isRep[state].toInt()
)
if (matchByte == currentByte && !(nextOptimum.posPrev < cur && nextOptimum.backPrev == 0)) {
val shortRepPrice = repMatchPrice + getRepLen1Price(state, posState)
if (shortRepPrice <= nextOptimum.price) {
nextOptimum.price = shortRepPrice
nextOptimum.posPrev = cur
nextOptimum.makeAsShortRep()
nextIsChar = true
}
}
var numAvailableBytesFull = _matchFinder!!.getNumAvailableBytes() + 1
numAvailableBytesFull = min(kNumOpts - 1 - cur, numAvailableBytesFull)
numAvailableBytes = numAvailableBytesFull
if (numAvailableBytes < 2)
continue
if (numAvailableBytes > _numFastBytes)
numAvailableBytes = _numFastBytes
if (!nextIsChar && matchByte != currentByte) {
// try Literal + rep0
val t = min(numAvailableBytesFull - 1, _numFastBytes)
val lenTest2 = _matchFinder!!.getMatchLen(0, reps[0], t)
if (lenTest2 >= 2) {
val state2 = LzmaBase.stateUpdateChar(state)
val posStateNext = ppos + 1 and _posStateMask
val nextRepMatchPrice = curAnd1Price +
RangeEncoder.getPrice1(
_isMatch[(state2 shl LzmaBase.kNumPosStatesBitsMax) + posStateNext].toInt()
) +
RangeEncoder.getPrice1(
_isRep[state2].toInt()
)
run {
val offset = cur + 1 + lenTest2
while (lenEnd < offset)
_optimum[++lenEnd].price =
kIfinityPrice
val curAndLenPrice = nextRepMatchPrice + getRepPrice(
0, lenTest2, state2, posStateNext
)
val optimum = _optimum[offset]
if (curAndLenPrice < optimum.price) {
optimum.price = curAndLenPrice
optimum.posPrev = cur + 1
optimum.backPrev = 0
optimum.prev1IsChar = true
optimum.prev2 = false
}
}
}
}
var startLen = 2 // speed optimization
for (repIndex in 0 until LzmaBase.kNumRepDistances) {
var lenTest = _matchFinder!!.getMatchLen(0 - 1, reps[repIndex], numAvailableBytes)
if (lenTest < 2)
continue
val lenTestTemp = lenTest
do {
while (lenEnd < cur + lenTest)
_optimum[++lenEnd].price =
kIfinityPrice
val curAndLenPrice = repMatchPrice + getRepPrice(repIndex, lenTest, state, posState)
val optimum = _optimum[cur + lenTest]
if (curAndLenPrice < optimum.price) {
optimum.price = curAndLenPrice
optimum.posPrev = cur
optimum.backPrev = repIndex
optimum.prev1IsChar = false
}
} while (--lenTest >= 2)
lenTest = lenTestTemp
if (repIndex == 0)
startLen = lenTest + 1
// if (_maxMode)
if (lenTest < numAvailableBytesFull) {
val t = min(numAvailableBytesFull - 1 - lenTest, _numFastBytes)
val lenTest2 = _matchFinder!!.getMatchLen(lenTest, reps[repIndex], t)
if (lenTest2 >= 2) {
var state2 =
LzmaBase.stateUpdateRep(state)
var posStateNext = ppos + lenTest and _posStateMask
val curAndLenCharPrice = repMatchPrice + getRepPrice(repIndex, lenTest, state, posState) +
RangeEncoder.getPrice0(
_isMatch[(state2 shl LzmaBase.kNumPosStatesBitsMax) + posStateNext].toInt()
) +
_literalEncoder.getSubCoder(
ppos + lenTest,
_matchFinder!!.getIndexByte(lenTest - 1 - 1)
).getPrice(
true,
_matchFinder!!.getIndexByte(lenTest - 1 - (reps[repIndex] + 1)),
_matchFinder!!.getIndexByte(lenTest - 1)
)
state2 = LzmaBase.stateUpdateChar(state2)
posStateNext = ppos + lenTest + 1 and _posStateMask
val nextMatchPrice =
curAndLenCharPrice + RangeEncoder.getPrice1(
_isMatch[(state2 shl LzmaBase.kNumPosStatesBitsMax) + posStateNext].toInt()
)
val nextRepMatchPrice =
nextMatchPrice + RangeEncoder.getPrice1(
_isRep[state2].toInt()
)
// for(; lenTest2 >= 2; lenTest2--)
run {
val offset = lenTest + 1 + lenTest2
while (lenEnd < cur + offset)
_optimum[++lenEnd].price =
kIfinityPrice
val curAndLenPrice = nextRepMatchPrice + getRepPrice(0, lenTest2, state2, posStateNext)
val optimum = _optimum[cur + offset]
if (curAndLenPrice < optimum.price) {
optimum.price = curAndLenPrice
optimum.posPrev = cur + lenTest + 1
optimum.backPrev = 0
optimum.prev1IsChar = true
optimum.prev2 = true
optimum.posPrev2 = cur
optimum.backPrev2 = repIndex
}
}
}
}
}
if (newLen > numAvailableBytes) {
newLen = numAvailableBytes
numDistancePairs = 0
while (newLen > _matchDistances[numDistancePairs]) {
numDistancePairs += 2
}
_matchDistances[numDistancePairs] = newLen
numDistancePairs += 2
}
if (newLen >= startLen) {
normalMatchPrice = matchPrice +
RangeEncoder.getPrice0(
_isRep[state].toInt()
)
while (lenEnd < cur + newLen)
_optimum[++lenEnd].price =
kIfinityPrice
var offs = 0
while (startLen > _matchDistances[offs])
offs += 2
var lenTest = startLen
while (true) {
val curBack = _matchDistances[offs + 1]
var curAndLenPrice = normalMatchPrice + getPosLenPrice(curBack, lenTest, posState)
var optimum = _optimum[cur + lenTest]
if (curAndLenPrice < optimum.price) {
optimum.price = curAndLenPrice
optimum.posPrev = cur
optimum.backPrev = curBack +
LzmaBase.kNumRepDistances
optimum.prev1IsChar = false
}
if (lenTest == _matchDistances[offs]) {
if (lenTest < numAvailableBytesFull) {
val t = min(numAvailableBytesFull - 1 - lenTest, _numFastBytes)
val lenTest2 = _matchFinder!!.getMatchLen(lenTest, curBack, t)
if (lenTest2 >= 2) {
var state2 =
LzmaBase.stateUpdateMatch(
state
)
var posStateNext = ppos + lenTest and _posStateMask
val curAndLenCharPrice = curAndLenPrice +
RangeEncoder.getPrice0(
_isMatch[(state2 shl LzmaBase.kNumPosStatesBitsMax) + posStateNext].toInt()
) +
_literalEncoder.getSubCoder(
ppos + lenTest,
_matchFinder!!.getIndexByte(lenTest - 1 - 1)
).getPrice(
true,
_matchFinder!!.getIndexByte(lenTest - (curBack + 1) - 1),
_matchFinder!!.getIndexByte(lenTest - 1)
)
state2 =
LzmaBase.stateUpdateChar(
state2
)
posStateNext = ppos + lenTest + 1 and _posStateMask
val nextMatchPrice =
curAndLenCharPrice + RangeEncoder.getPrice1(
_isMatch[(state2 shl LzmaBase.kNumPosStatesBitsMax) + posStateNext].toInt()
)
val nextRepMatchPrice =
nextMatchPrice + RangeEncoder.getPrice1(
_isRep[state2].toInt()
)
val offset = lenTest + 1 + lenTest2
while (lenEnd < cur + offset)
_optimum[++lenEnd].price =
kIfinityPrice
curAndLenPrice = nextRepMatchPrice + getRepPrice(0, lenTest2, state2, posStateNext)
optimum = _optimum[cur + offset]
if (curAndLenPrice < optimum.price) {
optimum.price = curAndLenPrice
optimum.posPrev = cur + lenTest + 1
optimum.backPrev = 0
optimum.prev1IsChar = true
optimum.prev2 = true
optimum.posPrev2 = cur
optimum.backPrev2 = curBack +
LzmaBase.kNumRepDistances
}
}
}
offs += 2
if (offs == numDistancePairs)
break
}
lenTest++
}
}
}
}
//internal fun changePair(smallDist: Int, bigDist: Int): Boolean {
// val kDif = 7
// return smallDist < 1 shl 32 - kDif && bigDist >= smallDist shl kDif
//}
private fun writeEndMarker(posState: Int) {
if (!_writeEndMark) return
_rangeEncoder.encode(_isMatch, (_state shl LzmaBase.kNumPosStatesBitsMax) + posState, 1)
_rangeEncoder.encode(_isRep, _state, 0)
_state = LzmaBase.stateUpdateMatch(_state)
val len = LzmaBase.kMatchMinLen
_lenEncoder.encode(_rangeEncoder, len - LzmaBase.kMatchMinLen, posState)
val posSlot = (1 shl LzmaBase.kNumPosSlotBits) - 1
val lenToPosState = LzmaBase.getLenToPosState(len)
_posSlotEncoder[lenToPosState].encode(_rangeEncoder, posSlot)
val footerBits = 30
val posReduced = (1 shl footerBits) - 1
_rangeEncoder.encodeDirectBits(posReduced shr LzmaBase.kNumAlignBits, footerBits - LzmaBase.kNumAlignBits)
_posAlignEncoder.reverseEncode(_rangeEncoder, posReduced and LzmaBase.kAlignMask)
}
private fun flush(nowPos: Int) {
releaseMFStream()
writeEndMarker(nowPos and _posStateMask)
_rangeEncoder.flushData()
_rangeEncoder.flushStream()
}
private fun codeOneBlock(inSize: LongArray, outSize: LongArray, finished: BooleanArray) {
inSize[0] = 0
outSize[0] = 0
finished[0] = true
if (_inStream != null) {
_matchFinder!!.setStream(_inStream!!)
_matchFinder!!.init()
_needReleaseMFStream = true
_inStream = null
}
if (_finished)
return
_finished = true
val progressPosValuePrev = nowPos64
if (nowPos64 == 0L) {
if (_matchFinder!!.getNumAvailableBytes() == 0) {
flush(nowPos64.toInt())
return
}
readMatchDistances()
val posState = nowPos64.toInt() and _posStateMask
_rangeEncoder.encode(_isMatch, (_state shl LzmaBase.kNumPosStatesBitsMax) + posState, 0)
_state = LzmaBase.stateUpdateChar(_state)
val curByte = _matchFinder!!.getIndexByte(0 - _additionalOffset)
_literalEncoder.getSubCoder(nowPos64.toInt(), _previousByte).encode(_rangeEncoder, curByte)
_previousByte = curByte
_additionalOffset--
nowPos64++
}
if (_matchFinder!!.getNumAvailableBytes() == 0) {
flush(nowPos64.toInt())
return
}
while (true) {
val len = getOptimum(nowPos64.toInt())
var pos = backRes
val posState = nowPos64.toInt() and _posStateMask
val complexState = (_state shl LzmaBase.kNumPosStatesBitsMax) + posState
if (len == 1 && pos == -1) {
_rangeEncoder.encode(_isMatch, complexState, 0)
val curByte = _matchFinder!!.getIndexByte(0 - _additionalOffset)
val subCoder = _literalEncoder.getSubCoder(nowPos64.toInt(), _previousByte)
if (!LzmaBase.stateIsCharState(_state)) {
val matchByte = _matchFinder!!.getIndexByte(0 - _repDistances[0] - 1 - _additionalOffset)
subCoder.encodeMatched(_rangeEncoder, matchByte, curByte)
} else
subCoder.encode(_rangeEncoder, curByte)
_previousByte = curByte
_state = LzmaBase.stateUpdateChar(_state)
} else {
_rangeEncoder.encode(_isMatch, complexState, 1)
if (pos < LzmaBase.kNumRepDistances) {
_rangeEncoder.encode(_isRep, _state, 1)
if (pos == 0) {
_rangeEncoder.encode(_isRepG0, _state, 0)
if (len == 1)
_rangeEncoder.encode(_isRep0Long, complexState, 0)
else
_rangeEncoder.encode(_isRep0Long, complexState, 1)
} else {
_rangeEncoder.encode(_isRepG0, _state, 1)
if (pos == 1)
_rangeEncoder.encode(_isRepG1, _state, 0)
else {
_rangeEncoder.encode(_isRepG1, _state, 1)
_rangeEncoder.encode(_isRepG2, _state, pos - 2)
}
}
_state = if (len == 1) {
LzmaBase.stateUpdateShortRep(_state)
} else {
_repMatchLenEncoder.encode(_rangeEncoder, len - LzmaBase.kMatchMinLen, posState)
LzmaBase.stateUpdateRep(_state)
}
val distance = _repDistances[pos]
if (pos != 0) {
for (i in pos downTo 1)
_repDistances[i] = _repDistances[i - 1]
_repDistances[0] = distance
}
} else {
_rangeEncoder.encode(_isRep, _state, 0)
_state = LzmaBase.stateUpdateMatch(_state)
_lenEncoder.encode(_rangeEncoder, len - LzmaBase.kMatchMinLen, posState)
pos -= LzmaBase.kNumRepDistances
val posSlot =
getPosSlot(pos)
val lenToPosState =
LzmaBase.getLenToPosState(len)
_posSlotEncoder[lenToPosState].encode(_rangeEncoder, posSlot)
if (posSlot >= LzmaBase.kStartPosModelIndex) {
val footerBits = (posSlot shr 1) - 1
val baseVal = 2 or (posSlot and 1) shl footerBits
val posReduced = pos - baseVal
if (posSlot < LzmaBase.kEndPosModelIndex)
BitTreeEncoder.reverseEncode(
_posEncoders,
baseVal - posSlot - 1, _rangeEncoder, footerBits, posReduced
)
else {
_rangeEncoder.encodeDirectBits(
posReduced shr LzmaBase.kNumAlignBits,
footerBits - LzmaBase.kNumAlignBits
)
_posAlignEncoder.reverseEncode(_rangeEncoder, posReduced and LzmaBase.kAlignMask)
_alignPriceCount++
}
}
val distance = pos
for (i in LzmaBase.kNumRepDistances - 1 downTo 1)
_repDistances[i] = _repDistances[i - 1]
_repDistances[0] = distance
_matchPriceCount++
}
_previousByte = _matchFinder!!.getIndexByte(len - 1 - _additionalOffset)
}
_additionalOffset -= len
nowPos64 += len.toLong()
if (_additionalOffset == 0) {
// if (!_fastMode)
if (_matchPriceCount >= 1 shl 7)
fillDistancesPrices()
if (_alignPriceCount >= LzmaBase.kAlignTableSize)
fillAlignPrices()
inSize[0] = nowPos64
outSize[0] = _rangeEncoder.getProcessedSizeAdd()
if (_matchFinder!!.getNumAvailableBytes() == 0) {
flush(nowPos64.toInt())
return
}
if (nowPos64 - progressPosValuePrev >= 1 shl 12) {
_finished = false
finished[0] = false
return
}
}
}
}
private fun releaseMFStream() {
if (_matchFinder != null && _needReleaseMFStream) {
_matchFinder!!.releaseStream()
_needReleaseMFStream = false
}
}
private fun setOutStream(outStream: LzmaOutputStream) {
_rangeEncoder.setStream(outStream)
}
private fun releaseOutStream() {
_rangeEncoder.releaseStream()
}
private fun releaseStreams() {
releaseMFStream()
releaseOutStream()
}
@Suppress("UNUSED_PARAMETER")
private fun setStreams(inStream: LzmaInputStream, outStream: LzmaOutputStream, inSize: Long, outSize: Long) {
_inStream = inStream
_finished = false
create()
setOutStream(outStream)
init()
// if (!_fastMode)
run {
fillDistancesPrices()
fillAlignPrices()
}
_lenEncoder.setTableSize(_numFastBytes + 1 - LzmaBase.kMatchMinLen)
_lenEncoder.updateTables(1 shl _posStateBits)
_repMatchLenEncoder.setTableSize(_numFastBytes + 1 - LzmaBase.kMatchMinLen)
_repMatchLenEncoder.updateTables(1 shl _posStateBits)
nowPos64 = 0
}
fun code(inStream: LzmaInputStream, outStream: LzmaOutputStream, inSize: Long, outSize: Long, progress: ICodeProgress?) {
_needReleaseMFStream = false
try {
setStreams(inStream, outStream, inSize, outSize)
while (true) {
codeOneBlock(processedInSize, processedOutSize, finished)
if (finished[0]) return
progress?.setProgress(processedInSize[0], processedOutSize[0])
}
} finally {
releaseStreams()
}
}
fun writeCoderProperties(outStream: LzmaOutputStream) {
properties[0] = ((_posStateBits * 5 + _numLiteralPosStateBits) * 9 + _numLiteralContextBits).toByte()
for (i in 0..3)
properties[1 + i] = (_dictionarySize shr 8 * i).toByte()
outStream.write(
properties, 0,
kPropSize
)
}
private fun fillDistancesPrices() {
for (i in LzmaBase.kStartPosModelIndex until LzmaBase.kNumFullDistances) {
val posSlot = getPosSlot(i)
val footerBits = (posSlot shr 1) - 1
val baseVal = 2 or (posSlot and 1) shl footerBits
tempPrices[i] = BitTreeEncoder.reverseGetPrice(_posEncoders, baseVal - posSlot - 1, footerBits, i - baseVal)
}
for (lenToPosState in 0 until LzmaBase.kNumLenToPosStates) {
val encoder = _posSlotEncoder[lenToPosState]
val st = lenToPosState shl LzmaBase.kNumPosSlotBits
var posSlot = 0
while (posSlot < _distTableSize) {
_posSlotPrices[st + posSlot] = encoder.getPrice(posSlot)
posSlot++
}
posSlot = LzmaBase.kEndPosModelIndex
while (posSlot < _distTableSize) {
_posSlotPrices[st + posSlot] += (posSlot shr 1) - 1 - LzmaBase.kNumAlignBits shl RangeEncoder.kNumBitPriceShiftBits
posSlot++
}
val st2 = lenToPosState * LzmaBase.kNumFullDistances
var i = 0
while (i < LzmaBase.kStartPosModelIndex) {
_distancesPrices[st2 + i] = _posSlotPrices[st + i]
i++
}
while (i < LzmaBase.kNumFullDistances) {
_distancesPrices[st2 + i] = _posSlotPrices[st + getPosSlot(
i
)] + tempPrices[i]
i++
}
}
_matchPriceCount = 0
}
private fun fillAlignPrices() {
for (i in 0 until LzmaBase.kAlignTableSize)
_alignPrices[i] = _posAlignEncoder.reverseGetPrice(i)
_alignPriceCount = 0
}
@Suppress("UNUSED_PARAMETER")
fun setAlgorithm(algorithm: Int): Boolean {
/*
_fastMode = (algorithm == 0);
_maxMode = (algorithm >= 2);
*/
return true
}
fun setDictionarySize(dictionarySize: Int): Boolean {
val kDicLogSizeMaxCompress = 29
val cond1 = dictionarySize < (1 shl LzmaBase.kDicLogSizeMin)
val cond2 = dictionarySize > (1 shl kDicLogSizeMaxCompress)
if (cond1 || cond2)
return false
_dictionarySize = dictionarySize
var dicLogSize = 0
while (dictionarySize > 1 shl dicLogSize) {
dicLogSize++
}
_distTableSize = dicLogSize * 2
return true
}
fun setNumFastBytes(numFastBytes: Int): Boolean {
if (numFastBytes < 5 || numFastBytes > LzmaBase.kMatchMaxLen)
return false
_numFastBytes = numFastBytes
return true
}
fun setMatchFinder(matchFinderIndex: Int): Boolean {
if (matchFinderIndex < 0 || matchFinderIndex > 2)
return false
val matchFinderIndexPrev = _matchFinderType
_matchFinderType = matchFinderIndex
if (_matchFinder != null && matchFinderIndexPrev != _matchFinderType) {
_dictionarySizePrev = -1
_matchFinder = null
}
return true
}
fun setLcLpPb(lc: Int, lp: Int, pb: Int): Boolean {
if (lp < 0 || lp > LzmaBase.kNumLitPosStatesBitsEncodingMax ||
lc < 0 || lc > LzmaBase.kNumLitContextBitsMax ||
pb < 0 || pb > LzmaBase.kNumPosStatesBitsEncodingMax
)
return false
_numLiteralPosStateBits = lp
_numLiteralContextBits = lc
_posStateBits = pb
_posStateMask = (1 shl _posStateBits) - 1
return true
}
fun setEndMarkerMode(endMarkerMode: Boolean) {
_writeEndMark = endMarkerMode
}
companion object {
const val EMatchFinderTypeBT2 = 0
const val EMatchFinderTypeBT4 = 1
internal const val kIfinityPrice = 0xFFFFFFF
private val g_FastPos = ByteArray(1 shl 11).also { g_FastPos ->
val kFastSlots = 22
var c = 2
g_FastPos[0] = 0
g_FastPos[1] = 1
for (slotFast in 2 until kFastSlots) {
val k = 1 shl (slotFast shr 1) - 1
var j = 0
while (j < k) {
g_FastPos[c] = slotFast.toByte()
j++
c++
}
}
}
internal fun getPosSlot(pos: Int): Int {
if (pos < 1 shl 11)
return g_FastPos[pos].toInt()
return if (pos < 1 shl 21) g_FastPos[pos shr 10] + 20 else g_FastPos[pos shr 20] + 40
}
internal fun getPosSlot2(pos: Int): Int {
if (pos < 1 shl 17)
return g_FastPos[pos shr 6] + 12
return if (pos < 1 shl 27) g_FastPos[pos shr 16] + 32 else g_FastPos[pos shr 26] + 52
}
internal const val kDefaultDictionaryLogSize = 22
internal const val kNumFastBytesDefault = 0x20
//const val kNumLenSpecSymbols = LzmaBase.kNumLowLenSymbols + LzmaBase.kNumMidLenSymbols
internal const val kNumOpts = 1 shl 12
const val kPropSize = 5
}
}
class LzBinTree : LzInWindow() {
private var _cyclicBufferPos: Int = 0
private var _cyclicBufferSize = 0
private var _matchMaxLen: Int = 0
private lateinit var _son: IntArray
private lateinit var _hash: IntArray
private var _cutValue = 0xFF
private var _hashMask: Int = 0
private var _hashSizeSum = 0
private var HASH_ARRAY = true
private var kNumHashDirectBytes = 0
private var kMinMatchCheck = 4
private var kFixHashSize = kHash2Size + kHash3Size
fun setType(numHashBytes: Int) {
HASH_ARRAY = numHashBytes > 2
if (HASH_ARRAY) {
kNumHashDirectBytes = 0
kMinMatchCheck = 4
kFixHashSize = kHash2Size +
kHash3Size
} else {
kNumHashDirectBytes = 2
kMinMatchCheck = 2 + 1
kFixHashSize = 0
}
}
override fun init() {
super.init()
for (i in 0 until _hashSizeSum)
_hash[i] = kEmptyHashValue
_cyclicBufferPos = 0
reduceOffsets(-1)
}
override fun movePos() {
if (++_cyclicBufferPos >= _cyclicBufferSize)
_cyclicBufferPos = 0
super.movePos()
if (_pos == kMaxValForNormalize)
normalize()
}
fun create(historySize: Int, keepAddBufferBefore: Int, matchMaxLen: Int, keepAddBufferAfter: Int): Boolean {
if (historySize > kMaxValForNormalize - 256) return false
_cutValue = 16 + (matchMaxLen shr 1)
val windowReservSize = (historySize + keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + 256
super.create(historySize + keepAddBufferBefore, matchMaxLen + keepAddBufferAfter, windowReservSize)
_matchMaxLen = matchMaxLen
val cyclicBufferSize = historySize + 1
if (_cyclicBufferSize != cyclicBufferSize) _cyclicBufferSize = cyclicBufferSize
_son = IntArray(_cyclicBufferSize * 2)
var hs = kBT2HashSize
if (HASH_ARRAY) {
hs = historySize - 1
hs = hs or (hs shr 1)
hs = hs or (hs shr 2)
hs = hs or (hs shr 4)
hs = hs or (hs shr 8)
hs = hs shr 1
hs = hs or 0xFFFF
if (hs > 1 shl 24) hs = hs shr 1
_hashMask = hs
hs++
hs += kFixHashSize
}
if (hs != _hashSizeSum) {
_hashSizeSum = hs
_hash = IntArray(_hashSizeSum)
}
return true
}
fun getMatches(distances: IntArray): Int {
val lenLimit: Int
if (_pos + _matchMaxLen <= _streamPos) {
lenLimit = _matchMaxLen
} else {
lenLimit = _streamPos - _pos
if (lenLimit < kMinMatchCheck) {
movePos()
return 0
}
}
var offset = 0
val matchMinPos = if (_pos > _cyclicBufferSize) _pos - _cyclicBufferSize else 0
val cur = _bufferOffset + _pos
var maxLen =
kStartMaxLen // to avoid items for len < hashSize;
val hashValue: Int
var hash2Value = 0
var hash3Value = 0
if (HASH_ARRAY) {
var temp = CrcTable[_bufferBase!![cur] and 0xFF] xor (_bufferBase!![cur + 1] and 0xFF)
hash2Value = temp and kHash2Size - 1
temp = temp xor ((_bufferBase!![cur + 2] and 0xFF) shl 8)
hash3Value = temp and kHash3Size - 1
hashValue = temp xor (CrcTable[_bufferBase!![cur + 3] and 0xFF] shl 5) and _hashMask
} else
hashValue = _bufferBase!![cur] and 0xFF xor ((_bufferBase!![cur + 1] and 0xFF) shl 8)
var curMatch = _hash[kFixHashSize + hashValue]
if (HASH_ARRAY) {
var curMatch2 = _hash[hash2Value]
val curMatch3 = _hash[kHash3Offset + hash3Value]
_hash[hash2Value] = _pos
_hash[kHash3Offset + hash3Value] = _pos
if (curMatch2 > matchMinPos)
if (_bufferBase!![_bufferOffset + curMatch2] == _bufferBase!![cur]) {
maxLen = 2
distances[offset++] = maxLen
distances[offset++] = _pos - curMatch2 - 1
}
if (curMatch3 > matchMinPos)
if (_bufferBase!![_bufferOffset + curMatch3] == _bufferBase!![cur]) {
if (curMatch3 == curMatch2)
offset -= 2
maxLen = 3
distances[offset++] = maxLen
distances[offset++] = _pos - curMatch3 - 1
curMatch2 = curMatch3
}
if (offset != 0 && curMatch2 == curMatch) {
offset -= 2
maxLen = kStartMaxLen
}
}
_hash[kFixHashSize + hashValue] = _pos
var ptr0 = (_cyclicBufferPos shl 1) + 1
var ptr1 = _cyclicBufferPos shl 1
var len0: Int
var len1: Int
len1 = kNumHashDirectBytes
len0 = len1
if (kNumHashDirectBytes != 0) {
if (curMatch > matchMinPos) {
if (_bufferBase!![_bufferOffset + curMatch + kNumHashDirectBytes] != _bufferBase!![cur + kNumHashDirectBytes]) {
maxLen = kNumHashDirectBytes
distances[offset++] = maxLen
distances[offset++] = _pos - curMatch - 1
}
}
}
var count = _cutValue
while (true) {
if (curMatch <= matchMinPos || count-- == 0) {
_son[ptr1] = kEmptyHashValue
_son[ptr0] = _son[ptr1]
break
}
val delta = _pos - curMatch
val cyclicPos = (if (delta <= _cyclicBufferPos)
_cyclicBufferPos - delta
else
_cyclicBufferPos - delta + _cyclicBufferSize) shl 1
val pby1 = _bufferOffset + curMatch
var len = min(len0, len1)
if (_bufferBase!![pby1 + len] == _bufferBase!![cur + len]) {
while (++len != lenLimit)
if (_bufferBase!![pby1 + len] != _bufferBase!![cur + len])
break
if (maxLen < len) {
maxLen = len
distances[offset++] = maxLen
distances[offset++] = delta - 1
if (len == lenLimit) {
_son[ptr1] = _son[cyclicPos]
_son[ptr0] = _son[cyclicPos + 1]
break
}
}
}
if (_bufferBase!![pby1 + len] and 0xFF < _bufferBase!![cur + len] and 0xFF) {
_son[ptr1] = curMatch
ptr1 = cyclicPos + 1
curMatch = _son[ptr1]
len1 = len
} else {
_son[ptr0] = curMatch
ptr0 = cyclicPos
curMatch = _son[ptr0]
len0 = len
}
}
movePos()
return offset
}
fun skip(num: Int) {
var nnum = num
do {
val lenLimit: Int
if (_pos + _matchMaxLen <= _streamPos)
lenLimit = _matchMaxLen
else {
lenLimit = _streamPos - _pos
if (lenLimit < kMinMatchCheck) {
movePos()
continue
}
}
val matchMinPos = if (_pos > _cyclicBufferSize) _pos - _cyclicBufferSize else 0
val cur = _bufferOffset + _pos
val hashValue: Int
if (HASH_ARRAY) {
var temp = CrcTable[_bufferBase!![cur] and 0xFF] xor (_bufferBase!![cur + 1] and 0xFF)
val hash2Value = temp and kHash2Size - 1
_hash[hash2Value] = _pos
temp = temp xor ((_bufferBase!![cur + 2] and 0xFF) shl 8)
val hash3Value = temp and kHash3Size - 1
_hash[kHash3Offset + hash3Value] = _pos
hashValue = temp xor (CrcTable[_bufferBase!![cur + 3] and 0xFF] shl 5) and _hashMask
} else
hashValue = _bufferBase!![cur] and 0xFF xor ((_bufferBase!![cur + 1] and 0xFF) shl 8)
var curMatch = _hash[kFixHashSize + hashValue]
_hash[kFixHashSize + hashValue] = _pos
var ptr0 = (_cyclicBufferPos shl 1) + 1
var ptr1 = _cyclicBufferPos shl 1
var len0: Int
var len1: Int
len1 = kNumHashDirectBytes
len0 = len1
var count = _cutValue
while (true) {
if (curMatch <= matchMinPos || count-- == 0) {
_son[ptr1] =
kEmptyHashValue
_son[ptr0] = _son[ptr1]
break
}
val delta = _pos - curMatch
val cyclicPos = (if (delta <= _cyclicBufferPos)
_cyclicBufferPos - delta
else
_cyclicBufferPos - delta + _cyclicBufferSize) shl 1
val pby1 = _bufferOffset + curMatch
var len = min(len0, len1)
if (_bufferBase!![pby1 + len] == _bufferBase!![cur + len]) {
while (++len != lenLimit)
if (_bufferBase!![pby1 + len] != _bufferBase!![cur + len])
break
if (len == lenLimit) {
_son[ptr1] = _son[cyclicPos]
_son[ptr0] = _son[cyclicPos + 1]
break
}
}
if (_bufferBase!![pby1 + len] and 0xFF < _bufferBase!![cur + len] and 0xFF) {
_son[ptr1] = curMatch
ptr1 = cyclicPos + 1
curMatch = _son[ptr1]
len1 = len
} else {
_son[ptr0] = curMatch
ptr0 = cyclicPos
curMatch = _son[ptr0]
len0 = len
}
}
movePos()
} while (--nnum != 0)
}
private fun normalizeLinks(items: IntArray, numItems: Int, subValue: Int) {
for (i in 0 until numItems) {
var value = items[i]
if (value <= subValue)
value = kEmptyHashValue
else
value -= subValue
items[i] = value
}
}
private fun normalize() {
val subValue = _pos - _cyclicBufferSize
normalizeLinks(_son, _cyclicBufferSize * 2, subValue)
normalizeLinks(_hash, _hashSizeSum, subValue)
reduceOffsets(subValue)
}
//fun setCutValue(cutValue: Int) = run { _cutValue = cutValue }
companion object {
internal const val kHash2Size = 1 shl 10
internal const val kHash3Size = 1 shl 16
internal const val kBT2HashSize = 1 shl 16
internal const val kStartMaxLen = 1
internal const val kHash3Offset = kHash2Size
internal const val kEmptyHashValue = 0
internal const val kMaxValForNormalize = (1 shl 30) - 1
private val CrcTable = IntArray(0x100) {
var c = it
for (k in 0 until 8) c = (if ((c and 1) != 0) -0x12477ce0 xor (c ushr 1) else c ushr 1)
c
}
}
}
open class LzInWindow {
var _bufferBase: ByteArray? = null // pointer to buffer with data
private var _stream: LzmaInputStream? = null
private var _posLimit: Int = 0 // offset (from _buffer) of first byte when new block reading must be done
private var _streamEndWasReached: Boolean = false // if (true) then _streamPos shows real end of stream
private var _pointerToLastSafePosition: Int = 0
var _bufferOffset: Int = 0
private var _blockSize: Int = 0 // Size of Allocated memory block
var _pos: Int = 0 // offset (from _buffer) of curent byte
private var _keepSizeBefore: Int = 0 // how many BYTEs must be kept in buffer before _pos
private var _keepSizeAfter: Int = 0 // how many BYTEs must be kept buffer after _pos
var _streamPos: Int = 0 // offset (from _buffer) of first not read byte from Stream
private fun moveBlock() {
var offset = _bufferOffset + _pos - _keepSizeBefore
// we need one additional byte, since MovePos moves on 1 byte.
if (offset > 0) offset--
val numBytes = _bufferOffset + _streamPos - offset
// check negative offset ????
for (i in 0 until numBytes) _bufferBase!![i] = _bufferBase!![offset + i]
_bufferOffset -= offset
}
private fun readBlock() {
if (_streamEndWasReached) return
while (true) {
val size = 0 - _bufferOffset + _blockSize - _streamPos
if (size == 0)
return
val numReadBytes = _stream!!.read(_bufferBase!!, _bufferOffset + _streamPos, size)
if (numReadBytes <= 0) {
_posLimit = _streamPos
val pointerToPostion = _bufferOffset + _posLimit
if (pointerToPostion > _pointerToLastSafePosition)
_posLimit = _pointerToLastSafePosition - _bufferOffset
_streamEndWasReached = true
return
}
_streamPos += numReadBytes
if (_streamPos >= _pos + _keepSizeAfter)
_posLimit = _streamPos - _keepSizeAfter
}
}
private fun free() {
_bufferBase = null
}
fun create(keepSizeBefore: Int, keepSizeAfter: Int, keepSizeReserv: Int) {
_keepSizeBefore = keepSizeBefore
_keepSizeAfter = keepSizeAfter
val blockSize = keepSizeBefore + keepSizeAfter + keepSizeReserv
if (_bufferBase == null || _blockSize != blockSize) {
free()
_blockSize = blockSize
_bufferBase = ByteArray(_blockSize)
}
_pointerToLastSafePosition = _blockSize - keepSizeAfter
}
fun setStream(stream: LzmaInputStream) {
_stream = stream
}
fun releaseStream() {
_stream = null
}
open fun init() {
_bufferOffset = 0
_pos = 0
_streamPos = 0
_streamEndWasReached = false
readBlock()
}
open fun movePos() {
_pos++
if (_pos > _posLimit) {
val pointerToPostion = _bufferOffset + _pos
if (pointerToPostion > _pointerToLastSafePosition) moveBlock()
readBlock()
}
}
fun getIndexByte(index: Int): Byte = _bufferBase!![_bufferOffset + _pos + index]
// index + limit have not to exceed _keepSizeAfter;
fun getMatchLen(index: Int, distance: Int, limit: Int): Int {
var ddis = distance
var dlim = limit
if (_streamEndWasReached && _pos + index + dlim > _streamPos) dlim = _streamPos - (_pos + index)
ddis++
// Byte *pby = _buffer + (size_t)_pos + index;
val pby = _bufferOffset + _pos + index
var i = 0
while (i < dlim && _bufferBase!![pby + i] == _bufferBase!![pby + i - ddis]) i++
return i
}
fun getNumAvailableBytes(): Int {
return _streamPos - _pos
}
fun reduceOffsets(subValue: Int) {
_bufferOffset += subValue
_posLimit -= subValue
_pos -= subValue
_streamPos -= subValue
}
}
class LzOutWindow {
private var _buffer: ByteArray? = null
private var _pos: Int = 0
private var _windowSize = 0
private var _streamPos: Int = 0
private var _stream: LzmaOutputStream? = null
fun create(windowSize: Int) {
if (_buffer == null || _windowSize != windowSize)
_buffer = ByteArray(windowSize)
_windowSize = windowSize
_pos = 0
_streamPos = 0
}
fun setStream(stream: LzmaOutputStream) {
releaseStream()
_stream = stream
}
fun releaseStream() {
flush()
_stream = null
}
fun init(solid: Boolean) {
if (!solid) {
_streamPos = 0
_pos = 0
}
}
fun flush() {
val size = _pos - _streamPos
if (size == 0) return
_stream!!.write(_buffer!!, _streamPos, size)
if (_pos >= _windowSize) _pos = 0
_streamPos = _pos
}
fun copyBlock(distance: Int, len: Int) {
var llen = len
var pos = _pos - distance - 1
if (pos < 0)
pos += _windowSize
while (llen != 0) {
if (pos >= _windowSize) pos = 0
_buffer!![_pos++] = _buffer!![pos++]
if (_pos >= _windowSize) flush()
llen--
}
}
fun putByte(b: Byte) {
_buffer!![_pos++] = b
if (_pos >= _windowSize)
flush()
}
fun getByte(distance: Int): Byte {
var pos = _pos - distance - 1
if (pos < 0) pos += _windowSize
return _buffer!![pos]
}
}
interface LzmaInputStream {
fun read(): Int
fun read(bytes: ByteArray, offset: Int, size: Int): Int
}
interface LzmaOutputStream {
fun flush()
fun write8(value: Int)
fun write(bytes: ByteArray, offset: Int, size: Int)
}
}
private infix fun Byte.and(mask: Int): Int = this.toInt() and mask
private infix fun Byte.shl(that: Int): Int = this.toInt() shl that
private infix fun Byte.shr(that: Int): Int = this.toInt() shr that