| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634 | 
							- var Reporter = require('../base').Reporter;
 
- var EncoderBuffer = require('../base').EncoderBuffer;
 
- var DecoderBuffer = require('../base').DecoderBuffer;
 
- var assert = require('minimalistic-assert');
 
- // Supported tags
 
- var 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
 
- var methods = [
 
-   'key', 'obj', 'use', 'optional', 'explicit', 'implicit', 'def', 'choice',
 
-   'any', 'contains'
 
- ].concat(tags);
 
- // Overrided methods list
 
- var overrided = [
 
-   '_peekTag', '_decodeTag', '_use',
 
-   '_decodeStr', '_decodeObjid', '_decodeTime',
 
-   '_decodeNull', '_decodeInt', '_decodeBool', '_decodeList',
 
-   '_encodeComposite', '_encodeStr', '_encodeObjid', '_encodeTime',
 
-   '_encodeNull', '_encodeInt', '_encodeBool'
 
- ];
 
- function Node(enc, parent) {
 
-   var state = {};
 
-   this._baseState = state;
 
-   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;
 
- var stateProps = [
 
-   'enc', 'parent', 'children', 'tag', 'args', 'reverseArgs', 'choice',
 
-   'optional', 'any', 'obj', 'use', 'alteredUse', 'key', 'default', 'explicit',
 
-   'implicit', 'contains'
 
- ];
 
- Node.prototype.clone = function clone() {
 
-   var state = this._baseState;
 
-   var cstate = {};
 
-   stateProps.forEach(function(prop) {
 
-     cstate[prop] = state[prop];
 
-   });
 
-   var res = new this.constructor(cstate.parent);
 
-   res._baseState = cstate;
 
-   return res;
 
- };
 
- Node.prototype._wrap = function wrap() {
 
-   var state = this._baseState;
 
-   methods.forEach(function(method) {
 
-     this[method] = function _wrappedMethod() {
 
-       var clone = new this.constructor(this);
 
-       state.children.push(clone);
 
-       return clone[method].apply(clone, arguments);
 
-     };
 
-   }, this);
 
- };
 
- Node.prototype._init = function init(body) {
 
-   var 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) {
 
-   var state = this._baseState;
 
-   // Filter children and args
 
-   var 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;
 
-       var res = {};
 
-       Object.keys(arg).forEach(function(key) {
 
-         if (key == (key | 0))
 
-           key |= 0;
 
-         var value = arg[key];
 
-         res[value] = key;
 
-       });
 
-       return res;
 
-     });
 
-   }
 
- };
 
- //
 
- // Overrided methods
 
- //
 
- overrided.forEach(function(method) {
 
-   Node.prototype[method] = function _overrided() {
 
-     var state = this._baseState;
 
-     throw new Error(method + ' not implemented for encoding: ' + state.enc);
 
-   };
 
- });
 
- //
 
- // Public methods
 
- //
 
- tags.forEach(function(tag) {
 
-   Node.prototype[tag] = function _tagMethod() {
 
-     var state = this._baseState;
 
-     var 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);
 
-   var state = this._baseState;
 
-   assert(state.use === null);
 
-   state.use = item;
 
-   return this;
 
- };
 
- Node.prototype.optional = function optional() {
 
-   var state = this._baseState;
 
-   state.optional = true;
 
-   return this;
 
- };
 
- Node.prototype.def = function def(val) {
 
-   var state = this._baseState;
 
-   assert(state['default'] === null);
 
-   state['default'] = val;
 
-   state.optional = true;
 
-   return this;
 
- };
 
- Node.prototype.explicit = function explicit(num) {
 
-   var state = this._baseState;
 
-   assert(state.explicit === null && state.implicit === null);
 
-   state.explicit = num;
 
-   return this;
 
- };
 
- Node.prototype.implicit = function implicit(num) {
 
-   var state = this._baseState;
 
-   assert(state.explicit === null && state.implicit === null);
 
-   state.implicit = num;
 
-   return this;
 
- };
 
- Node.prototype.obj = function obj() {
 
-   var state = this._baseState;
 
-   var args = Array.prototype.slice.call(arguments);
 
-   state.obj = true;
 
-   if (args.length !== 0)
 
-     this._useArgs(args);
 
-   return this;
 
- };
 
- Node.prototype.key = function key(newKey) {
 
-   var state = this._baseState;
 
-   assert(state.key === null);
 
-   state.key = newKey;
 
-   return this;
 
- };
 
- Node.prototype.any = function any() {
 
-   var state = this._baseState;
 
-   state.any = true;
 
-   return this;
 
- };
 
- Node.prototype.choice = function choice(obj) {
 
-   var 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) {
 
-   var state = this._baseState;
 
-   assert(state.use === null);
 
-   state.contains = item;
 
-   return this;
 
- };
 
- //
 
- // Decoding
 
- //
 
- Node.prototype._decode = function decode(input, options) {
 
-   var state = this._baseState;
 
-   // Decode root node
 
-   if (state.parent === null)
 
-     return input.wrapResult(state.children[0]._decode(input, options));
 
-   var result = state['default'];
 
-   var present = true;
 
-   var prevKey = null;
 
-   if (state.key !== null)
 
-     prevKey = input.enterKey(state.key);
 
-   // Check if tag is there
 
-   if (state.optional) {
 
-     var 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
 
-       var 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
 
-   var prevObj;
 
-   if (state.obj && present)
 
-     prevObj = input.enterObject();
 
-   if (present) {
 
-     // Unwrap explicit values
 
-     if (state.explicit !== null) {
 
-       var explicit = this._decodeTag(input, state.explicit);
 
-       if (input.isError(explicit))
 
-         return explicit;
 
-       input = explicit;
 
-     }
 
-     var start = input.offset;
 
-     // Unwrap implicit and normal values
 
-     if (state.use === null && state.choice === null) {
 
-       if (state.any)
 
-         var save = input.save();
 
-       var 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)
 
-       result = result;
 
-     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')) {
 
-       var 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) {
 
-   var 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) {
 
-   var 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) {
 
-   var state = this._baseState;
 
-   var result = null;
 
-   var match = false;
 
-   Object.keys(state.choice).some(function(key) {
 
-     var save = input.save();
 
-     var node = state.choice[key];
 
-     try {
 
-       var 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) {
 
-   var state = this._baseState;
 
-   if (state['default'] !== null && state['default'] === data)
 
-     return;
 
-   var 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) {
 
-   var state = this._baseState;
 
-   // Decode root node
 
-   if (state.parent === null)
 
-     return state.children[0]._encode(data, reporter || new Reporter());
 
-   var 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
 
-   var content = null;
 
-   var 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');
 
-       var prevKey = reporter.enterKey(child._baseState.key);
 
-       if (typeof data !== 'object')
 
-         return reporter.error('Child expected, but input is not object');
 
-       var 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');
 
-       var child = this.clone();
 
-       child._baseState.implicit = null;
 
-       content = this._createEncoderBuffer(data.map(function(item) {
 
-         var 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
 
-   var result;
 
-   if (!state.any && state.choice === null) {
 
-     var tag = state.implicit !== null ? state.implicit : state.tag;
 
-     var 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) {
 
-   var state = this._baseState;
 
-   var 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) {
 
-   var 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);
 
- };
 
 
  |