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.
358 lines
9.6 KiB
358 lines
9.6 KiB
5 years ago
|
"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
|