github.com/jgbaldwinbrown/perf@v0.1.1/benchfmt/internal/bytesconv/atoi.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package bytesconv
     6  
     7  import (
     8  	"errors"
     9  	"math"
    10  	"strconv"
    11  )
    12  
    13  // lower(c) is a lower-case letter if and only if
    14  // c is either that lower-case letter or the equivalent upper-case letter.
    15  // Instead of writing c == 'x' || c == 'X' one can write lower(c) == 'x'.
    16  // Note that lower of non-letters can produce other non-letters.
    17  func lower(c byte) byte {
    18  	return c | ('x' - 'X')
    19  }
    20  
    21  // ErrRange indicates that a value is out of range for the target type.
    22  var ErrRange = errors.New("value out of range")
    23  
    24  // ErrSyntax indicates that a value does not have the right syntax for the target type.
    25  var ErrSyntax = errors.New("invalid syntax")
    26  
    27  // A NumError records a failed conversion.
    28  type NumError struct {
    29  	Func string // the failing function (ParseBool, ParseInt, ParseUint, ParseFloat)
    30  	Num  string // the input
    31  	Err  error  // the reason the conversion failed (e.g. ErrRange, ErrSyntax, etc.)
    32  }
    33  
    34  func (e *NumError) Error() string {
    35  	return "strconv." + e.Func + ": " + "parsing " + strconv.Quote(e.Num) + ": " + e.Err.Error()
    36  }
    37  
    38  func syntaxError(fn string, str []byte) *NumError {
    39  	return &NumError{fn, string(str), ErrSyntax}
    40  }
    41  
    42  func rangeError(fn string, str []byte) *NumError {
    43  	return &NumError{fn, string(str), ErrRange}
    44  }
    45  
    46  func baseError(fn string, str []byte, base int) *NumError {
    47  	return &NumError{fn, string(str), errors.New("invalid base " + strconv.Itoa(base))}
    48  }
    49  
    50  func bitSizeError(fn string, str []byte, bitSize int) *NumError {
    51  	return &NumError{fn, string(str), errors.New("invalid bit size " + strconv.Itoa(bitSize))}
    52  }
    53  
    54  const intSize = 32 << (^uint(0) >> 63)
    55  
    56  // IntSize is the size in bits of an int or uint value.
    57  const IntSize = intSize
    58  
    59  
    60  // ParseUint is like ParseInt but for unsigned numbers.
    61  func ParseUint(s []byte, base int, bitSize int) (uint64, error) {
    62  	const fnParseUint = "ParseUint"
    63  
    64  	if len(s) == 0 || !underscoreOK(s) {
    65  		return 0, syntaxError(fnParseUint, s)
    66  	}
    67  
    68  	base0 := base == 0
    69  
    70  	s0 := s
    71  	switch {
    72  	case 2 <= base && base <= 36:
    73  		// valid base; nothing to do
    74  
    75  	case base == 0:
    76  		// Look for octal, hex prefix.
    77  		base = 10
    78  		if s[0] == '0' {
    79  			switch {
    80  			case len(s) >= 3 && lower(s[1]) == 'b':
    81  				base = 2
    82  				s = s[2:]
    83  			case len(s) >= 3 && lower(s[1]) == 'o':
    84  				base = 8
    85  				s = s[2:]
    86  			case len(s) >= 3 && lower(s[1]) == 'x':
    87  				base = 16
    88  				s = s[2:]
    89  			default:
    90  				base = 8
    91  				s = s[1:]
    92  			}
    93  		}
    94  
    95  	default:
    96  		return 0, baseError(fnParseUint, s0, base)
    97  	}
    98  
    99  	if bitSize == 0 {
   100  		bitSize = int(IntSize)
   101  	} else if bitSize < 0 || bitSize > 64 {
   102  		return 0, bitSizeError(fnParseUint, s0, bitSize)
   103  	}
   104  
   105  	// Cutoff is the smallest number such that cutoff*base > maxUint64.
   106  	// Use compile-time constants for common cases.
   107  	var cutoff uint64
   108  	switch base {
   109  	case 10:
   110  		cutoff = math.MaxUint64/10 + 1
   111  	case 16:
   112  		cutoff = math.MaxUint64/16 + 1
   113  	default:
   114  		cutoff = math.MaxUint64/uint64(base) + 1
   115  	}
   116  
   117  	maxVal := uint64(1)<<uint(bitSize) - 1
   118  
   119  	var n uint64
   120  	for _, c := range s {
   121  		var d byte
   122  		switch {
   123  		case c == '_' && base0:
   124  			// underscoreOK already called
   125  			continue
   126  		case '0' <= c && c <= '9':
   127  			d = c - '0'
   128  		case 'a' <= lower(c) && lower(c) <= 'z':
   129  			d = lower(c) - 'a' + 10
   130  		default:
   131  			return 0, syntaxError(fnParseUint, s0)
   132  		}
   133  
   134  		if d >= byte(base) {
   135  			return 0, syntaxError(fnParseUint, s0)
   136  		}
   137  
   138  		if n >= cutoff {
   139  			// n*base overflows
   140  			return maxVal, rangeError(fnParseUint, s0)
   141  		}
   142  		n *= uint64(base)
   143  
   144  		n1 := n + uint64(d)
   145  		if n1 < n || n1 > maxVal {
   146  			// n+v overflows
   147  			return maxVal, rangeError(fnParseUint, s0)
   148  		}
   149  		n = n1
   150  	}
   151  
   152  	return n, nil
   153  }
   154  
   155  // ParseInt interprets a string s in the given base (0, 2 to 36) and
   156  // bit size (0 to 64) and returns the corresponding value i.
   157  //
   158  // If base == 0, the base is implied by the string's prefix:
   159  // base 2 for "0b", base 8 for "0" or "0o", base 16 for "0x",
   160  // and base 10 otherwise. Also, for base == 0 only, underscore
   161  // characters are permitted per the Go integer literal syntax.
   162  // If base is below 0, is 1, or is above 36, an error is returned.
   163  //
   164  // The bitSize argument specifies the integer type
   165  // that the result must fit into. Bit sizes 0, 8, 16, 32, and 64
   166  // correspond to int, int8, int16, int32, and int64.
   167  // If bitSize is below 0 or above 64, an error is returned.
   168  //
   169  // The errors that ParseInt returns have concrete type *NumError
   170  // and include err.Num = s. If s is empty or contains invalid
   171  // digits, err.Err = ErrSyntax and the returned value is 0;
   172  // if the value corresponding to s cannot be represented by a
   173  // signed integer of the given size, err.Err = ErrRange and the
   174  // returned value is the maximum magnitude integer of the
   175  // appropriate bitSize and sign.
   176  func ParseInt(s []byte, base int, bitSize int) (i int64, err error) {
   177  	const fnParseInt = "ParseInt"
   178  
   179  	if len(s) == 0 {
   180  		return 0, syntaxError(fnParseInt, s)
   181  	}
   182  
   183  	// Pick off leading sign.
   184  	s0 := s
   185  	neg := false
   186  	if s[0] == '+' {
   187  		s = s[1:]
   188  	} else if s[0] == '-' {
   189  		neg = true
   190  		s = s[1:]
   191  	}
   192  
   193  	// Convert unsigned and check range.
   194  	var un uint64
   195  	un, err = ParseUint(s, base, bitSize)
   196  	if err != nil && err.(*NumError).Err != ErrRange {
   197  		err.(*NumError).Func = fnParseInt
   198  		err.(*NumError).Num = string(s0)
   199  		return 0, err
   200  	}
   201  
   202  	if bitSize == 0 {
   203  		bitSize = int(IntSize)
   204  	}
   205  
   206  	cutoff := uint64(1 << uint(bitSize-1))
   207  	if !neg && un >= cutoff {
   208  		return int64(cutoff - 1), rangeError(fnParseInt, s0)
   209  	}
   210  	if neg && un > cutoff {
   211  		return -int64(cutoff), rangeError(fnParseInt, s0)
   212  	}
   213  	n := int64(un)
   214  	if neg {
   215  		n = -n
   216  	}
   217  	return n, nil
   218  }
   219  
   220  // Atoi is equivalent to ParseInt(s, 10, 0), converted to type int.
   221  func Atoi(s []byte) (int, error) {
   222  	const fnAtoi = "Atoi"
   223  
   224  	sLen := len(s)
   225  	if intSize == 32 && (0 < sLen && sLen < 10) ||
   226  		intSize == 64 && (0 < sLen && sLen < 19) {
   227  		// Fast path for small integers that fit int type.
   228  		s0 := s
   229  		if s[0] == '-' || s[0] == '+' {
   230  			s = s[1:]
   231  			if len(s) < 1 {
   232  				return 0, &NumError{fnAtoi, string(s0), ErrSyntax}
   233  			}
   234  		}
   235  
   236  		n := 0
   237  		for _, ch := range s {
   238  			ch -= '0'
   239  			if ch > 9 {
   240  				return 0, &NumError{fnAtoi, string(s0), ErrSyntax}
   241  			}
   242  			n = n*10 + int(ch)
   243  		}
   244  		if s0[0] == '-' {
   245  			n = -n
   246  		}
   247  		return n, nil
   248  	}
   249  
   250  	// Slow path for invalid, big, or underscored integers.
   251  	i64, err := ParseInt(s, 10, 0)
   252  	if nerr, ok := err.(*NumError); ok {
   253  		nerr.Func = fnAtoi
   254  	}
   255  	return int(i64), err
   256  }
   257  
   258  // underscoreOK reports whether the underscores in s are allowed.
   259  // Checking them in this one function lets all the parsers skip over them simply.
   260  // Underscore must appear only between digits or between a base prefix and a digit.
   261  func underscoreOK(s []byte) bool {
   262  	// saw tracks the last character (class) we saw:
   263  	// ^ for beginning of number,
   264  	// 0 for a digit or base prefix,
   265  	// _ for an underscore,
   266  	// ! for none of the above.
   267  	saw := '^'
   268  	i := 0
   269  
   270  	// Optional sign.
   271  	if len(s) >= 1 && (s[0] == '-' || s[0] == '+') {
   272  		s = s[1:]
   273  	}
   274  
   275  	// Optional base prefix.
   276  	hex := false
   277  	if len(s) >= 2 && s[0] == '0' && (lower(s[1]) == 'b' || lower(s[1]) == 'o' || lower(s[1]) == 'x') {
   278  		i = 2
   279  		saw = '0' // base prefix counts as a digit for "underscore as digit separator"
   280  		hex = lower(s[1]) == 'x'
   281  	}
   282  
   283  	// Number proper.
   284  	for ; i < len(s); i++ {
   285  		// Digits are always okay.
   286  		if '0' <= s[i] && s[i] <= '9' || hex && 'a' <= lower(s[i]) && lower(s[i]) <= 'f' {
   287  			saw = '0'
   288  			continue
   289  		}
   290  		// Underscore must follow digit.
   291  		if s[i] == '_' {
   292  			if saw != '0' {
   293  				return false
   294  			}
   295  			saw = '_'
   296  			continue
   297  		}
   298  		// Underscore must also be followed by digit.
   299  		if saw == '_' {
   300  			return false
   301  		}
   302  		// Saw non-digit, non-underscore.
   303  		saw = '!'
   304  	}
   305  	return saw != '_'
   306  }