var assert = require('assert') var inspect = require('util').inspect var debug = require('debug')('ref') exports = module.exports = require('bindings')('binding') /** * A `Buffer` that references the C NULL pointer. That is, its memory address * points to 0. Its `length` is 0 because accessing any data from this buffer * would cause a _segmentation fault_. * * ``` * console.log(ref.NULL); * * ``` * * @name NULL * @type Buffer */ /** * A string that represents the native endianness of the machine's processor. * The possible values are either `"LE"` or `"BE"`. * * ``` * console.log(ref.endianness); * 'LE' * ``` * * @name endianness * @type String */ /** * Accepts a `Buffer` instance and returns the memory address of the buffer * instance. * * ``` * console.log(ref.address(new Buffer(1))); * 4320233616 * * console.log(ref.address(ref.NULL))); * 0 * ``` * * @param {Buffer} buffer The buffer to get the memory address of. * @return {Number} The memory address the buffer instance. * @name address * @type method */ /** * Accepts a `Buffer` instance and returns _true_ if the buffer represents the * NULL pointer, _false_ otherwise. * * ``` * console.log(ref.isNull(new Buffer(1))); * false * * console.log(ref.isNull(ref.NULL)); * true * ``` * * @param {Buffer} buffer The buffer to check for NULL. * @return {Boolean} true or false. * @name isNull * @type method */ /** * Reads a JavaScript Object that has previously been written to the given * _buffer_ at the given _offset_. * * ``` * var obj = { foo: 'bar' }; * var buf = ref.alloc('Object', obj); * * var obj2 = ref.readObject(buf, 0); * console.log(obj === obj2); * true * ``` * * @param {Buffer} buffer The buffer to read an Object from. * @param {Number} offset The offset to begin reading from. * @return {Object} The Object that was read from _buffer_. * @name readObject * @type method */ /** * Reads a Buffer instance from the given _buffer_ at the given _offset_. * The _size_ parameter specifies the `length` of the returned Buffer instance, * which defaults to __0__. * * ``` * var buf = new Buffer('hello world'); * var pointer = ref.alloc('pointer'); * * var buf2 = ref.readPointer(pointer, 0, buf.length); * console.log(buf.toString()); * 'hello world' * ``` * * @param {Buffer} buffer The buffer to read a Buffer from. * @param {Number} offset The offset to begin reading from. * @param {Number} length (optional) The length of the returned Buffer. Defaults to 0. * @return {Buffer} The Buffer instance that was read from _buffer_. * @name readPointer * @type method */ /** * Returns a JavaScript String read from _buffer_ at the given _offset_. The * C String is read until the first NULL byte, which indicates the end of the * String. * * This function can read beyond the `length` of a Buffer. * * ``` * var buf = new Buffer('hello\0world\0'); * * var str = ref.readCString(buf, 0); * console.log(str); * 'hello' * ``` * * @param {Buffer} buffer The buffer to read a Buffer from. * @param {Number} offset The offset to begin reading from. * @return {String} The String that was read from _buffer_. * @name readCString * @type method */ /** * Returns a big-endian signed 64-bit int read from _buffer_ at the given * _offset_. * * If the returned value will fit inside a JavaScript Number without losing * precision, then a Number is returned, otherwise a String is returned. * * ``` * var buf = ref.alloc('int64'); * ref.writeInt64BE(buf, 0, '9223372036854775807'); * * var val = ref.readInt64BE(buf, 0) * console.log(val) * '9223372036854775807' * ``` * * @param {Buffer} buffer The buffer to read a Buffer from. * @param {Number} offset The offset to begin reading from. * @return {Number|String} The Number or String that was read from _buffer_. * @name readInt64BE * @type method */ /** * Returns a little-endian signed 64-bit int read from _buffer_ at the given * _offset_. * * If the returned value will fit inside a JavaScript Number without losing * precision, then a Number is returned, otherwise a String is returned. * * ``` * var buf = ref.alloc('int64'); * ref.writeInt64LE(buf, 0, '9223372036854775807'); * * var val = ref.readInt64LE(buf, 0) * console.log(val) * '9223372036854775807' * ``` * * @param {Buffer} buffer The buffer to read a Buffer from. * @param {Number} offset The offset to begin reading from. * @return {Number|String} The Number or String that was read from _buffer_. * @name readInt64LE * @type method */ /** * Returns a big-endian unsigned 64-bit int read from _buffer_ at the given * _offset_. * * If the returned value will fit inside a JavaScript Number without losing * precision, then a Number is returned, otherwise a String is returned. * * ``` * var buf = ref.alloc('uint64'); * ref.writeUInt64BE(buf, 0, '18446744073709551615'); * * var val = ref.readUInt64BE(buf, 0) * console.log(val) * '18446744073709551615' * ``` * * @param {Buffer} buffer The buffer to read a Buffer from. * @param {Number} offset The offset to begin reading from. * @return {Number|String} The Number or String that was read from _buffer_. * @name readUInt64BE * @type method */ /** * Returns a little-endian unsigned 64-bit int read from _buffer_ at the given * _offset_. * * If the returned value will fit inside a JavaScript Number without losing * precision, then a Number is returned, otherwise a String is returned. * * ``` * var buf = ref.alloc('uint64'); * ref.writeUInt64LE(buf, 0, '18446744073709551615'); * * var val = ref.readUInt64LE(buf, 0) * console.log(val) * '18446744073709551615' * ``` * * @param {Buffer} buffer The buffer to read a Buffer from. * @param {Number} offset The offset to begin reading from. * @return {Number|String} The Number or String that was read from _buffer_. * @name readUInt64LE * @type method */ /** * Writes the _input_ Number or String as a big-endian signed 64-bit int into * _buffer_ at the given _offset_. * * ``` * var buf = ref.alloc('int64'); * ref.writeInt64BE(buf, 0, '9223372036854775807'); * ``` * * @param {Buffer} buffer The buffer to write to. * @param {Number} offset The offset to begin writing from. * @param {Number|String} input This String or Number which gets written. * @name writeInt64BE * @type method */ /** * Writes the _input_ Number or String as a little-endian signed 64-bit int into * _buffer_ at the given _offset_. * * ``` * var buf = ref.alloc('int64'); * ref.writeInt64LE(buf, 0, '9223372036854775807'); * ``` * * @param {Buffer} buffer The buffer to write to. * @param {Number} offset The offset to begin writing from. * @param {Number|String} input This String or Number which gets written. * @name writeInt64LE * @type method */ /** * Writes the _input_ Number or String as a big-endian unsigned 64-bit int into * _buffer_ at the given _offset_. * * ``` * var buf = ref.alloc('uint64'); * ref.writeUInt64BE(buf, 0, '18446744073709551615'); * ``` * * @param {Buffer} buffer The buffer to write to. * @param {Number} offset The offset to begin writing from. * @param {Number|String} input This String or Number which gets written. * @name writeUInt64BE * @type method */ /** * Writes the _input_ Number or String as a little-endian unsigned 64-bit int * into _buffer_ at the given _offset_. * * ``` * var buf = ref.alloc('uint64'); * ref.writeUInt64LE(buf, 0, '18446744073709551615'); * ``` * * @param {Buffer} buffer The buffer to write to. * @param {Number} offset The offset to begin writing from. * @param {Number|String} input This String or Number which gets written. * @name writeUInt64LE * @type method */ /** * Returns a new clone of the given "type" object, with its * `indirection` level incremented by **1**. * * Say you wanted to create a type representing a `void *`: * * ``` * var voidPtrType = ref.refType(ref.types.void); * ``` * * @param {Object|String} type The "type" object to create a reference type from. Strings get coerced first. * @return {Object} The new "type" object with its `indirection` incremented by 1. */ exports.refType = function refType (type) { var _type = exports.coerceType(type) var rtn = Object.create(_type) rtn.indirection++ if (_type.name) { Object.defineProperty(rtn, 'name', { value: _type.name + '*', configurable: true, enumerable: true, writable: true }) } return rtn } /** * Returns a new clone of the given "type" object, with its * `indirection` level decremented by 1. * * @param {Object|String} type The "type" object to create a dereference type from. Strings get coerced first. * @return {Object} The new "type" object with its `indirection` decremented by 1. */ exports.derefType = function derefType (type) { var _type = exports.coerceType(type) if (_type.indirection === 1) { throw new Error('Cannot create deref\'d type for type with indirection 1') } var rtn = Object.getPrototypeOf(_type) if (rtn.indirection !== _type.indirection - 1) { // slow case rtn = Object.create(_type) rtn.indirection-- } return rtn } /** * Coerces a "type" object from a String or an actual "type" object. String values * are looked up from the `ref.types` Object. So: * * * `"int"` gets coerced into `ref.types.int`. * * `"int *"` gets translated into `ref.refType(ref.types.int)` * * `ref.types.int` gets translated into `ref.types.int` (returns itself) * * Throws an Error if no valid "type" object could be determined. Most `ref` * functions use this function under the hood, so anywhere a "type" object is * expected, a String may be passed as well, including simply setting the * `buffer.type` property. * * ``` * var type = ref.coerceType('int **'); * * console.log(type.indirection); * 3 * ``` * * @param {Object|String} type The "type" Object or String to coerce. * @return {Object} A "type" object */ exports.coerceType = function coerceType (type) { var rtn = type if (typeof rtn === 'string') { rtn = exports.types[type] if (rtn) return rtn // strip whitespace rtn = type.replace(/\s+/g, '').toLowerCase() if (rtn === 'pointer') { // legacy "pointer" being used :( rtn = exports.refType(exports.types.void) // void * } else if (rtn === 'string') { rtn = exports.types.CString // special char * type } else { var refCount = 0 rtn = rtn.replace(/\*/g, function () { refCount++ return '' }) // allow string names to be passed in rtn = exports.types[rtn] if (refCount > 0) { if (!(rtn && 'size' in rtn && 'indirection' in rtn)) { throw new TypeError('could not determine a proper "type" from: ' + JSON.stringify(type)) } for (var i = 0; i < refCount; i++) { rtn = exports.refType(rtn) } } } } if (!(rtn && 'size' in rtn && 'indirection' in rtn)) { throw new TypeError('could not determine a proper "type" from: ' + JSON.stringify(type)) } return rtn } /** * Returns the "type" property of the given Buffer. * Creates a default type for the buffer when none exists. * * @param {Buffer} buffer The Buffer instance to get the "type" object from. * @return {Object} The "type" object from the given Buffer. */ exports.getType = function getType (buffer) { if (!buffer.type) { debug('WARN: no "type" found on buffer, setting default "type"', buffer) buffer.type = {} buffer.type.size = buffer.length buffer.type.indirection = 1 buffer.type.get = function get () { throw new Error('unknown "type"; cannot get()') } buffer.type.set = function set () { throw new Error('unknown "type"; cannot set()') } } return exports.coerceType(buffer.type) } /** * Calls the `get()` function of the Buffer's current "type" (or the * passed in _type_ if present) at the given _offset_. * * This function handles checking the "indirection" level and returning a * proper "dereferenced" Bufffer instance when necessary. * * @param {Buffer} buffer The Buffer instance to read from. * @param {Number} offset (optional) The offset on the Buffer to start reading from. Defaults to 0. * @param {Object|String} type (optional) The "type" object to use when reading. Defaults to calling `getType()` on the buffer. * @return {?} Whatever value the "type" used when reading returns. */ exports.get = function get (buffer, offset, type) { if (!offset) { offset = 0 } if (type) { type = exports.coerceType(type) } else { type = exports.getType(buffer) } debug('get(): (offset: %d)', offset, buffer) assert(type.indirection > 0, '"indirection" level must be at least 1') if (type.indirection === 1) { // need to check "type" return type.get(buffer, offset) } else { // need to create a deref'd Buffer var size = type.indirection === 2 ? type.size : exports.sizeof.pointer var reference = exports.readPointer(buffer, offset, size) reference.type = exports.derefType(type) return reference } } /** * Calls the `set()` function of the Buffer's current "type" (or the * passed in _type_ if present) at the given _offset_. * * This function handles checking the "indirection" level writing a pointer rather * than calling the `set()` function if the indirection is greater than 1. * * @param {Buffer} buffer The Buffer instance to write to. * @param {Number} offset The offset on the Buffer to start writing to. * @param {?} value The value to write to the Buffer instance. * @param {Object|String} type (optional) The "type" object to use when reading. Defaults to calling `getType()` on the buffer. */ exports.set = function set (buffer, offset, value, type) { if (!offset) { offset = 0 } if (type) { type = exports.coerceType(type) } else { type = exports.getType(buffer) } debug('set(): (offset: %d)', offset, buffer, value) assert(type.indirection >= 1, '"indirection" level must be at least 1') if (type.indirection === 1) { type.set(buffer, offset, value) } else { exports.writePointer(buffer, offset, value) } } /** * Returns a new Buffer instance big enough to hold `type`, * with the given `value` written to it. * * ``` js * var intBuf = ref.alloc(ref.types.int) * var int_with_4 = ref.alloc(ref.types.int, 4) * ``` * * @param {Object|String} type The "type" object to allocate. Strings get coerced first. * @param {?} value (optional) The initial value set on the returned Buffer, using _type_'s `set()` function. * @return {Buffer} A new Buffer instance with it's `type` set to "type", and (optionally) "value" written to it. */ exports.alloc = function alloc (_type, value) { var type = exports.coerceType(_type) debug('allocating Buffer for type with "size"', type.size) var size if (type.indirection === 1) { size = type.size } else { size = exports.sizeof.pointer } var buffer = new Buffer(size) buffer.type = type if (arguments.length >= 2) { debug('setting value on allocated buffer', value) exports.set(buffer, 0, value, type) } return buffer } /** * Returns a new `Buffer` instance with the given String written to it with the * given encoding (defaults to __'utf8'__). The buffer is 1 byte longer than the * string itself, and is NUL terminated. * * ``` * var buf = ref.allocCString('hello world'); * * console.log(buf.toString()); * 'hello world\u0000' * ``` * * @param {String} string The JavaScript string to be converted to a C string. * @param {String} encoding (optional) The encoding to use for the C string. Defaults to __'utf8'__. * @return {Buffer} The new `Buffer` instance with the specified String wrtten to it, and a trailing NUL byte. */ exports.allocCString = function allocCString (string, encoding) { if (null == string || (Buffer.isBuffer(string) && exports.isNull(string))) { return exports.NULL } var size = Buffer.byteLength(string, encoding) + 1 var buffer = new Buffer(size) exports.writeCString(buffer, 0, string, encoding) buffer.type = charPtrType return buffer } /** * Writes the given string as a C String (NULL terminated) to the given buffer * at the given offset. "encoding" is optional and defaults to __'utf8'__. * * Unlike `readCString()`, this function requires the buffer to actually have the * proper length. * * @param {Buffer} buffer The Buffer instance to write to. * @param {Number} offset The offset of the buffer to begin writing at. * @param {String} string The JavaScript String to write that will be written to the buffer. * @param {String} encoding (optional) The encoding to read the C string as. Defaults to __'utf8'__. */ exports.writeCString = function writeCString (buffer, offset, string, encoding) { assert(Buffer.isBuffer(buffer), 'expected a Buffer as the first argument') assert.equal('string', typeof string, 'expected a "string" as the third argument') if (!offset) { offset = 0 } if (!encoding) { encoding = 'utf8' } var size = buffer.length - offset var len = buffer.write(string, offset, size, encoding) buffer.writeUInt8(0, offset + len) // NUL terminate } exports['readInt64' + exports.endianness] = exports.readInt64 exports['readUInt64' + exports.endianness] = exports.readUInt64 exports['writeInt64' + exports.endianness] = exports.writeInt64 exports['writeUInt64' + exports.endianness] = exports.writeUInt64 var opposite = exports.endianness == 'LE' ? 'BE' : 'LE' var int64temp = new Buffer(exports.sizeof.int64) var uint64temp = new Buffer(exports.sizeof.uint64) exports['readInt64' + opposite] = function (buffer, offset) { for (var i = 0; i < exports.sizeof.int64; i++) { int64temp[i] = buffer[offset + exports.sizeof.int64 - i - 1] } return exports.readInt64(int64temp, 0) } exports['readUInt64' + opposite] = function (buffer, offset) { for (var i = 0; i < exports.sizeof.uint64; i++) { uint64temp[i] = buffer[offset + exports.sizeof.uint64 - i - 1] } return exports.readUInt64(uint64temp, 0) } exports['writeInt64' + opposite] = function (buffer, offset, value) { exports.writeInt64(int64temp, 0, value) for (var i = 0; i < exports.sizeof.int64; i++) { buffer[offset + i] = int64temp[exports.sizeof.int64 - i - 1] } } exports['writeUInt64' + opposite] = function (buffer, offset, value) { exports.writeUInt64(uint64temp, 0, value) for (var i = 0; i < exports.sizeof.uint64; i++) { buffer[offset + i] = uint64temp[exports.sizeof.uint64 - i - 1] } } /** * `ref()` accepts a Buffer instance and returns a new Buffer * instance that is "pointer" sized and has its data pointing to the given * Buffer instance. Essentially the created Buffer is a "reference" to the * original pointer, equivalent to the following C code: * * ``` c * char *buf = buffer; * char **ref = &buf; * ``` * * @param {Buffer} buffer A Buffer instance to create a reference to. * @return {Buffer} A new Buffer instance pointing to _buffer_. */ exports.ref = function ref (buffer) { debug('creating a reference to buffer', buffer) var type = exports.refType(exports.getType(buffer)) return exports.alloc(type, buffer) } /** * Accepts a Buffer instance and attempts to "dereference" it. * That is, first it checks the `indirection` count of _buffer_'s "type", and if * it's greater than __1__ then it merely returns another Buffer, but with one * level less `indirection`. * * When _buffer_'s indirection is at __1__, then it checks for `buffer.type` * which should be an Object with its own `get()` function. * * ``` * var buf = ref.alloc('int', 6); * * var val = ref.deref(buf); * console.log(val); * 6 * ``` * * * @param {Buffer} buffer A Buffer instance to dereference. * @return {?} The returned value after dereferencing _buffer_. */ exports.deref = function deref (buffer) { debug('dereferencing buffer', buffer) return exports.get(buffer) } /** * Attaches _object_ to _buffer_ such that it prevents _object_ from being garbage * collected until _buffer_ does. * * @param {Buffer} buffer A Buffer instance to attach _object_ to. * @param {Object|Buffer} object An Object or Buffer to prevent from being garbage collected until _buffer_ does. * @api private */ exports._attach = function _attach (buf, obj) { if (!buf._refs) { buf._refs = [] } buf._refs.push(obj) } /** * Same as `ref.writeObject()`, except that this version does not _attach_ the * Object to the Buffer, which is potentially unsafe if the garbage collector * runs. * * @param {Buffer} buffer A Buffer instance to write _object_ to. * @param {Number} offset The offset on the Buffer to start writing at. * @param {Object} object The Object to be written into _buffer_. * @api private */ exports._writeObject = exports.writeObject /** * Writes a pointer to _object_ into _buffer_ at the specified _offset. * * This function "attaches" _object_ to _buffer_ to prevent it from being garbage * collected. * * ``` * var buf = ref.alloc('Object'); * ref.writeObject(buf, 0, { foo: 'bar' }); * * ``` * * @param {Buffer} buffer A Buffer instance to write _object_ to. * @param {Number} offset The offset on the Buffer to start writing at. * @param {Object} object The Object to be written into _buffer_. */ exports.writeObject = function writeObject (buf, offset, obj, persistent) { debug('writing Object to buffer', buf, offset, obj, persistent) exports._writeObject(buf, offset, obj, persistent) exports._attach(buf, obj) } /** * Same as `ref.writePointer()`, except that this version does not attach * _pointer_ to _buffer_, which is potentially unsafe if the garbage collector * runs. * * @param {Buffer} buffer A Buffer instance to write _pointer to. * @param {Number} offset The offset on the Buffer to start writing at. * @param {Buffer} pointer The Buffer instance whose memory address will be written to _buffer_. * @api private */ exports._writePointer = exports.writePointer /** * Writes the memory address of _pointer_ to _buffer_ at the specified _offset_. * * This function "attaches" _object_ to _buffer_ to prevent it from being garbage * collected. * * ``` * var someBuffer = new Buffer('whatever'); * var buf = ref.alloc('pointer'); * ref.writePointer(buf, 0, someBuffer); * ``` * * @param {Buffer} buffer A Buffer instance to write _pointer to. * @param {Number} offset The offset on the Buffer to start writing at. * @param {Buffer} pointer The Buffer instance whose memory address will be written to _buffer_. */ exports.writePointer = function writePointer (buf, offset, ptr) { debug('writing pointer to buffer', buf, offset, ptr) exports._writePointer(buf, offset, ptr) exports._attach(buf, ptr) } /** * Same as `ref.reinterpret()`, except that this version does not attach * _buffer_ to the returned Buffer, which is potentially unsafe if the * garbage collector runs. * * @param {Buffer} buffer A Buffer instance to base the returned Buffer off of. * @param {Number} size The `length` property of the returned Buffer. * @param {Number} offset The offset of the Buffer to begin from. * @return {Buffer} A new Buffer instance with the same memory address as _buffer_, and the requested _size_. * @api private */ exports._reinterpret = exports.reinterpret /** * Returns a new Buffer instance with the specified _size_, with the same memory * address as _buffer_. * * This function "attaches" _buffer_ to the returned Buffer to prevent it from * being garbage collected. * * @param {Buffer} buffer A Buffer instance to base the returned Buffer off of. * @param {Number} size The `length` property of the returned Buffer. * @param {Number} offset The offset of the Buffer to begin from. * @return {Buffer} A new Buffer instance with the same memory address as _buffer_, and the requested _size_. */ exports.reinterpret = function reinterpret (buffer, size, offset) { debug('reinterpreting buffer to "%d" bytes', size) var rtn = exports._reinterpret(buffer, size, offset || 0) exports._attach(rtn, buffer) return rtn } /** * Same as `ref.reinterpretUntilZeros()`, except that this version does not * attach _buffer_ to the returned Buffer, which is potentially unsafe if the * garbage collector runs. * * @param {Buffer} buffer A Buffer instance to base the returned Buffer off of. * @param {Number} size The number of sequential, aligned `NULL` bytes that are required to terminate the buffer. * @param {Number} offset The offset of the Buffer to begin from. * @return {Buffer} A new Buffer instance with the same memory address as _buffer_, and a variable `length` that is terminated by _size_ NUL bytes. * @api private */ exports._reinterpretUntilZeros = exports.reinterpretUntilZeros /** * Accepts a `Buffer` instance and a number of `NULL` bytes to read from the * pointer. This function will scan past the boundary of the Buffer's `length` * until it finds `size` number of aligned `NULL` bytes. * * This is useful for finding the end of NUL-termintated array or C string. For * example, the `readCString()` function _could_ be implemented like: * * ``` * function readCString (buf) { * return ref.reinterpretUntilZeros(buf, 1).toString('utf8') * } * ``` * * This function "attaches" _buffer_ to the returned Buffer to prevent it from * being garbage collected. * * @param {Buffer} buffer A Buffer instance to base the returned Buffer off of. * @param {Number} size The number of sequential, aligned `NULL` bytes are required to terminate the buffer. * @param {Number} offset The offset of the Buffer to begin from. * @return {Buffer} A new Buffer instance with the same memory address as _buffer_, and a variable `length` that is terminated by _size_ NUL bytes. */ exports.reinterpretUntilZeros = function reinterpretUntilZeros (buffer, size, offset) { debug('reinterpreting buffer to until "%d" NULL (0) bytes are found', size) var rtn = exports._reinterpretUntilZeros(buffer, size, offset || 0) exports._attach(rtn, buffer) return rtn } // the built-in "types" var types = exports.types = {} /** * The `void` type. * * @section types */ types.void = { size: 0 , indirection: 1 , get: function get (buf, offset) { debug('getting `void` type (returns `null`)') return null } , set: function set (buf, offset, val) { debug('setting `void` type (no-op)') } } /** * The `int8` type. */ types.int8 = { size: exports.sizeof.int8 , indirection: 1 , get: function get (buf, offset) { return buf.readInt8(offset || 0) } , set: function set (buf, offset, val) { if (typeof val === 'string') { val = val.charCodeAt(0) } return buf.writeInt8(val, offset || 0) } } /** * The `uint8` type. */ types.uint8 = { size: exports.sizeof.uint8 , indirection: 1 , get: function get (buf, offset) { return buf.readUInt8(offset || 0) } , set: function set (buf, offset, val) { if (typeof val === 'string') { val = val.charCodeAt(0) } return buf.writeUInt8(val, offset || 0) } } /** * The `int16` type. */ types.int16 = { size: exports.sizeof.int16 , indirection: 1 , get: function get (buf, offset) { return buf['readInt16' + exports.endianness](offset || 0) } , set: function set (buf, offset, val) { return buf['writeInt16' + exports.endianness](val, offset || 0) } } /** * The `uint16` type. */ types.uint16 = { size: exports.sizeof.uint16 , indirection: 1 , get: function get (buf, offset) { return buf['readUInt16' + exports.endianness](offset || 0) } , set: function set (buf, offset, val) { return buf['writeUInt16' + exports.endianness](val, offset || 0) } } /** * The `int32` type. */ types.int32 = { size: exports.sizeof.int32 , indirection: 1 , get: function get (buf, offset) { return buf['readInt32' + exports.endianness](offset || 0) } , set: function set (buf, offset, val) { return buf['writeInt32' + exports.endianness](val, offset || 0) } } /** * The `uint32` type. */ types.uint32 = { size: exports.sizeof.uint32 , indirection: 1 , get: function get (buf, offset) { return buf['readUInt32' + exports.endianness](offset || 0) } , set: function set (buf, offset, val) { return buf['writeUInt32' + exports.endianness](val, offset || 0) } } /** * The `int64` type. */ types.int64 = { size: exports.sizeof.int64 , indirection: 1 , get: function get (buf, offset) { return buf['readInt64' + exports.endianness](offset || 0) } , set: function set (buf, offset, val) { return buf['writeInt64' + exports.endianness](val, offset || 0) } } /** * The `uint64` type. */ types.uint64 = { size: exports.sizeof.uint64 , indirection: 1 , get: function get (buf, offset) { return buf['readUInt64' + exports.endianness](offset || 0) } , set: function set (buf, offset, val) { return buf['writeUInt64' + exports.endianness](val, offset || 0) } } /** * The `float` type. */ types.float = { size: exports.sizeof.float , indirection: 1 , get: function get (buf, offset) { return buf['readFloat' + exports.endianness](offset || 0) } , set: function set (buf, offset, val) { return buf['writeFloat' + exports.endianness](val, offset || 0) } } /** * The `double` type. */ types.double = { size: exports.sizeof.double , indirection: 1 , get: function get (buf, offset) { return buf['readDouble' + exports.endianness](offset || 0) } , set: function set (buf, offset, val) { return buf['writeDouble' + exports.endianness](val, offset || 0) } } /** * The `Object` type. This can be used to read/write regular JS Objects * into raw memory. */ types.Object = { size: exports.sizeof.Object , indirection: 1 , get: function get (buf, offset) { return buf.readObject(offset || 0) } , set: function set (buf, offset, val) { return buf.writeObject(val, offset || 0) } } /** * The `CString` (a.k.a `"string"`) type. * * CStrings are a kind of weird thing. We say it's `sizeof(char *)`, and * `indirection` level of 1, which means that we have to return a Buffer that * is pointer sized, and points to a some utf8 string data, so we have to create * a 2nd "in-between" buffer. */ types.CString = { size: exports.sizeof.pointer , alignment: exports.alignof.pointer , indirection: 1 , get: function get (buf, offset) { var _buf = exports.readPointer(buf, offset) if (exports.isNull(_buf)) { return null } return exports.readCString(_buf, 0) } , set: function set (buf, offset, val) { var _buf if (Buffer.isBuffer(val)) { _buf = val } else { // assume string _buf = exports.allocCString(val) } return exports.writePointer(buf, offset, _buf) } } // alias Utf8String var utfstringwarned = false Object.defineProperty(types, 'Utf8String', { enumerable: false , configurable: true , get: function () { if (!utfstringwarned) { utfstringwarned = true console.error('"Utf8String" type is deprecated, use "CString" instead') } return types.CString } }) /** * The `bool` type. * * Wrapper type around `types.uint8` that accepts/returns `true` or * `false` Boolean JavaScript values. * * @name bool * */ /** * The `byte` type. * * @name byte */ /** * The `char` type. * * @name char */ /** * The `uchar` type. * * @name uchar */ /** * The `short` type. * * @name short */ /** * The `ushort` type. * * @name ushort */ /** * The `int` type. * * @name int */ /** * The `uint` type. * * @name uint */ /** * The `long` type. * * @name long */ /** * The `ulong` type. * * @name ulong */ /** * The `longlong` type. * * @name longlong */ /** * The `ulonglong` type. * * @name ulonglong */ /** * The `size_t` type. * * @name size_t */ // "typedef"s for the variable-sized types ;[ 'bool', 'byte', 'char', 'uchar', 'short', 'ushort', 'int', 'uint', 'long' , 'ulong', 'longlong', 'ulonglong', 'size_t' ].forEach(function (name) { var unsigned = name === 'bool' || name === 'byte' || name === 'size_t' || name[0] === 'u' var size = exports.sizeof[name] assert(size >= 1 && size <= 8) var typeName = 'int' + (size * 8) if (unsigned) { typeName = 'u' + typeName } var type = exports.types[typeName] assert(type) exports.types[name] = Object.create(type) }) // set the "alignment" property on the built-in types Object.keys(exports.alignof).forEach(function (name) { if (name === 'pointer') return exports.types[name].alignment = exports.alignof[name] assert(exports.types[name].alignment > 0) }) // make the `bool` type work with JS true/false values exports.types.bool.get = (function (_get) { return function get (buf, offset) { return _get(buf, offset) ? true : false } })(exports.types.bool.get) exports.types.bool.set = (function (_set) { return function set (buf, offset, val) { if (typeof val !== 'number') { val = val ? 1 : 0 } return _set(buf, offset, val) } })(exports.types.bool.set) /*! * Set the `name` property of the types. Used for debugging... */ Object.keys(exports.types).forEach(function (name) { exports.types[name].name = name }) /*! * This `char *` type is used by "allocCString()" above. */ var charPtrType = exports.refType(exports.types.char) /*! * Set the `type` property of the `NULL` pointer Buffer object. */ exports.NULL.type = exports.types.void /** * `NULL_POINTER` is a pointer-sized `Buffer` instance pointing to `NULL`. * Conceptually, it's equivalent to the following C code: * * ``` c * char *null_pointer; * null_pointer = NULL; * ``` * * @type Buffer */ exports.NULL_POINTER = exports.ref(exports.NULL) /** * All these '...' comment blocks below are for the documentation generator. * * @section buffer */ Buffer.prototype.address = function address () { return exports.address(this, 0) } /** * ... */ Buffer.prototype.hexAddress = function hexAddress () { return exports.hexAddress(this, 0) } /** * ... */ Buffer.prototype.isNull = function isNull () { return exports.isNull(this, 0) } /** * ... */ Buffer.prototype.ref = function ref () { return exports.ref(this) } /** * ... */ Buffer.prototype.deref = function deref () { return exports.deref(this) } /** * ... */ Buffer.prototype.readObject = function readObject (offset) { return exports.readObject(this, offset) } /** * ... */ Buffer.prototype.writeObject = function writeObject (obj, offset) { return exports.writeObject(this, offset, obj) } /** * ... */ Buffer.prototype.readPointer = function readPointer (offset, size) { return exports.readPointer(this, offset, size) } /** * ... */ Buffer.prototype.writePointer = function writePointer (ptr, offset) { return exports.writePointer(this, offset, ptr) } /** * ... */ Buffer.prototype.readCString = function readCString (offset) { return exports.readCString(this, offset) } /** * ... */ Buffer.prototype.writeCString = function writeCString (string, offset, encoding) { return exports.writeCString(this, offset, string, encoding) } /** * ... */ Buffer.prototype.readInt64BE = function readInt64BE (offset) { return exports.readInt64BE(this, offset) } /** * ... */ Buffer.prototype.writeInt64BE = function writeInt64BE (val, offset) { return exports.writeInt64BE(this, offset, val) } /** * ... */ Buffer.prototype.readUInt64BE = function readUInt64BE (offset) { return exports.readUInt64BE(this, offset) } /** * ... */ Buffer.prototype.writeUInt64BE = function writeUInt64BE (val, offset) { return exports.writeUInt64BE(this, offset, val) } /** * ... */ Buffer.prototype.readInt64LE = function readInt64LE (offset) { return exports.readInt64LE(this, offset) } /** * ... */ Buffer.prototype.writeInt64LE = function writeInt64LE (val, offset) { return exports.writeInt64LE(this, offset, val) } /** * ... */ Buffer.prototype.readUInt64LE = function readUInt64LE (offset) { return exports.readUInt64LE(this, offset) } /** * ... */ Buffer.prototype.writeUInt64LE = function writeUInt64LE (val, offset) { return exports.writeUInt64LE(this, offset, val) } /** * ... */ Buffer.prototype.reinterpret = function reinterpret (size, offset) { return exports.reinterpret(this, size, offset) } /** * ... */ Buffer.prototype.reinterpretUntilZeros = function reinterpretUntilZeros (size, offset) { return exports.reinterpretUntilZeros(this, size, offset) } /** * `ref` overwrites the default `Buffer#inspect()` function to include the * hex-encoded memory address of the Buffer instance when invoked. * * This is simply a nice-to-have. * * **Before**: * * ``` js * console.log(new Buffer('ref')); * * ``` * * **After**: * * ``` js * console.log(new Buffer('ref')); * * ``` */ var inspectSym = inspect.custom || 'inspect' Buffer.prototype[inspectSym] = overwriteInspect(Buffer.prototype[inspectSym]) // does SlowBuffer inherit from Buffer? (node >= v0.7.9) if (!(exports.NULL instanceof Buffer)) { debug('extending SlowBuffer\'s prototype since it doesn\'t inherit from Buffer.prototype') /*! * SlowBuffer convenience methods. */ var SlowBuffer = require('buffer').SlowBuffer SlowBuffer.prototype.address = Buffer.prototype.address SlowBuffer.prototype.hexAddress = Buffer.prototype.hexAddress SlowBuffer.prototype.isNull = Buffer.prototype.isNull SlowBuffer.prototype.ref = Buffer.prototype.ref SlowBuffer.prototype.deref = Buffer.prototype.deref SlowBuffer.prototype.readObject = Buffer.prototype.readObject SlowBuffer.prototype.writeObject = Buffer.prototype.writeObject SlowBuffer.prototype.readPointer = Buffer.prototype.readPointer SlowBuffer.prototype.writePointer = Buffer.prototype.writePointer SlowBuffer.prototype.readCString = Buffer.prototype.readCString SlowBuffer.prototype.writeCString = Buffer.prototype.writeCString SlowBuffer.prototype.reinterpret = Buffer.prototype.reinterpret SlowBuffer.prototype.reinterpretUntilZeros = Buffer.prototype.reinterpretUntilZeros SlowBuffer.prototype.readInt64BE = Buffer.prototype.readInt64BE SlowBuffer.prototype.writeInt64BE = Buffer.prototype.writeInt64BE SlowBuffer.prototype.readUInt64BE = Buffer.prototype.readUInt64BE SlowBuffer.prototype.writeUInt64BE = Buffer.prototype.writeUInt64BE SlowBuffer.prototype.readInt64LE = Buffer.prototype.readInt64LE SlowBuffer.prototype.writeInt64LE = Buffer.prototype.writeInt64LE SlowBuffer.prototype.readUInt64LE = Buffer.prototype.readUInt64LE SlowBuffer.prototype.writeUInt64LE = Buffer.prototype.writeUInt64LE SlowBuffer.prototype[inspectSym] = overwriteInspect(SlowBuffer.prototype[inspectSym]) } function overwriteInspect (inspect) { if (inspect.name === 'refinspect') { return inspect } else { return function refinspect () { var v = inspect.apply(this, arguments) return v.replace('Buffer', 'Buffer@0x' + this.hexAddress()) } } }