// 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") }
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 }
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 }
// 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 }