#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);