github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/examples/gno.land/p/demo/uint256/utils.gno (about)

     1  package uint256
     2  
     3  // lower(c) is a lower-case letter if and only if
     4  // c is either that lower-case letter or the equivalent upper-case letter.
     5  // Instead of writing c == 'x' || c == 'X' one can write lower(c) == 'x'.
     6  // Note that lower of non-letters can produce other non-letters.
     7  func lower(c byte) byte {
     8  	return c | ('x' - 'X')
     9  }
    10  
    11  // underscoreOK reports whether the underscores in s are allowed.
    12  // Checking them in this one function lets all the parsers skip over them simply.
    13  // Underscore must appear only between digits or between a base prefix and a digit.
    14  func underscoreOK(s string) bool {
    15  	// saw tracks the last character (class) we saw:
    16  	// ^ for beginning of number,
    17  	// 0 for a digit or base prefix,
    18  	// _ for an underscore,
    19  	// ! for none of the above.
    20  	saw := '^'
    21  	i := 0
    22  
    23  	// Optional sign.
    24  	if len(s) >= 1 && (s[0] == '-' || s[0] == '+') {
    25  		s = s[1:]
    26  	}
    27  
    28  	// Optional base prefix.
    29  	hex := false
    30  	if len(s) >= 2 && s[0] == '0' && (lower(s[1]) == 'b' || lower(s[1]) == 'o' || lower(s[1]) == 'x') {
    31  		i = 2
    32  		saw = '0' // base prefix counts as a digit for "underscore as digit separator"
    33  		hex = lower(s[1]) == 'x'
    34  	}
    35  
    36  	// Number proper.
    37  	for ; i < len(s); i++ {
    38  		// Digits are always okay.
    39  		if '0' <= s[i] && s[i] <= '9' || hex && 'a' <= lower(s[i]) && lower(s[i]) <= 'f' {
    40  			saw = '0'
    41  			continue
    42  		}
    43  		// Underscore must follow digit.
    44  		if s[i] == '_' {
    45  			if saw != '0' {
    46  				return false
    47  			}
    48  			saw = '_'
    49  			continue
    50  		}
    51  		// Underscore must also be followed by digit.
    52  		if saw == '_' {
    53  			return false
    54  		}
    55  		// Saw non-digit, non-underscore.
    56  		saw = '!'
    57  	}
    58  	return saw != '_'
    59  }
    60  
    61  func checkNumberS(input string) error {
    62  	const fn = "UnmarshalText"
    63  	l := len(input)
    64  	if l == 0 {
    65  		return errEmptyString(fn, input)
    66  	}
    67  	if l < 2 || input[0] != '0' ||
    68  		(input[1] != 'x' && input[1] != 'X') {
    69  		return errMissingPrefix(fn, input)
    70  	}
    71  	if l == 2 {
    72  		return errEmptyNumber(fn, input)
    73  	}
    74  	if len(input) > 3 && input[2] == '0' {
    75  		return errLeadingZero(fn, input)
    76  	}
    77  	return nil
    78  }
    79  
    80  // ParseUint is like ParseUint but for unsigned numbers.
    81  //
    82  // A sign prefix is not permitted.
    83  func parseUint(s string, base int, bitSize int) (uint64, error) {
    84  	const fnParseUint = "ParseUint"
    85  
    86  	if s == "" {
    87  		return 0, errSyntax(fnParseUint, s)
    88  	}
    89  
    90  	base0 := base == 0
    91  
    92  	s0 := s
    93  	switch {
    94  	case 2 <= base && base <= 36:
    95  		// valid base; nothing to do
    96  
    97  	case base == 0:
    98  		// Look for octal, hex prefix.
    99  		base = 10
   100  		if s[0] == '0' {
   101  			switch {
   102  			case len(s) >= 3 && lower(s[1]) == 'b':
   103  				base = 2
   104  				s = s[2:]
   105  			case len(s) >= 3 && lower(s[1]) == 'o':
   106  				base = 8
   107  				s = s[2:]
   108  			case len(s) >= 3 && lower(s[1]) == 'x':
   109  				base = 16
   110  				s = s[2:]
   111  			default:
   112  				base = 8
   113  				s = s[1:]
   114  			}
   115  		}
   116  
   117  	default:
   118  		return 0, errInvalidBase(fnParseUint, base)
   119  	}
   120  
   121  	if bitSize == 0 {
   122  		bitSize = uintSize
   123  	} else if bitSize < 0 || bitSize > 64 {
   124  		return 0, errInvalidBitSize(fnParseUint, bitSize)
   125  	}
   126  
   127  	// Cutoff is the smallest number such that cutoff*base > maxUint64.
   128  	// Use compile-time constants for common cases.
   129  	var cutoff uint64
   130  	switch base {
   131  	case 10:
   132  		cutoff = MaxUint64/10 + 1
   133  	case 16:
   134  		cutoff = MaxUint64/16 + 1
   135  	default:
   136  		cutoff = MaxUint64/uint64(base) + 1
   137  	}
   138  
   139  	maxVal := uint64(1)<<uint(bitSize) - 1
   140  
   141  	underscores := false
   142  	var n uint64
   143  	for _, c := range []byte(s) {
   144  		var d byte
   145  		switch {
   146  		case c == '_' && base0:
   147  			underscores = true
   148  			continue
   149  		case '0' <= c && c <= '9':
   150  			d = c - '0'
   151  		case 'a' <= lower(c) && lower(c) <= 'z':
   152  			d = lower(c) - 'a' + 10
   153  		default:
   154  			return 0, errSyntax(fnParseUint, s0)
   155  		}
   156  
   157  		if d >= byte(base) {
   158  			return 0, errSyntax(fnParseUint, s0)
   159  		}
   160  
   161  		if n >= cutoff {
   162  			// n*base overflows
   163  			return maxVal, errRange(fnParseUint, s0)
   164  		}
   165  		n *= uint64(base)
   166  
   167  		n1 := n + uint64(d)
   168  		if n1 < n || n1 > maxVal {
   169  			// n+d overflows
   170  			return maxVal, errRange(fnParseUint, s0)
   171  		}
   172  		n = n1
   173  	}
   174  
   175  	if underscores && !underscoreOK(s0) {
   176  		return 0, errSyntax(fnParseUint, s0)
   177  	}
   178  
   179  	return n, nil
   180  }