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.

183 lines
4.1 KiB

  1. // Copyright 2015 PingCAP, Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. package codec
  14. import (
  15. "bytes"
  16. "math/big"
  17. "github.com/juju/errors"
  18. "github.com/pingcap/tidb/mysql"
  19. )
  20. const (
  21. negativeSign int64 = 8
  22. zeroSign int64 = 16
  23. positiveSign int64 = 24
  24. )
  25. func codecSign(value int64) int64 {
  26. if value < 0 {
  27. return negativeSign
  28. }
  29. return positiveSign
  30. }
  31. func encodeExp(expValue int64, expSign int64, valSign int64) int64 {
  32. if expSign == negativeSign {
  33. expValue = -expValue
  34. }
  35. if expSign != valSign {
  36. expValue = ^expValue
  37. }
  38. return expValue
  39. }
  40. func decodeExp(expValue int64, expSign int64, valSign int64) int64 {
  41. if expSign != valSign {
  42. expValue = ^expValue
  43. }
  44. if expSign == negativeSign {
  45. expValue = -expValue
  46. }
  47. return expValue
  48. }
  49. func codecValue(value []byte, valSign int64) {
  50. if valSign == negativeSign {
  51. reverseBytes(value)
  52. }
  53. }
  54. // EncodeDecimal encodes a decimal d into a byte slice which can be sorted lexicographically later.
  55. // EncodeDecimal guarantees that the encoded value is in ascending order for comparison.
  56. // Decimal encoding:
  57. // Byte -> value sign
  58. // Byte -> exp sign
  59. // EncodeInt -> exp value
  60. // EncodeBytes -> abs value bytes
  61. func EncodeDecimal(b []byte, d mysql.Decimal) []byte {
  62. if d.Equals(mysql.ZeroDecimal) {
  63. return append(b, byte(zeroSign))
  64. }
  65. v := d.BigIntValue()
  66. valSign := codecSign(int64(v.Sign()))
  67. absVal := new(big.Int)
  68. absVal.Abs(v)
  69. value := []byte(absVal.String())
  70. // Trim right side "0", like "12.34000" -> "12.34" or "0.1234000" -> "0.1234".
  71. if d.Exponent() != 0 {
  72. value = bytes.TrimRight(value, "0")
  73. }
  74. // Get exp and value, format is "value":"exp".
  75. // like "12.34" -> "0.1234":"2".
  76. // like "-0.01234" -> "-0.1234":"-1".
  77. exp := int64(0)
  78. div := big.NewInt(10)
  79. for ; ; exp++ {
  80. if absVal.Sign() == 0 {
  81. break
  82. }
  83. absVal = absVal.Div(absVal, div)
  84. }
  85. expVal := exp + int64(d.Exponent())
  86. expSign := codecSign(expVal)
  87. // For negtive exp, do bit reverse for exp.
  88. // For negtive decimal, do bit reverse for exp and value.
  89. expVal = encodeExp(expVal, expSign, valSign)
  90. codecValue(value, valSign)
  91. b = append(b, byte(valSign))
  92. b = append(b, byte(expSign))
  93. b = EncodeInt(b, expVal)
  94. b = EncodeBytes(b, value)
  95. return b
  96. }
  97. // DecodeDecimal decodes bytes to decimal.
  98. // DecodeFloat decodes a float from a byte slice
  99. // Decimal decoding:
  100. // Byte -> value sign
  101. // Byte -> exp sign
  102. // DecodeInt -> exp value
  103. // DecodeBytes -> abs value bytes
  104. func DecodeDecimal(b []byte) ([]byte, mysql.Decimal, error) {
  105. var (
  106. r = b
  107. d mysql.Decimal
  108. err error
  109. )
  110. // Decode value sign.
  111. valSign := int64(r[0])
  112. r = r[1:]
  113. if valSign == zeroSign {
  114. d, err = mysql.ParseDecimal("0")
  115. return r, d, errors.Trace(err)
  116. }
  117. // Decode exp sign.
  118. expSign := int64(r[0])
  119. r = r[1:]
  120. // Decode exp value.
  121. expVal := int64(0)
  122. r, expVal, err = DecodeInt(r)
  123. if err != nil {
  124. return r, d, errors.Trace(err)
  125. }
  126. expVal = decodeExp(expVal, expSign, valSign)
  127. // Decode abs value bytes.
  128. value := []byte{}
  129. r, value, err = DecodeBytes(r)
  130. if err != nil {
  131. return r, d, errors.Trace(err)
  132. }
  133. codecValue(value, valSign)
  134. // Generate decimal string value.
  135. var decimalStr []byte
  136. if valSign == negativeSign {
  137. decimalStr = append(decimalStr, '-')
  138. }
  139. if expVal <= 0 {
  140. // Like decimal "0.1234" or "0.01234".
  141. decimalStr = append(decimalStr, '0')
  142. decimalStr = append(decimalStr, '.')
  143. decimalStr = append(decimalStr, bytes.Repeat([]byte{'0'}, -int(expVal))...)
  144. decimalStr = append(decimalStr, value...)
  145. } else {
  146. // Like decimal "12.34".
  147. decimalStr = append(decimalStr, value[:expVal]...)
  148. decimalStr = append(decimalStr, '.')
  149. decimalStr = append(decimalStr, value[expVal:]...)
  150. }
  151. d, err = mysql.ParseDecimal(string(decimalStr))
  152. return r, d, errors.Trace(err)
  153. }