gitee.com/runner.mei/dm@v0.0.0-20220207044607-a9ba0dc20bf7/o.go (about)

     1  /*
     2   * Copyright (c) 2000-2018, 达梦数据库有限公司.
     3   * All rights reserved.
     4   */
     5  package dm
     6  
     7  import (
     8  	"database/sql/driver"
     9  	"math/big"
    10  	"reflect"
    11  	"strconv"
    12  	"strings"
    13  )
    14  
    15  const (
    16  	XDEC_MAX_PREC int = 38
    17  	XDEC_SIZE         = 21
    18  
    19  	FLAG_ZERO     int = 0x80
    20  	FLAG_POSITIVE int = 0xC1
    21  	FLAG_NEGTIVE  int = 0x3E
    22  	EXP_MAX       int = 0xFF - 1 - FLAG_POSITIVE
    23  	EXP_MIN       int = FLAG_NEGTIVE + 1 - 0x7F
    24  
    25  	NUM_POSITIVE int = 1
    26  	NUM_NEGTIVE  int = 101
    27  )
    28  
    29  type DmDecimal struct {
    30  	sign   int
    31  	weight int
    32  	prec   int
    33  	scale  int
    34  	digits string
    35  
    36  	Valid bool
    37  }
    38  
    39  func NewDecimalFromInt64(x int64) (*DmDecimal, error) {
    40  	return NewDecimalFromBigInt(big.NewInt(x))
    41  }
    42  
    43  func (d DmDecimal) ToInt64() int64 {
    44  	return d.ToBigInt().Int64()
    45  }
    46  
    47  func NewDecimalFromFloat64(x float64) (*DmDecimal, error) {
    48  	return NewDecimalFromBigFloat(big.NewFloat(x))
    49  }
    50  
    51  func (d DmDecimal) ToFloat64() float64 {
    52  	f, _ := d.ToBigFloat().Float64()
    53  	return f
    54  }
    55  
    56  func NewDecimalFromBigInt(bigInt *big.Int) (*DmDecimal, error) {
    57  	return newDecimal(bigInt, len(bigInt.String()), 0)
    58  }
    59  
    60  func (d DmDecimal) ToBigInt() *big.Int {
    61  	if d.isZero() {
    62  		return big.NewInt(0)
    63  	}
    64  	var digits = d.digits
    65  	if d.sign < 0 {
    66  		digits = "-" + digits
    67  	}
    68  	i1, ok := new(big.Int).SetString(digits, 10)
    69  	if !ok {
    70  		return nil
    71  	}
    72  	if d.weight > 0 {
    73  		i2, ok := new(big.Int).SetString("1"+strings.Repeat("0", d.weight), 10)
    74  		if !ok {
    75  			return nil
    76  		}
    77  		i1.Mul(i1, i2)
    78  	} else if d.weight < 0 {
    79  		i2, ok := new(big.Int).SetString("1"+strings.Repeat("0", -d.weight), 10)
    80  		if !ok {
    81  			return nil
    82  		}
    83  		i1.Quo(i1, i2)
    84  	}
    85  	return i1
    86  }
    87  
    88  func NewDecimalFromBigFloat(bigFloat *big.Float) (*DmDecimal, error) {
    89  	return newDecimal(bigFloat, int(bigFloat.Prec()), int(bigFloat.Prec()))
    90  }
    91  
    92  func (d DmDecimal) ToBigFloat() *big.Float {
    93  	if d.isZero() {
    94  		return big.NewFloat(0.0)
    95  	}
    96  	var digits = d.digits
    97  	if d.sign < 0 {
    98  		digits = "-" + digits
    99  	}
   100  	f1, ok := new(big.Float).SetString(digits)
   101  	if !ok {
   102  		return nil
   103  	}
   104  	if d.weight > 0 {
   105  		f2, ok := new(big.Float).SetString("1" + strings.Repeat("0", d.weight))
   106  		if !ok {
   107  			return nil
   108  		}
   109  		f1.Mul(f1, f2)
   110  	} else if d.weight < 0 {
   111  		f2, ok := new(big.Float).SetString("1" + strings.Repeat("0", -d.weight))
   112  		if !ok {
   113  			return nil
   114  		}
   115  		f1.Quo(f1, f2)
   116  	}
   117  	return f1
   118  }
   119  
   120  func NewDecimalFromString(s string) (*DmDecimal, error) {
   121  	num, ok := new(big.Float).SetString(strings.TrimSpace(s))
   122  	if !ok {
   123  		return nil, ECGO_DATA_CONVERTION_ERROR.throw()
   124  	}
   125  	return NewDecimalFromBigFloat(num)
   126  }
   127  
   128  func (d DmDecimal) String() string {
   129  
   130  	if d.isZero() {
   131  		return "0"
   132  	}
   133  	digitsStr := d.digits
   134  	if d.weight > 0 {
   135  		digitsStr = digitsStr + strings.Repeat("0", d.weight)
   136  	} else if d.weight < 0 {
   137  		digitsStr = strings.Repeat("0", -d.weight+1) + digitsStr
   138  	}
   139  
   140  	if digitsStr[0] == '0' && digitsStr[1] != '.' {
   141  		digitsStr = digitsStr[1:]
   142  	}
   143  
   144  	if digitsStr[len(digitsStr)-1] == '0' && strings.IndexRune(digitsStr, '.') >= 0 {
   145  		digitsStr = digitsStr[0 : len(digitsStr)-1]
   146  	}
   147  
   148  	if d.sign < 0 {
   149  		digitsStr = "-" + digitsStr
   150  	}
   151  
   152  	return digitsStr
   153  }
   154  
   155  func (d DmDecimal) Sign() int {
   156  	return d.sign
   157  }
   158  
   159  func (dest *DmDecimal) Scan(src interface{}) error {
   160  	if dest == nil {
   161  		return ECGO_STORE_IN_NIL_POINTER.throw()
   162  	}
   163  	switch src := src.(type) {
   164  	case nil:
   165  		*dest = *new(DmDecimal)
   166  
   167  		(*dest).Valid = false
   168  		return nil
   169  	case int, int8, int16, int32, int64:
   170  		d, err := NewDecimalFromInt64(reflect.ValueOf(src).Int())
   171  		if err != nil {
   172  			return err
   173  		}
   174  		*dest = *d
   175  		return nil
   176  	case uint, uint8, uint16, uint32, uint64:
   177  		d, err := NewDecimalFromBigInt(new(big.Int).SetUint64(reflect.ValueOf(src).Uint()))
   178  		if err != nil {
   179  			return err
   180  		}
   181  		*dest = *d
   182  		return nil
   183  	case string:
   184  		d, err := NewDecimalFromString(src)
   185  		if err != nil {
   186  			return err
   187  		}
   188  		*dest = *d
   189  		return nil
   190  	case *DmDecimal:
   191  		*dest = *src
   192  		return nil
   193  	default:
   194  		return UNSUPPORTED_SCAN
   195  	}
   196  }
   197  
   198  func (d DmDecimal) Value() (driver.Value, error) {
   199  	if !d.Valid {
   200  		return nil, nil
   201  	}
   202  	return d, nil
   203  }
   204  
   205  func newDecimal(dec interface{}, prec int, scale int) (*DmDecimal, error) {
   206  	d := &DmDecimal{
   207  		prec:  prec,
   208  		scale: scale,
   209  		Valid: true,
   210  	}
   211  	if isFloat(DECIMAL, scale) {
   212  		d.prec = getFloatPrec(prec)
   213  		d.scale = -1
   214  	}
   215  	switch de := dec.(type) {
   216  	case *big.Int:
   217  		d.sign = de.Sign()
   218  
   219  		if d.isZero() {
   220  			return d, nil
   221  		}
   222  		str := de.String()
   223  
   224  		if d.sign < 0 {
   225  			str = str[1:]
   226  		}
   227  
   228  		if err := checkPrec(len(str), prec); err != nil {
   229  			return d, err
   230  		}
   231  		i := 0
   232  		istart := len(str) - 1
   233  
   234  		for i = istart; i > 0; i-- {
   235  			if str[i] != '0' {
   236  				break
   237  			}
   238  		}
   239  		str = str[:i+1]
   240  		d.weight += istart - i
   241  
   242  		if isOdd(d.weight) {
   243  			str += "0"
   244  			d.weight -= 1
   245  		}
   246  		if isOdd(len(str)) {
   247  			str = "0" + str
   248  		}
   249  		d.digits = str
   250  	case *big.Float:
   251  		d.sign = de.Sign()
   252  
   253  		if d.isZero() {
   254  			return d, nil
   255  		}
   256  		str := de.Text('f', -1)
   257  
   258  		if d.sign < 0 {
   259  			str = str[1:]
   260  		}
   261  
   262  		pointIndex := strings.IndexByte(str, '.')
   263  		i, istart, length := 0, 0, len(str)
   264  
   265  		if pointIndex != -1 {
   266  			if str[0] == '0' {
   267  
   268  				istart = 2
   269  				for i = istart; i < length; i++ {
   270  					if str[i] != '0' {
   271  						break
   272  					}
   273  				}
   274  				str = str[i:]
   275  				d.weight -= i - istart + len(str)
   276  			} else {
   277  				str = str[:pointIndex] + str[pointIndex+1:]
   278  				d.weight -= length - pointIndex - 1
   279  			}
   280  		}
   281  
   282  		length = len(str)
   283  		istart = length - 1
   284  		for i = istart; i > 0; i-- {
   285  			if str[i] != '0' {
   286  				break
   287  			}
   288  		}
   289  		str = str[:i+1] + str[length:]
   290  		d.weight += istart - i
   291  
   292  		if isOdd(d.weight) {
   293  			str += "0"
   294  			d.weight -= 1
   295  		}
   296  		if isOdd(len(str)) {
   297  			str = "0" + str
   298  		}
   299  		d.digits = str
   300  	case []byte:
   301  		return decodeDecimal(de, prec, scale)
   302  	}
   303  	return d, nil
   304  }
   305  
   306  func (d DmDecimal) encodeDecimal() ([]byte, error) {
   307  	if d.isZero() {
   308  		return []byte{byte(FLAG_ZERO)}, nil
   309  	}
   310  	exp := (d.weight+len(d.digits))/2 - 1
   311  	if exp > EXP_MAX || exp < EXP_MIN {
   312  		return nil, ECGO_DATA_TOO_LONG.throw()
   313  	}
   314  	validLen := len(d.digits)/2 + 1
   315  
   316  	if d.sign < 0 && validLen >= XDEC_SIZE {
   317  		validLen = XDEC_SIZE - 1
   318  	} else if validLen > XDEC_SIZE {
   319  		validLen = XDEC_SIZE
   320  	}
   321  	retLen := validLen
   322  	if d.sign < 0 {
   323  		retLen = validLen + 1
   324  	}
   325  	retBytes := make([]byte, retLen)
   326  	if d.sign > 0 {
   327  		retBytes[0] = byte(exp + FLAG_POSITIVE)
   328  	} else {
   329  		retBytes[0] = byte(FLAG_NEGTIVE - exp)
   330  	}
   331  
   332  	ibytes := 1
   333  	for ichar := 0; ibytes < validLen; {
   334  		digit1, err := strconv.Atoi(string(d.digits[ichar]))
   335  		if err != nil {
   336  			return nil, err
   337  		}
   338  		ichar++
   339  		digit2, err := strconv.Atoi(string(d.digits[ichar]))
   340  		ichar++
   341  		if err != nil {
   342  			return nil, err
   343  		}
   344  
   345  		digit := digit1*10 + digit2
   346  		if d.sign > 0 {
   347  			retBytes[ibytes] = byte(digit + NUM_POSITIVE)
   348  		} else {
   349  			retBytes[ibytes] = byte(NUM_NEGTIVE - digit)
   350  		}
   351  		ibytes++
   352  	}
   353  	if d.sign < 0 && ibytes < retLen {
   354  		retBytes[ibytes] = 0x66
   355  		ibytes++
   356  	}
   357  	if ibytes < retLen {
   358  		retBytes[ibytes] = 0x00
   359  	}
   360  	return retBytes, nil
   361  }
   362  
   363  func decodeDecimal(values []byte, prec int, scale int) (*DmDecimal, error) {
   364  	var decimal = &DmDecimal{
   365  		prec:   prec,
   366  		scale:  scale,
   367  		sign:   0,
   368  		weight: 0,
   369  		Valid:  true,
   370  	}
   371  	if values == nil || len(values) == 0 || len(values) > XDEC_SIZE {
   372  		return nil, ECGO_FATAL_ERROR.throw()
   373  	}
   374  	if values[0] == byte(FLAG_ZERO) || len(values) == 1 {
   375  		return decimal, nil
   376  	}
   377  	if values[0]&byte(FLAG_ZERO) != 0 {
   378  		decimal.sign = 1
   379  	} else {
   380  		decimal.sign = -1
   381  	}
   382  
   383  	var flag = int(Dm_build_623.Dm_build_743(values, 0))
   384  	var exp int
   385  	if decimal.sign > 0 {
   386  		exp = flag - FLAG_POSITIVE
   387  	} else {
   388  		exp = FLAG_NEGTIVE - flag
   389  	}
   390  	var digit = 0
   391  	var sf = ""
   392  	for ival := 1; ival < len(values); ival++ {
   393  		if decimal.sign > 0 {
   394  			digit = int(values[ival]) - NUM_POSITIVE
   395  		} else {
   396  			digit = NUM_NEGTIVE - int(values[ival])
   397  		}
   398  		if digit < 0 || digit > 99 {
   399  			break
   400  		}
   401  		if digit < 10 {
   402  			sf += "0"
   403  		}
   404  		sf += strconv.Itoa(digit)
   405  	}
   406  	decimal.digits = sf
   407  	decimal.weight = exp*2 - (len(decimal.digits) - 2)
   408  
   409  	return decimal, nil
   410  }
   411  
   412  func (d DmDecimal) isZero() bool {
   413  	return d.sign == 0
   414  }
   415  
   416  func checkPrec(len int, prec int) error {
   417  	if prec > 0 && len > prec || len > XDEC_MAX_PREC {
   418  		return ECGO_DATA_TOO_LONG.throw()
   419  	}
   420  	return nil
   421  }
   422  
   423  func isOdd(val int) bool {
   424  	return val%2 != 0
   425  }
   426  
   427  func (d *DmDecimal) checkValid() error {
   428  	if !d.Valid {
   429  		return ECGO_IS_NULL.throw()
   430  	}
   431  	return nil
   432  }