You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					639 lines
				
				16 KiB
			
		
		
			
		
	
	
					639 lines
				
				16 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								'use strict';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const Reporter = require('../base/reporter').Reporter;
							 | 
						||
| 
								 | 
							
								const EncoderBuffer = require('../base/buffer').EncoderBuffer;
							 | 
						||
| 
								 | 
							
								const DecoderBuffer = require('../base/buffer').DecoderBuffer;
							 | 
						||
| 
								 | 
							
								const assert = require('minimalistic-assert');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Supported tags
							 | 
						||
| 
								 | 
							
								const tags = [
							 | 
						||
| 
								 | 
							
								  'seq', 'seqof', 'set', 'setof', 'objid', 'bool',
							 | 
						||
| 
								 | 
							
								  'gentime', 'utctime', 'null_', 'enum', 'int', 'objDesc',
							 | 
						||
| 
								 | 
							
								  'bitstr', 'bmpstr', 'charstr', 'genstr', 'graphstr', 'ia5str', 'iso646str',
							 | 
						||
| 
								 | 
							
								  'numstr', 'octstr', 'printstr', 't61str', 'unistr', 'utf8str', 'videostr'
							 | 
						||
| 
								 | 
							
								];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Public methods list
							 | 
						||
| 
								 | 
							
								const methods = [
							 | 
						||
| 
								 | 
							
								  'key', 'obj', 'use', 'optional', 'explicit', 'implicit', 'def', 'choice',
							 | 
						||
| 
								 | 
							
								  'any', 'contains'
							 | 
						||
| 
								 | 
							
								].concat(tags);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Overrided methods list
							 | 
						||
