distcodec.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. // Copyright 2014-2022 Ulrich Kunitz. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package lzma
  5. // Constants used by the distance codec.
  6. const (
  7. // minimum supported distance
  8. minDistance = 1
  9. // maximum supported distance, value is used for the eos marker.
  10. maxDistance = 1 << 32
  11. // number of the supported len states
  12. lenStates = 4
  13. // start for the position models
  14. startPosModel = 4
  15. // first index with align bits support
  16. endPosModel = 14
  17. // bits for the position slots
  18. posSlotBits = 6
  19. // number of align bits
  20. alignBits = 4
  21. )
  22. // distCodec provides encoding and decoding of distance values.
  23. type distCodec struct {
  24. posSlotCodecs [lenStates]treeCodec
  25. posModel [endPosModel - startPosModel]treeReverseCodec
  26. alignCodec treeReverseCodec
  27. }
  28. // deepcopy initializes dc as deep copy of the source.
  29. func (dc *distCodec) deepcopy(src *distCodec) {
  30. if dc == src {
  31. return
  32. }
  33. for i := range dc.posSlotCodecs {
  34. dc.posSlotCodecs[i].deepcopy(&src.posSlotCodecs[i])
  35. }
  36. for i := range dc.posModel {
  37. dc.posModel[i].deepcopy(&src.posModel[i])
  38. }
  39. dc.alignCodec.deepcopy(&src.alignCodec)
  40. }
  41. // newDistCodec creates a new distance codec.
  42. func (dc *distCodec) init() {
  43. for i := range dc.posSlotCodecs {
  44. dc.posSlotCodecs[i] = makeTreeCodec(posSlotBits)
  45. }
  46. for i := range dc.posModel {
  47. posSlot := startPosModel + i
  48. bits := (posSlot >> 1) - 1
  49. dc.posModel[i] = makeTreeReverseCodec(bits)
  50. }
  51. dc.alignCodec = makeTreeReverseCodec(alignBits)
  52. }
  53. // lenState converts the value l to a supported lenState value.
  54. func lenState(l uint32) uint32 {
  55. if l >= lenStates {
  56. l = lenStates - 1
  57. }
  58. return l
  59. }
  60. // Encode encodes the distance using the parameter l. Dist can have values from
  61. // the full range of uint32 values. To get the distance offset the actual match
  62. // distance has to be decreased by 1. A distance offset of 0xffffffff (eos)
  63. // indicates the end of the stream.
  64. func (dc *distCodec) Encode(e *rangeEncoder, dist uint32, l uint32) (err error) {
  65. // Compute the posSlot using nlz32
  66. var posSlot uint32
  67. var bits uint32
  68. if dist < startPosModel {
  69. posSlot = dist
  70. } else {
  71. bits = uint32(30 - nlz32(dist))
  72. posSlot = startPosModel - 2 + (bits << 1)
  73. posSlot += (dist >> uint(bits)) & 1
  74. }
  75. if err = dc.posSlotCodecs[lenState(l)].Encode(e, posSlot); err != nil {
  76. return
  77. }
  78. switch {
  79. case posSlot < startPosModel:
  80. return nil
  81. case posSlot < endPosModel:
  82. tc := &dc.posModel[posSlot-startPosModel]
  83. return tc.Encode(dist, e)
  84. }
  85. dic := directCodec(bits - alignBits)
  86. if err = dic.Encode(e, dist>>alignBits); err != nil {
  87. return
  88. }
  89. return dc.alignCodec.Encode(dist, e)
  90. }
  91. // Decode decodes the distance offset using the parameter l. The dist value
  92. // 0xffffffff (eos) indicates the end of the stream. Add one to the distance
  93. // offset to get the actual match distance.
  94. func (dc *distCodec) Decode(d *rangeDecoder, l uint32) (dist uint32, err error) {
  95. posSlot, err := dc.posSlotCodecs[lenState(l)].Decode(d)
  96. if err != nil {
  97. return
  98. }
  99. // posSlot equals distance
  100. if posSlot < startPosModel {
  101. return posSlot, nil
  102. }
  103. // posSlot uses the individual models
  104. bits := (posSlot >> 1) - 1
  105. dist = (2 | (posSlot & 1)) << bits
  106. var u uint32
  107. if posSlot < endPosModel {
  108. tc := &dc.posModel[posSlot-startPosModel]
  109. if u, err = tc.Decode(d); err != nil {
  110. return 0, err
  111. }
  112. dist += u
  113. return dist, nil
  114. }
  115. // posSlots use direct encoding and a single model for the four align
  116. // bits.
  117. dic := directCodec(bits - alignBits)
  118. if u, err = dic.Decode(d); err != nil {
  119. return 0, err
  120. }
  121. dist += u << alignBits
  122. if u, err = dc.alignCodec.Decode(d); err != nil {
  123. return 0, err
  124. }
  125. dist += u
  126. return dist, nil
  127. }