json-wire-protocol
Advanced tools
Comparing version 0.2.1 to 1.0.0
@@ -0,1 +1,4 @@ | ||
# 1.0.0 | ||
- node only version with complete change of module interface. | ||
# 0.2.0 | ||
@@ -2,0 +5,0 @@ - expose separator constant value. |
309
lib/index.js
@@ -1,237 +0,134 @@ | ||
(function(global, exports) { | ||
var HAS_BUFFER = typeof Buffer !== 'undefined'; | ||
var Transform = require('stream').Transform; | ||
var SEPARATOR = ':'; | ||
var SEPARATOR_CODE = SEPARATOR.charCodeAt(0); | ||
var EventEmitter = | ||
global.EventEmitter2 || | ||
require('eventemitter2').EventEmitter2; | ||
var SEPARATOR = ':'; | ||
var SEPARATOR_CODE = SEPARATOR.charCodeAt(0); | ||
/** | ||
* First ocurrence of where string occurs in a buffer. | ||
* | ||
* NOTE: this is not UTF8 safe generally we expect to find the correct | ||
* char fairly quickly unless the buffer is incorrectly formatted. | ||
* | ||
* @param {Buffer} buffer haystack. | ||
* @param {String} string needle. | ||
* @return {Numeric} -1 if not found index otherwise. | ||
*/ | ||
function indexInBuffer(buffer, string) { | ||
if (typeof buffer === 'string') | ||
return buffer.indexOf(string); | ||
if (buffer.length === 0) | ||
return -1; | ||
var index = 0; | ||
var length = buffer.length; | ||
do { | ||
if (buffer[index] === SEPARATOR_CODE) | ||
return index; | ||
} while ( | ||
++index && index + 1 < length | ||
); | ||
/** | ||
* First ocurrence of where string occurs in a buffer. | ||
* | ||
* NOTE: this is not UTF8 safe generally we expect to find the correct | ||
* char fairly quickly unless the buffer is incorrectly formatted. | ||
* | ||
* @param {Buffer} buffer haystack. | ||
* @param {String} code needle. | ||
* @return {Numeric} -1 if not found index otherwise. | ||
*/ | ||
function indexInBuffer(buffer, code) { | ||
if (buffer.length === 0) | ||
return -1; | ||
} | ||
/** | ||
* Wrapper for creating either a buffer or ArrayBuffer. | ||
*/ | ||
function createByteContainer() { | ||
if (HAS_BUFFER) | ||
return new Buffer(0); | ||
var index = 0; | ||
var length = buffer.length; | ||
return new Uint8Array(); | ||
} | ||
do { | ||
if (buffer[index] === code) return index; | ||
} while ( | ||
++index && index + 1 < length | ||
); | ||
/** | ||
* Join the contents of byte container a and b returning c. | ||
*/ | ||
function concatByteContainers(a, b) { | ||
if (HAS_BUFFER) | ||
return Buffer.concat([a, b]); | ||
return -1; | ||
} | ||
// make sure everything is unit8 | ||
if (a instanceof ArrayBuffer) | ||
a = new Uint8Array(a); | ||
/** | ||
* converts an object to a string representation suitable for storage on disk. | ||
* Its very important to note that the length in the string refers to the utf8 | ||
* size of the json content in bytes (as utf8) not the JS string length. | ||
* | ||
* @param {Object} object to stringify. | ||
* @return {String} serialized object. | ||
*/ | ||
function stringify(object) { | ||
var json = JSON.stringify(object); | ||
var len = Buffer.byteLength(json); | ||
if (b instanceof ArrayBuffer) | ||
b = new Uint8Array(b); | ||
return len + SEPARATOR + json; | ||
} | ||
// sizes of originals | ||
var aLen = a.length; | ||
var bLen = b.length; | ||
/** | ||
* attempts to parse a given buffer or string. | ||
* | ||
* @param {Uint8Array|Buffer} input in byteLength:{json..} format. | ||
* @return {Objec} JS object. | ||
*/ | ||
function parse(input) { | ||
var stream = new Stream(); | ||
var result; | ||
var array = new Uint8Array(aLen + bLen); | ||
array.set(a); | ||
array.set(b, aLen); | ||
stream.once('data', function(data) { | ||
result = data; | ||
}); | ||
// return new byte container | ||
return array; | ||
} | ||
stream.write(input); | ||
function sliceByteContainers(container, start, end) { | ||
start = start || 0; | ||
end = end || byteLength(container); | ||
if (HAS_BUFFER) | ||
return container.slice(start, end); | ||
return container.subarray(start, end); | ||
if (!result) { | ||
throw new Error( | ||
'no command available from parsing:' + input | ||
); | ||
} | ||
/** | ||
* Like Buffer.byteLength but works on ArrayBuffers too. | ||
*/ | ||
function byteLength(input) { | ||
if (typeof input === 'string') { | ||
if (HAS_BUFFER) { | ||
return Buffer.byteLength(input); | ||
} | ||
var encoder = new TextEncoder(); | ||
var out = encoder.encode(input); | ||
return out.length; | ||
} | ||
return result; | ||
} | ||
return input.length; | ||
} | ||
function Stream() { | ||
this._pendingLength = null; | ||
this._buffer = new Buffer(0); | ||
function bytesToUtf8(container, start, end) { | ||
if (!start) | ||
start = 0; | ||
Transform.call(this, { objectMode: true }); | ||
} | ||
if (!end) | ||
end = byteLength(container); | ||
Stream.prototype = { | ||
__proto__: Transform.prototype, | ||
if (HAS_BUFFER) | ||
return container.toString('utf8', start, end); | ||
_transform: function(chunk, encoding, cb) { | ||
if (!this._pendingLength) { | ||
var idx = indexInBuffer(chunk, SEPARATOR_CODE) | ||
var decoder = new TextDecoder(); | ||
var array = container.subarray(start, end); | ||
// Nothing to do just buffer it... | ||
if (idx === -1) { | ||
this._buffer = Buffer.concat([this._buffer, chunk]); | ||
return cb(); | ||
} | ||
return decoder.decode(array); | ||
} | ||
// number of bytes in the json segment... | ||
var length = Buffer.concat([this._buffer, chunk.slice(0, idx)]); | ||
this._pendingLength = parseInt(length.toString(), 10); | ||
this._buffer = new Buffer(0); | ||
/** | ||
* converts an object to a string representation suitable for storage on disk. | ||
* Its very important to note that the length in the string refers to the utf8 | ||
* size of the json content in bytes (as utf8) not the JS string length. | ||
* | ||
* @param {Object} object to stringify. | ||
* @return {String} serialized object. | ||
*/ | ||
function stringify(object) { | ||
var json = JSON.stringify(object); | ||
var len = byteLength(json); | ||
// We have transitioned to a pending length state so do another pass. | ||
return this._transform(chunk.slice(idx + 1), encoding, cb); | ||
} | ||
return len + SEPARATOR + json; | ||
} | ||
// Total length too small nothing to do... | ||
if (this._buffer.length + chunk.length < this._pendingLength) { | ||
this._buffer = Buffer.concat([this._buffer, chunk]); | ||
return cb(); | ||
} | ||
/** | ||
* attempts to parse a given buffer or string. | ||
* | ||
* @param {Uint8Array|Buffer} input in byteLength:{json..} format. | ||
* @return {Objec} JS object. | ||
*/ | ||
function parse(input) { | ||
var stream = new Stream(); | ||
var result; | ||
var buffer = Buffer.concat([this._buffer, chunk]); | ||
var remainder = null; | ||
stream.once('data', function(data) { | ||
result = data; | ||
}); | ||
if (buffer.length > this._pendingLength) { | ||
remainder = buffer.slice(this._pendingLength); | ||
buffer = buffer.slice(0, this._pendingLength); | ||
} | ||
stream.write(input); | ||
// Reset internal state and push current json string. | ||
this._pendingLength = null; | ||
this._buffer = new Buffer(0); | ||
this.push(JSON.parse(buffer.toString())) | ||
if (!result) { | ||
throw new Error( | ||
'no command available from parsing:' + input | ||
); | ||
// If we have any remaining data we need to keep processing... | ||
if (remainder) { | ||
return this._transform(remainder, encoding, cb); | ||
} | ||
return result; | ||
// Otherwise we are done yay.... | ||
return cb(); | ||
} | ||
function Stream() { | ||
EventEmitter.call(this); | ||
}; | ||
this._pendingLength = null; | ||
// zero length buffer so we can concat later | ||
// this is always a unit8 array or a buffer. | ||
this._buffer = createByteContainer(); | ||
} | ||
Stream.prototype = { | ||
__proto__: EventEmitter.prototype, | ||
_findLength: function() { | ||
if (this._pendingLength === null) { | ||
var idx = indexInBuffer(this._buffer, SEPARATOR); | ||
if (idx === -1) | ||
return; | ||
// mark the length to read out of the rolling buffer. | ||
this._pendingLength = parseInt( | ||
bytesToUtf8(this._buffer, 0, idx), | ||
10 | ||
); | ||
this._buffer = sliceByteContainers(this._buffer, idx + 1); | ||
} | ||
}, | ||
_readBuffer: function() { | ||
// if the buffer.length is < then we pendingLength need to buffer | ||
// more data. | ||
if (!this._pendingLength || this._buffer.length < this._pendingLength) | ||
return false; | ||
// extract remainder and parse json | ||
var message = sliceByteContainers(this._buffer, 0, this._pendingLength); | ||
this._buffer = sliceByteContainers(this._buffer, this._pendingLength); | ||
this._pendingLength = null; | ||
var result; | ||
try { | ||
message = bytesToUtf8(message); | ||
result = JSON.parse(message); | ||
} catch (e) { | ||
this.emit('error', e); | ||
return false; | ||
} | ||
this.emit('data', result); | ||
return true; | ||
}, | ||
write: function(buffer) { | ||
// append new buffer to whatever we have. | ||
this._buffer = concatByteContainers(this._buffer, buffer); | ||
do { | ||
// attempt to find length of next message. | ||
this._findLength(); | ||
} while ( | ||
// keep repeating while there are messages | ||
this._readBuffer() | ||
); | ||
} | ||
}; | ||
exports.parse = parse; | ||
exports.stringify = stringify; | ||
exports.Stream = Stream; | ||
exports.separator = SEPARATOR; | ||
}).apply( | ||
null, | ||
typeof window === 'undefined' ? | ||
[global, module.exports] : | ||
[window, window.jsonWireProtocol = {}] | ||
); | ||
exports.parse = parse; | ||
exports.stringify = stringify; | ||
exports.Stream = Stream; | ||
exports.separator = SEPARATOR; |
{ | ||
"name": "json-wire-protocol", | ||
"version": "0.2.1", | ||
"main": "index.js", | ||
"version": "1.0.0", | ||
"main": "lib/index.js", | ||
"description": "JSON wire protocol parser/writer (used in mozilla debugger protocol)", | ||
"author": "James Lal", | ||
"main": "lib/index.js", | ||
"dependencies" : { | ||
"eventemitter2": "~0.4" | ||
}, | ||
"repository": "git@github.com:lightsofapollo/json-wire-protocol.git", | ||
"scripts": { | ||
"test": "make test-node" | ||
"test": "mocha" | ||
}, | ||
"devDependencies": { | ||
"mocha": "~1.7", | ||
"chai": "~1.6", | ||
"test-agent": "~0.12" | ||
"mocha": "^2.0.1" | ||
}, | ||
"license": "MIT", | ||
@@ -24,0 +15,0 @@ "engine": { |
suite('json wire protocol', function() { | ||
var assert = require('assert'); | ||
var jsonWireProtocol = require('../'); | ||
var subject; | ||
@@ -7,9 +10,3 @@ var TWO_BYTE = 'ž'; | ||
function createBytes(content) { | ||
if (HAS_BUFFER) { | ||
return new Buffer(content); | ||
} | ||
// create text encode to initialize ArrayBuffer | ||
var encoder = new TextEncoder(); | ||
return encoder.encode(content).buffer; | ||
} | ||
@@ -92,4 +89,2 @@ | ||
return; | ||
test('result after writing to stream', function() { | ||
@@ -96,0 +91,0 @@ assert.deepEqual( |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
0
1
1
8683
7
211
1
- Removedeventemitter2@~0.4
- Removedeventemitter2@0.4.14(transitive)