1
2
3
4
5
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
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
78
79
80
81
82
83
84
85
86
87
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
124
125
126
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
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
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
192
193
194
195
196
197
198
199
200
201
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
208
209
210
211
212
213
214
215
216
217
218
219
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"
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
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"
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
290
291
292
293
294
295
296
297
298
299
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
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
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
369
370
371
372
373
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
385
386
387
388
389
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
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
430
431
432
433
434
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
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
458
459
460
461
462
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
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
487
488
489
490
491
492
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
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
509
510
511
512
513
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
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
537
538
539
540
541
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
554 func consumeDisplayString(s string) (consumed, rest string, ok bool) {
555
556
557
558
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
613
614
615
616
617
618
619
620 func ParseDisplayString(s string) (parsed string, ok bool) {
621 if _, rest, ok := consumeDisplayString(s); !ok || rest != "" {
622 return "", false
623 }
624
625
626
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
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