constructor.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. // -*- js-indent-level: 2 -*-
  2. // Constructors given patterns
  3. 'use strict';
  4. var ints = require('buffer-more-ints');
  5. var Buffer = require('safe-buffer').Buffer;
  6. // Interpret the pattern, writing values into a buffer
  7. function write(buf, offset, pattern, bindings) {
  8. for (var i=0, len = pattern.length; i < len; i++) {
  9. var segment = pattern[i];
  10. switch (segment.type) {
  11. case 'string':
  12. offset += buf.write(segment.value, offset, 'utf8');
  13. break;
  14. case 'binary':
  15. offset += writeBinary(segment, buf, offset, bindings);
  16. break;
  17. case 'integer':
  18. offset += writeInteger(segment, buf, offset, bindings);
  19. break;
  20. case 'float':
  21. offset += writeFloat(segment, buf, offset, bindings);
  22. break;
  23. }
  24. }
  25. return offset;
  26. }
  27. function build(pattern, bindings) {
  28. var bufsize = size_of(pattern, bindings);
  29. var buf = Buffer.alloc(bufsize);
  30. write(buf, 0, pattern, bindings);
  31. return buf;
  32. }
  33. // In bytes
  34. function size_of_segment(segment, bindings) {
  35. // size refers to a variable
  36. if (typeof segment.size === 'string') {
  37. return (bindings[segment.size] * segment.unit) / 8;
  38. }
  39. if (segment.type === 'string') {
  40. return Buffer.byteLength(segment.value, 'utf8');
  41. }
  42. if (segment.type === 'binary' && segment.size === true) {
  43. var val = bindings[segment.name];
  44. return val.length;
  45. }
  46. return (segment.size * segment.unit) / 8;
  47. }
  48. // size of the to-be-constructed binary, in bytes
  49. function size_of(segments, bindings) {
  50. var size = 0;
  51. for (var i=0, len = segments.length; i < len; i++) {
  52. size += size_of_segment(segments[i], bindings);
  53. }
  54. return size;
  55. }
  56. function writeBinary(segment, buf, offset, bindings) {
  57. var bin = bindings[segment.name];
  58. var size = size_of_segment(segment, bindings);
  59. bin.copy(buf, offset, 0, size);
  60. return size;
  61. }
  62. // TODO in ff might use the noAssert argument to Buffer.write*() but
  63. // need to check that it does the right thing wrt little-endian
  64. function writeInteger(segment, buf, offset, bindings) {
  65. var value = (segment.name) ? bindings[segment.name] : segment.value;
  66. var size = size_of_segment(segment, bindings);
  67. return write_int(buf, value, offset, size, segment.bigendian);
  68. }
  69. function write_int(buf, value, offset, size, bigendian) {
  70. switch (size) {
  71. case 1:
  72. buf.writeUInt8(value, offset);
  73. break;
  74. case 2:
  75. (bigendian) ?
  76. buf.writeUInt16BE(value, offset) :
  77. buf.writeUInt16LE(value, offset);
  78. break;
  79. case 4:
  80. (bigendian) ?
  81. buf.writeUInt32BE(value, offset) :
  82. buf.writeUInt32LE(value, offset);
  83. break;
  84. case 8:
  85. (bigendian) ?
  86. ints.writeUInt64BE(buf, value, offset) :
  87. ints.writeUInt64LE(buf, value, offset);
  88. break;
  89. default:
  90. throw new Error("integer size * unit must be 8, 16, 32 or 64");
  91. }
  92. return size;
  93. }
  94. function writeFloat(segment, buf, offset, bindings) {
  95. var value = (segment.name) ? bindings[segment.name] : segment.value;
  96. var size = size_of_segment(segment, bindings);
  97. return write_float(buf, value, offset, size, segment.bigendian);
  98. }
  99. function write_float(buf, value, offset, size, bigendian) {
  100. if (size === 4) {
  101. (bigendian) ?
  102. buf.writeFloatBE(value, offset) :
  103. buf.writeFloatLE(value, offset);
  104. }
  105. else if (size === 8) {
  106. (bigendian) ?
  107. buf.writeDoubleBE(value, offset) :
  108. buf.writeDoubleLE(value, offset);
  109. }
  110. else {
  111. throw new Error("float size * unit must be 32 or 64");
  112. }
  113. return size;
  114. }
  115. var parse = require('./parse').parse;
  116. module.exports.write = write;
  117. module.exports.build = build;
  118. module.exports.write_int = write_int;
  119. module.exports.write_float = write_float;
  120. module.exports.builder = function(pstr) {
  121. pstr = (arguments.length > 1) ? [].join.call(arguments, ',') : pstr;
  122. var pattern = parse(pstr);
  123. return function(vars) {
  124. return build(pattern, vars);
  125. };
  126. };