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