github.com/pingcap/tidb/parser@v0.0.0-20231013125129-93a834a6bf8d/test_driver/test_driver_mydecimal.go (about)

     1  // Copyright 2019 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  //go:build !codes
    15  // +build !codes
    16  
    17  package test_driver
    18  
    19  const panicInfo = "This branch is not implemented. " +
    20  	"This is because you are trying to test something specific to TiDB's MyDecimal implementation. " +
    21  	"It is recommended to do this in TiDB repository."
    22  
    23  // constant values.
    24  const (
    25  	maxWordBufLen = 9 // A MyDecimal holds 9 words.
    26  	digitsPerWord = 9 // A word holds 9 digits.
    27  	digMask       = 100000000
    28  )
    29  
    30  var (
    31  	wordBufLen = 9
    32  )
    33  
    34  // fixWordCntError limits word count in wordBufLen, and returns overflow or truncate error.
    35  func fixWordCntError(wordsInt, wordsFrac int) (newWordsInt int, newWordsFrac int, err error) {
    36  	if wordsInt+wordsFrac > wordBufLen {
    37  		panic(panicInfo)
    38  	}
    39  	return wordsInt, wordsFrac, nil
    40  }
    41  
    42  /*
    43  countLeadingZeroes returns the number of leading zeroes that can be removed from fraction.
    44  
    45  @param   i    start index
    46  @param   word value to compare against list of powers of 10
    47  */
    48  func countLeadingZeroes(i int, word int32) int {
    49  	leading := 0
    50  	for word < pow10(i) {
    51  		i--
    52  		leading++
    53  	}
    54  	return leading
    55  }
    56  
    57  func digitsToWords(digits int) int {
    58  	return (digits + digitsPerWord - 1) / digitsPerWord
    59  }
    60  
    61  // MyDecimal represents a decimal value.
    62  type MyDecimal struct {
    63  	digitsInt int8 // the number of *decimal* digits before the point.
    64  
    65  	digitsFrac int8 // the number of decimal digits after the point.
    66  
    67  	resultFrac int8 // result fraction digits.
    68  
    69  	negative bool
    70  
    71  	// wordBuf is an array of int32 words.
    72  	// A word is an int32 value can hold 9 digits.(0 <= word < wordBase)
    73  	wordBuf [maxWordBufLen]int32
    74  }
    75  
    76  // String returns the decimal string representation rounded to resultFrac.
    77  func (d *MyDecimal) String() string {
    78  	tmp := *d
    79  	return string(tmp.ToString())
    80  }
    81  
    82  func (d *MyDecimal) stringSize() int {
    83  	// sign, zero integer and dot.
    84  	return int(d.digitsInt + d.digitsFrac + 3)
    85  }
    86  
    87  func (d *MyDecimal) removeLeadingZeros() (wordIdx int, digitsInt int) {
    88  	digitsInt = int(d.digitsInt)
    89  	i := ((digitsInt - 1) % digitsPerWord) + 1
    90  	for digitsInt > 0 && d.wordBuf[wordIdx] == 0 {
    91  		digitsInt -= i
    92  		i = digitsPerWord
    93  		wordIdx++
    94  	}
    95  	if digitsInt > 0 {
    96  		digitsInt -= countLeadingZeroes((digitsInt-1)%digitsPerWord, d.wordBuf[wordIdx])
    97  	} else {
    98  		digitsInt = 0
    99  	}
   100  	return
   101  }
   102  
   103  // ToString converts decimal to its printable string representation without rounding.
   104  //
   105  //	RETURN VALUE
   106  //
   107  //	    str       - result string
   108  //	    errCode   - eDecOK/eDecTruncate/eDecOverflow
   109  func (d *MyDecimal) ToString() (str []byte) {
   110  	str = make([]byte, d.stringSize())
   111  	digitsFrac := int(d.digitsFrac)
   112  	wordStartIdx, digitsInt := d.removeLeadingZeros()
   113  	if digitsInt+digitsFrac == 0 {
   114  		digitsInt = 1
   115  		wordStartIdx = 0
   116  	}
   117  
   118  	digitsIntLen := digitsInt
   119  	if digitsIntLen == 0 {
   120  		digitsIntLen = 1
   121  	}
   122  	digitsFracLen := digitsFrac
   123  	length := digitsIntLen + digitsFracLen
   124  	if d.negative {
   125  		length++
   126  	}
   127  	if digitsFrac > 0 {
   128  		length++
   129  	}
   130  	str = str[:length]
   131  	strIdx := 0
   132  	if d.negative {
   133  		str[strIdx] = '-'
   134  		strIdx++
   135  	}
   136  	var fill int
   137  	if digitsFrac > 0 {
   138  		fracIdx := strIdx + digitsIntLen
   139  		fill = digitsFracLen - digitsFrac
   140  		wordIdx := wordStartIdx + digitsToWords(digitsInt)
   141  		str[fracIdx] = '.'
   142  		fracIdx++
   143  		for ; digitsFrac > 0; digitsFrac -= digitsPerWord {
   144  			x := d.wordBuf[wordIdx]
   145  			wordIdx++
   146  			for i := myMin(digitsFrac, digitsPerWord); i > 0; i-- {
   147  				y := x / digMask
   148  				str[fracIdx] = byte(y) + '0'
   149  				fracIdx++
   150  				x -= y * digMask
   151  				x *= 10
   152  			}
   153  		}
   154  		for ; fill > 0; fill-- {
   155  			str[fracIdx] = '0'
   156  			fracIdx++
   157  		}
   158  	}
   159  	fill = digitsIntLen - digitsInt
   160  	if digitsInt == 0 {
   161  		fill-- /* symbol 0 before digital point */
   162  	}
   163  	for ; fill > 0; fill-- {
   164  		str[strIdx] = '0'
   165  		strIdx++
   166  	}
   167  	if digitsInt > 0 {
   168  		strIdx += digitsInt
   169  		wordIdx := wordStartIdx + digitsToWords(digitsInt)
   170  		for ; digitsInt > 0; digitsInt -= digitsPerWord {
   171  			wordIdx--
   172  			x := d.wordBuf[wordIdx]
   173  			for i := myMin(digitsInt, digitsPerWord); i > 0; i-- {
   174  				y := x / 10
   175  				strIdx--
   176  				str[strIdx] = '0' + byte(x-y*10)
   177  				x = y
   178  			}
   179  		}
   180  	} else {
   181  		str[strIdx] = '0'
   182  	}
   183  	return
   184  }
   185  
   186  // FromString parses decimal from string.
   187  func (d *MyDecimal) FromString(str []byte) error {
   188  	for i := 0; i < len(str); i++ {
   189  		if !isSpace(str[i]) {
   190  			str = str[i:]
   191  			break
   192  		}
   193  	}
   194  	if len(str) == 0 {
   195  		panic(panicInfo)
   196  	}
   197  	switch str[0] {
   198  	case '-':
   199  		d.negative = true
   200  		fallthrough
   201  	case '+':
   202  		str = str[1:]
   203  	}
   204  	var strIdx int
   205  	for strIdx < len(str) && isDigit(str[strIdx]) {
   206  		strIdx++
   207  	}
   208  	digitsInt := strIdx
   209  	var digitsFrac int
   210  	var endIdx int
   211  	if strIdx < len(str) && str[strIdx] == '.' {
   212  		endIdx = strIdx + 1
   213  		for endIdx < len(str) && isDigit(str[endIdx]) {
   214  			endIdx++
   215  		}
   216  		digitsFrac = endIdx - strIdx - 1
   217  	} else {
   218  		digitsFrac = 0
   219  		endIdx = strIdx
   220  	}
   221  	if digitsInt+digitsFrac == 0 {
   222  		panic(panicInfo)
   223  	}
   224  	wordsInt := digitsToWords(digitsInt)
   225  	wordsFrac := digitsToWords(digitsFrac)
   226  	wordsInt, _, err := fixWordCntError(wordsInt, wordsFrac)
   227  	if err != nil {
   228  		panic(panicInfo)
   229  	}
   230  	d.digitsInt = int8(digitsInt)
   231  	d.digitsFrac = int8(digitsFrac)
   232  	wordIdx := wordsInt
   233  	strIdxTmp := strIdx
   234  	var word int32
   235  	var innerIdx int
   236  	for digitsInt > 0 {
   237  		digitsInt--
   238  		strIdx--
   239  		word += int32(str[strIdx]-'0') * pow10(innerIdx)
   240  		innerIdx++
   241  		if innerIdx == digitsPerWord {
   242  			wordIdx--
   243  			d.wordBuf[wordIdx] = word
   244  			word = 0
   245  			innerIdx = 0
   246  		}
   247  	}
   248  	if innerIdx != 0 {
   249  		wordIdx--
   250  		d.wordBuf[wordIdx] = word
   251  	}
   252  
   253  	wordIdx = wordsInt
   254  	strIdx = strIdxTmp
   255  	word = 0
   256  	innerIdx = 0
   257  	for digitsFrac > 0 {
   258  		digitsFrac--
   259  		strIdx++
   260  		word = int32(str[strIdx]-'0') + word*10
   261  		innerIdx++
   262  		if innerIdx == digitsPerWord {
   263  			d.wordBuf[wordIdx] = word
   264  			wordIdx++
   265  			word = 0
   266  			innerIdx = 0
   267  		}
   268  	}
   269  	if innerIdx != 0 {
   270  		d.wordBuf[wordIdx] = word * pow10(digitsPerWord-innerIdx)
   271  	}
   272  	if endIdx+1 <= len(str) && (str[endIdx] == 'e' || str[endIdx] == 'E') {
   273  		panic(panicInfo)
   274  	}
   275  	allZero := true
   276  	for i := 0; i < wordBufLen; i++ {
   277  		if d.wordBuf[i] != 0 {
   278  			allZero = false
   279  			break
   280  		}
   281  	}
   282  	if allZero {
   283  		d.negative = false
   284  	}
   285  	d.resultFrac = d.digitsFrac
   286  	return err
   287  }