| 
								 | 
							
								const overrided = [
							 | 
						||
| 
								 | 
							
								  '_peekTag', '_decodeTag', '_use',
							 | 
						||
| 
								 | 
							
								  '_decodeStr', '_decodeObjid', '_decodeTime',
							 | 
						||
| 
								 | 
							
								  '_decodeNull', '_decodeInt', '_decodeBool', '_decodeList',
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  '_encodeComposite', '_encodeStr', '_encodeObjid', '_encodeTime',
							 | 
						||
| 
								 | 
							
								  '_encodeNull', '_encodeInt', '_encodeBool'
							 | 
						||
| 
								 | 
							
								];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function Node(enc, parent, name) {
							 | 
						||
| 
								 | 
							
								  const state = {};
							 | 
						||
| 
								 | 
							
								  this._baseState = state;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  state.name = name;
							 | 
						||
| 
								 | 
							
								  state.enc = enc;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  state.parent = parent || null;
							 | 
						||
| 
								 | 
							
								  state.children = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // State
							 | 
						||
| 
								 | 
							
								  state.tag = null;
							 | 
						||
| 
								 | 
							
								  state.args = null;
							 | 
						||
| 
								 | 
							
								  state.reverseArgs = null;
							 | 
						||
| 
								 | 
							
								  state.choice = null;
							 | 
						||
| 
								 | 
							
								  state.optional = false;
							 | 
						||
| 
								 | 
							
								  state.any = false;
							 | 
						||
| 
								 | 
							
								  state.obj = false;
							 | 
						||
| 
								 | 
							
								  state.use = null;
							 | 
						||
| 
								 | 
							
								  state.useDecoder = null;
							 | 
						||
| 
								 | 
							
								  state.key = null;
							 | 
						||
| 
								 | 
							
								  state['default'] = null;
							 | 
						||
| 
								 | 
							
								  state.explicit = null;
							 | 
						||
| 
								 | 
							
								  state.implicit = null;
							 | 
						||
| 
								 | 
							
								  state.contains = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Should create new instance on each method
							 | 
						||
| 
								 | 
							
								  if (!state.parent) {
							 | 
						||
| 
								 | 
							
								    state.children = [];
							 | 
						||
| 
								 | 
							
								    this._wrap();
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								module.exports = Node;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const stateProps = [
							 | 
						||
| 
								 | 
							
								  'enc', 'parent', 'children', 'tag', 'args', 'reverseArgs', 'choice',
							 | 
						||
| 
								 | 
							
								  'optional', 'any', 'obj', 'use', 'alteredUse', 'key', 'default', 'explicit',
							 | 
						||
| 
								 | 
							
								  'implicit', 'contains'
							 | 
						||
| 
								 | 
							
								];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Node.prototype.clone = function clone() {
							 | 
						||
| 
								 | 
							
								  const state = this._baseState;
							 | 
						||
| 
								 | 
							
								  const cstate = {};
							 | 
						||
| 
								 | 
							
								  stateProps.forEach(function(prop) {
							 | 
						||
| 
								 | 
							
								    cstate[prop] = state[prop];
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								  const res = new this.constructor(cstate.parent);
							 | 
						||
| 
								 | 
							
								  res._baseState = cstate;
							 | 
						||
| 
								 | 
							
								  return res;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Node.prototype._wrap = function wrap() {
							 | 
						||
| 
								 | 
							
								  const state = this._baseState;
							 | 
						||
| 
								 | 
							
								  methods.forEach(function(method) {
							 | 
						||
| 
								 | 
							
								    this[method] = function _wrappedMethod() {
							 | 
						||
| 
								 | 
							
								      const clone = new this.constructor(this);
							 | 
						||
| 
								 | 
							
								      state.children.push(clone);
							 | 
						||
| 
								 | 
							
								      return clone[method].apply(clone, arguments);
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								  }, this);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Node.prototype._init = function init(body) {
							 | 
						||
| 
								 | 
							
								  const state = this._baseState;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  assert(state.parent === null);
							 | 
						||
| 
								 | 
							
								  body.call(this);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Filter children
							 | 
						||
| 
								 | 
							
								  state.children = state.children.filter(function(child) {
							 | 
						||
| 
								 | 
							
								    return child._baseState.parent === this;
							 | 
						||
| 
								 | 
							
								  }, this);
							 | 
						||
| 
								 | 
							
								  assert.equal(state.children.length, 1, 'Root node can have only one child');
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Node.prototype._useArgs = function useArgs(args) {
							 | 
						||
| 
								 | 
							
								  const state = this._baseState;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Filter children and args
							 | 
						||
| 
								 | 
							
								  const children = args.filter(function(arg) {
							 | 
						||
| 
								 | 
							
								    return arg instanceof this.constructor;
							 | 
						||
| 
								 | 
							
								  }, this);
							 | 
						||
| 
								 | 
							
								  args = args.filter(function(arg) {
							 | 
						||
| 
								 | 
							
								    return !(arg instanceof this.constructor);
							 | 
						||
| 
								 | 
							
								  }, this);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (children.length !== 0) {
							 | 
						||
| 
								 | 
							
								    assert(state.children === null);
							 | 
						||
| 
								 | 
							
								    state.children = children;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Replace parent to maintain backward link
							 | 
						||
| 
								 | 
							
								    children.forEach(function(child) {
							 | 
						||
| 
								 | 
							
								      child._baseState.parent = this;
							 | 
						||
| 
								 | 
							
								    }, this);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  if (args.length !== 0) {
							 | 
						||
| 
								 | 
							
								    assert(state.args === null);
							 | 
						||
| 
								 | 
							
								    state.args = args;
							 | 
						||
| 
								 | 
							
								    state.reverseArgs = args.map(function(arg) {
							 | 
						||
| 
								 | 
							
								      if (typeof arg !== 'object' || arg.constructor !== Object)
							 | 
						||
| 
								 | 
							
								        return arg;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      const res = {};
							 | 
						||
| 
								 | 
							
								      Object.keys(arg).forEach(function(key) {
							 | 
						||
| 
								 | 
							
								        if (key == (key | 0))
							 | 
						||
| 
								 | 
							
								          key |= 0;
							 | 
						||
| 
								 | 
							
								        const value = arg[key];
							 | 
						||
| 
								 | 
							
								        res[value] = key;
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								      return res;
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Overrided methods
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								overrided.forEach(function(method) {
							 | 
						||
| 
								 | 
							
								  Node.prototype[method] = function _overrided() {
							 | 
						||
| 
								 | 
							
								    const state = this._baseState;
							 | 
						||
| 
								 | 
							
								    throw new Error(method + ' not implemented for encoding: ' + state.enc);
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Public methods
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								tags.forEach(function(tag) {
							 | 
						||
| 
								 | 
							
								  Node.prototype[tag] = function _tagMethod() {
							 | 
						||
| 
								 | 
							
								    const state = this._baseState;
							 | 
						||
| 
								 | 
							
								    const args = Array.prototype.slice.call(arguments);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    assert(state.tag === null);
							 | 
						||
| 
								 | 
							
								    state.tag = tag;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this._useArgs(args);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return this;
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Node.prototype.use = function use(item) {
							 | 
						||
| 
								 | 
							
								  assert(item);
							 | 
						||
| 
								 | 
							
								  const state = this._baseState;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  assert(state.use === null);
							 | 
						||
| 
								 | 
							
								  state.use = item;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return this;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Node.prototype.optional = function optional() {
							 | 
						||
| 
								 | 
							
								  const state = this._baseState;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  state.optional = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return this;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Node.prototype.def = function def(val) {
							 | 
						||
| 
								 | 
							
								  const state = this._baseState;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  assert(state['default'] === null);
							 | 
						||
| 
								 | 
							
								  state['default'] = val;
							 | 
						||
| 
								 | 
							
								  state.optional = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return this;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Node.prototype.explicit = function explicit(num) {
							 | 
						||
| 
								 | 
							
								  const state = this._baseState;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  assert(state.explicit === null && state.implicit === null);
							 | 
						||
| 
								 | 
							
								  state.explicit = num;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return this;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Node.prototype.implicit = function implicit(num) {
							 | 
						||
| 
								 | 
							
								  const state = this._baseState;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  assert(state.explicit === null && state.implicit === null);
							 | 
						||
| 
								 | 
							
								  state.implicit = num;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return this;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Node.prototype.obj = function obj() {
							 | 
						||
| 
								 | 
							
								  const state = this._baseState;
							 | 
						||
| 
								 | 
							
								  const args = Array.prototype.slice.call(arguments);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  state.obj = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (args.length !== 0)
							 | 
						||
| 
								 | 
							
								    this._useArgs(args);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return this;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Node.prototype.key = function key(newKey) {
							 | 
						||
| 
								 | 
							
								  const state = this._baseState;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  assert(state.key === null);
							 | 
						||
| 
								 | 
							
								  state.key = newKey;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return this;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Node.prototype.any = function any() {
							 | 
						||
| 
								 | 
							
								  const state = this._baseState;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  state.any = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return this;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Node.prototype.choice = function choice(obj) {
							 | 
						||
| 
								 | 
							
								  const state = this._baseState;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  assert(state.choice === null);
							 | 
						||
| 
								 | 
							
								  state.choice = obj;
							 | 
						||
| 
								 | 
							
								  this._useArgs(Object.keys(obj).map(function(key) {
							 | 
						||
| 
								 | 
							
								    return obj[key];
							 | 
						||
| 
								 | 
							
								  }));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return this;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Node.prototype.contains = function contains(item) {
							 | 
						||
| 
								 | 
							
								  const state = this._baseState;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  assert(state.use === null);
							 | 
						||
| 
								 | 
							
								  state.contains = item;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return this;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Decoding
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Node.prototype._decode = function decode(input, options) {
							 | 
						||
| 
								 | 
							
								  const state = this._baseState;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Decode root node
							 | 
						||
| 
								 | 
							
								  if (state.parent === null)
							 | 
						||
| 
								 | 
							
								    return input.wrapResult(state.children[0]._decode(input, options));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  let result = state['default'];
							 | 
						||
| 
								 | 
							
								  let present = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  let prevKey = null;
							 | 
						||
| 
								 | 
							
								  if (state.key !== null)
							 | 
						||
| 
								 | 
							
								    prevKey = input.enterKey(state.key);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Check if tag is there
							 | 
						||
| 
								 | 
							
								  if (state.optional) {
							 | 
						||
| 
								 | 
							
								    let tag = null;
							 | 
						||
| 
								 | 
							
								    if (state.explicit !== null)
							 | 
						||
| 
								 | 
							
								      tag = state.explicit;
							 | 
						||
| 
								 | 
							
								    else if (state.implicit !== null)
							 | 
						||
| 
								 | 
							
								      tag = state.implicit;
							 | 
						||
| 
								 | 
							
								    else if (state.tag !== null)
							 | 
						||
| 
								 | 
							
								      tag = state.tag;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (tag === null && !state.any) {
							 | 
						||
| 
								 | 
							
								      // Trial and Error
							 | 
						||
| 
								 | 
							
								      const save = input.save();
							 | 
						||
| 
								 | 
							
								      try {
							 | 
						||
| 
								 | 
							
								        if (state.choice === null)
							 | 
						||
| 
								 | 
							
								          this._decodeGeneric(state.tag, input, options);
							 | 
						||
| 
								 | 
							
								        else
							 | 
						||
| 
								 | 
							
								          this._decodeChoice(input, options);
							 | 
						||
| 
								 | 
							
								        present = true;
							 | 
						||
| 
								 | 
							
								      } catch (e) {
							 | 
						||
| 
								 | 
							
								        present = false;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      input.restore(save);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      present = this._peekTag(input, tag, state.any);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (input.isError(present))
							 | 
						||
| 
								 | 
							
								        return present;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Push object on stack
							 | 
						||
| 
								 | 
							
								  let prevObj;
							 | 
						||
| 
								 | 
							
								  if (state.obj && present)
							 | 
						||
| 
								 | 
							
								    prevObj = input.enterObject();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (present) {
							 | 
						||
| 
								 | 
							
								    // Unwrap explicit values
							 | 
						||
| 
								 | 
							
								    if (state.explicit !== null) {
							 | 
						||
| 
								 | 
							
								      const explicit = this._decodeTag(input, state.explicit);
							 | 
						||
| 
								 | 
							
								      if (input.isError(explicit))
							 | 
						||
| 
								 | 
							
								        return explicit;
							 | 
						||
| 
								 | 
							
								      input = explicit;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const start = input.offset;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Unwrap implicit and normal values
							 | 
						||
| 
								 | 
							
								    if (state.use === null && state.choice === null) {
							 | 
						||
| 
								 | 
							
								      let save;
							 | 
						||
| 
								 | 
							
								      if (state.any)
							 | 
						||
| 
								 | 
							
								        save = input.save();
							 | 
						||
| 
								 | 
							
								      const body = this._decodeTag(
							 | 
						||
| 
								 | 
							
								        input,
							 | 
						||
| 
								 | 
							
								        state.implicit !== null ? state.implicit : state.tag,
							 | 
						||
| 
								 | 
							
								        state.any
							 | 
						||
| 
								 | 
							
								      );
							 | 
						||
| 
								 | 
							
								      if (input.isError(body))
							 | 
						||
| 
								 | 
							
								        return body;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (state.any)
							 | 
						||
| 
								 | 
							
								        result = input.raw(save);
							 | 
						||
| 
								 | 
							
								      else
							 | 
						||
| 
								 | 
							
								        input = body;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (options && options.track && state.tag !== null)
							 | 
						||
| 
								 | 
							
								      options.track(input.path(), start, input.length, 'tagged');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (options && options.track && state.tag !== null)
							 | 
						||
| 
								 | 
							
								      options.track(input.path(), input.offset, input.length, 'content');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Select proper method for tag
							 | 
						||
| 
								 | 
							
								    if (state.any) {
							 | 
						||
| 
								 | 
							
								      // no-op
							 | 
						||
| 
								 | 
							
								    } else if (state.choice === null) {
							 | 
						||
| 
								 | 
							
								      result = this._decodeGeneric(state.tag, input, options);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      result = this._decodeChoice(input, options);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (input.isError(result))
							 | 
						||
| 
								 | 
							
								      return result;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Decode children
							 | 
						||
| 
								 | 
							
								    if (!state.any && state.choice === null && state.children !== null) {
							 | 
						||
| 
								 | 
							
								      state.children.forEach(function decodeChildren(child) {
							 | 
						||
| 
								 | 
							
								        // NOTE: We are ignoring errors here, to let parser continue with other
							 | 
						||
| 
								 | 
							
								        // parts of encoded data
							 | 
						||
| 
								 | 
							
								        child._decode(input, options);
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Decode contained/encoded by schema, only in bit or octet strings
							 | 
						||
| 
								 | 
							
								    if (state.contains && (state.tag === 'octstr' || state.tag === 'bitstr')) {
							 | 
						||
| 
								 | 
							
								      const data = new DecoderBuffer(result);
							 | 
						||
| 
								 | 
							
								      result = this._getUse(state.contains, input._reporterState.obj)
							 | 
						||
| 
								 | 
							
								        ._decode(data, options);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Pop object
							 | 
						||
| 
								 | 
							
								  if (state.obj && present)
							 | 
						||
| 
								 | 
							
								    result = input.leaveObject(prevObj);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Set key
							 | 
						||
| 
								 | 
							
								  if (state.key !== null && (result !== null || present === true))
							 | 
						||
| 
								 | 
							
								    input.leaveKey(prevKey, state.key, result);
							 | 
						||
| 
								 | 
							
								  else if (prevKey !== null)
							 | 
						||
| 
								 | 
							
								    input.exitKey(prevKey);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return result;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Node.prototype._decodeGeneric = function decodeGeneric(tag, input, options) {
							 | 
						||
| 
								 | 
							
								  const state = this._baseState;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (tag === 'seq' || tag === 'set')
							 | 
						||
| 
								 | 
							
								    return null;
							 | 
						||
| 
								 | 
							
								  if (tag === 'seqof' || tag === 'setof')
							 | 
						||
| 
								 | 
							
								    return this._decodeList(input, tag, state.args[0], options);
							 | 
						||
| 
								 | 
							
								  else if (/str$/.test(tag))
							 | 
						||
| 
								 | 
							
								    return this._decodeStr(input, tag, options);
							 | 
						||
| 
								 | 
							
								  else if (tag === 'objid' && state.args)
							 | 
						||
| 
								 | 
							
								    return this._decodeObjid(input, state.args[0], state.args[1], options);
							 | 
						||
| 
								 | 
							
								  else if (tag === 'objid')
							 | 
						||
| 
								 | 
							
								    return this._decodeObjid(input, null, null, options);
							 | 
						||
| 
								 | 
							
								  else if (tag === 'gentime' || tag === 'utctime')
							 | 
						||
| 
								 | 
							
								    return this._decodeTime(input, tag, options);
							 | 
						||
| 
								 | 
							
								  else if (tag === 'null_')
							 | 
						||
| 
								 | 
							
								    return this._decodeNull(input, options);
							 | 
						||
| 
								 | 
							
								  else if (tag === 'bool')
							 | 
						||
| 
								 | 
							
								    return this._decodeBool(input, options);
							 | 
						||
| 
								 | 
							
								  else if (tag === 'objDesc')
							 | 
						||
| 
								 | 
							
								    return this._decodeStr(input, tag, options);
							 | 
						||
| 
								 | 
							
								  else if (tag === 'int' || tag === 'enum')
							 | 
						||
| 
								 | 
							
								    return this._decodeInt(input, state.args && state.args[0], options);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (state.use !== null) {
							 | 
						||
| 
								 | 
							
								    return this._getUse(state.use, input._reporterState.obj)
							 | 
						||
| 
								 | 
							
								      ._decode(input, options);
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    return input.error('unknown tag: ' + tag);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Node.prototype._getUse = function _getUse(entity, obj) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const state = this._baseState;
							 | 
						||
| 
								 | 
							
								  // Create altered use decoder if implicit is set
							 | 
						||
| 
								 | 
							
								  state.useDecoder = this._use(entity, obj);
							 | 
						||
| 
								 | 
							
								  assert(state.useDecoder._baseState.parent === null);
							 | 
						||
| 
								 | 
							
								  state.useDecoder = state.useDecoder._baseState.children[0];
							 | 
						||
| 
								 | 
							
								  if (state.implicit !== state.useDecoder._baseState.implicit) {
							 | 
						||
| 
								 | 
							
								    state.useDecoder = state.useDecoder.clone();
							 | 
						||
| 
								 | 
							
								    state.useDecoder._baseState.implicit = state.implicit;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return state.useDecoder;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Node.prototype._decodeChoice = function decodeChoice(input, options) {
							 | 
						||
| 
								 | 
							
								  const state = this._baseState;
							 | 
						||
| 
								 | 
							
								  let result = null;
							 | 
						||
| 
								 | 
							
								  let match = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  Object.keys(state.choice).some(function(key) {
							 | 
						||
| 
								 | 
							
								    const save = input.save();
							 | 
						||
| 
								 | 
							
								    const node = state.choice[key];
							 | 
						||
| 
								 | 
							
								    try {
							 | 
						||
| 
								 | 
							
								      const value = node._decode(input, options);
							 | 
						||
| 
								 | 
							
								      if (input.isError(value))
							 | 
						||
| 
								 | 
							
								        return false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      result = { type: key, value: value };
							 | 
						||
| 
								 | 
							
								      match = true;
							 | 
						||
| 
								 | 
							
								    } catch (e) {
							 | 
						||
| 
								 | 
							
								      input.restore(save);
							 | 
						||
| 
								 | 
							
								      return false;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return true;
							 | 
						||
| 
								 | 
							
								  }, this);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (!match)
							 | 
						||
| 
								 | 
							
								    return input.error('Choice not matched');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return result;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Encoding
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Node.prototype._createEncoderBuffer = function createEncoderBuffer(data) {
							 | 
						||
| 
								 | 
							
								  return new EncoderBuffer(data, this.reporter);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Node.prototype._encode = function encode(data, reporter, parent) {
							 | 
						||
| 
								 | 
							
								  const state = this._baseState;
							 | 
						||
| 
								 | 
							
								  if (state['default'] !== null && state['default'] === data)
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const result = this._encodeValue(data, reporter, parent);
							 | 
						||
| 
								 | 
							
								  if (result === undefined)
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (this._skipDefault(result, reporter, parent))
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return result;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Node.prototype._encodeValue = function encode(data, reporter, parent) {
							 | 
						||
| 
								 | 
							
								  const state = this._baseState;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Decode root node
							 | 
						||
| 
								 | 
							
								  if (state.parent === null)
							 | 
						||
| 
								 | 
							
								    return state.children[0]._encode(data, reporter || new Reporter());
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  let result = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Set reporter to share it with a child class
							 | 
						||
| 
								 | 
							
								  this.reporter = reporter;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Check if data is there
							 | 
						||
| 
								 | 
							
								  if (state.optional && data === undefined) {
							 | 
						||
| 
								 | 
							
								    if (state['default'] !== null)
							 | 
						||
| 
								 | 
							
								      data = state['default'];
							 | 
						||
| 
								 | 
							
								    else
							 | 
						||
| 
								 | 
							
								      return;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Encode children first
							 | 
						||
| 
								 | 
							
								  let content = null;
							 | 
						||
| 
								 | 
							
								  let primitive = false;
							 | 
						||
| 
								 | 
							
								  if (state.any) {
							 | 
						||
| 
								 | 
							
								    // Anything that was given is translated to buffer
							 | 
						||
| 
								 | 
							
								    result = this._createEncoderBuffer(data);
							 | 
						||
| 
								 | 
							
								  } else if (state.choice) {
							 | 
						||
| 
								 | 
							
								    result = this._encodeChoice(data, reporter);
							 | 
						||
| 
								 | 
							
								  } else if (state.contains) {
							 | 
						||
| 
								 | 
							
								    content = this._getUse(state.contains, parent)._encode(data, reporter);
							 | 
						||
| 
								 | 
							
								    primitive = true;
							 | 
						||
| 
								 | 
							
								  } else if (state.children) {
							 | 
						||
| 
								 | 
							
								    content = state.children.map(function(child) {
							 | 
						||
| 
								 | 
							
								      if (child._baseState.tag === 'null_')
							 | 
						||
| 
								 | 
							
								        return child._encode(null, reporter, data);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (child._baseState.key === null)
							 | 
						||
| 
								 | 
							
								        return reporter.error('Child should have a key');
							 | 
						||
| 
								 | 
							
								      const prevKey = reporter.enterKey(child._baseState.key);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (typeof data !== 'object')
							 | 
						||
| 
								 | 
							
								        return reporter.error('Child expected, but input is not object');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      const res = child._encode(data[child._baseState.key], reporter, data);
							 | 
						||
| 
								 | 
							
								      reporter.leaveKey(prevKey);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      return res;
							 | 
						||
| 
								 | 
							
								    }, this).filter(function(child) {
							 | 
						||
| 
								 | 
							
								      return child;
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								    content = this._createEncoderBuffer(content);
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    if (state.tag === 'seqof' || state.tag === 'setof') {
							 | 
						||
| 
								 | 
							
								      // TODO(indutny): this should be thrown on DSL level
							 | 
						||
| 
								 | 
							
								      if (!(state.args && state.args.length === 1))
							 | 
						||
| 
								 | 
							
								        return reporter.error('Too many args for : ' + state.tag);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (!Array.isArray(data))
							 | 
						||
| 
								 | 
							
								        return reporter.error('seqof/setof, but data is not Array');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      const child = this.clone();
							 | 
						||
| 
								 | 
							
								      child._baseState.implicit = null;
							 | 
						||
| 
								 | 
							
								      content = this._createEncoderBuffer(data.map(function(item) {
							 | 
						||
| 
								 | 
							
								        const state = this._baseState;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return this._getUse(state.args[0], data)._encode(item, reporter);
							 | 
						||
| 
								 | 
							
								      }, child));
							 | 
						||
| 
								 | 
							
								    } else if (state.use !== null) {
							 | 
						||
| 
								 | 
							
								      result = this._getUse(state.use, parent)._encode(data, reporter);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      content = this._encodePrimitive(state.tag, data);
							 | 
						||
| 
								 | 
							
								      primitive = true;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Encode data itself
							 | 
						||
| 
								 | 
							
								  if (!state.any && state.choice === null) {
							 | 
						||
| 
								 | 
							
								    const tag = state.implicit !== null ? state.implicit : state.tag;
							 | 
						||
| 
								 | 
							
								    const cls = state.implicit === null ? 'universal' : 'context';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (tag === null) {
							 | 
						||
| 
								 | 
							
								      if (state.use === null)
							 | 
						||
| 
								 | 
							
								        reporter.error('Tag could be omitted only for .use()');
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      if (state.use === null)
							 | 
						||
| 
								 | 
							
								        result = this._encodeComposite(tag, primitive, cls, content);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Wrap in explicit
							 | 
						||
| 
								 | 
							
								  if (state.explicit !== null)
							 | 
						||
| 
								 | 
							
								    result = this._encodeComposite(state.explicit, false, 'context', result);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return result;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Node.prototype._encodeChoice = function encodeChoice(data, reporter) {
							 | 
						||
| 
								 | 
							
								  const state = this._baseState;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const node = state.choice[data.type];
							 | 
						||
| 
								 | 
							
								  if (!node) {
							 | 
						||
| 
								 | 
							
								    assert(
							 | 
						||
| 
								 | 
							
								      false,
							 | 
						||
| 
								 | 
							
								      data.type + ' not found in ' +
							 | 
						||
| 
								 | 
							
								            JSON.stringify(Object.keys(state.choice)));
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return node._encode(data.value, reporter);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Node.prototype._encodePrimitive = function encodePrimitive(tag, data) {
							 | 
						||
| 
								 | 
							
								  const state = this._baseState;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (/str$/.test(tag))
							 | 
						||
| 
								 | 
							
								    return this._encodeStr(data, tag);
							 | 
						||
| 
								 | 
							
								  else if (tag === 'objid' && state.args)
							 | 
						||
| 
								 | 
							
								    return this._encodeObjid(data, state.reverseArgs[0], state.args[1]);
							 | 
						||
| 
								 | 
							
								  else if (tag === 'objid')
							 | 
						||
| 
								 | 
							
								    return this._encodeObjid(data, null, null);
							 | 
						||
| 
								 | 
							
								  else if (tag === 'gentime' || tag === 'utctime')
							 | 
						||
| 
								 | 
							
								    return this._encodeTime(data, tag);
							 | 
						||
| 
								 | 
							
								  else if (tag === 'null_')
							 | 
						||
| 
								 | 
							
								    return this._encodeNull();
							 | 
						||
| 
								 | 
							
								  else if (tag === 'int' || tag === 'enum')
							 | 
						||
| 
								 | 
							
								    return this._encodeInt(data, state.args && state.reverseArgs[0]);
							 | 
						||
| 
								 | 
							
								  else if (tag === 'bool')
							 | 
						||
| 
								 | 
							
								    return this._encodeBool(data);
							 | 
						||
| 
								 | 
							
								  else if (tag === 'objDesc')
							 | 
						||
| 
								 | 
							
								    return this._encodeStr(data, tag);
							 | 
						||
| 
								 | 
							
								  else
							 | 
						||
| 
								 | 
							
								    throw new Error('Unsupported tag: ' + tag);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Node.prototype._isNumstr = function isNumstr(str) {
							 | 
						||
| 
								 | 
							
								  return /^[0-9 ]*$/.test(str);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Node.prototype._isPrintstr = function isPrintstr(str) {
							 | 
						||
| 
								 | 
							
								  return /^[A-Za-z0-9 '()+,-./:=?]*$/.test(str);
							 | 
						||
| 
								 | 
							
								};
							 |