| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- // Copyright 2014-2022 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
- }
|