github.com/XiaoMi/Gaea@v1.2.5/parser/tidb-types/binary_literal.go (about) 1 // Copyright 2017 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 14 package types 15 16 import ( 17 "bytes" 18 "encoding/binary" 19 "encoding/hex" 20 "fmt" 21 "math" 22 "strconv" 23 "strings" 24 25 "github.com/pingcap/errors" 26 27 "github.com/XiaoMi/Gaea/parser/stmtctx" 28 ) 29 30 // BinaryLiteral is the internal type for storing bit / hex literal type. 31 type BinaryLiteral []byte 32 33 // BitLiteral is the bit literal type. 34 type BitLiteral BinaryLiteral 35 36 // HexLiteral is the hex literal type. 37 type HexLiteral BinaryLiteral 38 39 // ZeroBinaryLiteral is a BinaryLiteral literal with zero value. 40 var ZeroBinaryLiteral = BinaryLiteral{} 41 42 func trimLeadingZeroBytes(bytes []byte) []byte { 43 if len(bytes) == 0 { 44 return bytes 45 } 46 pos, posMax := 0, len(bytes)-1 47 for ; pos < posMax; pos++ { 48 if bytes[pos] != 0 { 49 break 50 } 51 } 52 return bytes[pos:] 53 } 54 55 // NewBinaryLiteralFromUint creates a new BinaryLiteral instance by the given uint value in BitEndian. 56 // byteSize will be used as the length of the new BinaryLiteral, with leading bytes filled to zero. 57 // If byteSize is -1, the leading zeros in new BinaryLiteral will be trimmed. 58 func NewBinaryLiteralFromUint(value uint64, byteSize int) BinaryLiteral { 59 if byteSize != -1 && (byteSize < 1 || byteSize > 8) { 60 panic("Invalid byteSize") 61 } 62 buf := make([]byte, 8) 63 binary.BigEndian.PutUint64(buf, value) 64 if byteSize == -1 { 65 buf = trimLeadingZeroBytes(buf) 66 } else { 67 buf = buf[8-byteSize:] 68 } 69 return buf 70 } 71 72 // String implements fmt.Stringer interface. 73 func (b BinaryLiteral) String() string { 74 if len(b) == 0 { 75 return "" 76 } 77 return "0x" + hex.EncodeToString(b) 78 } 79 80 // ToString returns the string representation for the literal. 81 func (b BinaryLiteral) ToString() string { 82 return string(b) 83 } 84 85 // ToBitLiteralString returns the bit literal representation for the literal. 86 func (b BinaryLiteral) ToBitLiteralString(trimLeadingZero bool) string { 87 if len(b) == 0 { 88 return "b''" 89 } 90 var buf bytes.Buffer 91 for _, data := range b { 92 fmt.Fprintf(&buf, "%08b", data) 93 } 94 ret := buf.Bytes() 95 if trimLeadingZero { 96 ret = bytes.TrimLeft(ret, "0") 97 if len(ret) == 0 { 98 ret = []byte{'0'} 99 } 100 } 101 return fmt.Sprintf("b'%s'", string(ret)) 102 } 103 104 // ToInt returns the int value for the literal. 105 func (b BinaryLiteral) ToInt(sc *stmtctx.StatementContext) (uint64, error) { 106 buf := trimLeadingZeroBytes(b) 107 length := len(buf) 108 if length == 0 { 109 return 0, nil 110 } 111 if length > 8 { 112 var err error = ErrTruncatedWrongVal.GenWithStackByArgs("BINARY", b) 113 if sc != nil { 114 err = sc.HandleTruncate(err) 115 } 116 return math.MaxUint64, err 117 } 118 // Note: the byte-order is BigEndian. 119 val := uint64(buf[0]) 120 for i := 1; i < length; i++ { 121 val = (val << 8) | uint64(buf[i]) 122 } 123 return val, nil 124 } 125 126 // Compare compares BinaryLiteral to another one 127 func (b BinaryLiteral) Compare(b2 BinaryLiteral) int { 128 bufB := trimLeadingZeroBytes(b) 129 bufB2 := trimLeadingZeroBytes(b2) 130 if len(bufB) > len(bufB2) { 131 return 1 132 } 133 if len(bufB) < len(bufB2) { 134 return -1 135 } 136 return bytes.Compare(bufB, bufB2) 137 } 138 139 // ParseBitStr parses bit string. 140 // The string format can be b'val', B'val' or 0bval, val must be 0 or 1. 141 // See https://dev.mysql.com/doc/refman/5.7/en/bit-value-literals.html 142 func ParseBitStr(s string) (BinaryLiteral, error) { 143 if len(s) == 0 { 144 return nil, errors.Errorf("invalid empty string for parsing bit type") 145 } 146 147 if s[0] == 'b' || s[0] == 'B' { 148 // format is b'val' or B'val' 149 s = strings.Trim(s[1:], "'") 150 } else if strings.HasPrefix(s, "0b") { 151 s = s[2:] 152 } else { 153 // here means format is not b'val', B'val' or 0bval. 154 return nil, errors.Errorf("invalid bit type format %s", s) 155 } 156 157 if len(s) == 0 { 158 return ZeroBinaryLiteral, nil 159 } 160 161 alignedLength := (len(s) + 7) &^ 7 162 s = ("00000000" + s)[len(s)+8-alignedLength:] // Pad with zero (slice from `-alignedLength`) 163 byteLength := len(s) >> 3 164 buf := make([]byte, byteLength) 165 166 for i := 0; i < byteLength; i++ { 167 strPosition := i << 3 168 val, err := strconv.ParseUint(s[strPosition:strPosition+8], 2, 8) 169 if err != nil { 170 return nil, errors.Trace(err) 171 } 172 buf[i] = byte(val) 173 } 174 175 return buf, nil 176 } 177 178 // NewBitLiteral parses bit string as BitLiteral type. 179 func NewBitLiteral(s string) (BitLiteral, error) { 180 b, err := ParseBitStr(s) 181 if err != nil { 182 return BitLiteral{}, err 183 } 184 return BitLiteral(b), nil 185 } 186 187 // ParseHexStr parses hexadecimal string literal. 188 // See https://dev.mysql.com/doc/refman/5.7/en/hexadecimal-literals.html 189 func ParseHexStr(s string) (BinaryLiteral, error) { 190 if len(s) == 0 { 191 return nil, errors.Errorf("invalid empty string for parsing hexadecimal literal") 192 } 193 194 if s[0] == 'x' || s[0] == 'X' { 195 // format is x'val' or X'val' 196 s = strings.Trim(s[1:], "'") 197 if len(s)%2 != 0 { 198 return nil, errors.Errorf("invalid hexadecimal format, must even numbers, but %d", len(s)) 199 } 200 } else if strings.HasPrefix(s, "0x") { 201 s = s[2:] 202 } else { 203 // here means format is not x'val', X'val' or 0xval. 204 return nil, errors.Errorf("invalid hexadecimal format %s", s) 205 } 206 207 if len(s) == 0 { 208 return ZeroBinaryLiteral, nil 209 } 210 211 if len(s)%2 != 0 { 212 s = "0" + s 213 } 214 buf, err := hex.DecodeString(s) 215 if err != nil { 216 return nil, errors.Trace(err) 217 } 218 return buf, nil 219 } 220 221 // NewHexLiteral parses hexadecimal string as HexLiteral type. 222 func NewHexLiteral(s string) (HexLiteral, error) { 223 h, err := ParseHexStr(s) 224 if err != nil { 225 return HexLiteral{}, err 226 } 227 return HexLiteral(h), nil 228 }