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); | ||
|  | }; |