diff options
Diffstat (limited to 'node_modules/structured-clone/serialize.js')
-rw-r--r-- | node_modules/structured-clone/serialize.js | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/node_modules/structured-clone/serialize.js b/node_modules/structured-clone/serialize.js new file mode 100644 index 000000000..44c984f38 --- /dev/null +++ b/node_modules/structured-clone/serialize.js @@ -0,0 +1,223 @@ +// Implements serialize/deserialize of structured clones. +// Also resolves cycles (code based on cycle.js by Douglas Crockford) + +function encodeRegExp (regexp) +{ + var flags = ''; + if (regexp.global) flags += 'g'; + if (regexp.multiline) flags += 'm'; + if (regexp.ignoreCase) flags += 'i'; + return [flags, regexp.source].join(','); +} + +function decodeRegExp (str) +{ + var flags = str.match(/^[^,]*/)[0]; + var source = str.substr(flags.length + 1); + return new RegExp(source, flags); +} + + +// The derez recurses through the object, producing the deep copy. + +function derez(value, path, objects, paths, buffers) +{ + if (Buffer.isBuffer(value)) { + var start = Buffer.concat(buffers).length; + buffers.push(value); + return '\x10b' + [start, value.length].join(',') + } + if (value instanceof Date) { + return '\x10d' + value.toJSON(); + } + if (value instanceof RegExp) { + return '\x10r' + encodeRegExp(value); + } + if (value instanceof Error) { + return '\x10e' + value.message + } + if (typeof value == 'string') { + return value.charAt(0) == '\x10' ? '\x10s' + value : value; + } + + var i, // The loop counter + name, // Property name + nu; // The new object or array + +// typeof null === 'object', so go on if this value is really an object but not +// one of the weird builtin objects. + + if (typeof value === 'object' && value !== null && + !(value instanceof Boolean) && + !(value instanceof Number) && + !(value instanceof String)) { + +// If the value is an object or array, look to see if we have already +// encountered it. If so, return a $ref/path object. This is a hard way, +// linear search that will get slower as the number of unique objects grows. + + i = objects.indexOf(value); + if (i !== -1) { + return '\x10j' + paths[i]; + } + +// Otherwise, accumulate the unique value and its path. + + objects.push(value); + paths.push(path); + +// If it is an array, replicate the array. + + if (Array.isArray(value)) { + nu = []; + for (i = 0; i < value.length; i += 1) { + nu[i] = derez(value[i], + path + '[' + i + ']', + objects, paths, buffers); + } + } else { + +// If it is an object, replicate the object. + + nu = {}; + for (name in value) { + if (Object.prototype.hasOwnProperty.call(value, name) && value != '__proto__') { + nu[name] = derez(value[name], + path + '[' + JSON.stringify(name) + ']', + objects, paths, buffers); + } + } + } + return nu; + } + + return value; +} + + +function rerez($, $$) +{ + +// Restore an object that was reduced by decycle. Members whose values are +// objects of the form +// {$ref: PATH} +// are replaced with references to the value found by the PATH. This will +// restore cycles. The object will be mutated. + +// The eval function is used to locate the values described by a PATH. The +// root object is kept in a $ variable. A regular expression is used to +// assure that the PATH is extremely well formed. The regexp contains nested +// * quantifiers. That has been known to have extremely bad performance +// problems on some browsers for very long strings. A PATH is expected to be +// reasonably short. A PATH is allowed to belong to a very restricted subset of +// Goessner's JSONPath. + +// So, +// var s = '[{"$ref":"$"}]'; +// return JSON.retrocycle(JSON.parse(s)); +// produces an array containing a single element which is the array itself. + + var px = + /^\${1,4}(?:\[(?:\d+|\"(?:[^\\\"\u0000-\u001f]|\\([\\\"\/bfnrt]|u[0-9a-zA-Z]{4}))*\")\])*$/; + + function redo (item) { + if (typeof item == 'string' && item.charAt(0) == '\x10') { + switch (item.charAt(1)) { + case 's': + return item.substr(2); + case 'b': + var bounds = item.substr(2).split(',', 2); + return $$.slice(bounds[0] || 0, (bounds[0] || 0) + (bounds[1] || [0])); + case 'd': + return new Date(item.substr(2)); + case 'r': + return decodeRegExp(item.substr(2)); + case 'e': + return new Error(item.substr(2)); + case 'j': + var path = item.substr(2); + if (px.test(path)) { + return eval(path); + } + default: return null; + } + } + + if (item && typeof item === 'object') { + rez(item, $$); + } + return item; + } + + function rez(value) { + +// The rez function walks recursively through the object looking for $ref +// properties. When it finds one that has a value that is a path, then it +// replaces the $ref object with a reference to the value that is found by +// the path. + + var i, name; + + if (value && typeof value === 'object') { + if (Array.isArray(value)) { + for (i = 0; i < value.length; i += 1) { + value[i] = redo(value[i]); + } + } else { + for (name in value) { + value[name] = redo(value[name]); + } + } + } + } + + $ = redo($) + + return $; +}; + + +// Public API + +exports.serialize = function (object) { + var objects = [], // Keep a reference to each unique object or array + paths = [], // Keep the path to each unique object or array + buffers = []; // Returned buffers + + var nilbyte = new Buffer([0x00]); + + if (Buffer.isBuffer(object)) { + return Buffer.concat([ + nilbyte, + object + ]); + } + + var json = new Buffer(JSON.stringify(derez(object, '$', objects, paths, buffers))); + if (buffers.length == 0) { + return json; + } + + return Buffer.concat([ + json, + nilbyte, + Buffer.concat(buffers) + ]); +} + +exports.deserialize = function (buf) { + for (var i = 0; i <= buf.length; i++) { + if (buf[i] == 0x00 || buf[i] == null) { + break; + } + } + var jsonbuf = buf.slice(0, i); + var bufbuf = buf.slice(i + 1); + + // Shortcut for only encoding a root buffer. + if (jsonbuf.length == 0) { + return bufbuf; + } + + return rerez(JSON.parse(jsonbuf.toString('utf-8')), bufbuf); +}
\ No newline at end of file |