"Fossies" - the Fresh Open Source Software Archive

Member "Atom/resources/app/apm/node_modules/ctype/ctype.js" (7 Feb 2017, 25257 Bytes) of archive /windows/misc/atom-windows.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Javascript source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /*
    2  * rm - Feb 2011
    3  * ctype.js
    4  *
    5  * This module provides a simple abstraction towards reading and writing
    6  * different types of binary data. It is designed to use ctio.js and provide a
    7  * richer and more expressive API on top of it.
    8  *
    9  * By default we support the following as built in basic types:
   10  *  int8_t
   11  *  int16_t
   12  *  int32_t
   13  *  uint8_t
   14  *  uint16_t
   15  *  uint32_t
   16  *  uint64_t
   17  *  float
   18  *  double
   19  *  char
   20  *  char[]
   21  *
   22  * Each type is returned as a Number, with the exception of char and char[]
   23  * which are returned as Node Buffers. A char is considered a uint8_t.
   24  *
   25  * Requests to read and write data are specified as an array of JSON objects.
   26  * This is also the same way that one declares structs. Even if just a single
   27  * value is requested, it must be done as a struct. The array order determines
   28  * the order that we try and read values. Each entry has the following format
   29  * with values marked with a * being optional.
   30  *
   31  * { key: { type: /type/, value*: /value/, offset*: /offset/ }
   32  *
   33  * If offset is defined, we lseek(offset, SEEK_SET) before reading the next
   34  * value. Value is defined when we're writing out data, otherwise it's ignored.
   35  *
   36  */
   37 
   38 var mod_ctf = require('./ctf.js');
   39 var mod_ctio = require('./ctio.js');
   40 var mod_assert = require('assert');
   41 
   42 /*
   43  * This is the set of basic types that we support.
   44  *
   45  *  read        The function to call to read in a value from a buffer
   46  *
   47  *  write       The function to call to write a value to a buffer
   48  *
   49  */
   50 var deftypes = {
   51     'uint8_t':  { read: ctReadUint8, write: ctWriteUint8 },
   52     'uint16_t': { read: ctReadUint16, write: ctWriteUint16 },
   53     'uint32_t': { read: ctReadUint32, write: ctWriteUint32 },
   54     'uint64_t': { read: ctReadUint64, write: ctWriteUint64 },
   55     'int8_t': { read: ctReadSint8, write: ctWriteSint8 },
   56     'int16_t': { read: ctReadSint16, write: ctWriteSint16 },
   57     'int32_t': { read: ctReadSint32, write: ctWriteSint32 },
   58     'int64_t': { read: ctReadSint64, write: ctWriteSint64 },
   59     'float': { read: ctReadFloat, write: ctWriteFloat },
   60     'double': { read: ctReadDouble, write: ctWriteDouble },
   61     'char': { read: ctReadChar, write: ctWriteChar },
   62     'char[]': { read: ctReadCharArray, write: ctWriteCharArray }
   63 };
   64 
   65 /*
   66  * The following are wrappers around the CType IO low level API. They encode
   67  * knowledge about the size and return something in the expected format.
   68  */
   69 function ctReadUint8(endian, buffer, offset)
   70 {
   71     var val = mod_ctio.ruint8(buffer, endian, offset);
   72     return ({ value: val, size: 1 });
   73 }
   74 
   75 function ctReadUint16(endian, buffer, offset)
   76 {
   77     var val = mod_ctio.ruint16(buffer, endian, offset);
   78     return ({ value: val, size: 2 });
   79 }
   80 
   81 function ctReadUint32(endian, buffer, offset)
   82 {
   83     var val = mod_ctio.ruint32(buffer, endian, offset);
   84     return ({ value: val, size: 4 });
   85 }
   86 
   87 function ctReadUint64(endian, buffer, offset)
   88 {
   89     var val = mod_ctio.ruint64(buffer, endian, offset);
   90     return ({ value: val, size: 8 });
   91 }
   92 
   93 function ctReadSint8(endian, buffer, offset)
   94 {
   95     var val = mod_ctio.rsint8(buffer, endian, offset);
   96     return ({ value: val, size: 1 });
   97 }
   98 
   99 function ctReadSint16(endian, buffer, offset)
  100 {
  101     var val = mod_ctio.rsint16(buffer, endian, offset);
  102     return ({ value: val, size: 2 });
  103 }
  104 
  105 function ctReadSint32(endian, buffer, offset)
  106 {
  107     var val = mod_ctio.rsint32(buffer, endian, offset);
  108     return ({ value: val, size: 4 });
  109 }
  110 
  111 function ctReadSint64(endian, buffer, offset)
  112 {
  113     var val = mod_ctio.rsint64(buffer, endian, offset);
  114     return ({ value: val, size: 8 });
  115 }
  116 
  117 function ctReadFloat(endian, buffer, offset)
  118 {
  119     var val = mod_ctio.rfloat(buffer, endian, offset);
  120     return ({ value: val, size: 4 });
  121 }
  122 
  123 function ctReadDouble(endian, buffer, offset)
  124 {
  125     var val = mod_ctio.rdouble(buffer, endian, offset);
  126     return ({ value: val, size: 8 });
  127 }
  128 
  129 /*
  130  * Reads a single character into a node buffer
  131  */
  132 function ctReadChar(endian, buffer, offset)
  133 {
  134     var res = new Buffer(1);
  135     res[0] = mod_ctio.ruint8(buffer, endian, offset);
  136     return ({ value: res, size: 1 });
  137 }
  138 
  139 function ctReadCharArray(length, endian, buffer, offset)
  140 {
  141     var ii;
  142     var res = new Buffer(length);
  143 
  144     for (ii = 0; ii < length; ii++)
  145         res[ii] = mod_ctio.ruint8(buffer, endian, offset + ii);
  146 
  147     return ({ value: res, size: length });
  148 }
  149 
  150 function ctWriteUint8(value, endian, buffer, offset)
  151 {
  152     mod_ctio.wuint8(value, endian, buffer, offset);
  153     return (1);
  154 }
  155 
  156 function ctWriteUint16(value, endian, buffer, offset)
  157 {
  158     mod_ctio.wuint16(value, endian, buffer, offset);
  159     return (2);
  160 }
  161 
  162 function ctWriteUint32(value, endian, buffer, offset)
  163 {
  164     mod_ctio.wuint32(value, endian, buffer, offset);
  165     return (4);
  166 }
  167 
  168 function ctWriteUint64(value, endian, buffer, offset)
  169 {
  170     mod_ctio.wuint64(value, endian, buffer, offset);
  171     return (8);
  172 }
  173 
  174 function ctWriteSint8(value, endian, buffer, offset)
  175 {
  176     mod_ctio.wsint8(value, endian, buffer, offset);
  177     return (1);
  178 }
  179 
  180 function ctWriteSint16(value, endian, buffer, offset)
  181 {
  182     mod_ctio.wsint16(value, endian, buffer, offset);
  183     return (2);
  184 }
  185 
  186 function ctWriteSint32(value, endian, buffer, offset)
  187 {
  188     mod_ctio.wsint32(value, endian, buffer, offset);
  189     return (4);
  190 }
  191 
  192 function ctWriteSint64(value, endian, buffer, offset)
  193 {
  194     mod_ctio.wsint64(value, endian, buffer, offset);
  195     return (8);
  196 }
  197 
  198 function ctWriteFloat(value, endian, buffer, offset)
  199 {
  200     mod_ctio.wfloat(value, endian, buffer, offset);
  201     return (4);
  202 }
  203 
  204 function ctWriteDouble(value, endian, buffer, offset)
  205 {
  206     mod_ctio.wdouble(value, endian, buffer, offset);
  207     return (8);
  208 }
  209 
  210 /*
  211  * Writes a single character into a node buffer
  212  */
  213 function ctWriteChar(value, endian, buffer, offset)
  214 {
  215     if (!(value instanceof Buffer))
  216         throw (new Error('Input must be a buffer'));
  217 
  218     mod_ctio.ruint8(value[0], endian, buffer, offset);
  219     return (1);
  220 }
  221 
  222 /*
  223  * We're going to write 0s into the buffer if the string is shorter than the
  224  * length of the array.
  225  */
  226 function ctWriteCharArray(value, length, endian, buffer, offset)
  227 {
  228     var ii;
  229 
  230     if (!(value instanceof Buffer))
  231         throw (new Error('Input must be a buffer'));
  232 
  233     if (value.length > length)
  234         throw (new Error('value length greater than array length'));
  235 
  236     for (ii = 0; ii < value.length && ii < length; ii++)
  237         mod_ctio.wuint8(value[ii], endian, buffer, offset + ii);
  238 
  239     for (; ii < length; ii++)
  240         mod_ctio.wuint8(0, endian, offset + ii);
  241 
  242 
  243     return (length);
  244 }
  245 
  246 /*
  247  * Each parser has their own set of types. We want to make sure that they each
  248  * get their own copy as they may need to modify it.
  249  */
  250 function ctGetBasicTypes()
  251 {
  252     var ret = {};
  253     var key;
  254     for (key in deftypes)
  255         ret[key] = deftypes[key];
  256 
  257     return (ret);
  258 }
  259 
  260 /*
  261  * Given a string in the form of type[length] we want to split this into an
  262  * object that extracts that information. We want to note that we could possibly
  263  * have nested arrays so this should only check the furthest one. It may also be
  264  * the case that we have no [] pieces, in which case we just return the current
  265  * type.
  266  */
  267 function ctParseType(str)
  268 {
  269     var begInd, endInd;
  270     var type, len;
  271     if (typeof (str) != 'string')
  272         throw (new Error('type must be a Javascript string'));
  273 
  274     endInd = str.lastIndexOf(']');
  275     if (endInd == -1) {
  276         if (str.lastIndexOf('[') != -1)
  277             throw (new Error('found invalid type with \'[\' but ' +
  278                 'no corresponding \']\''));
  279 
  280         return ({ type: str });
  281     }
  282 
  283     begInd = str.lastIndexOf('[');
  284     if (begInd == -1)
  285         throw (new Error('found invalid type with \']\' but ' +
  286             'no corresponding \'[\''));
  287 
  288     if (begInd >= endInd)
  289         throw (new Error('malformed type, \']\' appears before \'[\''));
  290 
  291     type = str.substring(0, begInd);
  292     len = str.substring(begInd + 1, endInd);
  293 
  294     return ({ type: type, len: len });
  295 }
  296 
  297 /*
  298  * Given a request validate that all of the fields for it are valid and make
  299  * sense. This includes verifying the following notions:
  300  *  - Each type requested is present in types
  301  *  - Only allow a name for a field to be specified once
  302  *  - If an array is specified, validate that the requested field exists and
  303  *    comes before it.
  304  *  - If fields is defined, check that each entry has the occurrence of field
  305  */
  306 function ctCheckReq(def, types, fields)
  307 {
  308     var ii, jj;
  309     var req, keys, key;
  310     var found = {};
  311 
  312     if (!(def instanceof Array))
  313         throw (new Error('definition is not an array'));
  314 
  315     if (def.length === 0)
  316         throw (new Error('definition must have at least one element'));
  317 
  318     for (ii = 0; ii < def.length; ii++) {
  319         req = def[ii];
  320         if (!(req instanceof Object))
  321             throw (new Error('definition must be an array of' +
  322                 'objects'));
  323 
  324         keys = Object.keys(req);
  325         if (keys.length != 1)
  326             throw (new Error('definition entry must only have ' +
  327                 'one key'));
  328 
  329         if (keys[0] in found)
  330             throw (new Error('Specified name already ' +
  331                 'specified: ' + keys[0]));
  332 
  333         if (!('type' in req[keys[0]]))
  334             throw (new Error('missing required type definition'));
  335 
  336         key = ctParseType(req[keys[0]]['type']);
  337 
  338         /*
  339          * We may have nested arrays, we need to check the validity of
  340          * the types until the len field is undefined in key. However,
  341          * each time len is defined we need to verify it is either an
  342          * integer or corresponds to an already seen key.
  343          */
  344         while (key['len'] !== undefined) {
  345             if (isNaN(parseInt(key['len'], 10))) {
  346                 if (!(key['len'] in found))
  347                     throw (new Error('Given an array ' +
  348                         'length without a matching type'));
  349 
  350             }
  351 
  352             key = ctParseType(key['type']);
  353         }
  354 
  355         /* Now we can validate if the type is valid */
  356         if (!(key['type'] in types))
  357             throw (new Error('type not found or typdefed: ' +
  358                 key['type']));
  359 
  360         /* Check for any required fields */
  361         if (fields !== undefined) {
  362             for (jj = 0; jj < fields.length; jj++) {
  363                 if (!(fields[jj] in req[keys[0]]))
  364                     throw (new Error('Missing required ' +
  365                         'field: ' + fields[jj]));
  366             }
  367         }
  368 
  369         found[keys[0]] = true;
  370     }
  371 }
  372 
  373 
  374 /*
  375  * Create a new instance of the parser. Each parser has its own store of
  376  * typedefs and endianness. Conf is an object with the following required
  377  * values:
  378  *
  379  *  endian      Either 'big' or 'little' do determine the endianness we
  380  *          want to read from or write to.
  381  *
  382  * And the following optional values:
  383  *
  384  *  char-type   Valid options here are uint8 and int8. If uint8 is
  385  *          specified this changes the default behavior of a single
  386  *          char from being a buffer of a single character to being
  387  *          a uint8_t. If int8, it becomes an int8_t instead.
  388  */
  389 function CTypeParser(conf)
  390 {
  391     if (!conf) throw (new Error('missing required argument'));
  392 
  393     if (!('endian' in conf))
  394         throw (new Error('missing required endian value'));
  395 
  396     if (conf['endian'] != 'big' && conf['endian'] != 'little')
  397         throw (new Error('Invalid endian type'));
  398 
  399     if ('char-type' in conf && (conf['char-type'] != 'uint8' &&
  400         conf['char-type'] != 'int8'))
  401         throw (new Error('invalid option for char-type: ' +
  402             conf['char-type']));
  403 
  404     this.endian = conf['endian'];
  405     this.types = ctGetBasicTypes();
  406 
  407     /*
  408      * There may be a more graceful way to do this, but this will have to
  409      * serve.
  410      */
  411     if ('char-type' in conf && conf['char-type'] == 'uint8')
  412         this.types['char'] = this.types['uint8_t'];
  413 
  414     if ('char-type' in conf && conf['char-type'] == 'int8')
  415         this.types['char'] = this.types['int8_t'];
  416 }
  417 
  418 /*
  419  * Sets the current endian value for the Parser. If the value is not valid,
  420  * throws an Error.
  421  *
  422  *  endian      Either 'big' or 'little' do determine the endianness we
  423  *          want to read from or write to.
  424  *
  425  */
  426 CTypeParser.prototype.setEndian = function (endian)
  427 {
  428     if (endian != 'big' && endian != 'little')
  429         throw (new Error('invalid endian type, must be big or ' +
  430             'little'));
  431 
  432     this.endian = endian;
  433 };
  434 
  435 /*
  436  * Returns the current value of the endian value for the parser.
  437  */
  438 CTypeParser.prototype.getEndian = function ()
  439 {
  440     return (this.endian);
  441 };
  442 
  443 /*
  444  * A user has requested to add a type, let us honor their request. Yet, if their
  445  * request doth spurn us, send them unto the Hells which Dante describes.
  446  *
  447  *  name        The string for the type definition we're adding
  448  *
  449  *  value       Either a string that is a type/array name or an object
  450  *          that describes a struct.
  451  */
  452 CTypeParser.prototype.typedef = function (name, value)
  453 {
  454     var type;
  455 
  456     if (name === undefined)
  457         throw (new (Error('missing required typedef argument: name')));
  458 
  459     if (value === undefined)
  460         throw (new (Error('missing required typedef argument: value')));
  461 
  462     if (typeof (name) != 'string')
  463         throw (new (Error('the name of a type must be a string')));
  464 
  465     type = ctParseType(name);
  466 
  467     if (type['len'] !== undefined)
  468         throw (new Error('Cannot have an array in the typedef name'));
  469 
  470     if (name in this.types)
  471         throw (new Error('typedef name already present: ' + name));
  472 
  473     if (typeof (value) != 'string' && !(value instanceof Array))
  474         throw (new Error('typedef value must either be a string or ' +
  475             'struct'));
  476 
  477     if (typeof (value) == 'string') {
  478         type = ctParseType(value);
  479         if (type['len'] !== undefined) {
  480             if (isNaN(parseInt(type['len'], 10)))
  481                 throw (new (Error('typedef value must use ' +
  482                     'fixed size array when outside of a ' +
  483                     'struct')));
  484         }
  485 
  486         this.types[name] = value;
  487     } else {
  488         /* We have a struct, validate it */
  489         ctCheckReq(value, this.types);
  490         this.types[name] = value;
  491     }
  492 };
  493 
  494 /*
  495  * Include all of the typedefs, but none of the built in types. This should be
  496  * treated as read-only.
  497  */
  498 CTypeParser.prototype.lstypes = function ()
  499 {
  500     var key;
  501     var ret = {};
  502 
  503     for (key in this.types) {
  504         if (key in deftypes)
  505             continue;
  506         ret[key] = this.types[key];
  507     }
  508 
  509     return (ret);
  510 };
  511 
  512 /*
  513  * Given a type string that may have array types that aren't numbers, try and
  514  * fill them in from the values object. The object should be of the format where
  515  * indexing into it should return a number for that type.
  516  *
  517  *  str     The type string
  518  *
  519  *  values      An object that can be used to fulfill type information
  520  */
  521 function ctResolveArray(str, values)
  522 {
  523     var ret = '';
  524     var type = ctParseType(str);
  525 
  526     while (type['len'] !== undefined) {
  527         if (isNaN(parseInt(type['len'], 10))) {
  528             if (typeof (values[type['len']]) != 'number')
  529                 throw (new Error('cannot sawp in non-number ' +
  530                     'for array value'));
  531             ret = '[' + values[type['len']] + ']' + ret;
  532         } else {
  533             ret = '[' + type['len'] + ']' + ret;
  534         }
  535         type = ctParseType(type['type']);
  536     }
  537 
  538     ret = type['type'] + ret;
  539 
  540     return (ret);
  541 }
  542 
  543 /*
  544  * [private] Either the typedef resolves to another type string or to a struct.
  545  * If it resolves to a struct, we just pass it off to read struct. If not, we
  546  * can just pass it off to read entry.
  547  */
  548 CTypeParser.prototype.resolveTypedef = function (type, dispatch, buffer,
  549     offset, value)
  550 {
  551     var pt;
  552 
  553     mod_assert.ok(type in this.types);
  554     if (typeof (this.types[type]) == 'string') {
  555         pt = ctParseType(this.types[type]);
  556         if (dispatch == 'read')
  557             return (this.readEntry(pt, buffer, offset));
  558         else if (dispatch == 'write')
  559             return (this.writeEntry(value, pt, buffer, offset));
  560         else
  561             throw (new Error('invalid dispatch type to ' +
  562                 'resolveTypedef'));
  563     } else {
  564         if (dispatch == 'read')
  565             return (this.readStruct(this.types[type], buffer,
  566                 offset));
  567         else if (dispatch == 'write')
  568             return (this.writeStruct(value, this.types[type],
  569                 buffer, offset));
  570         else
  571             throw (new Error('invalid dispatch type to ' +
  572                 'resolveTypedef'));
  573     }
  574 
  575 };
  576 
  577 /*
  578  * [private] Try and read in the specific entry.
  579  */
  580 CTypeParser.prototype.readEntry = function (type, buffer, offset)
  581 {
  582     var parse, len;
  583 
  584     /*
  585      * Because we want to special case char[]s this is unfortunately
  586      * a bit uglier than it really should be. We want to special
  587      * case char[]s so that we return a node buffer, thus they are a
  588      * first class type where as all other arrays just call into a
  589      * generic array routine which calls their data-specific routine
  590      * the specified number of times.
  591      *
  592      * The valid dispatch options we have are:
  593      *  - Array and char => char[] handler
  594      *  - Generic array handler
  595      *  - Generic typedef handler
  596      *  - Basic type handler
  597      */
  598     if (type['len'] !== undefined) {
  599         len = parseInt(type['len'], 10);
  600         if (isNaN(len))
  601             throw (new Error('somehow got a non-numeric length'));
  602 
  603         if (type['type'] == 'char')
  604             parse = this.types['char[]']['read'](len,
  605                 this.endian, buffer, offset);
  606         else
  607             parse = this.readArray(type['type'],
  608                 len, buffer, offset);
  609     } else {
  610         if (type['type'] in deftypes)
  611             parse = this.types[type['type']]['read'](this.endian,
  612                 buffer, offset);
  613         else
  614             parse = this.resolveTypedef(type['type'], 'read',
  615                 buffer, offset);
  616     }
  617 
  618     return (parse);
  619 };
  620 
  621 /*
  622  * [private] Read an array of data
  623  */
  624 CTypeParser.prototype.readArray = function (type, length, buffer, offset)
  625 {
  626     var ii, ent, pt;
  627     var baseOffset = offset;
  628     var ret = new Array(length);
  629     pt = ctParseType(type);
  630 
  631     for (ii = 0; ii < length; ii++) {
  632         ent = this.readEntry(pt, buffer, offset);
  633         offset += ent['size'];
  634         ret[ii] = ent['value'];
  635     }
  636 
  637     return ({ value: ret, size: offset - baseOffset });
  638 };
  639 
  640 /*
  641  * [private] Read a single struct in.
  642  */
  643 CTypeParser.prototype.readStruct = function (def, buffer, offset)
  644 {
  645     var parse, ii, type, entry, key;
  646     var baseOffset = offset;
  647     var ret = {};
  648 
  649     /* Walk it and handle doing what's necessary */
  650     for (ii = 0; ii < def.length; ii++) {
  651         key = Object.keys(def[ii])[0];
  652         entry = def[ii][key];
  653 
  654         /* Resolve all array values */
  655         type = ctParseType(ctResolveArray(entry['type'], ret));
  656 
  657         if ('offset' in entry)
  658             offset = baseOffset + entry['offset'];
  659 
  660         parse = this.readEntry(type, buffer, offset);
  661 
  662         offset += parse['size'];
  663         ret[key] = parse['value'];
  664     }
  665 
  666     return ({ value: ret, size: (offset-baseOffset)});
  667 };
  668 
  669 /*
  670  * This is what we were born to do. We read the data from a buffer and return it
  671  * in an object whose keys match the values from the object.
  672  *
  673  *  def     The array definition of the data to read in
  674  *
  675  *  buffer      The buffer to read data from
  676  *
  677  *  offset      The offset to start writing to
  678  *
  679  * Returns an object where each key corresponds to an entry in def and the value
  680  * is the read value.
  681  */
  682 CTypeParser.prototype.readData = function (def, buffer, offset)
  683 {
  684     /* Sanity check for arguments */
  685     if (def === undefined)
  686         throw (new Error('missing definition for what we should be' +
  687             'parsing'));
  688 
  689     if (buffer === undefined)
  690         throw (new Error('missing buffer for what we should be ' +
  691             'parsing'));
  692 
  693     if (offset === undefined)
  694         throw (new Error('missing offset for what we should be ' +
  695             'parsing'));
  696 
  697     /* Sanity check the object definition */
  698     ctCheckReq(def, this.types);
  699 
  700     return (this.readStruct(def, buffer, offset)['value']);
  701 };
  702 
  703 /*
  704  * [private] Write out an array of data
  705  */
  706 CTypeParser.prototype.writeArray = function (value, type, length, buffer,
  707     offset)
  708 {
  709     var ii, pt;
  710     var baseOffset = offset;
  711     if (!(value instanceof Array))
  712         throw (new Error('asked to write an array, but value is not ' +
  713             'an array'));
  714 
  715     if (value.length != length)
  716         throw (new Error('asked to write array of length ' + length +
  717             ' but that does not match value length: ' + value.length));
  718 
  719     pt = ctParseType(type);
  720     for (ii = 0; ii < length; ii++)
  721         offset += this.writeEntry(value[ii], pt, buffer, offset);
  722 
  723     return (offset - baseOffset);
  724 };
  725 
  726 /*
  727  * [private] Write the specific entry
  728  */
  729 CTypeParser.prototype.writeEntry = function (value, type, buffer, offset)
  730 {
  731     var len, ret;
  732 
  733     if (type['len'] !== undefined) {
  734         len = parseInt(type['len'], 10);
  735         if (isNaN(len))
  736             throw (new Error('somehow got a non-numeric length'));
  737 
  738         if (type['type'] == 'char')
  739             ret = this.types['char[]']['write'](value, len,
  740                 this.endian, buffer, offset);
  741         else
  742             ret = this.writeArray(value, type['type'],
  743                 len, buffer, offset);
  744     } else {
  745         if (type['type'] in deftypes)
  746             ret = this.types[type['type']]['write'](value,
  747                 this.endian, buffer, offset);
  748         else
  749             ret = this.resolveTypedef(type['type'], 'write',
  750                 buffer, offset, value);
  751     }
  752 
  753     return (ret);
  754 };
  755 
  756 /*
  757  * [private] Write a single struct out.
  758  */
  759 CTypeParser.prototype.writeStruct = function (value, def, buffer, offset)
  760 {
  761     var ii, entry, type, key;
  762     var baseOffset = offset;
  763     var vals = {};
  764 
  765     for (ii = 0; ii < def.length; ii++) {
  766         key = Object.keys(def[ii])[0];
  767         entry = def[ii][key];
  768 
  769         type = ctParseType(ctResolveArray(entry['type'], vals));
  770 
  771         if ('offset' in entry)
  772             offset = baseOffset + entry['offset'];
  773 
  774         offset += this.writeEntry(value[ii], type, buffer, offset);
  775         /* Now that we've written it out, we can use it for arrays */
  776         vals[key] = value[ii];
  777     }
  778 
  779     return (offset);
  780 };
  781 
  782 /*
  783  * Unfortunately, we're stuck with the sins of an initial poor design. Because
  784  * of that, we are going to have to support the old way of writing data via
  785  * writeData. There we insert the values that you want to write into the
  786  * definition. A little baroque. Internally, we use the new model. So we need to
  787  * just get those values out of there. But to maintain the principle of least
  788  * surprise, we're not going to modify the input data.
  789  */
  790 function getValues(def)
  791 {
  792     var ii, out, key;
  793     out = [];
  794     for (ii = 0; ii < def.length; ii++) {
  795         key = Object.keys(def[ii])[0];
  796         mod_assert.ok('value' in def[ii][key]);
  797         out.push(def[ii][key]['value']);
  798     }
  799 
  800     return (out);
  801 }
  802 
  803 /*
  804  * This is the second half of what we were born to do, write out the data
  805  * itself. Historically this function required you to put your values in the
  806  * definition section. This was not the smartest thing to do and a bit of an
  807  * oversight to be honest. As such, this function now takes a values argument.
  808  * If values is non-null and non-undefined, it will be used to determine the
  809  * values. This means that the old method is still supported, but is no longer
  810  * acceptable.
  811  *
  812  *  def     The array definition of the data to write out with
  813  *          values
  814  *
  815  *  buffer      The buffer to write to
  816  *
  817  *  offset      The offset in the buffer to write to
  818  *
  819  *  values      An array of values to write.
  820  */
  821 CTypeParser.prototype.writeData = function (def, buffer, offset, values)
  822 {
  823     var hv;
  824 
  825     if (def === undefined)
  826         throw (new Error('missing definition for what we should be' +
  827             'parsing'));
  828 
  829     if (buffer === undefined)
  830         throw (new Error('missing buffer for what we should be ' +
  831             'parsing'));
  832 
  833     if (offset === undefined)
  834         throw (new Error('missing offset for what we should be ' +
  835             'parsing'));
  836 
  837     hv = (values != null && values != undefined);
  838     if (hv) {
  839         if (!Array.isArray(values))
  840             throw (new Error('missing values for writing'));
  841         ctCheckReq(def, this.types);
  842     } else {
  843         ctCheckReq(def, this.types, [ 'value' ]);
  844     }
  845 
  846     this.writeStruct(hv ? values : getValues(def), def, buffer, offset);
  847 };
  848 
  849 /*
  850  * Functions to go to and from 64 bit numbers in a way that is compatible with
  851  * Javascript limitations. There are two sets. One where the user is okay with
  852  * an approximation and one where they are definitely not okay with an
  853  * approximation.
  854  */
  855 
  856 /*
  857  * Attempts to convert an array of two integers returned from rsint64 / ruint64
  858  * into an absolute 64 bit number. If however the value would exceed 2^52 this
  859  * will instead throw an error. The mantissa in a double is a 52 bit number and
  860  * rather than potentially give you a value that is an approximation this will
  861  * error. If you would rather an approximation, please see toApprox64.
  862  *
  863  *  val     An array of two 32-bit integers
  864  */
  865 function toAbs64(val)
  866 {
  867     if (val === undefined)
  868         throw (new Error('missing required arg: value'));
  869 
  870     if (!Array.isArray(val))
  871         throw (new Error('value must be an array'));
  872 
  873     if (val.length != 2)
  874         throw (new Error('value must be an array of length 2'));
  875 
  876     /* We have 20 bits worth of precision in this range */
  877     if (val[0] >= 0x100000)
  878         throw (new Error('value would become approximated'));
  879 
  880     return (val[0] * Math.pow(2, 32) + val[1]);
  881 }
  882 
  883 /*
  884  * Will return the 64 bit value as returned in an array from rsint64 / ruint64
  885  * to a value as close as it can. Note that Javascript stores all numbers as a
  886  * double and the mantissa only has 52 bits. Thus this version may approximate
  887  * the value.
  888  *
  889  *  val     An array of two 32-bit integers
  890  */
  891 function toApprox64(val)
  892 {
  893     if (val === undefined)
  894         throw (new Error('missing required arg: value'));
  895 
  896     if (!Array.isArray(val))
  897         throw (new Error('value must be an array'));
  898 
  899     if (val.length != 2)
  900         throw (new Error('value must be an array of length 2'));
  901 
  902     return (Math.pow(2, 32) * val[0] + val[1]);
  903 }
  904 
  905 function parseCTF(json, conf)
  906 {
  907     var ctype = new CTypeParser(conf);
  908     mod_ctf.ctfParseJson(json, ctype);
  909 
  910     return (ctype);
  911 }
  912 
  913 /*
  914  * Export the few things we actually want to. Currently this is just the CType
  915  * Parser and ctio.
  916  */
  917 exports.Parser = CTypeParser;
  918 exports.toAbs64 = toAbs64;
  919 exports.toApprox64 = toApprox64;
  920 
  921 exports.parseCTF = parseCTF;
  922 
  923 exports.ruint8 = mod_ctio.ruint8;
  924 exports.ruint16 = mod_ctio.ruint16;
  925 exports.ruint32 = mod_ctio.ruint32;
  926 exports.ruint64 = mod_ctio.ruint64;
  927 exports.wuint8 = mod_ctio.wuint8;
  928 exports.wuint16 = mod_ctio.wuint16;
  929 exports.wuint32 = mod_ctio.wuint32;
  930 exports.wuint64 = mod_ctio.wuint64;
  931 
  932 exports.rsint8 = mod_ctio.rsint8;
  933 exports.rsint16 = mod_ctio.rsint16;
  934 exports.rsint32 = mod_ctio.rsint32;
  935 exports.rsint64 = mod_ctio.rsint64;
  936 exports.wsint8 = mod_ctio.wsint8;
  937 exports.wsint16 = mod_ctio.wsint16;
  938 exports.wsint32 = mod_ctio.wsint32;
  939 exports.wsint64 = mod_ctio.wsint64;
  940 
  941 exports.rfloat = mod_ctio.rfloat;
  942 exports.rdouble = mod_ctio.rdouble;
  943 exports.wfloat = mod_ctio.wfloat;
  944 exports.wdouble = mod_ctio.wdouble;