Source file src/net/http/internal/httpsfv/httpsfv.go

     1  // Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT.
     2  //go:generate bundle -o httpsfv.go -prefix= golang.org/x/net/internal/httpsfv
     3  
     4  // Package httpsfv provides functionality for dealing with HTTP Structured
     5  // Field Values.
     6  //
     7  
     8  package httpsfv
     9  
    10  import (
    11  	"slices"
    12  	"strconv"
    13  	"strings"
    14  	"time"
    15  	"unicode/utf8"
    16  )
    17  
    18  func isLCAlpha(b byte) bool {
    19  	return (b >= 'a' && b <= 'z')
    20  }
    21  
    22  func isAlpha(b byte) bool {
    23  	return isLCAlpha(b) || (b >= 'A' && b <= 'Z')
    24  }
    25  
    26  func isDigit(b byte) bool {
    27  	return b >= '0' && b <= '9'
    28  }
    29  
    30  func isVChar(b byte) bool {
    31  	return b >= 0x21 && b <= 0x7e
    32  }
    33  
    34  func isSP(b byte) bool {
    35  	return b == 0x20
    36  }
    37  
    38  func isTChar(b byte) bool {
    39  	if isAlpha(b) || isDigit(b) {
    40  		return true
    41  	}
    42  	return slices.Contains([]byte{'!', '#', '$', '%', '&', '\'', '*', '+', '-', '.', '^', '_', '`', '|', '~'}, b)
    43  }
    44  
    45  func countLeftWhitespace(s string) int {
    46  	i := 0
    47  	for _, ch := range []byte(s) {
    48  		if ch != ' ' && ch != '\t' {
    49  			break
    50  		}
    51  		i++
    52  	}
    53  	return i
    54  }
    55  
    56  // https://www.rfc-editor.org/rfc/rfc4648#section-8.
    57  func decOctetHex(ch1, ch2 byte) (ch byte, ok bool) {
    58  	decBase16 := func(in byte) (out byte, ok bool) {
    59  		if !isDigit(in) && !(in >= 'a' && in <= 'f') {
    60  			return 0, false
    61  		}
    62  		if isDigit(in) {
    63  			return in - '0', true
    64  		}
    65  		return in - 'a' + 10, true
    66  	}
    67  
    68  	if ch1, ok = decBase16(ch1); !ok {
    69  		return 0, ok
    70  	}
    71  	if ch2, ok = decBase16(ch2); !ok {
    72  		return 0, ok
    73  	}
    74  	return ch1<<4 | ch2, true
    75  }
    76  
    77  // ParseList parses a list from a given HTTP Structured Field Values.
    78  //
    79  // Given an HTTP SFV string that represents a list, it will call the given
    80  // function using each of the members and parameters contained in the list.
    81  // This allows the caller to extract information out of the list.
    82  //
    83  // This function will return once it encounters the end of the string, or
    84  // something that is not a list. If it cannot consume the entire given
    85  // string, the ok value returned will be false.
    86  //
    87  // https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-list.
    88  func ParseList(s string, f func(member, param string)) (ok bool) {
    89  	for len(s) != 0 {
    90  		var member, param string
    91  		if len(s) != 0 && s[0] == '(' {
    92  			if member, s, ok = consumeBareInnerList(s, nil); !ok {
    93  				return ok
    94  			}
    95  		} else {
    96  			if member, s, ok = consumeBareItem(s); !ok {
    97  				return ok
    98  			}
    99  		}
   100  		if param, s, ok = consumeParameter(s, nil); !ok {
   101  			return ok
   102  		}
   103  		if f != nil {
   104  			f(member, param)
   105  		}
   106  
   107  		s = s[countLeftWhitespace(s):]
   108  		if len(s) == 0 {
   109  			break
   110  		}
   111  		if s[0] != ',' {
   112  			return false
   113  		}
   114  		s = s[1:]
   115  		s = s[countLeftWhitespace(s):]
   116  		if len(s) == 0 {
   117  			return false
   118  		}
   119  	}
   120  	return true
   121  }
   122  
   123  // consumeBareInnerList consumes an inner list
   124  // (https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-an-inner-list),
   125  // except for the inner list's top-most parameter.
   126  // For example, given `(a;b c;d);e`, it will consume only `(a;b c;d)`.
   127  func consumeBareInnerList(s string, f func(bareItem, param string)) (consumed, rest string, ok bool) {
   128  	if len(s) == 0 || s[0] != '(' {
   129  		return "", s, false
   130  	}
   131  	rest = s[1:]
   132  	for len(rest) != 0 {
   133  		var bareItem, param string
   134  		rest = rest[countLeftWhitespace(rest):]
   135  		if len(rest) != 0 && rest[0] == ')' {
   136  			rest = rest[1:]
   137  			break
   138  		}
   139  		if bareItem, rest, ok = consumeBareItem(rest); !ok {
   140  			return "", s, ok
   141  		}
   142  		if param, rest, ok = consumeParameter(rest, nil); !ok {
   143  			return "", s, ok
   144  		}
   145  		if len(rest) == 0 || (rest[0] != ')' && !isSP(rest[0])) {
   146  			return "", s, false
   147  		}
   148  		if f != nil {
   149  			f(bareItem, param)
   150  		}
   151  	}
   152  	return s[:len(s)-len(rest)], rest, true
   153  }
   154  
   155  // ParseBareInnerList parses a bare inner list from a given HTTP Structured
   156  // Field Values.
   157  //
   158  // We define a bare inner list as an inner list
   159  // (https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-an-inner-list),
   160  // without the top-most parameter of the inner list. For example, given the
   161  // inner list `(a;b c;d);e`, the bare inner list would be `(a;b c;d)`.
   162  //
   163  // Given an HTTP SFV string that represents a bare inner list, it will call the
   164  // given function using each of the bare item and parameter within the bare
   165  // inner list. This allows the caller to extract information out of the bare
   166  // inner list.
   167  //
   168  // This function will return once it encounters the end of the bare inner list,
   169  // or something that is not a bare inner list. If it cannot consume the entire
   170  // given string, the ok value returned will be false.
   171  func ParseBareInnerList(s string, f func(bareItem, param string)) (ok bool) {
   172  	_, rest, ok := consumeBareInnerList(s, f)
   173  	return rest == "" && ok
   174  }
   175  
   176  // https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-an-item.
   177  func consumeItem(s string, f func(bareItem, param string)) (consumed, rest string, ok bool) {
   178  	var bareItem, param string
   179  	if bareItem, rest, ok = consumeBareItem(s); !ok {
   180  		return "", s, ok
   181  	}
   182  	if param, rest, ok = consumeParameter(rest, nil); !ok {
   183  		return "", s, ok
   184  	}
   185  	if f != nil {
   186  		f(bareItem, param)
   187  	}
   188  	return s[:len(s)-len(rest)], rest, true
   189  }
   190  
   191  // ParseItem parses an item from a given HTTP Structured Field Values.
   192  //
   193  // Given an HTTP SFV string that represents an item, it will call the given
   194  // function once, with the bare item and the parameter of the item. This allows
   195  // the caller to extract information out of the item.
   196  //
   197  // This function will return once it encounters the end of the string, or
   198  // something that is not an item. If it cannot consume the entire given
   199  // string, the ok value returned will be false.
   200  //
   201  // https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-an-item.
   202  func ParseItem(s string, f func(bareItem, param string)) (ok bool) {
   203  	_, rest, ok := consumeItem(s, f)
   204  	return rest == "" && ok
   205  }
   206  
   207  // ParseDictionary parses a dictionary from a given HTTP Structured Field
   208  // Values.
   209  //
   210  // Given an HTTP SFV string that represents a dictionary, it will call the
   211  // given function using each of the keys, values, and parameters contained in
   212  // the dictionary. This allows the caller to extract information out of the
   213  // dictionary.
   214  //
   215  // This function will return once it encounters the end of the string, or
   216  // something that is not a dictionary. If it cannot consume the entire given
   217  // string, the ok value returned will be false.
   218  //
   219  // https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-dictionary.
   220  func ParseDictionary(s string, f func(key, val, param string)) (ok bool) {
   221  	for len(s) != 0 {
   222  		var key, val, param string
   223  		val = "?1" // Default value for empty val is boolean true.
   224  		if key, s, ok = consumeKey(s); !ok {
   225  			return ok
   226  		}
   227  		if len(s) != 0 && s[0] == '=' {
   228  			s = s[1:]
   229  			if len(s) != 0 && s[0] == '(' {
   230  				if val, s, ok = consumeBareInnerList(s, nil); !ok {
   231  					return ok
   232  				}
   233  			} else {
   234  				if val, s, ok = consumeBareItem(s); !ok {
   235  					return ok
   236  				}
   237  			}
   238  		}
   239  		if param, s, ok = consumeParameter(s, nil); !ok {
   240  			return ok
   241  		}
   242  		if f != nil {
   243  			f(key, val, param)
   244  		}
   245  		s = s[countLeftWhitespace(s):]
   246  		if len(s) == 0 {
   247  			break
   248  		}
   249  		if s[0] == ',' {
   250  			s = s[1:]
   251  		}
   252  		s = s[countLeftWhitespace(s):]
   253  		if len(s) == 0 {
   254  			return false
   255  		}
   256  	}
   257  	return true
   258  }
   259  
   260  // https://www.rfc-editor.org/rfc/rfc9651.html#parse-param.
   261  func consumeParameter(s string, f func(key, val string)) (consumed, rest string, ok bool) {
   262  	rest = s
   263  	for len(rest) != 0 {
   264  		var key, val string
   265  		val = "?1" // Default value for empty val is boolean true.
   266  		if rest[0] != ';' {
   267  			break
   268  		}
   269  		rest = rest[1:]
   270  		rest = rest[countLeftWhitespace(rest):]
   271  		key, rest, ok = consumeKey(rest)
   272  		if !ok {
   273  			return "", s, ok
   274  		}
   275  		if len(rest) != 0 && rest[0] == '=' {
   276  			rest = rest[1:]
   277  			val, rest, ok = consumeBareItem(rest)
   278  			if !ok {
   279  				return "", s, ok
   280  			}
   281  		}
   282  		if f != nil {
   283  			f(key, val)
   284  		}
   285  	}
   286  	return s[:len(s)-len(rest)], rest, true
   287  }
   288  
   289  // ParseParameter parses a parameter from a given HTTP Structured Field Values.
   290  //
   291  // Given an HTTP SFV string that represents a parameter, it will call the given
   292  // function using each of the keys and values contained in the parameter. This
   293  // allows the caller to extract information out of the parameter.
   294  //
   295  // This function will return once it encounters the end of the string, or
   296  // something that is not a parameter. If it cannot consume the entire given
   297  // string, the ok value returned will be false.
   298  //
   299  // https://www.rfc-editor.org/rfc/rfc9651.html#parse-param.
   300  func ParseParameter(s string, f func(key, val string)) (ok bool) {
   301  	_, rest, ok := consumeParameter(s, f)
   302  	return rest == "" && ok
   303  }
   304  
   305  // https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-key.
   306  func consumeKey(s string) (consumed, rest string, ok bool) {
   307  	if len(s) == 0 || (!isLCAlpha(s[0]) && s[0] != '*') {
   308  		return "", s, false
   309  	}
   310  	i := 0
   311  	for _, ch := range []byte(s) {
   312  		if !isLCAlpha(ch) && !isDigit(ch) && !slices.Contains([]byte("_-.*"), ch) {
   313  			break
   314  		}
   315  		i++
   316  	}
   317  	return s[:i], s[i:], true
   318  }
   319  
   320  // https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-an-integer-or-decim.
   321  func consumeIntegerOrDecimal(s string) (consumed, rest string, ok bool) {
   322  	var i, signOffset, periodIndex int
   323  	var isDecimal bool
   324  	if i < len(s) && s[i] == '-' {
   325  		i++
   326  		signOffset++
   327  	}
   328  	if i >= len(s) {
   329  		return "", s, false
   330  	}
   331  	if !isDigit(s[i]) {
   332  		return "", s, false
   333  	}
   334  	for i < len(s) {
   335  		ch := s[i]
   336  		if isDigit(ch) {
   337  			i++
   338  			continue
   339  		}
   340  		if !isDecimal && ch == '.' {
   341  			if i-signOffset > 12 {
   342  				return "", s, false
   343  			}
   344  			periodIndex = i
   345  			isDecimal = true
   346  			i++
   347  			continue
   348  		}
   349  		break
   350  	}
   351  	if !isDecimal && i-signOffset > 15 {
   352  		return "", s, false
   353  	}
   354  	if isDecimal {
   355  		if i-signOffset > 16 {
   356  			return "", s, false
   357  		}
   358  		if s[i-1] == '.' {
   359  			return "", s, false
   360  		}
   361  		if i-periodIndex-1 > 3 {
   362  			return "", s, false
   363  		}
   364  	}
   365  	return s[:i], s[i:], true
   366  }
   367  
   368  // ParseInteger parses an integer from a given HTTP Structured Field Values.
   369  //
   370  // The entire HTTP SFV string must consist of a valid integer. It returns the
   371  // parsed integer and an ok boolean value, indicating success or not.
   372  //
   373  // https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-an-integer-or-decim.
   374  func ParseInteger(s string) (parsed int64, ok bool) {
   375  	if _, rest, ok := consumeIntegerOrDecimal(s); !ok || rest != "" {
   376  		return 0, false
   377  	}
   378  	if n, err := strconv.ParseInt(s, 10, 64); err == nil {
   379  		return n, true
   380  	}
   381  	return 0, false
   382  }
   383  
   384  // ParseDecimal parses a decimal from a given HTTP Structured Field Values.
   385  //
   386  // The entire HTTP SFV string must consist of a valid decimal. It returns the
   387  // parsed decimal and an ok boolean value, indicating success or not.
   388  //
   389  // https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-an-integer-or-decim.
   390  func ParseDecimal(s string) (parsed float64, ok bool) {
   391  	if _, rest, ok := consumeIntegerOrDecimal(s); !ok || rest != "" {
   392  		return 0, false
   393  	}
   394  	if !strings.Contains(s, ".") {
   395  		return 0, false
   396  	}
   397  	if n, err := strconv.ParseFloat(s, 64); err == nil {
   398  		return n, true
   399  	}
   400  	return 0, false
   401  }
   402  
   403  // https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-string.
   404  func consumeString(s string) (consumed, rest string, ok bool) {
   405  	if len(s) == 0 || s[0] != '"' {
   406  		return "", s, false
   407  	}
   408  	for i := 1; i < len(s); i++ {
   409  		switch ch := s[i]; ch {
   410  		case '\\':
   411  			if i+1 >= len(s) {
   412  				return "", s, false
   413  			}
   414  			i++
   415  			if ch = s[i]; ch != '"' && ch != '\\' {
   416  				return "", s, false
   417  			}
   418  		case '"':
   419  			return s[:i+1], s[i+1:], true
   420  		default:
   421  			if !isVChar(ch) && !isSP(ch) {
   422  				return "", s, false
   423  			}
   424  		}
   425  	}
   426  	return "", s, false
   427  }
   428  
   429  // ParseString parses a Go string from a given HTTP Structured Field Values.
   430  //
   431  // The entire HTTP SFV string must consist of a valid string. It returns the
   432  // parsed string and an ok boolean value, indicating success or not.
   433  //
   434  // https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-string.
   435  func ParseString(s string) (parsed string, ok bool) {
   436  	if _, rest, ok := consumeString(s); !ok || rest != "" {
   437  		return "", false
   438  	}
   439  	return s[1 : len(s)-1], true
   440  }
   441  
   442  // https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-token
   443  func consumeToken(s string) (consumed, rest string, ok bool) {
   444  	if len(s) == 0 || (!isAlpha(s[0]) && s[0] != '*') {
   445  		return "", s, false
   446  	}
   447  	i := 0
   448  	for _, ch := range []byte(s) {
   449  		if !isTChar(ch) && !slices.Contains([]byte(":/"), ch) {
   450  			break
   451  		}
   452  		i++
   453  	}
   454  	return s[:i], s[i:], true
   455  }
   456  
   457  // ParseToken parses a token from a given HTTP Structured Field Values.
   458  //
   459  // The entire HTTP SFV string must consist of a valid token. It returns the
   460  // parsed token and an ok boolean value, indicating success or not.
   461  //
   462  // https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-token
   463  func ParseToken(s string) (parsed string, ok bool) {
   464  	if _, rest, ok := consumeToken(s); !ok || rest != "" {
   465  		return "", false
   466  	}
   467  	return s, true
   468  }
   469  
   470  // https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-byte-sequence.
   471  func consumeByteSequence(s string) (consumed, rest string, ok bool) {
   472  	if len(s) == 0 || s[0] != ':' {
   473  		return "", s, false
   474  	}
   475  	for i := 1; i < len(s); i++ {
   476  		if ch := s[i]; ch == ':' {
   477  			return s[:i+1], s[i+1:], true
   478  		}
   479  		if ch := s[i]; !isAlpha(ch) && !isDigit(ch) && !slices.Contains([]byte("+/="), ch) {
   480  			return "", s, false
   481  		}
   482  	}
   483  	return "", s, false
   484  }
   485  
   486  // ParseByteSequence parses a byte sequence from a given HTTP Structured Field
   487  // Values.
   488  //
   489  // The entire HTTP SFV string must consist of a valid byte sequence. It returns
   490  // the parsed byte sequence and an ok boolean value, indicating success or not.
   491  //
   492  // https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-byte-sequence.
   493  func ParseByteSequence(s string) (parsed []byte, ok bool) {
   494  	if _, rest, ok := consumeByteSequence(s); !ok || rest != "" {
   495  		return nil, false
   496  	}
   497  	return []byte(s[1 : len(s)-1]), true
   498  }
   499  
   500  // https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-boolean.
   501  func consumeBoolean(s string) (consumed, rest string, ok bool) {
   502  	if len(s) >= 2 && (s[:2] == "?0" || s[:2] == "?1") {
   503  		return s[:2], s[2:], true
   504  	}
   505  	return "", s, false
   506  }
   507  
   508  // ParseBoolean parses a boolean from a given HTTP Structured Field Values.
   509  //
   510  // The entire HTTP SFV string must consist of a valid boolean. It returns the
   511  // parsed boolean and an ok boolean value, indicating success or not.
   512  //
   513  // https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-boolean.
   514  func ParseBoolean(s string) (parsed bool, ok bool) {
   515  	if _, rest, ok := consumeBoolean(s); !ok || rest != "" {
   516  		return false, false
   517  	}
   518  	return s == "?1", true
   519  }
   520  
   521  // https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-date.
   522  func consumeDate(s string) (consumed, rest string, ok bool) {
   523  	if len(s) == 0 || s[0] != '@' {
   524  		return "", s, false
   525  	}
   526  	if _, rest, ok = consumeIntegerOrDecimal(s[1:]); !ok {
   527  		return "", s, ok
   528  	}
   529  	consumed = s[:len(s)-len(rest)]
   530  	if slices.Contains([]byte(consumed), '.') {
   531  		return "", s, false
   532  	}
   533  	return consumed, rest, ok
   534  }
   535  
   536  // ParseDate parses a date from a given HTTP Structured Field Values.
   537  //
   538  // The entire HTTP SFV string must consist of a valid date. It returns the
   539  // parsed date and an ok boolean value, indicating success or not.
   540  //
   541  // https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-date.
   542  func ParseDate(s string) (parsed time.Time, ok bool) {
   543  	if _, rest, ok := consumeDate(s); !ok || rest != "" {
   544  		return time.Time{}, false
   545  	}
   546  	if n, ok := ParseInteger(s[1:]); !ok {
   547  		return time.Time{}, false
   548  	} else {
   549  		return time.Unix(n, 0), true
   550  	}
   551  }
   552  
   553  // https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-display-string.
   554  func consumeDisplayString(s string) (consumed, rest string, ok bool) {
   555  	// To prevent excessive allocation, especially when input is large, we
   556  	// maintain a buffer of 4 bytes to keep track of the last rune we
   557  	// encounter. This way, we can validate that the display string conforms to
   558  	// UTF-8 without actually building the whole string.
   559  	var lastRune [4]byte
   560  	var runeLen int
   561  	isPartOfValidRune := func(ch byte) bool {
   562  		lastRune[runeLen] = ch
   563  		runeLen++
   564  		if utf8.FullRune(lastRune[:runeLen]) {
   565  			r, s := utf8.DecodeRune(lastRune[:runeLen])
   566  			if r == utf8.RuneError {
   567  				return false
   568  			}
   569  			copy(lastRune[:], lastRune[s:runeLen])
   570  			runeLen -= s
   571  			return true
   572  		}
   573  		return runeLen <= 4
   574  	}
   575  
   576  	if len(s) <= 1 || s[:2] != `%"` {
   577  		return "", s, false
   578  	}
   579  	i := 2
   580  	for i < len(s) {
   581  		ch := s[i]
   582  		if !isVChar(ch) && !isSP(ch) {
   583  			return "", s, false
   584  		}
   585  		switch ch {
   586  		case '"':
   587  			if runeLen > 0 {
   588  				return "", s, false
   589  			}
   590  			return s[:i+1], s[i+1:], true
   591  		case '%':
   592  			if i+2 >= len(s) {
   593  				return "", s, false
   594  			}
   595  			if ch, ok = decOctetHex(s[i+1], s[i+2]); !ok {
   596  				return "", s, ok
   597  			}
   598  			if ok = isPartOfValidRune(ch); !ok {
   599  				return "", s, ok
   600  			}
   601  			i += 3
   602  		default:
   603  			if ok = isPartOfValidRune(ch); !ok {
   604  				return "", s, ok
   605  			}
   606  			i++
   607  		}
   608  	}
   609  	return "", s, false
   610  }
   611  
   612  // ParseDisplayString parses a display string from a given HTTP Structured
   613  // Field Values.
   614  //
   615  // The entire HTTP SFV string must consist of a valid display string. It
   616  // returns the parsed display string and an ok boolean value, indicating
   617  // success or not.
   618  //
   619  // https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-display-string.
   620  func ParseDisplayString(s string) (parsed string, ok bool) {
   621  	if _, rest, ok := consumeDisplayString(s); !ok || rest != "" {
   622  		return "", false
   623  	}
   624  	// consumeDisplayString() already validates that we have a valid display
   625  	// string. Therefore, we can just construct the display string, without
   626  	// validating it again.
   627  	s = s[2 : len(s)-1]
   628  	var b strings.Builder
   629  	for i := 0; i < len(s); {
   630  		if s[i] == '%' {
   631  			decoded, _ := decOctetHex(s[i+1], s[i+2])
   632  			b.WriteByte(decoded)
   633  			i += 3
   634  			continue
   635  		}
   636  		b.WriteByte(s[i])
   637  		i++
   638  	}
   639  	return b.String(), true
   640  }
   641  
   642  // https://www.rfc-editor.org/rfc/rfc9651.html#parse-bare-item.
   643  func consumeBareItem(s string) (consumed, rest string, ok bool) {
   644  	if len(s) == 0 {
   645  		return "", s, false
   646  	}
   647  	ch := s[0]
   648  	switch {
   649  	case ch == '-' || isDigit(ch):
   650  		return consumeIntegerOrDecimal(s)
   651  	case ch == '"':
   652  		return consumeString(s)
   653  	case ch == '*' || isAlpha(ch):
   654  		return consumeToken(s)
   655  	case ch == ':':
   656  		return consumeByteSequence(s)
   657  	case ch == '?':
   658  		return consumeBoolean(s)
   659  	case ch == '@':
   660  		return consumeDate(s)
   661  	case ch == '%':
   662  		return consumeDisplayString(s)
   663  	default:
   664  		return "", s, false
   665  	}
   666  }
   667  

View as plain text