Sorry, the diff of this file is too big to display
| /// reduced to ~ 410 LOCs (parser only 300 vs. 1400+) with (some, needed) BSON classes "inlined". | ||
| /// Compare ~ 4,300 (22KB vs. 157KB) in browser build at: https://github.com/mongodb/js-bson/blob/master/browser_build/bson.js | ||
| module.exports.calculateObjectSize = calculateObjectSize; | ||
| function calculateObjectSize(object) { | ||
| var totalLength = (4 + 1); /// handles the obj.length prefix + terminating '0' ?! | ||
| for(var key in object) { /// looks like it handles arrays under the same for...in loop!? | ||
| totalLength += calculateElement(key, object[key]) | ||
| } | ||
| return totalLength; | ||
| } | ||
| function calculateElement(name, value) { | ||
| var len = 1; /// always starting with 1 for the data type byte! | ||
| if (name) len += Buffer.byteLength(name, 'utf8') + 1; /// cstring: name + '0' termination | ||
| if (value === undefined || value === null) return len; /// just the type byte plus name cstring | ||
| switch( value.constructor ) { /// removed all checks 'isBuffer' if Node.js Buffer class is present!? | ||
| case ObjectID: /// we want these sorted from most common case to least common/deprecated; | ||
| return len + 12; | ||
| case String: | ||
| return len + 4 + Buffer.byteLength(value, 'utf8') +1; /// | ||
| case Number: | ||
| if (Math.floor(value) === value) { /// case: integer; pos.# more common, '&&' stops if 1st fails! | ||
| if ( value <= 2147483647 && value >= -2147483647 ) // 32 bit | ||
| return len + 4; | ||
| else return len + 8; /// covers Long-ish JS integers as Longs! | ||
| } else return len + 8; /// 8+1 --- covers Double & std. float | ||
| case Boolean: | ||
| return len + 1; | ||
| case Array: | ||
| case Object: | ||
| return len + calculateObjectSize(value); | ||
| case Buffer: /// replaces the entire Binary class! | ||
| return len + 4 + value.length + 1; | ||
| case Regex: /// these are handled as strings by serializeFast() later, hence 'gim' opts = 3 + 1 chars | ||
| return len + Buffer.byteLength(value.source, 'utf8') + 1 | ||
| + (value.global ? 1 : 0) + (value.ignoreCase ? 1 : 0) + (value.multiline ? 1 : 0) +1; | ||
| case Date: | ||
| case Long: | ||
| case Timestamp: | ||
| case Double: | ||
| return len + 8; | ||
| case MinKey: | ||
| case MaxKey: | ||
| return len; /// these two return the type byte and name cstring only! | ||
| } | ||
| return 0; | ||
| } | ||
| module.exports.serializeFast = serializeFast; | ||
| module.exports.serialize = function(object, checkKeys, asBuffer, serializeFunctions, index) { | ||
| var buffer = new Buffer(calculateObjectSize(object)); | ||
| return serializeFast(object, checkKeys, buffer, 0); | ||
| } | ||
| function serializeFast(object, checkKeys, buffer, i) { /// set checkKeys = false in query(..., options object to save performance IFF you're certain your keys are safe/system-set! | ||
| var size = buffer.length; | ||
| buffer[i++] = size & 0xff; buffer[i++] = (size >> 8) & 0xff; /// these get overwritten later! | ||
| buffer[i++] = (size >> 16) & 0xff; buffer[i++] = (size >> 24) & 0xff; | ||
| if (object.constructor === Array) { /// any need to checkKeys here?!? since we're doing for rather than for...in, should be safe from extra (non-numeric) keys added to the array?! | ||
| for(var j = 0; j < object.length; j++) { | ||
| i = packElement(j.toString(), object[j], checkKeys, buffer, i); | ||
| } | ||
| } else { /// checkKeys is needed if any suspicion of end-user key tampering/"injection" (a la SQL) | ||
| for(var key in object) { /// mostly there should never be direct access to them!? | ||
| if (checkKeys && (key.indexOf('\x00') >= 0 || key === '$where') ) { /// = "no script"?!; could add back key.indexOf('$') or maybe check for 'eval'?! | ||
| /// took out: || key.indexOf('.') >= 0... Don't we allow dot notation queries?! | ||
| console.log('checkKeys error: '); | ||
| return new Error('Illegal object key!'); | ||
| } | ||
| i = packElement(key, object[key], checkKeys, buffer, i); /// checkKeys pass needed for recursion! | ||
| } | ||
| } | ||
| buffer[i++] = 0; /// write terminating zero; !we do NOT -1 the index increase here as original does! | ||
| return i; | ||
| } | ||
| function packElement(name, value, checkKeys, buffer, i) { /// serializeFunctions removed! checkKeys needed for Array & Object cases pass through (calling serializeFast recursively!) | ||
| if (value === undefined || value === null){ | ||
| buffer[i++] = 10; /// = BSON.BSON_DATA_NULL; | ||
| i += buffer.write(name, i, 'utf8'); buffer[i++] = 0; /// buffer.write(...) returns bytesWritten! | ||
| return i; | ||
| } | ||
| switch(value.constructor) { | ||
| case ObjectID: | ||
| buffer[i++] = 7; /// = BSON.BSON_DATA_OID; | ||
| i += buffer.write(name, i, 'utf8'); buffer[i++] = 0; | ||
| /// i += buffer.write(value.id, i, 'binary'); /// OLD: writes a String to a Buffer; 'binary' deprecated!! | ||
| value.id.copy(buffer, i); /// NEW ObjectID version has this.id = Buffer at the ready! | ||
| return i += 12; | ||
| case String: | ||
| buffer[i++] = 2; /// = BSON.BSON_DATA_STRING; | ||
| i += buffer.write(name, i, 'utf8'); buffer[i++] = 0; | ||
| var size = Buffer.byteLength(value) + 1; /// includes the terminating '0'!? | ||
| buffer[i++] = size & 0xff; buffer[i++] = (size >> 8) & 0xff; | ||
| buffer[i++] = (size >> 16) & 0xff; buffer[i++] = (size >> 24) & 0xff; | ||
| i += buffer.write(value, i, 'utf8'); buffer[i++] = 0; | ||
| return i; | ||
| case Number: | ||
| if ( ~~(value) === value) { /// double-Tilde is equiv. to Math.floor(value) | ||
| if ( value <= 2147483647 && value >= -2147483647){ /// = BSON.BSON_INT32_MAX / MIN asf. | ||
| buffer[i++] = 16; /// = BSON.BSON_DATA_INT; | ||
| i += buffer.write(name, i, 'utf8'); buffer[i++] = 0; | ||
| buffer[i++] = value & 0xff; buffer[i++] = (value >> 8) & 0xff; | ||
| buffer[i++] = (value >> 16) & 0xff; buffer[i++] = (value >> 24) & 0xff; | ||
| // Else large-ish JS int!? to Long!? | ||
| } else { /// if (value <= BSON.JS_INT_MAX && value >= BSON.JS_INT_MIN){ /// 9007199254740992 asf. | ||
| buffer[i++] = 18; /// = BSON.BSON_DATA_LONG; | ||
| i += buffer.write(name, i, 'utf8'); buffer[i++] = 0; | ||
| var lowBits = ( value % 4294967296 ) | 0, highBits = ( value / 4294967296 ) | 0; | ||
| buffer[i++] = lowBits & 0xff; buffer[i++] = (lowBits >> 8) & 0xff; | ||
| buffer[i++] = (lowBits >> 16) & 0xff; buffer[i++] = (lowBits >> 24) & 0xff; | ||
| buffer[i++] = highBits & 0xff; buffer[i++] = (highBits >> 8) & 0xff; | ||
| buffer[i++] = (highBits >> 16) & 0xff; buffer[i++] = (highBits >> 24) & 0xff; | ||
| } | ||
| } else { /// we have a float / Double | ||
| buffer[i++] = 1; /// = BSON.BSON_DATA_NUMBER; | ||
| i += buffer.write(name, i, 'utf8'); buffer[i++] = 0; | ||
| /// OLD: writeIEEE754(buffer, value, i, 'little', 52, 8); | ||
| buffer.writeDoubleLE(value, i); i += 8; | ||
| } | ||
| return i; | ||
| case Boolean: | ||
| buffer[i++] = 8; /// = BSON.BSON_DATA_BOOLEAN; | ||
| i += buffer.write(name, i, 'utf8'); buffer[i++] = 0; | ||
| buffer[i++] = value ? 1 : 0; | ||
| return i; | ||
| case Array: | ||
| case Object: | ||
| buffer[i++] = value.constructor === Array ? 4 : 3; /// = BSON.BSON_DATA_ARRAY / _OBJECT; | ||
| i += buffer.write(name, i, 'utf8'); buffer[i++] = 0; | ||
| var endIndex = serializeFast(value, checkKeys, buffer, i); /// + 4); no longer needed b/c serializeFast writes a temp 4 bytes for length | ||
| var size = endIndex - i; | ||
| buffer[i++] = size & 0xff; buffer[i++] = (size >> 8) & 0xff; | ||
| buffer[i++] = (size >> 16) & 0xff; buffer[i++] = (size >> 24) & 0xff; | ||
| return endIndex; | ||
| /// case Binary: /// is basically identical unless special/deprecated options! | ||
| case Buffer: /// solves ALL of our Binary needs without the BSON.Binary class!? | ||
| buffer[i++] = 5; /// = BSON.BSON_DATA_BINARY; | ||
| i += buffer.write(name, i, 'utf8'); buffer[i++] = 0; | ||
| var size = value.length; | ||
| buffer[i++] = size & 0xff; buffer[i++] = (size >> 8) & 0xff; | ||
| buffer[i++] = (size >> 16) & 0xff; buffer[i++] = (size >> 24) & 0xff; | ||
| buffer[i++] = 0; /// write BSON.BSON_BINARY_SUBTYPE_DEFAULT; | ||
| value.copy(buffer, i); ///, 0, size); << defaults to sourceStart=0, sourceEnd=sourceBuffer.length); | ||
| i += size; | ||
| return i; | ||
| case RegExp: | ||
| buffer[i++] = 11; /// = BSON.BSON_DATA_REGEXP; | ||
| i += buffer.write(name, i, 'utf8'); buffer[i++] = 0; | ||
| i += buffer.write(value.source, i, 'utf8'); buffer[i++] = 0x00; | ||
| if (value.global) buffer[i++] = 0x73; // s = 'g' for JS Regex! | ||
| if (value.ignoreCase) buffer[i++] = 0x69; // i | ||
| if (value.multiline) buffer[i++] = 0x6d; // m | ||
| buffer[i++] = 0x00; | ||
| return i; | ||
| case Date: | ||
| buffer[i++] = 9; /// = BSON.BSON_DATA_DATE; | ||
| i += buffer.write(name, i, 'utf8'); buffer[i++] = 0; | ||
| var millis = value.getTime(); | ||
| var lowBits = ( millis % 4294967296 ) | 0, highBits = ( millis / 4294967296 ) | 0; | ||
| buffer[i++] = lowBits & 0xff; buffer[i++] = (lowBits >> 8) & 0xff; | ||
| buffer[i++] = (lowBits >> 16) & 0xff; buffer[i++] = (lowBits >> 24) & 0xff; | ||
| buffer[i++] = highBits & 0xff; buffer[i++] = (highBits >> 8) & 0xff; | ||
| buffer[i++] = (highBits >> 16) & 0xff; buffer[i++] = (highBits >> 24) & 0xff; | ||
| return i; | ||
| case Long: | ||
| case Timestamp: | ||
| buffer[i++] = value.constructor === Long ? 18 : 17; /// = BSON.BSON_DATA_LONG / _TIMESTAMP | ||
| i += buffer.write(name, i, 'utf8'); buffer[i++] = 0; | ||
| var lowBits = value.getLowBits(), highBits = value.getHighBits(); | ||
| buffer[i++] = lowBits & 0xff; buffer[i++] = (lowBits >> 8) & 0xff; | ||
| buffer[i++] = (lowBits >> 16) & 0xff; buffer[i++] = (lowBits >> 24) & 0xff; | ||
| buffer[i++] = highBits & 0xff; buffer[i++] = (highBits >> 8) & 0xff; | ||
| buffer[i++] = (highBits >> 16) & 0xff; buffer[i++] = (highBits >> 24) & 0xff; | ||
| return i; | ||
| case Double: | ||
| buffer[i++] = 1; /// = BSON.BSON_DATA_NUMBER; | ||
| i += buffer.write(name, i, 'utf8'); buffer[i++] = 0; | ||
| /// OLD: writeIEEE754(buffer, value, i, 'little', 52, 8); i += 8; | ||
| buffer.writeDoubleLE(value, i); i += 8; | ||
| return i | ||
| case MinKey: /// = BSON.BSON_DATA_MINKEY; | ||
| buffer[i++] = 127; i += buffer.write(name, i, 'utf8'); buffer[i++] = 0; | ||
| return i; | ||
| case MaxKey: /// = BSON.BSON_DATA_MAXKEY; | ||
| buffer[i++] = 255; i += buffer.write(name, i, 'utf8'); buffer[i++] = 0; | ||
| return i; | ||
| } /// end of switch | ||
| return i; /// ?! If no value to serialize | ||
| } | ||
| module.exports.deserializeFast = deserializeFast; | ||
| function deserializeFast(buffer, i, isArray){ //// , options, isArray) { //// no more options! | ||
| if (buffer.length < 5) return new Error('Corrupt bson message < 5 bytes long'); /// from 'throw' | ||
| var elementType, tempindex = 0, name; | ||
| var string, low, high; /// = lowBits / highBits | ||
| /// using 'i' as the index to keep the lines shorter: | ||
| i || ( i = 0 ); /// for parseResponse it's 0; set to running index in deserialize(object/array) recursion | ||
| var object = isArray ? [] : {}; /// needed for type ARRAY recursion later! | ||
| var size = buffer[i++] | buffer[i++] << 8 | buffer[i++] << 16 | buffer[i++] << 24; | ||
| if(size < 5 || size > buffer.length) return new Error('Corrupt BSON message'); | ||
| /// 'size' var was not used by anything after this, so we can reuse it | ||
| while(true) { // While we have more left data left keep parsing | ||
| elementType = buffer[i++]; // Read the type | ||
| if (elementType === 0) break; // If we get a zero it's the last byte, exit | ||
| tempindex = i; /// inlined readCStyleString & removed extra i<buffer.length check slowing EACH loop! | ||
| while( buffer[tempindex] !== 0x00 ) tempindex++; /// read ahead w/out changing main 'i' index | ||
| if (tempindex >= buffer.length) return new Error('Corrupt BSON document: illegal CString') | ||
| name = buffer.toString('utf8', i, tempindex); | ||
| i = tempindex + 1; /// Update index position to after the string + '0' termination | ||
| switch(elementType) { | ||
| case 7: /// = BSON.BSON_DATA_OID: | ||
| var buf = new Buffer(12); | ||
| buffer.copy(buf, 0, i, i += 12 ); /// copy 12 bytes from the current 'i' offset into fresh Buffer | ||
| object[name] = new ObjectID(buf); ///... & attach to the new ObjectID instance | ||
| break; | ||
| case 2: /// = BSON.BSON_DATA_STRING: | ||
| size = buffer[i++] | buffer[i++] <<8 | buffer[i++] <<16 | buffer[i++] <<24; | ||
| object[name] = buffer.toString('utf8', i, i += size -1 ); | ||
| i++; break; /// need to get the '0' index "tick-forward" back! | ||
| case 16: /// = BSON.BSON_DATA_INT: // Decode the 32bit value | ||
| object[name] = buffer[i++] | buffer[i++] << 8 | buffer[i++] << 16 | buffer[i++] << 24; break; | ||
| case 1: /// = BSON.BSON_DATA_NUMBER: // Decode the double value | ||
| object[name] = buffer.readDoubleLE(i); /// slightly faster depending on dec.points; a LOT cleaner | ||
| /// OLD: object[name] = readIEEE754(buffer, i, 'little', 52, 8); | ||
| i += 8; break; | ||
| case 8: /// = BSON.BSON_DATA_BOOLEAN: | ||
| object[name] = buffer[i++] == 1; break; | ||
| case 6: /// = BSON.BSON_DATA_UNDEFINED: /// deprecated | ||
| case 10: /// = BSON.BSON_DATA_NULL: | ||
| object[name] = null; break; | ||
| case 4: /// = BSON.BSON_DATA_ARRAY | ||
| size = buffer[i] | buffer[i+1] <<8 | buffer[i+2] <<16 | buffer[i+3] <<24; /// NO 'i' increment since the size bytes are reread during the recursion! | ||
| object[name] = deserializeFast(buffer, i, true ); /// pass current index & set isArray = true | ||
| i += size; break; | ||
| case 3: /// = BSON.BSON_DATA_OBJECT: | ||
| size = buffer[i] | buffer[i+1] <<8 | buffer[i+2] <<16 | buffer[i+3] <<24; | ||
| object[name] = deserializeFast(buffer, i, false ); /// isArray = false => Object | ||
| i += size; break; | ||
| case 5: /// = BSON.BSON_DATA_BINARY: // Decode the size of the binary blob | ||
| size = buffer[i++] | buffer[i++] << 8 | buffer[i++] << 16 | buffer[i++] << 24; | ||
| buffer[i++]; /// Skip, as we assume always default subtype, i.e. 0! | ||
| object[name] = buffer.slice(i, i += size); /// creates a new Buffer "slice" view of the same memory! | ||
| break; | ||
| case 9: /// = BSON.BSON_DATA_DATE: /// SEE notes below on the Date type vs. other options... | ||
| low = buffer[i++] | buffer[i++] << 8 | buffer[i++] << 16 | buffer[i++] << 24; | ||
| high = buffer[i++] | buffer[i++] << 8 | buffer[i++] << 16 | buffer[i++] << 24; | ||
| object[name] = new Date( high * 4294967296 + (low < 0 ? low + 4294967296 : low) ); break; | ||
| case 18: /// = BSON.BSON_DATA_LONG: /// usage should be somewhat rare beyond parseResponse() -> cursorId, where it is handled inline, NOT as part of deserializeFast(returnedObjects); get lowBits, highBits: | ||
| low = buffer[i++] | buffer[i++] << 8 | buffer[i++] << 16 | buffer[i++] << 24; | ||
| high = buffer[i++] | buffer[i++] << 8 | buffer[i++] << 16 | buffer[i++] << 24; | ||
| size = high * 4294967296 + (low < 0 ? low + 4294967296 : low); /// from long.toNumber() | ||
| if (size < JS_INT_MAX && size > JS_INT_MIN) object[name] = size; /// positive # more likely! | ||
| else object[name] = new Long(low, high); break; | ||
| case 127: /// = BSON.BSON_DATA_MIN_KEY: /// do we EVER actually get these BACK from MongoDB server?! | ||
| object[name] = new MinKey(); break; | ||
| case 255: /// = BSON.BSON_DATA_MAX_KEY: | ||
| object[name] = new MaxKey(); break; | ||
| case 17: /// = BSON.BSON_DATA_TIMESTAMP: /// somewhat obscure internal BSON type; MongoDB uses it for (pseudo) high-res time timestamp (past millisecs precision is just a counter!) in the Oplog ts: field, etc. | ||
| low = buffer[i++] | buffer[i++] << 8 | buffer[i++] << 16 | buffer[i++] << 24; | ||
| high = buffer[i++] | buffer[i++] << 8 | buffer[i++] << 16 | buffer[i++] << 24; | ||
| object[name] = new Timestamp(low, high); break; | ||
| /// case 11: /// = RegExp is skipped; we should NEVER be getting any from the MongoDB server!? | ||
| } /// end of switch(elementType) | ||
| } /// end of while(1) | ||
| return object; // Return the finalized object | ||
| } | ||
| function MinKey() { this._bsontype = 'MinKey'; } /// these are merely placeholders/stubs to signify the type!? | ||
| function MaxKey() { this._bsontype = 'MaxKey'; } | ||
| function Long(low, high) { | ||
| this._bsontype = 'Long'; | ||
| this.low_ = low | 0; this.high_ = high | 0; /// force into 32 signed bits. | ||
| } | ||
| Long.prototype.getLowBits = function(){ return this.low_; } | ||
| Long.prototype.getHighBits = function(){ return this.high_; } | ||
| Long.prototype.toNumber = function(){ | ||
| return this.high_ * 4294967296 + (this.low_ < 0 ? this.low_ + 4294967296 : this.low_); | ||
| } | ||
| Long.fromNumber = function(num){ | ||
| return new Long(num % 4294967296, num / 4294967296); /// |0 is forced in the constructor! | ||
| } | ||
| function Double(value) { | ||
| this._bsontype = 'Double'; | ||
| this.value = value; | ||
| } | ||
| function Timestamp(low, high) { | ||
| this._bsontype = 'Timestamp'; | ||
| this.low_ = low | 0; this.high_ = high | 0; /// force into 32 signed bits. | ||
| } | ||
| Timestamp.prototype.getLowBits = function(){ return this.low_; } | ||
| Timestamp.prototype.getHighBits = function(){ return this.high_; } | ||
| /////////////////////////////// ObjectID ///////////////////////////////// | ||
| /// machine & proc IDs stored as 1 string, b/c Buffer shouldn't be held for long periods (could use SlowBuffer?!) | ||
| var MACHINE = parseInt(Math.random() * 0xFFFFFF, 10); | ||
| var PROCESS = process.pid % 0xFFFF; | ||
| var MACHINE_AND_PROC = encodeIntBE(MACHINE, 3) + encodeIntBE(PROCESS, 2); /// keep as ONE string, ready to go. | ||
| function encodeIntBE(data, bytes){ /// encode the bytes to a string | ||
| var result = ''; | ||
| if (bytes >= 4){ result += String.fromCharCode(Math.floor(data / 0x1000000)); data %= 0x1000000; } | ||
| if (bytes >= 3){ result += String.fromCharCode(Math.floor(data / 0x10000)); data %= 0x10000; } | ||
| if (bytes >= 2){ result += String.fromCharCode(Math.floor(data / 0x100)); data %= 0x100; } | ||
| result += String.fromCharCode(Math.floor(data)); | ||
| return result; | ||
| } | ||
| var _counter = ~~(Math.random() * 0xFFFFFF); /// double-tilde is equivalent to Math.floor() | ||
| var checkForHex = new RegExp('^[0-9a-fA-F]{24}$'); | ||
| function ObjectID(id) { | ||
| this._bsontype = 'ObjectID'; | ||
| if (!id){ this.id = createFromScratch(); /// base case, DONE. | ||
| } else { | ||
| if (id.constructor === Buffer){ | ||
| this.id = id; /// case of | ||
| } else if (id.constructor === String) { | ||
| if ( id.length === 24 && checkForHex.test(id) ) { | ||
| this.id = new Buffer(id, 'hex'); | ||
| } else { | ||
| this.id = new Error('Illegal/faulty Hexadecimal string supplied!'); /// changed from 'throw' | ||
| } | ||
| } else if (id.constructor === Number) { | ||
| this.id = createFromTime(id); /// this is what should be the only interface for this!? | ||
| } | ||
| } | ||
| } | ||
| function createFromScratch() { | ||
| var buf = new Buffer(12), i = 0; | ||
| var ts = ~~(Date.now()/1000); /// 4 bytes timestamp in seconds, BigEndian notation! | ||
| buf[i++] = (ts >> 24) & 0xFF; buf[i++] = (ts >> 16) & 0xFF; | ||
| buf[i++] = (ts >> 8) & 0xFF; buf[i++] = (ts) & 0xFF; | ||
| buf.write(MACHINE_AND_PROC, i, 5, 'utf8'); i += 5; /// write 3 bytes + 2 bytes MACHINE_ID and PROCESS_ID | ||
| _counter = ++_counter % 0xFFFFFF; /// 3 bytes internal _counter for subsecond resolution; BigEndian | ||
| buf[i++] = (_counter >> 16) & 0xFF; | ||
| buf[i++] = (_counter >> 8) & 0xFF; | ||
| buf[i++] = (_counter) & 0xFF; | ||
| return buf; | ||
| } | ||
| function createFromTime(ts) { | ||
| ts || ( ts = ~~(Date.now()/1000) ); /// 4 bytes timestamp in seconds only | ||
| var buf = new Buffer(12), i = 0; | ||
| buf[i++] = (ts >> 24) & 0xFF; buf[i++] = (ts >> 16) & 0xFF; | ||
| buf[i++] = (ts >> 8) & 0xFF; buf[i++] = (ts) & 0xFF; | ||
| for (;i < 12; ++i) buf[i] = 0x00; /// indeces 4 through 11 (8 bytes) get filled up with nulls | ||
| return buf; | ||
| } | ||
| ObjectID.prototype.toHexString = function toHexString() { | ||
| return this.id.toString('hex'); | ||
| } | ||
| ObjectID.prototype.getTimestamp = function getTimestamp() { | ||
| return this.id.readUIntBE(0, 4); | ||
| } | ||
| ObjectID.prototype.getTimestampDate = function getTimestampDate() { | ||
| var ts = new Date(); | ||
| ts.setTime(this.id.readUIntBE(0, 4) * 1000); | ||
| return ts; | ||
| } | ||
| ObjectID.createPk = function createPk () { ///?override if a PrivateKey factory w/ unique factors is warranted?! | ||
| return new ObjectID(); | ||
| } | ||
| ObjectID.prototype.toJSON = function toJSON() { | ||
| return "ObjectID('" +this.id.toString('hex')+ "')"; | ||
| } | ||
| /// module.exports.BSON = BSON; /// not needed anymore!? exports.Binary = Binary; | ||
| module.exports.ObjectID = ObjectID; | ||
| module.exports.MinKey = MinKey; | ||
| module.exports.MaxKey = MaxKey; | ||
| module.exports.Long = Long; /// ?! we really don't want to do the complicated Long math anywhere for now!? | ||
| //module.exports.Double = Double; | ||
| //module.exports.Timestamp = Timestamp; |
| "use strict" | ||
| var writeIEEE754 = require('../float_parser').writeIEEE754 | ||
| , readIEEE754 = require('../float_parser').readIEEE754 | ||
| , 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 | ||
| , Binary = require('../binary').Binary; | ||
| // To ensure that 0.4 of node works correctly | ||
| var isDate = function isDate(d) { | ||
| return typeof d === 'object' && Object.prototype.toString.call(d) === '[object Date]'; | ||
| } | ||
| var calculateObjectSize = function calculateObjectSize(object, serializeFunctions) { | ||
| var totalLength = (4 + 1); | ||
| if(Array.isArray(object)) { | ||
| for(var i = 0; i < object.length; i++) { | ||
| totalLength += calculateElement(i.toString(), object[i], serializeFunctions) | ||
| } | ||
| } else { | ||
| // If we have toBSON defined, override the current object | ||
| if(object.toBSON) { | ||
| object = object.toBSON(); | ||
| } | ||
| // Calculate size | ||
| for(var key in object) { | ||
| totalLength += calculateElement(key, object[key], serializeFunctions) | ||
| } | ||
| } | ||
| return totalLength; | ||
| } | ||
| /** | ||
| * @ignore | ||
| * @api private | ||
| */ | ||
| function calculateElement(name, value, serializeFunctions) { | ||
| // If we have toBSON defined, override the current object | ||
| if(value && value.toBSON){ | ||
| value = value.toBSON(); | ||
| } | ||
| switch(typeof value) { | ||
| case 'string': | ||
| return 1 + Buffer.byteLength(name, 'utf8') + 1 + 4 + Buffer.byteLength(value, 'utf8') + 1; | ||
| case 'number': | ||
| if(Math.floor(value) === value && value >= BSON.JS_INT_MIN && value <= BSON.JS_INT_MAX) { | ||
| if(value >= BSON.BSON_INT32_MIN && value <= BSON.BSON_INT32_MAX) { // 32 bit | ||
| return (name != null ? (Buffer.byteLength(name, 'utf8') + 1) : 0) + (4 + 1); | ||
| } else { | ||
| return (name != null ? (Buffer.byteLength(name, 'utf8') + 1) : 0) + (8 + 1); | ||
| } | ||
| } else { // 64 bit | ||
| return (name != null ? (Buffer.byteLength(name, 'utf8') + 1) : 0) + (8 + 1); | ||
| } | ||
| case 'undefined': | ||
| return (name != null ? (Buffer.byteLength(name, 'utf8') + 1) : 0) + (1); | ||
| case 'boolean': | ||
| return (name != null ? (Buffer.byteLength(name, 'utf8') + 1) : 0) + (1 + 1); | ||
| case 'object': | ||
| if(value == null || value instanceof MinKey || value instanceof MaxKey || value['_bsontype'] == 'MinKey' || value['_bsontype'] == 'MaxKey') { | ||
| return (name != null ? (Buffer.byteLength(name, 'utf8') + 1) : 0) + (1); | ||
| } else if(value instanceof ObjectID || value['_bsontype'] == 'ObjectID') { | ||
| return (name != null ? (Buffer.byteLength(name, 'utf8') + 1) : 0) + (12 + 1); | ||
| } else if(value instanceof Date || isDate(value)) { | ||
| return (name != null ? (Buffer.byteLength(name, 'utf8') + 1) : 0) + (8 + 1); | ||
| } else if(typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) { | ||
| return (name != null ? (Buffer.byteLength(name, 'utf8') + 1) : 0) + (1 + 4 + 1) + value.length; | ||
| } else if(value instanceof Long || value instanceof Double || value instanceof Timestamp | ||
| || value['_bsontype'] == 'Long' || value['_bsontype'] == 'Double' || value['_bsontype'] == 'Timestamp') { | ||
| return (name != null ? (Buffer.byteLength(name, 'utf8') + 1) : 0) + (8 + 1); | ||
| } else if(value instanceof Code || value['_bsontype'] == 'Code') { | ||
| // Calculate size depending on the availability of a scope | ||
| if(value.scope != null && Object.keys(value.scope).length > 0) { | ||
| return (name != null ? (Buffer.byteLength(name, 'utf8') + 1) : 0) + 1 + 4 + 4 + Buffer.byteLength(value.code.toString(), 'utf8') + 1 + calculateObjectSize(value.scope, serializeFunctions); | ||
| } else { | ||
| return (name != null ? (Buffer.byteLength(name, 'utf8') + 1) : 0) + 1 + 4 + Buffer.byteLength(value.code.toString(), 'utf8') + 1; | ||
| } | ||
| } else if(value instanceof Binary || value['_bsontype'] == 'Binary') { | ||
| // Check what kind of subtype we have | ||
| if(value.sub_type == Binary.SUBTYPE_BYTE_ARRAY) { | ||
| return (name != null ? (Buffer.byteLength(name, 'utf8') + 1) : 0) + (value.position + 1 + 4 + 1 + 4); | ||
| } else { | ||
| return (name != null ? (Buffer.byteLength(name, 'utf8') + 1) : 0) + (value.position + 1 + 4 + 1); | ||
| } | ||
| } else if(value instanceof Symbol || value['_bsontype'] == 'Symbol') { | ||
| return (name != null ? (Buffer.byteLength(name, 'utf8') + 1) : 0) + Buffer.byteLength(value.value, 'utf8') + 4 + 1 + 1; | ||
| } else if(value instanceof DBRef || value['_bsontype'] == 'DBRef') { | ||
| // Set up correct object for serialization | ||
| var ordered_values = { | ||
| '$ref': value.namespace | ||
| , '$id' : value.oid | ||
| }; | ||
| // Add db reference if it exists | ||
| if(null != value.db) { | ||
| ordered_values['$db'] = value.db; | ||
| } | ||
| return (name != null ? (Buffer.byteLength(name, 'utf8') + 1) : 0) + 1 + calculateObjectSize(ordered_values, serializeFunctions); | ||
| } else if(value instanceof RegExp || Object.prototype.toString.call(value) === '[object RegExp]') { | ||
| return (name != null ? (Buffer.byteLength(name, 'utf8') + 1) : 0) + 1 + Buffer.byteLength(value.source, 'utf8') + 1 | ||
| + (value.global ? 1 : 0) + (value.ignoreCase ? 1 : 0) + (value.multiline ? 1 : 0) + 1 | ||
| } else { | ||
| return (name != null ? (Buffer.byteLength(name, 'utf8') + 1) : 0) + calculateObjectSize(value, serializeFunctions) + 1; | ||
| } | ||
| case 'function': | ||
| // WTF for 0.4.X where typeof /someregexp/ === 'function' | ||
| if(value instanceof RegExp || Object.prototype.toString.call(value) === '[object RegExp]' || String.call(value) == '[object RegExp]') { | ||
| return (name != null ? (Buffer.byteLength(name, 'utf8') + 1) : 0) + 1 + Buffer.byteLength(value.source, 'utf8') + 1 | ||
| + (value.global ? 1 : 0) + (value.ignoreCase ? 1 : 0) + (value.multiline ? 1 : 0) + 1 | ||
| } else { | ||
| if(serializeFunctions && value.scope != null && Object.keys(value.scope).length > 0) { | ||
| return (name != null ? (Buffer.byteLength(name, 'utf8') + 1) : 0) + 1 + 4 + 4 + Buffer.byteLength(value.toString(), 'utf8') + 1 + calculateObjectSize(value.scope, serializeFunctions); | ||
| } else if(serializeFunctions) { | ||
| return (name != null ? (Buffer.byteLength(name, 'utf8') + 1) : 0) + 1 + 4 + Buffer.byteLength(value.toString(), 'utf8') + 1; | ||
| } | ||
| } | ||
| } | ||
| return 0; | ||
| } | ||
| 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. | ||
| module.exports = calculateObjectSize; |
| "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, | ||
| Binary = require('../binary').Binary; | ||
| var deserialize = 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']; | ||
| // 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; | ||
| // Reads in a C style string | ||
| var readCStyleString = function() { | ||
| // 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 string; | ||
| } | ||
| // Create holding object | ||
| var object = isArray ? [] : {}; | ||
| // 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"); | ||
| // 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; | ||
| // Read the name of the field | ||
| var name = readCStyleString(); | ||
| // Switch on the type | ||
| if(elementType == BSON.BSON_DATA_OID) { | ||
| var string = buffer.toString('binary', index, index + 12); | ||
| // Decode the oid | ||
| object[name] = new ObjectID(string); | ||
| // Update index | ||
| index = index + 12; | ||
| } else if(elementType == BSON.BSON_DATA_STRING) { | ||
| // Read the content of the field | ||
| var stringSize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; | ||
| // Validate if string Size is larger than the actual provided buffer | ||
| if(stringSize <= 0 || stringSize > (buffer.length - index) || buffer[index + stringSize - 1] != 0) throw new Error("bad string length in bson"); | ||
| // Add string to object | ||
| object[name] = buffer.toString('utf8', index, index + stringSize - 1); | ||
| // Update parse index position | ||
| index = index + stringSize; | ||
| } else if(elementType == BSON.BSON_DATA_INT) { | ||
| // Decode the 32bit value | ||
| object[name] = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; | ||
| } else if(elementType == BSON.BSON_DATA_NUMBER) { | ||
| // Decode the double value | ||
| object[name] = readIEEE754(buffer, index, 'little', 52, 8); | ||
| // Update the index | ||
| index = index + 8; | ||
| } else if(elementType == BSON.BSON_DATA_DATE) { | ||
| // Unpack the low and high bits | ||
| 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; | ||
| // Set date object | ||
| object[name] = new Date(new Long(lowBits, highBits).toNumber()); | ||
| } else if(elementType == BSON.BSON_DATA_BOOLEAN) { | ||
| // Parse the boolean value | ||
| object[name] = buffer[index++] == 1; | ||
| } else if(elementType == BSON.BSON_DATA_UNDEFINED || elementType == BSON.BSON_DATA_NULL) { | ||
| // Parse the boolean value | ||
| object[name] = null; | ||
| } else if(elementType == BSON.BSON_DATA_BINARY) { | ||
| // Decode the size of the binary blob | ||
| var binarySize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; | ||
| // Decode the subtype | ||
| var subType = buffer[index++]; | ||
| // Decode as raw Buffer object if options specifies it | ||
| if(buffer['slice'] != null) { | ||
| // If we have subtype 2 skip the 4 bytes for the size | ||
| if(subType == Binary.SUBTYPE_BYTE_ARRAY) { | ||
| binarySize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; | ||
| } | ||
| // Slice the data | ||
| object[name] = new Binary(buffer.slice(index, index + binarySize), subType); | ||
| } else { | ||
| var _buffer = typeof Uint8Array != 'undefined' ? new Uint8Array(new ArrayBuffer(binarySize)) : new Array(binarySize); | ||
| // If we have subtype 2 skip the 4 bytes for the size | ||
| if(subType == Binary.SUBTYPE_BYTE_ARRAY) { | ||
| binarySize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; | ||
| } | ||
| // Copy the data | ||
| for(var i = 0; i < binarySize; i++) { | ||
| _buffer[i] = buffer[index + i]; | ||
| } | ||
| // Create the binary object | ||
| object[name] = new Binary(_buffer, subType); | ||
| } | ||
| // Update the index | ||
| index = index + binarySize; | ||
| } else if(elementType == BSON.BSON_DATA_ARRAY) { | ||
| options['index'] = index; | ||
| // Decode the size of the array document | ||
| var objectSize = buffer[index] | buffer[index + 1] << 8 | buffer[index + 2] << 16 | buffer[index + 3] << 24; | ||
| // Set the array to the object | ||
| object[name] = deserialize(buffer, options, true); | ||
| // Adjust the index | ||
| index = index + objectSize; | ||
| } else if(elementType == BSON.BSON_DATA_OBJECT) { | ||
| options['index'] = index; | ||
| // Decode the size of the object document | ||
| var objectSize = buffer[index] | buffer[index + 1] << 8 | buffer[index + 2] << 16 | buffer[index + 3] << 24; | ||
| // Validate if string Size is larger than the actual provided buffer | ||
| if(objectSize <= 0 || objectSize > (buffer.length - index)) throw new Error("bad embedded document length in bson"); | ||
| // Set the array to the object | ||
| object[name] = deserialize(buffer, options, false); | ||
| // Adjust the index | ||
| index = index + objectSize; | ||
| } else if(elementType == BSON.BSON_DATA_REGEXP) { | ||
| // Create the regexp | ||
| var source = readCStyleString(); | ||
| var regExpOptions = readCStyleString(); | ||
| // For each option add the corresponding one for javascript | ||
| var optionsArray = new Array(regExpOptions.length); | ||
| // Parse options | ||
| for(var i = 0; i < regExpOptions.length; i++) { | ||
| switch(regExpOptions[i]) { | ||
| case 'm': | ||
| optionsArray[i] = 'm'; | ||
| break; | ||
| case 's': | ||
| optionsArray[i] = 'g'; | ||
| break; | ||
| case 'i': | ||
| optionsArray[i] = 'i'; | ||
| break; | ||
| } | ||
| } | ||
| object[name] = new RegExp(source, optionsArray.join('')); | ||
| } else if(elementType == BSON.BSON_DATA_LONG) { | ||
| // Unpack the low and high bits | ||
| 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; | ||
| // Create long object | ||
| var long = new Long(lowBits, highBits); | ||
| // Promote the long if possible | ||
| if(promoteLongs) { | ||
| object[name] = long.lessThanOrEqual(JS_INT_MAX_LONG) && long.greaterThanOrEqual(JS_INT_MIN_LONG) ? long.toNumber() : long; | ||
| } else { | ||
| object[name] = long; | ||
| } | ||
| } else if(elementType == BSON.BSON_DATA_SYMBOL) { | ||
| // Read the content of the field | ||
| var stringSize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; | ||
| // Validate if string Size is larger than the actual provided buffer | ||
| if(stringSize <= 0 || stringSize > (buffer.length - index) || buffer[index + stringSize - 1] != 0) throw new Error("bad string length in bson"); | ||
| // Add string to object | ||
| object[name] = new Symbol(buffer.toString('utf8', index, index + stringSize - 1)); | ||
| // Update parse index position | ||
| index = index + stringSize; | ||
| } else if(elementType == BSON.BSON_DATA_TIMESTAMP) { | ||
| // Unpack the low and high bits | ||
| 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; | ||
| // Set the object | ||
| object[name] = new Timestamp(lowBits, highBits); | ||
| } else if(elementType == BSON.BSON_DATA_MIN_KEY) { | ||
| // Parse the object | ||
| object[name] = new MinKey(); | ||
| } else if(elementType == BSON.BSON_DATA_MAX_KEY) { | ||
| // Parse the object | ||
| object[name] = new MaxKey(); | ||
| } else if(elementType == BSON.BSON_DATA_CODE) { | ||
| // Read the content of the field | ||
| var stringSize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; | ||
| // Validate if string Size is larger than the actual provided buffer | ||
| if(stringSize <= 0 || stringSize > (buffer.length - index) || buffer[index + stringSize - 1] != 0) throw new Error("bad string length in bson"); | ||
| // Function string | ||
| var functionString = buffer.toString('utf8', index, index + stringSize - 1); | ||
| // If we are evaluating the functions | ||
| if(evalFunctions) { | ||
| // Contains the value we are going to set | ||
| var value = null; | ||
| // If we have cache enabled let's look for the md5 of the function in the cache | ||
| if(cacheFunctions) { | ||
| var hash = cacheFunctionsCrc32 ? crc32(functionString) : functionString; | ||
| // Got to do this to avoid V8 deoptimizing the call due to finding eval | ||
| object[name] = isolateEvalWithHash(functionCache, hash, functionString, object); | ||
| } else { | ||
| // Set directly | ||
| object[name] = isolateEval(functionString); | ||
| } | ||
| } else { | ||
| object[name] = new Code(functionString, {}); | ||
| } | ||
| // Update parse index position | ||
| index = index + stringSize; | ||
| } else if(elementType == BSON.BSON_DATA_CODE_W_SCOPE) { | ||
| // Read the content of the field | ||
| var totalSize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; | ||
| var stringSize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; | ||
| // Validate if string Size is larger than the actual provided buffer | ||
| if(stringSize <= 0 || stringSize > (buffer.length - index) || buffer[index + stringSize - 1] != 0) throw new Error("bad string length in bson"); | ||
| // Javascript function | ||
| var functionString = buffer.toString('utf8', index, index + stringSize - 1); | ||
| // Update parse index position | ||
| index = index + stringSize; | ||
| // Parse the element | ||
| options['index'] = index; | ||
| // Decode the size of the object document | ||
| var objectSize = buffer[index] | buffer[index + 1] << 8 | buffer[index + 2] << 16 | buffer[index + 3] << 24; | ||
| // Decode the scope object | ||
| var scopeObject = deserialize(buffer, options, false); | ||
| // Adjust the index | ||
| index = index + objectSize; | ||
| // If we are evaluating the functions | ||
| if(evalFunctions) { | ||
| // Contains the value we are going to set | ||
| var value = null; | ||
| // If we have cache enabled let's look for the md5 of the function in the cache | ||
| if(cacheFunctions) { | ||
| var hash = cacheFunctionsCrc32 ? crc32(functionString) : functionString; | ||
| // Got to do this to avoid V8 deoptimizing the call due to finding eval | ||
| object[name] = isolateEvalWithHash(functionCache, hash, functionString, object); | ||
| } else { | ||
| // Set directly | ||
| object[name] = isolateEval(functionString); | ||
| } | ||
| // Set the scope on the object | ||
| object[name].scope = scopeObject; | ||
| } else { | ||
| object[name] = new Code(functionString, scopeObject); | ||
| } | ||
| } | ||
| } | ||
| // 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; | ||
| } | ||
| 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. | ||
| module.exports = deserialize |
| "use strict" | ||
| var writeIEEE754 = require('../float_parser').writeIEEE754 | ||
| , readIEEE754 = require('../float_parser').readIEEE754 | ||
| , 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 | ||
| , Binary = require('../binary').Binary; | ||
| // To ensure that 0.4 of node works correctly | ||
| var isDate = function isDate(d) { | ||
| return typeof d === 'object' && Object.prototype.toString.call(d) === '[object Date]'; | ||
| } | ||
| var checkKey = function checkKey (key, dollarsAndDotsOk) { | ||
| if (!key.length) return; | ||
| // Check if we have a legal key for the object | ||
| if (!!~key.indexOf("\x00")) { | ||
| // The BSON spec doesn't allow keys with null bytes because keys are | ||
| // null-terminated. | ||
| throw Error("key " + key + " must not contain null bytes"); | ||
| } | ||
| if (!dollarsAndDotsOk) { | ||
| if('$' == key[0]) { | ||
| throw Error("key " + key + " must not start with '$'"); | ||
| } else if (!!~key.indexOf('.')) { | ||
| throw Error("key " + key + " must not contain '.'"); | ||
| } | ||
| } | ||
| }; | ||
| var serializeString = function(buffer, key, value, index) { | ||
| // Encode String type | ||
| buffer[index++] = BSON.BSON_DATA_STRING; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes + 1; | ||
| buffer[index - 1] = 0; | ||
| // Calculate size | ||
| var size = Buffer.byteLength(value) + 1; | ||
| // Write the size of the string to buffer | ||
| buffer[index + 3] = (size >> 24) & 0xff; | ||
| buffer[index + 2] = (size >> 16) & 0xff; | ||
| buffer[index + 1] = (size >> 8) & 0xff; | ||
| buffer[index] = size & 0xff; | ||
| // Ajust the index | ||
| index = index + 4; | ||
| // Write the string | ||
| buffer.write(value, index, 'utf8'); | ||
| // Update index | ||
| index = index + size - 1; | ||
| // Write zero | ||
| buffer[index++] = 0; | ||
| return index; | ||
| } | ||
| var serializeNumber = function(buffer, key, value, index) { | ||
| // We have an integer value | ||
| if(Math.floor(value) === value && value >= BSON.JS_INT_MIN && value <= BSON.JS_INT_MAX) { | ||
| // If the value fits in 32 bits encode as int, if it fits in a double | ||
| // encode it as a double, otherwise long | ||
| if(value >= BSON.BSON_INT32_MIN && value <= BSON.BSON_INT32_MAX) { | ||
| // Set int type 32 bits or less | ||
| buffer[index++] = BSON.BSON_DATA_INT; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Write the int value | ||
| buffer[index++] = value & 0xff; | ||
| buffer[index++] = (value >> 8) & 0xff; | ||
| buffer[index++] = (value >> 16) & 0xff; | ||
| buffer[index++] = (value >> 24) & 0xff; | ||
| } else if(value >= BSON.JS_INT_MIN && value <= BSON.JS_INT_MAX) { | ||
| // Encode as double | ||
| buffer[index++] = BSON.BSON_DATA_NUMBER; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Write float | ||
| writeIEEE754(buffer, value, index, 'little', 52, 8); | ||
| // Ajust index | ||
| index = index + 8; | ||
| } else { | ||
| // Set long type | ||
| buffer[index++] = BSON.BSON_DATA_LONG; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| var longVal = Long.fromNumber(value); | ||
| var lowBits = longVal.getLowBits(); | ||
| var highBits = longVal.getHighBits(); | ||
| // Encode low bits | ||
| buffer[index++] = lowBits & 0xff; | ||
| buffer[index++] = (lowBits >> 8) & 0xff; | ||
| buffer[index++] = (lowBits >> 16) & 0xff; | ||
| buffer[index++] = (lowBits >> 24) & 0xff; | ||
| // Encode high bits | ||
| buffer[index++] = highBits & 0xff; | ||
| buffer[index++] = (highBits >> 8) & 0xff; | ||
| buffer[index++] = (highBits >> 16) & 0xff; | ||
| buffer[index++] = (highBits >> 24) & 0xff; | ||
| } | ||
| } else { | ||
| // Encode as double | ||
| buffer[index++] = BSON.BSON_DATA_NUMBER; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Write float | ||
| writeIEEE754(buffer, value, index, 'little', 52, 8); | ||
| // Ajust index | ||
| index = index + 8; | ||
| } | ||
| return index; | ||
| } | ||
| var serializeUndefined = function(buffer, key, value, index) { | ||
| // Set long type | ||
| buffer[index++] = BSON.BSON_DATA_NULL; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| return index; | ||
| } | ||
| var serializeBoolean = function(buffer, key, value, index) { | ||
| // Write the type | ||
| buffer[index++] = BSON.BSON_DATA_BOOLEAN; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Encode the boolean value | ||
| buffer[index++] = value ? 1 : 0; | ||
| return index; | ||
| } | ||
| var serializeDate = function(buffer, key, value, index) { | ||
| // Write the type | ||
| buffer[index++] = BSON.BSON_DATA_DATE; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Write the date | ||
| var dateInMilis = Long.fromNumber(value.getTime()); | ||
| var lowBits = dateInMilis.getLowBits(); | ||
| var highBits = dateInMilis.getHighBits(); | ||
| // Encode low bits | ||
| buffer[index++] = lowBits & 0xff; | ||
| buffer[index++] = (lowBits >> 8) & 0xff; | ||
| buffer[index++] = (lowBits >> 16) & 0xff; | ||
| buffer[index++] = (lowBits >> 24) & 0xff; | ||
| // Encode high bits | ||
| buffer[index++] = highBits & 0xff; | ||
| buffer[index++] = (highBits >> 8) & 0xff; | ||
| buffer[index++] = (highBits >> 16) & 0xff; | ||
| buffer[index++] = (highBits >> 24) & 0xff; | ||
| return index; | ||
| } | ||
| var serializeRegExp = function(buffer, key, value, index) { | ||
| // Write the type | ||
| buffer[index++] = BSON.BSON_DATA_REGEXP; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Write the regular expression string | ||
| buffer.write(value.source, index, 'utf8'); | ||
| // Adjust the index | ||
| index = index + Buffer.byteLength(value.source); | ||
| // Write zero | ||
| buffer[index++] = 0x00; | ||
| // Write the parameters | ||
| if(value.global) buffer[index++] = 0x73; // s | ||
| if(value.ignoreCase) buffer[index++] = 0x69; // i | ||
| if(value.multiline) buffer[index++] = 0x6d; // m | ||
| // Add ending zero | ||
| buffer[index++] = 0x00; | ||
| return index; | ||
| } | ||
| var serializeMinMax = function(buffer, key, value, index) { | ||
| // Write the type of either min or max key | ||
| if(value === null) { | ||
| buffer[index++] = BSON.BSON_DATA_NULL; | ||
| } else if(value instanceof MinKey) { | ||
| buffer[index++] = BSON.BSON_DATA_MIN_KEY; | ||
| } else { | ||
| buffer[index++] = BSON.BSON_DATA_MAX_KEY; | ||
| } | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| return index; | ||
| } | ||
| var serializeObjectId = function(buffer, key, value, index) { | ||
| // Write the type | ||
| buffer[index++] = BSON.BSON_DATA_OID; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Write the objectId into the shared buffer | ||
| buffer.write(value.id, index, 'binary') | ||
| // Ajust index | ||
| return index + 12; | ||
| } | ||
| var serializeBuffer = function(buffer, key, value, index) { | ||
| // Write the type | ||
| buffer[index++] = BSON.BSON_DATA_BINARY; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Get size of the buffer (current write point) | ||
| var size = value.length; | ||
| // Write the size of the string to buffer | ||
| buffer[index++] = size & 0xff; | ||
| buffer[index++] = (size >> 8) & 0xff; | ||
| buffer[index++] = (size >> 16) & 0xff; | ||
| buffer[index++] = (size >> 24) & 0xff; | ||
| // Write the default subtype | ||
| buffer[index++] = BSON.BSON_BINARY_SUBTYPE_DEFAULT; | ||
| // Copy the content form the binary field to the buffer | ||
| value.copy(buffer, index, 0, size); | ||
| // Adjust the index | ||
| index = index + size; | ||
| return index; | ||
| } | ||
| var serializeObject = function(buffer, key, value, index, checkKeys, depth, serializeFunctions) { | ||
| // Write the type | ||
| buffer[index++] = Array.isArray(value) ? BSON.BSON_DATA_ARRAY : BSON.BSON_DATA_OBJECT; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| var endIndex = serializeInto(buffer, value, checkKeys, index, depth + 1, serializeFunctions); | ||
| // Write size | ||
| var size = endIndex - index; | ||
| return endIndex; | ||
| } | ||
| var serializeLong = function(buffer, key, value, index) { | ||
| // Write the type | ||
| buffer[index++] = value instanceof Long ? BSON.BSON_DATA_LONG : BSON.BSON_DATA_TIMESTAMP; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Write the date | ||
| var lowBits = value.getLowBits(); | ||
| var highBits = value.getHighBits(); | ||
| // Encode low bits | ||
| buffer[index++] = lowBits & 0xff; | ||
| buffer[index++] = (lowBits >> 8) & 0xff; | ||
| buffer[index++] = (lowBits >> 16) & 0xff; | ||
| buffer[index++] = (lowBits >> 24) & 0xff; | ||
| // Encode high bits | ||
| buffer[index++] = highBits & 0xff; | ||
| buffer[index++] = (highBits >> 8) & 0xff; | ||
| buffer[index++] = (highBits >> 16) & 0xff; | ||
| buffer[index++] = (highBits >> 24) & 0xff; | ||
| return index; | ||
| } | ||
| var serializeDouble = function(buffer, key, value, index) { | ||
| // Encode as double | ||
| buffer[index++] = BSON.BSON_DATA_NUMBER; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Write float | ||
| writeIEEE754(buffer, value, index, 'little', 52, 8); | ||
| // Ajust index | ||
| index = index + 8; | ||
| return index; | ||
| } | ||
| var serializeFunction = function(buffer, key, value, index, checkKeys, depth) { | ||
| buffer[index++] = BSON.BSON_DATA_CODE; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Function string | ||
| var functionString = value.toString(); | ||
| // Function Size | ||
| var size = Buffer.byteLength(functionString) + 1; | ||
| // Write the size of the string to buffer | ||
| buffer[index++] = size & 0xff; | ||
| buffer[index++] = (size >> 8) & 0xff; | ||
| buffer[index++] = (size >> 16) & 0xff; | ||
| buffer[index++] = (size >> 24) & 0xff; | ||
| // Write the string | ||
| buffer.write(functionString, index, 'utf8'); | ||
| // Update index | ||
| index = index + size - 1; | ||
| // Write zero | ||
| buffer[index++] = 0; | ||
| return index; | ||
| } | ||
| var serializeCode = function(buffer, key, value, index, checkKeys, depth, serializeFunctions) { | ||
| if(value.scope != null && Object.keys(value.scope).length > 0) { | ||
| // Write the type | ||
| buffer[index++] = BSON.BSON_DATA_CODE_W_SCOPE; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Starting index | ||
| var startIndex = index; | ||
| // Serialize the function | ||
| // Get the function string | ||
| var functionString = typeof value.code == 'string' ? value.code : value.code.toString(); | ||
| var codeSize = Buffer.byteLength(functionString) + 1; | ||
| // Index adjustment | ||
| index = index + 4; | ||
| // Write the size of the string to buffer | ||
| buffer[index] = codeSize & 0xff; | ||
| buffer[index + 1] = (codeSize >> 8) & 0xff; | ||
| buffer[index + 2] = (codeSize >> 16) & 0xff; | ||
| buffer[index + 3] = (codeSize >> 24) & 0xff; | ||
| // Write string into buffer | ||
| buffer.write(functionString, index + 4, 'utf8'); | ||
| // Write end 0 | ||
| buffer[index + 4 + codeSize - 1] = 0; | ||
| // Write the | ||
| index = index + codeSize + 4; | ||
| // | ||
| // Serialize the scope value | ||
| var endIndex = serializeInto(buffer, value.scope, checkKeys, index, depth + 1, serializeFunctions) | ||
| index = endIndex - 1; | ||
| // Writ the total | ||
| var totalSize = endIndex - startIndex; | ||
| // Write the total size of the object | ||
| buffer[startIndex++] = totalSize & 0xff; | ||
| buffer[startIndex++] = (totalSize >> 8) & 0xff; | ||
| buffer[startIndex++] = (totalSize >> 16) & 0xff; | ||
| buffer[startIndex++] = (totalSize >> 24) & 0xff; | ||
| // Write trailing zero | ||
| buffer[index++] = 0; | ||
| } else { | ||
| buffer[index++] = BSON.BSON_DATA_CODE; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Function string | ||
| var functionString = value.code.toString(); | ||
| // Function Size | ||
| var size = Buffer.byteLength(functionString) + 1; | ||
| // Write the size of the string to buffer | ||
| buffer[index++] = size & 0xff; | ||
| buffer[index++] = (size >> 8) & 0xff; | ||
| buffer[index++] = (size >> 16) & 0xff; | ||
| buffer[index++] = (size >> 24) & 0xff; | ||
| // Write the string | ||
| buffer.write(functionString, index, 'utf8'); | ||
| // Update index | ||
| index = index + size - 1; | ||
| // Write zero | ||
| buffer[index++] = 0; | ||
| } | ||
| return index; | ||
| } | ||
| var serializeBinary = function(buffer, key, value, index) { | ||
| // Write the type | ||
| buffer[index++] = BSON.BSON_DATA_BINARY; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Extract the buffer | ||
| var data = value.value(true); | ||
| // Calculate size | ||
| var size = value.position; | ||
| // Write the size of the string to buffer | ||
| buffer[index++] = size & 0xff; | ||
| buffer[index++] = (size >> 8) & 0xff; | ||
| buffer[index++] = (size >> 16) & 0xff; | ||
| buffer[index++] = (size >> 24) & 0xff; | ||
| // Write the subtype to the buffer | ||
| buffer[index++] = value.sub_type; | ||
| // If we have binary type 2 the 4 first bytes are the size | ||
| if(value.sub_type == Binary.SUBTYPE_BYTE_ARRAY) { | ||
| buffer[index++] = size & 0xff; | ||
| buffer[index++] = (size >> 8) & 0xff; | ||
| buffer[index++] = (size >> 16) & 0xff; | ||
| buffer[index++] = (size >> 24) & 0xff; | ||
| } | ||
| // Write the data to the object | ||
| data.copy(buffer, index, 0, value.position); | ||
| // Adjust the index | ||
| index = index + value.position; | ||
| return index; | ||
| } | ||
| var serializeSymbol = function(buffer, key, value, index) { | ||
| // Write the type | ||
| buffer[index++] = BSON.BSON_DATA_SYMBOL; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Calculate size | ||
| var size = Buffer.byteLength(value.value) + 1; | ||
| // Write the size of the string to buffer | ||
| buffer[index++] = size & 0xff; | ||
| buffer[index++] = (size >> 8) & 0xff; | ||
| buffer[index++] = (size >> 16) & 0xff; | ||
| buffer[index++] = (size >> 24) & 0xff; | ||
| // Write the string | ||
| buffer.write(value.value, index, 'utf8'); | ||
| // Update index | ||
| index = index + size - 1; | ||
| // Write zero | ||
| buffer[index++] = 0x00; | ||
| return index; | ||
| } | ||
| var serializeDBRef = function(buffer, key, value, index, depth, serializeFunctions) { | ||
| // Write the type | ||
| buffer[index++] = BSON.BSON_DATA_OBJECT; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| var startIndex = index; | ||
| var endIndex; | ||
| // Serialize object | ||
| if(null != value.db) { | ||
| endIndex = serializeInto(buffer, { | ||
| '$ref': value.namespace | ||
| , '$id' : value.oid | ||
| , '$db' : value.db | ||
| }, false, index, depth + 1, serializeFunctions); | ||
| } else { | ||
| endIndex = serializeInto(buffer, { | ||
| '$ref': value.namespace | ||
| , '$id' : value.oid | ||
| }, false, index, depth + 1, serializeFunctions); | ||
| } | ||
| // Calculate object size | ||
| var size = endIndex - startIndex; | ||
| // Write the size | ||
| buffer[startIndex++] = size & 0xff; | ||
| buffer[startIndex++] = (size >> 8) & 0xff; | ||
| buffer[startIndex++] = (size >> 16) & 0xff; | ||
| buffer[startIndex++] = (size >> 24) & 0xff; | ||
| // Set index | ||
| return endIndex; | ||
| } | ||
| var serializeInto = function serializeInto(buffer, object, checkKeys, startingIndex, depth, serializeFunctions) { | ||
| startingIndex = startingIndex || 0; | ||
| // Start place to serialize into | ||
| var index = startingIndex + 4; | ||
| var self = this; | ||
| // Special case isArray | ||
| if(Array.isArray(object)) { | ||
| // Get object keys | ||
| for(var i = 0; i < object.length; i++) { | ||
| var key = "" + i; | ||
| var value = object[i]; | ||
| // Is there an override value | ||
| if(value && value.toBSON) { | ||
| if(typeof value.toBSON != 'function') throw new Error("toBSON is not a function"); | ||
| value = value.toBSON(); | ||
| } | ||
| var type = typeof value; | ||
| if(type == 'string') { | ||
| index = serializeString(buffer, key, value, index); | ||
| } else if(type == 'number') { | ||
| index = serializeNumber(buffer, key, value, index); | ||
| } else if(type == 'undefined' || value == null) { | ||
| index = serializeUndefined(buffer, key, value, index); | ||
| } else if(type == 'boolean') { | ||
| index = serializeBoolean(buffer, key, value, index); | ||
| } else if(value instanceof Date || isDate(value)) { | ||
| index = serializeDate(buffer, key, value, index); | ||
| } else if(value instanceof RegExp || Object.prototype.toString.call(value) === '[object RegExp]') { | ||
| index = serializeRegExp(buffer, key, value, index); | ||
| } else if(value['_bsontype'] == 'MinKey' || value['_bsontype'] == 'MaxKey') { | ||
| index = serializeMinMax(buffer, key, value, index); | ||
| } else if(value['_bsontype'] == 'ObjectID') { | ||
| index = serializeObjectId(buffer, key, value, index); | ||
| } else if(Buffer.isBuffer(value)) { | ||
| index = serializeBuffer(buffer, key, value, index); | ||
| } else if(type == 'object' && value['_bsontype'] == null) { | ||
| index = serializeObject(buffer, key, value, index, checkKeys, depth); | ||
| } else if(value['_bsontype'] == 'Long' || value['_bsontype'] == 'Timestamp') { | ||
| index = serializeLong(buffer, key, value, index); | ||
| } else if(value['_bsontype'] == 'Double') { | ||
| index = serializeDouble(buffer, key, value, index); | ||
| } else if(typeof value == 'function' && serializeFunctions) { | ||
| index = serializeFunction(buffer, key, value, index, checkKeys, depth); | ||
| } else if(value['_bsontype'] == 'Code') { | ||
| index = serializeCode(buffer, key, value, index, checkKeys, depth); | ||
| } else if(value['_bsontype'] == 'Binary') { | ||
| index = serializeBinary(buffer, key, value, index); | ||
| } else if(value['_bsontype'] == 'Symbol') { | ||
| index = serializeSymbol(buffer, key, value, index); | ||
| } else if(value['_bsontype'] == 'DBRef') { | ||
| index = serializeDBRef(buffer, key, value, index, depth); | ||
| } | ||
| } | ||
| } else { | ||
| // Did we provide a custom serialization method | ||
| if(object.toBSON) { | ||
| if(typeof object.toBSON != 'function') throw new Error("toBSON is not a function"); | ||
| object = object.toBSON(); | ||
| if(object != null && typeof object != 'object') throw new Error("toBSON function did not return an object"); | ||
| } | ||
| // Iterate over all the keys | ||
| for(var key in object) { | ||
| var value = object[key]; | ||
| // Is there an override value | ||
| if(value && value.toBSON) { | ||
| if(typeof value.toBSON != 'function') throw new Error("toBSON is not a function"); | ||
| value = value.toBSON(); | ||
| } | ||
| // Check the type of the value | ||
| var type = typeof value; | ||
| // Check the key and throw error if it's illegal | ||
| if(key != '$db' && key != '$ref' && key != '$id') { | ||
| checkKey(key, !checkKeys); | ||
| } | ||
| if(type == 'string') { | ||
| index = serializeString(buffer, key, value, index); | ||
| } else if(type == 'number') { | ||
| index = serializeNumber(buffer, key, value, index); | ||
| } else if(type == 'undefined' || value == null) { | ||
| index = serializeUndefined(buffer, key, value, index); | ||
| } else if(type == 'boolean') { | ||
| index = serializeBoolean(buffer, key, value, index); | ||
| } else if(value instanceof Date || isDate(value)) { | ||
| index = serializeDate(buffer, key, value, index); | ||
| } else if(value instanceof RegExp || Object.prototype.toString.call(value) === '[object RegExp]') { | ||
| index = serializeRegExp(buffer, key, value, index); | ||
| } else if(value['_bsontype'] == 'MinKey' || value['_bsontype'] == 'MaxKey') { | ||
| index = serializeMinMax(buffer, key, value, index); | ||
| } else if(value['_bsontype'] == 'ObjectID') { | ||
| index = serializeObjectId(buffer, key, value, index); | ||
| } else if(Buffer.isBuffer(value)) { | ||
| index = serializeBuffer(buffer, key, value, index); | ||
| } else if(type == 'object' && value['_bsontype'] == null) { | ||
| index = serializeObject(buffer, key, value, index, checkKeys, depth); | ||
| } else if(value['_bsontype'] == 'Long' || value['_bsontype'] == 'Timestamp') { | ||
| index = serializeLong(buffer, key, value, index); | ||
| } else if(value['_bsontype'] == 'Double') { | ||
| index = serializeDouble(buffer, key, value, index); | ||
| } else if(value['_bsontype'] == 'Code') { | ||
| index = serializeCode(buffer, key, value, index, checkKeys, depth); | ||
| } else if(typeof value == 'function' && serializeFunctions) { | ||
| index = serializeFunction(buffer, key, value, index, checkKeys, depth); | ||
| } else if(value['_bsontype'] == 'Binary') { | ||
| index = serializeBinary(buffer, key, value, index); | ||
| } else if(value['_bsontype'] == 'Symbol') { | ||
| index = serializeSymbol(buffer, key, value, index); | ||
| } else if(value['_bsontype'] == 'DBRef') { | ||
| index = serializeDBRef(buffer, key, value, index, depth); | ||
| } | ||
| } | ||
| } | ||
| // Final padding byte for object | ||
| buffer[index++] = 0x00; | ||
| // Final size | ||
| var size = index - startingIndex; | ||
| // Write the size of the object | ||
| buffer[startingIndex++] = size & 0xff; | ||
| buffer[startingIndex++] = (size >> 8) & 0xff; | ||
| buffer[startingIndex++] = (size >> 16) & 0xff; | ||
| buffer[startingIndex++] = (size >> 24) & 0xff; | ||
| return index; | ||
| } | ||
| 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. | ||
| module.exports = serializeInto; |
+6
-1
@@ -0,1 +1,6 @@ | ||
| 0.4.0 2015-06-16 | ||
| ---------------- | ||
| - New JS serializer serializing into a single buffer then copying out the new buffer. Performance is similar to current C++ parser. | ||
| - Removed bson-ext extension dependency for now. | ||
| 0.3.2 2015-03-27 | ||
@@ -42,2 +47,2 @@ ---------------- | ||
| ---------------- | ||
| - Added precompiled c++ native extensions for win32 ia32 and x64 | ||
| - Added precompiled c++ native extensions for win32 ia32 and x64 |
+34
-34
@@ -5,3 +5,3 @@ /** | ||
| */ | ||
| if(typeof window === 'undefined') { | ||
| if(typeof window === 'undefined') { | ||
| var Buffer = require('buffer').Buffer; // TODO just use global Buffer | ||
@@ -12,3 +12,3 @@ } | ||
| * A class representation of the BSON Binary type. | ||
| * | ||
| * | ||
| * Sub types | ||
@@ -29,3 +29,3 @@ * - **BSON.BSON_BINARY_SUBTYPE_DEFAULT**, default BSON type. | ||
| if(!(this instanceof Binary)) return new Binary(buffer, subType); | ||
| this._bsontype = 'Binary'; | ||
@@ -36,3 +36,3 @@ | ||
| this.position = 0; | ||
| } else { | ||
| } else { | ||
| this.sub_type = subType == null ? BSON_BINARY_SUBTYPE_DEFAULT : subType; | ||
@@ -54,3 +54,3 @@ this.position = 0; | ||
| } else { | ||
| this.buffer = buffer; | ||
| this.buffer = buffer; | ||
| } | ||
@@ -60,3 +60,3 @@ this.position = buffer.length; | ||
| if(typeof Buffer != 'undefined') { | ||
| this.buffer = new Buffer(Binary.BUFFER_SIZE); | ||
| this.buffer = new Buffer(Binary.BUFFER_SIZE); | ||
| } else if(typeof Uint8Array != 'undefined'){ | ||
@@ -82,7 +82,7 @@ this.buffer = new Uint8Array(new ArrayBuffer(Binary.BUFFER_SIZE)); | ||
| if(typeof byte_value != 'number' && byte_value < 0 || byte_value > 255) throw new Error("only accepts number in a valid unsigned byte range 0-255"); | ||
| // Decode the byte value once | ||
| var decoded_byte = null; | ||
| if(typeof byte_value == 'string') { | ||
| decoded_byte = byte_value.charCodeAt(0); | ||
| decoded_byte = byte_value.charCodeAt(0); | ||
| } else if(byte_value['length'] != null) { | ||
@@ -93,7 +93,7 @@ decoded_byte = byte_value[0]; | ||
| } | ||
| if(this.buffer.length > this.position) { | ||
| this.buffer[this.position++] = decoded_byte; | ||
| } else { | ||
| if(typeof Buffer != 'undefined' && Buffer.isBuffer(this.buffer)) { | ||
| if(typeof Buffer != 'undefined' && Buffer.isBuffer(this.buffer)) { | ||
| // Create additional overflow buffer | ||
@@ -112,4 +112,4 @@ var buffer = new Buffer(Binary.BUFFER_SIZE + this.buffer.length); | ||
| buffer = new Array(Binary.BUFFER_SIZE + this.buffer.length); | ||
| } | ||
| } | ||
| // We need to copy all the content to the new array | ||
@@ -119,3 +119,3 @@ for(var i = 0; i < this.buffer.length; i++) { | ||
| } | ||
| // Reassign the buffer | ||
@@ -144,5 +144,5 @@ this.buffer = buffer; | ||
| // If we are in node.js | ||
| if(typeof Buffer != 'undefined' && Buffer.isBuffer(this.buffer)) { | ||
| if(typeof Buffer != 'undefined' && Buffer.isBuffer(this.buffer)) { | ||
| buffer = new Buffer(this.buffer.length + string.length); | ||
| this.buffer.copy(buffer, 0, 0, this.buffer.length); | ||
| this.buffer.copy(buffer, 0, 0, this.buffer.length); | ||
| } else if(Object.prototype.toString.call(this.buffer) == '[object Uint8Array]') { | ||
@@ -156,3 +156,3 @@ // Create a new buffer | ||
| } | ||
| // Assign the new buffer | ||
@@ -167,10 +167,10 @@ this.buffer = buffer; | ||
| } else if(typeof Buffer != 'undefined' && typeof string == 'string' && Buffer.isBuffer(this.buffer)) { | ||
| this.buffer.write(string, 'binary', offset); | ||
| this.buffer.write(string, offset, 'binary'); | ||
| this.position = (offset + string.length) > this.position ? (offset + string.length) : this.position; | ||
| // offset = string.length; | ||
| } else if(Object.prototype.toString.call(string) == '[object Uint8Array]' | ||
| || Object.prototype.toString.call(string) == '[object Array]' && typeof string != 'string') { | ||
| } else if(Object.prototype.toString.call(string) == '[object Uint8Array]' | ||
| || Object.prototype.toString.call(string) == '[object Array]' && typeof string != 'string') { | ||
| for(var i = 0; i < string.length; i++) { | ||
| this.buffer[offset++] = string[i]; | ||
| } | ||
| } | ||
@@ -199,3 +199,3 @@ this.position = offset > this.position ? offset : this.position; | ||
| : this.position; | ||
| // Let's return the data based on the type we have | ||
@@ -222,3 +222,3 @@ if(this.buffer['slice']) { | ||
| Binary.prototype.value = function value(asRaw) { | ||
| asRaw = asRaw == null ? false : asRaw; | ||
| asRaw = asRaw == null ? false : asRaw; | ||
@@ -228,3 +228,3 @@ // Optimize to serialize for the situation where the data == size of buffer | ||
| return this.buffer; | ||
| // If it's a node.js buffer object | ||
@@ -280,3 +280,3 @@ if(typeof Buffer != 'undefined' && Buffer.isBuffer(this.buffer)) { | ||
| * Binary default subtype | ||
| * @ignore | ||
| * @ignore | ||
| */ | ||
@@ -294,3 +294,3 @@ var BSON_BINARY_SUBTYPE_DEFAULT = 0; | ||
| buffer[i] = data.charCodeAt(i); | ||
| } | ||
| } | ||
| // Write the string to the buffer | ||
@@ -310,3 +310,3 @@ return buffer; | ||
| } | ||
| return result; | ||
| return result; | ||
| }; | ||
@@ -318,3 +318,3 @@ | ||
| * Default BSON type | ||
| * | ||
| * | ||
| * @classconstant SUBTYPE_DEFAULT | ||
@@ -325,3 +325,3 @@ **/ | ||
| * Function BSON type | ||
| * | ||
| * | ||
| * @classconstant SUBTYPE_DEFAULT | ||
@@ -332,3 +332,3 @@ **/ | ||
| * Byte Array BSON type | ||
| * | ||
| * | ||
| * @classconstant SUBTYPE_DEFAULT | ||
@@ -339,3 +339,3 @@ **/ | ||
| * OLD UUID BSON type | ||
| * | ||
| * | ||
| * @classconstant SUBTYPE_DEFAULT | ||
@@ -346,3 +346,3 @@ **/ | ||
| * UUID BSON type | ||
| * | ||
| * | ||
| * @classconstant SUBTYPE_DEFAULT | ||
@@ -353,3 +353,3 @@ **/ | ||
| * MD5 BSON type | ||
| * | ||
| * | ||
| * @classconstant SUBTYPE_DEFAULT | ||
@@ -360,3 +360,3 @@ **/ | ||
| * User BSON type | ||
| * | ||
| * | ||
| * @classconstant SUBTYPE_DEFAULT | ||
@@ -370,2 +370,2 @@ **/ | ||
| module.exports = Binary; | ||
| module.exports.Binary = Binary; | ||
| module.exports.Binary = Binary; |
@@ -5,2 +5,3 @@ try { | ||
| } catch(err) { | ||
| console.dir(err) | ||
| // do nothing | ||
@@ -72,3 +73,3 @@ } | ||
| }); | ||
| // Catch error and return no classes found | ||
@@ -75,0 +76,0 @@ try { |
@@ -113,3 +113,3 @@ /** | ||
| } | ||
| var time4Bytes = BinaryParser.encodeInt(time, 32, true, true); | ||
@@ -275,2 +275,2 @@ /* for time-based ObjectID the bytes following the time will be zeroed */ | ||
| module.exports.ObjectID = ObjectID; | ||
| module.exports.ObjectId = ObjectID; | ||
| module.exports.ObjectId = ObjectID; |
+4
-5
| { "name" : "bson" | ||
| , "description" : "A bson parser for node.js and the browser" | ||
| , "keywords" : ["mongodb", "bson", "parser"] | ||
| , "version" : "0.3.2" | ||
| , "version" : "0.4.0" | ||
| , "author" : "Christian Amor Kvalheim <christkv@gmail.com>" | ||
@@ -16,6 +16,5 @@ , "contributors" : [] | ||
| , "one": "2.X.X" | ||
| , "benchmark": "1.0.0" | ||
| , "colors": "1.1.0" | ||
| } | ||
| , "optionalDependencies": { | ||
| "bson-ext": "~0.1" | ||
| } | ||
| , "config": { "native" : false } | ||
@@ -26,3 +25,3 @@ , "main": "./lib/bson/index" | ||
| , "scripts": { | ||
| "test" : "nodeunit ./test/node && TEST_NATIVE=TRUE nodeunit ./test/node" | ||
| "test" : "nodeunit ./test/node" | ||
| } | ||
@@ -29,0 +28,0 @@ , "browser": "lib/bson/bson.js" |
| var writeIEEE754 = require('./float_parser').writeIEEE754 | ||
| , 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 | ||
| , Binary = require('./binary').Binary | ||
| , BinaryParser = require('./binary_parser').BinaryParser; | ||
| // Max Document Buffer size | ||
| var buffer = new Buffer(1024 * 1024 * 16); | ||
| var checkKey = function checkKey (key, dollarsAndDotsOk) { | ||
| if (!key.length) return; | ||
| // Check if we have a legal key for the object | ||
| if (!!~key.indexOf("\x00")) { | ||
| // The BSON spec doesn't allow keys with null bytes because keys are | ||
| // null-terminated. | ||
| throw Error("key " + key + " must not contain null bytes"); | ||
| } | ||
| if (!dollarsAndDotsOk) { | ||
| if('$' == key[0]) { | ||
| throw Error("key " + key + " must not start with '$'"); | ||
| } else if (!!~key.indexOf('.')) { | ||
| throw Error("key " + key + " must not contain '.'"); | ||
| } | ||
| } | ||
| }; | ||
| var serializeString = function(key, value, index) { | ||
| // Encode String type | ||
| buffer[index++] = BSON.BSON_DATA_STRING; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes + 1; | ||
| buffer[index - 1] = 0; | ||
| // Calculate size | ||
| var size = Buffer.byteLength(value) + 1; | ||
| // Write the size of the string to buffer | ||
| buffer[index + 3] = (size >> 24) & 0xff; | ||
| buffer[index + 2] = (size >> 16) & 0xff; | ||
| buffer[index + 1] = (size >> 8) & 0xff; | ||
| buffer[index] = size & 0xff; | ||
| // Ajust the index | ||
| index = index + 4; | ||
| // Write the string | ||
| buffer.write(value, index, 'utf8'); | ||
| // Update index | ||
| index = index + size - 1; | ||
| // Write zero | ||
| buffer[index++] = 0; | ||
| return index; | ||
| } | ||
| var serializeNumber = function(key, value, index) { | ||
| // We have an integer value | ||
| if(Math.floor(value) === value && value >= BSON.JS_INT_MIN && value <= BSON.JS_INT_MAX) { | ||
| // If the value fits in 32 bits encode as int, if it fits in a double | ||
| // encode it as a double, otherwise long | ||
| if(value >= BSON.BSON_INT32_MIN && value <= BSON.BSON_INT32_MAX) { | ||
| // Set int type 32 bits or less | ||
| buffer[index++] = BSON.BSON_DATA_INT; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Write the int value | ||
| buffer[index++] = value & 0xff; | ||
| buffer[index++] = (value >> 8) & 0xff; | ||
| buffer[index++] = (value >> 16) & 0xff; | ||
| buffer[index++] = (value >> 24) & 0xff; | ||
| } else if(value >= BSON.JS_INT_MIN && value <= BSON.JS_INT_MAX) { | ||
| // Encode as double | ||
| buffer[index++] = BSON.BSON_DATA_NUMBER; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Write float | ||
| writeIEEE754(buffer, value, index, 'little', 52, 8); | ||
| // Ajust index | ||
| index = index + 8; | ||
| } else { | ||
| // Set long type | ||
| buffer[index++] = BSON.BSON_DATA_LONG; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| var longVal = Long.fromNumber(value); | ||
| var lowBits = longVal.getLowBits(); | ||
| var highBits = longVal.getHighBits(); | ||
| // Encode low bits | ||
| buffer[index++] = lowBits & 0xff; | ||
| buffer[index++] = (lowBits >> 8) & 0xff; | ||
| buffer[index++] = (lowBits >> 16) & 0xff; | ||
| buffer[index++] = (lowBits >> 24) & 0xff; | ||
| // Encode high bits | ||
| buffer[index++] = highBits & 0xff; | ||
| buffer[index++] = (highBits >> 8) & 0xff; | ||
| buffer[index++] = (highBits >> 16) & 0xff; | ||
| buffer[index++] = (highBits >> 24) & 0xff; | ||
| } | ||
| } else { | ||
| // Encode as double | ||
| buffer[index++] = BSON.BSON_DATA_NUMBER; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Write float | ||
| writeIEEE754(buffer, value, index, 'little', 52, 8); | ||
| // Ajust index | ||
| index = index + 8; | ||
| } | ||
| return index; | ||
| } | ||
| var serializeUndefined = function(key, value, index) { | ||
| // Set long type | ||
| buffer[index++] = BSON.BSON_DATA_NULL; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| return index; | ||
| } | ||
| var serializeBoolean = function(key, value, index) { | ||
| // Write the type | ||
| buffer[index++] = BSON.BSON_DATA_BOOLEAN; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Encode the boolean value | ||
| buffer[index++] = value ? 1 : 0; | ||
| return index; | ||
| } | ||
| var serializeDate = function(key, value, index) { | ||
| // Write the type | ||
| buffer[index++] = BSON.BSON_DATA_DATE; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Write the date | ||
| var dateInMilis = Long.fromNumber(value.getTime()); | ||
| var lowBits = dateInMilis.getLowBits(); | ||
| var highBits = dateInMilis.getHighBits(); | ||
| // Encode low bits | ||
| buffer[index++] = lowBits & 0xff; | ||
| buffer[index++] = (lowBits >> 8) & 0xff; | ||
| buffer[index++] = (lowBits >> 16) & 0xff; | ||
| buffer[index++] = (lowBits >> 24) & 0xff; | ||
| // Encode high bits | ||
| buffer[index++] = highBits & 0xff; | ||
| buffer[index++] = (highBits >> 8) & 0xff; | ||
| buffer[index++] = (highBits >> 16) & 0xff; | ||
| buffer[index++] = (highBits >> 24) & 0xff; | ||
| return index; | ||
| } | ||
| var serializeRegExp = function(key, value, index) { | ||
| // Write the type | ||
| buffer[index++] = BSON.BSON_DATA_REGEXP; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Write the regular expression string | ||
| buffer.write(value.source, index, 'utf8'); | ||
| // Adjust the index | ||
| index = index + Buffer.byteLength(value.source); | ||
| // Write zero | ||
| buffer[index++] = 0x00; | ||
| // Write the parameters | ||
| if(value.global) buffer[index++] = 0x73; // s | ||
| if(value.ignoreCase) buffer[index++] = 0x69; // i | ||
| if(value.multiline) buffer[index++] = 0x6d; // m | ||
| // Add ending zero | ||
| buffer[index++] = 0x00; | ||
| return index; | ||
| } | ||
| var serializeMinMax = function(key, value, index) { | ||
| // Write the type of either min or max key | ||
| if(value === null) { | ||
| buffer[index++] = BSON.BSON_DATA_NULL; | ||
| } else if(value instanceof MinKey) { | ||
| buffer[index++] = BSON.BSON_DATA_MIN_KEY; | ||
| } else { | ||
| buffer[index++] = BSON.BSON_DATA_MAX_KEY; | ||
| } | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| return index; | ||
| } | ||
| var serializeObjectId = function(key, value, index) { | ||
| // Write the type | ||
| buffer[index++] = BSON.BSON_DATA_OID; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| for(var j = 0; j < 12; j++) { | ||
| buffer[index + j] = value.binId[j]; | ||
| } | ||
| // Ajust index | ||
| index = index + 12; | ||
| return index; | ||
| } | ||
| var serializeBuffer = function(key, value, index) { | ||
| // Write the type | ||
| buffer[index++] = BSON.BSON_DATA_BINARY; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Get size of the buffer (current write point) | ||
| var size = value.length; | ||
| // Write the size of the string to buffer | ||
| buffer[index++] = size & 0xff; | ||
| buffer[index++] = (size >> 8) & 0xff; | ||
| buffer[index++] = (size >> 16) & 0xff; | ||
| buffer[index++] = (size >> 24) & 0xff; | ||
| // Write the default subtype | ||
| buffer[index++] = BSON.BSON_BINARY_SUBTYPE_DEFAULT; | ||
| // Copy the content form the binary field to the buffer | ||
| value.copy(buffer, index, 0, size); | ||
| // Adjust the index | ||
| index = index + size; | ||
| return index; | ||
| } | ||
| var serializeObject = function(key, value, index, checkKeys, depth) { | ||
| // Write the type | ||
| buffer[index++] = Array.isArray(value) ? BSON.BSON_DATA_ARRAY : BSON.BSON_DATA_OBJECT; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| var endIndex = serializeInto(value, checkKeys, index, depth + 1); | ||
| // Write size | ||
| var size = endIndex - index; | ||
| return endIndex; | ||
| } | ||
| var serializeLong = function(key, value, index) { | ||
| // Write the type | ||
| buffer[index++] = value instanceof Long ? BSON.BSON_DATA_LONG : BSON.BSON_DATA_TIMESTAMP; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Write the date | ||
| var lowBits = value.getLowBits(); | ||
| var highBits = value.getHighBits(); | ||
| // Encode low bits | ||
| buffer[index++] = lowBits & 0xff; | ||
| buffer[index++] = (lowBits >> 8) & 0xff; | ||
| buffer[index++] = (lowBits >> 16) & 0xff; | ||
| buffer[index++] = (lowBits >> 24) & 0xff; | ||
| // Encode high bits | ||
| buffer[index++] = highBits & 0xff; | ||
| buffer[index++] = (highBits >> 8) & 0xff; | ||
| buffer[index++] = (highBits >> 16) & 0xff; | ||
| buffer[index++] = (highBits >> 24) & 0xff; | ||
| return index; | ||
| } | ||
| var serializeDouble = function(key, value, index) { | ||
| // Encode as double | ||
| buffer[index++] = BSON.BSON_DATA_NUMBER; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Write float | ||
| writeIEEE754(buffer, value, index, 'little', 52, 8); | ||
| // Ajust index | ||
| index = index + 8; | ||
| return index; | ||
| } | ||
| var serializeCode = function(key, value, index, checkKeys, depth) { | ||
| if(value.scope != null && Object.keys(value.scope).length > 0) { | ||
| // Write the type | ||
| buffer[index++] = BSON.BSON_DATA_CODE_W_SCOPE; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Starting index | ||
| var startIndex = index; | ||
| // Serialize the function | ||
| // Get the function string | ||
| var functionString = typeof value.code == 'string' ? value.code : value.code.toString(); | ||
| var codeSize = Buffer.byteLength(functionString) + 1; | ||
| // Index adjustment | ||
| index = index + 4; | ||
| // Write the size of the string to buffer | ||
| buffer[index] = codeSize & 0xff; | ||
| buffer[index + 1] = (codeSize >> 8) & 0xff; | ||
| buffer[index + 2] = (codeSize >> 16) & 0xff; | ||
| buffer[index + 3] = (codeSize >> 24) & 0xff; | ||
| // Write string into buffer | ||
| buffer.write(functionString, index + 4, 'utf8'); | ||
| // Write end 0 | ||
| buffer[index + 4 + codeSize - 1] = 0; | ||
| // Write the | ||
| index = index + codeSize + 4; | ||
| // | ||
| // Serialize the scope value | ||
| var endIndex = serializeInto(value.scope, checkKeys, index, depth + 1) | ||
| index = endIndex - 1; | ||
| // Writ the total | ||
| var totalSize = endIndex - startIndex; | ||
| // Write the total size of the object | ||
| buffer[startIndex++] = totalSize & 0xff; | ||
| buffer[startIndex++] = (totalSize >> 8) & 0xff; | ||
| buffer[startIndex++] = (totalSize >> 16) & 0xff; | ||
| buffer[startIndex++] = (totalSize >> 24) & 0xff; | ||
| // Write trailing zero | ||
| buffer[index++] = 0; | ||
| } else { | ||
| buffer[index++] = BSON.BSON_DATA_CODE; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Function string | ||
| var functionString = value.code.toString(); | ||
| // Function Size | ||
| var size = Buffer.byteLength(functionString) + 1; | ||
| // Write the size of the string to buffer | ||
| buffer[index++] = size & 0xff; | ||
| buffer[index++] = (size >> 8) & 0xff; | ||
| buffer[index++] = (size >> 16) & 0xff; | ||
| buffer[index++] = (size >> 24) & 0xff; | ||
| // Write the string | ||
| buffer.write(functionString, index, 'utf8'); | ||
| // Update index | ||
| index = index + size - 1; | ||
| // Write zero | ||
| buffer[index++] = 0; | ||
| } | ||
| return index; | ||
| } | ||
| var serializeBinary = function(key, value, index) { | ||
| // Write the type | ||
| buffer[index++] = BSON.BSON_DATA_BINARY; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Extract the buffer | ||
| var data = value.value(true); | ||
| // Calculate size | ||
| var size = value.position; | ||
| // Write the size of the string to buffer | ||
| buffer[index++] = size & 0xff; | ||
| buffer[index++] = (size >> 8) & 0xff; | ||
| buffer[index++] = (size >> 16) & 0xff; | ||
| buffer[index++] = (size >> 24) & 0xff; | ||
| // Write the subtype to the buffer | ||
| buffer[index++] = value.sub_type; | ||
| // If we have binary type 2 the 4 first bytes are the size | ||
| if(value.sub_type == Binary.SUBTYPE_BYTE_ARRAY) { | ||
| buffer[index++] = size & 0xff; | ||
| buffer[index++] = (size >> 8) & 0xff; | ||
| buffer[index++] = (size >> 16) & 0xff; | ||
| buffer[index++] = (size >> 24) & 0xff; | ||
| } | ||
| // Write the data to the object | ||
| data.copy(buffer, index, 0, value.position); | ||
| // Adjust the index | ||
| index = index + value.position; | ||
| return index; | ||
| } | ||
| var serializeSymbol = function(key, value, index) { | ||
| // Write the type | ||
| buffer[index++] = BSON.BSON_DATA_SYMBOL; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| // Calculate size | ||
| var size = Buffer.byteLength(value.value) + 1; | ||
| // Write the size of the string to buffer | ||
| buffer[index++] = size & 0xff; | ||
| buffer[index++] = (size >> 8) & 0xff; | ||
| buffer[index++] = (size >> 16) & 0xff; | ||
| buffer[index++] = (size >> 24) & 0xff; | ||
| // Write the string | ||
| buffer.write(value.value, index, 'utf8'); | ||
| // Update index | ||
| index = index + size - 1; | ||
| // Write zero | ||
| buffer[index++] = 0x00; | ||
| return index; | ||
| } | ||
| var serializeDBRef = function(key, value, index, depth) { | ||
| // Write the type | ||
| buffer[index++] = BSON.BSON_DATA_OBJECT; | ||
| // Number of written bytes | ||
| var numberOfWrittenBytes = buffer.write(key, index, 'utf8'); | ||
| // Encode the name | ||
| index = index + numberOfWrittenBytes; | ||
| buffer[index++] = 0; | ||
| var startIndex = index; | ||
| var endIndex; | ||
| // Serialize object | ||
| if(null != value.db) { | ||
| endIndex = serializeInto({ | ||
| '$ref': value.namespace | ||
| , '$id' : value.oid | ||
| , '$db' : value.db | ||
| }, false, index, depth + 1); | ||
| } else { | ||
| endIndex = serializeInto({ | ||
| '$ref': value.namespace | ||
| , '$id' : value.oid | ||
| }, false, index, depth + 1); | ||
| } | ||
| // Calculate object size | ||
| var size = endIndex - startIndex; | ||
| // Write the size | ||
| buffer[startIndex++] = size & 0xff; | ||
| buffer[startIndex++] = (size >> 8) & 0xff; | ||
| buffer[startIndex++] = (size >> 16) & 0xff; | ||
| buffer[startIndex++] = (size >> 24) & 0xff; | ||
| // Set index | ||
| return endIndex; | ||
| } | ||
| var BSON = function() { | ||
| this.buffer = buffer; | ||
| } | ||
| BSON.prototype.serialize = function serialize(object, checkKeys, index) { | ||
| var finishedBuffer = new Buffer(serializeInto(object, checkKeys, index || 0, 0)); | ||
| this.buffer.copy(finishedBuffer, 0, 0, finishedBuffer.length); | ||
| return finishedBuffer; | ||
| } | ||
| var serializeInto = function serializeInto(object, checkKeys, startingIndex, depth) { | ||
| startingIndex = startingIndex || 0; | ||
| // Start place to serialize into | ||
| var index = startingIndex + 4; | ||
| var self = this; | ||
| // Special case isArray | ||
| if(Array.isArray(object)) { | ||
| // Get object keys | ||
| for(var i = 0; i < object.length; i++) { | ||
| var key = "" + i; | ||
| var type = typeof object[i]; | ||
| // Check the key and throw error if it's illegal | ||
| if(key != '$db' && key != '$ref' && key != '$id') { | ||
| checkKey(key, !checkKeys); | ||
| } | ||
| if(type == 'string') { | ||
| index = serializeString(key, object[i], index); | ||
| } else if(type == 'number') { | ||
| index = serializeNumber(key, object[i], index); | ||
| } else if(type == 'undefined') { | ||
| index = serializeUndefined(key, object[i], index); | ||
| } else if(type == 'boolean') { | ||
| index = serializeBoolean(key, object[i], index); | ||
| } else if(object[i] instanceof Date) { | ||
| index = serializeDate(key, object[i], index); | ||
| } else if(object[i] instanceof RegExp || Object.prototype.toString.call(object[i]) === '[object RegExp]') { | ||
| index = serializeRegExp(key, object[i], index); | ||
| } else if(object[i]['_bsontype'] == 'MinKey' || object[i]['_bsontype'] == 'MaxKey') { | ||
| index = serializeMinMax(key, object[i], index); | ||
| } else if(object[i]['_bsontype'] == 'ObjectID') { | ||
| index = serializeObjectId(key, object[i], index); | ||
| } else if(Buffer.isBuffer(object[i])) { | ||
| index = serializeBuffer(key, object[i], index); | ||
| } else if(type == 'object' && object[i]['_bsontype'] == null) { | ||
| index = serializeObject(key, object[i], index, checkKeys, depth); | ||
| } else if(object[i]['_bsontype'] == 'Long' || object[i]['_bsontype'] == 'Timestamp') { | ||
| index = serializeLong(key, object[i], index); | ||
| } else if(object[i]['_bsontype'] == 'Double') { | ||
| index = serializeDouble(key, object[i], index); | ||
| } else if(object[i]['_bsontype'] == 'Code') { | ||
| index = serializeCode(key, object[i], index, checkKeys, depth); | ||
| } else if(object[i]['_bsontype'] == 'Binary') { | ||
| index = serializeBinary(key, object[i], index); | ||
| } else if(object[i]['_bsontype'] == 'Symbol') { | ||
| index = serializeSymbol(key, object[i], index); | ||
| } else if(object[i]['_bsontype'] == 'DBRef') { | ||
| index = serializeDBRef(key, object[i], index, depth); | ||
| } | ||
| } | ||
| } else { | ||
| var keys = Object.keys(object); | ||
| for(var i = 0; i < keys.length; i++) { | ||
| var key = keys[i]; | ||
| var type = typeof object[key]; | ||
| // Check the key and throw error if it's illegal | ||
| if(key != '$db' && key != '$ref' && key != '$id') { | ||
| checkKey(key, !checkKeys); | ||
| } | ||
| if(type == 'string') { | ||
| index = serializeString(key, object[key], index); | ||
| } else if(type == 'number') { | ||
| index = serializeNumber(key, object[key], index); | ||
| } else if(type == 'undefined') { | ||
| index = serializeUndefined(key, object[key], index); | ||
| } else if(type == 'boolean') { | ||
| index = serializeBoolean(key, object[key], index); | ||
| } else if(object[key] instanceof Date) { | ||
| index = serializeDate(key, object[key], index); | ||
| } else if(object[key] instanceof RegExp || Object.prototype.toString.call(object[key]) === '[object RegExp]') { | ||
| index = serializeRegExp(key, object[key], index); | ||
| } else if(object[key]['_bsontype'] == 'MinKey' || object[key]['_bsontype'] == 'MaxKey') { | ||
| index = serializeMinMax(key, object[key], index); | ||
| } else if(object[key]['_bsontype'] == 'ObjectID') { | ||
| index = serializeObjectId(key, object[key], index); | ||
| } else if(Buffer.isBuffer(object[key])) { | ||
| index = serializeBuffer(key, object[key], index); | ||
| } else if(type == 'object' && object[key]['_bsontype'] == null) { | ||
| index = serializeObject(key, object[key], index, checkKeys, depth); | ||
| } else if(object[key]['_bsontype'] == 'Long' || object[key]['_bsontype'] == 'Timestamp') { | ||
| index = serializeLong(key, object[key], index); | ||
| } else if(object[key]['_bsontype'] == 'Double') { | ||
| index = serializeDouble(key, object[key], index); | ||
| } else if(object[key]['_bsontype'] == 'Code') { | ||
| index = serializeCode(key, object[key], index, checkKeys, depth); | ||
| } else if(object[key]['_bsontype'] == 'Binary') { | ||
| index = serializeBinary(key, object[key], index); | ||
| } else if(object[key]['_bsontype'] == 'Symbol') { | ||
| index = serializeSymbol(key, object[key], index); | ||
| } else if(object[key]['_bsontype'] == 'DBRef') { | ||
| index = serializeDBRef(key, object[key], index, depth); | ||
| } | ||
| } | ||
| } | ||
| // Final padding byte for object | ||
| buffer[index++] = 0x00; | ||
| // Final size | ||
| var size = index - startingIndex; | ||
| // Write the size of the object | ||
| buffer[startingIndex++] = size & 0xff; | ||
| buffer[startingIndex++] = (size >> 8) & 0xff; | ||
| buffer[startingIndex++] = (size >> 16) & 0xff; | ||
| buffer[startingIndex++] = (size >> 24) & 0xff; | ||
| return index; | ||
| } | ||
| /** | ||
| * @ignore | ||
| * @api private | ||
| */ | ||
| // 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. | ||
| /** | ||
| * 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; | ||
| // Return BSON | ||
| exports.BSON = BSON; |
Sorry, the diff of this file is too big to display
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
411121
17.25%0
-100%27
17.39%10727
15.48%5
66.67%42
10.53%6
50%