You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
357 lines
9.6 KiB
357 lines
9.6 KiB
"use strict" |
|
|
|
var writeIEEE754 = require('../float_parser').writeIEEE754, |
|
readIEEE754 = require('../float_parser').readIEEE754, |
|
f = require('util').format, |
|
Long = require('../long').Long, |
|
Double = require('../double').Double, |
|
Timestamp = require('../timestamp').Timestamp, |
|
ObjectID = require('../objectid').ObjectID, |
|
Symbol = require('../symbol').Symbol, |
|
Code = require('../code').Code, |
|
MinKey = require('../min_key').MinKey, |
|
MaxKey = require('../max_key').MaxKey, |
|
DBRef = require('../db_ref').DBRef, |
|
BSONRegExp = require('../regexp').BSONRegExp, |
|
Binary = require('../binary').Binary; |
|
|
|
var BSON = {}; |
|
|
|
/** |
|
* Contains the function cache if we have that enable to allow for avoiding the eval step on each deserialization, comparison is by md5 |
|
* |
|
* @ignore |
|
* @api private |
|
*/ |
|
var functionCache = BSON.functionCache = {}; |
|
|
|
/** |
|
* Number BSON Type |
|
* |
|
* @classconstant BSON_DATA_NUMBER |
|
**/ |
|
BSON.BSON_DATA_NUMBER = 1; |
|
/** |
|
* String BSON Type |
|
* |
|
* @classconstant BSON_DATA_STRING |
|
**/ |
|
BSON.BSON_DATA_STRING = 2; |
|
/** |
|
* Object BSON Type |
|
* |
|
* @classconstant BSON_DATA_OBJECT |
|
**/ |
|
BSON.BSON_DATA_OBJECT = 3; |
|
/** |
|
* Array BSON Type |
|
* |
|
* @classconstant BSON_DATA_ARRAY |
|
**/ |
|
BSON.BSON_DATA_ARRAY = 4; |
|
/** |
|
* Binary BSON Type |
|
* |
|
* @classconstant BSON_DATA_BINARY |
|
**/ |
|
BSON.BSON_DATA_BINARY = 5; |
|
/** |
|
* ObjectID BSON Type |
|
* |
|
* @classconstant BSON_DATA_OID |
|
**/ |
|
BSON.BSON_DATA_OID = 7; |
|
/** |
|
* Boolean BSON Type |
|
* |
|
* @classconstant BSON_DATA_BOOLEAN |
|
**/ |
|
BSON.BSON_DATA_BOOLEAN = 8; |
|
/** |
|
* Date BSON Type |
|
* |
|
* @classconstant BSON_DATA_DATE |
|
**/ |
|
BSON.BSON_DATA_DATE = 9; |
|
/** |
|
* null BSON Type |
|
* |
|
* @classconstant BSON_DATA_NULL |
|
**/ |
|
BSON.BSON_DATA_NULL = 10; |
|
/** |
|
* RegExp BSON Type |
|
* |
|
* @classconstant BSON_DATA_REGEXP |
|
**/ |
|
BSON.BSON_DATA_REGEXP = 11; |
|
/** |
|
* Code BSON Type |
|
* |
|
* @classconstant BSON_DATA_CODE |
|
**/ |
|
BSON.BSON_DATA_CODE = 13; |
|
/** |
|
* Symbol BSON Type |
|
* |
|
* @classconstant BSON_DATA_SYMBOL |
|
**/ |
|
BSON.BSON_DATA_SYMBOL = 14; |
|
/** |
|
* Code with Scope BSON Type |
|
* |
|
* @classconstant BSON_DATA_CODE_W_SCOPE |
|
**/ |
|
BSON.BSON_DATA_CODE_W_SCOPE = 15; |
|
/** |
|
* 32 bit Integer BSON Type |
|
* |
|
* @classconstant BSON_DATA_INT |
|
**/ |
|
BSON.BSON_DATA_INT = 16; |
|
/** |
|
* Timestamp BSON Type |
|
* |
|
* @classconstant BSON_DATA_TIMESTAMP |
|
**/ |
|
BSON.BSON_DATA_TIMESTAMP = 17; |
|
/** |
|
* Long BSON Type |
|
* |
|
* @classconstant BSON_DATA_LONG |
|
**/ |
|
BSON.BSON_DATA_LONG = 18; |
|
/** |
|
* MinKey BSON Type |
|
* |
|
* @classconstant BSON_DATA_MIN_KEY |
|
**/ |
|
BSON.BSON_DATA_MIN_KEY = 0xff; |
|
/** |
|
* MaxKey BSON Type |
|
* |
|
* @classconstant BSON_DATA_MAX_KEY |
|
**/ |
|
BSON.BSON_DATA_MAX_KEY = 0x7f; |
|
|
|
/** |
|
* Binary Default Type |
|
* |
|
* @classconstant BSON_BINARY_SUBTYPE_DEFAULT |
|
**/ |
|
BSON.BSON_BINARY_SUBTYPE_DEFAULT = 0; |
|
/** |
|
* Binary Function Type |
|
* |
|
* @classconstant BSON_BINARY_SUBTYPE_FUNCTION |
|
**/ |
|
BSON.BSON_BINARY_SUBTYPE_FUNCTION = 1; |
|
/** |
|
* Binary Byte Array Type |
|
* |
|
* @classconstant BSON_BINARY_SUBTYPE_BYTE_ARRAY |
|
**/ |
|
BSON.BSON_BINARY_SUBTYPE_BYTE_ARRAY = 2; |
|
/** |
|
* Binary UUID Type |
|
* |
|
* @classconstant BSON_BINARY_SUBTYPE_UUID |
|
**/ |
|
BSON.BSON_BINARY_SUBTYPE_UUID = 3; |
|
/** |
|
* Binary MD5 Type |
|
* |
|
* @classconstant BSON_BINARY_SUBTYPE_MD5 |
|
**/ |
|
BSON.BSON_BINARY_SUBTYPE_MD5 = 4; |
|
/** |
|
* Binary User Defined Type |
|
* |
|
* @classconstant BSON_BINARY_SUBTYPE_USER_DEFINED |
|
**/ |
|
BSON.BSON_BINARY_SUBTYPE_USER_DEFINED = 128; |
|
|
|
// BSON MAX VALUES |
|
BSON.BSON_INT32_MAX = 0x7FFFFFFF; |
|
BSON.BSON_INT32_MIN = -0x80000000; |
|
|
|
BSON.BSON_INT64_MAX = Math.pow(2, 63) - 1; |
|
BSON.BSON_INT64_MIN = -Math.pow(2, 63); |
|
|
|
// JS MAX PRECISE VALUES |
|
BSON.JS_INT_MAX = 0x20000000000000; // Any integer up to 2^53 can be precisely represented by a double. |
|
BSON.JS_INT_MIN = -0x20000000000000; // Any integer down to -2^53 can be precisely represented by a double. |
|
|
|
// Internal long versions |
|
var JS_INT_MAX_LONG = Long.fromNumber(0x20000000000000); // Any integer up to 2^53 can be precisely represented by a double. |
|
var JS_INT_MIN_LONG = Long.fromNumber(-0x20000000000000); // Any integer down to -2^53 can be precisely represented by a double. |
|
|
|
var deserialize = function(buffer, options, isArray) { |
|
var index = 0; |
|
// Read the document size |
|
var size = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; |
|
|
|
// Ensure buffer is valid size |
|
if(size < 5 || buffer.length < size) { |
|
throw new Error("corrupt bson message"); |
|
} |
|
|
|
// Illegal end value |
|
if(buffer[size - 1] != 0) { |
|
throw new Error("One object, sized correctly, with a spot for an EOO, but the EOO isn't 0x00"); |
|
} |
|
|
|
// Start deserializtion |
|
return deserializeObject(buffer, options, isArray); |
|
} |
|
|
|
// // Reads in a C style string |
|
// var readCStyleStringSpecial = function(buffer, index) { |
|
// // Get the start search index |
|
// var i = index; |
|
// // Locate the end of the c string |
|
// while(buffer[i] !== 0x00 && i < buffer.length) { |
|
// i++ |
|
// } |
|
// // If are at the end of the buffer there is a problem with the document |
|
// if(i >= buffer.length) throw new Error("Bad BSON Document: illegal CString") |
|
// // Grab utf8 encoded string |
|
// var string = buffer.toString('utf8', index, i); |
|
// // Update index position |
|
// index = i + 1; |
|
// // Return string |
|
// return {s: string, i: index}; |
|
// } |
|
|
|
// Reads in a C style string |
|
var readCStyleStringSpecial = function(buffer, index) { |
|
// Get the start search index |
|
var i = index; |
|
// Locate the end of the c string |
|
while(buffer[i] !== 0x00 && i < buffer.length) { |
|
i++ |
|
} |
|
// If are at the end of the buffer there is a problem with the document |
|
if(i >= buffer.length) throw new Error("Bad BSON Document: illegal CString") |
|
// Grab utf8 encoded string |
|
return buffer.toString('utf8', index, i); |
|
} |
|
|
|
var DeserializationMethods = {} |
|
DeserializationMethods[BSON.BSON_DATA_OID] = function(name, object, buffer, index) { |
|
var string = buffer.toString('binary', index, index + 12); |
|
object[name] = new ObjectID(string); |
|
return index + 12; |
|
} |
|
|
|
DeserializationMethods[BSON.BSON_DATA_NUMBER] = function(name, object, buffer, index) { |
|
object[name] = buffer.readDoubleLE(index); |
|
return index + 8; |
|
} |
|
|
|
DeserializationMethods[BSON.BSON_DATA_INT] = function(name, object, buffer, index) { |
|
object[name] = buffer.readInt32LE(index); |
|
return index + 4; |
|
} |
|
|
|
DeserializationMethods[BSON.BSON_DATA_TIMESTAMP] = function(name, object, buffer, index) { |
|
var lowBits = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; |
|
var highBits = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; |
|
object[name] = new Timestamp(lowBits, highBits); |
|
return index; |
|
} |
|
|
|
DeserializationMethods[BSON.BSON_DATA_STRING] = function(name, object, buffer, index) { |
|
var stringSize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; |
|
if(stringSize <= 0 || stringSize > (buffer.length - index) || buffer[index + stringSize - 1] != 0) throw new Error("bad string length in bson"); |
|
object[name] = buffer.toString('utf8', index, index + stringSize - 1); |
|
return index + stringSize; |
|
} |
|
|
|
DeserializationMethods[BSON.BSON_DATA_BOOLEAN] = function(name, object, buffer, index) { |
|
object[name] = buffer[index++] == 1; |
|
return index; |
|
} |
|
|
|
var deserializeObject = function(buffer, options, isArray) { |
|
// Options |
|
options = options == null ? {} : options; |
|
var evalFunctions = options['evalFunctions'] == null ? false : options['evalFunctions']; |
|
var cacheFunctions = options['cacheFunctions'] == null ? false : options['cacheFunctions']; |
|
var cacheFunctionsCrc32 = options['cacheFunctionsCrc32'] == null ? false : options['cacheFunctionsCrc32']; |
|
var promoteLongs = options['promoteLongs'] == null ? true : options['promoteLongs']; |
|
var fieldsAsRaw = options['fieldsAsRaw'] == null ? {} : options['fieldsAsRaw']; |
|
// Return BSONRegExp objects instead of native regular expressions |
|
var bsonRegExp = typeof options['bsonRegExp'] == 'boolean' ? options['bsonRegExp'] : false; |
|
var promoteBuffers = options['promoteBuffers'] == null ? false : options['promoteBuffers']; |
|
|
|
// Validate that we have at least 4 bytes of buffer |
|
if(buffer.length < 5) throw new Error("corrupt bson message < 5 bytes long"); |
|
|
|
// Set up index |
|
var index = typeof options['index'] == 'number' ? options['index'] : 0; |
|
|
|
// Read the document size |
|
var size = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; |
|
|
|
// Ensure buffer is valid size |
|
if(size < 5 || size > buffer.length) throw new Error("corrupt bson message"); |
|
|
|
// Create holding object |
|
var object = isArray ? [] : {}; |
|
|
|
// While we have more left data left keep parsing |
|
while(true) { |
|
// Read the type |
|
var elementType = buffer[index++]; |
|
// If we get a zero it's the last byte, exit |
|
if(elementType == 0) break; |
|
var name = readCStyleStringSpecial(buffer, index); |
|
index = index + name.length + 1; |
|
|
|
// console.log("----------- 0 " + elementType + " :: " + name) |
|
index = DeserializationMethods[elementType](name, object, buffer, index); |
|
// console.log('--------------- 1') |
|
} |
|
|
|
// Check if we have a db ref object |
|
if(object['$id'] != null) object = new DBRef(object['$ref'], object['$id'], object['$db']); |
|
|
|
// Return the final objects |
|
return object; |
|
} |
|
|
|
/** |
|
* Ensure eval is isolated. |
|
* |
|
* @ignore |
|
* @api private |
|
*/ |
|
var isolateEvalWithHash = function(functionCache, hash, functionString, object) { |
|
// Contains the value we are going to set |
|
var value = null; |
|
|
|
// Check for cache hit, eval if missing and return cached function |
|
if(functionCache[hash] == null) { |
|
eval("value = " + functionString); |
|
functionCache[hash] = value; |
|
} |
|
// Set the object |
|
return functionCache[hash].bind(object); |
|
} |
|
|
|
/** |
|
* Ensure eval is isolated. |
|
* |
|
* @ignore |
|
* @api private |
|
*/ |
|
var isolateEval = function(functionString) { |
|
// Contains the value we are going to set |
|
var value = null; |
|
// Eval the function |
|
eval("value = " + functionString); |
|
return value; |
|
} |
|
|
|
module.exports = deserialize
|
|
|