Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80647 views
1
var Reporter = require('../base').Reporter;
2
var EncoderBuffer = require('../base').EncoderBuffer;
3
var assert = require('minimalistic-assert');
4
5
// Supported tags
6
var tags = [
7
'seq', 'seqof', 'set', 'setof', 'octstr', 'bitstr', 'objid', 'bool',
8
'gentime', 'utctime', 'null_', 'enum', 'int', 'ia5str'
9
];
10
11
// Public methods list
12
var methods = [
13
'key', 'obj', 'use', 'optional', 'explicit', 'implicit', 'def', 'choice',
14
'any'
15
].concat(tags);
16
17
// Overrided methods list
18
var overrided = [
19
'_peekTag', '_decodeTag', '_use',
20
'_decodeStr', '_decodeObjid', '_decodeTime',
21
'_decodeNull', '_decodeInt', '_decodeBool', '_decodeList',
22
23
'_encodeComposite', '_encodeStr', '_encodeObjid', '_encodeTime',
24
'_encodeNull', '_encodeInt', '_encodeBool'
25
];
26
27
function Node(enc, parent) {
28
var state = {};
29
this._baseState = state;
30
31
state.enc = enc;
32
33
state.parent = parent || null;
34
state.children = null;
35
36
// State
37
state.tag = null;
38
state.args = null;
39
state.reverseArgs = null;
40
state.choice = null;
41
state.optional = false;
42
state.any = false;
43
state.obj = false;
44
state.use = null;
45
state.useDecoder = null;
46
state.key = null;
47
state['default'] = null;
48
state.explicit = null;
49
state.implicit = null;
50
51
// Should create new instance on each method
52
if (!state.parent) {
53
state.children = [];
54
this._wrap();
55
}
56
}
57
module.exports = Node;
58
59
var stateProps = [
60
'enc', 'parent', 'children', 'tag', 'args', 'reverseArgs', 'choice',
61
'optional', 'any', 'obj', 'use', 'alteredUse', 'key', 'default', 'explicit',
62
'implicit'
63
];
64
65
Node.prototype.clone = function clone() {
66
var state = this._baseState;
67
var cstate = {};
68
stateProps.forEach(function(prop) {
69
cstate[prop] = state[prop];
70
});
71
var res = new this.constructor(cstate.parent);
72
res._baseState = cstate;
73
return res;
74
};
75
76
Node.prototype._wrap = function wrap() {
77
var state = this._baseState;
78
methods.forEach(function(method) {
79
this[method] = function _wrappedMethod() {
80
var clone = new this.constructor(this);
81
state.children.push(clone);
82
return clone[method].apply(clone, arguments);
83
};
84
}, this);
85
};
86
87
Node.prototype._init = function init(body) {
88
var state = this._baseState;
89
90
assert(state.parent === null);
91
body.call(this);
92
93
// Filter children
94
state.children = state.children.filter(function(child) {
95
return child._baseState.parent === this;
96
}, this);
97
assert.equal(state.children.length, 1, 'Root node can have only one child');
98
};
99
100
Node.prototype._useArgs = function useArgs(args) {
101
var state = this._baseState;
102
103
// Filter children and args
104
var children = args.filter(function(arg) {
105
return arg instanceof this.constructor;
106
}, this);
107
args = args.filter(function(arg) {
108
return !(arg instanceof this.constructor);
109
}, this);
110
111
if (children.length !== 0) {
112
assert(state.children === null);
113
state.children = children;
114
115
// Replace parent to maintain backward link
116
children.forEach(function(child) {
117
child._baseState.parent = this;
118
}, this);
119
}
120
if (args.length !== 0) {
121
assert(state.args === null);
122
state.args = args;
123
state.reverseArgs = args.map(function(arg) {
124
if (typeof arg !== 'object' || arg.constructor !== Object)
125
return arg;
126
127
var res = {};
128
Object.keys(arg).forEach(function(key) {
129
if (key == (key | 0))
130
key |= 0;
131
var value = arg[key];
132
res[value] = key;
133
});
134
return res;
135
});
136
}
137
};
138
139
//
140
// Overrided methods
141
//
142
143
overrided.forEach(function(method) {
144
Node.prototype[method] = function _overrided() {
145
var state = this._baseState;
146
throw new Error(method + ' not implemented for encoding: ' + state.enc);
147
};
148
});
149
150
//
151
// Public methods
152
//
153
154
tags.forEach(function(tag) {
155
Node.prototype[tag] = function _tagMethod() {
156
var state = this._baseState;
157
var args = Array.prototype.slice.call(arguments);
158
159
assert(state.tag === null);
160
state.tag = tag;
161
162
this._useArgs(args);
163
164
return this;
165
};
166
});
167
168
Node.prototype.use = function use(item) {
169
var state = this._baseState;
170
171
assert(state.use === null);
172
state.use = item;
173
174
return this;
175
};
176
177
Node.prototype.optional = function optional() {
178
var state = this._baseState;
179
180
state.optional = true;
181
182
return this;
183
};
184
185
Node.prototype.def = function def(val) {
186
var state = this._baseState;
187
188
assert(state['default'] === null);
189
state['default'] = val;
190
state.optional = true;
191
192
return this;
193
};
194
195
Node.prototype.explicit = function explicit(num) {
196
var state = this._baseState;
197
198
assert(state.explicit === null && state.implicit === null);
199
state.explicit = num;
200
201
return this;
202
};
203
204
Node.prototype.implicit = function implicit(num) {
205
var state = this._baseState;
206
207
assert(state.explicit === null && state.implicit === null);
208
state.implicit = num;
209
210
return this;
211
};
212
213
Node.prototype.obj = function obj() {
214
var state = this._baseState;
215
var args = Array.prototype.slice.call(arguments);
216
217
state.obj = true;
218
219
if (args.length !== 0)
220
this._useArgs(args);
221
222
return this;
223
};
224
225
Node.prototype.key = function key(newKey) {
226
var state = this._baseState;
227
228
assert(state.key === null);
229
state.key = newKey;
230
231
return this;
232
};
233
234
Node.prototype.any = function any() {
235
var state = this._baseState;
236
237
state.any = true;
238
239
return this;
240
};
241
242
Node.prototype.choice = function choice(obj) {
243
var state = this._baseState;
244
245
assert(state.choice === null);
246
state.choice = obj;
247
this._useArgs(Object.keys(obj).map(function(key) {
248
return obj[key];
249
}));
250
251
return this;
252
};
253
254
//
255
// Decoding
256
//
257
258
Node.prototype._decode = function decode(input) {
259
var state = this._baseState;
260
261
// Decode root node
262
if (state.parent === null)
263
return input.wrapResult(state.children[0]._decode(input));
264
265
var result = state['default'];
266
var present = true;
267
268
var prevKey;
269
if (state.key !== null)
270
prevKey = input.enterKey(state.key);
271
272
// Check if tag is there
273
if (state.optional) {
274
present = this._peekTag(
275
input,
276
state.explicit !== null ? state.explicit :
277
state.implicit !== null ? state.implicit :
278
state.tag || 0,
279
state.any
280
);
281
if (input.isError(present))
282
return present;
283
}
284
285
// Push object on stack
286
var prevObj;
287
if (state.obj && present)
288
prevObj = input.enterObject();
289
290
if (present) {
291
// Unwrap explicit values
292
if (state.explicit !== null) {
293
var explicit = this._decodeTag(input, state.explicit);
294
if (input.isError(explicit))
295
return explicit;
296
input = explicit;
297
}
298
299
// Unwrap implicit and normal values
300
if (state.use === null && state.choice === null) {
301
if (state.any)
302
var save = input.save();
303
var body = this._decodeTag(
304
input,
305
state.implicit !== null ? state.implicit : state.tag,
306
state.any
307
);
308
if (input.isError(body))
309
return body;
310
311
if (state.any)
312
result = input.raw(save);
313
else
314
input = body;
315
}
316
317
// Select proper method for tag
318
if (state.any)
319
result = result;
320
else if (state.choice === null)
321
result = this._decodeGeneric(state.tag, input);
322
else
323
result = this._decodeChoice(input);
324
325
if (input.isError(result))
326
return result;
327
328
// Decode children
329
if (!state.any && state.choice === null && state.children !== null) {
330
var fail = state.children.some(function decodeChildren(child) {
331
// NOTE: We are ignoring errors here, to let parser continue with other
332
// parts of encoded data
333
child._decode(input);
334
});
335
if (fail)
336
return err;
337
}
338
}
339
340
// Pop object
341
if (state.obj && present)
342
result = input.leaveObject(prevObj);
343
344
// Set key
345
if (state.key !== null && (result !== null || present === true))
346
input.leaveKey(prevKey, state.key, result);
347
348
return result;
349
};
350
351
Node.prototype._decodeGeneric = function decodeGeneric(tag, input) {
352
var state = this._baseState;
353
354
if (tag === 'seq' || tag === 'set')
355
return null;
356
if (tag === 'seqof' || tag === 'setof')
357
return this._decodeList(input, tag, state.args[0]);
358
else if (tag === 'octstr' || tag === 'bitstr' || tag === 'ia5str')
359
return this._decodeStr(input, tag);
360
else if (tag === 'objid' && state.args)
361
return this._decodeObjid(input, state.args[0], state.args[1]);
362
else if (tag === 'objid')
363
return this._decodeObjid(input, null, null);
364
else if (tag === 'gentime' || tag === 'utctime')
365
return this._decodeTime(input, tag);
366
else if (tag === 'null_')
367
return this._decodeNull(input);
368
else if (tag === 'bool')
369
return this._decodeBool(input);
370
else if (tag === 'int' || tag === 'enum')
371
return this._decodeInt(input, state.args && state.args[0]);
372
else if (state.use !== null)
373
return this._getUse(state.use, input._reporterState.obj)._decode(input);
374
else
375
return input.error('unknown tag: ' + tag);
376
377
return null;
378
};
379
380
Node.prototype._getUse = function _getUse(entity, obj) {
381
382
var state = this._baseState;
383
// Create altered use decoder if implicit is set
384
state.useDecoder = this._use(entity, obj);
385
assert(state.useDecoder._baseState.parent === null);
386
state.useDecoder = state.useDecoder._baseState.children[0];
387
if (state.implicit !== state.useDecoder._baseState.implicit) {
388
state.useDecoder = state.useDecoder.clone();
389
state.useDecoder._baseState.implicit = state.implicit;
390
}
391
return state.useDecoder;
392
};
393
394
Node.prototype._decodeChoice = function decodeChoice(input) {
395
var state = this._baseState;
396
var result = null;
397
var match = false;
398
399
Object.keys(state.choice).some(function(key) {
400
var save = input.save();
401
var node = state.choice[key];
402
try {
403
var value = node._decode(input);
404
if (input.isError(value))
405
return false;
406
407
result = { type: key, value: value };
408
match = true;
409
} catch (e) {
410
input.restore(save);
411
return false;
412
}
413
return true;
414
}, this);
415
416
if (!match)
417
return input.error('Choice not matched');
418
419
return result;
420
};
421
422
//
423
// Encoding
424
//
425
426
Node.prototype._createEncoderBuffer = function createEncoderBuffer(data) {
427
return new EncoderBuffer(data, this.reporter);
428
};
429
430
Node.prototype._encode = function encode(data, reporter, parent) {
431
var state = this._baseState;
432
if (state['default'] !== null && state['default'] === data)
433
return;
434
435
var result = this._encodeValue(data, reporter, parent);
436
if (result === undefined)
437
return;
438
439
if (this._skipDefault(result, reporter, parent))
440
return;
441
442
return result;
443
};
444
445
Node.prototype._encodeValue = function encode(data, reporter, parent) {
446
var state = this._baseState;
447
448
// Decode root node
449
if (state.parent === null)
450
return state.children[0]._encode(data, reporter || new Reporter());
451
452
var result = null;
453
var present = true;
454
455
// Set reporter to share it with a child class
456
this.reporter = reporter;
457
458
// Check if data is there
459
if (state.optional && data === undefined) {
460
if (state['default'] !== null)
461
data = state['default']
462
else
463
return;
464
}
465
466
// For error reporting
467
var prevKey;
468
469
// Encode children first
470
var content = null;
471
var primitive = false;
472
if (state.any) {
473
// Anything that was given is translated to buffer
474
result = this._createEncoderBuffer(data);
475
} else if (state.choice) {
476
result = this._encodeChoice(data, reporter);
477
} else if (state.children) {
478
content = state.children.map(function(child) {
479
if (child._baseState.tag === 'null_')
480
return child._encode(null, reporter, data);
481
482
if (child._baseState.key === null)
483
return reporter.error('Child should have a key');
484
var prevKey = reporter.enterKey(child._baseState.key);
485
486
if (typeof data !== 'object')
487
return reporter.error('Child expected, but input is not object');
488
489
var res = child._encode(data[child._baseState.key], reporter, data);
490
reporter.leaveKey(prevKey);
491
492
return res;
493
}, this).filter(function(child) {
494
return child;
495
});
496
497
content = this._createEncoderBuffer(content);
498
} else {
499
if (state.tag === 'seqof' || state.tag === 'setof') {
500
// TODO(indutny): this should be thrown on DSL level
501
if (!(state.args && state.args.length === 1))
502
return reporter.error('Too many args for : ' + state.tag);
503
504
if (!Array.isArray(data))
505
return reporter.error('seqof/setof, but data is not Array');
506
507
var child = this.clone();
508
child._baseState.implicit = null;
509
content = this._createEncoderBuffer(data.map(function(item) {
510
var state = this._baseState;
511
512
return this._getUse(state.args[0], data)._encode(item, reporter);
513
}, child));
514
} else if (state.use !== null) {
515
result = this._getUse(state.use, parent)._encode(data, reporter);
516
} else {
517
content = this._encodePrimitive(state.tag, data);
518
primitive = true;
519
}
520
}
521
522
// Encode data itself
523
var result;
524
if (!state.any && state.choice === null) {
525
var tag = state.implicit !== null ? state.implicit : state.tag;
526
var cls = state.implicit === null ? 'universal' : 'context';
527
528
if (tag === null) {
529
if (state.use === null)
530
reporter.error('Tag could be ommited only for .use()');
531
} else {
532
if (state.use === null)
533
result = this._encodeComposite(tag, primitive, cls, content);
534
}
535
}
536
537
// Wrap in explicit
538
if (state.explicit !== null)
539
result = this._encodeComposite(state.explicit, false, 'context', result);
540
541
return result;
542
};
543
544
Node.prototype._encodeChoice = function encodeChoice(data, reporter) {
545
var state = this._baseState;
546
547
var node = state.choice[data.type];
548
if (!node) {
549
assert(
550
false,
551
data.type + ' not found in ' +
552
JSON.stringify(Object.keys(state.choice)));
553
}
554
return node._encode(data.value, reporter);
555
};
556
557
Node.prototype._encodePrimitive = function encodePrimitive(tag, data) {
558
var state = this._baseState;
559
560
if (tag === 'octstr' || tag === 'bitstr' || tag === 'ia5str')
561
return this._encodeStr(data, tag);
562
else if (tag === 'objid' && state.args)
563
return this._encodeObjid(data, state.reverseArgs[0], state.args[1]);
564
else if (tag === 'objid')
565
return this._encodeObjid(data, null, null);
566
else if (tag === 'gentime' || tag === 'utctime')
567
return this._encodeTime(data, tag);
568
else if (tag === 'null_')
569
return this._encodeNull();
570
else if (tag === 'int' || tag === 'enum')
571
return this._encodeInt(data, state.args && state.reverseArgs[0]);
572
else if (tag === 'bool')
573
return this._encodeBool(data);
574
else
575
throw new Error('Unsupported tag: ' + tag);
576
};
577
578