interp.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. // -*- js-indent: 2 -*-
  2. // Interpreter for bit syntax AST.
  3. // Grammar:
  4. //
  5. // pattern := segment ("," segment)*
  6. // segment := (value | var) (":" size)? ("/" specifier ("-" specifier)*)? | string
  7. // var := "_" | identifier
  8. // size := integer | var
  9. // specifier := "little" | "big" | "signed" | "unsigned" | "unit" ":" 0..256 | type
  10. // type := "integer" | "binary" | "float"
  11. //
  12. // where integer has the obvious meaning, and identifier is anything
  13. // other than "_" that fits the JavaScript identifier specification.
  14. //
  15. // We'll use an object to represent each segment, and an array of
  16. // segments for a pattern. We won't try to optimise for groups of
  17. // patterns; we'll just step through each to see if it works. We rely
  18. // a hypothetical prior step to check that it's a valid pattern.
  19. // ? compile to intermediate instructions ?
  20. // A segment looks like
  21. // {
  22. // type: string, // 'string' is special case
  23. // size: integer | true, // true means 'all remaining'
  24. // name: string | null, // (may be '_')
  25. // value: value | null, // either name OR value
  26. // unit: integer,
  27. // signed: boolean,
  28. // bigendian: boolean
  29. // }
  30. 'use strict';
  31. var ints = require('buffer-more-ints'),
  32. debug = require('debug')('bitsyntax-Interpreter');
  33. function parse_int(bin, off, sizeInBytes, bigendian, signed) {
  34. switch (sizeInBytes) {
  35. case 1:
  36. return (signed) ? bin.readInt8(off) : bin.readUInt8(off);
  37. case 2:
  38. return (bigendian) ?
  39. (signed) ? bin.readInt16BE(off) : bin.readUInt16BE(off) :
  40. (signed) ? bin.readInt16LE(off) : bin.readUInt16LE(off);
  41. case 4:
  42. return (bigendian) ?
  43. (signed) ? bin.readInt32BE(off) : bin.readUInt32BE(off) :
  44. (signed) ? bin.readInt32LE(off) : bin.readUInt32LE(off);
  45. case 8:
  46. return (bigendian) ?
  47. ((signed) ? ints.readInt64BE : ints.readUInt64BE)(bin, off) :
  48. ((signed) ? ints.readInt64LE : ints.readUInt64LE)(bin, off);
  49. default:
  50. throw "Integers must be 8-, 16-, 32- or 64-bit";
  51. }
  52. }
  53. function parse_float(bin, off, sizeInBytes, bigendian) {
  54. switch (sizeInBytes) {
  55. case 4:
  56. return (bigendian) ? bin.readFloatBE(off) : bin.readFloatLE(off);
  57. case 8:
  58. return (bigendian) ? bin.readDoubleBE(off) : bin.readDoubleLE(off);
  59. default:
  60. throw "Floats must be 32- or 64-bit";
  61. }
  62. }
  63. function size_of(segment, bound) {
  64. var size = segment.size;
  65. if (typeof size === 'string') {
  66. return bound[size];
  67. }
  68. else {
  69. return size;
  70. }
  71. }
  72. function new_scope(env) {
  73. function scope() {};
  74. scope.prototype = env;
  75. return new scope();
  76. }
  77. function bindings(scope) {
  78. var s = {};
  79. for (var k in scope) {
  80. if (scope.hasOwnProperty(k)) {
  81. s[k] = scope[k];
  82. }
  83. }
  84. return s;
  85. }
  86. function match(pattern, binary, boundvars) {
  87. var offset = 0, vars = new_scope(boundvars);
  88. var binsize = binary.length * 8;
  89. function skip_bits(segment) {
  90. debug("skip bits"); debug(segment);
  91. var size = size_of(segment, vars);
  92. if (size === true) {
  93. if (offset % 8 === 0) {
  94. offset = binsize;
  95. return true;
  96. }
  97. else {
  98. return false;
  99. }
  100. }
  101. var bits = segment.unit * size;
  102. if (offset + bits > binsize) {
  103. return false;
  104. }
  105. else {
  106. offset += bits;
  107. }
  108. }
  109. function get_integer(segment) {
  110. debug("get_integer"); debug(segment);
  111. // let's do only multiples of eight bits for now
  112. var unit = segment.unit, size = size_of(segment, vars);
  113. var bitsize = size * unit;
  114. var byteoffset = offset / 8; // NB assumes aligned
  115. offset += bitsize;
  116. if (bitsize % 8 > 0 || (offset > binsize)) {
  117. return false;
  118. }
  119. else {
  120. return parse_int(binary, byteoffset, bitsize / 8,
  121. segment.bigendian, segment.signed);
  122. }
  123. }
  124. function get_float(segment) {
  125. debug("get_float"); debug(segment);
  126. var unit = segment.unit; var size = size_of(segment, vars);
  127. var bitsize = size * unit;
  128. var byteoffset = offset / 8; // assume aligned
  129. offset += bitsize;
  130. if (offset > binsize) {
  131. return false;
  132. }
  133. else {
  134. return parse_float(binary, byteoffset,
  135. bitsize / 8, segment.bigendian);
  136. }
  137. }
  138. function get_binary(segment) {
  139. debug("get_binary"); debug(segment);
  140. var unit = segment.unit, size = size_of(segment, vars);
  141. var byteoffset = offset / 8; // NB alignment
  142. if (size === true) {
  143. offset = binsize;
  144. return binary.slice(byteoffset);
  145. }
  146. else {
  147. var bitsize = size * unit;
  148. if (bitsize % 8 > 0 || (offset + bitsize) > binsize) {
  149. return false;
  150. }
  151. else {
  152. offset += bitsize;
  153. return binary.slice(byteoffset, byteoffset + bitsize / 8);
  154. }
  155. }
  156. }
  157. function get_string(segment) {
  158. debug("get_string"); debug(segment);
  159. var len = segment.value.length;
  160. var byteoffset = offset / 8;
  161. offset += len * 8;
  162. if (offset > binsize) {
  163. return false;
  164. }
  165. // FIXME bytes vs UTF8 characters
  166. return binary.slice(byteoffset, byteoffset + len).toString('utf8');
  167. }
  168. var patternlen = pattern.length;
  169. for (var i = 0; i < patternlen; i++) {
  170. var segment = pattern[i];
  171. var result = false;
  172. if (segment.name === '_') {
  173. result = skip_bits(segment);
  174. }
  175. else {
  176. switch (segment.type) {
  177. case 'string':
  178. result = get_string(segment);
  179. break;
  180. case 'integer':
  181. result = get_integer(segment);
  182. break;
  183. case 'float':
  184. result = get_float(segment);
  185. break;
  186. case 'binary':
  187. result = get_binary(segment);
  188. break;
  189. }
  190. if (result === false) {
  191. return false;
  192. }
  193. else if (segment.name) {
  194. vars[segment.name] = result;
  195. }
  196. else if (segment.value != result) {
  197. return false;
  198. }
  199. }
  200. }
  201. if (offset == binsize) {
  202. return bindings(vars);
  203. }
  204. else {
  205. return false;
  206. }
  207. }
  208. module.exports.match = match;
  209. module.exports.parse_int = parse_int;
  210. module.exports.parse_float = parse_float;