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 }