如何用Node.js读取非字节对齐的整数?

时间:2014-07-28 13:31:47

标签: node.js flash binaryfiles

我正在尝试使用Node.JS读取二进制SWF文件。正如specification底部提到的the 17th page,有些整数使用可变长度位字段进行编码,根据定义,大多数整数都不是字节对齐的。

问题是Node.js的Buffer只提供读取字节对齐整数的函数。所以我试着写一个可以一点一点读取的包装器对象。然而,它似乎真的很hacky。下面是我写的对象原型:

/*jslint node:true, bitwise:true */

'use strict';

var util = require('util');
var maxBits = 32;

function BitBuffer() {
    Buffer.apply(this, arguments);

    this.cursor = 0;
    this.bitCursor = 0;
}

util.inherits(BitBuffer, Buffer);
module.exports = BitBuffer;

function padBits(bits, length, bit) {
    var leftPad = '', repeatLength, i;

    bits = bits.toString(2);
    length = length || 8;
    bit = bit || '0';

    if (bits.length >= length) {
        return bits;
    } else {
        repeatLength = length - bits.length;

        for (i = 0; i < repeatLength; i += 1) {
            leftPad += bit;
        }

        return leftPad + bits;
    }
}

BitBuffer.prototype.move = function (bits) {
    var bytes = Math.floor((this.bitCursor + bits) / 8);

    this.bitCursor += bits;

    if (this.bitCursor > 8) {
        this.cursor += bytes;
        this.bitCursor -= bytes * 8;
    }

    if (this.cursor >= this.length) {
        this.rewind();
        return false;
    }

    return true;
};

BitBuffer.prototype.align = function () {
    if (this.bitCursor > 0) {
        this.bitCursor = 0;
        this.cursor += 1;
    }
};

BitBuffer.prototype.rewind = function () {
    this.cursor = this.bitCursor = 0;
};

BitBuffer.prototype.readBits = function (bits) {
    var bytes = Math.ceil((this.bitCursor + bits) / 8), result = 0,
        length, buffer, i;

    if (bits > maxBits) {
        throw new RangeError('Cannot read more than ' + maxBits + ' bits');
    }

    buffer = this.slice(this.cursor, this.cursor + bytes);
    result = padBits(buffer[0]).substr(this.bitCursor);
    length = buffer.length;

    for (i = 1; i < length; i += 1) {
        result += padBits(buffer[i]);
    }

    result = result.substr(0, bits);
    return (this.move(bits)) ? parseInt(result, 2) : false;
};

BitBuffer.prototype.readUB = BitBuffer.prototype.readBits;

BitBuffer.prototype.readSB = function (bits) {
    var readBits = this.readBits(bits),
        stringBits = readBits.toString(2);

    if (readBits === false) {
        return false;
    }

    // add automatically removed zeros
    if (stringBits.length < bits) {
        stringBits = padBits(stringBits, bits);
    }

    // negative, pad to 32 bits then invert bits
    if (stringBits[0] === '1') {
        return -~parseInt(padBits(stringBits, maxBits, '1'), 2) - 1;
    } else {
        return readBits;
    }
};

BitBuffer.prototype.readFB = function (bits) {
    var highBits, lowBits, result;

    if (bits < 17) {
        throw new RangeError('Should be at least 17 bits long');
    }

    highBits = this.readSB(bits - 16);
    lowBits = this.readUB(16);
    lowBits *= Math.pow(10, -lowBits.toString(10).length);

    return (highBits >= 0) ?
            highBits + lowBits : highBits - lowBits;
};

// wrap read functions
(function () {
    var nativeReadFunctions = {
        'readInt8': 8,
        'readInt16LE': 16,
        'readInt16BE': 16,
        'readInt32LE': 32,
        'readInt32BE': 32,
        'readUInt8': 8,
        'readUInt16LE': 16,
        'readUInt16BE': 16,
        'readUInt32LE': 32,
        'readUInt32BE': 32,
        'readDoubleLE': 64,
        'readDoubleBE': 64,
        'readFloatLE': 32,
        'readFloatBE': 32
    }, method;

    function wrapNativeRead(method, length) {
        return function (noAssert) {
            var cursor;

            this.align();
            cursor = this.cursor;
            this.move(length);

            return Buffer.prototype[method].call(
                this,
                this.cursor,
                noAssert
            );
        };
    }

    for (method in nativeReadFunctions) {
        if (nativeReadFunctions.hasOwnProperty(method)) {
            BitBuffer.prototype[method] =
                wrapNativeRead(method, nativeReadFunctions[method]);
        }
    }
}());

写自己的对象是好方法吗?

1 个答案:

答案 0 :(得分:0)

除了您的实施非常好,您应该了解在您的实施this.buffer === this中,您应该更改以下行:

// inside BitBuffer prototype constructor

this.buffer = new Buffer(param1, param2);

// change to

Buffer.call(this, param1, param2); // The right way to call the super constructor

// inside BitBuffer.prototype.readBits

buffer = this.buffer.slice(this.cursor, this.cursor + bytes);

// change to

buffer = this.slice(this.cursor, this.cursor + bytes);