escape.mx raw
1 package text
2
3 // NostrEscape for JSON encoding according to RFC8259 / NIP-01.
4 func NostrEscape(dst, src []byte) []byte {
5 l := len(src)
6 if dst == nil && l > 0 {
7 dst = []byte{:0:l*3/2}
8 }
9 for i := 0; i < l; i++ {
10 c := src[i]
11 if c == '"' {
12 dst = append(dst, '\\', '"')
13 } else if c == '\\' {
14 if i+1 < l && src[i+1] == 'u' {
15 dst = append(dst, '\\')
16 } else {
17 dst = append(dst, '\\', '\\')
18 }
19 } else if c == '\b' {
20 dst = append(dst, '\\', 'b')
21 } else if c == '\t' {
22 dst = append(dst, '\\', 't')
23 } else if c == '\n' {
24 dst = append(dst, '\\', 'n')
25 } else if c == '\f' {
26 dst = append(dst, '\\', 'f')
27 } else if c == '\r' {
28 dst = append(dst, '\\', 'r')
29 } else if c < 32 {
30 dst = append(dst, '\\', 'u', '0', '0')
31 hexHigh := (c >> 4) & 0x0F
32 hexLow := c & 0x0F
33 if hexHigh < 10 {
34 dst = append(dst, byte('0'+hexHigh))
35 } else {
36 dst = append(dst, byte('a'+(hexHigh-10)))
37 }
38 if hexLow < 10 {
39 dst = append(dst, byte('0'+hexLow))
40 } else {
41 dst = append(dst, byte('a'+(hexLow-10)))
42 }
43 } else {
44 dst = append(dst, c)
45 }
46 }
47 return dst
48 }
49
50 // NostrUnescape reverses NostrEscape in-place.
51 func NostrUnescape(dst []byte) []byte {
52 var r, w int
53 for ; r < len(dst); r++ {
54 if dst[r] == '\\' {
55 r++
56 c := dst[r]
57 switch {
58 case c == '"':
59 dst[w] = '"'
60 w++
61 case c == '\\':
62 dst[w] = '\\'
63 w++
64 case c == 'b':
65 dst[w] = '\b'
66 w++
67 case c == 't':
68 dst[w] = '\t'
69 w++
70 case c == 'n':
71 dst[w] = '\n'
72 w++
73 case c == 'f':
74 dst[w] = '\f'
75 w++
76 case c == 'r':
77 dst[w] = '\r'
78 w++
79 case c == 'u':
80 if r+4 < len(dst) && dst[r+1] == '0' && dst[r+2] == '0' {
81 hexHigh := dst[r+3]
82 hexLow := dst[r+4]
83 var val byte
84 if hexHigh >= '0' && hexHigh <= '9' {
85 val = (hexHigh - '0') << 4
86 } else if hexHigh >= 'a' && hexHigh <= 'f' {
87 val = (hexHigh - 'a' + 10) << 4
88 } else if hexHigh >= 'A' && hexHigh <= 'F' {
89 val = (hexHigh - 'A' + 10) << 4
90 }
91 if hexLow >= '0' && hexLow <= '9' {
92 val |= hexLow - '0'
93 } else if hexLow >= 'a' && hexLow <= 'f' {
94 val |= hexLow - 'a' + 10
95 } else if hexLow >= 'A' && hexLow <= 'F' {
96 val |= hexLow - 'A' + 10
97 }
98 if val < 32 {
99 dst[w] = val
100 w++
101 r += 4
102 continue
103 }
104 }
105 dst[w] = '\\'
106 w++
107 dst[w] = 'u'
108 w++
109 case c == '/':
110 dst[w] = '\\'
111 w++
112 dst[w] = '/'
113 w++
114 case c >= '0' && c <= '9':
115 dst[w] = '\\'
116 w++
117 dst[w] = c
118 w++
119 default:
120 dst[w] = dst[r]
121 w++
122 dst[w] = c
123 w++
124 }
125 } else {
126 dst[w] = dst[r]
127 w++
128 }
129 }
130 return dst[:w]
131 }
132