generate-defs.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  1. var FS = require('fs');
  2. var format = require('util').format;
  3. var defs = require('./amqp-rabbitmq-0.9.1.json');
  4. var FRAME_OVERHEAD = 8; // type + channel + size + frame-end
  5. var METHOD_OVERHEAD = FRAME_OVERHEAD + 4;
  6. // F_O + classId + methodId
  7. var PROPERTIES_OVERHEAD = FRAME_OVERHEAD + 4 + 8 + 2;
  8. // F_O + classId + weight + content size + flags
  9. var out = process.stdout;
  10. function printf() {
  11. out.write(format.apply(format, arguments), 'utf8');
  12. }
  13. function nl() { out.write('\n'); }
  14. function println() { printf.apply(printf, arguments); nl(); }
  15. function isEmptyObject(val) {
  16. return (val != null && typeof val === 'object' &&
  17. Object.keys(val).length === 0);
  18. }
  19. function stringifyValue(val) {
  20. return (isEmptyObject(val)) ? 'EMPTY_OBJECT' :
  21. JSON.stringify(val);
  22. }
  23. var constants = {};
  24. var constant_strs = {};
  25. for (var i = 0, len = defs.constants.length; i < len; i++) {
  26. var cdef = defs.constants[i];
  27. constants[constantName(cdef)] = cdef.value;
  28. constant_strs[cdef.value] = cdef.name;
  29. }
  30. function constantName(def) {
  31. return def.name.replace(/-/g, '_');
  32. }
  33. function methodName(clazz, method) {
  34. return initial(clazz.name) + method.name.split('-').map(initial).join('');
  35. }
  36. function propertyName(dashed) {
  37. var parts = dashed.split('-');
  38. return parts[0] + parts.slice(1).map(initial).join('');
  39. }
  40. function initial(part) {
  41. return part.charAt(0).toUpperCase() + part.substr(1);
  42. }
  43. function argument(a) {
  44. var type = a.type || domains[a.domain];
  45. var friendlyName = propertyName(a.name);
  46. return {type: type, name: friendlyName, default: a['default-value']};
  47. }
  48. var domains = {};
  49. for (var i=0, len = defs.domains.length; i < len; i++) {
  50. var dom = defs.domains[i];
  51. domains[dom[0]] = dom[1];
  52. }
  53. var methods = {};
  54. var propertieses = {};
  55. for (var i = 0, len = defs.classes.length; i < len; i++) {
  56. var clazz = defs.classes[i];
  57. for (var j = 0, num = clazz.methods.length; j < num; j++) {
  58. var method = clazz.methods[j];
  59. var name = methodName(clazz, method);
  60. var info = 'methodInfo' + name;
  61. methods[name] = {
  62. id: methodId(clazz, method),
  63. name: name,
  64. methodId: method.id,
  65. clazzId: clazz.id,
  66. clazz: clazz.name,
  67. args: method['arguments'].map(argument),
  68. isReply: method.answer,
  69. encoder: 'encode' + name,
  70. decoder: 'decode' + name,
  71. info: info
  72. };
  73. }
  74. if (clazz.properties && clazz.properties.length > 0) {
  75. var name = propertiesName(clazz);
  76. var props = clazz.properties;
  77. propertieses[name] = {
  78. id: clazz.id,
  79. name: name,
  80. encoder: 'encode' + name,
  81. decoder: 'decode' + name,
  82. info: 'propertiesInfo' + name,
  83. args: props.map(argument),
  84. };
  85. }
  86. }
  87. // OK let's get emitting
  88. println(
  89. '/** @preserve This file is generated by the script\n',
  90. '* ../bin/generate-defs.js, which is not in general included in a\n',
  91. '* distribution, but is available in the source repository e.g. at\n',
  92. '* https://github.com/squaremo/amqp.node/\n',
  93. '*/');
  94. println("'use strict';"); nl();
  95. nl()
  96. println('var codec = require("./codec");');
  97. println('var ints = require("buffer-more-ints");');
  98. println('var encodeTable = codec.encodeTable;');
  99. println('var decodeFields = codec.decodeFields;');
  100. nl();
  101. println('var SCRATCH = Buffer.alloc(65536);');
  102. println('var EMPTY_OBJECT = Object.freeze({});');
  103. println('module.exports.constants = %s',
  104. JSON.stringify(constants));
  105. nl();
  106. println('module.exports.constant_strs = %s',
  107. JSON.stringify(constant_strs));
  108. nl();
  109. println('module.exports.FRAME_OVERHEAD = %d;', FRAME_OVERHEAD);
  110. nl();
  111. println('module.exports.decode = function(id, buf) {');
  112. println('switch (id) {');
  113. for (var m in methods) {
  114. var method = methods[m];
  115. println('case %d: return %s(buf);', method.id, method.decoder);
  116. }
  117. for (var p in propertieses) {
  118. var props = propertieses[p];
  119. println('case %d: return %s(buf);', props.id, props.decoder);
  120. }
  121. println('default: throw new Error("Unknown class/method ID");');
  122. println('}}'); nl();
  123. println('module.exports.encodeMethod =',
  124. 'function(id, channel, fields) {');
  125. println('switch (id) {');
  126. for (var m in methods) {
  127. var method = methods[m];
  128. println('case %d: return %s(channel, fields);',
  129. method.id, method.encoder);
  130. }
  131. println('default: throw new Error("Unknown class/method ID");');
  132. println('}}'); nl();
  133. println('module.exports.encodeProperties ='
  134. , 'function(id, channel, size, fields) {');
  135. println('switch (id) {');
  136. for (var p in propertieses) {
  137. var props = propertieses[p];
  138. println('case %d: return %s(channel, size, fields);',
  139. props.id, props.encoder);
  140. }
  141. println('default: throw new Error("Unknown class/properties ID");');
  142. println('}}'); nl();
  143. println('module.exports.info = function(id) {');
  144. println('switch(id) {');
  145. for (var m in methods) {
  146. var method = methods[m];
  147. println('case %d: return %s; ', method.id, method.info);
  148. }
  149. for (var p in propertieses) {
  150. var properties = propertieses[p];
  151. println('case %d: return %s', properties.id, properties.info);
  152. }
  153. println('default: throw new Error("Unknown class/method ID");');
  154. println('}}'); nl();
  155. for (var m in methods) {
  156. var method = methods[m];
  157. println('module.exports.%s = %d;', m, method.id);
  158. decoderFn(method); nl();
  159. encoderFn(method); nl();
  160. infoObj(method); nl();
  161. }
  162. for (var p in propertieses) {
  163. var properties = propertieses[p];
  164. println('module.exports.%s = %d;', p, properties.id);
  165. encodePropsFn(properties); nl();
  166. decodePropsFn(properties); nl();
  167. infoObj(properties); nl();
  168. }
  169. function methodId(clazz, method) {
  170. return (clazz.id << 16) + method.id;
  171. }
  172. function propertiesName(clazz) {
  173. return initial(clazz.name) + 'Properties';
  174. }
  175. function valTypeTest(arg) {
  176. switch (arg.type) {
  177. // everything is booleany
  178. case 'bit': return 'true'
  179. case 'octet':
  180. case 'short':
  181. case 'long':
  182. case 'longlong':
  183. case 'timestamp': return "typeof val === 'number' && !isNaN(val)";
  184. case 'shortstr': return "typeof val === 'string' &&" +
  185. " Buffer.byteLength(val) < 256";
  186. case 'longstr': return "Buffer.isBuffer(val)";
  187. case 'table': return "typeof val === 'object'";
  188. }
  189. }
  190. function typeDesc(t) {
  191. switch (t) {
  192. case 'bit': return 'booleany';
  193. case 'octet':
  194. case 'short':
  195. case 'long':
  196. case 'longlong':
  197. case 'timestamp': return "a number (but not NaN)";
  198. case 'shortstr': return "a string (up to 255 chars)";
  199. case 'longstr': return "a Buffer";
  200. case 'table': return "an object";
  201. }
  202. }
  203. function defaultValueRepr(arg) {
  204. switch (arg.type) {
  205. case 'longstr':
  206. return format("Buffer.from(%s)", JSON.stringify(arg.default));
  207. default:
  208. // assumes no tables as defaults
  209. return JSON.stringify(arg.default);
  210. }
  211. }
  212. // Emit code to assign the arg value to `val`.
  213. function assignArg(a) {
  214. println("val = fields['%s'];", a.name);
  215. }
  216. function assignOrDefault(a) {
  217. println("val = fields['%s'];", a.name);
  218. println("if (val === undefined) val = %s;", defaultValueRepr(a));
  219. }
  220. // Emit code for assigning an argument value to `val`, checking that
  221. // it exists (if it does not have a default) and is the correct
  222. // type.
  223. function checkAssignArg(a) {
  224. assignArg(a);
  225. println('if (val === undefined) {');
  226. if (a.default !== undefined) {
  227. println('val = %s;', defaultValueRepr(a));
  228. }
  229. else {
  230. println('throw new Error("Missing value for mandatory field \'%s\'");', a.name);
  231. }
  232. println('}'); // undefined test
  233. println('else if (!(%s)) {', valTypeTest(a));
  234. println('throw new TypeError(');
  235. println('"Field \'%s\' is the wrong type; must be %s");',
  236. a.name, typeDesc(a.type));
  237. println('}'); // type test
  238. }
  239. // Emit code for encoding `val` as a table and assign to a fresh
  240. // variable (based on the arg name). I use a scratch buffer to compose
  241. // the encoded table, otherwise I'd have to do a size calculation pass
  242. // first. I can get away with this only because 1. the encoding
  243. // procedures are not re-entrant; and, 2. I copy the result into
  244. // another buffer before returning. `scratchOffset`, `val`, `len` are
  245. // expected to have been declared.
  246. function assignTable(a) {
  247. var varname = tableVar(a);
  248. println(
  249. "len = encodeTable(SCRATCH, val, scratchOffset);");
  250. println('var %s = SCRATCH.slice(scratchOffset, scratchOffset + len);', varname);
  251. println('scratchOffset += len;');
  252. }
  253. function tableVar(a) {
  254. return a.name + '_encoded';
  255. }
  256. function stringLenVar(a) {
  257. return a.name + '_len';
  258. }
  259. function assignStringLen(a) {
  260. var v = stringLenVar(a);
  261. // Assumes the value or default is in val
  262. println("var %s = Buffer.byteLength(val, 'utf8');", v);
  263. }
  264. function encoderFn(method) {
  265. var args = method['args'];
  266. println('function %s(channel, fields) {', method.encoder);
  267. println('var offset = 0, val = null, bits = 0, varyingSize = 0;');
  268. println('var len, scratchOffset = 0;');
  269. // Encoding is split into two parts. Some fields have a fixed size
  270. // (e.g., integers of a specific width), while some have a size that
  271. // depends on the datum (e.g., strings). Each field will therefore
  272. // either 1. contribute to the fixed size; or 2. emit code to
  273. // calculate the size (and possibly the encoded value, in the case
  274. // of tables).
  275. var fixedSize = METHOD_OVERHEAD;
  276. var bitsInARow = 0;
  277. for (var i=0, len = args.length; i < len; i++) {
  278. var arg = args[i];
  279. if (arg.type != 'bit') bitsInARow = 0;
  280. switch (arg.type) {
  281. // varying size
  282. case 'shortstr':
  283. checkAssignArg(arg);
  284. assignStringLen(arg);
  285. println("varyingSize += %s;", stringLenVar(arg));
  286. fixedSize += 1;
  287. break;
  288. case 'longstr':
  289. checkAssignArg(arg);
  290. println("varyingSize += val.length;");
  291. fixedSize += 4;
  292. break;
  293. case 'table':
  294. // For a table we have to encode the table before we can see its
  295. // length.
  296. checkAssignArg(arg);
  297. assignTable(arg);
  298. println('varyingSize += %s.length;', tableVar(arg));
  299. break;
  300. // fixed size
  301. case 'octet': fixedSize += 1; break;
  302. case 'short': fixedSize += 2; break;
  303. case 'long': fixedSize += 4; break;
  304. case 'longlong': //fall through
  305. case 'timestamp':
  306. fixedSize += 8; break;
  307. case 'bit':
  308. bitsInARow ++;
  309. // open a fresh pack o' bits
  310. if (bitsInARow === 1) fixedSize += 1;
  311. // just used a pack; reset
  312. else if (bitsInARow === 8) bitsInARow = 0;
  313. break;
  314. }
  315. }
  316. println('var buffer = Buffer.alloc(%d + varyingSize);', fixedSize);
  317. println('buffer[0] = %d;', constants.FRAME_METHOD);
  318. println('buffer.writeUInt16BE(channel, 1);');
  319. // skip size for now, we'll write it in when we know
  320. println('buffer.writeUInt32BE(%d, 7);', method.id);
  321. println('offset = 11;');
  322. bitsInARow = 0;
  323. for (var i = 0, len = args.length; i < len; i++) {
  324. var a = args[i];
  325. // Flush any collected bits before doing a new field
  326. if (a.type != 'bit' && bitsInARow > 0) {
  327. bitsInARow = 0;
  328. println('buffer[offset] = bits; offset++; bits = 0;');
  329. }
  330. switch (a.type) {
  331. case 'octet':
  332. checkAssignArg(a);
  333. println('buffer.writeUInt8(val, offset); offset++;');
  334. break;
  335. case 'short':
  336. checkAssignArg(a);
  337. println('buffer.writeUInt16BE(val, offset); offset += 2;');
  338. break;
  339. case 'long':
  340. checkAssignArg(a);
  341. println('buffer.writeUInt32BE(val, offset); offset += 4;');
  342. break;
  343. case 'longlong':
  344. case 'timestamp':
  345. checkAssignArg(a);
  346. println('ints.writeUInt64BE(buffer, val, offset); offset += 8;');
  347. break;
  348. case 'bit':
  349. checkAssignArg(a);
  350. println('if (val) bits += %d;', 1 << bitsInARow);
  351. if (bitsInARow === 7) { // I don't think this ever happens, but whatever
  352. println('buffer[offset] = bits; offset++; bits = 0;');
  353. bitsInARow = 0;
  354. }
  355. else bitsInARow++;
  356. break;
  357. case 'shortstr':
  358. assignOrDefault(a);
  359. println('buffer[offset] = %s; offset++;', stringLenVar(a));
  360. println('buffer.write(val, offset, "utf8"); offset += %s;',
  361. stringLenVar(a));
  362. break;
  363. case 'longstr':
  364. assignOrDefault(a);
  365. println('len = val.length;');
  366. println('buffer.writeUInt32BE(len, offset); offset += 4;');
  367. println('val.copy(buffer, offset); offset += len;');
  368. break;
  369. case 'table':
  370. println('offset += %s.copy(buffer, offset);', tableVar(a));
  371. break;
  372. default: throw new Error("Unexpected argument type: " + a.type);
  373. }
  374. }
  375. // Flush any collected bits at the end
  376. if (bitsInARow > 0) {
  377. println('buffer[offset] = bits; offset++;');
  378. }
  379. println('buffer[offset] = %d;', constants.FRAME_END);
  380. // size does not include the frame header or frame end byte
  381. println('buffer.writeUInt32BE(offset - 7, 3);');
  382. println('return buffer;');
  383. println('}');
  384. }
  385. function fieldsDecl(args) {
  386. println('var fields = {');
  387. for (var i=0, num=args.length; i < num; i++) {
  388. println('%s: undefined,', args[i].name);
  389. }
  390. println('};');
  391. }
  392. function decoderFn(method) {
  393. var args = method.args;
  394. println('function %s(buffer) {', method.decoder);
  395. println('var offset = 0, val, len;');
  396. fieldsDecl(args);
  397. var bitsInARow = 0;
  398. for (var i=0, num=args.length; i < num; i++) {
  399. var a = args[i];
  400. var field = "fields['" + a.name + "']";
  401. // Flush any collected bits before doing a new field
  402. if (a.type != 'bit' && bitsInARow > 0) {
  403. bitsInARow = 0;
  404. println('offset++;');
  405. }
  406. switch (a.type) {
  407. case 'octet':
  408. println('val = buffer[offset]; offset++;');
  409. break;
  410. case 'short':
  411. println('val = buffer.readUInt16BE(offset); offset += 2;');
  412. break;
  413. case 'long':
  414. println('val = buffer.readUInt32BE(offset); offset += 4;');
  415. break;
  416. case 'longlong':
  417. case 'timestamp':
  418. println('val = ints.readUInt64BE(buffer, offset); offset += 8;');
  419. break;
  420. case 'bit':
  421. var bit = 1 << bitsInARow;
  422. println('val = !!(buffer[offset] & %d);', bit);
  423. if (bitsInARow === 7) {
  424. println('offset++;');
  425. bitsInARow = 0;
  426. }
  427. else bitsInARow++;
  428. break;
  429. case 'longstr':
  430. println('len = buffer.readUInt32BE(offset); offset += 4;');
  431. println('val = buffer.subarray(offset, offset + len);');
  432. println('offset += len;');
  433. break;
  434. case 'shortstr':
  435. println('len = buffer.readUInt8(offset); offset++;');
  436. println('val = buffer.toString("utf8", offset, offset + len);');
  437. println('offset += len;');
  438. break;
  439. case 'table':
  440. println('len = buffer.readUInt32BE(offset); offset += 4;');
  441. println('val = decodeFields(buffer.subarray(offset, offset + len));');
  442. println('offset += len;');
  443. break;
  444. default:
  445. throw new TypeError("Unexpected type in argument list: " + a.type);
  446. }
  447. println('%s = val;', field);
  448. }
  449. println('return fields;');
  450. println('}');
  451. }
  452. function infoObj(thing) {
  453. var info = JSON.stringify({id: thing.id,
  454. classId: thing.clazzId,
  455. methodId: thing.methodId,
  456. name: thing.name,
  457. args: thing.args});
  458. println('var %s = module.exports.%s = %s;',
  459. thing.info, thing.info, info);
  460. }
  461. // The flags are laid out in groups of fifteen in a short (high to
  462. // low bits), with a continuation bit (at 0) and another group
  463. // following if there's more than fifteen. Presence and absence
  464. // are conflated with true and false, for bit fields (i.e., if the
  465. // flag for the field is set, it's true, otherwise false).
  466. //
  467. // However, none of that is actually used in AMQP 0-9-1. The only
  468. // instance of properties -- basic properties -- has 14 fields, none
  469. // of them bits.
  470. function flagAt(index) {
  471. return 1 << (15 - index);
  472. }
  473. function encodePropsFn(props) {
  474. println('function %s(channel, size, fields) {', props.encoder);
  475. println('var offset = 0, flags = 0, val, len;');
  476. println('var scratchOffset = 0, varyingSize = 0;');
  477. var fixedSize = PROPERTIES_OVERHEAD;
  478. var args = props.args;
  479. function incVarying(by) {
  480. println("varyingSize += %d;", by);
  481. }
  482. for (var i=0, num=args.length; i < num; i++) {
  483. var p = args[i];
  484. assignArg(p);
  485. println("if (val != undefined) {");
  486. println("if (%s) {", valTypeTest(p));
  487. switch (p.type) {
  488. case 'shortstr':
  489. assignStringLen(p);
  490. incVarying(1);
  491. println('varyingSize += %s;', stringLenVar(p));
  492. break;
  493. case 'longstr':
  494. incVarying(4);
  495. println('varyingSize += val.length;');
  496. break;
  497. case 'table':
  498. assignTable(p);
  499. println('varyingSize += %s.length;', tableVar(p));
  500. break;
  501. case 'octet': incVarying(1); break;
  502. case 'short': incVarying(2); break;
  503. case 'long': incVarying(4); break;
  504. case 'longlong': // fall through
  505. case 'timestamp':
  506. incVarying(8); break;
  507. // no case for bit, as they are accounted for in the flags
  508. }
  509. println('} else {');
  510. println('throw new TypeError(');
  511. println('"Field \'%s\' is the wrong type; must be %s");',
  512. p.name, typeDesc(p.type));
  513. println('}');
  514. println('}');
  515. }
  516. println('var buffer = Buffer.alloc(%d + varyingSize);', fixedSize);
  517. println('buffer[0] = %d', constants.FRAME_HEADER);
  518. println('buffer.writeUInt16BE(channel, 1);');
  519. // content class ID and 'weight' (== 0)
  520. println('buffer.writeUInt32BE(%d, 7);', props.id << 16);
  521. // skip frame size for now, we'll write it in when we know.
  522. // body size
  523. println('ints.writeUInt64BE(buffer, size, 11);');
  524. println('flags = 0;');
  525. // we'll write the flags later too
  526. println('offset = 21;');
  527. for (var i=0, num=args.length; i < num; i++) {
  528. var p = args[i];
  529. var flag = flagAt(i);
  530. assignArg(p);
  531. println("if (val != undefined) {");
  532. if (p.type === 'bit') { // which none of them are ..
  533. println('if (val) flags += %d;', flag);
  534. }
  535. else {
  536. println('flags += %d;', flag);
  537. // %%% FIXME only slightly different to the method args encoding
  538. switch (p.type) {
  539. case 'octet':
  540. println('buffer.writeUInt8(val, offset); offset++;');
  541. break;
  542. case 'short':
  543. println('buffer.writeUInt16BE(val, offset); offset += 2;');
  544. break;
  545. case 'long':
  546. println('buffer.writeUInt32BE(val, offset); offset += 4;');
  547. break;
  548. case 'longlong':
  549. case 'timestamp':
  550. println('ints.writeUInt64BE(buffer, val, offset);');
  551. println('offset += 8;');
  552. break;
  553. case 'shortstr':
  554. var v = stringLenVar(p);
  555. println('buffer[offset] = %s; offset++;', v);
  556. println("buffer.write(val, offset, 'utf8');");
  557. println("offset += %s;", v);
  558. break;
  559. case 'longstr':
  560. println('buffer.writeUInt32BE(val.length, offset);');
  561. println('offset += 4;');
  562. println('offset += val.copy(buffer, offset);');
  563. break;
  564. case 'table':
  565. println('offset += %s.copy(buffer, offset);', tableVar(p));
  566. break;
  567. default: throw new Error("Unexpected argument type: " + p.type);
  568. }
  569. }
  570. println('}'); // != undefined
  571. }
  572. println('buffer[offset] = %d;', constants.FRAME_END);
  573. // size does not include the frame header or frame end byte
  574. println('buffer.writeUInt32BE(offset - 7, 3);');
  575. println('buffer.writeUInt16BE(flags, 19);');
  576. println('return buffer.subarray(0, offset + 1);');
  577. println('}');
  578. }
  579. function decodePropsFn(props) {
  580. var args = props.args;
  581. println('function %s(buffer) {', props.decoder);
  582. println('var flags, offset = 2, val, len;');
  583. println('flags = buffer.readUInt16BE(0);');
  584. println('if (flags === 0) return {};');
  585. fieldsDecl(args);
  586. for (var i=0, num=args.length; i < num; i++) {
  587. var p = argument(args[i]);
  588. var field = "fields['" + p.name + "']";
  589. println('if (flags & %d) {', flagAt(i));
  590. if (p.type === 'bit') {
  591. println('%d = true;', field);
  592. }
  593. else {
  594. switch (p.type) {
  595. case 'octet':
  596. println('val = buffer[offset]; offset++;');
  597. break;
  598. case 'short':
  599. println('val = buffer.readUInt16BE(offset); offset += 2;');
  600. break;
  601. case 'long':
  602. println('val = buffer.readUInt32BE(offset); offset += 4;');
  603. break;
  604. case 'longlong':
  605. case 'timestamp':
  606. println('val = ints.readUInt64BE(buffer, offset); offset += 8;');
  607. break;
  608. case 'longstr':
  609. println('len = buffer.readUInt32BE(offset); offset += 4;');
  610. println('val = buffer.subarray(offset, offset + len);');
  611. println('offset += len;');
  612. break;
  613. case 'shortstr':
  614. println('len = buffer.readUInt8(offset); offset++;');
  615. println('val = buffer.toString("utf8", offset, offset + len);');
  616. println('offset += len;');
  617. break;
  618. case 'table':
  619. println('len = buffer.readUInt32BE(offset); offset += 4;');
  620. println('val = decodeFields(buffer.subarray(offset, offset + len));');
  621. println('offset += len;');
  622. break;
  623. default:
  624. throw new TypeError("Unexpected type in argument list: " + p.type);
  625. }
  626. println('%s = val;', field);
  627. }
  628. println('}');
  629. }
  630. println('return fields;');
  631. println('}');
  632. }