github.com/xiyichan/dm8@v0.0.0-20211213021639-be727be3e136/q.go (about)

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