1 | 'use strict'
|
2 |
|
3 | const convert = require('./convert')
|
4 | const protocols = require('./protocols-table')
|
5 | const varint = require('varint')
|
6 | const { concat: uint8ArrayConcat } = require('uint8arrays/concat')
|
7 | const { toString: uint8ArrayToString } = require('uint8arrays/to-string')
|
8 |
|
9 |
|
10 | module.exports = {
|
11 | stringToStringTuples,
|
12 | stringTuplesToString,
|
13 |
|
14 | tuplesToStringTuples,
|
15 | stringTuplesToTuples,
|
16 |
|
17 | bytesToTuples,
|
18 | tuplesToBytes,
|
19 |
|
20 | bytesToString,
|
21 | stringToBytes,
|
22 |
|
23 | fromString,
|
24 | fromBytes,
|
25 | validateBytes,
|
26 | isValidBytes,
|
27 | cleanPath,
|
28 |
|
29 | ParseError,
|
30 | protoFromTuple,
|
31 |
|
32 | sizeForAddr
|
33 | }
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 | function stringToStringTuples (str) {
|
40 | const tuples = []
|
41 | const parts = str.split('/').slice(1)
|
42 | if (parts.length === 1 && parts[0] === '') {
|
43 | return []
|
44 | }
|
45 |
|
46 | for (let p = 0; p < parts.length; p++) {
|
47 | const part = parts[p]
|
48 | const proto = protocols(part)
|
49 |
|
50 | if (proto.size === 0) {
|
51 | tuples.push([part])
|
52 | continue
|
53 | }
|
54 |
|
55 | p++
|
56 | if (p >= parts.length) {
|
57 | throw ParseError('invalid address: ' + str)
|
58 | }
|
59 |
|
60 |
|
61 | if (proto.path) {
|
62 | tuples.push([
|
63 | part,
|
64 |
|
65 |
|
66 |
|
67 | cleanPath(parts.slice(p).join('/'))
|
68 | ])
|
69 | break
|
70 | }
|
71 |
|
72 | tuples.push([part, parts[p]])
|
73 | }
|
74 |
|
75 | return tuples
|
76 | }
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 | function stringTuplesToString (tuples) {
|
83 |
|
84 | const parts = []
|
85 | tuples.map((tup) => {
|
86 | const proto = protoFromTuple(tup)
|
87 | parts.push(proto.name)
|
88 | if (tup.length > 1) {
|
89 | parts.push(tup[1])
|
90 | }
|
91 | return null
|
92 | })
|
93 |
|
94 | return cleanPath(parts.join('/'))
|
95 | }
|
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 | function stringTuplesToTuples (tuples) {
|
103 | return tuples.map((tup) => {
|
104 | if (!Array.isArray(tup)) {
|
105 | tup = [tup]
|
106 | }
|
107 | const proto = protoFromTuple(tup)
|
108 | if (tup.length > 1) {
|
109 | return [proto.code, convert.toBytes(proto.code, tup[1])]
|
110 | }
|
111 | return [proto.code]
|
112 | })
|
113 | }
|
114 |
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 |
|
122 |
|
123 |
|
124 | function tuplesToStringTuples (tuples) {
|
125 | return tuples.map(tup => {
|
126 | const proto = protoFromTuple(tup)
|
127 | if (tup[1]) {
|
128 | return [proto.code, convert.toString(proto.code, tup[1])]
|
129 | }
|
130 | return [proto.code]
|
131 | })
|
132 | }
|
133 |
|
134 |
|
135 |
|
136 |
|
137 |
|
138 | function tuplesToBytes (tuples) {
|
139 | return fromBytes(uint8ArrayConcat(tuples.map((/** @type {any[]} */ tup) => {
|
140 | const proto = protoFromTuple(tup)
|
141 | let buf = Uint8Array.from(varint.encode(proto.code))
|
142 |
|
143 | if (tup.length > 1) {
|
144 | buf = uint8ArrayConcat([buf, tup[1]])
|
145 | }
|
146 |
|
147 | return buf
|
148 | })))
|
149 | }
|
150 |
|
151 |
|
152 |
|
153 |
|
154 |
|
155 | function sizeForAddr (p, addr) {
|
156 | if (p.size > 0) {
|
157 | return p.size / 8
|
158 | } else if (p.size === 0) {
|
159 | return 0
|
160 | } else {
|
161 | const size = varint.decode(addr)
|
162 | return size + varint.decode.bytes
|
163 | }
|
164 | }
|
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 |
|
171 | function bytesToTuples (buf) {
|
172 |
|
173 | const tuples = []
|
174 | let i = 0
|
175 | while (i < buf.length) {
|
176 | const code = varint.decode(buf, i)
|
177 | const n = varint.decode.bytes
|
178 |
|
179 | const p = protocols(code)
|
180 |
|
181 | const size = sizeForAddr(p, buf.slice(i + n))
|
182 |
|
183 | if (size === 0) {
|
184 | tuples.push([code])
|
185 | i += n
|
186 | continue
|
187 | }
|
188 |
|
189 | const addr = buf.slice(i + n, i + n + size)
|
190 |
|
191 | i += (size + n)
|
192 |
|
193 | if (i > buf.length) {
|
194 | throw ParseError('Invalid address Uint8Array: ' + uint8ArrayToString(buf, 'base16'))
|
195 | }
|
196 |
|
197 |
|
198 | tuples.push([code, addr])
|
199 | }
|
200 |
|
201 | return tuples
|
202 | }
|
203 |
|
204 |
|
205 |
|
206 |
|
207 |
|
208 | function bytesToString (buf) {
|
209 | const a = bytesToTuples(buf)
|
210 | const b = tuplesToStringTuples(a)
|
211 | return stringTuplesToString(b)
|
212 | }
|
213 |
|
214 |
|
215 |
|
216 |
|
217 |
|
218 | function stringToBytes (str) {
|
219 | str = cleanPath(str)
|
220 | const a = stringToStringTuples(str)
|
221 | const b = stringTuplesToTuples(a)
|
222 |
|
223 | return tuplesToBytes(b)
|
224 | }
|
225 |
|
226 |
|
227 |
|
228 |
|
229 |
|
230 | function fromString (str) {
|
231 | return stringToBytes(str)
|
232 | }
|
233 |
|
234 |
|
235 |
|
236 |
|
237 |
|
238 | function fromBytes (buf) {
|
239 | const err = validateBytes(buf)
|
240 | if (err) throw err
|
241 | return Uint8Array.from(buf)
|
242 | }
|
243 |
|
244 |
|
245 |
|
246 |
|
247 | function validateBytes (buf) {
|
248 | try {
|
249 | bytesToTuples(buf)
|
250 | } catch (err) {
|
251 | return err
|
252 | }
|
253 | }
|
254 |
|
255 |
|
256 |
|
257 |
|
258 | function isValidBytes (buf) {
|
259 | return validateBytes(buf) === undefined
|
260 | }
|
261 |
|
262 |
|
263 |
|
264 |
|
265 | function cleanPath (str) {
|
266 | return '/' + str.trim().split('/').filter((/** @type {any} */ a) => a).join('/')
|
267 | }
|
268 |
|
269 |
|
270 |
|
271 |
|
272 | function ParseError (str) {
|
273 | return new Error('Error parsing address: ' + str)
|
274 | }
|
275 |
|
276 |
|
277 |
|
278 |
|
279 | function protoFromTuple (tup) {
|
280 | const proto = protocols(tup[0])
|
281 | return proto
|
282 | }
|