78 lines
2.1 KiB
Go
78 lines
2.1 KiB
Go
package mysql
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
)
|
|
|
|
// DecodeDecimal decodes a decimal value.
|
|
// Implementation borrowed from https://github.com/siddontang/go-mysql/
|
|
func DecodeDecimal(data []byte, precision int, decimals int) (string, int) {
|
|
const digitsPerInteger int = 9
|
|
var compressedBytes = [...]int{0, 1, 1, 2, 2, 3, 3, 4, 4, 4}
|
|
|
|
decodeDecimalDecompressValue := func(compIndx int, data []byte, mask uint8) (size int, value uint32) {
|
|
size = compressedBytes[compIndx]
|
|
databuff := make([]byte, size)
|
|
for i := 0; i < size; i++ {
|
|
databuff[i] = data[i] ^ mask
|
|
}
|
|
value = uint32(DecodeVarLen64BigEndian(databuff))
|
|
return
|
|
}
|
|
// See python mysql replication and https://github.com/jeremycole/mysql_binlog
|
|
integral := (precision - decimals)
|
|
uncompIntegral := int(integral / digitsPerInteger)
|
|
uncompFractional := int(decimals / digitsPerInteger)
|
|
compIntegral := integral - (uncompIntegral * digitsPerInteger)
|
|
compFractional := decimals - (uncompFractional * digitsPerInteger)
|
|
|
|
binSize := uncompIntegral*4 + compressedBytes[compIntegral] +
|
|
uncompFractional*4 + compressedBytes[compFractional]
|
|
|
|
buf := make([]byte, binSize)
|
|
copy(buf, data[:binSize])
|
|
|
|
// Must copy the data for later change
|
|
data = buf
|
|
|
|
// Support negative
|
|
// The sign is encoded in the high bit of the the byte
|
|
// But this bit can also be used in the value
|
|
value := uint32(data[0])
|
|
var res bytes.Buffer
|
|
var mask uint32
|
|
if value&0x80 == 0 {
|
|
mask = uint32((1 << 32) - 1)
|
|
res.WriteString("-")
|
|
}
|
|
|
|
// Clear sign
|
|
data[0] ^= 0x80
|
|
|
|
pos, value := decodeDecimalDecompressValue(compIntegral, data, uint8(mask))
|
|
res.WriteString(fmt.Sprintf("%d", value))
|
|
|
|
for i := 0; i < uncompIntegral; i++ {
|
|
value = binary.BigEndian.Uint32(data[pos:]) ^ mask
|
|
pos += 4
|
|
res.WriteString(fmt.Sprintf("%09d", value))
|
|
}
|
|
|
|
res.WriteString(".")
|
|
|
|
for i := 0; i < uncompFractional; i++ {
|
|
value = binary.BigEndian.Uint32(data[pos:]) ^ mask
|
|
pos += 4
|
|
res.WriteString(fmt.Sprintf("%09d", value))
|
|
}
|
|
|
|
if size, value := decodeDecimalDecompressValue(compFractional, data[pos:], uint8(mask)); size > 0 {
|
|
res.WriteString(fmt.Sprintf("%0*d", compFractional, value))
|
|
pos += size
|
|
}
|
|
|
|
return res.String(), pos
|
|
}
|