Permalink
Browse files

Add reading/writing of floats and doubles from/to buffers

Code for readIEEE754/writeIEEE754 is from jspack: http://code.google.com/p/jspack/
  • Loading branch information...
1 parent 91bd144 commit e505a1215c5e1243c8790bf7d23c96c27900fff7 @mscdex mscdex committed with ry May 16, 2011
View
@@ -66,3 +66,6 @@ The externally maintained libraries used by Node are:
- tools/cpplint.py is copyright Google Inc. and released under a
BSD license.
+
+ - lib/buffer_ieee754.js is copyright 2008 Fair Oaks Labs, Inc. and released
+ under the New BSD license.
View
@@ -275,6 +275,48 @@ bytes from the buffer in.
Works as `buffer.readUInt32`, except buffer contents are treated as twos
complement signed values.
+### buffer.readFloat(offset, endian)
+
+Reads a 32 bit float from the buffer at the specified offset. Endian must be
+either 'big' or 'little' and specifies what endian ordering to read the bytes
+from the buffer in.
+
+Example:
+
+ var buf = new Buffer(4);
+
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+ buf[2] = 0x80;
+ buf[3] = 0x3f;
+
+ console.log(buf.readFloat(0, 'little');
+
+ // 0x01
+
+### buffer.readDouble(offset, endian)
+
+Reads a 64 bit double from the buffer at the specified offset. Endian must be
+either 'big' or 'little' and specifies what endian ordering to read the bytes
+from the buffer in.
+
+Example:
+
+ var buf = new Buffer(8);
+
+ buf[0] = 0x55;
+ buf[1] = 0x55;
+ buf[2] = 0x55;
+ buf[3] = 0x55;
+ buf[4] = 0x55;
+ buf[5] = 0x55;
+ buf[6] = 0xd5;
+ buf[7] = 0x3f;
+
+ console.log(buf.readDouble(0, 'little');
+
+ // 0.3333333333333333
+
### buffer.writeUInt8(value, offset, endian)
Writes `value` to the buffer at the specified offset with specified endian
@@ -364,6 +406,44 @@ format. Note, `value` must be a valid 16 bit signed integer.
Works as `buffer.writeUInt832, except value is written out as a two's complement
signed integer into `buffer`.
+### buffer.writeFloat(value, offset, endian)
+
+Writes `value` to the buffer at the specified offset with specified endian
+format. Note, `value` must be a valid 32 bit float.
+
+Example:
+
+ var buf = new Buffer(4);
+ buf.writeFloat(0xcafebabe, 0, 'big');
+
+ console.log(buf);
+
+ buf.writeFloat(0xcafebabe, 0, 'little');
+
+ console.log(buf);
+
+ // <Buffer 4f 4a fe bb>
+ // <Buffer bb fe 4a 4f>
+
+### buffer.writeDouble(value, offset, endian)
+
+Writes `value` to the buffer at the specified offset with specified endian
+format. Note, `value` must be a valid 64 bit double.
+
+Example:
+
+ var buf = new Buffer(8);
+ buf.writeFloat(0xdeadbeefcafebabe, 0, 'big');
+
+ console.log(buf);
+
+ buf.writeFloat(0xdeadbeefcafebabe, 0, 'little');
+
+ console.log(buf);
+
+ // <Buffer 43 eb d5 b7 dd f9 5f d7>
+ // <Buffer d7 5f f9 dd b7 d5 eb 43>
+
### buffer.fill(value, offset=0, length=-1)
View
@@ -20,6 +20,7 @@
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var SlowBuffer = process.binding('buffer').SlowBuffer;
+var IEEE754 = require('buffer_ieee754');
var assert = require('assert');
@@ -684,6 +685,43 @@ Buffer.prototype.readInt32 = function(offset, endian) {
};
+Buffer.prototype.readFloat = function(offset, endian) {
+ var buffer = this;
+
+ assert.ok(endian !== undefined && endian !== null,
+ 'missing endian');
+
+ assert.ok(endian == 'big' || endian == 'little',
+ 'bad endian value');
+
+ assert.ok(offset !== undefined && offset !== null,
+ 'missing offset');
+
+ assert.ok(offset + 3 < buffer.length,
+ 'Trying to read beyond buffer length');
+
+ return IEEE754.readIEEE754(buffer, offset, endian, 23, 4);
+};
+
+Buffer.prototype.readDouble = function(offset, endian) {
+ var buffer = this;
+
+ assert.ok(endian !== undefined && endian !== null,
+ 'missing endian');
+
+ assert.ok(endian == 'big' || endian == 'little',
+ 'bad endian value');
+
+ assert.ok(offset !== undefined && offset !== null,
+ 'missing offset');
+
+ assert.ok(offset + 7 < buffer.length,
+ 'Trying to read beyond buffer length');
+
+ return IEEE754.readIEEE754(buffer, offset, endian, 52, 8);
+};
+
+
/*
* We have to make sure that the value is a valid integer. This means that it is
* non-negative. It has no fractional component and that it does not exceed the
@@ -843,6 +881,17 @@ function verifsint(value, max, min) {
assert.ok(Math.floor(value) === value, 'value has a fractional component');
}
+
+function verifIEEE754(value, max, min) {
+ assert.ok(typeof (value) == 'number',
+ 'cannot write a non-number as a number');
+
+ assert.ok(value <= max, 'value larger than maximum allowed value');
+
+ assert.ok(value >= min, 'value smaller than minimum allowed value');
+}
+
+
Buffer.prototype.writeInt8 = function(value, offset, endian) {
var buffer = this;
@@ -924,3 +973,49 @@ Buffer.prototype.writeInt32 = function(value, offset, endian) {
buffer.writeUInt32(0xffffffff + value + 1, offset, endian);
}
};
+
+
+Buffer.prototype.writeFloat = function(value, offset, endian) {
+ var buffer = this;
+
+ assert.ok(value !== undefined && value !== null,
+ 'missing value');
+
+ assert.ok(endian !== undefined && endian !== null,
+ 'missing endian');
+
+ assert.ok(endian == 'big' || endian == 'little',
+ 'bad endian value');
+
+ assert.ok(offset !== undefined && offset !== null,
+ 'missing offset');
+
+ assert.ok(offset + 3 < buffer.length,
+ 'Trying to read beyond buffer length');
+
+ verifIEEE754(value, 3.4028234663852886e+38, -3.4028234663852886e+38);
+ IEEE754.writeIEEE754(buffer, value, offset, endian, 23, 4);
+};
+
+
+Buffer.prototype.writeDouble = function(value, offset, endian) {
+ var buffer = this;
+
+ assert.ok(value !== undefined && value !== null,
+ 'missing value');
+
+ assert.ok(endian !== undefined && endian !== null,
+ 'missing endian');
+
+ assert.ok(endian == 'big' || endian == 'little',
+ 'bad endian value');
+
+ assert.ok(offset !== undefined && offset !== null,
+ 'missing offset');
+
+ assert.ok(offset + 7 < buffer.length,
+ 'Trying to read beyond buffer length');
+
+ verifIEEE754(value, 1.7976931348623157E+308, -1.7976931348623157E+308);
+ IEEE754.writeIEEE754(buffer, value, offset, endian, 52, 8);
+};
View
@@ -0,0 +1,118 @@
+// Copyright (c) 2008, Fair Oaks Labs, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// * Neither the name of Fair Oaks Labs, Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+//
+// Modifications to writeIEEE754 to support negative zeroes made by Brian White
+
+exports.readIEEE754 = function(buffer, offset, endian, mLen, nBytes) {
+ var e, m,
+ bBE = (endian === 'big'),
+ eLen = nBytes * 8 - mLen - 1,
+ eMax = (1 << eLen) - 1,
+ eBias = eMax >> 1,
+ nBits = -7,
+ i = bBE ? 0 : (nBytes - 1),
+ d = bBE ? 1 : -1,
+ s = buffer[offset + i];
+
+ i += d;
+
+ e = s & ((1 << (-nBits)) - 1);
+ s >>= (-nBits);
+ nBits += eLen;
+ for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8);
+
+ m = e & ((1 << (-nBits)) - 1);
+ e >>= (-nBits);
+ nBits += mLen;
+ for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8);
+
+ if (e === 0) {
+ e = 1 - eBias;
+ } else if (e === eMax) {
+ return m ? NaN : ((s ? -1 : 1) * Infinity);
+ } else {
+ m = m + Math.pow(2, mLen);
+ e = e - eBias;
+ }
+ return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
+};
+
+exports.writeIEEE754 = function(buffer, value, offset, endian, mLen, nBytes) {
+ var e, m, c,
+ bBE = (endian === 'big'),
+ eLen = nBytes * 8 - mLen - 1,
+ eMax = (1 << eLen) - 1,
+ eBias = eMax >> 1,
+ rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0),
+ i = bBE ? (nBytes-1) : 0,
+ d = bBE ? -1 : 1,
+ s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0;
+
+ value = Math.abs(value);
+
+ if (isNaN(value) || value === Infinity) {
+ m = isNaN(value) ? 1 : 0;
+ e = eMax;
+ } else {
+ e = Math.floor(Math.log(value) / Math.LN2);
+ if (value * (c = Math.pow(2, -e)) < 1) {
+ e--;
+ c *= 2;
+ }
+ if (e+eBias >= 1) {
+ value += rt / c;
+ } else {
+ value += rt * Math.pow(2, 1 - eBias);
+ }
+ if (value * c >= 2) {
+ e++;
+ c /= 2;
+ }
+
+ if (e + eBias >= eMax) {
+ m = 0;
+ e = eMax;
+ } else if (e + eBias >= 1) {
+ m = (value * c - 1) * Math.pow(2, mLen);
+ e = e + eBias;
+ } else {
+ m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
+ e = 0;
+ }
+ }
+
+ for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8);
+
+ e = (e << mLen) | m;
+ eLen += mLen;
+ for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8);
+
+ buffer[offset + i - d] |= s * 128;
+};
Oops, something went wrong.

2 comments on commit e505a12

Wouldn't it be nice to be able to set a default endian on the buffer? Something like buf.endian = "little". Or is it common for a binary buffer to use both endians?

@slaskis It depends on the data you're working with. Some protocols and file formats can have both big and little endian data in them. You could just write some wrapper functions that always use 'little' or 'big' for the endianness argument if it becomes enough of an issue.

Please sign in to comment.