Projekt

Obecné

Profil

Stáhnout (16.2 KB) Statistiky
| Větev: | Revize:
1
var Reporter = require('../base').Reporter;
2
var EncoderBuffer = require('../base').EncoderBuffer;
3
var DecoderBuffer = require('../base').DecoderBuffer;
4
var assert = require('minimalistic-assert');
5

    
6
// Supported tags
7
var tags = [
8
  'seq', 'seqof', 'set', 'setof', 'objid', 'bool',
9
  'gentime', 'utctime', 'null_', 'enum', 'int', 'objDesc',
10
  'bitstr', 'bmpstr', 'charstr', 'genstr', 'graphstr', 'ia5str', 'iso646str',
11
  'numstr', 'octstr', 'printstr', 't61str', 'unistr', 'utf8str', 'videostr'
12
];
13

    
14
// Public methods list
15
var methods = [
16
  'key', 'obj', 'use', 'optional', 'explicit', 'implicit', 'def', 'choice',
17
  'any', 'contains'
18
].concat(tags);
19

    
20
// Overrided methods list
21
var overrided = [
22
  '_peekTag', '_decodeTag', '_use',
23
  '_decodeStr', '_decodeObjid', '_decodeTime',
24
  '_decodeNull', '_decodeInt', '_decodeBool', '_decodeList',
25

    
26
  '_encodeComposite', '_encodeStr', '_encodeObjid', '_encodeTime',
27
  '_encodeNull', '_encodeInt', '_encodeBool'
28
];
29

    
30
function Node(enc, parent) {
31
  var state = {};
32
  this._baseState = state;
33

    
34
  state.enc = enc;
35

    
36
  state.parent = parent || null;
37
  state.children = null;
38

    
39
  // State
40
  state.tag = null;
41
  state.args = null;
42
  state.reverseArgs = null;
43
  state.choice = null;
44
  state.optional = false;
45
  state.any = false;
46
  state.obj = false;
47
  state.use = null;
48
  state.useDecoder = null;
49
  state.key = null;
50
  state['default'] = null;
51
  state.explicit = null;
52
  state.implicit = null;
53
  state.contains = null;
54

    
55
  // Should create new instance on each method
56
  if (!state.parent) {
57
    state.children = [];
58
    this._wrap();
59
  }
60
}
61
module.exports = Node;
62

    
63
var stateProps = [
64
  'enc', 'parent', 'children', 'tag', 'args', 'reverseArgs', 'choice',
65
  'optional', 'any', 'obj', 'use', 'alteredUse', 'key', 'default', 'explicit',
66
  'implicit', 'contains'
67
];
68

    
69
Node.prototype.clone = function clone() {
70
  var state = this._baseState;
71
  var cstate = {};
72
  stateProps.forEach(function(prop) {
73
    cstate[prop] = state[prop];
74
  });
75
  var res = new this.constructor(cstate.parent);
76
  res._baseState = cstate;
77
  return res;
78
};
79

    
80
Node.prototype._wrap = function wrap() {
81
  var state = this._baseState;
82
  methods.forEach(function(method) {
83
    this[method] = function _wrappedMethod() {
84
      var clone = new this.constructor(this);
85
      state.children.push(clone);
86
      return clone[method].apply(clone, arguments);
87
    };
88
  }, this);
89
};
90

    
91
Node.prototype._init = function init(body) {
92
  var state = this._baseState;
93

    
94
  assert(state.parent === null);
95
  body.call(this);
96

    
97
  // Filter children
98
  state.children = state.children.filter(function(child) {
99
    return child._baseState.parent === this;
100
  }, this);
101
  assert.equal(state.children.length, 1, 'Root node can have only one child');
102
};
103

    
104
Node.prototype._useArgs = function useArgs(args) {
105
  var state = this._baseState;
106

    
107
  // Filter children and args
108
  var children = args.filter(function(arg) {
109
    return arg instanceof this.constructor;
110
  }, this);
111
  args = args.filter(function(arg) {
112
    return !(arg instanceof this.constructor);
113
  }, this);
114

    
115
  if (children.length !== 0) {
116
    assert(state.children === null);
117
    state.children = children;
118

    
119
    // Replace parent to maintain backward link
120
    children.forEach(function(child) {
121
      child._baseState.parent = this;
122
    }, this);
123
  }
124
  if (args.length !== 0) {
125
    assert(state.args === null);
126
    state.args = args;
127
    state.reverseArgs = args.map(function(arg) {
128
      if (typeof arg !== 'object' || arg.constructor !== Object)
129
        return arg;
130

    
131
      var res = {};
132
      Object.keys(arg).forEach(function(key) {
133
        if (key == (key | 0))
134
          key |= 0;
135
        var value = arg[key];
136
        res[value] = key;
137
      });
138
      return res;
139
    });
140
  }
141
};
142

    
143
//
144
// Overrided methods
145
//
146

    
147
overrided.forEach(function(method) {
148
  Node.prototype[method] = function _overrided() {
149
    var state = this._baseState;
150
    throw new Error(method + ' not implemented for encoding: ' + state.enc);
151
  };
152
});
153

    
154
//
155
// Public methods
156
//
157

    
158
tags.forEach(function(tag) {
159
  Node.prototype[tag] = function _tagMethod() {
160
    var state = this._baseState;
161
    var args = Array.prototype.slice.call(arguments);
162

    
163
    assert(state.tag === null);
164
    state.tag = tag;
165

    
166
    this._useArgs(args);
167

    
168
    return this;
169
  };
170
});
171

    
172
Node.prototype.use = function use(item) {
173
  assert(item);
174
  var state = this._baseState;
175

    
176
  assert(state.use === null);
177
  state.use = item;
178

    
179
  return this;
180
};
181

    
182
Node.prototype.optional = function optional() {
183
  var state = this._baseState;
184

    
185
  state.optional = true;
186

    
187
  return this;
188
};
189

    
190
Node.prototype.def = function def(val) {
191
  var state = this._baseState;
192

    
193
  assert(state['default'] === null);
194
  state['default'] = val;
195
  state.optional = true;
196

    
197
  return this;
198
};
199

    
200
Node.prototype.explicit = function explicit(num) {
201
  var state = this._baseState;
202

    
203
  assert(state.explicit === null && state.implicit === null);
204
  state.explicit = num;
205

    
206
  return this;
207
};
208

    
209
Node.prototype.implicit = function implicit(num) {
210
  var state = this._baseState;
211

    
212
  assert(state.explicit === null && state.implicit === null);
213
  state.implicit = num;
214

    
215
  return this;
216
};
217

    
218
Node.prototype.obj = function obj() {
219
  var state = this._baseState;
220
  var args = Array.prototype.slice.call(arguments);
221

    
222
  state.obj = true;
223

    
224
  if (args.length !== 0)
225
    this._useArgs(args);
226

    
227
  return this;
228
};
229

    
230
Node.prototype.key = function key(newKey) {
231
  var state = this._baseState;
232

    
233
  assert(state.key === null);
234
  state.key = newKey;
235

    
236
  return this;
237
};
238

    
239
Node.prototype.any = function any() {
240
  var state = this._baseState;
241

    
242
  state.any = true;
243

    
244
  return this;
245
};
246

    
247
Node.prototype.choice = function choice(obj) {
248
  var state = this._baseState;
249

    
250
  assert(state.choice === null);
251
  state.choice = obj;
252
  this._useArgs(Object.keys(obj).map(function(key) {
253
    return obj[key];
254
  }));
255

    
256
  return this;
257
};
258

    
259
Node.prototype.contains = function contains(item) {
260
  var state = this._baseState;
261

    
262
  assert(state.use === null);
263
  state.contains = item;
264

    
265
  return this;
266
};
267

    
268
//
269
// Decoding
270
//
271

    
272
Node.prototype._decode = function decode(input, options) {
273
  var state = this._baseState;
274

    
275
  // Decode root node
276
  if (state.parent === null)
277
    return input.wrapResult(state.children[0]._decode(input, options));
278

    
279
  var result = state['default'];
280
  var present = true;
281

    
282
  var prevKey = null;
283
  if (state.key !== null)
284
    prevKey = input.enterKey(state.key);
285

    
286
  // Check if tag is there
287
  if (state.optional) {
288
    var tag = null;
289
    if (state.explicit !== null)
290
      tag = state.explicit;
291
    else if (state.implicit !== null)
292
      tag = state.implicit;
293
    else if (state.tag !== null)
294
      tag = state.tag;
295

    
296
    if (tag === null && !state.any) {
297
      // Trial and Error
298
      var save = input.save();
299
      try {
300
        if (state.choice === null)
301
          this._decodeGeneric(state.tag, input, options);
302
        else
303
          this._decodeChoice(input, options);
304
        present = true;
305
      } catch (e) {
306
        present = false;
307
      }
308
      input.restore(save);
309
    } else {
310
      present = this._peekTag(input, tag, state.any);
311

    
312
      if (input.isError(present))
313
        return present;
314
    }
315
  }
316

    
317
  // Push object on stack
318
  var prevObj;
319
  if (state.obj && present)
320
    prevObj = input.enterObject();
321

    
322
  if (present) {
323
    // Unwrap explicit values
324
    if (state.explicit !== null) {
325
      var explicit = this._decodeTag(input, state.explicit);
326
      if (input.isError(explicit))
327
        return explicit;
328
      input = explicit;
329
    }
330

    
331
    var start = input.offset;
332

    
333
    // Unwrap implicit and normal values
334
    if (state.use === null && state.choice === null) {
335
      if (state.any)
336
        var save = input.save();
337
      var body = this._decodeTag(
338
        input,
339
        state.implicit !== null ? state.implicit : state.tag,
340
        state.any
341
      );
342
      if (input.isError(body))
343
        return body;
344

    
345
      if (state.any)
346
        result = input.raw(save);
347
      else
348
        input = body;
349
    }
350

    
351
    if (options && options.track && state.tag !== null)
352
      options.track(input.path(), start, input.length, 'tagged');
353

    
354
    if (options && options.track && state.tag !== null)
355
      options.track(input.path(), input.offset, input.length, 'content');
356

    
357
    // Select proper method for tag
358
    if (state.any)
359
      result = result;
360
    else if (state.choice === null)
361
      result = this._decodeGeneric(state.tag, input, options);
362
    else
363
      result = this._decodeChoice(input, options);
364

    
365
    if (input.isError(result))
366
      return result;
367

    
368
    // Decode children
369
    if (!state.any && state.choice === null && state.children !== null) {
370
      state.children.forEach(function decodeChildren(child) {
371
        // NOTE: We are ignoring errors here, to let parser continue with other
372
        // parts of encoded data
373
        child._decode(input, options);
374
      });
375
    }
376

    
377
    // Decode contained/encoded by schema, only in bit or octet strings
378
    if (state.contains && (state.tag === 'octstr' || state.tag === 'bitstr')) {
379
      var data = new DecoderBuffer(result);
380
      result = this._getUse(state.contains, input._reporterState.obj)
381
          ._decode(data, options);
382
    }
383
  }
384

    
385
  // Pop object
386
  if (state.obj && present)
387
    result = input.leaveObject(prevObj);
388

    
389
  // Set key
390
  if (state.key !== null && (result !== null || present === true))
391
    input.leaveKey(prevKey, state.key, result);
392
  else if (prevKey !== null)
393
    input.exitKey(prevKey);
394

    
395
  return result;
396
};
397

    
398
Node.prototype._decodeGeneric = function decodeGeneric(tag, input, options) {
399
  var state = this._baseState;
400

    
401
  if (tag === 'seq' || tag === 'set')
402
    return null;
403
  if (tag === 'seqof' || tag === 'setof')
404
    return this._decodeList(input, tag, state.args[0], options);
405
  else if (/str$/.test(tag))
406
    return this._decodeStr(input, tag, options);
407
  else if (tag === 'objid' && state.args)
408
    return this._decodeObjid(input, state.args[0], state.args[1], options);
409
  else if (tag === 'objid')
410
    return this._decodeObjid(input, null, null, options);
411
  else if (tag === 'gentime' || tag === 'utctime')
412
    return this._decodeTime(input, tag, options);
413
  else if (tag === 'null_')
414
    return this._decodeNull(input, options);
415
  else if (tag === 'bool')
416
    return this._decodeBool(input, options);
417
  else if (tag === 'objDesc')
418
    return this._decodeStr(input, tag, options);
419
  else if (tag === 'int' || tag === 'enum')
420
    return this._decodeInt(input, state.args && state.args[0], options);
421

    
422
  if (state.use !== null) {
423
    return this._getUse(state.use, input._reporterState.obj)
424
        ._decode(input, options);
425
  } else {
426
    return input.error('unknown tag: ' + tag);
427
  }
428
};
429

    
430
Node.prototype._getUse = function _getUse(entity, obj) {
431

    
432
  var state = this._baseState;
433
  // Create altered use decoder if implicit is set
434
  state.useDecoder = this._use(entity, obj);
435
  assert(state.useDecoder._baseState.parent === null);
436
  state.useDecoder = state.useDecoder._baseState.children[0];
437
  if (state.implicit !== state.useDecoder._baseState.implicit) {
438
    state.useDecoder = state.useDecoder.clone();
439
    state.useDecoder._baseState.implicit = state.implicit;
440
  }
441
  return state.useDecoder;
442
};
443

    
444
Node.prototype._decodeChoice = function decodeChoice(input, options) {
445
  var state = this._baseState;
446
  var result = null;
447
  var match = false;
448

    
449
  Object.keys(state.choice).some(function(key) {
450
    var save = input.save();
451
    var node = state.choice[key];
452
    try {
453
      var value = node._decode(input, options);
454
      if (input.isError(value))
455
        return false;
456

    
457
      result = { type: key, value: value };
458
      match = true;
459
    } catch (e) {
460
      input.restore(save);
461
      return false;
462
    }
463
    return true;
464
  }, this);
465

    
466
  if (!match)
467
    return input.error('Choice not matched');
468

    
469
  return result;
470
};
471

    
472
//
473
// Encoding
474
//
475

    
476
Node.prototype._createEncoderBuffer = function createEncoderBuffer(data) {
477
  return new EncoderBuffer(data, this.reporter);
478
};
479

    
480
Node.prototype._encode = function encode(data, reporter, parent) {
481
  var state = this._baseState;
482
  if (state['default'] !== null && state['default'] === data)
483
    return;
484

    
485
  var result = this._encodeValue(data, reporter, parent);
486
  if (result === undefined)
487
    return;
488

    
489
  if (this._skipDefault(result, reporter, parent))
490
    return;
491

    
492
  return result;
493
};
494

    
495
Node.prototype._encodeValue = function encode(data, reporter, parent) {
496
  var state = this._baseState;
497

    
498
  // Decode root node
499
  if (state.parent === null)
500
    return state.children[0]._encode(data, reporter || new Reporter());
501

    
502
  var result = null;
503

    
504
  // Set reporter to share it with a child class
505
  this.reporter = reporter;
506

    
507
  // Check if data is there
508
  if (state.optional && data === undefined) {
509
    if (state['default'] !== null)
510
      data = state['default']
511
    else
512
      return;
513
  }
514

    
515
  // Encode children first
516
  var content = null;
517
  var primitive = false;
518
  if (state.any) {
519
    // Anything that was given is translated to buffer
520
    result = this._createEncoderBuffer(data);
521
  } else if (state.choice) {
522
    result = this._encodeChoice(data, reporter);
523
  } else if (state.contains) {
524
    content = this._getUse(state.contains, parent)._encode(data, reporter);
525
    primitive = true;
526
  } else if (state.children) {
527
    content = state.children.map(function(child) {
528
      if (child._baseState.tag === 'null_')
529
        return child._encode(null, reporter, data);
530

    
531
      if (child._baseState.key === null)
532
        return reporter.error('Child should have a key');
533
      var prevKey = reporter.enterKey(child._baseState.key);
534

    
535
      if (typeof data !== 'object')
536
        return reporter.error('Child expected, but input is not object');
537

    
538
      var res = child._encode(data[child._baseState.key], reporter, data);
539
      reporter.leaveKey(prevKey);
540

    
541
      return res;
542
    }, this).filter(function(child) {
543
      return child;
544
    });
545
    content = this._createEncoderBuffer(content);
546
  } else {
547
    if (state.tag === 'seqof' || state.tag === 'setof') {
548
      // TODO(indutny): this should be thrown on DSL level
549
      if (!(state.args && state.args.length === 1))
550
        return reporter.error('Too many args for : ' + state.tag);
551

    
552
      if (!Array.isArray(data))
553
        return reporter.error('seqof/setof, but data is not Array');
554

    
555
      var child = this.clone();
556
      child._baseState.implicit = null;
557
      content = this._createEncoderBuffer(data.map(function(item) {
558
        var state = this._baseState;
559

    
560
        return this._getUse(state.args[0], data)._encode(item, reporter);
561
      }, child));
562
    } else if (state.use !== null) {
563
      result = this._getUse(state.use, parent)._encode(data, reporter);
564
    } else {
565
      content = this._encodePrimitive(state.tag, data);
566
      primitive = true;
567
    }
568
  }
569

    
570
  // Encode data itself
571
  var result;
572
  if (!state.any && state.choice === null) {
573
    var tag = state.implicit !== null ? state.implicit : state.tag;
574
    var cls = state.implicit === null ? 'universal' : 'context';
575

    
576
    if (tag === null) {
577
      if (state.use === null)
578
        reporter.error('Tag could be omitted only for .use()');
579
    } else {
580
      if (state.use === null)
581
        result = this._encodeComposite(tag, primitive, cls, content);
582
    }
583
  }
584

    
585
  // Wrap in explicit
586
  if (state.explicit !== null)
587
    result = this._encodeComposite(state.explicit, false, 'context', result);
588

    
589
  return result;
590
};
591

    
592
Node.prototype._encodeChoice = function encodeChoice(data, reporter) {
593
  var state = this._baseState;
594

    
595
  var node = state.choice[data.type];
596
  if (!node) {
597
    assert(
598
        false,
599
        data.type + ' not found in ' +
600
            JSON.stringify(Object.keys(state.choice)));
601
  }
602
  return node._encode(data.value, reporter);
603
};
604

    
605
Node.prototype._encodePrimitive = function encodePrimitive(tag, data) {
606
  var state = this._baseState;
607

    
608
  if (/str$/.test(tag))
609
    return this._encodeStr(data, tag);
610
  else if (tag === 'objid' && state.args)
611
    return this._encodeObjid(data, state.reverseArgs[0], state.args[1]);
612
  else if (tag === 'objid')
613
    return this._encodeObjid(data, null, null);
614
  else if (tag === 'gentime' || tag === 'utctime')
615
    return this._encodeTime(data, tag);
616
  else if (tag === 'null_')
617
    return this._encodeNull();
618
  else if (tag === 'int' || tag === 'enum')
619
    return this._encodeInt(data, state.args && state.reverseArgs[0]);
620
  else if (tag === 'bool')
621
    return this._encodeBool(data);
622
  else if (tag === 'objDesc')
623
    return this._encodeStr(data, tag);
624
  else
625
    throw new Error('Unsupported tag: ' + tag);
626
};
627

    
628
Node.prototype._isNumstr = function isNumstr(str) {
629
  return /^[0-9 ]*$/.test(str);
630
};
631

    
632
Node.prototype._isPrintstr = function isPrintstr(str) {
633
  return /^[A-Za-z0-9 '\(\)\+,\-\.\/:=\?]*$/.test(str);
634
};
(3-3/4)