gitee.com/curryzheng/dm@v0.0.1/p.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 string:
   188  		d, err := NewDecimalFromString(src)
   189  		if err != nil {
   190  			return err
   191  		}
   192  		*dest = *d
   193  		return nil
   194  	case *DmDecimal:
   195  		*dest = *src
   196  		return nil
   197  	default:
   198  		return UNSUPPORTED_SCAN
   199  	}
   200  }
   201  
   202  func (d DmDecimal) Value() (driver.Value, error) {
   203  	if !d.Valid {
   204  		return nil, nil
   205  	}
   206  	return d, nil
   207  }
   208  
   209  func newDecimal(dec interface{}, prec int, scale int) (*DmDecimal, error) {
   210  	d := &DmDecimal{
   211  		prec:  prec,
   212  		scale: scale,
   213  		Valid: true,
   214  	}
   215  	if isFloat(DECIMAL, scale) {
   216  		d.prec = getFloatPrec(prec)
   217  		d.scale = -1
   218  	}
   219  	switch de := dec.(type) {
   220  	case *big.Int:
   221  		d.sign = de.Sign()
   222  
   223  		if d.isZero() {
   224  			return d, nil
   225  		}
   226  		str := de.String()
   227  
   228  		if d.sign < 0 {
   229  			str = str[1:]
   230  		}
   231  
   232  		if err := checkPrec(len(str), prec); err != nil {
   233  			return d, err
   234  		}
   235  		i := 0
   236  		istart := len(str) - 1
   237  
   238  		for i = istart; i > 0; i-- {
   239  			if str[i] != '0' {
   240  				break
   241  			}
   242  		}
   243  		str = str[:i+1]
   244  		d.weight += istart - i
   245  
   246  		if isOdd(d.weight) {
   247  			str += "0"
   248  			d.weight -= 1
   249  		}
   250  		if isOdd(len(str)) {
   251  			str = "0" + str
   252  		}
   253  		d.digits = str
   254  	case *big.Float:
   255  		d.sign = de.Sign()
   256  
   257  		if d.isZero() {
   258  			return d, nil
   259  		}
   260  		str := de.Text('f', -1)
   261  
   262  		if d.sign < 0 {
   263  			str = str[1:]
   264  		}
   265  
   266  		pointIndex := strings.IndexByte(str, '.')
   267  		i, istart, length := 0, 0, len(str)
   268  
   269  		if pointIndex != -1 {
   270  			if str[0] == '0' {
   271  
   272  				istart = 2
   273  				for i = istart; i < length; i++ {
   274  					if str[i] != '0' {
   275  						break
   276  					}
   277  				}
   278  				str = str[i:]
   279  				d.weight -= i - istart + len(str)
   280  			} else {
   281  				str = str[:pointIndex] + str[pointIndex+1:]
   282  				d.weight -= length - pointIndex - 1
   283  			}
   284  		}
   285  
   286  		length = len(str)
   287  		istart = length - 1
   288  		for i = istart; i > 0; i-- {
   289  			if str[i] != '0' {
   290  				break
   291  			}
   292  		}
   293  		str = str[:i+1] + str[length:]
   294  		d.weight += istart - i
   295  
   296  		if isOdd(d.weight) {
   297  			str += "0"
   298  			d.weight -= 1
   299  		}
   300  		if isOdd(len(str)) {
   301  			str = "0" + str
   302  		}
   303  		d.digits = str
   304  	case []byte:
   305  		return decodeDecimal(de, prec, scale)
   306  	}
   307  	return d, nil
   308  }
   309  
   310  func (d DmDecimal) encodeDecimal() ([]byte, error) {
   311  	if d.isZero() {
   312  		return []byte{byte(FLAG_ZERO)}, nil
   313  	}
   314  	exp := (d.weight+len(d.digits))/2 - 1
   315  	if exp > EXP_MAX || exp < EXP_MIN {
   316  		return nil, ECGO_DATA_TOO_LONG.throw()
   317  	}
   318  	validLen := len(d.digits)/2 + 1
   319  
   320  	if d.sign < 0 && validLen >= XDEC_SIZE {
   321  		validLen = XDEC_SIZE - 1
   322  	} else if validLen > XDEC_SIZE {
   323  		validLen = XDEC_SIZE
   324  	}
   325  	retLen := validLen
   326  	if d.sign < 0 {
   327  		retLen = validLen + 1
   328  	}
   329  	retBytes := make([]byte, retLen)
   330  	if d.sign > 0 {
   331  		retBytes[0] = byte(exp + FLAG_POSITIVE)
   332  	} else {
   333  		retBytes[0] = byte(FLAG_NEGTIVE - exp)
   334  	}
   335  
   336  	ibytes := 1
   337  	for ichar := 0; ibytes < validLen; {
   338  		digit1, err := strconv.Atoi(string(d.digits[ichar]))
   339  		if err != nil {
   340  			return nil, err
   341  		}
   342  		ichar++
   343  		digit2, err := strconv.Atoi(string(d.digits[ichar]))
   344  		ichar++
   345  		if err != nil {
   346  			return nil, err
   347  		}
   348  
   349  		digit := digit1*10 + digit2
   350  		if d.sign > 0 {
   351  			retBytes[ibytes] = byte(digit + NUM_POSITIVE)
   352  		} else {
   353  			retBytes[ibytes] = byte(NUM_NEGTIVE - digit)
   354  		}
   355  		ibytes++
   356  	}
   357  	if d.sign < 0 && ibytes < retLen {
   358  		retBytes[ibytes] = 0x66
   359  		ibytes++
   360  	}
   361  	if ibytes < retLen {
   362  		retBytes[ibytes] = 0x00
   363  	}
   364  	return retBytes, nil
   365  }
   366  
   367  func decodeDecimal(values []byte, prec int, scale int) (*DmDecimal, error) {
   368  	var decimal = &DmDecimal{
   369  		prec:   prec,
   370  		scale:  scale,
   371  		sign:   0,
   372  		weight: 0,
   373  		Valid:  true,
   374  	}
   375  	if values == nil || len(values) == 0 || len(values) > XDEC_SIZE {
   376  		return nil, ECGO_FATAL_ERROR.throw()
   377  	}
   378  	if values[0] == byte(FLAG_ZERO) || len(values) == 1 {
   379  		return decimal, nil
   380  	}
   381  	if values[0]&byte(FLAG_ZERO) != 0 {
   382  		decimal.sign = 1
   383  	} else {
   384  		decimal.sign = -1
   385  	}
   386  
   387  	var flag = int(Dm_build_1.Dm_build_121(values, 0))
   388  	var exp int
   389  	if decimal.sign > 0 {
   390  		exp = flag - FLAG_POSITIVE
   391  	} else {
   392  		exp = FLAG_NEGTIVE - flag
   393  	}
   394  	var digit = 0
   395  	var sf = ""
   396  	for ival := 1; ival < len(values); ival++ {
   397  		if decimal.sign > 0 {
   398  			digit = int(values[ival]) - NUM_POSITIVE
   399  		} else {
   400  			digit = NUM_NEGTIVE - int(values[ival])
   401  		}
   402  		if digit < 0 || digit > 99 {
   403  			break
   404  		}
   405  		if digit < 10 {
   406  			sf += "0"
   407  		}
   408  		sf += strconv.Itoa(digit)
   409  	}
   410  	decimal.digits = sf
   411  	decimal.weight = exp*2 - (len(decimal.digits) - 2)
   412  
   413  	return decimal, nil
   414  }
   415  
   416  func (d DmDecimal) isZero() bool {
   417  	return d.sign == 0
   418  }
   419  
   420  func checkPrec(len int, prec int) error {
   421  	if prec > 0 && len > prec || len > XDEC_MAX_PREC {
   422  		return ECGO_DATA_TOO_LONG.throw()
   423  	}
   424  	return nil
   425  }
   426  
   427  func isOdd(val int) bool {
   428  	return val%2 != 0
   429  }
   430  
   431  func (d *DmDecimal) checkValid() error {
   432  	if !d.Valid {
   433  		return ECGO_IS_NULL.throw()
   434  	}
   435  	return nil
   436  }