| // -*- js-indent-level: 2 -*- |
| |
| // Constructors given patterns |
| |
| 'use strict'; |
| |
| var ints = require('buffer-more-ints'); |
| |
| // Interpret the pattern, writing values into a buffer |
| function write(buf, offset, pattern, bindings) { |
| for (var i=0, len = pattern.length; i < len; i++) { |
| var segment = pattern[i]; |
| |
| switch (segment.type) { |
| case 'string': |
| offset += buf.write(segment.value, offset, 'utf8'); |
| break; |
| case 'binary': |
| offset += writeBinary(segment, buf, offset, bindings); |
| break; |
| case 'integer': |
| offset += writeInteger(segment, buf, offset, bindings); |
| break; |
| case 'float': |
| offset += writeFloat(segment, buf, offset, bindings); |
| break; |
| } |
| } |
| return offset; |
| } |
| |
| function build(pattern, bindings) { |
| var bufsize = size_of(pattern, bindings); |
| var buf = new Buffer(bufsize); |
| write(buf, 0, pattern, bindings); |
| return buf; |
| } |
| |
| // In bytes |
| function size_of_segment(segment, bindings) { |
| // size refers to a variable |
| if (typeof segment.size === 'string') { |
| return (bindings[segment.size] * segment.unit) / 8; |
| } |
| if (segment.type === 'string') { |
| return Buffer.byteLength(segment.value, 'utf8'); |
| } |
| if (segment.type === 'binary' && segment.size === true) { |
| var val = bindings[segment.name]; |
| return val.length; |
| } |
| return (segment.size * segment.unit) / 8; |
| } |
| |
| // size of the to-be-constructed binary, in bytes |
| function size_of(segments, bindings) { |
| var size = 0; |
| for (var i=0, len = segments.length; i < len; i++) { |
| size += size_of_segment(segments[i], bindings); |
| } |
| return size; |
| } |
| |
| function writeBinary(segment, buf, offset, bindings) { |
| var bin = bindings[segment.name]; |
| var size = size_of_segment(segment, bindings); |
| bin.copy(buf, offset, 0, size); |
| return size; |
| } |
| |
| // TODO in ff might use the noAssert argument to Buffer.write*() but |
| // need to check that it does the right thing wrt little-endian |
| |
| function writeInteger(segment, buf, offset, bindings) { |
| var value = (segment.name) ? bindings[segment.name] : segment.value; |
| var size = size_of_segment(segment, bindings); |
| return write_int(buf, value, offset, size, segment.bigendian); |
| } |
| |
| function write_int(buf, value, offset, size, bigendian) { |
| switch (size) { |
| case 1: |
| buf.writeUInt8(value, offset); |
| break; |
| case 2: |
| (bigendian) ? |
| buf.writeUInt16BE(value, offset) : |
| buf.writeUInt16LE(value, offset); |
| break; |
| case 4: |
| (bigendian) ? |
| buf.writeUInt32BE(value, offset) : |
| buf.writeUInt32LE(value, offset); |
| break; |
| case 8: |
| (bigendian) ? |
| ints.writeUInt64BE(buf, value, offset) : |
| ints.writeUInt64LE(buf, value, offset); |
| break; |
| default: |
| throw new Error("integer size * unit must be 8, 16, 32 or 64"); |
| } |
| return size; |
| } |
| |
| function writeFloat(segment, buf, offset, bindings) { |
| var value = (segment.name) ? bindings[segment.name] : segment.value; |
| var size = size_of_segment(segment, bindings); |
| return write_float(buf, value, offset, size, segment.bigendian); |
| } |
| |
| function write_float(buf, value, offset, size, bigendian) { |
| if (size === 4) { |
| (bigendian) ? |
| buf.writeFloatBE(value, offset) : |
| buf.writeFloatLE(value, offset); |
| } |
| else if (size === 8) { |
| (bigendian) ? |
| buf.writeDoubleBE(value, offset) : |
| buf.writeDoubleLE(value, offset); |
| } |
| else { |
| throw new Error("float size * unit must be 32 or 64"); |
| } |
| return size; |
| } |
| |
| var parse = require('./parse').parse; |
| |
| module.exports.write = write; |
| module.exports.build = build; |
| module.exports.write_int = write_int; |
| module.exports.write_float = write_float; |
| |
| module.exports.builder = function(pstr) { |
| pstr = (arguments.length > 1) ? [].join.call(arguments, ',') : pstr; |
| var pattern = parse(pstr); |
| return function(vars) { |
| return build(pattern, vars); |
| }; |
| }; |