gitee.com/chunanyong/dm@v1.8.12/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  		if len(digitsStr) < -d.weight {
   138  			digitsStr = strings.Repeat("0", -d.weight-len(digitsStr)+1) + digitsStr
   139  		}
   140  		indexOfDot := len(digitsStr) + d.weight
   141  		digitsStr = digitsStr[:indexOfDot] + "." + digitsStr[indexOfDot:]
   142  	}
   143  
   144  	if digitsStr[0] == '0' && digitsStr[1] != '.' {
   145  		digitsStr = digitsStr[1:]
   146  	}
   147  
   148  	if digitsStr[len(digitsStr)-1] == '0' && strings.IndexRune(digitsStr, '.') >= 0 {
   149  		digitsStr = digitsStr[0 : len(digitsStr)-1]
   150  	}
   151  
   152  	if d.sign < 0 {
   153  		digitsStr = "-" + digitsStr
   154  	}
   155  
   156  	return digitsStr
   157  }
   158  
   159  func (d DmDecimal) Sign() int {
   160  	return d.sign
   161  }
   162  
   163  func (dest *DmDecimal) Scan(src interface{}) error {
   164  	if dest == nil {
   165  		return ECGO_STORE_IN_NIL_POINTER.throw()
   166  	}
   167  	switch src := src.(type) {
   168  	case nil:
   169  		*dest = *new(DmDecimal)
   170  
   171  		(*dest).Valid = false
   172  		return nil
   173  	case int, int8, int16, int32, int64:
   174  		d, err := NewDecimalFromInt64(reflect.ValueOf(src).Int())
   175  		if err != nil {
   176  			return err
   177  		}
   178  		*dest = *d
   179  		return nil
   180  	case uint, uint8, uint16, uint32, uint64:
   181  		d, err := NewDecimalFromBigInt(new(big.Int).SetUint64(reflect.ValueOf(src).Uint()))
   182  		if err != nil {
   183  			return err
   184  		}
   185  		*dest = *d
   186  		return nil
   187  	case float32, float64:
   188  		d, err := NewDecimalFromFloat64(reflect.ValueOf(src).Float())
   189  		if err != nil {
   190  			return err
   191  		}
   192  		*dest = *d
   193  		return nil
   194  	case string:
   195  		d, err := NewDecimalFromString(src)
   196  		if err != nil {
   197  			return err
   198  		}
   199  		*dest = *d
   200  		return nil
   201  	case *DmDecimal:
   202  		*dest = *src
   203  		return nil
   204  	default:
   205  		return UNSUPPORTED_SCAN
   206  	}
   207  }
   208  
   209  func (d DmDecimal) Value() (driver.Value, error) {
   210  	if !d.Valid {
   211  		return nil, nil
   212  	}
   213  	return d, nil
   214  }
   215  
   216  func newDecimal(dec interface{}, prec int, scale int) (*DmDecimal, error) {
   217  	d := &DmDecimal{
   218  		prec:  prec,
   219  		scale: scale,
   220  		Valid: true,
   221  	}
   222  	if isFloat(DECIMAL, scale) {
   223  		d.prec = getFloatPrec(prec)
   224  		d.scale = -1
   225  	}
   226  	switch de := dec.(type) {
   227  	case *big.Int:
   228  		d.sign = de.Sign()
   229  
   230  		if d.isZero() {
   231  			return d, nil
   232  		}
   233  		str := de.String()
   234  
   235  		if d.sign < 0 {
   236  			str = str[1:]
   237  		}
   238  
   239  		if err := checkPrec(len(str), prec); err != nil {
   240  			return d, err
   241  		}
   242  		i := 0
   243  		istart := len(str) - 1
   244  
   245  		for i = istart; i > 0; i-- {
   246  			if str[i] != '0' {
   247  				break
   248  			}
   249  		}
   250  		str = str[:i+1]
   251  		d.weight += istart - i
   252  
   253  		if isOdd(d.weight) {
   254  			str += "0"
   255  			d.weight -= 1
   256  		}
   257  		if isOdd(len(str)) {
   258  			str = "0" + str
   259  		}
   260  		d.digits = str
   261  	case *big.Float:
   262  		d.sign = de.Sign()
   263  
   264  		if d.isZero() {
   265  			return d, nil
   266  		}
   267  		str := de.Text('f', -1)
   268  
   269  		if d.sign < 0 {
   270  			str = str[1:]
   271  		}
   272  
   273  		pointIndex := strings.IndexByte(str, '.')
   274  		i, istart, length := 0, 0, len(str)
   275  
   276  		if pointIndex != -1 {
   277  			if str[0] == '0' {
   278  
   279  				istart = 2
   280  				for i = istart; i < length; i++ {
   281  					if str[i] != '0' {
   282  						break
   283  					}
   284  				}
   285  				str = str[i:]
   286  				d.weight -= i - istart + len(str)
   287  			} else {
   288  				str = str[:pointIndex] + str[pointIndex+1:]
   289  				d.weight -= length - pointIndex - 1
   290  			}
   291  		}
   292  
   293  		length = len(str)
   294  		istart = length - 1
   295  		for i = istart; i > 0; i-- {
   296  			if str[i] != '0' {
   297  				break
   298  			}
   299  		}
   300  		str = str[:i+1] + str[length:]
   301  		d.weight += istart - i
   302  
   303  		if isOdd(d.weight) {
   304  			str += "0"
   305  			d.weight -= 1
   306  		}
   307  		if isOdd(len(str)) {
   308  			str = "0" + str
   309  		}
   310  		d.digits = str
   311  	case []byte:
   312  		return decodeDecimal(de, prec, scale)
   313  	}
   314  	return d, nil
   315  }
   316  
   317  func (d DmDecimal) encodeDecimal() ([]byte, error) {
   318  	if d.isZero() {
   319  		return []byte{byte(FLAG_ZERO)}, nil
   320  	}
   321  	exp := (d.weight+len(d.digits))/2 - 1
   322  	if exp > EXP_MAX || exp < EXP_MIN {
   323  		return nil, ECGO_DATA_TOO_LONG.throw()
   324  	}
   325  	validLen := len(d.digits)/2 + 1
   326  
   327  	if d.sign < 0 && validLen >= XDEC_SIZE {
   328  		validLen = XDEC_SIZE - 1
   329  	} else if validLen > XDEC_SIZE {
   330  		validLen = XDEC_SIZE
   331  	}
   332  	retLen := validLen
   333  	if d.sign < 0 {
   334  		retLen = validLen + 1
   335  	}
   336  	retBytes := make([]byte, retLen)
   337  	if d.sign > 0 {
   338  		retBytes[0] = byte(exp + FLAG_POSITIVE)
   339  	} else {
   340  		retBytes[0] = byte(FLAG_NEGTIVE - exp)
   341  	}
   342  
   343  	ibytes := 1
   344  	for ichar := 0; ibytes < validLen; {
   345  		digit1, err := strconv.Atoi(string(d.digits[ichar]))
   346  		if err != nil {
   347  			return nil, err
   348  		}
   349  		ichar++
   350  		digit2, err := strconv.Atoi(string(d.digits[ichar]))
   351  		ichar++
   352  		if err != nil {
   353  			return nil, err
   354  		}
   355  
   356  		digit := digit1*10 + digit2
   357  		if d.sign > 0 {
   358  			retBytes[ibytes] = byte(digit + NUM_POSITIVE)
   359  		} else {
   360  			retBytes[ibytes] = byte(NUM_NEGTIVE - digit)
   361  		}
   362  		ibytes++
   363  	}
   364  	if d.sign < 0 && ibytes < retLen {
   365  		retBytes[ibytes] = 0x66
   366  		ibytes++
   367  	}
   368  	if ibytes < retLen {
   369  		retBytes[ibytes] = 0x00
   370  	}
   371  	return retBytes, nil
   372  }
   373  
   374  func decodeDecimal(values []byte, prec int, scale int) (*DmDecimal, error) {
   375  	var decimal = &DmDecimal{
   376  		prec:   prec,
   377  		scale:  scale,
   378  		sign:   0,
   379  		weight: 0,
   380  		Valid:  true,
   381  	}
   382  	if values == nil || len(values) == 0 || len(values) > XDEC_SIZE {
   383  		return nil, ECGO_FATAL_ERROR.throw()
   384  	}
   385  	if values[0] == byte(FLAG_ZERO) || len(values) == 1 {
   386  		return decimal, nil
   387  	}
   388  	if values[0]&byte(FLAG_ZERO) != 0 {
   389  		decimal.sign = 1
   390  	} else {
   391  		decimal.sign = -1
   392  	}
   393  
   394  	var flag = int(Dm_build_649.Dm_build_769(values, 0))
   395  	var exp int
   396  	if decimal.sign > 0 {
   397  		exp = flag - FLAG_POSITIVE
   398  	} else {
   399  		exp = FLAG_NEGTIVE - flag
   400  	}
   401  	var digit = 0
   402  	var sf = ""
   403  	for ival := 1; ival < len(values); ival++ {
   404  		if decimal.sign > 0 {
   405  			digit = int(values[ival]) - NUM_POSITIVE
   406  		} else {
   407  			digit = NUM_NEGTIVE - int(values[ival])
   408  		}
   409  		if digit < 0 || digit > 99 {
   410  			break
   411  		}
   412  		if digit < 10 {
   413  			sf += "0"
   414  		}
   415  		sf += strconv.Itoa(digit)
   416  	}
   417  	decimal.digits = sf
   418  	decimal.weight = exp*2 - (len(decimal.digits) - 2)
   419  
   420  	return decimal, nil
   421  }
   422  
   423  func (d DmDecimal) isZero() bool {
   424  	return d.sign == 0
   425  }
   426  
   427  func checkPrec(len int, prec int) error {
   428  	if prec > 0 && len > prec || len > XDEC_MAX_PREC {
   429  		return ECGO_DATA_TOO_LONG.throw()
   430  	}
   431  	return nil
   432  }
   433  
   434  func isOdd(val int) bool {
   435  	return val%2 != 0
   436  }
   437  
   438  func (d *DmDecimal) checkValid() error {
   439  	if !d.Valid {
   440  		return ECGO_IS_NULL.throw()
   441  	}
   442  	return nil
   443  }
   444  
   445  func (d *DmDecimal) GormDataType() string {
   446  	return "DECIMAL"
   447  }