예제 #1
0
// ScanEnum is a helper function to simplify the implementation of fmt.Scanner
// methods for "enum-like" types, that is, user-defined types where the set of
// values and string representations is fixed.
// ScanEnum allows multiple string representations for the same value.
//
// State is the state passed to the implementation of the fmt.Scanner method.
// Values holds as map values the values of the type, with their string
// representations as keys.
// If fold is true, comparison of the string representation uses
// strings.EqualFold, otherwise the equal operator for strings.
//
// On a match, ScanEnum stops after reading the last rune of the matched string,
// and returns the corresponding value together with a nil error.
// On no match, ScanEnum attempts to unread the last rune (the first rune that
// could not potentially match any of the values), and returns a non-nil error,
// together with a nil value for interface{}.
// On I/O error, ScanEnum returns the I/O error, together with a nil value for
// interface{}.
//
func scanEnum(state fmt.ScanState, values map[string]interface{}, fold bool) (
	interface{}, error) {
	//
	rd := make([]rune, 0, scanEnumBufferHint)
	keys := make(map[string]struct{}, len(values)) // potential keys
	for s, _ := range values {
		keys[s] = struct{}{}
	}
	for {
		r, _, err := state.ReadRune()
		if err != nil {
			return nil, err
		}
		rd = append(rd, r)
		srd := string(rd)
		lrd := len(srd)
		for s, _ := range keys {
			if strEq(srd, s, fold) {
				return values[s], nil
			}
			if len(rd) < len(s) && !strEq(srd, s[:lrd], fold) {
				delete(keys, s)
			}
		}
		if len(keys) == 0 {
			state.UnreadRune()
			return nil, fmt.Errorf("unsupported value %q", srd)
		}
	}
	panic("never reached")
}
예제 #2
0
파일: int.go 프로젝트: locusf/gmp
// Scan is a support routine for fmt.Scanner; it sets z to the value of
// the scanned number. It accepts the formats 'b' (binary), 'o' (octal),
// 'd' (decimal), 'x' (lowercase hexadecimal), and 'X' (uppercase hexadecimal).
func (z *Int) Scan(s fmt.ScanState, ch rune) error {
	s.SkipSpace() // skip leading space characters
	base := 0
	switch ch {
	case 'b':
		base = 2
	case 'o':
		base = 8
	case 'd':
		base = 10
	case 'x', 'X':
		base = 16
	case 's', 'v':
		// let scan determine the base
	case 'z':
		base = 60
	default:
		return errors.New("Int.Scan: invalid verb")
	}
	charset := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz."
	if base != 0 {
		charset = charset[:base]
	}

	// Read the number into in
	in := make([]byte, 0, 16)
	var err error
	var n int
	for {
		ch, n, err = s.ReadRune()
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}
		if n > 1 {
			// Wide character - must be the end
			s.UnreadRune()
			break
		}
		if len(in) == 0 {
			if ch == '+' {
				// Skip leading + as gmp doesn't understand them
				continue
			}
			if ch == '-' {
				goto ok
			}
		}
		if len(in) == 1 && base == 0 {
			if ch == 'b' || ch == 'x' {
				goto ok
			}
		}
		if !strings.ContainsRune(charset, ch) {
			// Bad character - end
			s.UnreadRune()
			break
		}
	ok:
		in = append(in, byte(ch))
	}

	// Use GMP to convert it as it is very efficient for large numbers
	z.doinit()
	// null terminate for C
	in = append(in, 0)
	if C.mpz_set_str(&z.i[0], (*C.char)(unsafe.Pointer(&in[0])), C.int(base)) < 0 {
		return errors.New("Int.Scan: failed")
	}
	return nil
}