Ejemplo n.º 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")
}
Ejemplo n.º 2
0
func (c *coords) Scan(state fmt.ScanState, verb rune) error {
	rx, _, _ := state.ReadRune()
	ry, _, _ := state.ReadRune()
	if rx < 'A' || 'G' < rx || ry < '1' || '8' < ry {
		return fmt.Errorf("Illegal chess coordinates: <%c, %c>", rx, ry)
	}
	c.x = int(rx - 'A')
	c.y = int(ry - '1')
	return nil
}
Ejemplo n.º 3
0
func (sum *SpamSum) Scan(state fmt.ScanState, verb rune) error {
	var blocksize int
	var leftPart, rightPart, blockPart, buffer []byte
	var err error

	if blockPart, err = state.Token(false, // do not skip spaces
		func(r rune) bool {
			return unicode.IsDigit(r)
		}); err != nil {
		return err
	} else if len(blockPart) == 0 {
		return errors.New("Cannot read block size.")
	}

	if blocksize, err = strconv.Atoi(string(blockPart)); err != nil {
		return err
	} else if blocksize < 3 {
		return errors.New("Block size too small")
	}

	if r, _, err := state.ReadRune(); err != nil {
		return err
	} else if r != ':' {
		return errors.New("Invalid token delimiter")
	}

	if buffer, err = state.Token(false, // do not skip spaces
		func(r rune) bool {
			return (bytes.IndexRune([]byte(b64), r) != -1)
		}); err != nil {
		return err
	} else if len(buffer) > SpamsumLength {
		return errors.New("First base64 string too long")
	}

	leftPart = make([]byte, len(buffer))
	copy(leftPart, buffer[:])

	if r, _, err := state.ReadRune(); err != nil {
		return err
	} else if r != ':' {
		return errors.New("Invalid token delimiter")
	}

	if buffer, err = state.Token(false, // do not skip spaces
		func(r rune) bool {
			return (bytes.IndexRune([]byte(b64), r) != -1)
		}); err != nil {
		return err
	} else if len(buffer) > (SpamsumLength / 2) {
		return errors.New("Second base64 string too long")
	}

	rightPart = make([]byte, len(buffer))
	copy(rightPart[:], buffer)

	sum.blocksize = uint32(blocksize)
	copy(sum.leftPart[:], leftPart)
	copy(sum.rightPart[:], rightPart)
	sum.leftIndex = len(leftPart)
	sum.rightIndex = len(rightPart)

	return nil
}
Ejemplo n.º 4
0
Archivo: int.go Proyecto: 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
}