1
2
3
4
5 package demangle
6
7 import (
8 "fmt"
9 "math"
10 "math/bits"
11 "strings"
12 "unicode/utf8"
13 )
14
15
16 func rustToString(name string, options []Option) (ret string, err error) {
17 if !strings.HasPrefix(name, "_R") {
18 return "", ErrNotMangledName
19 }
20
21
22
23 defer func() {
24 if r := recover(); r != nil {
25 if de, ok := r.(demangleErr); ok {
26 ret = ""
27 err = de
28 return
29 }
30 panic(r)
31 }
32 }()
33
34 suffix := ""
35 dot := strings.Index(name, ".")
36 if dot >= 0 {
37 suffix = name[dot:]
38 name = name[:dot]
39 }
40
41 name = name[2:]
42 rst := &rustState{orig: name, str: name}
43 rst.symbolName()
44
45 if len(rst.str) > 0 {
46 rst.fail("unparsed characters at end of mangled name")
47 }
48
49 if suffix != "" {
50 rst.skip = false
51 rst.writeString(" (")
52 rst.writeString(suffix)
53 rst.writeByte(')')
54 }
55
56 return rst.buf.String(), nil
57 }
58
59
60 type rustState struct {
61 orig string
62 str string
63 off int
64 buf strings.Builder
65 skip bool
66 lifetimes int64
67 last byte
68 }
69
70
71 func (rst *rustState) fail(err string) {
72 panic(demangleErr{err: err, off: rst.off})
73 }
74
75
76 func (rst *rustState) advance(add int) {
77 if len(rst.str) < add {
78 panic("internal error")
79 }
80 rst.str = rst.str[add:]
81 rst.off += add
82 }
83
84
85
86 func (rst *rustState) checkChar(c byte) {
87 if len(rst.str) == 0 || rst.str[0] != c {
88 rst.fail("expected " + string(c))
89 }
90 rst.advance(1)
91 }
92
93
94 func (rst *rustState) writeByte(c byte) {
95 if rst.skip {
96 return
97 }
98 rst.last = c
99 rst.buf.WriteByte(c)
100 }
101
102
103 func (rst *rustState) writeString(s string) {
104 if rst.skip {
105 return
106 }
107 if len(s) > 0 {
108 rst.last = s[len(s)-1]
109 rst.buf.WriteString(s)
110 }
111 }
112
113
114
115
116
117 func (rst *rustState) symbolName() {
118 if len(rst.str) < 1 {
119 rst.fail("expected symbol-name")
120 }
121
122 if isDigit(rst.str[0]) {
123 rst.fail("unsupported Rust encoding version")
124 }
125
126 rst.path(true)
127
128 if len(rst.str) > 0 {
129 rst.skip = true
130 rst.path(false)
131 }
132 }
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148 func (rst *rustState) path(needsSeparator bool) {
149 if len(rst.str) < 1 {
150 rst.fail("expected path")
151 }
152 switch c := rst.str[0]; c {
153 case 'C':
154 rst.advance(1)
155 _, ident := rst.identifier()
156 rst.writeString(ident)
157 case 'M', 'X':
158 rst.advance(1)
159 rst.implPath()
160 rst.writeByte('<')
161 rst.demangleType()
162 if c == 'X' {
163 rst.writeString(" as ")
164 rst.path(false)
165 }
166 rst.writeByte('>')
167 case 'Y':
168 rst.advance(1)
169 rst.writeByte('<')
170 rst.demangleType()
171 rst.writeString(" as ")
172 rst.path(false)
173 rst.writeByte('>')
174 case 'N':
175 rst.advance(1)
176
177 if len(rst.str) < 1 {
178 rst.fail("expected namespace")
179 }
180 ns := rst.str[0]
181 switch {
182 case ns >= 'a' && ns <= 'z':
183 case ns >= 'A' && ns <= 'Z':
184 default:
185 rst.fail("invalid namespace character")
186 }
187 rst.advance(1)
188
189 rst.path(needsSeparator)
190
191 dis, ident := rst.identifier()
192
193 if ns >= 'A' && ns <= 'Z' {
194 rst.writeString("::{")
195 switch ns {
196 case 'C':
197 rst.writeString("closure")
198 case 'S':
199 rst.writeString("shim")
200 default:
201 rst.writeByte(ns)
202 }
203 if len(ident) > 0 {
204 rst.writeByte(':')
205 rst.writeString(ident)
206 }
207 if !rst.skip {
208 fmt.Fprintf(&rst.buf, "#%d}", dis)
209 rst.last = '}'
210 }
211 } else {
212 rst.writeString("::")
213 rst.writeString(ident)
214 }
215 case 'I':
216 rst.advance(1)
217 rst.path(needsSeparator)
218 if needsSeparator {
219 rst.writeString("::")
220 }
221 rst.writeByte('<')
222 first := true
223 for len(rst.str) > 0 && rst.str[0] != 'E' {
224 if first {
225 first = false
226 } else {
227 rst.writeString(", ")
228 }
229 rst.genericArg()
230 }
231 rst.writeByte('>')
232 rst.checkChar('E')
233 case 'B':
234 rst.backref(func() { rst.path(needsSeparator) })
235 default:
236 rst.fail("unrecognized letter in path")
237 }
238 }
239
240
241 func (rst *rustState) implPath() {
242
243 hold := rst.skip
244 rst.skip = true
245 defer func() {
246 rst.skip = hold
247 }()
248
249 rst.disambiguator()
250 rst.path(false)
251 }
252
253
254
255 func (rst *rustState) identifier() (int64, string) {
256 dis := rst.disambiguator()
257 ident := rst.undisambiguatedIdentifier()
258 return dis, ident
259 }
260
261
262
263 func (rst *rustState) disambiguator() int64 {
264 if len(rst.str) == 0 || rst.str[0] != 's' {
265 return 0
266 }
267 rst.advance(1)
268 return rst.base62Number() + 1
269 }
270
271
272 func (rst *rustState) undisambiguatedIdentifier() string {
273 punycode := false
274 if len(rst.str) > 0 && rst.str[0] == 'u' {
275 rst.advance(1)
276 punycode = true
277 }
278
279 val := rst.decimalNumber()
280
281 if len(rst.str) > 0 && rst.str[0] == '_' {
282 rst.advance(1)
283 }
284
285 if len(rst.str) < val {
286 rst.fail("not enough characters for identifier")
287 }
288 id := rst.str[:val]
289 rst.advance(val)
290
291 for i := 0; i < len(id); i++ {
292 c := id[i]
293 switch {
294 case c >= '0' && c <= '9':
295 case c >= 'A' && c <= 'Z':
296 case c >= 'a' && c <= 'z':
297 case c == '_':
298 default:
299 rst.fail("invalid character in identifier")
300 }
301 }
302
303 if punycode {
304 id = rst.expandPunycode(id)
305 }
306
307 return id
308 }
309
310
311
312 func (rst *rustState) expandPunycode(s string) string {
313 const (
314 base = 36
315 tmin = 1
316 tmax = 26
317 skew = 38
318 damp = 700
319 initialBias = 72
320 initialN = 128
321 )
322
323 idx := strings.LastIndex(s, "_")
324 if idx < 0 {
325 rst.fail("missing underscore in punycode string")
326 }
327
328 output := []rune(s[:idx])
329 encoding := s[idx+1:]
330
331 i := 0
332 n := initialN
333 bias := initialBias
334
335 pos := 0
336 for pos < len(encoding) {
337 oldI := i
338 w := 1
339 for k := base; ; k += base {
340 if pos == len(encoding) {
341 rst.fail("unterminated punycode")
342 }
343
344 var digit byte
345 d := encoding[pos]
346 pos++
347 switch {
348 case '0' <= d && d <= '9':
349 digit = d - '0' + 26
350 case 'A' <= d && d <= 'Z':
351 digit = d - 'A'
352 case 'a' <= d && d <= 'z':
353 digit = d - 'a'
354 default:
355 rst.fail("invalid punycode digit")
356 }
357
358 i += int(digit) * w
359 if i < 0 {
360 rst.fail("punycode number overflow")
361 }
362
363 var t int
364 if k <= bias {
365 t = tmin
366 } else if k > bias+tmax {
367 t = tmax
368 } else {
369 t = k - bias
370 }
371
372 if int(digit) < t {
373 break
374 }
375
376 if w >= math.MaxInt32/base {
377 rst.fail("punycode number overflow")
378 }
379 w *= base - t
380 }
381
382 delta := i - oldI
383 numPoints := len(output) + 1
384 firstTime := oldI == 0
385 if firstTime {
386 delta /= damp
387 } else {
388 delta /= 2
389 }
390 delta += delta / numPoints
391 k := 0
392 for delta > ((base-tmin)*tmax)/2 {
393 delta /= base - tmin
394 k += base
395 }
396 bias = k + ((base-tmin+1)*delta)/(delta+skew)
397
398 n += i / (len(output) + 1)
399 if n > utf8.MaxRune {
400 rst.fail("punycode rune overflow")
401 }
402 i %= len(output) + 1
403 output = append(output, 0)
404 copy(output[i+1:], output[i:])
405 output[i] = rune(n)
406 i++
407 }
408
409 return string(output)
410 }
411
412
413
414
415
416 func (rst *rustState) genericArg() {
417 if len(rst.str) < 1 {
418 rst.fail("expected generic-arg")
419 }
420 if rst.str[0] == 'L' {
421 rst.advance(1)
422 rst.writeLifetime(rst.base62Number())
423 } else if rst.str[0] == 'K' {
424 rst.advance(1)
425 rst.demangleConst()
426 } else {
427 rst.demangleType()
428 }
429 }
430
431
432
433 func (rst *rustState) binder() {
434 if len(rst.str) < 1 || rst.str[0] != 'G' {
435 return
436 }
437 rst.advance(1)
438
439 binderLifetimes := rst.base62Number() + 1
440
441
442 if binderLifetimes >= int64(len(rst.str))-rst.lifetimes {
443 rst.fail("binder lifetimes overflow")
444 }
445
446 rst.writeString("for<")
447 for i := int64(0); i < binderLifetimes; i++ {
448 if i > 0 {
449 rst.writeString(", ")
450 }
451 rst.lifetimes++
452 rst.writeLifetime(1)
453 }
454 rst.writeString("> ")
455 }
456
457
458
459
460
461
462
463
464
465
466
467
468
469 func (rst *rustState) demangleType() {
470 if len(rst.str) < 1 {
471 rst.fail("expected type")
472 }
473 c := rst.str[0]
474 if c >= 'a' && c <= 'z' {
475 rst.basicType()
476 return
477 }
478 switch c {
479 case 'C', 'M', 'X', 'Y', 'N', 'I':
480 rst.path(false)
481 case 'A', 'S':
482 rst.advance(1)
483 rst.writeByte('[')
484 rst.demangleType()
485 if c == 'A' {
486 rst.writeString("; ")
487 rst.demangleConst()
488 }
489 rst.writeByte(']')
490 case 'T':
491 rst.advance(1)
492 rst.writeByte('(')
493 c := 0
494 for len(rst.str) > 0 && rst.str[0] != 'E' {
495 if c > 0 {
496 rst.writeString(", ")
497 }
498 c++
499 rst.demangleType()
500 }
501 if c == 1 {
502 rst.writeByte(',')
503 }
504 rst.writeByte(')')
505 rst.checkChar('E')
506 case 'R', 'Q':
507 rst.advance(1)
508 rst.writeByte('&')
509 if len(rst.str) > 0 && rst.str[0] == 'L' {
510 rst.advance(1)
511 if lifetime := rst.base62Number(); lifetime > 0 {
512 rst.writeLifetime(lifetime)
513 rst.writeByte(' ')
514 }
515 }
516 if c == 'Q' {
517 rst.writeString("mut ")
518 }
519 rst.demangleType()
520 case 'P':
521 rst.advance(1)
522 rst.writeString("*const ")
523 rst.demangleType()
524 case 'O':
525 rst.advance(1)
526 rst.writeString("*mut ")
527 rst.demangleType()
528 case 'F':
529 rst.advance(1)
530 hold := rst.lifetimes
531 rst.fnSig()
532 rst.lifetimes = hold
533 case 'D':
534 rst.advance(1)
535 hold := rst.lifetimes
536 rst.dynBounds()
537 rst.lifetimes = hold
538 if len(rst.str) == 0 || rst.str[0] != 'L' {
539 rst.fail("expected L")
540 }
541 rst.advance(1)
542 if lifetime := rst.base62Number(); lifetime > 0 {
543 if rst.last != ' ' {
544 rst.writeByte(' ')
545 }
546 rst.writeString("+ ")
547 rst.writeLifetime(lifetime)
548 }
549 case 'B':
550 rst.backref(rst.demangleType)
551 default:
552 rst.fail("unrecognized character in type")
553 }
554 }
555
556 var rustBasicTypes = map[byte]string{
557 'a': "i8",
558 'b': "bool",
559 'c': "char",
560 'd': "f64",
561 'e': "str",
562 'f': "f32",
563 'h': "u8",
564 'i': "isize",
565 'j': "usize",
566 'l': "i32",
567 'm': "u32",
568 'n': "i128",
569 'o': "u128",
570 'p': "_",
571 's': "i16",
572 't': "u16",
573 'u': "()",
574 'v': "...",
575 'x': "i64",
576 'y': "u64",
577 'z': "!",
578 }
579
580
581 func (rst *rustState) basicType() {
582 if len(rst.str) < 1 {
583 rst.fail("expected basic type")
584 }
585 str, ok := rustBasicTypes[rst.str[0]]
586 if !ok {
587 rst.fail("unrecognized basic type character")
588 }
589 rst.advance(1)
590 rst.writeString(str)
591 }
592
593
594
595
596 func (rst *rustState) fnSig() {
597 rst.binder()
598 if len(rst.str) > 0 && rst.str[0] == 'U' {
599 rst.advance(1)
600 rst.writeString("unsafe ")
601 }
602 if len(rst.str) > 0 && rst.str[0] == 'K' {
603 rst.advance(1)
604 if len(rst.str) > 0 && rst.str[0] == 'C' {
605 rst.advance(1)
606 rst.writeString(`extern "C" `)
607 } else {
608 rst.writeString(`extern "`)
609 id := rst.undisambiguatedIdentifier()
610 id = strings.ReplaceAll(id, "_", "-")
611 rst.writeString(id)
612 rst.writeString(`" `)
613 }
614 }
615 rst.writeString("fn(")
616 first := true
617 for len(rst.str) > 0 && rst.str[0] != 'E' {
618 if first {
619 first = false
620 } else {
621 rst.writeString(", ")
622 }
623 rst.demangleType()
624 }
625 rst.checkChar('E')
626 rst.writeByte(')')
627 if len(rst.str) > 0 && rst.str[0] == 'u' {
628 rst.advance(1)
629 } else {
630 rst.writeString(" -> ")
631 rst.demangleType()
632 }
633 }
634
635
636 func (rst *rustState) dynBounds() {
637 rst.writeString("dyn ")
638 rst.binder()
639 first := true
640 for len(rst.str) > 0 && rst.str[0] != 'E' {
641 if first {
642 first = false
643 } else {
644 rst.writeString(" + ")
645 }
646 rst.dynTrait()
647 }
648 rst.checkChar('E')
649 }
650
651
652
653 func (rst *rustState) dynTrait() {
654 started := rst.pathStartGenerics()
655 for len(rst.str) > 0 && rst.str[0] == 'p' {
656 rst.advance(1)
657 if started {
658 rst.writeString(", ")
659 } else {
660 rst.writeByte('<')
661 started = true
662 }
663 rst.writeString(rst.undisambiguatedIdentifier())
664 rst.writeString(" = ")
665 rst.demangleType()
666 }
667 if started {
668 rst.writeByte('>')
669 }
670 }
671
672
673
674 func (rst *rustState) pathStartGenerics() bool {
675 if len(rst.str) < 1 {
676 rst.fail("expected path")
677 }
678 switch rst.str[0] {
679 case 'I':
680 rst.advance(1)
681 rst.path(false)
682 rst.writeByte('<')
683 first := true
684 for len(rst.str) > 0 && rst.str[0] != 'E' {
685 if first {
686 first = false
687 } else {
688 rst.writeString(", ")
689 }
690 rst.genericArg()
691 }
692 rst.checkChar('E')
693 return true
694 case 'B':
695 var started bool
696 rst.backref(func() { started = rst.pathStartGenerics() })
697 return started
698 default:
699 rst.path(false)
700 return false
701 }
702 }
703
704
705 func (rst *rustState) writeLifetime(lifetime int64) {
706 rst.writeByte('\'')
707 if lifetime == 0 {
708 rst.writeByte('_')
709 return
710 }
711 depth := rst.lifetimes - lifetime
712 if depth < 0 {
713 rst.fail("invalid lifetime")
714 } else if depth < 26 {
715 rst.writeByte('a' + byte(depth))
716 } else {
717 rst.writeByte('z')
718 if !rst.skip {
719 fmt.Fprintf(&rst.buf, "%d", depth-26+1)
720 rst.last = '0'
721 }
722 }
723 }
724
725
726
727
728
729 func (rst *rustState) demangleConst() {
730 if len(rst.str) < 1 {
731 rst.fail("expected constant")
732 }
733
734 if rst.str[0] == 'B' {
735 rst.backref(rst.demangleConst)
736 return
737 }
738
739 if rst.str[0] == 'p' {
740 rst.advance(1)
741 rst.writeByte('_')
742 return
743 }
744
745 typ := rst.str[0]
746
747 const (
748 invalid = iota
749 signedInt
750 unsignedInt
751 boolean
752 character
753 )
754
755 var kind int
756 switch typ {
757 case 'a', 's', 'l', 'x', 'n', 'i':
758 kind = signedInt
759 case 'h', 't', 'm', 'y', 'o', 'j':
760 kind = unsignedInt
761 case 'b':
762 kind = boolean
763 case 'c':
764 kind = character
765 default:
766 rst.fail("unrecognized constant type")
767 }
768
769 rst.advance(1)
770
771 if kind == signedInt && len(rst.str) > 0 && rst.str[0] == 'n' {
772 rst.advance(1)
773 rst.writeByte('-')
774 }
775
776 start := rst.str
777 digits := 0
778 val := uint64(0)
779 digitLoop:
780 for len(rst.str) > 0 {
781 c := rst.str[0]
782 var digit uint64
783 switch {
784 case c >= '0' && c <= '9':
785 digit = uint64(c - '0')
786 case c >= 'a' && c <= 'f':
787 digit = uint64(c - 'a' + 10)
788 case c == '_':
789 rst.advance(1)
790 break digitLoop
791 default:
792 rst.fail("expected hex digit or _")
793 }
794 rst.advance(1)
795 if val == 0 && digit == 0 && (len(rst.str) == 0 || rst.str[0] != '_') {
796 rst.fail("invalid leading 0 in constant")
797 }
798 val *= 16
799 val += digit
800 digits++
801 }
802
803 if digits == 0 {
804 rst.fail("expected constant")
805 }
806
807 switch kind {
808 case signedInt, unsignedInt:
809 if digits > 16 {
810
811 rst.writeString("0x")
812 rst.writeString(start[:digits])
813 } else {
814 if !rst.skip {
815 fmt.Fprintf(&rst.buf, "%d", val)
816 rst.last = '0'
817 }
818 }
819 case boolean:
820 if digits > 1 {
821 rst.fail("boolean value too large")
822 } else if val == 0 {
823 rst.writeString("false")
824 } else if val == 1 {
825 rst.writeString("true")
826 } else {
827 rst.fail("invalid boolean value")
828 }
829 case character:
830 if digits > 6 {
831 rst.fail("character value too large")
832 }
833 rst.writeByte('\'')
834 if val == '\t' {
835 rst.writeString(`\t`)
836 } else if val == '\r' {
837 rst.writeString(`\r`)
838 } else if val == '\n' {
839 rst.writeString(`\n`)
840 } else if val == '\\' {
841 rst.writeString(`\\`)
842 } else if val == '\'' {
843 rst.writeString(`\'`)
844 } else if val >= ' ' && val <= '~' {
845
846 rst.writeByte(byte(val))
847 } else {
848 if !rst.skip {
849 fmt.Fprintf(&rst.buf, `\u{%x}`, val)
850 rst.last = '}'
851 }
852 }
853 rst.writeByte('\'')
854 default:
855 panic("internal error")
856 }
857 }
858
859
860 func (rst *rustState) base62Number() int64 {
861 if len(rst.str) > 0 && rst.str[0] == '_' {
862 rst.advance(1)
863 return 0
864 }
865 val := int64(0)
866 for len(rst.str) > 0 {
867 c := rst.str[0]
868 rst.advance(1)
869 if c == '_' {
870 return val + 1
871 }
872 val *= 62
873 if c >= '0' && c <= '9' {
874 val += int64(c - '0')
875 } else if c >= 'a' && c <= 'z' {
876 val += int64(c - 'a' + 10)
877 } else if c >= 'A' && c <= 'Z' {
878 val += int64(c - 'A' + 36)
879 } else {
880 rst.fail("invalid digit in base 62 number")
881 }
882 }
883 rst.fail("expected _ after base 62 number")
884 return 0
885 }
886
887
888 func (rst *rustState) backref(demangle func()) {
889 backoff := rst.off
890
891 rst.checkChar('B')
892 idx64 := rst.base62Number()
893
894 if rst.skip {
895 return
896 }
897
898 idx := int(idx64)
899 if int64(idx) != idx64 {
900 rst.fail("backref index overflow")
901 }
902 if idx < 0 || idx >= backoff {
903 rst.fail("invalid backref index")
904 }
905
906 holdStr := rst.str
907 holdOff := rst.off
908 rst.str = rst.orig[idx:backoff]
909 rst.off = idx
910 defer func() {
911 rst.str = holdStr
912 rst.off = holdOff
913 }()
914
915 demangle()
916 }
917
918 func (rst *rustState) decimalNumber() int {
919 if len(rst.str) == 0 {
920 rst.fail("expected number")
921 }
922
923 val := 0
924 for len(rst.str) > 0 && isDigit(rst.str[0]) {
925 add := int(rst.str[0] - '0')
926 if val >= math.MaxInt32/10-add {
927 rst.fail("decimal number overflow")
928 }
929 val *= 10
930 val += add
931 rst.advance(1)
932 }
933 return val
934 }
935
936
937
938 func oldRustToString(name string, options []Option) (string, bool) {
939
940 name = name[3:]
941
942 hexDigit := func(c byte) (byte, bool) {
943 switch {
944 case c >= '0' && c <= '9':
945 return c - '0', true
946 case c >= 'a' && c <= 'f':
947 return c - 'a' + 10, true
948 default:
949 return 0, false
950 }
951 }
952
953
954
955
956 seen := uint16(0)
957 for i := len(name) - 17; i < len(name) - 1; i++ {
958 digit, ok := hexDigit(name[i])
959 if !ok {
960 return "", false
961 }
962 seen |= 1 << digit
963 }
964 if bits.OnesCount16(seen) < 5 {
965 return "", false
966 }
967 name = name[:len(name)-20]
968
969
970 var sb strings.Builder
971 for len(name) > 0 {
972 if !isDigit(name[0]) {
973 return "", false
974 }
975
976 val := 0
977 for len(name) > 0 && isDigit(name[0]) {
978 add := int(name[0] - '0')
979 if val >= math.MaxInt32/10-add {
980 return "", false
981 }
982 val *= 10
983 val += add
984 name = name[1:]
985 }
986
987
988
989 if len(name) > 0 && name[0] == '_' {
990 name = name[1:]
991 val--
992 }
993
994 if len(name) < val {
995 return "", false
996 }
997
998 id := name[:val]
999 name = name[val:]
1000
1001 if sb.Len() > 0 {
1002 sb.WriteString("::")
1003 }
1004
1005
1006 if strings.HasPrefix(id, "_$") {
1007 id = id[1:]
1008 }
1009
1010
1011 escape:
1012 for len(id) > 0 {
1013 switch c := id[0]; c {
1014 case '$':
1015 codes := map[string]byte {
1016 "SP": '@',
1017 "BP": '*',
1018 "RF": '&',
1019 "LT": '<',
1020 "GT": '>',
1021 "LP": '(',
1022 "RP": ')',
1023 }
1024
1025 valid := true
1026 if len(id) > 2 && id[1] == 'C' && id[2] == '$' {
1027 sb.WriteByte(',')
1028 id = id[3:]
1029 } else if len(id) > 4 && id[1] == 'u' && id[4] == '$' {
1030 dig1, ok1 := hexDigit(id[2])
1031 dig2, ok2 := hexDigit(id[3])
1032 val := (dig1 << 4) | dig2
1033 if !ok1 || !ok2 || dig1 > 7 || val < ' ' {
1034 valid = false
1035 } else {
1036 sb.WriteByte(val)
1037 id = id[5:]
1038 }
1039 } else if len(id) > 3 && id[3] == '$' {
1040 if code, ok := codes[id[1:3]]; !ok {
1041 valid = false
1042 } else {
1043 sb.WriteByte(code)
1044 id = id[4:]
1045 }
1046 } else {
1047 valid = false
1048 }
1049 if !valid {
1050 sb.WriteString(id)
1051 break escape
1052 }
1053 case '.':
1054 if strings.HasPrefix(id, "..") {
1055 sb.WriteString("::")
1056 id = id[2:]
1057 } else {
1058 sb.WriteByte(c)
1059 id = id[1:]
1060 }
1061 default:
1062 sb.WriteByte(c)
1063 id = id[1:]
1064 }
1065 }
1066 }
1067
1068 return sb.String(), true
1069 }
1070
View as plain text