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  }