Initial commit
This commit is contained in:
+140
@@ -0,0 +1,140 @@
|
||||
const { inherits } = require('util')
|
||||
|
||||
const { Reporter } = require('../base/reporter')
|
||||
|
||||
function DecoderBuffer (base, options) {
|
||||
Reporter.call(this, options)
|
||||
if (!Buffer.isBuffer(base)) {
|
||||
this.error('Input not Buffer')
|
||||
return
|
||||
}
|
||||
|
||||
this.base = base
|
||||
this.offset = 0
|
||||
this.length = base.length
|
||||
}
|
||||
inherits(DecoderBuffer, Reporter)
|
||||
|
||||
DecoderBuffer.isDecoderBuffer = function isDecoderBuffer (data) {
|
||||
if (data instanceof DecoderBuffer) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Or accept compatible API
|
||||
const isCompatible = typeof data === 'object' &&
|
||||
Buffer.isBuffer(data.base) &&
|
||||
data.constructor.name === 'DecoderBuffer' &&
|
||||
typeof data.offset === 'number' &&
|
||||
typeof data.length === 'number' &&
|
||||
typeof data.save === 'function' &&
|
||||
typeof data.restore === 'function' &&
|
||||
typeof data.isEmpty === 'function' &&
|
||||
typeof data.readUInt8 === 'function' &&
|
||||
typeof data.skip === 'function' &&
|
||||
typeof data.raw === 'function'
|
||||
|
||||
return isCompatible
|
||||
}
|
||||
|
||||
DecoderBuffer.prototype.save = function save () {
|
||||
return { offset: this.offset, reporter: Reporter.prototype.save.call(this) }
|
||||
}
|
||||
|
||||
DecoderBuffer.prototype.restore = function restore (save) {
|
||||
// Return skipped data
|
||||
const res = new DecoderBuffer(this.base)
|
||||
res.offset = save.offset
|
||||
res.length = this.offset
|
||||
|
||||
this.offset = save.offset
|
||||
Reporter.prototype.restore.call(this, save.reporter)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
DecoderBuffer.prototype.isEmpty = function isEmpty () {
|
||||
return this.offset === this.length
|
||||
}
|
||||
|
||||
DecoderBuffer.prototype.readUInt8 = function readUInt8 (fail) {
|
||||
if (this.offset + 1 <= this.length) { return this.base.readUInt8(this.offset++, true) } else { return this.error(fail || 'DecoderBuffer overrun') }
|
||||
}
|
||||
|
||||
DecoderBuffer.prototype.skip = function skip (bytes, fail) {
|
||||
if (!(this.offset + bytes <= this.length)) { return this.error(fail || 'DecoderBuffer overrun') }
|
||||
|
||||
const res = new DecoderBuffer(this.base)
|
||||
|
||||
// Share reporter state
|
||||
res._reporterState = this._reporterState
|
||||
|
||||
res.offset = this.offset
|
||||
res.length = this.offset + bytes
|
||||
this.offset += bytes
|
||||
return res
|
||||
}
|
||||
|
||||
DecoderBuffer.prototype.raw = function raw (save) {
|
||||
return this.base.slice(save ? save.offset : this.offset, this.length)
|
||||
}
|
||||
|
||||
function EncoderBuffer (value, reporter) {
|
||||
if (Array.isArray(value)) {
|
||||
this.length = 0
|
||||
this.value = value.map(function (item) {
|
||||
if (!EncoderBuffer.isEncoderBuffer(item)) { item = new EncoderBuffer(item, reporter) }
|
||||
this.length += item.length
|
||||
return item
|
||||
}, this)
|
||||
} else if (typeof value === 'number') {
|
||||
if (!(value >= 0 && value <= 0xff)) { return reporter.error('non-byte EncoderBuffer value') }
|
||||
this.value = value
|
||||
this.length = 1
|
||||
} else if (typeof value === 'string') {
|
||||
this.value = value
|
||||
this.length = Buffer.byteLength(value)
|
||||
} else if (Buffer.isBuffer(value)) {
|
||||
this.value = value
|
||||
this.length = value.length
|
||||
} else {
|
||||
return reporter.error(`Unsupported type: ${typeof value}`)
|
||||
}
|
||||
}
|
||||
|
||||
EncoderBuffer.isEncoderBuffer = function isEncoderBuffer (data) {
|
||||
if (data instanceof EncoderBuffer) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Or accept compatible API
|
||||
const isCompatible = typeof data === 'object' &&
|
||||
data.constructor.name === 'EncoderBuffer' &&
|
||||
typeof data.length === 'number' &&
|
||||
typeof data.join === 'function'
|
||||
|
||||
return isCompatible
|
||||
}
|
||||
|
||||
EncoderBuffer.prototype.join = function join (out, offset) {
|
||||
if (!out) { out = Buffer.alloc(this.length) }
|
||||
if (!offset) { offset = 0 }
|
||||
|
||||
if (this.length === 0) { return out }
|
||||
|
||||
if (Array.isArray(this.value)) {
|
||||
this.value.forEach(function (item) {
|
||||
item.join(out, offset)
|
||||
offset += item.length
|
||||
})
|
||||
} else {
|
||||
if (typeof this.value === 'number') { out[offset] = this.value } else if (typeof this.value === 'string') { out.write(this.value, offset) } else if (Buffer.isBuffer(this.value)) { this.value.copy(out, offset) }
|
||||
offset += this.length
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
DecoderBuffer,
|
||||
EncoderBuffer
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
const { Reporter } = require('./reporter')
|
||||
const { DecoderBuffer, EncoderBuffer } = require('./buffer')
|
||||
const Node = require('./node')
|
||||
|
||||
module.exports = {
|
||||
DecoderBuffer,
|
||||
EncoderBuffer,
|
||||
Node,
|
||||
Reporter
|
||||
}
|
||||
+555
@@ -0,0 +1,555 @@
|
||||
const { strict: assert } = require('assert')
|
||||
|
||||
const { Reporter } = require('../base/reporter')
|
||||
const { DecoderBuffer, EncoderBuffer } = require('../base/buffer')
|
||||
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
|
||||
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 } // eslint-disable-line eqeqeq
|
||||
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') {
|
||||
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)
|
||||
}
|
||||
|
||||
module.exports = Node
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
const { inherits } = require('util')
|
||||
|
||||
function Reporter (options) {
|
||||
this._reporterState = {
|
||||
obj: null,
|
||||
path: [],
|
||||
options: options || {},
|
||||
errors: []
|
||||
}
|
||||
}
|
||||
|
||||
Reporter.prototype.isError = function isError (obj) {
|
||||
return obj instanceof ReporterError
|
||||
}
|
||||
|
||||
Reporter.prototype.save = function save () {
|
||||
const state = this._reporterState
|
||||
|
||||
return { obj: state.obj, pathLen: state.path.length }
|
||||
}
|
||||
|
||||
Reporter.prototype.restore = function restore (data) {
|
||||
const state = this._reporterState
|
||||
|
||||
state.obj = data.obj
|
||||
state.path = state.path.slice(0, data.pathLen)
|
||||
}
|
||||
|
||||
Reporter.prototype.enterKey = function enterKey (key) {
|
||||
return this._reporterState.path.push(key)
|
||||
}
|
||||
|
||||
Reporter.prototype.exitKey = function exitKey (index) {
|
||||
const state = this._reporterState
|
||||
|
||||
state.path = state.path.slice(0, index - 1)
|
||||
}
|
||||
|
||||
Reporter.prototype.leaveKey = function leaveKey (index, key, value) {
|
||||
const state = this._reporterState
|
||||
|
||||
this.exitKey(index)
|
||||
if (state.obj !== null) { state.obj[key] = value }
|
||||
}
|
||||
|
||||
Reporter.prototype.path = function path () {
|
||||
return this._reporterState.path.join('/')
|
||||
}
|
||||
|
||||
Reporter.prototype.enterObject = function enterObject () {
|
||||
const state = this._reporterState
|
||||
|
||||
const prev = state.obj
|
||||
state.obj = {}
|
||||
return prev
|
||||
}
|
||||
|
||||
Reporter.prototype.leaveObject = function leaveObject (prev) {
|
||||
const state = this._reporterState
|
||||
|
||||
const now = state.obj
|
||||
state.obj = prev
|
||||
return now
|
||||
}
|
||||
|
||||
Reporter.prototype.error = function error (msg) {
|
||||
let err
|
||||
const state = this._reporterState
|
||||
|
||||
const inherited = msg instanceof ReporterError
|
||||
if (inherited) {
|
||||
err = msg
|
||||
} else {
|
||||
err = new ReporterError(state.path.map(function (elem) {
|
||||
return `[${JSON.stringify(elem)}]`
|
||||
}).join(''), msg.message || msg, msg.stack)
|
||||
}
|
||||
|
||||
if (!state.options.partial) { throw err }
|
||||
|
||||
if (!inherited) { state.errors.push(err) }
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
Reporter.prototype.wrapResult = function wrapResult (result) {
|
||||
const state = this._reporterState
|
||||
if (!state.options.partial) { return result }
|
||||
|
||||
return {
|
||||
result: this.isError(result) ? null : result,
|
||||
errors: state.errors
|
||||
}
|
||||
}
|
||||
|
||||
function ReporterError (path, msg) {
|
||||
this.path = path
|
||||
this.rethrow(msg)
|
||||
}
|
||||
inherits(ReporterError, Error)
|
||||
|
||||
ReporterError.prototype.rethrow = function rethrow (msg) {
|
||||
this.message = `${msg} at: ${this.path || '(shallow)'}`
|
||||
if (Error.captureStackTrace) { Error.captureStackTrace(this, ReporterError) }
|
||||
|
||||
if (!this.stack) {
|
||||
try {
|
||||
// IE only adds stack when thrown
|
||||
throw new Error(this.message)
|
||||
} catch (e) {
|
||||
this.stack = e.stack
|
||||
}
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
exports.Reporter = Reporter
|
||||
Reference in New Issue
Block a user