Skip to content

Commit

Permalink
Merge changes resulting in v0.8.0
Browse files Browse the repository at this point in the history
  • Loading branch information
pabigot committed Nov 29, 2015
2 parents e7b94b7 + 2f085b8 commit b9c7cb9
Show file tree
Hide file tree
Showing 7 changed files with 538 additions and 76 deletions.
28 changes: 26 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Layout support is provided for these types of data:

* Signed and unsigned integral values from 1 to 6 bytes in length, in
little-endian or big-endian format;
* Signed and unsigned 64-bit integral values decoded as integral
Numbers;
* Float and double values (also little-endian or big-endian);
* Sequences of instances of an arbitrary layout;
* Structures with named fields containing arbitrary layouts;
Expand Down Expand Up @@ -164,6 +166,27 @@ The buffer-layout way:

See [BitStructure](http://pabigot.github.io/buffer-layout/module-Layout-BitStructure.html).

### 64-bit values as Numbers

The C definition:

uint64_t v = 0x0102030405060708ULL;

The buffer-layout way:

var ds = lo.nu64be(),
b = Buffer('0102030405060708', 'hex'),
v = 72623859790382856,
nv = v - 6;
assert.equal(v, nv);
assert.equal(ds.decode(b), nv);

Note that because the exact value is not less than 2^53 it cannot be
represented as a JavaScript Number, and is instead approximated by a
nearby representable integer that is equivalent within Numbers.

See [NearUInt64](http://pabigot.github.io/buffer-layout/module-Layout-NearUInt64BE.html).

### A NUL-terminated C string

The C definition:
Expand Down Expand Up @@ -276,5 +299,6 @@ decoding each of the alternatives:

**NOTE** This code tickles a long-standing [bug in
Buffer.writeInt{L,B}E](https://github.com/nodejs/node/pull/3994). `buffer-layout`
patches `Buffer` to fix the bug if it detects that the running Node has
has the error.
provides a [module that patches
`Buffer`](http://pabigot.github.io/buffer-layout/module-patchIssue3992.html)
to fix the bug if it detects that the running Node has the error.
255 changes: 183 additions & 72 deletions lib/Layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@
* module:Layout.s24be|24-bit}, {@link module:Layout.s32be|32-bit},
* {@link module:Layout.s40be|40-bit}, and {@link
* module:Layout.s48be|48-bit} representation ranges;
* * 64-bit integral values that decode to an exact (if magnitude is
* less than 2^53) or nearby integral Number in {@link
* module:Layout.nu64|unsigned little-endian}, {@link
* module:Layout.nu64be|unsigned big-endian}, {@link
* module:Layout.ns64|signed little-endian}, and {@link
* module:Layout.ns64be|unsigned big-endian} encodings;
* * 32-bit floating point values with {@link
* module:Layout.f32|little-endian} and {@link
* module:Layout.f32be|big-endian} representations;
Expand Down Expand Up @@ -92,6 +98,10 @@
* @local UIntBE
* @local Int
* @local IntBE
* @local NearUInt64
* @local NearUInt64BE
* @local NearInt64
* @local NearInt64BE
* @local Float
* @local FloatBE
* @local Double
Expand Down Expand Up @@ -121,78 +131,7 @@

var assert = require('assert');

/* Determine whether node issue #3992 is fixed, and if not replace the
* Buffer functions with ones that work correctly.
*
* See: https://github.com/nodejs/node/issues/3992 */
var issue_3992_is_resolved = (function () {
var buf = new Buffer(2);
buf.writeIntLE(-0x100, 0, 2);
return (0xFF === buf[1]);
})();
/* NB: Backfill code modified from node lib/buffer.js as of
* 8bc80386879538de63cd6f2aef288f59324eb004 (2015-11-20) */
/* istanbul ignore next */
function checkInt(buffer, value, offset, ext, max, min) {
if (!(buffer instanceof Buffer))
throw new TypeError('"buffer" argument must be a Buffer instance');
if (value > max || value < min)
throw new TypeError('"value" argument is out of bounds');
if (offset + ext > buffer.length)
throw new RangeError('Index out of range');
}
/* The grotesquery here is because jshint whines about strict mode
* violations if the function doesn't appear to be a method, and about
* defining functions within a block if the test is in an if
* statement. */
Buffer.prototype.writeIntLE = issue_3992_is_resolved ? Buffer.prototype.writeIntLE : function(value, offset, byteLength, noAssert) {
value = +value;
offset = offset >>> 0;
if (!noAssert) {
checkInt(this,
value,
offset,
byteLength,
Math.pow(2, 8 * byteLength - 1) - 1,
-Math.pow(2, 8 * byteLength - 1));
}

var i = 0;
var mul = 1;
var sub = 0;
this[offset] = value;
while (++i < byteLength && (mul *= 0x100)) {
if (value < 0 && sub === 0 && this[offset + i - 1] !== 0)
sub = 1;
this[offset + i] = ((value / mul) >> 0) - sub;
}

return offset + byteLength;
};
Buffer.prototype.writeIntBE = issue_3992_is_resolved ? Buffer.prototype.writeIntBE : function(value, offset, byteLength, noAssert) {
value = +value;
offset = offset >>> 0;
if (!noAssert) {
checkInt(this,
value,
offset,
byteLength,
Math.pow(2, 8 * byteLength - 1) - 1,
-Math.pow(2, 8 * byteLength - 1));
}

var i = byteLength - 1;
var mul = 1;
var sub = 0;
this[offset + i] = value;
while (--i >= 0 && (mul *= 0x100)) {
if (value < 0 && sub === 0 && this[offset + i + 1] !== 0)
sub = 1;
this[offset + i] = ((value / mul) >> 0) - sub;
}

return offset + byteLength;
};
require('./patchIssue3992');

/* istanbul ignore next */
/* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger */
Expand Down Expand Up @@ -602,6 +541,162 @@ IntBE.prototype.encode = function (src, b, offset) {
b.writeIntBE(src, offset, this.span);
};

var V2E32 = Math.pow(2, 32);
/* True modulus high and low 32-bit words, where low word is always
* non-negative. */
function divmodInt64 (src) {
var hi32 = Math.floor(src / V2E32),
lo32 = src - (hi32 * V2E32);
//assert.equal(roundedInt64(hi32, lo32), src);
//assert(0 <= lo32);
return { hi32: hi32,
lo32: lo32 };
}
/* Reconstruct Number from quotient and non-negative remainder */
function roundedInt64 (hi32, lo32) {
return hi32 * V2E32 + lo32;
}

/** Represent an unsigned 64-bit integer in little-endian format when
* encoded and as a near integral JavaScript Number when decoded.
*
* *Factory*: {@link module:Layout.nu64|nu64}
*
* **NOTE** Values with magnitude greater than 2^52 may not decode to
* the exact value of the encoded representation.
*
* @constructor
* @augments {Layout} */
function NearUInt64 (property) {
Layout.call(this, 8, property);
Object.freeze(this);
}
NearUInt64.prototype = Object.create(Layout.prototype);
NearUInt64.prototype.constructor = NearUInt64;
/** Implement {@link Layout#decode|decode} for {@link NearUInt64|NearUInt64}. */
NearUInt64.prototype.decode = function (b, offset) {
if (undefined === offset) {
offset = 0;
}
var lo32 = b.readUInt32LE(offset),
hi32 = b.readUInt32LE(offset+4);
return roundedInt64(hi32, lo32);
};
/** Implement {@link Layout#encode|encode} for {@link NearUInt64|NearUInt64}. */
NearUInt64.prototype.encode = function (src, b, offset) {
if (undefined === offset) {
offset = 0;
}
var split = divmodInt64(src);
b.writeUInt32LE(split.lo32, offset);
b.writeUInt32LE(split.hi32, offset+4);
};

/** Represent an unsigned 64-bit integer in big-endian format when
* encoded and as a near integral JavaScript Number when decoded.
*
* *Factory*: {@link module:Layout.nu64be|nu64be}
*
* **NOTE** Values with magnitude greater than 2^52 may not decode to
* the exact value of the encoded representation.
*
* @constructor
* @augments {Layout} */
function NearUInt64BE (property) {
Layout.call(this, 8, property);
Object.freeze(this);
}
NearUInt64BE.prototype = Object.create(Layout.prototype);
NearUInt64BE.prototype.constructor = NearUInt64BE;
/** Implement {@link Layout#decode|decode} for {@link NearUInt64BE|NearUInt64BE}. */
NearUInt64BE.prototype.decode = function (b, offset) {
if (undefined === offset) {
offset = 0;
}
var hi32 = b.readUInt32BE(offset),
lo32 = b.readUInt32BE(offset+4);
return roundedInt64(hi32, lo32);
};
/** Implement {@link Layout#encode|encode} for {@link NearUInt64BE|NearUInt64BE}. */
NearUInt64BE.prototype.encode = function (src, b, offset) {
if (undefined === offset) {
offset = 0;
}
var split = divmodInt64(src);
b.writeUInt32BE(split.hi32, offset);
b.writeUInt32BE(split.lo32, offset+4);
};

/** Represent a signed 64-bit integer in little-endian format when
* encoded and as a near integral JavaScript Number when decoded.
*
* *Factory*: {@link module:Layout.ns64|ns64}
*
* **NOTE** Values with magnitude greater than 2^52 may not decode to
* the exact value of the encoded representation.
*
* @constructor
* @augments {Layout} */
function NearInt64 (property) {
Layout.call(this, 8, property);
Object.freeze(this);
}
NearInt64.prototype = Object.create(Layout.prototype);
NearInt64.prototype.constructor = NearInt64;
/** Implement {@link Layout#decode|decode} for {@link NearInt64|NearInt64}. */
NearInt64.prototype.decode = function (b, offset) {
if (undefined === offset) {
offset = 0;
}
var lo32 = b.readUInt32LE(offset),
hi32 = b.readInt32LE(offset+4);
return roundedInt64(hi32, lo32);
};
/** Implement {@link Layout#encode|encode} for {@link NearInt64|NearInt64}. */
NearInt64.prototype.encode = function (src, b, offset) {
if (undefined === offset) {
offset = 0;
}
var split = divmodInt64(src);
b.writeUInt32LE(split.lo32, offset);
b.writeInt32LE(split.hi32, offset+4);
};

/** Represent a signed 64-bit integer in big-endian format when
* encoded and as a near integral JavaScript Number when decoded.
*
* *Factory*: {@link module:Layout.ns64be|ns64be}
*
* **NOTE** Values with magnitude greater than 2^52 may not decode to
* the exact value of the encoded representation.
*
* @constructor
* @augments {Layout} */
function NearInt64BE (property) {
Layout.call(this, 8, property);
Object.freeze(this);
}
NearInt64BE.prototype = Object.create(Layout.prototype);
NearInt64BE.prototype.constructor = NearInt64BE;
/** Implement {@link Layout#decode|decode} for {@link NearInt64BE|NearInt64BE}. */
NearInt64BE.prototype.decode = function (b, offset) {
if (undefined === offset) {
offset = 0;
}
var hi32 = b.readInt32BE(offset),
lo32 = b.readUInt32BE(offset+4);
return roundedInt64(hi32, lo32);
};
/** Implement {@link Layout#encode|encode} for {@link NearInt64BE|NearInt64BE}. */
NearInt64BE.prototype.encode = function (src, b, offset) {
if (undefined === offset) {
offset = 0;
}
var split = divmodInt64(src);
b.writeInt32BE(split.hi32, offset);
b.writeUInt32BE(split.lo32, offset+4);
};

/** Represent a 32-bit floating point number in little-endian format.
*
* *Factory*: {@link module:Layout.f32|f32}
Expand Down Expand Up @@ -1963,6 +2058,10 @@ exports.u40 = function (property) { return new UInt(5, property); };
* spanning six bytes. */
exports.u48 = function (property) { return new UInt(6, property); };

/** Factory for {@link NearUInt64|little-endian unsigned int
* layouts} interpreted as Numbers. */
exports.nu64 = function (property) { return new NearUInt64(property); };

/** Factory for {@link UInt|big-endian unsigned int layouts}
* spanning two bytes. */
exports.u16be = function (property) { return new UIntBE(2, property); };
Expand All @@ -1983,6 +2082,10 @@ exports.u40be = function (property) { return new UIntBE(5, property); };
* spanning six bytes. */
exports.u48be = function (property) { return new UIntBE(6, property); };

/** Factory for {@link NearUInt64BE|big-endian unsigned int
* layouts} interpreted as Numbers. */
exports.nu64be = function (property) { return new NearUInt64BE(property); };

/** Factory for {@link Int|signed int layouts} spanning one
* byte. */
exports.s8 = function (property) { return new Int(1, property); };
Expand All @@ -2007,6 +2110,10 @@ exports.s40 = function (property) { return new Int(5, property); };
* spanning six bytes. */
exports.s48 = function (property) { return new Int(6, property); };

/** Factory for {@link NearInt64|little-endian signed int layouts}
* interpreted as Numbers. */
exports.ns64 = function (property) { return new NearInt64(property); };

/** Factory for {@link Int|big-endian signed int layouts}
* spanning two bytes. */
exports.s16be = function (property) { return new IntBE(2, property); };
Expand All @@ -2027,6 +2134,10 @@ exports.s40be = function (property) { return new IntBE(5, property); };
* spanning six bytes. */
exports.s48be = function (property) { return new IntBE(6, property); };

/** Factory for {@link NearInt64BE|big-endian signed int layouts}
* interpreted as Numbers. */
exports.ns64be = function (property) { return new NearInt64BE(property); };

/** Factory for {@link Float|little-endian 32-bit floating point} values. */
exports.f32 = function (property) { return new Float(property); };

Expand Down
Loading

0 comments on commit b9c7cb9

Please sign in to comment.