tidb binary_literal 代码
文件路径:/types/binary_literal.go
/ Copyright 2017 PingCAP, Inc.
/
/ Licensed under the Apache License, Version 2.0 (the "License");
/ you may not use this file except in compliance with the License.
/ You may obtain a copy of the License at
/
/ http://www.apache.org/licenses/LICENSE-2.0
/
/ Unless required by applicable law or agreed to in writing, software
/ distributed under the License is distributed on an "AS IS" BASIS,
/ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/ See the License for the specific language governing permissions and
/ limitations under the License.
package types
import (
"bytes"
"encoding/binary"
"encoding/hex"
"fmt"
"math"
"strconv"
"strings"
"github.com/pingcap/errors"
"github.com/pingcap/tidb/sessionctx/stmtctx"
)
/ BinaryLiteral is the internal type for storing bit / hex literal type.
type BinaryLiteral []byte
/ BitLiteral is the bit literal type.
type BitLiteral BinaryLiteral
/ HexLiteral is the hex literal type.
type HexLiteral BinaryLiteral
/ ZeroBinaryLiteral is a BinaryLiteral literal with zero value.
var ZeroBinaryLiteral = BinaryLiteral{}
func trimLeadingZeroBytes(bytes []byte) []byte {
if len(bytes) == 0 {
return bytes
}
pos, posMax := 0, len(bytes)-1
for ; pos < posMax; pos++ {
if bytes[pos] != 0 {
break
}
}
return bytes[pos:]
}
/ NewBinaryLiteralFromUint creates a new BinaryLiteral instance by the given uint value in BitEndian.
/ byteSize will be used as the length of the new BinaryLiteral, with leading bytes filled to zero.
/ If byteSize is -1, the leading zeros in new BinaryLiteral will be trimmed.
func NewBinaryLiteralFromUint(value uint64, byteSize int) BinaryLiteral {
if byteSize != -1 && (byteSize < 1 || byteSize > 8) {
panic("Invalid byteSize")
}
buf := make([]byte, 8)
binary.BigEndian.PutUint64(buf, value)
if byteSize == -1 {
buf = trimLeadingZeroBytes(buf)
} else {
buf = buf[8-byteSize:]
}
return buf
}
/ String implements fmt.Stringer interface.
func (b BinaryLiteral) String() string {
if len(b) == 0 {
return ""
}
return "0x" + hex.EncodeToString(b)
}
/ ToString returns the string representation for the literal.
func (b BinaryLiteral) ToString() string {
return string(b)
}
/ ToBitLiteralString returns the bit literal representation for the literal.
func (b BinaryLiteral) ToBitLiteralString(trimLeadingZero bool) string {
if len(b) == 0 {
return "b''"
}
var buf bytes.Buffer
for _, data := range b {
fmt.Fprintf(&buf, "%08b", data)
}
ret := buf.Bytes()
if trimLeadingZero {
ret = bytes.TrimLeft(ret, "0")
if len(ret) == 0 {
ret = []byte{'0'}
}
}
return fmt.Sprintf("b'%s'", string(ret))
}
/ ToInt returns the int value for the literal.
func (b BinaryLiteral) ToInt(sc *stmtctx.StatementContext) (uint64, error) {
buf := trimLeadingZeroBytes(b)
length := len(buf)
if length == 0 {
return 0, nil
}
if length > 8 {
var err = ErrTruncatedWrongVal.FastGenByArgs("BINARY", b)
if sc != nil {
err = sc.HandleTruncate(err)
}
return math.MaxUint64, err
}
/ Note: the byte-order is BigEndian.
val := uint64(buf[0])
for i := 1; i < length; i++ {
val = (val << 8) | uint64(buf[i])
}
return val, nil
}
/ Compare compares BinaryLiteral to another one
func (b BinaryLiteral) Compare(b2 BinaryLiteral) int {
bufB := trimLeadingZeroBytes(b)
bufB2 := trimLeadingZeroBytes(b2)
if len(bufB) > len(bufB2) {
return 1
}
if len(bufB) < len(bufB2) {
return -1
}
return bytes.Compare(bufB, bufB2)
}
/ ParseBitStr parses bit string.
/ The string format can be b'val', B'val' or 0bval, val must be 0 or 1.
/ See https://dev.mysql.com/doc/refman/5.7/en/bit-value-literals.html
func ParseBitStr(s string) (BinaryLiteral, error) {
if len(s) == 0 {
return nil, errors.Errorf("invalid empty string for parsing bit type")
}
if s[0] == 'b' || s[0] == 'B' {
/ format is b'val' or B'val'
s = strings.Trim(s[1:], "'")
} else if !strings.HasPrefix(s, "0b") {
/ here means format is not b'val', B'val' or 0bval.
return nil, errors.Errorf("invalid bit type format %s", s)
} else {
s = s[2:]
}
if len(s) == 0 {
return ZeroBinaryLiteral, nil
}
alignedLength := (len(s) + 7) &^ 7
s = ("00000000" + s)[len(s)+8-alignedLength:] / Pad with zero (slice from `-alignedLength`)
byteLength := len(s) >> 3
buf := make([]byte, byteLength)
for i := 0; i < byteLength; i++ {
strPosition := i << 3
val, err := strconv.ParseUint(s[strPosition:strPosition+8], 2, 8)
if err != nil {
return nil, errors.Trace(err)
}
buf[i] = byte(val)
}
return buf, nil
}
/ NewBitLiteral parses bit string as BitLiteral type.
func NewBitLiteral(s string) (BitLiteral, error) {
b, err := ParseBitStr(s)
if err != nil {
return BitLiteral{}, err
}
return BitLiteral(b), nil
}
/ ToString implement ast.BinaryLiteral interface
func (b BitLiteral) ToString() string {
return BinaryLiteral(b).ToString()
}
/ ParseHexStr parses hexadecimal string literal.
/ See https://dev.mysql.com/doc/refman/5.7/en/hexadecimal-literals.html
func ParseHexStr(s string) (BinaryLiteral, error) {
if len(s) == 0 {
return nil, errors.Errorf("invalid empty string for parsing hexadecimal literal")
}
if s[0] == 'x' || s[0] == 'X' {
/ format is x'val' or X'val'
s = strings.Trim(s[1:], "'")
if len(s)%2 != 0 {
return nil, errors.Errorf("invalid hexadecimal format, must even numbers, but %d", len(s))
}
} else if !strings.HasPrefix(s, "0x") {
/ here means format is not x'val', X'val' or 0xval.
return nil, errors.Errorf("invalid hexadecimal format %s", s)
} else {
s = s[2:]
}
if len(s) == 0 {
return ZeroBinaryLiteral, nil
}
if len(s)%2 != 0 {
s = "0" + s
}
buf, err := hex.DecodeString(s)
if err != nil {
return nil, errors.Trace(err)
}
return buf, nil
}
/ NewHexLiteral parses hexadecimal string as HexLiteral type.
func NewHexLiteral(s string) (HexLiteral, error) {
h, err := ParseHexStr(s)
if err != nil {
return HexLiteral{}, err
}
return HexLiteral(h), nil
}
/ ToString implement ast.BinaryLiteral interface
func (b HexLiteral) ToString() string {
return BinaryLiteral(b).ToString()
}