gitee.com/quant1x/pkg@v0.2.8/fastjson/fastfloat/parse.go (about)

     1  package fastfloat
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"strconv"
     7  	"strings"
     8  )
     9  
    10  // ParseUint64BestEffort parses uint64 number s.
    11  //
    12  // It is equivalent to strconv.ParseUint(s, 10, 64), but is faster.
    13  //
    14  // 0 is returned if the number cannot be parsed.
    15  // See also ParseUint64, which returns parse error if the number cannot be parsed.
    16  func ParseUint64BestEffort(s string) uint64 {
    17  	if len(s) == 0 {
    18  		return 0
    19  	}
    20  	i := uint(0)
    21  	d := uint64(0)
    22  	j := i
    23  	for i < uint(len(s)) {
    24  		if s[i] >= '0' && s[i] <= '9' {
    25  			d = d*10 + uint64(s[i]-'0')
    26  			i++
    27  			if i > 18 {
    28  				// The integer part may be out of range for uint64.
    29  				// Fall back to slow parsing.
    30  				dd, err := strconv.ParseUint(s, 10, 64)
    31  				if err != nil {
    32  					return 0
    33  				}
    34  				return dd
    35  			}
    36  			continue
    37  		}
    38  		break
    39  	}
    40  	if i <= j {
    41  		return 0
    42  	}
    43  	if i < uint(len(s)) {
    44  		// Unparsed tail left.
    45  		return 0
    46  	}
    47  	return d
    48  }
    49  
    50  // ParseUint64 parses uint64 from s.
    51  //
    52  // It is equivalent to strconv.ParseUint(s, 10, 64), but is faster.
    53  //
    54  // See also ParseUint64BestEffort.
    55  func ParseUint64(s string) (uint64, error) {
    56  	if len(s) == 0 {
    57  		return 0, fmt.Errorf("cannot parse uint64 from empty string")
    58  	}
    59  	i := uint(0)
    60  	d := uint64(0)
    61  	j := i
    62  	for i < uint(len(s)) {
    63  		if s[i] >= '0' && s[i] <= '9' {
    64  			d = d*10 + uint64(s[i]-'0')
    65  			i++
    66  			if i > 18 {
    67  				// The integer part may be out of range for uint64.
    68  				// Fall back to slow parsing.
    69  				dd, err := strconv.ParseUint(s, 10, 64)
    70  				if err != nil {
    71  					return 0, err
    72  				}
    73  				return dd, nil
    74  			}
    75  			continue
    76  		}
    77  		break
    78  	}
    79  	if i <= j {
    80  		return 0, fmt.Errorf("cannot parse uint64 from %q", s)
    81  	}
    82  	if i < uint(len(s)) {
    83  		// Unparsed tail left.
    84  		return 0, fmt.Errorf("unparsed tail left after parsing uint64 from %q: %q", s, s[i:])
    85  	}
    86  	return d, nil
    87  }
    88  
    89  // ParseInt64BestEffort parses int64 number s.
    90  //
    91  // It is equivalent to strconv.ParseInt(s, 10, 64), but is faster.
    92  //
    93  // 0 is returned if the number cannot be parsed.
    94  // See also ParseInt64, which returns parse error if the number cannot be parsed.
    95  func ParseInt64BestEffort(s string) int64 {
    96  	if len(s) == 0 {
    97  		return 0
    98  	}
    99  	i := uint(0)
   100  	minus := s[0] == '-'
   101  	if minus {
   102  		i++
   103  		if i >= uint(len(s)) {
   104  			return 0
   105  		}
   106  	}
   107  
   108  	d := int64(0)
   109  	j := i
   110  	for i < uint(len(s)) {
   111  		if s[i] >= '0' && s[i] <= '9' {
   112  			d = d*10 + int64(s[i]-'0')
   113  			i++
   114  			if i > 18 {
   115  				// The integer part may be out of range for int64.
   116  				// Fall back to slow parsing.
   117  				dd, err := strconv.ParseInt(s, 10, 64)
   118  				if err != nil {
   119  					return 0
   120  				}
   121  				return dd
   122  			}
   123  			continue
   124  		}
   125  		break
   126  	}
   127  	if i <= j {
   128  		return 0
   129  	}
   130  	if i < uint(len(s)) {
   131  		// Unparsed tail left.
   132  		return 0
   133  	}
   134  	if minus {
   135  		d = -d
   136  	}
   137  	return d
   138  }
   139  
   140  // ParseInt64 parses int64 number s.
   141  //
   142  // It is equivalent to strconv.ParseInt(s, 10, 64), but is faster.
   143  //
   144  // See also ParseInt64BestEffort.
   145  func ParseInt64(s string) (int64, error) {
   146  	if len(s) == 0 {
   147  		return 0, fmt.Errorf("cannot parse int64 from empty string")
   148  	}
   149  	i := uint(0)
   150  	minus := s[0] == '-'
   151  	if minus {
   152  		i++
   153  		if i >= uint(len(s)) {
   154  			return 0, fmt.Errorf("cannot parse int64 from %q", s)
   155  		}
   156  	}
   157  
   158  	d := int64(0)
   159  	j := i
   160  	for i < uint(len(s)) {
   161  		if s[i] >= '0' && s[i] <= '9' {
   162  			d = d*10 + int64(s[i]-'0')
   163  			i++
   164  			if i > 18 {
   165  				// The integer part may be out of range for int64.
   166  				// Fall back to slow parsing.
   167  				dd, err := strconv.ParseInt(s, 10, 64)
   168  				if err != nil {
   169  					return 0, err
   170  				}
   171  				return dd, nil
   172  			}
   173  			continue
   174  		}
   175  		break
   176  	}
   177  	if i <= j {
   178  		return 0, fmt.Errorf("cannot parse int64 from %q", s)
   179  	}
   180  	if i < uint(len(s)) {
   181  		// Unparsed tail left.
   182  		return 0, fmt.Errorf("unparsed tail left after parsing int64 form %q: %q", s, s[i:])
   183  	}
   184  	if minus {
   185  		d = -d
   186  	}
   187  	return d, nil
   188  }
   189  
   190  // Exact powers of 10.
   191  //
   192  // This works faster than math.Pow10, since it avoids additional multiplication.
   193  var float64pow10 = [...]float64{
   194  	1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16,
   195  }
   196  
   197  // ParseBestEffort parses floating-point number s.
   198  //
   199  // It is equivalent to strconv.ParseFloat(s, 64), but is faster.
   200  //
   201  // 0 is returned if the number cannot be parsed.
   202  // See also Parse, which returns parse error if the number cannot be parsed.
   203  func ParseBestEffort(s string) float64 {
   204  	if len(s) == 0 {
   205  		return 0
   206  	}
   207  	i := uint(0)
   208  	minus := s[0] == '-'
   209  	if minus {
   210  		i++
   211  		if i >= uint(len(s)) {
   212  			return 0
   213  		}
   214  	}
   215  
   216  	// the integer part might be elided to remain compliant
   217  	// with https://go.dev/ref/spec#Floating-point_literals
   218  	if s[i] == '.' && (i+1 >= uint(len(s)) || s[i+1] < '0' || s[i+1] > '9') {
   219  		return 0
   220  	}
   221  
   222  	d := uint64(0)
   223  	j := i
   224  	for i < uint(len(s)) {
   225  		if s[i] >= '0' && s[i] <= '9' {
   226  			d = d*10 + uint64(s[i]-'0')
   227  			i++
   228  			if i > 18 {
   229  				// The integer part may be out of range for uint64.
   230  				// Fall back to slow parsing.
   231  				f, err := strconv.ParseFloat(s, 64)
   232  				if err != nil && !math.IsInf(f, 0) {
   233  					return 0
   234  				}
   235  				return f
   236  			}
   237  			continue
   238  		}
   239  		break
   240  	}
   241  	if i <= j && s[i] != '.' {
   242  		s = s[i:]
   243  		if strings.HasPrefix(s, "+") {
   244  			s = s[1:]
   245  		}
   246  		// "infinity" is needed for OpenMetrics support.
   247  		// See https://github.com/OpenObservability/OpenMetrics/blob/master/OpenMetrics.md
   248  		if strings.EqualFold(s, "inf") || strings.EqualFold(s, "infinity") {
   249  			if minus {
   250  				return -inf
   251  			}
   252  			return inf
   253  		}
   254  		if strings.EqualFold(s, "nan") {
   255  			return nan
   256  		}
   257  		return 0
   258  	}
   259  	f := float64(d)
   260  	if i >= uint(len(s)) {
   261  		// Fast path - just integer.
   262  		if minus {
   263  			f = -f
   264  		}
   265  		return f
   266  	}
   267  
   268  	if s[i] == '.' {
   269  		// Parse fractional part.
   270  		i++
   271  		if i >= uint(len(s)) {
   272  			// the fractional part may be elided to remain compliant
   273  			// with https://go.dev/ref/spec#Floating-point_literals
   274  			return f
   275  		}
   276  		k := i
   277  		for i < uint(len(s)) {
   278  			if s[i] >= '0' && s[i] <= '9' {
   279  				d = d*10 + uint64(s[i]-'0')
   280  				i++
   281  				if i-j >= uint(len(float64pow10)) {
   282  					// The mantissa is out of range. Fall back to standard parsing.
   283  					f, err := strconv.ParseFloat(s, 64)
   284  					if err != nil && !math.IsInf(f, 0) {
   285  						return 0
   286  					}
   287  					return f
   288  				}
   289  				continue
   290  			}
   291  			break
   292  		}
   293  		if i < k {
   294  			return 0
   295  		}
   296  		// Convert the entire mantissa to a float at once to avoid rounding errors.
   297  		f = float64(d) / float64pow10[i-k]
   298  		if i >= uint(len(s)) {
   299  			// Fast path - parsed fractional number.
   300  			if minus {
   301  				f = -f
   302  			}
   303  			return f
   304  		}
   305  	}
   306  	if s[i] == 'e' || s[i] == 'E' {
   307  		// Parse exponent part.
   308  		i++
   309  		if i >= uint(len(s)) {
   310  			return 0
   311  		}
   312  		expMinus := false
   313  		if s[i] == '+' || s[i] == '-' {
   314  			expMinus = s[i] == '-'
   315  			i++
   316  			if i >= uint(len(s)) {
   317  				return 0
   318  			}
   319  		}
   320  		exp := int16(0)
   321  		j := i
   322  		for i < uint(len(s)) {
   323  			if s[i] >= '0' && s[i] <= '9' {
   324  				exp = exp*10 + int16(s[i]-'0')
   325  				i++
   326  				if exp > 300 {
   327  					// The exponent may be too big for float64.
   328  					// Fall back to standard parsing.
   329  					f, err := strconv.ParseFloat(s, 64)
   330  					if err != nil && !math.IsInf(f, 0) {
   331  						return 0
   332  					}
   333  					return f
   334  				}
   335  				continue
   336  			}
   337  			break
   338  		}
   339  		if i <= j {
   340  			return 0
   341  		}
   342  		if expMinus {
   343  			exp = -exp
   344  		}
   345  		f *= math.Pow10(int(exp))
   346  		if i >= uint(len(s)) {
   347  			if minus {
   348  				f = -f
   349  			}
   350  			return f
   351  		}
   352  	}
   353  	return 0
   354  }
   355  
   356  // Parse parses floating-point number s.
   357  //
   358  // It is equivalent to strconv.ParseFloat(s, 64), but is faster.
   359  //
   360  // See also ParseBestEffort.
   361  func Parse(s string) (float64, error) {
   362  	if len(s) == 0 {
   363  		return 0, fmt.Errorf("cannot parse float64 from empty string")
   364  	}
   365  	i := uint(0)
   366  	minus := s[0] == '-'
   367  	if minus {
   368  		i++
   369  		if i >= uint(len(s)) {
   370  			return 0, fmt.Errorf("cannot parse float64 from %q", s)
   371  		}
   372  	}
   373  
   374  	// the integer part might be elided to remain compliant
   375  	// with https://go.dev/ref/spec#Floating-point_literals
   376  	if s[i] == '.' && (i+1 >= uint(len(s)) || s[i+1] < '0' || s[i+1] > '9') {
   377  		return 0, fmt.Errorf("missing integer and fractional part in %q", s)
   378  	}
   379  
   380  	d := uint64(0)
   381  	j := i
   382  	for i < uint(len(s)) {
   383  		if s[i] >= '0' && s[i] <= '9' {
   384  			d = d*10 + uint64(s[i]-'0')
   385  			i++
   386  			if i > 18 {
   387  				// The integer part may be out of range for uint64.
   388  				// Fall back to slow parsing.
   389  				f, err := strconv.ParseFloat(s, 64)
   390  				if err != nil && !math.IsInf(f, 0) {
   391  					return 0, err
   392  				}
   393  				return f, nil
   394  			}
   395  			continue
   396  		}
   397  		break
   398  	}
   399  	if i <= j && s[i] != '.' {
   400  		ss := s[i:]
   401  		if strings.HasPrefix(ss, "+") {
   402  			ss = ss[1:]
   403  		}
   404  		// "infinity" is needed for OpenMetrics support.
   405  		// See https://github.com/OpenObservability/OpenMetrics/blob/master/OpenMetrics.md
   406  		if strings.EqualFold(ss, "inf") || strings.EqualFold(ss, "infinity") {
   407  			if minus {
   408  				return -inf, nil
   409  			}
   410  			return inf, nil
   411  		}
   412  		if strings.EqualFold(ss, "nan") {
   413  			return nan, nil
   414  		}
   415  		return 0, fmt.Errorf("unparsed tail left after parsing float64 from %q: %q", s, ss)
   416  	}
   417  	f := float64(d)
   418  	if i >= uint(len(s)) {
   419  		// Fast path - just integer.
   420  		if minus {
   421  			f = -f
   422  		}
   423  		return f, nil
   424  	}
   425  
   426  	if s[i] == '.' {
   427  		// Parse fractional part.
   428  		i++
   429  		if i >= uint(len(s)) {
   430  			// the fractional part might be elided to remain compliant
   431  			// with https://go.dev/ref/spec#Floating-point_literals
   432  			return f, nil
   433  		}
   434  		k := i
   435  		for i < uint(len(s)) {
   436  			if s[i] >= '0' && s[i] <= '9' {
   437  				d = d*10 + uint64(s[i]-'0')
   438  				i++
   439  				if i-j >= uint(len(float64pow10)) {
   440  					// The mantissa is out of range. Fall back to standard parsing.
   441  					f, err := strconv.ParseFloat(s, 64)
   442  					if err != nil && !math.IsInf(f, 0) {
   443  						return 0, fmt.Errorf("cannot parse mantissa in %q: %s", s, err)
   444  					}
   445  					return f, nil
   446  				}
   447  				continue
   448  			}
   449  			break
   450  		}
   451  		if i < k {
   452  			return 0, fmt.Errorf("cannot find mantissa in %q", s)
   453  		}
   454  		// Convert the entire mantissa to a float at once to avoid rounding errors.
   455  		f = float64(d) / float64pow10[i-k]
   456  		if i >= uint(len(s)) {
   457  			// Fast path - parsed fractional number.
   458  			if minus {
   459  				f = -f
   460  			}
   461  			return f, nil
   462  		}
   463  	}
   464  	if s[i] == 'e' || s[i] == 'E' {
   465  		// Parse exponent part.
   466  		i++
   467  		if i >= uint(len(s)) {
   468  			return 0, fmt.Errorf("cannot parse exponent in %q", s)
   469  		}
   470  		expMinus := false
   471  		if s[i] == '+' || s[i] == '-' {
   472  			expMinus = s[i] == '-'
   473  			i++
   474  			if i >= uint(len(s)) {
   475  				return 0, fmt.Errorf("cannot parse exponent in %q", s)
   476  			}
   477  		}
   478  		exp := int16(0)
   479  		j := i
   480  		for i < uint(len(s)) {
   481  			if s[i] >= '0' && s[i] <= '9' {
   482  				exp = exp*10 + int16(s[i]-'0')
   483  				i++
   484  				if exp > 300 {
   485  					// The exponent may be too big for float64.
   486  					// Fall back to standard parsing.
   487  					f, err := strconv.ParseFloat(s, 64)
   488  					if err != nil && !math.IsInf(f, 0) {
   489  						return 0, fmt.Errorf("cannot parse exponent in %q: %s", s, err)
   490  					}
   491  					return f, nil
   492  				}
   493  				continue
   494  			}
   495  			break
   496  		}
   497  		if i <= j {
   498  			return 0, fmt.Errorf("cannot parse exponent in %q", s)
   499  		}
   500  		if expMinus {
   501  			exp = -exp
   502  		}
   503  		f *= math.Pow10(int(exp))
   504  		if i >= uint(len(s)) {
   505  			if minus {
   506  				f = -f
   507  			}
   508  			return f, nil
   509  		}
   510  	}
   511  	return 0, fmt.Errorf("cannot parse float64 from %q", s)
   512  }
   513  
   514  var inf = math.Inf(1)
   515  var nan = math.NaN()