From 686106d544ecc3b6ffd4db2b665d3bc879a58d8c Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Mon, 24 Sep 2012 16:22:07 -0400 Subject: ok --- node_modules/mongodb/external-libs/bson/Makefile | 45 + node_modules/mongodb/external-libs/bson/bson.cc | 2165 ++++++++++++++++++++ node_modules/mongodb/external-libs/bson/bson.h | 105 + node_modules/mongodb/external-libs/bson/index.js | 20 + .../mongodb/external-libs/bson/test/test_bson.js | 349 ++++ .../external-libs/bson/test/test_full_bson.js | 218 ++ .../external-libs/bson/test/test_stackless_bson.js | 132 ++ node_modules/mongodb/external-libs/bson/wscript | 39 + 8 files changed, 3073 insertions(+) create mode 100644 node_modules/mongodb/external-libs/bson/Makefile create mode 100644 node_modules/mongodb/external-libs/bson/bson.cc create mode 100644 node_modules/mongodb/external-libs/bson/bson.h create mode 100644 node_modules/mongodb/external-libs/bson/index.js create mode 100644 node_modules/mongodb/external-libs/bson/test/test_bson.js create mode 100644 node_modules/mongodb/external-libs/bson/test/test_full_bson.js create mode 100644 node_modules/mongodb/external-libs/bson/test/test_stackless_bson.js create mode 100644 node_modules/mongodb/external-libs/bson/wscript (limited to 'node_modules/mongodb/external-libs') diff --git a/node_modules/mongodb/external-libs/bson/Makefile b/node_modules/mongodb/external-libs/bson/Makefile new file mode 100644 index 0000000..ad877d4 --- /dev/null +++ b/node_modules/mongodb/external-libs/bson/Makefile @@ -0,0 +1,45 @@ +NODE = node +name = all +JOBS = 1 + +all: + rm -rf build .lock-wscript bson.node + node-waf configure build + cp -R ./build/Release/bson.node . || true + @$(NODE) --expose-gc test/test_bson.js + @$(NODE) --expose-gc test/test_full_bson.js + # @$(NODE) --expose-gc test/test_stackless_bson.js + +all_debug: + rm -rf build .lock-wscript bson.node + node-waf --debug configure build + cp -R ./build/Release/bson.node . || true + @$(NODE) --expose-gc test/test_bson.js + @$(NODE) --expose-gc test/test_full_bson.js + # @$(NODE) --expose-gc test/test_stackless_bson.js + +test: + @$(NODE) --expose-gc test/test_bson.js + @$(NODE) --expose-gc test/test_full_bson.js + # @$(NODE) --expose-gc test/test_stackless_bson.js + +clang: + rm -rf build .lock-wscript bson.node + CXX=clang node-waf configure build + cp -R ./build/Release/bson.node . || true + @$(NODE) --expose-gc test/test_bson.js + @$(NODE) --expose-gc test/test_full_bson.js + # @$(NODE) --expose-gc test/test_stackless_bson.js + +clang_debug: + rm -rf build .lock-wscript bson.node + CXX=clang node-waf --debug configure build + cp -R ./build/Release/bson.node . || true + @$(NODE) --expose-gc test/test_bson.js + @$(NODE) --expose-gc test/test_full_bson.js + # @$(NODE) --expose-gc test/test_stackless_bson.js + +clean: + rm -rf build .lock-wscript bson.node + +.PHONY: all \ No newline at end of file diff --git a/node_modules/mongodb/external-libs/bson/bson.cc b/node_modules/mongodb/external-libs/bson/bson.cc new file mode 100644 index 0000000..8906eea --- /dev/null +++ b/node_modules/mongodb/external-libs/bson/bson.cc @@ -0,0 +1,2165 @@ +#include +#include +#include + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" +#endif + +#include + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bson.h" + +using namespace v8; +using namespace node; +using namespace std; + +// BSON DATA TYPES +const uint32_t BSON_DATA_NUMBER = 1; +const uint32_t BSON_DATA_STRING = 2; +const uint32_t BSON_DATA_OBJECT = 3; +const uint32_t BSON_DATA_ARRAY = 4; +const uint32_t BSON_DATA_BINARY = 5; +const uint32_t BSON_DATA_OID = 7; +const uint32_t BSON_DATA_BOOLEAN = 8; +const uint32_t BSON_DATA_DATE = 9; +const uint32_t BSON_DATA_NULL = 10; +const uint32_t BSON_DATA_REGEXP = 11; +const uint32_t BSON_DATA_CODE = 13; +const uint32_t BSON_DATA_SYMBOL = 14; +const uint32_t BSON_DATA_CODE_W_SCOPE = 15; +const uint32_t BSON_DATA_INT = 16; +const uint32_t BSON_DATA_TIMESTAMP = 17; +const uint32_t BSON_DATA_LONG = 18; +const uint32_t BSON_DATA_MIN_KEY = 0xff; +const uint32_t BSON_DATA_MAX_KEY = 0x7f; + +const int32_t BSON_INT32_MAX = (int32_t)2147483647L; +const int32_t BSON_INT32_MIN = (int32_t)(-1) * 2147483648L; + +const int64_t BSON_INT64_MAX = ((int64_t)1 << 63) - 1; +const int64_t BSON_INT64_MIN = (int64_t)-1 << 63; + +const int64_t JS_INT_MAX = (int64_t)1 << 53; +const int64_t JS_INT_MIN = (int64_t)-1 << 53; + +static Handle VException(const char *msg) { + HandleScope scope; + return ThrowException(Exception::Error(String::New(msg))); + }; + +Persistent BSON::constructor_template; + +void BSON::Initialize(v8::Handle target) { + // Grab the scope of the call from Node + HandleScope scope; + // Define a new function template + Local t = FunctionTemplate::New(New); + constructor_template = Persistent::New(t); + constructor_template->InstanceTemplate()->SetInternalFieldCount(1); + constructor_template->SetClassName(String::NewSymbol("BSON")); + + // Instance methods + NODE_SET_PROTOTYPE_METHOD(constructor_template, "calculateObjectSize", CalculateObjectSize); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "serialize", BSONSerialize); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "serializeWithBufferAndIndex", SerializeWithBufferAndIndex); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "deserialize", BSONDeserialize); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "deserializeStream", BSONDeserializeStream); + + // Experimental + // NODE_SET_PROTOTYPE_METHOD(constructor_template, "calculateObjectSize2", CalculateObjectSize2); + // NODE_SET_PROTOTYPE_METHOD(constructor_template, "serialize2", BSONSerialize2); + // NODE_SET_METHOD(constructor_template->GetFunction(), "serialize2", BSONSerialize2); + + target->ForceSet(String::NewSymbol("BSON"), constructor_template->GetFunction()); +} + +// Create a new instance of BSON and assing it the existing context +Handle BSON::New(const Arguments &args) { + HandleScope scope; + + // Check that we have an array + if(args.Length() == 1 && args[0]->IsArray()) { + // Cast the array to a local reference + Local array = Local::Cast(args[0]); + + if(array->Length() > 0) { + // Create a bson object instance and return it + BSON *bson = new BSON(); + + // Setup pre-allocated comparision objects + bson->_bsontypeString = Persistent::New(String::New("_bsontype")); + bson->_longLowString = Persistent::New(String::New("low_")); + bson->_longHighString = Persistent::New(String::New("high_")); + bson->_objectIDidString = Persistent::New(String::New("id")); + bson->_binaryPositionString = Persistent::New(String::New("position")); + bson->_binarySubTypeString = Persistent::New(String::New("sub_type")); + bson->_binaryBufferString = Persistent::New(String::New("buffer")); + bson->_doubleValueString = Persistent::New(String::New("value")); + bson->_symbolValueString = Persistent::New(String::New("value")); + bson->_dbRefRefString = Persistent::New(String::New("$ref")); + bson->_dbRefIdRefString = Persistent::New(String::New("$id")); + bson->_dbRefDbRefString = Persistent::New(String::New("$db")); + bson->_dbRefNamespaceString = Persistent::New(String::New("namespace")); + bson->_dbRefDbString = Persistent::New(String::New("db")); + bson->_dbRefOidString = Persistent::New(String::New("oid")); + + // total number of found classes + uint32_t numberOfClasses = 0; + + // Iterate over all entries to save the instantiate funtions + for(uint32_t i = 0; i < array->Length(); i++) { + // Let's get a reference to the function + Local func = Local::Cast(array->Get(i)); + Local functionName = func->GetName()->ToString(); + + // Save the functions making them persistant handles (they don't get collected) + if(functionName->StrictEquals(String::New("Long"))) { + bson->longConstructor = Persistent::New(func); + bson->longString = Persistent::New(String::New("Long")); + numberOfClasses = numberOfClasses + 1; + } else if(functionName->StrictEquals(String::New("ObjectID"))) { + bson->objectIDConstructor = Persistent::New(func); + bson->objectIDString = Persistent::New(String::New("ObjectID")); + numberOfClasses = numberOfClasses + 1; + } else if(functionName->StrictEquals(String::New("Binary"))) { + bson->binaryConstructor = Persistent::New(func); + bson->binaryString = Persistent::New(String::New("Binary")); + numberOfClasses = numberOfClasses + 1; + } else if(functionName->StrictEquals(String::New("Code"))) { + bson->codeConstructor = Persistent::New(func); + bson->codeString = Persistent::New(String::New("Code")); + numberOfClasses = numberOfClasses + 1; + } else if(functionName->StrictEquals(String::New("DBRef"))) { + bson->dbrefConstructor = Persistent::New(func); + bson->dbrefString = Persistent::New(String::New("DBRef")); + numberOfClasses = numberOfClasses + 1; + } else if(functionName->StrictEquals(String::New("Symbol"))) { + bson->symbolConstructor = Persistent::New(func); + bson->symbolString = Persistent::New(String::New("Symbol")); + numberOfClasses = numberOfClasses + 1; + } else if(functionName->StrictEquals(String::New("Double"))) { + bson->doubleConstructor = Persistent::New(func); + bson->doubleString = Persistent::New(String::New("Double")); + numberOfClasses = numberOfClasses + 1; + } else if(functionName->StrictEquals(String::New("Timestamp"))) { + bson->timestampConstructor = Persistent::New(func); + bson->timestampString = Persistent::New(String::New("Timestamp")); + numberOfClasses = numberOfClasses + 1; + } else if(functionName->StrictEquals(String::New("MinKey"))) { + bson->minKeyConstructor = Persistent::New(func); + bson->minKeyString = Persistent::New(String::New("MinKey")); + numberOfClasses = numberOfClasses + 1; + } else if(functionName->StrictEquals(String::New("MaxKey"))) { + bson->maxKeyConstructor = Persistent::New(func); + bson->maxKeyString = Persistent::New(String::New("MaxKey")); + numberOfClasses = numberOfClasses + 1; + } + } + + // Check if we have the right number of constructors otherwise throw an error + if(numberOfClasses != 10) { + // Destroy object + delete(bson); + // Fire exception + return VException("Missing function constructor for either [Long/ObjectID/Binary/Code/DbRef/Symbol/Double/Timestamp/MinKey/MaxKey]"); + } else { + bson->Wrap(args.This()); + return args.This(); + } + } else { + return VException("No types passed in"); + } + } else { + return VException("Argument passed in must be an array of types"); + } +} + +void BSON::write_int32(char *data, uint32_t value) { + // Write the int to the char* + memcpy(data, &value, 4); +} + +void BSON::write_double(char *data, double value) { + // Write the double to the char* + memcpy(data, &value, 8); +} + +void BSON::write_int64(char *data, int64_t value) { + // Write the int to the char* + memcpy(data, &value, 8); +} + +char *BSON::check_key(Local key) { + // Allocate space for they key string + char *key_str = (char *)malloc(key->Utf8Length() * sizeof(char) + 1); + // Error string + char *error_str = (char *)malloc(256 * sizeof(char)); + // Decode the key + ssize_t len = DecodeBytes(key, BINARY); + DecodeWrite(key_str, len, key, BINARY); + *(key_str + key->Utf8Length()) = '\0'; + // Check if we have a valid key + if(key->Utf8Length() > 0 && *(key_str) == '$') { + // Create the string + sprintf(error_str, "key %s must not start with '$'", key_str); + // Free up memory + free(key_str); + // Throw exception with string + throw error_str; + } else if(key->Utf8Length() > 0 && strchr(key_str, '.') != NULL) { + // Create the string + sprintf(error_str, "key %s must not contain '.'", key_str); + // Free up memory + free(key_str); + // Throw exception with string + throw error_str; + } + // Free allocated space + free(key_str); + free(error_str); + // Return No check key error + return NULL; +} + +const char* BSON::ToCString(const v8::String::Utf8Value& value) { + return *value ? *value : ""; +} + +Handle BSON::decodeDBref(BSON *bson, Local ref, Local oid, Local db) { + HandleScope scope; + Local argv[] = {ref, oid, db}; + Handle dbrefObj = bson->dbrefConstructor->NewInstance(3, argv); + return scope.Close(dbrefObj); +} + +Handle BSON::decodeCode(BSON *bson, char *code, Handle scope_object) { + HandleScope scope; + + Local argv[] = {String::New(code), scope_object->ToObject()}; + Handle codeObj = bson->codeConstructor->NewInstance(2, argv); + return scope.Close(codeObj); +} + +Handle BSON::decodeBinary(BSON *bson, uint32_t sub_type, uint32_t number_of_bytes, char *data) { + HandleScope scope; + + // Create a buffer object that wraps the raw stream + Buffer *bufferObj = Buffer::New(data, number_of_bytes); + // Arguments to be passed to create the binary + Handle argv[] = {bufferObj->handle_, Uint32::New(sub_type)}; + // Return the buffer handle + Local bufferObjHandle = bson->binaryConstructor->NewInstance(2, argv); + // Close the scope + return scope.Close(bufferObjHandle); +} + +Handle BSON::decodeOid(BSON *bson, char *oid) { + HandleScope scope; + + // Encode the string (string - null termiating character) + Local bin_value = Encode(oid, 12, BINARY)->ToString(); + + // Return the id object + Local argv[] = {bin_value}; + Local oidObj = bson->objectIDConstructor->NewInstance(1, argv); + return scope.Close(oidObj); +} + +Handle BSON::decodeLong(BSON *bson, char *data, uint32_t index) { + HandleScope scope; + + // Decode the integer value + int32_t lowBits = 0; + int32_t highBits = 0; + memcpy(&lowBits, (data + index), 4); + memcpy(&highBits, (data + index + 4), 4); + + // Decode 64bit value + int64_t value = 0; + memcpy(&value, (data + index), 8); + + // If value is < 2^53 and >-2^53 + if((highBits < 0x200000 || (highBits == 0x200000 && lowBits == 0)) && highBits >= -0x200000) { + int64_t finalValue = 0; + memcpy(&finalValue, (data + index), 8); + return scope.Close(Number::New(finalValue)); + } + + // Instantiate the js object and pass it back + Local argv[] = {Int32::New(lowBits), Int32::New(highBits)}; + Local longObject = bson->longConstructor->NewInstance(2, argv); + return scope.Close(longObject); +} + +Handle BSON::decodeTimestamp(BSON *bson, char *data, uint32_t index) { + HandleScope scope; + + // Decode the integer value + int32_t lowBits = 0; + int32_t highBits = 0; + memcpy(&lowBits, (data + index), 4); + memcpy(&highBits, (data + index + 4), 4); + + // Build timestamp + Local argv[] = {Int32::New(lowBits), Int32::New(highBits)}; + Handle timestamp_obj = bson->timestampConstructor->NewInstance(2, argv); + return scope.Close(timestamp_obj); +} + +// Search for 0 terminated C string and return the string +char* BSON::extract_string(char *data, uint32_t offset) { + char *prt = strchr((data + offset), '\0'); + if(prt == NULL) return NULL; + // Figure out the length of the string + uint32_t length = (prt - data) - offset; + // Allocate memory for the new string + char *string_name = (char *)malloc((length * sizeof(char)) + 1); + // Copy the variable into the string_name + strncpy(string_name, (data + offset), length); + // Ensure the string is null terminated + *(string_name + length) = '\0'; + // Return the unpacked string + return string_name; +} + +// Decode a byte +uint16_t BSON::deserialize_int8(char *data, uint32_t offset) { + uint16_t value = 0; + value |= *(data + offset + 0); + return value; +} + +// Requires a 4 byte char array +uint32_t BSON::deserialize_int32(char* data, uint32_t offset) { + uint32_t value = 0; + memcpy(&value, (data + offset), 4); + return value; +} + +//------------------------------------------------------------------------------------------------ +// +// Experimental +// +//------------------------------------------------------------------------------------------------ +Handle BSON::CalculateObjectSize2(const Arguments &args) { + HandleScope scope; + // Ensure we have a valid object + if(args.Length() == 1 && !args[0]->IsObject()) return VException("One argument required - [object]"); + if(args.Length() > 1) return VException("One argument required - [object]"); + // Calculate size of the object + uint32_t object_size = BSON::calculate_object_size2(args[0]); + // Return the object size + return scope.Close(Uint32::New(object_size)); +} + +uint32_t BSON::calculate_object_size2(Handle value) { + // Final object size + uint32_t object_size = (4 + 1); + uint32_t stackIndex = 0; + // Controls the flow + bool done = false; + bool finished = false; + + // Current object we are processing + Local currentObject = value->ToObject(); + + // Current list of object keys + #if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION < 6 + Local keys = currentObject->GetPropertyNames(); + #else + Local keys = currentObject->GetOwnPropertyNames(); + #endif + + // Contains pointer to keysIndex + uint32_t keysIndex = 0; + uint32_t keysLength = keys->Length(); + + // printf("=================================================================================\n"); + // printf("Start serializing\n"); + + while(!done) { + // If the index is bigger than the number of keys for the object + // we finished up the previous object and are ready for the next one + if(keysIndex >= keysLength) { + #if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION < 6 + keys = currentObject->GetPropertyNames(); + #else + keys = currentObject->GetOwnPropertyNames(); + #endif + keysLength = keys->Length(); + } + + // Iterate over all the keys + while(keysIndex < keysLength) { + // Fetch the key name + Local name = keys->Get(keysIndex++)->ToString(); + // Fetch the object related to the key + Local value = currentObject->Get(name); + // Add size of the name, plus zero, plus type + object_size += name->Utf8Length() + 1 + 1; + + // If we have a string + if(value->IsString()) { + object_size += value->ToString()->Utf8Length() + 1 + 4; + } else if(value->IsNumber()) { + // Check if we have a float value or a long value + Local number = value->ToNumber(); + double d_number = number->NumberValue(); + int64_t l_number = number->IntegerValue(); + // Check if we have a double value and not a int64 + double d_result = d_number - l_number; + // If we have a value after subtracting the integer value we have a float + if(d_result > 0 || d_result < 0) { + object_size = object_size + 8; + } else if(l_number <= BSON_INT32_MAX && l_number >= BSON_INT32_MIN) { + object_size = object_size + 4; + } else { + object_size = object_size + 8; + } + } else if(value->IsBoolean()) { + object_size = object_size + 1; + } else if(value->IsDate()) { + object_size = object_size + 8; + } else if(value->IsRegExp()) { + // Fetch the string for the regexp + Handle regExp = Handle::Cast(value); + ssize_t len = DecodeBytes(regExp->GetSource(), UTF8); + int flags = regExp->GetFlags(); + + // global + if((flags & (1 << 0)) != 0) len++; + // ignorecase + if((flags & (1 << 1)) != 0) len++; + //multiline + if((flags & (1 << 2)) != 0) len++; + // if((flags & (1 << 2)) != 0) len++; + // Calculate the space needed for the regexp: size of string - 2 for the /'ses +2 for null termiations + object_size = object_size + len + 2; + } else if(value->IsNull() || value->IsUndefined()) { + } + // } else if(value->IsNumber()) { + // // Check if we have a float value or a long value + // Local number = value->ToNumber(); + // double d_number = number->NumberValue(); + // int64_t l_number = number->IntegerValue(); + // // Check if we have a double value and not a int64 + // double d_result = d_number - l_number; + // // If we have a value after subtracting the integer value we have a float + // if(d_result > 0 || d_result < 0) { + // object_size = name->Utf8Length() + 1 + object_size + 8 + 1; + // } else if(l_number <= BSON_INT32_MAX && l_number >= BSON_INT32_MIN) { + // object_size = name->Utf8Length() + 1 + object_size + 4 + 1; + // } else { + // object_size = name->Utf8Length() + 1 + object_size + 8 + 1; + // } + // } else if(value->IsObject()) { + // printf("------------- hello\n"); + // } + } + + // If we have finished all the keys + if(keysIndex == keysLength) { + finished = false; + } + + // Validate the stack + if(stackIndex == 0) { + // printf("======================================================================== 3\n"); + done = true; + } else if(finished || keysIndex == keysLength) { + // Pop off the stack + stackIndex = stackIndex - 1; + // Fetch the current object stack + // vector > currentObjectStored = stack.back(); + // stack.pop_back(); + // // Unroll the current object + // currentObject = currentObjectStored.back()->ToObject(); + // currentObjectStored.pop_back(); + // // Unroll the keysIndex + // keys = Local::Cast(currentObjectStored.back()->ToObject()); + // currentObjectStored.pop_back(); + // // Unroll the keysIndex + // keysIndex = currentObjectStored.back()->ToUint32()->Value(); + // currentObjectStored.pop_back(); + // // Check if we finished up + // if(keysIndex == keys->Length()) { + // finished = true; + // } + } + } + + return object_size; +} + +//------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------------ +Handle BSON::BSONDeserialize(const Arguments &args) { + HandleScope scope; + + // Ensure that we have an parameter + if(Buffer::HasInstance(args[0]) && args.Length() > 1) return VException("One argument required - buffer1."); + if(args[0]->IsString() && args.Length() > 1) return VException("One argument required - string1."); + // Throw an exception if the argument is not of type Buffer + if(!Buffer::HasInstance(args[0]) && !args[0]->IsString()) return VException("Argument must be a Buffer or String."); + + // Define pointer to data + char *data; + Local obj = args[0]->ToObject(); + + // Unpack the BSON parser instance + BSON *bson = ObjectWrap::Unwrap(args.This()); + + // If we passed in a buffer, let's unpack it, otherwise let's unpack the string + if(Buffer::HasInstance(obj)) { + + #if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION < 3 + Buffer *buffer = ObjectWrap::Unwrap(obj); + data = buffer->data(); + uint32_t length = buffer->length(); + #else + data = Buffer::Data(obj); + uint32_t length = Buffer::Length(obj); + #endif + + // Validate that we have at least 5 bytes + if(length < 5) { + return VException("corrupt bson message < 5 bytes long"); + } + + // Deserialize the data + return BSON::deserialize(bson, data, length, 0, NULL); + } else { + // The length of the data for this encoding + ssize_t len = DecodeBytes(args[0], BINARY); + + // Validate that we have at least 5 bytes + if(len < 5) { + return VException("corrupt bson message < 5 bytes long"); + } + + // Let's define the buffer size + data = (char *)malloc(len); + // Write the data to the buffer from the string object + ssize_t written = DecodeWrite(data, len, args[0], BINARY); + // Assert that we wrote the same number of bytes as we have length + assert(written == len); + // Get result + Handle result = BSON::deserialize(bson, data, len, 0, NULL); + // Free memory + free(data); + // Deserialize the content + return result; + } +} + +// Deserialize the stream +Handle BSON::deserialize(BSON *bson, char *data, uint32_t inDataLength, uint32_t startIndex, bool is_array_item) { + HandleScope scope; + // Holds references to the objects that are going to be returned + Local return_data = Object::New(); + Local return_array = Array::New(); + // The current index in the char data + uint32_t index = startIndex; + // Decode the size of the BSON data structure + uint32_t size = BSON::deserialize_int32(data, index); + + // If we have an illegal message size + if(size > inDataLength) return VException("corrupt bson message"); + + // Data length + uint32_t dataLength = index + size; + + // Adjust the index to point to next piece + index = index + 4; + + // While we have data left let's decode + while(index < dataLength) { + // Read the first to bytes to indicate the type of object we are decoding + uint8_t type = BSON::deserialize_int8(data, index); + // Adjust index to skip type byte + index = index + 1; + + if(type == BSON_DATA_STRING) { + // Read the null terminated index String + char *string_name = BSON::extract_string(data, index); + if(string_name == NULL) return VException("Invalid C String found."); + // Let's create a new string + index = index + strlen(string_name) + 1; + // Handle array value if applicable + uint32_t insert_index = 0; + if(is_array_item) { + insert_index = atoi(string_name); + } + + // Read the length of the string (next 4 bytes) + uint32_t string_size = BSON::deserialize_int32(data, index); + // Adjust index to point to start of string + index = index + 4; + // Decode the string and add zero terminating value at the end of the string + char *value = (char *)malloc((string_size * sizeof(char))); + strncpy(value, (data + index), string_size); + // Encode the string (string - null termiating character) + Local utf8_encoded_str = Encode(value, string_size - 1, UTF8)->ToString(); + // Add the value to the data + if(is_array_item) { + return_array->Set(Number::New(insert_index), utf8_encoded_str); + } else { + return_data->ForceSet(String::New(string_name), utf8_encoded_str); + } + + // Adjust index + index = index + string_size; + // Free up the memory + free(value); + free(string_name); + } else if(type == BSON_DATA_INT) { + // Read the null terminated index String + char *string_name = BSON::extract_string(data, index); + if(string_name == NULL) return VException("Invalid C String found."); + // Let's create a new string + index = index + strlen(string_name) + 1; + // Handle array value if applicable + uint32_t insert_index = 0; + if(is_array_item) { + insert_index = atoi(string_name); + } + + // Decode the integer value + uint32_t value = 0; + memcpy(&value, (data + index), 4); + + // Adjust the index for the size of the value + index = index + 4; + // Add the element to the object + if(is_array_item) { + return_array->Set(Integer::New(insert_index), Integer::New(value)); + } else { + return_data->ForceSet(String::New(string_name), Integer::New(value)); + } + // Free up the memory + free(string_name); + } else if(type == BSON_DATA_TIMESTAMP) { + // Read the null terminated index String + char *string_name = BSON::extract_string(data, index); + if(string_name == NULL) return VException("Invalid C String found."); + // Let's create a new string + index = index + strlen(string_name) + 1; + // Handle array value if applicable + uint32_t insert_index = 0; + if(is_array_item) { + insert_index = atoi(string_name); + } + + // Add the element to the object + if(is_array_item) { + return_array->Set(Number::New(insert_index), BSON::decodeTimestamp(bson, data, index)); + } else { + return_data->ForceSet(String::New(string_name), BSON::decodeTimestamp(bson, data, index)); + } + + // Adjust the index for the size of the value + index = index + 8; + + // Free up the memory + free(string_name); + } else if(type == BSON_DATA_LONG) { + // Read the null terminated index String + char *string_name = BSON::extract_string(data, index); + if(string_name == NULL) return VException("Invalid C String found."); + // Let's create a new string + index = index + strlen(string_name) + 1; + // Handle array value if applicable + uint32_t insert_index = 0; + if(is_array_item) { + insert_index = atoi(string_name); + } + + // Add the element to the object + if(is_array_item) { + return_array->Set(Number::New(insert_index), BSON::decodeLong(bson, data, index)); + } else { + return_data->ForceSet(String::New(string_name), BSON::decodeLong(bson, data, index)); + } + + // Adjust the index for the size of the value + index = index + 8; + + // Free up the memory + free(string_name); + } else if(type == BSON_DATA_NUMBER) { + // Read the null terminated index String + char *string_name = BSON::extract_string(data, index); + if(string_name == NULL) return VException("Invalid C String found."); + // Let's create a new string + index = index + strlen(string_name) + 1; + // Handle array value if applicable + uint32_t insert_index = 0; + if(is_array_item) { + insert_index = atoi(string_name); + } + + // Decode the integer value + double value = 0; + memcpy(&value, (data + index), 8); + // Adjust the index for the size of the value + index = index + 8; + + // Add the element to the object + if(is_array_item) { + return_array->Set(Number::New(insert_index), Number::New(value)); + } else { + return_data->ForceSet(String::New(string_name), Number::New(value)); + } + // Free up the memory + free(string_name); + } else if(type == BSON_DATA_MIN_KEY) { + // Read the null terminated index String + char *string_name = BSON::extract_string(data, index); + if(string_name == NULL) return VException("Invalid C String found."); + // Let's create a new string + index = index + strlen(string_name) + 1; + // Handle array value if applicable + uint32_t insert_index = 0; + if(is_array_item) { + insert_index = atoi(string_name); + } + + // Create new MinKey + Local minKey = bson->minKeyConstructor->NewInstance(); + // Add the element to the object + if(is_array_item) { + return_array->Set(Number::New(insert_index), minKey); + } else { + return_data->ForceSet(String::New(string_name), minKey); + } + // Free up the memory + free(string_name); + } else if(type == BSON_DATA_MAX_KEY) { + // Read the null terminated index String + char *string_name = BSON::extract_string(data, index); + if(string_name == NULL) return VException("Invalid C String found."); + // Let's create a new string + index = index + strlen(string_name) + 1; + // Handle array value if applicable + uint32_t insert_index = 0; + if(is_array_item) { + insert_index = atoi(string_name); + } + + // Create new MinKey + Local maxKey = bson->maxKeyConstructor->NewInstance(); + // Add the element to the object + if(is_array_item) { + return_array->Set(Number::New(insert_index), maxKey); + } else { + return_data->ForceSet(String::New(string_name), maxKey); + } + // Free up the memory + free(string_name); + } else if(type == BSON_DATA_NULL) { + // Read the null terminated index String + char *string_name = BSON::extract_string(data, index); + if(string_name == NULL) return VException("Invalid C String found."); + // Let's create a new string + index = index + strlen(string_name) + 1; + // Handle array value if applicable + uint32_t insert_index = 0; + if(is_array_item) { + insert_index = atoi(string_name); + } + + // Add the element to the object + if(is_array_item) { + return_array->Set(Number::New(insert_index), Null()); + } else { + return_data->ForceSet(String::New(string_name), Null()); + } + // Free up the memory + free(string_name); + } else if(type == BSON_DATA_BOOLEAN) { + // Read the null terminated index String + char *string_name = BSON::extract_string(data, index); + if(string_name == NULL) return VException("Invalid C String found."); + // Let's create a new string + index = index + strlen(string_name) + 1; + // Handle array value if applicable + uint32_t insert_index = 0; + if(is_array_item) { + insert_index = atoi(string_name); + } + + // Decode the boolean value + char bool_value = *(data + index); + // Adjust the index for the size of the value + index = index + 1; + + // Add the element to the object + if(is_array_item) { + return_array->Set(Number::New(insert_index), bool_value == 1 ? Boolean::New(true) : Boolean::New(false)); + } else { + return_data->ForceSet(String::New(string_name), bool_value == 1 ? Boolean::New(true) : Boolean::New(false)); + } + // Free up the memory + free(string_name); + } else if(type == BSON_DATA_DATE) { + // Read the null terminated index String + char *string_name = BSON::extract_string(data, index); + if(string_name == NULL) return VException("Invalid C String found."); + // Let's create a new string + index = index + strlen(string_name) + 1; + // Handle array value if applicable + uint32_t insert_index = 0; + if(is_array_item) { + insert_index = atoi(string_name); + } + + // Decode the value 64 bit integer + int64_t value = 0; + memcpy(&value, (data + index), 8); + // Adjust the index for the size of the value + index = index + 8; + // Add the element to the object + if(is_array_item) { + return_array->Set(Number::New(insert_index), Date::New((double)value)); + } else { + return_data->ForceSet(String::New(string_name), Date::New((double)value)); + } + // Free up the memory + free(string_name); + } else if(type == BSON_DATA_REGEXP) { + // Read the null terminated index String + char *string_name = BSON::extract_string(data, index); + if(string_name == NULL) return VException("Invalid C String found."); + // Let's create a new string + index = index + strlen(string_name) + 1; + // Handle array value if applicable + uint32_t insert_index = 0; + if(is_array_item) { + insert_index = atoi(string_name); + } + + // Length variable + int32_t length_regexp = 0; + char chr; + + // Locate end of the regexp expression \0 + while((chr = *(data + index + length_regexp)) != '\0') { + length_regexp = length_regexp + 1; + } + + // Contains the reg exp + char *reg_exp = (char *)malloc(length_regexp * sizeof(char) + 2); + // Copy the regexp from the data to the char * + memcpy(reg_exp, (data + index), (length_regexp + 1)); + // Adjust the index to skip the first part of the regular expression + index = index + length_regexp + 1; + + // Reset the length + int32_t options_length = 0; + // Locate the end of the options for the regexp terminated with a '\0' + while((chr = *(data + index + options_length)) != '\0') { + options_length = options_length + 1; + } + + // Contains the reg exp + char *options = (char *)malloc(options_length * sizeof(char) + 1); + // Copy the options from the data to the char * + memcpy(options, (data + index), (options_length + 1)); + // Adjust the index to skip the option part of the regular expression + index = index + options_length + 1; + // ARRRRGH Google does not expose regular expressions through the v8 api + // Have to use Script to instantiate the object (slower) + + // Generate the string for execution in the string context + int flag = 0; + + for(int i = 0; i < options_length; i++) { + // Multiline + if(*(options + i) == 'm') { + flag = flag | 4; + } else if(*(options + i) == 'i') { + flag = flag | 2; + } + } + + // Add the element to the object + if(is_array_item) { + return_array->Set(Number::New(insert_index), RegExp::New(String::New(reg_exp), (v8::RegExp::Flags)flag)); + } else { + return_data->ForceSet(String::New(string_name), RegExp::New(String::New(reg_exp), (v8::RegExp::Flags)flag)); + } + + // Free memory + free(reg_exp); + free(options); + free(string_name); + } else if(type == BSON_DATA_OID) { + // Read the null terminated index String + char *string_name = BSON::extract_string(data, index); + if(string_name == NULL) return VException("Invalid C String found."); + // Let's create a new string + index = index + strlen(string_name) + 1; + // Handle array value if applicable + uint32_t insert_index = 0; + if(is_array_item) { + insert_index = atoi(string_name); + } + + // The id string + char *oid_string = (char *)malloc(12 * sizeof(char)); + // Copy the options from the data to the char * + memcpy(oid_string, (data + index), 12); + + // Adjust the index + index = index + 12; + + // Add the element to the object + if(is_array_item) { + return_array->Set(Number::New(insert_index), BSON::decodeOid(bson, oid_string)); + } else { + return_data->ForceSet(String::New(string_name), BSON::decodeOid(bson, oid_string)); + } + + // Free memory + free(oid_string); + free(string_name); + } else if(type == BSON_DATA_BINARY) { + // Read the null terminated index String + char *string_name = BSON::extract_string(data, index); + if(string_name == NULL) return VException("Invalid C String found."); + // Let's create a new string + index = index + strlen(string_name) + 1; + // Handle array value if applicable + uint32_t insert_index = 0; + if(is_array_item) { + insert_index = atoi(string_name); + } + + // Read the binary data size + uint32_t number_of_bytes = BSON::deserialize_int32(data, index); + // Adjust the index + index = index + 4; + // Decode the subtype, ensure it's positive + uint32_t sub_type = (int)*(data + index) & 0xff; + // Adjust the index + index = index + 1; + // Copy the binary data into a buffer + char *buffer = (char *)malloc(number_of_bytes * sizeof(char) + 1); + memcpy(buffer, (data + index), number_of_bytes); + *(buffer + number_of_bytes) = '\0'; + + // Adjust the index + index = index + number_of_bytes; + + // Add the element to the object + if(is_array_item) { + return_array->Set(Number::New(insert_index), BSON::decodeBinary(bson, sub_type, number_of_bytes, buffer)); + } else { + return_data->ForceSet(String::New(string_name), BSON::decodeBinary(bson, sub_type, number_of_bytes, buffer)); + } + // Free memory + free(buffer); + free(string_name); + } else if(type == BSON_DATA_SYMBOL) { + // Read the null terminated index String + char *string_name = BSON::extract_string(data, index); + if(string_name == NULL) return VException("Invalid C String found."); + // Let's create a new string + index = index + strlen(string_name) + 1; + // Handle array value if applicable + uint32_t insert_index = 0; + if(is_array_item) { + insert_index = atoi(string_name); + } + + // Read the length of the string (next 4 bytes) + uint32_t string_size = BSON::deserialize_int32(data, index); + // Adjust index to point to start of string + index = index + 4; + // Decode the string and add zero terminating value at the end of the string + char *value = (char *)malloc((string_size * sizeof(char))); + strncpy(value, (data + index), string_size); + // Encode the string (string - null termiating character) + Local utf8_encoded_str = Encode(value, string_size - 1, UTF8)->ToString(); + + // Wrap up the string in a Symbol Object + Local argv[] = {utf8_encoded_str}; + Handle symbolObj = bson->symbolConstructor->NewInstance(1, argv); + + // Add the value to the data + if(is_array_item) { + return_array->Set(Number::New(insert_index), symbolObj); + } else { + return_data->ForceSet(String::New(string_name), symbolObj); + } + + // Adjust index + index = index + string_size; + // Free up the memory + free(value); + free(string_name); + } else if(type == BSON_DATA_CODE) { + // Read the null terminated index String + char *string_name = BSON::extract_string(data, index); + if(string_name == NULL) return VException("Invalid C String found."); + // Let's create a new string + index = index + strlen(string_name) + 1; + // Handle array value if applicable + uint32_t insert_index = 0; + if(is_array_item) { + insert_index = atoi(string_name); + } + + // Read the string size + uint32_t string_size = BSON::deserialize_int32(data, index); + // Adjust the index + index = index + 4; + // Read the string + char *code = (char *)malloc(string_size * sizeof(char) + 1); + // Copy string + terminating 0 + memcpy(code, (data + index), string_size); + + // Define empty scope object + Handle scope_object = Object::New(); + + // Define the try catch block + TryCatch try_catch; + // Decode the code object + Handle obj = BSON::decodeCode(bson, code, scope_object); + // If an error was thrown push it up the chain + if(try_catch.HasCaught()) { + free(string_name); + free(code); + // Rethrow exception + return try_catch.ReThrow(); + } + + // Add the element to the object + if(is_array_item) { + return_array->Set(Number::New(insert_index), obj); + } else { + return_data->ForceSet(String::New(string_name), obj); + } + + // Clean up memory allocation + free(code); + free(string_name); + } else if(type == BSON_DATA_CODE_W_SCOPE) { + // Read the null terminated index String + char *string_name = BSON::extract_string(data, index); + if(string_name == NULL) return VException("Invalid C String found."); + // Let's create a new string + index = index + strlen(string_name) + 1; + // Handle array value if applicable + uint32_t insert_index = 0; + if(is_array_item) { + insert_index = atoi(string_name); + } + + // Total number of bytes after array index + uint32_t total_code_size = BSON::deserialize_int32(data, index); + // Adjust the index + index = index + 4; + // Read the string size + uint32_t string_size = BSON::deserialize_int32(data, index); + // Adjust the index + index = index + 4; + // Read the string + char *code = (char *)malloc(string_size * sizeof(char) + 1); + // Copy string + terminating 0 + memcpy(code, (data + index), string_size); + // Adjust the index + index = index + string_size; + // Get the scope object (bson object) + uint32_t bson_object_size = total_code_size - string_size - 8; + // Allocate bson object buffer and copy out the content + char *bson_buffer = (char *)malloc(bson_object_size * sizeof(char)); + memcpy(bson_buffer, (data + index), bson_object_size); + // Adjust the index + index = index + bson_object_size; + // Parse the bson object + Handle scope_object = BSON::deserialize(bson, bson_buffer, inDataLength, 0, false); + // Define the try catch block + TryCatch try_catch; + // Decode the code object + Handle obj = BSON::decodeCode(bson, code, scope_object); + // If an error was thrown push it up the chain + if(try_catch.HasCaught()) { + // Clean up memory allocation + free(string_name); + free(bson_buffer); + free(code); + // Rethrow exception + return try_catch.ReThrow(); + } + + // Add the element to the object + if(is_array_item) { + return_array->Set(Number::New(insert_index), obj); + } else { + return_data->ForceSet(String::New(string_name), obj); + } + + // Clean up memory allocation + free(code); + free(bson_buffer); + free(string_name); + } else if(type == BSON_DATA_OBJECT) { + // If this is the top level object we need to skip the undecoding + // Read the null terminated index String + char *string_name = BSON::extract_string(data, index); + if(string_name == NULL) return VException("Invalid C String found."); + // Let's create a new string + index = index + strlen(string_name) + 1; + // Handle array value if applicable + uint32_t insert_index = 0; + if(is_array_item) { + insert_index = atoi(string_name); + } + + // Get the object size + uint32_t bson_object_size = BSON::deserialize_int32(data, index); + // Define the try catch block + TryCatch try_catch; + // Decode the code object + Handle obj = BSON::deserialize(bson, data + index, inDataLength, 0, false); + // Adjust the index + index = index + bson_object_size; + // If an error was thrown push it up the chain + if(try_catch.HasCaught()) { + // Rethrow exception + return try_catch.ReThrow(); + } + + // Add the element to the object + if(is_array_item) { + return_array->Set(Number::New(insert_index), obj); + } else { + return_data->ForceSet(String::New(string_name), obj); + } + + // Clean up memory allocation + free(string_name); + } else if(type == BSON_DATA_ARRAY) { + // Read the null terminated index String + char *string_name = BSON::extract_string(data, index); + if(string_name == NULL) return VException("Invalid C String found."); + // Let's create a new string + index = index + strlen(string_name) + 1; + // Handle array value if applicable + uint32_t insert_index = 0; + if(is_array_item) { + insert_index = atoi(string_name); + } + + // Get the size + uint32_t array_size = BSON::deserialize_int32(data, index); + // Define the try catch block + TryCatch try_catch; + + // Decode the code object + Handle obj = BSON::deserialize(bson, data + index, inDataLength, 0, true); + // If an error was thrown push it up the chain + if(try_catch.HasCaught()) { + // Rethrow exception + return try_catch.ReThrow(); + } + // Adjust the index for the next value + index = index + array_size; + // Add the element to the object + if(is_array_item) { + return_array->Set(Number::New(insert_index), obj); + } else { + return_data->ForceSet(String::New(string_name), obj); + } + // Clean up memory allocation + free(string_name); + } + } + + // Check if we have a db reference + if(!is_array_item && return_data->Has(String::New("$ref")) && return_data->Has(String::New("$id"))) { + Handle dbrefValue = BSON::decodeDBref(bson, return_data->Get(String::New("$ref")), return_data->Get(String::New("$id")), return_data->Get(String::New("$db"))); + return scope.Close(dbrefValue); + } + + // Return the data object to javascript + if(is_array_item) { + return scope.Close(return_array); + } else { + return scope.Close(return_data); + } +} + +Handle BSON::BSONSerialize(const Arguments &args) { + HandleScope scope; + + if(args.Length() == 1 && !args[0]->IsObject()) return VException("One, two or tree arguments required - [object] or [object, boolean] or [object, boolean, boolean]"); + if(args.Length() == 2 && !args[0]->IsObject() && !args[1]->IsBoolean()) return VException("One, two or tree arguments required - [object] or [object, boolean] or [object, boolean, boolean]"); + if(args.Length() == 3 && !args[0]->IsObject() && !args[1]->IsBoolean() && !args[2]->IsBoolean()) return VException("One, two or tree arguments required - [object] or [object, boolean] or [object, boolean, boolean]"); + if(args.Length() == 4 && !args[0]->IsObject() && !args[1]->IsBoolean() && !args[2]->IsBoolean() && !args[3]->IsBoolean()) return VException("One, two or tree arguments required - [object] or [object, boolean] or [object, boolean, boolean] or [object, boolean, boolean, boolean]"); + if(args.Length() > 4) return VException("One, two, tree or four arguments required - [object] or [object, boolean] or [object, boolean, boolean] or [object, boolean, boolean, boolean]"); + + // Unpack the BSON parser instance + BSON *bson = ObjectWrap::Unwrap(args.This()); + + uint32_t object_size = 0; + // Calculate the total size of the document in binary form to ensure we only allocate memory once + // With serialize function + if(args.Length() == 4) { + object_size = BSON::calculate_object_size(bson, args[0], args[3]->BooleanValue()); + } else { + object_size = BSON::calculate_object_size(bson, args[0], false); + } + + // Allocate the memory needed for the serializtion + char *serialized_object = (char *)malloc(object_size * sizeof(char)); + // Catch any errors + try { + // Check if we have a boolean value + bool check_key = false; + if(args.Length() >= 3 && args[1]->IsBoolean()) { + check_key = args[1]->BooleanValue(); + } + + // Check if we have a boolean value + bool serializeFunctions = false; + if(args.Length() == 4 && args[1]->IsBoolean()) { + serializeFunctions = args[3]->BooleanValue(); + } + + // Serialize the object + BSON::serialize(bson, serialized_object, 0, Null(), args[0], check_key, serializeFunctions); + } catch(char *err_msg) { + // Free up serialized object space + free(serialized_object); + V8::AdjustAmountOfExternalAllocatedMemory(-object_size); + // Throw exception with the string + Handle error = VException(err_msg); + // free error message + free(err_msg); + // Return error + return error; + } + + // Write the object size + BSON::write_int32((serialized_object), object_size); + + // If we have 3 arguments + if(args.Length() == 3 || args.Length() == 4) { + // Local asBuffer = args[2]->ToBoolean(); + Buffer *buffer = Buffer::New(serialized_object, object_size); + // Release the serialized string + free(serialized_object); + return scope.Close(buffer->handle_); + } else { + // Encode the string (string - null termiating character) + Local bin_value = Encode(serialized_object, object_size, BINARY)->ToString(); + // Return the serialized content + return bin_value; + } +} + +Handle BSON::CalculateObjectSize(const Arguments &args) { + HandleScope scope; + // Ensure we have a valid object + if(args.Length() == 1 && !args[0]->IsObject()) return VException("One argument required - [object]"); + if(args.Length() == 2 && !args[0]->IsObject() && !args[1]->IsBoolean()) return VException("Two arguments required - [object, boolean]"); + if(args.Length() > 3) return VException("One or two arguments required - [object] or [object, boolean]"); + + // Unpack the BSON parser instance + BSON *bson = ObjectWrap::Unwrap(args.This()); + + // Object size + uint32_t object_size = 0; + // Check if we have our argument, calculate size of the object + if(args.Length() >= 2) { + object_size = BSON::calculate_object_size(bson, args[0], args[1]->BooleanValue()); + } else { + object_size = BSON::calculate_object_size(bson, args[0], false); + } + + // Return the object size + return scope.Close(Uint32::New(object_size)); +} + +uint32_t BSON::calculate_object_size(BSON *bson, Handle value, bool serializeFunctions) { + uint32_t object_size = 0; + + // If we have an object let's unwrap it and calculate the sub sections + if(value->IsString()) { + // Let's calculate the size the string adds, length + type(1 byte) + size(4 bytes) + object_size += value->ToString()->Utf8Length() + 1 + 4; + } else if(value->IsNumber()) { + // Check if we have a float value or a long value + Local number = value->ToNumber(); + double d_number = number->NumberValue(); + int64_t l_number = number->IntegerValue(); + // Check if we have a double value and not a int64 + double d_result = d_number - l_number; + // If we have a value after subtracting the integer value we have a float + if(d_result > 0 || d_result < 0) { + object_size = object_size + 8; + } else if(l_number <= BSON_INT32_MAX && l_number >= BSON_INT32_MIN) { + object_size = object_size + 4; + } else { + object_size = object_size + 8; + } + } else if(value->IsBoolean()) { + object_size = object_size + 1; + } else if(value->IsDate()) { + object_size = object_size + 8; + } else if(value->IsRegExp()) { + // Fetch the string for the regexp + Handle regExp = Handle::Cast(value); + ssize_t len = DecodeBytes(regExp->GetSource(), UTF8); + int flags = regExp->GetFlags(); + + // global + if((flags & (1 << 0)) != 0) len++; + // ignorecase + if((flags & (1 << 1)) != 0) len++; + //multiline + if((flags & (1 << 2)) != 0) len++; + // if((flags & (1 << 2)) != 0) len++; + // Calculate the space needed for the regexp: size of string - 2 for the /'ses +2 for null termiations + object_size = object_size + len + 2; + } else if(value->IsNull() || value->IsUndefined()) { + } else if(value->IsArray()) { + // Cast to array + Local array = Local::Cast(value->ToObject()); + // Turn length into string to calculate the size of all the strings needed + char *length_str = (char *)malloc(256 * sizeof(char)); + // Calculate the size of each element + for(uint32_t i = 0; i < array->Length(); i++) { + // Add "index" string size for each element + sprintf(length_str, "%d", i); + // Add the size of the string length + uint32_t label_length = strlen(length_str) + 1; + // Add the type definition size for each item + object_size = object_size + label_length + 1; + // Add size of the object + uint32_t object_length = BSON::calculate_object_size(bson, array->Get(Integer::New(i)), serializeFunctions); + object_size = object_size + object_length; + } + // Add the object size + object_size = object_size + 4 + 1; + // Free up memory + free(length_str); + } else if(value->IsFunction()) { + if(serializeFunctions) { + object_size += value->ToString()->Utf8Length() + 4 + 1; + } + } else if(value->ToObject()->Has(bson->_bsontypeString)) { + // Handle holder + Local constructorString = value->ToObject()->GetConstructorName(); + + // BSON type object, avoid non-needed checking unless we have a type + if(bson->longString->StrictEquals(constructorString)) { + object_size = object_size + 8; + } else if(bson->timestampString->StrictEquals(constructorString)) { + object_size = object_size + 8; + } else if(bson->objectIDString->StrictEquals(constructorString)) { + object_size = object_size + 12; + } else if(bson->binaryString->StrictEquals(constructorString)) { + // Unpack the object and encode + Local positionObj = value->ToObject()->Get(String::New("position"))->ToUint32(); + // Adjust the object_size, binary content lengt + total size int32 + binary size int32 + subtype + object_size += positionObj->Value() + 4 + 1; + } else if(bson->codeString->StrictEquals(constructorString)) { + // Unpack the object and encode + Local obj = value->ToObject(); + // Get the function + Local function = obj->Get(String::New("code"))->ToString(); + // Get the scope object + Local scope = obj->Get(String::New("scope"))->ToObject(); + + // For Node < 0.6.X use the GetPropertyNames + #if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION < 6 + uint32_t propertyNameLength = scope->GetPropertyNames()->Length(); + #else + uint32_t propertyNameLength = scope->GetOwnPropertyNames()->Length(); + #endif + + // Check if the scope has any parameters + // Let's calculate the size the code object adds adds + if(propertyNameLength > 0) { + object_size += function->Utf8Length() + 4 + BSON::calculate_object_size(bson, scope, serializeFunctions) + 4 + 1; + } else { + object_size += function->Utf8Length() + 4 + 1; + } + } else if(bson->dbrefString->StrictEquals(constructorString)) { + // Unpack the dbref + Local dbref = value->ToObject(); + // Create an object containing the right namespace variables + Local obj = Object::New(); + // Build the new object + obj->Set(bson->_dbRefRefString, dbref->Get(bson->_dbRefNamespaceString)); + obj->Set(bson->_dbRefIdRefString, dbref->Get(bson->_dbRefOidString)); + if(!dbref->Get(bson->_dbRefDbString)->IsNull() && !dbref->Get(bson->_dbRefDbString)->IsUndefined()) obj->Set(bson->_dbRefDbRefString, dbref->Get(bson->_dbRefDbString)); + // Calculate size + object_size += BSON::calculate_object_size(bson, obj, serializeFunctions); + } else if(bson->minKeyString->StrictEquals(constructorString) || bson->maxKeyString->Equals(constructorString)) { + } else if(bson->symbolString->StrictEquals(constructorString)) { + // Get string + Local str = value->ToObject()->Get(String::New("value"))->ToString(); + // Get the utf8 length + int utf8_length = str->Utf8Length(); + // Check if we have a utf8 encoded string or not + if(utf8_length != str->Length()) { + // Let's calculate the size the string adds, length + type(1 byte) + size(4 bytes) + object_size += str->Utf8Length() + 1 + 4; + } else { + object_size += str->Length() + 1 + 4; + } + } else if(bson->doubleString->StrictEquals(constructorString)) { + object_size = object_size + 8; + } + } else if(value->IsObject()) { + // Unwrap the object + Local object = value->ToObject(); + + #if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION < 6 + Local property_names = object->GetPropertyNames(); + #else + Local property_names = object->GetOwnPropertyNames(); + #endif + + // Length of the property + uint32_t propertyLength = property_names->Length(); + + // Process all the properties on the object + for(uint32_t index = 0; index < propertyLength; index++) { + // Fetch the property name + Local property_name = property_names->Get(index)->ToString(); + + // Fetch the object for the property + Local property = object->Get(property_name); + // Get size of property (property + property name length + 1 for terminating 0) + if(!property->IsFunction() || (property->IsFunction() && serializeFunctions)) { + // Convert name to char* + object_size += BSON::calculate_object_size(bson, property, serializeFunctions) + property_name->Utf8Length() + 1 + 1; + } + } + + object_size = object_size + 4 + 1; + } + + return object_size; +} + +uint32_t BSON::serialize(BSON *bson, char *serialized_object, uint32_t index, Handle name, Handle value, bool check_key, bool serializeFunctions) { + // Scope for method execution + HandleScope scope; + + // If we have a name check that key is valid + if(!name->IsNull() && check_key) { + if(BSON::check_key(name->ToString()) != NULL) return -1; + } + + // If we have an object let's serialize it + if(value->IsString()) { + // Save the string at the offset provided + *(serialized_object + index) = BSON_DATA_STRING; + // Adjust writing position for the first byte + index = index + 1; + // Convert name to char* + ssize_t len = DecodeBytes(name, UTF8); + ssize_t written = DecodeWrite((serialized_object + index), len, name, UTF8); + assert(written == len); + // Add null termiation for the string + *(serialized_object + index + len) = '\0'; + // Adjust the index + index = index + len + 1; + + // Write the actual string into the char array + Local str = value->ToString(); + // Let's fetch the int value + uint32_t utf8_length = str->Utf8Length(); + + // Write the integer to the char * + BSON::write_int32((serialized_object + index), utf8_length + 1); + // Adjust the index + index = index + 4; + // Write string to char in utf8 format + str->WriteUtf8((serialized_object + index), utf8_length); + // Add the null termination + *(serialized_object + index + utf8_length) = '\0'; + // Adjust the index + index = index + utf8_length + 1; + } else if(value->IsNumber()) { + uint32_t first_pointer = index; + // Save the string at the offset provided + *(serialized_object + index) = BSON_DATA_INT; + // Adjust writing position for the first byte + index = index + 1; + // Convert name to char* + ssize_t len = DecodeBytes(name, UTF8); + ssize_t written = DecodeWrite((serialized_object + index), len, name, UTF8); + assert(written == len); + // Add null termiation for the string + *(serialized_object + index + len) = '\0'; + // Adjust the index + index = index + len + 1; + + Local number = value->ToNumber(); + // Get the values + double d_number = number->NumberValue(); + int64_t l_number = number->IntegerValue(); + + // Check if we have a double value and not a int64 + double d_result = d_number - l_number; + // If we have a value after subtracting the integer value we have a float + if(d_result > 0 || d_result < 0) { + // Write the double to the char array + BSON::write_double((serialized_object + index), d_number); + // Adjust type to be double + *(serialized_object + first_pointer) = BSON_DATA_NUMBER; + // Adjust index for double + index = index + 8; + } else if(l_number <= BSON_INT32_MAX && l_number >= BSON_INT32_MIN) { + // Smaller than 32 bit, write as 32 bit value + BSON::write_int32(serialized_object + index, value->ToInt32()->Value()); + // Adjust the size of the index + index = index + 4; + } else if(l_number <= JS_INT_MAX && l_number >= JS_INT_MIN) { + // Write the double to the char array + BSON::write_double((serialized_object + index), d_number); + // Adjust type to be double + *(serialized_object + first_pointer) = BSON_DATA_NUMBER; + // Adjust index for double + index = index + 8; + } else { + BSON::write_double((serialized_object + index), d_number); + // Adjust type to be double + *(serialized_object + first_pointer) = BSON_DATA_NUMBER; + // Adjust the size of the index + index = index + 8; + } + } else if(value->IsBoolean()) { + // Save the string at the offset provided + *(serialized_object + index) = BSON_DATA_BOOLEAN; + // Adjust writing position for the first byte + index = index + 1; + // Convert name to char* + ssize_t len = DecodeBytes(name, UTF8); + ssize_t written = DecodeWrite((serialized_object + index), len, name, UTF8); + assert(written == len); + // Add null termiation for the string + *(serialized_object + index + len) = '\0'; + // Adjust the index + index = index + len + 1; + + // Save the boolean value + *(serialized_object + index) = value->BooleanValue() ? '\1' : '\0'; + // Adjust the index + index = index + 1; + } else if(value->IsDate()) { + // Save the string at the offset provided + *(serialized_object + index) = BSON_DATA_DATE; + // Adjust writing position for the first byte + index = index + 1; + // Convert name to char* + ssize_t len = DecodeBytes(name, UTF8); + ssize_t written = DecodeWrite((serialized_object + index), len, name, UTF8); + assert(written == len); + // Add null termiation for the string + *(serialized_object + index + len) = '\0'; + // Adjust the index + index = index + len + 1; + + // Fetch the Integer value + int64_t integer_value = value->IntegerValue(); + BSON::write_int64((serialized_object + index), integer_value); + // Adjust the index + index = index + 8; + } else if(value->IsNull() || value->IsUndefined()) { + // Save the string at the offset provided + *(serialized_object + index) = BSON_DATA_NULL; + // Adjust writing position for the first byte + index = index + 1; + // Convert name to char* + ssize_t len = DecodeBytes(name, UTF8); + ssize_t written = DecodeWrite((serialized_object + index), len, name, UTF8); + assert(written == len); + // Add null termiation for the string + *(serialized_object + index + len) = '\0'; + // Adjust the index + index = index + len + 1; + } else if(value->IsArray()) { + // Cast to array + Local array = Local::Cast(value->ToObject()); + // Turn length into string to calculate the size of all the strings needed + char *length_str = (char *)malloc(256 * sizeof(char)); + // Save the string at the offset provided + *(serialized_object + index) = BSON_DATA_ARRAY; + // Adjust writing position for the first byte + index = index + 1; + // Convert name to char* + ssize_t len = DecodeBytes(name, UTF8); + ssize_t written = DecodeWrite((serialized_object + index), len, name, UTF8); + assert(written == len); + // Add null termiation for the string + *(serialized_object + index + len) = '\0'; + // Adjust the index + index = index + len + 1; + // Object size + uint32_t object_size = BSON::calculate_object_size(bson, value, serializeFunctions); + // Write the size of the object + BSON::write_int32((serialized_object + index), object_size); + // Adjust the index + index = index + 4; + // Write out all the elements + for(uint32_t i = 0; i < array->Length(); i++) { + // Add "index" string size for each element + sprintf(length_str, "%d", i); + // Encode the values + index = BSON::serialize(bson, serialized_object, index, String::New(length_str), array->Get(Integer::New(i)), check_key, serializeFunctions); + // Write trailing '\0' for object + *(serialized_object + index) = '\0'; + } + + // Pad the last item + *(serialized_object + index) = '\0'; + index = index + 1; + // Free up memory + free(length_str); + } else if(value->IsRegExp()) { + // Save the string at the offset provided + *(serialized_object + index) = BSON_DATA_REGEXP; + // Adjust writing position for the first byte + index = index + 1; + // Convert name to char* + ssize_t len = DecodeBytes(name, UTF8); + ssize_t written = DecodeWrite((serialized_object + index), len, name, UTF8); + // Add null termiation for the string + *(serialized_object + index + len) = '\0'; + // Adjust the index + index = index + len + 1; + + // Fetch the string for the regexp + Handle regExp = Handle::Cast(value); + len = DecodeBytes(regExp->GetSource(), UTF8); + written = DecodeWrite((serialized_object + index), len, regExp->GetSource(), UTF8); + int flags = regExp->GetFlags(); + // Add null termiation for the string + *(serialized_object + index + len) = '\0'; + // Adjust the index + index = index + len + 1; + + // global + if((flags & (1 << 0)) != 0) { + *(serialized_object + index) = 's'; + index = index + 1; + } + + // ignorecase + if((flags & (1 << 1)) != 0) { + *(serialized_object + index) = 'i'; + index = index + 1; + } + + //multiline + if((flags & (1 << 2)) != 0) { + *(serialized_object + index) = 'm'; + index = index + 1; + } + + // Add null termiation for the string + *(serialized_object + index) = '\0'; + // Adjust the index + index = index + 1; + } else if(value->IsFunction()) { + if(serializeFunctions) { + // Save the string at the offset provided + *(serialized_object + index) = BSON_DATA_CODE; + + // Adjust writing position for the first byte + index = index + 1; + // Convert name to char* + ssize_t len = DecodeBytes(name, UTF8); + ssize_t written = DecodeWrite((serialized_object + index), len, name, UTF8); + // Add null termiation for the string + *(serialized_object + index + len) = '\0'; + // Adjust the index + index = index + len + 1; + + // Function String + Local function = value->ToString(); + + // Decode the function + len = DecodeBytes(function, BINARY); + // Write the size of the code string + 0 byte end of cString + BSON::write_int32((serialized_object + index), len + 1); + // Adjust the index + index = index + 4; + + // Write the data into the serialization stream + written = DecodeWrite((serialized_object + index), len, function, BINARY); + // Write \0 for string + *(serialized_object + index + len) = 0x00; + // Adjust the index + index = index + len + 1; + } + } else if(value->ToObject()->Has(bson->_bsontypeString)) { + // Handle holder + Local constructorString = value->ToObject()->GetConstructorName(); + uint32_t originalIndex = index; + // Adjust writing position for the first byte + index = index + 1; + // Convert name to char* + ssize_t len = DecodeBytes(name, UTF8); + ssize_t written = DecodeWrite((serialized_object + index), len, name, UTF8); + // Add null termiation for the string + *(serialized_object + index + len) = 0x00; + // Adjust the index + index = index + len + 1; + + // BSON type object, avoid non-needed checking unless we have a type + if(bson->longString->StrictEquals(constructorString)) { + // Save the string at the offset provided + *(serialized_object + originalIndex) = BSON_DATA_LONG; + // Object reference + Local longObject = value->ToObject(); + + // Fetch the low and high bits + int32_t lowBits = longObject->Get(bson->_longLowString)->ToInt32()->Value(); + int32_t highBits = longObject->Get(bson->_longHighString)->ToInt32()->Value(); + + // Write the content to the char array + BSON::write_int32((serialized_object + index), lowBits); + BSON::write_int32((serialized_object + index + 4), highBits); + // Adjust the index + index = index + 8; + } else if(bson->timestampString->StrictEquals(constructorString)) { + // Save the string at the offset provided + *(serialized_object + originalIndex) = BSON_DATA_TIMESTAMP; + // Object reference + Local timestampObject = value->ToObject(); + + // Fetch the low and high bits + int32_t lowBits = timestampObject->Get(bson->_longLowString)->ToInt32()->Value(); + int32_t highBits = timestampObject->Get(bson->_longHighString)->ToInt32()->Value(); + + // Write the content to the char array + BSON::write_int32((serialized_object + index), lowBits); + BSON::write_int32((serialized_object + index + 4), highBits); + // Adjust the index + index = index + 8; + } else if(bson->objectIDString->StrictEquals(constructorString)) { + // Save the string at the offset provided + *(serialized_object + originalIndex) = BSON_DATA_OID; + // Convert to object + Local objectIDObject = value->ToObject(); + // Let's grab the id + Local idString = objectIDObject->Get(bson->_objectIDidString)->ToString(); + // Let's decode the raw chars from the string + len = DecodeBytes(idString, BINARY); + written = DecodeWrite((serialized_object + index), len, idString, BINARY); + // Adjust the index + index = index + 12; + } else if(bson->binaryString->StrictEquals(constructorString)) { + // Save the string at the offset provided + *(serialized_object + originalIndex) = BSON_DATA_BINARY; + + // Let's get the binary object + Local binaryObject = value->ToObject(); + + // Grab the size(position of the binary) + uint32_t position = value->ToObject()->Get(bson->_binaryPositionString)->ToUint32()->Value(); + // Grab the subtype + uint32_t subType = value->ToObject()->Get(bson->_binarySubTypeString)->ToUint32()->Value(); + // Grab the buffer object + Local bufferObj = value->ToObject()->Get(bson->_binaryBufferString)->ToObject(); + + // Buffer data pointers + char *data; + uint32_t length; + + // Unpack the buffer variable + #if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION < 3 + Buffer *buffer = ObjectWrap::Unwrap(bufferObj); + data = buffer->data(); + length = buffer->length(); + #else + data = Buffer::Data(bufferObj); + length = Buffer::Length(bufferObj); + #endif + + // Write the size of the buffer out + BSON::write_int32((serialized_object + index), position); + // Adjust index + index = index + 4; + // Write subtype + *(serialized_object + index) = (char)subType; + // Adjust index + index = index + 1; + // Write binary content + memcpy((serialized_object + index), data, position); + // Adjust index.rar">_ + index = index + position; + } else if(bson->doubleString->StrictEquals(constructorString)) { + // Save the string at the offset provided + *(serialized_object + originalIndex) = BSON_DATA_NUMBER; + + // Unpack the double + Local doubleObject = value->ToObject(); + + // Fetch the double value + Local doubleValue = doubleObject->Get(bson->_doubleValueString)->ToNumber(); + // Write the double to the char array + BSON::write_double((serialized_object + index), doubleValue->NumberValue()); + // Adjust index for double + index = index + 8; + } else if(bson->symbolString->StrictEquals(constructorString)) { + // Save the string at the offset provided + *(serialized_object + originalIndex) = BSON_DATA_SYMBOL; + // Unpack symbol object + Local symbolObj = value->ToObject(); + + // Grab the actual string + Local str = symbolObj->Get(bson->_symbolValueString)->ToString(); + // Let's fetch the int value + int utf8_length = str->Utf8Length(); + + // If the Utf8 length is different from the string length then we + // have a UTF8 encoded string, otherwise write it as ascii + if(utf8_length != str->Length()) { + // Write the integer to the char * + BSON::write_int32((serialized_object + index), utf8_length + 1); + // Adjust the index + index = index + 4; + // Write string to char in utf8 format + str->WriteUtf8((serialized_object + index), utf8_length); + // Add the null termination + *(serialized_object + index + utf8_length) = '\0'; + // Adjust the index + index = index + utf8_length + 1; + } else { + // Write the integer to the char * + BSON::write_int32((serialized_object + index), str->Length() + 1); + // Adjust the index + index = index + 4; + // Write string to char in utf8 format + written = DecodeWrite((serialized_object + index), str->Length(), str, BINARY); + // Add the null termination + *(serialized_object + index + str->Length()) = '\0'; + // Adjust the index + index = index + str->Length() + 1; + } + } else if(bson->codeString->StrictEquals(constructorString)) { + // Unpack the object and encode + Local obj = value->ToObject(); + // Get the function + Local function = obj->Get(String::New("code"))->ToString(); + // Get the scope object + Local scope = obj->Get(String::New("scope"))->ToObject(); + + #if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION < 6 + uint32_t propertyNameLength = scope->GetPropertyNames()->Length(); + #else + uint32_t propertyNameLength = scope->GetOwnPropertyNames()->Length(); + #endif + + // Set the right type if we have a scope or not + if(propertyNameLength > 0) { + // Set basic data code object with scope object + *(serialized_object + originalIndex) = BSON_DATA_CODE_W_SCOPE; + + // Calculate the size of the whole object + uint32_t scopeSize = BSON::calculate_object_size(bson, scope, false); + // Decode the function length + ssize_t len = DecodeBytes(function, UTF8); + // Calculate total size + uint32_t size = 4 + len + 1 + 4 + scopeSize; + + // Write the total size + BSON::write_int32((serialized_object + index), size); + // Adjust the index + index = index + 4; + + // Write the function size + BSON::write_int32((serialized_object + index), len + 1); + // Adjust the index + index = index + 4; + + // Write the data into the serialization stream + ssize_t written = DecodeWrite((serialized_object + index), len, function, UTF8); + assert(written == len); + // Write \0 for string + *(serialized_object + index + len) = 0x00; + // Adjust the index with the length of the function + index = index + len + 1; + // Write the scope object + BSON::serialize(bson, (serialized_object + index), 0, Null(), scope, check_key, serializeFunctions); + // Adjust the index + index = index + scopeSize; + } else { + // Set basic data code object + *(serialized_object + originalIndex) = BSON_DATA_CODE; + // Decode the function + ssize_t len = DecodeBytes(function, BINARY); + // Write the size of the code string + 0 byte end of cString + BSON::write_int32((serialized_object + index), len + 1); + // Adjust the index + index = index + 4; + + // Write the data into the serialization stream + ssize_t written = DecodeWrite((serialized_object + index), len, function, BINARY); + assert(written == len); + // Write \0 for string + *(serialized_object + index + len) = 0x00; + // Adjust the index + index = index + len + 1; + } + } else if(bson->dbrefString->StrictEquals(constructorString)) { + // Unpack the dbref + Local dbref = value->ToObject(); + // Create an object containing the right namespace variables + Local obj = Object::New(); + + // Build the new object + obj->Set(bson->_dbRefRefString, dbref->Get(bson->_dbRefNamespaceString)); + obj->Set(bson->_dbRefIdRefString, dbref->Get(bson->_dbRefOidString)); + if(!dbref->Get(bson->_dbRefDbString)->IsNull() && !dbref->Get(bson->_dbRefDbString)->IsUndefined()) obj->Set(bson->_dbRefDbRefString, dbref->Get(bson->_dbRefDbString)); + + // Encode the variable + index = BSON::serialize(bson, serialized_object, originalIndex, name, obj, false, serializeFunctions); + } else if(bson->minKeyString->StrictEquals(constructorString)) { + // Save the string at the offset provided + *(serialized_object + originalIndex) = BSON_DATA_MIN_KEY; + } else if(bson->maxKeyString->StrictEquals(constructorString)) { + *(serialized_object + originalIndex) = BSON_DATA_MAX_KEY; + } + } else if(value->IsObject()) { + if(!name->IsNull()) { + // Save the string at the offset provided + *(serialized_object + index) = BSON_DATA_OBJECT; + // Adjust writing position for the first byte + index = index + 1; + // Convert name to char* + ssize_t len = DecodeBytes(name, UTF8); + ssize_t written = DecodeWrite((serialized_object + index), len, name, UTF8); + assert(written == len); + // Add null termiation for the string + *(serialized_object + index + len) = '\0'; + // Adjust the index + index = index + len + 1; + } + + // Unwrap the object + Local object = value->ToObject(); + + #if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION < 6 + Local property_names = object->GetPropertyNames(); + #else + Local property_names = object->GetOwnPropertyNames(); + #endif + + // Calculate size of the total object + uint32_t object_size = BSON::calculate_object_size(bson, value, serializeFunctions); + // Write the size + BSON::write_int32((serialized_object + index), object_size); + // Adjust size + index = index + 4; + + // Process all the properties on the object + for(uint32_t i = 0; i < property_names->Length(); i++) { + // Fetch the property name + Local property_name = property_names->Get(i)->ToString(); + // Fetch the object for the property + Local property = object->Get(property_name); + // Write the next serialized object + // printf("========== !property->IsFunction() || (property->IsFunction() && serializeFunctions) = %d\n", !property->IsFunction() || (property->IsFunction() && serializeFunctions) == true ? 1 : 0); + if(!property->IsFunction() || (property->IsFunction() && serializeFunctions)) { + // Convert name to char* + ssize_t len = DecodeBytes(property_name, UTF8); + // char *data = new char[len]; + char *data = (char *)malloc(len + 1); + *(data + len) = '\0'; + ssize_t written = DecodeWrite(data, len, property_name, UTF8); + assert(written == len); + // Serialize the content + index = BSON::serialize(bson, serialized_object, index, property_name, property, check_key, serializeFunctions); + // Free up memory of data + free(data); + } + } + // Pad the last item + *(serialized_object + index) = '\0'; + index = index + 1; + + // Null out reminding fields if we have a toplevel object and nested levels + if(name->IsNull()) { + for(uint32_t i = 0; i < (object_size - index); i++) { + *(serialized_object + index + i) = '\0'; + } + } + } + + return index; +} + +Handle BSON::SerializeWithBufferAndIndex(const Arguments &args) { + HandleScope scope; + + //BSON.serializeWithBufferAndIndex = function serializeWithBufferAndIndex(object, checkKeys, buffer, index) { + // Ensure we have the correct values + if(args.Length() > 5) return VException("Four or five parameters required [object, boolean, Buffer, int] or [object, boolean, Buffer, int, boolean]"); + if(args.Length() == 4 && !args[0]->IsObject() && !args[1]->IsBoolean() && !Buffer::HasInstance(args[2]) && !args[3]->IsUint32()) return VException("Four parameters required [object, boolean, Buffer, int]"); + if(args.Length() == 5 && !args[0]->IsObject() && !args[1]->IsBoolean() && !Buffer::HasInstance(args[2]) && !args[3]->IsUint32() && !args[4]->IsBoolean()) return VException("Four parameters required [object, boolean, Buffer, int, boolean]"); + + // Unpack the BSON parser instance + BSON *bson = ObjectWrap::Unwrap(args.This()); + + // Define pointer to data + char *data; + uint32_t length; + // Unpack the object + Local obj = args[2]->ToObject(); + + // Unpack the buffer object and get pointers to structures + #if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION < 3 + Buffer *buffer = ObjectWrap::Unwrap(obj); + data = buffer->data(); + length = buffer->length(); + #else + data = Buffer::Data(obj); + length = Buffer::Length(obj); + #endif + + uint32_t object_size = 0; + // Calculate the total size of the document in binary form to ensure we only allocate memory once + if(args.Length() == 5) { + object_size = BSON::calculate_object_size(bson, args[0], args[4]->BooleanValue()); + } else { + object_size = BSON::calculate_object_size(bson, args[0], false); + } + + // Unpack the index variable + Local indexObject = args[3]->ToUint32(); + uint32_t index = indexObject->Value(); + + // Allocate the memory needed for the serializtion + char *serialized_object = (char *)malloc(object_size * sizeof(char)); + + // Catch any errors + try { + // Check if we have a boolean value + bool check_key = false; + if(args.Length() >= 4 && args[1]->IsBoolean()) { + check_key = args[1]->BooleanValue(); + } + + bool serializeFunctions = false; + if(args.Length() == 5) { + serializeFunctions = args[4]->BooleanValue(); + } + + // Serialize the object + BSON::serialize(bson, serialized_object, 0, Null(), args[0], check_key, serializeFunctions); + } catch(char *err_msg) { + // Free up serialized object space + free(serialized_object); + V8::AdjustAmountOfExternalAllocatedMemory(-object_size); + // Throw exception with the string + Handle error = VException(err_msg); + // free error message + free(err_msg); + // Return error + return error; + } + + for(uint32_t i = 0; i < object_size; i++) { + *(data + index + i) = *(serialized_object + i); + } + + return scope.Close(Uint32::New(index + object_size - 1)); +} + +Handle BSON::BSONDeserializeStream(const Arguments &args) { + HandleScope scope; + + // At least 3 arguments required + if(args.Length() < 5) VException("Arguments required (Buffer(data), Number(index in data), Number(number of documents to deserialize), Array(results), Number(index in the array), Object(optional))"); + + // If the number of argumets equals 3 + if(args.Length() >= 5) { + if(!Buffer::HasInstance(args[0])) return VException("First argument must be Buffer instance"); + if(!args[1]->IsUint32()) return VException("Second argument must be a positive index number"); + if(!args[2]->IsUint32()) return VException("Third argument must be a positive number of documents to deserialize"); + if(!args[3]->IsArray()) return VException("Fourth argument must be an array the size of documents to deserialize"); + if(!args[4]->IsUint32()) return VException("Sixth argument must be a positive index number"); + } + + // If we have 4 arguments + if(args.Length() == 6 && !args[5]->IsObject()) return VException("Fifth argument must be an object with options"); + + // Define pointer to data + char *data; + uint32_t length; + Local obj = args[0]->ToObject(); + uint32_t numberOfDocuments = args[2]->ToUint32()->Value(); + uint32_t index = args[1]->ToUint32()->Value(); + uint32_t resultIndex = args[4]->ToUint32()->Value(); + + // Unpack the BSON parser instance + BSON *bson = ObjectWrap::Unwrap(args.This()); + + // Unpack the buffer variable + #if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION < 3 + Buffer *buffer = ObjectWrap::Unwrap(obj); + data = buffer->data(); + length = buffer->length(); + #else + data = Buffer::Data(obj); + length = Buffer::Length(obj); + #endif + + // Fetch the documents + Local documents = args[3]->ToObject(); + + for(uint32_t i = 0; i < numberOfDocuments; i++) { + // Decode the size of the BSON data structure + uint32_t size = BSON::deserialize_int32(data, index); + + // Get result + Handle result = BSON::deserialize(bson, data, size, index, NULL); + + // Add result to array + documents->Set(i + resultIndex, result); + + // Adjust the index for next pass + index = index + size; + } + + // Return new index of parsing + return scope.Close(Uint32::New(index)); +} + +// Exporting function +extern "C" void init(Handle target) { + HandleScope scope; + BSON::Initialize(target); +} + +// NODE_MODULE(bson, BSON::Initialize); +// NODE_MODULE(l, Long::Initialize); diff --git a/node_modules/mongodb/external-libs/bson/bson.h b/node_modules/mongodb/external-libs/bson/bson.h new file mode 100644 index 0000000..dcf21d1 --- /dev/null +++ b/node_modules/mongodb/external-libs/bson/bson.h @@ -0,0 +1,105 @@ +#ifndef BSON_H_ +#define BSON_H_ + +#include +#include +#include + +using namespace v8; +using namespace node; + +class BSON : public ObjectWrap { + public: + BSON() : ObjectWrap() {} + ~BSON() {} + + static void Initialize(Handle target); + static Handle BSONDeserializeStream(const Arguments &args); + + // JS based objects + static Handle BSONSerialize(const Arguments &args); + static Handle BSONDeserialize(const Arguments &args); + + // Calculate size of function + static Handle CalculateObjectSize(const Arguments &args); + static Handle SerializeWithBufferAndIndex(const Arguments &args); + + // Experimental + static Handle CalculateObjectSize2(const Arguments &args); + static Handle BSONSerialize2(const Arguments &args); + + // Constructor used for creating new BSON objects from C++ + static Persistent constructor_template; + + private: + static Handle New(const Arguments &args); + static Handle deserialize(BSON *bson, char *data, uint32_t dataLength, uint32_t startIndex, bool is_array_item); + static uint32_t serialize(BSON *bson, char *serialized_object, uint32_t index, Handle name, Handle value, bool check_key, bool serializeFunctions); + + static char* extract_string(char *data, uint32_t offset); + static const char* ToCString(const v8::String::Utf8Value& value); + static uint32_t calculate_object_size(BSON *bson, Handle object, bool serializeFunctions); + + static void write_int32(char *data, uint32_t value); + static void write_int64(char *data, int64_t value); + static void write_double(char *data, double value); + static uint16_t deserialize_int8(char *data, uint32_t offset); + static uint32_t deserialize_int32(char* data, uint32_t offset); + static char *check_key(Local key); + + // BSON type instantiate functions + Persistent longConstructor; + Persistent objectIDConstructor; + Persistent binaryConstructor; + Persistent codeConstructor; + Persistent dbrefConstructor; + Persistent symbolConstructor; + Persistent doubleConstructor; + Persistent timestampConstructor; + Persistent minKeyConstructor; + Persistent maxKeyConstructor; + + // Equality Objects + Persistent longString; + Persistent objectIDString; + Persistent binaryString; + Persistent codeString; + Persistent dbrefString; + Persistent symbolString; + Persistent doubleString; + Persistent timestampString; + Persistent minKeyString; + Persistent maxKeyString; + + // Equality speed up comparision objects + Persistent _bsontypeString; + Persistent _longLowString; + Persistent _longHighString; + Persistent _objectIDidString; + Persistent _binaryPositionString; + Persistent _binarySubTypeString; + Persistent _binaryBufferString; + Persistent _doubleValueString; + Persistent _symbolValueString; + + Persistent _dbRefRefString; + Persistent _dbRefIdRefString; + Persistent _dbRefDbRefString; + Persistent _dbRefNamespaceString; + Persistent _dbRefDbString; + Persistent _dbRefOidString; + + // Decode JS function + static Handle decodeLong(BSON *bson, char *data, uint32_t index); + static Handle decodeTimestamp(BSON *bson, char *data, uint32_t index); + static Handle decodeOid(BSON *bson, char *oid); + static Handle decodeBinary(BSON *bson, uint32_t sub_type, uint32_t number_of_bytes, char *data); + static Handle decodeCode(BSON *bson, char *code, Handle scope); + static Handle decodeDBref(BSON *bson, Local ref, Local oid, Local db); + + // Experimental + static uint32_t calculate_object_size2(Handle object); + static uint32_t serialize2(char *serialized_object, uint32_t index, Handle name, Handle value, uint32_t object_size, bool check_key); +}; + +#endif // BSON_H_ diff --git a/node_modules/mongodb/external-libs/bson/index.js b/node_modules/mongodb/external-libs/bson/index.js new file mode 100644 index 0000000..2c66dee --- /dev/null +++ b/node_modules/mongodb/external-libs/bson/index.js @@ -0,0 +1,20 @@ +var bson = require('./bson'); +exports.BSON = bson.BSON; +exports.Long = require('../../lib/mongodb/bson/long').Long; +exports.ObjectID = require('../../lib/mongodb/bson/objectid').ObjectID; +exports.DBRef = require('../../lib/mongodb/bson/db_ref').DBRef; +exports.Code = require('../../lib/mongodb/bson/code').Code; +exports.Timestamp = require('../../lib/mongodb/bson/timestamp').Timestamp; +exports.Binary = require('../../lib/mongodb/bson/binary').Binary; +exports.Double = require('../../lib/mongodb/bson/double').Double; +exports.MaxKey = require('../../lib/mongodb/bson/max_key').MaxKey; +exports.MinKey = require('../../lib/mongodb/bson/min_key').MinKey; +exports.Symbol = require('../../lib/mongodb/bson/symbol').Symbol; + +// Just add constants tot he Native BSON parser +exports.BSON.BSON_BINARY_SUBTYPE_DEFAULT = 0; +exports.BSON.BSON_BINARY_SUBTYPE_FUNCTION = 1; +exports.BSON.BSON_BINARY_SUBTYPE_BYTE_ARRAY = 2; +exports.BSON.BSON_BINARY_SUBTYPE_UUID = 3; +exports.BSON.BSON_BINARY_SUBTYPE_MD5 = 4; +exports.BSON.BSON_BINARY_SUBTYPE_USER_DEFINED = 128; diff --git a/node_modules/mongodb/external-libs/bson/test/test_bson.js b/node_modules/mongodb/external-libs/bson/test/test_bson.js new file mode 100644 index 0000000..706f1df --- /dev/null +++ b/node_modules/mongodb/external-libs/bson/test/test_bson.js @@ -0,0 +1,349 @@ +var sys = require('util'), + debug = require('util').debug, + inspect = require('util').inspect, + Buffer = require('buffer').Buffer, + BSON = require('../bson').BSON, + Buffer = require('buffer').Buffer, + BSONJS = require('../../../lib/mongodb/bson/bson').BSON, + BinaryParser = require('../../../lib/mongodb/bson/binary_parser').BinaryParser, + Long = require('../../../lib/mongodb/bson/long').Long, + ObjectID = require('../../../lib/mongodb/bson/bson').ObjectID, + Binary = require('../../../lib/mongodb/bson/bson').Binary, + Code = require('../../../lib/mongodb/bson/bson').Code, + DBRef = require('../../../lib/mongodb/bson/bson').DBRef, + Symbol = require('../../../lib/mongodb/bson/bson').Symbol, + Double = require('../../../lib/mongodb/bson/bson').Double, + MaxKey = require('../../../lib/mongodb/bson/bson').MaxKey, + MinKey = require('../../../lib/mongodb/bson/bson').MinKey, + Timestamp = require('../../../lib/mongodb/bson/bson').Timestamp, + assert = require('assert'); + +if(process.env['npm_package_config_native'] != null) return; + +sys.puts("=== EXECUTING TEST_BSON ==="); + +// Should fail due to illegal key +assert.throws(function() { new ObjectID('foo'); }) +assert.throws(function() { new ObjectID('foo'); }) + +// Parsers +var bsonC = new BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]); +var bsonJS = new BSONJS([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]); + +// Simple serialization and deserialization of edge value +var doc = {doc:0x1ffffffffffffe}; +var simple_string_serialized = bsonC.serialize(doc, false, true); +assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); +assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + +var doc = {doc:-0x1ffffffffffffe}; +var simple_string_serialized = bsonC.serialize(doc, false, true); +assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); +assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + +// +// Assert correct toJSON +// +var a = Long.fromNumber(10); +assert.equal(10, a); + +var a = Long.fromNumber(9223372036854775807); +assert.equal(9223372036854775807, a); + +// Simple serialization and deserialization test for a Single String value +var doc = {doc:'Serialize'}; +var simple_string_serialized = bsonC.serialize(doc, true, false); + +assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); +assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + +// Nested doc +var doc = {a:{b:{c:1}}}; +var simple_string_serialized = bsonC.serialize(doc, false, true); + +assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); +assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + +// Simple integer serialization/deserialization test, including testing boundary conditions +var doc = {doc:-1}; +var simple_string_serialized = bsonC.serialize(doc, false, true); +assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); +assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + +var doc = {doc:2147483648}; +var simple_string_serialized = bsonC.serialize(doc, false, true); +assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + +var doc = {doc:-2147483648}; +var simple_string_serialized = bsonC.serialize(doc, false, true); +assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); +assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + +// Simple serialization and deserialization test for a Long value +var doc = {doc:Long.fromNumber(9223372036854775807)}; +var simple_string_serialized = bsonC.serialize(doc, false, true); +assert.deepEqual(simple_string_serialized, bsonJS.serialize({doc:Long.fromNumber(9223372036854775807)}, false, true)); +assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + +var doc = {doc:Long.fromNumber(-9223372036854775807)}; +var simple_string_serialized = bsonC.serialize(doc, false, true); +assert.deepEqual(simple_string_serialized, bsonJS.serialize({doc:Long.fromNumber(-9223372036854775807)}, false, true)); +assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + +// Simple serialization and deserialization for a Float value +var doc = {doc:2222.3333}; +var simple_string_serialized = bsonC.serialize(doc, false, true); +assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); +assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + +var doc = {doc:-2222.3333}; +var simple_string_serialized = bsonC.serialize(doc, false, true); +assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); +assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + +// Simple serialization and deserialization for a null value +var doc = {doc:null}; +var simple_string_serialized = bsonC.serialize(doc, false, true); +assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); +assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + +// Simple serialization and deserialization for a boolean value +var doc = {doc:true}; +var simple_string_serialized = bsonC.serialize(doc, false, true); +assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); +assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + +// Simple serialization and deserialization for a date value +var date = new Date(); +var doc = {doc:date}; +var simple_string_serialized = bsonC.serialize(doc, false, true); +assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); +assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + +// Simple serialization and deserialization for a boolean value +var doc = {doc:/abcd/mi}; +var simple_string_serialized = bsonC.serialize(doc, false, true); +assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); +assert.equal(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')).doc.toString(), bsonC.deserialize(simple_string_serialized).doc.toString()); + +var doc = {doc:/abcd/}; +var simple_string_serialized = bsonC.serialize(doc, false, true); +assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); +assert.equal(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')).doc.toString(), bsonC.deserialize(simple_string_serialized).doc.toString()); + +// Simple serialization and deserialization for a objectId value +var doc = {doc:new ObjectID()}; +var simple_string_serialized = bsonC.serialize(doc, false, true); +var doc2 = {doc:ObjectID.createFromHexString(doc.doc.toHexString())}; + +assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc2, false, true)); +assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')).doc.toString(), bsonC.deserialize(simple_string_serialized).doc.toString()); + +// Simple serialization and deserialization for a Binary value +var binary = new Binary(); +var string = 'binstring' +for(var index = 0; index < string.length; index++) { binary.put(string.charAt(index)); } + +var Binary = new Binary(); +var string = 'binstring' +for(var index = 0; index < string.length; index++) { Binary.put(string.charAt(index)); } + +var simple_string_serialized = bsonC.serialize({doc:binary}, false, true); +assert.deepEqual(simple_string_serialized, bsonJS.serialize({doc:Binary}, false, true)); +assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')).doc.value(), bsonC.deserialize(simple_string_serialized).doc.value()); + +// Simple serialization and deserialization for a Code value +var code = new Code('this.a > i', {'i': 1}); +var Code = new Code('this.a > i', {'i': 1}); +var simple_string_serialized_2 = bsonJS.serialize({doc:Code}, false, true); +var simple_string_serialized = bsonC.serialize({doc:code}, false, true); + +assert.deepEqual(simple_string_serialized, simple_string_serialized_2); +assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized_2, 'binary')).doc.scope, bsonC.deserialize(simple_string_serialized).doc.scope); + +// Simple serialization and deserialization for an Object +var simple_string_serialized = bsonC.serialize({doc:{a:1, b:{c:2}}}, false, true); +var simple_string_serialized_2 = bsonJS.serialize({doc:{a:1, b:{c:2}}}, false, true); +assert.deepEqual(simple_string_serialized, simple_string_serialized_2) +assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized_2, 'binary')).doc, bsonC.deserialize(simple_string_serialized).doc); + +// Simple serialization and deserialization for an Array +var simple_string_serialized = bsonC.serialize({doc:[9, 9, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1]}, false, true); +var simple_string_serialized_2 = bsonJS.serialize({doc:[9, 9, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1]}, false, true); + +assert.deepEqual(simple_string_serialized, simple_string_serialized_2) +assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized_2, 'binary')).doc, bsonC.deserialize(simple_string_serialized).doc); + +// Simple serialization and deserialization for a DBRef +var oid = new ObjectID() +var oid2 = new ObjectID.createFromHexString(oid.toHexString()) +var simple_string_serialized = bsonJS.serialize({doc:new DBRef('namespace', oid2, 'integration_tests_')}, false, true); +var simple_string_serialized_2 = bsonC.serialize({doc:new DBRef('namespace', oid, 'integration_tests_')}, false, true); + +assert.deepEqual(simple_string_serialized, simple_string_serialized_2) +// Ensure we have the same values for the dbref +var object_js = bsonJS.deserialize(new Buffer(simple_string_serialized_2, 'binary')); +var object_c = bsonC.deserialize(simple_string_serialized); + +assert.equal(object_js.doc.namespace, object_c.doc.namespace); +assert.equal(object_js.doc.oid.toHexString(), object_c.doc.oid.toHexString()); +assert.equal(object_js.doc.db, object_c.doc.db); + +// Serialized document +var bytes = [47,0,0,0,2,110,97,109,101,0,6,0,0,0,80,97,116,116,121,0,16,97,103,101,0,34,0,0,0,7,95,105,100,0,76,100,12,23,11,30,39,8,89,0,0,1,0]; +var serialized_data = ''; +// Convert to chars +for(var i = 0; i < bytes.length; i++) { + serialized_data = serialized_data + BinaryParser.fromByte(bytes[i]); +} +var object = bsonC.deserialize(new Buffer(serialized_data, 'binary')); +assert.equal('Patty', object.name) +assert.equal(34, object.age) +assert.equal('4c640c170b1e270859000001', object._id.toHexString()) + +// Serialize utf8 +var doc = { "name" : "本荘由利地域に洪水警報", "name1" : "öüóőúéáűíÖÜÓŐÚÉÁŰÍ", "name2" : "abcdedede"}; +var simple_string_serialized = bsonC.serialize(doc, false, true); +var simple_string_serialized2 = bsonJS.serialize(doc, false, true); +assert.deepEqual(simple_string_serialized, simple_string_serialized2) + +var object = bsonC.deserialize(simple_string_serialized); +assert.equal(doc.name, object.name) +assert.equal(doc.name1, object.name1) +assert.equal(doc.name2, object.name2) + +// Serialize object with array +var doc = {b:[1, 2, 3]}; +var simple_string_serialized = bsonC.serialize(doc, false, true); +var simple_string_serialized_2 = bsonJS.serialize(doc, false, true); +assert.deepEqual(simple_string_serialized, simple_string_serialized_2) + +var object = bsonC.deserialize(simple_string_serialized); +assert.deepEqual(doc, object) + +// Test equality of an object ID +var object_id = new ObjectID(); +var object_id_2 = new ObjectID(); +assert.ok(object_id.equals(object_id)); +assert.ok(!(object_id.equals(object_id_2))) + +// Test same serialization for Object ID +var object_id = new ObjectID(); +var object_id2 = ObjectID.createFromHexString(object_id.toString()) +var simple_string_serialized = bsonJS.serialize({doc:object_id}, false, true); +var simple_string_serialized_2 = bsonC.serialize({doc:object_id2}, false, true); + +assert.equal(simple_string_serialized_2.length, simple_string_serialized.length); +assert.deepEqual(simple_string_serialized, simple_string_serialized_2) +var object = bsonJS.deserialize(new Buffer(simple_string_serialized_2, 'binary')); +var object2 = bsonC.deserialize(simple_string_serialized); +assert.equal(object.doc.id, object2.doc.id) + +// JS Object +var c1 = { _id: new ObjectID, comments: [], title: 'number 1' }; +var c2 = { _id: new ObjectID, comments: [], title: 'number 2' }; +var doc = { + numbers: [] + , owners: [] + , comments: [c1, c2] + , _id: new ObjectID +}; + +var simple_string_serialized = bsonJS.serialize(doc, false, true); + +// C++ Object +var c1 = { _id: ObjectID.createFromHexString(c1._id.toHexString()), comments: [], title: 'number 1' }; +var c2 = { _id: ObjectID.createFromHexString(c2._id.toHexString()), comments: [], title: 'number 2' }; +var doc = { + numbers: [] + , owners: [] + , comments: [c1, c2] + , _id: ObjectID.createFromHexString(doc._id.toHexString()) +}; + +var simple_string_serialized_2 = bsonC.serialize(doc, false, true); + +for(var i = 0; i < simple_string_serialized_2.length; i++) { + // debug(i + "[" + simple_string_serialized_2[i] + "] = [" + simple_string_serialized[i] + "]") + assert.equal(simple_string_serialized_2[i], simple_string_serialized[i]); +} + +// Deserialize the string +var doc1 = bsonJS.deserialize(new Buffer(simple_string_serialized_2)); +var doc2 = bsonC.deserialize(new Buffer(simple_string_serialized_2)); +assert.equal(doc._id.id, doc1._id.id) +assert.equal(doc._id.id, doc2._id.id) +assert.equal(doc1._id.id, doc2._id.id) + +var doc = { + _id: 'testid', + key1: { code: 'test1', time: {start:1309323402727,end:1309323402727}, x:10, y:5 }, + key2: { code: 'test1', time: {start:1309323402727,end:1309323402727}, x:10, y:5 } +}; + +var simple_string_serialized = bsonJS.serialize(doc, false, true); +var simple_string_serialized_2 = bsonC.serialize(doc, false, true); + +// Deserialize the string +var doc1 = bsonJS.deserialize(new Buffer(simple_string_serialized_2)); +var doc2 = bsonC.deserialize(new Buffer(simple_string_serialized_2)); +assert.deepEqual(doc2, doc1) +assert.deepEqual(doc, doc2) +assert.deepEqual(doc, doc1) + +// Serialize function +var doc = { + _id: 'testid', + key1: function() {} +} + +var simple_string_serialized = bsonJS.serialize(doc, false, true, true); +var simple_string_serialized_2 = bsonC.serialize(doc, false, true, true); + +// Deserialize the string +var doc1 = bsonJS.deserialize(new Buffer(simple_string_serialized_2)); +var doc2 = bsonC.deserialize(new Buffer(simple_string_serialized_2)); +assert.equal(doc1.key1.code.toString(), doc2.key1.code.toString()) + +var doc = {"user_id":"4e9fc8d55883d90100000003","lc_status":{"$ne":"deleted"},"owner_rating":{"$exists":false}}; +var simple_string_serialized = bsonJS.serialize(doc, false, true, true); +var simple_string_serialized_2 = bsonC.serialize(doc, false, true, true); + +// Should serialize to the same value +assert.equal(simple_string_serialized_2.toString('base64'), simple_string_serialized.toString('base64')) +var doc1 = bsonJS.deserialize(simple_string_serialized_2); +var doc2 = bsonC.deserialize(simple_string_serialized); +assert.deepEqual(doc1, doc2) + +// Hex Id +var hexId = new ObjectID().toString(); +var docJS = {_id: ObjectID.createFromHexString(hexId), 'funds.remaining': {$gte: 1.222}, 'transactions.id': {$ne: ObjectID.createFromHexString(hexId)}}; +var docC = {_id: ObjectID.createFromHexString(hexId), 'funds.remaining': {$gte: 1.222}, 'transactions.id': {$ne: ObjectID.createFromHexString(hexId)}}; +var docJSBin = bsonJS.serialize(docJS, false, true, true); +var docCBin = bsonC.serialize(docC, false, true, true); +assert.equal(docCBin.toString('base64'), docJSBin.toString('base64')); + +// // Complex document serialization +// doc = {"DateTime": "Tue Nov 40 2011 17:27:55 GMT+0000 (WEST)","isActive": true,"Media": {"URL": "http://videos.sapo.pt/Tc85NsjaKjj8o5aV7Ubb"},"Title": "Lisboa fecha a ganhar 0.19%","SetPosition": 60,"Type": "videos","Thumbnail": [{"URL": "http://rd3.videos.sapo.pt/Tc85NsjaKjj8o5aV7Ubb/pic/320x240","Dimensions": {"Height": 240,"Width": 320}}],"Source": {"URL": "http://videos.sapo.pt","SetID": "1288","SourceID": "http://videos.sapo.pt/tvnet/rss2","SetURL": "http://noticias.sapo.pt/videos/tv-net_1288/","ItemID": "Tc85NsjaKjj8o5aV7Ubb","Name": "SAPO Vídeos"},"Category": "Tec_ciencia","Description": "Lisboa fecha a ganhar 0.19%","GalleryID": new ObjectID("4eea2a634ce8573200000000"),"InternalRefs": {"RegisterDate": "Thu Dec 15 2011 17:12:51 GMT+0000 (WEST)","ChangeDate": "Thu Dec 15 2011 17:12:51 GMT+0000 (WEST)","Hash": 332279244514},"_id": new ObjectID("4eea2a96e52778160000003a")} +// var docJSBin = bsonJS.serialize(docJS, false, true, true); +// var docCBin = bsonC.serialize(docC, false, true, true); +// +// + +// // Force garbage collect +// global.gc(); + + + + + + + + + + + + + + + diff --git a/node_modules/mongodb/external-libs/bson/test/test_full_bson.js b/node_modules/mongodb/external-libs/bson/test/test_full_bson.js new file mode 100644 index 0000000..2a6506c --- /dev/null +++ b/node_modules/mongodb/external-libs/bson/test/test_full_bson.js @@ -0,0 +1,218 @@ +var sys = require('util'), + fs = require('fs'), + Buffer = require('buffer').Buffer, + BSON = require('../bson').BSON, + Buffer = require('buffer').Buffer, + assert = require('assert'), + BinaryParser = require('../../../lib/mongodb/bson/binary_parser').BinaryParser, + BSONJS = require('../../../lib/mongodb/bson/bson').BSON, + Long = require('../../../lib/mongodb/bson/long').Long, + ObjectID = require('../../../lib/mongodb/bson/bson').ObjectID, + Binary = require('../../../lib/mongodb/bson/bson').Binary, + Code = require('../../../lib/mongodb/bson/bson').Code, + DBRef = require('../../../lib/mongodb/bson/bson').DBRef, + Symbol = require('../../../lib/mongodb/bson/bson').Symbol, + Double = require('../../../lib/mongodb/bson/bson').Double, + MaxKey = require('../../../lib/mongodb/bson/bson').MaxKey, + MinKey = require('../../../lib/mongodb/bson/bson').MinKey, + Timestamp = require('../../../lib/mongodb/bson/bson').Timestamp; + +if(process.env['npm_package_config_native'] != null) return; + +sys.puts("=== EXECUTING TEST_FULL_BSON ==="); + +// Parsers +var bsonC = new BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]); +var bsonJS = new BSONJS([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]); + +// Should Correctly Deserialize object +var bytes = [95,0,0,0,2,110,115,0,42,0,0,0,105,110,116,101,103,114,97,116,105,111,110,95,116,101,115,116,115,95,46,116,101,115,116,95,105,110,100,101,120,95,105,110,102,111,114,109,97,116,105,111,110,0,8,117,110,105,113,117,101,0,0,3,107,101,121,0,12,0,0,0,16,97,0,1,0,0,0,0,2,110,97,109,101,0,4,0,0,0,97,95,49,0,0]; +var serialized_data = ''; +// Convert to chars +for(var i = 0; i < bytes.length; i++) { + serialized_data = serialized_data + BinaryParser.fromByte(bytes[i]); +} +var object = bsonC.deserialize(serialized_data); +assert.equal("a_1", object.name); +assert.equal(false, object.unique); +assert.equal(1, object.key.a); + +// Should Correctly Deserialize object with all types +var bytes = [26,1,0,0,7,95,105,100,0,161,190,98,75,118,169,3,0,0,3,0,0,4,97,114,114,97,121,0,26,0,0,0,16,48,0,1,0,0,0,16,49,0,2,0,0,0,16,50,0,3,0,0,0,0,2,115,116,114,105,110,103,0,6,0,0,0,104,101,108,108,111,0,3,104,97,115,104,0,19,0,0,0,16,97,0,1,0,0,0,16,98,0,2,0,0,0,0,9,100,97,116,101,0,161,190,98,75,0,0,0,0,7,111,105,100,0,161,190,98,75,90,217,18,0,0,1,0,0,5,98,105,110,97,114,121,0,7,0,0,0,2,3,0,0,0,49,50,51,16,105,110,116,0,42,0,0,0,1,102,108,111,97,116,0,223,224,11,147,169,170,64,64,11,114,101,103,101,120,112,0,102,111,111,98,97,114,0,105,0,8,98,111,111,108,101,97,110,0,1,15,119,104,101,114,101,0,25,0,0,0,12,0,0,0,116,104,105,115,46,120,32,61,61,32,51,0,5,0,0,0,0,3,100,98,114,101,102,0,37,0,0,0,2,36,114,101,102,0,5,0,0,0,116,101,115,116,0,7,36,105,100,0,161,190,98,75,2,180,1,0,0,2,0,0,0,10,110,117,108,108,0,0]; +var serialized_data = ''; +// Convert to chars +for(var i = 0; i < bytes.length; i++) { + serialized_data = serialized_data + BinaryParser.fromByte(bytes[i]); +} + +var object = bsonJS.deserialize(new Buffer(serialized_data, 'binary')); +assert.equal("hello", object.string); +assert.deepEqual([1, 2, 3], object.array); +assert.equal(1, object.hash.a); +assert.equal(2, object.hash.b); +assert.ok(object.date != null); +assert.ok(object.oid != null); +assert.ok(object.binary != null); +assert.equal(42, object.int); +assert.equal(33.3333, object.float); +assert.ok(object.regexp != null); +assert.equal(true, object.boolean); +assert.ok(object.where != null); +assert.ok(object.dbref != null); +assert.ok(object['null'] == null); + +// Should Serialize and Deserialze String +var test_string = {hello: 'world'} +var serialized_data = bsonC.serialize(test_string) +assert.deepEqual(test_string, bsonC.deserialize(serialized_data)); + +// Should Correctly Serialize and Deserialize Integer +var test_number = {doc: 5} +var serialized_data = bsonC.serialize(test_number) +assert.deepEqual(test_number, bsonC.deserialize(serialized_data)); + +// Should Correctly Serialize and Deserialize null value +var test_null = {doc:null} +var serialized_data = bsonC.serialize(test_null) +var object = bsonC.deserialize(serialized_data); +assert.deepEqual(test_null, object); + +// Should Correctly Serialize and Deserialize undefined value +var test_undefined = {doc:undefined} +var serialized_data = bsonC.serialize(test_undefined) +var object = bsonJS.deserialize(new Buffer(serialized_data, 'binary')); +assert.equal(null, object.doc) + +// Should Correctly Serialize and Deserialize Number +var test_number = {doc: 5.5} +var serialized_data = bsonC.serialize(test_number) +assert.deepEqual(test_number, bsonC.deserialize(serialized_data)); + +// Should Correctly Serialize and Deserialize Integer +var test_int = {doc: 42} +var serialized_data = bsonC.serialize(test_int) +assert.deepEqual(test_int, bsonC.deserialize(serialized_data)); + +test_int = {doc: -5600} +serialized_data = bsonC.serialize(test_int) +assert.deepEqual(test_int, bsonC.deserialize(serialized_data)); + +test_int = {doc: 2147483647} +serialized_data = bsonC.serialize(test_int) +assert.deepEqual(test_int, bsonC.deserialize(serialized_data)); + +test_int = {doc: -2147483648} +serialized_data = bsonC.serialize(test_int) +assert.deepEqual(test_int, bsonC.deserialize(serialized_data)); + +// Should Correctly Serialize and Deserialize Object +var doc = {doc: {age: 42, name: 'Spongebob', shoe_size: 9.5}} +var serialized_data = bsonC.serialize(doc) +assert.deepEqual(doc, bsonC.deserialize(serialized_data)); + +// Should Correctly Serialize and Deserialize Array +var doc = {doc: [1, 2, 'a', 'b']} +var serialized_data = bsonC.serialize(doc) +assert.deepEqual(doc, bsonC.deserialize(serialized_data)); + +// Should Correctly Serialize and Deserialize Array with added on functions +var doc = {doc: [1, 2, 'a', 'b']} +var serialized_data = bsonC.serialize(doc) +assert.deepEqual(doc, bsonC.deserialize(serialized_data)); + +// Should Correctly Serialize and Deserialize A Boolean +var doc = {doc: true} +var serialized_data = bsonC.serialize(doc) +assert.deepEqual(doc, bsonC.deserialize(serialized_data)); + +// Should Correctly Serialize and Deserialize a Date +var date = new Date() +//(2009, 11, 12, 12, 00, 30) +date.setUTCDate(12) +date.setUTCFullYear(2009) +date.setUTCMonth(11 - 1) +date.setUTCHours(12) +date.setUTCMinutes(0) +date.setUTCSeconds(30) +var doc = {doc: date} +var serialized_data = bsonC.serialize(doc) +assert.deepEqual(doc, bsonC.deserialize(serialized_data)); + +// // Should Correctly Serialize and Deserialize Oid +var doc = {doc: new ObjectID()} +var serialized_data = bsonC.serialize(doc) +assert.deepEqual(doc.doc.toHexString(), bsonC.deserialize(serialized_data).doc.toHexString()) + +// Should Correctly encode Empty Hash +var test_code = {} +var serialized_data = bsonC.serialize(test_code) +assert.deepEqual(test_code, bsonC.deserialize(serialized_data)); + +// Should Correctly Serialize and Deserialize Ordered Hash +var doc = {doc: {b:1, a:2, c:3, d:4}} +var serialized_data = bsonC.serialize(doc) +var decoded_hash = bsonC.deserialize(serialized_data).doc +var keys = [] +for(name in decoded_hash) keys.push(name) +assert.deepEqual(['b', 'a', 'c', 'd'], keys) + +// Should Correctly Serialize and Deserialize Regular Expression +// Serialize the regular expression +var doc = {doc: /foobar/mi} +var serialized_data = bsonC.serialize(doc) +var doc2 = bsonC.deserialize(serialized_data); +assert.equal(doc.doc.toString(), doc2.doc.toString()) + +// Should Correctly Serialize and Deserialize a Binary object +var bin = new Binary() +var string = 'binstring' +for(var index = 0; index < string.length; index++) { + bin.put(string.charAt(index)) +} +var doc = {doc: bin} +var serialized_data = bsonC.serialize(doc) +var deserialized_data = bsonC.deserialize(serialized_data); +assert.equal(doc.doc.value(), deserialized_data.doc.value()) + +// Should Correctly Serialize and Deserialize a big Binary object +var data = fs.readFileSync("../../test/gridstore/test_gs_weird_bug.png", 'binary'); +var bin = new Binary() +bin.write(data) +var doc = {doc: bin} +var serialized_data = bsonC.serialize(doc) +var deserialized_data = bsonC.deserialize(serialized_data); +assert.equal(doc.doc.value(), deserialized_data.doc.value()) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/node_modules/mongodb/external-libs/bson/test/test_stackless_bson.js b/node_modules/mongodb/external-libs/bson/test/test_stackless_bson.js new file mode 100644 index 0000000..f271cac --- /dev/null +++ b/node_modules/mongodb/external-libs/bson/test/test_stackless_bson.js @@ -0,0 +1,132 @@ +var Buffer = require('buffer').Buffer, + BSON = require('../bson').BSON, + Buffer = require('buffer').Buffer, + BSONJS = require('../../../lib/mongodb/bson/bson').BSON, + BinaryParser = require('../../../lib/mongodb/bson/binary_parser').BinaryParser, + Long = require('../../../lib/mongodb/bson/long').Long, + ObjectID = require('../../../lib/mongodb/bson/bson').ObjectID, + Binary = require('../../../lib/mongodb/bson/bson').Binary, + Code = require('../../../lib/mongodb/bson/bson').Code, + DBRef = require('../../../lib/mongodb/bson/bson').DBRef, + Symbol = require('../../../lib/mongodb/bson/bson').Symbol, + Double = require('../../../lib/mongodb/bson/bson').Double, + MaxKey = require('../../../lib/mongodb/bson/bson').MaxKey, + MinKey = require('../../../lib/mongodb/bson/bson').MinKey, + Timestamp = require('../../../lib/mongodb/bson/bson').Timestamp; + assert = require('assert'); + +if(process.env['npm_package_config_native'] != null) return; + +console.log("=== EXECUTING TEST_STACKLESS_BSON ==="); + +// Parsers +var bsonC = new BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]); +var bsonJS = new BSONJS([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]); + +// Number of iterations for the benchmark +var COUNT = 10000; +// var COUNT = 1; +// Sample simple doc +var doc = {key:"Hello world", key2:"šđžčćŠĐŽČĆ", key3:'客家话', key4:'how are you doing dog!!'}; +// var doc = {}; +// for(var i = 0; i < 100; i++) { +// doc['string' + i] = "dumdyms fsdfdsfdsfdsfsdfdsfsdfsdfsdfsdfsdfsdfsdffsfsdfs"; +// } + +// // Calculate size +console.log(bsonC.calculateObjectSize2(doc)); +console.log(bsonJS.calculateObjectSize(doc)); +// assert.equal(bsonJS.calculateObjectSize(doc), bsonC.calculateObjectSize2(doc)); + +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- +// Benchmark calculateObjectSize +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- + +// Benchmark 1 JS BSON +console.log(COUNT + "x (objectBSON = bsonC.calculateObjectSize(object))") +start = new Date + +for (j=COUNT; --j>=0; ) { + var objectBSON = bsonJS.calculateObjectSize(doc); +} + +end = new Date +var opsprsecond = COUNT / ((end - start)/1000); +console.log("time = ", end - start, "ms -", COUNT / ((end - start)/1000), " ops/sec"); + +// Benchmark 2 C++ BSON calculateObjectSize +console.log(COUNT + "x (objectBSON = bsonC.calculateObjectSize(object))") +start = new Date + +for (j=COUNT; --j>=0; ) { + var objectBSON = bsonC.calculateObjectSize(doc); +} + +end = new Date +var opsprsecond = COUNT / ((end - start)/1000); +console.log("time = ", end - start, "ms -", COUNT / ((end - start)/1000), " ops/sec"); + +// Benchmark 3 C++ BSON calculateObjectSize2 +console.log(COUNT + "x (objectBSON = bsonC.calculateObjectSize2(object))") +start = new Date + +for (j=COUNT; --j>=0; ) { + var objectBSON = bsonC.calculateObjectSize2(doc); +} + +end = new Date +var opsprsecond = COUNT / ((end - start)/1000); +console.log("time = ", end - start, "ms -", COUNT / ((end - start)/1000), " ops/sec"); + +// // Serialize the content +// var _serializedDoc1 = bsonJS.serialize(doc, true, false); +// var _serializedDoc2 = bsonC.serialize2(doc, true, false); +// console.dir(_serializedDoc1); +// console.dir(_serializedDoc2); +// assert.equal(_serializedDoc1.toString('base64'), _serializedDoc2.toString('base64')) +// +// +// // Benchmark 1 +// console.log(COUNT + "x (objectBSON = bsonC.serialize(object))") +// start = new Date +// +// for (j=COUNT; --j>=0; ) { +// // var objectBSON = bsonC.serialize2(doc, true, false); +// var objectBSON = bsonJS.serialize(doc, true, false); +// } +// +// end = new Date +// var opsprsecond = COUNT / ((end - start)/1000); +// console.log("bson size (bytes): ", objectbsonC.length); +// console.log("time = ", end - start, "ms -", COUNT / ((end - start)/1000), " ops/sec"); +// console.log("MB/s = " + ((opsprsecond*objectbsonC.length)/1024)); +// +// // Benchmark 2 +// console.log(COUNT + "x (objectBSON = bsonC.serialize(object))") +// start = new Date +// +// for (j=COUNT; --j>=0; ) { +// var objectBSON = bsonC.serialize2(doc, true, false); +// } +// +// end = new Date +// var opsprsecond = COUNT / ((end - start)/1000); +// console.log("bson size (bytes): ", objectbsonC.length); +// console.log("time = ", end - start, "ms -", COUNT / ((end - start)/1000), " ops/sec"); +// console.log("MB/s = " + ((opsprsecond*objectbsonC.length)/1024)); +// +// // Benchmark 3 +// console.log(COUNT + "x (objectBSON = bsonC.serialize(object))") +// start = new Date +// +// for (j=COUNT; --j>=0; ) { +// var objectBSON = bsonC.serialize(doc, true, false); +// } +// +// end = new Date +// var opsprsecond = COUNT / ((end - start)/1000); +// console.log("bson size (bytes): ", objectbsonC.length); +// console.log("time = ", end - start, "ms -", COUNT / ((end - start)/1000), " ops/sec"); +// console.log("MB/s = " + ((opsprsecond*objectbsonC.length)/1024)); diff --git a/node_modules/mongodb/external-libs/bson/wscript b/node_modules/mongodb/external-libs/bson/wscript new file mode 100644 index 0000000..40f5317 --- /dev/null +++ b/node_modules/mongodb/external-libs/bson/wscript @@ -0,0 +1,39 @@ +import Options +from os import unlink, symlink, popen +from os.path import exists + +srcdir = "." +blddir = "build" +VERSION = "0.1.0" + +def set_options(opt): + opt.tool_options("compiler_cxx") + opt.add_option( '--debug' + , action='store_true' + , default=False + , help='Build debug variant [Default: False]' + , dest='debug' + ) + +def configure(conf): + conf.check_tool("compiler_cxx") + conf.check_tool("node_addon") + conf.env.append_value('CXXFLAGS', ['-O3', '-funroll-loops']) + + # conf.env.append_value('CXXFLAGS', ['-DDEBUG', '-g', '-O0', '-Wall', '-Wextra']) + # conf.check(lib='node', libpath=['/usr/lib', '/usr/local/lib'], uselib_store='NODE') + +def build(bld): + obj = bld.new_task_gen("cxx", "shlib", "node_addon") + obj.target = "bson" + obj.source = ["bson.cc"] + # obj.uselib = "NODE" + +def shutdown(): + # HACK to get compress.node out of build directory. + # better way to do this? + if Options.commands['clean']: + if exists('bson.node'): unlink('bson.node') + else: + if exists('build/default/bson.node') and not exists('bson.node'): + symlink('build/default/bson.node', 'bson.node') -- cgit v1.2.3-70-g09d2