dotfiles/.vscode/extensions/saviorisdead.RustyCode-0.18.0/node_modules/ctype/ctype.js
Cyryl Płotnicki 2f946d6200 Add .vscode
2016-09-11 10:29:13 +02:00

944 lines
25 KiB
JavaScript

/*
* rm - Feb 2011
* ctype.js
*
* This module provides a simple abstraction towards reading and writing
* different types of binary data. It is designed to use ctio.js and provide a
* richer and more expressive API on top of it.
*
* By default we support the following as built in basic types:
* int8_t
* int16_t
* int32_t
* uint8_t
* uint16_t
* uint32_t
* uint64_t
* float
* double
* char
* char[]
*
* Each type is returned as a Number, with the exception of char and char[]
* which are returned as Node Buffers. A char is considered a uint8_t.
*
* Requests to read and write data are specified as an array of JSON objects.
* This is also the same way that one declares structs. Even if just a single
* value is requested, it must be done as a struct. The array order determines
* the order that we try and read values. Each entry has the following format
* with values marked with a * being optional.
*
* { key: { type: /type/, value*: /value/, offset*: /offset/ }
*
* If offset is defined, we lseek(offset, SEEK_SET) before reading the next
* value. Value is defined when we're writing out data, otherwise it's ignored.
*
*/
var mod_ctf = require('./ctf.js');
var mod_ctio = require('./ctio.js');
var mod_assert = require('assert');
/*
* This is the set of basic types that we support.
*
* read The function to call to read in a value from a buffer
*
* write The function to call to write a value to a buffer
*
*/
var deftypes = {
'uint8_t': { read: ctReadUint8, write: ctWriteUint8 },
'uint16_t': { read: ctReadUint16, write: ctWriteUint16 },
'uint32_t': { read: ctReadUint32, write: ctWriteUint32 },
'uint64_t': { read: ctReadUint64, write: ctWriteUint64 },
'int8_t': { read: ctReadSint8, write: ctWriteSint8 },
'int16_t': { read: ctReadSint16, write: ctWriteSint16 },
'int32_t': { read: ctReadSint32, write: ctWriteSint32 },
'int64_t': { read: ctReadSint64, write: ctWriteSint64 },
'float': { read: ctReadFloat, write: ctWriteFloat },
'double': { read: ctReadDouble, write: ctWriteDouble },
'char': { read: ctReadChar, write: ctWriteChar },
'char[]': { read: ctReadCharArray, write: ctWriteCharArray }
};
/*
* The following are wrappers around the CType IO low level API. They encode
* knowledge about the size and return something in the expected format.
*/
function ctReadUint8(endian, buffer, offset)
{
var val = mod_ctio.ruint8(buffer, endian, offset);
return ({ value: val, size: 1 });
}
function ctReadUint16(endian, buffer, offset)
{
var val = mod_ctio.ruint16(buffer, endian, offset);
return ({ value: val, size: 2 });
}
function ctReadUint32(endian, buffer, offset)
{
var val = mod_ctio.ruint32(buffer, endian, offset);
return ({ value: val, size: 4 });
}
function ctReadUint64(endian, buffer, offset)
{
var val = mod_ctio.ruint64(buffer, endian, offset);
return ({ value: val, size: 8 });
}
function ctReadSint8(endian, buffer, offset)
{
var val = mod_ctio.rsint8(buffer, endian, offset);
return ({ value: val, size: 1 });
}
function ctReadSint16(endian, buffer, offset)
{
var val = mod_ctio.rsint16(buffer, endian, offset);
return ({ value: val, size: 2 });
}
function ctReadSint32(endian, buffer, offset)
{
var val = mod_ctio.rsint32(buffer, endian, offset);
return ({ value: val, size: 4 });
}
function ctReadSint64(endian, buffer, offset)
{
var val = mod_ctio.rsint64(buffer, endian, offset);
return ({ value: val, size: 8 });
}
function ctReadFloat(endian, buffer, offset)
{
var val = mod_ctio.rfloat(buffer, endian, offset);
return ({ value: val, size: 4 });
}
function ctReadDouble(endian, buffer, offset)
{
var val = mod_ctio.rdouble(buffer, endian, offset);
return ({ value: val, size: 8 });
}
/*
* Reads a single character into a node buffer
*/
function ctReadChar(endian, buffer, offset)
{
var res = new Buffer(1);
res[0] = mod_ctio.ruint8(buffer, endian, offset);
return ({ value: res, size: 1 });
}
function ctReadCharArray(length, endian, buffer, offset)
{
var ii;
var res = new Buffer(length);
for (ii = 0; ii < length; ii++)
res[ii] = mod_ctio.ruint8(buffer, endian, offset + ii);
return ({ value: res, size: length });
}
function ctWriteUint8(value, endian, buffer, offset)
{
mod_ctio.wuint8(value, endian, buffer, offset);
return (1);
}
function ctWriteUint16(value, endian, buffer, offset)
{
mod_ctio.wuint16(value, endian, buffer, offset);
return (2);
}
function ctWriteUint32(value, endian, buffer, offset)
{
mod_ctio.wuint32(value, endian, buffer, offset);
return (4);
}
function ctWriteUint64(value, endian, buffer, offset)
{
mod_ctio.wuint64(value, endian, buffer, offset);
return (8);
}
function ctWriteSint8(value, endian, buffer, offset)
{
mod_ctio.wsint8(value, endian, buffer, offset);
return (1);
}
function ctWriteSint16(value, endian, buffer, offset)
{
mod_ctio.wsint16(value, endian, buffer, offset);
return (2);
}
function ctWriteSint32(value, endian, buffer, offset)
{
mod_ctio.wsint32(value, endian, buffer, offset);
return (4);
}
function ctWriteSint64(value, endian, buffer, offset)
{
mod_ctio.wsint64(value, endian, buffer, offset);
return (8);
}
function ctWriteFloat(value, endian, buffer, offset)
{
mod_ctio.wfloat(value, endian, buffer, offset);
return (4);
}
function ctWriteDouble(value, endian, buffer, offset)
{
mod_ctio.wdouble(value, endian, buffer, offset);
return (8);
}
/*
* Writes a single character into a node buffer
*/
function ctWriteChar(value, endian, buffer, offset)
{
if (!(value instanceof Buffer))
throw (new Error('Input must be a buffer'));
mod_ctio.ruint8(value[0], endian, buffer, offset);
return (1);
}
/*
* We're going to write 0s into the buffer if the string is shorter than the
* length of the array.
*/
function ctWriteCharArray(value, length, endian, buffer, offset)
{
var ii;
if (!(value instanceof Buffer))
throw (new Error('Input must be a buffer'));
if (value.length > length)
throw (new Error('value length greater than array length'));
for (ii = 0; ii < value.length && ii < length; ii++)
mod_ctio.wuint8(value[ii], endian, buffer, offset + ii);
for (; ii < length; ii++)
mod_ctio.wuint8(0, endian, offset + ii);
return (length);
}
/*
* Each parser has their own set of types. We want to make sure that they each
* get their own copy as they may need to modify it.
*/
function ctGetBasicTypes()
{
var ret = {};
var key;
for (key in deftypes)
ret[key] = deftypes[key];
return (ret);
}
/*
* Given a string in the form of type[length] we want to split this into an
* object that extracts that information. We want to note that we could possibly
* have nested arrays so this should only check the furthest one. It may also be
* the case that we have no [] pieces, in which case we just return the current
* type.
*/
function ctParseType(str)
{
var begInd, endInd;
var type, len;
if (typeof (str) != 'string')
throw (new Error('type must be a Javascript string'));
endInd = str.lastIndexOf(']');
if (endInd == -1) {
if (str.lastIndexOf('[') != -1)
throw (new Error('found invalid type with \'[\' but ' +
'no corresponding \']\''));
return ({ type: str });
}
begInd = str.lastIndexOf('[');
if (begInd == -1)
throw (new Error('found invalid type with \']\' but ' +
'no corresponding \'[\''));
if (begInd >= endInd)
throw (new Error('malformed type, \']\' appears before \'[\''));
type = str.substring(0, begInd);
len = str.substring(begInd + 1, endInd);
return ({ type: type, len: len });
}
/*
* Given a request validate that all of the fields for it are valid and make
* sense. This includes verifying the following notions:
* - Each type requested is present in types
* - Only allow a name for a field to be specified once
* - If an array is specified, validate that the requested field exists and
* comes before it.
* - If fields is defined, check that each entry has the occurrence of field
*/
function ctCheckReq(def, types, fields)
{
var ii, jj;
var req, keys, key;
var found = {};
if (!(def instanceof Array))
throw (new Error('definition is not an array'));
if (def.length === 0)
throw (new Error('definition must have at least one element'));
for (ii = 0; ii < def.length; ii++) {
req = def[ii];
if (!(req instanceof Object))
throw (new Error('definition must be an array of' +
'objects'));
keys = Object.keys(req);
if (keys.length != 1)
throw (new Error('definition entry must only have ' +
'one key'));
if (keys[0] in found)
throw (new Error('Specified name already ' +
'specified: ' + keys[0]));
if (!('type' in req[keys[0]]))
throw (new Error('missing required type definition'));
key = ctParseType(req[keys[0]]['type']);
/*
* We may have nested arrays, we need to check the validity of
* the types until the len field is undefined in key. However,
* each time len is defined we need to verify it is either an
* integer or corresponds to an already seen key.
*/
while (key['len'] !== undefined) {
if (isNaN(parseInt(key['len'], 10))) {
if (!(key['len'] in found))
throw (new Error('Given an array ' +
'length without a matching type'));
}
key = ctParseType(key['type']);
}
/* Now we can validate if the type is valid */
if (!(key['type'] in types))
throw (new Error('type not found or typdefed: ' +
key['type']));
/* Check for any required fields */
if (fields !== undefined) {
for (jj = 0; jj < fields.length; jj++) {
if (!(fields[jj] in req[keys[0]]))
throw (new Error('Missing required ' +
'field: ' + fields[jj]));
}
}
found[keys[0]] = true;
}
}
/*
* Create a new instance of the parser. Each parser has its own store of
* typedefs and endianness. Conf is an object with the following required
* values:
*
* endian Either 'big' or 'little' do determine the endianness we
* want to read from or write to.
*
* And the following optional values:
*
* char-type Valid options here are uint8 and int8. If uint8 is
* specified this changes the default behavior of a single
* char from being a buffer of a single character to being
* a uint8_t. If int8, it becomes an int8_t instead.
*/
function CTypeParser(conf)
{
if (!conf) throw (new Error('missing required argument'));
if (!('endian' in conf))
throw (new Error('missing required endian value'));
if (conf['endian'] != 'big' && conf['endian'] != 'little')
throw (new Error('Invalid endian type'));
if ('char-type' in conf && (conf['char-type'] != 'uint8' &&
conf['char-type'] != 'int8'))
throw (new Error('invalid option for char-type: ' +
conf['char-type']));
this.endian = conf['endian'];
this.types = ctGetBasicTypes();
/*
* There may be a more graceful way to do this, but this will have to
* serve.
*/
if ('char-type' in conf && conf['char-type'] == 'uint8')
this.types['char'] = this.types['uint8_t'];
if ('char-type' in conf && conf['char-type'] == 'int8')
this.types['char'] = this.types['int8_t'];
}
/*
* Sets the current endian value for the Parser. If the value is not valid,
* throws an Error.
*
* endian Either 'big' or 'little' do determine the endianness we
* want to read from or write to.
*
*/
CTypeParser.prototype.setEndian = function (endian)
{
if (endian != 'big' && endian != 'little')
throw (new Error('invalid endian type, must be big or ' +
'little'));
this.endian = endian;
};
/*
* Returns the current value of the endian value for the parser.
*/
CTypeParser.prototype.getEndian = function ()
{
return (this.endian);
};
/*
* A user has requested to add a type, let us honor their request. Yet, if their
* request doth spurn us, send them unto the Hells which Dante describes.
*
* name The string for the type definition we're adding
*
* value Either a string that is a type/array name or an object
* that describes a struct.
*/
CTypeParser.prototype.typedef = function (name, value)
{
var type;
if (name === undefined)
throw (new (Error('missing required typedef argument: name')));
if (value === undefined)
throw (new (Error('missing required typedef argument: value')));
if (typeof (name) != 'string')
throw (new (Error('the name of a type must be a string')));
type = ctParseType(name);
if (type['len'] !== undefined)
throw (new Error('Cannot have an array in the typedef name'));
if (name in this.types)
throw (new Error('typedef name already present: ' + name));
if (typeof (value) != 'string' && !(value instanceof Array))
throw (new Error('typedef value must either be a string or ' +
'struct'));
if (typeof (value) == 'string') {
type = ctParseType(value);
if (type['len'] !== undefined) {
if (isNaN(parseInt(type['len'], 10)))
throw (new (Error('typedef value must use ' +
'fixed size array when outside of a ' +
'struct')));
}
this.types[name] = value;
} else {
/* We have a struct, validate it */
ctCheckReq(value, this.types);
this.types[name] = value;
}
};
/*
* Include all of the typedefs, but none of the built in types. This should be
* treated as read-only.
*/
CTypeParser.prototype.lstypes = function ()
{
var key;
var ret = {};
for (key in this.types) {
if (key in deftypes)
continue;
ret[key] = this.types[key];
}
return (ret);
};
/*
* Given a type string that may have array types that aren't numbers, try and
* fill them in from the values object. The object should be of the format where
* indexing into it should return a number for that type.
*
* str The type string
*
* values An object that can be used to fulfill type information
*/
function ctResolveArray(str, values)
{
var ret = '';
var type = ctParseType(str);
while (type['len'] !== undefined) {
if (isNaN(parseInt(type['len'], 10))) {
if (typeof (values[type['len']]) != 'number')
throw (new Error('cannot sawp in non-number ' +
'for array value'));
ret = '[' + values[type['len']] + ']' + ret;
} else {
ret = '[' + type['len'] + ']' + ret;
}
type = ctParseType(type['type']);
}
ret = type['type'] + ret;
return (ret);
}
/*
* [private] Either the typedef resolves to another type string or to a struct.
* If it resolves to a struct, we just pass it off to read struct. If not, we
* can just pass it off to read entry.
*/
CTypeParser.prototype.resolveTypedef = function (type, dispatch, buffer,
offset, value)
{
var pt;
mod_assert.ok(type in this.types);
if (typeof (this.types[type]) == 'string') {
pt = ctParseType(this.types[type]);
if (dispatch == 'read')
return (this.readEntry(pt, buffer, offset));
else if (dispatch == 'write')
return (this.writeEntry(value, pt, buffer, offset));
else
throw (new Error('invalid dispatch type to ' +
'resolveTypedef'));
} else {
if (dispatch == 'read')
return (this.readStruct(this.types[type], buffer,
offset));
else if (dispatch == 'write')
return (this.writeStruct(value, this.types[type],
buffer, offset));
else
throw (new Error('invalid dispatch type to ' +
'resolveTypedef'));
}
};
/*
* [private] Try and read in the specific entry.
*/
CTypeParser.prototype.readEntry = function (type, buffer, offset)
{
var parse, len;
/*
* Because we want to special case char[]s this is unfortunately
* a bit uglier than it really should be. We want to special
* case char[]s so that we return a node buffer, thus they are a
* first class type where as all other arrays just call into a
* generic array routine which calls their data-specific routine
* the specified number of times.
*
* The valid dispatch options we have are:
* - Array and char => char[] handler
* - Generic array handler
* - Generic typedef handler
* - Basic type handler
*/
if (type['len'] !== undefined) {
len = parseInt(type['len'], 10);
if (isNaN(len))
throw (new Error('somehow got a non-numeric length'));
if (type['type'] == 'char')
parse = this.types['char[]']['read'](len,
this.endian, buffer, offset);
else
parse = this.readArray(type['type'],
len, buffer, offset);
} else {
if (type['type'] in deftypes)
parse = this.types[type['type']]['read'](this.endian,
buffer, offset);
else
parse = this.resolveTypedef(type['type'], 'read',
buffer, offset);
}
return (parse);
};
/*
* [private] Read an array of data
*/
CTypeParser.prototype.readArray = function (type, length, buffer, offset)
{
var ii, ent, pt;
var baseOffset = offset;
var ret = new Array(length);
pt = ctParseType(type);
for (ii = 0; ii < length; ii++) {
ent = this.readEntry(pt, buffer, offset);
offset += ent['size'];
ret[ii] = ent['value'];
}
return ({ value: ret, size: offset - baseOffset });
};
/*
* [private] Read a single struct in.
*/
CTypeParser.prototype.readStruct = function (def, buffer, offset)
{
var parse, ii, type, entry, key;
var baseOffset = offset;
var ret = {};
/* Walk it and handle doing what's necessary */
for (ii = 0; ii < def.length; ii++) {
key = Object.keys(def[ii])[0];
entry = def[ii][key];
/* Resolve all array values */
type = ctParseType(ctResolveArray(entry['type'], ret));
if ('offset' in entry)
offset = baseOffset + entry['offset'];
parse = this.readEntry(type, buffer, offset);
offset += parse['size'];
ret[key] = parse['value'];
}
return ({ value: ret, size: (offset-baseOffset)});
};
/*
* This is what we were born to do. We read the data from a buffer and return it
* in an object whose keys match the values from the object.
*
* def The array definition of the data to read in
*
* buffer The buffer to read data from
*
* offset The offset to start writing to
*
* Returns an object where each key corresponds to an entry in def and the value
* is the read value.
*/
CTypeParser.prototype.readData = function (def, buffer, offset)
{
/* Sanity check for arguments */
if (def === undefined)
throw (new Error('missing definition for what we should be' +
'parsing'));
if (buffer === undefined)
throw (new Error('missing buffer for what we should be ' +
'parsing'));
if (offset === undefined)
throw (new Error('missing offset for what we should be ' +
'parsing'));
/* Sanity check the object definition */
ctCheckReq(def, this.types);
return (this.readStruct(def, buffer, offset)['value']);
};
/*
* [private] Write out an array of data
*/
CTypeParser.prototype.writeArray = function (value, type, length, buffer,
offset)
{
var ii, pt;
var baseOffset = offset;
if (!(value instanceof Array))
throw (new Error('asked to write an array, but value is not ' +
'an array'));
if (value.length != length)
throw (new Error('asked to write array of length ' + length +
' but that does not match value length: ' + value.length));
pt = ctParseType(type);
for (ii = 0; ii < length; ii++)
offset += this.writeEntry(value[ii], pt, buffer, offset);
return (offset - baseOffset);
};
/*
* [private] Write the specific entry
*/
CTypeParser.prototype.writeEntry = function (value, type, buffer, offset)
{
var len, ret;
if (type['len'] !== undefined) {
len = parseInt(type['len'], 10);
if (isNaN(len))
throw (new Error('somehow got a non-numeric length'));
if (type['type'] == 'char')
ret = this.types['char[]']['write'](value, len,
this.endian, buffer, offset);
else
ret = this.writeArray(value, type['type'],
len, buffer, offset);
} else {
if (type['type'] in deftypes)
ret = this.types[type['type']]['write'](value,
this.endian, buffer, offset);
else
ret = this.resolveTypedef(type['type'], 'write',
buffer, offset, value);
}
return (ret);
};
/*
* [private] Write a single struct out.
*/
CTypeParser.prototype.writeStruct = function (value, def, buffer, offset)
{
var ii, entry, type, key;
var baseOffset = offset;
var vals = {};
for (ii = 0; ii < def.length; ii++) {
key = Object.keys(def[ii])[0];
entry = def[ii][key];
type = ctParseType(ctResolveArray(entry['type'], vals));
if ('offset' in entry)
offset = baseOffset + entry['offset'];
offset += this.writeEntry(value[ii], type, buffer, offset);
/* Now that we've written it out, we can use it for arrays */
vals[key] = value[ii];
}
return (offset);
};
/*
* Unfortunately, we're stuck with the sins of an initial poor design. Because
* of that, we are going to have to support the old way of writing data via
* writeData. There we insert the values that you want to write into the
* definition. A little baroque. Internally, we use the new model. So we need to
* just get those values out of there. But to maintain the principle of least
* surprise, we're not going to modify the input data.
*/
function getValues(def)
{
var ii, out, key;
out = [];
for (ii = 0; ii < def.length; ii++) {
key = Object.keys(def[ii])[0];
mod_assert.ok('value' in def[ii][key]);
out.push(def[ii][key]['value']);
}
return (out);
}
/*
* This is the second half of what we were born to do, write out the data
* itself. Historically this function required you to put your values in the
* definition section. This was not the smartest thing to do and a bit of an
* oversight to be honest. As such, this function now takes a values argument.
* If values is non-null and non-undefined, it will be used to determine the
* values. This means that the old method is still supported, but is no longer
* acceptable.
*
* def The array definition of the data to write out with
* values
*
* buffer The buffer to write to
*
* offset The offset in the buffer to write to
*
* values An array of values to write.
*/
CTypeParser.prototype.writeData = function (def, buffer, offset, values)
{
var hv;
if (def === undefined)
throw (new Error('missing definition for what we should be' +
'parsing'));
if (buffer === undefined)
throw (new Error('missing buffer for what we should be ' +
'parsing'));
if (offset === undefined)
throw (new Error('missing offset for what we should be ' +
'parsing'));
hv = (values != null && values != undefined);
if (hv) {
if (!Array.isArray(values))
throw (new Error('missing values for writing'));
ctCheckReq(def, this.types);
} else {
ctCheckReq(def, this.types, [ 'value' ]);
}
this.writeStruct(hv ? values : getValues(def), def, buffer, offset);
};
/*
* Functions to go to and from 64 bit numbers in a way that is compatible with
* Javascript limitations. There are two sets. One where the user is okay with
* an approximation and one where they are definitely not okay with an
* approximation.
*/
/*
* Attempts to convert an array of two integers returned from rsint64 / ruint64
* into an absolute 64 bit number. If however the value would exceed 2^52 this
* will instead throw an error. The mantissa in a double is a 52 bit number and
* rather than potentially give you a value that is an approximation this will
* error. If you would rather an approximation, please see toApprox64.
*
* val An array of two 32-bit integers
*/
function toAbs64(val)
{
if (val === undefined)
throw (new Error('missing required arg: value'));
if (!Array.isArray(val))
throw (new Error('value must be an array'));
if (val.length != 2)
throw (new Error('value must be an array of length 2'));
/* We have 20 bits worth of precision in this range */
if (val[0] >= 0x100000)
throw (new Error('value would become approximated'));
return (val[0] * Math.pow(2, 32) + val[1]);
}
/*
* Will return the 64 bit value as returned in an array from rsint64 / ruint64
* to a value as close as it can. Note that Javascript stores all numbers as a
* double and the mantissa only has 52 bits. Thus this version may approximate
* the value.
*
* val An array of two 32-bit integers
*/
function toApprox64(val)
{
if (val === undefined)
throw (new Error('missing required arg: value'));
if (!Array.isArray(val))
throw (new Error('value must be an array'));
if (val.length != 2)
throw (new Error('value must be an array of length 2'));
return (Math.pow(2, 32) * val[0] + val[1]);
}
function parseCTF(json, conf)
{
var ctype = new CTypeParser(conf);
mod_ctf.ctfParseJson(json, ctype);
return (ctype);
}
/*
* Export the few things we actually want to. Currently this is just the CType
* Parser and ctio.
*/
exports.Parser = CTypeParser;
exports.toAbs64 = toAbs64;
exports.toApprox64 = toApprox64;
exports.parseCTF = parseCTF;
exports.ruint8 = mod_ctio.ruint8;
exports.ruint16 = mod_ctio.ruint16;
exports.ruint32 = mod_ctio.ruint32;
exports.ruint64 = mod_ctio.ruint64;
exports.wuint8 = mod_ctio.wuint8;
exports.wuint16 = mod_ctio.wuint16;
exports.wuint32 = mod_ctio.wuint32;
exports.wuint64 = mod_ctio.wuint64;
exports.rsint8 = mod_ctio.rsint8;
exports.rsint16 = mod_ctio.rsint16;
exports.rsint32 = mod_ctio.rsint32;
exports.rsint64 = mod_ctio.rsint64;
exports.wsint8 = mod_ctio.wsint8;
exports.wsint16 = mod_ctio.wsint16;
exports.wsint32 = mod_ctio.wsint32;
exports.wsint64 = mod_ctio.wsint64;
exports.rfloat = mod_ctio.rfloat;
exports.rdouble = mod_ctio.rdouble;
exports.wfloat = mod_ctio.wfloat;
exports.wdouble = mod_ctio.wdouble;