Skip to content

Commit

Permalink
import atoi from strconv
Browse files Browse the repository at this point in the history
  • Loading branch information
bpowers committed Jan 2, 2013
1 parent fdb7a95 commit 33d7fe8
Show file tree
Hide file tree
Showing 2 changed files with 198 additions and 9 deletions.
195 changes: 195 additions & 0 deletions atoi.go
@@ -0,0 +1,195 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package strconv

import "errors"

// ErrRange indicates that a value is out of range for the target type.
var ErrRange = errors.New("value out of range")

// ErrSyntax indicates that a value does not have the right syntax for the target type.
var ErrSyntax = errors.New("invalid syntax")

// A NumError records a failed conversion.
type NumError struct {
Func string // the failing function (ParseBool, ParseInt, ParseUint, ParseFloat)
Num string // the input
Err error // the reason the conversion failed (ErrRange, ErrSyntax)
}

func (e *NumError) Error() string {
return "strconv." + e.Func + ": " + `parsing "` + e.Num + `": ` + e.Err.Error()
}

func syntaxError(fn, str string) *NumError {
return &NumError{fn, str, ErrSyntax}
}

func rangeError(fn, str string) *NumError {
return &NumError{fn, str, ErrRange}
}

const intSize = 32 << uint(^uint(0)>>63)

const IntSize = intSize // number of bits in int, uint (32 or 64)

// Return the first number n such that n*base >= 1<<64.
func cutoff64(base int) uint64 {
if base < 2 {
return 0
}
return (1<<64-1)/uint64(base) + 1
}

// ParseUint is like ParseInt but for unsigned numbers.
func ParseUint(s string, base int, bitSize int) (n uint64, err error) {
var cutoff, maxVal uint64

if bitSize == 0 {
bitSize = int(IntSize)
}

s0 := s
switch {
case len(s) < 1:
err = ErrSyntax
goto Error

case 2 <= base && base <= 36:
// valid base; nothing to do

case base == 0:
// Look for octal, hex prefix.
switch {
case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'):
base = 16
s = s[2:]
if len(s) < 1 {
err = ErrSyntax
goto Error
}
case s[0] == '0':
base = 8
default:
base = 10
}

default:
err = errors.New("invalid base " + Itoa(base))
goto Error
}

n = 0
cutoff = cutoff64(base)
maxVal = 1<<uint(bitSize) - 1

for i := 0; i < len(s); i++ {
var v byte
d := s[i]
switch {
case '0' <= d && d <= '9':
v = d - '0'
case 'a' <= d && d <= 'z':
v = d - 'a' + 10
case 'A' <= d && d <= 'Z':
v = d - 'A' + 10
default:
n = 0
err = ErrSyntax
goto Error
}
if int(v) >= base {
n = 0
err = ErrSyntax
goto Error
}

if n >= cutoff {
// n*base overflows
n = 1<<64 - 1
err = ErrRange
goto Error
}
n *= uint64(base)

n1 := n + uint64(v)
if n1 < n || n1 > maxVal {
// n+v overflows
n = 1<<64 - 1
err = ErrRange
goto Error
}
n = n1
}

return n, nil

Error:
return n, &NumError{"ParseUint", s0, err}
}

// ParseInt interprets a string s in the given base (2 to 36) and
// returns the corresponding value i. If base == 0, the base is
// implied by the string's prefix: base 16 for "0x", base 8 for
// "0", and base 10 otherwise.
//
// The bitSize argument specifies the integer type
// that the result must fit into. Bit sizes 0, 8, 16, 32, and 64
// correspond to int, int8, int16, int32, and int64.
//
// The errors that ParseInt returns have concrete type *NumError
// and include err.Num = s. If s is empty or contains invalid
// digits, err.Error = ErrSyntax; if the value corresponding
// to s cannot be represented by a signed integer of the
// given size, err.Error = ErrRange.
func ParseInt(s string, base int, bitSize int) (i int64, err error) {
const fnParseInt = "ParseInt"

if bitSize == 0 {
bitSize = int(IntSize)
}

// Empty string bad.
if len(s) == 0 {
return 0, syntaxError(fnParseInt, s)
}

// Pick off leading sign.
s0 := s
neg := false
if s[0] == '+' {
s = s[1:]
} else if s[0] == '-' {
neg = true
s = s[1:]
}

// Convert unsigned and check range.
var un uint64
un, err = ParseUint(s, base, bitSize)
if err != nil && err.(*NumError).Err != ErrRange {
err.(*NumError).Func = fnParseInt
err.(*NumError).Num = s0
return 0, err
}
cutoff := uint64(1 << uint(bitSize-1))
if !neg && un >= cutoff {
return int64(cutoff - 1), rangeError(fnParseInt, s0)
}
if neg && un > cutoff {
return -int64(cutoff), rangeError(fnParseInt, s0)
}
n := int64(un)
if neg {
n = -n
}
return n, nil
}

// Atoi is shorthand for ParseInt(s, 10, 0).
func Atoi(s string) (i int, err error) {
i64, err := ParseInt(s, 10, 0)
return int(i64), err
}
12 changes: 3 additions & 9 deletions main.go
Expand Up @@ -137,12 +137,6 @@ func procName(pid int) (string, error) {
return n, nil
}

// isSpace wrapps unicode.IsSpace, accepting a byte argument instead
// of a rune arg.
func isSpace(b byte) bool {
return unicode.IsSpace(rune(b))
}

// splitSpaces returns a slice of byte slices which are the space
// delimited words from the original byte slice. Unlike
// strings.Split($X, " "), runs of multiple spaces in a row are
Expand All @@ -153,16 +147,16 @@ func splitSpaces(b []byte) [][]byte {
lenB := len(b)
for i = 0; i < lenB; i++ {
// fast forward past any spaces
for i < lenB-1 && isSpace(b[i]) {
for i < lenB-1 && unicode.IsSpace(rune(b[i])) {
i++
start = i
}
for i < lenB-1 && !isSpace(b[i]) {
for i < lenB-1 && !unicode.IsSpace(rune(b[i])) {
i++
}
if i > start {
// we sometimes have to rewind
if i < lenB-1 && isSpace(b[i]) {
if i < lenB-1 && unicode.IsSpace(rune(b[i])) {
i--
}
res = append(res, b[start:i+1])
Expand Down

0 comments on commit 33d7fe8

Please sign in to comment.