blob: 2faa09a41f79d81e003f35a0cd5fd7396db32544 [file] [log] [blame]
// -*- 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);
};
};