github.com/camlistore/go4@v0.0.0-20200104003542-c7e774b10ea0/strutil/strconv.go (about)

     1  /*
     2  Copyright 2013 The Perkeep Authors
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8       http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package strutil
    18  
    19  import (
    20  	"errors"
    21  	"strconv"
    22  )
    23  
    24  // ParseUintBytes is like strconv.ParseUint, but using a []byte.
    25  func ParseUintBytes(s []byte, base int, bitSize int) (n uint64, err error) {
    26  	var cutoff, maxVal uint64
    27  
    28  	if bitSize == 0 {
    29  		bitSize = int(strconv.IntSize)
    30  	}
    31  
    32  	s0 := s
    33  	switch {
    34  	case len(s) < 1:
    35  		err = strconv.ErrSyntax
    36  		goto Error
    37  
    38  	case 2 <= base && base <= 36:
    39  		// valid base; nothing to do
    40  
    41  	case base == 0:
    42  		// Look for octal, hex prefix.
    43  		switch {
    44  		case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'):
    45  			base = 16
    46  			s = s[2:]
    47  			if len(s) < 1 {
    48  				err = strconv.ErrSyntax
    49  				goto Error
    50  			}
    51  		case s[0] == '0':
    52  			base = 8
    53  		default:
    54  			base = 10
    55  		}
    56  
    57  	default:
    58  		err = errors.New("invalid base " + strconv.Itoa(base))
    59  		goto Error
    60  	}
    61  
    62  	n = 0
    63  	cutoff = cutoff64(base)
    64  	maxVal = 1<<uint(bitSize) - 1
    65  
    66  	for i := 0; i < len(s); i++ {
    67  		var v byte
    68  		d := s[i]
    69  		switch {
    70  		case '0' <= d && d <= '9':
    71  			v = d - '0'
    72  		case 'a' <= d && d <= 'z':
    73  			v = d - 'a' + 10
    74  		case 'A' <= d && d <= 'Z':
    75  			v = d - 'A' + 10
    76  		default:
    77  			n = 0
    78  			err = strconv.ErrSyntax
    79  			goto Error
    80  		}
    81  		if int(v) >= base {
    82  			n = 0
    83  			err = strconv.ErrSyntax
    84  			goto Error
    85  		}
    86  
    87  		if n >= cutoff {
    88  			// n*base overflows
    89  			n = 1<<64 - 1
    90  			err = strconv.ErrRange
    91  			goto Error
    92  		}
    93  		n *= uint64(base)
    94  
    95  		n1 := n + uint64(v)
    96  		if n1 < n || n1 > maxVal {
    97  			// n+v overflows
    98  			n = 1<<64 - 1
    99  			err = strconv.ErrRange
   100  			goto Error
   101  		}
   102  		n = n1
   103  	}
   104  
   105  	return n, nil
   106  
   107  Error:
   108  	return n, &strconv.NumError{Func: "ParseUint", Num: string(s0), Err: err}
   109  }
   110  
   111  // Return the first number n such that n*base >= 1<<64.
   112  func cutoff64(base int) uint64 {
   113  	if base < 2 {
   114  		return 0
   115  	}
   116  	return (1<<64-1)/uint64(base) + 1
   117  }