diff --git a/.gitignore b/.gitignore index 0a87614..06636c8 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,5 @@ logs results node_modules npm-debug.log -coverage \ No newline at end of file +coverage +build \ No newline at end of file diff --git a/.npmignore b/.npmignore index 6bcbf6f..6d4d0f3 100644 --- a/.npmignore +++ b/.npmignore @@ -2,4 +2,5 @@ .idea* node_modules/ npm-debug.log -coverage \ No newline at end of file +coverage +src \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 9d67de5..7473b03 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,14 @@ language: node_js node_js: - - 0.10 - - 0.12 - 4 - 6 + - 7 - stable +before_script: + - npm install -g typescript + - tsc -p ./ + script: "npm run coverage" # Send coverage data to Coveralls after_script: "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js" \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..f5dd97d --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,10 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "0.1.0", + "command": "tsc", + "isShellCommand": true, + "args": ["-p", "./"], + "showOutput": "silent", + "problemMatcher": "$tsc" +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..2e51e82 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,18 @@ +# Change Log + +## 2.0 +> Relased 01/30/2017 + +### New Features: + +* Entire package re-written in TypeScript (2.1) +* Backwards compatibility is preserved for now +* New factory methods for creating SmartBuffer instances + * SmartBuffer.fromSize() + * SmartBuffer.fromBuffer() + * SmartBuffer.fromOptions() +* New SmartBufferOptions constructor options +* Added additional tests + +### Bug Fixes: +* Fixes a bug where reading null terminated strings may result in an exception. \ No newline at end of file diff --git a/LICENSE b/LICENSE index b2442a9..aab5771 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013 Josh Glazebrook +Copyright (c) 2013-2017 Josh Glazebrook Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/README.md b/README.md index cf49d72..4ef7fef 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,12 @@ Key Features: * Grows the internal Buffer as you add data to it. * Useful string operations. (Null terminating strings) * Allows for inserting values at specific points in the internal Buffer. +* Built in TypeScript +* Type Definitions Provided + +Requirements: +* Node v4.0+ is supported at this time. (Versions prior to 2.0 will work on node 0.10) + #### Note: smart-buffer can be used for writing to an underlying buffer as well as reading from it. It however does not function correctly if you're mixing both read and write operations with each other. @@ -22,6 +28,10 @@ smart-buffer can be used for writing to an underlying buffer as well as reading `npm install smart-buffer` +or + +`yarn install smart-buffer` + ## Using smart-buffer ### Example @@ -34,7 +44,7 @@ To build this packet using the vanilla Buffer class, you would have to count up ```javascript function createLoginPacket(username, password, age, country) { - var packet = new SmartBuffer(); + let packet = new SmartBuffer(); packet.writeUInt16LE(0x0060); // Login Packet Type/ID packet.writeStringNT(username); packet.writeStringNT(password); @@ -47,7 +57,7 @@ function createLoginPacket(username, password, age, country) { ``` With the above function, you now can do this: ```javascript -var login = createLoginPacket("Josh", "secret123", 22, "United States"); +let login = createLoginPacket("Josh", "secret123", 22, "United States"); // ``` @@ -56,9 +66,9 @@ Notice that the `[PacketLength:2]` part of the packet was inserted after we had Reading back the packet we created above is just as easy: ```javascript -var reader = new SmartBuffer(login); +let reader = SmartBuffer.fromBuffer(login); -var logininfo = { +let logininfo = { packetType: reader.readUInt16LE(), packetLength: reader.readUInt16LE(), username: reader.readStringNT(), @@ -83,20 +93,43 @@ var logininfo = { ### Constructing a smart-buffer -smart-buffer has a few different constructor signatures you can use. By default, utf8 encoding is used, and the internal Buffer length will be 4096. When reading from a Buffer, smart-buffer does NOT make a copy of the Buffer. It reads from the Buffer it was given. +smart-buffer has a few different ways to construct an instance. Starting with version 2.0, the following factory methods are preffered. ```javascript -var SmartBuffer = require('smart-buffer'); +let SmartBuffer = require('smart-buffer'); + +// Creating SmartBuffer from existing Buffer +let buff = SmartBuffer.fromBuffer(buffer); // Creates instance from buffer. (Uses default utf8 encoding) +let buff = SmartBuffer.fromBuffer(buffer, 'ascii'); // Creates instance from buffer with ascii encoding for Strings. -// Reading from an existing Buffer: -var reader = new SmartBuffer(buffer); -var reader = new SmartBuffer(buffer, 'ascii'); +// Creating SmartBuffer with specified internal Buffer size. +let buff = SmartBuffer.fromSize(1024); // Creates instance with internal Buffer size of 1024. +let buff = SmartBuffer.fromSize(1024, 'utf8'); // Creates instance with intenral Buffer size of 1024, and utf8 encoding. -// Writing to a new Buffer: -var writer = new SmartBuffer(); // Defaults to utf8, 4096 length internal Buffer. -var writer = new SmartBuffer(1024); // Defaults to utf8, 1024 length internal Buffer. -var writer = new SmartBuffer('ascii'); // Sets to ascii encoding, 4096 length internal buffer. -var writer = new SmartBuffer(1024, 'ascii'); // Sets to ascii encoding, 1024 length internal buffer. +// Creating SmartBuffer with options object. This one specifies size and encoding. +let buff = SmartBuffer.fromOptions({ + size: 1024, + encoding: 'ascii' +}); + +// Creating SmartBuffer with options object. This one specified an existing Buffer. +let buff = SmartBuffer.fromOptions({ + buff: buffer +}); + +// Just want a regular SmartBuffer with all default options? +let buff = new SmartBuffer(); +``` + +## Backwards Compatibility: + +All constructors used prior to 2.0 still are supported. However it's not recommended to use these. + +```javascript +let writer = new SmartBuffer(); // Defaults to utf8, 4096 length internal Buffer. +let writer = new SmartBuffer(1024); // Defaults to utf8, 1024 length internal Buffer. +let writer = new SmartBuffer('ascii'); // Sets to ascii encoding, 4096 length internal buffer. +let writer = new SmartBuffer(1024, 'ascii'); // Sets to ascii encoding, 1024 length internal buffer. ``` ## Reading Data @@ -124,8 +157,8 @@ Supported Operations: * readDoubleLE ```javascript -var reader = new SmartBuffer(somebuffer); -var num = reader.readInt8(); +let reader = new SmartBuffer(somebuffer); +let num = reader.readInt8(); ``` ## Reading String Values @@ -133,15 +166,15 @@ var num = reader.readInt8(); When reading String values, you can either choose to read a null terminated string, or a string of a specified length. ### SmartBuffer.readStringNT( [encoding] ) -> `String` **String encoding to use** - Defaults to the encoding set in the constructor, or utf8. +> `String` **String encoding to use** - Defaults to the encoding set in the constructor. returns `String` > Note: When readStringNT is called and there is no null character found, smart-buffer will read to the end of the internal Buffer. -### SmartBuffer.readString( [length], [encoding] ) ### SmartBuffer.readString( [length] ) ### SmartBuffer.readString( [encoding] ) +### SmartBuffer.readString( [length], [encoding] ) > `Number` **Length of the string to read** > `String` **String encoding to use** - Defaults to the encoding set in the constructor, or utf8. @@ -274,7 +307,7 @@ Rewinds the read position backwards by the given value. returns this -### SmartBuffer.skipTo( position ) +### SmartBuffer.moveTo( position ) > `Number` **The point to skip the read position to** Moves the read position to the given point. @@ -291,11 +324,6 @@ returns `Buffer` A Buffer containing the contents of the internal Buffer. returns `String` The internal Buffer in String representation. -### SmartBuffer.destroy() -Attempts to destroy the smart-buffer. - -returns this - ## Properties ### SmartBuffer.length diff --git a/lib/smart-buffer.js b/lib/smart-buffer.js deleted file mode 100644 index ea69cfc..0000000 --- a/lib/smart-buffer.js +++ /dev/null @@ -1,371 +0,0 @@ -var SmartBuffer = (function () { - - /** - * Constructor for SmartBuffer. - * @param arg1 {Buffer || Number || String} Buffer to read from, or expected size to write to, or encoding to use. - * @param arg2 {String} Encoding to use for writing and reading strings. Defaults to utf8. If encoding is given in arg1, this is ignored. - * @constructor - * - * There are a few ways to construct a SmartBuffer: - * - * SmartBuffer() - Defaults to utf8, 4096 pre-set internal Buffer length. - * SmartBuffer(size) - Defaults to utf8, sets internal Buffer length to the size given. - * SmartBuffer(encoding) - Sets the given encoding, defaults to 4096 pre-set internal Buffer length. - * SmartBuffer(Buffer) - Defaults to utf8, sets the internal Buffer to the given buffer (same memory). - * SmartBuffer(Buffer, encoding) - Sets the given encoding, sets the internal Buffer to the given buffer (same memory). - * - */ - function SmartBuffer(arg1, arg2) { - var type; - switch (type = typeof arg1) { - case 'number': - if (isFinite(arg1) && arg1 > 0) { - this.buff = new Buffer(Math.ceil(arg1)); - this.length = 0; - } else { - throw new Error('When specifying a size, it must be a valid number above zero.'); - } - break; - - case 'string': - if (Buffer.isEncoding(arg1)) { - this.buff = new Buffer(4096); - this.length = 0; - this.encoding = arg1; - } else { - throw new Error('Invalid Encoding'); - } - break; - - case 'object': - if (Buffer.isBuffer(arg1)) { - this.buff = arg1; - this.length = arg1.length; - } else { - throw new TypeError('First argument must be a Buffer, Number representing the size, or a String representing the encoding.'); - } - break; - - default: - this.buff = new Buffer(4096); - this.length = 0; - break; - } - - if (typeof this.encoding === 'undefined') { - if (typeof arg2 === 'string') { - if (Buffer.isEncoding(arg2)) { - this.encoding = arg2; - } else { - throw new Error('Invalid Encoding'); - } - } - } - - this._readOffset = 0; - this._writeOffset = 0; - } - - - SmartBuffer.prototype._ensureWritable = function (len, offset) { - this._ensureCapacity(this.length + len + (typeof offset === 'number' ? offset : 0)); - - if (typeof offset === 'number') { - this.buff.copy(this.buff, offset + len, offset, this.buff.length); - } - this.length = Math.max(this.length + len, (typeof offset === 'number' ? offset : 0) + len); - }; - - SmartBuffer.prototype._ensureCapacity = function (minlen) { - var oldlen = this.buff.length; - - if (minlen > oldlen) { - var data = this.buff; - var newlen = (oldlen * 3) / 2 + 1; - if (newlen < minlen) - newlen = minlen; - this.buff = new Buffer(newlen); - data.copy(this.buff, 0, 0, oldlen); - } - }; - - - var makeReader = function (func, size) { - return function () { - var ret = func.call(this.buff, this._readOffset); - this._readOffset += size; - return ret; - } - }; - - var makeWriter = function (func, size) { - return function (value, offset) { - this._ensureWritable(size, offset); - func.call(this.buff, value, typeof offset === 'number' ? offset : this._writeOffset); - this._writeOffset += size; - return this; - } - }; - - - /* - Read Operations - */ - - SmartBuffer.prototype.readInt8 = makeReader(Buffer.prototype.readInt8, 1); - SmartBuffer.prototype.readInt16BE = makeReader(Buffer.prototype.readInt16BE, 2); - SmartBuffer.prototype.readInt16LE = makeReader(Buffer.prototype.readInt16LE, 2); - SmartBuffer.prototype.readInt32BE = makeReader(Buffer.prototype.readInt32BE, 4); - SmartBuffer.prototype.readInt32LE = makeReader(Buffer.prototype.readInt32LE, 4); - - SmartBuffer.prototype.readUInt8 = makeReader(Buffer.prototype.readUInt8, 1); - SmartBuffer.prototype.readUInt16BE = makeReader(Buffer.prototype.readUInt16BE, 2); - SmartBuffer.prototype.readUInt16LE = makeReader(Buffer.prototype.readUInt16LE, 2); - SmartBuffer.prototype.readUInt32BE = makeReader(Buffer.prototype.readUInt32BE, 4); - SmartBuffer.prototype.readUInt32LE = makeReader(Buffer.prototype.readUInt32LE, 4); - - SmartBuffer.prototype.readFloatBE = makeReader(Buffer.prototype.readFloatBE, 4); - SmartBuffer.prototype.readFloatLE = makeReader(Buffer.prototype.readFloatLE, 4); - - SmartBuffer.prototype.readDoubleBE = makeReader(Buffer.prototype.readDoubleBE, 8); - SmartBuffer.prototype.readDoubleLE = makeReader(Buffer.prototype.readDoubleLE, 8); - - - /** - * Reads a string of the given length. - * @param length {Number} The length of the string to read. (Defaults to the length of the remaining data) - * @param encoding {String} The encoding to use. (Defaults to encoding set in constructor, or utf8) - * @returns {string} The string. - */ - SmartBuffer.prototype.readString = function (length, encoding) { - var len = Math.min(length, this.length - this._readOffset) || (this.length - this._readOffset); - var ret = this.buff.slice(this._readOffset, this._readOffset + len).toString(encoding || this.encoding); - this._readOffset += len; - return ret; - }; - - /** - * Reads a null terminated string from the underlying buffer. - * @param encoding {String} Encoding to use. Defaults to encoding set in constructor, or utf8. - * @returns {string} - */ - SmartBuffer.prototype.readStringNT = function (encoding) { - var nullpos = this.length; - for (var i = this._readOffset; i < this.length; i++) { - if (this.buff[i] == 0x00) { - nullpos = i; - break; - } - } - - var result = this.buff.slice(this._readOffset, nullpos); - this._readOffset = nullpos + 1; - - return result.toString(encoding || this.encoding); - }; - - - /** - * Reads a specified number of bytes. - * @param len {Number} Numbers of bytes to read. (Defaults to the remaining data length) - * @returns {Buffer} Buffer containing the read bytes. - */ - SmartBuffer.prototype.readBuffer = function (len) { - var endpoint = Math.min(this.length, this._readOffset + (typeof len === 'number' ? len : this.length)); - var ret = this.buff.slice(this._readOffset, endpoint); - this._readOffset = endpoint; - return ret; - }; - - /** - * Reads a null terminated sequence of bytes from the underlying buffer. - * @returns {Buffer} Buffer containing the read bytes. - */ - SmartBuffer.prototype.readBufferNT = function () { - var nullpos = this.length; - for (var i = this._readOffset; i < this.length; i++) { - if (this.buff[i] == 0x00) { - nullpos = i; - break; - } - } - - var ret = this.buff.slice(this._readOffset, nullpos); - this._readOffset = nullpos + 1; - - return ret; - }; - - - /* - Write Operations - */ - - - SmartBuffer.prototype.writeInt8 = makeWriter(Buffer.prototype.writeInt8, 1); - SmartBuffer.prototype.writeInt16BE = makeWriter(Buffer.prototype.writeInt16BE, 2); - SmartBuffer.prototype.writeInt16LE = makeWriter(Buffer.prototype.writeInt16LE, 2); - SmartBuffer.prototype.writeInt32BE = makeWriter(Buffer.prototype.writeInt32BE, 4); - SmartBuffer.prototype.writeInt32LE = makeWriter(Buffer.prototype.writeInt32LE, 4); - - SmartBuffer.prototype.writeUInt8 = makeWriter(Buffer.prototype.writeUInt8, 1); - SmartBuffer.prototype.writeUInt16BE = makeWriter(Buffer.prototype.writeUInt16BE, 2); - SmartBuffer.prototype.writeUInt16LE = makeWriter(Buffer.prototype.writeUInt16LE, 2); - SmartBuffer.prototype.writeUInt32BE = makeWriter(Buffer.prototype.writeUInt32BE, 4); - SmartBuffer.prototype.writeUInt32LE = makeWriter(Buffer.prototype.writeUInt32LE, 4); - - SmartBuffer.prototype.writeFloatBE = makeWriter(Buffer.prototype.writeFloatBE, 4); - SmartBuffer.prototype.writeFloatLE = makeWriter(Buffer.prototype.writeFloatLE, 4); - - SmartBuffer.prototype.writeDoubleBE = makeWriter(Buffer.prototype.writeDoubleBE, 8); - SmartBuffer.prototype.writeDoubleLE = makeWriter(Buffer.prototype.writeDoubleLE, 8); - - - /** - * Writes a string to the underlying buffer. - * @param value {String} The string to write. - * @param offset {Number} The offset to write the string to. (Encoding can also be set here in place of offset) - * @param encoding {String} The encoding to use. (Defaults to encoding set in constructor, or to utf8) - * @returns {*} - */ - SmartBuffer.prototype.writeString = function (value, offset, encoding) { - var len, _offset, type = typeof offset; - - if (type === 'number') { - _offset = offset; - } else if (type === 'string') { - encoding = offset; - offset = this._writeOffset; - } else { - encoding = undefined; - offset = this._writeOffset; - } - - len = Buffer.byteLength(value, encoding || this.encoding); - this._ensureWritable(len, _offset); - - this.buff.write(value, offset, len, encoding || this.encoding); - this._writeOffset += len; - return this; - }; - - /** - * Writes a null terminated string to the underlying buffer. - * @param value {String} The string to write. - * @param offset {Number} The offset to write the string to. (Encoding can also be set here in place of offset) - * @param encoding {String} The encoding to use. (Defaults to encoding set in constructor, or to utf8) - * @returns {*} - */ - SmartBuffer.prototype.writeStringNT = function (value, offset, encoding) { - this.writeString(value, offset, encoding); - this.writeUInt8(0x00, (typeof offset === 'number' ? offset + value.length : this._writeOffset)); - return this; - }; - - /** - * Writes a Buffer to the underlying buffer. - * @param value {Buffer} The buffer to write. - * @param offset {Number} The offset to write the Buffer to. - * @returns {*} - */ - SmartBuffer.prototype.writeBuffer = function (value, offset) { - var len = value.length; - this._ensureWritable(len, offset); - value.copy(this.buff, typeof offset === 'number' ? offset : this._writeOffset); - this._writeOffset += len; - return this; - }; - - /** - * Writes a null terminated Buffer to the underlying buffer. - * @param value {Buffer} The buffer to write. - * @param offset {Number} The offset to write the Buffer to. - * @returns {*} - */ - SmartBuffer.prototype.writeBufferNT = function (value, offset) { - this.writeBuffer(value, offset); - this.writeUInt8(0x00, (typeof offset === 'number' ? offset + value.length : this._writeOffset)); - - return this; - }; - - - /** - * Resets the Endless Buffer. - */ - SmartBuffer.prototype.clear = function () { - this._writeOffset = 0; - this._readOffset = 0; - this.length = 0; - }; - - /** - * Gets the remaining number of bytes to be read from the existing Buffer. - * @returns {number} The number of bytes remaining. - */ - SmartBuffer.prototype.remaining = function () { - return this.length - this._readOffset; - }; - - /** - * Skips the read position forward by the amount of given. - * @param amount {Number} The amount of bytes to skip forward. - */ - SmartBuffer.prototype.skip = function (amount) { - if (this._readOffset + amount > this.length) - throw new Error('Target position is beyond the bounds of the data.'); - - this._readOffset += amount; - }; - - /** - * Rewinds the read position backward by the amount given. - * @param amount {Number} The amount of bytes to reverse backward. - */ - SmartBuffer.prototype.rewind = function (amount) { - if (this._readOffset - amount < 0) - throw new Error('Target position is beyond the bounds of the data.'); - - this._readOffset -= amount; - }; - - /** - * Skips the read position to the given position. - * @param position {Number} The position to skip to. - */ - SmartBuffer.prototype.skipTo = function (position) { - if (position < 0 || position > this.length) - throw new Error('Target position is beyond the bounds of the data.'); - - this._readOffset = position; - }; - - /** - * Gets the underlying Buffer. - * @returns {*} - */ - SmartBuffer.prototype.toBuffer = function () { - return this.buff.slice(0, this.length); - }; - - /** - * Gets a string representation of the underlying Buffer. - * @param encoding {String} Encoding to use. (Defaults to encoding set in constructor, or utf8.) - * @returns {*} - */ - SmartBuffer.prototype.toString = function (encoding) { - return this.buff.toString(encoding || this.encoding, 0, this.length); - }; - - /** - * Destroys the underlying Buffer, and resets the SmartBuffer. - */ - SmartBuffer.prototype.destroy = function () { - delete this.buff; - this.clear(); - }; - - return SmartBuffer; -})(); - -module.exports = SmartBuffer; \ No newline at end of file diff --git a/package.json b/package.json index 2869f17..146f55e 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "smart-buffer", - "version": "1.0.13", + "version": "2.0.0", "description": "A smarter Buffer that keeps track of its own read and write positions while growing endlessly.", - "main": "lib/smart-buffer.js", + "main": "build/smartbuffer.js", "homepage": "https://github.com/JoshGlazebrook/smart-buffer/", "repository": { "type": "git", @@ -14,15 +14,15 @@ "keywords": [ "buffer", "smart", - "serialize", "packet", + "serialize", "network", "cursor", "simple" ], "engines": { - "node": ">= 0.10.15", - "npm": ">= 1.3.5" + "node": ">= 4.0.0", + "npm": ">= 3.0.0" }, "author": "Josh Glazebrook", "license": "MIT", @@ -35,10 +35,13 @@ "mocha-lcov-reporter": "^1.2.0" }, "typings": "typings/index", - "dependencies": {}, + "dependencies": { + "@types/node": "^7.0.4" + }, "scripts": { - "test": "mocha test/smart-buffer.test.js", + "test": "mocha test/smartbuffer.test.js", "coverage": "istanbul cover node_modules/mocha/bin/_mocha recursive test", - "fullcoverage": "node_modules/.bin/istanbul -include-all-sources cover node_modules/mocha/bin/_mocha recursive test" + "fullcoverage": "node_modules/.bin/istanbul -include-all-sources cover node_modules/mocha/bin/_mocha recursive test", + "prepublish": "npm install -g typescript && tsc -p ./" } } diff --git a/src/smartbuffer.ts b/src/smartbuffer.ts new file mode 100644 index 0000000..12b3db1 --- /dev/null +++ b/src/smartbuffer.ts @@ -0,0 +1,837 @@ + +/** + * Object interface for constructing new SmartBuffer instances. + */ +interface SmartBufferOptions { + // Buffer Encoding to use for reading/writing strings if one is not provided. + encoding?: BufferEncoding; + // The initial size of the internal Buffer. + size?: number; + // If a Buffer is provided, this Buffer's value will be used as the internal Buffer. + buff?: Buffer; +} + +// The default Buffer size if one is not provided. +const DEFAULT_SMARTBUFFER_SIZE = 4096; + +// The default string encoding to use for reading/writing strings. +const DEFAULT_SMARTBUFFER_ENCODING = 'utf8'; + +class SmartBuffer { + private buff: Buffer; + public length: number = 0; + public encoding: BufferEncoding = DEFAULT_SMARTBUFFER_ENCODING; + + private writeOffset: number = 0; + private readOffset: number = 0; + + + /** + * Creates a new SmartBuffer instance. + * + * @param arg1 { Number | BufferEncoding | Buffer | SmartBufferOptions } + * @param arg2 { BufferEncoding } + */ + constructor(arg1?: number | BufferEncoding | Buffer | SmartBufferOptions, arg2?: BufferEncoding) { + + // Initial buffer size provided + if (typeof arg1 === 'number') { + + if (Number.isFinite(arg1) && Number.isInteger(arg1) && arg1 > 0) { + this.buff = Buffer.allocUnsafe(arg1); + } else { + throw new Error('Invalid size provided. Size must be a valid integer greater than zero.'); + } + // String Encoding Provided + } else if (typeof arg1 === 'string') { + if (Buffer.isEncoding(arg1)) { + this.buff = Buffer.allocUnsafe(DEFAULT_SMARTBUFFER_SIZE); + this.encoding = arg1; + } else { + throw new Error('Invalid encoding provided. Please specify a valid encoding the internal Node.js Buffer supports.'); + } + // Buffer instance provided + } else if (arg1 instanceof Buffer) { + this.buff = arg1; + this.length = arg1.length; + } else if (SmartBuffer.isSmartBufferOptions(arg1)) { + + // Checks for encoding + if (arg1.encoding) { + if (Buffer.isEncoding(arg1.encoding)) { + this.encoding = arg1.encoding; + } else { + throw new Error('Invalid encoding provided. Please specify a valid encoding the internal Node.js Buffer supports.'); + } + } + + // Checks for initial size length + if (arg1.size) { + if (Number.isFinite(arg1.size) && Number.isInteger(arg1.size) && arg1.size > 0) { + this.buff = Buffer.allocUnsafe(arg1.size); + } else { + throw new Error('Invalid size provided. Size must be a valid integer greater than zero.'); + } + // Check for initial Buffer + } else if (arg1.buff) { + if (arg1.buff instanceof Buffer) { + this.buff = arg1.buff; + this.length = arg1.buff.length; + } else { + throw new Error('Invalid buffer provided in SmartBufferOptions.'); + } + } else { + this.buff = Buffer.allocUnsafe(DEFAULT_SMARTBUFFER_SIZE); + } + } else if (typeof arg1 === 'object') { + throw new Error('Invalid object supplied to SmartBuffer constructor.'); + } else { + this.buff = Buffer.allocUnsafe(DEFAULT_SMARTBUFFER_SIZE); + } + + // Check for encoding (Buffer, Encoding) constructor. + if (typeof arg2 === 'string') { + if (Buffer.isEncoding(arg2)) { + this.encoding = arg2; + } else { + throw new Error('Invalid encoding provided. Please specify a valid encoding the internal Node.js Buffer supports.'); + } + } + } + + /** + * Creates a new SmartBuffer instance with the provided internal Buffer size and optional encoding. + * + * @param size { Number } The size of the internal Buffer. + * @param encoding { String } The BufferEncoding to use for strings. + * + * @return { SmartBuffer } + */ + public static fromSize(size: number, encoding?: BufferEncoding): SmartBuffer { + return new this({ + size: size, + encoding: encoding + }); + } + + /** + * Creates a new SmartBuffer instance with the provided Buffer and optional encoding. + * + * @param buffer { Buffer } The Buffer to use as the internal Buffer value. + * @param encoding { String } The BufferEncoding to use for strings. + * + * @return { SmartBuffer } + */ + public static fromBuffer(buff: Buffer, encoding?: BufferEncoding): SmartBuffer { + return new this({ + buff: buff, + encoding: encoding + }); + } + + /** + * Creates a new SmartBuffer instance with the provided SmartBufferOptions options. + * + * @param options { SmartBufferOptions } The options to use when creating the SmartBuffer instance. + */ + public static fromOptions(options: SmartBufferOptions): SmartBuffer { + return new this(options); + } + + /** + * Ensures that the internal Buffer is large enough to write data. + * + * @param minLength { Number } The minimum length of the data that needs to be written. + * @param offset { Number } The offset of the data to be written. + */ + private ensureWriteable(minLength: number, offset?: number) { + const offsetVal = typeof offset === 'number' ? offset : 0; + + // Ensure there is enough internal Buffer capacity. + this.ensureCapacity(this.length + minLength + offsetVal); + + // If offset is provided, copy data into appropriate location in regards to the offset. + if (typeof offset === 'number') { + this.buff.copy(this.buff, offsetVal + minLength, offsetVal, this.buff.length); + } + + // Adjust instance length. + this.length = Math.max(this.length + minLength, offsetVal + minLength); + } + + + /** + * Ensures that the internal Buffer is large enough to write at least the given amount of data. + * + * @param minLength { Number } The minimum length of the data needs to be written. + */ + private ensureCapacity(minLength: number) { + const oldLength = this.buff.length; + + if (minLength > oldLength) { + let data = this.buff; + let newLength = (oldLength * 3) / 2 + 1; + if (newLength < minLength) { + newLength = minLength; + } + this.buff = Buffer.allocUnsafe(newLength); + + data.copy(this.buff, 0, 0, oldLength); + } + } + + /** + * Reads a numeric number value using the provided function. + * + * @param func { Function(offset: number) => number } The function to read data on the internal Buffer with. + * @param byteSize { Number } The number of bytes read. + * + * @param { Number } + */ + private readNumberValue(func: (offset: number) => number, byteSize: number) { + // Call Buffer.readXXXX(); + const value = func.call(this.buff, this.readOffset); + + // Adjust internal read offset + this.readOffset += byteSize; + + return value; + } + + /** + * Writes a numeric number value using the provided function. + * + * @param func { Function(offset: number, offset?) => number} The function to write data on the internal Buffer with. + * @param byteSize { Number } The number of bytes written. + * @param value { Number } The number value to write. + * @param offset { Number } the offset to write the number at. + * + */ + private writeNumberValue(func: (value: number, offset?: number) => number, byteSize: number, value: number, offset?: number) { + const offsetVal = typeof offset === 'number' ? offset : this.writeOffset; + + // Ensure there is enough internal Buffer capacity. (raw offset is passed) + this.ensureWriteable(byteSize, offset); + + // Call buffer.writeXXXX(); + func.call(this.buff, value, offsetVal); + + // Adjusts internal write offset + this.writeOffset += byteSize; + } + + + // Signed integers + + /** + * Reads an Int8 value from the current read position. + * + * @return { Number } + */ + readInt8(): number { + return this.readNumberValue(Buffer.prototype.readUInt8, 1); + } + + /** + * Reads an Int16BE value from the current read position. + * + * @return { Number } + */ + readInt16BE(): number { + return this.readNumberValue(Buffer.prototype.readUInt16BE, 2); + } + + /** + * Reads an Int16LE value from the current read position. + * + * @return { Number } + */ + readInt16LE(): number { + return this.readNumberValue(Buffer.prototype.readUInt16LE, 2); + } + + /** + * Reads an Int32BE value from the current read position. + * + * @return { Number } + */ + readInt32BE(): number { + return this.readNumberValue(Buffer.prototype.readUInt32BE, 4); + } + + /** + * Reads an Int32LE value from the current read position. + * + * @return { Number } + */ + readInt32LE(): number { + return this.readNumberValue(Buffer.prototype.readUInt32LE, 4); + } + + /** + * Writes an Int8 value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. + * + * @return this + */ + writeInt8(value: number, offset?: number): SmartBuffer { + this.writeNumberValue(Buffer.prototype.writeInt8, 1, value, offset); + return this; + } + + /** + * Writes an Int16BE value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. + * + * @return this + */ + writeInt16BE(value: number, offset?: number): SmartBuffer { + this.writeNumberValue(Buffer.prototype.writeInt16BE, 2, value, offset); + return this; + } + + /** + * Writes an Int16LE value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. + * + * @return this + */ + writeInt16LE(value: number, offset?: number): SmartBuffer { + this.writeNumberValue(Buffer.prototype.writeInt16LE, 2, value, offset); + return this; + } + + /** + * Writes an Int32BE value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. + * + * @return this + */ + writeInt32BE(value: number, offset?: number): SmartBuffer { + this.writeNumberValue(Buffer.prototype.writeInt32BE, 4, value, offset); + return this; + } + + /** + * Writes an Int32LE value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. + * + * @return this + */ + writeInt32LE(value: number, offset?: number): SmartBuffer { + this.writeNumberValue(Buffer.prototype.writeInt32LE, 4, value, offset); + return this; + } + + // Unsigned Integers + + /** + * Reads an UInt8 value from the current read position. + * + * @return { Number } + */ + readUInt8(): number { + return this.readNumberValue(Buffer.prototype.readUInt8, 1); + } + + /** + * Reads an UInt16BE value from the current read position. + * + * @return { Number } + */ + readUInt16BE(): number { + return this.readNumberValue(Buffer.prototype.readUInt16BE, 2); + } + + /** + * Reads an UInt16LE value from the current read position. + * + * @return { Number } + */ + readUInt16LE(): number { + return this.readNumberValue(Buffer.prototype.readUInt16LE, 2); + } + + /** + * Reads an UInt32BE value from the current read position. + * + * @return { Number } + */ + readUInt32BE(): number { + return this.readNumberValue(Buffer.prototype.readUInt32BE, 4); + } + + /** + * Reads an UInt32LE value from the current read position. + * + * @return { Number } + */ + readUInt32LE(): number { + return this.readNumberValue(Buffer.prototype.readUInt32LE, 4); + } + + /** + * Writes an UInt8 value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. + * + * @return this + */ + writeUInt8(value: number, offset?: number): SmartBuffer { + this.writeNumberValue(Buffer.prototype.writeUInt8, 1, value, offset); + return this; + } + + /** + * Writes an UInt16BE value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. + * + * @return this + */ + writeUInt16BE(value: number, offset?: number): SmartBuffer { + this.writeNumberValue(Buffer.prototype.writeUInt16BE, 2, value, offset); + return this; + } + + /** + * Writes an UInt16LE value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. + * + * @return this + */ + writeUInt16LE(value: number, offset?: number): SmartBuffer { + this.writeNumberValue(Buffer.prototype.writeUInt16LE, 2, value, offset); + return this; + } + + /** + * Writes an UInt32BE value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. + * + * @return this + */ + writeUInt32BE(value: number, offset?: number): SmartBuffer { + this.writeNumberValue(Buffer.prototype.writeUInt32BE, 4, value, offset); + return this; + } + + /** + * Writes an UInt32LE value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. + * + * @return this + */ + writeUInt32LE(value: number, offset?: number): SmartBuffer { + this.writeNumberValue(Buffer.prototype.writeUInt32LE, 4, value, offset); + return this; + } + + // Floating Point + + + /** + * Reads an FloatBE value from the current read position. + * + * @return { Number } + */ + readFloatBE(): number { + return this.readNumberValue(Buffer.prototype.readFloatBE, 4); + } + + /** + * Reads an FloatLE value from the current read position. + * + * @return { Number } + */ + readFloatLE(): number { + return this.readNumberValue(Buffer.prototype.readFloatLE, 4); + } + + /** + * Writes a FloatBE value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. + * + * @return this + */ + writeFloatBE(value: number, offset?: number): SmartBuffer { + this.writeNumberValue(Buffer.prototype.writeFloatBE, 4, value, offset); + return this; + } + + /** + * Writes a FloatLE value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. + * + * @return this + */ + writeFloatLE(value: number, offset?: number): SmartBuffer { + this.writeNumberValue(Buffer.prototype.writeFloatLE, 4, value, offset); + return this; + } + + + // Double Floating Point + + /** + * Reads an DoublEBE value from the current read position. + * + * @return { Number } + */ + readDoubleBE(): number { + return this.readNumberValue(Buffer.prototype.readDoubleBE, 8); + } + + /** + * Reads an DoubleLE value from the current read position. + * + * @return { Number } + */ + readDoubleLE(): number { + return this.readNumberValue(Buffer.prototype.readDoubleLE, 8); + } + + /** + * Writes a DoubleBE value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. + * + * @return this + */ + writeDoubleBE(value: number, offset?: number): SmartBuffer { + this.writeNumberValue(Buffer.prototype.writeDoubleBE, 8, value, offset); + return this; + } + + /** + * Writes a DoubleLE value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. + * + * @return this + */ + writeDoubleLE(value: number, offset?: number): SmartBuffer { + this.writeNumberValue(Buffer.prototype.writeDoubleLE, 8, value, offset); + return this; + } + + + // Strings + + /** + * Reads a String from the current read position. + * + * @param length { Number } The number of bytes to read as a String. + * @param encoding { String } The BufferEncoding to use for the string (Defaults to instance level encoding). + * + * @return { String } + */ + readString(length?: number, encoding?: BufferEncoding): string { + const lengthVal = Math.min(length, this.length - this.readOffset) || this.length - this.readOffset; + const value = this.buff.slice(this.readOffset, this.readOffset + lengthVal).toString(encoding || this.encoding); + + this.readOffset += lengthVal; + return value; + } + + /** + * Writes a String to the current write position. + * + * @param value { String } The String value to write. + * @param arg2 { Number | String } The offset to write the string to, or the BufferEncoding to use. + * @param encoding { String } The BufferEncoding to use for writing strings (defaults to instance encoding). + */ + writeString(value: string, arg2?: number | BufferEncoding, encoding?: BufferEncoding) { + let offsetVal = this.writeOffset; + let encodingVal = this.encoding; + + // Check for offset + if (typeof arg2 === 'number') { + offsetVal = arg2; + // Check for encoding + } else if (typeof arg2 === 'string') { + if (Buffer.isEncoding(arg2)) { + encodingVal = arg2; + } else { + throw new Error('Invalid encoding provided. Please specify a valid encoding the internal Node.js Buffer supports.'); + } + } + + // Check for encoding (third param) + if (typeof encoding === 'string') { + if (Buffer.isEncoding(encoding)) { + encodingVal = encoding; + } else { + throw new Error('Invalid encoding provided. Please specify a valid encoding the internal Node.js Buffer supports.'); + } + } + + // Calculate bytelength of string. + const byteLength = Buffer.byteLength(value, encodingVal); + + // Ensure there is enough internal Buffer capacity. + this.ensureWriteable(byteLength, offsetVal); + + // Write value + this.buff.write(value, offsetVal, byteLength, encodingVal); + + // Increment internal Buffer write offset; + this.writeOffset += byteLength; + return this; + } + + /** + * Reads a null-terminated String from the current read position. + * + * @param encoding { String } The BufferEncoding to use for the string (Defaults to instance level encoding). + * + * @return { String } + */ + readStringNT(encoding?: BufferEncoding): string { + + // Set null character position to the end SmartBuffer instance. + let nullPos = this.length; + + // Find next null character (if one is not found, default from above is used) + for(let i = this.readOffset; i < this.length; i++) { + if (this.buff[i] === 0x00) { + nullPos = i; + break; + } + } + + // Read string value + const value = this.buff.slice(this.readOffset, nullPos); + + // Increment internal Buffer read offset + this.readOffset = nullPos + 1; + + return value.toString(encoding || this.encoding); + } + + /** + * Writes a null-terminated String to the current write position. + * + * @param value { String } The String value to write. + * @param arg2 { Number | String } The offset to write the string to, or the BufferEncoding to use. + * @param encoding { String } The BufferEncoding to use for writing strings (defaults to instance encoding). + */ + writeStringNT(value: string, offset?: number | BufferEncoding, encoding?: BufferEncoding) { + // Write Values + this.writeString(value, offset, encoding); + this.writeUInt8(0x00, (typeof offset === 'number' ? offset + value.length : this.writeOffset)); + } + + // Buffers + + /** + * Reads a Buffer from the internal read position. + * + * @param length { Number } The length of data to read as a Buffer. + * + * @return { Buffer } + */ + readBuffer(length?: number): Buffer { + const lengthVal = typeof length === 'number' ? length : this.length; + const endPoint = Math.min(this.length, this.readOffset + lengthVal); + + // Read buffer value + const value = this.buff.slice(this.readOffset, endPoint); + + // Increment internal Buffer read offset + this.readOffset = endPoint; + return value; + } + + /** + * Writes a Buffer to the current write position. + * + * @param value { Buffer } The Buffer to write. + * @param offset { Number } The offset to write the Buffer to. + */ + writeBuffer(value: Buffer, offset?: number) { + const offsetVal = typeof offset === 'number' ? offset : this.writeOffset; + + // Ensure there is enough internal Buffer capacity. + this.ensureWriteable(value.length, offsetVal); + + // Write buffer value + value.copy(this.buff, offsetVal); + + // Increment internal Buffer write offset + this.writeOffset += value.length; + return this; + } + + /** + * Reads a null-terminated Buffer from the current read poisiton. + * + * @return { Buffer } + */ + readBufferNT(): Buffer { + // Set null character position to the end SmartBuffer instance. + let nullPos = this.length; + + // Find next null character (if one is not found, default from above is used) + for(let i = this.readOffset; i < this.length; i++) { + if (this.buff[i] === 0x00) { + nullPos = i; + break; + } + } + + // Read value + const value = this.buff.slice(this.readOffset, nullPos); + + // Increment internal Buffer read offset + this.readOffset = nullPos + 1; + return value; + } + + /** + * Writes a null-terminated Buffer to the current write position. + * + * @param value { Buffer } The Buffer to write. + * @param offset { Number } The offset to write the Buffer to. + */ + writeBufferNT(value: Buffer, offset?: number) { + // Write Values + this.writeBuffer(value, offset); + this.writeUInt8(0, (typeof offset === 'number' ? offset + value.length : this.writeOffset)); + + return this; + } + + /** + * Clears the SmartBuffer instance to its original empty state. + */ + clear() { + this.writeOffset = 0; + this.readOffset = 0; + this.length = 0; + } + + /** + * Gets the remaining data left to be read from the SmartBuffer instance. + * + * @return { Number } + */ + remaining(): number { + return this.length - this.readOffset; + } + + /** + * Moves the read offset forward. + * + * @param amount { Number } The amount to move the read offset forward by. + */ + skip(amount: number) { + if (this.readOffset + amount > this.length) { + throw new Error('Target position is beyond the bounds of the SmartBuffer size.'); + } + + this.readOffset += amount; + } + + /** + * Moves the read offset backwards. + * + * @param amount { Number } The amount to move the read offset backwards by. + */ + rewind(amount: number) { + if (this.readOffset - amount < 0) { + throw new Error('Target position is beyond the bounds of the SmartBuffer size.'); + } + + this.readOffset -= amount; + } + + /** + * Moves the read offset to a specific position. + * + * @param position { Number } The position to move the read offset to. + */ + skipTo(position: number) { + this.moveTo(position); + } + + /** + * Moves the read offset to a specific position. + * + * @param position { Number } The position to move the read offset to. + */ + moveTo(position: number) { + if (position > this.length) { + throw new Error('Target position is beyond the bounds of the SmartBuffer size.'); + } + + this.readOffset = position; + } + + /** + * Gets the value of the internal managed Buffer + * + * @param { Buffer } + */ + toBuffer(): Buffer { + return this.buff.slice(0, this.length); + } + + /** + * Gets the String value of the internal managed Buffer + * + * @param encoding { String } The BufferEncoding to display the Buffer as (defaults to instance level encoding). + */ + toString(encoding?: BufferEncoding) { + const encodingVal = typeof encoding === 'string' ? encoding : this.encoding; + + if (Buffer.isEncoding(encodingVal)) { + return this.buff.toString(encodingVal, 0, this.length); + } else { + throw new Error('Invalid encoding provided. Please specify a valid encoding the internal Node.js Buffer supports.'); + } + } + + /** + * Destroys the SmartBuffer instance. + */ + destroy() { + this.clear(); + } + + /** + * Type checking function that determines if an object is a SmartBufferOptions object. + */ + static isSmartBufferOptions(options: SmartBufferOptions): options is SmartBufferOptions { + const castOptions = (options); + + return castOptions && (castOptions.encoding !== undefined || castOptions.size !== undefined || castOptions.buff !== undefined); + } +} + + +export = SmartBuffer; \ No newline at end of file diff --git a/test/smart-buffer.test.js b/test/smartbuffer.test.js similarity index 73% rename from test/smart-buffer.test.js rename to test/smartbuffer.test.js index 0897d54..e929926 100644 --- a/test/smart-buffer.test.js +++ b/test/smartbuffer.test.js @@ -1,7 +1,6 @@ -var SmartBuffer = require('../lib/smart-buffer.js'); +var SmartBuffer = require('../build/smartbuffer'); var assert = require('chai').assert; - describe('Constructing a SmartBuffer', function () { describe('Constructing with an existing Buffer', function () { var buff = new Buffer([0xAA, 0xBB, 0xCC, 0xDD, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99]); @@ -53,6 +52,68 @@ describe('Constructing a SmartBuffer', function () { }); + describe('Constructing with SmartBufferOptions', function () { + var validOptions1 = { + size: 1024, + encoding: 'ascii' + }; + + var validOptions2 = { + buff: Buffer.alloc(1024) + } + + var validOptions3 = { + encoding: 'utf8' + }; + + var invalidOptions1 = { + encoding: 'invalid' + }; + + var invalidOptions2 = { + size: -1 + }; + + var invalidOptions3 = { + buff: 'notabuffer' + }; + + it('should create a SmartBuffer with size 1024 and ascii encoding', function () { + var sbuff = new SmartBuffer(validOptions1); + assert.strictEqual(sbuff.encoding, validOptions1.encoding); + assert.strictEqual(sbuff.buff.length, validOptions1.size); + }); + + it('should create a SmartBuffer with the provided buffer as the initial value', function () { + var sbuff = new SmartBuffer(validOptions2); + assert.deepEqual(sbuff.buff, validOptions2.buff); + }); + + it('should create a SmartBuffer with the provided ascii encoding, and create a default buffer size', function () { + var sbuff = new SmartBuffer(validOptions3); + assert.strictEqual(sbuff.encoding, validOptions3.encoding); + assert.strictEqual(sbuff.buff.length, 4096); + }) + + it('should throw an error when given an options object with an invalid encoding', function () { + assert.throws(function() { + var sbuff = new SmartBuffer(invalidOptions1); + }); + }); + + it('should throw an error when given an options object with an invalid size', function () { + assert.throws(function() { + var sbuff = new SmartBuffer(invalidOptions2); + }); + }); + + it('should throw an error when given an options object with an invalid buffer', function () { + assert.throws(function() { + var sbuff = new SmartBuffer(invalidOptions3); + }); + }); + }); + describe('Constructing with invalid parameters', function () { it('should throw an exception when given an invalid number size', function () { assert.throws(function () { @@ -73,9 +134,39 @@ describe('Constructing a SmartBuffer', function () { it('should throw and exception when given an object that is not a Buffer', function () { assert.throws(function () { var reader = new SmartBuffer(null); - }, TypeError); + }, Error); }); }); + + describe('Constructing with factory methods', function () { + var originalBuffer = new Buffer(10); + + var sbuff1 = SmartBuffer.fromBuffer(originalBuffer); + + it('Should create a SmartBuffer with a provided internal Buffer as the initial value', function () { + assert.deepEqual(sbuff1.buff,originalBuffer); + }); + + var sbuff2 = SmartBuffer.fromSize(1024); + + it('Should create a SmartBuffer with a set provided initial Buffer size', function () { + assert.strictEqual(sbuff2.buff.length, 1024); + }); + + var options = { + size: 1024, + encoding: 'ascii' + }; + + var sbuff3 = SmartBuffer.fromOptions(options); + + it('Should create a SmartBuffer instance with a given SmartBufferOptions object', function () { + assert.strictEqual(sbuff3.encoding, options.encoding); + assert.strictEqual(sbuff3.buff.length, options.size); + }); + + + }); }); @@ -100,8 +191,10 @@ describe('Reading/Writing To/From SmartBuffer', function () { reader.writeFloatLE(1.234); reader.writeDoubleBE(1.234567890); reader.writeDoubleLE(1.234567890); + reader.writeUInt8(200, 0); it('should equal the correct values that were written above', function () { + assert.strictEqual(reader.readUInt8(), 200); assert.strictEqual(reader.readInt8(), 0x44); assert.strictEqual(reader.readUInt8(), 0xFF); assert.strictEqual(reader.readInt16BE(), 0x6699); @@ -125,8 +218,10 @@ describe('Reading/Writing To/From SmartBuffer', function () { reader.writeStringNT('hello'); reader.writeString('world'); reader.writeStringNT('✎✏✎✏✎✏'); + reader.writeStringNT('first', 0); - it('should equal the correct strings that were written above', function () { + it('should equal the correct strings that were written prior', function () { + assert.strictEqual(reader.readStringNT(), 'first'); assert.strictEqual(reader.readStringNT(), 'hello'); assert.strictEqual(reader.readString(5), 'world'); assert.strictEqual(reader.readStringNT(), '✎✏✎✏✎✏'); @@ -137,13 +232,29 @@ describe('Reading/Writing To/From SmartBuffer', function () { var reader = new SmartBuffer('ascii'); reader.writeStringNT('some ascii text'); reader.writeStringNT('ѕσмє υтƒ8 тєχт', 'utf8'); + reader.writeStringNT('first', 0, 'ascii'); it('should equal the correct strings that were written above', function () { + assert.strictEqual(reader.readStringNT(), 'first'); assert.strictEqual(reader.readStringNT(), 'some ascii text'); assert.strictEqual(reader.readStringNT('utf8'), 'ѕσмє υтƒ8 тєχт'); }); + + it('should throw an error when an invalid encoding is provided', function() { + assert.throws(function() { + reader.writeString('hello', 'invalid'); + }); + }) + + it('should throw an error when an invalid encoding is provided along with a valid offset', function () { + assert.throws(function() { + reader.writeString('hellothere', 2, 'invalid'); + }); + }) }); + + describe('Null/non-null terminating strings', function () { var reader = new SmartBuffer(); reader.writeString('hello\0test\0bleh'); @@ -211,11 +322,8 @@ describe('Reading/Writing To/From SmartBuffer', function () { buff.writeStringNT('hello'); buff.writeBufferNT(frontBuff, 0); - console.log(buff); - it('should write the buffer to the front of the smart buffer instance', function () { var readBuff = buff.readBufferNT(); - console.log(readBuff); assert.deepEqual(readBuff, frontBuff); }); }); @@ -392,6 +500,12 @@ describe('Displaying the buffer as a string', function () { it('Should return a valid base64 string representing the internal buffer', function () { assert.strictEqual(str64, sbuff.toString('base64')); }); + + it('Should throw an error if an invalid encoding is provided', function () { + assert.throws(function() { + var strError = sbuff.toString('invalidEncoding'); + }); + }) }); describe('Destroying the buffer', function () { @@ -403,8 +517,34 @@ describe('Destroying the buffer', function () { it('Should have a length of zero when buffer is destroyed', function () { assert.strictEqual(0, writer.length); }); +}); + +describe('ensureWritable()', function () { + var sbuff = new SmartBuffer(10); + + it('should increase the internal buffer size to accomodate given size.', function () { + sbuff.ensureWriteable(100); + + assert.strictEqual(sbuff.buff.length >= 100, true); + }); +}); + +describe('isSmartBufferOptions()', function () { + it('should return true when encoding is defined', function () { + assert.strictEqual(SmartBuffer.isSmartBufferOptions({ + encoding: 'utf8' + }), true); + }); + + it('should return true when size is defined', function () { + assert.strictEqual(SmartBuffer.isSmartBufferOptions({ + size: 1024 + }), true); + }); - it('Should have no internal buff property when buffer is destroyed', function () { - assert.notProperty(writer, 'buff'); + it('should return true when buff is defined', function () { + assert.strictEqual(SmartBuffer.isSmartBufferOptions({ + buff: Buffer.alloc(4096) + }), true); }); }); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..4ad7582 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compileOnSave": true, + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "sourceMap": true, + "moduleResolution": "node", + "outDir": "build", + "watch": false, + "declaration": false, + "removeComments":false, + "noImplicitAny": true, + "typeRoots": [ + "node_modules/@types" + ] + }, + "exclude": [ + "node_modules", + ".idea", + "build", + "typings" + ] +} \ No newline at end of file diff --git a/typings/index.d.ts b/typings/index.d.ts index b567f1e..ae79234 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1,383 +1,443 @@ -// Type definitions for smart-buffer -// Project: https://github.com/JoshGlazebrook/smart-buffer -// Definitions by: Josh Glazebrook - - +/// +/** + * Object interface for constructing new SmartBuffer instances. + */ +interface SmartBufferOptions { + encoding?: BufferEncoding; + size?: number; + buff?: Buffer; +} declare class SmartBuffer { + private buff; + length: number; + encoding: BufferEncoding; + private writeOffset; + private readOffset; + /** - * Creates a new SmartBuffer instance (defaults to utf8 encoding) + * Creates a new SmartBuffer instance (defaults to utf8 encoding, 4096 internal Buffer size) */ constructor(); /** * Creates a new SmartBuffer instance * - * @param arg1 { Number } The size the underlying buffer instance should be instantiated to (defaults to 4096) - * @param arg2 { String } The string encoding to use for reading/writing strings (defaults to utf8) + * @param size { Number } The size the underlying buffer instance should be instantiated to (defaults to 4096) + * @param encoding { BufferEncoding } The string encoding to use for reading/writing strings (defaults to utf8) + * + * @deprecated The .fromXXX() factory methods are now preferred over the new instantiator method. */ - constructor(size: number, encoding?: string); + constructor(size: number, encoding?: BufferEncoding); /** * Creates a new SmartBuffer instance * - * @param arg1 { String } The string encoding to use for reading/writing strings (defaults to utf8) + * @param encoding { BufferEncoding } The string encoding to use for reading/writing strings (defaults to utf8) + * + * @deprecated The .fromXXX() factory methods are now preferred over the new instantiator method. */ - constructor(encoding?: string); + constructor(encoding?: BufferEncoding); /** * Creates a new SmartBuffer instance * - * @param arg1 { Buffer } An existing buffer instance to copy to this smart buffer instance - * @param arg2 { String } The string encoding to use for reading/writing strings (defaults to utf8) + * @param buff { Buffer } An existing buffer instance to copy to this smart buffer instance + * @param encoding { BufferEncoding } The string encoding to use for reading/writing strings (defaults to utf8) + * + * @deprecated The .fromXXX() factory methods are now preferred over the new instantiator method. */ - constructor(buffer: Buffer, encoding?: string) - - - - // Signed number readers + constructor(buff: Buffer, encoding?: string); /** - * Reads a 8-bit signed integer + * Creates a new SmartBuffer instance + * + * @param options { SmartBufferOptions } The SmartBufferOptions settings to use when creating the SmartBuffer instance. + * */ - readInt8(): number; + constructor(options: SmartBufferOptions); /** - * Reads a 16-bit signed integer (big endian) + * Creates a new SmartBuffer instance with the provided internal Buffer size and optional encoding. + * + * @param size { Number } The size of the internal Buffer. + * @param encoding { String } The BufferEncoding to use for strings. + * + * @return { SmartBuffer } */ - readInt16BE(): number; - + static fromSize(size: number, encoding?: BufferEncoding): SmartBuffer; /** - * Reads a 16-bit signed integer (little endian) + * Creates a new SmartBuffer instance with the provided Buffer and optional encoding. + * + * @param buffer { Buffer } The Buffer to use as the internal Buffer value. + * @param encoding { String } The BufferEncoding to use for strings. + * + * @return { SmartBuffer } */ - readInt16LE(): number; - + static fromBuffer(buff: Buffer, encoding?: BufferEncoding): SmartBuffer; /** - * Reads a 32-bit signed integer (big endian) + * Creates a new SmartBuffer instance with the provided SmartBufferOptions options. + * + * @param options { SmartBufferOptions } The options to use when creating the SmartBuffer instance. */ - readInt32BE(): number; - + static fromOptions(options: SmartBufferOptions): SmartBuffer /** - * Reads a 32-bit signed integer (little endian) + * Ensures that the internal Buffer is large enough to write data. + * + * @param minLength { Number } The minimum length of the data that needs to be written. + * @param offset { Number } The offset of the data to be written. */ - readInt32LE(): number; - - // Unsigned number readers - + private ensureWriteable(minLength, offset?); /** - * Reads a 8-bit unsigned integer + * Ensures that the internal Buffer is large enough to write at least the given amount of data. + * + * @param minLength { Number } The minimum length of the data needs to be written. */ - readUInt8(): number; - + private ensureCapacity(minLength); /** - * Reads a 16-bit unsigned integer (big endian) + * Reads a numeric number value using the provided function. + * + * @param func { Function(offset: number) => number } The function to read data on the internal Buffer with. + * @param byteSize { Number } The number of bytes read. + * + * @param { Number } */ - readUInt16BE(): number; - + private readNumberValue(func, byteSize); /** - * Reads a 16-bit unsigned integer (little endian) + * Writes a numeric number value using the provided function. + * + * @param func { Function(offset: number, offset?) => number} The function to write data on the internal Buffer with. + * @param byteSize { Number } The number of bytes written. + * @param value { Number } The number value to write. + * @param offset { Number } the offset to write the number at. + * */ - readUInt16LE(): number; - + private writeNumberValue(func, byteSize, value, offset?); /** - * Reads a 32-bit unsigned integer (big endian) + * Reads an Int8 value from the current read position. + * + * @return { Number } */ - readUInt32BE(): number; - + readInt8(): number; /** - * Reads a 32-bit unsigned integer (little endian) + * Reads an Int16BE value from the current read position. + * + * @return { Number } */ - readUInt32LE(): number; - - // Floating point readers - + readInt16BE(): number; /** - * Reads a float (big endian) + * Reads an Int16LE value from the current read position. + * + * @return { Number } */ - readFloatBE(): number; - + readInt16LE(): number; /** - * Reads a float (little endian) + * Reads an Int32BE value from the current read position. + * + * @return { Number } */ - readFloatLE(): number; - + readInt32BE(): number; /** - * Reads a double (big endian) + * Reads an Int32LE value from the current read position. + * + * @return { Number } */ - readDoubleBE(): number; - + readInt32LE(): number; /** - * Reads a double (little endian) + * Writes an Int8 value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. + * + * @return this */ - readDoubleLE(): number; - - // String readers - + writeInt8(value: number, offset?: number): SmartBuffer; /** - * Reads a string + * Writes an Int16BE value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. * - * @param length { Number } The length of the string to read - * @param encoding { Number} The encoding to use (defaults to instance level encoding) + * @return this */ - readString(length?: number, encoding?: string): string; - + writeInt16BE(value: number, offset?: number): SmartBuffer; /** - * Reads a null terminated string + * Writes an Int16LE value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. * - * @param encoding The encoding to use (defaults to instance level encoding) + * @return this */ - readStringNT(encoding?: string): string; - - // Buffer readers - + writeInt16LE(value: number, offset?: number): SmartBuffer; /** - * Reads binary data into a Buffer + * Writes an Int32BE value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. * - * @param len { Number } The amount of data to read + * @return this */ - readBuffer(len?: number): Buffer; - + writeInt32BE(value: number, offset?: number): SmartBuffer; /** - * Reads null terminated binary data into a Buffer + * Writes an Int32LE value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. + * + * @return this */ - readBufferNT(): Buffer; - - - // Signed number writers - + writeInt32LE(value: number, offset?: number): SmartBuffer; /** - * Writes a 8-bit signed integer value + * Reads an UInt8 value from the current read position. * - * @param value { Number } The value to write to the buffer - * @param offset { Number } The offset position to write the value to + * @return { Number } */ - writeInt8(value: number, offset?: number): this; - + readUInt8(): number; /** - * Writes a 16-bit signed integer (big endian) value + * Reads an UInt16BE value from the current read position. * - * @param value { Number } The value to write to the buffer - * @param offset { Number } The offset position to write the value to + * @return { Number } */ - writeInt16BE(value: number, offset?: number): this; - + readUInt16BE(): number; /** - * Writes a 16-bit signed integer (little endian) value + * Reads an UInt16LE value from the current read position. * - * @param value { Number } The value to write to the buffer - * @param offset { Number } The offset position to write the value to + * @return { Number } */ - writeInt16LE(value: number, offset?: number): this; - + readUInt16LE(): number; /** - * Writes a 32-bit signed integer (big endian) value + * Reads an UInt32BE value from the current read position. * - * @param value { Number } The value to write to the buffer - * @param offset { Number } The offset position to write the value to + * @return { Number } */ - writeInt32BE(value: number, offset?: number): this; - + readUInt32BE(): number; /** - * Writes a 32-bit signed integer (little endian) value + * Reads an UInt32LE value from the current read position. * - * @param value { Number } The value to write to the buffer - * @param offset { Number } The offset position to write the value to + * @return { Number } */ - writeInt32LE(value: number, offset?: number): this; - - // Unsigned number writers - + readUInt32LE(): number; /** - * Writes a 8-bit unsigned integer value + * Writes an UInt8 value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. * - * @param value { Number } The value to write to the buffer - * @param offset { Number } The offset position to write the value to + * @return this */ - writeUInt8(value: number, offset?: number): this; - + writeUInt8(value: number, offset?: number): SmartBuffer; /** - * Writes a 16-bit unsigned integer (big endian) value + * Writes an UInt16BE value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. * - * @param value { Number } The value to write to the buffer - * @param offset { Number } The offset position to write the value to + * @return this */ - writeUInt16BE(value: number, offset?: number): this; - + writeUInt16BE(value: number, offset?: number): SmartBuffer; /** - * Writes a 16-bit unsigned integer (little endian) value + * Writes an UInt16LE value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. * - * @param value { Number } The value to write to the buffer - * @param offset { Number } The offset position to write the value to + * @return this */ - writeUInt16LE(value: number, offset?: number): this; - + writeUInt16LE(value: number, offset?: number): SmartBuffer; /** - * Writes a 32-bit unsigned integer (big endian) value + * Writes an UInt32BE value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. * - * @param value { Number } The value to write to the buffer - * @param offset { Number } The offset position to write the value to + * @return this */ - writeUInt32BE(value: number, offset?: number): this; - + writeUInt32BE(value: number, offset?: number): SmartBuffer; /** - * Writes a 32-bit unsigned integer (little endian) value + * Writes an UInt32LE value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. * - * @param value { Number } The value to write to the buffer - * @param offset { Number } The offset position to write the value to + * @return this */ - writeUInt32LE(value: number, offset?: number): this; - - // Floating point writers - + writeUInt32LE(value: number, offset?: number): SmartBuffer; /** - * Writes a float (big endian) value + * Reads an FloatBE value from the current read position. * - * @param value { Number } The value to write to the buffer - * @param offset { Number } The offset position to write the value to + * @return { Number } */ - writeFloatBE(value: number, offset?: number): this; - + readFloatBE(): number; /** - * Writes a float (little endian) value + * Reads an FloatLE value from the current read position. * - * @param value { Number } The value to write to the buffer - * @param offset { Number } The offset position to write the value to + * @return { Number } */ - writeFloatLE(value: number, offset?: number): this; - + readFloatLE(): number; /** - * Writes a double (big endian) value + * Writes a FloatBE value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. * - * @param value { Number } The value to write to the buffer - * @param offset { Number } The offset position to write the value to + * @return this */ - writeDoubleBE(value: number, offset?: number): this; - + writeFloatBE(value: number, offset?: number): SmartBuffer; /** - * Writes a double (little endian) value + * Writes a FloatLE value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. * - * @param value { Number } The value to write to the buffer - * @param offset { Number } The offset position to write the value to + * @return this */ - writeDoubleLE(value: number, offset?: number): this; - - // String writers - + writeFloatLE(value: number, offset?: number): SmartBuffer; /** - * Writes a string + * Reads an DoublEBE value from the current read position. * - * @param value { String } The value to write to the buffer - * @param offset { Number } The offset position to write the value to + * @return { Number } */ + readDoubleBE(): number; /** - * Writes a string + * Reads an DoubleLE value from the current read position. * - * @param value { String } The value to write to the buffer - * @param offset { String } The encoding to use when writing the string (defaults to instance level encoding) + * @return { Number } */ + readDoubleLE(): number; /** - * Writes a string + * Writes a DoubleBE value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. * - * @param value { String } The value to write to the buffer - * @param offset { Number } The offset position to write the value to - * @param encoding { String } The encoding to use when writing the string (defaults to instance level encoding) + * @return this */ - writeString(value: string, offset?: number | string, encoding?: string): this; - + writeDoubleBE(value: number, offset?: number): SmartBuffer; /** - * Writes a null terminated string + * Writes a DoubleLE value to the current write position (or at optional offset). + * + * @param value { Number } The value to write. + * @param offset { Number } The offset to write the value at. * - * @param value { String } The value to write to the buffer - * @param offset { Number } The offset position to write the value to + * @return this */ + writeDoubleLE(value: number, offset?: number): SmartBuffer; /** - * Writes a null terminated string + * Reads a String from the current read position. * - * @param value { String } The value to write to the buffer - * @param offset { String } The encoding to use when writing the string (defaults to instance level encoding) + * @param length { Number } The number of bytes to read as a String. + * @param encoding { String } The BufferEncoding to use for the string (Defaults to instance level encoding). + * + * @return { String } */ + readString(length?: number, encoding?: BufferEncoding): string; /** - * Writes a null terminated string + * Writes a String to the current write position. * - * @param value { String } The value to write to the buffer - * @param offset { Number } The offset position to write the value to - * @param encoding { String } The encoding to use when writing the string (defaults to instance level encoding) + * @param value { String } The String value to write. + * @param arg2 { Number | String } The offset to write the string to, or the BufferEncoding to use. + * @param encoding { String } The BufferEncoding to use for writing strings (defaults to instance encoding). */ - writeStringNT(value: string, offset?: number | string, encoding?: string): this; - - // Buffer writers - + writeString(value: string, arg2?: number | BufferEncoding, encoding?: BufferEncoding): this; + /** + * Reads a null-terminated String from the current read position. + * + * @param encoding { String } The BufferEncoding to use for the string (Defaults to instance level encoding). + * + * @return { String } + */ + readStringNT(encoding?: BufferEncoding): string; + /** + * Writes a null-terminated String to the current write position. + * + * @param value { String } The String value to write. + * @param arg2 { Number | String } The offset to write the string to, or the BufferEncoding to use. + * @param encoding { String } The BufferEncoding to use for writing strings (defaults to instance encoding). + */ + writeStringNT(value: string, offset?: number | BufferEncoding, encoding?: BufferEncoding): void; + /** + * Reads a Buffer from the internal read position. + * + * @param length { Number } The length of data to read as a Buffer. + * + * @return { Buffer } + */ + readBuffer(length?: number): Buffer; /** - * Writes a Buffer + * Writes a Buffer to the current write position. * - * @param value { Buffer } The Buffer to write to the smart buffer - * @param offset { Number } The offset position to write the value to + * @param value { Buffer } The Buffer to write. + * @param offset { Number } The offset to write the Buffer to. */ writeBuffer(value: Buffer, offset?: number): this; - /** - * Writes a Buffer with null termination + * Reads a null-terminated Buffer from the current read poisiton. + * + * @return { Buffer } + */ + readBufferNT(): Buffer; + /** + * Writes a null-terminated Buffer to the current write position. * - * @param value { Buffer } The buffer to write to the smart buffer - * @param offset { Number } The offset position to write the value to + * @param value { Buffer } The Buffer to write. + * @param offset { Number } The offset to write the Buffer to. */ writeBufferNT(value: Buffer, offset?: number): this; - - - // Misc Functions - /** - * Clears the smart buffer + * Clears the SmartBuffer instance to its original empty state. */ - clear(); - + clear(): void; /** - * Gets the number of bytes that remain to be read + * Gets the remaining data left to be read from the SmartBuffer instance. + * + * @return { Number } */ remaining(): number; - /** - * Increases the read offset position + * Moves the read offset forward. * - * @param amount { Number } The amount to increase the read offset position by + * @param amount { Number } The amount to move the read offset forward by. */ - skip(amount: number); - + skip(amount: number): void; /** - * Changes the read offset position + * Moves the read offset backwards. * - * @param position { Number } The position to change the read offset to + * @param amount { Number } The amount to move the read offset backwards by. */ - skipTo(position: number); - + rewind(amount: number): void; /** - * Decreases the read offset position + * Moves the read offset to a specific position. * - * @param amount { Number } The amount to decrease the read offset position by + * @param position { Number } The position to move the read offset to. */ - rewind(amount: number); - + skipTo(position: number): void; /** - * Gets the underlying Buffer instance + * Moves the read offset to a specific position. + * + * @param position { Number } The position to move the read offset to. + */ + moveTo(position: number): void; + /** + * Gets the value of the internal managed Buffer + * + * @param { Buffer } */ toBuffer(): Buffer; - /** - * Gets the string representation of the underlying Buffer + * Gets the String value of the internal managed Buffer * - * @param encoding { String } The encoding to use (defaults to instance level encoding) + * @param encoding { String } The BufferEncoding to display the Buffer as (defaults to instance level encoding). */ - toString(encoding?: string): string; - + toString(encoding?: BufferEncoding): string; /** - * Destroys the smart buffer instance + * Destroys the SmartBuffer instance. */ - destroy(); - + destroy(): void; /** - * Gets the current length of the smart buffer instance + * Type checking function that determines if an object is a SmartBufferOptions object. */ - length: number; + static isSmartBufferOptions(options: SmartBufferOptions): options is SmartBufferOptions; } export = SmartBuffer; \ No newline at end of file