UNPKG

5.76 kBJavaScriptView Raw
1'use strict'
2
3const convert = require('./convert')
4const protocols = require('./protocols-table')
5const varint = require('varint')
6const { concat: uint8ArrayConcat } = require('uint8arrays/concat')
7const { toString: uint8ArrayToString } = require('uint8arrays/to-string')
8
9// export codec
10module.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// string -> [[str name, str addr]... ]
36/**
37 * @param {string} str
38 */
39function stringToStringTuples (str) {
40 const tuples = []
41 const parts = str.split('/').slice(1) // skip first empty elem
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++ // advance addr part
56 if (p >= parts.length) {
57 throw ParseError('invalid address: ' + str)
58 }
59
60 // if it's a path proto, take the rest
61 if (proto.path) {
62 tuples.push([
63 part,
64 // TODO: should we need to check each path part to see if it's a proto?
65 // This would allow for other protocols to be added after a unix path,
66 // however it would have issues if the path had a protocol name in the path
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// [[str name, str addr]... ] -> string
79/**
80 * @param {[number, string?][]} tuples
81 */
82function stringTuplesToString (tuples) {
83 /** @type {Array<string | undefined>} */
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// [[str name, str addr]... ] -> [[int code, Uint8Array]... ]
98/**
99 * @param {Array<string[] | string >} tuples
100 * @returns {[number , Uint8Array?][]}
101 */
102function 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 * Convert tuples to string tuples
117 *
118 * [[int code, Uint8Array]... ] -> [[int code, str addr]... ]
119 *
120 * @param {Array<[number, Uint8Array?]>} tuples
121 * @returns {Array<[number, string?]>}
122 */
123
124function 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// [[int code, Uint8Array ]... ] -> Uint8Array
135/**
136 * @param {[number, Uint8Array?][]} tuples
137 */
138function 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]]) // add address buffer
145 }
146
147 return buf
148 })))
149}
150
151/**
152 * @param {import("./types").Protocol} p
153 * @param {Uint8Array | number[]} addr
154 */
155function 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 * @param {Uint8Array} buf
169 * @returns {Array<[number, Uint8Array?]>}
170 */
171function bytesToTuples (buf) {
172 /** @type {Array<[number, Uint8Array?]>} */
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) { // did not end _exactly_ at buffer.length
194 throw ParseError('Invalid address Uint8Array: ' + uint8ArrayToString(buf, 'base16'))
195 }
196
197 // ok, tuple seems good.
198 tuples.push([code, addr])
199 }
200
201 return tuples
202}
203
204// Uint8Array -> String
205/**
206 * @param {Uint8Array} buf
207 */
208function bytesToString (buf) {
209 const a = bytesToTuples(buf)
210 const b = tuplesToStringTuples(a)
211 return stringTuplesToString(b)
212}
213
214// String -> Uint8Array
215/**
216 * @param {string} str
217 */
218function stringToBytes (str) {
219 str = cleanPath(str)
220 const a = stringToStringTuples(str)
221 const b = stringTuplesToTuples(a)
222
223 return tuplesToBytes(b)
224}
225
226// String -> Uint8Array
227/**
228 * @param {string} str
229 */
230function fromString (str) {
231 return stringToBytes(str)
232}
233
234// Uint8Array -> Uint8Array
235/**
236 * @param {Uint8Array} buf
237 */
238function fromBytes (buf) {
239 const err = validateBytes(buf)
240 if (err) throw err
241 return Uint8Array.from(buf) // copy
242}
243
244/**
245 * @param {Uint8Array} buf
246 */
247function validateBytes (buf) {
248 try {
249 bytesToTuples(buf) // try to parse. will throw if breaks
250 } catch (err) {
251 return err
252 }
253}
254
255/**
256 * @param {Uint8Array} buf
257 */
258function isValidBytes (buf) {
259 return validateBytes(buf) === undefined
260}
261
262/**
263 * @param {string} str
264 */
265function cleanPath (str) {
266 return '/' + str.trim().split('/').filter((/** @type {any} */ a) => a).join('/')
267}
268
269/**
270 * @param {string} str
271 */
272function ParseError (str) {
273 return new Error('Error parsing address: ' + str)
274}
275
276/**
277 * @param {any[]} tup
278 */
279function protoFromTuple (tup) {
280 const proto = protocols(tup[0])
281 return proto
282}