Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
140 lines
3.6 KiB
140 lines
3.6 KiB
// Copyright 2014-2021 Ulrich Kunitz. All rights reserved. |
|
// Use of this source code is governed by a BSD-style |
|
// license that can be found in the LICENSE file. |
|
|
|
package lzma |
|
|
|
// Constants used by the distance codec. |
|
const ( |
|
// minimum supported distance |
|
minDistance = 1 |
|
// maximum supported distance, value is used for the eos marker. |
|
maxDistance = 1 << 32 |
|
// number of the supported len states |
|
lenStates = 4 |
|
// start for the position models |
|
startPosModel = 4 |
|
// first index with align bits support |
|
endPosModel = 14 |
|
// bits for the position slots |
|
posSlotBits = 6 |
|
// number of align bits |
|
alignBits = 4 |
|
) |
|
|
|
// distCodec provides encoding and decoding of distance values. |
|
type distCodec struct { |
|
posSlotCodecs [lenStates]treeCodec |
|
posModel [endPosModel - startPosModel]treeReverseCodec |
|
alignCodec treeReverseCodec |
|
} |
|
|
|
// deepcopy initializes dc as deep copy of the source. |
|
func (dc *distCodec) deepcopy(src *distCodec) { |
|
if dc == src { |
|
return |
|
} |
|
for i := range dc.posSlotCodecs { |
|
dc.posSlotCodecs[i].deepcopy(&src.posSlotCodecs[i]) |
|
} |
|
for i := range dc.posModel { |
|
dc.posModel[i].deepcopy(&src.posModel[i]) |
|
} |
|
dc.alignCodec.deepcopy(&src.alignCodec) |
|
} |
|
|
|
// newDistCodec creates a new distance codec. |
|
func (dc *distCodec) init() { |
|
for i := range dc.posSlotCodecs { |
|
dc.posSlotCodecs[i] = makeTreeCodec(posSlotBits) |
|
} |
|
for i := range dc.posModel { |
|
posSlot := startPosModel + i |
|
bits := (posSlot >> 1) - 1 |
|
dc.posModel[i] = makeTreeReverseCodec(bits) |
|
} |
|
dc.alignCodec = makeTreeReverseCodec(alignBits) |
|
} |
|
|
|
// lenState converts the value l to a supported lenState value. |
|
func lenState(l uint32) uint32 { |
|
if l >= lenStates { |
|
l = lenStates - 1 |
|
} |
|
return l |
|
} |
|
|
|
// Encode encodes the distance using the parameter l. Dist can have values from |
|
// the full range of uint32 values. To get the distance offset the actual match |
|
// distance has to be decreased by 1. A distance offset of 0xffffffff (eos) |
|
// indicates the end of the stream. |
|
func (dc *distCodec) Encode(e *rangeEncoder, dist uint32, l uint32) (err error) { |
|
// Compute the posSlot using nlz32 |
|
var posSlot uint32 |
|
var bits uint32 |
|
if dist < startPosModel { |
|
posSlot = dist |
|
} else { |
|
bits = uint32(30 - nlz32(dist)) |
|
posSlot = startPosModel - 2 + (bits << 1) |
|
posSlot += (dist >> uint(bits)) & 1 |
|
} |
|
|
|
if err = dc.posSlotCodecs[lenState(l)].Encode(e, posSlot); err != nil { |
|
return |
|
} |
|
|
|
switch { |
|
case posSlot < startPosModel: |
|
return nil |
|
case posSlot < endPosModel: |
|
tc := &dc.posModel[posSlot-startPosModel] |
|
return tc.Encode(dist, e) |
|
} |
|
dic := directCodec(bits - alignBits) |
|
if err = dic.Encode(e, dist>>alignBits); err != nil { |
|
return |
|
} |
|
return dc.alignCodec.Encode(dist, e) |
|
} |
|
|
|
// Decode decodes the distance offset using the parameter l. The dist value |
|
// 0xffffffff (eos) indicates the end of the stream. Add one to the distance |
|
// offset to get the actual match distance. |
|
func (dc *distCodec) Decode(d *rangeDecoder, l uint32) (dist uint32, err error) { |
|
posSlot, err := dc.posSlotCodecs[lenState(l)].Decode(d) |
|
if err != nil { |
|
return |
|
} |
|
|
|
// posSlot equals distance |
|
if posSlot < startPosModel { |
|
return posSlot, nil |
|
} |
|
|
|
// posSlot uses the individual models |
|
bits := (posSlot >> 1) - 1 |
|
dist = (2 | (posSlot & 1)) << bits |
|
var u uint32 |
|
if posSlot < endPosModel { |
|
tc := &dc.posModel[posSlot-startPosModel] |
|
if u, err = tc.Decode(d); err != nil { |
|
return 0, err |
|
} |
|
dist += u |
|
return dist, nil |
|
} |
|
|
|
// posSlots use direct encoding and a single model for the four align |
|
// bits. |
|
dic := directCodec(bits - alignBits) |
|
if u, err = dic.Decode(d); err != nil { |
|
return 0, err |
|
} |
|
dist += u << alignBits |
|
if u, err = dc.alignCodec.Decode(d); err != nil { |
|
return 0, err |
|
} |
|
dist += u |
|
return dist, nil |
|
}
|
|
|